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