[7.x] [kbn/optimizer] link to kibanaReact/kibanaUtils plugins… (#63009)

* [kbn/optimizer] link to data/kibanaReact/kibanaUtils plugins

* depend on normalize-path package

* typos

* avoid loading kibanaUtils and kibanaReact from urls

* update types and tests, now that whole plugin is exported to window

* update snapshot, removed export of `plugins` property

* fix condition, ignore things NOT in data/react/utils

* make es_ui_shared a "static bundle" too

* move kibana_utils/common usage to /public

* convert some more /common usage to /public

* use async-download/ordered-execution for bootstrap script

* fix typo

* remove kibanaUtils bundle

* remove kibanaReact bundle

* Revert "remove kibanaReact bundle"

This reverts commit f14e9ee604.

* Revert "remove kibanaUtils bundle"

This reverts commit a64b2a7f64.

* stop linking to the data plugin

* add comment pointing to async-download info

Co-authored-by: spalger <spalger@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

Co-authored-by: spalger <spalger@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Spencer 2020-04-08 14:27:30 -07:00 committed by GitHub
parent 35571bcd98
commit 6d5c6d1162
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 182 additions and 78 deletions

View file

@ -32,6 +32,7 @@
"json-stable-stringify": "^1.0.1",
"loader-utils": "^1.2.3",
"node-sass": "^4.13.0",
"normalize-path": "^3.0.0",
"postcss-loader": "^3.0.0",
"raw-loader": "^3.1.0",
"resolve-url-loader": "^3.1.1",

File diff suppressed because one or more lines are too long

View file

@ -19,6 +19,7 @@
import Path from 'path';
import normalizePath from 'normalize-path';
import { stringifyRequest } from 'loader-utils';
import webpack from 'webpack';
// @ts-ignore
@ -34,6 +35,59 @@ import { Bundle, WorkerConfig, parseDirPath, DisallowedSyntaxPlugin } from '../c
const PUBLIC_PATH_PLACEHOLDER = '__REPLACE_WITH_PUBLIC_PATH__';
const BABEL_PRESET_PATH = require.resolve('@kbn/babel-preset/webpack_preset');
const STATIC_BUNDLE_PLUGINS = [
// { id: 'data', dirname: 'data' },
{ id: 'kibanaReact', dirname: 'kibana_react' },
{ id: 'kibanaUtils', dirname: 'kibana_utils' },
{ id: 'esUiShared', dirname: 'es_ui_shared' },
];
/**
* Determine externals statements for require/import statements by looking
* for requests resolving to the primary public export of the data, kibanaReact,
* amd kibanaUtils plugins. If this module is being imported then rewrite
* the import to access the global `__kbnBundles__` variables and access
* the relavent properties from that global object.
*
* @param bundle
* @param context the directory containing the module which made `request`
* @param request the request for a module from a commonjs require() call or import statement
*/
function dynamicExternals(bundle: Bundle, context: string, request: string) {
// ignore imports that have loaders defined
if (request.includes('!')) {
return;
}
// don't allow any static bundle to rely on other static bundles
if (STATIC_BUNDLE_PLUGINS.some(p => bundle.id === p.id)) {
return;
}
// ignore requests that don't include a /data/public, /kibana_react/public, or
// /kibana_utils/public segment as a cheap way to avoid doing path resolution
// for paths that couldn't possibly resolve to what we're looking for
const reqToStaticBundle = STATIC_BUNDLE_PLUGINS.some(p =>
request.includes(`/${p.dirname}/public`)
);
if (!reqToStaticBundle) {
return;
}
// determine the most acurate resolution string we can without running full resolution
const rootRelative = normalizePath(
Path.relative(bundle.sourceRoot, Path.resolve(context, request))
);
for (const { id, dirname } of STATIC_BUNDLE_PLUGINS) {
if (rootRelative === `src/plugins/${dirname}/public`) {
return `__kbnBundles__['plugin/${id}']`;
}
}
// import doesn't match a root public import
return undefined;
}
export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) {
const commonConfig: webpack.Configuration = {
node: { fs: 'empty' },
@ -61,7 +115,6 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) {
// When the entry point is loaded, assign it's exported `plugin`
// value to a key on the global `__kbnBundles__` object.
library: ['__kbnBundles__', `plugin/${bundle.id}`],
libraryExport: 'plugin',
}
: {}),
},
@ -70,9 +123,16 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) {
noEmitOnErrors: true,
},
externals: {
...UiSharedDeps.externals,
},
externals: [
UiSharedDeps.externals,
function(context, request, cb) {
try {
cb(undefined, dynamicExternals(bundle, context, request));
} catch (error) {
cb(error, undefined);
}
},
],
plugins: [new CleanWebpackPlugin(), new DisallowedSyntaxPlugin()],

