1/* 2 * Copyright (c) 2020 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'); 18const crypto = require('crypto'); 19const JSON5 = require('json5'); 20 21const { 22 readFile, 23 writeFileSync, 24 resourcesRawfile, 25 getStoredFileInfo 26} = require('./lib/utils'); 27 28const { 29 COLD_RELOAD_MODE, 30 ES2ABC, 31 FAIL, 32 TEST_RUNNER_DIR_SET, 33 TS2ABC, 34 WORKERS_DIR 35} = require('./lib/pre_define'); 36 37const { 38 checkAotConfig 39} = require('./lib/gen_aot'); 40 41const { 42 configure, 43 getLogger 44} = require('log4js'); 45 46configure({ 47 appenders: { 'ETS': {type: 'stderr', layout: {type: 'messagePassThrough'}}}, 48 categories: {'default': {appenders: ['ETS'], level: 'info'}} 49}); 50const logger = getLogger('ETS'); 51 52let staticPreviewPage = process.env.aceStaticPreview; 53let aceCompileMode = process.env.aceCompileMode || 'page'; 54const abilityConfig = { 55 abilityType: process.env.abilityType || 'page', 56 abilityEntryFile: null, 57 projectAbilityPath: [], 58 testRunnerFile: [] 59}; 60const projectConfig = {}; 61const resources = { 62 app: {}, 63 sys: {} 64}; 65const systemModules = []; 66const abilityPagesFullPath = []; 67let globalModulePaths = []; 68let sdkConfigs = []; 69let defaultSdkConfigs = []; 70let extendSdkConfigs = []; 71let sdkConfigPrefix = 'ohos|system|kit|arkts'; 72let ohosSystemModulePaths = []; 73let ohosSystemModuleSubDirPaths = []; 74let allModulesPaths = []; 75 76function initProjectConfig(projectConfig) { 77 projectConfig.entryObj = {}; 78 projectConfig.cardObj = {}; 79 projectConfig.projectPath = projectConfig.projectPath || process.env.aceModuleRoot || 80 path.join(process.cwd(), 'sample'); 81 projectConfig.buildPath = projectConfig.buildPath || process.env.aceModuleBuild || 82 path.resolve(projectConfig.projectPath, 'build'); 83 projectConfig.aceModuleBuild = projectConfig.buildPath; // To be compatible with both webpack and rollup 84 projectConfig.manifestFilePath = projectConfig.manifestFilePath || process.env.aceManifestPath || 85 path.join(projectConfig.projectPath, 'manifest.json'); 86 projectConfig.aceProfilePath = projectConfig.aceProfilePath || process.env.aceProfilePath; 87 projectConfig.aceModuleJsonPath = projectConfig.aceModuleJsonPath || process.env.aceModuleJsonPath; 88 projectConfig.aceSuperVisualPath = projectConfig.aceSuperVisualPath || 89 process.env.aceSuperVisualPath; 90 projectConfig.hashProjectPath = projectConfig.hashProjectPath || 91 hashProjectPath(projectConfig.projectPath); 92 projectConfig.aceBuildJson = projectConfig.aceBuildJson || process.env.aceBuildJson; 93 projectConfig.cachePath = projectConfig.cachePath || process.env.cachePath || 94 path.resolve(__dirname, 'node_modules/.cache'); 95 projectConfig.aceSoPath = projectConfig.aceSoPath || process.env.aceSoPath; 96 projectConfig.xtsMode = /ets_loader_ark$/.test(__dirname); 97 projectConfig.localPropertiesPath = projectConfig.localPropertiesPath || process.env.localPropertiesPath; 98 projectConfig.projectProfilePath = projectConfig.projectProfilePath || process.env.projectProfilePath; 99 projectConfig.isPreview = projectConfig.isPreview || process.env.isPreview === 'true'; 100 projectConfig.compileMode = projectConfig.compileMode || process.env.compileMode || 'jsbundle'; 101 projectConfig.runtimeOS = projectConfig.runtimeOS || process.env.runtimeOS || 'default'; 102 projectConfig.sdkInfo = projectConfig.sdkInfo || process.env.sdkInfo || 'default'; 103 projectConfig.compileHar = false; 104 projectConfig.compileShared = false; 105 projectConfig.splitCommon = false; 106 projectConfig.checkEntry = projectConfig.checkEntry || process.env.checkEntry; 107 projectConfig.obfuscateHarType = projectConfig.obfuscateHarType || process.env.obfuscate; 108 projectConfig.packageDir = 'node_modules'; 109 projectConfig.packageJson = 'package.json'; 110 projectConfig.packageManagerType = 'npm'; 111 projectConfig.cardEntryObj = {}; 112 projectConfig.compilerTypes = []; 113 projectConfig.isCrossplatform = projectConfig.isCrossplatform || false; 114 projectConfig.enableDebugLine = projectConfig.enableDebugLine || process.env.enableDebugLine || false; 115 projectConfig.bundleType = projectConfig.bundleType || process.env.bundleType || ''; 116 projectConfig.optLazyForEach = false; 117 projectConfig.hspResourcesMap = false; 118 projectConfig.useArkoala = false; 119 projectConfig.resetBundleName = false; 120 projectConfig.integratedHsp = false; 121 projectConfig.useTsHar = false; 122} 123 124function loadEntryObj(projectConfig) { 125 let manifest = {}; 126 initMain(); 127 initProjectConfig(projectConfig); 128 loadBuildJson(); 129 if (process.env.aceManifestPath && aceCompileMode === 'page') { 130 setEntryFile(projectConfig); 131 setFaTestRunnerFile(projectConfig); 132 } 133 if (process.env.aceModuleJsonPath) { 134 setIntentEntryPages(projectConfig); 135 setAbilityPages(projectConfig); 136 setStageTestRunnerFile(projectConfig); 137 loadNavigationConfig(aceBuildJson); 138 } 139 140 if (staticPreviewPage) { 141 projectConfig.entryObj['./' + staticPreviewPage] = projectConfig.projectPath + path.sep + 142 staticPreviewPage + '.ets?entry'; 143 } else if (abilityConfig.abilityType === 'page') { 144 if (fs.existsSync(projectConfig.manifestFilePath)) { 145 const jsonString = fs.readFileSync(projectConfig.manifestFilePath).toString(); 146 manifest = JSON.parse(jsonString); 147 if (manifest && manifest.minPlatformVersion) { 148 process.env.minPlatformVersion = manifest.minPlatformVersion; 149 partialUpdateController(manifest.minPlatformVersion); 150 } 151 projectConfig.pagesJsonFileName = 'config.json'; 152 } else if (projectConfig.aceModuleJsonPath && fs.existsSync(projectConfig.aceModuleJsonPath)) { 153 process.env.compileMode = 'moduleJson'; 154 buildManifest(manifest, projectConfig.aceModuleJsonPath); 155 } else { 156 throw Error('\u001b[31m ERROR: the manifest file ' + projectConfig.manifestFilePath.replace(/\\/g, '/') + 157 ' or module.json is lost or format is invalid. \u001b[39m').message; 158 } 159 if (!projectConfig.compileHar) { 160 if (manifest.pages) { 161 const pages = manifest.pages; 162 pages.forEach((element) => { 163 const sourcePath = element.replace(/^\.\/ets\//, ''); 164 const fileName = path.resolve(projectConfig.projectPath, sourcePath + '.ets'); 165 if (fs.existsSync(fileName)) { 166 projectConfig.entryObj['./' + sourcePath] = fileName + '?entry'; 167 } else { 168 throw Error(`\u001b[31m ERROR: page '${fileName.replace(/\\/g, '/')}' does not exist. \u001b[39m`) 169 .message; 170 } 171 }); 172 } else { 173 throw Error('\u001b[31m ERROR: missing pages attribute in ' + 174 projectConfig.manifestFilePath.replace(/\\/g, '/') + 175 '. \u001b[39m').message; 176 } 177 } 178 } 179} 180 181function loadNavigationConfig(aceBuildJson) { 182 if (aceBuildJson && aceBuildJson.routerMap && Array.isArray(aceBuildJson.routerMap)) { 183 aceBuildJson.routerMap.forEach((item) => { 184 if (item.pageSourceFile && item.name && item.buildFunction) { 185 const filePath = path.resolve(item.pageSourceFile); 186 const storedFileInfo = getStoredFileInfo(); 187 if (storedFileInfo.routerInfo.has(filePath)) { 188 storedFileInfo.routerInfo.get(filePath).push({name: item.name, buildFunction: item.buildFunction}); 189 } else { 190 storedFileInfo.routerInfo.set(filePath, [{name: item.name, buildFunction: item.buildFunction}]); 191 } 192 } 193 }); 194 } 195} 196 197function buildManifest(manifest, aceConfigPath) { 198 try { 199 const moduleConfigJson = JSON.parse(fs.readFileSync(aceConfigPath).toString()); 200 manifest.type = process.env.abilityType; 201 if (moduleConfigJson && moduleConfigJson.app && moduleConfigJson.app.minAPIVersion) { 202 if (moduleConfigJson.module && moduleConfigJson.module.metadata) { 203 partialUpdateController(moduleConfigJson.app.minAPIVersion, moduleConfigJson.module.metadata, 204 moduleConfigJson.module.type); 205 stageOptimization(moduleConfigJson.module.metadata); 206 } else { 207 partialUpdateController(moduleConfigJson.app.minAPIVersion); 208 } 209 } 210 if (moduleConfigJson.module) { 211 switch (moduleConfigJson.module.type) { 212 case 'har': 213 projectConfig.compileHar = true; 214 getPackageJsonEntryPath(); 215 break; 216 case 'shared': 217 projectConfig.compileShared = true; 218 getPackageJsonEntryPath(); 219 manifest.pages = getPages(moduleConfigJson); 220 break; 221 default: 222 manifest.pages = getPages(moduleConfigJson); 223 break; 224 } 225 } else { 226 throw Error('\u001b[31m' + 227 'BUIDERROR: the config.json file miss key word module || module[abilities].' + 228 '\u001b[39m').message; 229 } 230 } catch (e) { 231 if (/BUIDERROR/.test(e)) { 232 throw Error(e.replace('BUIDERROR', 'ERROR')).message; 233 } else { 234 throw Error('\x1B[31m' + 'ERROR: the module.json file is lost or format is invalid.' + 235 '\x1B[39m').message; 236 } 237 } 238} 239 240function getPackageJsonEntryPath() { 241 const rootPackageJsonPath = path.resolve(projectConfig.projectPath, '../../../' + projectConfig.packageJson); 242 if (fs.existsSync(rootPackageJsonPath)) { 243 let rootPackageJsonContent; 244 try { 245 rootPackageJsonContent = (projectConfig.packageManagerType === 'npm' ? 246 JSON : JSON5).parse(fs.readFileSync(rootPackageJsonPath, 'utf-8')); 247 } catch (e) { 248 throw Error('\u001b[31m' + 'BUIDERROR: ' + rootPackageJsonPath + ' format is invalid.' + '\u001b[39m').message; 249 } 250 if (rootPackageJsonContent) { 251 if (rootPackageJsonContent.module) { 252 getEntryPath(rootPackageJsonContent.module, rootPackageJsonPath); 253 } else if (rootPackageJsonContent.main) { 254 getEntryPath(rootPackageJsonContent.main, rootPackageJsonPath); 255 } else { 256 getEntryPath('', rootPackageJsonPath); 257 } 258 } else if (projectConfig.compileHar) { 259 throw Error('\u001b[31m' + 'BUIDERROR: lack message in ' + projectConfig.packageJson + '.' + 260 '\u001b[39m').message; 261 } 262 } 263} 264 265function supportSuffix(mainEntryPath) { 266 if (fs.existsSync(path.join(mainEntryPath, 'index.ets'))) { 267 mainEntryPath = path.join(mainEntryPath, 'index.ets'); 268 } else if (fs.existsSync(path.join(mainEntryPath, 'index.ts'))) { 269 mainEntryPath = path.join(mainEntryPath, 'index.ts'); 270 } else if (fs.existsSync(path.join(mainEntryPath, 'index.js'))) { 271 mainEntryPath = path.join(mainEntryPath, 'index.js'); 272 } else if (projectConfig.compileHar) { 273 throw Error('\u001b[31m' + 'BUIDERROR: not find entry file in ' + projectConfig.packageJson + 274 '.' + '\u001b[39m').message; 275 } 276 return mainEntryPath; 277} 278 279function supportExtName(mainEntryPath) { 280 if (path.extname(mainEntryPath) === '') { 281 if (fs.existsSync(mainEntryPath + '.ets')) { 282 mainEntryPath = mainEntryPath + '.ets'; 283 } else if (fs.existsSync(mainEntryPath + '.ts')) { 284 mainEntryPath = mainEntryPath + '.ts'; 285 } else if (fs.existsSync(mainEntryPath + '.js')) { 286 mainEntryPath = mainEntryPath + '.js'; 287 } 288 } 289 return mainEntryPath; 290} 291 292function getEntryPath(entryPath, rootPackageJsonPath) { 293 let mainEntryPath = path.resolve(rootPackageJsonPath, '../', entryPath); 294 if (fs.existsSync(mainEntryPath) && fs.statSync(mainEntryPath).isDirectory()) { 295 mainEntryPath = supportSuffix(mainEntryPath); 296 } else { 297 mainEntryPath = supportExtName(mainEntryPath); 298 } 299 if (fs.existsSync(mainEntryPath) && fs.statSync(mainEntryPath).isFile()) { 300 const entryKey = path.relative(projectConfig.projectPath, mainEntryPath); 301 projectConfig.entryObj[entryKey] = mainEntryPath; 302 abilityPagesFullPath.push(path.resolve(mainEntryPath).toLowerCase()); 303 } else if (projectConfig.compileHar) { 304 throw Error('\u001b[31m' + `BUIDERROR: not find entry file in ${rootPackageJsonPath}.` + '\u001b[39m').message; 305 } 306} 307 308function stageOptimization(metadata) { 309 if (Array.isArray(metadata) && metadata.length) { 310 metadata.some(item => { 311 if (item.name && item.name === 'USE_COMMON_CHUNK' && 312 item.value && item.value === 'true') { 313 projectConfig.splitCommon = true; 314 return true; 315 } 316 }); 317 } 318} 319 320function getPages(configJson) { 321 const pages = []; 322 let pagesJsonFileName = ''; 323 // pages is not necessary in stage 324 if (process.env.compileMode === 'moduleJson' && configJson.module && configJson.module.pages) { 325 pagesJsonFileName = `${configJson.module.pages.replace(/\$profile\:/, '')}.json`; 326 } else { 327 return pages; 328 } 329 const modulePagePath = path.resolve(projectConfig.aceProfilePath, pagesJsonFileName); 330 if (fs.existsSync(modulePagePath)) { 331 try { 332 const pagesConfig = JSON.parse(fs.readFileSync(modulePagePath, 'utf-8')); 333 if (pagesConfig && pagesConfig.src) { 334 projectConfig.pagesJsonFileName = pagesJsonFileName; 335 return pagesConfig.src; 336 } 337 } catch (e) { 338 throw Error('\x1B[31m' + `BUIDERROR: the ${modulePagePath} file format is invalid.` + 339 '\x1B[39m').message; 340 } 341 } 342 return pages; 343} 344 345function setEntryFile(projectConfig) { 346 const entryFileName = abilityConfig.abilityType === 'page' ? 'app' : abilityConfig.abilityType; 347 const extendFile = entryFileName === 'app' ? '.ets' : '.ts'; 348 const entryFileRealPath = entryFileName + extendFile; 349 const entryFilePath = path.resolve(projectConfig.projectPath, entryFileRealPath); 350 abilityConfig.abilityEntryFile = entryFilePath; 351 if (!fs.existsSync(entryFilePath) && aceCompileMode === 'page') { 352 throw Error(`\u001b[31m ERROR: missing ${entryFilePath.replace(/\\/g, '/')}. \u001b[39m`).message; 353 } 354 projectConfig.entryObj[`./${entryFileName}`] = entryFilePath + '?entry'; 355} 356 357function setIntentEntryPages(projectConfig) { 358 projectConfig.intentEntry.forEach(pages => { 359 const entryKey = path.relative(projectConfig.projectPath, pages).replace(/\.(ets|ts|js)$/, ''); 360 projectConfig.entryObj[entryKey] = pages; 361 if (/\.ets$/.test(pages)) { 362 abilityPagesFullPath.push(path.resolve(pages).toLowerCase()); 363 } 364 }); 365} 366 367function setAbilityPages(projectConfig) { 368 let abilityPages = []; 369 if (projectConfig.aceModuleJsonPath && fs.existsSync(projectConfig.aceModuleJsonPath)) { 370 const moduleJson = JSON.parse(fs.readFileSync(projectConfig.aceModuleJsonPath).toString()); 371 abilityPages = readAbilityEntrance(moduleJson); 372 setAbilityFile(projectConfig, abilityPages); 373 setBundleModuleInfo(projectConfig, moduleJson); 374 } 375} 376 377function setTestRunnerFile(projectConfig, isStageBased) { 378 const index = projectConfig.projectPath.split(path.sep).join('/').lastIndexOf('\/'); 379 TEST_RUNNER_DIR_SET.forEach((dir) => { 380 const projectPath = isStageBased ? projectConfig.projectPath : projectConfig.projectPath.substring(0, index + 1); 381 const testRunnerPath = path.resolve(projectPath, dir); 382 if (fs.existsSync(testRunnerPath) && folderExistsCaseSensitive(testRunnerPath)) { 383 const testRunnerFiles = []; 384 readFile(testRunnerPath, testRunnerFiles); 385 testRunnerFiles.forEach((item) => { 386 if (/\.(ts|js|ets)$/.test(item)) { 387 if (/\.ets$/.test(item)) { 388 abilityPagesFullPath.push(path.resolve(item).toLowerCase()); 389 } 390 const relativePath = path.relative(testRunnerPath, item).replace(/\.(ts|js|ets)$/, ''); 391 if (isStageBased) { 392 projectConfig.entryObj[`./${dir}/${relativePath}`] = item; 393 } else { 394 projectConfig.entryObj[`../${dir}/${relativePath}`] = item; 395 } 396 abilityConfig.testRunnerFile.push(item); 397 } 398 }); 399 } 400 }); 401} 402 403function folderExistsCaseSensitive(folderPath) { 404 try { 405 const folders = fs.readdirSync(path.dirname(folderPath), { withFileTypes: true }) 406 .filter(entry => entry.isDirectory()) 407 .map(entry => entry.name); 408 const targetFolderName = path.basename(folderPath); 409 return folders.includes(targetFolderName); 410 } catch (error) { 411 return false; 412 } 413} 414 415function setFaTestRunnerFile(projectConfig) { 416 setTestRunnerFile(projectConfig, false); 417} 418 419function setStageTestRunnerFile(projectConfig) { 420 setTestRunnerFile(projectConfig, true); 421} 422 423function setBundleModuleInfo(projectConfig, moduleJson) { 424 if (moduleJson.module) { 425 projectConfig.moduleName = moduleJson.module.name; 426 } 427 if (moduleJson.app) { 428 projectConfig.bundleName = moduleJson.app.bundleName; 429 } 430} 431 432function setAbilityFile(projectConfig, abilityPages) { 433 abilityPages.forEach(abilityPath => { 434 const projectAbilityPath = path.resolve(projectConfig.projectPath, '../', abilityPath); 435 if (path.isAbsolute(abilityPath)) { 436 abilityPath = '.' + abilityPath.slice(projectConfig.projectPath.length); 437 } 438 const entryPageKey = abilityPath.replace(/^\.\/ets\//, './').replace(/\.ts$/, '').replace(/\.ets$/, ''); 439 if (fs.existsSync(projectAbilityPath)) { 440 abilityConfig.projectAbilityPath.push(projectAbilityPath); 441 projectConfig.entryObj[entryPageKey] = projectAbilityPath + '?entry'; 442 } else { 443 throw Error( 444 `\u001b[31m ERROR: srcEntry file '${projectAbilityPath.replace(/\\/g, '/')}' does not exist. \u001b[39m` 445 ).message; 446 } 447 }); 448} 449 450function readAbilityEntrance(moduleJson) { 451 const abilityPages = []; 452 if (moduleJson.module) { 453 const moduleSrcEntrance = moduleJson.module.srcEntrance; 454 const moduleSrcEntry = moduleJson.module.srcEntry; 455 if (moduleSrcEntry) { 456 abilityPages.push(moduleSrcEntry); 457 abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, moduleSrcEntry)); 458 } else if (moduleSrcEntrance) { 459 abilityPages.push(moduleSrcEntrance); 460 abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, moduleSrcEntrance)); 461 } 462 if (moduleJson.module.abilities && moduleJson.module.abilities.length > 0) { 463 setEntrance(moduleJson.module.abilities, abilityPages); 464 } 465 if (moduleJson.module.extensionAbilities && moduleJson.module.extensionAbilities.length > 0) { 466 setEntrance(moduleJson.module.extensionAbilities, abilityPages); 467 setCardPages(moduleJson.module.extensionAbilities); 468 } 469 } 470 return abilityPages; 471} 472 473function setEntrance(abilityConfig, abilityPages) { 474 if (abilityConfig && abilityConfig.length > 0) { 475 abilityConfig.forEach(ability => { 476 if (ability.srcEntry) { 477 abilityPages.push(ability.srcEntry); 478 abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, ability.srcEntry)); 479 } else if (ability.srcEntrance) { 480 abilityPages.push(ability.srcEntrance); 481 abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, ability.srcEntrance)); 482 } 483 }); 484 } 485} 486 487function setCardPages(extensionAbilities) { 488 if (extensionAbilities && extensionAbilities.length > 0) { 489 extensionAbilities.forEach(extensionAbility => { 490 if (extensionAbility.metadata) { 491 extensionAbility.metadata.forEach(metadata => { 492 if (metadata.resource) { 493 readCardResource(metadata.resource); 494 } 495 }); 496 } 497 }); 498 } 499} 500 501function readCardResource(resource) { 502 const cardJsonFileName = `${resource.replace(/\$profile\:/, '')}.json`; 503 const modulePagePath = path.resolve(projectConfig.aceProfilePath, cardJsonFileName); 504 if (fs.existsSync(modulePagePath)) { 505 const cardConfig = JSON.parse(fs.readFileSync(modulePagePath, 'utf-8')); 506 if (cardConfig.forms) { 507 cardConfig.forms.forEach(form => { 508 readCardForm(form); 509 }); 510 } 511 } 512} 513 514function readCardForm(form) { 515 if ((form.type && form.type === 'eTS') || 516 (form.uiSyntax && form.uiSyntax === 'arkts')) { 517 const sourcePath = form.src.replace(/\.ets$/, ''); 518 const cardPath = path.resolve(projectConfig.projectPath, '..', sourcePath + '.ets'); 519 if (cardPath && fs.existsSync(cardPath)) { 520 projectConfig.entryObj['../' + sourcePath] = cardPath + '?entry'; 521 projectConfig.cardEntryObj['../' + sourcePath] = cardPath; 522 projectConfig.cardObj[cardPath] = sourcePath.replace(/^\.\//, ''); 523 } 524 } 525} 526 527function getAbilityFullPath(projectPath, abilityPath) { 528 const finalPath = path.resolve(path.resolve(projectPath, '../'), abilityPath); 529 if (fs.existsSync(finalPath)) { 530 return finalPath.toLowerCase(); 531 } else { 532 return path.resolve(abilityPath).toLowerCase(); 533 } 534} 535 536function loadWorker(projectConfig, workerFileEntry) { 537 if (workerFileEntry) { 538 projectConfig.entryObj = Object.assign(projectConfig.entryObj, workerFileEntry); 539 } else { 540 const workerPath = path.resolve(projectConfig.projectPath, WORKERS_DIR); 541 if (fs.existsSync(workerPath)) { 542 const workerFiles = []; 543 readFile(workerPath, workerFiles); 544 workerFiles.forEach((item) => { 545 if (/\.(ts|js|ets)$/.test(item)) { 546 const relativePath = path.relative(workerPath, item) 547 .replace(/\.(ts|js|ets)$/, '').replace(/\\/g, '/'); 548 projectConfig.entryObj[`./${WORKERS_DIR}/` + relativePath] = item; 549 abilityPagesFullPath.push(path.resolve(item).toLowerCase()); 550 } 551 }); 552 } 553 } 554} 555 556let aceBuildJson = {}; 557function loadBuildJson() { 558 if (projectConfig.aceBuildJson && fs.existsSync(projectConfig.aceBuildJson)) { 559 aceBuildJson = JSON.parse(fs.readFileSync(projectConfig.aceBuildJson).toString()); 560 } 561 if (aceBuildJson.packageManagerType === 'ohpm') { 562 projectConfig.packageManagerType = 'ohpm'; 563 projectConfig.packageDir = 'oh_modules'; 564 projectConfig.packageJson = 'oh-package.json5'; 565 } 566 // add intent framework entry file 567 projectConfig.intentEntry = aceBuildJson.compileEntry || []; 568} 569 570function initBuildInfo() { 571 projectConfig.projectRootPath = aceBuildJson.projectRootPath; 572 if (projectConfig.compileHar && aceBuildJson.moduleName && 573 aceBuildJson.modulePathMap[aceBuildJson.moduleName]) { 574 projectConfig.moduleRootPath = aceBuildJson.modulePathMap[aceBuildJson.moduleName]; 575 } 576} 577 578function readWorkerFile() { 579 const workerFileEntry = {}; 580 if (aceBuildJson.workers) { 581 aceBuildJson.workers.forEach(worker => { 582 if (!/\.(ts|js|ets)$/.test(worker)) { 583 throw Error( 584 '\u001b[31mArkTS:ERROR: File: ' + worker + '.' + '\n' + 585 " The worker file can only be an '.ets', '.ts', or '.js' file.\u001b[39m" 586 ).message; 587 } 588 const relativePath = path.relative(projectConfig.projectPath, worker); 589 if (filterWorker(relativePath)) { 590 const workerKey = relativePath.replace(/\.(ts|js)$/, '').replace(/\\/g, '/'); 591 if (workerFileEntry[workerKey]) { 592 throw Error( 593 '\u001b[31m ERROR: The worker file cannot use the same file name: \n' + 594 workerFileEntry[workerKey] + '\n' + worker + '\u001b[39m' 595 ).message; 596 } else { 597 workerFileEntry[workerKey] = worker; 598 abilityPagesFullPath.push(path.resolve(workerFileEntry[workerKey]).toLowerCase()); 599 } 600 } 601 }); 602 return workerFileEntry; 603 } 604 return null; 605} 606 607function readPatchConfig() { 608 if (aceBuildJson.patchConfig) { 609 projectConfig.hotReload = process.env.watchMode === 'true' && !projectConfig.isPreview; 610 projectConfig.coldReload = aceBuildJson.patchConfig.mode === COLD_RELOAD_MODE ? true : false; 611 // The "isFirstBuild" field indicates whether it is the first compilation of cold reload mode 612 // It is determined by hvigor and passed via env 613 projectConfig.isFirstBuild = process.env.isFirstBuild === 'false' ? false : true; 614 projectConfig.patchAbcPath = aceBuildJson.patchConfig.patchAbcPath; 615 projectConfig.changedFileList = aceBuildJson.patchConfig.changedFileList ? 616 aceBuildJson.patchConfig.changedFileList : path.join(projectConfig.cachePath, 'changedFileList.json'); 617 if (projectConfig.hotReload) { 618 writeFileSync(projectConfig.changedFileList, JSON.stringify({ 619 modifiedFiles: [], 620 removedFiles: [] 621 })); 622 } 623 } 624} 625 626function filterWorker(workerPath) { 627 return /\.(ts|js|ets)$/.test(workerPath); 628} 629 630;(function initSystemResource() { 631 const sysResourcePath = path.resolve(__dirname, './sysResource.js'); 632 if (fs.existsSync(sysResourcePath)) { 633 resources.sys = require(sysResourcePath).sys; 634 } 635 if (process.env.externalApiPaths) { 636 const sysResourceExtPath = path.resolve(__dirname, process.env.externalApiPaths, 'sysResource.js'); 637 if (fs.existsSync(sysResourceExtPath)) { 638 Object.entries(require(sysResourceExtPath).sys).forEach(([key, value]) => { 639 if (key in resources.sys) { 640 Object.assign(resources.sys[key], value); 641 } 642 }); 643 } 644 } 645})(); 646 647;(function readSystemModules() { 648 const apiDirPath = path.resolve(__dirname, '../../api'); 649 const arktsDirPath = path.resolve(__dirname, '../../arkts'); 650 const kitsDirPath = path.resolve(__dirname, '../../kits'); 651 const systemModulePathArray = [apiDirPath]; 652 if (!process.env.isFaMode) { 653 systemModulePathArray.push(arktsDirPath, kitsDirPath); 654 } 655 systemModulePathArray.forEach(systemModulesPath => { 656 if (fs.existsSync(systemModulesPath)) { 657 globalModulePaths.push(systemModulesPath); 658 const modulePaths = []; 659 readFile(systemModulesPath, modulePaths); 660 systemModules.push(...fs.readdirSync(systemModulesPath)); 661 ohosSystemModulePaths.push(...modulePaths); 662 const moduleSubdir = modulePaths.filter(filePath => { 663 const dirName = path.dirname(filePath); 664 return !(dirName === apiDirPath || dirName === arktsDirPath || dirName === kitsDirPath); 665 }).map(filePath => { 666 return filePath 667 .replace(apiDirPath, '') 668 .replace(arktsDirPath, '') 669 .replace(kitsDirPath, '') 670 .replace(/(^\\)|(.d.e?ts$)/g, '') 671 .replace(/\\/g, '/'); 672 }); 673 ohosSystemModuleSubDirPaths.push(...moduleSubdir); 674 allModulesPaths.push(...modulePaths); 675 } 676 }); 677 defaultSdkConfigs = [ 678 { 679 'apiPath': systemModulePathArray, 680 'prefix': '@ohos' 681 }, { 682 'apiPath': systemModulePathArray, 683 'prefix': '@system' 684 }, { 685 'apiPath': systemModulePathArray, 686 'prefix': '@arkts' 687 } 688 ]; 689 const externalApiPathStr = process.env.externalApiPaths || ''; 690 const externalApiPaths = externalApiPathStr.split(path.delimiter); 691 collectExternalModules(externalApiPaths); 692 sdkConfigs = [...defaultSdkConfigs, ...extendSdkConfigs]; 693})(); 694 695function collectExternalModules(sdkPaths) { 696 for (let i = 0; i < sdkPaths.length; i++) { 697 const sdkPath = sdkPaths[i]; 698 const sdkConfigPath = path.resolve(sdkPath, 'sdkConfig.json'); 699 if (!fs.existsSync(sdkConfigPath)) { 700 continue; 701 } 702 const sdkConfig = JSON.parse(fs.readFileSync(sdkConfigPath, 'utf-8')); 703 if (!sdkConfig.apiPath) { 704 continue; 705 } 706 let externalApiPathArray = []; 707 if (Array.isArray(sdkConfig.apiPath)) { 708 externalApiPathArray = sdkConfig.apiPath; 709 } else { 710 externalApiPathArray.push(sdkConfig.apiPath); 711 } 712 const resolveApiPathArray = []; 713 externalApiPathArray.forEach(element => { 714 const resolvePath = path.resolve(sdkPath, element); 715 resolveApiPathArray.push(resolvePath); 716 if (fs.existsSync(resolvePath)) { 717 const extrenalModulePaths = []; 718 globalModulePaths.push(resolvePath); 719 systemModules.push(...fs.readdirSync(resolvePath)); 720 readFile(resolvePath, extrenalModulePaths); 721 allModulesPaths.push(...extrenalModulePaths); 722 } 723 }); 724 sdkConfigPrefix += `|${sdkConfig.prefix.replace(/^@/, '')}`; 725 sdkConfig.apiPath = resolveApiPathArray; 726 extendSdkConfigs.push(sdkConfig); 727 } 728} 729 730function readHspResource() { 731 if (aceBuildJson.hspResourcesMap) { 732 projectConfig.hspResourcesMap = true; 733 for (const hspName in aceBuildJson.hspResourcesMap) { 734 if (fs.existsSync(aceBuildJson.hspResourcesMap[hspName])) { 735 const resourceMap = new Map(); 736 const hspResourceCollect = resources[hspName] = {}; 737 const hspResource = fs.readFileSync(aceBuildJson.hspResourcesMap[hspName], 'utf-8'); 738 const resourceArr = hspResource.split(/\n/); 739 processResourceArr(resourceArr, resourceMap, aceBuildJson.hspResourcesMap[hspName]); 740 for (const [key, value] of resourceMap) { 741 hspResourceCollect[key] = value; 742 } 743 } 744 } 745 } 746} 747 748function readAppResource(filePath) { 749 readHspResource(); 750 if (fs.existsSync(filePath)) { 751 const appResource = fs.readFileSync(filePath, 'utf-8'); 752 const resourceArr = appResource.split(/\n/); 753 const resourceMap = new Map(); 754 processResourceArr(resourceArr, resourceMap, filePath); 755 for (let [key, value] of resourceMap) { 756 resources.app[key] = value; 757 } 758 } 759 if (process.env.rawFileResource && process.env.compileMode === 'moduleJson') { 760 resourcesRawfile(process.env.rawFileResource, getStoredFileInfo().resourcesArr); 761 } 762} 763 764function processResourceArr(resourceArr, resourceMap, filePath) { 765 for (let i = 0; i < resourceArr.length; i++) { 766 if (!resourceArr[i].length) { 767 continue; 768 } 769 const resourceData = resourceArr[i].split(/\s/); 770 if (resourceData.length === 3 && !isNaN(Number(resourceData[2]))) { 771 if (resourceMap.get(resourceData[0])) { 772 const resourceKeys = resourceMap.get(resourceData[0]); 773 if (!resourceKeys[resourceData[1]] || resourceKeys[resourceData[1]] !== Number(resourceData[2])) { 774 resourceKeys[resourceData[1]] = Number(resourceData[2]); 775 } 776 } else { 777 let obj = {}; 778 obj[resourceData[1]] = Number(resourceData[2]); 779 resourceMap.set(resourceData[0], obj); 780 } 781 if (process.env.compileTool === 'rollup' && process.env.compileMode === 'moduleJson') { 782 getStoredFileInfo().updateResourceList(resourceData[0] + '_' + resourceData[1]); 783 } 784 } else { 785 logger.warn(`\u001b[31m ArkTS:WARN The format of file '${filePath}' is incorrect. \u001b[39m`); 786 break; 787 } 788 } 789} 790 791function hashProjectPath(projectPath) { 792 const hash = crypto.createHash('sha256'); 793 hash.update(projectPath.toString()); 794 process.env.hashProjectPath = '_' + hash.digest('hex'); 795 return process.env.hashProjectPath; 796} 797 798function loadModuleInfo(projectConfig, envArgs) { 799 if (projectConfig.aceBuildJson && fs.existsSync(projectConfig.aceBuildJson)) { 800 const buildJsonInfo = JSON.parse(fs.readFileSync(projectConfig.aceBuildJson).toString()); 801 if (buildJsonInfo.compileMode) { 802 projectConfig.compileMode = buildJsonInfo.compileMode; 803 } 804 projectConfig.projectRootPath = buildJsonInfo.projectRootPath; 805 projectConfig.modulePathMap = buildJsonInfo.modulePathMap; 806 projectConfig.isOhosTest = buildJsonInfo.isOhosTest; 807 let faultHandler = function (error) { 808 // rollup's error will be handled in fast build 809 if (process.env.compileTool === 'rollup') { 810 return; 811 } 812 logger.error(error); 813 process.exit(FAIL); 814 }; 815 projectConfig.es2abcCompileTsInAotMode = true; 816 projectConfig.es2abcCompileTsInNonAotMode = false; 817 const compileMode = process.env.compileTool === 'rollup' ? projectConfig.compileMode : buildJsonInfo.compileMode; 818 if (checkAotConfig(compileMode, buildJsonInfo, faultHandler)) { 819 projectConfig.processTs = true; 820 projectConfig.pandaMode = TS2ABC; 821 projectConfig.anBuildOutPut = buildJsonInfo.anBuildOutPut; 822 projectConfig.anBuildMode = buildJsonInfo.anBuildMode; 823 projectConfig.apPath = buildJsonInfo.apPath; 824 if (projectConfig.es2abcCompileTsInAotMode) { 825 projectConfig.pandaMode = ES2ABC; 826 } 827 } else { 828 projectConfig.processTs = false; 829 projectConfig.pandaMode = buildJsonInfo.pandaMode; 830 if (projectConfig.es2abcCompileTsInNonAotMode) { 831 projectConfig.pandaMode = ES2ABC; 832 projectConfig.processTs = true; 833 } 834 } 835 if (envArgs !== undefined) { 836 projectConfig.buildArkMode = envArgs.buildMode; 837 } 838 if (compileMode === 'esmodule') { 839 projectConfig.nodeModulesPath = buildJsonInfo.nodeModulesPath; 840 projectConfig.harNameOhmMap = buildJsonInfo.harNameOhmMap; 841 } 842 if (projectConfig.compileHar && buildJsonInfo.moduleName && 843 buildJsonInfo.modulePathMap[buildJsonInfo.moduleName]) { 844 if (projectConfig.useTsHar) { 845 projectConfig.processTs = true; 846 } 847 projectConfig.moduleRootPath = buildJsonInfo.modulePathMap[buildJsonInfo.moduleName]; 848 } 849 } 850} 851 852function checkAppResourcePath(appResourcePath, config) { 853 if (appResourcePath) { 854 readAppResource(appResourcePath); 855 if (fs.existsSync(appResourcePath) && config.cache) { 856 config.cache.buildDependencies.config.push(appResourcePath); 857 } 858 if (!projectConfig.xtsMode) { 859 const appResourcePathSavePath = path.resolve(projectConfig.cachePath, 'resource_path.txt'); 860 saveAppResourcePath(appResourcePath, appResourcePathSavePath); 861 if (fs.existsSync(appResourcePathSavePath) && config.cache) { 862 config.cache.buildDependencies.config.push(appResourcePathSavePath); 863 } 864 } 865 } 866} 867 868function saveAppResourcePath(appResourcePath, appResourcePathSavePath) { 869 let isSave = false; 870 if (fs.existsSync(appResourcePathSavePath)) { 871 const saveContent = fs.readFileSync(appResourcePathSavePath); 872 if (appResourcePath !== saveContent) { 873 isSave = true; 874 } 875 } else { 876 isSave = true; 877 } 878 if (isSave) { 879 fs.writeFileSync(appResourcePathSavePath, appResourcePath); 880 } 881} 882 883function addSDKBuildDependencies(config) { 884 if (projectConfig.localPropertiesPath && 885 fs.existsSync(projectConfig.localPropertiesPath) && config.cache) { 886 config.cache.buildDependencies.config.push(projectConfig.localPropertiesPath); 887 } 888 if (projectConfig.projectProfilePath && 889 fs.existsSync(projectConfig.projectProfilePath) && config.cache) { 890 config.cache.buildDependencies.config.push(projectConfig.projectProfilePath); 891 } 892} 893 894function getCleanConfig(workerFile) { 895 const cleanPath = []; 896 if (projectConfig.compileMode === 'esmodule') { 897 return cleanPath; 898 } 899 cleanPath.push(projectConfig.buildPath); 900 if (workerFile) { 901 const workerFilesPath = Object.keys(workerFile); 902 for (const workerFilePath of workerFilesPath) { 903 cleanPath.push(path.join(projectConfig.buildPath, workerFilePath, '..')); 904 } 905 } 906 return cleanPath; 907} 908 909function isPartialUpdate(metadata, moduleType) { 910 if (!Array.isArray(metadata) || !metadata.length) { 911 return; 912 } 913 metadata.some(item => { 914 if (item.name && item.value) { 915 if (item.name === 'ArkTSPartialUpdate' && item.value === 'false') { 916 partialUpdateConfig.partialUpdateMode = false; 917 if (projectConfig.aceModuleJsonPath) { 918 logger.warn('\u001b[33m ArkTS:WARN File: ' + projectConfig.aceModuleJsonPath + '.' + '\n' + 919 " The 'ArkTSPartialUpdate' field will no longer be supported in the future. \u001b[39m"); 920 } 921 } 922 if (item.name === 'ArkTSBuilderCheck' && item.value === 'false') { 923 partialUpdateConfig.builderCheck = false; 924 } 925 if (item.name === 'Api11ArkTSCheck' && item.value === 'SkipArkTSCheckInApi11') { 926 partialUpdateConfig.executeArkTSLinter = false; 927 } 928 if (item.name === 'Api11ArkTSCheckMode' && item.value === 'DoArkTSCheckInCompatibleModeInApi11') { 929 partialUpdateConfig.standardArkTSLinter = false; 930 } 931 if (item.name === 'ArkTSVersion') { 932 partialUpdateConfig.arkTSVersion = item.value; 933 } 934 if (item.name === 'OPTLazyForEach' && item.value === 'true') { 935 projectConfig.optLazyForEach = true; 936 } 937 if (item.name === 'SkipTscOhModuleCheck' && item.value === 'true') { 938 partialUpdateConfig.skipTscOhModuleCheck = true; 939 } 940 if (item.name === 'SkipArkTSStaticBlocksCheck' && item.value === 'true') { 941 partialUpdateConfig.skipArkTSStaticBlocksCheck = true; 942 } 943 if (item.name === 'ArkoalaPlugin' && item.value === 'true') { 944 projectConfig.useArkoala = true; 945 } 946 if (item.name === 'UseTsHar' && item.value === 'true' && moduleType === 'har') { 947 projectConfig.useTsHar = true; 948 } 949 } 950 return !partialUpdateConfig.partialUpdateMode && !partialUpdateConfig.builderCheck && 951 !partialUpdateConfig.executeArkTSLinter && !partialUpdateConfig.standardArkTSLinter && 952 partialUpdateConfig.arkTSVersion !== undefined && projectConfig.optLazyForEach && 953 partialUpdateConfig.skipTscOhModuleCheck && partialUpdateConfig.skipArkTSStaticBlocksCheck; 954 }); 955} 956 957function applicationConfig() { 958 const localProperties = path.resolve(aceBuildJson.projectRootPath, 'local.properties'); 959 if (fs.existsSync(localProperties)) { 960 try { 961 const localPropertiesFile = fs.readFileSync(localProperties, {encoding: 'utf-8'}).split(/\r?\n/); 962 localPropertiesFile.some((item) => { 963 const builderCheckValue = item.replace(/\s+|;/g, ''); 964 if (builderCheckValue === 'ArkTSConfig.ArkTSBuilderCheck=false') { 965 partialUpdateConfig.builderCheck = false; 966 return true; 967 } 968 }); 969 } catch (err) { 970 } 971 } 972} 973 974function partialUpdateController(minAPIVersion, metadata = null, moduleType = '') { 975 projectConfig.minAPIVersion = minAPIVersion; 976 if (minAPIVersion >= 9) { 977 partialUpdateConfig.partialUpdateMode = true; 978 } 979 const MIN_VERSION_OPTIMIZE_COMPONENT = 10; 980 if (minAPIVersion < MIN_VERSION_OPTIMIZE_COMPONENT) { 981 partialUpdateConfig.optimizeComponent = false; 982 } 983 if (metadata) { 984 isPartialUpdate(metadata, moduleType); 985 } 986 if (aceBuildJson.projectRootPath) { 987 applicationConfig(); 988 } 989} 990 991const globalProgram = { 992 builderProgram: null, 993 program: null, 994 watchProgram: null, 995 checker: null, 996 strictLanguageService: null, 997}; 998 999const partialUpdateConfig = { 1000 partialUpdateMode: false, 1001 builderCheck: true, 1002 executeArkTSLinter: true, 1003 standardArkTSLinter: true, 1004 optimizeComponent: true, 1005 arkTSVersion: undefined, 1006 skipTscOhModuleCheck: false, 1007 skipArkTSStaticBlocksCheck: false, 1008}; 1009 1010function resetMain() { 1011 staticPreviewPage = undefined; 1012 aceCompileMode = 'page'; 1013 resetAbilityConfig(); 1014 resetProjectConfig(); 1015 resources.app = {}; 1016 abilityPagesFullPath.length = 0; 1017 aceBuildJson = {}; 1018 resetGlobalProgram(); 1019 partialUpdateConfig.builderCheck = true; 1020 globalModulePaths = []; 1021 sdkConfigs = []; 1022 defaultSdkConfigs = []; 1023 extendSdkConfigs = []; 1024 sdkConfigPrefix = 'ohos|system|kit'; 1025 ohosSystemModulePaths = []; 1026 ohosSystemModuleSubDirPaths = []; 1027 allModulesPaths = []; 1028} 1029 1030function resetAbilityConfig() { 1031 abilityConfig.abilityType = 'page'; 1032 abilityConfig.abilityEntryFile = null; 1033 abilityConfig.projectAbilityPath = []; 1034 abilityConfig.testRunnerFile = []; 1035} 1036 1037function resetProjectConfig() { 1038 projectConfig.entryObj = {}; 1039 projectConfig.cardObj = {}; 1040 projectConfig.compileHar = false; 1041 projectConfig.compileShared = false; 1042 projectConfig.packageDir = 'node_modules'; 1043 projectConfig.packageJson = 'package.json'; 1044 projectConfig.packageManagerType = 'npm'; 1045 projectConfig.cardEntryObj = {}; 1046 projectConfig.compilerTypes = []; 1047 projectConfig.optLazyForEach = false; 1048 projectConfig.hspResourcesMap = false; 1049 projectConfig.coldReload = undefined; 1050 projectConfig.isFirstBuild = undefined; 1051 projectConfig.changedFileList = undefined; 1052 projectConfig.patchAbcPath = undefined; 1053 const props = ['projectPath', 'buildPath', 'aceModuleBuild', 'manifestFilePath', 'aceProfilePath', 1054 'aceModuleJsonPath', 'aceSuperVisualPath', 'hashProjectPath', 'aceBuildJson', 'cachePath', 1055 'aceSoPath', 'localPropertiesPath', 'projectProfilePath', 'isPreview', 'compileMode', 'runtimeOS', 1056 'sdkInfo', 'checkEntry', 'obfuscateHarType', 'isCrossplatform', 'enableDebugLine', 'bundleType' 1057 ]; 1058 for (let key in projectConfig) { 1059 if (props.includes(key)) { 1060 projectConfig[key] = undefined; 1061 } 1062 } 1063} 1064 1065function resetGlobalProgram() { 1066 globalProgram.builderProgram = null; 1067 globalProgram.program = null; 1068 globalProgram.checker = null; 1069 globalProgram.strictLanguageService = null; 1070} 1071 1072function initMain() { 1073 staticPreviewPage = process.env.aceStaticPreview; 1074 aceCompileMode = process.env.aceCompileMode || 'page'; 1075 abilityConfig.abilityType = process.env.abilityType || 'page'; 1076} 1077 1078exports.globalProgram = globalProgram; 1079exports.projectConfig = projectConfig; 1080exports.loadEntryObj = loadEntryObj; 1081exports.readAppResource = readAppResource; 1082exports.resources = resources; 1083exports.loadWorker = loadWorker; 1084exports.abilityConfig = abilityConfig; 1085exports.readWorkerFile = readWorkerFile; 1086exports.abilityPagesFullPath = abilityPagesFullPath; 1087exports.loadModuleInfo = loadModuleInfo; 1088exports.systemModules = systemModules; 1089exports.checkAppResourcePath = checkAppResourcePath; 1090exports.addSDKBuildDependencies = addSDKBuildDependencies; 1091exports.partialUpdateConfig = partialUpdateConfig; 1092exports.readPatchConfig = readPatchConfig; 1093exports.initBuildInfo = initBuildInfo; 1094exports.getCleanConfig = getCleanConfig; 1095exports.globalModulePaths = globalModulePaths; 1096exports.defaultSdkConfigs = defaultSdkConfigs; 1097exports.extendSdkConfigs = extendSdkConfigs; 1098exports.sdkConfigs = sdkConfigs; 1099exports.sdkConfigPrefix = sdkConfigPrefix; 1100exports.ohosSystemModulePaths = ohosSystemModulePaths; 1101exports.resetMain = resetMain; 1102exports.ohosSystemModuleSubDirPaths = ohosSystemModuleSubDirPaths; 1103exports.allModulesPaths = allModulesPaths; 1104exports.resetProjectConfig = resetProjectConfig; 1105exports.resetGlobalProgram = resetGlobalProgram; 1106