Skip to content

feat: add support for custom source-extensions to watchers and bundlers#4231

Open
seanstrom wants to merge 4 commits intonitrojs:mainfrom
seanstrom:seanstrom/add-custom-source-extension-support
Open

feat: add support for custom source-extensions to watchers and bundlers#4231
seanstrom wants to merge 4 commits intonitrojs:mainfrom
seanstrom:seanstrom/add-custom-source-extension-support

Conversation

@seanstrom
Copy link
Copy Markdown

🔗 Linked issue

Related Discussions

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme, or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

  • This PR adds support for additional file-extensions to be resolved for Server routing and Nitro modules.
    • For example, if someone want to use Nitro in combination with Civet (alternative to Typescript that compiles to TypeScript and JavaScript), then they can register a Vite plugin and/or a module loader for the .civet file extension.
  • With these changes, I'm able to successfully combine Nitro with Vite, and allow Vite to handle pre-processing Civet (or other alt-js language) source files, while integrating with Nitro's file-based routing and nitro-modules.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@seanstrom seanstrom requested a review from pi0 as a code owner April 28, 2026 00:28
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 28, 2026

@seanstrom is attempting to deploy a commit to the Nitro Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduces a configurable sourceExtensions option and a new src/utils/source-extensions.ts helper set. Replaces many hard-coded JS/TS extension lists and regexes with dynamic extension lists/patterns used for scanning, resolution, dev reload detection, module resolution, and tests (adds .civet fixtures and HMR tests).

Unified Source-Extension Support

Layer / File(s) Summary
Data Shape / Config
src/types/config.ts, src/config/defaults.ts
Adds NitroOptions.sourceExtensions: string[] and default sourceExtensions: [] to exported defaults.
Utility primitives
src/utils/source-extensions.ts
New utilities: normalize/merge extensions, build regex alternation (getSourceExtensionPattern), glob (getScanPattern), resolver list (getSourceExtensions) and stripSourceExtension.
Core resolution & scanning
src/config/resolvers/paths.ts, src/scan.ts, src/module.ts
Replace hard-coded resolvable extensions/glob/strip logic with getSourceExtensions, getScanPattern, and stripSourceExtension for server entry discovery, route/task naming, and module resolution.
Build config & types
src/build/config.ts, src/build/types.ts
baseBuildConfig.extensions, noExternal regex, middleware relativePath stripping, and resolver extensions now use the dynamic helpers instead of fixed JS/TS lists.
Dev tooling — watchers
src/build/vite/dev.ts, src/build/rollup/dev.ts, src/build/rolldown/dev.ts
Dev root-watcher serverEntryRe and reload gating rebuilt from getSourceExtensionPattern(nitro.options), allowing custom extensions (with optional .node).
Vite plugin & service env
src/build/vite/plugin.ts, src/build/vite/env.ts
Auto-resolution of service/SSR entries now passes getSourceExtensions(ctx.nitro!.options) to resolveModulePath; createServiceEnvironment and plugin entry discovery updated accordingly.
Platform preset
src/presets/cloudflare/entry-exports.ts
Cloudflare exports resolution switched to getSourceExtensions(nitro.options) instead of fixed extension list.
Tests & docs
docs/1.docs/50.configuration.md, test/fixture/nitro.config.ts, test/fixture/server/routes/api/civet.civet, test/tests.ts, test/vite/hmr-fixture/api/state-source-extension.civet, test/vite/hmr.test.ts
Adds docs for sourceExtensions, test fixture .civet files, unit test asserting route resolution, and an HMR integration test verifying reload on editing a custom-extension file.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title follows the conventional commits format with 'feat:' prefix and clearly describes the main change about adding custom source-extensions support.
Description check ✅ Passed The description is related to the changeset, explaining the rationale for supporting custom file extensions like .civet and how it enables integration with Nitro's routing and module system.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with 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.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/build/rolldown/dev.ts`:
- Around line 44-45: The root watcher regex only matches "server.<ext>" and
misses "server.node.<ext>"; update the serverEntryRe construction to accept an
optional ".node" segment by changing the pattern to something like
RegExp(String.raw`^server(?:\.node)?\.(?:${sourceExtensionPattern})$`) so both
server.<ext> and server.node.<ext> are matched—update the line that defines
serverEntryRe (which currently uses getSourceExtensionPattern and
sourceExtensionPattern) accordingly.

In `@src/build/rollup/dev.ts`:
- Around line 46-47: The server entry regex (serverEntryRe) currently only
matches "server.<ext>" and misses "server.node.<ext>"; update the RegExp
construction that uses sourceExtensionPattern so it accepts an optional ".node"
segment (e.g. change the pattern used in serverEntryRe to something like
^server(?:\.node)?\.(?:${sourceExtensionPattern})$) so both "server.<ext>" and
"server.node.<ext>" trigger the watcher; adjust the serverEntryRe initialization
that references sourceExtensionPattern accordingly.

In `@src/utils/source-extensions.ts`:
- Around line 15-20: normalizeSourceExtensions currently accepts invalid tokens
(e.g. "." or strings containing glob/path chars) which can produce malformed
scan/regex patterns; update normalizeSourceExtensions to validate each token
after trimming: reject and throw an Error for tokens that are exactly "." or
contain any of the characters used in globs/paths (e.g. '*', '?', '[', ']', '{',
'}', '/', '\\', path separators) or that are empty after trimming, and include
the offending token in the error message so callers know which value failed;
keep existing normalization behavior (prepend "." when missing) for valid tokens
and consider deduplicating the result before returning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7ded3dee-bb65-4c1e-93f3-32c8e2afffb8

📥 Commits

Reviewing files that changed from the base of the PR and between 6edbf84 and 7029bff.

📒 Files selected for processing (20)
  • docs/1.docs/50.configuration.md
  • src/build/config.ts
  • src/build/rolldown/dev.ts
  • src/build/rollup/dev.ts
  • src/build/types.ts
  • src/build/vite/dev.ts
  • src/build/vite/env.ts
  • src/build/vite/plugin.ts
  • src/config/defaults.ts
  • src/config/resolvers/paths.ts
  • src/module.ts
  • src/presets/cloudflare/entry-exports.ts
  • src/scan.ts
  • src/types/config.ts
  • src/utils/source-extensions.ts
  • test/fixture/nitro.config.ts
  • test/fixture/server/routes/api/civet.civet
  • test/tests.ts
  • test/vite/hmr-fixture/api/state-source-extension.civet
  • test/vite/hmr.test.ts

Comment thread src/build/rolldown/dev.ts Outdated
Comment thread src/build/rollup/dev.ts Outdated
Comment thread src/utils/source-extensions.ts
@seanstrom
Copy link
Copy Markdown
Author

@coderabbitai review please

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

@seanstrom Sure, I'll kick off a review of your PR right away!

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

♻️ Duplicate comments (1)
src/utils/source-extensions.ts (1)

15-24: ⚠️ Potential issue | 🟠 Major

Fail fast on invalid extension tokens instead of silently filtering.

normalizeSourceExtensions currently drops invalid values ("", ".") and still allows path/glob-special characters. That can produce malformed or over-broad glob/regex patterns downstream. Please throw with the offending token.

Proposed hard validation
 export function normalizeSourceExtensions(extensions: string[] = []) {
   return extensions
-    .map((ext) => ext.trim())
-    .filter((ext) => {
-      const trimmedExt = ext.trim();
-      const isEmpty = trimmedExt.length === 0;
-      const isInvalid = isEmpty || trimmedExt === ".";
-      return !isInvalid;
-    })
-    .map((ext) => (ext.startsWith(".") ? ext : `.${ext}`));
+    .map((rawExt) => {
+      const trimmedExt = rawExt.trim();
+      const normalizedExt = trimmedExt.startsWith(".")
+        ? trimmedExt
+        : `.${trimmedExt}`;
+      const extBody = normalizedExt.slice(1);
+
+      if (!extBody || /[\/\\*?[\]{}(),]/.test(extBody)) {
+        throw new Error(
+          `Invalid source extension "${rawExt}". Use a plain extension like ".civet" without path/glob characters.`
+        );
+      }
+      return normalizedExt;
+    });
 }
As per coding guidelines: "Prefer explicit errors over silent failures; use warnings for recoverable situations and throw for invalid states" and "Include actionable context in error messages".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/source-extensions.ts` around lines 15 - 24,
normalizeSourceExtensions currently silently filters invalid tokens; change it
to validate each input and throw a descriptive Error when encountering an
offending token. Specifically, in normalizeSourceExtensions iterate over the
input extensions and for each token (e.g., variable trimmedExt) check for empty
string, a single dot ".", and any path/glob-special characters (such as "/",
"\\", "*", "?", "[", "]", "{", "}", "(", ")", "!", "|", etc.); if any of these
are present throw an Error that includes the offending token and a short
actionable message. If the token is valid, normalize it to start with a dot
(ext.startsWith(".") ? ext : `.${ext}`) and return the normalized array.
🧹 Nitpick comments (2)
src/utils/source-extensions.ts (2)

27-59: Prefer options-object second parameters for these exported APIs.

getSourceExtensions, getSourceExtensionPattern, and stripSourceExtension currently rely on positional baseExtensions, which is easier to misuse and diverges from repo convention.

API-shape refactor (same behavior)
 export function getSourceExtensions(
   { sourceExtensions }: SourceExtensionOptions,
-  baseExtensions = BASE_SOURCE_EXTENSIONS
+  options: { baseExtensions?: string[] } = {}
 ) {
+  const { baseExtensions = BASE_SOURCE_EXTENSIONS } = options;
   return [...new Set([...baseExtensions, ...normalizeSourceExtensions(sourceExtensions)])];
 }

 export function getSourceExtensionPattern(
   { sourceExtensions }: SourceExtensionOptions,
-  baseExtensions = BASE_SOURCE_EXTENSIONS
+  options: { baseExtensions?: string[] } = {}
 ) {
-  return getSourceExtensions({ sourceExtensions }, baseExtensions)
+  return getSourceExtensions({ sourceExtensions }, options)
     .map((ext) => escapeRegExp(ext.slice(1)))
     .join("|");
 }

 export function stripSourceExtension(
   id: string,
   options: SourceExtensionOptions,
-  baseExtensions = BASE_SOURCE_EXTENSIONS
+  extra: { baseExtensions?: string[] } = {}
 ) {
-  const ext = getSourceExtensions(options, baseExtensions)
+  const ext = getSourceExtensions(options, extra)
     .sort((a, b) => b.length - a.length)
     .find((ext) => id.endsWith(ext));
   return ext ? id.slice(0, -ext.length) : id;
 }
As per coding guidelines: "For multi-arg functions, use an options object as the second parameter".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/source-extensions.ts` around lines 27 - 59, These exported
functions use a positional second parameter for baseExtensions which violates
the options-object convention; change the signatures of getSourceExtensions,
getSourceExtensionPattern, and stripSourceExtension to accept a second options
object (e.g. { baseExtensions } = {}) instead of a positional array, defaulting
baseExtensions to BASE_SOURCE_EXTENSIONS, update internal callers to read
baseExtensions from that options object (and keep normalizeSourceExtensions
calls and existing behavior), and also update getScanPattern to continue calling
getSourceExtensions(options) unchanged; search for and update all usages of
getSourceExtensions(..., baseExtensions), getSourceExtensionPattern(...,
baseExtensions), and stripSourceExtension(..., baseExtensions) to pass an
options object { baseExtensions } so behavior remains the same while following
the options-object API.

6-9: Move internal helper constants to the end of the file.

moduleExtensions is non-exported/internal and is currently declared before exported members.

As per coding guidelines: "Place non-exported/internal helpers at the end of the file".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/source-extensions.ts` around lines 6 - 9, The non-exported helper
constant moduleExtensions is declared before exported members; move the const
moduleExtensions declaration to the end of the file after all exported
functions/types so internal helpers follow the coding guideline. Ensure you keep
the same const name and values, do not export it, and update any internal
references (e.g., where exported functions use moduleExtensions) — if those
functions reference it, keep their implementations above but rely on the
module-scoped constant defined later (no code changes needed besides
reordering).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/utils/source-extensions.ts`:
- Around line 15-24: normalizeSourceExtensions currently silently filters
invalid tokens; change it to validate each input and throw a descriptive Error
when encountering an offending token. Specifically, in normalizeSourceExtensions
iterate over the input extensions and for each token (e.g., variable trimmedExt)
check for empty string, a single dot ".", and any path/glob-special characters
(such as "/", "\\", "*", "?", "[", "]", "{", "}", "(", ")", "!", "|", etc.); if
any of these are present throw an Error that includes the offending token and a
short actionable message. If the token is valid, normalize it to start with a
dot (ext.startsWith(".") ? ext : `.${ext}`) and return the normalized array.

---

Nitpick comments:
In `@src/utils/source-extensions.ts`:
- Around line 27-59: These exported functions use a positional second parameter
for baseExtensions which violates the options-object convention; change the
signatures of getSourceExtensions, getSourceExtensionPattern, and
stripSourceExtension to accept a second options object (e.g. { baseExtensions }
= {}) instead of a positional array, defaulting baseExtensions to
BASE_SOURCE_EXTENSIONS, update internal callers to read baseExtensions from that
options object (and keep normalizeSourceExtensions calls and existing behavior),
and also update getScanPattern to continue calling getSourceExtensions(options)
unchanged; search for and update all usages of getSourceExtensions(...,
baseExtensions), getSourceExtensionPattern(..., baseExtensions), and
stripSourceExtension(..., baseExtensions) to pass an options object {
baseExtensions } so behavior remains the same while following the options-object
API.
- Around line 6-9: The non-exported helper constant moduleExtensions is declared
before exported members; move the const moduleExtensions declaration to the end
of the file after all exported functions/types so internal helpers follow the
coding guideline. Ensure you keep the same const name and values, do not export
it, and update any internal references (e.g., where exported functions use
moduleExtensions) — if those functions reference it, keep their implementations
above but rely on the module-scoped constant defined later (no code changes
needed besides reordering).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c80394e9-c029-45ae-9e53-023af49b5a48

📥 Commits

Reviewing files that changed from the base of the PR and between 53f6a32 and bc440c9.

📒 Files selected for processing (1)
  • src/utils/source-extensions.ts

@seanstrom seanstrom force-pushed the seanstrom/add-custom-source-extension-support branch from bc440c9 to e680dd4 Compare May 2, 2026 04:33
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/vite/hmr.test.ts`:
- Around line 97-144: The test "editing custom source extension API entry" can
exceed the default 5s Vitest timeout because it creates a Vite dev server, waits
for an initial fetch, edits a file and polls for HMR; update the test
declaration to increase the timeout (e.g. to 30_000 ms) by adding a timeout
argument to the test call so the async function that uses createServer,
customServer.listen, and pollResponse has enough time to complete.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 767043de-6eef-403f-8383-33a74830ac6b

📥 Commits

Reviewing files that changed from the base of the PR and between bc440c9 and e680dd4.

📒 Files selected for processing (20)
  • docs/1.docs/50.configuration.md
  • src/build/config.ts
  • src/build/rolldown/dev.ts
  • src/build/rollup/dev.ts
  • src/build/types.ts
  • src/build/vite/dev.ts
  • src/build/vite/env.ts
  • src/build/vite/plugin.ts
  • src/config/defaults.ts
  • src/config/resolvers/paths.ts
  • src/module.ts
  • src/presets/cloudflare/entry-exports.ts
  • src/scan.ts
  • src/types/config.ts
  • src/utils/source-extensions.ts
  • test/fixture/nitro.config.ts
  • test/fixture/server/routes/api/civet.civet
  • test/tests.ts
  • test/vite/hmr-fixture/api/state-source-extension.civet
  • test/vite/hmr.test.ts
✅ Files skipped from review due to trivial changes (6)
  • test/fixture/server/routes/api/civet.civet
  • test/vite/hmr-fixture/api/state-source-extension.civet
  • test/fixture/nitro.config.ts
  • docs/1.docs/50.configuration.md
  • src/build/config.ts
  • src/utils/source-extensions.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/module.ts
  • src/presets/cloudflare/entry-exports.ts
  • src/build/vite/plugin.ts
  • src/build/vite/dev.ts
  • src/build/vite/env.ts
  • src/types/config.ts
  • src/build/types.ts

Comment thread test/vite/hmr.test.ts
Comment on lines +97 to +144
test("editing custom source extension API entry", async () => {
const customAPI = openFileForEditing(join(rootDir, "api/state-source-extension.civet"));
const customMessages: any[] = [];
const customServer = await createServer({
root: rootDir,
configFile: false,
plugins: [
{
name: "test:source-extensions",
enforce: "pre",
transform(code: any, id: any) {
if (id.endsWith(".civet")) {
return { code, map: null };
}
},
},
nitro({ serverDir: "./", sourceExtensions: [".civet"] }),
],
});
const originalSend = customServer.ws.send.bind(customServer.ws);
customServer.ws.send = function (payload: any) {
customMessages.push(payload);
return originalSend(payload);
};

try {
await customServer.listen("0" as unknown as number);
const addr = customServer.httpServer?.address() as {
port: number;
address: string;
family: string;
};
const customServerURL = `http://${addr.family === "IPv6" ? `[${addr.address}]` : addr.address}:${addr.port}`;
const initialResponse = await fetch(`${customServerURL}/api/state-source-extension`).then(
(r) => r.text()
);
expect(initialResponse).toContain('"state":1');

