Move module federation enablement in app-next at runtime.
Signed-off-by: David Festal <dfestal@redhat.com> Assisted-by: Cursor
This commit is contained in:
@@ -55,6 +55,7 @@
|
||||
"@backstage/errors": "workspace:^",
|
||||
"@backstage/eslint-plugin": "workspace:^",
|
||||
"@backstage/integration": "workspace:^",
|
||||
"@backstage/module-federation-common": "workspace:^",
|
||||
"@backstage/release-manifests": "workspace:^",
|
||||
"@backstage/types": "workspace:^",
|
||||
"@manypkg/get-packages": "^1.1.3",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { readJson } from 'fs-extra';
|
||||
import { resolve as resolvePath } from 'node:path';
|
||||
import {
|
||||
getModuleFederationOptions,
|
||||
getModuleFederationRemoteOptions,
|
||||
serveBundle,
|
||||
} from '../../../../build/lib/bundler';
|
||||
import { paths } from '../../../../../lib/paths';
|
||||
@@ -55,11 +55,12 @@ export async function startFrontend(options: StartAppOptions) {
|
||||
verifyVersions: options.verifyVersions,
|
||||
skipOpenBrowser: options.skipOpenBrowser,
|
||||
linkedWorkspace: options.linkedWorkspace,
|
||||
moduleFederation: await getModuleFederationOptions(
|
||||
packageJson,
|
||||
resolvePath(paths.targetDir),
|
||||
options.isModuleFederationRemote,
|
||||
),
|
||||
moduleFederationRemote: options.isModuleFederationRemote
|
||||
? await getModuleFederationRemoteOptions(
|
||||
packageJson,
|
||||
resolvePath(paths.targetDir),
|
||||
)
|
||||
: undefined,
|
||||
});
|
||||
|
||||
await waitForExit();
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import { resolve as resolvePath } from 'node:path';
|
||||
import { buildBundle, getModuleFederationOptions } from './bundler';
|
||||
import { buildBundle, getModuleFederationRemoteOptions } from './bundler';
|
||||
import { getEnvironmentParallelism } from '../../../lib/parallel';
|
||||
import { loadCliConfig } from '../../config/lib/config';
|
||||
import { BackstagePackageJson } from '@backstage/cli-node';
|
||||
@@ -25,7 +25,7 @@ interface BuildAppOptions {
|
||||
targetDir: string;
|
||||
writeStats: boolean;
|
||||
configPaths: string[];
|
||||
isModuleFederationRemote?: true;
|
||||
isModuleFederationRemote?: boolean;
|
||||
webpack?: typeof import('webpack');
|
||||
}
|
||||
|
||||
@@ -39,11 +39,12 @@ export async function buildFrontend(options: BuildAppOptions) {
|
||||
entry: 'src/index',
|
||||
parallelism: getEnvironmentParallelism(),
|
||||
statsJsonEnabled: writeStats,
|
||||
moduleFederation: await getModuleFederationOptions(
|
||||
packageJson,
|
||||
resolvePath(targetDir),
|
||||
options.isModuleFederationRemote,
|
||||
),
|
||||
moduleFederationRemote: options.isModuleFederationRemote
|
||||
? await getModuleFederationRemoteOptions(
|
||||
packageJson,
|
||||
resolvePath(targetDir),
|
||||
)
|
||||
: undefined,
|
||||
...(await loadCliConfig({
|
||||
args: configPaths,
|
||||
fromPackage: packageJson.name,
|
||||
|
||||
@@ -28,6 +28,7 @@ import { BuildOptions } from './types';
|
||||
import { resolveBundlingPaths, resolveOptionalBundlingPaths } from './paths';
|
||||
import chalk from 'chalk';
|
||||
import { createDetectedModulesEntryPoint } from './packageDetection';
|
||||
import { createRuntimeSharedDependeciesEntryPoint } from './moduleFederation';
|
||||
|
||||
// TODO(Rugvip): Limits from CRA, we might want to tweak these though.
|
||||
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
|
||||
@@ -55,8 +56,7 @@ export async function buildBundle(options: BuildOptions) {
|
||||
};
|
||||
|
||||
const configs: Configuration[] = [];
|
||||
|
||||
if (options.moduleFederation?.mode === 'remote') {
|
||||
if (options.moduleFederationRemote) {
|
||||
// Package detection is disabled for remote bundles
|
||||
configs.push(await createConfig(paths, commonConfigOptions));
|
||||
} else {
|
||||
@@ -65,10 +65,18 @@ export async function buildBundle(options: BuildOptions) {
|
||||
targetPath: paths.targetPath,
|
||||
});
|
||||
|
||||
const moduleFederationSharedDependenciesEntryPoint =
|
||||
await createRuntimeSharedDependeciesEntryPoint({
|
||||
targetPath: paths.targetPath,
|
||||
});
|
||||
|
||||
configs.push(
|
||||
await createConfig(paths, {
|
||||
...commonConfigOptions,
|
||||
additionalEntryPoints: detectedModulesEntryPoint,
|
||||
additionalEntryPoints: [
|
||||
...detectedModulesEntryPoint,
|
||||
...moduleFederationSharedDependenciesEntryPoint,
|
||||
],
|
||||
appMode: publicPaths ? 'protected' : 'public',
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BundlingOptions, ModuleFederationOptions } from './types';
|
||||
import { resolve as resolvePath } from 'node:path';
|
||||
import { BundlingOptions, ModuleFederationRemoteOptions } from './types';
|
||||
import { rspack, Configuration } from '@rspack/core';
|
||||
|
||||
import { BundlingPaths } from './paths';
|
||||
@@ -39,14 +39,13 @@ import { ConfigInjectingHtmlWebpackPlugin } from './ConfigInjectingHtmlWebpackPl
|
||||
|
||||
export function resolveBaseUrl(
|
||||
config: Config,
|
||||
moduleFederation?: ModuleFederationOptions,
|
||||
moduleFederationRemote?: ModuleFederationRemoteOptions,
|
||||
): URL {
|
||||
const baseUrl = config.getOptionalString('app.baseUrl');
|
||||
|
||||
const defaultBaseUrl =
|
||||
moduleFederation?.mode === 'remote'
|
||||
? `http://localhost:${process.env.PORT ?? '3000'}`
|
||||
: 'http://localhost:3000';
|
||||
const defaultBaseUrl = moduleFederationRemote
|
||||
? `http://localhost:${process.env.PORT ?? '3000'}`
|
||||
: 'http://localhost:3000';
|
||||
|
||||
try {
|
||||
return new URL(baseUrl ?? '/', defaultBaseUrl);
|
||||
@@ -57,12 +56,12 @@ export function resolveBaseUrl(
|
||||
|
||||
export function resolveEndpoint(
|
||||
config: Config,
|
||||
moduleFederation?: ModuleFederationOptions,
|
||||
moduleFederationRemote?: ModuleFederationRemoteOptions,
|
||||
): {
|
||||
host: string;
|
||||
port: number;
|
||||
} {
|
||||
const url = resolveBaseUrl(config, moduleFederation);
|
||||
const url = resolveBaseUrl(config, moduleFederationRemote);
|
||||
|
||||
return {
|
||||
host: config.getOptionalString('app.listen.host') ?? url.hostname,
|
||||
@@ -117,7 +116,7 @@ export async function createConfig(
|
||||
checksEnabled,
|
||||
isDev,
|
||||
frontendConfig,
|
||||
moduleFederation,
|
||||
moduleFederationRemote,
|
||||
publicSubPath = '',
|
||||
webpack,
|
||||
} = options;
|
||||
@@ -126,7 +125,7 @@ export async function createConfig(
|
||||
// Any package that is part of the monorepo but outside the monorepo root dir need
|
||||
// separate resolution logic.
|
||||
|
||||
const validBaseUrl = resolveBaseUrl(frontendConfig, moduleFederation);
|
||||
const validBaseUrl = resolveBaseUrl(frontendConfig, moduleFederationRemote);
|
||||
let publicPath = validBaseUrl.pathname.replace(/\/$/, '');
|
||||
if (publicSubPath) {
|
||||
publicPath = `${publicPath}${publicSubPath}`.replace('//', '/');
|
||||
@@ -135,7 +134,7 @@ export async function createConfig(
|
||||
if (isDev) {
|
||||
const { host, port } = resolveEndpoint(
|
||||
options.frontendConfig,
|
||||
options.moduleFederation,
|
||||
options.moduleFederationRemote,
|
||||
);
|
||||
|
||||
const refreshOptions = {
|
||||
@@ -186,7 +185,7 @@ export async function createConfig(
|
||||
}),
|
||||
);
|
||||
|
||||
if (options.moduleFederation?.mode !== 'remote') {
|
||||
if (!options.moduleFederationRemote) {
|
||||
const templateOptions = {
|
||||
meta: {
|
||||
'backstage-app-mode': options?.appMode ?? 'public',
|
||||
@@ -231,20 +230,17 @@ export async function createConfig(
|
||||
);
|
||||
}
|
||||
|
||||
if (options.moduleFederation) {
|
||||
const isRemote = options.moduleFederation?.mode === 'remote';
|
||||
|
||||
if (options.moduleFederationRemote) {
|
||||
const AdaptedModuleFederationPlugin = webpack
|
||||
? (require('@module-federation/enhanced/webpack')
|
||||
.ModuleFederationPlugin as unknown as typeof ModuleFederationPlugin)
|
||||
: ModuleFederationPlugin;
|
||||
|
||||
const exposes = options.moduleFederation?.exposes
|
||||
const exposes = options.moduleFederationRemote.exposes
|
||||
? Object.fromEntries(
|
||||
Object.entries(options.moduleFederation?.exposes).map(([k, v]) => [
|
||||
k,
|
||||
resolvePath(paths.targetPath, v),
|
||||
]),
|
||||
Object.entries(options.moduleFederationRemote?.exposes).map(
|
||||
([k, v]) => [k, resolvePath(paths.targetPath, v)],
|
||||
),
|
||||
)
|
||||
: {
|
||||
'.': paths.targetEntry,
|
||||
@@ -252,66 +248,28 @@ export async function createConfig(
|
||||
|
||||
plugins.push(
|
||||
new AdaptedModuleFederationPlugin({
|
||||
...(isRemote && {
|
||||
filename: 'remoteEntry.js',
|
||||
exposes,
|
||||
}),
|
||||
name: options.moduleFederation.name,
|
||||
filename: 'remoteEntry.js',
|
||||
exposes,
|
||||
name: options.moduleFederationRemote.name,
|
||||
runtime: false,
|
||||
shared: {
|
||||
// React
|
||||
react: {
|
||||
singleton: true,
|
||||
requiredVersion: '*',
|
||||
eager: !isRemote,
|
||||
...(isRemote && { import: false }),
|
||||
},
|
||||
'react-dom': {
|
||||
singleton: true,
|
||||
requiredVersion: '*',
|
||||
eager: !isRemote,
|
||||
...(isRemote && { import: false }),
|
||||
},
|
||||
// React Router
|
||||
'react-router': {
|
||||
singleton: true,
|
||||
requiredVersion: '*',
|
||||
eager: !isRemote,
|
||||
...(isRemote && { import: false }),
|
||||
},
|
||||
'react-router-dom': {
|
||||
singleton: true,
|
||||
requiredVersion: '*',
|
||||
eager: !isRemote,
|
||||
...(isRemote && { import: false }),
|
||||
},
|
||||
// MUI v4
|
||||
// not setting import: false for MUI packages as this
|
||||
// will break once Backstage moves to BUI
|
||||
'@material-ui/core/styles': {
|
||||
singleton: true,
|
||||
requiredVersion: '*',
|
||||
eager: !isRemote,
|
||||
},
|
||||
'@material-ui/styles': {
|
||||
singleton: true,
|
||||
requiredVersion: '*',
|
||||
eager: !isRemote,
|
||||
},
|
||||
// MUI v5
|
||||
// not setting import: false for MUI packages as this
|
||||
// will break once Backstage moves to BUI
|
||||
'@mui/material/styles/': {
|
||||
singleton: true,
|
||||
requiredVersion: '*',
|
||||
eager: !isRemote,
|
||||
},
|
||||
'@emotion/react': {
|
||||
singleton: true,
|
||||
requiredVersion: '*',
|
||||
eager: !isRemote,
|
||||
},
|
||||
},
|
||||
shared: Object.fromEntries(
|
||||
Object.entries(options.moduleFederationRemote.sharedDependencies).map(
|
||||
([name, p]) => [
|
||||
name,
|
||||
{
|
||||
...(p.version === undefined ? {} : { version: p.version }),
|
||||
...(p.requiredVersion === undefined
|
||||
? {}
|
||||
: { requiredVersion: p.requiredVersion }),
|
||||
...(p.singleton === undefined
|
||||
? {}
|
||||
: { singleton: p.singleton }),
|
||||
...(p.import === undefined ? {} : { import: p.import }),
|
||||
eager: false,
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -426,10 +384,9 @@ export async function createConfig(
|
||||
rules: loaders,
|
||||
},
|
||||
output: {
|
||||
uniqueName: options.moduleFederation?.name,
|
||||
uniqueName: options.moduleFederationRemote?.name,
|
||||
path: paths.targetDist,
|
||||
publicPath:
|
||||
options.moduleFederation?.mode === 'remote' ? 'auto' : `${publicPath}/`,
|
||||
publicPath: options.moduleFederationRemote ? 'auto' : `${publicPath}/`,
|
||||
filename: isDev ? '[name].js' : 'static/[name].[contenthash:8].js',
|
||||
chunkFilename: isDev
|
||||
? '[name].chunk.js'
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
*/
|
||||
|
||||
export { buildBundle } from './bundle';
|
||||
export { getModuleFederationOptions } from './moduleFederation';
|
||||
export { getModuleFederationRemoteOptions } from './moduleFederation';
|
||||
export { serveBundle } from './server';
|
||||
|
||||
@@ -14,36 +14,34 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import chalk from 'chalk';
|
||||
import { ModuleFederationOptions } from './types';
|
||||
import { ModuleFederationRemoteOptions } from './types';
|
||||
import { BackstagePackageJson } from '@backstage/cli-node';
|
||||
import { readEntryPoints } from '../../../../lib/entryPoints';
|
||||
import {
|
||||
createTypeDistProject,
|
||||
getEntryPointDefaultFeatureType,
|
||||
} from '../../../../lib/typeDistProject';
|
||||
import {
|
||||
SharedDependencies,
|
||||
Host,
|
||||
prepareRuntimeSharedDependenciesScript,
|
||||
defaultRemoteSharedDependencies,
|
||||
defaultHostSharedDependencies,
|
||||
} from '@backstage/module-federation-common';
|
||||
import { dirname, join as joinPath, resolve as resolvePath } from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import chokidar from 'chokidar';
|
||||
import PQueue from 'p-queue';
|
||||
|
||||
export async function getModuleFederationOptions(
|
||||
// Remote modules management utilities
|
||||
|
||||
export async function getModuleFederationRemoteOptions(
|
||||
packageJson: BackstagePackageJson,
|
||||
packageDir: string,
|
||||
isModuleFederationRemote?: boolean,
|
||||
): Promise<ModuleFederationOptions | undefined> {
|
||||
if (
|
||||
!isModuleFederationRemote &&
|
||||
!process.env.EXPERIMENTAL_MODULE_FEDERATION
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`⚠️ WARNING: Module federation is experimental and will receive immediate breaking changes in the future.`,
|
||||
),
|
||||
);
|
||||
|
||||
let exposes: ModuleFederationOptions['exposes'];
|
||||
): Promise<ModuleFederationRemoteOptions | undefined> {
|
||||
let exposes: ModuleFederationRemoteOptions['exposes'];
|
||||
const packageRole = packageJson.backstage?.role;
|
||||
if (isModuleFederationRemote && packageJson.exports && packageRole) {
|
||||
if (packageJson.exports && packageRole) {
|
||||
const project = await createTypeDistProject();
|
||||
exposes = Object.fromEntries(
|
||||
readEntryPoints(packageJson)
|
||||
@@ -70,7 +68,6 @@ export async function getModuleFederationOptions(
|
||||
}
|
||||
|
||||
return {
|
||||
mode: isModuleFederationRemote ? 'remote' : 'host',
|
||||
// The default output mode requires the name to be a usable as a code
|
||||
// symbol, there might be better options here but for now we need to
|
||||
// sanitize the name.
|
||||
@@ -79,5 +76,98 @@ export async function getModuleFederationOptions(
|
||||
.replaceAll('/', '__')
|
||||
.replaceAll('-', '_'),
|
||||
exposes,
|
||||
sharedDependencies: defaultRemoteSharedDependencies(),
|
||||
};
|
||||
}
|
||||
|
||||
// Module federation host management utilities
|
||||
|
||||
const RUNTIME_SHARED_DEPENDENCIES_MODULE_NAME =
|
||||
'__backstage-module-federation-runtime-shared-dependencies__';
|
||||
|
||||
// Make sure we're not issuing multiple writes at the same time, which can cause partial overwrites
|
||||
const writeQueue = new PQueue({ concurrency: 1 });
|
||||
|
||||
async function writeRuntimeSharedDependenciesModule(
|
||||
targetPath: string,
|
||||
runtimeSharedDependencies: SharedDependencies<Host & { version: string }>,
|
||||
) {
|
||||
const script = prepareRuntimeSharedDependenciesScript(
|
||||
runtimeSharedDependencies,
|
||||
);
|
||||
|
||||
await writeQueue.add(async () => {
|
||||
const path = joinPath(
|
||||
targetPath,
|
||||
'node_modules',
|
||||
`${RUNTIME_SHARED_DEPENDENCIES_MODULE_NAME}.js`,
|
||||
);
|
||||
|
||||
await fs.ensureDir(dirname(path));
|
||||
await fs.writeFile(path, script);
|
||||
});
|
||||
}
|
||||
|
||||
function resolveSharedDependencyVersions(
|
||||
targetPath: string,
|
||||
hostSharedDependencies: SharedDependencies<Host>,
|
||||
): SharedDependencies<Host & { version: string }> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(hostSharedDependencies)
|
||||
.filter(([_, sharedDep]) => sharedDep !== undefined)
|
||||
.map(([name, sharedDep]) => {
|
||||
// Use require.resolve to find the package
|
||||
// For scoped modules, keep the scope and the module name, but remove any sub-folder
|
||||
const nameParts = name.split('/');
|
||||
const moduleName =
|
||||
nameParts[0].startsWith('@') && nameParts.length > 1
|
||||
? `${nameParts[0]}/${nameParts[1]}`
|
||||
: nameParts[0];
|
||||
let packagePath: string;
|
||||
try {
|
||||
packagePath = require.resolve(`${moduleName}/package.json`, {
|
||||
paths: [targetPath],
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Failed to resolve package.json for module federation shared dependency '${name}': ${e}`,
|
||||
);
|
||||
}
|
||||
const packageJson = require(packagePath);
|
||||
|
||||
if (sharedDep.version && packageJson.version !== sharedDep.version) {
|
||||
throw new Error(
|
||||
`Version mismatch for module federation shared dependency '${name}': '${sharedDep.version}' vs '${packageJson.version}' found in '${packagePath}'.`,
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
name,
|
||||
{ ...sharedDep, version: sharedDep.version ?? packageJson.version },
|
||||
];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export async function createRuntimeSharedDependeciesEntryPoint(options: {
|
||||
targetPath: string;
|
||||
watch?: () => void;
|
||||
}): Promise<string[]> {
|
||||
const { targetPath, watch } = options;
|
||||
|
||||
const doWriteSharedDependenciesModule = async () => {
|
||||
const sharedDependencies = defaultHostSharedDependencies();
|
||||
await writeRuntimeSharedDependenciesModule(
|
||||
targetPath,
|
||||
resolveSharedDependencyVersions(targetPath, sharedDependencies),
|
||||
);
|
||||
};
|
||||
|
||||
if (watch) {
|
||||
const watcher = chokidar.watch(resolvePath(targetPath, 'package.json'));
|
||||
watcher.on('change', doWriteSharedDependenciesModule);
|
||||
}
|
||||
await doWriteSharedDependenciesModule();
|
||||
|
||||
return [RUNTIME_SHARED_DEPENDENCIES_MODULE_NAME];
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { createConfig, resolveBaseUrl, resolveEndpoint } from './config';
|
||||
import { createDetectedModulesEntryPoint } from './packageDetection';
|
||||
import { resolveBundlingPaths, resolveOptionalBundlingPaths } from './paths';
|
||||
import { ServeOptions } from './types';
|
||||
import { createRuntimeSharedDependeciesEntryPoint } from './moduleFederation';
|
||||
|
||||
export async function serveBundle(options: ServeOptions) {
|
||||
const paths = resolveBundlingPaths(options);
|
||||
@@ -108,10 +109,10 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be
|
||||
}
|
||||
|
||||
const { frontendConfig, fullConfig } = cliConfig;
|
||||
const url = resolveBaseUrl(frontendConfig, options.moduleFederation);
|
||||
const url = resolveBaseUrl(frontendConfig, options.moduleFederationRemote);
|
||||
const { host, port } = resolveEndpoint(
|
||||
frontendConfig,
|
||||
options.moduleFederation,
|
||||
options.moduleFederationRemote,
|
||||
);
|
||||
|
||||
const detectedModulesEntryPoint = await createDetectedModulesEntryPoint({
|
||||
@@ -122,6 +123,14 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be
|
||||
},
|
||||
});
|
||||
|
||||
const moduleFederationSharedDependenciesEntryPoint =
|
||||
await createRuntimeSharedDependeciesEntryPoint({
|
||||
targetPath: paths.targetPath,
|
||||
watch() {
|
||||
triggerReload();
|
||||
},
|
||||
});
|
||||
|
||||
const webpack = process.env.LEGACY_WEBPACK_BUILD
|
||||
? (require('webpack') as typeof import('webpack'))
|
||||
: undefined;
|
||||
@@ -140,8 +149,11 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be
|
||||
|
||||
const config = await createConfig(paths, {
|
||||
...commonConfigOptions,
|
||||
additionalEntryPoints: detectedModulesEntryPoint,
|
||||
moduleFederation: options.moduleFederation,
|
||||
additionalEntryPoints: [
|
||||
...detectedModulesEntryPoint,
|
||||
...moduleFederationSharedDependenciesEntryPoint,
|
||||
],
|
||||
moduleFederationRemote: options.moduleFederationRemote,
|
||||
});
|
||||
|
||||
const bundler = (webpack ?? rspack) as typeof rspack;
|
||||
@@ -181,17 +193,16 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be
|
||||
directory: paths.targetPublic,
|
||||
}
|
||||
: undefined,
|
||||
historyApiFallback:
|
||||
options.moduleFederation?.mode === 'remote'
|
||||
? false
|
||||
: {
|
||||
// Paths with dots should still use the history fallback.
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/387.
|
||||
disableDotRule: true,
|
||||
historyApiFallback: options.moduleFederationRemote
|
||||
? false
|
||||
: {
|
||||
// Paths with dots should still use the history fallback.
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/387.
|
||||
disableDotRule: true,
|
||||
|
||||
// The index needs to be rewritten relative to the new public path, including subroutes.
|
||||
index: `${config.output?.publicPath}index.html`,
|
||||
},
|
||||
// The index needs to be rewritten relative to the new public path, including subroutes.
|
||||
index: `${config.output?.publicPath}index.html`,
|
||||
},
|
||||
server:
|
||||
url.protocol === 'https:'
|
||||
? {
|
||||
|
||||
@@ -17,18 +17,21 @@
|
||||
import { AppConfig, Config } from '@backstage/config';
|
||||
import { BundlingPathsOptions } from './paths';
|
||||
import { ConfigSchema } from '@backstage/config-loader';
|
||||
import {
|
||||
SharedDependencies,
|
||||
Remote,
|
||||
} from '@backstage/module-federation-common';
|
||||
|
||||
export type ModuleFederationOptions = {
|
||||
export type ModuleFederationRemoteOptions = {
|
||||
// Unique name for this module federation bundle
|
||||
name: string;
|
||||
// Whether this is a host or remote bundle
|
||||
mode: 'host' | 'remote';
|
||||
exposes?: {
|
||||
/**
|
||||
* Modules that should be exposed by this container.
|
||||
*/
|
||||
[k: string]: string;
|
||||
};
|
||||
sharedDependencies: SharedDependencies<Remote>;
|
||||
};
|
||||
|
||||
export type BundlingOptions = {
|
||||
@@ -44,7 +47,7 @@ export type BundlingOptions = {
|
||||
appMode?: string;
|
||||
// An external linked workspace to include in the bundling
|
||||
linkedWorkspace?: string;
|
||||
moduleFederation?: ModuleFederationOptions;
|
||||
moduleFederationRemote?: ModuleFederationRemoteOptions;
|
||||
webpack?: typeof import('webpack');
|
||||
};
|
||||
|
||||
@@ -54,7 +57,7 @@ export type ServeOptions = BundlingPathsOptions & {
|
||||
configPaths: string[];
|
||||
verifyVersions?: boolean;
|
||||
skipOpenBrowser?: boolean;
|
||||
moduleFederation?: ModuleFederationOptions;
|
||||
moduleFederationRemote?: ModuleFederationRemoteOptions;
|
||||
// An external linked workspace to include in the bundling
|
||||
linkedWorkspace?: string;
|
||||
};
|
||||
@@ -68,7 +71,7 @@ export type BuildOptions = BundlingPathsOptions & {
|
||||
frontendConfig: Config;
|
||||
frontendAppConfigs: AppConfig[];
|
||||
fullConfig: Config;
|
||||
moduleFederation?: ModuleFederationOptions;
|
||||
moduleFederationRemote?: ModuleFederationRemoteOptions;
|
||||
webpack?: typeof import('webpack');
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user