Skip to content

Commit

Permalink
Clean up the webpack recipe
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Jun 17, 2017
1 parent f228401 commit 212b5de
Showing 1 changed file with 65 additions and 50 deletions.
115 changes: 65 additions & 50 deletions docs/recipes/precompiling-with-webpack.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ The AVA [readme](https://github.com/avajs/ava#transpiling-imported-modules) ment
- [Multiple test files](#multiple-test-files)

### Single test file

This is the simplest use case. You might need this if you are [using aliases](https://github.com/avajs/ava/issues/1011).

###### webpack.config.js
###### `webpack.config.js`

```js
const path = require('path');
Expand All @@ -25,11 +26,15 @@ module.exports = {
},
externals: [nodeExternals()],
module: {
rules: [{
test: /\.(js|jsx)$/,
use: 'babel-loader',
options: { cacheDirectory: true }
}]
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
options: {
cacheDirectory: true
}
}
]
}
};
```
Expand All @@ -39,6 +44,7 @@ The important bits are `target: 'node'`, which ignores Node.js-specific `require
You can now run `$ ava _build/test.js` to run the tests contained in this output.

### Multiple test files

Things are a little more complicated with multiple test files. We recommend [using babel-register](babelrc.md) until the performance penalty becomes too great.

The possible approaches are:
Expand All @@ -49,6 +55,7 @@ The possible approaches are:
- [Test against precompiled sources](#test-against-precompiled-sources)

#### Refer precompiled source in tests

Source files can be compiled to `_src` folder and referenced in tests. While this is less than elegant, it performs well and the workflow can be optimized with [`babel-cli` watch mode](https://babeljs.io/docs/usage/cli/#babel).

```js
Expand All @@ -59,16 +66,16 @@ import fresh from '../_src';
```

#### Single entry file
Multiple test files can be compiled into a single file. This may have the best performance but that does come at a cost. All tests will be in the same file, which can make it harder to know which test has failed, since AVA can't show the file name the test was originally in. You'll also lose [process isolation](https://github.com/avajs/ava#process-isolation).

###### webpack.config.js
Multiple test files can be compiled into a single file. This may have the best performance, but it does come at a cost. All tests will be in the same file, which can make it harder to know which test has failed, since AVA can't show the file name the test was originally in. You'll also lose [process isolation](https://github.com/avajs/ava#process-isolation).

###### `webpack.config.js`

[Related stackoverflow answer](http://stackoverflow.com/questions/32874025/how-to-add-wildcard-mapping-in-entry-of-webpack/34545812#34545812)
[Related Stack Overflow answer](http://stackoverflow.com/questions/32874025/how-to-add-wildcard-mapping-in-entry-of-webpack/34545812#34545812)

```js
const path = require('path');
const glob = require('glob');

const nodeExternals = require('webpack-node-externals');

module.exports = {
Expand All @@ -80,13 +87,17 @@ module.exports = {
},
externals: [nodeExternals()],
module: {
rules: [{
test: /\.(js|jsx)$/,
use: {
loader: 'babel-loader',
options: { cacheDirectory: true }
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
}
}]
]
}
};
```
Expand Down Expand Up @@ -134,25 +145,24 @@ module.exports = {
}
```

</details>

#### Multiple entry files
We can ask webpack to generate multiple entry files. This helps retain file names so that error reports are easy to interpret. But each entry file gets it's own copy of the source files. This results in considerably larger file sizes. This can [perform quite poorly](https://github.com/avajs/ava/pull/1385#issuecomment-304684047) on the first execution.

###### webpack.config.js
We can ask webpack to generate multiple entry files. This helps retain file names so that error reports are easy to interpret. But each entry file gets its own copy of the source files. This results in considerably larger file sizes. This can [perform quite poorly](https://github.com/avajs/ava/pull/1385#issuecomment-304684047) on the first execution.

###### `webpack.config.js`

```js
const path = require('path');
const glob = require('glob');

const nodeExternals = require('webpack-node-externals');

const entryObj = glob.sync('./test/**/*.js')
.reduce((acc, file) => {
acc[path.basename(file, path.extname(file))] = file;
return acc;
}, {}); // empty object as initial value
.reduce((acc, file) => {
acc[path.basename(file, path.extname(file))] = file;
return acc;
}, {});

module.exports = {
target: 'node',
Expand All @@ -163,21 +173,27 @@ module.exports = {
},
externals: [nodeExternals()],
module: {
rules: [{
test: /\.(js|jsx)$/,
use: {
loader: 'babel-loader',
options: { cacheDirectory: true }
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
}
}]
]
}
};
```

#### Test against precompiled sources

This is the most complicated to setup but performs quite well and also retains file names. In this approach, we use the `babel-cli` to compile the source files but preserve file structure. Require paths in tests are rewritten when compiling them in webpack. The following example is for a specific file structure. Depending on how your source and test files are organised, you might need to make changes.

File structure:

```
├───src
│ ├───my-pkg-fldr
Expand All @@ -201,33 +217,32 @@ File structure:
```

npm scripts:

```js
{
"scripts": {
"precompile-src": "cross-env NODE_ENV=test babel src --out-dir _src",
"precompile-tests": "cross-env NODE_ENV=test webpack --config webpack.config.test.js",
"pretest": "npm run precompile-src && npm run precompile-tests",
"test": "cross-env NODE_ENV=test nyc --cache ava _build --concurrency 3"
}
"scripts": {
"precompile-src": "cross-env NODE_ENV=test babel src --out-dir _src",
"precompile-tests": "cross-env NODE_ENV=test webpack --config webpack.config.test.js",
"pretest": "npm run precompile-src && npm run precompile-tests",
"test": "cross-env NODE_ENV=test nyc --cache ava _build --concurrency 3"
}
}
```

###### webpack.config.js
###### `webpack.config.js`

Webpack Externals Docs - https://webpack.js.org/configuration/externals/#function
[Webpack `externals` docs](https://webpack.js.org/configuration/externals/#function)

```js
const path = require('path');
const glob = require('glob');

const nodeExternals = require('webpack-node-externals');

const entryObj = glob.sync('./test/**/*.js')
.reduce((acc, file) => {
acc[path.basename(file, path.extname(file))] = file;
return acc;
}, {}); // empty object as initial value

.reduce((acc, file) => {
acc[path.basename(file, path.extname(file))] = file;
return acc;
}, {});

module.exports = {
target: 'node',
Expand All @@ -238,22 +253,22 @@ module.exports = {
},
externals: [
nodeExternals(),
// Rewrite the require paths to use _src
// Rewrite the require paths to use `_src`
(context, request, callback) => {
// This is a little messy because tests are not output in original file structure
// test/index.test.js -> _build/index.test.js
// => ../src -> ../_src
// test/my-pkg-fldr/my-module.test.js -> _build/my-module.test.js
// => ../../src -> ../_src
// test/index.test.js _build/index.test.js
//=> ../src ../_src
// test/my-pkg-fldr/my-module.test.js _build/my-module.test.js
//=> ../../src ../_src
if (request.includes('/src')) {
const requestReqwrite = request
.replace('/src', '/_src')
.replace('../../_src', '../_src');
return callback(null, `commonjs ${requestReqwrite}`);
}

callback();
}
]
};
```

0 comments on commit 212b5de

Please sign in to comment.