Merge branch 'backstage:master' into gh-29512-add-browser-feedback

This commit is contained in:
Josephine Pfeiffer
2025-06-03 17:09:32 +02:00
committed by GitHub
403 changed files with 6553 additions and 1647 deletions
+22
View File
@@ -1,5 +1,27 @@
# @backstage/cli
## 0.32.2-next.0
### Patch Changes
- ce70439: The `BACKSTAGE_CLI_EXPERIMENTAL_BUILD_CACHE` flag has been removed. Existing users are encouraged to switch to `EXPERIMENTAL_RSPACK` instead.
- 1d8f00b: Switched to using the `ModuleFederationPlugin` from `@module-federation/enhanced/rspack` for Rspack, rather than the built-in one.
- 2b9633f: The experimental `FORCE_REACT_DEVELOPMENT` flag has been removed.
- d8c4a54: Only use the caching Jest module loader for frontend packages in order to avoid breaking real ESM module imports.
- 1bab255: Internal refactor to combine alpha `build` and `start` modules.
- 713e957: fix: merge eslint reports when using json format
- Updated dependencies
- @backstage/catalog-model@1.7.4
- @backstage/cli-common@0.1.15
- @backstage/cli-node@0.2.13
- @backstage/config@1.3.2
- @backstage/config-loader@1.10.1
- @backstage/errors@1.2.7
- @backstage/eslint-plugin@0.1.10
- @backstage/integration@1.17.0
- @backstage/release-manifests@0.0.13
- @backstage/types@1.2.1
## 0.32.1
### Patch Changes
+5 -4
View File
@@ -212,6 +212,11 @@ function getRoleConfig(role, pkgJson) {
if (FRONTEND_ROLES.includes(role)) {
return {
testEnvironment: require.resolve('jest-environment-jsdom'),
// The caching module loader is only used to speed up frontend tests,
// as it breaks real dynamic imports of ESM modules.
runtime: envOptions.oldTests
? undefined
: require.resolve('./jestCachingModuleLoader'),
transform,
};
}
@@ -257,10 +262,6 @@ async function getProjectConfig(targetPath, extraConfig, extraOptions) {
// A bit more opinionated
testMatch: [`**/*.test.{${SRC_EXTS.join(',')}}`],
runtime: envOptions.oldTests
? undefined
: require.resolve('./jestCachingModuleLoader'),
transformIgnorePatterns: [`/node_modules/(?:${transformIgnorePattern})/`],
...getRoleConfig(pkgJson.backstage?.role, pkgJson),
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/cli",
"version": "0.32.1",
"version": "0.32.2-next.0",
"description": "CLI for developing Backstage plugins and apps",
"backstage": {
"role": "cli"
-1
View File
@@ -33,7 +33,6 @@ import chalk from 'chalk';
initializer.add(import('./modules/maintenance/alpha'));
initializer.add(import('./modules/migrate/alpha'));
initializer.add(import('./modules/new/alpha'));
initializer.add(import('./modules/start/alpha'));
initializer.add(import('./modules/test/alpha'));
await initializer.run();
})();
-6
View File
@@ -21,10 +21,6 @@ import {
registerRepoCommands as registerRepoBuildCommands,
registerCommands as registerBuildCommands,
} from '../modules/build';
import {
registerPackageCommands as registerPackageStartCommands,
registerRepoCommands as registerRepoStartCommands,
} from '../modules/start';
import { registerCommands as registerInfoCommands } from '../modules/info';
import { registerCommands as registerMigrateCommand } from '../modules/migrate';
import {
@@ -48,7 +44,6 @@ export function registerRepoCommand(program: Command) {
.command('repo [command]')
.description('Command that run across an entire Backstage project');
registerRepoStartCommands(command);
registerRepoBuildCommands(command);
registerRepoTestCommands(command);
registerRepoLintCommands(command);
@@ -60,7 +55,6 @@ export function registerScriptCommand(program: Command) {
.command('package [command]')
.description('Lifecycle scripts for individual packages');
registerPackageStartCommands(command);
registerPackageBuildCommands(command);
registerPackageTestCommands(command);
registerMaintenancePackageCommands(command);
+75
View File
@@ -18,6 +18,7 @@ import { Command, Option } from 'commander';
import { createCliPlugin } from '../../wiring/factory';
import { lazy } from '../../lib/lazy';
import { registerPackageCommands } from '.';
import { configOption } from '../config';
export const buildPlugin = createCliPlugin({
pluginId: 'build',
@@ -85,6 +86,80 @@ export const buildPlugin = createCliPlugin({
},
});
reg.addCommand({
path: ['package', 'start'],
description: 'Start a package for local development',
execute: async ({ args }) => {
const command = new Command();
const defaultCommand = command
.option(...configOption)
.option(
'--role <name>',
'Run the command with an explicit package role',
)
.option('--check', 'Enable type checking and linting if available')
.option('--inspect [host]', 'Enable debugger in Node.js environments')
.option(
'--inspect-brk [host]',
'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('./commands/package/start'), 'command'));
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
reg.addCommand({
path: ['repo', 'start'],
description: 'Starts packages in the repo for local development',
execute: async ({ args }) => {
const command = new Command();
const defaultCommand = command
.argument(
'[...packageNameOrPath]',
'Run the specified package instead of the defaults.',
)
.option(
'--plugin <pluginId>',
'Start the dev entry-point for any matching plugin package in the repo',
(opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]),
Array<string>(),
)
.option(...configOption)
.option(
'--inspect [host]',
'Enable debugger in Node.js environments. Applies to backend package only',
)
.option(
'--inspect-brk [host]',
'Enable debugger in Node.js environments, breaking before code starts. Applies to backend package only',
)
.option(
'--require <path...>',
'Add a --require argument to the node process. Applies to backend package only',
)
.option(
'--link <path>',
'Link an external workspace for module resolution',
)
.action(
lazy(() => import('../build/commands/repo/start'), 'command'),
);
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
reg.addCommand({
path: ['build-workspace'],
description:
+48
View File
@@ -15,6 +15,7 @@
*/
import { Command, Option } from 'commander';
import { lazy } from '../../lib/lazy';
import { configOption } from '../config';
export function registerRepoCommands(command: Command) {
command
@@ -35,6 +36,35 @@ export function registerRepoCommands(command: Command) {
'Minify the generated code. Does not apply to app package (app is minified by default).',
)
.action(lazy(() => import('./commands/repo/build'), 'command'));
command
.command('start')
.description('Starts packages in the repo for local development')
.argument(
'[packageNameOrPath...]',
'Run the specified package instead of the defaults.',
)
.option(
'--plugin <pluginId>',
'Start the dev entry-point for any matching plugin package in the repo',
(opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]),
Array<string>(),
)
.option(...configOption)
.option(
'--inspect [host]',
'Enable debugger in Node.js environments. Applies to backend package only',
)
.option(
'--inspect-brk [host]',
'Enable debugger in Node.js environments, breaking before code starts. Applies to backend package only',
)
.option(
'--require <path...>',
'Add a --require argument to the node process. Applies to backend package only',
)
.option('--link <path>', 'Link an external workspace for module resolution')
.action(lazy(() => import('../build/commands/repo/start'), 'command'));
}
export function registerPackageCommands(command: Command) {
@@ -61,6 +91,24 @@ export function registerPackageCommands(command: Command) {
Array<string>(),
)
.action(lazy(() => import('./commands/package/build'), 'command'));
command
.command('start')
.description('Start a package for local development')
.option(...configOption)
.option('--role <name>', 'Run the command with an explicit package role')
.option('--check', 'Enable type checking and linting if available')
.option('--inspect [host]', 'Enable debugger in Node.js environments')
.option(
'--inspect-brk [host]',
'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('./commands/package/start'), 'command'));
}
export function registerCommands(program: Command) {
@@ -15,8 +15,7 @@
*/
import { BundlingOptions, ModuleFederationOptions } from './types';
import { resolve as resolvePath, dirname } from 'path';
import chalk from 'chalk';
import { resolve as resolvePath } from 'path';
import webpack from 'webpack';
import { BundlingPaths } from './paths';
@@ -39,8 +38,6 @@ import { hasReactDomClient } from './hasReactDomClient';
import { createWorkspaceLinkingPlugins } from './linkWorkspaces';
import { ConfigInjectingHtmlWebpackPlugin } from './ConfigInjectingHtmlWebpackPlugin';
const BUILD_CACHE_ENV_VAR = 'BACKSTAGE_CLI_EXPERIMENTAL_BUILD_CACHE';
export function resolveBaseUrl(
config: Config,
moduleFederation?: ModuleFederationOptions,
@@ -231,8 +228,8 @@ export async function createConfig(
const isRemote = options.moduleFederation?.mode === 'remote';
const AdaptedModuleFederationPlugin = rspack
? (rspack.container
.ModuleFederationPlugin as unknown as typeof ModuleFederationPlugin)
? (require('@module-federation/enhanced/rspack')
.ModuleFederationPlugin as typeof ModuleFederationPlugin)
: ModuleFederationPlugin;
const exposes = options.moduleFederation?.exposes
@@ -348,49 +345,6 @@ export async function createConfig(
const mode = isDev ? 'development' : 'production';
const optimization = optimizationConfig(options);
if (
mode === 'production' &&
process.env.EXPERIMENTAL_MODULE_FEDERATION &&
process.env.FORCE_REACT_DEVELOPMENT
) {
console.log(
chalk.yellow(
`⚠️ WARNING: Forcing react and react-dom into development mode. This build should not be used in production.`,
),
);
const reactPackageDirs = [
`${dirname(require.resolve('react/package.json'))}/`,
`${dirname(require.resolve('react-dom/package.json'))}/`,
];
// Don't define process.env.NODE_ENV with value matching config.mode. If we
// don't set this to false, webpack will define the value of
// process.env.NODE_ENV for us, and the definition below will be ignored.
optimization.nodeEnv = false;
// Instead, provide a custom definition which always uses "development" if
// the module is part of `react` or `react-dom`, and `config.mode` otherwise.
plugins.push(
new bundler.DefinePlugin({
'process.env.NODE_ENV': rspack
? // FIXME: see also https://github.com/web-infra-dev/rspack/issues/5606
JSON.stringify(mode)
: webpack.DefinePlugin.runtimeValue(({ module }) => {
if (
reactPackageDirs.some(val => module.resource.startsWith(val))
) {
return '"development"';
}
return `"${mode}"`;
}),
}),
);
}
const withCache = yn(process.env[BUILD_CACHE_ENV_VAR], { default: false });
return {
mode,
profile: false,
@@ -473,13 +427,5 @@ export async function createConfig(
}),
},
plugins,
...(withCache && {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename],
},
},
}),
};
}
@@ -220,6 +220,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
});
const outputSuccessCache = [];
const jsonResults = [];
let errorOutput = '';
@@ -238,7 +239,11 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
// dump of all warnings that might be irrelevant
if (resultText) {
if (opts.outputFile) {
errorOutput += `${resultText}\n`;
if (opts.format === 'json') {
jsonResults.push(resultText);
} else {
errorOutput += `${resultText}\n`;
}
} else {
console.log();
console.log(resultText.trimStart());
@@ -249,6 +254,14 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
}
}
if (opts.format === 'json') {
let mergedJsonResults: any[] = [];
for (const jsonResult of jsonResults) {
mergedJsonResults = mergedJsonResults.concat(JSON.parse(jsonResult));
}
errorOutput = JSON.stringify(mergedJsonResults, null, 2);
}
if (opts.outputFile && errorOutput) {
await fs.writeFile(paths.resolveTargetRoot(opts.outputFile), errorOutput);
}
-99
View File
@@ -1,99 +0,0 @@
/*
* 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 { Command } from 'commander';
import { createCliPlugin } from '../../wiring/factory';
import { lazy } from '../../lib/lazy';
import { configOption } from '../config';
export const startPlugin = createCliPlugin({
pluginId: 'start',
init: async reg => {
reg.addCommand({
path: ['package', 'start'],
description: 'Start a package for local development',
execute: async ({ args }) => {
const command = new Command();
const defaultCommand = command
.option(...configOption)
.option(
'--role <name>',
'Run the command with an explicit package role',
)
.option('--check', 'Enable type checking and linting if available')
.option('--inspect [host]', 'Enable debugger in Node.js environments')
.option(
'--inspect-brk [host]',
'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('./commands/package/start'), 'command'));
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
reg.addCommand({
path: ['repo', 'start'],
description: 'Starts packages in the repo for local development',
execute: async ({ args }) => {
const command = new Command();
const defaultCommand = command
.argument(
'[...packageNameOrPath]',
'Run the specified package instead of the defaults.',
)
.option(
'--plugin <pluginId>',
'Start the dev entry-point for any matching plugin package in the repo',
(opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]),
Array<string>(),
)
.option(...configOption)
.option(
'--inspect [host]',
'Enable debugger in Node.js environments. Applies to backend package only',
)
.option(
'--inspect-brk [host]',
'Enable debugger in Node.js environments, breaking before code starts. Applies to backend package only',
)
.option(
'--require <path...>',
'Add a --require argument to the node process. Applies to backend package only',
)
.option(
'--link <path>',
'Link an external workspace for module resolution',
)
.action(lazy(() => import('./commands/repo/start'), 'command'));
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
},
});
export default startPlugin;
-69
View File
@@ -1,69 +0,0 @@
/*
* 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 { Command } from 'commander';
import { lazy } from '../../lib/lazy';
import { configOption } from '../config';
export function registerRepoCommands(command: Command) {
command
.command('start')
.description('Starts packages in the repo for local development')
.argument(
'[packageNameOrPath...]',
'Run the specified package instead of the defaults.',
)
.option(
'--plugin <pluginId>',
'Start the dev entry-point for any matching plugin package in the repo',
(opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]),
Array<string>(),
)
.option(...configOption)
.option(
'--inspect [host]',
'Enable debugger in Node.js environments. Applies to backend package only',
)
.option(
'--inspect-brk [host]',
'Enable debugger in Node.js environments, breaking before code starts. Applies to backend package only',
)
.option(
'--require <path...>',
'Add a --require argument to the node process. Applies to backend package only',
)
.option('--link <path>', 'Link an external workspace for module resolution')
.action(lazy(() => import('./commands/repo/start'), 'command'));
}
export function registerPackageCommands(command: Command) {
command
.command('start')
.description('Start a package for local development')
.option(...configOption)
.option('--role <name>', 'Run the command with an explicit package role')
.option('--check', 'Enable type checking and linting if available')
.option('--inspect [host]', 'Enable debugger in Node.js environments')
.option(
'--inspect-brk [host]',
'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('./commands/package/start'), 'command'));
}