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 fs = require('fs'); 17const path = require('path'); 18import Compilation from 'webpack/lib/Compilation'; 19import JavascriptModulesPlugin from 'webpack/lib/javascript/JavascriptModulesPlugin'; 20import CachedSource from 'webpack-sources/lib/CachedSource'; 21import ConcatSource from 'webpack-sources/lib/ConcatSource'; 22 23import { 24 circularFile, 25 useOSFiles, 26 mkDir, 27 elements 28} from './util'; 29import cluster from 'cluster'; 30 31let mStats; 32let mErrorCount = 0; 33let mWarningCount = 0; 34let isShowError = true; 35let isShowWarning = true; 36let isShowNote = true; 37let warningCount = 0; 38let noteCount = 0; 39let errorCount = 0; 40 41let GLOBAL_COMMON_MODULE_CACHE; 42 43class ResultStates { 44 constructor(options) { 45 this.options = options; 46 GLOBAL_COMMON_MODULE_CACHE = ` 47 globalThis["__common_module_cache__${process.env.hashProjectPath}"] =` + 48 ` globalThis["__common_module_cache__${process.env.hashProjectPath}"] || {};`; 49 } 50 51 apply(compiler) { 52 const buildPath = this.options.build; 53 const commonPaths = new Set(); 54 const i18nPaths = new Set(); 55 const cachePath = path.resolve(process.env.cachePath, '.rich_cache'); 56 const entryFile = path.join(cachePath, 'entry.json'); 57 const entryPaths = new Set(); 58 59 compiler.hooks.compilation.tap('toFindModule', (compilation) => { 60 compilation.hooks.buildModule.tap("findModule", (module) => { 61 if (module.resource && fs.existsSync(module.resource)) { 62 entryPaths.add(module.resource); 63 } 64 if (module.context.indexOf(process.env.projectPath) >= 0) { 65 return; 66 } 67 const modulePath = path.join(module.context); 68 const srcIndex = modulePath.lastIndexOf(path.join('src', 'main', 'js')); 69 if (srcIndex < 0) { 70 return; 71 } 72 const commonPath = path.resolve(modulePath.substring(0, srcIndex), 73 'src', 'main', 'js', 'common'); 74 if (fs.existsSync(commonPath)) { 75 commonPaths.add(commonPath); 76 } 77 const i18nPath = path.resolve(modulePath.substring(0, srcIndex), 78 'src', 'main', 'js', 'i18n'); 79 if (fs.existsSync(i18nPath)) { 80 i18nPaths.add(i18nPath); 81 } 82 }); 83 }); 84 85 compiler.hooks.afterCompile.tap('copyFindModule', () => { 86 for (let commonPath of commonPaths) { 87 circularFile(commonPath, path.resolve(buildPath, '../share/common')); 88 } 89 for (let i18nPath of i18nPaths) { 90 circularFile(i18nPath, path.resolve(buildPath, '../share/i18n')); 91 } 92 addCacheFiles(entryFile, cachePath, entryPaths); 93 }); 94 95 compiler.hooks.done.tap('Result States', (stats) => { 96 Object.keys(elements).forEach(key => { 97 delete elements[key]; 98 }) 99 if (process.env.isPreview && process.env.aceSoPath && 100 useOSFiles && useOSFiles.size > 0) { 101 writeUseOSFiles(); 102 } 103 mStats = stats; 104 warningCount = 0; 105 noteCount = 0; 106 errorCount = 0; 107 if (mStats.compilation.errors) { 108 mErrorCount = mStats.compilation.errors.length; 109 } 110 if (mStats.compilation.warnings) { 111 mWarningCount = mStats.compilation.warnings.length; 112 } 113 if (process.env.error === 'false') { 114 isShowError = false; 115 } 116 if (process.env.warning === 'false') { 117 isShowWarning = false; 118 } 119 if (process.env.note === 'false') { 120 isShowNote = false; 121 } 122 printResult(buildPath); 123 }); 124 125 compiler.hooks.compilation.tap('CommonAsset', compilation => { 126 compilation.hooks.processAssets.tap( 127 { 128 name: 'GLOBAL_COMMON_MODULE_CACHE', 129 stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS, 130 }, 131 (assets) => { 132 if (assets['commons.js']) { 133 assets['commons.js'] = new CachedSource( 134 new ConcatSource(assets['commons.js'], GLOBAL_COMMON_MODULE_CACHE)); 135 } else if (assets['vendors.js']) { 136 assets['vendors.js'] = new CachedSource( 137 new ConcatSource(assets['vendors.js'], GLOBAL_COMMON_MODULE_CACHE)); 138 } 139 } 140 ); 141 }); 142 143 compiler.hooks.compilation.tap('Require', compilation => { 144 JavascriptModulesPlugin.getCompilationHooks(compilation).renderRequire.tap('renderRequire', 145 (source) => { 146 return process.env.DEVICE_LEVEL === 'rich' ? `var commonCachedModule =` + 147 ` globalThis["__common_module_cache__${process.env.hashProjectPath}"] ? ` + 148 `globalThis["__common_module_cache__${process.env.hashProjectPath}"]` + 149 `[moduleId]: null;\n` + 150 `if (commonCachedModule) { return commonCachedModule.exports; }\n` + 151 source.replace('// Execute the module function', 152 `function isCommonModue(moduleId) { 153 if (globalThis["webpackChunk${process.env.hashProjectPath}"]) { 154 const length = globalThis["webpackChunk${process.env.hashProjectPath}"].length; 155 switch (length) { 156 case 1: 157 return globalThis["webpackChunk${process.env.hashProjectPath}"][0][1][moduleId]; 158 case 2: 159 return globalThis["webpackChunk${process.env.hashProjectPath}"][0][1][moduleId] || 160 globalThis["webpackChunk${process.env.hashProjectPath}"][1][1][moduleId]; 161 } 162 } 163 return undefined; 164 }\n` + 165 `if (globalThis["__common_module_cache__${process.env.hashProjectPath}"]` + 166 ` && String(moduleId).indexOf("?name=") < 0 && isCommonModue(moduleId)) {\n` + 167 ` globalThis["__common_module_cache__${process.env.hashProjectPath}"]` + 168 `[moduleId] = module;\n}`) : source; 169 }); 170 }); 171 } 172} 173 174function addCacheFiles(entryFile, cachePath, entryPaths) { 175 const entryArray = []; 176 if (fs.existsSync(entryFile)) { 177 const oldArray = JSON.parse(fs.readFileSync(entryFile)); 178 oldArray.forEach(element => { 179 entryPaths.add(element); 180 }) 181 } else if (!fs.existsSync(cachePath)) { 182 mkDir(cachePath); 183 } 184 entryArray.push(...entryPaths); 185 fs.writeFileSync(entryFile, JSON.stringify(entryArray)); 186} 187 188const red = '\u001b[31m'; 189const yellow = '\u001b[33m'; 190const blue = '\u001b[34m'; 191const reset = '\u001b[39m'; 192 193const writeError = (buildPath, content) => { 194 fs.writeFile(path.resolve(buildPath, 'compile_error.log'), content, (err) => { 195 if (err) { 196 return console.error(err); 197 } 198 }); 199}; 200 201function printResult(buildPath) { 202 printWarning(); 203 printError(buildPath); 204 if (errorCount + warningCount + noteCount > 0 || process.env.abcCompileSuccess === 'false') { 205 let result; 206 const resultInfo = {}; 207 if (errorCount > 0) { 208 resultInfo.ERROR = errorCount; 209 result = 'FAIL '; 210 } else { 211 result = 'SUCCESS '; 212 } 213 214 if (process.env.abcCompileSuccess === 'false') { 215 result = 'FAIL '; 216 } 217 218 if (warningCount > 0) { 219 resultInfo.WARN = warningCount; 220 } 221 222 if (noteCount > 0) { 223 resultInfo.NOTE = noteCount; 224 } 225 if (result === 'SUCCESS ' && process.env.isPreview === 'true') { 226 printPreviewResult(resultInfo); 227 } else { 228 console.log(blue, 'COMPILE RESULT:' + result + JSON.stringify(resultInfo), reset); 229 } 230 } else { 231 if (process.env.isPreview === 'true') { 232 printPreviewResult(); 233 } else { 234 console.log(blue, 'COMPILE RESULT:SUCCESS ', reset); 235 } 236 } 237 clearArkCompileStatus(); 238} 239 240function clearArkCompileStatus() { 241 process.env.abcCompileSuccess = 'true'; 242} 243 244function printPreviewResult(resultInfo = "") { 245 let workerNum = Object.keys(cluster.workers).length; 246 if (workerNum === 0) { 247 printSuccessInfo(resultInfo); 248 } 249} 250 251function printSuccessInfo(resultInfo) { 252 if (resultInfo.length === 0) { 253 console.log(blue, 'COMPILE RESULT:SUCCESS ', reset); 254 } else { 255 console.log(blue, 'COMPILE RESULT:SUCCESS ' + JSON.stringify(resultInfo), reset); 256 } 257} 258 259function printWarning() { 260 if (mWarningCount > 0) { 261 const warnings = mStats.compilation.warnings; 262 const length = warnings.length; 263 for (let index = 0; index < length; index++) { 264 let message = warnings[index].message 265 if (message.match(/noteStart(([\s\S])*)noteEnd/) !== null) { 266 noteCount++; 267 if (isShowNote) { 268 console.info(' ' + message.match(/noteStart(([\s\S])*)noteEnd/)[1].trim(), reset, '\n') 269 } 270 } else if (message.match(/warnStart(([\s\S])*)warnEnd/) !== null) { 271 warningCount++; 272 if (isShowWarning) { 273 console.warn(yellow, message.match(/warnStart(([\s\S])*)warnEnd/)[1].trim(), reset, '\n') 274 } 275 } 276 } 277 if (mWarningCount > length) { 278 warningCount = warningCount + mWarningCount - length; 279 } 280 } 281} 282 283function printError(buildPath) { 284 if (mErrorCount > 0) { 285 const errors = mStats.compilation.errors; 286 const length = errors.length; 287 if (isShowError) { 288 let errorContent = ''; 289 for (let index = 0; index < length; index++) { 290 if (errors[index]) { 291 let message = errors[index].message 292 if (message) { 293 if (message.match(/errorStart(([\s\S])*)errorEnd/) !== null) { 294 const errorMessage = message.match(/errorStart(([\s\S])*)errorEnd/)[1]; 295 console.error(red, errorMessage.trim(), reset, '\n'); 296 } else { 297 const messageArrary = message.split('\n') 298 let logContent = '' 299 messageArrary.forEach(element => { 300 if (!(/^at/.test(element.trim()))) { 301 logContent = logContent + element + '\n' 302 } 303 }); 304 console.error(red, logContent, reset, '\n'); 305 } 306 errorCount ++; 307 errorContent += message 308 } 309 } 310 } 311 writeError(buildPath, errorContent); 312 } 313 } 314} 315 316function writeUseOSFiles() { 317 let oldInfo = ''; 318 if (!fs.existsSync(process.env.aceSoPath)) { 319 const parent = path.join(process.env.aceSoPath, '..'); 320 if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) { 321 mkDir(parent); 322 } 323 } else { 324 oldInfo = fs.readFileSync(process.env.aceSoPath, 'utf-8') + '\n'; 325 } 326 fs.writeFileSync(process.env.aceSoPath, oldInfo + Array.from(useOSFiles).join('\n')); 327} 328 329module.exports = ResultStates; 330