cli: remove legacy backend start

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-10-11 10:09:42 +02:00
parent 6fa100e824
commit bc71665cb1
7 changed files with 29 additions and 309 deletions
+5
View File
@@ -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.
-2
View File
@@ -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",
+22 -77
View File
@@ -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();
}
-52
View File
@@ -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;
}
+2 -161
View File
@@ -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<webpack.Configuration> {
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<string>();
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<typeof nodeExternals>[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);
};
}
-1
View File
@@ -14,7 +14,6 @@
* limitations under the License.
*/
export { serveBackend } from './backend';
export { buildBundle } from './bundle';
export { getModuleFederationOptions } from './moduleFederation';
export { serveBundle } from './server';
-16
View File
@@ -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"