From b3a3f2a72471b0cef50172f3ccd3c96e7e173319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Jona=C5=A1?= Date: Thu, 6 Apr 2023 16:47:15 +0200 Subject: [PATCH] fix(core): ensurePackage fails on pnpm workspaces (#16002) --- e2e/utils/command-utils.ts | 8 +- e2e/utils/create-project-utils.ts | 19 ++-- e2e/workspace-create-npm/jest.config.ts | 4 +- .../src/create-nx-workspace-npm.test.ts | 100 +++++++----------- packages/devkit/src/utils/package-json.ts | 20 +++- 5 files changed, 71 insertions(+), 80 deletions(-) diff --git a/e2e/utils/command-utils.ts b/e2e/utils/command-utils.ts index a90fdc44730b6..4858dc1785a74 100644 --- a/e2e/utils/command-utils.ts +++ b/e2e/utils/command-utils.ts @@ -13,8 +13,9 @@ import { ChildProcess, exec, execSync, ExecSyncOptions } from 'child_process'; import { join } from 'path'; import * as isCI from 'is-ci'; import { Workspaces } from '../../packages/nx/src/config/workspaces'; -import { updateFile } from './file-utils'; +import { exists, updateFile } from './file-utils'; import { logError, stripConsoleColors } from './log-utils'; +import { existsSync } from 'fs-extra'; export interface RunCmdOpts { silenceError?: boolean; @@ -118,6 +119,7 @@ export function getPackageManagerCommand({ } { const npmMajorVersion = getNpmMajorVersion(); const publishedVersion = getPublishedVersion(); + const isPnpmWorkspace = existsSync(join(path, 'pnpm-workspace.yaml')); return { npm: { @@ -158,8 +160,8 @@ export function getPackageManagerCommand({ runUninstalledPackage: 'pnpm dlx', install: 'pnpm i', ciInstall: 'pnpm install --frozen-lockfile', - addProd: `pnpm add`, - addDev: `pnpm add -D`, + addProd: isPnpmWorkspace ? 'pnpm add -w' : 'pnpm add', + addDev: isPnpmWorkspace ? 'pnpm add -Dw' : 'pnpm add -D', list: 'npm ls --depth 10', runLerna: `pnpm exec lerna`, }, diff --git a/e2e/utils/create-project-utils.ts b/e2e/utils/create-project-utils.ts index c4f8e79a6ff00..f89dabaf6aa45 100644 --- a/e2e/utils/create-project-utils.ts +++ b/e2e/utils/create-project-utils.ts @@ -47,7 +47,7 @@ export function newProject({ if (!directoryExists(tmpBackupProjPath())) { runCreateWorkspace(projScope, { - preset: 'apps', + preset: 'empty', packageManager, }); @@ -287,12 +287,17 @@ export function packageInstall( } ${pkgsWithVersions}${isVerbose() ? ' --verbose' : ''}`; try { - const install = execSync(command, { - cwd, - stdio: 'pipe', - env: process.env, - encoding: 'utf-8', - }); + const install = execSync( + `${mode === 'dev' ? pm.addDev : pm.addProd} ${pkgsWithVersions}${ + isVerbose() ? ' --verbose' : '' + }`, + { + cwd, + stdio: 'pipe', + env: process.env, + encoding: 'utf-8', + } + ); if (isVerbose()) { output.log({ diff --git a/e2e/workspace-create-npm/jest.config.ts b/e2e/workspace-create-npm/jest.config.ts index 893e2c7c9ff5f..577af9c54737b 100644 --- a/e2e/workspace-create-npm/jest.config.ts +++ b/e2e/workspace-create-npm/jest.config.ts @@ -1,11 +1,11 @@ /* eslint-disable */ export default { transform: { - '^.+\\.[tj]sx?$': 'ts-jest', + '^.+\\.[tj]sx?$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], maxWorkers: 1, - globals: { 'ts-jest': { tsconfig: '/tsconfig.spec.json' } }, + globals: {}, displayName: 'e2e-workspace-create-npm', preset: '../../jest.preset.js', }; diff --git a/e2e/workspace-create-npm/src/create-nx-workspace-npm.test.ts b/e2e/workspace-create-npm/src/create-nx-workspace-npm.test.ts index bde47d6f419db..850e2d7c6023a 100644 --- a/e2e/workspace-create-npm/src/create-nx-workspace-npm.test.ts +++ b/e2e/workspace-create-npm/src/create-nx-workspace-npm.test.ts @@ -1,33 +1,48 @@ import { checkFilesExist, cleanupProject, + getSelectedPackageManager, packageInstall, readJson, runCLI, + runCommand, runCreateWorkspace, uniq, } from '@nrwl/e2e/utils'; describe('create-nx-workspace --preset=npm', () => { - let wsName; + const wsName = uniq('npm'); + + let orginalGlobCache; + + beforeAll(() => { + orginalGlobCache = process.env.NX_PROJECT_GLOB_CACHE; + // glob cache is causing previous projects to show in Workspace for maxWorkers overrides + // which fails due to files no longer being available + process.env.NX_PROJECT_GLOB_CACHE = 'false'; - beforeEach(() => { - wsName = uniq('npm'); runCreateWorkspace(wsName, { preset: 'npm', + packageManager: getSelectedPackageManager(), }); }); - afterEach(() => cleanupProject()); + afterEach(() => { + // cleanup previous projects + runCommand(`rm -rf packages/** tsconfig.base.json`); + }); + + afterAll(() => { + process.env.NX_PROJECT_GLOB_CACHE = orginalGlobCache; + cleanupProject({ skipReset: true }); + }); it('should add angular application', () => { packageInstall('@nrwl/angular', wsName); const appName = uniq('my-app'); expect(() => { - runCLI( - `generate @nrwl/angular:app ${appName} --skipPackageJson --no-interactive` - ); + runCLI(`generate @nrwl/angular:app ${appName} --no-interactive`); }).not.toThrowError(); checkFilesExist('tsconfig.base.json'); }, 1_000_000); @@ -37,9 +52,7 @@ describe('create-nx-workspace --preset=npm', () => { const libName = uniq('lib'); expect(() => { - runCLI( - `generate @nrwl/angular:lib ${libName} --skipPackageJson --no-interactive` - ); + runCLI(`generate @nrwl/angular:lib ${libName} --no-interactive`); }).not.toThrowError(); checkFilesExist('tsconfig.base.json'); const tsconfig = readJson(`tsconfig.base.json`); @@ -48,32 +61,13 @@ describe('create-nx-workspace --preset=npm', () => { }); }, 1_000_000); - it('should add workspace library', () => { - packageInstall('@nrwl/workspace', wsName); - - const libName = uniq('lib'); - - expect(() => - runCLI( - `generate @nrwl/workspace:library ${libName} --skipPackageJson --no-interactive` - ) - ).not.toThrowError(); - checkFilesExist('tsconfig.base.json'); - const tsconfig = readJson(`tsconfig.base.json`); - expect(tsconfig.compilerOptions.paths).toEqual({ - [libName]: [`packages/${libName}/src/index.ts`], - }); - }); - it('should add js library', () => { packageInstall('@nrwl/js', wsName); const libName = uniq('lib'); expect(() => - runCLI( - `generate @nrwl/js:library ${libName} --skipPackageJson --no-interactive` - ) + runCLI(`generate @nrwl/js:library ${libName} --no-interactive`) ).not.toThrowError(); checkFilesExist('tsconfig.base.json'); const tsconfig = readJson(`tsconfig.base.json`); @@ -88,9 +82,7 @@ describe('create-nx-workspace --preset=npm', () => { const appName = uniq('my-app'); expect(() => - runCLI( - `generate @nrwl/web:app ${appName} --skipPackageJson --no-interactive` - ) + runCLI(`generate @nrwl/web:app ${appName} --no-interactive`) ).not.toThrowError(); checkFilesExist('tsconfig.base.json'); }); @@ -101,9 +93,7 @@ describe('create-nx-workspace --preset=npm', () => { const appName = uniq('my-app'); expect(() => { - runCLI( - `generate @nrwl/react:app ${appName} --skipPackageJson --no-interactive` - ); + runCLI(`generate @nrwl/react:app ${appName} --no-interactive`); }).not.toThrowError(); checkFilesExist('tsconfig.base.json'); }); @@ -114,9 +104,7 @@ describe('create-nx-workspace --preset=npm', () => { const libName = uniq('lib'); expect(() => { - runCLI( - `generate @nrwl/react:lib ${libName} --skipPackageJson --no-interactive` - ); + runCLI(`generate @nrwl/react:lib ${libName} --no-interactive`); }).not.toThrowError(); checkFilesExist('tsconfig.base.json'); const tsconfig = readJson(`tsconfig.base.json`); @@ -131,9 +119,7 @@ describe('create-nx-workspace --preset=npm', () => { const appName = uniq('my-app'); expect(() => { - runCLI( - `generate @nrwl/next:app ${appName} --skipPackageJson --no-interactive` - ); + runCLI(`generate @nrwl/next:app ${appName} --no-interactive`); }).not.toThrowError(); checkFilesExist('tsconfig.base.json'); }); @@ -144,9 +130,7 @@ describe('create-nx-workspace --preset=npm', () => { const libName = uniq('lib'); expect(() => { - runCLI( - `generate @nrwl/next:lib ${libName} --skipPackageJson --no-interactive` - ); + runCLI(`generate @nrwl/next:lib ${libName} --no-interactive`); }).not.toThrowError(); checkFilesExist('tsconfig.base.json'); const tsconfig = readJson(`tsconfig.base.json`); @@ -162,7 +146,7 @@ describe('create-nx-workspace --preset=npm', () => { expect(() => { runCLI( - `generate @nrwl/react-native:app ${appName} --install=false --skipPackageJson --no-interactive` + `generate @nrwl/react-native:app ${appName} --install=false --no-interactive` ); }).not.toThrowError(); checkFilesExist('tsconfig.base.json'); @@ -174,9 +158,7 @@ describe('create-nx-workspace --preset=npm', () => { const libName = uniq('lib'); expect(() => { - runCLI( - `generate @nrwl/react-native:lib ${libName} --skipPackageJson --no-interactive` - ); + runCLI(`generate @nrwl/react-native:lib ${libName} --no-interactive`); }).not.toThrowError(); checkFilesExist('tsconfig.base.json'); const tsconfig = readJson(`tsconfig.base.json`); @@ -191,9 +173,7 @@ describe('create-nx-workspace --preset=npm', () => { const appName = uniq('my-app'); expect(() => { - runCLI( - `generate @nrwl/node:app ${appName} --skipPackageJson --no-interactive` - ); + runCLI(`generate @nrwl/node:app ${appName} --no-interactive`); }).not.toThrowError(); checkFilesExist('tsconfig.base.json'); }); @@ -204,9 +184,7 @@ describe('create-nx-workspace --preset=npm', () => { const libName = uniq('lib'); expect(() => { - runCLI( - `generate @nrwl/node:lib ${libName} --skipPackageJson --no-interactive` - ); + runCLI(`generate @nrwl/node:lib ${libName} --no-interactive`); }).not.toThrowError(); checkFilesExist('tsconfig.base.json'); const tsconfig = readJson(`tsconfig.base.json`); @@ -221,9 +199,7 @@ describe('create-nx-workspace --preset=npm', () => { const appName = uniq('my-app'); expect(() => { - runCLI( - `generate @nrwl/nest:app ${appName} --skipPackageJson --no-interactive` - ); + runCLI(`generate @nrwl/nest:app ${appName} --no-interactive`); }).not.toThrowError(); checkFilesExist('tsconfig.base.json'); }); @@ -234,9 +210,7 @@ describe('create-nx-workspace --preset=npm', () => { const libName = uniq('lib'); expect(() => { - runCLI( - `generate @nrwl/nest:lib ${libName} --skipPackageJson --no-interactive` - ); + runCLI(`generate @nrwl/nest:lib ${libName} --no-interactive`); }).not.toThrowError(); checkFilesExist('tsconfig.base.json'); const tsconfig = readJson(`tsconfig.base.json`); @@ -251,9 +225,7 @@ describe('create-nx-workspace --preset=npm', () => { const appName = uniq('my-app'); expect(() => { - runCLI( - `generate @nrwl/express:app ${appName} --skipPackageJson --no-interactive` - ); + runCLI(`generate @nrwl/express:app ${appName} --no-interactive`); }).not.toThrowError(); checkFilesExist('tsconfig.base.json'); }); diff --git a/packages/devkit/src/utils/package-json.ts b/packages/devkit/src/utils/package-json.ts index d78cba3e7b7a3..b2726a34f9609 100644 --- a/packages/devkit/src/utils/package-json.ts +++ b/packages/devkit/src/utils/package-json.ts @@ -11,8 +11,13 @@ import { requireNx } from '../../nx'; import { dirSync } from 'tmp'; import { join } from 'path'; -const { readJson, updateJson, getPackageManagerCommand, workspaceRoot } = - requireNx(); +const { + readJson, + updateJson, + getPackageManagerCommand, + workspaceRoot, + detectPackageManager, +} = requireNx(); const UNIDENTIFIED_VERSION = 'UNIDENTIFIED_VERSION'; const NON_SEMVER_TAGS = { @@ -448,9 +453,16 @@ export function ensurePackage( const tempDir = dirSync().name; console.log(`Fetching ${pkg}...`); - execSync(`${getPackageManagerCommand().addDev} ${pkg}@${requiredVersion}`, { + const packageManager = detectPackageManager(); + let addCommand = getPackageManagerCommand(packageManager).addDev; + if (packageManager === 'pnpm') { + addCommand = 'pnpm add -D'; // we need to ensure that we are not using workspace command + } + + const isVerbose = process.env.NX_VERBOSE_LOGGING === 'true'; + execSync(`${addCommand} ${pkg}@${requiredVersion}`, { cwd: tempDir, - stdio: 'ignore', + stdio: isVerbose ? 'inherit' : 'ignore', }); addToNodePath(join(workspaceRoot, 'node_modules'));