diff --git a/packages/cli/src/commands/export-dynamic-plugin/backend-embed-as-dependencies.ts b/packages/cli/src/commands/export-dynamic-plugin/backend-embed-as-dependencies.ts index 5cbadf2d44..ee96a51144 100644 --- a/packages/cli/src/commands/export-dynamic-plugin/backend-embed-as-dependencies.ts +++ b/packages/cli/src/commands/export-dynamic-plugin/backend-embed-as-dependencies.ts @@ -600,7 +600,7 @@ function checkWorkspacePackageVersion( ); } -function customizeForDynamicUse(options: { +export function customizeForDynamicUse(options: { embedded: ResolvedEmbedded[]; monoRepoPackages: Packages | undefined; sharedPackages?: SharedPackagesRules | undefined; diff --git a/packages/cli/src/commands/export-dynamic-plugin/command.ts b/packages/cli/src/commands/export-dynamic-plugin/command.ts index a49ddc2582..c593c57401 100644 --- a/packages/cli/src/commands/export-dynamic-plugin/command.ts +++ b/packages/cli/src/commands/export-dynamic-plugin/command.ts @@ -20,6 +20,8 @@ import chalk from 'chalk'; import { OptionValues } from 'commander'; import fs from 'fs-extra'; +import path from 'path'; + import { paths } from '../../lib/paths'; import { getConfigSchema } from '../../lib/schema/collect'; import { Task } from '../../lib/tasks'; @@ -28,9 +30,9 @@ import { backend as backendEmbedAsDependencies } from './backend-embed-as-depend import { applyDevOptions } from './dev'; import { frontend } from './frontend'; -const saveSchema = async (packageName: string, path: string) => { +const saveSchema = async (packageName: string, destination: string) => { const configSchema = await getConfigSchema(packageName); - await fs.writeJson(paths.resolveTarget(path), configSchema, { + await fs.writeJson(paths.resolveTarget(destination), configSchema, { encoding: 'utf8', spaces: 2, }); @@ -53,10 +55,10 @@ export async function command(opts: OptionValues): Promise { } else { targetPath = await backendEmbedAsCode(roleInfo, opts); } - configSchemaPath = 'dist-dynamic/dist/configSchema.json'; + configSchemaPath = path.join(targetPath, 'dist/configSchema.json'); } else if (role === 'frontend-plugin') { targetPath = await frontend(roleInfo, opts); - configSchemaPath = 'dist-scalprum/configSchema.json'; + configSchemaPath = path.join(targetPath, 'dist-scalprum/configSchema.json'); } else { throw new Error( 'Only packages with the "backend-plugin", "backend-plugin-module" or "frontend-plugin" roles can be exported as dynamic backend plugins', diff --git a/packages/cli/src/commands/export-dynamic-plugin/frontend.ts b/packages/cli/src/commands/export-dynamic-plugin/frontend.ts index edfa5e245f..9812a98ea3 100644 --- a/packages/cli/src/commands/export-dynamic-plugin/frontend.ts +++ b/packages/cli/src/commands/export-dynamic-plugin/frontend.ts @@ -16,20 +16,28 @@ import { PackageRoleInfo } from '@backstage/cli-node'; +import { getPackages } from '@manypkg/get-packages'; +import chalk from 'chalk'; import { OptionValues } from 'commander'; import fs from 'fs-extra'; +import path from 'path'; + import { buildScalprumPlugin } from '../../lib/builder/buildScalprumPlugin'; +import { productionPack } from '../../lib/packager/productionPack'; import { paths } from '../../lib/paths'; +import { Task } from '../../lib/tasks'; +import { customizeForDynamicUse } from './backend-embed-as-dependencies'; export async function frontend( _: PackageRoleInfo, - __: OptionValues, + opts: OptionValues, ): Promise { const { name, version, scalprum: scalprumExternal, + files, } = await fs.readJson(paths.resolveTarget('package.json')); let scalprum = scalprumExternal; @@ -54,7 +62,63 @@ export async function frontend( ); } - await fs.remove(paths.resolveTarget('dist-scalprum')); + const distDynamicRelativePath = 'dist-dynamic'; + const target = opts.inPlace + ? path.resolve(paths.targetDir) + : path.resolve(paths.targetDir, distDynamicRelativePath); + + if (!opts.inPlace) { + Task.log( + `Packing main package to ${chalk.cyan( + path.join(distDynamicRelativePath, 'package.json'), + )}`, + ); + + if (opts.clean) { + await fs.remove(target); + } + + await fs.mkdirs(target); + + await productionPack({ + packageDir: paths.targetDir, + targetDir: target, + }); + + Task.log( + `Customizing main package in ${chalk.cyan( + path.join(distDynamicRelativePath, 'package.json'), + )} for dynamic loading`, + ); + if (files && Array.isArray(files) && !files.includes('dist-scalprum')) { + files.push('dist-scalprum'); + } + const monoRepoPackages = await getPackages(paths.targetDir); + await customizeForDynamicUse({ + embedded: [], + monoRepoPackages, + overridding: { + name: `${name}-dynamic`, + // We remove scripts, because they do not make sense for this derived package. + // They even bring errors, especially the pre-pack and post-pack ones: + // we want to be able to use npm pack on this derived package to distribute it as a dynamic plugin, + // and obviously this should not trigger the backstage pre-pack or post-pack actions + // which are related to the packaging of the original static package. + scripts: {}, + files, + }, + })(path.resolve(target, 'package.json')); + } + + const resolvedScalprumDistPath = path.join(target, 'dist-scalprum'); + + Task.log( + `Generating dynamic frontend plugin assets in ${chalk.cyan( + resolvedScalprumDistPath, + )}`, + ); + + await fs.remove(resolvedScalprumDistPath); await buildScalprumPlugin({ writeStats: false, @@ -65,7 +129,8 @@ export async function frontend( version, }, fromPackage: name, + resolvedScalprumDistPath, }); - return paths.targetDir; + return target; } diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 91c109c953..fd30fa8ad9 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -123,8 +123,16 @@ export function registerScriptCommand(program: Command) { ) .option( '--embed-as-dependencies', - 'Include embedded packages as private dependencies, instead of merging the with the generated code. Experimental for now, but expected to become the default.', + 'Include embedded packages as private dependencies of backend plugins, instead of merging them with the generated code. Experimental for now, but expected to become the default.', + false, ) + .option('--no-embed-as-dependencies', undefined, true) + .option( + '--in-place', + 'Adds the frontend dynamic plugin assets to the `dist-scalprum` folder of the original plugin package. When value is `false` (using `--no-in-place`), it produces the assets in a distinct package located in the `dist-dynamic` sub-folder, as for backend plugins. `true` by default for now, it is expected to become `false` by default.', + true, + ) + .option('--no-in-place', undefined, false) .action(lazy(() => import('./export-dynamic-plugin').then(m => m.command))); command diff --git a/packages/cli/src/lib/builder/buildScalprumPlugin.ts b/packages/cli/src/lib/builder/buildScalprumPlugin.ts index 1163aac045..b7c8d42e89 100644 --- a/packages/cli/src/lib/builder/buildScalprumPlugin.ts +++ b/packages/cli/src/lib/builder/buildScalprumPlugin.ts @@ -10,10 +10,12 @@ interface BuildScalprumPluginOptions { configPaths: string[]; pluginMetadata: PluginBuildMetadata; fromPackage: string; + resolvedScalprumDistPath: string; } export async function buildScalprumPlugin(options: BuildScalprumPluginOptions) { - const { targetDir, pluginMetadata, fromPackage } = options; + const { targetDir, pluginMetadata, fromPackage, resolvedScalprumDistPath } = + options; await buildScalprumBundle({ targetDir, entry: 'src/index', @@ -23,5 +25,6 @@ export async function buildScalprumPlugin(options: BuildScalprumPluginOptions) { args: [], fromPackage, })), + resolvedScalprumDistPath, }); } diff --git a/packages/cli/src/lib/bundler/bundlePlugin.ts b/packages/cli/src/lib/bundler/bundlePlugin.ts index bf1ad0d7e4..cf20f446d4 100644 --- a/packages/cli/src/lib/bundler/bundlePlugin.ts +++ b/packages/cli/src/lib/bundler/bundlePlugin.ts @@ -23,23 +23,35 @@ function applyContextToError(error: string, moduleName: string): string { } export async function buildScalprumBundle( - options: BundlingPathsOptions & DynamicPluginOptions, + options: BundlingPathsOptions & + DynamicPluginOptions & { + resolvedScalprumDistPath: string; + }, ) { const paths = resolveBundlingPaths(options); - const config = await createScalprumConfig(paths, { - ...options, - checksEnabled: false, - isDev: false, - baseUrl: resolveBaseUrl(options.frontendConfig), - }); + const config = await createScalprumConfig( + { + targetScalprumDist: options.resolvedScalprumDistPath, + ...paths, + }, + { + ...options, + checksEnabled: false, + isDev: false, + baseUrl: resolveBaseUrl(options.frontendConfig), + }, + ); const isCi = yn(process.env.CI, { default: false }); const previousFileSizes = await measureFileSizesBeforeBuild( - paths.targetScalprumDist, + options.resolvedScalprumDistPath, ); - await fs.emptyDir(paths.targetScalprumDist); + await fs.emptyDir(options.resolvedScalprumDistPath); + // TODO(davidfestal): ask @tumido or @Hyperkid123if this still makes sense here for dynamic plugins. + // That seems strange since the public assets are not copied to `dist-scalprum`, + // which is the place from where the dynamic plugin assets will be served if (paths.targetPublic) { await fs.copy(paths.targetPublic, paths.targetDist, { dereference: true, @@ -56,7 +68,7 @@ export async function buildScalprumBundle( printFileSizesAfterBuild( stats, previousFileSizes, - paths.targetScalprumDist, + options.resolvedScalprumDistPath, WARN_AFTER_BUNDLE_GZIP_SIZE, WARN_AFTER_CHUNK_GZIP_SIZE, ); diff --git a/packages/cli/src/lib/bundler/paths.ts b/packages/cli/src/lib/bundler/paths.ts index 7f59351c58..abacb46c19 100644 --- a/packages/cli/src/lib/bundler/paths.ts +++ b/packages/cli/src/lib/bundler/paths.ts @@ -63,7 +63,6 @@ export function resolveBundlingPaths(options: BundlingPathsOptions) { targetPath: resolvePath(targetDir, '.'), targetRunFile: runFileExists ? targetRunFile : undefined, targetDist: resolvePath(targetDir, 'dist'), - targetScalprumDist: resolvePath(targetDir, 'dist-scalprum'), targetAssets: resolvePath(targetDir, 'assets'), targetSrc: resolvePath(targetDir, 'src'), targetDev: resolvePath(targetDir, 'dev'), diff --git a/packages/cli/src/lib/bundler/scalprumConfig.ts b/packages/cli/src/lib/bundler/scalprumConfig.ts index 0c8328bc54..9983104d37 100644 --- a/packages/cli/src/lib/bundler/scalprumConfig.ts +++ b/packages/cli/src/lib/bundler/scalprumConfig.ts @@ -71,7 +71,7 @@ export const sharedModules = { }; export async function createScalprumConfig( - paths: BundlingPaths, + paths: BundlingPaths & { targetScalprumDist: string }, options: DynamicPluginOptions, ): Promise { const { checksEnabled, isDev } = options;