188c88e8eSopenharmony_ci'use strict' 288c88e8eSopenharmony_ci 388c88e8eSopenharmony_civar css = require('css') 488c88e8eSopenharmony_civar util = require('./lib/util') 588c88e8eSopenharmony_civar validateItem = require('./lib/validator').validate 688c88e8eSopenharmony_civar fs = require('fs') 788c88e8eSopenharmony_civar path = require('path') 888c88e8eSopenharmony_civar lodash = require('lodash') 988c88e8eSopenharmony_ci 1088c88e8eSopenharmony_civar SELECTOR_MATCHER = /^[\.#]?[A-Za-z0-9_\-:]+$/ 1188c88e8eSopenharmony_civar DESCENDANT_SELECTOR_MATCHER = /^([.#]?[A-Za-z0-9_-]+(\s+|\s*>\s*))+([.#]?[A-Za-z0-9_\-:]+)$/ 1288c88e8eSopenharmony_civar IMPORT_MATCHER = /(['"]([^()]+?)['"])|(['"]([^()]+?)['"]\s+(only|not)?\s?(screen)?\s?((and|or|,|not|landscape)?\s?[(]([^()])+[)]\s*)+)/g 1388c88e8eSopenharmony_civar LENGTH_REGEXP = /^[-+]?\d*\.?\d+(\S*)$/ 1488c88e8eSopenharmony_ciconst CARD_SELECTOR = /^[\.#][A-Za-z0-9_\-]+$/ 1588c88e8eSopenharmony_ciconst card = process.env.DEVICE_LEVEL === 'card' 1688c88e8eSopenharmony_civar ALL_SELECTOR_MATCHER = /^\*$/ 1788c88e8eSopenharmony_civar ATTRIBUTE_SELECTOR = /^\[+(?![0-9])\w{0,}(\s*=\s*)((?![0-9])\w{0,}|\"\w{0,}\")\]+$/ 1888c88e8eSopenharmony_civar ELEMENT_AND_ELEMENT = /^[a-zA-Z][a-zA-Z-]{0,}\s{0,}(\+\s{0,}[a-zA-Z][a-zA-Z-]{0,})+$/ 1988c88e8eSopenharmony_civar CONTENT_ID = /^[a-zA-Z][a-zA-Z-]{0,}([#][a-zA-Z][a-zA-Z0-9-]{0,})(.+?::(after|before))+$/ 2088c88e8eSopenharmony_ci 2188c88e8eSopenharmony_ci/** 2288c88e8eSopenharmony_ci * expand margin、padding、border、borderWidth、borderColor、borderStyle properties、animation 2388c88e8eSopenharmony_ci * 2488c88e8eSopenharmony_ci * @param {object} subResult 2588c88e8eSopenharmony_ci * @param {String} camelCasedName 2688c88e8eSopenharmony_ci * @param {object} ruleResult 2788c88e8eSopenharmony_ci */ 2888c88e8eSopenharmony_cifunction expand (subResult, camelCasedName, ruleResult) { 2988c88e8eSopenharmony_ci if (camelCasedName === 'border') { 3088c88e8eSopenharmony_ci subResult.value.forEach(function (item) { 3188c88e8eSopenharmony_ci if (item.type === 'Width' || item.type === 'Color' || item.type === 'Style') { 3288c88e8eSopenharmony_ci const spliceName = [camelCasedName + 'Top' + item.type, camelCasedName + 'Right' + item.type, camelCasedName + 3388c88e8eSopenharmony_ci 'Bottom' + item.type, camelCasedName + 'Left' + item.type] 3488c88e8eSopenharmony_ci util.splitAttr(ruleResult, item.value, spliceName) 3588c88e8eSopenharmony_ci } 3688c88e8eSopenharmony_ci else { 3788c88e8eSopenharmony_ci ruleResult[camelCasedName + item.type] = item.value 3888c88e8eSopenharmony_ci } 3988c88e8eSopenharmony_ci }) 4088c88e8eSopenharmony_ci } 4188c88e8eSopenharmony_ci else if (['borderTop', 'borderRight', 'borderBottom', 'borderLeft'].includes(camelCasedName)) { 4288c88e8eSopenharmony_ci subResult.value.forEach(function (item) { 4388c88e8eSopenharmony_ci ruleResult[camelCasedName + item.type] = item.value 4488c88e8eSopenharmony_ci }) 4588c88e8eSopenharmony_ci } 4688c88e8eSopenharmony_ci else if (camelCasedName === 'margin' || camelCasedName === 'padding') { 4788c88e8eSopenharmony_ci const spliceName = [camelCasedName + 'Top', camelCasedName + 'Right', 4888c88e8eSopenharmony_ci camelCasedName + 'Bottom', camelCasedName + 'Left'] 4988c88e8eSopenharmony_ci util.splitAttr(ruleResult, subResult.value, spliceName) 5088c88e8eSopenharmony_ci } 5188c88e8eSopenharmony_ci else if (camelCasedName === 'borderWidth') { 5288c88e8eSopenharmony_ci util.splitAttr(ruleResult, subResult.value, ['borderTopWidth', 'borderRightWidth', 5388c88e8eSopenharmony_ci 'borderBottomWidth', 'borderLeftWidth']) 5488c88e8eSopenharmony_ci } 5588c88e8eSopenharmony_ci else if (camelCasedName === 'borderColor') { 5688c88e8eSopenharmony_ci util.splitAttr(ruleResult, subResult.value, ['borderTopColor', 'borderRightColor', 5788c88e8eSopenharmony_ci 'borderBottomColor', 'borderLeftColor']) 5888c88e8eSopenharmony_ci } 5988c88e8eSopenharmony_ci else if (camelCasedName === 'borderStyle') { 6088c88e8eSopenharmony_ci util.splitAttr(ruleResult, subResult.value, ['borderTopStyle', 'borderRightStyle', 6188c88e8eSopenharmony_ci 'borderBottomStyle', 'borderLeftStyle']) 6288c88e8eSopenharmony_ci } 6388c88e8eSopenharmony_ci else if (camelCasedName === 'borderRadius') { 6488c88e8eSopenharmony_ci util.splitAttr(ruleResult, subResult.value, ['borderBottomLeftRadius', 'borderBottomRightRadius', 6588c88e8eSopenharmony_ci 'borderTopLeftRadius', 'borderTopRightRadius']) 6688c88e8eSopenharmony_ci } 6788c88e8eSopenharmony_ci else if (camelCasedName === 'gridGap') { 6888c88e8eSopenharmony_ci util.splitAttr(ruleResult, subResult.value, ['gridRowsGap', 'gridColumnsGap']) 6988c88e8eSopenharmony_ci } 7088c88e8eSopenharmony_ci else if (camelCasedName === 'boxShadow') { 7188c88e8eSopenharmony_ci subResult.value.forEach(function (item) { 7288c88e8eSopenharmony_ci if (item.type === 'H' || item.type === 'V' || item.type === 'Blur' || item.type === 'Spread' || 7388c88e8eSopenharmony_ci item.type === 'Color') { 7488c88e8eSopenharmony_ci util.splitAttr(ruleResult, item.value, [camelCasedName + item.type]) 7588c88e8eSopenharmony_ci } 7688c88e8eSopenharmony_ci }) 7788c88e8eSopenharmony_ci } 7888c88e8eSopenharmony_ci else if (camelCasedName === 'animation') { 7988c88e8eSopenharmony_ci Object.assign(ruleResult, subResult.value); 8088c88e8eSopenharmony_ci } 8188c88e8eSopenharmony_ci else { 8288c88e8eSopenharmony_ci // never to do 8388c88e8eSopenharmony_ci } 8488c88e8eSopenharmony_ci} 8588c88e8eSopenharmony_ci 8688c88e8eSopenharmony_ci/** 8788c88e8eSopenharmony_ci * expand flex style 8888c88e8eSopenharmony_ci * 8988c88e8eSopenharmony_ci * @param {object} rule 9088c88e8eSopenharmony_ci * @param {Array} ruleLog 9188c88e8eSopenharmony_ci */ 9288c88e8eSopenharmony_cifunction flexExpand(rule, ruleLog) { 9388c88e8eSopenharmony_ci for (let i = 0; i < rule.declarations.length; i++) { 9488c88e8eSopenharmony_ci let declaration = rule.declarations[i] 9588c88e8eSopenharmony_ci if (declaration.property === 'flex') { 9688c88e8eSopenharmony_ci let values = declaration.value.split(/\s+/) 9788c88e8eSopenharmony_ci rule.declarations.splice(i, 1) 9888c88e8eSopenharmony_ci if (values.length === 1) { 9988c88e8eSopenharmony_ci checkFlexOne(rule, ruleLog, declaration, values, i) 10088c88e8eSopenharmony_ci } else if (values.length === 2) { 10188c88e8eSopenharmony_ci checkFlexTwo(rule, ruleLog, declaration, values, i) 10288c88e8eSopenharmony_ci } else if (values.length === 3) { 10388c88e8eSopenharmony_ci checkFlexThree(rule, ruleLog, declaration, values, i) 10488c88e8eSopenharmony_ci } else { 10588c88e8eSopenharmony_ci ruleLog.push({ 10688c88e8eSopenharmony_ci line: declaration.position.start.line, 10788c88e8eSopenharmony_ci column: declaration.position.start.column, 10888c88e8eSopenharmony_ci reason: 'ERROR: Value `' + declaration.value + '` of the `' + 10988c88e8eSopenharmony_ci declaration.property + '` attribute is incorrect.' 11088c88e8eSopenharmony_ci }) 11188c88e8eSopenharmony_ci } 11288c88e8eSopenharmony_ci } 11388c88e8eSopenharmony_ci } 11488c88e8eSopenharmony_ci} 11588c88e8eSopenharmony_ci 11688c88e8eSopenharmony_cifunction getUnit(value) { 11788c88e8eSopenharmony_ci value = value.toString().trim() 11888c88e8eSopenharmony_ci let match = value.match(LENGTH_REGEXP) 11988c88e8eSopenharmony_ci if (match) { 12088c88e8eSopenharmony_ci let unit = match[1] 12188c88e8eSopenharmony_ci if (unit) { 12288c88e8eSopenharmony_ci if (unit === 'px') { 12388c88e8eSopenharmony_ci return "px" 12488c88e8eSopenharmony_ci } 12588c88e8eSopenharmony_ci } else { 12688c88e8eSopenharmony_ci return "none" 12788c88e8eSopenharmony_ci } 12888c88e8eSopenharmony_ci } 12988c88e8eSopenharmony_ci return null 13088c88e8eSopenharmony_ci} 13188c88e8eSopenharmony_ci 13288c88e8eSopenharmony_cifunction checkFlexOne(rule, ruleLog, declaration, values, i) { 13388c88e8eSopenharmony_ci const array = ['none', 'auto', 'initial'] 13488c88e8eSopenharmony_ci if (array.includes(values[0])) { 13588c88e8eSopenharmony_ci rule.declarations.splice(i, 0, {type: 'declaration', property: 'flex', 13688c88e8eSopenharmony_ci value: values[0], position: declaration.position}) 13788c88e8eSopenharmony_ci } else if (getUnit(values[0]) === 'px') { 13888c88e8eSopenharmony_ci rule.declarations.splice(i, 0, {type: 'declaration', property: 'flex-basis', 13988c88e8eSopenharmony_ci value: values[0], position: declaration.position}) 14088c88e8eSopenharmony_ci } else if (getUnit(values[0]) === 'none') { 14188c88e8eSopenharmony_ci rule.declarations.splice(i, 0, {type: 'declaration', property: 'flex-grow', 14288c88e8eSopenharmony_ci value: values[0], position: declaration.position}) 14388c88e8eSopenharmony_ci } else { 14488c88e8eSopenharmony_ci ruleLog.push({ 14588c88e8eSopenharmony_ci line: declaration.position.start.line, 14688c88e8eSopenharmony_ci column: declaration.position.start.column, 14788c88e8eSopenharmony_ci reason: 'ERROR: Value `' + declaration.value + '` of the `' + declaration.property + 14888c88e8eSopenharmony_ci '` attribute is incorrect.' + 'It must be a number, a number with unit `' + 'px`' + 14988c88e8eSopenharmony_ci ', none, auto, or initial.' 15088c88e8eSopenharmony_ci }) 15188c88e8eSopenharmony_ci } 15288c88e8eSopenharmony_ci} 15388c88e8eSopenharmony_ci 15488c88e8eSopenharmony_cifunction checkFlexTwo(rule, ruleLog, declaration, values, i) { 15588c88e8eSopenharmony_ci if (getUnit(values[0]) === 'none') { 15688c88e8eSopenharmony_ci rule.declarations.splice(i, 0, {type: 'declaration', property: 'flex-grow', 15788c88e8eSopenharmony_ci value: values[0], position: declaration.position}) 15888c88e8eSopenharmony_ci if (getUnit(values[1]) === 'px') { 15988c88e8eSopenharmony_ci rule.declarations.splice(i, 0, {type: 'declaration', property: 'flex-basis', 16088c88e8eSopenharmony_ci value: values[1], position: declaration.position}) 16188c88e8eSopenharmony_ci } else if (getUnit(values[1]) === 'none') { 16288c88e8eSopenharmony_ci rule.declarations.splice(i, 0, {type: 'declaration', property: 'flex-shrink', 16388c88e8eSopenharmony_ci value: values[1], position: declaration.position}) 16488c88e8eSopenharmony_ci } else { 16588c88e8eSopenharmony_ci ruleLog.push({ 16688c88e8eSopenharmony_ci line: declaration.position.start.line, 16788c88e8eSopenharmony_ci column: declaration.position.start.column, 16888c88e8eSopenharmony_ci reason: 'ERROR: Value `' + declaration.value + '` of the `' + declaration.property + 16988c88e8eSopenharmony_ci '` attribute is incorrect. Value `' + values[1] + 17088c88e8eSopenharmony_ci '` must be a number or a number with unit `' + 'px`.' 17188c88e8eSopenharmony_ci }) 17288c88e8eSopenharmony_ci } 17388c88e8eSopenharmony_ci } else { 17488c88e8eSopenharmony_ci ruleLog.push({ 17588c88e8eSopenharmony_ci line: declaration.position.start.line, 17688c88e8eSopenharmony_ci column: declaration.position.start.column, 17788c88e8eSopenharmony_ci reason: 'ERROR: Value `' + declaration.value + '` of the `' + 17888c88e8eSopenharmony_ci declaration.property + '` attribute is incorrect. Value `' + values[0] + '` must be a number.' 17988c88e8eSopenharmony_ci }) 18088c88e8eSopenharmony_ci } 18188c88e8eSopenharmony_ci} 18288c88e8eSopenharmony_ci 18388c88e8eSopenharmony_cifunction checkFlexThree(rule, ruleLog, declaration, values, i) { 18488c88e8eSopenharmony_ci if (getUnit(values[0]) === 'none' && getUnit(values[1]) === 'none' && getUnit(values[2]) === 'px') { 18588c88e8eSopenharmony_ci rule.declarations.splice(i, 0, {type: 'declaration', property: 'flex-grow', 18688c88e8eSopenharmony_ci value: values[0], position: declaration.position}) 18788c88e8eSopenharmony_ci rule.declarations.splice(i, 0, {type: 'declaration', property: 'flex-shrink', 18888c88e8eSopenharmony_ci value: values[1], position: declaration.position}) 18988c88e8eSopenharmony_ci rule.declarations.splice(i, 0, {type: 'declaration', property: 'flex-basis', 19088c88e8eSopenharmony_ci value: values[2], position: declaration.position}) 19188c88e8eSopenharmony_ci } else { 19288c88e8eSopenharmony_ci ruleLog.push({ 19388c88e8eSopenharmony_ci line: declaration.position.start.line, 19488c88e8eSopenharmony_ci column: declaration.position.start.column, 19588c88e8eSopenharmony_ci reason: 'ERROR: Value `' + declaration.value + '` of the `' + declaration.property + 19688c88e8eSopenharmony_ci '` attribute is incorrect. It must be in the format of (1, 1, 1px).' 19788c88e8eSopenharmony_ci }) 19888c88e8eSopenharmony_ci } 19988c88e8eSopenharmony_ci} 20088c88e8eSopenharmony_ci 20188c88e8eSopenharmony_ci/** 20288c88e8eSopenharmony_ci * Parse `<style>` code to a JSON Object and log errors & warnings 20388c88e8eSopenharmony_ci * 20488c88e8eSopenharmony_ci * @param {string} code 20588c88e8eSopenharmony_ci * @param {function} done which will be called with 20688c88e8eSopenharmony_ci * - err:Error 20788c88e8eSopenharmony_ci * - data.jsonStyle{}: `classname.propname.value`-like object 20888c88e8eSopenharmony_ci * - data.log[{line, column, reason}] 20988c88e8eSopenharmony_ci */ 21088c88e8eSopenharmony_cifunction parse(code, done, resourcePath) { 21188c88e8eSopenharmony_ci var ast, err, jsonStyle = {}, log = [] 21288c88e8eSopenharmony_ci 21388c88e8eSopenharmony_ci // css parse 21488c88e8eSopenharmony_ci ast = css.parse(code, {silent: true, source: resourcePath}); 21588c88e8eSopenharmony_ci 21688c88e8eSopenharmony_ci // catch syntax error 21788c88e8eSopenharmony_ci if (ast.stylesheet.parsingErrors && ast.stylesheet.parsingErrors.length) { 21888c88e8eSopenharmony_ci err = ast.stylesheet.parsingErrors 21988c88e8eSopenharmony_ci err.forEach(function (error) { 22088c88e8eSopenharmony_ci log.push({line: error.line, column: error.column, reason: error.toString().replace('Error', 'ERROR')}) 22188c88e8eSopenharmony_ci }) 22288c88e8eSopenharmony_ci } 22388c88e8eSopenharmony_ci 22488c88e8eSopenharmony_ci // walk all 22588c88e8eSopenharmony_ci /* istanbul ignore else */ 22688c88e8eSopenharmony_ci if (ast && ast.type === 'stylesheet' && ast.stylesheet && 22788c88e8eSopenharmony_ci ast.stylesheet.rules && ast.stylesheet.rules.length) { 22888c88e8eSopenharmony_ci ast.stylesheet.rules.forEach(function (rule) { 22988c88e8eSopenharmony_ci var type = rule.type 23088c88e8eSopenharmony_ci var ruleResult = {} 23188c88e8eSopenharmony_ci var ruleLog = [] 23288c88e8eSopenharmony_ci 23388c88e8eSopenharmony_ci if (type === 'rule') { 23488c88e8eSopenharmony_ci if (rule.declarations && rule.declarations.length) { 23588c88e8eSopenharmony_ci flexExpand(rule, ruleLog) 23688c88e8eSopenharmony_ci 23788c88e8eSopenharmony_ci rule.declarations.forEach(function (declaration) { 23888c88e8eSopenharmony_ci var subType = declaration.type 23988c88e8eSopenharmony_ci var name, value, line, column, subResult, camelCasedName 24088c88e8eSopenharmony_ci 24188c88e8eSopenharmony_ci /* istanbul ignore if */ 24288c88e8eSopenharmony_ci if (subType !== 'declaration') { 24388c88e8eSopenharmony_ci return 24488c88e8eSopenharmony_ci } 24588c88e8eSopenharmony_ci 24688c88e8eSopenharmony_ci name = declaration.property 24788c88e8eSopenharmony_ci value = declaration.value 24888c88e8eSopenharmony_ci 24988c88e8eSopenharmony_ci // validate declarations and collect them to result 25088c88e8eSopenharmony_ci camelCasedName = util.hyphenedToCamelCase(name) 25188c88e8eSopenharmony_ci subResult = validateItem(camelCasedName, value) 25288c88e8eSopenharmony_ci 25388c88e8eSopenharmony_ci // expand margin、padding、border、borderWidth、borderColor、borderStyle properties、animation 25488c88e8eSopenharmony_ci if (subResult.value && Object.values(util.SPLECIAL_ATTR).indexOf(camelCasedName) !== -1) { 25588c88e8eSopenharmony_ci expand(subResult, camelCasedName, ruleResult) 25688c88e8eSopenharmony_ci } 25788c88e8eSopenharmony_ci 25888c88e8eSopenharmony_ci /* istanbul ignore else */ 25988c88e8eSopenharmony_ci if ((typeof subResult.value === 'number' || typeof subResult.value === 'string') 26088c88e8eSopenharmony_ci && !Object.values(util.SPLECIAL_ATTR).includes(camelCasedName)) { 26188c88e8eSopenharmony_ci ruleResult[camelCasedName] = subResult.value 26288c88e8eSopenharmony_ci } 26388c88e8eSopenharmony_ci if (subResult.log) { 26488c88e8eSopenharmony_ci subResult.log.line = declaration.position.start.line 26588c88e8eSopenharmony_ci subResult.log.column = declaration.position.start.column 26688c88e8eSopenharmony_ci ruleLog.push(subResult.log) 26788c88e8eSopenharmony_ci } 26888c88e8eSopenharmony_ci }) 26988c88e8eSopenharmony_ci 27088c88e8eSopenharmony_ci if (card && rule.selectors.length > 1) { 27188c88e8eSopenharmony_ci log.push({ 27288c88e8eSopenharmony_ci line: rule.position.start.line, 27388c88e8eSopenharmony_ci column: rule.position.start.column, 27488c88e8eSopenharmony_ci reason: 'ERROR: The `' + rule.selectors.join(', ') + '` selector is not supported.' 27588c88e8eSopenharmony_ci }) 27688c88e8eSopenharmony_ci } else { 27788c88e8eSopenharmony_ci rule.selectors.forEach(function (selector) { 27888c88e8eSopenharmony_ci const flag = card ? selector.match(CARD_SELECTOR) : 27988c88e8eSopenharmony_ci selector.match(SELECTOR_MATCHER) || selector.match(DESCENDANT_SELECTOR_MATCHER) || 28088c88e8eSopenharmony_ci selector.match(ALL_SELECTOR_MATCHER) || selector.match(ATTRIBUTE_SELECTOR) || 28188c88e8eSopenharmony_ci selector.match(ELEMENT_AND_ELEMENT) || selector.match(CONTENT_ID) 28288c88e8eSopenharmony_ci if (flag) { 28388c88e8eSopenharmony_ci var className = selector 28488c88e8eSopenharmony_ci 28588c88e8eSopenharmony_ci // handle pseudo class 28688c88e8eSopenharmony_ci var pseudoIndex = className.indexOf(':') 28788c88e8eSopenharmony_ci if (pseudoIndex > -1) { 28888c88e8eSopenharmony_ci var pseudoCls = className.slice(pseudoIndex) 28988c88e8eSopenharmony_ci className = className.slice(0, pseudoIndex) 29088c88e8eSopenharmony_ci var pseudoRuleResult = {} 29188c88e8eSopenharmony_ci Object.keys(ruleResult).forEach(function (prop) { 29288c88e8eSopenharmony_ci pseudoRuleResult[prop + pseudoCls] = ruleResult[prop] 29388c88e8eSopenharmony_ci }) 29488c88e8eSopenharmony_ci ruleResult = pseudoRuleResult 29588c88e8eSopenharmony_ci } 29688c88e8eSopenharmony_ci 29788c88e8eSopenharmony_ci // merge style 29888c88e8eSopenharmony_ci Object.keys(ruleResult).forEach(function (prop) { 29988c88e8eSopenharmony_ci // handle transition 30088c88e8eSopenharmony_ci if (prop.indexOf('transition') === 0 && prop !== 'transition') { 30188c88e8eSopenharmony_ci var realProp = prop.replace('transition', '') 30288c88e8eSopenharmony_ci realProp = realProp[0].toLowerCase() + realProp.slice(1) 30388c88e8eSopenharmony_ci jsonStyle['@TRANSITION'] = jsonStyle['@TRANSITION'] || {} 30488c88e8eSopenharmony_ci jsonStyle['@TRANSITION'][className] = jsonStyle['@TRANSITION'][className] || {} 30588c88e8eSopenharmony_ci jsonStyle['@TRANSITION'][className][realProp] = ruleResult[prop] 30688c88e8eSopenharmony_ci } 30788c88e8eSopenharmony_ci 30888c88e8eSopenharmony_ci jsonStyle[className] = jsonStyle[className] || {} 30988c88e8eSopenharmony_ci jsonStyle[className][prop] = ruleResult[prop] 31088c88e8eSopenharmony_ci }) 31188c88e8eSopenharmony_ci } else { 31288c88e8eSopenharmony_ci log.push({ 31388c88e8eSopenharmony_ci line: rule.position.start.line, 31488c88e8eSopenharmony_ci column: rule.position.start.column, 31588c88e8eSopenharmony_ci reason: 'ERROR: The `' + selector + '` selector is not supported.' 31688c88e8eSopenharmony_ci }) 31788c88e8eSopenharmony_ci } 31888c88e8eSopenharmony_ci }) 31988c88e8eSopenharmony_ci } 32088c88e8eSopenharmony_ci log = log.concat(ruleLog) 32188c88e8eSopenharmony_ci } 32288c88e8eSopenharmony_ci } 32388c88e8eSopenharmony_ci /* istanbul ignore else */ 32488c88e8eSopenharmony_ci else if (type === 'font-face') { 32588c88e8eSopenharmony_ci /* istanbul ignore else */ 32688c88e8eSopenharmony_ci if (rule.declarations && rule.declarations.length) { 32788c88e8eSopenharmony_ci rule.declarations.forEach(function (declaration) { 32888c88e8eSopenharmony_ci /* istanbul ignore if */ 32988c88e8eSopenharmony_ci if (declaration.type !== 'declaration') { 33088c88e8eSopenharmony_ci return 33188c88e8eSopenharmony_ci } 33288c88e8eSopenharmony_ci var name = util.hyphenedToCamelCase(declaration.property) 33388c88e8eSopenharmony_ci var value = declaration.value 33488c88e8eSopenharmony_ci if (name === 'fontFamily' && '\"\''.indexOf(value[0]) > -1) { 33588c88e8eSopenharmony_ci value = value.slice(1, value.length - 1) 33688c88e8eSopenharmony_ci } 33788c88e8eSopenharmony_ci ruleResult[name] = value 33888c88e8eSopenharmony_ci }) 33988c88e8eSopenharmony_ci if (!jsonStyle['@FONT-FACE']) { 34088c88e8eSopenharmony_ci jsonStyle['@FONT-FACE'] = [] 34188c88e8eSopenharmony_ci } 34288c88e8eSopenharmony_ci jsonStyle['@FONT-FACE'].push(ruleResult) 34388c88e8eSopenharmony_ci } 34488c88e8eSopenharmony_ci } 34588c88e8eSopenharmony_ci else if (type === 'import') { 34688c88e8eSopenharmony_ci parseImport(resourcePath, rule, jsonStyle, log) 34788c88e8eSopenharmony_ci } 34888c88e8eSopenharmony_ci else if (type === 'keyframes' && !card) { 34988c88e8eSopenharmony_ci if (!jsonStyle['@KEYFRAMES']) { 35088c88e8eSopenharmony_ci jsonStyle['@KEYFRAMES'] = {} 35188c88e8eSopenharmony_ci } 35288c88e8eSopenharmony_ci var keyName = rule.name 35388c88e8eSopenharmony_ci jsonStyle['@KEYFRAMES'][keyName] = [] 35488c88e8eSopenharmony_ci if (rule.keyframes && rule.keyframes.length) { 35588c88e8eSopenharmony_ci if (card) { 35688c88e8eSopenharmony_ci log.push({ 35788c88e8eSopenharmony_ci line: rule.position.start.line, 35888c88e8eSopenharmony_ci column: rule.position.start.column, 35988c88e8eSopenharmony_ci reason: 'ERROR: The keyframes is not supported!' 36088c88e8eSopenharmony_ci }) 36188c88e8eSopenharmony_ci } else { 36288c88e8eSopenharmony_ci rule.keyframes.forEach(function (keyframe) { 36388c88e8eSopenharmony_ci 36488c88e8eSopenharmony_ci var keyframeType = keyframe.type 36588c88e8eSopenharmony_ci 36688c88e8eSopenharmony_ci /* istanbul ignore if */ 36788c88e8eSopenharmony_ci if (keyframeType !== 'keyframe') { 36888c88e8eSopenharmony_ci return 36988c88e8eSopenharmony_ci } 37088c88e8eSopenharmony_ci 37188c88e8eSopenharmony_ci if (keyframe.declarations && keyframe.declarations.length) { 37288c88e8eSopenharmony_ci keyframe.declarations.forEach(function (declaration) { 37388c88e8eSopenharmony_ci var subType = declaration.type 37488c88e8eSopenharmony_ci var name, value, line, column, subResult, camelCasedName 37588c88e8eSopenharmony_ci 37688c88e8eSopenharmony_ci /* istanbul ignore if */ 37788c88e8eSopenharmony_ci if (subType !== 'declaration') { 37888c88e8eSopenharmony_ci return 37988c88e8eSopenharmony_ci } 38088c88e8eSopenharmony_ci 38188c88e8eSopenharmony_ci name = declaration.property 38288c88e8eSopenharmony_ci value = declaration.value 38388c88e8eSopenharmony_ci 38488c88e8eSopenharmony_ci // validate declarations and collect them to result 38588c88e8eSopenharmony_ci camelCasedName = util.hyphenedToCamelCase(name) 38688c88e8eSopenharmony_ci subResult = validateItem(camelCasedName, value) 38788c88e8eSopenharmony_ci 38888c88e8eSopenharmony_ci // expand margin、padding、border、borderWidth、borderColor、borderStyle properties 38988c88e8eSopenharmony_ci if (subResult.value && Object.values(util.SPLECIAL_ATTR).indexOf(camelCasedName) !== -1) { 39088c88e8eSopenharmony_ci expand(subResult, camelCasedName, ruleResult) 39188c88e8eSopenharmony_ci } 39288c88e8eSopenharmony_ci /* istanbul ignore else */ 39388c88e8eSopenharmony_ci if ((typeof subResult.value === 'number' || typeof subResult.value === 'string') 39488c88e8eSopenharmony_ci && !Object.values(util.SPLECIAL_ATTR).includes(camelCasedName)) { 39588c88e8eSopenharmony_ci ruleResult[camelCasedName] = subResult.value 39688c88e8eSopenharmony_ci } 39788c88e8eSopenharmony_ci if (subResult.log) { 39888c88e8eSopenharmony_ci subResult.log.line = declaration.position.start.line 39988c88e8eSopenharmony_ci subResult.log.column = declaration.position.start.column 40088c88e8eSopenharmony_ci ruleLog.push(subResult.log) 40188c88e8eSopenharmony_ci } 40288c88e8eSopenharmony_ci }) 40388c88e8eSopenharmony_ci } 40488c88e8eSopenharmony_ci 40588c88e8eSopenharmony_ci if (keyframe.values[0] === 'from') { 40688c88e8eSopenharmony_ci var keyframeResult = {} 40788c88e8eSopenharmony_ci Object.keys(ruleResult).forEach(function (prop) { 40888c88e8eSopenharmony_ci keyframeResult[prop] = ruleResult[prop] 40988c88e8eSopenharmony_ci }) 41088c88e8eSopenharmony_ci keyframeResult['time'] = 0 41188c88e8eSopenharmony_ci jsonStyle['@KEYFRAMES'][keyName].push(keyframeResult) 41288c88e8eSopenharmony_ci } 41388c88e8eSopenharmony_ci if (keyframe.values[0] === 'to') { 41488c88e8eSopenharmony_ci var keyframeResult = {} 41588c88e8eSopenharmony_ci Object.keys(ruleResult).forEach(function (prop) { 41688c88e8eSopenharmony_ci keyframeResult[prop] = ruleResult[prop] 41788c88e8eSopenharmony_ci }) 41888c88e8eSopenharmony_ci keyframeResult['time'] = 100 41988c88e8eSopenharmony_ci jsonStyle['@KEYFRAMES'][keyName].push(keyframeResult) 42088c88e8eSopenharmony_ci } 42188c88e8eSopenharmony_ci var patt = new RegExp(/^(100|[1-9]?\d)%$/) 42288c88e8eSopenharmony_ci if (patt.test(keyframe.values[0])) { 42388c88e8eSopenharmony_ci var keyframeResult = {} 42488c88e8eSopenharmony_ci Object.keys(ruleResult).forEach(function (prop) { 42588c88e8eSopenharmony_ci keyframeResult[prop] = ruleResult[prop] 42688c88e8eSopenharmony_ci }) 42788c88e8eSopenharmony_ci keyframeResult['time'] = keyframe.values[0].replace("%", "") 42888c88e8eSopenharmony_ci jsonStyle['@KEYFRAMES'][keyName].push(keyframeResult) 42988c88e8eSopenharmony_ci } 43088c88e8eSopenharmony_ci }) 43188c88e8eSopenharmony_ci log = log.concat(ruleLog) 43288c88e8eSopenharmony_ci } 43388c88e8eSopenharmony_ci } 43488c88e8eSopenharmony_ci } 43588c88e8eSopenharmony_ci else if (type === 'media') { 43688c88e8eSopenharmony_ci if (!jsonStyle['@MEDIA']) { 43788c88e8eSopenharmony_ci jsonStyle['@MEDIA'] = [] 43888c88e8eSopenharmony_ci } 43988c88e8eSopenharmony_ci var condition = rule.media 44088c88e8eSopenharmony_ci var mediaObj = {} 44188c88e8eSopenharmony_ci mediaObj['condition'] = condition 44288c88e8eSopenharmony_ci 44388c88e8eSopenharmony_ci if (rule.rules && rule.rules.length) { 44488c88e8eSopenharmony_ci rule.rules.forEach(function(rule) { 44588c88e8eSopenharmony_ci ruleResult = {} 44688c88e8eSopenharmony_ci if (rule.type === 'import') { 44788c88e8eSopenharmony_ci parseImport(resourcePath, rule, mediaObj, log) 44888c88e8eSopenharmony_ci } 44988c88e8eSopenharmony_ci if (rule.declarations && rule.declarations.length) { 45088c88e8eSopenharmony_ci flexExpand(rule, ruleLog) 45188c88e8eSopenharmony_ci rule.declarations.forEach(function (declaration) { 45288c88e8eSopenharmony_ci var subType = declaration.type 45388c88e8eSopenharmony_ci var name, value, line, column, subResult, camelCasedName 45488c88e8eSopenharmony_ci 45588c88e8eSopenharmony_ci /* istanbul ignore if */ 45688c88e8eSopenharmony_ci if (subType !== 'declaration') { 45788c88e8eSopenharmony_ci return 45888c88e8eSopenharmony_ci } 45988c88e8eSopenharmony_ci 46088c88e8eSopenharmony_ci name = declaration.property 46188c88e8eSopenharmony_ci value = declaration.value 46288c88e8eSopenharmony_ci 46388c88e8eSopenharmony_ci // validate declarations and collect them to result 46488c88e8eSopenharmony_ci camelCasedName = util.hyphenedToCamelCase(name) 46588c88e8eSopenharmony_ci subResult = validateItem(camelCasedName, value) 46688c88e8eSopenharmony_ci // expand margin、padding、border、borderWidth、borderColor、borderStyle properties 46788c88e8eSopenharmony_ci if (subResult.value && Object.values(util.SPLECIAL_ATTR).indexOf(camelCasedName) !== -1) { 46888c88e8eSopenharmony_ci expand(subResult, camelCasedName, ruleResult) 46988c88e8eSopenharmony_ci } 47088c88e8eSopenharmony_ci 47188c88e8eSopenharmony_ci /* istanbul ignore else */ 47288c88e8eSopenharmony_ci if ((typeof subResult.value === 'number' || typeof subResult.value === 'string') 47388c88e8eSopenharmony_ci && !Object.values(util.SPLECIAL_ATTR).includes(camelCasedName)) { 47488c88e8eSopenharmony_ci ruleResult[camelCasedName] = subResult.value 47588c88e8eSopenharmony_ci } 47688c88e8eSopenharmony_ci if (subResult.log) { 47788c88e8eSopenharmony_ci subResult.log.line = declaration.position.start.line 47888c88e8eSopenharmony_ci subResult.log.column = declaration.position.start.column 47988c88e8eSopenharmony_ci ruleLog.push(subResult.log) 48088c88e8eSopenharmony_ci } 48188c88e8eSopenharmony_ci }) 48288c88e8eSopenharmony_ci rule.selectors.forEach(function (selector) { 48388c88e8eSopenharmony_ci if (selector.match(SELECTOR_MATCHER) || selector.match(DESCENDANT_SELECTOR_MATCHER)) { 48488c88e8eSopenharmony_ci var className = selector 48588c88e8eSopenharmony_ci 48688c88e8eSopenharmony_ci // handle pseudo class 48788c88e8eSopenharmony_ci var pseudoIndex = className.indexOf(':') 48888c88e8eSopenharmony_ci if (pseudoIndex > -1) { 48988c88e8eSopenharmony_ci var pseudoCls = className.slice(pseudoIndex) 49088c88e8eSopenharmony_ci className = className.slice(0, pseudoIndex) 49188c88e8eSopenharmony_ci var pseudoRuleResult = {} 49288c88e8eSopenharmony_ci Object.keys(ruleResult).forEach(function (prop) { 49388c88e8eSopenharmony_ci pseudoRuleResult[prop + pseudoCls] = ruleResult[prop] 49488c88e8eSopenharmony_ci }) 49588c88e8eSopenharmony_ci ruleResult = pseudoRuleResult 49688c88e8eSopenharmony_ci } 49788c88e8eSopenharmony_ci // merge style 49888c88e8eSopenharmony_ci Object.keys(ruleResult).forEach(function (prop) { 49988c88e8eSopenharmony_ci // handle transition 50088c88e8eSopenharmony_ci if (prop.indexOf('transition') === 0 && prop !== 'transition') { 50188c88e8eSopenharmony_ci var realProp = prop.replace('transition', '') 50288c88e8eSopenharmony_ci realProp = realProp[0].toLowerCase() + realProp.slice(1) 50388c88e8eSopenharmony_ci mediaObj['@TRANSITION'] = mediaObj['@TRANSITION'] || {} 50488c88e8eSopenharmony_ci mediaObj['@TRANSITION'][className] = mediaObj['@TRANSITION'][className] || {} 50588c88e8eSopenharmony_ci mediaObj['@TRANSITION'][className][realProp] = ruleResult[prop] 50688c88e8eSopenharmony_ci } 50788c88e8eSopenharmony_ci mediaObj[className] = mediaObj[className] || {} 50888c88e8eSopenharmony_ci mediaObj[className][prop] = ruleResult[prop] 50988c88e8eSopenharmony_ci }) 51088c88e8eSopenharmony_ci } else { 51188c88e8eSopenharmony_ci log.push({ 51288c88e8eSopenharmony_ci line: rule.position.start.line, 51388c88e8eSopenharmony_ci column: rule.position.start.column, 51488c88e8eSopenharmony_ci reason: 'ERROR: The `' + selector + '` selector is not supported.' 51588c88e8eSopenharmony_ci }) 51688c88e8eSopenharmony_ci } 51788c88e8eSopenharmony_ci }) 51888c88e8eSopenharmony_ci log = log.concat(ruleLog) 51988c88e8eSopenharmony_ci } 52088c88e8eSopenharmony_ci }) 52188c88e8eSopenharmony_ci } 52288c88e8eSopenharmony_ci jsonStyle['@MEDIA'].push(mediaObj) 52388c88e8eSopenharmony_ci } 52488c88e8eSopenharmony_ci }) 52588c88e8eSopenharmony_ci } 52688c88e8eSopenharmony_ci 52788c88e8eSopenharmony_ci done(err, {jsonStyle: jsonStyle, log: log}) 52888c88e8eSopenharmony_ci} 52988c88e8eSopenharmony_ci 53088c88e8eSopenharmony_cifunction parseImport(resourcePath, rule, jsonStyle, log) { 53188c88e8eSopenharmony_ci if(!resourcePath) { 53288c88e8eSopenharmony_ci return 53388c88e8eSopenharmony_ci } 53488c88e8eSopenharmony_ci const resourcePath_ = resourcePath 53588c88e8eSopenharmony_ci let importString = rule.import 53688c88e8eSopenharmony_ci let importPath 53788c88e8eSopenharmony_ci let mediaString = '' 53888c88e8eSopenharmony_ci let source = '' 53988c88e8eSopenharmony_ci if (importString.match(IMPORT_MATCHER)) { 54088c88e8eSopenharmony_ci let filePath = importString.match(/['"]([^()]+?)['"]/) 54188c88e8eSopenharmony_ci importPath = filePath[1] 54288c88e8eSopenharmony_ci mediaString = importString.replace(importPath, '').replace(/['"]/g, '') 54388c88e8eSopenharmony_ci } 54488c88e8eSopenharmony_ci if(/^(\.)|(\.\.)\//.test(importPath)) { 54588c88e8eSopenharmony_ci resourcePath = resourcePath.substring(0, resourcePath.lastIndexOf(path.sep) + 1); 54688c88e8eSopenharmony_ci importPath = path.resolve(resourcePath, importPath) 54788c88e8eSopenharmony_ci } 54888c88e8eSopenharmony_ci if (!fs.existsSync(importPath)) { 54988c88e8eSopenharmony_ci const fileSearch = findFile(importPath); 55088c88e8eSopenharmony_ci if (fileSearch.result == true) { 55188c88e8eSopenharmony_ci importPath = fileSearch.filePath; 55288c88e8eSopenharmony_ci } else { 55388c88e8eSopenharmony_ci writeErrorOption(); 55488c88e8eSopenharmony_ci log.push({ 55588c88e8eSopenharmony_ci line: rule.position.start.line, 55688c88e8eSopenharmony_ci column: rule.position.start.column, 55788c88e8eSopenharmony_ci reason: 'ERROR: no such file or directory, open ' + importPath 55888c88e8eSopenharmony_ci }); 55988c88e8eSopenharmony_ci return; 56088c88e8eSopenharmony_ci } 56188c88e8eSopenharmony_ci } 56288c88e8eSopenharmony_ci source = fs.readFileSync(importPath).toString(); 56388c88e8eSopenharmony_ci addPreviewCSS(importPath, resourcePath_); 56488c88e8eSopenharmony_ci if (mediaString.length !== 0) { 56588c88e8eSopenharmony_ci source = '@media ' + mediaString + '{\n' + source + '\n}' 56688c88e8eSopenharmony_ci } 56788c88e8eSopenharmony_ci parse(source, (err, obj) => { 56888c88e8eSopenharmony_ci if (err) { 56988c88e8eSopenharmony_ci throw(err) 57088c88e8eSopenharmony_ci } else { 57188c88e8eSopenharmony_ci jsonStyle = Object.assign(jsonStyle, obj.jsonStyle) 57288c88e8eSopenharmony_ci } 57388c88e8eSopenharmony_ci }, importPath) 57488c88e8eSopenharmony_ci} 57588c88e8eSopenharmony_ci 57688c88e8eSopenharmony_cifunction addPreviewCSS(importPath, resourcePath) { 57788c88e8eSopenharmony_ci importPath = path.join(importPath); 57888c88e8eSopenharmony_ci resourcePath = path.join(resourcePath); 57988c88e8eSopenharmony_ci if (fs.existsSync(process.env.watchCSSFiles)) { 58088c88e8eSopenharmony_ci const content = JSON.parse(fs.readFileSync(process.env.watchCSSFiles)); 58188c88e8eSopenharmony_ci if (content['entry'] && content['entry'][resourcePath]) { 58288c88e8eSopenharmony_ci content[importPath] = content[importPath] || []; 58388c88e8eSopenharmony_ci content[importPath].push(resourcePath); 58488c88e8eSopenharmony_ci content[importPath] = lodash.uniqWith(content[importPath], lodash.isEqual); 58588c88e8eSopenharmony_ci } else if (content[resourcePath]) { 58688c88e8eSopenharmony_ci content[importPath] = content[importPath] || []; 58788c88e8eSopenharmony_ci content[importPath].push(...content[resourcePath]); 58888c88e8eSopenharmony_ci content[importPath] = lodash.uniqWith(content[importPath], lodash.isEqual); 58988c88e8eSopenharmony_ci } 59088c88e8eSopenharmony_ci content["atime"] = content["atime"] || {}; 59188c88e8eSopenharmony_ci content["atime"][importPath] = fs.statSync(importPath).atime.toString(); 59288c88e8eSopenharmony_ci fs.writeFileSync(process.env.watchCSSFiles, JSON.stringify(content, null, 2)); 59388c88e8eSopenharmony_ci } 59488c88e8eSopenharmony_ci} 59588c88e8eSopenharmony_ci 59688c88e8eSopenharmony_cifunction findFile(importPath) { 59788c88e8eSopenharmony_ci const resultObject = { 59888c88e8eSopenharmony_ci result: false 59988c88e8eSopenharmony_ci }; 60088c88e8eSopenharmony_ci if (!importPath || !process.env.resolveModules) { 60188c88e8eSopenharmony_ci return resultObject; 60288c88e8eSopenharmony_ci } 60388c88e8eSopenharmony_ci try { 60488c88e8eSopenharmony_ci const modules = JSON.parse(process.env.resolveModules); 60588c88e8eSopenharmony_ci modules.forEach(item => { 60688c88e8eSopenharmony_ci if (fs.existsSync(item)) { 60788c88e8eSopenharmony_ci if (fs.existsSync(path.join(item, importPath))) { 60888c88e8eSopenharmony_ci resultObject.result = true; 60988c88e8eSopenharmony_ci resultObject.filePath = path.join(item, importPath); 61088c88e8eSopenharmony_ci return resultObject; 61188c88e8eSopenharmony_ci } 61288c88e8eSopenharmony_ci } else { 61388c88e8eSopenharmony_ci const resolveItem = path.resolve(__dirname, item); 61488c88e8eSopenharmony_ci if (fs.existsSync(resolveItem)) { 61588c88e8eSopenharmony_ci resultObject.result = true; 61688c88e8eSopenharmony_ci resultObject.filePath = path.join(resolveItem, importPath); 61788c88e8eSopenharmony_ci return resultObject; 61888c88e8eSopenharmony_ci } 61988c88e8eSopenharmony_ci } 62088c88e8eSopenharmony_ci }); 62188c88e8eSopenharmony_ci } catch (error) { 62288c88e8eSopenharmony_ci resultObject.result = false; 62388c88e8eSopenharmony_ci } 62488c88e8eSopenharmony_ci return resultObject; 62588c88e8eSopenharmony_ci} 62688c88e8eSopenharmony_ci 62788c88e8eSopenharmony_cifunction writeErrorOption() { 62888c88e8eSopenharmony_ci if (fs.existsSync(process.env.watchCSSFiles)) { 62988c88e8eSopenharmony_ci const content = JSON.parse(fs.readFileSync(process.env.watchCSSFiles)); 63088c88e8eSopenharmony_ci content['clear'] = true; 63188c88e8eSopenharmony_ci fs.writeFileSync(process.env.watchCSSFiles, JSON.stringify(content, null, 2)); 63288c88e8eSopenharmony_ci } 63388c88e8eSopenharmony_ci} 63488c88e8eSopenharmony_ci 63588c88e8eSopenharmony_ci/** 63688c88e8eSopenharmony_ci * Validate a JSON Object and log errors & warnings 63788c88e8eSopenharmony_ci * 63888c88e8eSopenharmony_ci * @param {object} json 63988c88e8eSopenharmony_ci * @param {function} done which will be called with 64088c88e8eSopenharmony_ci * - err:Error 64188c88e8eSopenharmony_ci * - data.jsonStyle{}: `classname.propname.value`-like object 64288c88e8eSopenharmony_ci * - data.log[{reason}] 64388c88e8eSopenharmony_ci */ 64488c88e8eSopenharmony_cifunction validate(json, done) { 64588c88e8eSopenharmony_ci var log = [] 64688c88e8eSopenharmony_ci var err 64788c88e8eSopenharmony_ci 64888c88e8eSopenharmony_ci try { 64988c88e8eSopenharmony_ci json = JSON.parse(JSON.stringify(json)) 65088c88e8eSopenharmony_ci } 65188c88e8eSopenharmony_ci catch (e) { 65288c88e8eSopenharmony_ci err = e 65388c88e8eSopenharmony_ci json = {} 65488c88e8eSopenharmony_ci } 65588c88e8eSopenharmony_ci 65688c88e8eSopenharmony_ci Object.keys(json).forEach(function (selector) { 65788c88e8eSopenharmony_ci var declarations = json[selector] 65888c88e8eSopenharmony_ci 65988c88e8eSopenharmony_ci Object.keys(declarations).forEach(function (name) { 66088c88e8eSopenharmony_ci var value = declarations[name] 66188c88e8eSopenharmony_ci var result = validateItem(name, value) 66288c88e8eSopenharmony_ci 66388c88e8eSopenharmony_ci if (typeof result.value === 'number' || typeof result.value === 'string') { 66488c88e8eSopenharmony_ci declarations[name] = result.value 66588c88e8eSopenharmony_ci } 66688c88e8eSopenharmony_ci else { 66788c88e8eSopenharmony_ci delete declarations[name] 66888c88e8eSopenharmony_ci } 66988c88e8eSopenharmony_ci 67088c88e8eSopenharmony_ci if (result.log) { 67188c88e8eSopenharmony_ci log.push(result.log) 67288c88e8eSopenharmony_ci } 67388c88e8eSopenharmony_ci }) 67488c88e8eSopenharmony_ci }) 67588c88e8eSopenharmony_ci 67688c88e8eSopenharmony_ci done(err, { 67788c88e8eSopenharmony_ci jsonStyle: json, 67888c88e8eSopenharmony_ci log: log 67988c88e8eSopenharmony_ci }) 68088c88e8eSopenharmony_ci} 68188c88e8eSopenharmony_ci 68288c88e8eSopenharmony_cimodule.exports = { 68388c88e8eSopenharmony_ci parse: parse, 68488c88e8eSopenharmony_ci validate: validate, 68588c88e8eSopenharmony_ci validateItem: validateItem, 68688c88e8eSopenharmony_ci util: util, 68788c88e8eSopenharmony_ci expand: expand, 68888c88e8eSopenharmony_ci getUnit: getUnit, 68988c88e8eSopenharmony_ci} 69088c88e8eSopenharmony_ci 691