• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ota-meshi / svelte-eslint-parser / 4581268530

01 Apr 2023 04:18AM UTC coverage: 90.693%. Remained the same
4581268530

push

github

GitHub
chore: release svelte-eslint-parser (#308)

880 of 1041 branches covered (84.53%)

Branch coverage included in aggregate %.

2053 of 2193 relevant lines covered (93.62%)

27060.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.9
/src/parser/typescript/restore.ts
1
import type { TSESTree } from "@typescript-eslint/types";
435✔
2
import { traverseNodes } from "../../traverse";
1✔
3
import { LinesAndColumns } from "../../context";
1✔
4
import type { TSESParseForESLintResult } from "./types";
5

6
/**
7
 * A function that restores the statement.
8
 * @param node The node to restore.
9
 * @param result The result of parsing.
10
 * @returns
11
 *   If `false`, it indicates that the specified node was not processed.
12
 *
13
 *   If `true`, it indicates that the specified node was processed for processing.
14
 *   This process will no longer be called.
15
 */
16
type RestoreStatementProcess = (
17
  node: TSESTree.Statement,
18
  result: TSESParseForESLintResult
19
) => boolean;
20

21
export class RestoreContext {
22
  private readonly originalLocs: LinesAndColumns;
23

24
  private readonly offsets: { original: number; dist: number }[] = [];
432✔
25

26
  private readonly virtualFragments: { start: number; end: number }[] = [];
432✔
27

28
  private readonly restoreStatementProcesses: RestoreStatementProcess[] = [];
432✔
29

30
  public constructor(code: string) {
31
    this.originalLocs = new LinesAndColumns(code);
432✔
32
  }
33

34
  public addRestoreStatementProcess(process: RestoreStatementProcess): void {
35
    this.restoreStatementProcesses.push(process);
1,007✔
36
  }
37

38
  public addOffset(offset: { original: number; dist: number }): void {
39
    this.offsets.push(offset);
2,340✔
40
  }
41

42
  public addVirtualFragmentRange(start: number, end: number): void {
43
    const peek = this.virtualFragments[this.virtualFragments.length - 1];
2,357✔
44
    if (peek && peek.end === start) {
2,357✔
45
      peek.end = end;
373✔
46
      return;
373✔
47
    }
48
    this.virtualFragments.push({ start, end });
1,984✔
49
  }
50

51
  /**
52
   * Restore AST nodes
53
   */
54
  public restore(result: TSESParseForESLintResult): void {
55
    remapLocations(result, {
432✔
56
      remapLocation: (n) => this.remapLocation(n),
98,257✔
57
      removeToken: (token) =>
58
        this.virtualFragments.some(
63,104✔
59
          (f) => f.start <= token.range[0] && token.range[1] <= f.end
208,225✔
60
        ),
61
    });
62

63
    restoreStatements(result, this.restoreStatementProcesses);
432✔
64

65
    // Adjust program node location
66
    const firstOffset = Math.min(
432✔
67
      ...[result.ast.body[0], result.ast.tokens?.[0], result.ast.comments?.[0]]
68
        .filter((t): t is NonNullable<typeof t> => Boolean(t))
1,296✔
69
        .map((t) => t.range[0])
880✔
70
    );
71
    if (firstOffset < result.ast.range[0]) {
432✔
72
      result.ast.range[0] = firstOffset;
1✔
73
      result.ast.loc.start = this.originalLocs.getLocFromIndex(firstOffset);
1✔
74
    }
75
  }
76

77
  private remapLocation(node: TSESTree.Node | TSESTree.Token): void {
78
    let [start, end] = node.range;
98,257✔
79
    const startFragment = this.virtualFragments.find(
98,257✔
80
      (f) => f.start <= start && start < f.end
314,725✔
81
    );
82
    if (startFragment) {
98,257✔
83
      start = startFragment.end;
10,292✔
84
    }
85
    const endFragment = this.virtualFragments.find(
98,257✔
86
      (f) => f.start < end && end <= f.end
315,879✔
87
    );
88
    if (endFragment) {
98,257✔
89
      end = endFragment.start;
10,901✔
90
      if (startFragment === endFragment) {
10,901✔
91
        start = startFragment.start;
7,839✔
92
      }
93
    }
94

95
    if (end < start) {
98,257!
96
      const w = start;
×
97
      start = end;
×
98
      end = w;
×
99
    }
100

101
    const locs = this.originalLocs.getLocations(
98,257✔
102
      ...this.getRemapRange(start, end)
103
    );
104

105
    node.loc = locs.loc;
98,257✔
106
    node.range = locs.range;
98,257✔
107

108
    if ((node as any).start != null) {
98,257!
109
      delete (node as any).start;
×
110
    }
111
    if ((node as any).end != null) {
98,257!
112
      delete (node as any).end;
×
113
    }
114
  }
115

116
  private getRemapRange(start: number, end: number): TSESTree.Range {
117
    if (!this.offsets.length) {
98,257!
118
      return [start, end];
×
119
    }
120
    let lastStart = this.offsets[0];
98,257✔
121
    let lastEnd = this.offsets[0];
98,257✔
122
    for (const offset of this.offsets) {
98,257✔
123
      if (offset.dist <= start) {
365,611✔
124
        lastStart = offset;
331,278✔
125
      }
126
      if (offset.dist < end) {
365,611✔
127
        lastEnd = offset;
333,579✔
128
      } else {
129
        break;
32,032✔
130
      }
131
    }
132

133
    const remapStart = lastStart.original + (start - lastStart.dist);
98,257✔
134
    const remapEnd = lastEnd.original + (end - lastEnd.dist);
98,257✔
135
    return [remapStart, remapEnd];
98,257✔
136
  }
137
}
138

139
/** Restore locations */
140
function remapLocations(
141
  result: TSESParseForESLintResult,
142
  {
143
    remapLocation,
144
    removeToken,
145
  }: {
146
    remapLocation: (node: TSESTree.Node | TSESTree.Token) => void;
147
    removeToken: (node: TSESTree.Token) => boolean;
148
  }
149
) {
150
  const traversed = new Map<TSESTree.Node, TSESTree.Node | null>();
432✔
151
  // remap locations
152
  traverseNodes(result.ast, {
432✔
153
    visitorKeys: result.visitorKeys,
154
    enterNode: (node, p) => {
155
      if (!traversed.has(node)) {
49,364!
156
        traversed.set(node, p);
49,364✔
157

158
        remapLocation(node);
49,364✔
159
      }
160
    },
161
    leaveNode: (_node) => {
162
      // noop
163
    },
164
  });
165
  const tokens: TSESTree.Token[] = [];
432✔
166
  for (const token of result.ast.tokens || []) {
432!
167
    if (removeToken(token)) {
63,104✔
168
      continue;
14,227✔
169
    }
170
    remapLocation(token);
48,877✔
171
    tokens.push(token);
48,877✔
172
  }
173
  result.ast.tokens = tokens;
432✔
174
  for (const token of result.ast.comments || []) {
432!
175
    remapLocation(token);
16✔
176
  }
177
}
178

179
/** Restore statement nodes */
180
function restoreStatements(
181
  result: TSESParseForESLintResult,
182
  restoreStatementProcesses: RestoreStatementProcess[]
183
) {
184
  const restoreStatementProcessesSet = new Set(restoreStatementProcesses);
432✔
185
  for (const node of [...result.ast.body]) {
432✔
186
    if (!restoreStatementProcessesSet.size) {
2,383!
187
      break;
×
188
    }
189
    for (const proc of restoreStatementProcessesSet) {
2,383✔
190
      if (proc(node, result)) {
3,251✔
191
        restoreStatementProcessesSet.delete(proc);
1,007✔
192
        break;
1,007✔
193
      }
194
    }
195
  }
196
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc