diff --git a/.changeset/violet-ears-dance.md b/.changeset/violet-ears-dance.md new file mode 100644 index 0000000000..dba3671338 --- /dev/null +++ b/.changeset/violet-ears-dance.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': minor +--- + +**BREAKING**: The `LEGACY_BACKEND_START` flag has been removed, along with support for `src/run.ts` as the development entry point. diff --git a/packages/cli/package.json b/packages/cli/package.json index 0ca580dd91..53f8e3f560 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -141,7 +141,6 @@ "rollup-plugin-esbuild": "^6.1.1", "rollup-plugin-postcss": "^4.0.0", "rollup-pluginutils": "^2.8.2", - "run-script-webpack-plugin": "^0.2.0", "semver": "^7.5.3", "style-loader": "^3.3.1", "sucrase": "^3.20.2", @@ -152,7 +151,6 @@ "util": "^0.12.3", "webpack": "^5.70.0", "webpack-dev-server": "^5.0.0", - "webpack-node-externals": "^3.0.0", "yaml": "^2.0.0", "yargs": "^16.2.0", "yml-loader": "^2.1.0", diff --git a/packages/cli/src/commands/start/startBackend.ts b/packages/cli/src/commands/start/startBackend.ts index 857e113b81..c9b6496bb0 100644 --- a/packages/cli/src/commands/start/startBackend.ts +++ b/packages/cli/src/commands/start/startBackend.ts @@ -16,7 +16,6 @@ import fs from 'fs-extra'; import { paths } from '../../lib/paths'; -import { serveBackend } from '../../lib/bundler'; import { startBackendExperimental } from '../../lib/experimental/startBackendExperimental'; interface StartBackendOptions { @@ -27,89 +26,35 @@ interface StartBackendOptions { } export async function startBackend(options: StartBackendOptions) { - if (!process.env.LEGACY_BACKEND_START) { - const waitForExit = await startBackendExperimental({ - entry: 'src/index', - checksEnabled: false, // not supported - inspectEnabled: options.inspectEnabled, - inspectBrkEnabled: options.inspectBrkEnabled, - require: options.require, - }); + const waitForExit = await startBackendExperimental({ + entry: 'src/index', + checksEnabled: false, // not supported + inspectEnabled: options.inspectEnabled, + inspectBrkEnabled: options.inspectBrkEnabled, + require: options.require, + }); - await waitForExit(); - } else { - console.warn( - 'LEGACY_BACKEND_START is deprecated and will be removed in a future release', - ); - - const waitForExit = await cleanDistAndServeBackend({ - entry: 'src/index', - checksEnabled: options.checksEnabled, - inspectEnabled: options.inspectEnabled, - inspectBrkEnabled: options.inspectBrkEnabled, - require: options.require, - }); - - await waitForExit(); - } + await waitForExit(); } export async function startBackendPlugin(options: StartBackendOptions) { - if (!process.env.LEGACY_BACKEND_START) { - const hasDevIndexEntry = await fs.pathExists( - paths.resolveTarget('dev', 'index.ts'), - ); - if (!hasDevIndexEntry) { - console.warn( - `The 'dev' directory is missing. Please create a proper dev/index.ts in order to start the plugin.`, - ); - return; - } - - const waitForExit = await startBackendExperimental({ - entry: 'dev/index', - checksEnabled: false, // not supported - inspectEnabled: options.inspectEnabled, - inspectBrkEnabled: options.inspectBrkEnabled, - require: options.require, - }); - - await waitForExit(); - } else { - const hasEntry = await fs.pathExists(paths.resolveTarget('src', 'run.ts')); - if (!hasEntry) { - console.warn( - `src/run.ts is missing. Please create the file or run the command without LEGACY_BACKEND_START`, - ); - return; - } + const hasDevIndexEntry = await fs.pathExists( + paths.resolveTarget('dev', 'index.ts'), + ); + if (!hasDevIndexEntry) { console.warn( - 'LEGACY_BACKEND_START is deprecated and will be removed in a future release', + `The 'dev' directory is missing. Please create a proper dev/index.ts in order to start the plugin.`, ); - - const waitForExit = await cleanDistAndServeBackend({ - entry: 'src/run', - checksEnabled: options.checksEnabled, - inspectEnabled: options.inspectEnabled, - inspectBrkEnabled: options.inspectBrkEnabled, - require: options.require, - }); - - await waitForExit(); + return; } -} -async function cleanDistAndServeBackend(options: { - entry: string; - checksEnabled: boolean; - inspectEnabled: boolean; - inspectBrkEnabled: boolean; - require?: string; -}) { - // Cleaning dist/ before we start the dev process helps work around an issue - // where we end up with the entrypoint executing multiple times, causing - // a port bind conflict among other things. - await fs.remove(paths.resolveTarget('dist')); + const waitForExit = await startBackendExperimental({ + entry: 'dev/index', + checksEnabled: false, // not supported + inspectEnabled: options.inspectEnabled, + inspectBrkEnabled: options.inspectBrkEnabled, + require: options.require, + }); - return serveBackend(options); + await waitForExit(); } diff --git a/packages/cli/src/lib/bundler/backend.ts b/packages/cli/src/lib/bundler/backend.ts deleted file mode 100644 index 598b77bc51..0000000000 --- a/packages/cli/src/lib/bundler/backend.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2020 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 webpack from 'webpack'; -import { createBackendConfig } from './config'; -import { resolveBundlingPaths } from './paths'; -import { BackendServeOptions } from './types'; - -export async function serveBackend(options: BackendServeOptions) { - const paths = resolveBundlingPaths(options); - const config = await createBackendConfig(paths, { - ...options, - isDev: true, - }); - - // Webpack only replaces occurrences of this in code it touches, which does - // not include dependencies in node_modules. So we set it here at runtime as well. - (process.env as { NODE_ENV: string }).NODE_ENV = 'development'; - - const compiler = webpack(config, (err: Error | null) => { - if (err) { - console.error(err); - } else console.log('Build succeeded'); - }); - - const waitForExit = async () => { - for (const signal of ['SIGINT', 'SIGTERM'] as const) { - process.on(signal, () => { - // exit instead of resolve. The process is shutting down and resolving a promise here logs an error - compiler.close(() => process.exit()); - }); - } - - // Block indefinitely and wait for the interrupt signal - return new Promise(() => {}); - }; - - return waitForExit; -} diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index cc8a467bc2..9bfd9628a2 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -14,16 +14,11 @@ * limitations under the License. */ -import { - BackendBundlingOptions, - BundlingOptions, - ModuleFederationOptions, -} from './types'; -import { posix as posixPath, resolve as resolvePath, dirname } from 'path'; +import { BundlingOptions, ModuleFederationOptions } from './types'; +import { resolve as resolvePath, dirname } from 'path'; import chalk from 'chalk'; import webpack, { ProvidePlugin } from 'webpack'; -import { BackstagePackage } from '@backstage/cli-node'; import { BundlingPaths } from './paths'; import { Config } from '@backstage/config'; import ESLintPlugin from 'eslint-webpack-plugin'; @@ -32,16 +27,13 @@ import HtmlWebpackPlugin from 'html-webpack-plugin'; import { ModuleFederationPlugin } from '@module-federation/enhanced/webpack'; import { LinkedPackageResolvePlugin } from './LinkedPackageResolvePlugin'; import ModuleScopePlugin from 'react-dev-utils/ModuleScopePlugin'; -import { RunScriptWebpackPlugin } from 'run-script-webpack-plugin'; import ReactRefreshPlugin from '@pmmmwh/react-refresh-webpack-plugin'; import { paths as cliPaths } from '../../lib/paths'; import fs from 'fs-extra'; import { getPackages } from '@manypkg/get-packages'; import { isChildPath } from '@backstage/cli-common'; -import nodeExternals from 'webpack-node-externals'; import { optimization as optimizationConfig } from './optimization'; import pickBy from 'lodash/pickBy'; -import { readEntryPoints } from '../entryPoints'; import { runPlain } from '../run'; import { transforms } from './transforms'; import { version } from '../../lib/version'; @@ -432,154 +424,3 @@ export async function createConfig( : {}), }; } - -export async function createBackendConfig( - paths: BundlingPaths, - options: BackendBundlingOptions, -): Promise { - const { checksEnabled, isDev } = options; - - // Find all local monorepo packages and their node_modules, and mark them as external. - const { packages } = await getPackages(cliPaths.targetDir); - const localPackageEntryPoints = packages.flatMap(p => { - const entryPoints = readEntryPoints((p as BackstagePackage).packageJson); - return entryPoints.map(e => posixPath.join(p.packageJson.name, e.mount)); - }); - const moduleDirs = packages.map(p => resolvePath(p.dir, 'node_modules')); - // See frontend config - const externalPkgs = packages.filter(p => !isChildPath(paths.root, p.dir)); - - const { loaders } = transforms({ ...options, isBackend: true }); - - const runScriptNodeArgs = new Array(); - if (options.inspectEnabled) { - const inspect = - typeof options.inspectEnabled === 'string' - ? `--inspect=${options.inspectEnabled}` - : '--inspect'; - runScriptNodeArgs.push(inspect); - } else if (options.inspectBrkEnabled) { - const inspect = - typeof options.inspectBrkEnabled === 'string' - ? `--inspect-brk=${options.inspectBrkEnabled}` - : '--inspect-brk'; - runScriptNodeArgs.push(inspect); - } - if (options.require) { - runScriptNodeArgs.push(`--require=${options.require}`); - } - - return { - mode: isDev ? 'development' : 'production', - profile: false, - ...(isDev - ? { - watch: true, - watchOptions: { - ignored: /node_modules\/(?!\@backstage)/, - }, - } - : {}), - externals: [ - nodeExternalsWithResolve({ - modulesDir: paths.rootNodeModules, - additionalModuleDirs: moduleDirs, - allowlist: ['webpack/hot/poll?100', ...localPackageEntryPoints], - }), - ], - target: 'node' as const, - node: { - /* eslint-disable-next-line no-restricted-syntax */ - __dirname: true, - __filename: true, - global: true, - }, - bail: false, - performance: { - hints: false, // we check the gzip size instead - }, - devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map', - context: paths.targetPath, - entry: [ - 'webpack/hot/poll?100', - paths.targetRunFile ? paths.targetRunFile : paths.targetEntry, - ], - resolve: { - extensions: ['.ts', '.mjs', '.js', '.json'], - mainFields: ['main'], - modules: [paths.rootNodeModules, ...moduleDirs], - plugins: [ - new LinkedPackageResolvePlugin(paths.rootNodeModules, externalPkgs), - new ModuleScopePlugin( - [paths.targetSrc, paths.targetDev], - [paths.targetPackageJson], - ), - ], - }, - module: { - rules: loaders, - }, - output: { - path: paths.targetDist, - filename: isDev ? '[name].js' : '[name].[hash:8].js', - chunkFilename: isDev - ? '[name].chunk.js' - : '[name].[chunkhash:8].chunk.js', - ...(isDev - ? { - devtoolModuleFilenameTemplate: (info: any) => - `file:///${resolvePath(info.absoluteResourcePath).replace( - /\\/g, - '/', - )}`, - } - : {}), - }, - plugins: [ - new RunScriptWebpackPlugin({ - name: 'main.js', - nodeArgs: runScriptNodeArgs.length > 0 ? runScriptNodeArgs : undefined, - args: process.argv.slice(3), // drop `node backstage-cli backend:dev` - }), - new webpack.HotModuleReplacementPlugin(), - ...(checksEnabled - ? [ - new ForkTsCheckerWebpackPlugin({ - typescript: { configFile: paths.targetTsConfig }, - }), - new ESLintPlugin({ - files: ['**/*.(ts|tsx|mts|cts|js|jsx|mjs|cjs)'], - }), - ] - : []), - ], - }; -} - -// This makes the module resolution happen from the context of each non-external module, rather -// than the main entrypoint. This fixes a bug where dependencies would be resolved from the backend -// package rather than each individual backend package and plugin. -// -// TODO(Rugvip): Feature suggestion/contribute this to webpack-externals -function nodeExternalsWithResolve( - options: Parameters[0], -) { - let currentContext: string; - const externals = nodeExternals({ - ...options, - importType(request) { - const resolved = require.resolve(request, { - paths: [currentContext], - }); - return `commonjs ${resolved}`; - }, - }); - - return ( - { context, request }: { context?: string; request?: string }, - callback: any, - ) => { - currentContext = context!; - return externals(context, request, callback); - }; -} diff --git a/packages/cli/src/lib/bundler/index.ts b/packages/cli/src/lib/bundler/index.ts index 2219784dc9..d8dd6ed0d6 100644 --- a/packages/cli/src/lib/bundler/index.ts +++ b/packages/cli/src/lib/bundler/index.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -export { serveBackend } from './backend'; export { buildBundle } from './bundle'; export { getModuleFederationOptions } from './moduleFederation'; export { serveBundle } from './server'; diff --git a/yarn.lock b/yarn.lock index 7d4e6a0452..25f7acd752 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4037,7 +4037,6 @@ __metadata: rollup-plugin-esbuild: ^6.1.1 rollup-plugin-postcss: ^4.0.0 rollup-pluginutils: ^2.8.2 - run-script-webpack-plugin: ^0.2.0 semver: ^7.5.3 style-loader: ^3.3.1 sucrase: ^3.20.2 @@ -4051,7 +4050,6 @@ __metadata: vite-plugin-node-polyfills: ^0.22.0 webpack: ^5.70.0 webpack-dev-server: ^5.0.0 - webpack-node-externals: ^3.0.0 yaml: ^2.0.0 yargs: ^16.2.0 yml-loader: ^2.1.0 @@ -40212,13 +40210,6 @@ __metadata: languageName: node linkType: hard -"run-script-webpack-plugin@npm:^0.2.0": - version: 0.2.0 - resolution: "run-script-webpack-plugin@npm:0.2.0" - checksum: 1f5df65b726e098d602b4cc27472d9e2cd88841862f7ca2112f702b01f3c4fc1cd89b54fa63780691d988c9ab36cc9adc08a6fa056cdb9c7b85b027b21ba6cdd - languageName: node - linkType: hard - "rxjs@npm:7.8.1, rxjs@npm:^7.2.0, rxjs@npm:^7.5.5": version: 7.8.1 resolution: "rxjs@npm:7.8.1" @@ -44521,13 +44512,6 @@ __metadata: languageName: node linkType: hard -"webpack-node-externals@npm:^3.0.0": - version: 3.0.0 - resolution: "webpack-node-externals@npm:3.0.0" - checksum: 355080c35c821115b97dda8c93d9d0565a90a6012a532324eb0d6a64f8f0d609431fd29504fc7ce414755841ac14f601f3eef99472c2c5dc00233b504ebe73f2 - languageName: node - linkType: hard - "webpack-sources@npm:^1.4.3": version: 1.4.3 resolution: "webpack-sources@npm:1.4.3"