feat: move build command to modular format

Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>
This commit is contained in:
aramissennyeydd
2025-01-28 21:24:32 -05:00
parent 2bbb493280
commit eb723128f8
38 changed files with 262 additions and 104 deletions
+1
View File
@@ -25,5 +25,6 @@ import chalk from 'chalk';
);
const initializer = new CliInitializer();
initializer.add(import('./modules/config/alpha'));
initializer.add(import('./modules/build/alpha'));
await initializer.run();
})();
+9 -59
View File
@@ -14,36 +14,24 @@
* limitations under the License.
*/
import { Command, Option } from 'commander';
import { Command } from 'commander';
import { lazy } from '../lib/lazy';
import {
configOption,
registerCommands as registerConfigCommands,
} from '../modules/config';
import {
registerPackageCommands as registerPackageBuildCommands,
registerRepoCommands as registerRepoBuildCommands,
registerCommands as registerBuildCommands,
} from '../modules/build';
export function registerRepoCommand(program: Command) {
const command = program
.command('repo [command]')
.description('Command that run across an entire Backstage project');
command
.command('build')
.description(
'Build packages in the project, excluding bundled app and backend packages.',
)
.option(
'--all',
'Build all packages, including bundled app and backend packages.',
)
.option(
'--since <ref>',
'Only build packages and their dev dependents that changed since the specified ref',
)
.option(
'--minify',
'Minify the generated code. Does not apply to app package (app is minified by default).',
)
.action(lazy(() => import('./repo/build'), 'command'));
registerRepoBuildCommands(command);
command
.command('lint')
@@ -139,29 +127,7 @@ export function registerScriptCommand(program: Command) {
.option('--link <path>', 'Link an external workspace for module resolution')
.action(lazy(() => import('./start'), 'command'));
command
.command('build')
.description('Build a package for production deployment or publishing')
.option('--role <name>', 'Run the command with an explicit package role')
.option(
'--minify',
'Minify the generated code. Does not apply to app package (app is minified by default).',
)
.option(
'--skip-build-dependencies',
'Skip the automatic building of local dependencies. Applies to backend packages only.',
)
.option(
'--stats',
'If bundle stats are available, write them to the output directory. Applies to app packages only.',
)
.option(
'--config <path>',
'Config files to load instead of app-config.yaml. Applies to app packages only.',
(opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]),
Array<string>(),
)
.action(lazy(() => import('./build'), 'command'));
registerPackageBuildCommands(command);
command
.command('lint [directories...]')
@@ -281,6 +247,7 @@ export function registerCommands(program: Command) {
registerRepoCommand(program);
registerScriptCommand(program);
registerMigrateCommand(program);
registerBuildCommands(program);
program
.command('versions:bump')
@@ -313,23 +280,6 @@ export function registerCommands(program: Command) {
)
.action(lazy(() => import('./versions/migrate'), 'default'));
program
.command('build-workspace <workspace-dir> [packages...]')
.addOption(
new Option(
'--alwaysYarnPack',
'Alias for --alwaysPack for backwards compatibility.',
)
.implies({ alwaysPack: true })
.hideHelp(true),
)
.option(
'--alwaysPack',
'Force workspace output to be a result of running `yarn pack` on each package (warning: very slow)',
)
.description('Builds a temporary dist workspace from the provided packages')
.action(lazy(() => import('./buildWorkspace'), 'default'));
program
.command('create-github-app <github-org>')
.description('Create new GitHub App in your organization.')
+1 -1
View File
@@ -17,7 +17,7 @@
import {
productionPack,
revertProductionPack,
} from '../lib/packager/productionPack';
} from '../modules/build/lib/packager/productionPack';
import { paths } from '../lib/paths';
import fs from 'fs-extra';
import { publishPreflightCheck } from '../lib/publishing';
@@ -16,7 +16,10 @@
import { readJson } from 'fs-extra';
import { resolve as resolvePath } from 'path';
import { getModuleFederationOptions, serveBundle } from '../../lib/bundler';
import {
getModuleFederationOptions,
serveBundle,
} from '../../modules/build/lib/bundler';
import { paths } from '../../lib/paths';
import { BackstagePackageJson } from '@backstage/cli-node';
+116
View File
@@ -0,0 +1,116 @@
/*
* 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, Option } from 'commander';
import { createCliPlugin } from '../../wiring/factory';
import { lazy } from '../../lib/lazy';
import { registerPackageCommands } from '.';
export const buildPlugin = createCliPlugin({
pluginId: 'build',
init: async reg => {
reg.addCommand({
path: ['package', 'build'],
description: 'Build a package for production deployment or publishing',
execute: async ({ args }) => {
const command = new Command();
const defaultCommand = command
.option(
'--role <name>',
'Run the command with an explicit package role',
)
.option(
'--minify',
'Minify the generated code. Does not apply to app package (app is minified by default).',
)
.option(
'--skip-build-dependencies',
'Skip the automatic building of local dependencies. Applies to backend packages only.',
)
.option(
'--stats',
'If bundle stats are available, write them to the output directory. Applies to app packages only.',
)
.option(
'--config <path>',
'Config files to load instead of app-config.yaml. Applies to app packages only.',
(opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]),
Array<string>(),
)
.action(lazy(() => import('./commands/package/build'), 'command'));
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
reg.addCommand({
path: ['repo', 'build'],
description:
'Build packages in the project, excluding bundled app and backend packages.',
execute: async ({ args }) => {
const command = new Command();
// This command expect `package build` to be registered, as its used to parse
// individual plugins' package build scripts.
registerPackageCommands(command.command('package'));
const defaultCommand = command
.option(
'--all',
'Build all packages, including bundled app and backend packages.',
)
.option(
'--since <ref>',
'Only build packages and their dev dependents that changed since the specified ref',
)
.option(
'--minify',
'Minify the generated code. Does not apply to app package (app is minified by default).',
)
.action(lazy(() => import('./commands/repo/build'), 'command'));
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
reg.addCommand({
path: ['build-workspace'],
description:
'Builds a temporary dist workspace from the provided packages',
execute: async ({ args }) => {
const command = new Command();
const defaultCommand = command
.arguments('<workspace-dir> [packages...]')
.addOption(
new Option(
'--alwaysYarnPack',
'Alias for --alwaysPack for backwards compatibility.',
)
.implies({ alwaysPack: true })
.hideHelp(true),
)
.option(
'--alwaysPack',
'Force workspace output to be a result of running `yarn pack` on each package (warning: very slow)',
)
.action(lazy(() => import('./commands/buildWorkspace'), 'default'));
console.log(args);
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
},
});
export default buildPlugin;
@@ -15,13 +15,13 @@
*/
import { OptionValues } from 'commander';
import { buildPackage, Output } from '../../lib/builder';
import { findRoleFromCommand } from '../../lib/role';
import { buildPackage, Output } from '../../../lib/builder';
import { findRoleFromCommand } from '../../../../../lib/role';
import { PackageGraph, PackageRoles } from '@backstage/cli-node';
import { paths } from '../../lib/paths';
import { buildFrontend } from './buildFrontend';
import { buildBackend } from './buildBackend';
import { isValidUrl } from '../../lib/urls';
import { paths } from '../../../../../lib/paths';
import { buildFrontend } from '../../../lib/buildFrontend';
import { buildBackend } from '../../../lib/buildBackend';
import { isValidUrl } from '../../../../../lib/urls';
import chalk from 'chalk';
export async function command(opts: OptionValues): Promise<void> {
@@ -18,16 +18,16 @@ import chalk from 'chalk';
import { Command, OptionValues } from 'commander';
import { relative as relativePath } from 'path';
import { buildPackages, getOutputsForRole } from '../../lib/builder';
import { paths } from '../../lib/paths';
import { paths } from '../../../../lib/paths';
import {
BackstagePackage,
PackageGraph,
PackageRoles,
} from '@backstage/cli-node';
import { runParallelWorkers } from '../../lib/parallel';
import { buildFrontend } from '../build/buildFrontend';
import { buildBackend } from '../build/buildBackend';
import { createScriptOptionsParser } from './optionsParser';
import { runParallelWorkers } from '../../../../lib/parallel';
import { buildFrontend } from '../../lib/buildFrontend';
import { buildBackend } from '../../lib/buildBackend';
import { createScriptOptionsParser } from '../../../../commands/repo/optionsParser';
export async function command(opts: OptionValues, cmd: Command): Promise<void> {
let packages = await PackageGraph.listTargetPackages();
+68
View File
@@ -0,0 +1,68 @@
import { Command, Option } from 'commander';
import { lazy } from '../../lib/lazy';
export function registerRepoCommands(command: Command) {
command
.command('build')
.description(
'Build packages in the project, excluding bundled app and backend packages.',
)
.option(
'--all',
'Build all packages, including bundled app and backend packages.',
)
.option(
'--since <ref>',
'Only build packages and their dev dependents that changed since the specified ref',
)
.option(
'--minify',
'Minify the generated code. Does not apply to app package (app is minified by default).',
)
.action(lazy(() => import('./commands/repo/build'), 'command'));
}
export function registerPackageCommands(command: Command) {
command
.command('build')
.description('Build a package for production deployment or publishing')
.option('--role <name>', 'Run the command with an explicit package role')
.option(
'--minify',
'Minify the generated code. Does not apply to app package (app is minified by default).',
)
.option(
'--skip-build-dependencies',
'Skip the automatic building of local dependencies. Applies to backend packages only.',
)
.option(
'--stats',
'If bundle stats are available, write them to the output directory. Applies to app packages only.',
)
.option(
'--config <path>',
'Config files to load instead of app-config.yaml. Applies to app packages only.',
(opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]),
Array<string>(),
)
.action(lazy(() => import('./commands/package/build'), 'command'));
}
export function registerCommands(program: Command) {
program
.command('build-workspace <workspace-dir> [packages...]')
.addOption(
new Option(
'--alwaysYarnPack',
'Alias for --alwaysPack for backwards compatibility.',
)
.implies({ alwaysPack: true })
.hideHelp(true),
)
.option(
'--alwaysPack',
'Force workspace output to be a result of running `yarn pack` on each package (warning: very slow)',
)
.description('Builds a temporary dist workspace from the provided packages')
.action(lazy(() => import('./commands/buildWorkspace'), 'default'));
}
@@ -18,9 +18,9 @@ import os from 'os';
import fs from 'fs-extra';
import { resolve as resolvePath } from 'path';
import tar, { CreateOptions } from 'tar';
import { createDistWorkspace } from '../../lib/packager';
import { getEnvironmentParallelism } from '../../lib/parallel';
import { buildPackage, Output } from '../../lib/builder';
import { createDistWorkspace } from './packager';
import { getEnvironmentParallelism } from '../../../lib/parallel';
import { buildPackage, Output } from './builder';
import { PackageGraph } from '@backstage/cli-node';
const BUNDLE_FILE = 'bundle.tar.gz';
@@ -16,9 +16,9 @@
import fs from 'fs-extra';
import { resolve as resolvePath } from 'path';
import { buildBundle, getModuleFederationOptions } from '../../lib/bundler';
import { getEnvironmentParallelism } from '../../lib/parallel';
import { loadCliConfig } from '../../modules/config/lib/config';
import { buildBundle, getModuleFederationOptions } from './bundler';
import { getEnvironmentParallelism } from '../../../lib/parallel';
import { loadCliConfig } from '../../config/lib/config';
import { BackstagePackageJson } from '@backstage/cli-node';
interface BuildAppOptions {
@@ -38,10 +38,10 @@ import {
import { forwardFileImports } from './plugins';
import { BuildOptions, Output } from './types';
import { paths } from '../paths';
import { paths } from '../../../../lib/paths';
import { BackstagePackageJson } from '@backstage/cli-node';
import { svgrTemplate } from '../svgrTemplate';
import { readEntryPoints } from '../entryPoints';
import { svgrTemplate } from '../../../../lib/svgrTemplate';
import { readEntryPoints } from '../../../../lib/entryPoints';
const SCRIPT_EXTS = ['.js', '.jsx', '.ts', '.tsx'];
@@ -18,11 +18,11 @@ import fs from 'fs-extra';
import { rollup, RollupOptions } from 'rollup';
import chalk from 'chalk';
import { relative as relativePath, resolve as resolvePath } from 'path';
import { paths } from '../paths';
import { paths } from '../../../../lib/paths';
import { makeRollupConfigs } from './config';
import { BuildOptions, Output } from './types';
import { PackageRoles } from '@backstage/cli-node';
import { runParallelWorkers } from '../parallel';
import { runParallelWorkers } from '../../../../lib/parallel';
export function formatErrorMessage(error: any) {
let msg = '';
@@ -27,13 +27,13 @@ import HtmlWebpackPlugin from 'html-webpack-plugin';
import { ModuleFederationPlugin } from '@module-federation/enhanced/webpack';
import ModuleScopePlugin from 'react-dev-utils/ModuleScopePlugin';
import ReactRefreshPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import { paths as cliPaths } from '../../lib/paths';
import { paths as cliPaths } from '../../../../lib/paths';
import fs from 'fs-extra';
import { optimization as optimizationConfig } from './optimization';
import pickBy from 'lodash/pickBy';
import { runPlain } from '../run';
import { runPlain } from '../../../../lib/run';
import { transforms } from './transforms';
import { version } from '../../lib/version';
import { version } from '../../../../lib/version';
import yn from 'yn';
import { hasReactDomClient } from './hasReactDomClient';
import { createWorkspaceLinkingPlugins } from './linkWorkspaces';
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { paths } from '../paths';
import { paths } from '../../../../lib/paths';
export function hasReactDomClient() {
try {
@@ -17,7 +17,7 @@
import { relative as relativePath } from 'path';
import { getPackages } from '@manypkg/get-packages';
import webpack from 'webpack';
import { paths } from '../paths';
import { paths } from '../../../../lib/paths';
/**
* This returns of collection of plugins that links a separate workspace into
@@ -17,11 +17,11 @@
import chalk from 'chalk';
import { ModuleFederationOptions } from './types';
import { BackstagePackageJson } from '@backstage/cli-node';
import { readEntryPoints } from '../entryPoints';
import { readEntryPoints } from '../../../../lib/entryPoints';
import {
createTypeDistProject,
getEntryPointDefaultFeatureType,
} from '../typeDistProject';
} from '../../../../lib/typeDistProject';
export async function getModuleFederationOptions(
packageJson: BackstagePackageJson,
@@ -20,7 +20,7 @@ import chokidar from 'chokidar';
import fs from 'fs-extra';
import PQueue from 'p-queue';
import { join as joinPath, resolve as resolvePath } from 'path';
import { paths as cliPaths } from '../paths';
import { paths as cliPaths } from '../../../../lib/paths';
const DETECTED_MODULES_MODULE_NAME = '__backstage-autodetected-plugins__';
@@ -16,7 +16,7 @@
import fs from 'fs-extra';
import { resolve as resolvePath } from 'path';
import { paths } from '../paths';
import { paths } from '../../../../lib/paths';
export type BundlingPathsOptions = {
// bundle entrypoint, e.g. 'src/index'
@@ -21,8 +21,8 @@ import openBrowser from 'react-dev-utils/openBrowser';
import webpack from 'webpack';
import WebpackDevServer from 'webpack-dev-server';
import { paths as libPaths } from '../../lib/paths';
import { loadCliConfig } from '../../modules/config/lib/config';
import { paths as libPaths } from '../../../../lib/paths';
import { loadCliConfig } from '../../../config/lib/config';
import { createConfig, resolveBaseUrl, resolveEndpoint } from './config';
import { createDetectedModulesEntryPoint } from './packageDetection';
import { resolveBundlingPaths, resolveOptionalBundlingPaths } from './paths';
@@ -16,7 +16,7 @@
import { RuleSetRule, WebpackPluginInstance } from 'webpack';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import { svgrTemplate } from '../svgrTemplate';
import { svgrTemplate } from '../../../../lib/svgrTemplate';
type Transforms = {
loaders: RuleSetRule[];
@@ -24,12 +24,12 @@ import {
import { tmpdir } from 'os';
import tar, { CreateOptions, FileOptions } from 'tar';
import partition from 'lodash/partition';
import { paths } from '../paths';
import { run } from '../run';
import { paths } from '../../../../lib/paths';
import { run } from '../../../../lib/run';
import {
dependencies as cliDependencies,
devDependencies as cliDevDependencies,
} from '../../../package.json';
} from '../../../../../package.json';
import {
BuildOptions,
buildPackages,
@@ -42,8 +42,8 @@ import {
PackageGraph,
PackageGraphNode,
} from '@backstage/cli-node';
import { runParallelWorkers } from '../parallel';
import { createTypeDistProject } from '../typeDistProject';
import { runParallelWorkers } from '../../../../lib/parallel';
import { createTypeDistProject } from '../../../../lib/typeDistProject';
// These packages aren't safe to pack in parallel since the CLI depends on them
const UNSAFE_PACKAGES = [
@@ -18,8 +18,8 @@ import fs from 'fs-extra';
import npmPackList from 'npm-packlist';
import { resolve as resolvePath, posix as posixPath } from 'path';
import { BackstagePackageJson } from '@backstage/cli-node';
import { readEntryPoints } from '../entryPoints';
import { getEntryPointDefaultFeatureType } from '../typeDistProject';
import { readEntryPoints } from '../../../../lib/entryPoints';
import { getEntryPointDefaultFeatureType } from '../../../../lib/typeDistProject';
import { Project } from 'ts-morph';
const PKG_PATH = 'package.json';
@@ -16,7 +16,7 @@
import { execFileSync } from 'child_process';
import { resolve as resolvePath } from 'path';
import { Output, buildPackage } from '../../lib/builder';
import { Output, buildPackage } from '../../modules/build/lib/builder';
const exportValues = {
all: {
+21 -1
View File
@@ -93,8 +93,28 @@ export class CliInitializer {
.allowExcessArguments(true)
.action(async () => {
try {
const args = program.parseOptions(process.argv);
const nonProcessArgs = args.operands.slice(2);
const positionalArgs = [];
let index = 0;
for (
let argIndex = 0;
argIndex < nonProcessArgs.length;
argIndex++
) {
// Skip the command name
if (
argIndex === index &&
node.command.path[argIndex] === nonProcessArgs[argIndex]
) {
index += 1;
continue;
}
positionalArgs.push(nonProcessArgs[argIndex]);
}
await node.command.execute({
args: program.parseOptions(process.argv).unknown,
args: [...positionalArgs, ...args.unknown],
});
process.exit(0);
} catch (error) {