Do not serve legacy JS when serving a Kibana Platform applicat… (#61011)

This commit is contained in:
Josh Dover 2020-04-22 11:14:21 -06:00 committed by GitHub
parent e708b415c9
commit fc1f4f2695
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 336 additions and 233 deletions

View file

@ -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

View file

@ -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];

View file

@ -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',

View file

@ -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(

View file

@ -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),
],
},
],
}

View file

@ -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,

View file

@ -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(

View file

@ -34,6 +34,7 @@ export function runKbnOptimizer(opts: Record<string, any>, config: LegacyConfig)
const optimizerConfig = OptimizerConfig.create({
repoRoot: REPO_ROOT,
watch: true,
includeCoreBundle: true,
oss: !!opts.oss,
examples: !!opts.runExamples,
pluginPaths: config.get('plugins.paths'),

View file

@ -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';

View file

@ -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,
});
});

View file

@ -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,
});
}

View file

@ -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();
});

View file

@ -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';

View file

@ -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`] = `
<div
class="ng-scope"
/>
`;
exports[`#stop() does nothing if angular was not bootstrapped to targetDomElement 1`] = `
<div>
<h1>
this should not be removed
</h1>
</div>
`;

View file

@ -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,
});
legacyPlatform.setup(defaultSetupDeps);
legacyPlatform.start(defaultStartDeps);
expect(mockUiTestHarnessBootstrap).not.toHaveBeenCalled();
expect(mockUiChromeBootstrap).toHaveBeenCalledTimes(1);
expect(mockUiChromeBootstrap).toHaveBeenCalledWith(defaultStartDeps.targetDomElement);
it('passes the targetDomElement to legacy bootstrap module', () => {
const legacyPlatform = new LegacyPlatformService({
...defaultParams,
});
});
describe('useLegacyTestHarness = true', () => {
it('passes the targetDomElement to ui/test_harness', () => {
const legacyPlatform = new LegacyPlatformService({
...defaultParams,
useLegacyTestHarness: true,
});
legacyPlatform.setup(defaultSetupDeps);
legacyPlatform.start(defaultStartDeps);
legacyPlatform.setup(defaultSetupDeps);
legacyPlatform.start(defaultStartDeps);
expect(mockUiChromeBootstrap).not.toHaveBeenCalled();
expect(mockUiTestHarnessBootstrap).toHaveBeenCalledTimes(1);
expect(mockUiTestHarnessBootstrap).toHaveBeenCalledWith(defaultStartDeps.targetDomElement);
});
expect(mockUiChromeBootstrap).toHaveBeenCalledTimes(1);
expect(mockUiChromeBootstrap).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();
it('loads ui/modules before ui/chrome, and both before legacy files', () => {
const legacyPlatform = new LegacyPlatformService({
...defaultParams,
});
});
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([]);
expect(mockLoadOrder).toEqual([]);
legacyPlatform.setup(defaultSetupDeps);
legacyPlatform.start(defaultStartDeps);
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(`
<div>
<h1>
this should not be removed
</h1>
</div>
`);
});
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(`
<div
class="ng-scope"
/>
`);
expect(scopeDestroySpy).toHaveBeenCalledTimes(1);
});
});

View file

@ -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<string, unknown>) => void;
__start__: (legacyCore: LegacyCoreStart, plugins: Record<string, unknown>) => 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[]) => {

View file

@ -13,7 +13,7 @@
display: flex;
flex-flow: column nowrap;
position: absolute;
left: $kbnGlobalNavClosedWidth;
left: 0;
top: 0;
right: 0;
bottom: 0;

View file

@ -0,0 +1 @@
@import './base';

View file

@ -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 = <T extends Record<string, any>, U extends Record<string, any>
...{ [keysMap[key] || key]: obj[key] },
};
}, {});
const isAutoCreateIndexError = (error: any) => {
return (
error?.res?.status === 503 && error?.body?.attributes?.code === 'ES_AUTO_CREATE_INDEX_ERROR'
);
};

View file

@ -29,6 +29,7 @@ export const BuildKibanaPlatformPluginsTask = {
examples: false,
watch: false,
dist: true,
includeCoreBundle: true,
});
await runOptimizer(optimizerConfig)

View file

@ -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 ')}
}

View file

@ -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';

View file

@ -1,3 +0,0 @@
@import './variables';
@import './directives/index';

View file

@ -1,4 +0,0 @@
$kbnGlobalNavClosedWidth: 53px;
$kbnGlobalNavOpenWidth: 180px;
$kbnGlobalNavLogoHeight: 70px;
$kbnGlobalNavAppIconHeight: $euiSizeXXL + $euiSizeXS;

View file

@ -1 +0,0 @@
@import './kbn_chrome';

View file

@ -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

View file

@ -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({

View file

@ -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}}
])
]);
});
};
}
}

View file

@ -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`,
},
});

View file

@ -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/',

View file

@ -1,5 +1,3 @@
@import 'src/legacy/ui/public/chrome/variables';
@mixin gphSvgText() {
font-family: $euiFontFamily;
font-size: $euiSizeS;

View file

@ -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,

View file

@ -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';