Socket
Socket
Sign inDemoInstall

lit-html

Package Overview
Dependencies
Maintainers
1
Versions
102
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

lit-html - npm Package Compare versions

Comparing version 0.6.0 to 0.7.0

demo/clock.html

10

CHANGELOG.md

@@ -13,4 +13,12 @@ # Change Log

## Unreleased
<!-- ## Unreleased -->
## [0.7.0] - 2017-10-06
* Added the `svg` template tag for creating partial SVG content
* Support for expressions inside tables and other elements with limited permitted content
* Only remove whitespace between elements, or at the start or end of elements
* Fixed bugs with rendering iterables
* A few IE/Edge fixes. Closer to full support.
## [0.6.0] - 2017-09-01

@@ -17,0 +25,0 @@

@@ -14,2 +14,10 @@ /**

*/
/**
* TypeScript has a problem with precompiling templates literals
* https://github.com/Microsoft/TypeScript/issues/17956
*
* TODO(justinfagnani): Run tests compiled to ES5 with both Babel and
* TypeScript to verify correctness.
*/
const envCachesTemplates = ((t) => t() === t())(() => ((s) => s) ``);
// The first argument to JS template tags retain identity across multiple

@@ -19,2 +27,3 @@ // calls to a tag for the same literal, so we can cache work done per literal

