mirror of
https://github.com/lancedikson/bowser
synced 2026-10-07 11:21:03 +00:00
fix: keep every published file reachable, and test all four build artifacts
Two follow-ups to the dual packaging change (#628), found while running a consumer-facing regression sweep against published 2.14.1. An exports map is a closed list. Before #628 bowser had no exports map, so every published file was reachable by subpath; afterwards LICENSE, README.md and index.d.ts resolved to ERR_PACKAGE_PATH_NOT_EXPORTED. Nothing in the documented API regressed, but `/// <reference types="bowser/index.d.ts" />` and tooling that resolves the license by specifier both did. Add the four non-code files to the map so the published surface matches 2.14.1 exactly, and assert it in the package smoke test (25 -> 29 checks). The acceptance suite asserted src/bowser.js and es5.js against the UA corpus but not bundled.js or bowser.mjs. bowser.mjs is what the `import` condition resolves to, so it is the file every modern ESM and bundler consumer runs, and a build regression confined to it would have kept CI green. Assert all four artifacts against the spec instead. Verified by injecting a Chrome-parsing regression into bowser.mjs and into bundled.js separately (each turns the suite red, labelled by artifact), and by running the new smoke assertions against a pre-fix tarball (exit 1) and this one (exit 0) on Node 12.16, 14, 18, 20 and 22.
This commit is contained in:
@@ -71,6 +71,10 @@
|
|||||||
"./src/parser-platforms": "./src/parser-platforms.js",
|
"./src/parser-platforms": "./src/parser-platforms.js",
|
||||||
"./src/utils.js": "./src/utils.js",
|
"./src/utils.js": "./src/utils.js",
|
||||||
"./src/utils": "./src/utils.js",
|
"./src/utils": "./src/utils.js",
|
||||||
|
"./LICENSE": "./LICENSE",
|
||||||
|
"./README.md": "./README.md",
|
||||||
|
"./index.d.ts": "./index.d.ts",
|
||||||
|
"./index.d.mts": "./index.d.mts",
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -2,7 +2,41 @@ import test from 'ava';
|
|||||||
import yaml from 'yamljs';
|
import yaml from 'yamljs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import Bowser from '../../src/bowser';
|
import Bowser from '../../src/bowser';
|
||||||
import BowserBuild from '../../es5';
|
import BowserEs5 from '../../es5';
|
||||||
|
import BowserBundled from '../../bundled';
|
||||||
|
import BowserMjs from '../../bowser.mjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every published JS artifact is built from `src/` through a different
|
||||||
|
* pipeline — raw ES modules, babel + terser UMD, UMD with core-js baked in,
|
||||||
|
* and the rolldown ESM build. A regression in any one of them ships silently
|
||||||
|
* unless each is asserted against the spec independently.
|
||||||
|
*
|
||||||
|
* `bowser.mjs` matters most: it is what the `import` condition of the exports
|
||||||
|
* map resolves to, so it is the file every modern ESM and bundler consumer
|
||||||
|
* actually runs. Testing only `src/` and `es5.js` leaves it uncovered.
|
||||||
|
*
|
||||||
|
* These are build outputs, so `pnpm build` has to run before `pnpm test`.
|
||||||
|
* Importing `bowser.mjs` from this CommonJS test file relies on Node's
|
||||||
|
* require(esm) support (Node >= 22.12) — the build toolchain already requires
|
||||||
|
* a newer Node than that, so anything able to produce these files can load them.
|
||||||
|
*/
|
||||||
|
const artifacts = [
|
||||||
|
['src/bowser.js', Bowser],
|
||||||
|
['es5.js', BowserEs5],
|
||||||
|
['bundled.js', BowserBundled],
|
||||||
|
['bowser.mjs', BowserMjs],
|
||||||
|
];
|
||||||
|
|
||||||
|
// A missing build output fails at import with a plain "Cannot find module".
|
||||||
|
// This catches the quieter failure: an artifact that loads but is not the
|
||||||
|
// Bowser class — a broken UMD wrapper or a lost CJS interop unwrap would
|
||||||
|
// otherwise make every assertion below vacuous instead of failing.
|
||||||
|
artifacts.forEach(([name, artifact]) => {
|
||||||
|
if (typeof artifact !== 'function' || typeof artifact.parse !== 'function') {
|
||||||
|
throw new Error(`${name} did not load as the Bowser class — run \`pnpm build\` first`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const listOfUA = yaml.load(path.join(__dirname, 'useragentstrings.yml'));
|
const listOfUA = yaml.load(path.join(__dirname, 'useragentstrings.yml'));
|
||||||
|
|
||||||
@@ -11,11 +45,10 @@ const browserNames = Object.keys(listOfUA);
|
|||||||
browserNames.forEach((browserName) => {
|
browserNames.forEach((browserName) => {
|
||||||
listOfUA[browserName].forEach((browser, index) => {
|
listOfUA[browserName].forEach((browser, index) => {
|
||||||
test(`Test ${browserName} ${index}`, (t) => {
|
test(`Test ${browserName} ${index}`, (t) => {
|
||||||
const parsed = Bowser.parse(browser.ua);
|
artifacts.forEach(([name, artifact]) => {
|
||||||
const parsedBuild = BowserBuild.parse(browser.ua);
|
t.deepEqual(artifact.parse(browser.ua), browser.spec, `${name}: ${browser.ua}`);
|
||||||
t.deepEqual(parsed, browser.spec, `${browser.ua}`);
|
});
|
||||||
t.deepEqual(parsedBuild, browser.spec, `${browser.ua}`);
|
t.is(Bowser.parse(browser.ua).browser.name, browserName, `${browser.ua}`);
|
||||||
t.is(parsed.browser.name, browserName, `${browser.ua}`);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -93,6 +93,19 @@ check('require.resolve("bowser/package.json") works', function () {
|
|||||||
assert.ok(fs.existsSync(require.resolve('bowser/package.json')));
|
assert.ok(fs.existsSync(require.resolve('bowser/package.json')));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// not code, but they did resolve on every previous version — license and
|
||||||
|
// attribution tooling and `/// <reference types="bowser/index.d.ts" />`
|
||||||
|
// consumers can all depend on them.
|
||||||
|
['bowser/LICENSE', 'bowser/README.md', 'bowser/index.d.ts', 'bowser/index.d.mts']
|
||||||
|
.forEach(function (id) {
|
||||||
|
check('resolves "' + id + '"', function () {
|
||||||
|
assert.ok(fs.existsSync(require.resolve(id)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// --- The published manifest ------------------------------------------------
|
// --- The published manifest ------------------------------------------------
|
||||||
|
|
||||||
check('main/browser/module/types fields are unchanged', function () {
|
check('main/browser/module/types fields are unchanged', function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user