Skip to content

Commit a723c4d

Browse files
committed
refactor(compat-eslint): hoist all lib requires to top-of-file imports
The lazy `require` pattern inside helper functions was a speculative optimization — a top-level `import` resolves at module init, so for IDE / CLI scenarios where the user lints at all, every lib eventually loads anyway. The lazy form just defers the cost from "module init" to "first lint", which is ~5-30 ms once-ever in practice — not user-visible amortized over a session. The cost was readability: dependencies hidden inside function bodies, the same `require(...) as typeof import(...)` boilerplate repeated 8 times, and call-site noise. Hoist them all (lazy-estree / lazy-source-code / selector-analysis / tokens / ts-ast-scan / ts-scope-manager / visitor-keys, plus CodePathAnalyzer via `import = require` for its `export =` shape). Top of the file now declares the dep graph; every helper just uses the imports. All four test suites still green.
1 parent d9eb45c commit a723c4d

1 file changed

Lines changed: 8 additions & 19 deletions

File tree

packages/compat-eslint/index.ts

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import type * as TSSLint from '@tsslint/types';
22
import type * as ESLint from 'eslint';
33
import type * as ts from 'typescript';
4+
import CodePathAnalyzer = require('./lib/code-path-analysis/code-path-analyzer.js');
5+
import { convertLazy } from './lib/lazy-estree';
6+
import { LazySourceCode } from './lib/lazy-source-code';
7+
import { decomposeSimple, isCodePathListener } from './lib/selector-analysis';
8+
import { convertComments, convertTokens } from './lib/tokens';
9+
import { tsScanTraverse } from './lib/ts-ast-scan';
10+
import { applyEslintGlobals, TsScopeManager } from './lib/ts-scope-manager';
11+
import { visitorKeys } from './lib/visitor-keys';
412

513
// Build a parse-time-named wrapper around a rule listener. V8's CPU
614
// profile reads the SharedFunctionInfo's parse-time-inferred name, so we
@@ -341,7 +349,6 @@ function runSharedTraversal(
341349
}) as unknown as ESLint.Rule.RuleContext;
342350
const ruleListeners = eslintRule.create(ruleContext);
343351

344-
const { isCodePathListener } = require('./lib/selector-analysis') as typeof import('./lib/selector-analysis');
345352
for (const selector in ruleListeners) {
346353
const listener = ruleListeners[selector];
347354
if (!listener) continue;
@@ -424,9 +431,6 @@ interface FastDispatch {
424431
function buildFastDispatch(
425432
allListeners: Array<[ESLint.Rule.RuleModule, string, (n: unknown) => void]>,
426433
): FastDispatch {
427-
const { decomposeSimple, isCodePathListener } = require(
428-
'./lib/selector-analysis',
429-
) as typeof import('./lib/selector-analysis');
430434
const enter = new Map<string, DispatchEntry[]>();
431435
const exit = new Map<string, DispatchEntry[]>();
432436
const enterAll: DispatchEntry[] = [];
@@ -509,7 +513,6 @@ function runTsScanInline(
509513
onTarget: (target: unknown) => void,
510514
convertContext: unknown,
511515
): void {
512-
const { tsScanTraverse } = require('./lib/ts-ast-scan') as typeof import('./lib/ts-ast-scan');
513516
const match = buildScanPredicate(fast);
514517
tsScanTraverse(file, match, convertContext as any, {
515518
enterNode(target) {
@@ -534,7 +537,6 @@ function runCpaInline(
534537
onTarget: (target: unknown) => void,
535538
convertContext: unknown,
536539
): void {
537-
const { tsScanTraverse } = require('./lib/ts-ast-scan') as typeof import('./lib/ts-ast-scan');
538540
const match = buildScanPredicate(fast);
539541
// CPA emits `onCodePathStart` / `onCodePathSegment*` / etc. Dispatch
540542
// directly to the per-event listener arrays we collected up front.
@@ -565,11 +567,6 @@ function runCpaInline(
565567
enterNode: (target: unknown) => dispatchTarget(target, true, fast, errors, onTarget),
566568
leaveNode: (target: unknown) => dispatchTarget(target, false, fast, errors, onTarget),
567569
};
568-
// CodePathAnalyzer is vendored from ESLint's `lib/linter/code-path-analysis/`
569-
// into our `lib/code-path-analysis/` (ESLint exposes no public surface for
570-
// it). Node `require` is already lazy + cached, so this loads on first
571-
// CPA-mode lint and is reused thereafter — no module-level memo needed.
572-
const CodePathAnalyzer = require('./lib/code-path-analysis/code-path-analyzer.js') as typeof import('./lib/code-path-analysis/code-path-analyzer');
573570
const cpa = new CodePathAnalyzer(wrapped);
574571
tsScanTraverse(file, match, convertContext as any, {
575572
enterNode(target) {
@@ -784,13 +781,6 @@ function buildEstree(file: ts.SourceFile, program: ts.Program): {
784781
sourceCode: ESLint.SourceCode;
785782
convertContext: unknown;
786783
} {
787-
const { visitorKeys } = require('./lib/visitor-keys') as typeof import('./lib/visitor-keys');
788-
const { TsScopeManager, applyEslintGlobals } = require(
789-
'./lib/ts-scope-manager',
790-
) as typeof import('./lib/ts-scope-manager');
791-
const { convertLazy } = require('./lib/lazy-estree') as typeof import('./lib/lazy-estree');
792-
const { LazySourceCode } = require('./lib/lazy-source-code') as typeof import('./lib/lazy-source-code');
793-
794784
// Lazy ESTree shim (lib/lazy-estree.ts). Byte-identical to
795785
// typescript-estree's eager Converter on every TS file under
796786
// packages/, but materialises children on first read. Rules see
@@ -808,7 +798,6 @@ function buildEstree(file: ts.SourceFile, program: ts.Program): {
808798
// `sourceCode.getTokenAfter()` and need the tokens array — but
809799
// most rules never touch tokens/comments. Defer the scan via lazy
810800
// getters: cheap when no rule reads, ~80ms saved on large files.
811-
const { convertTokens, convertComments } = require('./lib/tokens') as typeof import('./lib/tokens');
812801
let _tokens: unknown[] | undefined;
813802
let _comments: unknown[] | undefined;
814803
Object.defineProperty(estree, 'tokens', {

0 commit comments

Comments
 (0)