Skip to content

Commit c9e1a20

Browse files
committed
fix(devtools): show validation errors
1 parent 22d91c5 commit c9e1a20

File tree

4 files changed

+31
-8
lines changed

4 files changed

+31
-8
lines changed

client/app.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,12 @@ function viewDocs(docs: string) {
245245
<div v-else-if="event.status === 'loading'" class="font-bold px-2 py-[2px] bg-yellow-100 text-yellow-700 rounded-lg">
246246
{{ event.status }}
247247
</div>
248+
<div v-else-if="event.status === 'validation-failed'" class="flex items-center gap-2">
249+
<div class="font-bold px-2 py-[2px] bg-purple-100 text-purple-700 rounded-lg">
250+
{{ event.status }}
251+
</div>
252+
{{ event.args.issues.map(i => `${key}.${i.path?.map(i => i.key).join(',')}: ${i.message}`).join(',') }}
253+
</div>
248254
</template>
249255
<div v-else-if="event.type === 'fn-call' && event.args" class="px-2 py-[2px] bg-gray-100 text-gray-700 rounded-lg font-mono">
250256
{{ `${event.fn}(${event.args?.map((a: any) => JSON.stringify(a, null, 2)).join(', ') || ''})` }}

src/runtime/composables/useScript.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ export function useScript<T extends Record<symbol | string, any> = Record<symbol
8989
syncScripts()
9090
})
9191
payload.$script = instance
92+
const err = options._validate?.()
93+
if (err) {
94+
payload.events.push({
95+
type: 'status',
96+
status: 'validation-failed',
97+
args: err,
98+
at: Date.now(),
99+
})
100+
}
92101
payload.events.push({
93102
type: 'status',
94103
status: 'awaitingLoad',

src/runtime/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type {
33
} from '@unhead/vue/types'
44
import type { UseScriptInput, VueScriptInstance, UseScriptOptions } from '@unhead/vue'
55
import type { ComputedRef, Ref } from 'vue'
6-
import type { InferInput, ObjectSchema } from 'valibot'
6+
import type {InferInput, ObjectSchema, ValiError} from 'valibot'
77
import type { Import } from 'unimport'
88
import type { SegmentInput } from './registry/segment'
99
import type { CloudflareWebAnalyticsInput } from './registry/cloudflare-web-analytics'
@@ -86,6 +86,10 @@ export type NuxtUseScriptOptions<T extends Record<symbol | string, any> = {}> =
8686
*/
8787
registryMeta?: Record<string, string>
8888
}
89+
/**
90+
* @internal Used to run custom validation logic in dev mode.
91+
*/
92+
_validate?: () => ValiError<any> | null | undefined
8993
}
9094

9195
export type NuxtUseScriptOptionsSerializable = Omit<NuxtUseScriptOptions, 'use' | 'skipValidation' | 'stub' | 'trigger' | 'eventContext' | 'beforeInit'> & { trigger?: 'client' | 'server' | 'onNuxtReady' }

src/runtime/utils.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ function validateScriptInputSchema<T extends GenericSchema>(key: string, schema:
2222
}
2323
catch (_e) {
2424
const e = _e as ValiError<any>
25-
// TODO nicer error handling
26-
// @ts-expect-error untyped?
2725
console.error(e.issues.map(i => `${key}.${i.path?.map(i => i.key).join(',')}: ${i.message}`).join('\n'))
26+
return e
2827
}
2928
}
29+
return null
3030
}
3131

3232
type OptionsFn<O> = (options: InferIfSchema<O>) => ({
@@ -60,14 +60,18 @@ export function useRegistryScript<T extends Record<string | symbol, any>, O = Em
6060
}
6161
}
6262
const init = scriptOptions.beforeInit
63-
scriptOptions.beforeInit = () => {
64-
// a manual trigger also means it was disabled by nuxt.config
65-
if (import.meta.dev && !scriptOptions.skipValidation && options.schema) {
63+
64+
if (import.meta.dev) {
65+
scriptOptions._validate = () => {
66+
// a manual trigger also means it was disabled by nuxt.config
6667
// overriding the src will skip validation
67-
if (!userOptions.scriptInput?.src) {
68-
validateScriptInputSchema(registryKey, options.schema, userOptions)
68+
if (!userOptions.scriptInput?.src && !scriptOptions.skipValidation && options.schema) {
69+
return validateScriptInputSchema(registryKey, options.schema, userOptions)
6970
}
7071
}
72+
}
73+
// wrap beforeInit to add validation
74+
scriptOptions.beforeInit = () => {
7175
// avoid clearing the user beforeInit
7276
init?.()
7377
if (import.meta.client) {

0 commit comments

Comments
 (0)