This repository has been archived by the owner on Apr 6, 2023. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
define.ts
131 lines (116 loc) · 4.68 KB
/
define.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import { promises as fsp } from 'node:fs'
import defu from 'defu'
import { applyDefaults } from 'untyped'
import { dirname } from 'pathe'
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'
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> (definition: ModuleDefinition<OptionsT>): NuxtModule<OptionsT> {
// Normalize definition and meta
if (!definition.meta) { definition.meta = {} }
if (definition.meta.configKey === undefined) {
definition.meta.configKey = definition.meta.name
}
// Resolves module options from inline options, [configKey] in nuxt.config, defaults and schema
async function getOptions (inlineOptions?: OptionsT, nuxt: Nuxt = useNuxt()) {
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 as keyof NuxtOptions], _defaults) as OptionsT
if (definition.schema) {
_options = await applyDefaults(definition.schema, _options) as OptionsT
}
return Promise.resolve(_options)
}
// Module format is always a simple function
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
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
}
// Check compatibility constraints
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()}`)
return
}
}
// Prepare
nuxt2Shims(nuxt)
// Resolve module and options
const _options = await getOptions(inlineOptions, nuxt)
// Register hooks
if (definition.hooks) {
nuxt.hooks.addHooks(definition.hooks)
}
// Call setup
await definition.setup?.call(null as any, _options, nuxt)
}
// Define getters for options and meta
normalizedModule.getMeta = () => Promise.resolve(definition.meta)
normalizedModule.getOptions = getOptions
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
// @ts-ignore
if (!isNuxt2(nuxt) || nuxt[NUXT2_SHIMS_KEY]) { return }
// @ts-ignore
nuxt[NUXT2_SHIMS_KEY] = true
// Allow using nuxt.hooks
// @ts-expect-error Nuxt 2 extends hookable
nuxt.hooks = nuxt
// Allow using useNuxt()
if (!nuxtCtx.tryUse()) {
nuxtCtx.set(nuxt)
nuxt.hook('close', () => nuxtCtx.unset())
}
// Support virtual templates with getContents() by writing them to .nuxt directory
let virtualTemplates: ResolvedNuxtTemplate[]
// @ts-ignore Nuxt 2 hook
nuxt.hook('builder:prepared', (_builder, buildOptions) => {
virtualTemplates = buildOptions.templates.filter((t: any) => t.getContents)
for (const template of virtualTemplates) {
buildOptions.templates.splice(buildOptions.templates.indexOf(template), 1)
}
})
// @ts-ignore Nuxt 2 hook
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)
}
})
}