fix(kit,nuxt,schema,vite): improve watching behaviour (#30620)

This commit is contained in:
Daniel Roe 2025-01-16 15:02:01 +00:00 committed by GitHub
parent f1264e2812
commit bc669cba0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 39 additions and 24 deletions

View File

@ -3,12 +3,14 @@ import ignore from 'ignore'
import { join, relative, resolve } from 'pathe' import { join, relative, resolve } from 'pathe'
import { tryUseNuxt } from './context' import { tryUseNuxt } from './context'
export function createIsIgnored (nuxt = tryUseNuxt()) {
return (pathname: string, stats?: unknown) => isIgnored(pathname, stats, nuxt)
}
/** /**
* Return a filter function to filter an array of paths * Return a filter function to filter an array of paths
*/ */
export function isIgnored (pathname: string): boolean { export function isIgnored (pathname: string, _stats?: unknown, nuxt = tryUseNuxt()): boolean {
const nuxt = tryUseNuxt()
// Happens with CLI reloads // Happens with CLI reloads
if (!nuxt) { if (!nuxt) {
return false return false

View File

@ -19,7 +19,7 @@ export { assertNuxtCompatibility, checkNuxtCompatibility, getNuxtVersion, hasNux
export { addComponent, addComponentsDir } from './components' export { addComponent, addComponentsDir } from './components'
export type { AddComponentOptions } from './components' export type { AddComponentOptions } from './components'
export { nuxtCtx, tryUseNuxt, useNuxt } from './context' export { nuxtCtx, tryUseNuxt, useNuxt } from './context'
export { isIgnored, resolveIgnorePatterns } from './ignore' export { createIsIgnored, isIgnored, resolveIgnorePatterns } from './ignore'
export { addLayout } from './layout' export { addLayout } from './layout'
export { addRouteMiddleware, extendPages, extendRouteRules } from './pages' export { addRouteMiddleware, extendPages, extendRouteRules } from './pages'
export type { AddRouteMiddlewareOptions, ExtendRouteRulesOptions } from './pages' export type { AddRouteMiddlewareOptions, ExtendRouteRulesOptions } from './pages'

View File

@ -58,7 +58,7 @@ export function TransformPlugin (nuxt: Nuxt, options: TransformPluginOptions) {
enforce: 'post', enforce: 'post',
transformInclude (id) { transformInclude (id) {
id = normalize(id) id = normalize(id)
return id.startsWith('virtual:') || id.startsWith('\0virtual:') || id.startsWith(nuxt.options.buildDir) || !isIgnored(id) return id.startsWith('virtual:') || id.startsWith('\0virtual:') || id.startsWith(nuxt.options.buildDir) || !isIgnored(id, undefined, nuxt)
}, },
async transform (code, id) { async transform (code, id) {
// Virtual component wrapper // Virtual component wrapper

View File

@ -1,7 +1,7 @@
import type { EventType } from '@parcel/watcher' import type { EventType } from '@parcel/watcher'
import type { FSWatcher } from 'chokidar' import type { FSWatcher } from 'chokidar'
import { watch as chokidarWatch } from 'chokidar' import { watch as chokidarWatch } from 'chokidar'
import { importModule, isIgnored, tryResolveModule, useNuxt } from '@nuxt/kit' import { createIsIgnored, importModule, isIgnored, tryResolveModule, useNuxt } from '@nuxt/kit'
import { debounce } from 'perfect-debounce' import { debounce } from 'perfect-debounce'
import { normalize, relative, resolve } from 'pathe' import { normalize, relative, resolve } from 'pathe'
import type { Nuxt, NuxtBuilder } from 'nuxt/schema' import type { Nuxt, NuxtBuilder } from 'nuxt/schema'
@ -100,14 +100,12 @@ async function watch (nuxt: Nuxt) {
function createWatcher () { function createWatcher () {
const nuxt = useNuxt() const nuxt = useNuxt()
const isIgnored = createIsIgnored(nuxt)
const watcher = chokidarWatch(nuxt.options._layers.map(i => i.config.srcDir as string).filter(Boolean), { const watcher = chokidarWatch(nuxt.options._layers.map(i => i.config.srcDir as string).filter(Boolean), {
...nuxt.options.watchers.chokidar, ...nuxt.options.watchers.chokidar,
ignoreInitial: true, ignoreInitial: true,
ignored: [ ignored: [isIgnored, /[\\/]node_modules[\\/]/],
isIgnored,
'node_modules',
],
}) })
watcher.on('all', (event, path) => { watcher.on('all', (event, path) => {
@ -121,6 +119,7 @@ function createWatcher () {
function createGranularWatcher () { function createGranularWatcher () {
const nuxt = useNuxt() const nuxt = useNuxt()
const isIgnored = createIsIgnored(nuxt)
if (nuxt.options.debug) { if (nuxt.options.debug) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -139,7 +138,7 @@ function createGranularWatcher () {
} }
for (const dir of pathsToWatch) { for (const dir of pathsToWatch) {
pending++ pending++
const watcher = chokidarWatch(dir, { ...nuxt.options.watchers.chokidar, ignoreInitial: false, depth: 0, ignored: [isIgnored, '**/node_modules'] }) const watcher = chokidarWatch(dir, { ...nuxt.options.watchers.chokidar, ignoreInitial: false, depth: 0, ignored: [isIgnored, /[\\/]node_modules[\\/]/] })
const watchers: Record<string, FSWatcher> = {} const watchers: Record<string, FSWatcher> = {}
watcher.on('all', (event, path) => { watcher.on('all', (event, path) => {

View File

@ -2,7 +2,7 @@ import { mkdir, open, readFile, stat, unlink, writeFile } from 'node:fs/promises
import type { FileHandle } from 'node:fs/promises' import type { FileHandle } from 'node:fs/promises'
import { resolve } from 'node:path' import { resolve } from 'node:path'
import { existsSync } from 'node:fs' import { existsSync } from 'node:fs'
import { isIgnored } from '@nuxt/kit' import { createIsIgnored } from '@nuxt/kit'
import type { Nuxt, NuxtConfig, NuxtConfigLayer } from '@nuxt/schema' import type { Nuxt, NuxtConfig, NuxtConfigLayer } from '@nuxt/schema'
import { hash, murmurHash, objectHash } from 'ohash' import { hash, murmurHash, objectHash } from 'ohash'
import { glob } from 'tinyglobby' import { glob } from 'tinyglobby'
@ -119,6 +119,7 @@ async function getHashes (nuxt: Nuxt, options: GetHashOptions): Promise<Hashes>
data: murmurHash(f.data as any /* ArrayBuffer */), data: murmurHash(f.data as any /* ArrayBuffer */),
})) }))
const isIgnored = createIsIgnored(nuxt)
const sourceFiles = await readFilesRecursive(options.cwd(layer), { const sourceFiles = await readFilesRecursive(options.cwd(layer), {
shouldIgnore: isIgnored, // TODO: Validate if works with absolute paths shouldIgnore: isIgnored, // TODO: Validate if works with absolute paths
cwd: nuxt.options.rootDir, cwd: nuxt.options.rootDir,

View File

@ -5,11 +5,8 @@ import { resolve } from 'pathe'
import { watch } from 'chokidar' import { watch } from 'chokidar'
import { defu } from 'defu' import { defu } from 'defu'
import { debounce } from 'perfect-debounce' import { debounce } from 'perfect-debounce'
import { createResolver, defineNuxtModule, importModule, tryResolveModule } from '@nuxt/kit' import { createIsIgnored, createResolver, defineNuxtModule, importModule, tryResolveModule } from '@nuxt/kit'
import { import { generateTypes, resolveSchema as resolveUntypedSchema } from 'untyped'
generateTypes,
resolveSchema as resolveUntypedSchema,
} from 'untyped'
import type { Schema, SchemaDefinition } from 'untyped' import type { Schema, SchemaDefinition } from 'untyped'
import untypedPlugin from 'untyped/babel-plugin' import untypedPlugin from 'untyped/babel-plugin'
import { createJiti } from 'jiti' import { createJiti } from 'jiti'
@ -71,11 +68,17 @@ export default defineNuxtModule({
logger.warn('Falling back to `chokidar` as `@parcel/watcher` cannot be resolved in your project.') logger.warn('Falling back to `chokidar` as `@parcel/watcher` cannot be resolved in your project.')
} }
const filesToWatch = await Promise.all(nuxt.options._layers.map(layer => const isIgnored = createIsIgnored(nuxt)
resolver.resolve(layer.config.rootDir, 'nuxt.schema.*'), const dirsToWatch = nuxt.options._layers.map(layer => resolver.resolve(layer.config.rootDir))
)) const SCHEMA_RE = /(?:^|\/)nuxt.schema.\w+$/
const watcher = watch(filesToWatch, { const watcher = watch(dirsToWatch, {
...nuxt.options.watchers.chokidar, ...nuxt.options.watchers.chokidar,
depth: 1,
ignored: [
(path, stats) => (stats && !stats.isFile()) || !SCHEMA_RE.test(path),
isIgnored,
/[\\/]node_modules[\\/]/,
],
ignoreInitial: true, ignoreInitial: true,
}) })
watcher.on('all', onChange) watcher.on('all', onChange)

View File

@ -1,5 +1,5 @@
import { existsSync } from 'node:fs' import { existsSync } from 'node:fs'
import { addBuildPlugin, addTemplate, addTypeTemplate, defineNuxtModule, isIgnored, resolveAlias, tryResolveModule, updateTemplates, useNuxt } from '@nuxt/kit' import { addBuildPlugin, addTemplate, addTypeTemplate, createIsIgnored, defineNuxtModule, resolveAlias, tryResolveModule, updateTemplates, useNuxt } from '@nuxt/kit'
import { isAbsolute, join, normalize, relative, resolve } from 'pathe' import { isAbsolute, join, normalize, relative, resolve } from 'pathe'
import type { Import, Unimport } from 'unimport' import type { Import, Unimport } from 'unimport'
import { createUnimport, scanDirExports, toExports } from 'unimport' import { createUnimport, scanDirExports, toExports } from 'unimport'
@ -118,6 +118,7 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
return IMPORTS_TEMPLATE_RE.test(template.filename) return IMPORTS_TEMPLATE_RE.test(template.filename)
} }
const isIgnored = createIsIgnored(nuxt)
const regenerateImports = async () => { const regenerateImports = async () => {
await ctx.modifyDynamicImports(async (imports) => { await ctx.modifyDynamicImports(async (imports) => {
// Clear old imports // Clear old imports

View File

@ -28,6 +28,7 @@ export default defineBuildConfig({
// Type imports // Type imports
'@unhead/schema', '@unhead/schema',
'@vitejs/plugin-vue', '@vitejs/plugin-vue',
'chokidar',
'@vitejs/plugin-vue-jsx', '@vitejs/plugin-vue-jsx',
'@vue/language-core', '@vue/language-core',
'autoprefixer', 'autoprefixer',

View File

@ -44,6 +44,7 @@
"@vue/compiler-sfc": "3.5.13", "@vue/compiler-sfc": "3.5.13",
"@vue/language-core": "2.2.0", "@vue/language-core": "2.2.0",
"c12": "2.0.1", "c12": "2.0.1",
"chokidar": "4.0.3",
"compatx": "0.1.8", "compatx": "0.1.8",
"esbuild-loader": "4.2.2", "esbuild-loader": "4.2.2",
"file-loader": "6.2.0", "file-loader": "6.2.0",

View File

@ -519,9 +519,11 @@ export default defineUntypedSchema({
/** /**
* Options to pass directly to `chokidar`. * Options to pass directly to `chokidar`.
* @see [chokidar](https://github.com/paulmillr/chokidar#api) * @see [chokidar](https://github.com/paulmillr/chokidar#api)
* @type {typeof import('chokidar').ChokidarOptions}
*/ */
chokidar: { chokidar: {
ignoreInitial: true, ignoreInitial: true,
ignorePermissionErrors: true,
}, },
}, },

View File

@ -2,7 +2,7 @@ import { existsSync } from 'node:fs'
import * as vite from 'vite' import * as vite from 'vite'
import { dirname, join, normalize, resolve } from 'pathe' import { dirname, join, normalize, resolve } from 'pathe'
import type { Nuxt, NuxtBuilder, ViteConfig } from '@nuxt/schema' import type { Nuxt, NuxtBuilder, ViteConfig } from '@nuxt/schema'
import { addVitePlugin, isIgnored, logger, resolvePath, useNitro } from '@nuxt/kit' import { addVitePlugin, createIsIgnored, logger, resolvePath, useNitro } from '@nuxt/kit'
import replace from '@rollup/plugin-replace' import replace from '@rollup/plugin-replace'
import type { RollupReplaceOptions } from '@rollup/plugin-replace' import type { RollupReplaceOptions } from '@rollup/plugin-replace'
import { sanitizeFilePath } from 'mlly' import { sanitizeFilePath } from 'mlly'
@ -53,6 +53,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
const { $client, $server, ...viteConfig } = nuxt.options.vite const { $client, $server, ...viteConfig } = nuxt.options.vite
const isIgnored = createIsIgnored(nuxt)
const ctx: ViteBuildContext = { const ctx: ViteBuildContext = {
nuxt, nuxt,
entry, entry,
@ -88,6 +89,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
}, },
}, },
watch: { watch: {
chokidar: { ...nuxt.options.watchers.chokidar, ignored: [isIgnored, /[\\/]node_modules[\\/]/] },
exclude: nuxt.options.ignore, exclude: nuxt.options.ignore,
}, },
}, },
@ -101,7 +103,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
replace({ preventAssignment: true, ...globalThisReplacements }), replace({ preventAssignment: true, ...globalThisReplacements }),
], ],
server: { server: {
watch: { ignored: isIgnored }, watch: { ...nuxt.options.watchers.chokidar, ignored: [isIgnored, /[\\/]node_modules[\\/]/] },
fs: { fs: {
allow: [...new Set(allowDirs)], allow: [...new Set(allowDirs)],
}, },

View File

@ -690,6 +690,9 @@ importers:
c12: c12:
specifier: 2.0.1 specifier: 2.0.1
version: 2.0.1(magicast@0.3.5) version: 2.0.1(magicast@0.3.5)
chokidar:
specifier: 4.0.3
version: 4.0.3
compatx: compatx:
specifier: 0.1.8 specifier: 0.1.8
version: 0.1.8 version: 0.1.8