Skip to content

Commit a72f51d

Browse files
authoredNov 10, 2021
refactor(transformers): use ts factory API for hoisting (#3058)
1 parent 7b602df commit a72f51d

17 files changed

+723
-338
lines changed
 

‎e2e/__tests__/hoist-jest.test.ts

+35-11
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,46 @@
1-
import * as path from 'path'
1+
import path from 'path'
2+
3+
import execa from 'execa'
24

35
import { json as runWithJson } from '../run-jest'
46
import { runNpmInstall } from '../utils'
57

6-
const DIR = path.resolve(__dirname, '..', 'hoist-jest')
8+
const { createBundle } = require('../../scripts/lib/bundle')
79

8-
beforeEach(() => {
9-
runNpmInstall(DIR)
10-
})
10+
const executeTest = (dirName: string): void => {
11+
test(`successfully runs the tests with isolatedModules: false`, () => {
12+
const { json } = runWithJson(dirName)
13+
14+
expect(json.success).toBe(true)
15+
})
16+
17+
test(`successfully runs the tests with isolatedModules true`, () => {
18+
const { json } = runWithJson(dirName, ['-c=jest-isolated.config.js'])
1119

12-
test('successfully runs the tests inside `hoist-jest/` with isolatedModules: false', () => {
13-
const { json } = runWithJson(DIR)
20+
expect(json.success).toBe(true)
21+
})
22+
}
1423

15-
expect(json.success).toBe(true)
24+
describe('non-ts-factory', () => {
25+
const DIR = path.resolve(__dirname, '..', 'hoist-jest', 'non-ts-factory')
26+
27+
beforeAll(() => {
28+
runNpmInstall(DIR)
29+
const bundle = createBundle()
30+
execa.sync('npm', ['install', '--no-package-lock', '--no-shrinkwrap', '--no-save', bundle], {
31+
cwd: DIR,
32+
})
33+
})
34+
35+
executeTest(DIR)
1636
})
1737

18-
test('successfully runs the tests inside `hoist-jest/` with isolatedModules true', () => {
19-
const { json } = runWithJson(DIR, ['-c=jest-isolated.config.js'])
38+
describe('ts-factory', () => {
39+
const DIR = path.resolve(__dirname, '..', 'hoist-jest', 'ts-factory')
40+
41+
beforeAll(() => {
42+
runNpmInstall(DIR)
43+
})
2044

21-
expect(json.success).toBe(true)
45+
executeTest(DIR)
2246
})

‎e2e/hoist-jest/__tests__/import-jest.test.js

+5-7
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@ aliasedJest.unmock('../__test_modules__/b')
1313
JestGlobals.jest.unmock('../__test_modules__/c')
1414

1515
// These will not be hoisted above imports
16-
// TODO: This is a bug to fix
17-
// {
18-
// const jest = {unmock: () => {}};
19-
// jest.unmock('../__test_modules__/d');
20-
// }
16+
{
17+
const jest = { unmock: () => {} }
18+
jest.unmock('../__test_modules__/d')
19+
}
2120

2221
// tests
2322

@@ -36,8 +35,7 @@ test('namespace import', () => {
3635
expect(c()).toBe('unmocked')
3736
})
3837

39-
// TODO: Enable this once the TODO above is fixed
40-
test.skip('fake jest, shadowed import', () => {
38+
test('fake jest, shadowed import', () => {
4139
expect(d._isMockFunction).toBe(true)
4240
expect(d()).toBe(undefined)
4341
})

‎e2e/hoist-jest/__tests__/integration.test.js

+10-13
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import React from 'react'
22
import Mocked from '../__test_modules__/mocked'
33
import Unmocked from '../__test_modules__/unmocked'
4-
// TODO: Enable when the 2nd TODO is fixed
5-
// import a from '../__test_modules__/a'
4+
import a from '../__test_modules__/a'
65
import b from '../__test_modules__/b'
76
import c from '../__test_modules__/c'
87
import d from '../__test_modules__/d'
@@ -48,18 +47,17 @@ jest.mock('virtual-module', () => 'kiwi', { virtual: true })
4847
jest.mock('has-flow-types', () => () => 3, {
4948
virtual: true,
5049
})
50+
jest.mock('../__test_modules__/a')
5151

5252
// These will not be hoisted
5353
jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b')
54-
// eslint-disable-next-line no-useless-concat
55-
jest.unmock('../__test_modules__/' + 'a')
5654
jest.dontMock('../__test_modules__/mocked')
57-
// TODO: This is a bug to fix
58-
// {
59-
// const jest = {unmock: () => {}};
60-
// // Would error (used before initialization) if hoisted to the top of the scope
61-
// jest.unmock('../__test_modules__/a');
62-
// }
55+
56+
{
57+
const jest = { unmock: () => {} }
58+
// Would error (used before initialization) if hoisted to the top of the scope
59+
jest.unmock('../__test_modules__/a')
60+
}
6361

6462
// This must not throw an error
6563
const myObject = { mock: () => {} }
@@ -122,9 +120,8 @@ describe('hoisting', () => {
122120
expect(Mocked._isMockFunction).toBe(true)
123121
expect(new Mocked().isMocked).toEqual(undefined)
124122

125-
// TODO: Enable this once the TODO above is fixed
126-
// expect(a._isMockFunction).toBe(true);
127-
// expect(a()).toEqual(undefined);
123+
expect(a._isMockFunction).toBe(true)
124+
expect(a()).toEqual(undefined)
128125

129126
expect(b._isMockFunction).toBe(true)
130127
expect(b()).toEqual(undefined)

‎e2e/hoist-jest/jest-isolated.config.js ‎e2e/hoist-jest/non-ts-factory/jest-isolated.config.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ module.exports = {
88
},
99
},
1010
},
11+
roots: ['<rootDir>', '<rootDir>/../__tests__'],
12+
moduleNameMapper: {
13+
react$: '<rootDir>/node_modules/react',
14+
},
1115
transform: {
12-
'^.+.[tj]sx?$': '<rootDir>/../../dist/index.js',
16+
'^.+.[tj]sx?$': 'ts-jest',
1317
},
1418
}

‎e2e/hoist-jest/non-ts-factory/package-lock.json

+95
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"dependencies": {
3+
"react": "^17.0.2",
4+
"typescript": "~3.8.3"
5+
},
6+
"jest": {
7+
"automock": true,
8+
"globals": {
9+
"ts-jest": {
10+
"diagnostics": false,
11+
"tsconfig": {
12+
"allowJs": true
13+
}
14+
}
15+
},
16+
"roots": ["<rootDir>", "<rootDir>/../__tests__"],
17+
"moduleNameMapper": {
18+
"react$": "<rootDir>/node_modules/react"
19+
},
20+
"transform": {
21+
"^.+\\.[tj]sx?$": "ts-jest"
22+
}
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module.exports = {
2+
automock: true,
3+
globals: {
4+
'ts-jest': {
5+
isolatedModules: true,
6+
tsconfig: {
7+
allowJs: true,
8+
},
9+
},
10+
},
11+
roots: ['<rootDir>', '<rootDir>/../__tests__'],
12+
moduleNameMapper: {
13+
react$: '<rootDir>/node_modules/react',
14+
},
15+
transform: {
16+
'^.+.[tj]sx?$': '<rootDir>/../../../dist/index.js',
17+
},
18+
}

‎e2e/hoist-jest/package-lock.json ‎e2e/hoist-jest/ts-factory/package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎e2e/hoist-jest/package.json ‎e2e/hoist-jest/ts-factory/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
}
1313
}
1414
},
15+
"roots": ["<rootDir>", "<rootDir>/../__tests__"],
1516
"moduleNameMapper": {
16-
"@jest/globals": "<rootDir>/../../node_modules/@jest/globals",
17-
"@types/react": "<rootDir>/../../node_modules/@types/react"
17+
"react$": "<rootDir>/node_modules/react"
1818
},
1919
"transform": {
20-
"^.+\\.[tj]sx?$": "<rootDir>/../../dist/index.js"
20+
"^.+\\.[tj]sx?$": "<rootDir>/../../../dist/index.js"
2121
}
2222
}
2323
}

‎e2e/utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const runNpmInstall = (cwd: Config.Path, env?: Record<string, string>): R
4040
fs.writeFileSync(lockfilePath, '')
4141
}
4242

43-
return run(exists ? 'npm ci' : 'npm i', cwd, env)
43+
return run(exists ? 'npm ci --no-optional' : 'npm i --no-optional', cwd, env)
4444
}
4545

