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