Skip to content

Commit e112642

Browse files
nzakasmdjermanovic
andauthored
refactor: Extract parsing logic from Linter (#18790)
* refactor: Extract parsing logic from Linter refs #18787 * Clean up type information * Update lib/linter/linter.js Co-authored-by: Milos Djermanovic <[email protected]> * Update lib/services/parser-service.js Co-authored-by: Milos Djermanovic <[email protected]> --------- Co-authored-by: Milos Djermanovic <[email protected]>
1 parent 241fcea commit e112642

File tree

2 files changed

+77
-45
lines changed

2 files changed

+77
-45
lines changed

lib/linter/linter.js

Lines changed: 12 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const DEFAULT_ERROR_LOC = { start: { line: 1, column: 0 }, end: { line: 1, colum
5555
const parserSymbol = Symbol.for("eslint.RuleTester.parser");
5656
const { LATEST_ECMA_VERSION } = require("../../conf/ecma-version");
5757
const { VFile } = require("./vfile");
58+
const { ParserService } = require("../services/parser-service");
5859
const STEP_KIND_VISIT = 1;
5960
const STEP_KIND_CALL = 2;
6061

@@ -922,43 +923,6 @@ function analyzeScope(ast, languageOptions, visitorKeys) {
922923
});
923924
}
924925

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-
962926
/**
963927
* Runs a rule, and gets its listeners
964928
* @param {Rule} rule A rule object
@@ -1407,10 +1371,13 @@ class Linter {
14071371
t = startTime();
14081372
}
14091373

1410-
const parseResult = parse(
1374+
const parserService = new ParserService();
1375+
const parseResult = parserService.parseSync(
14111376
file,
1412-
jslang,
1413-
languageOptions
1377+
{
1378+
language: jslang,
1379+
languageOptions
1380+
}
14141381
);
14151382

14161383
if (options.stats) {
@@ -1420,7 +1387,7 @@ class Linter {
14201387
storeTime(time, timeOpts, slots);
14211388
}
14221389

1423-
if (!parseResult.success) {
1390+
if (!parseResult.ok) {
14241391
return parseResult.errors;
14251392
}
14261393

@@ -1712,10 +1679,10 @@ class Linter {
17121679
t = startTime();
17131680
}
17141681

1715-
const parseResult = parse(
1682+
const parserService = new ParserService();
1683+
const parseResult = parserService.parseSync(
17161684
file,
1717-
config.language,
1718-
languageOptions
1685+
config
17191686
);
17201687

17211688
if (options.stats) {
@@ -1724,7 +1691,7 @@ class Linter {
17241691
storeTime(time, { type: "parse" }, slots);
17251692
}
17261693

1727-
if (!parseResult.success) {
1694+
if (!parseResult.ok) {
17281695
return parseResult.errors;
17291696
}
17301697

lib/services/parser-service.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 };

0 commit comments

Comments
 (0)