eslint-plugin-sonarjs 0.4.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cirrus/nodejs-10.Dockerfile +29 -0
- package/.cirrus/nodejs-12.Dockerfile +28 -0
- package/.cirrus/nodejs-14.Dockerfile +28 -0
- package/.cirrus/nodejs-15.Dockerfile +28 -0
- package/.cirrus.yml +47 -0
- package/.eslintignore +1 -0
- package/README.md +14 -8
- package/lib/index.d.ts +3 -1
- package/lib/index.js +11 -6
- package/lib/index.js.map +1 -1
- package/lib/rules/cognitive-complexity.js +91 -65
- package/lib/rules/cognitive-complexity.js.map +1 -1
- package/lib/rules/max-switch-cases.js +10 -9
- package/lib/rules/max-switch-cases.js.map +1 -1
- package/lib/rules/no-all-duplicated-branches.js +21 -18
- package/lib/rules/no-all-duplicated-branches.js.map +1 -1
- package/lib/rules/no-collapsible-if.js +11 -10
- package/lib/rules/no-collapsible-if.js.map +1 -1
- package/lib/rules/no-collection-size-mischeck.d.ts +3 -0
- package/lib/rules/no-collection-size-mischeck.js +65 -0
- package/lib/rules/no-collection-size-mischeck.js.map +1 -0
- package/lib/rules/no-duplicate-string.js +23 -20
- package/lib/rules/no-duplicate-string.js.map +1 -1
- package/lib/rules/no-duplicated-branches.js +42 -37
- package/lib/rules/no-duplicated-branches.js.map +1 -1
- package/lib/rules/no-element-overwrite.js +34 -35
- package/lib/rules/no-element-overwrite.js.map +1 -1
- package/lib/rules/no-extra-arguments.js +45 -42
- package/lib/rules/no-extra-arguments.js.map +1 -1
- package/lib/rules/no-identical-conditions.js +12 -11
- package/lib/rules/no-identical-conditions.js.map +1 -1
- package/lib/rules/no-identical-expressions.js +12 -13
- package/lib/rules/no-identical-expressions.js.map +1 -1
- package/lib/rules/no-identical-functions.js +25 -25
- package/lib/rules/no-identical-functions.js.map +1 -1
- package/lib/rules/no-inverted-boolean-check.js +17 -13
- package/lib/rules/no-inverted-boolean-check.js.map +1 -1
- package/lib/rules/no-one-iteration-loop.js +24 -22
- package/lib/rules/no-one-iteration-loop.js.map +1 -1
- package/lib/rules/no-redundant-boolean.js +14 -11
- package/lib/rules/no-redundant-boolean.js.map +1 -1
- package/lib/rules/no-redundant-jump.d.ts +3 -0
- package/lib/rules/no-redundant-jump.js +67 -0
- package/lib/rules/no-redundant-jump.js.map +1 -0
- package/lib/rules/no-same-line-conditional.d.ts +3 -0
- package/lib/rules/no-same-line-conditional.js +72 -0
- package/lib/rules/no-same-line-conditional.js.map +1 -0
- package/lib/rules/no-small-switch.js +11 -8
- package/lib/rules/no-small-switch.js.map +1 -1
- package/lib/rules/no-unused-collection.d.ts +3 -0
- package/lib/rules/no-unused-collection.js +164 -0
- package/lib/rules/no-unused-collection.js.map +1 -0
- package/lib/rules/no-use-of-empty-return-value.js +29 -28
- package/lib/rules/no-use-of-empty-return-value.js.map +1 -1
- package/lib/rules/no-useless-catch.js +14 -9
- package/lib/rules/no-useless-catch.js.map +1 -1
- package/lib/rules/prefer-immediate-return.js +30 -29
- package/lib/rules/prefer-immediate-return.js.map +1 -1
- package/lib/rules/prefer-object-literal.js +20 -16
- package/lib/rules/prefer-object-literal.js.map +1 -1
- package/lib/rules/prefer-single-boolean-return.js +8 -5
- package/lib/rules/prefer-single-boolean-return.js.map +1 -1
- package/lib/rules/prefer-while.js +18 -14
- package/lib/rules/prefer-while.js.map +1 -1
- package/lib/utils/collections.d.ts +2 -0
- package/lib/utils/collections.js +41 -0
- package/lib/utils/collections.js.map +1 -0
- package/lib/utils/conditions.js +12 -11
- package/lib/utils/conditions.js.map +1 -1
- package/lib/utils/equivalence.js +3 -2
- package/lib/utils/equivalence.js.map +1 -1
- package/lib/utils/locations.d.ts +1 -0
- package/lib/utils/locations.js +31 -14
- package/lib/utils/locations.js.map +1 -1
- package/lib/utils/nodes.d.ts +1 -1
- package/lib/utils/nodes.js +3 -2
- package/lib/utils/nodes.js.map +1 -1
- package/lib/utils/parser-services.d.ts +5 -0
- package/lib/utils/parser-services.js +8 -0
- package/lib/utils/parser-services.js.map +1 -0
- package/package.json +49 -26
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
FROM gcr.io/language-team/base:latest
|
|
2
|
+
|
|
3
|
+
USER root
|
|
4
|
+
|
|
5
|
+
ENV NODE_VERSION v10.23.2
|
|
6
|
+
|
|
7
|
+
RUN wget -U "nodejs" -q -O nodejs.tar.xz https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-x64.tar.xz \
|
|
8
|
+
&& tar -xJf "nodejs.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
|
|
9
|
+
&& rm nodejs.tar.xz \
|
|
10
|
+
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
|
|
11
|
+
|
|
12
|
+
ENV YARN_VERSION 1.22.5
|
|
13
|
+
|
|
14
|
+
RUN curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
|
|
15
|
+
&& mkdir -p /opt \
|
|
16
|
+
&& tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ \
|
|
17
|
+
&& ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn \
|
|
18
|
+
&& ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg \
|
|
19
|
+
&& rm yarn-v$YARN_VERSION.tar.gz
|
|
20
|
+
|
|
21
|
+
RUN curl "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.3.0.2102.zip" -o /tmp/sonar-scanner.zip \
|
|
22
|
+
&& unzip -d /opt /tmp/sonar-scanner.zip \
|
|
23
|
+
&& mv /opt/sonar-scanner-4.3.0.2102 /opt/sonar-scanner \
|
|
24
|
+
&& rm /tmp/sonar-scanner.zip
|
|
25
|
+
|
|
26
|
+
USER sonarsource
|
|
27
|
+
|
|
28
|
+
ENV PATH "/opt/sonar-scanner/bin:${PATH}"
|
|
29
|
+
ENV SONARCLOUD_ANALYSIS true
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
FROM gcr.io/language-team/base:latest
|
|
2
|
+
|
|
3
|
+
USER root
|
|
4
|
+
|
|
5
|
+
ENV NODE_VERSION v12.20.1
|
|
6
|
+
|
|
7
|
+
RUN wget -U "nodejs" -q -O nodejs.tar.xz https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-x64.tar.xz \
|
|
8
|
+
&& tar -xJf "nodejs.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
|
|
9
|
+
&& rm nodejs.tar.xz \
|
|
10
|
+
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
|
|
11
|
+
|
|
12
|
+
ENV YARN_VERSION 1.22.5
|
|
13
|
+
|
|
14
|
+
RUN curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
|
|
15
|
+
&& mkdir -p /opt \
|
|
16
|
+
&& tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ \
|
|
17
|
+
&& ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn \
|
|
18
|
+
&& ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg \
|
|
19
|
+
&& rm yarn-v$YARN_VERSION.tar.gz
|
|
20
|
+
|
|
21
|
+
RUN curl "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.3.0.2102.zip" -o /tmp/sonar-scanner.zip \
|
|
22
|
+
&& unzip -d /opt /tmp/sonar-scanner.zip \
|
|
23
|
+
&& mv /opt/sonar-scanner-4.3.0.2102 /opt/sonar-scanner \
|
|
24
|
+
&& rm /tmp/sonar-scanner.zip
|
|
25
|
+
|
|
26
|
+
USER sonarsource
|
|
27
|
+
|
|
28
|
+
ENV PATH "/opt/sonar-scanner/bin:${PATH}"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
FROM gcr.io/language-team/base:latest
|
|
2
|
+
|
|
3
|
+
USER root
|
|
4
|
+
|
|
5
|
+
ENV NODE_VERSION v14.15.4
|
|
6
|
+
|
|
7
|
+
RUN wget -U "nodejs" -q -O nodejs.tar.xz https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-x64.tar.xz \
|
|
8
|
+
&& tar -xJf "nodejs.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
|
|
9
|
+
&& rm nodejs.tar.xz \
|
|
10
|
+
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
|
|
11
|
+
|
|
12
|
+
ENV YARN_VERSION 1.22.5
|
|
13
|
+
|
|
14
|
+
RUN curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
|
|
15
|
+
&& mkdir -p /opt \
|
|
16
|
+
&& tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ \
|
|
17
|
+
&& ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn \
|
|
18
|
+
&& ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg \
|
|
19
|
+
&& rm yarn-v$YARN_VERSION.tar.gz
|
|
20
|
+
|
|
21
|
+
RUN curl "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.3.0.2102.zip" -o /tmp/sonar-scanner.zip \
|
|
22
|
+
&& unzip -d /opt /tmp/sonar-scanner.zip \
|
|
23
|
+
&& mv /opt/sonar-scanner-4.3.0.2102 /opt/sonar-scanner \
|
|
24
|
+
&& rm /tmp/sonar-scanner.zip
|
|
25
|
+
|
|
26
|
+
USER sonarsource
|
|
27
|
+
|
|
28
|
+
ENV PATH "/opt/sonar-scanner/bin:${PATH}"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
FROM gcr.io/language-team/base:latest
|
|
2
|
+
|
|
3
|
+
USER root
|
|
4
|
+
|
|
5
|
+
ENV NODE_VERSION v15.10.0
|
|
6
|
+
|
|
7
|
+
RUN wget -U "nodejs" -q -O nodejs.tar.xz https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-x64.tar.xz \
|
|
8
|
+
&& tar -xJf "nodejs.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
|
|
9
|
+
&& rm nodejs.tar.xz \
|
|
10
|
+
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
|
|
11
|
+
|
|
12
|
+
ENV YARN_VERSION 1.22.5
|
|
13
|
+
|
|
14
|
+
RUN curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
|
|
15
|
+
&& mkdir -p /opt \
|
|
16
|
+
&& tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ \
|
|
17
|
+
&& ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn \
|
|
18
|
+
&& ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg \
|
|
19
|
+
&& rm yarn-v$YARN_VERSION.tar.gz
|
|
20
|
+
|
|
21
|
+
RUN curl "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.3.0.2102.zip" -o /tmp/sonar-scanner.zip \
|
|
22
|
+
&& unzip -d /opt /tmp/sonar-scanner.zip \
|
|
23
|
+
&& mv /opt/sonar-scanner-4.3.0.2102 /opt/sonar-scanner \
|
|
24
|
+
&& rm /tmp/sonar-scanner.zip
|
|
25
|
+
|
|
26
|
+
USER sonarsource
|
|
27
|
+
|
|
28
|
+
ENV PATH "/opt/sonar-scanner/bin:${PATH}"
|
package/.cirrus.yml
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
gcp_credentials: ENCRYPTED[!17c59813193e86cc337bce848b358412b90f50bc5fe1b8b39d363cdf14a41ebe76cfba0482e7f81d076994b9f6dbfb4c!]
|
|
2
|
+
|
|
3
|
+
env:
|
|
4
|
+
GITHUB_TOKEN: ENCRYPTED[!f272985ea5b49b3cf9c414b98de6a8e9096be47bfcee52f33311ba3131a2af637c1b956f49585b7757dd84b7c030233a!]
|
|
5
|
+
SONAR_TOKEN: ENCRYPTED[!4e5894b7b32f763db69e70fb56188d3f42539db36374b1f97c425f37a7ecd441c28a9da267c461b9cc8fb53124f14c22!]
|
|
6
|
+
NPM_DEPLOYER_AUTH: ENCRYPTED[!f4021c529fe14ea134a81e2a147782f095a68c3805328c09409e06e229f66e7fbe742e1b3c522a0cdc2187e730de3814!]
|
|
7
|
+
# Use bash (instead of sh on linux or cmd.exe on windows)
|
|
8
|
+
CIRRUS_SHELL: bash
|
|
9
|
+
|
|
10
|
+
only_sonarsource_qa: &ONLY_SONARSOURCE_QA
|
|
11
|
+
only_if: $CIRRUS_USER_COLLABORATOR == 'true' && ($CIRRUS_PR != "" || $CIRRUS_BRANCH == "master" || $CIRRUS_BRANCH =~ "branch-.*" || $CIRRUS_BRANCH =~ "dogfood-on-.*")
|
|
12
|
+
|
|
13
|
+
container_definition: &CONTAINER_DEFINITION
|
|
14
|
+
builder_image_project: language-team
|
|
15
|
+
builder_image_name: docker-builder-lt-v1
|
|
16
|
+
cluster_name: cirrus-ci-lt-cluster
|
|
17
|
+
zone: us-central1-a
|
|
18
|
+
namespace: default
|
|
19
|
+
use_in_memory_disk: true
|
|
20
|
+
|
|
21
|
+
plugin_qa_task:
|
|
22
|
+
<<: *ONLY_SONARSOURCE_QA
|
|
23
|
+
gke_container:
|
|
24
|
+
matrix:
|
|
25
|
+
- dockerfile: .cirrus/nodejs-10.Dockerfile
|
|
26
|
+
- dockerfile: .cirrus/nodejs-12.Dockerfile
|
|
27
|
+
- dockerfile: .cirrus/nodejs-14.Dockerfile
|
|
28
|
+
- dockerfile: .cirrus/nodejs-15.Dockerfile
|
|
29
|
+
<<: *CONTAINER_DEFINITION
|
|
30
|
+
cpu: 3
|
|
31
|
+
memory: 8G
|
|
32
|
+
env:
|
|
33
|
+
CIRRUS_CLONE_DEPTH: 10
|
|
34
|
+
SONARSOURCE_QA: true
|
|
35
|
+
npmrc_file:
|
|
36
|
+
path: ${HOME}/.npmrc
|
|
37
|
+
variable_name: NPM_DEPLOYER_AUTH
|
|
38
|
+
script:
|
|
39
|
+
- init_git_submodules -r
|
|
40
|
+
- yarn
|
|
41
|
+
- yarn typecheck
|
|
42
|
+
- yarn build
|
|
43
|
+
- ./scripts/test-ci.sh
|
|
44
|
+
- yarn prettier --list-different "{src,tests}/**/*.{js,ts}"
|
|
45
|
+
- yarn lint
|
|
46
|
+
- yarn ruling
|
|
47
|
+
- ./scripts/analyze_and_publish.sh
|
package/.eslintignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tests/resources
|
package/README.md
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
# eslint-plugin-sonarjs [](https://badge.fury.io/js/eslint-plugin-sonarjs) [](https://badge.fury.io/js/eslint-plugin-sonarjs) [](https://cirrus-ci.com/github/SonarSource/eslint-plugin-sonarjs) [](https://sonarcloud.io/dashboard?id=eslint-plugin-sonarjs) [](https://sonarcloud.io/dashboard?id=eslint-plugin-sonarjs)
|
|
2
2
|
|
|
3
3
|
SonarJS rules for ESLint to detect bugs and suspicious patterns in your code.
|
|
4
4
|
|
|
5
|
-
_[We also have a plugin for TSLint](https://github.com/SonarSource/SonarTS)_
|
|
6
|
-
|
|
7
5
|
## Rules
|
|
8
6
|
|
|
9
7
|
### Bug Detection :bug:
|
|
@@ -25,22 +23,27 @@ Code Smells, or maintainability issues, are raised for places of code which migh
|
|
|
25
23
|
* Cognitive Complexity of functions should not be too high ([`cognitive-complexity`])
|
|
26
24
|
* "switch" statements should not have too many "case" clauses ([`max-switch-cases`])
|
|
27
25
|
* Collapsible "if" statements should be merged ([`no-collapsible-if`])
|
|
26
|
+
* Collection sizes and array length comparisons should make sense ([`no-collection-size-mischeck`])
|
|
28
27
|
* String literals should not be duplicated ([`no-duplicate-string`])
|
|
29
28
|
* Two branches in a conditional structure should not have exactly the same implementation ([`no-duplicated-branches`])
|
|
30
29
|
* Functions should not have identical implementations ([`no-identical-functions`])
|
|
31
|
-
* Boolean checks should not be inverted ([`no-inverted-boolean-check`])
|
|
30
|
+
* Boolean checks should not be inverted ([`no-inverted-boolean-check`]) (:wrench: *fixable*)
|
|
32
31
|
* Boolean literals should not be redundant ([`no-redundant-boolean`])
|
|
32
|
+
* Jump statements should not be redundant ([`no-redundant-jump`])
|
|
33
|
+
* Conditionals should start on new lines ([`no-same-line-conditional`])
|
|
33
34
|
* "switch" statements should have at least 3 "case" clauses ([`no-small-switch`])
|
|
35
|
+
* Collection and array contents should be used ([`no-unused-collection`])
|
|
34
36
|
* "catch" clauses should do more than rethrow ([`no-useless-catch`])
|
|
35
37
|
* Local variables should not be declared and then immediately returned or thrown ([`prefer-immediate-return`]) (:wrench: *fixable*)
|
|
36
38
|
* Object literal syntax should be used ([`prefer-object-literal`])
|
|
37
39
|
* Return of boolean expressions should not be wrapped into an "if-then-else" statement ([`prefer-single-boolean-return`])
|
|
38
|
-
* A "while" loop should be used instead of a "for" loop ([`prefer-while`]) (:wrench: *fixable*)
|
|
40
|
+
* A "while" loop should be used instead of a "for" loop ([`prefer-while`]) (:wrench: *fixable*)
|
|
39
41
|
|
|
40
42
|
[`cognitive-complexity`]: ./docs/rules/cognitive-complexity.md
|
|
41
43
|
[`max-switch-cases`]: ./docs/rules/max-switch-cases.md
|
|
42
44
|
[`no-all-duplicated-branches`]: ./docs/rules/no-all-duplicated-branches.md
|
|
43
45
|
[`no-collapsible-if`]: ./docs/rules/no-collapsible-if.md
|
|
46
|
+
[`no-collection-size-mischeck`]: ./docs/rules/no-collection-size-mischeck.md
|
|
44
47
|
[`no-duplicate-string`]: ./docs/rules/no-duplicate-string.md
|
|
45
48
|
[`no-duplicated-branches`]: ./docs/rules/no-duplicated-branches.md
|
|
46
49
|
[`no-element-overwrite`]: ./docs/rules/no-element-overwrite.md
|
|
@@ -51,8 +54,11 @@ Code Smells, or maintainability issues, are raised for places of code which migh
|
|
|
51
54
|
[`no-inverted-boolean-check`]: ./docs/rules/no-inverted-boolean-check.md
|
|
52
55
|
[`no-one-iteration-loop`]: ./docs/rules/no-one-iteration-loop.md
|
|
53
56
|
[`no-redundant-boolean`]: ./docs/rules/no-redundant-boolean.md
|
|
57
|
+
[`no-redundant-jump`]: ./docs/rules/no-redundant-jump.md
|
|
58
|
+
[`no-same-line-conditional`]: ./docs/rules/no-same-line-conditional.md
|
|
54
59
|
[`no-small-switch`]: ./docs/rules/no-small-switch.md
|
|
55
60
|
[`no-use-of-empty-return-value`]: ./docs/rules/no-use-of-empty-return-value.md
|
|
61
|
+
[`no-unused-collection`]: ./docs/rules/no-unused-collection.md
|
|
56
62
|
[`no-useless-catch`]: ./docs/rules/no-useless-catch.md
|
|
57
63
|
[`prefer-immediate-return`]: ./docs/rules/prefer-immediate-return.md
|
|
58
64
|
[`prefer-object-literal`]: ./docs/rules/prefer-object-literal.md
|
|
@@ -61,7 +67,7 @@ Code Smells, or maintainability issues, are raised for places of code which migh
|
|
|
61
67
|
|
|
62
68
|
## Prerequisites
|
|
63
69
|
|
|
64
|
-
Node.js (>=
|
|
70
|
+
Node.js (>=10.x).
|
|
65
71
|
|
|
66
72
|
## Usage
|
|
67
73
|
|
|
@@ -91,12 +97,12 @@ npm install eslint-plugin-sonarjs -g # or install globally
|
|
|
91
97
|
|
|
92
98
|
* or enable only some rules manually:
|
|
93
99
|
|
|
94
|
-
```
|
|
100
|
+
```javascript
|
|
95
101
|
{
|
|
96
102
|
"rules": {
|
|
97
103
|
"sonarjs/cognitive-complexity": "error",
|
|
98
104
|
"sonarjs/no-identical-expressions": "error"
|
|
99
|
-
// etc
|
|
105
|
+
// etc.
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
108
|
```
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
3
|
+
exports.configs = exports.rules = void 0;
|
|
4
|
+
const sonarjsRules = [
|
|
4
5
|
["cognitive-complexity", "error"],
|
|
5
6
|
["max-switch-cases", "error"],
|
|
6
7
|
["no-all-duplicated-branches", "error"],
|
|
7
8
|
["no-collapsible-if", "error"],
|
|
9
|
+
["no-collection-size-mischeck", "error"],
|
|
8
10
|
["no-duplicate-string", "error"],
|
|
9
11
|
["no-duplicated-branches", "error"],
|
|
10
12
|
["no-element-overwrite", "error"],
|
|
@@ -15,7 +17,10 @@ var sonarjsRules = [
|
|
|
15
17
|
["no-inverted-boolean-check", "error"],
|
|
16
18
|
["no-one-iteration-loop", "error"],
|
|
17
19
|
["no-redundant-boolean", "error"],
|
|
20
|
+
["no-redundant-jump", "error"],
|
|
21
|
+
["no-same-line-conditional", "error"],
|
|
18
22
|
["no-small-switch", "error"],
|
|
23
|
+
["no-unused-collection", "error"],
|
|
19
24
|
["no-use-of-empty-return-value", "error"],
|
|
20
25
|
["no-useless-catch", "error"],
|
|
21
26
|
["prefer-immediate-return", "error"],
|
|
@@ -23,12 +28,12 @@ var sonarjsRules = [
|
|
|
23
28
|
["prefer-single-boolean-return", "error"],
|
|
24
29
|
["prefer-while", "error"],
|
|
25
30
|
];
|
|
26
|
-
|
|
31
|
+
const sonarjsRuleModules = {};
|
|
27
32
|
exports.rules = sonarjsRuleModules;
|
|
28
|
-
|
|
29
|
-
recommended: { rules: {} },
|
|
33
|
+
const configs = {
|
|
34
|
+
recommended: { plugins: ["sonarjs"], rules: {} },
|
|
30
35
|
};
|
|
31
36
|
exports.configs = configs;
|
|
32
|
-
sonarjsRules.forEach(
|
|
33
|
-
sonarjsRules.forEach(
|
|
37
|
+
sonarjsRules.forEach(rule => (sonarjsRuleModules[rule[0]] = require(`./rules/${rule[0]}`)));
|
|
38
|
+
sonarjsRules.forEach(rule => (configs.recommended.rules[`sonarjs/${rule[0]}`] = rule[1]));
|
|
34
39
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAqBA,MAAM,YAAY,GAAiC;IACjD,CAAC,sBAAsB,EAAE,OAAO,CAAC;IACjC,CAAC,kBAAkB,EAAE,OAAO,CAAC;IAC7B,CAAC,4BAA4B,EAAE,OAAO,CAAC;IACvC,CAAC,mBAAmB,EAAE,OAAO,CAAC;IAC9B,CAAC,6BAA6B,EAAE,OAAO,CAAC;IACxC,CAAC,qBAAqB,EAAE,OAAO,CAAC;IAChC,CAAC,wBAAwB,EAAE,OAAO,CAAC;IACnC,CAAC,sBAAsB,EAAE,OAAO,CAAC;IACjC,CAAC,oBAAoB,EAAE,OAAO,CAAC;IAC/B,CAAC,yBAAyB,EAAE,OAAO,CAAC;IACpC,CAAC,wBAAwB,EAAE,OAAO,CAAC;IACnC,CAAC,0BAA0B,EAAE,OAAO,CAAC;IACrC,CAAC,2BAA2B,EAAE,OAAO,CAAC;IACtC,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAClC,CAAC,sBAAsB,EAAE,OAAO,CAAC;IACjC,CAAC,mBAAmB,EAAE,OAAO,CAAC;IAC9B,CAAC,0BAA0B,EAAE,OAAO,CAAC;IACrC,CAAC,iBAAiB,EAAE,OAAO,CAAC;IAC5B,CAAC,sBAAsB,EAAE,OAAO,CAAC;IACjC,CAAC,8BAA8B,EAAE,OAAO,CAAC;IACzC,CAAC,kBAAkB,EAAE,OAAO,CAAC;IAC7B,CAAC,yBAAyB,EAAE,OAAO,CAAC;IACpC,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAClC,CAAC,8BAA8B,EAAE,OAAO,CAAC;IACzC,CAAC,cAAc,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,MAAM,kBAAkB,GAAQ,EAAE,CAAC;AASJ,mCAAK;AAPpC,MAAM,OAAO,GAA2D;IACtE,WAAW,EAAE,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;CACjD,CAAC;AAKoC,0BAAO;AAH7C,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5F,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,KAAM,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -19,93 +19,110 @@
|
|
|
19
19
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
20
20
|
*/
|
|
21
21
|
// https://jira.sonarsource.com/browse/RSPEC-3776
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const nodes_1 = require("../utils/nodes");
|
|
23
|
+
const locations_1 = require("../utils/locations");
|
|
24
|
+
const DEFAULT_THRESHOLD = 15;
|
|
25
|
+
const rule = {
|
|
26
26
|
meta: {
|
|
27
|
+
type: "suggestion",
|
|
27
28
|
schema: [
|
|
28
29
|
{ type: "integer", minimum: 0 },
|
|
29
30
|
{
|
|
30
31
|
// internal parameter
|
|
31
|
-
enum: ["sonar-runtime"],
|
|
32
|
+
enum: ["sonar-runtime", "metric"],
|
|
32
33
|
},
|
|
33
34
|
],
|
|
34
35
|
},
|
|
35
|
-
create
|
|
36
|
-
|
|
36
|
+
create(context) {
|
|
37
|
+
const threshold = getThreshold();
|
|
38
|
+
const isFileComplexity = context.options.includes("metric");
|
|
39
|
+
/** Complexity of the file */
|
|
40
|
+
let fileComplexity = 0;
|
|
37
41
|
/** Complexity of the current function if it is *not* considered nested to the first level function */
|
|
38
|
-
|
|
42
|
+
let complexityIfNotNested = [];
|
|
39
43
|
/** Complexity of the current function if it is considered nested to the first level function */
|
|
40
|
-
|
|
44
|
+
let complexityIfNested = [];
|
|
41
45
|
/** Current nesting level (number of enclosing control flow statements and functions) */
|
|
42
|
-
|
|
46
|
+
let nesting = 0;
|
|
43
47
|
/** Indicator if the current top level function has a structural (generated by control flow statements) complexity */
|
|
44
|
-
|
|
48
|
+
let topLevelHasStructuralComplexity = false;
|
|
45
49
|
/** Own (not including nested functions) complexity of the current top function */
|
|
46
|
-
|
|
50
|
+
let topLevelOwnComplexity = [];
|
|
47
51
|
/** Nodes that should increase nesting level */
|
|
48
|
-
|
|
52
|
+
const nestingNodes = new Set();
|
|
49
53
|
/** Set of already considered (with already computed complexity) logical expressions */
|
|
50
|
-
|
|
54
|
+
const consideredLogicalExpressions = new Set();
|
|
51
55
|
/** Stack of enclosing functions */
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
const enclosingFunctions = [];
|
|
57
|
+
let secondLevelFunctions = [];
|
|
54
58
|
return {
|
|
55
|
-
":function":
|
|
59
|
+
":function": (node) => {
|
|
56
60
|
onEnterFunction(node);
|
|
57
61
|
},
|
|
58
|
-
":function:exit"
|
|
62
|
+
":function:exit"(node) {
|
|
59
63
|
onLeaveFunction(node);
|
|
60
64
|
},
|
|
61
|
-
"*"
|
|
65
|
+
"*"(node) {
|
|
62
66
|
if (nestingNodes.has(node)) {
|
|
63
67
|
nesting++;
|
|
64
68
|
}
|
|
65
69
|
},
|
|
66
|
-
"*:exit"
|
|
70
|
+
"*:exit"(node) {
|
|
67
71
|
if (nestingNodes.has(node)) {
|
|
68
72
|
nesting--;
|
|
69
73
|
nestingNodes.delete(node);
|
|
70
74
|
}
|
|
71
75
|
},
|
|
72
|
-
|
|
76
|
+
Program() {
|
|
77
|
+
fileComplexity = 0;
|
|
78
|
+
},
|
|
79
|
+
"Program:exit"(node) {
|
|
80
|
+
if (isFileComplexity) {
|
|
81
|
+
// as issues are the only communication channel of a rule
|
|
82
|
+
// we pass data as serialized json as an issue message
|
|
83
|
+
context.report({ node, message: fileComplexity.toString() });
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
IfStatement(node) {
|
|
73
87
|
visitIfStatement(node);
|
|
74
88
|
},
|
|
75
|
-
ForStatement
|
|
89
|
+
ForStatement(node) {
|
|
76
90
|
visitLoop(node);
|
|
77
91
|
},
|
|
78
|
-
ForInStatement
|
|
92
|
+
ForInStatement(node) {
|
|
79
93
|
visitLoop(node);
|
|
80
94
|
},
|
|
81
|
-
ForOfStatement
|
|
95
|
+
ForOfStatement(node) {
|
|
82
96
|
visitLoop(node);
|
|
83
97
|
},
|
|
84
|
-
DoWhileStatement
|
|
98
|
+
DoWhileStatement(node) {
|
|
85
99
|
visitLoop(node);
|
|
86
100
|
},
|
|
87
|
-
WhileStatement
|
|
101
|
+
WhileStatement(node) {
|
|
88
102
|
visitLoop(node);
|
|
89
103
|
},
|
|
90
|
-
SwitchStatement
|
|
104
|
+
SwitchStatement(node) {
|
|
91
105
|
visitSwitchStatement(node);
|
|
92
106
|
},
|
|
93
|
-
ContinueStatement
|
|
107
|
+
ContinueStatement(node) {
|
|
94
108
|
visitContinueOrBreakStatement(node);
|
|
95
109
|
},
|
|
96
|
-
BreakStatement
|
|
110
|
+
BreakStatement(node) {
|
|
97
111
|
visitContinueOrBreakStatement(node);
|
|
98
112
|
},
|
|
99
|
-
CatchClause
|
|
113
|
+
CatchClause(node) {
|
|
100
114
|
visitCatchClause(node);
|
|
101
115
|
},
|
|
102
|
-
LogicalExpression
|
|
116
|
+
LogicalExpression(node) {
|
|
103
117
|
visitLogicalExpression(node);
|
|
104
118
|
},
|
|
105
|
-
ConditionalExpression
|
|
119
|
+
ConditionalExpression(node) {
|
|
106
120
|
visitConditionalExpression(node);
|
|
107
121
|
},
|
|
108
122
|
};
|
|
123
|
+
function getThreshold() {
|
|
124
|
+
return context.options[0] !== undefined ? context.options[0] : DEFAULT_THRESHOLD;
|
|
125
|
+
}
|
|
109
126
|
function onEnterFunction(node) {
|
|
110
127
|
if (enclosingFunctions.length === 0) {
|
|
111
128
|
// top level function
|
|
@@ -129,15 +146,15 @@ var rule = {
|
|
|
129
146
|
if (enclosingFunctions.length === 0) {
|
|
130
147
|
// top level function
|
|
131
148
|
if (topLevelHasStructuralComplexity) {
|
|
132
|
-
|
|
133
|
-
secondLevelFunctions.forEach(
|
|
134
|
-
|
|
149
|
+
let totalComplexity = topLevelOwnComplexity;
|
|
150
|
+
secondLevelFunctions.forEach(secondLevelFunction => {
|
|
151
|
+
totalComplexity = totalComplexity.concat(secondLevelFunction.complexityIfNested);
|
|
135
152
|
});
|
|
136
|
-
checkFunction(
|
|
153
|
+
checkFunction(totalComplexity, locations_1.getMainFunctionTokenLocation(node, nodes_1.getParent(context), context));
|
|
137
154
|
}
|
|
138
155
|
else {
|
|
139
156
|
checkFunction(topLevelOwnComplexity, locations_1.getMainFunctionTokenLocation(node, nodes_1.getParent(context), context));
|
|
140
|
-
secondLevelFunctions.forEach(
|
|
157
|
+
secondLevelFunctions.forEach(secondLevelFunction => {
|
|
141
158
|
checkFunction(secondLevelFunction.complexityIfThisSecondaryIsTopLevel, locations_1.getMainFunctionTokenLocation(secondLevelFunction.node, secondLevelFunction.parent, context));
|
|
142
159
|
});
|
|
143
160
|
}
|
|
@@ -145,9 +162,9 @@ var rule = {
|
|
|
145
162
|
else if (enclosingFunctions.length === 1) {
|
|
146
163
|
// second level function
|
|
147
164
|
secondLevelFunctions.push({
|
|
148
|
-
node
|
|
165
|
+
node,
|
|
149
166
|
parent: nodes_1.getParent(context),
|
|
150
|
-
complexityIfNested
|
|
167
|
+
complexityIfNested,
|
|
151
168
|
complexityIfThisSecondaryIsTopLevel: complexityIfNotNested,
|
|
152
169
|
loc: locations_1.getMainFunctionTokenLocation(node, nodes_1.getParent(context), context),
|
|
153
170
|
});
|
|
@@ -158,8 +175,8 @@ var rule = {
|
|
|
158
175
|
}
|
|
159
176
|
}
|
|
160
177
|
function visitIfStatement(ifStatement) {
|
|
161
|
-
|
|
162
|
-
|
|
178
|
+
const parent = nodes_1.getParent(context);
|
|
179
|
+
const { loc: ifLoc } = locations_1.getFirstToken(ifStatement, context);
|
|
163
180
|
// if the current `if` statement is `else if`, do not count it in structural complexity
|
|
164
181
|
if (nodes_1.isIfStatement(parent) && parent.alternate === ifStatement) {
|
|
165
182
|
addComplexity(ifLoc);
|
|
@@ -174,7 +191,7 @@ var rule = {
|
|
|
174
191
|
// - add +1 complexity
|
|
175
192
|
if (ifStatement.alternate && !nodes_1.isIfStatement(ifStatement.alternate)) {
|
|
176
193
|
nestingNodes.add(ifStatement.alternate);
|
|
177
|
-
|
|
194
|
+
const elseTokenLoc = locations_1.getFirstTokenAfter(ifStatement.consequent, context).loc;
|
|
178
195
|
addComplexity(elseTokenLoc);
|
|
179
196
|
}
|
|
180
197
|
}
|
|
@@ -184,8 +201,7 @@ var rule = {
|
|
|
184
201
|
}
|
|
185
202
|
function visitSwitchStatement(switchStatement) {
|
|
186
203
|
addStructuralComplexity(locations_1.getFirstToken(switchStatement, context).loc);
|
|
187
|
-
for (
|
|
188
|
-
var switchCase = _a[_i];
|
|
204
|
+
for (const switchCase of switchStatement.cases) {
|
|
189
205
|
nestingNodes.add(switchCase);
|
|
190
206
|
}
|
|
191
207
|
}
|
|
@@ -199,19 +215,18 @@ var rule = {
|
|
|
199
215
|
nestingNodes.add(catchClause.body);
|
|
200
216
|
}
|
|
201
217
|
function visitConditionalExpression(conditionalExpression) {
|
|
202
|
-
|
|
218
|
+
const questionTokenLoc = locations_1.getFirstTokenAfter(conditionalExpression.test, context).loc;
|
|
203
219
|
addStructuralComplexity(questionTokenLoc);
|
|
204
220
|
nestingNodes.add(conditionalExpression.consequent);
|
|
205
221
|
nestingNodes.add(conditionalExpression.alternate);
|
|
206
222
|
}
|
|
207
223
|
function visitLogicalExpression(logicalExpression) {
|
|
208
224
|
if (!consideredLogicalExpressions.has(logicalExpression)) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
for (
|
|
212
|
-
var current = flattenedLogicalExpressions_1[_i];
|
|
225
|
+
const flattenedLogicalExpressions = flattenLogicalExpression(logicalExpression);
|
|
226
|
+
let previous;
|
|
227
|
+
for (const current of flattenedLogicalExpressions) {
|
|
213
228
|
if (!previous || previous.operator !== current.operator) {
|
|
214
|
-
|
|
229
|
+
const operatorTokenLoc = locations_1.getFirstTokenAfter(logicalExpression.left, context).loc;
|
|
215
230
|
addComplexity(operatorTokenLoc);
|
|
216
231
|
}
|
|
217
232
|
previous = current;
|
|
@@ -221,27 +236,35 @@ var rule = {
|
|
|
221
236
|
function flattenLogicalExpression(node) {
|
|
222
237
|
if (nodes_1.isLogicalExpression(node)) {
|
|
223
238
|
consideredLogicalExpressions.add(node);
|
|
224
|
-
return flattenLogicalExpression(node.left)
|
|
239
|
+
return [...flattenLogicalExpression(node.left), node, ...flattenLogicalExpression(node.right)];
|
|
225
240
|
}
|
|
226
241
|
return [];
|
|
227
242
|
}
|
|
228
243
|
function addStructuralComplexity(location) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (enclosingFunctions.length ===
|
|
244
|
+
const added = nesting + 1;
|
|
245
|
+
const complexityPoint = { complexity: added, location };
|
|
246
|
+
if (enclosingFunctions.length === 0) {
|
|
247
|
+
// top level scope
|
|
248
|
+
fileComplexity += added;
|
|
249
|
+
}
|
|
250
|
+
else if (enclosingFunctions.length === 1) {
|
|
232
251
|
// top level function
|
|
233
252
|
topLevelHasStructuralComplexity = true;
|
|
234
253
|
topLevelOwnComplexity.push(complexityPoint);
|
|
235
254
|
}
|
|
236
255
|
else {
|
|
237
256
|
// second+ level function
|
|
238
|
-
complexityIfNested.push({ complexity: added + 1, location
|
|
257
|
+
complexityIfNested.push({ complexity: added + 1, location });
|
|
239
258
|
complexityIfNotNested.push(complexityPoint);
|
|
240
259
|
}
|
|
241
260
|
}
|
|
242
261
|
function addComplexity(location) {
|
|
243
|
-
|
|
244
|
-
if (enclosingFunctions.length ===
|
|
262
|
+
const complexityPoint = { complexity: 1, location };
|
|
263
|
+
if (enclosingFunctions.length === 0) {
|
|
264
|
+
// top level scope
|
|
265
|
+
fileComplexity += 1;
|
|
266
|
+
}
|
|
267
|
+
else if (enclosingFunctions.length === 1) {
|
|
245
268
|
// top level function
|
|
246
269
|
topLevelOwnComplexity.push(complexityPoint);
|
|
247
270
|
}
|
|
@@ -251,18 +274,21 @@ var rule = {
|
|
|
251
274
|
complexityIfNotNested.push(complexityPoint);
|
|
252
275
|
}
|
|
253
276
|
}
|
|
254
|
-
function checkFunction(complexity, loc) {
|
|
255
|
-
|
|
256
|
-
|
|
277
|
+
function checkFunction(complexity = [], loc) {
|
|
278
|
+
const complexityAmount = complexity.reduce((acc, cur) => acc + cur.complexity, 0);
|
|
279
|
+
fileComplexity += complexityAmount;
|
|
280
|
+
if (isFileComplexity) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
257
283
|
if (complexityAmount > threshold) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
284
|
+
const secondaryLocations = complexity.map(complexityPoint => {
|
|
285
|
+
const { complexity, location } = complexityPoint;
|
|
286
|
+
const message = complexity === 1 ? "+1" : `+${complexity} (incl. ${complexity - 1} for nesting)`;
|
|
261
287
|
return locations_1.issueLocation(location, undefined, message);
|
|
262
288
|
});
|
|
263
289
|
locations_1.report(context, {
|
|
264
|
-
message:
|
|
265
|
-
loc
|
|
290
|
+
message: `Refactor this function to reduce its Cognitive Complexity from ${complexityAmount} to the ${threshold} allowed.`,
|
|
291
|
+
loc,
|
|
266
292
|
}, secondaryLocations, complexityAmount - threshold);
|
|
267
293
|
}
|
|
268
294
|
}
|