Skip to content

fix: avoid Babel parsing raw HTML under Vite 8 bundledDev#518

Closed
Innei wants to merge 1 commit into
zh-lx:mainfrom
Innei:fix/vite-bundled-dev-html-parse
Closed

fix: avoid Babel parsing raw HTML under Vite 8 bundledDev#518
Innei wants to merge 1 commit into
zh-lx:mainfrom
Innei:fix/vite-bundled-dev-html-parse

Conversation

@Innei
Copy link
Copy Markdown

@Innei Innei commented Apr 14, 2026

Summary

  • When experimental.bundledDev: true is enabled in Vite 8, rolldown invokes filterless transform hooks on every module, including the raw index.html entry — before vite:build-html converts it into a JS proxy.
  • The plugin's transform hook has no id filter, so raw HTML reaches getCodeWithWebComponent, where Babel parse() is invoked on the .html source and throws SyntaxError: Unexpected token.

Fix

  • packages/vite/src/index.ts: early-return for raw .html ids (no query) before calling getCodeWithWebComponent.
  • packages/core/src/server/use-client.ts: guard the inject path with isJsTypeFile(file) so addImportToEntry / addNextEmptyElementToEntry never feed non-JS sources into Babel.

Test plan

  • Reproduced in a Vite 8 + React demo with experimental.bundledDev: true + codeInspectorPlugin. Without the fix, dev server throws Build error ... Unexpected token while transforming index.html. With the fix, the server boots and the bundled output contains correct data-inspector-path attributes pointing to the source .tsx files (e.g. src/App.tsx:7:5:div).
  • Verified no regression in classic Vite dev mode (bundledDev: false).

In Vite 8 with `experimental.bundledDev: true`, rolldown invokes filterless
`transform` hooks on every module, including `index.html` — which reaches the
plugin before `vite:build-html` converts it into a JS proxy. The plugin then
forwards the raw HTML into `getCodeWithWebComponent`, where Babel `parse()`
throws on HTML syntax.

- `packages/vite/src/index.ts`: early-return for raw `.html` ids (no query)
  before calling `getCodeWithWebComponent`.
- `packages/core/src/server/use-client.ts`: guard the inject path with
  `isJsTypeFile(file)` so `addImportToEntry`/`addNextEmptyElementToEntry`
  never feed non-JS sources into Babel.
Copilot AI review requested due to automatic review settings April 14, 2026 08:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

Review Summary by Qodo

Avoid Babel parsing raw HTML under Vite 8 bundledDev

🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Prevent Babel parsing raw HTML in Vite 8 bundledDev mode
• Add early-return for .html files without query parameters
• Guard inject path with isJsTypeFile() check
• Avoid "Unexpected token" errors from Babel on HTML syntax
Diagram
flowchart LR
  A["Vite 8 bundledDev<br/>transform hook"] -->|raw HTML| B["Check file type<br/>and query"]
  B -->|.html no query| C["Early return<br/>skip processing"]
  B -->|JS file| D["getCodeWithWebComponent"]
  D -->|inject needed| E["isJsTypeFile guard"]
  E -->|JS type| F["addImportToEntry<br/>Babel parse"]
  E -->|non-JS| G["Skip injection<br/>avoid error"]
Loading

Grey Divider

File Changes

1. packages/vite/src/index.ts 🐞 Bug fix +11/-3

Add HTML file filtering in transform hook

• Extract file path and query string from module id
• Add early-return guard for raw .html files without query parameters
• Prevent raw HTML from reaching getCodeWithWebComponent and Babel parser
• Reorganize code by moving path extraction before getCodeWithWebComponent call

packages/vite/src/index.ts


2. packages/core/src/server/use-client.ts 🐞 Bug fix +4/-1

Guard injection with JS type validation

• Add canParseAsJs check using isJsTypeFile() function
• Guard injection code path to only process JS-type files
• Prevent non-JS sources (e.g., raw HTML) from being passed to Babel
• Add explanatory comment about Babel parsing limitations

packages/core/src/server/use-client.ts


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented Apr 14, 2026

Code Review by Qodo

🐞 Bugs (1)   📘 Rule violations (0)   📎 Requirement gaps (0)
🐞\ ≡ Correctness (1)

Grey Divider


Remediation recommended

1. Astro target now unreachable 🐞
Description
getCodeWithWebComponent now requires isJsTypeFile(file) to enter the injection block, but
isTargetFileToInject still treats file === AstroToolbarFile as a valid injection target. Because
AstroToolbarFile is a virtual id (\0astro:dev-toolbar) that cannot pass isJsTypeFile()’s
extension check, injection will never run for that target even when isTargetFileToInject returns
true.
Code

packages/core/src/server/use-client.ts[R336-339]

+  // Babel can only parse JS-type source. Skip non-JS (e.g. raw `.html`) to avoid
+  // "Unexpected token" errors from `addImportToEntry` / `addNextEmptyElementToEntry`.
+  const canParseAsJs = !file || isJsTypeFile(file);
+  if ((isTargetFile || inject) && canParseAsJs) {
Evidence
The new guard (canParseAsJs) is solely based on isJsTypeFile(file), which checks only for JS/TS
extensions via endsWith. Separately, target selection explicitly includes `file ===
AstroToolbarFile, but AstroToolbarFile` is defined as a virtual module id without a JS extension,
so canParseAsJs is always false and the injection block is skipped even when isTargetFile is
true.

packages/core/src/server/use-client.ts[334-366]
packages/core/src/server/use-client.ts[258-268]
packages/core/src/shared/constant.ts[6-8]
packages/core/src/shared/utils.ts[46-49]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`getCodeWithWebComponent()` now gates the injection block behind `isJsTypeFile(file)`, but `isTargetFileToInject()` still marks `file === AstroToolbarFile` as an injection target. Since `AstroToolbarFile` is a virtual id (`\0astro:dev-toolbar`) that cannot satisfy `isJsTypeFile()` (extension-based), the AstroToolbarFile target clause becomes ineffective and injection can never run for that target.

### Issue Context
- `AstroToolbarFile` is explicitly listed as an injection target.
- `isJsTypeFile()` is implemented as a simple `endsWith(ext)` check against `JsFileExtList`.

### Fix Focus Areas
- packages/core/src/server/use-client.ts[334-366]
- packages/core/src/server/use-client.ts[258-268]

### Suggested fix approach
Update the `canParseAsJs` logic to permit known JS-producing virtual modules (at minimum `AstroToolbarFile`), e.g.:
- Treat `file === AstroToolbarFile` as parseable, OR
- Broaden to `file.startsWith('\0')` if virtual modules are expected to be JS, OR
- Move the guard so it only blocks the *Babel-parse-dependent* paths (Next/file import mutation) while still allowing safe string-prefix injection where appropriate.

Keep the original goal (skip raw `.html`) intact while preserving the intent of the existing `AstroToolbarFile` target special-case.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 14, 2026

Codecov Report

❌ Patch coverage is 88.88889% with 1 line in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@e2b1838). Learn more about missing BASE report.

Files with missing lines Patch % Lines
packages/vite/src/index.ts 80.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #518   +/-   ##
=======================================
  Coverage        ?   99.97%           
=======================================
  Files           ?       23           
  Lines           ?     3693           
  Branches        ?      984           
=======================================
  Hits            ?     3692           
  Misses          ?        1           
  Partials        ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@zh-lx
Copy link
Copy Markdown
Owner

zh-lx commented Apr 18, 2026

I can't reproduct your issue. Could you please share a repository?

// invokes filterless `transform` hooks on every module including `index.html`
// before `vite:build-html` converts it to a JS proxy. Passing raw HTML into
// `getCodeWithWebComponent` may trigger Babel `parse()` on HTML and throw.
if (filePath.endsWith('.html') && !query) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

.html has already been excluded in isTargetFileToInject

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

In the Vite 8 bundledDev, the logic seems not to be like that; without this, it indeed goes to the HTML parse.

@Innei
Copy link
Copy Markdown
Author

Innei commented Apr 22, 2026

我之前误解了你的意思。实际上这个问题应该通过 consumer 侧的 exclude 配置来解决,而不是修改 plugin 源码——只需在 codeInspectorPlugin({ exclude: [/\.(css|json|html)$/] }) 中加上 html 即可。这个 PR 的改动方向不对,关闭之。Sorry for the noise!

@Innei Innei closed this Apr 22, 2026
@Innei
Copy link
Copy Markdown
Author

Innei commented Apr 22, 2026

之前误解了你的意思,抱歉。这个问题的根因是 Vite 8 的 experimental.bundledDev 模式下,code-inspector-plugin 的 transform hook 会错误地解析 index.html 导致 SyntaxError。但修复不需要改 plugin 源码——在 consumer 侧配置 exclude: [/\.(css|json|html)$/] 加上 html 即可跳过 HTML 文件。已在 lobehub 验证通过。关闭此 PR。

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.

3 participants