diff --git a/ace-loader/main.product.js b/ace-loader/main.product.js index 77cd134..0a3bd3c 100644 --- a/ace-loader/main.product.js +++ b/ace-loader/main.product.js @@ -74,7 +74,19 @@ function addPageEntryObj(manifest, projectPath) { throw Error('ERROR: missing pages').message; } pages.forEach((element) => { - entryObj['./' + element] = projectPath + path.sep + element + '.hml?entry' + const sourcePath = element; + const hmlPath = path.join(projectPath, sourcePath + '.hml'); + const aceSuperVisualPath = process.env.aceSuperVisualPath || ''; + const visualPath = path.join(aceSuperVisualPath, sourcePath + '.visual'); + const isHml = fs.existsSync(hmlPath); + const isVisual = fs.existsSync(visualPath); + if (isHml && isVisual) { + throw Error(red + 'ERROR: ' + sourcePath + ' cannot both have hml && visual').message; + } else if (isHml) { + entryObj['./' + element] = hmlPath + '?entry'; + } else if (isVisual) { + entryObj['./' + element] = visualPath + '?entry'; + } }) return entryObj; } diff --git a/ace-loader/plugin/codegen/card-index.js b/ace-loader/plugin/codegen/card-index.js new file mode 100644 index 0000000..0d09d15 --- /dev/null +++ b/ace-loader/plugin/codegen/card-index.js @@ -0,0 +1,1268 @@ +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ 784: +/***/ ((__unused_webpack_module, exports) => { + + +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.errorMap = void 0; +exports.errorMap = new Map([ + ["fileError", "Visual file is damaged"], + ["versionError", "Version number of visual file does not match"], + ["modelError", "Visual model in visual file is damaged"], + ["codegenError", "Codegen hml and css failed"], +]); + + +/***/ }), + +/***/ 117: +/***/ ((__unused_webpack_module, exports) => { + + +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.ASTNode = void 0; +class ASTNode { + accept(v) { + return v.visit(this); + } +} +exports.ASTNode = ASTNode; + + +/***/ }), + +/***/ 862: +/***/ ((__unused_webpack_module, exports) => { + + +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.Cache = void 0; +// There is no way pass value by reference with JS and TS, but object +// This Cache is used to store output code temporarily +class Cache { + /** + * @description: constructor for Cache + * @param INDENT the IDENT string you want to use, such as 4 spaces + */ + constructor(INDENT) { + this.value = ""; + this.indent = 0; + this.flag = true; + this.INDENT = INDENT; + } + /** + * @description: when flag is true, there should be some indents + * @return void + */ + indentOn() { + this.flag = true; + } + /** + * @description: when flag is false, there should be no indents + * @return void + */ + indentOff() { + this.flag = false; + } + /** + * @description: increase indent + * @return void + */ + incIndent() { + this.indent++; + } + /** + * @description: decrease indent + * @return void + */ + decIndent() { + this.indent--; + } + /** + * @description: check whether indent is LT 0 + * @return boolean value representing whether indent is LT 0 + */ + checkIndent() { + return this.indent < 0; + } + /** + * @description: get indent + * @return indents + */ + getIndents() { + if (this.flag) { + let indents = ""; + for (let i = 0; i < this.indent; i++) { + indents += this.INDENT; + } + return indents; + } + else { + return ""; + } + } + /** + * @description: concat indents and HML/CSS code + * @param strings means HML/CSS code + * @return HML/CSS code after indents are set + */ + concat(...strings) { + this.value += this.getIndents(); + this.value = this.value.concat(...strings); + return String(this.value); + } + /** + * @description: concat indents and HML/CSS code + * @return HML/CSS code + */ + toString() { + return this.value; + } +} +exports.Cache = Cache; + + +/***/ }), + +/***/ 243: +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + + +/* + * @Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.Style = exports.Tag = void 0; +const ASTNode_1 = __webpack_require__(117); +class Tag extends ASTNode_1.ASTNode { + /** + * @description: constructor for Tag + * @param tagName is name of component + * @param attributes is attributes of component + * @param content is child elements or innerHtml of component + */ + constructor(tagName, attributes, content) { + super(); + this.tagName = tagName; + this.attributes = attributes; + this.content = content; + } +} +exports.Tag = Tag; +class Style extends ASTNode_1.ASTNode { + /** + * @description: constructor for Style + * @param kind distinguishes id and class + * @param name is name of id or class + * @param content is style name and value of component + */ + constructor(kind, name, content) { + super(); + this.kind = kind; + this.name = name; + this.content = content; + } +} +exports.Style = Style; + + +/***/ }), + +/***/ 573: +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + + +/* + * @Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.ASTNodeGenerator = void 0; +const AST_1 = __webpack_require__(243); +const Token_1 = __webpack_require__(334); +class ASTNodeGenerator { + /** + * @description: constructor for BridgeVisitor + * @param reference is cache for Harmony FA code + */ + constructor(reference) { + this.cache = reference; + } + /** + * @description: visitor mode dispatcher + * @param t Node in AST + */ + visit(t) { + if (t instanceof AST_1.Tag) { + this.genTag(t); + } + else if (t instanceof AST_1.Style) { + this.genStyle(t); + } + } + /** + * @description: code generator + * @param ref is cache for code + * @return ast node generator + */ + static getMethodGen(ref) { + if (ASTNodeGenerator.instance === undefined) { + ASTNodeGenerator.instance = new ASTNodeGenerator(ref); + } + else { + ASTNodeGenerator.instance.setCache(ref); + } + return ASTNodeGenerator.instance; + } + /** + * @description: cache for code + * @param ref is cache for code + * @return void + */ + setCache(ref) { + this.cache = ref; + } + /** + * @description: parse Tag in AST and generate code for Tag in cache + * @param t Tag in AST + * @return void + */ + genTag(t) { + this.cache.concat(Token_1.TokenClass.TAG_START, t.tagName); + this.cache.indentOff(); + t.attributes.forEach((value, key) => { + let valueBK = ""; + for (const char of value) { + valueBK += (char === "\"" ? """ : (char === "\n" ? " " : char)); + } + this.cache.concat(Token_1.TokenClass.SPACE, key, Token_1.TokenClass.ASSIGN, Token_1.TokenClass.LQUOTE, valueBK, Token_1.TokenClass.RQUOTE); + }); + if (t.content === null) { + this.cache.concat(Token_1.TokenClass.EMPTY_TAG_END); + } + else { + this.cache.concat(Token_1.TokenClass.TAG_END); + if (typeof t.content === "string") { + let contentBK = ""; + for (const char of t.content) { + contentBK += (char === "<" ? "<" : char); + } + this.cache.concat(contentBK); + } + else if (t.content.length !== 0) { + this.cache.concat(Token_1.TokenClass.NEW_LINE); + this.cache.indentOn(); + this.cache.incIndent(); + t.content.forEach((tag) => { + tag.accept(this); + this.cache.indentOff(); + this.cache.concat(Token_1.TokenClass.NEW_LINE); + this.cache.indentOn(); + }); + this.cache.decIndent(); + this.cache.indentOn(); + } + this.cache.concat(Token_1.TokenClass.END_TAG_START, t.tagName, Token_1.TokenClass.TAG_END); + } + } + /** + * @description: parse Style in AST and generate code for Style in cache + * @param s Style in AST + * @return void + */ + genStyle(s) { + if (s.kind === "IDStyle") { + this.cache.concat(Token_1.TokenClass.ID_STYLE_START); + this.cache.indentOff(); + } + this.cache.concat(s.name, Token_1.TokenClass.SPACE, Token_1.TokenClass.LBRA, Token_1.TokenClass.NEW_LINE); + this.cache.indentOn(); + this.cache.incIndent(); + s.content.forEach((value, key) => { + this.cache.concat(key, Token_1.TokenClass.COLON, Token_1.TokenClass.SPACE, value, Token_1.TokenClass.SEMICOLON, Token_1.TokenClass.NEW_LINE); + }); + this.cache.decIndent(); + this.cache.concat(Token_1.TokenClass.RBRA, Token_1.TokenClass.NEW_LINE, Token_1.TokenClass.NEW_LINE); + } +} +exports.ASTNodeGenerator = ASTNodeGenerator; +ASTNodeGenerator.instance = undefined; + + +/***/ }), + +/***/ 844: +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + + +/* + * @Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.CSSBridge = exports.HMLBridge = void 0; +const AST_1 = __webpack_require__(243); +const PropertySet_1 = __webpack_require__(571); +class HMLBridge { + constructor() { + this.errors = 0; + } + /** + * @description: generate error message + * @param msg is error message to show up in console + */ + error(msg) { + console.error("Code generating error: " + msg); + this.errors += 1; + } + /** + * @description: get error number + * @return error number + */ + getErrorCount() { + return this.errors; + } + /** + * @description: visitor guidance method for contents, + * sort out incoming type and guide to matching code generator + * @param visualModel is a object with Model or Container or CharUI primitive types to be generated + * @return a code tree representing input object + */ + visit(visualModel) { + const attributes = new Map([["id", visualModel.id]]); + let content = ""; + for (let [key, val] of visualModel.property) { + if (PropertySet_1.isAttribute(key)) { + if (typeof val !== "string") { + val = JSON.stringify(val); + } + attributes.set(key, val); + } + else if (PropertySet_1.isContent(key)) { + content = val; + } + } + if (visualModel.children.length > 0) { + content = []; + for (const visualChild of visualModel.children) { + content.push(visualChild.accept(this)); + } + } + return new AST_1.Tag(visualModel.type, attributes, content); + } +} +exports.HMLBridge = HMLBridge; +class CSSBridge { + constructor() { + this.errors = 0; + this.styles = []; + } + /** + * @description: generate error message + * @param msg is error message to show up in console + */ + error(msg) { + console.error("Code generating error: " + msg); + this.errors += 1; + } + /** + * @description: get error number + * @return error number + */ + getErrorCount() { + return this.errors; + } + /** + * @description: code generator for ID Style, which is CSS type in AST in IR + * @param visualModel is a object with CSS type to be generated + * @return a code tree representing Harmony FA CSS code + */ + genIDStyle(visualModel) { + const styles = new Map(); + for (const prop of PropertySet_1.styleSet) { + if (visualModel.property.has(prop) && PropertySet_1.isStyle(prop)) { + styles.set(prop, visualModel.property.get(prop)); + } + } + if (styles.size > 0) { + this.styles.push(new AST_1.Style("IDStyle", visualModel.id, styles)); + } + for (const visualChild of visualModel.children) { + visualChild.accept(this); + } + } + /** + * @description: visitor guidance method for contents, + * sort out incoming type and guide to matching code generator + * @param obj is a object with Model or Container or CharUI primitive types to be generated + * @return a code tree representing input object + */ + visit(obj) { + this.genIDStyle(obj); + return this.styles; + } +} +exports.CSSBridge = CSSBridge; + + +/***/ }), + +/***/ 55: +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + + +/* + * @Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.genFACSS = exports.genFAHML = void 0; +const ASTNodeVisitor_1 = __webpack_require__(573); +const Cache_1 = __webpack_require__(862); +/** + * @description: generate HML + * @param t is Tag in AST + * @return HML code + */ +function genFAHML(t) { + const generator = ASTNodeVisitor_1.ASTNodeGenerator.getMethodGen(new Cache_1.Cache(" ")); + t.accept(generator); + return generator.cache.toString(); +} +exports.genFAHML = genFAHML; +/** + * @description: generate CSS + * @param t is Style in AST + * @return CSS code + */ +function genFACSS(t) { + const generator = ASTNodeVisitor_1.ASTNodeGenerator.getMethodGen(new Cache_1.Cache(" ")); + t.forEach((value) => { + value.accept(generator); + }); + return generator.cache.toString(); +} +exports.genFACSS = genFACSS; + + +/***/ }), + +/***/ 571: +/***/ ((__unused_webpack_module, exports) => { + + +/* + * @Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isEvent = exports.isData = exports.isUnknown = exports.isContent = exports.isAttribute = exports.isStyle = exports.styleSet = void 0; +// Unlike the property list in ComponentList, this one is only used to tell a property is a style or an attribute +const SizeStyle = ["width", "height", "min-width", "min-height", "max-width", "max-height"]; +const FlexStyle = ["flex", "flex-grow", "flex-shrink", "flex-basis"]; +const BackgroundImageStyle = ["background", "background-image", "background-size", "background-position", "background-repeat"]; +const BackgroundStyle = ["background-color", ...BackgroundImageStyle]; +const PositionStyle = ["position", "display", "top", "right", "bottom", "left"]; +const PaddingStyle = ["padding", "padding-top", "padding-right", "padding-bottom", "padding-left", "padding-start", "padding-end"]; +const MarginStyle = ["margin", "margin-top", "margin-right", "margin-bottom", "margin-left", "margin-start", "margin-end"]; +const BorderStyle = ["border-width", "border-style", "border-color", "border-radius", "border-top-width", + "border-top-style", "border-top-color", "border-top-left-radius", "border-right-width", + "border-right-style", "border-right-color", "border-top-right-radius", "border-bottom-width", "border-bottom-style", + "border-bottom-color", "border-bottom-right-radius", "border-left-width", "border-left-style", "border-left-color", + "border-bottom-left-radius"]; +const gridStyle = ["grid-template-columns", "grid-template-rows", "grid-row-start", "grid-row-end", + "grid-column-start", "grid-column-end", "grid-gap", "grid-columns-gap", "grid-rows-gap"]; +const DisplayStyle = ["display", "opacity", "visibility"]; +const AtomicLayoutStyle = ["display-index", "flex-weight", "aspect-ratio"]; +const commonStyle = [...SizeStyle, ...PaddingStyle, ...MarginStyle, ...BorderStyle, ...BackgroundStyle, ...DisplayStyle, + ...FlexStyle, ...PositionStyle, ...AtomicLayoutStyle]; +const FlexSizeStyle = ["flex-wrap", "justify-content", "align-items", "align-content"]; +const FontStyle = ["font-size", "font-family", "font-style", "font-weight"]; +const divStyle = ["flex-direction", ...FlexSizeStyle]; +const textStyle = ["text-align", "line-height", "text-decoration", "letter-spacing", "max-lines", "text-overflow", + "allow-scale", "min-font-size", "max-font-size", "font-size-step", "prefer-font-sizes", ...FontStyle]; +const imageStyle = ["object-fit", "match-text-direction", "fit-original-size"]; +const dividerStyle = ["stroke-width", "line-cap"]; +const spanStyle = ["allow-scale", "text-decoration", "color", ...FontStyle]; +const buttonStyle = ["text-color", "allow-scale", "icon-width", "icon-height", "radius", ...FontStyle]; +const switchStyle = ["texton-color", "textoff-color", "text-padding", "allow-scale", ...FontStyle]; +const inputStyle = ["font-size", "font-family", "font-weight", "color", "placeholder-color", "allow-scale"]; +const refreshStyle = ["progress-color"]; +const chartStyle = ["stroke-width", "radius", "start-angle", "total-angle", "center-x", "center-y", "colors", "weights"]; +const swiperStyle = ["indicator-color", "indicator-selected-color", "indicator-size", "indicator-top", "indicator-right", + "indicator-bottom", "indicator-left"]; +const pickerStyle = ["column-height", "text-color", "allow-scale", "letter-spacing", "text-decoration", "line-height", "opacity", + ...FontStyle]; +const sliderStyle = ["color", "selected-color", "block-color"]; +const listStyle = ["flex-direction", "columns", "item-extent", "fade-color"]; +const listItemStyle = ["column-span"]; +const progressStyle = ["color", "stroke-width", "background-color", "secondary-color", "scale-width", "scale-number", + "start-angle", "total-angle", "center-x", "center-y", "radius"]; +exports.styleSet = new Set([ + ...commonStyle, + ...divStyle, + ...textStyle, + ...imageStyle, + ...spanStyle, + ...inputStyle, + ...buttonStyle, + ...switchStyle, + ...refreshStyle, + ...dividerStyle, + ...chartStyle, + ...pickerStyle, + ...sliderStyle, + ...swiperStyle, + ...listStyle, + ...listItemStyle, + ...progressStyle, + ...gridStyle, +]); +const commonAttribute = ["id", "ref", "disabled", "focusable", "data", "if", "for"]; +const imageAttribute = ["src", "alt"]; +const buttonAttribute = ["type", "value", "icon", "waiting"]; +const switchAttribute = ["checked", "showtext", "texton", "textoff"]; +const inputAttribute = ["type", "checked", "name", "value", "placeholder", "maxlength", "enterkeytype", "headericon"]; +const refreshAttribute = ["offset", "refreshing", "type", "lasttime", "friction"]; +const optionAttribute = ["value"]; +const chartAttribute = ["percent", "datasets", "options"]; +const swiperAttribute = ["index", "autoplay", "interval", "indicator", "digital", "indicatordisabled", "loop", "duration", "vertical"]; +const pickerAttribute = ["range", "selected", "start", "end", "lunar", "lunarSwitch", "columns", "hours", "containSecond"]; +const sliderAttribute = ["min", "max", "step", "showtips", "showsteps", "mode"]; +const menuAttribute = ["target", "title"]; +const clockAttribute = ["clockconfig", "showdigit", "hourswest"]; +const dividerAttribute = ["vertical"]; +const listAttribute = ["scrollpage", "cachedcount", "scrollbar", "scrolleffect", "shapemode", "indexer", "itemscale", + "itemcenter", "updateeffect", "scrollvibrate", "initialindex", "initialoffset"]; +const listItemAttribute = ["for", "type", "primary", "section", "sticky", "stickyradius", "clickeffect"]; +const progressAttribute = ["type", "percent", "secondarypercent", "clockwise"]; +const commonEvent = ["ontouchstart", "ontouchmove", "ontouchcancel", "ontouchend", "onclick", "onlongpress", "onfocus", + "onblur", "onkey", "onswipe"]; +const imageEvent = ["oncomplete", "onerror"]; +const selectEvent = ["onchange"]; +const inputEvent = ["onchange", "onenterkeyclick"]; +const refreshEvent = ["onrefresh", "onpulldown"]; +const listEvent = ["onindexerchange", "onscroll", "onscrollbottom", "onscrolltop", "onscrollend", + "onscrolltouchup", "onrequestitem"]; +const listItemEvent = ["onsticky"]; +const swiperEvent = ["change", "onrotation"]; +const menuEvent = ["onselected", "oncancel"]; +const pickerEvent = ["oncolumnchange"]; +const dataSet = new Set([ + ...commonAttribute, + ...imageAttribute, + ...buttonAttribute, + ...refreshAttribute, + ...inputAttribute, + ...switchAttribute, + ...optionAttribute, + ...chartAttribute, + ...pickerAttribute, + ...sliderAttribute, + ...dividerAttribute, + ...listAttribute, + ...listItemAttribute, + ...swiperAttribute, + ...progressAttribute, + ...menuAttribute, + ...clockAttribute, +]); +const eventSet = new Set([ + ...commonEvent, + ...imageEvent, + ...inputEvent, + ...selectEvent, + ...refreshEvent, + ...swiperEvent, + ...listEvent, + ...listItemEvent, + ...menuEvent, + ...pickerEvent, +]); +function isStyle(s) { + return exports.styleSet.has(s); +} +exports.isStyle = isStyle; +function isAttribute(s) { + return dataSet.has(s) || eventSet.has(s); +} +exports.isAttribute = isAttribute; +function isContent(s) { + return s === "content"; +} +exports.isContent = isContent; +function isUnknown(s) { + return !isStyle(s) && !isAttribute(s); +} +exports.isUnknown = isUnknown; +function isData(s) { + return dataSet.has(s); +} +exports.isData = isData; +function isEvent(s) { + return eventSet.has(s); +} +exports.isEvent = isEvent; + + +/***/ }), + +/***/ 334: +/***/ ((__unused_webpack_module, exports) => { + + +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.TokenClass = void 0; +var TokenClass; +(function (TokenClass) { + TokenClass[TokenClass["IDENTIFIER"] = 0] = "IDENTIFIER"; + // literals + TokenClass[TokenClass["STRING_LITERAL"] = 1] = "STRING_LITERAL"; + TokenClass[TokenClass["NUMBER"] = 2] = "NUMBER"; + TokenClass[TokenClass["CHARACTER"] = 3] = "CHARACTER"; + // special tokens + TokenClass[TokenClass["EOF"] = 4] = "EOF"; + TokenClass[TokenClass["INVALID"] = 5] = "INVALID"; + TokenClass["EMPTY_DATA"] = "empty"; + TokenClass["ASSIGN"] = "="; + // Escape character + TokenClass["NEW_LINE"] = "\n"; + TokenClass["CARRIAGE_RETURN"] = "\r"; + TokenClass["INDENT"] = " "; + // delimiters + TokenClass["SPACE"] = " "; + TokenClass["LQUOTE"] = "\""; + TokenClass["RQUOTE"] = "\""; + TokenClass["TAG_START"] = "<"; + TokenClass["TAG_END"] = ">"; + TokenClass["EMPTY_TAG_END"] = "/>"; + TokenClass["END_TAG_START"] = " { + + +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021:. All rights reserved. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.Enum = exports.Characteristic = exports.Event = exports.Service = exports.Device = void 0; +class Device { + constructor() { + this.prodId = ""; + this.udid = ""; + this.deviceModel = ""; + this.deviceName = ""; + this.deviceTypeId = ""; + this.deviceTypeName = ""; + this.manufacturerId = ""; + this.manufacturerName = ""; + this.services = []; + } +} +exports.Device = Device; +class Service { + constructor() { + this.serviceId = ""; + this.serviceType = ""; + this.characteristics = []; + } +} +exports.Service = Service; +class Event { + constructor() { + this.eventId = ""; + this.eventType = ""; + this.characteristics = []; + } +} +exports.Event = Event; +class Characteristic { + constructor() { + this.name = ""; + this.type = ""; + this.format = "int"; + this.method = []; + } +} +exports.Characteristic = Characteristic; +class Enum { + constructor() { + this.enumVal = ""; + this.description = {}; + } +} +exports.Enum = Enum; + + +/***/ }), + +/***/ 207: +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.formManager = void 0; +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + */ +const Instance_1 = __webpack_require__(891); +const FormModel_1 = __webpack_require__(945); +exports.formManager = { + /** + * + * remove a key-value in data node + * @param key the entry to remove + */ + removeData(key) { + this.getFormModel().data.delete(key); + }, + /** + * + * add a key-value + * @param data + */ + addData(data) { + if (data instanceof Map) { + data.forEach((value, key) => { + this.getFormModel().data.set(key, value); + }); + } + else { + Object.keys(data).forEach(key => { + this.getFormModel().data.set(key, data[key]); + }); + } + }, + /** + * update all the data + * @param data + */ + updateAllData(data) { + this.getFormModel().data.clear(); + this.addData(data); + }, + /** + * + * add an action in action node + * @param actionName + * @param actionType + * @param params + * @param abilityName optional param + */ + addAction(actionName, actionType, params, abilityName) { + const paramMap = new Map(); + Object.keys(params).forEach(key => { + paramMap.set(key, params[key]); + }); + this.getFormModel().actions.set(actionName, new FormModel_1.FormAction(actionType, paramMap, abilityName)); + }, + /** + * update all the actions + * @param map: the new actions to be updated + */ + updateAllActions(map) { + this.getFormModel().actions.clear(); + map.forEach((value, key) => { + const params = this.objectToMap(value.params); + const action = new FormModel_1.FormAction(value.actionType, params, value.abilityName); + this.getFormModel().actions.set(key, action); + }); + }, + /** + * + * remove an action + * @param actionName + */ + removeAction(actionName) { + this.getFormModel().actions.delete(actionName); + }, + /** + * + * add params in an action + * @param actionName + * @param params + */ + addActionParams(actionName, params) { + var _a, _b; + const action = (_a = this.getFormModel().actions.get(actionName)) !== null && _a !== void 0 ? _a : new FormModel_1.FormAction(actionName); + const actionParams = (_b = action.params) !== null && _b !== void 0 ? _b : new Map(); + Object.keys(params).forEach(key => { + actionParams.set(key, params[key]); + }); + action.params = actionParams; + }, + /** + * + * remove params in an action + * @param actionName + * @param paramKey entry to remove + */ + removeActionParam(actionName, paramKey) { + var _a; + const action = this.getFormModel().actions.get(actionName); + (_a = action === null || action === void 0 ? void 0 : action.params) === null || _a === void 0 ? void 0 : _a.delete(paramKey); + }, + /** + * get the whole node + */ + getFormModel() { + return Instance_1.getInstance().formData; + }, + /** + * codegen formModel to json + */ + codegenToJson: function () { + const model = Instance_1.getInstance().formData; + const data = this.mapToObject(model.data); + const actions = this.mapToObject(model.actions); + const retObj = { actions: {}, data: {} }; + retObj.data = data; + retObj.actions = actions; + model.actions.forEach((value, key) => { + retObj.actions[key].params = this.mapToObject(value.params); + }); + return JSON.stringify(retObj, null, 4); + }, + /** + * clear all datas and actionsin model + */ + clear() { + Instance_1.getInstance.formData.data.clear(); + Instance_1.getInstance.formData.actions.clear(); + }, + /** + * + * convert a map to an object + * @param sourceMap + */ + mapToObject(sourceMap) { + if (sourceMap === undefined) { + return {}; + } + return Array.from(sourceMap.entries()).reduce((main, [key, value]) => (Object.assign(Object.assign({}, main), { [key]: value })), {}); + }, + /** + * convert an object to map + */ + objectToMap(sourceObj) { + const map = new Map(); + Object.keys(sourceObj).forEach((value, index) => { + map.set(value, sourceObj[value]); + }); + return map; + }, +}; + + +/***/ }), + +/***/ 945: +/***/ ((__unused_webpack_module, exports) => { + + +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.FormAction = exports.FormModel = void 0; +class FormModel { + constructor() { + this.data = new Map(); + this.actions = new Map(); + } +} +exports.FormModel = FormModel; +class FormAction { + constructor(action, params, abilityName) { + this.action = action; + this.abilityName = abilityName; + this.params = params; + } +} +exports.FormAction = FormAction; + + +/***/ }), + +/***/ 964: +/***/ ((__unused_webpack_module, exports) => { + + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isBindingEvent = exports.CombinedSelfType = void 0; +var CombinedSelfType; +(function (CombinedSelfType) { + CombinedSelfType[CombinedSelfType["None"] = 0] = "None"; + CombinedSelfType[CombinedSelfType["Root"] = 1] = "Root"; + CombinedSelfType[CombinedSelfType["Container"] = 2] = "Container"; + CombinedSelfType[CombinedSelfType["Active"] = 4] = "Active"; +})(CombinedSelfType = exports.CombinedSelfType || (exports.CombinedSelfType = {})); +function isBindingEvent(obj) { + return obj !== undefined && typeof obj.type === "string" && typeof obj.physicalModelName === "string"; +} +exports.isBindingEvent = isBindingEvent; + + +/***/ }), + +/***/ 891: +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.addPhysicalModel = exports.getPhysicalModel = exports.setInstance = exports.getInstance = void 0; +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + */ +const PhysicalModel_1 = __webpack_require__(234); +const VisualModel_1 = __webpack_require__(933); +const FormModel_1 = __webpack_require__(945); +const instance = { + document: { VisualVersion: "12", type: "FA" }, + visualModel: new VisualModel_1.VisualModel({ type: "div", id: "wrapper" }), + harmonyConnectDevice: new PhysicalModel_1.Device(), + formData: new FormModel_1.FormModel(), +}; +/** + * instance is unique during the entire web page lifecycle + */ +function getInstance() { + return instance; +} +exports.getInstance = getInstance; +/** + * replace instance + * @param ins + */ +function setInstance(ins) { + for (const key in instance) { + if (Object.prototype.hasOwnProperty.call(ins, key)) { + instance[key] = ins[key]; + } + } +} +exports.setInstance = setInstance; +/** + * get a physical model by its serviceId and characteristicName + * @param path + */ +function getPhysicalModel(path) { + const service = getInstance().harmonyConnectDevice.services.find(e => e.serviceId === path.serviceId); + if (service === undefined) { + return undefined; + } + return service.characteristics.find(e => e.name === path.characteristicName); +} +exports.getPhysicalModel = getPhysicalModel; +/** + * add a service, would overide if serviceId and characteristicName is same + * @param service + */ +function addPhysicalModel(service) { + const device = getInstance().harmonyConnectDevice; + const existed = device.services.find(e => e.serviceId === service.serviceId); + if (existed === undefined) { + device.services.push(service); + } + else { + for (const ch of service.characteristics) { + if (existed.characteristics.every(e => e.name !== ch.name)) { + existed.characteristics.push(ch); + } + } + } +} +exports.addPhysicalModel = addPhysicalModel; + + +/***/ }), + +/***/ 977: +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.deserialize = exports.serialize = void 0; +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + */ +const VisualModel_1 = __webpack_require__(933); +const Instance_1 = __webpack_require__(891); +/** + * @description: convert JsonModel to Json + * @return model in json format + */ +function serialize() { + return JSON.stringify(Instance_1.getInstance(), replacer); +} +exports.serialize = serialize; +/** + * @description: convert Json to JsonModel + * @return model in json format + */ +function deserialize(json) { + const ins = JSON.parse(json, reviver); + Instance_1.setInstance(ins); +} +exports.deserialize = deserialize; +/** + * json replacer, turn any class into string + * @param key + * @param value + */ +function replacer(key, value) { + if (value instanceof Map) { + return { + dataType: "Map", + value: Object.fromEntries(value.entries()), + }; + } + else if (value instanceof Set) { + return { + dataType: "Set", + value: Array.from(value.entries()), + }; + } + else if (value instanceof VisualModel_1.VisualModel) { + const visualObj = {}; + Object.assign(visualObj, value); + return { + dataType: "VisualModel", + value: visualObj, + }; + } + else { + return value; + } +} +/** + * json reviver, true magic, replace plain json to classes + * @param key + * @param value + */ +function reviver(key, value) { + if (typeof value === "object" && value !== null && value !== undefined) { + if (value.dataType === "Map") { + return new Map(Object.entries(value.value)); + } + else if (value.dataType === "Set") { + return new Set(value.value); + } + else if (value.dataType === "VisualModel") { + const res = new VisualModel_1.VisualModel({ type: "" }); + Object.assign(res, value.value); + value = res; + } + } + return value; +} + + +/***/ }), + +/***/ 933: +/***/ ((__unused_webpack_module, exports, __webpack_require__) => { + + +/* + * @Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.VisualModel = void 0; +const CombinedModel_1 = __webpack_require__(964); +// sometimes the type of VisualModel would be add a suffix to distinguish origin component of GrapesJs +// e.g. button-visual +const typeSuffix = "-visual"; +class VisualModel { + constructor(obj) { + this.property = (obj.property !== undefined) ? obj.property : new Map(); + this.children = (obj.children !== undefined) ? obj.children : []; + this.physicalModel = obj.physicalModel; + if (obj.type === "wrapper") { + this.id = "wrapper"; + this.type = "div"; + return; + } + if (obj.type.endsWith(typeSuffix)) { + obj.type = obj.type.substring(0, obj.type.length - typeSuffix.length); + } + this.id = (obj.id !== undefined) ? obj.id : ""; + this.type = obj.type; + if (obj.combinedSelfType !== undefined || obj.data !== undefined || obj.event !== undefined) { + this.combinedInfo = { + id: "", + type: "", + selfType: obj.combinedSelfType === undefined ? CombinedModel_1.CombinedSelfType.None : obj.combinedSelfType, + data: obj.data, + event: obj.event, + }; + } + } + accept(v) { + return v.visit(this); + } +} +exports.VisualModel = VisualModel; + + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +(() => { +var exports = __webpack_exports__; + +/** + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +const errorMap_1 = __webpack_require__(784); +const Instance_1 = __webpack_require__(891); +const Serialization_1 = __webpack_require__(977); +const BridgeVisitor_1 = __webpack_require__(844); +const HmlCssCodeGenerator_1 = __webpack_require__(55); +const FormManager_1 = __webpack_require__(207); +const visualVersion = 12; +/** + * @description: codegen hml and css according to code in visual file + * @param source is code in visual file + * @return object of hmlCSS, errorType and errorMessage + */ +function genHmlAndCss(source) { + const retObj = { + hmlCss: { + hml: "", + css: "", + json: "", + }, + errorType: "", + errorMessage: "", + }; + try { + // parse source code in visual file + Serialization_1.deserialize(source); + const destVisualVersion = Instance_1.getInstance().document.VisualVersion; + const regex = /^([1-9]+[0-9]*)$/; + if (destVisualVersion === undefined) { + retObj.errorType = "versionError"; + } + else { + const expression = destVisualVersion.match(regex); + if (expression === null || parseInt(expression[1]) > visualVersion) { + retObj.errorType = "versionError"; + } + } + try { + const model = Instance_1.getInstance().visualModel; + const hmlCss = emitFA(model); + if (hmlCss.hml === "error" || hmlCss.css === "error") { + retObj.errorType = "codegenError"; + } + retObj.hmlCss = hmlCss; + if (Instance_1.getInstance().document.type === "FORM") { + retObj.hmlCss.json = FormManager_1.formManager.codegenToJson(); + } + } + catch (e) { + retObj.errorType = "modelError"; + } + } + catch (e) { + retObj.errorType = "fileError"; + } + if (retObj.errorType !== "") { + retObj.errorMessage = errorMap_1.errorMap.get(retObj.errorType); + retObj.hmlCss.hml = ""; + retObj.hmlCss.css = ""; + } + return retObj; +} +/** + * @description: output hml and css source code of the model + */ +function emitFA(rootModel) { + const hmlCss = { + hml: "", + css: "", + json: "", + }; + // generate hml + const hmlVisitor = new BridgeVisitor_1.HMLBridge(); + const ast = rootModel.accept(hmlVisitor); + const hmlRes = HmlCssCodeGenerator_1.genFAHML(ast); + if (hmlVisitor.getErrorCount() > 0) { + hmlCss.hml = "error"; + } + else { + hmlCss.hml = hmlRes; + } + // generate css + const cssVisitor = new BridgeVisitor_1.CSSBridge(); + const styles = rootModel.accept(cssVisitor); + const cssRes = HmlCssCodeGenerator_1.genFACSS(styles); + if (cssVisitor.getErrorCount() > 0) { + hmlCss.css = "error"; + } + else { + hmlCss.css = cssRes; + } + return hmlCss; +} +exports.genHmlAndCss = genHmlAndCss; + +})(); + +var __webpack_export_target__ = exports; +for(var i in __webpack_exports__) __webpack_export_target__[i] = __webpack_exports__[i]; +if(__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true }); +/******/ })() +; \ No newline at end of file diff --git a/ace-loader/plugin/templater/bind.js b/ace-loader/plugin/templater/bind.js index 9f42b6f..1ba595e 100644 --- a/ace-loader/plugin/templater/bind.js +++ b/ace-loader/plugin/templater/bind.js @@ -63,7 +63,7 @@ function parseExp(value, functionFlag, isValue, out, nodeLoc) { out.log.push({ line: nodeLoc.line || 1, column: nodeLoc.col || 1, - reason: 'ERROR: The variable concatenation is not supported in card (supproted later).', + reason: 'ERROR: Variable concatenation is not supported currently.', }) } } @@ -175,14 +175,14 @@ function checkCard(value, out, nodeLoc) { out.log.push({ line: nodeLoc.line || 1, column: nodeLoc.col || 1, - reason: `ERROR: The expression '${value}' is not supported in card (only support a single variable in the verion).` + reason: `ERROR: Version 5: The expression '${value}' is not supported. Only single variables are supported.` }) } else if (!checkCardVersionLimit() && !checkExpression(value)) { out.log.push({ line: nodeLoc.line || 1, column: nodeLoc.col || 1, - reason: `ERROR: The expression '${value}' is not supported in card (only support the binocular expression, ` + - `OR expression, AND expression and NOT expression).` + reason: `ERROR: Version 6 and above: The expression '${value}' is not supported. Only the binocular expression, ` + + `OR expression, AND expression and NOT expression are supported.` }) } } diff --git a/ace-loader/plugin/templater/component_validator.js b/ace-loader/plugin/templater/component_validator.js index d0c7db6..37754a2 100644 --- a/ace-loader/plugin/templater/component_validator.js +++ b/ace-loader/plugin/templater/component_validator.js @@ -1114,7 +1114,7 @@ function validateAttr(resourcePath, attrName, attrValue, out, tagName, nodeLoc, out.log.push({ line: nodeLoc.line, column: nodeLoc.col, - reason: `ERROR: The 'tid' cannot support the expression.` + reason: `ERROR: The 'tid' does not support the expression.` }) } if (attrName === 'value') { diff --git a/ace-loader/plugin/templater/index.js b/ace-loader/plugin/templater/index.js index 7bed2cc..fec735c 100644 --- a/ace-loader/plugin/templater/index.js +++ b/ace-loader/plugin/templater/index.js @@ -220,7 +220,7 @@ function checkAttrFor(node, attributes, pos) { compileResult.log.push({ line: pos.line || 1, column: pos.col || 1, - reason: `WARNING: The 'tid' is recommended to use here.`, + reason: `WARNING: The 'tid' is recommended here.`, }) } if (ifExists) { @@ -242,7 +242,7 @@ function checkAttrFor(node, attributes, pos) { compileResult.log.push({ line: pos.line || 1, column: pos.col || 1, - reason: `ERROR: The nested 'for' is not support.`, + reason: `ERROR: The nested 'for' is not supported.`, }) } } diff --git a/ace-loader/src/card-loader.js b/ace-loader/src/card-loader.js index bc2ba31..6658653 100644 --- a/ace-loader/src/card-loader.js +++ b/ace-loader/src/card-loader.js @@ -35,11 +35,11 @@ function loader(source) { const customLang = options.lang || {} const resourcePath = this.resourcePath const fileName = resourcePath.replace(path.extname(resourcePath).toString(), '') - let output = '' - output = getRequireString(this, jsonLoaders('template'), resourcePath) + let output = '//card_start\n' + output += 'var card_template =' + getRequireString(this, jsonLoaders('template'), resourcePath) const styleInfo = findStyleFile(fileName) if (styleInfo.extStyle == true) { - output += getRequireString(this, jsonLoaders('style', customLang[styleInfo.type]), styleInfo.styleFileName) + output += 'var card_style =' + getRequireString(this, jsonLoaders('style', customLang[styleInfo.type]), styleInfo.styleFileName) } output = addJson(this, output, fileName, '') @@ -84,16 +84,18 @@ function loader(source) { nameSet.add(customElementName) } const compFileName = compResourcepath.replace(path.extname(compResourcepath).toString(), '') - output += getRequireString(this, jsonLoaders('template'), + const elementLastName = path.basename(compResourcepath).replace(path.extname(compResourcepath).toString(), '') + output += `var card_element_template_${elementLastName} =` + getRequireString(this, jsonLoaders('template'), compResourcepath + `?${customElementName}#${fileName}`) const compStyleInfo = findStyleFile(compFileName) if (compStyleInfo.extStyle == true) { - output += getRequireString(this, jsonLoaders('style', customLang[compStyleInfo.type]), + output += `var card_element_style_${elementLastName} =` + getRequireString(this, jsonLoaders('style', customLang[compStyleInfo.type]), compStyleInfo.styleFileName + `?${customElementName}#${fileName}`) } - output = addJson(this, output, compFileName, `?${customElementName}#${fileName}`) + output = addJson(this, output, compFileName, `?${customElementName}#${fileName}`, elementLastName) }) } + output = output + '\n//card_end' return output } @@ -128,22 +130,23 @@ function findStyleFile (fileName) { return {extStyle: extStyle, styleFileName: styleFileName, type: type} } -function addJson(_this, output, fileName, query) { +function addJson(_this, output, fileName, query, elementLastName) { + const content = `${elementLastName ? 'var card_element_json_' + elementLastName : 'var card_json'} =` if (fs.existsSync(fileName + '.json') && !fs.existsSync(fileName + '.js')) { - output += getRequireString(_this, jsonLoaders('json'), fileName + '.json' + query) + output += content + getRequireString(_this, jsonLoaders('json'), fileName + '.json' + query) } else if (fs.existsSync(fileName + '.js') && !fs.existsSync(fileName + '.json')) { logWarn(_this, [{ reason: `WARNING: The JS file '${fileName}.js' will be discarded in future version, ` + `use the JSON file '${fileName}.json' instead.`, }]) - output += getRequireString(_this, jsonLoaders('json'), fileName + '.js' + query) + output += content + getRequireString(_this, jsonLoaders('json'), fileName + '.js' + query) } else if (fs.existsSync(fileName + '.json') && fs.existsSync(fileName + '.js')) { logWarn(_this, [{ reason: `WARNING: '${fileName}' cannot have the same name files '.json' and '.js', otherwise '.json' in default.`, }]) - output += getRequireString(_this, jsonLoaders('json'), fileName + '.json' + query) + output += content + getRequireString(_this, jsonLoaders('json'), fileName + '.json' + query) } return output } -module.exports = loader +module.exports = loader \ No newline at end of file diff --git a/ace-loader/src/cardJson-plugin.js b/ace-loader/src/cardJson-plugin.js index 13d251a..4b430c8 100644 --- a/ace-loader/src/cardJson-plugin.js +++ b/ace-loader/src/cardJson-plugin.js @@ -13,71 +13,220 @@ * limitations under the License. */ -const fs = require('fs') const path = require('path') - -let output = '' -const initIndexJSONObject = { "template": {}, "styles": {}, "actions": {},"data":{} } - -function compileJson(compiler, type, filePath, content, contentType, elementType) { - compiler.hooks.done.tap(type + contentType, () => { - if (type === 'init') { - writeFileSync(filePath, initIndexJSONObject) - } else { - writeFileSync(filePath, content, contentType, elementType) - } - }) -} - -function writeFileSync(filePath, content, contentType, elementType) { - if (!fs.existsSync(filePath)) { - fs.writeFileSync(filePath, stringify(content)) - } else { - const fileContent = fs.readFileSync(filePath, {encoding:'utf-8'}) - try { - const jsonContent = JSON.parse(fileContent) - if (contentType && !elementType) { - jsonContent[contentType] = content ? content : {} - fs.writeFileSync(filePath, stringify(jsonContent)) - } else if (contentType && elementType) { - if (!jsonContent[contentType]) { - jsonContent[contentType] = {} - } - jsonContent[contentType][elementType] = content ? content : {} - fs.writeFileSync(filePath, stringify(jsonContent)) - } - } catch (e) { - fs.writeFileSync(filePath, stringify(initIndexJSONObject)) - writeFileSync(filePath, content, contentType) - } - } -} - -function stringify (jsonObect) { - return JSON.stringify(jsonObect, null, 2) -} +const { Compilation } = require('webpack') class AfterEmitPlugin { - constructor(output_) { - output = output_ + constructor() { } apply(compiler) { - compiler.hooks.afterEmit.tap('delete', (compilation) => { - const assets = compilation.assets - const keys = Object.keys(assets) - keys.forEach(key => { - if (fs.existsSync(path.resolve(output, key))) { - if (key.indexOf('i18n') !== 0) { - fs.unlinkSync(path.resolve(output, key)) - } + compiler.hooks.thisCompilation.tap('card', (compilation) => { + compilation.hooks.processAssets.tapAsync( + { + name: 'MyPlugin', + stage: Compilation.PROCESS_ASSETS_STAGE_REPOR, + }, + (assets, back) => { + const keys = Object.keys(assets) + keys.forEach(key => { + sourceChange(key, assets, compilation); + }); + back && back(); } - }) + ); }) } } -module.exports = { - compileJson: compileJson, - AfterEmitPlugin: AfterEmitPlugin +function sourceChange(key, assets, compilation) { + try { + const extName = path.extname(key); + if (extName === '.js') { + const jsonOut = {}; + const source = assets[key].source(); + const str = source.match(/card_start((\s||\S)*)card_end/)[1]; + const arrary = str.split('\n'); + arrary.forEach(element => { + elementChange(element, source, jsonOut); + }); + const assetReplace = {}; + Object.keys(jsonOut).forEach(function (jsonOutKey) { + toAddJson(assetReplace, jsonOutKey, JSON.parse(jsonOut[jsonOutKey]), compilation, path.join(process.env.projectPath, key)); + }); + assets[key.replace(extName, '') + '.json'] = { + source: function () { + return JSON.stringify(assetReplace, null, 2); + }, + size: function () { + return JSON.stringify(assetReplace, null, 2).size; + } + } + delete assets[key]; + } + } catch (e) { + compilation.errors.push({ message: 'errorStartERROR File:' + key + + '\n' + ` ${e.message}` + 'errorEnd' }); + } } + +function elementChange(element, source, jsonOut) { + if (element.trim() === '' || element.trim() === '//') { + } else { + const key = element.match(/var ((\s||\S)*) =/)[1]; + const value = element.match(/"((\s||\S)*)"/)[1]; + const replaceSource = source.replace(element, '').toString(); + const partStart = replaceSource.indexOf(value); + const subSource = replaceSource.substr(partStart); + const partEnd = subSource.indexOf('/***/ })'); + let out = subSource.substr(0, partEnd).match(/module\.exports \= ((\s||\S)*)/)[1].trim(); + if (out.indexOf('JSON.parse(') === 0) { + out = JSON.stringify(eval(out)); + } + if (out.substr(out.length - 1, 1) === ';') { + out = out.substr(0, out.length - 1); + } + jsonOut[key] = out; + } +} + +function toAddJson(assetReplace, key, value, compilation, sourceKey) { + assetReplace['template'] = assetReplace['template'] || {}; + assetReplace['styles'] = assetReplace['styles'] || {}; + assetReplace['data'] = assetReplace['data'] || {}; + assetReplace['actions'] = assetReplace['actions'] || {}; + assetReplace['apiVersion'] = assetReplace['apiVersion'] || {}; + + switch(key) { + case 'card_template': + assetReplace['template'] = value; + break; + case 'card_style': + assetReplace['styles'] = value; + break; + case 'card_json': + if (value) { + if (value.data) { + assetReplace['data'] = validateData(value.data, compilation, sourceKey); + } + if (value.actions) { + assetReplace['actions'] = processActions(value.actions, compilation, sourceKey); + } + if (value.apiVersion) { + assetReplace['apiVersion'] = validateData(value.apiVersion, compilation, sourceKey); + } + if (value.props) { + assetReplace['props'] = replacePropsArray(value.propsValue, compilation, sourceKey); + } + } + break; + default: + addElement(assetReplace, key, value, compilation, sourceKey); + break; + } +} + +function addElement(assetReplace, key, value, compilation, sourceKey) { + const keyName = key.substr(key.lastIndexOf('_') + 1, key.length - key.lastIndexOf('_') + 1); + assetReplace[keyName] = assetReplace[keyName] || {}; + assetReplace[keyName]['template'] = assetReplace[keyName]['template'] || {}; + assetReplace[keyName]['styles'] = assetReplace[keyName]['styles'] || {}; + assetReplace[keyName]['data'] = assetReplace[keyName]['data'] || {}; + assetReplace[keyName]['actions'] = assetReplace[keyName]['actions'] || {}; + assetReplace[keyName]['apiVersion'] = assetReplace[keyName]['apiVersion'] || {}; + + switch(key.replace(keyName, '')) { + case 'card_element_template_': + assetReplace[keyName]['template'] = value; + break; + case 'card_element_style_': + assetReplace[keyName]['styles'] = value; + break; + case 'card_element_json_': + if (value) { + if (value.data) { + assetReplace[keyName]['data'] = validateData(value.data, compilation, sourceKey); + } + if (value.actions) { + assetReplace[keyName]['actions'] = processActions(value.actions, compilation, sourceKey); + } + if (value.apiVersion) { + assetReplace[keyName]['apiVersion'] = validateData(value.apiVersion, compilation, sourceKey); + } + if (value.props) { + assetReplace[keyName]['props'] = replacePropsArray(value.propsValue, compilation, sourceKey); + } + } + break; + default: + break; + } +} + +function replacePropsArray(propsValue, _this, key) { + if (!propsValue) { + return propsValue; + } + if (Array.isArray(propsValue)) { + const propsObject = {}; + propsValue.forEach(item => { + if (typeof(item) !== 'string') { + _this.warnings.push({message: 'warnStartWARNING File:' + key + + '\n' + `The props value type should be 'string', not '${typeof(item)}' in props array in custom elements.` + 'warnEnd'}); + } + propsObject[item] = { 'default': '' }; + }); + propsValue = propsObject; + } else if (Object.prototype.toString.call(propsValue) === '[object Object]') { + Object.keys(propsValue).forEach(item => { + if (Object.prototype.toString.call(propsValue[item]) !== '[object Object]') { + _this.warnings.push({message: 'warnStartWARNING File:' + key + + '\n' + 'The props default value type can only be Object in custom elements.' + 'warnEnd'}); + } + if (!propsValue[item].hasOwnProperty('default')) { + propsValue[item] = { 'default': '' } + } + }); + } else { + _this.warnings.push({message: 'warnStartWARNING File:' + key + + '\n' + 'The props type can only be Array or Object in custom elements.' + 'warnEnd'}); + } + return propsValue; +} + +function processActions(actionsValue, _this, key) { + if (Object.prototype.toString.call(actionsValue) === '[object Object]') { + Object.keys(actionsValue).forEach(item => { + if (actionsValue[item].method) { + if (typeof(actionsValue[item].method) === 'string') { + if (actionsValue[item].method.toLowerCase() !== actionsValue[item].method) { + _this.warnings.push({message: 'warnStartWARNING File:' + key + + '\n' + `WARNING: The key method '${actionsValue[item].method}' in the actions don't support uppercase letters.` + 'warnEnd'}); + actionsValue[item].method = actionsValue[item].method.toLowerCase(); + } + } else { + _this.warnings.push({message: 'warnStartWARNING File:' + key + + '\n' + `WARNING: The key method type in the actions should be 'string', not '${typeof(actionsValue[item].method)}'.` + 'warnEnd'}); + } + } + }) + } else { + if (actionsValue) { + _this.warnings.push({message: 'warnStartWARNING File:' + key + + '\n' + 'WARNING: The actions value type can only be Object.' + 'warnEnd'}); + } + } + return actionsValue; +} + +function validateData(dataValue, _this, key) { + if (dataValue && Object.prototype.toString.call(dataValue) !== '[object Object]') { + _this.warnings.push({message: 'warnStartWARNING File:' + key + + '\n' + 'WARNING: The data value type can only be Object.' + 'warnEnd'}); + } + return dataValue; +} + +module.exports = { + AfterEmitPlugin: AfterEmitPlugin +} \ No newline at end of file diff --git a/ace-loader/src/extgen.js b/ace-loader/src/extgen.js index 9f14b65..8f630c3 100644 --- a/ace-loader/src/extgen.js +++ b/ace-loader/src/extgen.js @@ -17,22 +17,31 @@ * under the License. */ -import loaderUtils from 'loader-utils' +import loaderUtils from 'loader-utils'; + const codegen = require("./codegen/index.js"); module.exports = function (source, map) { - this.cacheable && this.cacheable() - const callback = this.async() - - const hmlCss = codegen.genHmlAndCss(source); - const loaderQuery = loaderUtils.getOptions(this) || {} - if (hmlCss.errorType && hmlCss.errorType !== '' && loaderQuery.type === 'template') { - callback(hmlCss.errorType + ' : ' + hmlCss.errorMessage, '') + this.cacheable && this.cacheable(); + const callback = this.async(); + if (process.env.DEVICE_LEVEL === 'card') { + codegen = require('./codegen/card-index.js'); + } + const parsed = codegen.genHmlAndCss(source); + const loaderQuery = loaderUtils.getOptions(this) || {}; + if (parsed.errorType && parsed.errorType !== '') { + callback(parsed.errorType + ' : ' + parsed.errorMessage, ''); } else { - if(loaderQuery.type === 'template') { - callback(null, hmlCss.hmlCss.hml, map) - } else { - callback(null, hmlCss.hmlCss.css, map) + switch (loaderQuery.type) { + case 'template': + callback(null, parsed.hmlCss.hml, map); + break; + case 'style': + callback(null, parsed.hmlCss.css, map); + break; + case 'json': + callback(null, parsed.hmlCss.json, map); + break; } } -} +}; diff --git a/ace-loader/src/loader-gen.js b/ace-loader/src/loader-gen.js index 255c8d9..1c6e7fe 100644 --- a/ace-loader/src/loader-gen.js +++ b/ace-loader/src/loader-gen.js @@ -3,25 +3,16 @@ */ import loaderUtils from 'loader-utils' import path from 'path' -import fs from 'fs' -import * as legacy from './legacy' -import { - parseFragment -} -from './parser' import { getNameByPath, getRequireString, stringifyLoaders, - logWarn, + jsonLoaders, loadBabelModule } from './util' -import { isReservedTag } from './templater/component_validator' -import loader from 'sass-loader' -const { DEVICE_LEVEL } = require('./lite/lite-enum') const loaderPath = __dirname const defaultLoaders = { none: '', @@ -120,41 +111,47 @@ function styleLoaderString (loaders, config, customLoader) { } function scriptLoaderString (loaders, config, customLoader) { - loaders = [{ - name: defaultLoaders.script - }] - if (customLoader) { - loaders = loaders.concat(customLoader) - } - else { + if (process.env.DEVICE_LEVEL === 'card') { + loaders = [{ + name: defaultLoaders.json + }] + if (customLoader) { + loaders = loaders.concat(customLoader); + } loaders.push({ - name: defaultLoaders.babel, + name: defaultLoaders.extgen, query: { - presets: [loadBabelModule('@babel/preset-env')], - plugins: [loadBabelModule('@babel/plugin-transform-modules-commonjs')], - comments: 'false' - } - }) - } - if (config.app) { - loaders.push({ - name: defaultLoaders.manifest, - query: { - path: config.source + type: 'json' } }) + } else { + loaders = [{ + name: defaultLoaders.script + }] + if (customLoader) { + loaders = loaders.concat(customLoader) + } else { + loaders.push({ + name: defaultLoaders.babel, + query: { + presets: [loadBabelModule('@babel/preset-env')], + plugins: [loadBabelModule('@babel/plugin-transform-modules-commonjs')], + comments: 'false' + } + }) + } } return stringifyLoaders(loaders) } -function configLoaderString (loaders, config) { +function configLoaderString (loaders) { loaders = [{ name: defaultLoaders.json }] return stringifyLoaders(loaders) } -function dataLoaderString (loaders, config) { +function dataLoaderString (loaders) { loaders = [{ name: defaultLoaders.json }] @@ -175,52 +172,58 @@ function codegenHmlAndCss() { const resourceQuery = this.resourceQuery && loaderUtils.parseQuery(this.resourceQuery) || {} const isEntry = resourceQuery.entry let output = '' - let jsFileName = this.resourcePath.replace(process.env.aceSuperVisualPath, process.env.aceModuleRoot) + let jsFileName = this.resourcePath.replace(process.env.aceSuperVisualPath, process.env.projectPath) jsFileName = jsFileName.substr(0, jsFileName.length - 6) + 'js'; - output = 'var $app_script$ = ' + getRequireString(this, getLoaderString('script', { - customLang, - lang: undefined, - element: undefined, - elementName: undefined, - source: jsFileName - }), jsFileName) - - output += 'var $app_template$ = ' + getRequireString(this, getLoaderString('template', { - customLang, - lang: undefined, - element: isElement, - elementName: undefined, - source: this.resourcePath - }), this.resourcePath) - - output += 'var $app_style$ = ' + getRequireString(this, getLoaderString('style', { - customLang, - lang: undefined, - element: isElement, - elementName: undefined, - source: this.resourcePath - }), this.resourcePath) - - output += ` - $app_define$('@app-component/${getNameByPath(this.resourcePath)}', [], function($app_require$, $app_exports$, $app_module$) { - ` + ` - $app_script$($app_module$, $app_exports$, $app_require$) - if ($app_exports$.__esModule && $app_exports$.default) { - $app_module$.exports = $app_exports$.default - } - ` + ` - $app_module$.exports.template = $app_template$ - ` + ` - $app_module$.exports.style = $app_style$ - ` + ` - }) - ` - - if (isEntry) { - output += `$app_bootstrap$('@app-component/${getNameByPath(this.resourcePath)}'` + ',undefined' + ',undefined' + `)` + if (process.env.DEVICE_LEVEL === 'card') { + output = '//card_start\n' + output += `var card_template =` + getRequireString(this, jsonLoaders('template', undefined, true, 'template'), this.resourcePath) + output += `var card_style =` + getRequireString(this, jsonLoaders('style', undefined, true, 'style'), this.resourcePath) + output += `var card_json =` + getRequireString(this, jsonLoaders('json', undefined, true, 'json'), this.resourcePath) + output += '\n//card_end' + } else { + output = 'var $app_script$ = ' + getRequireString(this, getLoaderString('script', { + customLang, + lang: undefined, + element: undefined, + elementName: undefined, + source: jsFileName + }), jsFileName) + + output += 'var $app_template$ = ' + getRequireString(this, getLoaderString('template', { + customLang, + lang: undefined, + element: isElement, + elementName: undefined, + source: this.resourcePath + }), this.resourcePath) + + output += 'var $app_style$ = ' + getRequireString(this, getLoaderString('style', { + customLang, + lang: undefined, + element: isElement, + elementName: undefined, + source: this.resourcePath + }), this.resourcePath) + + output += ` + $app_define$('@app-component/${getNameByPath(this.resourcePath)}', [], function($app_require$, $app_exports$, $app_module$) { + ` + ` + $app_script$($app_module$, $app_exports$, $app_require$) + if ($app_exports$.__esModule && $app_exports$.default) { + $app_module$.exports = $app_exports$.default + } + ` + ` + $app_module$.exports.template = $app_template$ + ` + ` + $app_module$.exports.style = $app_style$ + ` + ` + }) + ` + if (isEntry) { + output += `$app_bootstrap$('@app-component/${getNameByPath(this.resourcePath)}'` + ',undefined' + ',undefined' + `)` + } } return output } - -module.exports = codegenHmlAndCss +module.exports = codegenHmlAndCss \ No newline at end of file diff --git a/ace-loader/src/resource-plugin.js b/ace-loader/src/resource-plugin.js index 81398e1..fff88f9 100644 --- a/ace-loader/src/resource-plugin.js +++ b/ace-loader/src/resource-plugin.js @@ -117,6 +117,9 @@ class ResourcePlugin { circularFile(input, output, '../share'); }); compiler.hooks.normalModuleFactory.tap('OtherEntryOptionPlugin', () => { + if (process.env.DEVICE_LEVEL === 'card') { + return; + } addPageEntryObj(); for (const key in entryObj) { if (!compiler.options.entry[key]) { diff --git a/ace-loader/test/card/expected/ifAttr/ifAttr.json b/ace-loader/test/card/expected/ifAttr/ifAttr.json index 673be09..02a4ba8 100644 --- a/ace-loader/test/card/expected/ifAttr/ifAttr.json +++ b/ace-loader/test/card/expected/ifAttr/ifAttr.json @@ -8,28 +8,28 @@ "attr": { "value": "Hello-TV" }, - "shown": "{{show}}" + "shown": "{{showIf}}" }, { "type": "text", "attr": { "value": "Hello-Wearable" }, - "shown": "{{display}}&&!{{show}}" + "shown": "{{display}}&&!{{showIf}}" }, { "type": "text", "attr": { "value": "Hello-World" }, - "shown": "!{{display}}&&!{{show}}" + "shown": "!{{display}}&&!{{showIf}}" } ] }, "styles": {}, "actions": {}, "data": { - "show": false, + "showIf": false, "display": true }, "apiVersion": {} diff --git a/ace-loader/test/card/expected/importCSS/importCSS.json b/ace-loader/test/card/expected/importCSS/importCSS.json index 4409b58..3f6a61e 100644 --- a/ace-loader/test/card/expected/importCSS/importCSS.json +++ b/ace-loader/test/card/expected/importCSS/importCSS.json @@ -16,6 +16,7 @@ "justifyContent": "center" } }, + "apiVersion": {}, "actions": {}, "data": {} } \ No newline at end of file diff --git a/ace-loader/test/card/expected/importLess/importLess.json b/ace-loader/test/card/expected/importLess/importLess.json index 0c6f186..91f1a0e 100644 --- a/ace-loader/test/card/expected/importLess/importLess.json +++ b/ace-loader/test/card/expected/importLess/importLess.json @@ -13,6 +13,7 @@ "backgroundColor": "#000000" } }, + "apiVersion": {}, "actions": {}, "data": {} } \ No newline at end of file diff --git a/ace-loader/test/card/expected/inlineStyle/inlineStyle.json b/ace-loader/test/card/expected/inlineStyle/inlineStyle.json index ecfc147..c6fd336 100644 --- a/ace-loader/test/card/expected/inlineStyle/inlineStyle.json +++ b/ace-loader/test/card/expected/inlineStyle/inlineStyle.json @@ -140,6 +140,7 @@ ] }, "styles": {}, + "apiVersion": {}, "actions": {}, "data": {} } \ No newline at end of file diff --git a/ace-loader/test/card/expected/privateAttr/privateAttr.json b/ace-loader/test/card/expected/privateAttr/privateAttr.json index f178bdd..ff67ab7 100644 --- a/ace-loader/test/card/expected/privateAttr/privateAttr.json +++ b/ace-loader/test/card/expected/privateAttr/privateAttr.json @@ -56,6 +56,7 @@ ] }, "styles": {}, + "apiVersion": {}, "actions": {}, "data": {} } \ No newline at end of file diff --git a/ace-loader/test/card/testcase/pages/ifAttr/ifAttr.hml b/ace-loader/test/card/testcase/pages/ifAttr/ifAttr.hml index 71a4f6d..afa0078 100644 --- a/ace-loader/test/card/testcase/pages/ifAttr/ifAttr.hml +++ b/ace-loader/test/card/testcase/pages/ifAttr/ifAttr.hml @@ -1,5 +1,5 @@
- Hello-TV + Hello-TV Hello-Wearable Hello-World
\ No newline at end of file diff --git a/ace-loader/test/card/testcase/pages/ifAttr/ifAttr.json b/ace-loader/test/card/testcase/pages/ifAttr/ifAttr.json index 692074d..ab9ddf9 100644 --- a/ace-loader/test/card/testcase/pages/ifAttr/ifAttr.json +++ b/ace-loader/test/card/testcase/pages/ifAttr/ifAttr.json @@ -1,6 +1,6 @@ { "data": { - "show": false, + "showIf": false, "display": true } } \ No newline at end of file diff --git a/ace-loader/webpack.rich.config.js b/ace-loader/webpack.rich.config.js index 746fb6d..d35c3a9 100644 --- a/ace-loader/webpack.rich.config.js +++ b/ace-loader/webpack.rich.config.js @@ -93,6 +93,12 @@ const richModule = { const cardModule = { rules: [ + { + test: /\.visual$/, + use: [{ + loader: path.resolve(__dirname, './lib/loader-gen.js') + }] + }, { test: /\.hml$/, use: [{ @@ -204,8 +210,7 @@ module.exports = (env) => { } if (process.env.DEVICE_LEVEL === 'card') { config.module = cardModule - config.plugins.push(new AfterEmitPlugin(process.env.buildPath)) - delete config.cache + config.plugins.push(new AfterEmitPlugin()) } else { if (env.isPreview !== "true") { if (env.compilerType && env.compilerType === 'ark') {