diff --git a/.changeset/great-zebras-hunt.md b/.changeset/great-zebras-hunt.md index cbb0beb1b6..252f270092 100644 --- a/.changeset/great-zebras-hunt.md +++ b/.changeset/great-zebras-hunt.md @@ -3,3 +3,5 @@ --- The app build process now outputs an additional `index.html.tmpl` file. This is an non-templated version of the `index.html` file, which can be used to delay templating until runtime. + +The new `index.html.tmpl` file also sets a `backstage-public-path` meta tag to be templated at runtime. The meta tag is in turn picked up by the new `@backstage/cli/config/webpack-public-path.js` entry point script, which uses it to set the runtime public path of the Webpack bundle. diff --git a/packages/cli/config/webpack-public-path.js b/packages/cli/config/webpack-public-path.js new file mode 100644 index 0000000000..3e14e96eb9 --- /dev/null +++ b/packages/cli/config/webpack-public-path.js @@ -0,0 +1,31 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed 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 script is used to pick up and set the public path of the Webpack bundle +// at runtime. The meta tag is injected by the app build, but only present in +// the `index.html.tmpl` file. The runtime value of the meta tag is populated by +// the app backend, when it templates the final `index.html` file. +// +// This is needed for additional chunks to use the correct public path, and it +// is not possible to set the `__webpack_public_path__` variable outside of the +// build itself. The Webpack output also does not read any tags or +// similar, this seems to be the only way to dynamically configure the public +// path at runtime. +const el = document.querySelector('meta[name="backstage-public-path"]'); +const path = el?.getAttribute('content'); +if (path) { + __webpack_public_path__ = path; +} diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 40ed82ef54..a64bc0fcd8 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -202,6 +202,8 @@ export async function createConfig( new HtmlWebpackPlugin({ meta: { 'backstage-app-mode': options?.appMode ?? 'public', + // This is added to be written in the later step, and finally read by the extra entry point + 'backstage-public-path': '<%= publicPath %>/', }, minify: false, publicPath: '<%= publicPath %>', @@ -352,7 +354,11 @@ export async function createConfig( }, devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map', context: paths.targetPath, - entry: [...(options.additionalEntryPoints ?? []), paths.targetEntry], + entry: [ + require.resolve('@backstage/cli/config/webpack-public-path'), + ...(options.additionalEntryPoints ?? []), + paths.targetEntry, + ], resolve: { extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx', '.json', '.wasm'], mainFields: ['browser', 'module', 'main'],