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

ota-meshi / jsonc-eslint-parser / 9441882216

10 Jun 2024 03:08AM UTC coverage: 88.791%. Remained the same
9441882216

Pull #167

github

web-flow
Merge 98884f0b5 into 1fe0d2e4e
Pull Request #167: chore(deps): update dependency typescript to ~5.4.0

308 of 365 branches covered (84.38%)

Branch coverage included in aggregate %.

603 of 661 relevant lines covered (91.23%)

64.16 hits per line

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

88.06
/src/parser/extend-parser.ts
1
import type { TokenStore } from "./token-store";
2
import { validateNode } from "./validate";
1✔
3
import type { Parser, Options, Node } from "acorn";
4
import type { Comment, Node as ESTreeNode } from "estree";
5
import { getAcorn } from "./modules/acorn";
1✔
6
import {
1✔
7
  ParseError,
8
  throwUnexpectedCommentError,
9
  throwUnexpectedTokenError,
10
} from "./errors";
11
import { TokenConvertor } from "./convert";
1✔
12
import type { JSONSyntaxContext } from "./syntax-context";
13

14
let parserCache: typeof Parser | undefined;
15

16
const PRIVATE = Symbol("ExtendParser#private");
1✔
17
const PRIVATE_PROCESS_NODE = Symbol("ExtendParser#processNode");
1✔
18

19
/** Get extend parser */
20
export function getParser(): typeof Parser {
1✔
21
  if (parserCache) {
178✔
22
    return parserCache;
177✔
23
  }
24

25
  parserCache = class ExtendParser
1✔
26
    // @ts-expect-error -- Ignore
27
    extends getAcorn().Parser
28
  {
29
    private [PRIVATE]: {
30
      code: string;
31
      ctx: JSONSyntaxContext;
32
      tokenStore: TokenStore;
33
      comments: Comment[];
34
      nodes: Node[];
35
    };
36

37
    public constructor(
38
      options: Options & {
39
        ctx: JSONSyntaxContext;
40
        tokenStore: TokenStore;
41
        comments: Comment[];
42
        nodes: Node[];
43
      },
44
      code: string,
45
      pos: number,
46
    ) {
47
      super(
178✔
48
        ((): Options => {
49
          const tokenConvertor = new TokenConvertor(options.ctx, code);
178✔
50

51
          const onToken: Options["onToken"] =
52
            options.onToken ||
178✔
53
            ((token) => {
54
              const t = tokenConvertor.convertToken(token as never);
845✔
55
              if (t) {
836✔
56
                this[PRIVATE].tokenStore.add(t);
812✔
57
              }
58
            });
59
          return {
178✔
60
            // do not use spread, because we don't want to pass any unknown options to acorn
61
            ecmaVersion: options.ecmaVersion,
62
            sourceType: options.sourceType,
63
            ranges: true,
64
            locations: true,
65
            allowReserved: true,
66

67
            // Collect tokens
68
            onToken,
69

70
            // Collect comments
71
            onComment: (block, text, start, end, startLoc, endLoc) => {
72
              const comment: Comment = {
17✔
73
                type: block ? "Block" : "Line",
17✔
74
                value: text,
75
                range: [start, end],
76
                loc: {
77
                  start: startLoc!,
78
                  end: endLoc!,
79
                },
80
              };
81
              if (!this[PRIVATE].ctx.comments) {
17✔
82
                throw throwUnexpectedCommentError(comment);
2✔
83
              }
84
              this[PRIVATE].comments.push(comment);
15✔
85
            },
86
          };
87
        })(),
88
        code,
89
        pos,
90
      );
91
      this[PRIVATE] = {
178✔
92
        code,
93
        ctx: options.ctx,
94
        tokenStore: options.tokenStore,
95
        comments: options.comments,
96
        nodes: options.nodes,
97
      };
98
    }
99

100
    public finishNode(
101
      ...args: Parameters<
102
        // @ts-expect-error -- Ignore
103
        Parser["finishNode"]
104
      >
105
    ) {
106
      const result: Node =
107
        // @ts-expect-error -- Ignore
108
        super.finishNode(...args);
561✔
109
      return this[PRIVATE_PROCESS_NODE](result);
561✔
110
    }
111

112
    public finishNodeAt(
113
      ...args: Parameters<
114
        // @ts-expect-error -- Ignore
115
        Parser["finishNodeAt"]
116
      >
117
    ) {
118
      const result: Node =
119
        // @ts-expect-error -- Ignore
120
        super.finishNodeAt(...args);
×
121
      return this[PRIVATE_PROCESS_NODE](result);
×
122
    }
123

124
    private [PRIVATE_PROCESS_NODE](node: Node) {
125
      const { tokenStore, ctx, nodes } = this[PRIVATE];
561✔
126
      validateNode(node as ESTreeNode, tokenStore, ctx);
561✔
127
      nodes.push(node);
501✔
128
      return node;
501✔
129
    }
130

131
    public raise(pos: number, message: string) {
132
      const loc = getAcorn().getLineInfo(this[PRIVATE].code, pos);
7✔
133
      const err = new ParseError(
7✔
134
        message,
135
        pos,
136
        loc.line,
137
        loc.column + 1, // acorn uses 0-based columns
138
      );
139
      throw err;
7✔
140
    }
141

142
    public raiseRecoverable(pos: number, message: string) {
143
      this.raise(pos, message);
×
144
    }
145

146
    public unexpected(pos?: number) {
147
      if (pos != null) {
7!
148
        this.raise(pos, "Unexpected token.");
×
149
        return;
×
150
      }
151
      // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
152
      const start: number = (this as any).start;
7✔
153
      // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
154
      const end: number = (this as any).end;
7✔
155

156
      const token = this[PRIVATE].code.slice(start, end);
7✔
157
      if (token) {
7✔
158
        const message = `Unexpected token '${token}'.`;
4✔
159

160
        this.raise(start, message);
4✔
161
      } else {
162
        if (!this[PRIVATE].nodes.length) {
3✔
163
          this.raise(0, "Expected to be an expression, but got empty.");
1✔
164
        }
165
        if (this[PRIVATE].tokenStore.tokens.length) {
2✔
166
          const last =
167
            this[PRIVATE].tokenStore.tokens[
2✔
168
              this[PRIVATE].tokenStore.tokens.length - 1
169
            ];
170
          this.raise(last.range[0], `Unexpected token '${last.value}'.`);
2✔
171
        }
172
        this.raise(start, "Unexpected token.");
×
173
      }
174
    }
175
  };
176

177
  return parserCache;
1✔
178
}
179

180
/** Get extend parser */
181
export function getAnyTokenErrorParser(): typeof Parser {
1✔
182
  const parser = class ExtendParser
2✔
183
    // @ts-expect-error -- Ignore
184
    extends getParser()
185
  {
186
    public constructor(options: Options, code: string, pos: number) {
187
      super(
2✔
188
        {
189
          ...options,
190
          onToken: (token) => {
191
            return throwUnexpectedTokenError(
×
192
              code.slice(...token.range!),
193
              token,
194
            );
195
          },
196
        },
197
        code,
198
        pos,
199
      );
200
    }
201
  };
202

203
  return parser;
2✔
204
}
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