refactor(nuxt, kit): improve type strictness (#6685)

This commit is contained in:
Anthony Fu 2022-08-22 18:12:02 +08:00 committed by GitHub
parent cdc75373d4
commit e1e39b7e79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 139 additions and 98 deletions

View File

@ -11,7 +11,7 @@ export function isIgnored (pathname: string): boolean {
// Happens with CLI reloads
if (!nuxt) {
return null
return false
}
if (!nuxt._ignore) {
@ -28,5 +28,5 @@ export function isIgnored (pathname: string): boolean {
if (relativePath.startsWith('..')) {
return false
}
return relativePath && nuxt._ignore.ignores(relativePath)
return !!(relativePath && nuxt._ignore.ignores(relativePath))
}

View File

@ -85,10 +85,10 @@ export function requireModulePkg (id: string, opts: RequireModuleOptions = {}) {
/** Resolve the path of a module. */
export function resolveModule (id: string, opts: ResolveModuleOptions = {}) {
return normalize(_require.resolve(id, {
paths: [].concat(
paths: ([] as string[]).concat(
// @ts-ignore
global.__NUXT_PREPATHS__,
opts.paths,
opts.paths || [],
process.cwd(),
// @ts-ignore
global.__NUXT_PATHS__
@ -100,8 +100,8 @@ export function resolveModule (id: string, opts: ResolveModuleOptions = {}) {
export function tryResolveModule (path: string, opts: ResolveModuleOptions = {}): string | null {
try {
return resolveModule(path, opts)
} catch (error) {
if (error.code !== 'MODULE_NOT_FOUND') {
} catch (error: any) {
if (error?.code !== 'MODULE_NOT_FOUND') {
throw error
}
}

View File

@ -15,11 +15,11 @@ export function parallel<T, R> (
return Promise.all(tasks.map(fn))
}
export function chainFn (base, fn) {
export function chainFn<Fn> (base: Fn, fn: Fn): Fn {
if (typeof fn !== 'function') {
return base
}
return function (...args) {
return function (this: any, ...args: any[]) {
if (typeof base !== 'function') {
return fn.apply(this, args)
}
@ -38,5 +38,5 @@ export function chainFn (base, fn) {
return baseResult
}
return fnResult
}
} as unknown as Fn
}

View File

@ -6,10 +6,10 @@ import { useNuxt } from './context'
import { logger } from './logger'
import { addTemplate } from './template'
export function addLayout (tmpl: NuxtTemplate, name?: string) {
export function addLayout (this: any, template: NuxtTemplate, name?: string) {
const nuxt = useNuxt()
const { filename, src } = addTemplate(tmpl)
const layoutName = kebabCase(name || parse(tmpl.filename).name).replace(/["']/g, '')
const { filename, src } = addTemplate(template)
const layoutName = kebabCase(name || parse(filename).name).replace(/["']/g, '')
if (isNuxt2(nuxt)) {
// Nuxt 2 adds layouts in options

View File

@ -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<NuxtConfig>({
const result = await loadConfig<NuxtConfig>({
name: 'nuxt',
configFile: 'nuxt.config',
rcFile: '.nuxtrc',
@ -15,6 +15,8 @@ export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<Nuxt
globalRc: true,
...opts
})
const { configFile, layers = [], cwd } = result
const nuxtConfig = result.config!
// Fill config
nuxtConfig.rootDir = nuxtConfig.rootDir || cwd
@ -25,7 +27,7 @@ export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<Nuxt
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)
layer.config.srcDir = resolve(layer.config.rootDir!, layer.config.srcDir!)
}
// Filter layers

View File

@ -29,7 +29,7 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
const nearestNuxtPkg = await Promise.all(['nuxt3', 'nuxt', 'nuxt-edge']
.map(pkg => resolvePackageJSON(pkg, { url: opts.cwd }).catch(() => null)))
.then(r => r.filter(Boolean).sort((a, b) => b.length - a.length)[0])
.then(r => (r.filter(Boolean) as string[]).sort((a, b) => b.length - a.length)[0])
if (!nearestNuxtPkg) {
throw new Error(`Cannot find any nuxt version from ${opts.cwd}`)
}

View File

@ -1,5 +1,5 @@
import { relative } from 'pathe'
import type { Nuxt, NuxtPluginTemplate, NuxtTemplate, ModuleContainer } from '@nuxt/schema'
import type { Nuxt, ModuleContainer } from '@nuxt/schema'
import { chainFn } from '../internal/task'
import { addTemplate } from '../template'
import { addLayout } from '../layout'
@ -12,11 +12,10 @@ import { installModule } from './install'
const MODULE_CONTAINER_KEY = '__module_container__'
export function useModuleContainer (nuxt: Nuxt = useNuxt()): ModuleContainer {
if (nuxt[MODULE_CONTAINER_KEY]) {
return nuxt[MODULE_CONTAINER_KEY]
}
// @ts-ignore
if (nuxt[MODULE_CONTAINER_KEY]) { return nuxt[MODULE_CONTAINER_KEY] }
async function requireModule (moduleOpts) {
async function requireModule (moduleOpts: any) {
let src, inlineOptions
if (typeof moduleOpts === 'string') {
src = moduleOpts
@ -35,7 +34,7 @@ export function useModuleContainer (nuxt: Nuxt = useNuxt()): ModuleContainer {
await installModule(src, inlineOptions)
}
nuxt[MODULE_CONTAINER_KEY] = <ModuleContainer>{
const container: ModuleContainer = {
nuxt,
options: nuxt.options,
@ -47,7 +46,7 @@ export function useModuleContainer (nuxt: Nuxt = useNuxt()): ModuleContainer {
addServerMiddleware,
addTemplate (template: string | NuxtTemplate) {
addTemplate (template) {
if (typeof template === 'string') {
template = { src: template }
}
@ -57,15 +56,15 @@ export function useModuleContainer (nuxt: Nuxt = useNuxt()): ModuleContainer {
return addTemplate(template)
},
addPlugin (pluginTemplate: NuxtPluginTemplate): NuxtPluginTemplate {
addPlugin (pluginTemplate) {
return addPluginTemplate(pluginTemplate)
},
addLayout (tmpl: NuxtTemplate, name?: string) {
addLayout (tmpl, name) {
return addLayout(tmpl, name)
},
addErrorLayout (dst: string) {
addErrorLayout (dst) {
const relativeBuildDir = relative(nuxt.options.rootDir, nuxt.options.buildDir)
nuxt.options.ErrorPage = `~/${relativeBuildDir}/${dst}`
},
@ -93,5 +92,9 @@ export function useModuleContainer (nuxt: Nuxt = useNuxt()): ModuleContainer {
}
}
// @ts-ignore
nuxt[MODULE_CONTAINER_KEY] = container
// @ts-ignore
return nuxt[MODULE_CONTAINER_KEY]
}

View File

@ -2,7 +2,7 @@ import { promises as fsp } from 'node:fs'
import defu from 'defu'
import { applyDefaults } from 'untyped'
import { dirname } from 'pathe'
import type { Nuxt, NuxtTemplate, NuxtModule, ModuleOptions, ModuleDefinition } from '@nuxt/schema'
import type { Nuxt, NuxtModule, ModuleOptions, ModuleDefinition, NuxtOptions, ResolvedNuxtTemplate } from '@nuxt/schema'
import { logger } from '../logger'
import { useNuxt, nuxtCtx, tryUseNuxt } from '../context'
import { isNuxt2, checkNuxtCompatibility } from '../compatibility'
@ -31,9 +31,9 @@ export function defineNuxtModule<OptionsT extends ModuleOptions> (definition: Mo
// Resolves module options from inline options, [configKey] in nuxt.config, defaults and schema
function getOptions (inlineOptions?: OptionsT, nuxt: Nuxt = useNuxt()) {
const configKey = definition.meta.configKey || definition.meta.name
const configKey = definition.meta!.configKey || definition.meta!.name!
const _defaults = definition.defaults instanceof Function ? definition.defaults(nuxt) : definition.defaults
let _options = defu(inlineOptions, nuxt.options[configKey], _defaults) as OptionsT
let _options = defu(inlineOptions, nuxt.options[configKey as keyof NuxtOptions], _defaults) as OptionsT
if (definition.schema) {
_options = applyDefaults(definition.schema, _options) as OptionsT
}
@ -41,13 +41,13 @@ export function defineNuxtModule<OptionsT extends ModuleOptions> (definition: Mo
}
// Module format is always a simple function
async function normalizedModule (inlineOptions: OptionsT, nuxt: Nuxt) {
async function normalizedModule (this: any, inlineOptions: OptionsT, nuxt: Nuxt) {
if (!nuxt) {
nuxt = tryUseNuxt() || this.nuxt /* invoked by nuxt 2 */
}
// Avoid duplicate installs
const uniqueKey = definition.meta.name || definition.meta.configKey
const uniqueKey = definition.meta!.name || definition.meta!.configKey
if (uniqueKey) {
nuxt.options._requiredModules = nuxt.options._requiredModules || {}
if (nuxt.options._requiredModules[uniqueKey]) {
@ -58,10 +58,10 @@ export function defineNuxtModule<OptionsT extends ModuleOptions> (definition: Mo
}
// Check compatibility constraints
if (definition.meta.compatibility) {
const issues = await checkNuxtCompatibility(definition.meta.compatibility, nuxt)
if (definition.meta!.compatibility) {
const issues = await checkNuxtCompatibility(definition.meta!.compatibility, nuxt)
if (issues.length) {
logger.warn(`Module \`${definition.meta.name}\` is disabled due to incompatibility issues:\n${issues.toString()}`)
logger.warn(`Module \`${definition.meta!.name}\` is disabled due to incompatibility issues:\n${issues.toString()}`)
return
}
}
@ -78,7 +78,7 @@ export function defineNuxtModule<OptionsT extends ModuleOptions> (definition: Mo
}
// Call setup
await definition.setup?.call(null, _options, nuxt)
await definition.setup?.call(null as any, _options, nuxt)
}
// Define getters for options and meta
@ -92,11 +92,13 @@ export function defineNuxtModule<OptionsT extends ModuleOptions> (definition: Mo
const NUXT2_SHIMS_KEY = '__nuxt2_shims_key__'
function nuxt2Shims (nuxt: Nuxt) {
// Avoid duplicate install and only apply to Nuxt2
// @ts-ignore
if (!isNuxt2(nuxt) || nuxt[NUXT2_SHIMS_KEY]) { return }
// @ts-ignore
nuxt[NUXT2_SHIMS_KEY] = true
// Allow using nuxt.hooks
// @ts-ignore Nuxt 2 extends hookable
// @ts-expect-error Nuxt 2 extends hookable
nuxt.hooks = nuxt
// Allow using useNuxt()
@ -106,7 +108,7 @@ function nuxt2Shims (nuxt: Nuxt) {
}
// Support virtual templates with getContents() by writing them to .nuxt directory
let virtualTemplates: NuxtTemplate[]
let virtualTemplates: ResolvedNuxtTemplate[]
nuxt.hook('builder:prepared', (_builder, buildOptions) => {
virtualTemplates = buildOptions.templates.filter(t => t.getContents)
for (const template of virtualTemplates) {

View File

@ -10,7 +10,12 @@ export async function installModule (moduleToInstall: string | NuxtModule, _inli
const { nuxtModule, inlineOptions } = await normalizeModule(moduleToInstall, _inlineOptions)
// Call module
await nuxtModule.call(useModuleContainer(), inlineOptions, nuxt)
await nuxtModule.call(
// Provide this context for backwards compatibility with Nuxt 2
useModuleContainer() as any,
inlineOptions,
nuxt
)
nuxt.options._installedModules = nuxt.options._installedModules || []
nuxt.options._installedModules.push({

View File

@ -73,7 +73,7 @@ export function addPluginTemplate (plugin: NuxtPluginTemplate | string, opts: Ad
const normalizedPlugin: NuxtPlugin = typeof plugin === 'string'
? { src: plugin }
// Update plugin src to template destination
: { ...plugin, src: addTemplate(plugin).dst }
: { ...plugin, src: addTemplate(plugin).dst! }
return addPlugin(normalizedPlugin, opts)
}

View File

@ -115,7 +115,7 @@ export function resolveAlias (path: string, alias?: Record<string, string>): str
}
export interface Resolver {
resolve(...path): string
resolve(...path: string[]): string
resolvePath(path: string, opts?: ResolvePathOptions): Promise<string>
}

View File

@ -1,16 +1,16 @@
import { existsSync } from 'node:fs'
import { basename, parse, resolve } from 'pathe'
import hash from 'hash-sum'
import type { NuxtTemplate } from '@nuxt/schema'
import type { NuxtTemplate, ResolvedNuxtTemplate } from '@nuxt/schema'
import { useNuxt } from './context'
/**
* Renders given template using lodash template during build into the project buildDir
*/
export function addTemplate (_template: NuxtTemplate | string) {
export function addTemplate (_template: NuxtTemplate<any> | string) {
const nuxt = useNuxt()
// Noprmalize template
// Normalize template
const template = normalizeTemplate(_template)
// Remove any existing template with the same filename
@ -26,7 +26,7 @@ export function addTemplate (_template: NuxtTemplate | string) {
/**
* Normalize a nuxt template object
*/
export function normalizeTemplate (template: NuxtTemplate | string): NuxtTemplate {
export function normalizeTemplate (template: NuxtTemplate<any> | string): ResolvedNuxtTemplate<any> {
if (!template) {
throw new Error('Invalid template: ' + JSON.stringify(template))
}
@ -69,5 +69,5 @@ export function normalizeTemplate (template: NuxtTemplate | string): NuxtTemplat
template.dst = resolve(nuxt.options.buildDir, template.filename)
}
return template
return template as ResolvedNuxtTemplate<any>
}

View File

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

View File

@ -34,7 +34,7 @@ export const clearError = async (options: { redirect?: string } = {}) => {
error.value = null
}
export const isNuxtError = (err?: string | object): err is NuxtError => err && typeof err === 'object' && ('__nuxt_error' in err)
export const isNuxtError = (err?: string | object): err is NuxtError => !!(err && typeof err === 'object' && ('__nuxt_error' in err))
export const createError = (err: string | Partial<NuxtError>): NuxtError => {
const _err: NuxtError = _createError(err)

View File

@ -80,7 +80,7 @@ interface _NuxtApp {
message: string
description: string
data?: any
}
} | null
[key: string]: any
}

View File

@ -5,7 +5,7 @@ import { Unimport } from 'unimport'
import { AutoImportsOptions } from '@nuxt/schema'
import { normalize } from 'pathe'
export const TransformPlugin = createUnplugin(({ ctx, options, sourcemap }: {ctx: Unimport, options: Partial<AutoImportsOptions>, sourcemap?: boolean }) => {
export const TransformPlugin = createUnplugin(({ ctx, options, sourcemap }: { ctx: Unimport, options: Partial<AutoImportsOptions>, sourcemap?: boolean }) => {
return {
name: 'nuxt:auto-imports-transform',
enforce: 'post',
@ -48,7 +48,9 @@ export const TransformPlugin = createUnplugin(({ ctx, options, sourcemap }: {ctx
if (s.hasChanged()) {
return {
code: s.toString(),
map: sourcemap && s.generateMap({ source: id, includeContent: true })
map: sourcemap
? s.generateMap({ source: id, includeContent: true })
: undefined
}
}
}

View File

@ -94,7 +94,9 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => {
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap && s.generateMap({ source: id, includeContent: true })
map: options.sourcemap
? s.generateMap({ source: id, includeContent: true })
: undefined
}
}
}

View File

@ -9,7 +9,7 @@ import { TreeShakeTemplatePlugin } from './tree-shake'
const isPureObjectOrString = (val: any) => (!Array.isArray(val) && typeof val === 'object') || typeof val === 'string'
const isDirectory = (p: string) => { try { return statSync(p).isDirectory() } catch (_e) { return false } }
function compareDirByPathLength ({ path: pathA }, { path: pathB }) {
function compareDirByPathLength ({ path: pathA }: { path: string}, { path: pathB }: { path: string}) {
return pathB.split(/[\\/]/).filter(Boolean).length - pathA.split(/[\\/]/).filter(Boolean).length
}
@ -26,7 +26,7 @@ export default defineNuxtModule<ComponentsOptions>({
dirs: []
},
setup (componentOptions, nuxt) {
let componentDirs = []
let componentDirs: ComponentsDir[] = []
const context = {
components: [] as Component[]
}
@ -37,7 +37,7 @@ export default defineNuxtModule<ComponentsOptions>({
: context.components
}
const normalizeDirs = (dir: any, cwd: string) => {
const normalizeDirs = (dir: any, cwd: string): ComponentsDir[] => {
if (Array.isArray(dir)) {
return dir.map(dir => normalizeDirs(dir, cwd)).flat().sort(compareDirByPathLength)
}
@ -48,14 +48,14 @@ export default defineNuxtModule<ComponentsOptions>({
]
}
if (typeof dir === 'string') {
return {
path: resolve(cwd, resolveAlias(dir))
}
return [
{ path: resolve(cwd, resolveAlias(dir)) }
]
}
if (!dir) {
return []
}
const dirs = (dir.dirs || [dir]).map(dir => typeof dir === 'string' ? { path: dir } : dir).filter(_dir => _dir.path)
const dirs: ComponentsDir[] = (dir.dirs || [dir]).map((dir: any): ComponentsDir => typeof dir === 'string' ? { path: dir } : dir).filter((_dir: ComponentsDir) => _dir.path)
return dirs.map(_dir => ({
..._dir,
path: resolve(cwd, resolveAlias(_dir.path))
@ -113,7 +113,7 @@ export default defineNuxtModule<ComponentsOptions>({
// components.d.ts
addTemplate({ ...componentsTypeTemplate, options: { getComponents } })
// components.plugin.mjs
addPluginTemplate({ ...componentsPluginTemplate, options: { getComponents } })
addPluginTemplate({ ...componentsPluginTemplate, options: { getComponents } } as any)
// components.server.mjs
addTemplate({ ...componentsTemplate, filename: 'components.server.mjs', options: { getComponents, mode: 'server' } })
// components.client.mjs
@ -121,12 +121,12 @@ export default defineNuxtModule<ComponentsOptions>({
nuxt.hook('vite:extendConfig', (config, { isClient }) => {
const mode = isClient ? 'client' : 'server'
config.resolve.alias['#components'] = resolve(nuxt.options.buildDir, `components.${mode}.mjs`)
;(config.resolve!.alias as any)['#components'] = resolve(nuxt.options.buildDir, `components.${mode}.mjs`)
})
nuxt.hook('webpack:config', (configs) => {
for (const config of configs) {
const mode = config.name === 'server' ? 'server' : 'client'
config.resolve.alias['#components'] = resolve(nuxt.options.buildDir, `components.${mode}.mjs`)
;(config.resolve!.alias as any)['#components'] = resolve(nuxt.options.buildDir, `components.${mode}.mjs`)
}
})

View File

@ -58,12 +58,12 @@ export async function scanComponents (dirs: ComponentsDir[], srcDir: string): Pr
*
* @example third-components/index.vue -> third-component
* if not take the filename
* @example thid-components/Awesome.vue -> Awesome
* @example third-components/Awesome.vue -> Awesome
*/
let fileName = basename(filePath, extname(filePath))
const global = /\.(global)$/.test(fileName) || dir.global
const mode = fileName.match(/(?<=\.)(client|server)(\.global)?$/)?.[1] as 'client' | 'server' || 'all'
const mode = (fileName.match(/(?<=\.)(client|server)(\.global)?$/)?.[1] || 'all') as 'client' | 'server' | 'all'
fileName = fileName.replace(/(\.(client|server))?(\.global)?$/, '')
if (fileName.toLowerCase() === 'index') {

View File

@ -1,5 +1,5 @@
import { isAbsolute, relative } from 'pathe'
import type { Component, Nuxt } from '@nuxt/schema'
import type { Component, Nuxt, NuxtPluginTemplate, NuxtTemplate } from '@nuxt/schema'
import { genDynamicImport, genExport, genObjectFromRawEntries } from 'knitwork'
export interface ComponentsTemplateContext {
@ -25,9 +25,9 @@ const createImportMagicComments = (options: ImportMagicCommentsOptions) => {
].filter(Boolean).join(', ')
}
export const componentsPluginTemplate = {
export const componentsPluginTemplate: NuxtPluginTemplate<ComponentsTemplateContext> = {
filename: 'components.plugin.mjs',
getContents ({ options }: ComponentsTemplateContext) {
getContents ({ options }) {
const globalComponents = options.getComponents().filter(c => c.global === true)
return `import { defineAsyncComponent } from 'vue'
@ -50,9 +50,9 @@ export default defineNuxtPlugin(nuxtApp => {
}
}
export const componentsTemplate = {
export const componentsTemplate: NuxtTemplate<ComponentsTemplateContext> = {
// components.[server|client].mjs'
getContents ({ options }: ComponentsTemplateContext) {
getContents ({ options }) {
return [
'import { defineAsyncComponent } from \'vue\'',
...options.getComponents(options.mode).flatMap((c) => {
@ -69,9 +69,9 @@ export const componentsTemplate = {
}
}
export const componentsTypeTemplate = {
export const componentsTypeTemplate: NuxtTemplate<ComponentsTemplateContext> = {
filename: 'components.d.ts',
getContents: ({ options, nuxt }: ComponentsTemplateContext) => {
getContents: ({ options, nuxt }) => {
const buildDir = nuxt.options.buildDir
const componentTypes = options.getComponents().map(c => [
c.pascalName,

View File

@ -31,7 +31,7 @@ export const TreeShakeTemplatePlugin = createUnplugin((options: TreeShakeTemplat
regexpMap.set(components, new RegExp(`(${clientOnlyComponents.join('|')})`, 'g'))
}
const COMPONENTS_RE = regexpMap.get(components)
const COMPONENTS_RE = regexpMap.get(components)!
const s = new MagicString(code)
// Do not render client-only slots on SSR, but preserve attributes
@ -40,7 +40,9 @@ export const TreeShakeTemplatePlugin = createUnplugin((options: TreeShakeTemplat
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap && s.generateMap({ source: id, includeContent: true })
map: options.sourcemap
? s.generateMap({ source: id, includeContent: true })
: undefined
}
}
}

View File

@ -17,8 +17,8 @@ export const vueAppPatterns = (nuxt: Nuxt) => [
[/^(nuxt3|nuxt)$/, '`nuxt3`/`nuxt` cannot be imported directly. Instead, import runtime Nuxt composables from `#app` or `#imports`.'],
[/^((|~|~~|@|@@)\/)?nuxt\.config(\.|$)/, 'Importing directly from a `nuxt.config` file is not allowed. Instead, use runtime config or a module.'],
[/(^|node_modules\/)@vue\/composition-api/],
...nuxt.options.modules.filter(m => typeof m === 'string').map((m: string) =>
[new RegExp(`^${escapeRE(m)}$`), 'Importing directly from module entry points is not allowed.']),
...nuxt.options.modules.filter(m => typeof m === 'string').map((m: any) =>
[new RegExp(`^${escapeRE(m as string)}$`), 'Importing directly from module entry points is not allowed.']),
...[/(^|node_modules\/)@nuxt\/kit/, /^nitropack/]
.map(i => [i, 'This module cannot be imported in the Vue part of your app.']),
[new RegExp(escapeRE(resolve(nuxt.options.srcDir, (nuxt.options.dir as any).server || 'server')) + '\\/(api|routes|middleware|plugins)\\/'), 'Importing from server is not allowed in the Vue part of your app.']
@ -31,10 +31,11 @@ export const ImportProtectionPlugin = createUnplugin(function (options: ImportPr
name: 'nuxt:import-protection',
enforce: 'pre',
resolveId (id, importer) {
if (!importer) { return }
if (importersToExclude.some(p => typeof p === 'string' ? importer === p : p.test(importer))) { return }
const invalidImports = options.patterns.filter(([pattern]) => pattern instanceof RegExp ? pattern.test(id) : pattern === id)
let matched: boolean
let matched = false
for (const match of invalidImports) {
cache[id] = cache[id] || new Map()
const [pattern, warning] = match

View File

@ -35,13 +35,15 @@ export const TreeShakePlugin = createUnplugin((options: TreeShakePluginOptions)
const s = new MagicString(code)
const strippedCode = stripLiteral(code)
for (const match of strippedCode.matchAll(COMPOSABLE_RE) || []) {
s.overwrite(match.index, match.index + match[0].length, `${match[1]} /*#__PURE__*/ false && ${match[2]}`)
s.overwrite(match.index!, match.index! + match[0].length, `${match[1]} /*#__PURE__*/ false && ${match[2]}`)
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap && s.generateMap({ source: id, includeContent: true })
map: options.sourcemap
? s.generateMap({ source: id, includeContent: true })
: undefined
}
}
}

View File

@ -39,7 +39,7 @@ export default defineNuxtPlugin((nuxtApp) => {
if (!vm) { return }
onBeforeUnmount(() => {
head.removeHeadObjs(headObj)
head.removeHeadObjs(headObj as any)
head.updateDOM()
})
}

View File

@ -135,7 +135,7 @@ export default defineNuxtModule({
// Check for router options
const routerOptionsFiles = (await Promise.all(nuxt.options._layers.map(
async layer => await findPath(resolve(layer.config.srcDir, 'app/router.options'))
))).filter(Boolean)
))).filter(Boolean) as string[]
const configRouterOptions = genObjectFromRawEntries(Object.entries(nuxt.options.router.options)
.map(([key, value]) => [key, genString(value as string)]))

View File

@ -20,8 +20,8 @@ describe('auto-imports:transform', () => {
const transformPlugin = TransformPlugin.raw({ ctx, options: { transform: { exclude: [/node_modules/] } } }, { framework: 'rollup' })
const transform = async (source: string) => {
const { code } = await transformPlugin.transform.call({ error: null, warn: null }, source, '') || { code: null }
return code
const result = await transformPlugin.transform!.call({ error: null, warn: null } as any, source, '')
return typeof result === 'string' ? result : result?.code
}
it('should correct inject', async () => {
@ -29,13 +29,13 @@ describe('auto-imports:transform', () => {
})
it('should ignore existing imported', async () => {
expect(await transform('import { ref } from "foo"; const a = ref(0)')).to.equal(null)
expect(await transform('import { computed as ref } from "foo"; const a = ref(0)')).to.equal(null)
expect(await transform('import ref from "foo"; const a = ref(0)')).to.equal(null)
expect(await transform('import { z as ref } from "foo"; const a = ref(0)')).to.equal(null)
expect(await transform('let ref = () => {}; const a = ref(0)')).to.equal(null)
expect(await transform('let { ref } = Vue; const a = ref(0)')).to.equal(null)
expect(await transform('let [\ncomputed,\nref\n] = Vue; const a = ref(0); const b = ref(0)')).to.equal(null)
expect(await transform('import { ref } from "foo"; const a = ref(0)')).to.equal(undefined)
expect(await transform('import { computed as ref } from "foo"; const a = ref(0)')).to.equal(undefined)
expect(await transform('import ref from "foo"; const a = ref(0)')).to.equal(undefined)
expect(await transform('import { z as ref } from "foo"; const a = ref(0)')).to.equal(undefined)
expect(await transform('let ref = () => {}; const a = ref(0)')).to.equal(undefined)
expect(await transform('let { ref } = Vue; const a = ref(0)')).to.equal(undefined)
expect(await transform('let [\ncomputed,\nref\n] = Vue; const a = ref(0); const b = ref(0)')).to.equal(undefined)
})
it('should ignore comments', async () => {
@ -48,7 +48,7 @@ describe('auto-imports:transform', () => {
})
it('should exclude files from transform', async () => {
expect(await transform('excluded')).toEqual(null)
expect(await transform('excluded')).toEqual(undefined)
})
})
@ -65,7 +65,7 @@ describe('auto-imports:nuxt', () => {
continue
}
it(`should register ${name} globally`, () => {
expect(defaultPresets.find(a => a.from === '#app').imports).to.include(name)
expect(defaultPresets.find(a => a.from === '#app')!.imports).to.include(name)
})
}
} catch (e) {
@ -176,7 +176,7 @@ describe('auto-imports:vue', () => {
continue
}
it(`should register ${name} globally`, () => {
expect(defaultPresets.find(a => a.from === 'vue').imports).toContain(name)
expect(defaultPresets.find(a => a.from === 'vue')!.imports).toContain(name)
})
}
})

View File

@ -304,7 +304,7 @@ describe('pages:generateRouteKey', () => {
const tests = [
{ description: 'should handle overrides', override: 'key', route: getRouteProps(), output: 'key' },
{ description: 'should handle overrides', override: route => route.meta.key as string, route: getRouteProps(), output: 'route-meta-key' },
{ description: 'should handle overrides', override: (route: any) => route.meta.key as string, route: getRouteProps(), output: 'route-meta-key' },
{ description: 'should handle overrides', override: false as any, route: getRouteProps(), output: false },
{
description: 'should key dynamic routes without keys',

View File

@ -4,7 +4,7 @@ import { expect, it, vi } from 'vitest'
import { scanComponents } from '../src/components/scan'
const fixtureDir = resolve(__dirname, 'fixture')
const rFixture = (...p) => resolve(fixtureDir, ...p)
const rFixture = (...p: string[]) => resolve(fixtureDir, ...p)
vi.mock('@nuxt/kit', () => ({
isIgnored: () => false
@ -108,6 +108,7 @@ const srcDir = rFixture('.')
it('components:scanComponents', async () => {
const scannedComponents = await scanComponents(dirs, srcDir)
for (const c of scannedComponents) {
// @ts-ignore
delete c.filePath
}
expect(scannedComponents).deep.eq(expectedComponents)

View File

@ -9,7 +9,7 @@ import type { NuxtTemplate, Nuxt, NuxtApp } from './nuxt'
import type { Preset as ImportPreset, Import } from 'unimport'
import type { NuxtConfig, NuxtOptions } from './config'
import type { Nitro, NitroConfig } from 'nitropack'
import type { Component, ComponentsDir, ScanDir, ComponentsOptions } from './components'
import type { Component, ComponentsOptions } from './components'
import { NuxtCompatibility, NuxtCompatibilityIssues } from '..'
@ -82,7 +82,7 @@ export interface NuxtHooks {
// Components
'components:dirs': (dirs: ComponentsOptions['dirs']) => HookResult
'components:extend': (components: (Component | ComponentsDir | ScanDir)[]) => HookResult
'components:extend': (components: Component[]) => HookResult
// @nuxt/builder
'build:before':

View File

@ -1,5 +1,5 @@
import { NuxtHooks } from './hooks'
import type { Nuxt, NuxtTemplate } from "./nuxt"
import type { Nuxt, NuxtPluginTemplate, NuxtTemplate } from "./nuxt"
import type { NuxtCompatibility } from './compatibility'
export interface ModuleMeta {
@ -58,6 +58,9 @@ export interface ModuleContainer {
/** Renders given template using lodash template during build into the project buildDir (`.nuxt`).*/
addTemplate(template: string | NuxtTemplate): NuxtTemplate
/** Registers a custom plugin. */
addPlugin(template: NuxtPluginTemplate): NuxtPluginTemplate
/** Registers a custom layout. If its name is 'error' it will override the default error layout. */
addLayout(tmpl: NuxtTemplate, name: string): any

View File

@ -34,15 +34,20 @@ export interface NuxtTemplate<Options = Record<string, any>> {
/** The target filename once the template is copied into the Nuxt buildDir */
filename?: string
/** An options object that will be accessible within the template via `<% options %>` */
options?: Record<string, any>
options?: Options
/** The resolved path to the source file to be template */
src?: string
/** Provided compile option instead of src */
getContents?: (data: Record<string, any>) => string | Promise<string>
getContents?: (data: Options) => string | Promise<string>
/** Write to filesystem */
write?: boolean
}
export interface ResolvedNuxtTemplate<Options = Record<string, any>> extends NuxtTemplate<Options> {
filename: string
dst: string
}
export interface NuxtPlugin {
/** @deprecated use mode */
ssr?: boolean
@ -63,5 +68,5 @@ export interface NuxtApp {
configs: string[]
}
type _TemplatePlugin = Omit<NuxtPlugin, 'src'> & NuxtTemplate
export interface NuxtPluginTemplate extends _TemplatePlugin { }
type _TemplatePlugin<Options> = Omit<NuxtPlugin, 'src'> & NuxtTemplate<Options>
export interface NuxtPluginTemplate<Options = Record<string, any>> extends _TemplatePlugin<Options> { }