diff --git a/packages/cli/src/commands/start/startFrontend.ts b/packages/cli/src/commands/start/startFrontend.ts index 00f7818b41..536b313dfe 100644 --- a/packages/cli/src/commands/start/startFrontend.ts +++ b/packages/cli/src/commands/start/startFrontend.ts @@ -91,11 +91,16 @@ export async function startFrontend(options: StartAppOptions) { checkReactVersion(); + const configChannel = new MessageChannel(); + const { name } = await fs.readJson(paths.resolveTarget('package.json')); const config = await loadCliConfig({ args: options.configPaths, fromPackage: name, withFilteredKeys: true, + watch(appConfigs) { + configChannel.port1.postMessage(appConfigs); + }, }); const appBaseUrl = config.frontendConfig.getString('app.baseUrl'); @@ -119,6 +124,7 @@ export async function startFrontend(options: StartAppOptions) { const waitForExit = await serveBundle({ entry: options.entry, checksEnabled: options.checksEnabled, + configChannel, ...config, }); diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 94b2a1c561..2f09a66e18 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -117,12 +117,6 @@ export async function createConfig( }), ); - plugins.push( - new webpack.EnvironmentPlugin({ - APP_CONFIG: options.frontendAppConfigs, - }), - ); - plugins.push( new HtmlWebpackPlugin({ template: paths.targetHtml, @@ -137,6 +131,10 @@ export async function createConfig( plugins.push( new webpack.DefinePlugin({ 'process.env.BUILD_INFO': JSON.stringify(buildInfo), + 'process.env.APP_CONFIG': webpack.DefinePlugin.runtimeValue( + () => JSON.stringify(options.getFrontendAppConfigs()), + true, + ), }), ); diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index cef55d3265..bef1a6fd7a 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -21,6 +21,7 @@ import openBrowser from 'react-dev-utils/openBrowser'; import { createConfig, resolveBaseUrl } from './config'; import { ServeOptions } from './types'; import { resolveBundlingPaths } from './paths'; +import { AppConfig } from '@backstage/config'; export async function serveBundle(options: ServeOptions) { const url = resolveBaseUrl(options.frontendConfig); @@ -32,6 +33,8 @@ export async function serveBundle(options: ServeOptions) { Number(url.port) || (url.protocol === 'https:' ? 443 : 80); + let latestFrontendAppConfigs = options.frontendAppConfigs; + const paths = resolveBundlingPaths(options); const pkgPath = paths.targetPackageJson; const pkg = await fs.readJson(pkgPath); @@ -39,6 +42,9 @@ export async function serveBundle(options: ServeOptions) { ...options, isDev: true, baseUrl: url, + getFrontendAppConfigs: () => { + return latestFrontendAppConfigs; + }, }); const compiler = webpack(config); @@ -95,6 +101,13 @@ export async function serveBundle(options: ServeOptions) { }); }); + options.configChannel.port2.onmessage = ({ + data, + }: MessageEvent) => { + latestFrontendAppConfigs = data; + server.invalidate(); + }; + const waitForExit = async () => { for (const signal of ['SIGINT', 'SIGTERM'] as const) { process.on(signal, () => { diff --git a/packages/cli/src/lib/bundler/types.ts b/packages/cli/src/lib/bundler/types.ts index 64916e349c..90fff4f3c1 100644 --- a/packages/cli/src/lib/bundler/types.ts +++ b/packages/cli/src/lib/bundler/types.ts @@ -22,7 +22,7 @@ export type BundlingOptions = { checksEnabled: boolean; isDev: boolean; frontendConfig: Config; - frontendAppConfigs: AppConfig[]; + getFrontendAppConfigs(): AppConfig[]; baseUrl: URL; parallelism?: number; }; @@ -32,6 +32,7 @@ export type ServeOptions = BundlingPathsOptions & { frontendConfig: Config; frontendAppConfigs: AppConfig[]; fullConfig: Config; + configChannel: MessageChannel; }; export type BuildOptions = BundlingPathsOptions & { diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index aaf3fcfcb9..b0efa8163a 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -19,7 +19,7 @@ import { loadConfig, loadConfigSchema, } from '@backstage/config-loader'; -import { ConfigReader } from '@backstage/config'; +import { AppConfig, ConfigReader } from '@backstage/config'; import { paths } from './paths'; import { isValidUrl } from './urls'; import { getPackages } from '@manypkg/get-packages'; @@ -33,6 +33,7 @@ type Options = { withDeprecatedKeys?: boolean; fullVisibility?: boolean; strict?: boolean; + watch?: (newFrontendAppConfigs: AppConfig[]) => void; }; export async function loadCliConfig(options: Options) { @@ -80,6 +81,19 @@ export async function loadCliConfig(options: Options) { : undefined, configRoot: paths.targetRoot, configTargets: configTargets, + watch: options.watch && { + onChange(newAppConfigs) { + const newFrontendAppConfigs = schema.process(newAppConfigs, { + visibility: options.fullVisibility + ? ['frontend', 'backend', 'secret'] + : ['frontend'], + withFilteredKeys: options.withFilteredKeys, + withDeprecatedKeys: options.withDeprecatedKeys, + ignoreSchemaErrors: !options.strict, + }); + options.watch!(newFrontendAppConfigs); + }, + }, }); // printing to stderr to not clobber stdout in case the cli command