@nx/js 23.2.0-pr.36527.2252709 → 23.2.0-pr.36838.ef8e258

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +2 -2
  2. package/dist/internal.d.ts +3 -0
  3. package/dist/internal.js +10 -2
  4. package/dist/src/executors/copy-workspace-modules/copy-workspace-modules.js +120 -17
  5. package/dist/src/executors/prune-lockfile/prune-lockfile.js +38 -26
  6. package/dist/src/executors/tsc/lib/typescript-compilation.js +1 -1
  7. package/dist/src/generators/library/schema.d.ts +1 -2
  8. package/dist/src/index.d.ts +1 -2
  9. package/dist/src/index.js +3 -3
  10. package/dist/src/migrations/update-23-2-0/add-pnpm-deploy-output-cache-inputs.d.ts +14 -0
  11. package/dist/src/migrations/update-23-2-0/add-pnpm-deploy-output-cache-inputs.js +242 -0
  12. package/dist/src/migrations/update-23-2-0/add-pnpm-deploy-output-cache-inputs.md +64 -0
  13. package/dist/src/migrations/update-23-2-0/add-pnpm-prune-lockfile-cache-config.d.ts +2 -0
  14. package/dist/src/migrations/update-23-2-0/add-pnpm-prune-lockfile-cache-config.js +305 -0
  15. package/dist/src/migrations/update-23-2-0/add-pnpm-prune-lockfile-cache-config.md +59 -0
  16. package/dist/src/release/utils/update-lock-file.js +42 -1
  17. package/dist/src/release/version-actions.d.ts +5 -1
  18. package/dist/src/release/version-actions.js +151 -47
  19. package/dist/src/utils/package-json/update-package-json.d.ts +1 -1
  20. package/dist/src/utils/package-json/update-package-json.js +7 -13
  21. package/dist/src/utils/pnpm-deploy-output-cache-inputs.d.ts +29 -0
  22. package/dist/src/utils/pnpm-deploy-output-cache-inputs.js +177 -0
  23. package/dist/src/utils/pnpm-install-settings-inputs.d.ts +45 -0
  24. package/dist/src/utils/pnpm-install-settings-inputs.js +77 -0
  25. package/dist/src/utils/schema.d.ts +1 -2
  26. package/dist/src/utils/swc/compile-swc.d.ts +1 -0
  27. package/dist/src/utils/swc/compile-swc.js +7 -2
  28. package/dist/src/utils/target-defaults-matching.d.ts +57 -0
  29. package/dist/src/utils/target-defaults-matching.js +126 -0
  30. package/dist/src/utils/workspace-module-sections.d.ts +16 -0
  31. package/dist/src/utils/workspace-module-sections.js +24 -0
  32. package/migrations.json +12 -0
  33. package/package.json +11 -11
package/README.md CHANGED
@@ -7,9 +7,9 @@
7
7
 
8
8
  <div style="text-align: center;">
9
9
 
10
- [![License](https://img.shields.io/npm/l/@nx/workspace.svg?style=flat-square)]()
10
+ [![License](https://img.shields.io/npm/l/@nx/workspace.svg?style=flat-square)](<>)
11
11
  [![NPM Version](https://badge.fury.io/js/nx.svg)](https://www.npmjs.com/package/nx)
12
- [![Semantic Release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)]()
12
+ [![Semantic Release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)](<>)
13
13
  [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
14
14
  [![Join us on the Official Nx Discord Server](https://img.shields.io/discord/1143497901675401286?label=discord)](https://go.nx.dev/community)
15
15
  [![Nx Sandboxing](https://staging.nx.app/workspaces/62d013ea0852fe0a2df74438/sandbox-badge.svg)](https://nx.dev/docs/features/ci-features/sandboxing)
@@ -3,6 +3,9 @@ export { TargetProjectLocator, isBuiltinModuleImport, } from '@nx/devkit/interna
3
3
  export { findProjectsNpmDependencies } from '@nx/devkit/internal';
4
4
  export { walkTsconfigExtendsChain, type RawTsconfigJsonCache, } from './src/utils/typescript/raw-tsconfig';
5
5
  export { isEsmProject } from './src/utils/module-format/is-esm-project';
6
+ export { PNPM_INSTALL_SETTINGS_INPUTS, PNPM_MAJOR_RUNTIME_INPUT, pnpmInstallSettingsInputsForInferredTarget, shouldIncludePnpmMajorRuntimeInput, } from './src/utils/pnpm-install-settings-inputs';
7
+ export { addPnpmDeployOutputCacheInputs } from './src/utils/pnpm-deploy-output-cache-inputs';
8
+ export type { MatchedTargetRef } from './src/utils/target-defaults-matching';
6
9
  export { TS_SOLUTION_SETUP_TSCONFIG_INPUT, addProjectToTsSolutionWorkspace, assertNotUsingTsSolutionSetup, findRuntimeTsConfigName, getDefinedCustomConditionName, getProjectSourceRoot, getProjectType, isUsingTsSolutionSetup, shouldConfigureTsSolutionSetup, updateTsconfigFiles, } from './src/utils/typescript/ts-solution-setup';
7
10
  export { getTsConfigBaseOptions } from './src/utils/typescript/create-ts-config';
8
11
  export { createTreeParseConfigHost, type TreeParseConfigHost, } from './src/utils/typescript/ts-config';
package/dist/internal.js CHANGED
@@ -5,8 +5,8 @@
5
5
  // internal consumers and may change without semver protection. Consider it
6
6
  // the @nx/js equivalent of `@nx/devkit/internal`.
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.getImportPath = exports.createGlobPatternsForDependencies = exports.addLintingToProject = exports.detectLinters = exports.normalizeUnitTestRunnerOption = exports.normalizeLinterOption = exports.isValidPackageJsonBuildConfig = exports.addBuildAndWatchDepsTargets = exports.findNpmDependencies = exports.getProjectPackageManagerWorkspaceStateWarningTask = exports.getProjectPackageManagerWorkspaceState = exports.getNpmScope = exports.sortPackageJsonFields = exports.CopyAssetsHandler = exports.addSwcRegisterDependencies = exports.addSwcDependencies = exports.addSwcTestConfig = exports.addSwcConfig = exports.createTmpTsConfig = exports.computeCompilerOptionsPaths = exports.calculateProjectDependencies = exports.calculateProjectBuildableDependencies = exports.compileTypeScript = exports.getNeededCompilerOptionOverrides = exports.isTypescriptVersionAtLeast = exports.getTsConfigModuleResolution = exports.getRangeMinimum = exports.ensureTypescript = exports.createTreeParseConfigHost = exports.getTsConfigBaseOptions = exports.updateTsconfigFiles = exports.shouldConfigureTsSolutionSetup = exports.isUsingTsSolutionSetup = exports.getProjectType = exports.getProjectSourceRoot = exports.getDefinedCustomConditionName = exports.findRuntimeTsConfigName = exports.assertNotUsingTsSolutionSetup = exports.addProjectToTsSolutionWorkspace = exports.TS_SOLUTION_SETUP_TSCONFIG_INPUT = exports.isEsmProject = exports.walkTsconfigExtendsChain = exports.findProjectsNpmDependencies = exports.isBuiltinModuleImport = exports.TargetProjectLocator = exports.requireWithTsconfigFallback = exports.registerTsConfigPaths = exports.registerTsProject = exports.loadTsFile = exports.forceRegisterEsmLoader = void 0;
9
- exports.typesNodeVersion = exports.tsLibVersion = exports.swcNodeVersion = exports.swcHelpersVersion = exports.swcCoreVersion = exports.swcCliVersion = exports.prettierVersion = exports.esbuildVersion = exports.nxVersion = exports.releaseTasks = exports.addReleaseConfigForTsSolution = exports.addReleaseConfigForNonTsSolution = exports.addLocalRegistryScripts = exports.stripGlobToBaseDir = void 0;
8
+ exports.normalizeLinterOption = exports.isValidPackageJsonBuildConfig = exports.addBuildAndWatchDepsTargets = exports.findNpmDependencies = exports.getProjectPackageManagerWorkspaceStateWarningTask = exports.getProjectPackageManagerWorkspaceState = exports.getNpmScope = exports.sortPackageJsonFields = exports.CopyAssetsHandler = exports.addSwcRegisterDependencies = exports.addSwcDependencies = exports.addSwcTestConfig = exports.addSwcConfig = exports.createTmpTsConfig = exports.computeCompilerOptionsPaths = exports.calculateProjectDependencies = exports.calculateProjectBuildableDependencies = exports.compileTypeScript = exports.getNeededCompilerOptionOverrides = exports.isTypescriptVersionAtLeast = exports.getTsConfigModuleResolution = exports.getRangeMinimum = exports.ensureTypescript = exports.createTreeParseConfigHost = exports.getTsConfigBaseOptions = exports.updateTsconfigFiles = exports.shouldConfigureTsSolutionSetup = exports.isUsingTsSolutionSetup = exports.getProjectType = exports.getProjectSourceRoot = exports.getDefinedCustomConditionName = exports.findRuntimeTsConfigName = exports.assertNotUsingTsSolutionSetup = exports.addProjectToTsSolutionWorkspace = exports.TS_SOLUTION_SETUP_TSCONFIG_INPUT = exports.addPnpmDeployOutputCacheInputs = exports.shouldIncludePnpmMajorRuntimeInput = exports.pnpmInstallSettingsInputsForInferredTarget = exports.PNPM_MAJOR_RUNTIME_INPUT = exports.PNPM_INSTALL_SETTINGS_INPUTS = exports.isEsmProject = exports.walkTsconfigExtendsChain = exports.findProjectsNpmDependencies = exports.isBuiltinModuleImport = exports.TargetProjectLocator = exports.requireWithTsconfigFallback = exports.registerTsConfigPaths = exports.registerTsProject = exports.loadTsFile = exports.forceRegisterEsmLoader = void 0;
9
+ exports.typesNodeVersion = exports.tsLibVersion = exports.swcNodeVersion = exports.swcHelpersVersion = exports.swcCoreVersion = exports.swcCliVersion = exports.prettierVersion = exports.esbuildVersion = exports.nxVersion = exports.releaseTasks = exports.addReleaseConfigForTsSolution = exports.addReleaseConfigForNonTsSolution = exports.addLocalRegistryScripts = exports.stripGlobToBaseDir = exports.getImportPath = exports.createGlobPatternsForDependencies = exports.addLintingToProject = exports.detectLinters = exports.normalizeUnitTestRunnerOption = void 0;
10
10
  // Re-exports of nx-source internals.
11
11
  var internal_1 = require("@nx/devkit/internal");
12
12
  Object.defineProperty(exports, "forceRegisterEsmLoader", { enumerable: true, get: function () { return internal_1.forceRegisterEsmLoader; } });
@@ -26,6 +26,14 @@ Object.defineProperty(exports, "walkTsconfigExtendsChain", { enumerable: true, g
26
26
  // TS solution awareness; mirrors Node's nearest-package.json semantics).
27
27
  var is_esm_project_1 = require("./src/utils/module-format/is-esm-project");
28
28
  Object.defineProperty(exports, "isEsmProject", { enumerable: true, get: function () { return is_esm_project_1.isEsmProject; } });
29
+ // Inputs for build targets that emit a pruned pnpm deploy output
30
+ var pnpm_install_settings_inputs_1 = require("./src/utils/pnpm-install-settings-inputs");
31
+ Object.defineProperty(exports, "PNPM_INSTALL_SETTINGS_INPUTS", { enumerable: true, get: function () { return pnpm_install_settings_inputs_1.PNPM_INSTALL_SETTINGS_INPUTS; } });
32
+ Object.defineProperty(exports, "PNPM_MAJOR_RUNTIME_INPUT", { enumerable: true, get: function () { return pnpm_install_settings_inputs_1.PNPM_MAJOR_RUNTIME_INPUT; } });
33
+ Object.defineProperty(exports, "pnpmInstallSettingsInputsForInferredTarget", { enumerable: true, get: function () { return pnpm_install_settings_inputs_1.pnpmInstallSettingsInputsForInferredTarget; } });
34
+ Object.defineProperty(exports, "shouldIncludePnpmMajorRuntimeInput", { enumerable: true, get: function () { return pnpm_install_settings_inputs_1.shouldIncludePnpmMajorRuntimeInput; } });
35
+ var pnpm_deploy_output_cache_inputs_1 = require("./src/utils/pnpm-deploy-output-cache-inputs");
36
+ Object.defineProperty(exports, "addPnpmDeployOutputCacheInputs", { enumerable: true, get: function () { return pnpm_deploy_output_cache_inputs_1.addPnpmDeployOutputCacheInputs; } });
29
37
  // TS solution setup detection
30
38
  var ts_solution_setup_1 = require("./src/utils/typescript/ts-solution-setup");
31
39
  Object.defineProperty(exports, "TS_SOLUTION_SETUP_TSCONFIG_INPUT", { enumerable: true, get: function () { return ts_solution_setup_1.TS_SOLUTION_SETUP_TSCONFIG_INPUT; } });
@@ -7,6 +7,49 @@ const node_fs_1 = require("node:fs");
7
7
  const path_1 = require("path");
8
8
  const fs_1 = require("fs");
9
9
  const strip_glob_to_base_dir_1 = require("../../utils/strip-glob-to-base-dir");
10
+ const workspace_module_sections_1 = require("../../utils/workspace-module-sections");
11
+ // Sections of a copied module's own manifest whose workspace-module deps are
12
+ // rewritten to file: and recursed into. pnpm installs a transitive dependency's
13
+ // regular, optional, and auto-installed peer deps (a workspace peer is linked
14
+ // and recorded under the depending importer's dependencies in the lockfile), but
15
+ // never its devDependencies. A workspace-module peer is moved into dependencies,
16
+ // since pnpm rejects a file: spec under peerDependencies (mirrors the app-level
17
+ // move in prune-lockfile).
18
+ const TRANSITIVE_INSTALL_SECTIONS = [
19
+ 'dependencies',
20
+ 'optionalDependencies',
21
+ 'peerDependencies',
22
+ ];
23
+ // Resolve `catalog:` references in a copied module manifest to the version the
24
+ // workspace pinned. Only the sections pnpm installs for a `file:` directory
25
+ // dependency are resolved: a copied module's devDependencies are never
26
+ // installed, so a `catalog:` there is inert (pnpm leaves it untouched), and
27
+ // resolving it would abort the whole deploy build if that catalog entry does
28
+ // not exist. Returns whether anything changed.
29
+ function resolveCatalogReferences(packageJson, manager) {
30
+ if (!manager) {
31
+ return false;
32
+ }
33
+ let modified = false;
34
+ for (const section of TRANSITIVE_INSTALL_SECTIONS) {
35
+ const deps = packageJson[section];
36
+ if (!deps) {
37
+ continue;
38
+ }
39
+ for (const [name, version] of Object.entries(deps)) {
40
+ if (!manager.isCatalogReference(version)) {
41
+ continue;
42
+ }
43
+ const resolved = manager.resolveCatalogReference(devkit_1.workspaceRoot, name, version);
44
+ if (!resolved) {
45
+ throw new Error(`Could not resolve catalog reference for package ${name}@${version}.`);
46
+ }
47
+ deps[name] = resolved;
48
+ modified = true;
49
+ }
50
+ }
51
+ return modified;
52
+ }
10
53
  async function copyWorkspaceModules(schema, context) {
11
54
  devkit_1.logger.log('Copying Workspace Modules to Build Directory...');
12
55
  const outputDirectory = getOutputDir(schema, context);
@@ -17,10 +60,14 @@ async function copyWorkspaceModules(schema, context) {
17
60
  return { success: true };
18
61
  }
19
62
  function handleWorkspaceModules(outputDirectory, packageJson, projectGraph) {
20
- if (!packageJson.dependencies) {
63
+ if (!workspace_module_sections_1.WORKSPACE_MODULE_INSTALL_SECTIONS.some((section) => packageJson[section])) {
21
64
  return;
22
65
  }
23
66
  const workspaceModules = (0, internal_1.getWorkspacePackagesFromGraph)(projectGraph);
67
+ const catalogManager = (0, internal_1.getCatalogManager)(devkit_1.workspaceRoot);
68
+ // Only the pnpm prune path ships non-workspace local-path targets, so only
69
+ // pnpm manifests get their file:/link: specs relocated.
70
+ const isPnpmWorkspace = (0, devkit_1.detectPackageManager)(devkit_1.workspaceRoot) === 'pnpm';
24
71
  const processedModules = new Set();
25
72
  const workspaceModulesDir = (0, path_1.join)(outputDirectory, 'workspace_modules');
26
73
  function calculateRelativePath(fromPkgName, toPkgName) {
@@ -41,11 +88,18 @@ function handleWorkspaceModules(outputDirectory, packageJson, projectGraph) {
41
88
  processedModules.add(pkgName);
42
89
  devkit_1.logger.verbose(`Copying ${pkgName}.`);
43
90
  const workspaceModuleProject = workspaceModules.get(pkgName);
44
- const workspaceModuleRoot = (0, path_1.join)(devkit_1.workspaceRoot, workspaceModuleProject.data.root);
91
+ const workspaceModuleRoot = workspaceModuleProject.data.root;
92
+ // data.root is workspace-root-relative in the graph, but may arrive
93
+ // absolute; normalize so the local-path relocation math is in
94
+ // workspace-root space.
95
+ const moduleWsRelativeRoot = (0, path_1.isAbsolute)(workspaceModuleRoot)
96
+ ? (0, path_1.relative)(devkit_1.workspaceRoot, workspaceModuleRoot).split(path_1.sep).join('/')
97
+ : workspaceModuleRoot;
45
98
  const newWorkspaceModulePath = (0, path_1.join)(workspaceModulesDir, pkgName);
46
- // Copy the module
99
+ // Copy the module, from the workspace root rather than the process cwd: a
100
+ // graph root arrives workspace-relative in the common case.
47
101
  (0, node_fs_1.mkdirSync)(newWorkspaceModulePath, { recursive: true });
48
- (0, node_fs_1.cpSync)(workspaceModuleRoot, newWorkspaceModulePath, {
102
+ (0, node_fs_1.cpSync)((0, path_1.join)(devkit_1.workspaceRoot, moduleWsRelativeRoot), newWorkspaceModulePath, {
49
103
  filter: (src) => !src.includes('node_modules'),
50
104
  recursive: true,
51
105
  });
@@ -60,26 +114,75 @@ function handleWorkspaceModules(outputDirectory, packageJson, projectGraph) {
60
114
  devkit_1.logger.warn(`Could not read package.json for ${pkgName}: ${e.message}`);
61
115
  return;
62
116
  }
63
- // Process and update dependencies
64
- if (copiedPackageJson.dependencies) {
65
- let packageJsonModified = false;
66
- for (const [depName, depVersion] of Object.entries(copiedPackageJson.dependencies)) {
117
+ // The standalone dist ships no catalog definition, so resolve any
118
+ // `catalog:` references to the version the workspace pinned.
119
+ let packageJsonModified = resolveCatalogReferences(copiedPackageJson, catalogManager);
120
+ // Rewrite sibling workspace-module deps to file: paths and recurse. A peer
121
+ // is moved into dependencies (pnpm rejects a file: spec under
122
+ // peerDependencies), dropping its now-orphaned optional/required marker.
123
+ for (const section of TRANSITIVE_INSTALL_SECTIONS) {
124
+ const deps = copiedPackageJson[section];
125
+ if (!deps) {
126
+ continue;
127
+ }
128
+ for (const depName of Object.keys(deps)) {
67
129
  if (workspaceModules.has(depName)) {
68
- const relativePath = calculateRelativePath(pkgName, depName);
69
- copiedPackageJson.dependencies[depName] = `file:${relativePath}`;
130
+ const fileSpec = `file:${calculateRelativePath(pkgName, depName)}`;
131
+ if (section === 'peerDependencies') {
132
+ (0, internal_1.movePeerDependencyToDependencies)(copiedPackageJson, depName, fileSpec);
133
+ }
134
+ else {
135
+ deps[depName] = fileSpec;
136
+ }
70
137
  packageJsonModified = true;
71
138
  processModule(depName);
139
+ continue;
140
+ }
141
+ // A non-workspace file:/link: dep must keep resolving after the module
142
+ // moves. pnpm reads a non-importer manifest file: spec relative to the
143
+ // package dir but a link: spec relative to the install root, so each
144
+ // rebases onto a different base (pnpm-only; see isPnpmWorkspace).
145
+ if (!isPnpmWorkspace) {
146
+ continue;
147
+ }
148
+ const spec = deps[depName];
149
+ const relocation = (0, internal_1.relocatePrunedLocalPathSpec)(spec, moduleWsRelativeRoot, spec.startsWith('link:') ? '' : `workspace_modules/${pkgName}`);
150
+ if (!relocation) {
151
+ continue;
152
+ }
153
+ if (relocation.reason) {
154
+ (0, internal_1.warnUnshippableLocalPathSpec)(`"${spec}" in ${pkgName}`, relocation.reason);
155
+ }
156
+ if (section === 'peerDependencies') {
157
+ // Moved even when the target cannot ship: pnpm rejects the spec here
158
+ // either way.
159
+ (0, internal_1.movePeerDependencyToDependencies)(copiedPackageJson, depName, relocation.spec);
160
+ packageJsonModified = true;
161
+ }
162
+ else if (!relocation.reason) {
163
+ deps[depName] = relocation.spec;
164
+ packageJsonModified = true;
72
165
  }
73
166
  }
74
- if (packageJsonModified) {
75
- (0, node_fs_1.writeFileSync)(copiedPackageJsonPath, JSON.stringify(copiedPackageJson, null, 2));
76
- devkit_1.logger.verbose(`Updated package.json for ${pkgName} with relative workspace module paths.`);
77
- }
167
+ }
168
+ (0, internal_1.dropEmptyPeerDependencySections)(copiedPackageJson);
169
+ if (packageJsonModified) {
170
+ (0, node_fs_1.writeFileSync)(copiedPackageJsonPath, JSON.stringify(copiedPackageJson, null, 2));
171
+ devkit_1.logger.verbose(`Updated package.json for ${pkgName}.`);
78
172
  }
79
173
  }
80
- // Process all top-level dependencies
81
- for (const [pkgName] of Object.entries(packageJson.dependencies)) {
82
- processModule(pkgName);
174
+ // Seed from every section the app declares a workspace module in. Copied
175
+ // modules recurse over production sections only (see processModule).
176
+ // processModule dedups via processedModules, so a module listed in several
177
+ // sections is copied once.
178
+ for (const section of workspace_module_sections_1.WORKSPACE_MODULE_INSTALL_SECTIONS) {
179
+ const deps = packageJson[section];
180
+ if (!deps) {
181
+ continue;
182
+ }
183
+ for (const pkgName of Object.keys(deps)) {
184
+ processModule(pkgName);
185
+ }
83
186
  }
84
187
  }
85
188
  function createWorkspaceModules(outputDirectory) {
@@ -3,48 +3,60 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = pruneLockfileExecutor;
4
4
  exports.resolveCatalogReferences = resolveCatalogReferences;
5
5
  const devkit_1 = require("@nx/devkit");
6
- const internal_1 = require("@nx/devkit/internal");
7
6
  const fs_1 = require("fs");
8
7
  const path_1 = require("path");
8
+ const internal_1 = require("@nx/devkit/internal");
9
9
  const strip_glob_to_base_dir_1 = require("../../utils/strip-glob-to-base-dir");
10
+ const workspace_module_sections_1 = require("../../utils/workspace-module-sections");
10
11
  async function pruneLockfileExecutor(schema, context) {
11
12
  devkit_1.logger.log('Pruning lockfile...');
12
13
  const outputDirectory = getOutputDir(schema, context);
13
14
  const packageJson = resolveCatalogReferences(getPackageJson(schema, context));
14
15
  mergeAllowScripts(packageJson);
15
16
  const packageManager = (0, devkit_1.detectPackageManager)(devkit_1.workspaceRoot);
16
- if (packageManager === 'bun') {
17
- devkit_1.logger.warn('Bun lockfile generation is not supported. Only package.json will be generated. Run "bun install" in the output directory if needed.');
18
- (0, fs_1.writeFileSync)((0, path_1.join)(outputDirectory, 'package.json'), JSON.stringify(packageJson, null, 2));
19
- }
20
- else {
21
- const { lockfileName, lockFile } = createPrunedLockfile(packageJson, context.projectGraph);
22
- const lockfileOutputPath = (0, path_1.join)(outputDirectory, lockfileName);
23
- (0, fs_1.writeFileSync)(lockfileOutputPath, lockFile);
24
- (0, fs_1.writeFileSync)((0, path_1.join)(outputDirectory, 'package.json'), JSON.stringify(packageJson, null, 2));
25
- devkit_1.logger.log(`Lockfile pruned: ${lockfileOutputPath}`);
26
- }
17
+ const { project } = (0, devkit_1.parseTargetString)(schema.buildTarget, context);
18
+ const projectRoot = context.projectGraph.nodes[project].data.root;
19
+ (0, internal_1.generatePrunedDeployOutput)(packageJson, context.projectGraph, projectRoot, {
20
+ outputDirectory,
21
+ packageManager,
22
+ workspaceRoot: devkit_1.workspaceRoot,
23
+ });
24
+ rewriteWorkspaceModuleSpecifiers(packageJson, context.projectGraph);
25
+ (0, fs_1.writeFileSync)((0, path_1.join)(outputDirectory, 'package.json'), JSON.stringify(packageJson, null, 2));
26
+ devkit_1.logger.log(`Pruned deploy output written to ${outputDirectory}`);
27
27
  return {
28
28
  success: true,
29
29
  };
30
30
  }
31
- function createPrunedLockfile(packageJson, graph) {
32
- const packageManager = (0, devkit_1.detectPackageManager)(devkit_1.workspaceRoot);
33
- const lockfileName = (0, internal_1.getLockFileName)(packageManager);
34
- const lockFile = (0, internal_1.createLockFile)(packageJson, graph, packageManager);
31
+ // Point every workspace-module dependency at its copied directory so the
32
+ // standalone output installs them as pnpm `file:` directory dependencies.
33
+ // pnpm rejects a `file:` spec under peerDependencies, so a peer-declared
34
+ // workspace module is moved into dependencies instead (an optional peer
35
+ // becomes required, which is moot since the module is always copied in). Gate
36
+ // strictly on graph membership: a `file:`/`link:` spec to a non-workspace
37
+ // local path (e.g. a vendored tarball) is left alone, since
38
+ // copy-workspace-modules only ever copies actual workspace projects.
39
+ function rewriteWorkspaceModuleSpecifiers(packageJson, graph) {
35
40
  const workspacePackages = (0, internal_1.getWorkspacePackagesFromGraph)(graph);
36
- for (const [pkgName, pkgVersion] of Object.entries(packageJson.dependencies ?? {})) {
37
- if (pkgVersion.startsWith('workspace:') ||
38
- pkgVersion.startsWith('file:') ||
39
- pkgVersion.startsWith('link:') ||
40
- workspacePackages.has(pkgName)) {
41
- packageJson.dependencies[pkgName] = `file:./workspace_modules/${pkgName}`;
41
+ for (const section of workspace_module_sections_1.WORKSPACE_MODULE_INSTALL_SECTIONS) {
42
+ const deps = packageJson[section];
43
+ if (!deps) {
44
+ continue;
45
+ }
46
+ for (const pkgName of Object.keys(deps)) {
47
+ if (!workspacePackages.has(pkgName)) {
48
+ continue;
49
+ }
50
+ const fileSpec = `file:./workspace_modules/${pkgName}`;
51
+ if (section === 'peerDependencies') {
52
+ (0, internal_1.movePeerDependencyToDependencies)(packageJson, pkgName, fileSpec);
53
+ }
54
+ else {
55
+ deps[pkgName] = fileSpec;
56
+ }
42
57
  }
43
58
  }
44
- return {
45
- lockfileName,
46
- lockFile,
47
- };
59
+ (0, internal_1.dropEmptyPeerDependencySections)(packageJson);
48
60
  }
49
61
  /**
50
62
  * npm reads the `allowScripts` install-script allowlist only from the install
@@ -154,7 +154,7 @@ function compileTSWithWatch(context, logger, hooks, reporters) {
154
154
  project.kind !== ts.InvalidatedProjectKind.UpdateOutputFileStamps) {
155
155
  logger.warn(`The project ${projectName} ` +
156
156
  `is using the deprecated "prepend" Typescript compiler option. ` +
157
- `This option is not supported by the batch executor and it's ignored.`);
157
+ `This option is not supported by the batch executor and it's ignored.`, project.project);
158
158
  continue;
159
159
  }
160
160
  hooks?.beforeProjectCompilationCallback(project.project);
@@ -41,8 +41,7 @@ export interface LibraryGeneratorSchema {
41
41
  passWithNoTests?: boolean;
42
42
  }
43
43
 
44
- export interface NormalizedLibraryGeneratorOptions
45
- extends LibraryGeneratorSchema {
44
+ export interface NormalizedLibraryGeneratorOptions extends LibraryGeneratorSchema {
46
45
  name: string;
47
46
  /** `normalizeOptions` always resolves this, so it is no longer optional. */
48
47
  linter: LinterType;
@@ -18,5 +18,4 @@ export { setupPrettierGenerator } from './generators/setup-prettier/generator';
18
18
  export { setupVerdaccio } from './generators/setup-verdaccio/generator';
19
19
  export { isValidVariable } from './utils/is-valid-variable';
20
20
  export { type LinterType } from './utils/linter';
21
- export { createLockFile, getLockFileName } from '@nx/devkit/internal';
22
- export { createPackageJson } from '@nx/devkit/internal';
21
+ export { createLockFile, createPackageJson, generatePrunedDeployOutput, getLockFileName, } from '@nx/devkit/internal';
package/dist/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createPackageJson = exports.getLockFileName = exports.createLockFile = exports.isValidVariable = exports.setupVerdaccio = exports.setupPrettierGenerator = exports.setUpFormatter = exports.initGenerator = exports.libraryGenerator = void 0;
3
+ exports.getLockFileName = exports.generatePrunedDeployOutput = exports.createPackageJson = exports.createLockFile = exports.isValidVariable = exports.setupVerdaccio = exports.setupPrettierGenerator = exports.setUpFormatter = exports.initGenerator = exports.libraryGenerator = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  tslib_1.__exportStar(require("./utils/typescript/add-tslib-dependencies"), exports);
6
6
  tslib_1.__exportStar(require("./utils/typescript/load-ts-transformers"), exports);
@@ -29,6 +29,6 @@ var is_valid_variable_1 = require("./utils/is-valid-variable");
29
29
  Object.defineProperty(exports, "isValidVariable", { enumerable: true, get: function () { return is_valid_variable_1.isValidVariable; } });
30
30
  var internal_1 = require("@nx/devkit/internal");
31
31
  Object.defineProperty(exports, "createLockFile", { enumerable: true, get: function () { return internal_1.createLockFile; } });
32
+ Object.defineProperty(exports, "createPackageJson", { enumerable: true, get: function () { return internal_1.createPackageJson; } });
33
+ Object.defineProperty(exports, "generatePrunedDeployOutput", { enumerable: true, get: function () { return internal_1.generatePrunedDeployOutput; } });
32
34
  Object.defineProperty(exports, "getLockFileName", { enumerable: true, get: function () { return internal_1.getLockFileName; } });
33
- var internal_2 = require("@nx/devkit/internal");
34
- Object.defineProperty(exports, "createPackageJson", { enumerable: true, get: function () { return internal_2.createPackageJson; } });
@@ -0,0 +1,14 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ /**
3
+ * Adds the workspace-root pnpm install settings sources to the `inputs` of
4
+ * build targets that emit the pruned pnpm deploy output. The build approvals
5
+ * and `supportedArchitectures` those outputs carry reach no lockfile, so
6
+ * without these a revoked approval leaves the task hash unchanged and a cached
7
+ * run replays an output that still grants it. Inferred targets are covered by
8
+ * the `@nx/webpack` and `@nx/rspack` plugins themselves; this migration covers
9
+ * the explicitly configured executor targets, plus overlays of inferred
10
+ * targets (a project-level entry, under the exact name or a glob-pattern key
11
+ * matching it, or a matching `targetDefaults` entry) whose replacing `inputs`
12
+ * array discards the plugin-generated one.
13
+ */
14
+ export default function update(tree: Tree): Promise<void>;
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = update;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const internal_1 = require("@nx/devkit/internal");
6
+ const path_1 = require("path");
7
+ const pnpm_deploy_output_cache_inputs_1 = require("../../utils/pnpm-deploy-output-cache-inputs");
8
+ const target_defaults_matching_1 = require("../../utils/target-defaults-matching");
9
+ // The executors that emit the pruned pnpm deploy output (lockfile,
10
+ // settings-only pnpm-workspace.yaml, patches, vendored local paths), each with
11
+ // the option that turns the emission on. webpack, rspack, vite and esbuild
12
+ // gate it on `generatePackageJson`; next, remix, tsc and swc emit the manifest
13
+ // regardless and gate only the deploy output on `generateLockfile`.
14
+ const DEPLOY_OUTPUT_EXECUTORS = {
15
+ '@nx/webpack:webpack': 'generatePackageJson',
16
+ '@nx/rspack:rspack': 'generatePackageJson',
17
+ '@nx/vite:build': 'generatePackageJson',
18
+ '@nx/esbuild:esbuild': 'generatePackageJson',
19
+ '@nx/next:build': 'generateLockfile',
20
+ '@nx/remix:build': 'generateLockfile',
21
+ '@nx/js:tsc': 'generateLockfile',
22
+ '@nx/js:swc': 'generateLockfile',
23
+ };
24
+ // The config-file plugins whose inferred build target can emit the deploy
25
+ // output (NxAppWebpackPlugin/NxAppRspackPlugin with generatePackageJson).
26
+ // The globs mirror each plugin's own createNodes pattern; the commands are
27
+ // what each plugin sets on the build target it creates.
28
+ const INFERRED_DEPLOY_PLUGINS = {
29
+ '@nx/webpack/plugin': {
30
+ configGlob: '**/webpack.config.{js,ts,mjs,cjs}',
31
+ command: 'webpack-cli build',
32
+ },
33
+ '@nx/rspack/plugin': {
34
+ configGlob: '**/rspack.config.{js,ts,mjs,mts,cjs,cts}',
35
+ command: 'rspack build',
36
+ },
37
+ };
38
+ /**
39
+ * Adds the workspace-root pnpm install settings sources to the `inputs` of
40
+ * build targets that emit the pruned pnpm deploy output. The build approvals
41
+ * and `supportedArchitectures` those outputs carry reach no lockfile, so
42
+ * without these a revoked approval leaves the task hash unchanged and a cached
43
+ * run replays an output that still grants it. Inferred targets are covered by
44
+ * the `@nx/webpack` and `@nx/rspack` plugins themselves; this migration covers
45
+ * the explicitly configured executor targets, plus overlays of inferred
46
+ * targets (a project-level entry, under the exact name or a glob-pattern key
47
+ * matching it, or a matching `targetDefaults` entry) whose replacing `inputs`
48
+ * array discards the plugin-generated one.
49
+ */
50
+ async function update(tree) {
51
+ // The deploy output's install settings are pnpm-specific; other package
52
+ // managers' outputs hash everything they are built from already.
53
+ if (!tree.exists('pnpm-lock.yaml')) {
54
+ return;
55
+ }
56
+ const nxJson = (0, devkit_1.readNxJson)(tree);
57
+ const targetDefaults = nxJson?.targetDefaults;
58
+ const projects = new Map();
59
+ const changedProjects = new Set();
60
+ let defaultsChanged = false;
61
+ for (const [projectName, project] of (0, devkit_1.getProjects)(tree)) {
62
+ projects.set(projectName, project);
63
+ const projectNode = {
64
+ name: projectName,
65
+ type: 'lib',
66
+ data: { root: project.root, tags: project.tags },
67
+ };
68
+ for (const [targetName, target] of Object.entries(project.targets ?? {})) {
69
+ // The executor may come from a matching targetDefaults entry rather
70
+ // than the target itself. A `command` target resolves to nx:run-commands
71
+ // before defaults apply, so a default can never re-identify it.
72
+ const executor = target.executor ??
73
+ (target.command
74
+ ? 'nx:run-commands'
75
+ : (0, target_defaults_matching_1.resolveDefaultsExecutor)(targetName, projectName, projectNode, targetDefaults));
76
+ if (!executor || !DEPLOY_OUTPUT_EXECUTORS[executor]) {
77
+ continue;
78
+ }
79
+ const ref = {
80
+ targetName,
81
+ projectName,
82
+ projectNode,
83
+ matcherExecutor: target.executor,
84
+ target,
85
+ };
86
+ const selectedKey = targetDefaults
87
+ ? (0, target_defaults_matching_1.selectDefaultsKey)(ref, targetDefaults, executor)
88
+ : null;
89
+ const entries = (0, target_defaults_matching_1.compatibleDefaultsEntries)(ref, selectedKey, targetDefaults);
90
+ const defaults = entries.length
91
+ ? (0, internal_1.readTargetDefaultsForTarget)(targetName, { [selectedKey]: entries }, ref.matcherExecutor, { projectName, projectNode })
92
+ : null;
93
+ // The effective view, not the target's own: the gating option may be
94
+ // supplied by a matching defaults entry or set only in a configuration.
95
+ const effective = (0, internal_1.mergeTargetConfigurations)(target, defaults ?? {});
96
+ if (!emitsDeployOutput(effective, DEPLOY_OUTPUT_EXECUTORS[executor])) {
97
+ continue;
98
+ }
99
+ const changed = (0, pnpm_deploy_output_cache_inputs_1.addPnpmDeployOutputCacheInputs)(ref, targetDefaults, executor);
100
+ if (changed === 'target') {
101
+ changedProjects.add(projectName);
102
+ }
103
+ else if (changed === 'defaults') {
104
+ defaultsChanged = true;
105
+ }
106
+ }
107
+ }
108
+ // Overlays of plugin-inferred build targets carry no executor, so the loop
109
+ // above cannot see them, yet a replacing `inputs` array in one discards the
110
+ // settings inputs the plugin generates. The plugin adds those inputs without
111
+ // knowing whether the config enables generatePackageJson (that lives inside
112
+ // the user's config file), so the overlay repair is equally unconditional.
113
+ // It appends the runtime probe even where the plugin omits it under a valid
114
+ // pnpm pin: the repair runs once and the pin can be removed later.
115
+ for (const ref of await inferredDeployTargetOverlayRefs(tree, nxJson, projects)) {
116
+ const changed = (0, pnpm_deploy_output_cache_inputs_1.addPnpmDeployOutputCacheInputsToInferredTargetOverlay)(ref, targetDefaults);
117
+ if (changed === 'target') {
118
+ changedProjects.add(ref.projectName);
119
+ }
120
+ else if (changed === 'defaults') {
121
+ defaultsChanged = true;
122
+ }
123
+ }
124
+ for (const projectName of changedProjects) {
125
+ (0, devkit_1.updateProjectConfiguration)(tree, projectName, projects.get(projectName));
126
+ }
127
+ if (defaultsChanged) {
128
+ (0, devkit_1.updateNxJson)(tree, nxJson);
129
+ }
130
+ await (0, devkit_1.formatFiles)(tree);
131
+ }
132
+ /**
133
+ * One ref per (project, inferred build target) the registered `@nx/webpack` or
134
+ * `@nx/rspack` config-file plugins would create, resolved the way the plugins
135
+ * do: their createNodes glob filtered by each registration's include/exclude,
136
+ * the config file's directory as the project root, and the registration's
137
+ * `buildTargetName` option. The overlaying project-level entry may sit under
138
+ * the exact target name or a glob-pattern key matching it; one that replaces
139
+ * what the target runs disqualifies the target instead.
140
+ */
141
+ async function inferredDeployTargetOverlayRefs(tree, nxJson, projects) {
142
+ const rootToProject = new Map();
143
+ for (const [projectName, project] of projects) {
144
+ rootToProject.set(project.root, projectName);
145
+ }
146
+ const refs = [];
147
+ for (const registration of nxJson?.plugins ?? []) {
148
+ const pluginName = typeof registration === 'string' ? registration : registration.plugin;
149
+ const inferredPlugin = INFERRED_DEPLOY_PLUGINS[pluginName];
150
+ if (!inferredPlugin) {
151
+ continue;
152
+ }
153
+ const options = typeof registration === 'string'
154
+ ? undefined
155
+ : registration.options;
156
+ const targetName = options?.buildTargetName ?? 'build';
157
+ const configFiles = (0, internal_1.findMatchingConfigFiles)(await (0, devkit_1.globAsync)(tree, [inferredPlugin.configGlob]), typeof registration === 'string' ? undefined : registration.include, typeof registration === 'string' ? undefined : registration.exclude);
158
+ for (const configFile of configFiles) {
159
+ const projectName = rootToProject.get((0, path_1.dirname)(configFile));
160
+ if (!projectName) {
161
+ continue;
162
+ }
163
+ const project = projects.get(projectName);
164
+ const target = inferredTargetOverlayEntry(project.targets, targetName);
165
+ // An entry that changes what the target runs is not an overlay; the
166
+ // executor loop covers the deploy executors among those.
167
+ if (target &&
168
+ replacesInferredRunCommandsIdentity(target, inferredPlugin.command, project.root)) {
169
+ continue;
170
+ }
171
+ refs.push({
172
+ targetName,
173
+ projectName,
174
+ projectNode: {
175
+ name: projectName,
176
+ type: 'lib',
177
+ data: { root: project.root, tags: project.tags },
178
+ },
179
+ // the runtime matcher sees the plugin target's effective identity:
180
+ // its `command` payload resolves to nx:run-commands
181
+ matcherExecutor: 'nx:run-commands',
182
+ // the runtime drops the plugin attribution as soon as the project
183
+ // entry authors an executor or command, restated or not, so a
184
+ // plugin-filtered defaults entry no longer applies to the target
185
+ sourcePlugin: target?.executor !== undefined || target?.command !== undefined
186
+ ? undefined
187
+ : pluginName,
188
+ target: target ?? {},
189
+ });
190
+ }
191
+ }
192
+ return refs;
193
+ }
194
+ /**
195
+ * The project-level entry that overlays the inferred `targetName`. Its key is
196
+ * the exact name or a glob pattern the name matches; the inferred target
197
+ * exists before any project-level entry merges, so a matching glob key
198
+ * overlays it too. When several keys match, each entry merges against the
199
+ * inferred target independently and the last write wins, so only the last
200
+ * one applies. A glob key matching an earlier same-file sibling never
201
+ * surfaces here: the project read already collapsed it onto that sibling and
202
+ * dropped the key.
203
+ */
204
+ function inferredTargetOverlayEntry(targets, targetName) {
205
+ const matching = Object.entries(targets ?? {}).filter(([key]) => key === targetName ||
206
+ (0, internal_1.findMatchingTargetNames)(key, [targetName]).length > 0);
207
+ return matching[matching.length - 1]?.[1];
208
+ }
209
+ /**
210
+ * Whether the entry changes what the inferred run-commands target runs when
211
+ * merged, resolved with nx's own merge so `"..."` placement and restated
212
+ * identities land the way the runtime lands them: a merged executor other
213
+ * than nx:run-commands, an effective command differing from the plugin's, or
214
+ * a `command`/`commands` clash the run-commands schema rejects. An entry
215
+ * restating the identity the plugin infers is still an overlay.
216
+ */
217
+ function replacesInferredRunCommandsIdentity(target, inferredCommand, projectRoot) {
218
+ // `command` alongside `executor` fails graph construction outright;
219
+ // there is nothing running to repair.
220
+ if (target.command && target.executor) {
221
+ return true;
222
+ }
223
+ const merged = (0, internal_1.mergeTargetConfigurations)((0, internal_1.resolveCommandSyntacticSugar)(target, projectRoot), { executor: 'nx:run-commands', options: { command: inferredCommand } });
224
+ if (merged.executor !== 'nx:run-commands') {
225
+ return true;
226
+ }
227
+ // Both present fails the run-commands schema's oneOf; the target cannot run.
228
+ if (merged.options?.command && merged.options?.commands) {
229
+ return true;
230
+ }
231
+ const command = merged.options?.command ?? merged.options?.commands?.join(' && ');
232
+ return command !== inferredCommand;
233
+ }
234
+ /**
235
+ * Whether the target ships the deploy output in any of its runs: the gating
236
+ * option can sit in `options` or in any configuration, and `inputs` cannot
237
+ * vary per configuration, so one enabled configuration is enough.
238
+ */
239
+ function emitsDeployOutput(effective, gate) {
240
+ return (Boolean(effective.options?.[gate]) ||
241
+ Object.values(effective.configurations ?? {}).some((configuration) => Boolean(configuration?.[gate])));
242
+ }