Skip to content

fix: handling of cjs deps in dev ssr#264

Merged
rturnq merged 1 commit intomainfrom
cjs-ssr-deps
Apr 7, 2026
Merged

fix: handling of cjs deps in dev ssr#264
rturnq merged 1 commit intomainfrom
cjs-ssr-deps

Conversation

@rturnq
Copy link
Copy Markdown
Contributor

@rturnq rturnq commented Apr 6, 2026

No description provided.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 6, 2026

🦋 Changeset detected

Latest commit: 29a0cd3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@marko/vite Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

Walkthrough

This pull request introduces a patch release for @marko/vite that improves handling of CommonJS dependencies in dev-mode server-side rendering. The implementation replaces the external @chialab/cjs-to-esm dependency with a custom transformCjsToEsm function that uses cjs-module-lexer, es-module-lexer, and magic-string utilities. The function detects CommonJS patterns, converts require() calls to ESM imports, and transforms module.exports into ESM export statements. A comprehensive test fixture (isomorphic-commonjs-module-exports) is added to validate the transformation behavior in an isomorphic context.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to assess whether the description relates to the changeset. Add a pull request description explaining the fix for CommonJS dependency handling in dev-mode SSR and the implementation approach.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: handling of cjs deps in dev ssr' accurately summarizes the main change: fixing how CommonJS dependencies are handled during development SSR, which is evident from the changeset, new transformation code, and test fixture.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cjs-ssr-deps

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

Copy link
Copy Markdown

@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: 2

🧹 Nitpick comments (1)
src/index.ts (1)

808-811: Avoid fully silent fallback on CJS transform errors.

At Line 808-Line 811, swallowing the error and returning null makes SSR transform failures hard to diagnose. Consider logging a debug/warn message with id before fallback.

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

In `@src/index.ts` around lines 808 - 811, The catch block in transformCjsToEsm is
currently swallowing errors and returning null; update the try/catch around the
call to transformCjsToEsm(source, id) to catch the error as a variable (e.g.,
err) and emit a suitable log (debug/warn) that includes the module id and the
error message/stack before returning null so SSR transform failures are visible;
reference the transformCjsToEsm call and the id parameter when adding the log.
🤖 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/__tests__/fixtures/isomorphic-commonjs-module-exports/src/index.js`:
- Around line 4-9: The request handler only responds when req.url === "/"
leaving other requests hanging; add a non-root fallback in the same handler to
set an appropriate status and body (e.g., 404 or 405) and end the response when
req.url !== "/", by calling res.statusCode = 404 (or other),
res.setHeader("Content-Type", ...), and res.end(...) so all paths (including the
branch invoking template.render(...)) always terminate the response.

In `@src/cjs-to-esm.ts`:
- Around line 5-7: REQUIRE_RE and REQUIRE_TEST_RE incorrectly match require()
occurrences inside comments; replace the regex-based detection with a
syntax-aware scan: parse the source (using the existing es-module-lexer or a JS
parser like acorn used elsewhere) and find CallExpression nodes whose callee is
an Identifier named "require" (and ensure they are not inside comments or string
literals), then rewrite only those AST-located ranges instead of using
REQUIRE_RE/REQUIRE_TEST_RE; update any code that references
REQUIRE_RE/REQUIRE_TEST_RE (and keep EXPORTS_RE for exports detection if still
needed) to use the new parser-based detection logic so commented-out require
calls are ignored.

---

Nitpick comments:
In `@src/index.ts`:
- Around line 808-811: The catch block in transformCjsToEsm is currently
swallowing errors and returning null; update the try/catch around the call to
transformCjsToEsm(source, id) to catch the error as a variable (e.g., err) and
emit a suitable log (debug/warn) that includes the module id and the error
message/stack before returning null so SSR transform failures are visible;
reference the transformCjsToEsm call and the id parameter when adding the log.
🪄 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: 63622409-2bcc-443d-a19d-dbd0b62e018c

📥 Commits

Reviewing files that changed from the base of the PR and between b041b85 and 29a0cd3.

⛔ Files ignored due to path filters (7)
  • package-lock.json is excluded by !**/package-lock.json and included by **
  • src/__tests__/fixtures/isomorphic-commonjs-module-exports/__snapshots__/build.expected.md is excluded by !**/__snapshots__/** and included by **
  • src/__tests__/fixtures/isomorphic-commonjs-module-exports/__snapshots__/dev.expected.md is excluded by !**/__snapshots__/** and included by **
  • src/__tests__/fixtures/isomorphic-commonjs-module-exports/node_modules/dep/index.js is excluded by !**/node_modules/** and included by **
  • src/__tests__/fixtures/isomorphic-commonjs-module-exports/node_modules/dep/package.json is excluded by !**/node_modules/** and included by **
  • src/__tests__/fixtures/isomorphic-commonjs-module-exports/node_modules/transient-dep/index.js is excluded by !**/node_modules/** and included by **
  • src/__tests__/fixtures/isomorphic-commonjs-module-exports/node_modules/transient-dep/package.json is excluded by !**/node_modules/** and included by **
📒 Files selected for processing (11)
  • .changeset/nice-tires-stare.md
  • package.json
  • src/__tests__/fixtures/isomorphic-commonjs-module-exports/dev-server.mjs
  • src/__tests__/fixtures/isomorphic-commonjs-module-exports/package.json
  • src/__tests__/fixtures/isomorphic-commonjs-module-exports/server.mjs
  • src/__tests__/fixtures/isomorphic-commonjs-module-exports/src/index.js
  • src/__tests__/fixtures/isomorphic-commonjs-module-exports/src/template.marko
  • src/__tests__/fixtures/isomorphic-commonjs-module-exports/test.config.ts
  • src/__tests__/fixtures/isomorphic-commonjs-module-exports/vite.config.mts
  • src/cjs-to-esm.ts
  • src/index.ts

Comment on lines +4 to +9
if (req.url === "/") {
res.statusCode = 200;
res.setHeader("Content-Type", "text/html; charset=utf-8");
template.render({}, res);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a non-root fallback to avoid hanging requests.

On Line 4, only "/" is handled. Other URLs currently get no status/body, which can leave the connection open.

Suggested fix
 export function handler(req, res) {
-  if (req.url === "/") {
-    res.statusCode = 200;
-    res.setHeader("Content-Type", "text/html; charset=utf-8");
-    template.render({}, res);
-  }
+  if (req.url !== "/") {
+    res.statusCode = 404;
+    res.end("Not Found");
+    return;
+  }
+
+  res.statusCode = 200;
+  res.setHeader("Content-Type", "text/html; charset=utf-8");
+  template.render({}, res);
 }
📝 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
if (req.url === "/") {
res.statusCode = 200;
res.setHeader("Content-Type", "text/html; charset=utf-8");
template.render({}, res);
}
}
if (req.url !== "/") {
res.statusCode = 404;
res.end("Not Found");
return;
}
res.statusCode = 200;
res.setHeader("Content-Type", "text/html; charset=utf-8");
template.render({}, res);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/fixtures/isomorphic-commonjs-module-exports/src/index.js`
around lines 4 - 9, The request handler only responds when req.url === "/"
leaving other requests hanging; add a non-root fallback in the same handler to
set an appropriate status and body (e.g., 404 or 405) and end the response when
req.url !== "/", by calling res.statusCode = 404 (or other),
res.setHeader("Content-Type", ...), and res.end(...) so all paths (including the
branch invoking template.render(...)) always terminate the response.

Comment thread src/cjs-to-esm.ts
Comment on lines +5 to +7
const REQUIRE_RE = /([^.\w$]|^)require\s*\(\s*(['"])(.*?)\2\s*\)/g;
const REQUIRE_TEST_RE = /([^.\w$]|^)require\s*\(\s*['"]/;
const EXPORTS_RE = /\b(module\.exports|exports)\b/;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify regex false-positives against non-code contexts (read-only demonstration).
python - <<'PY'
import re

REQUIRE_RE = re.compile(r"([^.\w$]|^)require\s*\(\s*(['\"])(.*?)\2\s*\)")

samples = {
  "string_literal": 'const s = "require(\\"fs\\")"; module.exports = {};',
  "line_comment": "// require('path')\nmodule.exports = 1;",
  "block_comment": "/* require('http') */\nmodule.exports = 2;",
  "real_call": "const fs = require('fs'); module.exports = fs;"
}

for name, code in samples.items():
    matches = [m.group(0) for m in REQUIRE_RE.finditer(code)]
    print(f"{name}: {matches}")
PY

Repository: marko-js/vite

Length of output: 176


🏁 Script executed:

# First, let's check the file structure and read the relevant sections
git ls-files src/cjs-to-esm.ts

# Read the first section around lines 5-7
sed -n '1,20p' src/cjs-to-esm.ts

# Read the second section around lines 57-72
sed -n '50,75p' src/cjs-to-esm.ts

# Get total file size for context
wc -l src/cjs-to-esm.ts

Repository: marko-js/vite

Length of output: 1640


🏁 Script executed:

# Get more context around the regex usage - lines 20-80 to see function signature and full usage
sed -n '20,80p' src/cjs-to-esm.ts

Repository: marko-js/vite

Length of output: 1527


🏁 Script executed:

# Get the rest of the function and lines 57-72 specifically
sed -n '52,100p' src/cjs-to-esm.ts

Repository: marko-js/vite

Length of output: 1453


Regex-based require() rewriting matches inside comments, producing incorrect transformations.

The REQUIRE_RE pattern operates on raw source text without syntax awareness. While string literals are generally safe (quotes prevent accidental matches), commented-out code like // require('fs') or /* require('http') */ will be matched and rewritten. This corrupts comments and can produce invalid output.

The codebase already uses lexer-based parsing (via cjs-module-lexer for exports and es-module-lexer for module detection), so switching the require-call matching to a similar syntax-aware approach is feasible and recommended.

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

In `@src/cjs-to-esm.ts` around lines 5 - 7, REQUIRE_RE and REQUIRE_TEST_RE
incorrectly match require() occurrences inside comments; replace the regex-based
detection with a syntax-aware scan: parse the source (using the existing
es-module-lexer or a JS parser like acorn used elsewhere) and find
CallExpression nodes whose callee is an Identifier named "require" (and ensure
they are not inside comments or string literals), then rewrite only those
AST-located ranges instead of using REQUIRE_RE/REQUIRE_TEST_RE; update any code
that references REQUIRE_RE/REQUIRE_TEST_RE (and keep EXPORTS_RE for exports
detection if still needed) to use the new parser-based detection logic so
commented-out require calls are ignored.

@rturnq rturnq merged commit 8cba092 into main Apr 7, 2026
9 checks passed
@rturnq rturnq deleted the cjs-ssr-deps branch April 7, 2026 04:43
@github-actions github-actions bot mentioned this pull request Apr 7, 2026
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