Skip to content

Commit ef3dc36

Browse files
committed
Add an ES module build for browsers
Fixes #494 testdouble's only `import` entry, lib/index.mjs, re-exports CommonJS files and quibble, so tools that serve node_modules to the browser as native ES modules, like @web/test-runner, failed to load it. Wrap the browserify bundle as a self-contained ES module, dist/testdouble.mjs, and route `import` under the `browser` export condition to it. `require` still resolves to lib/ under that condition. The export names come from the built bundle, and it doesn't set a global `td`. Expose dist/testdouble.js and dist/testdouble.mjs as subpath exports, so the documented browser bundle can be imported by path again. Add a Node test that checks the bundle's exports against lib/index.mjs, and an @web/test-runner example that runs in headless Chrome on Node 22+. Split the webpack example into three builds, so the `import` entry checks both the browser build and lib/index.mjs.
1 parent ec31f1b commit ef3dc36

13 files changed

Lines changed: 7651 additions & 27 deletions

‎CHANGELOG.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@
1717
browser bundles. In a browser, `td.replaceEsm()` now throws a clear
1818
"only supported under Node.js" error, and `td.listReplacedModules()` returns
1919
`[]` [#531](https://github.com/testdouble/testdouble.js/issues/531)
20+
* Add an ES module build for browsers, so `import * as td from 'testdouble'`
21+
works in tools that load native ES modules, like @web/test-runner. Tools that
22+
resolve with the `browser` export condition get `dist/testdouble.mjs`. Webpack,
23+
Vite and esbuild apply it when targeting the browser; @web/test-runner needs
24+
`nodeResolve: { exportConditions: ['browser', 'development'] }` in its config.
25+
`testdouble/dist/testdouble.js` can be imported by path again
26+
[#494](https://github.com/testdouble/testdouble.js/issues/494)
27+
* **Behavior change:** any `import` of testdouble resolved with the `browser`
28+
condition now gets the browser build, which can't replace modules. That
29+
includes Jest's jsdom environment with `--experimental-vm-modules`, and Node
30+
run with `--conditions=browser`. `require('testdouble')` is unaffected
2031

2132
## 3.23.0
2233

‎docs/1-installation.md‎

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,32 @@ $ npm install --save-dev testdouble
4141
And then find the browser distribution of the library in
4242
`node_modules/testdouble/dist/testdouble.js`.
4343

44-
testdouble.js can also be bundled for the browser with webpack 5 and other
45-
bundlers. Module replacement with `td.replace(path)` and `td.replaceEsm()` is
46-
only supported under Node.js.
44+
#### As an ES module
45+
46+
You can also `import * as td from 'testdouble'` in browser tests. Tools that
47+
resolve packages with the `browser` export condition get a self-contained ES
48+
module build of the library, `testdouble/dist/testdouble.mjs`. That build
49+
doesn't set a `td` global.
50+
51+
Bundlers that target the browser apply the `browser` condition already, so
52+
they need no setup. That includes webpack 5 (`target: 'web'`), Vite and
53+
esbuild (`--platform=browser`).
54+
55+
[@web/test-runner](https://modern-web.dev/docs/test-runner/overview/) and
56+
@web/dev-server don't apply it by default. Turn it on in your config file:
57+
58+
```js
59+
// web-test-runner.config.mjs
60+
export default {
61+
nodeResolve: {
62+
exportConditions: ['browser', 'development']
63+
}
64+
}
65+
```
66+
67+
Module replacement with `td.replace(path)` and `td.replaceEsm()` is only
68+
supported under Node.js, so the browser build throws an error if you call them.
69+
`td.replace(object, property)` works everywhere.
4770

4871
#### via unpkg
4972

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/node_modules/*

0 commit comments

Comments
 (0)