cli: add --link option for frontend start
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -138,6 +138,7 @@ export function registerScriptCommand(program: Command) {
|
||||
'Enable debugger in Node.js environments, breaking before code starts',
|
||||
)
|
||||
.option('--require <path>', 'Add a --require argument to the node process')
|
||||
.option('--link <path>', 'Link an external workspace for module resolution')
|
||||
.action(lazy(() => import('./start').then(m => m.command)));
|
||||
|
||||
command
|
||||
|
||||
@@ -26,6 +26,7 @@ export async function command(opts: OptionValues): Promise<void> {
|
||||
const options = {
|
||||
configPaths: opts.config as string[],
|
||||
checksEnabled: Boolean(opts.check),
|
||||
linkedWorkspace: opts.link,
|
||||
inspectEnabled: opts.inspect,
|
||||
inspectBrkEnabled: opts.inspectBrk,
|
||||
require: opts.require,
|
||||
@@ -33,10 +34,20 @@ export async function command(opts: OptionValues): Promise<void> {
|
||||
|
||||
switch (role) {
|
||||
case 'backend':
|
||||
if (options.linkedWorkspace) {
|
||||
throw new Error(
|
||||
'The --link flag is not supported for this package role',
|
||||
);
|
||||
}
|
||||
return startBackend(options);
|
||||
case 'backend-plugin':
|
||||
case 'backend-plugin-module':
|
||||
case 'node-library':
|
||||
if (options.linkedWorkspace) {
|
||||
throw new Error(
|
||||
'The --link flag is not supported for this package role',
|
||||
);
|
||||
}
|
||||
return startBackendPlugin(options);
|
||||
case 'frontend':
|
||||
return startFrontend({
|
||||
|
||||
@@ -26,6 +26,7 @@ interface StartAppOptions {
|
||||
configPaths: string[];
|
||||
skipOpenBrowser?: boolean;
|
||||
isModuleFederationRemote?: boolean;
|
||||
linkedWorkspace?: string;
|
||||
}
|
||||
|
||||
export async function startFrontend(options: StartAppOptions) {
|
||||
@@ -37,6 +38,7 @@ export async function startFrontend(options: StartAppOptions) {
|
||||
configPaths: options.configPaths,
|
||||
verifyVersions: options.verifyVersions,
|
||||
skipOpenBrowser: options.skipOpenBrowser,
|
||||
linkedWorkspace: options.linkedWorkspace,
|
||||
moduleFederation: getModuleFederationOptions(
|
||||
name,
|
||||
options.isModuleFederationRemote,
|
||||
|
||||
@@ -36,6 +36,7 @@ import { transforms } from './transforms';
|
||||
import { version } from '../../lib/version';
|
||||
import yn from 'yn';
|
||||
import { hasReactDomClient } from './hasReactDomClient';
|
||||
import { createWorkspaceLinkingPlugins } from './linkWorkspaces';
|
||||
|
||||
const BUILD_CACHE_ENV_VAR = 'BACKSTAGE_CLI_EXPERIMENTAL_BUILD_CACHE';
|
||||
|
||||
@@ -295,6 +296,15 @@ export async function createConfig(
|
||||
}),
|
||||
);
|
||||
|
||||
if (options.linkedWorkspace) {
|
||||
plugins.push(
|
||||
...(await createWorkspaceLinkingPlugins(
|
||||
bundler,
|
||||
options.linkedWorkspace,
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
// These files are required by the transpiled code when using React Refresh.
|
||||
// They need to be excluded to the module scope plugin which ensures that files
|
||||
// that exist in the package are required.
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { relative as relativePath } from 'path';
|
||||
import { getPackages } from '@manypkg/get-packages';
|
||||
import webpack from 'webpack';
|
||||
import { paths } from '../paths';
|
||||
|
||||
/**
|
||||
* This returns of collection of plugins that links a separate workspace into
|
||||
* the target one. Any packages that are present in the linked workspaces will
|
||||
* always be used in place of the ones in the target workspace, with the exception
|
||||
* of react and react-dom which are always resolved from the target workspace.
|
||||
*/
|
||||
export async function createWorkspaceLinkingPlugins(
|
||||
bundler: typeof webpack,
|
||||
workspace: string,
|
||||
) {
|
||||
const { packages: linkedPackages, root: linkedRoot } = await getPackages(
|
||||
workspace,
|
||||
);
|
||||
|
||||
// Matches all packages in the linked workspaces, as well as sub-path exports from them
|
||||
const replacementRegex = new RegExp(
|
||||
`^(?:${linkedPackages
|
||||
.map(pkg => pkg.packageJson.name)
|
||||
.join('|')})(?:/.*)?$`,
|
||||
);
|
||||
|
||||
return [
|
||||
// Any imports of a package that is present in the linked workspace will
|
||||
// be redirected to be resolved within the context of the linked workspace
|
||||
new bundler.NormalModuleReplacementPlugin(replacementRegex, resource => {
|
||||
resource.context = linkedRoot.dir;
|
||||
}),
|
||||
// react and react-dom are always resolved from the target directory
|
||||
// Note: this often requires that the linked and target workspace use the same versions of React
|
||||
new bundler.NormalModuleReplacementPlugin(/^react(?:-dom)?$/, resource => {
|
||||
if (!relativePath(linkedRoot.dir, resource.context).startsWith('..')) {
|
||||
resource.context = paths.targetDir;
|
||||
}
|
||||
}),
|
||||
];
|
||||
}
|
||||
@@ -36,6 +36,8 @@ export type BundlingOptions = {
|
||||
publicSubPath?: string;
|
||||
// Mode that the app is running in, 'protected' or 'public', default is 'public'
|
||||
appMode?: string;
|
||||
// An external linked workspace to include in the bundling
|
||||
linkedWorkspace?: string;
|
||||
moduleFederation?: ModuleFederationOptions;
|
||||
rspack?: typeof import('@rspack/core').rspack;
|
||||
};
|
||||
@@ -46,6 +48,8 @@ export type ServeOptions = BundlingPathsOptions & {
|
||||
verifyVersions?: boolean;
|
||||
skipOpenBrowser?: boolean;
|
||||
moduleFederation?: ModuleFederationOptions;
|
||||
// An external linked workspace to include in the bundling
|
||||
linkedWorkspace?: string;
|
||||
};
|
||||
|
||||
export type BuildOptions = BundlingPathsOptions & {
|
||||
|
||||
Reference in New Issue
Block a user