Skip to content

Commit b2b3934

Browse files
committed
fix: transform js ESM file from node_modules
`ts.LanguageService` somehow can't transform `js` file which has content in ESM. When dealing with those files, we simply transform them with `ts.transpileModule` API without applying any AST transformers. We don't apply type-check on these files either since they are prebuilt. This should benefit the performance and help users to work easier with ESM packages when running Jest in CJS mode. Fixes #2913
1 parent bc78441 commit b2b3934

7 files changed

+72
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import camelCase from 'lodash-es/camelCase';
2+
import { union } from 'set-utilities';
23
import { __assign } from 'tslib';
34

45
test('should pass', () => {
56
expect(camelCase('foo-bar')).toBe('fooBar');
67
expect(typeof __assign).toBe('function');
8+
expect(union(new Set([1, 2, 3]), new Set([4, 5, 6]))).toStrictEqual(new Set([1, 2, 3, 4, 5, 6]));
79
});

e2e/process-js-packages/jest-cjs.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const config: JestConfigWithTsJest = {
1616
},
1717
],
1818
},
19-
transformIgnorePatterns: ['node_modules/(?!lodash-es)'],
19+
transformIgnorePatterns: ['node_modules/(?!lodash-es|set-utilities)'],
2020
};
2121

2222
export default config;

e2e/process-js-packages/jest-transpile-cjs.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const config: JestConfigWithTsJest = {
1717
},
1818
],
1919
},
20-
transformIgnorePatterns: ['node_modules/(?!lodash-es)'],
20+
transformIgnorePatterns: ['node_modules/(?!lodash-es|set-utilities)'],
2121
};
2222

2323
export default config;

e2e/process-js-packages/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"private": true,
44
"devDependencies": {
55
"@types/lodash-es": "^4.17.12",
6-
"lodash-es": "^4.17.21"
6+
"lodash-es": "^4.17.21",
7+
"set-utilities": "^1.5.7"
78
}
89
}

e2e/process-js-packages/yarn.lock

+8
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,13 @@ __metadata:
3434
dependencies:
3535
"@types/lodash-es": "npm:^4.17.12"
3636
lodash-es: "npm:^4.17.21"
37+
set-utilities: "npm:^1.5.7"
3738
languageName: unknown
3839
linkType: soft
40+
41+
"set-utilities@npm:^1.5.7":
42+
version: 1.5.7
43+
resolution: "set-utilities@npm:1.5.7"
44+
checksum: 10/d36f10778cb31bd2e6e5521473dbe9df1dd8e7ff86300910134aa0c7397da4845a4d058ffb773a1079b2ed2f9b85b76a5880921588cf211430e930eda171cfc5
45+
languageName: node
46+
linkType: hard

src/ng-jest-transformer.spec.ts

+35
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { transformSync } from 'esbuild';
22
import { TsJestTransformer } from 'ts-jest';
3+
import { transpileModule } from 'typescript';
34

45
import packageJson from '../package.json';
56
import { NgJestCompiler } from './compiler/ng-jest-compiler';
@@ -15,11 +16,21 @@ jest.mock('esbuild', () => {
1516
};
1617
});
1718
const mockedTransformSync = jest.mocked(transformSync);
19+
jest.mock('typescript', () => {
20+
return {
21+
...jest.requireActual('typescript'),
22+
transpileModule: jest.fn().mockReturnValue({
23+
outputText: '',
24+
}),
25+
};
26+
});
27+
const mockTranspileModule = jest.mocked(transpileModule);
1828

1929
describe('NgJestTransformer', () => {
2030
beforeEach(() => {
2131
// @ts-expect-error testing purpose
2232
TsJestTransformer._cachedConfigSets = [];
33+
mockTranspileModule.mockClear();
2334
});
2435

2536
test('should create NgJestCompiler and NgJestConfig instances', () => {
@@ -148,6 +159,30 @@ describe('NgJestTransformer', () => {
148159
mockedTransformSync.mockClear();
149160
});
150161

162+
it('should use "transpileModule" to process `node_modules` js file', () => {
163+
const transformCfg = {
164+
cacheFS: new Map(),
165+
config: {
166+
cwd: process.cwd(),
167+
extensionsToTreatAsEsm: [],
168+
testMatch: [],
169+
testRegex: [],
170+
},
171+
} as any; // eslint-disable-line @typescript-eslint/no-explicit-any
172+
const tr = new NgJestTransformer({});
173+
tr.process(
174+
`
175+
const pi = parseFloat(3.124);
176+
177+
export { pi };
178+
`,
179+
'node_modules/random-package/foo.js',
180+
transformCfg,
181+
);
182+
183+
expect(mockTranspileModule.mock.calls[0][1].fileName).toBe('node_modules/random-package/foo.js');
184+
});
185+
151186
test.each([
152187
{
153188
tsconfig: {

src/ng-jest-transformer.ts

+23
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import type { TransformedSource } from '@jest/transform';
44
import { LogContexts, LogLevels, type Logger, createLogger } from 'bs-logger';
55
import { transformSync } from 'esbuild';
66
import { type TsJestTransformerOptions, ConfigSet, TsJestTransformer, type TsJestTransformOptions } from 'ts-jest';
7+
import { updateOutput } from 'ts-jest/dist/legacy/compiler/compiler-utils';
8+
import type { TranspileOutput } from 'typescript';
79

810
import { NgJestCompiler } from './compiler/ng-jest-compiler';
911
import { NgJestConfig } from './config/ng-jest-config';
@@ -93,6 +95,27 @@ export class NgJestTransformer extends TsJestTransformer {
9395
map,
9496
};
9597
} else {
98+
if (filePath.includes('node_modules') && filePath.endsWith('.js')) {
99+
/**
100+
* Simply transform the file without applying any extra AST transformers. We might consider to support using AST transformers on `node_modules` files when there are requests.
101+
* Since the files in `node_modules` are prebuilt, we don't need to perform type check on these files which should benefit the performance.
102+
*/
103+
const result: TranspileOutput = configSet.compilerModule.transpileModule(fileContent, {
104+
compilerOptions: {
105+
...configSet.parsedTsConfig.options,
106+
module:
107+
transformOptions.supportsStaticESM && configSet.useESM
108+
? configSet.parsedTsConfig.options.module
109+
: configSet.compilerModule.ModuleKind.CommonJS,
110+
},
111+
fileName: filePath,
112+
});
113+
114+
return {
115+
code: updateOutput(result.outputText, filePath, result.sourceMapText),
116+
};
117+
}
118+
96119
return super.process(fileContent, filePath, transformOptions);
97120
}
98121
}

0 commit comments

Comments
 (0)