Skip to content

Improve module resolution method #93

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 79 additions & 15 deletions src/parser/modules/acorn.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import type * as acorn from "acorn"
import { createRequire } from "module"
import { getRequireFromCwd, getRequireFromLinter } from "../require-utils"
import {
getRequireFromCwd,
getRequireFromLinter,
loadNewest,
requireFromCwd,
requireFromLinter,
} from "./require-utils"

let acornCache: typeof acorn | undefined
/**
Expand All @@ -9,20 +15,78 @@ let acornCache: typeof acorn | undefined
*/
export function getAcorn(): typeof acorn {
if (!acornCache) {
try {
const nodeRequire = getRequireFromCwd() || getRequireFromLinter()
if (nodeRequire) {
acornCache = createRequire(nodeRequire.resolve("espree"))(
"acorn",
)
}
} catch {
// ignore
}
if (!acornCache) {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- ignore
acornCache = require("acorn")
}
acornCache = loadNewest([
{
getPkg() {
return requireFromCwd("acorn/package.json")
},
get() {
return requireFromCwd("acorn")
},
},
{
getPkg() {
return requireFromEspree("acorn/package.json")
},
get() {
return requireFromEspree("acorn")
},
},
{
getPkg() {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- special require
return require("acorn/package.json")
},
get() {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- special require
return require("acorn")
},
},
])
}
return acornCache!
}

/**
* Get module from espree
*/
function requireFromEspree<T>(module: string): T | null {
// Lookup the loaded espree
try {
return createRequire(getEspreePath())(module)
} catch {
// ignore
}
return null
}

/** Get espree path */
function getEspreePath(): string {
return loadNewest([
{
getPkg() {
return requireFromCwd("espree/package.json")
},
get() {
return getRequireFromCwd()!.resolve("espree")
},
},
{
getPkg() {
return requireFromLinter("espree/package.json")
},
get() {
return getRequireFromLinter()!.resolve("espree")
},
},
{
getPkg() {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- special require
return require("espree/package.json")
},
get() {
return require.resolve("espree")
},
},
])
}
69 changes: 63 additions & 6 deletions src/parser/modules/espree.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { requireFromCwd, requireFromLinter } from "../require-utils"
import { loadNewest, requireFromCwd, requireFromLinter } from "./require-utils"
import { lte } from "semver"

/**
* The interface of ESLint custom parsers.
Expand All @@ -16,11 +17,67 @@ let espreeCache: ESPree | null = null
*/
export function getEspree(): ESPree {
if (!espreeCache) {
espreeCache =
requireFromCwd("espree") ||
requireFromLinter("espree") ||
// eslint-disable-next-line @typescript-eslint/no-require-imports -- special require
require("espree")
espreeCache = loadNewest([
{
getPkg() {
return requireFromCwd("espree/package.json")
},
get() {
return requireFromCwd("espree")
},
},
{
getPkg() {
return requireFromLinter("espree/package.json")
},
get() {
return requireFromLinter("espree")
},
},
{
getPkg() {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- special require
return require("espree/package.json")
},
get() {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- special require
return require("espree")
},
},
])
}
return espreeCache!
}

type NewestKind = "cwd" | "linter" | "self"

let kindCache: NewestKind | null = null

