1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import path from 'path'; 17import ts from 'typescript'; 18import fs from 'fs'; 19import os from 'os'; 20import uglifyJS from 'uglify-js'; 21 22import { 23 partialUpdateConfig, 24 projectConfig, 25 globalProgram 26} from '../main'; 27import { createHash } from 'crypto'; 28import type { Hash } from 'crypto'; 29import { 30 AUXILIARY, 31 EXTNAME_ETS, 32 EXTNAME_JS, 33 MAIN, 34 FAIL, 35 TEMPORARY, 36 ESMODULE, 37 $$, 38 EXTEND_DECORATORS, 39 COMPONENT_EXTEND_DECORATOR, 40 COMPONENT_ANIMATABLE_EXTEND_DECORATOR, 41 COMPONENT_CONSTRUCTOR_LOCALSTORAGE_TYPE_PU, 42 GET_SHARED, 43 COMPONENT_CONSTRUCTOR_UNDEFINED, 44 USE_SHARED_STORAGE, 45 STORAGE 46} from './pre_define'; 47 48export enum LogType { 49 ERROR = 'ERROR', 50 WARN = 'WARN', 51 NOTE = 'NOTE' 52} 53export const TEMPORARYS: string = 'temporarys'; 54export const BUILD: string = 'build'; 55export const SRC_MAIN: string = 'src/main'; 56 57const red: string = '\u001b[31m'; 58const reset: string = '\u001b[39m'; 59 60const WINDOWS: string = 'Windows_NT'; 61const LINUX: string = 'Linux'; 62const MAC: string = 'Darwin'; 63const HARMONYOS: string = 'HarmonyOS'; 64 65export interface LogInfo { 66 type: LogType, 67 message: string, 68 pos?: number, 69 line?: number, 70 column?: number, 71 fileName?: string 72} 73 74export const repeatLog: Map<string, LogInfo> = new Map(); 75 76export class FileLog { 77 private _sourceFile: ts.SourceFile | undefined; 78 private _errors: LogInfo[] = []; 79 80 public get sourceFile() { 81 return this._sourceFile; 82 } 83 84 public set sourceFile(newValue: ts.SourceFile) { 85 this._sourceFile = newValue; 86 } 87 88 public get errors() { 89 return this._errors; 90 } 91 92 public set errors(newValue: LogInfo[]) { 93 this._errors = newValue; 94 } 95 96 public cleanUp(): void { 97 this._sourceFile = undefined; 98 this._errors = []; 99 } 100} 101 102export function emitLogInfo(loader: any, infos: LogInfo[], fastBuild: boolean = false, 103 resourcePath: string = null): void { 104 if (infos && infos.length) { 105 infos.forEach((item) => { 106 switch (item.type) { 107 case LogType.ERROR: 108 fastBuild ? loader.error('\u001b[31m' + getMessage(item.fileName || resourcePath, item, true)) : 109 loader.emitError(getMessage(item.fileName || loader.resourcePath, item)); 110 break; 111 case LogType.WARN: 112 fastBuild ? loader.warn('\u001b[33m' + getMessage(item.fileName || resourcePath, item, true)) : 113 loader.emitWarning(getMessage(item.fileName || loader.resourcePath, item)); 114 break; 115 case LogType.NOTE: 116 fastBuild ? loader.info('\u001b[34m' + getMessage(item.fileName || resourcePath, item, true)) : 117 loader.emitWarning(getMessage(loader.resourcePath, item)); 118 break; 119 } 120 }); 121 } 122} 123 124export function addLog(type: LogType, message: string, pos: number, log: LogInfo[], 125 sourceFile: ts.SourceFile) { 126 const posOfNode: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition(pos); 127 log.push({ 128 type: type, 129 message: message, 130 line: posOfNode.line + 1, 131 column: posOfNode.character + 1, 132 fileName: sourceFile.fileName 133 }); 134} 135 136export function getMessage(fileName: string, info: LogInfo, fastBuild: boolean = false): string { 137 let message: string; 138 if (info.line && info.column) { 139 message = `BUILD${info.type} File: ${fileName}:${info.line}:${info.column}\n ${info.message}`; 140 } else { 141 message = `BUILD${info.type} File: ${fileName}\n ${info.message}`; 142 } 143 if (fastBuild) { 144 message = message.replace(/^BUILD/, 'ArkTS:'); 145 } 146 return message; 147} 148 149export function getTransformLog(transformLog: FileLog): LogInfo[] { 150 const sourceFile: ts.SourceFile = transformLog.sourceFile; 151 const logInfos: LogInfo[] = transformLog.errors.map((item) => { 152 if (item.pos) { 153 if (!item.column || !item.line) { 154 const posOfNode: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition(item.pos); 155 item.line = posOfNode.line + 1; 156 item.column = posOfNode.character + 1; 157 } 158 } else { 159 item.line = item.line || undefined; 160 item.column = item.column || undefined; 161 } 162 if (!item.fileName) { 163 item.fileName = sourceFile.fileName; 164 } 165 return item; 166 }); 167 return logInfos; 168} 169 170class ComponentInfo { 171 private _id: number = 0; 172 private _componentNames: Set<string> = new Set(['ForEach']); 173 public set id(id: number) { 174 this._id = id; 175 } 176 public get id() { 177 return this._id; 178 } 179 public set componentNames(componentNames: Set<string>) { 180 this._componentNames = componentNames; 181 } 182 public get componentNames() { 183 return this._componentNames; 184 } 185} 186 187export let componentInfo: ComponentInfo = new ComponentInfo(); 188 189export function hasDecorator(node: ts.MethodDeclaration | ts.FunctionDeclaration | 190 ts.StructDeclaration | ts.ClassDeclaration, decortorName: string, 191 customBuilder: ts.Decorator[] = null, log: LogInfo[] = null): boolean { 192 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node); 193 if (decorators && decorators.length) { 194 const extendResult = { 195 Extend: false, 196 AnimatableExtend: false 197 }; 198 for (let i = 0; i < decorators.length; i++) { 199 const originalDecortor: string = decorators[i].getText().replace(/\(.*\)$/, '').replace(/\s*/g, '').trim(); 200 if (log && EXTEND_DECORATORS.includes(decortorName)) { 201 if (originalDecortor === COMPONENT_EXTEND_DECORATOR) { 202 extendResult.Extend = true; 203 } 204 if (originalDecortor === COMPONENT_ANIMATABLE_EXTEND_DECORATOR) { 205 extendResult.AnimatableExtend = true; 206 } 207 } else { 208 if (originalDecortor === decortorName) { 209 if (customBuilder) { 210 customBuilder.push(...decorators.slice(i + 1), ...decorators.slice(0, i)); 211 } 212 return true; 213 } 214 } 215 } 216 if (log && extendResult.Extend && extendResult.AnimatableExtend) { 217 log.push({ 218 type: LogType.ERROR, 219 message: `The function can not be decorated by '@Extend' and '@AnimatableExtend' at the same time.`, 220 pos: node.getStart() 221 }); 222 } 223 return (decortorName === COMPONENT_EXTEND_DECORATOR && extendResult.Extend) || 224 (decortorName === COMPONENT_ANIMATABLE_EXTEND_DECORATOR && extendResult.AnimatableExtend); 225 } 226 return false; 227} 228 229const STATEMENT_EXPECT: number = 1128; 230const SEMICOLON_EXPECT: number = 1005; 231const STATESTYLES_EXPECT: number = 1003; 232export const IGNORE_ERROR_CODE: number[] = [STATEMENT_EXPECT, SEMICOLON_EXPECT, STATESTYLES_EXPECT]; 233 234export function readFile(dir: string, utFiles: string[]): void { 235 try { 236 const files: string[] = fs.readdirSync(dir); 237 files.forEach((element) => { 238 const filePath: string = path.join(dir, element); 239 const status: fs.Stats = fs.statSync(filePath); 240 if (status.isDirectory()) { 241 readFile(filePath, utFiles); 242 } else { 243 utFiles.push(filePath); 244 } 245 }); 246 } catch (e) { 247 console.error(red, 'ArkTS ERROR: ' + e, reset); 248 } 249} 250 251export function circularFile(inputPath: string, outputPath: string): void { 252 if (!inputPath || !outputPath) { 253 return; 254 } 255 fs.readdir(inputPath, function (err, files) { 256 if (!files) { 257 return; 258 } 259 files.forEach(file => { 260 const inputFile: string = path.resolve(inputPath, file); 261 const outputFile: string = path.resolve(outputPath, file); 262 const fileStat: fs.Stats = fs.statSync(inputFile); 263 if (fileStat.isFile()) { 264 copyFile(inputFile, outputFile); 265 } else { 266 circularFile(inputFile, outputFile); 267 } 268 }); 269 }); 270} 271 272function copyFile(inputFile: string, outputFile: string): void { 273 try { 274 const parent: string = path.join(outputFile, '..'); 275 if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) { 276 mkDir(parent); 277 } 278 if (fs.existsSync(outputFile)) { 279 return; 280 } 281 const readStream: fs.ReadStream = fs.createReadStream(inputFile); 282 const writeStream: fs.WriteStream = fs.createWriteStream(outputFile); 283 readStream.pipe(writeStream); 284 readStream.on('close', function () { 285 writeStream.end(); 286 }); 287 } catch (err) { 288 throw err.message; 289 } 290} 291 292export function mkDir(path_: string): void { 293 const parent: string = path.join(path_, '..'); 294 if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) { 295 mkDir(parent); 296 } 297 fs.mkdirSync(path_); 298} 299 300export function toUnixPath(data: string): string { 301 if (/^win/.test(require('os').platform())) { 302 const fileTmps: string[] = data.split(path.sep); 303 const newData: string = path.posix.join(...fileTmps); 304 return newData; 305 } 306 return data; 307} 308 309export function tryToLowerCasePath(filePath: string): string { 310 return toUnixPath(filePath).toLowerCase(); 311} 312 313export function toHashData(path: string): string { 314 const content: string = fs.readFileSync(path).toString(); 315 const hash: Hash = createHash('sha256'); 316 hash.update(content); 317 return hash.digest('hex'); 318} 319 320export function writeFileSync(filePath: string, content: string): void { 321 if (!fs.existsSync(filePath)) { 322 const parent: string = path.join(filePath, '..'); 323 if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) { 324 mkDir(parent); 325 } 326 } 327 fs.writeFileSync(filePath, content); 328} 329export function genLoaderOutPathOfHar(filePath: string, cachePath: string, buildPath: string, moduleRootPath: string, projectRootPath): string { 330 filePath = toUnixPath(filePath); 331 buildPath = toUnixPath(buildPath); 332 const cacheRootPath: string = toUnixPath(cachePath); 333 const moduleName = toUnixPath(moduleRootPath).replace(toUnixPath(projectRootPath), ''); 334 const relativeFilePath: string = filePath.replace(cacheRootPath, '').replace(moduleName, ''); 335 const output: string = path.join(buildPath, relativeFilePath); 336 return output; 337} 338 339export function genTemporaryPath(filePath: string, projectPath: string, buildPath: string, projectConfig: Object, 340 metaInfo: Object, buildInHar: boolean = false): string { 341 filePath = toUnixPath(filePath).replace(/\.[cm]js$/, EXTNAME_JS); 342 projectPath = toUnixPath(projectPath); 343 344 if (process.env.compileTool === 'rollup') { 345 let relativeFilePath: string = ''; 346 if (metaInfo) { 347 if (metaInfo.isLocalDependency) { 348 // When buildInHar and compileHar are both True, 349 // this is the path under the PackageHar directory being spliced together. 350 // Here, only the relative path based on moduleRootPath needs to be retained. 351 // eg. moduleA/index.js --> index.js --> PackageHar/index.js 352 // eg. moduleA/src/main/ets/test.js --> src/main/ets/test.js --> PackageHar/src/main/ets/test.js 353 const moduleName: string = buildInHar && projectConfig.compileHar ? '' : metaInfo.moduleName; 354 relativeFilePath = filePath.replace(toUnixPath(metaInfo.belongModulePath), moduleName); 355 } else { 356 relativeFilePath = filePath.replace(toUnixPath(metaInfo.belongProjectPath), ''); 357 } 358 } else { 359 relativeFilePath = filePath.replace(toUnixPath(projectConfig.projectRootPath), ''); 360 } 361 const output: string = path.join(buildPath, relativeFilePath); 362 return output; 363 } 364 365 if (isPackageModulesFile(filePath, projectConfig)) { 366 const packageDir: string = projectConfig.packageDir; 367 const fakePkgModulesPath: string = toUnixPath(path.join(projectConfig.projectRootPath, packageDir)); 368 let output: string = ''; 369 if (filePath.indexOf(fakePkgModulesPath) === -1) { 370 const hapPath: string = toUnixPath(projectConfig.projectRootPath); 371 const tempFilePath: string = filePath.replace(hapPath, ''); 372 const relativeFilePath: string = tempFilePath.substring(tempFilePath.indexOf(packageDir) + packageDir.length + 1); 373 output = path.join(buildPath, buildInHar ? '' : TEMPORARY, packageDir, MAIN, relativeFilePath); 374 } else { 375 output = filePath.replace(fakePkgModulesPath, 376 path.join(buildPath, buildInHar ? '' : TEMPORARY, packageDir, AUXILIARY)); 377 } 378 return output; 379 } 380 381 if (filePath.indexOf(projectPath) !== -1) { 382 const relativeFilePath: string = filePath.replace(projectPath, ''); 383 const output: string = path.join(buildPath, buildInHar ? '' : TEMPORARY, relativeFilePath); 384 return output; 385 } 386 387 return ''; 388} 389 390export function isPackageModulesFile(filePath: string, projectConfig: Object): boolean { 391 filePath = toUnixPath(filePath); 392 const hapPath: string = toUnixPath(projectConfig.projectRootPath); 393 const tempFilePath: string = filePath.replace(hapPath, ''); 394 const packageDir: string = projectConfig.packageDir; 395 if (tempFilePath.indexOf(packageDir) !== -1) { 396 const fakePkgModulesPath: string = toUnixPath(path.resolve(projectConfig.projectRootPath, packageDir)); 397 if (filePath.indexOf(fakePkgModulesPath) !== -1) { 398 return true; 399 } 400 if (projectConfig.modulePathMap) { 401 for (const key in projectConfig.modulePathMap) { 402 const value: string = projectConfig.modulePathMap[key]; 403 const fakeModulePkgModulesPath: string = toUnixPath(path.resolve(value, packageDir)); 404 if (filePath.indexOf(fakeModulePkgModulesPath) !== -1) { 405 return true; 406 } 407 } 408 } 409 } 410 411 return false; 412} 413 414export interface GeneratedFileInHar { 415 sourcePath: string; 416 sourceCachePath?: string; 417 obfuscatedSourceCachePath?: string; 418 originalDeclarationCachePath?: string; 419 originalDeclarationContent?: string; 420 obfuscatedDeclarationCachePath?: string; 421} 422 423export const harFilesRecord: Map<string, GeneratedFileInHar> = new Map(); 424 425export function generateSourceFilesInHar(sourcePath: string, sourceContent: string, suffix: string, 426 projectConfig: Object, modulePathMap?: Object): void { 427 const belongModuleInfo: Object = getBelongModuleInfo(sourcePath, modulePathMap, projectConfig.projectRootPath); 428 // compileShared: compile shared har of project 429 let jsFilePath: string = genTemporaryPath(sourcePath, 430 projectConfig.compileShared ? projectConfig.projectRootPath : projectConfig.moduleRootPath, 431 projectConfig.compileShared || projectConfig.byteCodeHar ? path.resolve(projectConfig.aceModuleBuild, '../etsFortgz') : projectConfig.cachePath, 432 projectConfig, belongModuleInfo, projectConfig.compileShared); 433 if (!jsFilePath.match(new RegExp(projectConfig.packageDir))) { 434 jsFilePath = jsFilePath.replace(/\.ets$/, suffix).replace(/\.ts$/, suffix); 435 if (projectConfig.obfuscateHarType === 'uglify' && suffix === '.js') { 436 sourceContent = uglifyJS.minify(sourceContent).code; 437 } 438 // collect the declaration files for obfuscation 439 if (projectConfig.compileMode === ESMODULE && (/\.d\.e?ts$/).test(jsFilePath)) { 440 sourcePath = toUnixPath(sourcePath); 441 const genFilesInHar: GeneratedFileInHar = { 442 sourcePath: sourcePath, 443 originalDeclarationCachePath: jsFilePath, 444 originalDeclarationContent: sourceContent 445 }; 446 harFilesRecord.set(sourcePath, genFilesInHar); 447 return; 448 } else { 449 mkdirsSync(path.dirname(jsFilePath)); 450 } 451 fs.writeFileSync(jsFilePath, sourceContent); 452 } 453} 454 455export function mkdirsSync(dirname: string): boolean { 456 if (fs.existsSync(dirname)) { 457 return true; 458 } else if (mkdirsSync(path.dirname(dirname))) { 459 fs.mkdirSync(dirname); 460 return true; 461 } 462 463 return false; 464} 465 466export function nodeLargeOrEqualTargetVersion(targetVersion: number): boolean { 467 const currentNodeVersion: number = parseInt(process.versions.node.split('.')[0]); 468 if (currentNodeVersion >= targetVersion) { 469 return true; 470 } 471 472 return false; 473} 474 475export function removeDir(dirName: string): void { 476 if (fs.existsSync(dirName)) { 477 if (nodeLargeOrEqualTargetVersion(16)) { 478 fs.rmSync(dirName, { recursive: true }); 479 } else { 480 fs.rmdirSync(dirName, { recursive: true }); 481 } 482 } 483} 484 485export function parseErrorMessage(message: string): string { 486 const messageArrary: string[] = message.split('\n'); 487 let logContent: string = ''; 488 messageArrary.forEach(element => { 489 if (!(/^at/.test(element.trim()))) { 490 logContent = logContent + element + '\n'; 491 } 492 }); 493 return logContent; 494} 495 496export function isWindows(): boolean { 497 return os.type() === WINDOWS; 498} 499 500export function isLinux(): boolean { 501 return os.type() === LINUX; 502} 503 504export function isMac(): boolean { 505 return os.type() === MAC; 506} 507 508export function isHarmonyOs(): boolean { 509 return os.type() === HARMONYOS; 510} 511 512export function maxFilePathLength(): number { 513 if (isWindows()) { 514 return 32766; 515 } else if (isLinux() || isHarmonyOs()) { 516 return 4095; 517 } else if (isMac()) { 518 return 1016; 519 } else { 520 return -1; 521 } 522} 523 524export function validateFilePathLength(filePath: string, logger: Object): boolean { 525 if (maxFilePathLength() < 0) { 526 logger.error(red, 'Unknown OS platform', reset); 527 process.exitCode = FAIL; 528 return false; 529 } else if (filePath.length > 0 && filePath.length <= maxFilePathLength()) { 530 return true; 531 } else if (filePath.length > maxFilePathLength()) { 532 logger.error(red, `The length of ${filePath} exceeds the limitation of current platform, which is ` + 533 `${maxFilePathLength()}. Please try moving the project folder to avoid deeply nested file path and try again`, 534 reset); 535 process.exitCode = FAIL; 536 return false; 537 } else { 538 logger.error(red, 'Validate file path failed', reset); 539 process.exitCode = FAIL; 540 return false; 541 } 542} 543 544export function validateFilePathLengths(filePaths: Array<string>, logger: any): boolean { 545 filePaths.forEach((filePath) => { 546 if (!validateFilePathLength(filePath, logger)) { 547 return false; 548 } 549 return true; 550 }); 551 return true; 552} 553 554export function unlinkSync(filePath: string): void { 555 if (fs.existsSync(filePath)) { 556 fs.unlinkSync(filePath); 557 } 558} 559 560export function getExtensionIfUnfullySpecifiedFilepath(filePath: string): string { 561 if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) { 562 return ""; 563 } 564 565 let extension: string = EXTNAME_ETS; 566 if (fs.existsSync(filePath + '.ts') && fs.statSync(filePath + '.ts').isFile()) { 567 extension = '.ts'; 568 } else if (fs.existsSync(filePath + '.d.ts') && fs.statSync(filePath + '.d.ts').isFile()) { 569 extension = '.d.ts'; 570 } else if (fs.existsSync(filePath + '.d.ets') && fs.statSync(filePath + '.d.ets').isFile()) { 571 extension = '.d.ets'; 572 } else if (fs.existsSync(filePath + '.js') && fs.statSync(filePath + '.js').isFile()) { 573 extension = '.js'; 574 } else if (fs.existsSync(filePath + '.json') && fs.statSync(filePath + '.json').isFile()) { 575 extension = '.json'; 576 } 577 578 return extension; 579} 580 581export function shouldWriteChangedList(watchModifiedFiles: string[], 582 watchRemovedFiles: string[]): boolean { 583 if (projectConfig.compileMode === ESMODULE && process.env.watchMode === 'true' && !projectConfig.isPreview && 584 projectConfig.changedFileList && (watchRemovedFiles.length + watchModifiedFiles.length)) { 585 if (process.env.compileTool !== 'rollup') { 586 if (!(watchModifiedFiles.length === 1 && 587 watchModifiedFiles[0] === projectConfig.projectPath && !watchRemovedFiles.length)) { 588 return true; 589 } else { 590 return false; 591 } 592 } 593 return true; 594 } 595 return false; 596} 597 598interface HotReloadIncrementalTime { 599 hotReloadIncrementalStartTime: string; 600 hotReloadIncrementalEndTime: string; 601} 602 603export const hotReloadIncrementalTime: HotReloadIncrementalTime = { 604 hotReloadIncrementalStartTime: '', 605 hotReloadIncrementalEndTime: '' 606}; 607 608interface FilesObj { 609 modifiedFiles: string[], 610 removedFiles: string[] 611} 612 613let allModifiedFiles: Set<string> = new Set(); 614 615export function getHotReloadFiles(watchModifiedFiles: string[], 616 watchRemovedFiles: string[], hotReloadSupportFiles: Set<string>): FilesObj { 617 hotReloadIncrementalTime.hotReloadIncrementalStartTime = new Date().getTime().toString(); 618 watchRemovedFiles = watchRemovedFiles.map(file => path.relative(projectConfig.projectPath, file)); 619 allModifiedFiles = new Set([...allModifiedFiles, ...watchModifiedFiles 620 .filter(file => fs.statSync(file).isFile() && 621 (hotReloadSupportFiles.has(file) || !['.ets', '.ts', '.js'].includes(path.extname(file)))) 622 .map(file => path.relative(projectConfig.projectPath, file))] 623 .filter(file => !watchRemovedFiles.includes(file))); 624 return { 625 modifiedFiles: [...allModifiedFiles], 626 removedFiles: [...watchRemovedFiles] 627 }; 628} 629 630export function getResolveModules(projectPath: string, faMode: boolean): string[] { 631 if (faMode) { 632 return [ 633 path.resolve(projectPath, '../../../../../'), 634 path.resolve(projectPath, '../../../../' + projectConfig.packageDir), 635 path.resolve(projectPath, '../../../../../' + projectConfig.packageDir), 636 path.resolve(projectPath, '../../') 637 ]; 638 } else { 639 return [ 640 path.resolve(projectPath, '../../../../'), 641 path.resolve(projectPath, '../../../' + projectConfig.packageDir), 642 path.resolve(projectPath, '../../../../' + projectConfig.packageDir), 643 path.resolve(projectPath, '../') 644 ]; 645 } 646} 647 648export function writeUseOSFiles(useOSFiles: Set<string>): void { 649 let info: string = ''; 650 if (!fs.existsSync(projectConfig.aceSoPath)) { 651 const parent: string = path.resolve(projectConfig.aceSoPath, '..'); 652 if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) { 653 mkDir(parent); 654 } 655 } else { 656 info = fs.readFileSync(projectConfig.aceSoPath, 'utf-8') + '\n'; 657 } 658 fs.writeFileSync(projectConfig.aceSoPath, info + Array.from(useOSFiles).join('\n')); 659} 660 661 662export function writeCollectionFile(cachePath: string, appCollection: Map<string, Set<string>>, 663 allComponentsOrModules: Map<string, Array<string>>, fileName: string, allFiles: Set<string> = null, 664 widgetPath: string = undefined): void { 665 for (let key of appCollection.keys()) { 666 if (appCollection.get(key).size === 0) { 667 allComponentsOrModules.delete(key); 668 continue; 669 } 670 if (allFiles && !allFiles.has(key)) { 671 continue; 672 } 673 const newKey: string = projectConfig.projectRootPath ? path.relative(projectConfig.projectRootPath, key) : key; 674 allComponentsOrModules.set(newKey, Array.from(appCollection.get(key))); 675 } 676 const content: string = JSON.stringify(Object.fromEntries(allComponentsOrModules), null, 2); 677 writeFileSync(path.resolve(cachePath, fileName), content); 678 if (widgetPath) { 679 writeFileSync(path.resolve(widgetPath, fileName), content); 680 } 681} 682 683export function getAllComponentsOrModules(allFiles: Set<string>, 684 cacheCollectionFileName: string): Map<string, Array<string>> { 685 const cacheCollectionFilePath: string = path.resolve(projectConfig.cachePath, cacheCollectionFileName); 686 const allComponentsOrModules: Map<string, Array<string>> = new Map(); 687 if (!fs.existsSync(cacheCollectionFilePath)) { 688 return allComponentsOrModules; 689 } 690 const lastComponentsOrModules = require(cacheCollectionFilePath); 691 for (let key in lastComponentsOrModules) { 692 if (allFiles.has(key)) { 693 allComponentsOrModules.set(key, lastComponentsOrModules[key]); 694 } 695 } 696 return allComponentsOrModules; 697} 698 699export function getPossibleBuilderTypeParameter(parameters: ts.ParameterDeclaration[]): string[] { 700 const parameterNames: string[] = []; 701 if (!partialUpdateConfig.builderCheck) { 702 if (isDollarParameter(parameters)) { 703 parameters[0].type.members.forEach((member) => { 704 if (member.name && ts.isIdentifier(member.name)) { 705 parameterNames.push(member.name.escapedText.toString()); 706 } 707 }); 708 } else { 709 parameters.forEach((parameter) => { 710 if (parameter.name && ts.isIdentifier(parameter.name)) { 711 parameterNames.push(parameter.name.escapedText.toString()); 712 } 713 }); 714 } 715 } 716 return parameterNames; 717} 718 719function isDollarParameter(parameters: ts.ParameterDeclaration[]): boolean { 720 return parameters.length === 1 && parameters[0].name && ts.isIdentifier(parameters[0].name) && 721 parameters[0].name.escapedText.toString() === $$ && parameters[0].type && ts.isTypeLiteralNode(parameters[0].type) && 722 parameters[0].type.members && parameters[0].type.members.length > 0; 723} 724 725interface ChildrenCacheFile { 726 fileName: string, 727 mtimeMs: number, 728} 729 730export interface CacheFile { 731 mtimeMs: number, 732 children: Array<ChildrenCacheFile>, 733} 734 735export interface RouterInfo { 736 name: string, 737 buildFunction: string, 738} 739 740// Global Information & Method 741export class ProcessFileInfo { 742 buildStart: boolean = true; 743 wholeFileInfo: { [id: string]: SpecialArkTSFileInfo | TSFileInfo } = {}; // Save ArkTS & TS file's infomation 744 transformedFiles: Set<string> = new Set(); // ArkTS & TS Files which should be transformed in this compilation 745 cachedFiles: string[] = []; // ArkTS & TS Files which should not be transformed in this compilation 746 shouldHaveEntry: string[] = []; // Which file should have @Entry decorator 747 resourceToFile: { [resource: string]: Set<string> } = {}; // Resource is used by which file 748 lastResourceList: Set<string> = new Set(); 749 resourceList: Set<string> = new Set(); // Whole project resource 750 shouldInvalidFiles: Set<string> = new Set(); 751 resourceTableChanged: boolean = false; 752 currentArkTsFile: SpecialArkTSFileInfo; 753 reUseProgram: boolean = false; 754 resourcesArr: Set<string> = new Set(); 755 lastResourcesSet: Set<string> = new Set(); 756 transformCacheFiles: { [fileName: string]: CacheFile } = {}; 757 processBuilder: boolean = false; 758 processGlobalBuilder: boolean = false; 759 processLocalBuilder: boolean = false; 760 builderLikeCollection: Set<string> = new Set(); 761 newTsProgram: ts.Program; 762 changeFiles: string[] = []; 763 isFirstBuild: boolean = true; 764 processForEach: number = 0; 765 processLazyForEach: number = 0; 766 processRepeat: boolean = false; 767 isAsPageImport: boolean = false; 768 overallObjectLinkCollection: Map<string, Set<string>> = new Map(); 769 overallLinkCollection: Map<string, Set<string>> = new Map(); 770 overallBuilderParamCollection: Map<string, Set<string>> = new Map(); 771 lazyForEachInfo: { 772 forEachParameters: ts.ParameterDeclaration, 773 isDependItem: boolean 774 } = { 775 forEachParameters: null, 776 isDependItem: false 777 }; 778 routerInfo: Map<string, Array<RouterInfo>> = new Map(); 779 hasLocalBuilderInFile: boolean = false; 780 781 addGlobalCacheInfo(resourceListCacheInfo: string[], 782 resourceToFileCacheInfo: { [resource: string]: Set<string> }, 783 cacheFile: { [fileName: string]: CacheFile }): void { 784 if (this.buildStart) { 785 for (const element in resourceToFileCacheInfo) { 786 this.resourceToFile[element] = new Set(resourceToFileCacheInfo[element]); 787 } 788 this.lastResourceList = new Set(resourceListCacheInfo); 789 } 790 if (this.resourceTableChanged) { 791 this.compareResourceDiff(); 792 } 793 if (cacheFile) { 794 this.transformCacheFiles = cacheFile; 795 } 796 } 797 798 addFileCacheInfo(id: string, fileCacheInfo: fileInfo) { 799 if (fileCacheInfo && process.env.compileMode === 'moduleJson') { 800 if (Array.isArray(fileCacheInfo.fileToResourceList)) { 801 fileCacheInfo.fileToResourceList = new Set(fileCacheInfo.fileToResourceList); 802 } else { 803 fileCacheInfo.fileToResourceList = new Set(); 804 } 805 } 806 if (id.match(/(?<!\.d)\.(ets)$/)) { 807 this.wholeFileInfo[id] = new SpecialArkTSFileInfo(fileCacheInfo); 808 } else if (id.match(/(?<!\.d)\.(ts)$/) && process.env.compileMode === 'moduleJson') { 809 this.wholeFileInfo[id] = new TSFileInfo(fileCacheInfo); 810 } 811 } 812 813 collectTransformedFiles(id: string) { 814 if (id.match(process.env.compileMode === 'moduleJson' ? /(?<!\.d)\.(ets|ts)$/ : /(?<!\.d)\.(ets)$/)) { 815 this.transformedFiles.add(id); 816 } 817 } 818 819 collectCachedFiles(id: string) { 820 if (id.match(process.env.compileMode === 'moduleJson' ? /(?<!\.d)\.(ets|ts)$/ : /(?<!\.d)\.(ets)$/)) { 821 this.cachedFiles.push(id); 822 } 823 } 824 825 judgeShouldHaveEntryFiles(entryFileWithoutEntryDecorator: Set<string>): void { 826 this.shouldHaveEntry = Object.values(projectConfig.entryObj as string[]).filter((item) => { 827 return !entryFileWithoutEntryDecorator.has(item.toLowerCase()) && item.match(/(?<!\.d)\.(ets)$/); 828 }); 829 } 830 831 saveCacheFileInfo(cache): void { 832 if (process.env.compileMode === 'moduleJson') { 833 const fileCacheInfo: { [id: string]: fileInfo | tsFileInfo } = cache.get('fileCacheInfo') || {}; 834 const resourceToFileCacheInfo = cache.get('resourceToFileCacheInfo') || {}; 835 for (const i in resourceToFileCacheInfo) { 836 resourceToFileCacheInfo[i] = new Set(resourceToFileCacheInfo[i]); 837 } 838 const resourceToFile: { [resource: string]: Set<string> | string[] } = Object.assign(resourceToFileCacheInfo, this.resourceToFile); 839 for (const id of this.transformedFiles) { 840 fileCacheInfo[id] = this.wholeFileInfo[id].fileInfo; 841 for (const resource of this.wholeFileInfo[id].newFileToResourceList) { 842 if (!(fileCacheInfo[id].fileToResourceList as Set<string>).has(resource)) { 843 this.resourceToFileBecomeSet(resourceToFile, resource, id); 844 } 845 } 846 for (const resource of fileCacheInfo[id].fileToResourceList) { 847 if (!this.wholeFileInfo[id].newFileToResourceList.has(resource)) { 848 (resourceToFile[resource] as Set<string>).delete(id); 849 } 850 } 851 fileCacheInfo[id].fileToResourceList = [...this.wholeFileInfo[id].newFileToResourceList]; 852 } 853 for (const id of this.cachedFiles) { 854 fileCacheInfo[id].fileToResourceList = [...fileCacheInfo[id].fileToResourceList]; 855 } 856 this.resourceToFile = resourceToFile as { [resource: string]: Set<string> }; 857 for (const resource in resourceToFile) { 858 resourceToFile[resource] = [...resourceToFile[resource]]; 859 } 860 cache.set('fileCacheInfo', fileCacheInfo); 861 cache.set('resourceListCacheInfo', [...this.resourceList]); 862 cache.set('resourceToFileCacheInfo', resourceToFile); 863 } else { 864 const cacheInfo: { [id: string]: fileInfo } = cache.get('fileCacheInfo') || {}; 865 for (const id of this.transformedFiles) { 866 cacheInfo[id] = this.wholeFileInfo[id].fileInfo; 867 } 868 cache.set('fileCacheInfo', cacheInfo); 869 } 870 } 871 872 resourceToFileBecomeSet(resourceToFile: { [resource: string]: Set<string> | string[] }, resource: string, id: string): void { 873 if (!resourceToFile[resource]) { 874 resourceToFile[resource] = new Set(); 875 } 876 if (resourceToFile[resource] instanceof Set) { 877 resourceToFile[resource].add(id); 878 } else if (Array.isArray(resourceToFile[resource])) { 879 resourceToFile[resource] = new Set(resourceToFile[resource]); 880 resourceToFile[resource].add(id); 881 } else { 882 return; 883 } 884 } 885 886 updateResourceList(resource: string) { 887 this.resourceList.add(resource); 888 } 889 890 compareResourceDiff() { 891 // delete resource 892 for (const resource of this.lastResourceList) { 893 if (!this.resourceList.has(resource) && this.resourceToFile[resource]) { 894 this.resourceToFile[resource].forEach(file => { 895 this.shouldInvalidFiles.add(file); 896 }); 897 } 898 } 899 // create resource 900 for (const resource of this.resourceList) { 901 if (!this.resourceToFile[resource]) { 902 this.resourceToFile[resource] = new Set(); 903 } 904 if (!this.lastResourceList.has(resource)) { 905 this.resourceToFile[resource].forEach(file => { 906 this.shouldInvalidFiles.add(file); 907 }); 908 } 909 } 910 } 911 912 collectResourceInFile(resource: string, file: string): void { 913 if (this.wholeFileInfo[file]) { 914 this.wholeFileInfo[file].newFileToResourceList.add(resource); 915 } 916 } 917 918 clearCollectedInfo(cache) { 919 this.buildStart = false; 920 this.resourceTableChanged = false; 921 this.isAsPageImport = false; 922 this.saveCacheFileInfo(cache); 923 this.transformedFiles = new Set(); 924 this.cachedFiles = []; 925 this.lastResourceList = new Set([...this.resourceList]); 926 this.shouldInvalidFiles.clear(); 927 this.resourcesArr.clear(); 928 } 929 setCurrentArkTsFile(): void { 930 this.currentArkTsFile = new SpecialArkTSFileInfo(); 931 } 932 getCurrentArkTsFile(): SpecialArkTSFileInfo { 933 return this.currentArkTsFile; 934 } 935} 936 937export let storedFileInfo: ProcessFileInfo = new ProcessFileInfo(); 938 939export interface fileInfo extends tsFileInfo { 940 hasEntry: boolean; // Has @Entry decorator or not 941} 942 943export interface tsFileInfo { 944 fileToResourceList: Set<string> | string[]; // How much Resource is used 945} 946 947// Save single TS file information 948class TSFileInfo { 949 fileInfo: tsFileInfo = { 950 fileToResourceList: new Set() 951 }; 952 newFileToResourceList: Set<string> = new Set(); 953 constructor(cacheInfo: fileInfo, etsFile?: boolean) { 954 if (!etsFile) { 955 this.fileInfo = cacheInfo || this.fileInfo; 956 } 957 } 958} 959 960// Save single ArkTS file information 961class SpecialArkTSFileInfo extends TSFileInfo { 962 fileInfo: fileInfo = { 963 hasEntry: false, 964 fileToResourceList: new Set() 965 }; 966 recycleComponents: Set<string> = new Set([]); 967 compFromDETS: Set<string> = new Set(); 968 animatableExtendAttribute: Map<string, Set<string>> = new Map(); 969 pkgName: string; 970 971 constructor(cacheInfo?: fileInfo) { 972 super(cacheInfo, true); 973 this.fileInfo = cacheInfo || this.fileInfo; 974 } 975 976 get hasEntry() { 977 return this.fileInfo.hasEntry; 978 } 979 set hasEntry(value: boolean) { 980 this.fileInfo.hasEntry = value; 981 } 982} 983 984export function setChecker(): void { 985 if (globalProgram.program) { 986 globalProgram.checker = globalProgram.program.getTypeChecker(); 987 globalProgram.strictChecker = globalProgram.program.getLinterTypeChecker(); 988 } else if (globalProgram.watchProgram) { 989 globalProgram.checker = globalProgram.watchProgram.getCurrentProgram().getProgram().getTypeChecker(); 990 } 991} 992export interface ExtendResult { 993 decoratorName: string; 994 componentName: string; 995} 996 997export function resourcesRawfile(rawfilePath: string, resourcesArr: Set<string>, resourceName: string = ''): void { 998 if (fs.existsSync(process.env.rawFileResource) && fs.statSync(rawfilePath).isDirectory()) { 999 const files: string[] = fs.readdirSync(rawfilePath); 1000 files.forEach((file: string) => { 1001 if (fs.statSync(path.join(rawfilePath, file)).isDirectory()) { 1002 resourcesRawfile(path.join(rawfilePath, file), resourcesArr, resourceName ? resourceName + '/' + file : file); 1003 } else { 1004 if (resourceName) { 1005 resourcesArr.add(resourceName + '/' + file); 1006 } else { 1007 resourcesArr.add(file); 1008 } 1009 } 1010 }); 1011 } 1012} 1013 1014export function differenceResourcesRawfile(oldRawfile: Set<string>, newRawfile: Set<string>): boolean { 1015 if (oldRawfile.size !== 0 && oldRawfile.size === newRawfile.size) { 1016 for (const singleRawfiles of oldRawfile.values()) { 1017 if (!newRawfile.has(singleRawfiles)) { 1018 return true; 1019 } 1020 } 1021 return false; 1022 } else if (oldRawfile.size === 0 && oldRawfile.size === newRawfile.size) { 1023 return false; 1024 } else { 1025 return true; 1026 } 1027} 1028 1029export function isString(text: unknown): text is string { 1030 return typeof text === 'string'; 1031} 1032 1033function getRollupCacheStoreKey(projectConfig: object): string { 1034 let keyInfo: string[] = [projectConfig.compileSdkVersion, projectConfig.compatibleSdkVersion, projectConfig.runtimeOS, 1035 projectConfig.etsLoaderPath]; 1036 return keyInfo.join('#'); 1037} 1038 1039function getRollupCacheKey(projectConfig: object): string { 1040 let isWidget: string = projectConfig.widgetCompile ? 'widget' : 'non-widget'; 1041 let ohosTestInfo: string = 'non-ohosTest'; 1042 if (projectConfig.testFrameworkPar) { 1043 ohosTestInfo = JSON.stringify(projectConfig.testFrameworkPar); 1044 } 1045 1046 let keyInfo: string[] = [projectConfig.entryModuleName, projectConfig.targetName, isWidget, ohosTestInfo, 1047 projectConfig.needCoverageInsert, projectConfig.isOhosTest]; 1048 return keyInfo.join('#'); 1049} 1050 1051function clearRollupCacheStore(cacheStoreManager: object, currentKey: string): void { 1052 if (!cacheStoreManager) { 1053 return; 1054 } 1055 1056 for (let key of cacheStoreManager.keys()) { 1057 if (key !== currentKey) { 1058 cacheStoreManager.unmount(key); 1059 } 1060 } 1061} 1062 1063export function startTimeStatisticsLocation(startTimeEvent: CompileEvent): void { 1064 if (startTimeEvent) { 1065 startTimeEvent.start(); 1066 } 1067} 1068 1069export function stopTimeStatisticsLocation(stopTimeEvent: CompileEvent): void { 1070 if (stopTimeEvent) { 1071 stopTimeEvent.stop(); 1072 } 1073} 1074export let resolveModuleNamesTime: CompileEvent; 1075export class CompilationTimeStatistics { 1076 hookEventFactory: HookEventFactoryType; 1077 createProgramTime: CompileEvent; 1078 runArkTSLinterTime: CompileEvent; 1079 diagnosticTime: CompileEvent; 1080 scriptSnapshotTime: CompileEvent; 1081 processImportTime: CompileEvent; 1082 processComponentClassTime: CompileEvent; 1083 validateEtsTime: CompileEvent; 1084 tsProgramEmitTime: CompileEvent; 1085 shouldEmitJsTime: CompileEvent; 1086 transformNodesTime: CompileEvent; 1087 emitTime: CompileEvent; 1088 printNodeTime: CompileEvent; 1089 noSourceFileRebuildProgramTime: CompileEvent; 1090 etsTransformBuildStartTime: CompileEvent; 1091 etsTransformLoadTime: CompileEvent; 1092 processKitImportTime: CompileEvent; 1093 processUISyntaxTime: CompileEvent; 1094 constructor(share: Record<string, any>, pluginName: string, hookName: string) { 1095 if (share && share.getHookEventFactory) { 1096 if (pluginName === 'etsChecker' && hookName === 'buildStart' && share.getHookEventFactory(pluginName, hookName)) { 1097 this.hookEventFactory = share.getHookEventFactory(pluginName, hookName); 1098 this.createProgramTime = this.hookEventFactory.createEvent('createProgram'); 1099 this.runArkTSLinterTime = this.hookEventFactory.createEvent('arkTSLinter'); 1100 this.diagnosticTime = this.hookEventFactory.createEvent('diagnostic'); 1101 this.scriptSnapshotTime = this.createProgramTime.createSubEvent('scriptSnapshot'); 1102 resolveModuleNamesTime = this.hookEventFactory.createEvent('resolveModuleNames'); 1103 } else if (pluginName === 'etsTransform' && hookName === 'transform' && share.getHookEventFactory(pluginName, hookName)) { 1104 this.hookEventFactory = share.getHookEventFactory(pluginName, hookName); 1105 this.validateEtsTime = this.hookEventFactory.createEvent('validateEts'); 1106 this.tsProgramEmitTime = this.hookEventFactory.createEvent('tsProgramEmit'); 1107 this.shouldEmitJsTime = this.hookEventFactory.createEvent('shouldEmitJs'); 1108 this.transformNodesTime = this.tsProgramEmitTime.createSubEvent('transformNodes'); 1109 this.emitTime = this.tsProgramEmitTime.createSubEvent('emit'); 1110 this.printNodeTime = this.hookEventFactory.createEvent('printNode'); 1111 this.noSourceFileRebuildProgramTime = this.hookEventFactory.createEvent('noSourceFileRebuildProgram'); 1112 this.processKitImportTime = this.tsProgramEmitTime.createSubEvent('processKitImport'); 1113 this.processUISyntaxTime = this.tsProgramEmitTime.createSubEvent('processUISyntax'); 1114 this.processImportTime = this.processUISyntaxTime.createSubEvent('processImport'); 1115 this.processComponentClassTime = this.processUISyntaxTime.createSubEvent('processComponentClass'); 1116 } else if (pluginName === 'etsTransform' && hookName === 'buildStart' && share.getHookEventFactory(pluginName, hookName)) { 1117 this.hookEventFactory = share.getHookEventFactory(pluginName, hookName); 1118 this.etsTransformBuildStartTime = this.hookEventFactory.createEvent('etsTransformBuildStart'); 1119 } else if (pluginName === 'etsTransform' && hookName === 'load' && share.getHookEventFactory(pluginName, hookName)) { 1120 this.hookEventFactory = share.getHookEventFactory(pluginName, hookName); 1121 this.etsTransformLoadTime = this.hookEventFactory.createEvent('etsTransformLoad'); 1122 } 1123 } 1124 } 1125} 1126 1127interface HookEventFactoryType { 1128 createEvent(name: string): CompileEvent | undefined; 1129} 1130 1131type CompileEventState = 'created' | 'beginning' | 'running' | 'failed' | 'success' | 'warn'; 1132interface CompileEvent { 1133 start(time?: number): CompileEvent; 1134 stop(state?: CompileEventState, time?: number): void; 1135 startAsyncEvent(time: number): CompileEvent; 1136 stopAsyncEvent(state?: CompileEventState, TIME?: number): void; 1137 createSubEvent(name: string): CompileEvent; 1138} 1139 1140export function resetUtils(): void { 1141 componentInfo = new ComponentInfo(); 1142 harFilesRecord.clear(); 1143 storedFileInfo = new ProcessFileInfo(); 1144} 1145 1146export function getStoredFileInfo(): ProcessFileInfo { 1147 return storedFileInfo; 1148} 1149 1150export class EntryOptionValue { 1151 routeName: ts.Expression; 1152 storage: ts.Expression; 1153 useSharedStorage: ts.Expression; 1154} 1155 1156function sharedNode(): ts.CallExpression { 1157 return ts.factory.createCallExpression( 1158 ts.factory.createPropertyAccessExpression( 1159 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_LOCALSTORAGE_TYPE_PU), 1160 ts.factory.createIdentifier(GET_SHARED) 1161 ), 1162 undefined, 1163 [] 1164 ); 1165} 1166 1167export function createGetShared(entryOptionValue: EntryOptionValue): ts.ConditionalExpression { 1168 return ts.factory.createConditionalExpression( 1169 entryOptionValue.useSharedStorage, 1170 ts.factory.createToken(ts.SyntaxKind.QuestionToken), 1171 sharedNode(), 1172 ts.factory.createToken(ts.SyntaxKind.ColonToken), 1173 entryOptionValue.storage || ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED) 1174 ); 1175} 1176 1177export function createGetSharedForVariable(entryOptionNode: ts.Expression, isShare: boolean = true): ts.ConditionalExpression { 1178 return ts.factory.createConditionalExpression( 1179 ts.factory.createPropertyAccessExpression( 1180 entryOptionNode, 1181 ts.factory.createIdentifier(USE_SHARED_STORAGE) 1182 ), 1183 ts.factory.createToken(ts.SyntaxKind.QuestionToken), 1184 sharedNode(), 1185 ts.factory.createToken(ts.SyntaxKind.ColonToken), 1186 isShare ? ts.factory.createPropertyAccessExpression( 1187 entryOptionNode, ts.factory.createIdentifier(STORAGE)) : 1188 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED) 1189 ); 1190} 1191 1192export function judgeUseSharedStorageForExpresion(entryOptionNode: ts.Expression): ts.BinaryExpression { 1193 return ts.factory.createBinaryExpression( 1194 entryOptionNode, 1195 ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), 1196 ts.factory.createBinaryExpression( 1197 ts.factory.createPropertyAccessExpression( 1198 entryOptionNode, 1199 ts.factory.createIdentifier(USE_SHARED_STORAGE) 1200 ), 1201 ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsToken), 1202 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED) 1203 ) 1204 ); 1205} 1206 1207export function getRollupCache(rollupShareObject: object, projectConfig: object, key: string): object | undefined { 1208 if (!rollupShareObject) { 1209 return undefined; 1210 } 1211 1212 // Preferentially get cache object from the rollup’s cache interface. 1213 if (rollupShareObject.cache) { 1214 // Only the cache object’s name as the cache key is required. 1215 return rollupShareObject.cache.get(key); 1216 } 1217 1218 // Try to get cache object from the rollup's cacheStoreManager interface. 1219 if (rollupShareObject.cacheStoreManager) { 1220 // The cache under cacheStoreManager is divided into two layers. The key for the first layer of cache is determined 1221 // by the SDK and runtimeOS, accessed through the `mount` interface. The key for the second layer of cache is 1222 // determined by the compilation task and the name of the cache object, 1223 // accessed through `getCache` or `setCache` interface. 1224 const cacheStoreKey: string = getRollupCacheStoreKey(projectConfig); 1225 const cacheServiceKey: string = getRollupCacheKey(projectConfig) + '#' + key; 1226 1227 // Clear the cache if the SDK path or runtimeOS changed 1228 clearRollupCacheStore(rollupShareObject.cacheStoreManager, cacheStoreKey); 1229 return rollupShareObject.cacheStoreManager.mount(cacheStoreKey).getCache(cacheServiceKey); 1230 } 1231 1232 return undefined; 1233} 1234 1235export function setRollupCache(rollupShareObject: object, projectConfig: object, key: string, value: object): void { 1236 if (!rollupShareObject) { 1237 return; 1238 } 1239 1240 // Preferentially set cache object to the rollup’s cache interface. 1241 if (rollupShareObject.cache) { 1242 // Only the cache object’s name as the cache key is required. 1243 rollupShareObject.cache.set(key, value); 1244 return; 1245 } 1246 1247 // Try to set cache object to the rollup's cacheStoreManager interface. 1248 if (rollupShareObject.cacheStoreManager) { 1249 const cacheStoreKey: string = getRollupCacheStoreKey(projectConfig); 1250 const cacheServiceKey: string = getRollupCacheKey(projectConfig) + '#' + key; 1251 1252 rollupShareObject.cacheStoreManager.mount(cacheStoreKey).setCache(cacheServiceKey, value); 1253 } 1254} 1255 1256export function removeDecorator(decorators: readonly ts.Decorator[], decoratorName: string): readonly ts.Decorator[] { 1257 return decorators.filter((item: ts.Node) => { 1258 if (ts.isDecorator(item) && ts.isIdentifier(item.expression) && 1259 item.expression.escapedText.toString() === decoratorName) { 1260 return false; 1261 } 1262 return true; 1263 }); 1264} 1265 1266export function isFileInProject(filePath: string, projectRootPath: string): boolean { 1267 const relativeFilePath: string = toUnixPath(path.relative(toUnixPath(projectRootPath), toUnixPath(filePath))); 1268 // When processing ohmurl, hsp's filePath is consistent with moduleRequest 1269 return fs.existsSync(filePath) && fs.statSync(filePath).isFile() && !relativeFilePath.startsWith('../'); 1270} 1271 1272export function getProjectRootPath(filePath: string, projectConfig: Object, rootPathSet: Object): string { 1273 if (rootPathSet) { 1274 for (const rootPath of rootPathSet) { 1275 if (isFileInProject(filePath, rootPath)) { 1276 return rootPath; 1277 } 1278 } 1279 } 1280 return projectConfig.projectRootPath; 1281} 1282 1283export function getBelongModuleInfo(filePath: string, modulePathMap: Object, projectRootPath: string): Object { 1284 for (const moduleName of Object.keys(modulePathMap)) { 1285 if (toUnixPath(filePath).startsWith(toUnixPath(modulePathMap[moduleName]) + '/')) { 1286 return { 1287 isLocalDependency: true, 1288 moduleName: moduleName, 1289 belongModulePath: modulePathMap[moduleName] 1290 }; 1291 } 1292 } 1293 return { 1294 isLocalDependency: false, 1295 moduleName: '', 1296 belongModulePath: projectRootPath 1297 }; 1298}