change the options to only allow csv

Signed-off-by: Juan Pablo Garcia Ripa <juanpablog@spotify.com>
This commit is contained in:
Juan Pablo Garcia Ripa
2022-12-07 11:43:21 +01:00
parent e5dc85a821
commit 808b53d492
6 changed files with 120 additions and 165 deletions
+3 -4
View File
@@ -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
+5 -5
View File
@@ -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 <messageCodes...>
-a, --allow-warnings <allowWarningsPaths>
--allow-all-warnings
-o, --omit-messages <messageCodes>
-h, --help
```
@@ -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();
});
});
});
@@ -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<string[] | undefined>} 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()) : [];
}
@@ -28,7 +28,7 @@ import { paths as cliPaths } from '../../lib/paths';
* @returns {Promise<void>} 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',
+7 -7
View File
@@ -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 <allowWarningsPaths>',
'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 <messageCodes...>',
'-o, --omit-messages <messageCodes>',
'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')