customAPI.update((content) =>
content.replace("({ state })", '({ state: state + " (modified)" })')
);
await pollResponse(`${customServerURL}/api/state-source-extension`, /modified/);
expect(customMessages).toMatchObject([{ type: "full-reload" }]);
} finally {
customAPI.restore();
await customServer.close();
}
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add an explicit timeout — default 5 s is insufficient for server creation + HMR polling.

The test creates a brand-new Vite dev server (which can take several seconds in CI), waits for an initial fetch, edits a file, and then calls pollResponse (up to 5 s internally). All of this must complete within Vitest's default 5000 ms test timeout, making it nearly certain to time out in any moderately loaded CI environment. The existing beforeAll uses 30_000 ms for the same reason.

⏱️ Proposed fix
-  test("editing custom source extension API entry", async () => {
+  test("editing custom source extension API entry", { timeout: 30_000 }, async () => {
📝 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.

Suggested change
test("editing custom source extension API entry", async () => {
const customAPI = openFileForEditing(join(rootDir, "api/state-source-extension.civet"));
const customMessages: any[] = [];
const customServer = await createServer({
root: rootDir,
configFile: false,
plugins: [
{
name: "test:source-extensions",
enforce: "pre",
transform(code: any, id: any) {
if (id.endsWith(".civet")) {
return { code, map: null };
}
},
},
nitro({ serverDir: "./", sourceExtensions: [".civet"] }),
],
});
const originalSend = customServer.ws.send.bind(customServer.ws);
customServer.ws.send = function (payload: any) {
customMessages.push(payload);
return originalSend(payload);
};
try {
await customServer.listen("0" as unknown as number);
const addr = customServer.httpServer?.address() as {
port: number;
address: string;
family: string;
};
const customServerURL = `http://${addr.family === "IPv6" ? `[${addr.address}]` : addr.address}:${addr.port}`;
const initialResponse = await fetch(`${customServerURL}/api/state-source-extension`).then(
(r) => r.text()
);
expect(initialResponse).toContain('"state":1');
customAPI.update((content) =>
content.replace("({ state })", '({ state: state + " (modified)" })')
);
await pollResponse(`${customServerURL}/api/state-source-extension`, /modified/);
expect(customMessages).toMatchObject([{ type: "full-reload" }]);
} finally {
customAPI.restore();
await customServer.close();
}
});
test("editing custom source extension API entry", { timeout: 30_000 }, async () => {
const customAPI = openFileForEditing(join(rootDir, "api/state-source-extension.civet"));
const customMessages: any[] = [];
const customServer = await createServer({
root: rootDir,
configFile: false,
plugins: [
{
name: "test:source-extensions",
enforce: "pre",
transform(code: any, id: any) {
if (id.endsWith(".civet")) {
return { code, map: null };
}
},
},
nitro({ serverDir: "./", sourceExtensions: [".civet"] }),
],
});
const originalSend = customServer.ws.send.bind(customServer.ws);
customServer.ws.send = function (payload: any) {
customMessages.push(payload);
return originalSend(payload);
};
try {
await customServer.listen("0" as unknown as number);
const addr = customServer.httpServer?.address() as {
port: number;
address: string;
family: string;
};
const customServerURL = `http://${addr.family === "IPv6" ? `[${addr.address}]` : addr.address}:${addr.port}`;
const initialResponse = await fetch(`${customServerURL}/api/state-source-extension`).then(
(r) => r.text()
);
expect(initialResponse).toContain('"state":1');
customAPI.update((content) =>
content.replace("({ state })", '({ state: state + " (modified)" })')
);
await pollResponse(`${customServerURL}/api/state-source-extension`, /modified/);
expect(customMessages).toMatchObject([{ type: "full-reload" }]);
} finally {
customAPI.restore();
await customServer.close();
}
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/vite/hmr.test.ts` around lines 97 - 144, The test "editing custom source
extension API entry" can exceed the default 5s Vitest timeout because it creates
a Vite dev server, waits for an initial fetch, edits a file and polls for HMR;
update the test declaration to increase the timeout (e.g. to 30_000 ms) by
adding a timeout argument to the test call so the async function that uses
createServer, customServer.listen, and pollResponse has enough time to complete.

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