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