Skip to content

Commit 691720f

Browse files
committed
chore: refactor to cleanup and use only "new" jest api
1 parent 09500c2 commit 691720f

31 files changed

Lines changed: 729 additions & 543 deletions

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"clean-build": "npm run clean && npm run build",
1313
"pretest": "npm run tslint && npm run clean-build",
1414
"test": "node scripts/tests.js",
15+
"test:unit": "jest --testRegex '' --testMatch '<rootDir>/src/**/*.spec.ts'",
1516
"tslint": "tslint src/**/*.ts",
1617
"doc": "doctoc .",
1718
"prepublish": "npm run clean-build",
@@ -62,6 +63,7 @@
6263
]
6364
},
6465
"dependencies": {
66+
"closest-file-data": "^0.1.4",
6567
"cpx": "^1.5.0",
6668
"fs-extra": "6.0.1",
6769
"jest-config": "^23.4.1",
@@ -79,6 +81,7 @@
7981
"@babel/preset-env": "^7.0.0-beta.54",
8082
"@types/babel-core": "^6.25.5",
8183
"@types/es6-shim": "0.31.37",
84+
"@types/flat": "^0.0.28",
8285
"@types/fs-extra": "5.0.4",
8386
"@types/jest": "^23.3.0",
8487
"@types/lodash": "^4.14.109",
@@ -90,6 +93,7 @@
9093
"cross-spawn": "latest",
9194
"cross-spawn-with-kill": "latest",
9295
"doctoc": "latest",
96+
"flat": "^4.1.0",
9397
"husky": "^0.14.3",
9498
"jest": "^23.4.1",
9599
"lint-staged": "^7.1.2",

scripts/tests.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ function createIntegrationMock() {
4141

4242
createIntegrationMock();
4343

44+
// HACK: allow us to change the `startDir()` during tests
45+
process.env.__RUNNING_TS_JEST_TESTS = Date.now();
46+
4447
const argv = process.argv.slice(2);
4548
argv.push('--no-cache');
4649
argv.push('--testPathPattern', '^(?!(.*watch.spec.ts$)).*');

src/index.ts

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,17 @@
1-
// structure of this file heavilly inspired on:
2-
// https://github.com/facebook/jest/blob/master/packages/babel-jest/src/index.js
3-
4-
import jestPreset from 'babel-preset-jest';
5-
import getCacheKeyForArgs from './utils/get-cache-key';
6-
import { TsJestContext, JestCacheKeyArguments } from './types';
1+
import getCacheKey from './utils/get-cache-key';
72
import preprocess from './preprocess';
83

4+
//FIXME: options is always empty
95
const createTransformer = (options?: any): jest.Transformer => {
10-
const cache = Object.create(null);
11-
12-
options = Object.assign({}, options, {
13-
compact: false,
14-
plugins: (options && options.plugins) || [],
15-
presets: ((options && options.presets) || []).concat([jestPreset]),
16-
sourceMaps: 'both',
17-
});
18-
delete options.cacheDirectory;
19-
delete options.filename;
20-
21-
const context: TsJestContext = { cache, options };
22-
23-
const getCacheKey = (...args: any[]) =>
24-
getCacheKeyForArgs(args as JestCacheKeyArguments, context);
6+
// const cache = Object.create(null);
7+
// options = Object.assign({}, options, {
8+
// compact: false,
9+
// plugins: (options && options.plugins) || [],
10+
// presets: ((options && options.presets) || []).concat([jestPreset]),
11+
// sourceMaps: 'both',
12+
// });
13+
// delete options.cacheDirectory;
14+
// delete options.filename;
2515

2616
return {
2717
canInstrument: true,

src/postprocess.ts

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,28 @@ let babel: typeof __types__babel;
99
let istanbulPlugin: typeof __types__istanbulPlugin;
1010
let jestPreset: typeof __types__jestPreset;
1111
function importBabelDeps() {
12-
if (babel) {
13-
return;
14-
}
15-
babel = require('@babel/core');
16-
istanbulPlugin = require('babel-plugin-istanbul').default;
17-
jestPreset = require('babel-preset-jest');
12+
if (babel) return; // tslint:ignore-line
13+
// ensure we use the require from jest
14+
babel = require.main.require('@babel/core');
15+
istanbulPlugin = require.main.require('babel-plugin-istanbul').default;
16+
jestPreset = require.main.require('babel-preset-jest');
1817
}
19-
import { CompilerOptions } from 'typescript';
2018
import {
2119
BabelTransformOptions,
2220
PostProcessHook,
2321
JestCacheKeyOptions,
24-
TsJestConfig,
2522
} from './types';
2623
import { logOnce } from './utils/logger';
24+
import getTSJestConfig from './utils/get-ts-jest-config';
2725

2826
// Function that takes the transpiled typescript and runs it through babel/whatever.
2927
export function postProcessCode(
30-
compilerOptions: CompilerOptions,
3128
jestConfig: jest.ProjectConfig,
32-
tsJestConfig: TsJestConfig,
3329
transformOptions: jest.TransformOptions,
3430
transpileOutput: jest.TransformedSource,
3531
filePath: string,
3632
): jest.TransformedSource {
37-
const postHook = getPostProcessHook(
38-
compilerOptions,
39-
jestConfig,
40-
tsJestConfig,
41-
);
33+
const postHook = getPostProcessHook(jestConfig);
4234

4335
return postHook(transpileOutput, filePath, jestConfig, transformOptions);
4436
}
@@ -89,29 +81,29 @@ function createBabelTransformer(
8981
}
9082

9183
export const getPostProcessHook = (
92-
tsCompilerOptions: CompilerOptions,
9384
jestConfig: jest.ProjectConfig,
94-
tsJestConfig: TsJestConfig,
9585
): PostProcessHook => {
86+
const tsJestConfig = getTSJestConfig(jestConfig);
9687
if (tsJestConfig.skipBabel) {
9788
logOnce('Not using any postprocess hook.');
9889
// Identity function
9990
return input => input;
10091
}
10192

102-
const plugins = Array.from(
103-
(tsJestConfig.babelConfig && tsJestConfig.babelConfig.plugins) || [],
104-
);
105-
93+
const tsJestBabelConfig = tsJestConfig.babelConfig || {};
10694
const babelOptions: BabelTransformOptions = {
107-
...tsJestConfig.babelConfig,
95+
...tsJestBabelConfig,
10896
babelrc: tsJestConfig.useBabelrc || false,
109-
plugins,
110-
presets: tsJestConfig.babelConfig ? tsJestConfig.babelConfig.presets : [],
111-
sourceMaps: tsJestConfig.disableSourceMapSupport !== true,
97+
plugins: toArray(tsJestBabelConfig.plugins),
98+
presets: toArray(tsJestBabelConfig.presets),
99+
sourceMaps: !tsJestConfig.disableSourceMapSupport,
112100
};
113101

114102
logOnce('Using babel with options:', babelOptions);
115103

116104
return createBabelTransformer(babelOptions);
117105
};
106+
107+
function toArray<T>(iter?: Iterable<T> | null): Array<T> {
108+
return iter ? Array.from(iter) : [];
109+
}

src/preprocess.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import { flushLogs, logOnce } from './utils/logger';
22
import { postProcessCode } from './postprocess';
3-
import { getTSConfig, getTSJestConfig, runTsDiagnostics } from './utils';
43
import { transpileTypescript } from './transpiler';
4+
import runTsDiagnostics from './utils/run-ts-diagnostics';
5+
import getTSConfig from './utils/get-ts-config';
6+
import getTSJestConfig from './utils/get-ts-jest-config';
57

68
export default function preprocess(
79
src: string,
810
filePath: jest.Path,
911
jestConfig: jest.ProjectConfig,
10-
transformOptions: jest.TransformOptions,
12+
transformOptions?: jest.TransformOptions,
1113
): jest.TransformedSource | string {
1214
// transformOptions.instrument is a proxy for collectCoverage
1315
// https://github.com/kulshekhar/ts-jest/issues/201#issuecomment-300572902
14-
const compilerOptions = getTSConfig(jestConfig.globals, jestConfig.rootDir);
16+
const compilerOptions = getTSConfig(jestConfig);
1517

1618
logOnce('final compilerOptions:', compilerOptions);
1719

@@ -31,7 +33,7 @@ export default function preprocess(
3133
return src;
3234
}
3335

34-
const tsJestConfig = getTSJestConfig(jestConfig.globals);
36+
const tsJestConfig = getTSJestConfig(jestConfig);
3537
logOnce('tsJestConfig: ', tsJestConfig);
3638

3739
// We can potentially do this faster by using the language service.
@@ -44,21 +46,19 @@ export default function preprocess(
4446

4547
if (tsJestConfig.ignoreCoverageForAllDecorators === true) {
4648
transpileOutput.code = transpileOutput.code.replace(
47-
/__decorate/g,
49+
/\b__decorate\b/g,
4850
'/* istanbul ignore next */__decorate',
4951
);
5052
}
5153
if (tsJestConfig.ignoreCoverageForDecorators === true) {
5254
transpileOutput.code = transpileOutput.code.replace(
53-
/(__decorate\(\[\r?\n[^\n\r]*)\/\*\s*istanbul\s*ignore\s*decorator(.*)\*\//g,
55+
/(\b__decorate\(\[\r?\n[^\n\r]*)\/\*\s*istanbul\s+ignore\s+decorator(.*)\*\//g,
5456
'/* istanbul ignore next$2*/$1',
5557
);
5658
}
5759

5860
const outputText = postProcessCode(
59-
compilerOptions,
6061
jestConfig,
61-
tsJestConfig,
6262
transformOptions,
6363
transpileOutput,
6464
filePath,

src/types.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,6 @@ export interface TsJestContext {
1010
options: any;
1111
}
1212

13-
export type JestCacheKeyArguments = [
14-
string,
15-
string,
16-
string,
17-
JestCacheKeyOptions
18-
];
19-
2013
export interface HasteConfig {
2114
defaultPlatform?: string | null;
2215
hasteImplModulePath?: string;

src/utils/cache-file.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { join } from 'path';
2+
import { createHash } from 'crypto';
3+
import { outputFileSync } from 'fs-extra';
4+
import { memoize } from 'lodash';
5+
6+
// to use the same compiled regexp object all the time
7+
// See https://jsperf.com/string-match-str-re/1
8+
const memoizedRegexp = memoize(RegExp);
9+
const cachedTest = (
10+
reString: string | undefined | null,
11+
subject: string,
12+
): boolean => {
13+
if (reString == null) {
14+
return false;
15+
}
16+
return memoizedRegexp(reString).test(subject);
17+
};
18+
19+
const cachedMd5 = memoize((path: string) =>
20+
createHash('md5')
21+
.update(path)
22+
.digest('hex'),
23+
);
24+
25+
// FIXME: I think there is testRegexp and testMatch in Jest configugation
26+
// There is a default setting for that, but maybe it's defaulted from scratch
27+
export function cacheFile(
28+
jestConfig: jest.ProjectConfig,
29+
filePath: string,
30+
src: string,
31+
): void {
32+
// store transpiled code contains source map into cache, except test cases
33+
if (!cachedTest(jestConfig.testRegex, filePath)) {
34+
const hash = cachedMd5(filePath);
35+
const outputFilePath = join(jestConfig.cacheDirectory, 'ts-jest', hash);
36+
outputFileSync(outputFilePath, src);
37+
}
38+
}

src/utils/format-diagnostics.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {
2+
sys,
3+
Diagnostic,
4+
FormatDiagnosticsHost,
5+
formatDiagnostics,
6+
} from 'typescript';
7+
8+
export default function formatTsDiagnostics(errors: Diagnostic[]): string {
9+
const defaultFormatHost: FormatDiagnosticsHost = {
10+
getCurrentDirectory: () => sys.getCurrentDirectory(),
11+
getCanonicalFileName: fileName => fileName,
12+
getNewLine: () => sys.newLine,
13+
};
14+
15+
return formatDiagnostics(errors, defaultFormatHost);
16+
}

src/utils/get-babel-rc.ts

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,23 @@
1-
import { readFileSync, existsSync } from 'fs';
2-
import { dirname, join, resolve } from 'path';
1+
import closestFileData from 'closest-file-data';
2+
import { readFileSync } from 'fs';
33
import {
44
BABELRC_FILENAME,
55
BABELRC_JS_FILENAME,
66
PACKAGE_JSON,
77
BABEL_CONFIG_KEY,
88
} from './constants';
9-
import { TsJestContext } from '../types';
9+
import { BabelTransformOptions } from '../types';
1010

11-
// ideally we'd get that from babel-jest if it was exported,
12-
// this is a pure translation from js
13-
export default function getBabelRC(filename, { cache }: TsJestContext) {
14-
const paths = [];
15-
let directory = filename;
16-
// tslint:disable-next-line:no-conditional-assignment
17-
while (directory !== (directory = dirname(directory))) {
18-
if (cache[directory]) {
19-
break;
20-
}
11+
const babelReaders = [
12+
{
13+
basename: BABELRC_FILENAME,
14+
read: f => JSON.parse(readFileSync(f, 'utf8')),
15+
},
16+
{ basename: BABELRC_JS_FILENAME, read: f => require(f) },
17+
{ basename: PACKAGE_JSON, read: f => require(f)[BABEL_CONFIG_KEY] },
18+
];
2119

22-
paths.push(directory);
23-
const configFilePath = join(directory, BABELRC_FILENAME);
24-
if (existsSync(configFilePath)) {
25-
cache[directory] = readFileSync(configFilePath, 'utf8');
26-
break;
27-
}
28-
const configJsFilePath = join(directory, BABELRC_JS_FILENAME);
29-
if (existsSync(configJsFilePath)) {
30-
cache[directory] = JSON.stringify(require(configJsFilePath));
31-
break;
32-
}
33-
const resolvedJsonFilePath = join(directory, PACKAGE_JSON);
34-
const packageJsonFilePath =
35-
resolvedJsonFilePath === PACKAGE_JSON
36-
? resolve(directory, PACKAGE_JSON)
37-
: resolvedJsonFilePath;
38-
if (existsSync(packageJsonFilePath)) {
39-
const packageJsonFileContents = require(packageJsonFilePath);
40-
if (packageJsonFileContents[BABEL_CONFIG_KEY]) {
41-
cache[directory] = JSON.stringify(
42-
packageJsonFileContents[BABEL_CONFIG_KEY],
43-
);
44-
break;
45-
}
46-
}
47-
}
48-
paths.forEach(directoryPath => (cache[directoryPath] = cache[directory]));
49-
return cache[directory] || '';
20+
export default function getBabelRC(filename): babel.BabylonOptions | void {
21+
const res = closestFileData(filename, babelReaders);
22+
return res && res.data;
5023
}

0 commit comments

Comments
 (0)