@nx/jest 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.
@@ -3,8 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.configurationGenerator = configurationGenerator;
4
4
  exports.configurationGeneratorInternal = configurationGeneratorInternal;
5
5
  const devkit_1 = require("@nx/devkit");
6
+ const internal_1 = require("@nx/devkit/internal");
6
7
  const js_1 = require("@nx/js");
7
- const internal_1 = require("@nx/js/internal");
8
+ const internal_2 = require("@nx/js/internal");
8
9
  const config_file_1 = require("../../utils/config/config-file");
9
10
  const init_1 = require("../init/init");
10
11
  const assert_supported_jest_version_1 = require("../../utils/assert-supported-jest-version");
@@ -47,7 +48,7 @@ function normalizeOptions(tree, options) {
47
48
  ...options,
48
49
  keepExistingVersions: options.keepExistingVersions ?? true,
49
50
  rootProject: project.root === '.' || project.root === '',
50
- isTsSolutionSetup: (0, internal_1.isUsingTsSolutionSetup)(tree),
51
+ isTsSolutionSetup: (0, internal_2.isUsingTsSolutionSetup)(tree),
51
52
  };
52
53
  }
53
54
  function configurationGenerator(tree, schema) {
@@ -93,12 +94,17 @@ async function configurationGeneratorInternal(tree, schema) {
93
94
  ignoreTestOutput(tree);
94
95
  // in the TS solution setup, the test target depends on the build outputs
95
96
  // so we need to setup the task pipeline accordingly
96
- const nxJson = (0, devkit_1.readNxJson)(tree);
97
- nxJson.targetDefaults ??= {};
98
- nxJson.targetDefaults[options.targetName] ??= {};
99
- nxJson.targetDefaults[options.targetName].dependsOn ??= [];
100
- nxJson.targetDefaults[options.targetName].dependsOn.push('^build');
101
- nxJson.targetDefaults[options.targetName].dependsOn = Array.from(new Set(nxJson.targetDefaults[options.targetName].dependsOn));
97
+ const nxJson = (0, devkit_1.readNxJson)(tree) ?? {};
98
+ // A bare `target` locator matches only the unfiltered generic entry, so we
99
+ // extend the workspace-wide baseline rather than a project-scoped one.
100
+ const existing = (0, internal_1.findTargetDefault)(nxJson.targetDefaults, {
101
+ target: options.targetName,
102
+ });
103
+ const dependsOn = Array.from(new Set([...(existing?.dependsOn ?? []), '^build']));
104
+ (0, internal_1.upsertTargetDefault)(tree, nxJson, {
105
+ target: options.targetName,
106
+ dependsOn,
107
+ });
102
108
  (0, devkit_1.updateNxJson)(tree, nxJson);
103
109
  }
104
110
  if (!schema.skipFormat) {
@@ -29,27 +29,44 @@ function updateProductionFileSet(tree) {
29
29
  (0, devkit_1.updateNxJson)(tree, nxJson);
30
30
  }
31
31
  function addJestTargetDefaults(tree, presetExt) {
32
- const nxJson = (0, devkit_1.readNxJson)(tree);
33
- nxJson.targetDefaults ??= {};
34
- nxJson.targetDefaults['@nx/jest:jest'] ??= {};
32
+ const nxJson = (0, devkit_1.readNxJson)(tree) ?? {};
35
33
  const productionFileSet = nxJson.namedInputs?.production;
36
- nxJson.targetDefaults['@nx/jest:jest'].cache ??= true;
34
+ // Manage the workspace-wide `@nx/jest:jest` default; `upsertTargetDefault`
35
+ // updates the unfiltered entry (or creates one), leaving any project- or
36
+ // plugin-scoped jest overrides the user authored untouched.
37
+ const existing = (0, internal_1.findTargetDefault)(nxJson.targetDefaults, {
38
+ executor: '@nx/jest:jest',
39
+ });
40
+ const patch = createJestDefaultPatch(existing, productionFileSet, presetExt);
41
+ if (Object.keys(patch).length > 0) {
42
+ (0, internal_1.upsertTargetDefault)(tree, nxJson, { executor: '@nx/jest:jest', ...patch });
43
+ (0, devkit_1.updateNxJson)(tree, nxJson);
44
+ }
45
+ }
46
+ function createJestDefaultPatch(existing, productionFileSet, presetExt) {
47
+ const patch = {};
48
+ if (existing?.cache === undefined)
49
+ patch.cache = true;
37
50
  // Test targets depend on all their project's sources + production sources of dependencies
38
- nxJson.targetDefaults['@nx/jest:jest'].inputs ??= [
39
- 'default',
40
- productionFileSet ? '^production' : '^default',
41
- `{workspaceRoot}/jest.preset.${presetExt}`,
42
- ];
43
- nxJson.targetDefaults['@nx/jest:jest'].options ??= {
44
- passWithNoTests: true,
45
- };
46
- nxJson.targetDefaults['@nx/jest:jest'].configurations ??= {
47
- ci: {
48
- ci: true,
49
- codeCoverage: true,
50
- },
51
- };
52
- (0, devkit_1.updateNxJson)(tree, nxJson);
51
+ if (existing?.inputs === undefined) {
52
+ patch.inputs = [
53
+ 'default',
54
+ productionFileSet ? '^production' : '^default',
55
+ `{workspaceRoot}/jest.preset.${presetExt}`,
56
+ ];
57
+ }
58
+ if (existing?.options === undefined) {
59
+ patch.options = { passWithNoTests: true };
60
+ }
61
+ if (existing?.configurations === undefined) {
62
+ patch.configurations = {
63
+ ci: {
64
+ ci: true,
65
+ codeCoverage: true,
66
+ },
67
+ };
68
+ }
69
+ return patch;
53
70
  }
54
71
  function updateDependencies(tree, options) {
55
72
  const { jestVersion, nxVersion } = (0, versions_1.versions)(tree);
@@ -19,29 +19,24 @@ async function default_1(tree) {
19
19
  }
20
20
  (0, devkit_1.updateProjectConfiguration)(tree, project, projectConfiguration);
21
21
  });
22
- // update options from nx.json target defaults
22
+ // update options from nx.json target defaults. `updateTargetDefault` walks
23
+ // both the object and filtered array value forms, drops entries left with
24
+ // nothing but their executor locator, and collapses lone unfiltered ones
25
+ // back to the object form.
23
26
  const nxJson = (0, devkit_1.readNxJson)(tree);
24
- if (nxJson.targetDefaults) {
25
- for (const [targetOrExecutor, targetConfig] of Object.entries(nxJson.targetDefaults)) {
26
- if (targetOrExecutor !== EXECUTOR_TO_MIGRATE &&
27
- targetConfig.executor !== EXECUTOR_TO_MIGRATE) {
28
- continue;
29
- }
30
- if (targetConfig.options) {
31
- updateOptions(targetConfig);
32
- }
33
- Object.keys(targetConfig.configurations ?? {}).forEach((config) => {
34
- updateConfiguration(targetConfig, config);
27
+ if (nxJson?.targetDefaults) {
28
+ (0, internal_1.updateTargetDefault)(nxJson, { executor: EXECUTOR_TO_MIGRATE }, (config) => {
29
+ if (config.options)
30
+ updateOptions(config);
31
+ Object.keys(config.configurations ?? {}).forEach((configuration) => {
32
+ updateConfiguration(config, configuration);
35
33
  });
36
- if (!Object.keys(targetConfig).length ||
37
- (Object.keys(targetConfig).length === 1 &&
38
- Object.keys(targetConfig)[0] === 'executor')) {
39
- delete nxJson.targetDefaults[targetOrExecutor];
34
+ // Drop the entry once nothing but its executor locator remains.
35
+ const keys = Object.keys(config);
36
+ if (keys.length === 0 || (keys.length === 1 && keys[0] === 'executor')) {
37
+ return null;
40
38
  }
41
- if (!Object.keys(nxJson.targetDefaults).length) {
42
- delete nxJson.targetDefaults;
43
- }
44
- }
39
+ });
45
40
  (0, devkit_1.updateNxJson)(tree, nxJson);
46
41
  }
47
42
  await (0, devkit_1.formatFiles)(tree);
@@ -14,24 +14,21 @@ async function default_1(tree) {
14
14
  renameTestPathPattern(config);
15
15
  (0, devkit_1.updateProjectConfiguration)(tree, project, projectConfiguration);
16
16
  });
17
- // update options from nx.json target defaults
17
+ // update options from nx.json target defaults. Migration order isn't
18
+ // guaranteed, so a default may already be in the filtered array shape;
19
+ // `updateTargetDefault` walks both the object and array value forms.
18
20
  const nxJson = (0, devkit_1.readNxJson)(tree);
19
- if (!nxJson.targetDefaults) {
20
- return;
21
- }
22
- for (const [targetOrExecutor, targetConfig] of Object.entries(nxJson.targetDefaults)) {
23
- if (targetOrExecutor !== '@nx/jest:jest' &&
24
- targetConfig.executor !== '@nx/jest:jest') {
25
- continue;
26
- }
27
- if (targetConfig.options) {
28
- renameTestPathPattern(targetConfig.options);
29
- }
30
- Object.values(targetConfig.configurations ?? {}).forEach((config) => {
31
- renameTestPathPattern(config);
21
+ if (nxJson?.targetDefaults) {
22
+ (0, internal_1.updateTargetDefault)(nxJson, { executor: '@nx/jest:jest' }, (config) => {
23
+ if (config.options) {
24
+ renameTestPathPattern(config.options);
25
+ }
26
+ Object.values(config.configurations ?? {}).forEach((c) => {
27
+ renameTestPathPattern(c);
28
+ });
32
29
  });
30
+ (0, devkit_1.updateNxJson)(tree, nxJson);
33
31
  }
34
- (0, devkit_1.updateNxJson)(tree, nxJson);
35
32
  await (0, devkit_1.formatFiles)(tree);
36
33
  }
37
34
  function renameTestPathPattern(config) {
@@ -190,47 +190,40 @@ function migrateInheritedSetupFile(tree, project, target, nxJson, projectConfigC
190
190
  function stripSetupFileFromNxJson(tree, nxJson) {
191
191
  if (!nxJson?.targetDefaults)
192
192
  return false;
193
- let changed = false;
194
193
  let hadSetupFile = false;
195
- for (const [targetOrExecutor, targetConfig] of Object.entries(nxJson.targetDefaults)) {
196
- if (targetOrExecutor !== EXECUTOR_TO_MIGRATE &&
197
- targetConfig.executor !== EXECUTOR_TO_MIGRATE) {
198
- continue;
199
- }
200
- if (targetConfig.options?.setupFile !== undefined) {
194
+ // Migration order isn't guaranteed, so a default may already be in the
195
+ // filtered array shape; `updateTargetDefault` walks both value forms, drops
196
+ // entries the callback empties, and collapses lone unfiltered ones back to
197
+ // the object form.
198
+ (0, internal_1.updateTargetDefault)(nxJson, { executor: EXECUTOR_TO_MIGRATE }, (config) => {
199
+ if (config.options?.setupFile !== undefined) {
201
200
  hadSetupFile = true;
202
- changed = true;
203
- delete targetConfig.options.setupFile;
204
- if (!Object.keys(targetConfig.options).length) {
205
- delete targetConfig.options;
201
+ delete config.options.setupFile;
202
+ if (!Object.keys(config.options).length) {
203
+ delete config.options;
206
204
  }
207
205
  }
208
- for (const config of Object.keys(targetConfig.configurations ?? {})) {
209
- if (targetConfig.configurations[config]?.setupFile !== undefined) {
206
+ for (const configuration of Object.keys(config.configurations ?? {})) {
207
+ if (config.configurations[configuration]?.setupFile !== undefined) {
210
208
  hadSetupFile = true;
211
- changed = true;
212
- delete targetConfig.configurations[config].setupFile;
213
- if (!Object.keys(targetConfig.configurations[config]).length &&
214
- (!targetConfig.defaultConfiguration ||
215
- targetConfig.defaultConfiguration !== config)) {
216
- delete targetConfig.configurations[config];
209
+ delete config.configurations[configuration].setupFile;
210
+ if (!Object.keys(config.configurations[configuration]).length &&
211
+ (!config.defaultConfiguration ||
212
+ config.defaultConfiguration !== configuration)) {
213
+ delete config.configurations[configuration];
217
214
  }
218
215
  }
219
216
  }
220
- if (targetConfig.configurations &&
221
- !Object.keys(targetConfig.configurations).length) {
222
- delete targetConfig.configurations;
217
+ if (config.configurations && !Object.keys(config.configurations).length) {
218
+ delete config.configurations;
223
219
  }
224
- if (!Object.keys(targetConfig).length ||
225
- (Object.keys(targetConfig).length === 1 &&
226
- Object.keys(targetConfig)[0] === 'executor')) {
227
- delete nxJson.targetDefaults[targetOrExecutor];
220
+ // Drop the entry once nothing but its executor locator remains.
221
+ const keys = Object.keys(config);
222
+ if (keys.length === 0 || (keys.length === 1 && keys[0] === 'executor')) {
223
+ return null;
228
224
  }
229
- }
230
- if (!Object.keys(nxJson.targetDefaults).length) {
231
- delete nxJson.targetDefaults;
232
- }
233
- if (changed)
225
+ });
226
+ if (hadSetupFile)
234
227
  (0, devkit_1.updateNxJson)(tree, nxJson);
235
228
  return hadSetupFile;
236
229
  }
@@ -137,7 +137,7 @@ Remove the option from a target default using the `@nx/jest:jest` executor:
137
137
 
138
138
  Per-project paths don't make sense as workspace defaults, so the option is removed without rewriting individual project Jest configs. A warning is logged so the setup file path can be added to each project's Jest config manually if needed.
139
139
 
140
- Remove the option from a target default using the `@nx/jest:jest` executor as the key:
140
+ Remove the option from a target default entry matching on the `@nx/jest:jest` executor:
141
141
 
142
142
  ##### Before
143
143
 
@@ -39,3 +39,13 @@ TypeScript 6 and above resolves `exports` under `bundler` with `commonjs`, so th
39
39
  }
40
40
  }
41
41
  ```
42
+
43
+ #### Verifying the workspace
44
+
45
+ After the migration runs, verify the workspace still typechecks and fix anything `isolatedModules` surfaced:
46
+
47
+ ```bash
48
+ nx run-many -t typecheck
49
+ ```
50
+
51
+ `isolatedModules` can fail typecheck (TS1205 - re-exporting a type needs `export type`; TS2748 - const enum access) or break a project's tests at runtime even when typecheck passes. The cause is a package that re-exports a value through both `module.exports` and an ESM `export` (for example napi bindings exposing a `const enum`); per-file transpilation cannot preserve it, and consumers of that const enum break too. To fix a broken project, remove `isolatedModules` from its `tsconfig.spec.json`; if that brings back the `TS2307: Cannot find module` error this migration was added to prevent, that project needs `isolatedModules`, so fix the source instead.
@@ -0,0 +1,14 @@
1
+ # Verify typecheck after the Nx 23.1 migration
2
+
3
+ This migration enables `isolatedModules` on ts-jest spec configs. Run
4
+ `nx run-many -t typecheck` and fix any project it broke.
5
+
6
+ `isolatedModules` can fail typecheck (TS1205 - a re-exported type needs `export type`;
7
+ TS2748 - const enum access) or break a project's tests at runtime. To fix a broken
8
+ project, remove `isolatedModules` from its `tsconfig.spec.json` and re-run. If a
9
+ `TS2307: Cannot find module` error then appears for a workspace library, that project
10
+ needs `isolatedModules` - keep it and fix the source instead (avoid mixing
11
+ `module.exports` with an ESM `export`, and avoid cross-file `const enum`).
12
+
13
+ Re-run until the projects this migration touched typecheck. See the migration
14
+ documentation for details.
package/migrations.json CHANGED
@@ -79,12 +79,13 @@
79
79
  "documentation": "./dist/src/migrations/update-23-0-0/migrate-create-nodes-v2-to-create-nodes.md"
80
80
  },
81
81
  "set-ts-jest-isolated-modules": {
82
- "version": "23.1.0-beta.3",
82
+ "version": "23.1.0-beta.4",
83
83
  "requires": {
84
84
  "ts-jest": ">=29.2.0"
85
85
  },
86
- "description": "Set `isolatedModules: true` in `tsconfig.spec.json` for ts-jest projects on TypeScript < 6. ts-jest 29.2+ forces `moduleResolution: node10` on the CommonJS path, which ignores package `exports` maps and breaks (TS2307) resolution of exports-only workspace libraries; transpiling per file avoids the type-resolution failure.",
86
+ "description": "Set `isolatedModules: true` in `tsconfig.spec.json` for ts-jest projects on TypeScript < 6 (ts-jest 29.2+ forces `moduleResolution: node10` on the CommonJS path, which ignores package `exports` maps and breaks resolution of exports-only workspace libraries), then verify the workspace still typechecks and remedy any failures the change surfaced.",
87
87
  "implementation": "./dist/src/migrations/update-23-1-0/set-ts-jest-isolated-modules",
88
+ "prompt": "./dist/src/migrations/update-23-1-0/verify-typecheck.md",
88
89
  "documentation": "./dist/src/migrations/update-23-1-0/set-ts-jest-isolated-modules.md"
89
90
  }
90
91
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/jest",
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": [
@@ -117,11 +117,11 @@
117
117
  "semver": "^7.6.3",
118
118
  "tslib": "^2.3.0",
119
119
  "yargs-parser": "21.1.1",
120
- "@nx/devkit": "23.1.0-beta.3",
121
- "@nx/js": "23.1.0-beta.3"
120
+ "@nx/devkit": "23.1.0-beta.5",
121
+ "@nx/js": "23.1.0-beta.5"
122
122
  },
123
123
  "devDependencies": {
124
- "nx": "23.1.0-beta.3"
124
+ "nx": "23.1.0-beta.5"
125
125
  },
126
126
  "publishConfig": {
127
127
  "access": "public"