const templates = new Map();
const svgTemplates = new Map();
/**

@@ -24,7 +33,16 @@ * Interprets a template literal as an HTML template that can efficiently

*/
export function html(strings, ...values) {
let template = templates.get(strings);
export const html = (strings, ...values) => litTag(strings, values, templates, false);
/**
* Interprets a template literal as an SVG template that can efficiently
* render to and update a container.
*/
export const svg = (strings, ...values) => litTag(strings, values, svgTemplates, true);
function litTag(strings, values, templates, isSvg) {
const key = envCachesTemplates ?
strings :
strings.join('{{--uniqueness-workaround--}}');
let template = templates.get(key);
if (template === undefined) {
template = new Template(strings);
templates.set(strings, template);
template = new Template(strings, isSvg);
templates.set(key, template);
}

@@ -72,4 +90,16 @@ return new TemplateResult(template, values);

*/
const exprMarker = `{{lit-${Math.random()}}}`;
const attributeMarker = `{{lit-${Math.random()}}}`;
/**
* Regex to scan the string preceding an expression to see if we're in a text
* context, and not an attribute context.
*
* This works by seeing if we have a `>` not followed by a `<`. If there is a
* `<` closer to the end of the strings, then we're inside a tag.
*/
const textRegex = />[^<]*$/;
const hasTagsRegex = /[^<]*/;
const textMarkerContent = '_-lit-html-_';
const textMarker = `<!--${textMarkerContent}-->`;
const attrOrTextRegex = new RegExp(`${attributeMarker}|${textMarker}`);
/**
* A placeholder for a dynamic expression in an HTML template.

@@ -100,20 +130,30 @@ *

export class Template {
constructor(strings) {
constructor(strings, svg = false) {
this.parts = [];
this.svg = svg;
this.element = document.createElement('template');
this.element.innerHTML = strings.join(exprMarker);
const walker = document.createTreeWalker(this.element.content, 5 /* elements & text */);
this.element.innerHTML = this._getHtml(strings, svg);
// Edge needs all 4 parameters present; IE11 needs 3rd parameter to be null
const walker = document.createTreeWalker(this.element.content, 133 /* NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT |
NodeFilter.SHOW_TEXT */, null, false);
let index = -1;
let partIndex = 0;
const nodesToRemove = [];
// The actual previous node, accounting for removals: if a node is removed
// it will never be the previousNode.
let previousNode;
// Used to set previousNode at the top of the loop.
let currentNode;
while (walker.nextNode()) {
index++;
const node = walker.currentNode;
if (node.nodeType === 1 /* ELEMENT_NODE */) {
if (!node.hasAttributes())
previousNode = currentNode;
const node = currentNode = walker.currentNode;
if (node.nodeType === 1 /* Node.ELEMENT_NODE */) {
if (!node.hasAttributes()) {
continue;
}
const attributes = node.attributes;
for (let i = 0; i < attributes.length; i++) {
const attribute = attributes.item(i);
const attributeStrings = attribute.value.split(exprMarker);
const attributeStrings = attribute.value.split(attrOrTextRegex);
if (attributeStrings.length > 1) {

@@ -134,4 +174,5 @@ // Get the template literal section leading up to the first

}
else if (node.nodeType === 3 /* TEXT_NODE */) {
const strings = node.nodeValue.split(exprMarker);
else if (node.nodeType === 3 /* Node.TEXT_NODE */) {
const nodeValue = node.nodeValue;
const strings = nodeValue.split(attributeMarker);
if (strings.length > 1) {

@@ -149,10 +190,48 @@ const parent = node.parentNode;

for (let i = 0; i < lastIndex; i++) {
parent.insertBefore(new Text(strings[i]), node);
parent.insertBefore(document.createTextNode(strings[i]), node);
this.parts.push(new TemplatePart('node', index++));
}
}
else if (!node.nodeValue.trim()) {
nodesToRemove.push(node);
else {
// Strip whitespace-only nodes, only between elements, or at the
// beginning or end of elements.
const previousSibling = node.previousSibling;
const nextSibling = node.nextSibling;
if ((previousSibling === null ||
previousSibling.nodeType === 1 /* Node.ELEMENT_NODE */) &&
(nextSibling === null ||
nextSibling.nodeType === 1 /* Node.ELEMENT_NODE */) &&
nodeValue.trim() === '') {
nodesToRemove.push(node);
currentNode = previousNode;
index--;
}
}
}
else if (node.nodeType === 8 /* Node.COMMENT_NODE */ &&
node.nodeValue === textMarkerContent) {
const parent = node.parentNode;
// If we don't have a previous node add a marker node.
// If the previousSibling is removed, because it's another part
// placholder, or empty text, add a marker node.
if (node.previousSibling === null ||
node.previousSibling !== previousNode) {
parent.insertBefore(new Text(), node);
}
else {
index--;
}
this.parts.push(new TemplatePart('node', index++));
nodesToRemove.push(node);
// If we don't have a next node add a marker node.
// We don't have to check if the next node is going to be removed,
// because that node will induce a marker if so.
if (node.nextSibling === null) {
parent.insertBefore(new Text(), node);
}
else {
index--;
}
currentNode = previousNode;
partIndex++;
}

@@ -165,2 +244,23 @@ }

}
/**
* Returns a string of HTML used to create a <template> element.
*/
_getHtml(strings, svg) {
const l = strings.length;
const a = [];
let isTextBinding = false;
for (let i = 0; i < l - 1; i++) {
const s = strings[i];
a.push(s);
// We're in a text position if the previous string matches the
// textRegex. If it doesn't and the previous string has no tags, then
// we use the previous text position state.
isTextBinding = s.match(textRegex) !== null ||
(s.match(hasTagsRegex) !== null && isTextBinding);
a.push(isTextBinding ? textMarker : attributeMarker);
}
a.push(strings[l - 1]);
const html = a.join('');
return svg ? `<svg>${html}</svg>` : html;
}
}

@@ -214,2 +314,3 @@ export const getValue = (part, value) => {

this.endNode = endNode;
this._previousValue = undefined;
}

@@ -263,3 +364,3 @@ setValue(value) {

else {
this._setNode(new Text(value));
this._setNode(document.createTextNode(value === undefined ? '' : value));
}

@@ -296,3 +397,3 @@ this._previousValue = value;

}
// Lets of keep track of how many items we stamped so we can clear leftover
// Lets us keep track of how many items we stamped so we can clear leftover
// items from a previous render

@@ -313,3 +414,3 @@ const itemParts = this._previousValue;

const previousPart = itemParts[partIndex - 1];
itemStart = previousPart.endNode = new Text();
itemStart = previousPart.endNode = document.createTextNode('');
this._insert(itemStart);

@@ -329,2 +430,4 @@ }

const lastPart = itemParts[partIndex - 1];
// Truncate the parts array so _previousValue reflects the current state
itemParts.length = partIndex;
this.clear(lastPart.endNode.previousSibling);

@@ -384,3 +487,5 @@ lastPart.endNode = this.endNode;

if (this.template.parts.length > 0) {
const walker = document.createTreeWalker(fragment, 5 /* elements & text */);
// Edge needs all 4 parameters present; IE11 needs 3rd parameter to be
// null
const walker = document.createTreeWalker(fragment, 133 /* NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT */, null, false);
const parts = this.template.parts;

@@ -402,2 +507,10 @@ let index = 0;

}
if (this.template.svg) {
const svgElement = fragment.firstChild;
fragment.removeChild(svgElement);
const nodes = svgElement.childNodes;
for (let i = 0; i < nodes.length; i++) {
fragment.appendChild(nodes.item(i));
}
}
return fragment;

@@ -404,0 +517,0 @@ }

10

package.json
{
"name": "lit-html",
"version": "0.6.0",
"version": "0.7.0",
"description": "HTML template literals in JavaScript",

@@ -15,5 +15,4 @@ "license": "BSD-3-Clause",

"gen-docs": "typedoc --readme none --mode modules --excludeNotExported --excludePrivate --exclude **/*_test.ts --out ./docs/api .",
"pretest": "npm run posttest; ln -s node_modules bower_components",
"test": "npm run build && wct -l chrome && npm run lint",
"posttest": "rm -f bower_components",
"test": "npm run build && wct -l chrome --npm && npm run lint",
"quicktest": "wct -l chrome -p --npm",
"checksize": "uglifyjs lit-html.js -mc --toplevel | gzip -9 | wc -c",

@@ -33,3 +32,4 @@ "format": "find src test | grep '\\.js$\\|\\.ts$' | xargs clang-format --style=file -i",

"uglify-es": "^3.0.27",
"web-component-tester": "^6.0.1"
"wct-browser-legacy": "^0.0.1-pre.10",
"web-component-tester": "^6.3.0"
},

@@ -36,0 +36,0 @@ "typings": "lit-html.d.ts",

@@ -10,42 +10,32 @@ # lit-html

`lit-html` provides two main exports:
```javascript
import {html, render} from 'lit-html';
* `html`: A JavaScript [template tag](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals) used to produce a `TemplateResult`, which is a container for a template, and the values that should populate the template.
* `render()`: A function that renders a `TemplateResult` to a DOM container, such as an element or shadow root.
// This is a lit-html template function. It returns a lit-html template.
const helloTemplate = (name) => html`<div>Hello ${name}!</div>`;
### Examples
// Call the function with some data, and pass the result to render()
`lit-html` can be used standalone and directly to help manage DOM:
```javascript
const helloTemplate = (name) => html`<div>Hello ${name}!</div>`;
// renders <div>Hello Steve!</div> to the document body
// This renders <div>Hello Steve!</div> to the document body
render(helloTemplate('Steve'), document.body);
// updates to <div>Hello Kevin!</div>, but only updates the ${name} part
// This updates to <div>Hello Kevin!</div>, but only updates the ${name} part
render(helloTemplate('Kevin'), document.body);
```
But it may also be common to use `lit-html` with a component system that calls `render()` for you, similar to React components:
`lit-html` provides two main exports:
_(this example uses JS Class Fields, an upcoming specification)_
```javascript
class MyElement extends CoolLitMixin(HTMLElement) {
* `html`: A JavaScript [template tag](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals) used to produce a `TemplateResult`, which is a container for a template, and the values that should populate the template.
* `render()`: A function that renders a `TemplateResult` to a DOM container, such as an element or shadow root.
static observedProperties = ['title', 'body'];
### Announcement at Polymer Summit 2017
title = `About lit-html`;
body = `It's got potential.`;
<p align="center">
<a href="https://www.youtube.com/watch?v=ruql541T7gc">
<img src="https://img.youtube.com/vi/ruql541T7gc/0.jpg" width="256" alt="Efficient, Expressive, and Extensible HTML Templates video">
<br>
Efficient, Expressive, and Extensible HTML Templates video
</a>
</p>
// Called by the base-class when properties change, result is passed
// to lit-html's render() function.
render() {
return html`
<h1>${this.title}</h1>
<p>${this.body}</p>
`;
}
}
```
## Motivation

@@ -70,3 +60,3 @@

`My name is ${name}.`
```
```

@@ -90,3 +80,3 @@ A _tagged_ template literal is prefixed with a special template tag function:

The first time `html` is called on a particular template literal it does one-time setup work to create the template. It joins all the string parts with a special placeholder, `"{{}}"`, then creates a `<template>` and sets its `innerHTML` to the result. The it walks the template's DOM and extracts the placeholder and remembers their location.
The first time `html` is called on a particular template literal it does one-time setup work to create the template. It joins all the string parts with a special placeholder, `"{{}}"`, then creates a `<template>` and sets its `innerHTML` to the result. Then it walks the template's DOM and extracts the placeholder and remembers their location.

@@ -129,2 +119,15 @@ Every call to `html` returns a `TemplateResult` which contains the template created on the first call, and the expression values for the current call.

### SVG Support
To create partial SVG templates - template that will rendering inside and `<svg>` tag (in the SVG namespace), use the `svg` template tag instead of the `html` template tag:
```javascript
const grid = svg`
<g>
${[0, 10, 20].map((x) => svg`<line x1=${x} y1="0" x2=${x} y2="20"/>`)}
${[0, 10, 20].map((y) => svg`<line x1="0" y1=${y} x2="0" y2=${y}/>`)}
</g>
`;
```
### Safety

@@ -149,3 +152,3 @@

const items = [1, 2, 3];
const render = () => html`items = ${items.map((i) => `item: ${i})}`;
const render = () => html`items = ${items.map((i) => `item: ${i}`)}`;
```

@@ -273,3 +276,3 @@

* It uses JavaScript modules, and there's no build set up yet, so out-of-the-box it only runs in Safari 10.1, Chrome Canary (coming in 61), and Firefox 54 (behind a flag).
* It uses JavaScript modules, and there's no build set up yet, so out-of-the-box it only runs in Safari 10.1, Chrome 61, and Firefox 54 (behind a flag).
* It has a growing test suite, but it has only been run manually on Chrome Canary, Safari 10.1 and Firefox 54.

@@ -276,0 +279,0 @@ * Much more test coverage is needed for complex templates, especially template composition and Function and Iterable values.

@@ -60,3 +60,2 @@ /**

let index = 0;
let oldPartsIndex = 0;
let currentMarker: Node|undefined;

@@ -77,4 +76,3 @@

// if there's no keyMap
let itemPart =
keyMap === undefined ? oldParts[oldPartsIndex++] : keyMap.get(key);
let itemPart = keyMap === undefined ? oldParts[0] : keyMap.get(key);

@@ -81,0 +79,0 @@ if (itemPart === undefined) {

@@ -15,6 +15,17 @@ /**

/**
* TypeScript has a problem with precompiling templates literals
* https://github.com/Microsoft/TypeScript/issues/17956
*
* TODO(justinfagnani): Run tests compiled to ES5 with both Babel and
* TypeScript to verify correctness.
*/
const envCachesTemplates =
((t: any) => t() === t())(() => ((s: TemplateStringsArray) => s) ``);
// The first argument to JS template tags retain identity across multiple
// calls to a tag for the same literal, so we can cache work done per literal
// in a Map.
const templates = new Map<TemplateStringsArray, Template>();
const templates = new Map<TemplateStringsArray|string, Template>();
const svgTemplates = new Map<TemplateStringsArray|string, Template>();

@@ -25,8 +36,24 @@ /**

*/
export function html(
strings: TemplateStringsArray, ...values: any[]): TemplateResult {
let template = templates.get(strings);
export const html = (strings: TemplateStringsArray, ...values: any[]) =>
litTag(strings, values, templates, false);
/**
* Interprets a template literal as an SVG template that can efficiently
* render to and update a container.
*/
export const svg = (strings: TemplateStringsArray, ...values: any[]) =>
litTag(strings, values, svgTemplates, true);
function litTag(
strings: TemplateStringsArray,
values: any[],
templates: Map<TemplateStringsArray|string, Template>,
isSvg: boolean): TemplateResult {
const key = envCachesTemplates ?
strings :
strings.join('{{--uniqueness-workaround--}}');
let template = templates.get(key);
if (template === undefined) {
template = new Template(strings);
templates.set(strings, template);
template = new Template(strings, isSvg);
templates.set(key, template);
}

@@ -87,5 +114,18 @@ return new TemplateResult(template, values);

*/
const exprMarker = `{{lit-${Math.random()}}}`;
const attributeMarker = `{{lit-${Math.random()}}}`;
/**
* Regex to scan the string preceding an expression to see if we're in a text
* context, and not an attribute context.
*
* This works by seeing if we have a `>` not followed by a `<`. If there is a
* `<` closer to the end of the strings, then we're inside a tag.
*/
const textRegex = />[^<]*$/;
const hasTagsRegex = /[^<]*/;
const textMarkerContent = '_-lit-html-_';
const textMarker = `<!--${textMarkerContent}-->`;
const attrOrTextRegex = new RegExp(`${attributeMarker}|${textMarker}`);
/**
* A placeholder for a dynamic expression in an HTML template.

@@ -113,25 +153,42 @@ *

export class Template {
parts: TemplatePart[] = [];
element: HTMLTemplateElement;
svg: boolean;
constructor(strings: TemplateStringsArray) {
constructor(strings: TemplateStringsArray, svg: boolean = false) {
this.svg = svg;
this.element = document.createElement('template');
this.element.innerHTML = strings.join(exprMarker);
this.element.innerHTML = this._getHtml(strings, svg);
// Edge needs all 4 parameters present; IE11 needs 3rd parameter to be null
const walker = document.createTreeWalker(
this.element.content, 5 /* elements & text */);
this.element.content,
133 /* NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT |
NodeFilter.SHOW_TEXT */
,
null as any,
false);
let index = -1;
let partIndex = 0;
const nodesToRemove = [];
const nodesToRemove: Node[] = [];
// The actual previous node, accounting for removals: if a node is removed
// it will never be the previousNode.
let previousNode: Node|undefined;
// Used to set previousNode at the top of the loop.
let currentNode: Node|undefined;
while (walker.nextNode()) {
index++;
const node = walker.currentNode as Element;
if (node.nodeType === 1 /* ELEMENT_NODE */) {
if (!node.hasAttributes())
previousNode = currentNode;
const node = currentNode = walker.currentNode as Element;
if (node.nodeType === 1 /* Node.ELEMENT_NODE */) {
if (!node.hasAttributes()) {
continue;
}
const attributes = node.attributes;
for (let i = 0; i < attributes.length; i++) {
const attribute = attributes.item(i);
const attributeStrings = attribute.value.split(exprMarker);
const attributeStrings = attribute.value.split(attrOrTextRegex);
if (attributeStrings.length > 1) {

@@ -153,4 +210,5 @@ // Get the template literal section leading up to the first

}
} else if (node.nodeType === 3 /* TEXT_NODE */) {
const strings = node.nodeValue!.split(exprMarker);
} else if (node.nodeType === 3 /* Node.TEXT_NODE */) {
const nodeValue = node.nodeValue!;
const strings = nodeValue.split(attributeMarker);
if (strings.length > 1) {

@@ -171,9 +229,45 @@ const parent = node.parentNode!;

for (let i = 0; i < lastIndex; i++) {
parent.insertBefore(new Text(strings[i]), node);
parent.insertBefore(document.createTextNode(strings[i]), node);
this.parts.push(new TemplatePart('node', index++));
}
} else if (!node.nodeValue!.trim()) {
nodesToRemove.push(node);
} else {
// Strip whitespace-only nodes, only between elements, or at the
// beginning or end of elements.
const previousSibling = node.previousSibling;
const nextSibling = node.nextSibling;
if ((previousSibling === null ||
previousSibling.nodeType === 1 /* Node.ELEMENT_NODE */) &&
(nextSibling === null ||
nextSibling.nodeType === 1 /* Node.ELEMENT_NODE */) &&
nodeValue.trim() === '') {
nodesToRemove.push(node);
currentNode = previousNode;
index--;
}
}
} else if (
node.nodeType === 8 /* Node.COMMENT_NODE */ &&
node.nodeValue === textMarkerContent) {
const parent = node.parentNode!;
// If we don't have a previous node add a marker node.
// If the previousSibling is removed, because it's another part
// placholder, or empty text, add a marker node.
if (node.previousSibling === null ||
node.previousSibling !== previousNode) {
parent.insertBefore(new Text(), node);
} else {
index--;
}
this.parts.push(new TemplatePart('node', index++));
nodesToRemove.push(node);
// If we don't have a next node add a marker node.
// We don't have to check if the next node is going to be removed,
// because that node will induce a marker if so.
if (node.nextSibling === null) {
parent.insertBefore(new Text(), node);
} else {
index--;
}
currentNode = previousNode;
partIndex++;
}

@@ -187,2 +281,24 @@ }

}
/**
* Returns a string of HTML used to create a <template> element.
*/
private _getHtml(strings: TemplateStringsArray, svg?: boolean): string {
const l = strings.length;
const a = [];
let isTextBinding = false;
for (let i = 0; i < l - 1; i++) {
const s = strings[i];
a.push(s);
// We're in a text position if the previous string matches the
// textRegex. If it doesn't and the previous string has no tags, then
// we use the previous text position state.
isTextBinding = s.match(textRegex) !== null ||
(s.match(hasTagsRegex) !== null && isTextBinding);
a.push(isTextBinding ? textMarker : attributeMarker);
}
a.push(strings[l - 1]);
const html = a.join('');
return svg ? `<svg>${html}</svg>` : html;
}
}

@@ -271,2 +387,3 @@

this.endNode = endNode;
this._previousValue = undefined;
}

@@ -319,3 +436,3 @@

} else {
this._setNode(new Text(value));
this._setNode(document.createTextNode(value === undefined ? '' : value));
}

@@ -355,5 +472,5 @@ this._previousValue = value;

// Lets of keep track of how many items we stamped so we can clear leftover
// Lets us keep track of how many items we stamped so we can clear leftover
// items from a previous render
const itemParts = this._previousValue;
const itemParts = this._previousValue as any[];
let partIndex = 0;

@@ -375,3 +492,3 @@

const previousPart = itemParts[partIndex - 1];
itemStart = previousPart.endNode = new Text();
itemStart = previousPart.endNode = document.createTextNode('');
this._insert(itemStart);

@@ -391,2 +508,4 @@ }

const lastPart = itemParts[partIndex - 1];
// Truncate the parts array so _previousValue reflects the current state
itemParts.length = partIndex;
this.clear(lastPart.endNode.previousSibling!);

@@ -464,4 +583,10 @@ lastPart.endNode = this.endNode;

if (this.template.parts.length > 0) {
const walker =
document.createTreeWalker(fragment, 5 /* elements & text */);
// Edge needs all 4 parameters present; IE11 needs 3rd parameter to be
// null
const walker = document.createTreeWalker(
fragment,
133 /* NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT */
,
null as any,
false);

@@ -483,4 +608,12 @@ const parts = this.template.parts;

}
if (this.template.svg) {
const svgElement = fragment.firstChild!;
fragment.removeChild(svgElement);
const nodes = svgElement.childNodes;
for (let i = 0; i < nodes.length; i++) {
fragment.appendChild(nodes.item(i));
}
}
return fragment;
}
}

@@ -160,3 +160,3 @@ /**

test('renderes an list', () => {
test('renders a list', () => {
const r = html`${repeat([1, 2, 3], (i: number) => html`

@@ -197,4 +197,14 @@ <li>item: ${i}</li>`)}`;

test('re-renders a list', () => {
const items = [1, 2, 3, 4, 5];
const t = () => html`${repeat(items, (i: number) => html`
<li>item: ${i}</li>`)}`;
render(t(), container);
render(t(), container);
assert.equal(
container.innerHTML,
`<li>item: 1</li><li>item: 2</li><li>item: 3</li><li>item: 4</li><li>item: 5</li>`);
});
});
});

@@ -18,3 +18,3 @@ /**

import {AttributePart, defaultPartCallback, html, NodePart, Part, render, TemplateInstance, TemplatePart, TemplateResult} from '../lit-html.js';
import {AttributePart, defaultPartCallback, html, NodePart, Part, render, svg, TemplateInstance, TemplatePart, TemplateResult} from '../lit-html.js';

@@ -65,3 +65,2 @@ const assert = chai.assert;

render(result, container);
console.log(container.innerHTML);
assert.equal(container.innerHTML, '{{}}');

@@ -139,4 +138,14 @@ });

let container: HTMLElement;
setup(() => {
container = document.createElement('div');
});
test('removes whitespace-only nodes', () => {
render(html`<div> </div>`, container);
assert.equal(container.innerHTML, '<div></div>');
});
test('renders a string', () => {
const container = document.createElement('div');
render(html`<div>${'foo'}</div>`, container);

@@ -147,3 +156,2 @@ assert.equal(container.innerHTML, '<div>foo</div>');

test('renders a number', () => {
const container = document.createElement('div');
render(html`<div>${123}</div>`, container);

@@ -154,3 +162,2 @@ assert.equal(container.innerHTML, '<div>123</div>');

test('renders undefined', () => {
const container = document.createElement('div');
render(html`<div>${undefined}</div>`, container);

@@ -161,3 +168,2 @@ assert.equal(container.innerHTML, '<div></div>');

test('renders null', () => {
const container = document.createElement('div');
render(html`<div>${null}</div>`, container);

@@ -169,3 +175,2 @@ assert.equal(container.innerHTML, '<div></div>');

// This test just checks that we don't call the function
const container = document.createElement('div');
render(html`<div>${(_: any) => 123}</div>`, container);

@@ -176,3 +181,2 @@ assert.equal(container.innerHTML, '<div>(_) =&gt; 123</div>');

test('renders arrays', () => {
const container = document.createElement('div');
render(html`<div>${[1, 2, 3]}</div>`, container);

@@ -183,3 +187,2 @@ assert.equal(container.innerHTML, '<div>123</div>');

test('renders nested templates', () => {
const container = document.createElement('div');
const partial = html`<h1>${'foo'}</h1>`;

@@ -190,4 +193,52 @@ render(html`${partial}${'bar'}`, container);

test('renders parts with whitespace after them', () => {
render(html`<div>${'foo'} </div>`, container);
assert.equal(container.innerHTML, '<div>foo </div>');
});
test('preserves whitespace between parts', () => {
render(html`<div>${'foo'} ${'bar'}</div>`, container);
assert.equal(container.innerHTML, '<div>foo bar</div>');
});
test('renders nested templates within table content', () => {
let table = html`<table>${html`<tr>${html`<td></td>`}</tr>`}</table>`;
render(table, container);
assert.equal(container.innerHTML, '<table><tr><td></td></tr></table>');
table = html`<tbody>${html`<tr></tr>`}</tbody>`;
render(table, container);
assert.equal(container.innerHTML, '<tbody><tr></tr></tbody>');
table = html`<table><tr></tr>${html`<tr></tr>`}</table>`;
render(table, container);
assert.equal(
container.innerHTML,
'<table><tbody><tr></tr><tr></tr></tbody></table>');
table = html`<table><tr><td></td>${html`<td></td>`}</tr></table>`;
render(table, container);
assert.equal(
container.innerHTML,
'<table><tbody><tr><td></td><td></td></tr></tbody></table>');
table = html`<table><tr><td></td>${html`<td></td>`}${
html`<td></td>`
}</tr></table>`;
render(table, container);
assert.equal(
container.innerHTML,
'<table><tbody><tr><td></td><td></td><td></td></tr></tbody></table>');
});
test(
'renders quoted attributes with the text <table> before an expression',
() => {
const container = document.createElement('div');
const template = html`<div a="<table>${'foo'}"></div>`;
render(template, container);
assert.equal(container.innerHTML, '<div a="<table>foo"></div>');
});
test('values contain interpolated values', () => {
const container = document.createElement('div');
const t = html`${'a'},${'b'},${'c'}`;

@@ -199,3 +250,2 @@ render(t, container);

// test('renders multiple nested templates', () => {
// const container = document.createElement('div');
// const partial = html`<h1>${'foo'}</h1>`;

@@ -208,3 +258,2 @@ // html`${partial}${'bar'}${partial}${'baz'}qux`, container);

test('renders arrays of nested templates', () => {
const container = document.createElement('div');
render(html`<div>${[1, 2, 3].map((i) => html`${i}`)}</div>`, container);

@@ -215,3 +264,2 @@ assert.equal(container.innerHTML, '<div>123</div>');

test('renders an element', () => {
const container = document.createElement('div');
const child = document.createElement('p');

@@ -223,3 +271,2 @@ render(html`<div>${child}</div>`, container);

test('renders an array of elements', () => {
const container = document.createElement('div');
const children = [

@@ -236,3 +283,2 @@ document.createElement('p'),

test('renders to an attribute', () => {
const container = document.createElement('div');
render(html`<div foo="${'bar'}"></div>`, container);

@@ -243,3 +289,2 @@ assert.equal(container.innerHTML, '<div foo="bar"></div>');

test('renders to an attribute without quotes', () => {
const container = document.createElement('div');
render(html`<div foo=${'bar'}></div>`, container);

@@ -250,3 +295,2 @@ assert.equal(container.innerHTML, '<div foo="bar"></div>');

test('renders interpolation to an attribute', () => {
const container = document.createElement('div');
render(html`<div foo="1${'bar'}2${'baz'}3"></div>`, container);

@@ -258,3 +302,2 @@ assert.equal(container.innerHTML, '<div foo="1bar2baz3"></div>');

// This test just checks that we don't call the function
const container = document.createElement('div');
render(html`<div foo=${(_: any) => 123}></div>`, container);

@@ -265,3 +308,2 @@ assert.equal(container.innerHTML, '<div foo="(_) => 123"></div>');

test('renders an array to an attribute', () => {
const container = document.createElement('div');
render(html`<div foo=${[1, 2, 3]}></div>`, container);

@@ -272,3 +314,2 @@ assert.equal(container.innerHTML, '<div foo="123"></div>');

test('renders to an attribute before a node', () => {
const container = document.createElement('div');
render(html`<div foo="${'bar'}">${'baz'}</div>`, container);

@@ -279,3 +320,2 @@ assert.equal(container.innerHTML, '<div foo="bar">baz</div>');

test('renders to an attribute after a node', () => {
const container = document.createElement('div');
render(html`<div>${'baz'}</div><div foo="${'bar'}"></div>`, container);

@@ -287,3 +327,2 @@ assert.equal(

test('renders a Promise', async () => {
const container = document.createElement('div');
let resolve: (v: any) => void;

@@ -301,3 +340,2 @@ const promise = new Promise((res, _) => {

test('renders racing Promises correctly', async () => {
const container = document.createElement('div');
let resolve1: (v: any) => void;

@@ -337,3 +375,2 @@ const promise1 = new Promise((res, _) => {

test('renders a combination of stuff', () => {
const container = document.createElement('div');
render(html`

@@ -349,2 +386,24 @@ <div foo="${'bar'}">

test('renders SVG', () => {
const container = document.createElement('svg');
const t = svg`<line y1="1" y2="1"/>`;
render(t, container);
const line = container.firstElementChild!;
assert.equal(line.tagName, 'line');
assert.equal(line.namespaceURI, 'http://www.w3.org/2000/svg');
});
test('renders templates with comments', () => {
const t = html`
<div>
<!-- this is a comment -->
<h1 class="${'foo'}">title</h1>
<p>${'foo'}</p>
</div>`;
render(t, container);
assert.equal(container.innerHTML, `<div>
<!-- this is a comment -->
<h1 class="foo">title</h1><p>foo</p></div>`);
});
});

@@ -354,4 +413,9 @@

let container: HTMLElement;
setup(() => {
container = document.createElement('div');
});
test('dirty checks simple values', () => {
const container = document.createElement('div');
const foo = 'aaa';

@@ -383,3 +447,2 @@

test('renders to and updates a container', () => {
const container = document.createElement('div');
let foo = 'aaa';

@@ -403,3 +466,2 @@

test('renders to and updates sibling parts', () => {
const container = document.createElement('div');
let foo = 'foo';

@@ -419,3 +481,2 @@ const bar = 'bar';

test('renders and updates attributes', () => {
const container = document.createElement('div');
let foo = 'foo';

@@ -435,3 +496,2 @@ const bar = 'bar';

test('updates nested templates', () => {
const container = document.createElement('div');
let foo = 'foo';

@@ -464,3 +524,2 @@ const bar = 'bar';

test('updates arrays', () => {
const container = document.createElement('div');
let items = [1, 2, 3];

@@ -476,4 +535,20 @@ const t = () => html`<div>${items}</div>`;

test('updates arrays that shrink then grow', () => {
let items: number[];
const t = () => html`<div>${items}</div>`;
items = [1, 2, 3];
render(t(), container);
assert.equal(container.innerHTML, '<div>123</div>');
items = [4];
render(t(), container);
assert.equal(container.innerHTML, '<div>4</div>');
items = [5, 6, 7];
render(t(), container);
assert.equal(container.innerHTML, '<div>567</div>');
});
test('updates an element', () => {
const container = document.createElement('div');
let child: any = document.createElement('p');

@@ -494,3 +569,2 @@ const t = () => html`<div>${child}<div></div></div>`;

test('updates an array of elements', () => {
const container = document.createElement('div');
let children: any = [

@@ -519,3 +593,2 @@ document.createElement('p'),

() => {
const container = document.createElement('div');

@@ -522,0 +595,0 @@ render(html`<div>foo</div>`, container);

@@ -8,2 +8,3 @@ {

"sourceMap": true,
"inlineSources": true,
"outDir": "./",

@@ -10,0 +11,0 @@ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc