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 16var path = require('path') 17var fs = require('fs') 18var shell = require('shelljs'); 19const crypto = require("crypto") 20 21const red = '\u001b[31m'; 22const reset = '\u001b[39m'; 23const multiResourceBuild = {}; 24const systemModules = []; 25 26;(function readSystemModules() { 27 const systemModulesPath = path.resolve(__dirname,'../../api'); 28 if (fs.existsSync(systemModulesPath)) { 29 systemModules.push(...fs.readdirSync(systemModulesPath)); 30 } 31})(); 32 33function deleteFolderRecursive(url) { 34 let files = []; 35 if (fs.existsSync(url)) { 36 files = fs.readdirSync(url); 37 files.forEach(function(file) { 38 const curPath = path.join(url, file); 39 if (fs.statSync(curPath).isDirectory()) { 40 deleteFolderRecursive(curPath); 41 } else { 42 fs.unlinkSync(curPath); 43 } 44 }); 45 fs.rmdir(url, function(err) {}); 46 } 47} 48 49function readManifest(manifestFilePath) { 50 let manifest = {}; 51 try { 52 if (fs.existsSync(manifestFilePath)) { 53 const jsonString = fs.readFileSync(manifestFilePath).toString(); 54 manifest = JSON.parse(jsonString); 55 } 56 } catch (e) { 57 throw Error('\u001b[31m' + 'ERROR: the manifest.json file format is invalid.' + 58 '\u001b[39m').message; 59 } 60 return manifest; 61} 62 63function loadEntryObj(projectPath, device_level, abilityType, manifestFilePath) { 64 let entryObj = {}; 65 switch (abilityType) { 66 case 'page': 67 const appJSPath = path.resolve(projectPath, 'app.js'); 68 if (device_level === 'card') { 69 entryObj = addPageEntryObj(readManifest(manifestFilePath), projectPath); 70 } else { 71 if (!fs.existsSync(appJSPath)) { 72 throw Error(red + 'ERROR: missing app.js' + reset).message; 73 } 74 entryObj['./app'] = path.resolve(projectPath, './app.js?entry'); 75 } 76 break; 77 case 'form': 78 entryObj = addPageEntryObj(readManifest(manifestFilePath), projectPath); 79 entryObj[`./${abilityType}`] = path.resolve(projectPath, `./${abilityType}.js?entry`); 80 break; 81 case 'testrunner': 82 break; 83 default: 84 entryObj[`./${abilityType}`] = path.resolve(projectPath, `./${abilityType}.js?entry`); 85 break; 86 } 87 return entryObj; 88} 89 90function addPageEntryObj(manifest, projectPath) { 91 let entryObj = {}; 92 const pages = manifest.pages; 93 if (pages === undefined) { 94 throw Error('ERROR: missing pages').message; 95 } 96 pages.forEach((element) => { 97 const sourcePath = element; 98 const hmlPath = path.join(projectPath, sourcePath + '.hml'); 99 const aceSuperVisualPath = process.env.aceSuperVisualPath || ''; 100 const visualPath = path.join(aceSuperVisualPath, sourcePath + '.visual'); 101 const isHml = fs.existsSync(hmlPath); 102 const isVisual = fs.existsSync(visualPath); 103 if (isHml && isVisual) { 104 throw Error(red + 'ERROR: ' + sourcePath + ' cannot both have hml && visual').message; 105 } else if (isHml) { 106 entryObj['./' + element] = path.resolve(projectPath, './' + sourcePath + '.hml?entry'); 107 } else if (isVisual) { 108 entryObj['./' + element] = path.resolve(aceSuperVisualPath, './' + sourcePath + 109 '.visual?entry'); 110 } 111 }) 112 return entryObj; 113} 114 115function compileCardModule(env) { 116 if (process.env.aceModuleJsonPath && fs.existsSync(process.env.aceModuleJsonPath)) { 117 const moduleJsonConfig = JSON.parse(fs.readFileSync(process.env.aceModuleJsonPath).toString()); 118 if (moduleJsonConfig.module && 119 (moduleJsonConfig.module.uiSyntax === 'ets' || moduleJsonConfig.module.language === 'ets')) { 120 process.env.DEVICE_LEVEL = 'card'; 121 } else if (validateCardModule(moduleJsonConfig) && !process.env.compileCardModule) { 122 process.env.compileCardModule = true; 123 const cmd = `webpack --config webpack.rich.config.js --env compilerType=${env.compilerType} ` + 124 `DEVICE_LEVEL=card aceModuleRoot=${process.env.projectPath} ` + 125 `aceModuleJsonPath=${process.env.aceModuleJsonPath} aceProfilePath=${process.env.aceProfilePath} ` + 126 `watchMode=${process.env.watchMode} cachePath=${process.env.cachePath} ` + 127 `aceModuleBuild=${process.env.buildPath}`; 128 shell.exec(cmd, (err) => { 129 if (err) { 130 throw Error(err).message; 131 } 132 }) 133 } 134 } 135} 136 137function validateCardModule(moduleJsonConfig) { 138 if (moduleJsonConfig.module && moduleJsonConfig.module.extensionAbilities) { 139 for (let i = 0; i < moduleJsonConfig.module.extensionAbilities.length; i++) { 140 const extensionAbility = moduleJsonConfig.module.extensionAbilities[i]; 141 if (extensionAbility.type && extensionAbility.type === 'form') { 142 return true; 143 } 144 } 145 } 146 return false; 147} 148 149function hashProjectPath(projectPath) { 150 const hash = crypto.createHash('sha256') 151 hash.update(projectPath.toString()) 152 process.env.hashProjectPath = "_" + hash.digest('hex'); 153} 154 155function checkMultiResourceBuild(aceBuildJson) { 156 if (aceBuildJson && fs.existsSync(aceBuildJson)) { 157 try { 158 const content = JSON.parse(fs.readFileSync(aceBuildJson)); 159 if (content["multiResourceBuild"]) { 160 multiResourceBuild.value = content["multiResourceBuild"] 161 } 162 if (content["projectRootPath"]) { 163 process.env.projectRootPath = content["projectRootPath"] 164 } 165 } catch (error) { 166 } 167 } 168} 169 170function readWorkerFile() { 171 const workerEntryObj = {}; 172 if (process.env.aceBuildJson && fs.existsSync(process.env.aceBuildJson)) { 173 const workerConfig = JSON.parse(fs.readFileSync(process.env.aceBuildJson).toString()); 174 if(workerConfig.workers) { 175 workerConfig.workers.forEach(worker => { 176 if (!/\.(js)$/.test(worker)) { 177 worker += '.js'; 178 } 179 const relativePath = path.relative(process.env.projectPath, worker); 180 if (filterWorker(relativePath)) { 181 workerEntryObj[relativePath.replace(/\.(ts|js)$/,'').replace(/\\/g, '/')] = worker; 182 } 183 }) 184 return workerEntryObj; 185 } 186 } 187 return null; 188} 189 190function filterWorker(workerPath) { 191 return /\.(ts|js)$/.test(workerPath); 192} 193 194function compareCache(cachePath) { 195 const entryFile = path.join(cachePath, 'entry.json'); 196 const cssFile = process.env.watchCSSFiles; 197 198 let files = []; 199 let cssObject = {}; 200 if (fs.existsSync(cssFile)) { 201 cssObject = JSON.parse(fs.readFileSync(cssFile)); 202 if (cssObject['clear'] === true) { 203 deleteFolderRecursive(cachePath); 204 return; 205 } 206 Object.keys(cssObject).forEach(key => { 207 if (key !== 'entry') { 208 files.push(key); 209 } 210 }) 211 } 212 213 if (fs.existsSync(entryFile)) { 214 files = files.concat(JSON.parse(fs.readFileSync(entryFile))); 215 } 216 217 for(let file of files) { 218 if (!fs.existsSync(file)) { 219 deleteFolderRecursive(cachePath); 220 break; 221 } else if (cssObject['atime'] && cssObject['atime'][file]) { 222 if (cssObject['atime'][file] !== fs.statSync(file).atime.toString()) { 223 deleteFolderRecursive(cachePath); 224 break; 225 } 226 } 227 } 228} 229 230function parseAbilityName(abilityType, projectPath) { 231 if (abilityType === 'page') { 232 return path.resolve(__dirname, projectPath, 'app.js'); 233 } else { 234 return path.resolve(__dirname, projectPath, abilityType + '.js'); 235 } 236} 237 238module.exports = { 239 deleteFolderRecursive: deleteFolderRecursive, 240 readManifest: readManifest, 241 loadEntryObj: loadEntryObj, 242 compileCardModule: compileCardModule, 243 hashProjectPath: hashProjectPath, 244 checkMultiResourceBuild: checkMultiResourceBuild, 245 multiResourceBuild: multiResourceBuild, 246 readWorkerFile: readWorkerFile, 247 compareCache: compareCache, 248 systemModules: systemModules, 249 parseAbilityName: parseAbilityName 250};