Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/stupid-knives-punch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
34 changes: 31 additions & 3 deletions packages/labs/compiler/src/lib/template-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
isElementNode,
isTextNode,
traverse,
getTextContent,
} from '@parse5/tools';
import {getTypeChecker} from './type-checker.js';
const {getTemplateHtml, markerMatch, marker, boundAttributeSuffix} =
Expand Down Expand Up @@ -64,6 +65,14 @@ interface TemplateInfo {
variableName: ts.Identifier;
}

/**
* Matches the raw text elements.
*
* Comments are not parsed within raw text elements, so we need to search their
* text content for marker strings.
*/
const rawTextElement = /^(?:script|style|textarea|title)$/i;

/**
* CompiledTemplatePass provides a `ts.TransformerFactory` that transforms
* `html` tagged templates into a `CompiledTemplateResult`.
Expand Down Expand Up @@ -120,7 +129,8 @@ class CompiledTemplatePass {
>();
/**
* Map an `html` tagged template expression to the template info for each
* template to compile.
* template to compile. If a template is not in this map, it will not be
* rewritten into a `CompiledTemplateResult`.
*/
private readonly expressionToTemplate = new Map<
ts.TaggedTemplateExpression,
Expand Down Expand Up @@ -268,8 +278,9 @@ class CompiledTemplatePass {
// Start at -1 to account for an extra root document fragment.
let nodeIndex = -1;
let attrNameIndex = 0;
let shouldCompile = true;
traverse(ast, {
'pre:node': (node) => {
'pre:node': (node): boolean | void => {
if (isElementNode(node)) {
const attributesToRemove = new Set<unknown>();
if (node.attrs.length > 0) {
Expand Down Expand Up @@ -314,6 +325,18 @@ class CompiledTemplatePass {
(attr) => !attributesToRemove.has(attr)
);
}
if (rawTextElement.test(node.tagName)) {
// Raw text elements treat their contents as raw text. E.g.,
// `<textarea><!--Hi--></textarea>` will render a textarea element
// with the comment as visible contents. Currently the compiled
// template runtime doesn't handle raw text elements with bindings,
// so we do not optimize these templates.
const hasMarkers = getTextContent(node).includes(marker);
if (hasMarkers) {
shouldCompile = false;
return false;
}
}
} else if (isCommentNode(node)) {
if (node.data === markerMatch) {
parts.push({
Expand All @@ -329,6 +352,10 @@ class CompiledTemplatePass {
},
});

if (!shouldCompile) {
return {shouldCompile: false};
}

// Add required unique ctor identifiers for parts added.
const f = this.context.factory;
for (const part of parts) {
Expand Down Expand Up @@ -401,7 +428,8 @@ class CompiledTemplatePass {
)!) {
const result = this.litHtmlPrepareRenderPhase(template.node.template);
if (!result.shouldCompile) {
throw new Error(`Unhandled TODO: Will be implemented in followup.`);
this.expressionToTemplate.delete(template.node);
continue;
}
const {parts: partData, preparedHtml} = result;
const parts = createTemplateParts({
Expand Down
32 changes: 32 additions & 0 deletions packages/labs/compiler/test_files/raw_text_element.golden.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { html } from 'lit-html';
const b_1 = i => i;
const lit_template_1 = { h: b_1 `<script>\n potato;\n</script>`, parts: [] };
// TODO: In the future we can figure out a way to correctly compile raw text
// elements. The complexity is that they depend on creating adjacent Text nodes
// which cannot be represented in the prepared HTML. Thus a binding in a raw
// text node results in the template not being optimized.
const scriptTemplateNoBinding = { ["_$litType$"]: lit_template_1, values: [] };
const lit_template_2 = { h: b_1 `<style>\n carrot\n</style>`, parts: [] };
const styleTemplateNoBinding = { ["_$litType$"]: lit_template_2, values: [] };
const lit_template_3 = { h: b_1 `<textarea>tomato</textarea>`, parts: [] };
const textareaTemplateNoBinding = { ["_$litType$"]: lit_template_3, values: [] };
const lit_template_4 = { h: b_1 `<title>avocado</title>`, parts: [] };
const titleTemplateNoBinding = { ["_$litType$"]: lit_template_4, values: [] };
const scriptTemplateOneBinding = html `<script>
${'potato'};
</script>`;
const styleTemplateOneBinding = html `<style>
${'carrot'}
</style>`;
const textareaTemplateOneBinding = html `<textarea>${'tomato'}</textarea>`;
const titleTemplateOneBinding = html `<title>${'avocado'}</title>`;
const scriptTemplateShouldNotBeCompiled = html `<script>
potato ${'tomato'}
</script>`;
const styleTemplateShouldNotBeCompiled = html `<style>
${''} carrot ${''}
</style>`;
const textareaTemplateShouldNotBeCompiled = html `<textarea>
${''}${''}</textarea
>`;
const titleTemplateShouldNotBeCompiled = html `<title>${'one '}${'two'}</title>`;
33 changes: 33 additions & 0 deletions packages/labs/compiler/test_files/raw_text_element.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {html} from 'lit-html';
// TODO: In the future we can figure out a way to correctly compile raw text
// elements. The complexity is that they depend on creating adjacent Text nodes
// which cannot be represented in the prepared HTML. Thus a binding in a raw
// text node results in the template not being optimized.
const scriptTemplateNoBinding = html`<script>
potato;
</script>`;
const styleTemplateNoBinding = html`<style>
carrot
</style>`;
const textareaTemplateNoBinding = html`<textarea>tomato</textarea>`;
const titleTemplateNoBinding = html`<title>avocado</title>`;

const scriptTemplateOneBinding = html`<script>
${'potato'};
</script>`;
const styleTemplateOneBinding = html`<style>
${'carrot'}
</style>`;
const textareaTemplateOneBinding = html`<textarea>${'tomato'}</textarea>`;
const titleTemplateOneBinding = html`<title>${'avocado'}</title>`;

const scriptTemplateShouldNotBeCompiled = html`<script>
potato ${'tomato'}
</script>`;
const styleTemplateShouldNotBeCompiled = html`<style>
${''} carrot ${''}
</style>`;
const textareaTemplateShouldNotBeCompiled = html`<textarea>
${''}${''}</textarea
>`;
const titleTemplateShouldNotBeCompiled = html`<title>${'one '}${'two'}</title>`;