From 1afdc0a38904fb18f77984b8b2d66f8a15e291c3 Mon Sep 17 00:00:00 2001 From: Brian Mann Date: Mon, 18 May 2020 08:24:17 -0400 Subject: [PATCH] feat: force sourceMap: true on all typescript projects (cypress-io/cypress-webpack-preprocessor#92) --- .mocharc.json | 1 + examples/use-ts-loader/tsconfig.json | 1 + import-sorter.json | 7 ++ index.ts | 20 +++- lib/typescript-overrides.ts | 54 +++++++++++ package.json | 10 +- test/unit/dist.spec.ts | 9 ++ test/unit/index.spec.js | 9 +- test/unit/typescript-overrides.spec.ts | 121 +++++++++++++++++++++++++ yarn.lock | 70 +++++++++++++- 10 files changed, 292 insertions(+), 10 deletions(-) create mode 100644 import-sorter.json create mode 100644 lib/typescript-overrides.ts create mode 100644 test/unit/dist.spec.ts create mode 100644 test/unit/typescript-overrides.spec.ts diff --git a/.mocharc.json b/.mocharc.json index 37f98b360f3e..a119a8d739e9 100644 --- a/.mocharc.json +++ b/.mocharc.json @@ -3,5 +3,6 @@ "./test/_test-output", "node_modules" ], + "require": "ts-node/register", "exit": true } diff --git a/examples/use-ts-loader/tsconfig.json b/examples/use-ts-loader/tsconfig.json index 4977cfea3131..e1261daae9d6 100644 --- a/examples/use-ts-loader/tsconfig.json +++ b/examples/use-ts-loader/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "sourceMap": true, "outDir": "./dist/", "noImplicitAny": true, "module": "commonJS", diff --git a/import-sorter.json b/import-sorter.json new file mode 100644 index 000000000000..9d2aa2e1a2f3 --- /dev/null +++ b/import-sorter.json @@ -0,0 +1,7 @@ +{ + "generalConfiguration": { + "exclude": [ + "index.ts" + ] + } +} diff --git a/index.ts b/index.ts index 5d0844710a01..e0ea2fc1e74c 100644 --- a/index.ts +++ b/index.ts @@ -1,3 +1,5 @@ +import { overrideSourceMaps } from './lib/typescript-overrides' + import * as Promise from 'bluebird' import * as events from 'events' import * as _ from 'lodash' @@ -95,7 +97,7 @@ interface WebpackPreprocessor extends WebpackPreprocessorFn { */ // @ts-ignore const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): FilePreprocessor => { - debug('user options:', options) + debug('user options: %o', options) // we return function that accepts the arguments provided by // the event 'file:preprocessor' @@ -151,11 +153,21 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F }, }) .tap((opts) => { - if (opts.devtool !== false) { - debug('setting devtool to inline-source-map') + if (opts.devtool === false) { + // disable any overrides if + // we've explictly turned off sourcemaps + overrideSourceMaps(false) - opts.devtool = 'inline-source-map' + return } + + debug('setting devtool to inline-source-map') + + opts.devtool = 'inline-source-map' + + // override typescript to always generate + // proper source maps + overrideSourceMaps(true) }) .value() as any diff --git a/lib/typescript-overrides.ts b/lib/typescript-overrides.ts new file mode 100644 index 000000000000..eea61dc78fd3 --- /dev/null +++ b/lib/typescript-overrides.ts @@ -0,0 +1,54 @@ +const debug = require('debug')('cypress:webpack') + +let sourceMapOverride: null | boolean = null + +export const tryRequireTypescript = () => { + try { + // reset each time this is called + sourceMapOverride = null + + const typescript = require('typescript') + + debug('typescript found, overriding typescript.createProgram()') + + const { createProgram } = typescript + + typescript.createProgram = (...args: [any]) => { + const [programOptions] = args + const { options } = programOptions + + debug('typescript unmodified createProgram options %o', options) + + // if sourceMap has been set then apply + // these overrides to force typescript + // to generate the right sourcemaps + if (sourceMapOverride !== null) { + options.sourceMap = sourceMapOverride + + delete options.inlineSources + delete options.inlineSourceMap + + debug('typescript modified createProgram options %o', options) + } + + return createProgram.apply(typescript, args) + } + + return typescript + } catch (err) { + debug('typescript not found') + + // for testing + return err + } +} + +export const overrideSourceMaps = (val: boolean) => { + sourceMapOverride = val +} + +export const getSourceMapOverride = () => { + return sourceMapOverride +} + +tryRequireTypescript() diff --git a/package.json b/package.json index 3ecff7e0140a..4240783bbb20 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "main": "dist", "scripts": { "ban": "ban", - "build": "tsc", + "build": "rm -rf dist && tsc", "deps": "deps-ok && dependency-check --no-dev .", "license": "license-checker --production --onlyunknown --csv", "lint": "eslint --ext .js,.jsx,.json,.ts,.tsx .", @@ -17,9 +17,9 @@ "pretest": "yarn lint && yarn build", "test": "yarn test-unit && yarn test-e2e", "test-debug": "node --inspect --debug-brk ./node_modules/.bin/_mocha", - "test-e2e": "mocha test/e2e/*.spec.js", - "test-unit": "mocha test/unit/*.spec.js", - "test-watch": "yarn test-unit & chokidar '*.js' 'test/unit/*.js' -c 'yarn test-unit'", + "test-e2e": "mocha test/e2e/*.spec.*", + "test-unit": "mocha test/unit/*.spec.*", + "test-watch": "yarn test-unit & chokidar '**/*.(js|ts)' 'test/unit/*.(js|ts)' -c 'yarn test-unit'", "types": "tsc --noEmit" }, "husky": { @@ -61,6 +61,7 @@ "mocha": "^7.1.0", "mockery": "2.1.0", "prettier-eslint-cli": "4.4.0", + "proxyquire": "2.1.3", "react": "16.13.1", "react-dom": "16.13.1", "react-scripts": "3.2", @@ -69,6 +70,7 @@ "sinon-chai": "^3.5.0", "snap-shot-it": "7.9.2", "start-server-and-test": "1.10.11", + "ts-node": "8.10.1", "typescript": "3.8.3", "webpack": "^4.18.1" }, diff --git a/test/unit/dist.spec.ts b/test/unit/dist.spec.ts new file mode 100644 index 000000000000..c2271c5f3403 --- /dev/null +++ b/test/unit/dist.spec.ts @@ -0,0 +1,9 @@ +const { expect } = require('chai') +const preprocessor = require('../../dist/index') + +describe('tyepscript ./dist output', () => { + it('builds dist correctly', () => { + expect(preprocessor).to.be.a('function') + expect(preprocessor).to.have.property('defaultOptions') + }) +}) diff --git a/test/unit/index.spec.js b/test/unit/index.spec.js index 3b0bcd05f0f0..f6c7d61c0aae 100644 --- a/test/unit/index.spec.js +++ b/test/unit/index.spec.js @@ -16,7 +16,8 @@ mockery.enable({ mockery.registerMock('webpack', webpack) -const preprocessor = require('../../dist/index') +const preprocessor = require('../../index') +const { getSourceMapOverride } = require('../../lib/typescript-overrides') describe('webpack preprocessor', function () { beforeEach(function () { @@ -155,6 +156,8 @@ describe('webpack preprocessor', function () { expect(webpack).to.be.calledWithMatch({ devtool: 'inline-source-map', }) + + expect(getSourceMapOverride()).to.be.true }) }) @@ -165,6 +168,8 @@ describe('webpack preprocessor', function () { expect(webpack).to.be.calledWithMatch({ devtool: false, }) + + expect(getSourceMapOverride()).to.be.false }) }) @@ -175,6 +180,8 @@ describe('webpack preprocessor', function () { expect(webpack).to.be.calledWithMatch({ devtool: 'inline-source-map', }) + + expect(getSourceMapOverride()).to.be.true }) }) }) diff --git a/test/unit/typescript-overrides.spec.ts b/test/unit/typescript-overrides.spec.ts new file mode 100644 index 000000000000..cbc8c2c970a5 --- /dev/null +++ b/test/unit/typescript-overrides.spec.ts @@ -0,0 +1,121 @@ +const { expect } = require('chai') +const sinon = require('sinon') +const proxyquire = require('proxyquire').noPreserveCache() + +type Typescript = { + createProgram: sinon.SinonStub +} + +let typescript: Typescript +let createProgram: Typescript['createProgram'] + +import '../../lib/typescript-overrides' + +describe('./lib/typescript-overrides', () => { + beforeEach(() => { + createProgram = sinon.stub() + typescript = { + createProgram, + } + }) + + context('.getSourceMapOverride', () => { + it('is null by default', () => { + const typescriptOverrides = proxyquire('../../lib/typescript-overrides', { + typescript, + }) + + expect(typescriptOverrides.getSourceMapOverride()).to.be.null + }) + }) + + context('.tryRequireTypescript', () => { + it('gracefully returns error when typescript cannot be required', () => { + const typescriptOverrides = proxyquire('../../lib/typescript-overrides', { + typescript: null, + }) + + const err = typescriptOverrides.tryRequireTypescript() + + expect(err).to.be.instanceOf(Error) + expect(err.message).to.eq(`Cannot find module 'typescript'`) + }) + }) + + context('.overrideSourceMaps', () => { + it('it sets sourceMap: true', () => { + const typescriptOverrides = proxyquire('../../lib/typescript-overrides', { + typescript, + }) + + typescriptOverrides.overrideSourceMaps(true) + + expect(typescriptOverrides.getSourceMapOverride()).to.be.true + + typescript.createProgram({ + options: { + sourceMap: false, + inlineSources: true, + inlineSourceMap: true, + }, + }) + + expect(createProgram).to.be.calledOn(typescript) + expect(createProgram).to.be.calledWith({ + options: { + sourceMap: true, + }, + }) + }) + + it('it sets sourceMap: false', () => { + const typescriptOverrides = proxyquire('../../lib/typescript-overrides', { + typescript, + }) + + typescriptOverrides.overrideSourceMaps(false) + + expect(typescriptOverrides.getSourceMapOverride()).to.be.false + + typescript.createProgram({ + options: { + sourceMap: true, + inlineSources: true, + inlineSourceMap: true, + }, + }) + + expect(createProgram).to.be.calledOn(typescript) + expect(createProgram).to.be.calledWith({ + options: { + sourceMap: false, + }, + }) + }) + + it('does not override sourcemaps', () => { + const typescriptOverrides = proxyquire('../../lib/typescript-overrides', { + typescript, + }) + + expect(typescriptOverrides.getSourceMapOverride()).to.be.null + + typescript.createProgram({ + options: { + sourceMap: true, + inlineSources: true, + inlineSourceMap: true, + }, + }) + + expect(createProgram).to.be.calledOn(typescript) + expect(createProgram).to.be.calledWith({ + options: { + sourceMap: true, + inlineSources: true, + inlineSourceMap: true, + }, + }) + }) + }) +}) diff --git a/yarn.lock b/yarn.lock index f3ec33651ee3..1ae8baa7fca2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3193,6 +3193,11 @@ arg@4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.0.tgz#583c518199419e0037abb74062c37f8519e575f0" integrity sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg== +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -6917,6 +6922,14 @@ filesize@3.6.1: resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== +fill-keys@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" + integrity sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA= + dependencies: + is-object "~1.0.1" + merge-descriptors "~1.0.0" + fill-range@^2.1.0: version "2.2.4" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" @@ -8616,6 +8629,11 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +is-object@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" + integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= + is-observable@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e" @@ -10191,6 +10209,11 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + make-fetch-happen@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz#aa8387104f2687edca01c8687ee45013d02d19bd" @@ -10368,7 +10391,7 @@ merge-deep@^3.0.2: clone-deep "^0.2.4" kind-of "^3.0.2" -merge-descriptors@1.0.1: +merge-descriptors@1.0.1, merge-descriptors@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= @@ -10678,6 +10701,11 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== +module-not-found-error@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/module-not-found-error/-/module-not-found-error-1.0.1.tgz#cf8b4ff4f29640674d6cdd02b0e3bc523c2bbdc0" + integrity sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA= + moment@2.17.0: version "2.17.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.0.tgz#a4c292e02aac5ddefb29a6eed24f51938dd3b74f" @@ -12972,6 +13000,15 @@ proxy-addr@~2.0.5: forwarded "~0.1.2" ipaddr.js "1.9.1" +proxyquire@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/proxyquire/-/proxyquire-2.1.3.tgz#2049a7eefa10a9a953346a18e54aab2b4268df39" + integrity sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg== + dependencies: + fill-keys "^1.0.2" + module-not-found-error "^1.0.1" + resolve "^1.11.1" + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -13909,6 +13946,13 @@ resolve@^1.1.6, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13 dependencies: path-parse "^1.0.6" +resolve@^1.11.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" @@ -14691,6 +14735,14 @@ source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" +source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@^0.5.6, source-map-support@~0.5.12: version "0.5.16" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" @@ -15567,6 +15619,17 @@ trim-off-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= +ts-node@8.10.1: + version "8.10.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.1.tgz#77da0366ff8afbe733596361d2df9a60fc9c9bd3" + integrity sha512-bdNz1L4ekHiJul6SHtZWs1ujEKERJnHs4HxN7rjTyyVOFf3HaJ6sLqe6aPG62XTzAB/63pKRh5jTSWL0D7bsvw== + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + ts-pnp@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.4.tgz#ae27126960ebaefb874c6d7fa4729729ab200d90" @@ -16861,3 +16924,8 @@ yauzl@2.10.0, yauzl@^2.10.0: dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==