mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-29 14:52:41 +00:00
feat(kit, schema)!: finalize nuxt 3 module spec and utils (#2275)
This commit is contained in:
parent
6e32bde7ae
commit
045b9edb5d
@ -8,6 +8,7 @@ export default defineBuildConfig({
|
||||
],
|
||||
externals: [
|
||||
'webpack',
|
||||
'vite'
|
||||
'vite',
|
||||
'vue-meta'
|
||||
]
|
||||
})
|
||||
|
@ -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,
|
||||
|
@ -35,5 +35,5 @@ export async function setupAutoImports () {
|
||||
autoImports.push({ name: 'useNuxt2Meta', as: 'useNuxt2Meta', from: '#app' })
|
||||
})
|
||||
|
||||
await installModule(nuxt, autoImports)
|
||||
await installModule(autoImports)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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
|
||||
|
2
packages/bridge/types.d.ts
vendored
2
packages/bridge/types.d.ts
vendored
@ -15,7 +15,7 @@ export interface BridgeConfig {
|
||||
scriptSetup: boolean | ScriptSetupOptions
|
||||
autoImports: boolean
|
||||
transpile: boolean
|
||||
constraints: boolean
|
||||
compatibility: boolean
|
||||
postcss8: boolean
|
||||
resolve: boolean
|
||||
typescript: boolean
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ export default defineBuildConfig({
|
||||
externals: [
|
||||
'@vue/reactivity',
|
||||
'@vue/shared',
|
||||
'@vueuse/head'
|
||||
'@vueuse/head',
|
||||
'vue-meta'
|
||||
]
|
||||
})
|
||||
|
@ -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)
|
||||
|
@ -26,6 +26,6 @@ export default defineBuildConfig({
|
||||
'vite',
|
||||
// Implicit
|
||||
'@vue/compiler-core',
|
||||
'@vue/shared ',
|
||||
'@vue/shared'
|
||||
]
|
||||
})
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
29
packages/schema/src/types/compatibility.ts
Normal file
29
packages/schema/src/types/compatibility.ts
Normal 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
|
||||
}
|
@ -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
|
||||
|
@ -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>
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user