/**
* Get the newest `espree` kind from the loaded ESLint or dependency.
*/
export function getNewestEspreeKind(): NewestKind {
if (kindCache) {
return kindCache
}
const cwdPkg: { version: string } | null = requireFromCwd(
"espree/package.json",
)
const linterPkg: { version: string } | null = requireFromLinter(
"espree/package.json",
)
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports -- ignore
const self: { version: string } = require("espree/package.json")

let target: { kind: NewestKind; version: string } = {
kind: "self",
version: self.version,
}
if (cwdPkg != null && lte(target.version, cwdPkg.version)) {
target = { kind: "cwd", version: cwdPkg.version }
}
if (linterPkg != null && lte(target.version, linterPkg.version)) {
target = { kind: "linter", version: linterPkg.version }
}
return (kindCache = target.kind)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import path from "path"
import { lte } from "semver"
import type ModuleClass from "module"

/**
Expand Down Expand Up @@ -94,3 +95,19 @@ export function requireFromCwd<T>(module: string): T | null {
}
return null
}

/**
* Get the newest `espree` kind from the loaded ESLint or dependency.
*/
export function loadNewest<T>(
items: { getPkg: () => { version: string } | null; get: () => T | null }[],
): T {
let target: { version: string; get: () => T | null } | null = null
for (const item of items) {
const pkg = item.getPkg()
if (pkg != null && (!target || lte(target.version, pkg.version))) {
target = { version: pkg.version, get: item.get }
}
}
return target!.get()!
}
39 changes: 33 additions & 6 deletions src/parser/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ import {
} from "./errors"
import type { TokenStore, MaybeNodeOrToken } from "./token-store"
import { isComma } from "./token-store"
import { requireFromCwd, requireFromLinter } from "./require-utils"
import { isRegExpLiteral } from "./utils"
import type { JSONIdentifier } from "./ast"
import {
loadNewest,
requireFromCwd,
requireFromLinter,
} from "./modules/require-utils"

const lineBreakPattern = /\r\n|[\n\r\u2028\u2029]/u
const octalNumericLiteralPattern = /^0[Oo]/u
Expand All @@ -34,11 +38,34 @@ let cacheCodePointEscapeMatcher: eslintUtils.PatternMatcher | null
/** Get codePointEscape matcher */
function getCodePointEscapeMatcher(): eslintUtils.PatternMatcher {
if (!cacheCodePointEscapeMatcher) {
const utils: typeof eslintUtils =
requireFromCwd("eslint-utils") ||
requireFromLinter("eslint-utils") ||
// eslint-disable-next-line @typescript-eslint/no-require-imports -- special require
require("eslint-utils")
const utils: typeof eslintUtils = loadNewest([
{
getPkg() {
return requireFromCwd("eslint-utils/package.json")
},
get() {
return requireFromCwd("eslint-utils")
},
},
{
getPkg() {
return requireFromLinter("eslint-utils/package.json")
},
get() {
return requireFromLinter("eslint-utils")
},
},
{
getPkg() {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- special require
return require("eslint-utils/package.json")
},
get() {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- special require
return require("eslint-utils")
},
},
])
cacheCodePointEscapeMatcher = new utils.PatternMatcher(
/\\u\{[\dA-Fa-f]+\}/gu,
)
Expand Down
39 changes: 33 additions & 6 deletions src/parser/visitor-keys.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import type { SourceCode } from "eslint"
import type * as Evk from "eslint-visitor-keys"
import type { JSONNode } from "./ast"
import { requireFromCwd, requireFromLinter } from "./require-utils"
import {
loadNewest,
requireFromCwd,
requireFromLinter,
} from "./modules/require-utils"

const jsonKeys: { [key in JSONNode["type"]]: string[] } = {
Program: ["body"],
Expand All @@ -22,11 +26,34 @@ let cache: SourceCode.VisitorKeys | null = null
*/
export function getVisitorKeys(): SourceCode.VisitorKeys {
if (!cache) {
const vk: typeof Evk =
requireFromCwd("eslint-visitor-keys") ||
requireFromLinter("eslint-visitor-keys") ||
// eslint-disable-next-line @typescript-eslint/no-require-imports -- special require
require("eslint-visitor-keys")
const vk: typeof Evk = loadNewest([
{
getPkg() {
return requireFromCwd("eslint-visitor-keys/package.json")
},
get() {
return requireFromCwd("eslint-visitor-keys")
},
},
{
getPkg() {
return requireFromLinter("eslint-visitor-keys/package.json")
},
get() {
return requireFromLinter("eslint-visitor-keys")
},
},
{
getPkg() {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- special require
return require("eslint-visitor-keys/package.json")
},
get() {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- special require
return require("eslint-visitor-keys")
},
},
])

cache = vk.unionWith(jsonKeys) as SourceCode.VisitorKeys
}
Expand Down