@nx/jest 21.0.0-beta.1 → 21.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/jest",
3
- "version": "21.0.0-beta.1",
3
+ "version": "21.0.0-beta.3",
4
4
  "private": false,
5
5
  "description": "The Nx Plugin for Jest contains executors and generators allowing your workspace to use the powerful Jest testing capabilities.",
6
6
  "repository": {
@@ -37,8 +37,8 @@
37
37
  "dependencies": {
38
38
  "@jest/reporters": "^29.4.1",
39
39
  "@jest/test-result": "^29.4.1",
40
- "@nx/devkit": "21.0.0-beta.1",
41
- "@nx/js": "21.0.0-beta.1",
40
+ "@nx/devkit": "21.0.0-beta.3",
41
+ "@nx/js": "21.0.0-beta.3",
42
42
  "@phenomnomnominal/tsquery": "~5.0.1",
43
43
  "identity-obj-proxy": "3.0.0",
44
44
  "jest-config": "^29.4.1",
@@ -16,7 +16,13 @@ process.env.NODE_ENV ??= 'test';
16
16
  async function jestExecutor(options, context) {
17
17
  // Jest registers ts-node with module CJS https://github.com/SimenB/jest/blob/v29.6.4/packages/jest-config/src/readConfigFileAndSetRootDir.ts#L117-L119
18
18
  // We want to support of ESM via 'module':'nodenext', we need to override the resolution until Jest supports it.
19
- process.env.TS_NODE_COMPILER_OPTIONS ??= '{"moduleResolution":"node10"}';
19
+ const existingValue = process.env['TS_NODE_COMPILER_OPTIONS'];
20
+ process.env['TS_NODE_COMPILER_OPTIONS'] = JSON.stringify({
21
+ ...(existingValue ? JSON.parse(existingValue) : {}),
22
+ moduleResolution: 'Node10',
23
+ module: 'commonjs',
24
+ customConditions: null,
25
+ });
20
26
  const config = await parseJestConfig(options, context);
21
27
  const { results } = await (0, jest_1.runCLI)(config, [options.jestConfig]);
22
28
  return { success: results.success };
@@ -1 +1,7 @@
1
- <% if(setupFile === 'react-native') { %>import '@testing-library/jest-native/extend-expect';<% } %>
1
+ <% if(setupFile === 'react-native') { %>import '@testing-library/jest-native/extend-expect';<% } %>
2
+ <%_ if(setupFile === 'react-router') { _%>
3
+ import { TextEncoder, TextDecoder as NodeTextDecoder } from "util";
4
+
5
+ global.TextEncoder = TextEncoder;
6
+ global.TextDecoder = NodeTextDecoder as typeof TextDecoder; // necessary because there is a mismatch between ts type and node type
7
+ <%_ } _%>
@@ -99,9 +99,10 @@ function generateGlobalConfig(tree, isJS) {
99
99
  module.exports = async () => ({
100
100
  projects: await getJestProjectsAsync()
101
101
  });`
102
- : `import { getJestProjectsAsync } from '@nx/jest';
102
+ : `import type { Config } from 'jest';
103
+ import { getJestProjectsAsync } from '@nx/jest';
103
104
 
104
- export default async () => ({
105
+ export default async (): Promise<Config> => ({
105
106
  projects: await getJestProjectsAsync()
106
107
  });`;
107
108
  tree.write(`jest.config.${isJS ? 'js' : 'ts'}`, contents);
@@ -6,7 +6,12 @@ export interface JestProjectSchema {
6
6
  * @deprecated use setupFile instead
7
7
  */
8
8
  skipSetupFile?: boolean;
9
- setupFile?: 'angular' | 'web-components' | 'react-native' | 'none';
9
+ setupFile?:
10
+ | 'angular'
11
+ | 'web-components'
12
+ | 'react-native'
13
+ | 'react-router'
14
+ | 'none';
10
15
  skipSerializers?: boolean;
11
16
  testEnvironment?: 'node' | 'jsdom' | 'none';
12
17
  /**
@@ -7,7 +7,7 @@ export interface JestPluginOptions {
7
7
  */
8
8
  ciGroupName?: string;
9
9
  /**
10
- * Whether to use jest-config and jest-runtime to load Jest configuration and context.
10
+ * Whether to use jest-config and jest-runtime are used to load Jest configuration and context.
11
11
  * Disabling this is much faster but could be less correct since we are using our own config loader
12
12
  * and test matcher instead of Jest's.
13
13
  */
@@ -31,8 +31,36 @@ exports.createNodesV2 = [
31
31
  const targetsCache = readTargetsCache(cachePath);
32
32
  // Cache jest preset(s) to avoid penalties of module load times. Most of jest configs will use the same preset.
33
33
  const presetCache = {};
34
+ const packageManagerWorkspacesGlob = (0, globs_1.combineGlobPatterns)((0, package_json_1.getGlobPatternsFromPackageManagerWorkspaces)(context.workspaceRoot));
35
+ options = normalizeOptions(options);
36
+ const { roots: projectRoots, configFiles: validConfigFiles } = configFiles.reduce((acc, configFile) => {
37
+ const potentialRoot = (0, path_1.dirname)(configFile);
38
+ if (checkIfConfigFileShouldBeProject(configFile, potentialRoot, packageManagerWorkspacesGlob, context)) {
39
+ acc.roots.push(potentialRoot);
40
+ acc.configFiles.push(configFile);
41
+ }
42
+ return acc;
43
+ }, {
44
+ roots: [],
45
+ configFiles: [],
46
+ });
47
+ const hashes = await (0, calculate_hash_for_create_nodes_1.calculateHashesForCreateNodes)(projectRoots, options, context);
34
48
  try {
35
- return await (0, devkit_1.createNodesFromFiles)((configFile, options, context) => createNodesInternal(configFile, options, context, targetsCache, presetCache), configFiles, options, context);
49
+ return await (0, devkit_1.createNodesFromFiles)(async (configFilePath, options, context, idx) => {
50
+ const projectRoot = projectRoots[idx];
51
+ const hash = hashes[idx];
52
+ targetsCache[hash] ??= await buildJestTargets(configFilePath, projectRoot, options, context, presetCache);
53
+ const { targets, metadata } = targetsCache[hash];
54
+ return {
55
+ projects: {
56
+ [projectRoot]: {
57
+ root: projectRoot,
58
+ targets,
59
+ metadata,
60
+ },
61
+ },
62
+ };
63
+ }, validConfigFiles, options, context);
36
64
  }
37
65
  finally {
38
66
  writeTargetsToCache(cachePath, targetsCache);
@@ -45,26 +73,39 @@ exports.createNodesV2 = [
45
73
  */
46
74
  exports.createNodes = [
47
75
  jestConfigGlob,
48
- (...args) => {
76
+ async (configFilePath, options, context) => {
49
77
  devkit_1.logger.warn('`createNodes` is deprecated. Update your plugin to utilize createNodesV2 instead. In Nx 20, this will change to the createNodesV2 API.');
50
- return createNodesInternal(...args, {}, {});
78
+ const projectRoot = (0, path_1.dirname)(configFilePath);
79
+ const packageManagerWorkspacesGlob = (0, globs_1.combineGlobPatterns)((0, package_json_1.getGlobPatternsFromPackageManagerWorkspaces)(context.workspaceRoot));
80
+ if (!checkIfConfigFileShouldBeProject(configFilePath, projectRoot, packageManagerWorkspacesGlob, context)) {
81
+ return {};
82
+ }
83
+ options = normalizeOptions(options);
84
+ const { targets, metadata } = await buildJestTargets(configFilePath, projectRoot, options, context, {});
85
+ return {
86
+ projects: {
87
+ [projectRoot]: {
88
+ root: projectRoot,
89
+ targets,
90
+ metadata,
91
+ },
92
+ },
93
+ };
51
94
  },
52
95
  ];
53
- async function createNodesInternal(configFilePath, options, context, targetsCache, presetCache) {
54
- const projectRoot = (0, path_1.dirname)(configFilePath);
55
- const packageManagerWorkspacesGlob = (0, globs_1.combineGlobPatterns)((0, package_json_1.getGlobPatternsFromPackageManagerWorkspaces)(context.workspaceRoot));
96
+ function checkIfConfigFileShouldBeProject(configFilePath, projectRoot, packageManagerWorkspacesGlob, context) {
56
97
  // Do not create a project if package.json and project.json isn't there.
57
98
  const siblingFiles = (0, fs_1.readdirSync)((0, path_1.join)(context.workspaceRoot, projectRoot));
58
99
  if (!siblingFiles.includes('package.json') &&
59
100
  !siblingFiles.includes('project.json')) {
60
- return {};
101
+ return false;
61
102
  }
62
103
  else if (!siblingFiles.includes('project.json') &&
63
104
  siblingFiles.includes('package.json')) {
64
105
  const path = (0, devkit_1.joinPathFragments)(projectRoot, 'package.json');
65
106
  const isPackageJsonProject = (0, minimatch_1.minimatch)(path, packageManagerWorkspacesGlob);
66
107
  if (!isPackageJsonProject) {
67
- return {};
108
+ return false;
68
109
  }
69
110
  }
70
111
  const jestConfigContent = (0, fs_1.readFileSync)((0, path_1.resolve)(context.workspaceRoot, configFilePath), 'utf-8');
@@ -72,21 +113,9 @@ async function createNodesInternal(configFilePath, options, context, targetsCach
72
113
  // The `getJestProjectsAsync` function uses the project graph, which leads to a
73
114
  // circular dependency. We can skip this since it's no intended to be used for
74
115
  // an Nx project.
75
- return {};
116
+ return false;
76
117
  }
77
- options = normalizeOptions(options);
78
- const hash = await (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(projectRoot, options, context);
79
- targetsCache[hash] ??= await buildJestTargets(configFilePath, projectRoot, options, context, presetCache);
80
- const { targets, metadata } = targetsCache[hash];
81
- return {
82
- projects: {
83
- [projectRoot]: {
84
- root: projectRoot,
85
- targets,
86
- metadata,
87
- },
88
- },
89
- };
118
+ return true;
90
119
  }
91
120
  async function buildJestTargets(configFilePath, projectRoot, options, context, presetCache) {
92
121
  const absConfigFilePath = (0, path_1.resolve)(context.workspaceRoot, configFilePath);
@@ -95,13 +124,18 @@ async function buildJestTargets(configFilePath, projectRoot, options, context, p
95
124
  const rawConfig = await (0, config_utils_1.loadConfigFile)(absConfigFilePath);
96
125
  const targets = {};
97
126
  const namedInputs = (0, get_named_inputs_1.getNamedInputs)(projectRoot, context);
127
+ const tsNodeCompilerOptions = JSON.stringify({
128
+ moduleResolution: 'node10',
129
+ module: 'commonjs',
130
+ customConditions: null,
131
+ });
98
132
  const target = (targets[options.targetName] = {
99
133
  command: 'jest',
100
134
  options: {
101
135
  cwd: projectRoot,
102
136
  // Jest registers ts-node with module CJS https://github.com/SimenB/jest/blob/v29.6.4/packages/jest-config/src/readConfigFileAndSetRootDir.ts#L117-L119
103
137
  // We want to support of ESM via 'module':'nodenext', we need to override the resolution until Jest supports it.
104
- env: { TS_NODE_COMPILER_OPTIONS: '{"moduleResolution":"node10"}' },
138
+ env: { TS_NODE_COMPILER_OPTIONS: tsNodeCompilerOptions },
105
139
  },
106
140
  metadata: {
107
141
  technologies: ['jest'],
@@ -116,11 +150,13 @@ async function buildJestTargets(configFilePath, projectRoot, options, context, p
116
150
  },
117
151
  },
118
152
  });
153
+ // Not normalizing it here since also affects options for convert-to-inferred.
154
+ const disableJestRuntime = options.disableJestRuntime !== false;
119
155
  const cache = (target.cache = true);
120
- const inputs = (target.inputs = getInputs(namedInputs, rawConfig.preset, projectRoot, context.workspaceRoot, options.disableJestRuntime));
156
+ const inputs = (target.inputs = getInputs(namedInputs, rawConfig.preset, projectRoot, context.workspaceRoot, disableJestRuntime));
121
157
  let metadata;
122
158
  const groupName = options?.ciGroupName ?? deductGroupNameFromTarget(options?.ciTargetName);
123
- if (options.disableJestRuntime) {
159
+ if (disableJestRuntime) {
124
160
  const outputs = (target.outputs = getOutputs(projectRoot, rawConfig.coverageDirectory
125
161
  ? (0, path_1.join)(context.workspaceRoot, projectRoot, rawConfig.coverageDirectory)
126
162
  : undefined, undefined, context));
@@ -148,7 +184,7 @@ async function buildJestTargets(configFilePath, projectRoot, options, context, p
148
184
  outputs,
149
185
  options: {
150
186
  cwd: projectRoot,
151
- env: { TS_NODE_COMPILER_OPTIONS: '{"moduleResolution":"node10"}' },
187
+ env: { TS_NODE_COMPILER_OPTIONS: tsNodeCompilerOptions },
152
188
  },
153
189
  metadata: {
154
190
  technologies: ['jest'],
@@ -192,10 +228,17 @@ async function buildJestTargets(configFilePath, projectRoot, options, context, p
192
228
  }
193
229
  else {
194
230
  const { readConfig } = requireJestUtil('jest-config', projectRoot, context.workspaceRoot);
195
- const config = await readConfig({
196
- _: [],
197
- $0: undefined,
198
- }, rawConfig, undefined, (0, path_1.dirname)(absConfigFilePath));
231
+ let config;
232
+ try {
233
+ config = await readConfig({
234
+ _: [],
235
+ $0: undefined,
236
+ }, rawConfig, undefined, (0, path_1.dirname)(absConfigFilePath));
237
+ }
238
+ catch (e) {
239
+ console.error(e);
240
+ throw e;
241
+ }
199
242
  const outputs = (target.outputs = getOutputs(projectRoot, config.globalConfig?.coverageDirectory, config.globalConfig?.outputFile, context));
200
243
  if (options?.ciTargetName) {
201
244
  // nx-ignore-next-line
@@ -208,8 +251,7 @@ async function buildJestTargets(configFilePath, projectRoot, options, context, p
208
251
  const source = new jest.SearchSource(jestContext);
209
252
  const jestVersion = (0, version_utils_1.getInstalledJestMajorVersion)();
210
253
  const specs = jestVersion >= 30
211
- ? // @ts-expect-error Jest 30+ expects the project config as the second argument
212
- await source.getTestPaths(config.globalConfig, config.projectConfig)
254
+ ? await source.getTestPaths(config.globalConfig, config.projectConfig)
213
255
  : await source.getTestPaths(config.globalConfig);
214
256
  const testPaths = new Set(specs.tests.map(({ path }) => path));
215
257
  if (testPaths.size > 0) {
@@ -252,9 +294,7 @@ async function buildJestTargets(configFilePath, projectRoot, options, context, p
252
294
  outputs,
253
295
  options: {
254
296
  cwd: projectRoot,
255
- env: {
256
- TS_NODE_COMPILER_OPTIONS: '{"moduleResolution":"node10"}',
257
- },
297
+ env: { TS_NODE_COMPILER_OPTIONS: tsNodeCompilerOptions },
258
298
  },
259
299
  metadata: {
260
300
  technologies: ['jest'],
@@ -401,32 +441,19 @@ function requireJestUtil(packageName, projectRoot, workspaceRoot) {
401
441
  }
402
442
  async function getTestPaths(projectRoot, rawConfig, absConfigFilePath, context, presetCache) {
403
443
  const testMatch = await getJestOption(rawConfig, absConfigFilePath, 'testMatch', presetCache);
404
- if (testMatch) {
405
- return await (0, workspace_context_1.globWithWorkspaceContext)(context.workspaceRoot, testMatch.map((pattern) => (0, path_1.join)(projectRoot, pattern)), []);
406
- }
407
- else {
408
- const testRegex = await getJestOption(rawConfig, absConfigFilePath, 'testRegex', presetCache);
409
- if (testRegex) {
410
- const files = [];
411
- const testRegexes = Array.isArray(rawConfig.testRegex)
412
- ? rawConfig.testRegex.map((r) => new RegExp(r))
413
- : [new RegExp(rawConfig.testRegex)];
414
- const projectFiles = await (0, workspace_context_1.getFilesInDirectoryUsingContext)(context.workspaceRoot, projectRoot);
415
- for (const file of projectFiles) {
416
- if (testRegexes.some((r) => r.test(file)))
417
- files.push(file);
418
- }
419
- return files;
420
- }
421
- else {
422
- // Default copied from https://github.com/jestjs/jest/blob/d1a2ed7/packages/jest-config/src/Defaults.ts#L84
423
- const defaultTestMatch = [
424
- '**/__tests__/**/*.?([mc])[jt]s?(x)',
425
- '**/?(*.)+(spec|test).?([mc])[jt]s?(x)',
426
- ];
427
- return await (0, workspace_context_1.globWithWorkspaceContext)(context.workspaceRoot, defaultTestMatch.map((pattern) => (0, path_1.join)(projectRoot, pattern)), []);
428
- }
444
+ let paths = await (0, workspace_context_1.globWithWorkspaceContext)(context.workspaceRoot, (testMatch || [
445
+ // Default copied from https://github.com/jestjs/jest/blob/d1a2ed7/packages/jest-config/src/Defaults.ts#L84
446
+ '**/__tests__/**/*.?([mc])[jt]s?(x)',
447
+ '**/?(*.)+(spec|test).?([mc])[jt]s?(x)',
448
+ ]).map((pattern) => (0, path_1.join)(projectRoot, pattern)), []);
449
+ const testRegex = await getJestOption(rawConfig, absConfigFilePath, 'testRegex', presetCache);
450
+ if (testRegex) {
451
+ const testRegexes = Array.isArray(rawConfig.testRegex)
452
+ ? rawConfig.testRegex.map((r) => new RegExp(r))
453
+ : [new RegExp(rawConfig.testRegex)];
454
+ paths = paths.filter((path) => testRegexes.some((r) => r.test(path)));
429
455
  }
456
+ return paths;
430
457
  }
431
458
  async function getJestOption(rawConfig, absConfigFilePath, optionName, presetCache) {
432
459
  if (rawConfig[optionName])