From 76e27c1c37fea160d84785c2fcd117b757ca0547 Mon Sep 17 00:00:00 2001 From: Huafu Gandon Date: Tue, 24 Jul 2018 11:42:34 +0200 Subject: [PATCH] fix(source-maps): fix source maps options/calls --- src/postprocess.ts | 93 ++++++++++++------- src/preprocess.ts | 1 + src/transpiler.ts | 7 +- src/utils/get-ts-config.ts | 4 +- .../ts-coverage-async.spec.ts.snap | 4 +- .../__snapshots__/ts-coverage.spec.ts.snap | 4 +- .../tsconfig-comments.spec.ts.snap | 6 +- .../tsconfig-default.spec.ts.snap | 3 +- .../tsconfig-string.spec.ts.snap | 9 +- tests/__tests__/jest-hoist.spec.ts | 4 - tests/__tests__/postprocess.spec.ts | 4 +- tests/hoist-test/__tests__/jest-hoist.test.ts | 4 +- 12 files changed, 85 insertions(+), 58 deletions(-) diff --git a/src/postprocess.ts b/src/postprocess.ts index cabe70fabd..4ead6c3195 100644 --- a/src/postprocess.ts +++ b/src/postprocess.ts @@ -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, @@ -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, @@ -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 @@ -78,6 +86,8 @@ function createBabelTransformer( theseOptions, ) as jest.TransformedSource; }; + + return babelTransformer; } export const getPostProcessHook = ( @@ -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); @@ -107,3 +117,20 @@ export const getPostProcessHook = ( function toArray(iter?: Iterable | 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; + }); +} diff --git a/src/preprocess.ts b/src/preprocess.ts index f350e0a33b..9e954bd46c 100644 --- a/src/preprocess.ts +++ b/src/preprocess.ts @@ -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, diff --git a/src/transpiler.ts b/src/transpiler.ts index 8c6721d857..9e17459111 100644 --- a/src/transpiler.ts +++ b/src/transpiler.ts @@ -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 }; } /** diff --git a/src/utils/get-ts-config.ts b/src/utils/get-ts-config.ts index 50fedf5005..9a20bb1583 100644 --- a/src/utils/get-ts-config.ts +++ b/src/utils/get-ts-config.ts @@ -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 diff --git a/tests/__tests__/__snapshots__/ts-coverage-async.spec.ts.snap b/tests/__tests__/__snapshots__/ts-coverage-async.spec.ts.snap index 0eb8e0a7cc..8439cd570a 100644 --- a/tests/__tests__/__snapshots__/ts-coverage-async.spec.ts.snap +++ b/tests/__tests__/__snapshots__/ts-coverage-async.spec.ts.snap @@ -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 ) ================================================================================ " `; diff --git a/tests/__tests__/__snapshots__/ts-coverage.spec.ts.snap b/tests/__tests__/__snapshots__/ts-coverage.spec.ts.snap index aa24548bb4..141c4d8d0a 100644 --- a/tests/__tests__/__snapshots__/ts-coverage.spec.ts.snap +++ b/tests/__tests__/__snapshots__/ts-coverage.spec.ts.snap @@ -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 ) ================================================================================ " `; diff --git a/tests/__tests__/__snapshots__/tsconfig-comments.spec.ts.snap b/tests/__tests__/__snapshots__/tsconfig-comments.spec.ts.snap index d9154208c7..e641618a0c 100644 --- a/tests/__tests__/__snapshots__/tsconfig-comments.spec.ts.snap +++ b/tests/__tests__/__snapshots__/tsconfig-comments.spec.ts.snap @@ -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, } `; @@ -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, } `; diff --git a/tests/__tests__/__snapshots__/tsconfig-default.spec.ts.snap b/tests/__tests__/__snapshots__/tsconfig-default.spec.ts.snap index 8adbd8b34c..59dc4907dd 100644 --- a/tests/__tests__/__snapshots__/tsconfig-default.spec.ts.snap +++ b/tests/__tests__/__snapshots__/tsconfig-default.spec.ts.snap @@ -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, } `; diff --git a/tests/__tests__/__snapshots__/tsconfig-string.spec.ts.snap b/tests/__tests__/__snapshots__/tsconfig-string.spec.ts.snap index 49b7d71850..73cffd5d6f 100644 --- a/tests/__tests__/__snapshots__/tsconfig-string.spec.ts.snap +++ b/tests/__tests__/__snapshots__/tsconfig-string.spec.ts.snap @@ -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, } `; @@ -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, } `; @@ -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, } `; diff --git a/tests/__tests__/jest-hoist.spec.ts b/tests/__tests__/jest-hoist.spec.ts index 81fb70d2bb..e117c915fd 100644 --- a/tests/__tests__/jest-hoist.spec.ts +++ b/tests/__tests__/jest-hoist.spec.ts @@ -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); diff --git a/tests/__tests__/postprocess.spec.ts b/tests/__tests__/postprocess.spec.ts index d5d8866cdb..74792d0ac2 100644 --- a/tests/__tests__/postprocess.spec.ts +++ b/tests/__tests__/postprocess.spec.ts @@ -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, { @@ -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 }, diff --git a/tests/hoist-test/__tests__/jest-hoist.test.ts b/tests/hoist-test/__tests__/jest-hoist.test.ts index ae01fd7949..83f92e02c7 100644 --- a/tests/hoist-test/__tests__/jest-hoist.test.ts +++ b/tests/hoist-test/__tests__/jest-hoist.test.ts @@ -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();