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