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

Skip to content
Closed
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/large-spoons-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
34 changes: 30 additions & 4 deletions packages/labs/compiler/src/lib/template-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ class CompiledTemplatePass {
return (sourceFile: ts.SourceFile): ts.SourceFile => {
const pass = new CompiledTemplatePass(context);
pass.findTemplates(sourceFile);
if (!pass.shouldCompileFile) {
return sourceFile;
}
const transformedSourceFile = pass.rewriteTemplates(sourceFile);
if (!ts.isSourceFile(transformedSourceFile)) {
throw new Error(
Expand All @@ -89,6 +92,10 @@ class CompiledTemplatePass {
ts.TaggedTemplateExpression,
TemplateInfo
>();
/**
* Only compile the file if an `html` tag was imported from lit or lit-html.
*/
private shouldCompileFile = false;
/**
* Track the added security brand AST node so we only add one.
*/
Expand Down Expand Up @@ -117,6 +124,7 @@ class CompiledTemplatePass {
const findTemplates = <T extends ts.Node>(node: T) => {
return ts.visitNode(node, (node: ts.Node): ts.Node => {
nodeStack.push(node);
this.shouldCompileFile ||= this.shouldMarkFileForCompilation(node);
if (isLitTemplate(node)) {
const topStatement = nodeStack[1] as ts.Statement;
const templateInfo = {
Expand All @@ -139,6 +147,28 @@ class CompiledTemplatePass {
return findTemplates(sourceFile);
}

/**
* Returns true if passed a node that marks the file for compilation.
* Currently files are marked for compilation if an importClause is detected
* that contains an `html` import from `lit` or `lit-html`.
*/
private shouldMarkFileForCompilation<T extends ts.Node>(node: T): boolean {
if (
ts.isImportDeclaration(node) &&
ts.isStringLiteral(node.moduleSpecifier) &&
(node.moduleSpecifier.text === 'lit' ||
node.moduleSpecifier.text === 'lit-html') &&
node.importClause?.namedBindings != null &&
ts.isNamedImports(node.importClause.namedBindings)
) {
const namedBindings = node.importClause.namedBindings;
return namedBindings.elements.some(
(imp) => ts.isIdentifier(imp.name) && imp.name.text === 'html'
);
}
return false;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we resolve the reference for the template tag function instead? I'm concerned this approach would miscompile:

import {html} from 'lit';
import * as static from 'lit/static.js';

function foo() {
  const html = static.html;
  return html``;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if we go with the currently implemented approach, does it properly handle code like this:

import {svg as html} from 'lit';

Copy link
Contributor Author

@AndrewJakubowicz AndrewJakubowicz Jul 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes – those cases will miscompile. I can add resolving the reference (and add those as test cases).

If we're resolving the reference – we could support aliased identifiers as long as they resolve to html imported from lit?

E.g. we could resolve import {html as h} from 'lit';

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also handle:

import * as lit from 'lit';

lit.html`rar`;


/**
* Perform an inorder traversal starting at the passed in `node`, calling
* `this.rewriteTemplates` on each node traversed.
Expand Down Expand Up @@ -356,10 +386,6 @@ export const compileLitTemplates = (): ts.TransformerFactory<ts.SourceFile> =>

/**
* E.g. html`foo` or html`foo${bar}`
*
* TODO(ajakubowicz): Figure out how to handle detecting templates to compile
* correctly and robustly. We want to avoid situations where `html` imported
* from 'lit/static.js' is compiled.
*/
const isLitTemplate = (node: ts.Node): node is ts.TaggedTemplateExpression =>
ts.isTaggedTemplateExpression(node) &&
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// TODO(ajakubowicz): Should we add support for aliased imports from 'lit' and
// 'lit-html'?
import { html as h } from 'lit-html';
import { render } from 'lit';
render(h `<p>Hello</p>`, document.body);
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// TODO(ajakubowicz): Should we add support for aliased imports from 'lit' and
// 'lit-html'?
import {html as h} from 'lit-html';
import {render} from 'lit';
render(h`<p>Hello</p>`, document.body);
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { html, literal } from 'lit-html/static.js';
import { render } from 'lit';
render(html `${literal `<p>Hello</p>`}`, document.body);
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {html, literal} from 'lit-html/static.js';
import {render} from 'lit';
render(html`${literal`<p>Hello</p>`}`, document.body);