| 
 | 1 | +import type { ESLintExtendedProgram, ESLintProgram } from "../ast"  | 
 | 2 | +import type { ParserOptions } from "../common/parser-options"  | 
 | 3 | +import { getLinterRequire } from "./linter-require"  | 
 | 4 | +// @ts-expect-error -- ignore  | 
 | 5 | +import * as espree from "espree"  | 
 | 6 | +import { lte, lt } from "semver"  | 
 | 7 | + | 
 | 8 | +/**  | 
 | 9 | + * The interface of a result of ESLint custom parser.  | 
 | 10 | + */  | 
 | 11 | +export type ESLintCustomParserResult = ESLintProgram | ESLintExtendedProgram  | 
 | 12 | + | 
 | 13 | +/**  | 
 | 14 | + * The interface of ESLint custom parsers.  | 
 | 15 | + */  | 
 | 16 | +export interface ESLintCustomParser {  | 
 | 17 | +    parse(code: string, options: any): ESLintCustomParserResult  | 
 | 18 | +    parseForESLint?(code: string, options: any): ESLintCustomParserResult  | 
 | 19 | +}  | 
 | 20 | +type OldEspree = ESLintCustomParser & {  | 
 | 21 | +    latestEcmaVersion?: number  | 
 | 22 | +    version: string  | 
 | 23 | +}  | 
 | 24 | +type Espree = ESLintCustomParser & {  | 
 | 25 | +    latestEcmaVersion: number  | 
 | 26 | +    version: string  | 
 | 27 | +}  | 
 | 28 | +let espreeCache: OldEspree | Espree | null = null  | 
 | 29 | + | 
 | 30 | +/**  | 
 | 31 | + * Gets the espree that the given ecmaVersion can parse.  | 
 | 32 | + */  | 
 | 33 | +export function getEspreeFromEcmaVersion(  | 
 | 34 | +    ecmaVersion: ParserOptions["ecmaVersion"],  | 
 | 35 | +): OldEspree | Espree {  | 
 | 36 | +    const linterEspree = getEspreeFromLinter()  | 
 | 37 | +    if (  | 
 | 38 | +        linterEspree.version != null &&  | 
 | 39 | +        lte(espree.version, linterEspree.version)  | 
 | 40 | +    ) {  | 
 | 41 | +        // linterEspree is newest  | 
 | 42 | +        return linterEspree  | 
 | 43 | +    }  | 
 | 44 | +    if (ecmaVersion == null) {  | 
 | 45 | +        return linterEspree  | 
 | 46 | +    }  | 
 | 47 | +    if (ecmaVersion === "latest") {  | 
 | 48 | +        return espree  | 
 | 49 | +    }  | 
 | 50 | +    if (normalizeEcmaVersion(ecmaVersion) <= getLinterLatestEcmaVersion()) {  | 
 | 51 | +        return linterEspree  | 
 | 52 | +    }  | 
 | 53 | +    return espree  | 
 | 54 | + | 
 | 55 | +    function getLinterLatestEcmaVersion() {  | 
 | 56 | +        if (linterEspree.latestEcmaVersion == null) {  | 
 | 57 | +            for (const { v, latest } of [  | 
 | 58 | +                { v: "6.1.0", latest: 2020 },  | 
 | 59 | +                { v: "4.0.0", latest: 2019 },  | 
 | 60 | +            ]) {  | 
 | 61 | +                if (lte(v, linterEspree.version)) {  | 
 | 62 | +                    return latest  | 
 | 63 | +                }  | 
 | 64 | +            }  | 
 | 65 | +            return 2018  | 
 | 66 | +        }  | 
 | 67 | +        return normalizeEcmaVersion(linterEspree.latestEcmaVersion)  | 
 | 68 | +    }  | 
 | 69 | +}  | 
 | 70 | + | 
 | 71 | +/**  | 
 | 72 | + * Load `espree` from the loaded ESLint.  | 
 | 73 | + * If the loaded ESLint was not found, just returns `require("espree")`.  | 
 | 74 | + */  | 
 | 75 | +export function getEspreeFromLinter(): Espree | OldEspree {  | 
 | 76 | +    if (!espreeCache) {  | 
 | 77 | +        espreeCache = getLinterRequire()?.("espree")  | 
 | 78 | +        if (!espreeCache) {  | 
 | 79 | +            espreeCache = espree  | 
 | 80 | +        }  | 
 | 81 | +    }  | 
 | 82 | + | 
 | 83 | +    return espreeCache!  | 
 | 84 | +}  | 
 | 85 | + | 
 | 86 | +/**  | 
 | 87 | + * Load the newest `espree` from the loaded ESLint or dependency.  | 
 | 88 | + */  | 
 | 89 | +function getNewestEspree(): Espree {  | 
 | 90 | +    const linterEspree = getEspreeFromLinter()  | 
 | 91 | +    if (  | 
 | 92 | +        linterEspree.version == null ||  | 
 | 93 | +        lte(linterEspree.version, espree.version)  | 
 | 94 | +    ) {  | 
 | 95 | +        return espree  | 
 | 96 | +    }  | 
 | 97 | +    return linterEspree as Espree  | 
 | 98 | +}  | 
 | 99 | + | 
 | 100 | +export function getEcmaVersionIfUseEspree(  | 
 | 101 | +    parserOptions: ParserOptions,  | 
 | 102 | +    getDefault?: (defaultVer: number) => number,  | 
 | 103 | +): number | undefined {  | 
 | 104 | +    if (parserOptions.parser != null && parserOptions.parser !== "espree") {  | 
 | 105 | +        return undefined  | 
 | 106 | +    }  | 
 | 107 | + | 
 | 108 | +    if (parserOptions.ecmaVersion === "latest") {  | 
 | 109 | +        return normalizeEcmaVersion(getNewestEspree().latestEcmaVersion)  | 
 | 110 | +    }  | 
 | 111 | +    if (parserOptions.ecmaVersion == null) {  | 
 | 112 | +        const defVer = getDefaultEcmaVersion()  | 
 | 113 | +        return getDefault?.(defVer) ?? defVer  | 
 | 114 | +    }  | 
 | 115 | +    return normalizeEcmaVersion(parserOptions.ecmaVersion)  | 
 | 116 | +}  | 
 | 117 | + | 
 | 118 | +function getDefaultEcmaVersion(): number {  | 
 | 119 | +    if (lt(getEspreeFromLinter().version, "9.0.0")) {  | 
 | 120 | +        return 5  | 
 | 121 | +    }  | 
 | 122 | +    // Perhaps the version 9 will change the default to "latest".  | 
 | 123 | +    return normalizeEcmaVersion(getNewestEspree().latestEcmaVersion)  | 
 | 124 | +}  | 
 | 125 | + | 
 | 126 | +/**  | 
 | 127 | + * Normalize ECMAScript version  | 
 | 128 | + */  | 
 | 129 | +function normalizeEcmaVersion(version: number) {  | 
 | 130 | +    if (version > 5 && version < 2015) {  | 
 | 131 | +        return version + 2009  | 
 | 132 | +    }  | 
 | 133 | +    return version  | 
 | 134 | +}  | 
0 commit comments