UNPKG

@hippy/vue-next

Version:

Vue-Next binding for Hippy native framework

1,687 lines (1,682 loc) 294 kB
/*! * @hippy/vue-next v3.3.2 * (Using Vue v3.4.32 and Hippy-Vue-Next v3.3.2) * Build at: Fri Nov 08 2024 20:45:13 GMT+0800 (中国标准时间) * * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2023-2024 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { camelize, capitalize, looseEqual, isFunction, isString, isOn } from '@vue/shared'; import { nextTick, toRaw, h, callWithAsyncErrorHandling, ErrorCodes, camelize as camelize$1, createRenderer, createHydrationRenderer } from '@vue/runtime-core'; /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const names = { transparent: 0x00000000, aliceblue: 0xf0f8ffff, antiquewhite: 0xfaebd7ff, aqua: 0x00ffffff, aquamarine: 0x7fffd4ff, azure: 0xf0ffffff, beige: 0xf5f5dcff, bisque: 0xffe4c4ff, black: 0x000000ff, blanchedalmond: 0xffebcdff, blue: 0x0000ffff, blueviolet: 0x8a2be2ff, brown: 0xa52a2aff, burlywood: 0xdeb887ff, burntsienna: 0xea7e5dff, cadetblue: 0x5f9ea0ff, chartreuse: 0x7fff00ff, chocolate: 0xd2691eff, coral: 0xff7f50ff, cornflowerblue: 0x6495edff, cornsilk: 0xfff8dcff, crimson: 0xdc143cff, cyan: 0x00ffffff, darkblue: 0x00008bff, darkcyan: 0x008b8bff, darkgoldenrod: 0xb8860bff, darkgray: 0xa9a9a9ff, darkgreen: 0x006400ff, darkgrey: 0xa9a9a9ff, darkkhaki: 0xbdb76bff, darkmagenta: 0x8b008bff, darkolivegreen: 0x556b2fff, darkorange: 0xff8c00ff, darkorchid: 0x9932ccff, darkred: 0x8b0000ff, darksalmon: 0xe9967aff, darkseagreen: 0x8fbc8fff, darkslateblue: 0x483d8bff, darkslategray: 0x2f4f4fff, darkslategrey: 0x2f4f4fff, darkturquoise: 0x00ced1ff, darkviolet: 0x9400d3ff, deeppink: 0xff1493ff, deepskyblue: 0x00bfffff, dimgray: 0x696969ff, dimgrey: 0x696969ff, dodgerblue: 0x1e90ffff, firebrick: 0xb22222ff, floralwhite: 0xfffaf0ff, forestgreen: 0x228b22ff, fuchsia: 0xff00ffff, gainsboro: 0xdcdcdcff, ghostwhite: 0xf8f8ffff, gold: 0xffd700ff, goldenrod: 0xdaa520ff, gray: 0x808080ff, green: 0x008000ff, greenyellow: 0xadff2fff, grey: 0x808080ff, honeydew: 0xf0fff0ff, hotpink: 0xff69b4ff, indianred: 0xcd5c5cff, indigo: 0x4b0082ff, ivory: 0xfffff0ff, khaki: 0xf0e68cff, lavender: 0xe6e6faff, lavenderblush: 0xfff0f5ff, lawngreen: 0x7cfc00ff, lemonchiffon: 0xfffacdff, lightblue: 0xadd8e6ff, lightcoral: 0xf08080ff, lightcyan: 0xe0ffffff, lightgoldenrodyellow: 0xfafad2ff, lightgray: 0xd3d3d3ff, lightgreen: 0x90ee90ff, lightgrey: 0xd3d3d3ff, lightpink: 0xffb6c1ff, lightsalmon: 0xffa07aff, lightseagreen: 0x20b2aaff, lightskyblue: 0x87cefaff, lightslategray: 0x778899ff, lightslategrey: 0x778899ff, lightsteelblue: 0xb0c4deff, lightyellow: 0xffffe0ff, lime: 0x00ff00ff, limegreen: 0x32cd32ff, linen: 0xfaf0e6ff, magenta: 0xff00ffff, maroon: 0x800000ff, mediumaquamarine: 0x66cdaaff, mediumblue: 0x0000cdff, mediumorchid: 0xba55d3ff, mediumpurple: 0x9370dbff, mediumseagreen: 0x3cb371ff, mediumslateblue: 0x7b68eeff, mediumspringgreen: 0x00fa9aff, mediumturquoise: 0x48d1ccff, mediumvioletred: 0xc71585ff, midnightblue: 0x191970ff, mintcream: 0xf5fffaff, mistyrose: 0xffe4e1ff, moccasin: 0xffe4b5ff, navajowhite: 0xffdeadff, navy: 0x000080ff, oldlace: 0xfdf5e6ff, olive: 0x808000ff, olivedrab: 0x6b8e23ff, orange: 0xffa500ff, orangered: 0xff4500ff, orchid: 0xda70d6ff, palegoldenrod: 0xeee8aaff, palegreen: 0x98fb98ff, paleturquoise: 0xafeeeeff, palevioletred: 0xdb7093ff, papayawhip: 0xffefd5ff, peachpuff: 0xffdab9ff, peru: 0xcd853fff, pink: 0xffc0cbff, plum: 0xdda0ddff, powderblue: 0xb0e0e6ff, purple: 0x800080ff, rebeccapurple: 0x663399ff, red: 0xff0000ff, rosybrown: 0xbc8f8fff, royalblue: 0x4169e1ff, saddlebrown: 0x8b4513ff, salmon: 0xfa8072ff, sandybrown: 0xf4a460ff, seagreen: 0x2e8b57ff, seashell: 0xfff5eeff, sienna: 0xa0522dff, silver: 0xc0c0c0ff, skyblue: 0x87ceebff, slateblue: 0x6a5acdff, slategray: 0x708090ff, slategrey: 0x708090ff, snow: 0xfffafaff, springgreen: 0x00ff7fff, steelblue: 0x4682b4ff, tan: 0xd2b48cff, teal: 0x008080ff, thistle: 0xd8bfd8ff, tomato: 0xff6347ff, turquoise: 0x40e0d0ff, violet: 0xee82eeff, wheat: 0xf5deb3ff, white: 0xffffffff, whitesmoke: 0xf5f5f5ff, yellow: 0xffff00ff, yellowgreen: 0x9acd32ff, }; const convertRegExp = (...args) => `\\(\\s*(${args.join(')\\s*,\\s*(')})\\s*\\)`; const NUMBER = '[-+]?\\d*\\.?\\d+'; const PERCENTAGE = `${NUMBER}%`; const matchers = { rgb: new RegExp(`rgb${convertRegExp(NUMBER, NUMBER, NUMBER)}`), rgba: new RegExp(`rgba${convertRegExp(NUMBER, NUMBER, NUMBER, NUMBER)}`), hsl: new RegExp(`hsl${convertRegExp(NUMBER, PERCENTAGE, PERCENTAGE)}`), hsla: new RegExp(`hsla${convertRegExp(NUMBER, PERCENTAGE, PERCENTAGE, NUMBER)}`), hex3: /^#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/, hex4: /^#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/, hex6: /^#([0-9a-fA-F]{6})$/, hex8: /^#([0-9a-fA-F]{8})$/, }; const parse255 = (str) => { const int = parseInt(str, 10); if (int < 0) { return 0; } if (int > 255) { return 255; } return int; }; const parse1 = (str) => { const num = parseFloat(str); if (num < 0) { return 0; } if (num > 1) { return 255; } return Math.round(num * 255); }; const hue2rgb = (p, q, tx) => { let t = tx; if (t < 0) { t += 1; } if (t > 1) { t -= 1; } if (t < 1 / 6) { return p + (q - p) * 6 * t; } if (t < 1 / 2) { return q; } if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6; } return p; }; const hslToRgb = (h, s, l) => { const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; const r = hue2rgb(p, q, h + 1 / 3); const g = hue2rgb(p, q, h); const b = hue2rgb(p, q, h - 1 / 3); return ((Math.round(r * 255) << 24) | (Math.round(g * 255) << 16) | (Math.round(b * 255) << 8)); }; const parse360 = (str) => { const int = parseFloat(str); return (((int % 360) + 360) % 360) / 360; }; const parsePercentage = (str) => { const int = parseFloat(str); if (int < 0) { return 0; } if (int > 100) { return 1; } return int / 100; }; // eslint-disable-next-line complexity function baseColor(color) { let match; if (typeof color === 'number') { if (color >>> 0 === color && color >= 0 && color <= 0xffffffff) { return color; } return null; } match = matchers.hex6.exec(color); if (Array.isArray(match)) { return parseInt(`${match[1]}ff`, 16) >>> 0; } if (Object.hasOwnProperty.call(names, color)) { return names[color]; } match = matchers.rgb.exec(color); if (Array.isArray(match)) { return (((parse255(match[1]) << 24) // r | (parse255(match[2]) << 16) // g | (parse255(match[3]) << 8) // b | 0x000000ff) // a >>> 0); } match = matchers.rgba.exec(color); if (match) { return (((parse255(match[1]) << 24) // r | (parse255(match[2]) << 16) // g | (parse255(match[3]) << 8) // b | parse1(match[4])) // a >>> 0); } match = matchers.hex3.exec(color); if (match) { return (parseInt(`${match[1] + match[1] // r + match[2] + match[2] // g + match[3] + match[3] // b }ff`, // a 16) >>> 0); } match = matchers.hex8.exec(color); if (match) { return parseInt(match[1], 16) >>> 0; } match = matchers.hex4.exec(color); if (match) { return (parseInt(match[1] + match[1] // r + match[2] + match[2] // g + match[3] + match[3] // b + match[4] + match[4], // a 16) >>> 0); } match = matchers.hsl.exec(color); if (match) { return ((hslToRgb(parse360(match[1]), // h parsePercentage(match[2]), // s parsePercentage(match[3])) | 0x000000ff) // a >>> 0); } match = matchers.hsla.exec(color); if (match) { return ((hslToRgb(parse360(match[1]), // h parsePercentage(match[2]), // s parsePercentage(match[3])) | parse1(match[4])) // a >>> 0); } return null; } /** * Convert css color value or string description to native supported color format * * @param color - color value or string description * * @public */ function translateColor(color) { let int32Color = baseColor(color); if (int32Color === null) { throw new Error(`Bad color value: ${color}`); } int32Color = ((int32Color << 24) | (int32Color >>> 8)) >>> 0; return int32Color; } /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * properties map * * @public */ const PROPERTIES_MAP = { textDecoration: 'textDecorationLine', boxShadowOffset: 'shadowOffset', boxShadowOffsetX: 'shadowOffsetX', boxShadowOffsetY: 'shadowOffsetY', boxShadowOpacity: 'shadowOpacity', boxShadowRadius: 'shadowRadius', boxShadowSpread: 'shadowSpread', boxShadowColor: 'shadowColor', }; // linear-gradient direction description map const LINEAR_GRADIENT_DIRECTION_MAP = { totop: '0', totopright: 'totopright', toright: '90', tobottomright: 'tobottomright', tobottom: '180', tobottomleft: 'tobottomleft', toleft: '270', totopleft: 'totopleft', }; // degree unit const DEGREE_UNIT = { TURN: 'turn', RAD: 'rad', DEG: 'deg', }; // regular expression of comment const commentRegexp = /\/\*.{0,1000}?\*\//gms; /** * Output warning debug information to console * * @param context - output content */ function warn$1(...context) { if (process.env.NODE_ENV === 'production') { return; } // eslint-disable-next-line no-console console.warn(...context); } // regular expression of number const numberRegEx$1 = new RegExp('^(?=.+)[+-]?\\d*\\.?\\d*([Ee][+-]?\\d+)?$'); /** * convert to number * @param str - target string or number */ function tryConvertNumber$1(str) { if (typeof str === 'number') { return str; } if (numberRegEx$1.test(str)) { try { return parseFloat(str); } catch (err) { // pass } } return str; } /** * Convert the px unit value to the pt unit value used by the native * * @param value - target px unit value */ function convertPxUnitToPt(value) { // If value is number just ignore if (Number.isInteger(value)) { return value; } // If value unit is px, change to use pt as 1:1. if (typeof value === 'string' && value.endsWith('px')) { const num = parseFloat(value.slice(0, value.indexOf('px'))); if (!Number.isNaN(num)) { value = num; } } return value; } /** * Convert the angle value of a string to an angle value * * @param value - target value * @param unit - unit type * * @public */ function convertToDegree(value, unit = DEGREE_UNIT.DEG) { const convertedNumValue = parseFloat(value); let result = value || ''; const [, decimals] = value.split('.'); if (decimals && decimals.length > 2) { result = convertedNumValue.toFixed(2); } switch (unit) { // turn unit case DEGREE_UNIT.TURN: result = `${(convertedNumValue * 360).toFixed(2)}`; break; // radius unit case DEGREE_UNIT.RAD: result = `${((180 / Math.PI) * convertedNumValue).toFixed(2)}`; break; } return result; } /** * Get the angle value of the linear gradient * * @param value - target value * * @public */ function getLinearGradientAngle(value) { const processedValue = (value || '').replace(/\s*/g, '').toLowerCase(); const reg = /^([+-]?(?=(?<digit>\d+))\k<digit>\.?\d*)+(deg|turn|rad)|(to\w+)$/g; const valueList = reg.exec(processedValue); if (!Array.isArray(valueList)) return ''; // default direction is to bottom, i.e. 180degree let angle = '180'; const [direction, angleValue, angleUnit] = valueList; if (angleValue && angleUnit) { // handling values of type angular angle = convertToDegree(angleValue, angleUnit); } else if (direction && typeof LINEAR_GRADIENT_DIRECTION_MAP[direction] !== 'undefined') { // direct direction angle = LINEAR_GRADIENT_DIRECTION_MAP[direction]; } else { warn$1('linear-gradient direction or angle is invalid, default value [to bottom] would be used'); } return angle; } /** * Get the color value of the linear gradient when it stops * * @param value - color * * @public */ function getLinearGradientColorStop(value = '') { const processedValue = value.replace(/\s+/g, ' ').trim(); const [color, percentage] = processedValue.split(/\s+(?![^(]*?\))/); const percentageCheckReg = /^([+-]?\d+\.?\d*)%$/g; if (color && !percentageCheckReg.exec(color) && !percentage) { return { color: translateColor(color), }; } if (color && percentageCheckReg.exec(percentage)) { return { // color stop ratio ratio: parseFloat(percentage.split('%')[0]) / 100, color: translateColor(color), }; } return null; } /** * parse background image style attribute * * @param property - property name * @param value - property value * * @public */ function parseBackgroundImage(property, value) { let processedValue = value; let processedProperty = property; if (value.indexOf('linear-gradient') === 0) { processedProperty = 'linearGradient'; const valueString = value.substring(value.indexOf('(') + 1, value.lastIndexOf(')')); const tokens = valueString.split(/,(?![^(]*?\))/); const colorStopList = []; processedValue = {}; tokens.forEach((token, index) => { if (index === 0) { // the angle of linear-gradient parameter can be optional const angle = getLinearGradientAngle(token); if (angle) { processedValue.angle = angle; } else { // if angle ignored, default direction is to bottom, i.e. 180degree processedValue.angle = '180'; const colorObject = getLinearGradientColorStop(token); if (colorObject) colorStopList.push(colorObject); } } else { const colorObject = getLinearGradientColorStop(token); if (colorObject) colorStopList.push(colorObject); } }); processedValue.colorStopList = colorStopList; } else { const regexp = /(?:\(['"]?)(.*?)(?:['"]?\))/; const executed = regexp.exec(value); if (executed && executed.length > 1) { [, processedValue] = executed; } } return [processedProperty, processedValue]; } /** * Recursively add non-enumerable parent nodes * * @param obj - target object * @param parent - parent node */ function addParent(obj, parent) { const isNode = obj && typeof obj.type === 'string'; const childParent = isNode ? obj : parent; // Take all the keys of the node, and judge the value, if it is an array and an object, then recursively addParent Object.keys(obj).forEach((k) => { const value = obj[k]; if (Array.isArray(value)) { value.forEach((v) => { addParent(v, childParent); }); } else if (value && typeof value === 'object') { addParent(value, childParent); } }); // If it is a node type, add a parent node, if no parent node is passed in, set it to null if (isNode) { Object.defineProperty(obj, 'parent', { configurable: true, writable: true, enumerable: false, value: parent, }); } return obj; } /** * parse css code into AST tree * * @param css - css code * @param options - parse options * * @public */ function parseCSS(css, options = { source: 0 }) { // position let lineno = 1; let column = 1; /** * update lineno and column */ function updatePosition(str) { const lines = str.match(/\n/g); if (lines) lineno += lines.length; const i = str.lastIndexOf('\n'); column = ~i ? str.length - i : column + str.length; } /** * Match `re` and return captures. */ function match(re) { const m = re.exec(css); if (!m) { return null; } const str = m[0]; updatePosition(str); css = css.slice(str.length); return m; } /** * Remove the whitespace at the beginning of css and update the location information */ function whitespace() { match(/^\s*/); } /** * Record the position and set the value of node.position */ function position() { return (node) => { node.position = { start: { line: lineno, column }, end: { line: lineno, column }, source: options.source, content: css, }; whitespace(); return node; }; } const errorsList = []; function error(msg) { const errorInstance = new Error(`${options.source}:${lineno}:${column}: ${msg}`); const newError = { ...errorInstance, reason: msg, filename: options.source, line: lineno, column, source: css, }; if (options.silent) { errorsList.push(newError); } else { throw newError; } } /** * Parse comment. */ function comment() { const pos = position(); if (css.charAt(0) !== '/' || css.charAt(1) !== '*') { return null; } let i = 2; while (css.charAt(i) !== '' && (css.charAt(i) !== '*' || css.charAt(i + 1) !== '/')) { i += 1; } i += 2; if (css.charAt(i - 1) === '') { return error('End of comment missing'); } const str = css.slice(2, i - 2); column += 2; updatePosition(str); css = css.slice(i); column += 2; return pos({ type: 'comment', comment: str, }); } /** * Parse comments; */ function comments(rawRules = []) { let c; const commentRules = rawRules || []; while ((c = comment())) { if (c !== false) { commentRules.push(c); } } return commentRules; } /** * Generate rule set */ function rules() { let node; const actualRules = []; whitespace(); comments(actualRules); // eslint-disable-next-line @typescript-eslint/no-use-before-define while (css.length && css.charAt(0) !== '}' && (node = atrule() || rule())) { if (node) { actualRules.push(node); comments(actualRules); } } return actualRules; } /** * Process the stylesheet and get a list of rules */ function stylesheet() { const rulesList = rules(); return { type: 'stylesheet', stylesheet: { source: options.source, rules: rulesList, parsingErrors: errorsList, }, }; } /** * Remove whitespace between opening brace and content */ function open() { return match(/^{\s*/); } /** * Remove whitespace between closing brace and content */ function close() { return match(/^}/); } /** * Parse selector. */ function selector() { const matched = match(/^([^{]+)/); if (!matched) { return null; } /* @fix Remove all comments from selectors like #root .result /* df *\/ div * http://ostermiller.org/findcomment.html */ return matched[0] .trim() .replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '') .replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, m => m.replace(/,/g, '\u200C')) .split(/\s*(?![^(]*\)),\s*/) .map(s => s.replace(/\u200C/g, ',')); } /** * Parse declaration. */ // eslint-disable-next-line complexity function declaration() { const pos = position(); // prop let prop = match(/^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+])?)\s*/); if (!prop) { return null; } prop = prop[0].trim(); // : if (!match(/^:\s*/)) { return error('property missing \':\''); } // val const propertyName = prop.replace(commentRegexp, ''); const camelizeProperty = camelize(propertyName); let property = (() => { const mapProperty = PROPERTIES_MAP[camelizeProperty]; if (mapProperty) { return mapProperty; } return camelizeProperty; })(); const val = match(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]{0,500}?\)|[^};])+)/); let value = val ? val[0].trim().replace(commentRegexp, '') : ''; switch (property) { case 'backgroundImage': { [property, value] = parseBackgroundImage(property, value); break; } case 'transform': { const keyReg = /((\w+)\s*\()/; const valueReg = /(?:\(['"]?)(.*?)(?:['"]?\))/; const oldValue = value; value = []; oldValue.split(' ').forEach((transformKeyValue) => { if (keyReg.test(transformKeyValue)) { let key; let v; const matchedKey = keyReg.exec(transformKeyValue); const matchedValue = valueReg.exec(transformKeyValue); if (matchedKey) { [, , key] = matchedKey; } if (matchedValue) { [, v] = matchedValue; } if (v.indexOf('.') === 0) { v = `0${v}`; } if (parseFloat(v).toString() === v) { v = parseFloat(v); } const transform = {}; transform[key] = v; value.push(transform); } else { error('missing \'(\''); } }); break; } case 'fontWeight': // Keep string and going on. break; case 'shadowOffset': { const declarationPos = value .split(' ') .filter(v => v) .map(v => convertPxUnitToPt(v)); const [x] = declarationPos; let [, y] = declarationPos; if (!y) { y = x; } // FIXME: should not be width and height, should be x and y. value = { x, y, }; break; } case 'collapsable': value = Boolean(value); break; default: { value = tryConvertNumber$1(value); // Convert the px to pt for specific properties const sizeProperties = [ 'top', 'left', 'right', 'bottom', 'height', 'width', 'size', 'padding', 'margin', 'ratio', 'radius', 'offset', 'spread', ]; if (sizeProperties.find(size => property.toLowerCase().indexOf(size) > -1)) { value = convertPxUnitToPt(value); } } } const ret = pos({ type: 'declaration', value, property, }); match(/^[;\s]*/); return ret; } /** * Parse declarations. */ function declarations() { let decls = []; if (!open()) return error('missing \'{\''); comments(decls); // declarations let decl; while ((decl = declaration())) { if (decl !== false) { if (Array.isArray(decl)) { decls = decls.concat(decl); } else { decls.push(decl); } comments(decls); } } if (!close()) return error('missing \'}\''); return decls; } /** * Parse rule. */ function rule() { const pos = position(); const sel = selector(); if (!sel) return error('selector missing'); comments(); return pos({ type: 'rule', selectors: sel, declarations: declarations(), }); } /** * Parse keyframe. */ function keyframe() { let m; const vals = []; const pos = position(); while ((m = match(/^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/))) { vals.push(m[1]); match(/^,\s*/); } if (!vals.length) { return null; } return pos({ type: 'keyframe', values: vals, declarations: declarations(), }); } /** * Parse keyframes. */ function atkeyframes() { const pos = position(); let m = match(/^@([-\w]+)?keyframes\s*/); if (!m) { return null; } const vendor = m[1]; // identifier m = match(/^([-\w]+)\s*/); if (!m) { return error('@keyframes missing name'); } const name = m[1]; if (!open()) return error('@keyframes missing \'{\''); let frame; let frames = comments(); while ((frame = keyframe())) { frames.push(frame); frames = frames.concat(comments()); } if (!close()) return error('@keyframes missing \'}\''); return pos({ type: 'keyframes', name, vendor, keyframes: frames, }); } /** * Parse supports. */ function atsupports() { const pos = position(); const m = match(/^@supports *([^{]+)/); if (!m) { return null; } const supports = m[1].trim(); if (!open()) return error('@supports missing \'{\''); const style = comments().concat(rules()); if (!close()) return error('@supports missing \'}\''); return pos({ type: 'supports', supports, rules: style, }); } /** * Parse host. */ function athost() { const pos = position(); const m = match(/^@host\s*/); if (!m) { return null; } if (!open()) { return error('@host missing \'{\''); } const style = comments().concat(rules()); if (!close()) { return error('@host missing \'}\''); } return pos({ type: 'host', rules: style, }); } /** * Parse media. */ function atmedia() { const pos = position(); const m = match(/^@media *([^{]+)/); if (!m) { return null; } const media = m[1].trim(); if (!open()) { return error('@media missing \'{\''); } const style = comments().concat(rules()); if (!close()) { return error('@media missing \'}\''); } return pos({ type: 'media', media, rules: style, }); } /** * Parse custom-media. */ function atcustommedia() { const pos = position(); const m = match(/^@custom-media\s+(--[^\s]+)\s*([^{;]{1,200}?);/); if (!m) { return null; } return pos({ type: 'custom-media', name: m[1].trim(), media: m[2].trim(), }); } /** * Parse paged media. */ function atpage() { const pos = position(); const m = match(/^@page */); if (!m) { return null; } const sel = selector() || []; if (!open()) { return error('@page missing \'{\''); } let decls = comments(); // declarations let decl; while ((decl = declaration())) { decls.push(decl); decls = decls.concat(comments()); } if (!close()) { return error('@page missing \'}\''); } return pos({ type: 'page', selectors: sel, declarations: decls, }); } /** * Parse document. */ function atdocument() { const pos = position(); const m = match(/^@([-\w]+)?document *([^{]+)/); if (!m) { return null; } const vendor = m[1].trim(); const doc = m[2].trim(); if (!open()) { return error('@document missing \'{\''); } const style = comments().concat(rules()); if (!close()) { return error('@document missing \'}\''); } return pos({ type: 'document', document: doc, vendor, rules: style, }); } /** * Parse font-face. */ function atfontface() { const pos = position(); const m = match(/^@font-face\s*/); if (!m) { return null; } if (!open()) { return error('@font-face missing \'{\''); } let decls = comments(); // declarations let decl; while ((decl = declaration())) { decls.push(decl); decls = decls.concat(comments()); } if (!close()) { return error('@font-face missing \'}\''); } return pos({ type: 'font-face', declarations: decls, }); } /** * Parse non-block at-rules */ function compileAtRule(name) { const re = new RegExp(`^@${name}\\s*([^;]+);`); return () => { const pos = position(); const m = match(re); if (!m) { return null; } const ret = { type: name }; ret[name] = m[1].trim(); return pos(ret); }; } /** * Parse import */ const atimport = compileAtRule('import'); /** * Parse charset */ const atcharset = compileAtRule('charset'); /** * Parse namespace */ const atnamespace = compileAtRule('namespace'); /** * Parse at rule. */ // eslint-disable-next-line complexity function atrule() { if (css[0] !== '@') { return null; } return (atkeyframes() || atmedia() || atcustommedia() || atsupports() || atimport() || atcharset() || atnamespace() || atdocument() || atpage() || athost() || atfontface()); } return addParent(stylesheet(), null); } /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2022 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * stores the Selector list matched by the node */ class SelectorsMatch { constructor() { this.changeMap = new Map(); } /** * Add attribute * * @param node - target node * @param attribute - attribute name */ addAttribute(node, attribute) { const deps = this.properties(node); if (!deps.attributes) { deps.attributes = new Set(); } deps.attributes.add(attribute); } /** * add pseudo class * * @param node - target node * @param pseudoClass - pseudo class */ addPseudoClass(node, pseudoClass) { const deps = this.properties(node); if (!deps.pseudoClasses) { deps.pseudoClasses = new Set(); } deps.pseudoClasses.add(pseudoClass); } properties(node) { let set = this.changeMap.get(node); if (!set) { this.changeMap.set(node, (set = {})); } return set; } } /** * selectors map class * * @public * */ class SelectorsMap { constructor(ruleSets) { this.id = {}; this.class = {}; this.type = {}; this.universal = []; this.position = 0; this.ruleSets = ruleSets; ruleSets.forEach(rule => rule.lookupSort(this)); } /** * Remove the specified style from the style rules map * * @param map - style map * @param head - style key * @param sel - selector */ static removeFromMap(map, head, sel) { const list = map[head]; const index = list.findIndex(item => { var _a; return item.sel.ruleSet.hash === ((_a = sel.ruleSet) === null || _a === void 0 ? void 0 : _a.hash); }); if (index !== -1) { list.splice(index, 1); } } /** * Append a new list of style rules * * @param appendRules - list of style rules */ append(appendRules) { this.ruleSets = this.ruleSets.concat(appendRules); appendRules.forEach(rule => rule.lookupSort(this)); } /** * Find the style in the list of style rules according to the hash value and delete it * * @param hash - hash of style chunk */ delete(hash) { const removeRuleSets = []; // Find the style in the list of style rules according to the hash value this.ruleSets = this.ruleSets.filter((rule) => { if (rule.hash !== hash) { return true; } removeRuleSets.push(rule); return false; }); // Call the remove rule api to remove the deactivated style rule removeRuleSets.forEach(rule => rule.removeSort(this)); } /** * Find the matching style information according to the id, class, attribute of the hippy node * * @param node - target node * @param ssrNodes - ssr node list */ query(node, ssrNodes) { const { tagName, id, classList, props } = node; let domId = id; let domClassList = classList; if (props === null || props === void 0 ? void 0 : props.attributes) { // props and attributes exist means this node is generated from server side(except development). // so we need to use these props first const { attributes } = props; domClassList = new Set(((attributes === null || attributes === void 0 ? void 0 : attributes.class) || '').split(' ').filter(x => x.trim())); domId = attributes.id; } const selectorClasses = [this.universal, this.id[domId], this.type[tagName]]; if (domClassList === null || domClassList === void 0 ? void 0 : domClassList.size) { domClassList.forEach(c => selectorClasses.push(this.class[c])); } const selectors = selectorClasses .filter(arr => !!arr) .reduce((cur, next) => cur.concat(next), []); const selectorsMatch = new SelectorsMatch(); selectorsMatch.selectors = selectors .filter(sel => sel.sel.accumulateChanges(node, selectorsMatch, ssrNodes)) .sort((a, b) => a.sel.specificity - b.sel.specificity || a.pos - b.pos) .map(docSel => docSel.sel); return selectorsMatch; } removeById(id, sel) { SelectorsMap.removeFromMap(this.id, id, sel); } sortById(id, sel) { this.addToMap(this.id, id, sel); } removeByClass(cssClass, sel) { SelectorsMap.removeFromMap(this.class, cssClass, sel); } sortByClass(cssClass, sel) { this.addToMap(this.class, cssClass, sel); } removeByType(cssType, sel) { SelectorsMap.removeFromMap(this.type, cssType, sel); } sortByType(cssType, sel) { this.addToMap(this.type, cssType, sel); } removeAsUniversal(sel) { const index = this.universal.findIndex(item => { var _a, _b; return ((_a = item.sel.ruleSet) === null || _a === void 0 ? void 0 : _a.hash) === ((_b = sel.ruleSet) === null || _b === void 0 ? void 0 : _b.hash); }); if (index !== -1) { this.universal.splice(index); } } sortAsUniversal(sel) { this.universal.push(this.makeDocSelector(sel)); } /** * add style selector to map * * @param map - style map * @param head - style key * @param sel - selector */ addToMap(map, head, sel) { this.position += 1; const list = map[head]; if (list) { list.push(this.makeDocSelector(sel)); } else { map[head] = [this.makeDocSelector(sel)]; } } makeDocSelector(sel) { this.position += 1; return { sel, pos: this.position }; } } /* * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2022 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * determine if the value is null or undefined * * @param value - value */ function isNullOrUndefined$1(value) { return typeof value === 'undefined' || value === null; } /** * wrap string text * * @param text - string */ function wrap(text) { return text ? ` ${text}` : ''; } /** * get node's parent node, in client side, node has parentNode props. in server side, find parentNode * by pId * * @param node - child node * @param ssrNodes - ssr node list * * @internal */ function getParentNode(node, ssrNodes) { if (ssrNodes) { // in server side mode, find parent node with pId if ((node === null || node === void 0 ? void 0 : node.pId) && ssrNodes[node.pId]) { return ssrNodes[node.pId]; } return null; } return node === null || node === void 0 ? void 0 : node.parentNode; } /** * Base classes */ class SelectorCore { constructor() { // style weight this.specificity = 0; } /** * Sort and store style rules according to categories, * such as id selectors are grouped into one category, class names are grouped into one category, etc. * * @param sorter - sort rules * @param base - base */ lookupSort(sorter, base) { sorter.sortAsUniversal(base !== null && base !== void 0 ? base : this); } /** * remove sort * * @param sorter - sort rules * @param base - base */ removeSort(sorter, base) { sorter.removeAsUniversal(base !== null && base !== void 0 ? base : this); } } /** * Simple selector type * provides the method for judging whether a node matches and the method for tracking node attributes */ class SimpleSelector extends SelectorCore { constructor() { super(...arguments); // rarity of style this.rarity = 0; } accumulateChanges(node, match) { if (!this.dynamic) { return this.match(node); } if (this.mayMatch(node)) { this.trackChanges(node, match); return true; } return false; } /** * determine if the node matches * * @param node - target node */ match(node) { return !!node; } /** * prejudgment * * @param node - target node */ mayMatch(node) { return this.match(node); } /** * track changes of node * * @param node - target node * @param match - SelectorsMatch */ trackChanges(node, match) { } } class SimpleSelectorSequence extends SimpleSelector { constructor(selectors) { super(); this.specificity = selectors.reduce((sum, sel) => sel.specificity + sum, 0); this.head = selectors.reduce((prev, curr) => { return !prev || (prev instanceof SimpleSelector && curr.rarity > prev.rarity) ? curr : prev; }, null); this.dynamic = selectors.some(sel => sel.dynamic); this.selectors = selectors; } toString() { return `${this.selectors.join('')}${wrap(this.combinator)}`; } match(node) { if (!node) { return false; } return this.selectors.every(sel => sel.match(node)); } mayMatch(node) { if (!node) { return false; } return this.selectors.every(sel => sel.mayMatch(node)); } trackChanges(node, match) { this.selectors.forEach(sel => sel.trackChanges(node, match)); } lookupSort(sorter, base) { if (this.head && this.head instanceof SimpleSelector) { this.head.lookupSort(sorter, base !== null && base !== void 0 ? base : this); } } removeSort(sorter, base) { if (this.head && this.head instanceof SimpleSelector) { this.head.removeSort(sorter, base !== null && base !== void 0 ? base : this); } } } /** * Generic selector type, eg. * */ class UniversalSelector extends SimpleSelector { constructor() { super(); this.specificity = 0x00000000; this.rarity = 0; this.dynamic = false; } toString() { return `*${wrap(this.combinator)}`; } match() { // universal selectors can all match return true; } } /** * ID selector, eg. #root */ class IdSelector extends SimpleSelector { constructor(id) { super(); this.specificity = 0x00010000; this.rarity = 3; this.dynamic = false; this.id = id; } toString() { return `#${this.id}${wrap(this.combinator)}`; } match(node) { var _a, _b; if (!node) { return false; } // ssr node's id is native id, the dom id is attribute's id return ((_b = (_a = node.props) === null || _a === void 0 ? void 0 : _a.attributes) === null || _b === void 0 ? void 0 : _b.id) === this.id || node.id === this.id; } lookupSort(sorter, base) { sorter.sortById(this.id, base !== null && base !== void 0 ? base : this); } removeSort(sorter, base) { sorter.removeById(this.id, base !== null && base !== void 0 ? base : this); } } /** * tag selector, eg. div, ul */ class TypeSelector extends SimpleSelector { constructor(cssType) { super(); this.specificity = 0x00000001; this.rarity = 1; this.dynamic = false; this.cssType = cssType; } toString() { return `${this.cssType}${wrap(this.combinator)}`; } match(node) { if (!node) { return false; } return node.tagName === this.cssType; } lookupSort(sorter, base) { sorter.sortByType(this.cssType, base !== null && base !== void 0 ? base : this); } removeSort(sorter, base) { sorter.removeByType(this.cssType, base !== null && base !== void 0 ? base : this); } } /** * class selector */ class ClassSelector extends SimpleSelector { constructor(className) { super(); this.specificity = 0x00000100; this.rarity = 2; this.dynamic = false; this.className = className; } toString() { return `.${this.className