From bc71665cb12b04f17cfe59142e454104864803b1 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 11 Oct 2024 10:09:42 +0200 Subject: [PATCH 1/2] cli: remove legacy backend start Signed-off-by: Patrik Oldsberg --- .changeset/violet-ears-dance.md | 5 + packages/cli/package.json | 2 - .../cli/src/commands/start/startBackend.ts | 99 +++-------- packages/cli/src/lib/bundler/backend.ts | 52 ------ packages/cli/src/lib/bundler/config.ts | 163 +----------------- packages/cli/src/lib/bundler/index.ts | 1 - yarn.lock | 16 -- 7 files changed, 29 insertions(+), 309 deletions(-) create mode 100644 .changeset/violet-ears-dance.md delete mode 100644 packages/cli/src/lib/bundler/backend.ts 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" From 1e416370784b15d16254c03a8141d00208d659ea Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 11 Oct 2024 10:20:43 +0200 Subject: [PATCH 2/2] cli: internal refactor of backend start implementation Signed-off-by: Patrik Oldsberg --- .../cli/src/commands/start/startBackend.ts | 8 +++----- packages/cli/src/lib/bundler/types.ts | 7 ------- .../src/lib/{experimental => ipc}/IpcServer.ts | 0 .../{experimental => ipc}/ServerDataStore.ts | 0 packages/cli/src/lib/ipc/index.ts | 18 ++++++++++++++++++ packages/cli/src/lib/runner/index.ts | 17 +++++++++++++++++ .../runBackend.ts} | 18 +++++++++++++----- 7 files changed, 51 insertions(+), 17 deletions(-) rename packages/cli/src/lib/{experimental => ipc}/IpcServer.ts (100%) rename packages/cli/src/lib/{experimental => ipc}/ServerDataStore.ts (100%) create mode 100644 packages/cli/src/lib/ipc/index.ts create mode 100644 packages/cli/src/lib/runner/index.ts rename packages/cli/src/lib/{experimental/startBackendExperimental.ts => runner/runBackend.ts} (90%) diff --git a/packages/cli/src/commands/start/startBackend.ts b/packages/cli/src/commands/start/startBackend.ts index c9b6496bb0..4f62044de7 100644 --- a/packages/cli/src/commands/start/startBackend.ts +++ b/packages/cli/src/commands/start/startBackend.ts @@ -16,7 +16,7 @@ import fs from 'fs-extra'; import { paths } from '../../lib/paths'; -import { startBackendExperimental } from '../../lib/experimental/startBackendExperimental'; +import { runBackend } from '../../lib/runner'; interface StartBackendOptions { checksEnabled: boolean; @@ -26,9 +26,8 @@ interface StartBackendOptions { } export async function startBackend(options: StartBackendOptions) { - const waitForExit = await startBackendExperimental({ + const waitForExit = await runBackend({ entry: 'src/index', - checksEnabled: false, // not supported inspectEnabled: options.inspectEnabled, inspectBrkEnabled: options.inspectBrkEnabled, require: options.require, @@ -48,9 +47,8 @@ export async function startBackendPlugin(options: StartBackendOptions) { return; } - const waitForExit = await startBackendExperimental({ + const waitForExit = await runBackend({ entry: 'dev/index', - checksEnabled: false, // not supported inspectEnabled: options.inspectEnabled, inspectBrkEnabled: options.inspectBrkEnabled, require: options.require, diff --git a/packages/cli/src/lib/bundler/types.ts b/packages/cli/src/lib/bundler/types.ts index f8e228c5c4..3f52e4f765 100644 --- a/packages/cli/src/lib/bundler/types.ts +++ b/packages/cli/src/lib/bundler/types.ts @@ -67,10 +67,3 @@ export type BackendBundlingOptions = { inspectBrkEnabled: boolean; require?: string; }; - -export type BackendServeOptions = BundlingPathsOptions & { - checksEnabled: boolean; - inspectEnabled: boolean; - inspectBrkEnabled: boolean; - require?: string; -}; diff --git a/packages/cli/src/lib/experimental/IpcServer.ts b/packages/cli/src/lib/ipc/IpcServer.ts similarity index 100% rename from packages/cli/src/lib/experimental/IpcServer.ts rename to packages/cli/src/lib/ipc/IpcServer.ts diff --git a/packages/cli/src/lib/experimental/ServerDataStore.ts b/packages/cli/src/lib/ipc/ServerDataStore.ts similarity index 100% rename from packages/cli/src/lib/experimental/ServerDataStore.ts rename to packages/cli/src/lib/ipc/ServerDataStore.ts diff --git a/packages/cli/src/lib/ipc/index.ts b/packages/cli/src/lib/ipc/index.ts new file mode 100644 index 0000000000..78ee98a942 --- /dev/null +++ b/packages/cli/src/lib/ipc/index.ts @@ -0,0 +1,18 @@ +/* + * 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. + */ + +export { IpcServer } from './IpcServer'; +export { ServerDataStore } from './ServerDataStore'; diff --git a/packages/cli/src/lib/runner/index.ts b/packages/cli/src/lib/runner/index.ts new file mode 100644 index 0000000000..e00ffaa3e5 --- /dev/null +++ b/packages/cli/src/lib/runner/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { runBackend } from './runBackend'; diff --git a/packages/cli/src/lib/experimental/startBackendExperimental.ts b/packages/cli/src/lib/runner/runBackend.ts similarity index 90% rename from packages/cli/src/lib/experimental/startBackendExperimental.ts rename to packages/cli/src/lib/runner/runBackend.ts index bd4bb94b17..f0ce971fee 100644 --- a/packages/cli/src/lib/experimental/startBackendExperimental.ts +++ b/packages/cli/src/lib/runner/runBackend.ts @@ -15,12 +15,9 @@ */ import { FSWatcher, watch } from 'chokidar'; - -import { BackendServeOptions } from '../bundler/types'; import type { ChildProcess } from 'child_process'; import { ctrlc } from 'ctrlc-windows'; -import { IpcServer } from './IpcServer'; -import { ServerDataStore } from './ServerDataStore'; +import { IpcServer, ServerDataStore } from '../ipc'; import debounce from 'lodash/debounce'; import { fileURLToPath } from 'url'; import { isAbsolute as isAbsolutePath } from 'path'; @@ -34,7 +31,18 @@ const loaderArgs = [ // TODO: Support modules, although there's currently no way to load them since import() is transpiled tp require() ]; -export async function startBackendExperimental(options: BackendServeOptions) { +export type RunBackendOptions = { + /** relative entry point path without extension, e.g. 'src/index' */ + entry: string; + /** Whether to forward the --inspect flag to the node process */ + inspectEnabled: boolean; + /** Whether to forward the --inspect-brk flag to the node process */ + inspectBrkEnabled: boolean; + /** Additional module to require via the --require flag to the node process */ + require?: string; +}; + +export async function runBackend(options: RunBackendOptions) { const envEnv = process.env as { NODE_ENV: string }; if (!envEnv.NODE_ENV) { envEnv.NODE_ENV = 'development';