Skip to content

fix(compiler): share logic for comments and whitespace #13550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

skirtles-code
Copy link
Contributor

@skirtles-code skirtles-code commented Jul 1, 2025

This was intended as a refactoring, reusing logic for detecting comments and whitespace, but as it makes small modifications to how templates are compiled (especially for non-breaking spaces) I've marked it as a fix.

This PR only targets the compiler. I think similar changes should be made to the runtime, but those can be added later.

Background

While reviewing other PRs, I noticed a common pattern. There are a lot of places where we need a single child/root node, but we typically need to skip comments and whitespace text to find the node we want. Each place where this logic occurs implements it separately and I wanted to introduce some helper functions to reduce the duplication. Hopefully that will help to reduce inconsistencies, bugs and missed edge cases too.

There are a few common sources of problems:

  1. Not considering comments at all.
  2. Assuming comments are removed in production builds. This used to be true, but is now configurable.
  3. Not accounting for whitespace with whitespace: 'preserve'. This is closely related to handling comments as, in several cases, comments will also introduce whitespace text nodes that would otherwise be stripped.

We also have an inconsistent definition of 'whitespace'.

Both trim() and /\s/ will treat many different characters as whitespace, including non-breaking spaces. For our purposes, this isn't really what we want, but we get away with it in practice.

The template parser includes a function isWhitespace that only considers 5 characters to be whitespace: space, tab, newline, carriage return and form feed. From the perspective of collapsing and discarding whitespace nodes, this is the appropriate definition. It accounts for the characters that are typically used to format HTML code and that shouldn't be treated as meaningful content.

A key part of this 'refactoring' is that trim() is no longer used to identify whitespace text, with isWhitespace being preferred instead. The result is that some whitespace characters, most notably non-breaking space, will no longer be ignored in some contexts where they were being ignored previously. Non-breaking spaces will be treated just like any other normal text character.

Examples

The examples below illustrate cases where something has changed in how this PR handles non-breaking spaces.

I don't think anyone will be intentionally using non-breaking spaces in the ways that are affected. If anyone is using them in these edge cases then it seems much more likely they've been included by accident and nobody noticed because the compiler ignored them. I believe these cases are rare, and it should be trivial to fix for anyone impacted.

The examples below use   for a non-breaking space, but it could also be included directly as a character, it doesn't need to be encoded as an entity.

v-if

This code would previously compile successfully, discarding the non-breaking space. With the changes in this PR it will throw an error, just as it would for non-whitespace text:

<div v-if="foo"></div>
&nbsp;
<div v-else></div>

v-slot

<MyComponent>
  &nbsp;
  <template #header>
    ...
  </template>
</MyComponent>

Previously, the &nbsp; would have been discarded. After this PR it will be treated as content of the default slot.

<Transition>

Similarly, this will now fail to compile because the non-breaking space will be considered a child node:

<Transition>
  &nbsp;
  <component :is="foo" />
</Transition>

Previously, the non-breaking space would have just been discarded.

Testing

The existing tests should all pass.

The tests I've added cover a variety of cases, many of which were already working previously. The tests that mention non-breaking spaces are typically testing an actual change.

Some parts of this are a bit tricky to test effectively, as they require specific combinations of compiler options and transforms. In particular, testing cases involving TEXT_CALL nodes, which are only created by transformText.

Related PRs

There are various open PRs that attempt to fix problems related to the handling of comments and whitespace nodes. In particular, these two may benefit from using the utility functions added in this PR:

Summary by CodeRabbit

  • New Features

    • Enhanced compiler utilities with new functions to detect whitespace and comment nodes in templates.
  • Bug Fixes

    • Improved handling of whitespace and comments in slot and transition components to ensure correct parsing and warnings.
  • Tests

    • Added comprehensive test cases for whitespace and non-breaking space handling in text nodes, slots, conditional branches, and transition components.
  • Refactor

    • Unified and simplified internal logic for identifying whitespace and comment nodes across compiler modules.

Copy link

coderabbitai bot commented Jul 1, 2025

Walkthrough

This update introduces new utility functions for identifying whitespace and comment nodes in the template AST, refactors several core compiler modules and tests to use these utilities, and adds comprehensive tests for whitespace handling in text, slot, and transition transformations. No changes to public APIs or exported entities were made.

Changes

File(s) Change Summary
.../src/utils.ts Added isAllWhitespace, isWhitespaceText, and isCommentOrWhitespace utility functions.
.../src/parser.ts Replaced local isAllWhitespace with imported version from utils.
.../src/transforms/vIf.ts Refactored logic to use isCommentOrWhitespace for filtering siblings in v-if branches.
.../src/transforms/vSlot.ts Switched to using isCommentOrWhitespace and isWhitespaceText for slot node filtering; removed local helper.
.../src/transforms/Transition.ts Updated child node filtering to use isCommentOrWhitespace in hasMultipleChildren.
.../transforms/transformText.spec.ts Added test for whitespace text node handling and isWhitespaceText utility validation.
.../transforms/vIf.spec.ts Added tests for error reporting when v-else/v-else-if are not adjacent, including whitespace-separated cases.
.../transforms/vSlot.spec.ts Added tests for non-breaking space handling in slots; updated helper to accept transformText flag.
.../transforms/Transition.spec.ts Added tests for non-breaking space and comment/whitespace handling in <transition> children.

Sequence Diagram(s)

sequenceDiagram
    participant Parser
    participant Utils
    participant Transformer

    Parser->>Utils: isAllWhitespace(str)
    Transformer->>Utils: isCommentOrWhitespace(node)
    Transformer->>Utils: isWhitespaceText(node)
    Note over Transformer,Utils: Utilities determine node type for whitespace/comment handling
Loading

Suggested labels

scope: compiler, :hammer: p3-minor-bug

Suggested reviewers

  • edison1105

Poem

In the land of code where whitespace hides,
A rabbit hops and peeks inside.
With new-found tools to spot the space,
It trims and tests with gentle grace.
Now comments, gaps, and lines so light—
Are handled cleanly, day and night!
🐇✨


📜 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 ba391f5 and 6b8f1f2.

