feat(nuxt3,bridge): add automatic schema augmentation declaration (#3096)

Co-authored-by: Pooya Parsa <pyapar@gmail.com>
This commit is contained in:
Daniel Roe 2022-02-08 19:09:44 +00:00 committed by GitHub
parent ba522b2034
commit 7825e2aa12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 79 additions and 19 deletions

View File

@ -34,6 +34,7 @@
"defu": "^5.0.1",
"destr": "^1.1.0",
"enhanced-resolve": "^5.9.0",
"escape-string-regexp": "^5.0.0",
"estree-walker": "^3.0.1",
"externality": "^0.1.6",
"fs-extra": "^10.0.0",

View File

@ -1,6 +1,8 @@
import { useNuxt, resolveModule, addTemplate } from '@nuxt/kit'
import { useNuxt, resolveModule, addTemplate, resolveAlias } from '@nuxt/kit'
import { NuxtModule } from '@nuxt/schema'
import { resolve } from 'pathe'
import { componentsTypeTemplate } from '../../nuxt3/src/components/templates'
import { schemaTemplate } from '../../nuxt3/src/core/templates'
import { distDir } from './dirs'
export function setupAppBridge (_options: any) {
@ -38,6 +40,15 @@ export function setupAppBridge (_options: any) {
references.push({ path: resolve(nuxt.options.buildDir, 'types/components.d.ts') })
})
// Augment schema with module types
nuxt.hook('modules:done', async (container: any) => {
nuxt.options._installedModules = await Promise.all(Object.values(container.requiredModules).map(async (m: { src: string, handler: NuxtModule }) => ({
meta: await m.handler.getMeta?.(),
entryPath: resolveAlias(m.src, nuxt.options.alias)
})))
addTemplate(schemaTemplate)
})
// Alias vue to have identical vue3 exports
nuxt.options.alias['vue2-bridge'] = resolve(distDir, 'runtime/vue2-bridge.mjs')
for (const alias of [

View File

@ -32,7 +32,7 @@ export function useModuleContainer (nuxt: Nuxt = useNuxt()): ModuleContainer {
} else {
src = moduleOpts
}
await installModule(src, inlineOptions, nuxt)
await installModule(src, inlineOptions)
}
nuxt[MODULE_CONTAINER_KEY] = <ModuleContainer>{

View File

@ -1,18 +1,34 @@
import type { NuxtModule, Nuxt } from '@nuxt/schema'
import type { Nuxt, NuxtModule } from '@nuxt/schema'
import { useNuxt } from '../context'
import { resolveModule, requireModule, importModule } from '../internal/cjs'
import { resolveAlias } from '../resolve'
import { useModuleContainer } from './container'
/** Installs a module on a Nuxt instance. */
export async function installModule (nuxtModule: string | NuxtModule, inlineOptions?: any, nuxt: Nuxt = useNuxt()) {
export async function installModule (moduleToInstall: string | NuxtModule, _inlineOptions?: any, _nuxt?: Nuxt) {
const nuxt = useNuxt()
const { nuxtModule, inlineOptions } = await normalizeModule(moduleToInstall, _inlineOptions)
// Call module
await nuxtModule.call(useModuleContainer(), inlineOptions, nuxt)
nuxt.options._installedModules = nuxt.options._installedModules || []
nuxt.options._installedModules.push({
meta: await nuxtModule.getMeta?.(),
entryPath: typeof moduleToInstall === 'string' ? resolveAlias(moduleToInstall) : undefined
})
}
// --- Internal ---
async function normalizeModule (nuxtModule: string | NuxtModule, inlineOptions?: any) {
const 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 = {}
[nuxtModule, inlineOptions] = [inlineOptions, {}]
console.warn(new Error('`installModule` is being called with old signature!'))
}
@ -29,6 +45,5 @@ export async function installModule (nuxtModule: string | NuxtModule, inlineOpti
throw new TypeError('Nuxt module should be a function: ' + nuxtModule)
}
// Call module
await nuxtModule.call(useModuleContainer(), inlineOptions, nuxt)
return { nuxtModule, inlineOptions } as { nuxtModule: NuxtModule<any>, inlineOptions: undefined | Record<string, any> }
}

View File

@ -49,6 +49,8 @@ async function initNuxt (nuxt: Nuxt) {
if (nuxt.options.typescript.shim) {
opts.references.push({ path: resolve(nuxt.options.buildDir, 'types/vue-shim.d.ts') })
}
// Add module augmentations directly to NuxtConfig
opts.references.push({ path: resolve(nuxt.options.buildDir, 'types/schema.d.ts') })
})
// Add import protection
@ -82,9 +84,9 @@ async function initNuxt (nuxt: Nuxt) {
for (const m of modulesToInstall) {
if (Array.isArray(m)) {
await installModule(m[0], m[1], nuxt)
await installModule(m[0], m[1])
} else {
await installModule(m, {}, nuxt)
await installModule(m, {})
}
}

View File

@ -1,13 +1,13 @@
import { templateUtils } from '@nuxt/kit'
import type { Nuxt, NuxtApp } from '@nuxt/schema'
import { genArrayFromRaw, genDynamicImport, genExport, genImport } from 'knitwork'
import { genArrayFromRaw, genDynamicImport, genExport, genImport, genString } from 'knitwork'
import { isAbsolute, join, relative } from 'pathe'
import escapeRE from 'escape-string-regexp'
type TemplateContext = {
nuxt: Nuxt;
app: NuxtApp;
export interface TemplateContext {
nuxt: Nuxt
app: NuxtApp
}
export const vueShim = {
@ -110,3 +110,25 @@ export { }
`
}
}
const adHocModules = ['router', 'pages', 'auto-imports', 'meta', 'components']
export const schemaTemplate = {
filename: 'types/schema.d.ts',
getContents: ({ nuxt }: TemplateContext) => {
const moduleInfo = nuxt.options._installedModules.map(m => ({
...m.meta || {},
importName: m.entryPath || m.meta?.name
})).filter(m => m.configKey && m.name && !adHocModules.includes(m.name))
return [
"import { NuxtModule } from '@nuxt/schema'",
"declare module '@nuxt/schema' {",
' interface NuxtConfig {',
...moduleInfo.filter(Boolean).map(meta =>
` [${genString(meta.configKey)}]?: typeof ${genDynamicImport(meta.importName, { wrapper: false })}.default extends NuxtModule<infer O> ? Partial<O> : Record<string, any>`
),
' }',
'}'
].join('\n')
}
}

View File

@ -288,6 +288,14 @@ export default {
*/
_modules: [],
/**
* Installed module metadata
*
* @version 3
* @private
*/
_installedModules: [],
/**
* Allows customizing the global ID used in the main HTML template as well as the main
* Vue instance name and other options.
@ -678,13 +686,13 @@ export default {
/**
* Runtime config allows passing dynamic config and environment variables to the Nuxt app context.
*
*
* The value of this object is accessible from server only using `$config` or `useRuntimeConfig`.
* It will override `publicRuntimeConfig` on the server-side.
*
* It should hold _private_ environment variables (that should not be exposed on the frontend).
* This could include a reference to your API secret tokens.
*
*
* Values are automatically replaced by matching env variables at runtime, e.g. setting an environment
* variable `API_SECRET=my-api-key` would overwrite the value in the example below.
* Note that the env variable has to be named exactly the same as the config key.
@ -705,9 +713,9 @@ export default {
/**
* Runtime config allows passing dynamic config and environment variables to the Nuxt app context.
*
*
* The value of this object is accessible from both client and server using `$config` or `useRuntimeConfig`.
*
*
* It should hold env variables that are _public_ as they will be accessible on the frontend. This could include a
* reference to your public URL.
*

View File

@ -2576,6 +2576,7 @@ __metadata:
defu: ^5.0.1
destr: ^1.1.0
enhanced-resolve: ^5.9.0
escape-string-regexp: ^5.0.0
estree-walker: ^3.0.1
externality: ^0.1.6
fs-extra: ^10.0.0