diff --git a/e2e/utils/index.ts b/e2e/utils/index.ts index 0d6b223980..dab60af7bb 100644 --- a/e2e/utils/index.ts +++ b/e2e/utils/index.ts @@ -43,6 +43,7 @@ export function runCreateWorkspace( base, packageManager, cli, + extraArgs, }: { preset: string; appName?: string; @@ -50,6 +51,7 @@ export function runCreateWorkspace( base?: string; packageManager?: string; cli?: string; + extraArgs?: string; } ) { setCurrentProjName(name); @@ -76,6 +78,10 @@ export function runCreateWorkspace( command += ` --package-manager=${packageManager}`; } + if (extraArgs) { + command += ` ${extraArgs}`; + } + const create = execSync(command, { cwd: `./tmp/${currentCli()}`, stdio: [0, 1, 2], diff --git a/e2e/workspace/src/create-nx-workspace.test.ts b/e2e/workspace/src/create-nx-workspace.test.ts index ff5556c072..7af19fec3b 100644 --- a/e2e/workspace/src/create-nx-workspace.test.ts +++ b/e2e/workspace/src/create-nx-workspace.test.ts @@ -100,6 +100,15 @@ describe('create-nx-workspace', () => { }); }); + it('should be able to create a workspace with custom commit information', () => { + const wsName = uniq('branch'); + runCreateWorkspace(wsName, { + preset: 'empty', + extraArgs: + '--commit.name="John Doe" --commit.email="myemail@test.com" --commit.message="Custom commit message!"', + }); + }); + it('should be able to create a nest workspace', () => { const wsName = uniq('nest'); const appName = uniq('app'); diff --git a/packages/create-nx-workspace/bin/create-nx-workspace.ts b/packages/create-nx-workspace/bin/create-nx-workspace.ts index d1d9f9f56e..4bfc42c079 100644 --- a/packages/create-nx-workspace/bin/create-nx-workspace.ts +++ b/packages/create-nx-workspace/bin/create-nx-workspace.ts @@ -1,7 +1,7 @@ #!/usr/bin/env node -// we can import from '@nrwl/workspace' because it will require typescript -import { output } from '@nrwl/workspace/src/utils/output'; +import { output, unparse } from '@nrwl/workspace'; +import { Schema, Preset } from '@nrwl/workspace/src/schematics/new/new'; import { getPackageManagerCommand } from '@nrwl/tao/src/shared/package-manager'; import { execSync } from 'child_process'; import { writeFileSync } from 'fs'; @@ -11,19 +11,7 @@ import { dirSync } from 'tmp'; import * as yargsParser from 'yargs-parser'; import { showNxWarning } from './shared'; -enum Preset { - Empty = 'empty', - OSS = 'oss', - WebComponents = 'web-components', - Angular = 'angular', - AngularWithNest = 'angular-nest', - React = 'react', - ReactWithExpress = 'react-express', - NextJs = 'next', - Nest = 'nest', -} - -const presetOptions = [ +const presetOptions: { value: Preset; name: string }[] = [ { value: Preset.Empty, name: @@ -46,7 +34,7 @@ const presetOptions = [ name: 'nest [a workspace with a single Nest application]', }, { - value: 'web-components', + value: Preset.WebComponents, name: 'web components [a workspace with a single app built using web components]', }, @@ -61,7 +49,7 @@ const presetOptions = [ 'angular-nest [a workspace with a full stack application (Angular + Nest)]', }, { - value: 'oss', + value: Preset.OSS, name: 'oss [an empty workspace with a layout that works best for open-source projects]', }, @@ -70,10 +58,14 @@ const presetOptions = [ const tsVersion = 'TYPESCRIPT_VERSION'; const cliVersion = 'NX_VERSION'; const nxVersion = 'NX_VERSION'; -const angularCliVersion = 'ANGULAR_CLI_VERSION'; const prettierVersion = 'PRETTIER_VERSION'; -const parsedArgs = yargsParser(process.argv, { +interface WorkspaceArgs extends Schema { + _?: string[]; + help?: boolean; +} + +const parsedArgs: WorkspaceArgs = yargsParser(process.argv.slice(2), { string: [ 'cli', 'preset', @@ -84,12 +76,17 @@ const parsedArgs = yargsParser(process.argv, { 'packageManager', ], alias: { - appName: 'app-name', - nxCloud: 'nx-cloud', - defaultBase: 'default-base', + packageManager: 'pm', }, boolean: ['help', 'interactive', 'nxCloud'], -}); + default: { + interactive: false, + }, + configuration: { + 'strip-dashed': true, + 'strip-aliased': true, + }, +}) as any; if (parsedArgs.help) { showHelp(); @@ -102,21 +99,17 @@ determineWorkspaceName(parsedArgs).then((name) => { return determineStyle(preset, parsedArgs).then((style) => { return determineCli(preset, parsedArgs).then((cli) => { return determineLinter(preset, parsedArgs).then((linter) => { - return askAboutNxCloud(parsedArgs).then((cloud) => { + return askAboutNxCloud(parsedArgs).then((nxCloud) => { const tmpDir = createSandbox(packageManager); - createApp( - tmpDir, + createApp(tmpDir, name, { + ...parsedArgs, cli, - parsedArgs, - name, preset, appName, style, linter, - cloud, - parsedArgs.interactive, - parsedArgs.defaultBase - ); + nxCloud, + }); showNxWarning(name); pointToTutorialAndCourse(preset); }); @@ -160,8 +153,8 @@ function showHelp() { `); } -function determineWorkspaceName(parsedArgs: any): Promise { - const workspaceName: string = parsedArgs._[2]; +function determineWorkspaceName(parsedArgs: WorkspaceArgs): Promise { + const workspaceName: string = parsedArgs._[0]; if (workspaceName) { return Promise.resolve(workspaceName); @@ -187,7 +180,7 @@ function determineWorkspaceName(parsedArgs: any): Promise { }); } -function determinePreset(parsedArgs: any): Promise { +function determinePreset(parsedArgs: WorkspaceArgs): Promise { if (parsedArgs.preset) { if (Object.values(Preset).indexOf(parsedArgs.preset) === -1) { output.error({ @@ -217,7 +210,10 @@ function determinePreset(parsedArgs: any): Promise { .then((a: { Preset: Preset }) => a.Preset); } -function determineAppName(preset: Preset, parsedArgs: any): Promise { +function determineAppName( + preset: Preset, + parsedArgs: WorkspaceArgs +): Promise { if (preset === Preset.Empty || preset === Preset.OSS) { return Promise.resolve(''); } @@ -248,7 +244,7 @@ function determineAppName(preset: Preset, parsedArgs: any): Promise { function determineCli( preset: Preset, - parsedArgs: any + parsedArgs: WorkspaceArgs ): Promise<'nx' | 'angular'> { if (parsedArgs.cli) { if (['nx', 'angular'].indexOf(parsedArgs.cli) === -1) { @@ -272,7 +268,7 @@ function determineCli( } } -function determineStyle(preset: Preset, parsedArgs: any) { +function determineStyle(preset: Preset, parsedArgs: WorkspaceArgs) { if ( preset === Preset.Empty || preset === Preset.OSS || @@ -351,7 +347,7 @@ function determineStyle(preset: Preset, parsedArgs: any) { return Promise.resolve(parsedArgs.style); } -function determineLinter(preset: Preset, parsedArgs: any) { +function determineLinter(preset: Preset, parsedArgs: WorkspaceArgs) { if (!parsedArgs.linter) { if (preset === Preset.Angular || preset === Preset.AngularWithNest) { return inquirer @@ -414,51 +410,12 @@ function createSandbox(packageManager: string) { return tmpDir; } -function createApp( - tmpDir: string, - cli: 'nx' | 'angular', - parsedArgs: any, - name: string, - preset: Preset, - appName: string, - style: string | null, - linter: string, - nxCloud: boolean, - interactive: boolean, - defaultBase: string -) { - const filterArgs = [ - '_', - 'app-name', - 'appName', - 'cli', - 'default-base', - 'defaultBase', - 'interactive', - 'nx-cloud', - 'nxCloud', - 'preset', - 'style', - 'linter', - ]; - - // These are the arguments that are passed to the schematic - const args = Object.keys(parsedArgs) - .filter((key) => !filterArgs.includes(key)) - .map((key) => `--${key}=${parsedArgs[key]}`) - .join(' '); - - const appNameArg = appName ? ` --appName="${appName}"` : ``; - const styleArg = style ? ` --style="${style}"` : ``; - const linterArg = ` --linter="${linter}"`; - const nxCloudArg = nxCloud ? ` --nxCloud` : ``; - const interactiveArg = interactive - ? ` --interactive=true` - : ` --interactive=false`; - const defaultBaseArg = defaultBase ? ` --defaultBase="${defaultBase}"` : ``; +function createApp(tmpDir: string, name: string, parsedArgs: WorkspaceArgs) { + const { _, cli, ...restArgs } = parsedArgs; + const args = unparse(restArgs).join(' '); const pmc = getPackageManagerCommand(packageManager); - const command = `new ${name} ${args} --preset="${preset}"${appNameArg}${styleArg}${linterArg}${nxCloudArg}${interactiveArg}${defaultBaseArg} --collection=@nrwl/workspace`; + const command = `new ${name} ${args} --collection=@nrwl/workspace`; console.log(command); execSync( @@ -471,7 +428,7 @@ function createApp( } ); - if (nxCloud) { + if (parsedArgs.nxCloud) { output.addVerticalSeparator(); execSync(`${pmc.exec} nx g @nrwl/nx-cloud:init --no-analytics`, { stdio: [0, 1, 2], @@ -480,7 +437,7 @@ function createApp( } } -async function askAboutNxCloud(parsedArgs: any) { +async function askAboutNxCloud(parsedArgs: WorkspaceArgs) { if (parsedArgs.nxCloud === undefined) { return inquirer .prompt([ diff --git a/packages/create-nx-workspace/package.json b/packages/create-nx-workspace/package.json index b97c5b8f2e..2a0bc0c38a 100644 --- a/packages/create-nx-workspace/package.json +++ b/packages/create-nx-workspace/package.json @@ -29,9 +29,9 @@ "dependencies": { "@nrwl/workspace": "*", "tmp": "0.0.33", - "yargs": "15.4.1", "yargs-parser": "20.0.0", "tslib": "^2.0.0", - "inquirer": "^6.3.1" + "inquirer": "^6.3.1", + "typescript": "~4.0.3" } } diff --git a/packages/workspace/index.ts b/packages/workspace/index.ts index 5ded60b6c1..81c5add0ec 100644 --- a/packages/workspace/index.ts +++ b/packages/workspace/index.ts @@ -59,6 +59,8 @@ export { serializeTarget, } from './src/utils/cli-config-utils'; +export { unparse } from './src/tasks-runner/utils'; + export { getWorkspace, updateWorkspace, diff --git a/packages/workspace/src/schematics/new/new.ts b/packages/workspace/src/schematics/new/new.ts index bc92ac9deb..775e3032fd 100644 --- a/packages/workspace/src/schematics/new/new.ts +++ b/packages/workspace/src/schematics/new/new.ts @@ -24,10 +24,21 @@ import * as path from 'path'; import { Observable } from 'rxjs'; import { spawn } from 'child_process'; import { getPackageManagerCommand } from '@nrwl/tao/src/shared/package-manager'; -// @ts-ignore -import yargsParser = require('yargs-parser'); +import * as yargsParser from 'yargs-parser'; import { names } from '@nrwl/devkit'; +export enum Preset { + Empty = 'empty', + OSS = 'oss', + WebComponents = 'web-components', + Angular = 'angular', + AngularWithNest = 'angular-nest', + React = 'react', + ReactWithExpress = 'react-express', + NextJs = 'next', + Nest = 'nest', +} + export interface Schema { cli: 'nx' | 'angular'; directory: string; @@ -38,16 +49,7 @@ export interface Schema { skipGit?: boolean; style?: string; nxCloud?: boolean; - preset: - | 'empty' - | 'oss' - | 'angular' - | 'react' - | 'web-components' - | 'angular-nest' - | 'react-express' - | 'next' - | 'nest'; + preset: Preset; commit?: { name: string; email: string; message?: string }; defaultBase?: string; nxWorkspaceRoot?: string; diff --git a/packages/workspace/src/tasks-runner/utils.spec.ts b/packages/workspace/src/tasks-runner/utils.spec.ts index 5b0b3fb5da..b73227d7e6 100644 --- a/packages/workspace/src/tasks-runner/utils.spec.ts +++ b/packages/workspace/src/tasks-runner/utils.spec.ts @@ -236,5 +236,19 @@ describe('utils', () => { '--foo.z=4', ]); }); + + it('should quote string values with space(s)', () => { + const options = { + string1: 'one', + string2: 'one two', + string3: 'one two three', + }; + + expect(unparse(options)).toEqual([ + '--string1=one', + '--string2="one two"', + '--string3="one two three"', + ]); + }); }); }); diff --git a/packages/workspace/src/tasks-runner/utils.ts b/packages/workspace/src/tasks-runner/utils.ts index 5ff7e27890..4195a63ad3 100644 --- a/packages/workspace/src/tasks-runner/utils.ts +++ b/packages/workspace/src/tasks-runner/utils.ts @@ -108,7 +108,9 @@ function unparseOption(key: string, value: any, unparsed: string[]) { unparsed ); } - } else if (typeof value === 'string' || value != null) { + } else if (typeof value === 'string' && value.includes(' ')) { + unparsed.push(`--${key}="${value}"`); + } else if (value != null) { unparsed.push(`--${key}=${value}`); } }