• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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