mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-26 15:42:09 +00:00
feat(nuxt): add watch
option and refactor dev server restarting (#19530)
This commit is contained in:
parent
eb1bb59542
commit
9036142b14
@ -1,7 +1,6 @@
|
|||||||
import type { AddressInfo } from 'node:net'
|
import type { AddressInfo } from 'node:net'
|
||||||
import type { RequestListener } from 'node:http'
|
import type { RequestListener } from 'node:http'
|
||||||
import { existsSync, readdirSync } from 'node:fs'
|
import { resolve, relative } 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'
|
||||||
import type { Nuxt } from '@nuxt/schema'
|
import type { Nuxt } from '@nuxt/schema'
|
||||||
@ -160,42 +159,11 @@ export default defineNuxtCommand({
|
|||||||
// Watch for config changes
|
// Watch for config changes
|
||||||
// TODO: Watcher service, modules, and requireTree
|
// TODO: Watcher service, modules, and requireTree
|
||||||
const dLoad = debounce(load)
|
const dLoad = debounce(load)
|
||||||
const watcher = chokidar.watch([rootDir], { ignoreInitial: true, depth: 1 })
|
const watcher = chokidar.watch([rootDir], { ignoreInitial: true, depth: 0 })
|
||||||
watcher.on('all', (event, _file) => {
|
watcher.on('all', (_event, _file) => {
|
||||||
if (!currentNuxt) { return }
|
const file = relative(rootDir, _file)
|
||||||
const file = normalize(_file)
|
if (file.match(/^(nuxt\.config\.(js|ts|mjs|cjs)|\.nuxtignore|\.env|\.nuxtrc)$/)) {
|
||||||
const buildDir = withTrailingSlash(normalize(currentNuxt.options.buildDir))
|
dLoad(true, `${file} updated`)
|
||||||
if (file.startsWith(buildDir)) { return }
|
|
||||||
const relativePath = relative(rootDir, file)
|
|
||||||
if (file.match(/(nuxt\.config\.(js|ts|mjs|cjs)|\.nuxtignore|\.env|\.nuxtrc)$/)) {
|
|
||||||
dLoad(true, `${relativePath} updated`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const isDirChange = ['addDir', 'unlinkDir'].includes(event)
|
|
||||||
const isFileChange = ['add', 'unlink'].includes(event)
|
|
||||||
const pagesDir = resolve(currentNuxt.options.srcDir, currentNuxt.options.dir.pages)
|
|
||||||
const reloadDirs = ['components', 'composables', 'utils'].map(d => resolve(currentNuxt.options.srcDir, d))
|
|
||||||
|
|
||||||
if (isDirChange) {
|
|
||||||
if (reloadDirs.includes(file)) {
|
|
||||||
return dLoad(true, `Directory \`${relativePath}/\` ${event === 'addDir' ? 'created' : 'removed'}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFileChange) {
|
|
||||||
if (file.match(/(app|error|app\.config)\.(js|ts|mjs|jsx|tsx|vue)$/)) {
|
|
||||||
return dLoad(true, `\`${relativePath}\` ${event === 'add' ? 'created' : 'removed'}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file.startsWith(pagesDir)) {
|
|
||||||
const hasPages = existsSync(pagesDir) ? readdirSync(pagesDir).length > 0 : false
|
|
||||||
if (currentNuxt && !currentNuxt.options.pages && hasPages) {
|
|
||||||
return dLoad(true, 'Pages enabled')
|
|
||||||
}
|
|
||||||
if (currentNuxt && currentNuxt.options.pages && !hasPages) {
|
|
||||||
return dLoad(true, 'Pages disabled')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -148,6 +148,17 @@ export default defineNuxtModule<ComponentsOptions>({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Restart dev server when component directories are added/removed
|
||||||
|
nuxt.hook('builder:watch', (event, path) => {
|
||||||
|
const isDirChange = ['addDir', 'unlinkDir'].includes(event)
|
||||||
|
const fullPath = resolve(nuxt.options.srcDir, path)
|
||||||
|
|
||||||
|
if (isDirChange && componentDirs.some(dir => dir.path === fullPath)) {
|
||||||
|
console.info(`Directory \`${path}/\` ${event === 'addDir' ? 'created' : 'removed'}`)
|
||||||
|
return nuxt.callHook('restart')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Scan components and add to plugin
|
// Scan components and add to plugin
|
||||||
nuxt.hook('app:templates', async () => {
|
nuxt.hook('app:templates', async () => {
|
||||||
const newComponents = await scanComponents(componentDirs, nuxt.options.srcDir!)
|
const newComponents = await scanComponents(componentDirs, nuxt.options.srcDir!)
|
||||||
|
@ -161,10 +161,6 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
// Register user and then ad-hoc modules
|
// Register user and then ad-hoc modules
|
||||||
modulesToInstall.push(...nuxt.options.modules, ...nuxt.options._modules)
|
modulesToInstall.push(...nuxt.options.modules, ...nuxt.options._modules)
|
||||||
|
|
||||||
nuxt.hooks.hookOnce('builder:watch', (event, path) => {
|
|
||||||
if (watchedPaths.has(path)) { nuxt.callHook('restart', { hard: true }) }
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add <NuxtWelcome>
|
// Add <NuxtWelcome>
|
||||||
addComponent({
|
addComponent({
|
||||||
name: 'NuxtWelcome',
|
name: 'NuxtWelcome',
|
||||||
@ -285,6 +281,29 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
|
|
||||||
await nuxt.callHook('modules:done')
|
await nuxt.callHook('modules:done')
|
||||||
|
|
||||||
|
nuxt.hooks.hook('builder:watch', (event, path) => {
|
||||||
|
// Local module patterns
|
||||||
|
if (watchedPaths.has(path)) {
|
||||||
|
return nuxt.callHook('restart', { hard: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
// User provided patterns
|
||||||
|
for (const pattern of nuxt.options.watch) {
|
||||||
|
if (typeof pattern === 'string') {
|
||||||
|
if (pattern === path) { return nuxt.callHook('restart') }
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (pattern.test(path)) { return nuxt.callHook('restart') }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Core Nuxt files: app.vue, error.vue and app.config.ts
|
||||||
|
const isFileChange = ['add', 'unlink'].includes(event)
|
||||||
|
if (isFileChange && path.match(/^(app|error|app\.config)\.(js|ts|mjs|jsx|tsx|vue)$/i)) {
|
||||||
|
console.info(`\`${path}\` ${event === 'add' ? 'created' : 'removed'}`)
|
||||||
|
return nuxt.callHook('restart')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Normalize windows transpile paths added by modules
|
// Normalize windows transpile paths added by modules
|
||||||
nuxt.options.build.transpile = nuxt.options.build.transpile.map(t => typeof t === 'string' ? normalize(t) : t)
|
nuxt.options.build.transpile = nuxt.options.build.transpile.map(t => typeof t === 'string' ? normalize(t) : t)
|
||||||
|
|
||||||
|
@ -61,6 +61,17 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
|
|||||||
await nuxt.callHook('imports:dirs', composablesDirs)
|
await nuxt.callHook('imports:dirs', composablesDirs)
|
||||||
composablesDirs = composablesDirs.map(dir => normalize(dir))
|
composablesDirs = composablesDirs.map(dir => normalize(dir))
|
||||||
|
|
||||||
|
// Restart nuxt when composable directories are added/removed
|
||||||
|
nuxt.hook('builder:watch', (event, path) => {
|
||||||
|
const isDirChange = ['addDir', 'unlinkDir'].includes(event)
|
||||||
|
const fullPath = resolve(nuxt.options.srcDir, path)
|
||||||
|
|
||||||
|
if (isDirChange && composablesDirs.includes(fullPath)) {
|
||||||
|
console.info(`Directory \`${path}/\` ${event === 'addDir' ? 'created' : 'removed'}`)
|
||||||
|
return nuxt.callHook('restart')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Support for importing from '#imports'
|
// Support for importing from '#imports'
|
||||||
addTemplate({
|
addTemplate({
|
||||||
filename: 'imports.mjs',
|
filename: 'imports.mjs',
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { existsSync, readdirSync } from 'node:fs'
|
import { existsSync, readdirSync } from 'node:fs'
|
||||||
import { defineNuxtModule, addTemplate, addPlugin, addVitePlugin, addWebpackPlugin, findPath, addComponent, updateTemplates } from '@nuxt/kit'
|
import { defineNuxtModule, addTemplate, addPlugin, addVitePlugin, addWebpackPlugin, findPath, addComponent, updateTemplates } from '@nuxt/kit'
|
||||||
import { relative, resolve } from 'pathe'
|
import { join, relative, resolve } from 'pathe'
|
||||||
import { genString, genImport, genObjectFromRawEntries } from 'knitwork'
|
import { genString, genImport, genObjectFromRawEntries } from 'knitwork'
|
||||||
import escapeRE from 'escape-string-regexp'
|
import escapeRE from 'escape-string-regexp'
|
||||||
import { joinURL } from 'ufo'
|
import { joinURL } from 'ufo'
|
||||||
@ -21,9 +21,10 @@ export default defineNuxtModule({
|
|||||||
|
|
||||||
// Disable module (and use universal router) if pages dir do not exists or user has disabled it
|
// Disable module (and use universal router) if pages dir do not exists or user has disabled it
|
||||||
const isNonEmptyDir = (dir: string) => existsSync(dir) && readdirSync(dir).length
|
const isNonEmptyDir = (dir: string) => existsSync(dir) && readdirSync(dir).length
|
||||||
|
const userPreference = nuxt.options.pages
|
||||||
const isPagesEnabled = () => {
|
const isPagesEnabled = () => {
|
||||||
if (typeof nuxt.options.pages === 'boolean') {
|
if (typeof userPreference === 'boolean') {
|
||||||
return nuxt.options.pages
|
return userPreference
|
||||||
}
|
}
|
||||||
if (nuxt.options._layers.some(layer => existsSync(resolve(layer.config.srcDir, 'app/router.options.ts')))) {
|
if (nuxt.options._layers.some(layer => existsSync(resolve(layer.config.srcDir, 'app/router.options.ts')))) {
|
||||||
return true
|
return true
|
||||||
@ -35,6 +36,22 @@ export default defineNuxtModule({
|
|||||||
}
|
}
|
||||||
nuxt.options.pages = isPagesEnabled()
|
nuxt.options.pages = isPagesEnabled()
|
||||||
|
|
||||||
|
// Restart Nuxt when pages dir is added or removed
|
||||||
|
const restartPaths = nuxt.options._layers.flatMap(layer => [
|
||||||
|
join(layer.config.srcDir, 'app/router.options.ts'),
|
||||||
|
join(layer.config.srcDir, layer.config.dir?.pages || 'pages')
|
||||||
|
])
|
||||||
|
nuxt.hooks.hook('builder:watch', (event, path) => {
|
||||||
|
const fullPath = join(nuxt.options.srcDir, path)
|
||||||
|
if (restartPaths.some(path => path === fullPath || fullPath.startsWith(path + '/'))) {
|
||||||
|
const newSetting = isPagesEnabled()
|
||||||
|
if (nuxt.options.pages !== newSetting) {
|
||||||
|
console.info('Pages', newSetting ? 'enabled' : 'disabled')
|
||||||
|
return nuxt.callHook('restart')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if (!nuxt.options.pages) {
|
if (!nuxt.options.pages) {
|
||||||
addPlugin(resolve(distDir, 'app/plugins/router'))
|
addPlugin(resolve(distDir, 'app/plugins/router'))
|
||||||
addTemplate({
|
addTemplate({
|
||||||
|
@ -345,6 +345,18 @@ export default defineUntypedSchema({
|
|||||||
].concat(val).filter(Boolean)
|
].concat(val).filter(Boolean)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The watch property lets you define patterns that will restart the Nuxt dev server when changed.
|
||||||
|
*
|
||||||
|
* It is an array of strings or regular expressions, which will be matched against the file path
|
||||||
|
* relative to the project `srcDir`.
|
||||||
|
*
|
||||||
|
* @type {Array<string | RegExp>}
|
||||||
|
*/
|
||||||
|
watch: {
|
||||||
|
$resolve: val => [].concat(val).filter((b: unknown) => typeof b === 'string' || b instanceof RegExp),
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The watchers property lets you overwrite watchers configuration in your `nuxt.config`.
|
* The watchers property lets you overwrite watchers configuration in your `nuxt.config`.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user