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

future-architect / eslint-plugin-vue-scoped-css / 22541414030

01 Mar 2026 10:23AM UTC coverage: 99.274% (+6.9%) from 92.36%
22541414030

push

github

web-flow
Change to ESM-only package, bundle with tsdown (#454)

* Initial plan

* Initial plan: Convert to ESM-only package with tsdown

Co-authored-by: ota-meshi <16508807+ota-meshi@users.noreply.github.com>

* feat: Change to ESM-only package using tsdown

Co-authored-by: ota-meshi <16508807+ota-meshi@users.noreply.github.com>

* fix: address PR review comments - remove plugin.ts, add typecheck, fix CI workflows

Co-authored-by: ota-meshi <16508807+ota-meshi@users.noreply.github.com>

* fix: use @oxc-node/core, c8 coverage, typecheck in lint; fix class field order for OXC compatibility

Co-authored-by: ota-meshi <16508807+ota-meshi@users.noreply.github.com>

* fix: rename stylelint.config.js to stylelint.config.cjs for ESM package compatibility

Co-authored-by: ota-meshi <16508807+ota-meshi@users.noreply.github.com>

* fix: add 'use strict' to stylelint.config.cjs and add --reporter=lcov to test:coverage

Co-authored-by: ota-meshi <16508807+ota-meshi@users.noreply.github.com>

* fix: use mocha.js path, drop dist/index.d.ts from files, improve test assertion message

Co-authored-by: ota-meshi <16508807+ota-meshi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ota-meshi <16508807+ota-meshi@users.noreply.github.com>

484 of 544 branches covered (88.97%)

Branch coverage included in aggregate %.

61 of 61 new or added lines in 19 files covered. (100.0%)

17 existing lines in 11 files now uncovered.

10595 of 10616 relevant lines covered (99.8%)

33.04 hits per line

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

97.8
/lib/styles/parser/css-parser.ts
1
import * as postcss from "postcss";
1✔
2
import postcssSafeParser from "postcss-safe-parser";
1✔
3
import { CSSSelectorParser } from "./selector/css-selector-parser";
1✔
4
import type { VCSSCommentNode, VCSSNode, VCSSContainerNode } from "../ast";
1✔
5
import {
1✔
6
  VCSSStyleSheet,
1✔
7
  VCSSStyleRule,
1✔
8
  VCSSDeclarationProperty,
1✔
9
  VCSSAtRule,
1✔
10
  VCSSComment,
1✔
11
  VCSSParsingError,
1✔
12
  VCSSUnknown,
1✔
13
} from "../ast";
1✔
14
import type {
1✔
15
  SourceCode,
1✔
16
  LineAndColumnData,
1✔
17
  PostCSSNode,
1✔
18
  PostCSSRoot,
1✔
19
  SourceLocation,
1✔
20
  PostCSSLoc,
1✔
21
  PostCSSRule,
1✔
22
  PostCSSAtRule,
1✔
23
  PostCSSDeclaration,
1✔
24
  PostCSSComment,
1✔
25
} from "../../types";
1✔
26
import { isPostCSSContainer } from "./utils";
1✔
27
import { isVCSSContainerNode } from "../utils/css-nodes";
1✔
28
import { isDefined } from "../../utils/utils";
1✔
29

1✔
30
/**
1✔
31
 * CSS Parser
1✔
32
 */
1✔
33
export class CSSParser {
1✔
34
  protected readonly sourceCode: SourceCode;
1✔
35

1✔
36
  protected commentContainer: VCSSCommentNode[];
1✔
37

1✔
38
  private _selectorParser: CSSSelectorParser | null = null;
1✔
39

1✔
40
  private readonly lang: string;
1✔
41

1✔
42
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- errors
1✔
43
  private anyErrors: any[] = [];
1✔
44

1✔
45
  /**
1✔
46
   * constructor.
1✔
47
   * @param {SourceCode} sourceCode the SourceCode object that you can use to work with the source that was passed to ESLint.
1✔
48
   */
1✔
49
  public constructor(sourceCode: SourceCode, lang: string) {
1✔
50
    this.sourceCode = sourceCode;
1✔
51
    this.commentContainer = [];
1✔
52
    this.lang = lang;
1✔
53
  }
1✔
54

1✔
55
  /**
1✔
56
   * Parse the CSS.
1✔
57
   * @param {string} css the CSS to parse
1✔
58
   * @param {LineAndColumnData} offsetLocation start location of css.
1✔
59
   * @return {VCSSStyleSheet} parsed result
1✔
60
   */
1✔
61
  public parse(css: string, offsetLocation: LineAndColumnData): VCSSStyleSheet {
1✔
62
    const { sourceCode } = this;
1✔
63

1✔
64
    this.commentContainer = [];
1✔
65
    this._selectorParser = this.createSelectorParser();
1✔
66
    this.anyErrors = [];
1✔
67

1✔
68
    try {
1✔
69
      const postcssRoot = this.parseInternal(css) as PostCSSRoot;
1✔
70

1✔
71
      const rootNode = this._postcssNodeToASTNode(offsetLocation, postcssRoot);
1✔
72
      rootNode.comments = this.commentContainer;
1✔
73
      rootNode.errors.push(
1✔
74
        ...this.collectErrors(this.anyErrors, offsetLocation),
1✔
75
      );
1✔
76

1✔
77
      return rootNode;
1✔
78
    } catch (e) {
1✔
79
      const startIndex = sourceCode.getIndexFromLoc(offsetLocation);
1✔
80
      const endIndex = startIndex + css.length;
1✔
81
      const styleLoc = {
1✔
82
        start: offsetLocation,
1✔
83
        end: sourceCode.getLocFromIndex(endIndex),
1✔
84
      };
1✔
85
      return new VCSSStyleSheet(null as never, styleLoc, startIndex, endIndex, {
1✔
86
        errors: this.collectErrors([...this.anyErrors, e], offsetLocation),
1✔
87
        lang: this.lang,
1✔
88
      });
1✔
89
    }
1✔
90
  }
1✔
91

1✔
92
  private addError(error: Error) {
1✔
93
    this.anyErrors.push(error);
1✔
94
  }
1✔
95

1✔
96
  private collectErrors(
1✔
97
    // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
1✔
98
    errors: any[],
1✔
99
    offsetLocation: LineAndColumnData,
1✔
100
  ): VCSSParsingError[] {
1✔
101
    const errorNodes = [];
1✔
102
    const duplicate = new Set<string>();
1✔
103
    for (const error of errors) {
1✔
104
      const errorLoc =
1✔
105
        error.line != null && error.column != null
1✔
106
          ? getESLintLineAndColumnFromPostCSSPosition(offsetLocation, error)
1✔
UNCOV
107
          : offsetLocation;
×
108
      const message = error.reason || error.message;
1!
109

1✔
110
      const key = `[${errorLoc.line}:${errorLoc.column}]: ${message}`;
1✔
111
      if (duplicate.has(key)) {
1✔
112
        continue;
1✔
113
      }
1✔
114
      duplicate.add(key);
1✔
115
      const errorIndex = this.sourceCode.getIndexFromLoc(errorLoc);
1✔
116
      errorNodes.push(
1✔
117
        new VCSSParsingError(
1✔
118
          null as never,
1✔
119
          {
1✔
120
            start: errorLoc,
1✔
121
            end: errorLoc,
1✔
122
          },
1✔
123
          errorIndex,
1✔
124
          errorIndex,
1✔
125
          {
1✔
126
            lang: this.lang,
1✔
127
            message,
1✔
128
          },
1✔
129
        ),
1✔
130
      );
1✔
131
    }
1✔
132
    return errorNodes;
1✔
133
  }
1✔
134

1✔
135
  protected get selectorParser(): CSSSelectorParser {
1✔
136
    return (
1✔
137
      this._selectorParser ||
1!
UNCOV
138
      (this._selectorParser = this.createSelectorParser())
×
139
    );
1✔
140
  }
1✔
141

1✔
142
  /**
1✔
143
   * Convert PostCSS node to node that can be handled by ESLint.
1✔
144
   * @param {LineAndColumnData} offsetLocation start location of css.
1✔
145
   * @param {object} node the PostCSS node to convert
1✔
146
   * @param {Node?} parent parent node
1✔
147
   * @return {Node|null} converted node.
1✔
148
   */
1✔
149
  private _postcssNodeToASTNode(
1✔
150
    offsetLocation: LineAndColumnData,
1✔
151
    node: PostCSSRoot,
1✔
152
  ): VCSSStyleSheet;
1✔
153

1✔
154
  private _postcssNodeToASTNode(
1✔
155
    offsetLocation: LineAndColumnData,
1✔
156
    node: PostCSSNode,
1✔
157
    parent: VCSSContainerNode,
1✔
158
  ): VCSSNode | null;
1✔
159

1✔
160
  private _postcssNodeToASTNode(
1✔
161
    offsetLocation: LineAndColumnData,
1✔
162
    node: PostCSSNode,
1✔
163
    parent?: VCSSContainerNode,
1✔
164
  ): VCSSNode | null {
1✔
165
    const { sourceCode } = this;
1✔
166
    const startLoc = getESLintLineAndColumnFromPostCSSNode(
1✔
167
      offsetLocation,
1✔
168
      node,
1✔
169
      "start",
1✔
170
    ) || { line: 0, column: 1 };
1!
171
    const start = sourceCode.getIndexFromLoc(startLoc);
1✔
172
    const endLoc =
1✔
173
      getESLintLineAndColumnFromPostCSSNode(offsetLocation, node, "end") ||
1✔
174
      // for node type: `root`
57✔
175
      sourceCode.getLocFromIndex(
57✔
176
        sourceCode.getIndexFromLoc(offsetLocation) +
57✔
177
          (node as PostCSSRoot).source.input.css.length,
57✔
178
      );
1✔
179
    const end = sourceCode.getIndexFromLoc(endLoc);
1✔
180
    const loc: SourceLocation = {
1✔
181
      start: startLoc,
1✔
182
      end: endLoc,
1✔
183
    };
1✔
184

1✔
185
    const astNode = this[typeToConvertMethodName(node.type)](
1✔
186
      node as never,
1✔
187
      loc,
1✔
188
      start,
1✔
189
      end,
1✔
190
      parent as never,
1✔
191
    );
1✔
192

1✔
193
    if (astNode == null) {
1✔
194
      return null;
1✔
195
    }
1✔
196
    if (isPostCSSContainer(node) && isVCSSContainerNode(astNode)) {
1✔
197
      astNode.nodes = node.nodes
1✔
198
        .map((n) => this._postcssNodeToASTNode(offsetLocation, n, astNode))
1✔
199
        .filter(isDefined);
1✔
200
    }
1✔
201
    return astNode;
1✔
202
  }
1✔
203

1✔
204
  protected parseInternal(css: string): postcss.Root {
1✔
205
    try {
1✔
206
      return postcss.parse(css);
1✔
207
    } catch (e: unknown) {
1✔
208
      this.addError(e as Error);
1✔
209
      return postcssSafeParser(css);
1✔
210
    }
1✔
211
  }
1✔
212

1✔
213
  protected createSelectorParser(): CSSSelectorParser {
1✔
214
    return new CSSSelectorParser(this.sourceCode, this.commentContainer);
1✔
215
  }
1✔
216

1✔
217
  /**
1✔
218
   * Convert root Node
1✔
219
   * @param  {object} node  The node.
1✔
220
   * @param  {SourceLocation} loc  The location.
1✔
221
   * @param  {number} start  The index of start.
1✔
222
   * @param  {number} end  The index of end.
1✔
223
   * @param  {Node} parent  The parent node.
1✔
224
   * @returns {VCSSStyleSheet}
1✔
225
   */
1✔
226
  protected convertRootNode(
1✔
227
    node: PostCSSRoot,
1✔
228
    loc: SourceLocation,
1✔
229
    start: number,
1✔
230
    end: number,
1✔
231
  ): VCSSNode | null {
1✔
232
    return new VCSSStyleSheet(node, loc, start, end, { lang: this.lang });
1✔
233
  }
1✔
234

1✔
235
  /**
1✔
236
   * Convert rule Node
1✔
237
   * @param  {object} node  The node.
1✔
238
   * @param  {SourceLocation} loc  The location.
1✔
239
   * @param  {number} start  The index of start.
1✔
240
   * @param  {number} end  The index of end.
1✔
241
   * @param  {Node} parent  The parent node.
1✔
242
   * @returns {VCSSStyleRule}
1✔
243
   */
1✔
244
  protected convertRuleNode(
1✔
245
    node: PostCSSRule,
1✔
246
    loc: SourceLocation,
1✔
247
    start: number,
1✔
248
    end: number,
1✔
249
    parent: VCSSContainerNode,
1✔
250
  ): VCSSNode | null {
1✔
251
    const astNode = new VCSSStyleRule(node, loc, start, end, {
1✔
252
      parent,
1✔
253
      rawSelectorText: this.getRaw(node, "selector")?.raw ?? null,
1✔
254
    });
1✔
255
    astNode.selectors = this.selectorParser.parse(
1✔
256
      astNode.rawSelectorText,
1✔
257
      astNode.loc.start,
1✔
258
      astNode,
1✔
259
    );
1✔
260

1✔
261
    if (this.getRaw(node, "between")?.trim()) {
1✔
262
      this.parseRuleRawsBetween(node, astNode);
1✔
263
    }
1✔
264

1✔
265
    return astNode;
1✔
266
  }
1✔
267

1✔
268
  protected parseRuleRawsBetween(node: PostCSSRule, astNode: VCSSNode): void {
1✔
269
    const between = this.getRaw(node, "between");
1✔
270
    const rawSelector = this.getRaw(node, "selector")?.raw ?? node.selector;
1✔
271
    const betweenStart = astNode.range[0] + rawSelector.length;
1✔
272
    const postcssRoot = this.parseInternal(between || "") as PostCSSRoot;
1!
273

1✔
274
    this._postcssNodeToASTNode(
1✔
275
      this.sourceCode.getLocFromIndex(betweenStart),
1✔
276
      postcssRoot,
1✔
277
    );
1✔
278
  }
1✔
279

1✔
280
  /**
1✔
281
   * Convert atrule Node
1✔
282
   * @param  {object} node  The node.
1✔
283
   * @param  {SourceLocation} loc  The location.
1✔
284
   * @param  {number} start  The index of start.
1✔
285
   * @param  {number} end  The index of end.
1✔
286
   * @param  {Node} parent  The parent node.
1✔
287
   * @returns {VCSSAtRule}
1✔
288
   */
1✔
289
  protected convertAtruleNode(
1✔
290
    node: PostCSSAtRule,
1✔
291
    loc: SourceLocation,
1✔
292
    start: number,
1✔
293
    end: number,
1✔
294
    parent: VCSSContainerNode,
1✔
295
  ): VCSSNode | null {
1✔
296
    const astNode = new VCSSAtRule(node, loc, start, end, {
1✔
297
      parent,
1✔
298
      rawParamsText: this.getRaw(node, "params")?.raw ?? null,
1✔
299
      identifier: this.getRaw(node as never, "identifier") ?? "@",
1✔
300
    });
1✔
301
    if (node.name === "nest") {
1✔
302
      // The parameters following `@nest` are parsed as selectors.
1✔
303
      const paramsStartIndex =
1✔
304
        astNode.range[0] + // start index of at-rule
1✔
305
        astNode.identifier.length + // `@`
1✔
306
        astNode.name.length + // `nest`
1✔
307
        (this.getRaw(node, "afterName") || "").length; // comments and spaces
1!
308

1✔
309
      astNode.selectors = this.selectorParser.parse(
1✔
310
        astNode.rawParamsText,
1✔
311
        this.sourceCode.getLocFromIndex(paramsStartIndex),
1✔
312
        astNode,
1✔
313
      );
1✔
314
    }
1✔
315

1✔
316
    if (this.getRaw(node, "afterName")?.trim()) {
1✔
317
      this.parseAtruleRawsAfterName(node, astNode);
1✔
318
    }
1✔
319
    if (this.getRaw(node, "between")?.trim()) {
1✔
320
      this.parseAtruleRawsBetween(node, astNode);
1✔
321
    }
1✔
322

1✔
323
    return astNode;
1✔
324
  }
1✔
325

1✔
326
  private parseAtruleRawsAfterName(node: PostCSSAtRule, astNode: VCSSAtRule) {
1✔
327
    const afterName = this.getRaw(node, "afterName");
1✔
328

1✔
329
    const afterNameStart =
1✔
330
      astNode.range[0] + // start index of at-rule
1✔
331
      astNode.identifier.length + // `@`
1✔
332
      astNode.name.length; // `nest`
1✔
333
    const postcssRoot = this.parseInternal(afterName || "") as PostCSSRoot;
1!
334

1✔
335
    this._postcssNodeToASTNode(
1✔
336
      this.sourceCode.getLocFromIndex(afterNameStart),
1✔
337
      postcssRoot,
1✔
338
    );
1✔
339
  }
1✔
340

1✔
341
  private parseAtruleRawsBetween(node: PostCSSAtRule, astNode: VCSSAtRule) {
1✔
342
    const between = this.getRaw(node, "between");
1✔
343

1✔
344
    const rawParams = this.getRaw(node, "params")?.raw ?? node.params;
1✔
345
    const betweenStart =
1✔
346
      astNode.range[0] + // start index of at-rule
1✔
347
      astNode.identifier.length + // `@`
1✔
348
      astNode.name.length + // `nest`
1✔
349
      (this.getRaw(node, "afterName") || "").length + // comments and spaces
1!
350
      rawParams.length;
1✔
351

1✔
352
    const postcssRoot = this.parseInternal(between || "") as PostCSSRoot;
1!
353
    this._postcssNodeToASTNode(
1✔
354
      this.sourceCode.getLocFromIndex(betweenStart),
1✔
355
      postcssRoot,
1✔
356
    );
1✔
357
  }
1✔
358

1✔
359
  /**
1✔
360
   * Convert decl Node
1✔
361
   * @param  {object} node  The node.
1✔
362
   * @param  {SourceLocation} loc  The location.
1✔
363
   * @param  {number} start  The index of start.
1✔
364
   * @param  {number} end  The index of end.
1✔
365
   * @param  {Node} parent  The parent node.
1✔
366
   * @returns {VCSSDeclarationProperty}
1✔
367
   */
1✔
368
  protected convertDeclNode(
1✔
369
    node: PostCSSDeclaration,
1✔
370
    loc: SourceLocation,
1✔
371
    start: number,
1✔
372
    end: number,
1✔
373
    parent: VCSSContainerNode,
1✔
374
  ): VCSSNode | null {
1✔
375
    // adjust star hack
1✔
376
    // `*color: red`
1✔
377
    //  ^
1✔
378
    let property = node.prop;
1✔
379
    let starLength = 1;
1✔
380
    let textProp = this.sourceCode.text.slice(start, start + property.length);
1✔
381
    while (property !== textProp) {
1✔
382
      property = textProp.slice(0, starLength) + node.prop;
1✔
383

1✔
384
      starLength++;
1✔
385
      textProp = this.sourceCode.text.slice(start, start + property.length);
1✔
386
    }
1✔
387

1✔
388
    return new VCSSDeclarationProperty(node, loc, start, end, {
1✔
389
      parent,
1✔
390
      property,
1✔
391
    });
1✔
392
  }
1✔
393

1✔
394
  /**
1✔
395
   * Convert comment Node
1✔
396
   * @param  {object} node  The node.
1✔
397
   * @param  {SourceLocation} loc  The location.
1✔
398
   * @param  {number} start  The index of start.
1✔
399
   * @param  {number} end  The index of end.
1✔
400
   * @param  {Node} parent  The parent node.
1✔
401
   * @returns {void}
1✔
402
   */
1✔
403
  protected convertCommentNode(
1✔
404
    node: PostCSSComment,
1✔
405
    loc: SourceLocation,
1✔
406
    start: number,
1✔
407
    end: number,
1✔
408
    parent: VCSSContainerNode,
1✔
409
  ): VCSSNode | null {
1✔
410
    this.commentContainer.push(
1✔
411
      new VCSSComment(node, node.text, loc, start, end, { parent }),
1✔
412
    );
1✔
413
    return null;
1✔
414
  }
1✔
415

1✔
416
  /**
1✔
417
   * Convert unknown Node
1✔
418
   * @param  {object} node  The node.
1✔
419
   * @param  {SourceLocation} loc  The location.
1✔
420
   * @param  {number} start  The index of start.
1✔
421
   * @param  {number} end  The index of end.
1✔
422
   * @param  {Node} parent  The parent node.
1✔
423
   * @returns {Node}
1✔
424
   */
1✔
425
  protected convertUnknownTypeNode(
1✔
426
    node: PostCSSNode,
1✔
427
    loc: SourceLocation,
1✔
428
    start: number,
1✔
429
    end: number,
1✔
430
    parent: VCSSContainerNode,
1✔
431
  ): VCSSNode | null {
1✔
432
    return new VCSSUnknown(node, loc, start, end, {
1✔
433
      parent,
1✔
434
      unknownType: node.type,
1✔
435
    });
1✔
436
  }
1✔
437

1✔
438
  protected getRaw<N extends PostCSSNode, K extends keyof N["raws"] & string>(
1✔
439
    node: N,
1✔
440
    keyName: K,
1✔
441
  ): N["raws"][K] {
1✔
442
    // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
1✔
443
    return (node.raws as any)[keyName];
1✔
444
  }
1✔
445
}
1✔
446

1✔
447
/**
1✔
448
 * Convert PostCSS location to ESLint location.
1✔
449
 * @param {LineAndColumnData} offsetLocation start location of selector.
1✔
450
 * @param {object} loc the PostCSS location to convert
1✔
451
 * @return {LineAndColumnData} converted location.
1✔
452
 */
1✔
453
function getESLintLineAndColumnFromPostCSSPosition(
1✔
454
  offsetLocation: LineAndColumnData,
1✔
455
  loc: PostCSSLoc,
1✔
456
) {
1✔
457
  let { line } = loc;
1✔
458
  let column = loc.column - 1; // Change to 0 base.
1✔
459
  if (line === 1) {
1✔
460
    line = offsetLocation.line;
559✔
461
    column = offsetLocation.column + column;
559✔
462
  } else {
559✔
463
    line = offsetLocation.line + line - 1;
1✔
464
  }
1✔
465
  return { line, column };
1✔
466
}
1✔
467

1✔
468
/**
1✔
469
 * Convert PostCSS location to ESLint location.
1✔
470
 * @param {LineAndColumnData} offsetLocation location of inside the `<style>` node.
1✔
471
 * @param {object} node the PostCSS node to convert
1✔
472
 * @param {"start"|"end"} locName the name of location
1✔
473
 * @return {LineAndColumnData} converted location.
1✔
474
 */
1✔
475
function getESLintLineAndColumnFromPostCSSNode(
1✔
476
  offsetLocation: LineAndColumnData,
1✔
477
  node: PostCSSNode,
1✔
478
  locName: "start" | "end",
1✔
479
): LineAndColumnData | null {
1✔
480
  const sourceLoc = node.source[locName];
1✔
481
  if (!sourceLoc) {
1✔
482
    return null;
1✔
483
  }
1✔
484
  const { line, column } = getESLintLineAndColumnFromPostCSSPosition(
1✔
485
    offsetLocation,
1✔
486
    sourceLoc,
1✔
487
  );
1✔
488
  if (
1✔
489
    locName === "end" &&
1✔
490
    // The end location of the PostCSS root node has a different position than other nodes.
1,699✔
491
    node.type !== "root"
1,699✔
492
  ) {
1✔
493
    // End column is shifted by one.
1✔
494
    return { line, column: column + 1 };
1✔
495
  }
1✔
496
  return { line, column };
1✔
497
}
1✔
498

1✔
499
interface ConvertNodeTypes {
1✔
500
  root: "convertRootNode";
1✔
501
  atrule: "convertAtruleNode";
1✔
502
  rule: "convertRuleNode";
1✔
503
  decl: "convertDeclNode";
1✔
504
  comment: "convertCommentNode";
1✔
505
}
1✔
506
const convertNodeTypes: ConvertNodeTypes = {
1✔
507
  root: "convertRootNode",
1✔
508
  atrule: "convertAtruleNode",
1✔
509
  rule: "convertRuleNode",
1✔
510
  decl: "convertDeclNode",
1✔
511
  comment: "convertCommentNode",
1✔
512
};
1✔
513

1✔
514
/**
1✔
515
 * Get convert method name from given type
1✔
516
 */
1✔
517
function typeToConvertMethodName(
1✔
518
  type: keyof ConvertNodeTypes,
1✔
519
): "convertUnknownTypeNode" | ConvertNodeTypes[keyof ConvertNodeTypes] {
1✔
520
  return convertNodeTypes[type] || "convertUnknownTypeNode";
1!
521
}
1✔
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

© 2026 Coveralls, Inc