refactor(nuxt): enable strict type checking (#6368)

Co-authored-by: Pooya Parsa <pooya@pi0.io>
This commit is contained in:
Anthony Fu 2022-08-13 01:47:58 +08:00 committed by GitHub
parent cb98c8b921
commit f350a70775
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 235 additions and 201 deletions

View File

@ -7,7 +7,7 @@ import { NuxtConfigSchema } from '@nuxt/schema'
export interface LoadNuxtConfigOptions extends LoadConfigOptions<NuxtConfig> {} export interface LoadNuxtConfigOptions extends LoadConfigOptions<NuxtConfig> {}
export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<NuxtOptions> { export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<NuxtOptions> {
const { config: nuxtConfig, configFile, layers, cwd } = await loadConfig({ const { config: nuxtConfig, configFile, layers, cwd } = await loadConfig<NuxtConfig>({
name: 'nuxt', name: 'nuxt',
configFile: 'nuxt.config', configFile: 'nuxt.config',
rcFile: '.nuxtrc', rcFile: '.nuxtrc',
@ -23,6 +23,7 @@ export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<Nuxt
// Resolve `rootDir` & `srcDir` of layers // Resolve `rootDir` & `srcDir` of layers
for (const layer of layers) { for (const layer of layers) {
layer.config = layer.config || {}
layer.config.rootDir = layer.config.rootDir ?? layer.cwd layer.config.rootDir = layer.config.rootDir ?? layer.cwd
layer.config.srcDir = resolve(layer.config.rootDir, layer.config.srcDir) layer.config.srcDir = resolve(layer.config.rootDir, layer.config.srcDir)
} }

View File

@ -2,19 +2,19 @@ export * from 'vue'
export const install = () => {} export const install = () => {}
export function set (target, key, val) { export function set (target: any, key: string | number | symbol, val: any) {
if (Array.isArray(target)) { if (Array.isArray(target)) {
target.length = Math.max(target.length, key) target.length = Math.max(target.length, key as number)
target.splice(key, 1, val) target.splice(key as number, 1, val)
return val return val
} }
target[key] = val target[key] = val
return val return val
} }
export function del (target, key) { export function del (target: any, key: string | number | symbol) {
if (Array.isArray(target)) { if (Array.isArray(target)) {
target.splice(key, 1) target.splice(key as number, 1)
return return
} }
delete target[key] delete target[key]

View File

@ -8,7 +8,7 @@ export default defineComponent({
} }
}, },
setup (_props, { slots, emit }) { setup (_props, { slots, emit }) {
const error = ref(null) const error = ref<Error | null>(null)
const nuxtApp = useNuxtApp() const nuxtApp = useNuxtApp()
onErrorCaptured((err) => { onErrorCaptured((err) => {

View File

@ -1,10 +1,10 @@
import { defineComponent, h, resolveComponent, PropType, computed, DefineComponent } from 'vue' import { defineComponent, h, resolveComponent, PropType, computed, DefineComponent, ComputedRef } from 'vue'
import { RouteLocationRaw, Router } from 'vue-router' import { RouteLocationRaw } from 'vue-router'
import { hasProtocol } from 'ufo' import { hasProtocol } from 'ufo'
import { navigateTo, useRouter } from '#app' import { navigateTo, useRouter } from '#app'
const firstNonUndefined = <T>(...args: T[]): T => args.find(arg => arg !== undefined) const firstNonUndefined = <T>(...args: (T | undefined)[]) => args.find(arg => arg !== undefined)
const DEFAULT_EXTERNAL_REL_ATTRIBUTE = 'noopener noreferrer' const DEFAULT_EXTERNAL_REL_ATTRIBUTE = 'noopener noreferrer'
@ -24,8 +24,8 @@ export type NuxtLinkProps = {
custom?: boolean custom?: boolean
// Attributes // Attributes
target?: string target?: string | null
rel?: string rel?: string | null
noRel?: boolean noRel?: boolean
// Styling // Styling
@ -39,7 +39,7 @@ export type NuxtLinkProps = {
export function defineNuxtLink (options: NuxtLinkOptions) { export function defineNuxtLink (options: NuxtLinkOptions) {
const componentName = options.componentName || 'NuxtLink' const componentName = options.componentName || 'NuxtLink'
const checkPropConflicts = (props: NuxtLinkProps, main: string, sub: string): void => { const checkPropConflicts = (props: NuxtLinkProps, main: keyof NuxtLinkProps, sub: keyof NuxtLinkProps): void => {
if (process.dev && props[main] !== undefined && props[sub] !== undefined) { if (process.dev && props[main] !== undefined && props[sub] !== undefined) {
console.warn(`[${componentName}] \`${main}\` and \`${sub}\` cannot be used together. \`${sub}\` will be ignored.`) console.warn(`[${componentName}] \`${main}\` and \`${sub}\` cannot be used together. \`${sub}\` will be ignored.`)
} }
@ -116,10 +116,10 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
} }
}, },
setup (props, { slots }) { setup (props, { slots }) {
const router = useRouter() as Router | undefined const router = useRouter()
// Resolving `to` value from `to` and `href` props // Resolving `to` value from `to` and `href` props
const to = computed<string | RouteLocationRaw>(() => { const to: ComputedRef<string | RouteLocationRaw> = computed(() => {
checkPropConflicts(props, 'to', 'href') checkPropConflicts(props, 'to', 'href')
return props.to || props.href || '' // Defaults to empty string (won't render any `href` attribute) return props.to || props.href || '' // Defaults to empty string (won't render any `href` attribute)
@ -127,7 +127,7 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
// Resolving link type // Resolving link type
const isExternal = computed<boolean>(() => { const isExternal = computed<boolean>(() => {
// External prop is explictly set // External prop is explicitly set
if (props.external) { if (props.external) {
return true return true
} }
@ -180,11 +180,13 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
// https://router.vuejs.org/api/#custom // https://router.vuejs.org/api/#custom
if (props.custom) { if (props.custom) {
if (!slots.default) { return null } if (!slots.default) {
return null
}
return slots.default({ return slots.default({
href, href,
navigate, navigate,
route: router.resolve(href), route: router.resolve(href!),
rel, rel,
target, target,
isActive: false, isActive: false,

View File

@ -1,11 +1,11 @@
import { h } from 'vue' import { defineComponent, h } from 'vue'
import type { Component } from 'vue' import type { Component } from 'vue'
const Fragment = { const Fragment = defineComponent({
setup (_props, { slots }) { setup (_props, { slots }) {
return () => slots.default?.() return () => slots.default?.()
} }
} })
/** /**
* Internal utility * Internal utility

View File

@ -1,6 +1,5 @@
import { onBeforeMount, onServerPrefetch, onUnmounted, ref, getCurrentInstance, watch, unref } from 'vue' import { onBeforeMount, onServerPrefetch, onUnmounted, ref, getCurrentInstance, watch, unref } from 'vue'
import type { Ref, WatchSource } from 'vue' import type { Ref, WatchSource } from 'vue'
import { wrapInRef } from './utils'
import { NuxtApp, useNuxtApp } from '#app' import { NuxtApp, useNuxtApp } from '#app'
export type _Transform<Input = any, Output = any> = (input: Input) => Output export type _Transform<Input = any, Output = any> = (input: Input) => Output
@ -25,7 +24,7 @@ export interface AsyncDataOptions<
> { > {
server?: boolean server?: boolean
lazy?: boolean lazy?: boolean
default?: () => DataT | Ref<DataT> default?: () => DataT | Ref<DataT> | null
transform?: Transform transform?: Transform
pick?: PickKeys pick?: PickKeys
watch?: MultiWatchSources watch?: MultiWatchSources
@ -37,10 +36,10 @@ export interface RefreshOptions {
} }
export interface _AsyncData<DataT, ErrorT> { export interface _AsyncData<DataT, ErrorT> {
data: Ref<DataT> data: Ref<DataT | null>
pending: Ref<boolean> pending: Ref<boolean>
refresh: (opts?: RefreshOptions) => Promise<void> refresh: (opts?: RefreshOptions) => Promise<void>
error: Ref<ErrorT> error: Ref<ErrorT | null>
} }
export type AsyncData<Data, Error> = _AsyncData<Data, Error> & Promise<_AsyncData<Data, Error>> export type AsyncData<Data, Error> = _AsyncData<Data, Error> & Promise<_AsyncData<Data, Error>>
@ -70,7 +69,7 @@ export function useAsyncData<
DataE = Error, DataE = Error,
Transform extends _Transform<DataT> = _Transform<DataT, DataT>, Transform extends _Transform<DataT> = _Transform<DataT, DataT>,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform> PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>
> (...args): AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, DataE | null | true> { > (...args: any[]): AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, DataE | null | true> {
const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined
if (typeof args[0] !== 'string') { args.unshift(autoKey) } if (typeof args[0] !== 'string') { args.unshift(autoKey) }
@ -102,7 +101,8 @@ export function useAsyncData<
// Setup hook callbacks once per instance // Setup hook callbacks once per instance
const instance = getCurrentInstance() const instance = getCurrentInstance()
if (instance && !instance._nuxtOnBeforeMountCbs) { if (instance && !instance._nuxtOnBeforeMountCbs) {
const cbs = instance._nuxtOnBeforeMountCbs = [] instance._nuxtOnBeforeMountCbs = []
const cbs = instance._nuxtOnBeforeMountCbs
if (instance && process.client) { if (instance && process.client) {
onBeforeMount(() => { onBeforeMount(() => {
cbs.forEach((cb) => { cb() }) cbs.forEach((cb) => { cb() })
@ -115,7 +115,7 @@ export function useAsyncData<
const useInitialCache = () => options.initialCache && nuxt.payload.data[key] !== undefined const useInitialCache = () => options.initialCache && nuxt.payload.data[key] !== undefined
const asyncData = { const asyncData = {
data: wrapInRef(nuxt.payload.data[key] ?? options.default()), data: ref(nuxt.payload.data[key] ?? options.default?.() ?? null),
pending: ref(!useInitialCache()), pending: ref(!useInitialCache()),
error: ref(nuxt.payload._errors[key] ?? null) error: ref(nuxt.payload._errors[key] ?? null)
} as AsyncData<DataT, DataE> } as AsyncData<DataT, DataE>
@ -151,7 +151,7 @@ export function useAsyncData<
}) })
.catch((error: any) => { .catch((error: any) => {
asyncData.error.value = error asyncData.error.value = error
asyncData.data.value = unref(options.default()) asyncData.data.value = unref(options.default?.() ?? null)
}) })
.finally(() => { .finally(() => {
asyncData.pending.value = false asyncData.pending.value = false
@ -230,7 +230,7 @@ export function useLazyAsyncData<
DataE = Error, DataE = Error,
Transform extends _Transform<DataT> = _Transform<DataT, DataT>, Transform extends _Transform<DataT> = _Transform<DataT, DataT>,
PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform> PickKeys extends KeyOfRes<Transform> = KeyOfRes<Transform>
> (...args): AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, DataE | null | true> { > (...args: any[]): AsyncData<PickFrom<ReturnType<Transform>, PickKeys>, DataE | null | true> {
const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined
if (typeof args[0] !== 'string') { args.unshift(autoKey) } if (typeof args[0] !== 'string') { args.unshift(autoKey) }
const [key, handler, options] = args as [string, (ctx?: NuxtApp) => Promise<DataT>, AsyncDataOptions<DataT, Transform, PickKeys>] const [key, handler, options] = args as [string, (ctx?: NuxtApp) => Promise<DataT>, AsyncDataOptions<DataT, Transform, PickKeys>]
@ -249,7 +249,7 @@ export function refreshNuxtData (keys?: string | string[]): Promise<void> {
function pick (obj: Record<string, any>, keys: string[]) { function pick (obj: Record<string, any>, keys: string[]) {
const newObj = {} const newObj = {}
for (const key of keys) { for (const key of keys) {
newObj[key] = obj[key] (newObj as any)[key] = obj[key]
} }
return newObj return newObj
} }

View File

@ -9,8 +9,8 @@ export const NuxtComponentIndicator = '__nuxt_component'
async function runLegacyAsyncData (res: Record<string, any> | Promise<Record<string, any>>, fn: (nuxtApp: NuxtApp) => Promise<Record<string, any>>) { async function runLegacyAsyncData (res: Record<string, any> | Promise<Record<string, any>>, fn: (nuxtApp: NuxtApp) => Promise<Record<string, any>>) {
const nuxt = useNuxtApp() const nuxt = useNuxtApp()
const route = useRoute() const route = useRoute()
const vm = getCurrentInstance() const vm = getCurrentInstance()!
const { fetchKey } = vm.proxy.$options const { fetchKey } = vm.proxy!.$options
const key = typeof fetchKey === 'function' ? fetchKey(() => '') : fetchKey || route.fullPath const key = typeof fetchKey === 'function' ? fetchKey(() => '') : fetchKey || route.fullPath
const { data } = await useAsyncData(`options:asyncdata:${key}`, () => fn(nuxt)) const { data } = await useAsyncData(`options:asyncdata:${key}`, () => fn(nuxt))
if (data.value && typeof data.value === 'object') { if (data.value && typeof data.value === 'object') {
@ -38,8 +38,7 @@ export const defineNuxtComponent: typeof defineComponent =
setup (props, ctx) { setup (props, ctx) {
const res = setup?.(props, ctx) || {} const res = setup?.(props, ctx) || {}
let promises: unknown[] | undefined = [] const promises: Promise<any>[] = []
promises = promises || []
if (options.asyncData) { if (options.asyncData) {
promises.push(runLegacyAsyncData(res, options.asyncData)) promises.push(runLegacyAsyncData(res, options.asyncData))
} }
@ -49,7 +48,6 @@ export const defineNuxtComponent: typeof defineComponent =
.then(() => res) .then(() => res)
.finally(() => { .finally(() => {
promises.length = 0 promises.length = 0
promises = null
}) })
} }
} as DefineComponent } as DefineComponent

View File

@ -1,11 +1,10 @@
import { Ref, watch } from 'vue' import { ref, Ref, watch } from 'vue'
import { parse, serialize, CookieParseOptions, CookieSerializeOptions } from 'cookie-es' import { parse, serialize, CookieParseOptions, CookieSerializeOptions } from 'cookie-es'
import { appendHeader } from 'h3' import { appendHeader } from 'h3'
import type { CompatibilityEvent } from 'h3' import type { CompatibilityEvent } from 'h3'
import destr from 'destr' import destr from 'destr'
import { isEqual } from 'ohash' import { isEqual } from 'ohash'
import { useRequestEvent } from './ssr' import { useRequestEvent } from './ssr'
import { wrapInRef } from './utils'
import { useNuxtApp } from '#app' import { useNuxtApp } from '#app'
type _CookieOptions = Omit<CookieSerializeOptions & CookieParseOptions, 'decode' | 'encode'> type _CookieOptions = Omit<CookieSerializeOptions & CookieParseOptions, 'decode' | 'encode'>
@ -26,9 +25,9 @@ const CookieDefaults: CookieOptions<any> = {
export function useCookie <T = string> (name: string, _opts?: CookieOptions<T>): CookieRef<T> { export function useCookie <T = string> (name: string, _opts?: CookieOptions<T>): CookieRef<T> {
const opts = { ...CookieDefaults, ..._opts } const opts = { ...CookieDefaults, ..._opts }
const cookies = readRawCookies(opts) const cookies = readRawCookies(opts) || {}
const cookie = wrapInRef<T>(cookies[name] ?? opts.default?.()) const cookie = ref<T | undefined>(cookies[name] as any ?? opts.default?.())
if (process.client) { if (process.client) {
watch(cookie, () => { writeClientCookie(name, cookie.value, opts as CookieSerializeOptions) }) watch(cookie, () => { writeClientCookie(name, cookie.value, opts as CookieSerializeOptions) })
@ -46,7 +45,7 @@ export function useCookie <T=string> (name: string, _opts?: CookieOptions<T>): C
return cookie as CookieRef<T> return cookie as CookieRef<T>
} }
function readRawCookies (opts: CookieOptions = {}): Record<string, string> { function readRawCookies (opts: CookieOptions = {}): Record<string, string> | undefined {
if (process.server) { if (process.server) {
return parse(useRequestEvent()?.req.headers.cookie || '', opts) return parse(useRequestEvent()?.req.headers.cookie || '', opts)
} else if (process.client) { } else if (process.client) {

View File

@ -37,7 +37,7 @@ export function useFetch<
arg1?: string | UseFetchOptions<_ResT, Transform, PickKeys>, arg1?: string | UseFetchOptions<_ResT, Transform, PickKeys>,
arg2?: string arg2?: string
) { ) {
const [opts, autoKey] = typeof arg1 === 'string' ? [{}, arg1] : [arg1, arg2] const [opts = {}, autoKey] = typeof arg1 === 'string' ? [{}, arg1] : [arg1, arg2]
const _key = opts.key || autoKey const _key = opts.key || autoKey
if (!_key || typeof _key !== 'string') { if (!_key || typeof _key !== 'string') {
throw new TypeError('[nuxt] [useFetch] key must be a string: ' + _key) throw new TypeError('[nuxt] [useFetch] key must be a string: ' + _key)

View File

@ -61,7 +61,7 @@ export interface NavigateToOptions {
redirectCode?: number redirectCode?: number
} }
export const navigateTo = (to: RouteLocationRaw, options: NavigateToOptions = {}): Promise<void | NavigationFailure> | RouteLocationRaw => { export const navigateTo = (to: RouteLocationRaw | undefined | null, options: NavigateToOptions = {}): Promise<void | NavigationFailure> | RouteLocationRaw => {
if (!to) { if (!to) {
to = '/' to = '/'
} }
@ -74,7 +74,7 @@ export const navigateTo = (to: RouteLocationRaw, options: NavigateToOptions = {}
const nuxtApp = useNuxtApp() const nuxtApp = useNuxtApp()
if (nuxtApp.ssrContext && nuxtApp.ssrContext.event) { if (nuxtApp.ssrContext && nuxtApp.ssrContext.event) {
const redirectLocation = joinURL(useRuntimeConfig().app.baseURL, router.resolve(to).fullPath || '/') const redirectLocation = joinURL(useRuntimeConfig().app.baseURL, router.resolve(to).fullPath || '/')
return nuxtApp.callHook('app:redirected').then(() => sendRedirect(nuxtApp.ssrContext.event, redirectLocation, options.redirectCode || 302)) return nuxtApp.callHook('app:redirected').then(() => sendRedirect(nuxtApp.ssrContext!.event, redirectLocation, options.redirectCode || 302))
} }
} }
// Client-side redirection using vue-router // Client-side redirection using vue-router

View File

@ -5,9 +5,9 @@ import { NuxtApp } from '#app/nuxt'
export function useRequestHeaders<K extends string = string> (include: K[]): Record<K, string | undefined> export function useRequestHeaders<K extends string = string> (include: K[]): Record<K, string | undefined>
export function useRequestHeaders (): Readonly<Record<string, string | undefined>> export function useRequestHeaders (): Readonly<Record<string, string | undefined>>
export function useRequestHeaders (include?) { export function useRequestHeaders (include?: any[]) {
if (process.client) { return {} } if (process.client) { return {} }
const headers: Record<string, string | string[]> = useNuxtApp().ssrContext?.event.req.headers ?? {} const headers = useNuxtApp().ssrContext?.event.req.headers ?? {}
if (!include) { return headers } if (!include) { return headers }
return Object.fromEntries(include.filter(key => headers[key]).map(key => [key, headers[key]])) return Object.fromEntries(include.filter(key => headers[key]).map(key => [key, headers[key]]))
} }

View File

@ -10,7 +10,7 @@ import { useNuxtApp } from '#app'
*/ */
export function useState <T> (key?: string, init?: (() => T | Ref<T>)): Ref<T> export function useState <T> (key?: string, init?: (() => T | Ref<T>)): Ref<T>
export function useState <T> (init?: (() => T | Ref<T>)): Ref<T> export function useState <T> (init?: (() => T | Ref<T>)): Ref<T>
export function useState <T> (...args): Ref<T> { export function useState <T> (...args: any): Ref<T> {
const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined
if (typeof args[0] !== 'string') { args.unshift(autoKey) } if (typeof args[0] !== 'string') { args.unshift(autoKey) }
const [_key, init] = args as [string, (() => T | Ref<T>)] const [_key, init] = args as [string, (() => T | Ref<T>)]

View File

@ -1,3 +0,0 @@
import { isRef, ref, Ref } from 'vue'
export const wrapInRef = <T> (value: T | Ref<T>) => isRef(value) ? value : ref(value)

View File

@ -35,7 +35,7 @@ if (process.server) {
await nuxt.hooks.callHook('app:created', vueApp) await nuxt.hooks.callHook('app:created', vueApp)
} catch (err) { } catch (err) {
await nuxt.callHook('app:error', err) await nuxt.callHook('app:error', err)
nuxt.payload.error = nuxt.payload.error || err nuxt.payload.error = (nuxt.payload.error || err) as any
} }
return vueApp return vueApp
@ -66,7 +66,7 @@ if (process.client) {
await applyPlugins(nuxt, plugins) await applyPlugins(nuxt, plugins)
} catch (err) { } catch (err) {
await nuxt.callHook('app:error', err) await nuxt.callHook('app:error', err)
nuxt.payload.error = nuxt.payload.error || err nuxt.payload.error = (nuxt.payload.error || err) as any
} }
try { try {
@ -77,11 +77,11 @@ if (process.client) {
await nextTick() await nextTick()
} catch (err) { } catch (err) {
await nuxt.callHook('app:error', err) await nuxt.callHook('app:error', err)
nuxt.payload.error = nuxt.payload.error || err nuxt.payload.error = (nuxt.payload.error || err) as any
} }
} }
entry().catch((error) => { entry().catch((error: unknown) => {
console.error('Error while mounting app:', error) // eslint-disable-line no-console console.error('Error while mounting app:', error) // eslint-disable-line no-console
}) })
} }

View File

@ -38,19 +38,7 @@ export interface RuntimeNuxtHooks {
'vue:error': (...args: Parameters<Parameters<typeof onErrorCaptured>[0]>) => HookResult 'vue:error': (...args: Parameters<Parameters<typeof onErrorCaptured>[0]>) => HookResult
} }
interface _NuxtApp { export interface NuxtSSRContext extends SSRContext {
vueApp: App<Element>
globalName: string
hooks: Hookable<RuntimeNuxtHooks>
hook: _NuxtApp['hooks']['hook']
callHook: _NuxtApp['hooks']['callHook']
[key: string]: any
_asyncDataPromises?: Record<string, Promise<any>>
ssrContext?: SSRContext & {
url: string url: string
event: CompatibilityEvent event: CompatibilityEvent
/** @deprecated Use `event` instead. */ /** @deprecated Use `event` instead. */
@ -66,10 +54,24 @@ interface _NuxtApp {
teleports?: Record<string, string> teleports?: Record<string, string>
renderMeta?: () => Promise<NuxtMeta> | NuxtMeta renderMeta?: () => Promise<NuxtMeta> | NuxtMeta
} }
interface _NuxtApp {
vueApp: App<Element>
globalName: string
hooks: Hookable<RuntimeNuxtHooks>
hook: _NuxtApp['hooks']['hook']
callHook: _NuxtApp['hooks']['callHook']
[key: string]: any
_asyncDataPromises: Record<string, Promise<any> | undefined>
ssrContext?: NuxtSSRContext
payload: { payload: {
serverRendered?: boolean serverRendered?: boolean
data?: Record<string, any> data: Record<string, any>
state?: Record<string, any> state: Record<string, any>
rendered?: Function rendered?: Function
error?: Error | { error?: Error | {
url: string url: string
@ -135,24 +137,24 @@ export function createNuxtApp (options: CreateOptions) {
} }
// Expose to server renderer to create window.__NUXT__ // Expose to server renderer to create window.__NUXT__
nuxtApp.ssrContext = nuxtApp.ssrContext || {} as any nuxtApp.ssrContext = nuxtApp.ssrContext || {} as any
if (nuxtApp.ssrContext.payload) { if (nuxtApp.ssrContext!.payload) {
Object.assign(nuxtApp.payload, nuxtApp.ssrContext.payload) Object.assign(nuxtApp.payload, nuxtApp.ssrContext!.payload)
} }
nuxtApp.ssrContext.payload = nuxtApp.payload nuxtApp.ssrContext!.payload = nuxtApp.payload
// Expose client runtime-config to the payload // Expose client runtime-config to the payload
nuxtApp.payload.config = { nuxtApp.payload.config = {
public: options.ssrContext.runtimeConfig.public, public: options.ssrContext!.runtimeConfig.public,
app: options.ssrContext.runtimeConfig.app app: options.ssrContext!.runtimeConfig.app
} }
} }
// Expose runtime config // Expose runtime config
const runtimeConfig = process.server const runtimeConfig = process.server
? options.ssrContext.runtimeConfig ? options.ssrContext!.runtimeConfig
: reactive(nuxtApp.payload.config) : reactive(nuxtApp.payload.config)
// Backward compatibilty following #4254 // Backward compatibility following #4254
const compatibilityConfig = new Proxy(runtimeConfig, { const compatibilityConfig = new Proxy(runtimeConfig, {
get (target, prop) { get (target, prop) {
if (prop === 'public') { if (prop === 'public') {
@ -192,9 +194,9 @@ export async function applyPlugins (nuxtApp: NuxtApp, plugins: Plugin[]) {
} }
export function normalizePlugins (_plugins: Plugin[]) { export function normalizePlugins (_plugins: Plugin[]) {
const unwrappedPlugins = [] const unwrappedPlugins: Plugin[] = []
const legacyInjectPlugins = [] const legacyInjectPlugins: Plugin[] = []
const invalidPlugins = [] const invalidPlugins: Plugin[] = []
const plugins = _plugins.map((plugin) => { const plugins = _plugins.map((plugin) => {
if (typeof plugin !== 'function') { if (typeof plugin !== 'function') {

View File

@ -6,7 +6,7 @@ export default defineNuxtPlugin((nuxtApp) => {
if (logs.length > 0) { if (logs.length > 0) {
const ssrLogStyle = 'background: #003C3C;border-radius: 0.5em;color: white;font-weight: bold;padding: 2px 0.5em;' const ssrLogStyle = 'background: #003C3C;border-radius: 0.5em;color: white;font-weight: bold;padding: 2px 0.5em;'
console.groupCollapsed && console.groupCollapsed('%cNuxt Server Logs', ssrLogStyle) console.groupCollapsed && console.groupCollapsed('%cNuxt Server Logs', ssrLogStyle)
logs.forEach(logObj => (console[logObj.type] || console.log)(...logObj.args)) logs.forEach((logObj:any) => (console[logObj.type as 'log'] || console.log)(...logObj.args))
delete nuxtApp.payload.logs delete nuxtApp.payload.logs
console.groupEnd && console.groupEnd() console.groupEnd && console.groupEnd()
} }

View File

@ -13,8 +13,8 @@ export default defineNuxtPlugin((nuxtApp) => {
el.style.transition = 'width 0.1s, opacity 0.4s' el.style.transition = 'width 0.1s, opacity 0.4s'
const duration = 3000 const duration = 3000
const progress = 10000 / Math.floor(duration) const progress = 10000 / Math.floor(duration)
let timeout let timeout: ReturnType<typeof setTimeout> | undefined
let interval let interval: ReturnType<typeof setInterval> | undefined
nuxtApp.hook('page:start', () => { nuxtApp.hook('page:start', () => {
if (timeout) { return } if (timeout) { return }
timeout = setTimeout(() => { timeout = setTimeout(() => {
@ -30,9 +30,9 @@ export default defineNuxtPlugin((nuxtApp) => {
}) })
nuxtApp.hook('page:finish', () => { nuxtApp.hook('page:finish', () => {
timeout && clearTimeout(timeout) timeout && clearTimeout(timeout)
timeout = null timeout = undefined
interval && clearInterval(interval) interval && clearInterval(interval)
interval = null interval = undefined
el.style.width = '100%' el.style.width = '100%'
el.style.opacity = '0%' el.style.opacity = '0%'
setTimeout(() => { setTimeout(() => {

View File

@ -1,9 +1,8 @@
import { reactive, h } from 'vue' import { reactive, h } from 'vue'
import { parseURL, parseQuery, withoutBase, isEqual, joinURL } from 'ufo' import { parseURL, parseQuery, withoutBase, isEqual, joinURL } from 'ufo'
import { createError } from 'h3' import { createError } from 'h3'
import { defineNuxtPlugin } from '..' import { defineNuxtPlugin, clearError, navigateTo, showError, useRuntimeConfig } from '..'
import { callWithNuxt } from '../nuxt' import { callWithNuxt } from '../nuxt'
import { clearError, navigateTo, showError, useRuntimeConfig } from '#app'
// @ts-ignore // @ts-ignore
import { globalMiddleware } from '#build/middleware' import { globalMiddleware } from '#build/middleware'
@ -90,8 +89,9 @@ interface Router {
export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => { export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
const initialURL = process.client const initialURL = process.client
? withoutBase(window.location.pathname, useRuntimeConfig().app.baseURL) + window.location.search + window.location.hash ? withoutBase(window.location.pathname, useRuntimeConfig().app.baseURL) + window.location.search + window.location.hash
: nuxtApp.ssrContext.url : nuxtApp.ssrContext!.url
const routes = []
const routes: Route[] = []
const hooks: { [key in keyof RouterHooks]: RouterHooks[key][] } = { const hooks: { [key in keyof RouterHooks]: RouterHooks[key][] } = {
'navigate:before': [], 'navigate:before': [],
@ -194,7 +194,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
const route = router.resolve(props.to) const route = router.resolve(props.to)
return props.custom return props.custom
? slots.default?.({ href: props.to, navigate, route }) ? slots.default?.({ href: props.to, navigate, route })
: h('a', { href: props.to, onClick: (e) => { e.preventDefault(); return navigate() } }, slots) : h('a', { href: props.to, onClick: (e: MouseEvent) => { e.preventDefault(); return navigate() } }, slots)
} }
} }
}) })

View File

@ -24,7 +24,7 @@ export default defineNuxtModule<Partial<AutoImportsOptions>>({
// Allow modules extending sources // Allow modules extending sources
await nuxt.callHook('autoImports:sources', options.presets as ImportPresetWithDeprecation[]) await nuxt.callHook('autoImports:sources', options.presets as ImportPresetWithDeprecation[])
options.presets.forEach((i: ImportPresetWithDeprecation) => { options.presets?.forEach((i: ImportPresetWithDeprecation | string) => {
if (typeof i !== 'string' && i.names && !i.imports) { if (typeof i !== 'string' && i.names && !i.imports) {
i.imports = i.names i.imports = i.names
logger.warn('auto-imports: presets.names is deprecated, use presets.imports instead') logger.warn('auto-imports: presets.names is deprecated, use presets.imports instead')
@ -45,10 +45,13 @@ export default defineNuxtModule<Partial<AutoImportsOptions>>({
}) })
// composables/ dirs from all layers // composables/ dirs from all layers
let composablesDirs = [] let composablesDirs: string[] = []
for (const layer of nuxt.options._layers) { for (const layer of nuxt.options._layers) {
composablesDirs.push(resolve(layer.config.srcDir, 'composables')) composablesDirs.push(resolve(layer.config.srcDir, 'composables'))
for (const dir of (layer.config.autoImports?.dirs ?? [])) { for (const dir of (layer.config.autoImports?.dirs ?? [])) {
if (!dir) {
continue
}
composablesDirs.push(resolve(layer.config.srcDir, dir)) composablesDirs.push(resolve(layer.config.srcDir, dir))
} }
} }
@ -123,7 +126,7 @@ function addDeclarationTemplates (ctx: Unimport) {
// Remove file extension for benefit of TypeScript // Remove file extension for benefit of TypeScript
const stripExtension = (path: string) => path.replace(/\.[a-z]+$/, '') const stripExtension = (path: string) => path.replace(/\.[a-z]+$/, '')
const resolved = {} const resolved: Record<string, string> = {}
const r = ({ from }: Import) => { const r = ({ from }: Import) => {
if (resolved[from]) { if (resolved[from]) {
return resolved[from] return resolved[from]

View File

@ -1,7 +1,7 @@
import { promises as fsp } from 'node:fs' import { promises as fsp } from 'node:fs'
import { dirname, resolve } from 'pathe' import { dirname, resolve } from 'pathe'
import defu from 'defu' import defu from 'defu'
import type { Nuxt, NuxtApp, NuxtPlugin } from '@nuxt/schema' import type { Nuxt, NuxtApp, NuxtPlugin, NuxtTemplate } from '@nuxt/schema'
import { findPath, resolveFiles, normalizePlugin, normalizeTemplate, compileTemplate, templateUtils, tryResolveModule, resolvePath, resolveAlias } from '@nuxt/kit' import { findPath, resolveFiles, normalizePlugin, normalizeTemplate, compileTemplate, templateUtils, tryResolveModule, resolvePath, resolveAlias } from '@nuxt/kit'
import * as defaultTemplates from './templates' import * as defaultTemplates from './templates'
@ -13,7 +13,7 @@ export function createApp (nuxt: Nuxt, options: Partial<NuxtApp> = {}): NuxtApp
extensions: nuxt.options.extensions, extensions: nuxt.options.extensions,
plugins: [], plugins: [],
templates: [] templates: []
} as NuxtApp) } as unknown as NuxtApp) as NuxtApp
} }
export async function generateApp (nuxt: Nuxt, app: NuxtApp) { export async function generateApp (nuxt: Nuxt, app: NuxtApp) {
@ -21,7 +21,7 @@ export async function generateApp (nuxt: Nuxt, app: NuxtApp) {
await resolveApp(nuxt, app) await resolveApp(nuxt, app)
// User templates from options.build.templates // User templates from options.build.templates
app.templates = Object.values(defaultTemplates).concat(nuxt.options.build.templates) app.templates = Object.values(defaultTemplates).concat(nuxt.options.build.templates) as NuxtTemplate[]
// Extend templates with hook // Extend templates with hook
await nuxt.callHook('app:templates', app) await nuxt.callHook('app:templates', app)
@ -34,10 +34,10 @@ export async function generateApp (nuxt: Nuxt, app: NuxtApp) {
await Promise.all(app.templates.map(async (template) => { await Promise.all(app.templates.map(async (template) => {
const contents = await compileTemplate(template, templateContext) const contents = await compileTemplate(template, templateContext)
const fullPath = template.dst || resolve(nuxt.options.buildDir, template.filename) const fullPath = template.dst || resolve(nuxt.options.buildDir, template.filename!)
nuxt.vfs[fullPath] = contents nuxt.vfs[fullPath] = contents
const aliasPath = '#build/' + template.filename.replace(/\.\w+$/, '') const aliasPath = '#build/' + template.filename!.replace(/\.\w+$/, '')
nuxt.vfs[aliasPath] = contents nuxt.vfs[aliasPath] = contents
// In case a non-normalized absolute path is called for on Windows // In case a non-normalized absolute path is called for on Windows
@ -102,10 +102,12 @@ export async function resolveApp (nuxt: Nuxt, app: NuxtApp) {
for (const config of nuxt.options._layers.map(layer => layer.config)) { for (const config of nuxt.options._layers.map(layer => layer.config)) {
app.plugins.push(...[ app.plugins.push(...[
...(config.plugins || []), ...(config.plugins || []),
...await resolveFiles(config.srcDir, [ ...config.srcDir
? await resolveFiles(config.srcDir, [
'plugins/*.{ts,js,mjs,cjs,mts,cts}', 'plugins/*.{ts,js,mjs,cjs,mts,cts}',
'plugins/*/index.*{ts,js,mjs,cjs,mts,cts}' 'plugins/*/index.*{ts,js,mjs,cjs,mts,cts}'
]) ])
: []
].map(plugin => normalizePlugin(plugin as NuxtPlugin))) ].map(plugin => normalizePlugin(plugin as NuxtPlugin)))
} }

View File

@ -15,10 +15,10 @@ export async function build (nuxt: Nuxt) {
nuxt.hook('builder:watch', async (event, path) => { nuxt.hook('builder:watch', async (event, path) => {
if (event !== 'change' && /^(app\.|error\.|plugins\/|middleware\/|layouts\/)/i.test(path)) { if (event !== 'change' && /^(app\.|error\.|plugins\/|middleware\/|layouts\/)/i.test(path)) {
if (path.startsWith('app')) { if (path.startsWith('app')) {
app.mainComponent = null app.mainComponent = undefined
} }
if (path.startsWith('error')) { if (path.startsWith('error')) {
app.errorComponent = null app.errorComponent = undefined
} }
await generateApp() await generateApp()
} }
@ -38,7 +38,7 @@ export async function build (nuxt: Nuxt) {
} }
function watch (nuxt: Nuxt) { function watch (nuxt: Nuxt) {
const watcher = chokidar.watch(nuxt.options._layers.map(i => i.config.srcDir), { const watcher = chokidar.watch(nuxt.options._layers.map(i => i.config.srcDir as string).filter(Boolean), {
...nuxt.options.watchers.chokidar, ...nuxt.options.watchers.chokidar,
cwd: nuxt.options.srcDir, cwd: nuxt.options.srcDir,
ignoreInitial: true, ignoreInitial: true,
@ -61,7 +61,7 @@ async function bundle (nuxt: Nuxt) {
: nuxt.options.builder : nuxt.options.builder
return bundle(nuxt) return bundle(nuxt)
} catch (error) { } catch (error: any) {
await nuxt.callHook('build:error', error) await nuxt.callHook('build:error', error)
if (error.toString().includes('Cannot find module \'@nuxt/webpack-builder\'')) { if (error.toString().includes('Cannot find module \'@nuxt/webpack-builder\'')) {

View File

@ -20,6 +20,7 @@ export const addModuleTranspiles = (opts: AddModuleTranspilesOptions = {}) => {
// Try to sanitize modules to better match imports // Try to sanitize modules to better match imports
nuxt.options.build.transpile = nuxt.options.build.transpile =
nuxt.options.build.transpile.map(m => typeof m === 'string' ? m.split('node_modules/').pop() : m) nuxt.options.build.transpile.map(m => typeof m === 'string' ? m.split('node_modules/').pop() : m)
.filter(<T>(x: T | undefined): x is T => !!x)
function isTranspilePresent (mod: string) { function isTranspilePresent (mod: string) {
return nuxt.options.build.transpile.some(t => !(t instanceof Function) && (t instanceof RegExp ? t.test(mod) : new RegExp(t).test(mod))) return nuxt.options.build.transpile.some(t => !(t instanceof Function) && (t instanceof RegExp ? t.test(mod) : new RegExp(t).test(mod)))

View File

@ -22,7 +22,7 @@ export async function initNitro (nuxt: Nuxt) {
dev: nuxt.options.dev, dev: nuxt.options.dev,
preset: nuxt.options.dev ? 'nitro-dev' : undefined, preset: nuxt.options.dev ? 'nitro-dev' : undefined,
buildDir: nuxt.options.buildDir, buildDir: nuxt.options.buildDir,
scanDirs: nuxt.options._layers.map(layer => join(layer.config.srcDir, 'server')), scanDirs: nuxt.options._layers.map(layer => layer.config.srcDir).filter(Boolean).map(dir => join(dir!, 'server')),
renderer: resolve(distDir, 'core/runtime/nitro/renderer'), renderer: resolve(distDir, 'core/runtime/nitro/renderer'),
errorHandler: resolve(distDir, 'core/runtime/nitro/error'), errorHandler: resolve(distDir, 'core/runtime/nitro/error'),
nodeModulesDirs: nuxt.options.modulesDir, nodeModulesDirs: nuxt.options.modulesDir,
@ -49,7 +49,7 @@ export async function initNitro (nuxt: Nuxt) {
], ],
prerender: { prerender: {
crawlLinks: nuxt.options._generate ? nuxt.options.generate.crawler : false, crawlLinks: nuxt.options._generate ? nuxt.options.generate.crawler : false,
routes: [] routes: ([] as string[])
.concat(nuxt.options._generate ? ['/', ...nuxt.options.generate.routes] : []) .concat(nuxt.options._generate ? ['/', ...nuxt.options.generate.routes] : [])
.concat(nuxt.options.ssr === false ? ['/', '/200.html', '/404.html'] : []) .concat(nuxt.options.ssr === false ? ['/', '/200.html', '/404.html'] : [])
}, },
@ -102,11 +102,11 @@ export async function initNitro (nuxt: Nuxt) {
// Add fallback server for `ssr: false` // Add fallback server for `ssr: false`
if (!nuxt.options.ssr) { if (!nuxt.options.ssr) {
nitroConfig.virtual['#build/dist/server/server.mjs'] = 'export default () => {}' nitroConfig.virtual!['#build/dist/server/server.mjs'] = 'export default () => {}'
} }
// Register nuxt protection patterns // Register nuxt protection patterns
nitroConfig.rollupConfig.plugins.push(ImportProtectionPlugin.rollup({ nitroConfig.rollupConfig!.plugins!.push(ImportProtectionPlugin.rollup({
rootDir: nuxt.options.rootDir, rootDir: nuxt.options.rootDir,
patterns: [ patterns: [
...['#app', /^#build(\/|$)/] ...['#app', /^#build(\/|$)/]

View File

@ -81,7 +81,7 @@ async function initNuxt (nuxt: Nuxt) {
// Transpile layers within node_modules // Transpile layers within node_modules
nuxt.options.build.transpile.push( nuxt.options.build.transpile.push(
...nuxt.options._layers.filter(i => i.cwd && i.cwd.includes('node_modules')).map(i => i.cwd) ...nuxt.options._layers.filter(i => i.cwd.includes('node_modules')).map(i => i.cwd as string)
) )
// Init user modules // Init user modules
@ -95,7 +95,7 @@ async function initNuxt (nuxt: Nuxt) {
// Add <NuxtWelcome> // Add <NuxtWelcome>
addComponent({ addComponent({
name: 'NuxtWelcome', name: 'NuxtWelcome',
filePath: tryResolveModule('@nuxt/ui-templates/templates/welcome.vue') filePath: tryResolveModule('@nuxt/ui-templates/templates/welcome.vue')!
}) })
addComponent({ addComponent({
@ -165,7 +165,7 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
transform: { transform: {
include: options._layers include: options._layers
.filter(i => i.cwd && i.cwd.includes('node_modules')) .filter(i => i.cwd && i.cwd.includes('node_modules'))
.map(i => new RegExp(`(^|\\/)${escapeRE(i.cwd.split('node_modules/').pop())}(\\/|$)(?!node_modules\\/)`)) .map(i => new RegExp(`(^|\\/)${escapeRE(i.cwd!.split('node_modules/').pop()!)}(\\/|$)(?!node_modules\\/)`))
} }
}]) }])
options.modulesDir.push(resolve(pkgDir, 'node_modules')) options.modulesDir.push(resolve(pkgDir, 'node_modules'))

View File

@ -1,16 +1,14 @@
import { withQuery } from 'ufo' import { withQuery } from 'ufo'
import type { NitroErrorHandler } from 'nitropack' import type { NitroErrorHandler } from 'nitropack'
import type { H3Error } from 'h3' import type { H3Error } from 'h3'
// @ts-ignore TODO
import { normalizeError, isJsonRequest } from '#internal/nitro/utils' import { normalizeError, isJsonRequest } from '#internal/nitro/utils'
import { NuxtApp } from '#app'
export default <NitroErrorHandler> async function errorhandler (error: H3Error, event) { export default <NitroErrorHandler> async function errorhandler (error: H3Error, event) {
// Parse and normalize error // Parse and normalize error
const { stack, statusCode, statusMessage, message } = normalizeError(error) const { stack, statusCode, statusMessage, message } = normalizeError(error)
// Create an error object // Create an error object
const errorObject: Exclude<NuxtApp['payload']['error'], Error> = { const errorObject = {
url: event.req.url, url: event.req.url,
statusCode, statusCode,
statusMessage, statusMessage,

View File

@ -1,20 +1,15 @@
import { createRenderer } from 'vue-bundle-renderer/runtime' import { createRenderer } from 'vue-bundle-renderer/runtime'
import type { RenderHandler, RenderResponse } from 'nitropack' import type { RenderResponse } from 'nitropack'
import type { Manifest } from 'vite' import type { Manifest } from 'vite'
import { CompatibilityEvent, getQuery } from 'h3' import { getQuery } from 'h3'
import devalue from '@nuxt/devalue' import devalue from '@nuxt/devalue'
import { renderToString as _renderToString } from 'vue/server-renderer' import { renderToString as _renderToString } from 'vue/server-renderer'
import type { NuxtApp } from '#app' import type { NuxtApp, NuxtSSRContext } from '#app'
import { useRuntimeConfig, useNitroApp, defineRenderHandler } from '#internal/nitro'
// @ts-ignore
import { useRuntimeConfig, useNitroApp, defineRenderHandler as _defineRenderHandler } from '#internal/nitro'
// @ts-ignore // @ts-ignore
import { buildAssetsURL } from '#paths' import { buildAssetsURL } from '#paths'
export type NuxtSSRContext = NuxtApp['ssrContext']
const defineRenderHandler = _defineRenderHandler as (h: RenderHandler) => CompatibilityEvent
export interface NuxtRenderHTMLContext { export interface NuxtRenderHTMLContext {
htmlAttrs: string[] htmlAttrs: string[]
head: string[] head: string[]
@ -31,10 +26,12 @@ export interface NuxtRenderResponse {
headers: Record<string, string> headers: Record<string, string>
} }
interface ClientManifest {}
// @ts-ignore // @ts-ignore
const getClientManifest: () => Promise<Manifest> = () => import('#build/dist/server/client.manifest.mjs') const getClientManifest: () => Promise<Manifest> = () => import('#build/dist/server/client.manifest.mjs')
.then(r => r.default || r) .then(r => r.default || r)
.then(r => typeof r === 'function' ? r() : r) .then(r => typeof r === 'function' ? r() : r) as Promise<ClientManifest>
// @ts-ignore // @ts-ignore
const getServerEntry = () => import('#build/dist/server/server.mjs').then(r => r.default || r) const getServerEntry = () => import('#build/dist/server/server.mjs').then(r => r.default || r)
@ -57,7 +54,8 @@ const getSSRRenderer = lazyCachedFunction(async () => {
// Create renderer // Create renderer
const renderer = createRenderer(createSSRApp, options) const renderer = createRenderer(createSSRApp, options)
async function renderToString (input, context) { type RenderToStringParams = Parameters<typeof _renderToString>
async function renderToString (input: RenderToStringParams[0], context: RenderToStringParams[1]) {
const html = await _renderToString(input, context) const html = await _renderToString(input, context)
// In development with vite-node, the manifest is on-demand and will be available after rendering // In development with vite-node, the manifest is on-demand and will be available after rendering
if (process.dev && process.env.NUXT_VITE_NODE_OPTIONS) { if (process.dev && process.env.NUXT_VITE_NODE_OPTIONS) {
@ -84,14 +82,16 @@ const getSPARenderer = lazyCachedFunction(async () => {
const renderToString = (ssrContext: NuxtSSRContext) => { const renderToString = (ssrContext: NuxtSSRContext) => {
const config = useRuntimeConfig() const config = useRuntimeConfig()
ssrContext.payload = { ssrContext!.payload = {
serverRendered: false, serverRendered: false,
config: { config: {
public: config.public, public: config.public,
app: config.app app: config.app
},
data: {},
state: {}
} }
} ssrContext!.renderMeta = ssrContext!.renderMeta ?? (() => ({}))
ssrContext.renderMeta = ssrContext.renderMeta ?? (() => ({}))
return Promise.resolve(result) return Promise.resolve(result)
} }
@ -109,23 +109,25 @@ export default defineRenderHandler(async (event) => {
event, event,
req: event.req, req: event.req,
res: event.res, res: event.res,
runtimeConfig: useRuntimeConfig(), runtimeConfig: useRuntimeConfig() as NuxtSSRContext['runtimeConfig'],
noSSR: !!event.req.headers['x-nuxt-no-ssr'], noSSR: !!event.req.headers['x-nuxt-no-ssr'],
error: !!ssrError, error: !!ssrError,
nuxt: undefined, /* NuxtApp */ nuxt: undefined!, /* NuxtApp */
payload: ssrError ? { error: ssrError } : undefined payload: ssrError ? { error: ssrError } as NuxtSSRContext['payload'] : undefined!
} }
// Render app // Render app
const renderer = (process.env.NUXT_NO_SSR || ssrContext.noSSR) ? await getSPARenderer() : await getSSRRenderer() const renderer = (process.env.NUXT_NO_SSR || ssrContext.noSSR) ? await getSPARenderer() : await getSSRRenderer()
const _rendered = await renderer.renderToString(ssrContext).catch((err) => { const _rendered = await renderer.renderToString(ssrContext).catch((err) => {
if (!ssrError) { throw err } if (!ssrError) {
throw err
}
}) })
await ssrContext.nuxt?.hooks.callHook('app:rendered', { ssrContext }) await ssrContext.nuxt?.hooks.callHook('app:rendered', { ssrContext })
// Handle errors // Handle errors
if (!_rendered) { if (!_rendered) {
return return undefined!
} }
if (ssrContext.payload?.error && !ssrError) { if (ssrContext.payload?.error && !ssrError) {
throw ssrContext.payload.error throw ssrContext.payload.error
@ -143,7 +145,7 @@ export default defineRenderHandler(async (event) => {
_rendered.renderStyles(), _rendered.renderStyles(),
ssrContext.styles ssrContext.styles
]), ]),
bodyAttrs: normalizeChunks([renderedMeta.bodyAttrs]), bodyAttrs: normalizeChunks([renderedMeta.bodyAttrs!]),
bodyPreprend: normalizeChunks([ bodyPreprend: normalizeChunks([
renderedMeta.bodyScriptsPrepend, renderedMeta.bodyScriptsPrepend,
ssrContext.teleports?.body ssrContext.teleports?.body
@ -188,8 +190,8 @@ function lazyCachedFunction <T> (fn: () => Promise<T>): () => Promise<T> {
} }
} }
function normalizeChunks (chunks: string[]) { function normalizeChunks (chunks: (string | undefined)[]) {
return chunks.filter(Boolean).map(i => i.trim()) return chunks.filter(Boolean).map(i => i!.trim())
} }
function joinTags (tags: string[]) { function joinTags (tags: string[]) {

View File

@ -11,7 +11,7 @@ export interface TemplateContext {
app: NuxtApp app: NuxtApp
} }
export const vueShim = { export const vueShim: NuxtTemplate = {
filename: 'types/vue-shim.d.ts', filename: 'types/vue-shim.d.ts',
getContents: () => getContents: () =>
[ [
@ -24,29 +24,29 @@ export const vueShim = {
} }
// TODO: Use an alias // TODO: Use an alias
export const appComponentTemplate = { export const appComponentTemplate: NuxtTemplate<TemplateContext> = {
filename: 'app-component.mjs', filename: 'app-component.mjs',
getContents: (ctx: TemplateContext) => genExport(ctx.app.mainComponent, ['default']) getContents: ctx => genExport(ctx.app.mainComponent!, ['default'])
} }
// TODO: Use an alias // TODO: Use an alias
export const rootComponentTemplate = { export const rootComponentTemplate: NuxtTemplate<TemplateContext> = {
filename: 'root-component.mjs', filename: 'root-component.mjs',
getContents: (ctx: TemplateContext) => genExport(ctx.app.rootComponent, ['default']) getContents: ctx => genExport(ctx.app.rootComponent!, ['default'])
} }
// TODO: Use an alias // TODO: Use an alias
export const errorComponentTemplate = { export const errorComponentTemplate: NuxtTemplate<TemplateContext> = {
filename: 'error-component.mjs', filename: 'error-component.mjs',
getContents: (ctx: TemplateContext) => genExport(ctx.app.errorComponent, ['default']) getContents: ctx => genExport(ctx.app.errorComponent!, ['default'])
} }
export const cssTemplate = { export const cssTemplate: NuxtTemplate<TemplateContext> = {
filename: 'css.mjs', filename: 'css.mjs',
getContents: (ctx: TemplateContext) => ctx.nuxt.options.css.map(i => genImport(i)).join('\n') getContents: ctx => ctx.nuxt.options.css.map(i => genImport(i)).join('\n')
} }
export const clientPluginTemplate = { export const clientPluginTemplate: NuxtTemplate<TemplateContext> = {
filename: 'plugins/client.mjs', filename: 'plugins/client.mjs',
getContents (ctx: TemplateContext) { getContents (ctx) {
const clientPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'server') const clientPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'server')
const exports: string[] = [] const exports: string[] = []
const imports: string[] = [] const imports: string[] = []
@ -63,9 +63,9 @@ export const clientPluginTemplate = {
} }
} }
export const serverPluginTemplate = { export const serverPluginTemplate: NuxtTemplate<TemplateContext> = {
filename: 'plugins/server.mjs', filename: 'plugins/server.mjs',
getContents (ctx: TemplateContext) { getContents (ctx) {
const serverPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'client') const serverPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'client')
const exports: string[] = ['preload'] const exports: string[] = ['preload']
const imports: string[] = ["import preload from '#app/plugins/preload.server'"] const imports: string[] = ["import preload from '#app/plugins/preload.server'"]
@ -82,9 +82,9 @@ export const serverPluginTemplate = {
} }
} }
export const pluginsDeclaration = { export const pluginsDeclaration: NuxtTemplate<TemplateContext> = {
filename: 'types/plugins.d.ts', filename: 'types/plugins.d.ts',
getContents: (ctx: TemplateContext) => { getContents: (ctx) => {
const EXTENSION_RE = new RegExp(`(?<=\\w)(${ctx.nuxt.options.extensions.map(e => escapeRE(e)).join('|')})$`, 'g') const EXTENSION_RE = new RegExp(`(?<=\\w)(${ctx.nuxt.options.extensions.map(e => escapeRE(e)).join('|')})$`, 'g')
const tsImports = ctx.app.plugins.map(p => (isAbsolute(p.src) ? relative(join(ctx.nuxt.options.buildDir, 'types'), p.src) : p.src).replace(EXTENSION_RE, '')) const tsImports = ctx.app.plugins.map(p => (isAbsolute(p.src) ? relative(join(ctx.nuxt.options.buildDir, 'types'), p.src) : p.src).replace(EXTENSION_RE, ''))
@ -111,9 +111,9 @@ export { }
} }
const adHocModules = ['router', 'pages', 'auto-imports', 'meta', 'components'] const adHocModules = ['router', 'pages', 'auto-imports', 'meta', 'components']
export const schemaTemplate = { export const schemaTemplate: NuxtTemplate<TemplateContext> = {
filename: 'types/schema.d.ts', filename: 'types/schema.d.ts',
getContents: ({ nuxt }: TemplateContext) => { getContents: ({ nuxt }) => {
const moduleInfo = nuxt.options._installedModules.map(m => ({ const moduleInfo = nuxt.options._installedModules.map(m => ({
...m.meta || {}, ...m.meta || {},
importName: m.entryPath || m.meta?.name importName: m.entryPath || m.meta?.name
@ -149,9 +149,9 @@ export const schemaTemplate = {
} }
// Add layouts template // Add layouts template
export const layoutTemplate: NuxtTemplate = { export const layoutTemplate: NuxtTemplate<TemplateContext> = {
filename: 'layouts.mjs', filename: 'layouts.mjs',
getContents ({ app }: TemplateContext) { getContents ({ app }) {
const layoutsObject = genObjectFromRawEntries(Object.values(app.layouts).map(({ name, file }) => { const layoutsObject = genObjectFromRawEntries(Object.values(app.layouts).map(({ name, file }) => {
return [name, `defineAsyncComponent(${genDynamicImport(file)})`] return [name, `defineAsyncComponent(${genDynamicImport(file)})`]
})) }))
@ -163,9 +163,9 @@ export const layoutTemplate: NuxtTemplate = {
} }
// Add middleware template // Add middleware template
export const middlewareTemplate: NuxtTemplate = { export const middlewareTemplate: NuxtTemplate<TemplateContext> = {
filename: 'middleware.mjs', filename: 'middleware.mjs',
getContents ({ app }: TemplateContext) { getContents ({ app }) {
const globalMiddleware = app.middleware.filter(mw => mw.global) const globalMiddleware = app.middleware.filter(mw => mw.global)
const namedMiddleware = app.middleware.filter(mw => !mw.global) const namedMiddleware = app.middleware.filter(mw => !mw.global)
const namedMiddlewareObject = genObjectFromRawEntries(namedMiddleware.map(mw => [mw.name, genDynamicImport(mw.path)])) const namedMiddlewareObject = genObjectFromRawEntries(namedMiddleware.map(mw => [mw.name, genDynamicImport(mw.path)]))
@ -184,7 +184,7 @@ export const useRuntimeConfig = () => window?.__NUXT__?.config || {}
` `
} }
export const publicPathTemplate: NuxtTemplate = { export const publicPathTemplate: NuxtTemplate<TemplateContext> = {
filename: 'paths.mjs', filename: 'paths.mjs',
getContents ({ nuxt }) { getContents ({ nuxt }) {
return [ return [

View File

@ -20,20 +20,20 @@ export default defineNuxtPlugin((nuxtApp) => {
} }
if (process.server) { if (process.server) {
nuxtApp.ssrContext.renderMeta = async () => { nuxtApp.ssrContext!.renderMeta = async () => {
// @ts-ignore // @ts-ignore
const { renderMetaToString } = await import('vue-meta/ssr') const { renderMetaToString } = await import('vue-meta/ssr')
nuxtApp.ssrContext.teleports = nuxtApp.ssrContext.teleports || {} nuxtApp.ssrContext!.teleports = nuxtApp.ssrContext!.teleports || {}
await renderMetaToString(nuxtApp.app, nuxtApp.ssrContext) await renderMetaToString(nuxtApp.app, nuxtApp.ssrContext)
return { return {
htmlAttrs: nuxtApp.ssrContext.teleports.htmlAttrs || '', htmlAttrs: nuxtApp.ssrContext!.teleports.htmlAttrs || '',
headAttrs: nuxtApp.ssrContext.teleports.headAttrs || '', headAttrs: nuxtApp.ssrContext!.teleports.headAttrs || '',
bodyAttrs: nuxtApp.ssrContext.teleports.bodyAttrs || '', bodyAttrs: nuxtApp.ssrContext!.teleports.bodyAttrs || '',
headTags: nuxtApp.ssrContext.teleports.head || '', headTags: nuxtApp.ssrContext!.teleports.head || '',
bodyScriptsPrepend: nuxtApp.ssrContext.teleports['body-prepend'] || '', bodyScriptsPrepend: nuxtApp.ssrContext!.teleports['body-prepend'] || '',
bodyScripts: nuxtApp.ssrContext.teleports.body || '' bodyScripts: nuxtApp.ssrContext!.teleports.body || ''
} }
} }
} }

View File

@ -45,7 +45,7 @@ export default defineNuxtPlugin((nuxtApp) => {
} }
if (process.server) { if (process.server) {
nuxtApp.ssrContext.renderMeta = () => { nuxtApp.ssrContext!.renderMeta = () => {
const meta = renderHeadToString(head) const meta = renderHeadToString(head)
return { return {
...meta, ...meta,

View File

@ -34,6 +34,6 @@ export default defineNuxtPlugin((nuxtApp) => {
for (const name in Components) { for (const name in Components) {
// eslint-disable-next-line import/namespace // eslint-disable-next-line import/namespace
nuxtApp.vueApp.component(name, Components[name]) nuxtApp.vueApp.component(name, (Components as any)[name])
} }
}) })

View File

@ -47,7 +47,7 @@ export default defineNuxtModule({
nuxt.hook('app:resolve', (app) => { nuxt.hook('app:resolve', (app) => {
// Add default layout for pages // Add default layout for pages
if (app.mainComponent.includes('@nuxt/ui-templates')) { if (app.mainComponent!.includes('@nuxt/ui-templates')) {
app.mainComponent = resolve(runtimeDir, 'app.vue') app.mainComponent = resolve(runtimeDir, 'app.vue')
} }
}) })

View File

@ -1,5 +1,5 @@
import { computed, DefineComponent, defineComponent, h, inject, provide, reactive, Suspense, Transition } from 'vue' import { computed, DefineComponent, defineComponent, h, inject, provide, reactive, Suspense, Transition } from 'vue'
import { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouterView } from 'vue-router' import { RouteLocation, RouteLocationNormalized, RouteLocationNormalizedLoaded, RouterView } from 'vue-router'
import { generateRouteKey, RouterViewSlotProps, wrapInKeepAlive } from './utils' import { generateRouteKey, RouterViewSlotProps, wrapInKeepAlive } from './utils'
import { useNuxtApp } from '#app' import { useNuxtApp } from '#app'
@ -58,6 +58,7 @@ export default defineComponent({
const defaultPageTransition = { name: 'page', mode: 'out-in' } const defaultPageTransition = { name: 'page', mode: 'out-in' }
const Component = defineComponent({ const Component = defineComponent({
// TODO: Type props
// eslint-disable-next-line vue/require-prop-types // eslint-disable-next-line vue/require-prop-types
props: ['routeProps', 'pageKey'], props: ['routeProps', 'pageKey'],
setup (props) { setup (props) {
@ -66,9 +67,9 @@ const Component = defineComponent({
const previousRoute = props.routeProps.route const previousRoute = props.routeProps.route
// Provide a reactive route within the page // Provide a reactive route within the page
const route = {} const route = {} as RouteLocation
for (const key in props.routeProps.route) { for (const key in props.routeProps.route) {
route[key] = computed(() => previousKey === props.pageKey ? props.routeProps.route[key] : previousRoute[key]) (route as any)[key] = computed(() => previousKey === props.pageKey ? props.routeProps.route[key] : previousRoute[key])
} }
provide('_route', reactive(route)) provide('_route', reactive(route))

View File

@ -59,7 +59,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
? createWebHistory(baseURL) ? createWebHistory(baseURL)
: createMemoryHistory(baseURL) : createMemoryHistory(baseURL)
const initialURL = process.server ? nuxtApp.ssrContext.url : createCurrentLocation(baseURL, window.location) const initialURL = process.server ? nuxtApp.ssrContext!.url : createCurrentLocation(baseURL, window.location)
const router = createRouter({ const router = createRouter({
...routerOptions, ...routerOptions,
history: routerHistory, history: routerHistory,
@ -89,9 +89,9 @@ export default defineNuxtPlugin(async (nuxtApp) => {
}) })
// https://github.com/vuejs/router/blob/main/packages/router/src/router.ts#L1225-L1233 // https://github.com/vuejs/router/blob/main/packages/router/src/router.ts#L1225-L1233
const route = {} const route = {} as RouteLocation
for (const key in _route.value) { for (const key in _route.value) {
route[key] = computed(() => _route.value[key]) (route as any)[key] = computed(() => _route.value[key as keyof RouteLocation])
} }
nuxtApp._route = reactive(route) nuxtApp._route = reactive(route)
@ -109,7 +109,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
} }
await router.isReady() await router.isReady()
} catch (error) { } catch (error: any) {
// We'll catch 404s here // We'll catch 404s here
callWithNuxt(nuxtApp, showError, [error]) callWithNuxt(nuxtApp, showError, [error])
} }
@ -133,7 +133,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
} }
for (const entry of middlewareEntries) { for (const entry of middlewareEntries) {
const middleware = typeof entry === 'string' ? nuxtApp._middleware.named[entry] || await namedMiddleware[entry]?.().then(r => r.default || r) : entry const middleware = typeof entry === 'string' ? nuxtApp._middleware.named[entry] || await namedMiddleware[entry]?.().then((r: any) => r.default || r) : entry
if (!middleware) { if (!middleware) {
if (process.dev) { if (process.dev) {
@ -169,7 +169,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
statusMessage: `Page not found: ${to.fullPath}` statusMessage: `Page not found: ${to.fullPath}`
})]) })])
} else if (process.server && to.matched[0].name === '404' && nuxtApp.ssrContext) { } else if (process.server && to.matched[0].name === '404' && nuxtApp.ssrContext) {
nuxtApp.ssrContext.res.statusCode = 404 nuxtApp.ssrContext.event.res.statusCode = 404
} else if (process.server) { } else if (process.server) {
const currentURL = to.fullPath || '/' const currentURL = to.fullPath || '/'
if (!isEqual(currentURL, initialURL)) { if (!isEqual(currentURL, initialURL)) {
@ -185,7 +185,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
name: undefined, // #4920, #$4982 name: undefined, // #4920, #$4982
force: true force: true
}) })
} catch (error) { } catch (error: any) {
// We'll catch middleware errors or deliberate exceptions here // We'll catch middleware errors or deliberate exceptions here
callWithNuxt(nuxtApp, showError, [error]) callWithNuxt(nuxtApp, showError, [error])
} }

View File

@ -12,8 +12,8 @@ const interpolatePath = (route: RouteLocationNormalizedLoaded, match: RouteLocat
} }
export const generateRouteKey = (override: string | ((route: RouteLocationNormalizedLoaded) => string), routeProps: RouterViewSlotProps) => { export const generateRouteKey = (override: string | ((route: RouteLocationNormalizedLoaded) => string), routeProps: RouterViewSlotProps) => {
const matchedRoute = routeProps.route.matched.find(m => m.components.default === routeProps.Component.type) const matchedRoute = routeProps.route.matched.find(m => m.components?.default === routeProps.Component.type)
const source = override ?? matchedRoute?.meta.key ?? interpolatePath(routeProps.route, matchedRoute) const source = override ?? matchedRoute?.meta.key ?? (matchedRoute && interpolatePath(routeProps.route, matchedRoute))
return typeof source === 'function' ? source(routeProps.route) : source return typeof source === 'function' ? source(routeProps.route) : source
} }

View File

@ -76,7 +76,7 @@ export function generateRoutesFromFiles (files: string[], pagesDir: string): Nux
// ex: parent.vue + parent/child.vue // ex: parent.vue + parent/child.vue
const child = parent.find(parentRoute => parentRoute.name === route.name && !parentRoute.path.endsWith('(.*)*')) const child = parent.find(parentRoute => parentRoute.name === route.name && !parentRoute.path.endsWith('(.*)*'))
if (child) { if (child && child.children) {
parent = child.children parent = child.children
route.path = '' route.path = ''
} else if (segmentName === '404' && isSingleSegment) { } else if (segmentName === '404' && isSingleSegment) {
@ -213,11 +213,11 @@ function prepareRoutes (routes: NuxtPage[], parent?: NuxtPage) {
route.path = route.path.slice(1) route.path = route.path.slice(1)
} }
if (route.children.length) { if (route.children?.length) {
route.children = prepareRoutes(route.children, route) route.children = prepareRoutes(route.children, route)
} }
if (route.children.find(childRoute => childRoute.path === '')) { if (route.children?.find(childRoute => childRoute.path === '')) {
delete route.name delete route.name
} }
} }

View File

@ -8,7 +8,7 @@ vi.mock('vue', async () => {
return { return {
...vue, ...vue,
resolveComponent: (name: string) => name, resolveComponent: (name: string) => name,
h: (...args) => args h: (...args: any[]) => args
} }
}) })
@ -17,7 +17,7 @@ vi.mock('#app', () => ({
useRouter: () => ({ resolve: ({ to }: { to: string }) => ({ href: to }) }) useRouter: () => ({ resolve: ({ to }: { to: string }) => ({ href: to }) })
})) }))
// Helpers for test lisibility // Helpers for test visibility
const EXTERNAL = 'a' const EXTERNAL = 'a'
const INTERNAL = 'RouterLink' const INTERNAL = 'RouterLink'
@ -44,7 +44,7 @@ describe('nuxt-link:to', () => {
}) })
it('renders link with `to` prop and warns about `href` prop conflict', () => { it('renders link with `to` prop and warns about `href` prop conflict', () => {
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(vi.fn()) const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(vi.fn() as any)
expect(nuxtLink({ to: '/to', href: '/href' }).props.to).toBe('/to') expect(nuxtLink({ to: '/to', href: '/href' }).props.to).toBe('/to')
// TODO: Uncomment when `dev` mode for tests is available // TODO: Uncomment when `dev` mode for tests is available
@ -136,7 +136,7 @@ describe('nuxt-link:propsOrAttributes', () => {
}) })
it('honors `noRel` prop and warns about `rel` prop conflict', () => { it('honors `noRel` prop and warns about `rel` prop conflict', () => {
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(vi.fn()) const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(vi.fn() as any)
expect(nuxtLink({ to: 'https://nuxtjs.org', noRel: true, rel: 'foo' }).props.rel).toBe(null) expect(nuxtLink({ to: 'https://nuxtjs.org', noRel: true, rel: 'foo' }).props.rel).toBe(null)
// TODO: Uncomment when `dev` mode for tests is available // TODO: Uncomment when `dev` mode for tests is available

View File

@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"strict": true,
"noImplicitAny": true
},
"include": [
"./src/**/*.ts",
"./test/**/*.ts"
]
}

View File

@ -1,5 +1,4 @@
import { ConfigSchema } from '../../schema/config' import { ConfigSchema } from '../../schema/config'
import type { ResolvedConfig } from 'c12'
import type { UserConfig as ViteUserConfig } from 'vite' import type { UserConfig as ViteUserConfig } from 'vite'
import type { Options as VuePluginOptions } from '@vitejs/plugin-vue' import type { Options as VuePluginOptions } from '@vitejs/plugin-vue'
@ -12,9 +11,20 @@ export interface NuxtConfig extends DeepPartial<Omit<ConfigSchema, 'vite'>> {
[key: string]: any [key: string]: any
} }
// TODO: Expose ConfigLayer<T> from c12
interface ConfigLayer<T> {
config: T;
cwd: string;
configFile: string
}
export type NuxtConfigLayer = ConfigLayer<NuxtConfig & {
srcDir: ConfigSchema['srcDir'],
rootDir: ConfigSchema['rootDir']
}>
/** Normalized Nuxt options available as `nuxt.options.*` */ /** Normalized Nuxt options available as `nuxt.options.*` */
export interface NuxtOptions extends ConfigSchema { export interface NuxtOptions extends ConfigSchema {
_layers: ResolvedConfig<NuxtConfig>[] _layers: NuxtConfigLayer[]
} }
type RuntimeConfigNamespace = Record<string, any> type RuntimeConfigNamespace = Record<string, any>

View File

@ -24,7 +24,7 @@ export interface Nuxt {
vfs: Record<string, string> vfs: Record<string, string>
} }
export interface NuxtTemplate { export interface NuxtTemplate<Options = Record<string, any>> {
/** @deprecated filename */ /** @deprecated filename */
fileName?: string fileName?: string
/** @deprecated whether template is custom or a nuxt core template */ /** @deprecated whether template is custom or a nuxt core template */
@ -51,9 +51,9 @@ export interface NuxtPlugin {
} }
export interface NuxtApp { export interface NuxtApp {
mainComponent?: string mainComponent?: string | null
rootComponent?: string rootComponent?: string | null
errorComponent?: string errorComponent?: string | null
dir: string dir: string
extensions: string[] extensions: string[]
plugins: NuxtPlugin[] plugins: NuxtPlugin[]

View File

@ -4,8 +4,8 @@ import type { Ref } from 'vue'
import { NavigationFailure, RouteLocationNormalizedLoaded, RouteLocationRaw, useRouter as vueUseRouter } from 'vue-router' import { NavigationFailure, RouteLocationNormalizedLoaded, RouteLocationRaw, useRouter as vueUseRouter } from 'vue-router'
import { defineNuxtConfig } from '~~/../../../packages/nuxt/src' import { defineNuxtConfig } from '~~/../../../packages/nuxt/src'
import { useRouter } from '#imports'
import { isVue3 } from '#app' import { isVue3 } from '#app'
import { useRouter } from '#imports'
interface TestResponse { message: string } interface TestResponse { message: string }

View File

@ -8,6 +8,7 @@
"moduleResolution": "Node", "moduleResolution": "Node",
"strict": false, "strict": false,
"allowJs": true, "allowJs": true,
"noEmit": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"types": [ "types": [
@ -23,6 +24,12 @@
"#head": [ "#head": [
"./packages/nuxt/src/head/runtime/index" "./packages/nuxt/src/head/runtime/index"
], ],
"#internal/nitro": [
"./node_modules/nitropack/dist/runtime"
],
"#internal/nitro/utils": [
"./node_modules/nitropack/dist/runtime/utils"
]
} }
}, },
"exclude": [ "exclude": [