diff --git a/packages/kbn-optimizer/src/cli.ts b/packages/kbn-optimizer/src/cli.ts index dcb4dcd35698d..c0bb408d60d8f 100644 --- a/packages/kbn-optimizer/src/cli.ts +++ b/packages/kbn-optimizer/src/cli.ts @@ -44,6 +44,11 @@ run( throw createFlagError('expected --cache to have no value'); } + const includeCoreBundle = flags.core ?? true; + if (typeof includeCoreBundle !== 'boolean') { + throw createFlagError('expected --core to have no value'); + } + const dist = flags.dist ?? false; if (typeof dist !== 'boolean') { throw createFlagError('expected --dist to have no value'); @@ -87,6 +92,7 @@ run( profileWebpack, extraPluginScanDirs, inspectWorkers, + includeCoreBundle, }); await runOptimizer(config) @@ -95,9 +101,10 @@ run( }, { flags: { - boolean: ['watch', 'oss', 'examples', 'dist', 'cache', 'profile', 'inspect-workers'], + boolean: ['core', 'watch', 'oss', 'examples', 'dist', 'cache', 'profile', 'inspect-workers'], string: ['workers', 'scan-dir'], default: { + core: true, examples: true, cache: true, 'inspect-workers': true, @@ -107,6 +114,7 @@ run( --workers max number of workers to use --oss only build oss plugins --profile profile the webpack builds and write stats.json files to build outputs + --no-core disable generating the core bundle --no-cache disable the cache --no-examples don't build the example plugins --dist create bundles that are suitable for inclusion in the Kibana distributable diff --git a/packages/kbn-optimizer/src/common/bundle.ts b/packages/kbn-optimizer/src/common/bundle.ts index f1bc0965a46cc..7581b90d60af2 100644 --- a/packages/kbn-optimizer/src/common/bundle.ts +++ b/packages/kbn-optimizer/src/common/bundle.ts @@ -23,7 +23,7 @@ import { BundleCache } from './bundle_cache'; import { UnknownVals } from './ts_helpers'; import { includes, ascending, entriesToObject } from './array_helpers'; -const VALID_BUNDLE_TYPES = ['plugin' as const]; +const VALID_BUNDLE_TYPES = ['plugin' as const, 'entry' as const]; export interface BundleSpec { readonly type: typeof VALID_BUNDLE_TYPES[0]; diff --git a/packages/kbn-optimizer/src/optimizer/get_bundles.test.ts b/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.test.ts similarity index 93% rename from packages/kbn-optimizer/src/optimizer/get_bundles.test.ts rename to packages/kbn-optimizer/src/optimizer/get_plugin_bundles.test.ts index 9d95d883d605c..36dc0ca64c6ca 100644 --- a/packages/kbn-optimizer/src/optimizer/get_bundles.test.ts +++ b/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.test.ts @@ -19,13 +19,13 @@ import { createAbsolutePathSerializer } from '@kbn/dev-utils'; -import { getBundles } from './get_bundles'; +import { getPluginBundles } from './get_plugin_bundles'; expect.addSnapshotSerializer(createAbsolutePathSerializer('/repo')); -it('returns a bundle for each plugin', () => { +it('returns a bundle for core and each plugin', () => { expect( - getBundles( + getPluginBundles( [ { directory: '/repo/plugins/foo', diff --git a/packages/kbn-optimizer/src/optimizer/get_bundles.ts b/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.ts similarity index 93% rename from packages/kbn-optimizer/src/optimizer/get_bundles.ts rename to packages/kbn-optimizer/src/optimizer/get_plugin_bundles.ts index 7cd7bf15317e0..4741cc3c30af7 100644 --- a/packages/kbn-optimizer/src/optimizer/get_bundles.ts +++ b/packages/kbn-optimizer/src/optimizer/get_plugin_bundles.ts @@ -23,7 +23,7 @@ import { Bundle } from '../common'; import { KibanaPlatformPlugin } from './kibana_platform_plugins'; -export function getBundles(plugins: KibanaPlatformPlugin[], repoRoot: string) { +export function getPluginBundles(plugins: KibanaPlatformPlugin[], repoRoot: string) { return plugins .filter(p => p.isUiPlugin) .map( diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts index cc564dd4a8387..d4152133f289d 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.test.ts @@ -19,7 +19,7 @@ jest.mock('./assign_bundles_to_workers.ts'); jest.mock('./kibana_platform_plugins.ts'); -jest.mock('./get_bundles.ts'); +jest.mock('./get_plugin_bundles.ts'); import Path from 'path'; import Os from 'os'; @@ -90,6 +90,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": true, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 2, "pluginPaths": Array [], @@ -114,6 +115,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": false, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 2, "pluginPaths": Array [], @@ -138,6 +140,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": true, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 2, "pluginPaths": Array [], @@ -164,6 +167,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": true, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 2, "pluginPaths": Array [], @@ -187,6 +191,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": true, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 2, "pluginPaths": Array [], @@ -210,6 +215,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": true, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 100, "pluginPaths": Array [], @@ -230,6 +236,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": false, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 100, "pluginPaths": Array [], @@ -250,6 +257,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": false, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 100, "pluginPaths": Array [], @@ -271,6 +279,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": false, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 100, "pluginPaths": Array [], @@ -292,6 +301,7 @@ describe('OptimizerConfig::parseOptions()', () => { Object { "cache": true, "dist": false, + "includeCoreBundle": false, "inspectWorkers": false, "maxWorkerCount": 100, "pluginPaths": Array [], @@ -314,7 +324,7 @@ describe('OptimizerConfig::create()', () => { .assignBundlesToWorkers; const findKibanaPlatformPlugins: jest.Mock = jest.requireMock('./kibana_platform_plugins.ts') .findKibanaPlatformPlugins; - const getBundles: jest.Mock = jest.requireMock('./get_bundles.ts').getBundles; + const getPluginBundles: jest.Mock = jest.requireMock('./get_plugin_bundles.ts').getPluginBundles; beforeEach(() => { if ('mock' in OptimizerConfig.parseOptions) { @@ -326,7 +336,7 @@ describe('OptimizerConfig::create()', () => { { config: Symbol('worker config 2') }, ]); findKibanaPlatformPlugins.mockReturnValue(Symbol('new platform plugins')); - getBundles.mockReturnValue(Symbol('bundles')); + getPluginBundles.mockReturnValue([Symbol('bundle1'), Symbol('bundle2')]); jest.spyOn(OptimizerConfig, 'parseOptions').mockImplementation((): any => ({ cache: Symbol('parsed cache'), @@ -348,7 +358,10 @@ describe('OptimizerConfig::create()', () => { expect(config).toMatchInlineSnapshot(` OptimizerConfig { - "bundles": Symbol(bundles), + "bundles": Array [ + Symbol(bundle1), + Symbol(bundle2), + ], "cache": Symbol(parsed cache), "dist": Symbol(parsed dist), "inspectWorkers": Symbol(parsed inspect workers), @@ -383,7 +396,7 @@ describe('OptimizerConfig::create()', () => { } `); - expect(getBundles.mock).toMatchInlineSnapshot(` + expect(getPluginBundles.mock).toMatchInlineSnapshot(` Object { "calls": Array [ Array [ @@ -400,7 +413,10 @@ describe('OptimizerConfig::create()', () => { "results": Array [ Object { "type": "return", - "value": Symbol(bundles), + "value": Array [ + Symbol(bundle1), + Symbol(bundle2), + ], }, ], } diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts index 7e1514058446b..d6336cf867470 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts @@ -23,7 +23,7 @@ import Os from 'os'; import { Bundle, WorkerConfig } from '../common'; import { findKibanaPlatformPlugins, KibanaPlatformPlugin } from './kibana_platform_plugins'; -import { getBundles } from './get_bundles'; +import { getPluginBundles } from './get_plugin_bundles'; function pickMaxWorkerCount(dist: boolean) { // don't break if cpus() returns nothing, or an empty array @@ -60,6 +60,9 @@ interface Options { pluginScanDirs?: string[]; /** absolute paths that should be added to the default scan dirs */ extraPluginScanDirs?: string[]; + + /** flag that causes the core bundle to be built along with plugins */ + includeCoreBundle?: boolean; } interface ParsedOptions { @@ -72,6 +75,7 @@ interface ParsedOptions { pluginPaths: string[]; pluginScanDirs: string[]; inspectWorkers: boolean; + includeCoreBundle: boolean; } export class OptimizerConfig { @@ -83,6 +87,7 @@ export class OptimizerConfig { const profileWebpack = !!options.profileWebpack; const inspectWorkers = !!options.inspectWorkers; const cache = options.cache !== false && !process.env.KBN_OPTIMIZER_NO_CACHE; + const includeCoreBundle = !!options.includeCoreBundle; const repoRoot = options.repoRoot; if (!Path.isAbsolute(repoRoot)) { @@ -134,13 +139,28 @@ export class OptimizerConfig { pluginScanDirs, pluginPaths, inspectWorkers, + includeCoreBundle, }; } static create(inputOptions: Options) { const options = OptimizerConfig.parseOptions(inputOptions); const plugins = findKibanaPlatformPlugins(options.pluginScanDirs, options.pluginPaths); - const bundles = getBundles(plugins, options.repoRoot); + const bundles = [ + ...(options.includeCoreBundle + ? [ + new Bundle({ + type: 'entry', + id: 'core', + entry: './public/entry_point', + sourceRoot: options.repoRoot, + contextDir: Path.resolve(options.repoRoot, 'src/core'), + outputDir: Path.resolve(options.repoRoot, 'src/core/target/public'), + }), + ] + : []), + ...getPluginBundles(plugins, options.repoRoot), + ]; return new OptimizerConfig( bundles, diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 2f81d92ec923c..bb58bd0a3f005 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -104,7 +104,7 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { output: { path: bundle.outputDir, - filename: '[name].plugin.js', + filename: `[name].${bundle.type}.js`, publicPath: PUBLIC_PATH_PLACEHOLDER, devtoolModuleFilenameTemplate: info => `/${bundle.type}:${bundle.id}/${Path.relative( diff --git a/src/cli/cluster/run_kbn_optimizer.ts b/src/cli/cluster/run_kbn_optimizer.ts index 7752d4a45ab65..b811fc1f6b294 100644 --- a/src/cli/cluster/run_kbn_optimizer.ts +++ b/src/cli/cluster/run_kbn_optimizer.ts @@ -34,6 +34,7 @@ export function runKbnOptimizer(opts: Record, config: LegacyConfig) const optimizerConfig = OptimizerConfig.create({ repoRoot: REPO_ROOT, watch: true, + includeCoreBundle: true, oss: !!opts.oss, examples: !!opts.runExamples, pluginPaths: config.get('plugins.paths'), diff --git a/src/core/public/core.css b/src/core/public/_core.scss similarity index 100% rename from src/core/public/core.css rename to src/core/public/_core.scss diff --git a/src/core/public/application/ui/app_router.tsx b/src/core/public/application/ui/app_router.tsx index 61c8bc3cadae5..4c135c5769067 100644 --- a/src/core/public/application/ui/app_router.tsx +++ b/src/core/public/application/ui/app_router.tsx @@ -21,7 +21,7 @@ import React, { FunctionComponent, useMemo } from 'react'; import { Route, RouteComponentProps, Router, Switch } from 'react-router-dom'; import { History } from 'history'; import { Observable } from 'rxjs'; -import { useObservable } from 'react-use'; +import useObservable from 'react-use/lib/useObservable'; import { AppLeaveHandler, AppStatus, Mounter } from '../types'; import { AppContainer } from './app_container'; diff --git a/src/core/public/core_system.test.ts b/src/core/public/core_system.test.ts index 94fa74f4bd861..a42719417a2b1 100644 --- a/src/core/public/core_system.test.ts +++ b/src/core/public/core_system.test.ts @@ -59,7 +59,6 @@ const defaultCoreSystemParams = { warnLegacyBrowsers: true, }, } as any, - requireLegacyFiles: jest.fn(), }; beforeEach(() => { @@ -104,19 +103,22 @@ describe('constructor', () => { }); }); - it('passes requireLegacyFiles, useLegacyTestHarness, and a dom element to LegacyPlatformService', () => { + it('passes required params to LegacyPlatformService', () => { const requireLegacyFiles = { requireLegacyFiles: true }; - const useLegacyTestHarness = { useLegacyTestHarness: true }; + const requireLegacyBootstrapModule = { requireLegacyBootstrapModule: true }; + const requireNewPlatformShimModule = { requireNewPlatformShimModule: true }; createCoreSystem({ requireLegacyFiles, - useLegacyTestHarness, + requireLegacyBootstrapModule, + requireNewPlatformShimModule, }); expect(LegacyPlatformServiceConstructor).toHaveBeenCalledTimes(1); expect(LegacyPlatformServiceConstructor).toHaveBeenCalledWith({ requireLegacyFiles, - useLegacyTestHarness, + requireLegacyBootstrapModule, + requireNewPlatformShimModule, }); }); diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index 5c10d89459128..e58114b69dcc1 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -17,8 +17,6 @@ * under the License. */ -import './core.css'; - import { CoreId } from '../server'; import { PackageInfo, EnvironmentMode } from '../server/types'; import { CoreSetup, CoreStart } from '.'; @@ -50,8 +48,9 @@ interface Params { rootDomElement: HTMLElement; browserSupportsCsp: boolean; injectedMetadata: InjectedMetadataParams['injectedMetadata']; - requireLegacyFiles: LegacyPlatformParams['requireLegacyFiles']; - useLegacyTestHarness?: LegacyPlatformParams['useLegacyTestHarness']; + requireLegacyFiles?: LegacyPlatformParams['requireLegacyFiles']; + requireLegacyBootstrapModule?: LegacyPlatformParams['requireLegacyBootstrapModule']; + requireNewPlatformShimModule?: LegacyPlatformParams['requireNewPlatformShimModule']; } /** @internal */ @@ -111,7 +110,8 @@ export class CoreSystem { browserSupportsCsp, injectedMetadata, requireLegacyFiles, - useLegacyTestHarness, + requireLegacyBootstrapModule, + requireNewPlatformShimModule, } = params; this.rootDomElement = rootDomElement; @@ -145,7 +145,8 @@ export class CoreSystem { this.legacy = new LegacyPlatformService({ requireLegacyFiles, - useLegacyTestHarness, + requireLegacyBootstrapModule, + requireNewPlatformShimModule, }); } diff --git a/src/core/public/entry_point.ts b/src/core/public/entry_point.ts new file mode 100644 index 0000000000000..9461acccf30b9 --- /dev/null +++ b/src/core/public/entry_point.ts @@ -0,0 +1,59 @@ +/* + * 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. + */ + +/** + * This is the entry point used to boot the frontend when serving a application + * that lives in the Kibana Platform. + * + * Any changes to this file should be kept in sync with + * src/legacy/ui/ui_bundles/app_entry_template.js + */ + +import './index.scss'; +import { i18n } from '@kbn/i18n'; +import { CoreSystem } from './core_system'; + +const injectedMetadata = JSON.parse( + document.querySelector('kbn-injected-metadata')!.getAttribute('data')! +); + +if (process.env.IS_KIBANA_DISTRIBUTABLE !== 'true' && process.env.ELASTIC_APM_ACTIVE === 'true') { + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { init } = require('@elastic/apm-rum'); + init(injectedMetadata.vars.apmConfig); +} + +i18n + .load(injectedMetadata.i18n.translationsUrl) + .catch(e => e) + .then(async i18nError => { + const coreSystem = new CoreSystem({ + injectedMetadata, + rootDomElement: document.body, + browserSupportsCsp: !(window as any).__kbnCspNotEnforced__, + }); + + const setup = await coreSystem.setup(); + if (i18nError && setup) { + setup.fatalErrors.add(i18nError); + } + + await coreSystem.start(); + }); diff --git a/src/core/public/index.scss b/src/core/public/index.scss index 86f2efdff7702..4be46899cff67 100644 --- a/src/core/public/index.scss +++ b/src/core/public/index.scss @@ -1,11 +1,11 @@ -// Functions need to be first, since we use them in our variables and mixin definitions -@import '@elastic/eui/src/global_styling/functions/index'; - -// Variables come next, and are used in some mixins -@import '@elastic/eui/src/global_styling/variables/index'; - -// Mixins provide generic code expansion through helpers -@import '@elastic/eui/src/global_styling/mixins/index'; +// This file is built by both the legacy and KP build systems so we need to +// import this explicitly +@import '../../legacy/ui/public/styles/_styling_constants'; +@import './core'; @import './chrome/index'; @import './overlays/index'; +@import './rendering/index'; + +// Global styles need to be migrated +@import '../../legacy/ui/public/styles/_legacy/_index'; diff --git a/src/core/public/legacy/__snapshots__/legacy_service.test.ts.snap b/src/core/public/legacy/__snapshots__/legacy_service.test.ts.snap deleted file mode 100644 index 97629fdd1add5..0000000000000 --- a/src/core/public/legacy/__snapshots__/legacy_service.test.ts.snap +++ /dev/null @@ -1,35 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`#start() load order useLegacyTestHarness = false loads ui/modules before ui/chrome, and both before legacy files 1`] = ` -Array [ - "ui/new_platform", - "ui/chrome", - "legacy files", -] -`; - -exports[`#start() load order useLegacyTestHarness = true loads ui/modules before ui/test_harness, and both before legacy files 1`] = ` -Array [ - "ui/new_platform", - "ui/test_harness", - "legacy files", -] -`; - -exports[`#stop() destroys the angular scope and empties the targetDomElement if angular is bootstrapped to targetDomElement 1`] = ` -
-`; - -exports[`#stop() does nothing if angular was not bootstrapped to targetDomElement 1`] = ` -
- - -

- this should not be removed -

- - -
-`; diff --git a/src/core/public/legacy/legacy_service.test.ts b/src/core/public/legacy/legacy_service.test.ts index c3de645c6b17e..fa29320aab4e6 100644 --- a/src/core/public/legacy/legacy_service.test.ts +++ b/src/core/public/legacy/legacy_service.test.ts @@ -19,34 +19,6 @@ import angular from 'angular'; -const mockLoadOrder: string[] = []; - -const mockUiNewPlatformSetup = jest.fn(); -const mockUiNewPlatformStart = jest.fn(); -jest.mock('ui/new_platform', () => { - mockLoadOrder.push('ui/new_platform'); - return { - __setup__: mockUiNewPlatformSetup, - __start__: mockUiNewPlatformStart, - }; -}); - -const mockUiChromeBootstrap = jest.fn(); -jest.mock('ui/chrome', () => { - mockLoadOrder.push('ui/chrome'); - return { - bootstrap: mockUiChromeBootstrap, - }; -}); - -const mockUiTestHarnessBootstrap = jest.fn(); -jest.mock('ui/test_harness', () => { - mockLoadOrder.push('ui/test_harness'); - return { - bootstrap: mockUiTestHarnessBootstrap, - }; -}); - import { chromeServiceMock } from '../chrome/chrome_service.mock'; import { fatalErrorsServiceMock } from '../fatal_errors/fatal_errors_service.mock'; import { httpServiceMock } from '../http/http_service.mock'; @@ -69,10 +41,24 @@ const injectedMetadataSetup = injectedMetadataServiceMock.createSetupContract(); const notificationsSetup = notificationServiceMock.createSetupContract(); const uiSettingsSetup = uiSettingsServiceMock.createSetupContract(); +const mockLoadOrder: string[] = []; +const mockUiNewPlatformSetup = jest.fn(); +const mockUiNewPlatformStart = jest.fn(); +const mockUiChromeBootstrap = jest.fn(); const defaultParams = { requireLegacyFiles: jest.fn(() => { mockLoadOrder.push('legacy files'); }), + requireLegacyBootstrapModule: jest.fn(() => { + mockLoadOrder.push('ui/chrome'); + return { + bootstrap: mockUiChromeBootstrap, + }; + }), + requireNewPlatformShimModule: jest.fn(() => ({ + __setup__: mockUiNewPlatformSetup, + __start__: mockUiNewPlatformStart, + })), }; const defaultSetupDeps = { @@ -128,7 +114,7 @@ afterEach(() => { describe('#setup()', () => { describe('default', () => { - it('initializes ui/new_platform with core APIs', () => { + it('initializes new platform shim module with core APIs', () => { const legacyPlatform = new LegacyPlatformService({ ...defaultParams, }); @@ -138,6 +124,21 @@ describe('#setup()', () => { expect(mockUiNewPlatformSetup).toHaveBeenCalledTimes(1); expect(mockUiNewPlatformSetup).toHaveBeenCalledWith(expect.any(Object), {}); }); + + it('throws error if requireNewPlatformShimModule is undefined', () => { + const legacyPlatform = new LegacyPlatformService({ + ...defaultParams, + requireNewPlatformShimModule: undefined, + }); + + expect(() => { + legacyPlatform.setup(defaultSetupDeps); + }).toThrowErrorMatchingInlineSnapshot( + `"requireNewPlatformShimModule must be specified when rendering a legacy application"` + ); + + expect(mockUiNewPlatformSetup).not.toHaveBeenCalled(); + }); }); }); @@ -171,6 +172,21 @@ describe('#start()', () => { expect(mockUiNewPlatformStart).toHaveBeenCalledWith(expect.any(Object), {}); }); + it('throws error if requireNewPlatformShimeModule is undefined', () => { + const legacyPlatform = new LegacyPlatformService({ + ...defaultParams, + requireNewPlatformShimModule: undefined, + }); + + expect(() => { + legacyPlatform.start(defaultStartDeps); + }).toThrowErrorMatchingInlineSnapshot( + `"requireNewPlatformShimModule must be specified when rendering a legacy application"` + ); + + expect(mockUiNewPlatformStart).not.toHaveBeenCalled(); + }); + it('resolves getStartServices with core and plugin APIs', async () => { const legacyPlatform = new LegacyPlatformService({ ...defaultParams, @@ -185,67 +201,35 @@ describe('#start()', () => { expect(pluginsStart).toBe(defaultStartDeps.plugins); }); - describe('useLegacyTestHarness = false', () => { - it('passes the targetDomElement to ui/chrome', () => { - const legacyPlatform = new LegacyPlatformService({ - ...defaultParams, - }); + it('passes the targetDomElement to legacy bootstrap module', () => { + const legacyPlatform = new LegacyPlatformService({ + ...defaultParams, + }); - legacyPlatform.setup(defaultSetupDeps); - legacyPlatform.start(defaultStartDeps); + legacyPlatform.setup(defaultSetupDeps); + legacyPlatform.start(defaultStartDeps); - expect(mockUiTestHarnessBootstrap).not.toHaveBeenCalled(); - expect(mockUiChromeBootstrap).toHaveBeenCalledTimes(1); - expect(mockUiChromeBootstrap).toHaveBeenCalledWith(defaultStartDeps.targetDomElement); - }); + expect(mockUiChromeBootstrap).toHaveBeenCalledTimes(1); + expect(mockUiChromeBootstrap).toHaveBeenCalledWith(defaultStartDeps.targetDomElement); }); - describe('useLegacyTestHarness = true', () => { - it('passes the targetDomElement to ui/test_harness', () => { + describe('load order', () => { + it('loads ui/modules before ui/chrome, and both before legacy files', () => { const legacyPlatform = new LegacyPlatformService({ ...defaultParams, - useLegacyTestHarness: true, }); + expect(mockLoadOrder).toEqual([]); + legacyPlatform.setup(defaultSetupDeps); legacyPlatform.start(defaultStartDeps); - expect(mockUiChromeBootstrap).not.toHaveBeenCalled(); - expect(mockUiTestHarnessBootstrap).toHaveBeenCalledTimes(1); - expect(mockUiTestHarnessBootstrap).toHaveBeenCalledWith(defaultStartDeps.targetDomElement); - }); - }); - - describe('load order', () => { - describe('useLegacyTestHarness = false', () => { - it('loads ui/modules before ui/chrome, and both before legacy files', () => { - const legacyPlatform = new LegacyPlatformService({ - ...defaultParams, - }); - - expect(mockLoadOrder).toEqual([]); - - legacyPlatform.setup(defaultSetupDeps); - legacyPlatform.start(defaultStartDeps); - - expect(mockLoadOrder).toMatchSnapshot(); - }); - }); - - describe('useLegacyTestHarness = true', () => { - it('loads ui/modules before ui/test_harness, and both before legacy files', () => { - const legacyPlatform = new LegacyPlatformService({ - ...defaultParams, - useLegacyTestHarness: true, - }); - - expect(mockLoadOrder).toEqual([]); - - legacyPlatform.setup(defaultSetupDeps); - legacyPlatform.start(defaultStartDeps); - - expect(mockLoadOrder).toMatchSnapshot(); - }); + expect(mockLoadOrder).toMatchInlineSnapshot(` + Array [ + "ui/chrome", + "legacy files", + ] + `); }); }); }); @@ -262,7 +246,17 @@ describe('#stop()', () => { }); legacyPlatform.stop(); - expect(targetDomElement).toMatchSnapshot(); + expect(targetDomElement).toMatchInlineSnapshot(` +
+ + +

+ this should not be removed +

+ + +
+ `); }); it('destroys the angular scope and empties the targetDomElement if angular is bootstrapped to targetDomElement', async () => { @@ -291,7 +285,11 @@ describe('#stop()', () => { legacyPlatform.start({ ...defaultStartDeps, targetDomElement }); legacyPlatform.stop(); - expect(targetDomElement).toMatchSnapshot(); + expect(targetDomElement).toMatchInlineSnapshot(` +
+ `); expect(scopeDestroySpy).toHaveBeenCalledTimes(1); }); }); diff --git a/src/core/public/legacy/legacy_service.ts b/src/core/public/legacy/legacy_service.ts index 39ca7bdf54b7c..01837ba6f5940 100644 --- a/src/core/public/legacy/legacy_service.ts +++ b/src/core/public/legacy/legacy_service.ts @@ -25,8 +25,12 @@ import { LegacyCoreSetup, LegacyCoreStart, MountPoint } from '../'; /** @internal */ export interface LegacyPlatformParams { - requireLegacyFiles: () => void; - useLegacyTestHarness?: boolean; + requireLegacyFiles?: () => void; + requireLegacyBootstrapModule?: () => BootstrapModule; + requireNewPlatformShimModule?: () => { + __setup__: (legacyCore: LegacyCoreSetup, plugins: Record) => void; + __start__: (legacyCore: LegacyCoreStart, plugins: Record) => void; + }; } interface SetupDeps { @@ -92,7 +96,13 @@ export class LegacyPlatformService { // Inject parts of the new platform into parts of the legacy platform // so that legacy APIs/modules can mimic their new platform counterparts if (core.injectedMetadata.getLegacyMode()) { - require('ui/new_platform').__setup__(legacyCore, plugins); + if (!this.params.requireNewPlatformShimModule) { + throw new Error( + `requireNewPlatformShimModule must be specified when rendering a legacy application` + ); + } + + this.params.requireNewPlatformShimModule().__setup__(legacyCore, plugins); } } @@ -131,16 +141,29 @@ export class LegacyPlatformService { this.startDependencies$.next([legacyCore, plugins, {}]); + if (!this.params.requireNewPlatformShimModule) { + throw new Error( + `requireNewPlatformShimModule must be specified when rendering a legacy application` + ); + } + if (!this.params.requireLegacyBootstrapModule) { + throw new Error( + `requireLegacyBootstrapModule must be specified when rendering a legacy application` + ); + } + // Inject parts of the new platform into parts of the legacy platform // so that legacy APIs/modules can mimic their new platform counterparts - require('ui/new_platform').__start__(legacyCore, plugins); + this.params.requireNewPlatformShimModule().__start__(legacyCore, plugins); // Load the bootstrap module before loading the legacy platform files so that // the bootstrap module can modify the environment a bit first - this.bootstrapModule = this.loadBootstrapModule(); + this.bootstrapModule = this.params.requireLegacyBootstrapModule(); // require the files that will tie into the legacy platform - this.params.requireLegacyFiles(); + if (this.params.requireLegacyFiles) { + this.params.requireLegacyFiles(); + } if (!this.bootstrapModule) { throw new Error('Bootstrap module must be loaded before `start`'); @@ -172,20 +195,6 @@ export class LegacyPlatformService { // clear the inner html of the root angular element this.targetDomElement.textContent = ''; } - - private loadBootstrapModule(): BootstrapModule { - if (this.params.useLegacyTestHarness) { - // wrapped in NODE_ENV check so the `ui/test_harness` module - // is not included in the distributable - if (process.env.IS_KIBANA_DISTRIBUTABLE !== 'true') { - return require('ui/test_harness'); - } - - throw new Error('tests bundle is not available in the distributable'); - } - - return require('ui/chrome'); - } } const notSupported = (methodName: string) => (...args: any[]) => { diff --git a/src/legacy/ui/public/chrome/directives/_kbn_chrome.scss b/src/core/public/rendering/_base.scss similarity index 96% rename from src/legacy/ui/public/chrome/directives/_kbn_chrome.scss rename to src/core/public/rendering/_base.scss index b29a83848d291..ff28fc75e367d 100644 --- a/src/legacy/ui/public/chrome/directives/_kbn_chrome.scss +++ b/src/core/public/rendering/_base.scss @@ -13,7 +13,7 @@ display: flex; flex-flow: column nowrap; position: absolute; - left: $kbnGlobalNavClosedWidth; + left: 0; top: 0; right: 0; bottom: 0; diff --git a/src/core/public/rendering/_index.scss b/src/core/public/rendering/_index.scss new file mode 100644 index 0000000000000..c8567498b42ec --- /dev/null +++ b/src/core/public/rendering/_index.scss @@ -0,0 +1 @@ +@import './base'; diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index afc77806afb91..d26cadecb184a 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -28,12 +28,6 @@ import { SavedObjectsMigrationVersion, } from '../../server'; -// TODO: Migrate to an error modal powered by the NP? -import { - isAutoCreateIndexError, - showAutoCreateIndexErrorPage, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../legacy/ui/public/error_auto_create_index/error_auto_create_index'; import { SimpleSavedObject } from './simple_saved_object'; import { HttpFetchOptions, HttpSetup } from '../http'; @@ -226,7 +220,9 @@ export class SavedObjectsClient { .then(resp => this.createSavedObject(resp)) .catch((error: object) => { if (isAutoCreateIndexError(error)) { - showAutoCreateIndexErrorPage(); + window.location.assign( + this.http.basePath.prepend('/app/kibana#/error/action.auto_create_index') + ); } throw error; @@ -472,3 +468,9 @@ const renameKeys = , U extends Record ...{ [keysMap[key] || key]: obj[key] }, }; }, {}); + +const isAutoCreateIndexError = (error: any) => { + return ( + error?.res?.status === 503 && error?.body?.attributes?.code === 'ES_AUTO_CREATE_INDEX_ERROR' + ); +}; diff --git a/src/dev/build/tasks/build_kibana_platform_plugins.js b/src/dev/build/tasks/build_kibana_platform_plugins.js index 101d6bd15fc10..79cd698e4782c 100644 --- a/src/dev/build/tasks/build_kibana_platform_plugins.js +++ b/src/dev/build/tasks/build_kibana_platform_plugins.js @@ -29,6 +29,7 @@ export const BuildKibanaPlatformPluginsTask = { examples: false, watch: false, dist: true, + includeCoreBundle: true, }); await runOptimizer(optimizerConfig) diff --git a/src/legacy/core_plugins/tests_bundle/tests_entry_template.js b/src/legacy/core_plugins/tests_bundle/tests_entry_template.js index 3e3dc284671da..f075d8365c299 100644 --- a/src/legacy/core_plugins/tests_bundle/tests_entry_template.js +++ b/src/legacy/core_plugins/tests_bundle/tests_entry_template.js @@ -135,7 +135,16 @@ const coreSystem = new CoreSystem({ }, }, rootDomElement, - useLegacyTestHarness: true, + requireLegacyBootstrapModule: () => { + // wrapped in NODE_ENV check so the 'ui/test_harness' module + // is not included in the distributable + if (process.env.IS_KIBANA_DISTRIBUTABLE !== 'true') { + return require('ui/test_harness'); + } + + throw new Error('tests bundle is not available in the distributable'); + }, + requireNewPlatformShimModule: () => require('ui/new_platform'), requireLegacyFiles: () => { ${bundle.getRequires().join('\n ')} } diff --git a/src/legacy/ui/public/_index.scss b/src/legacy/ui/public/_index.scss index aaed52f8b120a..b36e62297cc23 100644 --- a/src/legacy/ui/public/_index.scss +++ b/src/legacy/ui/public/_index.scss @@ -9,7 +9,6 @@ // kbnChart__legend-isLoading @import './accessibility/index'; -@import './chrome/index'; @import './directives/index'; @import './error_auto_create_index/index'; @import './error_url_overflow/index'; diff --git a/src/legacy/ui/public/chrome/_index.scss b/src/legacy/ui/public/chrome/_index.scss deleted file mode 100644 index 7e6c3ebaccc5c..0000000000000 --- a/src/legacy/ui/public/chrome/_index.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import './variables'; - -@import './directives/index'; diff --git a/src/legacy/ui/public/chrome/_variables.scss b/src/legacy/ui/public/chrome/_variables.scss deleted file mode 100644 index 5097fe4c9bfae..0000000000000 --- a/src/legacy/ui/public/chrome/_variables.scss +++ /dev/null @@ -1,4 +0,0 @@ -$kbnGlobalNavClosedWidth: 53px; -$kbnGlobalNavOpenWidth: 180px; -$kbnGlobalNavLogoHeight: 70px; -$kbnGlobalNavAppIconHeight: $euiSizeXXL + $euiSizeXS; diff --git a/src/legacy/ui/public/chrome/directives/_index.scss b/src/legacy/ui/public/chrome/directives/_index.scss deleted file mode 100644 index 4d00b02279116..0000000000000 --- a/src/legacy/ui/public/chrome/directives/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './kbn_chrome'; diff --git a/src/legacy/ui/ui_bundles/app_entry_template.js b/src/legacy/ui/ui_bundles/app_entry_template.js index a1c3a153a196c..683fedd34316f 100644 --- a/src/legacy/ui/ui_bundles/app_entry_template.js +++ b/src/legacy/ui/ui_bundles/app_entry_template.js @@ -25,6 +25,9 @@ export const appEntryTemplate = bundle => ` * * This is programmatically created and updated, do not modify * + * Any changes to this file should be kept in sync with + * src/core/public/entry_point.ts + * * context: ${bundle.getContext()} */ @@ -45,7 +48,9 @@ i18n.load(injectedMetadata.i18n.translationsUrl) browserSupportsCsp: !window.__kbnCspNotEnforced__, requireLegacyFiles: () => { ${bundle.getRequires().join('\n ')} - } + }, + requireLegacyBootstrapModule: () => require('ui/chrome'), + requireNewPlatformShimModule: () => require('ui/new_platform'), }); coreSystem diff --git a/src/legacy/ui/ui_bundles/ui_bundles_controller.js b/src/legacy/ui/ui_bundles/ui_bundles_controller.js index 7afa283af83e0..79112fd687e84 100644 --- a/src/legacy/ui/ui_bundles/ui_bundles_controller.js +++ b/src/legacy/ui/ui_bundles/ui_bundles_controller.js @@ -99,13 +99,6 @@ export class UiBundlesController { this._postLoaders = []; this._bundles = []; - // create a bundle for core-only with no modules - this.add({ - id: 'core', - modules: [], - template: appEntryTemplate, - }); - // create a bundle for each uiApp for (const uiApp of uiApps) { this.add({ diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index 1093153edbbf7..7250fa4fc9eca 100644 --- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs +++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs @@ -69,26 +69,16 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { } load([ - {{#each sharedJsDepFilenames}} - '{{../regularBundlePath}}/kbn-ui-shared-deps/{{this}}', - {{/each}} - '{{regularBundlePath}}/kbn-ui-shared-deps/{{sharedJsFilename}}', - '{{dllBundlePath}}/vendors_runtime.bundle.dll.js', - {{#each dllJsChunks}} + {{#each jsDependencyPaths}} '{{this}}', {{/each}} - '{{regularBundlePath}}/commons.bundle.js', - {{!-- '{{regularBundlePath}}/plugin/data/data.plugin.js', --}} - '{{regularBundlePath}}/plugin/kibanaUtils/kibanaUtils.plugin.js', - '{{regularBundlePath}}/plugin/esUiShared/esUiShared.plugin.js', - '{{regularBundlePath}}/plugin/kibanaReact/kibanaReact.plugin.js' ], function () { load([ - '{{regularBundlePath}}/{{appId}}.bundle.js', + '{{entryBundlePath}}', {{#each styleSheetPaths}} '{{this}}', {{/each}} - ]) + ]); }); - }; + } } diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js index 0912d8683fc48..1e84405dd5153 100644 --- a/src/legacy/ui/ui_render/ui_render_mixin.js +++ b/src/legacy/ui/ui_render/ui_render_mixin.js @@ -103,41 +103,64 @@ export function uiRenderMixin(kbnServer, server, config) { const dllJsChunks = DllCompiler.getRawDllConfig().chunks.map( chunk => `${dllBundlePath}/vendors${chunk}.bundle.dll.js` ); + const styleSheetPaths = [ - ...dllStyleChunks, + ...(isCore ? [] : dllStyleChunks), `${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.baseCssDistFilename}`, ...(darkMode ? [ `${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.darkCssDistFilename}`, `${basePath}/node_modules/@kbn/ui-framework/dist/kui_dark.css`, + `${regularBundlePath}/dark_theme.style.css`, ] : [ `${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.lightCssDistFilename}`, `${basePath}/node_modules/@kbn/ui-framework/dist/kui_light.css`, + `${regularBundlePath}/light_theme.style.css`, ]), - `${regularBundlePath}/${darkMode ? 'dark' : 'light'}_theme.style.css`, `${regularBundlePath}/commons.style.css`, - ...(!isCore ? [`${regularBundlePath}/${app.getId()}.style.css`] : []), - ...kbnServer.uiExports.styleSheetPaths - .filter(path => path.theme === '*' || path.theme === (darkMode ? 'dark' : 'light')) - .map(path => - path.localPath.endsWith('.scss') - ? `${basePath}/built_assets/css/${path.publicPath}` - : `${basePath}/${path.publicPath}` - ) - .reverse(), + ...(isCore + ? [] + : [ + `${regularBundlePath}/${app.getId()}.style.css`, + ...kbnServer.uiExports.styleSheetPaths + .filter( + path => path.theme === '*' || path.theme === (darkMode ? 'dark' : 'light') + ) + .map(path => + path.localPath.endsWith('.scss') + ? `${basePath}/built_assets/css/${path.publicPath}` + : `${basePath}/${path.publicPath}` + ) + .reverse(), + ]), + ]; + + const jsDependencyPaths = [ + ...UiSharedDeps.jsDepFilenames.map( + filename => `${regularBundlePath}/kbn-ui-shared-deps/${filename}` + ), + `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.jsFilename}`, + ...(isCore + ? [] + : [ + `${dllBundlePath}/vendors_runtime.bundle.dll.js`, + ...dllJsChunks, + `${regularBundlePath}/commons.bundle.js`, + ]), + `${regularBundlePath}/plugin/kibanaUtils/kibanaUtils.plugin.js`, + `${regularBundlePath}/plugin/esUiShared/esUiShared.plugin.js`, + `${regularBundlePath}/plugin/kibanaReact/kibanaReact.plugin.js`, ]; const bootstrap = new AppBootstrap({ templateData: { - appId: isCore ? 'core' : app.getId(), - regularBundlePath, - dllBundlePath, - dllJsChunks, - styleSheetPaths, - sharedJsFilename: UiSharedDeps.jsFilename, - sharedJsDepFilenames: UiSharedDeps.jsDepFilenames, darkMode, + jsDependencyPaths, + styleSheetPaths, + entryBundlePath: isCore + ? `${regularBundlePath}/core/core.entry.js` + : `${regularBundlePath}/${app.getId()}.bundle.js`, }, }); diff --git a/src/optimize/bundles_route/bundles_route.js b/src/optimize/bundles_route/bundles_route.js index 0c2e98b5acd63..530dabb9a46a3 100644 --- a/src/optimize/bundles_route/bundles_route.js +++ b/src/optimize/bundles_route/bundles_route.js @@ -17,11 +17,12 @@ * under the License. */ -import { isAbsolute, extname } from 'path'; +import { isAbsolute, extname, join } from 'path'; import LruCache from 'lru-cache'; import * as UiSharedDeps from '@kbn/ui-shared-deps'; import { createDynamicAssetResponse } from './dynamic_asset_response'; import { assertIsNpUiPluginPublicDirs } from '../np_ui_plugin_public_dirs'; +import { fromRoot } from '../../core/server/utils'; /** * Creates the routes that serves files from `bundlesPath` or from @@ -85,6 +86,12 @@ export function createBundlesRoute({ fileHashCache ) ), + buildRouteForBundles( + `${basePublicPath}/bundles/core/`, + `/bundles/core/`, + fromRoot(join('src', 'core', 'target', 'public')), + fileHashCache + ), buildRouteForBundles( `${basePublicPath}/bundles/`, '/bundles/', diff --git a/x-pack/plugins/graph/public/angular/templates/_graph.scss b/x-pack/plugins/graph/public/angular/templates/_graph.scss index e6bd4693d1e9b..4ba65e7ec6b96 100644 --- a/x-pack/plugins/graph/public/angular/templates/_graph.scss +++ b/x-pack/plugins/graph/public/angular/templates/_graph.scss @@ -1,5 +1,3 @@ -@import 'src/legacy/ui/public/chrome/variables'; - @mixin gphSvgText() { font-family: $euiFontFamily; font-size: $euiSizeS; diff --git a/x-pack/plugins/graph/public/application.ts b/x-pack/plugins/graph/public/application.ts index f804265f1f5ab..fee42bdbeaf3b 100644 --- a/x-pack/plugins/graph/public/application.ts +++ b/x-pack/plugins/graph/public/application.ts @@ -12,6 +12,8 @@ import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import '../../../../webpackShims/ace'; // required for i18nIdDirective import 'angular-sanitize'; +// required for ngRoute +import 'angular-route'; // type imports import { AppMountContext, diff --git a/x-pack/plugins/graph/public/index.scss b/x-pack/plugins/graph/public/index.scss index f4e38de3e93a4..964ef320e4352 100644 --- a/x-pack/plugins/graph/public/index.scss +++ b/x-pack/plugins/graph/public/index.scss @@ -12,3 +12,5 @@ @import './main'; @import './angular/templates/index'; @import './components/index'; +// Local application mount wrapper styles +@import 'src/legacy/core_plugins/kibana/public/local_application_service/index';