4646
const replaceTime = (str: string): string =>

‎src/config/__snapshots__/config-set.spec.ts.snap

+5-5
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ Object {
6060
Object {
6161
"factory": [Function],
6262
"name": "hoist-jest",
63-
"version": 1,
63+
"version": 2,
6464
},
6565
],
6666
}
@@ -74,7 +74,7 @@ Object {
7474
Object {
7575
"factory": [Function],
7676
"name": "hoist-jest",
77-
"version": 1,
77+
"version": 2,
7878
},
7979
Object {
8080
"factory": [Function],
@@ -95,7 +95,7 @@ Object {
9595
Object {
9696
"factory": [Function],
9797
"name": "hoist-jest",
98-
"version": 1,
98+
"version": 2,
9999
},
100100
],
101101
}
@@ -113,7 +113,7 @@ Object {
113113
Object {
114114
"factory": [Function],
115115
"name": "hoist-jest",
116-
"version": 1,
116+
"version": 2,
117117
},
118118
],
119119
}
@@ -127,7 +127,7 @@ Object {
127127
Object {
128128
"factory": [Function],
129129
"name": "hoist-jest",
130-
"version": 1,
130+
"version": 2,
131131
},
132132
Object {
133133
"factory": [Function],
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,64 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`hoisting should hoist correctly jest methods 1`] = `
3+
exports[`hoist-jest with module CommonJS should hoist correctly when not using @jest/globals 1`] = `
44
"\\"use strict\\";
55
Object.defineProperty(exports, \\"__esModule\\", { value: true });
6-
var globals_1 = require(\\"@jest/globals\\");
76
// These will all be hoisted above imports
87
jest.unmock('react');
98
jest.deepUnmock('../__test_modules__/Unmocked');
109
jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d');
11-
jest.mock('../__test_modules__/f', function () {
10+
jest.mock('../__test_modules__/f', () => {
1211
if (!global.CALLS) {
1312
global.CALLS = 0;
1413
}
1514
global.CALLS++;
1615
return {
1716
_isMock: true,
18-
fn: function () {
17+
fn: () => {
1918
// The \`jest.mock\` transform will allow require, built-ins and globals.
20-
var path = require('path');
21-
var array = new Array(3);
19+
const path = require('path');
20+
const array = new Array(3);
2221
array[0] = path.sep;
23-
return jest.fn(function () { return array; });
22+
return jest.fn(() => array);
2423
},
2524
};
2625
});
27-
jest.mock(\\"../__test_modules__/jestBackticks\\");
28-
jest.mock('virtual-module', function () { return 'kiwi'; }, { virtual: true });
26+
jest.mock(\`../__test_modules__/jestBackticks\`);
27+
jest.mock('virtual-module', () => 'kiwi', { virtual: true });
2928
// This has types that should be ignored by the out-of-scope variables check.
30-
jest.mock('has-flow-types', function () { return function (props) { return 3; }; }, {
29+
jest.mock('has-flow-types', () => (props) => 3, {
3130
virtual: true,
3231
});
33-
jest.unmock('../__test_modules__/' + 'a');
34-
jest.mock('../__test_modules__/f', function () { return MockMethods; });
35-
var Unmocked_1 = require(\\"../__test_modules__/Unmocked\\");
36-
var Mocked_1 = require(\\"../__test_modules__/Mocked\\");
37-
var a_1 = require(\\"../__test_modules__/a\\");
38-
var b_1 = require(\\"../__test_modules__/b\\");
39-
var c_1 = require(\\"../__test_modules__/c\\");
40-
var d_1 = require(\\"../__test_modules__/d\\");
41-
var jestBackticks_1 = require(\\"../__test_modules__/jestBackticks\\");
32+
jest.mock('../__test_modules__/f', () => MockMethods);
33+
const Unmocked_1 = require(\\"../__test_modules__/Unmocked\\");
34+
const Mocked_1 = require(\\"../__test_modules__/Mocked\\");
35+
const a_1 = require(\\"../__test_modules__/a\\");
36+
const b_1 = require(\\"../__test_modules__/b\\");
37+
const c_1 = require(\\"../__test_modules__/c\\");
38+
const d_1 = require(\\"../__test_modules__/d\\");
39+
const jestBackticks_1 = require(\\"../__test_modules__/jestBackticks\\");
4240
// The virtual mock call below will be hoisted above this \`require\` call.
43-
var virtualModule = require('virtual-module');
44-
var e;
41+
const virtualModule = require('virtual-module');
42+
let e;
4543
(function () {
4644
// hoisted to the top of the function scope
4745
jest.unmock('../__test_modules__/e');
48-
var _getJestObj = 42;
46+
const _getJestObj = 42;
4947
e = require('../__test_modules__/e').default;
5048
})();
5149
// These will not be hoisted
5250
jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b');
5351
jest.dontMock('../__test_modules__/Mocked');
5452
{
53+
const jest = { unmock: () => { } };
5554
// Would error (used before initialization) if hoisted to the top of the scope
5655
jest.unmock('../__test_modules__/a');
57-
var jest = { unmock: function () { } };
5856
}
5957
// This must not throw an error
60-
var myObject = { mock: function () { } };
58+
const myObject = { mock: () => { } };
6159
myObject.mock('apple', 27);
6260
// Variable names prefixed with \`mock\` (ignore case) should not throw as out-of-scope
63-
var MockMethods = function () { };
61+
const MockMethods = () => { };
6462
console.log(Unmocked_1.default);
6563
console.log(Mocked_1.default);
6664
console.log(a_1.default);
@@ -70,33 +68,220 @@ console.log(d_1.default);
7068
console.log(e);
7169
console.log(virtualModule);
7270
console.log(jestBackticks_1.default);
73-
console.log(globals_1.it);
7471
"
7572
`;
7673

77-
exports[`hoisting should hoist correctly jest methods 2`] = `
74+
exports[`hoist-jest with module CommonJS should hoist correctly when using with @jest/globals 1`] = `
7875
"\\"use strict\\";
7976
Object.defineProperty(exports, \\"__esModule\\", { value: true });
80-
var globals_1 = require(\\"@jest/globals\\");
81-
var globals_2 = require(\\"@jest/globals\\");
82-
var JestGlobals = require(\\"@jest/globals\\");
83-
// These will be hoisted above imports
84-
globals_1.jest.unmock('../__test_modules__/a');
85-
globals_2.jest.unmock('../__test_modules__/b');
86-
JestGlobals.jest.unmock('../__test_modules__/c');
87-
var a_1 = require(\\"../__test_modules__/a\\");
88-
var b_1 = require(\\"../__test_modules__/b\\");
89-
var c_1 = require(\\"../__test_modules__/c\\");
90-
var d_1 = require(\\"../__test_modules__/d\\");
91-
// These will not be hoisted above imports
77+
const JestGlobals = require(\\"@jest/globals\\");
78+
const globals_1 = require(\\"@jest/globals\\");
79+
const globals_2 = require(\\"@jest/globals\\");
80+
// These will all be hoisted above imports
81+
globals_2.jest.unmock('react');
82+
globals_1.jest.deepUnmock('../__test_modules__/Unmocked');
83+
JestGlobals.jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d');
84+
JestGlobals.jest.mock('../__test_modules__/f', () => {
85+
if (!global.CALLS) {
86+
global.CALLS = 0;
87+
}
88+
global.CALLS++;
89+
return {
90+
_isMock: true,
91+
fn: () => {
92+
// The \`jest.mock\` transform will allow require, built-ins and globals.
93+
const path = require('path');
94+
const array = new Array(3);
95+
array[0] = path.sep;
96+
return globals_2.jest.fn(() => array);
97+
},
98+
};
99+
});
100+
globals_1.jest.jest.mock(\`../__test_modules__/jestBackticks\`);
101+
globals_2.jest.mock('virtual-module', () => 'kiwi', { virtual: true });
102+
// This has types that should be ignored by the out-of-scope variables check.
103+
globals_2.jest.mock('has-flow-types', () => (props) => 3, {
104+
virtual: true,
105+
});
106+
JestGlobals.jest.mock('../__test_modules__/f', () => MockMethods);
107+
const Unmocked_1 = require(\\"../__test_modules__/Unmocked\\");
108+
const Mocked_1 = require(\\"../__test_modules__/Mocked\\");
109+
const a_1 = require(\\"../__test_modules__/a\\");
110+
const b_1 = require(\\"../__test_modules__/b\\");
111+
const c_1 = require(\\"../__test_modules__/c\\");
112+
const d_1 = require(\\"../__test_modules__/d\\");
113+
const jestBackticks_1 = require(\\"../__test_modules__/jestBackticks\\");
114+
// The virtual mock call below will be hoisted above this \`require\` call.
115+
const virtualModule = require('virtual-module');
116+
let e;
117+
(function () {
118+
// hoisted to the top of the function scope
119+
globals_2.jest.unmock('../__test_modules__/e');
120+
const _getJestObj = 42;
121+
e = require('../__test_modules__/e').default;
122+
})();
123+
// These will not be hoisted
124+
globals_2.jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b');
125+
globals_1.jest.dontMock('../__test_modules__/Mocked');
92126
{
93-
jest_1.unmock('../__test_modules__/d');
94-
var jest_1 = { unmock: function () { } };
127+
const jest = { unmock: () => { } };
128+
// Would error (used before initialization) if hoisted to the top of the scope
129+
jest.unmock('../__test_modules__/a');
95130
}
131+
// This must not throw an error
132+
const myObject = { mock: () => { } };
133+
myObject.mock('apple', 27);
134+
// Variable names prefixed with \`mock\` (ignore case) should not throw as out-of-scope
135+
const MockMethods = () => { };
136+
console.log(Unmocked_1.default);
137+
console.log(Mocked_1.default);
96138
console.log(a_1.default);
97139
console.log(b_1.default);
98140
console.log(c_1.default);
99141
console.log(d_1.default);
100-
console.log(globals_1.it);
142+
console.log(e);
143+
console.log(virtualModule);
144+
console.log(jestBackticks_1.default);
145+
"
146+
`;
147+
148+
exports[`hoist-jest with module ESM should hoist correctly when not using @jest/globals 1`] = `
149+
"// These will all be hoisted above imports
150+
jest.unmock('react');
151+
jest.deepUnmock('../__test_modules__/Unmocked');
152+
jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d');
153+
jest.mock('../__test_modules__/f', () => {
154+
if (!global.CALLS) {
155+
global.CALLS = 0;
156+
}
157+
global.CALLS++;
158+
return {
159+
_isMock: true,
160+
fn: () => {
161+
// The \`jest.mock\` transform will allow require, built-ins and globals.
162+
const path = require('path');
163+
const array = new Array(3);
164+
array[0] = path.sep;
165+
return jest.fn(() => array);
166+
},
167+
};
168+
});
169+
jest.mock(\`../__test_modules__/jestBackticks\`);
170+
jest.mock('virtual-module', () => 'kiwi', { virtual: true });
171+
// This has types that should be ignored by the out-of-scope variables check.
172+
jest.mock('has-flow-types', () => (props) => 3, {
173+
virtual: true,
174+
});
175+
jest.mock('../__test_modules__/f', () => MockMethods);
176+
import Unmocked from '../__test_modules__/Unmocked';
177+
import Mocked from '../__test_modules__/Mocked';
178+
import a from '../__test_modules__/a';
179+
import b from '../__test_modules__/b';
180+
import c from '../__test_modules__/c';
181+
import d from '../__test_modules__/d';
182+
import jestBackticks from '../__test_modules__/jestBackticks';
183+
// The virtual mock call below will be hoisted above this \`require\` call.
184+
const virtualModule = require('virtual-module');
185+
let e;
186+
(function () {
187+
// hoisted to the top of the function scope
188+
jest.unmock('../__test_modules__/e');
189+
const _getJestObj = 42;
190+
e = require('../__test_modules__/e').default;
191+
})();
192+
// These will not be hoisted
193+
jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b');
194+
jest.dontMock('../__test_modules__/Mocked');
195+
{
196+
const jest = { unmock: () => { } };
197+
// Would error (used before initialization) if hoisted to the top of the scope
198+
jest.unmock('../__test_modules__/a');
199+
}
200+
// This must not throw an error
201+
const myObject = { mock: () => { } };
202+
myObject.mock('apple', 27);
203+
// Variable names prefixed with \`mock\` (ignore case) should not throw as out-of-scope
204+
const MockMethods = () => { };
205+
console.log(Unmocked);
206+
console.log(Mocked);
207+
console.log(a);
208+
console.log(b);
209+
console.log(c);
210+
console.log(d);
211+
console.log(e);
212+
console.log(virtualModule);
213+
console.log(jestBackticks);
214+
"
215+
`;
216+
217+
exports[`hoist-jest with module ESM should hoist correctly when using with @jest/globals 1`] = `
218+
"import * as JestGlobals from '@jest/globals';
219+
import { jest as aliasedJest } from '@jest/globals';
220+
import { jest } from '@jest/globals';
221+
// These will all be hoisted above imports
222+
jest.unmock('react');
223+
aliasedJest.deepUnmock('../__test_modules__/Unmocked');
224+
JestGlobals.jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d');
225+
JestGlobals.jest.mock('../__test_modules__/f', () => {
226+
if (!global.CALLS) {
227+
global.CALLS = 0;
228+
}
229+
global.CALLS++;
230+
return {
231+
_isMock: true,
232+
fn: () => {
233+
// The \`jest.mock\` transform will allow require, built-ins and globals.
234+
const path = require('path');
235+
const array = new Array(3);
236+
array[0] = path.sep;
237+
return jest.fn(() => array);
238+
},
239+
};
240+
});
241+
aliasedJest.jest.mock(\`../__test_modules__/jestBackticks\`);
242+
jest.mock('virtual-module', () => 'kiwi', { virtual: true });
243+
// This has types that should be ignored by the out-of-scope variables check.
244+
jest.mock('has-flow-types', () => (props) => 3, {
245+
virtual: true,
246+
});
247+
JestGlobals.jest.mock('../__test_modules__/f', () => MockMethods);
248+
import Unmocked from '../__test_modules__/Unmocked';
249+
import Mocked from '../__test_modules__/Mocked';
250+
import a from '../__test_modules__/a';
251+
import b from '../__test_modules__/b';
252+
import c from '../__test_modules__/c';
253+
import d from '../__test_modules__/d';
254+
import jestBackticks from '../__test_modules__/jestBackticks';
255+
// The virtual mock call below will be hoisted above this \`require\` call.
256+
const virtualModule = require('virtual-module');
257+
let e;
258+
(function () {
259+
// hoisted to the top of the function scope
260+
jest.unmock('../__test_modules__/e');
261+
const _getJestObj = 42;
262+
e = require('../__test_modules__/e').default;
263+
})();
264+
// These will not be hoisted
265+
jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b');
266+
aliasedJest.dontMock('../__test_modules__/Mocked');
267+
{
268+
const jest = { unmock: () => { } };
269+
// Would error (used before initialization) if hoisted to the top of the scope
270+
jest.unmock('../__test_modules__/a');
271+
}
272+
// This must not throw an error
273+
const myObject = { mock: () => { } };
274+
myObject.mock('apple', 27);
275+
// Variable names prefixed with \`mock\` (ignore case) should not throw as out-of-scope
276+
const MockMethods = () => { };
277+
console.log(Unmocked);
278+
console.log(Mocked);
279+
console.log(a);
280+
console.log(b);
281+
console.log(c);
282+
console.log(d);
283+
console.log(e);
284+
console.log(virtualModule);
285+
console.log(jestBackticks);
101286
"
102287
`;

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

+173-105
Original file line numberDiff line numberDiff line change
@@ -3,130 +3,198 @@ import ts from 'typescript'
33
import { createConfigSet } from '../__helpers__/fakers'
44
import { TsCompiler } from '../compiler/ts-compiler'
55

6-
import * as hoist from './hoist-jest'
7-
8-
const CODE_WITH_HOISTING_NO_JEST_GLOBALS = `
9-
import React from 'react'
10-
import Unmocked from '../__test_modules__/Unmocked'
11-
import Mocked from '../__test_modules__/Mocked'
12-
import {it} from '@jest/globals'
13-
import a from '../__test_modules__/a'
14-
import b from '../__test_modules__/b'
15-
import c from '../__test_modules__/c'
16-
import d from '../__test_modules__/d'
17-
import f from '../__test_modules__/f'
18-
import jestBackticks from '../__test_modules__/jestBackticks'
19-
20-
// The virtual mock call below will be hoisted above this \`require\` call.
21-
const virtualModule = require('virtual-module')
22-
23-
// These will all be hoisted above imports
24-
jest.unmock('react')
25-
jest.deepUnmock('../__test_modules__/Unmocked')
26-
jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d')
27-
28-
let e;
29-
(function () {
30-
const _getJestObj = 42;
31-
e = require('../__test_modules__/e').default
32-
// hoisted to the top of the function scope
33-
jest.unmock('../__test_modules__/e')
34-
})()
35-
36-
jest.mock('../__test_modules__/f', () => {
37-
if (!global.CALLS) {
38-
global.CALLS = 0
39-
}
40-
global.CALLS++
41-
42-
return {
43-
_isMock: true,
44-
fn: () => {
45-
// The \`jest.mock\` transform will allow require, built-ins and globals.
46-
const path = require('path')
47-
const array = new Array(3)
48-
array[0] = path.sep
49-
return jest.fn(() => array)
50-
},
51-
};
52-
})
53-
jest.mock(\`../__test_modules__/jestBackticks\`)
54-
jest.mock('virtual-module', () => 'kiwi', {virtual: true})
55-
// This has types that should be ignored by the out-of-scope variables check.
56-
jest.mock('has-flow-types', () => (props: {children: mixed}) => 3, {
57-
virtual: true,
58-
})
6+
import { factory as hoistJest, name, version } from './hoist-jest'
597

60-
// These will not be hoisted
61-
jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b')
62-
jest.unmock('../__test_modules__/' + 'a')
63-
jest.dontMock('../__test_modules__/Mocked')
64-
{
65-
const jest = {unmock: () => {}};
66-
// Would error (used before initialization) if hoisted to the top of the scope
67-
jest.unmock('../__test_modules__/a')
68-
}
69-
70-
// This must not throw an error
71-
const myObject = {mock: () => {}}
72-
myObject.mock('apple', 27)
73-
74-
// Variable names prefixed with \`mock\` (ignore case) should not throw as out-of-scope
75-
const MockMethods = () => {}
76-
jest.mock('../__test_modules__/f', () => MockMethods)
77-
78-
console.log(Unmocked)
79-
console.log(Mocked)
80-
console.log(a)
81-
console.log(b)
82-
console.log(c)
83-
console.log(d)
84-
console.log(e)
85-
console.log(virtualModule)
86-
console.log(jestBackticks)
87-
console.log(it)
88-
`
89-
const CODE_WITH_HOISTING_HAS_JEST_GLOBALS = `
8+
const BASE_IMPORT_CODE = `
9+
import React from 'react'
10+
import Unmocked from '../__test_modules__/Unmocked'
11+
import Mocked from '../__test_modules__/Mocked'
9012
import a from '../__test_modules__/a'
9113
import b from '../__test_modules__/b'
92-
93-
import {it, jest} from '@jest/globals'
94-
import {jest as aliasedJest} from '@jest/globals'
95-
import * as JestGlobals from '@jest/globals'
96-
9714
import c from '../__test_modules__/c'
9815
import d from '../__test_modules__/d'
16+
import f from '../__test_modules__/f'
17+
import jestBackticks from '../__test_modules__/jestBackticks'
18+
`
9919

100-
// These will be hoisted above imports
20+
const CODE_WITH_HOISTING_NO_JEST_GLOBALS =
21+
BASE_IMPORT_CODE +
22+
`\n` +
23+
`
24+
// The virtual mock call below will be hoisted above this \`require\` call.
25+
const virtualModule = require('virtual-module')
26+
27+
// These will all be hoisted above imports
28+
jest.unmock('react')
29+
jest.deepUnmock('../__test_modules__/Unmocked')
30+
jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d')
31+
32+
let e;
33+
(function () {
34+
const _getJestObj = 42;
35+
e = require('../__test_modules__/e').default
36+
// hoisted to the top of the function scope
37+
jest.unmock('../__test_modules__/e')
38+
})()
39+
40+
jest.mock('../__test_modules__/f', () => {
41+
if (!global.CALLS) {
42+
global.CALLS = 0
43+
}
44+
global.CALLS++
45+
46+
return {
47+
_isMock: true,
48+
fn: () => {
49+
// The \`jest.mock\` transform will allow require, built-ins and globals.
50+
const path = require('path')
51+
const array = new Array(3)
52+
array[0] = path.sep
53+
return jest.fn(() => array)
54+
},
55+
};
56+
})
57+
jest.mock(\`../__test_modules__/jestBackticks\`)
58+
jest.mock('virtual-module', () => 'kiwi', {virtual: true})
59+
// This has types that should be ignored by the out-of-scope variables check.
60+
jest.mock('has-flow-types', () => (props: {children: mixed}) => 3, {
61+
virtual: true,
62+
})
63+
64+
// These will not be hoisted
65+
jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b')
66+
jest.dontMock('../__test_modules__/Mocked')
67+
{
68+
const jest = {unmock: () => {}};
69+
// Would error (used before initialization) if hoisted to the top of the scope
70+
jest.unmock('../__test_modules__/a')
71+
}
10172
102-
jest.unmock('../__test_modules__/a')
103-
aliasedJest.unmock('../__test_modules__/b')
104-
JestGlobals.jest.unmock('../__test_modules__/c')
73+
// This must not throw an error
74+
const myObject = {mock: () => {}}
75+
myObject.mock('apple', 27)
10576
106-
// These will not be hoisted above imports
77+
// Variable names prefixed with \`mock\` (ignore case) should not throw as out-of-scope
78+
const MockMethods = () => {}
79+
jest.mock('../__test_modules__/f', () => MockMethods)
10780
81+
console.log(Unmocked)
82+
console.log(Mocked)
83+
console.log(a)
84+
console.log(b)
85+
console.log(c)
86+
console.log(d)
87+
console.log(e)
88+
console.log(virtualModule)
89+
console.log(jestBackticks)
90+
`
91+
const CODE_WITH_HOISTING_HAS_JEST_GLOBALS =
92+
BASE_IMPORT_CODE +
93+
`\n` +
94+
`
95+
import {jest} from '@jest/globals'
96+
import {jest as aliasedJest} from '@jest/globals'
97+
import * as JestGlobals from '@jest/globals'
98+
99+
// The virtual mock call below will be hoisted above this \`require\` call.
100+
const virtualModule = require('virtual-module')
101+
102+
// These will all be hoisted above imports
103+
jest.unmock('react')
104+
aliasedJest.deepUnmock('../__test_modules__/Unmocked')
105+
JestGlobals.jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d')
106+
107+
let e;
108+
(function () {
109+
const _getJestObj = 42;
110+
e = require('../__test_modules__/e').default
111+
// hoisted to the top of the function scope
112+
jest.unmock('../__test_modules__/e')
113+
})()
114+
115+
JestGlobals.jest.mock('../__test_modules__/f', () => {
116+
if (!global.CALLS) {
117+
global.CALLS = 0
118+
}
119+
global.CALLS++
120+
121+
return {
122+
_isMock: true,
123+
fn: () => {
124+
// The \`jest.mock\` transform will allow require, built-ins and globals.
125+
const path = require('path')
126+
const array = new Array(3)
127+
array[0] = path.sep
128+
return jest.fn(() => array)
129+
},
130+
};
131+
})
132+
aliasedJest.jest.mock(\`../__test_modules__/jestBackticks\`)
133+
jest.mock('virtual-module', () => 'kiwi', {virtual: true})
134+
// This has types that should be ignored by the out-of-scope variables check.
135+
jest.mock('has-flow-types', () => (props: {children: mixed}) => 3, {
136+
virtual: true,
137+
})
138+
139+
// These will not be hoisted
140+
jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b')
141+
aliasedJest.dontMock('../__test_modules__/Mocked')
108142
{
109143
const jest = {unmock: () => {}};
110-
jest.unmock('../__test_modules__/d');
144+
// Would error (used before initialization) if hoisted to the top of the scope
145+
jest.unmock('../__test_modules__/a')
111146
}
112147
148+
// This must not throw an error
149+
const myObject = {mock: () => {}}
150+
myObject.mock('apple', 27)
151+
152+
// Variable names prefixed with \`mock\` (ignore case) should not throw as out-of-scope
153+
const MockMethods = () => {}
154+
JestGlobals.jest.mock('../__test_modules__/f', () => MockMethods)
155+
156+
console.log(Unmocked)
157+
console.log(Mocked)
113158
console.log(a)
114159
console.log(b)
115160
console.log(c)
116161
console.log(d)
117-
console.log(it)
162+
console.log(e)
163+
console.log(virtualModule)
164+
console.log(jestBackticks)
118165
`
119166

120-
const createFactory = () => hoist.factory(new TsCompiler(createConfigSet(), new Map()))
121-
const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } })
167+
const createFactory = () => hoistJest(new TsCompiler(createConfigSet(), new Map()))
168+
const transpile = (source: string, moduleKind: number) =>
169+
ts.transpileModule(source, {
170+
transformers: { before: [createFactory()] },
171+
compilerOptions: {
172+
module: moduleKind,
173+
target: ts.ScriptTarget.ES2015,
174+
},
175+
})
122176

123-
describe('hoisting', () => {
124-
test.each([CODE_WITH_HOISTING_NO_JEST_GLOBALS, CODE_WITH_HOISTING_HAS_JEST_GLOBALS])(
125-
'should hoist correctly jest methods',
126-
(data) => {
127-
const out = transpile(data)
177+
const executeTest = (moduleKind: number): void => {
178+
test('should hoist correctly when not using @jest/globals', () => {
179+
expect(transpile(CODE_WITH_HOISTING_NO_JEST_GLOBALS, moduleKind).outputText).toMatchSnapshot()
180+
})
128181

129-
expect(out.outputText).toMatchSnapshot()
130-
},
131-
)
182+
test('should hoist correctly when using with @jest/globals', () => {
183+
expect(transpile(CODE_WITH_HOISTING_HAS_JEST_GLOBALS, moduleKind).outputText).toMatchSnapshot()
184+
})
185+
}
186+
187+
describe('hoist-jest', () => {
188+
test('should have correct transformer name and version', () => {
189+
expect(name).toBe('hoist-jest')
190+
expect(version).toBe(2)
191+
})
192+
193+
describe('with module CommonJS', () => {
194+
executeTest(ts.ModuleKind.CommonJS)
195+
})
196+
197+
describe('with module ESM', () => {
198+
executeTest(ts.ModuleKind.ESNext)
199+
})
132200
})

‎src/transformers/hoist-jest.ts

+116-140
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,51 @@
1+
import type { Jest } from '@jest/environment'
12
import { LogContexts, LogLevels } from 'bs-logger'
2-
import type {
3-
Block,
4-
ExpressionStatement,
5-
ImportDeclaration,
6-
Node,
7-
SourceFile,
8-
Statement,
9-
TransformationContext,
10-
Transformer,
11-
Visitor,
12-
} from 'typescript'
3+
import type _ts from 'typescript'
134

145
import type { TsCompilerInstance } from '../types'
156

167
/**
178
* Remember to increase the version whenever transformer's content is changed. This is to inform Jest to not reuse
189
* the previous cache which contains old transformer's content
1910
*/
20-
export const version = 1
11+
export const version = 2
2112
// Used for constructing cache key
2213
export const name = 'hoist-jest'
2314

24-
/**
25-
* What methods of `jest` we should hoist
26-
*/
27-
const HOIST_METHODS = ['mock', 'unmock', 'enableAutomock', 'disableAutomock', 'deepUnmock']
15+
type HoistedMethod = keyof Pick<Jest, 'mock' | 'unmock' | 'enableAutomock' | 'disableAutomock' | 'deepUnmock'>
16+
17+
const HOIST_METHODS: HoistedMethod[] = ['mock', 'unmock', 'enableAutomock', 'disableAutomock', 'deepUnmock']
2818
const JEST_GLOBALS_MODULE_NAME = '@jest/globals'
2919
const JEST_GLOBAL_NAME = 'jest'
30-
const ROOT_LEVEL_AST = 1
3120

32-
/**
33-
* The factory of hoisting transformer factory
34-
*
35-
* @internal
36-
*/
37-
export function factory({ configSet }: TsCompilerInstance): (ctx: TransformationContext) => Transformer<SourceFile> {
38-
const logger = configSet.logger.child({ namespace: 'ts-hoisting' })
39-
/**
40-
* Our compiler (typescript, or a module with typescript-like interface)
41-
* To access Program or TypeChecker, do: cs.tsCompiler.program or cs.tsCompiler.program.getTypeChecker()
42-
*/
21+
export function factory({ configSet }: TsCompilerInstance) {
22+
const logger = configSet.logger.child({ namespace: name })
4323
const ts = configSet.compilerModule
44-
const importNames: string[] = []
24+
const tsFactory = ts.factory ? ts.factory : ts
25+
const importNamesOfJestObj: string[] = []
26+
27+
const isJestGlobalImport = (node: _ts.Node): node is _ts.ImportDeclaration => {
28+
return (
29+
ts.isImportDeclaration(node) &&
30+
ts.isStringLiteral(node.moduleSpecifier) &&
31+
node.moduleSpecifier.text === JEST_GLOBALS_MODULE_NAME
32+
)
33+
}
4534

46-
function shouldHoistExpression(node: Node): boolean {
35+
const shouldHoistExpression = (node: _ts.Node): node is _ts.ExpressionStatement => {
4736
if (
4837
ts.isCallExpression(node) &&
4938
ts.isPropertyAccessExpression(node.expression) &&
50-
HOIST_METHODS.includes(node.expression.name.text)
39+
HOIST_METHODS.includes(node.expression.name.text as HoistedMethod)
5140
) {
52-
if (importNames.length) {
41+
if (importNamesOfJestObj.length) {
5342
// @jest/globals is in used
5443
return (
55-
(ts.isIdentifier(node.expression.expression) && importNames.includes(node.expression.expression.text)) ||
44+
(ts.isIdentifier(node.expression.expression) &&
45+
importNamesOfJestObj.includes(node.expression.expression.text)) ||
5646
(ts.isPropertyAccessExpression(node.expression.expression) &&
5747
ts.isIdentifier(node.expression.expression.expression) &&
58-
importNames.includes(node.expression.expression.expression.text)) ||
48+
importNamesOfJestObj.includes(node.expression.expression.expression.text)) ||
5949
shouldHoistExpression(node.expression.expression)
6050
)
6151
} else {
@@ -73,132 +63,118 @@ export function factory({ configSet }: TsCompilerInstance): (ctx: Transformation
7363
/**
7464
* Checks whether given node is a statement that we need to hoist
7565
*/
76-
function shouldHoistNode(node: Node): node is ExpressionStatement {
66+
const isHoistableStatement = (node: _ts.Node): node is _ts.ExpressionStatement => {
7767
return ts.isExpressionStatement(node) && shouldHoistExpression(node.expression)
7868
}
7969

80-
function isJestGlobalImport(node: Node): node is ImportDeclaration {
81-
return (
82-
ts.isImportDeclaration(node) &&
83-
ts.isStringLiteral(node.moduleSpecifier) &&
84-
node.moduleSpecifier.text === JEST_GLOBALS_MODULE_NAME
70+
const canHoistInBlockScope = (node: _ts.Block): node is _ts.Block =>
71+
!!node.statements.find(
72+
(stmt) =>
73+
ts.isVariableStatement(stmt) &&
74+
stmt.declarationList.declarations.find((decl) => decl.name.getText() !== JEST_GLOBAL_NAME) &&
75+
node.statements.find((stmt) => isHoistableStatement(stmt)),
8576
)
86-
}
8777

8878
/**
89-
* Create a source file visitor which will visit all nodes in a source file
79+
* Sort statements according to priority
80+
* - Import Jest object from `@jest/globals`
81+
* - Hoistable methods
82+
* - Non-hoistable methods
9083
*/
91-
function createVisitor(ctx: TransformationContext, _: SourceFile) {
92-
/**
93-
* Current block level
94-
*/
95-
let level = 0
96-
/**
97-
* List of nodes which needs to be hoisted, indexed by their owning level
98-
*/
99-
const hoisted: Statement[][] = []
100-
/**
101-
* Called when we enter a block to increase the level
102-
*/
103-
const enter = () => {
104-
level++
105-
// reuse arrays
106-
if (hoisted[level]) {
107-
hoisted[level].splice(0, hoisted[level].length)
108-
}
84+
const sortStatements = (statements: _ts.Statement[]): _ts.Statement[] => {
85+
if (statements.length <= 1) {
86+
return statements
10987
}
110-
/**
111-
* Called when we leave a block to decrease the level
112-
*/
113-
const exit = () => level--
114-
/**
115-
* Adds a node to the list of nodes to be hoisted in the current level
116-
*
117-
* @param node The node to hoist
118-
*/
119-
const hoist = (node: Statement) => {
120-
if (hoisted[level]) {
121-
hoisted[level].push(node)
88+
const pivot = statements[0]
89+
const leftPart: _ts.Statement[] = []
90+
const rightPart: _ts.Statement[] = []
91+
for (let i = 1; i < statements.length; i++) {
92+
const currentStatement = statements[i]
93+
if (isJestGlobalImport(currentStatement)) {
94+
leftPart.push(currentStatement)
12295
} else {
123-
hoisted[level] = [node]
96+
isHoistableStatement(currentStatement) && !isHoistableStatement(pivot) && !isJestGlobalImport(pivot)
97+
? leftPart.push(currentStatement)
98+
: rightPart.push(currentStatement)
12499
}
125100
}
126-
/**
127-
* Our main visitor, which will be called recursively for each node in the source file's AST
128-
*/
129-
const visitor: Visitor = (node) => {
130-
// enter this level
131-
enter()
132-
133-
// visit each child
134-
let resultNode = ts.visitEachChild(node, visitor, ctx)
135-
/**
136-
* Gather all possible import names, from different types of import syntax including:
137-
* - named import, e.g. `import { jest } from '@jest/globals'`
138-
* - aliased named import, e.g. `import {jest as aliasedJest} from '@jest/globals'`
139-
* - namespace import, e.g `import * as JestGlobals from '@jest/globals'`
140-
*/
141-
if (
142-
isJestGlobalImport(resultNode) &&
143-
resultNode.importClause?.namedBindings &&
144-
(ts.isNamespaceImport(resultNode.importClause.namedBindings) ||
145-
ts.isNamedImports(resultNode.importClause.namedBindings))
146-
) {
147-
const { namedBindings } = resultNode.importClause
148-
const jestImportName = ts.isNamespaceImport(namedBindings)
149-
? namedBindings.name.text
150-
: namedBindings.elements.find(
151-
(element) => element.name.text === JEST_GLOBAL_NAME || element.propertyName?.text === JEST_GLOBAL_NAME,
152-
)?.name.text
153-
if (jestImportName) {
154-
importNames.push(jestImportName)
155-
}
156-
}
157-
// check if we have something to hoist in this level
158-
if (hoisted[level] && hoisted[level].length) {
159-
// re-order children so that hoisted ones appear first
160-
// this is actually the main job of this transformer
161-
const hoistedStmts = hoisted[level]
162-
const otherStmts = (resultNode as Block).statements.filter(
163-
(s) => !hoistedStmts.includes(s) && !isJestGlobalImport(s),
101+
102+
return sortStatements(leftPart).concat(pivot, sortStatements(rightPart))
103+
}
104+
105+
const createVisitor = (ctx: _ts.TransformationContext, _: _ts.SourceFile) => {
106+
const visitor: _ts.Visitor = (node) => {
107+
const resultNode = ts.visitEachChild(node, visitor, ctx)
108+
// Since we use `visitEachChild`, we go upwards tree so all children node elements are checked first
109+
if (ts.isBlock(resultNode) && canHoistInBlockScope(resultNode)) {
110+
const newNodeArrayStatements = tsFactory.createNodeArray(
111+
sortStatements(resultNode.statements as unknown as _ts.Statement[]),
164112
)
165-
const newNode = ts.getMutableClone(resultNode) as Block
166-
const newStatements = [...hoistedStmts, ...otherStmts]
167-
if (level === ROOT_LEVEL_AST) {
168-
const jestGlobalsImportStmts = (resultNode as Block).statements.filter((s) => isJestGlobalImport(s))
169-
// jest methods should not be hoisted higher than import `@jest/globals`
170-
resultNode = {
171-
...newNode,
172-
statements: ts.createNodeArray([...jestGlobalsImportStmts, ...newStatements]),
173-
} as Statement
174-
} else {
175-
resultNode = {
176-
...newNode,
177-
statements: ts.createNodeArray(newStatements),
178-
} as Statement
179-
}
180-
}
181113

182-
// exit the level
183-
exit()
114+
return tsFactory.updateBlock(resultNode, newNodeArrayStatements)
115+
} else {
116+
if (ts.isSourceFile(resultNode)) {
117+
resultNode.statements.forEach((stmt) => {
118+
/**
119+
* Gather all possible import names, from different types of import syntax including:
120+
* - named import, e.g. `import { jest } from '@jest/globals'`
121+
* - aliased named import, e.g. `import {jest as aliasedJest} from '@jest/globals'`
122+
* - namespace import, e.g `import * as JestGlobals from '@jest/globals'`
123+
*/
124+
if (
125+
isJestGlobalImport(stmt) &&
126+
stmt.importClause?.namedBindings &&
127+
(ts.isNamespaceImport(stmt.importClause.namedBindings) ||
128+
ts.isNamedImports(stmt.importClause.namedBindings))
129+
) {
130+
const { namedBindings } = stmt.importClause
131+
const jestImportName = ts.isNamespaceImport(namedBindings)
132+
? namedBindings.name.text
133+
: namedBindings.elements.find(
134+
(element) =>
135+
element.name.text === JEST_GLOBAL_NAME || element.propertyName?.text === JEST_GLOBAL_NAME,
136+
)?.name.text
137+
if (jestImportName) {
138+
importNamesOfJestObj.push(jestImportName)
139+
}
140+
}
141+
})
142+
const newNodeArrayStatements = tsFactory.createNodeArray(
143+
sortStatements(resultNode.statements as unknown as _ts.Statement[]),
144+
)
145+
importNamesOfJestObj.length = 0
184146

185-
if (shouldHoistNode(resultNode)) {
186-
// hoist into current level
187-
hoist(resultNode as Statement)
188-
}
147+
return ts.factory
148+
? ts.factory.updateSourceFile(
149+
resultNode,
150+
newNodeArrayStatements,
151+
resultNode.isDeclarationFile,
152+
resultNode.referencedFiles,
153+
resultNode.typeReferenceDirectives,
154+
resultNode.hasNoDefaultLib,
155+
resultNode.libReferenceDirectives,
156+
)
157+
: ts.updateSourceFileNode(
158+
resultNode,
159+
newNodeArrayStatements,
160+
resultNode.isDeclarationFile,
161+
resultNode.referencedFiles,
162+
resultNode.typeReferenceDirectives,
163+
resultNode.hasNoDefaultLib,
164+
resultNode.libReferenceDirectives,
165+
)
166+
}
189167

190-
// finally returns the currently visited node
191-
return resultNode
168+
return resultNode
169+
}
192170
}
193171

194172
return visitor
195173
}
196174

197175
// returns the transformer factory
198-
return (ctx: TransformationContext): Transformer<SourceFile> =>
199-
logger.wrap(
200-
{ [LogContexts.logLevel]: LogLevels.debug, call: null },
201-
'visitSourceFileNode(): hoisting',
202-
(sf: SourceFile) => ts.visitNode(sf, createVisitor(ctx, sf)),
176+
return (ctx: _ts.TransformationContext): _ts.Transformer<_ts.SourceFile> =>
177+
logger.wrap({ [LogContexts.logLevel]: LogLevels.debug, call: null }, 'visitSourceFileNode(): hoist jest', (sf) =>
178+
ts.visitNode(sf, createVisitor(ctx, sf)),
203179
)
204180
}

‎src/transformers/path-mapping.spec.ts

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import path from 'path'
22

3-
import { testing } from 'bs-logger'
43
import ts from 'typescript'
54

65
import { createConfigSet } from '../__helpers__/fakers'
76
import { TsCompiler } from '../compiler/ts-compiler'
87
import { normalizeSlashes } from '../utils/normalize-slashes'
98

10-
import * as pathMapping from './path-mapping'
9+
import { factory as pathMapping } from './path-mapping'
1110

12-
const logger = testing.createLoggerMock()
1311
const TS_JS_CODE_WITH_PATH_ALIAS = `
1412
import { parse } from '@utils/json'
1513
import hoo from '@utils/json'
@@ -51,9 +49,8 @@ describe('path-mapping', () => {
5149
tsJestConfig: {
5250
tsconfig,
5351
},
54-
logger,
5552
})
56-
const createFactory = () => pathMapping.factory(new TsCompiler(configSet, new Map()))
53+
const createFactory = () => pathMapping(new TsCompiler(configSet, new Map()))
5754
const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } })
5855
jest.spyOn(ts, 'resolveModuleName').mockReturnValue({
5956
resolvedModule: {
@@ -106,9 +103,8 @@ describe('path-mapping', () => {
106103
},
107104
},
108105
},
109-
logger,
110106
})
111-
const createFactory = () => pathMapping.factory(new TsCompiler(configSet, new Map()))
107+
const createFactory = () => pathMapping(new TsCompiler(configSet, new Map()))
112108
const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } })
113109
const resolvedFileNameStub = path.join('..', `utils/json.${extension}`)
114110
jest.spyOn(ts, 'resolveModuleName').mockReturnValue({

‎src/transformers/path-mapping.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const isBaseDir = (base: string, dir: string) => !relative(base, dir)?.startsWit
2727
export function factory({
2828
configSet,
2929
}: TsCompilerInstance): (ctx: _ts.TransformationContext) => _ts.Transformer<_ts.SourceFile> {
30-
const logger = configSet.logger.child({ namespace: 'ts-path-mapping' })
30+
const logger = configSet.logger.child({ namespace: name })
3131
const ts = configSet.compilerModule
3232
const tsFactory = ts.factory ? ts.factory : ts
3333
const compilerOptions = configSet.parsedTsConfig.options

‎tsconfig.spec.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
"target": "ES2015",
66
},
77
"includes": ["src/**/*.spec.ts"],
8-
"exclude": ["e2e"]
8+
"exclude": ["e2e", "examples"]
99
}

0 commit comments

Comments
 (0)
Please sign in to comment.