From 808b53d492679d5e4e83932e6397438c56cb5f55 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Wed, 7 Dec 2022 11:43:21 +0100 Subject: [PATCH] change the options to only allow csv Signed-off-by: Juan Pablo Garcia Ripa --- .changeset/lemon-coats-camp.md | 7 +- packages/repo-tools/cli-report.md | 10 +- .../commands/api-reports/api-reports.test.ts | 194 +++++++----------- .../src/commands/api-reports/api-reports.ts | 58 +++--- ...rateTSC.ts => generateTypeDeclarations.ts} | 2 +- packages/repo-tools/src/commands/index.ts | 14 +- 6 files changed, 120 insertions(+), 165 deletions(-) rename packages/repo-tools/src/commands/api-reports/{generateTSC.ts => generateTypeDeclarations.ts} (95%) diff --git a/.changeset/lemon-coats-camp.md b/.changeset/lemon-coats-camp.md index ae31098e2f..f30a20f432 100644 --- a/.changeset/lemon-coats-camp.md +++ b/.changeset/lemon-coats-camp.md @@ -1,12 +1,11 @@ --- -'@backstage/repo-tools': minor +'@backstage/repo-tools': patch --- Add new command options to the `api-report` -- added `--allow-warnings`, `-a` to continue processing packages if some packages have warnings +- added `--allow-warnings`, `-a` to continue processing packages if selected packages have warnings +- added `--allow-all-warnings` to continue processing packages any packages have warnings - added `--omit-messages`, `-o` to pass some warnings messages code to be omitted from the api-report.md files -- added `--paths`, `-p` to select packages path to process - The `paths` argument for this command now takes as default the value on `workspaces.packages` inside the root package.json -- Removed the `paths` argument replaced by the option `--paths` - change the path resolution to use the `@backstage/cli-common` packages instead diff --git a/packages/repo-tools/cli-report.md b/packages/repo-tools/cli-report.md index d3446e0262..3378d20ea7 100644 --- a/packages/repo-tools/cli-report.md +++ b/packages/repo-tools/cli-report.md @@ -12,7 +12,7 @@ Options: -h, --help Commands: - api-reports [options] + api-reports [options] [paths...] type-deps help [command] ``` @@ -20,15 +20,15 @@ Commands: ### `backstage-repo-tools api-reports` ``` -Usage: backstage-repo-tools api-reports [options] +Usage: backstage-repo-tools api-reports [options] [paths...] Options: - -p --paths [paths...] --ci --tsc --docs - -a, --allow-warnings [allowWarningsPaths...] - -o, --omit-messages + -a, --allow-warnings + --allow-all-warnings + -o, --omit-messages -h, --help ``` diff --git a/packages/repo-tools/src/commands/api-reports/api-reports.test.ts b/packages/repo-tools/src/commands/api-reports/api-reports.test.ts index fa5bfd6973..7121d8a06f 100644 --- a/packages/repo-tools/src/commands/api-reports/api-reports.test.ts +++ b/packages/repo-tools/src/commands/api-reports/api-reports.test.ts @@ -26,9 +26,9 @@ import { } from './api-extractor'; import { buildApiReports } from './api-reports'; -import { generateTSC } from './generateTSC'; +import { generateTypeDeclarations } from './generateTypeDeclarations'; -jest.mock('./generateTSC'); +jest.mock('./generateTypeDeclarations'); // create mocks for the dependencies of the `buildApiReports` function jest.mock('./api-extractor', () => ({ createTemporaryTsConfig: jest.fn(), @@ -44,17 +44,17 @@ jest.mock('./api-extractor', () => ({ buildDocs: jest.fn(), })); -const paths = pathsLib.paths; +const projectPaths = pathsLib.paths; -jest.spyOn(paths, 'targetRoot', 'get').mockReturnValue('/root'); -jest.spyOn(paths, 'resolveTargetRoot').mockImplementation((...path) => { +jest.spyOn(projectPaths, 'targetRoot', 'get').mockReturnValue('/root'); +jest.spyOn(projectPaths, 'resolveTargetRoot').mockImplementation((...path) => { return resolvePath('/root', ...path); }); describe('buildApiReports', () => { beforeEach(() => { mockFs({ - [paths.targetRoot]: { + [projectPaths.targetRoot]: { 'package.json': JSON.stringify({ workspaces: { packages: ['packages/*', 'plugins/*'] }, }), @@ -89,8 +89,9 @@ describe('buildApiReports', () => { }); it('should run whitout any options', async () => { const opts = {}; + const paths: string[] = []; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(categorizePackageDirs).toHaveBeenCalledWith([ 'packages/package-a', @@ -100,7 +101,7 @@ describe('buildApiReports', () => { 'plugins/plugin-c', ]); - expect(generateTSC).not.toHaveBeenCalled(); + expect(generateTypeDeclarations).not.toHaveBeenCalled(); expect(runApiExtraction).toHaveBeenCalledWith({ packageDirs: [ 'packages/package-a', @@ -110,7 +111,7 @@ describe('buildApiReports', () => { 'plugins/plugin-c', ], tsconfigFilePath: '/root/tsconfig.json', - allowWarnings: undefined, + allowWarnings: [], omitMessages: [], isLocalBuild: true, outputDir: '/root/node_modules/.cache/api-extractor', @@ -132,11 +133,10 @@ describe('buildApiReports', () => { describe('paths', () => { it('should generate API reports for one specific package', async () => { - const opts = { - paths: ['packages/package-a'], - }; + const paths = ['packages/package-a']; + const opts = {}; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(categorizePackageDirs).toHaveBeenCalledWith([ 'packages/package-a', @@ -145,7 +145,7 @@ describe('buildApiReports', () => { expect(runApiExtraction).toHaveBeenCalledWith({ packageDirs: ['packages/package-a'], tsconfigFilePath: '/root/tsconfig.json', - allowWarnings: undefined, + allowWarnings: [], omitMessages: [], isLocalBuild: true, outputDir: '/root/node_modules/.cache/api-extractor', @@ -158,11 +158,10 @@ describe('buildApiReports', () => { expect(buildDocs).not.toHaveBeenCalled(); }); it('should generate API reports for multiple specific packages', async () => { - const opts = { - paths: ['packages/package-a', 'packages/package-b'], - }; + const paths = ['packages/package-a', 'packages/package-b']; + const opts = {}; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(categorizePackageDirs).toHaveBeenCalledWith([ 'packages/package-a', @@ -172,7 +171,7 @@ describe('buildApiReports', () => { expect(runApiExtraction).toHaveBeenCalledWith({ packageDirs: ['packages/package-a', 'packages/package-b'], tsconfigFilePath: '/root/tsconfig.json', - allowWarnings: undefined, + allowWarnings: [], omitMessages: [], isLocalBuild: true, outputDir: '/root/node_modules/.cache/api-extractor', @@ -185,11 +184,10 @@ describe('buildApiReports', () => { expect(buildDocs).not.toHaveBeenCalled(); }); it('should generate API reports for all packages matching the glob pattern', async () => { - const opts = { - paths: ['packages/*'], - }; + const paths = ['packages/*']; + const opts = {}; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(categorizePackageDirs).toHaveBeenCalledWith([ 'packages/package-a', @@ -199,7 +197,7 @@ describe('buildApiReports', () => { expect(runApiExtraction).toHaveBeenCalledWith({ packageDirs: ['packages/package-a', 'packages/package-b'], tsconfigFilePath: '/root/tsconfig.json', - allowWarnings: undefined, + allowWarnings: [], omitMessages: [], isLocalBuild: true, outputDir: '/root/node_modules/.cache/api-extractor', @@ -213,11 +211,10 @@ describe('buildApiReports', () => { }); it('should generate API reports for all packages matching multiple glob patterns', async () => { - const opts = { - paths: ['packages/*', 'plugins/*a'], - }; + const paths = ['packages/*', 'plugins/*a']; + const opts = {}; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(categorizePackageDirs).toHaveBeenCalledWith([ 'packages/package-a', @@ -232,7 +229,7 @@ describe('buildApiReports', () => { 'plugins/plugin-a', ], tsconfigFilePath: '/root/tsconfig.json', - allowWarnings: undefined, + allowWarnings: [], omitMessages: [], isLocalBuild: true, outputDir: '/root/node_modules/.cache/api-extractor', @@ -250,11 +247,10 @@ describe('buildApiReports', () => { }); it('should generate API reports for specific packages and glob pattern', async () => { - const opts = { - paths: ['packages/package-a', 'plugins/*'], - }; + const opts = {}; + const paths = ['packages/package-a', 'plugins/*']; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(categorizePackageDirs).toHaveBeenCalledWith([ 'packages/package-a', @@ -271,7 +267,7 @@ describe('buildApiReports', () => { 'plugins/plugin-c', ], tsconfigFilePath: '/root/tsconfig.json', - allowWarnings: undefined, + allowWarnings: [], omitMessages: [], isLocalBuild: true, outputDir: '/root/node_modules/.cache/api-extractor', @@ -290,31 +286,13 @@ describe('buildApiReports', () => { }); }); describe('allowWarnings', () => { - it('should accept boolean values', async () => { - const opts = { - paths: ['packages/*'], - allowWarnings: true, - }; - - await buildApiReports(opts); - - expect(runApiExtraction).toHaveBeenCalledWith({ - packageDirs: ['packages/package-a', 'packages/package-b'], - tsconfigFilePath: '/root/tsconfig.json', - allowWarnings: true, - omitMessages: [], - isLocalBuild: true, - outputDir: '/root/node_modules/.cache/api-extractor', - }); - }); - it('should accept single path value', async () => { const opts = { - paths: ['packages/*'], - allowWarnings: ['packages/package-a'], + allowWarnings: 'packages/package-a', }; + const paths = ['packages/*']; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(runApiExtraction).toHaveBeenCalledWith({ packageDirs: ['packages/package-a', 'packages/package-b'], @@ -326,31 +304,13 @@ describe('buildApiReports', () => { }); }); - it('should accept multiple path values as array', async () => { - const opts = { - paths: ['packages/*'], - allowWarnings: ['packages/package-a', 'packages/package-b'], - }; - - await buildApiReports(opts); - - expect(runApiExtraction).toHaveBeenCalledWith({ - packageDirs: ['packages/package-a', 'packages/package-b'], - tsconfigFilePath: '/root/tsconfig.json', - allowWarnings: ['packages/package-a', 'packages/package-b'], - omitMessages: [], - isLocalBuild: true, - outputDir: '/root/node_modules/.cache/api-extractor', - }); - }); - it('should accept multiple path values as comma separated string', async () => { const opts = { - paths: ['packages/*'], - allowWarnings: ['packages/package-a,packages/package-b'], + allowWarnings: 'packages/package-a,packages/package-b', }; + const paths = ['packages/*']; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(runApiExtraction).toHaveBeenCalledWith({ packageDirs: ['packages/package-a', 'packages/package-b'], @@ -364,11 +324,11 @@ describe('buildApiReports', () => { it('should accept multiple path values as comma separated string with spaces', async () => { const opts = { - paths: ['packages/*'], - allowWarnings: ['packages/package-a, packages/package-b'], + allowWarnings: 'packages/package-a, packages/package-b', }; + const paths = ['packages/*']; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(runApiExtraction).toHaveBeenCalledWith({ packageDirs: ['packages/package-a', 'packages/package-b'], @@ -380,54 +340,56 @@ describe('buildApiReports', () => { }); }); }); + describe('allowAllWarnings', () => { + it('should accept boolean values', async () => { + const opts = { + allowAllWarnings: true, + }; + const paths = ['packages/*']; + + await buildApiReports(paths, opts); + + expect(runApiExtraction).toHaveBeenCalledWith({ + packageDirs: ['packages/package-a', 'packages/package-b'], + tsconfigFilePath: '/root/tsconfig.json', + allowWarnings: true, + omitMessages: [], + isLocalBuild: true, + outputDir: '/root/node_modules/.cache/api-extractor', + }); + }); + }); describe('omitMessages', () => { it('should accept single message value', async () => { const opts = { - paths: ['packages/*'], - omitMessages: ['ae-missing-release-tag'], + omitMessages: 'ae-missing-release-tag', }; + const paths = ['packages/*']; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(runApiExtraction).toHaveBeenCalledWith({ packageDirs: ['packages/package-a', 'packages/package-b'], tsconfigFilePath: '/root/tsconfig.json', - allowWarnings: undefined, + allowWarnings: [], omitMessages: ['ae-missing-release-tag'], isLocalBuild: true, outputDir: '/root/node_modules/.cache/api-extractor', }); }); - it('should accept multiple message values as array', async () => { - const opts = { - paths: ['packages/*'], - omitMessages: ['ae-missing-release-tag', 'ae-missing-annotations'], - }; - - await buildApiReports(opts); - - expect(runApiExtraction).toHaveBeenCalledWith({ - packageDirs: ['packages/package-a', 'packages/package-b'], - tsconfigFilePath: '/root/tsconfig.json', - allowWarnings: undefined, - omitMessages: ['ae-missing-release-tag', 'ae-missing-annotations'], - isLocalBuild: true, - outputDir: '/root/node_modules/.cache/api-extractor', - }); - }); it('should accept multiple message values as comma separated string', async () => { const opts = { - paths: ['packages/*'], - omitMessages: ['ae-missing-release-tag,ae-missing-annotations'], + omitMessages: 'ae-missing-release-tag,ae-missing-annotations', }; + const paths = ['packages/*']; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(runApiExtraction).toHaveBeenCalledWith({ packageDirs: ['packages/package-a', 'packages/package-b'], tsconfigFilePath: '/root/tsconfig.json', - allowWarnings: undefined, + allowWarnings: [], omitMessages: ['ae-missing-release-tag', 'ae-missing-annotations'], isLocalBuild: true, outputDir: '/root/node_modules/.cache/api-extractor', @@ -436,16 +398,16 @@ describe('buildApiReports', () => { it('should accept multiple message values as comma separated string with spaces', async () => { const opts = { - paths: ['packages/*'], - omitMessages: ['ae-missing-release-tag, ae-missing-annotations'], + omitMessages: 'ae-missing-release-tag, ae-missing-annotations', }; + const paths = ['packages/*']; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(runApiExtraction).toHaveBeenCalledWith({ packageDirs: ['packages/package-a', 'packages/package-b'], tsconfigFilePath: '/root/tsconfig.json', - allowWarnings: undefined, + allowWarnings: [], omitMessages: ['ae-missing-release-tag', 'ae-missing-annotations'], isLocalBuild: true, outputDir: '/root/node_modules/.cache/api-extractor', @@ -455,16 +417,16 @@ describe('buildApiReports', () => { describe('isCI', () => { it('should set localBuild to false if CI option is passed', async () => { const opts = { - paths: ['packages/*'], ci: true, }; + const paths = ['packages/*']; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(runApiExtraction).toHaveBeenCalledWith({ packageDirs: ['packages/package-a', 'packages/package-b'], tsconfigFilePath: '/root/tsconfig.json', - allowWarnings: undefined, + allowWarnings: [], omitMessages: [], isLocalBuild: false, outputDir: '/root/node_modules/.cache/api-extractor', @@ -478,11 +440,11 @@ describe('buildApiReports', () => { describe('docs', () => { it('should run typedoc if docs option is passed', async () => { const opts = { - paths: ['packages/*'], docs: true, }; + const paths = ['packages/*']; - await buildApiReports(opts); + await buildApiReports(paths, opts); expect(buildDocs).toHaveBeenCalledWith({ inputDir: '/root/node_modules/.cache/api-extractor', @@ -493,13 +455,13 @@ describe('buildApiReports', () => { describe('tsc', () => { it('should run tsc if tsc option is passed', async () => { const opts = { - paths: ['packages/*'], tsc: true, }; + const paths = ['packages/*']; - await buildApiReports(opts); + await buildApiReports(paths, opts); - expect(generateTSC).toHaveBeenCalled(); + expect(generateTypeDeclarations).toHaveBeenCalled(); }); }); }); diff --git a/packages/repo-tools/src/commands/api-reports/api-reports.ts b/packages/repo-tools/src/commands/api-reports/api-reports.ts index b59f498d7b..6270072912 100644 --- a/packages/repo-tools/src/commands/api-reports/api-reports.ts +++ b/packages/repo-tools/src/commands/api-reports/api-reports.ts @@ -24,18 +24,18 @@ import { buildDocs, } from './api-extractor'; import { findPackageDirs, paths as cliPaths } from '../../lib/paths'; -import { generateTSC } from './generateTSC'; +import { generateTypeDeclarations } from './generateTypeDeclarations'; type Options = { ci?: boolean; docs?: boolean; tsc?: boolean; - paths?: string[]; - allowWarnings?: string[] | boolean; - omitMessages?: string[]; + allowWarnings?: string; + allowAllWarnings?: boolean; + omitMessages?: string; } & OptionValues; -export const buildApiReports = async (opts: Options) => { +export const buildApiReports = async (paths: string[] = [], opts: Options) => { const tmpDir = cliPaths.resolveTargetRoot( './node_modules/.cache/api-extractor', ); @@ -43,15 +43,16 @@ export const buildApiReports = async (opts: Options) => { const isCiBuild = opts.ci; const isDocsBuild = opts.docs; const runTsc = opts.tsc; - - const parsedPaths = parseArrayOption(opts.paths); - const isAllPackages = !Array.isArray(parsedPaths) || !parsedPaths?.length; - const selectedPaths = isAllPackages ? await getWorkspacePkgs() : parsedPaths; - const selectedPackageDirs = await findPackageDirs(selectedPaths); - const allowWarnings = parseArrayOption(opts.allowWarnings); + const allowAllWarnings = opts.allowAllWarnings; const omitMessages = parseArrayOption(opts.omitMessages); + const isAllPackages = !paths?.length; + const selectedPaths = isAllPackages + ? await getWorkspacePackagePathPatterns() + : paths; + const selectedPackageDirs = await findPackageDirs(selectedPaths); + if (isAllPackages && !isCiBuild && !isDocsBuild) { console.log(''); console.log( @@ -59,7 +60,7 @@ export const buildApiReports = async (opts: Options) => { ); console.log(''); console.log( - ' yarn build:api-reports -p packages/config -p packages/core-plugin-api,plugins/*', + ' yarn build:api-reports packages/config packages/core-plugin-api plugins/*', ); console.log(''); } @@ -73,7 +74,7 @@ export const buildApiReports = async (opts: Options) => { if (runTsc) { console.log('# Compiling TypeScript'); - await generateTSC(tsconfigFilePath); + await generateTypeDeclarations(tsconfigFilePath); } const { tsPackageDirs, cliPackageDirs } = await categorizePackageDirs( @@ -87,7 +88,7 @@ export const buildApiReports = async (opts: Options) => { outputDir: tmpDir, isLocalBuild: !isCiBuild, tsconfigFilePath, - allowWarnings, + allowWarnings: allowAllWarnings || allowWarnings, omitMessages: Array.isArray(omitMessages) ? omitMessages : [], }); } @@ -99,7 +100,6 @@ export const buildApiReports = async (opts: Options) => { }); } - console.log(isDocsBuild); if (isDocsBuild) { console.log('# Generating package documentation'); await buildDocs({ @@ -116,7 +116,7 @@ export const buildApiReports = async (opts: Options) => { * * @returns {Promise} The list of package names, or `undefined` if not found. */ -async function getWorkspacePkgs() { +async function getWorkspacePackagePathPatterns() { const pkgJson = await fs .readJson(cliPaths.resolveTargetRoot('package.json')) .catch(error => { @@ -130,28 +130,22 @@ async function getWorkspacePkgs() { } /** - * Splits each string in the input array on comma, and returns an array of the resulting substrings. - * If the input array is `undefined`, returns `undefined`. If the input value is `true` or `false`, - * returns the value as-is. + * Splits the input string on comma, and returns an array of the resulting substrings. + * for `undefined` or an empty string, returns an empty array. * - * @param value An array of strings to be split on comma, or a boolean value (inherithed from commanderjs array args). - * @returns An array of the resulting substrings, the original boolean value, or `undefined` if the input value is `undefined`. + * @param value A string to be split on comma. + * @returns An array of the resulting substrings, or an empty array if the input value is `undefined` or an empty string. * * @example - * parseOption(['foo,bar,baz']) + * parseOption('foo,bar,baz') * // returns ['foo', 'bar', 'baz'] * - * parseOption(true) - * // returns true + * parseOption('') + * // returns [] * * parseOption() - * // returns undefined + * // returns [] */ -function parseArrayOption(value: string[] | boolean | undefined) { - if (typeof value === 'boolean') { - return value; - } - return value?.flatMap((str: string) => - str.includes(',') ? str.split(',').map(s => s.trim()) : str, - ); +function parseArrayOption(value: string | undefined) { + return value ? value.split(',').map(s => s.trim()) : []; } diff --git a/packages/repo-tools/src/commands/api-reports/generateTSC.ts b/packages/repo-tools/src/commands/api-reports/generateTypeDeclarations.ts similarity index 95% rename from packages/repo-tools/src/commands/api-reports/generateTSC.ts rename to packages/repo-tools/src/commands/api-reports/generateTypeDeclarations.ts index 5c10c5085f..58f4117bc2 100644 --- a/packages/repo-tools/src/commands/api-reports/generateTSC.ts +++ b/packages/repo-tools/src/commands/api-reports/generateTypeDeclarations.ts @@ -28,7 +28,7 @@ import { paths as cliPaths } from '../../lib/paths'; * @returns {Promise} A promise that resolves when the declaration files have been generated. */ -export async function generateTSC(tsconfigFilePath: string) { +export async function generateTypeDeclarations(tsconfigFilePath: string) { await fs.remove(cliPaths.resolveTargetRoot('dist-types')); const { status } = spawnSync( 'yarn', diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index 3f3b2cdbe7..f7a16b6d4e 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -20,21 +20,21 @@ import { exitWithError } from '../lib/errors'; export function registerCommands(program: Command) { program - .command('api-reports') - .option( - '-p --paths [paths...]', - 'paths of package folder to extract API reports, `workspaces.packages` from root packages.json by default. Allows glob patterns and comma separated values', - ) + .command('api-reports [paths...]') .option('--ci', 'CI run checks that there is no changes on API reports') .option('--tsc', 'executes the tsc compilation before extracting the APIs') .option('--docs', 'generates the api documentation') .option( - '-a, --allow-warnings [allowWarningsPaths...]', + '-a, --allow-warnings ', 'continue processing packages after getting errors on selected packages Allows glob patterns and comma separated values (i.e. packages/core,plugins/core-*)', + ) + .option( + '--allow-all-warnings', + 'continue processing packages after getting errors on all packages', false, ) .option( - '-o, --omit-messages ', + '-o, --omit-messages ', 'select some message code to be omited on the API Extractor (comma separated values i.e ae-cyclic-inherit-doc,ae-missing-getter )', ) .description('Generate an API report for selected packages')