1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15const isType = require('../lite/lite-utils'); 16const rulePath = process.env.RULE_PATH; 17const smartvisionTag = { 18 camera: { 19 events: ['error'], 20 }, 21 video: { 22 events: [ 23 'prepared', 24 'start', 25 'pause', 26 'finish', 27 'error', 28 'seeking', 29 'seeked', 30 'timeupdate', 31 ], 32 attrs: { 33 autoplay: { 34 enum: ['false', 'true'], 35 }, 36 controls: { 37 enum: ['true', 'false'], 38 }, 39 muted: { 40 enum: ['false', 'true'], 41 }, 42 src: { checkPath: true }, 43 }, 44 }, 45}; 46 47const litewearableTag = { 48 'div': {}, 49 'canvas': {}, 50 'stack': {}, 51 'qrcode': { 52 atomic: true, 53 selfClosing: true, 54 uevents: ['click', 'longpress', 'swipe'], 55 attrs: { 56 value: { 57 required: true, 58 }, 59 type: { 60 enum: ['rect', 'circle'], 61 }, 62 }, 63 }, 64 'list': { 65 events: ['scrollend'], 66 children: ['list-item'], 67 }, 68 'list-item': { 69 excludeRoot: true, 70 parents: ['list'], 71 }, 72 'swiper': { 73 unSupportedChildren: ['list'], 74 events: ['change'], 75 attrs: { 76 index: { 77 checkFunc: 'number', 78 }, 79 loop: { 80 enum: ['true', 'false'], 81 }, 82 duration: { 83 checkFunc: 'number', 84 }, 85 vertical: { 86 enum: ['false', 'true'], 87 }, 88 }, 89 }, 90 'tabs': { 91 events: ['change'], 92 children: ['tab-content', 'tab-bar'], 93 }, 94 'tab-bar': { 95 parents: ['tabs'], 96 children: ['text'], 97 attrs: { 98 mode: { 99 enum: ['fixed'], 100 }, 101 }, 102 }, 103 'tab-content': { 104 parents: ['tabs'], 105 children: ['div', 'stack'], // to be checked 106 }, 107 'image-animator': { 108 atomic: true, 109 selfClosing: true, 110 events: ['stop'], 111 attrs: { 112 images: { 113 required: true, 114 }, 115 iteration: {}, 116 reverse: { 117 enum: ['false', 'true'], 118 }, 119 fixedsize: { 120 enum: ['true', 'false'], 121 }, 122 duration: { 123 required: true, 124 }, 125 fillmode: { 126 enum: ['none', 'forwards'], 127 }, 128 }, 129 }, 130 'image': { 131 alias: ['img'], 132 atomic: true, 133 selfClosing: true, 134 attrs: { 135 src: { 136 checkPath: true, 137 }, 138 }, 139 }, 140 'progress': { 141 atomic: true, 142 selfClosing: true, 143 attrs: { 144 type: { 145 enum: ['horizontal', 'arc'], 146 }, 147 percent: { 148 checkFunc: 'number', 149 }, 150 }, 151 }, 152 'text': { 153 atomic: true, 154 textContent: true, 155 attrs: { 156 type: { 157 enum: ['text', 'html'], 158 }, 159 value: {}, 160 }, 161 }, 162 'marquee': { 163 atomic: true, 164 attrs: { 165 scrollamount: { 166 def: 6, 167 checkFunc: 'number', 168 }, 169 }, 170 }, 171 'analog-clock': { 172 attrs: { 173 hour: { 174 checkFunc: 'number', 175 }, 176 min: { 177 checkFunc: 'number', 178 }, 179 sec: { 180 checkFunc: 'number', 181 }, 182 }, 183 }, 184 'clock-hand': { 185 parents: ['analog-clock'], 186 attrs: { 187 type: { 188 enum: ['hour', 'min', 'sec'], 189 }, 190 src: { 191 checkPath: true, 192 }, 193 }, 194 }, 195 'chart': { 196 atomic: true, 197 selfClosing: true, 198 attrs: { 199 type: { 200 enum: ['line', 'bar'], 201 }, 202 datasets: {}, 203 options: {}, 204 }, 205 }, 206 'input': { 207 atomic: true, 208 selfClosing: true, 209 events: ['change'], 210 attrs: { 211 checked: { 212 enum: ['false', 'true'], 213 }, 214 type: { 215 enum: ['button', 'checkbox', 'password', 'radio', 'text'], 216 }, 217 name: {}, 218 value: {}, 219 placeholder: {}, 220 maxlength: { 221 checkFunc: 'number', 222 }, 223 }, 224 }, 225 'slider': { 226 atomic: true, 227 selfClosing: true, 228 events: ['change'], 229 attrs: { 230 min: { 231 def: 0, 232 checkFunc: 'number', 233 }, 234 max: { 235 def: 100, 236 checkFunc: 'number', 237 }, 238 value: { 239 def: 0, 240 checkFunc: 'number', 241 }, 242 }, 243 }, 244 'switch': { 245 events: ['change'], 246 atomic: true, 247 selfClosing: true, 248 attrs: { 249 checked: { 250 enum: ['false', 'true'], 251 }, 252 }, 253 }, 254 'picker-view': { 255 atomic: true, 256 selfClosing: true, 257 uevents: ['change'], 258 attrs: { 259 type: { 260 enum: ['text', 'time'], 261 }, 262 range: {}, 263 selected: {}, 264 }, 265 }, 266}; 267 268const liteCommonTag = { 269 events: [ 270 'click', 271 'longpress', 272 'touchstart', 273 'touchmove', 274 'touchcancel', 275 'touchend', 276 'key', 277 'swipe', 278 ], 279 attrs: { 280 id: {}, 281 style: {}, 282 class: {}, 283 ref: {}, 284 if: { 285 excludeRoot: true, 286 def: 'true', 287 }, 288 elif: { 289 def: 'true', 290 }, 291 else: { 292 excludeRoot: true, 293 }, 294 for: { 295 excludeRoot: true, 296 }, 297 tid: {}, 298 show: { 299 excludeRoot: true, 300 def: 'true', 301 }, 302 }, 303}; 304 305/** 306 * Rules for adapting to different environments. If it is `liteWearable` device type, 307 * set to this type of verification rule. 308 * @param {String} deviceType device type. 309 * @return {Object} Validation rules. 310 */ 311function select(deviceType) { 312 tag = { 313 liteWearable: litewearableTag, 314 smartVision: { ...litewearableTag, ...smartvisionTag }, 315 }; 316 return tag[deviceType]; 317} 318let liteNativeTag = select(process.env.DEVICE_TYPE); 319 320/** 321 * Whether the file exists, get customized rules. 322 */ 323(function checkFile() { 324 if (rulePath) { 325 const customTag = require(rulePath); 326 isExtends(customTag); 327 } 328})(); 329 330/** 331 * Get the component types supported by the current verification rule. 332 * @return {Array} Supported component name. 333 */ 334function getKeys() { 335 const res = []; 336 const keys = Object.keys(liteNativeTag); 337 for (let i = 0; i < keys.length; i++) { 338 const key = keys[i]; 339 res.push(key); 340 } 341 res.push('attrs'); 342 return res; 343} 344 345/** 346 * According to user-defined rules, combine into new verification rules. 347 * @param {Object} customTag User-defined rule object. 348 */ 349function isExtends(customTag) { 350 if (customTag.extends == 'recommended') { 351 const nativekeys = getKeys(); 352 merge(liteNativeTag, customTag.rules, nativekeys); 353 } else { 354 liteNativeTag = customTag.rules; 355 } 356} 357/** 358 * Combine the original rules and user-defined rules. 359 * @param {Object} object Original rules. 360 * @param {Object} source user-defined rules. 361 * @param {Array} nativekeys Supported component name. 362 * @return {Array} Merged object. 363 */ 364function merge(object, source, nativekeys) { 365 const keys = Object.keys(source); 366 for (let i = 0; i < keys.length; i++) { 367 const key = keys[i]; 368 const value = source[key]; 369 let target = object[key]; 370 if (target != null && !nativekeys.includes(key)) { 371 console.error( 372 `\u001b[31mError in .literc.js: \n` + 373 `Attribute '${key}' already exists and cannot be modified\u001b[39m`, 374 ); 375 process.exit(1); 376 } else if (isType.isObject(value)) { 377 target = isType.isObject(target) ? target : {}; 378 object[key] = merge(target, value, nativekeys); 379 } else if (!(isType.isNull(value) || isType.isUndefined(value))) { 380 object[key] = value; 381 } 382 } 383 return object; 384} 385 386module.exports = { 387 liteNativeTag: liteNativeTag, 388 liteCommonTag: liteCommonTag, 389}; 390