UNPKG

element-vir

Version:

Heroic. Reactive. Declarative. Type safe. Web components without compromise.

97 lines (96 loc) 4.29 kB
/* eslint-disable @typescript-eslint/no-deprecated */ import { check } from '@augment-vir/assert'; import { collapseWhiteSpace, getOrSet, safeMatch } from '@augment-vir/common'; import { assign } from '../../declarative-element/directives/assign.directive.js'; import { declarativeElementRequired } from '../../require-declarative-element.js'; import { hasTagName, isMinimalDefinitionWithInputs, } from '../minimal-element-definition.js'; import { transformTemplate } from '../transform-template.js'; import { tagNameKeys } from './tag-name-keys.js'; export function mapHtmlValues(inputTemplateStrings, inputValues) { return inputValues.map((currentValue, currentValueIndex) => { const lastString = inputTemplateStrings[currentValueIndex]; const nextString = inputTemplateStrings[currentValueIndex + 1]; if (lastString && nextString) { const { shouldHaveTagNameHere } = classifyValue(lastString, nextString); if (shouldHaveTagNameHere && check.isString(currentValue)) { const replacement = { tagName: currentValue, tagInterpolationKey: getOrSet(tagNameKeys, currentValue, () => { return { tagName: currentValue }; }), }; return replacement; } } return currentValue; }); } function classifyValue(lastNewString, currentTemplateString) { const isOpeningTag = lastNewString.trim().endsWith('<') && !!currentTemplateString.match(/^[\s>]/); const isClosingTag = lastNewString.trim().endsWith('</') && currentTemplateString.trim().startsWith('>'); const shouldHaveTagNameHere = isOpeningTag || isClosingTag; return { isOpeningTag, shouldHaveTagNameHere, }; } function transformHtml(...[lastNewString, currentTemplateString, rawCurrentValue,]) { const currentValue = isMinimalDefinitionWithInputs(rawCurrentValue) ? rawCurrentValue.definition : rawCurrentValue; const { isOpeningTag, shouldHaveTagNameHere } = classifyValue(lastNewString, currentTemplateString); const isTagNameWrapper = hasTagName(currentValue); if (isTagNameWrapper && shouldHaveTagNameHere && currentValue.tagInterpolationKey) { return { replacement: currentValue.tagName, getExtraValues: undefined, }; } if (shouldHaveTagNameHere && !isTagNameWrapper) { console.error({ lastNewString, currentTemplateString, currentValue, }); throw new Error(`Got interpolated tag name but found no tag name on the given value: '${currentValue?.tagName || currentValue?.prototype?.constructor?.name || currentValue?.constructor?.name}'`); } if (!shouldHaveTagNameHere || !isTagNameWrapper) { return undefined; } const replacement = currentValue.tagName; return { replacement, getExtraValues(extraValueCurrentValue) { const assignedInputs = isMinimalDefinitionWithInputs(extraValueCurrentValue) ? extraValueCurrentValue.inputs : undefined; return [ isOpeningTag && assignedInputs ? assign(assignedInputs) : undefined, ].filter(check.isTruthy); }, }; } function extractCustomElementTags(input) { const tagNameMatches = safeMatch(input, /<\/\s*[^\s><]+\s*>/g); return tagNameMatches.reduce((accum, match) => { const tagName = collapseWhiteSpace(match.replace(/\n/g, ' ')).replace(/<\/|>/g, ''); // custom elements always have a dash in them if (tagName.includes('-')) { return accum.concat(tagName); } return accum; }, []); } function stringValidator(input) { if (declarativeElementRequired) { const customElementTagNames = extractCustomElementTags(input); if (customElementTagNames.length) { console.error(`Custom element tags must be interpolated from declarative elements: ${customElementTagNames.join(', ')}`); } } } export function transformHtmlTemplate(litTemplate) { return transformTemplate(litTemplate.strings, litTemplate.values, transformHtml, stringValidator); }