-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
feat(kit,nuxt): add getLayerDirectories util and refactor to use it #33098
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
|
WalkthroughAdds a new kit API to compute and cache per-layer directory descriptors (LayerDirectories and getLayerDirectories) and migrates many internal subsystems to use it instead of directly reading nuxt.options._layers or per-layer config shapes. Changes touch kit, nuxt core, builder, nitro, schema, imports, pages, pages utils, vite, template/type generation (including resolveLayerPaths signature change), module installation, ignore handling, tests, schema types (making _layers readonly), and docs. Path computations are centralised, trailing-slash normalisation added, and layer-aware discovery/watch logic updated across the codebase. Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
@nuxt/kit
nuxt
@nuxt/rspack-builder
@nuxt/schema
@nuxt/vite-builder
@nuxt/webpack-builder
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/nuxt/src/core/schema.ts (1)
85-96: Watch the actual schema files (depth=1 misses layers//nuxt.schema.).Chokidar with depth: 1 won’t notice rootDir/layers//nuxt.schema.. Watch explicit globs and dedupe inputs instead.
- const rootDirs = layerDirs.map(layer => layer.rootDir) - const SCHEMA_RE = /(?:^|\/)nuxt.schema.\w+$/ - const watcher = watch(rootDirs, { + const schemaGlobs = [...new Set(layerDirs.flatMap(l => [ + join(l.rootDir, 'nuxt.schema.*'), + join(l.rootDir, 'layers/*/nuxt.schema.*'), + ]))] + const watcher = watch(schemaGlobs, { ...nuxt.options.watchers.chokidar, - depth: 1, ignored: [ - (path, stats) => (stats && !stats.isFile()) || !SCHEMA_RE.test(path), isIgnored, /[\\/]node_modules[\\/]/, ], ignoreInitial: true, })Also applies to: 97-99
packages/nuxt/src/core/nitro.ts (1)
42-45: Make excludePattern Windows-safeThe current regex only matches forward slashes, so on Windows paths with backslashes won’t be excluded, causing excessive scans. Use a [\/]-based pattern.
- const excludePattern = excludePaths.length - ? [new RegExp(`node_modules\\/(?!${excludePaths.join('|')})`)] - : [/node_modules/] + const excludePattern = excludePaths.length + ? [new RegExp(`[\\\\/]node_modules[\\\\/](?!${excludePaths.join('|')})`)] + : [/[\\/]node_modules[\\/]/]
🧹 Nitpick comments (26)
packages/kit/src/index.ts (1)
13-15: Also export the LayerDirectories type for ergonomics.Most consumers will want both the function and its result type.
Apply this diff:
// Layers -export { getLayerDirectories } from './layers' +export { getLayerDirectories } from './layers' +export type { LayerDirectories } from './layers'packages/kit/src/ignore.ts (1)
25-31: Normalisepathnamebefore prefix checks to avoid false negatives with relative paths.Ensures consistent behaviour across platforms and call sites that pass relative paths.
Apply this diff:
- const cwds = getLayerDirectories(nuxt)?.map(layer => layer.rootDir).sort((a, b) => b.length - a.length) - const layer = cwds?.find(cwd => pathname.startsWith(cwd)) - const relativePath = relative(layer ?? nuxt.options.rootDir, pathname) + const absPath = pathname.startsWith('/') ? pathname : resolve(nuxt.options.rootDir, pathname) + const cwds = getLayerDirectories(nuxt)?.map(layer => layer.rootDir).sort((a, b) => b.length - a.length) + const layer = cwds?.find(cwd => absPath.startsWith(cwd)) + const relativePath = relative(layer ?? nuxt.options.rootDir, absPath)packages/kit/src/module/install.ts (1)
29-32: Deduplicate local module dirs and simplify collection.Avoid repeated entries when multiple layers resolve to the same
modulesdir.Apply this diff:
- const localLayerModuleDirs: string[] = [] - for (const l of getLayerDirectories(nuxt)) { - if (!NODE_MODULES_RE.test(l.srcDir)) { - localLayerModuleDirs.push(l.dir.modules) - } - } + const localLayerModuleDirs = [...new Set( + getLayerDirectories(nuxt) + .filter(l => !NODE_MODULES_RE.test(l.srcDir)) + .map(l => l.dir.modules) + )]packages/nuxt/src/pages/utils.ts (1)
50-51: Verify layer precedence when de-duplicating pages across layers.
You sort by relativePath and then uniqueBy keeps the first occurrence. With stable sort this implicitly depends on getLayerDirectories(nuxt) order. Please confirm root-layer pages correctly override upstream layers for duplicate relative paths. If not, prefer “keep last” semantics:- const allRoutes = generateRoutesFromFiles(uniqueBy(scannedFiles, 'relativePath'), { + const deduped = Array.from(new Map(scannedFiles.map(f => [f.relativePath, f])).values()) + const allRoutes = generateRoutesFromFiles(deduped, { shouldUseServerComponents: !!nuxt.options.experimental.componentIslands, })packages/nuxt/src/imports/module.ts (1)
117-117: Normalise paths before priority matching; double-check priority direction.
startsWith comparisons can fail cross‑platform if not normalised. Also confirm that Unimport interprets more-negative priorities as higher precedence as intended.- const priorities = getLayerDirectories(nuxt).map((layer, i) => [layer.srcDir, -i] as const).sort(([a], [b]) => b.length - a.length) + const priorities = getLayerDirectories(nuxt) + .map((layer, i) => [normalize(layer.srcDir), -i] as const) + .sort(([a], [b]) => b.length - a.length)And when assigning:
- i.priority ||= priorities.find(([dir]) => i.from.startsWith(dir))?.[1] + const from = normalize(i.from) + i.priority ||= priorities.find(([dir]) => from.startsWith(dir))?.[1]packages/kit/test/generate-types.spec.ts (1)
118-119: Nice: exercising resolveLayerPaths with LayerDirectories.
Consider adding a quick assertion that getLayerDirectories preserves the expected layer order (root last) to guard precedence-sensitive behaviour elsewhere.packages/nuxt/src/core/builder.ts (1)
105-105: Deduplicate watched srcDirs to avoid redundant watchers.
Not critical, but a Set can prevent duplicate entries.- const watcher = chokidarWatch(getLayerDirectories(nuxt).map(i => i.srcDir), { + const watcher = chokidarWatch([...new Set(getLayerDirectories(nuxt).map(i => i.srcDir))], {packages/vite/src/vite.ts (1)
174-177: Prefer withTrailingSlash for robustness.
String concatenation may miss normalisation on Windows or double slashes.- const delimitedRootDir = nuxt.options.rootDir + '/' + const delimitedRootDir = withTrailingSlash(nuxt.options.rootDir)packages/nuxt/src/core/schema.ts (1)
67-77: Parcel watchignorenegations unsupported – filter in callback instead
ignoreonly accepts positive globs (no!-style negation), soignore: ['!nuxt.schema.*']won’t work. Subscribe on the root (omitignore) and invokeonChangeonly for matching events:- for (const layer of layerDirs) { - const subscription = await subscribe(layer.rootDir, onChange, { - ignore: ['!nuxt.schema.*'], - }) + const SCHEMA_RE = /(?:^|\/)nuxt\.schema\.\w+$/ + for (const layer of layerDirs) { + const subscription = await subscribe(layer.rootDir, (_err, events) => { + if (events.some(e => SCHEMA_RE.test(e.path))) onChange() + }) nuxt.hook('close', () => subscription.unsubscribe()) }packages/nuxt/src/core/app.ts (2)
137-139: Call getLayerDirectories once and derive others from it.Avoid recomputation and guarantee ordering alignment with
_layers.- const layerSrcs = getLayerDirectories(nuxt).map(l => l.srcDir) + const layerDirs = getLayerDirectories(nuxt) + const layerSrcs = layerDirs.map(l => l.srcDir) ... - const layerConfigs = nuxt.options._layers.map(layer => layer.config) - const reversedConfigs = layerConfigs.slice().reverse() - const layerDirs = getLayerDirectories(nuxt) - const reversedLayerDirs = [...layerDirs].reverse() + const layerConfigs = nuxt.options._layers.map(layer => layer.config) + const reversedConfigs = [...layerConfigs].reverse() + const reversedLayerDirs = [...layerDirs].reverse()Also applies to: 154-159
195-205: Keep config/plugins and directory plugins in lockstep.Index-based pairing of
reversedConfigs[i]withreversedLayerDirs[i]assumes identical lengths/order. Add a quick invariant check or guard to prevent subtle misalignment.Example guard:
+ if (reversedConfigs.length !== reversedLayerDirs.length) { + logger.warn('Layer configs and directories length mismatch; plugin resolution order may be off.') + }packages/kit/src/template.ts (2)
171-211: resolveLayerPaths(layer, projectBuildDir): solid refactor; one micro-tweak.Great consolidation. Consider adding
globalDeclarationsfor nested layer roots beyond one level if you intend to support deeper nesting in future.
171-211: Potential public API break (signature change).
resolveLayerPathschanged its signature; if external consumers import it, this is breaking. Consider an overload/deprecation shim to accept the old parameters for one minor cycle.Would you like a backward-compatible overload implementation?
packages/nuxt/src/core/nuxt.ts (4)
162-163: Cache and reuse layerDirs within initNuxtYou compute layerDirs once here; later you recompute via getLayerDirectories in the watcher. Prefer using this cached value for clarity and to avoid redundant calls (even if memoised).
Apply this diff near the watcher to reuse the cached array:
- const layerRelativePaths = new Set(getLayerDirectories(nuxt).map(l => relative(l.srcDir, path))) + const layerRelativePaths = new Set(layerDirs.map(l => relative(l.srcDir, path)))
419-421: Transpile selection for node_modules-backed layers looks good; consider dedupingPushing many paths can introduce duplicates. Optional: dedupe nuxt.options.build.transpile after population to keep config lean.
nuxt.options.build.transpile.push( ...layerDirs.filter(i => i.rootDir.includes('node_modules')).map(i => i.rootDir), ) + // Optional: dedupe + nuxt.options.build.transpile = [...new Set(nuxt.options.build.transpile)]
423-431: Modules dir injection: path prefix check is OK; add trailing-slash normalisation for robustnessThe startsWith check is string-based. Guard against accidental partial matches by ensuring both sides are normalised with a trailing slash.
- const locallyScannedLayersDirs = layerDirs.map(l => join(l.rootDir, 'layers/')) + const locallyScannedLayersDirs = layerDirs.map(l => withTrailingSlash(join(l.rootDir, 'layers'))) for (const layer of layerDirs) { - if (locallyScannedLayersDirs.every(dir => !layer.rootDir.startsWith(dir))) { + const layerRoot = withTrailingSlash(layer.rootDir) + if (locallyScannedLayersDirs.every(dir => !layerRoot.startsWith(dir))) { nuxt.options.modulesDir.push(join(layer.rootDir, 'node_modules')) } }
685-699: Reuse cached layerDirs in watcherSmall nit: use the earlier layerDirs variable to avoid recomputation and keep intent clear.
- const layerRelativePaths = new Set(getLayerDirectories(nuxt).map(l => relative(l.srcDir, path))) + const layerRelativePaths = new Set(layerDirs.map(l => relative(l.srcDir, path)))packages/nuxt/src/core/nitro.ts (3)
158-160: Reuse cached layerDirs instead of recomputingMinor tidy-up to avoid repeated getLayerDirectories calls.
- ...getLayerDirectories(nuxt).map(layer => + ...layerDirs.map(layer => relativeWithDot(nuxt.options.buildDir, join(layer.dir.shared, '**/*.d.ts')), ),
177-181: Reuse cached layerDirs for public assetsAvoid repeated getLayerDirectories calls.
- ...getLayerDirectories(nuxt) - .map(layer => layer.dir.public) + ...layerDirs + .map(layer => layer.dir.public) .filter(dir => existsSync(dir)) .map(dir => ({ dir })),
206-207: Reuse cached layerDirs for externals inlineSame tidy-up here.
- ...getLayerDirectories(nuxt).map(layer => join(layer.srcDir, 'app.config')), + ...layerDirs.map(layer => join(layer.srcDir, 'app.config')),packages/nuxt/src/pages/module.ts (1)
307-312: Template regeneration watch set: good coverage of pages/layouts/middlewareOptional: dedupe directories to avoid redundant startsWith checks when multiple layers point to the same path.
- const updateTemplatePaths = getLayerDirectories(nuxt) - .flatMap(l => [ - l.dir.pages, - l.dir.layouts, - l.dir.middleware, - ]) + const updateTemplatePaths = Array.from(new Set( + getLayerDirectories(nuxt).flatMap(l => [ + l.dir.pages, + l.dir.layouts, + l.dir.middleware, + ]), + ))packages/kit/src/layers.ts (5)
21-21: Add an explicit return type for the public API.This is part of the public kit surface; make the return type explicit to avoid accidental breaking changes due to inference shifts.
-export function getLayerDirectories (nuxt = useNuxt()) { +export function getLayerDirectories (nuxt = useNuxt()): LayerDirectories[] {
52-54: Use a more robust trailing-slash helper (optional).Local regex is fine, but we already rely on
pathe. Consider a shared utility (or at least guard the empty-string case) to avoid subtle edge cases.-function withTrailingSlash (dir: string) { - return dir.replace(/[^/]$/, '$&/') -} +function withTrailingSlash (dir: string) { + return dir.endsWith('/') ? dir : (dir ? `${dir}/` : '/`) +}
19-25: Cache invalidation note (FYI).WeakMap caching is good. If any code mutates layer config at runtime (rare), results may become stale. If that’s possible in dev mode, consider an explicit
invalidateLayerDirectories(layer)or clearing the cache onnuxt.hook('restart', ...).
5-18: Consider including other commonly used directories (optional).Depending on adoption plans,
assetsandcomponentsare frequent lookups. Including them up-front can reduce repeated ad-hoc resolutions in consumers.
37-45: Document rootDir-relative keys indiror lift them out
dir.modules,dir.sharedanddir.publicare all resolved fromrootDir, whereas otherdir.*entries usesrcDir. This asymmetry is intentional and consistently handled across the codebase, but may confuse consumers. Consider either:
- Extracting
modules,shared, andpublicinto dedicated top-level options (e.g.modulesDir,sharedDir,publicDir), or- Adding clear documentation/comments that these specific entries are
rootDir-relative.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (17)
packages/kit/src/ignore.test.ts(1 hunks)packages/kit/src/ignore.ts(2 hunks)packages/kit/src/index.ts(1 hunks)packages/kit/src/layers.ts(1 hunks)packages/kit/src/module/install.ts(2 hunks)packages/kit/src/template.ts(4 hunks)packages/kit/test/generate-types.spec.ts(3 hunks)packages/nuxt/src/core/app.ts(5 hunks)packages/nuxt/src/core/builder.ts(4 hunks)packages/nuxt/src/core/nitro.ts(7 hunks)packages/nuxt/src/core/nuxt.ts(5 hunks)packages/nuxt/src/core/schema.ts(5 hunks)packages/nuxt/src/imports/module.ts(2 hunks)packages/nuxt/src/pages/module.ts(4 hunks)packages/nuxt/src/pages/utils.ts(2 hunks)packages/schema/src/types/config.ts(1 hunks)packages/vite/src/vite.ts(3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
packages/kit/src/index.tspackages/nuxt/src/core/builder.tspackages/nuxt/src/imports/module.tspackages/kit/src/ignore.tspackages/kit/src/ignore.test.tspackages/nuxt/src/core/schema.tspackages/kit/src/module/install.tspackages/nuxt/src/pages/utils.tspackages/vite/src/vite.tspackages/kit/src/layers.tspackages/kit/test/generate-types.spec.tspackages/kit/src/template.tspackages/nuxt/src/core/app.tspackages/nuxt/src/core/nitro.tspackages/schema/src/types/config.tspackages/nuxt/src/core/nuxt.tspackages/nuxt/src/pages/module.ts
**/*.{test,spec}.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Write unit tests for core functionality using
vitest
Files:
packages/kit/src/ignore.test.tspackages/kit/test/generate-types.spec.ts
🧠 Learnings (4)
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.
Applied to files:
packages/kit/src/index.tspackages/nuxt/src/core/builder.tspackages/nuxt/src/imports/module.tspackages/nuxt/src/core/schema.tspackages/kit/src/module/install.tspackages/nuxt/src/pages/utils.tspackages/vite/src/vite.tspackages/kit/test/generate-types.spec.tspackages/kit/src/template.tspackages/nuxt/src/core/app.tspackages/nuxt/src/core/nuxt.tspackages/nuxt/src/pages/module.ts
📚 Learning: 2025-07-18T16:46:07.446Z
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-18T16:46:07.446Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Write unit tests for core functionality using `vitest`
Applied to files:
packages/kit/src/ignore.test.ts
📚 Learning: 2025-07-18T16:46:07.446Z
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-18T16:46:07.446Z
Learning: Applies to **/e2e/**/*.{ts,js} : Write end-to-end tests using Playwright and `nuxt/test-utils`
Applied to files:
packages/kit/src/ignore.test.ts
📚 Learning: 2024-12-12T12:36:34.871Z
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:
```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```
instead of wrapping the import in a computed property or importing the component unconditionally.
Applied to files:
packages/nuxt/src/pages/module.ts
🧬 Code graph analysis (2)
packages/kit/test/generate-types.spec.ts (1)
packages/kit/src/template.ts (1)
resolveLayerPaths(171-211)
packages/nuxt/src/core/app.ts (2)
packages/kit/src/resolve.ts (2)
findPath(68-86)resolveFiles(277-285)packages/nuxt/src/core/utils/names.ts (1)
getNameFromPath(6-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-on, json, lts/-1)
- GitHub Check: release-pkg-pr-new
- GitHub Check: typecheck (windows-latest, bundler)
- GitHub Check: typecheck (ubuntu-latest, bundler)
- GitHub Check: test-benchmark
- GitHub Check: code
🔇 Additional comments (28)
packages/kit/src/ignore.test.ts (1)
2-2: LGTM: Types import adjustment is correct.Switching to
Nuxt/NuxtConfighere aligns with current schema exports.packages/schema/src/types/config.ts (1)
91-91: Confirm no runtime mutations on_layers
- No calls to push/pop/splice/shift/unshift/sort/reverse on this array.
- The only assignment to
_layersis in packages/kit/src/loader/config.ts:110, where the array is initialised.packages/kit/src/ignore.ts (1)
5-5: LGTM: Centralising layer discovery viagetLayerDirectories.This removes private
_layerscoupling.packages/kit/src/module/install.ts (1)
17-17: LGTM: UsinggetLayerDirectorieshere reduces duplication and private API usage.packages/nuxt/src/pages/utils.ts (1)
3-5: Imports update looks correct.
Switch to using getLayerDirectories and related utilities is aligned with the new API.packages/nuxt/src/imports/module.ts (1)
2-2: Adopting getLayerDirectories for imports module.
Good move towards the public API.packages/kit/test/generate-types.spec.ts (2)
8-8: Test import path update is fine.
Using the public entry ensures parity with consumers.
27-27: Layer mock includes rootDir and srcDir as expected.
Matches the new directory-centric API.packages/nuxt/src/core/builder.ts (3)
4-4: Importing getLayerDirectories is appropriate here.
Aligns builder with the new layer API.
27-29: App/error invalidation across layers.
Using relative(layer.srcDir, path) ensures correct detection per layer. Looks good.
250-254: Using getLayerDirectories in resolvePathsToWatch.
Simple and correct; respects ignores.packages/vite/src/vite.ts (2)
5-5: Vite now depends on getLayerDirectories.
Good consistency with the rest of the refactor.
41-41: Allow-listing layer rootDirs is correct.
Ensures external layer files are accessible to Vite.packages/nuxt/src/core/schema.ts (1)
58-59: Adoption of getLayerDirectories is solid.Using
layer.rootDirwithresolver.resolvePath(join(layer.rootDir, 'nuxt.schema'))is correct and improves clarity.Also applies to: 110-112
packages/nuxt/src/core/app.ts (3)
160-173: Layouts discovery across layers looks correct.Using per-layer
dir.layoutsand deriving names relative to that base is the right approach.
177-191: Middleware resolution order and patterns look good.Reverse layering is preserved; patterns cover file and index variants.
221-226: Layered app.config resolution LGTM.Scanning each
srcDirforapp.configmaintains expected override semantics.packages/kit/src/template.ts (2)
19-21: New layer utilities import is appropriate.
232-235: Types include/exclude logic works with LayerDirectories.Conditionals and globs read well and preserve prior behaviour with clearer data flow.
Also applies to: 256-260
packages/nuxt/src/core/nuxt.ts (2)
9-9: Importing getLayerDirectories into core is fineNo issues with adding this import; matches the PR’s objective.
263-270: Nice: per-layer index.d.ts is now discovered via layer.rootDirThis simplifies type discovery and removes cwd assumptions.
packages/nuxt/src/core/nitro.ts (4)
11-11: Import looks correctAdopting getLayerDirectories here aligns Nitro initialisation with the new abstraction.
36-41: Exclude list derivation: capture logic is fineExtracting the tail after node_modules/.pnpm paths is sensible. See follow-up on Windows-safe regex below.
122-123: scanDirs via layer.serverDir: LGTMThis matches the new layer contract and removes per-config coupling.
140-140: appConfigFiles via layer.srcDir: LGTMConsistent with new structure.
packages/nuxt/src/pages/module.ts (3)
3-3: Importing getLayerDirectories here is appropriateBrings pages module in line with the new layer API.
22-22: Type imports look correctSwitch to importing Nuxt and NuxtPage only; consistent with usage below.
88-88: pagesDirs via getLayerDirectories: LGTMThis removes manual per-layer resolution and reads better.
packages/kit/src/layers.ts
Outdated
| const isRoot = layer.config.rootDir === nuxt.options.rootDir | ||
| const config = isRoot ? nuxt.options : (layer.config as NuxtOptions) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Root-layer detection can be brittle due to path normalisation differences.
Comparing config.rootDir with nuxt.options.rootDir can fail with trailing-slash or case/relative differences. Prefer comparing normalised cwd to nuxt.options.rootDir (also normalised).
-import { resolve } from 'pathe'
+import { resolve, normalize } from 'pathe'
@@
- const isRoot = layer.config.rootDir === nuxt.options.rootDir
+ const isRoot = normalize(layer.cwd) === normalize(nuxt.options.rootDir)
const config = isRoot ? nuxt.options : (layer.config as NuxtOptions)Also applies to: 3-3
🤖 Prompt for AI Agents
In packages/kit/src/layers.ts around lines 27 to 29, the root-layer detection
compares layer.config.rootDir to nuxt.options.rootDir which is brittle across
trailing slashes, case or relative paths; change the check to compare a
normalized absolute cwd for the layer (e.g. path.resolve or fs.realpathSync of
layer.config.cwd or rootDir) against a normalized absolute nuxt.options.rootDir
(also resolved/realpathSync), ensuring both values are normalized
(path.resolve/path.normalize or realpath) before equality comparison; apply the
same normalization fix to the other occurrence noted (lines 3-3).
packages/kit/src/layers.ts
Outdated
| const srcDir = withTrailingSlash(config.srcDir || layer.cwd) | ||
| const rootDir = withTrailingSlash(config.rootDir || layer.cwd) | ||
| const directories = { | ||
| srcDir, | ||
| rootDir, | ||
| serverDir: withTrailingSlash(resolve(layer.cwd, config?.serverDir || 'server')), | ||
| dir: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolve rootDir/srcDir relative to the layer root and base serverDir off rootDir.
- Today
srcDir/rootDiraccept relative paths; resolving them vialayer.cwdmakes outcomes stable. serverDirshould be relative torootDir(notlayer.cwd) to respect an overriddenrootDir.
- const srcDir = withTrailingSlash(config.srcDir || layer.cwd)
- const rootDir = withTrailingSlash(config.rootDir || layer.cwd)
+ const rootDir = withTrailingSlash(resolve(layer.cwd, config.rootDir || '.'))
+ const srcDir = withTrailingSlash(resolve(rootDir, config.srcDir || '.'))
@@
- serverDir: withTrailingSlash(resolve(layer.cwd, config?.serverDir || 'server')),
+ serverDir: withTrailingSlash(resolve(rootDir, config.serverDir || 'server')),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const srcDir = withTrailingSlash(config.srcDir || layer.cwd) | |
| const rootDir = withTrailingSlash(config.rootDir || layer.cwd) | |
| const directories = { | |
| srcDir, | |
| rootDir, | |
| serverDir: withTrailingSlash(resolve(layer.cwd, config?.serverDir || 'server')), | |
| dir: { | |
| const rootDir = withTrailingSlash(resolve(layer.cwd, config.rootDir || '.')) | |
| const srcDir = withTrailingSlash(resolve(rootDir, config.srcDir || '.')) | |
| const directories = { | |
| srcDir, | |
| rootDir, | |
| serverDir: withTrailingSlash(resolve(rootDir, config.serverDir || 'server')), | |
| dir: { |
🤖 Prompt for AI Agents
In packages/kit/src/layers.ts around lines 30–36, change how directories are
resolved: first resolve rootDir against layer.cwd (e.g. resolve(layer.cwd,
config.rootDir || '.' ) and then resolve srcDir against that resolved rootDir
(e.g. resolve(resolvedRootDir, config.srcDir || '.') ) so relative paths are
interpreted from the layer root; finally compute serverDir relative to the
resolved rootDir (not layer.cwd) so an overridden rootDir affects serverDir.
Ensure you still wrap results with withTrailingSlash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/nuxt/src/core/app.ts (2)
156-167: Layouts precedence is inverted; later layers (user) should override earlier ones (base).Iterate reversed so first match comes from the user layer and keep it with
||=.Apply this diff:
- for (const layer of layerDirs) { + for (const layer of reversedLayerDirs) { const layoutFiles = await resolveFiles(layer.dir.layouts, `**/*{${extensionGlob}}`) for (const file of layoutFiles) { const name = getNameFromPath(file, layer.dir.layouts) if (!name) { // Ignore files like `~/layouts/index.vue` which end up not having a name at all logger.warn(`No layout name could be resolved for \`${resolveToAlias(file, nuxt)}\`. Bear in mind that \`index\` is ignored for the purpose of creating a layout name.`) continue } layouts[name] ||= { name, file } } }
217-222: Reverseapp.configsbefore merging so user values override – currently we collectlayerDirsbase→user and calldefuFn(cfg0, …, inlineConfig)(leftmost wins), causing base configs to take precedence. Invert the iteration (e.g.for (const layer of [...layerDirs].reverse())) or reverseapp.configsbefore thedefuFncall so user layers are merged first.
🧹 Nitpick comments (1)
packages/nuxt/src/core/app.ts (1)
188-194: Avoid positional coupling between two reversed arrays.Indexing
reversedLayers[i]alongsidereversedLayerDirs[i]is brittle. Build a zipped structure for clarity and safety.Example refactor:
- for (let i = 0; i < reversedLayerDirs.length; i++) { - const config = reversedLayers[i]!.config - const layer = reversedLayerDirs[i]! + const reversed = reversedLayers.map((l, i) => ({ layer: reversedLayerDirs[i]!, config: l.config })) + for (const { layer, config } of reversed) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
packages/kit/src/index.ts(1 hunks)packages/nuxt/src/core/app.ts(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/kit/src/index.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
packages/nuxt/src/core/app.ts
🧠 Learnings (1)
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.
Applied to files:
packages/nuxt/src/core/app.ts
🧬 Code graph analysis (1)
packages/nuxt/src/core/app.ts (3)
packages/kit/src/layers.ts (1)
getLayerDirectories(21-50)packages/kit/src/resolve.ts (2)
findPath(68-86)resolveFiles(277-285)packages/nuxt/src/core/utils/names.ts (1)
getNameFromPath(6-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
- GitHub Check: test-fixtures (windows-latest, built, vite, default, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, webpack, async, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (windows-latest, dev, vite, default, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, rspack, default, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, js, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, rspack, async, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, js, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, json, lts/-1)
- GitHub Check: test-benchmark
- GitHub Check: typecheck (windows-latest, bundler)
🔇 Additional comments (3)
packages/nuxt/src/core/app.ts (3)
4-4: Imports look good.The new kit utilities are correctly imported and used below.
138-141: LGTM: centralised, cached layer directory resolution.Using
getLayerDirectories(nuxt)once and derivingreversedLayerDirsis clean and avoids repeated normalisation.
196-199: Plugin scanning depth is intentional and matches documentation
Code and docs specify only top-level files and immediate subdirectoryindex.*files are auto-registered (no deeper nesting), so the current glob (*{…},*/index{…}) is correct.
packages/nuxt/src/core/app.ts
Outdated
| join(layer.config.srcDir, 'app'), | ||
| ]), | ||
| ) | ||
| app.mainComponent ||= await findPath(layerDirs.flatMap(d => [join(d.srcDir, 'App'), join(d.srcDir, 'app')]),) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix layer precedence for app.vue: user/project layer should win.
Current order checks base layers first. Reverse the search to honour user overrides.
Apply this diff:
- app.mainComponent ||= await findPath(layerDirs.flatMap(d => [join(d.srcDir, 'App'), join(d.srcDir, 'app')]),)
+ app.mainComponent ||= await findPath(reversedLayerDirs.flatMap(d => [join(d.srcDir, 'App'), join(d.srcDir, 'app')]))Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/nuxt/src/core/app.ts around line 143, the app.vue search currently
iterates layerDirs from base → user so base layers win; reverse the search order
so user/project layers take precedence by iterating layerDirs from last → first
(e.g. reverse a copy of layerDirs before flatMap) when building the candidate
paths for findPath, ensuring the user layer entries appear earlier in the list.
packages/nuxt/src/core/app.ts
Outdated
| app.errorComponent ||= (await findPath( | ||
| nuxt.options._layers.map(layer => join(layer.config.srcDir, 'error')), | ||
| )) ?? resolve(nuxt.options.appDir, 'components/nuxt-error-page.vue') | ||
| app.errorComponent ||= (await findPath(layerDirs.map(d => join(d.srcDir, 'error')))) ?? resolve(nuxt.options.appDir, 'components/nuxt-error-page.vue') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer user error component over base layers.
Use reversedLayerDirs so the project’s error.vue takes precedence.
Apply this diff:
- app.errorComponent ||= (await findPath(layerDirs.map(d => join(d.srcDir, 'error')))) ?? resolve(nuxt.options.appDir, 'components/nuxt-error-page.vue')
+ app.errorComponent ||= (await findPath(reversedLayerDirs.map(d => join(d.srcDir, 'error')))) ?? resolve(nuxt.options.appDir, 'components/nuxt-error-page.vue')📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| app.errorComponent ||= (await findPath(layerDirs.map(d => join(d.srcDir, 'error')))) ?? resolve(nuxt.options.appDir, 'components/nuxt-error-page.vue') | |
| app.errorComponent ||= (await findPath(reversedLayerDirs.map(d => join(d.srcDir, 'error')))) ?? resolve(nuxt.options.appDir, 'components/nuxt-error-page.vue') |
🤖 Prompt for AI Agents
In packages/nuxt/src/core/app.ts around line 150, the error component resolution
uses layerDirs.map which gives base layers precedence; replace
layerDirs.map(...) with reversedLayerDirs.map(...) so the project's error.vue
(topmost layer) is preferred over base layers when calling
findPath(join(d.srcDir, 'error')), ensuring reversedLayerDirs is available in
scope or imported as used elsewhere in this file.
packages/nuxt/src/core/app.ts
Outdated
| for (const layer of reversedLayerDirs) { | ||
| const middlewareFiles = await resolveFiles(layer.dir.middleware, [ | ||
| `*{${extensionGlob}}`, | ||
| `*/index{${extensionGlob}}`, | ||
| ]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Middleware precedence regression: “base wins” due to double-reverse.
Scanning with reversedLayerDirs then applying uniqueBy([...middleware].reverse()) keeps the base copy on conflict. Scan in base→user order to preserve “last wins” (user).
Apply this diff:
- for (const layer of reversedLayerDirs) {
+ for (const layer of layerDirs) {
const middlewareFiles = await resolveFiles(layer.dir.middleware, [
`*{${extensionGlob}}`,
`*/index{${extensionGlob}}`,
])
}No changes needed to the de-dupe line at 212; it already implements “last wins” for base→user scans.
Also applies to: 212-212
🤖 Prompt for AI Agents
In packages/nuxt/src/core/app.ts around lines 171 to 175, the loop currently
iterates over reversedLayerDirs which causes middleware scanning to be performed
in reverse and then later deduped with a reverse again, making base middleware
win; change the loop to iterate in the original layer order (e.g., layerDirs or
the non-reversed array) so scanning happens base→user and the existing
uniqueBy([...middleware].reverse()) at line 212 will correctly implement
last-wins (user overrides); do not change the de-dupe line at 212.
CodSpeed Performance ReportMerging #33098 will not alter performanceComparing Summary
|
Or the other way around and move the rest "inside" |
|
would it make sense for srcDir to become app, etc.? export interface LayerDirectories {
/** Nuxt rootDir (`/` by default) */
root: string
/** Nitro source directory (`/server` by default) */
server: string
/** Local modules directory (`/modules` by default) */
modules: string
/** Shared directory (`/shared` by default) */
shared: string
/** Public directory (`/public` by default) */
public: string
/** Nuxt srcDir (`/app/` by default) */
app: string
/** Layouts directory (`/app/layouts` by default) */
appLayouts: string
/** Middleware directory (`/app/middleware` by default) */
appMiddleware: string
/** Pages directory (`/app/pages` by default) */
appPages: string
/** Plugins directory (`/app/plugins` by default) */
appPlugins: string
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (6)
packages/nuxt/src/core/app.ts (3)
150-150: Prefer user error component over base layers.Use reversedLayerDirs so the project’s error.vue takes precedence.
Apply this diff:
-app.errorComponent ||= (await findPath(layerDirs.map(d => join(d.src, 'error')))) ?? resolve(nuxt.options.appDir, 'components/nuxt-error-page.vue') +app.errorComponent ||= (await findPath(reversedLayerDirs.map(d => join(d.src, 'error')))) ?? resolve(nuxt.options.appDir, 'components/nuxt-error-page.vue')
171-175: Middleware precedence regression: double-reverse makes base win.Scan base→user so the existing de-dupe at Line 212 preserves user copies.
Apply this diff:
-for (const dirs of reversedLayerDirs) { +for (const dirs of layerDirs) {
143-143: Fix layer precedence for app.vue: user/project layer should win.Search currently prefers base layers. Reverse the search order.
Apply this diff:
-app.mainComponent ||= await findPath(layerDirs.flatMap(d => [join(d.src, 'App'), join(d.src, 'app')]),) +app.mainComponent ||= await findPath(reversedLayerDirs.flatMap(d => [join(d.src, 'App'), join(d.src, 'app')]))packages/kit/src/layers.ts (3)
36-38: Fix brittle root-layer detection (normalise and compare absolute paths).Comparing raw
config.rootDirstrings can mis-detect the root layer due to relative paths, trailing slashes, or case differences. Resolve againstcwdand normalise both sides before comparing.Apply this diff:
- const isRoot = layer.config.rootDir === nuxt.options.rootDir - const config = isRoot ? nuxt.options : (layer.config as NuxtOptions) + const layerRootAbs = resolve(layer.cwd, layer.config.rootDir || '.') + const nuxtRootAbs = resolve(nuxt.options.rootDir || '.') + const isRoot = normalize(layerRootAbs) === normalize(nuxtRootAbs) + const config = isRoot ? nuxt.options : (layer.config as NuxtOptions)
39-41: ResolverootDirrelative to the layer root and derivesrcDirfrom that.Current code may treat relative
rootDir/srcDirincorrectly and can also keep Windows backslashes insrc/root. Resolve first to stabilise outcomes across platforms.Apply this diff:
- const src = withTrailingSlash(config.srcDir || layer.cwd) - const root = withTrailingSlash(config.rootDir || layer.cwd) + const root = withTrailingSlash(resolve(layer.cwd, config.rootDir || '.')) + const src = withTrailingSlash(resolve(root, config.srcDir || '.'))
1-3: Importnormalizefrom pathe for the root-layer comparison.Apply this diff:
-import { resolve } from 'pathe' +import { resolve, normalize } from 'pathe'
🧹 Nitpick comments (4)
packages/kit/src/template.ts (1)
171-211: Exclude current layer’s server directory from app TS includes, and validate nested layer src defaults.
- nitro globs don’t exclude dirs.server for the current layer; when server sits under src (v3-style), server files can leak into app TS. Add it explicitly.
- Nested layer globs assume app/ as src; custom srcDir in nested layers may be missed.
Apply this diff to exclude the current layer’s server:
export function resolveLayerPaths (dirs: LayerDirectories, projectBuildDir: string) { const relativeRootDir = relativeWithDot(projectBuildDir, dirs.root) const relativeSrcDir = relativeWithDot(projectBuildDir, dirs.src) const relativeModulesDir = relativeWithDot(projectBuildDir, dirs.modules) const relativeSharedDir = relativeWithDot(projectBuildDir, dirs.shared) + const relativeServerDir = relativeWithDot(projectBuildDir, dirs.server) return { nuxt: [ join(relativeSrcDir, '**/*'), join(relativeModulesDir, `*/runtime/**/*`), join(relativeRootDir, `layers/*/app/**/*`), join(relativeRootDir, `layers/*/modules/*/runtime/**/*`), ], nitro: [ + join(relativeServerDir, `**/*`), join(relativeModulesDir, `*/runtime/server/**/*`), join(relativeRootDir, `layers/*/server/**/*`), join(relativeRootDir, `layers/*/modules/*/runtime/server/**/*`), ],If you want to support nested layers with custom srcDir in the globs, we can extend resolveLayerPaths or rely solely on getLayerDirectories in _generateTypes instead of hard-coded app paths.
packages/kit/src/layers.ts (3)
28-35: Cache invalidation for HMR/config changes.WeakMap avoids leaks but cached paths can become stale if a layer’s config mutates in place (e.g. via hooks). Consider clearing cache on config extension or derive a cache key from resolved
root/src.Example addition (outside this hunk) to expose an invalidator:
// near layerMap export function invalidateLayerDirectoriesCache () { layerMap && (layerMap as WeakMap<any, any>).clear?.() }And optionally hook once:
// when you have access to nuxt instance nuxt.hooks.hook('config:extend', () => invalidateLayerDirectoriesCache())
62-64: MakewithTrailingSlashsimpler and robust post-normalisation.After switching to
pathe.resolve, inputs are POSIX-style. A fastendsWithis clearer.Apply this diff:
-function withTrailingSlash (dir: string) { - return dir.replace(/[^/]$/, '$&/') -} +function withTrailingSlash (dir: string) { + return dir.endsWith('/') ? dir : (dir + '/') +}
5-26: Naming shape: consider explicitappvssrcto mirror config discussion.To match the proposed clarity (root/server/modules/shared/public vs app-local), consider adding
appandappLayouts/appMiddleware/appPages/appPlugins(keepsrcas alias for back-compat).Possible type tweak:
export interface LayerDirectories { - readonly src: string + /** App directory (`/app/` by default). Alias: `src` for back-compat. */ + readonly app: string + /** Back-compat alias for `app`. */ + readonly src: string - readonly layouts: string - readonly middleware: string - readonly pages: string - readonly plugins: string + readonly appLayouts: string + readonly appMiddleware: string + readonly appPages: string + readonly appPlugins: string }And map properties accordingly while still populating the old names for transition.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
nuxt.config.ts(1 hunks)packages/kit/src/ignore.ts(2 hunks)packages/kit/src/index.ts(1 hunks)packages/kit/src/layers.ts(1 hunks)packages/kit/src/module/install.ts(2 hunks)packages/kit/src/template.ts(4 hunks)packages/nuxt/src/core/app.ts(5 hunks)packages/nuxt/src/core/builder.ts(4 hunks)packages/nuxt/src/core/nitro.ts(7 hunks)packages/nuxt/src/core/nuxt.ts(5 hunks)packages/nuxt/src/core/schema.ts(5 hunks)packages/nuxt/src/imports/module.ts(2 hunks)packages/nuxt/src/pages/module.ts(4 hunks)packages/nuxt/src/pages/utils.ts(2 hunks)packages/vite/src/vite.ts(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
- packages/nuxt/src/pages/module.ts
- packages/nuxt/src/core/builder.ts
- packages/vite/src/vite.ts
- packages/nuxt/src/core/nitro.ts
- packages/kit/src/index.ts
- packages/nuxt/src/core/schema.ts
- packages/kit/src/module/install.ts
- packages/nuxt/src/pages/utils.ts
- packages/kit/src/ignore.ts
- packages/nuxt/src/imports/module.ts
- packages/nuxt/src/core/nuxt.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
nuxt.config.tspackages/nuxt/src/core/app.tspackages/kit/src/layers.tspackages/kit/src/template.ts
🧠 Learnings (4)
📚 Learning: 2025-07-18T16:46:07.446Z
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-18T16:46:07.446Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js,jsx} : Write unit tests for core functionality using `vitest`
Applied to files:
nuxt.config.ts
📚 Learning: 2025-07-18T16:46:07.446Z
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-18T16:46:07.446Z
Learning: Applies to **/*.vue : Use `<script setup lang="ts">` and the composition API when creating Vue components
Applied to files:
nuxt.config.ts
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.
Applied to files:
packages/nuxt/src/core/app.tspackages/kit/src/template.ts
📚 Learning: 2024-12-12T12:36:34.871Z
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:
```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```
instead of wrapping the import in a computed property or importing the component unconditionally.
Applied to files:
packages/nuxt/src/core/app.tspackages/kit/src/layers.ts
🧬 Code graph analysis (3)
packages/nuxt/src/core/app.ts (3)
packages/kit/src/layers.ts (1)
getLayerDirectories(30-60)packages/kit/src/resolve.ts (2)
findPath(68-86)resolveFiles(277-285)packages/nuxt/src/core/utils/names.ts (1)
getNameFromPath(6-14)
packages/kit/src/layers.ts (1)
packages/schema/src/types/config.ts (2)
NuxtConfigLayer(68-74)NuxtOptions(81-93)
packages/kit/src/template.ts (2)
packages/kit/src/layers.ts (2)
LayerDirectories(5-26)getLayerDirectories(30-60)packages/kit/src/index.ts (2)
LayerDirectories(15-15)getLayerDirectories(14-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: test-fixtures (windows-latest, built, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, js, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, webpack, default, manifest-on, json, lts/-1)
- GitHub Check: release-pkg-pr-new
- GitHub Check: test-size
- GitHub Check: code
🔇 Additional comments (9)
nuxt.config.ts (1)
56-60: TS include for test sources looks good.Including ./test/nuxt via fileURLToPath is fine and will be relativised later during type generation.
packages/nuxt/src/core/app.ts (4)
4-4: Importing getLayerDirectories is appropriate.Good move away from private nuxt.options._layers access.
138-141: Using cached layer directories with a reversed copy is sensible.Establishes a clear basis for precedence decisions.
188-199: Plugins order logic LGTM.Iterating reversedLayerDirs with first-wins de-dupe correctly prioritises user plugins.
217-219: Confirm desired ordering for app.config layering.You collect in base→user order. If downstream merge expects user-last, this is fine; if first-wins is used anywhere, reverse here.
Would you like me to add a small unit test asserting user app.config overrides base?
packages/kit/src/template.ts (3)
19-21: New layer API imports look correct.
232-235: Switch to getLayerDirectories for source dirs is a solid simplification.
257-260: Path selection condition merits a quick sanity check.The guard includes the root layer and external layers; nested in-repo layers are skipped. Confirm this matches the intended include/exclude strategy for type scanning.
I can add tests covering: root layer, nested in-repo layer, external node_modules layer, and a layer with custom srcDir.
packages/kit/src/layers.ts (1)
48-50: Switch base ofserver,modules, andpublictoroot– aligns with v4 semantics; downstream code contains no references to the oldsrc-based directories.
| for (const dirs of layerDirs) { | ||
| const layoutFiles = await resolveFiles(dirs.layouts, `**/*{${extensionGlob}}`) | ||
| for (const file of layoutFiles) { | ||
| const name = getNameFromPath(file, resolve(config.srcDir, layoutDir)) | ||
| const name = getNameFromPath(file, dirs.layouts) | ||
| if (!name) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Layouts precedence: ensure user overrides base.
Scanning base→user combined with ||= makes base win. Either scan user→base or overwrite unconditionally.
Apply one of:
- Reverse the scan (minimal change):
-for (const dirs of layerDirs) {
+for (const dirs of reversedLayerDirs) {- Or keep order and overwrite:
- layouts[name] ||= { name, file }
+ layouts[name] = { name, file }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for (const dirs of layerDirs) { | |
| const layoutFiles = await resolveFiles(dirs.layouts, `**/*{${extensionGlob}}`) | |
| for (const file of layoutFiles) { | |
| const name = getNameFromPath(file, resolve(config.srcDir, layoutDir)) | |
| const name = getNameFromPath(file, dirs.layouts) | |
| if (!name) { | |
| for (const dirs of reversedLayerDirs) { | |
| const layoutFiles = await resolveFiles(dirs.layouts, `**/*{${extensionGlob}}`) | |
| for (const file of layoutFiles) { | |
| const name = getNameFromPath(file, dirs.layouts) | |
| if (!name) { |
🤖 Prompt for AI Agents
In packages/nuxt/src/core/app.ts around lines 156 to 160, the current layout
load loop scans layers in base→user order and uses a conditional assignment that
prevents user layouts from overriding base ones; change behavior so user
overrides win by either reversing the layerDirs iteration (scan user→base) or by
keeping the current iteration order but replace the conditional assignment with
an unconditional overwrite when a layout name is encountered, ensuring later
(user) layers override earlier (base) layers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
packages/nuxt/src/core/nuxt.ts (2)
262-269: Optional: also look for declarations in src as a fallbackSome layers may historically place
index.d.tsundersrcDir. Consider checkingdirs.srcas a fallback for smoother v3→v4 transitions.Proposed change:
- for (const dirs of layerDirs) { - const declaration = join(dirs.root, 'index.d.ts') - if (existsSync(declaration)) { - opts.references.push({ path: declaration }) - opts.nodeReferences.push({ path: declaration }) - opts.sharedReferences.push({ path: declaration }) - } - } + for (const dirs of layerDirs) { + const candidates = [join(dirs.root, 'index.d.ts'), join(dirs.src, 'index.d.ts')] + for (const declaration of candidates) { + if (existsSync(declaration)) { + opts.references.push({ path: declaration }) + opts.nodeReferences.push({ path: declaration }) + opts.sharedReferences.push({ path: declaration }) + break + } + } + }
681-696: Nit: reuse cached layerDirs and normalise string patterns
- Reuse the already computed
layerDirsinstead of re-callinggetLayerDirectories(nuxt); it’s cached, but avoiding the call is cleaner.- Normalise string watch patterns before comparing, to be resilient to path separator differences.
Suggested tweak:
- const layerRelativePaths = new Set(getLayerDirectories(nuxt).map(l => relative(l.src, path))) + const normalizedPath = normalize(path) + const layerRelativePaths = new Set(layerDirs.map(l => relative(l.src, normalizedPath))) for (const pattern of nuxt.options.watch) { if (typeof pattern === 'string') { - // Test (normalized) strings against absolute path and relative path to any layer `srcDir` - if (pattern === path || layerRelativePaths.has(pattern)) { return nuxt.callHook('restart') } + const p = normalize(pattern) + // Test normalised strings against absolute path and relative path to any layer `srcDir` + if (p === normalizedPath || layerRelativePaths.has(p)) { return nuxt.callHook('restart') } continue }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
packages/kit/src/layers.ts(1 hunks)packages/nuxt/src/core/app.ts(5 hunks)packages/nuxt/src/core/nuxt.ts(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/nuxt/src/core/app.ts
- packages/kit/src/layers.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
packages/nuxt/src/core/nuxt.ts
🧠 Learnings (1)
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.
Applied to files:
packages/nuxt/src/core/nuxt.ts
🧬 Code graph analysis (1)
packages/nuxt/src/core/nuxt.ts (1)
packages/kit/src/layers.ts (1)
getLayerDirectories(30-60)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: test-fixtures (windows-latest, built, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, webpack, async, manifest-on, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, default, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, js, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-off, json, lts/-1)
- GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, js, lts/-1)
- GitHub Check: test-benchmark
- GitHub Check: typecheck (windows-latest, bundler)
🔇 Additional comments (3)
packages/nuxt/src/core/nuxt.ts (3)
9-9: LGTM: import of getLayerDirectories and related kit utilitiesThe expanded import set aligns with the new utility usage and the rest of this file. No issues spotted.
160-162: Good: centralised layer directory lookupInitialising
layerDirsviagetLayerDirectories(nuxt)early is sound and matches the PR goal of avoiding direct_layersaccess.
418-419: LGTM: transpile node_modules-backed layersFiltering
layerDirsbyi.root.includes('node_modules')and pushing the roots tobuild.transpileis correct; later normalisation (Line 712) keeps paths consistent across platforms.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (5)
docs/3.api/5.kit/16.layers.md (5)
103-125: Example 1: Prefer layer.app and minor ESM nitUse layer.app (if that’s the final field name), and consider
node:-prefixed built-ins elsewhere for consistency.- const files = await globby('**/*.vue', { - cwd: resolve(layer.src, 'components'), + const files = await globby('**/*.vue', { + cwd: resolve(layer.app, 'components'), absolute: true })
130-147: Example 2: Use layer.app and prefer node:fsAlign with the API and Node ESM import style.
-import { existsSync } from 'fs' +import { existsSync } from 'node:fs' @@ - const configPath = resolve(layer.src, 'my-module.config.ts') + const configPath = resolve(layer.app, 'my-module.config.ts')
155-186: Example 3: Avoid mutating with reverse() and use app fields
- reverse() mutates; prefer a non-mutating approach.
- Update to layer.app and Node ESM imports.
-import { existsSync, readFileSync } from 'fs' +import { existsSync, readFileSync } from 'node:fs' @@ - const configPath = resolve(layer.src, 'my-config.json') + const configPath = resolve(layer.app, 'my-config.json') @@ - for (const layer of layerDirs.reverse()) { // Process from lowest to highest priority - const configPath = resolve(layer.src, 'my-config.json') + for (const layer of [...layerDirs].reverse()) { // Process from lowest to highest priority + const configPath = resolve(layer.app, 'my-config.json')
191-207: Example 4: Use layer.app and prefer node:fs-import { existsSync } from 'fs' +import { existsSync } from 'node:fs' @@ - const layersWithAssets = layerDirs.filter(layer => { - return existsSync(resolve(layer.src, 'assets')) - }) + const layersWithAssets = layerDirs.filter(layer => { + return existsSync(resolve(layer.app, 'assets')) + })
17-37: Add a brief note on call contextClarify that getLayerDirectories should be called within a Nuxt context (e.g., inside defineNuxtModule setup) or with an explicit nuxt instance.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
docs/3.api/5.kit/16.layers.md(1 hunks)packages/kit/src/layers.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/kit/src/layers.ts
🧰 Additional context used
🪛 LanguageTool
docs/3.api/5.kit/16.layers.md
[uncategorized] ~83-~83: Loose punctuation mark.
Context: ...rom base layers. LayerDirectories: An object containing the resolved direc...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~208-~208: Loose punctuation mark.
Context: ...ers with assets directory) } }) ``` ::note The getLayerDirectories` function...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~210-~210: Loose punctuation mark.
Context: ...performance when called multiple times. :: ::note Directory paths returned by th...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~212-~212: Loose punctuation mark.
Context: ...ormance when called multiple times. :: ::note Directory paths returned by this f...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~214-~214: Loose punctuation mark.
Context: ...clude a trailing slash for consistency. ::
(UNLIKELY_OPENING_PUNCTUATION)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: codeql (javascript-typescript)
🔇 Additional comments (4)
docs/3.api/5.kit/16.layers.md (4)
70-82: Layer priority explanation reads clearlyThe ordering and precedence rules are unambiguous and match Nuxt expectations.
209-215: Admonition syntax looks correct for MDCThe ::note blocks and closers (::) are consistent; LanguageTool’s “loose punctuation” warning can be ignored.
41-66: No renaming needed: docs reflect implementation. TheLayerDirectoriesinterface inpackages/kit/src/layers.tsusessrc,layouts,middleware,pagesandplugins, matching the documentation.Likely an incorrect or invalid review comment.
213-215: Trailing slash invariant confirmed — thewithTrailingSlashhelper (used for every directory ingetLayerDirectories) guarantees a trailing slash, so the documentation note is accurate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/nuxt/src/core/schema.ts (1)
72-77: Parcel watcher ignores are incorrect; filter events in callback
@parcel/watcherdoesn’t support negated globs like'!nuxt.schema.*'. As written, it won’t narrow events tonuxt.schema.*, potentially causing excessive rebuilds. Also, the callback should filter by path.Apply:
- for (const dirs of layerDirs) { - const subscription = await subscribe(dirs.root, onChange, { - ignore: ['!nuxt.schema.*'], - }) + for (const dirs of layerDirs) { + const subscription = await subscribe(dirs.root, (_err, events) => { + if (events?.some(e => /(?:^|\/)nuxt\.schema\.\w+$/.test(e.path))) { + onChange() + } + }, { + // keep noise down + ignore: [/[\\/]node_modules[\\/]/], + }) nuxt.hook('close', () => subscription.unsubscribe()) }packages/kit/src/template.ts (1)
171-211: Exclude main server directory from app TS config and type the return.
nitroglobs don’t exclude the current layer’sserverdir (only layer/embedded module patterns), sosrc/server/**/*may leak intotsconfig.appvianuxtincludes. Also, the function lacks an explicit return type, andnodeuses a shallow*.*for local modules while layer modules use a deep**/*(inconsistent).
- Add
relativeServerDirand exclude it innitro.- Provide an explicit return type.
- Make local modules glob deep to match layered modules.
Apply this diff:
-export function resolveLayerPaths (dirs: LayerDirectories, projectBuildDir: string) { +export function resolveLayerPaths (dirs: LayerDirectories, projectBuildDir: string): { + nuxt: string[] + nitro: string[] + node: string[] + shared: string[] + sharedDeclarations: string[] + globalDeclarations: string[] +} { const relativeRootDir = relativeWithDot(projectBuildDir, dirs.root) const relativeSrcDir = relativeWithDot(projectBuildDir, dirs.src) const relativeModulesDir = relativeWithDot(projectBuildDir, dirs.modules) const relativeSharedDir = relativeWithDot(projectBuildDir, dirs.shared) + const relativeServerDir = relativeWithDot(projectBuildDir, dirs.server) return { nuxt: [ join(relativeSrcDir, '**/*'), join(relativeModulesDir, `*/runtime/**/*`), join(relativeRootDir, `layers/*/app/**/*`), join(relativeRootDir, `layers/*/modules/*/runtime/**/*`), ], nitro: [ + join(relativeServerDir, `**/*`), join(relativeModulesDir, `*/runtime/server/**/*`), join(relativeRootDir, `layers/*/server/**/*`), join(relativeRootDir, `layers/*/modules/*/runtime/server/**/*`), ], node: [ - join(relativeModulesDir, `*.*`), + join(relativeModulesDir, `**/*`), join(relativeRootDir, `nuxt.config.*`), join(relativeRootDir, `.config/nuxt.*`), join(relativeRootDir, `layers/*/nuxt.config.*`), join(relativeRootDir, `layers/*/.config/nuxt.*`), join(relativeRootDir, `layers/*/modules/**/*`), ],
♻️ Duplicate comments (3)
packages/nuxt/src/core/nuxt.ts (1)
422-430: Fix local layers detection and root comparison normalisationUsing all layers’
layers/paths can misclassify non-local layers, and comparingdirs.root(trailing slash) tonuxt.options.rootDircan fail. Restrict to the project’s./layers/and normalise with trailing slashes.- const locallyScannedLayersDirs = layerDirs.map(l => join(l.root, 'layers/')) - for (const dirs of layerDirs) { - if (normalize(dirs.root) === normalize(nuxt.options.rootDir)) { - continue - } - if (locallyScannedLayersDirs.every(dir => !dirs.root.startsWith(dir))) { - nuxt.options.modulesDir.push(join(dirs.root, 'node_modules')) - } - } + const locallyScannedLayersDir = withTrailingSlash(join(nuxt.options.rootDir, 'layers')) + for (const dirs of layerDirs) { + // Skip the project root layer + if (withTrailingSlash(dirs.root) === withTrailingSlash(nuxt.options.rootDir)) { + continue + } + // Only add node_modules for layers not under the project's ./layers/* + if (!withTrailingSlash(dirs.root).startsWith(locallyScannedLayersDir)) { + nuxt.options.modulesDir.push(join(dirs.root, 'node_modules')) + } + }Optional: deduplicate afterwards.
nuxt.options.modulesDir = Array.from(new Set(nuxt.options.modulesDir))packages/kit/src/layers.ts (2)
49-51: Make root-layer detection robust (normalisecwdvsrootDir).Comparing
config.rootDirtonuxt.options.rootDircan be brittle; uselayer.cwdnormalised (and compare tonuxt.options.rootDir) to avoid trailing-slash/relative differences.Apply this diff:
- const isRoot = normalize(layer.config.rootDir) === normalize(nuxt.options.rootDir) + const isRoot = normalize(layer.cwd) === normalize(nuxt.options.rootDir)
52-54: ResolverootDir/srcDirrelative to the layer root.Relative
rootDir/srcDirshould be resolved againstlayer.cwd. Also computerootbeforesrcsosrccan be relative toroot.Apply this diff:
- const src = withTrailingSlash(config.srcDir || layer.cwd) - const root = withTrailingSlash(config.rootDir || layer.cwd) + const root = withTrailingSlash(resolve(layer.cwd, config.rootDir || '.')) + const src = withTrailingSlash(resolve(root, config.srcDir || '.'))
🧹 Nitpick comments (4)
docs/3.api/5.kit/16.layers.md (1)
85-97: Document resolution base nuances (v3 vs v4) for directoriesServer/modules/public are resolved relative to
srcDirfor v3 back-compat in code; v4 leans root-relative. Add a note to reduce confusion.| `public` | `string` | The public directory for static assets | | `layouts` | `string` | The layouts directory for Vue layout components | | `middleware` | `string` | The middleware directory for route middleware | | `pages` | `string` | The pages directory for file-based routing | | `plugins` | `string` | The plugins directory for Nuxt plugins | + +> Note +> For Nuxt 3 compatibility, `server`, `modules` and `public` are currently resolved relative to `srcDir`. In Nuxt 4 they are treated as root-relative by default. `getLayerDirectories` resolves paths accordingly for each layer.packages/nuxt/src/core/nuxt.ts (1)
681-683: Optional: avoid recomputing layer dirs on every eventIf layers don’t change during a watch session, reuse
layerDirsfor this handler. Not critical.- const layerRelativePaths = new Set(getLayerDirectories(nuxt).map(l => relative(l.src, path))) + const layerRelativePaths = new Set(layerDirs.map(l => relative(l.src, path)))packages/kit/src/template.ts (1)
256-260: Remove unusedrootGlobor include it intentionally.
rootGlobis computed but never added to any include/exclude set; the conditionalif (path !== rootGlob)will always be true. Either drop the variable and condition or addrootGlobtopaths.nuxt.Apply this diff to simplify:
- if (!dirs.src.startsWith(rootDirWithSlash) || normalize(dirs.root) === normalize(nuxt.options.rootDir) || dirs.src.includes('node_modules')) { - const rootGlob = join(relativeWithDot(nuxt.options.buildDir, dirs.root), '**/*') + if (!dirs.src.startsWith(rootDirWithSlash) || normalize(dirs.root) === normalize(nuxt.options.rootDir) || dirs.src.includes('node_modules')) { const paths = resolveLayerPaths(dirs, nuxt.options.buildDir) for (const path of paths.nuxt) { include.add(path) legacyInclude.add(path) - if (path !== rootGlob) { - nodeExclude.add(path) - } + nodeExclude.add(path) }packages/kit/src/layers.ts (1)
75-77: Reuseufo’swithTrailingSlashinstead of a local helper.The local regex doesn’t handle edge cases (e.g. empty strings, backslashes).
ufois already used elsewhere and is cross-platform.Apply these diffs:
@@ -import type { NuxtConfigLayer, NuxtOptions } from '@nuxt/schema' -import { normalize, resolve } from 'pathe' +import type { NuxtConfigLayer, NuxtOptions } from '@nuxt/schema' +import { normalize, resolve } from 'pathe' +import { withTrailingSlash } from 'ufo' @@ -function withTrailingSlash (dir: string) { - return dir.replace(/[^/]$/, '$&/') -} +// use `ufo`'s withTrailingSlash for consistent behaviour
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
docs/3.api/5.kit/16.layers.md(1 hunks)packages/kit/src/layers.ts(1 hunks)packages/kit/src/template.ts(4 hunks)packages/nuxt/src/core/nuxt.ts(6 hunks)packages/nuxt/src/core/schema.ts(5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
packages/kit/src/layers.tspackages/nuxt/src/core/schema.tspackages/kit/src/template.tspackages/nuxt/src/core/nuxt.ts
🧠 Learnings (2)
📚 Learning: 2024-12-12T12:36:34.871Z
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:
```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```
instead of wrapping the import in a computed property or importing the component unconditionally.
Applied to files:
packages/kit/src/layers.tspackages/nuxt/src/core/nuxt.ts
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.
Applied to files:
packages/nuxt/src/core/schema.tspackages/kit/src/template.tspackages/nuxt/src/core/nuxt.ts
🧬 Code graph analysis (4)
packages/kit/src/layers.ts (1)
packages/schema/src/types/config.ts (2)
NuxtConfigLayer(68-74)NuxtOptions(81-93)
packages/nuxt/src/core/schema.ts (1)
packages/kit/src/layers.ts (1)
getLayerDirectories(43-73)
packages/kit/src/template.ts (2)
packages/kit/src/layers.ts (2)
LayerDirectories(7-28)getLayerDirectories(43-73)packages/kit/src/index.ts (2)
LayerDirectories(15-15)getLayerDirectories(14-14)
packages/nuxt/src/core/nuxt.ts (2)
packages/kit/src/layers.ts (1)
getLayerDirectories(43-73)packages/kit/src/resolve.ts (1)
resolveFiles(277-285)
🪛 LanguageTool
docs/3.api/5.kit/16.layers.md
[uncategorized] ~83-~83: Loose punctuation mark.
Context: ...rom base layers. LayerDirectories: An object containing the resolved direc...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~208-~208: Loose punctuation mark.
Context: ...ers with assets directory) } }) ``` ::note The getLayerDirectories` function...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~210-~210: Loose punctuation mark.
Context: ...performance when called multiple times. :: ::note Directory paths returned by th...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~212-~212: Loose punctuation mark.
Context: ...ormance when called multiple times. :: ::note Directory paths returned by this f...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~214-~214: Loose punctuation mark.
Context: ...clude a trailing slash for consistency. ::
(UNLIKELY_OPENING_PUNCTUATION)
🔇 Additional comments (12)
packages/nuxt/src/core/schema.ts (4)
8-8: LGTM: import changeImporting
getLayerDirectorieshere is appropriate.
58-59: LGTM: use cached layer directoriesGrabbing
layerDirsonce per module setup is fine (Nuxt restarts on layer add/remove).
85-97: LGTM: chokidar watcher targets only nuxt.schema. at layer roots*The
ignoredpredicate withSCHEMA_REkeeps this lightweight.
110-112: LGTM: per-layer schema resolutionResolving
nuxt.schemafrom eachdirs.rootmatches the new layering API.docs/3.api/5.kit/16.layers.md (1)
19-37: Confirm property names match the final public APIExamples use
src/pages/layouts/plugins. The PR discussion mentions a possible rename toapp/appPages/appLayouts/appPlugins. Ensure the docs reflect whatever ships inpackages/kit/src/layers.tsat merge time.Do you want me to update the examples automatically once the final naming is confirmed?
packages/nuxt/src/core/nuxt.ts (4)
9-9: LGTM: importgetLayerDirectoriesConsistent with broader refactor.
160-162: LGTM: computelayerDirsonce in initMatches usage pattern elsewhere.
262-269: LGTM: include per-layerindex.d.tsGood DX; references added across node/shared.
418-420: LGTM: transpile layers under node_modulesNormalised later via
normalize, so OK cross-platform.packages/kit/src/template.ts (2)
19-21: Imports look good.Publicly importing
getLayerDirectoriesand its type keeps usage consistent across kit. No concerns.
232-235: Good switch togetLayerDirectories.Deriving
sourceDirsfrom computed layer dirs de-dupes normalisation and aligns with the new API.packages/kit/src/layers.ts (1)
7-28: Interface shape is clear and useful.Readonly path strings with explicit comments are great for module authors. No issues.
| // Secondly automatically register modules from layer's module directory | ||
| const modulesDir = (config.rootDir === nuxt.options.rootDir ? nuxt.options.dir : config.dir)?.modules || 'modules' | ||
| const layerModules = await resolveFiles(config.srcDir, [ | ||
| `${modulesDir}/*{${nuxt.options.extensions.join(',')}}`, | ||
| `${modulesDir}/*/index{${nuxt.options.extensions.join(',')}}`, | ||
| const modulesDir = resolve(config.srcDir, (config.rootDir === nuxt.options.rootDir ? nuxt.options.dir : config.dir)?.modules || 'modules') | ||
| const layerModules = await resolveFiles(modulesDir, [ | ||
| `*{${nuxt.options.extensions.join(',')}}`, | ||
| `*/index{${nuxt.options.extensions.join(',')}}`, | ||
| ]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Normalise rootDir comparison when resolving layer modules directory
Comparing config.rootDir against nuxt.options.rootDir without normalisation can pick the wrong dir source and resolve the wrong path.
- const modulesDir = resolve(config.srcDir, (config.rootDir === nuxt.options.rootDir ? nuxt.options.dir : config.dir)?.modules || 'modules')
+ const isRootLayer = normalize(config.rootDir) === normalize(nuxt.options.rootDir)
+ const baseDir = (isRootLayer ? nuxt.options.dir : config.dir)?.modules || 'modules'
+ const modulesDir = resolve(config.srcDir, baseDir)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Secondly automatically register modules from layer's module directory | |
| const modulesDir = (config.rootDir === nuxt.options.rootDir ? nuxt.options.dir : config.dir)?.modules || 'modules' | |
| const layerModules = await resolveFiles(config.srcDir, [ | |
| `${modulesDir}/*{${nuxt.options.extensions.join(',')}}`, | |
| `${modulesDir}/*/index{${nuxt.options.extensions.join(',')}}`, | |
| const modulesDir = resolve(config.srcDir, (config.rootDir === nuxt.options.rootDir ? nuxt.options.dir : config.dir)?.modules || 'modules') | |
| const layerModules = await resolveFiles(modulesDir, [ | |
| `*{${nuxt.options.extensions.join(',')}}`, | |
| `*/index{${nuxt.options.extensions.join(',')}}`, | |
| ]) | |
| // Secondly automatically register modules from layer's module directory | |
| const isRootLayer = normalize(config.rootDir) === normalize(nuxt.options.rootDir) | |
| const baseDir = (isRootLayer ? nuxt.options.dir : config.dir)?.modules || 'modules' | |
| const modulesDir = resolve(config.srcDir, baseDir) | |
| const layerModules = await resolveFiles(modulesDir, [ | |
| `*{${nuxt.options.extensions.join(',')}}`, | |
| `*/index{${nuxt.options.extensions.join(',')}}`, | |
| ]) |
🤖 Prompt for AI Agents
In packages/nuxt/src/core/nuxt.ts around lines 921 to 926, the comparison of
config.rootDir to nuxt.options.rootDir is done without normalising paths which
can lead to choosing the wrong dir and resolving an incorrect modules path;
update the comparison to normalise/resolve both paths (e.g. use path.resolve or
path.normalize on both config.rootDir and nuxt.options.rootDir) before
comparing, so the ternary picks the correct dir value, then keep the rest of the
modulesDir resolution unchanged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/kit/src/template.ts (1)
171-211: Exclude each layer’s ownserverdir from app TS includesWithout this, server code from non-root layers can leak into app TS context.
return { nuxt: [ join(relativeSrcDir, '**/*'), join(relativeModulesDir, `*/runtime/**/*`), join(relativeRootDir, `layers/*/app/**/*`), join(relativeRootDir, `layers/*/modules/*/runtime/**/*`), ], nitro: [ + join(relativeSrcDir, `server/**/*`), join(relativeModulesDir, `*/runtime/server/**/*`), join(relativeRootDir, `layers/*/server/**/*`), join(relativeRootDir, `layers/*/modules/*/runtime/server/**/*`), ],
♻️ Duplicate comments (5)
packages/nuxt/src/core/nuxt.ts (2)
422-430: Normalise root comparison and simplify local layers exclusion.
Use a single locally-scanned layers path and normalise with trailing slashes to skip the project root reliably.- const locallyScannedLayersDirs = layerDirs.map(l => join(l.root, 'layers/')) - for (const dirs of layerDirs) { - if (normalize(dirs.root) === normalize(nuxt.options.rootDir)) { - continue - } - if (locallyScannedLayersDirs.every(dir => !dirs.root.startsWith(dir))) { - nuxt.options.modulesDir.push(join(dirs.root, 'node_modules')) - } - } + const locallyScannedLayersDir = join(withTrailingSlash(nuxt.options.rootDir), 'layers/') + for (const dirs of layerDirs) { + if (withTrailingSlash(dirs.root) === withTrailingSlash(nuxt.options.rootDir)) { continue } + if (!dirs.root.startsWith(locallyScannedLayersDir)) { + nuxt.options.modulesDir.push(join(dirs.root, 'node_modules')) + } + }
921-926: Normalise root comparison when resolving modulesDir.
Prevents choosing the wrong base dir.- const modulesDir = resolve(config.srcDir, (config.rootDir === nuxt.options.rootDir ? nuxt.options.dir : config.dir)?.modules || 'modules') + const isRootLayer = normalize(config.rootDir) === normalize(nuxt.options.rootDir) + const baseDir = (isRootLayer ? nuxt.options.dir : config.dir)?.modules || 'modules' + const modulesDir = resolve(config.srcDir, baseDir)packages/kit/src/layers.ts (3)
49-51: Root-layer detection should use cwd (normalised).
Comparing config.rootDir can be brittle; prefer layer.cwd vs nuxt.options.rootDir.- const isRoot = normalize(layer.config.rootDir) === normalize(nuxt.options.rootDir) + const isRoot = normalize(layer.cwd) === normalize(nuxt.options.rootDir)
52-54: Resolve root/src relative to the layer root.
Relative values should be resolved from layer.cwd, and src from the resolved root.- const src = withTrailingSlash(config.srcDir || layer.cwd) - const root = withTrailingSlash(config.rootDir || layer.cwd) + const root = withTrailingSlash(resolve(layer.cwd, config.rootDir || '.')) + const src = withTrailingSlash(resolve(root, config.srcDir || '.'))
55-63: Bug: server should be based on root (Nuxt v4 semantics).
Using src can misplace the server dir when srcDir ≠ rootDir.- // these are resolved relative to root in `@nuxt/schema` for v4+ - // so resolving relative to `src` covers backward compatibility for v3 - server: withTrailingSlash(resolve(src, resolveAlias(config.serverDir || 'server', nuxt.options.alias))), + // In v4, serverDir is resolved from root + server: withTrailingSlash(resolve(root, resolveAlias(config.serverDir || 'server', nuxt.options.alias))),
🧹 Nitpick comments (14)
packages/vite/src/vite.ts (2)
37-41: Normalise paths to avoid duplicate or missed pruning due to trailing slashes.
getLayerDirectories().map(d => d.root)returns paths with a trailing slash, while others (e.g.modulesDir,workspaceDir) may not. The subsequentstartsWithpruning is order- and slash-sensitive and may leave near-duplicates (e.g./avs/a/). Normalise before pruning.let allowDirs = [ nuxt.options.appDir, nuxt.options.workspaceDir, ...nuxt.options.modulesDir, - ...getLayerDirectories(nuxt).map(d => d.root), + ...getLayerDirectories(nuxt).map(d => d.root), ...Object.values(nuxt.apps).flatMap(app => [ ...app.components.map(c => dirname(c.filePath)), ...app.plugins.map(p => dirname(p.src)), ...app.middleware.map(m => dirname(m.path)), ...Object.values(app.layouts || {}).map(l => dirname(l.file)), dirname(nuxt.apps.default!.rootComponent!), dirname(nuxt.apps.default!.errorComponent!), ]), ].filter(d => d && existsSync(d)) + // Ensure consistent comparison (remove trailing slash, normalise separators) + allowDirs = allowDirs.map(d => normalize(d).replace(/\/$/, '')) + for (const dir of allowDirs) { allowDirs = allowDirs.filter(d => !d.startsWith(dir) || d === dir) }Also applies to: 50-55
169-177: Make root/app comparisons robust; drop redundant equality check.
dirs.appis normalised with a trailing slash;nuxt.options.srcDirtypically is not. The equality check can be dropped; thestartsWith(delimitedRootDir)guard already excludes the root layer. Also normalise both sides for cross-platform safety.- const delimitedRootDir = nuxt.options.rootDir + '/' - for (const dirs of getLayerDirectories(nuxt)) { - if (dirs.app !== nuxt.options.srcDir && !dirs.app.startsWith(delimitedRootDir)) { - layerDirs.push(dirs.app) - } - } + const delimitedRootDir = withTrailingSlash(normalize(nuxt.options.rootDir)) + for (const dirs of getLayerDirectories(nuxt)) { + const appDir = withTrailingSlash(normalize(dirs.app)) + if (!appDir.startsWith(delimitedRootDir)) { + layerDirs.push(appDir) + } + }packages/nuxt/src/pages/module.ts (2)
88-89: Consider de-duplicatingpagesDirs.Multiple layers can point to the same
appPagesdirectory (e.g. symlinks). It’s harmless but easy to dedupe.- const pagesDirs = getLayerDirectories(nuxt).map(dirs => dirs.appPages) + const pagesDirs = Array.from(new Set(getLayerDirectories(nuxt).map(dirs => dirs.appPages)))
307-313: Use a Set to avoid redundant checks and tiny perf win.Flattened array may include duplicates across layers. Using a
Setreduces repeatedstartsWithchecks during rebuilds.- const updateTemplatePaths = getLayerDirectories(nuxt) - .flatMap(dirs => [ - dirs.appPages, - dirs.appLayouts, - dirs.appMiddleware, - ]) + const updateTemplatePaths = new Set( + getLayerDirectories(nuxt).flatMap(dirs => [ + dirs.appPages, + dirs.appLayouts, + dirs.appMiddleware, + ]), + )And adjust the usage below:
- if (shouldAlwaysRegenerate || updateTemplatePaths.some(dir => path.startsWith(dir))) { + if (shouldAlwaysRegenerate || Array.from(updateTemplatePaths).some(dir => path.startsWith(dir))) {packages/nuxt/src/core/builder.ts (2)
27-29: Path check is fine; consider normalisation for safety.
relative(dirs.app, path)is robust, but givendirs.apphas a trailing slash, normalising both occasionally avoids edge cases on Windows or unusual separators.- const relativePath = relative(dirs.app, path) + const relativePath = relative(normalize(dirs.app), normalize(path))
248-254: Skip ignored app dirs early and normalise paths.Good to use
isIgnored(dirs.app); add normalisation to avoid platform-specific mismatches and keep the set canonical.- for (const dirs of getLayerDirectories(nuxt)) { - if (!dirs.app || isIgnored(dirs.app)) { continue } - pathsToWatch.add(dirs.app) + for (const dirs of getLayerDirectories(nuxt)) { + const appDir = normalize(dirs.app) + if (!appDir || isIgnored(appDir)) { continue } + pathsToWatch.add(appDir) }packages/nuxt/src/core/nitro.ts (3)
158-159: ReuselayerDirsinstead of recomputingAvoids extra calls (even if cached).
- ...getLayerDirectories(nuxt).map(dirs => relativeWithDot(nuxt.options.buildDir, join(dirs.shared, '**/*.d.ts'))), + ...layerDirs.map(dirs => relativeWithDot(nuxt.options.buildDir, join(dirs.shared, '**/*.d.ts'))),
175-178: ReuselayerDirshere as wellMinor micro-optimisation.
- ...getLayerDirectories(nuxt) - .filter(dirs => existsSync(dirs.public)) - .map(dirs => ({ dir: dirs.public })), + ...layerDirs + .filter(dirs => existsSync(dirs.public)) + .map(dirs => ({ dir: dirs.public })),
203-204: And reuselayerDirshereKeeps things consistent.
- ...getLayerDirectories(nuxt).map(dirs => join(dirs.app, 'app.config')), + ...layerDirs.map(dirs => join(dirs.app, 'app.config')),packages/kit/src/template.ts (1)
255-259: Normalise paths for cross‑platform checks
startsWith/includescan misfire on Windows due to backslashes.- const rootDirWithSlash = withTrailingSlash(nuxt.options.rootDir) + const rootDirWithSlash = withTrailingSlash(normalize(nuxt.options.rootDir)) for (const dirs of layerDirs) { - if (!dirs.app.startsWith(rootDirWithSlash) || normalize(dirs.root) === normalize(nuxt.options.rootDir) || dirs.app.includes('node_modules')) { + const appPosix = normalize(dirs.app) + if (!appPosix.startsWith(rootDirWithSlash) || normalize(dirs.root) === normalize(nuxt.options.rootDir) || appPosix.includes('/node_modules/')) { const rootGlob = join(relativeWithDot(nuxt.options.buildDir, dirs.root), '**/*') const paths = resolveLayerPaths(dirs, nuxt.options.buildDir)packages/nuxt/src/core/nuxt.ts (2)
418-419: Harden node_modules detection to avoid false positives.
Use a normalised path and a bounded match.- ...layerDirs.filter(i => i.root.includes('node_modules')).map(i => i.root), + ...layerDirs + .filter(i => normalize(i.root).includes('/node_modules/')) + .map(i => i.root),
681-687: Avoid recomputing layer dirs in the watch callback.
Reuse the existing layerDirs; compute relatives against those.- const layerRelativePaths = new Set(getLayerDirectories(nuxt).map(l => relative(l.app, path))) + const layerRelativePaths = new Set(layerDirs.map(l => relative(l.app, path)))If desired, precompute once:
const layerApps = layerDirs.map(l => l.app) // … const layerRelativePaths = new Set(layerApps.map(app => relative(app, path)))Also applies to: 690-695
packages/kit/src/layers.ts (2)
61-63: Confirm intended base for modules/public.
PR text says these are root-relative by default; code resolves from src. Please confirm and align.Possible adjustment (if root-relative intended):
- modules: withTrailingSlash(resolve(src, resolveAlias(config.dir?.modules || 'modules', nuxt.options.alias))), - public: withTrailingSlash(resolve(src, resolveAlias(config.dir?.public || 'public', nuxt.options.alias))), + modules: withTrailingSlash(resolve(root, resolveAlias(config.dir?.modules || 'modules', nuxt.options.alias))), + public: withTrailingSlash(resolve(root, resolveAlias(config.dir?.public || 'public', nuxt.options.alias))),
75-77: Prefer shared helper rather than custom withTrailingSlash.
Use ufo.withTrailingSlash for consistency and edge cases.-function withTrailingSlash (dir: string) { - return dir.replace(/[^/]$/, '$&/') -} +// Prefer: import { withTrailingSlash } from 'ufo'Add import:
import { withTrailingSlash } from 'ufo'
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (12)
docs/3.api/5.kit/16.layers.md(1 hunks)packages/kit/src/layers.ts(1 hunks)packages/kit/src/module/install.ts(2 hunks)packages/kit/src/template.ts(4 hunks)packages/nuxt/src/core/app.ts(5 hunks)packages/nuxt/src/core/builder.ts(4 hunks)packages/nuxt/src/core/nitro.ts(7 hunks)packages/nuxt/src/core/nuxt.ts(6 hunks)packages/nuxt/src/imports/module.ts(2 hunks)packages/nuxt/src/pages/module.ts(4 hunks)packages/nuxt/src/pages/utils.ts(2 hunks)packages/vite/src/vite.ts(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/nuxt/src/pages/utils.ts
- packages/kit/src/module/install.ts
- packages/nuxt/src/imports/module.ts
- packages/nuxt/src/core/app.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
packages/nuxt/src/pages/module.tspackages/vite/src/vite.tspackages/nuxt/src/core/builder.tspackages/kit/src/layers.tspackages/nuxt/src/core/nitro.tspackages/kit/src/template.tspackages/nuxt/src/core/nuxt.ts
🧠 Learnings (2)
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.
Applied to files:
packages/nuxt/src/pages/module.tspackages/vite/src/vite.tspackages/nuxt/src/core/builder.tspackages/kit/src/template.tspackages/nuxt/src/core/nuxt.ts
📚 Learning: 2024-12-12T12:36:34.871Z
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:
```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```
instead of wrapping the import in a computed property or importing the component unconditionally.
Applied to files:
packages/nuxt/src/pages/module.tspackages/kit/src/layers.tspackages/nuxt/src/core/nuxt.ts
🧬 Code graph analysis (7)
packages/nuxt/src/pages/module.ts (1)
packages/kit/src/layers.ts (1)
getLayerDirectories(43-73)
packages/vite/src/vite.ts (1)
packages/kit/src/layers.ts (1)
getLayerDirectories(43-73)
packages/nuxt/src/core/builder.ts (2)
packages/kit/src/layers.ts (1)
getLayerDirectories(43-73)packages/kit/src/ignore.ts (1)
isIgnored(14-34)
packages/kit/src/layers.ts (1)
packages/schema/src/types/config.ts (2)
NuxtConfigLayer(68-74)NuxtOptions(81-93)
packages/nuxt/src/core/nitro.ts (1)
packages/kit/src/layers.ts (1)
getLayerDirectories(43-73)
packages/kit/src/template.ts (1)
packages/kit/src/layers.ts (2)
LayerDirectories(7-28)getLayerDirectories(43-73)
packages/nuxt/src/core/nuxt.ts (2)
packages/kit/src/layers.ts (1)
getLayerDirectories(43-73)packages/kit/src/resolve.ts (1)
resolveFiles(277-285)
🪛 LanguageTool
docs/3.api/5.kit/16.layers.md
[uncategorized] ~86-~86: Loose punctuation mark.
Context: ...rom base layers. LayerDirectories: An object containing the resolved direc...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~213-~213: Loose punctuation mark.
Context: ...ers with assets directory) } }) ``` ::note The getLayerDirectories` function...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~215-~215: Loose punctuation mark.
Context: ...performance when called multiple times. :: ::note Directory paths returned by th...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~217-~217: Loose punctuation mark.
Context: ...ormance when called multiple times. :: ::note Directory paths returned by this f...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~219-~219: Loose punctuation mark.
Context: ...clude a trailing slash for consistency. ::
(UNLIKELY_OPENING_PUNCTUATION)
🪛 GitHub Actions: docs
docs/3.api/5.kit/16.layers.md
[error] 1-1: Rejected status code (Not Found) while fetching https://github.com/nuxt/nuxt/blob/main/packages/kit/src/layers.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: code
🔇 Additional comments (16)
packages/vite/src/vite.ts (1)
5-5: Good move to public API.Switching to
getLayerDirectoriesavoids relying on private_layersand centralises normalisation/caching. No issues here.packages/nuxt/src/pages/module.ts (2)
3-3: Consistent usage ofgetLayerDirectories.Importing the helper here aligns with the PR goal and removes ad‑hoc per-layer path resolution. Looks good.
22-22: Type import trim is OK.Dropping
NuxtOptionsfrom imports is consistent with moving logic to@nuxt/kitwhere needed. No action required.packages/nuxt/src/core/builder.ts (1)
105-110: Watching all layer apps via the public API: LGTM.Switching to
getLayerDirectories(nuxt).map(d => d.app)is the right abstraction and matches ignore handling. No further changes needed.docs/3.api/5.kit/16.layers.md (3)
19-37: Usage snippet reads well and matches the final APIProperty names align with the implemented
LayerDirectoriesshape.
41-69: Type definition section looks consistentMatches the exported interface and defaults explained elsewhere.
214-220: Good note on trailing slashes and cachingClear and helpful operational details.
packages/nuxt/src/core/nitro.ts (3)
11-11: ImportinggetLayerDirectorieshere is correctKeeps Nitro initialisation aligned with the new layer API.
122-122: Using per-layer server scan dirs is spot on
scanDirs: layerDirs.map(dirs => dirs.server)matches Nitro’s expectations.
139-140: Correct source for app config filesReads
app.configfrom each layer’sappdir as intended.packages/kit/src/template.ts (2)
19-21: Importing and typing the layer helpers is correctSets up the file to use the new
LayerDirectoriesmodel cleanly.
232-235: Good: switch togetLayerDirectoriesfor source dirsRemoves reliance on private
_layers.packages/nuxt/src/core/nuxt.ts (3)
9-9: Import changes look good.
161-162: Good: centralised layer dirs via getLayerDirectories.
262-269: Correct: discover per-layer declarations from dirs.root.packages/kit/src/layers.ts (1)
43-47: Cache hit path is fine.
| const layerDirs = getLayerDirectories(nuxt) | ||
| const excludePaths = layerDirs.flatMap(dirs => [ | ||
| dirs.root.match(NODE_MODULES_RE)?.[1], | ||
| dirs.root.match(PNPM_NODE_MODULES_RE)?.[1], | ||
| ].filter((dir): dir is string => Boolean(dir)).map(dir => escapeRE(dir))) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Make node_modules exclusion robust across OS and pnpm layouts
Current regex relies on forward slashes and may miss Windows paths. Also, collapse the pnpm and classic cases into one extraction.
- const layerDirs = getLayerDirectories(nuxt)
- const excludePaths = layerDirs.flatMap(dirs => [
- dirs.root.match(NODE_MODULES_RE)?.[1],
- dirs.root.match(PNPM_NODE_MODULES_RE)?.[1],
- ].filter((dir): dir is string => Boolean(dir)).map(dir => escapeRE(dir)))
+ const layerDirs = getLayerDirectories(nuxt)
+ const excludePaths = layerDirs.flatMap((dirs) => {
+ const root = dirs.root.replace(/\\/g, '/')
+ // supports: node_modules/<pkg>, node_modules/.pnpm/<...>/node_modules/<pkg>
+ const m = root.match(/\/node_modules\/(?:(?:\.pnpm\/[^/]+\/)?node_modules\/)?((?:@[^/]+\/)?[^/]+)/)
+ return m?.[1] ? [escapeRE(m[1])] : []
+ })Additionally, adjust the exclude regex to handle both separators:
// outside the hunk above
const nmSegment = String.raw`node_modules[\\/]"
const excludePattern = excludePaths.length
? [new RegExp(`${nmSegment}(?!${excludePaths.join('|')})`)]
: [new RegExp(nmSegment)]There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
packages/nuxt/src/core/nuxt.ts (2)
922-926: Normalise rootDir comparison to resolve correct modules dir (or use directories.modules)Raw string equality can mis-pick the base dir. Prefer a normalised comparison; alternatively, derive directly from the new directories API.
- const modulesDir = resolve(config.srcDir, (config.rootDir === nuxt.options.rootDir ? nuxt.options.dir : config.dir)?.modules || 'modules') + const isRootLayer = normalize(config.rootDir) === normalize(nuxt.options.rootDir) + const baseDir = (isRootLayer ? nuxt.options.dir : config.dir)?.modules || 'modules' + const modulesDir = resolve(config.srcDir, baseDir)
422-430: Fix local layers detection; current logic misclassifies and pollutes modulesDirComparing against every
<layer.root>/layers/never matches; you only need the project’s./layers/. Also normalise the root comparison. Optionally dedupe modulesDir afterwards.- const locallyScannedLayersDirs = layerDirs.map(l => join(l.root, 'layers/')) - for (const dirs of layerDirs) { - if (dirs.root === withTrailingSlash(nuxt.options.rootDir)) { - continue - } - if (locallyScannedLayersDirs.every(dir => !dirs.root.startsWith(dir))) { - nuxt.options.modulesDir.push(join(dirs.root, 'node_modules')) - } - } + const locallyScannedLayersDir = join(nuxt.options.rootDir, 'layers/') + for (const dirs of layerDirs) { + // Skip the project root layer (normalise both sides) + if (withTrailingSlash(dirs.root) === withTrailingSlash(nuxt.options.rootDir)) { + continue + } + // Only add node_modules for layers not under the project's ./layers/* + if (!dirs.root.startsWith(locallyScannedLayersDir)) { + nuxt.options.modulesDir.push(join(dirs.root, 'node_modules')) + } + } + // Optional: dedupe modulesDir + nuxt.options.modulesDir = Array.from(new Set(nuxt.options.modulesDir))
🧹 Nitpick comments (7)
packages/nuxt/src/core/nuxt.ts (4)
19-19: Avoid duplicating withTrailingSlash helpersEither import it from ufo for consistency or keep the local helper but document why it diverges from ufo.
Apply this diff if you prefer reusing ufo:
-import { withoutLeadingSlash } from 'ufo' +import { withoutLeadingSlash, withTrailingSlash } from 'ufo'
418-419: Be segment-aware when detecting node_modules and dedupeincludes('node_modules') can false-match; also avoid duplicate transpile entries.
- ...layerDirs.filter(i => i.root.includes('node_modules')).map(i => i.root), + ...Array.from(new Set( + layerDirs + .filter(i => i.root.includes('/node_modules/')) + .map(i => i.root) + )),
681-683: Reuse precomputed layerDirs to avoid repeated helper callsMinor nit; keeps things consistent.
- const layerRelativePaths = new Set(getLayerDirectories(nuxt).map(l => relative(l.app, path))) + const layerRelativePaths = new Set(layerDirs.map(l => relative(l.app, path)))
990-992: Prefer using shared helper over bespoke withTrailingSlashAvoids subtle divergences and keeps semantics consistent across the codebase.
If you adopt the ufo import above, remove this local helper:
-function withTrailingSlash (dir: string) { - return dir.replace(/[^/]$/, '$&/') -}packages/kit/src/template.ts (2)
254-259: Remove ineffective rootGlob comparison.
paths.nuxtnever includesrootGlob, soif (path !== rootGlob)is always true. Simplify to unconditionally add tonodeExclude(or includerootGlobinpaths.nuxtif that was intended).Apply this diff:
- if (!dirs.app.startsWith(rootDirWithSlash) || dirs.root === rootDirWithSlash || dirs.app.includes('node_modules')) { - const rootGlob = join(relativeWithDot(nuxt.options.buildDir, dirs.root), '**/*') + if (!dirs.app.startsWith(rootDirWithSlash) || dirs.root === rootDirWithSlash || dirs.app.includes('node_modules')) { const paths = resolveLayerPaths(dirs, nuxt.options.buildDir) for (const path of paths.nuxt) { include.add(path) legacyInclude.add(path) - if (path !== rootGlob) { - nodeExclude.add(path) - } + nodeExclude.add(path) }
662-664: Avoid duplicating withTrailingSlash implementations.This local helper diverges easily from other places (e.g.
layers.ts). Consider centralising it (e.g.utils/path.ts) and importing it here and inlayers.ts. If exporting fromlayers.ts, import it here to keep semantics aligned across files.packages/kit/test/generate-types.spec.ts (1)
127-129: Nice: exercising resolveLayerPaths via real Nuxt options.Optional: Add a second case with an external layer (app dir outside
rootDir) to cover the conditional inclusion logic in_generateTypes.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
packages/kit/src/layers.ts(1 hunks)packages/kit/src/template.ts(5 hunks)packages/kit/test/generate-types.spec.ts(4 hunks)packages/nuxt/src/core/nuxt.ts(8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/kit/src/layers.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
packages/nuxt/src/core/nuxt.tspackages/kit/test/generate-types.spec.tspackages/kit/src/template.ts
**/*.{test,spec}.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Write unit tests for core functionality using
vitest
Files:
packages/kit/test/generate-types.spec.ts
🧠 Learnings (2)
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.
Applied to files:
packages/nuxt/src/core/nuxt.tspackages/kit/test/generate-types.spec.tspackages/kit/src/template.ts
📚 Learning: 2024-12-12T12:36:34.871Z
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:
```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```
instead of wrapping the import in a computed property or importing the component unconditionally.
Applied to files:
packages/nuxt/src/core/nuxt.ts
🧬 Code graph analysis (3)
packages/nuxt/src/core/nuxt.ts (2)
packages/kit/src/layers.ts (1)
getLayerDirectories(43-73)packages/kit/src/resolve.ts (1)
resolveFiles(277-285)
packages/kit/test/generate-types.spec.ts (2)
packages/kit/src/layers.ts (1)
getLayerDirectories(43-73)packages/kit/src/template.ts (1)
resolveLayerPaths(170-210)
packages/kit/src/template.ts (1)
packages/kit/src/layers.ts (2)
LayerDirectories(7-28)getLayerDirectories(43-73)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: code
🔇 Additional comments (9)
packages/nuxt/src/core/nuxt.ts (3)
9-9: Good switch to centralised layer dirs via getLayerDirectoriesThis reduces private API usage and keeps path normalisation in one place.
160-162: Initialising layerDirs once is sensibleMemoised in kit; caching here improves readability and avoids scattered calls.
262-263: Including per-layer index.d.ts is correctPulling from dirs.root aligns with the new directories contract.
packages/kit/src/template.ts (3)
18-20: Good migration to layer-based API.Importing
getLayerDirectoriesand theLayerDirectoriestype here clarifies intent and decouples from private_layers.
170-209: resolveLayerPaths: API and glob shape look sound.The new signature is clearer and the returned groups cover nuxt/nitro/node/shared/global declaration needs. No functional concerns.
231-234: Use of getLayerDirectories for sourceDirs is appropriate.This removes direct dependence on
_layersand keeps logic in one place. Nice.packages/kit/test/generate-types.spec.ts (3)
27-27: Mock layer shape aligns with getLayerDirectories expectations.Including
config.rootDirandconfig.srcDiron_layersis sufficient for tests using the layer-based API.
60-69: Updated exclude snapshot matches new glob strategy.The additional nitro/node/layers globs reflect the refactor correctly.
8-8: getLayerDirectories re-export verified.
packages/kit/src/index.tsexportsgetLayerDirectoriesfrom./layers(line 14), so importing it from'nuxt/kit'is valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
packages/nuxt/src/core/nuxt.ts (2)
422-431: Normalise root comparison and avoid O(n²) locals scan for modulesDir augmentationThis reintroduces two issues previously discussed: trailing-slash sensitivity for the root comparison and computing
locallyScannedLayersDirsfor every layer. Use a single projectlayers/path and a normalised root comparison to prevent accidentally adding the project’s ownnode_modulesand to simplify the logic.- const locallyScannedLayersDirs = layerDirs.map(l => join(l.root, 'layers/')) - const rootWithTrailingSlash = withTrailingSlash(nuxt.options.rootDir) - for (const dirs of layerDirs) { - if (dirs.root === rootWithTrailingSlash) { - continue - } - if (locallyScannedLayersDirs.every(dir => !dirs.root.startsWith(dir))) { - nuxt.options.modulesDir.push(join(dirs.root, 'node_modules')) - } - } + const locallyScannedLayersDir = join(nuxt.options.rootDir, 'layers/') + const rootWithTrailingSlash = withTrailingSlash(nuxt.options.rootDir) + for (const dirs of layerDirs) { + // skip project root + if (dirs.root === rootWithTrailingSlash) { continue } + // only add for layers not under ./layers/* + if (!dirs.root.startsWith(withTrailingSlash(locallyScannedLayersDir))) { + nuxt.options.modulesDir.push(join(dirs.root, 'node_modules')) + } + }Optional: deduplicate
modulesDirafterwards if necessary.
923-927: Root detection should be normalised; consider using getLayerDirectories hereThe strict equality on
rootDircan pick the wrong base dir and mis-resolve the layer modules path. At minimum, normalise both sides; ideally, usegetLayerDirectories(nuxt)to readdirs.modulesdirectly.Minimal fix:
- const modulesDir = resolve(config.srcDir, (config.rootDir === nuxt.options.rootDir ? nuxt.options.dir : config.dir)?.modules || 'modules') + const isRootLayer = normalize(withTrailingSlash(config.rootDir)) === normalize(withTrailingSlash(nuxt.options.rootDir)) + const baseDir = (isRootLayer ? nuxt.options.dir : config.dir)?.modules || 'modules' + const modulesDir = resolve(config.srcDir, baseDir)Optional refactor (preferred): map
getLayerDirectories(nuxt)bydirs.rootand usedirs.modulesfor the matching layer to avoid duplicating resolution rules.
🧹 Nitpick comments (3)
packages/nuxt/src/core/nuxt.ts (3)
19-19: Prefer reusing ufo's withTrailingSlash over a local helperYou already depend on
ufo. To avoid subtle divergences, importwithTrailingSlashinstead of maintaining a local variant.-import { withoutLeadingSlash } from 'ufo' +import { withoutLeadingSlash, withTrailingSlash as ufoWithTrailingSlash } from 'ufo'Apply corresponding replacements where
withTrailingSlashis used, and remove the local helper at the end of the file. See also lines 991-993.
418-419: Tighten node_modules detection and simplify trailing-slash dropAvoid false positives from paths that merely contain the substring and prefer a clearer trailing-slash trim.
- ...layerDirs.filter(i => i.root.includes('node_modules')).map(i => i.root.replace(/\/$/, '')), + ...layerDirs + .filter(i => i.root.includes('/node_modules/')) + .map(i => i.root.slice(0, -1)),
991-993: Drop local withTrailingSlash helperUse
ufo’swithTrailingSlashto keep semantics aligned and avoid edge-case drift. See import adjustment on Line 19.-function withTrailingSlash (dir: string) { - return dir.replace(/[^/]$/, '$&/') -} +// Use: ufoWithTrailingSlash imported from 'ufo'
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/nuxt/src/core/nuxt.ts(8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
packages/nuxt/src/core/nuxt.ts
🧠 Learnings (2)
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.
Applied to files:
packages/nuxt/src/core/nuxt.ts
📚 Learning: 2024-12-12T12:36:34.871Z
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:
```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```
instead of wrapping the import in a computed property or importing the component unconditionally.
Applied to files:
packages/nuxt/src/core/nuxt.ts
🧬 Code graph analysis (1)
packages/nuxt/src/core/nuxt.ts (2)
packages/kit/src/layers.ts (1)
getLayerDirectories(43-73)packages/kit/src/resolve.ts (1)
resolveFiles(277-285)
🔇 Additional comments (4)
packages/nuxt/src/core/nuxt.ts (4)
9-9: Good move: centralise on getLayerDirectoriesImporting and using
getLayerDirectoriesacross core is the right direction and will reduce repeated normalisation logic.
160-162: LGTM: cacheable layer directory resolutionCapturing
layerDirsearly viagetLayerDirectories(nuxt)is correct and leverages kit-side caching.
262-269: LGTM: include per-layer root-level index.d.tsSwitching to
dirs.rootfrom layer cwd is consistent with the new API and avoids cwd ambiguity.
682-697: LGTM: watch patterns relative to each layer srcDirComputing
layerRelativePathsagainst every layer’sappdir makesnuxt.options.watchmore robust for multi-layer setups.
🔗 Linked issue
vuejs/pinia#3028 (comment)
📚 Description
This adds a new utility to replace direct access of
nuxt.options._layers(which is private) that will expose key nuxt layer directories for module authors, namely:I've tried to mirror nuxt config options (hencesrcDirvsdir.layouts) but I wonder if it would make sense at least to move modules/shared/public out ofdiras they are relative to the root directory by default. (thoughts welcome @nuxt/core)As a very nice side effect, I've been able to refactor a lot of Nuxt internals that accessed
nuxt.options._layersand I think this PR should nicely improve performance as we were performing the same normalisation steps over and over again and now we can do it once + cache it. Let's see 🤞