1
0
mirror of https://github.com/lancedikson/bowser synced 2026-10-15 23:51:36 +00:00
Files
lancedikson_bowser/AGENTS.md
naorpeled 5249a7e4e8 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.
2026-09-27 22:26:39 +03:00

5.2 KiB

Agents Instructions for Bowser

Architecture

Bowser parses User-Agent strings through a pipeline of four independent parser modules, each exporting an ordered array of descriptors:

  • src/parser-browsers.js → src/parser-os.js → src/parser-platforms.js → src/parser-engines.js

src/parser.js (the Parser class) orchestrates these by iterating each descriptor list using Utils.find(), testing via regex arrays or custom test(parser) functions, then calling describe(ua) on the first match. Descriptor order matters — more specific browsers (e.g., Opera, Edge) must come before generic ones (e.g., Chrome, Safari) to avoid false matches.

src/bowser.js is a stateless facade exposing Bowser.getParser(UA) and Bowser.parse(UA). src/constants.js holds bidirectional maps (BROWSER_ALIASES_MAP ↔ BROWSER_MAP) used for satisfies() alias lookups. src/utils.js provides regex helpers (getFirstMatch, getSecondMatch) and version comparison logic.

Also supports User-Agent Client Hints via an optional clientHints parameter on the constructor.

Commands

  • 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

Adding Browser/Bot Support (5 files to touch)

  1. src/parser-browsers.js: Add a descriptor with test (regex array) and describe(ua) returning { name, version }. Place it above any generic browser it might conflict with.
  2. src/parser-platforms.js: Add a platform descriptor if the new entry is a bot (return { type: PLATFORMS_MAP.bot, vendor }) or needs specific device detection.
  3. src/constants.js: Add entries to both BROWSER_ALIASES_MAP (display name → alias) and BROWSER_MAP (alias → display name). Alias convention: lowercase, underscores for spaces/dashes, drop "browser" suffix (e.g., 'UC Browser' → 'uc', 'Opera Coast' → 'opera_coast').
  4. test/acceptance/useragentstrings.yml: Add real UA strings under the browser's display name with full spec (browser, os, platform, engine). The acceptance test iterates this YAML and asserts Bowser.parse(ua) deep-equals the spec. The top-level key must match the browser's name field exactly.
  5. index.d.ts: Update TypeScript definitions if adding new API surface.

Run npm test to validate — acceptance tests auto-generate from the YAML file.

Descriptor Pattern

Every parser file exports an array of descriptors following this pattern:

{
  test: [/someregex/i],           // array of RegExp, OR:
  // test(parser) { return parser.test(/regex/); },  // function form for complex logic
  describe(ua) {
    return {
      name: 'BrowserName',
      version: Utils.getFirstMatch(/someregex\/(\d+(\.\d+)+)/i, ua),
    };
  },
}

Engine descriptors in src/parser-engines.js may use test(parser) to access parser.getBrowserName() for browser-dependent engine detection (e.g., Edge → Blink vs EdgeHTML).

satisfies() Version DSL

Parser.satisfies(checkTree) accepts a nested object for conditional browser/OS/platform checks. Keys in the check tree are resolved via BROWSER_ALIASES_MAP (so both 'chrome' and 'Chrome' work). Version operators: >, >=, <, <=, =, ~ (loose/prefix match). Values can be a single string or an array of version strings (OR logic).

// Simple browser check
parser.satisfies({ chrome: '>118', firefox: '~100' })

// Nested by OS or platform
parser.satisfies({ windows: { chrome: '>118' }, macos: { safari: '>=15' } })
parser.satisfies({ desktop: { chrome: '>118' }, mobile: { safari: '>=15' } })

Returns true/false if the browser matches, or undefined if the browser isn't mentioned in the tree. The ~ operator does a loose prefix comparison (e.g., ~100 matches 100.x.x).

Code Conventions

  • ES6 modules with explicit .js extensions in imports: import Utils from './utils.js'
  • Airbnb ESLint base; underscore-prefixed private properties (_ua, _hints)
  • Use void (0) instead of undefined for comparisons (legacy convention)
  • JSDoc on all public methods with @param, @return, @example
  • Browser versions are always strings, never numbers

Testing Details

  • 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.

Branching

  • master is the production branch.

Common Pitfalls

  • Always check BROWSER_ALIASES_MAP for existing aliases before adding new ones.
  • User-Agent strings can be complex; test edge cases thoroughly.
  • Remember to update both the alias map and the reverse map in constants.
  • Browser versions should be treated as strings, not numbers.
  • Keep regex patterns readable with comments explaining their purpose.