ts-jest 29.4.7 → 29.4.8
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/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/.opencode/archive/sessions/ses_2bfd9d52bffeFFWDJAjOaEp37I/.last.sha256 +0 -1
- package/.opencode/archive/sessions/ses_2bfd9d52bffeFFWDJAjOaEp37I/2026-03-31T07-18-10.759Z.json +0 -14293
- package/.opencode/archive/sessions/ses_2bfd9d52bffeFFWDJAjOaEp37I/2026-03-31T07-18-10.759Z.md +0 -13422
- package/.opencode/plans/pr1.md +0 -337
- package/.opencode/plans/pr2.md +0 -280
- package/.opencode/plans/pr3.md +0 -127
- package/.opencode/plans/pr4.md +0 -229
- package/.opencode/plans/pr5.md +0 -211
- package/.opencode/plans/pr6.md +0 -178
package/.opencode/plans/pr3.md
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
# PR 3 — Compiler Factory + CompilerFactoryInterface
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Introduces `CompilerFactoryInterface` and `CompilerFactory` — the single routing point that decides
|
|
6
|
-
which compiler implementation to instantiate based on resolved config.
|
|
7
|
-
|
|
8
|
-
**User-visible change**: NO
|
|
9
|
-
**Depends on**: PR 1 (`ResolvedConfigInterface`, `DiagnosticsMode`), PR 2 (`CompilerInterface`,
|
|
10
|
-
`LanguageServiceCompiler`, `FastTranspileCompiler`)
|
|
11
|
-
**Invariant**: zero changes to any existing file
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Files to Create
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
src/experimental/
|
|
19
|
-
interfaces/
|
|
20
|
-
compiler-factory.interface.ts
|
|
21
|
-
compiler/
|
|
22
|
-
compiler-factory.ts
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Requirements
|
|
28
|
-
|
|
29
|
-
### `src/experimental/interfaces/compiler-factory.interface.ts`
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
interface CompilerFactoryInterface {
|
|
33
|
-
create(config: ResolvedConfigInterface, runtimeCacheFS: Map<string, string>): CompilerInterface
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Single method. `runtimeCacheFS` is a mutable map shared at runtime for in-memory file content
|
|
38
|
-
(used by `LanguageServiceCompiler`; ignored by `FastTranspileCompiler`).
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
### `src/experimental/compiler/compiler-factory.ts`
|
|
43
|
-
|
|
44
|
-
**Routing logic**:
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
config.diagnosticsMode === 'disabled'
|
|
48
|
-
→ FastTranspileCompiler(config)
|
|
49
|
-
|
|
50
|
-
config.diagnosticsMode !== 'disabled'
|
|
51
|
-
→ LanguageServiceCompiler(config, runtimeCacheFS)
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
`diagnosticsMode` is derived in PR 1 (`diagnostics-config.ts`) from `noCheck` in the resolved
|
|
55
|
-
tsconfig. The factory does not inspect `noCheck` directly — it reads the already-derived mode from
|
|
56
|
-
`config.diagnosticsMode`.
|
|
57
|
-
|
|
58
|
-
**SWC stub**:
|
|
59
|
-
|
|
60
|
-
```ts
|
|
61
|
-
case 'swc':
|
|
62
|
-
throw new NotImplementedError(
|
|
63
|
-
'SWC compiler support is not yet implemented. ' +
|
|
64
|
-
'Track progress at https://github.com/kulshekhar/ts-jest/issues/XXXX'
|
|
65
|
-
)
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
**esbuild stub**:
|
|
69
|
-
|
|
70
|
-
```ts
|
|
71
|
-
case 'esbuild':
|
|
72
|
-
throw new NotImplementedError(
|
|
73
|
-
'esbuild compiler support is not yet implemented. ' +
|
|
74
|
-
'Track progress at https://github.com/kulshekhar/ts-jest/issues/XXXX'
|
|
75
|
-
)
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
SWC/esbuild stubs are behind a `compilerBackend` option (reserved for future use, not yet in
|
|
79
|
-
`TsJestTransformerOptions`). Currently unreachable in normal flow.
|
|
80
|
-
|
|
81
|
-
**`NotImplementedError`**:
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
class NotImplementedError extends Error {
|
|
85
|
-
constructor(message: string) {
|
|
86
|
-
super(message)
|
|
87
|
-
this.name = 'NotImplementedError'
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
|
-
## Import Rules
|
|
95
|
-
|
|
96
|
-
- `src/experimental/interfaces/*` — allowed
|
|
97
|
-
- `src/experimental/compiler/language-service-compiler.ts` — allowed
|
|
98
|
-
- `src/experimental/compiler/fast-transpile-compiler.ts` — allowed
|
|
99
|
-
- `src/utils/*` — allowed
|
|
100
|
-
- `src/legacy/**` — FORBIDDEN
|
|
101
|
-
- All other `src/` folders — FORBIDDEN
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## Test Requirements
|
|
106
|
-
|
|
107
|
-
Unit tests under `src/experimental/compiler/__tests__/`.
|
|
108
|
-
|
|
109
|
-
| Test file | Key scenarios |
|
|
110
|
-
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
111
|
-
| `compiler-factory.spec.ts` | `diagnosticsMode: 'disabled'` → returns `FastTranspileCompiler`; `diagnosticsMode: 'full'` → returns `LanguageServiceCompiler`; `diagnosticsMode: 'syntactic'` → returns `LanguageServiceCompiler`; SWC stub throws `NotImplementedError`; esbuild stub throws `NotImplementedError` |
|
|
112
|
-
|
|
113
|
-
**Existing tests must remain green** (no changes to legacy):
|
|
114
|
-
|
|
115
|
-
- `src/legacy/compiler/ts-jest-compiler.spec.ts`
|
|
116
|
-
- Legacy `TsJestCompiler` still uses `TsCompiler` directly — untouched
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
## Acceptance Criteria
|
|
121
|
-
|
|
122
|
-
- [ ] All new unit tests pass
|
|
123
|
-
- [ ] `ts-jest-compiler.spec.ts` unmodified and green
|
|
124
|
-
- [ ] `tsc --noEmit` passes
|
|
125
|
-
- [ ] Factory correctly routes based on `diagnosticsMode`
|
|
126
|
-
- [ ] SWC and esbuild stubs throw `NotImplementedError` with issue tracker link
|
|
127
|
-
- [ ] Every exported symbol has TSDoc
|
package/.opencode/plans/pr4.md
DELETED
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
# PR 4 — Transformer Concern Modules
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Extracts the five discrete concerns from `TsJestTransformer` into focused, independently testable
|
|
6
|
-
modules. This PR produces no user-facing changes — it is groundwork for the new transformer in PR 5.
|
|
7
|
-
|
|
8
|
-
**User-visible change**: NO
|
|
9
|
-
**Depends on**: PR 1 (`ResolvedConfigInterface`), PR 2 (`CompilerInterface`), PR 3 (`CompilerFactoryInterface`)
|
|
10
|
-
**Invariant**: zero changes to any existing file
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## Files to Create
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
src/experimental/transformer/
|
|
18
|
-
cache-store.ts
|
|
19
|
-
file-processor.ts
|
|
20
|
-
cache-key-builder.ts
|
|
21
|
-
hooks-runner.ts
|
|
22
|
-
babel-pipeline.ts
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Requirements
|
|
28
|
-
|
|
29
|
-
### `src/experimental/transformer/cache-store.ts`
|
|
30
|
-
|
|
31
|
-
Replaces the static `_cachedConfigSets: CachedConfigSet[]` array in `TsJestTransformer`.
|
|
32
|
-
|
|
33
|
-
**Purpose**: deduplicates `ResolvedConfig` instances across `getCacheKey` and `process` calls
|
|
34
|
-
(Jest sometimes splits these calls across separate invocations with the same config but different
|
|
35
|
-
object references).
|
|
36
|
-
|
|
37
|
-
**2-pass deduplication**:
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
Pass 1 — object identity:
|
|
41
|
-
Compare jestConfig reference (===)
|
|
42
|
-
→ Hit: return cached ResolvedConfig
|
|
43
|
-
|
|
44
|
-
Pass 2 — serialised string:
|
|
45
|
-
Serialise jestConfig (excluding cacheDirectory) + cacheSuffix
|
|
46
|
-
→ Hit: update stored .value reference, return cached ResolvedConfig
|
|
47
|
-
|
|
48
|
-
Miss:
|
|
49
|
-
Merge transformerOptions into config.globals['ts-jest']
|
|
50
|
-
Create new ResolvedConfig
|
|
51
|
-
Compute _transformCfgStr = serialize(jest-without-cacheDirectory) + cacheSuffix
|
|
52
|
-
Detect watch = process.argv.includes('--watch')
|
|
53
|
-
Store and return
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
```ts
|
|
57
|
-
class CacheStore {
|
|
58
|
-
private _entries: CacheEntry[]
|
|
59
|
-
|
|
60
|
-
getOrCreate(
|
|
61
|
-
jestConfig: Config.ProjectConfig,
|
|
62
|
-
transformerOptions: TsJestTransformerOptions,
|
|
63
|
-
factory: (config: Config.ProjectConfig, options: TsJestTransformerOptions) => ResolvedConfigInterface,
|
|
64
|
-
): ResolvedConfigInterface
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Thread-safety: not required (Node.js single-threaded; Jest workers are separate processes).
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
### `src/experimental/transformer/file-processor.ts`
|
|
73
|
-
|
|
74
|
-
Implements the 5-branch file dispatch logic extracted from `processWithTs()`.
|
|
75
|
-
|
|
76
|
-
```ts
|
|
77
|
-
function processFile(
|
|
78
|
-
fileContent: string,
|
|
79
|
-
filePath: string,
|
|
80
|
-
options: ProcessFileOptions,
|
|
81
|
-
config: ResolvedConfigInterface,
|
|
82
|
-
compiler: CompilerInterface,
|
|
83
|
-
): CompiledOutput
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
**5 branches in exact order** (first matching branch wins):
|
|
87
|
-
|
|
88
|
-
1. `config.shouldStringifyContent(filePath)`
|
|
89
|
-
→ `{ code: \`module.exports=\${JSON.stringify(fileContent)}\` }`
|
|
90
|
-
|
|
91
|
-
2. `filePath.endsWith('.d.ts')`
|
|
92
|
-
→ `{ code: '' }`
|
|
93
|
-
|
|
94
|
-
3. `isNodeModule(filePath) && (isJs(filePath) || isJsx(filePath))`
|
|
95
|
-
→ fast `ts.transpileModule` with `{ module: useESM && supportsStaticESM ? ESNext : CommonJS }`
|
|
96
|
-
|
|
97
|
-
- `isNodeModule`: `filePath.includes(path.sep + 'node_modules' + path.sep)`
|
|
98
|
-
|
|
99
|
-
4. `isJs(filePath) || isJsx(filePath) || isTs(filePath) || isTsx(filePath)` (not node_modules)
|
|
100
|
-
→ `compiler.getCompiledOutput(fileContent, filePath, compileOptions)`
|
|
101
|
-
|
|
102
|
-
5. Fallback
|
|
103
|
-
→ warn "unknown extension, returning as-is" + return `{ code: fileContent }`
|
|
104
|
-
|
|
105
|
-
---
|
|
106
|
-
|
|
107
|
-
### `src/experimental/transformer/cache-key-builder.ts`
|
|
108
|
-
|
|
109
|
-
Pure function — `fs.statSync` injected for testability.
|
|
110
|
-
|
|
111
|
-
```ts
|
|
112
|
-
function buildCacheKey(
|
|
113
|
-
fileContent: string,
|
|
114
|
-
filePath: string,
|
|
115
|
-
options: CacheKeyOptions,
|
|
116
|
-
config: ResolvedConfigInterface,
|
|
117
|
-
compiler: CompilerInterface,
|
|
118
|
-
statSync: (path: string) => { mtimeMs: number },
|
|
119
|
-
): string
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
**Elements hashed in exact order**:
|
|
123
|
-
|
|
124
|
-
1. `_transformCfgStr` (from `CacheStore` entry)
|
|
125
|
-
2. `config.rootDir`
|
|
126
|
-
3. `instrument ? 'instrument:on' : 'instrument:off'`
|
|
127
|
-
4. `supportsStaticESM ? 'esm:on' : 'esm:off'`
|
|
128
|
-
5. `fileContent`
|
|
129
|
-
6. `filePath`
|
|
130
|
-
7. If `!config.isolatedModules && config.tsCacheDir`:
|
|
131
|
-
- For each module in `compiler.getResolvedModules(fileContent, filePath, runtimeCacheFS)`:
|
|
132
|
-
- `moduleName`
|
|
133
|
-
- `statSync(moduleName).mtimeMs.toString()`
|
|
134
|
-
|
|
135
|
-
Returns `sha1(elements.join(''))`.
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
### `src/experimental/transformer/hooks-runner.ts`
|
|
140
|
-
|
|
141
|
-
Encapsulates `TS_JEST_HOOKS` reading and invocation.
|
|
142
|
-
|
|
143
|
-
```ts
|
|
144
|
-
function runAfterProcessHook(
|
|
145
|
-
result: TransformResult,
|
|
146
|
-
fileContent: string,
|
|
147
|
-
filePath: string,
|
|
148
|
-
jestOptions: TransformOptions,
|
|
149
|
-
): TransformResult
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
**Behaviour**:
|
|
153
|
-
|
|
154
|
-
- Read `process.env.TS_JEST_HOOKS` **at call time** (not at module load time)
|
|
155
|
-
- If not set → return `result` as-is
|
|
156
|
-
- Resolve hook file path relative to `process.cwd()`
|
|
157
|
-
- `require()` the hooks module
|
|
158
|
-
- Call `hooksModule.afterProcess(result, fileContent, filePath, jestOptions)`
|
|
159
|
-
- If return value is truthy → return it
|
|
160
|
-
- Else → return original `result`
|
|
161
|
-
|
|
162
|
-
This matches the legacy behavior in `TsJestTransformer.runTsJestHook()` exactly.
|
|
163
|
-
|
|
164
|
-
---
|
|
165
|
-
|
|
166
|
-
### `src/experimental/transformer/babel-pipeline.ts`
|
|
167
|
-
|
|
168
|
-
Shared sync + async Babel transformation logic.
|
|
169
|
-
|
|
170
|
-
```ts
|
|
171
|
-
function transformWithBabel(
|
|
172
|
-
result: CompiledOutput,
|
|
173
|
-
filePath: string,
|
|
174
|
-
options: TransformOptions,
|
|
175
|
-
babelJestTransformer: BabelTransformer,
|
|
176
|
-
): TransformResult
|
|
177
|
-
|
|
178
|
-
async function transformWithBabelAsync(
|
|
179
|
-
result: CompiledOutput,
|
|
180
|
-
filePath: string,
|
|
181
|
-
options: TransformOptions,
|
|
182
|
-
babelJestTransformer: BabelTransformer,
|
|
183
|
-
): Promise<TransformResult>
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
**Requirements**:
|
|
187
|
-
|
|
188
|
-
- Force `instrument: false` when passing to `babelJestTransformer.process()` / `processAsync()`
|
|
189
|
-
(instrumentation is handled by Jest, not Babel)
|
|
190
|
-
- Pass `result.code` + `result.map` as the content to transform
|
|
191
|
-
- Return the Babel transformer's output directly
|
|
192
|
-
|
|
193
|
-
---
|
|
194
|
-
|
|
195
|
-
## Import Rules
|
|
196
|
-
|
|
197
|
-
- `src/experimental/interfaces/*` — allowed
|
|
198
|
-
- `src/experimental/config/*` — allowed
|
|
199
|
-
- `src/experimental/compiler/*` — allowed
|
|
200
|
-
- `src/utils/*` — allowed (sha1, logger, ts-error)
|
|
201
|
-
- `src/types.ts` — allowed
|
|
202
|
-
- `src/legacy/**` — FORBIDDEN
|
|
203
|
-
- All other `src/` folders — FORBIDDEN
|
|
204
|
-
|
|
205
|
-
---
|
|
206
|
-
|
|
207
|
-
## Test Requirements
|
|
208
|
-
|
|
209
|
-
Unit tests under `src/experimental/transformer/__tests__/`.
|
|
210
|
-
|
|
211
|
-
| Test file | Key scenarios |
|
|
212
|
-
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
213
|
-
| `cache-store.spec.ts` | identity hit returns same instance; serialised string hit returns same instance and updates reference; miss creates new instance; watch detection via process.argv |
|
|
214
|
-
| `file-processor.spec.ts` | each branch fires in correct priority order; stringify branch; .d.ts returns empty; node_module JS → fast transpile; TS/TSX → compiler; unknown ext → warn + passthrough |
|
|
215
|
-
| `cache-key-builder.spec.ts` | same inputs → same key; any single input change → different key; isolated mode skips module mtimes; non-isolated includes module mtime |
|
|
216
|
-
| `hooks-runner.spec.ts` | no TS_JEST_HOOKS env → passthrough; env set → requires module + calls afterProcess; truthy return → used; falsy return → original used; env read at call time not load time |
|
|
217
|
-
| `babel-pipeline.spec.ts` | calls babelJest with instrument:false; async variant works; result code passed through |
|
|
218
|
-
|
|
219
|
-
---
|
|
220
|
-
|
|
221
|
-
## Acceptance Criteria
|
|
222
|
-
|
|
223
|
-
- [ ] All new unit tests pass
|
|
224
|
-
- [ ] All existing legacy tests unmodified and green
|
|
225
|
-
- [ ] `tsc --noEmit` passes
|
|
226
|
-
- [ ] No import from `src/legacy/`
|
|
227
|
-
- [ ] `cache-key-builder.ts` has no direct `fs` import — `statSync` is injected
|
|
228
|
-
- [ ] `hooks-runner.ts` reads `TS_JEST_HOOKS` at call time, not at module load time
|
|
229
|
-
- [ ] Every exported symbol has TSDoc
|
package/.opencode/plans/pr5.md
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
# PR 5 — New TsJestTransformer + `future.v30_newTransformer` Opt-in
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Assembles all experimental modules into a new `TsJestTransformer` class and wires it behind the
|
|
6
|
-
`future.v30_newTransformer` opt-in flag. Users explicitly opt in by adding the flag to their Jest
|
|
7
|
-
config. The legacy transformer continues to be the default.
|
|
8
|
-
|
|
9
|
-
**User-visible change**: YES — opt-in only via `future: { v30_newTransformer: true }`
|
|
10
|
-
**Depends on**: PR 1, PR 2, PR 3, PR 4
|
|
11
|
-
**Existing file changes**: minimal, additive only (`src/types.ts`, `src/index.ts`)
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Files to Create
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
src/experimental/transformer/
|
|
19
|
-
transformer.ts
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Files to Modify (additive only)
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
src/types.ts — add optional future field to TsJestTransformerOptions
|
|
28
|
-
src/index.ts — createTransformer branches on future.v30_newTransformer
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Requirements
|
|
34
|
-
|
|
35
|
-
### `src/experimental/transformer/transformer.ts`
|
|
36
|
-
|
|
37
|
-
Clean-room `SyncTransformer<TsJestTransformerOptions>` implementation. No static state, no
|
|
38
|
-
`process.env` writes, all I/O injected.
|
|
39
|
-
|
|
40
|
-
**Constructor**:
|
|
41
|
-
|
|
42
|
-
```ts
|
|
43
|
-
constructor(
|
|
44
|
-
tsJestConfig?: TsJestTransformerOptions,
|
|
45
|
-
private readonly _fs: Pick<typeof fs, 'statSync'> = fs,
|
|
46
|
-
)
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
- No `process.env.TS_JEST = '1'` (removed in v30)
|
|
50
|
-
- `_cacheStore = new CacheStore()`
|
|
51
|
-
- `_runtimeCacheFS = new Map<string, string>()`
|
|
52
|
-
|
|
53
|
-
**`_configsFor(jestConfig)`** (private):
|
|
54
|
-
|
|
55
|
-
- Delegates to `_cacheStore.getOrCreate(jestConfig, this._tsJestConfig, ResolvedConfig)`
|
|
56
|
-
- Returns `{ config: ResolvedConfigInterface, compiler: CompilerInterface }`
|
|
57
|
-
- `compiler` created lazily via `CompilerFactory.create(config, _runtimeCacheFS)`
|
|
58
|
-
- Compiler is cached per config entry (same config → same compiler instance)
|
|
59
|
-
|
|
60
|
-
**`process(fileContent, filePath, jestOptions)`**:
|
|
61
|
-
|
|
62
|
-
1. `{ config, compiler } = _configsFor(jestOptions.transformerConfig ?? {})`
|
|
63
|
-
2. `result = processFile(fileContent, filePath, jestOptions, config, compiler)`
|
|
64
|
-
3. If `config.babelJestTransformer`: `result = transformWithBabel(result, filePath, jestOptions, babelJestTransformer)`
|
|
65
|
-
4. `result = runAfterProcessHook(result, fileContent, filePath, jestOptions)`
|
|
66
|
-
5. Return `result`
|
|
67
|
-
|
|
68
|
-
**`processAsync(fileContent, filePath, jestOptions)`**:
|
|
69
|
-
|
|
70
|
-
1. Same as `process` steps 1–2
|
|
71
|
-
2. Collect diagnostics: if `result.diagnostics?.length > 0` → throw `TSError`
|
|
72
|
-
3. If `config.babelJestTransformer`: `result = await transformWithBabelAsync(...)`
|
|
73
|
-
4. `result = runAfterProcessHook(...)`
|
|
74
|
-
5. Return `result`
|
|
75
|
-
|
|
76
|
-
**`getCacheKey(fileContent, filePath, options)`**:
|
|
77
|
-
|
|
78
|
-
- `{ config, compiler } = _configsFor(options.transformerConfig ?? {})`
|
|
79
|
-
- Returns `buildCacheKey(fileContent, filePath, options, config, compiler, this._fs.statSync)`
|
|
80
|
-
|
|
81
|
-
**`getCacheKeyAsync`**: delegates to `getCacheKey` (same logic, wraps in Promise)
|
|
82
|
-
|
|
83
|
-
**Protected extension points** (for subclassing, preserved from legacy):
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
protected _createConfigSet(config: Config.ProjectConfig): ResolvedConfigInterface
|
|
87
|
-
protected _createCompiler(config: ResolvedConfigInterface): CompilerInterface
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
**v30 behavioral differences vs legacy** (only active under this flag):
|
|
91
|
-
|
|
92
|
-
| Scenario | Legacy behavior | v30 behavior |
|
|
93
|
-
| ----------------------------------- | ---------------------------------------------- | ---------------------------------------------------------- |
|
|
94
|
-
| `isolatedModules: true` in tsconfig | routes to `ts.transpileModule` (no type check) | routes to `LanguageServiceCompiler` (type checking works) |
|
|
95
|
-
| `diagnostics: false` | silently disables diagnostics | accepted + deprecation warning pointing to `noCheck: true` |
|
|
96
|
-
| `noCheck: true` in tsconfig | unknown (not supported) | routes to `FastTranspileCompiler` |
|
|
97
|
-
| `process.env.TS_JEST = '1'` | set in constructor | NOT set |
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
### `src/types.ts` — Additive change
|
|
102
|
-
|
|
103
|
-
Add `future` field to `TsJestTransformerOptions` (the existing public type):
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
// Addition only — existing fields unchanged
|
|
107
|
-
future?: {
|
|
108
|
-
/**
|
|
109
|
-
* Opt in to the v30 transformer rewrite.
|
|
110
|
-
* @experimental
|
|
111
|
-
*/
|
|
112
|
-
v30_newTransformer?: boolean
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
No existing fields modified. No existing types removed.
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
### `src/index.ts` — Additive change
|
|
121
|
-
|
|
122
|
-
Branch `createTransformer` on the future flag:
|
|
123
|
-
|
|
124
|
-
```ts
|
|
125
|
-
export const createTransformer = (tsJestConfig?: TsJestTransformerOptions) => {
|
|
126
|
-
if (tsJestConfig?.future?.v30_newTransformer) {
|
|
127
|
-
const { NewTsJestTransformer } = require('./experimental/transformer/transformer')
|
|
128
|
-
return new NewTsJestTransformer(tsJestConfig)
|
|
129
|
-
}
|
|
130
|
-
return new TsJestTransformer(tsJestConfig) // legacy path unchanged
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
The `require()` is intentional: avoids loading any experimental code unless the flag is set.
|
|
135
|
-
No other changes to `src/index.ts`.
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
## Usage (opt-in)
|
|
140
|
-
|
|
141
|
-
```js
|
|
142
|
-
// jest.config.js
|
|
143
|
-
module.exports = {
|
|
144
|
-
transform: {
|
|
145
|
-
'^.+\\.tsx?$': [
|
|
146
|
-
'ts-jest',
|
|
147
|
-
{
|
|
148
|
-
future: { v30_newTransformer: true },
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
},
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
Users not setting this flag get identical behavior to before.
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
## Import Rules
|
|
160
|
-
|
|
161
|
-
### `src/experimental/transformer/transformer.ts`
|
|
162
|
-
|
|
163
|
-
- `src/experimental/**` — all submodules allowed
|
|
164
|
-
- `src/utils/*` — allowed
|
|
165
|
-
- `src/types.ts` — allowed
|
|
166
|
-
- `src/legacy/**` — FORBIDDEN
|
|
167
|
-
|
|
168
|
-
### `src/index.ts` change
|
|
169
|
-
|
|
170
|
-
- Uses `require()` for lazy load of experimental transformer
|
|
171
|
-
- No static import of experimental code at module level
|
|
172
|
-
|
|
173
|
-
---
|
|
174
|
-
|
|
175
|
-
## Test Requirements
|
|
176
|
-
|
|
177
|
-
Unit tests under `src/experimental/transformer/__tests__/`.
|
|
178
|
-
|
|
179
|
-
| Test file | Key scenarios |
|
|
180
|
-
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
181
|
-
| `transformer.spec.ts` | `process` happy path; `processAsync` throws TSError on diagnostics; `getCacheKey` returns stable key; babel pipeline invoked when configured; hooks runner invoked; no `process.env.TS_JEST` set; `isolatedModules: true` → `LanguageServiceCompiler`; `noCheck: true` → `FastTranspileCompiler`; `diagnostics: false` → deprecation warning |
|
|
182
|
-
|
|
183
|
-
Integration (e2e) — add a new e2e project `e2e/v30-transformer/` exercising the new transformer
|
|
184
|
-
via `future: { v30_newTransformer: true }`:
|
|
185
|
-
|
|
186
|
-
| Scenario | e2e test |
|
|
187
|
-
| ------------------------------------ | ---------------------------------------------------------------- |
|
|
188
|
-
| Basic TS compilation | `.spec.ts` with simple import, passes |
|
|
189
|
-
| Type error caught | `.spec.ts` with deliberate type error, jest reports it |
|
|
190
|
-
| `noCheck: true` skips type errors | `.spec.ts` with type error + `noCheck: true` in tsconfig, passes |
|
|
191
|
-
| Source maps work | Source map points to original `.ts` line numbers |
|
|
192
|
-
| `isolatedModules: true` + type error | Type error IS caught (fix for legacy bug) |
|
|
193
|
-
|
|
194
|
-
**All existing tests must remain green** — legacy path is completely unchanged.
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
## Acceptance Criteria
|
|
199
|
-
|
|
200
|
-
- [ ] All new unit tests pass
|
|
201
|
-
- [ ] New e2e tests pass
|
|
202
|
-
- [ ] All existing unit + e2e tests unmodified and green
|
|
203
|
-
- [ ] `tsc --noEmit` passes
|
|
204
|
-
- [ ] `process.env.TS_JEST` is NOT set by new transformer
|
|
205
|
-
- [ ] `future.v30_newTransformer: false` or absent → legacy `TsJestTransformer` used (no regression)
|
|
206
|
-
- [ ] `future.v30_newTransformer: true` → new transformer used
|
|
207
|
-
- [ ] `isolatedModules: true` → `LanguageServiceCompiler` (type checking works)
|
|
208
|
-
- [ ] `diagnostics: false` → deprecation warning logged
|
|
209
|
-
- [ ] `noCheck: true` + `diagnostics.warnOnly` → silent (no warning)
|
|
210
|
-
- [ ] No experimental code loaded unless flag is set
|
|
211
|
-
- [ ] Every exported symbol has TSDoc
|