Skip to content

Commit 25157eb

Browse files
committed
fix: allow isolatedModules mode to have Program under Node16/Next
1 parent cc1f630 commit 25157eb

File tree

6 files changed

+221
-79
lines changed

6 files changed

+221
-79
lines changed

src/legacy/compiler/ts-compiler.spec.ts

+3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ jest.mock('typescript', () => {
2424
}
2525
})
2626
jest.mock('../../transpilers/typescript/transpile-module', () => {
27+
const actualModule = jest.requireActual('../../transpilers/typescript/transpile-module')
28+
2729
return {
30+
...actualModule,
2831
tsTranspileModule: jest.fn(),
2932
}
3033
})

src/legacy/compiler/ts-compiler.ts

+15-21
Original file line numberDiff line numberDiff line change
@@ -23,36 +23,21 @@ import ts, {
2323
} from 'typescript'
2424

2525
import { LINE_FEED, TS_TSX_REGEX } from '../../constants'
26-
// import { tsTranspileModule } from '../../transpilers/typescript/transpile-module'
27-
import { tsTranspileModule } from '../../transpilers/typescript/transpile-module'
26+
import { isModernNodeModuleKind, tsTranspileModule } from '../../transpilers/typescript/transpile-module'
2827
import type {
2928
StringMap,
3029
TsCompilerInstance,
3130
TsJestAstTransformer,
3231
TsJestCompileOptions,
3332
TTypeScript,
33+
CompiledOutput,
3434
} from '../../types'
35-
import { CompiledOutput } from '../../types'
3635
import { rootLogger } from '../../utils'
3736
import { Errors, Helps, interpolate } from '../../utils/messages'
3837
import type { ConfigSet } from '../config/config-set'
3938

4039
import { updateOutput } from './compiler-utils'
4140

