-
Notifications
You must be signed in to change notification settings - Fork 426
fix(template-compiler): fix static td/th/etc elements #2888
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
Changes from all commits
c9d63ff
5b662df
3ecf6bd
24ce951
730db04
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| <template> | ||
| <table> | ||
| <thead> | ||
| <template for:each={rows} for:item="row"> | ||
| <tr key={row.id}> | ||
| <th></th> | ||
| </tr> | ||
| </template> | ||
| </thead> | ||
| <tbody> | ||
| <template for:each={rows} for:item="row"> | ||
| <tr key={row.id}> | ||
| <td></td> | ||
| </tr> | ||
| </template> | ||
| </tbody> | ||
| </table> | ||
| </template> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import { registerTemplate } from "lwc"; | ||
| const stc0 = { | ||
| key: 0, | ||
| }; | ||
| const stc1 = { | ||
| key: 1, | ||
| }; | ||
| const stc2 = { | ||
| key: 3, | ||
| }; | ||
| const stc3 = { | ||
| key: 4, | ||
| }; | ||
| const stc4 = { | ||
| key: 6, | ||
| }; | ||
| function tmpl($api, $cmp, $slotset, $ctx) { | ||
| const { k: api_key, h: api_element, i: api_iterator } = $api; | ||
| return [ | ||
| api_element("table", stc0, [ | ||
| api_element( | ||
| "thead", | ||
| stc1, | ||
| api_iterator($cmp.rows, function (row) { | ||
| return api_element( | ||
| "tr", | ||
| { | ||
| key: api_key(2, row.id), | ||
| }, | ||
| [api_element("th", stc2)] | ||
| ); | ||
| }) | ||
| ), | ||
| api_element( | ||
| "tbody", | ||
| stc3, | ||
| api_iterator($cmp.rows, function (row) { | ||
| return api_element( | ||
| "tr", | ||
| { | ||
| key: api_key(5, row.id), | ||
| }, | ||
| [api_element("td", stc4)] | ||
| ); | ||
| }) | ||
| ), | ||
| ]), | ||
| ]; | ||
| /*LWC compiler vX.X.X*/ | ||
| } | ||
| export default registerTemplate(tmpl); | ||
| tmpl.stylesheets = []; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "warnings": [] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ import * as astring from 'astring'; | |
| import { isBooleanAttribute, SVG_NAMESPACE, LWC_VERSION_COMMENT } from '@lwc/shared'; | ||
| import { generateCompilerError, TemplateErrors } from '@lwc/errors'; | ||
|
|
||
| import { isUnsafeTopLevelSerializableElement } from '../parser/tag'; | ||
| import { | ||
| isComment, | ||
| isText, | ||
|
|
@@ -71,7 +72,12 @@ function transform(codeGen: CodeGen): t.Expression { | |
| const databag = elementDataBag(element, slotParentName); | ||
| let res: t.Expression; | ||
|
|
||
| if (codeGen.staticNodes.has(element) && isElement(element)) { | ||
| if ( | ||
| codeGen.staticNodes.has(element) && | ||
| isElement(element) && | ||
| // Tags like <th> and <td> are okay as static fragments, but only if they're not at the top level | ||
| !isUnsafeTopLevelSerializableElement(element) | ||
| ) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually noticed this was a problem because in one of the snapshots we had a fragment like: <table><tbody><tr><td></td></tr></tbody></table>We should totally allow these kinds of fragments. There's no problem with them. The only problem is if elements like |
||
| // do not process children of static nodes. | ||
| return codeGen.genHoistedElement(element, slotParentName); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -168,10 +168,42 @@ export const KNOWN_HTML_ELEMENTS = new Set(HTML_ELEMENTS.concat(HTML_VOID_ELEMEN | |
| export const HTML_TAG = { | ||
| A: 'a', | ||
| AREA: 'area', | ||
| BODY: 'body', | ||
| CAPTION: 'caption', | ||
| COL: 'col', | ||
| COLGROUP: 'colgroup', | ||
| HEAD: 'head', | ||
| HTML: 'html', | ||
| TBODY: 'tbody', | ||
| TD: 'td', | ||
| TFOOT: 'tfoot', | ||
| TH: 'th', | ||
| THEAD: 'thead', | ||
| TR: 'tr', | ||
| USE: 'use', | ||
| }; | ||
| export const ATTR_NAME = { | ||
| HREF: 'href', | ||
| XLINK_HREF: 'xlink:href', | ||
| }; | ||
| export const TEMPLATE_DIRECTIVES = [/^key$/, /^lwc:*/, /^if:*/, /^for:*/, /^iterator:*/]; | ||
|
|
||
| // Tags that cannot be parsed at the top level of a fragment. Generated from | ||
| // https://github.com/sindresorhus/html-tags/blob/95dcdd5/index.js | ||
| // using the test: | ||
| // document.createRange().createContextualFragment(`<${tag}></${tag}>`).firstChild === null | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's important to call out that we are doing this because we still support IE11. If it wasn't for IE11, we wouldn't need to use Another option worth calling out would be to disable entirely static VNode hoisting in compat node and transition to
At this stage of the release, I would favor the approach with the least amount of changes (the current PR).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call. I'll add the comment. I looked back at the previous perf analysis I did (#2781 (comment)), and it looks like using a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Opened #2890 |
||
| // TODO [#2890]: use <template> instead, drop IE11 support for static optimization | ||
| export const TAGS_THAT_CANNOT_BE_PARSED_AS_TOP_LEVEL = new Set([ | ||
| HTML_TAG.BODY, | ||
| HTML_TAG.CAPTION, | ||
| HTML_TAG.COL, | ||
| HTML_TAG.COLGROUP, | ||
| HTML_TAG.HEAD, | ||
| HTML_TAG.HTML, | ||
| HTML_TAG.TBODY, | ||
| HTML_TAG.TD, | ||
| HTML_TAG.TFOOT, | ||
| HTML_TAG.TH, | ||
| HTML_TAG.THEAD, | ||
| HTML_TAG.TR, | ||
| ]); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see why anybody would put an |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| /* | ||
| * Copyright (c) 2018, salesforce.com, inc. | ||
| * All rights reserved. | ||
| * SPDX-License-Identifier: MIT | ||
| * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT | ||
| */ | ||
|
|
||
| import { BaseElement } from '../shared/types'; | ||
| import { TAGS_THAT_CANNOT_BE_PARSED_AS_TOP_LEVEL } from './constants'; | ||
|
|
||
| export function isUnsafeTopLevelSerializableElement(element: BaseElement) { | ||
| return TAGS_THAT_CANNOT_BE_PARSED_AS_TOP_LEVEL.has(element.name); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| <template> | ||
| <table> | ||
| <tbody> | ||
| <template for:each={rows} for:item="row"> | ||
| <tr key={row}> | ||
| <td></td> | ||
| </tr> | ||
| </template> | ||
| </tbody> | ||
| </table> | ||
| </template> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { LightningElement, api, track } from 'lwc'; | ||
|
|
||
| let count = 0; | ||
|
|
||
| export default class extends LightningElement { | ||
| @track | ||
| rows = []; | ||
|
|
||
| @api | ||
| addRow() { | ||
| this.rows.push(++count); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a fan of the double negation like this... a more elegant approach is to have something like:
isSafeTopLevelSerializableElement(element)instead @nolanlawson