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