Skip to content

perf!: resolve virtual templates with single plugin#3819

Merged
pi0 merged 7 commits intomainfrom
refactor/build-virtuals
Nov 27, 2025
Merged

perf!: resolve virtual templates with single plugin#3819
pi0 merged 7 commits intomainfrom
refactor/build-virtuals

Conversation

@pi0
Copy link
Copy Markdown
Member

@pi0 pi0 commented Nov 27, 2025

Nitro has several virtual templates which were being provided individually, each as a rollup plugin.

This PR refactors them into build/templates as abstract entries and uses a single VFS plugin that resolves them. Now with filter hooks that also reduce builder hook calls.

Minor breaking change: Type of nitro.vfs change to Map<string, { render }> as this allowed better lazy rendering.

@vercel
Copy link
Copy Markdown

vercel bot commented Nov 27, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
nitro.build Ready Ready Preview Comment Nov 27, 2025 11:26am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Nov 27, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Consolidates many per-feature Rollup plugins into a template-driven virtual VFS: introduces VirtualModule API and virtualTemplates, moves plugin logic into src/build/virtual/*, switches nitro.vfs to a Map of renderable entries, removes legacy plugin files, and fixes polyfills import typos.

Changes

Cohort / File(s) Change Summary
Plugin entry / assembly
src/build/plugins.ts
Replaced explicit per-plugin assembly with a single virtual-based plugin using virtualTemplates; removed the prior inline plugin injection block.
Removed legacy plugin files
src/build/plugins/*.ts
src/build/plugins/database.ts, .../feature-flags.ts, .../runtime-config.ts, .../server-assets.ts, .../storage.ts
Deleted old Rollup plugin implementations; their responsibilities were migrated to virtual template modules.
Virtual plugin implementation
src/build/plugins/virtual.ts
New VirtualModule type, Map-based modules registry, rewritten resolveId / load logic for null-prefixed virtual IDs, exposes api.modules.
Virtual templates aggregator
src/build/virtual/_all.ts
Added virtualTemplates(nitro, _polyfills) that collects built-in and user virtual templates into a single array.
New virtual template modules
src/build/virtual/*
(e.g. database.ts, error-handler.ts, feature-flags.ts, plugins.ts, polyfills.ts, public-assets.ts, renderer-template.ts, routing-meta.ts, routing.ts, runtime-config.ts, server-assets.ts, storage.ts, tasks.ts)
Added default-exported template descriptors returning { id, template, [moduleSideEffects] }; migrated logic from removed plugin files into these template modules.
VFS structure & types
src/types/nitro.ts, src/nitro.ts
nitro.vfs changed from plain object to `Map<string, { render: () => string
Dev VFS tooling
src/dev/vfs.ts
Switched to nitro.vfs.has() / nitro.vfs.get()?.render() and listing from nitro.vfs.keys(); JSON output now includes current rendered content when requested.
Task helper removal
src/task.ts
Removed addNitroTasksVirtualFile helper; task virtual handling moved into new virtual templates.
Type & path mapping updates
src/types/virtual/app-config.d.ts, tsconfig.json
Removed appConfig virtual declaration; updated TS path mapping #nitro-internal-virtual/*./src/build/virtual/types/*.ts.
Public presets / runtimes import fixes
src/presets/**, src/runtime/internal/vite/dev-entry.mjs
Corrected misspelled polyfills import from #nitro-internal-**pollyfills** to #nitro-internal-**polyfills** across many runtime/preset files.
Utility addition
src/utils/regex.ts
Added escapeRegExp(string: string): string.
Tests updated
test/unit/runtime-config.env.test.ts
Updated mock to return explicit empty object for runtimeConfig.
Manifest / package
package.json
Mentioned in manifest analyzer; no functional changes summarized here.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Points to focus:

  • Consumers of nitro.vfs: ensure all usages expect a Map with entries exposing async render() and update call sites accordingly.
  • Behavioral parity between removed plugin implementations and new virtual templates: database connectors, server-assets (production metadata), storage mounts, routing (handlersMeta removal), and tasks.
  • Ordering and moduleSideEffects for polyfills and any side-effectful virtual modules.
  • virtual plugin resolveId/load semantics for Rollup/Vite integration and error/reporting on missing modules.
  • TS path mapping change and removed appConfig declaration effects on IDE/type resolution.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.70% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'perf!: resolve virtual templates with single plugin' follows conventional commits format with 'perf!' prefix and clearly describes the main refactoring change.
Description check ✅ Passed The PR description is directly related to the changeset, explaining the refactoring of virtual templates from individual plugins to a single VFS plugin with supporting details about the breaking change.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3506464 and d429e2e.

📒 Files selected for processing (1)
  • src/build/virtual/database.ts (1 hunks)

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (9)
src/dev/vfs.ts (1)

33-37: Consider handling render errors gracefully.

The VFS API usage is correct. However, if render() throws an exception (e.g., during template generation), the error will propagate as a 500 error with potentially confusing details.

Consider wrapping the render call to provide a clearer error message:

-    const content = id ? await nitro.vfs.get(id)?.render() : undefined;
+    let content: string | undefined;
+    if (id) {
+      try {
+        content = await nitro.vfs.get(id)?.render();
+      } catch (error) {
+        throw new HTTPError({ message: `Failed to render: ${id}`, status: 500 });
+      }
+    }
src/build/virtual/_all.ts (1)

26-40: Consider consistent function signatures.

All template functions are invoked with (nitro, _polyfills) but most only accept nitro as a parameter. While extra arguments are silently ignored in JavaScript, consider updating the individual template function signatures to explicitly include the _polyfills parameter (even if unused) for consistency, or pass it only to the polyfills template that actually requires it.

src/build/virtual/routing.ts (1)

63-65: Consider extracting shared uniqueBy helper.

This uniqueBy function is duplicated in routing-meta.ts. Consider extracting it to a shared utility module to maintain DRY principles.

For example, create src/build/virtual/_utils.ts:

export function uniqueBy<T>(arr: T[], key: keyof T): T[] {
  return [...new Map(arr.map((item) => [item[key], item])).values()];
}
src/build/virtual/server-assets.ts (1)

41-53: Consider parallelizing file I/O for better build performance.

The sequential fsp.readFile and fsp.stat calls inside the nested loop could slow down builds with many assets. Processing files in parallel would improve performance.

       for (const _id of files) {
         const fsPath = resolve(asset.dir, _id);
         const id = asset.baseName + "/" + _id;
-        assets[id] = { fsPath, meta: {} };
-        // @ts-ignore TODO: Use mime@2 types
-        let type = mime.getType(id) || "text/plain";
-        if (type.startsWith("text")) {
-          type += "; charset=utf-8";
-        }
-        const etag = createEtag(await fsp.readFile(fsPath));
-        const mtime = await fsp.stat(fsPath).then((s) => s.mtime.toJSON());
-        assets[id].meta = { type, etag, mtime };
+        assets[id] = { fsPath, meta: {} };
       }
+      
+      // Process file metadata in parallel
+      await Promise.all(
+        Object.entries(assets).map(async ([id, asset]) => {
+          // @ts-ignore TODO: Use mime@2 types
+          let type = mime.getType(id) || "text/plain";
+          if (type.startsWith("text")) {
+            type += "; charset=utf-8";
+          }
+          const etag = createEtag(await fsp.readFile(asset.fsPath));
+          const mtime = await fsp.stat(asset.fsPath).then((s) => s.mtime.toJSON());
+          asset.meta = { type, etag, mtime };
+        })
+      );
src/build/virtual/error-handler.ts (1)

32-35: Variable shadowing: inner error shadows outer error parameter.

In the generated template, the catch(error) on line 32 shadows the error parameter from line 25. While intentional, renaming the caught error improves clarity.

-    } catch(error) {
+    } catch(handlerError) {
       // Handler itself thrown, log and continue
-      console.error(error);
+      console.error(handlerError);
     }
src/build/virtual/tasks.ts (1)

29-45: Consider using JSON.stringify for task names to handle special characters.

Task names are quoted with template literals ("${name}"), but if a task name contains special characters like backslashes or quotes, this could produce invalid JavaScript.

-      ([name, task]) =>
-        `"${name}": {
+      ([name, task]) =>
+        `${JSON.stringify(name)}: {
src/build/virtual/renderer-template.ts (1)

39-48: Variable shadowing: template redeclared within inner scope.

The variable template on Line 40 shadows the outer template from Line 13. While this works correctly, it can cause confusion during maintenance.

Consider renaming the inner variable for clarity:

         } else {
-          const template = compileTemplateToString(html, {
+          const compiledTemplate = compileTemplateToString(html, {
             contextKeys: [...RENDER_CONTEXT_KEYS],
           });
           return /* js */ `
             import { renderToResponse } from 'rendu'
             import { serverFetch } from 'nitro/app'
-            const template = ${template};
+            const template = ${compiledTemplate};
             export const rendererTemplate = (request) => renderToResponse(template, { request, context: { serverFetch } })
             `;
         }
