Skip to content

Commit 27a275f

Browse files
committed
✨ use espree in eslint deps
1 parent 6234993 commit 27a275f

File tree

4 files changed

+133
-18
lines changed

4 files changed

+133
-18
lines changed

Diff for: .github/workflows/CI.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: CI
22
on:
33
push:
4-
branches: [master]
4+
branches: [master, try]
55
pull_request:
66
branches: [master]
77
schedule:
@@ -21,7 +21,7 @@ jobs:
2121
with:
2222
node-version: 14
2323
- name: Install Packages
24-
run: npm install
24+
run: npm install && cd test/fixtures/eslint && npm install
2525
- name: Lint
2626
run: npm run -s lint
2727

Diff for: src/script/espree.ts

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import Module from "module"
2+
import path from "path"
3+
import { ESLintExtendedProgram, ESLintProgram } from "../ast"
4+
5+
/**
6+
* The interface of a result of ESLint custom parser.
7+
*/
8+
export type ESLintCustomParserResult = ESLintProgram | ESLintExtendedProgram
9+
10+
/**
11+
* The interface of ESLint custom parsers.
12+
*/
13+
export interface ESLintCustomParser {
14+
parse(code: string, options: any): ESLintCustomParserResult
15+
parseForESLint?(code: string, options: any): ESLintCustomParserResult
16+
}
17+
18+
const createRequire: (filename: string) => (filename: string) => any =
19+
// Added in v12.2.0
20+
(Module as any).createRequire ||
21+
// Added in v10.12.0, but deprecated in v12.2.0.
22+
Module.createRequireFromPath ||
23+
// Polyfill - This is not executed on the tests on node@>=10.
24+
/* istanbul ignore next */
25+
(filename => {
26+
const mod = new Module(filename)
27+
28+
mod.filename = filename
29+
mod.paths = (Module as any)._nodeModulePaths(path.dirname(filename))
30+
;(mod as any)._compile("module.exports = require;", filename)
31+
return mod.exports
32+
})
33+
34+
let espreeCache: ESLintCustomParser | null = null
35+
36+
function isLinterPath(p: string): boolean {
37+
return (
38+
// ESLint 6 and above
39+
p.includes(
40+
`eslint${path.sep}lib${path.sep}linter${path.sep}linter.js`,
41+
) ||
42+
// ESLint 5
43+
p.includes(`eslint${path.sep}lib${path.sep}linter.js`)
44+
)
45+
}
46+
47+
/**
48+
* Load `espree` from the loaded ESLint.
49+
* If the loaded ESLint was not found, just returns `require("espree")`.
50+
*/
51+
export function getEspree(): ESLintCustomParser {
52+
if (!espreeCache) {
53+
// Lookup the loaded eslint
54+
const linterPath = Object.keys(require.cache).find(isLinterPath)
55+
if (linterPath) {
56+
try {
57+
espreeCache = createRequire(linterPath)("espree")
58+
} catch {
59+
// ignore
60+
}
61+
}
62+
if (!espreeCache) {
63+
//eslint-disable-next-line @mysticatea/ts/no-require-imports
64+
espreeCache = require("espree")
65+
}
66+
}
67+
68+
return espreeCache!
69+
}

Diff for: src/script/index.ts

+2-16
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {
1717
ESLintForOfStatement,
1818
ESLintFunctionExpression,
1919
ESLintPattern,
20-
ESLintProgram,
2120
ESLintVariableDeclaration,
2221
ESLintUnaryExpression,
2322
HasLocation,
@@ -39,6 +38,7 @@ import {
3938
analyzeExternalReferences,
4039
analyzeVariablesAndExternalReferences,
4140
} from "./scope-analyzer"
41+
import { ESLintCustomParser, getEspree } from "./espree"
4242

4343
// [1] = spacing before the aliases.
4444
// [2] = aliases.
@@ -51,14 +51,6 @@ const DUMMY_PARENT: any = {}
5151
const IS_FUNCTION_EXPRESSION = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/u
5252
const IS_SIMPLE_PATH = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?'\]|\["[^"]*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/u
5353

54-
/**
55-
* The interface of ESLint custom parsers.
56-
*/
57-
interface ESLintCustomParser {
58-
parse(code: string, options: any): ESLintCustomParserResult
59-
parseForESLint?(code: string, options: any): ESLintCustomParserResult
60-
}
61-
6254
/**
6355
* Do post-process of parsing an expression.
6456
*
@@ -548,11 +540,6 @@ export interface ExpressionParseResult<T extends Node> {
548540
variables: Variable[]
549541
}
550542

551-
/**
552-
* The interface of a result of ESLint custom parser.
553-
*/
554-
export type ESLintCustomParserResult = ESLintProgram | ESLintExtendedProgram
555-
556543
/**
557544
* Parse the given source code.
558545
*
@@ -568,8 +555,7 @@ export function parseScript(
568555
typeof parserOptions.parser === "string"
569556
? // eslint-disable-next-line @mysticatea/ts/no-require-imports
570557
require(parserOptions.parser)
571-
: // eslint-disable-next-line @mysticatea/ts/no-require-imports
572-
require("espree")
558+
: getEspree()
573559
const result: any =
574560
// eslint-disable-next-line @mysticatea/ts/unbound-method
575561
typeof parser.parseForESLint === "function"

Diff for: test/espree.js

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"use strict"
2+
3+
const path = require("path")
4+
5+
/**
6+
* Spawn a child process to run `childMain()`.
7+
*/
8+
function parentMain() {
9+
const { spawn } = require("child_process")
10+
11+
describe("Loading espree from ESLint", () => {
12+
it("should load espree from the ESLint location.", done => {
13+
spawn(process.execPath, [__filename, "--child"], {
14+
stdio: "inherit",
15+
})
16+
.on("error", done)
17+
.on("exit", code =>
18+
code
19+
? done(new Error(`Exited with non-zero: ${code}`))
20+
: done()
21+
)
22+
})
23+
})
24+
}
25+
26+
/**
27+
* Check this parser loads the `espree` from the location of the loaded ESLint.
28+
*/
29+
function childMain() {
30+
const assert = require("assert")
31+
const { Linter } = require("./fixtures/eslint")
32+
const linter = new Linter()
33+
linter.defineParser("vue-eslint-parser", require("../src"))
34+
35+
const beforeEsprees = Object.keys(require.cache).filter(isEspreePath)
36+
37+
linter.verify(
38+
"<script>'hello'</script>",
39+
{ parser: "vue-eslint-parser" },
40+
{ filename: "a.vue" }
41+
)
42+
43+
const afterEsprees = Object.keys(require.cache).filter(isEspreePath)
44+
45+
assert.strictEqual(
46+
afterEsprees.length,
47+
beforeEsprees.length,
48+
"espree should be loaded from the expected place"
49+
)
50+
}
51+
52+
function isEspreePath(p) {
53+
return p.includes(`${path.sep}node_modules${path.sep}espree${path.sep}`)
54+
}
55+
56+
if (process.argv.includes("--child")) {
57+
childMain()
58+
} else {
59+
parentMain()
60+
}

0 commit comments

Comments
 (0)