Skip to content

Commit

Permalink
perf(babel): uses babel-jest cache key as part of ours
Browse files Browse the repository at this point in the history
  • Loading branch information
huafu committed Aug 14, 2018
1 parent 0ed1587 commit f51c4a7
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 159 deletions.
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,13 @@ module.exports = {
### Using `babel-jest`
By default ts-jest does not rely on babel-jest. But you may want to use some babel plugins and stll be able to write TypeScript. This can be achieved using the `babelJest` config key. It can be:

- `true`, in which case it'll try to find a babel configuration file:
- `true`, in which case it'll use defaults from babelrc or any other found config file:
```js
// jest.config.js
module.exports = {
globals: {
'ts-jest': {
babelConfig: true
babelJest: true
}
}
};
Expand All @@ -224,7 +224,7 @@ By default ts-jest does not rely on babel-jest. But you may want to use some bab
module.exports = {
globals: {
'ts-jest': {
babelConfig: 'babelrc.test.js'
babelJest: 'babelrc.test.js'
}
}
};
Expand All @@ -236,7 +236,7 @@ By default ts-jest does not rely on babel-jest. But you may want to use some bab
module.exports = {
globals: {
'ts-jest': {
babelConfig: {
babelJest: {
plugins: [
// ...
]
Expand All @@ -246,6 +246,18 @@ By default ts-jest does not rely on babel-jest. But you may want to use some bab
};
```

- `false`, in which case it'll disable the use of babel-jest (which is the default):
```js
// jest.config.js
module.exports = {
globals: {
'ts-jest': {
babelJest: false
}
}
};
```

### TS compiler & error reporting

<center>=== TBD ===</center>
Expand Down
31 changes: 20 additions & 11 deletions src/ts-jest-transformer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import TsJestTransformer from './ts-jest-transformer';
import TsJestTransformerOriginal from './ts-jest-transformer';
import * as fakers from './__helpers__/fakers';
import * as babelCfg from './utils/babel-config';
import * as closesPkgJson from './utils/closest-package-json';
import * as TsJestProgram from './ts-program';

Expand All @@ -9,28 +8,35 @@ jest.mock('./utils/closest-package-json');
jest.mock('./utils/backports');

const mocks = {
babelConfig: undefined as any,
babelJestCacheKey: undefined as any,
set packageJson(val: any) {
(closesPkgJson as any).__default = val;
},
set tsConfig(val: any) {
(TsJestProgram as any).__tsConfig = val;
},
reset() {
this.babelConfig = undefined;
this.babelJestCacheKey = 'babel-jest-cache-key';
this.packageJson = { name: 'mock' };
this.tsConfig = {};
},
};
beforeAll(() => {
jest
.spyOn(babelCfg, 'loadDefault')
.mockImplementation(() => mocks.babelConfig);
});
afterEach(() => {
mocks.reset();
});

class TsJestTransformer extends TsJestTransformerOriginal {
babelJestFor(jestCfg: jest.ProjectConfig) {
const bj = super.babelJestFor(jestCfg);
if (bj && !(bj.getCacheKey as any).mock) {
jest
.spyOn(bj, 'getCacheKey')
.mockImplementation(() => mocks.babelJestCacheKey);
}
return bj;
}
}

describe('process', () => {
describe('hoisting', () => {
const transformer = new TsJestTransformer();
Expand Down Expand Up @@ -78,7 +84,10 @@ describe('getCacheKey', () => {
const call: typeof TsJestTransformer['prototype']['getCacheKey'] = (
// tslint:disable-next-line:trailing-comma
...args
) => new TsJestTransformer().getCacheKey(...args);
) => {
const tr = new TsJestTransformer();
return tr.getCacheKey(...args);
};
const defaultCall = () => call(fakeSource, fakeFilePath, fakeJestConfig);

it('should be a 28 chars string, different for each case', () => {
Expand All @@ -91,7 +100,7 @@ describe('getCacheKey', () => {
call(fakeSource, fakeFilePath, fakeJestConfig, { rootDir: '/child' }),
];

mocks.babelConfig = '{sourceMaps: true}';
mocks.babelJestCacheKey = 'another-babel-jest-cache-key';
allCacheKeys.push(defaultCall());
mocks.reset();

Expand Down
56 changes: 36 additions & 20 deletions src/ts-jest-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
TsJestConfig,
BabelConfig,
TsJestHooksMap,
BabelJestTransformer,
} from './types';
import TsProgram from './ts-program';
import Memoize from './utils/memoize';
Expand All @@ -11,7 +12,6 @@ import { backportJestConfig } from './utils/backports';
import jestRootDir from './utils/jest-root-dir';
import { sep, resolve } from 'path';
import parseJsonUnsafe from './utils/parse-json-unsafe';
import * as babelCfg from './utils/babel-config';
import closestPatckageJson from './utils/closest-package-json';
import sha1 from './utils/sha1';
import importer from './utils/importer';
Expand Down Expand Up @@ -42,25 +42,28 @@ export default class TsJestTransformer implements jest.Transformer {
}

@Memoize(jestRootDir)
babelJestFor(jestConfig: jest.ProjectConfig): jest.Transformer {
babelJestFor(
jestConfig: jest.ProjectConfig,
): BabelJestTransformer | undefined {
const babelJestConfig = this.babelJestConfigFor(jestConfig);
if (!babelJestConfig) {
return;
}
return importer
.babelJest(ImportReasons.babelJest)
.createTransformer(this.babelConfigFor(jestConfig));
.createTransformer(babelJestConfig) as BabelJestTransformer;
}

@Memoize(jestRootDir)
babelConfigFor(jestConfig: jest.ProjectConfig): BabelConfig | undefined {
babelJestConfigFor(jestConfig: jest.ProjectConfig): BabelConfig | undefined {
const config = this.configFor(jestConfig);
const rootDir = jestRootDir(jestConfig);
if (config.babelJest === false) {
return;
}

let babelConfig!: BabelConfig;
if (config.babelJest === true) {
// lookup babelrc file
babelConfig = babelCfg.extend({}, babelCfg.loadDefault(rootDir));
} else if (typeof config.babelJest === 'string') {
if (typeof config.babelJest === 'string') {
// path to a babelrc file
let filePath = config.babelJest.replace('<rootDir>', `${rootDir}${sep}`);
filePath = resolve(rootDir, filePath);
Expand All @@ -70,8 +73,7 @@ export default class TsJestTransformer implements jest.Transformer {
babelConfig = config.babelJest;
}

// ensure to return a freezed copy object
return babelCfg.freeze(babelCfg.extend({}, babelConfig));
return babelConfig;
}

@Memoize(jestRootDir)
Expand Down Expand Up @@ -99,6 +101,11 @@ export default class TsJestTransformer implements jest.Transformer {
stringifyRegEx = undefined;
}

// babelJest true => {}
if (options.babelJest === true) {
options.babelJest = {};
}

// parsed options
return {
inputOptions: options,
Expand Down Expand Up @@ -126,7 +133,7 @@ export default class TsJestTransformer implements jest.Transformer {
const stringify =
config.stringifyContentPathRegex &&
config.stringifyContentPathRegex.test(filePath);
const useBabelJest = !stringify && config.babelJest;
const babelJest = !stringify && this.babelJestFor(jestConfig);

// get the tranformer instance
const program = this.programFor(jestConfig);
Expand All @@ -142,8 +149,8 @@ export default class TsJestTransformer implements jest.Transformer {
result = program.transpileModule(filePath, source, instrument);

// calling babel-jest transformer
if (useBabelJest) {
result = this.babelJestFor(jestConfig).process(
if (babelJest) {
result = babelJest.process(
result,
filePath,
jestConfig,
Expand All @@ -170,11 +177,10 @@ export default class TsJestTransformer implements jest.Transformer {
fileContent: string,
filePath: string,
jestConfigStr: string,
{
instrument = false,
rootDir,
}: { instrument?: boolean; rootDir?: string } = {},
transformOptions: { instrument?: boolean; rootDir?: string } = {},
): string {
// tslint:disable-next-line:prefer-const
let { instrument = false, rootDir } = transformOptions;
const CHAR0 = '\0';
// will be used as the hashing data source
const hashData: string[] = [];
Expand All @@ -192,14 +198,24 @@ export default class TsJestTransformer implements jest.Transformer {
const sanitizedJestConfig: jest.ProjectConfig = this.sanitizedJestConfigFor(
jestConfig,
);

// add jest config
hashUpdate(JSON.stringify(sanitizedJestConfig));
// add project's package.json
const projectPkg = closestPatckageJson(rootDir, true);
hashUpdate(projectPkg);
// add babel config if using babel jest
const babelConfig = this.babelConfigFor(jestConfig) || {};
hashUpdate(JSON.stringify(babelConfig));
// if using babel jest, adds its cacheKey as well
const babelJest = this.babelJestFor(jestConfig);
if (babelJest) {
hashUpdate(
babelJest.getCacheKey(
fileContent,
filePath,
jestConfigStr,
transformOptions as any,
),
);
}
// add tsconfig
const tsConfig = this.programFor(sanitizedJestConfig).parsedConfig;
hashUpdate(JSON.stringify(tsConfig));
Expand Down
8 changes: 7 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ export type TBabelCore = typeof _babel;
export type TPackageJson = IPackageJSON;
export type TClosestFileData = typeof _closestFileData;
export type TBabelJest = Required<jest.Transformer>;
export type BabelJestTransformer = {
[K in Exclude<keyof jest.Transformer, 'createTransformer'>]: Exclude<
jest.Transformer[K],
undefined
>
};

// CAUTION: use same key-value pair allow us to not store a list of values somewhere
export enum DiagnosticTypes {
Expand Down Expand Up @@ -53,7 +59,7 @@ export interface TsJestGlobalOptions {

export interface TsJestConfig {
inputOptions: TsJestGlobalOptions;
babelJest: BabelConfig | string | boolean;
babelJest: BabelConfig | string | false;
diagnostics: DiagnosticTypes[];

// to deprecate / deprecated === === ===
Expand Down
53 changes: 0 additions & 53 deletions src/utils/babel-config.spec.ts

This file was deleted.

57 changes: 0 additions & 57 deletions src/utils/babel-config.ts

This file was deleted.

Loading

0 comments on commit f51c4a7

Please sign in to comment.