From dded36fb093959fd14f03133ba2eea070a1cbedd Mon Sep 17 00:00:00 2001 From: yutent Date: Tue, 7 Mar 2023 19:15:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E7=94=A8lit-core=E4=BA=8C=E6=AC=A1?= =?UTF-8?q?=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants.js | 5 + src/css.js | 16 +- src/html.js | 945 +++++++++++++++++++++++++++++++++++++++++++++-- src/index.js | 457 ++++++++++++++++++----- 4 files changed, 1298 insertions(+), 125 deletions(-) diff --git a/src/constants.js b/src/constants.js index 1365312..9652672 100644 --- a/src/constants.js +++ b/src/constants.js @@ -5,3 +5,8 @@ */ export const finalize = Symbol('finalize') + +export const RESET_CSS_STYLE = ` + * {box-sizing: border-box;margin: 0;padding: 0;} + ::before,::after {box-sizing: border-box;} +` diff --git a/src/css.js b/src/css.js index f2b131a..a831e9e 100644 --- a/src/css.js +++ b/src/css.js @@ -4,22 +4,10 @@ * @date 2023/03/06 16:27:49 */ -export const RESET_CSS_STYLE = css` - * { - box-sizing: border-box; - margin: 0; - padding: 0; - } - ::before, - ::after { - box-sizing: border-box; - } -` +import { RESET_CSS_STYLE } from './constants.js' export function css(strs, ...args) { - let output = ` - - ` + let output = '' let tmp = Array.from(strs) while (tmp.length) { output += tmp.shift() + (args.shift() || '') diff --git a/src/html.js b/src/html.js index 78d691c..b251fcb 100644 --- a/src/html.js +++ b/src/html.js @@ -1,32 +1,925 @@ -export function html(strs, ...args) { - let output = '' - let tmp = Array.from(strs) - // console.log(tmp, args) +var ENABLE_EXTRA_SECURITY_HOOKS = true +var global3 = window +var debugLogEvent2 = void 0 +var debugLogRenderId = 0 +var issueWarning2 - while (tmp.length) { - let _ = args.shift() - switch (typeof _) { - case 'function': - console.log([_], _.name) - _ = _.name - break - case 'object': - if (Array.isArray(_)) { - _ = _.join('') - } - break - } - output += tmp.shift() + (_ === void 0 ? '' : _) +var wrap = node => node + +var identityFunction = value => value +var noopSanitizer = (_node, _name, _type) => identityFunction +var 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 +} +var _testOnlyClearSanitizerFactoryDoNotCallOrElse = () => { + sanitizerFactoryInternal = noopSanitizer +} +var createSanitizer = (node, name, type) => { + return sanitizerFactoryInternal(node, name, type) +} +var boundAttributeSuffix = '$wc$' +var marker = `wc$${String(Math.random()).slice(9)}$` +var markerMatch = '?' + marker +var nodeMarker = `<${markerMatch}>` +var d = document +var createMarker = (v = '') => d.createComment(v) +var isPrimitive = value => + value === null || (typeof value != 'object' && typeof value != 'function') +var isArray = Array.isArray +var isIterable = value => + isArray(value) || + typeof (value === null || value === void 0 + ? void 0 + : value[Symbol.iterator]) === 'function' +var SPACE_CHAR = `[ +\f\r]` +var ATTR_VALUE_CHAR = `[^ +\f\r"'\`<>=]` +var NAME_CHAR = `[^\\s"'>=/]` +var textEndRegex = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g +var COMMENT_START = 1 +var TAG_NAME = 2 +var DYNAMIC_TAG_NAME = 3 +var commentEndRegex = /-->/g +var comment2EndRegex = />/g +var tagEndRegex = new RegExp( + `>|${SPACE_CHAR}(?:(${NAME_CHAR}+)(${SPACE_CHAR}*=${SPACE_CHAR}*(?:${ATTR_VALUE_CHAR}|("|')|))|$)`, + 'g' +) +var ENTIRE_MATCH = 0 +var ATTRIBUTE_NAME = 1 +var SPACES_AND_EQUALS = 2 +var QUOTE_CHAR = 3 +var singleQuoteAttrEndRegex = /'/g +var doubleQuoteAttrEndRegex = /"/g +var rawTextElement = /^(?:script|style|textarea|title)$/i +var HTML_RESULT = 1 +var SVG_RESULT = 2 +var ATTRIBUTE_PART = 1 +var CHILD_PART = 2 +var PROPERTY_PART = 3 +var BOOLEAN_ATTRIBUTE_PART = 4 +var EVENT_PART = 5 +var ELEMENT_PART = 6 +var COMMENT_PART = 7 +var tag = + type => + (strings, ...values) => { + return { + __dom_type__: type, + strings, + values + } + } +export const html = tag(HTML_RESULT) +export const svg = tag(SVG_RESULT) +var noChange = Symbol.for('wc-noChange') +var nothing = Symbol.for('wc-nothing') +var templateCache = /* @__PURE__ */ new WeakMap() +var walker = d.createTreeWalker(d, 129, null, false) +var sanitizerFactoryInternal = noopSanitizer +var getTemplateHtml = (strings, type) => { + const l = strings.length - 1 + const attrNames = [] + let html2 = type === SVG_RESULT ? '' : '' + let rawTextEndRegex + let regex = textEndRegex + for (let i = 0; i < l; i++) { + const s = strings[i] + let attrNameEndIndex = -1 + let attrName + let lastIndex = 0 + let match + while (lastIndex < s.length) { + regex.lastIndex = lastIndex + match = regex.exec(s) + if (match === null) { + break + } + lastIndex = regex.lastIndex + if (regex === textEndRegex) { + if (match[COMMENT_START] === '!--') { + regex = commentEndRegex + } else if (match[COMMENT_START] !== void 0) { + regex = comment2EndRegex + } else if (match[TAG_NAME] !== void 0) { + if (rawTextElement.test(match[TAG_NAME])) { + rawTextEndRegex = new RegExp(`') { + regex = + rawTextEndRegex !== null && rawTextEndRegex !== void 0 + ? rawTextEndRegex + : textEndRegex + attrNameEndIndex = -1 + } else if (match[ATTRIBUTE_NAME] === void 0) { + attrNameEndIndex = -2 + } else { + attrNameEndIndex = regex.lastIndex - match[SPACES_AND_EQUALS].length + attrName = match[ATTRIBUTE_NAME] + regex = + match[QUOTE_CHAR] === void 0 + ? tagEndRegex + : match[QUOTE_CHAR] === '"' + ? doubleQuoteAttrEndRegex + : singleQuoteAttrEndRegex + } + } else if ( + regex === doubleQuoteAttrEndRegex || + regex === singleQuoteAttrEndRegex + ) { + regex = tagEndRegex + } else if (regex === commentEndRegex || regex === comment2EndRegex) { + regex = textEndRegex + } else { + regex = tagEndRegex + rawTextEndRegex = void 0 + } + } + + const end = + regex === tagEndRegex && strings[i + 1].startsWith('/>') ? ' ' : '' + html2 += + regex === textEndRegex + ? s + nodeMarker + : attrNameEndIndex >= 0 + ? (attrNames.push(attrName), + s.slice(0, attrNameEndIndex) + + boundAttributeSuffix + + s.slice(attrNameEndIndex)) + + marker + + end + : s + + marker + + (attrNameEndIndex === -2 ? (attrNames.push(void 0), i) : end) + } + const htmlResult = + html2 + (strings[l] || '') + (type === SVG_RESULT ? '' : '') + if (!Array.isArray(strings) || !strings.hasOwnProperty('raw')) { + let message = 'invalid template strings array' + + throw new Error(message) + } + return [htmlResult, attrNames] +} +class Template { + constructor({ strings, ['__dom_type__']: type }, options) { + this.parts = [] + let node + let nodeIndex = 0 + let attrNameIndex = 0 + const partCount = strings.length - 1 + const parts = this.parts + const [html2, attrNames] = getTemplateHtml(strings, type) + this.el = Template.createElement(html2, options) + walker.currentNode = this.el.content + if (type === SVG_RESULT) { + const content = this.el.content + const svgElement = content.firstChild + svgElement.remove() + content.append(...svgElement.childNodes) + } + while ((node = walker.nextNode()) !== null && parts.length < partCount) { + if (node.nodeType === 1) { + if (node.hasAttributes()) { + const attrsToRemove = [] + for (const name of node.getAttributeNames()) { + if ( + name.endsWith(boundAttributeSuffix) || + name.startsWith(marker) + ) { + const realName = attrNames[attrNameIndex++] + attrsToRemove.push(name) + if (realName !== void 0) { + const value = node.getAttribute( + realName.toLowerCase() + boundAttributeSuffix + ) + const statics = value.split(marker) + const m = /([.?@])?(.*)/.exec(realName) + parts.push({ + type: ATTRIBUTE_PART, + index: nodeIndex, + name: m[2], + strings: statics, + ctor: + m[1] === '.' + ? PropertyPart + : m[1] === '?' + ? BooleanAttributePart + : m[1] === '@' + ? EventPart + : AttributePart + }) + } else { + parts.push({ + type: ELEMENT_PART, + index: nodeIndex + }) + } + } + } + for (const name of attrsToRemove) { + node.removeAttribute(name) + } + } + if (rawTextElement.test(node.tagName)) { + const strings2 = node.textContent.split(marker) + const lastIndex = strings2.length - 1 + if (lastIndex > 0) { + node.textContent = '' + for (let i = 0; i < lastIndex; i++) { + node.append(strings2[i], createMarker()) + walker.nextNode() + parts.push({ type: CHILD_PART, index: ++nodeIndex }) + } + node.append(strings2[lastIndex], createMarker()) + } + } + } else if (node.nodeType === 8) { + const data = node.data + if (data === markerMatch) { + parts.push({ type: CHILD_PART, index: nodeIndex }) + } else { + let i = -1 + while ((i = node.data.indexOf(marker, i + 1)) !== -1) { + parts.push({ type: COMMENT_PART, index: nodeIndex }) + i += marker.length - 1 + } + } + } + nodeIndex++ + } + debugLogEvent2 === null || debugLogEvent2 === void 0 + ? void 0 + : debugLogEvent2({ + kind: 'template prep', + template: this, + clonableTemplate: this.el, + parts: this.parts, + strings + }) + } + static createElement(html2, _options) { + const el = d.createElement('template') + el.innerHTML = html2 + return el + } +} +function resolveDirective(part, value, parent = part, attributeIndex) { + var _a4, _b4, _c4 + var _d3 + if (value === noChange) { + return value + } + let currentDirective = + attributeIndex !== void 0 + ? (_a4 = parent.__directives) === null || _a4 === void 0 + ? void 0 + : _a4[attributeIndex] + : parent.__directive + const nextDirectiveConstructor = isPrimitive(value) + ? void 0 + : value['_$litDirective$'] + if ( + (currentDirective === null || currentDirective === void 0 + ? void 0 + : currentDirective.constructor) !== nextDirectiveConstructor + ) { + ;(_b4 = + currentDirective === null || currentDirective === void 0 + ? void 0 + : currentDirective['_$notifyDirectiveConnectionChanged']) === null || + _b4 === void 0 + ? void 0 + : _b4.call(currentDirective, false) + if (nextDirectiveConstructor === void 0) { + currentDirective = void 0 + } else { + currentDirective = new nextDirectiveConstructor(part) + currentDirective._$initialize(part, parent, attributeIndex) + } + if (attributeIndex !== void 0) { + ;((_c4 = (_d3 = parent).__directives) !== null && _c4 !== void 0 + ? _c4 + : (_d3.__directives = []))[attributeIndex] = currentDirective + } else { + parent.__directive = currentDirective + } + } + if (currentDirective !== void 0) { + value = resolveDirective( + part, + currentDirective._$resolve(part, value.values), + currentDirective, + attributeIndex + ) + } + return value +} +class TemplateInstance { + constructor(template, parent) { + this._parts = [] + this._$disconnectableChildren = void 0 + this._$template = template + this._$parent = parent + } + get parentNode() { + return this._$parent.parentNode + } + get _$isConnected() { + return this._$parent._$isConnected + } + _clone(options) { + var _a4 + const { + el: { content }, + parts + } = this._$template + const fragment = ( + (_a4 = + options === null || options === void 0 + ? void 0 + : options.creationScope) !== null && _a4 !== void 0 + ? _a4 + : d + ).importNode(content, true) + walker.currentNode = fragment + let node = walker.nextNode() + let nodeIndex = 0 + let partIndex = 0 + let templatePart = parts[0] + while (templatePart !== void 0) { + if (nodeIndex === templatePart.index) { + let part + if (templatePart.type === CHILD_PART) { + part = new ChildPart(node, node.nextSibling, this, options) + } else if (templatePart.type === ATTRIBUTE_PART) { + part = new templatePart.ctor( + node, + templatePart.name, + templatePart.strings, + this, + options + ) + } else if (templatePart.type === ELEMENT_PART) { + part = new ElementPart(node, this, options) + } + this._parts.push(part) + templatePart = parts[++partIndex] + } + if ( + nodeIndex !== + (templatePart === null || templatePart === void 0 + ? void 0 + : templatePart.index) + ) { + node = walker.nextNode() + nodeIndex++ + } + } + return fragment + } + _update(values) { + let i = 0 + for (const part of this._parts) { + if (part !== void 0) { + debugLogEvent2 === null || debugLogEvent2 === void 0 + ? void 0 + : debugLogEvent2({ + kind: 'set part', + part, + value: values[i], + valueIndex: i, + values, + templateInstance: this + }) + if (part.strings !== void 0) { + part._$setValue(values, part, i) + i += part.strings.length - 2 + } else { + part._$setValue(values[i]) + } + } + i++ + } + } +} +class ChildPart { + constructor(startNode, endNode, parent, options) { + var _a4 + this.type = CHILD_PART + this._$committedValue = nothing + this._$disconnectableChildren = void 0 + this._$startNode = startNode + this._$endNode = endNode + this._$parent = parent + this.options = options + this.__isConnected = + (_a4 = + options === null || options === void 0 + ? void 0 + : options.isConnected) !== null && _a4 !== void 0 + ? _a4 + : true + if (ENABLE_EXTRA_SECURITY_HOOKS) { + this._textSanitizer = void 0 + } + } + get _$isConnected() { + var _a4, _b4 + return (_b4 = + (_a4 = this._$parent) === null || _a4 === void 0 + ? void 0 + : _a4._$isConnected) !== null && _b4 !== void 0 + ? _b4 + : this.__isConnected + } + get parentNode() { + let parentNode = wrap(this._$startNode).parentNode + const parent = this._$parent + if (parent !== void 0 && parentNode.nodeType === 11) { + parentNode = parent.parentNode + } + return parentNode + } + get startNode() { + return this._$startNode + } + get endNode() { + return this._$endNode + } + _$setValue(value, directiveParent = this) { + var _a4 + + value = resolveDirective(this, value, directiveParent) + if (isPrimitive(value)) { + if (value === nothing || value == null || value === '') { + if (this._$committedValue !== nothing) { + debugLogEvent2 === null || debugLogEvent2 === void 0 + ? void 0 + : debugLogEvent2({ + kind: 'commit nothing to child', + start: this._$startNode, + end: this._$endNode, + parent: this._$parent, + options: this.options + }) + this._$clear() + } + this._$committedValue = nothing + } else if (value !== this._$committedValue && value !== noChange) { + this._commitText(value) + } + } else if (value['__dom_type__'] !== void 0) { + this._commitTemplateResult(value) + } else if (value.nodeType !== void 0) { + this._commitNode(value) + } else if (isIterable(value)) { + this._commitIterable(value) + } else { + this._commitText(value) + } + } + _insert(node, ref = this._$endNode) { + return wrap(wrap(this._$startNode).parentNode).insertBefore(node, ref) + } + _commitNode(value) { + var _a4 + if (this._$committedValue !== value) { + this._$clear() + if ( + ENABLE_EXTRA_SECURITY_HOOKS && + sanitizerFactoryInternal !== noopSanitizer + ) { + const parentNodeName = + (_a4 = this._$startNode.parentNode) === null || _a4 === void 0 + ? void 0 + : _a4.nodeName + if (parentNodeName === 'STYLE' || parentNodeName === 'SCRIPT') { + let message = 'Forbidden' + + throw new Error(message) + } + } + debugLogEvent2 === null || debugLogEvent2 === void 0 + ? void 0 + : debugLogEvent2({ + kind: 'commit node', + start: this._$startNode, + parent: this._$parent, + value, + options: this.options + }) + this._$committedValue = this._insert(value) + } + } + _commitText(value) { + if ( + this._$committedValue !== nothing && + isPrimitive(this._$committedValue) + ) { + const node = wrap(this._$startNode).nextSibling + if (ENABLE_EXTRA_SECURITY_HOOKS) { + if (this._textSanitizer === void 0) { + this._textSanitizer = createSanitizer(node, 'data', 'property') + } + value = this._textSanitizer(value) + } + debugLogEvent2 === null || debugLogEvent2 === void 0 + ? void 0 + : debugLogEvent2({ + kind: 'commit text', + node, + value, + options: this.options + }) + node.data = value + } else { + if (ENABLE_EXTRA_SECURITY_HOOKS) { + const textNode = document.createTextNode('') + this._commitNode(textNode) + if (this._textSanitizer === void 0) { + this._textSanitizer = createSanitizer(textNode, 'data', 'property') + } + value = this._textSanitizer(value) + debugLogEvent2 === null || debugLogEvent2 === void 0 + ? void 0 + : debugLogEvent2({ + kind: 'commit text', + node: textNode, + value, + options: this.options + }) + textNode.data = value + } else { + this._commitNode(d.createTextNode(value)) + debugLogEvent2 === null || debugLogEvent2 === void 0 + ? void 0 + : debugLogEvent2({ + kind: 'commit text', + node: wrap(this._$startNode).nextSibling, + value, + options: this.options + }) + } + } + this._$committedValue = value + } + _commitTemplateResult(result) { + var _a4 + const { values, ['__dom_type__']: type } = result + const template = + typeof type === 'number' + ? this._$getTemplate(result) + : (type.el === void 0 && + (type.el = Template.createElement(type.h, this.options)), + type) + if ( + ((_a4 = this._$committedValue) === null || _a4 === void 0 + ? void 0 + : _a4._$template) === template + ) { + this._$committedValue._update(values) + } else { + const instance = new TemplateInstance(template, this) + const fragment = instance._clone(this.options) + + instance._update(values) + + this._commitNode(fragment) + this._$committedValue = instance + } + } + _$getTemplate(result) { + let template = templateCache.get(result.strings) + if (template === void 0) { + templateCache.set(result.strings, (template = new Template(result))) + } + return template + } + _commitIterable(value) { + if (!isArray(this._$committedValue)) { + this._$committedValue = [] + this._$clear() + } + const itemParts = this._$committedValue + let partIndex = 0 + let itemPart + for (const item of value) { + if (partIndex === itemParts.length) { + itemParts.push( + (itemPart = new ChildPart( + this._insert(createMarker()), + this._insert(createMarker()), + this, + this.options + )) + ) + } else { + itemPart = itemParts[partIndex] + } + itemPart._$setValue(item) + partIndex++ + } + if (partIndex < itemParts.length) { + this._$clear(itemPart && wrap(itemPart._$endNode).nextSibling, partIndex) + itemParts.length = partIndex + } + } + _$clear(start = wrap(this._$startNode).nextSibling, from) { + var _a4 + ;(_a4 = this._$notifyConnectionChanged) === null || _a4 === void 0 + ? void 0 + : _a4.call(this, false, true, from) + while (start && start !== this._$endNode) { + const n = wrap(start).nextSibling + wrap(start).remove() + start = n + } + } + setConnected(isConnected) { + var _a4 + if (this._$parent === void 0) { + this.__isConnected = isConnected + ;(_a4 = this._$notifyConnectionChanged) === null || _a4 === void 0 + ? void 0 + : _a4.call(this, isConnected) + } + } +} +class AttributePart { + constructor(element, name, strings, parent, options) { + this.type = ATTRIBUTE_PART + this._$committedValue = nothing + this._$disconnectableChildren = void 0 + this.element = element + this.name = name + this._$parent = parent + this.options = options + if (strings.length > 2 || strings[0] !== '' || strings[1] !== '') { + this._$committedValue = new Array(strings.length - 1).fill(new String()) + this.strings = strings + } else { + this._$committedValue = nothing + } + if (ENABLE_EXTRA_SECURITY_HOOKS) { + this._sanitizer = void 0 + } + } + get tagName() { + return this.element.tagName + } + get _$isConnected() { + return this._$parent._$isConnected + } + _$setValue(value, directiveParent = this, valueIndex, noCommit) { + const strings = this.strings + let change = false + if (strings === void 0) { + value = resolveDirective(this, value, directiveParent, 0) + change = + !isPrimitive(value) || + (value !== this._$committedValue && value !== noChange) + if (change) { + this._$committedValue = value + } + } else { + const values = value + value = strings[0] + let i, v + for (i = 0; i < strings.length - 1; i++) { + v = resolveDirective(this, values[valueIndex + i], directiveParent, i) + if (v === noChange) { + v = this._$committedValue[i] + } + change || (change = !isPrimitive(v) || v !== this._$committedValue[i]) + if (v === nothing) { + value = nothing + } else if (value !== nothing) { + value += (v !== null && v !== void 0 ? v : '') + strings[i + 1] + } + this._$committedValue[i] = v + } + } + if (change && !noCommit) { + this._commitValue(value) + } + } + _commitValue(value) { + if (value === nothing) { + wrap(this.element).removeAttribute(this.name) + } else { + if (ENABLE_EXTRA_SECURITY_HOOKS) { + if (this._sanitizer === void 0) { + this._sanitizer = sanitizerFactoryInternal( + this.element, + this.name, + 'attribute' + ) + } + value = this._sanitizer(value !== null && value !== void 0 ? value : '') + } + debugLogEvent2 === null || debugLogEvent2 === void 0 + ? void 0 + : debugLogEvent2({ + kind: 'commit attribute', + element: this.element, + name: this.name, + value, + options: this.options + }) + wrap(this.element).setAttribute( + this.name, + value !== null && value !== void 0 ? value : '' + ) + } + } +} +class PropertyPart extends AttributePart { + constructor() { + super(...arguments) + this.type = PROPERTY_PART + } + _commitValue(value) { + if (ENABLE_EXTRA_SECURITY_HOOKS) { + if (this._sanitizer === void 0) { + this._sanitizer = sanitizerFactoryInternal( + this.element, + this.name, + 'property' + ) + } + value = this._sanitizer(value) + } + debugLogEvent2 === null || debugLogEvent2 === void 0 + ? void 0 + : debugLogEvent2({ + kind: 'commit property', + element: this.element, + name: this.name, + value, + options: this.options + }) + this.element[this.name] = value === nothing ? void 0 : value + } +} +var emptyStringForBooleanAttribute2 = '' +class BooleanAttributePart extends AttributePart { + constructor() { + super(...arguments) + this.type = BOOLEAN_ATTRIBUTE_PART + } + _commitValue(value) { + debugLogEvent2 === null || debugLogEvent2 === void 0 + ? void 0 + : debugLogEvent2({ + kind: 'commit boolean attribute', + element: this.element, + name: this.name, + value: !!(value && value !== nothing), + options: this.options + }) + if (value && value !== nothing) { + wrap(this.element).setAttribute( + this.name, + emptyStringForBooleanAttribute2 + ) + } else { + wrap(this.element).removeAttribute(this.name) + } + } +} +class EventPart extends AttributePart { + constructor(element, name, strings, parent, options) { + super(element, name, strings, parent, options) + this.type = EVENT_PART + } + _$setValue(newListener, directiveParent = this) { + var _a4 + newListener = + (_a4 = resolveDirective(this, newListener, directiveParent, 0)) !== + null && _a4 !== void 0 + ? _a4 + : nothing + if (newListener === noChange) { + return + } + const oldListener = this._$committedValue + const shouldRemoveListener = + (newListener === nothing && oldListener !== nothing) || + newListener.capture !== oldListener.capture || + newListener.once !== oldListener.once || + newListener.passive !== oldListener.passive + const shouldAddListener = + newListener !== nothing && + (oldListener === nothing || shouldRemoveListener) + debugLogEvent2 === null || debugLogEvent2 === void 0 + ? void 0 + : debugLogEvent2({ + kind: 'commit event listener', + element: this.element, + name: this.name, + value: newListener, + options: this.options, + removeListener: shouldRemoveListener, + addListener: shouldAddListener, + oldListener + }) + if (shouldRemoveListener) { + this.element.removeEventListener(this.name, this, oldListener) + } + if (shouldAddListener) { + this.element.addEventListener(this.name, this, newListener) + } + this._$committedValue = newListener + } + handleEvent(event) { + var _a4, _b4 + if (typeof this._$committedValue === 'function') { + this._$committedValue.call( + (_b4 = + (_a4 = this.options) === null || _a4 === void 0 + ? void 0 + : _a4.host) !== null && _b4 !== void 0 + ? _b4 + : this.element, + event + ) + } else { + this._$committedValue.handleEvent(event) + } + } +} +class ElementPart { + constructor(element, parent, options) { + this.element = element + this.type = ELEMENT_PART + this._$disconnectableChildren = void 0 + this._$parent = parent + this.options = options + } + get _$isConnected() { + return this._$parent._$isConnected + } + _$setValue(value) { + debugLogEvent2 === null || debugLogEvent2 === void 0 + ? void 0 + : debugLogEvent2({ + kind: 'commit to element binding', + element: this.element, + value, + options: this.options + }) + resolveDirective(this, value) } - return output } -function getTemplate(html) { - let template = document.createElement('template') - template.innerHTML = html - return template.content.cloneNode(true) -} +export function render(value, container, options) { + var _a4, _b4 -export function renderRoot(root, html) { - root.appendChild(getTemplate(html)) + const renderId = 0 + const partOwnerNode = + (_a4 = + options === null || options === void 0 + ? void 0 + : options.renderBefore) !== null && _a4 !== void 0 + ? _a4 + : container + let part = partOwnerNode['_$litPart$'] + + if (part === void 0) { + const endNode = + (_b4 = + options === null || options === void 0 + ? void 0 + : options.renderBefore) !== null && _b4 !== void 0 + ? _b4 + : null + partOwnerNode['_$litPart$'] = part = new ChildPart( + container.insertBefore(createMarker(), endNode), + endNode, + void 0, + options !== null && options !== void 0 ? options : {} + ) + } + part._$setValue(value) + + return part +} +if (ENABLE_EXTRA_SECURITY_HOOKS) { + render.setSanitizer = setSanitizer + render.createSanitizer = createSanitizer } diff --git a/src/index.js b/src/index.js index 6f321cd..7b615b8 100644 --- a/src/index.js +++ b/src/index.js @@ -1,94 +1,381 @@ /** - * {全新的组件核心库} + * {wcui的核心库, 基于lit-core二次开发} * @author yutent - * @date 2023/03/03 11:21:44 + * @date 2023/03/07 18:10:43 */ - -import { finalize } from './constants.js' import { css, adoptStyles } from './css.js' -import { html, renderRoot } from './html.js' +import { render, html, svg } from './html.js' +export { html, css, svg } -export { css, html } - -export class Component extends HTMLElement { - // 声明要监听的属性 - static get observedAttributes() { - this[finalize]() - - let list = [] - this.elemProps.forEach((it, prop) => { - list.push(prop) - }) - return list - } - - constructor() { - super() - this.created && this.created() - } - - /** - * 预处理rpops, styles等 - */ - static [finalize]() { - if (this.finalized) { - return +var defaultConverter = { + toAttribute(value, type) { + switch (type) { + case Boolean: + value = value ? '' : null + break + case Object: + case Array: + value = value == null ? value : JSON.stringify(value) + break } - - this.finalized = true - - this.elemProps = new Map() - - const t = Object.getPrototypeOf(this) - - console.log('>>>', t) - console.log('>>>', this) - - for (let k in this.props) { - let prop = Symbol(k) - let options = this.props[k] - this.elemProps.set(k, prop) - Object.defineProperty(this.prototype, k, { - get() { - return this[prop] || options.default - }, - set(val) { - this[prop] = val - }, - enumerable: false - }) + return value + }, + fromAttribute(value, type) { + let fromValue = value + switch (type) { + case Boolean: + fromValue = value !== null + break + case Number: + fromValue = value === null ? null : Number(value) + break + case Object: + case Array: + try { + fromValue = JSON.parse(value) + } catch (e) { + fromValue = null + } + break } - } - - render() { - return '' - } - - // 组件节点被插入文档时触发的回调 - connectedCallback() { - Object.defineProperty(this, 'root', { - value: this.shadowRoot || this.attachShadow({ mode: 'open' }), - enumerable: false - }) - - adoptStyles(this.root, this.constructor.styles) - - renderRoot(this.root, this.render()) - - this.mounted && this.mounted() - } - - disconnectedCallback() { - console.log('>>>>') - } - - attributeChangedCallback(name, old, val) { - if (old === val) { - return - } - } - - adoptedCallback() { - // + return fromValue } } +var notEqual = (value, old) => { + return old !== value && (old === old || value === value) +} +var defaultPropertyDeclaration = { + attribute: true, + type: String, + converter: defaultConverter, + reflect: false, + hasChanged: notEqual +} +var finalized = 'finalized' + +export class Component extends HTMLElement { + constructor() { + super() + + this.__instanceProperties = new Map() + this.isUpdatePending = false + this.hasUpdated = false + this.__reflectingProperty = null + this._initialize() + } + static addInitializer(initializer) { + var _a4 + this.finalize() + ;((_a4 = this._initializers) !== null && _a4 !== void 0 + ? _a4 + : (this._initializers = []) + ).push(initializer) + } + static get observedAttributes() { + this.finalize() + const attributes = [] + this.elementProperties.forEach((v, p) => { + const attr = this.__attributeNameForProperty(p, v) + if (attr !== void 0) { + this.__attributeToPropertyMap.set(attr, p) + attributes.push(attr) + } + }) + return attributes + } + static createProperty(name, options = defaultPropertyDeclaration) { + if (options.state) { + options.attribute = false + } + this.finalize() + this.elementProperties.set(name, options) + + let key = Symbol(name) + let descriptor = this.getPropertyDescriptor(name, key, options) + + this.prototype[key] = options.default + Object.defineProperty(this.prototype, name, descriptor) + } + static getPropertyDescriptor(name, key, options) { + return { + get() { + return this[key] + }, + set(value) { + const oldValue = this[name] + this[key] = value + this.requestUpdate(name, oldValue, options) + }, + configurable: true + } + } + static getPropertyOptions(name) { + return this.elementProperties.get(name) || defaultPropertyDeclaration + } + static finalize() { + if (this.hasOwnProperty(finalized)) { + return false + } + this[finalized] = true + + this.elementProperties = new Map() + this.__attributeToPropertyMap = new Map() + if (this.hasOwnProperty('props')) { + for (let k in this.props) { + this.createProperty(k, this.props[k]) + } + } + + return true + } + + static __attributeNameForProperty(name, options) { + const attribute = options.attribute + return attribute === false + ? void 0 + : typeof attribute === 'string' + ? attribute + : typeof name === 'string' + ? name.toLowerCase() + : void 0 + } + _initialize() { + var _a4 + this.__updatePromise = new Promise(res => (this.enableUpdating = res)) + this._$changedProperties = new Map() + this.__saveInstanceProperties() + this.requestUpdate() + ;(_a4 = this.constructor._initializers) === null || _a4 === void 0 + ? void 0 + : _a4.forEach(i => i(this)) + } + addController(controller) { + var _a4, _b4 + ;((_a4 = this.__controllers) !== null && _a4 !== void 0 + ? _a4 + : (this.__controllers = []) + ).push(controller) + if (this.root !== void 0 && this.isConnected) { + ;(_b4 = controller.hostConnected) === null || _b4 === void 0 + ? void 0 + : _b4.call(controller) + } + } + removeController(controller) { + var _a4 + ;(_a4 = this.__controllers) === null || _a4 === void 0 + ? void 0 + : _a4.splice(this.__controllers.indexOf(controller) >>> 0, 1) + } + __saveInstanceProperties() { + this.constructor.elementProperties.forEach((_v, p) => { + if (this.hasOwnProperty(p)) { + this.__instanceProperties.set(p, this[p]) + delete this[p] + } + }) + } + createRenderRoot() { + let root = this.shadowRoot || this.attachShadow({ mode: 'open' }) + adoptStyles(root, this.constructor.styles) + return root + } + connectedCallback() { + var _a4 + if (this.root === void 0) { + this.root = this.createRenderRoot() + } + + this.enableUpdating(true) + ;(_a4 = this.__controllers) === null || _a4 === void 0 + ? void 0 + : _a4.forEach(c => { + var _a5 + return (_a5 = c.hostConnected) === null || _a5 === void 0 + ? void 0 + : _a5.call(c) + }) + this.__childPart && this.__childPart.setConnected(true) + } + enableUpdating(_requestedUpdate) {} + disconnectedCallback() { + var _a4 + ;(_a4 = this.__controllers) === null || _a4 === void 0 + ? void 0 + : _a4.forEach(c => { + var _a5 + return (_a5 = c.hostDisconnected) === null || _a5 === void 0 + ? void 0 + : _a5.call(c) + }) + this.__childPart && this.__childPart.setConnected(false) + } + attributeChangedCallback(name, _old, value) { + this._$attributeToProperty(name, value) + } + __propertyToAttribute(name, value, options = defaultPropertyDeclaration) { + var _a4 + const attr = this.constructor.__attributeNameForProperty(name, options) + if (attr !== void 0 && options.reflect === true) { + const converter = + ((_a4 = options.converter) === null || _a4 === void 0 + ? void 0 + : _a4.toAttribute) !== void 0 + ? options.converter + : defaultConverter + const attrValue = converter.toAttribute(value, options.type) + + this.__reflectingProperty = name + if (attrValue == null) { + this.removeAttribute(attr) + } else { + this.setAttribute(attr, attrValue) + } + this.__reflectingProperty = null + } + } + _$attributeToProperty(name, value) { + var _a4 + const ctor = this.constructor + const propName = ctor.__attributeToPropertyMap.get(name) + if (propName !== void 0 && this.__reflectingProperty !== propName) { + const options = ctor.getPropertyOptions(propName) + const converter = + typeof options.converter === 'function' + ? { fromAttribute: options.converter } + : ((_a4 = options.converter) === null || _a4 === void 0 + ? void 0 + : _a4.fromAttribute) !== void 0 + ? options.converter + : defaultConverter + this.__reflectingProperty = propName + this[propName] = converter.fromAttribute(value, options.type) + this.__reflectingProperty = null + } + } + requestUpdate(name, oldValue, options) { + let shouldRequestUpdate = true + if (name !== void 0) { + options = options || this.constructor.getPropertyOptions(name) + const hasChanged = options.hasChanged || notEqual + if (hasChanged(this[name], oldValue)) { + if (!this._$changedProperties.has(name)) { + this._$changedProperties.set(name, oldValue) + } + if (options.reflect === true && this.__reflectingProperty !== name) { + if (this.__reflectingProperties === void 0) { + this.__reflectingProperties = new Map() + } + this.__reflectingProperties.set(name, options) + } + } else { + shouldRequestUpdate = false + } + } + if (!this.isUpdatePending && shouldRequestUpdate) { + this.__updatePromise = this.__enqueueUpdate() + } + } + async __enqueueUpdate() { + this.isUpdatePending = true + try { + await this.__updatePromise + } catch (e) { + Promise.reject(e) + } + const result = this.scheduleUpdate() + if (result != null) { + await result + } + return !this.isUpdatePending + } + scheduleUpdate() { + return this.performUpdate() + } + performUpdate() { + var _a4, _b4 + if (!this.isUpdatePending) { + return + } + + if (this.__instanceProperties) { + this.__instanceProperties.forEach((v, p) => (this[p] = v)) + this.__instanceProperties = void 0 + } + let shouldUpdate = false + const changedProperties = this._$changedProperties + try { + shouldUpdate = this.shouldUpdate(changedProperties) + if (shouldUpdate) { + this.willUpdate(changedProperties) + ;(_b4 = this.__controllers) === null || _b4 === void 0 + ? void 0 + : _b4.forEach(c => { + var _a5 + return (_a5 = c.hostUpdate) === null || _a5 === void 0 + ? void 0 + : _a5.call(c) + }) + this.update(changedProperties) + } else { + this.__markUpdated() + } + } catch (e) { + shouldUpdate = false + this.__markUpdated() + throw e + } + if (shouldUpdate) { + this._$didUpdate(changedProperties) + } + } + willUpdate(_changedProperties) {} + _$didUpdate(changedProperties) { + var _a4 + ;(_a4 = this.__controllers) === null || _a4 === void 0 + ? void 0 + : _a4.forEach(c => { + var _a5 + return (_a5 = c.hostUpdated) === null || _a5 === void 0 + ? void 0 + : _a5.call(c) + }) + if (!this.hasUpdated) { + this.hasUpdated = true + this.firstUpdated(changedProperties) + } + this.updated(changedProperties) + } + __markUpdated() { + this._$changedProperties = new Map() + this.isUpdatePending = false + } + get updateComplete() { + return this.getUpdateComplete() + } + getUpdateComplete() { + return this.__updatePromise + } + shouldUpdate(_changedProperties) { + return true + } + update(_changedProperties) { + const value = this.render() + + /* */ + if (this.__reflectingProperties !== void 0) { + this.__reflectingProperties.forEach((v, k) => + this.__propertyToAttribute(k, this[k], v) + ) + this.__reflectingProperties = void 0 + } + this.__markUpdated() + + this.__childPart = render(value, this.root, { + host: this, + isConnected: !this.hasUpdated && this.isConnected + }) + } + updated(_changedProperties) {} + firstUpdated(_changedProperties) {} +}