feat(misc): expose nx init command flags (#16287)

This commit is contained in:
Leosvel Pérez Espinosa 2023-04-19 18:32:51 +01:00 committed by GitHub
parent 5d7ad348e0
commit 6677a9c932
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 141 additions and 143 deletions

View File

@ -30,7 +30,9 @@ describe('nx init (Angular CLI)', () => {
it('should successfully convert an Angular CLI workspace to an Nx standalone workspace', () => {
const output = runCommand(
`${pmc.runUninstalledPackage} nx@${getPublishedVersion()} init -y`
`${
pmc.runUninstalledPackage
} nx@${getPublishedVersion()} init --no-interactive`
);
expect(output).toContain('🎉 Done!');
@ -64,7 +66,7 @@ describe('nx init (Angular CLI)', () => {
const output = runCommand(
`${
pmc.runUninstalledPackage
} nx@${getPublishedVersion()} init -y --integrated`
} nx@${getPublishedVersion()} init --integrated --no-interactive`
);
expect(output).toContain('🎉 Done!');

View File

@ -40,7 +40,7 @@ describe('nx init (Monorepo)', () => {
const output = runCommand(
`${
pmc.runUninstalledPackage
} nx@${getPublishedVersion()} init -y --cacheable=build`
} nx@${getPublishedVersion()} init --cacheable=build --no-interactive`
);
expect(output).toContain('🎉 Done!');

View File

@ -34,7 +34,7 @@ describe('nx init (for NestCLI)', () => {
const output = execSync(
`${
pmc.runUninstalledPackage
} nx@${getPublishedVersion()} init -y --cacheable=format`,
} nx@${getPublishedVersion()} init --cacheable=format --no-interactive`,
{
cwd: projectRoot,
encoding: 'utf-8',

View File

@ -32,7 +32,7 @@ describe('nx init (NPM repo)', () => {
const output = runCommand(
`${
pmc.runUninstalledPackage
} nx@${getPublishedVersion()} init -y --cacheable=echo`
} nx@${getPublishedVersion()} init --cacheable=echo --no-interactive`
);
console.log(output);
expect(output).toContain('Enabled computation caching');
@ -61,7 +61,7 @@ describe('nx init (NPM repo)', () => {
runCommand(
`${
pmc.runUninstalledPackage
} nx@${getPublishedVersion()} init -y --cacheable=compound`
} nx@${getPublishedVersion()} init --cacheable=compound --no-interactive`
);
const output = runCommand('npm run compound TEST');

View File

@ -14,7 +14,13 @@ import { directoryExists, readJsonFile } from '../utils/fileutils';
import { PackageJson } from '../utils/package-json';
export interface InitArgs {
addE2e: boolean;
force: boolean;
integrated: boolean;
interactive: boolean;
vite: boolean;
nxCloud?: boolean;
cacheable?: string[];
}
export async function initHandler(options: InitArgs) {
@ -40,15 +46,15 @@ export async function initHandler(options: InitArgs) {
} else if (existsSync('package.json')) {
const packageJson: PackageJson = readJsonFile('package.json');
if (existsSync('angular.json')) {
await addNxToAngularCliRepo(options.integrated);
await addNxToAngularCliRepo(options);
} else if (isCRA(packageJson)) {
await addNxToCraRepo(options.integrated);
await addNxToCraRepo(options);
} else if (isNestCLI(packageJson)) {
await addNxToNest(packageJson);
await addNxToNest(options, packageJson);
} else if (isMonorepo(packageJson)) {
await addNxToMonorepo();
await addNxToMonorepo(options);
} else {
await addNxToNpmRepo();
await addNxToNpmRepo(options);
}
} else {
const useDotNxFolder = await prompt<{ useDotNxFolder: string }>([

View File

@ -2,14 +2,12 @@ import * as chalk from 'chalk';
import { execSync } from 'child_process';
import * as path from 'path';
import * as yargs from 'yargs';
import { nxVersion } from '../utils/versions';
import { examples } from './examples';
import { workspaceRoot } from '../utils/workspace-root';
import { getPackageManagerCommand } from '../utils/package-manager';
import { writeJsonFile } from '../utils/fileutils';
import { WatchArguments } from './watch';
import { runNxSync } from '../utils/child-process';
import { stripIndents } from '../utils/strip-indents';
import { writeJsonFile } from '../utils/fileutils';
import { getPackageManagerCommand } from '../utils/package-manager';
import { workspaceRoot } from '../utils/workspace-root';
import { examples } from './examples';
import { WatchArguments } from './watch';
// Ensure that the output takes up the available width of the terminal.
yargs.wrap(yargs.terminalWidth());
@ -305,7 +303,7 @@ export const commandsObject = yargs
command: 'init',
describe:
'Adds Nx to any type of workspace. It installs nx, creates an nx.json configuration file and optionally sets up distributed caching. For more info, check https://nx.dev/recipes/adopting-nx.',
builder: (yargs) => withIntegratedOption(yargs),
builder: (yargs) => withInitOptions(yargs),
handler: async (args: any) => {
await (await import('./init')).initHandler(args);
process.exit(0);
@ -1122,14 +1120,54 @@ function withListOptions(yargs) {
});
}
function withIntegratedOption(yargs) {
return yargs.option('integrated', {
type: 'boolean',
description:
'Migrate to an Nx integrated layout workspace. Only for CRA and Angular projects.',
// TODO(leo): keep it hidden until feature is released
hidden: true,
});
function withInitOptions(yargs: yargs.Argv) {
// TODO(leo): make them visible in docs/help once the feature is released in Nx 16
return yargs
.options('nxCloud', {
type: 'boolean',
description: 'Set up distributed caching with Nx Cloud.',
hidden: true,
})
.option('interactive', {
describe: 'When false disables interactive input prompts for options.',
type: 'boolean',
default: true,
hidden: true,
})
.option('integrated', {
type: 'boolean',
description:
'Migrate to an Nx integrated layout workspace. Only for Angular CLI workspaces and CRA projects.',
default: false,
hidden: true,
})
.option('addE2e', {
describe:
'Set up Cypress E2E tests in integrated workspaces. Only for CRA projects.',
type: 'boolean',
default: false,
hidden: true,
})
.option('force', {
describe:
'Force the migration to continue and ignore custom webpack setup or uncommitted changes. Only for CRA projects.',
type: 'boolean',
default: false,
hidden: true,
})
.options('vite', {
type: 'boolean',
description: 'Use Vite as the bundler. Only for CRA projects.',
default: true,
hidden: true,
})
.options('cacheable', {
type: 'string',
description:
'Comma-separated list of cacheable operations. Only used for internal testing.',
coerce: parseCSV,
hidden: true,
});
}
function runMigration() {

View File

@ -2,7 +2,7 @@ import { prompt } from 'enquirer';
import { readdirSync, readFileSync, statSync } from 'fs';
import ignore from 'ignore';
import { join, relative } from 'path';
import * as yargsParser from 'yargs-parser';
import { InitArgs } from '../command-line/init';
import { readJsonFile } from '../utils/fileutils';
import { output } from '../utils/output';
import { getPackageManagerCommand } from '../utils/package-manager';
@ -15,15 +15,9 @@ import {
runInstall,
} from './utils';
const parsedArgs = yargsParser(process.argv, {
boolean: ['yes'],
string: ['cacheable'], // only used for testing
alias: {
yes: ['y'],
},
});
type Options = Pick<InitArgs, 'nxCloud' | 'interactive' | 'cacheable'>;
export async function addNxToMonorepo() {
export async function addNxToMonorepo(options: Options) {
const repoRoot = process.cwd();
output.log({ title: '🐳 Nx initialization' });
@ -36,7 +30,7 @@ export async function addNxToMonorepo() {
let scriptOutputs = {} as { [script: string]: string };
let useNxCloud: boolean;
if (parsedArgs.yes !== true && scripts.length > 0) {
if (options.interactive && scripts.length > 0) {
output.log({
title:
'🧑‍🔧 Please answer the following questions about the scripts found in your workspace in order to generate task runner configuration',
@ -78,13 +72,13 @@ export async function addNxToMonorepo() {
)[scriptName];
}
useNxCloud = await askAboutNxCloud();
useNxCloud = options.nxCloud ?? (await askAboutNxCloud());
} else {
targetDefaults = [];
cacheableOperations = parsedArgs.cacheable
? parsedArgs.cacheable.split(',')
: [];
useNxCloud = false;
cacheableOperations = options.cacheable ?? [];
useNxCloud =
options.nxCloud ??
(options.interactive ? await askAboutNxCloud() : false);
}
createNxJsonFile(

View File

@ -1,10 +1,13 @@
import { unlinkSync, writeFileSync } from 'fs-extra';
import * as yargsParser from 'yargs-parser';
import * as enquirer from 'enquirer';
import { unlinkSync, writeFileSync } from 'fs-extra';
import { join } from 'path';
import { InitArgs } from '../command-line/init';
import { NrwlJsPluginConfig, NxJsonConfiguration } from '../config/nx-json';
import { ProjectConfiguration } from '../config/workspace-json-project-json';
import { fileExists, readJsonFile, writeJsonFile } from '../utils/fileutils';
import { output } from '../utils/output';
import { PackageJson } from '../utils/package-json';
import { fileExists, readJsonFile, writeJsonFile } from '../utils/fileutils';
import { getPackageManagerCommand } from '../utils/package-manager';
import {
addDepsToPackageJson,
askAboutNxCloud,
@ -14,20 +17,11 @@ import {
printFinalMessage,
runInstall,
} from './utils';
import { getPackageManagerCommand } from '../utils/package-manager';
import { ProjectConfiguration } from '../config/workspace-json-project-json';
import { NrwlJsPluginConfig, NxJsonConfiguration } from '../config/nx-json';
type Options = Pick<InitArgs, 'nxCloud' | 'interactive' | 'cacheable'>;
type NestCLIConfiguration = any;
const parsedArgs = yargsParser(process.argv, {
boolean: ['yes'],
string: ['cacheable'], // only used for testing
alias: {
yes: ['y'],
},
});
export async function addNxToNest(packageJson: PackageJson) {
export async function addNxToNest(options: Options, packageJson: PackageJson) {
const repoRoot = process.cwd();
output.log({ title: '🐳 Nx initialization' });
@ -69,7 +63,7 @@ export async function addNxToNest(packageJson: PackageJson) {
let scriptOutputs = {};
let useNxCloud: boolean;
if (parsedArgs.yes !== true) {
if (options.interactive) {
output.log({
title:
'🧑‍🔧 Please answer the following questions about the scripts found in your package.json in order to generate task runner configuration',
@ -98,12 +92,10 @@ export async function addNxToNest(packageJson: PackageJson) {
)[scriptName];
}
useNxCloud = await askAboutNxCloud();
useNxCloud = options.nxCloud ?? (await askAboutNxCloud());
} else {
cacheableOperations = parsedArgs.cacheable
? parsedArgs.cacheable.split(',')
: [];
useNxCloud = false;
cacheableOperations = options.cacheable ?? [];
useNxCloud = options.nxCloud ?? false;
}
createNxJsonFile(

View File

@ -1,5 +1,5 @@
import * as enquirer from 'enquirer';
import * as yargsParser from 'yargs-parser';
import { InitArgs } from '../command-line/init';
import { readJsonFile } from '../utils/fileutils';
import { output } from '../utils/output';
import { getPackageManagerCommand } from '../utils/package-manager';
@ -13,15 +13,9 @@ import {
runInstall,
} from './utils';
const parsedArgs = yargsParser(process.argv, {
boolean: ['yes'],
string: ['cacheable'], // only used for testing
alias: {
yes: ['y'],
},
});
type Options = Pick<InitArgs, 'nxCloud' | 'interactive' | 'cacheable'>;
export async function addNxToNpmRepo() {
export async function addNxToNpmRepo(options: Options) {
const repoRoot = process.cwd();
output.log({ title: '🐳 Nx initialization' });
@ -35,7 +29,7 @@ export async function addNxToNpmRepo() {
(s) => !s.startsWith('pre') && !s.startsWith('post')
);
if (parsedArgs.yes !== true) {
if (options.interactive) {
output.log({
title:
'🧑‍🔧 Please answer the following questions about the scripts found in your package.json in order to generate task runner configuration',
@ -66,12 +60,10 @@ export async function addNxToNpmRepo() {
)[scriptName];
}
useNxCloud = await askAboutNxCloud();
useNxCloud = options.nxCloud ?? (await askAboutNxCloud());
} else {
cacheableOperations = parsedArgs.cacheable
? parsedArgs.cacheable.split(',')
: [];
useNxCloud = false;
cacheableOperations = options.cacheable ?? [];
useNxCloud = options.nxCloud ?? false;
}
createNxJsonFile(repoRoot, [], cacheableOperations, {});

View File

@ -14,8 +14,7 @@ import {
import { setupIntegratedWorkspace } from './integrated-workspace';
import { getLegacyMigrationFunctionIfApplicable } from './legacy-angular-versions';
import { setupStandaloneWorkspace } from './standalone-workspace';
import type { AngularJsonConfig } from './types';
import yargsParser = require('yargs-parser');
import type { AngularJsonConfig, Options } from './types';
const defaultCacheableOperations: string[] = [
'build',
@ -23,25 +22,17 @@ const defaultCacheableOperations: string[] = [
'test',
'lint',
];
const parsedArgs = yargsParser(process.argv, {
boolean: ['yes'],
string: ['cacheable'], // only used for testing
alias: {
yes: ['y'],
},
});
let repoRoot: string;
let workspaceTargets: string[];
export async function addNxToAngularCliRepo(integrated: boolean) {
export async function addNxToAngularCliRepo(options: Options) {
repoRoot = process.cwd();
output.log({ title: '🧐 Checking versions compatibility' });
const legacyMigrationFn = await getLegacyMigrationFunctionIfApplicable(
repoRoot,
integrated,
parsedArgs.yes !== true
options
);
if (legacyMigrationFn) {
output.log({ title: '💽 Running migration for a legacy Angular version' });
@ -55,16 +46,17 @@ export async function addNxToAngularCliRepo(integrated: boolean) {
});
output.log({ title: '🐳 Nx initialization' });
const cacheableOperations = !integrated
? await collectCacheableOperations()
const cacheableOperations = !options.integrated
? await collectCacheableOperations(options)
: [];
const useNxCloud = parsedArgs.yes !== true ? await askAboutNxCloud() : false;
const useNxCloud =
options.nxCloud ?? (options.interactive ? await askAboutNxCloud() : false);
output.log({ title: '📦 Installing dependencies' });
installDependencies(useNxCloud);
output.log({ title: '📝 Setting up workspace' });
await setupWorkspace(cacheableOperations, integrated);
await setupWorkspace(cacheableOperations, options.integrated);
if (useNxCloud) {
output.log({ title: '🛠️ Setting up Nx Cloud' });
@ -79,7 +71,7 @@ export async function addNxToAngularCliRepo(integrated: boolean) {
});
}
async function collectCacheableOperations(): Promise<string[]> {
async function collectCacheableOperations(options: Options): Promise<string[]> {
let cacheableOperations: string[];
workspaceTargets = getWorkspaceTargets();
@ -87,7 +79,7 @@ async function collectCacheableOperations(): Promise<string[]> {
(t) => workspaceTargets.includes(t)
);
if (parsedArgs.yes !== true) {
if (options.interactive) {
output.log({
title:
'🧑‍🔧 Please answer the following questions about the targets found in your angular.json in order to generate task runner configuration',
@ -107,9 +99,8 @@ async function collectCacheableOperations(): Promise<string[]> {
])) as any
).cacheableOperations;
} else {
cacheableOperations = parsedArgs.cacheable
? parsedArgs.cacheable.split(',')
: defaultCacheableTargetsInWorkspace;
cacheableOperations =
options.cacheable ?? defaultCacheableTargetsInWorkspace;
}
return cacheableOperations;

View File

@ -12,6 +12,7 @@ import {
resolvePackageVersionUsingRegistry,
} from '../../utils/package-manager';
import { askAboutNxCloud, initCloud, printFinalMessage } from '../utils';
import type { Options } from './types';
// map of Angular major versions to Nx versions to use for legacy `nx init` migrations,
// key is major Angular version and value is Nx version to use
@ -23,8 +24,7 @@ const versionWithConsolidatedPackages = '13.9.0';
export async function getLegacyMigrationFunctionIfApplicable(
repoRoot: string,
isIntegratedMigration: boolean,
interactive: boolean
options: Options
): Promise<() => Promise<void> | null> {
const angularVersion =
readModulePackageJson('@angular/core').packageJson.version;
@ -44,7 +44,7 @@ export async function getLegacyMigrationFunctionIfApplicable(
pkgName,
`^${majorAngularVersion}.0.0`
);
const preserveAngularCliLayoutFlag = !isIntegratedMigration
const preserveAngularCliLayoutFlag = !options.integrated
? '--preserveAngularCLILayout'
: '--preserveAngularCLILayout=false';
legacyMigrationCommand = `ng g ${pkgName}:ng-add ${preserveAngularCliLayoutFlag}`;
@ -52,7 +52,7 @@ export async function getLegacyMigrationFunctionIfApplicable(
// for v13, the migration was in @nrwl/angular:ng-add
pkgName = '@nrwl/angular';
pkgVersion = await resolvePackageVersion(pkgName, '~14.1.0');
const preserveAngularCliLayoutFlag = !isIntegratedMigration
const preserveAngularCliLayoutFlag = !options.integrated
? '--preserve-angular-cli-layout'
: '--preserve-angular-cli-layout=false';
legacyMigrationCommand = `ng g ${pkgName}:ng-add ${preserveAngularCliLayoutFlag}`;
@ -70,7 +70,9 @@ export async function getLegacyMigrationFunctionIfApplicable(
return async () => {
output.log({ title: '🐳 Nx initialization' });
const useNxCloud = interactive ? await askAboutNxCloud() : false;
const useNxCloud =
options.nxCloud ??
(options.interactive ? await askAboutNxCloud() : false);
output.log({ title: '📦 Installing dependencies' });
const pmc = getPackageManagerCommand();

View File

@ -1,5 +1,11 @@
import { InitArgs } from '../../command-line/init';
import type { TargetConfiguration } from '../../config/workspace-json-project-json';
export type Options = Pick<
InitArgs,
'nxCloud' | 'integrated' | 'interactive' | 'cacheable'
>;
export type AngularJsonConfigTargetConfiguration = Exclude<
TargetConfiguration,
'command' | 'executor' | 'outputs' | 'dependsOn' | 'inputs'

View File

@ -1,13 +1,13 @@
import { execSync } from 'child_process';
import { copySync, moveSync, readdirSync, removeSync } from 'fs-extra';
import { join } from 'path';
import * as yargsParser from 'yargs-parser';
import { InitArgs } from '../../command-line/init';
import { fileExists, readJsonFile } from '../../utils/fileutils';
import { output } from '../../utils/output';
import {
PackageManagerCommands,
detectPackageManager,
getPackageManagerCommand,
PackageManagerCommands,
} from '../../utils/package-manager';
import { askAboutNxCloud, printFinalMessage } from '../utils';
import { checkForCustomWebpackSetup } from './check-for-custom-webpack-setup';
@ -21,16 +21,9 @@ import { writeCracoConfig } from './write-craco-config';
import { writeViteConfig } from './write-vite-config';
import { writeViteIndexHtml } from './write-vite-index-html';
export interface Options {
force: boolean;
e2e: boolean;
nxCloud: boolean;
vite: boolean;
integrated: boolean;
interactive: boolean;
}
type Options = InitArgs;
interface NormalizedOptions extends Options {
type NormalizedOptions = Options & {
packageManager: string;
pmc: PackageManagerCommands;
appIsJs: boolean;
@ -39,31 +32,17 @@ interface NormalizedOptions extends Options {
npxYesFlagNeeded: boolean;
isVite: boolean;
isStandalone: boolean;
}
};
const parsedArgs = yargsParser(process.argv, {
boolean: ['force', 'e2e', 'nxCloud', 'vite', 'interactive'],
default: {
interactive: true,
vite: true,
},
configuration: {
'strip-dashed': true,
},
});
export async function addNxToCraRepo(integrated: boolean) {
if (!parsedArgs.force) {
export async function addNxToCraRepo(options: Options) {
if (!options.force) {
checkForUncommittedChanges();
checkForCustomWebpackSetup();
}
output.log({ title: '🐳 Nx initialization' });
const normalizedOptions = await normalizeOptions(
parsedArgs as unknown as Options,
integrated
);
const normalizedOptions = await normalizeOptions(options);
await reorgnizeWorkspaceStructure(normalizedOptions);
}
@ -90,10 +69,7 @@ function installDependencies(options: NormalizedOptions) {
});
}
async function normalizeOptions(
options: Options,
integrated: boolean
): Promise<NormalizedOptions> {
async function normalizeOptions(options: Options): Promise<NormalizedOptions> {
const packageManager = detectPackageManager();
const pmc = getPackageManagerCommand(packageManager);
@ -110,14 +86,14 @@ async function normalizeOptions(
// Should remove this check 04/2023 once Node 14 & npm 6 reach EOL
const npxYesFlagNeeded = !npmVersion.startsWith('6'); // npm 7 added -y flag to npx
const isVite = options.vite;
const isStandalone = !integrated;
options.nxCloud =
options.interactive && options.nxCloud === undefined
? await askAboutNxCloud()
: options.nxCloud ?? false;
const isStandalone = !options.integrated;
const nxCloud =
options.nxCloud ?? (options.interactive ? await askAboutNxCloud() : false);
return {
...options,
nxCloud,
packageManager,
pmc,
appIsJs,
@ -126,7 +102,6 @@ async function normalizeOptions(
npxYesFlagNeeded,
isVite,
isStandalone,
integrated,
};
}
@ -312,7 +287,7 @@ function cleanUpUnusedFilesAndAddConfigFiles(options: NormalizedOptions) {
setupTsConfig(options.reactAppName, options.isStandalone);
if (options.e2e && !options.isStandalone) {
if (options.addE2e && !options.isStandalone) {
output.log({ title: '📃 Setup e2e tests' });
setupE2eProject(options.reactAppName);
} else {