From 45fd57623165257c45fb74b18b5b7d826fd95691 Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Fri, 6 Mar 2026 14:08:09 +1100 Subject: [PATCH 1/3] fix: allow arbitrary attributes on script tags without type errors Closes #292 --- src/runtime/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/types.ts b/src/runtime/types.ts index a46b57b6..1ce425f1 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -122,7 +122,7 @@ export type NuxtUseScriptOptions = {}> = export type NuxtUseScriptOptionsSerializable = Omit & { trigger?: 'client' | 'server' | 'onNuxtReady' | { idleTimeout: number } | { interaction: string[] } | { serviceWorker: true } } -export type NuxtUseScriptInput = UseScriptInput +export type NuxtUseScriptInput = UseScriptInput & Record export interface TrackedPage { title?: string From a73cec8298ad6c688a0cdf570332e14160097249 Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Fri, 6 Mar 2026 15:40:43 +1100 Subject: [PATCH 2/3] fix: wire NuxtUseScriptInput through useScript and utils public APIs Replace UseScriptInput with NuxtUseScriptInput in parameter types so arbitrary script attributes are accepted by the public API. --- src/runtime/composables/useScript.ts | 6 +++--- src/runtime/utils.ts | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/runtime/composables/useScript.ts b/src/runtime/composables/useScript.ts index 28e82df8..469a53d3 100644 --- a/src/runtime/composables/useScript.ts +++ b/src/runtime/composables/useScript.ts @@ -1,5 +1,5 @@ -import type { UseScriptInput, UseScriptOptions, VueScriptInstance } from '@unhead/vue/scripts' -import type { NuxtDevToolsScriptInstance, NuxtUseScriptOptions, UseFunctionType, UseScriptContext } from '../types' +import type { UseScriptOptions, VueScriptInstance } from '@unhead/vue/scripts' +import type { NuxtDevToolsScriptInstance, NuxtUseScriptInput, NuxtUseScriptOptions, UseFunctionType, UseScriptContext } from '../types' // @ts-expect-error virtual template import { resolveTrigger } from '#build/nuxt-scripts-trigger-resolver' import { useScript as _useScript } from '@unhead/vue/scripts' @@ -32,7 +32,7 @@ export function resolveScriptKey(input: any): string { return input.key || input.src || (typeof input.innerHTML === 'string' ? input.innerHTML : '') } -export function useScript = Record>(input: UseScriptInput, options?: NuxtUseScriptOptions): UseScriptContext, T>> { +export function useScript = Record>(input: NuxtUseScriptInput, options?: NuxtUseScriptOptions): UseScriptContext, T>> { input = typeof input === 'string' ? { src: input } : input options = defu(options, useNuxtScriptRuntimeConfig()?.defaultScriptOptions) as NuxtUseScriptOptions diff --git a/src/runtime/utils.ts b/src/runtime/utils.ts index 1e22e9b5..55d91bf8 100644 --- a/src/runtime/utils.ts +++ b/src/runtime/utils.ts @@ -1,13 +1,13 @@ import type { EmptyOptionsSchema, InferIfSchema, + NuxtUseScriptInput, NuxtUseScriptOptions, RegistryScriptInput, ScriptRegistry, UseFunctionType, UseScriptContext, } from '#nuxt-scripts/types' -import type { UseScriptInput } from '@unhead/vue' import type { GenericSchema, InferInput, ObjectSchema, UnionSchema, ValiError } from 'valibot' import { parse } from '#nuxt-scripts-validator' import { defu } from 'defu' @@ -30,8 +30,8 @@ function validateScriptInputSchema(key: string, schema: return null } -type OptionsFn = (options: InferIfSchema, ctx: { scriptInput?: UseScriptInput & { src?: string } }) => ({ - scriptInput?: UseScriptInput +type OptionsFn = (options: InferIfSchema, ctx: { scriptInput?: NuxtUseScriptInput & { src?: string } }) => ({ + scriptInput?: NuxtUseScriptInput scriptOptions?: NuxtUseScriptOptions schema?: O extends ObjectSchema | UnionSchema ? O : undefined clientInit?: () => void | Promise @@ -45,7 +45,7 @@ export function scriptRuntimeConfig(key: T) { export function useRegistryScript, O = EmptyOptionsSchema>(registryKey: keyof ScriptRegistry | string, optionsFn: OptionsFn, _userOptions?: RegistryScriptInput): UseScriptContext, T>> { const scriptConfig = scriptRuntimeConfig(registryKey as keyof ScriptRegistry) const userOptions = Object.assign(_userOptions || {}, typeof scriptConfig === 'object' ? scriptConfig : {}) - const options = optionsFn(userOptions as InferIfSchema, { scriptInput: userOptions.scriptInput as UseScriptInput & { src?: string } }) + const options = optionsFn(userOptions as InferIfSchema, { scriptInput: userOptions.scriptInput as NuxtUseScriptInput & { src?: string } }) // NEW: Handle NPM-only scripts differently if (options.scriptMode === 'npm') { @@ -82,7 +82,7 @@ export function useRegistryScript, O = Em } } - const scriptInput = defu(finalScriptInput, userOptions.scriptInput, { key: registryKey }) as any as UseScriptInput + const scriptInput = defu(finalScriptInput, userOptions.scriptInput, { key: registryKey }) as any as NuxtUseScriptInput const scriptOptions = Object.assign(userOptions?.scriptOptions || {}, options.scriptOptions || {}) if (import.meta.dev) { // Capture where the component was loaded from From f732ffa5dc0b2571fdf8f2de3741489bdc7bbf0f Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Fri, 6 Mar 2026 15:49:13 +1100 Subject: [PATCH 3/3] fix: restrict arbitrary attributes to serializable values Only allow string | boolean | number | undefined for custom attributes, matching what HTML attributes actually accept. --- src/runtime/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/types.ts b/src/runtime/types.ts index 1ce425f1..23968389 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -122,7 +122,7 @@ export type NuxtUseScriptOptions = {}> = export type NuxtUseScriptOptionsSerializable = Omit & { trigger?: 'client' | 'server' | 'onNuxtReady' | { idleTimeout: number } | { interaction: string[] } | { serviceWorker: true } } -export type NuxtUseScriptInput = UseScriptInput & Record +export type NuxtUseScriptInput = UseScriptInput & Record export interface TrackedPage { title?: string