Skip to content

Commit 9bb0c05

Browse files
authoredJan 12, 2021
feat(compiler): support ESM for isolatedModules: false (#2269)
Related to #1709
1 parent a2ab478 commit 9bb0c05

File tree

8 files changed

+153
-241
lines changed

8 files changed

+153
-241
lines changed
 

‎src/__helpers__/path.ts

+2
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ export function tempDir(ns: string): string {
1111

1212
return dir
1313
}
14+
15+
export const mockFolder = join(process.cwd(), 'src', '__mocks__')

‎src/__helpers__/processed-source.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { isAbsolute, relative } from 'path'
22

33
import type { RawSourceMap } from 'source-map'
44

5+
import { SOURCE_MAPPING_PREFIX } from '../compiler/compiler-utils'
6+
57
import { ROOT } from './path'
68
import { ParsedSourceWithMaps, parseSource, relativisePaths, rewriteSourceMaps } from './source-maps'
79

@@ -19,7 +21,7 @@ export default class ProcessedSource {
1921
return parseSource(this.output)
2022
}
2123
get outputCodeWithoutMaps(): string {
22-
return this.parsedSource.source
24+
return this.output.substring(0, this.output.indexOf(SOURCE_MAPPING_PREFIX))
2325
}
2426
get outputSourceMaps(): RawSourceMap | undefined {
2527
return this.parsedSource.sourceMaps
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,17 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

33
exports[`TsCompiler isolatedModule false allowJs option should compile js file for allowJs true with outDir 1`] = `
4-
===[ FILE: test-allow-js.js ]===================================================
5-
"use strict";
6-
Object.defineProperty(exports, "__esModule", { value: true });
7-
exports.default = 42;
8-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJmaWxlIjoidGVzdC1hbGxvdy1qcy5qcyIsIm1hcHBpbmdzIjoiOztBQUFBLGtCQUFlLEVBQUUsQ0FBQSIsIm5hbWVzIjpbXSwic291cmNlcyI6WyJ0ZXN0LWFsbG93LWpzLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IDQyIl0sInZlcnNpb24iOjN9
9-
===[ INLINE SOURCE MAPS ]=======================================================
10-
file: test-allow-js.js
11-
mappings: ';;AAAA,kBAAe,EAAE,CAAA'
12-
names: []
13-
sources:
14-
- test-allow-js.js
15-
sourcesContent:
16-
- export default 42
17-
version: 3
18-
================================================================================
4+
"\\"use strict\\";
5+
Object.defineProperty(exports, \\"__esModule\\", { value: true });
6+
exports.default = 42;
7+
//# "
198
`;
209

2110
exports[`TsCompiler isolatedModule false allowJs option should compile js file for allowJs true without outDir 1`] = `
22-
===[ FILE: test-allow-js.js ]===================================================
23-
"use strict";
24-
Object.defineProperty(exports, "__esModule", { value: true });
25-
exports.default = 42;
26-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJmaWxlIjoidGVzdC1hbGxvdy1qcy5qcyIsIm1hcHBpbmdzIjoiOztBQUFBLGtCQUFlLEVBQUUsQ0FBQSIsIm5hbWVzIjpbXSwic291cmNlcyI6WyJ0ZXN0LWFsbG93LWpzLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IDQyIl0sInZlcnNpb24iOjN9
27-
===[ INLINE SOURCE MAPS ]=======================================================
28-
file: test-allow-js.js
29-
mappings: ';;AAAA,kBAAe,EAAE,CAAA'
30-
names: []
31-
sources:
32-
- test-allow-js.js
33-
sourcesContent:
34-
- export default 42
35-
version: 3
36-
================================================================================
11+
"\\"use strict\\";
12+
Object.defineProperty(exports, \\"__esModule\\", { value: true });
13+
exports.default = 42;
14+
//# "
3715
`;
3816

3917
exports[`TsCompiler isolatedModule false diagnostics should throw error when cannot compile 1`] = `
@@ -55,131 +33,54 @@ Array [
5533
`;
5634

5735
exports[`TsCompiler isolatedModule false jsx option should compile tsx file for jsx preserve 1`] = `
58-
===[ FILE: test-jsx.tsx ]=======================================================
59-
"use strict";
60-
const App = () => {
61-
return <>Test</>;
62-
};
63-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJmaWxlIjoidGVzdC1qc3gudHN4IiwibWFwcGluZ3MiOiI7QUFDUSxNQUFNLEdBQUcsR0FBRyxHQUFHLEVBQUU7SUFDZixPQUFPLEVBQUUsSUFBSSxHQUFHLENBQUE7QUFDbEIsQ0FBQyxDQUFBIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbInRlc3QtanN4LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJcbiAgICAgICAgY29uc3QgQXBwID0gKCkgPT4ge1xuICAgICAgICAgIHJldHVybiA8PlRlc3Q8Lz5cbiAgICAgICAgfVxuICAgICAgIl0sInZlcnNpb24iOjN9
64-
===[ INLINE SOURCE MAPS ]=======================================================
65-
file: test-jsx.tsx
66-
mappings: ';AACQ,MAAM,GAAG,GAAG,GAAG,EAAE;IACf,OAAO,EAAE,IAAI,GAAG,CAAA;AAClB,CAAC,CAAA'
67-
names: []
68-
sources:
69-
- test-jsx.tsx
70-
sourcesContent:
71-
- |2-
72-
73-
const App = () => {
74-
return <>Test</>
75-
}
76-
77-
version: 3
78-
================================================================================
36+
"\\"use strict\\";
37+
const App = () => {
38+
return <>Test</>;
39+
};
40+
//# "
7941
`;
8042

8143
exports[`TsCompiler isolatedModule false jsx option should compile tsx file for other jsx options 1`] = `
82-
===[ FILE: test-jsx.tsx ]=======================================================
83-
"use strict";
84-
const App = () => {
85-
return React.createElement(React.Fragment, null, "Test");
86-
};
87-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJmaWxlIjoidGVzdC1qc3gudHN4IiwibWFwcGluZ3MiOiI7QUFDUSxNQUFNLEdBQUcsR0FBRyxHQUFHLEVBQUU7SUFDZixPQUFPLGlEQUFTLENBQUE7QUFDbEIsQ0FBQyxDQUFBIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbInRlc3QtanN4LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJcbiAgICAgICAgY29uc3QgQXBwID0gKCkgPT4ge1xuICAgICAgICAgIHJldHVybiA8PlRlc3Q8Lz5cbiAgICAgICAgfVxuICAgICAgIl0sInZlcnNpb24iOjN9
88-
===[ INLINE SOURCE MAPS ]=======================================================
89-
file: test-jsx.tsx
90-
mappings: ';AACQ,MAAM,GAAG,GAAG,GAAG,EAAE;IACf,OAAO,iDAAS,CAAA;AAClB,CAAC,CAAA'
91-
names: []
92-
sources:
93-
- test-jsx.tsx
94-
sourcesContent:
95-
- |2-
96-
97-
const App = () => {
98-
return <>Test</>
99-
}
100-
101-
version: 3
102-
================================================================================
44+
"\\"use strict\\";
45+
const App = () => {
46+
return React.createElement(React.Fragment, null, \\"Test\\");
47+
};
48+
//# "
49+
`;
50+
51+
exports[`TsCompiler isolatedModule false should compile codes with useESM true 1`] = `
52+
"export const thing = { a: 1, b: 2 };
53+
//# "
10354
`;
10455

10556
exports[`TsCompiler isolatedModule true diagnostics should report diagnostics related to codes with pathRegex config is undefined 1`] = `"foo.ts(2,23): error TS1005: '=>' expected."`;
10657

10758
exports[`TsCompiler isolatedModule true diagnostics should report diagnostics related to codes with pathRegex config matches file name 1`] = `"foo.ts(2,23): error TS1005: '=>' expected."`;
10859

10960
exports[`TsCompiler isolatedModule true jsx option should compile tsx file for jsx preserve 1`] = `
110-
===[ FILE: foo.tsx ]============================================================
111-
"use strict";
112-
const App = () => {
113-
return <>Test</>;
114-
};
115-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJmaWxlIjoiZm9vLnRzeCIsIm1hcHBpbmdzIjoiO0FBQ1EsTUFBTSxHQUFHLEdBQUcsR0FBRyxFQUFFO0lBQ2YsT0FBTyxFQUFFLElBQUksR0FBRyxDQUFBO0FBQ2xCLENBQUMsQ0FBQSIsIm5hbWVzIjpbXSwic291cmNlcyI6WyJmb28udHN4Il0sInNvdXJjZXNDb250ZW50IjpbIlxuICAgICAgICBjb25zdCBBcHAgPSAoKSA9PiB7XG4gICAgICAgICAgcmV0dXJuIDw+VGVzdDwvPlxuICAgICAgICB9XG4gICAgICAiXSwidmVyc2lvbiI6M30=
116-
===[ INLINE SOURCE MAPS ]=======================================================
117-
file: foo.tsx
118-
mappings: ';AACQ,MAAM,GAAG,GAAG,GAAG,EAAE;IACf,OAAO,EAAE,IAAI,GAAG,CAAA;AAClB,CAAC,CAAA'
119-
names: []
120-
sources:
121-
- foo.tsx
122-
sourcesContent:
123-
- |2-
124-
125-
const App = () => {
126-
return <>Test</>
127-
}
128-
129-
version: 3
130-
================================================================================
61+
"\\"use strict\\";
62+
const App = () => {
63+
return <>Test</>;
64+
};
65+
//# "
13166
`;
13267

13368
exports[`TsCompiler isolatedModule true jsx option should compile tsx file for other jsx options 1`] = `
134-
===[ FILE: foo.tsx ]============================================================
135-
"use strict";
136-
const App = () => {
137-
return React.createElement(React.Fragment, null, "Test");
138-
};
139-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJmaWxlIjoiZm9vLnRzeCIsIm1hcHBpbmdzIjoiO0FBQ1EsTUFBTSxHQUFHLEdBQUcsR0FBRyxFQUFFO0lBQ2YsT0FBTyxpREFBUyxDQUFBO0FBQ2xCLENBQUMsQ0FBQSIsIm5hbWVzIjpbXSwic291cmNlcyI6WyJmb28udHN4Il0sInNvdXJjZXNDb250ZW50IjpbIlxuICAgICAgICBjb25zdCBBcHAgPSAoKSA9PiB7XG4gICAgICAgICAgcmV0dXJuIDw+VGVzdDwvPlxuICAgICAgICB9XG4gICAgICAiXSwidmVyc2lvbiI6M30=
140-
===[ INLINE SOURCE MAPS ]=======================================================
141-
file: foo.tsx
142-
mappings: ';AACQ,MAAM,GAAG,GAAG,GAAG,EAAE;IACf,OAAO,iDAAS,CAAA;AAClB,CAAC,CAAA'
143-
names: []
144-
sources:
145-
- foo.tsx
146-
sourcesContent:
147-
- |2-
148-
149-
const App = () => {
150-
return <>Test</>
151-
}
152-
153-
version: 3
154-
================================================================================
69+
"\\"use strict\\";
70+
const App = () => {
71+
return React.createElement(React.Fragment, null, \\"Test\\");
72+
};
73+
//# "
15574
`;
15675

15776
exports[`TsCompiler isolatedModule true should compile js file for allowJs true 1`] = `
158-
===[ FILE: foo.js ]=============================================================
159-
"use strict";
160-
Object.defineProperty(exports, "__esModule", { value: true });
161-
exports.default = 42;
162-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJmaWxlIjoiZm9vLmpzIiwibWFwcGluZ3MiOiI7O0FBQUEsa0JBQWUsRUFBRSxDQUFBIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbImZvby5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCA0MiJdLCJ2ZXJzaW9uIjozfQ==
163-
===[ INLINE SOURCE MAPS ]=======================================================
164-
file: foo.js
165-
mappings: ';;AAAA,kBAAe,EAAE,CAAA'
166-
names: []
167-
sources:
168-
- foo.js
169-
sourcesContent:
170-
- export default 42
171-
version: 3
172-
================================================================================
77+
"\\"use strict\\";
78+
Object.defineProperty(exports, \\"__esModule\\", { value: true });
79+
exports.default = 42;
80+
//# "
17381
`;
17482

175-
exports[`TsCompiler isolatedModule true support ESM should transpile codes to correct syntax with supportsStaticESM and useESM options 1`] = `99`;
176-
177-
exports[`TsCompiler isolatedModule true support ESM should transpile codes to correct syntax with supportsStaticESM and useESM options 2`] = `99`;
178-
179-
exports[`TsCompiler isolatedModule true support ESM should transpile codes to correct syntax with supportsStaticESM and useESM options 3`] = `99`;
180-
181-
exports[`TsCompiler isolatedModule true support ESM should transpile codes to correct syntax with supportsStaticESM and useESM options 4`] = `1`;
182-
183-
exports[`TsCompiler isolatedModule true support ESM should transpile codes to correct syntax with supportsStaticESM and useESM options 5`] = `1`;
184-
185-
exports[`TsCompiler isolatedModule true support ESM should transpile codes to correct syntax with supportsStaticESM and useESM options 6`] = `1`;
83+
exports[`TsCompiler isolatedModule true should transpile code with useESM true 1`] = `
84+
"export const thing = { a: 1, b: 2 };
85+
//# "
86+
`;

‎src/compiler/ts-compiler.spec.ts

+35-64
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { readFileSync } from 'fs'
22
import { join } from 'path'
33

44
import { LogLevels } from 'bs-logger'
5-
import ts from 'typescript'
65

76
import { makeCompiler } from '../__helpers__/fakers'
87
import { logTargetMock } from '../__helpers__/mocks'
8+
import { mockFolder } from '../__helpers__/path'
99
import ProcessedSource from '../__helpers__/processed-source'
1010
import { TS_JEST_OUT_DIR } from '../config/config-set'
1111

@@ -17,16 +17,27 @@ describe('TsCompiler', () => {
1717
isolatedModules: true,
1818
}
1919

20+
test('should transpile code with useESM true', () => {
21+
const compiler = makeCompiler({
22+
tsJestConfig: { ...baseTsJestConfig, useESM: true },
23+
})
24+
const fileName = join(mockFolder, 'thing.spec.ts')
25+
26+
const compiledOutput = compiler.getCompiledOutput(readFileSync(fileName, 'utf-8'), fileName, true)
27+
28+
expect(new ProcessedSource(compiledOutput, fileName).outputCodeWithoutMaps).toMatchSnapshot()
29+
})
30+
2031
it('should compile js file for allowJs true', () => {
2132
const fileName = 'foo.js'
2233
const compiler = makeCompiler({
2334
tsJestConfig: { ...baseTsJestConfig, tsconfig: { allowJs: true, outDir: TS_JEST_OUT_DIR } },
2435
})
2536
const source = 'export default 42'
2637

27-
const compiled = compiler.getCompiledOutput(source, fileName, false)
38+
const compiledOutput = compiler.getCompiledOutput(source, fileName, false)
2839

29-
expect(new ProcessedSource(compiled, fileName)).toMatchSnapshot()
40+
expect(new ProcessedSource(compiledOutput, fileName).outputCodeWithoutMaps).toMatchSnapshot()
3041
})
3142

3243
describe('jsx option', () => {
@@ -46,9 +57,9 @@ describe('TsCompiler', () => {
4657
},
4758
},
4859
})
49-
const compiled = compiler.getCompiledOutput(source, fileName, false)
60+
const compiledOutput = compiler.getCompiledOutput(source, fileName, false)
5061

51-
expect(new ProcessedSource(compiled, fileName)).toMatchSnapshot()
62+
expect(new ProcessedSource(compiledOutput, fileName).outputCodeWithoutMaps).toMatchSnapshot()
5263
})
5364

5465
it('should compile tsx file for other jsx options', () => {
@@ -60,9 +71,9 @@ describe('TsCompiler', () => {
6071
},
6172
},
6273
})
63-
const compiled = compiler.getCompiledOutput(source, fileName, false)
74+
const compiledOutput = compiler.getCompiledOutput(source, fileName, false)
6475

65-
expect(new ProcessedSource(compiled, fileName)).toMatchSnapshot()
76+
expect(new ProcessedSource(compiledOutput, fileName).outputCodeWithoutMaps).toMatchSnapshot()
6677
})
6778
})
6879

