1
0
mirror of https://github.com/lancedikson/bowser synced 2026-10-18 09:04:33 +00:00

fix: keep ESM consumers on ES5, enable tree-shaking, trim bundled.js

Found by a differential test of the packed candidate against the published
2.14.1 across Node 12-22, Bun, TypeScript 4.7-7.0, seven bundlers and a
real browser.

- bowser.mjs is now lowered to ES5 syntax (keeping `export`). Before the
  exports map, webpack, Rollup and esbuild resolved `import 'bowser'` to
  es5.js via the `browser` field; the ES2015 bowser.mjs broke esbuild
  `--target=es5` and shipped classes into webpack ES5 builds. The ESM build
  sets `minify: false` explicitly, because rolldown's default DCE-only pass
  re-prints the chunk after renderChunk and restores shorthand properties.
- `"sideEffects": ["./bundled.js"]` lets esbuild, Vite and webpack 4 drop an
  unused `import 'bowser'`; bundled.js stays listed so polyfill-only imports
  are never tree-shaken away.
- `./package` export restores extensionless `require('bowser/package')`,
  which resolved on 2.14.1.
- bundled.js imports `core-js/es` plus the timers, immediate and
  DOM-collection web polyfills instead of `core-js/stable`, dropping URL,
  URLSearchParams and queueMicrotask, which no released bundled.js ever
  shipped: 176.9 kB -> 156.1 kB (56.6 -> 49.3 kB gzip).

Tests: bowser.mjs must parse as ES5 apart from its export; bundled.js must
install the @babel/polyfill surface 2.14.1 shipped (passes on 2.14.1's
bundled.js, fails on unpolyfilled es5.js); package smoke test covers
`bowser/package` and `sideEffects`.
This commit is contained in:
naorpeled
2026-09-27 01:04:05 +03:00
parent c1208b4a56
commit 7b144d8247
6 changed files with 119 additions and 23 deletions

View File

@@ -101,6 +101,12 @@ check('require.resolve("bowser/package.json") works', function () {
assert.ok(fs.existsSync(require.resolve('bowser/package.json')));
});
// Extensionless, like `bowser/es5` — CommonJS resolution appended `.json`
// before the exports map existed.
check('require("bowser/package") works', function () {
assert.strictEqual(require('bowser/package').name, 'bowser');
});
// Before the exports map existed, *every* published file was reachable by
// subpath. An exports map is a closed list, so anything published but not
// enumerated silently becomes ERR_PACKAGE_PATH_NOT_EXPORTED. These four are
@@ -124,6 +130,14 @@ check('main/browser/module/types fields are unchanged', function () {
assert.strictEqual(pkg.types, 'index.d.ts');
});
// Lets esbuild and Vite drop an unused `import 'bowser'` (webpack and Rollup
// already do). `bundled.js` is the one file whose import *is* the point: it
// installs polyfills, so it must never be tree-shaken away.
check('"sideEffects" lists exactly bundled.js', function () {
var pkg = require('bowser/package.json');
assert.deepStrictEqual(pkg.sideEffects, ['./bundled.js']);
});
check('no "engines" field (would warn/fail installs on old Node)', function () {
var pkg = require('bowser/package.json');
assert.strictEqual(pkg.engines, undefined);