Skip to content

Commit f6c25a5

Browse files
authored
feat: build-time debug flag with script-lifecycle tracing (#760)
1 parent 6f74a6c commit f6c25a5

5 files changed

Lines changed: 76 additions & 0 deletions

File tree

‎packages/script/src/module.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,14 @@ export default defineNuxtModule<ModuleOptions>({
563563
: undefined,
564564
}asany
565565

566+
// Build-time constant: `__NUXT_SCRIPTS_DEBUG__` is replaced inline by the
567+
// bundler, so debug branches DCE away in production when `debug: false`.
568+
constdebugConst=JSON.stringify(!!config.debug)
569+
nuxt.options.vite||={}
570+
nuxt.options.vite.define={ ...nuxt.options.vite.define,__NUXT_SCRIPTS_DEBUG__: debugConst}
571+
nuxt.options.nitro||={}
572+
nuxt.options.nitro.replace={ ...nuxt.options.nitro.replace,__NUXT_SCRIPTS_DEBUG__: debugConst}
573+
566574
// Register proxy handler unconditionally. The handler rejects unknown domains
567575
// at runtime, so it's safe to register even when no scripts use proxy.
568576
constscriptsBase=config.prefix||'/_scripts'

‎packages/script/src/runtime/composables/useScript.ts‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { injectHead, onNuxtReady, useHead, useNuxtApp, useRuntimeConfig } from '
66
import{markRaw,ref}from'vue'
77
// @ts-expect-error virtual template
88
import{resolveTrigger}from'#build/nuxt-scripts-trigger-resolver'
9+
import{debugEnabled}from'../debug'
910
import{logger}from'../logger'
1011

1112
typeNuxtScriptsApp=ReturnType<typeofuseNuxtApp>&{
@@ -293,6 +294,58 @@ export function useScript<T extends Record<symbol | string, any> = Record<symbol
293294
returnreloaded.load()
294295
}
295296
nuxtApp.$scripts[id]=instance
297+
298+
// Debug logging: emit a structured log per script lifecycle event when debug
299+
// is enabled at build-time (or in dev). Tagged with registryKey when present
300+
// (e.g. `googleTagManager`), else the script id (src/key).
301+
if(import.meta.client&&debugEnabled){
302+
constregistryKey=options?.devtools?.registryKeyasstring|undefined
303+
constsrc=(inputasany)?.src
304+
consttrigger=options?.trigger
305+
constloadedFrom=options?.devtools?.loadedFromasstring|undefined
306+
constctx={
307+
id: instance.id,
308+
...(registryKey ? { registryKey } : {}),
309+
...(src ? { src } : {}),
310+
...(loadedFrom ? { loadedFrom } : {}),
311+
}
312+
constlog=logger.withTag(registryKey||instance.id)
313+
constt0=performance.now()
314+
lettLoadStart=0
315+
log.debug('registered',{
316+
...ctx,
317+
trigger: typeoftrigger==='object' ? (triggerinstanceofPromise ? 'promise' : JSON.stringify(trigger)) : trigger,
318+
})
319+
options.head.hooks.hook('script:updated',(entry)=>{
320+
if(entry.script.id!==instance.id)
321+
return
322+
conststatus=entry.script.status
323+
constelapsed=Math.round(performance.now()-t0)
324+
if(status==='loading')
325+
tLoadStart=performance.now()
326+
constpayload: Record<string,any>={ ...ctx, status,elapsedMs: elapsed}
327+
if(status==='loaded'&&tLoadStart)
328+
payload.loadMs=Math.round(performance.now()-tLoadStart)
329+
constfn=status==='error' ? log.warn : log.debug
330+
fn(`status: ${status}`,payload)
331+
})
332+
const_origLoad=instance.load
333+
instance.load=()=>{
334+
log.debug('load() called',ctx)
335+
return_origLoad()
336+
}
337+
const_origRemove=instance.remove
338+
instance.remove=()=>{
339+
log.debug('remove() called',ctx)
340+
return_origRemove()
341+
}
342+
const_origReload=instance.reload
343+
instance.reload=async()=>{
344+
log.debug('reload() called',ctx)
345+
return_origReload()
346+
}
347+
}
348+
296349
// used for devtools integration
297350
if(import.meta.dev&&import.meta.client){
298351
if(exists){
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declareconst__NUXT_SCRIPTS_DEBUG__: boolean
2+
3+
exportconstdebugEnabled: boolean
4+
=typeof__NUXT_SCRIPTS_DEBUG__!=='undefined'&&__NUXT_SCRIPTS_DEBUG__

‎packages/script/src/runtime/logger.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import{createConsola}from'consola'
2+
import{debugEnabled}from'./debug'
23

34
exportconstlogger=createConsola({
5+
// 4 = debug, 3 = info (consola defaults). Lift the threshold so `logger.debug`
6+
// fires when debug is opted-in at build time or in dev.
7+
level: debugEnabled ? 4 : 3,
48
defaults: {
59
tag: 'nuxt-scripts',
610
},

‎packages/script/src/runtime/types.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,13 @@ export type NuxtUseScriptOptions<T extends Record<symbol | string, any> = {}> =
168168
* @internal
169169
*/
170170
registryMeta?: Record<string,string>
171+
/**
172+
* Source location (file:line:col) the script was registered from, captured
173+
* via dev-only stack-trace parsing in `useRegistryScript`. Surfaced in
174+
* debug logs and Nuxt DevTools.
175+
* @internal
176+
*/
177+
loadedFrom?: string
171178
/**
172179
* Known third-party domains this script communicates with.
173180
* @internal

0 commit comments

Comments
 (0)