diff --git a/e2e/utils/index.ts b/e2e/utils/index.ts index c235047f202e0..8e193143b31a2 100644 --- a/e2e/utils/index.ts +++ b/e2e/utils/index.ts @@ -84,11 +84,13 @@ export function runCreateWorkspace( appName, style, base, + packageManager, }: { preset: string; appName?: string; style?: string; base?: string; + packageManager?: string; } ) { const linterArg = @@ -105,6 +107,10 @@ export function runCreateWorkspace( command += ` --defaultBase="${base}"`; } + if (packageManager) { + command += ` --package-manager=${packageManager}`; + } + const create = execSync(command, { cwd: `./tmp/${cli}`, 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 59ac44fd7078f..11d04d33889e2 100644 --- a/e2e/workspace/src/create-nx-workspace.test.ts +++ b/e2e/workspace/src/create-nx-workspace.test.ts @@ -1,4 +1,12 @@ -import { cli, forEachCli, runCreateWorkspace, uniq } from '@nrwl/e2e/utils'; +import { + cli, + forEachCli, + readJson, + runCreateWorkspace, + setCurrentProjName, + uniq, + workspaceConfigName, +} from '@nrwl/e2e/utils'; import { existsSync, mkdirSync } from 'fs-extra'; import { execSync } from 'child_process'; @@ -111,5 +119,18 @@ forEachCli(() => { expect(existsSync(`${tmpDir}/${wsName}/package.json`)).toBeTruthy(); }); + + it('should store `packageManager` preference', () => { + const wsName = uniq('empty'); + setCurrentProjName(wsName); + + runCreateWorkspace(wsName, { + preset: 'empty', + packageManager: 'yarn', + }); + + const workspaceJson = readJson(`${workspaceConfigName()}`); + expect(workspaceJson.cli.packageManager).toEqual('yarn'); + }); }); }); diff --git a/packages/create-nx-workspace/bin/create-nx-workspace.ts b/packages/create-nx-workspace/bin/create-nx-workspace.ts index f3b27abbdb75c..c0a9ea35082f4 100644 --- a/packages/create-nx-workspace/bin/create-nx-workspace.ts +++ b/packages/create-nx-workspace/bin/create-nx-workspace.ts @@ -74,7 +74,15 @@ const angularCliVersion = 'ANGULAR_CLI_VERSION'; const prettierVersion = 'PRETTIER_VERSION'; const parsedArgs = yargsParser(process.argv, { - string: ['cli', 'preset', 'appName', 'style', 'linter', 'defaultBase'], + string: [ + 'cli', + 'preset', + 'appName', + 'style', + 'linter', + 'defaultBase', + 'packageManager', + ], alias: { appName: 'app-name', nxCloud: 'nx-cloud', @@ -87,7 +95,7 @@ if (parsedArgs.help) { showHelp(); process.exit(0); } -const packageManager = determinePackageManager(); +const packageManager = determinePackageManager(parsedArgs.packageManager); determineWorkspaceName(parsedArgs).then((name) => { determinePreset(parsedArgs).then((preset) => { return determineAppName(preset, parsedArgs).then((appName) => { diff --git a/packages/create-nx-workspace/bin/shared.spec.ts b/packages/create-nx-workspace/bin/shared.spec.ts new file mode 100644 index 0000000000000..dd19e736afc74 --- /dev/null +++ b/packages/create-nx-workspace/bin/shared.spec.ts @@ -0,0 +1,42 @@ +import { determinePackageManager } from './shared'; + +let INSTALLED_PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm']; + +jest.mock('child_process', () => { + return { + execSync: (command) => { + if (command.endsWith(`--version`)) { + const pm = command.split(' ')[0]; + if (INSTALLED_PACKAGE_MANAGERS.includes(pm)) { + return; + } + throw Error(); + } + }, + }; +}); + +describe('determinePackageManager', () => { + it('will prefer Yarn if installed and there is no preference', () => { + const pm = determinePackageManager(); + expect(pm).toEqual('yarn'); + }); + + it('will use preferred one if installed', () => { + expect(determinePackageManager('pnpm')).toEqual('pnpm'); + expect(determinePackageManager('yarn')).toEqual('yarn'); + expect(determinePackageManager('npm')).toEqual('npm'); + }); + + it('will not use preferred one if not installed', () => { + INSTALLED_PACKAGE_MANAGERS = ['npm', 'pnpm']; + const pm = determinePackageManager('yarn'); + expect(pm).toEqual('pnpm'); + }); + + it('will fallback to NPM', () => { + INSTALLED_PACKAGE_MANAGERS = []; + const pm = determinePackageManager(); + expect(pm).toEqual('npm'); + }); +}); diff --git a/packages/create-nx-workspace/bin/shared.ts b/packages/create-nx-workspace/bin/shared.ts index 5641d327a49e2..a63b68e41be8a 100644 --- a/packages/create-nx-workspace/bin/shared.ts +++ b/packages/create-nx-workspace/bin/shared.ts @@ -22,26 +22,19 @@ export function showNxWarning(workspaceName: string) { } } -export function determinePackageManager() { - let packageManager = getPackageManagerFromAngularCLI(); - if (packageManager === 'npm' || isPackageManagerInstalled(packageManager)) { - return packageManager; - } - - if (isPackageManagerInstalled('yarn')) { - return 'yarn'; - } - - if (isPackageManagerInstalled('pnpm')) { - return 'pnpm'; - } - - return 'npm'; +export function determinePackageManager(preferredPackageManager?: string) { + return ( + [ + preferredPackageManager, + getPackageManagerFromAngularCLI(), + 'yarn', + 'pnpm', + ].find((pm) => pm && isPackageManagerInstalled(pm)) || 'npm' + ); } function getPackageManagerFromAngularCLI(): string { // If you have Angular CLI installed, read Angular CLI config. - // If it isn't installed, default to 'yarn'. try { return execSync('ng config -g cli.packageManager', { stdio: ['ignore', 'pipe', 'ignore'], @@ -50,7 +43,7 @@ function getPackageManagerFromAngularCLI(): string { .toString() .trim(); } catch (e) { - return 'yarn'; + return; } } diff --git a/packages/workspace/src/schematics/ng-new/schema.json b/packages/workspace/src/schematics/ng-new/schema.json index 739db563d4731..56ba55e17c543 100644 --- a/packages/workspace/src/schematics/ng-new/schema.json +++ b/packages/workspace/src/schematics/ng-new/schema.json @@ -92,6 +92,11 @@ "type": "string", "enum": ["tslint", "eslint"], "default": "eslint" + }, + "packageManager": { + "description": "The package manager used to install dependencies.", + "type": "string", + "enum": ["npm", "yarn", "pnpm"] } } } diff --git a/packages/workspace/src/schematics/shared-new/shared-new.ts b/packages/workspace/src/schematics/shared-new/shared-new.ts index 452eb6efb25e0..1be23489b6d29 100644 --- a/packages/workspace/src/schematics/shared-new/shared-new.ts +++ b/packages/workspace/src/schematics/shared-new/shared-new.ts @@ -52,6 +52,7 @@ export interface Schema { defaultBase?: string; nxWorkspaceRoot?: string; linter: 'tslint' | 'eslint'; + packageManager?: string; } class RunPresetTask { @@ -133,6 +134,7 @@ export function sharedNew(cli: string, options: Schema): Rule { return chain([ schematic('workspace', { ...workspaceOpts, cli }), + setDefaultPackageManager(options), setDefaultLinter(options), addPresetDependencies(options), addCloudDependencies(options), @@ -326,3 +328,17 @@ function setTSLintDefault() { return json; }); } + +function setDefaultPackageManager({ packageManager }: Schema) { + if (!packageManager) { + return noop(); + } + + return updateWorkspaceInTree((json) => { + if (!json.cli) { + json.cli = {}; + } + json.cli['packageManager'] = packageManager; + return json; + }); +} diff --git a/packages/workspace/src/schematics/tao-new/schema.json b/packages/workspace/src/schematics/tao-new/schema.json index 5f3b8e188f7b2..e34f371b99032 100644 --- a/packages/workspace/src/schematics/tao-new/schema.json +++ b/packages/workspace/src/schematics/tao-new/schema.json @@ -87,6 +87,11 @@ "type": "string", "enum": ["tslint", "eslint"], "default": "eslint" + }, + "packageManager": { + "description": "The package manager used to install dependencies.", + "type": "string", + "enum": ["npm", "yarn", "pnpm"] } } }