nx 23.1.0-beta.1 → 23.1.0-beta.3

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.
@@ -80,9 +80,10 @@ function buildScopeRules(mode) {
80
80
  return [
81
81
  `<scope_rules>`,
82
82
  `- Apply only the changes the migration prompt asks for.`,
83
- `- Do not refactor, reformat, or update dependencies beyond what the migration prompt directs.`,
83
+ `- Do not refactor or update dependencies beyond what the migration prompt directs, and do not reformat files you did not change.`,
84
+ `- After applying your changes and before writing the handoff, format the files you created or modified so they match the workspace's style. If the workspace uses Prettier, run \`nx format:write\`, which formats your uncommitted changes; if it has no formatter configured, skip this.`,
84
85
  `- Do not modify files outside the workspace root.`,
85
- `- Do not run other \`nx\` commands that mutate workspace state (\`nx migrate\`, \`nx reset\`, \`nx run-many\`, generators, etc.). Read-only inspection (\`nx show\`, \`nx graph --file\`, reading files) is fine.`,
86
+ `- Do not run other \`nx\` commands that mutate workspace state (\`nx migrate\`, \`nx reset\`, \`nx run-many\`, generators, etc.), except \`nx format:write\` to format the files you changed. Read-only inspection (\`nx show\`, \`nx graph --file\`, reading files) is fine.`,
86
87
  `- If the migration instructions are unclear, internally inconsistent, or conflict with the current workspace state, ask the user for direction (see the handoff contract). Do not guess.`,
87
88
  `</scope_rules>`,
88
89
  ].join('\n');
@@ -172,6 +172,7 @@ export declare function createFetcher(pmc: PackageManagerCommands): ((pkg: strin
172
172
  stats?: MigrateFetchStats;
173
173
  };
174
174
  export { filterDowngradedUpdates };
175
+ export declare function generateMigrationsJsonAndUpdatePackageJson(root: string, opts: GenerateMigrations, fetch?: MigratorOptions['fetch']): Promise<void>;
175
176
  /**
176
177
  * Detects npm peer-dependency resolution failures. Keyed on the `ERESOLVE`
177
178
  * error code, which npm consistently emits for this class of failure across
@@ -6,6 +6,7 @@ exports.resolveCanonicalNxPackage = resolveCanonicalNxPackage;
6
6
  exports.resolveInclude = resolveInclude;
7
7
  exports.parseMigrationsOptions = parseMigrationsOptions;
8
8
  exports.createFetcher = createFetcher;
9
+ exports.generateMigrationsJsonAndUpdatePackageJson = generateMigrationsJsonAndUpdatePackageJson;
9
10
  exports.isNpmPeerDepsError = isNpmPeerDepsError;
10
11
  exports.resolveAgenticRunId = resolveAgenticRunId;
11
12
  exports.formatSkippedPromptsNextStep = formatSkippedPromptsNextStep;
@@ -1347,6 +1348,7 @@ function readNxVersion(packageJson, root) {
1347
1348
  (0, package_json_1.getDependencyVersionFromPackageJson)('@nx/workspace', root, packageJson) ??
1348
1349
  (0, package_json_1.getDependencyVersionFromPackageJson)('@nrwl/workspace', root, packageJson));
1349
1350
  }
1351
+ // Exported for testing the optional-include orchestration seam (see NXC-4590).
1350
1352
  async function generateMigrationsJsonAndUpdatePackageJson(root, opts, fetch) {
1351
1353
  const pmc = (0, package_manager_1.getPackageManagerCommand)();
1352
1354
  let phase = 'fetch_migrations';
@@ -1417,7 +1419,11 @@ async function generateMigrationsJsonAndUpdatePackageJson(root, opts, fetch) {
1417
1419
  const writableUpdates = (0, update_filters_1.filterDowngradedUpdates)(packageUpdates, (0, catalog_1.resolveCatalogSpecifiers)(originalPackageJson), installedPackageVersions);
1418
1420
  const wrotePackageJson = await updatePackageJson(root, writableUpdates);
1419
1421
  const wroteNxJsonInstallation = await updateInstallationDetails(root, writableUpdates);
1420
- const promptMigrationFiles = (0, prompt_files_1.writePromptMigrationFiles)(root, migrations, promptContents ?? {}, packageUpdates[walkedTargetPackage].version);
1422
+ // Under `--include=optional` the target's own entry is filtered out of
1423
+ // `packageUpdates` (it's a required package), so resolve the version
1424
+ // defensively. Also reused by the completion analytics below.
1425
+ const resolvedTargetVersion = packageUpdates[walkedTargetPackage]?.version ?? opts.targetVersion;
1426
+ const promptMigrationFiles = (0, prompt_files_1.writePromptMigrationFiles)(root, migrations, promptContents ?? {}, resolvedTargetVersion);
1421
1427
  if (migrations.length > 0) {
1422
1428
  await createMigrationsFile(root, [
1423
1429
  ...addSplitConfigurationMigrationIfAvailable(from, writableUpdates),
@@ -1429,7 +1435,6 @@ async function generateMigrationsJsonAndUpdatePackageJson(root, opts, fetch) {
1429
1435
  : include === 'optional'
1430
1436
  ? `- Processed optional dependency updates only (skipped required package updates).`
1431
1437
  : null;
1432
- const resolvedTargetVersion = packageUpdates[walkedTargetPackage]?.version ?? opts.targetVersion;
1433
1438
  // The param expressions below evaluate before the report function is
1434
1439
  // entered; `safeReport` keeps them inside the analytics boundary so a
1435
1440
  // param-building throw can't surface here and convert an already
Binary file
@@ -35,22 +35,43 @@ export declare function forceRegisterEsmLoader(): void;
35
35
  * the importing module is itself TypeScript, and the specifier is relative, so
36
36
  * it never hijacks resolution that would otherwise succeed.
37
37
  *
38
+ * Used only as the fallback for runtimes without `module.registerHooks()`
39
+ * (Node < 22.15 / < 23.5); newer runtimes register the in-thread synchronous
40
+ * twin `nodeNextEsmResolveHook` instead. Keep the two in sync.
41
+ *
38
42
  * Exported so the hook can be exercised directly in unit tests.
39
43
  */
40
44
  export declare const NODENEXT_ESM_RESOLVER_SOURCE = "\nconst EXT_FALLBACK = { '.js': ['.ts', '.tsx'], '.mjs': ['.mts'], '.cjs': ['.cts'] };\nconst TS_PARENT_RE = /\\.(?:ts|tsx|mts|cts)(?:$|\\?)/;\nexport async function resolve(specifier, context, nextResolve) {\n try {\n return await nextResolve(specifier, context);\n } catch (err) {\n if (err?.code !== 'ERR_MODULE_NOT_FOUND') throw err;\n const parent = context.parentURL;\n if (!parent || !TS_PARENT_RE.test(parent)) throw err;\n if (!(specifier.startsWith('./') || specifier.startsWith('../') || specifier.startsWith('file:'))) throw err;\n const m = specifier.match(/(\\.(?:js|mjs|cjs))($|\\?)/);\n if (!m) throw err;\n const fallbacks = EXT_FALLBACK[m[1]];\n if (!fallbacks) throw err;\n const base = specifier.slice(0, m.index);\n const suffix = specifier.slice(m.index + m[1].length);\n for (const ext of fallbacks) {\n try { return await nextResolve(base + ext + suffix, context); } catch {}\n }\n throw err;\n }\n}\n";
41
45
  /**
42
- * Register a self-contained ESM resolution hook (via `Module.register`) that
43
- * rewrites TypeScript NodeNext-style `.js`/`.mjs`/`.cjs` relative specifiers to
44
- * their `.ts`/`.mts`/`.cts` sources. This is the ESM counterpart to
45
- * `ensureCjsResolverPatched`: Node's native type stripping loads the `.ts`
46
- * file, but neither native strip nor Node's ESM resolver rewrites the
47
- * extension, so `import './foo.js'` from a `.ts` source where only `foo.ts`
48
- * exists fails with ERR_MODULE_NOT_FOUND without it.
46
+ * Synchronous in-thread twin of `NODENEXT_ESM_RESOLVER_SOURCE` for
47
+ * `module.registerHooks()`: `nextResolve` throws synchronously rather than
48
+ * rejecting, so this uses try/catch instead of `await`. Keep the two in sync.
49
+ * Exported for unit tests.
50
+ */
51
+ export declare function nodeNextEsmResolveHook(specifier: string, context: {
52
+ parentURL?: string;
53
+ }, nextResolve: (specifier: string, context?: unknown) => {
54
+ url: string;
55
+ }): {
56
+ url: string;
57
+ };
58
+ /**
59
+ * Register an ESM resolution hook that rewrites TypeScript NodeNext-style
60
+ * `.js`/`.mjs`/`.cjs` relative specifiers to their `.ts`/`.mts`/`.cts` sources.
61
+ * This is the ESM counterpart to `ensureCjsResolverPatched`: Node's native type
62
+ * stripping loads the `.ts` file, but neither native strip nor Node's ESM
63
+ * resolver rewrites the extension, so `import './foo.js'` from a `.ts` source
64
+ * where only `foo.ts` exists fails with ERR_MODULE_NOT_FOUND without it.
49
65
  *
50
- * The hook is inlined as a `data:` module (see `NODENEXT_ESM_RESOLVER_SOURCE`)
51
- * and relies only on Node's default resolver, so it needs no ts-node/swc-node.
66
+ * Prefers `module.registerHooks()` (Node 22.15+ / 23.5+), passing the in-thread
67
+ * `nodeNextEsmResolveHook`. Falls back to `module.register()` with the inlined
68
+ * `data:` module (`NODENEXT_ESM_RESOLVER_SOURCE`) on older runtimes that lack
69
+ * `registerHooks` - `module.register()` emits a runtime DeprecationWarning
70
+ * (DEP0205) on Node 25.9+/26+, so it is only used when nothing better exists.
71
+ * Either way the hook relies only on Node's default resolver - no
72
+ * ts-node/swc-node.
52
73
  *
53
- * Idempotent and best-effort: a no-op when `Module.register` is unavailable,
74
+ * Idempotent and best-effort: a no-op when no registration API is available,
54
75
  * when a TypeScript transpiler is already preloaded (see
55
76
  * `isTsTranspilerPreloaded`), or if registration fails.
56
77
  */
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NODENEXT_ESM_RESOLVER_SOURCE = void 0;
4
4
  exports.forceRegisterEsmLoader = forceRegisterEsmLoader;
5
+ exports.nodeNextEsmResolveHook = nodeNextEsmResolveHook;
5
6
  exports.ensureNodeNextEsmResolverRegistered = ensureNodeNextEsmResolverRegistered;
6
7
  exports.ensureCjsResolverPatched = ensureCjsResolverPatched;
7
8
  exports.isNativeStripPreferred = isNativeStripPreferred;
@@ -100,6 +101,10 @@ function ensureEsmLoaderRegistered(opts) {
100
101
  * the importing module is itself TypeScript, and the specifier is relative, so
101
102
  * it never hijacks resolution that would otherwise succeed.
102
103
  *
104
+ * Used only as the fallback for runtimes without `module.registerHooks()`
105
+ * (Node < 22.15 / < 23.5); newer runtimes register the in-thread synchronous
106
+ * twin `nodeNextEsmResolveHook` instead. Keep the two in sync.
107
+ *
103
108
  * Exported so the hook can be exercised directly in unit tests.
104
109
  */
105
110
  exports.NODENEXT_ESM_RESOLVER_SOURCE = `
@@ -126,20 +131,70 @@ export async function resolve(specifier, context, nextResolve) {
126
131
  }
127
132
  }
128
133
  `;
134
+ const NODENEXT_EXT_FALLBACK = {
135
+ '.js': ['.ts', '.tsx'],
136
+ '.mjs': ['.mts'],
137
+ '.cjs': ['.cts'],
138
+ };
139
+ const NODENEXT_TS_PARENT_RE = /\.(?:ts|tsx|mts|cts)(?:$|\?)/;
140
+ /**
141
+ * Synchronous in-thread twin of `NODENEXT_ESM_RESOLVER_SOURCE` for
142
+ * `module.registerHooks()`: `nextResolve` throws synchronously rather than
143
+ * rejecting, so this uses try/catch instead of `await`. Keep the two in sync.
144
+ * Exported for unit tests.
145
+ */
146
+ function nodeNextEsmResolveHook(specifier, context, nextResolve) {
147
+ try {
148
+ return nextResolve(specifier, context);
149
+ }
150
+ catch (err) {
151
+ if (err?.code !== 'ERR_MODULE_NOT_FOUND')
152
+ throw err;
153
+ const parent = context.parentURL;
154
+ if (!parent || !NODENEXT_TS_PARENT_RE.test(parent))
155
+ throw err;
156
+ if (!(specifier.startsWith('./') ||
157
+ specifier.startsWith('../') ||
158
+ specifier.startsWith('file:'))) {
159
+ throw err;
160
+ }
161
+ const m = specifier.match(/(\.(?:js|mjs|cjs))($|\?)/);
162
+ if (!m)
163
+ throw err;
164
+ const fallbacks = NODENEXT_EXT_FALLBACK[m[1]];
165
+ if (!fallbacks)
166
+ throw err;
167
+ const base = specifier.slice(0, m.index);
168
+ const suffix = specifier.slice(m.index + m[1].length);
169
+ for (const ext of fallbacks) {
170
+ try {
171
+ return nextResolve(base + ext + suffix, context);
172
+ }
173
+ catch {
174
+ // try the next fallback
175
+ }
176
+ }
177
+ throw err;
178
+ }
179
+ }
129
180
  let nodeNextEsmResolverRegistered = false;
130
181
  /**
131
- * Register a self-contained ESM resolution hook (via `Module.register`) that
132
- * rewrites TypeScript NodeNext-style `.js`/`.mjs`/`.cjs` relative specifiers to
133
- * their `.ts`/`.mts`/`.cts` sources. This is the ESM counterpart to
134
- * `ensureCjsResolverPatched`: Node's native type stripping loads the `.ts`
135
- * file, but neither native strip nor Node's ESM resolver rewrites the
136
- * extension, so `import './foo.js'` from a `.ts` source where only `foo.ts`
137
- * exists fails with ERR_MODULE_NOT_FOUND without it.
182
+ * Register an ESM resolution hook that rewrites TypeScript NodeNext-style
183
+ * `.js`/`.mjs`/`.cjs` relative specifiers to their `.ts`/`.mts`/`.cts` sources.
184
+ * This is the ESM counterpart to `ensureCjsResolverPatched`: Node's native type
185
+ * stripping loads the `.ts` file, but neither native strip nor Node's ESM
186
+ * resolver rewrites the extension, so `import './foo.js'` from a `.ts` source
187
+ * where only `foo.ts` exists fails with ERR_MODULE_NOT_FOUND without it.
138
188
  *
139
- * The hook is inlined as a `data:` module (see `NODENEXT_ESM_RESOLVER_SOURCE`)
140
- * and relies only on Node's default resolver, so it needs no ts-node/swc-node.
189
+ * Prefers `module.registerHooks()` (Node 22.15+ / 23.5+), passing the in-thread
190
+ * `nodeNextEsmResolveHook`. Falls back to `module.register()` with the inlined
191
+ * `data:` module (`NODENEXT_ESM_RESOLVER_SOURCE`) on older runtimes that lack
192
+ * `registerHooks` - `module.register()` emits a runtime DeprecationWarning
193
+ * (DEP0205) on Node 25.9+/26+, so it is only used when nothing better exists.
194
+ * Either way the hook relies only on Node's default resolver - no
195
+ * ts-node/swc-node.
141
196
  *
142
- * Idempotent and best-effort: a no-op when `Module.register` is unavailable,
197
+ * Idempotent and best-effort: a no-op when no registration API is available,
143
198
  * when a TypeScript transpiler is already preloaded (see
144
199
  * `isTsTranspilerPreloaded`), or if registration fails.
145
200
  */
@@ -148,16 +203,16 @@ function ensureNodeNextEsmResolverRegistered() {
148
203
  return;
149
204
  nodeNextEsmResolverRegistered = true;
150
205
  const module = require('node:module');
151
- if (typeof module.register !== 'function')
152
- return;
153
206
  // Skip when a transpiler was preloaded via `--require`/`--import` (e.g.
154
207
  // `--require ts-node/register`, which Nx uses only when it runs from `.ts`
155
- // source). `module.register()` spins up a loader-hook worker thread on which
156
- // Node re-runs those preloads, resolved relative to the *current* working
157
- // directory - and Nx plugin workers `chdir()` into the analyzed workspace
158
- // first. If that workspace can't resolve the preloaded module, the loader
159
- // worker throws and can leave module resolution in a bad state, so we must
160
- // avoid the call entirely; catching it is not a clean recovery.
208
+ // source). The `module.register()` fallback below spins up a loader-hook
209
+ // worker thread on which Node re-runs those preloads, resolved relative to
210
+ // the *current* working directory - and Nx plugin workers `chdir()` into the
211
+ // analyzed workspace first. If that workspace can't resolve the preloaded
212
+ // module, the loader worker throws and can leave module resolution in a bad
213
+ // state, so we must avoid the call entirely; catching it is not a clean
214
+ // recovery. `module.registerHooks()` runs in-thread with no such worker, but
215
+ // we keep the skip uniform so resolver coverage doesn't vary by Node version.
161
216
  //
162
217
  // Consequence: in that from-`.ts`-source invocation a `type: module` plugin
163
218
  // using NodeNext `.js` specifiers won't get this resolver (a preloaded
@@ -165,6 +220,20 @@ function ensureNodeNextEsmResolverRegistered() {
165
220
  // unaffected - its workers run compiled `.js` with no preload.
166
221
  if (isTsTranspilerPreloaded())
167
222
  return;
223
+ // Synchronous in-thread hooks (Node 22.15+ / 23.5+). Preferred because
224
+ // `module.register()` is deprecated (DEP0205) from Node 25.9+/26+.
225
+ const registerHooks = module.registerHooks;
226
+ if (typeof registerHooks === 'function') {
227
+ try {
228
+ registerHooks.call(module, { resolve: nodeNextEsmResolveHook });
229
+ }
230
+ catch {
231
+ // Best-effort: leave Node's native handling in place rather than failing.
232
+ }
233
+ return;
234
+ }
235
+ if (typeof module.register !== 'function')
236
+ return;
168
237
  try {
169
238
  module.register('data:text/javascript,' + encodeURIComponent(exports.NODENEXT_ESM_RESOLVER_SOURCE));
170
239
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nx",
3
- "version": "23.1.0-beta.1",
3
+ "version": "23.1.0-beta.3",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "description": "The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.",
@@ -172,16 +172,16 @@
172
172
  }
173
173
  },
174
174
  "optionalDependencies": {
175
- "@nx/nx-darwin-arm64": "23.1.0-beta.1",
176
- "@nx/nx-darwin-x64": "23.1.0-beta.1",
177
- "@nx/nx-freebsd-x64": "23.1.0-beta.1",
178
- "@nx/nx-linux-arm-gnueabihf": "23.1.0-beta.1",
179
- "@nx/nx-linux-arm64-gnu": "23.1.0-beta.1",
180
- "@nx/nx-linux-arm64-musl": "23.1.0-beta.1",
181
- "@nx/nx-linux-x64-gnu": "23.1.0-beta.1",
182
- "@nx/nx-linux-x64-musl": "23.1.0-beta.1",
183
- "@nx/nx-win32-arm64-msvc": "23.1.0-beta.1",
184
- "@nx/nx-win32-x64-msvc": "23.1.0-beta.1"
175
+ "@nx/nx-darwin-arm64": "23.1.0-beta.3",
176
+ "@nx/nx-darwin-x64": "23.1.0-beta.3",
177
+ "@nx/nx-freebsd-x64": "23.1.0-beta.3",
178
+ "@nx/nx-linux-arm-gnueabihf": "23.1.0-beta.3",
179
+ "@nx/nx-linux-arm64-gnu": "23.1.0-beta.3",
180
+ "@nx/nx-linux-arm64-musl": "23.1.0-beta.3",
181
+ "@nx/nx-linux-x64-gnu": "23.1.0-beta.3",
182
+ "@nx/nx-linux-x64-musl": "23.1.0-beta.3",
183
+ "@nx/nx-win32-arm64-msvc": "23.1.0-beta.3",
184
+ "@nx/nx-win32-x64-msvc": "23.1.0-beta.3"
185
185
  },
186
186
  "nx-migrations": {
187
187
  "migrations": "./migrations.json",