From 0626bf8c8c0fc5a285a287683ceac09c909d4d7a Mon Sep 17 00:00:00 2001 From: pastak Date: Mon, 11 Dec 2023 17:37:33 +0900 Subject: [PATCH 01/14] Fix link to doc page on README.md (#543) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 242d63b..b13f97d 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The library is made to help to detect what browser your user has and gives you a ### ⚠️ Version 2.0 breaking changes ⚠️ -Version 2.0 has drastically changed the API. All available methods are on the [docs page](https://lancedikson.github.io/bowser/docs). +Version 2.0 has drastically changed the API. All available methods are on the [docs page](https://bowser-js.github.io/bowser/docs/). _For legacy code, check out the [1.x](https://github.com/lancedikson/bowser/tree/v1.x) branch and install it through `npm install bowser@1.9.4`._ From 327e6f5e519e9e4a553879befd167f5742a2b4f3 Mon Sep 17 00:00:00 2001 From: Naor Peled Date: Sat, 20 Apr 2024 14:35:56 +0300 Subject: [PATCH 02/14] ci: add release drafter (#545) --- .github/release-drafter.yml | 19 ++++++++++++ .../draft-or-update-next-release.yml | 20 +++++++++++++ .github/workflows/publish.yml | 29 +++++++++++++++++++ package.json | 1 - 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/draft-or-update-next-release.yml create mode 100644 .github/workflows/publish.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..131e1ca --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,19 @@ +name-template: 'v$RESOLVED_VERSION 🌈' +tag-template: 'v$RESOLVED_VERSION' +version-resolver: + major: + labels: + - major + minor: + labels: + - minor + patch: + labels: + - patch + default: patch +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +change-title-escapes: '\<*_&' +template: | + ## Changes + + $CHANGES diff --git a/.github/workflows/draft-or-update-next-release.yml b/.github/workflows/draft-or-update-next-release.yml new file mode 100644 index 0000000..bb55892 --- /dev/null +++ b/.github/workflows/draft-or-update-next-release.yml @@ -0,0 +1,20 @@ +name: 📝 Draft or update next release +concurrency: draft_or_update_next_release + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + prepare-deployment: + name: 📝 Draft or update next release + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v1 + + - uses: release-drafter/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..9cfdef7 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,29 @@ +name: Release + +on: + # This job runs when a new release is published + release: + types: [published] + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 16 + registry-url: https://registry.npmjs.org + - uses: actions/cache@v2 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }} + # Store the name of the release + # See https://stackoverflow.com/questions/58177786/get-the-current-pushed-tag-in-github-actions + - run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + - run: npm ci + - run: npm version $RELEASE_VERSION --no-git-tag-version + - run: npm run build + - run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.BOWSER_NPM_PUBLISH_TOKEN }} diff --git a/package.json b/package.json index c719065..8411f81 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,5 @@ { "name": "bowser", - "version": "2.11.0", "description": "Lightweight browser detector", "keywords": [ "browser", From 16c9f22546bb36be064f3d38286095e192db0c39 Mon Sep 17 00:00:00 2001 From: Naor Peled Date: Sat, 20 Apr 2024 14:54:03 +0300 Subject: [PATCH 03/14] ci: attempt to resolve merge to master errors (#546) --- .github/workflows/merge-to-master.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/merge-to-master.yml b/.github/workflows/merge-to-master.yml index b22b0a0..40189c7 100644 --- a/.github/workflows/merge-to-master.yml +++ b/.github/workflows/merge-to-master.yml @@ -28,6 +28,7 @@ jobs: uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} + - run: npm i -g nyc - run: npm ci - run: npm run build - run: nyc npm test && nyc report --reporter=text-lcov | ./node_modules/coveralls/bin/coveralls.js From 5bab4ee97d75337e431e432ff37619ae643882bc Mon Sep 17 00:00:00 2001 From: Meir Roth <12494197+meirroth@users.noreply.github.com> Date: Sun, 28 Apr 2024 23:42:58 +0300 Subject: [PATCH 04/14] docs: remove non-existing links in README --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index b13f97d..2b5c0c4 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,6 @@ Don't hesitate to support the project on Github or [OpenCollective](https://open # Contents - [Overview](#overview) - [Use cases](#use-cases) -- [Advanced usage](#advanced-usage) -- [How can I help?](#contributing) # Overview From 8c489bd98b0128ce57edb4cbaf2a398170f2c466 Mon Sep 17 00:00:00 2001 From: Denis Demchenko Date: Mon, 12 Aug 2024 13:42:09 +0300 Subject: [PATCH 05/14] chore: move the token to secrets --- .coveralls.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.coveralls.yml b/.coveralls.yml index 6d1e992..8b13789 100644 --- a/.coveralls.yml +++ b/.coveralls.yml @@ -1 +1 @@ -repo_token: Ba2bS7pOlSLZWuESBnff8qxDjIS8Mg1Z0 + From e0ee0e7baf7a4679e3cc9ccbd5eb24c240a9d3e1 Mon Sep 17 00:00:00 2001 From: Naor Peled Date: Sun, 6 Jul 2025 19:21:23 +0300 Subject: [PATCH 06/14] ci: bump cache action to v4 --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9cfdef7..4882ce3 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,7 +14,7 @@ jobs: with: node-version: 16 registry-url: https://registry.npmjs.org - - uses: actions/cache@v2 + - uses: actions/cache@v4 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }} From afc0fbf1319347597d93b8b2ad9a4afb73ab01a1 Mon Sep 17 00:00:00 2001 From: Naor Peled Date: Sun, 6 Jul 2025 19:24:07 +0300 Subject: [PATCH 07/14] ci(release): bump all deps --- .github/workflows/publish.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4882ce3..2dbcb31 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,11 +9,10 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 16 - registry-url: https://registry.npmjs.org - uses: actions/cache@v4 with: path: ~/.npm From 20fc037785c533525e70c77013d69b4e5639a4c6 Mon Sep 17 00:00:00 2001 From: Naor Peled Date: Sat, 9 Aug 2025 20:51:00 +0300 Subject: [PATCH 08/14] fix: resolve CI failures (#560) --- .../workflows/draft-or-update-next-release.yml | 4 ++-- .github/workflows/merge-to-master.yml | 6 +++--- .github/workflows/publish.yml | 2 +- .github/workflows/pull-request.yml | 16 ++++++++-------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/draft-or-update-next-release.yml b/.github/workflows/draft-or-update-next-release.yml index bb55892..a5f6567 100644 --- a/.github/workflows/draft-or-update-next-release.yml +++ b/.github/workflows/draft-or-update-next-release.yml @@ -4,7 +4,7 @@ concurrency: draft_or_update_next_release on: push: branches: - - main + - master workflow_dispatch: jobs: @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - uses: release-drafter/release-drafter@v5 env: diff --git a/.github/workflows/merge-to-master.yml b/.github/workflows/merge-to-master.yml index 40189c7..0fb47fb 100644 --- a/.github/workflows/merge-to-master.yml +++ b/.github/workflows/merge-to-master.yml @@ -1,4 +1,4 @@ -name: 'Merge to master' +name: "Merge to master" on: push: @@ -23,9 +23,9 @@ jobs: shell: bash run: echo "BRANCH_NAME=$(echo ${GITHUB_HEAD_REF} | tr / -)" >> $GITHUB_ENV - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm i -g nyc diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2dbcb31..b9dee4f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 16 + node-version: 12.16.3 - uses: actions/cache@v4 with: path: ~/.npm diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index d326d04..f145bc1 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -1,4 +1,4 @@ -name: 'Pull Request' +name: "Pull Request" on: pull_request: types: [opened, reopened, synchronize] @@ -11,12 +11,12 @@ jobs: node: [12.16.3] name: Node ${{ matrix.node }} steps: - - name: 'Checkout latest code' - uses: actions/checkout@v3 + - name: "Checkout latest code" + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} - name: Set up node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} - name: Install dependencies @@ -27,17 +27,17 @@ jobs: run: npm run test lint: - name: 'ESLint' + name: "ESLint" runs-on: ubuntu-latest steps: - name: Checkout latest code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} - name: Set up node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: '16' + node-version: "16" - name: Install dependencies run: npm ci - name: Run ESLint From 8ac2f6a1ecbcc5f68ea5d17460a50bea3692f236 Mon Sep 17 00:00:00 2001 From: Naor Peled Date: Sat, 9 Aug 2025 21:05:17 +0300 Subject: [PATCH 09/14] chore(deps): change nyc version to 15 for Node 12 to work (#565) --- .github/workflows/merge-to-master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/merge-to-master.yml b/.github/workflows/merge-to-master.yml index 0fb47fb..f83149b 100644 --- a/.github/workflows/merge-to-master.yml +++ b/.github/workflows/merge-to-master.yml @@ -28,7 +28,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - - run: npm i -g nyc + - run: npm i -g nyc@15 - run: npm ci - run: npm run build - run: nyc npm test && nyc report --reporter=text-lcov | ./node_modules/coveralls/bin/coveralls.js From 3c25806efebcb7fd05ba1466486585f258001e37 Mon Sep 17 00:00:00 2001 From: Naor Peled Date: Sat, 9 Aug 2025 21:27:38 +0300 Subject: [PATCH 10/14] ci(publish): add manual trigger and retry logic for npm publish --- .github/workflows/publish.yml | 43 +++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b9dee4f..1e16464 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,6 +4,13 @@ on: # This job runs when a new release is published release: types: [published] + # Manual trigger with version input + workflow_dispatch: + inputs: + version: + description: "Version to release (e.g., 2.12.1)" + required: true + type: string jobs: release: @@ -13,16 +20,44 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 12.16.3 + registry-url: "https://registry.npmjs.org" - uses: actions/cache@v4 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }} - # Store the name of the release - # See https://stackoverflow.com/questions/58177786/get-the-current-pushed-tag-in-github-actions - - run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + # Store the release version (from release tag or manual input) + - name: Set release version + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "RELEASE_VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV + echo "Manual release triggered for version: ${{ github.event.inputs.version }}" + else + echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + echo "Release triggered from tag: ${GITHUB_REF#refs/*/}" + fi - run: npm ci - run: npm version $RELEASE_VERSION --no-git-tag-version - run: npm run build - - run: npm publish --access public + - name: Publish to npm with retry + run: | + max_attempts=5 + attempt=1 + while [ $attempt -le $max_attempts ]; do + echo "Attempt $attempt of $max_attempts..." + if npm publish --access public; then + echo "Successfully published!" + break + else + if [ $attempt -eq $max_attempts ]; then + echo "Failed to publish after $max_attempts attempts" + exit 1 + fi + echo "Publish failed, waiting before retry..." + sleep_time=$((attempt * 30)) + echo "Waiting ${sleep_time} seconds before retry..." + sleep $sleep_time + attempt=$((attempt + 1)) + fi + done env: NODE_AUTH_TOKEN: ${{ secrets.BOWSER_NPM_PUBLISH_TOKEN }} From 5bfd3e9e6cd5d52a07c8e3ef489b95072ccbe8e5 Mon Sep 17 00:00:00 2001 From: Naor Peled Date: Sat, 23 Aug 2025 19:32:29 +0300 Subject: [PATCH 11/14] revert(index.d.ts): rollback default export to `=` syntax (#570) --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index d5b72c4..e4b7e41 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2,7 +2,7 @@ // Project: https://github.com/lancedikson/bowser // Definitions by: Alexander P. Cerutti , -export default Bowser; +export = Bowser; export as namespace Bowser; declare namespace Bowser { From a4d0b828e459ee5c7f6f90fa40a4c252f62f532e Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 13:31:12 +0200 Subject: [PATCH 12/14] chore: add Copilot instructions for repository (#574) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: naorpeled <6171622+naorpeled@users.noreply.github.com> --- .github/copilot-instructions.md | 129 ++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..7492a7c --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,129 @@ +# Copilot Instructions for Bowser + +## Project Overview + +Bowser is a small, fast, and rich-API browser/platform/engine detector for both browser and Node.js environments. It's designed to parse User-Agent strings and provide detailed information about browsers, operating systems, platforms, and rendering engines. + +## Architecture + +### Core Components + +- **`src/bowser.js`**: Main entry point and public API. Provides static methods `getParser()` and `parse()`. +- **`src/parser.js`**: Core parsing engine that orchestrates all parsers and returns structured results. +- **`src/parser-browsers.js`**: Browser detection logic using regex patterns. +- **`src/parser-os.js`**: Operating system detection logic. +- **`src/parser-platforms.js`**: Platform type detection (desktop, tablet, mobile). +- **`src/parser-engines.js`**: Rendering engine detection (WebKit, Blink, Gecko, etc.). +- **`src/constants.js`**: Centralized constants including browser aliases and mappings. +- **`src/utils.js`**: Utility functions for string matching and manipulation. + +### Build Output + +- **`es5.js`**: ES5 transpiled version (default export). +- **`bundled.js`**: ES5 version with babel-polyfill included. + +## Development Workflow + +### Setup + +```bash +npm install +``` + +### Key Commands + +- **Build**: `npm run build` - Compiles source files using Webpack and Babel. +- **Test**: `npm test` - Runs unit and acceptance tests using AVA. +- **Lint**: `npm run lint:check` - Checks code style using ESLint. +- **Lint Fix**: `npm run lint:fix` - Auto-fixes linting issues. +- **Watch Mode**: `npm run watch` - Builds on file changes. +- **Test Watch**: `npm run test:watch` - Runs tests on file changes. + +### Testing + +- Tests are located in `test/acceptance/` and `test/unit/`. +- Acceptance tests use real User-Agent strings from `test/acceptance/useragentstrings.yml`. +- Always update `useragentstrings.yml` when adding browser support. +- Test framework: AVA with Babel integration. + +## Coding Standards + +### Style Guide + +- **ESLint Config**: Based on Airbnb Base style guide. +- **Parser**: Uses `babel-eslint`. +- **Exceptions**: + - Underscore-dangle allowed for private properties. + - `no-void` disabled. + - ES6 imports must include `.js` extension. + +### Naming Conventions + +- **Browser Aliases**: Use lowercase letters, replace spaces/dashes with underscores, drop "browser" suffix. + - Examples: `Opera Coast` → `opera_coast`, `UC Browser` → `uc`, `SeaMonkey` → `seamonkey`. +- **Private Properties**: Prefix with underscore (e.g., `_ua`). +- **Constants**: Use `UPPER_SNAKE_CASE` for constant maps and aliases. + +### Code Patterns + +- Use ES6 modules with explicit `.js` extensions. +- Prefer static methods in Bowser class. +- Use class-based structure for Parser. +- Regex patterns should be well-documented and tested. +- Keep parsers modular and focused on single responsibility. + +## Adding Browser Support + +When adding support for a new browser: + +1. Add regex pattern to `src/parser-browsers.js`. +2. Add browser name to `BROWSER_ALIASES_MAP` in `src/constants.js`. +3. Add corresponding entry to `BROWSER_MAP`. +4. Add test cases to `test/acceptance/useragentstrings.yml`. +5. Run tests to verify: `npm test`. +6. Check for duplicates before adding aliases. + +## Branching Strategy + +- **`master`**: Development branch. +- **`production`**: Production branch. +- **New Features**: Branch from `master`, PR back to `master`. +- **Hot-fixes/Browser Support**: Branch from `production`, PR back to `production`. + +## Important Files + +- **`index.d.ts`**: TypeScript definitions. +- **`.babelrc`**: Babel configuration for ES5 transpilation. +- **`webpack.config.js`**: Build configuration. +- **`.eslintrc.yml`**: Linting rules. +- **`package.json`**: Dependencies and scripts. + +## API Design Principles + +- Keep the API simple and intuitive. +- Bowser class should be stateless and provide factory methods. +- Parser class handles instance-specific logic. +- Results should be structured and predictable. +- Support both immediate parsing and lazy parsing. + +## Performance Considerations + +- Parsers use lazy evaluation where possible. +- Regex patterns are optimized for common browsers first. +- Optional `skipParsing` parameter for delayed parsing. +- Minimal bundle size is a priority (~4.8kB gzipped). + +## Documentation + +- Use JSDoc comments for all public APIs. +- Document parameters, return types, and provide examples. +- Update README.md for API changes. +- Generate docs with: `npm run generate-docs`. + +## 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. From 2b5ee5de8c5e56559a963d1e6aa73fd5318377ee Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 14:27:00 +0200 Subject: [PATCH 13/14] feat: add Support for HarmonyOS User-Agent Detection (#567) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: naorpeled <6171622+naorpeled@users.noreply.github.com> --- src/constants.js | 1 + src/parser-os.js | 12 ++++++++++++ test/acceptance/useragentstrings.yml | 14 ++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/src/constants.js b/src/constants.js index 9ce0c25..b0da115 100644 --- a/src/constants.js +++ b/src/constants.js @@ -107,6 +107,7 @@ export const OS_MAP = { ChromeOS: 'Chrome OS', PlayStation4: 'PlayStation 4', Roku: 'Roku', + HarmonyOS: 'HarmonyOS', }; export const ENGINE_MAP = { diff --git a/src/parser-os.js b/src/parser-os.js index 4c516dd..abc2aac 100644 --- a/src/parser-os.js +++ b/src/parser-os.js @@ -87,6 +87,18 @@ export default [ }, }, + /* HarmonyOS */ + { + test: [/OpenHarmony/i], + describe(ua) { + const version = Utils.getFirstMatch(/OpenHarmony\s+(\d+(\.\d+)*)/i, ua); + return { + name: OS_MAP.HarmonyOS, + version, + }; + }, + }, + /* Android */ { test(parser) { diff --git a/test/acceptance/useragentstrings.yml b/test/acceptance/useragentstrings.yml index 9fedcb4..76d08fb 100644 --- a/test/acceptance/useragentstrings.yml +++ b/test/acceptance/useragentstrings.yml @@ -365,6 +365,20 @@ type: "mobile" engine: name: "Blink" + - + ua: "Mozilla/5.0 (Phone; OpenHarmony 5.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Mobile HuaweiBrowser/5.1.5.352" + spec: + browser: + name: "Chrome" + version: "114.0.0.0" + os: + name: "HarmonyOS" + version: "5.0" + platform: + type: "mobile" + vendor: "Huawei" + engine: + name: "Blink" Google Search: - ua: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) GSA/83.0.268992909 Mobile/15E148 Safari/605.1" From bd5cb7186c804778a69ba7047939dc6a74d6004d Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 15:15:13 +0200 Subject: [PATCH 14/14] feat: add named exports for tree shaking support with modern bundlers (#566) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: naorpeled <6171622+naorpeled@users.noreply.github.com> Co-authored-by: naorpeled --- index.d.ts | 7 +++++ src/bowser.js | 6 +++++ test/unit/bowser.js | 63 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index e4b7e41..b78ae12 100644 --- a/index.d.ts +++ b/index.d.ts @@ -5,6 +5,13 @@ export = Bowser; export as namespace Bowser; +export function getParser(UA: string, skipParsing?: boolean): Bowser.Parser.Parser; +export function parse(UA: string): Bowser.Parser.ParsedResult; +export const BROWSER_MAP: Record; +export const ENGINE_MAP: Record; +export const OS_MAP: Record; +export const PLATFORMS_MAP: Record; + declare namespace Bowser { /** * Creates a Parser instance diff --git a/src/bowser.js b/src/bowser.js index f79e6e0..311ce89 100644 --- a/src/bowser.js +++ b/src/bowser.js @@ -75,3 +75,9 @@ class Bowser { } export default Bowser; + +export const { getParser } = Bowser; +export const { parse } = Bowser; +export { + BROWSER_MAP, ENGINE_MAP, OS_MAP, PLATFORMS_MAP, +}; diff --git a/test/unit/bowser.js b/test/unit/bowser.js index a27b2c9..f568893 100644 --- a/test/unit/bowser.js +++ b/test/unit/bowser.js @@ -1,5 +1,5 @@ import test from 'ava'; -import Bowser from '../../src/bowser'; +import Bowser, { getParser, parse, BROWSER_MAP, ENGINE_MAP, OS_MAP, PLATFORMS_MAP } from '../../src/bowser'; import Parser from '../../src/parser'; const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 OPR/43.0.2442.1165'; @@ -16,3 +16,64 @@ test('Bowser`s constructor fails if UA is empty', (t) => { test('Bowser.parse parses UA and returns result', (t) => { t.deepEqual(Bowser.parse(UA), browser.getResult()); }); + +test('Named export getParser works like Bowser.getParser', (t) => { + const namedExportParser = getParser(UA); + t.truthy(namedExportParser instanceof Parser); + t.deepEqual(namedExportParser.getResult(), browser.getResult()); +}); + +test('Named export getParser with skipParsing parameter', (t) => { + const parserWithSkip = getParser(UA, true); + t.truthy(parserWithSkip instanceof Parser); + // With skipParsing=true, the result should be undefined until we explicitly parse + t.deepEqual(parserWithSkip.getResult(), Bowser.getParser(UA, true).getResult()); +}); + +test('Named export getParser throws error for invalid UA', (t) => { + t.throws(() => getParser(undefined), { message: 'UserAgent should be a string' }); + t.throws(() => getParser(123), { message: 'UserAgent should be a string' }); + t.throws(() => getParser(null), { message: 'UserAgent should be a string' }); + t.throws(() => getParser({}), { message: 'UserAgent should be a string' }); +}); + +test('Named export parse works like Bowser.parse', (t) => { + t.deepEqual(parse(UA), Bowser.parse(UA)); +}); + +test('Named export parse produces consistent results', (t) => { + const result1 = parse(UA); + const result2 = parse(UA); + t.deepEqual(result1, result2); + t.deepEqual(result1, Bowser.parse(UA)); +}); + +test('Named exports of constants are available', (t) => { + t.truthy(BROWSER_MAP); + t.truthy(ENGINE_MAP); + t.truthy(OS_MAP); + t.truthy(PLATFORMS_MAP); + t.is(BROWSER_MAP, Bowser.BROWSER_MAP); + t.is(ENGINE_MAP, Bowser.ENGINE_MAP); + t.is(OS_MAP, Bowser.OS_MAP); + t.is(PLATFORMS_MAP, Bowser.PLATFORMS_MAP); +}); + +test('Named exports constants are objects with expected structure', (t) => { + t.is(typeof BROWSER_MAP, 'object'); + t.is(typeof ENGINE_MAP, 'object'); + t.is(typeof OS_MAP, 'object'); + t.is(typeof PLATFORMS_MAP, 'object'); +}); + +test('All named exports work together', (t) => { + // Test that we can use multiple named exports in the same scope + const result = parse(UA); + const parser = getParser(UA); + + t.deepEqual(result, parser.getResult()); + t.truthy(BROWSER_MAP); + t.truthy(ENGINE_MAP); + t.truthy(OS_MAP); + t.truthy(PLATFORMS_MAP); +});