@popeindustries/lit-html-server
Version:
Efficiently render streaming lit-html templates on the server (or in a ServiceWorker!)
1,169 lines (1,151 loc) • 37.8 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
// src/lit-html-server.js
import "./dom-shim.js";
// src/internal/buffer.js
var buffer_exports = {};
__reExport(buffer_exports, node_buffer_star);
import * as node_buffer_star from "node:buffer";
// src/internal/consts.js
var EMPTY_STRING_BUFFER = buffer_exports.Buffer.from("");
var SPACE_STRING_BUFFER = buffer_exports.Buffer.from(" ");
var META_CHILD_CLOSE = buffer_exports.Buffer.from(`<!--/lit-child-->`);
var META_CHILD_OPEN = buffer_exports.Buffer.from(`<!--lit-child-->`);
var META_CLOSE = buffer_exports.Buffer.from(`<!--/lit-->`);
var META_CLOSE_SHADOW = buffer_exports.Buffer.from(`<!--/lit--></template>`);
// src/internal/is.js
import { isDirectiveResult } from "@popeindustries/lit-html/directive-helpers.js";
function isPrimitive(value) {
const type = typeof value;
return value === null || !(type === "object" || type === "function");
}
function isTemplateResult(result) {
const r = (
/** @type { TemplateResult } */
result
);
return r != null && "_$litType$" in r;
}
function isTemplateInstanceOrResult(result) {
const r = (
/** @type { TemplateResult | TemplateInstance } */
result
);
return r != null && ("_$litType$" in r || "_$litServerTemplateInstance$" in r);
}
function isPromise(promise) {
return promise != null && /** @type { Promise<unknown> } */
promise.then != null;
}
function isSyncIterator(iterator) {
return iterator != null && // Ignore strings (which are also iterable)
typeof iterator !== "string" && typeof /** @type { IterableIterator<unknown> } */
iterator[Symbol.iterator] === "function";
}
function isAsyncIterator(iterator) {
return iterator != null && typeof /** @type { AsyncIterable<unknown> } */
iterator[Symbol.asyncIterator] === "function";
}
function isIteratorResult(result) {
return result != null && typeof result === "object" && "value" in result && "done" in result;
}
function isBuffer(value) {
return buffer_exports.Buffer.isBuffer(value);
}
function isArray(value) {
return Array.isArray(value);
}
// src/internal/parts.js
import { noChange, nothing } from "@popeindustries/lit-html";
// src/internal/digest.js
function digestForTemplateStrings(strings) {
const digestSize = 2;
const hashes = new Uint32Array(digestSize).fill(5381);
for (const s of strings) {
for (let i = 0; i < s.length; i++) {
hashes[i % digestSize] = hashes[i % digestSize] * 33 ^ s.charCodeAt(i);
}
}
return buffer_exports.Buffer.from(String.fromCharCode(...new Uint8Array(hashes.buffer)), "binary").toString("base64");
}
// src/internal/escape.js
var HTML_ESCAPES = {
'"': """,
"'": "'",
"&": "&",
"<": "<",
">": ">"
};
var RE_HTML = /["'&<>]/g;
var RE_SCRIPT_STYLE_TAG = /<\/(script|style)/gi;
function escape(string, context = "text") {
switch (context) {
case "script":
case "style":
return string.replace(RE_SCRIPT_STYLE_TAG, "<\\/$1").replace(/<!--/g, "\\x3C!--");
case "attribute":
case "text":
default:
return string.replace(RE_HTML, (match) => HTML_ESCAPES[match]);
}
}
// src/internal/get-element-renderer.js
import { ElementRenderer } from "./element-renderer.js";
function getElementRenderer({ elementRenderers = [] }, tagName, ceClass = customElements.get(tagName)) {
if (ceClass !== void 0) {
for (const renderer of elementRenderers) {
if (renderer.matchesClass(ceClass, tagName)) {
return new renderer(tagName);
}
}
}
return new DefaultElementRenderer(tagName);
}
var DefaultElementRenderer = class extends ElementRenderer {
/**
* @param { string } tagName
*/
constructor(tagName) {
super(tagName);
const ceClass = customElements.get(tagName) ?? HTMLElement;
this.element = /** @type { CustomElement } */
new ceClass();
}
};
// src/internal/parts.js
var partType = {
METADATA: 0,
ATTRIBUTE: 1,
CHILD: 2,
CUSTOMELEMENT_OPEN: 3,
CUSTOMELEMENT_CLOSE: 4
};
var EMPTY_STRINGS_ARRAY = ["", ""];
var RE_RAW_TEXT_ELEMENT = /^(?:script|style|textarea|title)$/i;
var SPACE_BUFFER = buffer_exports.Buffer.from(" ");
var TYPE_TO_LIT_PART_TYPE = {
attribute: 1,
child: 2,
property: 3,
boolean: 4,
event: 5,
element: 6
};
function isAttributePart(part) {
return part.type === partType.ATTRIBUTE;
}
function isChildPart(part) {
return part.type === partType.CHILD;
}
function isCustomElementOpenPart(part) {
return part.type === partType.CUSTOMELEMENT_OPEN;
}
function isCustomElementClosePart(part) {
return part.type === partType.CUSTOMELEMENT_CLOSE;
}
function isMetadataPart(part) {
return part.type === partType.METADATA;
}
function getAttributeTypeFromName(name) {
if (name === "") {
return "element";
}
switch (name[0]) {
case "?":
return "boolean";
case ".":
return "property";
case "@":
return "event";
default:
return "attribute";
}
}
var AttributePart = class {
/**
* Constructor
* @param { string } tagName
*/
constructor(tagName) {
this.length = 0;
this.tagName = tagName;
this.type = partType.ATTRIBUTE;
this._parts = [];
}
/**
* Add data for specific attribute
* @param { AttributeDataType } type
* @param { string } [name]
* @param { string } [value]
* @param { Array<string> } [strings]
*/
addAttributeData(type, name = "", value, strings) {
const hasValue = value !== void 0;
let data;
let length = 0;
switch (type) {
case "boolean": {
const unprefixedName = name.startsWith("?") ? name.slice(1) : name;
length = hasValue ? 0 : 1;
data = {
type,
length,
name,
nameBuffer: buffer_exports.Buffer.from(unprefixedName)
};
if (hasValue) {
data.value = "";
data.resolvedBuffer = data.nameBuffer;
}
break;
}
case "attribute":
case "property": {
length = strings !== void 0 ? strings.length - 1 : 0;
data = {
type,
length,
name,
strings
};
if (hasValue) {
data.value = value;
data.resolvedBuffer = data.type === "attribute" ? buffer_exports.Buffer.from(`${name}="${value}"`) : EMPTY_STRING_BUFFER;
}
break;
}
default: {
length = 1;
data = {
type,
length,
name,
value: "",
resolvedBuffer: EMPTY_STRING_BUFFER
};
}
}
this.length += length;
this._parts.push(data);
}
/**
* Retrieve resolved string Buffer from passed "values".
* Resolves to a single string even when responsible for multiple values.
* @param { Array<unknown> } values
* @param { InternalRenderOptions } options
* @returns { unknown }
*/
resolveValue(values, options) {
const buffer = [];
let valuesIndex = 0;
for (let data of this._parts) {
if (data.resolvedBuffer !== void 0) {
if (data.resolvedBuffer !== EMPTY_STRING_BUFFER) {
buffer.push(SPACE_BUFFER, data.resolvedBuffer);
}
} else {
if (data.type === "boolean") {
const partValue = resolveAttributeValue(values[valuesIndex], this.tagName, data);
if (partValue !== nothing) {
buffer.push(SPACE_BUFFER, data.nameBuffer);
}
} else if (data.type === "attribute") {
let resolvedValue = "";
const strings = (
/** @type { Array<string> } */
data.strings
);
const n = data.length;
let bailed = false;
for (let i = 0; i < n; i++) {
const partValue = resolveAttributeValue(values[valuesIndex + i], this.tagName, data);
if (partValue === nothing) {
bailed = true;
break;
}
resolvedValue += strings[i] + partValue;
}
if (!bailed) {
resolvedValue += strings[strings.length - 1];
resolvedValue = `${data.name}="${resolvedValue}"`;
buffer.push(SPACE_BUFFER, buffer_exports.Buffer.from(resolvedValue));
}
}
}
valuesIndex += data.length;
}
return buffer_exports.Buffer.concat(buffer);
}
};
var ChildPart = class {
/**
* Constructor
* @param { string } tagName
*/
constructor(tagName) {
this.tagName = tagName;
this.type = partType.CHILD;
}
/**
* Retrieve resolved value given passed "value"
* @param { unknown } value
* @param { InternalRenderOptions } options
* @returns { unknown }
*/
resolveValue(value, options) {
return resolveNodeValue(
value,
this.tagName,
RE_RAW_TEXT_ELEMENT.test(this.tagName) || !options.includeHydrationMetadata ? false : true
);
}
};
var CustomElementOpenPart = class extends AttributePart {
/**
* Constructor
* @param { string } tagName
*/
constructor(tagName) {
super(tagName);
this.ceClass = customElements.get(tagName);
this.tagName = tagName;
this.type = partType.CUSTOMELEMENT_OPEN;
}
/**
* Retrieve resolved value given passed "values"
* @param { Array<unknown> } values
* @param { InternalRenderOptions } options
* @returns { unknown }
*/
resolveValue(values, options) {
options.customElementStack.push(this.tagName);
const renderer = getElementRenderer(options, this.tagName, this.ceClass);
renderer.connectedCallback();
let valuesIndex = 0;
for (let data of this._parts) {
if (data.value !== void 0) {
setRendererPropertyOrAttribute(renderer, data.name, data.value);
} else {
if (data.type === "boolean") {
const partValue = resolveAttributeValue(values[valuesIndex], this.tagName, data);
if (partValue !== nothing) {
setRendererPropertyOrAttribute(renderer, data.name, "");
}
} else if (data.type === "property" && data.length === 1) {
const partValue = resolvePropertyValue(values[valuesIndex], this.tagName, data);
if (partValue !== nothing) {
setRendererPropertyOrAttribute(renderer, data.name, partValue);
}
} else if (data.type === "attribute" || data.type === "property") {
let resolvedValue = "";
const strings = (
/** @type { Array<string> } */
data.strings
);
const n = data.length;
let bailed = false;
for (let i = 0; i < n; i++) {
const partValue = resolveAttributeValue(values[valuesIndex + i], this.tagName, data);
if (partValue === nothing) {
bailed = true;
break;
}
resolvedValue += strings[i] + partValue;
}
if (!bailed) {
resolvedValue += strings[strings.length - 1];
setRendererPropertyOrAttribute(renderer, data.name, resolvedValue);
}
}
}
valuesIndex += data.length;
}
const shouldRender = !renderer.element.hasAttribute("render:client");
if (shouldRender) {
renderer.setAttribute("hydrate:defer", "");
}
const resolvedAttributes = buffer_exports.Buffer.from(`${renderer.renderAttributes()}>`);
const result = [resolvedAttributes];
if (options.includeHydrationMetadata) {
result.push(buffer_exports.Buffer.from(`<!--lit-attr ${this.length}-->`));
}
if (shouldRender) {
let renderedContent = renderer.render();
if (renderedContent != null) {
if (typeof renderedContent === "string") {
renderedContent = /** @type { TemplateResult } */
{
_$litType$: 1,
strings: EMPTY_STRINGS_ARRAY,
values: [buffer_exports.Buffer.from(renderedContent)]
};
}
const hasShadowDOM = renderer.element.shadowRoot !== null;
const instance = getTemplateInstance(renderedContent);
if (hasShadowDOM) {
instance.setAsRoot("shadow", renderer.renderStyles());
} else {
instance.setAsRoot("light");
}
result.push(resolveNodeValue(instance, this.tagName, options.includeHydrationMetadata ?? false));
}
}
return result;
}
};
var CustomElementClosePart = class {
/**
* Constructor
* @param { string } tagName
*/
constructor(tagName) {
this.tagName = tagName;
this.type = partType.CUSTOMELEMENT_CLOSE;
}
/**
* Retrieve resolved value and manage active custom element stack
* @param { InternalRenderOptions } options
* @returns { unknown }
*/
resolveValue(options) {
if (options.customElementStack[options.customElementStack.length - 1] === this.tagName) {
options.customElementStack.pop();
} else {
}
return EMPTY_STRING_BUFFER;
}
};
var MetadataPart = class {
/**
* Constructor
* @param { string } tagName
* @param { Buffer } value
*/
constructor(tagName, value) {
this.type = partType.METADATA;
this.tagName = tagName;
this.value = value;
}
/**
* Retrieve resolved value given passed "value"
* @param { InternalRenderOptions } options
* @returns { unknown }
*/
resolveValue(options) {
return RE_RAW_TEXT_ELEMENT.test(this.tagName) || !options.includeHydrationMetadata ? EMPTY_STRING_BUFFER : this.value;
}
};
function resolveAttributeValue(value, tagName, data) {
if (isDirectiveResult(value)) {
const partInfo = {
name: data.name,
tagName,
type: TYPE_TO_LIT_PART_TYPE[data.type]
};
if (data.type === "attribute" && data.strings !== void 0 && data.length > 1) {
partInfo.strings = data.strings.map((string) => string.toString());
}
[, value] = resolveDirectiveValue(value, partInfo);
}
if (value === nothing) {
return value;
}
if (data.type === "boolean") {
return value ? "" : nothing;
} else if (isPrimitive(value)) {
const string = typeof value !== "string" ? String(value) : value;
return escape(string, "attribute");
} else if (isBuffer(value)) {
return value.toString();
} else {
return String(value);
}
}
function resolvePropertyValue(value, tagName, data) {
if (isDirectiveResult(value)) {
const partInfo = {
name: data.name,
tagName,
type: TYPE_TO_LIT_PART_TYPE[data.type]
};
if (data.strings !== void 0 && data.length > 1) {
partInfo.strings = data.strings.map((string) => string.toString());
}
[, value] = resolveDirectiveValue(value, partInfo);
}
return value;
}
function resolveNodeValue(value, tagName, withMetadata) {
let valueIsDirective = false;
if (isDirectiveResult(value)) {
valueIsDirective = true;
const [directiveName, result] = resolveDirectiveValue(value, {
type: TYPE_TO_LIT_PART_TYPE["child"],
tagName
});
if (directiveName === "unsafeHTML" || directiveName === "unsafeSVG") {
const strings = (
/** @type { TemplateResult } */
result.strings
);
const buffer = buffer_exports.Buffer.from(strings[0]);
return withMetadata ? [buffer_exports.Buffer.from(`<!--lit-child ${digestForTemplateStrings(strings)}-->`), buffer, META_CHILD_CLOSE] : buffer;
} else {
value = result;
}
}
if (value === nothing || value == null) {
value = SPACE_STRING_BUFFER;
}
if (isPrimitive(value)) {
let string = typeof value !== "string" ? String(value) : value;
string = escape(string, tagName === "script" || tagName === "style" ? tagName : "text");
value = buffer_exports.Buffer.from(string);
}
if (isBuffer(value)) {
return withMetadata ? [META_CHILD_OPEN, value, META_CHILD_CLOSE] : value;
} else if (isTemplateInstanceOrResult(value)) {
return value;
} else if (isPromise(value)) {
if (!valueIsDirective && withMetadata) {
throw Error(
`lit-html does not support interpolation of Promises, and these will not be rendered correctly in the browser. Use the "until" directive instead.`
);
}
return value.then((value2) => resolveNodeValue(value2, tagName, withMetadata));
} else if (isSyncIterator(value)) {
if (!isArray(value)) {
value = Array.from(value);
}
const collection = withMetadata ? [META_CHILD_OPEN] : [];
for (
let val of
/** @type { Array<unknown> } */
value
) {
val = resolveNodeValue(val, tagName, withMetadata);
if (isArray(val)) {
collection.push(...val);
} else {
collection.push(val);
}
}
if (withMetadata) {
collection.push(META_CHILD_CLOSE);
}
return collection;
} else if (isAsyncIterator(value)) {
if (!valueIsDirective && withMetadata) {
throw Error(
`lit-html does not support interpolation of AsyncIterators, and these will not be rendered correctly in the browser. Use the "async-*" directives instead.`
);
}
return resolveAsyncIteratorValue(value, tagName, withMetadata);
} else {
throw Error(`unknown NodePart value: ${value}`);
}
}
async function* resolveAsyncIteratorValue(iterator, tagName, withMetadata) {
for await (const value of iterator) {
yield resolveNodeValue(value, tagName, withMetadata);
}
}
function resolveDirectiveValue(directiveResult, partInfo) {
const Ctor = directiveResult._$litDirective$;
const { directiveName } = Ctor;
const directive = new Ctor(partInfo);
let result = directive.render(...directiveResult.values);
if (result === noChange) {
result = EMPTY_STRING_BUFFER;
}
return [directiveName, result];
}
function setRendererPropertyOrAttribute(renderer, name, value) {
if (name.startsWith(".")) {
renderer.setProperty(name.slice(1), value);
} else if (name.startsWith("?")) {
renderer.setAttribute(name.slice(1), value);
} else {
renderer.setAttribute(name, value);
}
}
// src/internal/template.js
var HTML_TAGS_WITH_HYPHENS = /* @__PURE__ */ new Set([
"annotation-xml",
"color-profile",
"font-face",
"font-face-src",
"font-face-uri",
"font-face-format",
"font-face-name",
"missing-glyph"
]);
var RE_TAG = /<(?:(?<commentStart>!--|\/[^a-zA-Z])|(?<tagName>\/?[a-zA-Z][^>\s]*)|(?<dynamicTagName>\/?$))/g;
var RE_TAG_END = />/g;
var RE_ATTR = />|[ \t\n\f\r](?:(?<attributeName>[^\s"'>=/]+)(?:(?<spacesAndEquals>[ \t\n\f\r]*=[ \t\n\f\r]*)(?<quoteChar>["'])?)?|$)/g;
var RE_COMMENT_END = /-->/g;
var RE_COMMENT_ALT_END = />/g;
var RE_CUSTOM_ELEMENT = /^[a-z][a-z0-9._\p{Emoji_Presentation}-]*-[a-z0-9._\p{Emoji_Presentation}-]*$/u;
var RE_SINGLE_QUOTED_ATTR_VALUE = /^(?<attributeValue>[^'\n\f\r]*)(?:(?<closingChar>')|$)/;
var RE_DOUBLE_QUOTED_ATTR_VALUE = /^(?<attributeValue>[^"\n\f\r]*)(?:(?<closingChar>")|$)/;
var RE_UNQUOTED_ATTR_VALUE = /^(?<attributeValue>[^'"=<>` \t\n\f\r]+)/;
var TEXT = 1;
var ATTRIBUTE = 2;
var COMMENT = 3;
function getTemplate(strings) {
return new Template(strings);
}
var Template = class {
/**
* Constructor
* @param { TemplateStringsArray } strings
*/
constructor(strings) {
this.digest = digestForTemplateStrings(strings);
this.strings = [];
this.parts = [];
this._parse(strings);
}
/**
* Prepare the template's static strings,
* and create Part instances for the dynamic values,
* based on lit-html syntax.
* @param { TemplateStringsArray } strings
*/
_parse(strings) {
let attributePart;
let isOpeningCustomElement = false;
let mode = TEXT;
let n = strings.length;
let nextString = strings[0];
let regex = RE_TAG;
let tagName = "";
for (let i = 0; i < n; i++) {
const isLastString = i === n - 1;
let string = nextString;
nextString = strings[i + 1] ?? "";
let lastIndex = 0;
let match;
while (lastIndex < string.length) {
regex.lastIndex = lastIndex;
match = regex.exec(string);
if (match === null) {
break;
}
lastIndex = regex.lastIndex;
if (mode === TEXT) {
const groups = (
/** @type { RegexTagGroups } */
match.groups
);
if (groups.commentStart === "!--") {
mode = COMMENT;
regex = RE_COMMENT_END;
} else if (groups.commentStart !== void 0) {
mode = COMMENT;
regex = RE_COMMENT_ALT_END;
} else {
const isDynamicTagName = groups.dynamicTagName !== void 0;
const rawTagName = (
/** @type { string } */
isDynamicTagName ? groups.dynamicTagName : groups.tagName
);
const isOpeningTag = rawTagName[0] !== "/";
const isClosingCustomElement = !isOpeningTag && isCustomElementTagName(rawTagName.slice(1));
isOpeningCustomElement = isCustomElementTagName(rawTagName);
mode = ATTRIBUTE;
regex = RE_ATTR;
if (isOpeningTag) {
tagName = rawTagName;
if (isOpeningCustomElement) {
attributePart = new CustomElementOpenPart(tagName);
} else {
RE_TAG_END.lastIndex = lastIndex;
if (RE_TAG_END.exec(string) !== null) {
lastIndex = RE_TAG_END.lastIndex - 1;
} else {
attributePart = new AttributePart(tagName);
}
}
if (attributePart !== void 0) {
this.strings.push(buffer_exports.Buffer.from(string.slice(0, lastIndex)));
string = string.slice(lastIndex);
lastIndex = 0;
this.parts.push(attributePart);
}
} else if (isClosingCustomElement) {
this.strings.push(buffer_exports.Buffer.from(string.slice(0, lastIndex)));
string = string.slice(lastIndex);
lastIndex = 0;
this.parts.push(new CustomElementClosePart(tagName));
}
if (isDynamicTagName) {
}
}
} else if (mode === ATTRIBUTE) {
const groups = (
/** @type { RegexAttrGroups } */
match.groups
);
if (match[0] === ">") {
if (isOpeningCustomElement) {
string = string.slice(lastIndex);
lastIndex = 0;
} else if (attributePart !== void 0) {
this.strings.push(buffer_exports.Buffer.from(">"));
this.parts.push(new MetadataPart(tagName, buffer_exports.Buffer.from(`<!--lit-attr ${attributePart.length}-->`)));
string = string.slice(lastIndex);
lastIndex = 0;
}
attributePart = void 0;
mode = TEXT;
regex = RE_TAG;
} else if (attributePart !== void 0) {
if (groups.attributeName === void 0) {
attributePart.addAttributeData("element");
} else {
const attributeName = groups.attributeName;
if (groups.spacesAndEquals === void 0) {
attributePart.addAttributeData("boolean", attributeName, "");
} else {
const hasQuotes = groups.quoteChar !== void 0;
let valueString = string.slice(lastIndex);
if (!hasQuotes) {
const valueMatch = RE_UNQUOTED_ATTR_VALUE.exec(valueString);
const attributeValue = (valueMatch == null ? void 0 : valueMatch.groups.attributeValue) ?? "";
if (attributeValue !== "") {
attributePart.addAttributeData("attribute", attributeName, attributeValue);
} else {
attributePart.addAttributeData(getAttributeTypeFromName(attributeName), attributeName, void 0, [
"",
""
]);
}
} else {
const attributeStrings = [];
const quoteChar = (
/** @type { string } */
groups.quoteChar
);
const valueRegex = quoteChar === '"' ? RE_DOUBLE_QUOTED_ATTR_VALUE : RE_SINGLE_QUOTED_ATTR_VALUE;
let j = 0;
while (valueString !== void 0) {
const valueMatch = valueRegex.exec(valueString);
if (valueMatch === null) {
break;
}
lastIndex += valueMatch[0].length;
const { attributeValue = "", closingChar } = (
/** @type { RegexAttrValueGroups } */
valueMatch.groups
);
if (closingChar !== void 0) {
if (j === 0) {
attributePart.addAttributeData("attribute", attributeName, attributeValue);
} else {
attributeStrings.push(attributeValue);
i += j - 1;
nextString = valueString.slice(valueMatch[0].length - 1);
}
break;
}
attributeStrings.push(attributeValue);
valueString = strings[i + ++j];
}
if (attributeStrings.length > 0) {
attributePart.addAttributeData(
getAttributeTypeFromName(attributeName),
attributeName,
void 0,
attributeStrings
);
}
}
}
}
}
} else if (mode === COMMENT) {
mode = TEXT;
regex = RE_TAG;
}
}
if (mode !== ATTRIBUTE) {
this.strings.push(buffer_exports.Buffer.from(string));
if (mode === TEXT) {
if (!isLastString) {
this.parts.push(new ChildPart(tagName));
}
} else if (mode === COMMENT) {
throw Error("parsing expressions inside comment tags is not supported!");
}
}
}
}
};
function isCustomElementTagName(tagName) {
return !HTML_TAGS_WITH_HYPHENS.has(tagName) && RE_CUSTOM_ELEMENT.test(tagName);
}
// src/internal/template-instance.js
var templateCache = /* @__PURE__ */ new Map();
var id = 0;
function getTemplateInstance(result) {
const strings = result.strings;
let template = templateCache.get(strings);
if (template === void 0) {
template = getTemplate(strings);
templateCache.set(strings, template);
}
return new TemplateInstance(template, result.values);
}
var TemplateInstance = class {
/**
* Constructor
* @param { Template } template
* @param { Array<unknown> } values
* @param { boolean } [hydratable]
*/
constructor(template, values, hydratable = false) {
this._$litServerTemplateInstance$ = true;
this.hydratable = hydratable;
this.id = id++;
this.index = 0;
this.maxIndex = template.strings.length + template.parts.length - 1;
this.prefix = buffer_exports.Buffer.from(`<!--lit-child ${template.digest}-->`);
this.suffix = META_CHILD_CLOSE;
this.template = template;
this.valueIndex = 0;
this.values = values;
}
/**
* Set as root instance.
* If a `shadow` root, add optional styles.
* @param { 'light' | 'shadow' } [type]
* @param { string } [styles]
*/
setAsRoot(type = "light", styles = "") {
const litOpen = `<!--lit ${this.template.digest}-->`;
if (type === "light") {
this.prefix = buffer_exports.Buffer.from(litOpen);
this.suffix = META_CLOSE;
} else {
const resolvedStyles = styles ? `<style>${styles}</style>`.replace(/[\n\s]/g, "") : "";
this.prefix = buffer_exports.Buffer.from(`<template shadowroot="open">${resolvedStyles}${litOpen}`);
this.suffix = META_CLOSE_SHADOW;
}
}
/**
* Consume template result content one chunk at a time.
* @param { InternalRenderOptions } options
* @returns { unknown }
*/
readChunk(options) {
const index = this.index / 2 | 0;
const isString = this.index % 2 === 0;
const isFirstString = this.index === 0;
const isLastString = this.index === this.maxIndex;
const withMetadata = options == null ? void 0 : options.includeHydrationMetadata;
if (!isString && this.index >= this.maxIndex) {
this.index = 0;
this.valueIndex = 0;
return null;
}
this.index++;
if (isString) {
let string = this.template.strings[index];
if (withMetadata) {
if (isFirstString) {
string = buffer_exports.Buffer.concat([this.prefix, string]);
}
if (isLastString) {
string = buffer_exports.Buffer.concat([string, this.suffix]);
}
}
return string;
}
const part = this.template.parts[index];
if (isAttributePart(part) || isCustomElementOpenPart(part)) {
const length = part.length;
const values = this.values.slice(this.valueIndex, this.valueIndex + length);
const value = part.resolveValue(values, options);
this.valueIndex += length;
return value;
} else if (isChildPart(part)) {
const value = part.resolveValue(this.values[this.valueIndex], options);
this.valueIndex++;
return value;
} else if (isMetadataPart(part) || isCustomElementClosePart(part)) {
return part.resolveValue(options);
} else {
this.valueIndex++;
throw Error(`unknown part: ${part}`);
}
}
};
// src/lit-html-server.js
import { html } from "@popeindustries/lit-html";
// src/internal/render-processor.js
function getProcessor(renderer, stack, highWaterMark = 0, options = { customElementStack: [] }) {
const buffer = [];
let bufferLength = 0;
let processing = false;
function flushBuffer() {
if (buffer.length > 0) {
const keepPushing = renderer.push(buffer_exports.Buffer.concat(buffer, bufferLength));
bufferLength = buffer.length = 0;
return keepPushing;
}
}
return function process() {
if (processing) {
return;
}
while (true) {
processing = true;
let chunk = stack[0];
let breakLoop = false;
let popStack = true;
if (chunk === void 0) {
flushBuffer();
return renderer.push(null);
}
if (isTemplateInstanceOrResult(chunk)) {
popStack = false;
chunk = getTemplateInstanceChunk(chunk, stack, options);
}
if (chunk !== null) {
if (isBuffer(chunk)) {
buffer.push(chunk);
bufferLength += chunk.length;
if (bufferLength > highWaterMark) {
breakLoop = !flushBuffer();
processing = !breakLoop;
}
} else if (isPromise(chunk)) {
flushBuffer();
breakLoop = true;
stack.unshift(chunk);
chunk.then((chunk2) => {
if (isIteratorResult(chunk2)) {
if (chunk2.done) {
stack.shift();
stack.shift();
} else {
stack[0] = chunk2.value;
}
} else {
stack[0] = chunk2;
}
processing = false;
process();
}).catch((err) => {
stack.length = 0;
renderer.destroy(err);
});
} else if (isArray(chunk)) {
if (stack[0] === chunk) {
popStack = false;
stack.shift();
}
stack.unshift(...chunk);
} else if (isAsyncIterator(chunk)) {
popStack = false;
if (stack[0] !== chunk) {
stack.unshift(chunk);
}
stack.unshift(chunk[Symbol.asyncIterator]().next());
} else {
stack.length = 0;
return renderer.destroy(Error(`unknown chunk type: ${chunk}`));
}
}
if (popStack) {
stack.shift();
}
if (breakLoop) {
break;
}
}
};
}
function getTemplateInstanceChunk(instance, stack, options) {
if (isTemplateResult(instance)) {
instance = getTemplateInstance(instance);
stack[0] = instance;
}
if (instance.hydratable && !options.includeHydrationMetadata) {
options.includeHydrationMetadata = true;
options.hydrationMetadataRootId = instance.id;
}
let chunk = instance.readChunk(options);
if (chunk === null) {
if (options.hydrationMetadataRootId === instance.id) {
options.includeHydrationMetadata = false;
options.hydrationMetadataRootId = void 0;
}
stack.shift();
} else if (isTemplateInstanceOrResult(chunk)) {
stack.unshift(chunk);
chunk = getTemplateInstanceChunk(chunk, stack, options);
}
return chunk;
}
// src/internal/node-stream-template-renderer.js
import { Readable } from "node:stream";
function nodeStreamTemplateRenderer(result, options) {
return new NodeStreamTemplateRenderer(result, options);
}
var NodeStreamTemplateRenderer = class extends Readable {
/**
* Constructor
* @param { TemplateInstance } result - a template result returned from call to "html`...`"
* @param { InternalRenderOptions } [options]
*/
constructor(result, options) {
super({ autoDestroy: true });
this.stack = [result];
this.process = getProcessor(this, this.stack, 16384, options);
}
/**
* Extend Readable.read()
*/
_read() {
if (this.process !== void 0) {
this.process();
}
}
/**
* Extend Readalbe.destroy()
* @param { Error | null } [err]
*/
_destroy(err) {
if (err) {
this.emit("error", err);
}
this.emit("close");
this.process = void 0;
this.stack = void 0;
this.removeAllListeners();
}
};
// src/internal/promise-template-renderer.js
function promiseTemplateRenderer(result, asBuffer = false, options) {
return new Promise((resolve, reject) => {
let stack = [result];
let buffer = [];
let bufferLength = 0;
getProcessor(
{
/** @param { Buffer | null } chunk */
push(chunk) {
if (chunk === null) {
const concatBuffer = buffer_exports.Buffer.concat(buffer, bufferLength);
resolve(asBuffer ? concatBuffer : concatBuffer.toString());
} else {
buffer.push(chunk);
bufferLength += chunk.length;
}
return true;
},
/** @param { Error } err */
destroy(err) {
buffer.length = stack.length = bufferLength = 0;
buffer = void 0;
stack = void 0;
reject(err);
}
},
stack,
0,
options
)();
});
}
// src/internal/web-stream-template-renderer.js
function webStreamTemplateRenderer(result, options) {
if (typeof ReadableStream === "undefined") {
throw Error("ReadableStream not supported on this platform");
}
if (typeof TextEncoder === "undefined") {
throw Error("TextEncoder not supported on this platform");
}
const underlyingSource = {
process: () => {
},
start(controller) {
const encoder = new TextEncoder();
let stack = [result];
this.process = getProcessor(
{
/** @param { Buffer | null } chunk */
push(chunk) {
if (chunk === null) {
controller.close();
return false;
}
controller.enqueue(encoder.encode(chunk.toString()));
return controller.desiredSize != null ? controller.desiredSize > 0 : true;
},
/** @param { Error } err */
destroy(err) {
controller.error(err);
stack = void 0;
}
},
stack,
16384,
options
);
},
pull() {
this.process();
}
};
return new ReadableStream(underlyingSource);
}
// src/lit-html-server.js
import { html as html2, noChange as noChange2, nothing as nothing2, svg } from "@popeindustries/lit-html";
function renderToNodeStream(result, options) {
return nodeStreamTemplateRenderer(getRootTemplateInstance(result), { ...options, customElementStack: [] });
}
function renderToWebStream(result, options) {
return webStreamTemplateRenderer(getRootTemplateInstance(result), { ...options, customElementStack: [] });
}
function renderToString(result, options) {
return promiseTemplateRenderer(getRootTemplateInstance(result), false, { ...options, customElementStack: [] });
}
function renderToBuffer(result, options) {
return promiseTemplateRenderer(getRootTemplateInstance(result), true, { ...options, customElementStack: [] });
}
function getRootTemplateInstance(result) {
if (!isTemplateResult(result)) {
result = html`${result}`;
}
const instance = getTemplateInstance(
/** @type { TemplateResult } */
result
);
instance.setAsRoot("light");
return instance;
}
export {
getTemplateInstance as __internalGetTemplateInstance__,
html2 as html,
noChange2 as noChange,
nothing2 as nothing,
renderToBuffer,
renderToNodeStream,
renderToString,
renderToWebStream,
svg
};