From e6ef66c5d6c0f2ca7bc025f1d739eb5423287973 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 19 Jul 2023 14:39:35 +0200 Subject: [PATCH] cli: detect plugins at build time Signed-off-by: Vincenzo Scamporlino --- packages/cli/src/lib/bundler/bundle.ts | 1 + packages/cli/src/lib/bundler/config.ts | 58 +++++++++++++++++++++----- packages/cli/src/lib/bundler/server.ts | 2 +- packages/cli/src/lib/bundler/types.ts | 6 ++- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/lib/bundler/bundle.ts b/packages/cli/src/lib/bundler/bundle.ts index 7c45528c5c..8129f38102 100644 --- a/packages/cli/src/lib/bundler/bundle.ts +++ b/packages/cli/src/lib/bundler/bundle.ts @@ -40,6 +40,7 @@ export async function buildBundle(options: BuildOptions) { const { statsJsonEnabled, schema: configSchema } = options; const paths = resolveBundlingPaths(options); + const config = await createConfig(paths, { ...options, checksEnabled: false, diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 5e0176fae9..46ababaabc 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -26,13 +26,17 @@ import { isChildPath } from '@backstage/cli-common'; import { getPackages } from '@manypkg/get-packages'; import { optimization } from './optimization'; import { Config } from '@backstage/config'; -import { BundlingPaths } from './paths'; +import { + BundlingPaths, + BundlingPathsOptions, + resolveBundlingPaths, +} from './paths'; import { transforms } from './transforms'; import { LinkedPackageResolvePlugin } from './LinkedPackageResolvePlugin'; import { BundlingOptions, BackendBundlingOptions } from './types'; import { version } from '../../lib/version'; import { paths as cliPaths } from '../../lib/paths'; -import { BackstagePackage } from '@backstage/cli-node'; +import { BackstagePackage, BackstagePackageJson } from '@backstage/cli-node'; import { runPlain } from '../run'; import ESLintPlugin from 'eslint-webpack-plugin'; import pickBy from 'lodash/pickBy'; @@ -85,7 +89,7 @@ export async function createConfig( paths: BundlingPaths, options: BundlingOptions, ): Promise { - const { checksEnabled, isDev, frontendConfig, extraPackages = [] } = options; + const { checksEnabled, isDev, frontendConfig } = options; const { plugins, loaders } = transforms(options); // Any package that is part of the monorepo but outside the monorepo root dir need @@ -128,7 +132,12 @@ export async function createConfig( }), ); - const requirePackageScript = extraPackages + const detectedPlugins = await detectPlugins({ + config: options.fullConfig, + entry: options.entry, + targetDir: options.targetDir, + }); + const requirePackageScript = detectedPlugins ?.map(pkg => `{name: '${pkg}', module: require('${pkg}')}`) .join(','); @@ -177,12 +186,7 @@ export async function createConfig( }, devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map', context: paths.targetPath, - entry: [ - ...(extraPackages.length > 0 - ? [`backstage-autodetected-plugins.js`] - : []), - paths.targetEntry, - ], + entry: paths.targetEntry, resolve: { extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx', '.json', '.wasm'], mainFields: ['browser', 'module', 'main'], @@ -384,3 +388,37 @@ function nodeExternalsWithResolve( return externals(context, request, callback); }; } + +export async function detectPlugins({ + config, + entry, + targetDir, +}: { config: Config } & BundlingPathsOptions) { + const paths = resolveBundlingPaths({ entry, targetDir }); + const pkg: BackstagePackageJson = await fs.readJson(paths.targetPackageJson); + // TODO: proper + // Assumption for config string based on https://github.com/backstage/backstage/issues/18372 ^ + + const packageDetectionMode = + config.getOptional('app.experimental.packages') || 'all'; + + const allowedPackages = + packageDetectionMode === 'all' + ? Object.keys(pkg.dependencies ?? {}) + : config.getStringArray('app.experimental.packages'); + + return allowedPackages.reduce((packages, depName) => { + const depPackageJson: BackstagePackageJson = require(require.resolve( + `${depName}/package.json`, + { paths: [paths.targetPath] }, + )); + if ( + ['frontend-plugin', 'frontend-plugin-module'].includes( + depPackageJson.backstage?.role || '', + ) + ) { + packages.push(depName); + } + return packages; + }, [] as string[]); +} diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index aa81fede81..96eee2a387 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import fs from 'fs-extra'; import webpack from 'webpack'; import WebpackDevServer from 'webpack-dev-server'; import openBrowser from 'react-dev-utils/openBrowser'; import uniq from 'lodash/uniq'; +import fs from 'fs-extra'; import { createConfig, resolveBaseUrl } from './config'; import { ServeOptions } from './types'; diff --git a/packages/cli/src/lib/bundler/types.ts b/packages/cli/src/lib/bundler/types.ts index 8996ed995d..0dc6807b41 100644 --- a/packages/cli/src/lib/bundler/types.ts +++ b/packages/cli/src/lib/bundler/types.ts @@ -23,9 +23,12 @@ export type BundlingOptions = { isDev: boolean; frontendConfig: Config; getFrontendAppConfigs(): AppConfig[]; + frontendAppConfigs: AppConfig[]; + fullConfig: Config; baseUrl: URL; parallelism?: number; - extraPackages?: string[]; + entry: string; + targetDir?: string; }; export type ServeOptions = BundlingPathsOptions & { @@ -42,6 +45,7 @@ export type BuildOptions = BundlingPathsOptions & { schema?: ConfigSchema; frontendConfig: Config; frontendAppConfigs: AppConfig[]; + fullConfig: Config; }; export type BackendBundlingOptions = {