refactor: enable strict type checking everywhere (#6943)

This commit is contained in:
Anthony Fu 2022-08-26 23:47:29 +08:00 committed by GitHub
parent 56c7b6168c
commit 9db2229f70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 365 additions and 273 deletions

View File

@ -20,7 +20,7 @@
"pathe": "^0.3.5", "pathe": "^0.3.5",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"scule": "^0.3.2", "scule": "^0.3.2",
"untyped": "^0.4.5", "untyped": "^0.4.7",
"vue-mq": "^1.0.1", "vue-mq": "^1.0.1",
"vue-plausible": "^1.3.2" "vue-plausible": "^1.3.2"
}, },

View File

@ -30,7 +30,7 @@
"semver": "^7.3.7", "semver": "^7.3.7",
"unctx": "^2.0.1", "unctx": "^2.0.1",
"unimport": "^0.6.7", "unimport": "^0.6.7",
"untyped": "^0.4.5" "untyped": "^0.4.7"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash.template": "^4", "@types/lodash.template": "^4",

View File

@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"strict": true,
"noImplicitAny": true
},
"include": [
"./src/**/*.ts",
"./test/**/*.ts"
]
}

View File

@ -21,6 +21,7 @@
"@nuxt/kit": "3.0.0-rc.8", "@nuxt/kit": "3.0.0-rc.8",
"@nuxt/schema": "3.0.0-rc.8", "@nuxt/schema": "3.0.0-rc.8",
"@types/clear": "^0", "@types/clear": "^0",
"@types/flat": "^5.0.2",
"@types/mri": "^1.1.1", "@types/mri": "^1.1.1",
"@types/semver": "^7", "@types/semver": "^7",
"c12": "^0.2.9", "c12": "^0.2.9",

View File

@ -1,4 +1,5 @@
import type { AddressInfo } from 'node:net' import type { AddressInfo } from 'node:net'
import { RequestListener } from 'node:http'
import { resolve, relative, normalize } from 'pathe' import { resolve, relative, normalize } from 'pathe'
import chokidar from 'chokidar' import chokidar from 'chokidar'
import { debounce } from 'perfect-debounce' import { debounce } from 'perfect-debounce'
@ -24,15 +25,15 @@ export default defineNuxtCommand({
overrideEnv('development') overrideEnv('development')
const { listen } = await import('listhen') const { listen } = await import('listhen')
let currentHandler let currentHandler: RequestListener | undefined
let loadingMessage = 'Nuxt is starting...' let loadingMessage = 'Nuxt is starting...'
const loadingHandler = async (_req, res) => { const loadingHandler: RequestListener = async (_req, res) => {
const { loading: loadingTemplate } = await importModule('@nuxt/ui-templates') const { loading: loadingTemplate } = await importModule('@nuxt/ui-templates')
res.setHeader('Content-Type', 'text/html; charset=UTF-8') res.setHeader('Content-Type', 'text/html; charset=UTF-8')
res.statusCode = 503 // Service Unavailable res.statusCode = 503 // Service Unavailable
res.end(loadingTemplate({ loading: loadingMessage })) res.end(loadingTemplate({ loading: loadingMessage }))
} }
const serverHandler = (req, res) => { const serverHandler: RequestListener = (req, res) => {
return currentHandler ? currentHandler(req, res) : loadingHandler(req, res) return currentHandler ? currentHandler(req, res) : loadingHandler(req, res)
} }
@ -64,7 +65,7 @@ export default defineNuxtCommand({
const load = async (isRestart: boolean, reason?: string) => { const load = async (isRestart: boolean, reason?: string) => {
try { try {
loadingMessage = `${reason ? reason + '. ' : ''}${isRestart ? 'Restarting' : 'Starting'} nuxt...` loadingMessage = `${reason ? reason + '. ' : ''}${isRestart ? 'Restarting' : 'Starting'} nuxt...`
currentHandler = null currentHandler = undefined
if (isRestart) { if (isRestart) {
consola.info(loadingMessage) consola.info(loadingMessage)
} }
@ -103,7 +104,7 @@ export default defineNuxtCommand({
} }
} catch (err) { } catch (err) {
consola.error(`Cannot ${isRestart ? 'restart' : 'start'} nuxt: `, err) consola.error(`Cannot ${isRestart ? 'restart' : 'start'} nuxt: `, err)
currentHandler = null currentHandler = undefined
loadingMessage = 'Error while loading nuxt. Please check console and fix errors.' loadingMessage = 'Error while loading nuxt. Please check console and fix errors.'
} }
} }

View File

@ -1,6 +1,6 @@
import type { Argv } from 'mri' import type { Argv } from 'mri'
const _rDefault = r => r.default || r const _rDefault = (r: any) => r.default || r
export const commands = { export const commands = {
dev: () => import('./dev').then(_rDefault), dev: () => import('./dev').then(_rDefault),

View File

@ -6,6 +6,7 @@ import jiti from 'jiti'
import destr from 'destr' import destr from 'destr'
import { splitByCase } from 'scule' import { splitByCase } from 'scule'
import clipboardy from 'clipboardy' import clipboardy from 'clipboardy'
import { NuxtModule } from '@nuxt/schema'
import { getPackageManager, getPackageManagerVersion } from '../utils/packageManagers' import { getPackageManager, getPackageManagerVersion } from '../utils/packageManagers'
import { findup } from '../utils/fs' import { findup } from '../utils/fs'
import { defineNuxtCommand } from './index' import { defineNuxtCommand } from './index'
@ -27,13 +28,13 @@ export default defineNuxtCommand({
const { dependencies = {}, devDependencies = {} } = findPackage(rootDir) const { dependencies = {}, devDependencies = {} } = findPackage(rootDir)
// Utils to query a dependency version // Utils to query a dependency version
const getDepVersion = name => getPkg(name, rootDir)?.version || dependencies[name] || devDependencies[name] const getDepVersion = (name: string) => getPkg(name, rootDir)?.version || dependencies[name] || devDependencies[name]
const listModules = (arr = []) => arr const listModules = (arr = []) => arr
.map(normalizeConfigModule) .map(m => normalizeConfigModule(m, rootDir))
.filter(Boolean) .filter(Boolean)
.map((name) => { .map((name) => {
const npmName = name.split('/').splice(0, 2).join('/') // @foo/bar/baz => @foo/bar const npmName = name!.split('/').splice(0, 2).join('/') // @foo/bar/baz => @foo/bar
const v = getDepVersion(npmName) const v = getDepVersion(npmName)
return '`' + (v ? `${name}@${v}` : name) + '`' return '`' + (v ? `${name}@${v}` : name) + '`'
}) })
@ -54,6 +55,7 @@ export default defineNuxtCommand({
if (packageManager) { if (packageManager) {
packageManager += '@' + getPackageManagerVersion(packageManager) packageManager += '@' + getPackageManagerVersion(packageManager)
} else { } else {
// @ts-expect-error
packageManager = 'unknown' packageManager = 'unknown'
} }
@ -95,14 +97,14 @@ export default defineNuxtCommand({
} }
}) })
function normalizeConfigModule (module, rootDir) { function normalizeConfigModule (module: NuxtModule | string | null | undefined, rootDir: string): string | null {
if (!module) { if (!module) {
return null return null
} }
if (typeof module === 'string') { if (typeof module === 'string') {
return module return module
.split(rootDir).pop() // Strip rootDir .split(rootDir).pop()! // Strip rootDir
.split('node_modules').pop() // Strip node_modules .split('node_modules').pop()! // Strip node_modules
.replace(/^\//, '') .replace(/^\//, '')
} }
if (typeof module === 'function') { if (typeof module === 'function') {
@ -111,9 +113,10 @@ function normalizeConfigModule (module, rootDir) {
if (Array.isArray(module)) { if (Array.isArray(module)) {
return normalizeConfigModule(module[0], rootDir) return normalizeConfigModule(module[0], rootDir)
} }
return null
} }
function getNuxtConfig (rootDir) { function getNuxtConfig (rootDir: string) {
try { try {
return jiti(rootDir, { interopDefault: true, esmResolve: true })('./nuxt.config') return jiti(rootDir, { interopDefault: true, esmResolve: true })('./nuxt.config')
} catch (err) { } catch (err) {
@ -122,7 +125,7 @@ function getNuxtConfig (rootDir) {
} }
} }
function getPkg (name, rootDir) { function getPkg (name: string, rootDir: string) {
// Assume it is in {rootDir}/node_modules/${name}/package.json // Assume it is in {rootDir}/node_modules/${name}/package.json
let pkgPath = resolve(rootDir, 'node_modules', name, 'package.json') let pkgPath = resolve(rootDir, 'node_modules', name, 'package.json')
@ -135,7 +138,7 @@ function getPkg (name, rootDir) {
return readJSONSync(pkgPath) return readJSONSync(pkgPath)
} }
function findPackage (rootDir) { function findPackage (rootDir: string) {
return findup(rootDir, (dir) => { return findup(rootDir, (dir) => {
const p = resolve(dir, 'package.json') const p = resolve(dir, 'package.json')
if (existsSync(p)) { if (existsSync(p)) {
@ -144,7 +147,7 @@ function findPackage (rootDir) {
}) || {} }) || {}
} }
function readJSONSync (filePath) { function readJSONSync (filePath: string) {
try { try {
return destr(readFileSync(filePath, 'utf-8')) return destr(readFileSync(filePath, 'utf-8'))
} catch (err) { } catch (err) {

View File

@ -1,13 +1,14 @@
import { existsSync, readdirSync } from 'node:fs' import { existsSync, readdirSync } from 'node:fs'
// @ts-expect-error missing types
import createTiged from 'tiged' import createTiged from 'tiged'
import { relative, resolve } from 'pathe' import { relative, resolve } from 'pathe'
import superb from 'superb' import superb from 'superb'
import consola from 'consola' import consola from 'consola'
import { defineNuxtCommand } from './index' import { defineNuxtCommand } from './index'
const rpath = p => relative(process.cwd(), p) const rpath = (p: string) => relative(process.cwd(), p)
const resolveTemplate = (template) => { const resolveTemplate = (template: string | boolean) => {
if (typeof template === 'boolean') { if (typeof template === 'boolean') {
consola.error('Please specify a template!') consola.error('Please specify a template!')
process.exit(1) process.exit(1)
@ -39,12 +40,12 @@ export default defineNuxtCommand({
consola.error(`Directory ${dstDir} is not empty. Please pick another name or remove it first. Aborting.`) consola.error(`Directory ${dstDir} is not empty. Please pick another name or remove it first. Aborting.`)
process.exit(1) process.exit(1)
} }
const formatArgs = msg => msg.replace('options.', '--') const formatArgs = (msg: string) => msg.replace('options.', '--')
tiged.on('warn', event => consola.warn(formatArgs(event.message))) tiged.on('warn', (event: any) => consola.warn(formatArgs(event.message)))
tiged.on('info', event => consola.info(formatArgs(event.message))) tiged.on('info', (event: any) => consola.info(formatArgs(event.message)))
try { try {
await tiged.clone(dstDir) await tiged.clone(dstDir)
} catch (e) { } catch (e: any) {
if (e.toString().includes('could not find commit hash')) { if (e.toString().includes('could not find commit hash')) {
consola.error(`Failed to clone template from \`${src}\`. Please check the repo is valid and that you have installed \`git\` correctly.`) consola.error(`Failed to clone template from \`${src}\`. Please check the repo is valid and that you have installed \`git\` correctly.`)
process.exit(1) process.exit(1)

View File

@ -3,14 +3,14 @@ import { pathToFileURL } from 'node:url'
import { normalize, dirname } from 'pathe' import { normalize, dirname } from 'pathe'
export function getModulePaths (paths?: string | string[]): string[] { export function getModulePaths (paths?: string | string[]): string[] {
return [].concat( return [
// @ts-ignore // @ts-ignore
global.__NUXT_PREPATHS__, global.__NUXT_PREPATHS__,
...(Array.isArray(paths) ? paths : [paths]), ...(paths ? [] : Array.isArray(paths) ? paths : [paths]),
process.cwd(), process.cwd(),
// @ts-ignore // @ts-ignore
global.__NUXT_PATHS__ global.__NUXT_PATHS__
).filter(Boolean) ].filter(Boolean)
} }
const _require = createRequire(process.cwd()) const _require = createRequire(process.cwd())

View File

@ -2,19 +2,19 @@ import flatten from 'flat'
import { detailedDiff } from 'deep-object-diff' import { detailedDiff } from 'deep-object-diff'
import { green, red, blue, cyan } from 'colorette' import { green, red, blue, cyan } from 'colorette'
function normalizeDiff (diffObj, type, ignore) { function normalizeDiff (diffObj: any, type: 'added' | 'deleted' | 'updated', ignore: string[]) {
return Object.entries(flatten(diffObj)) return Object.entries(flatten(diffObj) as Record<string, any>)
.map(([key, value]) => ({ key, value, type })) .map(([key, value]) => ({ key, value, type }))
.filter(item => !ignore.includes(item.key) && typeof item.value !== 'function') .filter(item => !ignore.includes(item.key) && typeof item.value !== 'function')
} }
export function diff (a, b, ignore) { export function diff (a: any, b: any, ignore: string[]) {
const _diff: any = detailedDiff(a, b) const _diff: any = detailedDiff(a, b)
return [].concat( return [
normalizeDiff(_diff.added, 'added', ignore), ...normalizeDiff(_diff.added, 'added', ignore),
normalizeDiff(_diff.deleted, 'deleted', ignore), ...normalizeDiff(_diff.deleted, 'deleted', ignore),
normalizeDiff(_diff.updated, 'updated', ignore) ...normalizeDiff(_diff.updated, 'updated', ignore)
) ]
} }
const typeMap = { const typeMap = {
@ -23,9 +23,9 @@ const typeMap = {
updated: blue('updated') updated: blue('updated')
} }
export function printDiff (diff) { export function printDiff (diff: any) {
for (const item of diff) { for (const item of diff) {
console.log(' ', typeMap[item.type] || item.type, cyan(item.key), item.value ? `~> ${cyan(item.value)}` : '') console.log(' ', typeMap[item.type as keyof typeof typeMap] || item.type, cyan(item.key), item.value ? `~> ${cyan(item.value)}` : '')
} }
console.log() console.log()
} }

View File

@ -1,13 +1,17 @@
import { cyan, magenta } from 'colorette' import { cyan, magenta } from 'colorette'
export function showHelp (meta?) { import { NuxtCommandMeta } from '../commands'
export function showHelp (meta?: Partial<NuxtCommandMeta>) {
const sections: string[] = [] const sections: string[] = []
if (meta.usage) { if (meta) {
sections.push(magenta('> ') + 'Usage: ' + cyan(meta.usage)) if (meta.usage) {
} sections.push(magenta('> ') + 'Usage: ' + cyan(meta.usage))
}
if (meta.description) { if (meta.description) {
sections.push(magenta('⋮ ') + meta.description) sections.push(magenta('⋮ ') + meta.description)
}
} }
sections.push(`Use ${cyan('npx nuxi [command] --help')} to see help for each command`) sections.push(`Use ${cyan('npx nuxi [command] --help')} to see help for each command`)

View File

@ -3,7 +3,7 @@ import { importModule } from './cjs'
export const loadKit = async (rootDir: string): Promise<typeof import('@nuxt/kit')> => { export const loadKit = async (rootDir: string): Promise<typeof import('@nuxt/kit')> => {
try { try {
return await importModule('@nuxt/kit', rootDir) as typeof import('@nuxt/kit') return await importModule('@nuxt/kit', rootDir) as typeof import('@nuxt/kit')
} catch (e) { } catch (e: any) {
if (e.toString().includes("Cannot find module '@nuxt/kit'")) { if (e.toString().includes("Cannot find module '@nuxt/kit'")) {
throw new Error('nuxi requires `@nuxt/kit` to be installed in your project. Try installing `nuxt3` or `@nuxt/bridge` first.') throw new Error('nuxi requires `@nuxt/kit` to be installed in your project. Try installing `nuxt3` or `@nuxt/bridge` first.')
} }

View File

@ -6,7 +6,7 @@ import type { Nuxt } from '@nuxt/schema'
import { rmRecursive } from './fs' import { rmRecursive } from './fs'
export interface NuxtProjectManifest { export interface NuxtProjectManifest {
_hash: string _hash: string | null
project: { project: {
rootDir: string rootDir: string
}, },

View File

@ -9,14 +9,17 @@ export const packageManagerLocks = {
pnpm: 'pnpm-lock.yaml' pnpm: 'pnpm-lock.yaml'
} }
type PackageManager = keyof typeof packageManagerLocks
export function getPackageManager (rootDir: string) { export function getPackageManager (rootDir: string) {
return findup(rootDir, (dir) => { return findup(rootDir, (dir) => {
for (const name in packageManagerLocks) { for (const name in packageManagerLocks) {
if (existsSync(resolve(dir, packageManagerLocks[name]))) { const path = packageManagerLocks[name as PackageManager]
if (path && existsSync(resolve(dir, path))) {
return name return name
} }
} }
}) }) as PackageManager | null
} }
export function getPackageManagerVersion (name: string) { export function getPackageManagerVersion (name: string) {

View File

@ -31,7 +31,7 @@ export const writeTypes = async (nuxt: Nuxt) => {
] ]
}) })
const aliases = { const aliases: Record<string, string> = {
...nuxt.options.alias, ...nuxt.options.alias,
'#build': nuxt.options.buildDir '#build': nuxt.options.buildDir
} }
@ -48,6 +48,7 @@ export const writeTypes = async (nuxt: Nuxt) => {
: aliases[alias] : aliases[alias]
const stats = await fsp.stat(resolve(nuxt.options.rootDir, relativePath)).catch(() => null /* file does not exist */) const stats = await fsp.stat(resolve(nuxt.options.rootDir, relativePath)).catch(() => null /* file does not exist */)
tsConfig.compilerOptions = tsConfig.compilerOptions || {}
if (stats?.isDirectory()) { if (stats?.isDirectory()) {
tsConfig.compilerOptions.paths[alias] = [relativePath] tsConfig.compilerOptions.paths[alias] = [relativePath]
tsConfig.compilerOptions.paths[`${alias}/*`] = [`${relativePath}/*`] tsConfig.compilerOptions.paths[`${alias}/*`] = [`${relativePath}/*`]

View File

@ -65,7 +65,7 @@
"unenv": "^0.6.1", "unenv": "^0.6.1",
"unimport": "^0.6.7", "unimport": "^0.6.7",
"unplugin": "^0.9.2", "unplugin": "^0.9.2",
"untyped": "^0.4.5", "untyped": "^0.4.7",
"vue": "^3.2.37", "vue": "^3.2.37",
"vue-bundle-renderer": "^0.4.2", "vue-bundle-renderer": "^0.4.2",
"vue-devtools-stub": "^0.1.0", "vue-devtools-stub": "^0.1.0",

View File

@ -144,7 +144,7 @@ export function useAsyncData<
result = options.transform(result) result = options.transform(result)
} }
if (options.pick) { if (options.pick) {
result = pick(result, options.pick) as DataT result = pick(result as any, options.pick) as DataT
} }
asyncData.data.value = result asyncData.data.value = result
asyncData.error.value = null asyncData.error.value = null

View File

@ -231,7 +231,7 @@ export function normalizePlugins (_plugins: Plugin[]) {
return plugins as Plugin[] return plugins as Plugin[]
} }
export function defineNuxtPlugin<T> (plugin: Plugin<T>) { export function defineNuxtPlugin<T extends Record<string, any>> (plugin: Plugin<T>) {
plugin[NuxtPluginIndicator] = true plugin[NuxtPluginIndicator] = true
return plugin return plugin
} }

View File

@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"strict": true,
"noImplicitAny": true
},
"include": [
"./src/**/*.ts",
"./test/**/*.ts"
]
}

View File

@ -41,6 +41,7 @@ export default defineBuildConfig({
'ignore', 'ignore',
// Implicit // Implicit
'@vue/compiler-core', '@vue/compiler-core',
'@vue/shared' '@vue/shared',
'untyped'
] ]
}) })

View File

@ -17,6 +17,7 @@
"@types/lodash.template": "^4", "@types/lodash.template": "^4",
"@types/semver": "^7", "@types/semver": "^7",
"unbuild": "latest", "unbuild": "latest",
"untyped": "^0.4.7",
"vite": "~3.0.9" "vite": "~3.0.9"
}, },
"dependencies": { "dependencies": {

View File

@ -1,4 +1,6 @@
export default { import { defineUntypedSchema } from 'untyped'
export default defineUntypedSchema({
/** /**
* Configure Nuxt component auto-registration. * Configure Nuxt component auto-registration.
* *
@ -57,4 +59,4 @@ export default {
* @version 3 * @version 3
*/ */
telemetry: undefined telemetry: undefined
} })

View File

@ -1,8 +1,11 @@
import { resolve, join } from 'pathe' import { resolve, join } from 'pathe'
import { existsSync, readdirSync } from 'node:fs' import { existsSync, readdirSync } from 'node:fs'
import defu from 'defu' import defu from 'defu'
import { defineUntypedSchema } from 'untyped'
export default { import { MetaObject } from '../types/meta'
export default defineUntypedSchema({
/** /**
* Vue.js config * Vue.js config
* @version 2 * @version 2
@ -17,8 +20,12 @@ export default {
* @version 2 * @version 2
*/ */
config: { config: {
silent: { $resolve: (val, get) => val ?? !get('dev') }, silent: {
performance: { $resolve: (val, get) => val ?? get('dev') }, $resolve: (val, get) => val ?? !get('dev')
},
performance: {
$resolve: (val, get) => val ?? get('dev')
},
}, },
/** /**
* Options for the Vue compiler that will be passed at build time. * Options for the Vue compiler that will be passed at build time.
@ -105,7 +112,7 @@ export default {
*/ */
head: { head: {
$resolve: (val, get) => { $resolve: (val, get) => {
const resolved = defu(val, get('meta'), { const resolved: Required<MetaObject> = defu(val, get('meta'), {
meta: [], meta: [],
link: [], link: [],
style: [], style: [],
@ -306,7 +313,7 @@ export default {
* @version 3 * @version 3
*/ */
css: { css: {
$resolve: val => (val ?? []).map(c => c.src || c) $resolve: val => (val ?? []).map((c: any) => c.src || c)
}, },
/** /**
@ -460,4 +467,4 @@ export default {
/** Set to false to disable the `<ClientOnly>` component (see [docs](https://github.com/egoist/vue-client-only)) */ /** Set to false to disable the `<ClientOnly>` component (see [docs](https://github.com/egoist/vue-client-only)) */
componentClientOnly: true componentClientOnly: true
} }
} })

View File

@ -4,9 +4,12 @@ import createRequire from 'create-require'
import { pascalCase } from 'scule' import { pascalCase } from 'scule'
import jiti from 'jiti' import jiti from 'jiti'
import defu from 'defu' import defu from 'defu'
import { RuntimeConfig } from '../types/config' import { RuntimeConfig } from '../types/config'
export default { import { defineUntypedSchema } from 'untyped'
export default defineUntypedSchema({
/** /**
* Extend nested configurations from multiple local or remote sources. * Extend nested configurations from multiple local or remote sources.
* *
@ -152,12 +155,13 @@ export default {
createRequire: { createRequire: {
$resolve: (val: any) => { $resolve: (val: any) => {
val = process.env.NUXT_CREATE_REQUIRE || val || val = process.env.NUXT_CREATE_REQUIRE || val ||
// @ts-expect-error global type
(typeof globalThis.jest !== 'undefined' ? 'native' : 'jiti') (typeof globalThis.jest !== 'undefined' ? 'native' : 'jiti')
if (val === 'jiti') { if (val === 'jiti') {
return p => jiti(typeof p === 'string' ? p : p.filename, { esmResolve: true }) return (p: string | { filename: string }) => jiti(typeof p === 'string' ? p : p.filename, { esmResolve: true })
} }
if (val === 'native') { if (val === 'native') {
return p => createRequire(typeof p === 'string' ? p : p.filename) return (p: string | { filename: string }) => createRequire(typeof p === 'string' ? p : p.filename)
} }
return val return val
} }
@ -312,17 +316,17 @@ export default {
*/ */
globals: { globals: {
/** @type {(globalName: string) => string} */ /** @type {(globalName: string) => string} */
id: globalName => `__${globalName}`, id: (globalName: string) => `__${globalName}`,
/** @type {(globalName: string) => string} */ /** @type {(globalName: string) => string} */
nuxt: globalName => `$${globalName}`, nuxt: (globalName: string) => `$${globalName}`,
/** @type {(globalName: string) => string} */ /** @type {(globalName: string) => string} */
context: globalName => `__${globalName.toUpperCase()}__`, context: (globalName: string) => `__${globalName.toUpperCase()}__`,
/** @type {(globalName: string) => string} */ /** @type {(globalName: string) => string} */
pluginPrefix: globalName => globalName, pluginPrefix: (globalName: string) => globalName,
/** @type {(globalName: string) => string} */ /** @type {(globalName: string) => string} */
readyCallback: globalName => `on${pascalCase(globalName)}Ready`, readyCallback: (globalName: string) => `on${pascalCase(globalName)}Ready`,
/** @type {(globalName: string) => string} */ /** @type {(globalName: string) => string} */
loadedCallback: globalName => `_on${pascalCase(globalName)}Loaded` loadedCallback: (globalName: string) => `_on${pascalCase(globalName)}Loaded`
}, },
/** /**
@ -427,10 +431,10 @@ export default {
*/ */
modulesDir: { modulesDir: {
$default: ['node_modules'], $default: ['node_modules'],
$resolve: (val, get) => [].concat( $resolve: (val, get) => [
val.map(dir => resolve(get('rootDir'), dir)), ...val.map((dir: string) => resolve(get('rootDir'), dir)),
resolve(process.cwd(), 'node_modules') resolve(process.cwd(), 'node_modules')
) ]
}, },
/** /**
@ -759,4 +763,4 @@ export default {
* @version 3 * @version 3
*/ */
appConfig: {}, appConfig: {},
} })

View File

@ -1,4 +1,6 @@
export default { import { defineUntypedSchema } from 'untyped'
export default defineUntypedSchema({
/** @private */ /** @private */
_majorVersion: 2, _majorVersion: 2,
/** @private */ /** @private */
@ -21,4 +23,4 @@ export default {
_nuxtConfigFiles: [], _nuxtConfigFiles: [],
/** @private */ /** @private */
appDir: '' appDir: ''
} })

View File

@ -2,8 +2,9 @@ import defu from 'defu'
import { join } from 'pathe' import { join } from 'pathe'
import { isCI, isTest } from 'std-env' import { isCI, isTest } from 'std-env'
import { normalizeURL, withTrailingSlash } from 'ufo' import { normalizeURL, withTrailingSlash } from 'ufo'
import { defineUntypedSchema } from 'untyped'
export default { export default defineUntypedSchema({
/** /**
* The builder to use for bundling the Vue part of your application. * The builder to use for bundling the Vue part of your application.
* *
@ -15,7 +16,7 @@ export default {
if (typeof val === 'object') { if (typeof val === 'object') {
return val return val
} }
const map = { const map: Record<string, string> = {
vite: '@nuxt/vite-builder', vite: '@nuxt/vite-builder',
webpack: '@nuxt/webpack-builder', webpack: '@nuxt/webpack-builder',
} }
@ -225,15 +226,16 @@ export default {
* chunk: ({ isDev }) => (isDev ? '[name].js' : '[id].[contenthash].js') * chunk: ({ isDev }) => (isDev ? '[name].js' : '[id].[contenthash].js')
* } * }
* ``` * ```
* @type {Record<string, ((arg: any) => string)>}
* @version 2 * @version 2
*/ */
filenames: { filenames: {
app: ({ isDev, isModern }) => isDev ? `[name]${isModern ? '.modern' : ''}.js` : `[contenthash:7]${isModern ? '.modern' : ''}.js`, app: ({ isDev, isModern }: any) => isDev ? `[name]${isModern ? '.modern' : ''}.js` : `[contenthash:7]${isModern ? '.modern' : ''}.js`,
chunk: ({ isDev, isModern }) => isDev ? `[name]${isModern ? '.modern' : ''}.js` : `[contenthash:7]${isModern ? '.modern' : ''}.js`, chunk: ({ isDev, isModern }: any) => isDev ? `[name]${isModern ? '.modern' : ''}.js` : `[contenthash:7]${isModern ? '.modern' : ''}.js`,
css: ({ isDev }) => isDev ? '[name].css' : 'css/[contenthash:7].css', css: ({ isDev }: any) => isDev ? '[name].css' : 'css/[contenthash:7].css',
img: ({ isDev }) => isDev ? '[path][name].[ext]' : 'img/[name].[contenthash:7].[ext]', img: ({ isDev }: any) => isDev ? '[path][name].[ext]' : 'img/[name].[contenthash:7].[ext]',
font: ({ isDev }) => isDev ? '[path][name].[ext]' : 'fonts/[name].[contenthash:7].[ext]', font: ({ isDev }: any) => isDev ? '[path][name].[ext]' : 'fonts/[name].[contenthash:7].[ext]',
video: ({ isDev }) => isDev ? '[path][name].[ext]' : 'videos/[name].[contenthash:7].[ext]' video: ({ isDev }: any) => isDev ? '[path][name].[ext]' : 'videos/[name].[contenthash:7].[ext]'
}, },
/** /**
@ -360,7 +362,9 @@ export default {
optimization: { optimization: {
runtimeChunk: 'single', runtimeChunk: 'single',
/** Set minimize to false to disable all minimizers. (It is disabled in development by default) */ /** Set minimize to false to disable all minimizers. (It is disabled in development by default) */
minimize: { $resolve: (val, get) => val ?? !get('dev') }, minimize: {
$resolve: (val, get) => val ?? !get('dev')
},
/** You can set minimizer to a customized array of plugins. */ /** You can set minimizer to a customized array of plugins. */
minimizer: undefined, minimizer: undefined,
splitChunks: { splitChunks: {
@ -639,4 +643,4 @@ export default {
*/ */
followSymlinks: false followSymlinks: false
} }
} })

View File

@ -1,4 +1,6 @@
export default { import { defineUntypedSchema } from 'untyped'
export default defineUntypedSchema({
/** /**
* Add a message to the CLI banner by adding a string to this array. * Add a message to the CLI banner by adding a string to this array.
* @type {string[]} * @type {string[]}
@ -11,4 +13,4 @@ export default {
* @version 2 * @version 2
*/ */
bannerColor: 'green' bannerColor: 'green'
} })

View File

@ -1,11 +1,13 @@
export default { import { defineUntypedSchema } from 'untyped'
export default defineUntypedSchema({
/** @version 3 */ /** @version 3 */
experimental: { experimental: {
/** /**
* Set to true to generate an async entry point for the Vue bundle (for module federation support). * Set to true to generate an async entry point for the Vue bundle (for module federation support).
*/ */
asyncEntry: { asyncEntry: {
$resolve: (val, get) => val ?? false $resolve: (val) => val ?? false
}, },
/** /**
@ -51,4 +53,4 @@ export default {
*/ */
viteServerDynamicImports: true viteServerDynamicImports: true
} }
} })

View File

@ -1,10 +1,11 @@
import { resolve } from 'pathe' import { resolve } from 'pathe'
import { joinURL } from 'ufo' import { joinURL } from 'ufo'
import { SchemaDefinition } from 'untyped'
/** /**
* @version 2 * @version 2
*/ */
export default { export default <SchemaDefinition> {
/** /**
* Directory name that holds all the assets and generated pages for a `static` build. * Directory name that holds all the assets and generated pages for a `static` build.
*/ */
@ -160,10 +161,16 @@ export default {
* The full path to the directory underneath `/_nuxt/` where static assets * The full path to the directory underneath `/_nuxt/` where static assets
* (payload, state and manifest files) will live. * (payload, state and manifest files) will live.
*/ */
base: { $resolve: (val, get) => val || joinURL(get('app').buildAssetsDir, get('generate.dir')) }, base: {
/** The full path to the versioned directory where static assets for the current buidl are located. */ $resolve: (val, get) => val || joinURL(get('app').buildAssetsDir, get('generate.dir'))
versionBase: { $resolve: (val, get) => val || joinURL(get('generate.base'), get('generate.version')) }, },
/** The full path to the versioned directory where static assets for the current build are located. */
versionBase: {
$resolve: (val, get) => val || joinURL(get('generate.base'), get('generate.version'))
},
/** A unique string to uniquely identify payload versions (defaults to the current timestamp). */ /** A unique string to uniquely identify payload versions (defaults to the current timestamp). */
version: { $resolve: val => val || (String(Math.round(Date.now() / 1000))) } version: {
$resolve: val => val || (String(Math.round(Date.now() / 1000)))
}
} }
} }

View File

@ -1,7 +1,9 @@
import { SchemaDefinition } from 'untyped'
/** /**
* @version 2 * @version 2
*/ */
export default { export default <SchemaDefinition> {
/** The text that displays on the Nuxt loading indicator when `ssr: false`. */ /** The text that displays on the Nuxt loading indicator when `ssr: false`. */
loading: 'Loading...', loading: 'Loading...',
/** The 404 text on the default Nuxt error page. */ /** The 404 text on the default Nuxt error page. */

View File

@ -1,4 +1,6 @@
export default { import { defineUntypedSchema } from 'untyped'
export default defineUntypedSchema({
/** /**
* Configuration for Nitro. * Configuration for Nitro.
* *
@ -31,4 +33,4 @@ export default {
* @version 3 * @version 3
*/ */
devServerHandlers: [] devServerHandlers: []
} })

View File

@ -1,7 +1,8 @@
import defu from 'defu' import defu from 'defu'
import createResolver from 'postcss-import-resolver' import createResolver from 'postcss-import-resolver'
import { defineUntypedSchema } from 'untyped'
export default { export default defineUntypedSchema({
/** @version 3 */ /** @version 3 */
postcss: { postcss: {
/** Path to postcss config file. */ /** Path to postcss config file. */
@ -51,4 +52,4 @@ export default {
} }
} }
} }
} })

View File

@ -1,7 +1,9 @@
import { SchemaDefinition } from 'untyped'
/** /**
* @version 2 * @version 2
*/ */
export default { export default <SchemaDefinition>{
/** /**
* Use this option to customize the Vue SSR bundle renderer. * Use this option to customize the Vue SSR bundle renderer.
* This option is skipped if `ssr: false`. * This option is skipped if `ssr: false`.
@ -10,9 +12,11 @@ export default {
*/ */
bundleRenderer: { bundleRenderer: {
shouldPrefetch: () => false, shouldPrefetch: () => false,
shouldPreload: (_fileWithoutQuery, asType) => ['script', 'style'].includes(asType), shouldPreload: (_fileWithoutQuery: string, asType: string) => ['script', 'style'].includes(asType),
/** enabled by default for development */ /** enabled by default for development */
runInNewContext: { $resolve: (val, get) => val ?? get('dev') } runInNewContext: {
$resolve: (val, get) => val ?? get('dev')
}
}, },
/** /**
@ -41,7 +45,9 @@ export default {
* *
* Set to `collapsed` to collapse the logs, or `false` to disable. * Set to `collapsed` to collapse the logs, or `false` to disable.
*/ */
ssrLog: { $resolve: (val, get) => get('dev') ? Boolean(val) : false }, ssrLog: {
$resolve: (val, get) => get('dev') ? Boolean(val) : false
},
/** /**
* Configuration for HTTP2 push headers. * Configuration for HTTP2 push headers.

View File

@ -1,6 +1,7 @@
import { normalizeURL, withTrailingSlash } from 'ufo' import { normalizeURL, withTrailingSlash } from 'ufo'
import { defineUntypedSchema } from 'untyped'
export default { export default defineUntypedSchema({
/** /**
* Additional options passed to `vue-router`. * Additional options passed to `vue-router`.
* *
@ -168,4 +169,4 @@ export default {
* @version 2 * @version 2
*/ */
trailingSlash: undefined trailingSlash: undefined
} })

View File

@ -1,5 +1,7 @@
import { SchemaDefinition } from 'untyped'
/** @version 2 */ /** @version 2 */
export default { export default <SchemaDefinition>{
/** /**
* Whether to enable HTTPS. * Whether to enable HTTPS.
* *

View File

@ -1,4 +1,6 @@
export default { import { defineUntypedSchema } from 'untyped'
export default defineUntypedSchema({
/** /**
* Configuration for Nuxt's TypeScript integration. * Configuration for Nuxt's TypeScript integration.
* *
@ -35,4 +37,4 @@ export default {
*/ */
shim: true shim: true
} }
} })

View File

@ -1,7 +1,8 @@
import { resolve } from 'pathe' import { resolve } from 'pathe'
import { withoutLeadingSlash } from 'ufo' import { withoutLeadingSlash } from 'ufo'
import { defineUntypedSchema } from 'untyped'
export default { export default defineUntypedSchema({
/** /**
* Configuration that will be passed directly to Vite. * Configuration that will be passed directly to Vite.
* *
@ -45,7 +46,7 @@ export default {
exclude: { exclude: {
$resolve: (val, get) => [ $resolve: (val, get) => [
...val || [], ...val || [],
...get('build.transpile').filter((i) => typeof i === 'string'), ...get('build.transpile').filter((i: string) => typeof i === 'string'),
'vue-demi' 'vue-demi'
] ]
} }
@ -77,4 +78,4 @@ export default {
} }
} }
} }
} })

View File

@ -1,6 +1,7 @@
import { join } from 'pathe' import { join } from 'pathe'
import { defineUntypedSchema } from 'untyped'
export default { export default defineUntypedSchema({
/** @version 3 */ /** @version 3 */
webpack: { webpack: {
/** /**
@ -122,12 +123,12 @@ export default {
* ``` * ```
*/ */
filenames: { filenames: {
app: ({ isDev }) => isDev ? `[name].js` : `[contenthash:7].js`, app: ({ isDev }: { isDev: boolean }) => isDev ? `[name].js` : `[contenthash:7].js`,
chunk: ({ isDev }) => isDev ? `[name].js` : `[contenthash:7].js`, chunk: ({ isDev }: { isDev: boolean }) => isDev ? `[name].js` : `[contenthash:7].js`,
css: ({ isDev }) => isDev ? '[name].css' : 'css/[contenthash:7].css', css: ({ isDev }: { isDev: boolean }) => isDev ? '[name].css' : 'css/[contenthash:7].css',
img: ({ isDev }) => isDev ? '[path][name].[ext]' : 'img/[name].[contenthash:7].[ext]', img: ({ isDev }: { isDev: boolean }) => isDev ? '[path][name].[ext]' : 'img/[name].[contenthash:7].[ext]',
font: ({ isDev }) => isDev ? '[path][name].[ext]' : 'fonts/[name].[contenthash:7].[ext]', font: ({ isDev }: { isDev: boolean }) => isDev ? '[path][name].[ext]' : 'fonts/[name].[contenthash:7].[ext]',
video: ({ isDev }) => isDev ? '[path][name].[ext]' : 'videos/[name].[contenthash:7].[ext]' video: ({ isDev }: { isDev: boolean }) => isDev ? '[path][name].[ext]' : 'videos/[name].[contenthash:7].[ext]'
}, },
/** /**
@ -297,4 +298,4 @@ export default {
*/ */
warningIgnoreFilters: [], warningIgnoreFilters: [],
} }
} })

View File

@ -71,10 +71,10 @@ export interface ModuleContainer {
addServerMiddleware(arg1: any): void addServerMiddleware(arg1: any): void
/** Allows extending webpack build config by chaining `options.build.extend` function. */ /** Allows extending webpack build config by chaining `options.build.extend` function. */
extendBuild(fn): void extendBuild(fn: Function): void
/** Allows extending routes by chaining `options.router.extendRoutes` function. */ /** Allows extending routes by chaining `options.router.extendRoutes` function. */
extendRoutes(fn): void extendRoutes(fn: Function): void
/** Registers a module. */ /** Registers a module. */
requireModule(installOptions: any, opts: any): Promise<void> requireModule(installOptions: any, opts: any): Promise<void>

View File

@ -31,7 +31,7 @@ export async function getBrowser (): Promise<Browser> {
if (!ctx.browser) { if (!ctx.browser) {
await createBrowser() await createBrowser()
} }
return ctx.browser return ctx.browser!
} }
export async function createPage (path?: string, options?: BrowserContextOptions) { export async function createPage (path?: string, options?: BrowserContextOptions) {

View File

@ -2,7 +2,7 @@ import { resolve } from 'node:path'
import defu from 'defu' import defu from 'defu'
import type { TestContext, TestOptions, TestRunner } from './types' import type { TestContext, TestOptions, TestRunner } from './types'
let currentContext: TestContext let currentContext: TestContext | undefined
export function createTestContext (options: Partial<TestOptions>): TestContext { export function createTestContext (options: Partial<TestOptions>): TestContext {
const _options: Partial<TestOptions> = defu(options, { const _options: Partial<TestOptions> = defu(options, {
@ -18,7 +18,7 @@ export function createTestContext (options: Partial<TestOptions>): TestContext {
// TODO: auto detect based on process.env // TODO: auto detect based on process.env
runner: <TestRunner>'vitest', runner: <TestRunner>'vitest',
browserOptions: { browserOptions: {
type: 'chromium' type: 'chromium' as const
} }
}) })
@ -32,7 +32,9 @@ export function useTestContext (): TestContext {
return currentContext return currentContext
} }
export function setTestContext (context: TestContext): TestContext { export function setTestContext (context: TestContext): TestContext
export function setTestContext (context?: TestContext): TestContext | undefined
export function setTestContext (context?: TestContext): TestContext | undefined {
currentContext = context currentContext = context
return currentContext return currentContext
} }

View File

@ -64,5 +64,5 @@ export async function loadFixture () {
export async function buildFixture () { export async function buildFixture () {
const ctx = useTestContext() const ctx = useTestContext()
await kit.buildNuxt(ctx.nuxt) await kit.buildNuxt(ctx.nuxt!)
} }

View File

@ -21,6 +21,7 @@ export async function runTests (opts: RunTestOptions) {
process.env.NUXT_TEST_DEV = 'true' process.env.NUXT_TEST_DEV = 'true'
} }
// @ts-ignore missing types
const { startVitest } = await import('vitest/dist/node.mjs') const { startVitest } = await import('vitest/dist/node.mjs')
const succeeded = await startVitest( const succeeded = await startVitest(
[] /* argv */, [] /* argv */,

View File

@ -16,7 +16,7 @@ export async function startServer () {
if (ctx.options.dev) { if (ctx.options.dev) {
const nuxiCLI = await kit.resolvePath('nuxi/cli') const nuxiCLI = await kit.resolvePath('nuxi/cli')
ctx.serverProcess = execa(nuxiCLI, ['dev'], { ctx.serverProcess = execa(nuxiCLI, ['dev'], {
cwd: ctx.nuxt.options.rootDir, cwd: ctx.nuxt!.options.rootDir,
stdio: 'inherit', stdio: 'inherit',
env: { env: {
...process.env, ...process.env,
@ -37,7 +37,7 @@ export async function startServer () {
throw new Error('Timeout waiting for dev server!') throw new Error('Timeout waiting for dev server!')
} else { } else {
ctx.serverProcess = execa('node', [ ctx.serverProcess = execa('node', [
resolve(ctx.nuxt.options.nitro.output.dir, 'server/index.mjs') resolve(ctx.nuxt!.options.nitro.output!.dir!, 'server/index.mjs')
], { ], {
stdio: 'inherit', stdio: 'inherit',
env: { env: {

View File

@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"strict": true,
"noImplicitAny": true
},
"include": [
"./src/**/*.ts",
"./test/**/*.ts"
]
}

View File

@ -58,6 +58,7 @@
}, },
"devDependencies": { "devDependencies": {
"@nuxt/schema": "3.0.0-rc.8", "@nuxt/schema": "3.0.0-rc.8",
"@types/lodash-es": "^4.17.6",
"@types/pify": "^5.0.1", "@types/pify": "^5.0.1",
"@types/webpack-bundle-analyzer": "^4.4.2", "@types/webpack-bundle-analyzer": "^4.4.2",
"@types/webpack-dev-middleware": "^5.0.2", "@types/webpack-dev-middleware": "^5.0.2",

View File

@ -68,6 +68,7 @@ function clientHMR (ctx: WebpackConfigContext) {
`webpack-hot-middleware/client?${hotMiddlewareClientOptionsStr}` `webpack-hot-middleware/client?${hotMiddlewareClientOptionsStr}`
) )
config.plugins = config.plugins || []
config.plugins.push(new webpack.HotModuleReplacementPlugin()) config.plugins.push(new webpack.HotModuleReplacementPlugin())
} }

View File

@ -26,7 +26,7 @@ export function server (ctx: WebpackConfigContext) {
function serverPreset (ctx: WebpackConfigContext) { function serverPreset (ctx: WebpackConfigContext) {
const { config } = ctx const { config } = ctx
config.output.filename = 'server.mjs' config.output!.filename = 'server.mjs'
config.devtool = 'cheap-module-source-map' config.devtool = 'cheap-module-source-map'
config.optimization = { config.optimization = {
@ -53,8 +53,11 @@ function serverStandalone (ctx: WebpackConfigContext) {
if (!Array.isArray(ctx.config.externals)) { return } if (!Array.isArray(ctx.config.externals)) { return }
ctx.config.externals.push(({ request }, cb) => { ctx.config.externals.push(({ request }, cb) => {
if (!request) {
return cb(undefined, false)
}
if (external.includes(request)) { if (external.includes(request)) {
return cb(null, true) return cb(undefined, true)
} }
if ( if (
request[0] === '.' || request[0] === '.' ||
@ -63,16 +66,18 @@ function serverStandalone (ctx: WebpackConfigContext) {
assetPattern.test(request) assetPattern.test(request)
) { ) {
// console.log('Inline', request) // console.log('Inline', request)
return cb(null, false) return cb(undefined, false)
} }
// console.log('Ext', request) // console.log('Ext', request)
return cb(null, true) return cb(undefined, true)
}) })
} }
function serverPlugins (ctx: WebpackConfigContext) { function serverPlugins (ctx: WebpackConfigContext) {
const { config, options } = ctx const { config, options } = ctx
config.plugins = config.plugins || []
// Server polyfills // Server polyfills
if (options.webpack.serverURLPolyfill) { if (options.webpack.serverURLPolyfill) {
config.plugins.push(new webpack.ProvidePlugin({ config.plugins.push(new webpack.ProvidePlugin({
@ -83,6 +88,6 @@ function serverPlugins (ctx: WebpackConfigContext) {
// Add type-checking // Add type-checking
if (ctx.nuxt.options.typescript.typeCheck === true || (ctx.nuxt.options.typescript.typeCheck === 'build' && !ctx.nuxt.options.dev)) { if (ctx.nuxt.options.typescript.typeCheck === true || (ctx.nuxt.options.typescript.typeCheck === 'build' && !ctx.nuxt.options.dev)) {
ctx.config.plugins.push(new ForkTSCheckerWebpackPlugin({ logger })) config.plugins.push(new ForkTSCheckerWebpackPlugin({ logger }))
} }
} }

View File

@ -24,7 +24,9 @@ export const DynamicBasePlugin = createUnplugin((options: DynamicBasePluginOptio
s.append(`${options.globalPublicPath} = buildAssetsURL();\n`) s.append(`${options.globalPublicPath} = buildAssetsURL();\n`)
return { return {
code: s.toString(), code: s.toString(),
map: options.sourcemap && s.generateMap({ source: id, includeContent: true }) map: options.sourcemap
? s.generateMap({ source: id, includeContent: true })
: undefined
} }
} }
} }

View File

@ -10,6 +10,7 @@ import { uniq } from 'lodash-es'
import fse from 'fs-extra' import fse from 'fs-extra'
import type { Nuxt } from '@nuxt/schema' import type { Nuxt } from '@nuxt/schema'
import type { Compilation, Compiler } from 'webpack'
import { isJS, isCSS, isHotUpdate } from './util' import { isJS, isCSS, isHotUpdate } from './util'
interface PluginOptions { interface PluginOptions {
@ -26,17 +27,17 @@ export default class VueSSRClientPlugin {
}, options) }, options)
} }
apply (compiler) { apply (compiler: Compiler) {
compiler.hooks.afterEmit.tap('VueSSRClientPlugin', async (compilation: any) => { compiler.hooks.afterEmit.tap('VueSSRClientPlugin', async (compilation: Compilation) => {
const stats = compilation.getStats().toJson() const stats = compilation.getStats().toJson()
const allFiles = uniq(stats.assets const allFiles = uniq(stats.assets!
.map(a => a.name)) .map(a => a.name))
.filter(file => !isHotUpdate(file)) .filter(file => !isHotUpdate(file))
const initialFiles = uniq(Object.keys(stats.entrypoints) const initialFiles = uniq(Object.keys(stats.entrypoints!)
.map(name => stats.entrypoints[name].assets) .map(name => stats.entrypoints![name].assets!)
.reduce((files, entryAssets) => files.concat(entryAssets.map(entryAsset => entryAsset.name)), []) .reduce((files, entryAssets) => files.concat(entryAssets.map(entryAsset => entryAsset.name)), [] as string[])
.filter(file => isJS(file) || isCSS(file))) .filter(file => isJS(file) || isCSS(file)))
.filter(file => !isHotUpdate(file)) .filter(file => !isHotUpdate(file))
@ -45,11 +46,11 @@ export default class VueSSRClientPlugin {
.filter(file => !initialFiles.includes(file)) .filter(file => !initialFiles.includes(file))
.filter(file => !isHotUpdate(file)) .filter(file => !isHotUpdate(file))
const assetsMapping = {} const assetsMapping: Record<string, string[]> = {}
stats.assets stats.assets!
.filter(({ name }) => isJS(name)) .filter(({ name }) => isJS(name))
.filter(({ name }) => !isHotUpdate(name)) .filter(({ name }) => !isHotUpdate(name))
.forEach(({ name, chunkNames }) => { .forEach(({ name, chunkNames = [] }) => {
const componentHash = hash(chunkNames.join('|')) const componentHash = hash(chunkNames.join('|'))
if (!assetsMapping[componentHash]) { if (!assetsMapping[componentHash]) {
assetsMapping[componentHash] = [] assetsMapping[componentHash] = []
@ -62,29 +63,29 @@ export default class VueSSRClientPlugin {
all: allFiles, all: allFiles,
initial: initialFiles, initial: initialFiles,
async: asyncFiles, async: asyncFiles,
modules: { /* [identifier: string]: Array<index: number> */ }, modules: { /* [identifier: string]: Array<index: number> */ } as Record<string, number[]>,
assetsMapping assetsMapping
} }
const { entrypoints, namedChunkGroups } = stats const { entrypoints = {}, namedChunkGroups = {} } = stats
const assetModules = stats.modules.filter(m => m.assets.length) const assetModules = stats.modules!.filter(m => m.assets!.length)
const fileToIndex = file => webpackManifest.all.indexOf(file) const fileToIndex = (file: string) => webpackManifest.all.indexOf(file)
stats.modules.forEach((m) => { stats.modules!.forEach((m) => {
// Ignore modules duplicated in multiple chunks // Ignore modules duplicated in multiple chunks
if (m.chunks.length === 1) { if (m.chunks!.length === 1) {
const [cid] = m.chunks const [cid] = m.chunks!
const chunk = stats.chunks.find(c => c.id === cid) const chunk = stats.chunks!.find(c => c.id === cid)
if (!chunk || !chunk.files) { if (!chunk || !chunk.files) {
return return
} }
const id = m.identifier.replace(/\s\w+$/, '') // remove appended hash const id = m.identifier!.replace(/\s\w+$/, '') // remove appended hash
const filesSet = new Set(chunk.files.map(fileToIndex).filter(i => i !== -1)) const filesSet = new Set(chunk.files.map(fileToIndex).filter(i => i !== -1))
for (const chunkName of chunk.names) { for (const chunkName of chunk.names!) {
if (!entrypoints[chunkName]) { if (!entrypoints[chunkName]) {
const chunkGroup = namedChunkGroups[chunkName] const chunkGroup = namedChunkGroups[chunkName]
if (chunkGroup) { if (chunkGroup) {
for (const asset of chunkGroup.assets) { for (const asset of chunkGroup.assets!) {
filesSet.add(fileToIndex(asset.name)) filesSet.add(fileToIndex(asset.name))
} }
} }
@ -98,7 +99,7 @@ export default class VueSSRClientPlugin {
// Include ConcatenatedModule for not losing module-component mapping // Include ConcatenatedModule for not losing module-component mapping
if (Array.isArray(m.modules)) { if (Array.isArray(m.modules)) {
for (const concatenatedModule of m.modules) { for (const concatenatedModule of m.modules) {
const id = hash(concatenatedModule.identifier.replace(/\s\w+$/, '')) const id = hash(concatenatedModule.identifier!.replace(/\s\w+$/, ''))
if (!webpackManifest.modules[id]) { if (!webpackManifest.modules[id]) {
webpackManifest.modules[id] = files webpackManifest.modules[id] = files
} }
@ -107,14 +108,14 @@ export default class VueSSRClientPlugin {
// Find all asset modules associated with the same chunk // Find all asset modules associated with the same chunk
assetModules.forEach((m) => { assetModules.forEach((m) => {
if (m.chunks.includes(cid)) { if (m.chunks!.includes(cid)) {
files.push.apply(files, m.assets.map(fileToIndex)) files.push.apply(files, (m.assets as string[]).map(fileToIndex))
} }
}) })
} }
}) })
const manifest = normalizeWebpackManifest(webpackManifest) const manifest = normalizeWebpackManifest(webpackManifest as any)
await this.options.nuxt.callHook('build:manifest', manifest) await this.options.nuxt.callHook('build:manifest', manifest)
const src = JSON.stringify(manifest, null, 2) const src = JSON.stringify(manifest, null, 2)

View File

@ -1,35 +1,36 @@
import webpack, { Compilation, Compiler } from 'webpack'
import webpack from 'webpack'
import { validate, isJS, extractQueryPartJS } from './util' import { validate, isJS, extractQueryPartJS } from './util'
export default class VueSSRServerPlugin { export interface VueSSRServerPluginOptions {
options: { filename: string
filename?: string }
}
constructor (options = {}) { export default class VueSSRServerPlugin {
options: VueSSRServerPluginOptions
constructor (options: Partial<VueSSRServerPluginOptions> = {}) {
this.options = Object.assign({ this.options = Object.assign({
filename: null filename: null
}, options) }, options) as VueSSRServerPluginOptions
} }
apply (compiler) { apply (compiler: Compiler) {
validate(compiler) validate(compiler)
compiler.hooks.make.tap('VueSSRServerPlugin', (compilation: any) => { compiler.hooks.make.tap('VueSSRServerPlugin', (compilation: Compilation) => {
compilation.hooks.processAssets.tapAsync({ compilation.hooks.processAssets.tapAsync({
name: 'VueSSRServerPlugin', name: 'VueSSRServerPlugin',
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
}, (assets, cb) => { }, (assets: any, cb: any) => {
const stats = compilation.getStats().toJson() const stats = compilation.getStats().toJson()
const [entryName] = Object.keys(stats.entrypoints) const [entryName] = Object.keys(stats.entrypoints!)
const entryInfo = stats.entrypoints[entryName] const entryInfo = stats.entrypoints![entryName]
if (!entryInfo) { if (!entryInfo) {
// #5553 // #5553
return cb() return cb()
} }
const entryAssets = entryInfo.assets.filter(asset => isJS(asset.name)) const entryAssets = entryInfo.assets!.filter((asset: { name:string }) => isJS(asset.name))
if (entryAssets.length > 1) { if (entryAssets.length > 1) {
throw new Error( throw new Error(
@ -47,11 +48,11 @@ export default class VueSSRServerPlugin {
const bundle = { const bundle = {
entry: entry.name, entry: entry.name,
files: {}, files: {} as Record<string, string>,
maps: {} maps: {} as Record<string, string>
} }
stats.assets.forEach((asset) => { stats.assets!.forEach((asset: any) => {
if (isJS(asset.name)) { if (isJS(asset.name)) {
const queryPart = extractQueryPartJS(asset.name) const queryPart = extractQueryPartJS(asset.name)
if (queryPart !== undefined) { if (queryPart !== undefined) {

View File

@ -4,8 +4,9 @@
*/ */
import { logger } from '@nuxt/kit' import { logger } from '@nuxt/kit'
import type { Compiler } from 'webpack'
export const validate = (compiler) => { export const validate = (compiler: Compiler) => {
if (compiler.options.target !== 'node') { if (compiler.options.target !== 'node') {
logger.warn('webpack config `target` should be "node".') logger.warn('webpack config `target` should be "node".')
} }
@ -20,10 +21,10 @@ export const validate = (compiler) => {
const isJSRegExp = /\.[cm]?js(\?[^.]+)?$/ const isJSRegExp = /\.[cm]?js(\?[^.]+)?$/
export const isJS = file => isJSRegExp.test(file) export const isJS = (file: string) => isJSRegExp.test(file)
export const extractQueryPartJS = file => isJSRegExp.exec(file)[1] export const extractQueryPartJS = (file: string) => isJSRegExp.exec(file)?.[1]
export const isCSS = file => /\.css(\?[^.]+)?$/.test(file) export const isCSS = (file: string) => /\.css(\?[^.]+)?$/.test(file)
export const isHotUpdate = file => file.includes('hot-update') export const isHotUpdate = (file: string) => file.includes('hot-update')

View File

@ -1,7 +1,7 @@
import { fileName, WebpackConfigContext } from '../utils/config' import { fileName, WebpackConfigContext } from '../utils/config'
export function assets (ctx: WebpackConfigContext) { export function assets (ctx: WebpackConfigContext) {
ctx.config.module.rules.push( ctx.config.module!.rules!.push(
{ {
test: /\.(png|jpe?g|gif|svg|webp)$/i, test: /\.(png|jpe?g|gif|svg|webp)$/i,
use: [{ use: [{

View File

@ -1,8 +1,10 @@
import { resolve, normalize } from 'pathe' import { resolve, normalize } from 'pathe'
// @ts-expect-error missing types
import TimeFixPlugin from 'time-fix-plugin' import TimeFixPlugin from 'time-fix-plugin'
import WebpackBar from 'webpackbar' import WebpackBar from 'webpackbar'
import webpack from 'webpack' import webpack from 'webpack'
import { logger } from '@nuxt/kit' import { logger } from '@nuxt/kit'
// @ts-expect-error missing types
import FriendlyErrorsWebpackPlugin from '@nuxt/friendly-errors-webpack-plugin' import FriendlyErrorsWebpackPlugin from '@nuxt/friendly-errors-webpack-plugin'
import escapeRegExp from 'escape-string-regexp' import escapeRegExp from 'escape-string-regexp'
import { joinURL } from 'ufo' import { joinURL } from 'ufo'
@ -44,6 +46,8 @@ function baseConfig (ctx: WebpackConfigContext) {
function basePlugins (ctx: WebpackConfigContext) { function basePlugins (ctx: WebpackConfigContext) {
const { config, options, nuxt } = ctx const { config, options, nuxt } = ctx
config.plugins = config.plugins || []
// Add timefix-plugin before other plugins // Add timefix-plugin before other plugins
if (options.dev) { if (options.dev) {
config.plugins.push(new TimeFixPlugin()) config.plugins.push(new TimeFixPlugin())
@ -63,7 +67,7 @@ function basePlugins (ctx: WebpackConfigContext) {
ctx.isServer || ctx.isServer ||
(ctx.isDev && !options.build.quiet && options.webpack.friendlyErrors) (ctx.isDev && !options.build.quiet && options.webpack.friendlyErrors)
) { ) {
ctx.config.plugins.push( config.plugins.push(
new FriendlyErrorsWebpackPlugin({ new FriendlyErrorsWebpackPlugin({
clearConsole: false, clearConsole: false,
reporter: 'consola', reporter: 'consola',
@ -81,16 +85,17 @@ function basePlugins (ctx: WebpackConfigContext) {
} }
config.plugins.push(new WebpackBar({ config.plugins.push(new WebpackBar({
name: ctx.name, name: ctx.name,
color: colors[ctx.name], color: colors[ctx.name as keyof typeof colors],
reporters: ['stats'], reporters: ['stats'],
stats: !ctx.isDev, stats: !ctx.isDev,
reporter: { reporter: {
// @ts-ignore // @ts-ignore
change: (_, { shortPath }) => { change: (_, { shortPath }) => {
if (!ctx.isServer) { if (!ctx.isServer) {
nuxt.callHook('bundler:change', shortPath) nuxt.callHook('bundler:change', shortPath)
} }
}, },
// @ts-ignore
done: ({ state }) => { done: ({ state }) => {
if (state.hasErrors) { if (state.hasErrors) {
nuxt.callHook('bundler:error') nuxt.callHook('bundler:error')
@ -101,6 +106,7 @@ function basePlugins (ctx: WebpackConfigContext) {
allDone: () => { allDone: () => {
nuxt.callHook('bundler:done') nuxt.callHook('bundler:done')
}, },
// @ts-ignore
progress ({ statesArray }) { progress ({ statesArray }) {
nuxt.callHook('bundler:progress', statesArray) nuxt.callHook('bundler:progress', statesArray)
} }
@ -220,7 +226,7 @@ function getWarningIgnoreFilter (ctx: WebpackConfigContext): WarningFilter {
function getEnv (ctx: WebpackConfigContext) { function getEnv (ctx: WebpackConfigContext) {
const { options } = ctx const { options } = ctx
const _env = { const _env: Record<string, string | boolean> = {
'process.env.NODE_ENV': JSON.stringify(ctx.config.mode), 'process.env.NODE_ENV': JSON.stringify(ctx.config.mode),
'process.mode': JSON.stringify(ctx.config.mode), 'process.mode': JSON.stringify(ctx.config.mode),
'process.dev': options.dev, 'process.dev': options.dev,
@ -239,7 +245,7 @@ function getEnv (ctx: WebpackConfigContext) {
Object.entries(options.env).forEach(([key, value]) => { Object.entries(options.env).forEach(([key, value]) => {
const isNative = ['boolean', 'number'].includes(typeof value) const isNative = ['boolean', 'number'].includes(typeof value)
_env['process.env.' + key] = isNative ? value : JSON.stringify(value) _env['process.env.' + key] = isNative ? value as string : JSON.stringify(value)
}) })
return _env return _env

View File

@ -10,9 +10,9 @@ export function esbuild (ctx: WebpackConfigContext) {
const target = ctx.isServer ? 'es2019' : 'chrome85' const target = ctx.isServer ? 'es2019' : 'chrome85'
// https://github.com/nuxt/framework/issues/2372 // https://github.com/nuxt/framework/issues/2372
config.optimization.minimizer.push(new (esbuildLoader as unknown as typeof import('esbuild-loader')).ESBuildMinifyPlugin()) config.optimization!.minimizer!.push(new (esbuildLoader as unknown as typeof import('esbuild-loader')).ESBuildMinifyPlugin())
config.module.rules.push( config.module!.rules!.push(
{ {
test: /\.m?[jt]s$/i, test: /\.m?[jt]s$/i,
loader: 'esbuild-loader', loader: 'esbuild-loader',

View File

@ -6,7 +6,7 @@ export function node (ctx: WebpackConfigContext) {
config.target = 'node' config.target = 'node'
config.node = false config.node = false
config.experiments.outputModule = true config.experiments!.outputModule = true
config.output = { config.output = {
...config.output, ...config.output,

View File

@ -1,7 +1,7 @@
import { WebpackConfigContext } from '../utils/config' import { WebpackConfigContext } from '../utils/config'
export function pug (ctx: WebpackConfigContext) { export function pug (ctx: WebpackConfigContext) {
ctx.config.module.rules.push({ ctx.config.module!.rules!.push({
test: /\.pug$/i, test: /\.pug$/i,
oneOf: [ oneOf: [
{ {

View File

@ -14,8 +14,8 @@ export function style (ctx: WebpackConfigContext) {
function minimizer (ctx: WebpackConfigContext) { function minimizer (ctx: WebpackConfigContext) {
const { options, config } = ctx const { options, config } = ctx
if (options.webpack.optimizeCSS && Array.isArray(config.optimization.minimizer)) { if (options.webpack.optimizeCSS && Array.isArray(config.optimization!.minimizer)) {
config.optimization.minimizer.push(new CssMinimizerPlugin({ config.optimization!.minimizer.push(new CssMinimizerPlugin({
...options.webpack.optimizeCSS ...options.webpack.optimizeCSS
})) }))
} }
@ -26,7 +26,7 @@ function extractCSS (ctx: WebpackConfigContext) {
// CSS extraction // CSS extraction
if (options.webpack.extractCSS) { if (options.webpack.extractCSS) {
config.plugins.push(new MiniCssExtractPlugin({ config.plugins!.push(new MiniCssExtractPlugin({
filename: fileName(ctx, 'css'), filename: fileName(ctx, 'css'),
chunkFilename: fileName(ctx, 'css'), chunkFilename: fileName(ctx, 'css'),
...options.webpack.extractCSS === true ? {} : options.webpack.extractCSS ...options.webpack.extractCSS === true ? {} : options.webpack.extractCSS
@ -38,28 +38,28 @@ function loaders (ctx: WebpackConfigContext) {
const { config, options } = ctx const { config, options } = ctx
// CSS // CSS
config.module.rules.push(createdStyleRule('css', /\.css$/i, null, ctx)) config.module!.rules!.push(createdStyleRule('css', /\.css$/i, null, ctx))
// Postcss // Postcss
config.module.rules.push(createdStyleRule('postcss', /\.p(ost)?css$/i, null, ctx)) config.module!.rules!.push(createdStyleRule('postcss', /\.p(ost)?css$/i, null, ctx))
// Less // Less
const lessLoader = { loader: 'less-loader', options: options.webpack.loaders.less } const lessLoader = { loader: 'less-loader', options: options.webpack.loaders.less }
config.module.rules.push(createdStyleRule('less', /\.less$/i, lessLoader, ctx)) config.module!.rules!.push(createdStyleRule('less', /\.less$/i, lessLoader, ctx))
// Sass (TODO: optional dependency) // Sass (TODO: optional dependency)
const sassLoader = { loader: 'sass-loader', options: options.webpack.loaders.sass } const sassLoader = { loader: 'sass-loader', options: options.webpack.loaders.sass }
config.module.rules.push(createdStyleRule('sass', /\.sass$/i, sassLoader, ctx)) config.module!.rules!.push(createdStyleRule('sass', /\.sass$/i, sassLoader, ctx))
const scssLoader = { loader: 'sass-loader', options: options.webpack.loaders.scss } const scssLoader = { loader: 'sass-loader', options: options.webpack.loaders.scss }
config.module.rules.push(createdStyleRule('scss', /\.scss$/i, scssLoader, ctx)) config.module!.rules!.push(createdStyleRule('scss', /\.scss$/i, scssLoader, ctx))
// Stylus // Stylus
const stylusLoader = { loader: 'stylus-loader', options: options.webpack.loaders.stylus } const stylusLoader = { loader: 'stylus-loader', options: options.webpack.loaders.stylus }
config.module.rules.push(createdStyleRule('stylus', /\.styl(us)?$/i, stylusLoader, ctx)) config.module!.rules!.push(createdStyleRule('stylus', /\.styl(us)?$/i, stylusLoader, ctx))
} }
function createdStyleRule (lang: string, test: RegExp, processorLoader, ctx: WebpackConfigContext) { function createdStyleRule (lang: string, test: RegExp, processorLoader: any, ctx: WebpackConfigContext) {
const { options } = ctx const { options } = ctx
const styleLoaders = [ const styleLoaders = [
@ -90,7 +90,7 @@ function createdStyleRule (lang: string, test: RegExp, processorLoader, ctx: Web
} }
} }
function createCssLoadersRule (ctx: WebpackConfigContext, cssLoaderOptions) { function createCssLoadersRule (ctx: WebpackConfigContext, cssLoaderOptions: any) {
const { options } = ctx const { options } = ctx
const cssLoader = { loader: 'css-loader', options: cssLoaderOptions } const cssLoader = { loader: 'css-loader', options: cssLoaderOptions }

View File

@ -11,7 +11,7 @@ export function vue (ctx: WebpackConfigContext) {
// @ts-ignore // @ts-ignore
config.plugins.push(new (VueLoaderPlugin.default || VueLoaderPlugin)()) config.plugins.push(new (VueLoaderPlugin.default || VueLoaderPlugin)())
config.module.rules.push({ config.module!.rules!.push({
test: /\.vue$/i, test: /\.vue$/i,
loader: 'vue-loader', loader: 'vue-loader',
options: { options: {
@ -21,12 +21,12 @@ export function vue (ctx: WebpackConfigContext) {
}) })
if (ctx.isClient) { if (ctx.isClient) {
config.plugins.push(new VueSSRClientPlugin({ config.plugins!.push(new VueSSRClientPlugin({
filename: resolve(options.buildDir, 'dist/server', `${ctx.name}.manifest.json`), filename: resolve(options.buildDir, 'dist/server', `${ctx.name}.manifest.json`),
nuxt: ctx.nuxt nuxt: ctx.nuxt
})) }))
} else { } else {
config.plugins.push(new VueSSRServerPlugin({ config.plugins!.push(new VueSSRServerPlugin({
filename: `${ctx.name}.manifest.json` filename: `${ctx.name}.manifest.json`
})) }))
} }
@ -34,7 +34,7 @@ export function vue (ctx: WebpackConfigContext) {
// Feature flags // Feature flags
// https://github.com/vuejs/vue-next/tree/master/packages/vue#bundler-build-feature-flags // https://github.com/vuejs/vue-next/tree/master/packages/vue#bundler-build-feature-flags
// TODO: Provide options to toggle // TODO: Provide options to toggle
config.plugins.push(new webpack.DefinePlugin({ config.plugins!.push(new webpack.DefinePlugin({
__VUE_OPTIONS_API__: 'true', __VUE_OPTIONS_API__: 'true',
__VUE_PROD_DEVTOOLS__: 'false' __VUE_PROD_DEVTOOLS__: 'false'
})) }))

View File

@ -19,7 +19,7 @@ export function createWebpackConfigContext (nuxt: Nuxt) {
isServer: false, isServer: false,
isClient: false, isClient: false,
alias: {} as Configuration['resolve']['alias'], alias: {} as { [index: string]: string | false | string[] },
transpile: [] as RegExp[] transpile: [] as RegExp[]
} }
} }
@ -40,7 +40,7 @@ export function applyPresets (ctx: WebpackConfigContext, presets: WebpackConfigP
export function fileName (ctx: WebpackConfigContext, key: string) { export function fileName (ctx: WebpackConfigContext, key: string) {
const { options } = ctx const { options } = ctx
let fileName = options.webpack.filenames[key] let fileName = options.webpack.filenames[key as keyof typeof options.webpack.filenames] as ((ctx: WebpackConfigContext) => string) | string
if (typeof fileName === 'function') { if (typeof fileName === 'function') {
fileName = fileName(ctx) fileName = fileName(ctx)
@ -61,7 +61,7 @@ export function getWebpackConfig (ctx: WebpackConfigContext): Configuration {
// TODO // TODO
const builder = {} const builder = {}
const loaders = [] const loaders: any[] = []
// @ts-ignore // @ts-ignore
const { extend } = options.build const { extend } = options.build

View File

@ -9,7 +9,7 @@ export function createMFS () {
const fs = createFsFromVolume(new Volume()) const fs = createFsFromVolume(new Volume())
// Clone to extend // Clone to extend
const _fs: Partial<IFs> & { join?(...paths: string[]): string } = { ...fs } const _fs: IFs & { join?(...paths: string[]): string } = { ...fs } as any
// fs.join method is (still) expected by webpack-dev-middleware // fs.join method is (still) expected by webpack-dev-middleware
// There might be differences with https://github.com/webpack/memory-fs/blob/master/lib/join.js // There might be differences with https://github.com/webpack/memory-fs/blob/master/lib/join.js

View File

@ -35,20 +35,20 @@ export const getPostcssConfig = (nuxt: Nuxt) => {
} }
} }
function sortPlugins ({ plugins, order }) { function sortPlugins ({ plugins, order }: any) {
const names = Object.keys(plugins) const names = Object.keys(plugins)
if (typeof order === 'string') { if (typeof order === 'string') {
order = orderPresets[order] order = orderPresets[order as keyof typeof orderPresets]
} }
return typeof order === 'function' ? order(names, orderPresets) : (order || names) return typeof order === 'function' ? order(names, orderPresets) : (order || names)
} }
function loadPlugins (config) { function loadPlugins (config: any) {
if (!isPureObject(config.plugins)) { return } if (!isPureObject(config.plugins)) { return }
// Map postcss plugins into instances on object mode once // Map postcss plugins into instances on object mode once
const cjs = createCommonJS(import.meta.url) const cjs = createCommonJS(import.meta.url)
config.plugins = sortPlugins(config).map((pluginName) => { config.plugins = sortPlugins(config).map((pluginName: string) => {
const pluginFn = requireModule(pluginName, { paths: [cjs.__dirname] }) const pluginFn = requireModule(pluginName, { paths: [cjs.__dirname] })
const pluginOptions = config.plugins[pluginName] const pluginOptions = config.plugins[pluginName]
if (!pluginOptions || typeof pluginFn !== 'function') { return null } if (!pluginOptions || typeof pluginFn !== 'function') { return null }
@ -81,9 +81,11 @@ export const getPostcssConfig = (nuxt: Nuxt) => {
loadPlugins(postcssOptions) loadPlugins(postcssOptions)
} }
// @ts-expect-error
delete nuxt.options.webpack.postcss.order delete nuxt.options.webpack.postcss.order
return { return {
// @ts-expect-error
sourceMap: nuxt.options.webpack.cssSourceMap, sourceMap: nuxt.options.webpack.cssSourceMap,
...nuxt.options.webpack.postcss, ...nuxt.options.webpack.postcss,
postcssOptions postcssOptions

View File

@ -21,6 +21,6 @@ export function registerVirtualModules () {
nuxt.hook('webpack:config', configs => configs.forEach((config) => { nuxt.hook('webpack:config', configs => configs.forEach((config) => {
// Support virtual modules (input) // Support virtual modules (input)
config.plugins.push(virtualModules) config.plugins!.push(virtualModules)
})) }))
} }

View File

@ -1,7 +1,7 @@
import type { IncomingMessage, ServerResponse } from 'node:http' import type { IncomingMessage, ServerResponse } from 'node:http'
import pify from 'pify' import pify from 'pify'
import webpack from 'webpack' import webpack from 'webpack'
import webpackDevMiddleware, { API } from 'webpack-dev-middleware' import webpackDevMiddleware, { API, OutputFileSystem } from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware' import webpackHotMiddleware from 'webpack-hot-middleware'
import type { Compiler, Watching } from 'webpack' import type { Compiler, Watching } from 'webpack'
@ -34,10 +34,10 @@ export async function bundle (nuxt: Nuxt) {
// Configure compilers // Configure compilers
const compilers = webpackConfigs.map((config) => { const compilers = webpackConfigs.map((config) => {
config.plugins.push(DynamicBasePlugin.webpack({ config.plugins!.push(DynamicBasePlugin.webpack({
sourcemap: nuxt.options.sourcemap sourcemap: nuxt.options.sourcemap
})) }))
config.plugins.push(composableKeysPlugin.webpack({ config.plugins!.push(composableKeysPlugin.webpack({
sourcemap: nuxt.options.sourcemap, sourcemap: nuxt.options.sourcemap,
rootDir: nuxt.options.rootDir rootDir: nuxt.options.rootDir
})) }))
@ -47,7 +47,7 @@ export async function bundle (nuxt: Nuxt) {
// In dev, write files in memory FS // In dev, write files in memory FS
if (nuxt.options.dev) { if (nuxt.options.dev) {
compiler.outputFileSystem = mfs compiler.outputFileSystem = mfs as unknown as OutputFileSystem
} }
return compiler return compiler
@ -88,7 +88,7 @@ async function createDevMiddleware (compiler: Compiler) {
const hotMiddleware = pify(webpackHotMiddleware(compiler, { const hotMiddleware = pify(webpackHotMiddleware(compiler, {
log: false, log: false,
heartbeat: 10000, heartbeat: 10000,
path: joinURL(nuxt.options.app.baseURL, '__webpack_hmr', compiler.options.name), path: joinURL(nuxt.options.app.baseURL, '__webpack_hmr', compiler.options.name!),
...hotMiddlewareOptions ...hotMiddlewareOptions
})) }))
@ -96,9 +96,9 @@ async function createDevMiddleware (compiler: Compiler) {
await nuxt.callHook('webpack:hotMiddleware', hotMiddleware) await nuxt.callHook('webpack:hotMiddleware', hotMiddleware)
// Register devMiddleware on server // Register devMiddleware on server
await nuxt.callHook('server:devMiddleware', async (req, res, next) => { await nuxt.callHook('server:devMiddleware', async (req: IncomingMessage, res: ServerResponse, next: (error?: any) => void) => {
for (const mw of [devMiddleware, hotMiddleware]) { for (const mw of [devMiddleware, hotMiddleware]) {
await mw?.(req, res) await mw?.(req, res, next)
} }
next() next()
}) })
@ -111,11 +111,11 @@ async function compile (compiler: Compiler) {
const { name } = compiler.options const { name } = compiler.options
await nuxt.callHook('build:compile', { name, compiler }) await nuxt.callHook('build:compile', { name: name!, compiler })
// Load renderer resources after build // Load renderer resources after build
compiler.hooks.done.tap('load-resources', async (stats) => { compiler.hooks.done.tap('load-resources', async (stats) => {
await nuxt.callHook('build:compiled', { name, compiler, stats }) await nuxt.callHook('build:compiled', { name: name!, compiler, stats })
// Reload renderer // Reload renderer
await nuxt.callHook('build:resources', compiler.outputFileSystem) await nuxt.callHook('build:resources', compiler.outputFileSystem)
}) })
@ -152,7 +152,7 @@ async function compile (compiler: Compiler) {
} }
// --- Production Build --- // --- Production Build ---
const stats = await new Promise<webpack.Stats>((resolve, reject) => compiler.run((err, stats) => err ? reject(err) : resolve(stats))) const stats = await new Promise<webpack.Stats>((resolve, reject) => compiler.run((err, stats) => err ? reject(err) : resolve(stats!)))
if (stats.hasErrors()) { if (stats.hasErrors()) {
// non-quiet mode: errors will be printed by webpack itself // non-quiet mode: errors will be printed by webpack itself

View File

@ -3,16 +3,22 @@ import { execSync } from 'node:child_process'
import { resolve } from 'pathe' import { resolve } from 'pathe'
import { globby } from 'globby' import { globby } from 'globby'
interface Dep {
name: string,
range: string,
type: string
}
async function loadPackage (dir: string) { async function loadPackage (dir: string) {
const pkgPath = resolve(dir, 'package.json') const pkgPath = resolve(dir, 'package.json')
const data = JSON.parse(await fsp.readFile(pkgPath, 'utf-8').catch(() => '{}')) const data = JSON.parse(await fsp.readFile(pkgPath, 'utf-8').catch(() => '{}'))
const save = () => fsp.writeFile(pkgPath, JSON.stringify(data, null, 2) + '\n') const save = () => fsp.writeFile(pkgPath, JSON.stringify(data, null, 2) + '\n')
const updateDeps = (reviver: Function) => { const updateDeps = (reviver: (dep: Dep) => Dep | void) => {
for (const type of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) { for (const type of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) {
if (!data[type]) { continue } if (!data[type]) { continue }
for (const e of Object.entries(data[type])) { for (const e of Object.entries(data[type])) {
const dep = { name: e[0], range: e[1], type } const dep: Dep = { name: e[0], range: e[1] as string, type }
delete data[type][dep.name] delete data[type][dep.name]
const updated = reviver(dep) || dep const updated = reviver(dep) || dep
data[updated.type] = data[updated.type] || {} data[updated.type] = data[updated.type] || {}

View File

@ -350,7 +350,7 @@ describe('extends support', () => {
describe('app', () => { describe('app', () => {
it('extends foo/app/router.options & bar/app/router.options', async () => { it('extends foo/app/router.options & bar/app/router.options', async () => {
const html: string = await $fetch('/') const html: string = await $fetch('/')
const routerLinkClasses = html.match(/href="\/" class="([^"]*)"/)[1].split(' ') const routerLinkClasses = html.match(/href="\/" class="([^"]*)"/)?.[1].split(' ')
expect(routerLinkClasses).toContain('foo-active-class') expect(routerLinkClasses).toContain('foo-active-class')
expect(routerLinkClasses).toContain('bar-exact-active-class') expect(routerLinkClasses).toContain('bar-exact-active-class')
}) })
@ -404,7 +404,7 @@ describe('dynamic paths', () => {
} }
it('should work with no overrides', async () => { it('should work with no overrides', async () => {
const html = await $fetch('/assets') const html: string = await $fetch('/assets')
for (const match of html.matchAll(/(href|src)="(.*?)"/g)) { for (const match of html.matchAll(/(href|src)="(.*?)"/g)) {
const url = match[2] const url = match[2]
expect(url.startsWith('/_nuxt/') || url === '/public.svg').toBeTruthy() expect(url.startsWith('/_nuxt/') || url === '/public.svg').toBeTruthy()
@ -417,11 +417,11 @@ describe('dynamic paths', () => {
return return
} }
const html = await $fetch('/assets') const html: string = await $fetch('/assets')
const urls = Array.from(html.matchAll(/(href|src)="(.*?)"/g)).map(m => m[2]) const urls = Array.from(html.matchAll(/(href|src)="(.*?)"/g)).map(m => m[2])
const cssURL = urls.find(u => /_nuxt\/assets.*\.css$/.test(u)) const cssURL = urls.find(u => /_nuxt\/assets.*\.css$/.test(u))
expect(cssURL).toBeDefined() expect(cssURL).toBeDefined()
const css = await $fetch(cssURL) const css: string = await $fetch(cssURL!)
const imageUrls = Array.from(css.matchAll(/url\(([^)]*)\)/g)).map(m => m[1].replace(/[-.][\w]{8}\./g, '.')) const imageUrls = Array.from(css.matchAll(/url\(([^)]*)\)/g)).map(m => m[1].replace(/[-.][\w]{8}\./g, '.'))
expect(imageUrls).toMatchInlineSnapshot(` expect(imageUrls).toMatchInlineSnapshot(`
[ [

View File

@ -4,13 +4,13 @@ import { expect } from 'vitest'
export async function renderPage (path = '/') { export async function renderPage (path = '/') {
const ctx = useTestContext() const ctx = useTestContext()
if (!ctx.options.browser) { if (!ctx.options.browser) {
return throw new Error('`renderPage` require `options.browser` to be set')
} }
const browser = await getBrowser() const browser = await getBrowser()
const page = await browser.newPage({}) const page = await browser.newPage({})
const pageErrors = [] const pageErrors: Error[] = []
const consoleLogs = [] const consoleLogs: { type:string, text:string }[] = []
page.on('console', (message) => { page.on('console', (message) => {
consoleLogs.push({ consoleLogs.push({
@ -39,7 +39,7 @@ export async function expectNoClientErrors (path: string) {
return return
} }
const { pageErrors, consoleLogs } = await renderPage(path) const { pageErrors, consoleLogs } = (await renderPage(path))!
const consoleLogErrors = consoleLogs.filter(i => i.type === 'error') const consoleLogErrors = consoleLogs.filter(i => i.type === 'error')
const consoleLogWarnings = consoleLogs.filter(i => i.type === 'warning') const consoleLogWarnings = consoleLogs.filter(i => i.type === 'warning')

View File

@ -6,7 +6,8 @@
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",
"moduleResolution": "Node", "moduleResolution": "Node",
"strict": false, "strict": true,
"noImplicitAny": true,
"allowJs": true, "allowJs": true,
"noEmit": true, "noEmit": true,
"noUnusedLocals": true, "noUnusedLocals": true,
@ -33,7 +34,7 @@
} }
}, },
"exclude": [ "exclude": [
"**/*/dist/*", "**/dist/**",
"**/.nuxt/**", "**/.nuxt/**",
"**/nuxt.d.ts", "**/nuxt.d.ts",
"**/examples/**", "**/examples/**",

View File

@ -327,7 +327,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/standalone@npm:^7.18.11": "@babel/standalone@npm:^7.18.11, @babel/standalone@npm:^7.18.13":
version: 7.18.13 version: 7.18.13
resolution: "@babel/standalone@npm:7.18.13" resolution: "@babel/standalone@npm:7.18.13"
checksum: da010b1ef0d53f7888d01b3ef93aac9a17af5711979e7bc048b80bf08ae6dfa6b637bf92fee0c5753b4ff1bc3639a5b82925f9234d4e2150fc6d4d5c2ccc1f89 checksum: da010b1ef0d53f7888d01b3ef93aac9a17af5711979e7bc048b80bf08ae6dfa6b637bf92fee0c5753b4ff1bc3639a5b82925f9234d4e2150fc6d4d5c2ccc1f89
@ -1661,7 +1661,7 @@ __metadata:
unbuild: latest unbuild: latest
unctx: ^2.0.1 unctx: ^2.0.1
unimport: ^0.6.7 unimport: ^0.6.7
untyped: ^0.4.5 untyped: ^0.4.7
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -1688,6 +1688,7 @@ __metadata:
ufo: ^0.8.5 ufo: ^0.8.5
unbuild: latest unbuild: latest
unimport: ^0.6.7 unimport: ^0.6.7
untyped: ^0.4.7
vite: ~3.0.9 vite: ~3.0.9
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -1825,6 +1826,7 @@ __metadata:
"@nuxt/friendly-errors-webpack-plugin": ^2.5.2 "@nuxt/friendly-errors-webpack-plugin": ^2.5.2
"@nuxt/kit": 3.0.0-rc.8 "@nuxt/kit": 3.0.0-rc.8
"@nuxt/schema": 3.0.0-rc.8 "@nuxt/schema": 3.0.0-rc.8
"@types/lodash-es": ^4.17.6
"@types/pify": ^5.0.1 "@types/pify": ^5.0.1
"@types/webpack-bundle-analyzer": ^4.4.2 "@types/webpack-bundle-analyzer": ^4.4.2
"@types/webpack-dev-middleware": ^5.0.2 "@types/webpack-dev-middleware": ^5.0.2
@ -2280,6 +2282,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/flat@npm:^5.0.2":
version: 5.0.2
resolution: "@types/flat@npm:5.0.2"
checksum: e21d51d872e788bdb381887c2880f717ba4377beb4055078136127134858efd15044655610f1fce4832d9a103ded468a25335203fc53f36db08edd5ab5a8b3db
languageName: node
linkType: hard
"@types/fs-extra@npm:^9.0.13": "@types/fs-extra@npm:^9.0.13":
version: 9.0.13 version: 9.0.13
resolution: "@types/fs-extra@npm:9.0.13" resolution: "@types/fs-extra@npm:9.0.13"
@ -2331,6 +2340,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/lodash-es@npm:^4.17.6":
version: 4.17.6
resolution: "@types/lodash-es@npm:4.17.6"
dependencies:
"@types/lodash": "*"
checksum: 9bd239dd525086e278821949ce12fbdd4f100a060fed9323fc7ad5661113e1641f28a7ebab617230ed3474680d8f4de705c1928b48252bb684be6ec9eed715db
languageName: node
linkType: hard
"@types/lodash.template@npm:^4": "@types/lodash.template@npm:^4":
version: 4.5.1 version: 4.5.1
resolution: "@types/lodash.template@npm:4.5.1" resolution: "@types/lodash.template@npm:4.5.1"
@ -9860,6 +9878,7 @@ __metadata:
"@nuxt/kit": 3.0.0-rc.8 "@nuxt/kit": 3.0.0-rc.8
"@nuxt/schema": 3.0.0-rc.8 "@nuxt/schema": 3.0.0-rc.8
"@types/clear": ^0 "@types/clear": ^0
"@types/flat": ^5.0.2
"@types/mri": ^1.1.1 "@types/mri": ^1.1.1
"@types/semver": ^7 "@types/semver": ^7
c12: ^0.2.9 c12: ^0.2.9
@ -9976,7 +9995,7 @@ __metadata:
unenv: ^0.6.1 unenv: ^0.6.1
unimport: ^0.6.7 unimport: ^0.6.7
unplugin: ^0.9.2 unplugin: ^0.9.2
untyped: ^0.4.5 untyped: ^0.4.7
vue: ^3.2.37 vue: ^3.2.37
vue-bundle-renderer: ^0.4.2 vue-bundle-renderer: ^0.4.2
vue-devtools-stub: ^0.1.0 vue-devtools-stub: ^0.1.0
@ -13190,6 +13209,18 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"untyped@npm:^0.4.7":
version: 0.4.7
resolution: "untyped@npm:0.4.7"
dependencies:
"@babel/core": ^7.18.13
"@babel/standalone": ^7.18.13
"@babel/types": ^7.18.13
scule: ^0.3.2
checksum: d5b189b19e114c4d60e122da9234c68a93d71b312a64bd8303e3aaa96f7a677befa8519ce003dec8cb587ed3e5503046131532196257ed10e647bc741532b1bc
languageName: node
linkType: hard
"upath@npm:^2.0.1": "upath@npm:^2.0.1":
version: 2.0.1 version: 2.0.1
resolution: "upath@npm:2.0.1" resolution: "upath@npm:2.0.1"