View file

@ -20,6 +20,13 @@
require('core-js/stable');
require('regenerator-runtime/runtime');
require('custom-event-polyfill');
if (typeof window.Event === 'object') {
// IE11 doesn't support unknown event types, required by react-use
// https://github.com/streamich/react-use/issues/73
window.Event = CustomEvent;
}
require('whatwg-fetch');
require('abortcontroller-polyfill/dist/polyfill-patch-fetch');
require('./vendor/childnode_remove_polyfill');

View file

@ -71,7 +71,7 @@ test('`loadPluginBundles` creates a script tag and loads initializer', async ()
// Setup a fake initializer as if a plugin bundle had actually been loaded.
const fakeInitializer = jest.fn();
coreWindow.__kbnBundles__['plugin/plugin-a'] = fakeInitializer;
coreWindow.__kbnBundles__['plugin/plugin-a'] = { plugin: fakeInitializer };
// Call the onload callback
fakeScriptTag.onload();
await expect(loadPromise).resolves.toEqual(fakeInitializer);

View file

@ -32,7 +32,7 @@ export type UnknownPluginInitializer = PluginInitializer<unknown, Record<string,
*/
export interface CoreWindow {
__kbnBundles__: {
[pluginBundleName: string]: UnknownPluginInitializer | undefined;
[pluginBundleName: string]: { plugin: UnknownPluginInitializer } | undefined;
};
}
@ -70,9 +70,28 @@ export const loadPluginBundle: LoadPluginBundle = <
) =>
new Promise<PluginInitializer<TSetup, TStart, TPluginsSetup, TPluginsStart>>(
(resolve, reject) => {
const script = document.createElement('script');
const coreWindow = (window as unknown) as CoreWindow;
const exportId = `plugin/${pluginName}`;
const readPluginExport = () => {
const PluginExport: any = coreWindow.__kbnBundles__[exportId];
if (typeof PluginExport?.plugin !== 'function') {
reject(
new Error(`Definition of plugin "${pluginName}" should be a function (${bundlePath}).`)
);
} else {
resolve(
PluginExport.plugin as PluginInitializer<TSetup, TStart, TPluginsSetup, TPluginsStart>
);
}
};
if (coreWindow.__kbnBundles__[exportId]) {
readPluginExport();
return;
}
const script = document.createElement('script');
// Assumes that all plugin bundles get put into the bundles/plugins subdirectory
const bundlePath = addBasePath(`/bundles/plugin/${pluginName}/${pluginName}.plugin.js`);
script.setAttribute('src', bundlePath);
@ -89,15 +108,7 @@ export const loadPluginBundle: LoadPluginBundle = <
// Wire up resolve and reject
script.onload = () => {
cleanupTag();
const initializer = coreWindow.__kbnBundles__[`plugin/${pluginName}`];
if (!initializer || typeof initializer !== 'function') {
reject(
new Error(`Definition of plugin "${pluginName}" should be a function (${bundlePath}).`)
);
} else {
resolve(initializer as PluginInitializer<TSetup, TStart, TPluginsSetup, TPluginsStart>);
}
readPluginExport();
};
script.onerror = () => {

View file

@ -23,7 +23,7 @@ import { createInputControlVisController } from './vis_controller';
import { getControlsTab } from './components/editor/controls_tab';
import { OptionsTab } from './components/editor/options_tab';
import { InputControlVisDependencies } from './plugin';
import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/common';
import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/public';
export function createInputControlVisTypeDefinition(deps: InputControlVisDependencies) {
const InputControlVisController = createInputControlVisController(deps);

View file

@ -17,6 +17,8 @@
* under the License.
*/
import { DiscoverServices } from './build_services';
import { createGetterSetter } from '../../../../../plugins/kibana_utils/public';
import { search } from '../../../../../plugins/data/public';
let angularModule: any = null;
let services: DiscoverServices | null = null;
@ -50,8 +52,6 @@ export const [getUrlTracker, setUrlTracker] = createGetterSetter<{
setTrackedUrl: (url: string) => void;
}>('urlTracker');
import { search } from '../../../../../plugins/data/public';
import { createGetterSetter } from '../../../../../plugins/kibana_utils/common';
export const { getRequestInspectorStats, getResponseInspectorStats, tabifyAggResponse } = search;
export {
unhashUrl,

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { createGetterSetter } from '../../../../plugins/kibana_utils/common';
import { createGetterSetter } from '../../../../plugins/kibana_utils/public';
import { DataPublicPluginStart } from '../../../../plugins/data/public';
export const [getFormatService, setFormatService] = createGetterSetter<

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { createGetterSetter } from '../../../../plugins/kibana_utils/common';
import { createGetterSetter } from '../../../../plugins/kibana_utils/public';
import { DataPublicPluginStart } from '../../../../plugins/data/public';
export const [getFormatService, setFormatService] = createGetterSetter<

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { createGetterSetter } from '../../../../plugins/kibana_utils/common';
import { createGetterSetter } from '../../../../plugins/kibana_utils/public';
import { DataPublicPluginStart } from '../../../../plugins/data/public';
export const [getFormatService, setFormatService] = createGetterSetter<

View file

@ -25,7 +25,7 @@ import { metricsRequestHandler } from './request_handler';
import { EditorController } from './editor_controller';
// @ts-ignore
import { PANEL_TYPES } from '../../../../plugins/vis_type_timeseries/common/panel_types';
import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/common';
import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/public';
export const metricsVisDefinition = {
name: 'metrics',

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { createGetterSetter } from '../../../../../plugins/kibana_utils/common';
import { createGetterSetter } from '../../../../../plugins/kibana_utils/public';
import { DataPublicPluginStart } from '../../../../../plugins/data/public';
import { IUiSettingsClient, NotificationsStart, SavedObjectsStart } from 'kibana/public';
import { dataPluginMock } from '../../../../../plugins/data/public/mocks';

View file

@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n';
import { DefaultEditorSize } from '../../../../plugins/vis_default_editor/public';
import { VegaVisualizationDependencies } from './plugin';
import { VegaVisEditor } from './components';
import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/common';
import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/public';
import { createVegaRequestHandler } from './vega_request_handler';
// @ts-ignore

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { createGetterSetter } from '../../../../plugins/kibana_utils/common';
import { createGetterSetter } from '../../../../plugins/kibana_utils/public';
import { DataPublicPluginStart } from '../../../../plugins/data/public';
export const [getDataActions, setDataActions] = createGetterSetter<

View file

@ -30,33 +30,27 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) {
function loadStyleSheet(url, cb) {
var dom = document.createElement('link');
dom.rel = 'stylesheet';
dom.type = 'text/css';
dom.href = url;
dom.addEventListener('error', failure);
dom.setAttribute('rel', 'stylesheet');
dom.setAttribute('type', 'text/css');
dom.setAttribute('href', url);
dom.addEventListener('load', cb);
document.head.appendChild(dom);
}
function loadScript(url, cb) {
var dom = document.createElement('script');
dom.setAttribute('async', '');
{{!-- NOTE: async = false is used to trigger async-download/ordered-execution as outlined here: https://www.html5rocks.com/en/tutorials/speed/script-loading/ --}}
dom.async = false;
dom.src = url;
dom.addEventListener('error', failure);
dom.setAttribute('src', url);
dom.addEventListener('load', cb);
document.head.appendChild(dom);
}
function load(urlSet, cb) {
if (urlSet.deps) {
load({ urls: urlSet.deps }, function () {
load({ urls: urlSet.urls }, cb);
});
return;
}
var pending = urlSet.urls.length;
urlSet.urls.forEach(function (url) {
function load(urls, cb) {
var pending = urls.length;
urls.forEach(function (url) {
var innerCb = function () {
pending = pending - 1;
if (pending === 0 && typeof cb === 'function') {
@ -74,36 +68,27 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) {
});
}
load({
deps: [
load([
{{#each sharedJsDepFilenames}}
'{{../regularBundlePath}}/kbn-ui-shared-deps/{{this}}',
{{/each}}
],
urls: [
{
deps: [
'{{regularBundlePath}}/kbn-ui-shared-deps/{{sharedJsFilename}}',
{
deps: [
'{{dllBundlePath}}/vendors_runtime.bundle.dll.js'
],
urls: [
{{#each dllJsChunks}}
'{{this}}',
{{/each}}
]
},
'{{regularBundlePath}}/commons.bundle.js',
],
urls: [
'{{regularBundlePath}}/{{appId}}.bundle.js',
{{#each styleSheetPaths}}
'{{this}}',
{{/each}}
]
}
]
'{{regularBundlePath}}/kbn-ui-shared-deps/{{sharedJsFilename}}',
'{{dllBundlePath}}/vendors_runtime.bundle.dll.js',
{{#each dllJsChunks}}
'{{this}}',
{{/each}}
'{{regularBundlePath}}/commons.bundle.js',
{{!-- '{{regularBundlePath}}/plugin/data/data.plugin.js', --}}
'{{regularBundlePath}}/plugin/kibanaUtils/kibanaUtils.plugin.js',
'{{regularBundlePath}}/plugin/esUiShared/esUiShared.plugin.js',
'{{regularBundlePath}}/plugin/kibanaReact/kibanaReact.plugin.js'
], function () {
load([
'{{regularBundlePath}}/{{appId}}.bundle.js',
{{#each styleSheetPaths}}
'{{this}}',
{{/each}}
])
});
};
}

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { createGetterSetter } from '../../kibana_utils/common';
import { createGetterSetter } from '../../kibana_utils/public';
import { DocViewsRegistry } from './doc_views/doc_views_registry';
export const [getDocViewsRegistry, setDocViewsRegistry] = createGetterSetter<DocViewsRegistry>(

View file

@ -21,7 +21,7 @@ import { Embeddable } from './embeddable';
import { EmbeddableInput } from './i_embeddable';
import { ViewMode } from '../types';
import { EmbeddableActionStorage, SerializedEvent } from './embeddable_action_storage';
import { of } from '../../../../kibana_utils/common';
import { of } from '../../../../kibana_utils/public';
class TestEmbeddable extends Embeddable<EmbeddableInput> {
public readonly type = 'test';

View file

@ -0,0 +1,5 @@
{
"id": "esUiShared",
"version": "kibana",
"ui": true
}

View file

@ -35,3 +35,11 @@ export {
export { indices } from './indices';
export { useUIAceKeyboardMode } from './use_ui_ace_keyboard_mode';
/** dummy plugin, we just want esUiShared to have its own bundle */
export function plugin() {
return new (class EsUiSharedPlugin {
setup() {}
start() {}
})();
}

View file

@ -0,0 +1,5 @@
{
"id": "kibanaReact",
"version": "kibana",
"ui": true
}

View file

@ -19,7 +19,7 @@
import { ComponentType, createElement as h } from 'react';
import { render as renderReact, unmountComponentAtNode } from 'react-dom';
import { UiComponent, UiComponentInstance } from '../../../kibana_utils/common';
import { UiComponent, UiComponentInstance } from '../../../kibana_utils/public';
/**
* Transform a React component into a `UiComponent`.

View file

@ -19,7 +19,7 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { UiComponent } from '../../../kibana_utils/common';
import { UiComponent } from '../../../kibana_utils/public';
import { uiToReactComponent } from './ui_to_react_component';
import { reactToUiComponent } from './react_to_ui_component';

View file

@ -18,7 +18,7 @@
*/
import { FC, createElement as h, useRef, useLayoutEffect, useMemo } from 'react';
import { UiComponent, UiComponentInstance } from '../../../kibana_utils/common';
import { UiComponent, UiComponentInstance } from '../../../kibana_utils/public';
/**
* Transforms `UiComponent` into a React component.

View file

@ -31,3 +31,11 @@ export { Markdown, MarkdownSimple } from './markdown';
export { reactToUiComponent, uiToReactComponent } from './adapters';
export { useUrlTracker } from './use_url_tracker';
export { toMountPoint } from './util';
/** dummy plugin, we just want kibanaReact to have its own bundle */
export function plugin() {
return new (class KibanaReactPlugin {
setup() {}
start() {}
})();
}

View file

@ -0,0 +1,5 @@
{
"id": "kibanaUtils",
"version": "kibana",
"ui": true
}

View file

@ -19,7 +19,6 @@
export {
calculateObjectHash,
createGetterSetter,
defer,
Defer,
Get,
@ -31,6 +30,8 @@ export {
UiComponent,
UiComponentInstance,
url,
createGetterSetter,
defaultFeedbackMessage,
} from '../common';
export * from './core';
export * from './errors';
@ -75,3 +76,11 @@ export {
} from './state_sync';
export { removeQueryParam, redirectWhenMissing, ensureDefaultIndexPattern } from './history';
export { applyDiff } from './state_management/utils/diff_object';
/** dummy plugin, we just want kibanaUtils to have its own bundle */
export function plugin() {
return new (class KibanaUtilsPlugin {
setup() {}
start() {}
})();
}

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { UiComponent } from 'src/plugins/kibana_utils/common';
import { UiComponent } from 'src/plugins/kibana_utils/public';
import { ActionType, ActionContextMapping } from '../types';
export type ActionByType<T extends ActionType> = Action<ActionContextMapping[T], T>;

View file

@ -17,7 +17,7 @@
* under the License.
*/
import { UiComponent } from 'src/plugins/kibana_utils/common';
import { UiComponent } from 'src/plugins/kibana_utils/public';
import { ActionType, ActionContextMapping } from '../types';
export interface ActionDefinition<T extends ActionType> {