diff --git a/package.json b/package.json index 6962b20..70015d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wkit", - "version": "1.11.2", + "version": "1.11.3", "type": "module", "description": "A library for building fast, lightweight web components.", "main": "dist/index.js", diff --git a/src/html.js b/src/html.js index b8c3eb9..86a21ab 100644 --- a/src/html.js +++ b/src/html.js @@ -6,31 +6,28 @@ import { boolMap, WC_PART, NOTHING } from './constants.js' import { animate, MODES } from './anim.js' -import { nextTick } from './utils.js' +import { nextTick, noop } from './utils.js' + +const BIND_ATTR_SUFFIX = '{{$wkit$}}' +const MARKER = `{{^wkit${String(Math.random()).slice(-8)}^}}` +const MARKER_MATCH = '?' + MARKER +const NODE_MARKER = `<${MARKER_MATCH}>` -const boundAttributeSuffix = '$wc$' -const marker = `wc$${String(Math.random()).slice(9)}$` -const markerMatch = '?' + marker -const nodeMarker = `<${markerMatch}>` -const createMarker = (v = '') => document.createComment(v) const isPrimitive = value => value === null || (typeof value != 'object' && typeof value != 'function') const isArray = Array.isArray const isIterable = value => - isArray(value) || - typeof (value === null || value === void 0 - ? false - : value[Symbol.iterator]) === 'function' + value ? isArray(value) || typeof value[Symbol.iterator] === 'function' : false const SPACE_CHAR = `[ \n\f\r]` const ATTR_VALUE_CHAR = `[^ \n\f\r"'\`<>=]` const NAME_CHAR = `[^\\s"'>=/]` -const textEndRegex = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g +const TEXT_END_REGEX = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g const COMMENT_START = 1 const TAG_NAME = 2 const DYNAMIC_TAG_NAME = 3 -const commentEndRegex = /-->/g -const comment2EndRegex = />/g -const tagEndRegex = new RegExp( +const COMMENT_END_REGEXP = /-->/g +const COMMENT_END_REGEXP_2 = />/g +const TAG_END_REGEXP = new RegExp( `>|${SPACE_CHAR}(?:(${NAME_CHAR}+)(${SPACE_CHAR}*=${SPACE_CHAR}*(?:${ATTR_VALUE_CHAR}|("|')|))|$)`, 'g' ) @@ -38,9 +35,9 @@ const ENTIRE_MATCH = 0 const ATTRIBUTE_NAME = 1 const SPACES_AND_EQUALS = 2 const QUOTE_CHAR = 3 -const singleQuoteAttrEndRegex = /'/g -const doubleQuoteAttrEndRegex = /"/g -const rawTextElement = /^(?:script|style|textarea|title)$/i +const SINGLE_QUOTE_REGEXP = /'/g +const DOUBLE_QUOTE_REGEXP = /"/g +const RAW_TEXT_ELEM_REGEXP = /^(?:script|style|textarea|title)$/i const HTML_RESULT = 1 const SVG_RESULT = 2 const ATTRIBUTE_PART = 1 @@ -52,14 +49,12 @@ const COMMENT_PART = 7 const TEMPLATE_CACHE = new Map() const walker = document.createTreeWalker(document, 129, null, false) -function noop() {} - function getTemplateHtml(strings, type) { let len = strings.length - 1 let attrNames = [] let html2 = type === SVG_RESULT ? '' : '' let rawTextEndRegex - let regex = textEndRegex + let regex = TEXT_END_REGEX for (let i = 0; i < len; i++) { let s = strings[i] let attrNameEndIndex = -1 @@ -73,25 +68,26 @@ function getTemplateHtml(strings, type) { break } lastIndex = regex.lastIndex - if (regex === textEndRegex) { + + if (regex === TEXT_END_REGEX) { if (match[COMMENT_START] === '!--') { - regex = commentEndRegex + regex = COMMENT_END_REGEXP } else if (match[COMMENT_START] !== void 0) { - regex = comment2EndRegex + regex = COMMENT_END_REGEXP_2 } else if (match[TAG_NAME] !== void 0) { - if (rawTextElement.test(match[TAG_NAME])) { + if (RAW_TEXT_ELEM_REGEXP.test(match[TAG_NAME])) { rawTextEndRegex = new RegExp(`') { regex = rawTextEndRegex !== null && rawTextEndRegex !== void 0 ? rawTextEndRegex - : textEndRegex + : TEXT_END_REGEX attrNameEndIndex = -1 } else if (match[ATTRIBUTE_NAME] === void 0) { attrNameEndIndex = -2 @@ -100,52 +96,65 @@ function getTemplateHtml(strings, type) { attrName = match[ATTRIBUTE_NAME] regex = match[QUOTE_CHAR] === void 0 - ? tagEndRegex + ? TAG_END_REGEXP : match[QUOTE_CHAR] === '"' - ? doubleQuoteAttrEndRegex - : singleQuoteAttrEndRegex + ? DOUBLE_QUOTE_REGEXP + : SINGLE_QUOTE_REGEXP } } else if ( - regex === doubleQuoteAttrEndRegex || - regex === singleQuoteAttrEndRegex + regex === DOUBLE_QUOTE_REGEXP || + regex === SINGLE_QUOTE_REGEXP ) { - regex = tagEndRegex - } else if (regex === commentEndRegex || regex === comment2EndRegex) { - regex = textEndRegex + regex = TAG_END_REGEXP + } else if ( + regex === COMMENT_END_REGEXP || + regex === COMMENT_END_REGEXP_2 + ) { + regex = TEXT_END_REGEX } else { - regex = tagEndRegex + regex = TAG_END_REGEXP rawTextEndRegex = void 0 } } let end = - regex === tagEndRegex && strings[i + 1].startsWith('/>') ? ' ' : '' + regex === TAG_END_REGEXP && strings[i + 1].startsWith('/>') ? ' ' : '' html2 += - regex === textEndRegex - ? s + nodeMarker + regex === TEXT_END_REGEX + ? s + NODE_MARKER : attrNameEndIndex >= 0 ? (attrNames.push(attrName), s.slice(0, attrNameEndIndex) + - boundAttributeSuffix + + BIND_ATTR_SUFFIX + s.slice(attrNameEndIndex)) + - marker + + MARKER + end : s + - marker + + MARKER + (attrNameEndIndex === -2 ? (attrNames.push(void 0), i) : end) } let htmlResult = html2 + (strings[len] || '') + (type === SVG_RESULT ? '' : '') - if (!Array.isArray(strings) || !strings.hasOwnProperty('raw')) { + if (!isArray(strings) || !strings.hasOwnProperty('raw')) { throw new Error('invalid html ast') } return [htmlResult, attrNames] } +function createElement(v = '') { + let el = document.createElement('template') + el.innerHTML = v + return el +} + +function createMarker(v = '') { + return document.createComment(v) +} + class Template { parts = [] - constructor({ strings, ['__dom_type__']: type }, options) { + constructor({ strings, values, __dom_type__: type }, options) { let node let nodeIndex = 0 let attrNameIndex = 0 @@ -153,7 +162,7 @@ class Template { let parts = this.parts let [html2, attrNames] = getTemplateHtml(strings, type) - this.el = Template.createElement(html2) + this.el = createElement(html2) walker.currentNode = this.el.content @@ -170,17 +179,14 @@ class Template { let attrsToRemove = [] for (let name of node.getAttributeNames()) { - if ( - name.endsWith(boundAttributeSuffix) || - name.startsWith(marker) - ) { + if (name.endsWith(BIND_ATTR_SUFFIX) || name.startsWith(MARKER)) { let realName = attrNames[attrNameIndex++] attrsToRemove.push(name) if (realName !== void 0) { let value = node.getAttribute( - realName.toLowerCase() + boundAttributeSuffix + realName.toLowerCase() + BIND_ATTR_SUFFIX ) - let statics = value.split(marker) + let statics = value.split(MARKER) let m = /([#:@])?(.*)/.exec(realName) let decorates = [] @@ -216,8 +222,8 @@ class Template { node.removeAttribute(name) } } - if (rawTextElement.test(node.tagName)) { - let strings2 = node.textContent.split(marker) + if (RAW_TEXT_ELEM_REGEXP.test(node.tagName)) { + let strings2 = node.textContent.split(MARKER) let lastIndex = strings2.length - 1 if (lastIndex > 0) { node.textContent = '' @@ -231,24 +237,19 @@ class Template { } } else if (node.nodeType === 8) { let data = node.data - if (data === markerMatch) { + if (data === MARKER_MATCH) { parts.push({ type: CHILD_PART, index: nodeIndex }) } else { let i = -1 - while ((i = node.data.indexOf(marker, i + 1)) !== -1) { + while ((i = node.data.indexOf(MARKER, i + 1)) !== -1) { parts.push({ type: COMMENT_PART, index: nodeIndex }) - i += marker.length - 1 + i += MARKER.length - 1 } } } nodeIndex++ } } - static createElement(html2) { - let el = document.createElement('template') - el.innerHTML = html2 - return el - } } class TemplateInstance { @@ -378,7 +379,7 @@ class ChildPart { } else if (value !== this.#value) { this.#commitText(value) } - } else if (value['__dom_type__'] !== void 0) { + } else if (value.__dom_type__ !== void 0) { this.#commitTemplateResult(value) } else if (value.nodeType !== void 0) { this.#commitNode(value) @@ -415,12 +416,11 @@ class ChildPart { } #commitTemplateResult(result) { - let { values, ['__dom_type__']: type } = result + let { values, __dom_type__: type } = result let template = typeof type === 'number' ? this.#getTemplate(result) - : (type.el === void 0 && (type.el = Template.createElement(type.h)), - type) + : (type.el === void 0 && (type.el = createElement(type.h)), type) if (this.#value?.$template === template) { this.#value.update(values) diff --git a/src/utils.js b/src/utils.js index 17b507b..c751ef2 100644 --- a/src/utils.js +++ b/src/utils.js @@ -4,7 +4,7 @@ * @date 2023/03/07 22:11:30 */ -function noop() {} +export function noop() {} export function $(selector, container, multi) { let fn = multi ? 'querySelectorAll' : 'querySelector'