Merge branch 'master' into master

This commit is contained in:
Kenneth Chau 2020-04-13 09:01:05 -07:00 коммит произвёл GitHub
Родитель f2ff882bf5 85874d7d37
Коммит 6cbae043a7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
39 изменённых файлов: 1689 добавлений и 1211 удалений

20
.github/workflows/stale.yml поставляемый
Просмотреть файл

@ -1,20 +0,0 @@
name: 'Close stale issues'
on:
schedule:
- cron: '0 0 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Issue seems to have gone stale.'
stale-pr-message: 'This PR seems to have gone stale.'
days-before-stale: 21
days-before-close: 3
stale-issue-label: 'stale-issue'
exempt-issue-label: 'awaiting-approval'
stale-pr-label: 'stale-pr'
exempt-pr-label: 'awaiting-approval'

Просмотреть файл

@ -16,11 +16,11 @@
"@types/jest": "^23.3.13",
"@types/fs-extra": "^5.0.5",
"create-just": "1.2.3",
"just-scripts": ">=0.36.1 <1.0.0",
"just-scripts": ">=0.38.0 <1.0.0",
"just-stack-monorepo": "1.0.6",
"just-stack-single-lib": "1.0.4",
"just-stack-uifabric": "1.0.2",
"just-scripts-utils": ">=0.8.4 <1.0.0",
"just-scripts-utils": ">=0.9.0 <1.0.0",
"fs-extra": "^7.0.1",
"ts-jest": "^24.0.1",
"jest": "^24.0.0"

Просмотреть файл

