1/* 2 * Copyright (c) 2024 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 * as ts from 'typescript'; 17 18export class IncrementalLinterState { 19 // @ts-expect-error : should expect method 20 private readonly changedFiles: ts.Set<ts.Path> = new ts.Set<ts.Path>(); 21 // @ts-expect-error : should expect method 22 private readonly programState: ts.ReusableBuilderProgramState; 23 private readonly oldDiagnostics: 24 | undefined 25 // @ts-expect-error : should expect method 26 | ts.ESMap<ts.Path, readonly ts.ReusableDiagnostic[] | readonly ts.Diagnostic[]>; 27 28 constructor(builderProgram: ts.BuilderProgram, arkTSVersion?: string, compilerOptions?: ts.CompilerOptions) { 29 // @ts-expect-error : should expect method 30 this.programState = builderProgram.getState(); 31 this.oldDiagnostics = this.programState.arktsLinterDiagnosticsPerFile; 32 this.programState.arktsLinterDiagnosticsPerFile = new Map(); 33 this.changedFiles = IncrementalLinterState.collectChangedFilesFromProgramState( 34 this.programState, 35 arkTSVersion, 36 compilerOptions 37 ); 38 } 39 40 isFileChanged(srcFile: ts.SourceFile): boolean { 41 // @ts-expect-error : should expect method 42 return this.changedFiles.has(srcFile.resolvedPath); 43 } 44 45 getOldDiagnostics(srcFile: ts.SourceFile): ts.Diagnostic[] { 46 // @ts-expect-error : should expect method 47 return (this.oldDiagnostics?.get(srcFile.resolvedPath) as ts.Diagnostic[]) ?? []; 48 } 49 50 updateDiagnostics(srcFile: ts.SourceFile, newDiagnostics: ts.Diagnostic[]): void { 51 // @ts-expect-error : should expect method 52 this.programState.arktsLinterDiagnosticsPerFile?.set(srcFile.resolvedPath, newDiagnostics); 53 } 54 55 updateProgramStateArkTSVersion(arkTSVersion?: string): void { 56 this.programState.arkTSVersion = arkTSVersion; 57 } 58 59 static emitBuildInfo(buildInfoWriteFile: ts.WriteFileCallback, builderProgram: ts.BuilderProgram): void { 60 // @ts-expect-error : should expect method 61 builderProgram.emitBuildInfo(buildInfoWriteFile); 62 } 63 64 private static collectChangedFilesFromProgramState( 65 // @ts-expect-error : should expect method 66 state: ts.ReusableBuilderProgramState, 67 arkTSVersion?: string, 68 compilerOptions?: ts.CompilerOptions 69 ): ts.Set<ts.Path> { 70 if ( 71 state.arkTSVersion !== arkTSVersion || 72 state.compatibleSdkVersion !== compilerOptions?.compatibleSdkVersion || 73 state.compatibleSdkVersionStage !== compilerOptions?.compatibleSdkVersionStage 74 ) { 75 // @ts-expect-error : should expect method 76 return new ts.Set<ts.Path>(IncrementalLinterState.arrayFrom(state.fileInfos.keys())); 77 } 78 79 // @ts-expect-error : should expect method 80 const changedFiles = new ts.Set<ts.Path>(state.changedFilesSet); 81 82 /* 83 * If any source file that affects global scope has been changed, 84 * then process all files in project. 85 */ 86 for (const changedFile of IncrementalLinterState.arrayFrom(changedFiles.keys())) { 87 const fileInfo = state.fileInfos.get(changedFile); 88 if (fileInfo?.affectsGlobalScope) { 89 // @ts-expect-error : should expect method 90 return new ts.Set<ts.Path>(IncrementalLinterState.arrayFrom(state.fileInfos.keys())); 91 } 92 } 93 94 if (!state.referencedMap) { 95 return changedFiles; 96 } 97 98 // @ts-expect-error : should expect method 99 const seenPaths = new ts.Set<ts.Path>(); 100 const queue = IncrementalLinterState.arrayFrom(changedFiles.keys()); 101 while (queue.length) { 102 const path = queue.pop()!; 103 if (!seenPaths.has(path)) { 104 seenPaths.add(path); 105 106 // @ts-expect-error : should expect method 107 queue.push(...ts.BuilderState.getReferencedByPaths(state, path)); 108 } 109 } 110 return seenPaths; 111 } 112 113 private static arrayFrom(iterator: Iterator<ts.Path>): ts.Path[] { 114 const result: ts.Path[] = []; 115 for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) { 116 result.push(iterResult.value); 117 } 118 return result; 119 } 120} 121