1/* 2 * Copyright (c) 2022 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 fs from 'fs'; 17import path from 'path'; 18import * as ts from 'typescript'; 19import * as crypto from 'crypto'; 20const fse = require('fs-extra'); 21 22import { 23 projectConfig, 24 systemModules, 25 globalProgram, 26 sdkConfigs, 27 sdkConfigPrefix, 28 partialUpdateConfig, 29 resetProjectConfig, 30 resetGlobalProgram 31} from '../main'; 32import { 33 preprocessExtend, 34 preprocessNewExtend 35} from './validate_ui_syntax'; 36import { 37 INNER_COMPONENT_MEMBER_DECORATORS, 38 COMPONENT_DECORATORS_PARAMS, 39 COMPONENT_BUILD_FUNCTION, 40 STYLE_ADD_DOUBLE_DOLLAR, 41 $$, 42 PROPERTIES_ADD_DOUBLE_DOLLAR, 43 DOLLAR_BLOCK_INTERFACE, 44 COMPONENT_EXTEND_DECORATOR, 45 COMPONENT_BUILDER_DECORATOR, 46 ESMODULE, 47 EXTNAME_D_ETS, 48 EXTNAME_JS, 49 FOREACH_LAZYFOREACH, 50 COMPONENT_IF, 51 TS_WATCH_END_MSG, 52 TS_BUILD_INFO_SUFFIX, 53 HOT_RELOAD_BUILD_INFO_SUFFIX, 54 WATCH_COMPILER_BUILD_INFO_SUFFIX, 55 COMPONENT_STYLES_DECORATOR 56} from './pre_define'; 57import { 58 INNER_COMPONENT_NAMES, 59 JS_BIND_COMPONENTS, 60 BUILDIN_STYLE_NAMES 61} from './component_map'; 62import { logger } from './compile_info'; 63import { 64 hasDecorator, 65 isString, 66 generateSourceFilesInHar, 67 startTimeStatisticsLocation, 68 stopTimeStatisticsLocation, 69 resolveModuleNamesTime, 70 CompilationTimeStatistics, 71 storedFileInfo, 72 toUnixPath, 73 isWindows, 74 isMac, 75 tryToLowerCasePath, 76 getRollupCache, 77 setRollupCache 78} from './utils'; 79import { 80 isExtendFunction, 81 isOriginalExtend 82} from './process_ui_syntax'; 83import { visualTransform } from './process_visual'; 84import { tsWatchEmitter } from './fast_build/ets_ui/rollup-plugin-ets-checker'; 85import { 86 doArkTSLinter, 87 ArkTSLinterMode, 88 ArkTSVersion, 89 transfromErrorCode, 90} from './do_arkTS_linter'; 91import { 92 getJsDocNodeCheckConfig, 93 isCardFile, 94 getRealModulePath, 95 getJsDocNodeConditionCheckResult 96} from './fast_build/system_api/api_check_utils'; 97import { sourceFileDependencies } from './fast_build/ark_compiler/common/ob_config_resolver'; 98import { MemoryMonitor } from './fast_build/meomry_monitor/rollup-plugin-memory-monitor'; 99import { MemoryDefine } from './fast_build/meomry_monitor/memory_define'; 100import { 101 LINTER_SUBSYSTEM_CODE, 102 HvigorErrorInfo 103} from './hvigor_error_code/hvigor_error_info'; 104import { ErrorCodeModule } from './hvigor_error_code/const/error_code_module'; 105import { buildErrorInfoFromDiagnostic } from './hvigor_error_code/utils'; 106 107export interface LanguageServiceCache { 108 service?: ts.LanguageService; 109 pkgJsonFileHash?: string; 110 targetESVersion?: ts.ScriptTarget; 111 maxFlowDepth?: number; 112 preTsImportSendable?: boolean; 113} 114 115export const SOURCE_FILES: Map<string, ts.SourceFile> = new Map(); 116export let localPackageSet: Set<string> = new Set(); 117export const TSC_SYSTEM_CODE = '105'; 118 119export const MAX_FLOW_DEPTH_DEFAULT_VALUE = 2000; 120export const MAX_FLOW_DEPTH_MAXIMUM_VALUE = 65535; 121 122export function readDeaclareFiles(): string[] { 123 const declarationsFileNames: string[] = []; 124 fs.readdirSync(path.resolve(__dirname, '../declarations')) 125 .forEach((fileName: string) => { 126 if (/\.d\.ts$/.test(fileName)) { 127 declarationsFileNames.push(path.resolve(__dirname, '../declarations', fileName)); 128 } 129 }); 130 return declarationsFileNames; 131} 132 133const buildInfoWriteFile: ts.WriteFileCallback = (fileName: string, data: string) => { 134 if (fileName.includes(TS_BUILD_INFO_SUFFIX)) { 135 const fd: number = fs.openSync(fileName, 'w'); 136 fs.writeSync(fd, data, undefined, 'utf8'); 137 fs.closeSync(fd); 138 } 139}; 140// The collection records the file name and the corresponding version, where the version is the hash value of the text in last compilation. 141const filesBuildInfo: Map<string, string> = new Map(); 142 143export const compilerOptions: ts.CompilerOptions = ts.readConfigFile( 144 path.resolve(__dirname, '../tsconfig.json'), ts.sys.readFile).config.compilerOptions; 145function setCompilerOptions(resolveModulePaths: string[]): void { 146 const allPath: Array<string> = ['*']; 147 const basePath: string = path.resolve(projectConfig.projectPath); 148 if (process.env.compileTool === 'rollup' && resolveModulePaths && resolveModulePaths.length) { 149 resolveModulePaths.forEach((item: string) => { 150 if (!(/oh_modules$/.test(item) || /node_modules$/.test(item))) { 151 allPath.push(path.join(path.relative(basePath, item), '*')); 152 } 153 }); 154 } else { 155 if (!projectConfig.aceModuleJsonPath) { 156 allPath.push('../../../../../*'); 157 allPath.push('../../*'); 158 } else { 159 allPath.push('../../../../*'); 160 allPath.push('../*'); 161 } 162 } 163 const suffix: string = projectConfig.hotReload ? HOT_RELOAD_BUILD_INFO_SUFFIX : TS_BUILD_INFO_SUFFIX; 164 const buildInfoPath: string = path.resolve(projectConfig.cachePath, '..', suffix); 165 checkArkTSVersion(); 166 Object.assign(compilerOptions, { 167 'allowJs': getArkTSLinterMode() !== ArkTSLinterMode.NOT_USE ? true : false, 168 'checkJs': getArkTSLinterMode() !== ArkTSLinterMode.NOT_USE ? false : undefined, 169 'emitNodeModulesFiles': true, 170 'importsNotUsedAsValues': ts.ImportsNotUsedAsValues.Preserve, 171 'module': ts.ModuleKind.CommonJS, 172 'moduleResolution': ts.ModuleResolutionKind.NodeJs, 173 'noEmit': true, 174 'target': convertConfigTarget(getTargetESVersion()), 175 'maxFlowDepth': getMaxFlowDepth(), 176 'baseUrl': basePath, 177 'paths': { 178 '*': allPath 179 }, 180 'lib': convertConfigLib(getTargetESVersionLib()), 181 'types': projectConfig.compilerTypes, 182 'etsAnnotationsEnable': projectConfig.allowEtsAnnotations, 183 'etsLoaderPath': projectConfig.etsLoaderPath, 184 'needDoArkTsLinter': getArkTSLinterMode() !== ArkTSLinterMode.NOT_USE, 185 'isCompatibleVersion': getArkTSLinterMode() === ArkTSLinterMode.COMPATIBLE_MODE, 186 'skipTscOhModuleCheck': partialUpdateConfig.skipTscOhModuleCheck, 187 'skipArkTSStaticBlocksCheck': partialUpdateConfig.skipArkTSStaticBlocksCheck, 188 // options incremental && tsBuildInfoFile are required for applying incremental ability of typescript 189 'incremental': true, 190 'tsBuildInfoFile': buildInfoPath, 191 'tsImportSendableEnable': tsImportSendable, 192 'skipPathsInKeyForCompilationSettings': reuseLanguageServiceForDepChange, 193 'compatibleSdkVersionStage': projectConfig.compatibleSdkVersionStage, 194 'compatibleSdkVersion': projectConfig.compatibleSdkVersion 195 }); 196 if (projectConfig.compileMode === ESMODULE) { 197 Object.assign(compilerOptions, { 198 'importsNotUsedAsValues': ts.ImportsNotUsedAsValues.Remove, 199 'module': ts.ModuleKind.ES2020 200 }); 201 } 202 if (projectConfig.packageDir === 'oh_modules') { 203 Object.assign(compilerOptions, {'packageManagerType': 'ohpm'}); 204 } 205 readTsBuildInfoFileInCrementalMode(buildInfoPath, projectConfig); 206} 207 208function checkArkTSVersion(): void { 209 const etsCheckerLogger = fastBuildLogger || logger; 210 if (getArkTSVersion() === ArkTSVersion.ArkTS_1_0 && tsImportSendable) { 211 const logMessage: string = 'ArkTS: ArkTSVersion1.0 does not support tsImportSendable in any condition'; 212 etsCheckerLogger.error('\u001b[31m' + logMessage); 213 tsImportSendable = false; 214 } 215} 216 217// Change target to enum's value,e.g: "es2021" => ts.ScriptTarget.ES2021 218function convertConfigTarget(target: number | string): number | string { 219 if ((typeof target === 'number') && (target in ts.ScriptTarget)) { 220 return target; 221 } 222 return ts.convertCompilerOptionsFromJson({ 'target': target }, '').options.target; 223} 224 225// Change lib to libMap's value,e.g: "es2021" => "lib.es2021.d.ts" 226function convertConfigLib(libs: string[]): string[] { 227 let converted: boolean = true; 228 let libMapValues: string[] = Array.from(ts.libMap.values()); 229 for (let i = 0; i < libs.length; i++) { 230 if (!libMapValues.includes(libs[i])) { 231 converted = false; 232 break; 233 } 234 } 235 if (converted) { 236 return libs; 237 } 238 return ts.convertCompilerOptionsFromJson({ 'lib': libs }, '').options.lib; 239} 240 241/** 242 * Read the source code information in the project of the last compilation process, and then use it 243 * to determine whether the file has been modified during this compilation process. 244 */ 245function readTsBuildInfoFileInCrementalMode(buildInfoPath: string, projectConfig: Object): void { 246 if (!fs.existsSync(buildInfoPath) || !(projectConfig.compileHar || projectConfig.compileShared)) { 247 return; 248 } 249 250 type FileInfoType = { 251 version: string; 252 affectsGlobalScope: boolean; 253 }; 254 type ProgramType = { 255 fileNames: string[]; 256 fileInfos: (FileInfoType | string)[]; 257 }; 258 let buildInfoProgram: ProgramType; 259 try { 260 const content: {program: ProgramType} = JSON.parse(fs.readFileSync(buildInfoPath, 'utf-8')); 261 buildInfoProgram = content.program; 262 if (!buildInfoProgram || !buildInfoProgram.fileNames || !buildInfoProgram.fileInfos) { 263 throw new Error('.tsbuildinfo content is invalid'); 264 } 265 } catch (err) { 266 fastBuildLogger.warn('\u001b[33m' + 'ArkTS: Failed to parse .tsbuildinfo file. Error message: ' + err.message.toString()); 267 return; 268 } 269 const buildInfoDirectory: string = path.dirname(buildInfoPath); 270 /** 271 * For the windos and mac platform, the file path in tsbuildinfo is in lowercase, while buildInfoDirectory is the original path (including uppercase). 272 * Therefore, the path needs to be converted to lowercase, and then perform path comparison. 273 */ 274 const isMacOrWin = isWindows() || isMac(); 275 const fileNames: string[] = buildInfoProgram.fileNames; 276 const fileInfos: (FileInfoType | string)[] = buildInfoProgram.fileInfos; 277 fileInfos.forEach((fileInfo, index) => { 278 const version: string = typeof fileInfo === 'string' ? fileInfo : fileInfo.version; 279 const absPath: string = path.resolve(buildInfoDirectory, fileNames[index]); 280 filesBuildInfo.set(isMacOrWin ? tryToLowerCasePath(absPath) : absPath, version); 281 }); 282} 283 284interface extendInfo { 285 start: number, 286 end: number, 287 compName: string 288} 289 290function createHash(str: string): string { 291 const hash = crypto.createHash('sha256'); 292 hash.update(str); 293 return hash.digest('hex'); 294} 295 296export const fileHashScriptVersion: (fileName: string) => string = (fileName: string) => { 297 if (!fs.existsSync(fileName)) { 298 return '0'; 299 } 300 301 let fileContent: string = fs.readFileSync(fileName).toString(); 302 let cacheInfo: CacheFileName = cache[path.resolve(fileName)]; 303 304 // Error code corresponding to message `Cannot find module xx or its corresponding type declarations` 305 const errorCodeRequireRecheck: number = 2307; 306 307 if (cacheInfo && cacheInfo.error === true && cacheInfo.errorCodes && cacheInfo.errorCodes.includes(errorCodeRequireRecheck)) { 308 // If this file had errors that require recheck in the last compilation, 309 // mark the file as modified by modifying its hash value, thereby triggering tsc to recheck. 310 fileContent += Date.now().toString(); 311 } 312 return createHash(fileContent); 313}; 314 315// Reuse the last language service when dependency in oh-package.json5 changes to enhance performance in incremental building. 316// Setting this to false will create a new language service on dependency changes, like a full rebuild. 317const reuseLanguageServiceForDepChange: boolean = true; 318// When dependency changes and reusing the last language service, enable this flag to recheck code dependent on those dependencies. 319export let needReCheckForChangedDepUsers: boolean = false; 320 321export function createLanguageService(rootFileNames: string[], resolveModulePaths: string[], 322 compilationTime: CompilationTimeStatistics = null, rollupShareObject?: any): ts.LanguageService { 323 setCompilerOptions(resolveModulePaths); 324 const servicesHost: ts.LanguageServiceHost = { 325 getScriptFileNames: () => [...rootFileNames, ...readDeaclareFiles()], 326 getScriptVersion: fileHashScriptVersion, 327 getScriptSnapshot: function(fileName) { 328 if (!fs.existsSync(fileName)) { 329 return undefined; 330 } 331 if (/(?<!\.d)\.(ets|ts)$/.test(fileName)) { 332 startTimeStatisticsLocation(compilationTime ? compilationTime.scriptSnapshotTime : undefined); 333 appComponentCollection.set(path.join(fileName), new Set()); 334 let content: string = processContent(fs.readFileSync(fileName).toString(), fileName); 335 const extendFunctionInfo: extendInfo[] = []; 336 content = instanceInsteadThis(content, fileName, extendFunctionInfo, this.uiProps); 337 stopTimeStatisticsLocation(compilationTime ? compilationTime.scriptSnapshotTime : undefined); 338 return ts.ScriptSnapshot.fromString(content); 339 } 340 return ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString()); 341 }, 342 getCurrentDirectory: () => process.cwd(), 343 getCompilationSettings: () => compilerOptions, 344 getDefaultLibFileName: options => ts.getDefaultLibFilePath(options), 345 fileExists: ts.sys.fileExists, 346 readFile: ts.sys.readFile, 347 readDirectory: ts.sys.readDirectory, 348 resolveModuleNames: resolveModuleNames, 349 resolveTypeReferenceDirectives: resolveTypeReferenceDirectives, 350 directoryExists: ts.sys.directoryExists, 351 getDirectories: ts.sys.getDirectories, 352 getJsDocNodeCheckedConfig: (fileCheckedInfo: ts.FileCheckModuleInfo, sourceFileName: string) => { 353 return getJsDocNodeCheckConfig(fileCheckedInfo.currentFileName, sourceFileName); 354 }, 355 getFileCheckedModuleInfo: (containFilePath: string) => { 356 return { 357 fileNeedCheck: true, 358 checkPayload: undefined, 359 currentFileName: containFilePath 360 }; 361 }, 362 getJsDocNodeConditionCheckedResult: (jsDocFileCheckedInfo: ts.FileCheckModuleInfo, jsDocs: ts.JsDocTagInfo[]) => { 363 return getJsDocNodeConditionCheckResult(jsDocFileCheckedInfo, jsDocs); 364 }, 365 uiProps: [], 366 clearProps: function() { 367 dollarCollection.clear(); 368 extendCollection.clear(); 369 this.uiProps.length = 0; 370 }, 371 // TSC will re-do resolution if this callback return true. 372 hasInvalidatedResolutions: (filePath: string): boolean => { 373 return reuseLanguageServiceForDepChange && needReCheckForChangedDepUsers; 374 } 375 }; 376 377 if (process.env.watchMode === 'true') { 378 const recordInfo = MemoryMonitor.recordStage(MemoryDefine.ETS_CHECKER_CREATE_LANGUAGE_SERVICE); 379 const tsLanguageService = ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); 380 MemoryMonitor.stopRecordStage(recordInfo); 381 return tsLanguageService; 382 } 383 const recordInfo = MemoryMonitor.recordStage(MemoryDefine.ETS_CHECKER_CREATE_LANGUAGE_SERVICE); 384 const tsLanguageService = getOrCreateLanguageService(servicesHost, rootFileNames, rollupShareObject); 385 MemoryMonitor.stopRecordStage(recordInfo); 386 return tsLanguageService; 387} 388 389export let targetESVersionChanged: boolean = false; 390 391function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFileNames: string[], 392 rollupShareObject?: any): ts.LanguageService { 393 let cacheKey: string = 'service'; 394 let cache: LanguageServiceCache | undefined = getRollupCache(rollupShareObject, projectConfig, cacheKey); 395 396 let service: ts.LanguageService | undefined = cache?.service; 397 const currentHash: string | undefined = rollupShareObject?.projectConfig?.pkgJsonFileHash; 398 const currentTargetESVersion: ts.ScriptTarget = compilerOptions.target; 399 const currentMaxFlowDepth: number | undefined = compilerOptions.maxFlowDepth; 400 const lastHash: string | undefined = cache?.pkgJsonFileHash; 401 const lastTargetESVersion: ts.ScriptTarget | undefined = cache?.targetESVersion; 402 const lastMaxFlowDepth: number | undefined = cache?.maxFlowDepth; 403 const hashDiffers: boolean | undefined = currentHash && lastHash && currentHash !== lastHash; 404 const shouldRebuildForDepDiffers: boolean | undefined = reuseLanguageServiceForDepChange ? 405 (hashDiffers && !rollupShareObject?.depInfo?.enableIncre) : hashDiffers; 406 const targetESVersionDiffers: boolean | undefined = lastTargetESVersion && currentTargetESVersion && lastTargetESVersion !== currentTargetESVersion; 407 const maxFlowDepthDiffers: boolean | undefined = lastMaxFlowDepth && currentMaxFlowDepth && lastMaxFlowDepth !== currentMaxFlowDepth; 408 const tsImportSendableDiff: boolean = (cache?.preTsImportSendable === undefined && !tsImportSendable) ? 409 false : 410 cache?.preTsImportSendable !== tsImportSendable; 411 const shouldRebuild: boolean | undefined = shouldRebuildForDepDiffers || targetESVersionDiffers || tsImportSendableDiff || maxFlowDepthDiffers; 412 if (reuseLanguageServiceForDepChange && hashDiffers && rollupShareObject?.depInfo?.enableIncre) { 413 needReCheckForChangedDepUsers = true; 414 } 415 416 if (!service || shouldRebuild) { 417 rebuildProgram(targetESVersionDiffers, tsImportSendableDiff, maxFlowDepthDiffers); 418 service = ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); 419 } else { 420 // Found language service from cache, update root files 421 const updateRootFileNames = [...rootFileNames, ...readDeaclareFiles()]; 422 service.updateRootFiles(updateRootFileNames); 423 } 424 425 const newCache: LanguageServiceCache = { 426 service: service, 427 pkgJsonFileHash: currentHash, 428 targetESVersion: currentTargetESVersion, 429 maxFlowDepth: currentMaxFlowDepth, 430 preTsImportSendable: tsImportSendable 431 }; 432 setRollupCache(rollupShareObject, projectConfig, cacheKey, newCache); 433 return service; 434} 435 436function rebuildProgram(targetESVersionDiffers: boolean | undefined, tsImportSendableDiff: boolean, maxFlowDepthDiffers: boolean | undefined): void { 437 if (targetESVersionDiffers) { 438 // If the targetESVersion is changed, we need to delete the build info cahce files 439 deleteBuildInfoCache(compilerOptions.tsBuildInfoFile); 440 targetESVersionChanged = true; 441 } else if (tsImportSendableDiff || maxFlowDepthDiffers) { 442 // When tsImportSendable or maxFlowDepth is changed, we need to delete the build info cahce files 443 deleteBuildInfoCache(compilerOptions.tsBuildInfoFile); 444 } 445} 446 447function deleteBuildInfoCache(tsBuildInfoFilePath: string): void { 448 // The file name of tsBuildInfoLinterFile is '.tsbuildinfo.linter', so we need to add '.linter' after tsBuildInfoFilePath 449 const tsBuildInfoLinterFilePath: string = tsBuildInfoFilePath + '.linter'; 450 deleteFile(tsBuildInfoFilePath); 451 deleteFile(tsBuildInfoLinterFilePath); 452} 453 454function deleteFile(filePath: string): void { 455 if (fs.existsSync(filePath)) { 456 fs.unlinkSync(filePath); 457 } 458} 459 460interface CacheFileName { 461 mtimeMs: number, 462 children: string[], 463 parent: string[], 464 error: boolean, 465 errorCodes?: number[] 466} 467interface NeedUpdateFlag { 468 flag: boolean; 469} 470interface CheckerResult { 471 count: number 472} 473 474interface WarnCheckerResult { 475 count: number 476} 477 478interface WholeCache { 479 runtimeOS: string, 480 sdkInfo: string, 481 fileList: Cache 482} 483type Cache = Record<string, CacheFileName>; 484export let cache: Cache = {}; 485export const hotReloadSupportFiles: Set<string> = new Set(); 486export const shouldResolvedFiles: Set<string> = new Set(); 487export const appComponentCollection: Map<string, Set<string>> = new Map(); 488const allResolvedModules: Set<string> = new Set(); 489// all files of tsc and rollup for obfuscation scanning. 490export const allSourceFilePaths: Set<string> = new Set(); 491// Used to collect file paths that have not been converted toUnixPath. 492export const allModuleIds: Set<string> = new Set(); 493export let props: string[] = []; 494 495export let fastBuildLogger = null; 496 497export const checkerResult: CheckerResult = { count: 0 }; 498export const warnCheckerResult: WarnCheckerResult = { count: 0 }; 499export let languageService: ts.LanguageService = null; 500let tsImportSendable: boolean = false; 501export function serviceChecker(rootFileNames: string[], newLogger: Object = null, resolveModulePaths: string[] = null, 502 compilationTime: CompilationTimeStatistics = null, rollupShareObject?: Object): void { 503 fastBuildLogger = newLogger; 504 let cacheFile: string = null; 505 tsImportSendable = rollupShareObject?.projectConfig.tsImportSendable; 506 if (projectConfig.xtsMode || process.env.watchMode === 'true') { 507 if (projectConfig.hotReload) { 508 rootFileNames.forEach(fileName => { 509 hotReloadSupportFiles.add(fileName); 510 }); 511 } 512 const recordInfo = MemoryMonitor.recordStage(MemoryDefine.CREATE_LANGUAGE_SERVICE); 513 languageService = createLanguageService(rootFileNames, resolveModulePaths, compilationTime); 514 MemoryMonitor.stopRecordStage(recordInfo); 515 props = languageService.getProps(); 516 } else { 517 cacheFile = path.resolve(projectConfig.cachePath, '../.ts_checker_cache'); 518 const [isJsonObject, cacheJsonObject]: [boolean, WholeCache | undefined] = isJsonString(cacheFile); 519 const wholeCache: WholeCache = isJsonObject ? cacheJsonObject : { 'runtimeOS': projectConfig.runtimeOS, 'sdkInfo': projectConfig.sdkInfo, 'fileList': {} }; 520 if (wholeCache.runtimeOS === projectConfig.runtimeOS && wholeCache.sdkInfo === projectConfig.sdkInfo) { 521 cache = wholeCache.fileList; 522 } else { 523 cache = {}; 524 } 525 const recordInfo = MemoryMonitor.recordStage(MemoryDefine.CREATE_LANGUAGE_SERVICE); 526 languageService = createLanguageService(rootFileNames, resolveModulePaths, compilationTime, rollupShareObject); 527 MemoryMonitor.stopRecordStage(recordInfo); 528 } 529 530 const timePrinterInstance = ts.ArkTSLinterTimePrinter.getInstance(); 531 timePrinterInstance.setArkTSTimePrintSwitch(false); 532 timePrinterInstance.appendTime(ts.TimePhase.START); 533 startTimeStatisticsLocation(compilationTime ? compilationTime.createProgramTime : undefined); 534 const recordInfo = MemoryMonitor.recordStage(MemoryDefine.GET_BUILDER_PROGRAM); 535 536 globalProgram.builderProgram = languageService.getBuilderProgram(/*withLinterProgram*/ true); 537 globalProgram.program = globalProgram.builderProgram.getProgram(); 538 traverseProgramSourceFiles(languageService.getProps()); 539 props = languageService.getProps(); 540 timePrinterInstance.appendTime(ts.TimePhase.GET_PROGRAM); 541 MemoryMonitor.stopRecordStage(recordInfo); 542 stopTimeStatisticsLocation(compilationTime ? compilationTime.createProgramTime : undefined); 543 544 collectAllFiles(globalProgram.program, undefined, undefined, rollupShareObject); 545 collectFileToIgnoreDiagnostics(rootFileNames); 546 startTimeStatisticsLocation(compilationTime ? compilationTime.runArkTSLinterTime : undefined); 547 const runArkTSLinterRecordInfo = MemoryMonitor.recordStage(MemoryDefine.RUN_ARK_TS_LINTER); 548 const errorCodeLogger: Object | undefined = !!rollupShareObject?.getHvigorConsoleLogger ? 549 rollupShareObject?.getHvigorConsoleLogger(LINTER_SUBSYSTEM_CODE) : undefined; 550 runArkTSLinter(errorCodeLogger); 551 MemoryMonitor.stopRecordStage(runArkTSLinterRecordInfo); 552 stopTimeStatisticsLocation(compilationTime ? compilationTime.runArkTSLinterTime : undefined); 553 554 if (process.env.watchMode !== 'true') { 555 const processBuildHaprrecordInfo = MemoryMonitor.recordStage(MemoryDefine.PROCESS_BUILD_HAP); 556 processBuildHap(cacheFile, rootFileNames, compilationTime, rollupShareObject); 557 MemoryMonitor.stopRecordStage(processBuildHaprrecordInfo); 558 } 559 560 if (globalProgram.program && 561 (process.env.watchMode !== 'true' && !projectConfig.isPreview && 562 !projectConfig.hotReload && !projectConfig.coldReload)) { 563 globalProgram.program.releaseTypeChecker(); 564 const allowGC: boolean = global && global.gc && typeof global.gc === 'function'; 565 if (allowGC) { 566 global.gc(); 567 } 568 } 569} 570 571export function traverseProgramSourceFiles(props: string[]): void { 572 globalProgram.program.getSourceFiles().forEach((sourceFile: ts.SourceFile) => { 573 checkUISyntax(sourceFile, sourceFile.fileName, [], props); 574 }); 575} 576 577function isJsonString(cacheFile: string): [boolean, WholeCache | undefined] { 578 if (fs.existsSync(cacheFile)) { 579 try { 580 return [true, JSON.parse(fs.readFileSync(cacheFile).toString())]; 581 } catch (e) { 582 return [false, undefined]; 583 } 584 } else { 585 return [false, undefined]; 586 } 587} 588 589// collect the compiled files of tsc and rollup for obfuscation scanning. 590export function collectAllFiles(program?: ts.Program, rollupFileList?: IterableIterator<string>, 591 rollupObject?: Object, rollupShareObject: Object = null): void { 592 if (program) { 593 collectTscFiles(program, rollupShareObject); 594 return; 595 } 596 mergeRollUpFiles(rollupFileList, rollupObject); 597} 598 599export function collectTscFiles(program: ts.Program, rollupShareObject: Object = null): void { 600 const programAllFiles: readonly ts.SourceFile[] = program.getSourceFiles(); 601 let projectRootPath: string = projectConfig.projectRootPath; 602 if (!projectRootPath) { 603 return; 604 } 605 projectRootPath = toUnixPath(projectRootPath); 606 const isMacOrWin = isWindows() || isMac(); 607 const recordInfo = MemoryMonitor.recordStage(MemoryDefine.COLLECT_TSC_FILES_ALL_RESOLVED_MODULES); 608 programAllFiles.forEach(sourceFile => { 609 const fileName = toUnixPath(sourceFile.fileName); 610 // @ts-ignore 611 sourceFileDependencies.set(fileName, sourceFile.resolvedModules); 612 if (!(fileName.startsWith(projectRootPath + '/') || isOtherProjectResolvedModulesFilePaths(rollupShareObject, fileName))) { 613 return; 614 } 615 allSourceFilePaths.add(fileName); 616 allModuleIds.add(sourceFile.fileName); 617 // For the windos and mac platform, the file path in filesBuildInfo is in lowercase, 618 // while fileName of sourceFile is the original path (including uppercase). 619 if (filesBuildInfo.size > 0 && 620 Reflect.get(sourceFile, 'version') !== filesBuildInfo.get(isMacOrWin ? tryToLowerCasePath(fileName) : fileName)) { 621 allResolvedModules.add(fileName); 622 } 623 }); 624 MemoryMonitor.stopRecordStage(recordInfo); 625} 626 627function isOtherProjectResolvedModulesFilePaths(rollupShareObject: Object, fileName: string): boolean { 628 if (!!rollupShareObject && rollupShareObject.projectConfig && !!rollupShareObject.projectConfig.rootPathSet) { 629 const rootPathSet: string[] | Set<string> = rollupShareObject.projectConfig.rootPathSet; 630 if (Array.isArray(rootPathSet)) { 631 for (let i = 0; i < rootPathSet.length; i++) { 632 const pathNormalization: string = toUnixPath(rootPathSet[i]) + '/'; 633 if (fileName.startsWith(pathNormalization)) { 634 return true; 635 } 636 } 637 } else { 638 return false; 639 } 640 } 641 return false; 642} 643 644export function mergeRollUpFiles(rollupFileList: IterableIterator<string>, rollupObject: Object): void { 645 const recordInfo = MemoryMonitor.recordStage(MemoryDefine.MERGE_ROLL_UP_FILES_LOCAL_PACKAGE_SET); 646 for (const moduleId of rollupFileList) { 647 if (fs.existsSync(moduleId)) { 648 allSourceFilePaths.add(toUnixPath(moduleId)); 649 allModuleIds.add(moduleId); 650 addLocalPackageSet(moduleId, rollupObject); 651 } 652 } 653 MemoryMonitor.stopRecordStage(recordInfo); 654} 655 656// collect the modulename or pkgname of all local modules. 657export function addLocalPackageSet(moduleId: string, rollupObject: Object): void { 658 const moduleInfo: Object = rollupObject.getModuleInfo(moduleId); 659 const metaInfo: Object = moduleInfo.meta; 660 if (metaInfo.isLocalDependency) { 661 if (projectConfig.useNormalizedOHMUrl && metaInfo.pkgName) { 662 localPackageSet.add(metaInfo.pkgName); 663 } 664 if (!projectConfig.useNormalizedOHMUrl && metaInfo.moduleName) { 665 localPackageSet.add(metaInfo.moduleName); 666 } 667 } 668} 669 670export function emitBuildInfo(): void { 671 globalProgram.builderProgram.emitBuildInfo(buildInfoWriteFile); 672} 673 674function processBuildHap(cacheFile: string, rootFileNames: string[], compilationTime: CompilationTimeStatistics, 675 rollupShareObject: Object): void { 676 startTimeStatisticsLocation(compilationTime ? compilationTime.diagnosticTime : undefined); 677 const semanticRecordInfo = MemoryMonitor.recordStage(MemoryDefine.PROCESS_BUILD_HAP_GET_SEMANTIC_DIAGNOSTICS); 678 const allDiagnostics: ts.Diagnostic[] = globalProgram.builderProgram 679 .getSyntacticDiagnostics() 680 .concat(globalProgram.builderProgram.getSemanticDiagnostics()); 681 MemoryMonitor.stopRecordStage(semanticRecordInfo); 682 stopTimeStatisticsLocation(compilationTime ? compilationTime.diagnosticTime : undefined); 683 const emitBuildRecordInfo = MemoryMonitor.recordStage(MemoryDefine.PROCESS_BUILD_HAP_EMIT_BUILD_INFO); 684 emitBuildInfo(); 685 let errorCodeLogger: Object | undefined = rollupShareObject?.getHvigorConsoleLogger ? 686 rollupShareObject?.getHvigorConsoleLogger(TSC_SYSTEM_CODE) : undefined; 687 688 allDiagnostics.forEach((diagnostic: ts.Diagnostic) => { 689 printDiagnostic(diagnostic, ErrorCodeModule.TSC, errorCodeLogger); 690 }); 691 MemoryMonitor.stopRecordStage(emitBuildRecordInfo); 692 if (!projectConfig.xtsMode) { 693 fse.ensureDirSync(projectConfig.cachePath); 694 fs.writeFileSync(cacheFile, JSON.stringify({ 695 'runtimeOS': projectConfig.runtimeOS, 696 'sdkInfo': projectConfig.sdkInfo, 697 'fileList': cache 698 }, null, 2)); 699 } 700 if (projectConfig.compileHar || projectConfig.compileShared) { 701 let emit: string | undefined = undefined; 702 let writeFile = (fileName: string, text: string, writeByteOrderMark: boolean): void => { 703 emit = text; 704 }; 705 [...allResolvedModules, ...rootFileNames].forEach(moduleFile => { 706 if (!(moduleFile.match(new RegExp(projectConfig.packageDir)) && projectConfig.compileHar)) { 707 try { 708 if ((/\.d\.e?ts$/).test(moduleFile)) { 709 generateSourceFilesInHar(moduleFile, fs.readFileSync(moduleFile, 'utf-8'), path.extname(moduleFile), 710 projectConfig, projectConfig.modulePathMap); 711 } else if ((/\.e?ts$/).test(moduleFile)) { 712 emit = undefined; 713 let sourcefile = globalProgram.program.getSourceFile(moduleFile); 714 if (sourcefile) { 715 globalProgram.program.emit(sourcefile, writeFile, undefined, true, undefined, true); 716 } 717 if (emit) { 718 generateSourceFilesInHar(moduleFile, emit, '.d' + path.extname(moduleFile), projectConfig, projectConfig.modulePathMap); 719 } 720 } 721 } catch (err) { } 722 } 723 }); 724 printDeclarationDiagnostics(errorCodeLogger); 725 } 726} 727 728function printDeclarationDiagnostics(errorCodeLogger?: Object | undefined): void { 729 globalProgram.builderProgram.getDeclarationDiagnostics().forEach((diagnostic: ts.Diagnostic) => { 730 printDiagnostic(diagnostic, ErrorCodeModule.TSC, errorCodeLogger); 731 }); 732} 733 734function containFormError(message: string): boolean { 735 if (/can't support form application./.test(message)) { 736 return true; 737 } 738 return false; 739} 740 741let fileToIgnoreDiagnostics: Set<string> | undefined = undefined; 742 743function collectFileToThrowDiagnostics(file: string, fileToThrowDiagnostics: Set<string>): void { 744 const normalizedFilePath: string = path.resolve(file); 745 const unixFilePath: string = toUnixPath(file); 746 if (fileToThrowDiagnostics.has(unixFilePath)) { 747 return; 748 } 749 750 fileToThrowDiagnostics.add(unixFilePath); 751 // Although the cache object filters JavaScript files when collecting dependency relationships, we still include the 752 // filtering of JavaScript files here to avoid potential omissions. 753 if ((/\.(c|m)?js$/).test(file) || 754 !cache[normalizedFilePath] || cache[normalizedFilePath].children.length === 0) { 755 return; 756 } 757 cache[normalizedFilePath].children.forEach(file => { 758 collectFileToThrowDiagnostics(file, fileToThrowDiagnostics); 759 }); 760} 761 762export function collectFileToIgnoreDiagnostics(rootFileNames: string[]): void { 763 if (getArkTSLinterMode() === ArkTSLinterMode.NOT_USE) { 764 return; 765 } 766 767 // In watch mode, the `beforeBuild` phase will clear the parent and children fields in the cache. For files that have 768 // not been modified, the information needs to be restored using the `resolvedModuleNames` variable. 769 if (process.env.watchMode === 'true') { 770 for (let [file, resolvedModules] of resolvedModulesCache) { 771 createOrUpdateCache(resolvedModules, file); 772 } 773 } 774 775 // With arkts linter enabled, `allowJs` option is set to true, resulting JavaScript files themselves and 776 // JavaScript-referenced files are included in the tsc program and checking process, 777 // potentially introducing new errors. For instance, in scenarios where an ets file imports js file imports ts file, 778 // it’s necessary to filter out errors from ts files. 779 let fileToThrowDiagnostics: Set<string> = new Set<string>(); 780 rootFileNames.forEach(file => { 781 if (!(/\.(c|m)?js$/).test(file)) { 782 collectFileToThrowDiagnostics(file, fileToThrowDiagnostics); 783 } 784 }); 785 786 let resolvedTypeReferenceDirectivesFiles: Set<string> = new Set<string>(); 787 globalProgram.program.getResolvedTypeReferenceDirectives().forEach( 788 (elem: ts.ResolvedTypeReferenceDirective | undefined) => { 789 elem && elem.resolvedFileName && resolvedTypeReferenceDirectivesFiles.add(elem.resolvedFileName); 790 }); 791 792 const ignoreDiagnosticsRecordInfo = MemoryMonitor.recordStage(MemoryDefine.FILE_TO_IGNORE_DIAGNOSTICS); 793 fileToIgnoreDiagnostics = new Set<string>(); 794 globalProgram.program.getSourceFiles().forEach(sourceFile => { 795 // Previous projects had js libraries that were available through SDK, so need to filter js-file in SDK, 796 // like: hypium library 797 sourceFile.fileName && 798 (!isInSDK(sourceFile.fileName) || (/\.(c|m)?js$/).test(sourceFile.fileName)) && 799 !resolvedTypeReferenceDirectivesFiles.has(sourceFile.fileName) && 800 fileToIgnoreDiagnostics.add(toUnixPath(sourceFile.fileName)); 801 }); 802 803 fileToThrowDiagnostics.forEach(file => { 804 fileToIgnoreDiagnostics.delete(file); 805 }); 806 MemoryMonitor.stopRecordStage(ignoreDiagnosticsRecordInfo); 807} 808 809interface MessageCollection { 810 positionMessage: string, 811 message: string, 812 logMessage: string 813} 814 815export function printDiagnostic(diagnostic: ts.Diagnostic, flag?: ErrorCodeModule, errorCodeLogger?: Object | undefined): void { 816 if (projectConfig.ignoreWarning) { 817 return; 818 } 819 820 if (fileToIgnoreDiagnostics && diagnostic.file && diagnostic.file.fileName && 821 fileToIgnoreDiagnostics.has(toUnixPath(diagnostic.file.fileName))) { 822 return; 823 } 824 825 const message: string = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); 826 if (validateError(message)) { 827 if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) { 828 updateErrorFileCache(diagnostic); 829 } 830 831 if (containFormError(message) && !isCardFile(diagnostic.file.fileName)) { 832 return; 833 } 834 835 const logPrefix: string = diagnostic.category === ts.DiagnosticCategory.Error ? 'ERROR' : 'WARN'; 836 const etsCheckerLogger = fastBuildLogger || logger; 837 let logMessage: string; 838 if (logPrefix === 'ERROR') { 839 checkerResult.count += 1; 840 } else { 841 warnCheckerResult.count += 1; 842 } 843 let positionMessage: string = ''; 844 if (diagnostic.file) { 845 const { line, character }: ts.LineAndCharacter = 846 diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!); 847 positionMessage = `File: ${diagnostic.file.fileName}:${line + 1}:${character + 1}`; 848 logMessage = `ArkTS:${logPrefix} ${positionMessage}\n ${message}\n`; 849 } else { 850 logMessage = `ArkTS:${logPrefix}: ${message}`; 851 } 852 853 if (errorCodeLogger) { 854 const msgCollection: MessageCollection = { positionMessage, message, logMessage }; 855 printErrorCode(diagnostic, etsCheckerLogger, msgCollection, errorCodeLogger, flag); 856 } else { 857 if (diagnostic.category === ts.DiagnosticCategory.Error) { 858 etsCheckerLogger.error('\u001b[31m' + logMessage); 859 } else { 860 etsCheckerLogger.warn('\u001b[33m' + logMessage); 861 } 862 } 863 } 864} 865 866function printErrorCode(diagnostic: ts.Diagnostic, etsCheckerLogger: Object, 867 msgCollection: MessageCollection, errorCodeLogger: Object, flag: ErrorCodeModule | undefined): void { 868 const { positionMessage, message, logMessage } = msgCollection; 869 // If the diagnostic is not an error, log a warning and return early. 870 if (diagnostic.category !== ts.DiagnosticCategory.Error) { 871 etsCheckerLogger.warn('\u001b[33m' + logMessage); 872 return; 873 } 874 875 // Check for TSC error codes 876 if (flag === ErrorCodeModule.TSC && 877 validateUseErrorCodeLogger(ErrorCodeModule.TSC, diagnostic.code)) { 878 const errorCode = ts.getErrorCode(diagnostic); 879 errorCodeLogger.printError(errorCode); 880 return; 881 } 882 883 // Check for LINTER error codes 884 if (flag === ErrorCodeModule.LINTER || (flag === ErrorCodeModule.TSC && 885 validateUseErrorCodeLogger(ErrorCodeModule.LINTER, diagnostic.code))) { 886 const linterErrorInfo: HvigorErrorInfo = transfromErrorCode(diagnostic.code, positionMessage, message); 887 errorCodeLogger.printError(linterErrorInfo); 888 return; 889 } 890 891 // Check for ArkUI error codes 892 if (flag === ErrorCodeModule.UI || (flag === ErrorCodeModule.TSC && 893 validateUseErrorCodeLogger(ErrorCodeModule.UI, diagnostic.code))) { 894 const uiErrorInfo: HvigorErrorInfo | undefined = buildErrorInfoFromDiagnostic( 895 diagnostic.code, positionMessage, message); 896 if (!uiErrorInfo) { 897 etsCheckerLogger.error('\u001b[31m' + logMessage); 898 } else { 899 errorCodeLogger.printError(uiErrorInfo); 900 } 901 return; 902 } 903 904 // If the error is not a TSC/Linter/ArkUI error, log using etsCheckerLogger 905 etsCheckerLogger.error('\u001b[31m' + logMessage); 906} 907 908function validateUseErrorCodeLogger(flag: ErrorCodeModule, code: number): boolean { 909 if (!ts.getErrorCodeArea || !ts.getErrorCode) { 910 return false; 911 } 912 if (flag === ErrorCodeModule.TSC) { 913 return ts.getErrorCodeArea(code) === ts.ErrorCodeArea.TSC; 914 } else if (flag === ErrorCodeModule.LINTER) { 915 return ts.getErrorCodeArea(code) === ts.ErrorCodeArea.LINTER; 916 } else if (flag === ErrorCodeModule.UI) { 917 return ts.getErrorCodeArea(code) === ts.ErrorCodeArea.UI; 918 } 919 return false; 920} 921 922function validateError(message: string): boolean { 923 const propInfoReg: RegExp = /Cannot find name\s*'(\$?\$?[_a-zA-Z0-9]+)'/; 924 const stateInfoReg: RegExp = /Property\s*'(\$?[_a-zA-Z0-9]+)' does not exist on type/; 925 if (matchMessage(message, props, propInfoReg) || 926 matchMessage(message, props, stateInfoReg)) { 927 return false; 928 } 929 return true; 930} 931function matchMessage(message: string, nameArr: any, reg: RegExp): boolean { 932 if (reg.test(message)) { 933 const match: string[] = message.match(reg); 934 if (match[1] && nameArr.includes(match[1])) { 935 return true; 936 } 937 } 938 return false; 939} 940 941function updateErrorFileCache(diagnostic: ts.Diagnostic): void { 942 if (!diagnostic.file) { 943 return; 944 } 945 946 let cacheInfo: CacheFileName = cache[path.resolve(diagnostic.file.fileName)]; 947 if (cacheInfo) { 948 cacheInfo.error = true; 949 if (!cacheInfo.errorCodes) { 950 cacheInfo.errorCodes = []; 951 } 952 cacheInfo.errorCodes.includes(diagnostic.code) || cacheInfo.errorCodes.push(diagnostic.code); 953 } 954} 955 956function filterInput(rootFileNames: string[]): string[] { 957 return rootFileNames.filter((file: string) => { 958 const needUpdate: NeedUpdateFlag = { flag: false }; 959 const alreadyCheckedFiles: Set<string> = new Set(); 960 checkNeedUpdateFiles(path.resolve(file), needUpdate, alreadyCheckedFiles); 961 if (!needUpdate.flag) { 962 storedFileInfo.changeFiles.push(path.resolve(file)); 963 } 964 return needUpdate.flag; 965 }); 966} 967 968function checkNeedUpdateFiles(file: string, needUpdate: NeedUpdateFlag, alreadyCheckedFiles: Set<string>): void { 969 if (alreadyCheckedFiles.has(file)) { 970 return; 971 } else { 972 alreadyCheckedFiles.add(file); 973 } 974 975 if (needUpdate.flag) { 976 return; 977 } 978 979 const value: CacheFileName = cache[file]; 980 const mtimeMs: number = fs.statSync(file).mtimeMs; 981 if (value) { 982 if (value.error || value.mtimeMs !== mtimeMs) { 983 needUpdate.flag = true; 984 return; 985 } 986 for (let i = 0; i < value.children.length; ++i) { 987 if (fs.existsSync(value.children[i])) { 988 checkNeedUpdateFiles(value.children[i], needUpdate, alreadyCheckedFiles); 989 } else { 990 needUpdate.flag = true; 991 } 992 } 993 } else { 994 cache[file] = { mtimeMs, children: [], parent: [], error: false }; 995 needUpdate.flag = true; 996 } 997} 998 999const fileExistsCache: Map<string, boolean> = new Map<string, boolean>(); 1000const dirExistsCache: Map<string, boolean> = new Map<string, boolean>(); 1001const moduleResolutionHost: ts.ModuleResolutionHost = { 1002 fileExists: (fileName: string): boolean => { 1003 let exists = fileExistsCache.get(fileName); 1004 if (exists === undefined) { 1005 exists = ts.sys.fileExists(fileName); 1006 fileExistsCache.set(fileName, exists); 1007 } 1008 return exists; 1009 }, 1010 directoryExists: (directoryName: string): boolean => { 1011 let exists = dirExistsCache.get(directoryName); 1012 if (exists === undefined) { 1013 exists = ts.sys.directoryExists(directoryName); 1014 dirExistsCache.set(directoryName, exists); 1015 } 1016 return exists; 1017 }, 1018 readFile(fileName: string): string | undefined { 1019 return ts.sys.readFile(fileName); 1020 }, 1021 realpath(path: string): string { 1022 return ts.sys.realpath(path); 1023 }, 1024 trace(s: string): void { 1025 console.info(s); 1026 } 1027}; 1028 1029//This is only for test 1030export const moduleResolutionHostTest = moduleResolutionHost; 1031 1032export function resolveTypeReferenceDirectives(typeDirectiveNames: string[] | ts.FileReference[]): ts.ResolvedTypeReferenceDirective[] { 1033 if (typeDirectiveNames.length === 0) { 1034 return []; 1035 } 1036 1037 const resolvedTypeReferenceCache: ts.ResolvedTypeReferenceDirective[] = []; 1038 const cache: Map<string, ts.ResolvedTypeReferenceDirective> = new Map<string, ts.ResolvedTypeReferenceDirective>(); 1039 const containingFile: string = path.join(projectConfig.modulePath, 'build-profile.json5'); 1040 1041 for (const entry of typeDirectiveNames) { 1042 const typeName = isString(entry) ? entry : entry.fileName.toLowerCase(); 1043 if (!cache.has(typeName)) { 1044 const resolvedFile = ts.resolveTypeReferenceDirective(typeName, containingFile, compilerOptions, moduleResolutionHost); 1045 if (!resolvedFile || !resolvedFile.resolvedTypeReferenceDirective) { 1046 logger.error('\u001b[31m', `ArkTS:Cannot find type definition file for: ${typeName}\n`); 1047 } 1048 const result: ts.ResolvedTypeReferenceDirective = resolvedFile.resolvedTypeReferenceDirective; 1049 cache.set(typeName, result); 1050 resolvedTypeReferenceCache.push(result); 1051 } 1052 } 1053 return resolvedTypeReferenceCache; 1054} 1055 1056// resolvedModulesCache records the files and their dependencies of program. 1057export const resolvedModulesCache: Map<string, ts.ResolvedModuleFull[]> = new Map(); 1058 1059export function resolveModuleNames(moduleNames: string[], containingFile: string): ts.ResolvedModuleFull[] { 1060 startTimeStatisticsLocation(resolveModuleNamesTime); 1061 const resolvedModules: ts.ResolvedModuleFull[] = []; 1062 const cacheFileContent: ts.ResolvedModuleFull[] = resolvedModulesCache.get(path.resolve(containingFile)); 1063 if (![...shouldResolvedFiles].length || shouldResolvedFiles.has(path.resolve(containingFile)) || 1064 !(cacheFileContent && cacheFileContent.length === moduleNames.length)) { 1065 for (const moduleName of moduleNames) { 1066 const result = ts.resolveModuleName(moduleName, containingFile, compilerOptions, moduleResolutionHost); 1067 if (result.resolvedModule) { 1068 if (result.resolvedModule.resolvedFileName && 1069 path.extname(result.resolvedModule.resolvedFileName) === EXTNAME_JS) { 1070 const resultDETSPath: string = 1071 result.resolvedModule.resolvedFileName.replace(EXTNAME_JS, EXTNAME_D_ETS); 1072 if (ts.sys.fileExists(resultDETSPath)) { 1073 resolvedModules.push(getResolveModule(resultDETSPath, EXTNAME_D_ETS)); 1074 } else { 1075 resolvedModules.push(result.resolvedModule); 1076 } 1077 } else { 1078 resolvedModules.push(result.resolvedModule); 1079 } 1080 } else if (new RegExp(`^@(${sdkConfigPrefix})\\.`, 'i').test(moduleName.trim())) { 1081 let apiFileExist: boolean = false; 1082 for (let i = 0; i < sdkConfigs.length; i++) { 1083 const sdkConfig = sdkConfigs[i]; 1084 const resolveModuleInfo: ResolveModuleInfo = getRealModulePath(sdkConfig.apiPath, moduleName, ['.d.ts', '.d.ets']); 1085 const modulePath: string = resolveModuleInfo.modulePath; 1086 const isDETS: boolean = resolveModuleInfo.isEts; 1087 if (systemModules.includes(moduleName + (isDETS ? '.d.ets' : '.d.ts')) && ts.sys.fileExists(modulePath)) { 1088 resolvedModules.push(getResolveModule(modulePath, isDETS ? '.d.ets' : '.d.ts')); 1089 apiFileExist = true; 1090 break; 1091 } 1092 } 1093 if (!apiFileExist) { 1094 resolvedModules.push(null); 1095 } 1096 } else if (/\.ets$/.test(moduleName) && !/\.d\.ets$/.test(moduleName)) { 1097 const modulePath: string = path.resolve(path.dirname(containingFile), moduleName); 1098 if (ts.sys.fileExists(modulePath)) { 1099 resolvedModules.push(getResolveModule(modulePath, '.ets')); 1100 } else { 1101 resolvedModules.push(null); 1102 } 1103 } else if (/\.ts$/.test(moduleName)) { 1104 const modulePath: string = path.resolve(path.dirname(containingFile), moduleName); 1105 if (ts.sys.fileExists(modulePath)) { 1106 resolvedModules.push(getResolveModule(modulePath, '.ts')); 1107 } else { 1108 resolvedModules.push(null); 1109 } 1110 } else { 1111 const modulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ts'); 1112 const systemDETSModulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ets'); 1113 const kitModulePath: string = path.resolve(__dirname, '../../../kits', moduleName + '.d.ts'); 1114 const kitSystemDETSModulePath: string = path.resolve(__dirname, '../../../kits', moduleName + '.d.ets'); 1115 const suffix: string = /\.js$/.test(moduleName) ? '' : '.js'; 1116 const jsModulePath: string = path.resolve(__dirname, '../node_modules', moduleName + suffix); 1117 const fileModulePath: string = 1118 path.resolve(__dirname, '../node_modules', moduleName + '/index.js'); 1119 const DETSModulePath: string = path.resolve(path.dirname(containingFile), 1120 /\.d\.ets$/.test(moduleName) ? moduleName : moduleName + EXTNAME_D_ETS); 1121 if (ts.sys.fileExists(modulePath)) { 1122 resolvedModules.push(getResolveModule(modulePath, '.d.ts')); 1123 } else if (ts.sys.fileExists(systemDETSModulePath)) { 1124 resolvedModules.push(getResolveModule(systemDETSModulePath, '.d.ets')); 1125 } else if (ts.sys.fileExists(kitModulePath)) { 1126 resolvedModules.push(getResolveModule(kitModulePath, '.d.ts')); 1127 } else if (ts.sys.fileExists(kitSystemDETSModulePath)) { 1128 resolvedModules.push(getResolveModule(kitSystemDETSModulePath, '.d.ets')); 1129 } else if (ts.sys.fileExists(jsModulePath)) { 1130 resolvedModules.push(getResolveModule(jsModulePath, '.js')); 1131 } else if (ts.sys.fileExists(fileModulePath)) { 1132 resolvedModules.push(getResolveModule(fileModulePath, '.js')); 1133 } else if (ts.sys.fileExists(DETSModulePath)) { 1134 resolvedModules.push(getResolveModule(DETSModulePath, '.d.ets')); 1135 } else { 1136 const srcIndex: number = projectConfig.projectPath.indexOf('src' + path.sep + 'main'); 1137 let DETSModulePathFromModule: string; 1138 if (srcIndex > 0) { 1139 DETSModulePathFromModule = path.resolve( 1140 projectConfig.projectPath.substring(0, srcIndex), moduleName + path.sep + 'index' + EXTNAME_D_ETS); 1141 if (DETSModulePathFromModule && ts.sys.fileExists(DETSModulePathFromModule)) { 1142 resolvedModules.push(getResolveModule(DETSModulePathFromModule, '.d.ets')); 1143 } else { 1144 resolvedModules.push(null); 1145 } 1146 } else { 1147 resolvedModules.push(null); 1148 } 1149 } 1150 } 1151 if (projectConfig.hotReload && resolvedModules.length && 1152 resolvedModules[resolvedModules.length - 1]) { 1153 hotReloadSupportFiles.add(path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName)); 1154 } 1155 if (collectShouldPackedFiles(resolvedModules)) { 1156 allResolvedModules.add(resolvedModules[resolvedModules.length - 1].resolvedFileName); 1157 } 1158 } 1159 if (!projectConfig.xtsMode) { 1160 createOrUpdateCache(resolvedModules, path.resolve(containingFile)); 1161 } 1162 resolvedModulesCache.set(path.resolve(containingFile), resolvedModules); 1163 stopTimeStatisticsLocation(resolveModuleNamesTime); 1164 return resolvedModules; 1165 } 1166 stopTimeStatisticsLocation(resolveModuleNamesTime); 1167 return resolvedModulesCache.get(path.resolve(containingFile)); 1168} 1169 1170export interface ResolveModuleInfo { 1171 modulePath: string; 1172 isEts: boolean; 1173} 1174 1175function collectShouldPackedFiles(resolvedModules: ts.ResolvedModuleFull[]): boolean | RegExpMatchArray { 1176 return (projectConfig.compileHar || projectConfig.compileShared) && resolvedModules[resolvedModules.length - 1] && 1177 resolvedModules[resolvedModules.length - 1].resolvedFileName && 1178 (path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match(/(\.[^d]|[^\.]d|[^\.][^d])\.e?ts$/) || 1179 path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match(/\.d\.e?ts$/) && 1180 path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match( 1181 new RegExp('\\' + path.sep + 'src' + '\\' + path.sep + 'main' + '\\' + path.sep))); 1182} 1183 1184function createOrUpdateCache(resolvedModules: ts.ResolvedModuleFull[], containingFile: string): void { 1185 const children: string[] = []; 1186 const error: boolean = false; 1187 resolvedModules.forEach(moduleObj => { 1188 if (moduleObj && moduleObj.resolvedFileName && /\.(ets|ts)$/.test(moduleObj.resolvedFileName)) { 1189 const file: string = path.resolve(moduleObj.resolvedFileName); 1190 const mtimeMs: number = fs.statSync(file).mtimeMs; 1191 children.push(file); 1192 const value: CacheFileName = cache[file]; 1193 if (value) { 1194 value.mtimeMs = mtimeMs; 1195 value.error = error; 1196 value.parent = value.parent || []; 1197 value.parent.push(path.resolve(containingFile)); 1198 value.parent = [...new Set(value.parent)]; 1199 } else { 1200 cache[file] = { mtimeMs, children: [], parent: [containingFile], error }; 1201 } 1202 } 1203 }); 1204 cache[path.resolve(containingFile)] = { 1205 mtimeMs: fs.statSync(containingFile).mtimeMs, children, 1206 parent: cache[path.resolve(containingFile)] && cache[path.resolve(containingFile)].parent ? 1207 cache[path.resolve(containingFile)].parent : [], error 1208 }; 1209} 1210 1211export function createWatchCompilerHost(rootFileNames: string[], 1212 reportDiagnostic: ts.DiagnosticReporter, delayPrintLogCount: Function, resetErrorCount: Function, 1213 isPipe: boolean = false, resolveModulePaths: string[] = null): ts.WatchCompilerHostOfFilesAndCompilerOptions<ts.BuilderProgram> { 1214 if (projectConfig.hotReload) { 1215 rootFileNames.forEach(fileName => { 1216 hotReloadSupportFiles.add(fileName); 1217 }); 1218 } 1219 if (!(isPipe && process.env.compileTool === 'rollup')) { 1220 setCompilerOptions(resolveModulePaths); 1221 } 1222 // Change the buildInfo file path, or it will cover the buildInfo file created before. 1223 const buildInfoPath: string = path.resolve(projectConfig.cachePath, '..', WATCH_COMPILER_BUILD_INFO_SUFFIX); 1224 const watchCompilerOptions = {...compilerOptions, tsBuildInfoFile: buildInfoPath}; 1225 const createProgram = ts.createSemanticDiagnosticsBuilderProgram; 1226 const host = ts.createWatchCompilerHost( 1227 [...rootFileNames, ...readDeaclareFiles()], watchCompilerOptions, 1228 ts.sys, createProgram, reportDiagnostic, 1229 (diagnostic: ts.Diagnostic) => { 1230 if ([6031, 6032].includes(diagnostic.code)) { 1231 if (!isPipe) { 1232 process.env.watchTs = 'start'; 1233 resetErrorCount(); 1234 } 1235 } 1236 // End of compilation in watch mode flag. 1237 if ([6193, 6194].includes(diagnostic.code)) { 1238 if (!isPipe) { 1239 process.env.watchTs = 'end'; 1240 if (fastBuildLogger) { 1241 fastBuildLogger.debug(TS_WATCH_END_MSG); 1242 tsWatchEmitter.emit(TS_WATCH_END_MSG); 1243 } 1244 } 1245 delayPrintLogCount(); 1246 } 1247 }); 1248 host.readFile = (fileName: string) => { 1249 if (!fs.existsSync(fileName)) { 1250 return undefined; 1251 } 1252 if (/(?<!\.d)\.(ets|ts)$/.test(fileName)) { 1253 let content: string = processContent(fs.readFileSync(fileName).toString(), fileName); 1254 const extendFunctionInfo: extendInfo[] = []; 1255 content = instanceInsteadThis(content, fileName, extendFunctionInfo, props); 1256 return content; 1257 } 1258 return fs.readFileSync(fileName).toString(); 1259 }; 1260 host.resolveModuleNames = resolveModuleNames; 1261 host.resolveTypeReferenceDirectives = resolveTypeReferenceDirectives; 1262 return host; 1263} 1264 1265export function watchChecker(rootFileNames: string[], newLogger: any = null, resolveModulePaths: string[] = null): void { 1266 fastBuildLogger = newLogger; 1267 globalProgram.watchProgram = ts.createWatchProgram( 1268 createWatchCompilerHost(rootFileNames, printDiagnostic, () => { }, () => { }, false, resolveModulePaths)); 1269} 1270 1271export function instanceInsteadThis(content: string, fileName: string, extendFunctionInfo: extendInfo[], 1272 props: string[]): string { 1273 extendFunctionInfo.reverse().forEach((item) => { 1274 const subStr: string = content.substring(item.start, item.end); 1275 const insert: string = subStr.replace(/(\s)\$(\.)/g, (origin, item1, item2) => { 1276 return item1 + item.compName + 'Instance' + item2; 1277 }); 1278 content = content.slice(0, item.start) + insert + content.slice(item.end); 1279 }); 1280 return content; 1281} 1282 1283function getResolveModule(modulePath: string, type): ts.ResolvedModuleFull { 1284 return { 1285 resolvedFileName: modulePath, 1286 isExternalLibraryImport: false, 1287 extension: type 1288 }; 1289} 1290 1291export const dollarCollection: Set<string> = new Set(); 1292export const extendCollection: Set<string> = new Set(); 1293export const importModuleCollection: Set<string> = new Set(); 1294 1295function checkUISyntax(sourceFile: ts.SourceFile, fileName: string, extendFunctionInfo: extendInfo[], props: string[]): void { 1296 if (/\.ets$/.test(fileName) && !/\.d.ets$/.test(fileName)) { 1297 if (process.env.compileMode === 'moduleJson' || 1298 path.resolve(fileName) !== path.resolve(projectConfig.projectPath, 'app.ets')) { 1299 collectComponents(sourceFile); 1300 collectionCustomizeStyles(sourceFile); 1301 parseAllNode(sourceFile, sourceFile, extendFunctionInfo); 1302 props.push(...dollarCollection, ...extendCollection); 1303 } 1304 } 1305} 1306 1307function collectionCustomizeStyles(node: ts.Node): void { 1308 if ((ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) && 1309 (isUIDecorator(node, COMPONENT_STYLES_DECORATOR) || isUIDecorator(node, COMPONENT_EXTEND_DECORATOR)) && 1310 node.name && ts.isIdentifier(node.name)) { 1311 BUILDIN_STYLE_NAMES.add(node.name.escapedText.toString()); 1312 } 1313 if (ts.isSourceFile(node)) { 1314 node.statements.forEach((item: ts.Node) => { 1315 return collectionCustomizeStyles(item); 1316 }); 1317 } else if (ts.isStructDeclaration(node)) { 1318 node.members.forEach((item: ts.Node) => { 1319 return collectionCustomizeStyles(item); 1320 }); 1321 } 1322} 1323 1324function isUIDecorator(node: ts.MethodDeclaration | ts.FunctionDeclaration | 1325 ts.StructDeclaration | ts.ClassDeclaration, decortorName: string): boolean { 1326 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node); 1327 if (decorators && decorators.length) { 1328 for (let i = 0; i < decorators.length; i++) { 1329 const originalDecortor: string = decorators[i].getText().replace(/\(.*\)$/, '').trim(); 1330 if (originalDecortor === decortorName) { 1331 return true; 1332 } else { 1333 return false; 1334 } 1335 } 1336 } 1337 return false; 1338} 1339function collectComponents(node: ts.SourceFile): void { 1340 // @ts-ignore 1341 if (process.env.watchMode !== 'true' && node.identifiers && node.identifiers.size) { 1342 // @ts-ignore 1343 for (const key of node.identifiers.keys()) { 1344 if (JS_BIND_COMPONENTS.has(key)) { 1345 appComponentCollection.get(path.join(node.fileName)).add(key); 1346 } 1347 } 1348 } 1349} 1350 1351function parseAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, extendFunctionInfo: extendInfo[]): void { 1352 if (ts.isStructDeclaration(node)) { 1353 if (node.members) { 1354 node.members.forEach(item => { 1355 if (ts.isPropertyDeclaration(item) && ts.isIdentifier(item.name)) { 1356 const propertyName: string = item.name.getText(); 1357 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(item); 1358 if (decorators && decorators.length) { 1359 for (let i = 0; i < decorators.length; i++) { 1360 const decoratorName: string = decorators[i].getText().replace(/\(.*\)$/, '').trim(); 1361 if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { 1362 dollarCollection.add('$' + propertyName); 1363 } 1364 } 1365 } 1366 } 1367 }); 1368 } 1369 } 1370 if (process.env.watchMode !== 'true' && ts.isIfStatement(node)) { 1371 appComponentCollection.get(path.join(sourceFileNode.fileName)).add(COMPONENT_IF); 1372 } 1373 if (ts.isMethodDeclaration(node) && node.name.getText() === COMPONENT_BUILD_FUNCTION || 1374 (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node)) && 1375 hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) { 1376 if (node.body && node.body.statements && node.body.statements.length) { 1377 const checkProp: ts.NodeArray<ts.Statement> = node.body.statements; 1378 checkProp.forEach((item, index) => { 1379 traverseBuild(item, index); 1380 }); 1381 } 1382 } 1383 if (ts.isFunctionDeclaration(node) && hasDecorator(node, COMPONENT_EXTEND_DECORATOR)) { 1384 if (node.body && node.body.statements && node.body.statements.length && 1385 !isOriginalExtend(node.body)) { 1386 extendFunctionInfo.push({ 1387 start: node.pos, 1388 end: node.end, 1389 compName: isExtendFunction(node, { decoratorName: '', componentName: '' }) 1390 }); 1391 } 1392 } 1393 ts.forEachChild(node, (child: ts.Node) => parseAllNode(child, sourceFileNode, extendFunctionInfo)); 1394} 1395 1396function isForeachAndLzayForEach(node: ts.Node): boolean { 1397 return ts.isCallExpression(node) && node.expression && ts.isIdentifier(node.expression) && 1398 FOREACH_LAZYFOREACH.has(node.expression.escapedText.toString()) && node.arguments && node.arguments[1] && 1399 ts.isArrowFunction(node.arguments[1]) && node.arguments[1].body && ts.isBlock(node.arguments[1].body); 1400} 1401 1402function getComponentName(node: ts.Node): string { 1403 let temp = node.expression; 1404 let name: string; 1405 while (temp) { 1406 if (ts.isIdentifier(temp) && temp.parent && (ts.isCallExpression(temp.parent) || 1407 ts.isEtsComponentExpression(temp.parent))) { 1408 name = temp.escapedText.toString(); 1409 break; 1410 } 1411 temp = temp.expression; 1412 } 1413 return name; 1414} 1415 1416function traverseBuild(node: ts.Node, index: number): void { 1417 if (ts.isExpressionStatement(node)) { 1418 const parentComponentName: string = getComponentName(node); 1419 node = node.expression; 1420 while (node) { 1421 if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) && 1422 ts.isIdentifier(node.expression) && !DOLLAR_BLOCK_INTERFACE.has(node.expression.escapedText.toString())) { 1423 node.body.statements.forEach((item: ts.Statement, indexBlock: number) => { 1424 traverseBuild(item, indexBlock); 1425 }); 1426 break; 1427 } else if (isForeachAndLzayForEach(node)) { 1428 node.arguments[1].body.statements.forEach((item: ts.Statement, indexBlock: number) => { 1429 traverseBuild(item, indexBlock); 1430 }); 1431 break; 1432 } else { 1433 loopNodeFindDoubleDollar(node, parentComponentName); 1434 if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) && 1435 ts.isIdentifier(node.expression)) { 1436 node.body.statements.forEach((item: ts.Statement, indexBlock: number) => { 1437 traverseBuild(item, indexBlock); 1438 }); 1439 break; 1440 } 1441 } 1442 node = node.expression; 1443 } 1444 } else if (ts.isIfStatement(node)) { 1445 ifInnerDollarAttribute(node); 1446 } 1447} 1448 1449function ifInnerDollarAttribute(node: ts.IfStatement): void { 1450 if (node.thenStatement && ts.isBlock(node.thenStatement) && node.thenStatement.statements) { 1451 node.thenStatement.statements.forEach((item, indexIfBlock) => { 1452 traverseBuild(item, indexIfBlock); 1453 }); 1454 } 1455 if (node.elseStatement) { 1456 elseInnerDollarAttribute(node); 1457 } 1458} 1459 1460function elseInnerDollarAttribute(node: ts.IfStatement): void { 1461 if (ts.isIfStatement(node.elseStatement) && node.elseStatement.thenStatement && ts.isBlock(node.elseStatement.thenStatement)) { 1462 traverseBuild(node.elseStatement, 0); 1463 } else if (ts.isBlock(node.elseStatement) && node.elseStatement.statements) { 1464 node.elseStatement.statements.forEach((item, indexElseBlock) => { 1465 traverseBuild(item, indexElseBlock); 1466 }); 1467 } 1468} 1469 1470function isPropertiesAddDoubleDollar(node: ts.Node): boolean { 1471 if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.arguments && node.arguments.length) { 1472 return true; 1473 } else if (ts.isEtsComponentExpression(node) && ts.isIdentifier(node.expression) && 1474 DOLLAR_BLOCK_INTERFACE.has(node.expression.escapedText.toString())) { 1475 return true; 1476 } else { 1477 return false; 1478 } 1479} 1480function loopNodeFindDoubleDollar(node: ts.Node, parentComponentName: string): void { 1481 if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) { 1482 const argument: ts.NodeArray<ts.Node> = node.arguments; 1483 const propertyName: ts.Identifier | ts.PrivateIdentifier = node.expression.name; 1484 if (isCanAddDoubleDollar(propertyName.getText(), parentComponentName)) { 1485 argument.forEach((item: ts.Node) => { 1486 doubleDollarCollection(item); 1487 }); 1488 } 1489 } else if (isPropertiesAddDoubleDollar(node)) { 1490 node.arguments.forEach((item: ts.Node) => { 1491 if (ts.isObjectLiteralExpression(item) && item.properties && item.properties.length) { 1492 item.properties.forEach((param: ts.Node) => { 1493 if (isObjectPram(param, parentComponentName)) { 1494 doubleDollarCollection(param.initializer); 1495 } 1496 }); 1497 } else if (ts.isPropertyAccessExpression(item) && (handleComponentDollarBlock(node as ts.CallExpression, parentComponentName) || 1498 STYLE_ADD_DOUBLE_DOLLAR.has(node.expression.getText()))) { 1499 doubleDollarCollection(item); 1500 } 1501 }); 1502 } 1503} 1504 1505function handleComponentDollarBlock(node: ts.CallExpression, parentComponentName: string): boolean { 1506 return ts.isCallExpression(node) && ts.isIdentifier(node.expression) && 1507 DOLLAR_BLOCK_INTERFACE.has(parentComponentName) && PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) && 1508 PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(node.expression.escapedText.toString()); 1509} 1510 1511function doubleDollarCollection(item: ts.Node): void { 1512 if (item.getText().startsWith($$)) { 1513 while (item.expression) { 1514 item = item.expression; 1515 } 1516 dollarCollection.add(item.getText()); 1517 } 1518} 1519 1520function isObjectPram(param: ts.Node, parentComponentName: string): boolean { 1521 return ts.isPropertyAssignment(param) && param.name && ts.isIdentifier(param.name) && 1522 param.initializer && PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) && 1523 PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(param.name.getText()); 1524} 1525 1526function isCanAddDoubleDollar(propertyName: string, parentComponentName: string): boolean { 1527 return PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) && 1528 PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(propertyName) || 1529 STYLE_ADD_DOUBLE_DOLLAR.has(propertyName); 1530} 1531 1532function processContent(source: string, id: string): string { 1533 if (fastBuildLogger) { 1534 source = visualTransform(source, id, fastBuildLogger); 1535 } 1536 source = preprocessExtend(source, extendCollection); 1537 source = preprocessNewExtend(source, extendCollection); 1538 return source; 1539} 1540 1541function judgeFileShouldResolved(file: string, shouldResolvedFiles: Set<string>): void { 1542 if (shouldResolvedFiles.has(file)) { 1543 return; 1544 } 1545 shouldResolvedFiles.add(file); 1546 if (cache && cache[file] && cache[file].parent) { 1547 cache[file].parent.forEach((item) => { 1548 judgeFileShouldResolved(item, shouldResolvedFiles); 1549 }); 1550 cache[file].parent = []; 1551 } 1552 if (cache && cache[file] && cache[file].children) { 1553 cache[file].children.forEach((item) => { 1554 judgeFileShouldResolved(item, shouldResolvedFiles); 1555 }); 1556 cache[file].children = []; 1557 } 1558} 1559 1560export function incrementWatchFile(watchModifiedFiles: string[], 1561 watchRemovedFiles: string[]): void { 1562 const changedFiles: string[] = [...watchModifiedFiles, ...watchRemovedFiles]; 1563 if (changedFiles.length) { 1564 shouldResolvedFiles.clear(); 1565 } 1566 changedFiles.forEach((file) => { 1567 judgeFileShouldResolved(file, shouldResolvedFiles); 1568 }); 1569} 1570 1571export function runArkTSLinter(errorCodeLogger?: Object | undefined): void { 1572 const originProgram: ts.BuilderProgram = globalProgram.builderProgram; 1573 1574 const timePrinterInstance = ts.ArkTSLinterTimePrinter.getInstance(); 1575 1576 const arkTSLinterDiagnostics = doArkTSLinter(getArkTSVersion(), 1577 getArkTSLinterMode(), 1578 originProgram, 1579 printArkTSLinterDiagnostic, 1580 !projectConfig.xtsMode, 1581 buildInfoWriteFile, 1582 errorCodeLogger); 1583 1584 if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) { 1585 arkTSLinterDiagnostics.forEach((diagnostic: ts.Diagnostic) => { 1586 updateErrorFileCache(diagnostic); 1587 }); 1588 timePrinterInstance.appendTime(ts.TimePhase.UPDATE_ERROR_FILE); 1589 } 1590 timePrinterInstance.printTimes(); 1591 ts.ArkTSLinterTimePrinter.destroyInstance(); 1592} 1593 1594function printArkTSLinterDiagnostic(diagnostic: ts.Diagnostic, errorCodeLogger?: Object | undefined): void { 1595 if (diagnostic.category === ts.DiagnosticCategory.Error && (isInOhModuleFile(diagnostic) || isEtsDeclFileInSdk(diagnostic))) { 1596 const originalCategory = diagnostic.category; 1597 diagnostic.category = ts.DiagnosticCategory.Warning; 1598 printDiagnostic(diagnostic); 1599 diagnostic.category = originalCategory; 1600 return; 1601 } 1602 printDiagnostic(diagnostic, ErrorCodeModule.LINTER, errorCodeLogger); 1603} 1604 1605function isEtsDeclFileInSdk(diagnostics: ts.Diagnostic): boolean { 1606 if (diagnostics.file?.fileName === undefined) { 1607 return false; 1608 } 1609 return isInSDK(diagnostics.file.fileName) && diagnostics.file.fileName.endsWith('.ets'); 1610} 1611 1612function isInOhModuleFile(diagnostics: ts.Diagnostic): boolean { 1613 return (diagnostics.file !== undefined) && 1614 ((diagnostics.file.fileName.indexOf('/oh_modules/') !== -1) || diagnostics.file.fileName.indexOf('\\oh_modules\\') !== -1); 1615} 1616 1617function isInSDK(fileName: string | undefined): boolean { 1618 if (projectConfig.etsLoaderPath === undefined || fileName === undefined) { 1619 return false; 1620 } 1621 const sdkPath = path.resolve(projectConfig.etsLoaderPath, '../../../'); 1622 return path.resolve(fileName).startsWith(sdkPath); 1623} 1624 1625export function getArkTSLinterMode(): ArkTSLinterMode { 1626 if (!partialUpdateConfig.executeArkTSLinter) { 1627 return ArkTSLinterMode.NOT_USE; 1628 } 1629 1630 if (!partialUpdateConfig.standardArkTSLinter) { 1631 return ArkTSLinterMode.COMPATIBLE_MODE; 1632 } 1633 1634 if (isStandardMode()) { 1635 return ArkTSLinterMode.STANDARD_MODE; 1636 } 1637 return ArkTSLinterMode.COMPATIBLE_MODE; 1638} 1639 1640export function isStandardMode(): boolean { 1641 const STANDARD_MODE_COMPATIBLE_SDK_VERSION = 10; 1642 if (projectConfig && 1643 projectConfig.compatibleSdkVersion && 1644 projectConfig.compatibleSdkVersion >= STANDARD_MODE_COMPATIBLE_SDK_VERSION) { 1645 return true; 1646 } 1647 return false; 1648} 1649 1650function getArkTSVersion(): ArkTSVersion { 1651 if (projectConfig.arkTSVersion === '1.0') { 1652 return ArkTSVersion.ArkTS_1_0; 1653 } else if (projectConfig.arkTSVersion === '1.1') { 1654 return ArkTSVersion.ArkTS_1_1; 1655 } else if (projectConfig.arkTSVersion !== undefined) { 1656 const arkTSVersionLogger = fastBuildLogger || logger; 1657 arkTSVersionLogger.warn('\u001b[33m' + 'ArkTS: Invalid ArkTS version\n'); 1658 } 1659 1660 if (partialUpdateConfig.arkTSVersion === '1.0') { 1661 return ArkTSVersion.ArkTS_1_0; 1662 } else if (partialUpdateConfig.arkTSVersion === '1.1') { 1663 return ArkTSVersion.ArkTS_1_1; 1664 } else if (partialUpdateConfig.arkTSVersion !== undefined) { 1665 const arkTSVersionLogger = fastBuildLogger || logger; 1666 arkTSVersionLogger.warn('\u001b[33m' + 'ArkTS: Invalid ArkTS version in metadata\n'); 1667 } 1668 1669 return ArkTSVersion.ArkTS_1_1; 1670} 1671 1672enum TargetESVersion { 1673 ES2017 = 'ES2017', 1674 ES2021 = 'ES2021', 1675} 1676 1677function getTargetESVersion(): TargetESVersion { 1678 const targetESVersion = projectConfig?.projectArkOption?.tscConfig?.targetESVersion; 1679 if (targetESVersion === 'ES2017') { 1680 return TargetESVersion.ES2017; 1681 } else if (targetESVersion === 'ES2021') { 1682 return TargetESVersion.ES2021; 1683 } else if (targetESVersion !== undefined) { 1684 const targetESVersionLogger = fastBuildLogger || logger; 1685 targetESVersionLogger.warn('\u001b[33m' + 'ArkTS: Invalid Target ES version\n'); 1686 } 1687 return TargetESVersion.ES2021; 1688} 1689 1690export function getMaxFlowDepth(): number { 1691 // The value of maxFlowDepth ranges from 2000 to 65535. 1692 let maxFlowDepth: number | undefined = projectConfig?.projectArkOption?.tscConfig?.maxFlowDepth; 1693 1694 if (maxFlowDepth === undefined) { 1695 maxFlowDepth = MAX_FLOW_DEPTH_DEFAULT_VALUE; 1696 } else if (maxFlowDepth < MAX_FLOW_DEPTH_DEFAULT_VALUE || maxFlowDepth > MAX_FLOW_DEPTH_MAXIMUM_VALUE) { 1697 const maxFlowDepthLogger = fastBuildLogger || logger; 1698 maxFlowDepth = MAX_FLOW_DEPTH_DEFAULT_VALUE; 1699 maxFlowDepthLogger.warn('\u001b[33m' + 'ArkTS: Invalid maxFlowDepth for control flow analysis.' + 1700 `The value of maxFlowDepth ranges from ${MAX_FLOW_DEPTH_DEFAULT_VALUE} to ${MAX_FLOW_DEPTH_MAXIMUM_VALUE}.\n` + 1701 'If the modification does not take effect, set maxFlowDepth to the default value.'); 1702 } 1703 return maxFlowDepth; 1704} 1705 1706interface TargetESVersionLib { 1707 ES2017: string[], 1708 ES2021: string[], 1709} 1710 1711const targetESVersionLib: TargetESVersionLib = { 1712 // When target is es2017, the lib is es2020. 1713 ES2017: ['ES2020'], 1714 ES2021: ['ES2021'], 1715}; 1716 1717function getTargetESVersionLib(): string[] { 1718 const targetESVersion = projectConfig?.projectArkOption?.tscConfig?.targetESVersion; 1719 if (targetESVersion === 'ES2017') { 1720 return targetESVersionLib.ES2017; 1721 } else if (targetESVersion === 'ES2021') { 1722 return targetESVersionLib.ES2021; 1723 } else if (targetESVersion !== undefined) { 1724 const targetESVersionLogger = fastBuildLogger || logger; 1725 targetESVersionLogger.warn('\u001b[33m' + 'ArkTS: Invalid Target ES version\n'); 1726 } 1727 return targetESVersionLib.ES2021; 1728} 1729 1730function initEtsStandaloneCheckerConfig(logger, config): void { 1731 fastBuildLogger = logger; 1732 if (config.packageManagerType === 'ohpm') { 1733 config.packageDir = 'oh_modules'; 1734 config.packageJson = 'oh-package.json5'; 1735 } else { 1736 config.packageDir = 'node_modules'; 1737 config.packageJson = 'package.json'; 1738 } 1739 if (config.aceModuleJsonPath && fs.existsSync(config.aceModuleJsonPath)) { 1740 process.env.compileMode = 'moduleJson'; 1741 } 1742 Object.assign(projectConfig, config); 1743} 1744 1745function resetEtsStandaloneCheckerConfig(beforeInitFastBuildLogger, beforeInitCompileMode: string): void { 1746 resetProjectConfig(); 1747 resetEtsCheck(); 1748 fastBuildLogger = beforeInitFastBuildLogger; 1749 process.env.compileMode = beforeInitCompileMode; 1750} 1751 1752export function etsStandaloneChecker(entryObj, logger, projectConfig): void { 1753 const beforeInitFastBuildLogger = fastBuildLogger; 1754 const beforeInitCompileMode = process.env.compileMode; 1755 initEtsStandaloneCheckerConfig(logger, projectConfig); 1756 const rootFileNames: string[] = []; 1757 const resolveModulePaths: string[] = []; 1758 Object.values(entryObj).forEach((fileName: string) => { 1759 rootFileNames.push(path.resolve(fileName)); 1760 }); 1761 if (projectConfig.resolveModulePaths && Array.isArray(projectConfig.resolveModulePaths)) { 1762 resolveModulePaths.push(...projectConfig.resolveModulePaths); 1763 } 1764 const filterFiles: string[] = filterInput(rootFileNames); 1765 languageService = createLanguageService(filterFiles, resolveModulePaths); 1766 const timePrinterInstance = ts.ArkTSLinterTimePrinter.getInstance(); 1767 timePrinterInstance.setArkTSTimePrintSwitch(false); 1768 timePrinterInstance.appendTime(ts.TimePhase.START); 1769 globalProgram.builderProgram = languageService.getBuilderProgram(/*withLinterProgram*/ true); 1770 globalProgram.program = globalProgram.builderProgram.getProgram(); 1771 props = languageService.getProps(); 1772 timePrinterInstance.appendTime(ts.TimePhase.GET_PROGRAM); 1773 collectFileToIgnoreDiagnostics(filterFiles); 1774 runArkTSLinter(); 1775 const allDiagnostics: ts.Diagnostic[] = globalProgram.builderProgram 1776 .getSyntacticDiagnostics() 1777 .concat(globalProgram.builderProgram.getSemanticDiagnostics()); 1778 globalProgram.builderProgram.emitBuildInfo(buildInfoWriteFile); 1779 1780 allDiagnostics.forEach((diagnostic: ts.Diagnostic) => { 1781 printDiagnostic(diagnostic); 1782 }); 1783 resetEtsStandaloneCheckerConfig(beforeInitFastBuildLogger, beforeInitCompileMode); 1784} 1785 1786export function resetEtsCheckTypeScript(): void { 1787 if (globalProgram.program) { 1788 globalProgram.program.releaseTypeChecker(); 1789 } else if (languageService) { 1790 languageService.getProgram().releaseTypeChecker(); 1791 } 1792 resetGlobalProgram(); 1793 languageService = null; 1794} 1795 1796export function resetEtsCheck(): void { 1797 cache = {}; 1798 props = []; 1799 needReCheckForChangedDepUsers = false; 1800 resetEtsCheckTypeScript(); 1801 allResolvedModules.clear(); 1802 checkerResult.count = 0; 1803 warnCheckerResult.count = 0; 1804 resolvedModulesCache.clear(); 1805 dollarCollection.clear(); 1806 extendCollection.clear(); 1807 allSourceFilePaths.clear(); 1808 allModuleIds.clear(); 1809 filesBuildInfo.clear(); 1810 fileExistsCache.clear(); 1811 dirExistsCache.clear(); 1812 targetESVersionChanged = false; 1813 fileToIgnoreDiagnostics = undefined; 1814} 1815