@nx/jest 23.1.0-beta.2 → 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.
@@ -3,7 +3,8 @@
3
3
  "compilerOptions": {
4
4
  "outDir": "<%= outDir %>",<% if (module) { %>
5
5
  "module": "<%= module %>",<% } if (module === 'commonjs') { %>
6
- "moduleResolution": "<%= moduleResolution %>",<% } if (supportTsx) { %>
6
+ "moduleResolution": "<%= moduleResolution %>",<% } if (isolatedModules) { %>
7
+ "isolatedModules": true,<% } if (supportTsx) { %>
7
8
  "jsx": "react-jsx",<% } %>"types": ["jest", "node"]
8
9
  },<% if(setupFile !== 'none') { %>
9
10
  "files": ["src/test-setup.ts"],<% } %>
@@ -46,6 +46,16 @@ function createFiles(tree, options, presetExt) {
46
46
  const coverageDirectory = options.isTsSolutionSetup
47
47
  ? `test-output/jest/coverage`
48
48
  : `${rootOffset}coverage/${projectRoot}`;
49
+ const usesTsJestModuleSettings = !options.isTsSolutionSetup || transformer === 'ts-jest';
50
+ const moduleResolution = usesTsJestModuleSettings
51
+ ? (0, internal_1.getTsConfigModuleResolution)(tree)
52
+ : undefined;
53
+ // ts-jest forces `moduleResolution: node10` on the CommonJS path for
54
+ // TypeScript < 6, and node10 ignores package `exports`. Transpiling per file
55
+ // keeps exports-only workspace libraries resolvable. See NXC-4591.
56
+ const isolatedModules = options.isTsSolutionSetup &&
57
+ transformer === 'ts-jest' &&
58
+ moduleResolution === 'node10';
49
59
  (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, commonFilesFolder), projectConfig.root, {
50
60
  tmpl: '',
51
61
  ...options,
@@ -64,12 +74,9 @@ function createFiles(tree, options, presetExt) {
64
74
  outDir: options.isTsSolutionSetup
65
75
  ? `./out-tsc/jest`
66
76
  : `${rootOffset}dist/out-tsc`,
67
- module: !options.isTsSolutionSetup || transformer === 'ts-jest'
68
- ? 'commonjs'
69
- : undefined,
70
- moduleResolution: !options.isTsSolutionSetup || transformer === 'ts-jest'
71
- ? (0, internal_1.getTsConfigModuleResolution)(tree)
72
- : undefined,
77
+ module: usesTsJestModuleSettings ? 'commonjs' : undefined,
78
+ moduleResolution,
79
+ isolatedModules,
73
80
  });
74
81
  if (options.setupFile !== 'angular') {
75
82
  (0, devkit_1.generateFiles)(tree, (0, path_1.join)(__dirname, options.isTsSolutionSetup
@@ -0,0 +1,8 @@
1
+ import { type Tree } from '@nx/devkit';
2
+ /**
3
+ * NXC-4591: ts-jest 29.2+ uses `moduleResolution: node10` on the CommonJS path
4
+ * for TypeScript < 6, which ignores package `exports` and breaks exports-only
5
+ * workspace libs (TS2307). `isolatedModules: true` makes ts-jest transpile per
6
+ * file, skipping that resolution. TS >= 6 is unaffected.
7
+ */
8
+ export default function (tree: Tree): Promise<void>;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = default_1;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const internal_1 = require("@nx/js/internal");
6
+ const jsonc_parser_1 = require("jsonc-parser");
7
+ const config_file_1 = require("../../utils/config/config-file");
8
+ const FORMATTING_OPTIONS = {
9
+ formattingOptions: { keepLines: true, insertSpaces: true, tabSize: 2 },
10
+ };
11
+ // A jest config mentioning any of these uses swc/babel/angular (or is the root
12
+ // aggregator), not ts-jest, so skip it.
13
+ const NON_TS_JEST_MARKERS = [
14
+ '@swc/jest',
15
+ 'babel-jest',
16
+ 'jest-preset-angular',
17
+ 'getJestProjectsAsync',
18
+ ];
19
+ /**
20
+ * NXC-4591: ts-jest 29.2+ uses `moduleResolution: node10` on the CommonJS path
21
+ * for TypeScript < 6, which ignores package `exports` and breaks exports-only
22
+ * workspace libs (TS2307). `isolatedModules: true` makes ts-jest transpile per
23
+ * file, skipping that resolution. TS >= 6 is unaffected.
24
+ */
25
+ async function default_1(tree) {
26
+ // TS >= 6 resolves `exports` under bundler + commonjs; not affected.
27
+ if ((0, internal_1.isTypescriptVersionAtLeast)(tree, '6.0.0')) {
28
+ return;
29
+ }
30
+ // Path-based workspaces resolve via tsconfig `paths`, not `exports`; skip them.
31
+ if (!(0, internal_1.isUsingTsSolutionSetup)(tree)) {
32
+ return;
33
+ }
34
+ // Fresh ts-solution workspaces already enable this in the base config.
35
+ if (rootEnablesIsolatedModules(tree)) {
36
+ return;
37
+ }
38
+ let count = 0;
39
+ for (const [, project] of (0, devkit_1.getProjects)(tree)) {
40
+ const specPath = getTsJestSpecTsconfig(tree, project.root);
41
+ if (specPath && setIsolatedModules(tree, specPath)) {
42
+ count++;
43
+ }
44
+ }
45
+ if (count > 0) {
46
+ devkit_1.logger.info(`NXC-4591: set "isolatedModules": true in ${count} tsconfig.spec.json file(s) so ts-jest resolves exports-only workspace libraries on TypeScript < 6.`);
47
+ }
48
+ await (0, devkit_1.formatFiles)(tree);
49
+ }
50
+ function rootEnablesIsolatedModules(tree) {
51
+ return (readCompilerOption(tree, 'tsconfig.base.json', 'isolatedModules') === true);
52
+ }
53
+ // The project's tsconfig.spec.json if it runs ts-jest, else null.
54
+ function getTsJestSpecTsconfig(tree, root) {
55
+ const jestConfig = (0, config_file_1.findJestConfig)(tree, root);
56
+ if (!jestConfig) {
57
+ return null;
58
+ }
59
+ const source = tree.read(jestConfig, 'utf-8') ?? '';
60
+ if (NON_TS_JEST_MARKERS.some((marker) => source.includes(marker))) {
61
+ return null;
62
+ }
63
+ const specPath = (0, devkit_1.joinPathFragments)(root, 'tsconfig.spec.json');
64
+ return tree.exists(specPath) ? specPath : null;
65
+ }
66
+ function setIsolatedModules(tree, tsconfigPath) {
67
+ if (readCompilerOption(tree, tsconfigPath, 'isolatedModules') === true) {
68
+ return false;
69
+ }
70
+ const contents = tree.read(tsconfigPath, 'utf-8');
71
+ if (!contents) {
72
+ return false;
73
+ }
74
+ const edits = (0, jsonc_parser_1.modify)(contents, ['compilerOptions', 'isolatedModules'], true, FORMATTING_OPTIONS);
75
+ tree.write(tsconfigPath, (0, jsonc_parser_1.applyEdits)(contents, edits));
76
+ return true;
77
+ }
78
+ function readCompilerOption(tree, tsconfigPath, option) {
79
+ const contents = tree.read(tsconfigPath, 'utf-8');
80
+ if (!contents) {
81
+ return undefined;
82
+ }
83
+ const root = (0, jsonc_parser_1.parseTree)(contents);
84
+ const node = root && (0, jsonc_parser_1.findNodeAtLocation)(root, ['compilerOptions', option]);
85
+ return node ? (0, jsonc_parser_1.getNodeValue)(node) : undefined;
86
+ }
@@ -0,0 +1,41 @@
1
+ #### Set isolatedModules for ts-jest below TypeScript 6
2
+
3
+ The jest 30 path of `@nx/jest` bumps ts-jest to 29.4.x.
4
+ On the CommonJS jest path, ts-jest 29.2+ uses `moduleResolution: node10` when `bundler` is invalid alongside the forced `module: commonjs`, which is the case below TypeScript 6.
5
+ `node10` does not read package `exports` maps, so a workspace library that exposes types only through `exports` fails to resolve during the ts-jest type check:
6
+
7
+ ```text
8
+ error TS2307: Cannot find module '@my-org/some-lib' or its corresponding type declarations.
9
+ ```
10
+
11
+ The migration sets `isolatedModules: true` in each affected `tsconfig.spec.json`, so ts-jest transpiles each file independently and skips the cross-file type resolution that was failing.
12
+ This matches the `isolatedModules: true` that fresh ts-solution workspaces already set in `tsconfig.base.json`.
13
+ Type checking stays on each project's dedicated typecheck target.
14
+
15
+ The migration runs only below TypeScript 6, in ts-solution workspaces that do not already enable `isolatedModules`.
16
+ TypeScript 6 and above resolves `exports` under `bundler` with `commonjs`, so those workspaces are left unchanged.
17
+
18
+ #### Sample code changes
19
+
20
+ ##### Before
21
+
22
+ ```json title="tsconfig.spec.json"
23
+ {
24
+ "extends": "../../tsconfig.base.json",
25
+ "compilerOptions": {
26
+ "types": ["jest", "node"]
27
+ }
28
+ }
29
+ ```
30
+
31
+ ##### After
32
+
33
+ ```json title="tsconfig.spec.json" {4}
34
+ {
35
+ "extends": "../../tsconfig.base.json",
36
+ "compilerOptions": {
37
+ "isolatedModules": true,
38
+ "types": ["jest", "node"]
39
+ }
40
+ }
41
+ ```
package/migrations.json CHANGED
@@ -77,6 +77,15 @@
77
77
  "description": "Rename imports of `createNodesV2` from `@nx/jest/plugin` to the canonical `createNodes` export.",
78
78
  "implementation": "./dist/src/migrations/update-23-0-0/migrate-create-nodes-v2-to-create-nodes",
79
79
  "documentation": "./dist/src/migrations/update-23-0-0/migrate-create-nodes-v2-to-create-nodes.md"
80
+ },
81
+ "set-ts-jest-isolated-modules": {
82
+ "version": "23.1.0-beta.3",
83
+ "requires": {
84
+ "ts-jest": ">=29.2.0"
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.",
87
+ "implementation": "./dist/src/migrations/update-23-1-0/set-ts-jest-isolated-modules",
88
+ "documentation": "./dist/src/migrations/update-23-1-0/set-ts-jest-isolated-modules.md"
80
89
  }
81
90
  },
82
91
  "packageJsonUpdates": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/jest",
3
- "version": "23.1.0-beta.2",
3
+ "version": "23.1.0-beta.3",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "files": [
@@ -110,17 +110,18 @@
110
110
  "jest-config": "^30.0.2",
111
111
  "jest-resolve": "^30.0.2",
112
112
  "jest-util": "^30.0.2",
113
+ "jsonc-parser": "^3.2.0",
113
114
  "minimatch": "10.2.5",
114
115
  "picocolors": "^1.1.0",
115
116
  "resolve.exports": "2.0.3",
116
117
  "semver": "^7.6.3",
117
118
  "tslib": "^2.3.0",
118
119
  "yargs-parser": "21.1.1",
119
- "@nx/devkit": "23.1.0-beta.2",
120
- "@nx/js": "23.1.0-beta.2"
120
+ "@nx/devkit": "23.1.0-beta.3",
121
+ "@nx/js": "23.1.0-beta.3"
121
122
  },
122
123
  "devDependencies": {
123
- "nx": "23.1.0-beta.2"
124
+ "nx": "23.1.0-beta.3"
124
125
  },
125
126
  "publishConfig": {
126
127
  "access": "public"