Skip to content

Commit

Permalink
fix(source-maps): fix source maps options/calls
Browse files Browse the repository at this point in the history
  • Loading branch information
huafu committed Jul 24, 2018
1 parent 60ab36e commit 76e27c1
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 58 deletions.
93 changes: 60 additions & 33 deletions src/postprocess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,6 @@
import * as __types__babel from 'babel-core';
import __types__istanbulPlugin from 'babel-plugin-istanbul';
import * as __types__jestPreset from 'babel-preset-jest';
let babel: typeof __types__babel;
let istanbulPlugin: typeof __types__istanbulPlugin;
let jestPreset: typeof __types__jestPreset;
function importBabelDeps() {
if (babel) return; // tslint:disable-line
// ensure we use the require from jest
babel = require.main.require('@babel/core');
istanbulPlugin = require.main.require('babel-plugin-istanbul').default;
jestPreset = require.main.require('babel-preset-jest');
}
import {
BabelTransformOptions,
PostProcessHook,
Expand All @@ -23,6 +13,18 @@ import {
import { logOnce } from './utils/logger';
import getTSJestConfig from './utils/get-ts-jest-config';

let babel: typeof __types__babel;
let istanbulPlugin: typeof __types__istanbulPlugin;
let jestPreset: typeof __types__jestPreset;

function importBabelDeps() {
if (babel) return; // tslint:disable-line
// we must use babel until we handle hoisting of jest.mock() internally
babel = require('@babel/core');
istanbulPlugin = require('babel-plugin-istanbul').default;
jestPreset = require('babel-preset-jest');
}

// Function that takes the transpiled typescript and runs it through babel/whatever.
export function postProcessCode(
jestConfig: jest.ProjectConfig,
Expand All @@ -39,37 +41,43 @@ function createBabelTransformer(
options: BabelTransformOptions,
): PostProcessHook {
importBabelDeps();
options = {
...options,
plugins: options.plugins || [],
presets: (options.presets || []).concat([jestPreset]),
};
delete options.cacheDirectory;
delete options.filename;
const presets = options.presets.slice();
const plugins = options.plugins.slice();

// adds babel-preset-jest if not present
if (!hasBabelAddon(presets, jestPreset)) {
presets.push(jestPreset);
}

// we need to know if there is istanbul plugin in the list so that we do not add it
// in the case the user already has it configured
const hasIstanbul = hasBabelAddon(plugins, istanbulPlugin);

// create a new object we'll use as options with the sliced presets and plugins
const optionsBase = { ...options, presets, plugins };

return (
delete optionsBase.cacheDirectory;

const babelTransformer = (
codeSourcemapPair: jest.TransformedSource,
filename: string,
config: jest.ProjectConfig,
transformOptions: JestCacheKeyOptions,
): jest.TransformedSource => {
const theseOptions = Object.assign(
{ filename, inputSourceMap: codeSourcemapPair.map },
options,
);
const inputSourceMap =
typeof codeSourcemapPair.map === 'string'
? JSON.parse(codeSourcemapPair.map)
: codeSourcemapPair.map;
const theseOptions = { ...optionsBase, filename, inputSourceMap };
if (transformOptions && transformOptions.instrument) {
theseOptions.auxiliaryCommentBefore = ' istanbul ignore next ';
// Copied from jest-runtime transform.js
theseOptions.plugins = theseOptions.plugins.concat([
[
istanbulPlugin,
{
// files outside `cwd` will not be instrumented
cwd: config.rootDir,
exclude: [],
},
],
]);
if (!hasIstanbul) {
theseOptions.plugins = [
...theseOptions.plugins,
istanbulPluginConfig(config),
];
}
}

// we typecast here because babel returns a more complete object than the one expected by jest
Expand All @@ -78,6 +86,8 @@ function createBabelTransformer(
theseOptions,
) as jest.TransformedSource;
};

return babelTransformer;
}

export const getPostProcessHook = (
Expand All @@ -96,7 +106,7 @@ export const getPostProcessHook = (
babelrc: tsJestConfig.useBabelrc || false,
plugins: toArray(tsJestBabelConfig.plugins),
presets: toArray(tsJestBabelConfig.presets),
sourceMaps: !tsJestConfig.disableSourceMapSupport,
sourceMaps: tsJestConfig.disableSourceMapSupport ? false : 'both',
};

logOnce('Using babel with options:', babelOptions);
Expand All @@ -107,3 +117,20 @@ export const getPostProcessHook = (
function toArray<T>(iter?: Iterable<T> | null): T[] {
return iter ? Array.from(iter) : [];
}

function istanbulPluginConfig(jestConfig: jest.ProjectConfig) {
return [
istanbulPlugin,
{
// files outside `cwd` will not be instrumented
cwd: jestConfig.rootDir,
exclude: [],
},
];
}

function hasBabelAddon(inputList: any[], ...addonMatches: any[]): boolean {
return inputList.some(item => {
return addonMatches.indexOf(Array.isArray(item) ? item[0] : item) !== -1;
});
}
1 change: 1 addition & 0 deletions src/preprocess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default function preprocess(

const transpileOutput = transpileTypescript(filePath, src, compilerOptions);

// FIXME: this should be done in the typescript source, else it's invalidating source maps
if (tsJestConfig.ignoreCoverageForAllDecorators === true) {
transpileOutput.code = transpileOutput.code.replace(
/\b__decorate\b/g,
Expand Down
7 changes: 2 additions & 5 deletions src/transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,12 @@ export function transpileTypescript(
compilerOptions: ts.CompilerOptions,
): jest.TransformedSource {
logOnce('Compiling via normal transpileModule call');
const transpileOutput = transpileViaTranspileModule(
const { outputText: code, sourceMapText: map } = transpileViaTranspileModule(
filePath,
fileSrc,
compilerOptions,
);
return {
code: transpileOutput.outputText,
map: transpileOutput.sourceMapText,
};
return { code, map };
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/utils/get-ts-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ function getTSConfig_local(jestConfig: jest.ProjectConfig): CompilerOptions {
// ts-jest will map lines numbers properly if inlineSourceMap and
// inlineSources are set to true. The sourceMap configuration
// is used to send the sourcemap back to Jest
delete config.sourceMap;
config.inlineSourceMap = true;
config.inlineSourceMap = false;
config.sourceMap = true;
config.inlineSources = true;

// the coverage report is broken if `.outDir` is set
Expand Down
4 changes: 2 additions & 2 deletions tests/__tests__/__snapshots__/ts-coverage-async.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
exports[`Typescript async coverage Should generate the correct async coverage numbers 1`] = `
"
=============================== Coverage summary ===============================
Statements : 68.75% ( 11/16 )
Statements : 71.43% ( 10/14 )
Branches : 33.33% ( 2/6 )
Functions : 66.67% ( 4/6 )
Lines : 73.33% ( 11/15 )
Lines : 66.67% ( 8/12 )
================================================================================
"
`;
4 changes: 2 additions & 2 deletions tests/__tests__/__snapshots__/ts-coverage.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
exports[`Typescript coverage Should generate the correct coverage numbers. 1`] = `
"
=============================== Coverage summary ===============================
Statements : 68.75% ( 11/16 )
Statements : 71.43% ( 10/14 )
Branches : 33.33% ( 2/6 )
Functions : 66.67% ( 4/6 )
Lines : 73.33% ( 11/15 )
Lines : 66.67% ( 8/12 )
================================================================================
"
`;
6 changes: 4 additions & 2 deletions tests/__tests__/__snapshots__/tsconfig-comments.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ Object {
"declaration": false,
"declarationMap": false,
"emitDeclarationOnly": false,
"inlineSourceMap": true,
"inlineSourceMap": false,
"inlineSources": true,
"jsx": 2,
"module": 1,
"pretty": false,
"sourceMap": true,
}
`;

Expand All @@ -20,10 +21,11 @@ Object {
"declaration": false,
"declarationMap": false,
"emitDeclarationOnly": false,
"inlineSourceMap": true,
"inlineSourceMap": false,
"inlineSources": true,
"jsx": 2,
"module": 1,
"pretty": false,
"sourceMap": true,
}
`;
3 changes: 2 additions & 1 deletion tests/__tests__/__snapshots__/tsconfig-default.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ Object {
"declaration": false,
"declarationMap": false,
"emitDeclarationOnly": false,
"inlineSourceMap": true,
"inlineSourceMap": false,
"inlineSources": true,
"jsx": 2,
"module": 1,
"moduleResolution": 2,
"noEmitOnError": false,
"sourceMap": true,
"target": 2,
}
`;
9 changes: 6 additions & 3 deletions tests/__tests__/__snapshots__/tsconfig-string.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ Object {
"declaration": false,
"declarationMap": false,
"emitDeclarationOnly": false,
"inlineSourceMap": true,
"inlineSourceMap": false,
"inlineSources": true,
"jsx": 2,
"module": 1,
"moduleResolution": 2,
"noEmitOnError": true,
"noImplicitAny": true,
"sourceMap": true,
"target": 1,
}
`;
Expand All @@ -23,12 +24,13 @@ Object {
"declaration": false,
"declarationMap": false,
"emitDeclarationOnly": false,
"inlineSourceMap": true,
"inlineSourceMap": false,
"inlineSources": true,
"jsx": 2,
"module": 1,
"moduleResolution": 2,
"noEmitOnError": true,
"sourceMap": true,
"target": 2,
}
`;
Expand All @@ -39,12 +41,13 @@ Object {
"declaration": false,
"declarationMap": false,
"emitDeclarationOnly": false,
"inlineSourceMap": true,
"inlineSourceMap": false,
"inlineSources": true,
"jsx": 2,
"module": 1,
"moduleResolution": 2,
"noEmitOnError": true,
"sourceMap": true,
"target": 2,
}
`;
4 changes: 0 additions & 4 deletions tests/__tests__/jest-hoist.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ describe('Jest.mock() calls', () => {
const stderr = result.stderr;

expect(stderr).toContain('Hello.ts:22:11');
// TODO FIX COMMENT - sourcemap is accurate now
// The actual error occurs at line 14. However, because the mock calls
// are hoisted, this changes - in this case, to 22
// The column numbers are accurate.
expect(stderr).toContain('Hello.test.ts:14:19');

expect(result.status).toBe(1);
Expand Down
4 changes: 2 additions & 2 deletions tests/__tests__/postprocess.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import jestConfig from '../__helpers__/jest-config';
describe('postprocess', () => {
function runHook(jestConfig = {} as any) {
return getPostProcessHook({ rootDir: '/tmp/project', ...jestConfig })(
{ code: 'input_code', map: 'input_source_map' },
{ code: 'input_code', map: '"input_source_map"' },
'fake_file',
{} as any,
{
Expand All @@ -33,7 +33,7 @@ describe('postprocess', () => {

runHook();
getPostProcessHook(jestConfig.simple())(
{ code: 'input_code', map: 'input_source_map' },
{ code: 'input_code', map: '"input_source_map"' },
'fake_file',
{} as any,
{ instrument: null },
Expand Down
4 changes: 2 additions & 2 deletions tests/hoist-test/__tests__/jest-hoist.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ describe('Local mocks', () => {
expect((SomeClass as any).mockReturnValue).toBeDefined();
});

it('Jest should be able to mock a local class', () => {
it('Jest should be able to mock a local function', () => {
expect((SomeFunction as any).mockReturnValueOnce).toBeDefined();
});

it('Jest should be able to mock a local class', () => {
it('Jest should be able to mock a local const', () => {
expect(
(SomeFunctionDeclaredAsConst as any).mockImplementation,
).toBeDefined();
Expand Down

0 comments on commit 76e27c1

Please sign in to comment.