42-
const isModernNodeResolution = (module: ts.ModuleKind | undefined): boolean => {
43-
return module ? [ts.ModuleKind.Node16, /* ModuleKind.Node18 */ 101, ts.ModuleKind.NodeNext].includes(module) : false
44-
}
45-
46-
const shouldUseNativeTsTranspile = (compilerOptions: ts.CompilerOptions | undefined): boolean => {
47-
if (!compilerOptions) {
48-
return true
49-
}
50-
51-
const { module } = compilerOptions
52-
53-
return !isModernNodeResolution(module)
54-
}
55-
5641
const assertCompilerOptionsWithJestTransformMode = (
5742
compilerOptions: ts.CompilerOptions,
5843
isEsmMode: boolean,
@@ -188,7 +173,7 @@ export class TsCompiler implements TsCompilerInstance {
188173

189174
let moduleKind = compilerOptions.module ?? this._ts.ModuleKind.ESNext
190175
let esModuleInterop = compilerOptions.esModuleInterop
191-
if (isModernNodeResolution(moduleKind)) {
176+
if (isModernNodeModuleKind(moduleKind)) {
192177
esModuleInterop = true
193178
moduleKind = this._ts.ModuleKind.ESNext
194179
}
@@ -208,7 +193,7 @@ export class TsCompiler implements TsCompilerInstance {
208193
getCompiledOutput(fileContent: string, fileName: string, options: TsJestCompileOptions): CompiledOutput {
209194
const isEsmMode = this.configSet.useESM && options.supportsStaticESM
210195
this._compilerOptions = this.fixupCompilerOptionsForModuleKind(this._initialCompilerOptions, isEsmMode)
211-
if (!this._initialCompilerOptions.isolatedModules && isModernNodeResolution(this._initialCompilerOptions.module)) {
196+
if (!this._initialCompilerOptions.isolatedModules && isModernNodeModuleKind(this._initialCompilerOptions.module)) {
212197
this._logger.warn(Helps.UsingModernNodeResolution)
213198
}
214199

@@ -295,7 +280,12 @@ export class TsCompiler implements TsCompilerInstance {
295280
}
296281

297282
protected _transpileOutput(fileContent: string, fileName: string): TranspileOutput {
298-
if (shouldUseNativeTsTranspile(this._initialCompilerOptions)) {
283+
/**
284+
* @deprecated
285+
*
286+
* This code path should be removed in the next major version to benefit from checking on compiler options
287+
*/
288+
if (!isModernNodeModuleKind(this._initialCompilerOptions.module)) {
299289
return this._ts.transpileModule(fileContent, {
300290
fileName,
301291
transformers: this._makeTransformers(this.configSet.resolvedTransformers),
@@ -306,7 +296,11 @@ export class TsCompiler implements TsCompilerInstance {
306296

307297
return tsTranspileModule(fileContent, {
308298
fileName,
309-
transformers: this._makeTransformers(this.configSet.resolvedTransformers),
299+
transformers: (program) => {
300+
this.program = program
301+
302+
return this._makeTransformers(this.configSet.resolvedTransformers)
303+
},
310304
compilerOptions: this._initialCompilerOptions,
311305
reportDiagnostics: fileName ? this.configSet.shouldReportDiagnostics(fileName) : false,
312306
})

src/transpilers/typescript/transpile-module.spec.ts

+68-26
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ function omitLeadingWhitespace(text: string): string {
5353

5454
describe('transpileModules', () => {
5555
describe('with modern Node resolution', () => {
56-
const esmModernNodeFilePath = path.join(workspaceRoot, 'esm-node-modern', 'foo.ts')
57-
const mtsFilePath = path.join(workspaceRoot, 'esm-node-modern', 'foo.mts')
58-
const cjsModernNodeFilePath = path.join(workspaceRoot, 'cjs-node-modern', 'foo.ts')
59-
const ctsFilePath = path.join(workspaceRoot, 'esm-node-modern', 'foo.cts')
56+
const tsFilePathInEsmModernNode = path.join(workspaceRoot, 'esm-node-modern', 'foo.ts')
57+
const mtsFilePath = path.join(workspaceRoot, 'foo.mts')
58+
const tsFilePathInCjsModernNode = path.join(workspaceRoot, 'cjs-node-modern', 'foo.ts')
59+
const ctsFilePath = path.join(workspaceRoot, 'foo.cts')
6060
vol.fromJSON(
6161
{
6262
'./esm-node-modern/package.json': JSON.stringify({
@@ -68,7 +68,7 @@ describe('transpileModules', () => {
6868
6969
console.log(foo);
7070
`,
71-
'./esm-node-modern/foo.mts': `
71+
'./foo.mts': `
7272
import { foo } from 'foo';
7373
7474
console.log(foo);
@@ -82,7 +82,7 @@ describe('transpileModules', () => {
8282
8383
console.log(foo);
8484
`,
85-
'./esm-node-modern/foo.cts': `
85+
'./foo.cts': `
8686
import { foo } from 'foo';
8787
8888
console.log(foo);
@@ -94,20 +94,17 @@ describe('transpileModules', () => {
9494
it.each([
9595
{
9696
module: ts.ModuleKind.Node16,
97-
moduleResolution: ts.ModuleResolutionKind.Node16,
9897
},
9998
{
10099
module: ts.ModuleKind.NodeNext,
101-
moduleResolution: ts.ModuleResolutionKind.NodeNext,
102100
},
103-
])('should emit CJS code with "type: commonjs" in package.json', ({ module, moduleResolution }) => {
104-
const result = tsTranspileModule(vol.readFileSync(cjsModernNodeFilePath, 'utf-8').toString(), {
105-
fileName: cjsModernNodeFilePath,
101+
])('should emit CJS code with "type: commonjs" in package.json', ({ module }) => {
102+
const result = tsTranspileModule(vol.readFileSync(tsFilePathInCjsModernNode, 'utf-8').toString(), {
103+
fileName: tsFilePathInCjsModernNode,
106104
compilerOptions: {
107105
module,
108106
target: ts.ScriptTarget.ESNext,
109107
verbatimModuleSyntax: true,
110-
moduleResolution,
111108
},
112109
})
113110

@@ -119,19 +116,16 @@ describe('transpileModules', () => {
119116
it.each([
120117
{
121118
module: ts.ModuleKind.Node16,
122-
moduleResolution: ts.ModuleResolutionKind.Node16,
123119
},
124120
{
125121
module: ts.ModuleKind.NodeNext,
126-
moduleResolution: ts.ModuleResolutionKind.NodeNext,
127122
},
128-
])('should emit ESM code with "type: module" in package.json', ({ module, moduleResolution }) => {
129-
const result = tsTranspileModule(vol.readFileSync(esmModernNodeFilePath, 'utf-8').toString(), {
130-
fileName: esmModernNodeFilePath,
123+
])('should emit ESM code with "type: module" in package.json', ({ module }) => {
124+
const result = tsTranspileModule(vol.readFileSync(tsFilePathInEsmModernNode, 'utf-8').toString(), {
125+
fileName: tsFilePathInEsmModernNode,
131126
compilerOptions: {
132127
module,
133128
target: ts.ScriptTarget.ESNext,
134-
moduleResolution,
135129
},
136130
})
137131

@@ -140,30 +134,78 @@ describe('transpileModules', () => {
140134
`)
141135
})
142136

143-
it('should emit ESM code with .mts extension', () => {
137+
it.each([
138+
{
139+
module: ts.ModuleKind.CommonJS,
140+
expectedResult: dedent`
141+
const foo_1 = require("foo");
142+
`,
143+
},
144+
{
145+
module: ts.ModuleKind.Node16,
146+
expectedResult: dedent`
147+
import { foo } from 'foo';
148+
`,
149+
},
150+
{
151+
module: ts.ModuleKind.ES2020,
152+
expectedResult: dedent`
153+
import { foo } from 'foo';
154+
`,
155+
},
156+
{
157+
module: undefined,
158+
expectedResult: dedent`
159+
import { foo } from 'foo';
160+
`,
161+
},
162+
])('should emit code with ".mts" extension respecting module option', ({ module, expectedResult }) => {
144163
const result = tsTranspileModule(vol.readFileSync(mtsFilePath, 'utf-8').toString(), {
145164
fileName: mtsFilePath,
146165
compilerOptions: {
166+
module,
147167
target: ts.ScriptTarget.ESNext,
148168
},
149169
})
150170

151-
expect(omitLeadingWhitespace(result.outputText)).toContain(dedent`
152-
import { foo } from 'foo';
153-
`)
171+
expect(omitLeadingWhitespace(result.outputText)).toContain(expectedResult)
154172
})
155173

156-
it('should emit CJS code with .cts extension', () => {
174+
it.each([
175+
{
176+
module: ts.ModuleKind.CommonJS,
177+
expectedResult: dedent`
178+
const foo_1 = require("foo");
179+
`,
180+
},
181+
{
182+
module: ts.ModuleKind.Node16,
183+
expectedResult: dedent`
184+
const foo_1 = require("foo");
185+
`,
186+
},
187+
{
188+
module: ts.ModuleKind.ES2020,
189+
expectedResult: dedent`
190+
import { foo } from 'foo';
191+
`,
192+
},
193+
{
194+
module: undefined,
195+
expectedResult: dedent`
196+
import { foo } from 'foo';
197+
`,
198+
},
199+
])('should emit code with ".cts" extension respecting module option', ({ module, expectedResult }) => {
157200
const result = tsTranspileModule(vol.readFileSync(ctsFilePath, 'utf-8').toString(), {
158201
fileName: ctsFilePath,
159202
compilerOptions: {
203+
module,
160204
target: ts.ScriptTarget.ESNext,
161205
},
162206
})
163207

164-
expect(omitLeadingWhitespace(result.outputText)).toContain(dedent`
165-
import { foo } from 'foo';
166-
`)
208+
expect(omitLeadingWhitespace(result.outputText)).toContain(expectedResult)
167209
})
168210
})
169211

src/transpilers/typescript/transpile-module.ts

+27-4
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,22 @@ function getNewLineCharacter(options: ts.CompilerOptions): string {
3939
}
4040
}
4141

42-
type ExtendedTsTranspileModuleFn = (input: string, transpileOptions: ts.TranspileOptions) => ts.TranspileOutput
42+
type ExtendedTranspileOptions = Omit<ts.TranspileOptions, 'transformers'> & {
43+
transformers?: (program: ts.Program) => ts.CustomTransformers
44+
}
45+
46+
type ExtendedTsTranspileModuleFn = (
47+
fileContent: string,
48+
transpileOptions: ExtendedTranspileOptions,
49+
) => ts.TranspileOutput
50+
51+
export const isModernNodeModuleKind = (module: ts.ModuleKind | undefined): boolean => {
52+
return module ? [ts.ModuleKind.Node16, /* ModuleKind.Node18 */ 101, ts.ModuleKind.NodeNext].includes(module) : false
53+
}
54+
55+
const shouldCheckProjectPkgJsonContent = (fileName: string, moduleKind: ts.ModuleKind | undefined): boolean => {
56+
return fileName.endsWith('package.json') && isModernNodeModuleKind(moduleKind)
57+
}
4358

4459
/**
4560
* Copy source code of {@link ts.transpileModule} from {@link https://github.com/microsoft/TypeScript/blob/main/src/services/transpile.ts}
@@ -113,10 +128,18 @@ const transpileWorker: ExtendedTsTranspileModuleFn = (input, transpileOptions) =
113128
getCurrentDirectory: () => '',
114129
getNewLine: () => newLine,
115130
fileExists: (fileName) => {
116-
return fileName.endsWith('package.json') ? ts.sys.fileExists(fileName) : fileName === inputFileName
131+
if (shouldCheckProjectPkgJsonContent(fileName, options.module)) {
132+
return ts.sys.fileExists(fileName)
133+
}
134+
135+
return fileName === inputFileName
117136
},
118137
readFile: (fileName) => {
119-
return fileName.endsWith('package.json') ? ts.sys.readFile(fileName) : ''
138+
if (shouldCheckProjectPkgJsonContent(fileName, options.module)) {
139+
return ts.sys.readFile(fileName)
140+
}
141+
142+
return ''
120143
},
121144
directoryExists: () => true,
122145
getDirectories: () => [],
@@ -161,7 +184,7 @@ const transpileWorker: ExtendedTsTranspileModuleFn = (input, transpileOptions) =
161184
/*writeFile*/ undefined,
162185
/*cancellationToken*/ undefined,
163186
/*emitOnlyDtsFiles*/ undefined,
164-
transpileOptions.transformers,
187+
transpileOptions.transformers?.(program),
165188
)
166189

167190
diagnostics.push(...result.diagnostics)

0 commit comments

Comments
 (0)