[kbn/optimizer] implement "requiredBundles" property of KP plugins (#70911)

Co-authored-by: Josh Dover <me@joshdover.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-07-09 18:43:17 -07:00 committed by GitHub
parent c1b26651bd
commit fa93a81ba6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
127 changed files with 888 additions and 181 deletions

View file

@ -19,5 +19,6 @@ export interface DiscoveredPlugin
| [configPath](./kibana-plugin-core-server.discoveredplugin.configpath.md) | <code>ConfigPath</code> | Root configuration path used by the plugin, defaults to "id" in snake\_case format. | | [configPath](./kibana-plugin-core-server.discoveredplugin.configpath.md) | <code>ConfigPath</code> | Root configuration path used by the plugin, defaults to "id" in snake\_case format. |
| [id](./kibana-plugin-core-server.discoveredplugin.id.md) | <code>PluginName</code> | Identifier of the plugin. | | [id](./kibana-plugin-core-server.discoveredplugin.id.md) | <code>PluginName</code> | Identifier of the plugin. |
| [optionalPlugins](./kibana-plugin-core-server.discoveredplugin.optionalplugins.md) | <code>readonly PluginName[]</code> | An optional list of the other plugins that if installed and enabled \*\*may be\*\* leveraged by this plugin for some additional functionality but otherwise are not required for this plugin to work properly. | | [optionalPlugins](./kibana-plugin-core-server.discoveredplugin.optionalplugins.md) | <code>readonly PluginName[]</code> | An optional list of the other plugins that if installed and enabled \*\*may be\*\* leveraged by this plugin for some additional functionality but otherwise are not required for this plugin to work properly. |
| [requiredBundles](./kibana-plugin-core-server.discoveredplugin.requiredbundles.md) | <code>readonly PluginName[]</code> | List of plugin ids that this plugin's UI code imports modules from that are not in <code>requiredPlugins</code>. |
| [requiredPlugins](./kibana-plugin-core-server.discoveredplugin.requiredplugins.md) | <code>readonly PluginName[]</code> | An optional list of the other plugins that \*\*must be\*\* installed and enabled for this plugin to function properly. | | [requiredPlugins](./kibana-plugin-core-server.discoveredplugin.requiredplugins.md) | <code>readonly PluginName[]</code> | An optional list of the other plugins that \*\*must be\*\* installed and enabled for this plugin to function properly. |

View file

@ -0,0 +1,18 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [DiscoveredPlugin](./kibana-plugin-core-server.discoveredplugin.md) &gt; [requiredBundles](./kibana-plugin-core-server.discoveredplugin.requiredbundles.md)
## DiscoveredPlugin.requiredBundles property
List of plugin ids that this plugin's UI code imports modules from that are not in `requiredPlugins`<!-- -->.
<b>Signature:</b>
```typescript
readonly requiredBundles: readonly PluginName[];
```
## Remarks
The plugins listed here will be loaded in the browser, even if the plugin is disabled. Required by `@kbn/optimizer` to support cross-plugin imports. "core" and plugins already listed in `requiredPlugins` do not need to be duplicated here.

View file

@ -25,6 +25,7 @@ Should never be used in code outside of Core but is exported for documentation p
| [id](./kibana-plugin-core-server.pluginmanifest.id.md) | <code>PluginName</code> | Identifier of the plugin. Must be a string in camelCase. Part of a plugin public contract. Other plugins leverage it to access plugin API, navigate to the plugin, etc. | | [id](./kibana-plugin-core-server.pluginmanifest.id.md) | <code>PluginName</code> | Identifier of the plugin. Must be a string in camelCase. Part of a plugin public contract. Other plugins leverage it to access plugin API, navigate to the plugin, etc. |
| [kibanaVersion](./kibana-plugin-core-server.pluginmanifest.kibanaversion.md) | <code>string</code> | The version of Kibana the plugin is compatible with, defaults to "version". | | [kibanaVersion](./kibana-plugin-core-server.pluginmanifest.kibanaversion.md) | <code>string</code> | The version of Kibana the plugin is compatible with, defaults to "version". |
| [optionalPlugins](./kibana-plugin-core-server.pluginmanifest.optionalplugins.md) | <code>readonly PluginName[]</code> | An optional list of the other plugins that if installed and enabled \*\*may be\*\* leveraged by this plugin for some additional functionality but otherwise are not required for this plugin to work properly. | | [optionalPlugins](./kibana-plugin-core-server.pluginmanifest.optionalplugins.md) | <code>readonly PluginName[]</code> | An optional list of the other plugins that if installed and enabled \*\*may be\*\* leveraged by this plugin for some additional functionality but otherwise are not required for this plugin to work properly. |
| [requiredBundles](./kibana-plugin-core-server.pluginmanifest.requiredbundles.md) | <code>readonly string[]</code> | List of plugin ids that this plugin's UI code imports modules from that are not in <code>requiredPlugins</code>. |
| [requiredPlugins](./kibana-plugin-core-server.pluginmanifest.requiredplugins.md) | <code>readonly PluginName[]</code> | An optional list of the other plugins that \*\*must be\*\* installed and enabled for this plugin to function properly. | | [requiredPlugins](./kibana-plugin-core-server.pluginmanifest.requiredplugins.md) | <code>readonly PluginName[]</code> | An optional list of the other plugins that \*\*must be\*\* installed and enabled for this plugin to function properly. |
| [server](./kibana-plugin-core-server.pluginmanifest.server.md) | <code>boolean</code> | Specifies whether plugin includes some server-side specific functionality. | | [server](./kibana-plugin-core-server.pluginmanifest.server.md) | <code>boolean</code> | Specifies whether plugin includes some server-side specific functionality. |
| [ui](./kibana-plugin-core-server.pluginmanifest.ui.md) | <code>boolean</code> | Specifies whether plugin includes some client/browser specific functionality that should be included into client bundle via <code>public/ui_plugin.js</code> file. | | [ui](./kibana-plugin-core-server.pluginmanifest.ui.md) | <code>boolean</code> | Specifies whether plugin includes some client/browser specific functionality that should be included into client bundle via <code>public/ui_plugin.js</code> file. |

View file

@ -0,0 +1,18 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [PluginManifest](./kibana-plugin-core-server.pluginmanifest.md) &gt; [requiredBundles](./kibana-plugin-core-server.pluginmanifest.requiredbundles.md)
## PluginManifest.requiredBundles property
List of plugin ids that this plugin's UI code imports modules from that are not in `requiredPlugins`<!-- -->.
<b>Signature:</b>
```typescript
readonly requiredBundles: readonly string[];
```
## Remarks
The plugins listed here will be loaded in the browser, even if the plugin is disabled. Required by `@kbn/optimizer` to support cross-plugin imports. "core" and plugins already listed in `requiredPlugins` do not need to be duplicated here.

View file

@ -5,5 +5,6 @@
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["bfetch", "developerExamples"], "requiredPlugins": ["bfetch", "developerExamples"],
"optionalPlugins": [] "optionalPlugins": [],
"requiredBundles": ["kibanaReact"]
} }

View file

@ -5,5 +5,6 @@
"server": false, "server": false,
"ui": true, "ui": true,
"requiredPlugins": ["embeddable", "embeddableExamples", "dashboard", "developerExamples"], "requiredPlugins": ["embeddable", "embeddableExamples", "dashboard", "developerExamples"],
"optionalPlugins": [] "optionalPlugins": [],
"requiredBundles": ["esUiShared"]
} }

View file

@ -6,5 +6,6 @@
"ui": true, "ui": true,
"requiredPlugins": ["embeddable", "uiActions"], "requiredPlugins": ["embeddable", "uiActions"],
"optionalPlugins": [], "optionalPlugins": [],
"extraPublicDirs": ["public/todo", "public/hello_world", "public/todo/todo_ref_embeddable"] "extraPublicDirs": ["public/todo", "public/hello_world", "public/todo/todo_ref_embeddable"],
"requiredBundles": ["kibanaReact"]
} }

View file

@ -5,5 +5,6 @@
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["navigation", "data", "developerExamples"], "requiredPlugins": ["navigation", "data", "developerExamples"],
"optionalPlugins": [] "optionalPlugins": [],
"requiredBundles": ["kibanaUtils", "kibanaReact"]
} }

View file

@ -5,5 +5,6 @@
"server": false, "server": false,
"ui": true, "ui": true,
"requiredPlugins": ["uiActions"], "requiredPlugins": ["uiActions"],
"optionalPlugins": [] "optionalPlugins": [],
"requiredBundles": ["kibanaReact"]
} }

View file

@ -5,5 +5,6 @@
"server": false, "server": false,
"ui": true, "ui": true,
"requiredPlugins": ["uiActions", "uiActionsExamples", "developerExamples"], "requiredPlugins": ["uiActions", "uiActionsExamples", "developerExamples"],
"optionalPlugins": [] "optionalPlugins": [],
"requiredBundles": ["kibanaReact"]
} }

View file

@ -1,4 +1,5 @@
{ {
"id": "bar", "id": "bar",
"ui": true "ui": true,
"requiredBundles": ["foo"]
} }

View file

@ -0,0 +1,3 @@
p {
background-color: rebeccapurple;
}

View file

