fix(misc): create-nx-workspace errors should display properly (#15988)

This commit is contained in:
Craigory Coppola 2023-03-30 18:27:36 -04:00 committed by GitHub
parent d24d850494
commit 5d51ed9be5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 158 additions and 109 deletions

View File

@ -41,7 +41,7 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
'strip-dashed': true, 'strip-dashed': true,
'dot-notation': true, 'dot-notation': true,
}) })
.command( .command<Arguments>(
// this is the default and only command // this is the default and only command
'$0 [name] [options]', '$0 [name] [options]',
'Create a new Nx workspace', 'Create a new Nx workspace',
@ -152,7 +152,7 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
throw error; throw error;
}); });
}, },
[normalizeArgsMiddleware] [normalizeArgsMiddleware as yargs.MiddlewareFunction<{}>]
) )
.help('help', chalk.dim`Show help`) .help('help', chalk.dim`Show help`)
.updateLocale(yargsDecorator) .updateLocale(yargsDecorator)
@ -195,7 +195,7 @@ async function normalizeArgsMiddleware(
"Let's create a new workspace [https://nx.dev/getting-started/intro]", "Let's create a new workspace [https://nx.dev/getting-started/intro]",
}); });
let thirdPartyPreset: string; let thirdPartyPreset: string | null;
try { try {
thirdPartyPreset = await getThirdPartyPreset(argv.preset); thirdPartyPreset = await getThirdPartyPreset(argv.preset);
} catch (e) { } catch (e) {
@ -262,7 +262,7 @@ async function normalizeArgsMiddleware(
} }
} else { } else {
name = await determineRepoName(argv); name = await determineRepoName(argv);
appName = await determineAppName(preset, argv); appName = await determineAppName(preset as Preset, argv);
if (preset === Preset.ReactMonorepo) { if (preset === Preset.ReactMonorepo) {
bundler = await determineBundler(argv); bundler = await determineBundler(argv);
} }
@ -276,7 +276,7 @@ async function normalizeArgsMiddleware(
(argv.interactive ? await determineRouting(argv) : true); (argv.interactive ? await determineRouting(argv) : true);
} }
} }
style = await determineStyle(preset, argv); style = await determineStyle(preset as Preset, argv);
} }
const packageManager = await determinePackageManager(argv); const packageManager = await determinePackageManager(argv);
@ -439,7 +439,7 @@ async function determinePackageManager(
], ],
}, },
]) ])
.then((a: { packageManager }) => a.packageManager); .then((a) => a.packageManager);
} }
return Promise.resolve(detectInvokedPackageManager()); return Promise.resolve(detectInvokedPackageManager());
@ -453,7 +453,7 @@ async function determineDefaultBase(
} }
if (parsedArgs.allPrompts) { if (parsedArgs.allPrompts) {
return enquirer return enquirer
.prompt([ .prompt<{ DefaultBase: string }>([
{ {
name: 'DefaultBase', name: 'DefaultBase',
message: `Main branch name `, message: `Main branch name `,
@ -461,7 +461,7 @@ async function determineDefaultBase(
type: 'input', type: 'input',
}, },
]) ])
.then((a: { DefaultBase: string }) => { .then((a) => {
if (!a.DefaultBase) { if (!a.DefaultBase) {
output.error({ output.error({
title: 'Invalid branch name', title: 'Invalid branch name',
@ -524,14 +524,14 @@ async function determineAppName(
} }
return enquirer return enquirer
.prompt([ .prompt<{ AppName: string }>([
{ {
name: 'AppName', name: 'AppName',
message: `Application name `, message: `Application name `,
type: 'input', type: 'input',
}, },
]) ])
.then((a: { AppName: string }) => { .then((a) => {
if (!a.AppName) { if (!a.AppName) {
output.error({ output.error({
title: 'Invalid name', title: 'Invalid name',
@ -571,7 +571,7 @@ async function determineFramework(
if (!parsedArgs.framework) { if (!parsedArgs.framework) {
return enquirer return enquirer
.prompt([ .prompt<{ framework: Framework }>([
{ {
message: 'What framework should be used?', message: 'What framework should be used?',
type: 'autocomplete', type: 'autocomplete',
@ -579,7 +579,7 @@ async function determineFramework(
choices: frameworkChoices, choices: frameworkChoices,
}, },
]) ])
.then((a: { framework: string }) => a.framework); .then((a) => a.framework);
} }
const foundFramework = frameworkChoices const foundFramework = frameworkChoices
@ -607,7 +607,7 @@ async function determineStandaloneApi(
): Promise<boolean> { ): Promise<boolean> {
if (parsedArgs.standaloneApi === undefined) { if (parsedArgs.standaloneApi === undefined) {
return enquirer return enquirer
.prompt([ .prompt<{ standaloneApi: 'Yes' | 'No' }>([
{ {
name: 'standaloneApi', name: 'standaloneApi',
message: message:
@ -625,7 +625,7 @@ async function determineStandaloneApi(
initial: 'No' as any, initial: 'No' as any,
}, },
]) ])
.then((a: { standaloneApi: 'Yes' | 'No' }) => a.standaloneApi === 'Yes'); .then((a) => a.standaloneApi === 'Yes');
} }
return parsedArgs.standaloneApi; return parsedArgs.standaloneApi;
@ -636,7 +636,7 @@ async function determineDockerfile(
): Promise<boolean> { ): Promise<boolean> {
if (parsedArgs.docker === undefined) { if (parsedArgs.docker === undefined) {
return enquirer return enquirer
.prompt([ .prompt<{ docker: 'Yes' | 'No' }>([
{ {
name: 'docker', name: 'docker',
message: message:
@ -654,7 +654,7 @@ async function determineDockerfile(
initial: 'No' as any, initial: 'No' as any,
}, },
]) ])
.then((a: { docker: 'Yes' | 'No' }) => a.docker === 'Yes'); .then((a) => a.docker === 'Yes');
} else { } else {
return Promise.resolve(parsedArgs.docker); return Promise.resolve(parsedArgs.docker);
} }
@ -663,7 +663,7 @@ async function determineDockerfile(
async function determineStyle( async function determineStyle(
preset: Preset, preset: Preset,
parsedArgs: yargs.Arguments<Arguments> parsedArgs: yargs.Arguments<Arguments>
): Promise<string> { ): Promise<string | null> {
if ( if (
preset === Preset.Apps || preset === Preset.Apps ||
preset === Preset.Core || preset === Preset.Core ||
@ -727,7 +727,7 @@ async function determineStyle(
if (!parsedArgs.style) { if (!parsedArgs.style) {
return enquirer return enquirer
.prompt([ .prompt<{ style: string }>([
{ {
name: 'style', name: 'style',
message: `Default stylesheet format `, message: `Default stylesheet format `,
@ -762,7 +762,7 @@ async function determineRouting(
): Promise<boolean> { ): Promise<boolean> {
if (!parsedArgs.routing) { if (!parsedArgs.routing) {
return enquirer return enquirer
.prompt([ .prompt<{ routing: 'Yes' | 'No' }>([
{ {
name: 'routing', name: 'routing',
message: 'Would you like to add routing?', message: 'Would you like to add routing?',
@ -779,7 +779,7 @@ async function determineRouting(
initial: 'Yes' as any, initial: 'Yes' as any,
}, },
]) ])
.then((a: { routing: 'Yes' | 'No' }) => a.routing === 'Yes'); .then((a) => a.routing === 'Yes');
} }
return parsedArgs.routing; return parsedArgs.routing;
@ -801,7 +801,7 @@ async function determineBundler(
if (!parsedArgs.bundler) { if (!parsedArgs.bundler) {
return enquirer return enquirer
.prompt([ .prompt<{ bundler: Bundler }>([
{ {
name: 'bundler', name: 'bundler',
message: `Bundler to be used to build the application`, message: `Bundler to be used to build the application`,
@ -810,7 +810,7 @@ async function determineBundler(
choices: choices, choices: choices,
}, },
]) ])
.then((a: { bundler: 'vite' | 'webpack' }) => a.bundler); .then((a) => a.bundler);
} }
const foundBundler = choices.find( const foundBundler = choices.find(
@ -838,7 +838,7 @@ async function determineNxCloud(
): Promise<boolean> { ): Promise<boolean> {
if (parsedArgs.nxCloud === undefined) { if (parsedArgs.nxCloud === undefined) {
return enquirer return enquirer
.prompt([ .prompt<{ NxCloud: 'Yes' | 'No' }>([
{ {
name: 'NxCloud', name: 'NxCloud',
message: messages.getPromptMessage('nxCloudCreation'), message: messages.getPromptMessage('nxCloudCreation'),
@ -856,7 +856,7 @@ async function determineNxCloud(
initial: 'Yes' as any, initial: 'Yes' as any,
}, },
]) ])
.then((a: { NxCloud: 'Yes' | 'No' }) => a.NxCloud === 'Yes'); .then((a) => a.NxCloud === 'Yes');
} else { } else {
return parsedArgs.nxCloud; return parsedArgs.nxCloud;
} }
@ -886,7 +886,7 @@ async function determineCI(
if (parsedArgs.allPrompts) { if (parsedArgs.allPrompts) {
return ( return (
enquirer enquirer
.prompt([ .prompt<{ CI: string }>([
{ {
name: 'CI', name: 'CI',
message: `CI workflow file to generate? `, message: `CI workflow file to generate? `,

View File

@ -2,13 +2,14 @@ import * as ora from 'ora';
import { join } from 'path'; import { join } from 'path';
import { CreateWorkspaceOptions } from './create-workspace-options'; import { CreateWorkspaceOptions } from './create-workspace-options';
import { execAndWait } from './utils/child-process-utils'; import { execAndWait } from './utils/child-process-utils';
import { mapErrorToBodyLines } from './utils/error-utils';
import { output } from './utils/output'; import { output } from './utils/output';
import { import {
getPackageManagerCommand, getPackageManagerCommand,
getPackageManagerVersion, getPackageManagerVersion,
PackageManager, PackageManager,
} from './utils/package-manager'; } from './utils/package-manager';
import { getFileName, mapErrorToBodyLines } from './utils/string-utils'; import { getFileName } from './utils/string-utils';
import { unparse } from './utils/unparse'; import { unparse } from './utils/unparse';
/** /**
@ -67,10 +68,14 @@ export async function createEmptyWorkspace<T extends CreateWorkspaceOptions>(
); );
} catch (e) { } catch (e) {
workspaceSetupSpinner.fail(); workspaceSetupSpinner.fail();
if (e instanceof Error) {
output.error({ output.error({
title: `Nx failed to create a workspace.`, title: `Nx failed to create a workspace.`,
bodyLines: mapErrorToBodyLines(e), bodyLines: mapErrorToBodyLines(e),
}); });
} else {
console.error(e);
}
process.exit(1); process.exit(1);
} finally { } finally {
workspaceSetupSpinner.stop(); workspaceSetupSpinner.stop();

View File

@ -10,8 +10,8 @@ import {
} from './utils/package-manager'; } from './utils/package-manager';
import { execAndWait } from './utils/child-process-utils'; import { execAndWait } from './utils/child-process-utils';
import { output } from './utils/output'; import { output } from './utils/output';
import { mapErrorToBodyLines } from './utils/string-utils';
import { nxVersion } from './utils/nx/nx-version'; import { nxVersion } from './utils/nx/nx-version';
import { mapErrorToBodyLines } from './utils/error-utils';
/** /**
* Creates a temporary directory and installs Nx in it. * Creates a temporary directory and installs Nx in it.
@ -44,10 +44,14 @@ export async function createSandbox(packageManager: PackageManager) {
installSpinner.succeed(); installSpinner.succeed();
} catch (e) { } catch (e) {
installSpinner.fail(); installSpinner.fail();
if (e instanceof Error) {
output.error({ output.error({
title: `Nx failed to install dependencies`, title: `Nx failed to install dependencies`,
bodyLines: mapErrorToBodyLines(e), bodyLines: mapErrorToBodyLines(e),
}); });
} else {
console.error(e);
}
process.exit(1); process.exit(1);
} finally { } finally {
installSpinner.stop(); installSpinner.stop();

View File

@ -10,6 +10,7 @@ import { messages, recordStat } from './utils/nx/ab-testing';
import { initializeGitRepo } from './utils/git/git'; import { initializeGitRepo } from './utils/git/git';
import { nxVersion } from './utils/nx/nx-version'; import { nxVersion } from './utils/nx/nx-version';
import { getThirdPartyPreset } from './utils/preset/get-third-party-preset'; import { getThirdPartyPreset } from './utils/preset/get-third-party-preset';
import { mapErrorToBodyLines } from './utils/error-utils';
export async function createWorkspace<T extends CreateWorkspaceOptions>( export async function createWorkspace<T extends CreateWorkspaceOptions>(
preset: string, preset: string,
@ -59,23 +60,27 @@ export async function createWorkspace<T extends CreateWorkspaceOptions>(
name, name,
ci, ci,
packageManager, packageManager,
nxCloud && nxCloudInstallRes.code === 0 nxCloud && nxCloudInstallRes?.code === 0
); );
} }
if (!skipGit) { if (!skipGit) {
try { try {
await initializeGitRepo(directory, { defaultBase, commit }); await initializeGitRepo(directory, { defaultBase, commit });
} catch (e) { } catch (e) {
if (e instanceof Error) {
output.error({ output.error({
title: 'Could not initialize git repository', title: 'Could not initialize git repository',
bodyLines: [e.message], bodyLines: mapErrorToBodyLines(e),
}); });
} else {
console.error(e);
}
} }
} }
showNxWarning(name); showNxWarning(name);
if (nxCloud && nxCloudInstallRes.code === 0) { if (nxCloud && nxCloudInstallRes?.code === 0) {
printNxCloudSuccessMessage(nxCloudInstallRes.stdout); printNxCloudSuccessMessage(nxCloudInstallRes.stdout);
} }

View File

@ -1,6 +1,7 @@
import { spawn, exec } from 'child_process'; import { spawn, exec } from 'child_process';
import { writeFileSync } from 'fs'; import { writeFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { CreateNxWorkspaceError } from './error-utils';
/** /**
* Use spawn only for interactive shells * Use spawn only for interactive shells
@ -24,7 +25,7 @@ export function spawnAndWait(command: string, args: string[], cwd: string) {
} }
export function execAndWait(command: string, cwd: string) { export function execAndWait(command: string, cwd: string) {
return new Promise((res, rej) => { return new Promise<{ code: number; stdout: string }>((res, rej) => {
exec( exec(
command, command,
{ cwd, env: { ...process.env, NX_DAEMON: 'false' } }, { cwd, env: { ...process.env, NX_DAEMON: 'false' } },
@ -32,7 +33,7 @@ export function execAndWait(command: string, cwd: string) {
if (error) { if (error) {
const logFile = join(cwd, 'error.log'); const logFile = join(cwd, 'error.log');
writeFileSync(logFile, `${stdout}\n${stderr}`); writeFileSync(logFile, `${stdout}\n${stderr}`);
rej({ code: error.code, logFile, logMessage: stderr }); rej(new CreateNxWorkspaceError(stderr, error.code, logFile));
} else { } else {
res({ code: 0, stdout }); res({ code: 0, stdout });
} }

View File

@ -2,9 +2,10 @@ import * as ora from 'ora';
import { join } from 'path'; import { join } from 'path';
import { execAndWait } from '../child-process-utils'; import { execAndWait } from '../child-process-utils';
import { mapErrorToBodyLines } from '../error-utils';
import { output } from '../output'; import { output } from '../output';
import { getPackageManagerCommand, PackageManager } from '../package-manager'; import { getPackageManagerCommand, PackageManager } from '../package-manager';
import { getFileName, mapErrorToBodyLines } from '../string-utils'; import { getFileName } from '../string-utils';
export async function setupCI( export async function setupCI(
name: string, name: string,
@ -32,11 +33,14 @@ export async function setupCI(
return res; return res;
} catch (e) { } catch (e) {
ciSpinner.fail(); ciSpinner.fail();
if (e instanceof Error) {
output.error({ output.error({
title: `Nx failed to generate CI workflow`, title: `Nx failed to generate CI workflow`,
bodyLines: mapErrorToBodyLines(e), bodyLines: mapErrorToBodyLines(e),
}); });
} else {
console.error(e);
}
process.exit(1); process.exit(1);
} finally { } finally {

View File

@ -0,0 +1,32 @@
export class CreateNxWorkspaceError extends Error {
constructor(
public logMessage: string,
public code: number | null | undefined,
public logFile: string
) {
super(logMessage);
this.name = 'CreateNxWorkspaceError';
}
}
export function mapErrorToBodyLines(error: Error): string[] {
const errorLines = error.message?.split('\n').filter((line) => !!line);
if (errorLines.length < 3) {
const lines = [`Error: ${error.message}`];
if (process.env.NX_VERBOSE_LOGGING) {
lines.push(`Stack: ${error.stack}`);
}
return lines;
}
const lines =
error instanceof CreateNxWorkspaceError
? [`Exit code: ${error.code}`, `Log file: ${error.logFile}`]
: [];
if (process.env.NX_VERBOSE_LOGGING) {
lines.push(`Error: ${error.message}`);
lines.push(`Stack: ${error.stack}`);
}
return lines;
}

View File

@ -2,10 +2,10 @@ import { execSync, spawn, SpawnOptions } from 'child_process';
import { deduceDefaultBase } from './default-base'; import { deduceDefaultBase } from './default-base';
import { output } from '../output'; import { output } from '../output';
export function checkGitVersion(): string | null { export function checkGitVersion(): string | null | undefined {
try { try {
let gitVersionOutput = execSync('git --version').toString().trim(); let gitVersionOutput = execSync('git --version').toString().trim();
return gitVersionOutput.match(/[0-9]+\.[0-9]+\.+[0-9]+/)[0]; return gitVersionOutput.match(/[0-9]+\.[0-9]+\.+[0-9]+/)?.[0];
} catch { } catch {
return null; return null;
} }
@ -15,7 +15,7 @@ export async function initializeGitRepo(
directory: string, directory: string,
options: { options: {
defaultBase: string; defaultBase: string;
commit: { message: string; name: string; email: string }; commit?: { message: string; name: string; email: string };
} }
) { ) {
const execute = (args: ReadonlyArray<string>, ignoreErrorStream = false) => { const execute = (args: ReadonlyArray<string>, ignoreErrorStream = false) => {
@ -27,13 +27,13 @@ export async function initializeGitRepo(
cwd: directory, cwd: directory,
env: { env: {
...process.env, ...process.env,
...(options.commit.name ...(options.commit?.name
? { ? {
GIT_AUTHOR_NAME: options.commit.name, GIT_AUTHOR_NAME: options.commit.name,
GIT_COMMITTER_NAME: options.commit.name, GIT_COMMITTER_NAME: options.commit.name,
} }
: {}), : {}),
...(options.commit.email ...(options.commit?.email
? { ? {
GIT_AUTHOR_EMAIL: options.commit.email, GIT_AUTHOR_EMAIL: options.commit.email,
GIT_COMMITTER_EMAIL: options.commit.email, GIT_COMMITTER_EMAIL: options.commit.email,

View File

@ -1,7 +1,6 @@
import { isCI } from '../ci/is-ci'; import { isCI } from '../ci/is-ci';
export class PromptMessages { const messageOptions = {
private messages = {
nxCloudCreation: [ nxCloudCreation: [
{ {
code: 'set-up-distributed-caching-ci', code: 'set-up-distributed-caching-ci',
@ -14,26 +13,33 @@ export class PromptMessages {
message: `Enable distributed caching to make your CI faster?`, message: `Enable distributed caching to make your CI faster?`,
}, },
], ],
}; } as const;
private selectedMessages = {}; type MessageKey = keyof typeof messageOptions;
getPromptMessage(key: string): string { export class PromptMessages {
private selectedMessages: { [key in MessageKey]?: number } = {};
getPromptMessage(key: MessageKey): string {
if (this.selectedMessages[key] === undefined) { if (this.selectedMessages[key] === undefined) {
if (process.env.NX_GENERATE_DOCS_PROCESS === 'true') { if (process.env.NX_GENERATE_DOCS_PROCESS === 'true') {
this.selectedMessages[key] = 0; this.selectedMessages[key] = 0;
} else { } else {
this.selectedMessages[key] = Math.floor( this.selectedMessages[key] = Math.floor(
Math.random() * this.messages[key].length Math.random() * messageOptions[key].length
); );
} }
} }
return this.messages[key][this.selectedMessages[key]].message; return messageOptions[key][this.selectedMessages[key]!].message;
} }
codeOfSelectedPromptMessage(key: string): string { codeOfSelectedPromptMessage(key: MessageKey): string {
if (this.selectedMessages[key] === undefined) return null; const selected = this.selectedMessages[key];
return this.messages[key][this.selectedMessages[key]].code; if (selected === undefined) {
return messageOptions[key][0].code;
} else {
return messageOptions[key][selected].code;
}
} }
} }

View File

@ -3,7 +3,8 @@ import { join } from 'path';
import { execAndWait } from '../child-process-utils'; import { execAndWait } from '../child-process-utils';
import { output } from '../output'; import { output } from '../output';
import { getPackageManagerCommand, PackageManager } from '../package-manager'; import { getPackageManagerCommand, PackageManager } from '../package-manager';
import { getFileName, mapErrorToBodyLines } from '../string-utils'; import { getFileName } from '../string-utils';
import { mapErrorToBodyLines } from '../error-utils';
export async function setupNxCloud( export async function setupNxCloud(
name: string, name: string,
@ -21,10 +22,14 @@ export async function setupNxCloud(
} catch (e) { } catch (e) {
nxCloudSpinner.fail(); nxCloudSpinner.fail();
if (e instanceof Error) {
output.error({ output.error({
title: `Nx failed to setup NxCloud`, title: `Nx failed to setup NxCloud`,
bodyLines: mapErrorToBodyLines(e), bodyLines: mapErrorToBodyLines(e),
}); });
} else {
console.error(e);
}
process.exit(1); process.exit(1);
} finally { } finally {

View File

@ -95,10 +95,10 @@ class CLIOutput {
applyNxPrefix(color = 'cyan', text: string): string { applyNxPrefix(color = 'cyan', text: string): string {
let nxPrefix = ''; let nxPrefix = '';
if (chalk[color]) { if ((chalk as any)[color]) {
nxPrefix = `${chalk[color]('>')} ${chalk.reset.inverse.bold[color]( nxPrefix = `${(chalk as any)[color]('>')} ${(
' NX ' chalk as any
)}`; ).reset.inverse.bold[color](' NX ')}`;
} else { } else {
nxPrefix = `${chalk.keyword(color)( nxPrefix = `${chalk.keyword(color)(
'>' '>'
@ -119,7 +119,9 @@ class CLIOutput {
addVerticalSeparatorWithoutNewLines(color = 'gray') { addVerticalSeparatorWithoutNewLines(color = 'gray') {
this.writeToStdOut( this.writeToStdOut(
`${this.X_PADDING}${chalk.dim[color](this.VERTICAL_SEPARATOR)}${EOL}` `${this.X_PADDING}${(chalk as any).dim[color](
this.VERTICAL_SEPARATOR
)}${EOL}`
); );
} }
@ -217,7 +219,7 @@ class CLIOutput {
this.writeOutputTitle({ this.writeOutputTitle({
color: 'cyan', color: 'cyan',
title: color ? chalk[color](title) : title, title: color ? (chalk as any)[color](title) : title,
}); });
this.writeOptionalOutputBody(bodyLines); this.writeOptionalOutputBody(bodyLines);

View File

@ -24,7 +24,7 @@ export async function getThirdPartyPreset(
bodyLines: [ bodyLines: [
`There was an error with the preset npm package you provided:`, `There was an error with the preset npm package you provided:`,
'', '',
...validateResult.errors, ...(validateResult.errors ?? []),
], ],
}); });
throw new Error('Invalid preset npm package'); throw new Error('Invalid preset npm package');

View File

@ -8,19 +8,3 @@ export function getFileName(name: string) {
.toLowerCase() .toLowerCase()
.replace(/[ _]/g, '-'); .replace(/[ _]/g, '-');
} }
export function mapErrorToBodyLines(error: {
logMessage: string;
code: number;
logFile: string;
}): string[] {
if (error.logMessage?.split('\n').filter((line) => !!line).length < 3) {
// print entire log message only if it's only a single message
return [`Error: ${error.logMessage}`];
}
const lines = [`Exit code: ${error.code}`, `Log file: ${error.logFile}`];
if (process.env.NX_VERBOSE_LOGGING) {
lines.push(`Error: ${error.logMessage}`);
}
return lines;
}

View File

@ -1,9 +1,9 @@
import { flatten } from 'flat'; import { flatten } from 'flat';
export function unparse(options: Object): string[] { export function unparse(options: Object): string[] {
const unparsed = []; const unparsed: string[] = [];
for (const key of Object.keys(options)) { for (const key of Object.keys(options)) {
const value = options[key]; const value = options[key as keyof typeof options];
unparseOption(key, value, unparsed); unparseOption(key, value, unparsed);
} }

View File

@ -14,8 +14,8 @@ export interface ValidateNpmResult {
} }
export function validateNpmPackage(name: string): ValidateNpmResult { export function validateNpmPackage(name: string): ValidateNpmResult {
let warnings = []; let warnings: string[] = [];
let errors = []; let errors: string[] = [];
if (name === null) { if (name === null) {
errors.push('name cannot be null'); errors.push('name cannot be null');

View File

@ -1,7 +1,8 @@
{ {
"extends": "../../tsconfig.base.json", "extends": "../../tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"types": ["node", "jest"] "types": ["node", "jest"],
"strict": true
}, },
"include": [], "include": [],
"files": [], "files": [],