Skip to content

Commit

Permalink
feat(webpack-preprocessor): Allow specifying typescript path (#9312)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbreiding authored Nov 30, 2020
1 parent 03cc828 commit 02347ef
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 88 deletions.
1 change: 1 addition & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ commands:
steps:
- attach_workspace:
at: ~/
- check-conditional-ci
# make sure the binary and NPM package files are present
- run: ls -l
- run: ls -l cypress.zip cypress.tgz
Expand Down
13 changes: 7 additions & 6 deletions npm/webpack-preprocessor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const quietErrorMessage = (err: Error) => {
interface PreprocessorOptions {
webpackOptions?: webpack.Configuration
watchOptions?: Object
typescript?: string
additionalEntries?: string[]
}

Expand Down Expand Up @@ -201,9 +202,8 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
})
.tap((opts) => {
if (opts.devtool === false) {
// disable any overrides if
// we've explictly turned off sourcemaps
overrideSourceMaps(false)
// disable any overrides if we've explictly turned off sourcemaps
overrideSourceMaps(false, options.typescript)

return
}
Expand All @@ -212,14 +212,15 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F

opts.devtool = 'inline-source-map'

// override typescript to always generate
// proper source maps
overrideSourceMaps(true)
// override typescript to always generate proper source maps
overrideSourceMaps(true, options.typescript)
})
.value() as any

debug('webpackOptions: %o', webpackOptions)
debug('watchOptions: %o', watchOptions)
if (options.typescript) debug('typescript: %s', options.typescript)

debug(`input: ${filePath}`)
debug(`output: ${outputPath}`)

Expand Down
43 changes: 16 additions & 27 deletions npm/webpack-preprocessor/lib/typescript-overrides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,50 @@ const _ = require('lodash')

import { CompilerOptions, CreateProgramOptions } from 'typescript'

let sourceMapOverride: null | boolean = null
let patched = false

export const getProgramOptions = (rootNamesOrOptions: CreateProgramOptions, options: CompilerOptions): CompilerOptions => {
const getProgramOptions = (rootNamesOrOptions: CreateProgramOptions, options: CompilerOptions): CompilerOptions => {
return _.isArray(rootNamesOrOptions) ? options : rootNamesOrOptions.options
}

export const tryRequireTypescript = () => {
export const overrideSourceMaps = (sourceMap: boolean, typescriptPath?: string) => {
try {
// reset each time this is called
sourceMapOverride = null
if (patched) {
debug('typescript.createProgram() already overridden')

const typescript = require('typescript') as typeof import('typescript')

debug('typescript found, overriding typescript.createProgram()')
return
}

const typescript = require(typescriptPath || 'typescript') as typeof import('typescript')
const { createProgram } = typescript

debug('typescript found, overriding typescript.createProgram()')

typescript.createProgram = (...args: any[]) => {
const [rootNamesOrOptions, _options] = args

const options = getProgramOptions(rootNamesOrOptions, _options)

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
options.sourceMap = sourceMap

delete options.inlineSources
delete options.inlineSourceMap
delete options.inlineSources
delete options.inlineSourceMap

debug('typescript modified createProgram options %o', options)
}
debug('typescript modified createProgram options %o', options)

// @ts-ignore
return createProgram.apply(typescript, args)
}

return typescript
patched = true
} catch (err) {
debug('typescript not found')

// for testing
// for testing purposes
return err
}
}

export const overrideSourceMaps = (val: boolean) => {
sourceMapOverride = val
}

export const getSourceMapOverride = () => {
return sourceMapOverride
}

tryRequireTypescript()
12 changes: 8 additions & 4 deletions npm/webpack-preprocessor/test/unit/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ mockery.enable({
mockery.registerMock('webpack', webpack)

const preprocessor = require('../../index')
const { getSourceMapOverride } = require('../../lib/typescript-overrides')
const typescriptOverrides = require('../../lib/typescript-overrides')

describe('webpack preprocessor', function () {
beforeEach(function () {
Expand Down Expand Up @@ -152,13 +152,17 @@ describe('webpack preprocessor', function () {
})

describe('devtool', function () {
beforeEach((() => {
sinon.stub(typescriptOverrides, 'overrideSourceMaps')
}))

it('enables inline source maps', function () {
return this.run().then(() => {
expect(webpack).to.be.calledWithMatch({
devtool: 'inline-source-map',
})

expect(getSourceMapOverride()).to.be.true
expect(typescriptOverrides.overrideSourceMaps).to.be.calledWith(true)
})
})

Expand All @@ -170,7 +174,7 @@ describe('webpack preprocessor', function () {
devtool: false,
})

expect(getSourceMapOverride()).to.be.false
expect(typescriptOverrides.overrideSourceMaps).to.be.calledWith(false)
})
})

Expand All @@ -182,7 +186,7 @@ describe('webpack preprocessor', function () {
devtool: 'inline-source-map',
})

expect(getSourceMapOverride()).to.be.true
expect(typescriptOverrides.overrideSourceMaps).to.be.calledWith(true)
})
})
})
Expand Down
113 changes: 62 additions & 51 deletions npm/webpack-preprocessor/test/unit/typescript-overrides.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as sinon from 'sinon'
const { expect } = require('chai')
const sinon = require('sinon')
const proxyquire = require('proxyquire').noPreserveCache()

