@@ -2,7 +2,7 @@ import type { ParserOptions } from "../common/parser-options"
2
2
import { getLinterRequire } from "./linter-require"
3
3
// @ts -expect-error -- ignore
4
4
import * as dependencyEspree from "espree"
5
- import { lte , lt } from "semver"
5
+ import { lte , satisfies } from "semver"
6
6
import { createRequire } from "./create-require"
7
7
import path from "path"
8
8
import type { BasicParserObject } from "./parser-object"
@@ -11,73 +11,100 @@ type Espree = BasicParserObject & {
11
11
latestEcmaVersion ?: number
12
12
version : string
13
13
}
14
- let espreeCache : Espree | null = null
15
-
16
- /**
17
- * Gets the espree that the given ecmaVersion can parse.
18
- */
19
- export function getEspreeFromEcmaVersion (
20
- ecmaVersion : ParserOptions [ "ecmaVersion" ] ,
21
- ) : Espree {
22
- const linterEspree = getEspreeFromLinter ( )
23
- if ( ecmaVersion == null ) {
24
- return linterEspree
25
- }
26
- if ( ecmaVersion === "latest" ) {
27
- return getNewestEspree ( )
28
- }
29
- if (
30
- normalizeEcmaVersion ( ecmaVersion ) <= getLatestEcmaVersion ( linterEspree )
31
- ) {
32
- return linterEspree
33
- }
34
- const userEspree = getEspreeFromUser ( )
35
- if ( normalizeEcmaVersion ( ecmaVersion ) <= getLatestEcmaVersion ( userEspree ) ) {
36
- return userEspree
37
- }
38
- return linterEspree
39
- }
40
14
41
15
/**
42
16
* Load `espree` from the user dir.
43
17
*/
44
- export function getEspreeFromUser ( ) : Espree {
18
+ function getEspreeFromUser ( ) : Espree {
45
19
try {
46
20
const cwd = process . cwd ( )
47
21
const relativeTo = path . join ( cwd , "__placeholder__.js" )
48
- return createRequire ( relativeTo ) ( "espree" )
22
+ const require = createRequire ( relativeTo )
23
+ const espree = getEspreeFromRequireFunction ( require )
24
+ if ( espree ) {
25
+ if ( espree !== dependencyEspree ) {
26
+ return espree
27
+ }
28
+ // If the user's espree is the same as the parser package's dependency espree,
29
+ // it checks whether the user has explicitly installed it.
30
+ if ( isExplicitlyInstalledEspree ( require as NodeRequire ) ) {
31
+ return espree
32
+ }
33
+ }
49
34
} catch {
50
- return getEspreeFromLinter ( )
35
+ // ignore
36
+ }
37
+ return getEspreeFromLinter ( )
38
+
39
+ function isExplicitlyInstalledEspree ( require : NodeRequire ) : boolean {
40
+ try {
41
+ const espreeRootPath = path . dirname (
42
+ require . resolve ( "espree/package.json" ) ,
43
+ )
44
+ const nodeModulesPath = path . dirname ( espreeRootPath )
45
+ const packageRootPath = path . dirname ( nodeModulesPath )
46
+ let pkg
47
+ try {
48
+ pkg = require ( path . join ( packageRootPath , "package.json" ) )
49
+ } catch {
50
+ // ignore
51
+ }
52
+ if ( pkg ) {
53
+ return Boolean (
54
+ pkg . dependencies ?. espree || pkg . devDependencies ?. espree ,
55
+ )
56
+ }
57
+ } catch {
58
+ // ignore
59
+ }
60
+ // If no package.json is found,
61
+ // it is assumed to have been explicitly installed by the user.
62
+ return true
51
63
}
52
64
}
53
65
54
66
/**
55
67
* Load `espree` from the loaded ESLint.
56
68
* If the loaded ESLint was not found, just returns `require("espree")`.
57
69
*/
58
- export function getEspreeFromLinter ( ) : Espree {
59
- if ( ! espreeCache ) {
60
- espreeCache = getLinterRequire ( ) ?.( "espree" )
61
- if ( ! espreeCache ) {
62
- espreeCache = dependencyEspree
70
+ function getEspreeFromLinter ( ) : Espree {
71
+ const require = getLinterRequire ( )
72
+ if ( require ) {
73
+ const espree = getEspreeFromRequireFunction ( require )
74
+ if ( espree ) {
75
+ return espree
63
76
}
64
77
}
78
+ return dependencyEspree
79
+ }
65
80
66
- return espreeCache !
81
+ /**
82
+ * Load `espree` from the given require function.
83
+ */
84
+ function getEspreeFromRequireFunction (
85
+ require : ( name : string ) => any ,
86
+ ) : Espree | null {
87
+ try {
88
+ const pkg = require ( "espree/package.json" )
89
+ const supportNodeVersion = pkg . engines ?. node
90
+ if (
91
+ // If the node version is not supported then espree will not use it.
92
+ ! supportNodeVersion ||
93
+ satisfies ( process . version , supportNodeVersion )
94
+ ) {
95
+ return require ( "espree" )
96
+ }
97
+ } catch {
98
+ // ignore
99
+ }
100
+ return null
67
101
}
68
102
69
103
/**
70
104
* Load the newest `espree` from the loaded ESLint or dependency.
71
105
*/
72
- function getNewestEspree ( ) : Espree {
73
- let newest = dependencyEspree
74
- const linterEspree = getEspreeFromLinter ( )
75
- if (
76
- linterEspree . version != null &&
77
- lte ( newest . version , linterEspree . version )
78
- ) {
79
- newest = linterEspree
80
- }
106
+ export function getNewestEspree ( ) : Espree {
107
+ let newest = getEspreeFromLinter ( )
81
108
const userEspree = getEspreeFromUser ( )
82
109
if ( userEspree . version != null && lte ( newest . version , userEspree . version ) ) {
83
110
newest = userEspree
@@ -87,30 +114,20 @@ function getNewestEspree(): Espree {
87
114
88
115
export function getEcmaVersionIfUseEspree (
89
116
parserOptions : ParserOptions ,
90
- getDefault ?: ( defaultVer : number ) => number ,
91
117
) : number | undefined {
92
118
if ( parserOptions . parser != null && parserOptions . parser !== "espree" ) {
93
119
return undefined
94
120
}
95
121
96
- if ( parserOptions . ecmaVersion === "latest" ) {
122
+ if (
123
+ parserOptions . ecmaVersion === "latest" ||
124
+ parserOptions . ecmaVersion == null
125
+ ) {
97
126
return normalizeEcmaVersion ( getLatestEcmaVersion ( getNewestEspree ( ) ) )
98
127
}
99
- if ( parserOptions . ecmaVersion == null ) {
100
- const defVer = getDefaultEcmaVersion ( )
101
- return getDefault ?.( defVer ) ?? defVer
102
- }
103
128
return normalizeEcmaVersion ( parserOptions . ecmaVersion )
104
129
}
105
130
106
- function getDefaultEcmaVersion ( ) : number {
107
- if ( lt ( getEspreeFromLinter ( ) . version , "9.0.0" ) ) {
108
- return 5
109
- }
110
- // Perhaps the version 9 will change the default to "latest".
111
- return normalizeEcmaVersion ( getLatestEcmaVersion ( getNewestEspree ( ) ) )
112
- }
113
-
114
131
/**
115
132
* Normalize ECMAScript version
116
133
*/
0 commit comments