UNPKG

@tarojs/plugin-html

Version:

Taro 小程序端支持使用 HTML 标签的插件

420 lines (416 loc) 15.4 kB
import { isHasExtractProp } from '@tarojs/runtime'; import { isString, isFunction, toCamelCase, hooks, warn } from '@tarojs/shared'; function genAttrMapFnFromDir(dir) { const fn = function (key, value) { const lowerKey = key.toLowerCase(); if (lowerKey in dir) { const res = dir[lowerKey]; if (isString(res)) { key = res; } else { key = res[0]; value = res[1][value] || value; } } return [key, value]; }; return fn; } const inlineElements = new Set([]); const blockElements = new Set([]); const specialElements = new Map([ ['slot', 'slot'], ['form', 'form'], ['iframe', 'web-view'], ['img', 'image'], ['audio', 'audio'], ['video', 'video'], ['canvas', 'canvas'], ['a', { mapName(props) { if (props.as && isString(props.as)) return props.as.toLowerCase(); return !props.href || (/^javascript/.test(props.href)) ? 'view' : 'navigator'; }, mapNameCondition: ['href'], mapAttr: genAttrMapFnFromDir({ href: 'url', target: ['openType', { _blank: 'navigate', _self: 'redirect' }] }) }], ['input', { mapName(props) { if (props.type === 'checkbox') { return 'checkbox'; } else if (props.type === 'radio') { return 'radio'; } return 'input'; }, mapNameCondition: ['type'], mapAttr(key, value, props) { const htmlKey = key.toLowerCase(); if (htmlKey === 'autofocus') { key = 'focus'; } else if (htmlKey === 'readonly') { if (props.disabled === true) { value = true; } key = 'disabled'; } else if (htmlKey === 'type') { if (value === 'password') { key = 'password'; value = true; } else if (value === 'tel') { value = 'number'; } } else if (htmlKey === 'maxlength') { key = 'maxlength'; } return [key, value]; } }], ['label', { mapName: 'label', mapAttr: genAttrMapFnFromDir({ htmlfor: 'for' }) }], ['textarea', { mapName: 'textarea', mapAttr: genAttrMapFnFromDir({ autofocus: 'focus', readonly: 'disabled', maxlength: 'maxlength' }) }], ['progress', { mapName: 'progress', mapAttr(key, value, props) { if (key === 'value') { const max = props.max || 1; key = 'percent'; value = Math.round(value / max * 100); } return [key, value]; } }], ['button', { mapName: 'button', mapAttr(key, value) { if (key === 'type' && (value === 'submit' || value === 'reset')) { key = 'formType'; } return [key, value]; } }] ]); function isHtmlTags(nodeName) { if (inlineElements.has(nodeName) || blockElements.has(nodeName) || specialElements.has(nodeName)) { return true; } return false; } function getMappedType(nodeName, rawProps, node) { if (inlineElements.has(nodeName)) { return 'text'; } else if (specialElements.has(nodeName)) { const mapping = specialElements.get(nodeName); if (isString(mapping)) { return mapping; } const { mapName } = mapping; return isFunction(mapName) ? mapName(rawProps) : mapName; } else { // fix #15326 if (process.env.TARO_ENV === 'swan') return 'view'; if (node) { const { props } = node; for (const prop in props) { const propInCamelCase = toCamelCase(prop); if (propInCamelCase === 'catchMove' && props[prop] !== false) { return 'catch-view'; } } } if (!node) { return 'view'; } if (node.isOnlyClickBinded() && !isHasExtractProp(node)) { return 'click-view'; } else if (node.isAnyEventBinded()) { return 'view'; } else if (isHasExtractProp(node)) { return 'static-view'; } else { return 'pure-view'; } } } function getAttrMapFn(nodeName) { const mapping = specialElements.get(nodeName); if (!isString(mapping)) { return mapping === null || mapping === void 0 ? void 0 : mapping.mapAttr; } } function getMapNameByCondition(nodeName, attr, props) { const mapping = specialElements.get(nodeName); if (!mapping || isString(mapping)) return; const { mapName, mapNameCondition } = mapping; if (!mapNameCondition) return; if (mapNameCondition.indexOf(attr) > -1 && !isString(mapName)) { return mapName(props); } } function mapNameByContion(nodeName, key, element, componentsAlias) { const mapName = getMapNameByCondition(nodeName, key, element.props); if (mapName) { const mapNameAlias = componentsAlias[mapName]._num; element.enqueueUpdate({ path: `${element._path}.${"nn" /* Shortcuts.NodeName */}`, value: mapNameAlias }); } } function ensureHtmlClass(tagName, className = '') { const classList = className.split(' '); const htmlClass = `h5-${tagName}`; if (classList.indexOf(htmlClass) === -1) { classList.unshift(htmlClass); } return classList.join(' '); } function ensureRect(props, style = '') { let cssText = style; const { width, height } = props; if (width) { cssText = `width: ${width};${cssText}`; } if (height) { cssText = `height: ${height};${cssText}`; } return cssText; } function defineMappedProp(obj, propName, mapName) { Object.defineProperty(obj, propName, { enumerable: true, configurable: true, get() { return obj[mapName]; }, set(val) { obj[mapName] = val; } }); } hooks.tap('modifyHydrateData', (data, node) => { const nodeName = data["nn" /* Shortcuts.NodeName */]; if (!isHtmlTags(nodeName)) return; process.env.NODE_ENV !== 'production' && warn(data["nn" /* Shortcuts.NodeName */] === 'select', '请使用 Picker 组件代替 <select>'); // map nodeName data["nn" /* Shortcuts.NodeName */] = getMappedType(nodeName, data, node); // map attr Key/Value const attrMapFn = getAttrMapFn(nodeName); if (attrMapFn) { for (const key in data) { const value = data[key]; const [mapKey, mapValue] = attrMapFn(key, value, data); if (key !== mapKey) { delete data[key]; data[mapKey] = mapValue; } else if (value !== mapValue) { data[key] = mapValue; } } } if (nodeName === 'br') { data["cn" /* Shortcuts.Childnodes */] = [{ ["nn" /* Shortcuts.NodeName */]: '#text', v: '\n' }]; } data["cl" /* Shortcuts.Class */] = ensureHtmlClass(nodeName, data["cl" /* Shortcuts.Class */]); data["st" /* Shortcuts.Style */] = ensureRect(data, data["st" /* Shortcuts.Style */]); }); hooks.tap('modifySetAttrPayload', (element, key, payload, componentsAlias) => { const { nodeName, _path, props } = element; if (!isHtmlTags(nodeName)) return; // map nodeName mapNameByContion(nodeName, key, element, componentsAlias); const mapName = getMappedType(nodeName, props); const alias = componentsAlias[mapName]; // map attr Key/Value const attrMapFn = getAttrMapFn(nodeName); if (attrMapFn) { const value = payload.value; const [mapKey, mapValue] = attrMapFn(key, value, props); payload.path = `${_path}.${alias[mapKey] || mapKey}`; payload.value = mapValue; } else if (alias[key] && alias[key] !== key) { payload.path = `${_path}.${toCamelCase(alias[key])}`; } if (key === "cl" /* Shortcuts.Class */) { payload.value = ensureHtmlClass(nodeName, payload.value); } else if (key === "st" /* Shortcuts.Style */ || key === 'width' || key === 'height') { payload.path = `${_path}.${"st" /* Shortcuts.Style */}`; payload.value = ensureRect(props, element.style.cssText); } if (blockElements.has(element.nodeName) && process.env.TARO_ENV !== 'swan') { const viewAlias = componentsAlias.view._num; const staticViewAlias = componentsAlias['static-view']._num; const catchViewAlias = componentsAlias['catch-view']._num; const clickViewAlias = componentsAlias['click-view']._num; const qualifiedNameInCamelCase = toCamelCase(key); const dataPath = `${_path}.${"nn" /* Shortcuts.NodeName */}`; if (qualifiedNameInCamelCase === 'catchMove') { // catchMove = true: catch-view // catchMove = false: view or click-view or static-view element.enqueueUpdate({ path: dataPath, value: payload.value ? catchViewAlias : (element.isOnlyClickBinded() && !isHasExtractProp(element) ? clickViewAlias : (element.isAnyEventBinded() ? viewAlias : staticViewAlias)) }); } else if (isHasExtractProp(element) && !element.isAnyEventBinded()) { // pure-view => static-view // static-view => static-view 因为没有办法分辨之前是不是 pure,所以就算之前是 static 也需要 setData element.enqueueUpdate({ path: dataPath, value: staticViewAlias }); } } }); hooks.tap('modifyRmAttrPayload', (element, key, payload, componentsAlias) => { const { nodeName, _path, props } = element; if (!isHtmlTags(nodeName)) return; // map nodeName mapNameByContion(nodeName, key, element, componentsAlias); const mapName = getMappedType(nodeName, props); const alias = componentsAlias[mapName]; // map attr Key/Value const attrMapFn = getAttrMapFn(nodeName); if (attrMapFn) { const value = payload[key]; const [mapKey] = attrMapFn(key, value, props); payload.path = `${_path}.${alias[mapKey] || mapKey}`; } else if (alias[key] && alias[key] !== key) { payload.path = `${_path}.${toCamelCase(alias[key])}`; } if (key === "cl" /* Shortcuts.Class */) { payload.value = ensureHtmlClass(nodeName, payload.value); } else if (key === "st" /* Shortcuts.Style */ || key === 'width' || key === 'height') { payload.path = `${_path}.${"st" /* Shortcuts.Style */}`; payload.value = ensureRect(props, element.style.cssText); } if (blockElements.has(element.nodeName) && process.env.TARO_ENV !== 'swan') { const viewAlias = componentsAlias.view._num; const staticViewAlias = componentsAlias['static-view']._num; const pureViewAlias = componentsAlias['pure-view']._num; const clickViewAlias = componentsAlias['click-view']._num; const qualifiedNameInCamelCase = toCamelCase(key); const dataPath = `${_path}.${"nn" /* Shortcuts.NodeName */}`; if (qualifiedNameInCamelCase === 'catchMove') { // catch-view => view or click-view or static-view or pure-view element.enqueueUpdate({ path: dataPath, value: element.isOnlyClickBinded() && !isHasExtractProp(element) ? clickViewAlias : (element.isAnyEventBinded() ? viewAlias : (isHasExtractProp(element) ? staticViewAlias : pureViewAlias)) }); } else if (!isHasExtractProp(element)) { // static-view => pure-view // pure-view => pure-view 因为没有办法分辨之前是不是 pure,所以就算之前是 pure 也需要 setData element.enqueueUpdate({ path: dataPath, value: pureViewAlias }); } } }); hooks.tap('onAddEvent', (type, _handler, _options, node) => { node = node; if (!isHtmlTags(node.nodeName)) return; if (type === 'click') { defineMappedProp(node.__handlers, type, 'tap'); } else if (node.nodeName === 'input') { if (type === 'change') { if (node.props.type === 'checkbox' || node.props.type === 'radio') { defineMappedProp(node.__handlers, type, 'tap'); } else { defineMappedProp(node.__handlers, type, 'input'); } } else if (type === 'keypress') { defineMappedProp(node.__handlers, type, 'confirm'); } } }); hooks.tap('modifyTaroEvent', (event, element) => { const { nodeName, props } = element; if (nodeName === 'input' && event.type === 'tap') { if (props.type === 'checkbox') { props.checked = !props.checked; } else if (props.type === 'radio' && !props.checked) { props.checked = true; } if (event.mpEvent) { const { currentTarget, target } = event.mpEvent; currentTarget.checked = props.checked; if (target.id === currentTarget.id) { target.checked = props.checked; } } } }); hooks.tap('modifyAddEventListener', (element, sideEffect, getComponentsAlias) => { // 如果是从没有事件绑定到有事件绑定,且是 block 元素,则转换为 view if (blockElements.has(element.nodeName) && sideEffect !== false && !element.isAnyEventBinded()) { const componentsAlias = getComponentsAlias(); const alias = componentsAlias.view._num; element.enqueueUpdate({ path: `${element._path}.${"nn" /* Shortcuts.NodeName */}`, value: alias }); } }); hooks.tap('modifyRemoveEventListener', (element, sideEffect, getComponentsAlias) => { // 如果已没有绑定事件,且是 block 元素,则转换为 static-view 或 pure-view if (process.env.TARO_ENV !== 'swan' && blockElements.has(element.nodeName) && sideEffect !== false && !element.isAnyEventBinded()) { const componentsAlias = getComponentsAlias(); const value = isHasExtractProp(element) ? 'static-view' : 'pure-view'; const valueAlias = componentsAlias[value]._num; element.enqueueUpdate({ path: `${element._path}.${"nn" /* Shortcuts.NodeName */}`, value: valueAlias }); } }); //# sourceMappingURL=runtime.js.map