mirror of
https://github.com/lancedikson/bowser
synced 2026-10-15 15:40:52 +00:00
perf: trim bundled.js to the 2.14.1 polyfill set, fix stale docs
- Exclude the 10 core-js 3.0 modules with no counterpart in the @babel/polyfill set every released bundled.js shipped (concat/splice species fixes, Array#flat, Object.fromEntries, the __defineGetter__ family, NodeList#forEach): 156.1 -> 153.2 kB (49.3 -> 48.4 kB gzip). babel rejects unknown exclude names, so a core-js rename fails the build. - terser `passes: 5`, where output stops shrinking: es5.js 7.52 -> 7.49 kB gzip. - New test: bundled.js must not install polyfills no release ever shipped (fails on the previous master build, passes on 2.14.1 and this one). - README no longer claims named imports shrink the bundle; AGENTS.md describes the tsdown build and four-artifact acceptance tests; CONTRIBUTING points at the existing lint:check script.
This commit is contained in:
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@@ -38,5 +38,5 @@ If you'd like to contribute a change to `bowser`, modify the files in `src/`, an
|
||||
$ npm install
|
||||
$ npm run build #build
|
||||
$ npm test #run tests
|
||||
$ npm run lint #check lint rules
|
||||
$ npm run lint:check #check lint rules
|
||||
```
|
||||
|
||||
@@ -14,7 +14,7 @@ Also supports User-Agent Client Hints via an optional `clientHints` parameter on
|
||||
|
||||
## Commands
|
||||
|
||||
- **Build**: `npm run build` (Webpack + Babel → `es5.js`, `bundled.js`)
|
||||
- **Build**: `npm run build` (tsdown + Babel → UMD `es5.js` / `bundled.js` and ESM `bowser.mjs`, all ES5; see `tsdown.config.ts`)
|
||||
- **Test**: `npm test` (AVA — runs both `test/unit/` and `test/acceptance/`)
|
||||
- **Lint**: `npm run lint:check` / `npm run lint:fix`
|
||||
|
||||
@@ -72,7 +72,7 @@ Returns `true`/`false` if the browser matches, or `undefined` if the browser isn
|
||||
|
||||
## Testing Details
|
||||
|
||||
- **Acceptance tests** (`test/acceptance/test-list-of-ua.js`): Loads `useragentstrings.yml`, tests every UA against both `src/bowser.js` (ES6) and `es5.js` (built) — so **build before running tests** if you changed source.
|
||||
- **Acceptance tests** (`test/acceptance/test-list-of-ua.js`): Loads `useragentstrings.yml`, tests every UA against `src/bowser.js` and all three built artifacts (`es5.js`, `bundled.js`, `bowser.mjs`) — so **build before running tests** if you changed source.
|
||||
- **Unit tests** (`test/unit/`): Test individual Parser/Bowser/Utils methods. Use `new Parser(UA, true)` (skipParsing=true) for isolated method testing.
|
||||
- Test framework: AVA with Babel transpilation via `@babel/register`.
|
||||
|
||||
|
||||
@@ -39,8 +39,9 @@ import * as Bowser from "bowser"; // TypeScript
|
||||
import Bowser from "bowser"; // ES6 (and TypeScript with --esModuleInterop enabled)
|
||||
```
|
||||
|
||||
The ES module build also exposes `parse` and `getParser` as named exports, so
|
||||
you can import just the part you use and let your bundler drop the rest:
|
||||
The ES module build also exposes `parse` and `getParser` as named exports.
|
||||
Detection needs every parser either way, so named imports are a matter of
|
||||
style, not bundle size — but an import that ends up unused is dropped entirely:
|
||||
|
||||
```javascript
|
||||
import { getParser, parse } from "bowser";
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
* `core-js/es` rather than `core-js/stable`: `stable` is `es` plus every web
|
||||
* polyfill, and the extra ones — `URL`, `URLSearchParams`, `queueMicrotask` —
|
||||
* were never in any released `bundled.js`, yet cost ~20 kB (~7 kB gzipped).
|
||||
* The few `es` modules `@babel/polyfill` did not have are excluded in
|
||||
* `tsdown.config.ts` (`NOT_IN_BABEL_POLYFILL`).
|
||||
* `test/acceptance/test-es5-runtime.js` pins the `@babel/polyfill` surface.
|
||||
*
|
||||
* See `./es5.js` for why this only re-exports the default.
|
||||
|
||||
@@ -179,3 +179,26 @@ test('bundled.js still installs everything @babel/polyfill did', (t) => {
|
||||
vm.runInContext(fs.readFileSync(path.join(root, 'bundled.js'), 'utf8'), context);
|
||||
t.deepEqual(BABEL_POLYFILL_SURFACE.filter((ref) => !present(ref)), [], 'bundled.js no longer installs these');
|
||||
});
|
||||
|
||||
/**
|
||||
* The other side of the contract: polyfills no released `bundled.js` ever
|
||||
* shipped stay out, because each one is pure weight for existing consumers.
|
||||
* Adding one back should be a deliberate decision that updates this list, not
|
||||
* a side effect of switching a core-js entry point.
|
||||
*/
|
||||
const NEVER_SHIPPED = [
|
||||
'this.URL', 'this.URLSearchParams', 'this.queueMicrotask',
|
||||
'Array.prototype.flat', 'Object.fromEntries', 'NodeList.prototype.forEach',
|
||||
];
|
||||
|
||||
test('bundled.js does not install polyfills no release ever shipped', (t) => {
|
||||
const context = createEs5Context();
|
||||
vm.runInContext(NEVER_SHIPPED
|
||||
.map((ref) => `try { delete ${ref}; } catch (e) {}`)
|
||||
.join('\n'), context);
|
||||
vm.runInContext('this.NodeList = function NodeList() {};', context);
|
||||
const present = (ref) => vm.runInContext(`typeof (${ref})`, context) !== 'undefined';
|
||||
|
||||
vm.runInContext(fs.readFileSync(path.join(root, 'bundled.js'), 'utf8'), context);
|
||||
t.deepEqual(NEVER_SHIPPED.filter(present), []);
|
||||
});
|
||||
|
||||
@@ -38,11 +38,34 @@ const legacyBabel = (useBuiltIns: false | 'entry') => babel({
|
||||
modules: false,
|
||||
loose: true,
|
||||
useBuiltIns,
|
||||
...(useBuiltIns ? { corejs: '3' } : {}),
|
||||
...(useBuiltIns ? { corejs: '3', exclude: NOT_IN_BABEL_POLYFILL } : {}),
|
||||
targets: legacyTargets,
|
||||
}]],
|
||||
});
|
||||
|
||||
/**
|
||||
* The only core-js 3.0 `es` / web modules with no counterpart in the
|
||||
* `@babel/polyfill` (core-js 2) set that every released `bundled.js` shipped:
|
||||
* the `concat`/`splice` species fixes, `Array#flat`, `Object.fromEntries`, the
|
||||
* Annex B `__defineGetter__` family and `NodeList#forEach`. Excluding them makes
|
||||
* the payload exactly that set again, 2.8 kB (0.9 kB gzipped) smaller.
|
||||
*
|
||||
* babel rejects unknown names here, so a rename in core-js fails the build
|
||||
* rather than silently shipping the module again.
|
||||
*/
|
||||
const NOT_IN_BABEL_POLYFILL = [
|
||||
'es.array.concat',
|
||||
'es.array.flat',
|
||||
'es.array.splice',
|
||||
'es.array.unscopables.flat',
|
||||
'es.object.define-getter',
|
||||
'es.object.define-setter',
|
||||
'es.object.from-entries',
|
||||
'es.object.lookup-getter',
|
||||
'es.object.lookup-setter',
|
||||
'web.dom-collections.for-each',
|
||||
];
|
||||
|
||||
/**
|
||||
* Lowers the *emitted chunk* to ES5, after bundling and before terser.
|
||||
*
|
||||
@@ -108,6 +131,8 @@ const terser = () => ({
|
||||
// IE 8: reserved words as property names must stay quoted.
|
||||
ie8: true,
|
||||
safari10: true,
|
||||
// Output stops shrinking at 5 passes (measured; 6+ is byte-identical).
|
||||
compress: { passes: 5 },
|
||||
format: {
|
||||
// Keep the `/*!` banner.
|
||||
comments: /^!/,
|
||||
|
||||
Reference in New Issue
Block a user