mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
refactor(nuxt): enable strict type checking (#6368)
Co-authored-by: Pooya Parsa <pooya@pi0.io>
This commit is contained in:
parent
cb98c8b921
commit
f350a70775
@ -7,7 +7,7 @@ import { NuxtConfigSchema } from '@nuxt/schema'
|
||||
export interface LoadNuxtConfigOptions extends LoadConfigOptions<NuxtConfig> {}
|
||||
|
||||
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',
|
||||
configFile: 'nuxt.config',
|
||||
rcFile: '.nuxtrc',
|
||||
@ -23,6 +23,7 @@ export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<Nuxt
|
||||
|
||||
// Resolve `rootDir` & `srcDir` of layers
|
||||
for (const layer of layers) {
|
||||
layer.config = layer.config || {}
|
||||
layer.config.rootDir = layer.config.rootDir ?? layer.cwd
|
||||
layer.config.srcDir = resolve(layer.config.rootDir, layer.config.srcDir)
|
||||
}
|
||||
|
@ -2,19 +2,19 @@ export * from 'vue'
|
||||
|
||||
export const install = () => {}
|
||||
|
||||
export function set (target, key, val) {
|
||||
export function set (target: any, key: string | number | symbol, val: any) {
|
||||
if (Array.isArray(target)) {
|
||||
target.length = Math.max(target.length, key)
|
||||
target.splice(key, 1, val)
|
||||
target.length = Math.max(target.length, key as number)
|
||||
target.splice(key as number, 1, val)
|
||||
return val
|
||||
}
|
||||
target[key] = val
|
||||
return val
|
||||
}
|
||||
|
||||
export function del (target, key) {
|
||||
export function del (target: any, key: string | number | symbol) {
|
||||
if (Array.isArray(target)) {
|
||||
target.splice(key, 1)
|
||||
target.splice(key as number, 1)
|
||||
return
|
||||
}
|
||||
delete target[key]
|
||||
|
@ -8,7 +8,7 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup (_props, { slots, emit }) {
|
||||
const error = ref(null)
|
||||
const error = ref<Error | null>(null)
|
||||
const nuxtApp = useNuxtApp()
|
||||
|
||||
onErrorCaptured((err) => {
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { defineComponent, h, resolveComponent, PropType, computed, DefineComponent } from 'vue'
|
||||
import { RouteLocationRaw, Router } from 'vue-router'
|
||||
import { defineComponent, h, resolveComponent, PropType, computed, DefineComponent, ComputedRef } from 'vue'
|
||||
import { RouteLocationRaw } from 'vue-router'
|
||||
import { hasProtocol } from 'ufo'
|
||||
|
||||
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'
|
||||
|
||||
@ -24,8 +24,8 @@ export type NuxtLinkProps = {
|
||||
custom?: boolean
|
||||
|
||||
// Attributes
|
||||
target?: string
|
||||
rel?: string
|
||||
target?: string | null
|
||||
rel?: string | null
|
||||
noRel?: boolean
|
||||
|
||||
// Styling
|
||||
@ -39,7 +39,7 @@ export type NuxtLinkProps = {
|
||||
export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
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) {
|
||||
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 }) {
|
||||
const router = useRouter() as Router | undefined
|
||||
const router = useRouter()
|
||||
|
||||
// Resolving `to` value from `to` and `href` props
|
||||
const to = computed<string | RouteLocationRaw>(() => {
|
||||
const to: ComputedRef<string | RouteLocationRaw> = computed(() => {
|
||||
checkPropConflicts(props, 'to', 'href')
|
||||
|
||||
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
|
||||
const isExternal = computed<boolean>(() => {
|
||||
// External prop is explictly set
|
||||
// External prop is explicitly set
|
||||
if (props.external) {
|
||||
return true
|
||||
}
|
||||
@ -180,11 +180,13 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
|
||||
// https://router.vuejs.org/api/#custom
|
||||
if (props.custom) {
|
||||
if (!slots.default) { return null }
|
||||
if (!slots.default) {
|
||||
return null
|
||||
}
|
||||
return slots.default({
|
||||
href,
|
||||
navigate,
|
||||
route: router.resolve(href),
|
||||
route: router.resolve(href!),
|
||||
rel,
|
||||
target,
|
||||
isActive: false,
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { h } from 'vue'
|
||||
import { defineComponent, h } from 'vue'
|
||||
import type { Component } from 'vue'
|
||||
|
||||
const Fragment = {
|
||||
const Fragment = defineComponent({
|
||||
setup (_props, { slots }) {
|
||||
return () => slots.default?.()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Internal utility
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { onBeforeMount, onServerPrefetch, onUnmounted, ref, getCurrentInstance, watch, unref } from 'vue'
|
||||
import type { Ref, WatchSource } from 'vue'
|
||||
import { wrapInRef } from './utils'
|
||||
import { NuxtApp, useNuxtApp } from '#app'
|
||||
|
||||
export type _Transform<Input = any, Output = any> = (input: Input) => Output
|
||||
@ -25,7 +24,7 @@ export interface AsyncDataOptions<
|
||||
> {
|
||||
server?: boolean
|
||||
lazy?: boolean
|
||||
default?: () => DataT | Ref<DataT>
|
||||
default?: () => DataT | Ref<DataT> | null
|
||||
transform?: Transform
|
||||
pick?: PickKeys
|
||||
watch?: MultiWatchSources
|
||||
@ -37,10 +36,10 @@ export interface RefreshOptions {
|
||||
}
|
||||
|
||||
export interface _AsyncData<DataT, ErrorT> {
|
||||
data: Ref<DataT>
|
||||
data: Ref<DataT | null>
|
||||
pending: Ref<boolean>
|
||||
refresh: (opts?: RefreshOptions) => Promise<void>
|
||||
error: Ref<ErrorT>
|
||||
error: Ref<ErrorT | null>
|
||||
}
|
||||
|
||||
export type AsyncData<Data, Error> = _AsyncData<Data, Error> & Promise<_AsyncData<Data, Error>>
|
||||
@ -70,7 +69,7 @@ export function useAsyncData<
|
||||
DataE = Error,
|
||||
Transform extends _Transform<DataT> = _Transform<DataT, DataT>,
|
||||
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
|
||||
if (typeof args[0] !== 'string') { args.unshift(autoKey) }
|
||||
|
||||
@ -102,7 +101,8 @@ export function useAsyncData<
|
||||
// Setup hook callbacks once per instance
|
||||
const instance = getCurrentInstance()
|
||||
if (instance && !instance._nuxtOnBeforeMountCbs) {
|
||||
const cbs = instance._nuxtOnBeforeMountCbs = []
|
||||
instance._nuxtOnBeforeMountCbs = []
|
||||
const cbs = instance._nuxtOnBeforeMountCbs
|
||||
if (instance && process.client) {
|
||||
onBeforeMount(() => {
|
||||
cbs.forEach((cb) => { cb() })
|
||||
@ -115,7 +115,7 @@ export function useAsyncData<
|
||||
const useInitialCache = () => options.initialCache && nuxt.payload.data[key] !== undefined
|
||||
|
||||
const asyncData = {
|
||||
data: wrapInRef(nuxt.payload.data[key] ?? options.default()),
|
||||
data: ref(nuxt.payload.data[key] ?? options.default?.() ?? null),
|
||||
pending: ref(!useInitialCache()),
|
||||
error: ref(nuxt.payload._errors[key] ?? null)
|
||||
} as AsyncData<DataT, DataE>
|
||||
@ -151,7 +151,7 @@ export function useAsyncData<
|
||||
})
|
||||
.catch((error: any) => {
|
||||
asyncData.error.value = error
|
||||
asyncData.data.value = unref(options.default())
|
||||
asyncData.data.value = unref(options.default?.() ?? null)
|
||||
})
|
||||
.finally(() => {
|
||||
asyncData.pending.value = false
|
||||
@ -230,7 +230,7 @@ export function useLazyAsyncData<
|
||||
DataE = Error,
|
||||
Transform extends _Transform<DataT> = _Transform<DataT, DataT>,
|
||||
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
|
||||
if (typeof args[0] !== 'string') { args.unshift(autoKey) }
|
||||
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[]) {
|
||||
const newObj = {}
|
||||
for (const key of keys) {
|
||||
newObj[key] = obj[key]
|
||||
(newObj as any)[key] = obj[key]
|
||||
}
|
||||
return newObj
|
||||
}
|
||||
|
@ -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>>) {
|
||||
const nuxt = useNuxtApp()
|
||||
const route = useRoute()
|
||||
const vm = getCurrentInstance()
|
||||
const { fetchKey } = vm.proxy.$options
|
||||
const vm = getCurrentInstance()!
|
||||
const { fetchKey } = vm.proxy!.$options
|
||||
const key = typeof fetchKey === 'function' ? fetchKey(() => '') : fetchKey || route.fullPath
|
||||
const { data } = await useAsyncData(`options:asyncdata:${key}`, () => fn(nuxt))
|
||||
if (data.value && typeof data.value === 'object') {
|
||||
@ -38,8 +38,7 @@ export const defineNuxtComponent: typeof defineComponent =
|
||||
setup (props, ctx) {
|
||||
const res = setup?.(props, ctx) || {}
|
||||
|
||||
let promises: unknown[] | undefined = []
|
||||
promises = promises || []
|
||||
const promises: Promise<any>[] = []
|
||||
if (options.asyncData) {
|
||||
promises.push(runLegacyAsyncData(res, options.asyncData))
|
||||
}
|
||||
@ -49,7 +48,6 @@ export const defineNuxtComponent: typeof defineComponent =
|
||||
.then(() => res)
|
||||
.finally(() => {
|
||||
promises.length = 0
|
||||
promises = null
|
||||
})
|
||||
}
|
||||
} as DefineComponent
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { Ref, watch } from 'vue'
|
||||
import { ref, Ref, watch } from 'vue'
|
||||
import { parse, serialize, CookieParseOptions, CookieSerializeOptions } from 'cookie-es'
|
||||
import { appendHeader } from 'h3'
|
||||
import type { CompatibilityEvent } from 'h3'
|
||||
import destr from 'destr'
|
||||
import { isEqual } from 'ohash'
|
||||
import { useRequestEvent } from './ssr'
|
||||
import { wrapInRef } from './utils'
|
||||
import { useNuxtApp } from '#app'
|
||||
|
||||
type _CookieOptions = Omit<CookieSerializeOptions & CookieParseOptions, 'decode' | 'encode'>
|
||||
@ -24,11 +23,11 @@ const CookieDefaults: CookieOptions<any> = {
|
||||
encode: val => encodeURIComponent(typeof val === 'string' ? val : JSON.stringify(val))
|
||||
}
|
||||
|
||||
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 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) {
|
||||
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>
|
||||
}
|
||||
|
||||
function readRawCookies (opts: CookieOptions = {}): Record<string, string> {
|
||||
function readRawCookies (opts: CookieOptions = {}): Record<string, string> | undefined {
|
||||
if (process.server) {
|
||||
return parse(useRequestEvent()?.req.headers.cookie || '', opts)
|
||||
} else if (process.client) {
|
||||
|
@ -37,7 +37,7 @@ export function useFetch<
|
||||
arg1?: string | UseFetchOptions<_ResT, Transform, PickKeys>,
|
||||
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
|
||||
if (!_key || typeof _key !== 'string') {
|
||||
throw new TypeError('[nuxt] [useFetch] key must be a string: ' + _key)
|
||||
|
@ -61,7 +61,7 @@ export interface NavigateToOptions {
|
||||
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) {
|
||||
to = '/'
|
||||
}
|
||||
@ -74,7 +74,7 @@ export const navigateTo = (to: RouteLocationRaw, options: NavigateToOptions = {}
|
||||
const nuxtApp = useNuxtApp()
|
||||
if (nuxtApp.ssrContext && nuxtApp.ssrContext.event) {
|
||||
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
|
||||
|
@ -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 (): Readonly<Record<string, string | undefined>>
|
||||
export function useRequestHeaders (include?) {
|
||||
export function useRequestHeaders (include?: any[]) {
|
||||
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 }
|
||||
return Object.fromEntries(include.filter(key => headers[key]).map(key => [key, headers[key]]))
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import { useNuxtApp } from '#app'
|
||||
*/
|
||||
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> (...args): Ref<T> {
|
||||
export function useState <T> (...args: any): Ref<T> {
|
||||
const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined
|
||||
if (typeof args[0] !== 'string') { args.unshift(autoKey) }
|
||||
const [_key, init] = args as [string, (() => T | Ref<T>)]
|
||||
|
@ -1,3 +0,0 @@
|
||||
import { isRef, ref, Ref } from 'vue'
|
||||
|
||||
export const wrapInRef = <T> (value: T | Ref<T>) => isRef(value) ? value : ref(value)
|
@ -35,7 +35,7 @@ if (process.server) {
|
||||
await nuxt.hooks.callHook('app:created', vueApp)
|
||||
} catch (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
|
||||
@ -66,7 +66,7 @@ if (process.client) {
|
||||
await applyPlugins(nuxt, plugins)
|
||||
} catch (err) {
|
||||
await nuxt.callHook('app:error', err)
|
||||
nuxt.payload.error = nuxt.payload.error || err
|
||||
nuxt.payload.error = (nuxt.payload.error || err) as any
|
||||
}
|
||||
|
||||
try {
|
||||
@ -77,11 +77,11 @@ if (process.client) {
|
||||
await nextTick()
|
||||
} catch (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
|
||||
})
|
||||
}
|
||||
|
@ -38,6 +38,23 @@ export interface RuntimeNuxtHooks {
|
||||
'vue:error': (...args: Parameters<Parameters<typeof onErrorCaptured>[0]>) => HookResult
|
||||
}
|
||||
|
||||
export interface NuxtSSRContext extends SSRContext {
|
||||
url: string
|
||||
event: CompatibilityEvent
|
||||
/** @deprecated Use `event` instead. */
|
||||
req?: CompatibilityEvent['req']
|
||||
/** @deprecated Use `event` instead. */
|
||||
res?: CompatibilityEvent['res']
|
||||
runtimeConfig: RuntimeConfig
|
||||
noSSR: boolean
|
||||
/** whether we are rendering an SSR error */
|
||||
error?: boolean
|
||||
nuxt: _NuxtApp
|
||||
payload: _NuxtApp['payload']
|
||||
teleports?: Record<string, string>
|
||||
renderMeta?: () => Promise<NuxtMeta> | NuxtMeta
|
||||
}
|
||||
|
||||
interface _NuxtApp {
|
||||
vueApp: App<Element>
|
||||
globalName: string
|
||||
@ -48,28 +65,13 @@ interface _NuxtApp {
|
||||
|
||||
[key: string]: any
|
||||
|
||||
_asyncDataPromises?: Record<string, Promise<any>>
|
||||
_asyncDataPromises: Record<string, Promise<any> | undefined>
|
||||
|
||||
ssrContext?: SSRContext & {
|
||||
url: string
|
||||
event: CompatibilityEvent
|
||||
/** @deprecated Use `event` instead. */
|
||||
req?: CompatibilityEvent['req']
|
||||
/** @deprecated Use `event` instead. */
|
||||
res?: CompatibilityEvent['res']
|
||||
runtimeConfig: RuntimeConfig
|
||||
noSSR: boolean
|
||||
/** whether we are rendering an SSR error */
|
||||
error?: boolean
|
||||
nuxt: _NuxtApp
|
||||
payload: _NuxtApp['payload']
|
||||
teleports?: Record<string, string>
|
||||
renderMeta?: () => Promise<NuxtMeta> | NuxtMeta
|
||||
}
|
||||
ssrContext?: NuxtSSRContext
|
||||
payload: {
|
||||
serverRendered?: boolean
|
||||
data?: Record<string, any>
|
||||
state?: Record<string, any>
|
||||
data: Record<string, any>
|
||||
state: Record<string, any>
|
||||
rendered?: Function
|
||||
error?: Error | {
|
||||
url: string
|
||||
@ -135,24 +137,24 @@ export function createNuxtApp (options: CreateOptions) {
|
||||
}
|
||||
// Expose to server renderer to create window.__NUXT__
|
||||
nuxtApp.ssrContext = nuxtApp.ssrContext || {} as any
|
||||
if (nuxtApp.ssrContext.payload) {
|
||||
Object.assign(nuxtApp.payload, nuxtApp.ssrContext.payload)
|
||||
if (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
|
||||
nuxtApp.payload.config = {
|
||||
public: options.ssrContext.runtimeConfig.public,
|
||||
app: options.ssrContext.runtimeConfig.app
|
||||
public: options.ssrContext!.runtimeConfig.public,
|
||||
app: options.ssrContext!.runtimeConfig.app
|
||||
}
|
||||
}
|
||||
|
||||
// Expose runtime config
|
||||
const runtimeConfig = process.server
|
||||
? options.ssrContext.runtimeConfig
|
||||
? options.ssrContext!.runtimeConfig
|
||||
: reactive(nuxtApp.payload.config)
|
||||
|
||||
// Backward compatibilty following #4254
|
||||
// Backward compatibility following #4254
|
||||
const compatibilityConfig = new Proxy(runtimeConfig, {
|
||||
get (target, prop) {
|
||||
if (prop === 'public') {
|
||||
@ -192,9 +194,9 @@ export async function applyPlugins (nuxtApp: NuxtApp, plugins: Plugin[]) {
|
||||
}
|
||||
|
||||
export function normalizePlugins (_plugins: Plugin[]) {
|
||||
const unwrappedPlugins = []
|
||||
const legacyInjectPlugins = []
|
||||
const invalidPlugins = []
|
||||
const unwrappedPlugins: Plugin[] = []
|
||||
const legacyInjectPlugins: Plugin[] = []
|
||||
const invalidPlugins: Plugin[] = []
|
||||
|
||||
const plugins = _plugins.map((plugin) => {
|
||||
if (typeof plugin !== 'function') {
|
||||
|
@ -6,7 +6,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
if (logs.length > 0) {
|
||||
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)
|
||||
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
|
||||
console.groupEnd && console.groupEnd()
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
el.style.transition = 'width 0.1s, opacity 0.4s'
|
||||
const duration = 3000
|
||||
const progress = 10000 / Math.floor(duration)
|
||||
let timeout
|
||||
let interval
|
||||
let timeout: ReturnType<typeof setTimeout> | undefined
|
||||
let interval: ReturnType<typeof setInterval> | undefined
|
||||
nuxtApp.hook('page:start', () => {
|
||||
if (timeout) { return }
|
||||
timeout = setTimeout(() => {
|
||||
@ -30,9 +30,9 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
})
|
||||
nuxtApp.hook('page:finish', () => {
|
||||
timeout && clearTimeout(timeout)
|
||||
timeout = null
|
||||
timeout = undefined
|
||||
interval && clearInterval(interval)
|
||||
interval = null
|
||||
interval = undefined
|
||||
el.style.width = '100%'
|
||||
el.style.opacity = '0%'
|
||||
setTimeout(() => {
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { reactive, h } from 'vue'
|
||||
import { parseURL, parseQuery, withoutBase, isEqual, joinURL } from 'ufo'
|
||||
import { createError } from 'h3'
|
||||
import { defineNuxtPlugin } from '..'
|
||||
import { defineNuxtPlugin, clearError, navigateTo, showError, useRuntimeConfig } from '..'
|
||||
import { callWithNuxt } from '../nuxt'
|
||||
import { clearError, navigateTo, showError, useRuntimeConfig } from '#app'
|
||||
// @ts-ignore
|
||||
import { globalMiddleware } from '#build/middleware'
|
||||
|
||||
@ -90,8 +89,9 @@ interface Router {
|
||||
export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
|
||||
const initialURL = process.client
|
||||
? withoutBase(window.location.pathname, useRuntimeConfig().app.baseURL) + window.location.search + window.location.hash
|
||||
: nuxtApp.ssrContext.url
|
||||
const routes = []
|
||||
: nuxtApp.ssrContext!.url
|
||||
|
||||
const routes: Route[] = []
|
||||
|
||||
const hooks: { [key in keyof RouterHooks]: RouterHooks[key][] } = {
|
||||
'navigate:before': [],
|
||||
@ -194,7 +194,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
|
||||
const route = router.resolve(props.to)
|
||||
return props.custom
|
||||
? 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)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -24,7 +24,7 @@ export default defineNuxtModule<Partial<AutoImportsOptions>>({
|
||||
// Allow modules extending sources
|
||||
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) {
|
||||
i.imports = i.names
|
||||
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
|
||||
let composablesDirs = []
|
||||
let composablesDirs: string[] = []
|
||||
for (const layer of nuxt.options._layers) {
|
||||
composablesDirs.push(resolve(layer.config.srcDir, 'composables'))
|
||||
for (const dir of (layer.config.autoImports?.dirs ?? [])) {
|
||||
if (!dir) {
|
||||
continue
|
||||
}
|
||||
composablesDirs.push(resolve(layer.config.srcDir, dir))
|
||||
}
|
||||
}
|
||||
@ -123,7 +126,7 @@ function addDeclarationTemplates (ctx: Unimport) {
|
||||
// Remove file extension for benefit of TypeScript
|
||||
const stripExtension = (path: string) => path.replace(/\.[a-z]+$/, '')
|
||||
|
||||
const resolved = {}
|
||||
const resolved: Record<string, string> = {}
|
||||
const r = ({ from }: Import) => {
|
||||
if (resolved[from]) {
|
||||
return resolved[from]
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { promises as fsp } from 'node:fs'
|
||||
import { dirname, resolve } from 'pathe'
|
||||
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 * as defaultTemplates from './templates'
|
||||
@ -13,7 +13,7 @@ export function createApp (nuxt: Nuxt, options: Partial<NuxtApp> = {}): NuxtApp
|
||||
extensions: nuxt.options.extensions,
|
||||
plugins: [],
|
||||
templates: []
|
||||
} as NuxtApp)
|
||||
} as unknown as NuxtApp) as 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)
|
||||
|
||||
// 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
|
||||
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) => {
|
||||
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
|
||||
|
||||
const aliasPath = '#build/' + template.filename.replace(/\.\w+$/, '')
|
||||
const aliasPath = '#build/' + template.filename!.replace(/\.\w+$/, '')
|
||||
nuxt.vfs[aliasPath] = contents
|
||||
|
||||
// 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)) {
|
||||
app.plugins.push(...[
|
||||
...(config.plugins || []),
|
||||
...await resolveFiles(config.srcDir, [
|
||||
'plugins/*.{ts,js,mjs,cjs,mts,cts}',
|
||||
'plugins/*/index.*{ts,js,mjs,cjs,mts,cts}'
|
||||
])
|
||||
...config.srcDir
|
||||
? await resolveFiles(config.srcDir, [
|
||||
'plugins/*.{ts,js,mjs,cjs,mts,cts}',
|
||||
'plugins/*/index.*{ts,js,mjs,cjs,mts,cts}'
|
||||
])
|
||||
: []
|
||||
].map(plugin => normalizePlugin(plugin as NuxtPlugin)))
|
||||
}
|
||||
|
||||
|
@ -15,10 +15,10 @@ export async function build (nuxt: Nuxt) {
|
||||
nuxt.hook('builder:watch', async (event, path) => {
|
||||
if (event !== 'change' && /^(app\.|error\.|plugins\/|middleware\/|layouts\/)/i.test(path)) {
|
||||
if (path.startsWith('app')) {
|
||||
app.mainComponent = null
|
||||
app.mainComponent = undefined
|
||||
}
|
||||
if (path.startsWith('error')) {
|
||||
app.errorComponent = null
|
||||
app.errorComponent = undefined
|
||||
}
|
||||
await generateApp()
|
||||
}
|
||||
@ -38,7 +38,7 @@ export async function build (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,
|
||||
cwd: nuxt.options.srcDir,
|
||||
ignoreInitial: true,
|
||||
@ -61,7 +61,7 @@ async function bundle (nuxt: Nuxt) {
|
||||
: nuxt.options.builder
|
||||
|
||||
return bundle(nuxt)
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
await nuxt.callHook('build:error', error)
|
||||
|
||||
if (error.toString().includes('Cannot find module \'@nuxt/webpack-builder\'')) {
|
||||
|
@ -20,6 +20,7 @@ export const addModuleTranspiles = (opts: AddModuleTranspilesOptions = {}) => {
|
||||
// Try to sanitize modules to better match imports
|
||||
nuxt.options.build.transpile =
|
||||
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) {
|
||||
return nuxt.options.build.transpile.some(t => !(t instanceof Function) && (t instanceof RegExp ? t.test(mod) : new RegExp(t).test(mod)))
|
||||
|
@ -22,7 +22,7 @@ export async function initNitro (nuxt: Nuxt) {
|
||||
dev: nuxt.options.dev,
|
||||
preset: nuxt.options.dev ? 'nitro-dev' : undefined,
|
||||
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'),
|
||||
errorHandler: resolve(distDir, 'core/runtime/nitro/error'),
|
||||
nodeModulesDirs: nuxt.options.modulesDir,
|
||||
@ -49,7 +49,7 @@ export async function initNitro (nuxt: Nuxt) {
|
||||
],
|
||||
prerender: {
|
||||
crawlLinks: nuxt.options._generate ? nuxt.options.generate.crawler : false,
|
||||
routes: []
|
||||
routes: ([] as string[])
|
||||
.concat(nuxt.options._generate ? ['/', ...nuxt.options.generate.routes] : [])
|
||||
.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`
|
||||
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
|
||||
nitroConfig.rollupConfig.plugins.push(ImportProtectionPlugin.rollup({
|
||||
nitroConfig.rollupConfig!.plugins!.push(ImportProtectionPlugin.rollup({
|
||||
rootDir: nuxt.options.rootDir,
|
||||
patterns: [
|
||||
...['#app', /^#build(\/|$)/]
|
||||
|
@ -81,7 +81,7 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
|
||||
// Transpile layers within node_modules
|
||||
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
|
||||
@ -95,7 +95,7 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
// Add <NuxtWelcome>
|
||||
addComponent({
|
||||
name: 'NuxtWelcome',
|
||||
filePath: tryResolveModule('@nuxt/ui-templates/templates/welcome.vue')
|
||||
filePath: tryResolveModule('@nuxt/ui-templates/templates/welcome.vue')!
|
||||
})
|
||||
|
||||
addComponent({
|
||||
@ -165,7 +165,7 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
|
||||
transform: {
|
||||
include: options._layers
|
||||
.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'))
|
||||
|
@ -1,16 +1,14 @@
|
||||
import { withQuery } from 'ufo'
|
||||
import type { NitroErrorHandler } from 'nitropack'
|
||||
import type { H3Error } from 'h3'
|
||||
// @ts-ignore TODO
|
||||
import { normalizeError, isJsonRequest } from '#internal/nitro/utils'
|
||||
import { NuxtApp } from '#app'
|
||||
|
||||
export default <NitroErrorHandler> async function errorhandler (error: H3Error, event) {
|
||||
// Parse and normalize error
|
||||
const { stack, statusCode, statusMessage, message } = normalizeError(error)
|
||||
|
||||
// Create an error object
|
||||
const errorObject: Exclude<NuxtApp['payload']['error'], Error> = {
|
||||
const errorObject = {
|
||||
url: event.req.url,
|
||||
statusCode,
|
||||
statusMessage,
|
||||
|
@ -1,20 +1,15 @@
|
||||
import { createRenderer } from 'vue-bundle-renderer/runtime'
|
||||
import type { RenderHandler, RenderResponse } from 'nitropack'
|
||||
import type { RenderResponse } from 'nitropack'
|
||||
import type { Manifest } from 'vite'
|
||||
import { CompatibilityEvent, getQuery } from 'h3'
|
||||
import { getQuery } from 'h3'
|
||||
import devalue from '@nuxt/devalue'
|
||||
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
|
||||
import { buildAssetsURL } from '#paths'
|
||||
|
||||
export type NuxtSSRContext = NuxtApp['ssrContext']
|
||||
|
||||
const defineRenderHandler = _defineRenderHandler as (h: RenderHandler) => CompatibilityEvent
|
||||
|
||||
export interface NuxtRenderHTMLContext {
|
||||
htmlAttrs: string[]
|
||||
head: string[]
|
||||
@ -31,10 +26,12 @@ export interface NuxtRenderResponse {
|
||||
headers: Record<string, string>
|
||||
}
|
||||
|
||||
interface ClientManifest {}
|
||||
|
||||
// @ts-ignore
|
||||
const getClientManifest: () => Promise<Manifest> = () => import('#build/dist/server/client.manifest.mjs')
|
||||
.then(r => r.default || r)
|
||||
.then(r => typeof r === 'function' ? r() : r)
|
||||
.then(r => typeof r === 'function' ? r() : r) as Promise<ClientManifest>
|
||||
|
||||
// @ts-ignore
|
||||
const getServerEntry = () => import('#build/dist/server/server.mjs').then(r => r.default || r)
|
||||
@ -57,7 +54,8 @@ const getSSRRenderer = lazyCachedFunction(async () => {
|
||||
// Create renderer
|
||||
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)
|
||||
// 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) {
|
||||
@ -84,14 +82,16 @@ const getSPARenderer = lazyCachedFunction(async () => {
|
||||
|
||||
const renderToString = (ssrContext: NuxtSSRContext) => {
|
||||
const config = useRuntimeConfig()
|
||||
ssrContext.payload = {
|
||||
ssrContext!.payload = {
|
||||
serverRendered: false,
|
||||
config: {
|
||||
public: config.public,
|
||||
app: config.app
|
||||
}
|
||||
},
|
||||
data: {},
|
||||
state: {}
|
||||
}
|
||||
ssrContext.renderMeta = ssrContext.renderMeta ?? (() => ({}))
|
||||
ssrContext!.renderMeta = ssrContext!.renderMeta ?? (() => ({}))
|
||||
return Promise.resolve(result)
|
||||
}
|
||||
|
||||
@ -109,23 +109,25 @@ export default defineRenderHandler(async (event) => {
|
||||
event,
|
||||
req: event.req,
|
||||
res: event.res,
|
||||
runtimeConfig: useRuntimeConfig(),
|
||||
runtimeConfig: useRuntimeConfig() as NuxtSSRContext['runtimeConfig'],
|
||||
noSSR: !!event.req.headers['x-nuxt-no-ssr'],
|
||||
error: !!ssrError,
|
||||
nuxt: undefined, /* NuxtApp */
|
||||
payload: ssrError ? { error: ssrError } : undefined
|
||||
nuxt: undefined!, /* NuxtApp */
|
||||
payload: ssrError ? { error: ssrError } as NuxtSSRContext['payload'] : undefined!
|
||||
}
|
||||
|
||||
// Render app
|
||||
const renderer = (process.env.NUXT_NO_SSR || ssrContext.noSSR) ? await getSPARenderer() : await getSSRRenderer()
|
||||
const _rendered = await renderer.renderToString(ssrContext).catch((err) => {
|
||||
if (!ssrError) { throw err }
|
||||
if (!ssrError) {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
await ssrContext.nuxt?.hooks.callHook('app:rendered', { ssrContext })
|
||||
|
||||
// Handle errors
|
||||
if (!_rendered) {
|
||||
return
|
||||
return undefined!
|
||||
}
|
||||
if (ssrContext.payload?.error && !ssrError) {
|
||||
throw ssrContext.payload.error
|
||||
@ -143,7 +145,7 @@ export default defineRenderHandler(async (event) => {
|
||||
_rendered.renderStyles(),
|
||||
ssrContext.styles
|
||||
]),
|
||||
bodyAttrs: normalizeChunks([renderedMeta.bodyAttrs]),
|
||||
bodyAttrs: normalizeChunks([renderedMeta.bodyAttrs!]),
|
||||
bodyPreprend: normalizeChunks([
|
||||
renderedMeta.bodyScriptsPrepend,
|
||||
ssrContext.teleports?.body
|
||||
@ -188,8 +190,8 @@ function lazyCachedFunction <T> (fn: () => Promise<T>): () => Promise<T> {
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeChunks (chunks: string[]) {
|
||||
return chunks.filter(Boolean).map(i => i.trim())
|
||||
function normalizeChunks (chunks: (string | undefined)[]) {
|
||||
return chunks.filter(Boolean).map(i => i!.trim())
|
||||
}
|
||||
|
||||
function joinTags (tags: string[]) {
|
||||
|
@ -11,7 +11,7 @@ export interface TemplateContext {
|
||||
app: NuxtApp
|
||||
}
|
||||
|
||||
export const vueShim = {
|
||||
export const vueShim: NuxtTemplate = {
|
||||
filename: 'types/vue-shim.d.ts',
|
||||
getContents: () =>
|
||||
[
|
||||
@ -24,29 +24,29 @@ export const vueShim = {
|
||||
}
|
||||
|
||||
// TODO: Use an alias
|
||||
export const appComponentTemplate = {
|
||||
export const appComponentTemplate: NuxtTemplate<TemplateContext> = {
|
||||
filename: 'app-component.mjs',
|
||||
getContents: (ctx: TemplateContext) => genExport(ctx.app.mainComponent, ['default'])
|
||||
getContents: ctx => genExport(ctx.app.mainComponent!, ['default'])
|
||||
}
|
||||
// TODO: Use an alias
|
||||
export const rootComponentTemplate = {
|
||||
export const rootComponentTemplate: NuxtTemplate<TemplateContext> = {
|
||||
filename: 'root-component.mjs',
|
||||
getContents: (ctx: TemplateContext) => genExport(ctx.app.rootComponent, ['default'])
|
||||
getContents: ctx => genExport(ctx.app.rootComponent!, ['default'])
|
||||
}
|
||||
// TODO: Use an alias
|
||||
export const errorComponentTemplate = {
|
||||
export const errorComponentTemplate: NuxtTemplate<TemplateContext> = {
|
||||
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',
|
||||
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',
|
||||
getContents (ctx: TemplateContext) {
|
||||
getContents (ctx) {
|
||||
const clientPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'server')
|
||||
const exports: string[] = []
|
||||
const imports: string[] = []
|
||||
@ -63,9 +63,9 @@ export const clientPluginTemplate = {
|
||||
}
|
||||
}
|
||||
|
||||
export const serverPluginTemplate = {
|
||||
export const serverPluginTemplate: NuxtTemplate<TemplateContext> = {
|
||||
filename: 'plugins/server.mjs',
|
||||
getContents (ctx: TemplateContext) {
|
||||
getContents (ctx) {
|
||||
const serverPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'client')
|
||||
const exports: string[] = ['preload']
|
||||
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',
|
||||
getContents: (ctx: TemplateContext) => {
|
||||
getContents: (ctx) => {
|
||||
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, ''))
|
||||
|
||||
@ -111,9 +111,9 @@ export { }
|
||||
}
|
||||
|
||||
const adHocModules = ['router', 'pages', 'auto-imports', 'meta', 'components']
|
||||
export const schemaTemplate = {
|
||||
export const schemaTemplate: NuxtTemplate<TemplateContext> = {
|
||||
filename: 'types/schema.d.ts',
|
||||
getContents: ({ nuxt }: TemplateContext) => {
|
||||
getContents: ({ nuxt }) => {
|
||||
const moduleInfo = nuxt.options._installedModules.map(m => ({
|
||||
...m.meta || {},
|
||||
importName: m.entryPath || m.meta?.name
|
||||
@ -149,9 +149,9 @@ export const schemaTemplate = {
|
||||
}
|
||||
|
||||
// Add layouts template
|
||||
export const layoutTemplate: NuxtTemplate = {
|
||||
export const layoutTemplate: NuxtTemplate<TemplateContext> = {
|
||||
filename: 'layouts.mjs',
|
||||
getContents ({ app }: TemplateContext) {
|
||||
getContents ({ app }) {
|
||||
const layoutsObject = genObjectFromRawEntries(Object.values(app.layouts).map(({ name, file }) => {
|
||||
return [name, `defineAsyncComponent(${genDynamicImport(file)})`]
|
||||
}))
|
||||
@ -163,9 +163,9 @@ export const layoutTemplate: NuxtTemplate = {
|
||||
}
|
||||
|
||||
// Add middleware template
|
||||
export const middlewareTemplate: NuxtTemplate = {
|
||||
export const middlewareTemplate: NuxtTemplate<TemplateContext> = {
|
||||
filename: 'middleware.mjs',
|
||||
getContents ({ app }: TemplateContext) {
|
||||
getContents ({ app }) {
|
||||
const globalMiddleware = 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)]))
|
||||
@ -184,7 +184,7 @@ export const useRuntimeConfig = () => window?.__NUXT__?.config || {}
|
||||
`
|
||||
}
|
||||
|
||||
export const publicPathTemplate: NuxtTemplate = {
|
||||
export const publicPathTemplate: NuxtTemplate<TemplateContext> = {
|
||||
filename: 'paths.mjs',
|
||||
getContents ({ nuxt }) {
|
||||
return [
|
||||
|
@ -20,20 +20,20 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
}
|
||||
|
||||
if (process.server) {
|
||||
nuxtApp.ssrContext.renderMeta = async () => {
|
||||
nuxtApp.ssrContext!.renderMeta = async () => {
|
||||
// @ts-ignore
|
||||
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)
|
||||
|
||||
return {
|
||||
htmlAttrs: nuxtApp.ssrContext.teleports.htmlAttrs || '',
|
||||
headAttrs: nuxtApp.ssrContext.teleports.headAttrs || '',
|
||||
bodyAttrs: nuxtApp.ssrContext.teleports.bodyAttrs || '',
|
||||
headTags: nuxtApp.ssrContext.teleports.head || '',
|
||||
bodyScriptsPrepend: nuxtApp.ssrContext.teleports['body-prepend'] || '',
|
||||
bodyScripts: nuxtApp.ssrContext.teleports.body || ''
|
||||
htmlAttrs: nuxtApp.ssrContext!.teleports.htmlAttrs || '',
|
||||
headAttrs: nuxtApp.ssrContext!.teleports.headAttrs || '',
|
||||
bodyAttrs: nuxtApp.ssrContext!.teleports.bodyAttrs || '',
|
||||
headTags: nuxtApp.ssrContext!.teleports.head || '',
|
||||
bodyScriptsPrepend: nuxtApp.ssrContext!.teleports['body-prepend'] || '',
|
||||
bodyScripts: nuxtApp.ssrContext!.teleports.body || ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
}
|
||||
|
||||
if (process.server) {
|
||||
nuxtApp.ssrContext.renderMeta = () => {
|
||||
nuxtApp.ssrContext!.renderMeta = () => {
|
||||
const meta = renderHeadToString(head)
|
||||
return {
|
||||
...meta,
|
||||
|
@ -34,6 +34,6 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
|
||||
for (const name in Components) {
|
||||
// eslint-disable-next-line import/namespace
|
||||
nuxtApp.vueApp.component(name, Components[name])
|
||||
nuxtApp.vueApp.component(name, (Components as any)[name])
|
||||
}
|
||||
})
|
||||
|
@ -47,7 +47,7 @@ export default defineNuxtModule({
|
||||
|
||||
nuxt.hook('app:resolve', (app) => {
|
||||
// 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')
|
||||
}
|
||||
})
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 { useNuxtApp } from '#app'
|
||||
@ -58,6 +58,7 @@ export default defineComponent({
|
||||
const defaultPageTransition = { name: 'page', mode: 'out-in' }
|
||||
|
||||
const Component = defineComponent({
|
||||
// TODO: Type props
|
||||
// eslint-disable-next-line vue/require-prop-types
|
||||
props: ['routeProps', 'pageKey'],
|
||||
setup (props) {
|
||||
@ -66,9 +67,9 @@ const Component = defineComponent({
|
||||
const previousRoute = props.routeProps.route
|
||||
|
||||
// Provide a reactive route within the page
|
||||
const route = {}
|
||||
const route = {} as RouteLocation
|
||||
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))
|
||||
|
@ -59,7 +59,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
||||
? createWebHistory(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({
|
||||
...routerOptions,
|
||||
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
|
||||
const route = {}
|
||||
const route = {} as RouteLocation
|
||||
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)
|
||||
@ -109,7 +109,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
||||
}
|
||||
|
||||
await router.isReady()
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
// We'll catch 404s here
|
||||
callWithNuxt(nuxtApp, showError, [error])
|
||||
}
|
||||
@ -133,7 +133,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
||||
}
|
||||
|
||||
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 (process.dev) {
|
||||
@ -169,7 +169,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
||||
statusMessage: `Page not found: ${to.fullPath}`
|
||||
})])
|
||||
} 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) {
|
||||
const currentURL = to.fullPath || '/'
|
||||
if (!isEqual(currentURL, initialURL)) {
|
||||
@ -185,7 +185,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
||||
name: undefined, // #4920, #$4982
|
||||
force: true
|
||||
})
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
// We'll catch middleware errors or deliberate exceptions here
|
||||
callWithNuxt(nuxtApp, showError, [error])
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ const interpolatePath = (route: RouteLocationNormalizedLoaded, match: RouteLocat
|
||||
}
|
||||
|
||||
export const generateRouteKey = (override: string | ((route: RouteLocationNormalizedLoaded) => string), routeProps: RouterViewSlotProps) => {
|
||||
const matchedRoute = routeProps.route.matched.find(m => m.components.default === routeProps.Component.type)
|
||||
const source = override ?? matchedRoute?.meta.key ?? interpolatePath(routeProps.route, matchedRoute)
|
||||
const matchedRoute = routeProps.route.matched.find(m => m.components?.default === routeProps.Component.type)
|
||||
const source = override ?? matchedRoute?.meta.key ?? (matchedRoute && interpolatePath(routeProps.route, matchedRoute))
|
||||
return typeof source === 'function' ? source(routeProps.route) : source
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ export function generateRoutesFromFiles (files: string[], pagesDir: string): Nux
|
||||
// ex: parent.vue + parent/child.vue
|
||||
const child = parent.find(parentRoute => parentRoute.name === route.name && !parentRoute.path.endsWith('(.*)*'))
|
||||
|
||||
if (child) {
|
||||
if (child && child.children) {
|
||||
parent = child.children
|
||||
route.path = ''
|
||||
} else if (segmentName === '404' && isSingleSegment) {
|
||||
@ -213,11 +213,11 @@ function prepareRoutes (routes: NuxtPage[], parent?: NuxtPage) {
|
||||
route.path = route.path.slice(1)
|
||||
}
|
||||
|
||||
if (route.children.length) {
|
||||
if (route.children?.length) {
|
||||
route.children = prepareRoutes(route.children, route)
|
||||
}
|
||||
|
||||
if (route.children.find(childRoute => childRoute.path === '')) {
|
||||
if (route.children?.find(childRoute => childRoute.path === '')) {
|
||||
delete route.name
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ vi.mock('vue', async () => {
|
||||
return {
|
||||
...vue,
|
||||
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 }) })
|
||||
}))
|
||||
|
||||
// Helpers for test lisibility
|
||||
// Helpers for test visibility
|
||||
const EXTERNAL = 'a'
|
||||
const INTERNAL = 'RouterLink'
|
||||
|
||||
@ -44,7 +44,7 @@ describe('nuxt-link:to', () => {
|
||||
})
|
||||
|
||||
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')
|
||||
// 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', () => {
|
||||
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)
|
||||
// TODO: Uncomment when `dev` mode for tests is available
|
||||
|
11
packages/nuxt/tsconfig.json
Normal file
11
packages/nuxt/tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"noImplicitAny": true
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*.ts",
|
||||
"./test/**/*.ts"
|
||||
]
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import { ConfigSchema } from '../../schema/config'
|
||||
import type { ResolvedConfig } from 'c12'
|
||||
import type { UserConfig as ViteUserConfig } from 'vite'
|
||||
import type { Options as VuePluginOptions } from '@vitejs/plugin-vue'
|
||||
|
||||
@ -12,9 +11,20 @@ export interface NuxtConfig extends DeepPartial<Omit<ConfigSchema, 'vite'>> {
|
||||
[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.*` */
|
||||
export interface NuxtOptions extends ConfigSchema {
|
||||
_layers: ResolvedConfig<NuxtConfig>[]
|
||||
_layers: NuxtConfigLayer[]
|
||||
}
|
||||
|
||||
type RuntimeConfigNamespace = Record<string, any>
|
||||
|
@ -24,7 +24,7 @@ export interface Nuxt {
|
||||
vfs: Record<string, string>
|
||||
}
|
||||
|
||||
export interface NuxtTemplate {
|
||||
export interface NuxtTemplate<Options = Record<string, any>> {
|
||||
/** @deprecated filename */
|
||||
fileName?: string
|
||||
/** @deprecated whether template is custom or a nuxt core template */
|
||||
@ -51,9 +51,9 @@ export interface NuxtPlugin {
|
||||
}
|
||||
|
||||
export interface NuxtApp {
|
||||
mainComponent?: string
|
||||
rootComponent?: string
|
||||
errorComponent?: string
|
||||
mainComponent?: string | null
|
||||
rootComponent?: string | null
|
||||
errorComponent?: string | null
|
||||
dir: string
|
||||
extensions: string[]
|
||||
plugins: NuxtPlugin[]
|
||||
|
2
test/fixtures/basic/types.ts
vendored
2
test/fixtures/basic/types.ts
vendored
@ -4,8 +4,8 @@ import type { Ref } from 'vue'
|
||||
|
||||
import { NavigationFailure, RouteLocationNormalizedLoaded, RouteLocationRaw, useRouter as vueUseRouter } from 'vue-router'
|
||||
import { defineNuxtConfig } from '~~/../../../packages/nuxt/src'
|
||||
import { useRouter } from '#imports'
|
||||
import { isVue3 } from '#app'
|
||||
import { useRouter } from '#imports'
|
||||
|
||||
interface TestResponse { message: string }
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
"moduleResolution": "Node",
|
||||
"strict": false,
|
||||
"allowJs": true,
|
||||
"noEmit": true,
|
||||
"noUnusedLocals": true,
|
||||
"resolveJsonModule": true,
|
||||
"types": [
|
||||
@ -23,6 +24,12 @@
|
||||
"#head": [
|
||||
"./packages/nuxt/src/head/runtime/index"
|
||||
],
|
||||
"#internal/nitro": [
|
||||
"./node_modules/nitropack/dist/runtime"
|
||||
],
|
||||
"#internal/nitro/utils": [
|
||||
"./node_modules/nitropack/dist/runtime/utils"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
|
Loading…
Reference in New Issue
Block a user