From 520a383e5e86d4ad3dd85c604dca2e0060ce0f77 Mon Sep 17 00:00:00 2001 From: Harrison Hogg Date: Fri, 16 Aug 2024 16:20:03 +0100 Subject: [PATCH] Added step to backstage-cli repo fix --publish command Added a fixer step to the backstage-cli fix command that will annotate default export features to the package json backstage metadata Signed-off-by: Harrison Hogg --- .changeset/clever-paws-stare.md | 6 + .changeset/healthy-moose-tease.md | 63 ++++ packages/cli-node/api-report.md | 59 ++++ .../cli-node/src/monorepo/PackageGraph.ts | 63 ++++ packages/cli-node/src/monorepo/index.ts | 4 + packages/cli/package.json | 1 + packages/cli/src/commands/repo/fix.ts | 54 +++- packages/cli/src/lib/features.test.ts | 273 ++++++++++++++++++ packages/cli/src/lib/features.ts | 163 +++++++++++ .../cli/src/tests/createFeatureEnvironment.ts | 196 +++++++++++++ plugins/api-docs/package.json | 6 + plugins/app-backend/package.json | 6 + .../catalog-backend-module-aws/package.json | 8 +- .../catalog-backend-module-azure/package.json | 8 +- .../package.json | 8 +- .../package.json | 8 +- .../catalog-backend-module-gcp/package.json | 7 +- .../package.json | 8 +- .../package.json | 8 +- .../package.json | 8 +- .../package.json | 8 +- .../package.json | 8 +- .../package.json | 8 +- .../package.json | 7 +- plugins/catalog-backend/package.json | 6 + plugins/catalog-graph/package.json | 6 + plugins/catalog-import/package.json | 6 + plugins/catalog/package.json | 6 + plugins/devtools/package.json | 6 + .../package.json | 8 +- .../events-backend-module-azure/package.json | 8 +- .../package.json | 8 +- .../events-backend-module-gerrit/package.json | 8 +- plugins/events-backend/package.json | 6 + plugins/home/package.json | 6 + plugins/kubernetes-backend/package.json | 6 + plugins/kubernetes/package.json | 6 + plugins/org/package.json | 6 + plugins/permission-backend/package.json | 6 + plugins/proxy-backend/package.json | 6 + .../package.json | 7 +- .../package.json | 7 +- .../package.json | 7 +- .../package.json | 7 +- .../package.json | 7 +- .../package.json | 7 +- .../package.json | 7 +- .../package.json | 7 +- .../package.json | 7 +- .../package.json | 7 +- .../package.json | 7 +- .../package.json | 7 +- .../package.json | 7 +- .../package.json | 7 +- plugins/scaffolder-backend/package.json | 6 + plugins/scaffolder/package.json | 6 + .../package.json | 8 +- .../package.json | 8 +- .../package.json | 8 +- plugins/search-backend-module-pg/package.json | 8 +- .../package.json | 8 +- plugins/search-backend/package.json | 6 + plugins/search/package.json | 6 + plugins/techdocs-backend/package.json | 6 + plugins/techdocs/package.json | 6 + plugins/user-settings-backend/package.json | 6 + plugins/user-settings/package.json | 6 + yarn.lock | 1 + 68 files changed, 1242 insertions(+), 37 deletions(-) create mode 100644 .changeset/clever-paws-stare.md create mode 100644 .changeset/healthy-moose-tease.md create mode 100644 packages/cli/src/lib/features.test.ts create mode 100644 packages/cli/src/lib/features.ts create mode 100644 packages/cli/src/tests/createFeatureEnvironment.ts diff --git a/.changeset/clever-paws-stare.md b/.changeset/clever-paws-stare.md new file mode 100644 index 0000000000..b3cb421723 --- /dev/null +++ b/.changeset/clever-paws-stare.md @@ -0,0 +1,6 @@ +--- +'@backstage/cli-node': patch +'@backstage/cli': patch +--- + +Added a new step to the `backstage-cli repo fix --publish` command that will annotate default export features to the 'backstage' metadata within plugin package.json. This is to help with identifying the declarative integration points for plugins without needing to fetch or run the plugins first. diff --git a/.changeset/healthy-moose-tease.md b/.changeset/healthy-moose-tease.md new file mode 100644 index 0000000000..1f9ba3952f --- /dev/null +++ b/.changeset/healthy-moose-tease.md @@ -0,0 +1,63 @@ +--- +'@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': patch +'@backstage/plugin-catalog-backend-module-scaffolder-entity-model': patch +'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch +'@backstage/plugin-scaffolder-backend-module-bitbucket-server': patch +'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': patch +'@backstage/plugin-catalog-backend-module-bitbucket-server': patch +'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch +'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch +'@backstage/plugin-events-backend-module-bitbucket-cloud': patch +'@backstage/plugin-scaffolder-backend-module-bitbucket': patch +'@backstage/plugin-search-backend-module-elasticsearch': patch +'@backstage/plugin-scaffolder-backend-module-gerrit': patch +'@backstage/plugin-scaffolder-backend-module-github': patch +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +'@backstage/plugin-scaffolder-backend-module-sentry': patch +'@backstage/plugin-scaffolder-backend-module-yeoman': patch +'@backstage/plugin-catalog-backend-module-puppetdb': patch +'@backstage/plugin-scaffolder-backend-module-azure': patch +'@backstage/plugin-scaffolder-backend-module-gitea': patch +'@backstage/plugin-scaffolder-backend-module-rails': patch +'@backstage/plugin-catalog-backend-module-msgraph': patch +'@backstage/plugin-search-backend-module-techdocs': patch +'@backstage/plugin-catalog-backend-module-gerrit': patch +'@backstage/plugin-catalog-backend-module-github': patch +'@backstage/plugin-catalog-backend-module-gitlab': patch +'@backstage/plugin-events-backend-module-aws-sqs': patch +'@backstage/plugin-scaffolder-backend-module-gcp': patch +'@backstage/plugin-search-backend-module-catalog': patch +'@backstage/plugin-search-backend-module-explore': patch +'@backstage/plugin-catalog-backend-module-azure': patch +'@backstage/plugin-events-backend-module-gerrit': patch +'@backstage/plugin-events-backend-module-github': patch +'@backstage/plugin-events-backend-module-gitlab': patch +'@backstage/plugin-events-backend-module-azure': patch +'@backstage/plugin-catalog-backend-module-aws': patch +'@backstage/plugin-catalog-backend-module-gcp': patch +'@backstage/plugin-search-backend-module-pg': patch +'@backstage/plugin-user-settings-backend': patch +'@backstage/plugin-kubernetes-backend': patch +'@backstage/plugin-permission-backend': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-techdocs-backend': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-catalog-import': patch +'@backstage/plugin-events-backend': patch +'@backstage/plugin-search-backend': patch +'@backstage/plugin-catalog-graph': patch +'@backstage/plugin-proxy-backend': patch +'@backstage/plugin-user-settings': patch +'@backstage/plugin-app-backend': patch +'@backstage/plugin-kubernetes': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-api-docs': patch +'@backstage/plugin-devtools': patch +'@backstage/plugin-techdocs': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-search': patch +'@backstage/plugin-home': patch +'@backstage/plugin-org': patch +--- + +Added `features` metadata to Package JSON diff --git a/packages/cli-node/api-report.md b/packages/cli-node/api-report.md index f22583dd25..519fe12bf9 100644 --- a/packages/cli-node/api-report.md +++ b/packages/cli-node/api-report.md @@ -12,6 +12,15 @@ export type BackstagePackage = { packageJson: BackstagePackageJson; }; +// @public +export type BackstagePackageFeature = { + path?: string; + type: BackstagePackageFeatureType; +}; + +// @public +export type BackstagePackageFeatureType = (typeof packageFeatureTypes)[number]; + // @public export interface BackstagePackageJson { // (undocumented) @@ -22,6 +31,7 @@ export interface BackstagePackageJson { pluginId?: string | null; pluginPackage?: string; pluginPackages?: string[]; + features?: BackstagePackageFeature[]; }; // (undocumented) bundled?: boolean; @@ -88,6 +98,31 @@ export class GitUtils { // @public export function isMonoRepo(): Promise; +// @public +export const isValidPackageFeatureType: ( + value: string, +) => value is + | '@backstage/TranslationResource' + | '@backstage/TranslationMessages' + | '@backstage/TranslationRef' + | '@backstage/RouteRef' + | '@backstage/SubRouteRef' + | '@backstage/ExternalRouteRef' + | '@backstage/ExtensionDataValue' + | '@backstage/ExtensionDataRef' + | '@backstage/ExtensionInput' + | '@backstage/ExtensionDefinition' + | '@backstage/Extension' + | '@backstage/ExtensionOverrides' + | '@backstage/FrontendPlugin' + | '@backstage/BackstagePlugin' + | '@backstage/FrontendModule' + | '@backstage/BackendFeatureFactory' + | '@backstage/BackendFeature' + | '@backstage/ServiceRef' + | '@backstage/BackstageCredentials' + | '@backstage/ExtensionPoint'; + // @public export class Lockfile { createSimplifiedDependencyGraph(): Map>; @@ -109,6 +144,30 @@ export type LockfileDiffEntry = { range: string; }; +// @public +export const packageFeatureTypes: readonly [ + '@backstage/BackendFeature', + '@backstage/BackendFeatureFactory', + '@backstage/BackstageCredentials', + '@backstage/BackstagePlugin', + '@backstage/Extension', + '@backstage/ExtensionDataRef', + '@backstage/ExtensionDataValue', + '@backstage/ExtensionDefinition', + '@backstage/ExtensionInput', + '@backstage/ExtensionOverrides', + '@backstage/ExtensionPoint', + '@backstage/ExternalRouteRef', + '@backstage/FrontendPlugin', + '@backstage/FrontendModule', + '@backstage/RouteRef', + '@backstage/ServiceRef', + '@backstage/SubRouteRef', + '@backstage/TranslationMessages', + '@backstage/TranslationRef', + '@backstage/TranslationResource', +]; + // @public export class PackageGraph extends Map { collectPackageNames( diff --git a/packages/cli-node/src/monorepo/PackageGraph.ts b/packages/cli-node/src/monorepo/PackageGraph.ts index 69ec99c2dd..374be3add7 100644 --- a/packages/cli-node/src/monorepo/PackageGraph.ts +++ b/packages/cli-node/src/monorepo/PackageGraph.ts @@ -22,6 +22,64 @@ import { GitUtils } from '../git'; import { Lockfile } from './Lockfile'; import { JsonValue } from '@backstage/types'; +/** + * All Backstage system features + * + * @public + */ +export const packageFeatureTypes = [ + '@backstage/BackendFeature', + '@backstage/BackendFeatureFactory', + '@backstage/BackstageCredentials', + '@backstage/BackstagePlugin', + '@backstage/Extension', + '@backstage/ExtensionDataRef', + '@backstage/ExtensionDataValue', + '@backstage/ExtensionDefinition', + '@backstage/ExtensionInput', + '@backstage/ExtensionOverrides', + '@backstage/ExtensionPoint', + '@backstage/ExternalRouteRef', + '@backstage/FrontendPlugin', + '@backstage/FrontendModule', + '@backstage/RouteRef', + '@backstage/ServiceRef', + '@backstage/SubRouteRef', + '@backstage/TranslationMessages', + '@backstage/TranslationRef', + '@backstage/TranslationResource', +] as const; + +/** + * Checks if a string is a valid package feature type. + * + * @public + */ +export const isValidPackageFeatureType = ( + value: string, +): value is BackstagePackageFeatureType => { + return packageFeatureTypes.includes(value as any); +}; + +/** + * The type of a Backstage system feature + * + * @public + */ +export type BackstagePackageFeatureType = (typeof packageFeatureTypes)[number]; + +/** + * Metadata relating to an exported Backstage system component + * + * @public + */ +export type BackstagePackageFeature = { + // The export path, if omitted then it is the default package export + path?: string; + // The type of the export + type: BackstagePackageFeatureType; +}; + /** * Known fields in Backstage package.json files. * @@ -70,6 +128,11 @@ export interface BackstagePackageJson { * All packages that are part of the plugin. Must always and only be set for plugin packages and plugin library packages. */ pluginPackages?: string[]; + + /** + * The Backstage system components exported from this package + */ + features?: BackstagePackageFeature[]; }; exports?: JsonValue; diff --git a/packages/cli-node/src/monorepo/index.ts b/packages/cli-node/src/monorepo/index.ts index fdb8395c16..b0e595077d 100644 --- a/packages/cli-node/src/monorepo/index.ts +++ b/packages/cli-node/src/monorepo/index.ts @@ -16,10 +16,14 @@ export { isMonoRepo } from './isMonoRepo'; export { + packageFeatureTypes, + isValidPackageFeatureType, PackageGraph, type PackageGraphNode, type BackstagePackage, type BackstagePackageJson, + type BackstagePackageFeature, + type BackstagePackageFeatureType, } from './PackageGraph'; export { Lockfile, diff --git a/packages/cli/package.json b/packages/cli/package.json index 9208b5e70e..e33fddee90 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -147,6 +147,7 @@ "swc-loader": "^0.2.3", "tar": "^6.1.12", "terser-webpack-plugin": "^5.1.3", + "ts-morph": "^23.0.0", "util": "^0.12.3", "webpack": "^5.70.0", "webpack-dev-server": "^5.0.0", diff --git a/packages/cli/src/commands/repo/fix.ts b/packages/cli/src/commands/repo/fix.ts index a55440c53f..68b07564fb 100644 --- a/packages/cli/src/commands/repo/fix.ts +++ b/packages/cli/src/commands/repo/fix.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { findPaths } from '@backstage/cli-common'; import { BackstagePackage, BackstagePackageJson, @@ -23,9 +24,13 @@ import { } from '@backstage/cli-node'; import { OptionValues } from 'commander'; import fs from 'fs-extra'; +import isEqual from 'lodash/isEqual'; import { resolve as resolvePath, posix, relative as relativePath } from 'path'; +import { Project } from 'ts-morph'; import { paths } from '../../lib/paths'; import { publishPreflightCheck } from '../../lib/publishing'; +import { getFeaturesMetadata } from '../../lib/features'; +import { readEntryPoints } from '../../lib/entryPoints'; /** * A mutable object representing a package.json file with potential fixes. @@ -426,7 +431,46 @@ export function fixPluginPackages( } } -type PackageFixer = (pkg: FixablePackage, packages: FixablePackage[]) => void; +// For each of the defined export locations, we want to annotate +// the backstage field with the exported system components. The +// "exports" field in package.json is a mapping of import paths to file paths. +// +// While the exports field is really flexible, this function will enforce a +// particular structure, which is a Record where the key is the +// import path and the value is the file path. +export function fixPluginFeatures( + pkg: FixablePackage, + _packages: FixablePackage[], + project: Project, +) { + const { dir, packageJson } = pkg; + + if ( + !packageJson.backstage || + !packageJson.backstage.role || + !packageJson.exports + ) { + return; + } + + const entryPoints = readEntryPoints(packageJson); + const { role } = packageJson.backstage; + const featuresMetadata = getFeaturesMetadata(project, role, dir, entryPoints); + + if ( + featuresMetadata.length && + !isEqual(packageJson.backstage.features, featuresMetadata) + ) { + packageJson.backstage.features = featuresMetadata; + pkg.changed = true; + } +} + +type PackageFixer = ( + pkg: FixablePackage, + packages: FixablePackage[], + project: Project, +) => void; export async function command(opts: OptionValues): Promise { const packages = await readFixablePackages(); @@ -440,14 +484,20 @@ export async function command(opts: OptionValues): Promise { fixRepositoryField, fixPluginId, fixPluginPackages, + fixPluginFeatures, // Run the publish preflight check too, to make sure we don't uncover errors during publishing publishPreflightCheck, ); } + const workspaceRoot = findPaths(process.cwd()).targetRoot; + const project = new Project({ + tsConfigFilePath: resolvePath(workspaceRoot, 'tsconfig.json'), + }); + for (const fixer of fixers) { for (const pkg of packages) { - fixer(pkg, packages); + fixer(pkg, packages, project); } } diff --git a/packages/cli/src/lib/features.test.ts b/packages/cli/src/lib/features.test.ts new file mode 100644 index 0000000000..b20ba7c6ac --- /dev/null +++ b/packages/cli/src/lib/features.test.ts @@ -0,0 +1,273 @@ +/* + * Copyright 2024 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 { BackstagePackageFeatureType, PackageRole } from '@backstage/cli-node'; +import createFeatureEnvironment from '../tests/createFeatureEnvironment'; +import { getEntryPointExports, getFeaturesMetadata } from './features'; + +describe('features', () => { + describe('for package role', () => { + // This Record makes sure we're checking all package roles + const packageRoles: Record = { + // Allowed + 'backend-plugin': true, + 'backend-plugin-module': true, + 'frontend-plugin': true, + 'frontend-plugin-module': true, + // Disallowed + frontend: false, + backend: false, + cli: false, + 'web-library': false, + 'node-library': false, + 'common-library': false, + }; + + const allowedPackageRoles = Object.keys(packageRoles).filter( + role => packageRoles[role as PackageRole], + ); + + const disallowedPackageRoles = Object.keys(packageRoles).filter( + role => !packageRoles[role as PackageRole], + ); + + it.each(allowedPackageRoles)(`returns features for %s`, r => { + const { project, role, dir, entryPoints } = createFeatureEnvironment({ + role: r as PackageRole, + }); + + expect(getFeaturesMetadata(project, role, dir, entryPoints)).toEqual([ + { + type: '@backstage/BackendFeature', + }, + ]); + }); + + it.each(disallowedPackageRoles)(`does not return features for %s`, r => { + const { project, role, dir, entryPoints } = createFeatureEnvironment({ + role: r as PackageRole, + }); + + expect(getFeaturesMetadata(project, role, dir, entryPoints)).toEqual([]); + }); + }); + + describe('for feature $$type', () => { + // This Record makes sure we're checking all feature types + const featureTypes: Record = { + // Allowed + '@backstage/BackendFeature': true, + '@backstage/BackstagePlugin': true, + '@backstage/FrontendPlugin': true, + '@backstage/FrontendModule': true, + // Disallowed + '@backstage/BackendFeatureFactory': false, + '@backstage/BackstageCredentials': false, + '@backstage/Extension': false, + '@backstage/ExtensionDataRef': false, + '@backstage/ExtensionDataValue': false, + '@backstage/ExtensionDefinition': false, + '@backstage/ExtensionInput': false, + '@backstage/ExtensionOverrides': false, + '@backstage/ExtensionPoint': false, + '@backstage/ExternalRouteRef': false, + '@backstage/RouteRef': false, + '@backstage/ServiceRef': false, + '@backstage/SubRouteRef': false, + '@backstage/TranslationMessages': false, + '@backstage/TranslationRef': false, + '@backstage/TranslationResource': false, + }; + + const allowedFeatureTypes = Object.keys(featureTypes).filter( + $$type => featureTypes[$$type as BackstagePackageFeatureType], + ); + + const disallowedFeatureTypes = Object.keys(featureTypes).filter( + $$type => !featureTypes[$$type as BackstagePackageFeatureType], + ); + + it.each(allowedFeatureTypes)(`returns features for "%s" $$type`, $$type => { + const { project, role, dir, entryPoints } = createFeatureEnvironment({ + $$type: $$type as BackstagePackageFeatureType, + }); + + expect(getFeaturesMetadata(project, role, dir, entryPoints)).toEqual([ + { + type: $$type, + }, + ]); + }); + + it.each(disallowedFeatureTypes)( + `does not return features for "%s" $$type`, + $$type => { + const { project, role, dir, entryPoints } = createFeatureEnvironment({ + $$type: $$type as BackstagePackageFeatureType, + }); + + expect(getFeaturesMetadata(project, role, dir, entryPoints)).toEqual( + [], + ); + }, + ); + }); + + describe('export declaration formats', () => { + it('supports format of "export default ..."', () => { + const { + project, + dir, + entryPoints: [entryPoint], + } = createFeatureEnvironment({ + format: 'DefaultExport', + }); + + expect(getEntryPointExports(entryPoint, project, dir)).toEqual([ + { + location: '.', + name: 'default', + type: '@backstage/BackendFeature', + }, + ]); + }); + + it('supports format of "export { default } from ..."', () => { + const { + project, + dir, + entryPoints: [entryPoint], + } = createFeatureEnvironment({ + format: 'DefaultExportLinked', + }); + + expect(getEntryPointExports(entryPoint, project, dir)).toEqual([ + { + location: '.', + name: 'default', + type: '@backstage/BackendFeature', + }, + ]); + }); + + it('supports format of "export { default, ... } from ..."', () => { + const { + project, + dir, + entryPoints: [entryPoint], + } = createFeatureEnvironment({ + format: 'DefaultExportLinkedWithSibling', + }); + + expect(getEntryPointExports(entryPoint, project, dir)).toEqual([ + { + location: '.', + name: 'default', + type: '@backstage/BackendFeature', + }, + { + location: '.', + name: 'test', + type: '@backstage/BackendFeature', + }, + ]); + }); + + it('supports format of "export const foo = ..."', () => { + const { + project, + dir, + entryPoints: [entryPoint], + } = createFeatureEnvironment({ + format: 'NamedExport', + }); + + expect(getEntryPointExports(entryPoint, project, dir)).toEqual([ + { + location: '.', + name: 'test', + type: '@backstage/BackendFeature', + }, + ]); + }); + + it('supports format of "export * from ..."', () => { + const { + project, + dir, + entryPoints: [entryPoint], + } = createFeatureEnvironment({ + format: 'WildCardExport', + }); + + expect(getEntryPointExports(entryPoint, project, dir)).toEqual([ + { + location: '.', + name: 'test', + type: '@backstage/BackendFeature', + }, + ]); + }); + }); + + describe('entry points', () => { + it('returns features for multiple entry points', () => { + const { project, role, dir, entryPoints } = createFeatureEnvironment({ + exports: { + '.': 'src/index.ts', + './alpha': 'src/alpha.ts', + './beta': 'src/beta.ts', + }, + }); + + expect(getFeaturesMetadata(project, role, dir, entryPoints)).toEqual([ + { + type: '@backstage/BackendFeature', + }, + { + path: './alpha', + type: '@backstage/BackendFeature', + }, + { + path: './beta', + type: '@backstage/BackendFeature', + }, + ]); + }); + + it('ignores entry points that are not .ts or .tsx files', () => { + const { project, role, dir, entryPoints } = createFeatureEnvironment({ + exports: { + '.': 'src/index.js', + }, + }); + + expect(getFeaturesMetadata(project, role, dir, entryPoints)).toEqual([]); + }); + }); + + it('only returns default exports', () => { + const { project, role, dir, entryPoints } = createFeatureEnvironment({ + format: 'DefaultExportLinkedWithSibling', + }); + + expect(getFeaturesMetadata(project, role, dir, entryPoints)).toEqual([ + { + type: '@backstage/BackendFeature', + }, + ]); + }); +}); diff --git a/packages/cli/src/lib/features.ts b/packages/cli/src/lib/features.ts new file mode 100644 index 0000000000..55245abea3 --- /dev/null +++ b/packages/cli/src/lib/features.ts @@ -0,0 +1,163 @@ +/* + * 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 { + isValidPackageFeatureType, + PackageRole, + type BackstagePackageFeature, + type BackstagePackageFeatureType, +} from '@backstage/cli-node'; +import { Project, SyntaxKind, ts, Type } from 'ts-morph'; +import { resolve as resolvePath } from 'node:path'; +import { EntryPoint } from './entryPoints'; + +// A list of the package roles we want to extract features for +const targetPackageRoles: PackageRole[] = [ + 'backend-plugin', + 'backend-plugin-module', + 'frontend-plugin', + 'frontend-plugin-module', +]; + +// A list of the feature types we want to extract from the project +// and include in the metadata +const targetFeatureTypes: BackstagePackageFeatureType[] = [ + '@backstage/BackendFeature', + '@backstage/BackstagePlugin', + '@backstage/FrontendPlugin', + '@backstage/FrontendModule', +]; + +// Returns all valid Backstage package features from a project +// for all entry points. +export function getFeaturesMetadata( + project: Project, + role: PackageRole, + dir: string, + entryPoints: EntryPoint[], +): BackstagePackageFeature[] { + if (!targetPackageRoles.includes(role)) { + return []; + } + + return entryPoints + .flatMap(entryPoint => getEntryPointExports(entryPoint, project, dir)) + .filter(isDefaultExport) + .filter(isTargetFeatureType) + .map(toBackstagePackageFeature); +} + +type EntryPointExport = { + name: string; + location: string; + type: BackstagePackageFeatureType; +}; + +// Returns all exports (default and named) from an entry point +// that are valid Backstage package features +export function getEntryPointExports( + { mount: location, path, ext }: EntryPoint, + project: Project, + dir: string, +): EntryPointExport[] { + const fullFilePath = resolvePath(dir, path); + const sourceFile = project.getSourceFile(fullFilePath); + + if (!sourceFile || (ext !== '.ts' && ext !== '.tsx')) { + return []; + } + + const exports = []; + + for (const exportSymbol of sourceFile.getExportSymbols()) { + const declaration = exportSymbol.getDeclarations()[0]; + const exportName = declaration.getSymbol()?.getName(); + let exportType: Type | undefined; + + if (declaration) { + if (declaration.isKind(SyntaxKind.ExportAssignment)) { + exportType = declaration.getExpression().getType(); + } else if (declaration.isKind(SyntaxKind.ExportSpecifier)) { + if (!declaration.isTypeOnly()) { + exportType = declaration.getType(); + } + } else if (declaration.isKind(SyntaxKind.VariableDeclaration)) { + exportType = declaration.getType(); + } + } + + if (exportName && exportType) { + const $$type = getBackstagePackageFeature$$TypeFromType(exportType); + + if ($$type) { + exports.push({ + name: exportName, + location, + type: $$type, + }); + } + } + } + + return exports; +} + +// Given a TS type, returns the Backstage package feature $$type value +function getBackstagePackageFeature$$TypeFromType( + type: Type, +): BackstagePackageFeatureType | null { + // Returns the concrete type of a generic type + const exportType = type.getTargetType() ?? type; + + for (const property of exportType.getProperties()) { + if (property.getName() === '$$type') { + const $$type = property + .getValueDeclaration() + ?.getText() + .match(/(?@backstage\/\w+)/)?.groups?.type; + + if ($$type && isValidPackageFeatureType($$type)) { + return $$type; + } + } + } + + return null; +} + +// Returns whether an export is the default export +function isDefaultExport({ name }: EntryPointExport): boolean { + return name === 'default'; +} + +// Returns whether an export is a valid Backstage package feature type +function isTargetFeatureType({ type }: EntryPointExport): boolean { + return targetFeatureTypes.includes(type); +} + +// Converts an entry point export to a Backstage package feature +function toBackstagePackageFeature({ + type, + location, +}: EntryPointExport): BackstagePackageFeature { + const feature: BackstagePackageFeature = { type }; + + if (location !== '.') { + feature.path = location; + } + + return feature; +} diff --git a/packages/cli/src/tests/createFeatureEnvironment.ts b/packages/cli/src/tests/createFeatureEnvironment.ts new file mode 100644 index 0000000000..6313e6439b --- /dev/null +++ b/packages/cli/src/tests/createFeatureEnvironment.ts @@ -0,0 +1,196 @@ +/* + * Copyright 2024 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 { BackstagePackageFeatureType, PackageRole } from '@backstage/cli-node'; +import path from 'path'; +import { Project } from 'ts-morph'; +import { EntryPoint } from '../lib/entryPoints'; + +type CreateFeatureEnvironmentOptions = { + $$type?: BackstagePackageFeatureType; + exports?: Record; + format?: + | 'DefaultExport' + | 'DefaultExportLinked' + | 'DefaultExportLinkedWithSibling' + | 'NamedExport' + | 'WildCardExport'; + role?: PackageRole; +}; + +type FeatureEnvironment = { + project: Project; + role: PackageRole; + dir: string; + entryPoints: EntryPoint[]; +}; + +type File = { + path: string; + content: string; +}; + +const createForExports = ( + exports: Record, + content: string, +): File[] => { + return Object.entries(exports).map(([_, filePath]) => ({ + path: `./${filePath}`, + content, + })); +}; + +const createTestType = ($$type: BackstagePackageFeatureType): File[] => [ + { + path: './src/createTestType.ts', + content: ` +export interface TestType { + readonly $$type: '${$$type}'; +}; + +export function createTestType(): TestType { + return { + $$type: '${$$type}', + }; +}; + `, + }, +]; + +const createTestDefaultExport = (exports: Record): File[] => + createForExports( + exports, + ` +import { createTestType } from './createTestType'; + +export { TestType } from './createTestType'; +export default createTestType(); + `, + ); + +const createTestDefaultExportLinked = ( + exports: Record, +): File[] => [ + ...createForExports( + exports, + ` +export { TestType } from './createTestType'; +export { default } from './linked'; + `, + ), + { + path: './src/linked.ts', + content: ` +import { createTestType } from './createTestType'; + +export default createTestType(); + `, + }, +]; + +const createTestDefaultExportLinkedWithSibling = ( + exports: Record, +): File[] => [ + ...createForExports( + exports, + ` +export { TestType } from './createTestType'; +export { default, test } from './linked'; + `, + ), + { + path: './src/linked.ts', + content: ` +import { createTestType } from './createTestType'; + +export const test = createTestType(); +export default createTestType(); + `, + }, +]; + +const createTestNamedExport = (exports: Record): File[] => [ + ...createForExports( + exports, + ` +import { createTestType } from './createTestType'; + +export { TestType } from './createTestType'; +export const test = createTestType(); + `, + ), +]; + +const createTestWildCardExport = (exports: Record): File[] => [ + ...createForExports( + exports, + ` +export * from './linked'; + `, + ), + { + path: './src/linked.ts', + content: ` +import { createTestType } from './createTestType'; + +export { TestType } from './createTestType'; +export const test = createTestType(); +export default createTestType(); + `, + }, +]; + +const formatToFiles = { + DefaultExport: createTestDefaultExport, + DefaultExportLinked: createTestDefaultExportLinked, + DefaultExportLinkedWithSibling: createTestDefaultExportLinkedWithSibling, + NamedExport: createTestNamedExport, + WildCardExport: createTestWildCardExport, +}; + +export default function createFeatureEnvironment( + options?: CreateFeatureEnvironmentOptions, +): FeatureEnvironment { + const { + $$type = '@backstage/BackendFeature', + exports = { '.': 'src/index.ts' }, + format = 'DefaultExport', + role = 'backend-plugin', + } = options ?? {}; + + const project = new Project(); + const entryPoints: EntryPoint[] = Object.entries(exports ?? {}).map( + ([mount, filePath]) => ({ + mount, + path: filePath, + name: mount, + ext: path.extname(filePath), + }), + ); + + const files = [...createTestType($$type), ...formatToFiles[format](exports)]; + + for (const file of files) { + project.createSourceFile(file.path, file.content); + } + + return { + project, + role, + dir: project.getFileSystem().getCurrentDirectory(), + entryPoints, + }; +} diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 6858d6a81f..4576a93f85 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -7,6 +7,12 @@ "pluginId": "api-docs", "pluginPackages": [ "@backstage/plugin-api-docs" + ], + "features": [ + { + "type": "@backstage/FrontendPlugin", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 606d5adea6..2d9978e254 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -9,6 +9,12 @@ "@backstage/plugin-app", "@backstage/plugin-app-backend", "@backstage/plugin-app-node" + ], + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 92f11d4b63..c6796e35bd 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -5,7 +5,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", - "pluginPackage": "@backstage/plugin-catalog-backend" + "pluginPackage": "@backstage/plugin-catalog-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index aa0b3663c7..84e3368efb 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -5,7 +5,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", - "pluginPackage": "@backstage/plugin-catalog-backend" + "pluginPackage": "@backstage/plugin-catalog-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index 4f0007bb44..5a24423396 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -5,7 +5,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", - "pluginPackage": "@backstage/plugin-catalog-backend" + "pluginPackage": "@backstage/plugin-catalog-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index af12601f87..030d8511ca 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -4,7 +4,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", - "pluginPackage": "@backstage/plugin-catalog-backend" + "pluginPackage": "@backstage/plugin-catalog-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index 13904c905b..b9609dbadf 100644 --- a/plugins/catalog-backend-module-gcp/package.json +++ b/plugins/catalog-backend-module-gcp/package.json @@ -5,7 +5,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", - "pluginPackage": "@backstage/plugin-catalog-backend" + "pluginPackage": "@backstage/plugin-catalog-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index caebb75b34..ce2af52542 100644 --- a/plugins/catalog-backend-module-gerrit/package.json +++ b/plugins/catalog-backend-module-gerrit/package.json @@ -4,7 +4,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", - "pluginPackage": "@backstage/plugin-catalog-backend" + "pluginPackage": "@backstage/plugin-catalog-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index 8b05d0f896..f58d08192b 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -5,7 +5,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", - "pluginPackage": "@backstage/plugin-catalog-backend" + "pluginPackage": "@backstage/plugin-catalog-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index ef8d1f9148..126cad0bed 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -5,7 +5,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", - "pluginPackage": "@backstage/plugin-catalog-backend" + "pluginPackage": "@backstage/plugin-catalog-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index 4e964ccc9d..1c55b088e1 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/package.json +++ b/plugins/catalog-backend-module-incremental-ingestion/package.json @@ -5,7 +5,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", - "pluginPackage": "@backstage/plugin-catalog-backend" + "pluginPackage": "@backstage/plugin-catalog-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index ebdd9bc316..e8c1a41ecb 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -5,7 +5,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", - "pluginPackage": "@backstage/plugin-catalog-backend" + "pluginPackage": "@backstage/plugin-catalog-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index 670dbe23a2..5d71befbb5 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -5,7 +5,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", - "pluginPackage": "@backstage/plugin-catalog-backend" + "pluginPackage": "@backstage/plugin-catalog-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/package.json b/plugins/catalog-backend-module-scaffolder-entity-model/package.json index d64b3b468e..565fe94206 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/package.json +++ b/plugins/catalog-backend-module-scaffolder-entity-model/package.json @@ -5,7 +5,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", - "pluginPackage": "@backstage/plugin-catalog-backend" + "pluginPackage": "@backstage/plugin-catalog-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 4826758d01..8aac88ac51 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -11,6 +11,12 @@ "@backstage/plugin-catalog-common", "@backstage/plugin-catalog-node", "@backstage/plugin-catalog-react" + ], + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 4e83990c95..14dfe03579 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -6,6 +6,12 @@ "pluginId": "catalog-graph", "pluginPackages": [ "@backstage/plugin-catalog-graph" + ], + "features": [ + { + "type": "@backstage/FrontendPlugin", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 964dbd3c33..fb6d40220f 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -7,6 +7,12 @@ "pluginId": "catalog-import", "pluginPackages": [ "@backstage/plugin-catalog-import" + ], + "features": [ + { + "type": "@backstage/FrontendPlugin", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 317e91fa4d..606ebb16d1 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -11,6 +11,12 @@ "@backstage/plugin-catalog-common", "@backstage/plugin-catalog-node", "@backstage/plugin-catalog-react" + ], + "features": [ + { + "type": "@backstage/FrontendPlugin", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 9e4c0caab6..1b0f14b193 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -8,6 +8,12 @@ "@backstage/plugin-devtools", "@backstage/plugin-devtools-backend", "@backstage/plugin-devtools-common" + ], + "features": [ + { + "type": "@backstage/FrontendPlugin", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/events-backend-module-aws-sqs/package.json b/plugins/events-backend-module-aws-sqs/package.json index d109490dce..8eac12d934 100644 --- a/plugins/events-backend-module-aws-sqs/package.json +++ b/plugins/events-backend-module-aws-sqs/package.json @@ -4,7 +4,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "events", - "pluginPackage": "@backstage/plugin-events-backend" + "pluginPackage": "@backstage/plugin-events-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/events-backend-module-azure/package.json b/plugins/events-backend-module-azure/package.json index 91cd11d286..376f507c07 100644 --- a/plugins/events-backend-module-azure/package.json +++ b/plugins/events-backend-module-azure/package.json @@ -4,7 +4,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "events", - "pluginPackage": "@backstage/plugin-events-backend" + "pluginPackage": "@backstage/plugin-events-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/events-backend-module-bitbucket-cloud/package.json b/plugins/events-backend-module-bitbucket-cloud/package.json index 423fbde187..e6c6f835eb 100644 --- a/plugins/events-backend-module-bitbucket-cloud/package.json +++ b/plugins/events-backend-module-bitbucket-cloud/package.json @@ -4,7 +4,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "events", - "pluginPackage": "@backstage/plugin-events-backend" + "pluginPackage": "@backstage/plugin-events-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/events-backend-module-gerrit/package.json b/plugins/events-backend-module-gerrit/package.json index 6f1bc451d9..65c09cd4ad 100644 --- a/plugins/events-backend-module-gerrit/package.json +++ b/plugins/events-backend-module-gerrit/package.json @@ -4,7 +4,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "events", - "pluginPackage": "@backstage/plugin-events-backend" + "pluginPackage": "@backstage/plugin-events-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/events-backend/package.json b/plugins/events-backend/package.json index 84ff688b16..998b76b091 100644 --- a/plugins/events-backend/package.json +++ b/plugins/events-backend/package.json @@ -8,6 +8,12 @@ "@backstage/plugin-events-backend", "@backstage/plugin-events-backend-test-utils", "@backstage/plugin-events-node" + ], + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/home/package.json b/plugins/home/package.json index 617778dd52..f5987fcf24 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -8,6 +8,12 @@ "pluginPackages": [ "@backstage/plugin-home", "@backstage/plugin-home-react" + ], + "features": [ + { + "type": "@backstage/FrontendPlugin", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index f47fd650e4..5ee33a8357 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -11,6 +11,12 @@ "@backstage/plugin-kubernetes-common", "@backstage/plugin-kubernetes-node", "@backstage/plugin-kubernetes-react" + ], + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 789c3375ce..e8a798d85b 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -11,6 +11,12 @@ "@backstage/plugin-kubernetes-common", "@backstage/plugin-kubernetes-node", "@backstage/plugin-kubernetes-react" + ], + "features": [ + { + "type": "@backstage/FrontendPlugin", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/org/package.json b/plugins/org/package.json index 88d1e783ca..fb9878fa52 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -8,6 +8,12 @@ "pluginPackages": [ "@backstage/plugin-org", "@backstage/plugin-org-react" + ], + "features": [ + { + "type": "@backstage/FrontendPlugin", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index be82562f0f..eec76b67ad 100644 --- a/plugins/permission-backend/package.json +++ b/plugins/permission-backend/package.json @@ -9,6 +9,12 @@ "@backstage/plugin-permission-common", "@backstage/plugin-permission-node", "@backstage/plugin-permission-react" + ], + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 4ba5b7df15..08edd2c406 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -7,6 +7,12 @@ "pluginId": "proxy", "pluginPackages": [ "@backstage/plugin-proxy-backend" + ], + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/scaffolder-backend-module-azure/package.json b/plugins/scaffolder-backend-module-azure/package.json index 7c5dd0a317..3203ac11f8 100644 --- a/plugins/scaffolder-backend-module-azure/package.json +++ b/plugins/scaffolder-backend-module-azure/package.json @@ -5,7 +5,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json index cb8cbd7400..9da56ff11f 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json @@ -5,7 +5,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend-module-bitbucket-server/package.json b/plugins/scaffolder-backend-module-bitbucket-server/package.json index 585668ddb5..bf0dc415df 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-server/package.json @@ -5,7 +5,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend-module-bitbucket/package.json b/plugins/scaffolder-backend-module-bitbucket/package.json index f579ea62b3..43149fa2ba 100644 --- a/plugins/scaffolder-backend-module-bitbucket/package.json +++ b/plugins/scaffolder-backend-module-bitbucket/package.json @@ -5,7 +5,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json index 9259eb0798..4e872be8a8 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json @@ -5,7 +5,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 467afb4339..5776875d16 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -5,7 +5,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend-module-gcp/package.json b/plugins/scaffolder-backend-module-gcp/package.json index 355b84c774..4b29179a44 100644 --- a/plugins/scaffolder-backend-module-gcp/package.json +++ b/plugins/scaffolder-backend-module-gcp/package.json @@ -5,7 +5,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend-module-gerrit/package.json b/plugins/scaffolder-backend-module-gerrit/package.json index 90136f4c34..8f6a772b08 100644 --- a/plugins/scaffolder-backend-module-gerrit/package.json +++ b/plugins/scaffolder-backend-module-gerrit/package.json @@ -5,7 +5,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 1f597f70ae..98c0e80e3e 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -5,7 +5,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend-module-github/package.json b/plugins/scaffolder-backend-module-github/package.json index 9ebd61f809..fe4ad110ad 100644 --- a/plugins/scaffolder-backend-module-github/package.json +++ b/plugins/scaffolder-backend-module-github/package.json @@ -5,7 +5,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index 2b73a9ce71..6c4b80ec92 100644 --- a/plugins/scaffolder-backend-module-gitlab/package.json +++ b/plugins/scaffolder-backend-module-gitlab/package.json @@ -4,7 +4,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index 26c2c9aca1..63ed830f47 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -5,7 +5,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index 8b15f66689..0e1546a247 100644 --- a/plugins/scaffolder-backend-module-sentry/package.json +++ b/plugins/scaffolder-backend-module-sentry/package.json @@ -4,7 +4,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index 5303cc543e..60a68975f1 100644 --- a/plugins/scaffolder-backend-module-yeoman/package.json +++ b/plugins/scaffolder-backend-module-yeoman/package.json @@ -4,7 +4,12 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", - "pluginPackage": "@backstage/plugin-scaffolder-backend" + "pluginPackage": "@backstage/plugin-scaffolder-backend", + "features": [ + { + "type": "@backstage/BackendFeature" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index a4a8b1fdf2..0471eb5fba 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -12,6 +12,12 @@ "@backstage/plugin-scaffolder-node", "@backstage/plugin-scaffolder-node-test-utils", "@backstage/plugin-scaffolder-react" + ], + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index c4fe3ab8ad..482ce13631 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -12,6 +12,12 @@ "@backstage/plugin-scaffolder-node", "@backstage/plugin-scaffolder-node-test-utils", "@backstage/plugin-scaffolder-react" + ], + "features": [ + { + "type": "@backstage/FrontendPlugin", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/search-backend-module-catalog/package.json b/plugins/search-backend-module-catalog/package.json index f3cd8ff7b0..3ca2f84501 100644 --- a/plugins/search-backend-module-catalog/package.json +++ b/plugins/search-backend-module-catalog/package.json @@ -5,7 +5,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "search", - "pluginPackage": "@backstage/plugin-search-backend" + "pluginPackage": "@backstage/plugin-search-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 4b1d173048..840cf23f0d 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -5,7 +5,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "search", - "pluginPackage": "@backstage/plugin-search-backend" + "pluginPackage": "@backstage/plugin-search-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json index aef6c7fd61..312a7af1b9 100644 --- a/plugins/search-backend-module-explore/package.json +++ b/plugins/search-backend-module-explore/package.json @@ -5,7 +5,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "search", - "pluginPackage": "@backstage/plugin-search-backend" + "pluginPackage": "@backstage/plugin-search-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 7147f83840..ef2a583d86 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -5,7 +5,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "search", - "pluginPackage": "@backstage/plugin-search-backend" + "pluginPackage": "@backstage/plugin-search-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index 6d89f7601a..81a594cc9c 100644 --- a/plugins/search-backend-module-techdocs/package.json +++ b/plugins/search-backend-module-techdocs/package.json @@ -5,7 +5,13 @@ "backstage": { "role": "backend-plugin-module", "pluginId": "search", - "pluginPackage": "@backstage/plugin-search-backend" + "pluginPackage": "@backstage/plugin-search-backend", + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } + ] }, "publishConfig": { "access": "public" diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 05dc6ba72e..f1ec4087f1 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -11,6 +11,12 @@ "@backstage/plugin-search-backend-node", "@backstage/plugin-search-common", "@backstage/plugin-search-react" + ], + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/search/package.json b/plugins/search/package.json index eb45b7f3b1..279c926428 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -11,6 +11,12 @@ "@backstage/plugin-search-backend-node", "@backstage/plugin-search-common", "@backstage/plugin-search-react" + ], + "features": [ + { + "type": "@backstage/FrontendPlugin", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 7006bd2758..872e0752bd 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -11,6 +11,12 @@ "@backstage/plugin-techdocs-common", "@backstage/plugin-techdocs-node", "@backstage/plugin-techdocs-react" + ], + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 4274f77072..70684ce581 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -11,6 +11,12 @@ "@backstage/plugin-techdocs-common", "@backstage/plugin-techdocs-node", "@backstage/plugin-techdocs-react" + ], + "features": [ + { + "type": "@backstage/FrontendPlugin", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index 8a6b5e2882..415202297d 100644 --- a/plugins/user-settings-backend/package.json +++ b/plugins/user-settings-backend/package.json @@ -9,6 +9,12 @@ "@backstage/plugin-user-settings", "@backstage/plugin-user-settings-backend", "@backstage/plugin-user-settings-common" + ], + "features": [ + { + "type": "@backstage/BackendFeature", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 3d545ca6ee..772cccee42 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -9,6 +9,12 @@ "@backstage/plugin-user-settings", "@backstage/plugin-user-settings-backend", "@backstage/plugin-user-settings-common" + ], + "features": [ + { + "type": "@backstage/FrontendPlugin", + "path": "./alpha" + } ] }, "publishConfig": { diff --git a/yarn.lock b/yarn.lock index 659c3d4635..ad8a46a2f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4025,6 +4025,7 @@ __metadata: swc-loader: ^0.2.3 tar: ^6.1.12 terser-webpack-plugin: ^5.1.3 + ts-morph: ^23.0.0 util: ^0.12.3 vite: ^5.0.0 vite-plugin-html: ^3.2.2