@ -1,3 +1,5 @@
@import "./other_styles.scss";
body { body {
width: $globalStyleConstant; width: $globalStyleConstant;
background-image: url("ui/icon.svg"); background-image: url("ui/icon.svg");

View file

@ -87,6 +87,11 @@ run(
throw createFlagError('expected --report-stats to have no value'); throw createFlagError('expected --report-stats to have no value');
} }
const filter = typeof flags.filter === 'string' ? [flags.filter] : flags.filter;
if (!Array.isArray(filter) || !filter.every((f) => typeof f === 'string')) {
throw createFlagError('expected --filter to be one or more strings');
}
const config = OptimizerConfig.create({ const config = OptimizerConfig.create({
repoRoot: REPO_ROOT, repoRoot: REPO_ROOT,
watch, watch,
@ -99,6 +104,7 @@ run(
extraPluginScanDirs, extraPluginScanDirs,
inspectWorkers, inspectWorkers,
includeCoreBundle, includeCoreBundle,
filter,
}); });
let update$ = runOptimizer(config); let update$ = runOptimizer(config);
@ -128,12 +134,13 @@ run(
'inspect-workers', 'inspect-workers',
'report-stats', 'report-stats',
], ],
string: ['workers', 'scan-dir'], string: ['workers', 'scan-dir', 'filter'],
default: { default: {
core: true, core: true,
examples: true, examples: true,
cache: true, cache: true,
'inspect-workers': true, 'inspect-workers': true,
filter: [],
}, },
help: ` help: `
--watch run the optimizer in watch mode --watch run the optimizer in watch mode
@ -142,6 +149,7 @@ run(
--profile profile the webpack builds and write stats.json files to build outputs --profile profile the webpack builds and write stats.json files to build outputs
--no-core disable generating the core bundle --no-core disable generating the core bundle
--no-cache disable the cache --no-cache disable the cache
--filter comma-separated list of bundle id filters, results from multiple flags are merged, * and ! are supported
--no-examples don't build the example plugins --no-examples don't build the example plugins
--dist create bundles that are suitable for inclusion in the Kibana distributable --dist create bundles that are suitable for inclusion in the Kibana distributable
--scan-dir add a directory to the list of directories scanned for plugins (specify as many times as necessary) --scan-dir add a directory to the list of directories scanned for plugins (specify as many times as necessary)

View file

@ -50,6 +50,7 @@ it('creates cache keys', () => {
"spec": Object { "spec": Object {
"contextDir": "/foo/bar", "contextDir": "/foo/bar",
"id": "bar", "id": "bar",
"manifestPath": undefined,
"outputDir": "/foo/bar/target", "outputDir": "/foo/bar/target",
"publicDirNames": Array [ "publicDirNames": Array [
"public", "public",
@ -85,6 +86,7 @@ it('parses bundles from JSON specs', () => {
}, },
"contextDir": "/foo/bar", "contextDir": "/foo/bar",
"id": "bar", "id": "bar",
"manifestPath": undefined,
"outputDir": "/foo/bar/target", "outputDir": "/foo/bar/target",
"publicDirNames": Array [ "publicDirNames": Array [
"public", "public",

View file

@ -18,6 +18,7 @@
*/ */
import Path from 'path'; import Path from 'path';
import Fs from 'fs';
import { BundleCache } from './bundle_cache'; import { BundleCache } from './bundle_cache';
import { UnknownVals } from './ts_helpers'; import { UnknownVals } from './ts_helpers';
@ -25,6 +26,11 @@ import { includes, ascending, entriesToObject } from './array_helpers';
const VALID_BUNDLE_TYPES = ['plugin' as const, 'entry' as const]; const VALID_BUNDLE_TYPES = ['plugin' as const, 'entry' as const];
const DEFAULT_IMPLICIT_BUNDLE_DEPS = ['core'];
const isStringArray = (input: any): input is string[] =>
Array.isArray(input) && input.every((x) => typeof x === 'string');
export interface BundleSpec { export interface BundleSpec {
readonly type: typeof VALID_BUNDLE_TYPES[0]; readonly type: typeof VALID_BUNDLE_TYPES[0];
/** Unique id for this bundle */ /** Unique id for this bundle */
@ -37,6 +43,8 @@ export interface BundleSpec {
readonly sourceRoot: string; readonly sourceRoot: string;
/** Absolute path to the directory where output should be written */ /** Absolute path to the directory where output should be written */
readonly outputDir: string; readonly outputDir: string;
/** Absolute path to a kibana.json manifest file, if omitted we assume there are not dependenices */
readonly manifestPath?: string;
} }
export class Bundle { export class Bundle {
@ -56,6 +64,12 @@ export class Bundle {
public readonly sourceRoot: BundleSpec['sourceRoot']; public readonly sourceRoot: BundleSpec['sourceRoot'];
/** Absolute path to the output directory for this bundle */ /** Absolute path to the output directory for this bundle */
public readonly outputDir: BundleSpec['outputDir']; public readonly outputDir: BundleSpec['outputDir'];
/**
* Absolute path to a manifest file with "requiredBundles" which will be
* used to allow bundleRefs from this bundle to the exports of another bundle.
* Every bundle mentioned in the `requiredBundles` must be built together.
*/
public readonly manifestPath: BundleSpec['manifestPath'];
public readonly cache: BundleCache; public readonly cache: BundleCache;
@ -66,6 +80,7 @@ export class Bundle {
this.contextDir = spec.contextDir; this.contextDir = spec.contextDir;
this.sourceRoot = spec.sourceRoot; this.sourceRoot = spec.sourceRoot;
this.outputDir = spec.outputDir; this.outputDir = spec.outputDir;
this.manifestPath = spec.manifestPath;
this.cache = new BundleCache(Path.resolve(this.outputDir, '.kbn-optimizer-cache')); this.cache = new BundleCache(Path.resolve(this.outputDir, '.kbn-optimizer-cache'));
} }
@ -96,8 +111,54 @@ export class Bundle {
contextDir: this.contextDir, contextDir: this.contextDir,
sourceRoot: this.sourceRoot, sourceRoot: this.sourceRoot,
outputDir: this.outputDir, outputDir: this.outputDir,
manifestPath: this.manifestPath,
}; };
} }
readBundleDeps(): { implicit: string[]; explicit: string[] } {
if (!this.manifestPath) {
return {
implicit: [...DEFAULT_IMPLICIT_BUNDLE_DEPS],
explicit: [],
};
}
let json: string;
try {
json = Fs.readFileSync(this.manifestPath, 'utf8');
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
json = '{}';
}
let parsedManifest: { requiredPlugins?: string[]; requiredBundles?: string[] };
try {
parsedManifest = JSON.parse(json);
} catch (error) {
throw new Error(
`unable to parse manifest at [${this.manifestPath}], error: [${error.message}]`
);
}
if (typeof parsedManifest === 'object' && parsedManifest) {
const explicit = parsedManifest.requiredBundles || [];
const implicit = [...DEFAULT_IMPLICIT_BUNDLE_DEPS, ...(parsedManifest.requiredPlugins || [])];
if (isStringArray(explicit) && isStringArray(implicit)) {
return {
explicit,
implicit,
};
}
}
throw new Error(
`Expected "requiredBundles" and "requiredPlugins" in manifest file [${this.manifestPath}] to be arrays of strings`
);
}
} }
/** /**
@ -152,6 +213,13 @@ export function parseBundles(json: string) {
throw new Error('`bundles[]` must have an absolute path `outputDir` property'); throw new Error('`bundles[]` must have an absolute path `outputDir` property');
} }
const { manifestPath } = spec;
if (manifestPath !== undefined) {
if (!(typeof manifestPath === 'string' && Path.isAbsolute(manifestPath))) {
throw new Error('`bundles[]` must have an absolute path `manifestPath` property');
}
}
return new Bundle({ return new Bundle({
type, type,
id, id,
@ -159,6 +227,7 @@ export function parseBundles(json: string) {
contextDir, contextDir,
sourceRoot, sourceRoot,
outputDir, outputDir,
manifestPath,
}); });
} }
); );

View file

@ -24,6 +24,7 @@ export interface State {
optimizerCacheKey?: unknown; optimizerCacheKey?: unknown;
cacheKey?: unknown; cacheKey?: unknown;
moduleCount?: number; moduleCount?: number;
workUnits?: number;
files?: string[]; files?: string[];
bundleRefExportIds?: string[]; bundleRefExportIds?: string[];
} }
@ -96,6 +97,10 @@ export class BundleCache {
return this.get().cacheKey; return this.get().cacheKey;
} }
public getWorkUnits() {
return this.get().workUnits;
}
public getOptimizerCacheKey() { public getOptimizerCacheKey() {
return this.get().optimizerCacheKey; return this.get().optimizerCacheKey;
} }

View file

@ -114,6 +114,10 @@ export class BundleRefs {
constructor(private readonly refs: BundleRef[]) {} constructor(private readonly refs: BundleRef[]) {}
public forBundleIds(bundleIds: string[]) {
return this.refs.filter((r) => bundleIds.includes(r.bundleId));
}
public filterByExportIds(exportIds: string[]) { public filterByExportIds(exportIds: string[]) {
return this.refs.filter((r) => exportIds.includes(r.exportId)); return this.refs.filter((r) => exportIds.includes(r.exportId));
} }

File diff suppressed because one or more lines are too long

View file

@ -139,6 +139,7 @@ it('builds expected bundles, saves bundle counts to metadata', async () => {
expect(foo.cache.getModuleCount()).toBe(6); expect(foo.cache.getModuleCount()).toBe(6);
expect(foo.cache.getReferencedFiles()).toMatchInlineSnapshot(` expect(foo.cache.getReferencedFiles()).toMatchInlineSnapshot(`
Array [ Array [
<absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/kibana.json,
<absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/async_import.ts, <absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/async_import.ts,
<absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/ext.ts, <absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/ext.ts,
<absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/index.ts, <absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/foo/public/index.ts,
@ -160,12 +161,17 @@ it('builds expected bundles, saves bundle counts to metadata', async () => {
Array [ Array [
<absolute path>/node_modules/css-loader/package.json, <absolute path>/node_modules/css-loader/package.json,
<absolute path>/node_modules/style-loader/package.json, <absolute path>/node_modules/style-loader/package.json,
<absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/kibana.json,
<absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/index.scss, <absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/index.scss,
<absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/index.ts, <absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/index.ts,
<absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/legacy/_other_styles.scss,
<absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/legacy/styles.scss, <absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/legacy/styles.scss,
<absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/lib.ts, <absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/plugins/bar/public/lib.ts,
<absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/src/legacy/ui/public/icon.svg, <absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/src/legacy/ui/public/icon.svg,
<absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/src/legacy/ui/public/styles/_globals_v7dark.scss,
<absolute path>/packages/kbn-optimizer/src/__fixtures__/__tmp__/mock_repo/src/legacy/ui/public/styles/_globals_v7light.scss,
<absolute path>/packages/kbn-optimizer/target/worker/entry_point_creator.js, <absolute path>/packages/kbn-optimizer/target/worker/entry_point_creator.js,
<absolute path>/packages/kbn-optimizer/target/worker/postcss.config.js,
<absolute path>/packages/kbn-ui-shared-deps/public_path_module_creator.js, <absolute path>/packages/kbn-ui-shared-deps/public_path_module_creator.js,
] ]
`); `);

View file

@ -54,12 +54,18 @@ export function logOptimizerState(log: ToolingLog, config: OptimizerConfig) {
if (event?.type === 'worker started') { if (event?.type === 'worker started') {
let moduleCount = 0; let moduleCount = 0;
let workUnits = 0;
for (const bundle of event.bundles) { for (const bundle of event.bundles) {
moduleCount += bundle.cache.getModuleCount() ?? NaN; moduleCount += bundle.cache.getModuleCount() ?? NaN;
workUnits += bundle.cache.getWorkUnits() ?? NaN;
} }
const mcString = isFinite(moduleCount) ? String(moduleCount) : '?';
const bcString = String(event.bundles.length); log.info(
log.info(`starting worker [${bcString} bundles, ${mcString} modules]`); `starting worker [${event.bundles.length} ${
event.bundles.length === 1 ? 'bundle' : 'bundles'
}]`
);
log.debug(`modules [${moduleCount}] work units [${workUnits}]`);
} }
if (state.phase === 'reallocating') { if (state.phase === 'reallocating') {

View file

@ -23,11 +23,11 @@ import { Bundle } from '../common';
import { assignBundlesToWorkers, Assignments } from './assign_bundles_to_workers'; import { assignBundlesToWorkers, Assignments } from './assign_bundles_to_workers';
const hasModuleCount = (b: Bundle) => b.cache.getModuleCount() !== undefined; const hasWorkUnits = (b: Bundle) => b.cache.getWorkUnits() !== undefined;
const noModuleCount = (b: Bundle) => b.cache.getModuleCount() === undefined; const noWorkUnits = (b: Bundle) => b.cache.getWorkUnits() === undefined;
const summarizeBundles = (w: Assignments) => const summarizeBundles = (w: Assignments) =>
[ [
w.moduleCount ? `${w.moduleCount} known modules` : '', w.workUnits ? `${w.workUnits} work units` : '',
w.newBundles ? `${w.newBundles} new bundles` : '', w.newBundles ? `${w.newBundles} new bundles` : '',
] ]
.filter(Boolean) .filter(Boolean)
@ -42,15 +42,15 @@ const assertReturnVal = (workers: Assignments[]) => {
expect(workers).toBeInstanceOf(Array); expect(workers).toBeInstanceOf(Array);
for (const worker of workers) { for (const worker of workers) {
expect(worker).toEqual({ expect(worker).toEqual({
moduleCount: expect.any(Number), workUnits: expect.any(Number),
newBundles: expect.any(Number), newBundles: expect.any(Number),
bundles: expect.any(Array), bundles: expect.any(Array),
}); });
expect(worker.bundles.filter(noModuleCount).length).toBe(worker.newBundles); expect(worker.bundles.filter(noWorkUnits).length).toBe(worker.newBundles);
expect( expect(
worker.bundles.filter(hasModuleCount).reduce((sum, b) => sum + b.cache.getModuleCount()!, 0) worker.bundles.filter(hasWorkUnits).reduce((sum, b) => sum + b.cache.getWorkUnits()!, 0)
).toBe(worker.moduleCount); ).toBe(worker.workUnits);
} }
}; };
@ -76,7 +76,7 @@ const getBundles = ({
for (let i = 1; i <= withCounts; i++) { for (let i = 1; i <= withCounts; i++) {
const id = `foo${i}`; const id = `foo${i}`;
const bundle = testBundle(id); const bundle = testBundle(id);
bundle.cache.set({ moduleCount: i % 5 === 0 ? i * 10 : i }); bundle.cache.set({ workUnits: i % 5 === 0 ? i * 10 : i });
bundles.push(bundle); bundles.push(bundle);
} }
@ -95,8 +95,8 @@ it('creates less workers if maxWorkersCount is larger than bundle count', () =>
expect(workers.length).toBe(2); expect(workers.length).toBe(2);
expect(readConfigs(workers)).toMatchInlineSnapshot(` expect(readConfigs(workers)).toMatchInlineSnapshot(`
Array [ Array [
"worker 0 (1 known modules) => foo1", "worker 0 (1 work units) => foo1",
"worker 1 (2 known modules) => foo2", "worker 1 (2 work units) => foo2",
] ]
`); `);
}); });
@ -121,10 +121,10 @@ it('distributes bundles without module counts evenly after assigning modules wit
assertReturnVal(workers); assertReturnVal(workers);
expect(readConfigs(workers)).toMatchInlineSnapshot(` expect(readConfigs(workers)).toMatchInlineSnapshot(`
Array [ Array [
"worker 0 (78 known modules, 3 new bundles) => foo5,foo11,foo8,foo6,foo2,foo1,bar9,bar5,bar1", "worker 0 (78 work units, 3 new bundles) => foo5,foo11,foo8,foo6,foo2,foo1,bar9,bar5,bar1",
"worker 1 (78 known modules, 3 new bundles) => foo16,foo14,foo13,foo12,foo9,foo7,foo4,foo3,bar8,bar4,bar0", "worker 1 (78 work units, 3 new bundles) => foo16,foo14,foo13,foo12,foo9,foo7,foo4,foo3,bar8,bar4,bar0",
"worker 2 (100 known modules, 2 new bundles) => foo10,bar7,bar3", "worker 2 (100 work units, 2 new bundles) => foo10,bar7,bar3",
"worker 3 (150 known modules, 2 new bundles) => foo15,bar6,bar2", "worker 3 (150 work units, 2 new bundles) => foo15,bar6,bar2",
] ]
`); `);
}); });
@ -135,8 +135,8 @@ it('distributes 2 bundles to workers evenly', () => {
assertReturnVal(workers); assertReturnVal(workers);
expect(readConfigs(workers)).toMatchInlineSnapshot(` expect(readConfigs(workers)).toMatchInlineSnapshot(`
Array [ Array [
"worker 0 (1 known modules) => foo1", "worker 0 (1 work units) => foo1",
"worker 1 (2 known modules) => foo2", "worker 1 (2 work units) => foo2",
] ]
`); `);
}); });
@ -147,10 +147,10 @@ it('distributes 5 bundles to workers evenly', () => {
assertReturnVal(workers); assertReturnVal(workers);
expect(readConfigs(workers)).toMatchInlineSnapshot(` expect(readConfigs(workers)).toMatchInlineSnapshot(`
Array [ Array [
"worker 0 (3 known modules) => foo2,foo1", "worker 0 (3 work units) => foo2,foo1",
"worker 1 (3 known modules) => foo3", "worker 1 (3 work units) => foo3",
"worker 2 (4 known modules) => foo4", "worker 2 (4 work units) => foo4",
"worker 3 (50 known modules) => foo5", "worker 3 (50 work units) => foo5",
] ]
`); `);
}); });
@ -161,10 +161,10 @@ it('distributes 10 bundles to workers evenly', () => {
assertReturnVal(workers); assertReturnVal(workers);
expect(readConfigs(workers)).toMatchInlineSnapshot(` expect(readConfigs(workers)).toMatchInlineSnapshot(`
Array [ Array [
"worker 0 (20 known modules) => foo9,foo6,foo4,foo1", "worker 0 (20 work units) => foo9,foo6,foo4,foo1",
"worker 1 (20 known modules) => foo8,foo7,foo3,foo2", "worker 1 (20 work units) => foo8,foo7,foo3,foo2",
"worker 2 (50 known modules) => foo5", "worker 2 (50 work units) => foo5",
"worker 3 (100 known modules) => foo10", "worker 3 (100 work units) => foo10",
] ]
`); `);
}); });
@ -175,10 +175,10 @@ it('distributes 15 bundles to workers evenly', () => {
assertReturnVal(workers); assertReturnVal(workers);
expect(readConfigs(workers)).toMatchInlineSnapshot(` expect(readConfigs(workers)).toMatchInlineSnapshot(`
Array [ Array [
"worker 0 (70 known modules) => foo14,foo13,foo12,foo11,foo9,foo6,foo4,foo1", "worker 0 (70 work units) => foo14,foo13,foo12,foo11,foo9,foo6,foo4,foo1",
"worker 1 (70 known modules) => foo5,foo8,foo7,foo3,foo2", "worker 1 (70 work units) => foo5,foo8,foo7,foo3,foo2",
"worker 2 (100 known modules) => foo10", "worker 2 (100 work units) => foo10",
"worker 3 (150 known modules) => foo15", "worker 3 (150 work units) => foo15",
] ]
`); `);
}); });
@ -189,10 +189,10 @@ it('distributes 20 bundles to workers evenly', () => {
assertReturnVal(workers); assertReturnVal(workers);
expect(readConfigs(workers)).toMatchInlineSnapshot(` expect(readConfigs(workers)).toMatchInlineSnapshot(`
Array [ Array [
"worker 0 (153 known modules) => foo15,foo3", "worker 0 (153 work units) => foo15,foo3",
"worker 1 (153 known modules) => foo10,foo16,foo13,foo11,foo7,foo6", "worker 1 (153 work units) => foo10,foo16,foo13,foo11,foo7,foo6",
"worker 2 (154 known modules) => foo5,foo19,foo18,foo17,foo14,foo12,foo9,foo8,foo4,foo2,foo1", "worker 2 (154 work units) => foo5,foo19,foo18,foo17,foo14,foo12,foo9,foo8,foo4,foo2,foo1",
"worker 3 (200 known modules) => foo20", "worker 3 (200 work units) => foo20",
] ]
`); `);
}); });
@ -203,10 +203,10 @@ it('distributes 25 bundles to workers evenly', () => {
assertReturnVal(workers); assertReturnVal(workers);
expect(readConfigs(workers)).toMatchInlineSnapshot(` expect(readConfigs(workers)).toMatchInlineSnapshot(`
Array [ Array [
"worker 0 (250 known modules) => foo20,foo17,foo13,foo9,foo8,foo2,foo1", "worker 0 (250 work units) => foo20,foo17,foo13,foo9,foo8,foo2,foo1",
"worker 1 (250 known modules) => foo15,foo23,foo22,foo18,foo16,foo11,foo7,foo3", "worker 1 (250 work units) => foo15,foo23,foo22,foo18,foo16,foo11,foo7,foo3",
"worker 2 (250 known modules) => foo10,foo5,foo24,foo21,foo19,foo14,foo12,foo6,foo4", "worker 2 (250 work units) => foo10,foo5,foo24,foo21,foo19,foo14,foo12,foo6,foo4",
"worker 3 (250 known modules) => foo25", "worker 3 (250 work units) => foo25",
] ]
`); `);
}); });
@ -217,10 +217,10 @@ it('distributes 30 bundles to workers evenly', () => {
assertReturnVal(workers); assertReturnVal(workers);
expect(readConfigs(workers)).toMatchInlineSnapshot(` expect(readConfigs(workers)).toMatchInlineSnapshot(`
Array [ Array [
"worker 0 (352 known modules) => foo30,foo22,foo14,foo11,foo4,foo1", "worker 0 (352 work units) => foo30,foo22,foo14,foo11,foo4,foo1",
"worker 1 (352 known modules) => foo15,foo10,foo28,foo24,foo19,foo16,foo9,foo6", "worker 1 (352 work units) => foo15,foo10,foo28,foo24,foo19,foo16,foo9,foo6",
"worker 2 (353 known modules) => foo20,foo5,foo29,foo23,foo21,foo13,foo12,foo3,foo2", "worker 2 (353 work units) => foo20,foo5,foo29,foo23,foo21,foo13,foo12,foo3,foo2",
"worker 3 (353 known modules) => foo25,foo27,foo26,foo18,foo17,foo8,foo7", "worker 3 (353 work units) => foo25,foo27,foo26,foo18,foo17,foo8,foo7",
] ]
`); `);
}); });

View file

@ -20,19 +20,18 @@
import { Bundle, descending, ascending } from '../common'; import { Bundle, descending, ascending } from '../common';
// helper types used inside getWorkerConfigs so we don't have // helper types used inside getWorkerConfigs so we don't have
// to calculate moduleCounts over and over // to calculate workUnits over and over
export interface Assignments { export interface Assignments {
moduleCount: number; workUnits: number;
newBundles: number; newBundles: number;
bundles: Bundle[]; bundles: Bundle[];
} }
/** assign a wrapped bundle to a worker */ /** assign a wrapped bundle to a worker */
const assignBundle = (worker: Assignments, bundle: Bundle) => { const assignBundle = (worker: Assignments, bundle: Bundle) => {
const moduleCount = bundle.cache.getModuleCount(); const workUnits = bundle.cache.getWorkUnits();
if (moduleCount !== undefined) { if (workUnits !== undefined) {
worker.moduleCount += moduleCount; worker.workUnits += workUnits;
} else { } else {
worker.newBundles += 1; worker.newBundles += 1;
} }
@ -59,7 +58,7 @@ export function assignBundlesToWorkers(bundles: Bundle[], maxWorkerCount: number
const workers: Assignments[] = []; const workers: Assignments[] = [];
for (let i = 0; i < workerCount; i++) { for (let i = 0; i < workerCount; i++) {
workers.push({ workers.push({
moduleCount: 0, workUnits: 0,
newBundles: 0, newBundles: 0,
bundles: [], bundles: [],
}); });
@ -67,18 +66,18 @@ export function assignBundlesToWorkers(bundles: Bundle[], maxWorkerCount: number
/** /**
* separate the bundles which do and don't have module * separate the bundles which do and don't have module
* counts and sort them by [moduleCount, id] * counts and sort them by [workUnits, id]
*/ */
const bundlesWithCountsDesc = bundles const bundlesWithCountsDesc = bundles
.filter((b) => b.cache.getModuleCount() !== undefined) .filter((b) => b.cache.getWorkUnits() !== undefined)
.sort( .sort(
descending( descending(
(b) => b.cache.getModuleCount(), (b) => b.cache.getWorkUnits(),
(b) => b.id (b) => b.id
) )
); );
const bundlesWithoutModuleCounts = bundles const bundlesWithoutModuleCounts = bundles
.filter((b) => b.cache.getModuleCount() === undefined) .filter((b) => b.cache.getWorkUnits() === undefined)
.sort(descending((b) => b.id)); .sort(descending((b) => b.id));
/** /**
@ -87,9 +86,9 @@ export function assignBundlesToWorkers(bundles: Bundle[], maxWorkerCount: number
* with module counts are assigned * with module counts are assigned
*/ */
while (bundlesWithCountsDesc.length) { while (bundlesWithCountsDesc.length) {
const [smallestWorker, nextSmallestWorker] = workers.sort(ascending((w) => w.moduleCount)); const [smallestWorker, nextSmallestWorker] = workers.sort(ascending((w) => w.workUnits));
while (!nextSmallestWorker || smallestWorker.moduleCount <= nextSmallestWorker.moduleCount) { while (!nextSmallestWorker || smallestWorker.workUnits <= nextSmallestWorker.workUnits) {
const bundle = bundlesWithCountsDesc.shift(); const bundle = bundlesWithCountsDesc.shift();
if (!bundle) { if (!bundle) {
@ -104,7 +103,7 @@ export function assignBundlesToWorkers(bundles: Bundle[], maxWorkerCount: number
* assign bundles without module counts to workers round-robin * assign bundles without module counts to workers round-robin
* starting with the smallest workers * starting with the smallest workers
*/ */
workers.sort(ascending((w) => w.moduleCount)); workers.sort(ascending((w) => w.workUnits));
while (bundlesWithoutModuleCounts.length) { while (bundlesWithoutModuleCounts.length) {
for (const worker of workers) { for (const worker of workers) {
const bundle = bundlesWithoutModuleCounts.shift(); const bundle = bundlesWithoutModuleCounts.shift();

View file

@ -0,0 +1,72 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { filterById, HasId } from './filter_by_id';
const bundles: HasId[] = [
{ id: 'foo' },
{ id: 'bar' },
{ id: 'abc' },
{ id: 'abcd' },
{ id: 'abcde' },
{ id: 'example_a' },
];
const print = (result: HasId[]) =>
result
.map((b) => b.id)
.sort((a, b) => a.localeCompare(b))
.join(', ');
it('[] matches everything', () => {
expect(print(filterById([], bundles))).toMatchInlineSnapshot(
`"abc, abcd, abcde, bar, example_a, foo"`
);
});
it('* matches everything', () => {
expect(print(filterById(['*'], bundles))).toMatchInlineSnapshot(
`"abc, abcd, abcde, bar, example_a, foo"`
);
});
it('combines mutliple filters to select any bundle which is matched', () => {
expect(print(filterById(['foo', 'bar'], bundles))).toMatchInlineSnapshot(`"bar, foo"`);
expect(print(filterById(['bar', 'abc*'], bundles))).toMatchInlineSnapshot(
`"abc, abcd, abcde, bar"`
);
});
it('matches everything if any filter is *', () => {
expect(print(filterById(['*', '!abc*'], bundles))).toMatchInlineSnapshot(
`"abc, abcd, abcde, bar, example_a, foo"`
);
});
it('only matches bundles which are matched by an entire single filter', () => {
expect(print(filterById(['*,!abc*'], bundles))).toMatchInlineSnapshot(`"bar, example_a, foo"`);
});
it('handles purely positive filters', () => {
expect(print(filterById(['abc*'], bundles))).toMatchInlineSnapshot(`"abc, abcd, abcde"`);
});
it('handles purely negative filters', () => {
expect(print(filterById(['!abc*'], bundles))).toMatchInlineSnapshot(`"bar, example_a, foo"`);
});

View file

@ -0,0 +1,48 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export interface HasId {
id: string;
}
function parseFilter(filter: string) {
const positive: RegExp[] = [];
const negative: RegExp[] = [];
for (const segment of filter.split(',')) {
let trimmed = segment.trim();
let list = positive;
if (trimmed.startsWith('!')) {
trimmed = trimmed.slice(1);
list = negative;
}
list.push(new RegExp(`^${trimmed.split('*').join('.*')}$`));
}
return (bundle: HasId) =>
(!positive.length || positive.some((p) => p.test(bundle.id))) &&
(!negative.length || !negative.some((p) => p.test(bundle.id)));
}
export function filterById<T extends HasId>(filterStrings: string[], bundles: T[]) {
const filters = filterStrings.map(parseFilter);
return bundles.filter((b) => !filters.length || filters.some((f) => f(b)));
}

View file

@ -32,18 +32,21 @@ it('returns a bundle for core and each plugin', () => {
id: 'foo', id: 'foo',
isUiPlugin: true, isUiPlugin: true,
extraPublicDirs: [], extraPublicDirs: [],
manifestPath: '/repo/plugins/foo/kibana.json',
}, },
{ {
directory: '/repo/plugins/bar', directory: '/repo/plugins/bar',
id: 'bar', id: 'bar',
isUiPlugin: false, isUiPlugin: false,
extraPublicDirs: [], extraPublicDirs: [],
manifestPath: '/repo/plugins/bar/kibana.json',
}, },
{ {
directory: '/outside/of/repo/plugins/baz', directory: '/outside/of/repo/plugins/baz',
id: 'baz', id: 'baz',
isUiPlugin: true, isUiPlugin: true,
extraPublicDirs: [], extraPublicDirs: [],
manifestPath: '/outside/of/repo/plugins/baz/kibana.json',
}, },
], ],
'/repo' '/repo'
@ -53,6 +56,7 @@ it('returns a bundle for core and each plugin', () => {
Object { Object {
"contextDir": <absolute path>/plugins/foo, "contextDir": <absolute path>/plugins/foo,
"id": "foo", "id": "foo",
"manifestPath": <absolute path>/plugins/foo/kibana.json,
"outputDir": <absolute path>/plugins/foo/target/public, "outputDir": <absolute path>/plugins/foo/target/public,
"publicDirNames": Array [ "publicDirNames": Array [
"public", "public",
@ -63,6 +67,7 @@ it('returns a bundle for core and each plugin', () => {
Object { Object {
"contextDir": "/outside/of/repo/plugins/baz", "contextDir": "/outside/of/repo/plugins/baz",
"id": "baz", "id": "baz",
"manifestPath": "/outside/of/repo/plugins/baz/kibana.json",
"outputDir": "/outside/of/repo/plugins/baz/target/public", "outputDir": "/outside/of/repo/plugins/baz/target/public",
"publicDirNames": Array [ "publicDirNames": Array [
"public", "public",

View file

@ -35,6 +35,7 @@ export function getPluginBundles(plugins: KibanaPlatformPlugin[], repoRoot: stri
sourceRoot: repoRoot, sourceRoot: repoRoot,
contextDir: p.directory, contextDir: p.directory,
outputDir: Path.resolve(p.directory, 'target/public'), outputDir: Path.resolve(p.directory, 'target/public'),
manifestPath: p.manifestPath,
}) })
); );
} }

View file

@ -40,24 +40,28 @@ it('parses kibana.json files of plugins found in pluginDirs', () => {
"extraPublicDirs": Array [], "extraPublicDirs": Array [],
"id": "bar", "id": "bar",
"isUiPlugin": true, "isUiPlugin": true,
"manifestPath": <absolute path>/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/kibana.json,
}, },
Object { Object {
"directory": <absolute path>/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo, "directory": <absolute path>/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo,
"extraPublicDirs": Array [], "extraPublicDirs": Array [],
"id": "foo", "id": "foo",
"isUiPlugin": true, "isUiPlugin": true,
"manifestPath": <absolute path>/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo/kibana.json,
}, },
Object { Object {
"directory": <absolute path>/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz, "directory": <absolute path>/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz,
"extraPublicDirs": Array [], "extraPublicDirs": Array [],
"id": "baz", "id": "baz",
"isUiPlugin": false, "isUiPlugin": false,
"manifestPath": <absolute path>/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz/kibana.json,
}, },
Object { Object {
"directory": <absolute path>/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz, "directory": <absolute path>/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz,
"extraPublicDirs": Array [], "extraPublicDirs": Array [],
"id": "test_baz", "id": "test_baz",
"isUiPlugin": false, "isUiPlugin": false,
"manifestPath": <absolute path>/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz/kibana.json,
}, },
] ]
`); `);

View file

@ -24,6 +24,7 @@ import loadJsonFile from 'load-json-file';
export interface KibanaPlatformPlugin { export interface KibanaPlatformPlugin {
readonly directory: string; readonly directory: string;
readonly manifestPath: string;
readonly id: string; readonly id: string;
readonly isUiPlugin: boolean; readonly isUiPlugin: boolean;
readonly extraPublicDirs: string[]; readonly extraPublicDirs: string[];
@ -92,6 +93,7 @@ function readKibanaPlatformPlugin(manifestPath: string): KibanaPlatformPlugin {
return { return {
directory: Path.dirname(manifestPath), directory: Path.dirname(manifestPath),
manifestPath,
id: manifest.id, id: manifest.id,
isUiPlugin: !!manifest.ui, isUiPlugin: !!manifest.ui,
extraPublicDirs: extraPublicDirs || [], extraPublicDirs: extraPublicDirs || [],

View file

@ -21,6 +21,7 @@ jest.mock('./assign_bundles_to_workers.ts');
jest.mock('./kibana_platform_plugins.ts'); jest.mock('./kibana_platform_plugins.ts');
jest.mock('./get_plugin_bundles.ts'); jest.mock('./get_plugin_bundles.ts');
jest.mock('../common/theme_tags.ts'); jest.mock('../common/theme_tags.ts');
jest.mock('./filter_by_id.ts');
import Path from 'path'; import Path from 'path';
import Os from 'os'; import Os from 'os';
@ -113,6 +114,7 @@ describe('OptimizerConfig::parseOptions()', () => {
Object { Object {
"cache": true, "cache": true,
"dist": false, "dist": false,
"filters": Array [],
"includeCoreBundle": false, "includeCoreBundle": false,
"inspectWorkers": false, "inspectWorkers": false,
"maxWorkerCount": 2, "maxWorkerCount": 2,
@ -139,6 +141,7 @@ describe('OptimizerConfig::parseOptions()', () => {
Object { Object {
"cache": false, "cache": false,
"dist": false, "dist": false,
"filters": Array [],
"includeCoreBundle": false, "includeCoreBundle": false,
"inspectWorkers": false, "inspectWorkers": false,
"maxWorkerCount": 2, "maxWorkerCount": 2,
@ -165,6 +168,7 @@ describe('OptimizerConfig::parseOptions()', () => {
Object { Object {
"cache": true, "cache": true,
"dist": false, "dist": false,
"filters": Array [],
"includeCoreBundle": false, "includeCoreBundle": false,
"inspectWorkers": false, "inspectWorkers": false,
"maxWorkerCount": 2, "maxWorkerCount": 2,
@ -193,6 +197,7 @@ describe('OptimizerConfig::parseOptions()', () => {
Object { Object {
"cache": true, "cache": true,
"dist": false, "dist": false,
"filters": Array [],
"includeCoreBundle": false, "includeCoreBundle": false,
"inspectWorkers": false, "inspectWorkers": false,
"maxWorkerCount": 2, "maxWorkerCount": 2,
@ -218,6 +223,7 @@ describe('OptimizerConfig::parseOptions()', () => {
Object { Object {
"cache": true, "cache": true,
"dist": false, "dist": false,
"filters": Array [],
"includeCoreBundle": false, "includeCoreBundle": false,
"inspectWorkers": false, "inspectWorkers": false,
"maxWorkerCount": 2, "maxWorkerCount": 2,
@ -243,6 +249,7 @@ describe('OptimizerConfig::parseOptions()', () => {
Object { Object {
"cache": true, "cache": true,
"dist": false, "dist": false,
"filters": Array [],
"includeCoreBundle": false, "includeCoreBundle": false,
"inspectWorkers": false, "inspectWorkers": false,
"maxWorkerCount": 100, "maxWorkerCount": 100,
@ -265,6 +272,7 @@ describe('OptimizerConfig::parseOptions()', () => {
Object { Object {
"cache": false, "cache": false,
"dist": false, "dist": false,
"filters": Array [],
"includeCoreBundle": false, "includeCoreBundle": false,
"inspectWorkers": false, "inspectWorkers": false,
"maxWorkerCount": 100, "maxWorkerCount": 100,
@ -287,6 +295,7 @@ describe('OptimizerConfig::parseOptions()', () => {
Object { Object {
"cache": false, "cache": false,
"dist": false, "dist": false,
"filters": Array [],
"includeCoreBundle": false, "includeCoreBundle": false,
"inspectWorkers": false, "inspectWorkers": false,
"maxWorkerCount": 100, "maxWorkerCount": 100,
@ -310,6 +319,7 @@ describe('OptimizerConfig::parseOptions()', () => {
Object { Object {
"cache": false, "cache": false,
"dist": false, "dist": false,
"filters": Array [],
"includeCoreBundle": false, "includeCoreBundle": false,
"inspectWorkers": false, "inspectWorkers": false,
"maxWorkerCount": 100, "maxWorkerCount": 100,
@ -333,6 +343,7 @@ describe('OptimizerConfig::parseOptions()', () => {
Object { Object {
"cache": true, "cache": true,
"dist": false, "dist": false,
"filters": Array [],
"includeCoreBundle": false, "includeCoreBundle": false,
"inspectWorkers": false, "inspectWorkers": false,
"maxWorkerCount": 100, "maxWorkerCount": 100,
@ -358,6 +369,7 @@ describe('OptimizerConfig::create()', () => {
const findKibanaPlatformPlugins: jest.Mock = jest.requireMock('./kibana_platform_plugins.ts') const findKibanaPlatformPlugins: jest.Mock = jest.requireMock('./kibana_platform_plugins.ts')
.findKibanaPlatformPlugins; .findKibanaPlatformPlugins;
const getPluginBundles: jest.Mock = jest.requireMock('./get_plugin_bundles.ts').getPluginBundles; const getPluginBundles: jest.Mock = jest.requireMock('./get_plugin_bundles.ts').getPluginBundles;
const filterById: jest.Mock = jest.requireMock('./filter_by_id.ts').filterById;
beforeEach(() => { beforeEach(() => {
if ('mock' in OptimizerConfig.parseOptions) { if ('mock' in OptimizerConfig.parseOptions) {
@ -370,6 +382,7 @@ describe('OptimizerConfig::create()', () => {
]); ]);
findKibanaPlatformPlugins.mockReturnValue(Symbol('new platform plugins')); findKibanaPlatformPlugins.mockReturnValue(Symbol('new platform plugins'));
getPluginBundles.mockReturnValue([Symbol('bundle1'), Symbol('bundle2')]); getPluginBundles.mockReturnValue([Symbol('bundle1'), Symbol('bundle2')]);
filterById.mockReturnValue(Symbol('filtered bundles'));
jest.spyOn(OptimizerConfig, 'parseOptions').mockImplementation((): any => ({ jest.spyOn(OptimizerConfig, 'parseOptions').mockImplementation((): any => ({
cache: Symbol('parsed cache'), cache: Symbol('parsed cache'),
@ -382,6 +395,7 @@ describe('OptimizerConfig::create()', () => {
themeTags: Symbol('theme tags'), themeTags: Symbol('theme tags'),
inspectWorkers: Symbol('parsed inspect workers'), inspectWorkers: Symbol('parsed inspect workers'),
profileWebpack: Symbol('parsed profile webpack'), profileWebpack: Symbol('parsed profile webpack'),
filters: [],
})); }));
}); });
@ -392,10 +406,7 @@ describe('OptimizerConfig::create()', () => {
expect(config).toMatchInlineSnapshot(` expect(config).toMatchInlineSnapshot(`
OptimizerConfig { OptimizerConfig {
"bundles": Array [ "bundles": Symbol(filtered bundles),
Symbol(bundle1),
Symbol(bundle2),
],
"cache": Symbol(parsed cache), "cache": Symbol(parsed cache),
"dist": Symbol(parsed dist), "dist": Symbol(parsed dist),
"inspectWorkers": Symbol(parsed inspect workers), "inspectWorkers": Symbol(parsed inspect workers),
@ -431,6 +442,32 @@ describe('OptimizerConfig::create()', () => {
} }
`); `);
expect(filterById.mock).toMatchInlineSnapshot(`
Object {
"calls": Array [
Array [
Array [],
Array [
Symbol(bundle1),
Symbol(bundle2),
],
],
],
"instances": Array [
[Window],
],
"invocationCallOrder": Array [
23,
],
"results": Array [
Object {
"type": "return",
"value": Symbol(filtered bundles),
},
],
}
`);
expect(getPluginBundles.mock).toMatchInlineSnapshot(` expect(getPluginBundles.mock).toMatchInlineSnapshot(`
Object { Object {
"calls": Array [ "calls": Array [

View file

@ -31,6 +31,7 @@ import {
import { findKibanaPlatformPlugins, KibanaPlatformPlugin } from './kibana_platform_plugins'; import { findKibanaPlatformPlugins, KibanaPlatformPlugin } from './kibana_platform_plugins';
import { getPluginBundles } from './get_plugin_bundles'; import { getPluginBundles } from './get_plugin_bundles';
import { filterById } from './filter_by_id';
function pickMaxWorkerCount(dist: boolean) { function pickMaxWorkerCount(dist: boolean) {
// don't break if cpus() returns nothing, or an empty array // don't break if cpus() returns nothing, or an empty array
@ -77,6 +78,18 @@ interface Options {
pluginScanDirs?: string[]; pluginScanDirs?: string[];
/** absolute paths that should be added to the default scan dirs */ /** absolute paths that should be added to the default scan dirs */
extraPluginScanDirs?: string[]; extraPluginScanDirs?: string[];
/**
* array of comma separated patterns that will be matched against bundle ids.
* bundles will only be built if they match one of the specified patterns.
* `*` can exist anywhere in each pattern and will match anything, `!` inverts the pattern
*
* examples:
* --filter foo --filter bar # [foo, bar], excludes [foobar]
* --filter foo,bar # [foo, bar], excludes [foobar]
* --filter foo* # [foo, foobar], excludes [bar]
* --filter f*r # [foobar], excludes [foo, bar]
*/
filter?: string[];
/** flag that causes the core bundle to be built along with plugins */ /** flag that causes the core bundle to be built along with plugins */
includeCoreBundle?: boolean; includeCoreBundle?: boolean;
@ -103,6 +116,7 @@ interface ParsedOptions {
dist: boolean; dist: boolean;
pluginPaths: string[]; pluginPaths: string[];
pluginScanDirs: string[]; pluginScanDirs: string[];
filters: string[];
inspectWorkers: boolean; inspectWorkers: boolean;
includeCoreBundle: boolean; includeCoreBundle: boolean;
themeTags: ThemeTags; themeTags: ThemeTags;
@ -118,6 +132,7 @@ export class OptimizerConfig {
const inspectWorkers = !!options.inspectWorkers; const inspectWorkers = !!options.inspectWorkers;
const cache = options.cache !== false && !process.env.KBN_OPTIMIZER_NO_CACHE; const cache = options.cache !== false && !process.env.KBN_OPTIMIZER_NO_CACHE;
const includeCoreBundle = !!options.includeCoreBundle; const includeCoreBundle = !!options.includeCoreBundle;
const filters = options.filter || [];
const repoRoot = options.repoRoot; const repoRoot = options.repoRoot;
if (!Path.isAbsolute(repoRoot)) { if (!Path.isAbsolute(repoRoot)) {
@ -172,6 +187,7 @@ export class OptimizerConfig {
cache, cache,
pluginScanDirs, pluginScanDirs,
pluginPaths, pluginPaths,
filters,
inspectWorkers, inspectWorkers,
includeCoreBundle, includeCoreBundle,
themeTags, themeTags,
@ -198,7 +214,7 @@ export class OptimizerConfig {
]; ];
return new OptimizerConfig( return new OptimizerConfig(
bundles, filterById(options.filters, bundles),
options.cache, options.cache,
options.watch, options.watch,
options.inspectWorkers, options.inspectWorkers,

View file

@ -10,6 +10,7 @@
// @ts-ignore not typed by @types/webpack // @ts-ignore not typed by @types/webpack
import Module from 'webpack/lib/Module'; import Module from 'webpack/lib/Module';
import { BundleRef } from '../common';
export class BundleRefModule extends Module { export class BundleRefModule extends Module {
public built = false; public built = false;
@ -17,12 +18,12 @@ export class BundleRefModule extends Module {
public buildInfo?: any; public buildInfo?: any;
public exportsArgument = '__webpack_exports__'; public exportsArgument = '__webpack_exports__';
constructor(public readonly exportId: string) { constructor(public readonly ref: BundleRef) {
super('kbn/bundleRef', null); super('kbn/bundleRef', null);
} }
libIdent() { libIdent() {
return this.exportId; return this.ref.exportId;
} }
chunkCondition(chunk: any) { chunkCondition(chunk: any) {
@ -30,7 +31,7 @@ export class BundleRefModule extends Module {
} }
identifier() { identifier() {
return '@kbn/bundleRef ' + JSON.stringify(this.exportId); return '@kbn/bundleRef ' + JSON.stringify(this.ref.exportId);
} }
readableIdentifier() { readableIdentifier() {
@ -51,7 +52,7 @@ export class BundleRefModule extends Module {
source() { source() {
return ` return `
__webpack_require__.r(__webpack_exports__); __webpack_require__.r(__webpack_exports__);
var ns = __kbnBundles__.get('${this.exportId}'); var ns = __kbnBundles__.get('${this.ref.exportId}');
Object.defineProperties(__webpack_exports__, Object.getOwnPropertyDescriptors(ns)) Object.defineProperties(__webpack_exports__, Object.getOwnPropertyDescriptors(ns))
`; `;
} }

View file

@ -44,6 +44,7 @@ export class BundleRefsPlugin {
private readonly resolvedRefEntryCache = new Map<BundleRef, Promise<string>>(); private readonly resolvedRefEntryCache = new Map<BundleRef, Promise<string>>();
private readonly resolvedRequestCache = new Map<string, Promise<string | undefined>>(); private readonly resolvedRequestCache = new Map<string, Promise<string | undefined>>();
private readonly ignorePrefix = Path.resolve(this.bundle.contextDir) + Path.sep; private readonly ignorePrefix = Path.resolve(this.bundle.contextDir) + Path.sep;
private allowedBundleIds = new Set<string>();
constructor(private readonly bundle: Bundle, private readonly bundleRefs: BundleRefs) {} constructor(private readonly bundle: Bundle, private readonly bundleRefs: BundleRefs) {}
@ -81,6 +82,45 @@ export class BundleRefsPlugin {
} }
); );
}); });
compiler.hooks.compilation.tap('BundleRefsPlugin/getRequiredBundles', (compilation) => {
this.allowedBundleIds.clear();
const manifestPath = this.bundle.manifestPath;
if (!manifestPath) {
return;
}
const deps = this.bundle.readBundleDeps();
for (const ref of this.bundleRefs.forBundleIds([...deps.explicit, ...deps.implicit])) {
this.allowedBundleIds.add(ref.bundleId);
}
compilation.hooks.additionalAssets.tap('BundleRefsPlugin/watchManifest', () => {
compilation.fileDependencies.add(manifestPath);
});
compilation.hooks.finishModules.tapPromise(
'BundleRefsPlugin/finishModules',
async (modules) => {
const usedBundleIds = (modules as any[])
.filter((m: any): m is BundleRefModule => m instanceof BundleRefModule)
.map((m) => m.ref.bundleId);
const unusedBundleIds = deps.explicit
.filter((id) => !usedBundleIds.includes(id))
.join(', ');
if (unusedBundleIds) {
const error = new Error(
`Bundle for [${this.bundle.id}] lists [${unusedBundleIds}] as a required bundle, but does not use it. Please remove it.`
);
(error as any).file = manifestPath;
compilation.errors.push(error);
}
}
);
});
} }
private cachedResolveRefEntry(ref: BundleRef) { private cachedResolveRefEntry(ref: BundleRef) {
@ -170,21 +210,29 @@ export class BundleRefsPlugin {
return; return;
} }
const eligibleRefs = this.bundleRefs.filterByContextPrefix(this.bundle, resolved); const possibleRefs = this.bundleRefs.filterByContextPrefix(this.bundle, resolved);
if (!eligibleRefs.length) { if (!possibleRefs.length) {
// import doesn't match a bundle context // import doesn't match a bundle context
return; return;
} }
for (const ref of eligibleRefs) { for (const ref of possibleRefs) {
const resolvedEntry = await this.cachedResolveRefEntry(ref); const resolvedEntry = await this.cachedResolveRefEntry(ref);
if (resolved === resolvedEntry) { if (resolved !== resolvedEntry) {
return new BundleRefModule(ref.exportId); continue;
} }
if (!this.allowedBundleIds.has(ref.bundleId)) {
throw new Error(
`import [${request}] references a public export of the [${ref.bundleId}] bundle, but that bundle is not in the "requiredPlugins" or "requiredBundles" list in the plugin manifest [${this.bundle.manifestPath}]`
);
}
return new BundleRefModule(ref);
} }
const bundleId = Array.from(new Set(eligibleRefs.map((r) => r.bundleId))).join(', '); const bundleId = Array.from(new Set(possibleRefs.map((r) => r.bundleId))).join(', ');
const publicDir = eligibleRefs.map((r) => r.entry).join(', '); const publicDir = possibleRefs.map((r) => r.entry).join(', ');
throw new Error( throw new Error(
`import [${request}] references a non-public export of the [${bundleId}] bundle and must point to one of the public directories: [${publicDir}]` `import [${request}] references a non-public export of the [${bundleId}] bundle and must point to one of the public directories: [${publicDir}]`
); );

View file

@ -50,6 +50,15 @@ import {
const PLUGIN_NAME = '@kbn/optimizer'; const PLUGIN_NAME = '@kbn/optimizer';
/**
* sass-loader creates about a 40% overhead on the overall optimizer runtime, and
* so this constant is used to indicate to assignBundlesToWorkers() that there is
* extra work done in a bundle that has a lot of scss imports. The value is
* arbitrary and just intended to weigh the bundles so that they are distributed
* across mulitple workers on machines with lots of cores.
*/
const EXTRA_SCSS_WORK_UNITS = 100;
/** /**
* Create an Observable<CompilerMsg> for a specific child compiler + bundle * Create an Observable<CompilerMsg> for a specific child compiler + bundle
*/ */
@ -102,6 +111,11 @@ const observeCompiler = (
const bundleRefExportIds: string[] = []; const bundleRefExportIds: string[] = [];
const referencedFiles = new Set<string>(); const referencedFiles = new Set<string>();
let normalModuleCount = 0; let normalModuleCount = 0;
let workUnits = stats.compilation.fileDependencies.size;
if (bundle.manifestPath) {
referencedFiles.add(bundle.manifestPath);
}
for (const module of stats.compilation.modules) { for (const module of stats.compilation.modules) {
if (isNormalModule(module)) { if (isNormalModule(module)) {
@ -111,6 +125,15 @@ const observeCompiler = (
if (!parsedPath.dirs.includes('node_modules')) { if (!parsedPath.dirs.includes('node_modules')) {
referencedFiles.add(path); referencedFiles.add(path);
if (path.endsWith('.scss')) {
workUnits += EXTRA_SCSS_WORK_UNITS;
for (const depPath of module.buildInfo.fileDependencies) {
referencedFiles.add(depPath);
}
}
continue; continue;
} }
@ -127,7 +150,7 @@ const observeCompiler = (
} }
if (module instanceof BundleRefModule) { if (module instanceof BundleRefModule) {
bundleRefExportIds.push(module.exportId); bundleRefExportIds.push(module.ref.exportId);
continue; continue;
} }
@ -158,6 +181,7 @@ const observeCompiler = (
optimizerCacheKey: workerConfig.optimizerCacheKey, optimizerCacheKey: workerConfig.optimizerCacheKey,
cacheKey: bundle.createCacheKey(files, mtimes), cacheKey: bundle.createCacheKey(files, mtimes),
moduleCount: normalModuleCount, moduleCount: normalModuleCount,
workUnits,
files, files,
}); });

View file

@ -32,6 +32,7 @@ function createManifest(
configPath: ['path'], configPath: ['path'],
requiredPlugins: required, requiredPlugins: required,
optionalPlugins: optional, optionalPlugins: optional,
requiredBundles: [],
} as DiscoveredPlugin; } as DiscoveredPlugin;
} }

View file

@ -73,6 +73,7 @@ function createManifest(
configPath: ['path'], configPath: ['path'],
requiredPlugins: required, requiredPlugins: required,
optionalPlugins: optional, optionalPlugins: optional,
requiredBundles: [],
}; };
} }

View file

@ -109,6 +109,7 @@ beforeEach(() => {
[ [
'plugin-id', 'plugin-id',
{ {
requiredBundles: [],
publicTargetDir: 'path/to/target/public', publicTargetDir: 'path/to/target/public',
publicAssetsDir: '/plugins/name/assets/', publicAssetsDir: '/plugins/name/assets/',
}, },

View file

@ -302,6 +302,7 @@ test('set defaults for all missing optional fields', async () => {
kibanaVersion: '7.0.0', kibanaVersion: '7.0.0',
optionalPlugins: [], optionalPlugins: [],
requiredPlugins: [], requiredPlugins: [],
requiredBundles: [],
server: true, server: true,
ui: false, ui: false,
}); });
@ -331,6 +332,7 @@ test('return all set optional fields as they are in manifest', async () => {
version: 'some-version', version: 'some-version',
kibanaVersion: '7.0.0', kibanaVersion: '7.0.0',
optionalPlugins: ['some-optional-plugin'], optionalPlugins: ['some-optional-plugin'],
requiredBundles: [],
requiredPlugins: ['some-required-plugin', 'some-required-plugin-2'], requiredPlugins: ['some-required-plugin', 'some-required-plugin-2'],
server: false, server: false,
ui: true, ui: true,
@ -361,6 +363,7 @@ test('return manifest when plugin expected Kibana version matches actual version
kibanaVersion: '7.0.0-alpha2', kibanaVersion: '7.0.0-alpha2',
optionalPlugins: [], optionalPlugins: [],
requiredPlugins: ['some-required-plugin'], requiredPlugins: ['some-required-plugin'],
requiredBundles: [],
server: true, server: true,
ui: false, ui: false,
}); });
@ -390,6 +393,7 @@ test('return manifest when plugin expected Kibana version is `kibana`', async ()
kibanaVersion: 'kibana', kibanaVersion: 'kibana',
optionalPlugins: [], optionalPlugins: [],
requiredPlugins: ['some-required-plugin'], requiredPlugins: ['some-required-plugin'],
requiredBundles: [],
server: true, server: true,
ui: true, ui: true,
}); });

View file

@ -58,6 +58,7 @@ const KNOWN_MANIFEST_FIELDS = (() => {
ui: true, ui: true,
server: true, server: true,
extraPublicDirs: true, extraPublicDirs: true,
requiredBundles: true,
}; };
return new Set(Object.keys(manifestFields)); return new Set(Object.keys(manifestFields));
@ -191,6 +192,7 @@ export async function parseManifest(
configPath: manifest.configPath || snakeCase(manifest.id), configPath: manifest.configPath || snakeCase(manifest.id),
requiredPlugins: Array.isArray(manifest.requiredPlugins) ? manifest.requiredPlugins : [], requiredPlugins: Array.isArray(manifest.requiredPlugins) ? manifest.requiredPlugins : [],
optionalPlugins: Array.isArray(manifest.optionalPlugins) ? manifest.optionalPlugins : [], optionalPlugins: Array.isArray(manifest.optionalPlugins) ? manifest.optionalPlugins : [],
requiredBundles: Array.isArray(manifest.requiredBundles) ? manifest.requiredBundles : [],
ui: includesUiPlugin, ui: includesUiPlugin,
server: includesServerPlugin, server: includesServerPlugin,
extraPublicDirs: manifest.extraPublicDirs, extraPublicDirs: manifest.extraPublicDirs,

View file

@ -43,6 +43,7 @@ describe('PluginsService', () => {
disabled = false, disabled = false,
version = 'some-version', version = 'some-version',
requiredPlugins = [], requiredPlugins = [],
requiredBundles = [],
optionalPlugins = [], optionalPlugins = [],
kibanaVersion = '7.0.0', kibanaVersion = '7.0.0',
configPath = [path], configPath = [path],
@ -53,6 +54,7 @@ describe('PluginsService', () => {
disabled?: boolean; disabled?: boolean;
version?: string; version?: string;
requiredPlugins?: string[]; requiredPlugins?: string[];
requiredBundles?: string[];
optionalPlugins?: string[]; optionalPlugins?: string[];
kibanaVersion?: string; kibanaVersion?: string;
configPath?: ConfigPath; configPath?: ConfigPath;
@ -68,6 +70,7 @@ describe('PluginsService', () => {
configPath: `${configPath}${disabled ? '-disabled' : ''}`, configPath: `${configPath}${disabled ? '-disabled' : ''}`,
kibanaVersion, kibanaVersion,
requiredPlugins, requiredPlugins,
requiredBundles,
optionalPlugins, optionalPlugins,
server, server,
ui, ui,

View file

@ -54,6 +54,7 @@ function createPluginManifest(manifestProps: Partial<PluginManifest> = {}): Plug
kibanaVersion: '7.0.0', kibanaVersion: '7.0.0',
requiredPlugins: ['some-required-dep'], requiredPlugins: ['some-required-dep'],
optionalPlugins: ['some-optional-dep'], optionalPlugins: ['some-optional-dep'],
requiredBundles: [],
server: true, server: true,
ui: true, ui: true,
...manifestProps, ...manifestProps,

View file

@ -53,6 +53,7 @@ export class PluginWrapper<
public readonly configPath: PluginManifest['configPath']; public readonly configPath: PluginManifest['configPath'];
public readonly requiredPlugins: PluginManifest['requiredPlugins']; public readonly requiredPlugins: PluginManifest['requiredPlugins'];
public readonly optionalPlugins: PluginManifest['optionalPlugins']; public readonly optionalPlugins: PluginManifest['optionalPlugins'];
public readonly requiredBundles: PluginManifest['requiredBundles'];
public readonly includesServerPlugin: PluginManifest['server']; public readonly includesServerPlugin: PluginManifest['server'];
public readonly includesUiPlugin: PluginManifest['ui']; public readonly includesUiPlugin: PluginManifest['ui'];
@ -81,6 +82,7 @@ export class PluginWrapper<
this.configPath = params.manifest.configPath; this.configPath = params.manifest.configPath;
this.requiredPlugins = params.manifest.requiredPlugins; this.requiredPlugins = params.manifest.requiredPlugins;
this.optionalPlugins = params.manifest.optionalPlugins; this.optionalPlugins = params.manifest.optionalPlugins;
this.requiredBundles = params.manifest.requiredBundles;
this.includesServerPlugin = params.manifest.server; this.includesServerPlugin = params.manifest.server;
this.includesUiPlugin = params.manifest.ui; this.includesUiPlugin = params.manifest.ui;
} }

View file

@ -43,6 +43,7 @@ function createPluginManifest(manifestProps: Partial<PluginManifest> = {}): Plug
configPath: 'path', configPath: 'path',
kibanaVersion: '7.0.0', kibanaVersion: '7.0.0',
requiredPlugins: ['some-required-dep'], requiredPlugins: ['some-required-dep'],
requiredBundles: [],
optionalPlugins: ['some-optional-dep'], optionalPlugins: ['some-optional-dep'],
server: true, server: true,
ui: true, ui: true,

View file

@ -64,6 +64,7 @@ const createPlugin = (
disabled = false, disabled = false,
version = 'some-version', version = 'some-version',
requiredPlugins = [], requiredPlugins = [],
requiredBundles = [],
optionalPlugins = [], optionalPlugins = [],
kibanaVersion = '7.0.0', kibanaVersion = '7.0.0',
configPath = [path], configPath = [path],
@ -74,6 +75,7 @@ const createPlugin = (
disabled?: boolean; disabled?: boolean;
version?: string; version?: string;
requiredPlugins?: string[]; requiredPlugins?: string[];
requiredBundles?: string[];
optionalPlugins?: string[]; optionalPlugins?: string[];
kibanaVersion?: string; kibanaVersion?: string;
configPath?: ConfigPath; configPath?: ConfigPath;
@ -89,6 +91,7 @@ const createPlugin = (
configPath: `${configPath}${disabled ? '-disabled' : ''}`, configPath: `${configPath}${disabled ? '-disabled' : ''}`,
kibanaVersion, kibanaVersion,
requiredPlugins, requiredPlugins,
requiredBundles,
optionalPlugins, optionalPlugins,
server, server,
ui, ui,
@ -460,6 +463,7 @@ describe('PluginsService', () => {
id: plugin.name, id: plugin.name,
configPath: plugin.manifest.configPath, configPath: plugin.manifest.configPath,
requiredPlugins: [], requiredPlugins: [],
requiredBundles: [],
optionalPlugins: [], optionalPlugins: [],
}, },
]; ];
@ -563,10 +567,12 @@ describe('PluginsService', () => {
"plugin-1" => Object { "plugin-1" => Object {
"publicAssetsDir": <absolute path>/path-1/public/assets, "publicAssetsDir": <absolute path>/path-1/public/assets,
"publicTargetDir": <absolute path>/path-1/target/public, "publicTargetDir": <absolute path>/path-1/target/public,
"requiredBundles": Array [],
}, },
"plugin-2" => Object { "plugin-2" => Object {
"publicAssetsDir": <absolute path>/path-2/public/assets, "publicAssetsDir": <absolute path>/path-2/public/assets,
"publicTargetDir": <absolute path>/path-2/target/public, "publicTargetDir": <absolute path>/path-2/target/public,
"requiredBundles": Array [],
}, },
} }
`); `);

View file

@ -228,6 +228,7 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS
if (plugin.includesUiPlugin) { if (plugin.includesUiPlugin) {
this.uiPluginInternalInfo.set(plugin.name, { this.uiPluginInternalInfo.set(plugin.name, {
requiredBundles: plugin.requiredBundles,
publicTargetDir: Path.resolve(plugin.path, 'target/public'), publicTargetDir: Path.resolve(plugin.path, 'target/public'),
publicAssetsDir: Path.resolve(plugin.path, 'public/assets'), publicAssetsDir: Path.resolve(plugin.path, 'public/assets'),
}); });
@ -239,6 +240,21 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS
.toPromise(); .toPromise();
for (const [pluginName, { plugin, isEnabled }] of pluginEnableStatuses) { for (const [pluginName, { plugin, isEnabled }] of pluginEnableStatuses) {
// validate that `requiredBundles` ids point to a discovered plugin which `includesUiPlugin`
for (const requiredBundleId of plugin.requiredBundles) {
if (!pluginEnableStatuses.has(requiredBundleId)) {
throw new Error(
`Plugin bundle with id "${requiredBundleId}" is required by plugin "${pluginName}" but it is missing.`
);
}
if (!pluginEnableStatuses.get(requiredBundleId)!.plugin.includesUiPlugin) {
throw new Error(
`Plugin bundle with id "${requiredBundleId}" is required by plugin "${pluginName}" but it doesn't have a UI bundle.`
);
}
}
const pluginEnablement = this.shouldEnablePlugin(pluginName, pluginEnableStatuses); const pluginEnablement = this.shouldEnablePlugin(pluginName, pluginEnableStatuses);
if (pluginEnablement.enabled) { if (pluginEnablement.enabled) {

View file

@ -55,6 +55,7 @@ function createPlugin(
kibanaVersion: '7.0.0', kibanaVersion: '7.0.0',
requiredPlugins: required, requiredPlugins: required,
optionalPlugins: optional, optionalPlugins: optional,
requiredBundles: [],
server, server,
ui, ui,
}, },

View file

@ -178,6 +178,7 @@ export class PluginsSystem {
optionalPlugins: plugin.manifest.optionalPlugins.filter((p) => optionalPlugins: plugin.manifest.optionalPlugins.filter((p) =>
uiPluginNames.includes(p) uiPluginNames.includes(p)
), ),
requiredBundles: plugin.manifest.requiredBundles,
}, },
]; ];
}) })

View file

@ -136,6 +136,18 @@ export interface PluginManifest {
*/ */
readonly requiredPlugins: readonly PluginName[]; readonly requiredPlugins: readonly PluginName[];
/**
* List of plugin ids that this plugin's UI code imports modules from that are
* not in `requiredPlugins`.
*
* @remarks
* The plugins listed here will be loaded in the browser, even if the plugin is
* disabled. Required by `@kbn/optimizer` to support cross-plugin imports.
* "core" and plugins already listed in `requiredPlugins` do not need to be
* duplicated here.
*/
readonly requiredBundles: readonly string[];
/** /**
* An optional list of the other plugins that if installed and enabled **may be** * An optional list of the other plugins that if installed and enabled **may be**
* leveraged by this plugin for some additional functionality but otherwise are * leveraged by this plugin for some additional functionality but otherwise are
@ -191,12 +203,28 @@ export interface DiscoveredPlugin {
* not required for this plugin to work properly. * not required for this plugin to work properly.
*/ */
readonly optionalPlugins: readonly PluginName[]; readonly optionalPlugins: readonly PluginName[];
/**
* List of plugin ids that this plugin's UI code imports modules from that are
* not in `requiredPlugins`.
*
* @remarks
* The plugins listed here will be loaded in the browser, even if the plugin is
* disabled. Required by `@kbn/optimizer` to support cross-plugin imports.
* "core" and plugins already listed in `requiredPlugins` do not need to be
* duplicated here.
*/
readonly requiredBundles: readonly PluginName[];
} }
/** /**
* @internal * @internal
*/ */
export interface InternalPluginInfo { export interface InternalPluginInfo {
/**
* Bundles that must be loaded for this plugoin
*/
readonly requiredBundles: readonly string[];
/** /**
* Path to the target/public directory of the plugin which should be * Path to the target/public directory of the plugin which should be
* served * served

View file

@ -637,6 +637,7 @@ export interface DiscoveredPlugin {
readonly configPath: ConfigPath; readonly configPath: ConfigPath;
readonly id: PluginName; readonly id: PluginName;
readonly optionalPlugins: readonly PluginName[]; readonly optionalPlugins: readonly PluginName[];
readonly requiredBundles: readonly PluginName[];
readonly requiredPlugins: readonly PluginName[]; readonly requiredPlugins: readonly PluginName[];
} }
@ -1684,6 +1685,7 @@ export interface PluginManifest {
readonly id: PluginName; readonly id: PluginName;
readonly kibanaVersion: string; readonly kibanaVersion: string;
readonly optionalPlugins: readonly PluginName[]; readonly optionalPlugins: readonly PluginName[];
readonly requiredBundles: readonly string[];
readonly requiredPlugins: readonly PluginName[]; readonly requiredPlugins: readonly PluginName[];
readonly server: boolean; readonly server: boolean;
readonly ui: boolean; readonly ui: boolean;
@ -2706,8 +2708,8 @@ export const validBodyOutput: readonly ["data", "stream"];
// src/core/server/legacy/types.ts:165:3 - (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts // src/core/server/legacy/types.ts:165:3 - (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts
// src/core/server/legacy/types.ts:166:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts // src/core/server/legacy/types.ts:166:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts
// src/core/server/legacy/types.ts:167:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts // src/core/server/legacy/types.ts:167:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:238:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:266:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:238:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:266:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:240:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:268:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts
``` ```

View file

@ -150,7 +150,23 @@ export function uiRenderMixin(kbnServer, server, config) {
]), ]),
]; ];
const kpPluginIds = Array.from(kbnServer.newPlatform.__internals.uiPlugins.public.keys()); const kpUiPlugins = kbnServer.newPlatform.__internals.uiPlugins;
const kpPluginPublicPaths = new Map();
const kpPluginBundlePaths = new Set();
// recursively iterate over the kpUiPlugin ids and their required bundles
// to populate kpPluginPublicPaths and kpPluginBundlePaths
(function readKpPlugins(ids) {
for (const id of ids) {
if (kpPluginPublicPaths.has(id)) {
continue;
}
kpPluginPublicPaths.set(id, `${regularBundlePath}/plugin/${id}/`);
kpPluginBundlePaths.add(`${regularBundlePath}/plugin/${id}/${id}.plugin.js`);
readKpPlugins(kpUiPlugins.internal.get(id).requiredBundles);
}
})(kpUiPlugins.public.keys());
const jsDependencyPaths = [ const jsDependencyPaths = [
...UiSharedDeps.jsDepFilenames.map( ...UiSharedDeps.jsDepFilenames.map(
@ -160,9 +176,7 @@ export function uiRenderMixin(kbnServer, server, config) {
...(isCore ? [] : [`${dllBundlePath}/vendors_runtime.bundle.dll.js`, ...dllJsChunks]), ...(isCore ? [] : [`${dllBundlePath}/vendors_runtime.bundle.dll.js`, ...dllJsChunks]),
`${regularBundlePath}/core/core.entry.js`, `${regularBundlePath}/core/core.entry.js`,
...kpPluginIds.map( ...kpPluginBundlePaths,
(pluginId) => `${regularBundlePath}/plugin/${pluginId}/${pluginId}.plugin.js`
),
]; ];
// These paths should align with the bundle routes configured in // These paths should align with the bundle routes configured in
@ -170,13 +184,7 @@ export function uiRenderMixin(kbnServer, server, config) {
const publicPathMap = JSON.stringify({ const publicPathMap = JSON.stringify({
core: `${regularBundlePath}/core/`, core: `${regularBundlePath}/core/`,
'kbn-ui-shared-deps': `${regularBundlePath}/kbn-ui-shared-deps/`, 'kbn-ui-shared-deps': `${regularBundlePath}/kbn-ui-shared-deps/`,
...kpPluginIds.reduce( ...Object.fromEntries(kpPluginPublicPaths),
(acc, pluginId) => ({
...acc,
[pluginId]: `${regularBundlePath}/plugin/${pluginId}/`,
}),
{}
),
}); });
const bootstrap = new AppBootstrap({ const bootstrap = new AppBootstrap({

View file

@ -3,5 +3,6 @@
"version": "kibana", "version": "kibana",
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["management"] "requiredPlugins": ["management"],
"requiredBundles": ["kibanaReact"]
} }

View file

@ -2,5 +2,6 @@
"id": "bfetch", "id": "bfetch",
"version": "kibana", "version": "kibana",
"server": true, "server": true,
"ui": true "ui": true,
"requiredBundles": ["kibanaUtils"]
} }

View file

@ -2,5 +2,6 @@
"id": "charts", "id": "charts",
"version": "kibana", "version": "kibana",
"server": true, "server": true,
"ui": true "ui": true,
"requiredBundles": ["kibanaUtils", "kibanaReact", "data"]
} }

View file

@ -4,5 +4,6 @@
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["devTools", "home"], "requiredPlugins": ["devTools", "home"],
"optionalPlugins": ["usageCollection"] "optionalPlugins": ["usageCollection"],
"requiredBundles": ["esUiShared", "kibanaReact", "kibanaUtils"]
} }

View file

@ -12,5 +12,6 @@
], ],
"optionalPlugins": ["home", "share", "usageCollection"], "optionalPlugins": ["home", "share", "usageCollection"],
"server": true, "server": true,
"ui": true "ui": true,
"requiredBundles": ["kibanaUtils", "kibanaReact", "home"]
} }

View file

@ -8,5 +8,11 @@
"uiActions" "uiActions"
], ],
"optionalPlugins": ["usageCollection"], "optionalPlugins": ["usageCollection"],
"extraPublicDirs": ["common", "common/utils/abort_utils"] "extraPublicDirs": ["common", "common/utils/abort_utils"],
"requiredBundles": [
"kibanaUtils",
"kibanaReact",
"kibanaLegacy",
"inspector"
]
} }

View file

@ -1,7 +1,6 @@
{ {
"id": "discover", "id": "discover",
"version": "kibana", "version": "kibana",
"optionalPlugins": ["share"],
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": [ "requiredPlugins": [
@ -14,5 +13,11 @@
"uiActions", "uiActions",
"visualizations" "visualizations"
], ],
"optionalPlugins": ["home", "share"] "optionalPlugins": ["home", "share"],
"requiredBundles": [
"kibanaUtils",
"home",
"savedObjects",
"kibanaReact"
]
} }

View file

@ -10,5 +10,9 @@
], ],
"extraPublicDirs": [ "extraPublicDirs": [
"public/lib/test_samples" "public/lib/test_samples"
],
"requiredBundles": [
"savedObjects",
"kibanaReact"
] ]
} }

View file

@ -10,5 +10,8 @@
"static/forms/helpers", "static/forms/helpers",
"static/forms/components", "static/forms/components",
"static/forms/helpers/field_validators/types" "static/forms/helpers/field_validators/types"
],
"requiredBundles": [
"data"
] ]
} }

View file

@ -6,5 +6,10 @@
"requiredPlugins": [ "requiredPlugins": [
"bfetch" "bfetch"
], ],
"extraPublicDirs": ["common", "common/fonts"] "extraPublicDirs": ["common", "common/fonts"],
"requiredBundles": [
"kibanaUtils",
"inspector",
"data"
]
} }

View file

@ -4,5 +4,8 @@
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["data", "kibanaLegacy"], "requiredPlugins": ["data", "kibanaLegacy"],
"optionalPlugins": ["usageCollection", "telemetry"] "optionalPlugins": ["usageCollection", "telemetry"],
"requiredBundles": [
"kibanaReact"
]
} }

View file

@ -3,5 +3,6 @@
"version": "kibana", "version": "kibana",
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["management", "data", "kibanaLegacy"] "requiredPlugins": ["management", "data", "kibanaLegacy"],
"requiredBundles": ["kibanaReact", "kibanaUtils"]
} }

View file

@ -4,5 +4,6 @@
"kibanaVersion": "kibana", "kibanaVersion": "kibana",
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["data", "expressions", "visualizations"] "requiredPlugins": ["data", "expressions", "visualizations"],
"requiredBundles": ["kibanaReact"]
} }

View file

@ -3,5 +3,6 @@
"version": "kibana", "version": "kibana",
"server": false, "server": false,
"ui": true, "ui": true,
"extraPublicDirs": ["common", "common/adapters/request"] "extraPublicDirs": ["common", "common/adapters/request"],
"requiredBundles": ["kibanaReact"]
} }

View file

@ -1,5 +1,6 @@
{ {
"id": "kibanaReact", "id": "kibanaReact",
"version": "kibana", "version": "kibana",
"ui": true "ui": true,
"requiredBundles": ["kibanaUtils"]
} }

View file

@ -3,5 +3,6 @@
"version": "kibana", "version": "kibana",
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["kibanaLegacy", "home"] "requiredPlugins": ["kibanaLegacy", "home"],
"requiredBundles": ["kibanaReact"]
} }

View file

@ -4,5 +4,6 @@
"kibanaVersion": "kibana", "kibanaVersion": "kibana",
"configPath": ["map"], "configPath": ["map"],
"ui": true, "ui": true,
"server": true "server": true,
"requiredBundles": ["kibanaReact", "charts"]
} }

View file

@ -11,5 +11,10 @@
"mapsLegacy", "mapsLegacy",
"kibanaLegacy", "kibanaLegacy",
"data" "data"
],
"requiredBundles": [
"kibanaUtils",
"kibanaReact",
"charts"
] ]
} }

View file

@ -3,5 +3,9 @@
"version": "kibana", "version": "kibana",
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["data"] "requiredPlugins": ["data"],
"requiredBundles": [
"kibanaUtils",
"kibanaReact"
]
} }

View file

@ -5,5 +5,6 @@
"ui": true, "ui": true,
"requiredPlugins": ["home", "management", "data"], "requiredPlugins": ["home", "management", "data"],
"optionalPlugins": ["dashboard", "visualizations", "discover"], "optionalPlugins": ["dashboard", "visualizations", "discover"],
"extraPublicDirs": ["public/lib"] "extraPublicDirs": ["public/lib"],
"requiredBundles": ["kibanaReact"]
} }

View file

@ -2,5 +2,6 @@
"id": "share", "id": "share",
"version": "kibana", "version": "kibana",
"server": true, "server": true,
"ui": true "ui": true,
"requiredBundles": ["kibanaUtils"]
} }

View file

@ -9,5 +9,9 @@
], ],
"extraPublicDirs": [ "extraPublicDirs": [
"common/constants" "common/constants"
],
"requiredBundles": [
"kibanaUtils",
"kibanaReact"
] ]
} }

View file

@ -11,5 +11,10 @@
"mapsLegacy", "mapsLegacy",
"kibanaLegacy", "kibanaLegacy",
"data" "data"
],
"requiredBundles": [
"kibanaUtils",
"kibanaReact",
"charts"
] ]
} }

View file

@ -5,5 +5,8 @@
"ui": true, "ui": true,
"extraPublicDirs": [ "extraPublicDirs": [
"public/tests/test_samples" "public/tests/test_samples"
],
"requiredBundles": [
"kibanaReact"
] ]
} }

View file

@ -3,5 +3,8 @@
"configPath": ["usageCollection"], "configPath": ["usageCollection"],
"version": "kibana", "version": "kibana",
"server": true, "server": true,
"ui": true "ui": true,
"requiredBundles": [
"kibanaUtils"
]
} }

View file

@ -3,5 +3,6 @@
"version": "kibana", "version": "kibana",
"ui": true, "ui": true,
"server": true, "server": true,
"requiredPlugins": ["expressions", "visualizations"] "requiredPlugins": ["expressions", "visualizations"],
"requiredBundles": ["kibanaUtils", "kibanaReact", "data", "charts"]
} }

View file

@ -4,5 +4,6 @@
"kibanaVersion": "kibana", "kibanaVersion": "kibana",
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["data", "visualizations", "charts","expressions"] "requiredPlugins": ["data", "visualizations", "charts","expressions"],
"requiredBundles": ["kibanaUtils", "kibanaReact"]
} }

View file

@ -8,5 +8,11 @@
"visualizations", "visualizations",
"data", "data",
"kibanaLegacy" "kibanaLegacy"
],
"requiredBundles": [
"kibanaUtils",
"kibanaReact",
"share",
"charts"
] ]
} }

View file

@ -3,5 +3,6 @@
"version": "kibana", "version": "kibana",
"ui": true, "ui": true,
"server": true, "server": true,
"requiredPlugins": ["data", "expressions", "visualizations", "charts"] "requiredPlugins": ["data", "expressions", "visualizations", "charts"],
"requiredBundles": ["kibanaUtils", "kibanaReact"]
} }

View file

@ -4,5 +4,6 @@
"kibanaVersion": "kibana", "kibanaVersion": "kibana",
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["visualizations", "data", "expressions"] "requiredPlugins": ["visualizations", "data", "expressions"],
"requiredBundles": ["kibanaUtils", "kibanaReact"]
} }

View file

@ -5,5 +5,6 @@
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["charts", "data", "expressions", "visualizations"], "requiredPlugins": ["charts", "data", "expressions", "visualizations"],
"optionalPlugins": ["usageCollection"] "optionalPlugins": ["usageCollection"],
"requiredBundles": ["kibanaUtils", "kibanaReact"]
} }

View file

@ -3,5 +3,6 @@
"version": "kibana", "version": "kibana",
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["data", "visualizations", "mapsLegacy", "expressions"] "requiredPlugins": ["data", "visualizations", "mapsLegacy", "expressions"],
"requiredBundles": ["kibanaUtils", "kibanaReact"]
} }

View file

@ -4,5 +4,6 @@
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["charts", "data", "expressions", "visualizations", "kibanaLegacy"], "requiredPlugins": ["charts", "data", "expressions", "visualizations", "kibanaLegacy"],
"optionalPlugins": ["visTypeXy"] "optionalPlugins": ["visTypeXy"],
"requiredBundles": ["kibanaUtils", "kibanaReact"]
} }

View file

@ -3,5 +3,6 @@
"version": "kibana", "version": "kibana",
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["data", "expressions", "uiActions", "embeddable", "usageCollection", "inspector"] "requiredPlugins": ["data", "expressions", "uiActions", "embeddable", "usageCollection", "inspector"],
"requiredBundles": ["kibanaUtils", "discover", "savedObjects"]
} }

View file

@ -11,5 +11,11 @@
"visualizations", "visualizations",
"embeddable" "embeddable"
], ],
"optionalPlugins": ["home", "share"] "optionalPlugins": ["home", "share"],
"requiredBundles": [
"kibanaUtils",
"kibanaReact",
"home",
"discover"
]
} }

View file

@ -9,5 +9,8 @@
"expressions" "expressions"
], ],
"server": false, "server": false,
"ui": true "ui": true,
"requiredBundles": [
"inspector"
]
} }

View file

@ -3,5 +3,6 @@
"version": "0.0.1", "version": "0.0.1",
"kibanaVersion": "kibana", "kibanaVersion": "kibana",
"server": false, "server": false,
"ui": true "ui": true,
"requiredBundles": ["kibanaReact"]
} }

View file

@ -5,5 +5,6 @@
"configPath": ["kbn_sample_panel_action"], "configPath": ["kbn_sample_panel_action"],
"server": false, "server": false,
"ui": true, "ui": true,
"requiredPlugins": ["uiActions", "embeddable"] "requiredPlugins": ["uiActions", "embeddable"],
} "requiredBundles": ["kibanaReact"]
}

View file

@ -2,16 +2,10 @@
source src/dev/ci_setup/setup_env.sh source src/dev/ci_setup/setup_env.sh
echo " -> building examples separate from test plugins" echo " -> building kibana platform plugins"
node scripts/build_kibana_platform_plugins \ node scripts/build_kibana_platform_plugins \
--oss \ --oss \
--examples \ --filter '!alertingExample' \
--verbose;
echo " -> building test plugins"
node scripts/build_kibana_platform_plugins \
--oss \
--no-examples \
--scan-dir "$KIBANA_DIR/test/plugin_functional/plugins" \ --scan-dir "$KIBANA_DIR/test/plugin_functional/plugins" \
--scan-dir "$KIBANA_DIR/test/interpreter_functional/plugins" \ --scan-dir "$KIBANA_DIR/test/interpreter_functional/plugins" \
--verbose; --verbose;

View file

@ -3,14 +3,8 @@
cd "$KIBANA_DIR" cd "$KIBANA_DIR"
source src/dev/ci_setup/setup_env.sh source src/dev/ci_setup/setup_env.sh
echo " -> building examples separate from test plugins" echo " -> building kibana platform plugins"
node scripts/build_kibana_platform_plugins \ node scripts/build_kibana_platform_plugins \
--examples \
--verbose;
echo " -> building test plugins"
node scripts/build_kibana_platform_plugins \
--no-examples \
--scan-dir "$KIBANA_DIR/test/plugin_functional/plugins" \ --scan-dir "$KIBANA_DIR/test/plugin_functional/plugins" \
--scan-dir "$XPACK_DIR/test/plugin_functional/plugins" \ --scan-dir "$XPACK_DIR/test/plugin_functional/plugins" \
--scan-dir "$XPACK_DIR/test/functional_with_es_ssl/fixtures/plugins" \ --scan-dir "$XPACK_DIR/test/functional_with_es_ssl/fixtures/plugins" \

View file

@ -6,5 +6,9 @@
"server": false, "server": false,
"ui": true, "ui": true,
"requiredPlugins": ["uiActionsEnhanced", "data", "discover"], "requiredPlugins": ["uiActionsEnhanced", "data", "discover"],
"optionalPlugins": [] "optionalPlugins": [],
"requiredBundles": [
"kibanaUtils",
"kibanaReact"
]
} }

View file

@ -28,5 +28,10 @@
], ],
"extraPublicDirs": [ "extraPublicDirs": [
"public/style/variables" "public/style/variables"
],
"requiredBundles": [
"kibanaReact",
"kibanaUtils",
"observability"
] ]
} }

View file

@ -6,5 +6,6 @@
"server": true, "server": true,
"ui": true, "ui": true,
"requiredPlugins": ["data", "embeddable", "expressions", "features", "home", "inspector", "uiActions"], "requiredPlugins": ["data", "embeddable", "expressions", "features", "home", "inspector", "uiActions"],
"optionalPlugins": ["usageCollection"] "optionalPlugins": ["usageCollection"],
"requiredBundles": ["kibanaReact", "maps", "lens", "visualizations", "kibanaUtils", "kibanaLegacy", "discover", "savedObjects", "reporting"]
} }

View file

@ -13,5 +13,10 @@
"optionalPlugins": [ "optionalPlugins": [
"usageCollection" "usageCollection"
], ],
"configPath": ["xpack", "ccr"] "configPath": ["xpack", "ccr"],
"requiredBundles": [
"kibanaReact",
"esUiShared",
"data"
]
} }

View file

@ -4,5 +4,10 @@
"server": false, "server": false,
"ui": true, "ui": true,
"requiredPlugins": ["data", "uiActionsEnhanced", "embeddable", "dashboard", "share"], "requiredPlugins": ["data", "uiActionsEnhanced", "embeddable", "dashboard", "share"],
"configPath": ["xpack", "dashboardEnhanced"] "configPath": ["xpack", "dashboardEnhanced"],
"requiredBundles": [
"kibanaUtils",
"embeddableEnhanced",
"kibanaReact"
]
} }

View file

@ -10,5 +10,6 @@
], ],
"optionalPlugins": ["kibanaReact", "kibanaUtils"], "optionalPlugins": ["kibanaReact", "kibanaUtils"],
"server": true, "server": true,
"ui": true "ui": true,
"requiredBundles": ["kibanaReact", "kibanaUtils"]
} }

View file

@ -6,5 +6,6 @@
"ui": true, "ui": true,
"requiredPlugins": ["uiActions", "embeddable", "discover"], "requiredPlugins": ["uiActions", "embeddable", "discover"],
"optionalPlugins": ["share"], "optionalPlugins": ["share"],
"configPath": ["xpack", "discoverEnhanced"] "configPath": ["xpack", "discoverEnhanced"],
"requiredBundles": ["kibanaUtils", "data"]
} }

View file

@ -6,5 +6,6 @@
"ui": true, "ui": true,
"requiredPlugins": ["licensing", "data", "navigation", "savedObjects", "kibanaLegacy"], "requiredPlugins": ["licensing", "data", "navigation", "savedObjects", "kibanaLegacy"],
"optionalPlugins": ["home", "features"], "optionalPlugins": ["home", "features"],
"configPath": ["xpack", "graph"] "configPath": ["xpack", "graph"],
"requiredBundles": ["kibanaUtils", "kibanaReact", "home"]
} }

View file

@ -9,5 +9,8 @@
], ],
"server": true, "server": true,
"ui": true, "ui": true,
"configPath": ["xpack", "grokdebugger"] "configPath": ["xpack", "grokdebugger"],
"requiredBundles": [
"kibanaReact"
]
} }

View file

@ -12,5 +12,10 @@
"usageCollection", "usageCollection",
"indexManagement" "indexManagement"
], ],
"configPath": ["xpack", "ilm"] "configPath": ["xpack", "ilm"],
"requiredBundles": [
"indexManagement",
"kibanaReact",
"esUiShared"
]
} }

Some files were not shown because too many files have changed in this diff Show more