Skip to content

Commit 746af73

Browse files
committed
Add support for parser object to parserOptions.parser
1 parent 9dc89b5 commit 746af73

File tree

7 files changed

+127
-32
lines changed

7 files changed

+127
-32
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,26 @@ You can also specify an object and change the parser separately for `<script lan
107107
}
108108
```
109109

110+
When using JavaScript configuration (`.eslintrc.js`), you can also give the parser object directly.
111+
112+
```js
113+
const tsParser = require("@typescript-eslint/parser")
114+
const espree = require("espree")
115+
116+
module.exports = {
117+
parser: "vue-eslint-parser",
118+
parserOptions: {
119+
// Single parser
120+
parser: tsParser,
121+
// Multiple parser
122+
parser: {
123+
js: espree,
124+
ts: tsParser,
125+
}
126+
},
127+
}
128+
```
129+
110130
If the `parserOptions.parser` is `false`, the `vue-eslint-parser` skips parsing `<script>` tags completely.
111131
This is useful for people who use the language ESLint community doesn't provide custom parser implementation.
112132

src/common/espree.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,13 @@
1-
import type { ESLintExtendedProgram, ESLintProgram } from "../ast"
21
import type { ParserOptions } from "../common/parser-options"
32
import { getLinterRequire } from "./linter-require"
43
// @ts-expect-error -- ignore
54
import * as dependencyEspree from "espree"
65
import { lte, lt } from "semver"
76
import { createRequire } from "./create-require"
87
import path from "path"
8+
import type { BasicParserObject } from "./parser-object"
99

