fix(misc): create-nx-workspace errors should display properly (#15988)
This commit is contained in:
parent
d24d850494
commit
5d51ed9be5
@ -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? `,
|
||||||
|
|||||||
@ -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();
|
||||||
output.error({
|
if (e instanceof Error) {
|
||||||
title: `Nx failed to create a workspace.`,
|
output.error({
|
||||||
bodyLines: mapErrorToBodyLines(e),
|
title: `Nx failed to create a workspace.`,
|
||||||
});
|
bodyLines: mapErrorToBodyLines(e),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
} finally {
|
} finally {
|
||||||
workspaceSetupSpinner.stop();
|
workspaceSetupSpinner.stop();
|
||||||
|
|||||||
@ -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();
|
||||||
output.error({
|
if (e instanceof Error) {
|
||||||
title: `Nx failed to install dependencies`,
|
output.error({
|
||||||
bodyLines: mapErrorToBodyLines(e),
|
title: `Nx failed to install dependencies`,
|
||||||
});
|
bodyLines: mapErrorToBodyLines(e),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
} finally {
|
} finally {
|
||||||
installSpinner.stop();
|
installSpinner.stop();
|
||||||
|
|||||||
@ -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) {
|
||||||
output.error({
|
if (e instanceof Error) {
|
||||||
title: 'Could not initialize git repository',
|
output.error({
|
||||||
bodyLines: [e.message],
|
title: 'Could not initialize git repository',
|
||||||
});
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
32
packages/create-nx-workspace/src/utils/error-utils.ts
Normal file
32
packages/create-nx-workspace/src/utils/error-utils.ts
Normal 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;
|
||||||
|
}
|
||||||
@ -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,
|
||||||
|
|||||||
@ -1,39 +1,45 @@
|
|||||||
import { isCI } from '../ci/is-ci';
|
import { isCI } from '../ci/is-ci';
|
||||||
|
|
||||||
|
const messageOptions = {
|
||||||
|
nxCloudCreation: [
|
||||||
|
{
|
||||||
|
code: 'set-up-distributed-caching-ci',
|
||||||
|
message: `Enable distributed caching to make your CI faster`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
nxCloudMigration: [
|
||||||
|
{
|
||||||
|
code: 'make-ci-faster',
|
||||||
|
message: `Enable distributed caching to make your CI faster?`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
type MessageKey = keyof typeof messageOptions;
|
||||||
|
|
||||||
export class PromptMessages {
|
export class PromptMessages {
|
||||||
private messages = {
|
private selectedMessages: { [key in MessageKey]?: number } = {};
|
||||||
nxCloudCreation: [
|
|
||||||
{
|
|
||||||
code: 'set-up-distributed-caching-ci',
|
|
||||||
message: `Enable distributed caching to make your CI faster`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
nxCloudMigration: [
|
|
||||||
{
|
|
||||||
code: 'make-ci-faster',
|
|
||||||
message: `Enable distributed caching to make your CI faster?`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
private selectedMessages = {};
|
getPromptMessage(key: MessageKey): string {
|
||||||
|
|
||||||
getPromptMessage(key: string): 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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();
|
||||||
|
|
||||||
output.error({
|
if (e instanceof Error) {
|
||||||
title: `Nx failed to setup NxCloud`,
|
output.error({
|
||||||
bodyLines: mapErrorToBodyLines(e),
|
title: `Nx failed to setup NxCloud`,
|
||||||
});
|
bodyLines: mapErrorToBodyLines(e),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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');
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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');
|
||||||
|
|||||||
@ -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": [],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user