/** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ const DEV_MODE = true; const ENABLE_EXTRA_SECURITY_HOOKS = true; const ENABLE_SHADYDOM_NOPATCH = true; const NODE_MODE = false; // Allows minifiers to rename references to globalThis const global = globalThis; /** * Useful for visualizing and logging insights into what the Lit template system is doing. * * Compiled out of prod mode builds. */ const debugLogEvent = DEV_MODE ? (event) => { const shouldEmit = global .emitLitDebugLogEvents; if (!shouldEmit) { return; } global.dispatchEvent(new CustomEvent('lit-debug', { detail: event, })); } : undefined; // Used for connecting beginRender and endRender events when there are nested // renders when errors are thrown preventing an endRender event from being // called. let debugLogRenderId = 0; let issueWarning; if (DEV_MODE) { global.litIssuedWarnings ??= new Set(); // Issue a warning, if we haven't already. issueWarning = (code, warning) => { warning += code ? ` See https://lit.dev/msg/${code} for more information.` : ''; if (!global.litIssuedWarnings.has(warning)) { console.warn(warning); global.litIssuedWarnings.add(warning); } }; issueWarning('dev-mode', `Lit is in dev mode. Not recommended for production!`); } const wrap = ENABLE_SHADYDOM_NOPATCH && global.ShadyDOM?.inUse && global.ShadyDOM?.noPatch === true ? global.ShadyDOM.wrap : (node) => node; const trustedTypes = global.trustedTypes; /** * Our TrustedTypePolicy for HTML which is declared using the html template * tag function. * * That HTML is a developer-authored constant, and is parsed with innerHTML * before any untrusted expressions have been mixed in. Therefor it is * considered safe by construction. */ const policy = trustedTypes ? trustedTypes.createPolicy('lit-html', { createHTML: (s) => s, }) : undefined; const identityFunction = (value) => value; const noopSanitizer = (_node, _name, _type) => identityFunction; /** Sets the global sanitizer factory. */ const setSanitizer = (newSanitizer) => { if (!ENABLE_EXTRA_SECURITY_HOOKS) { return; } if (sanitizerFactoryInternal !== noopSanitizer) { throw new Error(`Attempted to overwrite existing lit-html security policy.` + ` setSanitizeDOMValueFactory should be called at most once.`); } sanitizerFactoryInternal = newSanitizer; }; /** * Only used in internal tests, not a part of the public API. */ const _testOnlyClearSanitizerFactoryDoNotCallOrElse = () => { sanitizerFactoryInternal = noopSanitizer; }; const createSanitizer = (node, name, type) => { return sanitizerFactoryInternal(node, name, type); }; // Added to an attribute name to mark the attribute as bound so we can find // it easily. const boundAttributeSuffix = '$lit$'; // This marker is used in many syntactic positions in HTML, so it must be // a valid element name and attribute name. We don't support dynamic names (yet) // but this at least ensures that the parse tree is closer to the template // intention. const marker = `lit$${String(Math.random()).slice(9)}$`; // String used to tell if a comment is a marker comment const markerMatch = '?' + marker; // Text used to insert a comment marker node. We use processing instruction // syntax because it's slightly smaller, but parses as a comment node. const nodeMarker = `<${markerMatch}>`; const d = NODE_MODE && global.document === undefined ? { createTreeWalker() { return {}; }, } : document; // Creates a dynamic marker. We never have to search for these in the DOM. const createMarker = () => d.createComment(''); const isPrimitive = (value) => value === null || (typeof value != 'object' && typeof value != 'function'); const isArray = Array.isArray; const isIterable = (value) => isArray(value) || // eslint-disable-next-line @typescript-eslint/no-explicit-any typeof value?.[Symbol.iterator] === 'function'; const SPACE_CHAR = `[ \t\n\f\r]`; const ATTR_VALUE_CHAR = `[^ \t\n\f\r"'\`<>=]`; const NAME_CHAR = `[^\\s"'>=/]`; // These regexes represent the five parsing states that we care about in the // Template's HTML scanner. They match the *end* of the state they're named // after. // Depending on the match, we transition to a new state. If there's no match, // we stay in the same state. // Note that the regexes are stateful. We utilize lastIndex and sync it // across the multiple regexes used. In addition to the five regexes below // we also dynamically create a regex to find the matching end tags for raw // text elements. /** * End of text is: `<` followed by: * (comment start) or (tag) or (dynamic tag binding) */ const textEndRegex = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g; const COMMENT_START = 1; const TAG_NAME = 2; const DYNAMIC_TAG_NAME = 3; const commentEndRegex = /-->/g; /** * Comments not started with