diff --git a/.changeset/lucky-glasses-slide.md b/.changeset/lucky-glasses-slide.md new file mode 100644 index 0000000000..9b56901919 --- /dev/null +++ b/.changeset/lucky-glasses-slide.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Fixed an issue that could cause conflicts of detected modules in workspaces with multiple apps. diff --git a/packages/cli/src/modules/build/lib/bundler/packageDetection.ts b/packages/cli/src/modules/build/lib/bundler/packageDetection.ts index df7ca9e77a..4c283c4a65 100644 --- a/packages/cli/src/modules/build/lib/bundler/packageDetection.ts +++ b/packages/cli/src/modules/build/lib/bundler/packageDetection.ts @@ -19,7 +19,7 @@ import { Config, ConfigReader } from '@backstage/config'; import chokidar from 'chokidar'; import fs from 'fs-extra'; import PQueue from 'p-queue'; -import { join as joinPath, resolve as resolvePath } from 'path'; +import { dirname, join as joinPath, resolve as resolvePath } from 'path'; import { paths as cliPaths } from '../../../../lib/paths'; const DETECTED_MODULES_MODULE_NAME = '__backstage-autodetected-plugins__'; @@ -107,6 +107,7 @@ async function detectPackages( const writeQueue = new PQueue({ concurrency: 1 }); async function writeDetectedPackagesModule( + targetPath: string, pkgs: { name: string; export?: string; import: string }[], ) { const requirePackageScript = pkgs @@ -118,16 +119,19 @@ async function writeDetectedPackagesModule( ) .join(','); - await writeQueue.add(() => - fs.writeFile( - joinPath( - cliPaths.targetRoot, - 'node_modules', - `${DETECTED_MODULES_MODULE_NAME}.js`, - ), + await writeQueue.add(async () => { + const detectedModulesPath = joinPath( + targetPath, + 'node_modules', + `${DETECTED_MODULES_MODULE_NAME}.js`, + ); + + await fs.ensureDir(dirname(detectedModulesPath)); + await fs.writeFile( + detectedModulesPath, `window['__@backstage/discovered__'] = { modules: [${requirePackageScript}] };`, - ), - ); + ); + }); } export async function createDetectedModulesEntryPoint(options: { @@ -142,11 +146,23 @@ export async function createDetectedModulesEntryPoint(options: { return []; } + // Previous versions of the CLI would write the detected modules file to the + // root `node_modules`, this makes sure that doesn't exist to minimize risk of conflicts + const legacyDetectedModulesPath = joinPath( + cliPaths.targetRoot, + 'node_modules', + `${DETECTED_MODULES_MODULE_NAME}.js`, + ); + if (await fs.pathExists(legacyDetectedModulesPath)) { + await fs.remove(legacyDetectedModulesPath); + } + if (watch) { const watcher = chokidar.watch(resolvePath(targetPath, 'package.json')); watcher.on('change', async () => { await writeDetectedPackagesModule( + targetPath, await detectPackages(targetPath, detectionConfig), ); watch(); @@ -154,6 +170,7 @@ export async function createDetectedModulesEntryPoint(options: { } await writeDetectedPackagesModule( + targetPath, await detectPackages(targetPath, detectionConfig), );