Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(scope-manager): ignore ECMA version (#5881)
* feat(scope-manager): ignore ECMA version

* Remove much more

* Fix WebLinter lint
  • Loading branch information
JoshuaKGoldberg committed Oct 26, 2022
1 parent 2019c2f commit 3b8d449
Show file tree
Hide file tree
Showing 11 changed files with 18 additions and 151 deletions.
1 change: 0 additions & 1 deletion packages/parser/src/parser.ts
Expand Up @@ -105,7 +105,6 @@ function parseForESLint(
jsx: validateBoolean(options.ecmaFeatures.jsx),
});
const analyzeOptions: AnalyzeOptions = {
ecmaVersion: options.ecmaVersion === 'latest' ? 1e8 : options.ecmaVersion,
globalReturn: options.ecmaFeatures.globalReturn,
jsxPragma: options.jsxPragma,
jsxFragmentName: options.jsxFragmentName,
Expand Down
8 changes: 0 additions & 8 deletions packages/parser/tests/lib/parser.ts
Expand Up @@ -19,11 +19,6 @@ describe('parser', () => {
expect(() => parseForESLint(code, null)).not.toThrow();
});

it("parseForESLint() should work if options.ecmaVersion is `'latest'`", () => {
const code = 'const valid = true;';
expect(() => parseForESLint(code, { ecmaVersion: 'latest' })).not.toThrow();
});

it('parseAndGenerateServices() should be called with options', () => {
const code = 'const valid = true;';
const spy = jest.spyOn(typescriptESTree, 'parseAndGenerateServices');
Expand All @@ -33,7 +28,6 @@ describe('parser', () => {
range: false,
tokens: false,
sourceType: 'module' as const,
ecmaVersion: 2018,
ecmaFeatures: {
globalReturn: false,
jsx: false,
Expand Down Expand Up @@ -84,7 +78,6 @@ describe('parser', () => {
range: false,
tokens: false,
sourceType: 'module' as const,
ecmaVersion: 2018,
ecmaFeatures: {
globalReturn: false,
jsx: false,
Expand All @@ -104,7 +97,6 @@ describe('parser', () => {
parseForESLint(code, config);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenLastCalledWith(expect.anything(), {
ecmaVersion: 2018,
globalReturn: false,
lib: ['dom.iterable'],
jsxPragma: 'Foo',
Expand Down
12 changes: 2 additions & 10 deletions packages/scope-manager/README.md
Expand Up @@ -36,13 +36,6 @@ interface AnalyzeOptions {
*/
childVisitorKeys?: Record<string, string[]> | null;

/**
* Which ECMAScript version is considered.
* Defaults to `2018`.
* `'latest'` is converted to 1e8 at parser.
*/
ecmaVersion?: EcmaVersion | 1e8;

/**
* Whether the whole script is executed under node.js environment.
* When enabled, the scope manager adds a function scope immediately following the global scope.
Expand All @@ -51,7 +44,7 @@ interface AnalyzeOptions {
globalReturn?: boolean;

/**
* Implied strict mode (if ecmaVersion >= 5).
* Implied strict mode.
* Defaults to `false`.
*/
impliedStrict?: boolean;
Expand All @@ -76,7 +69,7 @@ interface AnalyzeOptions {
* This automatically defines a type variable for any types provided by the configured TS libs.
* For more information, see https://www.typescriptlang.org/tsconfig#lib
*
* Defaults to the lib for the provided `ecmaVersion`.
* Defaults to ['esnext'].
*/
lib?: Lib[];

Expand Down Expand Up @@ -105,7 +98,6 @@ const ast = parse(code, {
range: true,
});
const scope = analyze(ast, {
ecmaVersion: 2020,
sourceType: 'module',
});
```
Expand Down
9 changes: 6 additions & 3 deletions packages/scope-manager/src/ScopeManager.ts
Expand Up @@ -28,9 +28,11 @@ interface ScopeManagerOptions {
globalReturn?: boolean;
sourceType?: 'module' | 'script';
impliedStrict?: boolean;
ecmaVersion?: number;
}

/**
* @see https://eslint.org/docs/latest/developer-guide/scope-manager-interface#scopemanager-interface
*/
class ScopeManager {
public currentScope: Scope | null;
public readonly declaredVariables: WeakMap<TSESTree.Node, Variable[]>;
Expand Down Expand Up @@ -77,12 +79,13 @@ class ScopeManager {
public isImpliedStrict(): boolean {
return this.#options.impliedStrict === true;
}

public isStrictModeSupported(): boolean {
return this.#options.ecmaVersion != null && this.#options.ecmaVersion >= 5;
return true;
}

public isES6(): boolean {
return this.#options.ecmaVersion != null && this.#options.ecmaVersion >= 6;
return true;
}

/**
Expand Down
35 changes: 4 additions & 31 deletions packages/scope-manager/src/analyze.ts
@@ -1,7 +1,6 @@
import type { EcmaVersion, Lib, TSESTree } from '@typescript-eslint/types';
import type { Lib, TSESTree } from '@typescript-eslint/types';
import { visitorKeys } from '@typescript-eslint/visitor-keys';

import { lib as TSLibraries } from './lib';
import type { ReferencerOptions } from './referencer';
import { Referencer } from './referencer';
import { ScopeManager } from './ScopeManager';
Expand All @@ -16,13 +15,6 @@ interface AnalyzeOptions {
*/
childVisitorKeys?: ReferencerOptions['childVisitorKeys'];

/**
* Which ECMAScript version is considered.
* Defaults to `2018`.
* `'latest'` is converted to 1e8 at parser.
*/
ecmaVersion?: EcmaVersion | 1e8;

/**
* Whether the whole script is executed under node.js environment.
* When enabled, the scope manager adds a function scope immediately following the global scope.
Expand All @@ -31,7 +23,7 @@ interface AnalyzeOptions {
globalReturn?: boolean;

/**
* Implied strict mode (if ecmaVersion >= 5).
* Implied strict mode.
* Defaults to `false`.
*/
impliedStrict?: boolean;
Expand All @@ -54,7 +46,7 @@ interface AnalyzeOptions {
/**
* The lib used by the project.
* This automatically defines a type variable for any types provided by the configured TS libs.
* Defaults to the lib for the provided `ecmaVersion`.
* Defaults to ['esnext'].
*
* https://www.typescriptlang.org/tsconfig#lib
*/
Expand All @@ -74,7 +66,6 @@ interface AnalyzeOptions {

const DEFAULT_OPTIONS: Required<AnalyzeOptions> = {
childVisitorKeys: visitorKeys,
ecmaVersion: 2018,
globalReturn: false,
impliedStrict: false,
jsxPragma: 'React',
Expand All @@ -84,34 +75,16 @@ const DEFAULT_OPTIONS: Required<AnalyzeOptions> = {
emitDecoratorMetadata: false,
};

/**
* Convert ecmaVersion to lib.
* `'latest'` is converted to 1e8 at parser.
*/
function mapEcmaVersion(version: EcmaVersion | 1e8 | undefined): Lib {
if (version == null || version === 3 || version === 5) {
return 'es5';
}

const year = version > 2000 ? version : 2015 + (version - 6);
const lib = `es${year}`;

return lib in TSLibraries ? (lib as Lib) : year > 2020 ? 'esnext' : 'es5';
}

/**
* Takes an AST and returns the analyzed scopes.
*/
function analyze(
tree: TSESTree.Node,
providedOptions?: AnalyzeOptions,
): ScopeManager {
const ecmaVersion =
providedOptions?.ecmaVersion ?? DEFAULT_OPTIONS.ecmaVersion;
const options: Required<AnalyzeOptions> = {
childVisitorKeys:
providedOptions?.childVisitorKeys ?? DEFAULT_OPTIONS.childVisitorKeys,
ecmaVersion,
globalReturn: providedOptions?.globalReturn ?? DEFAULT_OPTIONS.globalReturn,
impliedStrict:
providedOptions?.impliedStrict ?? DEFAULT_OPTIONS.impliedStrict,
Expand All @@ -122,7 +95,7 @@ function analyze(
jsxFragmentName:
providedOptions?.jsxFragmentName ?? DEFAULT_OPTIONS.jsxFragmentName,
sourceType: providedOptions?.sourceType ?? DEFAULT_OPTIONS.sourceType,
lib: providedOptions?.lib ?? [mapEcmaVersion(ecmaVersion)],
lib: providedOptions?.lib ?? ['esnext'],
emitDecoratorMetadata:
providedOptions?.emitDecoratorMetadata ??
DEFAULT_OPTIONS.emitDecoratorMetadata,
Expand Down
17 changes: 5 additions & 12 deletions packages/scope-manager/src/referencer/Referencer.ts
Expand Up @@ -371,9 +371,7 @@ class Referencer extends Visitor {
}

protected BlockStatement(node: TSESTree.BlockStatement): void {
if (this.scopeManager.isES6()) {
this.scopeManager.nestBlockScope(node);
}
this.scopeManager.nestBlockScope(node);

this.visitChildren(node);

Expand Down Expand Up @@ -487,7 +485,7 @@ class Referencer extends Visitor {

protected ImportDeclaration(node: TSESTree.ImportDeclaration): void {
assert(
this.scopeManager.isES6() && this.scopeManager.isModule(),
this.scopeManager.isModule(),
'ImportDeclaration should appear when the mode is ES6 and in the module context.',
);

Expand Down Expand Up @@ -579,14 +577,11 @@ class Referencer extends Visitor {
this.scopeManager.nestFunctionScope(node, false);
}

if (this.scopeManager.isES6() && this.scopeManager.isModule()) {
if (this.scopeManager.isModule()) {
this.scopeManager.nestModuleScope(node);
}

if (
this.scopeManager.isStrictModeSupported() &&
this.scopeManager.isImpliedStrict()
) {
if (this.scopeManager.isImpliedStrict()) {
this.currentScope().isStrict = true;
}

Expand All @@ -601,9 +596,7 @@ class Referencer extends Visitor {
protected SwitchStatement(node: TSESTree.SwitchStatement): void {
this.visit(node.discriminant);

if (this.scopeManager.isES6()) {
this.scopeManager.nestSwitchScope(node);
}
this.scopeManager.nestSwitchScope(node);

for (const switchCase of node.cases) {
this.visit(switchCase);
Expand Down
Expand Up @@ -12,7 +12,6 @@ describe('ScopeManager.prototype.getDeclaredVariables', () => {
expectedNamesList: string[][],
): void {
const scopeManager = analyze(ast, {
ecmaVersion: 6,
sourceType: 'module',
});

Expand Down
30 changes: 1 addition & 29 deletions packages/scope-manager/tests/eslint-scope/implied-strict.test.ts
Expand Up @@ -8,7 +8,7 @@ import {
} from '../util';

describe('impliedStrict option', () => {
it('ensures all user scopes are strict if ecmaVersion >= 5', () => {
it('ensures all user scopes are strict', () => {
const { scopeManager } = parseAndAnalyze(
`
function foo() {
Expand All @@ -18,7 +18,6 @@ describe('impliedStrict option', () => {
}
`,
{
ecmaVersion: 5,
impliedStrict: true,
},
);
Expand All @@ -42,38 +41,12 @@ describe('impliedStrict option', () => {
expect(scope.isStrict).toBeTruthy();
});

it('ensures impliedStrict option is only effective when ecmaVersion option >= 5', () => {
const { scopeManager } = parseAndAnalyze(
`
function foo() {}
`,
{
ecmaVersion: 3,
impliedStrict: true,
},
);

expect(scopeManager.scopes).toHaveLength(2);

let scope = scopeManager.scopes[0];

expectToBeGlobalScope(scope);
expect(scope.block.type).toBe(AST_NODE_TYPES.Program);
expect(scope.isStrict).toBeFalsy();

scope = scopeManager.scopes[1];
expectToBeFunctionScope(scope);
expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionDeclaration);
expect(scope.isStrict).toBeFalsy();
});

it('omits a nodejs global scope when ensuring all user scopes are strict', () => {
const { scopeManager } = parseAndAnalyze(
`
function foo() {}
`,
{
ecmaVersion: 5,
globalReturn: true,
impliedStrict: true,
},
Expand All @@ -100,7 +73,6 @@ describe('impliedStrict option', () => {

it('omits a module global scope when ensuring all user scopes are strict', () => {
const { scopeManager } = parseAndAnalyze('function foo() {}', {
ecmaVersion: 6,
impliedStrict: true,
sourceType: 'module',
});
Expand Down
51 changes: 0 additions & 51 deletions packages/scope-manager/tests/eslint-scope/map-ecma-version.test.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/scope-manager/tests/fixtures.test.ts
Expand Up @@ -42,7 +42,6 @@ const ALLOWED_OPTIONS: Map<string, ALLOWED_VALUE> = new Map<
keyof AnalyzeOptions,
ALLOWED_VALUE
>([
['ecmaVersion', ['number']],
['globalReturn', ['boolean']],
['impliedStrict', ['boolean']],
['jsxPragma', ['string']],
Expand Down
4 changes: 0 additions & 4 deletions packages/website/src/components/linter/WebLinter.ts
Expand Up @@ -111,10 +111,6 @@ export class WebLinter {
);

const scopeManager = this.lintUtils.analyze(ast, {
ecmaVersion:
eslintOptions.ecmaVersion === 'latest'
? 1e8
: eslintOptions.ecmaVersion,
globalReturn: eslintOptions.ecmaFeatures?.globalReturn ?? false,
sourceType: eslintOptions.sourceType ?? 'script',
});
Expand Down

0 comments on commit 3b8d449

Please sign in to comment.