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 */ 15 16const path = require('path') 17const { Compilation } = require('webpack') 18 19class AfterEmitPlugin { 20 constructor() { 21 } 22 23 apply(compiler) { 24 compiler.hooks.thisCompilation.tap('card', (compilation) => { 25 compilation.hooks.processAssets.tapAsync( 26 { 27 name: 'MyPlugin', 28 stage: Compilation.PROCESS_ASSETS_STAGE_REPOR, 29 }, 30 (assets, back) => { 31 const keys = Object.keys(assets) 32 keys.forEach(key => { 33 if ('./' + process.env.abilityType + '.js' !== key) { 34 sourceChange(key, assets, compilation); 35 } 36 }); 37 back && back(); 38 } 39 ); 40 }) 41 } 42} 43 44function sourceChange(key, assets, compilation) { 45 try { 46 const extName = path.extname(key); 47 if (extName === '.js') { 48 const jsonOut = {}; 49 const source = assets[key].source(); 50 const str = source.match(/card_start((\s||\S)*)card_end/)[1]; 51 const array = str.split('\n'); 52 array.forEach(element => { 53 elementChange(element, source, jsonOut); 54 }); 55 const assetReplace = {}; 56 Object.keys(jsonOut).forEach(function (jsonOutKey) { 57 toAddJson(assetReplace, jsonOutKey, JSON.parse(jsonOut[jsonOutKey]), compilation, path.join(process.env.projectPath, key)); 58 }); 59 assets[key.replace(extName, '') + '.json'] = { 60 source: function () { 61 return JSON.stringify(assetReplace, null, 2); 62 }, 63 size: function () { 64 return JSON.stringify(assetReplace, null, 2).size; 65 } 66 } 67 delete assets[key]; 68 } 69 } catch (e) { 70 compilation.errors.push({ message: 'errorStartERROR File:' + key + 71 '\n' + ` ${e.message}` + 'errorEnd' }); 72 } 73} 74 75function elementChange(element, source, jsonOut) { 76 if (element.trim() === '' || element.trim() === '//') { 77 } else { 78 const key = element.match(/var ((\s||\S)*) =/)[1]; 79 const value = element.match(/"((\s||\S)*)"/)[1]; 80 const replaceSource = source.replace(element, '').toString(); 81 const partStart = replaceSource.indexOf(value); 82 const subSource = replaceSource.substr(partStart); 83 const partEnd = subSource.indexOf('/***/ })'); 84 let out = subSource.substr(0, partEnd).match(/module\.exports \= ((\s||\S)*)/)[1].trim(); 85 if (out.indexOf('JSON.parse(') === 0) { 86 out = JSON.stringify(eval(out)); 87 } 88 if (out.substr(out.length - 1, 1) === ';') { 89 out = out.substr(0, out.length - 1); 90 } 91 jsonOut[key] = out; 92 } 93} 94 95function toAddJson(assetReplace, key, value, compilation, sourceKey) { 96 assetReplace['template'] = assetReplace['template'] || {}; 97 assetReplace['styles'] = assetReplace['styles'] || {}; 98 assetReplace['data'] = assetReplace['data'] || {}; 99 assetReplace['actions'] = assetReplace['actions'] || {}; 100 assetReplace['apiVersion'] = assetReplace['apiVersion'] || {}; 101 102 switch(key) { 103 case 'card_template': 104 assetReplace['template'] = value; 105 break; 106 case 'card_style': 107 assetReplace['styles'] = value; 108 break; 109 case 'card_json': 110 if (value) { 111 if (value.data) { 112 assetReplace['data'] = validateData(value.data, compilation, sourceKey); 113 } 114 if (value.actions) { 115 assetReplace['actions'] = processActions(value.actions, compilation, sourceKey); 116 } 117 if (value.apiVersion) { 118 assetReplace['apiVersion'] = validateData(value.apiVersion, compilation, sourceKey); 119 } 120 if (value.props) { 121 assetReplace['props'] = replacePropsArray(value.propsValue, compilation, sourceKey); 122 } 123 } 124 break; 125 default: 126 addElement(assetReplace, key, value, compilation, sourceKey); 127 break; 128 } 129} 130 131function addElement(assetReplace, key, value, compilation, sourceKey) { 132 const keyName = key.substr(key.lastIndexOf('_') + 1, key.length - key.lastIndexOf('_') + 1); 133 assetReplace[keyName] = assetReplace[keyName] || {}; 134 assetReplace[keyName]['template'] = assetReplace[keyName]['template'] || {}; 135 assetReplace[keyName]['styles'] = assetReplace[keyName]['styles'] || {}; 136 assetReplace[keyName]['data'] = assetReplace[keyName]['data'] || {}; 137 assetReplace[keyName]['actions'] = assetReplace[keyName]['actions'] || {}; 138 assetReplace[keyName]['apiVersion'] = assetReplace[keyName]['apiVersion'] || {}; 139 140 switch(key.replace(keyName, '')) { 141 case 'card_element_template_': 142 assetReplace[keyName]['template'] = value; 143 break; 144 case 'card_element_style_': 145 assetReplace[keyName]['styles'] = value; 146 break; 147 case 'card_element_json_': 148 if (value) { 149 if (value.data) { 150 assetReplace[keyName]['data'] = validateData(value.data, compilation, sourceKey); 151 } 152 if (value.actions) { 153 assetReplace[keyName]['actions'] = processActions(value.actions, compilation, sourceKey); 154 } 155 if (value.apiVersion) { 156 assetReplace[keyName]['apiVersion'] = validateData(value.apiVersion, compilation, sourceKey); 157 } 158 if (value.props) { 159 assetReplace[keyName]['props'] = replacePropsArray(value.propsValue, compilation, sourceKey); 160 } 161 } 162 break; 163 default: 164 break; 165 } 166} 167 168function replacePropsArray(propsValue, _this, key) { 169 if (!propsValue) { 170 return propsValue; 171 } 172 if (Array.isArray(propsValue)) { 173 const propsObject = {}; 174 propsValue.forEach(item => { 175 if (typeof(item) !== 'string') { 176 _this.warnings.push({message: 'warnStartWARNING File:' + key + 177 '\n' + `The props value type should be 'string', not '${typeof(item)}' in props array in custom elements.` + 'warnEnd'}); 178 } 179 propsObject[item] = { 'default': '' }; 180 }); 181 propsValue = propsObject; 182 } else if (Object.prototype.toString.call(propsValue) === '[object Object]') { 183 Object.keys(propsValue).forEach(item => { 184 if (Object.prototype.toString.call(propsValue[item]) !== '[object Object]') { 185 _this.warnings.push({message: 'warnStartWARNING File:' + key + 186 '\n' + 'The props default value type can only be Object in custom elements.' + 'warnEnd'}); 187 } 188 if (!propsValue[item].hasOwnProperty('default')) { 189 propsValue[item] = { 'default': '' } 190 } 191 }); 192 } else { 193 _this.warnings.push({message: 'warnStartWARNING File:' + key + 194 '\n' + 'The props type can only be Array or Object in custom elements.' + 'warnEnd'}); 195 } 196 return propsValue; 197} 198 199function processActions(actionsValue, _this, key) { 200 if (Object.prototype.toString.call(actionsValue) === '[object Object]') { 201 Object.keys(actionsValue).forEach(item => { 202 if (actionsValue[item].method) { 203 if (typeof(actionsValue[item].method) === 'string') { 204 if (actionsValue[item].method.toLowerCase() !== actionsValue[item].method) { 205 _this.warnings.push({message: 'warnStartWARNING File:' + key + 206 '\n' + `WARNING: The key method '${actionsValue[item].method}' in the actions don't support uppercase letters.` + 'warnEnd'}); 207 actionsValue[item].method = actionsValue[item].method.toLowerCase(); 208 } 209 } else { 210 _this.warnings.push({message: 'warnStartWARNING File:' + key + 211 '\n' + `WARNING: The key method type in the actions should be 'string', not '${typeof(actionsValue[item].method)}'.` + 'warnEnd'}); 212 } 213 } 214 }) 215 } else { 216 if (actionsValue) { 217 _this.warnings.push({message: 'warnStartWARNING File:' + key + 218 '\n' + 'WARNING: The actions value type can only be Object.' + 'warnEnd'}); 219 } 220 } 221 return actionsValue; 222} 223 224function validateData(dataValue, _this, key) { 225 if (dataValue && Object.prototype.toString.call(dataValue) !== '[object Object]') { 226 _this.warnings.push({message: 'warnStartWARNING File:' + key + 227 '\n' + 'WARNING: The data value type can only be Object.' + 'warnEnd'}); 228 } 229 return dataValue; 230} 231 232module.exports = { 233 AfterEmitPlugin: AfterEmitPlugin 234}