From ab3adf8f0e53f8ca51048808d762b7f5025d5953 Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Wed, 22 Apr 2020 12:42:46 -0600 Subject: [PATCH] Use runtime publicPath for KP plugin bundles --- .../src/worker/public_path_loader.js | 27 +++++++++++++++++++ .../src/worker/webpack.config.ts | 27 ++++++++++++------- .../ui/ui_render/bootstrap/template.js.hbs | 1 + src/legacy/ui/ui_render/ui_render_mixin.js | 11 ++++++++ src/optimize/bundles_route/bundles_route.js | 15 ++++++++--- .../bundles_route/dynamic_asset_response.js | 6 +++-- 6 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 packages/kbn-optimizer/src/worker/public_path_loader.js diff --git a/packages/kbn-optimizer/src/worker/public_path_loader.js b/packages/kbn-optimizer/src/worker/public_path_loader.js new file mode 100644 index 0000000000000..464cf26ef1683 --- /dev/null +++ b/packages/kbn-optimizer/src/worker/public_path_loader.js @@ -0,0 +1,27 @@ +/* + * 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. + */ + +module.exports = function(source) { + const options = this.query; + if (!new RegExp(options.test).test(this.resourcePath)) { + return source; + } + + return `__webpack_public_path__ = window.__kbnPublicPath__['${options.key}'];\n${source}`; +}; diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index bb58bd0a3f005..0c3dfe63fa729 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -34,7 +34,6 @@ import { Bundle, WorkerConfig, parseDirPath, DisallowedSyntaxPlugin } from '../c const IS_CODE_COVERAGE = !!process.env.CODE_COVERAGE; const ISTANBUL_PRESET_PATH = require.resolve('@kbn/babel-preset/istanbul_preset'); -const PUBLIC_PATH_PLACEHOLDER = '__REPLACE_WITH_PUBLIC_PATH__'; const BABEL_PRESET_PATH = require.resolve('@kbn/babel-preset/webpack_preset'); const STATIC_BUNDLE_PLUGINS = [ @@ -105,7 +104,6 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { output: { path: bundle.outputDir, filename: `[name].${bundle.type}.js`, - publicPath: PUBLIC_PATH_PLACEHOLDER, devtoolModuleFilenameTemplate: info => `/${bundle.type}:${bundle.id}/${Path.relative( bundle.sourceRoot, @@ -268,15 +266,24 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { { test: /\.(js|tsx?)$/, exclude: /node_modules/, - use: { - loader: 'babel-loader', - options: { - babelrc: false, - presets: IS_CODE_COVERAGE - ? [ISTANBUL_PRESET_PATH, BABEL_PRESET_PATH] - : [BABEL_PRESET_PATH], + use: [ + { + loader: require.resolve('./public_path_loader.js'), + options: { + test: bundle.entry, + key: bundle.id, + }, }, - }, + { + loader: 'babel-loader', + options: { + babelrc: false, + presets: IS_CODE_COVERAGE + ? [ISTANBUL_PRESET_PATH, BABEL_PRESET_PATH] + : [BABEL_PRESET_PATH], + }, + }, + ], }, { test: /\.(html|md|txt|tmpl)$/, diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index 7250fa4fc9eca..8a71c6ccb1506 100644 --- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs +++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs @@ -1,6 +1,7 @@ var kbnCsp = JSON.parse(document.querySelector('kbn-csp').getAttribute('data')); window.__kbnStrictCsp__ = kbnCsp.strictCsp; window.__kbnDarkMode__ = {{darkMode}}; +window.__kbnPublicPath__ = {{publicPathMap}}; if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { var legacyBrowserError = document.getElementById('kbn_legacy_browser_error'); diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js index 1e84405dd5153..9e578dc012b97 100644 --- a/src/legacy/ui/ui_render/ui_render_mixin.js +++ b/src/legacy/ui/ui_render/ui_render_mixin.js @@ -153,11 +153,22 @@ export function uiRenderMixin(kbnServer, server, config) { `${regularBundlePath}/plugin/kibanaReact/kibanaReact.plugin.js`, ]; + const uiPluginIds = [...kbnServer.newPlatform.__internals.uiPlugins.public.keys()]; + + const publicPathMap = JSON.stringify({ + core: `${regularBundlePath}/core/`, + ...uiPluginIds.reduce( + (acc, nextId) => ({ ...acc, [nextId]: `${regularBundlePath}/plugin/${nextId}/` }), + {} + ), + }); + const bootstrap = new AppBootstrap({ templateData: { darkMode, jsDependencyPaths, styleSheetPaths, + publicPathMap, 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 530dabb9a46a3..629973bc4ff96 100644 --- a/src/optimize/bundles_route/bundles_route.js +++ b/src/optimize/bundles_route/bundles_route.js @@ -83,14 +83,16 @@ export function createBundlesRoute({ `${basePublicPath}/bundles/plugin/${id}/`, `/bundles/plugin/${id}/`, path, - fileHashCache + fileHashCache, + false ) ), buildRouteForBundles( `${basePublicPath}/bundles/core/`, `/bundles/core/`, fromRoot(join('src', 'core', 'target', 'public')), - fileHashCache + fileHashCache, + false ), buildRouteForBundles( `${basePublicPath}/bundles/`, @@ -108,7 +110,13 @@ export function createBundlesRoute({ ]; } -function buildRouteForBundles(publicPath, routePath, bundlesPath, fileHashCache) { +function buildRouteForBundles( + publicPath, + routePath, + bundlesPath, + fileHashCache, + replacePublicPath = true +) { return { method: 'GET', path: `${routePath}{path*}`, @@ -129,6 +137,7 @@ function buildRouteForBundles(publicPath, routePath, bundlesPath, fileHashCache) bundlesPath, fileHashCache, publicPath, + replacePublicPath, }); }, }, diff --git a/src/optimize/bundles_route/dynamic_asset_response.js b/src/optimize/bundles_route/dynamic_asset_response.js index 7af780a79e430..f13c4489b50ad 100644 --- a/src/optimize/bundles_route/dynamic_asset_response.js +++ b/src/optimize/bundles_route/dynamic_asset_response.js @@ -52,7 +52,7 @@ import { replacePlaceholder } from '../public_path_placeholder'; * @property {LruCache} options.fileHashCache */ export async function createDynamicAssetResponse(options) { - const { request, h, bundlesPath, publicPath, fileHashCache } = options; + const { request, h, bundlesPath, publicPath, fileHashCache, replacePublicPath } = options; let fd; try { @@ -78,8 +78,10 @@ export async function createDynamicAssetResponse(options) { }); fd = null; // read stream is now responsible for fd + const content = replacePublicPath ? replacePlaceholder(read, publicPath) : read; + return h - .response(replacePlaceholder(read, publicPath)) + .response(content) .takeover() .code(200) .etag(`${hash}-${publicPath}`)