UNPKG

lit-html

Version:

HTML templates literals in JavaScript

187 lines (186 loc) 8.71 kB
/** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ var _a; // Scopes that have had styling prepared. Note, must only be done once per // scope. var styledScopes = new Set(); // Map of css per scope. This is collected during first scope render, used when // styling is prepared, and then discarded. var scopeCssStore = new Map(); var ENABLE_SHADYDOM_NOPATCH = true; /** * lit-html patches. These properties cannot be renamed. * * ChildPart.prototype._$getTemplate * * ChildPart.prototype._$setValue */ var polyfillSupport = function (Template, ChildPart) { var _a, _b; // polyfill-support is only needed if ShadyCSS or the ApplyShim is in use // We test at the point of patching, which makes it safe to load // webcomponentsjs and polyfill-support in either order if (window.ShadyCSS === undefined || (window.ShadyCSS.nativeShadow && !window.ShadyCSS.ApplyShim)) { return; } // console.log( // '%c Making lit-html compatible with ShadyDOM/CSS.', // 'color: lightgreen; font-style: italic' // ); var wrap = ((_a = window.ShadyDOM) === null || _a === void 0 ? void 0 : _a.inUse) && ((_b = window.ShadyDOM) === null || _b === void 0 ? void 0 : _b.noPatch) === true ? window.ShadyDOM.wrap : function (node) { return node; }; var needsPrepareStyles = function (name) { return name !== undefined && !styledScopes.has(name); }; var cssForScope = function (name) { var scopeCss = scopeCssStore.get(name); if (scopeCss === undefined) { scopeCssStore.set(name, (scopeCss = [])); } return scopeCss; }; var prepareStyles = function (name, template) { // Get styles var scopeCss = cssForScope(name); var hasScopeCss = scopeCss.length !== 0; if (hasScopeCss) { var style = document.createElement('style'); style.textContent = scopeCss.join('\n'); // Note, it's important to add the style to the *end* of the template so // it doesn't mess up part indices. template.content.appendChild(style); } // Mark this scope as styled. styledScopes.add(name); // Remove stored data since it's no longer needed. scopeCssStore.delete(name); // ShadyCSS removes scopes and removes the style under ShadyDOM and leaves // it under native Shadow DOM window.ShadyCSS.prepareTemplateStyles(template, name); // Note, under native Shadow DOM, the style is added to the beginning of the // template. It must be moved to the *end* of the template so it doesn't // mess up part indices. if (hasScopeCss && window.ShadyCSS.nativeShadow) { // If there were styles but the CSS text was empty, ShadyCSS will // eliminate the style altogether, so the style here could be null var style = template.content.querySelector('style'); if (style !== null) { template.content.appendChild(style); } } }; var scopedTemplateCache = new Map(); /** * Override to extract style elements from the template * and store all style.textContent in the shady scope data. * Note, it's ok to patch Template since it's only used via ChildPart. */ var originalCreateElement = Template.createElement; Template.createElement = function (html, options) { var element = originalCreateElement.call(Template, html, options); var scope = options === null || options === void 0 ? void 0 : options.scope; if (scope !== undefined) { if (!window.ShadyCSS.nativeShadow) { window.ShadyCSS.prepareTemplateDom(element, scope); } // Process styles only if this scope is being prepared. Otherwise, // leave styles as is for back compat with Lit1. if (needsPrepareStyles(scope)) { var scopeCss = cssForScope(scope); // Remove styles and store textContent. var styles = element.content.querySelectorAll('style'); // Store the css in this template in the scope css and remove the <style> // from the template _before_ the node-walk captures part indices scopeCss.push.apply(scopeCss, Array.from(styles).map(function (style) { var _a; (_a = style.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(style); return style.textContent; })); } } return element; }; var renderContainer = document.createDocumentFragment(); var renderContainerMarker = document.createComment(''); var childPartProto = ChildPart.prototype; /** * Patch to apply gathered css via ShadyCSS. This is done only once per scope. */ var setValue = childPartProto._$setValue; childPartProto._$setValue = function (value, directiveParent) { var _a, _b; if (directiveParent === void 0) { directiveParent = this; } var container = wrap(this._$startNode).parentNode; var scope = (_a = this.options) === null || _a === void 0 ? void 0 : _a.scope; if (container instanceof ShadowRoot && needsPrepareStyles(scope)) { // Note, @apply requires outer => inner scope rendering on initial // scope renders to apply property values correctly. Style preparation // is tied to rendering into `shadowRoot`'s and this is typically done by // custom elements. If this is done in `connectedCallback`, as is typical, // the code below ensures the right order since content is rendered // into a fragment first so the hosting element can prepare styles first. // If rendering is done in the constructor, this won't work, but that's // not supported in ShadyDOM anyway. var startNode = this._$startNode; var endNode = this._$endNode; // Temporarily move this part into the renderContainer. renderContainer.appendChild(renderContainerMarker); this._$startNode = renderContainerMarker; this._$endNode = null; // Note, any nested template results render here and their styles will // be extracted and collected. setValue.call(this, value, directiveParent); // Get the template for this result or create a dummy one if a result // is not being rendered. // This property needs to remain unminified. var template = (value === null || value === void 0 ? void 0 : value['_$litType$']) ? this._$committedValue._$template.el : document.createElement('template'); prepareStyles(scope, template); // Note, this is the temporary startNode. renderContainer.removeChild(renderContainerMarker); // When using native Shadow DOM, include prepared style in shadowRoot. if ((_b = window.ShadyCSS) === null || _b === void 0 ? void 0 : _b.nativeShadow) { var style = template.content.querySelector('style'); if (style !== null) { renderContainer.appendChild(style.cloneNode(true)); } } container.insertBefore(renderContainer, endNode); // Move part back to original container. this._$startNode = startNode; this._$endNode = endNode; } else { setValue.call(this, value, directiveParent); } }; /** * Patch ChildPart._$getTemplate to look up templates in a cache bucketed * by element name. */ childPartProto._$getTemplate = function (result) { var _a; var scope = (_a = this.options) === null || _a === void 0 ? void 0 : _a.scope; var templateCache = scopedTemplateCache.get(scope); if (templateCache === undefined) { scopedTemplateCache.set(scope, (templateCache = new Map())); } var template = templateCache.get(result.strings); if (template === undefined) { templateCache.set(result.strings, (template = new Template(result, this.options))); } return template; }; }; { polyfillSupport.noPatchSupported = ENABLE_SHADYDOM_NOPATCH; } { (_a = globalThis.litHtmlPolyfillSupportDevMode) !== null && _a !== void 0 ? _a : (globalThis.litHtmlPolyfillSupportDevMode = polyfillSupport); } //# sourceMappingURL=polyfill-support.js.map