From 8e6b4a5db9f0ddfb592a5f29466eb1676f3b62a4 Mon Sep 17 00:00:00 2001 From: Eugene Kashida Date: Tue, 5 Mar 2024 13:44:22 -0800 Subject: [PATCH] chore: avoid discarding text content whitespace (#4029) --- .../valid/text-node-adjacency/ast.json | 144 ++++++++++++++++++ .../valid/text-node-adjacency/expected.js | 22 ++- .../template-compiler/src/parser/index.ts | 18 ++- 3 files changed, 176 insertions(+), 8 deletions(-) diff --git a/packages/@lwc/template-compiler/src/__tests__/fixtures/expression-complex/valid/text-node-adjacency/ast.json b/packages/@lwc/template-compiler/src/__tests__/fixtures/expression-complex/valid/text-node-adjacency/ast.json index 8699fb47f6..1eebce1650 100644 --- a/packages/@lwc/template-compiler/src/__tests__/fixtures/expression-complex/valid/text-node-adjacency/ast.json +++ b/packages/@lwc/template-compiler/src/__tests__/fixtures/expression-complex/valid/text-node-adjacency/ast.json @@ -207,6 +207,22 @@ "start": 67, "end": 79 } + }, + { + "type": "Text", + "raw": " ", + "value": { + "type": "Literal", + "value": " " + }, + "location": { + "startLine": 4, + "startColumn": 26, + "endLine": 4, + "endColumn": 27, + "start": 79, + "end": 80 + } } ] }, @@ -243,6 +259,22 @@ "directives": [], "listeners": [], "children": [ + { + "type": "Text", + "raw": " ", + "value": { + "type": "Literal", + "value": " " + }, + "location": { + "startLine": 5, + "startColumn": 14, + "endLine": 5, + "endColumn": 15, + "start": 100, + "end": 101 + } + }, { "type": "Text", "raw": "{spaceLeft}", @@ -433,6 +465,22 @@ "directives": [], "listeners": [], "children": [ + { + "type": "Text", + "raw": " ", + "value": { + "type": "Literal", + "value": " " + }, + "location": { + "startLine": 7, + "startColumn": 14, + "endLine": 7, + "endColumn": 15, + "start": 162, + "end": 163 + } + }, { "type": "Text", "raw": "{one}", @@ -627,6 +675,22 @@ "start": 198, "end": 203 } + }, + { + "type": "Text", + "raw": " ", + "value": { + "type": "Literal", + "value": " " + }, + "location": { + "startLine": 8, + "startColumn": 24, + "endLine": 8, + "endColumn": 25, + "start": 203, + "end": 204 + } } ] }, @@ -703,6 +767,22 @@ "end": 229 } }, + { + "type": "Text", + "raw": " ", + "value": { + "type": "Literal", + "value": " " + }, + "location": { + "startLine": 9, + "startColumn": 19, + "endLine": 9, + "endColumn": 20, + "start": 229, + "end": 230 + } + }, { "type": "Text", "raw": "{two}", @@ -778,6 +858,22 @@ "directives": [], "listeners": [], "children": [ + { + "type": "Text", + "raw": " ", + "value": { + "type": "Literal", + "value": " " + }, + "location": { + "startLine": 10, + "startColumn": 14, + "endLine": 10, + "endColumn": 15, + "start": 255, + "end": 256 + } + }, { "type": "Text", "raw": "{one}", @@ -818,6 +914,22 @@ "end": 261 } }, + { + "type": "Text", + "raw": " ", + "value": { + "type": "Literal", + "value": " " + }, + "location": { + "startLine": 10, + "startColumn": 20, + "endLine": 10, + "endColumn": 21, + "start": 261, + "end": 262 + } + }, { "type": "Text", "raw": "{two}", @@ -933,6 +1045,22 @@ "end": 292 } }, + { + "type": "Text", + "raw": " ", + "value": { + "type": "Literal", + "value": " " + }, + "location": { + "startLine": 11, + "startColumn": 19, + "endLine": 11, + "endColumn": 20, + "start": 292, + "end": 293 + } + }, { "type": "Text", "raw": "{two}", @@ -972,6 +1100,22 @@ "start": 293, "end": 298 } + }, + { + "type": "Text", + "raw": " ", + "value": { + "type": "Literal", + "value": " " + }, + "location": { + "startLine": 11, + "startColumn": 25, + "endLine": 11, + "endColumn": 26, + "start": 298, + "end": 299 + } } ] } diff --git a/packages/@lwc/template-compiler/src/__tests__/fixtures/expression-complex/valid/text-node-adjacency/expected.js b/packages/@lwc/template-compiler/src/__tests__/fixtures/expression-complex/valid/text-node-adjacency/expected.js index 46c8438e46..6b52417268 100644 --- a/packages/@lwc/template-compiler/src/__tests__/fixtures/expression-complex/valid/text-node-adjacency/expected.js +++ b/packages/@lwc/template-compiler/src/__tests__/fixtures/expression-complex/valid/text-node-adjacency/expected.js @@ -34,25 +34,33 @@ function tmpl($api, $cmp, $slotset, $ctx) { return [ api_element("section", stc0, [ api_element("div", stc1, [api_text(api_dynamic_text($cmp.noSpace))]), - api_element("div", stc2, [api_text(api_dynamic_text($cmp.spaceRight))]), - api_element("div", stc3, [api_text(api_dynamic_text($cmp.spaceLeft))]), + api_element("div", stc2, [ + api_text(api_dynamic_text($cmp.spaceRight) + " "), + ]), + api_element("div", stc3, [ + api_text(" " + api_dynamic_text($cmp.spaceLeft)), + ]), api_element("div", stc4, [ api_text(api_dynamic_text($cmp.one) + api_dynamic_text($cmp.two)), ]), api_element("div", stc5, [ - api_text(api_dynamic_text($cmp.one) + api_dynamic_text($cmp.two)), + api_text(" " + api_dynamic_text($cmp.one) + api_dynamic_text($cmp.two)), ]), api_element("div", stc6, [ - api_text(api_dynamic_text($cmp.one) + api_dynamic_text($cmp.two)), + api_text(api_dynamic_text($cmp.one) + api_dynamic_text($cmp.two) + " "), ]), api_element("div", stc7, [ - api_text(api_dynamic_text($cmp.one) + api_dynamic_text($cmp.two)), + api_text(api_dynamic_text($cmp.one) + " " + api_dynamic_text($cmp.two)), ]), api_element("div", stc8, [ - api_text(api_dynamic_text($cmp.one) + api_dynamic_text($cmp.two)), + api_text( + " " + api_dynamic_text($cmp.one) + " " + api_dynamic_text($cmp.two) + ), ]), api_element("div", stc9, [ - api_text(api_dynamic_text($cmp.one) + api_dynamic_text($cmp.two)), + api_text( + api_dynamic_text($cmp.one) + " " + api_dynamic_text($cmp.two) + " " + ), ]), ]), ]; diff --git a/packages/@lwc/template-compiler/src/parser/index.ts b/packages/@lwc/template-compiler/src/parser/index.ts index 340a04f760..a35e666dcf 100644 --- a/packages/@lwc/template-compiler/src/parser/index.ts +++ b/packages/@lwc/template-compiler/src/parser/index.ts @@ -467,7 +467,23 @@ function parseText(ctx: ParserCtx, parse5Text: parse5Tools.TextNode): Text[] { // Extract the raw source to avoid HTML entity decoding done by parse5 const rawText = cleanTextNode(ctx.getSource(location.startOffset, location.endOffset)); - if (!rawText.trim().length) { + /* + The original job of this if-block was to discard the whitespace between HTML tags, HTML + comments, and HTML tags and HTML comments. The whitespace inside the text content of HTML tags + would never be considered here because they would not be parsed into individual text nodes until + later (several lines below). + + ["Hello {first} {last}!"] => ["Hello ", "{first}", " ", "{last}", "!"] + + With the implementation of complex template expressions, whitespace that shouldn't be discarded + has already been parsed into individual text nodes at this point so we only discard when + experimentalComplexExpressions is disabled. + + When removing the experimentalComplexExpressions flag, we need to figure out how to best discard + the HTML whitespace while preserving text content whitespace, while also taking into account how + comments are sometimes preserved (in which case we need to keep the HTML whitespace). + */ + if (!rawText.trim().length && !ctx.config.experimentalComplexExpressions) { return parsedTextNodes; }