@nx/js 23.1.0-beta.3 → 23.1.0-beta.5

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.
@@ -1,5 +1,7 @@
1
1
  import { type ExecutorContext } from '@nx/devkit';
2
+ import { type PackageJson } from 'nx/src/utils/package-json';
2
3
  import { type PruneLockfileOptions } from './schema';
3
4
  export default function pruneLockfileExecutor(schema: PruneLockfileOptions, context: ExecutorContext): Promise<{
4
5
  success: boolean;
5
6
  }>;
7
+ export declare function resolveCatalogReferences(packageJson: PackageJson): PackageJson;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = pruneLockfileExecutor;
4
+ exports.resolveCatalogReferences = resolveCatalogReferences;
4
5
  const devkit_1 = require("@nx/devkit");
6
+ const internal_1 = require("@nx/devkit/internal");
5
7
  const fs_1 = require("fs");
6
8
  const path_1 = require("path");
7
9
  const utils_1 = require("nx/src/tasks-runner/utils");
@@ -13,7 +15,8 @@ const strip_glob_to_base_dir_1 = require("../../utils/strip-glob-to-base-dir");
13
15
  async function pruneLockfileExecutor(schema, context) {
14
16
  devkit_1.logger.log('Pruning lockfile...');
15
17
  const outputDirectory = getOutputDir(schema, context);
16
- const packageJson = getPackageJson(schema, context);
18
+ const packageJson = resolveCatalogReferences(getPackageJson(schema, context));
19
+ mergeAllowScripts(packageJson);
17
20
  const packageManager = (0, devkit_1.detectPackageManager)(devkit_1.workspaceRoot);
18
21
  if (packageManager === 'bun') {
19
22
  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.');
@@ -48,6 +51,55 @@ function createPrunedLockfile(packageJson, graph) {
48
51
  lockFile,
49
52
  };
50
53
  }
54
+ /**
55
+ * npm reads the `allowScripts` install-script allowlist only from the install
56
+ * root, but `npm approve-scripts` writes it to the workspace root, so it never
57
+ * lives in the project package.json the prune output is built from. Carry the
58
+ * root allowlist over, with project-level entries preserved and winning on
59
+ * conflict. Mirrors the `pnpm.allowBuilds` handling in createPackageJson.
60
+ */
61
+ function mergeAllowScripts(packageJson) {
62
+ const rootPackageJson = (0, devkit_1.readJsonFile)((0, path_1.join)(devkit_1.workspaceRoot, 'package.json'));
63
+ if (!rootPackageJson.allowScripts) {
64
+ return;
65
+ }
66
+ packageJson.allowScripts = {
67
+ ...rootPackageJson.allowScripts,
68
+ ...packageJson.allowScripts,
69
+ };
70
+ }
71
+ function resolveCatalogReferences(packageJson) {
72
+ const manager = (0, internal_1.getCatalogManager)(devkit_1.workspaceRoot);
73
+ if (!manager) {
74
+ return packageJson;
75
+ }
76
+ const sections = [
77
+ 'dependencies',
78
+ 'optionalDependencies',
79
+ 'devDependencies',
80
+ 'peerDependencies',
81
+ ];
82
+ const resolved = { ...packageJson };
83
+ for (const section of sections) {
84
+ const deps = packageJson[section];
85
+ if (!deps) {
86
+ continue;
87
+ }
88
+ const resolvedDeps = { ...deps };
89
+ for (const [packageName, version] of Object.entries(deps)) {
90
+ if (!manager.isCatalogReference(version)) {
91
+ continue;
92
+ }
93
+ const resolvedVersion = manager.resolveCatalogReference(devkit_1.workspaceRoot, packageName, version);
94
+ if (!resolvedVersion) {
95
+ throw new Error(`Could not resolve catalog reference for package ${packageName}@${version}.`);
96
+ }
97
+ resolvedDeps[packageName] = resolvedVersion;
98
+ }
99
+ resolved[section] = resolvedDeps;
100
+ }
101
+ return resolved;
102
+ }
51
103
  function getPackageJson(schema, context) {
52
104
  const target = (0, devkit_1.parseTargetString)(schema.buildTarget, context);
53
105
  const project = context.projectGraph.nodes[target.project].data;
@@ -249,7 +249,5 @@ function updatePackageJson(tree, projectName, projectRoot, main, outputPath, roo
249
249
  function mergeTargetDefaults(tree, project, buildTarget) {
250
250
  const nxJson = (0, devkit_1.readNxJson)(tree);
251
251
  const projectTarget = project.targets[buildTarget];
252
- return (0, devkit_internals_1.mergeTargetConfigurations)(projectTarget, (projectTarget.executor
253
- ? nxJson.targetDefaults?.[projectTarget.executor]
254
- : undefined) ?? nxJson.targetDefaults?.[buildTarget]);
252
+ return (0, devkit_internals_1.mergeTargetConfigurations)(projectTarget, (0, internal_1.readTargetDefaultsForTarget)(buildTarget, nxJson.targetDefaults, projectTarget.executor));
255
253
  }
@@ -35,30 +35,50 @@ async function default_1(tree) {
35
35
  if (!nxJson.targetDefaults) {
36
36
  return;
37
37
  }
38
- for (const [targetOrExecutor, targetConfig] of Object.entries(nxJson.targetDefaults)) {
39
- if (!exports.executors.includes(targetOrExecutor) &&
40
- !exports.executors.includes(targetConfig.executor)) {
41
- continue;
42
- }
43
- if (targetConfig.options) {
44
- delete targetConfig.options.external;
45
- delete targetConfig.options.externalBuildTargets;
46
- if (!Object.keys(targetConfig.options).length) {
47
- delete targetConfig.options;
38
+ const cleanEntry = (entry) => {
39
+ if (entry.options) {
40
+ delete entry.options.external;
41
+ delete entry.options.externalBuildTargets;
42
+ if (!Object.keys(entry.options).length) {
43
+ delete entry.options;
48
44
  }
49
45
  }
50
- Object.entries(targetConfig.configurations ?? {}).forEach(([, config]) => {
46
+ Object.values(entry.configurations ?? {}).forEach((config) => {
51
47
  delete config.external;
52
48
  delete config.externalBuildTargets;
53
49
  });
54
- if (!Object.keys(targetConfig).length ||
55
- (Object.keys(targetConfig).length === 1 &&
56
- Object.keys(targetConfig)[0] === 'executor')) {
57
- delete nxJson.targetDefaults[targetOrExecutor];
50
+ };
51
+ const targetDefaults = nxJson.targetDefaults;
52
+ for (const key of Object.keys(targetDefaults)) {
53
+ const value = targetDefaults[key];
54
+ const entries = Array.isArray(value)
55
+ ? value
56
+ : [value];
57
+ const usesExecutor = (entry) => exports.executors.includes(key) ||
58
+ exports.executors.includes(entry.executor) ||
59
+ exports.executors.includes(entry.filter?.executor);
60
+ const kept = entries.filter((entry) => {
61
+ if (usesExecutor(entry)) {
62
+ cleanEntry(entry);
63
+ }
64
+ // Drop entries left with nothing but their `filter`/`executor` locator.
65
+ return Object.keys(entry).some((k) => k !== 'filter' && k !== 'executor');
66
+ });
67
+ if (kept.length === 0) {
68
+ delete targetDefaults[key];
58
69
  }
59
- if (!Object.keys(nxJson.targetDefaults).length) {
60
- delete nxJson.targetDefaults;
70
+ else if (kept.length === 1 && kept[0].filter === undefined) {
71
+ // A lone unfiltered entry is stored as the plain object form (which
72
+ // omits `filter`).
73
+ const { filter: _filter, ...config } = kept[0];
74
+ targetDefaults[key] = config;
61
75
  }
76
+ else {
77
+ targetDefaults[key] = kept;
78
+ }
79
+ }
80
+ if (Object.keys(targetDefaults).length === 0) {
81
+ delete nxJson.targetDefaults;
62
82
  }
63
83
  (0, devkit_1.updateNxJson)(tree, nxJson);
64
84
  await (0, devkit_1.formatFiles)(tree);
@@ -10,14 +10,19 @@ import { type Tree } from '@nx/devkit';
10
10
  * false, downlevelIteration set to any value).
11
11
  *
12
12
  * 2. default-preserving pass - for every chain root (no "extends" key), pins
13
- * the TS6 compiler-option defaults that flipped to a stricter value back to
14
- * their pre-TS6 value, but only when the root does not set them explicitly:
13
+ * the TS6 compiler-option defaults that changed in a way that breaks existing
14
+ * workspaces back to their pre-TS6 value, but only when the root does not set
15
+ * them explicitly:
15
16
  * - "strict": false - TS6 treats an absent "strict" as true; TS5 as false.
16
17
  * - "noUncheckedSideEffectImports": false - TS6 defaults it to true, which
17
18
  * turns a bare side-effect import of an asset lacking an ambient
18
19
  * declaration (e.g. `import './styles.css'`) into a hard TS2882 error; it
19
20
  * is a semantic diagnostic, not a deprecation, so `ignoreDeprecations`
20
21
  * cannot silence it.
22
+ * - "types": ["*"]. TS6 loads no @types packages when `types` is unset,
23
+ * whereas TS5 loaded them all; the "*" wildcard restores that default so
24
+ * a config relying on it (e.g. ts-node type-checking jest.config.ts)
25
+ * keeps finding @types/node.
21
26
  * Files with "extends" inherit from their chain root and are left untouched.
22
27
  * Pure solution-style containers (root has `"files": []` and no "include")
23
28
  * select no source files, so pinning there is noise and they are skipped.
@@ -18,14 +18,19 @@ const FORMATTING_OPTIONS = {
18
18
  * false, downlevelIteration set to any value).
19
19
  *
20
20
  * 2. default-preserving pass - for every chain root (no "extends" key), pins
21
- * the TS6 compiler-option defaults that flipped to a stricter value back to
22
- * their pre-TS6 value, but only when the root does not set them explicitly:
21
+ * the TS6 compiler-option defaults that changed in a way that breaks existing
22
+ * workspaces back to their pre-TS6 value, but only when the root does not set
23
+ * them explicitly:
23
24
  * - "strict": false - TS6 treats an absent "strict" as true; TS5 as false.
24
25
  * - "noUncheckedSideEffectImports": false - TS6 defaults it to true, which
25
26
  * turns a bare side-effect import of an asset lacking an ambient
26
27
  * declaration (e.g. `import './styles.css'`) into a hard TS2882 error; it
27
28
  * is a semantic diagnostic, not a deprecation, so `ignoreDeprecations`
28
29
  * cannot silence it.
30
+ * - "types": ["*"]. TS6 loads no @types packages when `types` is unset,
31
+ * whereas TS5 loaded them all; the "*" wildcard restores that default so
32
+ * a config relying on it (e.g. ts-node type-checking jest.config.ts)
33
+ * keeps finding @types/node.
29
34
  * Files with "extends" inherit from their chain root and are left untouched.
30
35
  * Pure solution-style containers (root has `"files": []` and no "include")
31
36
  * select no source files, so pinning there is noise and they are skipped.
@@ -52,7 +57,7 @@ async function default_1(tree) {
52
57
  devkit_1.logger.info(`Added "ignoreDeprecations": "6.0" to ${deprecationCount} tsconfig file(s) carrying TS6-deprecated options.`);
53
58
  }
54
59
  if (defaultsPinCount > 0) {
55
- devkit_1.logger.info(`Pinned pre-TS6 compiler option defaults ("strict", "noUncheckedSideEffectImports") on ${defaultsPinCount} tsconfig chain root(s) to preserve existing behavior.`);
60
+ devkit_1.logger.info(`Pinned pre-TS6 compiler option defaults ("strict", "noUncheckedSideEffectImports", "types") on ${defaultsPinCount} tsconfig chain root(s) to preserve existing behavior.`);
56
61
  }
57
62
  await (0, devkit_1.formatFiles)(tree);
58
63
  }
@@ -137,12 +142,15 @@ function hasDeprecatedValue(compilerOptions) {
137
142
  }
138
143
  return false;
139
144
  }
140
- // TS6 compiler-option defaults that flipped to a stricter value. We pin each
141
- // back to its pre-TS6 value on chain roots that don't set it, so existing
142
- // workspaces keep building without adopting a legit TS6 setup.
145
+ // TS6 compiler-option defaults that changed in a way that breaks existing
146
+ // workspaces. We pin each back to its pre-TS6 value on chain roots that don't
147
+ // set it, so existing workspaces keep building without adopting a legit TS6
148
+ // setup.
143
149
  const DEFAULT_PRESERVING_PINS = [
144
150
  ['strict', false],
145
151
  ['noUncheckedSideEffectImports', false],
152
+ // TS6 loads no @types when `types` is unset (TS5 loaded all); "*" restores it.
153
+ ['types', ['*']],
146
154
  ];
147
155
  function pinPreTs6Defaults(tree, tsconfigPath) {
148
156
  const original = tree.read(tsconfigPath, 'utf-8');
@@ -177,7 +185,7 @@ function pinPreTs6Defaults(tree, tsconfigPath) {
177
185
  let contents = original;
178
186
  let changed = false;
179
187
  for (const [key, value] of DEFAULT_PRESERVING_PINS) {
180
- // An explicit value (true or false) means the user opted in - leave it.
188
+ // An explicit value means the user opted in; leave it.
181
189
  if (key in compilerOptions) {
182
190
  continue;
183
191
  }
@@ -3,7 +3,7 @@
3
3
  TypeScript 6 turns several long-deprecated compiler options into hard errors and flips a few option defaults to stricter values. So that an existing workspace keeps compiling on TypeScript 6 without being migrated to a full TypeScript 6 setup, this migration makes two edits to the `tsconfig*.json` files in the workspace:
4
4
 
5
5
  - Adds `"ignoreDeprecations": "6.0"` to any `compilerOptions` (or `ts-node.compilerOptions`) block that directly sets a TypeScript 6 deprecated option - for example `moduleResolution` set to `node`/`node10`/`classic`, `baseUrl`, `target` set to `es5`, `esModuleInterop: false`, `outFile`, `module` set to `amd`/`umd`/`system`/`none`, `alwaysStrict: false`, `allowSyntheticDefaultImports: false`, or `downlevelIteration`.
6
- - Pins `"strict": false` and `"noUncheckedSideEffectImports": false` on every chain-root tsconfig (one without an `extends`) that does not already set them. TypeScript 6 treats an absent `strict` as `true` (it was `false` when unset before) and defaults `noUncheckedSideEffectImports` to `true`, which turns a bare side-effect import such as `import './styles.css'` without an ambient module declaration into an error. Pinning both preserves the pre-TypeScript 6 behavior.
6
+ - Pins `"strict": false`, `"noUncheckedSideEffectImports": false`, and `"types": ["*"]` on every chain-root tsconfig (one without an `extends`) that does not already set them. TypeScript 6 treats an absent `strict` as `true` (it was `false` when unset before), defaults `noUncheckedSideEffectImports` to `true` (which turns a bare side-effect import such as `import './styles.css'` without an ambient module declaration into an error), and no longer auto-loads every `@types` package when `types` is unset the way TypeScript 5 did. The `"*"` wildcard restores that last default. Pinning all three preserves the pre-TypeScript 6 behavior.
7
7
 
8
8
  Files that use `extends` inherit these settings from their chain root and are left untouched, and pure solution-style tsconfigs (`"files": []` with no `include`) are skipped. The migration only runs when the workspace is on TypeScript 6.
9
9
 
@@ -23,7 +23,7 @@ Files that use `extends` inherit these settings from their chain root and are le
23
23
 
24
24
  ##### After
25
25
 
26
- ```json title="tsconfig.json" {6-8}
26
+ ```json title="tsconfig.json" {6-9}
27
27
  {
28
28
  "compilerOptions": {
29
29
  "target": "es5",
@@ -31,7 +31,8 @@ Files that use `extends` inherit these settings from their chain root and are le
31
31
  "moduleResolution": "bundler",
32
32
  "ignoreDeprecations": "6.0",
33
33
  "strict": false,
34
- "noUncheckedSideEffectImports": false
34
+ "noUncheckedSideEffectImports": false,
35
+ "types": ["*"]
35
36
  }
36
37
  }
37
38
  ```
@@ -282,7 +282,15 @@ function updatePaths(dependencies, paths) {
282
282
  const { root } = dep.node.data;
283
283
  // Update nested mappings to point to the dependency's output paths
284
284
  mappedPaths = mappedPaths.concat(paths[path].flatMap((p) => dep.outputs.flatMap((output) => {
285
- const basePath = p.replace(root, output);
285
+ // Re-map the root prefix to the output. Match root only as a
286
+ // leading segment (after an optional `./`) so a root that
287
+ // also appears later in the value (e.g. output `dist/libs/base`
288
+ // for root `base`) isn't doubled.
289
+ const dotPrefix = p.startsWith('./') ? './' : '';
290
+ const value = dotPrefix ? p.slice(2) : p;
291
+ const basePath = value === root || value.startsWith(`${root}/`)
292
+ ? `${dotPrefix}${output}${value.slice(root.length)}`
293
+ : p;
286
294
  return [
287
295
  // extension-less path to support compiled output
288
296
  basePath.replace(new RegExp(`${(0, path_1.extname)(basePath)}$`, 'gi'), ''),
@@ -44,7 +44,10 @@ function getTypeCheckOptions(normalizedOptions) {
44
44
  };
45
45
  if (watch) {
46
46
  typeCheckOptions.incremental = true;
47
- typeCheckOptions.cacheDir = devkit_1.cacheDir;
47
+ // Scope the incremental .tsbuildinfo per project (in its own subdir, not
48
+ // alongside Nx's cache files) so concurrent watch type-checks don't collide
49
+ // on a single file.
50
+ typeCheckOptions.cacheDir = (0, node_path_1.join)(devkit_1.cacheDir, 'swc', projectRoot);
48
51
  }
49
52
  if (normalizedOptions.isTsSolutionSetup && normalizedOptions.skipTypeCheck) {
50
53
  typeCheckOptions.ignoreDiagnostics = true;
@@ -43,6 +43,10 @@ async function runTypeCheck(options) {
43
43
  options: {
44
44
  ...compilerOptions,
45
45
  incremental: true,
46
+ // Set after the spread so it overrides any user-set tsBuildInfoFile.
47
+ // This is a dedicated type-check program with Nx-injected options that
48
+ // differ from the real build, so its build info must not share a file
49
+ // with the build, or it corrupts the build's incremental cache.
46
50
  tsBuildInfoFile: path.join(cacheDir, '.tsbuildinfo'),
47
51
  },
48
52
  });
package/migrations.json CHANGED
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "23-1-0-add-ignore-deprecations-for-ts6": {
33
33
  "version": "23.1.0-beta.0",
34
- "description": "Adds `\"ignoreDeprecations\": \"6.0\"` to tsconfig files whose compilerOptions (or ts-node.compilerOptions) directly carry a TypeScript 6 deprecated option value (e.g. moduleResolution node/node10/classic, baseUrl, target es5, esModuleInterop false, outFile, module amd/umd/system/none, alwaysStrict false, allowSyntheticDefaultImports false, downlevelIteration set). Also pins `\"strict\": false` and `\"noUncheckedSideEffectImports\": false` in chain-root tsconfigs (no \"extends\") that lack each key, preserving pre-TS6 behavior where their defaults were less strict.",
34
+ "description": "Adds `\"ignoreDeprecations\": \"6.0\"` to tsconfig files whose compilerOptions (or ts-node.compilerOptions) directly carry a TypeScript 6 deprecated option value (e.g. moduleResolution node/node10/classic, baseUrl, target es5, esModuleInterop false, outFile, module amd/umd/system/none, alwaysStrict false, allowSyntheticDefaultImports false, downlevelIteration set). Also pins `\"strict\": false`, `\"noUncheckedSideEffectImports\": false`, and `\"types\": [\"*\"]` in chain-root tsconfigs (no \"extends\") that lack each key, preserving pre-TS6 behavior where these defaults were stricter or, for `types`, stopped auto-loading all @types packages.",
35
35
  "requires": {
36
36
  "typescript": ">=6.0.0"
37
37
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/js",
3
- "version": "23.1.0-beta.3",
3
+ "version": "23.1.0-beta.5",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "files": [
@@ -144,11 +144,11 @@
144
144
  "source-map-support": "0.5.19",
145
145
  "tinyglobby": "^0.2.12",
146
146
  "tslib": "^2.3.0",
147
- "@nx/devkit": "23.1.0-beta.3",
148
- "@nx/workspace": "23.1.0-beta.3"
147
+ "@nx/devkit": "23.1.0-beta.5",
148
+ "@nx/workspace": "23.1.0-beta.5"
149
149
  },
150
150
  "devDependencies": {
151
- "nx": "23.1.0-beta.3"
151
+ "nx": "23.1.0-beta.5"
152
152
  },
153
153
  "peerDependencies": {
154
154
  "@swc/cli": ">=0.6.0 <0.9.0",