Skip to content

fix: inject vue inspector attributes via compiler transform#548

Merged
zh-lx merged 1 commit into
zh-lx:pre-mainfrom
lejunyang:main
Jun 28, 2026
Merged

fix: inject vue inspector attributes via compiler transform#548
zh-lx merged 1 commit into
zh-lx:pre-mainfrom
lejunyang:main

Conversation

@lejunyang

Copy link
Copy Markdown
Contributor

fix #547
全部由AI编写,仅供参考,本地测试确实解决了问题,辛苦看看是否有用

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Fix Vue inspector attribute injection via Vue compiler node transform
🐞 Bug fix ✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

Description

• Add a Vue compiler-dom nodeTransform to inject data-insp-path at AST level.
• Auto-register the transform into vue-loader/templateLoader and skip redundant loader transforms.
• Extend type exports and add tests covering transform registration and injection behavior.
Diagram

graph TD
  plugin["WebpackCodeInspectorPlugin"] -->|"patch vue-loader compilerOptions"| vloader["vue-loader/templateLoader"] -->|"compile template"| vcompiler["@vue/compiler-dom"] --> ntransform["Vue inspector nodeTransform"] --> bundle["render code w/ data-insp-path"]
  plugin -->|"add code-inspector loader"| loader["Webpack loader"] -->|"non-Vue / scripts"| tcode["transformCode"] --> bundle
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Keep string-based Vue template injection only (MagicString)
  • ➕ No webpack/vue-loader coupling; works in any pipeline that passes template source through loader
  • ➕ No reliance on Vue compiler AST node types
  • ➖ Brittle against vue-loader/templateLoader changes and query variants (root of production使用会导致vue scoped css有问题 #547-style issues)
  • ➖ Harder to avoid double-injection and source/offset mismatches across compilation steps
2. Integrate via Vue compiler plugins in vue-loader config (user-land)
  • ➕ Cleaner separation: users explicitly opt in and control compilerOptions
  • ➕ Avoids plugin walking/patching webpack rules automatically
  • ➖ Requires user config changes; not a drop-in fix
  • ➖ Ecosystem fragmentation: different bundlers/configs need different setup
3. Inject via Vue directive/compile-time transform instead of nodeTransforms
  • ➕ Could leverage Vue’s higher-level transform hooks for specific nodes/attrs
  • ➖ Less direct for adding raw attributes to all element nodes
  • ➖ May not cover all compiled node shapes as reliably as nodeTransforms

Recommendation: Proceed with the nodeTransform approach: it aligns with Vue’s compilation model, avoids fragile string edits, and fixes cases where loader-level template transforms are bypassed/altered. Keep the loader skip flag (vueCompilerNodeTransform) to prevent double work/injection, and ensure the escapeTags + existing-attribute checks remain consistent with the legacy transform path.

Files changed (11) +392 / -29

Enhancement (2) +6 / -2
index.tsRe-export Vue node transform from core server entry +5/-2

Re-export Vue node transform from core server entry

• Updates server entry exports to include createVueInspectorNodeTransform alongside transformCode. Also normalizes the export style and ensures server exports are included.

packages/core/src/server/index.ts

index.tsExport Vue compiler AST node transform from transform index +1/-0

Export Vue compiler AST node transform from transform index

• Exposes createVueInspectorNodeTransform from the transform barrel to make it consumable by bundler integrations (e.g., webpack plugin).

packages/core/src/server/transform/index.ts

Bug fix (3) +225 / -2
vue-node-transform.tsAdd Vue compiler-dom nodeTransform to inject data-insp-path +114/-0

Add Vue compiler-dom nodeTransform to inject data-insp-path

• Introduces an AST-level nodeTransform that injects the inspector attribute onto element nodes during Vue compilation. Respects escape tags, mappings/pathType, and avoids double-injection when the attribute already exists.

packages/core/src/server/transform/vue-node-transform.ts

index.tsRegister Vue compiler nodeTransforms via webpack plugin and set skip flag +108/-2

Register Vue compiler nodeTransforms via webpack plugin and set skip flag

• Adds logic to walk webpack module rules, detect vue-loader/templateLoader, and inject createVueInspectorNodeTransform into compilerOptions.nodeTransforms exactly once. Passes a vueCompilerNodeTransform flag into the code-inspector loader options and hardens cached-port fallback to default to 0.

packages/webpack/src/index.ts

loader.tsSkip Vue template transform when compiler nodeTransform is enabled +3/-0

Skip Vue template transform when compiler nodeTransform is enabled

• When vueCompilerNodeTransform is true and the resource is a Vue template (SFC template or vue HTML template), returns original content to avoid duplicate/incorrect injection. Keeps existing behavior for other file types and Vue script JSX cases.

packages/webpack/src/loader.ts

Tests (3) +147 / -23
transform-vue.test.tsAdd tests for createVueInspectorNodeTransform injection/escape behavior +52/-0

Add tests for createVueInspectorNodeTransform injection/escape behavior

• Adds compiler-dom compile-based tests asserting injected data-insp-path output without mutating source strings. Verifies escapeTags support and that existing inspector attributes are not overwritten/duplicated.

test/core/server/transform/transform-vue.test.ts

index.test.tsTest vue-loader compilerOptions nodeTransforms registration from plugin +48/-1

Test vue-loader compilerOptions nodeTransforms registration from plugin

• Mocks createVueInspectorNodeTransform and asserts the plugin registers a single nodeTransform into vue-loader options. Also verifies the loader rule receives vueCompilerNodeTransform=true and adjusts isDev mock behavior for function inputs.

test/webpack/index.test.ts

loader.test.tsAdd loader test for skipping Vue template transforms when flag enabled +47/-22

Add loader test for skipping Vue template transforms when flag enabled

• Refactors loader invocation into a helper and adds coverage ensuring Vue template content is returned unchanged when vueCompilerNodeTransform is set. Updates existing tests to use the helper consistently.

test/webpack/loader.test.ts

Other (3) +14 / -2
index.d.tsUpdate type exports to include createVueInspectorNodeTransform +1/-1

Update type exports to include createVueInspectorNodeTransform

• Updates generated type declarations so consumers can import createVueInspectorNodeTransform from the server entrypoint.

packages/core/types/server/index.d.ts

index.d.tsExpose Vue node transform in transform type declarations +1/-1

Expose Vue node transform in transform type declarations

• Adds an explicit export for vue-node-transform typings and removes the empty export artifact.

packages/core/types/server/transform/index.d.ts

vue-node-transform.d.tsAdd TypeScript declarations for Vue inspector node transform +12/-0

Add TypeScript declarations for Vue inspector node transform

• Defines the options type (escapeTags/pathType/mappings) and declares createVueInspectorNodeTransform returning a NodeTransform.

packages/core/types/server/transform/vue-node-transform.d.ts

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (1) 📜 Skill insights (0)

Context used

Grey Divider


Remediation recommended

1. vue-loader string use ignored 📎 Requirement gap ☼ Reliability
Description
applyVueCompilerNodeTransform() fails to register Vue compiler nodeTransforms for common
webpack/rspack rule shapes where the matched vue-loader entry is a string in rule.use (and may
also miss non-array use objects), causing the code to fall back to the old pre-loader
transformCode template rewrite path. This risks production template/style scoped scopeId
desynchronization and inconsistent data-insp-path injection when the plugin is enabled.
Code

packages/webpack/src/index.ts[R69-81]

+  walkRules(rules, (rule) => {
+    getUseItems(rule).forEach((item) => {
+      const loader = typeof item === 'string' ? item : item?.loader;
+      if (
+        typeof loader !== 'string' ||
+        (!isVueLoader(loader) && !isVueTemplateLoader(loader))
+      ) {
+        return;
+      }
+
+      if (typeof item === 'string') {
+        return;
+      }
Evidence
Compliance ID 3 requires avoiding pre-loader template mutations because they can desynchronize
template vs style scoped-ID computation, but the current rule scanning/transform registration logic
only reliably handles use arrays or loader fields; when it encounters a vue-loader item
expressed as a string in rule.use, it returns early (if (typeof item === 'string') return;)
instead of injecting compilerOptions.nodeTransforms. Since the loader’s behavior hinges on whether
vueCompilerNodeTransform is enabled (it only skips transformCode(...fileType:'vue') when the
compiler node transform is registered), these unhandled rule shapes keep vueCompilerNodeTransform
false and therefore continue to use the legacy source-rewriting path that the change is trying to
avoid for production correctness.

Loader ordering with rspack-vue-loader: pre-loader transforms must not desynchronize template vs style scope ID computation
packages/webpack/src/index.ts[69-81]
packages/webpack/src/loader.ts[82-93]
packages/webpack/src/index.ts[27-41]
packages/webpack/src/index.ts[63-122]
packages/webpack/src/loader.ts[72-94]
packages/core/src/server/transform/transform-vue.ts[73-140]

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

## Issue description
`applyVueCompilerNodeTransform()` does not reliably register `compilerOptions.nodeTransforms` for `vue-loader` across all valid webpack/rspack rule shapes: it early-returns when the matched loader entry is a string in `rule.use`, and the current helper logic only returns entries when `rule.use` is an array or `rule.loader` exists (missing cases like `use: 'vue-loader'` and potentially `use: { loader, options }`). As a result, the intended compiler-AST injection path is not applied in these configurations and the loader continues to run the legacy `transformCode` template rewrite, which compliance wants to avoid to prevent production scoped-CSS/template scopeId mismatches and to keep `data-insp-path` injection consistent.

## Issue Context
- Compliance requires Vue template vs style scope ID computation to remain synchronized in production builds when the plugin is enabled, and to avoid pre-loader template modifications that can cause desync.
- The implementation currently only applies the compiler transform when it can find a `vue-loader` entry with mutable `options` to inject `compilerOptions.nodeTransforms`.
- When the compiler node transform is not registered, `vueCompilerNodeTransform` remains `false` and the loader continues to call `transformCode(...fileType: 'vue')`, which mutates the template source (the behavior this change is meant to avoid).
- Webpack rules can declare loaders not only via `use: [...]` but also via `use: 'vue-loader'` and `use: { loader: 'vue-loader', options: ... }`, which should be handled so the transform is consistently registered.

## Fix Focus Areas
- packages/webpack/src/index.ts[27-41]
- packages/webpack/src/index.ts[63-122]
- packages/webpack/src/index.ts[69-81]
- packages/webpack/src/loader.ts[82-93]

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


2. Directive path not detected 🐞 Bug ≡ Correctness
Description
hasInspectorPath() only treats static AttributeNode props as “existing”, so
createVueInspectorNodeTransform() can still inject a second data-insp-path when the template
already has :data-insp-path / v-bind:data-insp-path. This can create duplicate/overridden
inspector attributes and diverges from the previous source-based guard that skipped injection
whenever PathName appeared in the element source.
Code

packages/core/src/server/transform/vue-node-transform.ts[R47-51]

+function hasInspectorPath(node: ElementNode) {
+  return node.props.some(
+    (prop) => prop.type === VueAttributeType && prop.name === PathName,
+  );
+}
Evidence
The new transform only checks for static AttributeNode props, while the previous transform avoided
injection if the element source contained PathName, which would also catch directive/binding
syntax.

packages/core/src/server/transform/vue-node-transform.ts[47-51]
packages/core/src/server/transform/vue-node-transform.ts[95-113]
packages/core/src/server/transform/transform-vue.ts[120-136]

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

### Issue description
`createVueInspectorNodeTransform()` injects `data-insp-path` based on `hasInspectorPath()`, but that check only matches static attributes (`AttributeNode`). If a template already contains a bound/directive form like `:data-insp-path="..."`, the transform will inject another static `data-insp-path`, causing duplicate/overridden props.

### Issue Context
The prior Vue transform (`transformVueTemplate`) prevented duplicates by skipping injection when `node.loc.source` contained `PathName` anywhere, which covered both static attributes and directive/binding syntax.

### Fix Focus Areas
- packages/core/src/server/transform/vue-node-transform.ts[47-51]
- packages/core/src/server/transform/vue-node-transform.ts[95-112]

### Suggested fix
- Extend `hasInspectorPath()` to also detect directive props that target `data-insp-path` (e.g., `prop.type === DIRECTIVE` and `prop.arg?.content === PathName`, handling `v-bind`/`:` forms).
- Optionally add a conservative fallback guard similar to the old logic (e.g., `node.loc.source.includes(PathName)`) to avoid missing other syntaxes that still result in `data-insp-path` being present.

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


Grey Divider

Qodo Logo

@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.37931% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 99.73%. Comparing base (ea5e6d3) to head (74974b9).

Files with missing lines Patch % Lines
packages/webpack/src/index.ts 83.14% 15 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##              main     #548      +/-   ##
===========================================
- Coverage   100.00%   99.73%   -0.27%     
===========================================
  Files           26       27       +1     
  Lines         5579     5750     +171     
  Branches      1547     1589      +42     
===========================================
+ Hits          5579     5735     +156     
- Misses           0       15      +15     

☔ View full report in Codecov by Harness.
📢 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 zh-lx changed the base branch from main to pre-main June 28, 2026 15:32
@zh-lx zh-lx merged commit d7daf01 into zh-lx:pre-main Jun 28, 2026
2 of 4 checks passed
@zh-lx

zh-lx commented Jun 29, 2026

Copy link
Copy Markdown
Owner

@lejunyang Thanks for your PR, merged and released on 1.6.3

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.

production使用会导致vue scoped css有问题

2 participants