Skip to content

Commit 6fd8222

Browse files
authored
Merge pull request #962 from hoverinc/jr.feature/swc-test
✨ Add debug output for SWC and automatic Jest module mapping
2 parents 4b58aae + 9f782b9 commit 6fd8222

13 files changed

+3260
-2113
lines changed

Diff for: .yarn/releases/yarn-3.6.4.cjs

-874
This file was deleted.

Diff for: .yarn/releases/yarn-3.8.6.cjs

+875
Large diffs are not rendered by default.

Diff for: .yarnrc.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ plugins:
1313
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
1414
spec: '@yarnpkg/plugin-workspace-tools'
1515

16-
yarnPath: .yarn/releases/yarn-3.6.4.cjs
16+
yarnPath: .yarn/releases/yarn-3.8.6.cjs

Diff for: package.json

+20-20
Original file line numberDiff line numberDiff line change
@@ -50,33 +50,33 @@
5050
"@commitlint/cli": "^17.8.1",
5151
"@commitlint/config-conventional": "^17.8.1",
5252
"@commitlint/prompt": "^17.8.1",
53-
"@swc-node/jest": "^1.5.6",
54-
"@swc/core": "^1.3.102",
55-
"@swc/helpers": "^0.5.3",
53+
"@swc-node/jest": "^1.8.12",
54+
"@swc/core": "^1.10.18",
5655
"@types/jest": "^29.5.4",
57-
"@types/lodash.has": "^4.5.8",
56+
"@types/lodash.has": "^4.5.9",
5857
"@types/mkdirp": "^1.0.2",
59-
"@types/node": "^18.18.6",
58+
"@types/node": "^18.19.61",
6059
"@types/rimraf": "^3.0.2",
6160
"@types/which": "^2.0.2",
6261
"@typescript-eslint/eslint-plugin": "^5.62.0",
6362
"@typescript-eslint/parser": "^5.62.0",
64-
"commitizen": "^4.3.0",
63+
"commitizen": "^4.3.1",
6564
"concurrently": "^7.6.0",
6665
"cosmiconfig": "^8.3.6",
6766
"cross-env": "^7.0.3",
6867
"cross-spawn": "^7.0.3",
68+
"debug": "^4.4.0",
6969
"doctoc": "^2.2.1",
70-
"eslint": "^8.52.0",
70+
"eslint": "^8.57.1",
7171
"eslint-config-airbnb": "19.0.4",
7272
"eslint-config-airbnb-typescript": "^17.1.0",
7373
"eslint-config-prettier": "^8.10.0",
74-
"eslint-plugin-import": "^2.29.0",
75-
"eslint-plugin-jest": "^27.4.3",
76-
"eslint-plugin-jsx-a11y": "^6.7.1",
74+
"eslint-plugin-import": "^2.31.0",
75+
"eslint-plugin-jest": "^27.9.0",
76+
"eslint-plugin-jsx-a11y": "^6.10.2",
7777
"eslint-plugin-prettier": "^4.2.1",
78-
"eslint-plugin-react": "^7.33.2",
79-
"eslint-plugin-react-hooks": "^4.6.0",
78+
"eslint-plugin-react": "^7.37.2",
79+
"eslint-plugin-react-hooks": "^4.6.2",
8080
"glob": "^8.1.0",
8181
"is-ci": "^3.0.1",
8282
"jest": "^29.5.0",
@@ -90,8 +90,8 @@
9090
"prettier": "^2.8.8",
9191
"read-pkg-up": "^7.0.1",
9292
"rimraf": "^4.1.1",
93-
"swc_mut_cjs_exports": "^0.86.17",
94-
"tslib": "^2.6.2",
93+
"swc_mut_cjs_exports": "^8.0.1",
94+
"tslib": "^2.8.0",
9595
"typescript": "^4.9.5",
9696
"which": "^3.0.0",
9797
"yargs-parser": "^21.1.1"
@@ -129,12 +129,12 @@
129129
},
130130
"homepage": "https://github.com/hoverinc/hover-javascript#readme",
131131
"devDependencies": {
132-
"@babel/cli": "^7.23.0",
133-
"@babel/core": "^7.23.2",
134-
"@babel/preset-env": "^7.23.2",
132+
"@babel/cli": "^7.25.9",
133+
"@babel/core": "^7.26.0",
134+
"@babel/preset-env": "^7.26.0",
135135
"@babel/preset-typescript": "^7.23.3",
136-
"@types/cross-spawn": "^6.0.4",
137-
"@types/lodash.merge": "^4",
136+
"@types/cross-spawn": "^6.0.6",
137+
"@types/debug": "^4",
138138
"depcheck": "^1.4.7",
139139
"eslint-config-kentcdodds": "^20.5.0",
140140
"husky": "^8.0.3",
@@ -150,5 +150,5 @@
150150
"optional": true
151151
}
152152
},
153-
"packageManager": "yarn@3.6.4"
153+
"packageManager": "yarn@3.8.6"
154154
}

Diff for: src/api/test.ts

-1
This file was deleted.

Diff for: src/api/test/__tests__/paths-to-module-name-mapper.ts renamed to src/api/test/__tests__/paths-to-module-name-mapper.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {pathsToModuleNameMapper} from '../paths-to-module-name-mapper'
1+
const pathsToModuleNameMapper = require('../paths-to-module-name-mapper')
22

33
const tsconfigMap = {
44
log: ['src/utils/log'],

Diff for: src/api/test/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
pathsToModuleNameMapper: require('./paths-to-module-name-mapper'),
3+
}

Diff for: src/api/test/index.ts

-1
This file was deleted.

Diff for: src/api/test/paths-to-module-name-mapper.ts renamed to src/api/test/paths-to-module-name-mapper.js

+27-16
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,38 @@
55
* @see {@link https://github.com/kulshekhar/ts-jest/blob/dd3523cb7571714f06f1ea2ed1e3cf11970fbfce/src/config/paths-to-module-name-mapper.ts}
66
*/
77

8-
import type {Config} from '@jest/types'
9-
import type {CompilerOptions} from 'typescript'
10-
11-
type TsPathMapping = Exclude<CompilerOptions['paths'], undefined>
12-
type JestPathMapping = Config.InitialOptions['moduleNameMapper']
8+
/**
9+
* We don't need to escape all chars, so commented out is the real one
10+
* const escapeRegex = (str: string) => str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
11+
*
12+
* @param {string} str
13+
* @returns {string}
14+
*/
15+
const escapeRegex = str => str.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&')
1316

14-
// we don't need to escape all chars, so commented out is the real one
15-
// const escapeRegex = (str: string) => str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
16-
const escapeRegex = (str: string) => str.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&')
17+
/**
18+
* @typedef {Exclude<import('typescript').CompilerOptions['paths'], undefined>} TsPathMapping
19+
* @typedef {import('@jest/types').Config.InitialOptions['moduleNameMapper']} JestPathMapping
20+
*/
1721

18-
export const pathsToModuleNameMapper = (
19-
mapping: TsPathMapping,
20-
{prefix = '', useESM = false}: {prefix?: string; useESM?: boolean} = {},
21-
): JestPathMapping => {
22-
const jestMap: JestPathMapping = {}
22+
/**
23+
* Converts TypeScript path mappings to Jest module name mappings
24+
*
25+
* @param {TsPathMapping} mapping - The TypeScript path mapping object
26+
* @param {{prefix?: string, useESM?: boolean}} options - Configuration options
27+
* @returns {JestPathMapping}
28+
*/
29+
const pathsToModuleNameMapper = (
30+
mapping,
31+
{prefix = '', useESM = false} = {},
32+
) => {
33+
/** @type {JestPathMapping} */
34+
const jestMap = {}
2335
for (const fromPath of Object.keys(mapping)) {
2436
const toPaths = mapping[fromPath]
2537
// check that we have only one target path
2638
if (toPaths.length === 0) {
2739
console.warn(`Not mapping "${fromPath}" because it has no target.`)
28-
2940
continue
3041
}
3142

@@ -35,7 +46,6 @@ export const pathsToModuleNameMapper = (
3546
const paths = toPaths.map(target => {
3647
const enrichedPrefix =
3748
prefix !== '' && !prefix.endsWith('/') ? `${prefix}/` : prefix
38-
3949
return `${enrichedPrefix}${target}`
4050
})
4151
const cjsPattern = `^${escapeRegex(fromPath)}$`
@@ -48,7 +58,6 @@ export const pathsToModuleNameMapper = (
4858
: target
4959
const enrichedPrefix =
5060
prefix !== '' && !prefix.endsWith('/') ? `${prefix}/` : prefix
51-
5261
return `${enrichedPrefix}${enrichedTarget.replace(/\*/g, '$1')}`
5362
})
5463
if (useESM) {
@@ -74,3 +83,5 @@ export const pathsToModuleNameMapper = (
7483

7584
return jestMap
7685
}
86+
87+
module.exports = pathsToModuleNameMapper

Diff for: src/config/jest.config.js

+98-49
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ const {
77
readDefaultTsConfig,
88
tsCompilerOptionsToSwcConfig,
99
} = require('@swc-node/register/read-default-tsconfig')
10-
const {ifAnyDep, hasFile, fromRoot, hasDevDep} = require('../utils')
10+
11+
const {ifAnyDep, hasFile, fromRoot, hasDevDep, getDebug} = require('../utils')
12+
const {pathsToModuleNameMapper} = require('../api/test')
1113

1214
const {
1315
testMatch,
1416
testMatchGlob,
1517
testMatchExtensions,
1618
} = require('./helpers/test-match')
1719

20+
const debug = getDebug('jest')
21+
1822
const ignores = [
1923
'/node_modules/',
2024
'/__fixtures__/',
@@ -24,12 +28,48 @@ const ignores = [
2428
'__mocks__',
2529
]
2630

31+
/**
32+
* @type {SwcNodeOptions}
33+
*/
34+
const DEFAULT_SWC_OPTIONS = {
35+
esModuleInterop: true,
36+
module: 'commonjs',
37+
react: {runtime: 'automatic'},
38+
swc: {
39+
jsc: {
40+
target: 'es2020',
41+
experimental: {
42+
plugins: [[require.resolve('swc_mut_cjs_exports'), {}]],
43+
},
44+
parser: {
45+
syntax: 'typescript',
46+
tsx: true,
47+
decorators: false,
48+
dynamicimport: true,
49+
},
50+
loose: true,
51+
externalHelpers: false,
52+
transform: {
53+
react: {
54+
runtime: 'automatic',
55+
},
56+
},
57+
},
58+
},
59+
}
60+
61+
const tsConfig = readDefaultTsConfig()
62+
const swcConfig = merge(
63+
tsCompilerOptionsToSwcConfig(tsConfig, ''),
64+
DEFAULT_SWC_OPTIONS,
65+
)
66+
67+
debug.prefix('tsconfig:paths')(tsConfig.paths)
68+
2769
/**
2870
* Get the path at which `@hover/javascript/jest` is installed in a dependent
2971
* project in order to resolve the Jest preset as sometimes package managers
3072
* nest the preset installation within the `@hover/javascript` installation.
31-
*
32-
* @returns
3373
*/
3474
const getResolvePaths = () => {
3575
try {
@@ -41,6 +81,54 @@ const getResolvePaths = () => {
4181
}
4282
}
4383

84+
/**
85+
* The default transform is now SWC, however, `ts-jest` will
86+
* still be used if it is installed in the host project
87+
*
88+
* @returns {JestConfig['transform']}
89+
*/
90+
const getTransform = () => {
91+
const log = debug.prefix('transform')
92+
93+
if (hasDevDep('ts-jest')) {
94+
log('Detected `ts-jest` package, using ts-jest transform')
95+
96+
const transform = Object.fromEntries(
97+
// Ensure we can resolve the preset even when
98+
// it's in a nested `node_modules` installation
99+
Object.entries(require('ts-jest/presets').jsWithTs.transform).map(
100+
([glob, [transformer, options]]) => [
101+
glob,
102+
[
103+
require.resolve(transformer),
104+
{
105+
...options,
106+
diagnostics: false,
107+
},
108+
],
109+
],
110+
),
111+
)
112+
113+
log(transform)
114+
115+
return transform
116+
}
117+
118+
log('No `ts-jest` package detected, using default SWC transform')
119+
120+
const transform = {
121+
'^.+\\.(t|j|mj)sx?$': [
122+
require.resolve('@swc-node/jest', getResolvePaths()),
123+
swcConfig,
124+
],
125+
}
126+
127+
log(transform)
128+
129+
return transform
130+
}
131+
44132
/** @type JestConfig */
45133
const jestConfig = {
46134
roots: [fromRoot('.')],
@@ -55,52 +143,13 @@ const jestConfig = {
55143
testMatch,
56144
testPathIgnorePatterns: [...ignores, '<rootDir>/dist'],
57145
testLocationInResults: true,
58-
// The default transform is now SWC, however, `ts-jest` will
59-
// still be used if it is installed in the host project
60-
transform: hasDevDep('ts-jest')
61-
? Object.fromEntries(
62-
// Ensure we can resolve the preset even when
63-
// it's in a nested `node_modules` installation
64-
Object.entries(require('ts-jest/presets').jsWithTs.transform).map(
65-
([glob, [transformer, options]]) => [
66-
glob,
67-
[
68-
require.resolve(transformer),
69-
{
70-
...options,
71-
diagnostics: false,
72-
},
73-
],
74-
],
75-
),
76-
)
77-
: {
78-
'^.+\\.(t|j)sx?$': [
79-
require.resolve('@swc-node/jest', getResolvePaths()),
80-
/** @type {SwcNodeOptions} */ (
81-
merge(tsCompilerOptionsToSwcConfig(readDefaultTsConfig(), ''), {
82-
esModuleInterop: true,
83-
module: 'commonjs',
84-
swc: {
85-
jsc: {
86-
target: 'es2020',
87-
experimental: {
88-
plugins: [[require.resolve('swc_mut_cjs_exports'), {}]],
89-
},
90-
parser: {
91-
syntax: 'typescript',
92-
tsx: true,
93-
decorators: false,
94-
dynamicimport: true,
95-
},
96-
loose: true,
97-
externalHelpers: false,
98-
},
99-
},
100-
})
101-
),
102-
],
103-
},
146+
moduleNameMapper: debug.trace(
147+
pathsToModuleNameMapper(
148+
debug.trace(swcConfig.paths, 'moduleNameMapper:paths'),
149+
),
150+
'moduleNameMapper',
151+
),
152+
transform: getTransform(),
104153
coveragePathIgnorePatterns: [
105154
...ignores,
106155
'src/(umd|cjs|esm)-entry.js$',

0 commit comments

Comments
 (0)