diff --git a/packages/devkit/.eslintrc.json b/packages/devkit/.eslintrc.json index ba61679fc8..db3f037515 100644 --- a/packages/devkit/.eslintrc.json +++ b/packages/devkit/.eslintrc.json @@ -1,34 +1,34 @@ { "extends": "../../.eslintrc", - "rules": { - "@typescript-eslint/no-restricted-imports": [ - "error", - { - "paths": [ - "@nx/workspace", - "@angular-devkit/core", - "@angular-devkit/architect", - "@angular-devkit/schematics" - ], - "patterns": [ - { - "group": ["nx/**/*"], - "message": "Use requireNx() from packages/devkit/nx.ts OR use a type import instead.", - "allowTypeImports": true - }, - { - "group": ["@nx/devkit/**/*"], - "message": "Use a relative import" - } - ] - } - ] - }, "ignorePatterns": ["!**/*"], "overrides": [ { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": {} + "rules": { + "@typescript-eslint/explicit-module-boundary-types": ["error"], + "@typescript-eslint/no-restricted-imports": [ + "error", + { + "paths": [ + "@nx/workspace", + "@angular-devkit/core", + "@angular-devkit/architect", + "@angular-devkit/schematics" + ], + "patterns": [ + { + "group": ["nx/**/*"], + "message": "Use requireNx() from packages/devkit/nx.ts OR use a type import instead.", + "allowTypeImports": true + }, + { + "group": ["@nx/devkit/**/*"], + "message": "Use a relative import" + } + ] + } + ] + } }, { "files": ["*.spec.ts"], @@ -38,9 +38,7 @@ }, { "files": ["*.ts", "*.tsx"], - "rules": { - "@typescript-eslint/explicit-module-boundary-types": ["error"] - } + "rules": {} }, { "files": ["*.js", "*.jsx"], diff --git a/packages/devkit/nx.ts b/packages/devkit/nx.ts index 1eccb0723e..9507d85076 100644 --- a/packages/devkit/nx.ts +++ b/packages/devkit/nx.ts @@ -1,7 +1,20 @@ -export function requireNx(): typeof import('nx/src/devkit-exports') { +// After Nx v18, this can be removed and replaced with either: +// - import {} from 'nx/src/devkit-exports' +// - import {} from 'nx/src/devkit-internals' +export function requireNx(): typeof import('nx/src/devkit-exports') & + Partial { try { - return require('nx/src/devkit-exports'); + let result = { ...require('nx/src/devkit-exports') }; + try { + result = { + ...result, + // Remove in Nx v18, devkit should not support Nx v16.0.2 at that point. + ...require('nx/src/devkit-internals'), + }; + } catch {} + return result; } catch { + // Remove in Nx V17, devkit should not support Nx < 16 at that point. return require('./nx-reexports-pre16'); } } diff --git a/packages/devkit/src/utils/package-json.ts b/packages/devkit/src/utils/package-json.ts index 3c7fc8711d..77535fe707 100644 --- a/packages/devkit/src/utils/package-json.ts +++ b/packages/devkit/src/utils/package-json.ts @@ -17,6 +17,7 @@ const { getPackageManagerCommand, workspaceRoot, detectPackageManager, + createTempNpmDirectory, } = requireNx(); const UNIDENTIFIED_VERSION = 'UNIDENTIFIED_VERSION'; @@ -450,7 +451,9 @@ export function ensurePackage( ); } - const tempDir = dirSync().name; + const { dir: tempDir } = createTempNpmDirectory?.() ?? { + dir: dirSync().name, + }; console.log(`Fetching ${pkg}...`); const packageManager = detectPackageManager(); diff --git a/packages/nx/src/devkit-internals.ts b/packages/nx/src/devkit-internals.ts new file mode 100644 index 0000000000..fca393f59c --- /dev/null +++ b/packages/nx/src/devkit-internals.ts @@ -0,0 +1,6 @@ +/** + * Note to developers: STOP! These exports are available via requireNx in @nx/devkit. + * + * These may not be available in certain version of Nx, so be sure to check them first. + */ +export { createTempNpmDirectory } from './utils/package-manager'; diff --git a/packages/nx/src/utils/package-manager.ts b/packages/nx/src/utils/package-manager.ts index e613806e49..22c1797b44 100644 --- a/packages/nx/src/utils/package-manager.ts +++ b/packages/nx/src/utils/package-manager.ts @@ -1,12 +1,13 @@ import { exec, execSync } from 'child_process'; import { copyFileSync, existsSync } from 'fs'; import { remove } from 'fs-extra'; -import { dirname, join } from 'path'; +import { dirname, join, relative } from 'path'; import { dirSync } from 'tmp'; import { promisify } from 'util'; import { writeJsonFile } from './fileutils'; import { readModulePackageJson } from './package-json'; import { gte, lt } from 'semver'; +import { workspaceRoot } from './workspace-root'; const execAsync = promisify(exec); @@ -118,16 +119,35 @@ export function getPackageManagerVersion( * Checks for a project level npmrc file by crawling up the file tree until * hitting a package.json file, as this is how npm finds them as well. */ -export function checkForNPMRC( +export function findFileInPackageJsonDirectory( + file: string, directory: string = process.cwd() ): string | null { while (!existsSync(join(directory, 'package.json'))) { directory = dirname(directory); } - const path = join(directory, '.npmrc'); + const path = join(directory, file); return existsSync(path) ? path : null; } +export function copyPackageManagerConfigurationFiles( + root: string, + destination: string +) { + for (const packageManagerConfigFile of ['.npmrc', '.yarnrc', '.yarnrc.yml']) { + // f is an absolute path, including the {workspaceRoot}. + const f = findFileInPackageJsonDirectory(packageManagerConfigFile, root); + if (f) { + // Destination should be the same relative path from the {workspaceRoot}, + // but now relative to the destination. `relative` makes `{workspaceRoot}/some/path` + // look like `./some/path`, and joining that gets us `{destination}/some/path + const destinationPath = join(destination, relative(root, f)); + // Copy config file if it exists, so that the package manager still follows it. + copyFileSync(f, destinationPath); + } + } +} + /** * Creates a temporary directory where you can run package manager commands safely. * @@ -140,11 +160,7 @@ export function createTempNpmDirectory() { // A package.json is needed for pnpm pack and for .npmrc to resolve writeJsonFile(`${dir}/package.json`, {}); - const npmrc = checkForNPMRC(); - if (npmrc) { - // Copy npmrc if it exists, so that npm still follows it. - copyFileSync(npmrc, `${dir}/.npmrc`); - } + copyPackageManagerConfigurationFiles(workspaceRoot, dir); const cleanup = async () => { try {