From 093e5e06e43bd15cd12b57e7ce8134b56bcd8e7c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 14 Jan 2022 19:39:06 +0100 Subject: [PATCH 1/7] cli: add experimental type builds Signed-off-by: Patrik Oldsberg --- packages/cli/package.json | 8 + packages/cli/src/commands/backend/build.ts | 1 + packages/cli/src/commands/build.ts | 6 +- packages/cli/src/commands/index.ts | 3 + packages/cli/src/commands/plugin/build.ts | 1 + .../src/lib/builder/buildTypeDefinitions.ts | 158 ++++++++++++++++++ packages/cli/src/lib/builder/config.ts | 8 +- packages/cli/src/lib/builder/packager.ts | 19 ++- packages/cli/src/lib/builder/types.ts | 1 + 9 files changed, 195 insertions(+), 10 deletions(-) create mode 100644 packages/cli/src/lib/builder/buildTypeDefinitions.ts diff --git a/packages/cli/package.json b/packages/cli/package.json index edb2cfb3ce..be7a170daf 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -144,6 +144,14 @@ "nodemon": "^2.0.2", "ts-node": "^10.0.0" }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.19.2" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + } + }, "resolutions": { "@types/webpack-dev-server/@types/webpack": "^5.28.0" }, diff --git a/packages/cli/src/commands/backend/build.ts b/packages/cli/src/commands/backend/build.ts index 8c25f56746..5d4e17e392 100644 --- a/packages/cli/src/commands/backend/build.ts +++ b/packages/cli/src/commands/backend/build.ts @@ -21,5 +21,6 @@ export default async (cmd: Command) => { await buildPackage({ outputs: new Set([Output.cjs, Output.types]), minify: cmd.minify, + useApiExtractor: cmd.experimentalTypeBuild, }); }; diff --git a/packages/cli/src/commands/build.ts b/packages/cli/src/commands/build.ts index d312811396..86ab5c4300 100644 --- a/packages/cli/src/commands/build.ts +++ b/packages/cli/src/commands/build.ts @@ -33,5 +33,9 @@ export default async (cmd: Command) => { outputs = new Set([Output.types, Output.esm, Output.cjs]); } - await buildPackage({ outputs, minify: cmd.minify }); + await buildPackage({ + outputs, + minify: cmd.minify, + useApiExtractor: cmd.experimentalTypeBuild, + }); }; diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index dfe427b27b..12fce66bd4 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -48,6 +48,7 @@ export function registerCommands(program: CommanderStatic) { .command('backend:build') .description('Build a backend plugin') .option('--minify', 'Minify the generated code') + .option('--experimental-type-build', 'Enable experimental type build') .action(lazy(() => import('./backend/build').then(m => m.default))); program @@ -118,6 +119,7 @@ export function registerCommands(program: CommanderStatic) { .command('plugin:build') .description('Build a plugin') .option('--minify', 'Minify the generated code') + .option('--experimental-type-build', 'Enable experimental type build') .action(lazy(() => import('./plugin/build').then(m => m.default))); program @@ -139,6 +141,7 @@ export function registerCommands(program: CommanderStatic) { .description('Build a package for publishing') .option('--outputs ', 'List of formats to output [types,cjs,esm]') .option('--minify', 'Minify the generated code') + .option('--experimental-type-build', 'Enable experimental type build') .action(lazy(() => import('./build').then(m => m.default))); program diff --git a/packages/cli/src/commands/plugin/build.ts b/packages/cli/src/commands/plugin/build.ts index 9c5e173989..56f99f59b1 100644 --- a/packages/cli/src/commands/plugin/build.ts +++ b/packages/cli/src/commands/plugin/build.ts @@ -21,5 +21,6 @@ export default async (cmd: Command) => { await buildPackage({ outputs: new Set([Output.esm, Output.types]), minify: cmd.minify, + useApiExtractor: cmd.experimentalTypeBuild, }); }; diff --git a/packages/cli/src/lib/builder/buildTypeDefinitions.ts b/packages/cli/src/lib/builder/buildTypeDefinitions.ts new file mode 100644 index 0000000000..b4ca87b825 --- /dev/null +++ b/packages/cli/src/lib/builder/buildTypeDefinitions.ts @@ -0,0 +1,158 @@ +/* + * 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 fs from 'fs-extra'; +import chalk from 'chalk'; +import { + relative as relativePath, + resolve as resolvePath, + dirname, +} from 'path'; +import { paths } from '../paths'; + +// These message types are ignored since we want to avoid duplicating the logic of +// handling them correctly, and we already have the API Reports warning about them. +const ignoredMessages = new Set(['tsdoc-undefined-tag', 'ae-forgotten-export']); + +let apiExtractor: undefined | typeof import('@microsoft/api-extractor'); +function prepareApiExtractor() { + if (apiExtractor) { + return apiExtractor; + } + + try { + apiExtractor = require('@microsoft/api-extractor'); + } catch (error) { + throw new Error( + 'Failed to resolve @microsoft/api-extractor, it must best installed ' + + 'as a dependency of your project in order to use experimental type builds', + ); + } + + /** + * All of this monkey patching below is because MUI has these bare package.json file as a method + * for making TypeScript accept imports like `@material-ui/core/Button`, and improve tree-shaking + * by declaring them side effect free. + * + * The package.json lookup logic in api-extractor really doesn't like that though, as it enforces + * that the 'name' field exists in all package.json files that it discovers. This below is just + * making sure that we ignore those file package.json files instead of crashing. + */ + const { + PackageJsonLookup, + // eslint-disable-next-line import/no-extraneous-dependencies + } = require('@rushstack/node-core-library/lib/PackageJsonLookup'); + + const old = PackageJsonLookup.prototype.tryGetPackageJsonFilePathFor; + PackageJsonLookup.prototype.tryGetPackageJsonFilePathFor = + function tryGetPackageJsonFilePathForPatch(path: string) { + if ( + path.includes('@material-ui') && + !dirname(path).endsWith('@material-ui') + ) { + return undefined; + } + return old.call(this, path); + }; + + return apiExtractor!; +} + +export async function buildTypeDefinitions() { + const { Extractor, ExtractorConfig } = prepareApiExtractor(); + + const distTypesPackageDir = paths.resolveTargetRoot( + 'dist-types', + relativePath(paths.targetRoot, paths.targetDir), + ); + const entryPoint = resolvePath(distTypesPackageDir, 'src/index.d.ts'); + + const declarationsExist = await fs.pathExists(entryPoint); + if (!declarationsExist) { + const path = relativePath(paths.targetDir, entryPoint); + throw new Error( + `No declaration files found at ${path}, be sure to run ${chalk.bgRed.white( + 'yarn tsc', + )} to generate .d.ts files before packaging`, + ); + } + + const extractorConfig = ExtractorConfig.prepare({ + configObject: { + mainEntryPointFilePath: entryPoint, + bundledPackages: [], + + compiler: { + tsconfigFilePath: paths.resolveTargetRoot('tsconfig.json'), + }, + + dtsRollup: { + enabled: true, + untrimmedFilePath: paths.resolveTarget('dist/index.d.ts'), + }, + + newlineKind: 'lf', + + projectFolder: paths.targetDir, + }, + configObjectFullPath: paths.targetDir, + packageJsonFullPath: paths.resolveTarget('package.json'), + }); + + const typescriptDir = paths.resolveTargetRoot('node_modules/typescript'); + const hasTypescript = await fs.pathExists(typescriptDir); + const extractorResult = Extractor.invoke(extractorConfig, { + typescriptCompilerFolder: hasTypescript ? typescriptDir : undefined, + localBuild: false, + showVerboseMessages: false, + showDiagnostics: false, + messageCallback(message) { + message.handled = true; + if (ignoredMessages.has(message.messageId)) { + return; + } + + let text = `${message.text} (${message.messageId})`; + if (message.sourceFilePath) { + text += ' at '; + text += relativePath(distTypesPackageDir, message.sourceFilePath); + if (message.sourceFileLine) { + text += `:${message.sourceFileLine}`; + if (message.sourceFileColumn) { + text += `:${message.sourceFileColumn}`; + } + } + } + if (message.logLevel === 'error') { + console.error(chalk.red(`Error: ${text}`)); + } else if ( + message.logLevel === 'warning' || + message.category === 'Extractor' + ) { + console.warn(`Warning: ${text}`); + } else { + console.log(text); + } + }, + }); + + if (!extractorResult.succeeded) { + throw new Error( + `Type definition build completed with ${extractorResult.errorCount} errors` + + ` and ${extractorResult.warningCount} warnings`, + ); + } +} diff --git a/packages/cli/src/lib/builder/config.ts b/packages/cli/src/lib/builder/config.ts index ba60e8fea9..617e354864 100644 --- a/packages/cli/src/lib/builder/config.ts +++ b/packages/cli/src/lib/builder/config.ts @@ -33,9 +33,9 @@ import { BuildOptions, Output } from './types'; import { paths } from '../paths'; import { svgrTemplate } from '../svgrTemplate'; -export const makeConfigs = async ( +export async function makeRollupConfigs( options: BuildOptions, -): Promise => { +): Promise { const configs = new Array(); if (options.outputs.has(Output.cjs) || options.outputs.has(Output.esm)) { @@ -106,7 +106,7 @@ export const makeConfigs = async ( }); } - if (options.outputs.has(Output.types)) { + if (options.outputs.has(Output.types) && !options.useApiExtractor) { const typesInput = paths.resolveTargetRoot( 'dist-types', relativePath(paths.targetRoot, paths.targetDir), @@ -134,4 +134,4 @@ export const makeConfigs = async ( } return configs; -}; +} diff --git a/packages/cli/src/lib/builder/packager.ts b/packages/cli/src/lib/builder/packager.ts index 3e0f1b9a46..96c488fce2 100644 --- a/packages/cli/src/lib/builder/packager.ts +++ b/packages/cli/src/lib/builder/packager.ts @@ -19,8 +19,9 @@ import { rollup, RollupOptions } from 'rollup'; import chalk from 'chalk'; import { relative as relativePath } from 'path'; import { paths } from '../paths'; -import { makeConfigs } from './config'; -import { BuildOptions } from './types'; +import { makeRollupConfigs } from './config'; +import { BuildOptions, Output } from './types'; +import { buildTypeDefinitions } from './buildTypeDefinitions'; export function formatErrorMessage(error: any) { let msg = ''; @@ -71,7 +72,7 @@ export function formatErrorMessage(error: any) { return msg; } -async function build(config: RollupOptions) { +async function rollupBuild(config: RollupOptions) { try { const bundle = await rollup(config); if (config.output) { @@ -103,7 +104,15 @@ export const buildPackage = async (options: BuildOptions) => { /* Errors ignored, this is just a warning */ } - const configs = await makeConfigs(options); + const rollupConfigs = await makeRollupConfigs(options); + await fs.remove(paths.resolveTarget('dist')); - await Promise.all(configs.map(build)); + + const buildTasks = rollupConfigs.map(rollupBuild); + + if (options.outputs.has(Output.types) && options.useApiExtractor) { + buildTasks.push(buildTypeDefinitions()); + } + + await Promise.all(buildTasks); }; diff --git a/packages/cli/src/lib/builder/types.ts b/packages/cli/src/lib/builder/types.ts index e88c388013..a02afef01a 100644 --- a/packages/cli/src/lib/builder/types.ts +++ b/packages/cli/src/lib/builder/types.ts @@ -23,4 +23,5 @@ export enum Output { export type BuildOptions = { outputs: Set; minify?: boolean; + useApiExtractor?: boolean; }; From d241348038d116df93358ba31642c76c80d4d4db Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 15 Jan 2022 13:23:21 +0100 Subject: [PATCH 2/7] cli: split release stages in experimental type build Signed-off-by: Patrik Oldsberg --- packages/cli/src/lib/builder/buildTypeDefinitions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/lib/builder/buildTypeDefinitions.ts b/packages/cli/src/lib/builder/buildTypeDefinitions.ts index b4ca87b825..aec557f99c 100644 --- a/packages/cli/src/lib/builder/buildTypeDefinitions.ts +++ b/packages/cli/src/lib/builder/buildTypeDefinitions.ts @@ -101,7 +101,9 @@ export async function buildTypeDefinitions() { dtsRollup: { enabled: true, - untrimmedFilePath: paths.resolveTarget('dist/index.d.ts'), + untrimmedFilePath: paths.resolveTarget('dist/index.alpha.d.ts'), + betaTrimmedFilePath: paths.resolveTarget('dist/index.beta.d.ts'), + publicTrimmedFilePath: paths.resolveTarget('dist/index.d.ts'), }, newlineKind: 'lf', From 3d79f6321f6773c409935f1b1adda063d45acb84 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 15 Jan 2022 13:24:21 +0100 Subject: [PATCH 3/7] cli: add support for separate release stage types in publishConfig Signed-off-by: Patrik Oldsberg --- packages/cli/src/commands/pack.ts | 41 ++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/pack.ts b/packages/cli/src/commands/pack.ts index 83b866aa88..e646568980 100644 --- a/packages/cli/src/commands/pack.ts +++ b/packages/cli/src/commands/pack.ts @@ -16,12 +16,30 @@ import fs from 'fs-extra'; import { paths } from '../lib/paths'; +import { join as joinPath } from 'path'; -const SKIPPED_KEYS = ['access', 'registry', 'tag']; +const SKIPPED_KEYS = ['access', 'registry', 'tag', 'alphaTypes', 'betaTypes']; const PKG_PATH = 'package.json'; const PKG_BACKUP_PATH = 'package.json-prepack'; +// Writes e.g. alpha/package.json +async function writeReleaseStageEntrypoint(pkg: any, stage: 'alpha' | 'beta') { + await fs.ensureDir(paths.resolveTarget(stage)); + await fs.writeJson( + paths.resolveTarget(stage, PKG_PATH), + { + name: pkg.name, + version: pkg.version, + main: (pkg.publishConfig.main || pkg.main) && '..', + module: (pkg.publishConfig.module || pkg.module) && '..', + browser: (pkg.publishConfig.browser || pkg.browser) && '..', + types: joinPath('..', pkg.publishConfig[`${stage}Types`]), + }, + { encoding: 'utf8', spaces: 2 }, + ); +} + export const pre = async () => { const pkgPath = paths.resolveTarget(PKG_PATH); @@ -29,18 +47,35 @@ export const pre = async () => { const pkg = JSON.parse(pkgContent); await fs.writeFile(PKG_BACKUP_PATH, pkgContent); - for (const key of Object.keys(pkg.publishConfig ?? {})) { + const publishConfig = pkg.publishConfig ?? {}; + for (const key of Object.keys(publishConfig)) { if (!SKIPPED_KEYS.includes(key)) { - pkg[key] = pkg.publishConfig[key]; + pkg[key] = publishConfig[key]; } } await fs.writeJson(pkgPath, pkg, { encoding: 'utf8', spaces: 2 }); + + if (publishConfig.alphaTypes) { + await writeReleaseStageEntrypoint(pkg, 'alpha'); + } + if (publishConfig.betaTypes) { + await writeReleaseStageEntrypoint(pkg, 'beta'); + } }; export const post = async () => { // postpack isn't called by yarn right now, so it needs to be called manually try { await fs.move(PKG_BACKUP_PATH, PKG_PATH, { overwrite: true }); + + // Check if we're shipping types for other release stages, clean up in that case + const pkg = await fs.readJson(PKG_PATH); + if (pkg.publishConfig?.alphaTypes) { + await fs.remove(paths.resolveTarget('alpha')); + } + if (pkg.publishConfig?.betaTypes) { + await fs.remove(paths.resolveTarget('beta')); + } } catch (error) { console.warn( `Failed to restore package.json during postpack, ${error}. ` + From 8b5a7763d5a9454cc8c2a33c3a97af96a7f65cae Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 15 Jan 2022 13:25:40 +0100 Subject: [PATCH 4/7] catalog-model: enable alpha release stage and add AlphaEntity Signed-off-by: Patrik Oldsberg --- .changeset/wise-cooks-admire.md | 21 ++++++++++ packages/catalog-model/api-report.md | 42 +++++++++---------- packages/catalog-model/package.json | 8 ++-- packages/catalog-model/src/entity/Entity.ts | 17 ++++++-- .../catalog-model/src/entity/EntityStatus.ts | 10 ++--- packages/catalog-model/src/entity/index.ts | 7 ++-- packages/catalog-model/src/entity/util.ts | 6 +-- 7 files changed, 73 insertions(+), 38 deletions(-) create mode 100644 .changeset/wise-cooks-admire.md diff --git a/.changeset/wise-cooks-admire.md b/.changeset/wise-cooks-admire.md new file mode 100644 index 0000000000..65f33ffff0 --- /dev/null +++ b/.changeset/wise-cooks-admire.md @@ -0,0 +1,21 @@ +--- +'@backstage/catalog-model': patch +--- + +Added `alpha` release stage type declarations, accessible via `@backstage/catalog-model/alpha`. + +**BREAKING**: The experimental entity `status` field was removed from the base `Entity` type and is now only available on the `AlphaEntity` type from the alpha release stage, along with all accompanying types that were previously marked as `UNSTABLE_`. + +For example, the following import: + +```ts +import { UNSTABLE_EntityStatusItem } from '@backstage/catalog-model'; +``` + +Becomes this: + +```ts +import { EntityStatusItem } from '@backstage/catalog-model/alpha'; +``` + +Note that exports that are only available from the alpha release stage are considered unstable and do not adhere to the semantic versioning of the package. diff --git a/packages/catalog-model/api-report.md b/packages/catalog-model/api-report.md index 9c0c33fa0e..59eeb98924 100644 --- a/packages/catalog-model/api-report.md +++ b/packages/catalog-model/api-report.md @@ -9,6 +9,11 @@ import { JsonValue } from '@backstage/types'; import { SerializedError } from '@backstage/errors'; import * as yup from 'yup'; +// @alpha +export interface AlphaEntity extends Entity { + status?: EntityStatus; +} + // @public @deprecated export const analyzeLocationSchema: yup.SchemaOf<{ location: LocationSpec; @@ -116,7 +121,6 @@ export type Entity = { metadata: EntityMeta; spec?: JsonObject; relations?: EntityRelation[]; - status?: UNSTABLE_EntityStatus; }; // @public @@ -225,6 +229,22 @@ export function entitySchemaValidator( schema?: unknown, ): (data: unknown) => T; +// @alpha +export type EntityStatus = { + items?: EntityStatusItem[]; +}; + +// @alpha +export type EntityStatusItem = { + type: string; + level: EntityStatusLevel; + message: string; + error?: SerializedError; +}; + +// @alpha +export type EntityStatusLevel = 'info' | 'warning' | 'error'; + // @public export class FieldFormatEntityPolicy implements EntityPolicy { constructor(validators?: Validators); @@ -550,22 +570,6 @@ export interface TemplateEntityV1beta2 extends Entity { // @public export const templateEntityV1beta2Validator: KindValidator; -// @alpha -export type UNSTABLE_EntityStatus = { - items?: UNSTABLE_EntityStatusItem[]; -}; - -// @alpha -export type UNSTABLE_EntityStatusItem = { - type: string; - level: UNSTABLE_EntityStatusLevel; - message: string; - error?: SerializedError; -}; - -// @alpha -export type UNSTABLE_EntityStatusLevel = 'info' | 'warning' | 'error'; - // @public interface UserEntityV1alpha1 extends Entity { // (undocumented) @@ -603,8 +607,4 @@ export type Validators = { // @public export const VIEW_URL_ANNOTATION = 'backstage.io/view-url'; - -// Warnings were encountered during analysis: -// -// src/entity/Entity.d.ts:41:5 - (ae-incompatible-release-tags) The symbol "status" is marked as @public, but its signature references "UNSTABLE_EntityStatus" which is marked as @alpha ``` diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index c1a13d00f2..bb93259a9f 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -10,7 +10,8 @@ "access": "public", "main": "dist/index.cjs.js", "module": "dist/index.esm.js", - "types": "dist/index.d.ts" + "types": "dist/index.d.ts", + "alphaTypes": "dist/index.alpha.d.ts" }, "homepage": "https://backstage.io", "repository": { @@ -22,7 +23,7 @@ "backstage" ], "scripts": { - "build": "backstage-cli build", + "build": "backstage-cli build --experimental-type-build", "lint": "backstage-cli lint", "test": "backstage-cli test", "prepack": "backstage-cli prepack", @@ -48,6 +49,7 @@ "yaml": "^1.9.2" }, "files": [ - "dist" + "dist", + "alpha" ] } diff --git a/packages/catalog-model/src/entity/Entity.ts b/packages/catalog-model/src/entity/Entity.ts index 9d65d3e8f3..f56774025c 100644 --- a/packages/catalog-model/src/entity/Entity.ts +++ b/packages/catalog-model/src/entity/Entity.ts @@ -16,7 +16,7 @@ import { JsonObject } from '@backstage/types'; import { EntityName } from '../types'; -import { UNSTABLE_EntityStatus } from './EntityStatus'; +import { EntityStatus } from './EntityStatus'; /** * The parts of the format that's common to all versions/kinds of entity. @@ -53,15 +53,26 @@ export type Entity = { * The relations that this entity has with other entities. */ relations?: EntityRelation[]; +}; +/** + * A version of the {@link Entity} type that contains unstable alpha fields. + * + * @remarks + * + * Available via the `@backstage/catalog-model/alpha` import. + * + * @alpha + */ +export interface AlphaEntity extends Entity { /** * The current status of the entity, as claimed by various sources. * * The keys are implementation defined and the values can be any JSON object * with semantics that match that implementation. */ - status?: UNSTABLE_EntityStatus; -}; + status?: EntityStatus; +} /** * Metadata fields common to all versions/kinds of entity. diff --git a/packages/catalog-model/src/entity/EntityStatus.ts b/packages/catalog-model/src/entity/EntityStatus.ts index 8f79fbd615..5ffd8a8e06 100644 --- a/packages/catalog-model/src/entity/EntityStatus.ts +++ b/packages/catalog-model/src/entity/EntityStatus.ts @@ -21,18 +21,18 @@ import { SerializedError } from '@backstage/errors'; * * @alpha */ -export type UNSTABLE_EntityStatus = { +export type EntityStatus = { /** * Specific status item on a well known format. */ - items?: UNSTABLE_EntityStatusItem[]; + items?: EntityStatusItem[]; }; /** * A specific status item on a well known format. * @alpha */ -export type UNSTABLE_EntityStatusItem = { +export type EntityStatusItem = { /** * The type of status as a unique key per source. */ @@ -43,7 +43,7 @@ export type UNSTABLE_EntityStatusItem = { * entry may apply to a different, newer version of the data than what is * being returned in the catalog response. */ - level: UNSTABLE_EntityStatusLevel; + level: EntityStatusLevel; /** * A brief message describing the status, intended for human consumption. */ @@ -58,7 +58,7 @@ export type UNSTABLE_EntityStatusItem = { * Each entity status item has a level, describing its severity. * @alpha */ -export type UNSTABLE_EntityStatusLevel = +export type EntityStatusLevel = | 'info' // Only informative data | 'warning' // Warnings were found | 'error'; // Errors were found diff --git a/packages/catalog-model/src/entity/index.ts b/packages/catalog-model/src/entity/index.ts index eac55ea325..ce1e76a20b 100644 --- a/packages/catalog-model/src/entity/index.ts +++ b/packages/catalog-model/src/entity/index.ts @@ -21,6 +21,7 @@ export { VIEW_URL_ANNOTATION, } from './constants'; export type { + AlphaEntity, Entity, EntityLink, EntityMeta, @@ -29,9 +30,9 @@ export type { } from './Entity'; export type { EntityEnvelope } from './EntityEnvelope'; export type { - UNSTABLE_EntityStatus, - UNSTABLE_EntityStatusItem, - UNSTABLE_EntityStatusLevel, + EntityStatus, + EntityStatusItem, + EntityStatusLevel, } from './EntityStatus'; export * from './policies'; export { diff --git a/packages/catalog-model/src/entity/util.ts b/packages/catalog-model/src/entity/util.ts index ce6415afde..4506a8d852 100644 --- a/packages/catalog-model/src/entity/util.ts +++ b/packages/catalog-model/src/entity/util.ts @@ -16,7 +16,7 @@ import lodash from 'lodash'; import { v4 as uuidv4 } from 'uuid'; -import { Entity } from './Entity'; +import { Entity, AlphaEntity } from './Entity'; /** * Generates a new random UID for an entity. @@ -89,9 +89,9 @@ export function entityHasChanges(previous: Entity, next: Entity): boolean { // Remove things that we explicitly do not compare delete e1.relations; - delete e1.status; + delete (e1 as AlphaEntity).status; delete e2.relations; - delete e2.status; + delete (e2 as AlphaEntity).status; return !lodash.isEqual(e1, e2); } From 2b27e49eb1bba6f2d02ef6d729e7d6d89791188b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 15 Jan 2022 16:47:39 +0100 Subject: [PATCH 5/7] catalog,catalog-backend: update to catalog-model status change Signed-off-by: Patrik Oldsberg --- .changeset/wicked-bobcats-pull.md | 6 ++++++ plugins/catalog-backend/src/stitching/Stitcher.ts | 8 ++++---- .../EntityProcessingErrorsPanel.test.tsx | 8 ++++---- .../EntityProcessingErrorsPanel.tsx | 7 ++++--- 4 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 .changeset/wicked-bobcats-pull.md diff --git a/.changeset/wicked-bobcats-pull.md b/.changeset/wicked-bobcats-pull.md new file mode 100644 index 0000000000..a2af286044 --- /dev/null +++ b/.changeset/wicked-bobcats-pull.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog': patch +'@backstage/plugin-catalog-backend': patch +--- + +Internal update to match status field changes in `@backstage/catalog-model`. diff --git a/plugins/catalog-backend/src/stitching/Stitcher.ts b/plugins/catalog-backend/src/stitching/Stitcher.ts index b4a693a4fa..9ec028ed35 100644 --- a/plugins/catalog-backend/src/stitching/Stitcher.ts +++ b/plugins/catalog-backend/src/stitching/Stitcher.ts @@ -16,9 +16,9 @@ import { ENTITY_STATUS_CATALOG_PROCESSING_TYPE } from '@backstage/catalog-client'; import { - Entity, + AlphaEntity, parseEntityRef, - UNSTABLE_EntityStatusItem, + EntityStatusItem, } from '@backstage/catalog-model'; import { SerializedError, stringifyError } from '@backstage/errors'; import { Knex } from 'knex'; @@ -152,9 +152,9 @@ export class Stitcher { // Grab the processed entity and stitch all of the relevant data into // it - const entity = JSON.parse(processedEntity) as Entity; + const entity = JSON.parse(processedEntity) as AlphaEntity; const isOrphan = Number(incomingReferenceCount) === 0; - let statusItems: UNSTABLE_EntityStatusItem[] = []; + let statusItems: EntityStatusItem[] = []; if (isOrphan) { this.logger.debug(`${entityRef} is an orphan`); diff --git a/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.test.tsx b/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.test.tsx index fcc4475f6a..ae5a4684b1 100644 --- a/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.test.tsx +++ b/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.test.tsx @@ -24,7 +24,7 @@ import { import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; import React from 'react'; import { EntityProcessingErrorsPanel } from './EntityProcessingErrorsPanel'; -import { Entity, getEntityName } from '@backstage/catalog-model'; +import { AlphaEntity, getEntityName } from '@backstage/catalog-model'; import { ApiProvider } from '@backstage/core-app-api'; describe('', () => { @@ -34,7 +34,7 @@ describe('', () => { const apis = TestApiRegistry.from([catalogApiRef, { getEntityAncestors }]); it('renders EntityProcessErrors if the entity has errors', async () => { - const entity: Entity = { + const entity: AlphaEntity = { apiVersion: 'v1', kind: 'Component', metadata: { @@ -122,7 +122,7 @@ describe('', () => { }); it('renders EntityProcessErrors if the parent entity has errors', async () => { - const entity: Entity = { + const entity: AlphaEntity = { apiVersion: 'v1', kind: 'Component', metadata: { @@ -136,7 +136,7 @@ describe('', () => { }, }; - const parent: Entity = { + const parent: AlphaEntity = { apiVersion: 'v1', kind: 'Component', metadata: { diff --git a/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.tsx b/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.tsx index 8469c7d4e4..e4df011b38 100644 --- a/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.tsx +++ b/plugins/catalog/src/components/EntityProcessingErrorsPanel/EntityProcessingErrorsPanel.tsx @@ -16,8 +16,9 @@ import { Entity, + AlphaEntity, stringifyEntityRef, - UNSTABLE_EntityStatusItem, + EntityStatusItem, compareEntityToRef, } from '@backstage/catalog-model'; import { @@ -36,7 +37,7 @@ import { useApi, ApiHolder } from '@backstage/core-plugin-api'; import useAsync from 'react-use/lib/useAsync'; import { SerializedError } from '@backstage/errors'; -const errorFilter = (i: UNSTABLE_EntityStatusItem) => +const errorFilter = (i: EntityStatusItem) => i.error && i.level === 'error' && i.type === ENTITY_STATUS_CATALOG_PROCESSING_TYPE; @@ -55,7 +56,7 @@ async function getOwnAndAncestorsErrors( const ancestors = await catalogApi.getEntityAncestors({ entityRef }); const items = ancestors.items .map(item => { - const statuses = item.entity.status?.items ?? []; + const statuses = (item.entity as AlphaEntity).status?.items ?? []; const errors = statuses .filter(errorFilter) .map(e => e.error) From 26cd3e2d019978c4cb54867e6c7f460c386bd8bc Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 16 Jan 2022 11:14:26 +0100 Subject: [PATCH 6/7] cli: set skipLibCheck for api extractor Signed-off-by: Patrik Oldsberg --- packages/cli/src/lib/builder/buildTypeDefinitions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/src/lib/builder/buildTypeDefinitions.ts b/packages/cli/src/lib/builder/buildTypeDefinitions.ts index aec557f99c..ba67532bdb 100644 --- a/packages/cli/src/lib/builder/buildTypeDefinitions.ts +++ b/packages/cli/src/lib/builder/buildTypeDefinitions.ts @@ -96,6 +96,7 @@ export async function buildTypeDefinitions() { bundledPackages: [], compiler: { + skipLibCheck: true, tsconfigFilePath: paths.resolveTargetRoot('tsconfig.json'), }, From 2a42d573d2f76d3b513f876b18314e5d02158a06 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 16 Jan 2022 11:48:51 +0100 Subject: [PATCH 7/7] cangesets: add changeset for the exterimental cli type builds Signed-off-by: Patrik Oldsberg --- .changeset/nine-trees-begin.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/nine-trees-begin.md diff --git a/.changeset/nine-trees-begin.md b/.changeset/nine-trees-begin.md new file mode 100644 index 0000000000..b274defea5 --- /dev/null +++ b/.changeset/nine-trees-begin.md @@ -0,0 +1,9 @@ +--- +'@backstage/cli': patch +--- + +Introduced a new `--experimental-type-build` option to the various package build commands. This option switches the type definition build to be executed using API Extractor rather than a `rollup` plugin. In order for this experimental switch to work, you must also have `@microsoft/api-extractor` installed within your project, as it is an optional peer dependency. + +The biggest difference between the existing mode and the experimental one is that rather than just building a single `dist/index.d.ts` file, API Extractor will output `index.d.ts`, `index.beta.d.ts`, and `index.alpha.d.ts`, all in the `dist` directory. Each of these files will have exports from more unstable release stages stripped, meaning that `index.d.ts` will omit all exports marked with `@alpha` or `@beta`, while `index.beta.d.ts` will omit all exports marked with `@alpha`. + +In addition, the `prepack` command now has support for `alphaTypes` and `betaTypes` fields in the `publishConfig` of `package.json`. These optional fields can be pointed to `dist/types.alpha.d.ts` and `dist/types.beta.d.ts` respectively, which will cause `/alpha` and `/beta` entry points to be generated for the package. See the [`@backstage/catalog-model`](https://github.com/backstage/backstage/blob/master/packages/catalog-model/package.json) package for an example of how this can be used in practice.