src/build/virtual/public-assets.ts (2)

156-165: Consider fixing indentation for consistency.

The function body has extra indentation (4 spaces) compared to other modules. While not affecting functionality, normalizing to 0 leading spaces would improve consistency.

Apply this diff to normalize indentation:

     {
       id: "#nitro-internal-virtual/public-assets-null",
       template: () => {
         return /* js */ `
-    export function readAsset (id) {
-        return Promise.resolve(null);
-    }`;
+export function readAsset (id) {
+  return Promise.resolve(null);
+}`;
       },
     },

167-181: Consider improving indentation and clarifying guard clause logic.

Two minor consistency improvements:

  1. The function body has extra indentation (2 spaces) compared to other modules
  2. Line 176's logic if (!assets[id].data) { return assets[id].data } is confusing—returning a falsy value rather than an explicit undefined obscures intent

Apply this diff to normalize indentation and clarify the guard clause:

     {
       id: "#nitro-internal-virtual/public-assets-inline",
       template: () => {
         return /* js */ `
-  import assets from '#nitro-internal-virtual/public-assets-data'
-  export function readAsset (id) {
-    if (!assets[id]) { return undefined }
-    if (assets[id]._data) { return assets[id]._data }
-    if (!assets[id].data) { return assets[id].data }
-    assets[id]._data = Uint8Array.from(atob(assets[id].data), (c) => c.charCodeAt(0))
-    return assets[id]._data
+import assets from '#nitro-internal-virtual/public-assets-data'
+export function readAsset (id) {
+  if (!assets[id]) { return undefined }
+  if (assets[id]._data) { return assets[id]._data }
+  if (!assets[id].data) { return undefined }
+  assets[id]._data = Uint8Array.from(atob(assets[id].data), (c) => c.charCodeAt(0))
+  return assets[id]._data
 }`;
       },
     },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dd47a8d and 365d2b2.

📒 Files selected for processing (29)
  • src/build/plugins.ts (4 hunks)
  • src/build/plugins/database.ts (0 hunks)
  • src/build/plugins/feature-flags.ts (0 hunks)
  • src/build/plugins/runtime-config.ts (0 hunks)
  • src/build/plugins/server-assets.ts (0 hunks)
  • src/build/plugins/storage.ts (0 hunks)
  • src/build/plugins/virtual.ts (1 hunks)
  • src/build/virtual/_all.ts (1 hunks)
  • src/build/virtual/database.ts (1 hunks)
  • src/build/virtual/error-handler.ts (2 hunks)
  • src/build/virtual/feature-flags.ts (1 hunks)
  • src/build/virtual/plugins.ts (1 hunks)
  • src/build/virtual/polyfills.ts (1 hunks)
  • src/build/virtual/public-assets.ts (4 hunks)
  • src/build/virtual/renderer-template.ts (1 hunks)
  • src/build/virtual/routing-meta.ts (1 hunks)
  • src/build/virtual/routing.ts (2 hunks)
  • src/build/virtual/runtime-config.ts (1 hunks)
  • src/build/virtual/server-assets.ts (1 hunks)
  • src/build/virtual/storage.ts (1 hunks)
  • src/build/virtual/tasks.ts (1 hunks)
  • src/dev/vfs.ts (2 hunks)
  • src/nitro.ts (0 hunks)
  • src/task.ts (1 hunks)
  • src/types/nitro.ts (1 hunks)
  • src/types/virtual/app-config.d.ts (0 hunks)
  • src/utils/regex.ts (1 hunks)
  • test/unit/runtime-config.env.test.ts (1 hunks)
  • tsconfig.json (1 hunks)
💤 Files with no reviewable changes (7)
  • src/nitro.ts
  • src/build/plugins/runtime-config.ts
  • src/build/plugins/feature-flags.ts
  • src/build/plugins/database.ts
  • src/build/plugins/server-assets.ts
  • src/build/plugins/storage.ts
  • src/types/virtual/app-config.d.ts
🧰 Additional context used
🧬 Code graph analysis (14)
src/build/plugins/virtual.ts (1)
src/utils/regex.ts (1)
  • escapeRegExp (1-3)
src/build/virtual/plugins.ts (2)
src/build/virtual/types/plugins.d.ts (1)
  • plugins (3-3)
src/types/nitro.ts (1)
  • Nitro (16-37)
src/build/virtual/storage.ts (1)
src/types/nitro.ts (1)
  • Nitro (16-37)
src/build/virtual/feature-flags.ts (1)
src/types/nitro.ts (1)
  • Nitro (16-37)
src/build/plugins.ts (4)
src/build/plugins/virtual.ts (1)
  • virtual (11-64)
src/build/virtual/_all.ts (1)
  • virtualTemplates (22-47)
src/build/virtual/plugins.ts (1)
  • plugins (4-24)
src/build/virtual/types/plugins.d.ts (1)
  • plugins (3-3)
src/build/virtual/tasks.ts (2)
src/build/virtual/types/tasks.d.ts (2)
  • tasks (3-6)
  • scheduledTasks (8-8)
src/types/nitro.ts (1)
  • Nitro (16-37)
src/build/virtual/database.ts (1)
src/types/nitro.ts (1)
  • Nitro (16-37)
src/build/virtual/server-assets.ts (2)
src/types/nitro.ts (1)
  • Nitro (16-37)
src/rollup/plugins/server-assets.ts (1)
  • serverAssets (20-60)
src/build/virtual/routing-meta.ts (1)
src/types/nitro.ts (1)
  • Nitro (16-37)
src/build/virtual/renderer-template.ts (1)
src/types/nitro.ts (1)
  • Nitro (16-37)
src/build/virtual/runtime-config.ts (2)
src/build/virtual/types/runtime-config.d.ts (1)
  • runtimeConfig (3-3)
src/types/nitro.ts (1)
  • Nitro (16-37)
src/build/virtual/error-handler.ts (2)
src/types/nitro.ts (1)
  • Nitro (16-37)
src/runtime/meta.ts (1)
  • runtimeDir (10-10)
src/build/virtual/routing.ts (1)
src/types/nitro.ts (1)
  • Nitro (16-37)
src/build/virtual/public-assets.ts (1)
src/types/nitro.ts (1)
  • Nitro (16-37)
🪛 ast-grep (0.40.0)
src/build/plugins/virtual.ts

[warning] 33-35: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
^(${[...modules.keys()].map((id) => escapeRegExp(id)).join("|")})$
)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 48-48: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^${escapeRegExp(PREFIX)})
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🔇 Additional comments (27)
src/utils/regex.ts (1)

1-3: Canonical regex-escaping helper; implementation looks solid.

The metacharacter class and String.raw$&`` replacement follow the standard pattern for safely escaping strings for use in regular expressions. Export and typing are straightforward; no changes needed.

test/unit/runtime-config.env.test.ts (1)

3-5: LGTM!

The mock correctly provides an explicit empty runtimeConfig object, aligning with the updated virtual module system. This ensures the test environment matches the new runtime-config handling where the value is provided directly rather than relying on an imported variable.

src/types/nitro.ts (1)

19-19: Breaking change is well-designed.

The type change from Record<string, string> to Map<string, { render: () => string | Promise<string> }> enables lazy rendering and better aligns with the new VFS plugin architecture. The Map provides efficient has/get/keys operations, and the async render support allows for deferred content generation.

src/dev/vfs.ts (1)

42-56: LGTM!

The JSON response and directory generation correctly use the new Map-based VFS API. The spread operator for keys() iteration and the current field with rendered content are well-implemented.

src/task.ts (1)

3-5: LGTM!

The import cleanup correctly removes unused Nitro type and normalize function after the addNitroTasksVirtualFile function was migrated to the new virtual module system under src/build/virtual/tasks.ts.

tsconfig.json (1)

14-14: Path mapping verification successful — new directory and configuration are correct.

The new path mapping target src/build/virtual/types/ exists and contains 13 type definition files. The old path src/types/virtual/ has been properly removed, and the alias is actively used across 34 files in runtime, presets, and build modules. The tsconfig.json configuration is properly set.

src/build/virtual/runtime-config.ts (1)

1-12: LGTM!

Clean implementation following the new virtual template pattern. The fallback to {} for undefined runtimeConfig is appropriate.

src/build/virtual/plugins.ts (1)

4-24: LGTM!

The implementation correctly handles identifier generation by prefixing with _ and stripping hyphens, ensuring valid JavaScript identifiers regardless of hash output. The Set deduplication is appropriate for preventing duplicate plugin imports.

src/build/virtual/routing.ts (1)

10-60: LGTM!

The refactored routing template correctly aggregates handlers from routes, routed middleware, and global middleware, deduplicates them, and generates appropriate import statements and exports. The lazy handler support with h3.defineLazyEventHandler is properly handled.

src/build/virtual/routing-meta.ts (1)

3-31: LGTM!

The routing metadata template correctly generates per-handler meta imports with the ?meta query suffix and builds the handlersMeta array with route, method, and meta references. The method normalization to lowercase is appropriate for consistent matching.

src/build/virtual/polyfills.ts (1)

6-11: Template logic is clean and correctly handles empty polyfills.

The fallback to a comment when no polyfills are provided is a good practice for debugging.

src/build/virtual/server-assets.ts (2)

69-69: Inlining normalizeKey.toString() is fragile.

If unstorage changes the normalizeKey implementation to rely on closures or external dependencies, this approach will break silently at runtime.

Consider importing normalizeKey directly in the generated code instead:

-const normalizeKey = ${normalizeKey.toString()}
+import { normalizeKey } from 'unstorage'

This would require adding the import to the generated template string at line 31.


21-31: Dev mode template looks correct.

The dev/prerender path correctly mounts filesystem drivers for each configured asset, enabling hot-reload during development.

src/build/virtual/error-handler.ts (1)

5-41: Refactored structure aligns with the new virtual module pattern.

The conversion from a direct virtual(...) wrapper to returning { id, template } is consistent with the PR's architectural changes.

src/build/virtual/storage.ts (1)

30-48: Template generation logic is well-structured.

The use of genImport and genSafeVariableName from knitwork ensures safe code generation. The assets mount and storage initialization follow the expected pattern.

src/build/virtual/database.ts (2)

23-27: Good validation of connector names before code generation.

Throwing early with a clear error message when an invalid connector is specified helps developers debug configuration issues.


9-11: Clean early return for disabled feature.

Returning an empty connectionConfigs object when the experimental database feature is disabled is the right approach.

src/build/plugins/virtual.ts (2)

30-45: Static analysis false positive for ReDoS - the regex is safely constructed.

The static analysis warning about ReDoS (Lines 33-35) is a false positive. The escapeRegExp utility properly escapes all regex metacharacters, and the module IDs come from a controlled set of internal virtual templates. The resulting pattern is a simple alternation of literal strings, which is O(n) and not vulnerable to catastrophic backtracking.


46-62: LGTM! Clean load handler implementation.

The load handler correctly strips the prefix, looks up the module, and awaits the async render. Proper error handling for missing modules ensures clear failure modes.

src/build/plugins.ts (2)

18-50: LGTM! Clean consolidation of virtual module handling.

The refactoring consolidates previously scattered virtual module handling into a single virtualPlugin, which aligns with the PR objectives. The plugin ordering appears correct, with virtual templates resolved after raw imports but before the replace plugin.


45-50: I'll verify the type compatibility concern raised in this review comment. Let me start by examining the relevant files and type definitions.
<function_calls>

#!/bin/bash

Find and examine the virtual template configuration and type definitions

First, find the virtual.ts file referenced

fd -t f "virtual" --extension ts | head -20


</function_calls>
<function_calls>

#!/bin/bash

Search for the virtualTemplates function and virtual plugin definition

rg -n "virtualTemplates|virtualPlugin" src/build/plugins.ts -A 5 -B 2


</function_calls>
<function_calls>

#!/bin/bash

Find where virtualTemplates is imported from

rg -n "import.*virtualTemplates" src/build/plugins.ts


</function_calls>
<function_calls>

#!/bin/bash

Look for the virtual module definition

fd -path "build/virtual" -type f -name "*.ts" | head -20


</function_calls>
<function_calls>

#!/bin/bash

Check the structure of src/build/plugins.ts around line 45-50

sed -n '40,55p' src/build/plugins.ts


</function_calls>
<function_calls>

#!/bin/bash

Check the type definition of nitro.vfs in types file

rg -n "vfs" src/types/nitro.ts -B 2 -A 2


</function_calls>
<function_calls>

#!/bin/bash

Look for the virtual plugin API structure - search for where api.modules is defined

rg -n "api.modules|virtualPlugin.api" src/build/ -B 3 -A 3


</function_calls>
<function_calls>

#!/bin/bash

Find the virtual function/module definition

fd -type f -name "_all.ts" -o -name "virtual.ts" | xargs ls -la 2>/dev/null | head -10


</function_calls>
<function_calls>

#!/bin/bash

Look for files in virtual directory

find src/build/virtual -type f -name "*.ts" 2>/dev/null


</function_calls>
<function_calls>

#!/bin/bash

Check the virtual plugin implementation

find . -path "/build/virtual" -name "*.ts" -type f | head -20


</function_calls>
<function_calls>

#!/bin/bash

Get the full plugins.ts file to understand context

wc -l src/build/plugins.ts


</function_calls>
<function_calls>

#!/bin/bash

Read the plugins.ts file to see imports and the full context

head -50 src/build/plugins.ts


</function_calls>
<function_calls>

#!/bin/bash

Search for where virtual function comes from

rg -n "from.*virtual|import.*virtual" src/build/plugins.ts


</function_calls>
<function_calls>

#!/bin/bash

Look at the build directory structure

ls -la src/build/


</function_calls>
<function_calls>

#!/bin/bash

Search for the virtual function definition anywhere in the codebase

rg -n "^export.virtual|^function virtual|virtual.=.*function" --type ts | head -30


</function_calls>
<function_calls>

#!/bin/bash

Look for nitro.vfs type definition

rg -n "interface Nitro|type Nitro" src/types/ -A 50 | head -100


</function_calls>
<function_calls>

#!/bin/bash

Search for vfs property specifically in Nitro type

rg -n "vfs\s*[:?]" src/types/nitro.ts -B 2 -A 2


</function_calls>

Now let me check what the actual structure of the plugins.ts file is, including all imports:
<function_calls>

#!/bin/bash

Read full plugins.ts to understand it completely

cat -n src/build/plugins.ts


</function_calls>

Let me check the types file as well:
<function_calls>

#!/bin/bash

Search for Nitro interface in types

cat -n src/types/nitro.ts | head -100


</function_calls>

Let me search more broadly for the virtual module and vfs definition:
<function_calls>

#!/bin/bash

Search more flexibly for vfs

rg -n "vfs" src/types/ --type ts -B 3 -A 3


</function_calls>
<function_calls>

#!/bin/bash

Look for how virtualTemplates and virtual are used

rg -n "virtual(" src/build/plugins.ts -B 5 -A 10


</function_calls>

Let me check if there's package information or look at the structure differently:
<function_calls>

#!/bin/bash

List files in src/types

ls -la src/types/


</function_calls>
<function_calls>

#!/bin/bash

Look at all type files

find src/types -name "*.ts" -type f


</function_calls>
<function_calls>

#!/bin/bash

Search entire codebase for vfs definition

rg -n "^\svfs\s[:?]" --type ts


</function_calls>
<function_calls>

#!/bin/bash

Search for VirtualModule type

rg -n "VirtualModule|type.*Module.*render" --type ts | head -30


</function_calls>

Let me get a comprehensive view of all TypeScript files in the repository to understand structure:
<function_calls>

#!/bin/bash

Find all main type/interface files

fd -e ts -type f src/ | grep -E "(types|interface)" | head -20


</function_calls>
<function_calls>

#!/bin/bash

Get a high-level view of src structure

find src -maxdepth 2 -type d | sort


</function_calls>

Now let me search for the actual usage pattern more directly:
<function_calls>

#!/bin/bash

Search for nitro.vfs assignments or references

rg -n "nitro.vfs\s*=" --type ts -B 3 -A 3


</function_calls>
<function_calls>

#!/bin/bash

Try to understand the virtual plugin package being used

rg -n "virtual|rollup" src/build/plugins.ts | head -20


</function_calls>

Let me examine the package.json to understand external dependencies:
<function_calls>

#!/bin/bash

Check package.json for relevant packages

cat package.json | grep -A 20 -B 2 "virtual|rollup"


</function_calls>
<function_calls>

#!/bin/bash

Let me read the entire plugins.ts more carefully with context

sed -n '1,100p' src/build/plugins.ts | cat -n


</function_calls>

Let me check if there's a compiled version or build output that might give us hints:
<function_calls>

#!/bin/bash

Search for all references to .api.modules

rg -n ".api.modules" --type ts -B 5 -A 3


</function_calls>

Let me search for the Nitro class/interface definition more systematically:
<function_calls>

#!/bin/bash

Search for where Nitro type is defined or extended

rg -n "class Nitro|interface Nitro|type Nitro|export.*Nitro" --type ts | head -20


</function_calls>
<function_calls>

#!/bin/bash

Look at all files in src directory

find src -name "*.ts" -type f | sort


</function_calls>

Let me try a different approach - check if there's a dist or other compiled outputs:
<function_calls>

#!/bin/bash

Check what's in the root directory

ls -la | grep -E "^d"


</function_calls>
<function_calls>

#!/bin/bash

List all TS files in src to understand structure

fd -t f -e ts src/ | sort


</function_calls>

Now let me read the actual files mentioned:
<function_calls>

#!/bin/bash

Read plugins.ts completely

cat src/build/plugins.ts


</function_calls>

Perfect! Now let me check the types file:
<function_calls>

#!/bin/bash

Read the nitro types file

cat src/types/nitro.ts


</function_calls>

Now let me check the virtual/_all.ts file to understand the structure of virtualTemplates:
<function_calls>

#!/bin/bash

Find and read virtual/_all.ts

if [ -f "src/build/virtual/_all.ts" ]; then
cat src/build/virtual/_all.ts
fi


</function_calls>
<function_calls>

#!/bin/bash

List files in virtual directory

ls -la src/build/virtual/


</function_calls>

Let me check the package.json for the virtual plugin dependency:
<function_calls>

#!/bin/bash

Read package.json to find the virtual plugin

cat package.json | python3 -m json.tool | grep -A 10 -B 2 "virtual"


</function_calls>

Let me check what virtual refers to in the imports more carefully:
<function_calls>

#!/bin/bash

Search for external 'virtual' module usage

rg -n "from\s+['"]virtual['"]" --type ts


</function_calls>
<function_calls>

#!/bin/bash

Check if it's from @rollup/plugin-virtual

rg -n "@rollup|rollup.*virtual" --type ts -C 3


</function_calls>

Let me check the actual imports in plugins.ts:
<function_calls>

#!/bin/bash

Extract all imports from plugins.ts

sed -n '1,20p' src/build/plugins.ts


</function_calls>

Now let me search for the virtual function type definition:
<function_calls>

#!/bin/bash

Search for the virtual function in node_modules or type stubs

First check what virtual is imported from

rg -n "virtual" src/build/plugins.ts | head -10


</function_calls>

Let me check the full plugins.ts import section more carefully:
<function_calls>

#!/bin/bash

Get first 25 lines of plugins.ts to see all imports

head -25 src/build/plugins.ts


</function_calls>

Now let me look at the actual type definition by searching package.json dependencies:
<function_calls>

#!/bin/bash

Check dependencies

grep -E "virtual|rollup" package.json


</function_calls>

Let me search for TypeScript configuration to understand module resolution:
<function_calls>

#!/bin/bash

Check tsconfig

cat tsconfig.json 2>/dev/null | head -50


</function_calls>

Let me check if there's path aliases:
<function_calls>

#!/bin/bash

Look for path aliases

cat tsconfig.json | grep -A 10 "paths"


</function_calls>

Now let me check the actual implementation by reviewing where virtual plugin gets assigned:
<function_calls>

#!/bin/bash

Get lines around the virtualPlugin creation (lines 45-50)

sed -n '40,60p' src/build/plugins.ts | cat -n


</function_calls>

Let me look for how the virtual function is imported by checking the full import section:
<function_calls>

#!/bin/bash

Show first 50 lines to capture all imports and understand where virtual comes from

head -50 src/build/plugins.ts | cat -n


</function_calls>

Now let me examine the full nitro types to check vfs property type:
<function_calls>

#!/bin/bash

Look at nitro.ts around vfs definition

grep -n "vfs" src/types/nitro.ts -B 5 -A 5


</function_calls>

Let me check web resources about @rollup/plugin-virtual to understand its API:
<function_calls>

@rollup/plugin-virtual API modules structure TypeScript
</function_calls>

src/build/virtual/renderer-template.ts (1)

9-52: LGTM! Well-structured renderer template with proper dev/prod branching.

The template correctly handles three scenarios:

  1. No template configured → static placeholder
  2. Development → dynamic file reading for HMR
  3. Production → static vs dynamic rendering based on template syntax

The use of compileTemplateToString from rendu for dynamic templates and pre-reading files during production builds is appropriate.

src/build/virtual/feature-flags.ts (1)

3-30: All verification checks passed—no issues found.

The hasRoutes() method is confirmed to exist on the Router class (src/routing.ts:179-181), and all three routing properties (routes, routeRules, routedMiddleware) are correctly typed as Router<T> instances in the Nitro type definition (src/types/nitro.ts:26-32). The feature flags template correctly calls this method on each property.

The observation about Boolean(value) being redundant is valid—all values in the featureFlags object are already booleans—but this is harmless and defensive coding. The implementation is correct.

src/build/virtual/public-assets.ts (4)

21-72: LGTM! Clean refactoring of asset data generation.

The shift from a plugin-based approach to a template-based virtual module is well-structured. The asset metadata computation correctly handles MIME types, encodings, and conditional inlining.


74-122: LGTM! Solid integration of virtual module imports.

The dynamic handler selection and helper function exports provide a clean API surface. The logic correctly filters and transforms publicAssets configuration.


126-140: LGTM! Node.js asset reader implementation is correct.

Properly uses Node.js filesystem APIs and resolves paths relative to the server directory.


142-154: LGTM! Deno asset reader implementation is correct.

Properly uses Deno's file reading API and constructs paths appropriately for Deno Deploy.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Nov 27, 2025

Open in StackBlitz

npm i https://pkg.pr.new/nitrojs/nitro@3819

commit: 3506464

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a 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 (1)
src/build/virtual/storage.ts (1)

17-26: Add validation for required driver property.

The destructuring on line 18 correctly separates the driver from options, addressing the past review comment. However, there's no validation that each mount configuration has a driver property. If driver is missing, driverName becomes undefined, which would cause issues in the generated import and mount statements.

Consider adding validation similar to the database module:

   for (const path in storageMounts) {
-    const { driver: driverName, ...driverOpts } = storageMounts[path];
+    const mount = storageMounts[path];
+    if (!mount?.driver) {
+      throw new Error(`Storage mount "${path}" is missing required "driver" property.`);
+    }
+    const { driver: driverName, ...driverOpts } = mount;
     mounts.push({
🧹 Nitpick comments (1)
src/presets/winterjs/runtime/winterjs.ts (1)

2-2: WinterJS now imports the shared polyfills alias; consider future cleanup

The switch to #nitro-internal-polyfills is correct and keeps this entry in sync with the rest of the presets. Given the // TODO: Remove after removing polyfills note and the custom polyfill block below, it may be worth revisiting this file in a follow-up to consolidate on the shared polyfills module and drop the local shims/@ts-nocheck once safe.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed06131 and 3506464.

📒 Files selected for processing (29)
  • src/build/virtual/database.ts (1 hunks)
  • src/build/virtual/polyfills.ts (1 hunks)
  • src/build/virtual/storage.ts (1 hunks)
  • src/presets/_nitro/runtime/nitro-dev.ts (1 hunks)
  • src/presets/_nitro/runtime/nitro-prerenderer.ts (1 hunks)
  • src/presets/_nitro/runtime/service-worker.ts (1 hunks)
  • src/presets/aws-amplify/runtime/aws-amplify.ts (1 hunks)
  • src/presets/aws-lambda/runtime/aws-lambda-streaming.ts (1 hunks)
  • src/presets/aws-lambda/runtime/aws-lambda.ts (1 hunks)
  • src/presets/azure/runtime/azure-swa.ts (1 hunks)
  • src/presets/bun/runtime/bun.ts (1 hunks)
  • src/presets/cloudflare/runtime/_module-handler.ts (1 hunks)
  • src/presets/cloudflare/runtime/cloudflare-durable.ts (1 hunks)
  • src/presets/cloudflare/runtime/cloudflare-module.ts (1 hunks)
  • src/presets/cloudflare/runtime/cloudflare-pages.ts (1 hunks)
  • src/presets/deno/runtime/deno-deploy.ts (1 hunks)
  • src/presets/deno/runtime/deno-server.ts (1 hunks)
  • src/presets/netlify/runtime/netlify-edge.ts (1 hunks)
  • src/presets/netlify/runtime/netlify.ts (1 hunks)
  • src/presets/node/runtime/node-cluster.ts (1 hunks)
  • src/presets/node/runtime/node-middleware.ts (1 hunks)
  • src/presets/node/runtime/node-server.ts (1 hunks)
  • src/presets/standard/runtime/server.ts (1 hunks)
  • src/presets/stormkit/runtime/stormkit.ts (1 hunks)
  • src/presets/vercel/runtime/vercel.node.ts (1 hunks)
  • src/presets/vercel/runtime/vercel.web.ts (1 hunks)
  • src/presets/winterjs/runtime/winterjs.ts (1 hunks)
  • src/presets/zeabur/runtime/zeabur.ts (1 hunks)
  • src/runtime/internal/vite/dev-entry.mjs (1 hunks)
✅ Files skipped from review due to trivial changes (8)
  • src/presets/stormkit/runtime/stormkit.ts
  • src/presets/deno/runtime/deno-deploy.ts
  • src/presets/_nitro/runtime/service-worker.ts
  • src/presets/standard/runtime/server.ts
  • src/presets/aws-lambda/runtime/aws-lambda.ts
  • src/presets/cloudflare/runtime/_module-handler.ts
  • src/presets/vercel/runtime/vercel.web.ts
  • src/presets/netlify/runtime/netlify.ts
🧰 Additional context used
🧬 Code graph analysis (3)
src/build/virtual/storage.ts (1)
src/types/nitro.ts (1)
  • Nitro (16-37)
src/build/virtual/polyfills.ts (1)
src/types/nitro.ts (1)
  • Nitro (16-37)
src/build/virtual/database.ts (1)
src/types/nitro.ts (1)
  • Nitro (16-37)
⏰ 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: tests-rolldown (windows-latest)
  • GitHub Check: tests-rollup (windows-latest)
🔇 Additional comments (25)
src/presets/cloudflare/runtime/cloudflare-durable.ts (1)

1-1: LGTM! Typo fix aligns with standardization effort.

The import path correction from "pollyfills" to "polyfills" is accurate and aligns with the broader standardization of polyfills imports across the codebase.

src/presets/_nitro/runtime/nitro-dev.ts (1)

1-1: Typo fix verified as complete and consistent across the codebase.

The import path correction from "pollyfills" to "polyfills" has been applied comprehensively. Search results confirm:

  • Zero remaining references to the old typo'd path "#nitro-internal-pollyfills"
  • 27+ files consistently using the corrected path "#nitro-internal-polyfills"
  • The corrected path is properly defined in src/build/virtual/polyfills.ts as a virtual module

The change is correct and requires no further action.

src/presets/node/runtime/node-cluster.ts (1)

1-1: Polyfills alias rename looks correct

Switching to "#nitro-internal-polyfills" aligns this runtime with the standardized internal polyfills alias and keeps the side‑effect import ordering intact. No further changes needed in this file.

src/presets/_nitro/runtime/nitro-prerenderer.ts (1)

1-1: Polyfills alias correction looks good

Using #nitro-internal-polyfills here keeps the prerender entry consistent with the new shared polyfills virtual module; no other behavior changes.

src/presets/cloudflare/runtime/cloudflare-pages.ts (1)

1-1: Cloudflare Pages now uses the canonical polyfills alias

The corrected #nitro-internal-polyfills import matches the rest of the presets and keeps polyfills loaded before any runtime logic.

src/presets/azure/runtime/azure-swa.ts (1)

1-1: Azure SWA polyfills import is correctly aligned

Switch to #nitro-internal-polyfills is consistent with the new virtual polyfills module; no further adjustments needed here.

src/presets/zeabur/runtime/zeabur.ts (1)

1-1: Zeabur runtime uses the unified polyfills alias

The updated #nitro-internal-polyfills import matches the rest of the runtime entries; wrapper logic stays the same.

src/presets/cloudflare/runtime/cloudflare-module.ts (1)

1-1: Cloudflare module polyfills import corrected

Using #nitro-internal-polyfills here matches the rest of the Cloudflare runtimes and ensures polyfills load before handler creation.

src/presets/aws-amplify/runtime/aws-amplify.ts (1)

1-1: AWS Amplify runtime aligned to shared polyfills alias

The updated #nitro-internal-polyfills import is consistent with other Node-style presets; server startup logic remains the same.

src/presets/aws-lambda/runtime/aws-lambda-streaming.ts (1)

1-1: AWS Lambda streaming entry now uses the canonical polyfills module

Correcting the import to #nitro-internal-polyfills brings this entry in line with the rest of the runtime surface; streaming behavior is unchanged.

src/presets/netlify/runtime/netlify-edge.ts (1)

1-1: LGTM: Polyfills import path corrected.

The import path has been correctly updated from the typo #nitro-internal-pollyfills to #nitro-internal-polyfills.

src/presets/node/runtime/node-server.ts (1)

1-1: LGTM: Polyfills import path corrected.

The import path has been correctly updated from the typo #nitro-internal-pollyfills to #nitro-internal-polyfills.

src/presets/bun/runtime/bun.ts (1)

1-1: LGTM: Polyfills import path corrected.

The import path has been correctly updated from the typo #nitro-internal-pollyfills to #nitro-internal-polyfills.

src/presets/node/runtime/node-middleware.ts (1)

1-1: LGTM: Polyfills import path corrected.

The import path has been correctly updated from the typo #nitro-internal-pollyfills to #nitro-internal-polyfills.

src/runtime/internal/vite/dev-entry.mjs (1)

1-1: LGTM: Polyfills import path corrected.

The import path has been correctly updated from the typo #nitro-internal-pollyfills to #nitro-internal-polyfills.

src/presets/deno/runtime/deno-server.ts (1)

1-1: LGTM: Polyfills import path corrected.

The import path has been correctly updated from the typo #nitro-internal-pollyfills to #nitro-internal-polyfills.

src/presets/vercel/runtime/vercel.node.ts (1)

1-1: LGTM: Polyfills import path corrected.

The import path has been correctly updated from the typo #nitro-internal-pollyfills to #nitro-internal-polyfills.

src/build/virtual/polyfills.ts (1)

1-14: Typo fix verified: No references to old path remain in codebase.

The virtual module implementation is correct, and the typo fix ("#nitro-internal-pollyfills""#nitro-internal-polyfills") has been comprehensively addressed with no lingering references to the old path detected anywhere in the codebase.

src/build/virtual/database.ts (4)

1-4: LGTM!

The imports are appropriate for the virtual module's functionality.


5-11: LGTM!

The early return for disabled experimental database feature is appropriate and efficient.


13-21: LGTM!

The connector name extraction correctly handles dev/prerender mode fallback and safely filters undefined connectors using optional chaining and Boolean filtering.


23-27: LGTM!

The connector validation provides early, clear feedback for invalid connector names, which is essential for catching configuration errors at build time.

src/build/virtual/storage.ts (3)

1-4: LGTM!

The imports are appropriate for generating the storage virtual module.


5-16: LGTM!

The dev/prerender mode detection and storage config merging logic correctly prioritizes devStorage when appropriate.


28-49: LGTM!

The template generation correctly deduplicates driver imports, uses safe variable names, and properly serializes mount options. The generated initStorage() function structure is sound.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant