From fd7bcf9ab25391f02ffe8520a586033ebd064aa9 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 13 Jul 2023 17:15:27 +0200 Subject: [PATCH 01/41] Explore first options to load plugin modules from package name Co-authored-by: Patrik Oldsberg Co-authored-by: Jonathan Roebuck Co-authored-by: Jack Palmer Co-authored-by: Vincenzo Scamporlino Signed-off-by: Philipp Hugenroth --- packages/cli/src/lib/bundler/config.ts | 20 ++++++- packages/cli/src/lib/bundler/server.ts | 4 ++ packages/cli/src/lib/bundler/types.ts | 1 + .../core-plugin-api/src/plugin/Plugin.tsx | 4 +- plugins/graphiql/dev/index.tsx | 59 +++++++++++++++++++ plugins/org/src/plugin.ts | 2 + 6 files changed, 87 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 2f09a66e18..7f629b9e11 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -84,7 +84,7 @@ export async function createConfig( paths: BundlingPaths, options: BundlingOptions, ): Promise { - const { checksEnabled, isDev, frontendConfig } = options; + const { checksEnabled, isDev, frontendConfig, extraPackages } = options; const { plugins, loaders } = transforms(options); // Any package that is part of the monorepo but outside the monorepo root dir need @@ -127,6 +127,20 @@ export async function createConfig( }), ); + plugins.push( + new ProvidePlugin( + Object.fromEntries( + extraPackages?.map(name => [ + `__backstageLoadedPackage_${name}`, + name, + ]) ?? [], + ), + ), + ); + + // In browser + // const packageKeys = Object.keys(window).filter(key => key.startsWith('__backstageLoadedPackage_')) + const buildInfo = await readBuildInfo(); plugins.push( new webpack.DefinePlugin({ @@ -135,6 +149,8 @@ export async function createConfig( () => JSON.stringify(options.getFrontendAppConfigs()), true, ), + 'process.env.EXTRA_PACKAGE_IMPORTS': JSON.stringify(extraPackages), + 'process.env.SINGLE_PACKAGE_IMPORT': JSON.stringify(extraPackages[0]), }), ); @@ -161,7 +177,7 @@ export async function createConfig( }, devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map', context: paths.targetPath, - entry: [paths.targetEntry], + entry: [paths.targetEntry, ...(extraPackages ?? [])], resolve: { extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx', '.json', '.wasm'], mainFields: ['browser', 'module', 'main'], diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index 74c6b2a53c..878d9d971d 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -106,6 +106,8 @@ export async function serveBundle(options: ServeOptions) { } const { frontendConfig, fullConfig } = cliConfig; + // TODO: proper + const extraPackages = ['@backstage/plugin-org']; const url = resolveBaseUrl(frontendConfig); const host = @@ -119,7 +121,9 @@ export async function serveBundle(options: ServeOptions) { const pkgPath = paths.targetPackageJson; const pkg = await fs.readJson(pkgPath); const config = await createConfig(paths, { + ...options, checksEnabled: options.checksEnabled, + extraPackages, isDev: true, baseUrl: url, frontendConfig, diff --git a/packages/cli/src/lib/bundler/types.ts b/packages/cli/src/lib/bundler/types.ts index 69e75b113d..8996ed995d 100644 --- a/packages/cli/src/lib/bundler/types.ts +++ b/packages/cli/src/lib/bundler/types.ts @@ -25,6 +25,7 @@ export type BundlingOptions = { getFrontendAppConfigs(): AppConfig[]; baseUrl: URL; parallelism?: number; + extraPackages?: string[]; }; export type ServeOptions = BundlingPathsOptions & { diff --git a/packages/core-plugin-api/src/plugin/Plugin.tsx b/packages/core-plugin-api/src/plugin/Plugin.tsx index da26cd4db0..dd82169e40 100644 --- a/packages/core-plugin-api/src/plugin/Plugin.tsx +++ b/packages/core-plugin-api/src/plugin/Plugin.tsx @@ -98,5 +98,7 @@ export function createPlugin< >( config: PluginConfig, ): BackstagePlugin { - return new PluginImpl(config); + const plugin = new PluginImpl(config); + window.allPlugins.push(plugin); + return plugin; } diff --git a/plugins/graphiql/dev/index.tsx b/plugins/graphiql/dev/index.tsx index 163b4d0512..2aabfc51ba 100644 --- a/plugins/graphiql/dev/index.tsx +++ b/plugins/graphiql/dev/index.tsx @@ -28,6 +28,65 @@ import { graphQlBrowseApiRef, GraphiQLPage, } from '../src'; +// import * as orgPlugin from '@backstage/plugin-org'; + +// console.log(`DEBUG: orgPlugin=`, orgPlugin); + +// // (process.env.EXTRA_PACKAGE_IMPORTS as string[]).map(name => require(name)) +// console.log( +// `DEBUG: process.env.EXTRA_PACKAGE_IMPORTS=`, +// process.env.EXTRA_PACKAGE_IMPORTS, +// ); + +// require.ensure(process.env.EXTRA_PACKAGE_IMPORTS as string[], innerRequire => { +// console.log(`DEBUG: innerRequire=`, innerRequire); +// const path3 = require.resolve('@backstage/plugin-org'); +// console.log(`DEBUG: path3=`, path3); +// const loadedModules = (process.env.EXTRA_PACKAGE_IMPORTS as string[]).map( +// name => console.log('require', name) || require(name), +// ); +// console.log(`DEBUG: loadedModules=`, loadedModules); +// }); + +console.log(process.env); +console.log( + `DEBUG: process.env.EXTRA_PACKAGE_IMPORTS=`, + process.env.EXTRA_PACKAGE_IMPORTS, +); +// const orgPlugin = require([ +// '@backstage/plugin-org', +// '@backstage/plugin-todo', +// ], modules => { +// console.log(`DEBUG: modules=`, modules); +// }); +// require.ensure( +// process.env.EXTRA_PACKAGE_IMPORTS, +// innerRequire => { +// console.log( +// `DEBUG: module1=`, +// require(process.env.EXTRA_PACKAGE_IMPORTS[0]), +// ); +// // console.log( +// // `DEBUG: module2=`, +// // innerRequire(require.resolve('@backstage/plugin-todo')), +// // ); +// }, +// err => console.error('ERROR CALLBACK: ', err), +// ); + +// console.log(`DEBUG: orgPlugin=`, orgPlugin); + +// const orgPlugin2 = require(process.env.EXTRA_PACKAGE_IMPORTS, modules => { +// console.log(`DEBUG: modules=`, modules); +// }); +// console.log(`DEBUG: orgPlugin2=`, orgPlugin2); +// __webpack_require__( +// process.env.EXTRA_PACKAGE_IMPORTS as string[], +// loadedModules => { +// console.log(`DEBUG: loadedModules=`, loadedModules); +// }, +// ); +// require.context() createDevApp() .registerPlugin(graphiqlPlugin) diff --git a/plugins/org/src/plugin.ts b/plugins/org/src/plugin.ts index ea6856b6b4..a6715801f5 100644 --- a/plugins/org/src/plugin.ts +++ b/plugins/org/src/plugin.ts @@ -27,6 +27,8 @@ export const orgPlugin = createPlugin({ }, }); +window.iAmTheOrgPlugin = orgPlugin; + /** @public */ export const EntityGroupProfileCard = orgPlugin.provide( createComponentExtension({ From 08e36cdab39c85bb4eac3ae45c690907edb13491 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 14 Jul 2023 12:11:18 +0200 Subject: [PATCH 02/41] cli: auto detect frontend plugins Co-authored-by: Patrik Oldsberg Co-authored-by: Jonathan Roebuck Co-authored-by: Jack Palmer Co-authored-by: Philipp Hugenroth Signed-off-by: Vincenzo Scamporlino --- packages/cli/package.json | 1 + packages/cli/src/lib/bundler/config.ts | 31 +++++----- packages/cli/src/lib/bundler/server.ts | 30 ++++++++-- .../core-plugin-api/src/plugin/Plugin.tsx | 4 +- plugins/graphiql/dev/index.tsx | 59 +------------------ plugins/org/src/plugin.ts | 2 - yarn.lock | 8 +++ 7 files changed, 54 insertions(+), 81 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 5999a55fc1..7889f3ab17 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -133,6 +133,7 @@ "webpack": "^5.70.0", "webpack-dev-server": "^4.7.3", "webpack-node-externals": "^3.0.0", + "webpack-virtual-modules": "^0.5.0", "yaml": "^2.0.0", "yml-loader": "^2.1.0", "yn": "^4.0.0", diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 7f629b9e11..6ebfeae99e 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -22,6 +22,7 @@ import ModuleScopePlugin from 'react-dev-utils/ModuleScopePlugin'; import { RunScriptWebpackPlugin } from 'run-script-webpack-plugin'; import webpack, { ProvidePlugin } from 'webpack'; import nodeExternals from 'webpack-node-externals'; +import VirtualModulesPlugin from 'webpack-virtual-modules'; import { isChildPath } from '@backstage/cli-common'; import { getPackages } from '@manypkg/get-packages'; import { optimization } from './optimization'; @@ -39,6 +40,7 @@ import pickBy from 'lodash/pickBy'; import yn from 'yn'; import { readEntryPoints } from '../entryPoints'; +const PACKAGES_GLOBAL = '__backstage_detected_packages__'; const BUILD_CACHE_ENV_VAR = 'BACKSTAGE_CLI_EXPERIMENTAL_BUILD_CACHE'; export function resolveBaseUrl(config: Config): URL { @@ -84,7 +86,7 @@ export async function createConfig( paths: BundlingPaths, options: BundlingOptions, ): Promise { - const { checksEnabled, isDev, frontendConfig, extraPackages } = options; + const { checksEnabled, isDev, frontendConfig, extraPackages = [] } = options; const { plugins, loaders } = transforms(options); // Any package that is part of the monorepo but outside the monorepo root dir need @@ -127,19 +129,17 @@ export async function createConfig( }), ); - plugins.push( - new ProvidePlugin( - Object.fromEntries( - extraPackages?.map(name => [ - `__backstageLoadedPackage_${name}`, - name, - ]) ?? [], - ), - ), - ); + if (extraPackages.length > 0) { + const requirePackageScript = extraPackages + ?.map(pkg => `require('${pkg}')`) + .join(','); - // In browser - // const packageKeys = Object.keys(window).filter(key => key.startsWith('__backstageLoadedPackage_')) + plugins.push( + new VirtualModulesPlugin({ + [`node_modules/${PACKAGES_GLOBAL}.js`]: `window['${PACKAGES_GLOBAL}'] = { modules: [${requirePackageScript}] }`, + }), + ); + } const buildInfo = await readBuildInfo(); plugins.push( @@ -177,7 +177,10 @@ export async function createConfig( }, devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map', context: paths.targetPath, - entry: [paths.targetEntry, ...(extraPackages ?? [])], + entry: [ + ...(extraPackages.length > 0 ? [`${PACKAGES_GLOBAL}.js`] : []), + paths.targetEntry, + ], resolve: { extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx', '.json', '.wasm'], mainFields: ['browser', 'module', 'main'], diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index 878d9d971d..d51ab7dc30 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -33,8 +33,34 @@ import { forbiddenDuplicatesFilter, includedFilter, } from '../../commands/versions/lint'; +import { BackstagePackageJson } from '@backstage/cli-node'; export async function serveBundle(options: ServeOptions) { + const paths = resolveBundlingPaths(options); + const pkgPath = paths.targetPackageJson; + const pkg: BackstagePackageJson = await fs.readJson(pkgPath); + + // TODO: proper + // Assumption for config string based on https://github.com/backstage/backstage/issues/18372 ^ + const packageDetectionMode = options.fullConfig.getOptionalString( + 'app.experimental.packages', + ); + const extraPackages = []; + if (packageDetectionMode === 'all') { + for (const depName of Object.keys(pkg.dependencies ?? {})) { + const depPackageJson: BackstagePackageJson = require(require.resolve( + `${depName}/package.json`, + { paths: [paths.targetPath] }, + )); + if ( + ['frontend-plugin', 'frontend-plugin-module'].includes( + depPackageJson.backstage?.role || '', + ) + ) { + extraPackages.push(depName); + } + } + } if (options.verifyVersions) { const lockfile = await Lockfile.load( libPaths.resolveTargetRoot('yarn.lock'), @@ -107,7 +133,6 @@ export async function serveBundle(options: ServeOptions) { const { frontendConfig, fullConfig } = cliConfig; // TODO: proper - const extraPackages = ['@backstage/plugin-org']; const url = resolveBaseUrl(frontendConfig); const host = @@ -117,9 +142,6 @@ export async function serveBundle(options: ServeOptions) { Number(url.port) || (url.protocol === 'https:' ? 443 : 80); - const paths = resolveBundlingPaths(options); - const pkgPath = paths.targetPackageJson; - const pkg = await fs.readJson(pkgPath); const config = await createConfig(paths, { ...options, checksEnabled: options.checksEnabled, diff --git a/packages/core-plugin-api/src/plugin/Plugin.tsx b/packages/core-plugin-api/src/plugin/Plugin.tsx index dd82169e40..da26cd4db0 100644 --- a/packages/core-plugin-api/src/plugin/Plugin.tsx +++ b/packages/core-plugin-api/src/plugin/Plugin.tsx @@ -98,7 +98,5 @@ export function createPlugin< >( config: PluginConfig, ): BackstagePlugin { - const plugin = new PluginImpl(config); - window.allPlugins.push(plugin); - return plugin; + return new PluginImpl(config); } diff --git a/plugins/graphiql/dev/index.tsx b/plugins/graphiql/dev/index.tsx index 2aabfc51ba..209a9a5c86 100644 --- a/plugins/graphiql/dev/index.tsx +++ b/plugins/graphiql/dev/index.tsx @@ -28,65 +28,8 @@ import { graphQlBrowseApiRef, GraphiQLPage, } from '../src'; -// import * as orgPlugin from '@backstage/plugin-org'; -// console.log(`DEBUG: orgPlugin=`, orgPlugin); - -// // (process.env.EXTRA_PACKAGE_IMPORTS as string[]).map(name => require(name)) -// console.log( -// `DEBUG: process.env.EXTRA_PACKAGE_IMPORTS=`, -// process.env.EXTRA_PACKAGE_IMPORTS, -// ); - -// require.ensure(process.env.EXTRA_PACKAGE_IMPORTS as string[], innerRequire => { -// console.log(`DEBUG: innerRequire=`, innerRequire); -// const path3 = require.resolve('@backstage/plugin-org'); -// console.log(`DEBUG: path3=`, path3); -// const loadedModules = (process.env.EXTRA_PACKAGE_IMPORTS as string[]).map( -// name => console.log('require', name) || require(name), -// ); -// console.log(`DEBUG: loadedModules=`, loadedModules); -// }); - -console.log(process.env); -console.log( - `DEBUG: process.env.EXTRA_PACKAGE_IMPORTS=`, - process.env.EXTRA_PACKAGE_IMPORTS, -); -// const orgPlugin = require([ -// '@backstage/plugin-org', -// '@backstage/plugin-todo', -// ], modules => { -// console.log(`DEBUG: modules=`, modules); -// }); -// require.ensure( -// process.env.EXTRA_PACKAGE_IMPORTS, -// innerRequire => { -// console.log( -// `DEBUG: module1=`, -// require(process.env.EXTRA_PACKAGE_IMPORTS[0]), -// ); -// // console.log( -// // `DEBUG: module2=`, -// // innerRequire(require.resolve('@backstage/plugin-todo')), -// // ); -// }, -// err => console.error('ERROR CALLBACK: ', err), -// ); - -// console.log(`DEBUG: orgPlugin=`, orgPlugin); - -// const orgPlugin2 = require(process.env.EXTRA_PACKAGE_IMPORTS, modules => { -// console.log(`DEBUG: modules=`, modules); -// }); -// console.log(`DEBUG: orgPlugin2=`, orgPlugin2); -// __webpack_require__( -// process.env.EXTRA_PACKAGE_IMPORTS as string[], -// loadedModules => { -// console.log(`DEBUG: loadedModules=`, loadedModules); -// }, -// ); -// require.context() +console.log('modules', window.__backstage_detected_packages__.modules); createDevApp() .registerPlugin(graphiqlPlugin) diff --git a/plugins/org/src/plugin.ts b/plugins/org/src/plugin.ts index a6715801f5..ea6856b6b4 100644 --- a/plugins/org/src/plugin.ts +++ b/plugins/org/src/plugin.ts @@ -27,8 +27,6 @@ export const orgPlugin = createPlugin({ }, }); -window.iAmTheOrgPlugin = orgPlugin; - /** @public */ export const EntityGroupProfileCard = orgPlugin.provide( createComponentExtension({ diff --git a/yarn.lock b/yarn.lock index 73bf45dd3e..3f730e9f31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3882,6 +3882,7 @@ __metadata: webpack: ^5.70.0 webpack-dev-server: ^4.7.3 webpack-node-externals: ^3.0.0 + webpack-virtual-modules: ^0.5.0 yaml: ^2.0.0 yml-loader: ^2.1.0 yn: ^4.0.0 @@ -42801,6 +42802,13 @@ __metadata: languageName: node linkType: hard +"webpack-virtual-modules@npm:^0.5.0": + version: 0.5.0 + resolution: "webpack-virtual-modules@npm:0.5.0" + checksum: 22b59257b55c89d11ae295b588b683ee9fdf3aeb591bc7b6f88ac1d69cb63f4fcb507666ea986866dfae161a1fa534ad6fb4e2ea91bbcd0a6d454368d7d4c64b + languageName: node + linkType: hard + "webpack@npm:^5, webpack@npm:^5.70.0": version: 5.88.2 resolution: "webpack@npm:5.88.2" From b9f81c945c8ec169f737e6ff7be4d8a9bcadd394 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 17 Jul 2023 15:49:45 +0200 Subject: [PATCH 03/41] Allow explicit package list & add UI Co-authored-by: Vincenzo Scamporlino Signed-off-by: Philipp Hugenroth --- packages/cli/src/lib/bundler/config.ts | 2 +- packages/cli/src/lib/bundler/server.ts | 19 +++++++++- .../Content/InfoContent/InfoContent.tsx | 35 +++++++++++++++++-- .../InfoContent/InfoDependenciesTable.tsx | 4 ++- 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 6ebfeae99e..df3f440fa5 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -131,7 +131,7 @@ export async function createConfig( if (extraPackages.length > 0) { const requirePackageScript = extraPackages - ?.map(pkg => `require('${pkg}')`) + ?.map(pkg => `{name: '${pkg}', module: require('${pkg}')}`) .join(','); plugins.push( diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index d51ab7dc30..db046e1e18 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -42,7 +42,7 @@ export async function serveBundle(options: ServeOptions) { // TODO: proper // Assumption for config string based on https://github.com/backstage/backstage/issues/18372 ^ - const packageDetectionMode = options.fullConfig.getOptionalString( + const packageDetectionMode = options.fullConfig.getOptional( 'app.experimental.packages', ); const extraPackages = []; @@ -60,6 +60,23 @@ export async function serveBundle(options: ServeOptions) { extraPackages.push(depName); } } + } else { + const packagesList = Array.isArray(packageDetectionMode) + ? packageDetectionMode + : []; + for (const depName of packagesList ?? []) { + const depPackageJson: BackstagePackageJson = require(require.resolve( + `${depName}/package.json`, + { paths: [paths.targetPath] }, + )); + if ( + ['frontend-plugin', 'frontend-plugin-module'].includes( + depPackageJson.backstage?.role || '', + ) + ) { + extraPackages.push(depName); + } + } } if (options.verifyVersions) { const lockfile = await Lockfile.load( diff --git a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx index 0ed43d2fee..d9301ea6ec 100644 --- a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx +++ b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx @@ -37,7 +37,12 @@ import MemoryIcon from '@material-ui/icons/Memory'; import DeveloperBoardIcon from '@material-ui/icons/DeveloperBoard'; import { BackstageLogoIcon } from './BackstageLogoIcon'; import FileCopyIcon from '@material-ui/icons/FileCopy'; -import { DevToolsInfo } from '@backstage/plugin-devtools-common'; +import { + DevToolsInfo, + PackageDependency, +} from '@backstage/plugin-devtools-common'; + +const PACKAGES_GLOBAL = '__backstage_detected_packages__'; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -74,6 +79,21 @@ export const InfoContent = () => { const classes = useStyles(); const { about, loading, error } = useInfo(); + const availablePlugins: PackageDependency[] = (window as any)[PACKAGES_GLOBAL] + ? ( + (window as any)[PACKAGES_GLOBAL].modules as Array> + ).map(({ name, module }) => { + const pluginImpl: any = Object.values(module).find( + (v: any) => !!v?.getId, + ); + + return { + name, + versions: (pluginImpl?.getId() as string) || '', + }; + }) + : []; + if (loading) { return ; } else if (error) { @@ -144,7 +164,18 @@ export const InfoContent = () => { - + + + + + + ); }; diff --git a/plugins/devtools/src/components/Content/InfoContent/InfoDependenciesTable.tsx b/plugins/devtools/src/components/Content/InfoContent/InfoDependenciesTable.tsx index e1ab59f279..ac54e2ebaa 100644 --- a/plugins/devtools/src/components/Content/InfoContent/InfoDependenciesTable.tsx +++ b/plugins/devtools/src/components/Content/InfoContent/InfoDependenciesTable.tsx @@ -35,12 +35,14 @@ const columns: TableColumn[] = [ export const InfoDependenciesTable = ({ infoDependencies, + title, }: { + title: string; infoDependencies: PackageDependency[] | undefined; }) => { return ( Date: Tue, 18 Jul 2023 10:38:04 +0200 Subject: [PATCH 04/41] cli: avoid global object Signed-off-by: Vincenzo Scamporlino --- packages/cli/src/lib/bundler/config.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index df3f440fa5..e732377a4d 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -40,7 +40,6 @@ import pickBy from 'lodash/pickBy'; import yn from 'yn'; import { readEntryPoints } from '../entryPoints'; -const PACKAGES_GLOBAL = '__backstage_detected_packages__'; const BUILD_CACHE_ENV_VAR = 'BACKSTAGE_CLI_EXPERIMENTAL_BUILD_CACHE'; export function resolveBaseUrl(config: Config): URL { @@ -136,7 +135,7 @@ export async function createConfig( plugins.push( new VirtualModulesPlugin({ - [`node_modules/${PACKAGES_GLOBAL}.js`]: `window['${PACKAGES_GLOBAL}'] = { modules: [${requirePackageScript}] }`, + 'node_modules/backstage-autodetected-plugins.js': `module.exports = { modules: [${requirePackageScript}] };`, }), ); } @@ -178,7 +177,9 @@ export async function createConfig( devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map', context: paths.targetPath, entry: [ - ...(extraPackages.length > 0 ? [`${PACKAGES_GLOBAL}.js`] : []), + ...(extraPackages.length > 0 + ? [`backstage-autodetected-plugins.js`] + : []), paths.targetEntry, ], resolve: { From 2729e06d28747a8fe5036470353bfc384f4cd203 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Tue, 18 Jul 2023 10:38:37 +0200 Subject: [PATCH 05/41] create autodetect plugin Signed-off-by: Vincenzo Scamporlino --- packages/autodetect/.eslintrc.js | 1 + packages/autodetect/README.md | 12 +++++++++++ packages/autodetect/package.json | 31 +++++++++++++++++++++++++++ packages/autodetect/src/index.ts | 22 +++++++++++++++++++ packages/autodetect/src/setupTests.ts | 16 ++++++++++++++ 5 files changed, 82 insertions(+) create mode 100644 packages/autodetect/.eslintrc.js create mode 100644 packages/autodetect/README.md create mode 100644 packages/autodetect/package.json create mode 100644 packages/autodetect/src/index.ts create mode 100644 packages/autodetect/src/setupTests.ts diff --git a/packages/autodetect/.eslintrc.js b/packages/autodetect/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/packages/autodetect/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/packages/autodetect/README.md b/packages/autodetect/README.md new file mode 100644 index 0000000000..b5d48ff3df --- /dev/null +++ b/packages/autodetect/README.md @@ -0,0 +1,12 @@ +# @backstage/autodetect + +_This package was created through the Backstage CLI_. + +## Installation + +Install the package via Yarn: + +```sh +cd # if within a monorepo +yarn add @backstage/autodetect +``` diff --git a/packages/autodetect/package.json b/packages/autodetect/package.json new file mode 100644 index 0000000000..dc3684a6bf --- /dev/null +++ b/packages/autodetect/package.json @@ -0,0 +1,31 @@ +{ + "name": "@backstage/autodetect", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "web-library" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@testing-library/jest-dom": "^5.10.1" + }, + "files": [ + "dist" + ] +} diff --git a/packages/autodetect/src/index.ts b/packages/autodetect/src/index.ts new file mode 100644 index 0000000000..4cc4c191f3 --- /dev/null +++ b/packages/autodetect/src/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 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. + */ + +function getAvailablePlugins() { + return __webpack_require__( + './node_modules/backstage-autodetected-plugins.js', + ); +} +export { getAvailablePlugins }; diff --git a/packages/autodetect/src/setupTests.ts b/packages/autodetect/src/setupTests.ts new file mode 100644 index 0000000000..865308e634 --- /dev/null +++ b/packages/autodetect/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 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 '@testing-library/jest-dom'; From e940ddcdf8ffeb1a30acb0f8d38908b81aa7bda4 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Tue, 18 Jul 2023 10:39:05 +0200 Subject: [PATCH 06/41] graphiql: remove modules reference Signed-off-by: Vincenzo Scamporlino --- plugins/graphiql/dev/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/graphiql/dev/index.tsx b/plugins/graphiql/dev/index.tsx index 209a9a5c86..163b4d0512 100644 --- a/plugins/graphiql/dev/index.tsx +++ b/plugins/graphiql/dev/index.tsx @@ -29,8 +29,6 @@ import { GraphiQLPage, } from '../src'; -console.log('modules', window.__backstage_detected_packages__.modules); - createDevApp() .registerPlugin(graphiqlPlugin) .registerApi({ From f690a28e5a25a3aaf278eed1004e6940cc21fe4e Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Tue, 18 Jul 2023 10:40:25 +0200 Subject: [PATCH 07/41] devtools: pull available modules Signed-off-by: Vincenzo Scamporlino --- plugins/devtools/package.json | 1 + .../src/components/Content/InfoContent/InfoContent.tsx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 7c7ec44575..de95310ea9 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -28,6 +28,7 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { + "@backstage/autodetect": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", diff --git a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx index d9301ea6ec..2379df8b60 100644 --- a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx +++ b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx @@ -41,6 +41,7 @@ import { DevToolsInfo, PackageDependency, } from '@backstage/plugin-devtools-common'; +import * as autodetect from '@backstage/autodetect'; const PACKAGES_GLOBAL = '__backstage_detected_packages__'; @@ -78,7 +79,7 @@ const copyToClipboard = ({ about }: { about: DevToolsInfo | undefined }) => { export const InfoContent = () => { const classes = useStyles(); const { about, loading, error } = useInfo(); - + console.log('ooo', autodetect.getAvailablePlugins()); const availablePlugins: PackageDependency[] = (window as any)[PACKAGES_GLOBAL] ? ( (window as any)[PACKAGES_GLOBAL].modules as Array> From 2cf06dffe74765dd71123805c03d2f859ebe89ee Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Tue, 18 Jul 2023 10:48:29 +0200 Subject: [PATCH 08/41] yarn lock Signed-off-by: Vincenzo Scamporlino --- yarn.lock | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/yarn.lock b/yarn.lock index 3f730e9f31..a6459092b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3377,6 +3377,15 @@ __metadata: languageName: unknown linkType: soft +"@backstage/autodetect@workspace:^, @backstage/autodetect@workspace:packages/autodetect": + version: 0.0.0-use.local + resolution: "@backstage/autodetect@workspace:packages/autodetect" + dependencies: + "@backstage/cli": "workspace:^" + "@testing-library/jest-dom": ^5.10.1 + languageName: unknown + linkType: soft + "@backstage/backend-app-api@workspace:^, @backstage/backend-app-api@workspace:packages/backend-app-api": version: 0.0.0-use.local resolution: "@backstage/backend-app-api@workspace:packages/backend-app-api" @@ -6428,6 +6437,7 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-devtools@workspace:plugins/devtools" dependencies: + "@backstage/autodetect": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/core-app-api": "workspace:^" "@backstage/core-components": "workspace:^" From 879b1aa6f5871502553d43e3673030191ec396e0 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 18 Jul 2023 11:38:33 +0200 Subject: [PATCH 09/41] use @backstage/autodetect for DevTools Plugin Co-authored-by: Vincenzo Scamporlino Signed-off-by: Philipp Hugenroth --- .../Content/InfoContent/InfoContent.tsx | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx index 2379df8b60..b40d2ce1df 100644 --- a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx +++ b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx @@ -43,8 +43,6 @@ import { } from '@backstage/plugin-devtools-common'; import * as autodetect from '@backstage/autodetect'; -const PACKAGES_GLOBAL = '__backstage_detected_packages__'; - const useStyles = makeStyles((theme: Theme) => createStyles({ paperStyle: { @@ -79,22 +77,17 @@ const copyToClipboard = ({ about }: { about: DevToolsInfo | undefined }) => { export const InfoContent = () => { const classes = useStyles(); const { about, loading, error } = useInfo(); - console.log('ooo', autodetect.getAvailablePlugins()); - const availablePlugins: PackageDependency[] = (window as any)[PACKAGES_GLOBAL] - ? ( - (window as any)[PACKAGES_GLOBAL].modules as Array> - ).map(({ name, module }) => { - const pluginImpl: any = Object.values(module).find( - (v: any) => !!v?.getId, - ); - - return { - name, - versions: (pluginImpl?.getId() as string) || '', - }; - }) - : []; + const plugins = autodetect.getAvailablePlugins(); + const availablePlugins: PackageDependency[] = ( + plugins.modules as Array> + ).map(({ name, module }) => { + const pluginImpl: any = Object.values(module).find((v: any) => !!v?.getId); + return { + name, + versions: (pluginImpl?.getId() as string) || '', + }; + }); if (loading) { return ; } else if (error) { From 12c3fadb6debeffeda55f1fa8de052811b1edacb Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 18 Jul 2023 14:53:57 +0200 Subject: [PATCH 10/41] Improve typing Signed-off-by: Philipp Hugenroth --- packages/autodetect/src/index.ts | 9 ++++++++- .../src/components/Content/InfoContent/InfoContent.tsx | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/autodetect/src/index.ts b/packages/autodetect/src/index.ts index 4cc4c191f3..11b77fb780 100644 --- a/packages/autodetect/src/index.ts +++ b/packages/autodetect/src/index.ts @@ -14,9 +14,16 @@ * limitations under the License. */ +type Modules = Array<{ + name: string; + module: object; // Loaded Webpack Module of Backstage Plugin +}>; + function getAvailablePlugins() { - return __webpack_require__( + const { modules }: { modules: Modules } = __webpack_require__( './node_modules/backstage-autodetected-plugins.js', ); + return modules; } + export { getAvailablePlugins }; diff --git a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx index b40d2ce1df..c55b57b4b8 100644 --- a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx +++ b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx @@ -79,7 +79,7 @@ export const InfoContent = () => { const { about, loading, error } = useInfo(); const plugins = autodetect.getAvailablePlugins(); const availablePlugins: PackageDependency[] = ( - plugins.modules as Array> + plugins as Array> ).map(({ name, module }) => { const pluginImpl: any = Object.values(module).find((v: any) => !!v?.getId); From 1d527ae2b68befd8413ab43c8d46d1d9e7106d27 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 18 Jul 2023 16:35:17 +0200 Subject: [PATCH 11/41] Add components to available plugins Co-authored-by: Vincenzo Scamporlino Signed-off-by: Philipp Hugenroth --- packages/autodetect/package.json | 5 +- packages/autodetect/src/index.ts | 59 ++++++++++++++++--- .../Content/InfoContent/InfoContent.tsx | 17 +++--- yarn.lock | 1 + 4 files changed, 65 insertions(+), 17 deletions(-) diff --git a/packages/autodetect/package.json b/packages/autodetect/package.json index dc3684a6bf..4cd00c6f8a 100644 --- a/packages/autodetect/package.json +++ b/packages/autodetect/package.json @@ -27,5 +27,8 @@ }, "files": [ "dist" - ] + ], + "dependencies": { + "@backstage/core-plugin-api": "workspace:^" + } } diff --git a/packages/autodetect/src/index.ts b/packages/autodetect/src/index.ts index 11b77fb780..b347a351da 100644 --- a/packages/autodetect/src/index.ts +++ b/packages/autodetect/src/index.ts @@ -14,16 +14,61 @@ * limitations under the License. */ -type Modules = Array<{ - name: string; - module: object; // Loaded Webpack Module of Backstage Plugin -}>; +import { BackstagePlugin } from '@backstage/core-plugin-api'; -function getAvailablePlugins() { - const { modules }: { modules: Modules } = __webpack_require__( +/** + * @public + */ +type DetectedPlugin = { + name: string; + plugin: BackstagePlugin; + components: Record; +}; + +type DetectedModule = { + name: string; + module: Record; +}; + +/** + * @public + */ +function getAvailablePlugins(): DetectedPlugin[] { + const { modules } = __webpack_require__( './node_modules/backstage-autodetected-plugins.js', + ) as { modules: DetectedModule[] }; + + return modules + .map(splitPluginFromComponents) + .filter((m): m is DetectedPlugin => !!m.plugin); +} + +function splitPluginFromComponents({ module, name }: DetectedModule) { + return Object.entries(module).reduce( + (acc, [k, v]) => { + if (!isBackstagePlugin(v)) { + acc.components[k] = v; + } else { + acc.plugin = v; + } + return acc; + }, + { name, components: {} } as { + name: string; + plugin?: BackstagePlugin; + components: Record; + }, + ); +} + +function isBackstagePlugin(obj: Record): obj is BackstagePlugin { + return ( + typeof obj.getId !== 'undefined' && + typeof obj.getApis !== 'undefined' && + typeof obj.getFeatureFlags !== 'undefined' && + typeof obj.provide !== 'undefined' ); - return modules; } export { getAvailablePlugins }; +export type { DetectedPlugin }; diff --git a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx index c55b57b4b8..6ed0140b96 100644 --- a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx +++ b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx @@ -78,16 +78,15 @@ export const InfoContent = () => { const classes = useStyles(); const { about, loading, error } = useInfo(); const plugins = autodetect.getAvailablePlugins(); - const availablePlugins: PackageDependency[] = ( - plugins as Array> - ).map(({ name, module }) => { - const pluginImpl: any = Object.values(module).find((v: any) => !!v?.getId); - return { - name, - versions: (pluginImpl?.getId() as string) || '', - }; - }); + const availablePlugins: PackageDependency[] = plugins.map( + ({ name, components }) => { + return { + name, + versions: Object.keys(components).toString(), + }; + }, + ); if (loading) { return ; } else if (error) { diff --git a/yarn.lock b/yarn.lock index a6459092b8..d914859dd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3382,6 +3382,7 @@ __metadata: resolution: "@backstage/autodetect@workspace:packages/autodetect" dependencies: "@backstage/cli": "workspace:^" + "@backstage/core-plugin-api": "workspace:^" "@testing-library/jest-dom": ^5.10.1 languageName: unknown linkType: soft From 4f14cae16e6273595a7e9f564ff7c3d7b1ff8e8e Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 18 Jul 2023 16:54:54 +0200 Subject: [PATCH 12/41] Fix tsc Co-authored-by: Vincenzo Scamporlino Signed-off-by: Philipp Hugenroth --- packages/cli/src/lib/bundler/server.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index db046e1e18..653f02f5a5 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -45,7 +45,7 @@ export async function serveBundle(options: ServeOptions) { const packageDetectionMode = options.fullConfig.getOptional( 'app.experimental.packages', ); - const extraPackages = []; + const extraPackages: string[] = []; if (packageDetectionMode === 'all') { for (const depName of Object.keys(pkg.dependencies ?? {})) { const depPackageJson: BackstagePackageJson = require(require.resolve( @@ -61,9 +61,10 @@ export async function serveBundle(options: ServeOptions) { } } } else { - const packagesList = Array.isArray(packageDetectionMode) - ? packageDetectionMode - : []; + const packagesList = options.fullConfig.getStringArray( + 'app.experimental.packages', + ); + for (const depName of packagesList ?? []) { const depPackageJson: BackstagePackageJson = require(require.resolve( `${depName}/package.json`, @@ -203,7 +204,6 @@ export async function serveBundle(options: ServeOptions) { : false, host, port, - proxy: pkg.proxy, // When the dev server is behind a proxy, the host and public hostname differ allowedHosts: [url.hostname], client: { From 6692f99f871f6b00a571e7660844ad2353570abb Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 19 Jul 2023 11:14:03 +0200 Subject: [PATCH 13/41] cli: default to all packages Co-authored-by: Jack Palmer Co-authored-by: Philipp Hugenroth Signed-off-by: Vincenzo Scamporlino --- packages/cli/src/lib/bundler/server.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index 653f02f5a5..aa81fede81 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -42,9 +42,8 @@ export async function serveBundle(options: ServeOptions) { // TODO: proper // Assumption for config string based on https://github.com/backstage/backstage/issues/18372 ^ - const packageDetectionMode = options.fullConfig.getOptional( - 'app.experimental.packages', - ); + const packageDetectionMode = + options.fullConfig.getOptional('app.experimental.packages') || 'all'; const extraPackages: string[] = []; if (packageDetectionMode === 'all') { for (const depName of Object.keys(pkg.dependencies ?? {})) { From 565ff751b848fa48056c58d66c8604b80ad71e90 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 19 Jul 2023 11:59:47 +0200 Subject: [PATCH 14/41] cli: write module instead of using a virtual module Co-authored-by: Philipp Hugenroth Signed-off-by: Vincenzo Scamporlino --- packages/autodetect/src/index.ts | 7 ++++--- packages/cli/src/lib/bundler/config.ts | 23 ++++++++++++----------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/autodetect/src/index.ts b/packages/autodetect/src/index.ts index b347a351da..490e6f25d7 100644 --- a/packages/autodetect/src/index.ts +++ b/packages/autodetect/src/index.ts @@ -34,9 +34,10 @@ type DetectedModule = { * @public */ function getAvailablePlugins(): DetectedPlugin[] { - const { modules } = __webpack_require__( - './node_modules/backstage-autodetected-plugins.js', - ) as { modules: DetectedModule[] }; + // eslint-disable-next-line @backstage/no-undeclared-imports + const { modules } = require('backstage-autodetected-plugins') as { + modules: DetectedModule[]; + }; return modules .map(splitPluginFromComponents) diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index e732377a4d..5e0176fae9 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -22,7 +22,6 @@ import ModuleScopePlugin from 'react-dev-utils/ModuleScopePlugin'; import { RunScriptWebpackPlugin } from 'run-script-webpack-plugin'; import webpack, { ProvidePlugin } from 'webpack'; import nodeExternals from 'webpack-node-externals'; -import VirtualModulesPlugin from 'webpack-virtual-modules'; import { isChildPath } from '@backstage/cli-common'; import { getPackages } from '@manypkg/get-packages'; import { optimization } from './optimization'; @@ -39,6 +38,7 @@ import ESLintPlugin from 'eslint-webpack-plugin'; import pickBy from 'lodash/pickBy'; import yn from 'yn'; import { readEntryPoints } from '../entryPoints'; +import path from 'path'; const BUILD_CACHE_ENV_VAR = 'BACKSTAGE_CLI_EXPERIMENTAL_BUILD_CACHE'; @@ -128,17 +128,18 @@ export async function createConfig( }), ); - if (extraPackages.length > 0) { - const requirePackageScript = extraPackages - ?.map(pkg => `{name: '${pkg}', module: require('${pkg}')}`) - .join(','); + const requirePackageScript = extraPackages + ?.map(pkg => `{name: '${pkg}', module: require('${pkg}')}`) + .join(','); - plugins.push( - new VirtualModulesPlugin({ - 'node_modules/backstage-autodetected-plugins.js': `module.exports = { modules: [${requirePackageScript}] };`, - }), - ); - } + await fs.writeFile( + path.join( + cliPaths.targetRoot, + 'node_modules', + 'backstage-autodetected-plugins.js', + ), + `module.exports = { modules: [${requirePackageScript}] };`, + ); const buildInfo = await readBuildInfo(); plugins.push( From 0811fcee4056a59cf5c3ba19b6f9545a839e42e2 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 19 Jul 2023 14:38:58 +0200 Subject: [PATCH 15/41] autodetect: add missing api-report Signed-off-by: Vincenzo Scamporlino --- packages/autodetect/api-report.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 packages/autodetect/api-report.md diff --git a/packages/autodetect/api-report.md b/packages/autodetect/api-report.md new file mode 100644 index 0000000000..034dd3e8f0 --- /dev/null +++ b/packages/autodetect/api-report.md @@ -0,0 +1,19 @@ +## API Report File for "@backstage/autodetect" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackstagePlugin } from '@backstage/core-plugin-api'; + +// @public (undocumented) +export type DetectedPlugin = { + name: string; + plugin: BackstagePlugin; + components: Record; +}; + +// @public (undocumented) +export function getAvailablePlugins(): DetectedPlugin[]; + +// (No @packageDocumentation comment for this package) +``` From e6ef66c5d6c0f2ca7bc025f1d739eb5423287973 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 19 Jul 2023 14:39:35 +0200 Subject: [PATCH 16/41] 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 = { From 3c4629526d7c844cc13d738e94869fde618451b0 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 19 Jul 2023 14:46:18 +0200 Subject: [PATCH 17/41] cli: refactor detectPlugins Signed-off-by: Vincenzo Scamporlino --- packages/cli/src/lib/bundler/config.ts | 30 ++++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 46ababaabc..be5a87b76a 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -407,18 +407,20 @@ export async function detectPlugins({ ? 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[]); + return allowedPackages + .map(depName => { + const depPackageJson: BackstagePackageJson = require(require.resolve( + `${depName}/package.json`, + { paths: [paths.targetPath] }, + )); + if ( + ['frontend-plugin', 'frontend-plugin-module'].includes( + depPackageJson.backstage?.role || '', + ) + ) { + return depName; + } + return undefined; + }) + .filter((d): d is string => !!d); } From 0af208f85fe9f3e7e907ea6fca53c29401f8893f Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 19 Jul 2023 15:27:13 +0200 Subject: [PATCH 18/41] Abstract detection logic in seperate file Co-authored-by: Vincenzo Scamporlino Signed-off-by: Philipp Hugenroth --- packages/cli/src/lib/bundler/bundle.ts | 7 +++ packages/cli/src/lib/bundler/config.ts | 63 +-------------------- packages/cli/src/lib/bundler/discover.ts | 72 ++++++++++++++++++++++++ packages/cli/src/lib/bundler/server.ts | 7 +++ 4 files changed, 88 insertions(+), 61 deletions(-) create mode 100644 packages/cli/src/lib/bundler/discover.ts diff --git a/packages/cli/src/lib/bundler/bundle.ts b/packages/cli/src/lib/bundler/bundle.ts index 8129f38102..ec0c360c59 100644 --- a/packages/cli/src/lib/bundler/bundle.ts +++ b/packages/cli/src/lib/bundler/bundle.ts @@ -27,6 +27,7 @@ import { createConfig, resolveBaseUrl } from './config'; import { BuildOptions } from './types'; import { resolveBundlingPaths } from './paths'; import chalk from 'chalk'; +import { writeDetectedPluginsModule } from './discover'; // TODO(Rugvip): Limits from CRA, we might want to tweak these though. const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; @@ -49,6 +50,12 @@ export async function buildBundle(options: BuildOptions) { getFrontendAppConfigs: () => options.frontendAppConfigs, }); + await writeDetectedPluginsModule({ + config: options.fullConfig, + entry: options.entry, + targetDir: options.targetDir, + }); + const isCi = yn(process.env.CI, { default: false }); const previousFileSizes = await measureFileSizesBeforeBuild(paths.targetDist); diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index be5a87b76a..25a3db1823 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -26,23 +26,18 @@ import { isChildPath } from '@backstage/cli-common'; import { getPackages } from '@manypkg/get-packages'; import { optimization } from './optimization'; import { Config } from '@backstage/config'; -import { - BundlingPaths, - BundlingPathsOptions, - resolveBundlingPaths, -} from './paths'; +import { BundlingPaths } 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, BackstagePackageJson } from '@backstage/cli-node'; +import { BackstagePackage } from '@backstage/cli-node'; import { runPlain } from '../run'; import ESLintPlugin from 'eslint-webpack-plugin'; import pickBy from 'lodash/pickBy'; import yn from 'yn'; import { readEntryPoints } from '../entryPoints'; -import path from 'path'; const BUILD_CACHE_ENV_VAR = 'BACKSTAGE_CLI_EXPERIMENTAL_BUILD_CACHE'; @@ -132,24 +127,6 @@ export async function createConfig( }), ); - const detectedPlugins = await detectPlugins({ - config: options.fullConfig, - entry: options.entry, - targetDir: options.targetDir, - }); - const requirePackageScript = detectedPlugins - ?.map(pkg => `{name: '${pkg}', module: require('${pkg}')}`) - .join(','); - - await fs.writeFile( - path.join( - cliPaths.targetRoot, - 'node_modules', - 'backstage-autodetected-plugins.js', - ), - `module.exports = { modules: [${requirePackageScript}] };`, - ); - const buildInfo = await readBuildInfo(); plugins.push( new webpack.DefinePlugin({ @@ -388,39 +365,3 @@ 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 - .map(depName => { - const depPackageJson: BackstagePackageJson = require(require.resolve( - `${depName}/package.json`, - { paths: [paths.targetPath] }, - )); - if ( - ['frontend-plugin', 'frontend-plugin-module'].includes( - depPackageJson.backstage?.role || '', - ) - ) { - return depName; - } - return undefined; - }) - .filter((d): d is string => !!d); -} diff --git a/packages/cli/src/lib/bundler/discover.ts b/packages/cli/src/lib/bundler/discover.ts new file mode 100644 index 0000000000..5f3d3e290b --- /dev/null +++ b/packages/cli/src/lib/bundler/discover.ts @@ -0,0 +1,72 @@ +/* + * 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 { BackstagePackageJson } from '@backstage/cli-node'; +import { Config } from '@backstage/config'; +import fs from 'fs-extra'; +import path from 'path'; + +import { BundlingPathsOptions, resolveBundlingPaths } from './paths'; +import { paths as cliPaths } from '../../lib/paths'; + +type Options = { config: Config } & BundlingPathsOptions; + +async function detectPlugins({ config, entry, targetDir }: Options) { + 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 + .map(depName => { + const depPackageJson: BackstagePackageJson = require(require.resolve( + `${depName}/package.json`, + { paths: [paths.targetPath] }, + )); + if ( + ['frontend-plugin', 'frontend-plugin-module'].includes( + depPackageJson.backstage?.role || '', + ) + ) { + return depName; + } + return undefined; + }) + .filter((d): d is string => !!d); +} + +export async function writeDetectedPluginsModule(options: Options) { + const requirePackageScript = (await detectPlugins(options)) + ?.map(pkg => `{name: '${pkg}', module: require('${pkg}')}`) + .join(','); + + await fs.writeFile( + path.join( + cliPaths.targetRoot, + 'node_modules', + 'backstage-autodetected-plugins.js', + ), + `module.exports = { modules: [${requirePackageScript}] };`, + ); +} diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index 96eee2a387..fd9c2f92fd 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -34,6 +34,7 @@ import { includedFilter, } from '../../commands/versions/lint'; import { BackstagePackageJson } from '@backstage/cli-node'; +import { writeDetectedPluginsModule } from './discover'; export async function serveBundle(options: ServeOptions) { const paths = resolveBundlingPaths(options); @@ -171,6 +172,12 @@ export async function serveBundle(options: ServeOptions) { }, }); + await writeDetectedPluginsModule({ + config: options.fullConfig, + entry: options.entry, + targetDir: options.targetDir, + }); + const compiler = webpack(config); server = new WebpackDevServer( From 02aba094c9f85b3d640875932851dc0b5cbd7dc0 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 19 Jul 2023 19:46:40 +0200 Subject: [PATCH 19/41] cli: rollback BundlingOptions changes Signed-off-by: Vincenzo Scamporlino --- packages/cli/src/lib/bundler/types.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/cli/src/lib/bundler/types.ts b/packages/cli/src/lib/bundler/types.ts index 0dc6807b41..43b2328239 100644 --- a/packages/cli/src/lib/bundler/types.ts +++ b/packages/cli/src/lib/bundler/types.ts @@ -24,11 +24,8 @@ export type BundlingOptions = { frontendConfig: Config; getFrontendAppConfigs(): AppConfig[]; frontendAppConfigs: AppConfig[]; - fullConfig: Config; baseUrl: URL; parallelism?: number; - entry: string; - targetDir?: string; }; export type ServeOptions = BundlingPathsOptions & { From 5c958a38525b3f5ceef1aa2919bdc546425b4506 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 19 Jul 2023 19:47:36 +0200 Subject: [PATCH 20/41] cli: use export Signed-off-by: Vincenzo Scamporlino --- packages/cli/src/lib/bundler/discover.ts | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/cli/src/lib/bundler/discover.ts b/packages/cli/src/lib/bundler/discover.ts index 5f3d3e290b..4de408d6c7 100644 --- a/packages/cli/src/lib/bundler/discover.ts +++ b/packages/cli/src/lib/bundler/discover.ts @@ -24,6 +24,21 @@ import { paths as cliPaths } from '../../lib/paths'; type Options = { config: Config } & BundlingPathsOptions; +export async function writeDetectedPluginsModule(options: Options) { + const requirePackageScript = (await detectPlugins(options)) + ?.map(pkg => `{name: '${pkg}', module: require('${pkg}')}`) + .join(','); + + await fs.writeFile( + path.join( + cliPaths.targetRoot, + 'node_modules', + 'backstage-autodetected-plugins.js', + ), + `export const modules = [${requirePackageScript}];`, + ); +} + async function detectPlugins({ config, entry, targetDir }: Options) { const paths = resolveBundlingPaths({ entry, targetDir }); const pkg: BackstagePackageJson = await fs.readJson(paths.targetPackageJson); @@ -55,18 +70,3 @@ async function detectPlugins({ config, entry, targetDir }: Options) { }) .filter((d): d is string => !!d); } - -export async function writeDetectedPluginsModule(options: Options) { - const requirePackageScript = (await detectPlugins(options)) - ?.map(pkg => `{name: '${pkg}', module: require('${pkg}')}`) - .join(','); - - await fs.writeFile( - path.join( - cliPaths.targetRoot, - 'node_modules', - 'backstage-autodetected-plugins.js', - ), - `module.exports = { modules: [${requirePackageScript}] };`, - ); -} From 7b7af928ec6c3d95a6dae7f96caf6341d0c89ffe Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 19 Jul 2023 19:48:45 +0200 Subject: [PATCH 21/41] cli: autodetect refactor typings Signed-off-by: Vincenzo Scamporlino --- .../autodetect/src/autodetect-plugins.d.ts | 22 +++++++++++++++++++ packages/autodetect/src/index.ts | 20 +++++------------ .../Content/InfoContent/InfoContent.tsx | 4 ++-- 3 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 packages/autodetect/src/autodetect-plugins.d.ts diff --git a/packages/autodetect/src/autodetect-plugins.d.ts b/packages/autodetect/src/autodetect-plugins.d.ts new file mode 100644 index 0000000000..5eb19dee39 --- /dev/null +++ b/packages/autodetect/src/autodetect-plugins.d.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 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. + */ +declare module 'backstage-autodetected-plugins' { + type DetectedModule = { + name: string; + module: Record; + }; + const modules: Array<{ name: string; module: Record }>; +} diff --git a/packages/autodetect/src/index.ts b/packages/autodetect/src/index.ts index 490e6f25d7..833b9bad7c 100644 --- a/packages/autodetect/src/index.ts +++ b/packages/autodetect/src/index.ts @@ -16,29 +16,22 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; +// eslint-disable-next-line @backstage/no-undeclared-imports +import { modules, DetectedModule } from 'backstage-autodetected-plugins'; + /** * @public */ -type DetectedPlugin = { +export type DetectedPlugin = { name: string; plugin: BackstagePlugin; components: Record; }; -type DetectedModule = { - name: string; - module: Record; -}; - /** * @public */ -function getAvailablePlugins(): DetectedPlugin[] { - // eslint-disable-next-line @backstage/no-undeclared-imports - const { modules } = require('backstage-autodetected-plugins') as { - modules: DetectedModule[]; - }; - +export function getAvailablePlugins(): DetectedPlugin[] { return modules .map(splitPluginFromComponents) .filter((m): m is DetectedPlugin => !!m.plugin); @@ -70,6 +63,3 @@ function isBackstagePlugin(obj: Record): obj is BackstagePlugin { typeof obj.provide !== 'undefined' ); } - -export { getAvailablePlugins }; -export type { DetectedPlugin }; diff --git a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx index 6ed0140b96..67d34e4010 100644 --- a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx +++ b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx @@ -41,7 +41,7 @@ import { DevToolsInfo, PackageDependency, } from '@backstage/plugin-devtools-common'; -import * as autodetect from '@backstage/autodetect'; +import { getAvailablePlugins } from '@backstage/autodetect'; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -77,7 +77,7 @@ const copyToClipboard = ({ about }: { about: DevToolsInfo | undefined }) => { export const InfoContent = () => { const classes = useStyles(); const { about, loading, error } = useInfo(); - const plugins = autodetect.getAvailablePlugins(); + const plugins = getAvailablePlugins(); const availablePlugins: PackageDependency[] = plugins.map( ({ name, components }) => { From 7d689d029ed8d63eab045463dc13ab5a09c5b5e1 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 19 Jul 2023 19:50:30 +0200 Subject: [PATCH 22/41] devtools: show plugin id Signed-off-by: Vincenzo Scamporlino --- .../src/components/Content/InfoContent/InfoContent.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx index 67d34e4010..4155552383 100644 --- a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx +++ b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx @@ -80,10 +80,10 @@ export const InfoContent = () => { const plugins = getAvailablePlugins(); const availablePlugins: PackageDependency[] = plugins.map( - ({ name, components }) => { + ({ name, plugin }) => { return { - name, - versions: Object.keys(components).toString(), + name: `${name} (${plugin.getId()})`, + versions: '', }; }, ); From 8a8b3c36132135e9d84082d11621b124f04707cd Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 19 Jul 2023 20:05:22 +0200 Subject: [PATCH 23/41] cli: wrap with underscores Signed-off-by: Vincenzo Scamporlino --- packages/autodetect/src/autodetect-plugins.d.ts | 2 +- packages/autodetect/src/index.ts | 2 +- packages/cli/src/lib/bundler/discover.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/autodetect/src/autodetect-plugins.d.ts b/packages/autodetect/src/autodetect-plugins.d.ts index 5eb19dee39..a5af550743 100644 --- a/packages/autodetect/src/autodetect-plugins.d.ts +++ b/packages/autodetect/src/autodetect-plugins.d.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -declare module 'backstage-autodetected-plugins' { +declare module '__backstage-autodetected-plugins__' { type DetectedModule = { name: string; module: Record; diff --git a/packages/autodetect/src/index.ts b/packages/autodetect/src/index.ts index 833b9bad7c..67c3251290 100644 --- a/packages/autodetect/src/index.ts +++ b/packages/autodetect/src/index.ts @@ -17,7 +17,7 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; // eslint-disable-next-line @backstage/no-undeclared-imports -import { modules, DetectedModule } from 'backstage-autodetected-plugins'; +import { modules, DetectedModule } from '__backstage-autodetected-plugins__'; /** * @public diff --git a/packages/cli/src/lib/bundler/discover.ts b/packages/cli/src/lib/bundler/discover.ts index 4de408d6c7..03de8d24c4 100644 --- a/packages/cli/src/lib/bundler/discover.ts +++ b/packages/cli/src/lib/bundler/discover.ts @@ -33,7 +33,7 @@ export async function writeDetectedPluginsModule(options: Options) { path.join( cliPaths.targetRoot, 'node_modules', - 'backstage-autodetected-plugins.js', + '__backstage-autodetected-plugins__.js', ), `export const modules = [${requirePackageScript}];`, ); From fe0ef349f193a0549e2e161b548c01e82c2b9fb9 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 24 Jul 2023 14:44:07 +0200 Subject: [PATCH 24/41] Fix rebase Signed-off-by: Philipp Hugenroth --- packages/cli/src/lib/bundler/config.ts | 2 - packages/cli/src/lib/bundler/discover.ts | 2 +- packages/cli/src/lib/bundler/server.ts | 69 +++++------------------- 3 files changed, 15 insertions(+), 58 deletions(-) diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 25a3db1823..b9ef77d528 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -135,8 +135,6 @@ export async function createConfig( () => JSON.stringify(options.getFrontendAppConfigs()), true, ), - 'process.env.EXTRA_PACKAGE_IMPORTS': JSON.stringify(extraPackages), - 'process.env.SINGLE_PACKAGE_IMPORT': JSON.stringify(extraPackages[0]), }), ); diff --git a/packages/cli/src/lib/bundler/discover.ts b/packages/cli/src/lib/bundler/discover.ts index 03de8d24c4..6f85210f80 100644 --- a/packages/cli/src/lib/bundler/discover.ts +++ b/packages/cli/src/lib/bundler/discover.ts @@ -19,8 +19,8 @@ import { Config } from '@backstage/config'; import fs from 'fs-extra'; import path from 'path'; -import { BundlingPathsOptions, resolveBundlingPaths } from './paths'; import { paths as cliPaths } from '../../lib/paths'; +import { BundlingPathsOptions, resolveBundlingPaths } from './paths'; type Options = { config: Config } & BundlingPathsOptions; diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index fd9c2f92fd..a726d04ba8 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -14,71 +14,30 @@ * limitations under the License. */ +import { PackageGraph } from '@backstage/cli-node'; +import { AppConfig } from '@backstage/config'; +import chalk from 'chalk'; +import fs from 'fs-extra'; +import uniq from 'lodash/uniq'; +import openBrowser from 'react-dev-utils/openBrowser'; 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'; -import { resolveBundlingPaths } from './paths'; -import { paths as libPaths } from '../../lib/paths'; -import { loadCliConfig } from '../config'; -import chalk from 'chalk'; -import { AppConfig } from '@backstage/config'; -import { PackageGraph } from '@backstage/cli-node'; -import { Lockfile } from '../versioning'; import { forbiddenDuplicatesFilter, includedFilter, } from '../../commands/versions/lint'; -import { BackstagePackageJson } from '@backstage/cli-node'; +import { paths as libPaths } from '../../lib/paths'; +import { loadCliConfig } from '../config'; +import { Lockfile } from '../versioning'; +import { createConfig, resolveBaseUrl } from './config'; import { writeDetectedPluginsModule } from './discover'; +import { resolveBundlingPaths } from './paths'; +import { ServeOptions } from './types'; export async function serveBundle(options: ServeOptions) { const paths = resolveBundlingPaths(options); - const pkgPath = paths.targetPackageJson; - const pkg: BackstagePackageJson = await fs.readJson(pkgPath); - // TODO: proper - // Assumption for config string based on https://github.com/backstage/backstage/issues/18372 ^ - const packageDetectionMode = - options.fullConfig.getOptional('app.experimental.packages') || 'all'; - const extraPackages: string[] = []; - if (packageDetectionMode === 'all') { - for (const depName of Object.keys(pkg.dependencies ?? {})) { - const depPackageJson: BackstagePackageJson = require(require.resolve( - `${depName}/package.json`, - { paths: [paths.targetPath] }, - )); - if ( - ['frontend-plugin', 'frontend-plugin-module'].includes( - depPackageJson.backstage?.role || '', - ) - ) { - extraPackages.push(depName); - } - } - } else { - const packagesList = options.fullConfig.getStringArray( - 'app.experimental.packages', - ); - - for (const depName of packagesList ?? []) { - const depPackageJson: BackstagePackageJson = require(require.resolve( - `${depName}/package.json`, - { paths: [paths.targetPath] }, - )); - if ( - ['frontend-plugin', 'frontend-plugin-module'].includes( - depPackageJson.backstage?.role || '', - ) - ) { - extraPackages.push(depName); - } - } - } if (options.verifyVersions) { const lockfile = await Lockfile.load( libPaths.resolveTargetRoot('yarn.lock'), @@ -163,17 +122,17 @@ export async function serveBundle(options: ServeOptions) { const config = await createConfig(paths, { ...options, checksEnabled: options.checksEnabled, - extraPackages, isDev: true, baseUrl: url, frontendConfig, + frontendAppConfigs: [], getFrontendAppConfigs: () => { return latestFrontendAppConfigs; }, }); await writeDetectedPluginsModule({ - config: options.fullConfig, + config: fullConfig, entry: options.entry, targetDir: options.targetDir, }); From 9fa2fae3c297c369e1bc69a39205075f70415a95 Mon Sep 17 00:00:00 2001 From: Jack Palmer Date: Tue, 25 Jul 2023 12:09:10 +0100 Subject: [PATCH 25/41] feat: Add watching functionality to plugin detection Signed-off-by: Jack Palmer --- packages/cli/src/lib/bundler/discover.ts | 21 +++++++++++++++++++-- packages/cli/src/lib/bundler/server.ts | 7 +++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/lib/bundler/discover.ts b/packages/cli/src/lib/bundler/discover.ts index 6f85210f80..5db8517f32 100644 --- a/packages/cli/src/lib/bundler/discover.ts +++ b/packages/cli/src/lib/bundler/discover.ts @@ -18,13 +18,30 @@ import { BackstagePackageJson } from '@backstage/cli-node'; import { Config } from '@backstage/config'; import fs from 'fs-extra'; import path from 'path'; +import chokidar from 'chokidar'; import { paths as cliPaths } from '../../lib/paths'; import { BundlingPathsOptions, resolveBundlingPaths } from './paths'; -type Options = { config: Config } & BundlingPathsOptions; +type Options = { config: Config; watch: () => void } & BundlingPathsOptions; -export async function writeDetectedPluginsModule(options: Options) { +export async function buildDetectedPlugins(options: Options) { + const { entry, targetDir } = options; + const { targetPackageJson } = resolveBundlingPaths({ entry, targetDir }); + + if (!!options.watch) { + const watcher = chokidar.watch(targetPackageJson); + + watcher.on('change', async () => { + await writeDetectedPluginsModule(options); + options.watch(); + }); + } + + await writeDetectedPluginsModule(options); +} + +async function writeDetectedPluginsModule(options: Options) { const requirePackageScript = (await detectPlugins(options)) ?.map(pkg => `{name: '${pkg}', module: require('${pkg}')}`) .join(','); diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index a726d04ba8..038d27ed56 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -31,7 +31,7 @@ import { paths as libPaths } from '../../lib/paths'; import { loadCliConfig } from '../config'; import { Lockfile } from '../versioning'; import { createConfig, resolveBaseUrl } from './config'; -import { writeDetectedPluginsModule } from './discover'; +import { buildDetectedPlugins } from './discover'; import { resolveBundlingPaths } from './paths'; import { ServeOptions } from './types'; @@ -131,10 +131,13 @@ export async function serveBundle(options: ServeOptions) { }, }); - await writeDetectedPluginsModule({ + await buildDetectedPlugins({ config: fullConfig, entry: options.entry, targetDir: options.targetDir, + watch() { + server?.invalidate(); + }, }); const compiler = webpack(config); From 24cf05cb36b309451babbd057643e1fde1d616f8 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Sep 2023 15:04:29 +0200 Subject: [PATCH 26/41] cli: refactor pacakge detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: Johan Haals Co-authored-by: Camila Belo Co-authored-by: Philipp Hugenroth Signed-off-by: Patrik Oldsberg --- packages/cli/src/lib/bundler/bundle.ts | 7 ++-- packages/cli/src/lib/bundler/discover.ts | 46 ++++++++++++++---------- packages/cli/src/lib/bundler/server.ts | 3 +- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/packages/cli/src/lib/bundler/bundle.ts b/packages/cli/src/lib/bundler/bundle.ts index ec0c360c59..9c67eb9bc3 100644 --- a/packages/cli/src/lib/bundler/bundle.ts +++ b/packages/cli/src/lib/bundler/bundle.ts @@ -27,7 +27,7 @@ import { createConfig, resolveBaseUrl } from './config'; import { BuildOptions } from './types'; import { resolveBundlingPaths } from './paths'; import chalk from 'chalk'; -import { writeDetectedPluginsModule } from './discover'; +import { buildDetectedPlugins } from './discover'; // TODO(Rugvip): Limits from CRA, we might want to tweak these though. const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; @@ -50,10 +50,9 @@ export async function buildBundle(options: BuildOptions) { getFrontendAppConfigs: () => options.frontendAppConfigs, }); - await writeDetectedPluginsModule({ + await buildDetectedPlugins({ config: options.fullConfig, - entry: options.entry, - targetDir: options.targetDir, + targetPath: paths.targetPath, }); const isCi = yn(process.env.CI, { default: false }); diff --git a/packages/cli/src/lib/bundler/discover.ts b/packages/cli/src/lib/bundler/discover.ts index 5db8517f32..9325c9bcd8 100644 --- a/packages/cli/src/lib/bundler/discover.ts +++ b/packages/cli/src/lib/bundler/discover.ts @@ -16,38 +16,39 @@ import { BackstagePackageJson } from '@backstage/cli-node'; import { Config } from '@backstage/config'; -import fs from 'fs-extra'; -import path from 'path'; import chokidar from 'chokidar'; - +import fs from 'fs-extra'; +import { join as joinPath, resolve as resolvePath } from 'path'; import { paths as cliPaths } from '../../lib/paths'; -import { BundlingPathsOptions, resolveBundlingPaths } from './paths'; -type Options = { config: Config; watch: () => void } & BundlingPathsOptions; - -export async function buildDetectedPlugins(options: Options) { - const { entry, targetDir } = options; - const { targetPackageJson } = resolveBundlingPaths({ entry, targetDir }); - - if (!!options.watch) { - const watcher = chokidar.watch(targetPackageJson); +export async function buildDetectedPlugins(options: { + config: Config; + targetPath: string; + watch?: () => void; +}) { + const { watch, targetPath } = options; + if (watch) { + const watcher = chokidar.watch(resolvePath(targetPath, 'package.json')); watcher.on('change', async () => { await writeDetectedPluginsModule(options); - options.watch(); + watch(); }); } await writeDetectedPluginsModule(options); } -async function writeDetectedPluginsModule(options: Options) { +async function writeDetectedPluginsModule(options: { + config: Config; + targetPath: string; +}) { const requirePackageScript = (await detectPlugins(options)) ?.map(pkg => `{name: '${pkg}', module: require('${pkg}')}`) .join(','); await fs.writeFile( - path.join( + joinPath( cliPaths.targetRoot, 'node_modules', '__backstage-autodetected-plugins__.js', @@ -56,9 +57,16 @@ async function writeDetectedPluginsModule(options: Options) { ); } -async function detectPlugins({ config, entry, targetDir }: Options) { - const paths = resolveBundlingPaths({ entry, targetDir }); - const pkg: BackstagePackageJson = await fs.readJson(paths.targetPackageJson); +async function detectPlugins({ + config, + targetPath, +}: { + config: Config; + targetPath: string; +}) { + const pkg: BackstagePackageJson = await fs.readJson( + resolvePath(targetPath, 'package.json'), + ); // TODO: proper // Assumption for config string based on https://github.com/backstage/backstage/issues/18372 ^ @@ -74,7 +82,7 @@ async function detectPlugins({ config, entry, targetDir }: Options) { .map(depName => { const depPackageJson: BackstagePackageJson = require(require.resolve( `${depName}/package.json`, - { paths: [paths.targetPath] }, + { paths: [targetPath] }, )); if ( ['frontend-plugin', 'frontend-plugin-module'].includes( diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index 038d27ed56..43a87db684 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -133,8 +133,7 @@ export async function serveBundle(options: ServeOptions) { await buildDetectedPlugins({ config: fullConfig, - entry: options.entry, - targetDir: options.targetDir, + targetPath: paths.targetPath, watch() { server?.invalidate(); }, From 1e2cf52e545764343f373674056ac30605fa782e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Sep 2023 15:20:37 +0200 Subject: [PATCH 27/41] add app-next-example-plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: Johan Haals Co-authored-by: Philipp Hugenroth Co-authored-by: Camila Belo Signed-off-by: Patrik Oldsberg --- packages/app-next-example-plugin/.eslintrc.js | 1 + packages/app-next-example-plugin/package.json | 53 +++++++++++++++++++ .../app-next-example-plugin/src/Component.tsx | 23 ++++++++ .../src/ExampleSidebarItem.tsx | 23 ++++++++ packages/app-next-example-plugin/src/index.ts | 0 .../app-next-example-plugin/src/plugin.tsx | 32 +++++++++++ 6 files changed, 132 insertions(+) create mode 100644 packages/app-next-example-plugin/.eslintrc.js create mode 100644 packages/app-next-example-plugin/package.json create mode 100644 packages/app-next-example-plugin/src/Component.tsx create mode 100644 packages/app-next-example-plugin/src/ExampleSidebarItem.tsx create mode 100644 packages/app-next-example-plugin/src/index.ts create mode 100644 packages/app-next-example-plugin/src/plugin.tsx diff --git a/packages/app-next-example-plugin/.eslintrc.js b/packages/app-next-example-plugin/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/packages/app-next-example-plugin/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/packages/app-next-example-plugin/package.json b/packages/app-next-example-plugin/package.json new file mode 100644 index 0000000000..2838cc097a --- /dev/null +++ b/packages/app-next-example-plugin/package.json @@ -0,0 +1,53 @@ +{ + "name": "@backstage/app-next-example-plugin", + "description": "Backstage internal example plugin", + "version": "0.0.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "private": true, + "backstage": { + "role": "frontend-plugin" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/app-next-example-plugin" + }, + "keywords": [ + "backstage" + ], + "license": "Apache-2.0", + "main": "src/index.ts", + "types": "src/index.ts", + "scripts": { + "build": "backstage-cli package build", + "start": "backstage-cli package start", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean" + }, + "dependencies": { + "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/core-components": "workspace:^", + "@material-ui/icons": "^4.9.1" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0", + "react-dom": "^16.13.1 || ^17.0.0", + "react-router-dom": "6.0.0-beta.0 || ^6.3.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "cross-fetch": "^3.1.5", + "msw": "^1.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/packages/app-next-example-plugin/src/Component.tsx b/packages/app-next-example-plugin/src/Component.tsx new file mode 100644 index 0000000000..2cca17c88c --- /dev/null +++ b/packages/app-next-example-plugin/src/Component.tsx @@ -0,0 +1,23 @@ +/* + * Copyright 2023 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 React from 'react'; + +export const Component = () => ( +
+

Winning

+
+); diff --git a/packages/app-next-example-plugin/src/ExampleSidebarItem.tsx b/packages/app-next-example-plugin/src/ExampleSidebarItem.tsx new file mode 100644 index 0000000000..6d8bedc509 --- /dev/null +++ b/packages/app-next-example-plugin/src/ExampleSidebarItem.tsx @@ -0,0 +1,23 @@ +/* + * Copyright 2023 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 React from 'react'; +import { SidebarItem } from '@backstage/core-components'; +import SaveIcon from '@material-ui/icons/Save'; + +export const ExampleSidebarItem = () => ( + +); diff --git a/packages/app-next-example-plugin/src/index.ts b/packages/app-next-example-plugin/src/index.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/app-next-example-plugin/src/plugin.tsx b/packages/app-next-example-plugin/src/plugin.tsx new file mode 100644 index 0000000000..85d2dd4cc9 --- /dev/null +++ b/packages/app-next-example-plugin/src/plugin.tsx @@ -0,0 +1,32 @@ +/* + * Copyright 2023 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 { + createPageExtension, + createPlugin, +} from '@backstage/frontend-plugin-api'; +import { Component } from './Component'; + +export const ExamplePage = createPageExtension({ + id: 'example.page', + defaultPath: '/example', + component: () => () => Component, +}); + +export const examplePlugin = createPlugin({ + id: 'example', + extensions: [ExamplePage], +}); From d2fb430784c6c815e27c9bce5ecbf1eff8e272ae Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Sep 2023 15:34:58 +0200 Subject: [PATCH 28/41] cli: refactor package detection to support inc/ecl + entry point Co-authored-by: Philipp Hugenroth Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- packages/cli/src/lib/bundler/bundle.ts | 13 ++- packages/cli/src/lib/bundler/config.ts | 2 +- packages/cli/src/lib/bundler/discover.ts | 142 ++++++++++++++--------- packages/cli/src/lib/bundler/server.ts | 19 +-- packages/cli/src/lib/bundler/types.ts | 1 + 5 files changed, 107 insertions(+), 70 deletions(-) diff --git a/packages/cli/src/lib/bundler/bundle.ts b/packages/cli/src/lib/bundler/bundle.ts index 9c67eb9bc3..6d5b2ff799 100644 --- a/packages/cli/src/lib/bundler/bundle.ts +++ b/packages/cli/src/lib/bundler/bundle.ts @@ -27,7 +27,7 @@ import { createConfig, resolveBaseUrl } from './config'; import { BuildOptions } from './types'; import { resolveBundlingPaths } from './paths'; import chalk from 'chalk'; -import { buildDetectedPlugins } from './discover'; +import { createDetectedModulesEntrypoint } from './discover'; // TODO(Rugvip): Limits from CRA, we might want to tweak these though. const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; @@ -42,17 +42,18 @@ export async function buildBundle(options: BuildOptions) { const paths = resolveBundlingPaths(options); + const detectedModulesEntryPoint = await createDetectedModulesEntrypoint({ + config: options.fullConfig, + targetPath: paths.targetPath, + }); + const config = await createConfig(paths, { ...options, checksEnabled: false, isDev: false, baseUrl: resolveBaseUrl(options.frontendConfig), getFrontendAppConfigs: () => options.frontendAppConfigs, - }); - - await buildDetectedPlugins({ - config: options.fullConfig, - targetPath: paths.targetPath, + additionalEntryPoints: detectedModulesEntryPoint, }); const isCi = yn(process.env.CI, { default: false }); diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index b9ef77d528..c9238e7e48 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -161,7 +161,7 @@ export async function createConfig( }, devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map', context: paths.targetPath, - entry: paths.targetEntry, + entry: [...(options.additionalEntryPoints ?? []), paths.targetEntry], resolve: { extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx', '.json', '.wasm'], mainFields: ['browser', 'module', 'main'], diff --git a/packages/cli/src/lib/bundler/discover.ts b/packages/cli/src/lib/bundler/discover.ts index 9325c9bcd8..c30c1131d4 100644 --- a/packages/cli/src/lib/bundler/discover.ts +++ b/packages/cli/src/lib/bundler/discover.ts @@ -21,29 +21,72 @@ import fs from 'fs-extra'; import { join as joinPath, resolve as resolvePath } from 'path'; import { paths as cliPaths } from '../../lib/paths'; -export async function buildDetectedPlugins(options: { - config: Config; - targetPath: string; - watch?: () => void; -}) { - const { watch, targetPath } = options; - if (watch) { - const watcher = chokidar.watch(resolvePath(targetPath, 'package.json')); +const DETECTED_MODULES_MODULE_NAME = '__backstage-autodetected-plugins__'; - watcher.on('change', async () => { - await writeDetectedPluginsModule(options); - watch(); - }); - } - - await writeDetectedPluginsModule(options); +interface PackageDetectionConfig { + include?: string[]; + exclude?: string[]; } -async function writeDetectedPluginsModule(options: { - config: Config; - targetPath: string; -}) { - const requirePackageScript = (await detectPlugins(options)) +function readPackageDetectionConfig( + config: Config, +): PackageDetectionConfig | undefined { + const packages = config.getOptional('app.experimental.packages'); + if (!packages) { + return undefined; + } + + if (typeof packages === 'string') { + if (packages !== 'all') { + throw new Error( + `Invalid app.experimental.packages mode, got '${packages}', expected 'all'`, + ); + } + return {}; + } + + return { + include: config.getOptionalStringArray('app.experimental.packages.include'), + exclude: config.getOptionalStringArray('app.experimental.packages.exclude'), + }; +} + +async function detectPackages( + targetPath: string, + { include, exclude }: PackageDetectionConfig, +) { + const pkg: BackstagePackageJson = await fs.readJson( + resolvePath(targetPath, 'package.json'), + ); + + return Object.keys(pkg.dependencies ?? {}) + .flatMap(depName => { + const depPackageJson: BackstagePackageJson = require(require.resolve( + `${depName}/package.json`, + { paths: [targetPath] }, + )); + if ( + ['frontend-plugin', 'frontend-plugin-module'].includes( + depPackageJson.backstage?.role ?? '', + ) + ) { + return [depName]; + } + return []; + }) + .filter(name => { + if (exclude?.includes(name)) { + return false; + } + if (include && !include.includes(name)) { + return false; + } + return true; + }); +} + +async function writeDetectedPackagesModule(packageNames: string[]) { + const requirePackageScript = packageNames ?.map(pkg => `{name: '${pkg}', module: require('${pkg}')}`) .join(','); @@ -51,47 +94,38 @@ async function writeDetectedPluginsModule(options: { joinPath( cliPaths.targetRoot, 'node_modules', - '__backstage-autodetected-plugins__.js', + `${DETECTED_MODULES_MODULE_NAME}.js`, ), `export const modules = [${requirePackageScript}];`, ); } -async function detectPlugins({ - config, - targetPath, -}: { +export async function createDetectedModulesEntrypoint(options: { config: Config; targetPath: string; -}) { - const pkg: BackstagePackageJson = await fs.readJson( - resolvePath(targetPath, 'package.json'), + watch?: () => void; +}): Promise { + const { config, watch, targetPath } = options; + + const detectionConfig = readPackageDetectionConfig(config); + if (!detectionConfig) { + return []; + } + + if (watch) { + const watcher = chokidar.watch(resolvePath(targetPath, 'package.json')); + + watcher.on('change', async () => { + await writeDetectedPackagesModule( + await detectPackages(targetPath, detectionConfig), + ); + watch(); + }); + } + + await writeDetectedPackagesModule( + await detectPackages(targetPath, detectionConfig), ); - // 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 - .map(depName => { - const depPackageJson: BackstagePackageJson = require(require.resolve( - `${depName}/package.json`, - { paths: [targetPath] }, - )); - if ( - ['frontend-plugin', 'frontend-plugin-module'].includes( - depPackageJson.backstage?.role || '', - ) - ) { - return depName; - } - return undefined; - }) - .filter((d): d is string => !!d); + return [DETECTED_MODULES_MODULE_NAME]; } diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index 43a87db684..70cf1645ba 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -31,7 +31,7 @@ import { paths as libPaths } from '../../lib/paths'; import { loadCliConfig } from '../config'; import { Lockfile } from '../versioning'; import { createConfig, resolveBaseUrl } from './config'; -import { buildDetectedPlugins } from './discover'; +import { createDetectedModulesEntrypoint as createDetectedModulesEntryPoint } from './discover'; import { resolveBundlingPaths } from './paths'; import { ServeOptions } from './types'; @@ -119,6 +119,14 @@ export async function serveBundle(options: ServeOptions) { Number(url.port) || (url.protocol === 'https:' ? 443 : 80); + const detectedModulesEntryPoint = await createDetectedModulesEntryPoint({ + config: fullConfig, + targetPath: paths.targetPath, + watch() { + server?.invalidate(); + }, + }); + const config = await createConfig(paths, { ...options, checksEnabled: options.checksEnabled, @@ -129,14 +137,7 @@ export async function serveBundle(options: ServeOptions) { getFrontendAppConfigs: () => { return latestFrontendAppConfigs; }, - }); - - await buildDetectedPlugins({ - config: fullConfig, - targetPath: paths.targetPath, - watch() { - server?.invalidate(); - }, + additionalEntryPoints: detectedModulesEntryPoint, }); const compiler = webpack(config); diff --git a/packages/cli/src/lib/bundler/types.ts b/packages/cli/src/lib/bundler/types.ts index 43b2328239..67cdd80531 100644 --- a/packages/cli/src/lib/bundler/types.ts +++ b/packages/cli/src/lib/bundler/types.ts @@ -26,6 +26,7 @@ export type BundlingOptions = { frontendAppConfigs: AppConfig[]; baseUrl: URL; parallelism?: number; + additionalEntryPoints?: string[]; }; export type ServeOptions = BundlingPathsOptions & { From 731b8913cf69bb2a4d7102c7b1781982e4793eba Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Sep 2023 15:36:02 +0200 Subject: [PATCH 29/41] cli: rename discovery.ts Co-authored-by: Johan Haals Co-authored-by: Philipp Hugenroth Signed-off-by: Patrik Oldsberg --- packages/cli/src/lib/bundler/bundle.ts | 2 +- .../cli/src/lib/bundler/{discover.ts => packageDetection.ts} | 2 +- packages/cli/src/lib/bundler/server.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename packages/cli/src/lib/bundler/{discover.ts => packageDetection.ts} (98%) diff --git a/packages/cli/src/lib/bundler/bundle.ts b/packages/cli/src/lib/bundler/bundle.ts index 6d5b2ff799..11330df5b2 100644 --- a/packages/cli/src/lib/bundler/bundle.ts +++ b/packages/cli/src/lib/bundler/bundle.ts @@ -27,7 +27,7 @@ import { createConfig, resolveBaseUrl } from './config'; import { BuildOptions } from './types'; import { resolveBundlingPaths } from './paths'; import chalk from 'chalk'; -import { createDetectedModulesEntrypoint } from './discover'; +import { createDetectedModulesEntrypoint } from './packageDetection'; // TODO(Rugvip): Limits from CRA, we might want to tweak these though. const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; diff --git a/packages/cli/src/lib/bundler/discover.ts b/packages/cli/src/lib/bundler/packageDetection.ts similarity index 98% rename from packages/cli/src/lib/bundler/discover.ts rename to packages/cli/src/lib/bundler/packageDetection.ts index c30c1131d4..957eb87715 100644 --- a/packages/cli/src/lib/bundler/discover.ts +++ b/packages/cli/src/lib/bundler/packageDetection.ts @@ -19,7 +19,7 @@ import { Config } from '@backstage/config'; import chokidar from 'chokidar'; import fs from 'fs-extra'; import { join as joinPath, resolve as resolvePath } from 'path'; -import { paths as cliPaths } from '../../lib/paths'; +import { paths as cliPaths } from '../paths'; const DETECTED_MODULES_MODULE_NAME = '__backstage-autodetected-plugins__'; diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index 70cf1645ba..f566c268f5 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -31,7 +31,7 @@ import { paths as libPaths } from '../../lib/paths'; import { loadCliConfig } from '../config'; import { Lockfile } from '../versioning'; import { createConfig, resolveBaseUrl } from './config'; -import { createDetectedModulesEntrypoint as createDetectedModulesEntryPoint } from './discover'; +import { createDetectedModulesEntrypoint as createDetectedModulesEntryPoint } from './packageDetection'; import { resolveBundlingPaths } from './paths'; import { ServeOptions } from './types'; From b3c846db5e3556c3b2078b8df7b5a217b1c12d2b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Sep 2023 15:40:02 +0200 Subject: [PATCH 30/41] devtools: remove plugin info Co-authored-by: Philipp Hugenroth Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- plugins/devtools/package.json | 1 - .../Content/InfoContent/InfoContent.tsx | 28 ++----------------- .../InfoContent/InfoDependenciesTable.tsx | 4 +-- 3 files changed, 3 insertions(+), 30 deletions(-) diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index de95310ea9..7c7ec44575 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -28,7 +28,6 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/autodetect": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", diff --git a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx index 4155552383..0ed43d2fee 100644 --- a/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx +++ b/plugins/devtools/src/components/Content/InfoContent/InfoContent.tsx @@ -37,11 +37,7 @@ import MemoryIcon from '@material-ui/icons/Memory'; import DeveloperBoardIcon from '@material-ui/icons/DeveloperBoard'; import { BackstageLogoIcon } from './BackstageLogoIcon'; import FileCopyIcon from '@material-ui/icons/FileCopy'; -import { - DevToolsInfo, - PackageDependency, -} from '@backstage/plugin-devtools-common'; -import { getAvailablePlugins } from '@backstage/autodetect'; +import { DevToolsInfo } from '@backstage/plugin-devtools-common'; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -77,16 +73,7 @@ const copyToClipboard = ({ about }: { about: DevToolsInfo | undefined }) => { export const InfoContent = () => { const classes = useStyles(); const { about, loading, error } = useInfo(); - const plugins = getAvailablePlugins(); - const availablePlugins: PackageDependency[] = plugins.map( - ({ name, plugin }) => { - return { - name: `${name} (${plugin.getId()})`, - versions: '', - }; - }, - ); if (loading) { return ; } else if (error) { @@ -157,18 +144,7 @@ export const InfoContent = () => { - - - - - - + ); }; diff --git a/plugins/devtools/src/components/Content/InfoContent/InfoDependenciesTable.tsx b/plugins/devtools/src/components/Content/InfoContent/InfoDependenciesTable.tsx index ac54e2ebaa..e1ab59f279 100644 --- a/plugins/devtools/src/components/Content/InfoContent/InfoDependenciesTable.tsx +++ b/plugins/devtools/src/components/Content/InfoContent/InfoDependenciesTable.tsx @@ -35,14 +35,12 @@ const columns: TableColumn[] = [ export const InfoDependenciesTable = ({ infoDependencies, - title, }: { - title: string; infoDependencies: PackageDependency[] | undefined; }) => { return (
Date: Tue, 5 Sep 2023 16:02:48 +0200 Subject: [PATCH 31/41] remove autodetect package Co-authored-by: Philipp Hugenroth Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- packages/autodetect/.eslintrc.js | 1 - packages/autodetect/README.md | 12 ---- packages/autodetect/api-report.md | 19 ------ packages/autodetect/package.json | 34 ---------- .../autodetect/src/autodetect-plugins.d.ts | 22 ------- packages/autodetect/src/index.ts | 65 ------------------- packages/autodetect/src/setupTests.ts | 16 ----- 7 files changed, 169 deletions(-) delete mode 100644 packages/autodetect/.eslintrc.js delete mode 100644 packages/autodetect/README.md delete mode 100644 packages/autodetect/api-report.md delete mode 100644 packages/autodetect/package.json delete mode 100644 packages/autodetect/src/autodetect-plugins.d.ts delete mode 100644 packages/autodetect/src/index.ts delete mode 100644 packages/autodetect/src/setupTests.ts diff --git a/packages/autodetect/.eslintrc.js b/packages/autodetect/.eslintrc.js deleted file mode 100644 index e2a53a6ad2..0000000000 --- a/packages/autodetect/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/packages/autodetect/README.md b/packages/autodetect/README.md deleted file mode 100644 index b5d48ff3df..0000000000 --- a/packages/autodetect/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# @backstage/autodetect - -_This package was created through the Backstage CLI_. - -## Installation - -Install the package via Yarn: - -```sh -cd # if within a monorepo -yarn add @backstage/autodetect -``` diff --git a/packages/autodetect/api-report.md b/packages/autodetect/api-report.md deleted file mode 100644 index 034dd3e8f0..0000000000 --- a/packages/autodetect/api-report.md +++ /dev/null @@ -1,19 +0,0 @@ -## API Report File for "@backstage/autodetect" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import { BackstagePlugin } from '@backstage/core-plugin-api'; - -// @public (undocumented) -export type DetectedPlugin = { - name: string; - plugin: BackstagePlugin; - components: Record; -}; - -// @public (undocumented) -export function getAvailablePlugins(): DetectedPlugin[]; - -// (No @packageDocumentation comment for this package) -``` diff --git a/packages/autodetect/package.json b/packages/autodetect/package.json deleted file mode 100644 index 4cd00c6f8a..0000000000 --- a/packages/autodetect/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "@backstage/autodetect", - "version": "0.0.0", - "main": "src/index.ts", - "types": "src/index.ts", - "license": "Apache-2.0", - "publishConfig": { - "access": "public", - "main": "dist/index.esm.js", - "types": "dist/index.d.ts" - }, - "backstage": { - "role": "web-library" - }, - "scripts": { - "start": "backstage-cli package start", - "build": "backstage-cli package build", - "lint": "backstage-cli package lint", - "test": "backstage-cli package test", - "clean": "backstage-cli package clean", - "prepack": "backstage-cli package prepack", - "postpack": "backstage-cli package postpack" - }, - "devDependencies": { - "@backstage/cli": "workspace:^", - "@testing-library/jest-dom": "^5.10.1" - }, - "files": [ - "dist" - ], - "dependencies": { - "@backstage/core-plugin-api": "workspace:^" - } -} diff --git a/packages/autodetect/src/autodetect-plugins.d.ts b/packages/autodetect/src/autodetect-plugins.d.ts deleted file mode 100644 index a5af550743..0000000000 --- a/packages/autodetect/src/autodetect-plugins.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2023 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. - */ -declare module '__backstage-autodetected-plugins__' { - type DetectedModule = { - name: string; - module: Record; - }; - const modules: Array<{ name: string; module: Record }>; -} diff --git a/packages/autodetect/src/index.ts b/packages/autodetect/src/index.ts deleted file mode 100644 index 67c3251290..0000000000 --- a/packages/autodetect/src/index.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2023 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 { BackstagePlugin } from '@backstage/core-plugin-api'; - -// eslint-disable-next-line @backstage/no-undeclared-imports -import { modules, DetectedModule } from '__backstage-autodetected-plugins__'; - -/** - * @public - */ -export type DetectedPlugin = { - name: string; - plugin: BackstagePlugin; - components: Record; -}; - -/** - * @public - */ -export function getAvailablePlugins(): DetectedPlugin[] { - return modules - .map(splitPluginFromComponents) - .filter((m): m is DetectedPlugin => !!m.plugin); -} - -function splitPluginFromComponents({ module, name }: DetectedModule) { - return Object.entries(module).reduce( - (acc, [k, v]) => { - if (!isBackstagePlugin(v)) { - acc.components[k] = v; - } else { - acc.plugin = v; - } - return acc; - }, - { name, components: {} } as { - name: string; - plugin?: BackstagePlugin; - components: Record; - }, - ); -} - -function isBackstagePlugin(obj: Record): obj is BackstagePlugin { - return ( - typeof obj.getId !== 'undefined' && - typeof obj.getApis !== 'undefined' && - typeof obj.getFeatureFlags !== 'undefined' && - typeof obj.provide !== 'undefined' - ); -} diff --git a/packages/autodetect/src/setupTests.ts b/packages/autodetect/src/setupTests.ts deleted file mode 100644 index 865308e634..0000000000 --- a/packages/autodetect/src/setupTests.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2023 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 '@testing-library/jest-dom'; From 8bdfb7a2f40c37207fdbe7bdade31786a4c51a35 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Sep 2023 16:03:09 +0200 Subject: [PATCH 32/41] frontend-app-api: implement package discovery Co-authored-by: Philipp Hugenroth Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- packages/frontend-app-api/src/createApp.tsx | 4 ++- .../src/discover/discover-plugins.d.ts | 22 ++++++++++++ .../frontend-app-api/src/discover/index.ts | 36 +++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 packages/frontend-app-api/src/discover/discover-plugins.d.ts create mode 100644 packages/frontend-app-api/src/discover/index.ts diff --git a/packages/frontend-app-api/src/createApp.tsx b/packages/frontend-app-api/src/createApp.tsx index 728610679d..988681e6a9 100644 --- a/packages/frontend-app-api/src/createApp.tsx +++ b/packages/frontend-app-api/src/createApp.tsx @@ -32,6 +32,7 @@ import { } from './wiring/parameters'; import { RoutingProvider } from './routing/RoutingContext'; import { RouteRef } from '@backstage/core-plugin-api'; +import { getAvailablePlugins } from './discover'; /** @public */ export function createApp(options: { plugins: BackstagePlugin[] }): { @@ -40,11 +41,12 @@ export function createApp(options: { plugins: BackstagePlugin[] }): { const appConfig = ConfigReader.fromConfigs(process.env.APP_CONFIG as any); const builtinExtensions = [CoreRouter]; + const discoveredPlugins = getAvailablePlugins(); // pull in default extension instance from discovered packages // apply config to adjust default extension instances and add more const extensionParams = mergeExtensionParameters({ - sources: options.plugins, + sources: [...options.plugins, ...discoveredPlugins], builtinExtensions, parameters: readAppExtensionParameters(appConfig), }); diff --git a/packages/frontend-app-api/src/discover/discover-plugins.d.ts b/packages/frontend-app-api/src/discover/discover-plugins.d.ts new file mode 100644 index 0000000000..a5af550743 --- /dev/null +++ b/packages/frontend-app-api/src/discover/discover-plugins.d.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 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. + */ +declare module '__backstage-autodetected-plugins__' { + type DetectedModule = { + name: string; + module: Record; + }; + const modules: Array<{ name: string; module: Record }>; +} diff --git a/packages/frontend-app-api/src/discover/index.ts b/packages/frontend-app-api/src/discover/index.ts new file mode 100644 index 0000000000..bde5242505 --- /dev/null +++ b/packages/frontend-app-api/src/discover/index.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2023 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 { BackstagePlugin } from '@backstage/frontend-plugin-api'; + +// eslint-disable-next-line @backstage/no-undeclared-imports +import { modules } from '__backstage-autodetected-plugins__'; + +/** + * @public + */ +export function getAvailablePlugins(): BackstagePlugin[] { + return modules.flatMap(({ module: mod }) => + Object.values(mod).flatMap(val => (isBackstagePlugin(val) ? [val] : [])), + ); +} + +function isBackstagePlugin(obj: unknown): obj is BackstagePlugin { + if (obj !== null && typeof obj === 'object' && '$$type' in obj) { + return obj.$$type === 'backstage-plugin'; + } + return false; +} From a44803ad8afced3bd443a24c73978b6119336bfa Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Sep 2023 16:03:31 +0200 Subject: [PATCH 33/41] app-next: complete package discovery example Co-authored-by: Philipp Hugenroth Co-authored-by: Johan Haals Signed-off-by: Patrik Oldsberg --- packages/app-next-example-plugin/package.json | 4 +-- .../app-next-example-plugin/src/Component.tsx | 2 +- packages/app-next-example-plugin/src/index.ts | 27 +++++++++++++++++++ .../app-next-example-plugin/src/plugin.tsx | 4 +-- packages/app-next/app-config.yaml | 4 ++- packages/app-next/package.json | 1 + 6 files changed, 36 insertions(+), 6 deletions(-) diff --git a/packages/app-next-example-plugin/package.json b/packages/app-next-example-plugin/package.json index 2838cc097a..62eeaba08b 100644 --- a/packages/app-next-example-plugin/package.json +++ b/packages/app-next-example-plugin/package.json @@ -1,5 +1,5 @@ { - "name": "@backstage/app-next-example-plugin", + "name": "app-next-example-plugin", "description": "Backstage internal example plugin", "version": "0.0.0", "publishConfig": { @@ -33,8 +33,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/frontend-plugin-api": "workspace:^", "@backstage/core-components": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "@material-ui/icons": "^4.9.1" }, "peerDependencies": { diff --git a/packages/app-next-example-plugin/src/Component.tsx b/packages/app-next-example-plugin/src/Component.tsx index 2cca17c88c..e90be162ed 100644 --- a/packages/app-next-example-plugin/src/Component.tsx +++ b/packages/app-next-example-plugin/src/Component.tsx @@ -18,6 +18,6 @@ import React from 'react'; export const Component = () => (
-

Winning

+

SHIP IT!

); diff --git a/packages/app-next-example-plugin/src/index.ts b/packages/app-next-example-plugin/src/index.ts index e69de29bb2..94858a71a5 100644 --- a/packages/app-next-example-plugin/src/index.ts +++ b/packages/app-next-example-plugin/src/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2023 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 { examplePlugin } from './plugin'; +// TODO: This should be an extension created & exported in the `plugin.tsx` +// YEAHHH most likely but there's no api(output) for it yet. +export { ExampleSidebarItem } from './ExampleSidebarItem'; + +export const a1 = null; +export const a2 = false; +export const a3 = true; +export const a4 = Symbol('b'); +export const a5 = 3; +export const a6 = []; diff --git a/packages/app-next-example-plugin/src/plugin.tsx b/packages/app-next-example-plugin/src/plugin.tsx index 85d2dd4cc9..4112d1de4e 100644 --- a/packages/app-next-example-plugin/src/plugin.tsx +++ b/packages/app-next-example-plugin/src/plugin.tsx @@ -14,16 +14,16 @@ * limitations under the License. */ +import React from 'react'; import { createPageExtension, createPlugin, } from '@backstage/frontend-plugin-api'; -import { Component } from './Component'; export const ExamplePage = createPageExtension({ id: 'example.page', defaultPath: '/example', - component: () => () => Component, + component: () => import('./Component').then(m => ), }); export const examplePlugin = createPlugin({ diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 3d29f19c13..182cda75e7 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -1,5 +1,7 @@ app: - packages: 'all' # ✨ + experimental: + # packages: 'all' # ✨ + packages: 'all' # scmAuthExtension: >- diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 013c7a3d5c..46be6c1bc1 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -86,6 +86,7 @@ "@roadiehq/backstage-plugin-github-insights": "^2.0.5", "@roadiehq/backstage-plugin-github-pull-requests": "^2.2.7", "@roadiehq/backstage-plugin-travis-ci": "^2.0.5", + "app-next-example-plugin": "workspace:^", "history": "^5.0.0", "lodash": "^4.17.21", "prop-types": "^15.7.2", From 1e5238ceae4ba9cd59768e81c74e32e200c27b0b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Sep 2023 16:03:55 +0200 Subject: [PATCH 34/41] update yarn.lock Signed-off-by: Patrik Oldsberg --- yarn.lock | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/yarn.lock b/yarn.lock index d914859dd7..3812cd2fd6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3377,16 +3377,6 @@ __metadata: languageName: unknown linkType: soft -"@backstage/autodetect@workspace:^, @backstage/autodetect@workspace:packages/autodetect": - version: 0.0.0-use.local - resolution: "@backstage/autodetect@workspace:packages/autodetect" - dependencies: - "@backstage/cli": "workspace:^" - "@backstage/core-plugin-api": "workspace:^" - "@testing-library/jest-dom": ^5.10.1 - languageName: unknown - linkType: soft - "@backstage/backend-app-api@workspace:^, @backstage/backend-app-api@workspace:packages/backend-app-api": version: 0.0.0-use.local resolution: "@backstage/backend-app-api@workspace:packages/backend-app-api" @@ -6438,7 +6428,6 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-devtools@workspace:plugins/devtools" dependencies: - "@backstage/autodetect": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/core-app-api": "workspace:^" "@backstage/core-components": "workspace:^" @@ -19747,6 +19736,23 @@ __metadata: languageName: node linkType: hard +"app-next-example-plugin@workspace:^, app-next-example-plugin@workspace:packages/app-next-example-plugin": + version: 0.0.0-use.local + resolution: "app-next-example-plugin@workspace:packages/app-next-example-plugin" + dependencies: + "@backstage/cli": "workspace:^" + "@backstage/core-components": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" + "@material-ui/icons": ^4.9.1 + cross-fetch: ^3.1.5 + msw: ^1.0.0 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + languageName: unknown + linkType: soft + "append-field@npm:^1.0.0": version: 1.0.0 resolution: "append-field@npm:1.0.0" @@ -25632,6 +25638,7 @@ __metadata: "@types/react": "*" "@types/react-dom": "*" "@types/zen-observable": ^0.8.0 + app-next-example-plugin: "workspace:^" cross-env: ^7.0.0 cypress: ^10.0.0 eslint-plugin-cypress: ^2.10.3 From e036ff862efae283303f74be3b1e12da2f81c50a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Sep 2023 16:24:58 +0200 Subject: [PATCH 35/41] cli: switch package discovery to use global Signed-off-by: Patrik Oldsberg --- packages/cli/src/lib/bundler/packageDetection.ts | 2 +- packages/frontend-app-api/src/discover/index.ts | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/lib/bundler/packageDetection.ts b/packages/cli/src/lib/bundler/packageDetection.ts index 957eb87715..bcc10a358b 100644 --- a/packages/cli/src/lib/bundler/packageDetection.ts +++ b/packages/cli/src/lib/bundler/packageDetection.ts @@ -96,7 +96,7 @@ async function writeDetectedPackagesModule(packageNames: string[]) { 'node_modules', `${DETECTED_MODULES_MODULE_NAME}.js`, ), - `export const modules = [${requirePackageScript}];`, + `window['__@backstage/discovered__'] = { modules: [${requirePackageScript}] };`, ); } diff --git a/packages/frontend-app-api/src/discover/index.ts b/packages/frontend-app-api/src/discover/index.ts index bde5242505..5ef15f77cb 100644 --- a/packages/frontend-app-api/src/discover/index.ts +++ b/packages/frontend-app-api/src/discover/index.ts @@ -16,15 +16,22 @@ import { BackstagePlugin } from '@backstage/frontend-plugin-api'; -// eslint-disable-next-line @backstage/no-undeclared-imports -import { modules } from '__backstage-autodetected-plugins__'; +interface DiscoveryGlobal { + modules: Array<{ name: string; module: object }>; +} /** * @public */ export function getAvailablePlugins(): BackstagePlugin[] { - return modules.flatMap(({ module: mod }) => - Object.values(mod).flatMap(val => (isBackstagePlugin(val) ? [val] : [])), + const discovered = ( + window as { '__@backstage/discovered__'?: DiscoveryGlobal } + )['__@backstage/discovered__']; + + return ( + discovered?.modules.flatMap(({ module: mod }) => + Object.values(mod).flatMap(val => (isBackstagePlugin(val) ? [val] : [])), + ) ?? [] ); } From f36113ca230531424a24356a3538047890eefaec Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Sep 2023 16:27:13 +0200 Subject: [PATCH 36/41] changesets: add changeset for package discovery Signed-off-by: Patrik Oldsberg --- .changeset/light-drinks-rush.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/light-drinks-rush.md diff --git a/.changeset/light-drinks-rush.md b/.changeset/light-drinks-rush.md new file mode 100644 index 0000000000..08383ab089 --- /dev/null +++ b/.changeset/light-drinks-rush.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Add experimental support for frontend package discovery. From 89f03fa0447e4d24e025127e0c770319f50e9ad3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Sep 2023 16:42:05 +0200 Subject: [PATCH 37/41] cli,frontend-app-api: cleanup Signed-off-by: Patrik Oldsberg --- packages/app-next/app-config.yaml | 4 +--- packages/cli/package.json | 1 - packages/cli/src/lib/bundler/server.ts | 2 -- packages/cli/src/lib/bundler/types.ts | 1 - packages/frontend-app-api/src/createApp.tsx | 2 +- .../src/discover/discover-plugins.d.ts | 22 ------------------- .../index.ts => wiring/discovery.ts} | 0 yarn.lock | 8 ------- 8 files changed, 2 insertions(+), 38 deletions(-) delete mode 100644 packages/frontend-app-api/src/discover/discover-plugins.d.ts rename packages/frontend-app-api/src/{discover/index.ts => wiring/discovery.ts} (100%) diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 182cda75e7..c798420c39 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -1,8 +1,6 @@ app: experimental: - # packages: 'all' # ✨ - packages: 'all' - + packages: 'all' # ✨ # scmAuthExtension: >- # createScmAuthExtension({ diff --git a/packages/cli/package.json b/packages/cli/package.json index 7889f3ab17..5999a55fc1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -133,7 +133,6 @@ "webpack": "^5.70.0", "webpack-dev-server": "^4.7.3", "webpack-node-externals": "^3.0.0", - "webpack-virtual-modules": "^0.5.0", "yaml": "^2.0.0", "yml-loader": "^2.1.0", "yn": "^4.0.0", diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index f566c268f5..afd7a015bf 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -109,7 +109,6 @@ export async function serveBundle(options: ServeOptions) { } const { frontendConfig, fullConfig } = cliConfig; - // TODO: proper const url = resolveBaseUrl(frontendConfig); const host = @@ -133,7 +132,6 @@ export async function serveBundle(options: ServeOptions) { isDev: true, baseUrl: url, frontendConfig, - frontendAppConfigs: [], getFrontendAppConfigs: () => { return latestFrontendAppConfigs; }, diff --git a/packages/cli/src/lib/bundler/types.ts b/packages/cli/src/lib/bundler/types.ts index 67cdd80531..414414fa81 100644 --- a/packages/cli/src/lib/bundler/types.ts +++ b/packages/cli/src/lib/bundler/types.ts @@ -23,7 +23,6 @@ export type BundlingOptions = { isDev: boolean; frontendConfig: Config; getFrontendAppConfigs(): AppConfig[]; - frontendAppConfigs: AppConfig[]; baseUrl: URL; parallelism?: number; additionalEntryPoints?: string[]; diff --git a/packages/frontend-app-api/src/createApp.tsx b/packages/frontend-app-api/src/createApp.tsx index 988681e6a9..c50b507642 100644 --- a/packages/frontend-app-api/src/createApp.tsx +++ b/packages/frontend-app-api/src/createApp.tsx @@ -32,7 +32,7 @@ import { } from './wiring/parameters'; import { RoutingProvider } from './routing/RoutingContext'; import { RouteRef } from '@backstage/core-plugin-api'; -import { getAvailablePlugins } from './discover'; +import { getAvailablePlugins } from './wiring/discovery'; /** @public */ export function createApp(options: { plugins: BackstagePlugin[] }): { diff --git a/packages/frontend-app-api/src/discover/discover-plugins.d.ts b/packages/frontend-app-api/src/discover/discover-plugins.d.ts deleted file mode 100644 index a5af550743..0000000000 --- a/packages/frontend-app-api/src/discover/discover-plugins.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2023 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. - */ -declare module '__backstage-autodetected-plugins__' { - type DetectedModule = { - name: string; - module: Record; - }; - const modules: Array<{ name: string; module: Record }>; -} diff --git a/packages/frontend-app-api/src/discover/index.ts b/packages/frontend-app-api/src/wiring/discovery.ts similarity index 100% rename from packages/frontend-app-api/src/discover/index.ts rename to packages/frontend-app-api/src/wiring/discovery.ts diff --git a/yarn.lock b/yarn.lock index 3812cd2fd6..3e6f96ef56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3882,7 +3882,6 @@ __metadata: webpack: ^5.70.0 webpack-dev-server: ^4.7.3 webpack-node-externals: ^3.0.0 - webpack-virtual-modules: ^0.5.0 yaml: ^2.0.0 yml-loader: ^2.1.0 yn: ^4.0.0 @@ -42820,13 +42819,6 @@ __metadata: languageName: node linkType: hard -"webpack-virtual-modules@npm:^0.5.0": - version: 0.5.0 - resolution: "webpack-virtual-modules@npm:0.5.0" - checksum: 22b59257b55c89d11ae295b588b683ee9fdf3aeb591bc7b6f88ac1d69cb63f4fcb507666ea986866dfae161a1fa534ad6fb4e2ea91bbcd0a6d454368d7d4c64b - languageName: node - linkType: hard - "webpack@npm:^5, webpack@npm:^5.70.0": version: 5.88.2 resolution: "webpack@npm:5.88.2" From 888748f10b54984e2710020dd1f16a0fa32e48dc Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Sep 2023 16:58:37 +0200 Subject: [PATCH 38/41] frontend-app-api: add discovery tests Signed-off-by: Patrik Oldsberg --- packages/app-next-example-plugin/src/index.ts | 11 +- .../src/wiring/discovery.test.ts | 115 ++++++++++++++++++ 2 files changed, 117 insertions(+), 9 deletions(-) create mode 100644 packages/frontend-app-api/src/wiring/discovery.test.ts diff --git a/packages/app-next-example-plugin/src/index.ts b/packages/app-next-example-plugin/src/index.ts index 94858a71a5..dae1eea379 100644 --- a/packages/app-next-example-plugin/src/index.ts +++ b/packages/app-next-example-plugin/src/index.ts @@ -15,13 +15,6 @@ */ export { examplePlugin } from './plugin'; -// TODO: This should be an extension created & exported in the `plugin.tsx` -// YEAHHH most likely but there's no api(output) for it yet. -export { ExampleSidebarItem } from './ExampleSidebarItem'; -export const a1 = null; -export const a2 = false; -export const a3 = true; -export const a4 = Symbol('b'); -export const a5 = 3; -export const a6 = []; +// TODO: This should be an extension created & exported in the `plugin.tsx` +export { ExampleSidebarItem } from './ExampleSidebarItem'; diff --git a/packages/frontend-app-api/src/wiring/discovery.test.ts b/packages/frontend-app-api/src/wiring/discovery.test.ts new file mode 100644 index 0000000000..249f9dcea9 --- /dev/null +++ b/packages/frontend-app-api/src/wiring/discovery.test.ts @@ -0,0 +1,115 @@ +/* + * Copyright 2023 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 { createPlugin } from '@backstage/frontend-plugin-api'; +import { getAvailablePlugins } from './discovery'; + +const globalSpy = jest.fn(); +Object.defineProperty(global, '__@backstage/discovered__', { + get: globalSpy, +}); + +describe('getAvailablePlugins', () => { + afterEach(jest.resetAllMocks); + + it('should discover nothing with undefined global', () => { + expect(getAvailablePlugins()).toEqual([]); + }); + + it('should discover nothing with empty global', () => { + globalSpy.mockReturnValue({ + modules: [], + }); + expect(getAvailablePlugins()).toEqual([]); + }); + + it('should discover a plugin', () => { + const testPlugin = createPlugin({ id: 'test' }); + globalSpy.mockReturnValue({ + modules: [ + { + module: { + testPlugin, + }, + }, + ], + }); + expect(getAvailablePlugins()).toEqual([testPlugin]); + }); + + it('should ignore garbage', () => { + const testPlugin = createPlugin({ id: 'test' }); + globalSpy.mockReturnValue({ + modules: [ + { + module: { + testPlugin, + a: 1, + b: null, + c: undefined, + d: Symbol('wat'), + e: () => {}, + f: [], + g: {}, + h: 'h', + i: NaN, + j: Infinity, + k: -Infinity, + l: new Date(), + m: new RegExp('wat'), + n: new Error('wat'), + o: new Map(), + p: new Set(), + q: new WeakMap(), + r: new WeakSet(), + s: new ArrayBuffer(1), + t: new DataView(new ArrayBuffer(1)), + u: false, + v: true, + w: 0, + }, + }, + ], + }); + expect(getAvailablePlugins()).toEqual([testPlugin]); + }); + + it('should discover multiple plugins', () => { + const test1Plugin = createPlugin({ id: 'test1' }); + const test2Plugin = createPlugin({ id: 'test2' }); + const test3Plugin = createPlugin({ id: 'test3' }); + globalSpy.mockReturnValue({ + modules: [ + { + module: { + test1Plugin, + test2Plugin, + }, + }, + { + module: { + test3Plugin, + }, + }, + ], + }); + expect(getAvailablePlugins()).toEqual([ + test1Plugin, + test2Plugin, + test3Plugin, + ]); + }); +}); From 3f49724db809c81b6a6f53772d7ca10e534477fd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 5 Sep 2023 18:07:27 +0200 Subject: [PATCH 39/41] cli,frontend-app-api: more cleanup Signed-off-by: Patrik Oldsberg --- packages/app-next/app-config.yaml | 1 + packages/cli/src/lib/bundler/bundle.ts | 4 ++-- packages/cli/src/lib/bundler/packageDetection.ts | 4 ++-- packages/cli/src/lib/bundler/server.ts | 4 +++- packages/frontend-app-api/src/wiring/discovery.test.ts | 7 +++++-- packages/frontend-app-api/src/wiring/discovery.ts | 2 +- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index c798420c39..c94d900b21 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -2,6 +2,7 @@ app: experimental: packages: 'all' # ✨ + # scmAuthExtension: >- # createScmAuthExtension({ # id: 'apis.scmAuth.addons.ghe', diff --git a/packages/cli/src/lib/bundler/bundle.ts b/packages/cli/src/lib/bundler/bundle.ts index 11330df5b2..11e18320f1 100644 --- a/packages/cli/src/lib/bundler/bundle.ts +++ b/packages/cli/src/lib/bundler/bundle.ts @@ -27,7 +27,7 @@ import { createConfig, resolveBaseUrl } from './config'; import { BuildOptions } from './types'; import { resolveBundlingPaths } from './paths'; import chalk from 'chalk'; -import { createDetectedModulesEntrypoint } from './packageDetection'; +import { createDetectedModulesEntryPoint } from './packageDetection'; // TODO(Rugvip): Limits from CRA, we might want to tweak these though. const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; @@ -42,7 +42,7 @@ export async function buildBundle(options: BuildOptions) { const paths = resolveBundlingPaths(options); - const detectedModulesEntryPoint = await createDetectedModulesEntrypoint({ + const detectedModulesEntryPoint = await createDetectedModulesEntryPoint({ config: options.fullConfig, targetPath: paths.targetPath, }); diff --git a/packages/cli/src/lib/bundler/packageDetection.ts b/packages/cli/src/lib/bundler/packageDetection.ts index bcc10a358b..293ef74f81 100644 --- a/packages/cli/src/lib/bundler/packageDetection.ts +++ b/packages/cli/src/lib/bundler/packageDetection.ts @@ -32,7 +32,7 @@ function readPackageDetectionConfig( config: Config, ): PackageDetectionConfig | undefined { const packages = config.getOptional('app.experimental.packages'); - if (!packages) { + if (packages === undefined || packages === null) { return undefined; } @@ -100,7 +100,7 @@ async function writeDetectedPackagesModule(packageNames: string[]) { ); } -export async function createDetectedModulesEntrypoint(options: { +export async function createDetectedModulesEntryPoint(options: { config: Config; targetPath: string; watch?: () => void; diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index afd7a015bf..78f6bb7823 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -31,12 +31,13 @@ import { paths as libPaths } from '../../lib/paths'; import { loadCliConfig } from '../config'; import { Lockfile } from '../versioning'; import { createConfig, resolveBaseUrl } from './config'; -import { createDetectedModulesEntrypoint as createDetectedModulesEntryPoint } from './packageDetection'; +import { createDetectedModulesEntryPoint } from './packageDetection'; import { resolveBundlingPaths } from './paths'; import { ServeOptions } from './types'; export async function serveBundle(options: ServeOptions) { const paths = resolveBundlingPaths(options); + const targetPkg = await fs.readJson(paths.targetPackageJson); if (options.verifyVersions) { const lockfile = await Lockfile.load( @@ -170,6 +171,7 @@ export async function serveBundle(options: ServeOptions) { : false, host, port, + proxy: targetPkg.proxy, // When the dev server is behind a proxy, the host and public hostname differ allowedHosts: [url.hostname], client: { diff --git a/packages/frontend-app-api/src/wiring/discovery.test.ts b/packages/frontend-app-api/src/wiring/discovery.test.ts index 249f9dcea9..a93f853c1b 100644 --- a/packages/frontend-app-api/src/wiring/discovery.test.ts +++ b/packages/frontend-app-api/src/wiring/discovery.test.ts @@ -57,14 +57,14 @@ describe('getAvailablePlugins', () => { { module: { testPlugin, - a: 1, + a: 'a', b: null, c: undefined, d: Symbol('wat'), e: () => {}, f: [], g: {}, - h: 'h', + h: class {}, i: NaN, j: Infinity, k: -Infinity, @@ -80,6 +80,9 @@ describe('getAvailablePlugins', () => { u: false, v: true, w: 0, + x: 1, + y: -1, + z: '', }, }, ], diff --git a/packages/frontend-app-api/src/wiring/discovery.ts b/packages/frontend-app-api/src/wiring/discovery.ts index 5ef15f77cb..7b0f4a50e4 100644 --- a/packages/frontend-app-api/src/wiring/discovery.ts +++ b/packages/frontend-app-api/src/wiring/discovery.ts @@ -30,7 +30,7 @@ export function getAvailablePlugins(): BackstagePlugin[] { return ( discovered?.modules.flatMap(({ module: mod }) => - Object.values(mod).flatMap(val => (isBackstagePlugin(val) ? [val] : [])), + Object.values(mod).filter(isBackstagePlugin), ) ?? [] ); } From c2c07a1c0217070c9802f99fcfbb45a64d6ab9ee Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 6 Sep 2023 10:28:35 +0200 Subject: [PATCH 40/41] app-next-example-plugin: api report + fixes Signed-off-by: Patrik Oldsberg --- .../app-next-example-plugin/api-report.md | 20 +++++++++++++++++++ .../src/ExampleSidebarItem.tsx | 1 + .../app-next-example-plugin/src/plugin.tsx | 1 + 3 files changed, 22 insertions(+) create mode 100644 packages/app-next-example-plugin/api-report.md diff --git a/packages/app-next-example-plugin/api-report.md b/packages/app-next-example-plugin/api-report.md new file mode 100644 index 0000000000..1963e69be4 --- /dev/null +++ b/packages/app-next-example-plugin/api-report.md @@ -0,0 +1,20 @@ +## API Report File for "app-next-example-plugin" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackstagePlugin } from '@backstage/frontend-plugin-api'; +import { default as React_2 } from 'react'; + +// Warning: (ae-missing-release-tag) "examplePlugin" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const examplePlugin: BackstagePlugin; + +// Warning: (ae-missing-release-tag) "ExampleSidebarItem" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const ExampleSidebarItem: () => React_2.JSX.Element; + +// (No @packageDocumentation comment for this package) +``` diff --git a/packages/app-next-example-plugin/src/ExampleSidebarItem.tsx b/packages/app-next-example-plugin/src/ExampleSidebarItem.tsx index 6d8bedc509..20e84f1933 100644 --- a/packages/app-next-example-plugin/src/ExampleSidebarItem.tsx +++ b/packages/app-next-example-plugin/src/ExampleSidebarItem.tsx @@ -18,6 +18,7 @@ import React from 'react'; import { SidebarItem } from '@backstage/core-components'; import SaveIcon from '@material-ui/icons/Save'; +/** @public */ export const ExampleSidebarItem = () => ( ); diff --git a/packages/app-next-example-plugin/src/plugin.tsx b/packages/app-next-example-plugin/src/plugin.tsx index 4112d1de4e..5b5adeab2f 100644 --- a/packages/app-next-example-plugin/src/plugin.tsx +++ b/packages/app-next-example-plugin/src/plugin.tsx @@ -26,6 +26,7 @@ export const ExamplePage = createPageExtension({ component: () => import('./Component').then(m => ), }); +/** @public */ export const examplePlugin = createPlugin({ id: 'example', extensions: [ExamplePage], From a8fbc03f5e50fd7ebd93e3eb02db454b3ef2507d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 6 Sep 2023 12:28:52 +0200 Subject: [PATCH 41/41] Update api-report.md yolo Signed-off-by: Patrik Oldsberg --- packages/app-next-example-plugin/api-report.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/app-next-example-plugin/api-report.md b/packages/app-next-example-plugin/api-report.md index 1963e69be4..08e283b879 100644 --- a/packages/app-next-example-plugin/api-report.md +++ b/packages/app-next-example-plugin/api-report.md @@ -6,13 +6,9 @@ import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { default as React_2 } from 'react'; -// Warning: (ae-missing-release-tag) "examplePlugin" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export const examplePlugin: BackstagePlugin; -// Warning: (ae-missing-release-tag) "ExampleSidebarItem" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export const ExampleSidebarItem: () => React_2.JSX.Element;