@@ -72,9 +83,9 @@ describe('TsCompiler', () => {
7283

7384
it('should have correct source maps without mapRoot', () => {
7485
const compiler = makeCompiler({ tsJestConfig: { ...baseTsJestConfig, tsconfig: false } })
75-
const compiled = compiler.getCompiledOutput(source, fileName, false)
86+
const compiledOutput = compiler.getCompiledOutput(source, fileName, false)
7687

77-
expect(new ProcessedSource(compiled, fileName).outputSourceMaps).toMatchObject({
88+
expect(new ProcessedSource(compiledOutput, fileName).outputSourceMaps).toMatchObject({
7889
file: fileName,
7990
sources: [fileName],
8091
sourcesContent: [source],
@@ -166,57 +177,6 @@ const t: string = f(5)
166177
).not.toThrowError()
167178
})
168179
})
169-
170-
describe('support ESM', () => {
171-
test.each([
172-
{
173-
supportsStaticESM: true,
174-
useESM: true,
175-
moduleKind: 'esnext',
176-
},
177-
{
178-
supportsStaticESM: true,
179-
useESM: true,
180-
moduleKind: 'amd',
181-
},
182-
{
183-
supportsStaticESM: true,
184-
useESM: true,
185-
moduleKind: undefined,
186-
},
187-
{
188-
supportsStaticESM: false,
189-
useESM: true,
190-
moduleKind: 'esnext',
191-
},
192-
{
193-
supportsStaticESM: true,
194-
useESM: false,
195-
moduleKind: 'amd',
196-
},
197-
{
198-
supportsStaticESM: false,
199-
useESM: false,
200-
moduleKind: 'es2015',
201-
},
202-
])('should transpile codes to correct syntax with supportsStaticESM and useESM options', (data) => {
203-
const transpileModuleSpy = (ts.transpileModule = jest.fn().mockReturnValueOnce({
204-
outputText: 'var foo = 1',
205-
diagnostics: [],
206-
sourceMapText: '{}',
207-
}))
208-
const fileContent = `const foo = import('./foo')`
209-
const fileName = 'foo.ts'
210-
211-
const compiler = makeCompiler({
212-
tsJestConfig: { ...baseTsJestConfig, tsconfig: { module: data.moduleKind as any }, useESM: data.useESM },
213-
})
214-
compiler.getCompiledOutput(fileContent, fileName, data.supportsStaticESM)
215-
216-
expect(transpileModuleSpy).toHaveBeenCalled()
217-
expect(transpileModuleSpy.mock.calls[0][1].compilerOptions.module).toMatchSnapshot()
218-
})
219-
})
220180
})
221181

222182
describe('isolatedModule false', () => {
@@ -227,6 +187,17 @@ const t: string = f(5)
227187
logTarget.clear()
228188
})
229189

190+
test('should compile codes with useESM true', () => {
191+
const compiler = makeCompiler({
192+
tsJestConfig: { ...baseTsJestConfig, useESM: true },
193+
})
194+
const fileName = join(mockFolder, 'thing.spec.ts')
195+
196+
const compiledOutput = compiler.getCompiledOutput(readFileSync(fileName, 'utf-8'), fileName, true)
197+
198+
expect(new ProcessedSource(compiledOutput, fileName).outputCodeWithoutMaps).toMatchSnapshot()
199+
})
200+
230201
describe('allowJs option', () => {
231202
const fileName = 'test-allow-js.js'
232203
const source = 'export default 42'
@@ -242,7 +213,7 @@ const t: string = f(5)
242213

243214
const compiled = compiler.getCompiledOutput(source, fileName, false)
244215

245-
expect(new ProcessedSource(compiled, fileName)).toMatchSnapshot()
216+
expect(new ProcessedSource(compiled, fileName).outputCodeWithoutMaps).toMatchSnapshot()
246217
})
247218

248219
it('should compile js file for allowJs true without outDir', () => {
@@ -254,7 +225,7 @@ const t: string = f(5)
254225
)
255226
const compiled = compiler.getCompiledOutput(source, fileName, false)
256227

257-
expect(new ProcessedSource(compiled, fileName)).toMatchSnapshot()
228+
expect(new ProcessedSource(compiled, fileName).outputCodeWithoutMaps).toMatchSnapshot()
258229
})
259230
})
260231

