pax_global_header 0000666 0000000 0000000 00000000064 13671663521 0014524 g ustar 00root root 0000000 0000000 52 comment=23709f84c8e21f1a38f61ad689c82a8763e0737f
eslint-utils-2.1.0/ 0000775 0000000 0000000 00000000000 13671663521 0014160 5 ustar 00root root 0000000 0000000 eslint-utils-2.1.0/.codecov.yml 0000664 0000000 0000000 00000000015 13671663521 0016377 0 ustar 00root root 0000000 0000000 comment: off
eslint-utils-2.1.0/.eslintignore 0000664 0000000 0000000 00000000127 13671663521 0016663 0 ustar 00root root 0000000 0000000 /.nyc_output
/coverage
/node_modules
/index.*
/test.*
!.vuepress
/docs/.vuepress/dist
eslint-utils-2.1.0/.eslintrc.yml 0000664 0000000 0000000 00000000423 13671663521 0016603 0 ustar 00root root 0000000 0000000 root: true
extends:
- plugin:@mysticatea/es2015
overrides:
- files:
- src/**/*.js
- test/**/*.js
extends: plugin:@mysticatea/+modules
rules:
init-declarations: "off"
"@mysticatea/node/no-unsupported-features/es-syntax":
- error
- ignores: [modules]
eslint-utils-2.1.0/.esmrc 0000664 0000000 0000000 00000000100 13671663521 0015261 0 ustar 00root root 0000000 0000000 {
"cache": true,
"debug": true,
"sourceMap": true
}
eslint-utils-2.1.0/.gitattributes 0000664 0000000 0000000 00000000023 13671663521 0017046 0 ustar 00root root 0000000 0000000 * text=auto eol=lf
eslint-utils-2.1.0/.github/ 0000775 0000000 0000000 00000000000 13671663521 0015520 5 ustar 00root root 0000000 0000000 eslint-utils-2.1.0/.github/FUNDING.yml 0000664 0000000 0000000 00000000025 13671663521 0017332 0 ustar 00root root 0000000 0000000 github:
- mysticatea
eslint-utils-2.1.0/.github/workflows/ 0000775 0000000 0000000 00000000000 13671663521 0017555 5 ustar 00root root 0000000 0000000 eslint-utils-2.1.0/.github/workflows/CI.yml 0000664 0000000 0000000 00000003024 13671663521 0020572 0 ustar 00root root 0000000 0000000 name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]
schedule:
- cron: 0 0 * * 0
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Install Node.js
uses: actions/setup-node@v1
with:
node-version: 12
- name: Install Packages
run: npm install
- name: Lint
run: npm run -s lint
test:
name: Test
strategy:
matrix:
node: [12, 10, 8, 6]
eslint: [6, 5]
exclude:
# ESLint 6 doesn't support Node 6.
- node: 6
eslint: 6
# Run ESLint 5 on only the newest and oldest Node.
- node: 10
eslint: 5
- node: 8
eslint: 5
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Install Node.js v${{ matrix.node }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- name: Install Packages
run: |
if [ ${{ matrix.node }} -eq 6 ]; then
npm install --global npm@^6.0.0
fi
npm install
- name: Install ESLint v${{ matrix.eslint }}
run: npm install --no-save eslint@^${{ matrix.eslint }}.0.0
- name: Build
run: npm run -s build
- name: Test
run: npm run -s test:mocha
- name: Send Coverage
run: npm run -s codecov
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
eslint-utils-2.1.0/.gitignore 0000664 0000000 0000000 00000000113 13671663521 0016143 0 ustar 00root root 0000000 0000000 /.nyc_output
/coverage
/docs/.vuepress/dist
/node_modules
/index.*
/test.*
eslint-utils-2.1.0/.npmrc 0000664 0000000 0000000 00000000025 13671663521 0015275 0 ustar 00root root 0000000 0000000 package-lock = false
eslint-utils-2.1.0/.nycrc 0000664 0000000 0000000 00000000145 13671663521 0015277 0 ustar 00root root 0000000 0000000 {
"include": ["src/**/*.js"],
"reporter": ["lcov", "text-summary"],
"require": ["esm"]
}
eslint-utils-2.1.0/.vscode/ 0000775 0000000 0000000 00000000000 13671663521 0015521 5 ustar 00root root 0000000 0000000 eslint-utils-2.1.0/.vscode/extensions.json 0000664 0000000 0000000 00000000104 13671663521 0020606 0 ustar 00root root 0000000 0000000 {
"recommendations": [
"dbaeumer.vscode-eslint"
]
}
eslint-utils-2.1.0/.vscode/settings.json 0000664 0000000 0000000 00000000276 13671663521 0020261 0 ustar 00root root 0000000 0000000 {
"[javascript]": {
"editor.formatOnSave": false,
"editor.formatOnType": false,
"editor.codeActionsOnSave": {
"source.fixAll": true
}
}
}
eslint-utils-2.1.0/LICENSE 0000664 0000000 0000000 00000002057 13671663521 0015171 0 ustar 00root root 0000000 0000000 MIT License
Copyright (c) 2018 Toru Nagashima
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
eslint-utils-2.1.0/README.md 0000664 0000000 0000000 00000003071 13671663521 0015440 0 ustar 00root root 0000000 0000000 # eslint-utils
[](https://www.npmjs.com/package/eslint-utils)
[](http://www.npmtrends.com/eslint-utils)
[](https://github.com/mysticatea/eslint-utils/actions)
[](https://codecov.io/gh/mysticatea/eslint-utils)
[](https://david-dm.org/mysticatea/eslint-utils)
## 🏁 Goal
This package provides utility functions and classes for make ESLint custom rules.
For examples:
- [getStaticValue](https://eslint-utils.mysticatea.dev/api/ast-utils.html#getstaticvalue) evaluates static value on AST.
- [ReferenceTracker](https://eslint-utils.mysticatea.dev/api/scope-utils.html#referencetracker-class) checks the members of modules/globals as handling assignments and destructuring.
## 📖 Usage
See [documentation](https://eslint-utils.mysticatea.dev/).
## 📰 Changelog
See [releases](https://github.com/mysticatea/eslint-utils/releases).
## ❤️ Contributing
Welcome contributing!
Please use GitHub's Issues/PRs.
### Development Tools
- `npm test` runs tests and measures coverage.
- `npm run clean` removes the coverage result of `npm test` command.
- `npm run coverage` shows the coverage result of the last `npm test` command.
- `npm run lint` runs ESLint.
- `npm run watch` runs tests on each file change.
eslint-utils-2.1.0/docs/ 0000775 0000000 0000000 00000000000 13671663521 0015110 5 ustar 00root root 0000000 0000000 eslint-utils-2.1.0/docs/.vuepress/ 0000775 0000000 0000000 00000000000 13671663521 0017042 5 ustar 00root root 0000000 0000000 eslint-utils-2.1.0/docs/.vuepress/config.js 0000664 0000000 0000000 00000001544 13671663521 0020651 0 ustar 00root root 0000000 0000000 "use strict"
module.exports = {
title: "eslint-utils",
description: "Utilities for ESLint plugins and custom rules.",
serviceWorker: true,
themeConfig: {
repo: "mysticatea/eslint-utils",
docsRepo: "mysticatea/eslint-utils",
docsDir: "docs",
docsBranch: "master",
editLinks: true,
sidebar: {
"/": [
{
title: "Guide",
collapsable: false,
children: ["/"],
},
{
title: "API Reference",
collapsable: false,
children: [
"/api/ast-utils",
"/api/scope-utils",
"/api/token-utils",
],
},
],
},
},
}
eslint-utils-2.1.0/docs/.vuepress/public/ 0000775 0000000 0000000 00000000000 13671663521 0020320 5 ustar 00root root 0000000 0000000 eslint-utils-2.1.0/docs/.vuepress/public/.nojekyll 0000664 0000000 0000000 00000000000 13671663521 0022136 0 ustar 00root root 0000000 0000000 eslint-utils-2.1.0/docs/.vuepress/styles/ 0000775 0000000 0000000 00000000000 13671663521 0020365 5 ustar 00root root 0000000 0000000 eslint-utils-2.1.0/docs/.vuepress/styles/palette.styl 0000664 0000000 0000000 00000000027 13671663521 0022737 0 ustar 00root root 0000000 0000000 $accentColor = #463fd4
eslint-utils-2.1.0/docs/README.md 0000664 0000000 0000000 00000002017 13671663521 0016367 0 ustar 00root root 0000000 0000000 # Getting Started
[](https://www.npmjs.com/package/eslint-utils)
[](http://www.npmtrends.com/eslint-utils)
[](https://travis-ci.org/mysticatea/eslint-utils)
[](https://codecov.io/gh/mysticatea/eslint-utils)
[](https://david-dm.org/mysticatea/eslint-utils)
## 🏁 Goal
`eslint-utils` package provides utility functions and classes for make ESLint custom rules.
## 💿 Installation
Use [npm](https://www.npmjs.com/) or a compatible tool to install.
```
npm install eslint-utils
```
::: tip Requirements
`eslint-utils` requires Node.js `6.5.0` or newer versions.
:::
## 📖 Usage
```js
const utils = require("eslint-utils")
// use it to define rules...
```
eslint-utils-2.1.0/docs/api/ 0000775 0000000 0000000 00000000000 13671663521 0015661 5 ustar 00root root 0000000 0000000 eslint-utils-2.1.0/docs/api/ast-utils.md 0000664 0000000 0000000 00000041642 13671663521 0020137 0 ustar 00root root 0000000 0000000 # AST utilities
----
## getFunctionHeadLocation
```js
const loc = utils.getFunctionHeadLocation(node, sourceCode)
```
Get the proper location of a given function node to report.
Show the location examples:
```
- `function foo() {}`
^^^^^^^^^^^^
- `(function foo() {})`
^^^^^^^^^^^^
- `(function() {})`
^^^^^^^^
- `function* foo() {}`
^^^^^^^^^^^^^
- `(function* foo() {})`
^^^^^^^^^^^^^
- `(function*() {})`
^^^^^^^^^
- `() => {}`
^^
- `async () => {}`
^^
- `({ foo: function foo() {} })`
^^^^^^^^^^^^^^^^^
- `({ foo: function() {} })`
^^^^^^^^^^^^^
- `({ ['foo']: function() {} })`
^^^^^^^^^^^^^^^^^
- `({ [foo]: function() {} })`
^^^^^^^^^^^^^^^
- `({ foo() {} })`
^^^
- `({ foo: function* foo() {} })`
^^^^^^^^^^^^^^^^^^
- `({ foo: function*() {} })`
^^^^^^^^^^^^^^
- `({ ['foo']: function*() {} })`
^^^^^^^^^^^^^^^^^^
- `({ [foo]: function*() {} })`
^^^^^^^^^^^^^^^^
- `({ *foo() {} })`
^^^^
- `({ foo: async function foo() {} })`
^^^^^^^^^^^^^^^^^^^^^^^
- `({ foo: async function() {} })`
^^^^^^^^^^^^^^^^^^^
- `({ ['foo']: async function() {} })`
^^^^^^^^^^^^^^^^^^^^^^^
- `({ [foo]: async function() {} })`
^^^^^^^^^^^^^^^^^^^^^
- `({ async foo() {} })`
^^^^^^^^^
- `({ get foo() {} })`
^^^^^^^
- `({ set foo(a) {} })`
^^^^^^^
- `class A { constructor() {} }`
^^^^^^^^^^^
- `class A { foo() {} }`
^^^
- `class A { *foo() {} }`
^^^^
- `class A { async foo() {} }`
^^^^^^^^^
- `class A { ['foo']() {} }`
^^^^^^^
- `class A { *['foo']() {} }`
^^^^^^^^
- `class A { async ['foo']() {} }`
^^^^^^^^^^^^^
- `class A { [foo]() {} }`
^^^^^
- `class A { *[foo]() {} }`
^^^^^^
- `class A { async [foo]() {} }`
^^^^^^^^^^^
- `class A { get foo() {} }`
^^^^^^^
- `class A { set foo(a) {} }`
^^^^^^^
- `class A { static foo() {} }`
^^^^^^^^^^
- `class A { static *foo() {} }`
^^^^^^^^^^^
- `class A { static async foo() {} }`
^^^^^^^^^^^^^^^^
- `class A { static get foo() {} }`
^^^^^^^^^^^^^^
- `class A { static set foo(a) {} }`
^^^^^^^^^^^^^^
```
### Parameters
Name | Type | Description
:-----|:-----|:------------
node | Node | The function node to get the location. This should be any of `FunctionDeclaration`, `FunctionExpression`, and `ArrowFunctionExpression` node.
sourceCode | SourceCode | The source code object to get tokens.
### Return value
The location object.
### Example
```js{12}
const { getFunctionHeadLocation } = require("eslint-utils")
module.exports = {
meta: {},
create(context) {
const sourceCode = context.getSourceCode()
return {
FunctionDeclaration(node) {
context.report({
node,
loc: getFunctionHeadLocation(node, sourceCode),
message: "disallow this function!",
})
},
}
},
}
```
----
## getFunctionNameWithKind
```js
const name = utils.getFunctionNameWithKind(node)
```
Get the name and kind of a given function node.
Show the name and kind examples:
```
- `function foo() {}` .................... `function 'foo'`
- `(function foo() {})` .................. `function 'foo'`
- `(function() {})` ...................... `function`
- `function* foo() {}` ................... `generator function 'foo'`
- `(function* foo() {})` ................. `generator function 'foo'`
- `(function*() {})` ..................... `generator function`
- `() => {}` ............................. `arrow function`
- `async () => {}` ....................... `async arrow function`
- `const foo = () => {}` ................. `arrow function 'foo'`
- `const foo = async () => {}` ........... `async arrow function 'foo'`
- `foo = () => {}` ....................... `arrow function 'foo'`
- `foo = async () => {}` ................. `async arrow function 'foo'`
- `({ foo: function foo() {} })` ......... `method 'foo'`
- `({ foo: function() {} })` ............. `method 'foo'`
- `({ ['foo']: function() {} })` ......... `method 'foo'`
- `({ [foo]: function() {} })` ........... `method`
- `({ foo() {} })` ....................... `method 'foo'`
- `({ foo: function* foo() {} })` ........ `generator method 'foo'`
- `({ foo: function*() {} })` ............ `generator method 'foo'`
- `({ ['foo']: function*() {} })` ........ `generator method 'foo'`
- `({ [foo]: function*() {} })` .......... `generator method`
- `({ *foo() {} })` ...................... `generator method 'foo'`
- `({ foo: async function foo() {} })` ... `async method 'foo'`
- `({ foo: async function() {} })` ....... `async method 'foo'`
- `({ ['foo']: async function() {} })` ... `async method 'foo'`
- `({ [foo]: async function() {} })` ..... `async method`
- `({ async foo() {} })` ................. `async method 'foo'`
- `({ get foo() {} })` ................... `getter 'foo'`
- `({ set foo(a) {} })` .................. `setter 'foo'`
- `class A { constructor() {} }` ......... `constructor`
- `class A { foo() {} }` ................. `method 'foo'`
- `class A { *foo() {} }` ................ `generator method 'foo'`
- `class A { async foo() {} }` ........... `async method 'foo'`
- `class A { ['foo']() {} }` ............. `method 'foo'`
- `class A { *['foo']() {} }` ............ `generator method 'foo'`
- `class A { async ['foo']() {} }` ....... `async method 'foo'`
- `class A { [foo]() {} }` ............... `method`
- `class A { *[foo]() {} }` .............. `generator method`
- `class A { async [foo]() {} }` ......... `async method`
- `class A { get foo() {} }` ............. `getter 'foo'`
- `class A { set foo(a) {} }` ............ `setter 'foo'`
- `class A { static foo() {} }` .......... `static method 'foo'`
- `class A { static *foo() {} }` ......... `static generator method 'foo'`
- `class A { static async foo() {} }` .... `static async method 'foo'`
- `class A { static get foo() {} }` ...... `static getter 'foo'`
- `class A { static set foo(a) {} }` ..... `static setter 'foo'`
```
### Parameters
Name | Type | Description
:-----|:-----|:------------
node | Node | The function node to get the name and kind. This should be any of `FunctionDeclaration`, `FunctionExpression`, and `ArrowFunctionExpression` node.
### Return value
The name and kind of the function.
### Example
```js{11}
const { getFunctionNameWithKind } = require("eslint-utils")
module.exports = {
meta: {},
create(context) {
return {
FunctionDeclaration(node) {
context.report({
node,
message: "disallow this {{name}}!",
data: { name: getFunctionNameWithKind(node) }
})
},
}
},
}
```
----
## getPropertyName
```js
const name = utils.getPropertyName(node)
const name = utils.getPropertyName(node, initialScope)
```
Get the property name of a given property node.
If the node is a computed property, this tries to compute the property name by the [getStringIfConstant](#getstringifconstant) function.
### Parameters
Name | Type | Description
:-----|:-----|:------------
node | Node | The node to get that name. This shuld be any of `MemberExpression`, `Property`, and `MethodDefinition` node.
initialScope | Scope or undefined | Optional. The scope object to find variables.
### Return value
The property name of the node.
If the property name is not constant then it returns `null`.
### Example
```js{8}
const { getPropertyName } = require("eslint-utils")
module.exports = {
meta: {},
create(context) {
return {
MemberExpression(node) {
const name = getPropertyName(node, context.getScope())
},
}
},
}
```
----
## getStaticValue
```js
const ret1 = utils.getStaticValue(node)
const ret2 = utils.getStaticValue(node, initialScope)
```
Get the value of a given node if it can decide the value statically.
If the 2nd parameter `initialScope` was given, this function tries to resolve identifier references which are in the given node as much as possible.
In the resolving way, it does on the assumption that built-in global objects have not been modified.
For example, it considers `Symbol.iterator`, ``String.raw`hello` ``, and `Object.freeze({a: 1}).a` as static.
For another complex example, this function can evaluate the following cases on AST:
```js{6}
const eventName = "click"
const aMap = Object.freeze({
click: 777
})
;`on${eventName} : ${aMap[eventName]}` // evaluated to "onclick : 777"
```
### Parameters
Name | Type | Description
:-----|:-----|:------------
node | Node | The node to get that the value.
initialScope | Scope or undefined | Optional. The scope object to find variables.
### Return value
The `{ value: any }` shaped object. The `value` property is the static value.
If it couldn't compute the static value of the node, it returns `null`.
### Example
```js{8}
const { getStaticValue } = require("eslint-utils")
module.exports = {
meta: {},
create(context) {
return {
ExpressionStatement(node) {
const evaluated = getStaticValue(node, context.getScope())
if (evaluated) {
const staticValue = evaluated.value
// ...
}
},
}
},
}
```
----
## getStringIfConstant
```js
const str1 = utils.getStringIfConstant(node)
const str2 = utils.getStringIfConstant(node, initialScope)
```
Get the string value of a given node.
This function is a tiny wrapper of the [getStaticValue](#getstaticvalue) function.
I.e., this is the same as below:
```js
function getStringIfConstant(node, initialScope) {
const evaluated = getStaticValue(node, initialScope)
return evaluated && String(evaluated.value)
}
```
----
## hasSideEffect
```js
const ret = utils.hasSideEffect(node, sourceCode, options)
```
Check whether a given node has any side effect or not.
The side effect means that it *may* modify a certain variable or object member. This function considers the node which contains the following types as the node which has side effects:
- `AssignmentExpression`
- `AwaitExpression`
- `CallExpression`
- `ImportExpression`
- `NewExpression`
- `UnaryExpression` (`[operator = "delete"]`)
- `UpdateExpression`
- `YieldExpression`
- When `options.considerGetters` is `true`:
- `MemberExpression`
- When `options.considerImplicitTypeConversion` is `true`:
- `BinaryExpression` (`[operator = "==" | "!=" | "<" | "<=" | ">" | ">=" | "<<" | ">>" | ">>>" | "+" | "-" | "*" | "/" | "%" | "|" | "^" | "&" | "in"]`)
- `MemberExpression` (`[computed = true]`)
- `MethodDefinition` (`[computed = true]`)
- `Property` (`[computed = true]`)
- `UnaryExpression` (`[operator = "-" | "+" | "!" | "~"]`)
### Parameters
Name | Type | Description
:-----|:-----|:------------
node | Node | The node to check.
sourceCode | SourceCode | The source code object to get visitor keys.
options.considerGetters | boolean | Default is `false`. If `true` then it considers member accesses as the node which has side effects.
options.considerImplicitTypeConversion | boolean | Default is `false`. If `true` then it considers implicit type conversion as the node which has side effects.
### Return value
`true` if the node has a certain side effect.
### Example
```js{9}
const { hasSideEffect } = require("eslint-utils")
module.exports = {
meta: {},
create(context) {
const sourceCode = context.getSourceCode()
return {
":expression"(node) {
if (hasSideEffect(node, sourceCode)) {
// ...
}
},
}
},
}
```
----
## isParenthesized
```js
const ret1 = utils.isParenthesized(times, node, sourceCode)
const ret2 = utils.isParenthesized(node, sourceCode)
```
Check whether a given node is parenthesized or not.
This function detects it correctly even if it's parenthesized by specific syntax.
```js
// those `a` are not parenthesized.
f(a);
new C(a);
if (a) {}
switch (a) {}
while (a) {}
do {} while (a);
with (a) {}
// those `b` are parenthesized.
f((b));
new C((b));
if ((b)) {}
switch ((b)) {}
while ((b)) {}
do {} while ((b));
with ((b)) {}
```
### Parameters
Name | Type | Description
:-----|:-----|:------------
times | number | Optional. The number of redundant parenthesized. Default is `1`.
node | Node | The node to check.
sourceCode | SourceCode | The source code object to get tokens.
### Return value
`true` if the node is parenthesized.
If `times` was given, it returns `true` only if the node is parenthesized the `times` times. For example, `isParenthesized(2, node, sourceCode)` returns `true` for `((foo))`, but not for `(foo)`.
### Example
```js{9}
const { isParenthesized } = require("eslint-utils")
module.exports = {
meta: {},
create(context) {
const sourceCode = context.getSourceCode()
return {
":expression"(node) {
if (isParenthesized(node, sourceCode)) {
// ...
}
},
}
},
}
```
----
## PatternMatcher class
```js
const matcher = new utils.PatternMatcher(pattern, options)
```
The class to find a pattern in strings as handling escape sequences.
It ignores the found pattern if it's escaped with `\`.
### Parameters
Name | Type | Description
:-----|:-----|:------------
pattern | RegExp | The regular expression pattern to find.
options.escaped | boolean | Optional. Default is `false`. If `true` then this matches to escaped patterns.
## matcher.execAll
```js
const iterator = matcher.execAll(str)
```
Iterate all matched parts in a given string.
### Parameters
Name | Type | Description
:-----|:-----|:------------
str | string | The string to find this pattern.
### Return value
The generator which iterates [RegExpExecArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec#Description) object.
### Example
```js{9}
const { PatternMatcher } = require("eslint-utils")
const matcher = new PatternMatcher(/\(\?<[_$\w]/g)
module.exports = {
meta: {},
create(context) {
return {
"Literal[regex]"(node) {
for (const { index } of matcher.execAll(node.regex.pattern)) {
context.report({
node,
loc: {
line: node.loc.start.line,
column: node.loc.start.column + index
},
message: "ES2018 RegExp named capture groups",
})
}
},
}
},
}
```
## matcher.test
```js
const matched = matcher.test(str)
```
Check whether this pattern matches a given string or not.
### Parameters
Name | Type | Description
:-----|:-----|:------------
str | string | The string to find this pattern.
### Return value
`true` if this pattern matched the string.
### Example
```js{9}
const { PatternMatcher } = require("eslint-utils")
const matcher = new PatternMatcher(/\(\?<[_$\w]/g)
module.exports = {
meta: {},
create(context) {
return {
"Literal[regex]"(node) {
if (matcher.test(node.regex.pattern)) {
context.report({
node,
message: "RegExp has ES2018 named capture groups",
})
}
},
}
},
}
```
## matcher[Symbol.replace]
```js
const replacedStr = str.replace(matcher, replacer)
```
Replace all matched parts by a given replacer.
### Parameters
Name | Type | Description
:-----|:-----|:------------
str | string | The string to be replaced.
replacer | string or function | The string or function to replace each matched part. This is the same as the 2nd parameter of [String.prototype.replace](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace).
### Return value
The replaced result.
### Example
```js{9}
const { PatternMatcher } = require("eslint-utils")
const matcher = new PatternMatcher(/\\p{Script=Greek}/g)
module.exports = {
meta: {},
create(context) {
return {
"Literal[regex]"(node) {
const replacedPattern = node.regex.pattern.replace(
matcher,
"[\\u0370-\\u0373\\u0375-\\u0377\\u037A-\\u037D\\u037F\\u0384\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03E1\\u03F0-\\u03FF\\u1D26-\\u1D2A\\u1D5D-\\u1D61\\u1D66-\\u1D6A\\u1DBF\\u1F00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FC4\\u1FC6-\\u1FD3\\u1FD6-\\u1FDB\\u1FDD-\\u1FEF\\u1FF2-\\u1FF4\\u1FF6-\\u1FFE\\u2126\\uAB65]|\\uD800[\\uDD40-\\uDD8E\\uDDA0]|\\uD834[\\uDE00-\\uDE45]"
)
},
}
},
}
```
eslint-utils-2.1.0/docs/api/scope-utils.md 0000664 0000000 0000000 00000022333 13671663521 0020455 0 ustar 00root root 0000000 0000000 # Scope analysis utilities
## findVariable
```js
const variable = utils.findVariable(initialScope, name)
```
Get the variable of a given name.
### Parameters
Name | Type | Description
:-----|:-----|:------------
initialScope | Scope | The scope object to start finding variables.
name | string or Node | The variable name to find. This can be an Identifier node.
### Return value
The found variable or `null`.
### Example
```js{8}
const { findVariable } = require("eslint-utils")
module.exports = {
meta: {},
create(context) {
return {
Identifier(node) {
const variable = findVariable(context.getScope(), node)
},
}
},
}
```
## getInnermostScope
```js
const scope = utils.getInnermostScope(initialScope, node)
```
Get the innermost scope which contains a given node.
### Parameters
Name | Type | Description
:-----|:-----|:------------
initialScope | Scope | The scope to start finding.
node | Node | The node to find the innermost scope.
### Return value
The innermost scope which contains the given node.
If such scope doesn't exist then it returns the 1st argument `initialScope`.
### Example
```js{9}
const { getInnermostScope } = require("eslint-utils")
module.exports = {
meta: {},
create(context) {
return {
"Program"(node) {
const globalScope = context.getScope()
const maybeNodejsScope = getInnermostScope(globalScope, node)
},
}
},
}
```
## ReferenceTracker class
```js
const tracker = new utils.ReferenceTracker(globalScope, options)
```
The tracker for references.
This provides reference tracking for global variables, CommonJS modules, and ES modules.
### Parameters
Name | Type | Description
:-----|:-----|:------------
globalScope | Scope | The global scope.
options.mode | `"strict"` or `"legacy"` | The mode which determines how the `tracker.iterateEsmReferences()` method scans CommonJS modules. If this is `"strict"`, the method binds CommonJS modules to the default export. Otherwise, the method binds CommonJS modules to both the default export and named exports. Optional. Default is `"strict"`.
options.globalObjectNames | string[] | The name list of Global Object. Optional. Default is `["global", "globalThis", "self", "window"]`.
## tracker.iterateGlobalReferences
```js
const it = tracker.iterateGlobalReferences(traceMap)
```
Iterate the references that the given `traceMap` determined.
This method starts to search from global variables.
### Parameters
Name | Type | Description
:-----|:-----|:------------
traceMap | object | The object which determines global variables and properties it iterates.
### Return value
The Iterator which iterates the reference of global variables.
Every reference is the object that has the following properties.
Name | Type | Description
:-----|:-----|:------------
node | Node | The node of the reference.
path | string[] | The path of the reference. For example, if it's the access of `console.log` then `["console", "log"]`.
type | symbol | The reference type. If this is `ReferenceTracker.READ` then it read the variable (or property). If this is `ReferenceTracker.CALL` then it called the variable (or property). If this is `ReferenceTracker.CONSTRUCT` then it called the variable (or property) with the `new` operator.
entry | any | The property value of any of `ReferenceTracker.READ`, `ReferenceTracker.CALL`, and `ReferenceTracker.CONSTRUCT`.
### Examples
```js
const { ReferenceTracker } = require("eslint-utils")
module.exports = {
meta: {},
create(context) {
return {
"Program:exit"() {
const tracker = new ReferenceTracker(context.getScope())
const traceMap = {
// Find `console.log`, `console.info`, `console.warn`, and `console.error`.
console: {
log: { [ReferenceTracker.READ]: true },
info: { [ReferenceTracker.READ]: true },
warn: { [ReferenceTracker.READ]: true },
error: { [ReferenceTracker.READ]: true },
},
// Find `Buffer()` and `new Buffer()`.
Buffer: {
[ReferenceTracker.CALL]: true,
[ReferenceTracker.CONSTRUCT]: true,
},
}
for (const { node, path } of tracker.iterateGlobalReferences(traceMap)) {
context.report({
node,
message: "disallow {{name}}.",
data: { name: path.join(".") },
})
}
},
}
},
}
```
## tracker.iterateCjsReferences
```js
const it = tracker.iterateCjsReferences(traceMap)
```
Iterate the references that the given `traceMap` determined.
This method starts to search from `require()` expression.
### Parameters
Name | Type | Description
:-----|:-----|:------------
traceMap | object | The object which determines modules it iterates.
### Return value
The Iterator which iterates the reference of modules.
Every reference is the object that has the following properties.
Name | Type | Description
:-----|:-----|:------------
node | Node | The node of the reference.
path | string[] | The path of the reference. For example, if it's the access of `fs.exists` then `["fs", "exists"]`.
type | symbol | The reference type. If this is `ReferenceTracker.READ` then it read the variable (or property). If this is `ReferenceTracker.CALL` then it called the variable (or property). If this is `ReferenceTracker.CONSTRUCT` then it called the variable (or property) with the `new` operator.
entry | any | The property value of any of `ReferenceTracker.READ`, `ReferenceTracker.CALL`, and `ReferenceTracker.CONSTRUCT`.
### Examples
```js
const { ReferenceTracker } = require("eslint-utils")
module.exports = {
meta: {},
create(context) {
return {
"Program:exit"() {
const tracker = new ReferenceTracker(context.getScope())
const traceMap = {
// Find `Buffer()` and `new Buffer()` of `buffer` module.
buffer: {
Buffer: {
[ReferenceTracker.CALL]: true,
[ReferenceTracker.CONSTRUCT]: true,
},
},
// Find `exists` of `fs` module.
fs: {
exists: {
[ReferenceTracker.READ]: true,
},
},
}
for (const { node, path } of tracker.iterateCjsReferences(traceMap)) {
context.report({
node,
message: "disallow {{name}}.",
data: { name: path.join(".") },
})
}
},
}
},
}
```
## tracker.iterateEsmReferences
```js
const it = tracker.iterateEsmReferences(traceMap)
```
Iterate the references that the given `traceMap` determined.
This method starts to search from `import`/`export` declarations.
### Parameters
Name | Type | Description
:-----|:-----|:------------
traceMap | object | The object which determines modules it iterates.
### Return value
The Iterator which iterates the reference of modules.
Every reference is the object that has the following properties.
Name | Type | Description
:-----|:-----|:------------
node | Node | The node of the reference.
path | string[] | The path of the reference. For example, if it's the access of `fs.exists` then `["fs", "exists"]`.
type | symbol | The reference type. If this is `ReferenceTracker.READ` then it read the variable (or property). If this is `ReferenceTracker.CALL` then it called the variable (or property). If this is `ReferenceTracker.CONSTRUCT` then it called the variable (or property) with the `new` operator.
entry | any | The property value of any of `ReferenceTracker.READ`, `ReferenceTracker.CALL`, and `ReferenceTracker.CONSTRUCT`.
### Examples
```js
const { ReferenceTracker } = require("eslint-utils")
module.exports = {
meta: {},
create(context) {
return {
"Program:exit"() {
const tracker = new ReferenceTracker(context.getScope())
const traceMap = {
// Find `Buffer()` and `new Buffer()` of `buffer` module.
buffer: {
Buffer: {
[ReferenceTracker.CALL]: true,
[ReferenceTracker.CONSTRUCT]: true,
},
},
// Find `exists` of `fs` module.
fs: {
exists: {
[ReferenceTracker.READ]: true,
},
},
}
for (const { node, path } of tracker.iterateEsmReferences(traceMap)) {
context.report({
node,
message: "disallow {{name}}.",
data: { name: path.join(".") },
})
}
},
}
},
}
```
eslint-utils-2.1.0/docs/api/token-utils.md 0000664 0000000 0000000 00000004351 13671663521 0020464 0 ustar 00root root 0000000 0000000 # Token utilities
## isArrowToken / isNotArrowToken
```js
utils.isArrowToken(token)
utils.isNotArrowToken(token)
```
Check whether a given token is a `=>` token or not.
### Examples
```js{10}
const { isArrowToken } = require("eslint-utils")
module.exports = {
meta: {},
create(context) {
const sourceCode = context.getSourceCode()
return {
ArrowFunctionExpression(node) {
const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken)
},
}
},
}
```
## isClosingBraceToken / isNotClosingBraceToken
```js
utils.isClosingBraceToken(token)
utils.isNotClosingBraceToken(token)
```
Check whether a given token is a `}` token or not.
## isClosingBracketToken / isNotClosingBracketToken
```js
utils.isClosingBracketToken(token)
utils.isNotClosingBracketToken(token)
```
Check whether a given token is a `]` token or not.
## isClosingParenToken / isNotClosingParenToken
```js
utils.isClosingParenToken(token)
utils.isNotClosingParenToken(token)
```
Check whether a given token is a `)` token or not.
## isColonToken / isNotColonToken
```js
utils.isColonToken(token)
utils.isNotColonToken(token)
```
Check whether a given token is a `:` token or not.
## isCommaToken / isNotCommaToken
```js
utils.isCommaToken(token)
utils.isNotCommaToken(token)
```
Check whether a given token is a `,` token or not.
## isCommentToken / isNotCommentToken
```js
utils.isCommentToken(token)
utils.isNotCommentToken(token)
```
Check whether a given token is a comment token or not.
## isOpeningBraceToken / isNotOpeningBraceToken
```js
utils.isOpeningBraceToken(token)
utils.isNotOpeningBraceToken(token)
```
Check whether a given token is a `{` token or not.
## isOpeningBracketToken / isNotOpeningBracketToken
```js
utils.isOpeningBracketToken(token)
utils.isNotOpeningBracketToken(token)
```
Check whether a given token is a `[` token or not.
## isOpeningParenToken / isNotOpeningParenToken
```js
utils.isOpeningParenToken(token)
utils.isNotOpeningParenToken(token)
```
Check whether a given token is a `(` token or not.
## isSemicolonToken / isNotSemicolonToken
```js
utils.isSemicolonToken(token)
utils.isNotSemicolonToken(token)
```
Check whether a given token is a `;` token or not.
eslint-utils-2.1.0/package.json 0000664 0000000 0000000 00000003423 13671663521 0016450 0 ustar 00root root 0000000 0000000 {
"name": "eslint-utils",
"version": "2.1.0",
"description": "Utilities for ESLint plugins.",
"engines": {
"node": ">=6"
},
"sideEffects": false,
"main": "index",
"module": "index.mjs",
"files": [
"index.*"
],
"dependencies": {
"eslint-visitor-keys": "^1.1.0"
},
"devDependencies": {
"@mysticatea/eslint-plugin": "^12.0.0",
"codecov": "^3.6.1",
"dot-prop": "^4.2.0",
"eslint": "^6.5.1",
"esm": "^3.2.25",
"espree": "^6.1.1",
"mocha": "^6.2.2",
"npm-run-all": "^4.1.5",
"nyc": "^14.1.1",
"opener": "^1.5.1",
"rimraf": "^3.0.0",
"rollup": "^1.25.0",
"rollup-plugin-sourcemaps": "^0.4.2",
"semver": "^7.3.2",
"vuepress": "^1.2.0",
"warun": "^1.0.0"
},
"scripts": {
"prebuild": "npm run -s clean",
"build": "rollup -c",
"clean": "rimraf .nyc_output coverage index.*",
"codecov": "nyc report -r lcovonly && codecov",
"coverage": "opener ./coverage/lcov-report/index.html",
"docs:build": "vuepress build docs",
"docs:watch": "vuepress dev docs",
"lint": "eslint src test",
"test": "run-s lint build test:mocha",
"test:mocha": "nyc mocha --reporter dot \"test/*.js\"",
"preversion": "npm test && npm run -s build",
"postversion": "git push && git push --tags",
"prewatch": "npm run -s clean",
"watch": "warun \"{src,test}/**/*.js\" -- npm run -s test:mocha"
},
"repository": {
"type": "git",
"url": "git+https://github.com/mysticatea/eslint-utils.git"
},
"keywords": [
"eslint"
],
"author": "Toru Nagashima",
"license": "MIT",
"bugs": {
"url": "https://github.com/mysticatea/eslint-utils/issues"
},
"homepage": "https://github.com/mysticatea/eslint-utils#readme",
"funding": "https://github.com/sponsors/mysticatea"
}
eslint-utils-2.1.0/rollup.config.js 0000664 0000000 0000000 00000001367 13671663521 0017306 0 ustar 00root root 0000000 0000000 /**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
import sourcemaps from "rollup-plugin-sourcemaps"
/**
* Define the output configuration.
* @param {string} ext The extension for generated files.
* @returns {object} The output configuration
*/
function config(ext) {
return {
input: "src/index.js",
output: {
file: `index${ext}`,
format: ext === ".mjs" ? "es" : "cjs",
sourcemap: true,
banner:
"/*! @author Toru Nagashima */",
},
plugins: [sourcemaps()],
external: Object.keys(require("./package.json").dependencies),
}
}
export default [config(".js"), config(".mjs")]
eslint-utils-2.1.0/src/ 0000775 0000000 0000000 00000000000 13671663521 0014747 5 ustar 00root root 0000000 0000000 eslint-utils-2.1.0/src/find-variable.js 0000664 0000000 0000000 00000001462 13671663521 0020013 0 ustar 00root root 0000000 0000000 import { getInnermostScope } from "./get-innermost-scope"
/**
* Find the variable of a given name.
* @param {Scope} initialScope The scope to start finding.
* @param {string|Node} nameOrNode The variable name to find. If this is a Node object then it should be an Identifier node.
* @returns {Variable|null} The found variable or null.
*/
export function findVariable(initialScope, nameOrNode) {
let name = ""
let scope = initialScope
if (typeof nameOrNode === "string") {
name = nameOrNode
} else {
name = nameOrNode.name
scope = getInnermostScope(scope, nameOrNode)
}
while (scope != null) {
const variable = scope.set.get(name)
if (variable != null) {
return variable
}
scope = scope.upper
}
return null
}
eslint-utils-2.1.0/src/get-function-head-location.js 0000664 0000000 0000000 00000002735 13671663521 0022423 0 ustar 00root root 0000000 0000000 import { isArrowToken, isOpeningParenToken } from "./token-predicate"
/**
* Get the `(` token of the given function node.
* @param {Node} node - The function node to get.
* @param {SourceCode} sourceCode - The source code object to get tokens.
* @returns {Token} `(` token.
*/
function getOpeningParenOfParams(node, sourceCode) {
return node.id
? sourceCode.getTokenAfter(node.id, isOpeningParenToken)
: sourceCode.getFirstToken(node, isOpeningParenToken)
}
/**
* Get the location of the given function node for reporting.
* @param {Node} node - The function node to get.
* @param {SourceCode} sourceCode - The source code object to get tokens.
* @returns {string} The location of the function node for reporting.
*/
export function getFunctionHeadLocation(node, sourceCode) {
const parent = node.parent
let start = null
let end = null
if (node.type === "ArrowFunctionExpression") {
const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken)
start = arrowToken.loc.start
end = arrowToken.loc.end
} else if (
parent.type === "Property" ||
parent.type === "MethodDefinition"
) {
start = parent.loc.start
end = getOpeningParenOfParams(node, sourceCode).loc.start
} else {
start = node.loc.start
end = getOpeningParenOfParams(node, sourceCode).loc.start
}
return {
start: Object.assign({}, start),
end: Object.assign({}, end),
}
}
eslint-utils-2.1.0/src/get-function-name-with-kind.js 0000664 0000000 0000000 00000003404 13671663521 0022522 0 ustar 00root root 0000000 0000000 import { getPropertyName } from "./get-property-name"
/**
* Get the name and kind of the given function node.
* @param {ASTNode} node - The function node to get.
* @returns {string} The name and kind of the function node.
*/
export function getFunctionNameWithKind(node) {
const parent = node.parent
const tokens = []
if (parent.type === "MethodDefinition" && parent.static) {
tokens.push("static")
}
if (node.async) {
tokens.push("async")
}
if (node.generator) {
tokens.push("generator")
}
if (node.type === "ArrowFunctionExpression") {
tokens.push("arrow", "function")
} else if (
parent.type === "Property" ||
parent.type === "MethodDefinition"
) {
if (parent.kind === "constructor") {
return "constructor"
}
if (parent.kind === "get") {
tokens.push("getter")
} else if (parent.kind === "set") {
tokens.push("setter")
} else {
tokens.push("method")
}
} else {
tokens.push("function")
}
if (node.id) {
tokens.push(`'${node.id.name}'`)
} else {
const name = getPropertyName(parent)
if (name) {
tokens.push(`'${name}'`)
}
}
if (node.type === "ArrowFunctionExpression") {
if (
parent.type === "VariableDeclarator" &&
parent.id &&
parent.id.type === "Identifier"
) {
tokens.push(`'${parent.id.name}'`)
}
if (
parent.type === "AssignmentExpression" &&
parent.left &&
parent.left.type === "Identifier"
) {
tokens.push(`'${parent.left.name}'`)
}
}
return tokens.join(" ")
}
eslint-utils-2.1.0/src/get-innermost-scope.js 0000664 0000000 0000000 00000001302 13671663521 0021203 0 ustar 00root root 0000000 0000000 /**
* Get the innermost scope which contains a given location.
* @param {Scope} initialScope The initial scope to search.
* @param {Node} node The location to search.
* @returns {Scope} The innermost scope.
*/
export function getInnermostScope(initialScope, node) {
const location = node.range[0]
let scope = initialScope
let found = false
do {
found = false
for (const childScope of scope.childScopes) {
const range = childScope.block.range
if (range[0] <= location && location < range[1]) {
scope = childScope
found = true
break
}
}
} while (found)
return scope
}
eslint-utils-2.1.0/src/get-property-name.js 0000664 0000000 0000000 00000002164 13671663521 0020667 0 ustar 00root root 0000000 0000000 import { getStringIfConstant } from "./get-string-if-constant"
/**
* Get the property name from a MemberExpression node or a Property node.
* @param {Node} node The node to get.
* @param {Scope} [initialScope] The scope to start finding variable. Optional. If the node is a computed property node and this scope was given, this checks the computed property name by the `getStringIfConstant` function with the scope, and returns the value of it.
* @returns {string|null} The property name of the node.
*/
export function getPropertyName(node, initialScope) {
switch (node.type) {
case "MemberExpression":
if (node.computed) {
return getStringIfConstant(node.property, initialScope)
}
return node.property.name
case "Property":
case "MethodDefinition":
if (node.computed) {
return getStringIfConstant(node.key, initialScope)
}
if (node.key.type === "Literal") {
return String(node.key.value)
}
return node.key.name
// no default
}
return null
}
eslint-utils-2.1.0/src/get-static-value.js 0000664 0000000 0000000 00000041340 13671663521 0020465 0 ustar 00root root 0000000 0000000 /* globals BigInt, globalThis, global, self, window */
import { findVariable } from "./find-variable"
const globalObject =
typeof globalThis !== "undefined"
? globalThis
: typeof self !== "undefined"
? self
: typeof window !== "undefined"
? window
: typeof global !== "undefined"
? global
: {}
const builtinNames = Object.freeze(
new Set([
"Array",
"ArrayBuffer",
"BigInt",
"BigInt64Array",
"BigUint64Array",
"Boolean",
"DataView",
"Date",
"decodeURI",
"decodeURIComponent",
"encodeURI",
"encodeURIComponent",
"escape",
"Float32Array",
"Float64Array",
"Function",
"Infinity",
"Int16Array",
"Int32Array",
"Int8Array",
"isFinite",
"isNaN",
"isPrototypeOf",
"JSON",
"Map",
"Math",
"NaN",
"Number",
"Object",
"parseFloat",
"parseInt",
"Promise",
"Proxy",
"Reflect",
"RegExp",
"Set",
"String",
"Symbol",
"Uint16Array",
"Uint32Array",
"Uint8Array",
"Uint8ClampedArray",
"undefined",
"unescape",
"WeakMap",
"WeakSet",
])
)
const callAllowed = new Set(
[
Array.isArray,
typeof BigInt === "function" ? BigInt : undefined,
Boolean,
Date,
Date.parse,
decodeURI,
decodeURIComponent,
encodeURI,
encodeURIComponent,
escape,
isFinite,
isNaN,
isPrototypeOf,
...Object.getOwnPropertyNames(Math)
.map(k => Math[k])
.filter(f => typeof f === "function"),
Number,
Number.isFinite,
Number.isNaN,
Number.parseFloat,
Number.parseInt,
Object,
Object.entries,
Object.is,
Object.isExtensible,
Object.isFrozen,
Object.isSealed,
Object.keys,
Object.values,
parseFloat,
parseInt,
RegExp,
String,
String.fromCharCode,
String.fromCodePoint,
String.raw,
Symbol,
Symbol.for,
Symbol.keyFor,
unescape,
].filter(f => typeof f === "function")
)
const callPassThrough = new Set([
Object.freeze,
Object.preventExtensions,
Object.seal,
])
/**
* Get the property descriptor.
* @param {object} object The object to get.
* @param {string|number|symbol} name The property name to get.
*/
function getPropertyDescriptor(object, name) {
let x = object
while ((typeof x === "object" || typeof x === "function") && x !== null) {
const d = Object.getOwnPropertyDescriptor(x, name)
if (d) {
return d
}
x = Object.getPrototypeOf(x)
}
return null
}
/**
* Check if a property is getter or not.
* @param {object} object The object to check.
* @param {string|number|symbol} name The property name to check.
*/
function isGetter(object, name) {
const d = getPropertyDescriptor(object, name)
return d != null && d.get != null
}
/**
* Get the element values of a given node list.
* @param {Node[]} nodeList The node list to get values.
* @param {Scope|undefined} initialScope The initial scope to find variables.
* @returns {any[]|null} The value list if all nodes are constant. Otherwise, null.
*/
function getElementValues(nodeList, initialScope) {
const valueList = []
for (let i = 0; i < nodeList.length; ++i) {
const elementNode = nodeList[i]
if (elementNode == null) {
valueList.length = i + 1
} else if (elementNode.type === "SpreadElement") {
const argument = getStaticValueR(elementNode.argument, initialScope)
if (argument == null) {
return null
}
valueList.push(...argument.value)
} else {
const element = getStaticValueR(elementNode, initialScope)
if (element == null) {
return null
}
valueList.push(element.value)
}
}
return valueList
}
const operations = Object.freeze({
ArrayExpression(node, initialScope) {
const elements = getElementValues(node.elements, initialScope)
return elements != null ? { value: elements } : null
},
AssignmentExpression(node, initialScope) {
if (node.operator === "=") {
return getStaticValueR(node.right, initialScope)
}
return null
},
//eslint-disable-next-line complexity
BinaryExpression(node, initialScope) {
if (node.operator === "in" || node.operator === "instanceof") {
// Not supported.
return null
}
const left = getStaticValueR(node.left, initialScope)
const right = getStaticValueR(node.right, initialScope)
if (left != null && right != null) {
switch (node.operator) {
case "==":
return { value: left.value == right.value } //eslint-disable-line eqeqeq
case "!=":
return { value: left.value != right.value } //eslint-disable-line eqeqeq
case "===":
return { value: left.value === right.value }
case "!==":
return { value: left.value !== right.value }
case "<":
return { value: left.value < right.value }
case "<=":
return { value: left.value <= right.value }
case ">":
return { value: left.value > right.value }
case ">=":
return { value: left.value >= right.value }
case "<<":
return { value: left.value << right.value }
case ">>":
return { value: left.value >> right.value }
case ">>>":
return { value: left.value >>> right.value }
case "+":
return { value: left.value + right.value }
case "-":
return { value: left.value - right.value }
case "*":
return { value: left.value * right.value }
case "/":
return { value: left.value / right.value }
case "%":
return { value: left.value % right.value }
case "**":
return { value: Math.pow(left.value, right.value) }
case "|":
return { value: left.value | right.value }
case "^":
return { value: left.value ^ right.value }
case "&":
return { value: left.value & right.value }
// no default
}
}
return null
},
CallExpression(node, initialScope) {
const calleeNode = node.callee
const args = getElementValues(node.arguments, initialScope)
if (args != null) {
if (calleeNode.type === "MemberExpression") {
const object = getStaticValueR(calleeNode.object, initialScope)
if (object != null) {
if (
object.value == null &&
(object.optional || node.optional)
) {
return { value: undefined, optional: true }
}
const property = calleeNode.computed
? getStaticValueR(calleeNode.property, initialScope)
: { value: calleeNode.property.name }
if (property != null) {
const receiver = object.value
const methodName = property.value
if (callAllowed.has(receiver[methodName])) {
return { value: receiver[methodName](...args) }
}
if (callPassThrough.has(receiver[methodName])) {
return { value: args[0] }
}
}
}
} else {
const callee = getStaticValueR(calleeNode, initialScope)
if (callee != null) {
if (callee.value == null && node.optional) {
return { value: undefined, optional: true }
}
const func = callee.value
if (callAllowed.has(func)) {
return { value: func(...args) }
}
if (callPassThrough.has(func)) {
return { value: args[0] }
}
}
}
}
return null
},
ConditionalExpression(node, initialScope) {
const test = getStaticValueR(node.test, initialScope)
if (test != null) {
return test.value
? getStaticValueR(node.consequent, initialScope)
: getStaticValueR(node.alternate, initialScope)
}
return null
},
ExpressionStatement(node, initialScope) {
return getStaticValueR(node.expression, initialScope)
},
Identifier(node, initialScope) {
if (initialScope != null) {
const variable = findVariable(initialScope, node)
// Built-in globals.
if (
variable != null &&
variable.defs.length === 0 &&
builtinNames.has(variable.name) &&
variable.name in globalObject
) {
return { value: globalObject[variable.name] }
}
// Constants.
if (variable != null && variable.defs.length === 1) {
const def = variable.defs[0]
if (
def.parent &&
def.parent.kind === "const" &&
// TODO(mysticatea): don't support destructuring here.
def.node.id.type === "Identifier"
) {
return getStaticValueR(def.node.init, initialScope)
}
}
}
return null
},
Literal(node) {
//istanbul ignore if : this is implementation-specific behavior.
if ((node.regex != null || node.bigint != null) && node.value == null) {
// It was a RegExp/BigInt literal, but Node.js didn't support it.
return null
}
return { value: node.value }
},
LogicalExpression(node, initialScope) {
const left = getStaticValueR(node.left, initialScope)
if (left != null) {
if (
(node.operator === "||" && Boolean(left.value) === true) ||
(node.operator === "&&" && Boolean(left.value) === false) ||
(node.operator === "??" && left.value != null)
) {
return left
}
const right = getStaticValueR(node.right, initialScope)
if (right != null) {
return right
}
}
return null
},
MemberExpression(node, initialScope) {
const object = getStaticValueR(node.object, initialScope)
if (object != null) {
if (object.value == null && (object.optional || node.optional)) {
return { value: undefined, optional: true }
}
const property = node.computed
? getStaticValueR(node.property, initialScope)
: { value: node.property.name }
if (property != null && !isGetter(object.value, property.value)) {
return { value: object.value[property.value] }
}
}
return null
},
ChainExpression(node, initialScope) {
const expression = getStaticValueR(node.expression, initialScope)
if (expression != null) {
return { value: expression.value }
}
return null
},
NewExpression(node, initialScope) {
const callee = getStaticValueR(node.callee, initialScope)
const args = getElementValues(node.arguments, initialScope)
if (callee != null && args != null) {
const Func = callee.value
if (callAllowed.has(Func)) {
return { value: new Func(...args) }
}
}
return null
},
ObjectExpression(node, initialScope) {
const object = {}
for (const propertyNode of node.properties) {
if (propertyNode.type === "Property") {
if (propertyNode.kind !== "init") {
return null
}
const key = propertyNode.computed
? getStaticValueR(propertyNode.key, initialScope)
: { value: propertyNode.key.name }
const value = getStaticValueR(propertyNode.value, initialScope)
if (key == null || value == null) {
return null
}
object[key.value] = value.value
} else if (
propertyNode.type === "SpreadElement" ||
propertyNode.type === "ExperimentalSpreadProperty"
) {
const argument = getStaticValueR(
propertyNode.argument,
initialScope
)
if (argument == null) {
return null
}
Object.assign(object, argument.value)
} else {
return null
}
}
return { value: object }
},
SequenceExpression(node, initialScope) {
const last = node.expressions[node.expressions.length - 1]
return getStaticValueR(last, initialScope)
},
TaggedTemplateExpression(node, initialScope) {
const tag = getStaticValueR(node.tag, initialScope)
const expressions = getElementValues(
node.quasi.expressions,
initialScope
)
if (tag != null && expressions != null) {
const func = tag.value
const strings = node.quasi.quasis.map(q => q.value.cooked)
strings.raw = node.quasi.quasis.map(q => q.value.raw)
if (func === String.raw) {
return { value: func(strings, ...expressions) }
}
}
return null
},
TemplateLiteral(node, initialScope) {
const expressions = getElementValues(node.expressions, initialScope)
if (expressions != null) {
let value = node.quasis[0].value.cooked
for (let i = 0; i < expressions.length; ++i) {
value += expressions[i]
value += node.quasis[i + 1].value.cooked
}
return { value }
}
return null
},
UnaryExpression(node, initialScope) {
if (node.operator === "delete") {
// Not supported.
return null
}
if (node.operator === "void") {
return { value: undefined }
}
const arg = getStaticValueR(node.argument, initialScope)
if (arg != null) {
switch (node.operator) {
case "-":
return { value: -arg.value }
case "+":
return { value: +arg.value } //eslint-disable-line no-implicit-coercion
case "!":
return { value: !arg.value }
case "~":
return { value: ~arg.value }
case "typeof":
return { value: typeof arg.value }
// no default
}
}
return null
},
})
/**
* Get the value of a given node if it's a static value.
* @param {Node} node The node to get.
* @param {Scope|undefined} initialScope The scope to start finding variable.
* @returns {{value:any}|{value:undefined,optional?:true}|null} The static value of the node, or `null`.
*/
function getStaticValueR(node, initialScope) {
if (node != null && Object.hasOwnProperty.call(operations, node.type)) {
return operations[node.type](node, initialScope)
}
return null
}
/**
* Get the value of a given node if it's a static value.
* @param {Node} node The node to get.
* @param {Scope} [initialScope] The scope to start finding variable. Optional. If this scope was given, this tries to resolve identifier references which are in the given node as much as possible.
* @returns {{value:any}|{value:undefined,optional?:true}|null} The static value of the node, or `null`.
*/
export function getStaticValue(node, initialScope = null) {
try {
return getStaticValueR(node, initialScope)
} catch (_error) {
return null
}
}
eslint-utils-2.1.0/src/get-string-if-constant.js 0000664 0000000 0000000 00000001701 13671663521 0021612 0 ustar 00root root 0000000 0000000 import { getStaticValue } from "./get-static-value"
/**
* Get the value of a given node if it's a literal or a template literal.
* @param {Node} node The node to get.
* @param {Scope} [initialScope] The scope to start finding variable. Optional. If the node is an Identifier node and this scope was given, this checks the variable of the identifier, and returns the value of it if the variable is a constant.
* @returns {string|null} The value of the node, or `null`.
*/
export function getStringIfConstant(node, initialScope = null) {
// Handle the literals that the platform doesn't support natively.
if (node && node.type === "Literal" && node.value === null) {
if (node.regex) {
return `/${node.regex.pattern}/${node.regex.flags}`
}
if (node.bigint) {
return node.bigint
}
}
const evaluated = getStaticValue(node, initialScope)
return evaluated && String(evaluated.value)
}
eslint-utils-2.1.0/src/has-side-effect.js 0000664 0000000 0000000 00000012442 13671663521 0020237 0 ustar 00root root 0000000 0000000 import evk from "eslint-visitor-keys"
const typeConversionBinaryOps = Object.freeze(
new Set([
"==",
"!=",
"<",
"<=",
">",
">=",
"<<",
">>",
">>>",
"+",
"-",
"*",
"/",
"%",
"|",
"^",
"&",
"in",
])
)
const typeConversionUnaryOps = Object.freeze(new Set(["-", "+", "!", "~"]))
/**
* Check whether the given value is an ASTNode or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if the value is an ASTNode.
*/
function isNode(x) {
return x !== null && typeof x === "object" && typeof x.type === "string"
}
const visitor = Object.freeze(
Object.assign(Object.create(null), {
$visit(node, options, visitorKeys) {
const { type } = node
if (typeof this[type] === "function") {
return this[type](node, options, visitorKeys)
}
return this.$visitChildren(node, options, visitorKeys)
},
$visitChildren(node, options, visitorKeys) {
const { type } = node
for (const key of visitorKeys[type] || evk.getKeys(node)) {
const value = node[key]
if (Array.isArray(value)) {
for (const element of value) {
if (
isNode(element) &&
this.$visit(element, options, visitorKeys)
) {
return true
}
}
} else if (
isNode(value) &&
this.$visit(value, options, visitorKeys)
) {
return true
}
}
return false
},
ArrowFunctionExpression() {
return false
},
AssignmentExpression() {
return true
},
AwaitExpression() {
return true
},
BinaryExpression(node, options, visitorKeys) {
if (
options.considerImplicitTypeConversion &&
typeConversionBinaryOps.has(node.operator) &&
(node.left.type !== "Literal" || node.right.type !== "Literal")
) {
return true
}
return this.$visitChildren(node, options, visitorKeys)
},
CallExpression() {
return true
},
FunctionExpression() {
return false
},
ImportExpression() {
return true
},
MemberExpression(node, options, visitorKeys) {
if (options.considerGetters) {
return true
}
if (
options.considerImplicitTypeConversion &&
node.computed &&
node.property.type !== "Literal"
) {
return true
}
return this.$visitChildren(node, options, visitorKeys)
},
MethodDefinition(node, options, visitorKeys) {
if (
options.considerImplicitTypeConversion &&
node.computed &&
node.key.type !== "Literal"
) {
return true
}
return this.$visitChildren(node, options, visitorKeys)
},
NewExpression() {
return true
},
Property(node, options, visitorKeys) {
if (
options.considerImplicitTypeConversion &&
node.computed &&
node.key.type !== "Literal"
) {
return true
}
return this.$visitChildren(node, options, visitorKeys)
},
UnaryExpression(node, options, visitorKeys) {
if (node.operator === "delete") {
return true
}
if (
options.considerImplicitTypeConversion &&
typeConversionUnaryOps.has(node.operator) &&
node.argument.type !== "Literal"
) {
return true
}
return this.$visitChildren(node, options, visitorKeys)
},
UpdateExpression() {
return true
},
YieldExpression() {
return true
},
})
)
/**
* Check whether a given node has any side effect or not.
* @param {Node} node The node to get.
* @param {SourceCode} sourceCode The source code object.
* @param {object} [options] The option object.
* @param {boolean} [options.considerGetters=false] If `true` then it considers member accesses as the node which has side effects.
* @param {boolean} [options.considerImplicitTypeConversion=false] If `true` then it considers implicit type conversion as the node which has side effects.
* @param {object} [options.visitorKeys=evk.KEYS] The keys to traverse nodes. Use `context.getSourceCode().visitorKeys`.
* @returns {boolean} `true` if the node has a certain side effect.
*/
export function hasSideEffect(
node,
sourceCode,
{ considerGetters = false, considerImplicitTypeConversion = false } = {}
) {
return visitor.$visit(
node,
{ considerGetters, considerImplicitTypeConversion },
sourceCode.visitorKeys || evk.KEYS
)
}
eslint-utils-2.1.0/src/index.js 0000664 0000000 0000000 00000005553 13671663521 0016424 0 ustar 00root root 0000000 0000000 import { findVariable } from "./find-variable"
import { getFunctionHeadLocation } from "./get-function-head-location"
import { getFunctionNameWithKind } from "./get-function-name-with-kind"
import { getInnermostScope } from "./get-innermost-scope"
import { getPropertyName } from "./get-property-name"
import { getStaticValue } from "./get-static-value"
import { getStringIfConstant } from "./get-string-if-constant"
import { hasSideEffect } from "./has-side-effect"
import { isParenthesized } from "./is-parenthesized"
import { PatternMatcher } from "./pattern-matcher"
import {
CALL,
CONSTRUCT,
ESM,
READ,
ReferenceTracker,
} from "./reference-tracker"
import {
isArrowToken,
isClosingBraceToken,
isClosingBracketToken,
isClosingParenToken,
isColonToken,
isCommaToken,
isCommentToken,
isNotArrowToken,
isNotClosingBraceToken,
isNotClosingBracketToken,
isNotClosingParenToken,
isNotColonToken,
isNotCommaToken,
isNotCommentToken,
isNotOpeningBraceToken,
isNotOpeningBracketToken,
isNotOpeningParenToken,
isNotSemicolonToken,
isOpeningBraceToken,
isOpeningBracketToken,
isOpeningParenToken,
isSemicolonToken,
} from "./token-predicate"
export default {
CALL,
CONSTRUCT,
ESM,
findVariable,
getFunctionHeadLocation,
getFunctionNameWithKind,
getInnermostScope,
getPropertyName,
getStaticValue,
getStringIfConstant,
hasSideEffect,
isArrowToken,
isClosingBraceToken,
isClosingBracketToken,
isClosingParenToken,
isColonToken,
isCommaToken,
isCommentToken,
isNotArrowToken,
isNotClosingBraceToken,
isNotClosingBracketToken,
isNotClosingParenToken,
isNotColonToken,
isNotCommaToken,
isNotCommentToken,
isNotOpeningBraceToken,
isNotOpeningBracketToken,
isNotOpeningParenToken,
isNotSemicolonToken,
isOpeningBraceToken,
isOpeningBracketToken,
isOpeningParenToken,
isParenthesized,
isSemicolonToken,
PatternMatcher,
READ,
ReferenceTracker,
}
export {
CALL,
CONSTRUCT,
ESM,
findVariable,
getFunctionHeadLocation,
getFunctionNameWithKind,
getInnermostScope,
getPropertyName,
getStaticValue,
getStringIfConstant,
hasSideEffect,
isArrowToken,
isClosingBraceToken,
isClosingBracketToken,
isClosingParenToken,
isColonToken,
isCommaToken,
isCommentToken,
isNotArrowToken,
isNotClosingBraceToken,
isNotClosingBracketToken,
isNotClosingParenToken,
isNotColonToken,
isNotCommaToken,
isNotCommentToken,
isNotOpeningBraceToken,
isNotOpeningBracketToken,
isNotOpeningParenToken,
isNotSemicolonToken,
isOpeningBraceToken,
isOpeningBracketToken,
isOpeningParenToken,
isParenthesized,
isSemicolonToken,
PatternMatcher,
READ,
ReferenceTracker,
}
eslint-utils-2.1.0/src/is-parenthesized.js 0000664 0000000 0000000 00000006645 13671663521 0020576 0 ustar 00root root 0000000 0000000 import { isClosingParenToken, isOpeningParenToken } from "./token-predicate"
/**
* Get the left parenthesis of the parent node syntax if it exists.
* E.g., `if (a) {}` then the `(`.
* @param {Node} node The AST node to check.
* @param {SourceCode} sourceCode The source code object to get tokens.
* @returns {Token|null} The left parenthesis of the parent node syntax
*/
function getParentSyntaxParen(node, sourceCode) {
const parent = node.parent
switch (parent.type) {
case "CallExpression":
case "NewExpression":
if (parent.arguments.length === 1 && parent.arguments[0] === node) {
return sourceCode.getTokenAfter(
parent.callee,
isOpeningParenToken
)
}
return null
case "DoWhileStatement":
if (parent.test === node) {
return sourceCode.getTokenAfter(
parent.body,
isOpeningParenToken
)
}
return null
case "IfStatement":
case "WhileStatement":
if (parent.test === node) {
return sourceCode.getFirstToken(parent, 1)
}
return null
case "ImportExpression":
if (parent.source === node) {
return sourceCode.getFirstToken(parent, 1)
}
return null
case "SwitchStatement":
if (parent.discriminant === node) {
return sourceCode.getFirstToken(parent, 1)
}
return null
case "WithStatement":
if (parent.object === node) {
return sourceCode.getFirstToken(parent, 1)
}
return null
default:
return null
}
}
/**
* Check whether a given node is parenthesized or not.
* @param {number} times The number of parantheses.
* @param {Node} node The AST node to check.
* @param {SourceCode} sourceCode The source code object to get tokens.
* @returns {boolean} `true` if the node is parenthesized the given times.
*/
/**
* Check whether a given node is parenthesized or not.
* @param {Node} node The AST node to check.
* @param {SourceCode} sourceCode The source code object to get tokens.
* @returns {boolean} `true` if the node is parenthesized.
*/
export function isParenthesized(
timesOrNode,
nodeOrSourceCode,
optionalSourceCode
) {
let times, node, sourceCode, maybeLeftParen, maybeRightParen
if (typeof timesOrNode === "number") {
times = timesOrNode | 0
node = nodeOrSourceCode
sourceCode = optionalSourceCode
if (!(times >= 1)) {
throw new TypeError("'times' should be a positive integer.")
}
} else {
times = 1
node = timesOrNode
sourceCode = nodeOrSourceCode
}
if (node == null) {
return false
}
maybeLeftParen = maybeRightParen = node
do {
maybeLeftParen = sourceCode.getTokenBefore(maybeLeftParen)
maybeRightParen = sourceCode.getTokenAfter(maybeRightParen)
} while (
maybeLeftParen != null &&
maybeRightParen != null &&
isOpeningParenToken(maybeLeftParen) &&
isClosingParenToken(maybeRightParen) &&
// Avoid false positive such as `if (a) {}`
maybeLeftParen !== getParentSyntaxParen(node, sourceCode) &&
--times > 0
)
return times === 0
}
eslint-utils-2.1.0/src/pattern-matcher.js 0000664 0000000 0000000 00000011364 13671663521 0020410 0 ustar 00root root 0000000 0000000 /**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
const placeholder = /\$(?:[$&`']|[1-9][0-9]?)/gu
/** @type {WeakMap} */
const internal = new WeakMap()
/**
* Check whether a given character is escaped or not.
* @param {string} str The string to check.
* @param {number} index The location of the character to check.
* @returns {boolean} `true` if the character is escaped.
*/
function isEscaped(str, index) {
let escaped = false
for (let i = index - 1; i >= 0 && str.charCodeAt(i) === 0x5c; --i) {
escaped = !escaped
}
return escaped
}
/**
* Replace a given string by a given matcher.
* @param {PatternMatcher} matcher The pattern matcher.
* @param {string} str The string to be replaced.
* @param {string} replacement The new substring to replace each matched part.
* @returns {string} The replaced string.
*/
function replaceS(matcher, str, replacement) {
const chunks = []
let index = 0
/** @type {RegExpExecArray} */
let match = null
/**
* @param {string} key The placeholder.
* @returns {string} The replaced string.
*/
function replacer(key) {
switch (key) {
case "$$":
return "$"
case "$&":
return match[0]
case "$`":
return str.slice(0, match.index)
case "$'":
return str.slice(match.index + match[0].length)
default: {
const i = key.slice(1)
if (i in match) {
return match[i]
}
return key
}
}
}
for (match of matcher.execAll(str)) {
chunks.push(str.slice(index, match.index))
chunks.push(replacement.replace(placeholder, replacer))
index = match.index + match[0].length
}
chunks.push(str.slice(index))
return chunks.join("")
}
/**
* Replace a given string by a given matcher.
* @param {PatternMatcher} matcher The pattern matcher.
* @param {string} str The string to be replaced.
* @param {(...strs[])=>string} replace The function to replace each matched part.
* @returns {string} The replaced string.
*/
function replaceF(matcher, str, replace) {
const chunks = []
let index = 0
for (const match of matcher.execAll(str)) {
chunks.push(str.slice(index, match.index))
chunks.push(String(replace(...match, match.index, match.input)))
index = match.index + match[0].length
}
chunks.push(str.slice(index))
return chunks.join("")
}
/**
* The class to find patterns as considering escape sequences.
*/
export class PatternMatcher {
/**
* Initialize this matcher.
* @param {RegExp} pattern The pattern to match.
* @param {{escaped:boolean}} options The options.
*/
constructor(pattern, { escaped = false } = {}) {
if (!(pattern instanceof RegExp)) {
throw new TypeError("'pattern' should be a RegExp instance.")
}
if (!pattern.flags.includes("g")) {
throw new Error("'pattern' should contains 'g' flag.")
}
internal.set(this, {
pattern: new RegExp(pattern.source, pattern.flags),
escaped: Boolean(escaped),
})
}
/**
* Find the pattern in a given string.
* @param {string} str The string to find.
* @returns {IterableIterator} The iterator which iterate the matched information.
*/
*execAll(str) {
const { pattern, escaped } = internal.get(this)
let match = null
let lastIndex = 0
pattern.lastIndex = 0
while ((match = pattern.exec(str)) != null) {
if (escaped || !isEscaped(str, match.index)) {
lastIndex = pattern.lastIndex
yield match
pattern.lastIndex = lastIndex
}
}
}
/**
* Check whether the pattern is found in a given string.
* @param {string} str The string to check.
* @returns {boolean} `true` if the pattern was found in the string.
*/
test(str) {
const it = this.execAll(str)
const ret = it.next()
return !ret.done
}
/**
* Replace a given string.
* @param {string} str The string to be replaced.
* @param {(string|((...strs:string[])=>string))} replacer The string or function to replace. This is the same as the 2nd argument of `String.prototype.replace`.
* @returns {string} The replaced string.
*/
[Symbol.replace](str, replacer) {
return typeof replacer === "function"
? replaceF(this, String(str), replacer)
: replaceS(this, String(str), String(replacer))
}
}
eslint-utils-2.1.0/src/reference-tracker.js 0000664 0000000 0000000 00000035732 13671663521 0020706 0 ustar 00root root 0000000 0000000 import { findVariable } from "./find-variable"
import { getPropertyName } from "./get-property-name"
import { getStringIfConstant } from "./get-string-if-constant"
const IMPORT_TYPE = /^(?:Import|Export(?:All|Default|Named))Declaration$/u
const has = Function.call.bind(Object.hasOwnProperty)
export const READ = Symbol("read")
export const CALL = Symbol("call")
export const CONSTRUCT = Symbol("construct")
export const ESM = Symbol("esm")
const requireCall = { require: { [CALL]: true } }
/**
* Check whether a given variable is modified or not.
* @param {Variable} variable The variable to check.
* @returns {boolean} `true` if the variable is modified.
*/
function isModifiedGlobal(variable) {
return (
variable == null ||
variable.defs.length !== 0 ||
variable.references.some(r => r.isWrite())
)
}
/**
* Check if the value of a given node is passed through to the parent syntax as-is.
* For example, `a` and `b` in (`a || b` and `c ? a : b`) are passed through.
* @param {Node} node A node to check.
* @returns {boolean} `true` if the node is passed through.
*/
function isPassThrough(node) {
const parent = node.parent
switch (parent && parent.type) {
case "ConditionalExpression":
return parent.consequent === node || parent.alternate === node
case "LogicalExpression":
return true
case "SequenceExpression":
return parent.expressions[parent.expressions.length - 1] === node
case "ChainExpression":
return true
default:
return false
}
}
/**
* The reference tracker.
*/
export class ReferenceTracker {
/**
* Initialize this tracker.
* @param {Scope} globalScope The global scope.
* @param {object} [options] The options.
* @param {"legacy"|"strict"} [options.mode="strict"] The mode to determine the ImportDeclaration's behavior for CJS modules.
* @param {string[]} [options.globalObjectNames=["global","globalThis","self","window"]] The variable names for Global Object.
*/
constructor(
globalScope,
{
mode = "strict",
globalObjectNames = ["global", "globalThis", "self", "window"],
} = {}
) {
this.variableStack = []
this.globalScope = globalScope
this.mode = mode
this.globalObjectNames = globalObjectNames.slice(0)
}
/**
* Iterate the references of global variables.
* @param {object} traceMap The trace map.
* @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
*/
*iterateGlobalReferences(traceMap) {
for (const key of Object.keys(traceMap)) {
const nextTraceMap = traceMap[key]
const path = [key]
const variable = this.globalScope.set.get(key)
if (isModifiedGlobal(variable)) {
continue
}
yield* this._iterateVariableReferences(
variable,
path,
nextTraceMap,
true
)
}
for (const key of this.globalObjectNames) {
const path = []
const variable = this.globalScope.set.get(key)
if (isModifiedGlobal(variable)) {
continue
}
yield* this._iterateVariableReferences(
variable,
path,
traceMap,
false
)
}
}
/**
* Iterate the references of CommonJS modules.
* @param {object} traceMap The trace map.
* @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
*/
*iterateCjsReferences(traceMap) {
for (const { node } of this.iterateGlobalReferences(requireCall)) {
const key = getStringIfConstant(node.arguments[0])
if (key == null || !has(traceMap, key)) {
continue
}
const nextTraceMap = traceMap[key]
const path = [key]
if (nextTraceMap[READ]) {
yield {
node,
path,
type: READ,
info: nextTraceMap[READ],
}
}
yield* this._iteratePropertyReferences(node, path, nextTraceMap)
}
}
/**
* Iterate the references of ES modules.
* @param {object} traceMap The trace map.
* @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
*/
*iterateEsmReferences(traceMap) {
const programNode = this.globalScope.block
for (const node of programNode.body) {
if (!IMPORT_TYPE.test(node.type) || node.source == null) {
continue
}
const moduleId = node.source.value
if (!has(traceMap, moduleId)) {
continue
}
const nextTraceMap = traceMap[moduleId]
const path = [moduleId]
if (nextTraceMap[READ]) {
yield { node, path, type: READ, info: nextTraceMap[READ] }
}
if (node.type === "ExportAllDeclaration") {
for (const key of Object.keys(nextTraceMap)) {
const exportTraceMap = nextTraceMap[key]
if (exportTraceMap[READ]) {
yield {
node,
path: path.concat(key),
type: READ,
info: exportTraceMap[READ],
}
}
}
} else {
for (const specifier of node.specifiers) {
const esm = has(nextTraceMap, ESM)
const it = this._iterateImportReferences(
specifier,
path,
esm
? nextTraceMap
: this.mode === "legacy"
? Object.assign(
{ default: nextTraceMap },
nextTraceMap
)
: { default: nextTraceMap }
)
if (esm) {
yield* it
} else {
for (const report of it) {
report.path = report.path.filter(exceptDefault)
if (
report.path.length >= 2 ||
report.type !== READ
) {
yield report
}
}
}
}
}
}
}
/**
* Iterate the references for a given variable.
* @param {Variable} variable The variable to iterate that references.
* @param {string[]} path The current path.
* @param {object} traceMap The trace map.
* @param {boolean} shouldReport = The flag to report those references.
* @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
*/
*_iterateVariableReferences(variable, path, traceMap, shouldReport) {
if (this.variableStack.includes(variable)) {
return
}
this.variableStack.push(variable)
try {
for (const reference of variable.references) {
if (!reference.isRead()) {
continue
}
const node = reference.identifier
if (shouldReport && traceMap[READ]) {
yield { node, path, type: READ, info: traceMap[READ] }
}
yield* this._iteratePropertyReferences(node, path, traceMap)
}
} finally {
this.variableStack.pop()
}
}
/**
* Iterate the references for a given AST node.
* @param rootNode The AST node to iterate references.
* @param {string[]} path The current path.
* @param {object} traceMap The trace map.
* @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
*/
//eslint-disable-next-line complexity
*_iteratePropertyReferences(rootNode, path, traceMap) {
let node = rootNode
while (isPassThrough(node)) {
node = node.parent
}
const parent = node.parent
if (parent.type === "MemberExpression") {
if (parent.object === node) {
const key = getPropertyName(parent)
if (key == null || !has(traceMap, key)) {
return
}
path = path.concat(key) //eslint-disable-line no-param-reassign
const nextTraceMap = traceMap[key]
if (nextTraceMap[READ]) {
yield {
node: parent,
path,
type: READ,
info: nextTraceMap[READ],
}
}
yield* this._iteratePropertyReferences(
parent,
path,
nextTraceMap
)
}
return
}
if (parent.type === "CallExpression") {
if (parent.callee === node && traceMap[CALL]) {
yield { node: parent, path, type: CALL, info: traceMap[CALL] }
}
return
}
if (parent.type === "NewExpression") {
if (parent.callee === node && traceMap[CONSTRUCT]) {
yield {
node: parent,
path,
type: CONSTRUCT,
info: traceMap[CONSTRUCT],
}
}
return
}
if (parent.type === "AssignmentExpression") {
if (parent.right === node) {
yield* this._iterateLhsReferences(parent.left, path, traceMap)
yield* this._iteratePropertyReferences(parent, path, traceMap)
}
return
}
if (parent.type === "AssignmentPattern") {
if (parent.right === node) {
yield* this._iterateLhsReferences(parent.left, path, traceMap)
}
return
}
if (parent.type === "VariableDeclarator") {
if (parent.init === node) {
yield* this._iterateLhsReferences(parent.id, path, traceMap)
}
}
}
/**
* Iterate the references for a given Pattern node.
* @param {Node} patternNode The Pattern node to iterate references.
* @param {string[]} path The current path.
* @param {object} traceMap The trace map.
* @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
*/
*_iterateLhsReferences(patternNode, path, traceMap) {
if (patternNode.type === "Identifier") {
const variable = findVariable(this.globalScope, patternNode)
if (variable != null) {
yield* this._iterateVariableReferences(
variable,
path,
traceMap,
false
)
}
return
}
if (patternNode.type === "ObjectPattern") {
for (const property of patternNode.properties) {
const key = getPropertyName(property)
if (key == null || !has(traceMap, key)) {
continue
}
const nextPath = path.concat(key)
const nextTraceMap = traceMap[key]
if (nextTraceMap[READ]) {
yield {
node: property,
path: nextPath,
type: READ,
info: nextTraceMap[READ],
}
}
yield* this._iterateLhsReferences(
property.value,
nextPath,
nextTraceMap
)
}
return
}
if (patternNode.type === "AssignmentPattern") {
yield* this._iterateLhsReferences(patternNode.left, path, traceMap)
}
}
/**
* Iterate the references for a given ModuleSpecifier node.
* @param {Node} specifierNode The ModuleSpecifier node to iterate references.
* @param {string[]} path The current path.
* @param {object} traceMap The trace map.
* @returns {IterableIterator<{node:Node,path:string[],type:symbol,info:any}>} The iterator to iterate references.
*/
*_iterateImportReferences(specifierNode, path, traceMap) {
const type = specifierNode.type
if (type === "ImportSpecifier" || type === "ImportDefaultSpecifier") {
const key =
type === "ImportDefaultSpecifier"
? "default"
: specifierNode.imported.name
if (!has(traceMap, key)) {
return
}
path = path.concat(key) //eslint-disable-line no-param-reassign
const nextTraceMap = traceMap[key]
if (nextTraceMap[READ]) {
yield {
node: specifierNode,
path,
type: READ,
info: nextTraceMap[READ],
}
}
yield* this._iterateVariableReferences(
findVariable(this.globalScope, specifierNode.local),
path,
nextTraceMap,
false
)
return
}
if (type === "ImportNamespaceSpecifier") {
yield* this._iterateVariableReferences(
findVariable(this.globalScope, specifierNode.local),
path,
traceMap,
false
)
return
}
if (type === "ExportSpecifier") {
const key = specifierNode.local.name
if (!has(traceMap, key)) {
return
}
path = path.concat(key) //eslint-disable-line no-param-reassign
const nextTraceMap = traceMap[key]
if (nextTraceMap[READ]) {
yield {
node: specifierNode,
path,
type: READ,
info: nextTraceMap[READ],
}
}
}
}
}
ReferenceTracker.READ = READ
ReferenceTracker.CALL = CALL
ReferenceTracker.CONSTRUCT = CONSTRUCT
ReferenceTracker.ESM = ESM
/**
* This is a predicate function for Array#filter.
* @param {string} name A name part.
* @param {number} index The index of the name.
* @returns {boolean} `false` if it's default.
*/
function exceptDefault(name, index) {
return !(index === 1 && name === "default")
}
eslint-utils-2.1.0/src/token-predicate.js 0000664 0000000 0000000 00000010517 13671663521 0020367 0 ustar 00root root 0000000 0000000 /**
* Negate the result of `this` calling.
* @param {Token} token The token to check.
* @returns {boolean} `true` if the result of `this(token)` is `false`.
*/
function negate0(token) {
return !this(token) //eslint-disable-line no-invalid-this
}
/**
* Creates the negate function of the given function.
* @param {function(Token):boolean} f - The function to negate.
* @returns {function(Token):boolean} Negated function.
*/
function negate(f) {
return negate0.bind(f)
}
/**
* Checks if the given token is an arrow token or not.
* @param {Token} token - The token to check.
* @returns {boolean} `true` if the token is an arrow token.
*/
export function isArrowToken(token) {
return token.value === "=>" && token.type === "Punctuator"
}
/**
* Checks if the given token is a comma token or not.
* @param {Token} token - The token to check.
* @returns {boolean} `true` if the token is a comma token.
*/
export function isCommaToken(token) {
return token.value === "," && token.type === "Punctuator"
}
/**
* Checks if the given token is a semicolon token or not.
* @param {Token} token - The token to check.
* @returns {boolean} `true` if the token is a semicolon token.
*/
export function isSemicolonToken(token) {
return token.value === ";" && token.type === "Punctuator"
}
/**
* Checks if the given token is a colon token or not.
* @param {Token} token - The token to check.
* @returns {boolean} `true` if the token is a colon token.
*/
export function isColonToken(token) {
return token.value === ":" && token.type === "Punctuator"
}
/**
* Checks if the given token is an opening parenthesis token or not.
* @param {Token} token - The token to check.
* @returns {boolean} `true` if the token is an opening parenthesis token.
*/
export function isOpeningParenToken(token) {
return token.value === "(" && token.type === "Punctuator"
}
/**
* Checks if the given token is a closing parenthesis token or not.
* @param {Token} token - The token to check.
* @returns {boolean} `true` if the token is a closing parenthesis token.
*/
export function isClosingParenToken(token) {
return token.value === ")" && token.type === "Punctuator"
}
/**
* Checks if the given token is an opening square bracket token or not.
* @param {Token} token - The token to check.
* @returns {boolean} `true` if the token is an opening square bracket token.
*/
export function isOpeningBracketToken(token) {
return token.value === "[" && token.type === "Punctuator"
}
/**
* Checks if the given token is a closing square bracket token or not.
* @param {Token} token - The token to check.
* @returns {boolean} `true` if the token is a closing square bracket token.
*/
export function isClosingBracketToken(token) {
return token.value === "]" && token.type === "Punctuator"
}
/**
* Checks if the given token is an opening brace token or not.
* @param {Token} token - The token to check.
* @returns {boolean} `true` if the token is an opening brace token.
*/
export function isOpeningBraceToken(token) {
return token.value === "{" && token.type === "Punctuator"
}
/**
* Checks if the given token is a closing brace token or not.
* @param {Token} token - The token to check.
* @returns {boolean} `true` if the token is a closing brace token.
*/
export function isClosingBraceToken(token) {
return token.value === "}" && token.type === "Punctuator"
}
/**
* Checks if the given token is a comment token or not.
* @param {Token} token - The token to check.
* @returns {boolean} `true` if the token is a comment token.
*/
export function isCommentToken(token) {
return (
token.type === "Line" ||
token.type === "Block" ||
token.type === "Shebang"
)
}
export const isNotArrowToken = negate(isArrowToken)
export const isNotCommaToken = negate(isCommaToken)
export const isNotSemicolonToken = negate(isSemicolonToken)
export const isNotColonToken = negate(isColonToken)
export const isNotOpeningParenToken = negate(isOpeningParenToken)
export const isNotClosingParenToken = negate(isClosingParenToken)
export const isNotOpeningBracketToken = negate(isOpeningBracketToken)
export const isNotClosingBracketToken = negate(isClosingBracketToken)
export const isNotOpeningBraceToken = negate(isOpeningBraceToken)
export const isNotClosingBraceToken = negate(isClosingBraceToken)
export const isNotCommentToken = negate(isCommentToken)
eslint-utils-2.1.0/test/ 0000775 0000000 0000000 00000000000 13671663521 0015137 5 ustar 00root root 0000000 0000000 eslint-utils-2.1.0/test/find-variable.js 0000664 0000000 0000000 00000005607 13671663521 0020210 0 ustar 00root root 0000000 0000000 import assert from "assert"
import eslint from "eslint"
import { findVariable } from "../src/"
describe("The 'findVariable' function", () => {
function getVariable(code, selector, withString = null) {
const linter = new eslint.Linter()
let variable = null
linter.defineRule("test", context => ({
[selector](node) {
variable = findVariable(context.getScope(), withString || node)
},
}))
linter.verify(code, {
parserOptions: { ecmaVersion: 2018 },
rules: { test: "error" },
})
return variable
}
describe("should return the variable of a given Identifier node", () => {
it("from the same scope.", () => {
const variable = getVariable(
"let a; foo(a)",
"CallExpression Identifier[name='a']"
)
assert.strictEqual(variable.name, "a")
})
it("from nested blocks.", () => {
const variable = getVariable(
"let a; if (b) { foo(a) }",
"CallExpression Identifier[name='a']"
)
assert.strictEqual(variable.name, "a")
})
it("from function blocks.", () => {
const variable = getVariable(
"let a; function f() { foo(a) }",
"CallExpression Identifier[name='a']"
)
assert.strictEqual(variable.name, "a")
})
})
describe("should return the variable of a given Identifier node", () => {
it("from the same scope.", () => {
const variable = getVariable(
"let a; foo(a)",
"CallExpression Identifier[name='a']",
"a"
)
assert.strictEqual(variable.name, "a")
})
it("from nested blocks.", () => {
const variable = getVariable(
"let a; if (b) { foo(a) }",
"CallExpression Identifier[name='a']",
"a"
)
assert.strictEqual(variable.name, "a")
})
it("from function blocks.", () => {
const variable = getVariable(
"let a; function f() { foo(a) }",
"CallExpression Identifier[name='a']",
"a"
)
assert.strictEqual(variable.name, "a")
})
})
it("should return global variables.", () => {
const variable = getVariable(
"let a; function f() { foo(a) }",
"CallExpression Identifier[name='a']",
"Object"
)
assert.strictEqual(variable.name, "Object")
})
it("should return null if it didn't exist.", () => {
const variable = getVariable(
"let a; function f() { foo(a) }",
"CallExpression Identifier[name='a']",
"x"
)
assert.strictEqual(variable, null)
})
})
eslint-utils-2.1.0/test/get-function-head-location.js 0000664 0000000 0000000 00000006216 13671663521 0022611 0 ustar 00root root 0000000 0000000 import assert from "assert"
import eslint from "eslint"
import { getFunctionHeadLocation } from "../src/"
describe("The 'getFunctionHeadLocation' function", () => {
const expectedResults = {
"function foo() {}": [0, 12],
"(function foo() {})": [1, 13],
"(function() {})": [1, 9],
"function* foo() {}": [0, 13],
"(function* foo() {})": [1, 14],
"(function*() {})": [1, 10],
"() => {}": [3, 5],
"async () => {}": [9, 11],
"({ foo: function foo() {} })": [3, 20],
"({ foo: function() {} })": [3, 16],
"({ ['foo']: function() {} })": [3, 20],
"({ [foo]: function() {} })": [3, 18],
"({ foo() {} })": [3, 6],
"({ foo: function* foo() {} })": [3, 21],
"({ foo: function*() {} })": [3, 17],
"({ ['foo']: function*() {} })": [3, 21],
"({ [foo]: function*() {} })": [3, 19],
"({ *foo() {} })": [3, 7],
"({ foo: async function foo() {} })": [3, 26],
"({ foo: async function() {} })": [3, 22],
"({ ['foo']: async function() {} })": [3, 26],
"({ [foo]: async function() {} })": [3, 24],
"({ async foo() {} })": [3, 12],
"({ get foo() {} })": [3, 10],
"({ set foo(a) {} })": [3, 10],
"class A { constructor() {} }": [10, 21],
"class A { foo() {} }": [10, 13],
"class A { *foo() {} }": [10, 14],
"class A { async foo() {} }": [10, 19],
"class A { ['foo']() {} }": [10, 17],
"class A { *['foo']() {} }": [10, 18],
"class A { async ['foo']() {} }": [10, 23],
"class A { [foo]() {} }": [10, 15],
"class A { *[foo]() {} }": [10, 16],
"class A { async [foo]() {} }": [10, 21],
"class A { get foo() {} }": [10, 17],
"class A { set foo(a) {} }": [10, 17],
"class A { static foo() {} }": [10, 20],
"class A { static *foo() {} }": [10, 21],
"class A { static async foo() {} }": [10, 26],
"class A { static get foo() {} }": [10, 24],
"class A { static set foo(a) {} }": [10, 24],
}
for (const key of Object.keys(expectedResults)) {
const expectedLoc = {
start: {
line: 1,
column: expectedResults[key][0],
},
end: {
line: 1,
column: expectedResults[key][1],
},
}
it(`should return "${JSON.stringify(
expectedLoc
)}" for "${key}".`, () => {
const linter = new eslint.Linter()
let actualLoc = null
linter.defineRule("test", context => ({
":function"(node) {
actualLoc = getFunctionHeadLocation(
node,
context.getSourceCode()
)
},
}))
linter.verify(
key,
{
rules: { test: "error" },
parserOptions: { ecmaVersion: 2018 },
},
"test.js",
true
)
assert.deepStrictEqual(actualLoc, expectedLoc)
})
}
})
eslint-utils-2.1.0/test/get-function-name-with-kind.js 0000664 0000000 0000000 00000006724 13671663521 0022722 0 ustar 00root root 0000000 0000000 import assert from "assert"
import eslint from "eslint"
import { getFunctionNameWithKind } from "../src/"
describe("The 'getFunctionNameWithKind' function", () => {
const expectedResults = {
"function foo() {}": "function 'foo'",
"(function foo() {})": "function 'foo'",
"(function() {})": "function",
"function* foo() {}": "generator function 'foo'",
"(function* foo() {})": "generator function 'foo'",
"(function*() {})": "generator function",
"() => {}": "arrow function",
"async () => {}": "async arrow function",
"const foo = () => {}": "arrow function 'foo'",
"const foo = async () => {}": "async arrow function 'foo'",
"foo = () => {}": "arrow function 'foo'",
"foo = async () => {}": "async arrow function 'foo'",
"foo.bar = () => {}": "arrow function",
"foo.bar = async () => {}": "async arrow function",
"({ foo: function foo() {} })": "method 'foo'",
"({ foo: function() {} })": "method 'foo'",
"({ ['foo']: function() {} })": "method 'foo'",
"({ [foo]: function() {} })": "method",
"({ foo() {} })": "method 'foo'",
"({ foo: function* foo() {} })": "generator method 'foo'",
"({ foo: function*() {} })": "generator method 'foo'",
"({ ['foo']: function*() {} })": "generator method 'foo'",
"({ [foo]: function*() {} })": "generator method",
"({ *foo() {} })": "generator method 'foo'",
"({ foo: async function foo() {} })": "async method 'foo'",
"({ foo: async function() {} })": "async method 'foo'",
"({ ['foo']: async function() {} })": "async method 'foo'",
"({ [foo]: async function() {} })": "async method",
"({ async foo() {} })": "async method 'foo'",
"({ get foo() {} })": "getter 'foo'",
"({ set foo(a) {} })": "setter 'foo'",
"class A { constructor() {} }": "constructor",
"class A { foo() {} }": "method 'foo'",
"class A { *foo() {} }": "generator method 'foo'",
"class A { async foo() {} }": "async method 'foo'",
"class A { ['foo']() {} }": "method 'foo'",
"class A { *['foo']() {} }": "generator method 'foo'",
"class A { async ['foo']() {} }": "async method 'foo'",
"class A { [foo]() {} }": "method",
"class A { *[foo]() {} }": "generator method",
"class A { async [foo]() {} }": "async method",
"class A { get foo() {} }": "getter 'foo'",
"class A { set foo(a) {} }": "setter 'foo'",
"class A { static foo() {} }": "static method 'foo'",
"class A { static *foo() {} }": "static generator method 'foo'",
"class A { static async foo() {} }": "static async method 'foo'",
"class A { static get foo() {} }": "static getter 'foo'",
"class A { static set foo(a) {} }": "static setter 'foo'",
}
for (const key of Object.keys(expectedResults)) {
it(`should return "${expectedResults[key]}" for "${key}".`, () => {
const linter = new eslint.Linter()
let actualResult = null
linter.defineRule("test", () => ({
":function"(node) {
actualResult = getFunctionNameWithKind(node)
},
}))
linter.verify(key, {
rules: { test: "error" },
parserOptions: { ecmaVersion: 2018 },
})
assert.strictEqual(actualResult, expectedResults[key])
})
}
})
eslint-utils-2.1.0/test/get-innermost-scope.js 0000664 0000000 0000000 00000005261 13671663521 0021403 0 ustar 00root root 0000000 0000000 import assert from "assert"
import eslint from "eslint"
import { getInnermostScope } from "../src/"
describe("The 'getInnermostScope' function", () => {
let i = 0
for (const { code, parserOptions, selectNode, selectScope } of [
{
code: "let a = 0",
parserOptions: {},
selectNode: node => node,
selectScope: scope => scope,
},
{
code: "let a = 0",
parserOptions: { ecmaFeatures: { globalReturn: true } },
selectNode: node => node,
selectScope: scope => scope.childScopes[0],
},
{
code: "let a = 0",
parserOptions: { sourceType: "module" },
selectNode: node => node,
selectScope: scope => scope.childScopes[0],
},
{
code: "a; { b; { c; } d; } e;",
parserOptions: {},
selectNode: node => node.body[0],
selectScope: scope => scope,
},
{
code: "a; { b; { c; } d; } e;",
parserOptions: {},
selectNode: node => node.body[2],
selectScope: scope => scope,
},
{
code: "a; { b; { c; } d; } e;",
parserOptions: {},
selectNode: node => node.body[1].body[0],
selectScope: scope => scope.childScopes[0],
},
{
code: "a; { b; { c; } d; } e;",
parserOptions: {},
selectNode: node => node.body[1].body[2],
selectScope: scope => scope.childScopes[0],
},
{
code: "a; { b; { c; } d; } e;",
parserOptions: {},
selectNode: node => node.body[1].body[1].body[0],
selectScope: scope => scope.childScopes[0].childScopes[0],
},
]) {
it(`should return the innermost scope (${++i})`, () => {
const linter = new eslint.Linter()
let actualScope = null
let expectedScope = null
linter.defineRule("test", context => ({
Program(node) {
const scope = context.getScope()
actualScope = getInnermostScope(scope, selectNode(node))
expectedScope = selectScope(scope)
},
}))
linter.verify(code, {
parserOptions: Object.assign(
{ ecmaVersion: 2018 },
parserOptions
),
rules: { test: "error" },
})
assert.notStrictEqual(expectedScope, null)
// assert.strictEqual makes tooooo large diff.
assert(actualScope === expectedScope)
})
}
})
eslint-utils-2.1.0/test/get-property-name.js 0000664 0000000 0000000 00000004536 13671663521 0021064 0 ustar 00root root 0000000 0000000 import assert from "assert"
import eslint from "eslint"
import { getPropertyName } from "../src/"
describe("The 'getPropertyName' function", () => {
for (const { code, expected } of [
{ code: "a.b", expected: "b" },
{ code: "a['b']", expected: "b" },
{ code: "a[`b`]", expected: "b" },
{ code: "a[100]", expected: "100" },
{ code: "a[b]", expected: null },
{ code: "a['a' + 'b']", expected: "ab" },
{ code: "a[tag`b`]", expected: null },
{ code: "a[`${b}`]", expected: null }, //eslint-disable-line no-template-curly-in-string
{ code: "({b: 1})", expected: "b" },
{ code: "({0x10: 1})", expected: "16" },
{ code: "({'foo': 1})", expected: "foo" },
{ code: "({b() {}})", expected: "b" },
{ code: "({get b() {}})", expected: "b" },
{ code: "({['b']: 1})", expected: "b" },
{ code: "({['b']() {}})", expected: "b" },
{ code: "({[`b`]: 1})", expected: "b" },
{ code: "({[100]: 1})", expected: "100" },
{ code: "({[b]: 1})", expected: null },
{ code: "({['a' + 'b']: 1})", expected: "ab" },
{ code: "({[tag`b`]: 1})", expected: null },
{ code: "({[`${b}`]: 1})", expected: null }, //eslint-disable-line no-template-curly-in-string
{ code: "(class {b() {}})", expected: "b" },
{ code: "(class {get b() {}})", expected: "b" },
{ code: "(class {['b']() {}})", expected: "b" },
{ code: "(class {[100]() {}})", expected: "100" },
{ code: "(class {[b]() {}})", expected: null },
{ code: "(class {['a' + 'b']() {}})", expected: "ab" },
{ code: "(class {[tag`b`]() {}})", expected: null },
{ code: "(class {[`${b}`]() {}})", expected: null }, //eslint-disable-line no-template-curly-in-string
]) {
it(`should return ${JSON.stringify(expected)} from ${code}`, () => {
const linter = new eslint.Linter()
let actual = null
linter.defineRule("test", () => ({
"Property,MethodDefinition,MemberExpression"(node) {
actual = getPropertyName(node)
},
}))
linter.verify(code, {
parserOptions: { ecmaVersion: 2018 },
rules: { test: "error" },
})
assert.strictEqual(actual, expected)
})
}
})
eslint-utils-2.1.0/test/get-static-value.js 0000664 0000000 0000000 00000026247 13671663521 0020666 0 ustar 00root root 0000000 0000000 import assert from "assert"
import eslint from "eslint"
import semver from "semver"
import { getStaticValue } from "../src/"
describe("The 'getStaticValue' function", () => {
for (const { code, expected, noScope = false } of [
{ code: "[]", expected: { value: [] } },
{ code: "[1, 2, 3]", expected: { value: [1, 2, 3] } },
{ code: "[,, 3]", expected: { value: [, , 3] } }, //eslint-disable-line no-sparse-arrays
{ code: "[1, ...[2, 3]]", expected: { value: [1, 2, 3] } },
{ code: "[0, a]", expected: null },
{ code: "[0, ...a]", expected: null },
{ code: "a = 1 + 2", expected: { value: 3 } },
{ code: "a += 1 + 2", expected: null },
{ code: "a in obj", expected: null },
{ code: "obj instanceof Object", expected: null },
{ code: "1 == '1'", expected: { value: true } },
{ code: "1 != '1'", expected: { value: false } },
{ code: "1 === '1'", expected: { value: false } },
{ code: "1 !== '1'", expected: { value: true } },
{ code: "1 < '1'", expected: { value: false } },
{ code: "1 <= '1'", expected: { value: true } },
{ code: "1 > '1'", expected: { value: false } },
{ code: "1 >= '1'", expected: { value: true } },
{ code: "1 << '1'", expected: { value: 2 } },
{ code: "1 >> '1'", expected: { value: 0 } },
{ code: "1 >>> '1'", expected: { value: 0 } },
{ code: "1 + '1'", expected: { value: "11" } },
{ code: "1 + 2", expected: { value: 3 } },
{ code: "1 - 2", expected: { value: -1 } },
{ code: "1 * 2", expected: { value: 2 } },
{ code: "1 / 2", expected: { value: 0.5 } },
{ code: "1 % 2", expected: { value: 1 } },
{ code: "2 ** 2", expected: { value: 4 } },
{ code: "1 | 2", expected: { value: 3 } },
{ code: "1 ^ 15", expected: { value: 14 } },
{ code: "3 & 2", expected: { value: 2 } },
{ code: "a + 1", expected: null },
{ code: "String(7)", expected: { value: "7" } },
{ code: "Math.round(0.7)", expected: { value: 1 } },
{ code: "Math['round'](0.4)", expected: { value: 0 } },
{ code: "foo(7)", expected: null },
{ code: "obj.foo(7)", expected: null },
{ code: "Math.round(a)", expected: null },
{ code: "true ? 1 : c", expected: { value: 1 } },
{ code: "false ? b : 2", expected: { value: 2 } },
{ code: "a ? 1 : 2", expected: null },
{ code: "true ? b : 2", expected: null },
{ code: "false ? 1 : c", expected: null },
{ code: "undefined", expected: { value: undefined } },
{ code: "var undefined; undefined", expected: null },
{ code: "const undefined = 1; undefined", expected: { value: 1 } },
{ code: "const a = 2; a", expected: { value: 2 } },
{ code: "let a = 2; a", expected: null },
{ code: "const a = 2; a", expected: null, noScope: true },
{ code: "const a = { b: 7 }; a.b", expected: { value: 7 } },
{ code: "null", expected: { value: null } },
{ code: "true", expected: { value: true } },
{ code: "false", expected: { value: false } },
{ code: "1", expected: { value: 1 } },
{ code: "'hello'", expected: { value: "hello" } },
{ code: "/foo/gu", expected: { value: /foo/gu } },
{ code: "true && 1", expected: { value: 1 } },
{ code: "false && a", expected: { value: false } },
{ code: "true || a", expected: { value: true } },
{ code: "false || 2", expected: { value: 2 } },
{ code: "true && a", expected: null },
{ code: "false || a", expected: null },
{ code: "a && 1", expected: null },
{ code: "Symbol.iterator", expected: { value: Symbol.iterator } },
{
code: "Symbol['iter' + 'ator']",
expected: { value: Symbol.iterator },
},
{ code: "Symbol[iterator]", expected: null },
{ code: "Object.freeze", expected: { value: Object.freeze } },
{ code: "Object.xxx", expected: { value: undefined } },
{ code: "new Array(2)", expected: null },
{ code: "new Array(len)", expected: null },
{ code: "({})", expected: { value: {} } },
{
code: "({a: 1, b: 2, c: 3})",
expected: { value: { a: 1, b: 2, c: 3 } },
},
{
code: "const obj = {b: 2}; ({a: 1, ...obj})",
expected: { value: { a: 1, b: 2 } },
},
{ code: "var obj = {b: 2}; ({a: 1, ...obj})", expected: null },
{ code: "({ get a() {} })", expected: null },
{ code: "({ a })", expected: null },
{ code: "({ a: b })", expected: null },
{ code: "({ [a]: 1 })", expected: null },
{ code: "(a, b, 3)", expected: { value: 3 } },
{ code: "(1, b)", expected: null },
{ code: "`hello`", expected: { value: "hello" } },
{ code: "const ll = 'll'; `he${ll}o`", expected: { value: "hello" } }, //eslint-disable-line no-template-curly-in-string
{ code: "String.raw`\\unicode`", expected: { value: "\\unicode" } },
{ code: "`he${a}o`", expected: null }, //eslint-disable-line no-template-curly-in-string
{ code: "x`hello`", expected: null },
{ code: "-1", expected: { value: -1 } },
{ code: "+'1'", expected: { value: 1 } },
{ code: "!0", expected: { value: true } },
{ code: "~-1", expected: { value: 0 } },
{ code: "typeof 0", expected: { value: "number" } },
{ code: "void a.b", expected: { value: undefined } },
{ code: "+a", expected: null },
{ code: "delete a.b", expected: null },
{ code: "!function(){ return true }", expected: null },
{ code: "'' + Symbol()", expected: null },
{
code: `const eventName = "click"
const aMap = Object.freeze({
click: 777
})
;\`on\${eventName} : \${aMap[eventName]}\``,
expected: { value: "onclick : 777" },
},
{
code: 'Function("return process.env.npm_name")()',
expected: null,
},
{
code: 'new Function("return process.env.npm_name")()',
expected: null,
},
{
code:
'({}.constructor.constructor("return process.env.npm_name")())',
expected: null,
},
{
code:
'JSON.stringify({a:1}, new {}.constructor.constructor("console.log(\\"code injected\\"); process.exit(1)"), 2)',
expected: null,
},
{
code:
'Object.create(null, {a:{get:new {}.constructor.constructor("console.log(\\"code injected\\"); process.exit(1)")}}).a',
expected: null,
},
{
code: "RegExp.$1",
expected: null,
},
...(semver.gte(eslint.CLIEngine.version, "6.0.0")
? [
{
code: "const a = null, b = 42; a ?? b",
expected: { value: 42 },
},
{
code: "const a = undefined, b = 42; a ?? b",
expected: { value: 42 },
},
{
code: "const a = false, b = 42; a ?? b",
expected: { value: false },
},
{
code: "const a = 42, b = null; a ?? b",
expected: { value: 42 },
},
{
code: "const a = 42, b = undefined; a ?? b",
expected: { value: 42 },
},
{
code: "const a = { b: { c: 42 } }; a?.b?.c",
expected: { value: 42 },
},
{
code: "const a = { b: { c: 42 } }; a?.b?.['c']",
expected: { value: 42 },
},
{
code: "const a = { b: null }; a?.b?.c",
expected: { value: undefined },
},
{
code: "const a = { b: undefined }; a?.b?.c",
expected: { value: undefined },
},
{
code: "const a = { b: null }; a?.b?.['c']",
expected: { value: undefined },
},
{
code: "const a = null; a?.b?.c",
expected: { value: undefined },
},
{
code: "const a = null; a?.b.c",
expected: { value: undefined },
},
{
code: "const a = void 0; a?.b.c",
expected: { value: undefined },
},
{
code: "const a = { b: { c: 42 } }; (a?.b).c",
expected: { value: 42 },
},
{
code: "const a = null; (a?.b).c",
expected: null,
},
{
code: "const a = { b: null }; (a?.b).c",
expected: null,
},
{
code: "const a = { b: { c: String } }; a?.b?.c?.(42)",
expected: { value: "42" },
},
{
code: "const a = null; a?.b?.c?.(42)",
expected: { value: undefined },
},
{
code: "const a = { b: { c: String } }; a?.b.c(42)",
expected: { value: "42" },
},
{
code: "const a = null; a?.b.c(42)",
expected: { value: undefined },
},
{
code: "null?.()",
expected: { value: undefined },
},
{
code: "const a = null; a?.()",
expected: { value: undefined },
},
{
code: "a?.()",
expected: null,
},
]
: []),
]) {
it(`should return ${JSON.stringify(expected)} from ${code}`, () => {
const linter = new eslint.Linter()
let actual = null
linter.defineRule("test", context => ({
ExpressionStatement(node) {
actual = getStaticValue(
node,
noScope ? null : context.getScope()
)
},
}))
linter.verify(code, {
env: { es6: true },
parserOptions: {
ecmaVersion: semver.gte(eslint.CLIEngine.version, "6.0.0")
? 2020
: 2018,
},
rules: { test: "error" },
})
if (actual == null) {
assert.strictEqual(actual, expected)
} else {
assert.deepStrictEqual(actual, expected)
}
})
}
})
eslint-utils-2.1.0/test/get-string-if-constant.js 0000664 0000000 0000000 00000005113 13671663521 0022003 0 ustar 00root root 0000000 0000000 import assert from "assert"
import eslint from "eslint"
import { getStringIfConstant } from "../src/"
describe("The 'getStringIfConstant' function", () => {
for (const { code, expected } of [
{ code: "true", expected: "true" },
{ code: "false", expected: "false" },
{ code: "0x100", expected: "256" },
{ code: "3.14e+2", expected: "314" },
{ code: '"test"', expected: "test" },
{ code: "'abc'", expected: "abc" },
{ code: "`abc`", expected: "abc" },
{ code: "null", expected: "null" },
{ code: "/a/", expected: "/a/" },
{ code: "/a/g", expected: "/a/g" },
{ code: "id", expected: null },
{ code: "tag`foo`", expected: null },
{ code: "`aaa${id}bbb`", expected: null }, //eslint-disable-line no-template-curly-in-string
{ code: "1 + 2", expected: "3" },
{ code: "'a' + 'b'", expected: "ab" },
{ code: "/(?\\w+)\\k/gu", expected: "/(?\\w+)\\k/gu" },
]) {
it(`should return ${JSON.stringify(expected)} from ${code}`, () => {
const linter = new eslint.Linter()
let actual = null
linter.defineRule("test", () => ({
"Program > ExpressionStatement > *"(node) {
actual = getStringIfConstant(node)
},
}))
linter.verify(code, {
parserOptions: { ecmaVersion: 2018 },
rules: { test: "error" },
})
assert.strictEqual(actual, expected)
})
}
describe("with the 2nd argument 'initialScope',", () => {
for (const { code, expected } of [
{ code: "id", expected: null },
{ code: "const id = 'abc'; id", expected: "abc" },
{ code: "let id = 'abc'; id", expected: null },
{ code: "var id = 'abc'; id", expected: null },
{ code: "const id = otherId; id", expected: null },
]) {
it(`should return ${JSON.stringify(expected)} from ${code}`, () => {
const linter = new eslint.Linter()
let actual = null
linter.defineRule("test", context => ({
"Program > ExpressionStatement > *"(node) {
actual = getStringIfConstant(node, context.getScope())
},
}))
linter.verify(code, {
parserOptions: { ecmaVersion: 2018 },
rules: { test: "error" },
})
assert.strictEqual(actual, expected)
})
}
})
})
eslint-utils-2.1.0/test/has-side-effect.js 0000664 0000000 0000000 00000020607 13671663521 0020431 0 ustar 00root root 0000000 0000000 import assert from "assert"
import eslint from "eslint"
import semver from "semver"
import dp from "dot-prop"
import { hasSideEffect } from "../src/"
describe("The 'hasSideEffect' function", () => {
for (const { code, key = "body.0.expression", options, expected } of [
{
code: "777",
options: undefined,
expected: false,
},
{
code: "foo",
options: undefined,
expected: false,
},
{
code: "a = 0",
options: undefined,
expected: true,
},
{
code: "async function f() { await g }",
key: "body.0.body.body.0.expression",
options: undefined,
expected: true,
},
{
code: "a + b",
options: undefined,
expected: false,
},
{
code: "a + b",
options: { considerImplicitTypeConversion: true },
expected: true,
},
{
code: "1 + 2",
options: { considerImplicitTypeConversion: true },
expected: false,
},
{
code: "f()",
options: undefined,
expected: true,
},
...(semver.gte(eslint.CLIEngine.version, "6.0.0")
? [
{
code: "f?.()",
options: undefined,
expected: true,
},
]
: []),
{
code: "a + f()",
options: undefined,
expected: true,
},
...(semver.gte(eslint.CLIEngine.version, "6.0.0")
? [
{
code: "a + f?.()",
options: undefined,
expected: true,
},
]
: []),
{
code: "obj.a",
options: undefined,
expected: false,
},
{
code: "obj.a",
options: { considerGetters: true },
expected: true,
},
...(semver.gte(eslint.CLIEngine.version, "6.0.0")
? [
{
code: "obj?.a",
options: undefined,
expected: false,
},
{
code: "obj?.a",
options: { considerGetters: true },
expected: true,
},
]
: []),
{
code: "obj[a]",
options: undefined,
expected: false,
},
{
code: "obj[a]",
options: { considerGetters: true },
expected: true,
},
{
code: "obj[a]",
options: { considerImplicitTypeConversion: true },
expected: true,
},
...(semver.gte(eslint.CLIEngine.version, "6.0.0")
? [
{
code: "obj?.[a]",
options: undefined,
expected: false,
},
{
code: "obj?.[a]",
options: { considerGetters: true },
expected: true,
},
{
code: "obj?.[a]",
options: { considerImplicitTypeConversion: true },
expected: true,
},
]
: []),
{
code: "obj[0]",
options: { considerImplicitTypeConversion: true },
expected: false,
},
{
code: "obj['@@abc']",
options: { considerImplicitTypeConversion: true },
expected: false,
},
{
code: "(class { f() { a++ } })",
options: undefined,
expected: false,
},
{
code: "(class { f() { a++ } })",
options: { considerImplicitTypeConversion: true },
expected: false,
},
{
code: "(class { [f]() { a++ } })",
options: undefined,
expected: false,
},
{
code: "(class { [f]() { a++ } })",
options: { considerImplicitTypeConversion: true },
expected: true,
},
{
code: "(class { [0]() { a++ } })",
options: { considerImplicitTypeConversion: true },
expected: false,
},
{
code: "(class { ['@@']() { a++ } })",
options: { considerImplicitTypeConversion: true },
expected: false,
},
{
code: "new C()",
options: undefined,
expected: true,
},
{
code: "({ key: 1 })",
options: undefined,
expected: false,
},
{
code: "({ key: 1 })",
options: { considerImplicitTypeConversion: true },
expected: false,
},
{
code: "({ [key]: 1 })",
options: undefined,
expected: false,
},
{
code: "({ [key]: 1 })",
options: { considerImplicitTypeConversion: true },
expected: true,
},
{
code: "({ [0]: 1 })",
options: { considerImplicitTypeConversion: true },
expected: false,
},
{
code: "({ ['@@']: 1 })",
options: { considerImplicitTypeConversion: true },
expected: false,
},
{
code: "+1",
options: undefined,
expected: false,
},
{
code: "+1",
options: { considerImplicitTypeConversion: true },
expected: false,
},
{
code: "-1",
options: undefined,
expected: false,
},
{
code: "-1",
options: { considerImplicitTypeConversion: true },
expected: false,
},
{
code: "+a",
options: undefined,
expected: false,
},
{
code: "+a",
options: { considerImplicitTypeConversion: true },
expected: true,
},
{
code: "delete obj.a",
options: undefined,
expected: true,
},
{
code: "++a",
options: undefined,
expected: true,
},
{
code: "function* g() { yield 1 }",
key: "body.0.body.body.0.expression",
options: undefined,
expected: true,
},
{
code: "(a, b, c)",
options: undefined,
expected: false,
},
{
code: "(a, b, ++c)",
options: undefined,
expected: true,
},
// Skip the definition body.
{
code: "(function f(a) { a++ })",
options: undefined,
expected: false,
},
{
code: "((a) => { a++ })",
options: undefined,
expected: false,
},
{
code: "((a) => a++)",
options: undefined,
expected: false,
},
]) {
it(`should return ${expected} on the code \`${code}\` and the options \`${JSON.stringify(
options
)}\``, () => {
const linter = new eslint.Linter()
let actual = null
linter.defineRule("test", context => ({
Program(node) {
actual = hasSideEffect(
dp.get(node, key),
context.getSourceCode(),
options
)
},
}))
const messages = linter.verify(code, {
env: { es6: true },
parserOptions: {
ecmaVersion: semver.gte(eslint.CLIEngine.version, "6.0.0")
? 2020
: 2018,
},
rules: { test: "error" },
})
assert.strictEqual(
messages.length,
0,
messages[0] && messages[0].message
)
assert.strictEqual(actual, expected)
})
}
})
eslint-utils-2.1.0/test/is-parenthesized.js 0000664 0000000 0000000 00000021673 13671663521 0020764 0 ustar 00root root 0000000 0000000 import assert from "assert"
import dotProp from "dot-prop"
import eslint from "eslint"
import { isParenthesized } from "../src/"
describe("The 'isParenthesized' function", () => {
for (const { code, expected } of [
{
code: "777",
expected: {
"body.0": false,
"body.0.expression": false,
},
},
{
code: "(777)",
expected: {
"body.0": false,
"body.0.expression": true,
},
},
{
code: "(777 + 223)",
expected: {
"body.0": false,
"body.0.expression": true,
"body.0.expression.left": false,
"body.0.expression.right": false,
},
},
{
code: "(777) + 223",
expected: {
"body.0": false,
"body.0.expression": false,
"body.0.expression.left": true,
"body.0.expression.right": false,
},
},
{
code: "((777) + 223)",
expected: {
"body.0": false,
"body.0.expression": true,
"body.0.expression.left": true,
"body.0.expression.right": false,
},
},
{
code: "f()",
expected: {
"body.0": false,
"body.0.expression": false,
"body.0.expression.arguments.0": false,
},
},
{
code: "(f())",
expected: {
"body.0": false,
"body.0.expression": true,
"body.0.expression.arguments.0": false,
},
},
{
code: "f(a)",
expected: {
"body.0": false,
"body.0.expression": false,
"body.0.expression.arguments.0": false,
},
},
{
code: "f((a))",
expected: {
"body.0": false,
"body.0.expression": false,
"body.0.expression.arguments.0": true,
},
},
{
code: "f(a,b)",
expected: {
"body.0": false,
"body.0.expression": false,
"body.0.expression.arguments.0": false,
"body.0.expression.arguments.1": false,
},
},
{
code: "f((a),b)",
expected: {
"body.0": false,
"body.0.expression": false,
"body.0.expression.arguments.0": true,
"body.0.expression.arguments.1": false,
},
},
{
code: "f(a,(b))",
expected: {
"body.0": false,
"body.0.expression": false,
"body.0.expression.arguments.0": false,
"body.0.expression.arguments.1": true,
},
},
{
code: "new f(a)",
expected: {
"body.0": false,
"body.0.expression": false,
"body.0.expression.arguments.0": false,
},
},
{
code: "new f((a))",
expected: {
"body.0": false,
"body.0.expression": false,
"body.0.expression.arguments.0": true,
},
},
{
code: "do f(); while (a)",
expected: {
"body.0": false,
"body.0.test": false,
"body.0.body": false,
"body.0.body.expression": false,
},
},
{
code: "do (f()); while ((a))",
expected: {
"body.0": false,
"body.0.test": true,
"body.0.body": false,
"body.0.body.expression": true,
},
},
{
code: "if (a) b()",
expected: {
"body.0": false,
"body.0.test": false,
"body.0.consequent": false,
"body.0.consequent.expression": false,
},
},
{
code: "if ((a)) (b())",
expected: {
"body.0": false,
"body.0.test": true,
"body.0.consequent": false,
"body.0.consequent.expression": true,
},
},
{
code: "while (a) b()",
expected: {
"body.0": false,
"body.0.test": false,
"body.0.body": false,
"body.0.body.expression": false,
},
},
{
code: "while ((a)) (b())",
expected: {
"body.0": false,
"body.0.test": true,
"body.0.body": false,
"body.0.body.expression": true,
},
},
{
code: "switch (a) {}",
expected: {
"body.0": false,
"body.0.discriminant": false,
},
},
{
code: "switch ((a)) {}",
expected: {
"body.0": false,
"body.0.discriminant": true,
},
},
{
code: "with (a) {}",
expected: {
"body.0": false,
"body.0.object": false,
},
},
{
code: "with ((a)) {}",
expected: {
"body.0": false,
"body.0.object": true,
},
},
]) {
describe(`on the code \`${code}\``, () => {
for (const key of Object.keys(expected)) {
it(`should return ${expected[key]} at "${key}"`, () => {
const linter = new eslint.Linter()
let actual = null
linter.defineRule("test", context => ({
Program(node) {
actual = isParenthesized(
dotProp.get(node, key),
context.getSourceCode()
)
},
}))
const messages = linter.verify(code, {
env: { es6: true },
parserOptions: { ecmaVersion: 2018 },
rules: { test: "error" },
})
assert.strictEqual(
messages.length,
0,
messages[0] && messages[0].message
)
assert.strictEqual(actual, expected[key])
})
}
})
}
for (const { code, expected } of [
{
code: "777",
expected: {
"body.0": false,
"body.0.expression": false,
},
},
{
code: "(777)",
expected: {
"body.0": false,
"body.0.expression": false,
},
},
{
code: "((777))",
expected: {
"body.0": false,
"body.0.expression": true,
},
},
{
code: "if (a) ;",
expected: {
"body.0": false,
"body.0.test": false,
},
},
{
code: "if ((a)) ;",
expected: {
"body.0": false,
"body.0.test": false,
},
},
{
code: "if (((a))) ;",
expected: {
"body.0": false,
"body.0.test": true,
},
},
]) {
describe(`on the code \`${code}\` and 2 times`, () => {
for (const key of Object.keys(expected)) {
it(`should return ${expected[key]} at "${key}"`, () => {
const linter = new eslint.Linter()
let actual = null
linter.defineRule("test", context => ({
Program(node) {
actual = isParenthesized(
2,
dotProp.get(node, key),
context.getSourceCode()
)
},
}))
const messages = linter.verify(code, {
env: { es6: true },
parserOptions: { ecmaVersion: 2018 },
rules: { test: "error" },
})
assert.strictEqual(
messages.length,
0,
messages[0] && messages[0].message
)
assert.strictEqual(actual, expected[key])
})
}
})
}
})
eslint-utils-2.1.0/test/pattern-matcher.js 0000664 0000000 0000000 00000026504 13671663521 0020602 0 ustar 00root root 0000000 0000000 import assert from "assert"
import { PatternMatcher } from "../src/"
const NAMED_CAPTURE_GROUP_SUPPORTED = (() => {
try {
new RegExp("(?)", "u") //eslint-disable-line no-new, prefer-regex-literals, @mysticatea/node/no-unsupported-features/es-syntax
return true
} catch (_error) {
return false
}
})()
/**
* Create a new RegExpExecArray.
* @param {string[]} subStrings The substrings.
* @param {number} index The index.
* @param {string} input The input.
* @returns {RegExpExecArray} The created object.
*/
function newRegExpExecArray(subStrings, index, input) {
Object.assign(subStrings, { index, input })
if (NAMED_CAPTURE_GROUP_SUPPORTED) {
subStrings.groups = undefined
}
return subStrings
}
describe("The 'PatternMatcher' class:", () => {
describe("the constructor", () => {
it("should throw TypeError if non-RegExp value was given.", () => {
for (const value of [
undefined,
null,
1,
"foo",
() => {
// empty
},
{
exec() {
// empty
},
},
]) {
assert.throws(
() => new PatternMatcher(value),
/^TypeError: 'pattern' should be a RegExp instance\.$/u
)
}
})
it("should throw Error if the RegExp value does not have 'g' flag.", () => {
for (const value of [/foo/u, /bar/imu]) {
assert.throws(
() => new PatternMatcher(value),
/^Error: 'pattern' should contains 'g' flag\.$/u
)
}
})
})
describe("the 'execAll' method", () => {
describe("with no options", () => {
for (const { str, expected } of [
{ str: "", expected: [] },
{ str: "abc", expected: [] },
{ str: String.raw`\foo`, expected: [] },
{ str: String.raw`\\\foo`, expected: [] },
{ str: String.raw`\a\foo`, expected: [] },
{
str: "foo",
expected: [newRegExpExecArray(["foo"], 0, "foo")],
},
{
str: String.raw`\\foo`,
expected: [
newRegExpExecArray(["foo"], 2, String.raw`\\foo`),
],
},
{
str: String.raw`\\\\foo`,
expected: [
newRegExpExecArray(["foo"], 4, String.raw`\\\\foo`),
],
},
{
str: "-foofoofooabcfoo-",
expected: [
newRegExpExecArray(["foo"], 1, "-foofoofooabcfoo-"),
newRegExpExecArray(["foo"], 4, "-foofoofooabcfoo-"),
newRegExpExecArray(["foo"], 7, "-foofoofooabcfoo-"),
newRegExpExecArray(["foo"], 13, "-foofoofooabcfoo-"),
],
},
{
str: String.raw`-foo\foofooabcfoo-`,
expected: [
newRegExpExecArray(
["foo"],
1,
String.raw`-foo\foofooabcfoo-`
),
newRegExpExecArray(
["foo"],
8,
String.raw`-foo\foofooabcfoo-`
),
newRegExpExecArray(
["foo"],
14,
String.raw`-foo\foofooabcfoo-`
),
],
},
]) {
it(`should return ${JSON.stringify(
expected
)} in ${JSON.stringify(str)}.`, () => {
const matcher = new PatternMatcher(/foo/gu)
const actual = Array.from(matcher.execAll(str))
assert.deepStrictEqual(actual, expected)
})
}
for (const { str, expected } of [
{
str: "ab0c",
expected: [newRegExpExecArray(["b0", "b", "0"], 1, "ab0c")],
},
{
str: "a1b2c3",
expected: [
newRegExpExecArray(["a1", "a", "1"], 0, "a1b2c3"),
newRegExpExecArray(["b2", "b", "2"], 2, "a1b2c3"),
newRegExpExecArray(["c3", "c", "3"], 4, "a1b2c3"),
],
},
]) {
it(`should return ${JSON.stringify(
expected
)} in ${JSON.stringify(str)}.`, () => {
const matcher = new PatternMatcher(/(\w)(\d)/gu)
const actual = Array.from(matcher.execAll(str))
assert.deepStrictEqual(actual, expected)
})
}
it("should iterate for two strings in parallel.", () => {
const matcher = new PatternMatcher(/\w/gu)
const expected1 = [
newRegExpExecArray(["a"], 0, "a--b-c"),
newRegExpExecArray(["b"], 3, "a--b-c"),
newRegExpExecArray(["c"], 5, "a--b-c"),
]
const expected2 = [
newRegExpExecArray(["a"], 1, "-ab-c-"),
newRegExpExecArray(["b"], 2, "-ab-c-"),
newRegExpExecArray(["c"], 4, "-ab-c-"),
]
const actual1 = []
const actual2 = []
const it1 = matcher.execAll("a--b-c")
const it2 = matcher.execAll("-ab-c-")
{
let ret1 = null
let ret2 = null
while (
((ret1 = it1.next()),
(ret2 = it2.next()),
!ret1.done && !ret2.done)
) {
actual1.push(ret1.value)
actual2.push(ret2.value)
}
}
assert.deepStrictEqual(actual1, expected1)
assert.deepStrictEqual(actual2, expected2)
})
})
describe("with 'escaped:true' option", () => {
for (const { str, expected } of [
{
str: "foo",
expected: [newRegExpExecArray(["foo"], 0, "foo")],
},
{
str: String.raw`\foo`,
expected: [
newRegExpExecArray(["foo"], 1, String.raw`\foo`),
],
},
{
str: String.raw`\\foo`,
expected: [
newRegExpExecArray(["foo"], 2, String.raw`\\foo`),
],
},
{
str: String.raw`\\\foo`,
expected: [
newRegExpExecArray(["foo"], 3, String.raw`\\\foo`),
],
},
{
str: String.raw`\\\\foo`,
expected: [
newRegExpExecArray(["foo"], 4, String.raw`\\\\foo`),
],
},
]) {
it(`should return ${JSON.stringify(
expected
)} in ${JSON.stringify(str)}.`, () => {
const matcher = new PatternMatcher(/foo/gu, {
escaped: true,
})
const actual = Array.from(matcher.execAll(str))
assert.deepStrictEqual(actual, expected)
})
}
})
})
describe("the 'test' method", () => {
for (const { str, expected } of [
{ str: "", expected: false },
{ str: "abc", expected: false },
{ str: String.raw`\foo`, expected: false },
{ str: String.raw`\\\foo`, expected: false },
{ str: String.raw`\a\foo`, expected: false },
{ str: String.raw`-\foo\foo\fooabc\foo-`, expected: false },
{ str: "foo", expected: true },
{ str: String.raw`\\foo`, expected: true },
{ str: String.raw`\\\\foo`, expected: true },
{ str: "-foofoofooabcfoo-", expected: true },
{ str: String.raw`-foo\foofooabcfoo-`, expected: true },
]) {
it(`should return ${expected} in ${JSON.stringify(str)}.`, () => {
const matcher = new PatternMatcher(/foo/gu)
const actual = matcher.test(str)
assert.deepStrictEqual(actual, expected)
})
}
})
describe("the 'Symbol.replace' method", () => {
for (const { pattern, str, replacer, expected } of [
{ str: "", replacer: "xyz", expected: "" },
{ str: "123", replacer: "xyz", expected: "123" },
{
str: String.raw`1\a2\b3`,
replacer: "x",
expected: String.raw`1\a2\b3`,
},
{
str: String.raw`1a2\b3`,
replacer: "x",
expected: String.raw`1x2\b3`,
},
{
str: String.raw`1a2b3`,
replacer: "x",
expected: String.raw`1x2x3`,
},
{ str: "abc", replacer: "x", expected: "xxx" },
{ str: "abc", replacer: "$$x", expected: "$x$x$x" },
{ str: "abc", replacer: "$$&", expected: "$&$&$&" },
{ str: "abc", replacer: "$$$&", expected: "$a$b$c" },
{ str: "abc", replacer: "$&", expected: "abc" },
{ str: "abc", replacer: "$'$`", expected: "bccaab" },
{
str: String.raw`a\bc`,
replacer: "$'$`",
expected: String.raw`\bc\ba\b`,
},
{ str: "abc", replacer: "$0", expected: "$0$0$0" },
{ str: "abc", replacer: "$1", expected: "$1$1$1" },
{
pattern: /a(b)/gu,
str: "abc",
replacer: "$1",
expected: "bc",
},
]) {
it(`should return ${expected} in ${JSON.stringify(
str
)} and ${JSON.stringify(replacer)}.`, () => {
const matcher = new PatternMatcher(pattern || /[a-c]/gu)
const actual = str.replace(matcher, replacer)
assert.deepStrictEqual(actual, expected)
})
}
it("should pass the correct arguments to replacers.", () => {
const matcher = new PatternMatcher(/(\w)(\d)/gu)
const actualArgs = []
const actual = "abc1d2efg".replace(matcher, (...args) => {
actualArgs.push(args)
return "x"
})
assert.deepStrictEqual(actualArgs, [
["c1", "c", "1", 2, "abc1d2efg"],
["d2", "d", "2", 4, "abc1d2efg"],
])
assert.deepStrictEqual(actual, "abxxefg")
})
})
})
eslint-utils-2.1.0/test/reference-tracker.js 0000664 0000000 0000000 00000106154 13671663521 0021073 0 ustar 00root root 0000000 0000000 import assert from "assert"
import eslint from "eslint"
import semver from "semver"
import { CALL, CONSTRUCT, ESM, READ, ReferenceTracker } from "../src/"
const config = {
parserOptions: {
ecmaVersion: semver.gte(eslint.CLIEngine.version, "6.0.0")
? 2020
: 2018,
sourceType: "module",
},
globals: { Reflect: false },
rules: { test: "error" },
}
describe("The 'ReferenceTracker' class:", () => {
describe("the 'iterateGlobalReferences' method", () => {
for (const { description, code, traceMap, expected } of [
{
description:
"should iterate the references of a given global variable.",
code: "var x = Object; { let Object; var y = Object }",
traceMap: {
Object: {
[READ]: 1,
foo: { [CALL]: 2 },
Foo: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "Identifier" },
path: ["Object"],
type: READ,
info: 1,
},
],
},
{
description:
"should iterate the member references of a given global variable, with MemberExpression",
code: [
"Object.a; Object.a(); new Object.a();",
"Object.b; Object.b(); new Object.b();",
"Object.c; Object.c(); new Object.c();",
].join("\n"),
traceMap: {
Object: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "MemberExpression" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "MemberExpression" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "MemberExpression" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["Object", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["Object", "c"],
type: CONSTRUCT,
info: 3,
},
],
},
{
description:
"should iterate the member references of a given global variable, with VariableDeclarator",
code: [
"var x = Object;",
"x.a; x.a(); new x.a();",
"x.b; x.b(); new x.b();",
"x.c; x.c(); new x.c();",
].join("\n"),
traceMap: {
Object: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "MemberExpression" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "MemberExpression" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "MemberExpression" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["Object", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["Object", "c"],
type: CONSTRUCT,
info: 3,
},
],
},
{
description:
"should iterate the member references of a given global variable, with VariableDeclarator 2",
code: [
"var x = Object, a = x.a, b = x.b, c = x.c;",
"a; a(); new a();",
"b; b(); new b();",
"c; c(); new c();",
].join("\n"),
traceMap: {
Object: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "MemberExpression" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["Object", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["Object", "c"],
type: CONSTRUCT,
info: 3,
},
],
},
{
description:
"should iterate the member references of a given global variable, with AssignmentExpression",
code: [
"var x, a, b, c;",
"a = (x = Object).a; b = x.b; c = x.c;",
"a; a(); new a();",
"b; b(); new b();",
"c; c(); new c();",
].join("\n"),
traceMap: {
Object: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "CallExpression" },
path: ["Object", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["Object", "c"],
type: CONSTRUCT,
info: 3,
},
{
node: { type: "MemberExpression" },
path: ["Object", "a"],
type: READ,
info: 1,
},
],
},
{
description:
"should iterate the member references of a given global variable, with destructuring",
code: [
"var {a, b, c} = Object;",
"a; a(); new a();",
"b; b(); new b();",
"c; c(); new c();",
].join("\n"),
traceMap: {
Object: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "Property" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["Object", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["Object", "c"],
type: CONSTRUCT,
info: 3,
},
],
},
{
description:
"should iterate the member references of a given global variable, with AssignmentPattern",
code: [
"var {x: {a, b, c} = Object} = {};",
"a; a(); new a();",
"b; b(); new b();",
"c; c(); new c();",
].join("\n"),
traceMap: {
Object: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "Property" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["Object", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["Object", "c"],
type: CONSTRUCT,
info: 3,
},
],
},
{
description:
"should iterate the member references of a given global variable, with 'window'.",
code: [
"/*global window */",
"var {Object: {a, b, c}} = window;",
"a; a(); new a();",
"b; b(); new b();",
"c; c(); new c();",
].join("\n"),
traceMap: {
Object: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "Property" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["Object", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["Object", "c"],
type: CONSTRUCT,
info: 3,
},
],
},
{
description:
"should iterate the member references of a given global variable, with 'global'.",
code: [
"/*global global */",
"global.Object.a;",
"global.Object.b; global.Object.b(); new global.Object.b();",
"global.Object.c; global.Object.c(); new global.Object.c();",
].join("\n"),
traceMap: {
Object: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "MemberExpression" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["Object", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["Object", "c"],
type: CONSTRUCT,
info: 3,
},
],
},
{
description:
"should iterate the member references of a given global variable, with 'globalThis'.",
code: [
"/*global globalThis */",
"globalThis.Object.a;",
"globalThis.Object.b; globalThis.Object.b(); new globalThis.Object.b();",
"globalThis.Object.c; globalThis.Object.c(); new globalThis.Object.c();",
].join("\n"),
traceMap: {
Object: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "MemberExpression" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["Object", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["Object", "c"],
type: CONSTRUCT,
info: 3,
},
],
},
{
description:
"should iterate the member references of a given global variable, with 'self'.",
code: [
"/*global self */",
"self.Object.a;",
"self.Object.b; self.Object.b(); new self.Object.b();",
"self.Object.c; self.Object.c(); new self.Object.c();",
].join("\n"),
traceMap: {
Object: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "MemberExpression" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["Object", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["Object", "c"],
type: CONSTRUCT,
info: 3,
},
],
},
{
description:
"should iterate the member references of a given global variable, with 'window'.",
code: [
"/*global window */",
"window.Object.a;",
"window.Object.b; window.Object.b(); new window.Object.b();",
"window.Object.c; window.Object.c(); new window.Object.c();",
].join("\n"),
traceMap: {
Object: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "MemberExpression" },
path: ["Object", "a"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["Object", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["Object", "c"],
type: CONSTRUCT,
info: 3,
},
],
},
{
description:
"should not iterate the references of a given global variable if it's modified.",
code: [
"Object = {}",
"Object.a",
"Object.b()",
"new Object.c()",
].join("\n"),
traceMap: {
Object: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [],
},
{
description:
"should not iterate the references through unary/binary expressions.",
code: [
'var construct = typeof Reflect !== "undefined" ? Reflect.construct : undefined',
"construct()",
].join("\n"),
traceMap: {
Reflect: { [CALL]: 1 },
},
expected: [],
},
]) {
it(description, () => {
const linter = new eslint.Linter()
let actual = null
linter.defineRule("test", context => ({
"Program:exit"() {
const tracker = new ReferenceTracker(context.getScope())
actual = Array.from(
tracker.iterateGlobalReferences(traceMap)
).map(x =>
Object.assign(x, {
node: Object.assign(
{ type: x.node.type },
x.node.optional
? { optional: x.node.optional }
: {}
),
})
)
},
}))
linter.verify(code, config)
assert.deepStrictEqual(actual, expected)
})
}
})
describe("the 'iterateCjsReferences' method", () => {
for (const { description, code, traceMap, expected } of [
{
description:
"should iterate the references of a given CJS modules.",
code: [
"/*global require */",
"const abc = require('abc');",
"abc();",
"new abc();",
"abc.xyz;",
...(semver.gte(eslint.CLIEngine.version, "6.0.0")
? [
"abc?.xyz;",
"abc?.();",
"abc?.xyz?.();",
"(abc.def).ghi;",
"(abc?.def)?.ghi;",
]
: []),
].join("\n"),
traceMap: {
abc: {
[READ]: 1,
[CALL]: 2,
[CONSTRUCT]: 3,
xyz: { [READ]: 4 },
def: { ghi: { [READ]: 5 } },
},
},
expected: [
{
node: { type: "CallExpression" },
path: ["abc"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["abc"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["abc"],
type: CONSTRUCT,
info: 3,
},
{
node: { type: "MemberExpression" },
path: ["abc", "xyz"],
type: READ,
info: 4,
},
...(semver.gte(eslint.CLIEngine.version, "6.0.0")
? [
{
node: {
type: "MemberExpression",
optional: true,
},
path: ["abc", "xyz"],
type: READ,
info: 4,
},
{
node: {
type: "CallExpression",
optional: true,
},
path: ["abc"],
type: CALL,
info: 2,
},
{
node: {
type: "MemberExpression",
optional: true,
},
path: ["abc", "xyz"],
type: READ,
info: 4,
},
{
node: { type: "MemberExpression" },
path: ["abc", "def", "ghi"],
type: READ,
info: 5,
},
{
node: {
type: "MemberExpression",
optional: true,
},
path: ["abc", "def", "ghi"],
type: READ,
info: 5,
},
]
: []),
],
},
{
description:
"should NOT iterate the references of a given CJS modules if the 'require' variable wasn't defined.",
code: [
"const abc = require('abc');",
"abc();",
"new abc();",
"abc.xyz;",
].join("\n"),
traceMap: {
abc: {
[READ]: 1,
[CALL]: 2,
[CONSTRUCT]: 3,
xyz: { [READ]: 4 },
},
},
expected: [],
},
{
description:
"should NOT iterate the references of a given CJS modules if the 'require' variable was overrided.",
code: [
"/*global require */",
"const require = () => {};",
"const abc = require('abc');",
"abc();",
"new abc();",
"abc.xyz;",
].join("\n"),
traceMap: {
abc: {
[READ]: 1,
[CALL]: 2,
[CONSTRUCT]: 3,
xyz: { [READ]: 4 },
},
},
expected: [],
},
]) {
it(description, () => {
const linter = new eslint.Linter()
let actual = null
linter.defineRule("test", context => ({
"Program:exit"() {
const tracker = new ReferenceTracker(context.getScope())
actual = Array.from(
tracker.iterateCjsReferences(traceMap)
).map(x =>
Object.assign(x, {
node: Object.assign(
{ type: x.node.type },
x.node.optional
? { optional: x.node.optional }
: {}
),
})
)
},
}))
linter.verify(code, config)
assert.deepStrictEqual(actual, expected)
})
}
})
describe("the 'iterateEsmReferences' method", () => {
for (const { description, code, traceMap, expected } of [
{
description:
"should iterate the references of a given ES modules (with CJS module and the default export).",
code: [
"import abc from 'abc';",
"abc();",
"new abc();",
"abc.xyz;",
].join("\n"),
traceMap: {
abc: {
[READ]: 1,
[CALL]: 2,
[CONSTRUCT]: 3,
xyz: { [READ]: 4 },
},
},
expected: [
{
node: { type: "ImportDeclaration" },
path: ["abc"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["abc"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["abc"],
type: CONSTRUCT,
info: 3,
},
{
node: { type: "MemberExpression" },
path: ["abc", "xyz"],
type: READ,
info: 4,
},
],
},
{
description: "should map CJS module to the default export.",
code: [
"import {default as x} from 'abc';",
"x.a;",
"x.b();",
"new x.c();",
].join("\n"),
traceMap: {
abc: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "MemberExpression" },
path: ["abc", "a"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["abc", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["abc", "c"],
type: CONSTRUCT,
info: 3,
},
],
},
{
description: "should NOT map CJS module to the named exports.",
code: [
"import {a, b, c} from 'abc';",
"a;",
"b();",
"new c();",
].join("\n"),
traceMap: {
abc: {
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [],
},
{
description:
"should iterate the references of a given ES modules.",
code: [
"import x, {a, b, c, y} from 'abc';",
"x.a;",
"x.y;",
"a;",
"b();",
"new c();",
].join("\n"),
traceMap: {
abc: {
[ESM]: true,
default: {
y: { [READ]: 4 },
},
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "MemberExpression" },
path: ["abc", "default", "y"],
type: READ,
info: 4,
},
{
node: { type: "ImportSpecifier" },
path: ["abc", "a"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["abc", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["abc", "c"],
type: CONSTRUCT,
info: 3,
},
],
},
{
description:
"should iterate the references of a given ES modules, with ImportNamespaceSpecifier.",
code: [
"import * as x from 'abc';",
"x.default.a;",
"x.default.y;",
"x.a;",
"x.b();",
"new x.c();",
].join("\n"),
traceMap: {
abc: {
[ESM]: true,
default: {
y: { [READ]: 4 },
},
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
},
},
expected: [
{
node: { type: "MemberExpression" },
path: ["abc", "default", "y"],
type: READ,
info: 4,
},
{
node: { type: "MemberExpression" },
path: ["abc", "a"],
type: READ,
info: 1,
},
{
node: { type: "CallExpression" },
path: ["abc", "b"],
type: CALL,
info: 2,
},
{
node: { type: "NewExpression" },
path: ["abc", "c"],
type: CONSTRUCT,
info: 3,
},
],
},
{
description:
"should iterate the references of a given ES modules, with ExportNamedDeclaration.",
code: "export {a, b, c} from 'abc';",
traceMap: {
abc: {
[ESM]: true,
default: {
y: { [READ]: 4 },
},
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
d: { [READ]: 5 },
},
},
expected: [
{
node: { type: "ExportSpecifier" },
path: ["abc", "a"],
type: READ,
info: 1,
},
],
},
{
description:
"should iterate the references of a given ES modules, with ExportAllDeclaration.",
code: "export * from 'abc';",
traceMap: {
abc: {
[ESM]: true,
default: {
y: { [READ]: 4 },
},
a: { [READ]: 1 },
b: { [CALL]: 2 },
c: { [CONSTRUCT]: 3 },
d: { [READ]: 5 },
},
},
expected: [
{
node: { type: "ExportAllDeclaration" },
path: ["abc", "a"],
type: READ,
info: 1,
},
{
node: { type: "ExportAllDeclaration" },
path: ["abc", "d"],
type: READ,
info: 5,
},
],
},
]) {
it(description, () => {
const linter = new eslint.Linter()
let actual = null
linter.defineRule("test", context => ({
"Program:exit"() {
const tracker = new ReferenceTracker(context.getScope())
actual = Array.from(
tracker.iterateEsmReferences(traceMap)
).map(x =>
Object.assign(x, {
node: Object.assign(
{ type: x.node.type },
x.node.optional
? { optional: x.node.optional }
: {}
),
})
)
},
}))
linter.verify(code, config)
assert.deepStrictEqual(actual, expected)
})
}
})
})
eslint-utils-2.1.0/test/token-predicate.js 0000664 0000000 0000000 00000012427 13671663521 0020561 0 ustar 00root root 0000000 0000000 import assert from "assert"
import {
isArrowToken,
isClosingBraceToken,
isClosingBracketToken,
isClosingParenToken,
isColonToken,
isCommaToken,
isCommentToken,
isNotArrowToken,
isNotClosingBraceToken,
isNotClosingBracketToken,
isNotClosingParenToken,
isNotColonToken,
isNotCommaToken,
isNotCommentToken,
isNotOpeningBraceToken,
isNotOpeningBracketToken,
isNotOpeningParenToken,
isNotSemicolonToken,
isOpeningBraceToken,
isOpeningBracketToken,
isOpeningParenToken,
isSemicolonToken,
} from "../src/"
describe("The predicate functions for tokens", () => {
for (const { positive, negative, patterns } of [
{
positive: isArrowToken,
negative: isNotArrowToken,
patterns: [
[{ type: "Punctuator", value: "=>" }, true],
[{ type: "Punctuator", value: ">" }, false],
[{ type: "Line", value: "=>" }, false],
],
},
{
positive: isClosingBraceToken,
negative: isNotClosingBraceToken,
patterns: [
[{ type: "Punctuator", value: "}" }, true],
[{ type: "Punctuator", value: "{" }, false],
[{ type: "Punctuator", value: ")" }, false],
[{ type: "Line", value: "}" }, false],
],
},
{
positive: isClosingBracketToken,
negative: isNotClosingBracketToken,
patterns: [
[{ type: "Punctuator", value: "]" }, true],
[{ type: "Punctuator", value: "[" }, false],
[{ type: "Punctuator", value: ")" }, false],
[{ type: "Line", value: "]" }, false],
],
},
{
positive: isClosingParenToken,
negative: isNotClosingParenToken,
patterns: [
[{ type: "Punctuator", value: ")" }, true],
[{ type: "Punctuator", value: "(" }, false],
[{ type: "Punctuator", value: "}" }, false],
[{ type: "Line", value: ")" }, false],
],
},
{
positive: isColonToken,
negative: isNotColonToken,
patterns: [
[{ type: "Punctuator", value: ":" }, true],
[{ type: "Punctuator", value: ";" }, false],
[{ type: "Line", value: ":" }, false],
],
},
{
positive: isCommaToken,
negative: isNotCommaToken,
patterns: [
[{ type: "Punctuator", value: "," }, true],
[{ type: "Punctuator", value: "." }, false],
[{ type: "Line", value: "," }, false],
],
},
{
positive: isCommentToken,
negative: isNotCommentToken,
patterns: [
[{ type: "Line", value: "." }, true],
[{ type: "Block", value: "." }, true],
[{ type: "Shebang", value: "." }, true],
[{ type: "Punctuator", value: "." }, false],
],
},
{
positive: isOpeningBraceToken,
negative: isNotOpeningBraceToken,
patterns: [
[{ type: "Punctuator", value: "{" }, true],
[{ type: "Punctuator", value: "(" }, false],
[{ type: "Punctuator", value: "}" }, false],
[{ type: "Line", value: "{" }, false],
],
},
{
positive: isOpeningBracketToken,
negative: isNotOpeningBracketToken,
patterns: [
[{ type: "Punctuator", value: "[" }, true],
[{ type: "Punctuator", value: "(" }, false],
[{ type: "Punctuator", value: "]" }, false],
[{ type: "Line", value: "[" }, false],
],
},
{
positive: isOpeningParenToken,
negative: isNotOpeningParenToken,
patterns: [
[{ type: "Punctuator", value: "(" }, true],
[{ type: "Punctuator", value: "{" }, false],
[{ type: "Punctuator", value: ")" }, false],
[{ type: "Line", value: "(" }, false],
],
},
{
positive: isSemicolonToken,
negative: isNotSemicolonToken,
patterns: [
[{ type: "Punctuator", value: ";" }, true],
[{ type: "Punctuator", value: ":" }, false],
[{ type: "Line", value: ";" }, false],
],
},
]) {
const baseName = positive.name.slice(2)
describe(`'is${baseName}'`, () => {
for (const [token, expected] of patterns) {
it(`should return ${expected} if ${JSON.stringify(
token
)} was given.`, () => {
assert.strictEqual(positive(token), expected)
})
}
})
describe(`'isNot${baseName}'`, () => {
for (const [token, expected] of patterns) {
it(`should return ${!expected} if ${JSON.stringify(
token
)} was given.`, () => {
assert.strictEqual(negative(token), !expected)
})
}
})
}
})