feat(kit, schema)!: finalize nuxt 3 module spec and utils (#2275)

This commit is contained in:
pooya parsa 2021-12-21 14:57:26 +01:00 committed by GitHub
parent 6e32bde7ae
commit 045b9edb5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 287 additions and 219 deletions

View File

@ -8,6 +8,7 @@ export default defineBuildConfig({
],
externals: [
'webpack',
'vite'
'vite',
'vue-meta'
]
})

View File

@ -3,20 +3,22 @@ module.exports = function (...args) {
return import('./dist/module.mjs').then(m => m.default.call(this, ...args))
}
const pkg = require('./package.json')
module.exports.defineNuxtConfig = (config = {}) => {
if (config.bridge !== false) {
config.bridge = config.bridge || {}
config.bridge._version = pkg.version
if (!config.buildModules) {
config.buildModules = []
}
if (!config.buildModules.find(m => m === '@nuxt/bridge' || m === '@nuxt/bridge-edge')) {
config.buildModules.push('@nuxt/bridge')
config.buildModules.unshift('@nuxt/bridge')
}
}
return config
}
const pkg = require('./package.json')
module.exports.meta = {
pkg,
name: pkg.name,

View File

@ -35,5 +35,5 @@ export async function setupAutoImports () {
autoImports.push({ name: 'useNuxt2Meta', as: 'useNuxt2Meta', from: '#app' })
})
await installModule(nuxt, autoImports)
await installModule(autoImports)
}

View File

@ -29,5 +29,5 @@ export const setupMeta = async (opts: SetupMetaOptions) => {
const runtimeDir = resolve(distDir, 'runtime/meta')
nuxt.options.alias['#meta'] = runtimeDir
await installModule(nuxt, metaModule)
await installModule(metaModule)
}

View File

@ -1,5 +1,7 @@
import { createRequire } from 'module'
import { defineNuxtModule, installModule, checkNuxtCompatibilityIssues } from '@nuxt/kit'
import { defineNuxtModule, installModule, checkNuxtCompatibility, nuxtCtx } from '@nuxt/kit'
import type { NuxtModule } from '@nuxt/schema'
import { NuxtCompatibility } from '@nuxt/schema/src/types/compatibility'
import type { BridgeConfig, ScriptSetupOptions } from '../types'
import { setupNitroBridge } from './nitro'
import { setupAppBridge } from './app'
@ -12,8 +14,10 @@ import { setupTranspile } from './transpile'
import { setupScriptSetup } from './setup'
export default defineNuxtModule({
name: 'nuxt-bridge',
configKey: 'bridge',
meta: {
name: 'nuxt-bridge',
configKey: 'bridge'
},
defaults: {
nitro: true,
vite: false,
@ -22,7 +26,7 @@ export default defineNuxtModule({
transpile: true,
scriptSetup: true,
autoImports: true,
constraints: true,
compatibility: true,
meta: null,
// TODO: Remove from 2.16
postcss8: true,
@ -32,6 +36,11 @@ export default defineNuxtModule({
async setup (opts, nuxt) {
const _require = createRequire(import.meta.url)
// Allow using kit compasables in all modules
if (!nuxtCtx.use()) {
nuxtCtx.set(nuxt)
}
if (opts.nitro) {
await setupNitroBridge()
}
@ -51,11 +60,11 @@ export default defineNuxtModule({
await setupAutoImports()
}
if (opts.vite) {
const viteModule = await import('./vite/module').then(r => r.default || r)
await installModule(nuxt, viteModule)
const viteModule = await import('./vite/module').then(r => r.default || r) as NuxtModule
await installModule(viteModule)
}
if (opts.postcss8) {
await installModule(nuxt, _require.resolve('@nuxt/postcss8'))
await installModule(_require.resolve('@nuxt/postcss8'))
}
if (opts.typescript) {
await setupTypescript()
@ -66,12 +75,12 @@ export default defineNuxtModule({
if (opts.transpile) {
setupTranspile()
}
if (opts.constraints) {
nuxt.hook('modules:done', (moduleContainer: any) => {
if (opts.compatibility) {
nuxt.hook('modules:done', async (moduleContainer: any) => {
for (const [name, m] of Object.entries(moduleContainer.requiredModules || {})) {
const requires = (m as any)?.handler?.meta?.requires
if (requires) {
const issues = checkNuxtCompatibilityIssues(requires, nuxt)
const compat = ((m as any)?.handler?.meta?.compatibility || {}) as NuxtCompatibility
if (compat) {
const issues = await checkNuxtCompatibility(compat, nuxt)
if (issues.length) {
console.warn(`[bridge] Detected module incompatibility issues for \`${name}\`:\n` + issues.toString())
}

View File

@ -5,10 +5,12 @@ import { middlewareTemplate, storeTemplate } from './templates'
import type { ViteOptions } from './types'
export default defineNuxtModule<ViteOptions>({
name: 'nuxt-bridge:vite',
meta: {
name: 'nuxt-bridge:vite',
version,
configKey: 'vite'
},
defaults: {},
version,
configKey: 'vite',
setup (viteOptions, nuxt) {
nuxt.options.cli.badgeMessages.push(`⚡ Vite Mode Enabled (v${version})`)
// eslint-disable-next-line no-console

View File

@ -15,7 +15,7 @@ export interface BridgeConfig {
scriptSetup: boolean | ScriptSetupOptions
autoImports: boolean
transpile: boolean
constraints: boolean
compatibility: boolean
postcss8: boolean
resolve: boolean
typescript: boolean

View File

@ -1,12 +1,14 @@
import satisfies from 'semver/functions/satisfies.js' // npm/node-semver#381
import type { Nuxt, NuxtCompatibilityConstraints, NuxtCompatibilityIssues } from '@nuxt/schema'
import type { Nuxt, NuxtCompatibility, NuxtCompatibilityIssues } from '@nuxt/schema'
import { useNuxt } from './context'
/**
* Check version constraints and return incompatibility issues as an array
*/
export function checkNuxtCompatibilityIssues (constraints: NuxtCompatibilityConstraints, nuxt: Nuxt = useNuxt()): NuxtCompatibilityIssues {
export async function checkNuxtCompatibility (constraints: NuxtCompatibility, nuxt: Nuxt = useNuxt()): Promise<NuxtCompatibilityIssues> {
const issues: NuxtCompatibilityIssues = []
// Nuxt version check
if (constraints.nuxt) {
const nuxtVersion = getNuxtVersion(nuxt)
const nuxtSemanticVersion = nuxtVersion.split('-').shift()
@ -17,15 +19,39 @@ export function checkNuxtCompatibilityIssues (constraints: NuxtCompatibilityCons
})
}
}
issues.toString = () => issues.map(issue => ` - [${issue.name}] ${issue.message}`).join('\n')
// Bridge compatibility check
if (isNuxt2(nuxt)) {
const bridgeRequirement = constraints?.bridge
const hasBridge = !!(nuxt.options as any).bridge
if (bridgeRequirement === true && !hasBridge) {
issues.push({
name: 'bridge',
message: 'Nuxt bridge is required'
})
} else if (bridgeRequirement === false && hasBridge) {
issues.push({
name: 'bridge',
message: 'Nuxt bridge is not supported'
})
}
}
// Allow extending compatibility checks
await nuxt.callHook('kit:compatibility', constraints, issues)
// Issues formatter
issues.toString = () =>
issues.map(issue => ` - [${issue.name}] ${issue.message}`).join('\n')
return issues
}
/**
* Check version constraints and throw a detailed error if has any, otherwise returns true
*/
export function ensureNuxtCompatibility (constraints: NuxtCompatibilityConstraints, nuxt: Nuxt = useNuxt()): true {
const issues = checkNuxtCompatibilityIssues(constraints, nuxt)
export async function assertNuxtCompatibility (constraints: NuxtCompatibility, nuxt: Nuxt = useNuxt()): Promise<true> {
const issues = await checkNuxtCompatibility(constraints, nuxt)
if (issues.length) {
throw new Error('Nuxt compatibility issues found:\n' + issues.toString())
}
@ -35,8 +61,9 @@ export function ensureNuxtCompatibility (constraints: NuxtCompatibilityConstrain
/**
* Check version constraints and return true if passed, otherwise returns false
*/
export function hasNuxtCompatibility (constraints: NuxtCompatibilityConstraints, nuxt: Nuxt = useNuxt()) {
return !checkNuxtCompatibilityIssues(constraints, nuxt).length
export async function hasNuxtCompatibility (constraints: NuxtCompatibility, nuxt: Nuxt = useNuxt()): Promise<boolean> {
const issues = await checkNuxtCompatibility(constraints, nuxt)
return !issues.length
}
/**

View File

@ -1,16 +1,16 @@
import { pascalCase, kebabCase } from 'scule'
import type { ComponentsDir, Component } from '@nuxt/schema'
import { useNuxt } from './context'
import { ensureNuxtCompatibility } from './compatibility'
import { assertNuxtCompatibility } from './compatibility'
/**
* Register a directory to be scanned for components and imported only when used.
*
* Requires Nuxt 2.13+
*/
export function addComponentsDir (dir: ComponentsDir) {
export async function addComponentsDir (dir: ComponentsDir) {
const nuxt = useNuxt()
ensureNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
await assertNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
nuxt.options.components = nuxt.options.components || []
nuxt.hook('components:dirs', (dirs) => { dirs.push(dir) })
}
@ -24,9 +24,9 @@ export type AddComponentOptions = { name: string, filePath: string } & Partial<E
*
* Requires Nuxt 2.13+
*/
export function addComponent (opts: AddComponentOptions) {
export async function addComponent (opts: AddComponentOptions) {
const nuxt = useNuxt()
ensureNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
await assertNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
nuxt.options.components = nuxt.options.components || []
// Apply defaults

View File

@ -6,19 +6,44 @@ import { addTemplate } from '../template'
import { addServerMiddleware } from '../server'
import { isNuxt2 } from '../compatibility'
import { addPluginTemplate } from '../plugin'
import { useNuxt } from '../context'
import { installModule } from './install'
export function createModuleContainer (nuxt: Nuxt): ModuleContainer {
return <ModuleContainer>{
const MODULE_CONTAINER_KEY = '__module_container__'
export function useModuleContainer (nuxt: Nuxt = useNuxt()): ModuleContainer {
if (nuxt[MODULE_CONTAINER_KEY]) {
return nuxt[MODULE_CONTAINER_KEY]
}
async function requireModule (moduleOpts) {
let src, inlineOptions
if (typeof moduleOpts === 'string') {
src = moduleOpts
} else if (Array.isArray(moduleOpts)) {
[src, inlineOptions] = moduleOpts
} else if (typeof moduleOpts === 'object') {
if (moduleOpts.src || moduleOpts.handler) {
src = moduleOpts.src || moduleOpts.handler
inlineOptions = moduleOpts.options
} else {
src = moduleOpts
}
} else {
src = moduleOpts
}
await installModule(src, inlineOptions, nuxt)
}
nuxt[MODULE_CONTAINER_KEY] = <ModuleContainer>{
nuxt,
options: nuxt.options,
ready () { return Promise.resolve() },
addVendor () {},
requireModule: installModule,
addModule: installModule,
requireModule,
addModule: requireModule,
addServerMiddleware,
@ -73,4 +98,6 @@ export function createModuleContainer (nuxt: Nuxt): ModuleContainer {
}
}
}
return nuxt[MODULE_CONTAINER_KEY]
}

View File

@ -3,105 +3,128 @@ import defu from 'defu'
import { applyDefaults } from 'untyped'
import consola from 'consola'
import { dirname } from 'pathe'
import type { Nuxt, NuxtTemplate, NuxtModule, LegacyNuxtModule, ModuleOptions } from '@nuxt/schema'
import type { Nuxt, NuxtTemplate, NuxtModule, ModuleOptions, ModuleDefinition } from '@nuxt/schema'
import { useNuxt, nuxtCtx } from '../context'
import { isNuxt2, checkNuxtCompatibilityIssues } from '../compatibility'
import { isNuxt2, checkNuxtCompatibility } from '../compatibility'
import { templateUtils, compileTemplate } from '../internal/template'
/**
* Define a Nuxt module, automatically merging defaults with user provided options, installing
* any hooks that are provided, and calling an optional setup function for full control.
*/
export function defineNuxtModule<OptionsT extends ModuleOptions> (input: NuxtModule<OptionsT> | ((nuxt: Nuxt) => NuxtModule<OptionsT>)): LegacyNuxtModule {
let mod: NuxtModule<OptionsT>
export function defineNuxtModule<OptionsT extends ModuleOptions> (definition: ModuleDefinition<OptionsT>): NuxtModule<OptionsT> {
// Normalize definition and meta
if (!definition.meta) { definition.meta = {} }
if (!definition.meta.configKey) {
// @ts-ignore TODO: Remove non-meta fallbacks in RC
definition.meta.name = definition.meta.name || definition.name
// @ts-ignore
definition.meta.configKey = definition.meta.configKey || definition.configKey || definition.meta.name
}
function wrappedModule (inlineOptions: OptionsT) {
// Get nuxt context
const nuxt: Nuxt = this.nuxt || useNuxt()
// 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 _defaults = typeof definition.defaults === 'function' ? definition.defaults(nuxt) : definition.defaults
let _options = defu(inlineOptions, nuxt.options[configKey], _defaults) as OptionsT
if (definition.schema) {
_options = applyDefaults(definition.schema, _options) as OptionsT
}
return Promise.resolve(_options)
}
// Resolve function
if (typeof input === 'function') {
mod = input(nuxt)
} else {
mod = input
// Module format is always a simple function
async function normalizedModule (inlineOptions: OptionsT, nuxt: Nuxt) {
if (!nuxt) {
nuxt = useNuxt() || this.nuxt /* invoked by nuxt 2 */
}
// Install hooks
if (mod.hooks) {
if (isNuxt2(nuxt)) {
nuxt.addHooks(mod.hooks)
} else {
nuxt.hooks.addHooks(mod.hooks)
// Avoid duplicate installs
const uniqueKey = definition.meta.name || definition.meta.configKey
if (uniqueKey) {
nuxt.options._requiredModules = nuxt.options._requiredModules || {}
if (nuxt.options._requiredModules[uniqueKey]) {
// TODO: Notify user if inline options is provided since will be ignored!
return
}
nuxt.options._requiredModules[uniqueKey] = true
}
// Stop if no install provided
if (typeof mod.setup !== 'function') {
return
}
// check nuxt version range
if (mod.requires) {
const issues = checkNuxtCompatibilityIssues(mod.requires, nuxt)
// Check compatibility contraints
if (definition.meta.compatibility) {
const issues = await checkNuxtCompatibility(definition.meta.compatibility, nuxt)
if (issues.length) {
consola.warn(`Module \`${mod.name}\` is disabled due to incompatibility issues:\n${issues.toString()}`)
consola.warn(`Module \`${definition.meta.name}\` is disabled due to incompatibility issues:\n${issues.toString()}`)
return
}
}
// Resolve options
const configKey = mod.configKey || mod.name
const userOptions = defu(inlineOptions, nuxt.options[configKey]) as OptionsT
const resolvedOptions = applyDefaults(mod.defaults as any, userOptions) as OptionsT
// Prepare
nuxt2Shims(nuxt)
// Ensure nuxt instance exists (nuxt2 compatibility)
if (!nuxtCtx.use()) {
nuxtCtx.set(nuxt)
// @ts-ignore
if (!nuxt.__nuxtkit_close__) {
nuxt.hook('close', () => nuxtCtx.unset())
// @ts-ignore
nuxt.__nuxtkit_close__ = true
}
}
// Resolve module and options
const _options = await getOptions(inlineOptions, nuxt)
if (isNuxt2()) {
// Support virtual templates with getContents() by writing them to .nuxt directory
let virtualTemplates: NuxtTemplate[]
nuxt.hook('builder:prepared', (_builder, buildOptions) => {
virtualTemplates = buildOptions.templates.filter(t => t.getContents)
for (const template of virtualTemplates) {
buildOptions.templates.splice(buildOptions.templates.indexOf(template), 1)
}
})
nuxt.hook('build:templates', async (templates) => {
const context = {
nuxt,
utils: templateUtils,
app: {
dir: nuxt.options.srcDir,
extensions: nuxt.options.extensions,
plugins: nuxt.options.plugins,
templates: [
...templates.templatesFiles,
...virtualTemplates
],
templateVars: templates.templateVars
}
}
for await (const template of virtualTemplates) {
const contents = await compileTemplate({ ...template, src: '' }, context)
await fsp.mkdir(dirname(template.dst), { recursive: true })
await fsp.writeFile(template.dst, contents)
}
})
// Register hooks
if (definition.hooks) {
nuxt.hooks.addHooks(definition.hooks)
}
// Call setup
return mod.setup.call(null, resolvedOptions, nuxt)
await definition.setup?.call(null, _options, nuxt)
}
wrappedModule.meta = mod
// Define getters for options and meta
normalizedModule.getMeta = () => Promise.resolve(definition.meta)
normalizedModule.getOptions = getOptions
return wrappedModule
return normalizedModule as NuxtModule<OptionsT>
}
// -- Nuxt 2 compatibility shims --
const NUXT2_SHIMS_KEY = '__nuxt2_shims_key__'
function nuxt2Shims (nuxt: Nuxt) {
// Avoid duplicate install and only apply to Nuxt2
if (!isNuxt2(nuxt) || nuxt[NUXT2_SHIMS_KEY]) { return }
nuxt[NUXT2_SHIMS_KEY] = true
// Allow using nuxt.hooks
// @ts-ignore Nuxt 2 extends hookable
nuxt.hooks = nuxt
// Allow using useNuxt()
if (!nuxtCtx.use()) {
nuxtCtx.set(nuxt)
nuxt.hook('close', () => nuxtCtx.unset())
}
// Support virtual templates with getContents() by writing them to .nuxt directory
let virtualTemplates: NuxtTemplate[]
nuxt.hook('builder:prepared', (_builder, buildOptions) => {
virtualTemplates = buildOptions.templates.filter(t => t.getContents)
for (const template of virtualTemplates) {
buildOptions.templates.splice(buildOptions.templates.indexOf(template), 1)
}
})
nuxt.hook('build:templates', async (templates) => {
const context = {
nuxt,
utils: templateUtils,
app: {
dir: nuxt.options.srcDir,
extensions: nuxt.options.extensions,
plugins: nuxt.options.plugins,
templates: [
...templates.templatesFiles,
...virtualTemplates
],
templateVars: templates.templateVars
}
}
for await (const template of virtualTemplates) {
const contents = await compileTemplate({ ...template, src: '' }, context)
await fsp.mkdir(dirname(template.dst), { recursive: true })
await fsp.writeFile(template.dst, contents)
}
})
}

View File

@ -1,63 +1,34 @@
import type { LegacyNuxtModule, NuxtModule, ModuleMeta, ModuleInstallOptions, ModuleOptions, ModuleSrc, Nuxt } from '@nuxt/schema'
import type { NuxtModule, Nuxt } from '@nuxt/schema'
import { useNuxt } from '../context'
import { resolveModule, requireModule, importModule } from '../internal/cjs'
import { resolveAlias } from '../resolve'
import { defineNuxtModule } from './define'
import { createModuleContainer } from './container'
import { useModuleContainer } from './container'
/** Installs a module on a Nuxt instance. */
export async function installModule (nuxt: Nuxt, installOpts: ModuleInstallOptions) {
let src: ModuleSrc
let options: ModuleOptions = {}
const meta: ModuleMeta = {}
// Extract src, meta and options
if (typeof installOpts === 'string') {
src = installOpts
} else if (Array.isArray(installOpts)) {
[src, options] = installOpts
} else if (typeof installOpts === 'object') {
if (installOpts.src || installOpts.handler) {
src = installOpts.src || installOpts.handler
options = installOpts.options
Object.assign(meta, installOpts.meta)
} else {
src = installOpts as NuxtModule
}
} else {
src = installOpts
export async function installModule (nuxtModule: string | NuxtModule, inlineOptions?: any, nuxt: Nuxt = useNuxt()) {
// Detect if `installModule` used with older signuture (nuxt, nuxtModule)
// TODO: Remove in RC
// @ts-ignore
if (nuxtModule?._version || nuxtModule?.version || nuxtModule?.constructor?.version || '') {
// @ts-ignore
[nuxt, nuxtModule] = [nuxtModule, inlineOptions]
inlineOptions = {}
console.warn(new Error('`installModule` is being called with old signature!'))
}
// Resolve as legacy handler
let handler: LegacyNuxtModule
if (typeof src === 'string') {
const _src = resolveModule(resolveAlias(src, nuxt.options.alias), { paths: nuxt.options.modulesDir })
// Import if input is string
if (typeof nuxtModule === 'string') {
const _src = resolveModule(resolveAlias(nuxtModule, nuxt.options.alias), { paths: nuxt.options.modulesDir })
// TODO: also check with type: 'module' in closest `package.json`
const isESM = _src.endsWith('.mjs') || meta.isESM
handler = isESM ? await importModule(_src) : requireModule(_src)
if (!meta.name) {
meta.name = src
}
} else if (typeof src === 'function') {
handler = src
} else {
handler = defineNuxtModule(src)
const isESM = _src.endsWith('.mjs')
nuxtModule = isESM ? await importModule(_src) : requireModule(_src)
}
// Merge meta
if (handler.meta) {
Object.assign(meta, handler.meta)
// Throw error if input is not a function
if (typeof nuxtModule !== 'function') {
throw new TypeError('Nuxt module should be a function: ' + nuxtModule)
}
// Ensure module is required once
if (typeof meta.name === 'string') {
nuxt.options._requiredModules = nuxt.options._requiredModules || {}
if (nuxt.options._requiredModules[meta.name]) {
return
}
nuxt.options._requiredModules[meta.name] = true
}
// Execute in legacy container
const container = createModuleContainer(nuxt)
await handler.call(container, options)
// Call module
await nuxtModule.call(useModuleContainer(), inlineOptions, nuxt)
}

View File

@ -21,6 +21,7 @@ export default defineBuildConfig({
externals: [
'@vue/reactivity',
'@vue/shared',
'@vueuse/head'
'@vueuse/head',
'vue-meta'
]
})

View File

@ -67,7 +67,7 @@ async function initNuxt (nuxt: Nuxt) {
})
for (const m of modulesToInstall) {
await installModule(nuxt, m)
await installModule(m, nuxt)
}
await nuxt.callHook('modules:done', { nuxt } as ModuleContainer)

View File

@ -26,6 +26,6 @@ export default defineBuildConfig({
'vite',
// Implicit
'@vue/compiler-core',
'@vue/shared ',
'@vue/shared'
]
})

View File

@ -225,7 +225,7 @@ export default {
* function () {}
* ]
* ```
* @type {typeof import('../src/types/module').ModuleInstallOptions[]}
* @type {(typeof import('../src/types/module').NuxtModule | string)[]}
* @version 2
* @version 3
*/
@ -262,7 +262,7 @@ export default {
* decreases the size of `node_modules` in production deployments. Please refer to each
* module's documentation to see if it is recommended to use `modules` or `buildModules`.
*
* @type {typeof import('../src/types/module').ModuleInstallOptions[]}
* @type {(typeof import('../src/types/module').NuxtModule | string)[]}
* @version 2
* @version 3
*/

View File

@ -1,13 +1,15 @@
// Types
import './types/global'
export * from './types/compatibility'
export * from './types/components'
export * from './types/config'
export * from './types/hooks'
export * from './types/module'
export * from './types/meta'
export * from './types/nuxt'
export * from './types/components'
export * from './types/imports'
export * from './types/meta'
export * from './types/module'
export * from './types/nuxt'
export * from './types/pages'
// Schema

View File

@ -0,0 +1,29 @@
export interface NuxtCompatibility {
/**
* Required nuxt version in semver format.
*
* @example `^2.14.0` or `>=3.0.0-27219851.6e49637`.
*
*/
nuxt?: string
/**
* Bridge constraint for Nuxt 2 support.
*
* - `true`: When using Nuxt 2, using bridge module is required
* - `false`: When using Nuxt 2, using bridge module is not supported
*/
bridge?: Boolean
}
export interface NuxtCompatibilityIssue {
name: string
message: string
}
export interface NuxtCompatibilityIssues extends Array<NuxtCompatibilityIssue> {
/**
* Return formatted error message
*/
toString(): string
}

View File

@ -6,6 +6,7 @@ import type { NuxtTemplate, Nuxt, NuxtApp } from './nuxt'
import type { AutoImport, AutoImportSource } from './imports'
import type { NuxtConfig, NuxtOptions } from './config'
import type { Component, ComponentsDir, ScanDir, ComponentsOptions } from './components'
import { NuxtCompatibility, NuxtCompatibilityIssues } from '..'
type HookResult = Promise<void> | void
@ -39,6 +40,9 @@ export type NuxtPage = {
}
export interface NuxtHooks {
// Kit
'kit:compatibility': (compatibility: NuxtCompatibility, issues: NuxtCompatibilityIssues) => HookResult
// nuxt3
'app:resolve': (app: NuxtApp) => HookResult
'app:templates': (app: NuxtApp) => HookResult

View File

@ -1,25 +1,6 @@
import { NuxtHooks } from './hooks'
import type { Nuxt, NuxtTemplate } from "./nuxt";
export interface NuxtCompatibilityConstraints {
/**
* Required nuxt version. for example, `^2.14.0` or `>=3.0.0-27219851.6e49637`.
*/
nuxt?: string
}
export interface NuxtCompatibilityIssue {
name: string
message: string
}
export interface NuxtCompatibilityIssues extends Array<NuxtCompatibilityIssue> {
/**
* Return formatted error message
*/
toString(): string
}
import type { Nuxt, NuxtTemplate } from "./nuxt"
import type { NuxtCompatibility } from './compatibility'
export interface ModuleMeta {
/** Module name */
@ -35,9 +16,9 @@ export interface ModuleMeta {
configKey?: string
/**
* Semver constraints for the versions of Nuxt or features this module are supported.
* Constraints for the versions of Nuxt or features this module requires
*/
requires?: NuxtCompatibilityConstraints
compatibility?: NuxtCompatibility
[key: string]: any
}
@ -45,33 +26,22 @@ export interface ModuleMeta {
/** The options received */
export type ModuleOptions = Record<string, any>
/** A pre-kit Nuxt module */
export interface LegacyNuxtModule {
(this: ModuleContainer, inlineOptions?: ModuleOptions): void | Promise<void>
/** Input module passed to defineNuxtModule */
export interface ModuleDefinition<T extends ModuleOptions = ModuleOptions> {
meta?: ModuleMeta
}
/** A Nuxt module definition */
export interface NuxtModule<T extends ModuleOptions = any> extends ModuleMeta {
defaults?: T
setup?: (this: null, resolvedOptions: T, nuxt: Nuxt) => void | Promise<void>
defaults?: T | ((nuxt: Nuxt) => T)
schema?: T
hooks?: Partial<NuxtHooks>
setup?: (this: void, resolvedOptions: T, nuxt: Nuxt) => void | Promise<void>
}
export type ModuleSrc = string | NuxtModule | LegacyNuxtModule
export interface ModuleInstallOptionsObj {
src: ModuleSrc,
meta: ModuleMeta
options: ModuleOptions
handler: LegacyNuxtModule
/** Nuxt modules are always a simple function */
export interface NuxtModule<T extends ModuleOptions = ModuleOptions> {
(this: void, inlineOptions: T, nuxt: Nuxt): void | Promise<void>
getOptions?: (inlineOptions?: T, nuxt?: Nuxt) => Promise<T>
getMeta?: () => Promise<ModuleMeta>
}
export type ModuleInstallOptions =
ModuleSrc |
[ModuleSrc, ModuleOptions?] |
Partial<ModuleInstallOptionsObj>
/**
* Legacy ModuleContainer for backwards compatibility with Nuxt 2 module format.
*/
@ -104,8 +74,8 @@ export interface ModuleContainer {
extendRoutes(fn): void
/** Registers a module */
requireModule(nuxt: Nuxt, opts: any): Promise<void>
requireModule(installOptions: any, opts: any): Promise<void>
/** Registers a module */
addModule(nuxt: Nuxt, opts: any): Promise<void>
addModule(installOptions: any, opts: any): Promise<void>
}