@@ -281,7 +252,7 @@ const t: string = f(5)
281252

282253
const compiled = compiler.getCompiledOutput(source, fileName, false)
283254

284-
expect(new ProcessedSource(compiled, fileName)).toMatchSnapshot()
255+
expect(new ProcessedSource(compiled, fileName).outputCodeWithoutMaps).toMatchSnapshot()
285256
})
286257

287258
it('should compile tsx file for other jsx options', () => {
@@ -297,7 +268,7 @@ const t: string = f(5)
297268
)
298269
const compiled = compiler.getCompiledOutput(source, fileName, false)
299270

300-
expect(new ProcessedSource(compiled, fileName)).toMatchSnapshot()
271+
expect(new ProcessedSource(compiled, fileName).outputCodeWithoutMaps).toMatchSnapshot()
301272
})
302273
})
303274

‎src/compiler/ts-compiler.ts

+35-26
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import { basename, normalize, relative } from 'path'
22

33
import { LogContexts, Logger, LogLevels } from 'bs-logger'
44
import memoize from 'lodash/memoize'
5-
import {
5+
import type {
66
EmitOutput,
77
LanguageService,
88
LanguageServiceHost,
99
ParsedCommandLine,
1010
ResolvedModuleFull,
1111
TranspileOutput,
12-
ModuleKind,
12+
CompilerOptions,
1313
} from 'typescript'
1414

1515
import type { ConfigSet } from '../config/config-set'
@@ -20,8 +20,6 @@ import { Errors, interpolate } from '../utils/messages'
2020

2121
import { updateOutput } from './compiler-utils'
2222

23-
const AVAILABLE_ESM_MODULE_KINDS = [ModuleKind.ES2015, ModuleKind.ES2020, ModuleKind.ESNext]
24-
2523
/**
2624
* @internal
2725
*/
@@ -31,6 +29,7 @@ export class TsCompiler implements CompilerInstance {
3129
private readonly _parsedTsConfig: ParsedCommandLine
3230
private readonly _compilerCacheFS: Map<string, number> = new Map<string, number>()
3331
private readonly _jestCacheFS: StringMap
32+
private _compilerOptions: CompilerOptions
3433
private _cachedReadFile: ((fileName: string) => string | undefined) | undefined
3534
private _projectVersion = 1
3635
private _languageService: LanguageService | undefined
@@ -40,16 +39,13 @@ export class TsCompiler implements CompilerInstance {
4039
this._logger = rootLogger.child({ namespace: 'ts-compiler' })
4140
this._parsedTsConfig = this.configSet.parsedTsConfig as ParsedCommandLine
4241
this._jestCacheFS = jestCacheFS
42+
this._compilerOptions = { ...this._parsedTsConfig.options, module: this._ts.ModuleKind.CommonJS }
4343
if (!this.configSet.isolatedModules) {
4444
this._createLanguageService()
4545
}
4646
}
4747

4848
private _createLanguageService(): void {
49-
const compilerOptions = {
50-
...this._parsedTsConfig.options,
51-
module: ModuleKind.CommonJS,
52-
}
5349
const serviceHostTraceCtx = {
5450
namespace: 'ts:serviceHost',
5551
call: null,
@@ -69,7 +65,11 @@ export class TsCompiler implements CompilerInstance {
6965
realpath: this._ts.sys.realpath && memoize(this._ts.sys.realpath),
7066
getDirectories: memoize(this._ts.sys.getDirectories),
7167
}
72-
const moduleResolutionCache = this._ts.createModuleResolutionCache(this.configSet.cwd, (x) => x, compilerOptions)
68+
const moduleResolutionCache = this._ts.createModuleResolutionCache(
69+
this.configSet.cwd,
70+
(x) => x,
71+
this._compilerOptions,
72+
)
7373
/* istanbul ignore next */
7474
const serviceHost: LanguageServiceHost = {
7575
getProjectVersion: () => String(this._projectVersion),
@@ -114,15 +114,15 @@ export class TsCompiler implements CompilerInstance {
114114
realpath: this._ts.sys.realpath && memoize(this._ts.sys.realpath),
115115
getNewLine: () => LINE_FEED,
116116
getCurrentDirectory: () => this.configSet.cwd,
117-
getCompilationSettings: () => compilerOptions,
118-
getDefaultLibFileName: () => this._ts.getDefaultLibFilePath(compilerOptions),
117+
getCompilationSettings: () => this._compilerOptions,
118+
getDefaultLibFileName: () => this._ts.getDefaultLibFilePath(this._compilerOptions),
119119
getCustomTransformers: () => this.configSet.customTransformers,
120120
resolveModuleNames: (moduleNames: string[], containingFile: string): (ResolvedModuleFull | undefined)[] =>
121121
moduleNames.map((moduleName) => {
122122
const { resolvedModule } = this._ts.resolveModuleName(
123123
moduleName,
124124
containingFile,
125-
compilerOptions,
125+
this._compilerOptions,
126126
moduleResolutionHost,
127127
moduleResolutionCache,
128128
)
@@ -144,6 +144,28 @@ export class TsCompiler implements CompilerInstance {
144144
}
145145

146146
getCompiledOutput(fileContent: string, fileName: string, supportsStaticESM: boolean): string {
147+
let moduleKind = this._compilerOptions.module
148+
let esModuleInterop = this._compilerOptions.esModuleInterop
149+
let allowSyntheticDefaultImports = this._compilerOptions.allowSyntheticDefaultImports
150+
if (supportsStaticESM && this.configSet.useESM) {
151+
moduleKind =
152+
!moduleKind ||
153+
(moduleKind &&
154+
![this._ts.ModuleKind.ES2015, this._ts.ModuleKind.ES2020, this._ts.ModuleKind.ESNext].includes(moduleKind))
155+
? this._ts.ModuleKind.ESNext
156+
: moduleKind
157+
// Make sure `esModuleInterop` and `allowSyntheticDefaultImports` true to support import CJS into ESM
158+
esModuleInterop = true
159+
allowSyntheticDefaultImports = true
160+
} else {
161+
moduleKind = this._ts.ModuleKind.CommonJS
162+
}
163+
this._compilerOptions = {
164+
...this._compilerOptions,
165+
allowSyntheticDefaultImports,
166+
esModuleInterop,
167+
module: moduleKind,
168+
}
147169
if (this._languageService) {
148170
this._logger.debug({ fileName }, 'getCompiledOutput(): compiling using language service')
149171

@@ -169,25 +191,12 @@ export class TsCompiler implements CompilerInstance {
169191

170192
return updateOutput(output.outputFiles[1].text, fileName, output.outputFiles[0].text)
171193
} else {
172-
let moduleKind = this._parsedTsConfig.options.module
173-
if (supportsStaticESM && this.configSet.useESM) {
174-
moduleKind =
175-
!moduleKind || (moduleKind && !AVAILABLE_ESM_MODULE_KINDS.includes(moduleKind))
176-
? ModuleKind.ESNext
177-
: moduleKind
178-
} else {
179-
moduleKind = ModuleKind.CommonJS
180-
}
181-
182194
this._logger.debug({ fileName }, 'getCompiledOutput(): compiling as isolated module')
183195

184196
const result: TranspileOutput = this._ts.transpileModule(fileContent, {
185197
fileName,
186198
transformers: this.configSet.customTransformers,
187-
compilerOptions: {
188-
...this._parsedTsConfig.options,
189-
module: moduleKind,
190-
},
199+
compilerOptions: this._compilerOptions,
191200
reportDiagnostics: this.configSet.shouldReportDiagnostics(fileName),
192201
})
193202
if (result.diagnostics && this.configSet.shouldReportDiagnostics(fileName)) {

‎src/transformers/hoist-jest.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { testing } from 'bs-logger'
2-
import * as tsc from 'typescript'
2+
import ts from 'typescript'
33

44
import * as hoist from './hoist-jest'
55

@@ -116,8 +116,8 @@ const CODE_WITH_HOISTING_HAS_JEST_GLOBALS = `
116116
`
117117

118118
const logger = testing.createLoggerMock()
119-
const createFactory = () => hoist.factory({ logger, compilerModule: tsc } as any)
120-
const transpile = (source: string) => tsc.transpileModule(source, { transformers: { before: [createFactory()] } })
119+
const createFactory = () => hoist.factory({ logger, compilerModule: ts } as any)
120+
const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } })
121121

122122
describe('hoisting', () => {
123123
it('should have correct signature', () => {

‎website/docs/esm-support.md

+33-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,36 @@ title: ESM Support
44

55
To use `ts-jest` with ESM support, you'll first need to check [ESM Jest documentation](https://jestjs.io/docs/en/ecmascript-modules).
66

7-
`ts-jest` supports ESM via a config option `useESM`. When this option is enabled and depending on which files Jest supports
8-
ESM, `ts-jest` will transform codes to ESM syntax. There are also 3 presets to use with `useESM` option.
9-
10-
More information see [useESM option](options/useESM.md) and [ESM presets](presets.md)
7+
`ts-jest` supports ESM via a config option [useESM](options/useESM.md) in combination with jest config option [extensionsToTreatAsEsm](https://jestjs.io/docs/en/next/configuration#extensionstotreatasesm-arraystring).
8+
9+
There are also [3 presets](presets.md) to work with ESM.
10+
11+
### Examples
12+
13+
```js
14+
// jest.config.js
15+
module.exports = {
16+
// [...]
17+
"extensionsToTreatAsEsm": ['.ts'],
18+
globals: {
19+
'ts-jest': {
20+
useESM: true,
21+
},
22+
},
23+
}
24+
```
25+
26+
```json5
27+
// OR package.json
28+
{
29+
// [...]
30+
"jest": {
31+
"extensionsToTreatAsEsm": [".ts"],
32+
"globals": {
33+
"ts-jest": {
34+
"useESM": true,
35+
}
36+
}
37+
}
38+
}
39+
```

‎website/docs/options/useESM.md

+1-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ title: useESM option
44

55
The `useESM` option allows `ts-jest` to transform codes to ESM syntax **if possible**.
66

7-
The default value is **false**, `ts-jest` will transform codes to `CommonJS` syntax.
8-
9-
Currently `ts-jest` only supports this option in combination with `isolatedModule: true`.
7+
The default value is **false**, `ts-jest` will transform codes to `CommonJS` syntax.
108

119
### Examples
1210

0 commit comments

Comments
 (0)
Please sign in to comment.