refactor: split schema to @nuxt/schema and simplify kit (#2059)

This commit is contained in:
pooya parsa 2021-11-21 17:14:46 +01:00 committed by GitHub
parent 07c7a20462
commit d68318f9d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
100 changed files with 781 additions and 696 deletions

View File

@ -12,7 +12,6 @@
"@docus/github": "1.0.4",
"@docus/social-image": "1.0.3",
"@docus/theme": "1.2.2",
"@nuxt/kit": "link:../packages/kit",
"@nuxt/typescript-build": "^2.1.0",
"fs-extra": "^10.0.0",
"jiti": "^1.12.9",

View File

@ -117,7 +117,7 @@ async function generateDocs ({ configFile, configTemplate }) {
const start = Date.now()
console.log(`Updating docs on ${configFile}`)
const template = await readFile(configTemplate, 'utf8')
const rootSchema = require('../../packages/kit/schema/config.schema.json') as Schema
const rootSchema = require('../../packages/schema/schema/config.schema.json') as Schema
const keys = Object.keys(rootSchema.properties).sort()
let generatedDocs = ''

View File

@ -4,5 +4,5 @@ set -e
cd ..
yarn="node `pwd`/.yarn/releases/yarn-*.cjs"
$yarn install
cd packages/kit
cd packages/schema
$yarn prepack --stub

View File

@ -1472,10 +1472,6 @@
untyped "^0.2.5"
upath "^2.0.1"
"@nuxt/kit@link:../packages/kit":
version "0.0.0"
uid ""
"@nuxt/kit@npm:@nuxt/kit-edge":
version "3.0.0-27247485.fb4359e"
resolved "https://registry.yarnpkg.com/@nuxt/kit-edge/-/kit-edge-3.0.0-27247485.fb4359e.tgz#efbc5a2289857a698b5d04089ecf5fe9511f4bb2"
@ -7328,16 +7324,6 @@ mlly@^0.3.0:
resolved "https://registry.yarnpkg.com/mlly/-/mlly-0.3.0.tgz#5705b2a95551d79ad33feedc046daf7c6d079748"
integrity sha512-6XphOVPsnIzuqNYlcZPMJhaMDDdrEgbLUlA7BPis8O0kkLPbOVA8GcXGeICGcnDotCtTkxAEWBT+FlhywrXTmg==
mlly@^0.3.13:
version "0.3.13"
resolved "https://registry.yarnpkg.com/mlly/-/mlly-0.3.13.tgz#5433261f38ccebba6f72fabda863a4dfc84b16d8"
integrity sha512-EXpbSPqSQLR9NEdB25uoyIYLSUvAqDEI7wUeM1HwXHsPF5Gx7cP7kuby5Mz2LfCPxBrgMnbcyPhcTCJRTQ+uvA==
mlly@^0.3.6:
version "0.3.10"
resolved "https://registry.yarnpkg.com/mlly/-/mlly-0.3.10.tgz#cf3353565c84e951311c46c8d2c8b320d90f9eb3"
integrity sha512-vD3A7naDtIOqHYZhnYUrECRO6UODWNqz6T0TS/pxwolzVoWKX/mXJF1XSM3qxruCDtkzJbzJPgesByihY/r3EA==
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@ -8170,15 +8156,6 @@ pkg-types@^0.2.1:
dependencies:
jsonc-parser "^3.0.0"
pkg-types@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-0.3.1.tgz#d7a8b69efb8777a05afc5aabfc259a29a5d6d159"
integrity sha512-BjECNgz/tsyqg0/T4Z/U7WbFQXUT24nfkxPbALcrk/uHVeZf9MrGG4tfvYtu+jsrHCFMseLQ6woQddDEBATw3A==
dependencies:
jsonc-parser "^3.0.0"
mlly "^0.3.6"
pathe "^0.2.0"
plausible-tracker@^0.3.4:
version "0.3.4"
resolved "https://registry.yarnpkg.com/plausible-tracker/-/plausible-tracker-0.3.4.tgz#6c4fb5888f62d0ab9b069fef7e3a06e2e728c65c"
@ -10253,11 +10230,6 @@ std-env@^2.2.1, std-env@^2.3.0, std-env@^2.3.1:
dependencies:
ci-info "^3.1.1"
std-env@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.0.1.tgz#bc4cbc0e438610197e34c2d79c3df30b491f5182"
integrity sha512-mC1Ps9l77/97qeOZc+HrOL7TIaOboHqMZ24dGVQrlxFcpPpfCHpH+qfUT7Dz+6mlG8+JPA1KfBQo19iC/+Ngcw==
stream-browserify@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"

View File

@ -22,6 +22,7 @@
"@nuxt/kit": "3.0.0",
"@nuxt/nitro": "3.0.0",
"@nuxt/postcss8": "^1.1.3",
"@nuxt/schema": "3.0.0",
"@vitejs/plugin-legacy": "^1.6.2",
"@vue/composition-api": "^1.4.0",
"@vueuse/head": "^0.7.2",

View File

@ -3,7 +3,7 @@ import type { CombinedVueInstance } from 'vue/types/vue'
import type { MetaInfo } from 'vue-meta'
import type VueRouter from 'vue-router'
import type { Route } from 'vue-router'
import type { RuntimeConfig } from '@nuxt/kit'
import type { RuntimeConfig } from '@nuxt/schema'
import { useNuxtApp } from './app'
export { useLazyAsyncData } from './asyncData'

View File

@ -1,7 +1,7 @@
import hash from 'hash-sum'
import { resolve } from 'pathe'
import type { Nuxt, NuxtApp } from '@nuxt/kit'
import type { Nuxt, NuxtApp } from '@nuxt/schema'
type TemplateContext = {
nuxt: Nuxt;

View File

@ -1,5 +1,5 @@
import type {} from '@nuxt/nitro'
import type { NuxtConfig as _NuxtConfig } from '@nuxt/kit'
import type { NuxtConfig as _NuxtConfig } from '@nuxt/schema'
import type { MetaInfo } from 'vue-meta'
export interface BridgeConfig {

View File

@ -4,27 +4,11 @@ export default defineBuildConfig({
declaration: true,
emitCJS: false,
entries: [
{
input: 'src/config/schema/index',
outDir: 'schema',
name: 'config',
builder: 'untyped',
defaults: {
rootDir: '/project/'
}
},
'src/index'
],
externals: [
'@nuxt/schema',
'webpack',
'vite',
'nuxt',
'nuxt3',
// type imports
'@vue/compiler-core',
'vue-meta',
'rollup-plugin-visualizer',
'webpack-bundle-analyzer',
'vue'
'vite'
]
})

View File

@ -7,25 +7,18 @@
"main": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist",
"schema"
"dist"
],
"scripts": {
"prepack": "unbuild"
},
"devDependencies": {
"@types/lodash.template": "^4",
"@types/semver": "^7",
"unbuild": "latest"
},
"dependencies": {
"@nuxt/schema": "^3.0.0",
"consola": "^2.15.3",
"create-require": "^1.1.1",
"defu": "^5.0.0",
"dotenv": "^10.0.0",
"globby": "^11.0.4",
"hash-sum": "^2.0.0",
"hookable": "^5.0.0",
"jiti": "^1.12.9",
"lodash.template": "^4.5.0",
"mlly": "^0.3.13",
@ -34,11 +27,14 @@
"rc9": "^1.2.0",
"scule": "^0.2.1",
"semver": "^7.3.5",
"std-env": "^3.0.1",
"ufo": "^0.7.9",
"unctx": "^1.0.2",
"untyped": "^0.3.0"
},
"devDependencies": {
"@types/lodash.template": "^4",
"@types/semver": "^7",
"unbuild": "latest"
},
"engines": {
"node": "^14.16.0 || ^16.11.0 || ^17.0.0"
}

123
packages/kit/src/build.ts Normal file
View File

@ -0,0 +1,123 @@
import type { WebpackPluginInstance, Configuration as WebpackConfig } from 'webpack'
import type { Plugin as VitePlugin, UserConfig as ViteConfig } from 'vite'
import { useNuxt } from './context'
export interface ExtendConfigOptions {
/**
* Install plugin on dev
*
* @default true
*/
dev?: boolean
/**
* Install plugin on build
*
* @default true
*/
build?: boolean
}
export interface ExtendWebpackConfigOptions extends ExtendConfigOptions {
/**
* Install plugin on server side
*
* @default true
*/
server?: boolean
/**
* Install plugin on client side
*
* @default true
*/
client?: boolean
/**
* Install plugin on modern build
*
* @default true
* @deprecated Nuxt 2 only
*/
modern?: boolean
}
export interface ExtendViteConfigOptions extends ExtendConfigOptions {}
/**
* Extend Webpack config
*
* The fallback function might be called multiple times
* when applying to both client and server builds.
*/
export function extendWebpackConfig (
fn: ((config: WebpackConfig)=> void),
options: ExtendWebpackConfigOptions = {}
) {
const nuxt = useNuxt()
if (options.dev === false && nuxt.options.dev) {
return
}
if (options.build === false && nuxt.options.build) {
return
}
nuxt.hook('webpack:config', (configs: WebpackConfig[]) => {
if (options.server !== false) {
const config = configs.find(i => i.name === 'server')
if (config) {
fn(config)
}
}
if (options.client !== false) {
const config = configs.find(i => i.name === 'client')
if (config) {
fn(config)
}
}
// Nuxt 2 backwards compatibility
if (options.modern !== false) {
const config = configs.find(i => i.name === 'modern')
if (config) {
fn(config)
}
}
})
}
/**
* Extend Vite config
*/
export function extendViteConfig (
fn: ((config: ViteConfig) => void),
options: ExtendViteConfigOptions = {}
) {
const nuxt = useNuxt()
if (options.dev === false && nuxt.options.dev) {
return
}
if (options.build === false && nuxt.options.build) {
return
}
nuxt.hook('vite:extend', ({ config }) => fn(config))
}
/**
* Append Webpack plugin to the config.
*/
export function addWebpackPlugin (plugin: WebpackPluginInstance, options?: ExtendWebpackConfigOptions) {
extendWebpackConfig((config) => {
config.plugins = config.plugins || []
config.plugins.push(plugin)
}, options)
}
/**
* Append Vite plugin to the config.
*/
export function addVitePlugin (plugin: VitePlugin, options?: ExtendViteConfigOptions) {
extendViteConfig((config) => {
config.plugins = config.plugins || []
config.plugins.push(plugin)
}, options)
}

View File

@ -0,0 +1,65 @@
import satisfies from 'semver/functions/satisfies.js' // npm/node-semver#381
import type { Nuxt, NuxtCompatibilityConstraints, 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 {
const issues: NuxtCompatibilityIssues = []
if (constraints.nuxt) {
const nuxtVersion = getNuxtVersion(nuxt)
const nuxtSemanticVersion = nuxtVersion.split('-').shift()
if (!satisfies(nuxtSemanticVersion, constraints.nuxt)) {
issues.push({
name: 'nuxt',
message: `Nuxt version \`${constraints.nuxt}\` is required but currently using \`${nuxtVersion}\``
})
}
}
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)
if (issues.length) {
throw new Error('Nuxt compatibility issues found:\n' + issues.toString())
}
return true
}
/**
* Check version constraints and return true if passed, otherwise returns false
*/
export function hasNuxtCompatibility (constraints: NuxtCompatibilityConstraints, nuxt: Nuxt = useNuxt()) {
return !checkNuxtCompatibilityIssues(constraints, nuxt).length
}
/**
* Check if current nuxt instance is version 2 legacy
*/
export function isNuxt2 (nuxt: Nuxt = useNuxt()) {
return getNuxtVersion(nuxt).startsWith('2.')
}
/**
* Check if current nuxt instance is version 3
*/
export function isNuxt3 (nuxt: Nuxt = useNuxt()) {
return getNuxtVersion(nuxt).startsWith('3.')
}
/**
* Get nuxt version
*/
export function getNuxtVersion (nuxt: Nuxt | any = useNuxt() /* TODO: LegacyNuxt */) {
const version = (nuxt?._version || nuxt?.version || nuxt?.constructor?.version || '').replace(/^v/g, '')
if (!version) {
throw new Error('Cannot determine nuxt version! Is currect instance passed?')
}
return version
}

View File

@ -0,0 +1,62 @@
import { pascalCase, kebabCase } from 'scule'
import type { ComponentsDir, Component } from '@nuxt/schema'
import { useNuxt } from './context'
import { ensureNuxtCompatibility } from './compatibility'
/**
* Register a directory to be scanned for components and imported only when used.
*
* Requires Nuxt 2.13+
*/
export function addComponentsDir (dir: ComponentsDir) {
const nuxt = useNuxt()
ensureNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
nuxt.options.components = nuxt.options.components || []
nuxt.hook('components:dirs', (dirs) => { dirs.push(dir) })
}
export type AddComponentOptions = { name: string, filePath: string } & Partial<Exclude<Component,
'shortPath' | 'async' | 'level' | 'import' | 'asyncImport'
>>
/**
* Register a directory to be scanned for components and imported only when used.
*
* Requires Nuxt 2.13+
*/
export function addComponent (opts: AddComponentOptions) {
const nuxt = useNuxt()
ensureNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
nuxt.options.components = nuxt.options.components || []
// Apply defaults
const component: Component = {
export: opts.export || 'default',
chunkName: 'components/' + kebabCase(opts.name),
global: opts.global ?? false,
kebabName: kebabCase(opts.name || ''),
pascalName: pascalCase(opts.name || ''),
prefetch: false,
preload: false,
// Nuxt 2 support
shortPath: opts.filePath,
async: false,
level: 0,
asyncImport: `() => import('${opts.filePath}').then(r => r['${opts.export || 'default'}'])`,
import: `require('${opts.filePath}')['${opts.export || 'default'}']`,
...opts
}
nuxt.hook('components:extend', (components: Component[]) => {
const existingComponent = components.find(c => c.pascalName === component.pascalName || c.kebabName === component.kebabName)
if (existingComponent) {
const name = existingComponent.pascalName || existingComponent.kebabName
console.warn(`Overriding ${name} component.`)
Object.assign(existingComponent, component)
} else {
components.push(component)
}
})
}

View File

@ -0,0 +1,15 @@
import { getContext } from 'unctx'
import type { Nuxt } from '@nuxt/schema'
/** Direct access to the Nuxt context - see https://github.com/unjs/unctx. */
export const nuxtCtx = getContext<Nuxt>('nuxt')
/**
* Get access to Nuxt (if run within the Nuxt context) - see https://github.com/unjs/unctx.
*
* @example
* ```js
* const nuxt = useNuxt()
* ```
*/
export const useNuxt = nuxtCtx.use

View File

@ -1,28 +1,24 @@
// Augmentations
import './types/global'
// Config
export * from './config/load'
export * from './config/env'
// Nuxt
export * from './nuxt'
// Module
export * from './module/container'
export * from './module/define'
export * from './module/install'
export * from './module/utils'
// Loader
export * from './loader/config'
export * from './loader/nuxt'
// Utils
export * from './utils/cjs'
export * from './utils/resolve'
export * from './build'
export * from './compatibility'
export * from './components'
export * from './context'
export * from './pages'
export * from './plugin'
export * from './resolve'
export * from './server'
export * from './template'
// Types
export * from './types/config'
export * from './types/hooks'
export * from './types/module'
export * from './types/nuxt'
export * from './types/components'
export * from './types/imports'
export * from './types/runtime-config'
// Internal Utils
// TODO
export * from './internal/cjs'
export * from './internal/template'

View File

@ -15,6 +15,7 @@ export interface RequireModuleOptions extends ResolveModuleOptions {
// native?: boolean
/** Clear the require cache (force fresh require) but only if not within `node_modules` */
clearCache?: boolean
/** Automatically de-default the result of requiring the module. */
interopDefault?: boolean
}

View File

@ -1,16 +1,17 @@
import { promises as fsp, existsSync } from 'fs'
import { resolve } from 'pathe'
import dotenv from 'dotenv'
import { LoadNuxtConfigOptions } from './load'
export interface LoadDotEnvOptions {
export interface DotenvOptions {
/** The project root directory (either absolute or relative to the current working directory). */
rootDir: string
/**
* What file to look in for environment variables (either absolute or relative
* to the current working directory). For example, `.env`.
*/
dotenvFile: string | false
fileName: string
/**
* Whether to interpolate variables within .env.
*
@ -22,44 +23,44 @@ export interface LoadDotEnvOptions {
* ```
*/
expand: boolean
/** An object describing environment variables (key, value pairs). */
env: NodeJS.ProcessEnv
}
export type Env = typeof process.env
/**
* Load and interpolate environment variables into `process.env`.
* If you need more control (or access to the values), consider using `loadDotenv` instead
*
* @param rootDir - The project root directory (either absolute or relative to the current working directory).
*/
export async function loadEnv (rootDir: string, options: LoadNuxtConfigOptions['envConfig'] = {}) {
export async function setupDotenv (options: DotenvOptions): Promise<Env> {
const targetEnv = options.env ?? process.env
// Load env
const env = await loadDotenv({
rootDir,
dotenvFile: options.dotenv ?? '.env',
rootDir: options.rootDir,
fileName: options.fileName ?? '.env',
env: targetEnv,
expand: options.expand ?? true
})
// Fill process.env so it is accessible in nuxt.config
// Fill process.env
for (const key in env) {
if (!key.startsWith('_') && targetEnv[key] === undefined) {
targetEnv[key] = env[key]
}
}
return env
}
/** Load environment variables into an object. */
export async function loadDotenv (opts: LoadDotEnvOptions) {
if (!opts.dotenvFile) {
return
}
export async function loadDotenv (opts: DotenvOptions): Promise<Env> {
const env = Object.create(null)
const dotenvFile = resolve(opts.rootDir, opts.dotenvFile)
const dotenvFile = resolve(opts.rootDir, opts.fileName)
if (existsSync(dotenvFile)) {
const parsed = dotenv.parse(await fsp.readFile(dotenvFile, 'utf-8'))

View File

@ -0,0 +1,42 @@
import { promises as fsp } from 'fs'
import lodashTemplate from 'lodash.template'
import hash from 'hash-sum'
import { camelCase } from 'scule'
import { basename, extname } from 'pathe'
import type { NuxtTemplate } from '@nuxt/schema'
export async function compileTemplate (template: NuxtTemplate, ctx: any) {
const data = { ...ctx, options: template.options }
if (template.src) {
try {
const srcContents = await fsp.readFile(template.src, 'utf-8')
return lodashTemplate(srcContents, {})(data)
} catch (err) {
console.error('Error compiling template: ', template)
throw err
}
}
if (template.getContents) {
return template.getContents(data)
}
throw new Error('Invalid template: ' + JSON.stringify(template))
}
const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"{(.+)}"/g, '$1')
const importName = (src: string) => `${camelCase(basename(src, extname(src))).replace(/[^a-zA-Z?\d\s:]/g, '')}_${hash(src)}`
const importSources = (sources: string | string[], { lazy = false } = {}) => {
if (!Array.isArray(sources)) {
sources = [sources]
}
return sources.map((src) => {
if (lazy) {
return `const ${importName(src)} = () => import('${src}' /* webpackChunkName: '${src}' */)`
}
return `import ${importName(src)} from '${src}'`
}).join('\n')
}
export const templateUtils = { serialize, importName, importSources }

View File

@ -3,29 +3,31 @@ import { resolve } from 'pathe'
import defu from 'defu'
import { applyDefaults } from 'untyped'
import * as rc from 'rc9'
import { tryResolveModule, requireModule, scanRequireTree } from '../utils/cjs'
import { NuxtOptions } from '../types/config'
import nuxtConfigSchema from './schema'
import { loadEnv } from './env'
import type { NuxtOptions } from '@nuxt/schema'
import { NuxtConfigSchema } from '@nuxt/schema'
import { tryResolveModule, requireModule, scanRequireTree } from '../internal/cjs'
import { setupDotenv, DotenvOptions } from '../internal/dotenv'
export interface LoadNuxtConfigOptions {
/** Your project root directory (either absolute or relative to the current working directory). */
rootDir?: string
/** The path to your `nuxt.config` file (either absolute or relative to your project `rootDir`). */
configFile?: string
/** Any overrides to your Nuxt configuration. */
config?: Record<string, any>
envConfig?: {
dotenv?: string | false
env?: Record<string, string | undefined>
expand?: boolean
}
/** Configuration for loading dotenv */
dotenv?: DotenvOptions | false
}
export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<NuxtOptions> {
const rootDir = resolve(process.cwd(), opts.rootDir || '.')
await loadEnv(rootDir, opts.envConfig)
if (opts.dotenv !== false) {
await setupDotenv({ rootDir, ...opts.dotenv })
}
const nuxtConfigFile = tryResolveModule(resolve(rootDir, opts.configFile || 'nuxt.config'))
@ -58,5 +60,5 @@ export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<Nuxt
}
// Resolve and apply defaults
return applyDefaults(nuxtConfigSchema, nuxtConfig) as NuxtOptions
return applyDefaults(NuxtConfigSchema, nuxtConfig) as NuxtOptions
}

View File

@ -1,22 +1,7 @@
import { getContext } from 'unctx'
import { readPackageJSON, resolvePackageJSON } from 'pkg-types'
import { importModule, tryImportModule, RequireModuleOptions } from './utils/cjs'
import type { Nuxt } from './types/nuxt'
import type { NuxtConfig } from './types/config'
import type { LoadNuxtConfigOptions } from './config/load'
/** Direct access to the Nuxt context - see https://github.com/unjs/unctx. */
export const nuxtCtx = getContext<Nuxt>('nuxt')
/**
* Get access to Nuxt (if run within the Nuxt context) - see https://github.com/unjs/unctx.
*
* @example
* ```js
* const nuxt = useNuxt()
* ```
*/
export const useNuxt = nuxtCtx.use
import type { Nuxt, NuxtConfig } from '@nuxt/schema'
import { importModule, tryImportModule, RequireModuleOptions } from '../internal/cjs'
import type { LoadNuxtConfigOptions } from './config'
export interface LoadNuxtOptions extends LoadNuxtConfigOptions {
rootDir: string
@ -52,7 +37,7 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
for: opts.dev ? 'dev' : 'build',
configOverrides: opts.config,
ready: opts.ready,
envConfig: opts.envConfig
envConfig: opts.dotenv // TODO: Backward format convertion
})
return nuxt as Nuxt

View File

@ -1,36 +1,27 @@
import { parse, relative } from 'pathe'
import consola from 'consola'
import type { Nuxt, NuxtPluginTemplate, NuxtTemplate } from '../types/nuxt'
import { chainFn } from '../utils/task'
import { isNuxt2, addTemplate, addPluginTemplate, addServerMiddleware } from './utils'
import type { Nuxt, NuxtPluginTemplate, NuxtTemplate, ModuleContainer } from '@nuxt/schema'
import { chainFn } from '../internal/task'
import { addTemplate } from '../template'
import { addServerMiddleware } from '../server'
import { isNuxt2 } from '../compatibility'
import { addPluginTemplate } from '../plugin'
import { installModule } from './install'
/** Legacy ModuleContainer for backwards compatibility with existing Nuxt 2 modules. */
export function createModuleContainer (nuxt: Nuxt) {
return {
export function createModuleContainer (nuxt: Nuxt): ModuleContainer {
return <ModuleContainer>{
nuxt,
options: nuxt.options,
/**
* Returns a resolved promise immediately.
*
* @deprecated
*/
ready () {
return Promise.resolve()
},
ready () { return Promise.resolve() },
addVendor () {},
/** @deprecated */
addVendor () {
console.warn('addVendor has been deprecated and has no effect.')
},
requireModule: installModule,
addModule: installModule,
addServerMiddleware,
/**
* Renders given template using lodash template during build into the project buildDir (`.nuxt`).
*
* If a fileName is not provided or the template is string, target file name defaults to
* [dirName].[fileName].[pathHash].[ext].
*/
addTemplate (template: string | NuxtTemplate) {
if (typeof template === 'string') {
template = { src: template }
@ -41,29 +32,10 @@ export function createModuleContainer (nuxt: Nuxt) {
return addTemplate(template)
},
/**
* Registers a plugin template and prepends it to the plugins[] array.
*
* Note: You can use mode or .client and .server modifiers with fileName option
* to use plugin only in client or server side.
*
* If you choose to specify a fileName, you can configure a custom path for the
* fileName too, so you can choose the folder structure inside .nuxt folder in
* order to prevent name collisioning:
*
* @example
* ```js
* this.addPlugin({
* src: path.resolve(__dirname, 'templates/foo.js'),
* fileName: 'foo.server.js' // [optional] only include in server bundle
* })
* ```
*/
addPlugin (pluginTemplate: NuxtPluginTemplate): NuxtPluginTemplate {
return addPluginTemplate(pluginTemplate)
},
/** Register a custom layout. If its name is 'error' it will override the default error layout. */
addLayout (tmpl: NuxtTemplate, name: string) {
const { filename, src } = addTemplate(tmpl)
const layoutName = name || parse(src).name
@ -72,36 +44,22 @@ export function createModuleContainer (nuxt: Nuxt) {
if (layout) {
consola.warn(`Duplicate layout registration, "${layoutName}" has been registered as "${layout}"`)
}
// Add to nuxt layouts
nuxt.options.layouts[layoutName] = `./${filename}`
// If error layout, set ErrorPage
if (name === 'error') {
this.addErrorLayout(filename)
}
},
/**
* Set the layout that will render Nuxt errors. It should already have been added via addLayout or addTemplate.
*
* @param dst - Path to layout file within the buildDir (`.nuxt/<dst>.vue`)
*/
addErrorLayout (dst: string) {
const relativeBuildDir = relative(nuxt.options.rootDir, nuxt.options.buildDir)
nuxt.options.ErrorPage = `~/${relativeBuildDir}/${dst}`
},
/** Adds a new server middleware to the end of the server middleware array. */
addServerMiddleware,
/** Allows extending webpack build config by chaining `options.build.extend` function. */
extendBuild (fn) {
// @ts-ignore
nuxt.options.build.extend = chainFn(nuxt.options.build.extend, fn)
},
/** Allows extending routes by chaining `options.build.extendRoutes` function. */
extendRoutes (fn) {
if (isNuxt2(nuxt)) {
nuxt.options.router.extendRoutes = chainFn(nuxt.options.router.extendRoutes, fn)
@ -113,14 +71,6 @@ export function createModuleContainer (nuxt: Nuxt) {
}
})
}
},
/** `requireModule` is a shortcut for `addModule` */
requireModule: installModule,
/** Registers a module. moduleOpts can be a string or an array ([src, options]). */
addModule: installModule
}
}
}
export type ModuleContainer = ReturnType<typeof createModuleContainer>

View File

@ -3,10 +3,10 @@ import defu from 'defu'
import { applyDefaults } from 'untyped'
import consola from 'consola'
import { dirname } from 'pathe'
import { useNuxt, nuxtCtx } from '../nuxt'
import type { Nuxt, NuxtTemplate } from '../types/nuxt'
import type { NuxtModule, LegacyNuxtModule, ModuleOptions } from '../types/module'
import { checkNuxtCompatibilityIssues, compileTemplate, isNuxt2, templateUtils } from './utils'
import type { Nuxt, NuxtTemplate, NuxtModule, LegacyNuxtModule, ModuleOptions } from '@nuxt/schema'
import { useNuxt, nuxtCtx } from '../context'
import { isNuxt2, checkNuxtCompatibilityIssues } from '../compatibility'
import { templateUtils, compileTemplate } from '../internal/template'
/**
* Define a Nuxt module, automatically merging defaults with user provided options, installing

View File

@ -1,7 +1,6 @@
import { resolveModule, requireModule, importModule } from '../utils/cjs'
import { resolveAlias } from '../utils/resolve'
import type { LegacyNuxtModule, NuxtModule, ModuleMeta, ModuleInstallOptions, ModuleOptions, ModuleSrc } from '../types/module'
import type { Nuxt } from '../types/nuxt'
import type { LegacyNuxtModule, NuxtModule, ModuleMeta, ModuleInstallOptions, ModuleOptions, ModuleSrc, Nuxt } from '@nuxt/schema'
import { resolveModule, requireModule, importModule } from '../internal/cjs'
import { resolveAlias } from '../resolve'
import { defineNuxtModule } from './define'
import { createModuleContainer } from './container'

View File

@ -1,446 +0,0 @@
import { existsSync, promises as fsp } from 'fs'
import { basename, extname, normalize, parse, resolve } from 'pathe'
import lodashTemplate from 'lodash.template'
import hash from 'hash-sum'
import { pascalCase, camelCase, kebabCase } from 'scule'
import type { WebpackPluginInstance, Configuration as WebpackConfig } from 'webpack'
import type { Plugin as VitePlugin, UserConfig as ViteConfig } from 'vite'
import satisfies from 'semver/functions/satisfies.js' // npm/node-semver#381
import { NuxtCompatibilityConstraints, NuxtCompatibilityIssues } from '../types/module'
import { Nuxt } from '../types/nuxt'
import { useNuxt } from '../nuxt'
import type { NuxtTemplate, NuxtPlugin, NuxtPluginTemplate } from '../types/nuxt'
import type { ComponentsDir, Component } from '../types/components'
import type { NuxtHooks } from '../types/hooks'
/**
* Renders given template using lodash template during build into the project buildDir
*/
export function addTemplate (_template: NuxtTemplate | string) {
const nuxt = useNuxt()
// Noprmalize template
const template = normalizeTemplate(_template)
// Remove any existing template with the same filename
nuxt.options.build.templates = nuxt.options.build.templates
.filter(p => normalizeTemplate(p).filename !== template.filename)
// Add to templates array
nuxt.options.build.templates.push(template)
return template
}
/**
* Normalize a nuxt template object
*/
export function normalizeTemplate (template: NuxtTemplate | string): NuxtTemplate {
if (!template) {
throw new Error('Invalid template: ' + JSON.stringify(template))
}
// Normalize
if (typeof template === 'string') {
template = { src: template }
} else {
template = { ...template }
}
// Use src if provided
if (template.src) {
if (!existsSync(template.src)) {
throw new Error('Template not found: ' + template.src)
}
if (!template.filename) {
const srcPath = parse(template.src)
template.filename = template.fileName ||
`${basename(srcPath.dir)}.${srcPath.name}.${hash(template.src)}${srcPath.ext}`
}
}
if (!template.src && !template.getContents) {
throw new Error('Invalid template. Either getContents or src options should be provided: ' + JSON.stringify(template))
}
if (!template.filename) {
throw new Error('Invalid template. Either filename should be provided: ' + JSON.stringify(template))
}
// Resolve dst
if (!template.dst) {
const nuxt = useNuxt()
template.dst = resolve(nuxt.options.buildDir, template.filename)
}
return template
}
/**
* Normalize a nuxt plugin object
*/
export function normalizePlugin (plugin: NuxtPlugin | string): NuxtPlugin {
// Normalize src
if (typeof plugin === 'string') {
plugin = { src: plugin }
} else {
plugin = { ...plugin }
}
if (!plugin.src) {
throw new Error('Invalid plugin. src option is required: ' + JSON.stringify(plugin))
}
// Normalize full path to plugin
plugin.src = normalize(plugin.src)
// Normalize mode
if (plugin.ssr) {
plugin.mode = 'server'
}
if (!plugin.mode) {
const [, mode = 'all'] = plugin.src.match(/\.(server|client)(\.\w+)*$/) || []
plugin.mode = mode as 'all' | 'client' | 'server'
}
return plugin
}
/**
* Registers a nuxt plugin and to the plugins array.
*
* Note: You can use mode or .client and .server modifiers with fileName option
* to use plugin only in client or server side.
*
* Note: By default plugin is prepended to the plugins array. You can use second argument to append (push) instead.
*
* @example
* ```js
* addPlugin({
* src: path.resolve(__dirname, 'templates/foo.js'),
* filename: 'foo.server.js' // [optional] only include in server bundle
* })
* ```
*/
export interface AddPluginOptions { append?: Boolean }
export function addPlugin (_plugin: NuxtPlugin | string, opts: AddPluginOptions = {}) {
const nuxt = useNuxt()
// Normalize plugin
const plugin = normalizePlugin(_plugin)
// Remove any existing plugin with the same src
nuxt.options.plugins = nuxt.options.plugins.filter(p => normalizePlugin(p).src !== plugin.src)
// Prepend to array by default to be before user provided plugins since is usually used by modules
nuxt.options.plugins[opts.append ? 'push' : 'unshift'](plugin)
return plugin
}
/**
* Adds a template and registers as a nuxt plugin.
*/
export function addPluginTemplate (plugin: NuxtPluginTemplate | string, opts: AddPluginOptions = {}): NuxtPluginTemplate {
if (typeof plugin === 'string') {
plugin = { src: plugin }
}
// Update plugin src to template destination
plugin.src = addTemplate(plugin).dst
return addPlugin(plugin, opts)
}
/** Adds a new server middleware to the end of the server middleware array. */
export function addServerMiddleware (middleware) {
useNuxt().options.serverMiddleware.push(middleware)
}
export interface ExtendConfigOptions {
/**
* Install plugin on dev
*
* @default true
*/
dev?: boolean
/**
* Install plugin on build
*
* @default true
*/
build?: boolean
}
export interface ExtendWebpackConfigOptions extends ExtendConfigOptions {
/**
* Install plugin on server side
*
* @default true
*/
server?: boolean
/**
* Install plugin on client side
*
* @default true
*/
client?: boolean
/**
* Install plugin on modern build
*
* @default true
* @deprecated Nuxt 2 only
*/
modern?: boolean
}
export interface ExtendViteConfigOptions extends ExtendConfigOptions {}
/**
* Extend Webpack config
*
* The fallback function might be called multiple times
* when applying to both client and server builds.
*/
export function extendWebpackConfig (
fn: ((config: WebpackConfig)=> void),
options: ExtendWebpackConfigOptions = {}
) {
const nuxt = useNuxt()
if (options.dev === false && nuxt.options.dev) {
return
}
if (options.build === false && nuxt.options.build) {
return
}
nuxt.hook('webpack:config', (configs: WebpackConfig[]) => {
if (options.server !== false) {
const config = configs.find(i => i.name === 'server')
if (config) {
fn(config)
}
}
if (options.client !== false) {
const config = configs.find(i => i.name === 'client')
if (config) {
fn(config)
}
}
// Nuxt 2 backwards compatibility
if (options.modern !== false) {
const config = configs.find(i => i.name === 'modern')
if (config) {
fn(config)
}
}
})
}
/**
* Extend Vite config
*/
export function extendViteConfig (
fn: ((config: ViteConfig) => void),
options: ExtendViteConfigOptions = {}
) {
const nuxt = useNuxt()
if (options.dev === false && nuxt.options.dev) {
return
}
if (options.build === false && nuxt.options.build) {
return
}
nuxt.hook('vite:extend', ({ config }) => fn(config))
}
/**
* Append Webpack plugin to the config.
*/
export function addWebpackPlugin (plugin: WebpackPluginInstance, options?: ExtendWebpackConfigOptions) {
extendWebpackConfig((config) => {
config.plugins = config.plugins || []
config.plugins.push(plugin)
}, options)
}
/**
* Append Vite plugin to the config.
*/
export function addVitePlugin (plugin: VitePlugin, options?: ExtendViteConfigOptions) {
extendViteConfig((config) => {
config.plugins = config.plugins || []
config.plugins.push(plugin)
}, options)
}
export async function compileTemplate (template: NuxtTemplate, ctx: any) {
const data = { ...ctx, options: template.options }
if (template.src) {
try {
const srcContents = await fsp.readFile(template.src, 'utf-8')
return lodashTemplate(srcContents, {})(data)
} catch (err) {
console.error('Error compiling template: ', template)
throw err
}
}
if (template.getContents) {
return template.getContents(data)
}
throw new Error('Invalid template: ' + JSON.stringify(template))
}
/**
* Register a directory to be scanned for components and imported only when used.
*
* Requires Nuxt 2.13+
*/
export function addComponentsDir (dir: ComponentsDir) {
const nuxt = useNuxt()
ensureNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
nuxt.options.components = nuxt.options.components || []
nuxt.hook('components:dirs', (dirs) => { dirs.push(dir) })
}
export type AddComponentOptions = { name: string, filePath: string } & Partial<Exclude<Component,
'shortPath' | 'async' | 'level' | 'import' | 'asyncImport'
>>
/**
* Register a directory to be scanned for components and imported only when used.
*
* Requires Nuxt 2.13+
*/
export function addComponent (opts: AddComponentOptions) {
const nuxt = useNuxt()
ensureNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
nuxt.options.components = nuxt.options.components || []
// Apply defaults
const component: Component = {
export: opts.export || 'default',
chunkName: 'components/' + kebabCase(opts.name),
global: opts.global ?? false,
kebabName: kebabCase(opts.name || ''),
pascalName: pascalCase(opts.name || ''),
prefetch: false,
preload: false,
// Nuxt 2 support
shortPath: opts.filePath,
async: false,
level: 0,
asyncImport: `() => import('${opts.filePath}').then(r => r['${opts.export || 'default'}'])`,
import: `require('${opts.filePath}')['${opts.export || 'default'}']`,
...opts
}
nuxt.hook('components:extend', (components: Component[]) => {
const existingComponent = components.find(c => c.pascalName === component.pascalName || c.kebabName === component.kebabName)
if (existingComponent) {
const name = existingComponent.pascalName || existingComponent.kebabName
console.warn(`Overriding ${name} component.`)
Object.assign(existingComponent, component)
} else {
components.push(component)
}
})
}
export function extendPages (cb: NuxtHooks['pages:extend']) {
const nuxt = useNuxt()
if (isNuxt2(nuxt)) {
nuxt.hook('build:extendRoutes', cb)
} else {
nuxt.hook('pages:extend', cb)
}
}
const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"{(.+)}"/g, '$1')
const importName = (src: string) => `${camelCase(basename(src, extname(src))).replace(/[^a-zA-Z?\d\s:]/g, '')}_${hash(src)}`
const importSources = (sources: string | string[], { lazy = false } = {}) => {
if (!Array.isArray(sources)) {
sources = [sources]
}
return sources.map((src) => {
if (lazy) {
return `const ${importName(src)} = () => import('${src}' /* webpackChunkName: '${src}' */)`
}
return `import ${importName(src)} from '${src}'`
}).join('\n')
}
export const templateUtils = {
serialize,
importName,
importSources
}
/**
* Check if current nuxt instance is version 2 legacy
*/
export function isNuxt2 (nuxt: Nuxt = useNuxt()) {
return getNuxtVersion(nuxt).startsWith('2.')
}
/**
* Check if current nuxt instance is version 3
*/
export function isNuxt3 (nuxt: Nuxt = useNuxt()) {
return getNuxtVersion(nuxt).startsWith('3.')
}
/**
* Get nuxt version
*/
export function getNuxtVersion (nuxt: Nuxt | any = useNuxt() /* TODO: LegacyNuxt */) {
const version = (nuxt?._version || nuxt?.version || nuxt?.constructor?.version || '').replace(/^v/g, '')
if (!version) {
throw new Error('Cannot determine nuxt version! Is currect instance passed?')
}
return version
}
/**
* Check version constraints and return incompatibility issues as an array
*/
export function checkNuxtCompatibilityIssues (constraints: NuxtCompatibilityConstraints, nuxt: Nuxt = useNuxt()): NuxtCompatibilityIssues {
const issues: NuxtCompatibilityIssues = []
if (constraints.nuxt) {
const nuxtVersion = getNuxtVersion(nuxt)
const nuxtSemanticVersion = nuxtVersion.split('-').shift()
if (!satisfies(nuxtSemanticVersion, constraints.nuxt)) {
issues.push({
name: 'nuxt',
message: `Nuxt version \`${constraints.nuxt}\` is required but currently using \`${nuxtVersion}\``
})
}
}
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)
if (issues.length) {
throw new Error('Nuxt compatibility issues found:\n' + issues.toString())
}
return true
}
/**
* Check version constraints and return true if passed, otherwise returns false
*/
export function hasNuxtCompatibility (constraints: NuxtCompatibilityConstraints, nuxt: Nuxt = useNuxt()) {
return !checkNuxtCompatibilityIssues(constraints, nuxt).length
}

12
packages/kit/src/pages.ts Normal file
View File

@ -0,0 +1,12 @@
import type { NuxtHooks } from '@nuxt/schema'
import { useNuxt } from './context'
import { isNuxt2 } from './compatibility'
export function extendPages (cb: NuxtHooks['pages:extend']) {
const nuxt = useNuxt()
if (isNuxt2(nuxt)) {
nuxt.hook('build:extendRoutes', cb)
} else {
nuxt.hook('pages:extend', cb)
}
}

View File

@ -0,0 +1,80 @@
import { normalize } from 'pathe'
import type { NuxtPlugin, NuxtPluginTemplate } from '@nuxt/schema'
import { useNuxt } from './context'
import { addTemplate } from './template'
/**
* Normalize a nuxt plugin object
*/
export function normalizePlugin (plugin: NuxtPlugin | string): NuxtPlugin {
// Normalize src
if (typeof plugin === 'string') {
plugin = { src: plugin }
} else {
plugin = { ...plugin }
}
if (!plugin.src) {
throw new Error('Invalid plugin. src option is required: ' + JSON.stringify(plugin))
}
// Normalize full path to plugin
plugin.src = normalize(plugin.src)
// Normalize mode
if (plugin.ssr) {
plugin.mode = 'server'
}
if (!plugin.mode) {
const [, mode = 'all'] = plugin.src.match(/\.(server|client)(\.\w+)*$/) || []
plugin.mode = mode as 'all' | 'client' | 'server'
}
return plugin
}
/**
* Registers a nuxt plugin and to the plugins array.
*
* Note: You can use mode or .client and .server modifiers with fileName option
* to use plugin only in client or server side.
*
* Note: By default plugin is prepended to the plugins array. You can use second argument to append (push) instead.
*
* @example
* ```js
* addPlugin({
* src: path.resolve(__dirname, 'templates/foo.js'),
* filename: 'foo.server.js' // [optional] only include in server bundle
* })
* ```
*/
export interface AddPluginOptions { append?: Boolean }
export function addPlugin (_plugin: NuxtPlugin | string, opts: AddPluginOptions = {}) {
const nuxt = useNuxt()
// Normalize plugin
const plugin = normalizePlugin(_plugin)
// Remove any existing plugin with the same src
nuxt.options.plugins = nuxt.options.plugins.filter(p => normalizePlugin(p).src !== plugin.src)
// Prepend to array by default to be before user provided plugins since is usually used by modules
nuxt.options.plugins[opts.append ? 'push' : 'unshift'](plugin)
return plugin
}
/**
* Adds a template and registers as a nuxt plugin.
*/
export function addPluginTemplate (plugin: NuxtPluginTemplate | string, opts: AddPluginOptions = {}): NuxtPluginTemplate {
if (typeof plugin === 'string') {
plugin = { src: plugin }
}
// Update plugin src to template destination
plugin.src = addTemplate(plugin).dst
return addPlugin(plugin, opts)
}

View File

@ -0,0 +1,6 @@
import { useNuxt } from './context'
/** Adds a new server middleware to the end of the server middleware array. */
export function addServerMiddleware (middleware) {
useNuxt().options.serverMiddleware.push(middleware)
}

View File

@ -0,0 +1,68 @@
import { existsSync } from 'fs'
import { basename, parse, resolve } from 'pathe'
import hash from 'hash-sum'
import type { NuxtTemplate } 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) {
const nuxt = useNuxt()
// Noprmalize template
const template = normalizeTemplate(_template)
// Remove any existing template with the same filename
nuxt.options.build.templates = nuxt.options.build.templates
.filter(p => normalizeTemplate(p).filename !== template.filename)
// Add to templates array
nuxt.options.build.templates.push(template)
return template
}
/**
* Normalize a nuxt template object
*/
export function normalizeTemplate (template: NuxtTemplate | string): NuxtTemplate {
if (!template) {
throw new Error('Invalid template: ' + JSON.stringify(template))
}
// Normalize
if (typeof template === 'string') {
template = { src: template }
} else {
template = { ...template }
}
// Use src if provided
if (template.src) {
if (!existsSync(template.src)) {
throw new Error('Template not found: ' + template.src)
}
if (!template.filename) {
const srcPath = parse(template.src)
template.filename = template.fileName ||
`${basename(srcPath.dir)}.${srcPath.name}.${hash(template.src)}${srcPath.ext}`
}
}
if (!template.src && !template.getContents) {
throw new Error('Invalid template. Either getContents or src options should be provided: ' + JSON.stringify(template))
}
if (!template.filename) {
throw new Error('Invalid template. Either filename should be provided: ' + JSON.stringify(template))
}
// Resolve dst
if (!template.dst) {
const nuxt = useNuxt()
template.dst = resolve(nuxt.options.buildDir, template.filename)
}
return template
}

View File

@ -1,5 +0,0 @@
export interface PublicRuntimeConfig extends Record<string, any> { }
export interface PrivateRuntimeConfig extends PublicRuntimeConfig { }
type _RuntimeConfig = PublicRuntimeConfig & Partial<PrivateRuntimeConfig>
export interface RuntimeConfig extends _RuntimeConfig { }

View File

@ -18,5 +18,8 @@ export default defineBuildConfig({
'ora',
'vue-bundle-renderer',
'vue-server-renderer'
],
externals: [
'@nuxt/schema'
]
})

View File

@ -72,6 +72,7 @@
"vue-server-renderer": "^2.6.14"
},
"devDependencies": {
"@nuxt/schema": "3.0.0",
"@types/fs-extra": "^9.0.13",
"@types/http-proxy": "^1.17.7",
"@types/node-fetch": "^3.0.2",

View File

@ -3,7 +3,7 @@ import { resolve } from 'pathe'
import defu from 'defu'
import { createHooks, Hookable, NestedHooks } from 'hookable'
import type { Preset } from 'unenv'
import type { NuxtHooks, NuxtOptions } from '@nuxt/kit'
import type { NuxtHooks, NuxtOptions } from '@nuxt/schema'
import type { PluginVisualizerOptions } from 'rollup-plugin-visualizer'
import { tryImport, resolvePath, detectTarget, extendPreset } from './utils'
import * as PRESETS from './presets'

View File

@ -2,7 +2,8 @@ import { resolve, join, extname } from 'pathe'
import { joinURL } from 'ufo'
import globby from 'globby'
import { watch } from 'chokidar'
import { tryResolvePath, Nuxt } from '@nuxt/kit'
import { tryResolvePath } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
import type { Middleware } from 'h3'
export interface ServerMiddleware {

View File

@ -11,7 +11,7 @@ declare module '#assets' {
}
declare module '#config' {
import type { PublicRuntimeConfig, PrivateRuntimeConfig } from '@nuxt/kit'
import type { PublicRuntimeConfig, PrivateRuntimeConfig } from '@nuxt/schema'
export const privateConfig: PrivateRuntimeConfig
export const publicConfig: PublicRuntimeConfig
const runtimeConfig: PrivateRuntimeConfig & PublicRuntimeConfig

View File

@ -10,6 +10,14 @@ export default defineBuildConfig({
],
externals: [
'@nuxt/kit',
'fsevents'
'@nuxt/schema',
'fsevents',
// TODO: Fix rollup/unbuild issue
'node:buffer',
'node:path',
'node:child_process',
'node:process',
'node:path',
'node:os'
]
})

View File

@ -19,6 +19,7 @@
"devDependencies": {
"@nuxt/design": "0.1.5",
"@nuxt/kit": "3.0.0",
"@nuxt/schema": "3.0.0",
"@types/clear": "^0",
"@types/mri": "^1.1.1",
"@types/rimraf": "^3",

View File

@ -1,7 +1,7 @@
import { resolve, relative } from 'pathe'
import chokidar from 'chokidar'
import debounce from 'p-debounce'
import type { Nuxt } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
import consola from 'consola'
import { createServer, createLoadingHandler } from '../utils/server'
import { showBanner } from '../utils/banner'

View File

@ -1,6 +1,6 @@
import { promises as fsp } from 'fs'
import { join, relative, resolve } from 'pathe'
import { Nuxt, TSReference } from '@nuxt/kit'
import { Nuxt, TSReference } from '@nuxt/schema'
import defu from 'defu'
import type { TSConfig } from 'pkg-types'
import { getModulePaths, getNearestPackage } from './cjs'

View File

@ -22,6 +22,7 @@
"@nuxt/design": "^0.1.5",
"@nuxt/kit": "3.0.0",
"@nuxt/nitro": "3.0.0",
"@nuxt/schema": "3.0.0",
"@nuxt/vite-builder": "3.0.0",
"@nuxt/webpack-builder": "3.0.0",
"@vue/reactivity": "^3.2.22",

View File

@ -2,7 +2,7 @@
import { getCurrentInstance, reactive } from 'vue'
import type { App, VNode } from 'vue'
import { createHooks, Hookable } from 'hookable'
import type { RuntimeConfig } from '@nuxt/kit'
import type { RuntimeConfig } from '@nuxt/schema'
import { legacyPlugin, LegacyContext } from './compat/legacy-app'
type NuxtMeta = {

View File

@ -3,7 +3,7 @@ import { parse as parsePath, join } from 'pathe'
import globby from 'globby'
import { findExports } from 'mlly'
import { camelCase } from 'scule'
import { AutoImport } from '@nuxt/kit'
import { AutoImport } from '@nuxt/schema'
import { filterInPlace } from './utils'
export async function scanForComposables (dir: string, autoImports: AutoImport[]) {

View File

@ -1,4 +1,4 @@
import type { AutoImport } from '@nuxt/kit'
import type { AutoImport } from '@nuxt/schema'
export interface AutoImportContext {
autoImports: AutoImport[]

View File

@ -1,4 +1,4 @@
import type { AutoImportSource } from '@nuxt/kit'
import type { AutoImportSource } from '@nuxt/schema'
export const Nuxt3AutoImports: AutoImportSource[] = [
// #app

View File

@ -1,5 +1,5 @@
import { addVitePlugin, addWebpackPlugin, defineNuxtModule, addTemplate, resolveAlias, addPluginTemplate, useNuxt } from '@nuxt/kit'
import type { AutoImportsOptions } from '@nuxt/kit'
import type { AutoImportsOptions } from '@nuxt/schema'
import { isAbsolute, join, relative, resolve, normalize } from 'pathe'
import { TransformPlugin } from './transform'
import { Nuxt3AutoImports } from './imports'

View File

@ -1,4 +1,4 @@
import type { AutoImport } from '@nuxt/kit'
import type { AutoImport } from '@nuxt/schema'
export function toImports (autoImports: AutoImport[], isCJS = false) {
const aliasKeyword = isCJS ? ' : ' : ' as '

View File

@ -1,6 +1,6 @@
import { createUnplugin } from 'unplugin'
import { parseQuery, parseURL } from 'ufo'
import { Component } from '@nuxt/kit'
import { Component } from '@nuxt/schema'
interface LoaderOptions {
getComponents(): Component[]

View File

@ -1,7 +1,7 @@
import { statSync } from 'fs'
import { resolve } from 'pathe'
import { defineNuxtModule, resolveAlias, addVitePlugin, addWebpackPlugin } from '@nuxt/kit'
import type { Component, ComponentsDir, ComponentsOptions } from '@nuxt/kit'
import type { Component, ComponentsDir, ComponentsOptions } from '@nuxt/schema'
import { componentsTemplate, componentsTypeTemplate } from './templates'
import { scanComponents } from './scan'
import { loaderPlugin } from './loader'

View File

@ -1,7 +1,7 @@
import { basename, extname, join, dirname, relative } from 'pathe'
import globby from 'globby'
import { pascalCase, splitByCase } from 'scule'
import type { ScanDir, Component, ComponentsDir } from '@nuxt/kit'
import type { ScanDir, Component, ComponentsDir } from '@nuxt/schema'
export function sortDirsByPathLength ({ path: pathA }: ScanDir, { path: pathB }: ScanDir): number {
return pathB.split(/[\\/]/).filter(Boolean).length - pathA.split(/[\\/]/).filter(Boolean).length

View File

@ -1,6 +1,6 @@
import { relative } from 'pathe'
import type { Component } from '@nuxt/kit'
import type { Component } from '@nuxt/schema'
export type ComponentsTemplateOptions = {
buildDir?: string

View File

@ -1,7 +1,8 @@
import { promises as fsp } from 'fs'
import { resolve } from 'pathe'
import defu from 'defu'
import { tryResolvePath, resolveFiles, Nuxt, NuxtApp, normalizePlugin, normalizeTemplate, compileTemplate, templateUtils } from '@nuxt/kit'
import type { Nuxt, NuxtApp } from '@nuxt/schema'
import { tryResolvePath, resolveFiles, normalizePlugin, normalizeTemplate, compileTemplate, templateUtils } from '@nuxt/kit'
import * as defaultTemplates from './templates'

View File

@ -1,5 +1,5 @@
import chokidar from 'chokidar'
import { Nuxt } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
import { createApp, generateApp } from './app'
export async function build (nuxt: Nuxt) {

View File

@ -1,6 +1,6 @@
import { resolve } from 'pathe'
import { wpfs, getNitroContext, createDevServer, resolveMiddleware, build, prepare, generate } from '@nuxt/nitro'
import type { Nuxt } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
export function initNitro (nuxt: Nuxt) {
// Create contexts

View File

@ -1,6 +1,7 @@
import { resolve } from 'pathe'
import { createHooks } from 'hookable'
import { loadNuxtConfig, LoadNuxtOptions, Nuxt, NuxtOptions, NuxtConfig, nuxtCtx, installModule, ModuleContainer, NuxtHooks, addComponent } from '@nuxt/kit'
import type { Nuxt, NuxtOptions, NuxtConfig, ModuleContainer, NuxtHooks } from '@nuxt/schema'
import { loadNuxtConfig, LoadNuxtOptions, nuxtCtx, installModule, addComponent } from '@nuxt/kit'
import pagesModule from '../pages/module'
import metaModule from '../meta/module'
import componentsModule from '../components/module'

View File

@ -1,5 +1,5 @@
import { templateUtils } from '@nuxt/kit'
import type { Nuxt, NuxtApp } from '@nuxt/kit'
import type { Nuxt, NuxtApp } from '@nuxt/schema'
import { relative } from 'pathe'

View File

@ -1,7 +1,7 @@
import { isFunction } from '@vue/shared'
import { computed } from '@vue/reactivity'
import type { ComputedGetter } from '@vue/reactivity'
import type { MetaObject } from './types'
import type { MetaObject } from '../../../../schema/src/types/meta'
import { useNuxtApp } from '#app'
/**

View File

@ -1,2 +1,2 @@
export * from './composables'
export * from './types'
export * from '../../../../schema/src/types/meta'

View File

@ -1,6 +1,7 @@
import { basename, extname, relative, resolve } from 'pathe'
import { encodePath } from 'ufo'
import { Nuxt, resolveFiles, NuxtRoute } from '@nuxt/kit'
import type { Nuxt, NuxtRoute } from '@nuxt/schema'
import { resolveFiles } from '@nuxt/kit'
import { kebabCase } from 'scule'
enum SegmentParserState {

View File

@ -1,4 +1,4 @@
import type { AutoImport } from '@nuxt/kit'
import type { AutoImport } from '@nuxt/schema'
import { expect } from 'chai'
import * as VueFunctions from 'vue'
import { AutoImportContext, updateAutoImportContext } from '../src/auto-imports/context'

View File

@ -1,5 +1,5 @@
import { resolve } from 'path'
import { ComponentsDir } from '@nuxt/kit'
import { ComponentsDir } from '@nuxt/schema'
import { expect } from 'chai'
import { scanComponents } from '../src/components/scan'

1
packages/schema/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/schema

View File

@ -0,0 +1,3 @@
# Nuxt Schema
> Cross-version Nuxt typedefs and defaults

View File

@ -0,0 +1,33 @@
import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
declaration: true,
emitCJS: false,
entries: [
{
input: 'src/config/index',
outDir: 'schema',
name: 'config',
builder: 'untyped',
defaults: {
rootDir: '/project/'
}
},
'src/index'
],
externals: [
// Type imports
'nuxt3',
'vue-meta',
'vue',
'hookable',
'webpack',
'pkg-types',
'webpack-bundle-analyzer',
'rollup-plugin-visualizer',
'vite',
// Implicit
'@vue/compiler-core',
'@vue/shared ',
]
})

View File

@ -0,0 +1,33 @@
{
"name": "@nuxt/schema",
"version": "3.0.0",
"repository": "nuxt/framework",
"license": "MIT",
"type": "module",
"main": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist",
"schema"
],
"scripts": {
"prepack": "unbuild"
},
"devDependencies": {
"@types/lodash.template": "^4",
"@types/semver": "^7",
"unbuild": "latest"
},
"dependencies": {
"create-require": "^1.1.1",
"defu": "^5.0.0",
"jiti": "^1.12.9",
"pathe": "^0.2.0",
"scule": "^0.2.1",
"std-env": "^3.0.1",
"ufo": "^0.7.9"
},
"engines": {
"node": "^14.16.0 || ^16.11.0 || ^17.0.0"
}
}

View File

@ -684,7 +684,7 @@ export default {
* }
* }
* ```
* @type {typeof import('../src/types/runtime-config').PrivateRuntimeConfig}
* @type {typeof import('../src/types/config').PrivateRuntimeConfig}
* @version 2
* @version 3
*/
@ -709,7 +709,7 @@ export default {
* }
* }
* ```
* @type {typeof import('../src/types/runtime-config').PublicRuntimeConfig}
* @type {typeof import('../src/types/config').PublicRuntimeConfig}
* @version 2
* @version 3
*/

View File

@ -0,0 +1,13 @@
// Types
import './types/global'
export * from './types/config'
export * from './types/hooks'
export * from './types/module'
export * from './types/nuxt'
export * from './types/components'
export * from './types/imports'
export * from './types/pages'
// Schema
export { default as NuxtConfigSchema } from './config/index'

View File

@ -5,3 +5,10 @@ export interface NuxtOptions extends ConfigSchema { }
type DeepPartial<T> = T extends Record<string, any> ? { [P in keyof T]?: DeepPartial<T[P]> | T[P] } : T
export interface NuxtConfig extends DeepPartial<ConfigSchema> { }
export interface PublicRuntimeConfig extends Record<string, any> { }
export interface PrivateRuntimeConfig extends PublicRuntimeConfig { }
type _RuntimeConfig = PublicRuntimeConfig & Partial<PrivateRuntimeConfig>
export interface RuntimeConfig extends _RuntimeConfig { }

View File

@ -1,9 +1,9 @@
import type { IncomingMessage, ServerResponse } from 'http'
import type { Compiler, Configuration, Stats } from 'webpack'
import type { TSConfig } from 'pkg-types'
import type { ModuleContainer } from '../module/container'
import type { NuxtTemplate, Nuxt, NuxtApp } from '../types/nuxt'
import type { AutoImport, AutoImportSource } from '../types/imports'
import type { ModuleContainer } from './module'
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'

View File

@ -1,6 +1,6 @@
import type { ModuleContainer } from '../module/container'
import { Nuxt } from './nuxt'
import { NuxtHooks } from './hooks'
import type { Nuxt, NuxtTemplate } from "./nuxt";
export interface NuxtCompatibilityConstraints {
/**
@ -71,3 +71,41 @@ export type ModuleInstallOptions =
ModuleSrc |
[ModuleSrc, ModuleOptions?] |
Partial<ModuleInstallOptionsObj>
/**
* Legacy ModuleContainer for backwards compatibility with Nuxt 2 module format.
*/
export interface ModuleContainer {
nuxt: Nuxt
options: Nuxt['options']
/** @deprecated */
ready(): Promise<any>
/** @deprecated */
addVendor(): void
/** Renders given template using lodash template during build into the project buildDir (`.nuxt`).*/
addTemplate(template: string | NuxtTemplate): NuxtTemplate
/** Register a custom layout. If its name is 'error' it will override the default error layout. */
addLayout(tmpl: NuxtTemplate, name: string): any
/** Set the layout that will render Nuxt errors. It should already have been added via addLayout or addTemplate. */
addErrorLayout(dst: string): void
/** Adds a new server middleware to the end of the server middleware array. */
addServerMiddleware(arg1: any): void
/** Allows extending webpack build config by chaining `options.build.extend` function. */
extendBuild(fn): void
/** Allows extending routes by chaining `options.build.extendRoutes` function. */
extendRoutes(fn): void
/** Registers a module */
requireModule(nuxt: Nuxt, opts: any): Promise<void>
/** Registers a module */
addModule(nuxt: Nuxt, opts: any): Promise<void>
}

View File

@ -0,0 +1,2 @@
// TODO: Lost!
export interface NuxtRoute { }

View File

@ -7,7 +7,9 @@ export default defineBuildConfig({
'src/index'
],
dependencies: [
'@nuxt/kit',
'vue'
],
externals: [
'@nuxt/schema'
]
})

View File

@ -13,6 +13,7 @@
"prepack": "unbuild"
},
"devDependencies": {
"@nuxt/schema": "3.0.0",
"unbuild": "latest",
"vue": "3.2.22"
},

View File

@ -1,6 +1,7 @@
import createResolver from 'postcss-import-resolver'
import defu from 'defu'
import { Nuxt, requireModule } from '@nuxt/kit'
import { requireModule } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
import { ViteOptions } from './vite'
import { distDir } from './dirs'

View File

@ -1,7 +1,7 @@
import * as vite from 'vite'
import { resolve } from 'pathe'
import consola from 'consola'
import type { Nuxt } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
import type { InlineConfig, SSROptions } from 'vite'
import type { Options } from '@vitejs/plugin-vue'
import { sanitizeFilePath } from 'mlly'

View File

@ -21,5 +21,8 @@ export default defineBuildConfig({
'vue-style-loader',
'@babel/core',
'vue'
],
externals: [
'@nuxt/schema'
]
})

View File

@ -56,6 +56,7 @@
"webpackbar": "^5.0.2"
},
"devDependencies": {
"@nuxt/schema": "3.0.0",
"@types/pify": "^5.0.1",
"@types/terser-webpack-plugin": "^5.0.4",
"@types/webpack-bundle-analyzer": "^4.4.1",

View File

@ -1,7 +1,7 @@
import consola from 'consola'
import { cloneDeep } from 'lodash-es'
import type { Configuration } from 'webpack'
import type { Nuxt } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
export interface WebpackConfigContext extends ReturnType<typeof createWebpackConfigContext>{ }

View File

@ -4,7 +4,8 @@ import consola from 'consola'
import { createCommonJS } from 'mlly'
import { defaults, merge, cloneDeep } from 'lodash-es'
import createResolver from 'postcss-import-resolver'
import { Nuxt, requireModule } from '@nuxt/kit'
import { requireModule } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
const isPureObject = obj => obj !== null && !Array.isArray(obj) && typeof obj === 'object'

View File

@ -12,7 +12,7 @@ import type { Compiler, Watching } from 'webpack'
import type { Context as WebpackDevMiddlewareContext, Options as WebpackDevMiddlewareOptions } from 'webpack-dev-middleware'
import type { MiddlewareOptions as WebpackHotMiddlewareOptions } from 'webpack-hot-middleware'
import type { Nuxt } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
import { createMFS } from './utils/mfs'
import { client, server } from './configs'
import { createWebpackConfigContext, applyPresets, getWebpackConfig } from './utils/config'

View File

@ -2425,6 +2425,7 @@ __metadata:
"@nuxt/kit": 3.0.0
"@nuxt/nitro": 3.0.0
"@nuxt/postcss8": ^1.1.3
"@nuxt/schema": 3.0.0
"@nuxt/types": ^2.15.8
"@types/fs-extra": ^9.0.13
"@types/hash-sum": ^1.0.0
@ -2743,15 +2744,14 @@ __metadata:
version: 0.0.0-use.local
resolution: "@nuxt/kit@workspace:packages/kit"
dependencies:
"@nuxt/schema": ^3.0.0
"@types/lodash.template": ^4
"@types/semver": ^7
consola: ^2.15.3
create-require: ^1.1.1
defu: ^5.0.0
dotenv: ^10.0.0
globby: ^11.0.4
hash-sum: ^2.0.0
hookable: ^5.0.0
jiti: ^1.12.9
lodash.template: ^4.5.0
mlly: ^0.3.13
@ -2760,8 +2760,6 @@ __metadata:
rc9: ^1.2.0
scule: ^0.2.1
semver: ^7.3.5
std-env: ^3.0.1
ufo: ^0.7.9
unbuild: latest
unctx: ^1.0.2
untyped: ^0.3.0
@ -2790,6 +2788,7 @@ __metadata:
"@nuxt/design": 0.1.5
"@nuxt/devalue": ^2.0.0
"@nuxt/kit": 3.0.0
"@nuxt/schema": 3.0.0
"@rollup/plugin-alias": ^3.1.8
"@rollup/plugin-commonjs": ^21.0.1
"@rollup/plugin-inject": ^4.0.3
@ -2880,6 +2879,23 @@ __metadata:
languageName: node
linkType: hard
"@nuxt/schema@3.0.0, @nuxt/schema@^3.0.0, @nuxt/schema@workspace:packages/schema":
version: 0.0.0-use.local
resolution: "@nuxt/schema@workspace:packages/schema"
dependencies:
"@types/lodash.template": ^4
"@types/semver": ^7
create-require: ^1.1.1
defu: ^5.0.0
jiti: ^1.12.9
pathe: ^0.2.0
scule: ^0.2.1
std-env: ^3.0.1
ufo: ^0.7.9
unbuild: latest
languageName: unknown
linkType: soft
"@nuxt/server-edge@npm:2.16.0-27282256.ab1c6cb4":
version: 2.16.0-27282256.ab1c6cb4
resolution: "@nuxt/server-edge@npm:2.16.0-27282256.ab1c6cb4"
@ -3030,6 +3046,7 @@ __metadata:
resolution: "@nuxt/vite-builder@workspace:packages/vite"
dependencies:
"@nuxt/kit": 3.0.0
"@nuxt/schema": 3.0.0
"@vitejs/plugin-vue": ^1.9.4
"@vitejs/plugin-vue-jsx": ^1.2.0
autoprefixer: ^10.4.0
@ -3137,6 +3154,7 @@ __metadata:
"@babel/core": ^7.16.0
"@nuxt/friendly-errors-webpack-plugin": ^2.5.2
"@nuxt/kit": 3.0.0
"@nuxt/schema": 3.0.0
"@types/pify": ^5.0.1
"@types/terser-webpack-plugin": ^5.0.4
"@types/webpack-bundle-analyzer": ^4.4.1
@ -15142,6 +15160,7 @@ __metadata:
dependencies:
"@nuxt/design": 0.1.5
"@nuxt/kit": 3.0.0
"@nuxt/schema": 3.0.0
"@types/clear": ^0
"@types/mri": ^1.1.1
"@types/rimraf": ^3
@ -15247,6 +15266,7 @@ __metadata:
"@nuxt/design": ^0.1.5
"@nuxt/kit": 3.0.0
"@nuxt/nitro": 3.0.0
"@nuxt/schema": 3.0.0
"@nuxt/vite-builder": 3.0.0
"@nuxt/webpack-builder": 3.0.0
"@types/hash-sum": ^1.0.0