New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@popeindustries/lit-html-server

Package Overview
Dependencies
Maintainers
1
Versions
58
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@popeindustries/lit-html-server - npm Package Compare versions

Comparing version 1.1.1 to 1.2.0

12

browser/directives/async-append.js

@@ -1,7 +0,3 @@

'use strict';
import { directive, isNodePart } from '../index.js';
Object.defineProperty(exports, '__esModule', { value: true });
var index_js = require('../index.js');
/**

@@ -11,3 +7,3 @@ * @typedef NodePart { import('../parts.js').NodePart }

const asyncAppend = index_js.directive(asyncAppendDirective);
const asyncAppend = directive(asyncAppendDirective);

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

return function(part) {
if (!index_js.isNodePart(part)) {
if (!isNodePart(part)) {
throw Error('The `asyncAppend` directive can only be used in text nodes');

@@ -30,2 +26,2 @@ }

exports.asyncAppend = asyncAppend;
export { asyncAppend };

@@ -1,7 +0,3 @@

'use strict';
import { directive, isNodePart } from '../index.js';
Object.defineProperty(exports, '__esModule', { value: true });
var index_js = require('../index.js');
/**

@@ -11,3 +7,3 @@ * @typedef NodePart { import('../parts.js').NodePart }

const cache = index_js.directive(cacheDirective);
const cache = directive(cacheDirective);

@@ -23,3 +19,3 @@ /**

return function(part) {
if (!index_js.isNodePart(part)) {
if (!isNodePart(part)) {
throw Error('The `cache` directive can only be used in text nodes');

@@ -31,2 +27,2 @@ }

exports.cache = cache;
export { cache };

@@ -1,9 +0,5 @@

'use strict';
import { directive, isAttributePart } from '../index.js';
Object.defineProperty(exports, '__esModule', { value: true });
const classMap = directive(classMapDirective);
var index_js = require('../index.js');
const classMap = index_js.directive(classMapDirective);
/**

@@ -18,3 +14,3 @@ * Applies CSS classes, where'classInfo' keys are added as class names if values are truthy.

return function(part) {
if (!index_js.isAttributePart(part) || part.name !== 'class') {
if (!isAttributePart(part) || part.name !== 'class') {
throw Error('The `classMap` directive can only be used in the `class` attribute');

@@ -35,2 +31,2 @@ }

exports.classMap = classMap;
export { classMap };

@@ -1,9 +0,5 @@

'use strict';
import { directive } from '../index.js';
Object.defineProperty(exports, '__esModule', { value: true });
const guard = directive(guardDirective);
var index_js = require('../index.js');
const guard = index_js.directive(guardDirective);
/**

@@ -24,2 +20,2 @@ * Guard against re-render.

exports.guard = guard;
export { guard };

@@ -1,9 +0,5 @@

'use strict';
import { directive, isAttributePart, nothingString } from '../index.js';
Object.defineProperty(exports, '__esModule', { value: true });
const ifDefined = directive(ifDefinedDirective);
var index_js = require('../index.js');
const ifDefined = index_js.directive(ifDefinedDirective);
/**

@@ -18,4 +14,4 @@ * Sets the attribute if 'value' is defined,

return function(part) {
if (value === undefined && index_js.isAttributePart(part)) {
return part.setValue(index_js.nothingString);
if (value === undefined && isAttributePart(part)) {
return part.setValue(nothingString);
}

@@ -26,2 +22,2 @@ part.setValue(value);

exports.ifDefined = ifDefined;
export { ifDefined };

@@ -1,9 +0,5 @@

'use strict';
import { directive } from '../index.js';
Object.defineProperty(exports, '__esModule', { value: true });
const repeat = directive(repeatDirective);
var index_js = require('../index.js');
const repeat = index_js.directive(repeatDirective);
/**

@@ -29,2 +25,2 @@ * Loop through 'items' and call 'template'.

exports.repeat = repeat;
export { repeat };

@@ -1,9 +0,5 @@

'use strict';
import { directive, isAttributePart } from '../index.js';
Object.defineProperty(exports, '__esModule', { value: true });
const styleMap = directive(styleMapDirective);
var index_js = require('../index.js');
const styleMap = index_js.directive(styleMapDirective);
/**

@@ -18,3 +14,3 @@ * Apply CSS properties, where 'styleInfo' keys and values are added as CSS properties.

return function(part) {
if (!index_js.isAttributePart(part) || part.name !== 'style') {
if (!isAttributePart(part) || part.name !== 'style') {
throw Error('The `styleMap` directive can only be used in the `style` attribute');

@@ -33,2 +29,2 @@ }

exports.styleMap = styleMap;
export { styleMap };

@@ -1,9 +0,5 @@

'use strict';
import { directive, isNodePart, unsafePrefixString } from '../index.js';
Object.defineProperty(exports, '__esModule', { value: true });
const unsafeHTML = directive(unsafeHTMLDirective);
var index_js = require('../index.js');
const unsafeHTML = index_js.directive(unsafeHTMLDirective);
/**

@@ -17,9 +13,9 @@ * Render "value" without HTML escaping

return function(part) {
if (!index_js.isNodePart(part)) {
if (!isNodePart(part)) {
throw Error('The `unsafeHTML` directive can only be used in text nodes');
}
part.setValue(`${index_js.unsafePrefixString}${value}`);
part.setValue(`${unsafePrefixString}${value}`);
};
}
exports.unsafeHTML = unsafeHTML;
export { unsafeHTML };

@@ -1,7 +0,3 @@

'use strict';
import { directive } from '../index.js';
Object.defineProperty(exports, '__esModule', { value: true });
var index_js = require('../index.js');
/**

@@ -19,3 +15,3 @@ * Determine if "value" is a primitive

const until = index_js.directive(untilDirective);
const until = directive(untilDirective);

@@ -45,2 +41,2 @@ /**

exports.until = until;
export { until };

@@ -1,612 +0,561 @@

(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.litHtmlServer = {}));
}(this, function (exports) { 'use strict';
/* global window */
/* eslint no-unused-vars: 0 */
const Buffer =
(typeof window !== 'undefined' && window.Buffer) ||
class Buffer {
/**
* Determine if 'buffer' is a buffer
*
* @param { any } buffer
* @returns { boolean }
*/
static isBuffer(buffer) {
return buffer != null && typeof buffer === 'object' && buffer.string !== undefined;
}
/* global window */
/* eslint no-unused-vars: 0 */
const Buffer =
(typeof window !== 'undefined' && window.Buffer) ||
class Buffer {
/**
* Determine if 'buffer' is a buffer
*
* @param { any } buffer
* @returns { boolean }
*/
static isBuffer(buffer) {
return buffer != null && typeof buffer === 'object' && buffer.string !== undefined;
/**
* Create buffer from 'string'
*
* @param { string } string
* @returns { string }
*/
static from(string) {
string = typeof string === 'string' ? string : String(string);
return new Buffer(string);
}
/**
* Join 'buffers' into a single string
*
* @param { Array<any> } buffers
* @param { number } [length]
* @returns { string }
*/
static concat(buffers, length) {
let string = '';
for (let i = 0, n = buffers.length; i < n; i++) {
const buffer = buffers[i];
string += typeof buffer === 'string' ? buffer : String(buffer);
}
/**
* Create buffer from 'string'
*
* @param { string } string
* @returns { string }
*/
static from(string) {
string = typeof string === 'string' ? string : String(string);
return new Buffer(string);
if (length !== undefined && string.length > length) {
string = string.slice(0, length);
}
/**
* Join 'buffers' into a single string
*
* @param { Array<any> } buffers
* @param { number } [length]
* @returns { string }
*/
static concat(buffers, length) {
let string = '';
return new Buffer(string);
}
for (let i = 0, n = buffers.length; i < n; i++) {
const buffer = buffers[i];
/**
* Construct Buffer instance
*
* @param { string } string
*/
constructor(string) {
this.string = string;
}
string += typeof buffer === 'string' ? buffer : String(buffer);
}
/**
* Stringify
*
* @returns { string }
*/
toString() {
return this.string;
}
};
if (length !== undefined && string.length > length) {
string = string.slice(0, length);
}
return new Buffer(string);
}
/**
* Determine if "promise" is a Promise instance
*
* @param { Promise<any> } promise
* @returns { boolean }
*/
function isPromise(promise) {
return promise != null && promise.then != null;
}
/**
* Construct Buffer instance
*
* @param { string } string
*/
constructor(string) {
this.string = string;
}
/**
* Determine if "iterator" is an synchronous iterator
*
* @param { IterableIterator } iterator
* @returns { boolean }
*/
function isSyncIterator(iterator) {
return (
iterator != null &&
// Ignore strings (which are also iterable)
typeof iterator !== 'string' &&
typeof iterator[Symbol.iterator] === 'function'
);
}
/**
* Stringify
*
* @returns { string }
*/
toString() {
return this.string;
}
};
/**
* Determine if "iterator" is an asynchronous iterator
*
* @param { AsyncIterableIterator } iterator
* @returns { boolean }
*/
function isAsyncIterator(iterator) {
return iterator != null && typeof iterator[Symbol.asyncIterator] === 'function';
}
/**
* Determine if "result" is an iterator result object
*
* @param { object } result
* @returns { boolean }
*/
function isIteratorResult(result) {
return typeof result === 'object' && 'value' in result && 'done' in result;
}
/**
* Determine if "promise" is a Promise instance
*
* @param { Promise<any> } promise
* @returns { boolean }
*/
function isPromise(promise) {
return promise != null && promise.then != null;
}
/**
* Determine if "value" is a primitive
*
* @param { any } value
* @returns { boolean }
*/
function isPrimitive(value) {
const type = typeof value;
/**
* Determine if "iterator" is an synchronous iterator
*
* @param { IterableIterator } iterator
* @returns { boolean }
*/
function isSyncIterator(iterator) {
return (
iterator != null &&
// Ignore strings (which are also iterable)
typeof iterator !== 'string' &&
typeof iterator[Symbol.iterator] === 'function'
);
}
return value === null || !(type === 'object' || type === 'function');
}
/**
* Determine if "iterator" is an asynchronous iterator
*
* @param { AsyncIterableIterator } iterator
* @returns { boolean }
*/
function isAsyncIterator(iterator) {
return iterator != null && typeof iterator[Symbol.asyncIterator] === 'function';
}
/**
* An empty string Buffer
*/
const emptyStringBuffer = Buffer.from('');
/**
* Determine if "result" is an iterator result object
*
* @param { object } result
* @returns { boolean }
*/
function isIteratorResult(result) {
return typeof result === 'object' && 'value' in result && 'done' in result;
}
/**
* A value for strings that signals a Part to clear its content
*/
const nothingString = '__nothing-lit-html-server-string__';
/**
* Determine if "value" is a primitive
*
* @param { any } value
* @returns { boolean }
*/
function isPrimitive(value) {
const type = typeof value;
/**
* A prefix value for strings that should not be escaped
*/
const unsafePrefixString = '__unsafe-lit-html-server-string__';
return value === null || !(type === 'object' || type === 'function');
}
// https://github.com/facebook/react/packages/react-dom/src/server/escapeTextForBrowser.js
/**
* An empty string Buffer
*/
const emptyStringBuffer = Buffer.from('');
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* Based on the escape-html library, which is used under the MIT License below:
*
* Copyright (c) 2012-2013 TJ Holowaychuk
* Copyright (c) 2015 Andreas Lubbe
* Copyright (c) 2015 Tiancheng "Timothy" Gu
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* A value for strings that signals a Part to clear its content
*/
const nothingString = '__nothing-lit-html-server-string__';
// code copied and modified from escape-html
/**
* Module variables.
* @private
*/
/**
* A prefix value for strings that should not be escaped
*/
const unsafePrefixString = '__unsafe-lit-html-server-string__';
const matchHtmlRegExp = /["'&<>]/;
// https://github.com/facebook/react/packages/react-dom/src/server/escapeTextForBrowser.js
/**
* Escapes special characters and HTML entities in a given html string.
*
* @param {string} string HTML string to escape for later insertion
* @return {string}
* @public
*/
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* Based on the escape-html library, which is used under the MIT License below:
*
* Copyright (c) 2012-2013 TJ Holowaychuk
* Copyright (c) 2015 Andreas Lubbe
* Copyright (c) 2015 Tiancheng "Timothy" Gu
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
function escapeHtml(string) {
const str = '' + string;
const match = matchHtmlRegExp.exec(str);
// code copied and modified from escape-html
/**
* Module variables.
* @private
*/
if (!match) {
return str;
}
const matchHtmlRegExp = /["'&<>]/;
let escape;
let html = '';
let index;
let lastIndex = 0;
/**
* Escapes special characters and HTML entities in a given html string.
*
* @param {string} string HTML string to escape for later insertion
* @return {string}
* @public
*/
for (index = match.index; index < str.length; index++) {
switch (str.charCodeAt(index)) {
case 34: // "
escape = '&quot;';
break;
case 38: // &
escape = '&amp;';
break;
case 39: // '
escape = '&#x27;'; // modified from escape-html; used to be '&#39'
break;
case 60: // <
escape = '&lt;';
break;
case 62: // >
escape = '&gt;';
break;
default:
continue;
}
function escapeHtml(string) {
const str = '' + string;
const match = matchHtmlRegExp.exec(str);
if (!match) {
return str;
if (lastIndex !== index) {
html += str.substring(lastIndex, index);
}
let escape;
let html = '';
let index;
let lastIndex = 0;
lastIndex = index + 1;
html += escape;
}
for (index = match.index; index < str.length; index++) {
switch (str.charCodeAt(index)) {
case 34: // "
escape = '&quot;';
break;
case 38: // &
escape = '&amp;';
break;
case 39: // '
escape = '&#x27;'; // modified from escape-html; used to be '&#39'
break;
case 60: // <
escape = '&lt;';
break;
case 62: // >
escape = '&gt;';
break;
default:
continue;
}
return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
}
// end code copied and modified from escape-html
if (lastIndex !== index) {
html += str.substring(lastIndex, index);
}
/**
* Escapes text to prevent scripting attacks.
*
* @param {any} text Text value to escape.
* @return {string} An escaped string.
*/
function escapeTextForBrowser(text) {
if (typeof text === 'boolean' || typeof text === 'number') {
// this shortcircuit helps perf for types that we know will never have
// special characters, especially given that this function is used often
// for numeric dom ids.
return '' + text;
}
return escapeHtml(text);
}
lastIndex = index + 1;
html += escape;
}
/**
* @typedef Part { import('./parts.js').Part }
*/
return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
}
// end code copied and modified from escape-html
/**
* Determine if "obj" is a directive function
*
* @param { any } obj
* @returns { boolean }
*/
function isDirective(obj) {
return typeof obj === 'function' && obj.isDirective;
}
/**
* Escapes text to prevent scripting attacks.
*
* @param {any} text Text value to escape.
* @return {string} An escaped string.
*/
function escapeTextForBrowser(text) {
if (typeof text === 'boolean' || typeof text === 'number') {
// this shortcircuit helps perf for types that we know will never have
// special characters, especially given that this function is used often
// for numeric dom ids.
return '' + text;
/**
* Define new directive for "fn".
* The passed function should be a factory function,
* and must return a function that will eventually be called with a Part instance
*
* @param { (...args) => (part: Part) => void } fn
* @returns { (...args) => (part: Part) => void }
*/
function directive(fn) {
return function directive(...args) {
const result = fn(...args);
if (typeof result !== 'function') {
throw Error('directives are factory functions and must return a function when called');
}
return escapeHtml(text);
}
/**
* @typedef Part { import('./parts.js').Part }
*/
result.isDirective = true;
return result;
};
}
/**
* Determine if "part" is a NodePart
*
* @param { Part } part
* @returns { boolean }
*/
function isNodePart(part) {
return part instanceof NodePart || typeof part.name === 'undefined';
}
/**
* Determine if "part" is an AttributePart
*
* @param { Part } part
* @returns { boolean }
*/
function isAttributePart(part) {
return part instanceof AttributePart || typeof part.name !== 'undefined';
}
/**
* Base class interface for Node/Attribute parts
*/
class Part {
/**
* Determine if "obj" is a directive function
*
* @param { any } obj
* @returns { boolean }
* Constructor
*/
function isDirective(obj) {
return typeof obj === 'function' && obj.isDirective;
constructor() {
this._value;
}
/**
* Define new directive for "fn".
* The passed function should be a factory function,
* and must return a function that will eventually be called with a Part instance
* Store the current value.
* Used by directives to temporarily transfer value
* (value will be deleted after reading).
*
* @param { (...args) => (part: Part) => void } fn
* @returns { (...args) => (part: Part) => void }
* @param { any } value
*/
function directive(fn) {
return function directive(...args) {
const result = fn(...args);
if (typeof result !== 'function') {
throw Error('directives are factory functions and must return a function when called');
}
result.isDirective = true;
return result;
};
setValue(value) {
this._value = value;
}
/**
* Determine if "part" is a NodePart
* Retrieve resolved string from passed "value"
*
* @param { Part } part
* @returns { boolean }
* @param { any } value
* @returns { any }
*/
function isNodePart(part) {
return part instanceof NodePart || typeof part.name === 'undefined';
getValue(value) {
return value;
}
/**
* Determine if "part" is an AttributePart
*
* @param { Part } part
* @returns { boolean }
* No-op
*/
function isAttributePart(part) {
return part instanceof AttributePart || typeof part.name !== 'undefined';
}
commit() {}
}
/**
* A dynamic template part for text nodes
*/
class NodePart extends Part {
/**
* Base class interface for Node/Attribute parts
* Retrieve resolved value given passed "value"
*
* @param { any } value
* @returns { any }
*/
class Part {
/**
* Constructor
*/
constructor() {
this._value;
}
/**
* Store the current value.
* Used by directives to temporarily transfer value
* (value will be deleted after reading).
*
* @param { any } value
*/
setValue(value) {
this._value = value;
}
/**
* Retrieve resolved string from passed "value"
*
* @param { any } value
* @returns { any }
*/
getValue(value) {
return value;
}
/**
* No-op
*/
commit() {}
getValue(value) {
return resolveNodeValue(value, this);
}
}
/**
* A dynamic template part for attributes.
* Unlike text nodes, attributes may contain multiple strings and parts.
*/
class AttributePart extends Part {
/**
* A dynamic template part for text nodes
* Constructor
*
* @param { string } name
* @param { Array<Buffer> } strings
*/
class NodePart extends Part {
/**
* Retrieve resolved value given passed "value"
*
* @param { any } value
* @returns { any }
*/
getValue(value) {
return resolveNodeValue(value, this);
}
constructor(name, strings) {
super();
this.name = name;
this.strings = strings;
this.length = strings.length - 1;
this.prefix = Buffer.from(`${this.name}="`);
this.suffix = Buffer.from(`${this.strings[this.length]}"`);
}
/**
* A dynamic template part for attributes.
* Unlike text nodes, attributes may contain multiple strings and parts.
* Retrieve resolved string Buffer from passed "values".
* Resolves to a single string, or Promise for a single string,
* even when responsible for multiple values.
*
* @param { Array<any> } values
* @returns { Buffer|Promise<Buffer> }
*/
class AttributePart extends Part {
/**
* Constructor
*
* @param { string } name
* @param { Array<Buffer> } strings
*/
constructor(name, strings) {
super();
this.name = name;
this.strings = strings;
this.length = strings.length - 1;
this.prefix = Buffer.from(`${this.name}="`);
this.suffix = Buffer.from(`${this.strings[this.length]}"`);
}
getValue(values) {
let chunks = [this.prefix];
let chunkLength = this.prefix.length;
let pendingChunks;
/**
* Retrieve resolved string Buffer from passed "values".
* Resolves to a single string, or Promise for a single string,
* even when responsible for multiple values.
*
* @param { Array<any> } values
* @returns { Buffer|Promise<Buffer> }
*/
getValue(values) {
let chunks = [this.prefix];
let chunkLength = this.prefix.length;
let pendingChunks;
for (let i = 0; i < this.length; i++) {
const string = this.strings[i];
let value = resolveAttributeValue(values[i], this);
for (let i = 0; i < this.length; i++) {
const string = this.strings[i];
let value = resolveAttributeValue(values[i], this);
// Bail if 'nothing'
if (value === nothingString) {
return emptyStringBuffer;
}
// Bail if 'nothing'
if (value === nothingString) {
return emptyStringBuffer;
chunks.push(string);
chunkLength += string.length;
if (Buffer.isBuffer(value)) {
chunks.push(value);
chunkLength += value.length;
} else if (isPromise(value)) {
// Lazy init for uncommon scenario
if (pendingChunks === undefined) {
pendingChunks = [];
}
chunks.push(string);
chunkLength += string.length;
const index = chunks.push(value) - 1;
if (Buffer.isBuffer(value)) {
chunks.push(value);
chunkLength += value.length;
} else if (isPromise(value)) {
// Lazy init for uncommon scenario
if (pendingChunks === undefined) {
pendingChunks = [];
}
const index = chunks.push(value) - 1;
pendingChunks.push(
value.then((value) => {
chunks[index] = value;
chunkLength += value.length;
})
);
} else if (Array.isArray(value)) {
for (const chunk of value) {
chunks.push(chunk);
chunkLength += chunk.length;
}
pendingChunks.push(
value.then((value) => {
chunks[index] = value;
chunkLength += value.length;
})
);
} else if (Array.isArray(value)) {
for (const chunk of value) {
chunks.push(chunk);
chunkLength += chunk.length;
}
}
}
chunks.push(this.suffix);
chunkLength += this.suffix.length;
if (pendingChunks !== undefined) {
return Promise.all(pendingChunks).then(() => Buffer.concat(chunks, chunkLength));
}
return Buffer.concat(chunks, chunkLength);
chunks.push(this.suffix);
chunkLength += this.suffix.length;
if (pendingChunks !== undefined) {
return Promise.all(pendingChunks).then(() => Buffer.concat(chunks, chunkLength));
}
return Buffer.concat(chunks, chunkLength);
}
}
/**
* A dynamic template part for boolean attributes.
* Boolean attributes are prefixed with "?"
*/
class BooleanAttributePart extends AttributePart {
/**
* A dynamic template part for boolean attributes.
* Boolean attributes are prefixed with "?"
* Constructor
*
* @param { string } name
* @param { Array<Buffer> } strings
* @throws error when multiple expressions
*/
class BooleanAttributePart extends AttributePart {
/**
* Constructor
*
* @param { string } name
* @param { Array<Buffer> } strings
* @throws error when multiple expressions
*/
constructor(name, strings) {
super(name, strings);
constructor(name, strings) {
super(name, strings);
this.name = Buffer.from(this.name);
this.name = Buffer.from(this.name);
if (
strings.length !== 2 ||
!strings[0] === emptyStringBuffer ||
!strings[1] === emptyStringBuffer
) {
throw Error('Boolean attributes can only contain a single expression');
}
if (
strings.length !== 2 ||
!strings[0] === emptyStringBuffer ||
!strings[1] === emptyStringBuffer
) {
throw Error('Boolean attributes can only contain a single expression');
}
}
/**
* Retrieve resolved string Buffer from passed "values".
*
* @param { Array<any> } values
* @returns { Buffer|Promise<Buffer> }
*/
getValue(values) {
let value = values[0];
/**
* Retrieve resolved string Buffer from passed "values".
*
* @param { Array<any> } values
* @returns { Buffer|Promise<Buffer> }
*/
getValue(values) {
let value = values[0];
if (isDirective(value)) {
value = resolveDirectiveValue(value, this);
}
if (isDirective(value)) {
value = resolveDirectiveValue(value, this);
}
if (isPromise(value)) {
return value.then((value) => (value ? this.name : emptyStringBuffer));
}
return value ? this.name : emptyStringBuffer;
if (isPromise(value)) {
return value.then((value) => (value ? this.name : emptyStringBuffer));
}
}
/**
* A dynamic template part for property attributes.
* Property attributes are prefixed with "."
*/
class PropertyAttributePart extends AttributePart {
/**
* Retrieve resolved string Buffer from passed "values".
* Properties have no server-side representation,
* so always returns an empty string.
*
* @param { Array<any> } values
* @returns { string }
*/
getValue(/* values */) {
return emptyStringBuffer;
}
return value ? this.name : emptyStringBuffer;
}
}
/**
* A dynamic template part for property attributes.
* Property attributes are prefixed with "."
*/
class PropertyAttributePart extends AttributePart {
/**
* A dynamic template part for event attributes.
* Event attributes are prefixed with "@"
* Retrieve resolved string Buffer from passed "values".
* Properties have no server-side representation,
* so always returns an empty string.
*
* @param { Array<any> } values
* @returns { string }
*/
class EventAttributePart extends AttributePart {
/**
* Retrieve resolved string Buffer from passed "values".
* Event bindings have no server-side representation,
* so always returns an empty string.
*
* @param { Array<any> } values
* @returns { string }
*/
getValue(/* values */) {
return emptyStringBuffer;
}
getValue(/* values */) {
return emptyStringBuffer;
}
}
/**
* A dynamic template part for event attributes.
* Event attributes are prefixed with "@"
*/
class EventAttributePart extends AttributePart {
/**
* Resolve "value" to string if possible
* Retrieve resolved string Buffer from passed "values".
* Event bindings have no server-side representation,
* so always returns an empty string.
*
* @param { any } value
* @param { AttributePart } part
* @returns { any }
* @param { Array<any> } values
* @returns { string }
*/
function resolveAttributeValue(value, part) {
if (isDirective(value)) {
value = resolveDirectiveValue(value, part);
}
getValue(/* values */) {
return emptyStringBuffer;
}
}
if (value === nothingString) {
return value;
}
/**
* Resolve "value" to string if possible
*
* @param { any } value
* @param { AttributePart } part
* @returns { any }
*/
function resolveAttributeValue(value, part) {
if (isDirective(value)) {
value = resolveDirectiveValue(value, part);
}
if (isTemplateResult(value)) {
value = value.read();
}
if (value === nothingString) {
return value;
}
if (isPrimitive(value)) {
const string = typeof value !== 'string' ? String(value) : value;
// Escape if not prefixed with unsafePrefixString, otherwise strip prefix
return Buffer.from(
string.indexOf(unsafePrefixString) === 0 ? string.slice(33) : escapeTextForBrowser(string)
);
} else if (Buffer.isBuffer(value)) {
return value;
} else if (isPromise(value)) {
return value.then((value) => resolveAttributeValue(value, part));
} else if (isSyncIterator(value)) {
if (!Array.isArray(value)) {
value = Array.from(value);
}
return Buffer.concat(
value.reduce((values, value) => {
value = resolveAttributeValue(value, part);
// Flatten
if (Array.isArray(value)) {
return values.concat(value);
}
values.push(value);
return values;
}, [])
);
} else {
throw Error('unknown AttributPart value', value);
}
if (isTemplateResult(value)) {
value = value.read();
}
/**
* Resolve "value" to string Buffer if possible
*
* @param { any } value
* @param { NodePart } part
* @returns { any }
*/
function resolveNodeValue(value, part) {
if (isDirective(value)) {
value = resolveDirectiveValue(value, part);
if (isPrimitive(value)) {
const string = typeof value !== 'string' ? String(value) : value;
// Escape if not prefixed with unsafePrefixString, otherwise strip prefix
return Buffer.from(
string.indexOf(unsafePrefixString) === 0 ? string.slice(33) : escapeTextForBrowser(string)
);
} else if (Buffer.isBuffer(value)) {
return value;
} else if (isPromise(value)) {
return value.then((value) => resolveAttributeValue(value, part));
} else if (isSyncIterator(value)) {
if (!Array.isArray(value)) {
value = Array.from(value);
}
if (value === nothingString || value === undefined) {
return emptyStringBuffer;
}
if (isPrimitive(value)) {
const string = typeof value !== 'string' ? String(value) : value;
// Escape if not prefixed with unsafePrefixString, otherwise strip prefix
return Buffer.from(
string.indexOf(unsafePrefixString) === 0 ? string.slice(33) : escapeTextForBrowser(string)
);
} else if (isTemplateResult(value) || Buffer.isBuffer(value)) {
return value;
} else if (isPromise(value)) {
return value.then((value) => resolveNodeValue(value, part));
} else if (isSyncIterator(value)) {
if (!Array.isArray(value)) {
value = Array.from(value);
}
return value.reduce((values, value) => {
value = resolveNodeValue(value, part);
return Buffer.concat(
value.reduce((values, value) => {
value = resolveAttributeValue(value, part);
// Flatten

@@ -618,717 +567,814 @@ if (Array.isArray(value)) {

return values;
}, []);
} else if (isAsyncIterator(value)) {
return resolveAsyncIteratorValue(value, part);
} else {
throw Error('unknown NodePart value', value);
}
}, [])
);
} else {
throw Error('unknown AttributPart value', value);
}
}
/**
* Resolve values of async "iterator"
*
* @param { AsyncIterator } iterator
* @param { NodePart } part
* @returns { AsyncGenerator }
*/
async function* resolveAsyncIteratorValue(iterator, part) {
for await (const value of iterator) {
yield resolveNodeValue(value, part);
}
/**
* Resolve "value" to string Buffer if possible
*
* @param { any } value
* @param { NodePart } part
* @returns { any }
*/
function resolveNodeValue(value, part) {
if (isDirective(value)) {
value = resolveDirectiveValue(value, part);
}
/**
* Resolve value of "directive"
*
* @param { function } directive
* @param { Part } part
* @returns { any }
*/
function resolveDirectiveValue(directive$$1, part) {
// Directives are synchronous, so it's safe to read and delete value
directive$$1(part);
const value = part._value;
part._value = undefined;
if (value === nothingString || value === undefined) {
return emptyStringBuffer;
}
if (isPrimitive(value)) {
const string = typeof value !== 'string' ? String(value) : value;
// Escape if not prefixed with unsafePrefixString, otherwise strip prefix
return Buffer.from(
string.indexOf(unsafePrefixString) === 0 ? string.slice(33) : escapeTextForBrowser(string)
);
} else if (isTemplateResult(value) || Buffer.isBuffer(value)) {
return value;
} else if (isPromise(value)) {
return value.then((value) => resolveNodeValue(value, part));
} else if (isSyncIterator(value)) {
if (!Array.isArray(value)) {
value = Array.from(value);
}
return value.reduce((values, value) => {
value = resolveNodeValue(value, part);
// Flatten
if (Array.isArray(value)) {
return values.concat(value);
}
values.push(value);
return values;
}, []);
} else if (isAsyncIterator(value)) {
return resolveAsyncIteratorValue(value, part);
} else {
throw Error('unknown NodePart value', value);
}
}
const pool = [];
let id = 0;
/**
* Resolve values of async "iterator"
*
* @param { AsyncIterator } iterator
* @param { NodePart } part
* @returns { AsyncGenerator }
*/
async function* resolveAsyncIteratorValue(iterator, part) {
for await (const value of iterator) {
yield resolveNodeValue(value, part);
}
}
/**
* Determine whether "result" is a TemplateResult
*
* @param { TemplateResult } result
* @returns { boolean }
*/
function isTemplateResult(result) {
return result instanceof TemplateResult;
/**
* Resolve value of "directive"
*
* @param { function } directive
* @param { Part } part
* @returns { any }
*/
function resolveDirectiveValue(directive, part) {
// Directives are synchronous, so it's safe to read and delete value
directive(part);
const value = part._value;
part._value = undefined;
return value;
}
const pool = [];
let id = 0;
/**
* Determine whether "result" is a TemplateResult
*
* @param { TemplateResult } result
* @returns { boolean }
*/
function isTemplateResult(result) {
return result instanceof TemplateResult;
}
/**
* Retrieve TemplateResult instance.
* Uses an object pool to recycle instances.
*
* @param { Template } template
* @param { Array<any> } values
* @returns { TemplateResult }
*/
function templateResult(template, values) {
let instance = pool.pop();
if (instance) {
instance.template = template;
instance.values = values;
} else {
instance = new TemplateResult(template, values);
}
return instance;
}
/**
* A class for consuming the combined static and dynamic parts of a lit-html Template.
* TemplateResults
*/
class TemplateResult {
/**
* Retrieve TemplateResult instance.
* Uses an object pool to recycle instances.
* Constructor
*
* @param { Template } template
* @param { Array<any> } values
* @returns { TemplateResult }
*/
function templateResult(template, values) {
let instance = pool.pop();
if (instance) {
instance.template = template;
instance.values = values;
} else {
instance = new TemplateResult(template, values);
}
return instance;
constructor(template, values) {
this.template = template;
this.values = values;
this.id = id++;
this.index = 0;
}
/**
* A class for consuming the combined static and dynamic parts of a lit-html Template.
* TemplateResults
* Consume template result content.
* *Note* that instances may only be read once,
* and will be destroyed upon completion.
*
* @param { boolean } deep - recursively resolve nested TemplateResults
* @returns { any }
*/
class TemplateResult {
/**
* Constructor
*
* @param { Template } template
* @param { Array<any> } values
*/
constructor(template, values) {
this.template = template;
this.values = values;
this.id = id++;
this.index = 0;
}
read(deep) {
let buffer = emptyStringBuffer;
let chunk, chunks;
/**
* Consume template result content.
* *Note* that instances may only be read once,
* and will be destroyed upon completion.
*
* @param { boolean } deep - recursively resolve nested TemplateResults
* @returns { any }
*/
read(deep) {
let buffer = emptyStringBuffer;
let chunk, chunks;
while ((chunk = this.readChunk()) !== null) {
if (Buffer.isBuffer(chunk)) {
buffer = Buffer.concat([buffer, chunk], buffer.length + chunk.length);
} else {
if (chunks === undefined) {
chunks = [];
}
buffer = reduce(buffer, chunks, chunk, deep);
while ((chunk = this.readChunk()) !== null) {
if (Buffer.isBuffer(chunk)) {
buffer = Buffer.concat([buffer, chunk], buffer.length + chunk.length);
} else {
if (chunks === undefined) {
chunks = [];
}
buffer = reduce(buffer, chunks, chunk, deep);
}
}
if (chunks !== undefined) {
chunks.push(buffer);
return chunks.length > 1 ? chunks : chunks[0];
}
return buffer;
if (chunks !== undefined) {
chunks.push(buffer);
return chunks.length > 1 ? chunks : chunks[0];
}
/**
* Consume template result content one chunk at a time.
* *Note* that instances may only be read once,
* and will be destroyed when the last chunk is read.
*
* @returns { any }
*/
readChunk() {
const isString = this.index % 2 === 0;
const index = (this.index / 2) | 0;
return buffer;
}
// Finished
if (!isString && index >= this.template.strings.length - 1) {
this.destroy();
return null;
}
/**
* Consume template result content one chunk at a time.
* *Note* that instances may only be read once,
* and will be destroyed when the last chunk is read.
*
* @returns { any }
*/
readChunk() {
const isString = this.index % 2 === 0;
const index = (this.index / 2) | 0;
this.index++;
// Finished
if (!isString && index >= this.template.strings.length - 1) {
this.destroy();
return null;
}
if (isString) {
return this.template.strings[index];
}
this.index++;
const part = this.template.parts[index];
let value;
if (isString) {
return this.template.strings[index];
}
if (part instanceof AttributePart) {
// AttributeParts can have multiple values, so slice based on length
// (strings in-between values are already handled the instance)
if (part.length > 1) {
value = part.getValue(this.values.slice(index, index + part.length));
this.index += part.length;
} else {
value = part.getValue([this.values[index]]);
}
const part = this.template.parts[index];
let value;
if (part instanceof AttributePart) {
// AttributeParts can have multiple values, so slice based on length
// (strings in-between values are already handled the instance)
if (part.length > 1) {
value = part.getValue(this.values.slice(index, index + part.length));
this.index += part.length;
} else {
value = part.getValue(this.values[index]);
value = part.getValue([this.values[index]]);
}
return value;
} else {
value = part.getValue(this.values[index]);
}
/**
* Destroy the instance,
* returning it to the object pool
*
* @param { boolean } permanent - permanently destroy instance and it's children
* @returns { void }
*/
destroy(permanent) {
if (this.values !== undefined) {
if (permanent) {
for (const value of this.values) {
if (isTemplateResult(value)) {
value.destroy(permanent);
}
return value;
}
/**
* Destroy the instance,
* returning it to the object pool
*
* @param { boolean } permanent - permanently destroy instance and it's children
* @returns { void }
*/
destroy(permanent) {
if (this.values !== undefined) {
if (permanent) {
for (const value of this.values) {
if (isTemplateResult(value)) {
value.destroy(permanent);
}
}
this.values.length = 0;
}
this.values = undefined;
this.template = undefined;
this.index = 0;
if (!permanent) {
pool.push(this);
}
this.values.length = 0;
}
this.values = undefined;
this.template = undefined;
this.index = 0;
if (!permanent) {
pool.push(this);
}
}
}
/**
* Commit "chunk" to string "buffer".
* Returns new "buffer" value.
*
* @param { Buffer } buffer
* @param { Array<any> } chunks
* @param { any } chunk
* @param { boolean } [deep]
* @returns { Buffer }
*/
function reduce(buffer, chunks, chunk, deep = false) {
if (Buffer.isBuffer(chunk)) {
return Buffer.concat([buffer, chunk], buffer.length + chunk.length);
} else if (isTemplateResult(chunk)) {
if (deep) {
return reduce(buffer, chunks, chunk.read(deep), deep);
} else {
chunks.push(buffer, chunk);
return emptyStringBuffer;
}
} else if (Array.isArray(chunk)) {
return chunk.reduce((buffer, chunk) => reduce(buffer, chunks, chunk), buffer);
} else if (isPromise(chunk) || isAsyncIterator(chunk)) {
/**
* Commit "chunk" to string "buffer".
* Returns new "buffer" value.
*
* @param { Buffer } buffer
* @param { Array<any> } chunks
* @param { any } chunk
* @param { boolean } [deep]
* @returns { Buffer }
*/
function reduce(buffer, chunks, chunk, deep = false) {
if (Buffer.isBuffer(chunk)) {
return Buffer.concat([buffer, chunk], buffer.length + chunk.length);
} else if (isTemplateResult(chunk)) {
if (deep) {
return reduce(buffer, chunks, chunk.read(deep), deep);
} else {
chunks.push(buffer, chunk);
return emptyStringBuffer;
}
} else if (Array.isArray(chunk)) {
return chunk.reduce((buffer, chunk) => reduce(buffer, chunks, chunk), buffer);
} else if (isPromise(chunk) || isAsyncIterator(chunk)) {
chunks.push(buffer, chunk);
return emptyStringBuffer;
}
}
/**
* @typedef TemplateProcessor
* @property { (name: string, strings: Array<string>) => AttributePart } handleAttributeExpressions
* @property { () => NodePart } handleTextExpression
*/
/**
* @typedef TemplateProcessor
* @property { (name: string, strings: Array<string>) => AttributePart } handleAttributeExpressions
* @property { () => NodePart } handleTextExpression
*/
/**
* Class representing the default Template processor.
* Exposes factory functions for generating Part instances to use for
* resolving a template's dynamic values.
*/
class DefaultTemplateProcessor {
/**
* Class representing the default Template processor.
* Exposes factory functions for generating Part instances to use for
* resolving a template's dynamic values.
* Create part instance for dynamic attribute values
*
* @param { string } name
* @param { Array<string> } strings
* @returns { AttributePart }
*/
class DefaultTemplateProcessor {
/**
* Create part instance for dynamic attribute values
*
* @param { string } name
* @param { Array<string> } strings
* @returns { AttributePart }
*/
handleAttributeExpressions(name, strings = []) {
const prefix = name[0];
handleAttributeExpressions(name, strings = []) {
const prefix = name[0];
if (prefix === '.') {
return new PropertyAttributePart(name.slice(1), strings);
} else if (prefix === '@') {
return new EventAttributePart(name.slice(1), strings);
} else if (prefix === '?') {
return new BooleanAttributePart(name.slice(1), strings);
}
return new AttributePart(name, strings);
if (prefix === '.') {
return new PropertyAttributePart(name.slice(1), strings);
} else if (prefix === '@') {
return new EventAttributePart(name.slice(1), strings);
} else if (prefix === '?') {
return new BooleanAttributePart(name.slice(1), strings);
}
/**
* Create part instance for dynamic text values
*
* @returns { NodePart }
*/
handleTextExpression() {
return new NodePart();
}
return new AttributePart(name, strings);
}
/**
* @typedef TemplateResultProcessor
* @property { (renderer: TemplateResultRenderer, stack: Array<any>, [highWaterMark: number]) => function } getProcessor
* Create part instance for dynamic text values
*
* @returns { NodePart }
*/
handleTextExpression() {
return new NodePart();
}
}
/* eslint no-constant-condition:0 */
/**
* Class for the default TemplateResult processor
* used by Promise/Stream TemplateRenderers.
*
* @implements TemplateResultProcessor
*/
class DefaultTemplateResultProcessor {
/**
* Class for the default TemplateResult processor
* used by Promise/Stream TemplateRenderers.
* Process "stack" and push chunks to "renderer"
*
* @implements TemplateResultProcessor
* @param { TemplateResultRenderer } renderer
* @param { Array<any> } stack
* @param { number } [highWaterMark] - byte length to buffer before pushing data
* @returns { () => void }
*/
class DefaultTemplateResultProcessor {
/**
* Process "stack" and push chunks to "renderer"
*
* @param { TemplateResultRenderer } renderer
* @param { Array<any> } stack
* @param { number } [highWaterMark] - byte length to buffer before pushing data
* @returns { () => void }
*/
getProcessor(renderer, stack, highWaterMark = 0) {
const buffer = [];
let bufferLength = 0;
let paused = false;
getProcessor(renderer, stack, highWaterMark = 0) {
const buffer = [];
let bufferLength = 0;
let processing = false;
function flushBuffer() {
if (buffer.length > 0) {
const keepPushing = renderer.push(Buffer.concat(buffer, bufferLength));
function flushBuffer() {
if (buffer.length > 0) {
const keepPushing = renderer.push(Buffer.concat(buffer, bufferLength));
bufferLength = buffer.length = 0;
return keepPushing;
}
bufferLength = buffer.length = 0;
return keepPushing;
}
}
return function process() {
while (!paused) {
let chunk = stack[0];
let breakLoop = false;
let popStack = true;
return function process() {
if (processing) {
return;
}
if (chunk === undefined) {
flushBuffer();
return renderer.push(null);
}
while (true) {
processing = true;
let chunk = stack[0];
let breakLoop = false;
let popStack = true;
if (isTemplateResult(chunk)) {
popStack = false;
chunk = getTemplateResultChunk(chunk, stack);
}
// Done
if (chunk === undefined) {
flushBuffer();
return renderer.push(null);
}
// Skip if finished reading TemplateResult (null)
if (chunk !== null) {
if (Buffer.isBuffer(chunk)) {
buffer.push(chunk);
bufferLength += chunk.length;
// Flush buffered data if over highWaterMark
if (bufferLength > highWaterMark) {
// Pause if backpressure triggered
breakLoop = !flushBuffer();
}
} else if (isPromise(chunk)) {
// Flush buffered data before waiting for Promise
flushBuffer();
breakLoop = true;
paused = true;
// Add pending Promise for value to stack
stack.unshift(chunk);
chunk
.then((chunk) => {
paused = false;
// Handle IteratorResults from AsyncIterator
if (isIteratorResult(chunk)) {
if (chunk.done) {
// Clear resolved Promise
stack.shift();
// Clear AsyncIterator
stack.shift();
} else {
// Replace resolved Promise with IteratorResult value
stack[0] = chunk.value;
}
if (isTemplateResult(chunk)) {
popStack = false;
chunk = getTemplateResultChunk(chunk, stack);
}
// Skip if finished reading TemplateResult (null)
if (chunk !== null) {
if (Buffer.isBuffer(chunk)) {
buffer.push(chunk);
bufferLength += chunk.length;
// Flush buffered data if over highWaterMark
if (bufferLength > highWaterMark) {
// Break if backpressure triggered
breakLoop = !flushBuffer();
processing = !breakLoop;
}
} else if (isPromise(chunk)) {
// Flush buffered data before waiting for Promise
flushBuffer();
// "processing" is still true, so prevented from restarting until Promise resolved
breakLoop = true;
// Add pending Promise for value to stack
stack.unshift(chunk);
chunk
.then((chunk) => {
// Handle IteratorResults from AsyncIterator
if (isIteratorResult(chunk)) {
if (chunk.done) {
// Clear resolved Promise
stack.shift();
// Clear AsyncIterator
stack.shift();
} else {
// Replace resolved Promise with value
stack[0] = chunk;
// Replace resolved Promise with IteratorResult value
stack[0] = chunk.value;
}
process();
})
.catch((err) => {
destroy(stack);
renderer.destroy(err);
});
} else if (Array.isArray(chunk)) {
// First remove existing Array if at top of stack (not added by pending TemplateResult)
if (stack[0] === chunk) {
popStack = false;
stack.shift();
}
stack.unshift(...chunk);
} else if (isAsyncIterator(chunk)) {
} else {
// Replace resolved Promise with value
stack[0] = chunk;
}
processing = false;
process();
})
.catch((err) => {
destroy(stack);
renderer.destroy(err);
});
} else if (Array.isArray(chunk)) {
// First remove existing Array if at top of stack (not added by pending TemplateResult)
if (stack[0] === chunk) {
popStack = false;
// Add AsyncIterator to stack (will be cleared when done iterating)
if (stack[0] !== chunk) {
stack.unshift(chunk);
}
// Add pending Promise for IteratorResult to stack
stack.unshift(chunk[Symbol.asyncIterator]().next());
} else {
destroy(stack);
return renderer.destroy(Error(`unknown chunk type: ${chunk}`));
stack.shift();
}
stack.unshift(...chunk);
} else if (isAsyncIterator(chunk)) {
popStack = false;
// Add AsyncIterator back to stack (will be cleared when done iterating)
if (stack[0] !== chunk) {
stack.unshift(chunk);
}
// Add pending Promise for IteratorResult to stack
stack.unshift(chunk[Symbol.asyncIterator]().next());
} else {
destroy(stack);
return renderer.destroy(Error(`unknown chunk type: ${chunk}`));
}
}
if (popStack) {
stack.shift();
}
if (popStack) {
stack.shift();
}
if (breakLoop) {
break;
}
if (breakLoop) {
break;
}
};
}
}
};
}
}
/**
* Permanently destroy all remaining TemplateResults in "stack".
* (Triggered on error)
*
* @param { Array<any> } stack
*/
function destroy(stack) {
if (stack.length > 0) {
for (const chunk of stack) {
if (isTemplateResult(chunk)) {
chunk.destroy(true);
}
/**
* Permanently destroy all remaining TemplateResults in "stack".
* (Triggered on error)
*
* @param { Array<any> } stack
*/
function destroy(stack) {
if (stack.length > 0) {
for (const chunk of stack) {
if (isTemplateResult(chunk)) {
chunk.destroy(true);
}
}
stack.length = 0;
}
stack.length = 0;
}
/**
* Retrieve next chunk from "result".
* Adds nested TemplateResults to the stack if necessary.
*
* @param { TemplateResult } result
* @param { Array<any> } stack
*/
function getTemplateResultChunk(result, stack) {
let chunk = result.readChunk();
// Skip empty strings
if (Buffer.isBuffer(chunk) && chunk.length === 0) {
chunk = result.readChunk();
}
// Finished reading, dispose
if (chunk === null) {
stack.shift();
} else if (isTemplateResult(chunk)) {
// Add to top of stack
stack.unshift(chunk);
chunk = getTemplateResultChunk(chunk, stack);
}
return chunk;
}
/**
* @typedef TemplateResult { import('./template-result.js).TemplateResult }
* @typedef TemplateResultProcessor { import('./default-template-result-processor.js).TemplateResultProcessor }
* @typedef TemplateResultRenderer { import('./default-template-result-renderer.js).TemplateResultRenderer }
*/
/**
* A class for rendering a template result to a string resolving Promise
*/
class PromiseTemplateRenderer {
/**
* Retrieve next chunk from "result".
* Adds nested TemplateResults to the stack if necessary.
* Constructor
*
* @param { TemplateResult } result
* @param { Array<any> } stack
* @param { TemplateResultProcessor } processor
* @param { boolean } [asBuffer]
* @returns { Promise<string> }
*/
function getTemplateResultChunk(result, stack) {
let chunk = result.readChunk();
constructor(result, processor, asBuffer = false) {
return new Promise((resolve, reject) => {
let stack = [result];
let buffer = [];
let bufferLength = 0;
// Skip empty strings
if (Buffer.isBuffer(chunk) && chunk.length === 0) {
chunk = result.readChunk();
}
processor.getProcessor(
{
push(chunk) {
if (chunk === null) {
buffer = Buffer.concat(buffer, bufferLength);
resolve(asBuffer ? buffer : buffer.toString());
} else {
buffer.push(chunk);
bufferLength += chunk.length;
}
return true;
},
destroy(err) {
buffer.length = stack.length = bufferLength = 0;
buffer = undefined;
stack = undefined;
reject(err);
}
},
stack
)();
});
}
}
// Finished reading, dispose
if (chunk === null) {
stack.shift();
} else if (isTemplateResult(chunk)) {
// Add to top of stack
stack.unshift(chunk);
chunk = getTemplateResultChunk(chunk, stack);
}
/* global ReadableStream */
return chunk;
}
/**
* A custom Readable stream class for rendering a template result to a stream
*
* @implements TemplateResultRenderer
*/
class StreamTemplateRenderer {
/**
* @typedef TemplateResult { import('./template-result.js).TemplateResult }
* Constructor
*
* @param { TemplateResult } result - a template result returned from call to "html`...`"
* @param { TemplateResultProcessor } processor
* @returns { ReadableStream }
*/
/**
* @typedef TemplateResultProcessor { import('./default-template-result-processor.js).TemplateResultProcessor }
*/
/**
* @typedef TemplateResultRenderer { import('./default-template-result-renderer.js).TemplateResultRenderer }
*/
constructor(result, processor) {
if (typeof ReadableStream === 'undefined') {
throw Error('ReadableStream not supported on this platform');
}
if (typeof TextEncoder === 'undefined') {
throw Error('TextEncoder not supported on this platform');
}
/**
* A class for rendering a template result to a string resolving Promise
*/
class PromiseTemplateRenderer {
/**
* Constructor
*
* @param { TemplateResult } result
* @param { TemplateResultProcessor } processor
* @param { boolean } [asBuffer]
* @returns { Promise<string> }
*/
constructor(result, processor, asBuffer = false) {
return new Promise((resolve, reject) => {
return new ReadableStream({
process: null,
start(controller) {
const encoder = new TextEncoder();
const underlyingSource = this;
let stack = [result];
let buffer = [];
let bufferLength = 0;
processor.getProcessor(
this.process = processor.getProcessor(
{
push(chunk) {
if (chunk === null) {
buffer = Buffer.concat(buffer, bufferLength);
resolve(asBuffer ? buffer : buffer.toString());
} else {
buffer.push(chunk);
bufferLength += chunk.length;
controller.close();
return false;
}
return true;
controller.enqueue(encoder.encode(chunk.toString()));
// Pause processing (return "false") if stream is full
return controller.desiredSize > 0;
},
destroy(err) {
buffer.length = stack.length = bufferLength = 0;
buffer = undefined;
controller.error(err);
underlyingSource.process = undefined;
stack = undefined;
reject(err);
}
},
stack
)();
});
}
stack,
16384
);
},
pull() {
this.process();
}
});
}
}
/**
* @license
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at
* http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at
* http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at
* http://polymer.github.io/PATENTS.txt
*/
/**
* An expression marker with embedded unique key to avoid collision with
* possible text in templates.
*/
const marker = `{{lit-${String(Math.random()).slice(2)}}}`;
/**
* This regex extracts the attribute name preceding an attribute-position
* expression. It does this by matching the syntax allowed for attributes
* against the string literal directly preceding the expression, assuming that
* the expression is in an attribute-value position.
*
* See attributes in the HTML spec:
* https://www.w3.org/TR/html5/syntax.html#attributes-0
*
* "\0-\x1F\x7F-\x9F" are Unicode control characters
*
* " \x09\x0a\x0c\x0d" are HTML space characters:
* https://www.w3.org/TR/html5/infrastructure.html#space-character
*
* So an attribute is:
* * The name: any character except a control character, space character, ('),
* ("), ">", "=", or "/"
* * Followed by zero or more space characters
* * Followed by "="
* * Followed by zero or more space characters
* * Followed by:
* * Any character except space, ('), ("), "<", ">", "=", (`), or
* * (") then any non-("), or
* * (') then any non-(')
*/
const lastAttributeNameRegex = /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F \x09\x0a\x0c\x0d"'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
/**
* @typedef TemplateProcessor { import('./default-template-processor.js').TemplateProcessor }
*/
const RE_QUOTE = /"[^"]*|'[^']*$/;
/**
* A cacheable Template that stores the "strings" and "parts" associated with a
* tagged template literal invoked with "html`...`".
*/
class Template {
/**
* @license
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at
* http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at
* http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at
* http://polymer.github.io/PATENTS.txt
*/
/**
* An expression marker with embedded unique key to avoid collision with
* possible text in templates.
*/
const marker = `{{lit-${String(Math.random()).slice(2)}}}`;
/**
* This regex extracts the attribute name preceding an attribute-position
* expression. It does this by matching the syntax allowed for attributes
* against the string literal directly preceding the expression, assuming that
* the expression is in an attribute-value position.
* Create Template instance
*
* See attributes in the HTML spec:
* https://www.w3.org/TR/html5/syntax.html#attributes-0
*
* "\0-\x1F\x7F-\x9F" are Unicode control characters
*
* " \x09\x0a\x0c\x0d" are HTML space characters:
* https://www.w3.org/TR/html5/infrastructure.html#space-character
*
* So an attribute is:
* * The name: any character except a control character, space character, ('),
* ("), ">", "=", or "/"
* * Followed by zero or more space characters
* * Followed by "="
* * Followed by zero or more space characters
* * Followed by:
* * Any character except space, ('), ("), "<", ">", "=", (`), or
* * (") then any non-("), or
* * (') then any non-(')
* @param { Array<string> } strings
* @param { TemplateProcessor } processor
*/
const lastAttributeNameRegex = /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F \x09\x0a\x0c\x0d"'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
constructor(strings, processor) {
this.strings = [];
this.parts = [];
/**
* @typedef TemplateProcessor { import('./default-template-processor.js').TemplateProcessor }
*/
this._prepare(strings, processor);
}
const RE_QUOTE = /"[^"]*|'[^']*$/;
/**
* A cacheable Template that stores the "strings" and "parts" associated with a
* tagged template literal invoked with "html`...`".
* Prepare the template's static strings,
* and create Part instances for the dynamic values,
* based on lit-html syntax.
*
* @param { Array<string> } strings
* @param { TemplateProcessor } processor
*/
class Template$1 {
/**
* Create Template instance
*
* @param { Array<string> } strings
* @param { TemplateProcessor } processor
*/
constructor(strings, processor) {
this.strings = [];
this.parts = [];
_prepare(strings, processor) {
const endIndex = strings.length - 1;
let attributeMode = false;
let nextString = strings[0];
this._prepare(strings, processor);
}
for (let i = 0; i < endIndex; i++) {
let string = nextString;
nextString = strings[i + 1];
const tagState = getTagState(string);
let skip = 0;
let part;
/**
* Prepare the template's static strings,
* and create Part instances for the dynamic values,
* based on lit-html syntax.
*
* @param { Array<string> } strings
* @param { TemplateProcessor } processor
*/
_prepare(strings, processor) {
const endIndex = strings.length - 1;
let attributeMode = false;
let nextString = strings[0];
// Open/close tag found at end of string
if (tagState !== -1) {
attributeMode = tagState === 1;
}
for (let i = 0; i < endIndex; i++) {
let string = nextString;
nextString = strings[i + 1];
const tagState = getTagState(string);
let skip = 0;
let part;
if (attributeMode) {
const matchName = lastAttributeNameRegex.exec(string);
// Open/close tag found at end of string
if (tagState !== -1) {
attributeMode = tagState === 1;
}
if (matchName) {
let [, prefix, name, suffix] = matchName;
if (attributeMode) {
const matchName = lastAttributeNameRegex.exec(string);
// Since attributes are conditional, remove "name" and "suffix" from static string
string = string.slice(0, matchName.index + prefix.length);
if (matchName) {
let [, prefix, name, suffix] = matchName;
const matchQuote = RE_QUOTE.exec(suffix);
// Since attributes are conditional, remove "name" and "suffix" from static string
string = string.slice(0, matchName.index + prefix.length);
// If attribute is quoted, handle potential multiple values
if (matchQuote) {
const quoteCharacter = matchQuote[0].charAt(0);
// Store any text between quote character and value
const attributeStrings = [Buffer.from(suffix.slice(matchQuote.index + 1))];
let open = true;
skip = 0;
let attributeString;
const matchQuote = RE_QUOTE.exec(suffix);
// Scan ahead and gather all strings for this attribute
while (open) {
attributeString = strings[i + skip + 1];
const closingQuoteIndex = attributeString.indexOf(quoteCharacter);
// If attribute is quoted, handle potential multiple values
if (matchQuote) {
const quoteCharacter = matchQuote[0].charAt(0);
// Store any text between quote character and value
const attributeStrings = [Buffer.from(suffix.slice(matchQuote.index + 1))];
let open = true;
skip = 0;
let attributeString;
// Scan ahead and gather all strings for this attribute
while (open) {
attributeString = strings[i + skip + 1];
const closingQuoteIndex = attributeString.indexOf(quoteCharacter);
if (closingQuoteIndex === -1) {
attributeStrings.push(Buffer.from(attributeString));
skip++;
} else {
attributeStrings.push(Buffer.from(attributeString.slice(0, closingQuoteIndex)));
nextString = attributeString.slice(closingQuoteIndex + 1);
i += skip;
open = false;
}
if (closingQuoteIndex === -1) {
attributeStrings.push(Buffer.from(attributeString));
skip++;
} else {
attributeStrings.push(Buffer.from(attributeString.slice(0, closingQuoteIndex)));
nextString = attributeString.slice(closingQuoteIndex + 1);
i += skip;
open = false;
}
}
part = processor.handleAttributeExpressions(name, attributeStrings);
} else {
part = processor.handleAttributeExpressions(name, [
emptyStringBuffer,
emptyStringBuffer
]);
}
part = processor.handleAttributeExpressions(name, attributeStrings);
} else {
part = processor.handleAttributeExpressions(name, [
emptyStringBuffer,
emptyStringBuffer
]);
}
} else {
part = processor.handleTextExpression();
}
} else {
part = processor.handleTextExpression();
}
this.strings.push(Buffer.from(string));
this.parts.push(part);
// Add placehholders for strings/parts that wil be skipped due to multple values in a single AttributePart
if (skip > 0) {
this.strings.push(null);
this.parts.push(null);
skip = 0;
}
this.strings.push(Buffer.from(string));
this.parts.push(part);
// Add placehholders for strings/parts that wil be skipped due to multple values in a single AttributePart
if (skip > 0) {
this.strings.push(null);
this.parts.push(null);
skip = 0;
}
}
this.strings.push(Buffer.from(nextString));
}
this.strings.push(Buffer.from(nextString));
}
}
/**
* Determine if 'string' terminates with an opened or closed tag.
*
* Iterating through all characters has at worst a time complexity of O(n),
* and is better than the alternative (using "indexOf/lastIndexOf") which is potentially O(2n).
*
* @param { string } string
* @returns { number } - returns "-1" if no tag, "0" if closed tag, or "1" if open tag
*/
function getTagState(string) {
for (let i = string.length - 1; i >= 0; i--) {
const char = string[i];
/**
* Determine if 'string' terminates with an opened or closed tag.
*
* Iterating through all characters has at worst a time complexity of O(n),
* and is better than the alternative (using "indexOf/lastIndexOf") which is potentially O(2n).
*
* @param { string } string
* @returns { number } - returns "-1" if no tag, "0" if closed tag, or "1" if open tag
*/
function getTagState(string) {
for (let i = string.length - 1; i >= 0; i--) {
const char = string[i];
if (char === '>') {
return 0;
} else if (char === '<') {
return 1;
}
if (char === '>') {
return 0;
} else if (char === '<') {
return 1;
}
return -1;
}
/**
* @typedef Readable { import('stream').Readable }
* @typedef TemplateResult { import('./template-result.js).TemplateResult }
*/
return -1;
}
const defaultTemplateProcessor = new DefaultTemplateProcessor();
const defaultTemplateResultProcessor = new DefaultTemplateResultProcessor();
const templateCache = new Map();
/**
* @typedef TemplateResult { import('./template-result.js).TemplateResult }
*/
/**
* Interprets a template literal as an HTML template that can be
* rendered as a Readable stream or String
*
* @param { Array<string> } strings
* @param { ...any } values
* @returns { TemplateResult }
*/
function html(strings, ...values) {
let template = templateCache.get(strings);
const defaultTemplateProcessor = new DefaultTemplateProcessor();
const defaultTemplateResultProcessor = new DefaultTemplateResultProcessor();
const templateCache = new Map();
if (template === undefined) {
template = new Template$1(strings, defaultTemplateProcessor);
templateCache.set(strings, template);
}
/**
* Interprets a template literal as an HTML template that can be
* rendered as a Readable stream or String
*
* @param { Array<string> } strings
* @param { ...any } values
* @returns { TemplateResult }
*/
function html(strings, ...values) {
let template = templateCache.get(strings);
return templateResult(template, values);
if (template === undefined) {
template = new Template(strings, defaultTemplateProcessor);
templateCache.set(strings, template);
}
/**
* Render a template result to a string resolving Promise.
* *Note* that TemplateResults are single use, and can only be rendered once.
*
* @param { TemplateResult } result - a template result returned from call to "html`...`"
* @returns { Promise<string> }
*/
function renderToString(result) {
return new PromiseTemplateRenderer(result, defaultTemplateResultProcessor, false);
}
return templateResult(template, values);
}
// TODO: renderToStream using browser ReadableStream
/**
* Render a template result to a Readable stream
* *Note* that TemplateResults are single use, and can only be rendered once.
*
* @param { TemplateResult } result - a template result returned from call to "html`...`"
* @returns { Readable }
*/
function renderToStream(result) {
return new StreamTemplateRenderer(result, defaultTemplateResultProcessor);
}
exports.defaultTemplateProcessor = defaultTemplateProcessor;
exports.defaultTemplateResultProcessor = defaultTemplateResultProcessor;
exports.html = html;
exports.svg = html;
exports.isTemplateResult = isTemplateResult;
exports.renderToString = renderToString;
exports.templateCache = templateCache;
exports.isAttributePart = isAttributePart;
exports.isNodePart = isNodePart;
exports.nothingString = nothingString;
exports.unsafePrefixString = unsafePrefixString;
exports.directive = directive;
/**
* Render a template result to a string resolving Promise.
* *Note* that TemplateResults are single use, and can only be rendered once.
*
* @param { TemplateResult } result - a template result returned from call to "html`...`"
* @returns { Promise<string> }
*/
function renderToString(result) {
return new PromiseTemplateRenderer(result, defaultTemplateResultProcessor, false);
}
Object.defineProperty(exports, '__esModule', { value: true });
}));
export { defaultTemplateProcessor, defaultTemplateResultProcessor, directive, html, isAttributePart, isNodePart, isTemplateResult, nothingString, renderToStream, renderToString, html as svg, templateCache, unsafePrefixString };

@@ -577,5 +577,5 @@ 'use strict';

*/
function resolveDirectiveValue(directive$$1, part) {
function resolveDirectiveValue(directive, part) {
// Directives are synchronous, so it's safe to read and delete value
directive$$1(part);
directive(part);
const value = part._value;

@@ -809,6 +809,3 @@ part._value = undefined;

/**
* @typedef TemplateResultProcessor
* @property { (renderer: TemplateResultRenderer, stack: Array<any>, [highWaterMark: number]) => function } getProcessor
*/
/* eslint no-constant-condition:0 */

@@ -833,3 +830,3 @@ /**

let bufferLength = 0;
let paused = false;
let processing = false;

@@ -846,3 +843,8 @@ function flushBuffer() {

return function process() {
while (!paused) {
if (processing) {
return;
}
while (true) {
processing = true;
let chunk = stack[0];

@@ -852,2 +854,3 @@ let breakLoop = false;

// Done
if (chunk === undefined) {

@@ -870,4 +873,5 @@ flushBuffer();

if (bufferLength > highWaterMark) {
// Pause if backpressure triggered
// Break if backpressure triggered
breakLoop = !flushBuffer();
processing = !breakLoop;
}

@@ -877,4 +881,4 @@ } else if (isPromise(chunk)) {

flushBuffer();
// "processing" is still true, so prevented from restarting until Promise resolved
breakLoop = true;
paused = true;
// Add pending Promise for value to stack

@@ -884,3 +888,2 @@ stack.unshift(chunk);

.then((chunk) => {
paused = false;
// Handle IteratorResults from AsyncIterator

@@ -901,2 +904,3 @@ if (isIteratorResult(chunk)) {

}
processing = false;
process();

@@ -917,3 +921,3 @@ })

popStack = false;
// Add AsyncIterator to stack (will be cleared when done iterating)
// Add AsyncIterator back to stack (will be cleared when done iterating)
if (stack[0] !== chunk) {

@@ -988,7 +992,3 @@ stack.unshift(chunk);

* @typedef TemplateResult { import('./template-result.js).TemplateResult }
*/
/**
* @typedef TemplateResultProcessor { import('./default-template-result-processor.js).TemplateResultProcessor }
*/
/**
* @typedef TemplateResultRenderer { import('./default-template-result-renderer.js).TemplateResultRenderer }

@@ -1042,2 +1042,4 @@ */

* @typedef TemplateResult { import('./template-result.js).TemplateResult }
* @typedef TemplateResultProcessor { import('./default-template-result-processor.js).TemplateResultProcessor }
* @typedef TemplateResultRenderer { import('./default-template-result-renderer.js).TemplateResultRenderer }
*/

@@ -1061,3 +1063,4 @@

this.process = processor.getProcessor(this, [result], 16384);
this.stack = [result];
this.process = processor.getProcessor(this, this.stack, 16384);
}

@@ -1083,3 +1086,4 @@

this.process = null;
this.process = undefined;
this.stack = undefined;
this.removeAllListeners();

@@ -1144,3 +1148,3 @@ }

*/
class Template$1 {
class Template {
/**

@@ -1290,3 +1294,3 @@ * Create Template instance

if (template === undefined) {
template = new Template$1(strings, defaultTemplateProcessor);
template = new Template(strings, defaultTemplateProcessor);
templateCache.set(strings, template);

@@ -1333,13 +1337,13 @@ }

exports.defaultTemplateResultProcessor = defaultTemplateResultProcessor;
exports.directive = directive;
exports.html = html;
exports.svg = html;
exports.isAttributePart = isAttributePart;
exports.isNodePart = isNodePart;
exports.isTemplateResult = isTemplateResult;
exports.nothingString = nothingString;
exports.renderToBuffer = renderToBuffer;
exports.renderToStream = renderToStream;
exports.renderToString = renderToString;
exports.svg = html;
exports.templateCache = templateCache;
exports.isAttributePart = isAttributePart;
exports.isNodePart = isNodePart;
exports.nothingString = nothingString;
exports.unsafePrefixString = unsafePrefixString;
exports.directive = directive;
{
"name": "@popeindustries/lit-html-server",
"version": "1.1.1",
"version": "1.2.0",
"description": "Render lit-html templates on the server",

@@ -15,3 +15,3 @@ "keywords": [

"module": "index.mjs",
"browser": "browser/index.mjs",
"browser": "browser/index.js",
"repository": "https://github.com/popeindustries/lit-html-server.git",

@@ -22,18 +22,20 @@ "author": "Alexander Pope <alex@pope-industries.com>",

"devDependencies": {
"autocannon": "^3.2.0",
"autocannon": "^3.2.1",
"babel-eslint": "^10.0.1",
"chai": "^4.2.0",
"eslint": "^5.14.1",
"eslint-config-prettier": "^4.0.0",
"eslint": "^5.16.0",
"eslint-config-prettier": "^4.2.0",
"eslint-plugin-prettier": "^3.0.1",
"esm": "^3.2.5",
"get-stream": "^4.1.0",
"husky": "^1.3.1",
"lint-staged": "^8.1.4",
"esm": "^3.2.22",
"get-stream": "^5.1.0",
"husky": "^2.1.0",
"lint-staged": "^8.1.5",
"lit-html": "^1.0.0",
"mocha": "^6.0.1",
"prettier": "^1.16.4",
"rollup": "^1.2.2",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-node-resolve": "^4.0.1"
"mocha": "^6.1.4",
"prettier": "^1.17.0",
"rollup": "^1.10.1",
"rollup-plugin-commonjs": "^9.3.4",
"rollup-plugin-node-resolve": "^4.2.3",
"text-encoding": "^0.7.0",
"web-streams-polyfill": "^2.0.3"
},

@@ -40,0 +42,0 @@ "engines": {

@@ -6,5 +6,5 @@ [![NPM Version](https://img.shields.io/npm/v/@popeindustries/lit-html-server.svg?style=flat)](https://npmjs.org/package/@popeindustries/lit-html-server)

Render [**lit-html**](https://polymer.github.io/lit-html/) templates on the server as Node.js streams. Supports all **lit-html** types, special attribute expressions, and many of the standard directives.
Render [**lit-html**](https://polymer.github.io/lit-html/) templates on the server as strings or streams (and in the browser too!). Supports all **lit-html** types, special attribute expressions, and many of the standard directives.
> Although based on **lit-html** semantics, **lit-html-server** is a great general purpose HTML template streaming library. Tagged template literals are a native JavaScript feature, and the HTML rendered is 100% standard markup, with no special syntax or client-side runtime required!
> Although based on **lit-html** semantics, **lit-html-server** is a great general purpose HTML template streaming library. Tagged template literals are a native JavaScript feature, and the HTML rendered is 100% standard markup, with no special syntax or runtime required!

@@ -48,3 +48,3 @@ ## Usage

<x-widget ?enabled="${data.hasWidget}"></x-widget>
<p class="${classMap({ 'negative': data.invertedText }">${data.text}</p>
<p class="${classMap({ negative: data.invertedText })}">${data.text}</p>
`;

@@ -70,9 +70,11 @@ }

## API
## API (Node.js)
### `html`
The tag function to apply to HTML template literals (also aliased as `svg`)
The tag function to apply to HTML template literals (also aliased as `svg`):
```js
const { html } = require('@popeindustries/lit-html-server');
const name = 'Bob';

@@ -87,5 +89,7 @@ html`

```js
const { html } = require('@popeindustries/lit-html-server');
const { unsafeHTML } = require('@popeindustries/lit-html-server/directives/unsafe-html.js');
html`
<div>${unsafeHTML('<span>dangerous!</span>')}</div>
<div>${unsafeHTML('<span>danger!</span>')}</div>
`;

@@ -96,5 +100,8 @@ ```

Returns the result of the template tagged by `html` as a Node.js `Readable` stream of markup.
Returns the result of the template tagged by `html` as a Node.js `Readable` stream of markup:
```js
const { html, renderToStream } = require('@popeindustries/lit-html-server');
const name = 'Bob';
renderToStream(

@@ -109,5 +116,8 @@ html`

Returns the result of the template tagged by `html` as a Promise which resolves to a string of markup.
Returns the result of the template tagged by `html` as a Promise which resolves to a string of markup:
```js
const { html, renderToString } = require('@popeindustries/lit-html-server');
const name = 'Bob';
const markup = await renderToString(

@@ -123,5 +133,8 @@ html`

Returns the result of the template tagged by `html` as a Promise which resolves to a Buffer of markup.
Returns the result of the template tagged by `html` as a Promise which resolves to a Buffer of markup:
```js
const { html, renderToBuffer } = require('@popeindustries/lit-html-server');
const name = 'Bob';
const markup = await renderToBuffer(

@@ -135,2 +148,60 @@ html`

## API (Browser)
**lit-html-server** may also be used in the browser to render strings of markup, or in a Service Worker script to render streams of markup.
### `html`
The tag function to apply to HTML template literals (also aliased as `svg`):
```js
import { html } from '@popeindustries/lit-html-server';
const name = 'Bob';
html`
<h1>Hello ${name}!</h1>
`;
```
### `renderToStream(TemplateResult): ReadableStream`
Returns the result of the template tagged by `html` as a `ReadableStream` stream of markup. This may be used in a Service Worker script to stream an html response to the browser:
```js
import { html, renderToStream } from '@popeindustries/lit-html-server');
self.addEventListener('fetch', (event) => {
const name = 'Bob';
const stream = renderToStream(
html`
<h1>Hello ${name}!</h1>
`
);
const response = new Response(stream, {
headers: {
'content-type': 'text/html'
}
});
event.respondWith(response);
});
```
> _NOTE: a bundler is required to package modules for use in a Service Worker_
### `renderToString(TemplateResult): Promise<string>`
Returns the result of the template tagged by `html` as a Promise which resolves to a string of markup:
```js
import { html, renderToString } from '@popeindustries/lit-html-server';
const name = 'Bob';
const markup = await renderToString(
html`
<h1>Hello ${name}!</h1>
`
);
document.body.innerHtml = markup;
```
## Writing templates

@@ -253,4 +324,6 @@

Most of the built-in **lit-html** [directives](https://polymer.github.io/lit-html/guide/writing-templates.html#directives) are also included for compatibility when using templates on the server and client (even though some directives are no-ops in a server context):
Most of the built-in **lit-html** [directives](https://polymer.github.io/lit-html/guide/writing-templates.html#directives) are also included for compatibility when using templates on the server and client (even though some directives are no-ops in a server rendered context):
> _NOTE: directives for use in the browser are imported from `@popeindustries/lit-html-server/browser/directives`_
- `asyncAppend(value)`: Renders the items of an AsyncIterable, appending new values after previous values:

@@ -260,2 +333,3 @@

const asyncAppend = require('@popeindustries/lit-html-server/directives/async-append.js');
html`

@@ -272,2 +346,3 @@ <ul>

const cache = require('@popeindustries/lit-html-server/directives/cache.js');
cache(

@@ -288,2 +363,3 @@ loggedIn

const classMap = require('@popeindustries/lit-html-server/directives/class-map.js');
html`

@@ -298,2 +374,3 @@ <div class="${classMap({ red: true })}"></div>

const guard = require('@popeindustries/lit-html-server/directives/guard.js');
html`

@@ -317,2 +394,3 @@ <div>

const ifDefined = require('@popeindustries/lit-html-server/directives/if-defined.js');
html`

@@ -327,2 +405,3 @@ <div class="${ifDefined(className)}"></div>

const repeat = require('@popeindustries/lit-html-server/directives/repeat.js');
html`

@@ -346,2 +425,3 @@ <ul>

const styleMap = require('@popeindustries/lit-html-server/directives/style-map.js');
html`

@@ -352,6 +432,7 @@ <div style="${styleMap({ color: 'red' })}"></div>

- `until(...args)`: renders one of a series of values, including Promises, in priority order. Since it's not possible to render more than once in a server context, primitive sync values are prioritised over async Promises, unless there are no more pending values, in which case the last value is rendered regardless of type:
- `until(...args)`: renders one of a series of values, including Promises, in priority order. Since it's not possible to render more than once in a server context, primitive synchronous values are prioritised over asynchronous Promises. If no synchronous values are passed, the last value is rendered regardless of type:
```js
const until = require('@popeindustries/lit-html-server/directives/until.js');
html`

@@ -358,0 +439,0 @@ <p>

Sorry, the diff of this file is not supported yet

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