diff --git a/.changeset/rotten-teachers-join.md b/.changeset/rotten-teachers-join.md new file mode 100644 index 00000000..ecff0a1a --- /dev/null +++ b/.changeset/rotten-teachers-join.md @@ -0,0 +1,5 @@ +--- +'astro-useragent': minor +--- + +Upgrade ua-parser-js to v2 diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 5a860270..7b28acff 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -34,4 +34,3 @@ jobs: - run: pnpm run test - run: pnpm run build - - run: pnpx pkg-pr-new publish './packages/astro-purgecss' diff --git a/.github/workflows/pkgpr.yml b/.github/workflows/pkgpr.yml new file mode 100644 index 00000000..71f12677 --- /dev/null +++ b/.github/workflows/pkgpr.yml @@ -0,0 +1,23 @@ +name: Publish packages +on: [pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - run: corepack enable + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + - name: Build + run: pnpm build + + - run: pnpx pkg-pr-new publish './packages/astro-purgecss' './packages/astro-useragent' diff --git a/packages/astro-useragent/README.md b/packages/astro-useragent/README.md index 77dfcbb5..89139799 100644 --- a/packages/astro-useragent/README.md +++ b/packages/astro-useragent/README.md @@ -8,7 +8,7 @@ Astro UserAgent is a simple helper for parsing `user-agent` header strings for browser matching inside your Astro Pages / API routes, when using [SSR Mode][astro-ssr] -> **Note** Due to the nature of Astro being an SSG by trade, This package only works when used with astro in [SSR Mode][astro-ssr]. +> **Note** Due to the nature of Astro being an SSG by trade, This package only works when used with Astro in [SSR Mode][astro-ssr]. ## 📦 Installation @@ -112,31 +112,42 @@ The parsed `UserAgent` object will have the following interface: ```typescript export interface UserAgent { - readonly source: string; // original user agent string. + readonly source: string | null; // The original user agent string. + readonly browser: string | null; + readonly browserVersion: number; + readonly cpu: string | null; readonly deviceType: string | null; readonly deviceVendor: string | null; - readonly os: string; - readonly osVersion: number; - readonly browser: string; - readonly browserVersion: number; - readonly engine: string; - readonly engineVersion: number; - readonly isIphone: boolean; - readonly isIpad: boolean; - readonly isMobile: boolean; - readonly isTablet: boolean; - readonly isDesktop: boolean; + readonly engine: string | null; + readonly engineVersion: number | null; + readonly os: string | null; + readonly osVersion: number | null; + readonly isAndroid: boolean; readonly isChrome: boolean; + readonly isChromeOS: boolean; + readonly isDesktop: boolean; + readonly isEdge: boolean; readonly isFirefox: boolean; - readonly isSafari: boolean; readonly isIE: boolean; - readonly isEdge: boolean; - readonly isOpera: boolean; + readonly isIos: boolean; + readonly isIpad: boolean; + readonly isIphone: boolean; readonly isMac: boolean; - readonly isChromeOS: boolean; + readonly isMobile: boolean; + readonly isOpera: boolean; + readonly isSafari: boolean; + readonly isTablet: boolean; readonly isWindows: boolean; - readonly isIos: boolean; - readonly isAndroid: boolean; + readonly isBot: boolean; + readonly isAIBot: boolean; + readonly isChromeFamily: boolean; + readonly isAppleSilicon: boolean; + getUA(): string; + getBrowser(): IBrowser; + getCPU(): ICPU; + getDevice(): IDevice; + getEngine(): IEngine; + getOS(): IOS; } ``` @@ -157,12 +168,11 @@ Please see the [Changelog](CHANGELOG.md) for more information on what has change ## Acknowledgements -`astro-useragent` is a port from [next-useragent][next-useragent] to Astro. so big thanks to [Tsuyoshi Tokuda][tokuda109] and the contributors behind next-useragent package. +`astro-useragent` is a port from [next-useragent][next-useragent] to Astro. so big thanks to the contributors behind next-useragent package. [astro-ssr]: https://docs.astro.build/en/guides/server-side-rendering [npm]: https://npmjs.com/package/astro-useragent -[next-useragent]: https://github.com/tokuda109/next-useragent -[tokuda109]: https://github.com/tokuda109 +[next-useragent]: https://github.com/warent/next-useragent diff --git a/packages/astro-useragent/package.json b/packages/astro-useragent/package.json index 4caf48a9..98b069b4 100644 --- a/packages/astro-useragent/package.json +++ b/packages/astro-useragent/package.json @@ -22,30 +22,33 @@ "directory": "packages/astro-useragent" }, "scripts": { - "build": "astro-build --src src/index.ts src/parse.ts src/useUserAgent.ts", - "typecheck": "tsc --declaration --emitDeclarationOnly" + "build": "tsup", + "check-types": "tsc", + "check-exports": "attw --pack . --ignore-rules false-export-default", + "typecheck": "pnpm check-types && pnpm check-exports" }, "type": "module", - "types": "dist/index.d.ts", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", "files": [ "dist" ], - "main": "./dist/index.mjs", "exports": { ".": { "import": { "types": "./dist/index.d.ts", - "default": "./dist/index.mjs" + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" } } }, "dependencies": { "ua-parser-js": "^2.0.0" }, - "devDependencies": { - "astro-build": "workspace:*", - "@types/ua-parser-js": "^0.7.36" - }, "peerDependencies": { "astro": "^4.0.0" } diff --git a/packages/astro-useragent/src/parse.ts b/packages/astro-useragent/src/parse.ts index 43dd7ed6..6a967d72 100644 --- a/packages/astro-useragent/src/parse.ts +++ b/packages/astro-useragent/src/parse.ts @@ -1,73 +1,113 @@ -import { UAParser } from 'ua-parser-js'; +import { + UAParser, + type IBrowser, + type ICPU, + type IDevice, + type IEngine, + type IOS +} from 'ua-parser-js'; + +import { + isAIBot, + isAppleSilicon, + isBot, + isChromeFamily +} from 'ua-parser-js/helpers'; // mostly ported from https://github.com/tokuda109/next-useragent export interface UserAgent { - // The original user agent string. - readonly source: string; + readonly source: string | null; // The original user agent string. + readonly browser: string | null; + readonly browserVersion: number; + readonly cpu: string | null; readonly deviceType: string | null; readonly deviceVendor: string | null; - readonly os: string; - readonly osVersion: number; - readonly browser: string; - readonly browserVersion: number; - readonly engine: string; - readonly engineVersion: number; - readonly isIphone: boolean; - readonly isIpad: boolean; - readonly isMobile: boolean; - readonly isTablet: boolean; - readonly isDesktop: boolean; + readonly engine: string | null; + readonly engineVersion: number | null; + readonly os: string | null; + readonly osVersion: number | null; + readonly isAndroid: boolean; readonly isChrome: boolean; + readonly isChromeOS: boolean; + readonly isDesktop: boolean; + readonly isEdge: boolean; readonly isFirefox: boolean; - readonly isSafari: boolean; readonly isIE: boolean; - readonly isEdge: boolean; - readonly isOpera: boolean; + readonly isIos: boolean; + readonly isIpad: boolean; + readonly isIphone: boolean; readonly isMac: boolean; - readonly isChromeOS: boolean; + readonly isMobile: boolean; + readonly isOpera: boolean; + readonly isSafari: boolean; + readonly isTablet: boolean; readonly isWindows: boolean; - readonly isIos: boolean; - readonly isAndroid: boolean; + readonly isBot: boolean; + readonly isAIBot: boolean; + readonly isChromeFamily: boolean; + readonly isAppleSilicon: boolean; + getUA(): string; + getBrowser(): IBrowser; + getCPU(): ICPU; + getDevice(): IDevice; + getEngine(): IEngine; + getOS(): IOS; } -export const parse = (uastring: string | null): UserAgent => { - const ua = uastring ?? ''; - const result: UAParser.IResult = new UAParser(ua).getResult(); +export const parse = (ua: string | null): UserAgent => { + const uap = new UAParser(ua ?? ''); - const browser: string = result.browser.name || ''; - const deviceType: string = result.device.type || ''; - const os: string = result.os.name || ''; - const engine: string = result.engine.name || ''; - const isMobile: boolean = deviceType === 'mobile'; - const isTablet: boolean = deviceType === 'tablet'; - const isIos: boolean = os === 'iOS'; + const browser = uap.getBrowser(); + const cpu = uap.getCPU(); + const device = uap.getDevice(); + const engine = uap.getEngine(); + const os = uap.getOS(); const userAgent: UserAgent = Object.freeze({ - browser, - deviceType, - os, - engine, - isMobile, - isTablet, - isIos, source: ua, - deviceVendor: result.device.vendor || null, - osVersion: parseInt(result.os.version || '0', 10), - browserVersion: parseFloat(result.browser.version || '0'), - engineVersion: parseFloat(result.engine.version || '0'), - isIphone: isMobile && isIos, - isIpad: isTablet && isIos, - isDesktop: !isMobile && !isTablet, - isChrome: browser === 'Chrome', - isFirefox: browser === 'Firefox', - isSafari: browser === 'Safari', - isIE: browser === 'IE', - isEdge: browser === 'Edge', - isOpera: browser === 'Opera', - isMac: os === 'Mac OS', - isChromeOS: os === 'Chromium OS', - isWindows: os === 'Windows', - isAndroid: os === 'Android' + browser: browser.name || null, + browserVersion: parseFloat(browser.version || '0'), + cpu: cpu.architecture || null, + deviceType: device.type || 'mobile', + deviceVendor: device.vendor || null, + engine: engine.name || null, + engineVersion: parseFloat(engine.version || '0'), + os: os.name || null, + osVersion: parseInt(os.version || '0', 10), + isAndroid: os.is('Android') || os.is('Android-x86'), + isChrome: browser.is('Chrome') || browser.is('Chrome Mobile'), + isChromeOS: os.is('Chrome OS'), + isDesktop: !device.is('mobile') && !device.is('tablet'), + isEdge: browser.is('Edge'), + isFirefox: browser.is('Firefox') || browser.is('Firefox Mobile'), + isIE: browser.is('IE') || browser.is('IEMobile'), + isIos: os.is('iOS'), + isIpad: device.is('tablet') && os.is('iOS'), + isIphone: device.is('mobile') && os.is('iOS'), + isMac: os.is('macOS'), + isMobile: device.is('mobile'), + isOpera: + browser.is('Opera') || + browser.is('Opera GX') || + browser.is('Opera Mini') || + browser.is('Opera Mobi') || + browser.is('Opera Tablet'), + isSafari: browser.is('Safari') || browser.is('Safari Mobile'), + isTablet: device.is('tablet'), + isWindows: + os.is('Windows') || os.is('Windows Phone') || os.is('Windows Mobile'), + isBot: isBot(ua ?? ''), + isAIBot: isAIBot(ua ?? ''), + isChromeFamily: isChromeFamily(ua ?? ''), + isAppleSilicon: isAppleSilicon(ua ?? ''), + + // methods + getBrowser: uap.getBrowser, + getCPU: uap.getCPU, + getDevice: uap.getDevice, + getEngine: uap.getEngine, + getOS: uap.getOS, + getUA: uap.getUA }); return userAgent; diff --git a/packages/astro-useragent/tsconfig.json b/packages/astro-useragent/tsconfig.json index ef8f9f5b..03bc7996 100644 --- a/packages/astro-useragent/tsconfig.json +++ b/packages/astro-useragent/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "astro/tsconfigs/strict", "compilerOptions": { - "noEmit": false, + "noEmit": true, "outDir": "./dist", "allowImportingTsExtensions": false } diff --git a/packages/astro-useragent/tsup.config.ts b/packages/astro-useragent/tsup.config.ts new file mode 100644 index 00000000..8d1c1a24 --- /dev/null +++ b/packages/astro-useragent/tsup.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['esm', 'cjs'], + outDir: 'dist', + clean: true, + dts: true, + minify: true, + sourcemap: true, + splitting: false, + treeshake: true, + platform: 'node' +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 073cc442..396e9b87 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -170,13 +170,6 @@ importers: ua-parser-js: specifier: ^2.0.0 version: 2.0.0 - devDependencies: - '@types/ua-parser-js': - specifier: ^0.7.36 - version: 0.7.39 - astro-build: - specifier: workspace:* - version: link:../astro-build packages/astro-vanilla-extract: dependencies: @@ -1195,9 +1188,6 @@ packages: '@types/node@22.9.0': resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==} - '@types/ua-parser-js@0.7.39': - resolution: {integrity: sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==} - '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -4203,8 +4193,6 @@ snapshots: dependencies: undici-types: 6.19.8 - '@types/ua-parser-js@0.7.39': {} - '@types/unist@3.0.3': {} '@ungap/structured-clone@1.2.0': {}