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 storedFileInfo 26} = require('./lib/utils'); 27 28const { 29 WORKERS_DIR, 30 TEST_RUNNER_DIR_SET, 31 TS2ABC, 32 ES2ABC, 33 FAIL 34} = require('./lib/pre_define'); 35 36const { 37 checkAotConfig 38} = require('./lib/gen_aot'); 39 40const { 41 configure, 42 getLogger 43} = require('log4js'); 44 45configure({ 46 appenders: { 'ETS': {type: 'stderr', layout: {type: 'messagePassThrough'}}}, 47 categories: {'default': {appenders: ['ETS'], level: 'info'}} 48}); 49const logger = getLogger('ETS'); 50 51const staticPreviewPage = process.env.aceStaticPreview; 52const aceCompileMode = process.env.aceCompileMode || 'page'; 53const abilityConfig = { 54 abilityType: process.env.abilityType || 'page', 55 abilityEntryFile: null, 56 projectAbilityPath: [], 57 testRunnerFile: [] 58}; 59const projectConfig = {}; 60const resources = { 61 app: {}, 62 sys: {} 63}; 64const systemModules = []; 65const abilityPagesFullPath = []; 66const globalModulePaths = []; 67let sdkConfigs = []; 68let defaultSdkConfigs = []; 69const extendSdkConfigs = []; 70let sdkConfigPrefix = 'ohos|system'; 71let ohosSystemModulePaths = []; 72 73function initProjectConfig(projectConfig) { 74 projectConfig.entryObj = {}; 75 projectConfig.cardObj = {}; 76 projectConfig.projectPath = projectConfig.projectPath || process.env.aceModuleRoot || 77 path.join(process.cwd(), 'sample'); 78 projectConfig.buildPath = projectConfig.buildPath || process.env.aceModuleBuild || 79 path.resolve(projectConfig.projectPath, 'build'); 80 projectConfig.aceModuleBuild = projectConfig.buildPath; // To be compatible with both webpack and rollup 81 projectConfig.manifestFilePath = projectConfig.manifestFilePath || process.env.aceManifestPath || 82 path.join(projectConfig.projectPath, 'manifest.json'); 83 projectConfig.aceProfilePath = projectConfig.aceProfilePath || process.env.aceProfilePath; 84 projectConfig.aceModuleJsonPath = projectConfig.aceModuleJsonPath || process.env.aceModuleJsonPath; 85 projectConfig.aceSuperVisualPath = projectConfig.aceSuperVisualPath || 86 process.env.aceSuperVisualPath; 87 projectConfig.hashProjectPath = projectConfig.hashProjectPath || 88 hashProjectPath(projectConfig.projectPath); 89 projectConfig.aceBuildJson = projectConfig.aceBuildJson || process.env.aceBuildJson; 90 projectConfig.cachePath = projectConfig.cachePath || process.env.cachePath || 91 path.resolve(__dirname, 'node_modules/.cache'); 92 projectConfig.aceSoPath = projectConfig.aceSoPath || process.env.aceSoPath; 93 projectConfig.xtsMode = /ets_loader_ark$/.test(__dirname); 94 projectConfig.localPropertiesPath = projectConfig.localPropertiesPath || process.env.localPropertiesPath; 95 projectConfig.projectProfilePath = projectConfig.projectProfilePath || process.env.projectProfilePath; 96 projectConfig.isPreview = projectConfig.isPreview || process.env.isPreview === 'true'; 97 projectConfig.compileMode = projectConfig.compileMode || 'jsbundle'; 98 projectConfig.runtimeOS = projectConfig.runtimeOS || process.env.runtimeOS || 'default'; 99 projectConfig.sdkInfo = projectConfig.sdkInfo || process.env.sdkInfo || 'default'; 100 projectConfig.compileHar = false; 101 projectConfig.compileShared = false; 102 projectConfig.splitCommon = false; 103 projectConfig.checkEntry = projectConfig.checkEntry || process.env.checkEntry; 104 projectConfig.obfuscateHarType = projectConfig.obfuscateHarType || process.env.obfuscate; 105 projectConfig.packageDir = 'node_modules'; 106 projectConfig.packageJson = 'package.json'; 107 projectConfig.packageManagerType = 'npm'; 108 projectConfig.cardEntryObj = {}; 109 projectConfig.compilerTypes = []; 110 projectConfig.isCrossplatform = projectConfig.isCrossplatform || false; 111} 112 113function loadEntryObj(projectConfig) { 114 let manifest = {}; 115 initProjectConfig(projectConfig); 116 loadBuildJson(); 117 if (process.env.aceManifestPath && aceCompileMode === 'page') { 118 setEntryFile(projectConfig); 119 setFaTestRunnerFile(projectConfig); 120 } 121 if (process.env.aceModuleJsonPath) { 122 setAbilityPages(projectConfig); 123 setStageTestRunnerFile(projectConfig); 124 } 125 126 if (staticPreviewPage) { 127 projectConfig.entryObj['./' + staticPreviewPage] = projectConfig.projectPath + path.sep + 128 staticPreviewPage + '.ets?entry'; 129 } else if (abilityConfig.abilityType === 'page') { 130 if (fs.existsSync(projectConfig.manifestFilePath)) { 131 const jsonString = fs.readFileSync(projectConfig.manifestFilePath).toString(); 132 manifest = JSON.parse(jsonString); 133 if (manifest && manifest.minPlatformVersion) { 134 process.env.minPlatformVersion = manifest.minPlatformVersion; 135 partialUpdateController(manifest.minPlatformVersion); 136 } 137 projectConfig.pagesJsonFileName = 'config.json'; 138 } else if (projectConfig.aceModuleJsonPath && fs.existsSync(projectConfig.aceModuleJsonPath)) { 139 process.env.compileMode = 'moduleJson'; 140 buildManifest(manifest, projectConfig.aceModuleJsonPath); 141 } else { 142 throw Error('\u001b[31m ERROR: the manifest file ' + projectConfig.manifestFilePath.replace(/\\/g, '/') + 143 ' or module.json is lost or format is invalid. \u001b[39m').message; 144 } 145 if (!projectConfig.compileHar) { 146 if (manifest.pages) { 147 const pages = manifest.pages; 148 pages.forEach((element) => { 149 const sourcePath = element.replace(/^\.\/ets\//, ''); 150 const fileName = path.resolve(projectConfig.projectPath, sourcePath + '.ets'); 151 if (fs.existsSync(fileName)) { 152 projectConfig.entryObj['./' + sourcePath] = fileName + '?entry'; 153 } else { 154 throw Error(`\u001b[31m ERROR: page '${fileName.replace(/\\/g, '/')}' does not exist. \u001b[39m`) 155 .message; 156 } 157 }); 158 } else { 159 throw Error('\u001b[31m ERROR: missing pages attribute in ' + 160 projectConfig.manifestFilePath.replace(/\\/g, '/') + 161 '. \u001b[39m').message; 162 } 163 } 164 } 165} 166 167function buildManifest(manifest, aceConfigPath) { 168 try { 169 const moduleConfigJson = JSON.parse(fs.readFileSync(aceConfigPath).toString()); 170 manifest.type = process.env.abilityType; 171 if (moduleConfigJson && moduleConfigJson.app && moduleConfigJson.app.minAPIVersion) { 172 if (moduleConfigJson.module && moduleConfigJson.module.metadata) { 173 partialUpdateController(moduleConfigJson.app.minAPIVersion, moduleConfigJson.module.metadata); 174 stageOptimization(moduleConfigJson.module.metadata); 175 } else { 176 partialUpdateController(moduleConfigJson.app.minAPIVersion); 177 } 178 } 179 if (moduleConfigJson.module) { 180 switch (moduleConfigJson.module.type) { 181 case 'har': 182 projectConfig.compileHar = true; 183 getPackageJsonEntryPath(); 184 break; 185 case 'shared': 186 projectConfig.compileShared = true; 187 getPackageJsonEntryPath(); 188 manifest.pages = getPages(moduleConfigJson); 189 break; 190 default: 191 manifest.pages = getPages(moduleConfigJson); 192 break; 193 } 194 } else { 195 throw Error('\u001b[31m' + 196 'BUIDERROR: the config.json file miss key word module || module[abilities].' + 197 '\u001b[39m').message; 198 } 199 } catch (e) { 200 if (/BUIDERROR/.test(e)) { 201 throw Error(e.replace('BUIDERROR', 'ERROR')).message; 202 } else { 203 throw Error('\x1B[31m' + 'ERROR: the module.json file is lost or format is invalid.' + 204 '\x1B[39m').message; 205 } 206 } 207} 208 209function getPackageJsonEntryPath() { 210 const rootPackageJsonPath = path.resolve(projectConfig.projectPath, '../../../' + projectConfig.packageJson); 211 if (fs.existsSync(rootPackageJsonPath)) { 212 let rootPackageJsonContent; 213 try { 214 rootPackageJsonContent = (projectConfig.packageManagerType === 'npm' ? 215 JSON : JSON5).parse(fs.readFileSync(rootPackageJsonPath, 'utf-8')); 216 } catch (e) { 217 throw Error('\u001b[31m' + 'BUIDERROR: ' + rootPackageJsonPath + ' format is invalid.' + '\u001b[39m').message; 218 } 219 if (rootPackageJsonContent) { 220 if (rootPackageJsonContent.module) { 221 getEntryPath(rootPackageJsonContent.module, rootPackageJsonPath); 222 } else if (rootPackageJsonContent.main) { 223 getEntryPath(rootPackageJsonContent.main, rootPackageJsonPath); 224 } else { 225 getEntryPath('', rootPackageJsonPath); 226 } 227 } else if (projectConfig.compileHar) { 228 throw Error('\u001b[31m' + 'BUIDERROR: lack message in ' + projectConfig.packageJson + '.' + 229 '\u001b[39m').message; 230 } 231 } 232} 233 234function supportSuffix(mainEntryPath) { 235 if (fs.existsSync(path.join(mainEntryPath, 'index.ets'))) { 236 mainEntryPath = path.join(mainEntryPath, 'index.ets'); 237 } else if (fs.existsSync(path.join(mainEntryPath, 'index.ts'))) { 238 mainEntryPath = path.join(mainEntryPath, 'index.ts'); 239 } else if (fs.existsSync(path.join(mainEntryPath, 'index.js'))) { 240 mainEntryPath = path.join(mainEntryPath, 'index.js'); 241 } else if (projectConfig.compileHar) { 242 throw Error('\u001b[31m' + 'BUIDERROR: not find entry file in ' + projectConfig.packageJson + 243 '.' + '\u001b[39m').message; 244 } 245 return mainEntryPath; 246} 247 248function supportExtName(mainEntryPath) { 249 if (path.extname(mainEntryPath) === '') { 250 if (fs.existsSync(mainEntryPath + '.ets')) { 251 mainEntryPath = mainEntryPath + '.ets'; 252 } else if (fs.existsSync(mainEntryPath + '.ts')) { 253 mainEntryPath = mainEntryPath + '.ts'; 254 } else if (fs.existsSync(mainEntryPath + '.js')) { 255 mainEntryPath = mainEntryPath + '.js'; 256 } 257 } 258 return mainEntryPath; 259} 260 261function getEntryPath(entryPath, rootPackageJsonPath) { 262 let mainEntryPath = path.resolve(rootPackageJsonPath, '../', entryPath); 263 if (fs.existsSync(mainEntryPath) && fs.statSync(mainEntryPath).isDirectory()) { 264 mainEntryPath = supportSuffix(mainEntryPath); 265 } else { 266 mainEntryPath = supportExtName(mainEntryPath); 267 } 268 if (fs.existsSync(mainEntryPath) && fs.statSync(mainEntryPath).isFile()) { 269 const entryKey = path.relative(projectConfig.projectPath, mainEntryPath); 270 projectConfig.entryObj[entryKey] = mainEntryPath; 271 abilityPagesFullPath.push(path.resolve(mainEntryPath).toLowerCase()); 272 } else if (projectConfig.compileHar) { 273 throw Error('\u001b[31m' + `BUIDERROR: not find entry file in ${rootPackageJsonPath}.` + '\u001b[39m').message; 274 } 275} 276 277function stageOptimization(metadata) { 278 if (Array.isArray(metadata) && metadata.length) { 279 metadata.some(item => { 280 if (item.name && item.name === 'USE_COMMON_CHUNK' && 281 item.value && item.value === 'true') { 282 projectConfig.splitCommon = true; 283 return true; 284 } 285 }); 286 } 287} 288 289function getPages(configJson) { 290 const pages = []; 291 let pagesJsonFileName = ''; 292 // pages is not necessary in stage 293 if (process.env.compileMode === 'moduleJson' && configJson.module && configJson.module.pages) { 294 pagesJsonFileName = `${configJson.module.pages.replace(/\$profile\:/, '')}.json`; 295 } else { 296 return pages; 297 } 298 const modulePagePath = path.resolve(projectConfig.aceProfilePath, pagesJsonFileName); 299 if (fs.existsSync(modulePagePath)) { 300 try { 301 const pagesConfig = JSON.parse(fs.readFileSync(modulePagePath, 'utf-8')); 302 if (pagesConfig && pagesConfig.src) { 303 projectConfig.pagesJsonFileName = pagesJsonFileName; 304 return pagesConfig.src; 305 } 306 } catch (e) { 307 throw Error("\x1B[31m" + `BUIDERROR: the ${modulePagePath} file format is invalid.` + 308 "\x1B[39m").message; 309 } 310 } 311 return pages; 312} 313 314function setEntryFile(projectConfig) { 315 const entryFileName = abilityConfig.abilityType === 'page' ? 'app' : abilityConfig.abilityType; 316 const extendFile = entryFileName === 'app' ? '.ets' : '.ts'; 317 const entryFileRealPath = entryFileName + extendFile; 318 const entryFilePath = path.resolve(projectConfig.projectPath, entryFileRealPath); 319 abilityConfig.abilityEntryFile = entryFilePath; 320 if (!fs.existsSync(entryFilePath) && aceCompileMode === 'page') { 321 throw Error(`\u001b[31m ERROR: missing ${entryFilePath.replace(/\\/g, '/')}. \u001b[39m`).message; 322 } 323 projectConfig.entryObj[`./${entryFileName}`] = entryFilePath + '?entry'; 324} 325 326function setAbilityPages(projectConfig) { 327 let abilityPages = []; 328 if (projectConfig.aceModuleJsonPath && fs.existsSync(projectConfig.aceModuleJsonPath)) { 329 const moduleJson = JSON.parse(fs.readFileSync(projectConfig.aceModuleJsonPath).toString()); 330 abilityPages = readAbilityEntrance(moduleJson); 331 setAbilityFile(projectConfig, abilityPages); 332 setBundleModuleInfo(projectConfig, moduleJson); 333 } 334} 335 336function setTestRunnerFile(projectConfig, isStageBased) { 337 const index = projectConfig.projectPath.split(path.sep).join('/').lastIndexOf('\/'); 338 TEST_RUNNER_DIR_SET.forEach((dir) => { 339 const projectPath = isStageBased ? projectConfig.projectPath : projectConfig.projectPath.substring(0, index + 1); 340 const testRunnerPath = path.resolve(projectPath, dir); 341 if (fs.existsSync(testRunnerPath)) { 342 const testRunnerFiles = []; 343 readFile(testRunnerPath, testRunnerFiles); 344 testRunnerFiles.forEach((item) => { 345 if (/\.(ts|js|ets)$/.test(item)) { 346 if (/\.ets$/.test(item)) { 347 abilityPagesFullPath.push(path.resolve(item).toLowerCase()); 348 } 349 const relativePath = path.relative(testRunnerPath, item).replace(/\.(ts|js|ets)$/, ''); 350 if (isStageBased) { 351 projectConfig.entryObj[`./${dir}/${relativePath}`] = item; 352 } else { 353 projectConfig.entryObj[`../${dir}/${relativePath}`] = item; 354 } 355 abilityConfig.testRunnerFile.push(item); 356 } 357 }); 358 } 359 }); 360} 361 362function setFaTestRunnerFile(projectConfig) { 363 setTestRunnerFile(projectConfig, false); 364} 365 366function setStageTestRunnerFile(projectConfig) { 367 setTestRunnerFile(projectConfig, true); 368} 369 370function setBundleModuleInfo(projectConfig, moduleJson) { 371 if (moduleJson.module) { 372 projectConfig.moduleName = moduleJson.module.name; 373 } 374 if (moduleJson.app) { 375 projectConfig.bundleName = moduleJson.app.bundleName; 376 } 377} 378 379function setAbilityFile(projectConfig, abilityPages) { 380 abilityPages.forEach(abilityPath => { 381 const projectAbilityPath = path.resolve(projectConfig.projectPath, '../', abilityPath); 382 if (path.isAbsolute(abilityPath)) { 383 abilityPath = '.' + abilityPath.slice(projectConfig.projectPath.length); 384 } 385 const entryPageKey = abilityPath.replace(/^\.\/ets\//, './').replace(/\.ts$/, '').replace(/\.ets$/, ''); 386 if (fs.existsSync(projectAbilityPath)) { 387 abilityConfig.projectAbilityPath.push(projectAbilityPath); 388 projectConfig.entryObj[entryPageKey] = projectAbilityPath + '?entry'; 389 } else { 390 throw Error( 391 `\u001b[31m ERROR: srcEntry file '${projectAbilityPath.replace(/\\/g, '/')}' does not exist. \u001b[39m` 392 ).message; 393 } 394 }); 395} 396 397function readAbilityEntrance(moduleJson) { 398 const abilityPages = []; 399 if (moduleJson.module) { 400 const moduleSrcEntrance = moduleJson.module.srcEntrance; 401 const moduleSrcEntry = moduleJson.module.srcEntry; 402 if (moduleSrcEntry) { 403 abilityPages.push(moduleSrcEntry); 404 abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, moduleSrcEntry)); 405 } else if (moduleSrcEntrance) { 406 abilityPages.push(moduleSrcEntrance); 407 abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, moduleSrcEntrance)); 408 } 409 if (moduleJson.module.abilities && moduleJson.module.abilities.length > 0) { 410 setEntrance(moduleJson.module.abilities, abilityPages); 411 } 412 if (moduleJson.module.extensionAbilities && moduleJson.module.extensionAbilities.length > 0) { 413 setEntrance(moduleJson.module.extensionAbilities, abilityPages); 414 setCardPages(moduleJson.module.extensionAbilities); 415 } 416 } 417 return abilityPages; 418} 419 420function setEntrance(abilityConfig, abilityPages) { 421 if (abilityConfig && abilityConfig.length > 0) { 422 abilityConfig.forEach(ability => { 423 if (ability.srcEntry) { 424 abilityPages.push(ability.srcEntry) 425 abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, ability.srcEntry)) 426 } else if (ability.srcEntrance) { 427 abilityPages.push(ability.srcEntrance); 428 abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, ability.srcEntrance)); 429 } 430 }); 431 } 432} 433 434function setCardPages(extensionAbilities) { 435 if (extensionAbilities && extensionAbilities.length > 0) { 436 extensionAbilities.forEach(extensionAbility => { 437 if (extensionAbility.metadata) { 438 extensionAbility.metadata.forEach(metadata => { 439 if (metadata.resource) { 440 readCardResource(metadata.resource); 441 } 442 }); 443 } 444 }); 445 } 446} 447 448function readCardResource(resource) { 449 const cardJsonFileName = `${resource.replace(/\$profile\:/, '')}.json`; 450 const modulePagePath = path.resolve(projectConfig.aceProfilePath, cardJsonFileName); 451 if (fs.existsSync(modulePagePath)) { 452 const cardConfig = JSON.parse(fs.readFileSync(modulePagePath, 'utf-8')); 453 if (cardConfig.forms) { 454 cardConfig.forms.forEach(form => { 455 readCardForm(form); 456 }); 457 } 458 } 459} 460 461function readCardForm(form) { 462 if ((form.type && form.type === 'eTS') || 463 (form.uiSyntax && form.uiSyntax === 'arkts')) { 464 const sourcePath = form.src.replace(/\.ets$/, ''); 465 const cardPath = path.resolve(projectConfig.projectPath, '..', sourcePath + '.ets'); 466 if (cardPath && fs.existsSync(cardPath)) { 467 projectConfig.entryObj['../' + sourcePath] = cardPath + '?entry'; 468 projectConfig.cardEntryObj['../' + sourcePath] = cardPath; 469 projectConfig.cardObj[cardPath] = sourcePath.replace(/^\.\//, ''); 470 } 471 } 472} 473 474function getAbilityFullPath(projectPath, abilityPath) { 475 const finalPath = path.resolve(path.resolve(projectPath, '../'), abilityPath); 476 if (fs.existsSync(finalPath)) { 477 return finalPath.toLowerCase(); 478 } else { 479 return path.resolve(abilityPath).toLowerCase(); 480 } 481} 482 483function loadWorker(projectConfig, workerFileEntry) { 484 if (workerFileEntry) { 485 projectConfig.entryObj = Object.assign(projectConfig.entryObj, workerFileEntry); 486 } else { 487 const workerPath = path.resolve(projectConfig.projectPath, WORKERS_DIR); 488 if (fs.existsSync(workerPath)) { 489 const workerFiles = []; 490 readFile(workerPath, workerFiles); 491 workerFiles.forEach((item) => { 492 if (/\.(ts|js)$/.test(item)) { 493 const relativePath = path.relative(workerPath, item) 494 .replace(/\.(ts|js)$/, '').replace(/\\/g, '/'); 495 projectConfig.entryObj[`./${WORKERS_DIR}/` + relativePath] = item; 496 } 497 }); 498 } 499 } 500} 501 502let aceBuildJson = {}; 503function loadBuildJson() { 504 if (projectConfig.aceBuildJson && fs.existsSync(projectConfig.aceBuildJson)) { 505 aceBuildJson = JSON.parse(fs.readFileSync(projectConfig.aceBuildJson).toString()); 506 } 507 if (aceBuildJson.packageManagerType === 'ohpm') { 508 projectConfig.packageManagerType = 'ohpm'; 509 projectConfig.packageDir = 'oh_modules'; 510 projectConfig.packageJson = 'oh-package.json5'; 511 } 512} 513 514function initBuildInfo() { 515 projectConfig.projectRootPath = aceBuildJson.projectRootPath; 516 if (projectConfig.compileHar && aceBuildJson.moduleName && 517 aceBuildJson.modulePathMap[aceBuildJson.moduleName]) { 518 projectConfig.moduleRootPath = aceBuildJson.modulePathMap[aceBuildJson.moduleName]; 519 } 520} 521 522function readWorkerFile() { 523 const workerFileEntry = {}; 524 if (aceBuildJson.workers) { 525 aceBuildJson.workers.forEach(worker => { 526 if (!/\.(ts|js)$/.test(worker)) { 527 worker += '.ts'; 528 } 529 const relativePath = path.relative(projectConfig.projectPath, worker); 530 if (filterWorker(relativePath)) { 531 const workerKey = relativePath.replace(/\.(ts|js)$/, '').replace(/\\/g, '/'); 532 if (workerFileEntry[workerKey]) { 533 throw Error( 534 '\u001b[31m ERROR: The worker file cannot use the same file name: \n' + 535 workerFileEntry[workerKey] + '\n' + worker + '\u001b[39m' 536 ).message; 537 } else { 538 workerFileEntry[workerKey] = worker; 539 } 540 } 541 }); 542 return workerFileEntry; 543 } 544 return null; 545} 546 547function readPatchConfig() { 548 if (aceBuildJson.patchConfig) { 549 projectConfig.hotReload = process.env.watchMode === 'true' && !projectConfig.isPreview; 550 projectConfig.patchAbcPath = aceBuildJson.patchConfig.patchAbcPath; 551 projectConfig.changedFileList = aceBuildJson.patchConfig.changedFileList ? 552 aceBuildJson.patchConfig.changedFileList : path.join(projectConfig.cachePath, 'changedFileList.json'); 553 if (projectConfig.hotReload) { 554 writeFileSync(projectConfig.changedFileList, JSON.stringify({ 555 modifiedFiles: [], 556 removedFiles: [] 557 })); 558 } 559 } 560} 561 562function filterWorker(workerPath) { 563 return /\.(ts|js)$/.test(workerPath); 564} 565 566;(function initSystemResource() { 567 const sysResourcePath = path.resolve(__dirname, './sysResource.js'); 568 if (fs.existsSync(sysResourcePath)) { 569 resources.sys = require(sysResourcePath).sys; 570 } 571})(); 572 573;(function readSystemModules() { 574 const systemModulesPath = path.resolve(__dirname, '../../api'); 575 if (fs.existsSync(systemModulesPath)) { 576 globalModulePaths.push(systemModulesPath); 577 const modulePaths = []; 578 readFile(systemModulesPath, modulePaths); 579 systemModules.push(...fs.readdirSync(systemModulesPath)); 580 ohosSystemModulePaths.push(...modulePaths); 581 defaultSdkConfigs = [ 582 { 583 'apiPath': systemModulesPath, 584 'prefix': '@ohos' 585 }, { 586 'apiPath': systemModulesPath, 587 'prefix': '@system' 588 } 589 ]; 590 } 591 const externalApiPathStr = process.env.externalApiPaths || ''; 592 const externalApiPaths = externalApiPathStr.split(path.delimiter); 593 externalApiPaths.forEach(sdkPath => { 594 const sdkConfigPath = path.resolve(sdkPath, 'sdkConfig.json'); 595 if (fs.existsSync(sdkConfigPath)) { 596 const sdkConfig = JSON.parse(fs.readFileSync(sdkConfigPath, "utf-8")); 597 sdkConfig.apiPath = path.resolve(sdkPath, sdkConfig.apiPath); 598 if (fs.existsSync(sdkConfig.apiPath)) { 599 globalModulePaths.push(sdkConfig.apiPath); 600 systemModules.push(...fs.readdirSync(sdkConfig.apiPath)); 601 sdkConfigPrefix += `|${sdkConfig.prefix.replace(/^@/, '')}`; 602 extendSdkConfigs.push(sdkConfig); 603 } 604 } 605 }); 606 sdkConfigs = [...defaultSdkConfigs, ...extendSdkConfigs]; 607})() 608 609function readAppResource(filePath) { 610 if (fs.existsSync(filePath)) { 611 const appResource = fs.readFileSync(filePath, 'utf-8'); 612 const resourceArr = appResource.split(/\n/); 613 const resourceMap = new Map(); 614 processResourceArr(resourceArr, resourceMap, filePath); 615 for (let [key, value] of resourceMap) { 616 resources.app[key] = value; 617 } 618 } 619 if (process.env.rawFileResource && process.env.compileMode === 'moduleJson') { 620 resourcesRawfile(process.env.rawFileResource, storedFileInfo.resourcesArr); 621 } 622} 623 624function processResourceArr(resourceArr, resourceMap, filePath) { 625 for (let i = 0; i < resourceArr.length; i++) { 626 if (!resourceArr[i].length) { 627 continue; 628 } 629 const resourceData = resourceArr[i].split(/\s/); 630 if (resourceData.length === 3 && !isNaN(Number(resourceData[2]))) { 631 if (resourceMap.get(resourceData[0])) { 632 const resourceKeys = resourceMap.get(resourceData[0]); 633 if (!resourceKeys[resourceData[1]] || resourceKeys[resourceData[1]] !== Number(resourceData[2])) { 634 resourceKeys[resourceData[1]] = Number(resourceData[2]); 635 } 636 } else { 637 let obj = {}; 638 obj[resourceData[1]] = Number(resourceData[2]); 639 resourceMap.set(resourceData[0], obj); 640 } 641 if (process.env.compileTool === 'rollup' && process.env.compileMode === 'moduleJson') { 642 storedFileInfo.updateResourceList(resourceData[0] + '_' + resourceData[1]); 643 } 644 } else { 645 logger.warn(`\u001b[31m ArkTS:WARN The format of file '${filePath}' is incorrect. \u001b[39m`); 646 break; 647 } 648 } 649} 650 651function hashProjectPath(projectPath) { 652 const hash = crypto.createHash('sha256'); 653 hash.update(projectPath.toString()); 654 process.env.hashProjectPath = "_" + hash.digest('hex'); 655 return process.env.hashProjectPath; 656} 657 658function loadModuleInfo(projectConfig, envArgs) { 659 if (projectConfig.aceBuildJson && fs.existsSync(projectConfig.aceBuildJson)) { 660 const buildJsonInfo = JSON.parse(fs.readFileSync(projectConfig.aceBuildJson).toString()); 661 if (buildJsonInfo.compileMode) { 662 projectConfig.compileMode = buildJsonInfo.compileMode; 663 } 664 projectConfig.projectRootPath = buildJsonInfo.projectRootPath; 665 projectConfig.modulePathMap = buildJsonInfo.modulePathMap; 666 projectConfig.isOhosTest = buildJsonInfo.isOhosTest; 667 let faultHandler = function (error) { 668 // rollup's error will be handled in fast build 669 if (process.env.compileTool === 'rollup') { 670 return; 671 } 672 logger.error(error); 673 process.exit(FAIL); 674 } 675 projectConfig.es2abcCompileTsInAotMode = true; 676 projectConfig.es2abcCompileTsInNonAotMode = false; 677 const compileMode = process.env.compileTool === 'rollup' ? projectConfig.compileMode : buildJsonInfo.compileMode; 678 if (checkAotConfig(compileMode, buildJsonInfo, faultHandler)) { 679 projectConfig.processTs = true; 680 projectConfig.pandaMode = TS2ABC; 681 projectConfig.anBuildOutPut = buildJsonInfo.anBuildOutPut; 682 projectConfig.anBuildMode = buildJsonInfo.anBuildMode; 683 projectConfig.apPath = buildJsonInfo.apPath; 684 if (projectConfig.es2abcCompileTsInAotMode) { 685 projectConfig.pandaMode = ES2ABC; 686 } 687 } else { 688 projectConfig.processTs = false; 689 projectConfig.pandaMode = buildJsonInfo.pandaMode; 690 if (projectConfig.es2abcCompileTsInNonAotMode) { 691 projectConfig.pandaMode = ES2ABC; 692 projectConfig.processTs = true; 693 } 694 } 695 if (envArgs !== undefined) { 696 projectConfig.buildArkMode = envArgs.buildMode; 697 } 698 if (compileMode === 'esmodule') { 699 projectConfig.nodeModulesPath = buildJsonInfo.nodeModulesPath; 700 projectConfig.harNameOhmMap = buildJsonInfo.harNameOhmMap; 701 } 702 if (projectConfig.compileHar && buildJsonInfo.moduleName && 703 buildJsonInfo.modulePathMap[buildJsonInfo.moduleName]) { 704 projectConfig.moduleRootPath = buildJsonInfo.modulePathMap[buildJsonInfo.moduleName]; 705 } 706 } 707} 708 709function checkAppResourcePath(appResourcePath, config) { 710 if (appResourcePath) { 711 readAppResource(appResourcePath); 712 if (fs.existsSync(appResourcePath) && config.cache) { 713 config.cache.buildDependencies.config.push(appResourcePath); 714 } 715 if (!projectConfig.xtsMode) { 716 const appResourcePathSavePath = path.resolve(projectConfig.cachePath, 'resource_path.txt'); 717 saveAppResourcePath(appResourcePath, appResourcePathSavePath); 718 if (fs.existsSync(appResourcePathSavePath) && config.cache) { 719 config.cache.buildDependencies.config.push(appResourcePathSavePath); 720 } 721 } 722 } 723} 724 725function saveAppResourcePath(appResourcePath, appResourcePathSavePath) { 726 let isSave = false; 727 if (fs.existsSync(appResourcePathSavePath)) { 728 const saveContent = fs.readFileSync(appResourcePathSavePath); 729 if (appResourcePath !== saveContent) { 730 isSave = true; 731 } 732 } else { 733 isSave = true; 734 } 735 if (isSave) { 736 fs.writeFileSync(appResourcePathSavePath, appResourcePath); 737 } 738} 739 740function addSDKBuildDependencies(config) { 741 if (projectConfig.localPropertiesPath && 742 fs.existsSync(projectConfig.localPropertiesPath) && config.cache) { 743 config.cache.buildDependencies.config.push(projectConfig.localPropertiesPath) 744 } 745 if (projectConfig.projectProfilePath && 746 fs.existsSync(projectConfig.projectProfilePath) && config.cache) { 747 config.cache.buildDependencies.config.push(projectConfig.projectProfilePath) 748 } 749} 750 751function getCleanConfig(workerFile) { 752 const cleanPath = []; 753 if (projectConfig.compileMode === 'esmodule') { 754 return cleanPath; 755 } 756 cleanPath.push(projectConfig.buildPath); 757 if (workerFile) { 758 const workerFilesPath = Object.keys(workerFile); 759 for (const workerFilePath of workerFilesPath) { 760 cleanPath.push(path.join(projectConfig.buildPath, workerFilePath, '..')); 761 } 762 } 763 return cleanPath; 764} 765 766function isPartialUpdate(metadata) { 767 if (Array.isArray(metadata) && metadata.length) { 768 metadata.some(item => { 769 if (item.name && item.name === 'ArkTSPartialUpdate' && 770 item.value && item.value === 'false') { 771 partialUpdateConfig.partialUpdateMode = false; 772 if (projectConfig.aceModuleJsonPath) { 773 logger.warn('\u001b[33m ArkTS:WARN File: ' + projectConfig.aceModuleJsonPath + '.' + '\n' + 774 " The 'ArkTSPartialUpdate' field will no longer be supported in the future. \u001b[39m"); 775 } 776 } 777 if (item.name && item.name === 'ArkTSBuilderCheck' && 778 item.value && item.value === 'false') { 779 partialUpdateConfig.builderCheck = false; 780 } 781 if (item.name && item.name === 'ExecuteArkTSLinter' && 782 item.value && item.value === 'false') { 783 partialUpdateConfig.executeArkTSLinter = false; 784 } 785 if (item.name && item.name === 'StandardArkTSLinter' && 786 item.value && item.value === 'false') { 787 partialUpdateConfig.standardArkTSLinter = false; 788 } 789 return !partialUpdateConfig.partialUpdateMode && !partialUpdateConfig.builderCheck && 790 !partialUpdateConfig.executeArkTSLinter && !partialUpdateConfig.standardArkTSLinter; 791 }); 792 } 793} 794 795function applicationConfig() { 796 const localProperties = path.resolve(aceBuildJson.projectRootPath, 'local.properties'); 797 if (fs.existsSync(localProperties)) { 798 try { 799 const localPropertiesFile = fs.readFileSync(localProperties, {encoding: 'utf-8'}).split(/\r?\n/); 800 localPropertiesFile.some((item) => { 801 const builderCheckValue = item.replace(/\s+|;/g, ''); 802 if (builderCheckValue === 'ArkTSConfig.ArkTSBuilderCheck=false') { 803 partialUpdateConfig.builderCheck = false; 804 return true; 805 } 806 }); 807 } catch (err) { 808 } 809 } 810} 811 812function partialUpdateController(minAPIVersion, metadata = null) { 813 if (minAPIVersion >= 9) { 814 partialUpdateConfig.partialUpdateMode = true; 815 } 816 if (minAPIVersion < 10) { 817 partialUpdateConfig.optimizeComponent = false; 818 } 819 if (metadata) { 820 isPartialUpdate(metadata); 821 } 822 if (aceBuildJson.projectRootPath) { 823 applicationConfig(); 824 } 825} 826 827const globalProgram = { 828 program: null, 829 watchProgram: null, 830 checker: null, 831}; 832 833const partialUpdateConfig = { 834 partialUpdateMode: false, 835 builderCheck: true, 836 executeArkTSLinter: true, 837 standardArkTSLinter: true, 838 optimizeComponent: true, 839}; 840 841exports.globalProgram = globalProgram; 842exports.projectConfig = projectConfig; 843exports.loadEntryObj = loadEntryObj; 844exports.readAppResource = readAppResource; 845exports.resources = resources; 846exports.loadWorker = loadWorker; 847exports.abilityConfig = abilityConfig; 848exports.readWorkerFile = readWorkerFile; 849exports.abilityPagesFullPath = abilityPagesFullPath; 850exports.loadModuleInfo = loadModuleInfo; 851exports.systemModules = systemModules; 852exports.checkAppResourcePath = checkAppResourcePath; 853exports.addSDKBuildDependencies = addSDKBuildDependencies; 854exports.partialUpdateConfig = partialUpdateConfig; 855exports.readPatchConfig = readPatchConfig; 856exports.initBuildInfo = initBuildInfo; 857exports.getCleanConfig = getCleanConfig; 858exports.globalModulePaths = globalModulePaths; 859exports.defaultSdkConfigs = defaultSdkConfigs; 860exports.extendSdkConfigs = extendSdkConfigs; 861exports.sdkConfigs = sdkConfigs; 862exports.sdkConfigPrefix = sdkConfigPrefix; 863exports.ohosSystemModulePaths = ohosSystemModulePaths; 864