Skip to content
This repository was archived by the owner on Oct 1, 2020. It is now read-only.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Object of webpack options. Just `require` in the options from your `webpack.conf

```javascript
{
mode: 'development',
module: {
rules: [
{
Expand All @@ -92,7 +93,9 @@ Object of webpack options. Just `require` in the options from your `webpack.conf
}
```

Source maps are always enabled unless explicitly disabled by specifying `devtool: false`.
Source maps are **always enabled** unless explicitly disabled by specifying `devtool: false`.

Webpack [mode](https://webpack.js.org/configuration/mode/) is set to `development` if not present. You can set `mode` to "development", "production" or "none".

### use babelrc

Expand Down Expand Up @@ -172,6 +175,18 @@ DEBUG=cypress:webpack:stats

Use the [version of Node that matches Cypress](https://github.com/cypress-io/cypress/blob/develop/.node-version).

Build the typescript files:

```shell
yarn build
```

Watch the typescript files and rebuild on file change:

```shell
yarn build --watch
```

Run all tests once:

```shell
Expand Down
7 changes: 2 additions & 5 deletions cypress/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

const webpack = require('../../')

// const webpackOptions = require('@packages/runner/webpack.config.ts').default
const webpackPreprocessor = require('../../')

/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on) => {
// on('file:preprocessor', webpack({ webpackOptions }))
on('file:preprocessor', webpack({ }))
on('file:preprocessor', webpackPreprocessor())
}
49 changes: 29 additions & 20 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import * as webpack from 'webpack'
import * as Promise from 'bluebird'
import * as events from 'events'

import * as _ from 'lodash'
import * as webpack from 'webpack'
import { createDeferred } from './deferred'

const path = require('path')
const debug = require('debug')('cypress:webpack')
const debugStats = require('debug')('cypress:webpack:stats')

const stubbableRequire = require('./stubbable-require')

type FilePath = string

// bundle promises from input spec filename to output bundled file paths
Expand All @@ -21,16 +19,17 @@ const getDefaultWebpackOptions = (): webpack.Configuration => {
debug('load default options')

return {
mode: 'development',
module: {
rules: [
{
test: /\.jsx?$/,
exclude: [/node_modules/],
use: [
{
loader: stubbableRequire.resolve('babel-loader'),
loader: 'babel-loader',
options: {
presets: [stubbableRequire.resolve('@babel/preset-env')],
presets: ['@babel/preset-env'],
},
},
],
Expand Down Expand Up @@ -123,34 +122,45 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
return bundles[filePath]
}

// user can override the default options
let webpackOptions: webpack.Configuration = options.webpackOptions || getDefaultWebpackOptions()
const watchOptions = options.watchOptions || {}
const defaultWebpackOptions = getDefaultWebpackOptions()

debug('webpackOptions: %o', webpackOptions)
debug('watchOptions: %o', watchOptions)

const entry = [filePath].concat(options.additionalEntries || [])
// we're provided a default output path that lives alongside Cypress's
// app data files so we don't have to worry about where to put the bundled
// file on disk
const outputPath = path.extname(file.outputPath) === '.js'
? file.outputPath
: `${file.outputPath}.js`

// we need to set entry and output
webpackOptions = Object.assign(webpackOptions, {
const entry = [filePath].concat(options.additionalEntries || [])

const watchOptions = options.watchOptions || {}

// user can override the default options
const webpackOptions: webpack.Configuration = _
.chain(options.webpackOptions)
.defaultTo(defaultWebpackOptions)
.defaults({
mode: defaultWebpackOptions.mode,
})
.assign({
// we need to set entry and output
entry,
output: {
path: path.dirname(outputPath),
filename: path.basename(outputPath),
},
})
.tap((opts) => {
if (opts.devtool !== false) {
debug('setting devtool to inline-source-map')

if (webpackOptions.devtool !== false) {
webpackOptions.devtool = 'inline-source-map'
}
opts.devtool = 'inline-source-map'
}
})
.value() as any

debug('webpackOptions: %o', webpackOptions)
debug('watchOptions: %o', watchOptions)
debug(`input: ${filePath}`)
debug(`output: ${outputPath}`)

Expand Down Expand Up @@ -277,8 +287,7 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
}
}

// provide a clone of the default options, lazy-loading them
// so they aren't required unless the user utilizes them
// provide a clone of the default options
Object.defineProperty(preprocessor, 'defaultOptions', {
get () {
debug('get default options')
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
"secure": "nsp check",
"semantic-release": "semantic-release",
"size": "npm pack --dry",
"pretest": "npm run lint && npm run build",
"test": "npm run test-unit && npm run test-e2e",
"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": "chokidar '*.js' 'test/unit/*.js' -c 'npm run test-unit'",
"test-watch": "yarn test-unit & chokidar '*.js' 'test/unit/*.js' -c 'yarn test-unit'",
"types": "tsc --noEmit"
},
"husky": {
Expand All @@ -29,7 +29,8 @@
},
"dependencies": {
"bluebird": "3.7.1",
"debug": "4.1.1"
"debug": "4.1.1",
"lodash": "4.17.15"
},
"devDependencies": {
"@babel/core": "^7.0.1",
Expand Down
5 changes: 0 additions & 5 deletions stubbable-require.ts

This file was deleted.

7 changes: 6 additions & 1 deletion test/e2e/compilation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ describe('webpack preprocessor - e2e', () => {
})

it('correctly preprocesses the file', () => {
return preprocessor()(file).then(() => {
const options = preprocessor.defaultOptions

// our snapshot is minified
options.webpackOptions.mode = 'production'

return preprocessor(options)(file).then(() => {
snapshot(fs.readFileSync(outputPath).toString())
})
})
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const glob = require('fast-glob')

describe('can test', async () => {
// runs every test in cypress/tests/e2e as its own test
// the comment above the test will determince the assertion on the results
// the comment above the test will determine the assertion on the results
glob.sync(path.join(__dirname, '../../cypress/tests/e2e/**/*'))
.map((v) => {
const filename = path.relative(process.cwd(), v)
Expand Down
100 changes: 62 additions & 38 deletions test/unit/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ mockery.enable({
mockery.registerMock('webpack', webpack)

const preprocessor = require('../../dist/index')
const stubbableRequire = require('../../dist/stubbable-require')

describe('webpack preprocessor', function () {
beforeEach(function () {
Expand Down Expand Up @@ -107,27 +106,33 @@ describe('webpack preprocessor', function () {

it('specifies the entry file', function () {
return this.run().then(() => {
expect(webpack.lastCall.args[0].entry).to.eql([this.file.filePath])
expect(webpack).to.be.calledWithMatch({
entry: [this.file.filePath],
})
})
})

it('includes additional entry files', function () {
return this.run({
additionalEntries: ['entry-1.js', 'entry-2.js'],
}).then(() => {
expect(webpack.lastCall.args[0].entry).to.eql([
this.file.filePath,
'entry-1.js',
'entry-2.js',
])
expect(webpack).to.be.calledWithMatch({
entry: [
this.file.filePath,
'entry-1.js',
'entry-2.js',
],
})
})
})

it('specifies output path and filename', function () {
return this.run().then(() => {
expect(webpack.lastCall.args[0].output).to.eql({
path: 'output',
filename: 'output.js',
expect(webpack).to.be.calledWithMatch({
output: {
path: 'output',
filename: 'output.js',
},
})
})
})
Expand All @@ -143,17 +148,53 @@ describe('webpack preprocessor', function () {
})
})

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

it('does not enable inline source maps when devtool is false', function () {
const options = { webpackOptions: { devtool: false } }

return this.run(options).then(() => {
expect(webpack).to.be.calledWithMatch({
devtool: false,
})
})
})

it('always sets devtool even when mode is "production"', function () {
const options = { webpackOptions: { mode: 'production' } }

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

it('does not enable inline source maps when devtool is false', function () {
const options = { webpackOptions: { devtool: false } }
describe('mode', function () {
it('sets mode to development by default', function () {
return this.run().then(() => {
expect(webpack).to.be.calledWithMatch({
mode: 'development',
})
})
})

return this.run(options).then(() => {
expect(webpack.lastCall.args[0].devtool).to.be.false
it('follows user mode if present', function () {
const options = { webpackOptions: { mode: 'production' } }

return this.run(options).then(() => {
expect(webpack).to.be.calledWithMatch({
mode: 'production',
})
})
})
})

Expand All @@ -178,7 +219,7 @@ describe('webpack preprocessor', function () {
const options = { watchOptions: { poll: true } }

return this.run(options).then(() => {
expect(this.compilerApi.watch.lastCall.args[0]).to.eql({
expect(this.compilerApi.watch).to.be.calledWith({
poll: true,
})
})
Expand Down Expand Up @@ -252,26 +293,9 @@ describe('webpack preprocessor', function () {
const options = { webpackOptions: { module: { rules: [] } } }

return this.run(options).then(() => {
expect(webpack.lastCall.args[0].module).to.equal(options.webpackOptions.module)
})
})

it('requires babel dependencies when default options are used', function () {
sinon.spy(stubbableRequire, 'resolve')

return this.run().then(() => {
expect(stubbableRequire.resolve).to.be.calledWith('babel-loader')
expect(stubbableRequire.resolve).to.be.calledWith('@babel/preset-env')
})
})

it('does not requires babel dependencies when user options are non-default', function () {
sinon.spy(stubbableRequire, 'resolve')
const options = { webpackOptions: { module: { rules: [] } } }

return this.run(options).then(() => {
expect(stubbableRequire.resolve).not.to.be.calledWith('babel-loader')
expect(stubbableRequire.resolve).not.to.be.calledWith('@babel/preset-env')
expect(webpack).to.be.calledWithMatch({
module: options.webpackOptions.module,
})
})
})
})
Expand Down