10-
/**
11-
* The interface of a result of ESLint custom parser.
12-
*/
13-
export type ESLintCustomParserResult = ESLintProgram | ESLintExtendedProgram
14-
15-
/**
16-
* The interface of ESLint custom parsers.
17-
*/
18-
export interface ESLintCustomParser {
19-
parse(code: string, options: any): ESLintCustomParserResult
20-
parseForESLint?(code: string, options: any): ESLintCustomParserResult
21-
}
22-
type Espree = ESLintCustomParser & {
10+
type Espree = BasicParserObject & {
2311
latestEcmaVersion?: number
2412
version: string
2513
}

src/common/parser-object.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { ESLintExtendedProgram, ESLintProgram } from "../ast"
2+
3+
export type BasicParserObject<R = ESLintProgram> = {
4+
parse(code: string, options: any): R
5+
parseForESLint: undefined
6+
}
7+
export type EnhancedParserObject<R = ESLintExtendedProgram> = {
8+
parseForESLint(code: string, options: any): R
9+
parse: undefined
10+
}
11+
12+
export type ParserObject<R1 = ESLintExtendedProgram, R2 = ESLintProgram> =
13+
| EnhancedParserObject<R1>
14+
| BasicParserObject<R2>
15+
16+
export function isParserObject<R1, R2>(
17+
value: ParserObject<R1, R2> | {} | undefined | null,
18+
): value is ParserObject<R1, R2> {
19+
return isEnhancedParserObject(value) || isBasicParserObject(value)
20+
}
21+
export function isEnhancedParserObject<R>(
22+
value: EnhancedParserObject<R> | {} | undefined | null,
23+
): value is EnhancedParserObject<R> {
24+
return Boolean(value && typeof (value as any).parseForESLint === "function")
25+
}
26+
export function isBasicParserObject<R>(
27+
value: BasicParserObject<R> | {} | undefined | null,
28+
): value is BasicParserObject<R> {
29+
return Boolean(value && typeof (value as any).parse === "function")
30+
}

src/common/parser-options.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import * as path from "path"
22
import type { VDocumentFragment } from "../ast"
33
import { getLang, isScriptElement, isScriptSetupElement } from "./ast-utils"
4+
import type { ParserObject } from "./parser-object"
5+
import { isParserObject } from "./parser-object"
46

57
export interface ParserOptions {
68
// vue-eslint-parser options
7-
parser?: boolean | string
9+
parser?:
10+
| boolean
11+
| string
12+
| ParserObject
13+
| Record<string, string | ParserObject | undefined>
814
vueFeatures?: {
915
interpolationAsNonHTML?: boolean // default true
1016
filter?: boolean // default true
@@ -55,9 +61,17 @@ export function isSFCFile(parserOptions: ParserOptions) {
5561
* Gets the script parser name from the given parser lang.
5662
*/
5763
export function getScriptParser(
58-
parser: boolean | string | Record<string, string | undefined> | undefined,
64+
parser:
65+
| boolean
66+
| string
67+
| ParserObject
68+
| Record<string, string | ParserObject | undefined>
69+
| undefined,
5970
getParserLang: () => string | null | Iterable<string | null>,
60-
): string | undefined {
71+
): string | ParserObject | undefined {
72+
if (isParserObject(parser)) {
73+
return parser
74+
}
6175
if (parser && typeof parser === "object") {
6276
const parserLang = getParserLang()
6377
const parserLangs =
@@ -68,7 +82,10 @@ export function getScriptParser(
6882
: parserLang
6983
for (const lang of parserLangs) {
7084
const parserForLang = lang && parser[lang]
71-
if (typeof parserForLang === "string") {
85+
if (
86+
typeof parserForLang === "string" ||
87+
isParserObject(parserForLang)
88+
) {
7289
return parserForLang
7390
}
7491
}

src/script/index.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ import {
4242
analyzeExternalReferences,
4343
analyzeVariablesAndExternalReferences,
4444
} from "./scope-analyzer"
45-
import type { ESLintCustomParser } from "../common/espree"
4645
import {
4746
getEcmaVersionIfUseEspree,
4847
getEspreeFromUser,
@@ -60,6 +59,8 @@ import {
6059
} from "../script-setup/parser-options"
6160
import { isScriptSetupElement } from "../common/ast-utils"
6261
import type { LinesAndColumns } from "../common/lines-and-columns"
62+
import type { ParserObject } from "../common/parser-object"
63+
import { isEnhancedParserObject, isParserObject } from "../common/parser-object"
6364

6465
// [1] = aliases.
6566
// [2] = delimiter.
@@ -545,15 +546,16 @@ export function parseScript(
545546
code: string,
546547
parserOptions: ParserOptions,
547548
): ESLintExtendedProgram {
548-
const parser: ESLintCustomParser =
549+
const parser: ParserObject =
549550
typeof parserOptions.parser === "string"
550551
? loadParser(parserOptions.parser)
552+
: isParserObject(parserOptions.parser)
553+
? parserOptions.parser
551554
: getEspreeFromEcmaVersion(parserOptions.ecmaVersion)
552555

553-
const result: any =
554-
typeof parser.parseForESLint === "function"
555-
? parser.parseForESLint(code, parserOptions)
556-
: parser.parse(code, parserOptions)
556+
const result: any = isEnhancedParserObject(parser)
557+
? parser.parseForESLint(code, parserOptions)
558+
: parser.parse(code, parserOptions)
557559

558560
if (result.ast != null) {
559561
return result

src/sfc/custom-block/index.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,12 @@ import { getEslintScope } from "../../common/eslint-scope"
1414
import { getEcmaVersionIfUseEspree } from "../../common/espree"
1515
import { fixErrorLocation, fixLocations } from "../../common/fix-locations"
1616
import type { LocationCalculatorForHtml } from "../../common/location-calculator"
17+
import type { ParserObject } from "../../common/parser-object"
18+
import { isEnhancedParserObject } from "../../common/parser-object"
1719
import type { ParserOptions } from "../../common/parser-options"
1820
import { DEFAULT_ECMA_VERSION } from "../../script-setup/parser-options"
1921

20-
export interface ESLintCustomBlockParser {
21-
parse(code: string, options: any): any
22-
parseForESLint?(code: string, options: any): any
23-
}
22+
export type ESLintCustomBlockParser = ParserObject<any, any>
2423

2524
export type CustomBlockContext = {
2625
getSourceCode(): SourceCode
@@ -181,10 +180,9 @@ function parseBlock(
181180
parser: ESLintCustomBlockParser,
182181
parserOptions: any,
183182
): any {
184-
const result: any =
185-
typeof parser.parseForESLint === "function"
186-
? parser.parseForESLint(code, parserOptions)
187-
: parser.parse(code, parserOptions)
183+
const result = isEnhancedParserObject(parser)
184+
? parser.parseForESLint(code, parserOptions)
185+
: parser.parse(code, parserOptions)
188186

189187
if (result.ast != null) {
190188
return result

test/index.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,46 @@ describe("Basic tests", () => {
308308
assert.deepStrictEqual(report[0].messages, [])
309309
assert.deepStrictEqual(report[1].messages, [])
310310
})
311+
312+
it("should notify no error with parser object with '@typescript-eslint/parser'", async () => {
313+
const cli = new ESLint({
314+
cwd: FIXTURE_DIR,
315+
overrideConfig: {
316+
env: { es6: true, node: true },
317+
parser: PARSER_PATH,
318+
parserOptions: {
319+
parser: require("@typescript-eslint/parser"),
320+
},
321+
rules: { semi: ["error", "never"] },
322+
},
323+
useEslintrc: false,
324+
})
325+
const report = await cli.lintFiles(["typed.js"])
326+
const messages = report[0].messages
327+
328+
assert.deepStrictEqual(messages, [])
329+
})
330+
331+
it("should notify no error with multiple parser object with '@typescript-eslint/parser'", async () => {
332+
const cli = new ESLint({
333+
cwd: FIXTURE_DIR,
334+
overrideConfig: {
335+
env: { es6: true, node: true },
336+
parser: PARSER_PATH,
337+
parserOptions: {
338+
parser: {
339+
ts: require("@typescript-eslint/parser"),
340+
},
341+
},
342+
rules: { semi: ["error", "never"] },
343+
},
344+
useEslintrc: false,
345+
})
346+
const report = await cli.lintFiles(["typed.ts", "typed.tsx"])
347+
348+
assert.deepStrictEqual(report[0].messages, [])
349+
assert.deepStrictEqual(report[1].messages, [])
350+
})
311351
}
312352
})
313353

0 commit comments

Comments
 (0)