@ -1,6 +1,20 @@
{
"name": "create-just",
"entries": [
{
"date": "Fri, 10 Apr 2020 20:34:02 GMT",
"tag": "create-just_v1.2.3",
"version": "1.2.3",
"comments": {
"none": [
{
"comment": "fix up initCommand with new yargs types",
"author": "kchau@microsoft.com",
"commit": "565e98494de5bae050cdc04365dee0a0aef638d8"
}
]
}
},
{
"date": "Mon, 04 Nov 2019 20:25:32 GMT",
"tag": "create-just_v1.2.3",

Просмотреть файл

@ -28,6 +28,6 @@
"yargs-parser": "^13.1.1",
"just-plop-helpers": "^1.1.2",
"node-plop": "^0.20.0",
"just-scripts-utils": ">=0.8.4 <1.0.0"
"just-scripts-utils": ">=0.9.0 <1.0.0"
}
}

Просмотреть файл

@ -39,7 +39,7 @@ async function getStackPath(pathName: string, registry?: string) {
* 4. git init and commit
* 5. yarn install
*/
export async function initCommand(argv: yargs.Arguments) {
export async function initCommand(argv: yargs.Arguments<{ [key: string]: string }>) {
// TODO: autosuggest just-stack-* packages from npmjs.org
if (!argv.stack) {
const { stack } = await prompts({

Просмотреть файл

@ -10,7 +10,7 @@
"author": "",
"license": "MIT",
"devDependencies": {
"just-scripts": ">=0.36.1 <1.0.0",
"just-scripts": ">=0.38.0 <1.0.0",
"typescript": "~3.4.4"
}
}

Просмотреть файл

@ -1,6 +1,20 @@
{
"name": "just-scripts-utils",
"entries": [
{
"date": "Mon, 13 Apr 2020 15:57:30 GMT",
"tag": "just-scripts-utils_v0.9.0",
"version": "0.9.0",
"comments": {
"minor": [
{
"comment": "Add execSync utility",
"author": "elcraig@microsoft.com",
"commit": "91c6179993b4cc25076b8511b4d5d4726cd402d4"
}
]
}
},
{
"date": "Tue, 13 Aug 2019 21:00:51 GMT",
"tag": "just-scripts-utils_v0.8.4",

Просмотреть файл

@ -1,7 +1,13 @@
# Change Log - just-scripts-utils
This log was last generated on Tue, 13 Aug 2019 21:00:51 GMT and should not be manually modified.
This log was last generated on Mon, 13 Apr 2020 15:57:30 GMT and should not be manually modified.
## 0.9.0
Mon, 13 Apr 2020 15:57:30 GMT
### Minor changes
- Add execSync utility (elcraig@microsoft.com)
## 0.8.4
Tue, 13 Aug 2019 21:00:51 GMT

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "just-scripts-utils",
"version": "0.8.4",
"version": "0.9.0",
"description": "Utilities for Just stack scripts",
"repository": {
"type": "git",

Просмотреть файл

@ -1,10 +1,13 @@
import cp from 'child_process';
import path from 'path';
export interface ExecError extends cp.ExecException {
stdout?: string;
stderr?: string;
}
const SEPARATOR = process.platform === 'win32' ? ';' : ':';
/**
* Execute a command.
*
@ -60,6 +63,29 @@ export function encodeArgs(cmdArgs: string[]) {
});
}
/**
* Execute a command synchronously.
*
* @param cmd Command to execute
* @param cwd Working directory in which to run the command (default: `process.cwd()`)
* @param returnOutput If true, return the command's output. If false/unspecified,
* inherit stdio from the parent process (so the child's output goes to the console).
* @returns If `returnOutput` is true, returns the command's output. Otherwise returns undefined.
*/
export function execSync(cmd: string, cwd?: string, returnOutput?: boolean): string | undefined {
cwd = cwd || process.cwd();
const env = { ...process.env };
env.PATH = path.resolve('./node_modules/.bin') + SEPARATOR + env.PATH;
const output = cp.execSync(cmd, {
cwd,
env,
stdio: returnOutput ? undefined : 'inherit'
});
return returnOutput ? (output || '').toString('utf8') : undefined;
}
/**
* Execute a command in a new process.
*

Просмотреть файл

@ -1,6 +1,62 @@
{
"name": "just-scripts",
"entries": [
{
"date": "Mon, 13 Apr 2020 15:57:30 GMT",
"tag": "just-scripts_v0.38.0",
"version": "0.38.0",
"comments": {
"minor": [
{
"comment": "Make API Extractor task accept all API Extractor options, and deprecate fixNewlines. Add shared Jest reporter.",
"author": "elcraig@microsoft.com",
"commit": "91c6179993b4cc25076b8511b4d5d4726cd402d4"
}
]
}
},
{
"date": "Fri, 10 Apr 2020 20:34:02 GMT",
"tag": "just-scripts_v0.37.2",
"version": "0.37.2",
"comments": {
"patch": [
{
"comment": "Jest now can take a positional arg to run a certain test pattern",
"author": "kchau@microsoft.com",
"commit": "565e98494de5bae050cdc04365dee0a0aef638d8"
}
]
}
},
{
"date": "Tue, 07 Apr 2020 17:35:38 GMT",
"tag": "just-scripts_v0.37.1",
"version": "0.37.1",
"comments": {
"patch": [
{
"comment": "another take on tar task using tar-fs instead because it can actually handle large amounts of data",
"author": "kchau@microsoft.com",
"commit": "b5c1b57062e97a6780c5c30d27f81e9c5298bedf"
}
]
}
},
{
"date": "Mon, 06 Apr 2020 21:43:56 GMT",
"tag": "just-scripts_v0.37.0",
"version": "0.37.0",
"comments": {
"minor": [
{
"comment": "Adds a new set of tar create and extract tasks",
"author": "kchau@microsoft.com",
"commit": "d8c91d78ed4337dbb8a592f388e382a8dd3628a2"
}
]
}
},
{
"date": "Wed, 13 Nov 2019 19:42:46 GMT",
"tag": "just-scripts_v0.36.1",

Просмотреть файл

@ -1,7 +1,31 @@
# Change Log - just-scripts
This log was last generated on Wed, 13 Nov 2019 19:42:46 GMT and should not be manually modified.
This log was last generated on Mon, 13 Apr 2020 15:57:30 GMT and should not be manually modified.
## 0.38.0
Mon, 13 Apr 2020 15:57:30 GMT
### Minor changes
- Make API Extractor task accept all API Extractor options, and deprecate fixNewlines. Add shared Jest reporter. (elcraig@microsoft.com)
## 0.37.2
Fri, 10 Apr 2020 20:34:02 GMT
### Patches
- Jest now can take a positional arg to run a certain test pattern (kchau@microsoft.com)
## 0.37.1
Tue, 07 Apr 2020 17:35:38 GMT
### Patches
- another take on tar task using tar-fs instead because it can actually handle large amounts of data (kchau@microsoft.com)
## 0.37.0
Mon, 06 Apr 2020 21:43:56 GMT
### Minor changes
- Adds a new set of tar create and extract tasks (kchau@microsoft.com)
## 0.36.1
Wed, 13 Nov 2019 19:42:46 GMT

Просмотреть файл

@ -1,26 +1,32 @@
{
"name": "just-scripts",
"version": "0.36.1",
"version": "0.38.0",
"description": "Just Stack Scripts",
"keywords": [],
"repository": {
"type": "git",
"url": "https://github.com/microsoft/just"
},
"license": "MIT",
"author": "",
"main": "./lib/index.js",
"bin": {
"just-scripts": "bin/just-scripts.js"
},
"keywords": [],
"author": "",
"license": "MIT",
"main": "./lib/index.js",
"scripts": {
"build": "tsc",
"start": "tsc -w --preserveWatchOutput",
"test": "jest",
"test:update": "jest --updateSnapshot"
},
"dependencies": {
"@types/node": "^10.12.18",
"chalk": "^2.4.1",
"diff-match-patch": "1.0.4",
"fs-extra": "^7.0.1",
"glob": "^7.1.3",
"just-scripts-utils": ">=0.8.4 <1.0.0",
"just-task": ">=0.14.3 <1.0.0",
"just-scripts-utils": ">=0.9.0 <1.0.0",
"just-task": ">=0.15.0 <1.0.0",
"npm-registry-fetch": "^3.9.0",
"prompts": "^2.0.1",
"run-parallel-limit": "^1.0.5",
@ -28,24 +34,19 @@
"webpack-merge": "^4.2.1"
},
"devDependencies": {
"@types/supports-color": "5.3.0",
"@types/diff-match-patch": "1.0.32",
"@types/fs-extra": "^5.0.4",
"@types/glob": "^7.1.1",
"@types/jest": "^24.0.13",
"@types/prompts": "^1.2.0",
"@types/run-parallel-limit": "^1.0.0",
"@types/supports-color": "5.3.0",
"@types/tar": "^4.0.3",
"@types/webpack-merge": "^4.1.3",
"async-done": "^1.3.2",
"jest": "^24.0.0",
"jest-cli": "^24.8.0",
"mock-fs": "^4.8.0",
"typescript": "~3.4.4"
},
"scripts": {
"build": "tsc",
"start": "tsc -w --preserveWatchOutput",
"test": "jest",
"test:update": "jest --updateSnapshot"
"typescript": "~3.8.3"
}
}

Просмотреть файл

@ -30,6 +30,6 @@ function executeSingleCopyInstruction(copyInstruction: CopyInstruction) {
// perform text merge operation.
return Promise.all(sourceFileNames.map(fileName => readFile(fileName))).then(fileContents => {
writeFile(copyInstruction.destinationFilePath, fileContents.join('\n'));
return writeFile(copyInstruction.destinationFilePath, fileContents.join('\n'));
});
}

Просмотреть файл

@ -0,0 +1,27 @@
// This doesn't have types for some reason
const { DefaultReporter } = require('@jest/reporters');
/**
* The purpose of this custom reporter is to prevent Jest from logging to stderr
* when there are no errors.
*/
class JestReporter extends DefaultReporter {
private _isLoggingError = false;
public log(message: string) {
if (this._isLoggingError) {
process.stderr.write(message + '\n');
} else {
process.stdout.write(message + '\n');
}
}
public printTestFileFailureMessage(...args: any[]) {
this._isLoggingError = true;
super.printTestFileFailureMessage(...args);
this._isLoggingError = false;
}
}
// jest needs this format
module.exports = JestReporter;

1051
packages/just-scripts/src/tasks/api-extractor.d.ts поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -2,46 +2,22 @@ import { logger, TaskFunction } from 'just-task';
import fs from 'fs-extra';
import path from 'path';
import { tryRequire } from '../tryRequire';
// This is a TYPING ONLY import from <root>/typings/api-extractor.d.ts
import * as ApiExtractorTypes from '@microsoft/api-extractor';
import * as ApiExtractorTypes from './api-extractor.d';
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/**
* Subset of the options from `IExtractorConfigOptions` that are exposed via this just task,
* plus additional options specific to the task.
* Options from `IExtractorConfigOptions` plus additional options specific to the task.
*/
export interface ApiExtractorOptions {
/**
* Indicates that API Extractor is running as part of a local build, e.g. on developer's
* machine. This disables certain validation that would normally be performed
* for a ship/production build. For example, the *.api.md report file is
* automatically updated in a local build.
*
* The default value is false.
*/
localBuild?: boolean;
/**
* If true, API Extractor will include {@link ExtractorLogLevel.Verbose} messages in its output.
*/
showVerboseMessages?: boolean;
export interface ApiExtractorOptions extends ApiExtractorTypes.IExtractorInvokeOptions {
/** @deprecated Does not appear to be used */
projectFolder?: string;
/**
* By default API Extractor uses its own TypeScript compiler version to analyze your project.
* This can often cause compiler errors due to incompatibilities between different TS versions.
* Use this option to specify the folder path for your compiler version.
*/
typescriptCompilerFolder?: string;
/** The config file path */
configJsonFilePath?: string;
/**
* API Extractor uses CRLF newlines by default and adds trailing spaces after empty comment lines,
* both of which can add excessive noise to diffs. Set this option to true to post-process the
* API Extractor .md file to fix these issues (newline type will be inferred from the type used in
* the config file). It will also remove trailing spaces.
* @deprecated Update API Extractor and use option `newlineKind: 'os'`.
*/
fixNewlines?: boolean;
@ -131,7 +107,7 @@ export function apiExtractorUpdateTask(
return function apiExtractorUpdate() {
const context = initApiExtractor(options);
if (context) {
const apiExtractorResult = apiExtractorWrapper(context);
let apiExtractorResult = apiExtractorWrapper(context);
if (apiExtractorResult) {
if (!apiExtractorResult.succeeded) {
@ -140,7 +116,8 @@ export function apiExtractorUpdateTask(
logger.info(`- Update API: successfully updated API file, verifying the updates...`);
if (!apiExtractorWrapper(context)!.succeeded) {
apiExtractorResult = apiExtractorWrapper(context);
if (!apiExtractorResult || !apiExtractorResult.succeeded) {
throw new Error(`- Update API: failed to verify API updates.`);
} else {
logger.info(`- Update API: successully verified API file. Please commit API file as part of your changes.`);
@ -155,6 +132,7 @@ export function apiExtractorUpdateTask(
/**
* Load the api-extractor module (if available) and the config file.
* Returns undefined if api-extractor or the config file couldn't be found.
*/
function initApiExtractor(options: ApiExtractorOptions): ApiExtractorContext | undefined {
const apiExtractorModule: typeof ApiExtractorTypes = tryRequire('@microsoft/api-extractor');

Просмотреть файл

@ -14,3 +14,4 @@ export * from './prettierTask';
export * from './eslintTask';
export * from './webpackCliInitTask';
export * from './webpackCliTask';
export * from './tarTask';

Просмотреть файл

@ -1,4 +1,4 @@
import { resolve, logger, resolveCwd, TaskFunction } from 'just-task';
import { resolve, logger, resolveCwd, TaskFunction, argv } from 'just-task';
import { spawn, encodeArgs } from 'just-scripts-utils';
import { existsSync } from 'fs';
import supportsColor from 'supports-color';
@ -11,6 +11,8 @@ export interface JestTaskOptions {
watch?: boolean;
colors?: boolean;
passWithNoTests?: boolean;
testPathPattern?: string;
testNamePattern?: string;
u?: boolean;
_?: string[];
@ -35,6 +37,10 @@ export function jestTask(options: JestTaskOptions = {}): TaskFunction {
if (configFile && jestCmd && existsSync(configFile)) {
logger.info(`Running Jest`);
const cmd = process.execPath;
const positional = argv()._.slice(1);
options = { ...options, ...{ ...argv(), _: positional } };
const args = [
...(options.nodeArgs || []),
jestCmd,
@ -45,6 +51,8 @@ export function jestTask(options: JestTaskOptions = {}): TaskFunction {
...(options.runInBand ? ['--runInBand'] : []),
...(options.coverage ? ['--coverage'] : []),
...(options.watch ? ['--watch'] : []),
...(options.testPathPattern ? ['--testPathPattern', options.testPathPattern] : []),
...(options.testNamePattern ? ['--testNamePattern', options.testNamePattern] : []),
...(options.u || options.updateSnapshot ? ['--updateSnapshot'] : ['']),
...(options._ || [])
].filter(arg => !!arg) as Array<string>;

Просмотреть файл

@ -0,0 +1,167 @@
import { resolve, logger, TaskFunction } from 'just-task';
import { createWriteStream, createReadStream } from 'fs';
import { createGzip, createGunzip } from 'zlib';
import { Stream } from 'stream';
export interface EntryHeader {
name: string;
size: number;
/** entry mode. defaults to to 0755 for dirs and 0644 otherwise */
mode: number; //
/** last modified date for entry. defaults to now. */
mtime: Date;
/** type of entry. defaults to "file". */
type: 'file' | 'link' | 'symlink' | 'directory' | 'block-device' | 'character-device' | 'fifo' | 'contiguous-file';
/** linked file name */
linkname: string;
uid: number;
gid: number;
uname: string;
gname: string;
devmajor: number;
devminor: number;
}
export interface CreateOptions {
/** output file */
file: string;
/** whether to gzip or not, either a boolean or uses the `zlib.Gzip` options like level, memLevel */
gzip?:
| boolean
| {
level?: number;
memLevel?: number;
};
/** the context path of the tar pack */
cwd?: string;
/** filter (ignore) entries */
filter?: (path: string, header: EntryHeader) => boolean;
/** change the header in the entry (e.g. to replace prefix) */
map?: (header: EntryHeader) => EntryHeader;
/** whether to dereference links */
dereference?: boolean;
/** specify a set of entries to be packed */
entries?: string[];
}
/**
* Creates an tar (optionally gzipped) archive
* @param options
*/
export function createTarTask(options: CreateOptions = { file: 'archive.tar.gz' }): TaskFunction {
const resolvedTar = resolve('tar-fs');
// Inject default options
options = { cwd: process.cwd(), gzip: true, ...options };
// Validate whether tar-fs is installed
if (!resolvedTar) {
logger.error('Please make sure to have "tar-fs" as a dependency in your package.json');
throw new Error('Required dependency "tar-fs" is not installed!');
}
const tar = require(resolvedTar);
let { entries, file, cwd, ...restOptions } = options;
return function archive(done) {
let tarStream = tar.pack(cwd, {
entries,
finalize: true,
finish: () => {
done();
},
...restOptions
});
if (options.gzip) {
const gzip = typeof options.gzip === 'boolean' ? createGzip() : createGzip(options.gzip as any);
tarStream = tarStream.pipe(gzip);
}
tarStream.pipe(createWriteStream(options.file));
};
}
export interface ExtractOptions {
/** output file */
file: string;
/** whether to gzip or not, either a boolean or uses the `zlib.Gzip` options like level, memLevel */
gzip?: boolean;
/** the context path of the tar pack */
cwd?: string;
/** filter (ignore) entries */
filter?: (path: string, header: EntryHeader) => boolean;
/** change the header in the entry (e.g. to replace prefix) */
map?: (header: EntryHeader) => EntryHeader;
/** mode for files (e.g. `parseInt(755, 8)`)*/
fmode?: number;
/** mode for directories (e.g. `parseInt(755, 8)`) */
dmode?: number;
/** set whether all files and dirs are readable */
readable?: boolean;
/** set whether all files and dirs are writable */
writable?: boolean;
/** whether to dereference links */
dereference?: boolean;
}
/**
* Creates an tar (optionally gzipped) archive
* @param options
*/
export function extractTarTask(options: ExtractOptions = { file: 'archive.tar.gz' }): TaskFunction {
const resolvedTar = resolve('tar-fs');
// Inject default options
options = { cwd: process.cwd(), gzip: true, ...options };
// Validate whether tar-fs is installed
if (!resolvedTar) {
logger.error('Please make sure to have "tar-fs" as a dependency in your package.json');
throw new Error('Required dependency "tar-fs" is not installed!');
}
const tar = require(resolvedTar);
const { cwd, file, ...restOptions } = options;
return function extract(done) {
let tarStream: Stream = createReadStream(file);
if (options.gzip) {
const gunzip = createGunzip();
tarStream = tarStream.pipe(gunzip);
}
tarStream = tarStream.pipe(
tar.extract(cwd, {
finalize: true,
finish: () => {
done();
},
...restOptions
})
);
};
}

Просмотреть файл

@ -1,918 +0,0 @@
//
// Types copied from api-extractor, so we can use some version of the types without a dependency.
// Copied from version 7.3.7.
//
declare module '@microsoft/api-extractor' {
/**
* This class represents the TypeScript compiler state. This allows an optimization where multiple invocations
* of API Extractor can reuse the same TypeScript compiler analysis.
*/
export class CompilerState {
/**
* The TypeScript compiler's `Program` object, which represents a complete scope of analysis.
* Actual type is `ts.Program`.
*/
readonly program: any;
private constructor();
/**
* Create a compiler state for use with the specified `IExtractorInvokeOptions`.
*/
static create(extractorConfig: ExtractorConfig, options?: ICompilerStateCreateOptions): CompilerState;
}
/**
* Unique identifiers for console messages reported by API Extractor.
*
* @remarks
*
* These strings are possible values for the {@link ExtractorMessage.messageId} property
* when the `ExtractorMessage.category` is {@link ExtractorMessageCategory.Console}.
*/
export const enum ConsoleMessageId {
/**
* "Found metadata in ___"
*/
FoundTSDocMetadata = 'console-found-tsdoc-metadata',
/**
* "Writing: ___"
*/
WritingDocModelFile = 'console-writing-doc-model-file',
/**
* "Writing package typings: ___"
*/
WritingDtsRollup = 'console-writing-dts-rollup',
/**
* "You have changed the public API signature for this project.
* Please copy the file ___ to ___, or perform a local build (which does this automatically).
* See the Git repo documentation for more info."
*
* OR
*
* "The API report file is missing.
* Please copy the file ___ to ___, or perform a local build (which does this automatically).
* See the Git repo documentation for more info."
*/
ApiReportNotCopied = 'console-api-report-not-copied',
/**
* "You have changed the public API signature for this project. Updating ___"
*/
ApiReportCopied = 'console-api-report-copied',
/**
* "The API report is up to date: ___"
*/
ApiReportUnchanged = 'console-api-report-unchanged',
/**
* "The API report file was missing, so a new file was created. Please add this file to Git: ___"
*/
ApiReportCreated = 'console-api-report-created',
/**
* "Unable to create the API report file. Please make sure the target folder exists: ___"
*/
ApiReportFolderMissing = 'console-api-report-folder-missing',
/**
* Used for the information printed when the "--diagnostics" flag is enabled.
*/
Diagnostics = 'console-diagnostics'
}
/**
* The starting point for invoking the API Extractor tool.
* @public
*/
export class Extractor {
/**
* Returns the version number of the API Extractor NPM package.
*/
static readonly version: string;
/**
* Returns the package name of the API Extractor NPM package.
*/
static readonly packageName: string;
/**
* Load the api-extractor.json config file from the specified path, and then invoke API Extractor.
*/
static loadConfigAndInvoke(configFilePath: string, options?: IExtractorInvokeOptions): ExtractorResult;
/**
* Invoke API Extractor using an already prepared `ExtractorConfig` object.
*/
static invoke(extractorConfig: ExtractorConfig, options?: IExtractorInvokeOptions): ExtractorResult;
}
/**
* The `ExtractorConfig` class loads, validates, interprets, and represents the api-extractor.json config file.
* @public
*/
export class ExtractorConfig {
/**
* The JSON Schema for API Extractor config file (api-extractor.schema.json).
*/
static readonly jsonSchema: object;
/**
* The config file name "api-extractor.json".
*/
static readonly FILENAME: string;
/** {@inheritDoc IConfigFile.projectFolder} */
readonly projectFolder: string;
/**
* The parsed package.json file for the working package, or undefined if API Extractor was invoked without
* a package.json file.
*/
readonly packageJson: object | undefined;
/**
* The absolute path of the folder containing the package.json file for the working package, or undefined
* if API Extractor was invoked without a package.json file.
*/
readonly packageFolder: string | undefined;
/** {@inheritDoc IConfigFile.mainEntryPointFilePath} */
readonly mainEntryPointFilePath: string;
/** {@inheritDoc IConfigCompiler.tsconfigFilePath} */
readonly tsconfigFilePath: string;
/** {@inheritDoc IConfigCompiler.overrideTsconfig} */
readonly overrideTsconfig: {} | undefined;
/** {@inheritDoc IConfigCompiler.skipLibCheck} */
readonly skipLibCheck: boolean;
/** {@inheritDoc IConfigApiReport.enabled} */
readonly apiReportEnabled: boolean;
/** The `reportFolder` path combined with the `reportFileName`. */
readonly reportFilePath: string;
/** The `reportTempFolder` path combined with the `reportFileName`. */
readonly reportTempFilePath: string;
/** {@inheritDoc IConfigDocModel.enabled} */
readonly docModelEnabled: boolean;
/** {@inheritDoc IConfigDocModel.apiJsonFilePath} */
readonly apiJsonFilePath: string;
/** {@inheritDoc IConfigDtsRollup.enabled} */
readonly rollupEnabled: boolean;
/** {@inheritDoc IConfigDtsRollup.untrimmedFilePath} */
readonly untrimmedFilePath: string;
/** {@inheritDoc IConfigDtsRollup.betaTrimmedFilePath} */
readonly betaTrimmedFilePath: string;
/** {@inheritDoc IConfigDtsRollup.publicTrimmedFilePath} */
readonly publicTrimmedFilePath: string;
/** {@inheritDoc IConfigDtsRollup.omitTrimmingComments} */
readonly omitTrimmingComments: boolean;
/** {@inheritDoc IConfigTsdocMetadata.enabled} */
readonly tsdocMetadataEnabled: boolean;
/** {@inheritDoc IConfigTsdocMetadata.tsdocMetadataFilePath} */
readonly tsdocMetadataFilePath: string;
/** {@inheritDoc IConfigFile.messages} */
readonly messages: IExtractorMessagesConfig;
/** {@inheritDoc IConfigFile.testMode} */
readonly testMode: boolean;
private constructor();
/**
* Returns a JSON-like string representing the `ExtractorConfig` state, which can be printed to a console
* for diagnostic purposes.
*
* @remarks
* This is used by the "--diagnostics" command-line option. The string is not intended to be deserialized;
* its format may be changed at any time.
*/
getDiagnosticDump(): string;
/**
* Returns a simplified file path for use in error messages.
* @internal
*/
_getShortFilePath(absolutePath: string): string;
/**
* Loads the api-extractor.json config file from the specified file path, and prepares an `ExtractorConfig` object.
*
* @remarks
* Loads the api-extractor.json config file from the specified file path. If the "extends" field is present,
* the referenced file(s) will be merged. For any omitted fields, the API Extractor default values are merged.
*
* The result is prepared using `ExtractorConfig.prepare()`.
*/
static loadFileAndPrepare(configJsonFilePath: string): ExtractorConfig;
/**
* Performs only the first half of {@link ExtractorConfig.loadFileAndPrepare}, providing an opportunity to
* modify the object before it is passed to {@link ExtractorConfig.prepare}.
*
* @remarks
* Loads the api-extractor.json config file from the specified file path. If the "extends" field is present,
* the referenced file(s) will be merged. For any omitted fields, the API Extractor default values are merged.
*/
static loadFile(jsonFilePath: string): IConfigFile;
/**
* Prepares an `ExtractorConfig` object using a configuration that is provided as a runtime object,
* rather than reading it from disk. This allows configurations to be constructed programmatically,
* loaded from an alternate source, and/or customized after loading.
*/
static prepare(options: IExtractorConfigPrepareOptions): ExtractorConfig;
/**
* Returns true if the specified file path has the ".d.ts" file extension.
*/
static hasDtsFileExtension(filePath: string): boolean;
}
/**
* Used with {@link IConfigMessageReportingRule.logLevel} and {@link IExtractorInvokeOptions.messageCallback}.
*
* @remarks
* This is part of the {@link IConfigFile} structure.
*/
export const enum ExtractorLogLevel {
/**
* The message will be displayed as an error.
*
* @remarks
* Errors typically cause the build to fail and return a nonzero exit code.
*/
Error = 'error',
/**
* The message will be displayed as an warning.
*
* @remarks
* Warnings typically cause a production build fail and return a nonzero exit code. For a non-production build
* (e.g. using the `--local` option with `api-extractor run`), the warning is displayed but the build will not fail.
*/
Warning = 'warning',
/**
* The message will be displayed as an informational message.
*
* @remarks
* Informational messages may contain newlines to ensure nice formatting of the output,
* however word-wrapping is the responsibility of the message handler.
*/
Info = 'info',
/**
* The message will be displayed only when "verbose" output is requested, e.g. using the `--verbose`
* command line option.
*/
Verbose = 'verbose',
/**
* The message will be discarded entirely.
*/
None = 'none'
}
/**
* This object is used to report an error or warning that occurred during API Extractor's analysis.
*/
export class ExtractorMessage {
/**
* The category of issue.
*/
readonly category: ExtractorMessageCategory;
/**
* A text string that uniquely identifies the issue type. This identifier can be used to suppress
* or configure the reporting of issues, and also to search for help about an issue.
*/
readonly messageId: /*tsdoc.TSDocMessageId |*/ ExtractorMessageId | ConsoleMessageId | string;
/**
* The text description of this issue.
*/
readonly text: string;
/**
* The absolute path to the affected input source file, if there is one.
*/
readonly sourceFilePath: string | undefined;
/**
* The line number where the issue occurred in the input source file. This is not used if `sourceFilePath`
* is undefined. The first line number is 1.
*/
readonly sourceFileLine: number | undefined;
/**
* The column number where the issue occurred in the input source file. This is not used if `sourceFilePath`
* is undefined. The first column number is 1.
*/
readonly sourceFileColumn: number | undefined;
/**
* Additional contextual information about the message that may be useful when reporting errors.
* All properties are optional.
*/
readonly properties: IExtractorMessageProperties;
/**
* If the {@link IExtractorInvokeOptions.messageCallback} sets this property to true, it will prevent the message
* from being displayed by API Extractor.
*
* @remarks
* If the `messageCallback` routes the message to a custom handler (e.g. a toolchain logger), it should
* assign `handled = true` to prevent API Extractor from displaying it. Assigning `handled = true` for all messages
* would effectively disable all console output from the `Extractor` API.
*
* If `handled` is set to true, the message will still be included in the count of errors/warnings;
* to discard a message entirely, instead assign `logLevel = none`.
*/
handled: boolean;
/**
* Specifies how the message should be reported.
*
* @remarks
* If the {@link IExtractorInvokeOptions.messageCallback} handles the message (i.e. sets `handled = true`),
* it can use the `logLevel` to determine how to display the message.
*
* Alternatively, if API Extractor is handling the message, the `messageCallback` could assign `logLevel` to change
* how it will be processed. However, in general the recommended practice is to configure message routing
* using the `messages` section in api-extractor.json.
*
* To discard a message entirely, assign `logLevel = none`.
*/
logLevel: ExtractorLogLevel;
/**
* Returns the message formatted with its identifier and file position.
* @remarks
* Example:
* ```
* src/folder/File.ts:123:4 - (ae-extra-release-tag) The doc comment should not contain more than one release tag.
* ```
*/
formatMessageWithLocation(workingPackageFolderPath: string | undefined): string;
formatMessageWithoutLocation(): string;
}
/**
* Specifies a category of messages for use with {@link ExtractorMessage}.
* @public
*/
export const enum ExtractorMessageCategory {
/**
* Messages originating from the TypeScript compiler.
*
* @remarks
* These strings begin with the prefix "TS" and have a numeric error code.
* Example: `TS2551`
*/
Compiler = 'Compiler',
/**
* Messages related to parsing of TSDoc comments.
*
* @remarks
* These strings begin with the prefix "tsdoc-".
* Example: `tsdoc-link-tag-unescaped-text`
*/
TSDoc = 'TSDoc',
/**
* Messages related to API Extractor's analysis.
*
* @remarks
* These strings begin with the prefix "ae-".
* Example: `ae-extra-release-tag`
*/
Extractor = 'Extractor',
/**
* Console messages communicate the progress of the overall operation. They may include newlines to ensure
* nice formatting. They are output in real time, and cannot be routed to the API Report file.
*
* @remarks
* These strings begin with the prefix "console-".
* Example: `console-writing-typings-file`
*/
Console = 'console'
}
/**
* Unique identifiers for messages reported by API Extractor during its analysis.
*
* @remarks
*
* These strings are possible values for the {@link ExtractorMessage.messageId} property
* when the `ExtractorMessage.category` is {@link ExtractorMessageCategory.Extractor}.
*/
export const enum ExtractorMessageId {
/**
* "The doc comment should not contain more than one release tag."
*/
ExtraReleaseTag = 'ae-extra-release-tag',
/**
* "This symbol has another declaration with a different release tag."
*/
DifferentReleaseTags = 'ae-different-release-tags',
/**
* "The symbol ___ is marked as ___, but its signature references ___ which is marked as ___."
*/
IncompatibleReleaseTags = 'ae-incompatible-release-tags',
/**
* "___ is exported by the package, but it is missing a release tag (`@alpha`, `@beta`, `@public`, or `@internal`)."
*/
MissingReleaseTag = 'ae-missing-release-tag',
/**
* "The `@packageDocumentation` comment must appear at the top of entry point *.d.ts file."
*/
MisplacedPackageTag = 'ae-misplaced-package-tag',
/**
* "The symbol ___ needs to be exported by the entry point ___."
*/
ForgottenExport = 'ae-forgotten-export',
/**
* "The name ___ should be prefixed with an underscore because the declaration is marked as `@internal`."
*/
InternalMissingUnderscore = 'ae-internal-missing-underscore',
/**
* "The `@preapproved` tag cannot be applied to ___ because it is not a supported declaration type."
*/
PreapprovedUnsupportedType = 'ae-preapproved-unsupported-type',
/**
* "The `@preapproved` tag cannot be applied to ___ without an `@internal` release tag."
*/
PreapprovedBadReleaseTag = 'ae-preapproved-bad-release-tag',
/**
* "The `@inheritDoc` reference could not be resolved".
*/
UnresolvedInheritDocReference = 'ae-unresolved-inheritdoc-reference',
/**
* "The `@inheritDoc` tag needs a TSDoc declaration reference; signature matching is not supported yet".
*
* @privateRemarks
* In the future, we will implement signature matching so that you can write `{@inheritDoc}` and API Extractor
* will find a corresponding member from a base class (or implemented interface). Until then, the tag
* always needs an explicit declaration reference such as `{@inhertDoc MyBaseClass.sameMethod}`.
*/
UnresolvedInheritDocBase = 'ae-unresolved-inheritdoc-base',
/**
* "The `@inheritDoc` tag for ___ refers to its own declaration".
*/
CyclicInheritDoc = 'ae-cyclic-inherit-doc',
/**
* "The `@link` reference could not be resolved".
*/
UnresolvedLink = 'ae-unresolved-link'
}
/**
* This object represents the outcome of an invocation of API Extractor.
*/
export class ExtractorResult {
/**
* The TypeScript compiler state that was used.
*/
readonly compilerState: CompilerState;
/**
* The API Extractor configuration that was used.
*/
readonly extractorConfig: ExtractorConfig;
/**
* Whether the invocation of API Extractor was successful. For example, if `succeeded` is false, then the build task
* would normally return a nonzero process exit code, indicating that the operation failed.
*
* @remarks
*
* Normally the operation "succeeds" if `errorCount` and `warningCount` are both zero. However if
* {@link IExtractorInvokeOptions.localBuild} is `true`, then the operation "succeeds" if `errorCount` is zero
* (i.e. warnings are ignored).
*/
readonly succeeded: boolean;
/**
* Returns true if the API report was found to have changed.
*/
readonly apiReportChanged: boolean;
/**
* Reports the number of errors encountered during analysis.
*
* @remarks
* This does not count exceptions, where unexpected issues prematurely abort the operation.
*/
readonly errorCount: number;
/**
* Reports the number of warnings encountered during analysis.
*
* @remarks
* This does not count warnings that are emitted in the API report file.
*/
readonly warningCount: number;
/** @internal */
constructor(properties: ExtractorResult);
}
/**
* Options for {@link CompilerState.create}
* @public
*/
export interface ICompilerStateCreateOptions {
/** {@inheritDoc IExtractorInvokeOptions.typescriptCompilerFolder} */
typescriptCompilerFolder?: string;
/**
* Additional .d.ts files to include in the analysis.
*/
additionalEntryPoints?: string[];
}
/**
* Configures how the API report files (*.api.md) will be generated.
*
* @remarks
* This is part of the {@link IConfigFile} structure.
*/
export interface IConfigApiReport {
/**
* Whether to generate an API report.
*/
enabled: boolean;
/**
* The filename for the API report files. It will be combined with `reportFolder` or `reportTempFolder` to produce
* a full output filename.
*
* @remarks
* The file extension should be ".api.md", and the string should not contain a path separator such as `\` or `/`.
*/
reportFileName?: string;
/**
* Specifies the folder where the API report file is written. The file name portion is determined by
* the `reportFileName` setting.
*
* @remarks
* The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy,
* e.g. for an API review.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as `<projectFolder>`.
*/
reportFolder?: string;
/**
* Specifies the folder where the temporary report file is written. The file name portion is determined by
* the `reportFileName` setting.
*
* @remarks
* After the temporary file is written to disk, it is compared with the file in the `reportFolder`.
* If they are different, a production build will fail.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as `<projectFolder>`.
*/
reportTempFolder?: string;
}
/**
* Determines how the TypeScript compiler engine will be invoked by API Extractor.
*
* @remarks
* This is part of the {@link IConfigFile} structure.
*/
export interface IConfigCompiler {
/**
* Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project.
*
* @remarks
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as `<projectFolder>`.
*
* Note: This setting will be ignored if `overrideTsconfig` is used.
*/
tsconfigFilePath?: string;
/**
* Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk.
*
* @remarks
* The value must conform to the TypeScript tsconfig schema:
*
* http://json.schemastore.org/tsconfig
*
* If omitted, then the tsconfig.json file will instead be read from the projectFolder.
*/
overrideTsconfig?: {};
/**
* This option causes the compiler to be invoked with the `--skipLibCheck` option.
*
* @remarks
* This option is not recommended and may cause API Extractor to produce incomplete or incorrect declarations,
* but it may be required when dependencies contain declarations that are incompatible with the TypeScript engine
* that API Extractor uses for its analysis. Where possible, the underlying issue should be fixed rather than
* relying on skipLibCheck.
*/
skipLibCheck?: boolean;
}
/**
* Configures how the doc model file (*.api.json) will be generated.
*
* @remarks
* This is part of the {@link IConfigFile} structure.
*/
export interface IConfigDocModel {
/**
* Whether to generate a doc model file.
*/
enabled: boolean;
/**
* The output path for the doc model file. The file extension should be ".api.json".
*
* @remarks
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as `<projectFolder>`.
*/
apiJsonFilePath?: string;
}
/**
* Configures how the .d.ts rollup file will be generated.
*
* @remarks
* This is part of the {@link IConfigFile} structure.
*/
export interface IConfigDtsRollup {
/**
* Whether to generate the .d.ts rollup file.
*/
enabled: boolean;
/**
* Specifies the output path for a .d.ts rollup file to be generated without any trimming.
*
* @remarks
* This file will include all declarations that are exported by the main entry point.
*
* If the path is an empty string, then this file will not be written.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as `<projectFolder>`.
*/
untrimmedFilePath?: string;
/**
* Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release.
*
* @remarks
* This file will include only declarations that are marked as `@public` or `@beta`.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as `<projectFolder>`.
*/
betaTrimmedFilePath?: string;
/**
* Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release.
*
* @remarks
* This file will include only declarations that are marked as `@public`.
*
* If the path is an empty string, then this file will not be written.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as `<projectFolder>`.
*/
publicTrimmedFilePath?: string;
/**
* When a declaration is trimmed, by default it will be replaced by a code comment such as
* "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the
* declaration completely.
*/
omitTrimmingComments?: boolean;
}
/**
* Configuration options for the API Extractor tool. These options can be constructed programmatically
* or loaded from the api-extractor.json config file using the {@link ExtractorConfig} class.
*/
export interface IConfigFile {
/**
* Optionally specifies another JSON config file that this file extends from. This provides a way for
* standard settings to be shared across multiple projects.
*
* @remarks
* If the path starts with `./` or `../`, the path is resolved relative to the folder of the file that contains
* the `extends` field. Otherwise, the first path segment is interpreted as an NPM package name, and will be
* resolved using NodeJS `require()`.
*/
extends?: string;
/**
* Determines the `<projectFolder>` token that can be used with other config file settings. The project folder
* typically contains the tsconfig.json and package.json config files, but the path is user-defined.
*
* @remarks
*
* The path is resolved relative to the folder of the config file that contains the setting.
*
* The default value for `projectFolder` is the token `<lookup>`, which means the folder is determined by traversing
* parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder
* that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error
* will be reported.
*/
projectFolder?: string;
/**
* Specifies the .d.ts file to be used as the starting point for analysis. API Extractor
* analyzes the symbols exported by this module.
*
* @remarks
*
* The file extension must be ".d.ts" and not ".ts".
* The path is resolved relative to the "projectFolder" location.
*/
mainEntryPointFilePath: string;
/**
* {@inheritDoc IConfigCompiler}
*/
compiler?: IConfigCompiler;
/**
* {@inheritDoc IConfigApiReport}
*/
apiReport?: IConfigApiReport;
/**
* {@inheritDoc IConfigDocModel}
*/
docModel?: IConfigDocModel;
/**
* {@inheritDoc IConfigDtsRollup}
* @beta
*/
dtsRollup?: IConfigDtsRollup;
/**
* {@inheritDoc IConfigTsdocMetadata}
* @beta
*/
tsdocMetadata?: IConfigTsdocMetadata;
/**
* {@inheritDoc IExtractorMessagesConfig}
*/
messages?: IExtractorMessagesConfig;
/**
* Set to true when invoking API Extractor's test harness.
* @remarks
* When `testMode` is true, the `toolVersion` field in the .api.json file is assigned an empty string
* to prevent spurious diffs in output files tracked for tests.
*/
testMode?: boolean;
}
/**
* Configures reporting for a given message identifier.
*
* @remarks
* This is part of the {@link IConfigFile} structure.
*/
export interface IConfigMessageReportingRule {
/**
* Specifies whether the message should be written to the the tool's output log.
*
* @remarks
* Note that the `addToApiReportFile` property may supersede this option.
*/
logLevel: ExtractorLogLevel;
/**
* When `addToApiReportFile` is true: If API Extractor is configured to write an API report file (.api.md),
* then the message will be written inside that file; otherwise, the message is instead logged according to
* the `logLevel` option.
*/
addToApiReportFile?: boolean;
}
/**
* Specifies a table of reporting rules for different message identifiers, and also the default rule used for
* identifiers that do not appear in the table.
*
* @remarks
* This is part of the {@link IConfigFile} structure.
*/
export interface IConfigMessageReportingTable {
/**
* The key is a message identifier for the associated type of message, or "default" to specify the default policy.
* For example, the key might be `TS2551` (a compiler message), `tsdoc-link-tag-unescaped-text` (a TSDOc message),
* or `ae-extra-release-tag` (a message related to the API Extractor analysis).
*/
[messageId: string]: IConfigMessageReportingRule;
}
/**
* Configures how the tsdoc-metadata.json file will be generated.
*
* @remarks
* This is part of the {@link IConfigFile} structure.
*/
export interface IConfigTsdocMetadata {
/**
* Whether to generate the tsdoc-metadata.json file.
*/
enabled: boolean;
/**
* Specifies where the TSDoc metadata file should be written.
*
* @remarks
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as `<projectFolder>`.
*
* The default value is `<lookup>`, which causes the path to be automatically inferred from the `tsdocMetadata`,
* `typings` or `main` fields of the project's package.json. If none of these fields are set, the lookup
* falls back to `tsdoc-metadata.json` in the package folder.
*/
tsdocMetadataFilePath?: string;
}
/**
* Options for {@link ExtractorConfig.prepare}.
*/
export interface IExtractorConfigPrepareOptions {
/**
* A configuration object as returned by {@link ExtractorConfig.loadFile}.
*/
configObject: IConfigFile;
/**
* The absolute path of the file that the `configObject` object was loaded from. This is used for error messages
* and when probing for `tsconfig.json`.
*
* @remarks
*
* If this is omitted, then the `projectFolder` must not be specified using the `<lookup>` token.
*/
configObjectFullPath: string | undefined;
/**
* The parsed package.json file for the working package, or undefined if API Extractor was invoked without
* a package.json file.
*
* @remarks
*
* If omitted, then the `<unscopedPackageName>` and `<packageName>` tokens will have default values.
*/
packageJson?: object | undefined;
/**
* The absolute path of the file that the `packageJson` object was loaded from, or undefined if API Extractor
* was invoked without a package.json file.
*
* @remarks
*
* This is used for error messages and when resolving paths found in package.json.
*
* If `packageJsonFullPath` is specified but `packageJson` is omitted, the file will be loaded automatically.
*/
packageJsonFullPath: string | undefined;
}
/**
* Runtime options for Extractor.
*/
export interface IExtractorInvokeOptions {
/**
* An optional TypeScript compiler state. This allows an optimization where multiple invocations of API Extractor
* can reuse the same TypeScript compiler analysis.
*/
compilerState?: CompilerState;
/**
* Indicates that API Extractor is running as part of a local build, e.g. on developer's
* machine.
*
* @remarks
* This disables certain validation that would normally be performed for a ship/production build. For example,
* the *.api.md report file is automatically updated in a local build.
*
* The default value is false.
*/
localBuild?: boolean;
/**
* If true, API Extractor will include {@link ExtractorLogLevel.Verbose} messages in its output.
*/
showVerboseMessages?: boolean;
/**
* If true, API Extractor will print diagnostic information used for troubleshooting problems.
* These messages will be included as {@link ExtractorLogLevel.Verbose} output.
*
* @remarks
* Setting `showDiagnostics=true` forces `showVerboseMessages=true`.
*/
showDiagnostics?: boolean;
/**
* Specifies an alternate folder path to be used when loading the TypeScript system typings.
*
* @remarks
* API Extractor uses its own TypeScript compiler engine to analyze your project. If your project
* is built with a significantly different TypeScript version, sometimes API Extractor may report compilation
* errors due to differences in the system typings (e.g. lib.dom.d.ts). You can use the "--typescriptCompilerFolder"
* option to specify the folder path where you installed the TypeScript package, and API Extractor's compiler will
* use those system typings instead.
*/
typescriptCompilerFolder?: string;
/**
* An optional callback function that will be called for each `ExtractorMessage` before it is displayed by
* API Extractor. The callback can customize the message, handle it, or discard it.
*
* @remarks
* If a `messageCallback` is not provided, then by default API Extractor will print the messages to
* the STDERR/STDOUT console.
*/
messageCallback?: (message: ExtractorMessage) => void;
}
/**
* Used by {@link ExtractorMessage.properties}.
*/
export interface IExtractorMessageProperties {
/**
* A declaration can have multiple names if it is exported more than once.
* If an `ExtractorMessage` applies to a specific export name, this property can indicate that.
*
* @remarks
*
* Used by {@link ExtractorMessageId.InternalMissingUnderscore}.
*/
readonly exportName?: string;
}
/**
* Configures how API Extractor reports error and warning messages produced during analysis.
*
* @remarks
* This is part of the {@link IConfigFile} structure.
*/
export interface IExtractorMessagesConfig {
/**
* Configures handling of diagnostic messages generating the TypeScript compiler while analyzing the
* input .d.ts files.
*/
compilerMessageReporting?: IConfigMessageReportingTable;
/**
* Configures handling of messages reported by API Extractor during its analysis.
*/
extractorMessageReporting?: IConfigMessageReportingTable;
/**
* Configures handling of messages reported by the TSDoc parser when analyzing code comments.
*/
tsdocMessageReporting?: IConfigMessageReportingTable;
}
export {};
}

Просмотреть файл

@ -13,7 +13,7 @@
"beachball": "^1.11.5",
"jest": "^24.8.0",
"just-plop-helpers": "^1.1.2",
"just-scripts": "^0.36.1",
"just-scripts": "^0.38.0",
"plop": "^2.5.0",
"typescript": "^3.5.3"
}

Просмотреть файл

@ -1,6 +1,20 @@
{
"name": "just-task",
"entries": [
{
"date": "Fri, 10 Apr 2020 20:34:02 GMT",
"tag": "just-task_v0.15.0",
"version": "0.15.0",
"comments": {
"minor": [
{
"comment": "Replace yargs with yargs-parser, taking over the command parsing",
"author": "kchau@microsoft.com",
"commit": "565e98494de5bae050cdc04365dee0a0aef638d8"
}
]
}
},
{
"date": "Fri, 06 Mar 2020 19:44:20 GMT",
"tag": "just-task_v0.14.3",

Просмотреть файл

@ -1,7 +1,13 @@
# Change Log - just-task
This log was last generated on Mon, 11 Nov 2019 21:09:42 GMT and should not be manually modified.
This log was last generated on Fri, 10 Apr 2020 20:34:02 GMT and should not be manually modified.
## 0.15.0
Fri, 10 Apr 2020 20:34:02 GMT
### Minor changes
- Replace yargs with yargs-parser, taking over the command parsing (kchau@microsoft.com)
## 0.14.3
Mon, 11 Nov 2019 21:09:42 GMT

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "just-task",
"version": "0.14.3",
"version": "0.15.0",
"description": "Build task definition library",
"keywords": [],
"repository": {
@ -15,19 +15,20 @@
},
"scripts": {
"build": "tsc",
"dev": "tsc -w --preserveWatchOutput",
"start": "tsc -w --preserveWatchOutput",
"start-test": "jest --watch",
"test": "jest"
},
"dependencies": {
"@microsoft/package-deps-hash": "^2.2.153",
"bach": "^1.2.0",
"chalk": "^2.4.1",
"fs-extra": "^7.0.1",
"just-task-logger": ">=0.3.0 <1.0.0",
"resolve": "^1.8.1",
"undertaker": "^1.2.0",
"undertaker": "^1.2.1",
"undertaker-registry": "^1.0.1",
"yargs": "^12.0.5"
"yargs-parser": "^18.1.2"
},
"devDependencies": {
"@types/fs-extra": "^5.0.4",
@ -37,7 +38,7 @@
"@types/resolve": "^0.0.8",
"@types/undertaker": "^1.2.0",
"@types/undertaker-registry": "^1.0.1",
"@types/yargs": "12.0.1",
"@types/yargs-parser": "^15.0.0",
"jest": "^24.0.0",
"mock-fs": "^4.8.0",
"ts-jest": "^24.0.1",

Просмотреть файл

@ -1,115 +0,0 @@
import yargs from 'yargs';
import fs from 'fs';
import path from 'path';
import { logger, mark } from './logger';
import UndertakerRegistry from 'undertaker-registry';
import Undertaker from 'undertaker';
import { resolve } from './resolve';
import { enableTypeScript } from './enableTypeScript';
export class JustTaskRegistry extends UndertakerRegistry {
private hasDefault: boolean = false;
public init(taker: Undertaker) {
super.init(taker);
// uses a separate instance of yargs to first parse the config (without the --help in the way) so we can parse the configFile first regardless
const configFile = [yargs.argv.config, './just.config.js', './just-task.js', './just.config.ts'].reduce(
(value, entry) => value || resolve(entry)
);
mark('registry:configModule');
if (configFile && fs.existsSync(configFile)) {
const ext = path.extname(configFile);
if (ext === '.ts' || ext === '.tsx') {
// TODO: add option to do typechecking as well
enableTypeScript({ transpileOnly: true });
}
try {
const configModule = require(configFile);
if (typeof configModule === 'function') {
configModule();
}
} catch (e) {
logger.error(`Invalid configuration file: ${configFile}`);
logger.error(`Error: ${e.stack || e.message || e}`);
process.exit(1);
}
} else {
logger.error(
`Cannot find config file "${configFile}".`,
`Please create a file called "just.config.js" in the root of the project next to "package.json".`
);
}
logger.perf('registry:configModule');
if (!validateCommands(yargs)) {
process.exit(1);
}
if (!this.hasDefault) {
yargs.demandCommand(1, 'No default tasks are defined.').help();
}
}
public set<TTaskFunction>(taskName: string, fn: TTaskFunction): TTaskFunction {
super.set(taskName, fn);
if (taskName === 'default') {
this.hasDefault = true;
}
return fn;
}
}
function validateCommands(yargs: any) {
const commandKeys = yargs.getCommandInstance().getCommands();
const argv = yargs.argv;
const unknown: string[] = [];
const currentContext = yargs.getContext();
if (commandKeys.length > 0) {
argv._.slice(currentContext.commands.length).forEach((key: string) => {
if (commandKeys.indexOf(key) === -1) {
unknown.push(key);
}
});
}
if (unknown.length > 0) {
logger.error(`Unknown command: ${unknown.join(', ')}`);
const recommended = recommendCommands(unknown[0], commandKeys);
if (recommended) {
logger.info(`Did you mean this task name: ${recommended}?`);
}
return false;
}
return true;
}
function recommendCommands(cmd: string, potentialCommands: string[]) {
const distance = require('yargs/lib/levenshtein');
const threshold = 3; // if it takes more than three edits, let's move on.
potentialCommands = potentialCommands.sort((a, b) => b.length - a.length);
let recommended = null;
let bestDistance = Infinity;
for (let i = 0, candidate; (candidate = potentialCommands[i]) !== undefined; i++) {
const d = distance(cmd, candidate);
if (d <= threshold && d < bestDistance) {
bestDistance = d;
recommended = candidate;
}
}
return recommended;
}

Просмотреть файл

@ -1,2 +0,0 @@
import * as yargs from './yargs/yargs';
export default yargs;

Просмотреть файл

@ -1,21 +0,0 @@
const yargs = () => yargs;
yargs.argv = {
config: undefined
} as { config: string | undefined };
yargs.command = () => yargs;
yargs.demandCommand = () => yargs;
yargs.help = () => yargs;
yargs.getCommandInstance = () => ({
getCommands: () => []
});
yargs.getContext = () => ({
commands: []
});
export = yargs;

Просмотреть файл

@ -1,14 +1,8 @@
import mockfs from 'mock-fs';
import path from 'path';
import yargsMock from './__mocks__/yargs/yargs';
import {
_isFileNameLike,
_tryResolve,
resetResolvePaths,
resolveCwd,
addResolvePath,
resolve
} from '../resolve';
import { _isFileNameLike, _tryResolve, resetResolvePaths, resolveCwd, addResolvePath, resolve } from '../resolve';
import * as option from '../option';
describe('_isFileNameLike', () => {
it('returns false for empty input', () => {
@ -83,6 +77,10 @@ describe('_tryResolve', () => {
});
describe('resolveCwd', () => {
beforeEach(() => {
jest.spyOn(option, 'argv').mockImplementation(() => ({ config: undefined } as any));
});
afterEach(() => {
mockfs.restore();
resetResolvePaths();
@ -117,9 +115,10 @@ describe('resolveCwd', () => {
});
describe('resolve', () => {
jest.spyOn(option, 'argv').mockImplementation(() => ({ config: undefined } as any));
afterEach(() => {
mockfs.restore();
yargsMock.argv.config = undefined;
resetResolvePaths();
});
@ -145,7 +144,9 @@ describe('resolve', () => {
mockfs({
a: { 'b.txt': '' }
});
yargsMock.argv.config = 'a/just-task.js';
jest.spyOn(option, 'argv').mockImplementation(() => ({ config: 'a/just-task.js' } as any));
expect(resolve('b.txt')).toContain(path.join('a', 'b.txt'));
});
@ -166,7 +167,9 @@ describe('resolve', () => {
d: { 'b.txt': '' }, // wrong
'b.txt': '' // wrong
});
yargsMock.argv.config = 'a/just-task.js';
jest.spyOn(option, 'argv').mockImplementation(() => ({ config: 'a/just-task.js' } as any));
addResolvePath('c');
expect(resolve('b.txt', 'd')).toContain(path.join('d', 'b.txt'));
});

Просмотреть файл

@ -1,22 +1,24 @@
import { task } from '../task';
import { parallel, undertaker } from '../undertaker';
import { JustTaskRegistry } from '../JustTaskRegistry';
import UndertakerRegistry from 'undertaker-registry';
import { logger } from '../logger';
import yargsMock from './__mocks__/yargs';
import path from 'path';
import * as option from '../option';
describe('task', () => {
beforeAll(() => {
yargsMock.argv.config = path.resolve(__dirname, '__mocks__/just-task.js');
jest.spyOn(option, 'argv').mockImplementation(() => ({ config: path.resolve(__dirname, '__mocks__/just-task.js') } as any));
jest.spyOn(logger, 'info').mockImplementation(() => undefined);
});
beforeEach(() => {
undertaker.registry(new JustTaskRegistry());
undertaker.registry(new UndertakerRegistry());
});
afterAll(() => {
yargsMock.argv.config = undefined;
jest.spyOn(option, 'argv').mockImplementation(() => ({ config: 'a/just-task.js' } as any));
jest.restoreAllMocks();
});

Просмотреть файл

@ -1,6 +1,8 @@
import { undertaker } from './undertaker';
import { JustTaskRegistry } from './JustTaskRegistry';
import yargs from 'yargs';
import { option, parseCommand } from './option';
import { logger } from 'just-task-logger';
import { TaskFunction } from './interfaces';
import { readConfig } from './config';
const originalEmitWarning = process.emitWarning;
@ -17,15 +19,33 @@ const originalEmitWarning = process.emitWarning;
return originalEmitWarning.apply(this, arguments);
};
yargs
.option({ config: { describe: 'path to a just-task.js file (includes the file name)' } })
.usage('$0 <cmd> [options]')
.updateStrings({
'Commands:': 'Tasks:\n'
});
function showHelp() {
const tasks = undertaker.registry().tasks();
const registry = new JustTaskRegistry();
console.log('All the tasks that are available to just:');
undertaker.registry(registry);
for (const [name, wrappedTask] of Object.entries(tasks)) {
const unwrapped = (wrappedTask as any).unwrap ? (wrappedTask as any).unwrap() : (wrappedTask as TaskFunction);
const description = (unwrapped as TaskFunction).description;
console.log(` ${name}${description ? `: ${description}` : ''}`);
}
}
yargs.parse();
// Define a built-in option of "config" so users can specify which path to choose for configurations
option('config', { describe: 'path to a just configuration file (includes the file name, e.g. /path/to/just.config.ts)' });
readConfig();
const registry = undertaker.registry();
const command = parseCommand();
if (command) {
if (registry.get(command)) {
undertaker.series(registry.get(command))(() => {});
} else {
logger.error(`Command not defined: ${command}`);
}
} else {
showHelp();
}

Просмотреть файл

@ -0,0 +1,46 @@
import fs from 'fs';
import path from 'path';
import { argv } from './option';
import { resolve } from './resolve';
import { mark, logger } from 'just-task-logger';
import { enableTypeScript } from './enableTypeScript';
export function readConfig() {
// uses a separate instance of yargs to first parse the config (without the --help in the way) so we can parse the configFile first regardless
let configFile: string | null = null;
for (const entry of [argv().config, './just.config.js', './just-task.js', './just.config.ts']) {
configFile = resolve(entry);
if (configFile) {
break;
}
}
mark('registry:configModule');
if (configFile && fs.existsSync(configFile)) {
const ext = path.extname(configFile);
if (ext === '.ts' || ext === '.tsx') {
// TODO: add option to do typechecking as well
enableTypeScript({ transpileOnly: true });
}
try {
const configModule = require(configFile);
if (typeof configModule === 'function') {
configModule();
}
} catch (e) {
logger.error(`Invalid configuration file: ${configFile}`);
logger.error(`Error: ${e.stack || e.message || e}`);
process.exit(1);
}
} else {
logger.error(
`Cannot find config file "${configFile}".`,
`Please create a file called "just.config.js" in the root of the project next to "package.json".`
);
}
logger.perf('registry:configModule');
}

Просмотреть файл

@ -13,4 +13,5 @@ export interface TaskContext {
export interface TaskFunction extends Undertaker.TaskFunctionParams {
(this: TaskContext, done: (error?: any) => void): void | Duplex | NodeJS.Process | Promise<never> | any;
cached?: () => void;
description?: string;
}

Просмотреть файл

@ -1,9 +1,90 @@
import yargs from 'yargs';
import parser, { Options, Arguments } from 'yargs-parser';
export function option(key: string, options: yargs.Options = {}): yargs.Argv {
return yargs.option.apply(yargs, [key, options]);
export interface OptionConfig {
/** Aliases for the argument, can be a string or array */
alias?: string | string[];
/** Argument should be an array */
array?: boolean;
/** Argument should be parsed as booleans: `{ boolean: ['x', 'y'] }`. */
boolean?: boolean;
/**
* Provide a custom synchronous function that returns a coerced value from the argument provided (or throws an error), e.g.
* `{ coerce: function (arg) { return modifiedArg } }`.
*/
coerce?: (arg: any) => any;
/** Indicate a key that should be used as a counter, e.g., `-vvv = {v: 3}`. */
count?: boolean;
/** Provide default values for keys: `{ default: { x: 33, y: 'hello world!' } }`. */
default?: { [key: string]: any };
/** Specify that a key requires n arguments: `{ narg: {x: 2} }`. */
narg?: number;
/** `path.normalize()` will be applied to values set to this key. */
normalize?: boolean;
/** Keys should be treated as strings (even if they resemble a number `-x 33`). */
string?: boolean;
/** Keys should be treated as numbers. */
number?: boolean;
/** A description of the option */
describe?: string;
}
export function argv(): yargs.Arguments {
return yargs.argv;
let argOptions: Options = {};
const descriptions: { [key: string]: string } = {};
const processArgs = process.argv.slice(2);
export function option(key: string, options: OptionConfig = {}) {
const booleanArgs = ['array', 'boolean', 'count', 'normalize', 'string', 'number'] as const;
const assignArgs = ['alias', 'coerce', 'default'] as const;
for (const argName of booleanArgs) {
if (options[argName]) {
if (!argOptions[argName]) {
argOptions[argName] = [];
}
const argOpts = argOptions[argName]! as string[];
if (argOpts.indexOf(key) === -1) {
argOpts.push(key);
}
}
}
for (const argName of assignArgs) {
if (options[argName]) {
if (!argOptions[argName]) {
argOptions[argName] = {};
}
argOptions[argName]![key] = options[argName];
}
}
if (options.describe) {
descriptions[key] = options.describe;
}
}
export function argv(): Arguments {
return parser(processArgs, argOptions);
}
export function parseCommand(): string | null {
const positionals = argv()._;
if (positionals.length > 0) {
return positionals[0];
}
return null;
}

Просмотреть файл

@ -1,12 +1,6 @@
import { sync as resolveSync } from 'resolve';
import path from 'path';
// It is important to keep this line like this:
// 1. it cannot be an import because TS will try to do type checks which isn't available in @types/yargs
// 2. this breaks a require.cache, which is needed because we need a new instance of yargs to check the config
// - this is because of the timing of when tasks are defined vs when this resolve is called the first time
// to figure out config path)
const yargsFn = require('yargs/yargs');
import { argv } from './option';
let resolvePaths: string[] = [__dirname];
@ -62,7 +56,8 @@ export function resolve(moduleName: string, cwd?: string): string | null {
if (!cwd) {
cwd = process.cwd();
}
const configArg = yargsFn(process.argv.slice(1).filter(a => a !== '--help')).argv.config;
const configArg = argv().config;
const configFilePath = configArg ? path.resolve(path.dirname(configArg)) : undefined;
const allResolvePaths = [cwd, ...(configFilePath ? [configFilePath] : []), ...resolvePaths];

Просмотреть файл

@ -1,9 +1,7 @@
import yargs from 'yargs';
import { undertaker } from './undertaker';
import { wrapTask } from './wrapTask';
import { TaskFunction } from './interfaces';
import { logger } from 'just-task-logger';
import { registerCachedTask, isCached, saveCache } from './cache';
import { registerCachedTask } from './cache';
export function task(firstParam: string | TaskFunction, secondParam?: string | TaskFunction, thirdParam?: TaskFunction): TaskFunction {
const argCount = arguments.length;
@ -19,7 +17,6 @@ export function task(firstParam: string | TaskFunction, secondParam?: string | T
};
undertaker.task(firstParam, wrapped);
yargs.command(getCommandModule(firstParam, ''));
return wrapped;
} else if (argCount === 2 && isString(firstParam) && isTaskFunction(secondParam)) {
@ -31,7 +28,6 @@ export function task(firstParam: string | TaskFunction, secondParam?: string | T
};
undertaker.task(firstParam, wrapped);
yargs.command(getCommandModule(firstParam, ''));
return wrapped;
} else if (argCount === 3 && isString(firstParam) && isString(secondParam) && isTaskFunction(thirdParam)) {
@ -41,8 +37,9 @@ export function task(firstParam: string | TaskFunction, secondParam?: string | T
registerCachedTask(firstParam);
};
wrapped.description = secondParam;
undertaker.task(firstParam, wrapped);
yargs.command(getCommandModule(firstParam, secondParam));
return wrapped;
} else {
@ -57,21 +54,3 @@ function isString(param: string | TaskFunction | undefined): param is string {
function isTaskFunction(param: string | TaskFunction | undefined): param is TaskFunction {
return typeof param === 'function';
}
function getCommandModule(taskName: string, describe?: string): yargs.CommandModule {
return {
command: taskName,
describe,
...(taskName === 'default' ? { aliases: ['*'] } : {}),
handler(_argvParam: any) {
if (isCached(taskName)) {
logger.info(`Skipped ${taskName} since it was cached`);
return;
}
return undertaker.parallel(taskName)(() => {
saveCache(taskName);
});
}
};
}

Просмотреть файл

@ -3,5 +3,5 @@
"compilerOptions": {
"outDir": "lib"
},
"include": ["src"]
"include": ["src", "types"]
}

Просмотреть файл

@ -5,8 +5,23 @@ const DefaultReporter = require('@jest/reporters').DefaultReporter;
* when there are no errors.
*/
class JestReporter extends DefaultReporter {
constructor(...args) {
super(...args);
this._isLoggingError = false;
}
log(message) {
process.stdout.write(message + '\n');
if (this._isLoggingError) {
process.stderr.write(message + '\n');
} else {
process.stdout.write(message + '\n');
}
}
printTestFileFailureMessage(...args) {
this._isLoggingError = true;
super.printTestFileFailureMessage(...args);
this._isLoggingError = false;
}
}

Просмотреть файл

@ -2271,7 +2271,7 @@
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370"
integrity sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ==
"@types/tar@^4.0.0":
"@types/tar@^4.0.0", "@types/tar@^4.0.3":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@types/tar/-/tar-4.0.3.tgz#e2cce0b8ff4f285293243f5971bd7199176ac489"
integrity sha512-Z7AVMMlkI8NTWF0qGhC4QIX0zkV/+y0J8x7b/RsHrN0310+YNjoJd8UrApCiGBCWtKjxS9QhNqLi2UJNToh5hA==
@ -2328,6 +2328,11 @@
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.0.0.tgz#453743c5bbf9f1bed61d959baab5b06be029b2d0"
integrity sha512-wBlsw+8n21e6eTd4yVv8YD/E3xq0O6nNnJIquutAsFGE7EyMKz7W6RNT6BRu1SmdgmlCZ9tb0X+j+D6HGr8pZw==
"@types/yargs-parser@^15.0.0":
version "15.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==
"@types/yargs@12.0.1":
version "12.0.1"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.1.tgz#c5ce4ad64499010ae4dc2acd9b14d49749a44233"
@ -3248,7 +3253,7 @@ babylon@^6.17.4:
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
bach@^1.0.0:
bach@^1.0.0, bach@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880"
integrity sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=
@ -13310,6 +13315,11 @@ typescript@~3.4.4:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99"
integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==
typescript@~3.8.3:
version "3.8.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061"
integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==
uglify-js@3.4.x:
version "3.4.10"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
@ -13359,7 +13369,7 @@ undertaker-registry@^1.0.0, undertaker-registry@^1.0.1:
resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50"
integrity sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=
undertaker@^1.2.0:
undertaker@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/undertaker/-/undertaker-1.2.1.tgz#701662ff8ce358715324dfd492a4f036055dfe4b"
integrity sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==
@ -14040,6 +14050,14 @@ yargs-parser@^13.1.0, yargs-parser@^13.1.1:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^18.1.2:
version "18.1.2"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.2.tgz#2f482bea2136dbde0861683abea7756d30b504f1"
integrity sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"