diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5d87f0beb7..956e08e8e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -70,7 +70,37 @@ To publish packages to a local registry, do the following: - Run `cd ./tmp` in Terminal 2 - Run `npx create-nx-workspace@999.9.9` in Terminal 2 -If you have problems publishing, make sure you use Node 14 and NPM 6 instead of Node 15 and NPM 7. +If you have problems publishing, make sure you use Node 16 and NPM 6 or 8. + +### Publishing for Yarn 2+ (Berry) + +Yarn Berry operates slightly differently than Yarn Classic. In order to publish packages for Berry follow next steps: + +- Run `yarn set version berry` to switch to latest Yarn version. +- Create `.yarnrc.yml` in root with following contents: + ```yml + nodeLinker: node-modules + npmRegistryServer: 'http://localhost:4873' + unsafeHttpWhitelist: + - localhost + ``` +- Run `yarn local-registry start` in Terminal 1 (keep it running) +- If you are creating nx workspace outside of your nx repo, make sure to add npm registry info to your root yarnrc (usually in ~/.yarnrc.yml). The file should look something like this: + + ```yml + npmRegistries: + 'https://registry.yarnpkg.com': + npmAuthToken: npm_****************** + yarnPath: .yarn/releases/yarn-3.2.2.cjs + + npmRegistryServer: 'http://localhost:4873' + unsafeHttpWhitelist: + - localhost + ``` + +- Run `yarn nx-release --local` in Terminal 2 to publish next minor version. If this version already exists, you can bump the minor version in `lerna.json` to toggle the next minor. The output will report the version of published packages. +- Go to your target folder (e.g. `cd ./tmp`) in Terminal 2 +- Run `yarn dlx create-nx-workspace@123.4.5` in Terminal 2 (replace `123.4.5` with the version that got published). ### Running Unit Tests diff --git a/packages/create-nx-workspace/bin/create-nx-workspace.ts b/packages/create-nx-workspace/bin/create-nx-workspace.ts index 6ab314fa42..ee24c76aee 100644 --- a/packages/create-nx-workspace/bin/create-nx-workspace.ts +++ b/packages/create-nx-workspace/bin/create-nx-workspace.ts @@ -13,6 +13,7 @@ import { getPackageManagerVersion, PackageManager, packageManagerList, + generatePackageManagerFiles, } from './package-manager'; import { validateNpmPackage } from './validate-npm-package'; import { deduceDefaultBase } from './default-base'; @@ -720,8 +721,9 @@ async function createSandbox(packageManager: PackageManager) { license: 'MIT', }) ); + generatePackageManagerFiles(tmpDir, packageManager); - await execAndWait(`${install} --silent --ignore-scripts`, tmpDir); + await execAndWait(install, tmpDir); installSpinner.succeed(); } catch (e) { diff --git a/packages/create-nx-workspace/bin/package-manager.ts b/packages/create-nx-workspace/bin/package-manager.ts index 18828b64b5..a7c7585f86 100644 --- a/packages/create-nx-workspace/bin/package-manager.ts +++ b/packages/create-nx-workspace/bin/package-manager.ts @@ -1,5 +1,5 @@ import { execSync } from 'child_process'; -import { existsSync } from 'fs'; +import { existsSync, writeFileSync } from 'fs'; import { join } from 'path'; /* @@ -37,21 +37,27 @@ export function getPackageManagerCommand( install: string; exec: string; } { + const [pmMajor, pmMinor] = + getPackageManagerVersion(packageManager).split('.'); + switch (packageManager) { case 'yarn': + const useBerry = +pmMajor >= 2; + const installCommand = 'yarn install --silent'; return { - install: 'yarn', + install: useBerry + ? installCommand + : `${installCommand} --ignore-scripts`, exec: 'yarn', }; case 'pnpm': - const [major, minor] = getPackageManagerVersion('pnpm').split('.'); let useExec = false; - if ((+major >= 6 && +minor >= 13) || +major >= 7) { + if ((+pmMajor >= 6 && +pmMinor >= 13) || +pmMajor >= 7) { useExec = true; } return { - install: 'pnpm install --no-frozen-lockfile', // explicitly disable in case of CI + install: 'pnpm install --no-frozen-lockfile --silent --ignore-scripts', exec: useExec ? 'pnpm exec' : 'pnpx', }; @@ -59,12 +65,29 @@ export function getPackageManagerCommand( process.env.npm_config_legacy_peer_deps = process.env.npm_config_legacy_peer_deps ?? 'true'; return { - install: 'npm install', + install: 'npm install --silent --ignore-scripts', exec: 'npx', }; } } +export function generatePackageManagerFiles( + root: string, + packageManager: PackageManager = detectPackageManager() +) { + const [pmMajor] = getPackageManagerVersion(packageManager).split('.'); + switch (packageManager) { + case 'yarn': + if (+pmMajor >= 2) { + writeFileSync( + join(root, '.yarnrc.yml'), + 'nodeLinker: node-modules\nenableScripts: false' + ); + } + break; + } +} + export function getPackageManagerVersion( packageManager: PackageManager ): string { diff --git a/packages/linter/src/utils/convert-tslint-to-eslint/project-converter.spec.ts b/packages/linter/src/utils/convert-tslint-to-eslint/project-converter.spec.ts index 8f28e823b5..988e461406 100644 --- a/packages/linter/src/utils/convert-tslint-to-eslint/project-converter.spec.ts +++ b/packages/linter/src/utils/convert-tslint-to-eslint/project-converter.spec.ts @@ -13,7 +13,17 @@ import { ProjectConverter } from './project-converter'; /** * Don't run actual child_process implementation of installPackagesTask() */ -jest.mock('child_process'); +jest.mock('child_process', () => { + return { + ...jest.requireActual('child_process'), + execSync: jest.fn((command: string) => { + if (command.includes('yarn --version')) { + return '1.22.0'; + } + return; + }), + }; +}); /** * Don't run the conversion util, it touches the file system and has its own tests diff --git a/packages/nx/src/utils/package-manager.ts b/packages/nx/src/utils/package-manager.ts index 77b72b6e26..c41165b979 100644 --- a/packages/nx/src/utils/package-manager.ts +++ b/packages/nx/src/utils/package-manager.ts @@ -4,8 +4,8 @@ import { remove } from 'fs-extra'; import { dirname, join } from 'path'; import { dirSync } from 'tmp'; import { promisify } from 'util'; -import { readJsonFile, writeJsonFile } from './fileutils'; -import { PackageJson, readModulePackageJson } from './package-json'; +import { writeJsonFile } from './fileutils'; +import { readModulePackageJson } from './package-json'; import { gte, lt } from 'semver'; const execAsync = promisify(exec); @@ -49,16 +49,23 @@ export function getPackageManagerCommand( packageManager: PackageManager = detectPackageManager() ): PackageManagerCommands { const commands: { [pm in PackageManager]: () => PackageManagerCommands } = { - yarn: () => ({ - install: 'yarn', - ciInstall: 'yarn --frozen-lockfile', - add: 'yarn add -W', - addDev: 'yarn add -D -W', - rm: 'yarn remove', - exec: 'yarn', - run: (script: string, args: string) => `yarn ${script} ${args}`, - list: 'yarn list', - }), + yarn: () => { + const yarnVersion = getPackageManagerVersion('yarn'); + const useBerry = gte(yarnVersion, '2.0.0'); + + return { + install: 'yarn', + ciInstall: useBerry + ? 'yarn install --immutable' + : 'yarn install --frozen-lockfile', + add: useBerry ? 'yarn add' : 'yarn add -W', + addDev: useBerry ? 'yarn add -D' : 'yarn add -D -W', + rm: 'yarn remove', + exec: useBerry ? 'yarn exec' : 'yarn', + run: (script: string, args: string) => `yarn ${script} ${args}`, + list: useBerry ? 'yarn info --name-only' : 'yarn list', + }; + }, pnpm: () => { const pnpmVersion = getPackageManagerVersion('pnpm'); const useExec = gte(pnpmVersion, '6.13.0'); diff --git a/packages/workspace/src/generators/ci-workflow/__snapshots__/ci-workflow.spec.ts.snap b/packages/workspace/src/generators/ci-workflow/__snapshots__/ci-workflow.spec.ts.snap index 950b4be305..852b1072de 100644 --- a/packages/workspace/src/generators/ci-workflow/__snapshots__/ci-workflow.spec.ts.snap +++ b/packages/workspace/src/generators/ci-workflow/__snapshots__/ci-workflow.spec.ts.snap @@ -28,7 +28,7 @@ jobs: pool: vmImage: 'ubuntu-latest' steps: - - script: yarn --frozen-lockfile + - script: yarn install --frozen-lockfile displayName: NPM Install Dependencies - script: npx nx-cloud start-agent displayName: Start Nx-Cloud agent @@ -38,7 +38,7 @@ jobs: pool: vmImage: 'ubuntu-latest' steps: - - script: yarn --frozen-lockfile + - script: yarn install --frozen-lockfile displayName: NPM Install Dependencies - script: yarn nx-cloud start-ci-run displayName: Start CI run @@ -75,7 +75,7 @@ jobs: - checkout - run: name: Install dependencies - command: yarn --frozen-lockfile + command: yarn install --frozen-lockfile - run: name: Start the agent << parameters.ordinal >> command: yarn nx-cloud start-agent @@ -89,7 +89,7 @@ jobs: - checkout - run: name: Install dependencies - command: yarn --frozen-lockfile + command: yarn install --frozen-lockfile - nx/set-shas: main-branch-name: 'main' - run: diff --git a/packages/workspace/src/generators/workspace/workspace.ts b/packages/workspace/src/generators/workspace/workspace.ts index 8b56f34234..f307f2b893 100644 --- a/packages/workspace/src/generators/workspace/workspace.ts +++ b/packages/workspace/src/generators/workspace/workspace.ts @@ -6,6 +6,8 @@ import { names, writeJson, formatFiles, + getPackageManagerVersion, + PackageManager, } from '@nrwl/devkit'; import { Schema } from './schema'; import { @@ -94,6 +96,14 @@ function createNpmrc(host: Tree, options: Schema) { ); } +// ensure that yarn (berry) install uses classic node linker +function createYarnrcYml(host: Tree, options: Schema) { + host.write( + join(options.directory, '.yarnrc.yml'), + 'nodeLinker: node-modules\n' + ); +} + function formatWorkspaceJson(host: Tree, options: Schema) { const path = join( options.directory, @@ -151,8 +161,13 @@ export async function workspaceGenerator(host: Tree, options: Schema) { if (options.cli === 'angular') { decorateAngularClI(host, options); } - if (options.packageManager === 'pnpm') { + const [packageMajor] = getPackageManagerVersion( + options.packageManager as PackageManager + ).split('.'); + if (options.packageManager === 'pnpm' && +packageMajor >= 7) { createNpmrc(host, options); + } else if (options.packageManager === 'yarn' && +packageMajor >= 2) { + createYarnrcYml(host, options); } setPresetProperty(host, options); addNpmScripts(host, options);