eslint 9.9.0 → 9.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,9 +17,6 @@ const
17
17
  espree = require("espree"),
18
18
  merge = require("lodash.merge"),
19
19
  pkg = require("../../package.json"),
20
- {
21
- directivesPattern
22
- } = require("../shared/directives"),
23
20
  {
24
21
  Legacy: {
25
22
  ConfigOps,
@@ -30,7 +27,7 @@ const
30
27
  Traverser = require("../shared/traverser"),
31
28
  { SourceCode } = require("../languages/js/source-code"),
32
29
  applyDisableDirectives = require("./apply-disable-directives"),
33
- ConfigCommentParser = require("./config-comment-parser"),
30
+ { ConfigCommentParser } = require("@eslint/plugin-kit"),
34
31
  NodeEventGenerator = require("./node-event-generator"),
35
32
  createReportTranslator = require("./report-translator"),
36
33
  Rules = require("./rules"),
@@ -55,6 +52,8 @@ const DEFAULT_ERROR_LOC = { start: { line: 1, column: 0 }, end: { line: 1, colum
55
52
  const parserSymbol = Symbol.for("eslint.RuleTester.parser");
56
53
  const { LATEST_ECMA_VERSION } = require("../../conf/ecma-version");
57
54
  const { VFile } = require("./vfile");
55
+ const { ParserService } = require("../services/parser-service");
56
+ const { FileContext } = require("./file-context");
58
57
  const STEP_KIND_VISIT = 1;
59
58
  const STEP_KIND_CALL = 2;
60
59
 
@@ -333,7 +332,7 @@ function createDisableDirectives({ type, value, justification, node }, ruleMappe
333
332
  directives: [], // valid disable directives
334
333
  directiveProblems: [] // problems in directives
335
334
  };
336
- const parentDirective = { node, ruleIds };
335
+ const parentDirective = { node, value, ruleIds };
337
336
 
338
337
  for (const ruleId of directiveRules) {
339
338
 
@@ -401,15 +400,20 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
401
400
  });
402
401
 
403
402
  sourceCode.getInlineConfigNodes().filter(token => token.type !== "Shebang").forEach(comment => {
404
- const { directivePart, justificationPart } = commentParser.extractDirectiveComment(comment.value);
405
403
 
406
- const match = directivesPattern.exec(directivePart);
404
+ const directive = commentParser.parseDirective(comment.value);
407
405
 
408
- if (!match) {
406
+ if (!directive) {
409
407
  return;
410
408
  }
411
- const directiveText = match[1];
412
- const lineCommentSupported = /^eslint-disable-(next-)?line$/u.test(directiveText);
409
+
410
+ const {
411
+ label,
412
+ value,
413
+ justification: justificationPart
414
+ } = directive;
415
+
416
+ const lineCommentSupported = /^eslint-disable-(next-)?line$/u.test(label);
413
417
 
414
418
  if (comment.type === "Line" && !lineCommentSupported) {
415
419
  return;
@@ -418,7 +422,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
418
422
  const loc = sourceCode.getLoc(comment);
419
423
 
420
424
  if (warnInlineConfig) {
421
- const kind = comment.type === "Block" ? `/*${directiveText}*/` : `//${directiveText}`;
425
+ const kind = comment.type === "Block" ? `/*${label}*/` : `//${label}`;
422
426
 
423
427
  problems.push(createLintingProblem({
424
428
  ruleId: null,
@@ -429,8 +433,8 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
429
433
  return;
430
434
  }
431
435
 
432
- if (directiveText === "eslint-disable-line" && loc.start.line !== loc.end.line) {
433
- const message = `${directiveText} comment should not span multiple lines.`;
436
+ if (label === "eslint-disable-line" && loc.start.line !== loc.end.line) {
437
+ const message = `${label} comment should not span multiple lines.`;
434
438
 
435
439
  problems.push(createLintingProblem({
436
440
  ruleId: null,
@@ -440,17 +444,15 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
440
444
  return;
441
445
  }
442
446
 
443
- const directiveValue = directivePart.slice(match.index + directiveText.length);
444
-
445
- switch (directiveText) {
447
+ switch (label) {
446
448
  case "eslint-disable":
447
449
  case "eslint-enable":
448
450
  case "eslint-disable-next-line":
449
451
  case "eslint-disable-line": {
450
- const directiveType = directiveText.slice("eslint-".length);
452
+ const directiveType = label.slice("eslint-".length);
451
453
  const { directives, directiveProblems } = createDisableDirectives({
452
454
  type: directiveType,
453
- value: directiveValue,
455
+ value,
454
456
  justification: justificationPart,
455
457
  node: comment
456
458
  }, ruleMapper, jslang, sourceCode);
@@ -461,16 +463,16 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
461
463
  }
462
464
 
463
465
  case "exported":
464
- Object.assign(exportedVariables, commentParser.parseListConfig(directiveValue, comment));
466
+ Object.assign(exportedVariables, commentParser.parseListConfig(value));
465
467
  break;
466
468
 
467
469
  case "globals":
468
470
  case "global":
469
- for (const [id, { value }] of Object.entries(commentParser.parseStringConfig(directiveValue, comment))) {
471
+ for (const [id, idSetting] of Object.entries(commentParser.parseStringConfig(value))) {
470
472
  let normalizedValue;
471
473
 
472
474
  try {
473
- normalizedValue = ConfigOps.normalizeConfigGlobal(value);
475
+ normalizedValue = ConfigOps.normalizeConfigGlobal(idSetting);
474
476
  } catch (err) {
475
477
  problems.push(createLintingProblem({
476
478
  ruleId: null,
@@ -493,9 +495,9 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
493
495
  break;
494
496
 
495
497
  case "eslint": {
496
- const parseResult = commentParser.parseJsonConfig(directiveValue);
498
+ const parseResult = commentParser.parseJSONLikeConfig(value);
497
499
 
498
- if (parseResult.success) {
500
+ if (parseResult.ok) {
499
501
  Object.keys(parseResult.config).forEach(name => {
500
502
  const rule = ruleMapper(name);
501
503
  const ruleValue = parseResult.config[name];
@@ -712,7 +714,7 @@ function findEslintEnv(text) {
712
714
  if (match[0].endsWith("*/")) {
713
715
  retv = Object.assign(
714
716
  retv || {},
715
- commentParser.parseListConfig(commentParser.extractDirectiveComment(match[1]).directivePart)
717
+ commentParser.parseListConfig(commentParser.parseDirective(match[0].slice(2, -2)).value)
716
718
  );
717
719
  }
718
720
  }
@@ -922,43 +924,6 @@ function analyzeScope(ast, languageOptions, visitorKeys) {
922
924
  });
923
925
  }
924
926
 
925
- /**
926
- * Parses file into an AST. Moved out here because the try-catch prevents
927
- * optimization of functions, so it's best to keep the try-catch as isolated
928
- * as possible
929
- * @param {VFile} file The file to parse.
930
- * @param {Language} language The language to use.
931
- * @param {LanguageOptions} languageOptions Options to pass to the parser
932
- * @returns {{success: false, error: LintMessage}|{success: true, sourceCode: SourceCode}}
933
- * An object containing the AST and parser services if parsing was successful, or the error if parsing failed
934
- * @private
935
- */
936
- function parse(file, language, languageOptions) {
937
-
938
- const result = language.parse(file, { languageOptions });
939
-
940
- if (result.ok) {
941
- return {
942
- success: true,
943
- sourceCode: language.createSourceCode(file, result, { languageOptions })
944
- };
945
- }
946
-
947
- // if we made it to here there was an error
948
- return {
949
- success: false,
950
- errors: result.errors.map(error => ({
951
- ruleId: null,
952
- nodeType: null,
953
- fatal: true,
954
- severity: 2,
955
- message: `Parsing error: ${error.message}`,
956
- line: error.line,
957
- column: error.column
958
- }))
959
- };
960
- }
961
-
962
927
  /**
963
928
  * Runs a rule, and gets its listeners
964
929
  * @param {Rule} rule A rule object
@@ -1015,24 +980,18 @@ function runRules(
1015
980
  * All rule contexts will inherit from this object. This avoids the performance penalty of copying all the
1016
981
  * properties once for each rule.
1017
982
  */
1018
- const sharedTraversalContext = Object.freeze(
1019
- {
1020
- getCwd: () => cwd,
1021
- cwd,
1022
- getFilename: () => filename,
1023
- filename,
1024
- getPhysicalFilename: () => physicalFilename || filename,
1025
- physicalFilename: physicalFilename || filename,
1026
- getSourceCode: () => sourceCode,
1027
- sourceCode,
1028
- parserOptions: {
1029
- ...languageOptions.parserOptions
1030
- },
1031
- parserPath: parserName,
1032
- languageOptions,
1033
- settings
1034
- }
1035
- );
983
+ const sharedTraversalContext = new FileContext({
984
+ cwd,
985
+ filename,
986
+ physicalFilename: physicalFilename || filename,
987
+ sourceCode,
988
+ parserOptions: {
989
+ ...languageOptions.parserOptions
990
+ },
991
+ parserPath: parserName,
992
+ languageOptions,
993
+ settings
994
+ });
1036
995
 
1037
996
  const lintingProblems = [];
1038
997
 
@@ -1407,10 +1366,13 @@ class Linter {
1407
1366
  t = startTime();
1408
1367
  }
1409
1368
 
1410
- const parseResult = parse(
1369
+ const parserService = new ParserService();
1370
+ const parseResult = parserService.parseSync(
1411
1371
  file,
1412
- jslang,
1413
- languageOptions
1372
+ {
1373
+ language: jslang,
1374
+ languageOptions
1375
+ }
1414
1376
  );
1415
1377
 
1416
1378
  if (options.stats) {
@@ -1420,7 +1382,7 @@ class Linter {
1420
1382
  storeTime(time, timeOpts, slots);
1421
1383
  }
1422
1384
 
1423
- if (!parseResult.success) {
1385
+ if (!parseResult.ok) {
1424
1386
  return parseResult.errors;
1425
1387
  }
1426
1388
 
@@ -1712,10 +1674,10 @@ class Linter {
1712
1674
  t = startTime();
1713
1675
  }
1714
1676
 
1715
- const parseResult = parse(
1677
+ const parserService = new ParserService();
1678
+ const parseResult = parserService.parseSync(
1716
1679
  file,
1717
- config.language,
1718
- languageOptions
1680
+ config
1719
1681
  );
1720
1682
 
1721
1683
  if (options.stats) {
@@ -1724,7 +1686,7 @@ class Linter {
1724
1686
  storeTime(time, { type: "parse" }, slots);
1725
1687
  }
1726
1688
 
1727
- if (!parseResult.success) {
1689
+ if (!parseResult.ok) {
1728
1690
  return parseResult.errors;
1729
1691
  }
1730
1692
 
@@ -117,6 +117,7 @@ module.exports = {
117
117
  return properties && !parent.computed && parent.key.name === node.name;
118
118
  },
119
119
  ImportDefaultSpecifier: true,
120
+ ImportNamespaceSpecifier: true,
120
121
  RestElement: true,
121
122
  FunctionExpression: true,
122
123
  ArrowFunctionExpression: true,
@@ -10,7 +10,7 @@
10
10
 
11
11
  const RegExpValidator = require("@eslint-community/regexpp").RegExpValidator;
12
12
  const validator = new RegExpValidator();
13
- const validFlags = /[dgimsuvy]/gu;
13
+ const validFlags = "dgimsuvy";
14
14
  const undefined1 = void 0;
15
15
 
16
16
  //------------------------------------------------------------------------------
@@ -49,13 +49,13 @@ module.exports = {
49
49
  create(context) {
50
50
 
51
51
  const options = context.options[0];
52
- let allowedFlags = null;
52
+ let allowedFlags = [];
53
53
 
54
54
  if (options && options.allowConstructorFlags) {
55
- const temp = options.allowConstructorFlags.join("").replace(validFlags, "");
55
+ const temp = options.allowConstructorFlags.join("").replace(new RegExp(`[${validFlags}]`, "gu"), "");
56
56
 
57
57
  if (temp) {
58
- allowedFlags = new RegExp(`[${temp}]`, "gu");
58
+ allowedFlags = [...new Set(temp)];
59
59
  }
60
60
  }
61
61
 
@@ -125,16 +125,19 @@ module.exports = {
125
125
  /**
126
126
  * Check syntax error in a given flags.
127
127
  * @param {string|null} flags The RegExp flags to validate.
128
+ * @param {string|null} flagsToCheck The RegExp invalid flags.
129
+ * @param {string} allFlags all valid and allowed flags.
128
130
  * @returns {string|null} The syntax error.
129
131
  */
130
- function validateRegExpFlags(flags) {
131
- if (!flags) {
132
- return null;
133
- }
134
- try {
135
- validator.validateFlags(flags);
136
- } catch {
137
- return `Invalid flags supplied to RegExp constructor '${flags}'`;
132
+ function validateRegExpFlags(flags, flagsToCheck, allFlags) {
133
+ const duplicateFlags = [];
134
+
135
+ if (typeof flagsToCheck === "string") {
136
+ for (const flag of flagsToCheck) {
137
+ if (allFlags.includes(flag)) {
138
+ duplicateFlags.push(flag);
139
+ }
140
+ }
138
141
  }
139
142
 
140
143
  /*
@@ -142,10 +145,19 @@ module.exports = {
142
145
  * but this rule may check only the flag when the pattern is unidentifiable, so check it here.
143
146
  * https://tc39.es/ecma262/multipage/text-processing.html#sec-parsepattern
144
147
  */
145
- if (flags.includes("u") && flags.includes("v")) {
148
+ if (flags && flags.includes("u") && flags.includes("v")) {
146
149
  return "Regex 'u' and 'v' flags cannot be used together";
147
150
  }
148
- return null;
151
+
152
+ if (duplicateFlags.length > 0) {
153
+ return `Duplicate flags ('${duplicateFlags.join("")}') supplied to RegExp constructor`;
154
+ }
155
+
156
+ if (!flagsToCheck) {
157
+ return null;
158
+ }
159
+
160
+ return `Invalid flags supplied to RegExp constructor '${flagsToCheck}'`;
149
161
  }
150
162
 
151
163
  return {
@@ -154,13 +166,17 @@ module.exports = {
154
166
  return;
155
167
  }
156
168
 
157
- let flags = getFlags(node);
169
+ const flags = getFlags(node);
170
+ let flagsToCheck = flags;
171
+ const allFlags = allowedFlags.length > 0 ? validFlags.split("").concat(allowedFlags) : validFlags.split("");
158
172
 
159
- if (flags && allowedFlags) {
160
- flags = flags.replace(allowedFlags, "");
173
+ if (flags) {
174
+ allFlags.forEach(flag => {
175
+ flagsToCheck = flagsToCheck.replace(flag, "");
176
+ });
161
177
  }
162
178
 
163
- let message = validateRegExpFlags(flags);
179
+ let message = validateRegExpFlags(flags, flagsToCheck, allFlags);
164
180
 
165
181
  if (message) {
166
182
  report(node, message);
@@ -18,6 +18,26 @@ const {
18
18
  const astUtils = require("./utils/ast-utils.js");
19
19
  const { isValidWithUnicodeFlag } = require("./utils/regular-expressions");
20
20
 
21
+ /**
22
+ * Checks whether the flag configuration should be treated as a missing flag.
23
+ * @param {"u"|"v"|undefined} requireFlag A particular flag to require
24
+ * @param {string} flags The regex flags
25
+ * @returns {boolean} Whether the flag configuration results in a missing flag.
26
+ */
27
+ function checkFlags(requireFlag, flags) {
28
+ let missingFlag;
29
+
30
+ if (requireFlag === "v") {
31
+ missingFlag = !flags.includes("v");
32
+ } else if (requireFlag === "u") {
33
+ missingFlag = !flags.includes("u");
34
+ } else {
35
+ missingFlag = !flags.includes("u") && !flags.includes("v");
36
+ }
37
+
38
+ return missingFlag;
39
+ }
40
+
21
41
  //------------------------------------------------------------------------------
22
42
  // Rule Definition
23
43
  //------------------------------------------------------------------------------
@@ -37,31 +57,65 @@ module.exports = {
37
57
 
38
58
  messages: {
39
59
  addUFlag: "Add the 'u' flag.",
40
- requireUFlag: "Use the 'u' flag."
60
+ addVFlag: "Add the 'v' flag.",
61
+ requireUFlag: "Use the 'u' flag.",
62
+ requireVFlag: "Use the 'v' flag."
41
63
  },
42
64
 
43
- schema: []
65
+ schema: [
66
+ {
67
+ type: "object",
68
+ properties: {
69
+ requireFlag: {
70
+ enum: ["u", "v"]
71
+ }
72
+ },
73
+ additionalProperties: false
74
+ }
75
+ ]
44
76
  },
45
77
 
46
78
  create(context) {
47
79
 
48
80
  const sourceCode = context.sourceCode;
49
81
 
82
+ const {
83
+ requireFlag
84
+ } = context.options[0] ?? {};
85
+
50
86
  return {
51
87
  "Literal[regex]"(node) {
52
88
  const flags = node.regex.flags || "";
53
89
 
54
- if (!flags.includes("u") && !flags.includes("v")) {
90
+ const missingFlag = checkFlags(requireFlag, flags);
91
+
92
+ if (missingFlag) {
55
93
  context.report({
56
- messageId: "requireUFlag",
94
+ messageId: requireFlag === "v" ? "requireVFlag" : "requireUFlag",
57
95
  node,
58
- suggest: isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, node.regex.pattern)
96
+ suggest: isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, node.regex.pattern, requireFlag)
59
97
  ? [
60
98
  {
61
99
  fix(fixer) {
62
- return fixer.insertTextAfter(node, "u");
100
+ const replaceFlag = requireFlag ?? "u";
101
+ const regex = sourceCode.getText(node);
102
+ const slashPos = regex.lastIndexOf("/");
103
+
104
+ if (requireFlag) {
105
+ const flag = requireFlag === "u" ? "v" : "u";
106
+
107
+ if (regex.includes(flag, slashPos)) {
108
+ return fixer.replaceText(
109
+ node,
110
+ regex.slice(0, slashPos) +
111
+ regex.slice(slashPos).replace(flag, requireFlag)
112
+ );
113
+ }
114
+ }
115
+
116
+ return fixer.insertTextAfter(node, replaceFlag);
63
117
  },
64
- messageId: "addUFlag"
118
+ messageId: requireFlag === "v" ? "addVFlag" : "addUFlag"
65
119
  }
66
120
  ]
67
121
  : null
@@ -85,22 +139,49 @@ module.exports = {
85
139
  const pattern = getStringIfConstant(patternNode, scope);
86
140
  const flags = getStringIfConstant(flagsNode, scope);
87
141
 
88
- if (!flagsNode || (typeof flags === "string" && !flags.includes("u") && !flags.includes("v"))) {
142
+ let missingFlag = !flagsNode;
143
+
144
+ if (typeof flags === "string") {
145
+ missingFlag = checkFlags(requireFlag, flags);
146
+ }
147
+
148
+ if (missingFlag) {
89
149
  context.report({
90
- messageId: "requireUFlag",
150
+ messageId: requireFlag === "v" ? "requireVFlag" : "requireUFlag",
91
151
  node: refNode,
92
- suggest: typeof pattern === "string" && isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern)
152
+ suggest: typeof pattern === "string" && isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern, requireFlag)
93
153
  ? [
94
154
  {
95
155
  fix(fixer) {
156
+ const replaceFlag = requireFlag ?? "u";
157
+
96
158
  if (flagsNode) {
97
159
  if ((flagsNode.type === "Literal" && typeof flagsNode.value === "string") || flagsNode.type === "TemplateLiteral") {
98
160
  const flagsNodeText = sourceCode.getText(flagsNode);
161
+ const flag = requireFlag === "u" ? "v" : "u";
162
+
163
+ if (flags.includes(flag)) {
164
+
165
+ // Avoid replacing "u" in escapes like `\uXXXX`
166
+ if (flagsNode.type === "Literal" && flagsNode.raw.includes("\\")) {
167
+ return null;
168
+ }
169
+
170
+ // Avoid replacing "u" in expressions like "`${regularFlags}g`"
171
+ if (flagsNode.type === "TemplateLiteral" && (
172
+ flagsNode.expressions.length ||
173
+ flagsNode.quasis.some(({ value: { raw } }) => raw.includes("\\"))
174
+ )) {
175
+ return null;
176
+ }
177
+
178
+ return fixer.replaceText(flagsNode, flagsNodeText.replace(flag, replaceFlag));
179
+ }
99
180
 
100
181
  return fixer.replaceText(flagsNode, [
101
182
  flagsNodeText.slice(0, flagsNodeText.length - 1),
102
183
  flagsNodeText.slice(flagsNodeText.length - 1)
103
- ].join("u"));
184
+ ].join(replaceFlag));
104
185
  }
105
186
 
106
187
  // We intentionally don't suggest concatenating + "u" to non-literals
@@ -112,11 +193,11 @@ module.exports = {
112
193
  return fixer.insertTextAfter(
113
194
  penultimateToken,
114
195
  astUtils.isCommaToken(penultimateToken)
115
- ? ' "u",'
116
- : ', "u"'
196
+ ? ` "${replaceFlag}",`
197
+ : `, "${replaceFlag}"`
117
198
  );
118
199
  },
119
- messageId: "addUFlag"
200
+ messageId: requireFlag === "v" ? "addVFlag" : "addUFlag"
120
201
  }
121
202
  ]
122
203
  : null
@@ -14,12 +14,16 @@ const REGEXPP_LATEST_ECMA_VERSION = 2025;
14
14
  * Checks if the given regular expression pattern would be valid with the `u` flag.
15
15
  * @param {number} ecmaVersion ECMAScript version to parse in.
16
16
  * @param {string} pattern The regular expression pattern to verify.
17
+ * @param {"u"|"v"} flag The type of Unicode flag
17
18
  * @returns {boolean} `true` if the pattern would be valid with the `u` flag.
18
19
  * `false` if the pattern would be invalid with the `u` flag or the configured
19
20
  * ecmaVersion doesn't support the `u` flag.
20
21
  */
21
- function isValidWithUnicodeFlag(ecmaVersion, pattern) {
22
- if (ecmaVersion <= 5) { // ecmaVersion <= 5 doesn't support the 'u' flag
22
+ function isValidWithUnicodeFlag(ecmaVersion, pattern, flag = "u") {
23
+ if (flag === "u" && ecmaVersion <= 5) { // ecmaVersion <= 5 doesn't support the 'u' flag
24
+ return false;
25
+ }
26
+ if (flag === "v" && ecmaVersion <= 2023) {
23
27
  return false;
24
28
  }
25
29
 
@@ -28,7 +32,11 @@ function isValidWithUnicodeFlag(ecmaVersion, pattern) {
28
32
  });
29
33
 
30
34
  try {
31
- validator.validatePattern(pattern, void 0, void 0, { unicode: /* uFlag = */ true });
35
+ validator.validatePattern(pattern, void 0, void 0, flag === "u" ? {
36
+ unicode: /* uFlag = */ true
37
+ } : {
38
+ unicodeSets: true
39
+ });
32
40
  } catch {
33
41
  return false;
34
42
  }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @fileoverview ESLint Parser
3
+ * @author Nicholas C. Zakas
4
+ */
5
+ /* eslint class-methods-use-this: off -- Anticipate future constructor arguments. */
6
+
7
+ "use strict";
8
+
9
+ //-----------------------------------------------------------------------------
10
+ // Types
11
+ //-----------------------------------------------------------------------------
12
+
13
+ /** @typedef {import("../linter/vfile.js").VFile} VFile */
14
+ /** @typedef {import("@eslint/core").Language} Language */
15
+ /** @typedef {import("@eslint/core").LanguageOptions} LanguageOptions */
16
+
17
+ //-----------------------------------------------------------------------------
18
+ // Exports
19
+ //-----------------------------------------------------------------------------
20
+
21
+ /**
22
+ * The parser for ESLint.
23
+ */
24
+ class ParserService {
25
+
26
+ /**
27
+ * Parses the given file synchronously.
28
+ * @param {VFile} file The file to parse.
29
+ * @param {{language:Language,languageOptions:LanguageOptions}} config The configuration to use.
30
+ * @returns {Object} An object with the parsed source code or errors.
31
+ * @throws {Error} If the parser returns a promise.
32
+ */
33
+ parseSync(file, config) {
34
+
35
+ const { language, languageOptions } = config;
36
+ const result = language.parse(file, { languageOptions });
37
+
38
+ if (typeof result.then === "function") {
39
+ throw new Error("Unsupported: Language parser returned a promise.");
40
+ }
41
+
42
+ if (result.ok) {
43
+ return {
44
+ ok: true,
45
+ sourceCode: language.createSourceCode(file, result, { languageOptions })
46
+ };
47
+ }
48
+
49
+ // if we made it to here there was an error
50
+ return {
51
+ ok: false,
52
+ errors: result.errors.map(error => ({
53
+ ruleId: null,
54
+ nodeType: null,
55
+ fatal: true,
56
+ severity: 2,
57
+ message: `Parsing error: ${error.message}`,
58
+ line: error.line,
59
+ column: error.column
60
+ }))
61
+ };
62
+ }
63
+ }
64
+
65
+ module.exports = { ParserService };