⛔ Files ignored due to path filters (2)
  • packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap is excluded by !**/*.snap
  • packages/compiler-dom/__tests__/transforms/__snapshots__/Transition.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (9)
  • packages/compiler-core/__tests__/transforms/transformText.spec.ts (2 hunks)
  • packages/compiler-core/__tests__/transforms/vIf.spec.ts (3 hunks)
  • packages/compiler-core/__tests__/transforms/vSlot.spec.ts (5 hunks)
  • packages/compiler-core/src/parser.ts (1 hunks)
  • packages/compiler-core/src/transforms/vIf.ts (2 hunks)
  • packages/compiler-core/src/transforms/vSlot.ts (3 hunks)
  • packages/compiler-core/src/utils.ts (2 hunks)
  • packages/compiler-dom/__tests__/transforms/Transition.spec.ts (2 hunks)
  • packages/compiler-dom/src/transforms/Transition.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
packages/compiler-core/src/transforms/vIf.ts (1)
packages/compiler-core/src/utils.ts (1)
  • isCommentOrWhitespace (585-587)
packages/compiler-core/src/transforms/vSlot.ts (1)
packages/compiler-core/src/utils.ts (2)
  • isCommentOrWhitespace (585-587)
  • isWhitespaceText (578-583)
packages/compiler-core/__tests__/transforms/transformText.spec.ts (1)
packages/compiler-core/src/utils.ts (1)
  • isWhitespaceText (578-583)
packages/compiler-core/src/utils.ts (2)
packages/compiler-core/src/tokenizer.ts (1)
  • isWhitespace (151-159)
packages/compiler-core/src/ast.ts (1)
  • TemplateChildNode (93-102)
packages/compiler-dom/src/transforms/Transition.ts (1)
packages/compiler-core/src/utils.ts (1)
  • isCommentOrWhitespace (585-587)
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: test / e2e-test
  • GitHub Check: test / unit-test-windows
  • GitHub Check: Redirect rules
  • GitHub Check: Header rules
  • GitHub Check: Pages changed
🔇 Additional comments (23)
packages/compiler-core/src/parser.ts (1)

43-43: LGTM: Clean centralization of utility function.

The import of isAllWhitespace from ./utils aligns perfectly with the PR objective of centralizing whitespace detection logic. This reduces code duplication and improves maintainability.

packages/compiler-dom/src/transforms/Transition.ts (2)

7-7: Good addition of centralized utility import.

Adding isCommentOrWhitespace import supports the PR's goal of centralizing whitespace and comment detection logic across the compiler.


60-63: Excellent refactoring with proper issue references.

The replacement of manual filtering logic with isCommentOrWhitespace utility function is a solid improvement that:

This change directly supports the PR objective of reducing duplicated logic.

packages/compiler-dom/__tests__/transforms/Transition.spec.ts (2)

139-149: Excellent test coverage for non-breaking space handling.

This test correctly verifies that non-breaking spaces (\u00a0) are treated as normal text nodes in <transition> components, which should trigger the multiple children warning. This directly validates the behavior change described in the PR objectives where non-breaking spaces are no longer treated as whitespace.


180-194: Good test for comments and preserved whitespace behavior.

This test ensures that HTML comments and preserved whitespace are properly ignored during <transition> compilation, validating the filtering logic implemented with isCommentOrWhitespace. The snapshot-based verification provides comprehensive coverage of the generated code.

packages/compiler-core/src/transforms/vIf.ts (2)

35-41: Clean import organization for centralized utilities.

The addition of isCommentOrWhitespace to the imports supports the refactoring goal of centralizing whitespace and comment detection logic.


134-139: Excellent simplification while preserving functionality.

The replacement of manual checks with isCommentOrWhitespace(sibling) is a great improvement that:

  • Standardizes the filtering logic across the compiler
  • Simplifies the conditional expression
  • Preserves the development-mode comment collection behavior
  • Maintains the same adjacency detection logic for v-if/v-else branches

This change directly addresses the PR objective of reducing duplicated logic.

packages/compiler-core/__tests__/transforms/vIf.spec.ts (2)

250-273: Comprehensive test coverage for v-else adjacency errors.

These additional test cases provide excellent coverage for scenarios where v-else is not adjacent to v-if due to intervening content:

  • Plain text between v-if and v-else
  • Non-breaking space characters between branches

The test properly validates that the new whitespace handling logic correctly identifies these as error conditions, with proper error code assertions.


313-336: Thorough validation of v-else-if error scenarios.

The extended test cases for v-else-if adjacency errors mirror the v-else tests and provide comprehensive coverage including:

  • Text content separating conditional branches
  • Non-breaking space characters as separators

The error call indices are correctly updated ([3] and [4]) to account for the additional test cases, ensuring proper test isolation and verification.

packages/compiler-core/__tests__/transforms/transformText.spec.ts (2)

7-7: Good addition of utility import.

The import of isWhitespaceText aligns with the PR objective to standardize whitespace detection across the compiler.


113-129: Excellent test coverage for whitespace text detection.

This test effectively validates the new whitespace detection utility by:

  • Testing a realistic scenario with mixed content and whitespace text nodes
  • Verifying the correct number of child nodes are generated
  • Using isWhitespaceText to confirm which nodes are identified as whitespace

The test template <div/>hello<div/> <div/> provides good coverage for both content text and whitespace text handling.

packages/compiler-core/src/utils.ts (4)

45-45: Appropriate import of whitespace checking utility.

Importing isWhitespace from the tokenizer ensures consistent whitespace character detection across the compiler.


569-576: Well-implemented whitespace string checker.

The function correctly uses the tokenizer's isWhitespace function to check each character, ensuring consistency with the parser's whitespace definition. This replaces the previous use of trim() as mentioned in the PR objectives.


578-583: Comprehensive whitespace text node detection.

The function properly handles both scenarios:

  • TEXT nodes with all-whitespace content
  • TEXT_CALL nodes with recursive content checking

The recursive approach for TEXT_CALL nodes is a good design choice that ensures deep whitespace detection.


585-587: Clean utility for comment and whitespace filtering.

This function provides a convenient way to identify nodes that should typically be skipped during processing, combining comment nodes with whitespace-only text nodes.

packages/compiler-core/src/transforms/vSlot.ts (3)

29-29: Good adoption of standardized utilities.

The imports of isCommentOrWhitespace and isWhitespaceText align with the PR objective to centralize whitespace and comment detection logic across the compiler.

Also applies to: 33-33


227-227: Cleaner backward search logic.

Replacing the custom condition with !isCommentOrWhitespace(prev) improves readability and consistency while maintaining the same logical behavior for skipping comments and whitespace nodes.


324-324: Improved implicit default children filtering.

The change from some(node => isNonWhitespaceContent(node)) to !implicitDefaultChildren.every(isWhitespaceText) is logically equivalent but uses the standardized utility function. This maintains the same behavior while improving consistency across the codebase.

packages/compiler-core/__tests__/transforms/vSlot.spec.ts (5)

31-31: Appropriate import for text transformation testing.

Adding the transformText import enables testing scenarios where text nodes need to be transformed, which is relevant for the new whitespace handling functionality.


33-36: Good enhancement to test helper function.

The addition of the transformText option to parseWithSlots allows for more comprehensive testing of slot transformations when text processing is involved. The conditional inclusion of the transform is well-implemented.

Also applies to: 50-50


315-347: Excellent test for non-breaking space handling.

This test verifies that non-breaking spaces (\u00a0) are now treated as content rather than whitespace, which aligns with the PR objective. The test correctly expects both the named slot and a default slot containing the non-breaking space content.


1031-1050: Comprehensive test for non-breaking space with whitespace preservation.

This test ensures that non-breaking spaces are properly handled even when whitespace: 'preserve' is enabled. The verification that a default slot is created demonstrates the correct behavior change from the new whitespace detection logic.


1066-1082: Good test coverage for conditional slots with comments.

This test verifies that the new comment and whitespace detection utilities work correctly in conditional slot scenarios with interspersed comments. The inclusion of transformText: true ensures the text transformation pipeline is properly tested.

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Jul 1, 2025

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 101 kB 38.3 kB 34.5 kB
vue.global.prod.js 159 kB (-35 B) 58.5 kB (-2 B) 52 kB (-28 B)

Usages

Name Size Gzip Brotli
createApp (CAPI only) 46.5 kB 18.2 kB 16.7 kB
createApp 54.5 kB 21.2 kB 19.4 kB
createSSRApp 58.7 kB 22.9 kB 20.9 kB
defineCustomElement 59.4 kB 22.8 kB 20.8 kB
overall 68.5 kB 26.4 kB 24 kB

Copy link

pkg-pr-new bot commented Jul 1, 2025

Open in StackBlitz

@vue/compiler-core

npm i https://pkg.pr.new/@vue/compiler-core@13550

@vue/compiler-dom

npm i https://pkg.pr.new/@vue/compiler-dom@13550

@vue/compiler-sfc

npm i https://pkg.pr.new/@vue/compiler-sfc@13550

@vue/compiler-ssr

npm i https://pkg.pr.new/@vue/compiler-ssr@13550

@vue/reactivity

npm i https://pkg.pr.new/@vue/reactivity@13550

@vue/runtime-core

npm i https://pkg.pr.new/@vue/runtime-core@13550

@vue/runtime-dom

npm i https://pkg.pr.new/@vue/runtime-dom@13550

@vue/server-renderer

npm i https://pkg.pr.new/@vue/server-renderer@13550

@vue/shared

npm i https://pkg.pr.new/@vue/shared@13550

vue

npm i https://pkg.pr.new/vue@13550

@vue/compat

npm i https://pkg.pr.new/@vue/compat@13550

commit: 6b8f1f2

@edison1105
Copy link
Member

/ecosystem-ci run

@edison1105 edison1105 added ready to merge The PR is ready to be merged. scope: compiler 🍰 p2-nice-to-have Priority 2: this is not breaking anything but nice to have it addressed. labels Jul 2, 2025
@vue-bot
Copy link
Contributor

vue-bot commented Jul 2, 2025

📝 Ran ecosystem CI: Open

suite result latest scheduled
pinia success success
nuxt failure failure
language-tools success success
primevue success success
radix-vue failure success
test-utils success success
quasar success success
vue-macros success failure
vant success failure
vueuse success success
vue-i18n success success
vitepress success success
vue-simple-compiler success success
vite-plugin-vue success success
router success success
vuetify success success

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🍰 p2-nice-to-have Priority 2: this is not breaking anything but nice to have it addressed. ready to merge The PR is ready to be merged. scope: compiler
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants