mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[uiExports] migrate uiApp "uses" to explicit imports in apps (#17828)
* [uiExports] migrate uiApp "uses" to explicit imports in apps * [uiApp] update tests for getModules() method * [optimize/uiExports] improve naming and comments * [uiExports] sort imports so they load in the same order as before * [testHarness] load hacks when testing in the browser * [x-pack/uiExports] use new uiExports modules * [testHarness] describe why we import uiExports/hacks * [optimize] remove needless [].concat() * [optimize/createUiExportsModule] string.includes > string.indexOf * [uiExports/createUiExportsModule] remove needless capture of module exports
This commit is contained in:
parent
6201d1a6c7
commit
e1a2fcbd96
22 changed files with 125 additions and 111 deletions
|
@ -65,7 +65,7 @@ module.exports = {
|
|||
// instructs import/no-extraneous-dependencies to treat modules
|
||||
// in plugins/ or ui/ namespace as "core modules" so they don't
|
||||
// trigger failures for not being listed in package.json
|
||||
'import/core-modules': ['plugins', 'ui'],
|
||||
'import/core-modules': ['plugins', 'ui', 'uiExports'],
|
||||
|
||||
'import/resolver': {
|
||||
'@kbn/eslint-import-resolver-kibana': {
|
||||
|
|
|
@ -202,6 +202,7 @@
|
|||
"ui-select": "0.19.6",
|
||||
"url-loader": "0.5.9",
|
||||
"uuid": "3.0.1",
|
||||
"val-loader": "^1.1.0",
|
||||
"validate-npm-package-name": "2.2.2",
|
||||
"vega-lib": "^3.3.1",
|
||||
"vega-lite": "^2.4.0",
|
||||
|
|
|
@ -35,6 +35,16 @@ function initContext(file, config) {
|
|||
|
||||
exports.resolve = function resolveKibanaPath(importRequest, file, config) {
|
||||
config = config || {};
|
||||
|
||||
// these modules are simulated by webpack, so there is no
|
||||
// path to resolve to and no reason to do any more work
|
||||
if (importRequest.startsWith('uiExports/')) {
|
||||
return {
|
||||
found: true,
|
||||
path: null,
|
||||
};
|
||||
}
|
||||
|
||||
const { webpackConfig, aliasEntries } = initContext(file, config);
|
||||
let isPathRequest = getIsPathRequest(importRequest);
|
||||
|
||||
|
|
|
@ -48,22 +48,6 @@ export default function (kibana) {
|
|||
listed: false,
|
||||
description: 'the kibana you know and love',
|
||||
main: 'plugins/kibana/kibana',
|
||||
uses: [
|
||||
'home',
|
||||
'visTypes',
|
||||
'visResponseHandlers',
|
||||
'visRequestHandlers',
|
||||
'visEditorTypes',
|
||||
'savedObjectTypes',
|
||||
'spyModes',
|
||||
'fieldFormats',
|
||||
'fieldFormatEditors',
|
||||
'navbarExtensions',
|
||||
'managementSections',
|
||||
'devTools',
|
||||
'docViews',
|
||||
'embeddableFactories',
|
||||
],
|
||||
},
|
||||
|
||||
links: [
|
||||
|
|
|
@ -5,6 +5,22 @@ import chrome from 'ui/chrome';
|
|||
import routes from 'ui/routes';
|
||||
import { uiModules } from 'ui/modules';
|
||||
|
||||
// import the uiExports that we want to "use"
|
||||
import 'uiExports/home';
|
||||
import 'uiExports/visTypes';
|
||||
import 'uiExports/visResponseHandlers';
|
||||
import 'uiExports/visRequestHandlers';
|
||||
import 'uiExports/visEditorTypes';
|
||||
import 'uiExports/savedObjectTypes';
|
||||
import 'uiExports/spyModes';
|
||||
import 'uiExports/fieldFormats';
|
||||
import 'uiExports/fieldFormatEditors';
|
||||
import 'uiExports/navbarExtensions';
|
||||
import 'uiExports/managementSections';
|
||||
import 'uiExports/devTools';
|
||||
import 'uiExports/docViews';
|
||||
import 'uiExports/embeddableFactories';
|
||||
|
||||
import 'ui/autoload/all';
|
||||
import './home';
|
||||
import './discover';
|
||||
|
|
|
@ -8,10 +8,6 @@ export default function (kibana) {
|
|||
description: 'Time series expressions for everything',
|
||||
icon: 'plugins/timelion/icon.svg',
|
||||
main: 'plugins/timelion/app',
|
||||
uses: [
|
||||
'fieldFormats',
|
||||
'savedObjectTypes'
|
||||
]
|
||||
},
|
||||
hacks: [
|
||||
'plugins/timelion/lib/panel_registry',
|
||||
|
|
|
@ -7,6 +7,10 @@ import { notify, fatalError, toastNotifications } from 'ui/notify';
|
|||
import { timezoneProvider } from 'ui/vis/lib/timezone';
|
||||
import { recentlyAccessed } from 'ui/persisted_log';
|
||||
|
||||
// import the uiExports that we want to "use"
|
||||
import 'uiExports/fieldFormats';
|
||||
import 'uiExports/savedObjectTypes';
|
||||
|
||||
require('ui/autoload/all');
|
||||
require('plugins/timelion/directives/cells/cells');
|
||||
require('plugins/timelion/directives/fixed_element');
|
||||
|
|
|
@ -154,6 +154,30 @@ export default class BaseOptimizer {
|
|||
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
|
||||
// replace imports for `uiExports/*` modules with a synthetic module
|
||||
// created by create_ui_exports_module.js
|
||||
new webpack.NormalModuleReplacementPlugin(/^uiExports\//, (resource) => {
|
||||
// the map of uiExport types to module ids
|
||||
const extensions = this.uiBundles.getAppExtensions();
|
||||
|
||||
// everything following the first / in the request is
|
||||
// treated as a type of appExtension
|
||||
const type = resource.request.slice(resource.request.indexOf('/') + 1);
|
||||
|
||||
resource.request = [
|
||||
// the "val-loader" is used to execute create_ui_exports_module
|
||||
// and use its return value as the source for the module in the
|
||||
// bundle. This allows us to bypass writing to the file system
|
||||
require.resolve('val-loader'),
|
||||
'!',
|
||||
require.resolve('./create_ui_exports_module'),
|
||||
'?',
|
||||
// this JSON is parsed by create_ui_exports_module and determines
|
||||
// what require() calls it will execute within the bundle
|
||||
JSON.stringify({ type, modules: extensions[type] || [] })
|
||||
].join('');
|
||||
}),
|
||||
|
||||
...this.uiBundles.getWebpackPluginProviders()
|
||||
.map(provider => provider(webpack)),
|
||||
],
|
||||
|
|
21
src/optimize/create_ui_exports_module.js
Normal file
21
src/optimize/create_ui_exports_module.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
// We normalize all path separators to `/` in generated files
|
||||
function normalizePath(path) {
|
||||
return path.replace(/[\\\/]+/g, '/');
|
||||
}
|
||||
|
||||
export default function () {
|
||||
if (!module.id.includes('?')) {
|
||||
throw new Error('create_ui_exports_module loaded without JSON args in module.id');
|
||||
}
|
||||
|
||||
const { type, modules } = JSON.parse(module.id.slice(module.id.indexOf('?') + 1));
|
||||
const comment = `// dynamically generated to load ${type} uiExports from plugins`;
|
||||
const requires = modules
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
.map(m => `require('${normalizePath(m)}')`)
|
||||
.join('\n ');
|
||||
|
||||
return {
|
||||
code: `${comment}\n${requires}\n`
|
||||
};
|
||||
}
|
|
@ -54,6 +54,12 @@ translationsApi(chrome, internals);
|
|||
|
||||
const waitForBootstrap = new Promise(resolve => {
|
||||
chrome.bootstrap = function () {
|
||||
// import chrome nav controls and hacks now so that they are executed after
|
||||
// everything else, can safely import the chrome, and interact with services
|
||||
// and such setup by all other modules
|
||||
require('uiExports/chromeNavControls');
|
||||
require('uiExports/hacks');
|
||||
|
||||
chrome.setupAngular();
|
||||
angular.bootstrap(document.body, ['kibana']);
|
||||
resolve();
|
||||
|
|
|
@ -67,5 +67,8 @@ afterEach(function () {
|
|||
|
||||
// Kick off mocha, called at the end of test entry files
|
||||
export function bootstrap() {
|
||||
// load the hacks since we aren't actually bootstrapping the
|
||||
// chrome, which is where the hacks would normally be loaded
|
||||
require('uiExports/hacks');
|
||||
chrome.setupAngular();
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
import sinon from 'sinon';
|
||||
import expect from 'expect.js';
|
||||
import Chance from 'chance';
|
||||
|
||||
import { UiApp } from '../ui_app';
|
||||
import { UiNavLink } from '../../ui_nav_links';
|
||||
|
||||
const chance = new Chance();
|
||||
|
||||
function createStubUiAppSpec(extraParams) {
|
||||
return {
|
||||
id: 'uiapp-test',
|
||||
|
@ -18,11 +15,6 @@ function createStubUiAppSpec(extraParams) {
|
|||
linkToLastSubUrl: true,
|
||||
hidden: false,
|
||||
listed: false,
|
||||
uses: [
|
||||
'visTypes',
|
||||
'chromeNavControls',
|
||||
'hacks',
|
||||
],
|
||||
...extraParams
|
||||
};
|
||||
}
|
||||
|
@ -30,13 +22,6 @@ function createStubUiAppSpec(extraParams) {
|
|||
function createStubKbnServer() {
|
||||
return {
|
||||
plugins: [],
|
||||
uiExports: {
|
||||
appExtensions: {
|
||||
hacks: [
|
||||
'plugins/foo/hack'
|
||||
]
|
||||
}
|
||||
},
|
||||
config: {
|
||||
get: sinon.stub()
|
||||
.withArgs('server.basePath')
|
||||
|
@ -129,7 +114,6 @@ describe('ui apps / UiApp', () => {
|
|||
it('includes main and hack modules', () => {
|
||||
expect(app.getModules()).to.eql([
|
||||
'main.js',
|
||||
'plugins/foo/hack'
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -305,34 +289,5 @@ describe('ui apps / UiApp', () => {
|
|||
const app = createUiApp({ id: 'foo', main: 'bar' });
|
||||
expect(app.getModules()).to.eql(['bar']);
|
||||
});
|
||||
|
||||
it('returns appExtensions for used types only, in alphabetical order, starting with main module', () => {
|
||||
const kbnServer = createStubKbnServer();
|
||||
kbnServer.uiExports.appExtensions = {
|
||||
abc: chance.shuffle([
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
]),
|
||||
def: chance.shuffle([
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
])
|
||||
};
|
||||
|
||||
const appExtensionType = chance.shuffle(Object.keys(kbnServer.uiExports.appExtensions))[0];
|
||||
const appSpec = {
|
||||
id: 'foo',
|
||||
main: 'bar',
|
||||
uses: [appExtensionType],
|
||||
};
|
||||
|
||||
const app = createUiApp(appSpec, kbnServer);
|
||||
expect(app.getModules()).to.eql([
|
||||
'bar',
|
||||
...appExtensionType.split(''),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,7 +14,6 @@ export class UiApp {
|
|||
linkToLastSubUrl,
|
||||
listed,
|
||||
url = `/app/${id}`,
|
||||
uses = []
|
||||
} = spec;
|
||||
|
||||
if (!id) {
|
||||
|
@ -38,18 +37,6 @@ export class UiApp {
|
|||
throw new Error(`Unknown plugin id "${this._pluginId}"`);
|
||||
}
|
||||
|
||||
const { appExtensions = [] } = kbnServer.uiExports;
|
||||
this._modules = [].concat(
|
||||
this._main || [],
|
||||
uses
|
||||
// flatten appExtensions for used types
|
||||
.reduce((acc, type) => acc.concat(appExtensions[type] || []), [])
|
||||
// de-dupe app extension module ids
|
||||
.reduce((acc, item) => !item || acc.includes(item) ? acc : acc.concat(item), [])
|
||||
// sort app extension module ids alphabetically
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
);
|
||||
|
||||
if (!this.isHidden()) {
|
||||
// unless an app is hidden it gets a navlink, but we only respond to `getNavLink()`
|
||||
// if the app is also listed. This means that all apps in the kibanaPayload will
|
||||
|
@ -93,7 +80,7 @@ export class UiApp {
|
|||
}
|
||||
|
||||
getModules() {
|
||||
return this._modules;
|
||||
return this._main ? [this._main] : [];
|
||||
}
|
||||
|
||||
_getPlugin() {
|
||||
|
|
|
@ -44,6 +44,8 @@ export class UiBundlesController {
|
|||
matchBase: true
|
||||
});
|
||||
|
||||
this._appExtensions = uiExports.appExtensions || {};
|
||||
|
||||
this._webpackAliases = {
|
||||
...getWebpackAliases(pluginSpecs),
|
||||
...uiExports.webpackAliases
|
||||
|
@ -103,6 +105,10 @@ export class UiBundlesController {
|
|||
return this._webpackAliases;
|
||||
}
|
||||
|
||||
getAppExtensions() {
|
||||
return this._appExtensions;
|
||||
}
|
||||
|
||||
isDevMode() {
|
||||
return this._env === 'development';
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { uniq } from 'lodash';
|
||||
|
||||
import { flatConcatAtType } from './reduce';
|
||||
import { alias, mapSpec, wrap } from './modify_reduce';
|
||||
|
||||
|
@ -16,13 +14,18 @@ function applySpecDefaults(spec, type, pluginSpec) {
|
|||
linkToLastSubUrl = true,
|
||||
listed = !hidden,
|
||||
url = `/app/${id}`,
|
||||
uses = [],
|
||||
} = spec;
|
||||
|
||||
if (spec.injectVars) {
|
||||
throw new Error(`[plugin:${pluginId}] uiExports.app.injectVars has been removed. Use server.injectUiAppVars('${id}', () => { ... })`);
|
||||
}
|
||||
|
||||
if (spec.uses) {
|
||||
throw new Error(
|
||||
`[plugin:${pluginId}] uiExports.app.uses has been removed. Import these uiExport types with "import 'uiExports/{type}'"`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
pluginId,
|
||||
id,
|
||||
|
@ -35,11 +38,6 @@ function applySpecDefaults(spec, type, pluginSpec) {
|
|||
linkToLastSubUrl,
|
||||
listed,
|
||||
url,
|
||||
uses: uniq([
|
||||
...uses,
|
||||
'chromeNavControls',
|
||||
'hacks',
|
||||
]),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -38,18 +38,6 @@ export function dashboardMode(kibana) {
|
|||
hidden: true,
|
||||
description: 'view dashboards',
|
||||
main: 'plugins/dashboard_mode/dashboard_viewer',
|
||||
uses: [
|
||||
'visTypes',
|
||||
'visResponseHandlers',
|
||||
'visRequestHandlers',
|
||||
'visEditorTypes',
|
||||
'savedObjectTypes',
|
||||
'embeddableFactories',
|
||||
'spyModes',
|
||||
'navbarExtensions',
|
||||
'docViews',
|
||||
'fieldFormats'
|
||||
],
|
||||
links: [
|
||||
{
|
||||
id: 'kibana:dashboard',
|
||||
|
|
|
@ -13,6 +13,18 @@ import chrome from 'ui/chrome';
|
|||
import routes from 'ui/routes';
|
||||
import { uiModules } from 'ui/modules';
|
||||
|
||||
// import the uiExports that we want to "use"
|
||||
import 'uiExports/visTypes';
|
||||
import 'uiExports/visResponseHandlers';
|
||||
import 'uiExports/visRequestHandlers';
|
||||
import 'uiExports/visEditorTypes';
|
||||
import 'uiExports/savedObjectTypes';
|
||||
import 'uiExports/embeddableFactories';
|
||||
import 'uiExports/spyModes';
|
||||
import 'uiExports/navbarExtensions';
|
||||
import 'uiExports/docViews';
|
||||
import 'uiExports/fieldFormats';
|
||||
|
||||
import _ from 'lodash';
|
||||
import 'ui/autoload/all';
|
||||
import 'plugins/kibana/dashboard';
|
||||
|
|
|
@ -23,10 +23,6 @@ export function graph(kibana) {
|
|||
icon: 'plugins/graph/icon.png',
|
||||
description: 'Graph exploration',
|
||||
main: 'plugins/graph/app',
|
||||
uses: [
|
||||
'fieldFormats',
|
||||
'savedObjectTypes',
|
||||
]
|
||||
},
|
||||
hacks: ['plugins/graph/hacks/toggle_app_link_in_nav'],
|
||||
home: ['plugins/graph/register_feature'],
|
||||
|
|
|
@ -8,6 +8,10 @@ import d3 from 'd3';
|
|||
import 'ace';
|
||||
import rison from 'rison-node';
|
||||
|
||||
// import the uiExports that we want to "use"
|
||||
import 'uiExports/fieldFormats';
|
||||
import 'uiExports/savedObjectTypes';
|
||||
|
||||
import 'ui/autoload/all';
|
||||
import 'ui/directives/saved_object_finder';
|
||||
import chrome from 'ui/chrome';
|
||||
|
|
|
@ -34,14 +34,9 @@ export const ml = (kibana) => {
|
|||
description: 'Machine Learning for the Elastic Stack',
|
||||
icon: 'plugins/ml/ml.svg',
|
||||
main: 'plugins/ml/app',
|
||||
uses: [
|
||||
'fieldFormats',
|
||||
'savedObjectTypes',
|
||||
]
|
||||
},
|
||||
hacks: ['plugins/ml/hacks/toggle_app_link_in_nav'],
|
||||
home: ['plugins/ml/register_feature']
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
*/
|
||||
|
||||
|
||||
// import the uiExports that we want to "use"
|
||||
import 'uiExports/fieldFormats';
|
||||
import 'uiExports/savedObjectTypes';
|
||||
|
||||
import 'ui/courier';
|
||||
import 'ui-bootstrap';
|
||||
|
@ -47,4 +50,3 @@ uiRoutes
|
|||
.otherwise({
|
||||
redirectTo: '/jobs'
|
||||
});
|
||||
|
||||
|
|
|
@ -7881,7 +7881,7 @@ loader-runner@^2.3.0:
|
|||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2"
|
||||
|
||||
loader-utils@^1.0.2, loader-utils@^1.1.0:
|
||||
loader-utils@^1.0.0, loader-utils@^1.0.2, loader-utils@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd"
|
||||
dependencies:
|
||||
|
@ -12836,6 +12836,12 @@ uuid@^3.0.0, uuid@^3.1.0:
|
|||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
|
||||
|
||||
val-loader@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/val-loader/-/val-loader-1.1.0.tgz#ed91537424d62a4ded98e846ccf07367756bf506"
|
||||
dependencies:
|
||||
loader-utils "^1.0.0"
|
||||
|
||||
validate-npm-package-license@^3.0.1:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue