From e2c7edd4797af7c5e137652b6e72c543d70a2d02 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 26 Jul 2023 11:16:01 +0200 Subject: [PATCH] fix(nuxt): pass (and handle) relative paths in `builder:watch` (#22333) --- packages/nuxt/src/components/module.ts | 18 +++++++++------- packages/nuxt/src/core/builder.ts | 29 +++++++++++++++---------- packages/nuxt/src/core/nuxt.ts | 7 +++--- packages/nuxt/src/imports/module.ts | 16 +++++++------- packages/nuxt/src/pages/module.ts | 30 ++++++++++++++------------ 5 files changed, 56 insertions(+), 44 deletions(-) diff --git a/packages/nuxt/src/components/module.ts b/packages/nuxt/src/components/module.ts index 85e599f8f9..94ecdac476 100644 --- a/packages/nuxt/src/components/module.ts +++ b/packages/nuxt/src/components/module.ts @@ -148,12 +148,14 @@ export default defineNuxtModule({ }) // 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) + nuxt.hook('builder:watch', (event, relativePath) => { + if (!['addDir', 'unlinkDir'].includes(event)) { + return + } - if (isDirChange && componentDirs.some(dir => dir.path === fullPath)) { - console.info(`Directory \`${path}/\` ${event === 'addDir' ? 'created' : 'removed'}`) + const path = resolve(nuxt.options.srcDir, relativePath) + if (componentDirs.some(dir => dir.path === path)) { + console.info(`Directory \`${relativePath}/\` ${event === 'addDir' ? 'created' : 'removed'}`) return nuxt.callHook('restart') } }) @@ -183,12 +185,12 @@ export default defineNuxtModule({ }) // Watch for changes - nuxt.hook('builder:watch', async (event, path) => { + nuxt.hook('builder:watch', async (event, relativePath) => { if (!['add', 'unlink'].includes(event)) { return } - const fPath = resolve(nuxt.options.srcDir, path) - if (componentDirs.find(dir => fPath.startsWith(dir.path))) { + const path = resolve(nuxt.options.srcDir, relativePath) + if (componentDirs.some(dir => path.startsWith(dir.path + '/'))) { await updateTemplates({ filter: template => [ 'components.plugin.mjs', diff --git a/packages/nuxt/src/core/builder.ts b/packages/nuxt/src/core/builder.ts index c6fd13cda3..2ba343d6e5 100644 --- a/packages/nuxt/src/core/builder.ts +++ b/packages/nuxt/src/core/builder.ts @@ -5,7 +5,7 @@ import chokidar from 'chokidar' import { isIgnored, tryResolveModule, useNuxt } from '@nuxt/kit' import { interopDefault } from 'mlly' import { debounce } from 'perfect-debounce' -import { normalize, resolve } from 'pathe' +import { normalize, relative, resolve } from 'pathe' import type { Nuxt } from 'nuxt/schema' import { generateApp as _generateApp, createApp } from './app' @@ -19,12 +19,16 @@ export async function build (nuxt: Nuxt) { if (nuxt.options.dev) { watch(nuxt) - nuxt.hook('builder:watch', async (event, path) => { - if (event !== 'change' && /^(app\.|error\.|plugins\/|middleware\/|layouts\/)/i.test(path)) { - if (path.startsWith('app')) { + nuxt.hook('builder:watch', async (event, relativePath) => { + if (event === 'change') { return } + const path = resolve(nuxt.options.srcDir, relativePath) + const relativePaths = nuxt.options._layers.map(l => relative(l.config.srcDir || l.cwd, path)) + const restartPath = relativePaths.find(relativePath => /^(app\.|error\.|plugins\/|middleware\/|layouts\/)/i.test(relativePath)) + if (restartPath) { + if (restartPath.startsWith('app')) { app.mainComponent = undefined } - if (path.startsWith('error')) { + if (restartPath.startsWith('error')) { app.errorComponent = undefined } await generateApp() @@ -72,7 +76,6 @@ function createWatcher () { const watcher = chokidar.watch(nuxt.options._layers.map(i => i.config.srcDir as string).filter(Boolean), { ...nuxt.options.watchers.chokidar, - cwd: nuxt.options.srcDir, ignoreInitial: true, ignored: [ isIgnored, @@ -80,7 +83,8 @@ function createWatcher () { ] }) - watcher.on('all', (event, path) => nuxt.callHook('builder:watch', event, normalize(path))) + // TODO: consider moving to emit absolute path in 3.8 or 4.0 + watcher.on('all', (event, path) => nuxt.callHook('builder:watch', event, normalize(relative(nuxt.options.srcDir, path)))) nuxt.hook('close', () => watcher?.close()) } @@ -94,7 +98,7 @@ function createGranularWatcher () { let pending = 0 const ignoredDirs = new Set([...nuxt.options.modulesDir, nuxt.options.buildDir]) - const pathsToWatch = nuxt.options._layers.map(layer => layer.config.srcDir).filter(d => d && !isIgnored(d)) + const pathsToWatch = nuxt.options._layers.map(layer => layer.config.srcDir || layer.cwd).filter(d => d && !isIgnored(d)) for (const pattern of nuxt.options.watch) { if (typeof pattern !== 'string') { continue } const path = resolve(nuxt.options.srcDir, pattern) @@ -109,7 +113,8 @@ function createGranularWatcher () { watcher.on('all', (event, path) => { path = normalize(path) if (!pending) { - nuxt.callHook('builder:watch', event, path) + // TODO: consider moving to emit absolute path in 3.8 or 4.0 + nuxt.callHook('builder:watch', event, relative(nuxt.options.srcDir, path)) } if (event === 'unlinkDir' && path in watchers) { watchers[path]?.close() @@ -117,7 +122,8 @@ function createGranularWatcher () { } if (event === 'addDir' && path !== dir && !ignoredDirs.has(path) && !pathsToWatch.includes(path) && !(path in watchers) && !isIgnored(path)) { watchers[path] = chokidar.watch(path, { ...nuxt.options.watchers.chokidar, ignored: [isIgnored] }) - watchers[path].on('all', (event, path) => nuxt.callHook('builder:watch', event, normalize(path))) + // TODO: consider moving to emit absolute path in 3.8 or 4.0 + watchers[path].on('all', (event, p) => nuxt.callHook('builder:watch', event, normalize(relative(nuxt.options.srcDir, p)))) nuxt.hook('close', () => watchers[path]?.close()) } }) @@ -144,7 +150,8 @@ async function createParcelWatcher () { if (err) { return } for (const event of events) { if (isIgnored(event.path)) { continue } - nuxt.callHook('builder:watch', watchEvents[event.type], normalize(event.path)) + // TODO: consider moving to emit absolute path in 3.8 or 4.0 + nuxt.callHook('builder:watch', watchEvents[event.type], normalize(relative(nuxt.options.srcDir, event.path))) } }, { ignore: [ diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index 7073d8099d..9aa9a65dfc 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -1,4 +1,4 @@ -import { join, normalize, relative, resolve } from 'pathe' +import { join, normalize, resolve } from 'pathe' import { createDebugger, createHooks } from 'hookable' import type { LoadNuxtOptions } from '@nuxt/kit' import { addBuildPlugin, addComponent, addPlugin, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, logger, nuxtCtx, resolveAlias, resolveFiles, resolvePath, tryResolveModule, useNitro } from '@nuxt/kit' @@ -180,7 +180,7 @@ async function initNuxt (nuxt: Nuxt) { `${config.dir?.modules || 'modules'}/*/index{${nuxt.options.extensions.join(',')}}` ]) for (const mod of layerModules) { - watchedPaths.add(relative(config.srcDir, mod)) + watchedPaths.add(mod) if (specifiedModules.has(mod)) { continue } specifiedModules.add(mod) modulesToInstall.push(mod) @@ -341,7 +341,8 @@ async function initNuxt (nuxt: Nuxt) { await nuxt.callHook('modules:done') - nuxt.hooks.hook('builder:watch', (event, path) => { + nuxt.hooks.hook('builder:watch', (event, relativePath) => { + const path = resolve(nuxt.options.srcDir, relativePath) // Local module patterns if (watchedPaths.has(path)) { return nuxt.callHook('restart', { hard: true }) diff --git a/packages/nuxt/src/imports/module.ts b/packages/nuxt/src/imports/module.ts index 437671e526..75f47d30b0 100644 --- a/packages/nuxt/src/imports/module.ts +++ b/packages/nuxt/src/imports/module.ts @@ -63,12 +63,12 @@ export default defineNuxtModule>({ 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) + nuxt.hook('builder:watch', (event, relativePath) => { + if (!['addDir', 'unlinkDir'].includes(event)) { return } - if (isDirChange && composablesDirs.includes(fullPath)) { - console.info(`Directory \`${path}/\` ${event === 'addDir' ? 'created' : 'removed'}`) + const path = resolve(nuxt.options.srcDir, relativePath) + if (composablesDirs.includes(path)) { + console.info(`Directory \`${relativePath}/\` ${event === 'addDir' ? 'created' : 'removed'}`) return nuxt.callHook('restart') } }) @@ -119,9 +119,9 @@ export default defineNuxtModule>({ 'imports.d.ts', 'imports.mjs' ] - nuxt.hook('builder:watch', async (_, path) => { - const _resolved = resolve(nuxt.options.srcDir, path) - if (composablesDirs.find(dir => _resolved.startsWith(dir))) { + nuxt.hook('builder:watch', async (_, relativePath) => { + const path = resolve(nuxt.options.srcDir, relativePath) + if (composablesDirs.some(dir => dir === path || path.startsWith(dir + '/'))) { await updateTemplates({ filter: template => templates.includes(template.filename) }) diff --git a/packages/nuxt/src/pages/module.ts b/packages/nuxt/src/pages/module.ts index 78455b57c5..6cd94aded2 100644 --- a/packages/nuxt/src/pages/module.ts +++ b/packages/nuxt/src/pages/module.ts @@ -3,7 +3,6 @@ import { mkdir, readFile } from 'node:fs/promises' import { addComponent, addPlugin, addTemplate, addVitePlugin, addWebpackPlugin, defineNuxtModule, findPath, updateTemplates } from '@nuxt/kit' import { dirname, join, relative, resolve } from 'pathe' import { genImport, genObjectFromRawEntries, genString } from 'knitwork' -import escapeRE from 'escape-string-regexp' import { joinURL } from 'ufo' import type { NuxtApp, NuxtPage } from 'nuxt/schema' import { createRoutesContext } from 'unplugin-vue-router' @@ -52,12 +51,13 @@ export default defineNuxtModule({ // 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') + join(layer.config.srcDir || layer.cwd, 'app/router.options.ts'), + join(layer.config.srcDir || layer.cwd, layer.config.dir?.pages || 'pages') ]) - nuxt.hooks.hook('builder:watch', async (event, path) => { - const fullPath = join(nuxt.options.srcDir, path) - if (restartPaths.some(path => path === fullPath || fullPath.startsWith(path + '/'))) { + + nuxt.hooks.hook('builder:watch', async (event, relativePath) => { + const path = resolve(nuxt.options.srcDir, relativePath) + if (restartPaths.some(p => p === path || path.startsWith(p + '/'))) { const newSetting = await isPagesEnabled() if (nuxt.options.pages !== newSetting) { console.info('Pages', newSetting ? 'enabled' : 'disabled') @@ -174,15 +174,17 @@ export default defineNuxtModule({ }) // Regenerate templates when adding or removing pages - nuxt.hook('builder:watch', async (event, path) => { - const dirs = [ - nuxt.options.dir.pages, - nuxt.options.dir.layouts, - nuxt.options.dir.middleware - ].filter(Boolean) + const updateTemplatePaths = nuxt.options._layers.flatMap(l => [ + join(l.config.srcDir || l.cwd, l.config.dir?.pages || 'pages') + '/', + join(l.config.srcDir || l.cwd, l.config.dir?.layouts || 'layouts') + '/', + join(l.config.srcDir || l.cwd, l.config.dir?.middleware || 'middleware') + '/' + ]) - const pathPattern = new RegExp(`(^|\\/)(${dirs.map(escapeRE).join('|')})/`) - if (event !== 'change' && pathPattern.test(path)) { + nuxt.hook('builder:watch', async (event, relativePath) => { + if (event === 'change') { return } + + const path = resolve(nuxt.options.srcDir, relativePath) + if (updateTemplatePaths.some(dir => path.startsWith(dir))) { await updateTemplates({ filter: template => template.filename === 'routes.mjs' })