From 0d9d56b99f9ebddae1a1f2b84b71005deedadce1 Mon Sep 17 00:00:00 2001 From: Damian Glowala Date: Tue, 29 Sep 2026 19:28:49 +0200 Subject: [PATCH] feat!: remove `defaultUserAgent` option with empty UA fallback --- README.md | 10 +++++----- package.json | 3 --- playground/nuxt.config.ts | 1 - pnpm-lock.yaml | 4 ---- src/module.ts | 11 ++--------- src/runtime/plugin.ts | 14 +++----------- src/types.ts | 13 ------------- test/basic.test.ts | 10 ++++++++++ 8 files changed, 20 insertions(+), 46 deletions(-) delete mode 100644 src/types.ts diff --git a/README.md b/README.md index b6677729..b1194220 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ You can use the following flags to detect the device type: The user agent is also injected and accessible with `$device.userAgent`. +If a request has no `User-Agent` header, `$device.userAgent` is an empty string and the device is treated as a desktop with an unknown OS and browser. + The crawler detection is powered by the [crawler-user-agents](https://github.com/monperrus/crawler-user-agents) package. ## Usage @@ -81,13 +83,11 @@ definePageMeta({ ``` -### Options - -#### `defaultUserAgent` +## Static Generation -Sets the default value for the `user-agent` header (useful when running `npm run generate`). +Static generation (e.g. `nuxt generate`) is not supported. Prerendered pages are rendered once at build time without a user agent, so they are always rendered as a desktop. -Default: `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.39 Safari/537.36` +To detect the device on each request, deploy with `nuxt build` instead. For a client-side only app, set `ssr: false` to detect the device in the browser. ## Amazon CloudFront Support diff --git a/package.json b/package.json index 28644e49..24770c71 100644 --- a/package.json +++ b/package.json @@ -44,9 +44,6 @@ "test": "vitest run", "test:watch": "vitest watch" }, - "dependencies": { - "defu": "^6.1.7" - }, "devDependencies": { "@nuxt/devtools": "latest", "@nuxt/eslint-config": "^1.17.0", diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts index 5223f1ba..4408ff03 100644 --- a/playground/nuxt.config.ts +++ b/playground/nuxt.config.ts @@ -5,5 +5,4 @@ export default defineNuxtConfig({ compatibilityVersion: 5, }, compatibilityDate: '2026-09-29', - device: {}, }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6446e8ee..07c21ff6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -165,10 +165,6 @@ settings: importers: .: - dependencies: - defu: - specifier: ^6.1.7 - version: 6.1.7 devDependencies: '@nuxt/devtools': specifier: latest diff --git a/src/module.ts b/src/module.ts index a9b5f5e8..9bfb9eed 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,23 +1,16 @@ -import { defu } from 'defu' import { defineNuxtModule, addPlugin, addImportsDir, createResolver, addTemplate } from '@nuxt/kit' import crawlers from 'crawler-user-agents' with { type: 'json' } import { name, version } from '../package.json' -import type { ModuleOptions } from './types' -export default defineNuxtModule({ +export default defineNuxtModule({ meta: { name, - configKey: 'device', compatibility: { nuxt: '>=4.0.0', }, version, }, - setup(options, nuxt) { - nuxt.options.runtimeConfig.public.device = defu(nuxt.options.runtimeConfig.public.device, { - defaultUserAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.39 Safari/537.36', - }) - + setup() { const { resolve } = createResolver(import.meta.url) addPlugin(resolve('./runtime/plugin')) diff --git a/src/runtime/plugin.ts b/src/runtime/plugin.ts index 8adf3217..4d75c720 100644 --- a/src/runtime/plugin.ts +++ b/src/runtime/plugin.ts @@ -1,26 +1,18 @@ import type { Plugin } from 'nuxt/app' import type { Device } from './types' import generateFlags from './generateFlags' -import { defineNuxtPlugin, reactive, useRequestHeaders, useRuntimeConfig } from '#imports' +import { defineNuxtPlugin, reactive, useRequestHeaders } from '#imports' const plugin: Plugin<{ device: Device }> = defineNuxtPlugin(() => { - const runtimeConfig = useRuntimeConfig() - - const defaultUserAgent = runtimeConfig.public.device.defaultUserAgent - let flags: Device if (import.meta.server) { const headers = useRequestHeaders() - const userAgent = headers['user-agent'] || defaultUserAgent - - flags = reactive(generateFlags(userAgent, headers)) + flags = reactive(generateFlags(headers['user-agent'] || '', headers)) } else { - const userAgent = navigator.userAgent || defaultUserAgent - - flags = reactive(generateFlags(userAgent)) + flags = reactive(generateFlags(navigator.userAgent)) } return { diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index 1c2b97a3..00000000 --- a/src/types.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface ModuleOptions { - /** - * Sets the default value for the `user-agent` header (useful when running `npm run generate`). - * @default 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.39 Safari/537.36' - */ - defaultUserAgent?: string -} - -declare module '@nuxt/schema' { - interface PublicRuntimeConfig { - device: Required - } -} diff --git a/test/basic.test.ts b/test/basic.test.ts index fcecd04c..7d15d03a 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -61,6 +61,16 @@ describe('ssr', async () => { expect(html).toContain('isCrawler') }) + it('treats an empty user agent as a desktop', async () => { + const html = await $fetch('/', { + headers: { + 'User-Agent': '', + }, + }) + + expect(parseHtml(html)).toEqual({ isDesktop: true, isMobile: false, isTablet: false, isMobileOrTablet: false, isDesktopOrTablet: true, isIos: false, isWindows: false, isMacOS: false, isLinux: false, isApple: false, isAndroid: false, isFirefox: false, isEdge: false, isChrome: false, isSafari: false, isSamsung: false, isCrawler: false }) + }) + describe('detects device', () => { it('Samsung Galaxy S9', async () => { const userAgent = 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36'