type Typescript = {
Expand All @@ -9,8 +9,6 @@ type Typescript = {
let typescript: Typescript
let createProgram: Typescript['createProgram']

import '../../lib/typescript-overrides'

describe('./lib/typescript-overrides', () => {
beforeEach(() => {
createProgram = sinon.stub()
Expand All @@ -19,29 +17,6 @@ describe('./lib/typescript-overrides', () => {
}
})

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', {
Expand All @@ -50,8 +25,6 @@ describe('./lib/typescript-overrides', () => {

typescriptOverrides.overrideSourceMaps(true)

expect(typescriptOverrides.getSourceMapOverride()).to.be.true

typescript.createProgram({
options: {
sourceMap: false,
Expand All @@ -60,7 +33,6 @@ describe('./lib/typescript-overrides', () => {
},
})

expect(createProgram).to.be.calledOn(typescript)
expect(createProgram).to.be.calledWith({
options: {
sourceMap: true,
Expand All @@ -75,8 +47,6 @@ describe('./lib/typescript-overrides', () => {

typescriptOverrides.overrideSourceMaps(false)

expect(typescriptOverrides.getSourceMapOverride()).to.be.false

typescript.createProgram({
options: {
sourceMap: true,
Expand All @@ -85,47 +55,75 @@ describe('./lib/typescript-overrides', () => {
},
})

expect(createProgram).to.be.calledOn(typescript)
expect(createProgram).to.be.calledWith({
options: {
sourceMap: false,
},
})
})

it('does not override sourcemaps', () => {
it('sets options when given an array', () => {
const typescriptOverrides = proxyquire('../../lib/typescript-overrides', {
typescript,
})

expect(typescriptOverrides.getSourceMapOverride()).to.be.null
typescriptOverrides.overrideSourceMaps(true)

typescript.createProgram({
options: {
sourceMap: true,
inlineSources: true,
inlineSourceMap: true,
},
typescript.createProgram([], {
sourceMap: false,
inlineSources: true,
inlineSourceMap: true,
})

expect(createProgram).to.be.calledOn(typescript)
expect(createProgram).to.be.calledWith({
options: {
sourceMap: true,
inlineSources: true,
inlineSourceMap: true,
},
expect(createProgram).to.be.calledWith([], {
sourceMap: true,
})
})

it('sets options when given an array', () => {
it('require "default" typescript if typescript option not specified', () => {
const typescriptOverrides = proxyquire('../../lib/typescript-overrides', {
typescript,
})

typescriptOverrides.overrideSourceMaps(true)

expect(typescriptOverrides.getSourceMapOverride()).to.be.true
typescript.createProgram([], {
sourceMap: false,
inlineSources: true,
inlineSourceMap: true,
})

expect(createProgram).to.be.calledOn(typescript)
})

it('requires typescript from typescript option if specified', () => {
const userCreateProgram = sinon.stub()
const userTypescript = {
createProgram: userCreateProgram,
}
const typescriptOverrides = proxyquire('../../lib/typescript-overrides', {
typescript,
'/path/to/user/typescript': userTypescript,
})

typescriptOverrides.overrideSourceMaps(true, '/path/to/user/typescript')

userTypescript.createProgram([], {
sourceMap: false,
inlineSources: true,
inlineSourceMap: true,
})

expect(userCreateProgram).to.be.calledOn(userTypescript)
})

it('does not run twice', () => {
const typescriptOverrides = proxyquire('../../lib/typescript-overrides', {
typescript,
'/path/to/user/typescript': null,
})

typescriptOverrides.overrideSourceMaps(true)

typescript.createProgram([], {
sourceMap: false,
Expand All @@ -134,9 +132,22 @@ describe('./lib/typescript-overrides', () => {
})

expect(createProgram).to.be.calledOn(typescript)
expect(createProgram).to.be.calledWith([], {
sourceMap: true,

const result = typescriptOverrides.overrideSourceMaps(true, '/path/to/user/typescript')

// result will be the error if it tries to require typescript again
expect(result).to.be.undefined
})

it('gracefully returns error when typescript cannot be required', () => {
const typescriptOverrides = proxyquire('../../lib/typescript-overrides', {
typescript: null,
})

const err = typescriptOverrides.overrideSourceMaps(true)

expect(err).to.be.instanceOf(Error)
expect(err.message).to.eq(`Cannot find module 'typescript'`)
})
})
})

0 comments on commit 02347ef

Please sign in to comment.