├── .editorconfig ├── .eslintignore ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── lib ├── copyright-header.txt └── index.js ├── package.json ├── scripts ├── build-core.js └── node-tests.js └── tests ├── qunit.config.js ├── tests.name.js ├── tests.params.js ├── tests.return.js ├── tests.this.js ├── tests.trivial.js └── tests.where.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = tab 8 | indent_size = 4 9 | 10 | [*.md] 11 | indent_style = space 12 | indent_size = 4 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | README.md 2 | node_modules/ 3 | dist/ 4 | coverage/ 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [getify] 4 | patreon: getify 5 | custom: ['https://www.paypal.com/paypalme2/getify','https://www.blockchain.com/btc/payment_request?address=3GrZuzooWAAjufEydb7c8MrUYznCiHHT9U&message=getify+Support+Donation'] 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [ 20.x, 22.x ] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | 25 | - name: Install dependencies 26 | run: npm install 27 | 28 | - name: Build 29 | run: npm run build 30 | 31 | - name: Run tests 32 | run: npm run test:all 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .coveralls.yml 2 | .history 3 | node_modules/ 4 | dist/ 5 | coverage/ 6 | test-snippets/ 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .npmignore 2 | .gitignore 3 | .coveralls.yml 4 | .history 5 | node_modules/ 6 | coverage/ 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - 12 5 | - 14 6 | - 16 7 | 8 | git: 9 | depth: 5 10 | 11 | cache: 12 | directories: 13 | - node_modules 14 | 15 | env: 16 | - TEST_PACKAGE=true 17 | branches: 18 | only: 19 | - master 20 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019-2021 Kyle Simpson 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESLint Plugin: proper-arrows 2 | 3 | [![Build Status](https://travis-ci.org/getify/eslint-plugin-proper-arrows.svg?branch=master)](https://travis-ci.org/getify/eslint-plugin-proper-arrows) 4 | [![npm Module](https://badge.fury.io/js/%40getify%2Feslint-plugin-proper-arrows.svg)](https://www.npmjs.org/package/@getify/eslint-plugin-proper-arrows) 5 | [![Dependencies](https://david-dm.org/getify/eslint-plugin-proper-arrows.svg)](https://david-dm.org/getify/eslint-plugin-proper-arrows) 6 | [![devDependencies](https://david-dm.org/getify/eslint-plugin-proper-arrows/dev-status.svg)](https://david-dm.org/getify/eslint-plugin-proper-arrows?type=dev) 7 | [![Coverage Status](https://coveralls.io/repos/github/getify/eslint-plugin-proper-arrows/badge.svg?branch=master)](https://coveralls.io/github/getify/eslint-plugin-proper-arrows?branch=master) 8 | 9 | ## Overview 10 | 11 | The **proper-arrows** ESLint plugin provides rules that control the definitions of `=>` arrow functions, restricting them to a narrower and more proper/readable form. 12 | 13 | The rules defined in this plugin: 14 | 15 | * [`"params"`](#rule-params): controls definitions of `=>` arrow function parameters, such as forbidding unused parameters, forbidding short/unsemantic parameter names, etc. 16 | 17 | * [`"name"`](#rule-name): requires `=>` arrow functions to only be used in positions where they receive an inferred name (i.e., assigned to a variable or property, etc), to avoid the poor readbility/debuggability of anonymous function expressions. 18 | 19 | **Note:** This rule is like the "as-needed" mode of the [built-in ESLint "func-names" rule](https://eslint.org/docs/rules/func-names), but applied to `=>` arrow functions; the built-in rule ignores them. 20 | 21 | * [`"where"`](#rule-where): restricts where in program structure `=>` arrow functions can be used: forbidding them in the global/top-level-module scope, object properties, `export` statements, etc. 22 | 23 | * [`"return"`](#rule-return): restricts the concise return value kind for `=>` arrow functions, such as forbidding object literal concise returns (`x => ({ x })`), forbidding concise returns of conditional/ternary expressions (`x => x ? y : z`), etc. 24 | 25 | * [`"this"`](#rule-this): requires/disallows `=>` arrow functions using a `this` reference, in the `=>` arrow function itself or in a nested `=>` arrow function; optionally, this rule can forbid `this`-containing `=>` arrow functions only from the global scope. 26 | 27 | ### Trivial `=>` Arrow Functions 28 | 29 | It's common for `=>` arrow functions to be used as a shorthand for simple/trivial function roles, such as: 30 | 31 | ```js 32 | // no-op function 33 | () => {}; 34 | 35 | // constant function 36 | () => 42; 37 | 38 | // closure function 39 | () => v; 40 | 41 | // identity function 42 | v => v; 43 | ``` 44 | 45 | **Note:** To be specific on the definition of "trivial" being used here: trivial functions have no more than 1 parameter (simple identifier only), and either have no return (`{}`) or a concise return of: a single variable, primitive (non-object) literal, or a `void __` expression. 46 | 47 | These types of functions are so simple that they don't suffer much from readability issues despite their concise syntax. As such, it's likely preferred to allow them even if one of the rules here would normally report them. 48 | 49 | By default, **all rules ignore** these trivial functions. To **force a rule to check trivial functions**, include this configuration option for the rule: 50 | 51 | ```json 52 | { "trivial": true } 53 | ``` 54 | 55 | **Note:** The `"trivial"` option **will not necessarily report** trivial functions, but it will force trivial functions to be checked by the rule/mode in question. 56 | 57 | ## Enabling The Plugin 58 | 59 | To use **proper-arrows**, load it as a plugin into ESLint and configure the rules as desired. 60 | 61 | ### `extends` 62 | 63 | If you'd like to use the **proper-arrows** plugin in a recommended configuration preset, you can add the plugin in the `extends` clause of your ESLint configuration, and pick a preset by name: 64 | 65 | ```js 66 | "extends": [ 67 | // .. 68 | "plugin:@getify/proper-arrows/CONFIG-PRESET-NAME", 69 | // .. 70 | ] 71 | ``` 72 | 73 | **Note:** All included configuration presets not only define specific rule configurations but also automatically load the plugin itself, so you *don't* need to list **proper-arrows** in the `plugins` clause. 74 | 75 | The available configuration presets to choose from: 76 | 77 | * `getify-says`: This is my personal configuration. See the [preset definition](/lib/index.js#L5-L14). 78 | 79 | * ..TBA.. 80 | 81 | It's important to note that you can still override any of the preset rule definitions in your configuration. Think of these presets as convenience "defaults" that can still be customized. 82 | 83 | ### Flat Config 84 | 85 | If you're using ESLint's newer flat config format, you can use the plugin's flat configuration preset: 86 | 87 | ```js 88 | // eslint.config.js 89 | import properArrows from "@getify/eslint-plugin-proper-arrows"; 90 | 91 | export default [ 92 | properArrows.flatConfigs["CONFIG-PRESET-NAME"], // i.e., "getify-says", etc 93 | // ... other configs 94 | ]; 95 | ``` 96 | 97 | This will automatically load the plugin and apply the recommended preset rules configuration. 98 | The preset includes the same rules configuration as the traditional `"extends": ["plugin:@getify/proper-arrows/CONFIG-PRESET-NAME"]` format. 99 | 100 | You can still override any of the preset rules in your configuration after including the flat config: 101 | 102 | ```js 103 | import properArrows from "@getify/eslint-plugin-proper-arrows"; 104 | 105 | export default [ 106 | properArrows.flatConfigs["getify-says"], 107 | { 108 | rules: { 109 | "@getify/proper-arrows/params": ["error", {"unused": "none"}], 110 | // ... other rule overrides 111 | } 112 | } 113 | ]; 114 | ``` 115 | 116 | ### `.eslintrc.json` 117 | 118 | To load the plugin and enable its rules via a local or global `.eslintrc.json` configuration file: 119 | 120 | ```json 121 | "plugins": [ 122 | "@getify/proper-arrows" 123 | ], 124 | "rules": { 125 | "@getify/proper-arrows/params": ["error",{"unused":"trailing"}], 126 | "@getify/proper-arrows/name": ["error",{"trivial":false}], 127 | "@getify/proper-arrows/where": ["error",{"global":true}], 128 | "@getify/proper-arrows/return": ["error",{"object":true}], 129 | "@getify/proper-arrows/this": ["error","always",{"no-global":true}] 130 | } 131 | ``` 132 | 133 | For users of ESLint's newer flat config format, the configuration would look like this: 134 | 135 | ```js 136 | import properArrows from "@getify/eslint-plugin-proper-arrows"; 137 | 138 | { 139 | plugins: { 140 | "@getify/proper-arrows": properArrows 141 | }, 142 | rules: { 143 | "@getify/proper-arrows/params": ["error",{"unused":"trailing"}], 144 | "@getify/proper-arrows/name": ["error",{"trivial":false}], 145 | "@getify/proper-arrows/where": ["error",{"global":true}], 146 | "@getify/proper-arrows/return": ["error",{"object":true}], 147 | "@getify/proper-arrows/this": ["error","always",{"no-global":true}] 148 | } 149 | } 150 | ``` 151 | 152 | ### `package.json` 153 | 154 | To load the plugin and enable its rules via a project's `package.json`: 155 | 156 | ```json 157 | "eslintConfig": { 158 | "plugins": [ 159 | "@getify/proper-arrows" 160 | ], 161 | "rules": { 162 | "@getify/proper-arrows/params": ["error",{"unused":"trailing"}], 163 | "@getify/proper-arrows/name": ["error",{"trivial":false}], 164 | "@getify/proper-arrows/name": ["error",{"global":true}], 165 | "@getify/proper-arrows/return": ["error",{"object":true}], 166 | "@getify/proper-arrows/this": ["error","always",{"no-global":true}] 167 | } 168 | } 169 | ``` 170 | 171 | ### ESLint CLI parameters 172 | 173 | To load the plugin and enable its rules via ESLint CLI parameters, use `--plugin` and `--rule` flags: 174 | 175 | ```cmd 176 | eslint .. --plugin='@getify/proper-arrows' --rule='@getify/proper-arrows/params: [error,{"unused":"trailing"}]' .. 177 | ``` 178 | 179 | ```cmd 180 | eslint .. --plugin='@getify/proper-arrows' --rule='@getify/proper-arrows/name: [error,{"trivial":false}]' .. 181 | ``` 182 | 183 | ```cmd 184 | eslint .. --plugin='@getify/proper-arrows' --rule='@getify/proper-arrows/where: [error,{"global":true}]' .. 185 | ``` 186 | 187 | ```cmd 188 | eslint .. --plugin='@getify/proper-arrows' --rule='@getify/proper-arrows/return: [error,{"object":true}' .. 189 | ``` 190 | 191 | ```cmd 192 | eslint .. --plugin='@getify/proper-arrows' --rule='@getify/proper-arrows/this: [error,always,{"no-global":true}]' .. 193 | ``` 194 | 195 | ### ESLint Node API 196 | 197 | To use this plugin in Node.js with the ESLint API, require the npm module, and then (for example) pass the rule's definition to `Linter#defineRule(..)`, similar to: 198 | 199 | ```js 200 | var properArrows = require("@getify/eslint-plugin-proper-arrows"); 201 | 202 | // .. 203 | 204 | var eslinter = new (require("eslint").Linter)(); 205 | 206 | eslinter.defineRule("@getify/proper-arrows/params",properArrows.rules.params); 207 | 208 | eslinter.defineRule("@getify/proper-arrows/name",properArrows.rules.name); 209 | 210 | eslinter.defineRule("@getify/proper-arrows/where",properArrows.rules.where); 211 | 212 | eslinter.defineRule("@getify/proper-arrows/return",properArrows.rules.return); 213 | 214 | eslinter.defineRule("@getify/proper-arrows/this",properArrows.rules.this); 215 | ``` 216 | 217 | Then lint some code like this: 218 | 219 | ```js 220 | eslinter.verify(".. some code ..",{ 221 | rules: { 222 | "@getify/proper-arrows/params": ["error",{unused:"trailing"}], 223 | "@getify/proper-arrows/name": ["error",{trivial:false}], 224 | "@getify/proper-arrows/where": ["error",{trivial:true}], 225 | "@getify/proper-arrows/return": ["error",{object:true}], 226 | "@getify/proper-arrows/this": ["error","always",{"no-global":true}] 227 | } 228 | }); 229 | ``` 230 | 231 | ### Inline Comments 232 | 233 | Once the plugin is loaded, the rule can be configured using inline code comments if desired, such as: 234 | 235 | ```js 236 | /* eslint "@getify/proper-arrows/params": ["error",{"unused":"trailing"}] */ 237 | ``` 238 | 239 | ```js 240 | /* eslint "@getify/proper-arrows/name": "error" */ 241 | ``` 242 | 243 | ```js 244 | /* eslint "@getify/proper-arrows/return": ["error",{"object":true}] */ 245 | ``` 246 | 247 | ```js 248 | /* eslint "@getify/proper-arrows/this": ["error","always",{"no-global":true}] */ 249 | ``` 250 | 251 | ## Rule: `"params"` 252 | 253 | The **proper-arrows**/*params* rule controls definitions of parameters for `=>` arrow functions. 254 | 255 | This rule can be configured to forbid unused parameter names (`"unused"`), limit the number of parameters (`"count"`), and require parameter names to be at least a certain length (`"length"`). Also, this rule can specify a list of exceptions to always allow certain parameter names (`"allow"`). 256 | 257 | To turn this rule on: 258 | 259 | ```json 260 | "@getify/proper-arrows/params": "error" 261 | ``` 262 | 263 | ```json 264 | "@getify/proper-arrows/params": [ "error", { "unused": true, "count": 3, "length": 4, "allow": [ "e", "err" ], "trivial": false } ] 265 | ``` 266 | 267 | The main purpose of this rule is to avoid readability harm for `=>` arrow functions by ensuring the parameters are clean and "proper". 268 | 269 | By forbidding unused parameters, the reader is not confused searching for their usage. By requiring parameters that are long enough to have meaningful names, the reader can understand the function better. By limiting the number of parameters, the reader can more easily visually distinguish the whole function definition. 270 | 271 | For example: 272 | 273 | ```js 274 | var fn = (data,user) => ajax(user.id,data); 275 | ``` 276 | 277 | In this snippet, the `=>` arrow function has two parameters, and both are 4 characters long and used in the function body. Therefore, the **proper-arrows**/*params* rule would not report any errors. 278 | 279 | By contrast, this rule *would* report errors for: 280 | 281 | ```js 282 | var fn = (d,u,m,b) => ajax(u.id,d); 283 | ``` 284 | 285 | Here, the `=>` arrow function has 4 parameters (too many!), each one is a single character (too short!), and two of them are unused. 286 | 287 | ### Rule Configuration 288 | 289 | The **proper-arrows**/*params* rule can be configured with any combination of three modes: `"unused"`, `"count"`, and `"length"`. Also, parameter names can be explicitly allowed by name (thus not reporting any error for these modes) in the `"allowed"` setting. 290 | 291 | **Note:** The default behavior is that all three modes are turned on. You must specifically configure each mode to (effectively) disable it. 292 | 293 | * [`"unused"`](#rule-params-configuration-unused) (default: `"all"`) forbids named parameters that are not used inside the function. Can also be set to `"trailing"` to only report unused errors for trailing parameters -- that is, only those which come after the last parameter that is used -- or set to `"none"` to disable this mode. 294 | 295 | * [`"count"`](#rule-params-configuration-count) (default: `3`) is the maximum count for parameters on an `=>` arrow function. All parameters are counted to check against the limit, including any `"allowed"` parameters and the "...rest" parameter. Set to a larger number to effectively disable this mode. 296 | 297 | * [`"length"`](#rule-params-configuration-length) (default: `2`) is the minimum length of allowed parameter names. Set to `0` to effectively disable this mode. 298 | 299 | * [`"allowed"`](#rule-params-configuration-allowed) (default: `[]`) is a list of parameter names to ignore and not report any errors for. 300 | 301 | #### Rule `"params"` Configuration: `"unused"` 302 | 303 | **Note:** This rule mode resembles the [built-in "no-unused-vars" rule](https://eslint.org/docs/rules/no-unused-vars), but instead focuses only on the parameters of `=>` arrow functions. 304 | 305 | To configure this rule mode (default: `"all"`): 306 | 307 | ```json 308 | "@getify/proper-arrows/params": "error" 309 | ``` 310 | 311 | ```json 312 | "@getify/proper-arrows/params": [ "error", { "unused": "all", "trivial": false } ] 313 | ``` 314 | 315 | The `"unused": "all"` (default) mode checks each parameter to see if it's used anywhere within the function. If not, the parameter is reported as an error. 316 | 317 | For example: 318 | 319 | ```js 320 | var fn1 = (one,two) => one + two; 321 | 322 | var fn2 = (one,two = one) => two * 2; 323 | 324 | var fn3 = one => two => three => one * two * three; 325 | ``` 326 | 327 | These statements all report no errors, because all parameters are used somewhere in each function. 328 | 329 | By contrast, this rule *would* report errors for: 330 | 331 | ```js 332 | var fn1 = (one,two) => one * 2; 333 | 334 | var fn2 = (one,...rest) => one * 2; 335 | 336 | var fn3 = one => two => three => one * three; 337 | ``` 338 | 339 | In each statement, a parameter is defined in an `=>` arrow function that is not used within. 340 | 341 | ##### `"unused": "trailing"` 342 | 343 | It is sometimes desired to name some positional parameters that are effectively ignored by the function body, while using other named parameters that come after. As such, it can be helpful to suppress reports except on unused parameters that come after the last used parameter in the function signature. 344 | 345 | In the `"unused": "trailing"` mode, only unused parameters that come positionally after the last used parameter are reported: 346 | 347 | ```js 348 | var fn1 = (one,two,three) => two * 2; 349 | ``` 350 | 351 | In this snippet, since `two` is the last used parameter, only `three` would be reported as an unused parameter (ignores `one`). 352 | 353 | ##### `"unused": "none"` 354 | 355 | Disables this `"unused"` mode; no checks are made for any unused parameters. 356 | 357 | #### Rule `"params"` Configuration: `"count"` 358 | 359 | To configure this rule mode (default: `3`): 360 | 361 | ```json 362 | "@getify/proper-arrows/params": "error" 363 | ``` 364 | 365 | ```json 366 | "@getify/proper-arrows/params": [ "error", { "count": 3, "trivial": false } ] 367 | ``` 368 | 369 | This rule mode counts all parameters to make sure the maximum `count` limit is not violated. 370 | 371 | For example: 372 | 373 | ```js 374 | var fn0 = () => ""; 375 | 376 | var fn1 = one => one; 377 | 378 | var fn2 = (one,two) => one + two; 379 | 380 | var fn3 = (one,two,three) => one * two * three; 381 | 382 | var fn3b = (one,two,...three) => one * two * three[0]; 383 | ``` 384 | 385 | These statements all report no errors, because all parameter counts are below the limit (default: `3`). 386 | 387 | By contrast, this rule *would* report errors for: 388 | 389 | ```js 390 | var fn4 = (one,two,three,four) => one + two * three / four; 391 | 392 | var fn4b = (one,two,three,...four) => one + two * three / four[0]; 393 | ``` 394 | 395 | In each statement, the parameter count is above the limit. 396 | 397 | #### Rule `"params"` Configuration: `"length"` 398 | 399 | **Note:** This rule mode resembles the [built-in ESLint "id-length" rule](https://eslint.org/docs/rules/id-length), but instead focuses only on the parameters of `=>` arrow functions. 400 | 401 | To configure this rule mode (default: `2`): 402 | 403 | ```json 404 | "@getify/proper-arrows/params": "error" 405 | ``` 406 | 407 | ```json 408 | "@getify/proper-arrows/params": [ "error", { "length": 2, "trivial": false } ] 409 | ``` 410 | 411 | This rule mode checks all parameters to make sure their basic character length is at least the minimum `"length"` threshold. 412 | 413 | For example: 414 | 415 | ```js 416 | var fn0 = () => 42; 417 | 418 | var fn1 = (data) => data.id; 419 | 420 | var fn2 = (user,cb) => ajax(user,cb); 421 | ``` 422 | 423 | These statements all report no errors, because all parameters are at least the length threshold specified (default: `2`), and the absence of a parameter is ignored. 424 | 425 | By contrast, this rule *would* report errors for: 426 | 427 | ```js 428 | users.map(v => v * 2); 429 | ``` 430 | 431 | In this statement, the parameter length of `v` is below the minimum threshold. 432 | 433 | #### Rule `"params"` Configuration: `"allowed"` 434 | 435 | To configure named exceptions to the main three rule modes (default: `[]`): 436 | 437 | ```json 438 | "@getify/proper-arrows/params": [ "error", { "allowed": [ "e", "err" ], "trivial": false } ] 439 | ``` 440 | 441 | This exception list prevents any listed parameter from being reported as an error by any of this **proper-arrows**/*params* rule's modes. 442 | 443 | For example: 444 | 445 | ```js 446 | var fn = (one,two,three,e) => 0; 447 | ``` 448 | 449 | By default, `e` would report all 3 errors: it's unused, it's beyond the default count limit, and it's below the minimum length threshold. However, no errors will be reported if `"e"` is included in the `"allowed"` exception list. 450 | 451 | ## Rule: `"name"` 452 | 453 | The **proper-arrows**/*name* rule requires `=>` arrow functions to be in a position where they will receive an inferred name, such as from being assigned to a variable or property. 454 | 455 | To turn this rule on: 456 | 457 | ```json 458 | "@getify/proper-arrows/name": "error" 459 | ``` 460 | 461 | ```json 462 | "@getify/proper-arrows/name": ["error",{ "trivial": false }] 463 | ``` 464 | 465 | The main purpose of this rule is to reduce the impact of the anonymous nature of `=>` arrow function expressions, making them more readable, improving stack trace output, and giving them a named self-reference (recursion, event unbinding, etc). Primarily, this rule disallows `=>` arrow functions passed directly as inline function expression arguments, as well as returned directly from other functions. 466 | 467 | **Note:** This rule is like the "as-needed" mode of the [built-in ESLint "func-names" rule](https://eslint.org/docs/rules/func-names), but applied to `=>` arrow functions; the built-in rule ignores them. 468 | 469 | Before being used (passed, called, returned, etc), `=>` arrow functions should be assigned somewhere, to receive a name inference: 470 | 471 | ```js 472 | function multiplier(x) { 473 | var multipliedBy = v => v * x; 474 | return multipliedBy; 475 | } 476 | 477 | var tripled = v => v * 3; 478 | var fns = { 479 | doubled: multiplier(2), 480 | tripled, 481 | quadrupled: v => v * 4 482 | }; 483 | 484 | [1,2,3].map(fns.doubled); // [2,4,6] 485 | [1,2,3].map(fns.tripled); // [3,6,9] 486 | [1,2,3].map(fns.quadrupled); // [4,8,12] 487 | 488 | fns.doubled.name; // "multipledBy" 489 | fns.tripled.name; // "tripled" 490 | fns.quadrupled.name; // "quadrupled" 491 | ``` 492 | 493 | In this snippet, each `=>` arrow function is first assigned to a variable or property, giving it an inferred name (`"multipledBy"`, `"tripled"`, or `"quadrupled"`). As such, they would all pass this rule. 494 | 495 | By contrast, this rule *would* report errors for each of the `=>` arrow functions here: 496 | 497 | ```js 498 | function getName(fn) { 499 | return fn.name; 500 | } 501 | 502 | function multiplier(x) { 503 | return v => v * x; 504 | } 505 | 506 | fns = [ multiplier(2), v => v * 3 ]; 507 | getName( fns[0] ); // "" 508 | getName( fns[1] ); // "" 509 | getName( v => v * 4 ); // "" 510 | ``` 511 | 512 | In this snippet, all three `=>` arrow functions remain anonymous because no name inferences are possible. 513 | 514 | ## Rule: `"where"` 515 | 516 | The **proper-arrows**/*where* rule restricts where in program structure `=>` arrow functions can be used. 517 | 518 | This rule can be configured to forbid `=>` arrow functions in the global/top-level-module scope (`"global"`) (or `"global-declaration"` just for global arrow declarations), forbid `=>` arrow functions as object properties (`"property"`), and forbid `=>` arrow functions in `export` statements (`"export"`). 519 | 520 | To turn this rule on: 521 | 522 | ```json 523 | "@getify/proper-arrows/where": "error" 524 | ``` 525 | 526 | ```json 527 | "@getify/proper-arrows/where": [ "error", { "global": true, "property": true, "export": true, "trivial": false } ] 528 | ``` 529 | 530 | The main purpose of this rule is to avoid readability harm when using `=>` arrow functions in certain program structure locations. 531 | 532 | Placing `=>` arrow functions in the global/top-level-module scope has no benefit other than preferred style; they're more proper as regular function declarations. Placing `=>` arrow functions on object properties has no benefit other than preferred style; they're more proper as concise object methods. Placing arrow functions in `export` statements offers no benefit other than being more concise; they're more proper as exported named function declarations. 533 | 534 | For example: 535 | 536 | ```js 537 | var PEOPLE_URL = "http://some.tld/api"; 538 | 539 | var People = { 540 | getData(id,cb) { 541 | ajax(PEOPLE_URL,{ id },cb); 542 | } 543 | }; 544 | 545 | function onData(data) { 546 | console.log(data); 547 | } 548 | 549 | export default function lookup(id) { 550 | return People.getData(id,onData); 551 | } 552 | 553 | export var lookup = function(id) { 554 | return People.getData(id,onData); 555 | }; 556 | ``` 557 | 558 | In this snippet, the `getData(..)` function is a clear and proper concise method on `People` object, `onData(..)` is a regular function declaration, and `lookup(..)` is either a default or named-declaration export declaration. Therefore, the **proper-arrows**/*where* rule would not report any errors. 559 | 560 | By contrast, this rule *would* default to reporting errors for each of these `=>` arrow functions: 561 | 562 | ```js 563 | var PEOPLE_URL = "http://some.tld/api"; 564 | 565 | var People = { 566 | getData: (id,cb) => ajax(PEOPLE_URL,{ id },cb) 567 | }; 568 | 569 | const onData = data => { 570 | console.log(data); 571 | }; 572 | 573 | export default id => People.getData(id,onData) 574 | 575 | export var lookup = id => People.getData(id,onData) 576 | ``` 577 | 578 | These usages of `=>` arrow functions are not helping the readability or behavior of this snippet. 579 | 580 | ### Rule Configuration 581 | 582 | The **proper-arrows**/*where* rule can be configured with four (non-exclusive) modes: `"global"`, `"global-declaration"`, `"property"`, and `"export"`. 583 | 584 | **Note:** The default behavior is that all three modes are turned on for this rule. You must specifically configure each mode to disable it. 585 | 586 | * [`"global"`](#rule-where-configuration-global) (default: `true`) forbids `=>` arrow functions in general expressions located in the global/top-level-module scope. 587 | 588 | * [`"global-declaration"`](#rule-where-configuration-global-declaration) (default: same as `global`) forbids `=>` "arrow function declarations" (e.g. `const foo = x => x * 2`) in the global/top-level-module scope. 589 | 590 | * [`"property"`](#rule-where-configuration-property) (default: `true`) forbids assigning `=>` arrow functions to object properties. 591 | 592 | * [`"export"`](#rule-where-configuration-export) (default: `true`) forbids `=>` arrow functions in `export` statements. 593 | 594 | #### Rule `"where"` Configuration: `"global"` 595 | 596 | To configure this rule mode (on by default, set as `false` to turn off): 597 | 598 | ```json 599 | "@getify/proper-arrows/where": [ "error", { "global": true, "trivial": false } ] 600 | ``` 601 | 602 | For inline function expressions in the global/top-level-module scope, use the regular `function` declaration form to avoid errors: 603 | 604 | ```js 605 | getResults({ query: "foo" },function onResults(results){ 606 | console.log(results); 607 | }); 608 | ``` 609 | 610 | By contrast, this rule mode *would* report errors for: 611 | 612 | ```js 613 | getResults({ query: "foo" },results => { 614 | console.log(results); 615 | }); 616 | ``` 617 | 618 | In this snippet, the `=>` arrow function is less obviously a function than the function expression form. 619 | 620 | ##### Global Arrow Declarations 621 | 622 | A ["global arrow declaration"](#global-arrow-declaration) is specifically and only an arrow function expression initially assigned to a variable at the moment the variable is declared (in the global/top-level-module scope): 623 | 624 | ```js 625 | const onData = data => { 626 | console.log(data); 627 | }; 628 | ``` 629 | 630 | The `global` rule mode includes forbidding this form of "declaration". Instead, the standard function declaration form should be used to avoid such errors: 631 | 632 | ```js 633 | function onData(data) { 634 | console.log(data); 635 | } 636 | ``` 637 | 638 | However, the [`"global-declaration"`](#rule-where-configuration-global-declaration) mode **only** applies to these types of "declarations", not all arrow expressions, and it overrides this rule mode's treatment of such declarations. 639 | 640 | #### Rule `"where"` Configuration: `"global-declaration"` 641 | 642 | This rule mode by default mirrors the [`"global"`](#rule-where-configuration-global) rule mode, whatever value it's set or defaulted to. However, you can explicitly configure this rule mode to a different value as such: 643 | 644 | ```json 645 | "@getify/proper-arrows/where": [ "error", { "global-declaration": true, "trivial": false } ] 646 | ``` 647 | 648 | This rule mode only controls reporting an error for a ["global arrow declaration"](#global-arrow-declaration), such as: 649 | 650 | ```js 651 | const onData = data => { 652 | console.log(data); 653 | }; 654 | ``` 655 | 656 | To avoid such errors, use the regular `function` declaration form: 657 | 658 | ```js 659 | function onData(data) { 660 | console.log(data); 661 | } 662 | ``` 663 | 664 | Other global arrow expressions are unaffected by this particular rule mode; however, the separate [`"global"`](#rule-where-configuration-global) rule mode, which is on by default, controls *all* arrow function forms in the global/top-level-module scope. 665 | 666 | If `"global"` mode is explicitly turned off (via `false`), but this `"global-declaration"` is explicitly turned on (via `true`), ***ONLY*** ["global arrow declarations"](#global-arrow-declaration) will report errors. 667 | 668 | If `"global"` mode is on (by default, or set to `true` explicitly), but this `"global-declaration"` mode is explicitly turned off (via `false`), all other global/top-level-module scope arrow function expressions ***EXCEPT*** ["global arrow declarations"](#global-arrow-declaration) will report errors. 669 | 670 | However, if this `"global-declaration"` mode is not set explicitly, it will always mirror the value of the [`"global"`](#rule-where-configuration-global) rule mode, whether it is set explicitly or left to default. 671 | 672 | #### Rule `"where"` Configuration: `"property"` 673 | 674 | To configure this rule mode (on by default, set as `false` to turn off): 675 | 676 | ```json 677 | "@getify/proper-arrows/where": [ "error", { "property": true, "trivial": false } ] 678 | ``` 679 | 680 | When defining a function in an object literal definition, use the concise method form: 681 | 682 | ```js 683 | var People = { 684 | getData(id,cb) { 685 | ajax(PEOPLE_URL,{ id },cb); 686 | } 687 | }; 688 | ``` 689 | 690 | By contrast, this rule mode *would* report errors for: 691 | 692 | ```js 693 | var People = { 694 | getData: (id,cb) => ajax(PEOPLE_URL,{ id },cb) 695 | }; 696 | ``` 697 | 698 | In this snippet, the `=>` arrow function is less obviously a method than the concise method form. 699 | 700 | #### Rule `"where"` Configuration: `"export"` 701 | 702 | To configure this rule mode (on by default, set as `false` to turn off): 703 | 704 | ```json 705 | "@getify/proper-arrows/where": [ "error", { "export": true, "trivial": false } ] 706 | ``` 707 | 708 | When exporting function declarations/expressions, use non-arrow functions: 709 | 710 | ```js 711 | export default function lookup(id) { 712 | return People.getData(id,onData); 713 | } 714 | 715 | export var lookup = function(id) { 716 | return People.getData(id,onData); 717 | } 718 | ``` 719 | 720 | By contrast, this rule mode *would* report errors for: 721 | 722 | ```js 723 | export default id => People.getData(id,onData) 724 | 725 | export var lookup = id => People.getData(id,onData) 726 | ``` 727 | 728 | In this snippet, the `=>` arrow functions are less obviously a function than named function form. 729 | 730 | ## Rule: `"return"` 731 | 732 | The **proper-arrows**/*return* rule restricts the concise return values for `=>` arrow functions. 733 | 734 | This rule can be configured to forbid concise return of object literals (`"object"`), forbid concise return of `=>` arrow functions (aka, "chained arrow returns") without visual delimiters like `( .. )` (`"chained"`), forbid concise return of ternary/conditional expressions (`"ternary"`), and forbid concise returns of comma sequences (`"sequence"`). 735 | 736 | To turn this rule on: 737 | 738 | ```json 739 | "@getify/proper-arrows/return": "error" 740 | ``` 741 | 742 | ```json 743 | "@getify/proper-arrows/return": [ "error", { "object": true, "chained": true, "sequence": true, "trivial": false } ] 744 | ``` 745 | 746 | The main purpose of this rule is to avoid readability harm for `=>` arrow functions by ensuring concise return values are clean and "proper". 747 | 748 | By forbidding concise return of object literals, the reader is not confused at first glance by the `{ .. }` looking like a non-concise function body. By forbidding chained `=>` arrow function concise returns without enclosing visual delimiters (like `( .. )`), the reader doesn't have to visually parse functions in a reverse/right-to-left fashion to determine the function boundaries. By forbidding concise return of comma sequences (ie, `(x = 3,y = foo(x + 1),[x,y])`), the reader doesn't have as much trouble figuring out which value will be returned from the function. By forbidding concise return of ternary/conditional expressions, especially nested ternaries, the function's boundary is not as visually ambiguous. 749 | 750 | For example: 751 | 752 | ```js 753 | var fn1 = prop => ( val => { return { [prop]: val }; } ); 754 | var fn2 = (x,y) => { x = 3; y = foo(x + 1); return [x,y]; }; 755 | var fn3 = (x,y) => { 756 | return ( 757 | x > 3 ? x : 758 | y > 3 ? y : 759 | 3; 760 | ); 761 | }; 762 | ``` 763 | 764 | In this snippet, the chained `=>` arrow function `fn1(..)` is surrounded by `( .. )` to visually delimit it, and the object literal being returned is done with a full function body and `return` keyword. For `fn2(..)`, the function body's return value (`[x,y]`) is clear. For `fn3(..)`, the presence of a `return` keyword inside the `{ }` function body more clearly delimits the nested ternary/conditional expression as determining the return value. Therefore, the **proper-arrows**/*return* rule would not report any errors. 765 | 766 | By contrast, this rule *would* report errors for each of these statements: 767 | 768 | ```js 769 | var fn1 = prop => val => ({ [prop]: val }); 770 | var fn2 = (x,y) => (x = 3, y = foo(x + 1), [x,y]); 771 | var fn3 = (x,y) => x > 3 ? x : y > 3 ? y : 3; 772 | ``` 773 | 774 | In this snippet, the chained `=>` arrow function return is not as clear, the concise return of the object literal can be confused as a function block at first glance, the concise return of the comma sequence makes it hard to determine which value will actually be returned, and the nested ternary/conditional expression obscures the determination of the return value. 775 | 776 | Some claim that extra whitespace solves these readability issues. However, if you go to the trouble to space/indent to this extent, skipping the `return` keyword doesn't really contribute to the readability, and in fact still makes the return values a little less visually distinct than the above forms. 777 | 778 | ```js 779 | var fn1 = prop 780 | => val 781 | => ( 782 | { [prop]: val } 783 | ); 784 | var fn2 = (x,y) => ( 785 | x = 3, 786 | y = foo(x + 1), 787 | [x,y] 788 | ); 789 | var fn3 = (x,y) => 790 | x > 3 ? x : 791 | y > 3 ? y : 792 | 3; 793 | ``` 794 | 795 | Also, many proponents of this whitespace "solution" advocate it in theory only; in practice, these sorts of functions are often just found in their less readable single-line form. 796 | 797 | ### Rule Configuration 798 | 799 | The **proper-arrows**/*return* rule can be configured with four (non-exclusive) modes: `"object"`, `"ternary"`, `"chained"`, and `"sequence"`. 800 | 801 | **Note:** The default behavior is that all four modes are turned on for this rule. You must specifically configure each mode to disable it. 802 | 803 | * [`"object"`](#rule-return-configuration-object) (default: `true`) forbids returning object literals as concise return expressions. 804 | 805 | * [`"ternary"`](#rule-return-configuration-ternary) (default: `0`) controls whether (and to what extent of nesting) conditional/ternary expressions (`x ? y : z`) are allowed as the concise return expression of an `=>` arrow function. 806 | 807 | * [`"chained"`](#rule-return-configuration-chained) (default: `true`) forbids returning `=>` arrow functions (aka, "chained arrow returns") as concise return expressions, without visual delimiters `( .. )`. 808 | 809 | * [`"sequence"`](#rule-return-configuration-sequence) (default: `true`) forbids returning comma sequences (ie, `(x = 3, y + x)`) as concise return expressions. 810 | 811 | #### Rule `"return"` Configuration: `"object"` 812 | 813 | **Note:** This rule is similar to the [built-in ESLint "arrow-body-style" rule](https://eslint.org/docs/rules/arrow-body-style), specifically the `requireReturnForObjectLiteral: true` mode. However, that built-in rule mode is only defined for `as-needed`, which requires always using the concise return expression form for all other `=>` arrow functions where it's possible to do so. 814 | 815 | The **proper-arrows**/*return* rule's `"object"` mode is different (more narrowly focused): it only disallows the concise return of an object literal; otherwise, it doesn't place any requirements or restrictions on usage of `=>` arrow functions. 816 | 817 | To configure this rule mode (on by default, set as `false` to turn off): 818 | 819 | ```json 820 | "@getify/proper-arrows/return": [ "error", { "object": true, "trivial": false } ] 821 | ``` 822 | 823 | If returning an object literal from an `=>` arrow function, use the full-body return form: 824 | 825 | ```js 826 | var fn = prop => val => { return { [prop]: val }; }; 827 | ``` 828 | 829 | In this snippet, the `=>` arrow function uses the full-body return form for the object literal. As such, it would pass this rule. 830 | 831 | By contrast, this rule mode *would* report errors for: 832 | 833 | ```js 834 | var fn = prop => val => ({ [prop]: val }); 835 | ``` 836 | 837 | In this snippet, the `=>` arrow function has an object literal as the concise return expression. 838 | 839 | #### Rule `"return"` Configuration: `"ternary"` 840 | 841 | **Note:** This rule is similar to the [built-in ESLint "no-ternary" rule](https://eslint.org/docs/rules/no-ternary) and ["no-nested-ternary" rule](https://eslint.org/docs/rules/no-nested-ternary). However, those built-in rules focus on all conditional/ternary (`? :`) expressions, and aren't configurable to an allowed level of nesting. 842 | 843 | The **proper-arrows**/*return* rule's `"ternary"` mode is different (more narrowly focused): it only controls the ternary/conditional expressions as the concise return of an `=>` arrow function; otherwise, it doesn't place any requirements or restrictions on ternary/conditional expressions. 844 | 845 | To configure this rule mode (defaults to `0`, set to a higher number to effectively disable): 846 | 847 | ```json 848 | "@getify/proper-arrows/return": [ "error", { "ternary": 1, "trivial": false } ] 849 | ``` 850 | 851 | The number specified for the `"ternary"` option controls what level of (nested) ternary/conditional expressions are allowed as the concise return of an `=>` arrow function. `0` means none allowed, `1` means a single ternary/conditional allowed, `2` means one level nested (`x ? y ? z : w : u`) allowed, etc. 852 | 853 | If this rule mode is set to `1`, it would pass this snippet: 854 | 855 | ```js 856 | var fn = data => data.id ? lookup(data.id) : lookup(-1); 857 | ``` 858 | 859 | But this rule mode set to `1` *would* report errors for: 860 | 861 | ```js 862 | var fn = data => data.id ? data.extra ? lookup(data.id,data.extra) : lookup(data.id) : lookup(-1); 863 | ``` 864 | 865 | #### Rule `"return"` Configuration: `"chained"` 866 | 867 | To configure this rule mode (on by default, set as `false` to turn off): 868 | 869 | ```json 870 | "@getify/proper-arrows/return": [ "error", { "chained": true, "trivial": false } ] 871 | ``` 872 | 873 | This rule mode requires `( .. )` surrounding any concise return that is itself an `=>` arrow function, because the parentheses help visually disambiguate where the function boundaries are, especially when there are several `=>` arrow functions chained together. 874 | 875 | If concise returning a chained `=>` arrow function, wrap `( .. )` around it: 876 | 877 | ```js 878 | var fn = prop => ( val => { return { [prop]: val }; } ); 879 | ``` 880 | 881 | In this snippet, the chained `=>` arrow function return is visually disambiguated with `( .. )`. As such, it would pass this rule. 882 | 883 | **Though this rule does require it**, visual ambiguity can even further be reduced by using whitespace to position the delimiters similarly to regular function bodies delimited by `{ .. }`: 884 | 885 | ```js 886 | var fn = prop => ( 887 | val => { return { [prop]: val }; } 888 | ); 889 | ``` 890 | 891 | **Note:** The extra whitespace is merely a readability suggestion if you plan to use this rule, not a requirement of it. 892 | 893 | By contrast, this rule mode *would* report errors for: 894 | 895 | ```js 896 | var fn = prop => val => { return { [prop]: val }; }; 897 | ``` 898 | 899 | In this snippet, the boundary of the chained `=>` arrow function return is less clear. 900 | 901 | #### Rule `"return"` Configuration: `"sequence"` 902 | 903 | **Note:** This rule is similar to the [built-in ESLint "no-sequences" rule](https://eslint.org/docs/rules/no-sequences). 904 | 905 | The **proper-arrows**/*return* rule's `"sequence"` mode is different (more narrowly focused): it only disallows comma sequences as `=>` arrow function concise return expressions. 906 | 907 | To configure this rule mode (on by default, set as `false` to turn off): 908 | 909 | ```json 910 | "@getify/proper-arrows/return": [ "error", { "sequence": true, "trivial": false } ] 911 | ``` 912 | 913 | Comma sequences are not very common, but one place they're a bit more common is as the concise return expression from an `=>` arrow function so that multiple expression-statements can be strung together without needing a full function body. This may be clever or concise, but it's almost always less readable than just using separate statements. 914 | 915 | For example: 916 | 917 | ```js 918 | var fn2 = (x,y) => { x = 3; y = foo(x + 1); return [x,y]; }; 919 | ``` 920 | 921 | In this snippet, the `=>` arrow function uses the full-body return form with separate statements and an explicit `return` statement. As such, it would pass this rule. 922 | 923 | By contrast, this rule mode *would* report errors for: 924 | 925 | ```js 926 | var fn2 = (x,y) => (x = 3, y = foo(x + 1), [x,y] ); 927 | ``` 928 | 929 | In this snippet, the `=>` arrow function has a comma sequence as the concise return expression. 930 | 931 | ## Rule: `"this"` 932 | 933 | The **proper-arrows**/*this* rule requires `=>` arrow functions to reference the `this` keyword. It also supports a `"never"` configuration mode, which reverses the rule and means that `=>` arrow functions must never use `this`, as well as a `"never-global"` configuration mode which only disallows `this` usage in arrow functions that are in the global scope. 934 | 935 | To turn this rule on: 936 | 937 | ```json 938 | "@getify/proper-arrows/this": "error" 939 | ``` 940 | 941 | ```json 942 | "@getify/proper-arrows/this": [ "error", "always", { "no-global": true, "trivial": false } ] 943 | ``` 944 | 945 | The main purpose of this rule is to avoid usage of `=>` arrow functions as just function shorthand (i.e., `arr.map(x => x * 2)`), which can be argued as a misusage. Concise `=>` arrow function syntax (with all its myriad variations) can harm readability, so instead `=>` arrow functions should only be used when the "lexical this" behavior is needed. 946 | 947 | Since `=>` arrow functions don't have their own `this`, they treat any `this` reference as a normal variable (not a special keyword). That means `this` is lexically looked up through any parent scopes until a valid definition of `this` is found -- from a normal non-arrow function, or finally in the global scope itself. 948 | 949 | Such `this` behavior is referred to as "lexical this", and it is one of the main design characteristics for `=>` arrow functions. 950 | 951 | For example: 952 | 953 | ```js 954 | var o = { 955 | id: 42, 956 | getData() { 957 | ajax("https://some.tld/api",() => console.log(this.id)); 958 | } 959 | }; 960 | 961 | o.getData(); // 42 962 | ``` 963 | 964 | In this snippet, the `=>` arrow function is inside a normal function and therefore looks up and adopts its `this` (aka, "lexical this") -- which is set to the `o` object by the `o.getData()` call. Therefore, the **proper-arrows**/*this* rule would not report an error. 965 | 966 | By contrast, this rule *would* report an error for: 967 | 968 | ```js 969 | var o = { 970 | id: 42, 971 | getData() { 972 | ajax("https://some.tld/api",() => console.log(o.id)); 973 | } 974 | }; 975 | 976 | o.getData(); // 42 977 | ``` 978 | 979 | Here, the `=>` arrow function is lexically closed over the `o` rather than using "lexical this", so it can be considered a misusage of the `=>` arrow function merely for shorthand; rather, the callback should have used `this.id` instead of `o.id`, or just been a regular named function expression (ie, `function onResp(){ console.log(o.id); }`). 980 | 981 | To pass the **proper-arrows**/*this* rule without a reported error, all `=>` arrow functions must reference a `this` somewhere in their arguments or body -- or, when using the `"nested"` configuration, any nested `=>` arrow functions. 982 | 983 | ### Rule Configuration 984 | 985 | The **proper-arrows**/*this* rule can be configured in one of four modes: `"nested"` (default), `"always"`, `"never"` and `"never-global"`. 986 | 987 | * [`"nested"`](#rule-this-configuration-nested) (default) permits a `this` to appear lower in a nested `=>` arrow function (i.e., `x = y => z => this.foo(z)`), as long as there is no non-arrow function boundary crossed. 988 | 989 | Additionally, include the `"no-global"` option (default: `false`) to forbid `this`-containing `=>` arrow functions from the global scope (where they might inherit the global object as `this`) or from the top-level of a module (where `this` would be `undefined`). 990 | 991 | * [`"always"`](#rule-this-configuration-always) is more strict, requiring every single `=>` arrow function to have its own `this` reference. 992 | 993 | Additionally, include the `"no-global"` option (default: `false`) to forbid `this`-containing `=>` arrow functions from the global scope (where they might inherit the global object as `this`) or from the top-level of a module (where `this` would be `undefined`). 994 | 995 | * [`"never"`](#rule-this-configuration-never) is the reverse of the rule: all `=>` arrow functions are forbidden from using `this`. 996 | 997 | * [`"never-global"`](#rule-this-configuration-never-global) disallows `this` usage in arrow functions that are in the global scope. 998 | 999 | #### Rule `"this"` Configuration: `"nested"` 1000 | 1001 | To configure this rule mode (default): 1002 | 1003 | ```json 1004 | "@getify/proper-arrows/this": "error" 1005 | ``` 1006 | 1007 | ```json 1008 | "@getify/proper-arrows/this": [ "error", "nested" ] 1009 | ``` 1010 | 1011 | ```json 1012 | "@getify/proper-arrows/this": [ "error", "nested", { "no-global": true, "trivial": false } ] 1013 | ``` 1014 | 1015 | This rule mode allows a `this` to appear either in the `=>` arrow function, or nested deeper in a chain of `=>` arrow functions, as long as there is **no non-arrow function boundary crossed in the nesting**. 1016 | 1017 | If the `"no-global"` option **has not** been set for this mode, these statements will all pass the rule: 1018 | 1019 | ```js 1020 | var a = b => this.foo(b); 1021 | 1022 | var c = d => e => this.foo(e); 1023 | 1024 | var f = (g = h => this.foo(h)) => g; 1025 | ``` 1026 | 1027 | But these statements will each fail this rule: 1028 | 1029 | ```js 1030 | var a = b => foo(b); 1031 | 1032 | var c = d => this.foo(e => e); 1033 | 1034 | var f = (g = h => h) => this.foo(g); 1035 | 1036 | var h = i => function(j){ this.foo(j); }; 1037 | 1038 | var k = l => function(){ return m => this.foo(m); }; 1039 | ``` 1040 | 1041 | ##### `"no-global"` Option 1042 | 1043 | If the `"no-global"` option **has** been set for this mode, these statements will all pass the rule: 1044 | 1045 | ```js 1046 | function foo() { return a => this.bar(a); } 1047 | 1048 | function foo() { return a => b => this.bar(a,b); } 1049 | 1050 | function foo(cb = a => this.bar(a)) { .. } 1051 | 1052 | function foo(cb = a => b => this.bar(a,b)) { .. } 1053 | ``` 1054 | 1055 | And these statments will each fail this `"no-global"` option rule: 1056 | 1057 | ```js 1058 | var foo = a => this.bar(a); 1059 | 1060 | var foo = a => b => this.bar(a,b); 1061 | 1062 | var o = { 1063 | foo: a => this.bar(a) 1064 | }; 1065 | 1066 | var o = { 1067 | foo: a => b => this.bar(a,b) 1068 | }; 1069 | ``` 1070 | 1071 | #### Rule `"this"` Configuration: `"always"` 1072 | 1073 | To configure this rule mode: 1074 | 1075 | ```json 1076 | "@getify/proper-arrows/this": [ "error", "always" ] 1077 | ``` 1078 | 1079 | ```json 1080 | "@getify/proper-arrows/this": [ "error", "always", { "no-global": true, "trivial": false } ] 1081 | ``` 1082 | 1083 | This rule mode requires a `this` reference to appear in every single `=>` arrow function (e.g., nested `this` is not sufficient). 1084 | 1085 | If the `"no-global"` option **has not** been set for this mode, these statements will all pass the rule: 1086 | 1087 | ```js 1088 | var a = b => this.foo(b); 1089 | 1090 | var c = d => this.foo(e => this.bar(e)); 1091 | 1092 | var f = (g = h => this.foo(h)) => this.bar(g); 1093 | ``` 1094 | 1095 | But these statements will each fail this rule: 1096 | 1097 | ```js 1098 | var a = b => foo(b); 1099 | 1100 | var c = d => e => this.foo(e); 1101 | 1102 | var f = g => this.foo(h => h); 1103 | 1104 | var i = (j = k => k) => this.foo(j); 1105 | ``` 1106 | 1107 | **Note:** In each of the above examples, at least one of the `=>` arrow functions does not have its own `this`, hence the mode's rule failure (doesn't consider nested `this`). 1108 | 1109 | ##### `"no-global"` Option 1110 | 1111 | If the `"no-global"` option **has** been set for this mode, these statements will all pass the rule: 1112 | 1113 | ```js 1114 | function foo() { return a => this.bar(a); } 1115 | 1116 | function foo(cb = a => this.bar(a)) { .. } 1117 | ``` 1118 | 1119 | And these statements will each fail this `"no-global"` option rule: 1120 | 1121 | ```js 1122 | var foo = a => this.bar(a); 1123 | 1124 | var o = { 1125 | foo: a => this.bar(a) 1126 | }; 1127 | ``` 1128 | 1129 | ### Rule `"this"` Configuration: `"never"` 1130 | 1131 | To configure this rule mode: 1132 | 1133 | ```json 1134 | "@getify/proper-arrows/this": [ "error", "never" ] 1135 | ``` 1136 | 1137 | This rule mode **forbids** a `this` reference from appearing in any `=>` arrow function. 1138 | 1139 | These statements will all pass the rule in this mode: 1140 | 1141 | ```js 1142 | var a = b => foo(b); 1143 | 1144 | var c = d => foo(e => bar(e)); 1145 | 1146 | var f = (g = h => foo(h)) => bar(g); 1147 | ``` 1148 | 1149 | But these statements will each fail the rule in this mode: 1150 | 1151 | ```js 1152 | var a = b => this.foo(b); 1153 | 1154 | var c = d => e => this.foo(e); 1155 | 1156 | var f = g => foo(h => this.bar(h)); 1157 | ``` 1158 | 1159 | **Note:** The `"no-global"` option and [`"trivial"` option](#trivial--arrow-functions) are not applicable and have no effect for this rule mode. 1160 | 1161 | 1162 | ### Rule `"this"` Configuration: `"never-global"` 1163 | 1164 | To configure this rule mode: 1165 | 1166 | ```json 1167 | "@getify/proper-arrows/this": [ "error", "never-global" ] 1168 | ``` 1169 | 1170 | This rule mode **forbids** `this` usage in arrow functions that are in the global scope (including nested arrow functions). 1171 | 1172 | These statements will all pass the rule in this mode: 1173 | 1174 | ```js 1175 | var a = b => foo(b); 1176 | 1177 | var c = d => foo({ set baz(v){ return z => this.bar(z); } }) 1178 | 1179 | var e = f => foo({ method(){ return z => this.bar(z); } }) 1180 | ``` 1181 | 1182 | But these statements will each fail the rule in this mode: 1183 | 1184 | ```js 1185 | var a = b => this.foo(b); 1186 | 1187 | var c = d => e => this.foo(e); 1188 | 1189 | var f = g => foo(h => this.bar(h)); 1190 | ``` 1191 | 1192 | ## npm Package 1193 | 1194 | To use this plugin with a global install of ESLint (recommended): 1195 | 1196 | ```cmd 1197 | npm install -g @getify/eslint-plugin-proper-arrows 1198 | ``` 1199 | 1200 | To use this plugin with a local install of ESLint: 1201 | 1202 | ```cmd 1203 | npm install @getify/eslint-plugin-proper-arrows 1204 | ``` 1205 | 1206 | ## Builds 1207 | 1208 | [![Build Status](https://travis-ci.org/getify/eslint-plugin-proper-arrows.svg?branch=master)](https://travis-ci.org/getify/eslint-plugin-proper-arrows) 1209 | [![npm Module](https://badge.fury.io/js/%40getify%2Feslint-plugin-proper-arrows.svg)](https://www.npmjs.org/package/@getify/eslint-plugin-proper-arrows) 1210 | 1211 | If you need to bundle/distribute this eslint plugin, use `dist/eslint-plugin-proper-arrows.js`, which comes pre-built with the npm package distribution; you shouldn't need to rebuild it under normal circumstances. 1212 | 1213 | However, if you download this repository via Git: 1214 | 1215 | 1. The included build utility (`scripts/build-core.js`) builds (and minifies) `dist/eslint-plugin-proper-arrows.js` from source. 1216 | 1217 | 2. To install the build and test dependencies, run `npm install` from the project root directory. 1218 | 1219 | 3. To manually run the build utility with npm: 1220 | 1221 | ```cmd 1222 | npm run build 1223 | ``` 1224 | 1225 | 4. To run the build utility directly without npm: 1226 | 1227 | ```cmd 1228 | node scripts/build-core.js 1229 | ``` 1230 | 1231 | ## Tests 1232 | 1233 | A comprehensive test suite is included in this repository, as well as the npm package distribution. The default test behavior runs the test suite against `lib/index.js`. 1234 | 1235 | 1. The included Node.js test utility (`scripts/node-tests.js`) runs the test suite. 1236 | 1237 | 2. Ensure the test dependencies are installed by running `npm install` from the project root directory. 1238 | 1239 | - **Note:** Starting with npm v5, the test utility is **not** run automatically during this `npm install`. With npm v4 and before, the test utility automatically runs at this point. 1240 | 1241 | 3. To run the test utility with npm: 1242 | 1243 | ```cmd 1244 | npm test 1245 | ``` 1246 | 1247 | Other npm test scripts: 1248 | 1249 | * `npm run test:dist` will run the test suite against `dist/eslint-plugins-proper-arrows.js` instead of the default of `lib/index.js`. 1250 | 1251 | * `npm run test:package` will run the test suite as if the package had just been installed via npm. This ensures `package.json`:`main` properly references `dist/eslint-plugins-proper-arrows.js` for inclusion. 1252 | 1253 | * `npm run test:all` will run all three modes of the test suite. 1254 | 1255 | 4. To run the test utility directly without npm: 1256 | 1257 | ```cmd 1258 | node scripts/node-tests.js 1259 | ``` 1260 | 1261 | ### Test Coverage 1262 | 1263 | [![Coverage Status](https://coveralls.io/repos/github/getify/eslint-plugin-proper-arrows/badge.svg?branch=master)](https://coveralls.io/github/getify/eslint-plugin-proper-arrows?branch=master) 1264 | 1265 | If you have [Istanbul](https://github.com/gotwarlost/istanbul) already installed on your system (requires v1.0+), you can use it to check the test coverage: 1266 | 1267 | ```cmd 1268 | npm run coverage 1269 | ``` 1270 | 1271 | Then open up `coverage/lcov-report/index.html` in a browser to view the report. 1272 | 1273 | To run Istanbul directly without npm: 1274 | 1275 | ```cmd 1276 | istanbul cover scripts/node-tests.js 1277 | ``` 1278 | 1279 | **Note:** The npm script `coverage:report` is only intended for use by project maintainers; it sends coverage reports to [Coveralls](https://coveralls.io/). 1280 | 1281 | ## License 1282 | 1283 | All code and documentation are (c) 2019-2021 Kyle Simpson and released under the [MIT License](http://getify.mit-license.org/). A copy of the MIT License [is also included](LICENSE.txt). 1284 | -------------------------------------------------------------------------------- /lib/copyright-header.txt: -------------------------------------------------------------------------------- 1 | /*! @getify/eslint-plugin-proper-arrows 2 | v${version} (c) ${year} Kyle Simpson 3 | MIT License: http://getify.mit-license.org 4 | */ 5 | 6 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var presetRulesConfig = { 4 | "getify-says": { 5 | "@getify/proper-arrows/params": [ "error", { "unused": "trailing", "count": 2, "length": 3, "allowed": [ "e", "v", "cb", "fn", "pr", ], }, ], 6 | "@getify/proper-arrows/name": "error", 7 | "@getify/proper-arrows/return": [ "error", { "ternary": 1, }, ], 8 | "@getify/proper-arrows/where": "error", 9 | "@getify/proper-arrows/this": [ "error", "nested", { "no-global": true, }, ], 10 | } 11 | }; 12 | 13 | var plugin = { 14 | configs: { 15 | "getify-says": { 16 | plugins: [ "@getify/proper-arrows", ], 17 | rules: presetRulesConfig["getify-says"], 18 | } 19 | }, 20 | rules: { 21 | "params": { 22 | meta: { 23 | type: "problem", 24 | docs: { 25 | description: "Control various aspects of arrow function parameters to keep them readable", 26 | category: "Best Practices", 27 | url: "https://github.com/getify/eslint-plugin-proper-arrows/#rule-params", 28 | }, 29 | schema: [ 30 | { 31 | type: "object", 32 | properties: { 33 | unused: { 34 | enum: [ "all", "trailing", "none", ], 35 | }, 36 | count: { 37 | type: "integer", 38 | min: 0, 39 | }, 40 | length: { 41 | type: "integer", 42 | min: 1, 43 | }, 44 | allowed: { 45 | type: "array", 46 | uniqueItems: true, 47 | items: { 48 | type: "string", 49 | }, 50 | }, 51 | trivial: { 52 | type: "boolean", 53 | }, 54 | }, 55 | additionalProperties: false, 56 | }, 57 | ], 58 | messages: { 59 | unused: "Parameter `{{param}}` is unused", 60 | tooMany: "Parameter `{{param}}` is beyond the parameter limit allowed", 61 | tooShort: "Parameter `{{param}}` is too short", 62 | }, 63 | }, 64 | create(context) { 65 | var defaultsOnly = context.options.length == 0; 66 | var extraOptions = (!defaultsOnly) ? context.options[0] : null; 67 | var allUnusedMode = defaultsOnly || !("unused" in extraOptions) || extraOptions.unused === "all"; 68 | var trailingUnusedMode = extraOptions && extraOptions.unused === "trailing"; 69 | var noneUnusedMode = extraOptions && extraOptions.unused === "none"; 70 | var countLimit = (defaultsOnly || !("count" in extraOptions)) ? 3 : extraOptions.count; 71 | var minLength = (defaultsOnly || !("length" in extraOptions)) ? 2 : extraOptions.length; 72 | var allowedParams = (defaultsOnly || !("allowed" in extraOptions)) ? [] : extraOptions.allowed; 73 | var ignoreTrivial = !(extraOptions && extraOptions.trivial === true); 74 | 75 | return { 76 | "ArrowFunctionExpression:exit": function exit(node) { 77 | // ignore a trivial arrow function? 78 | if (ignoreTrivial && isTrivialArrow(node)) { 79 | return; 80 | } 81 | 82 | // handle "count" mode 83 | var allParamIds = getAllIdentifiers(node.params); 84 | if (allParamIds.length > countLimit) { 85 | for (let paramId of allParamIds.slice(countLimit)) { 86 | if (!allowedParams.includes(paramId.name)) { 87 | context.report({ 88 | node: paramId, 89 | messageId: "tooMany", 90 | data: { param: paramId.name, }, 91 | }); 92 | break; 93 | } 94 | } 95 | } 96 | 97 | // handle "length" mode 98 | var checkParamIds = allParamIds.filter(function skipAllowed(paramId){ 99 | return !allowedParams.includes(paramId.name); 100 | }); 101 | for (let paramId of checkParamIds) { 102 | let paramName = paramId.name; 103 | if ( 104 | !allowedParams.includes(paramName) && 105 | paramName.length < minLength 106 | ) { 107 | context.report({ 108 | node: paramId, 109 | messageId: "tooShort", 110 | data: { param: paramName, }, 111 | }); 112 | } 113 | } 114 | 115 | // handle "unused" mode 116 | if (!noneUnusedMode) { 117 | let sourceCode = getSourceCode(context); 118 | let scope = getScope(context,sourceCode,node); 119 | let checkParamNames = checkParamIds.map(function getName(paramId){ 120 | return paramId.name; 121 | }); 122 | let unusedParamIds = []; 123 | let lastUsedParamName = null; 124 | for (let variable of scope.variables) { 125 | let paramDef = variable.defs.find(function isParameter(def){ 126 | return def.type == "Parameter"; 127 | }); 128 | 129 | // is this a parameter-defined variable? 130 | if (paramDef) { 131 | // is there also a shadowed variable in the function body? 132 | let isShadowedDef = !!variable.defs.find(function isVariable(def){ 133 | return def.type == "Variable"; 134 | }); 135 | let varName = variable.name; 136 | let paramUsed = false; 137 | 138 | // any references to this parameter identifier? 139 | if (variable.references.length > 0) { 140 | for (let ref of variable.references) { 141 | let idInFuncParam = inArrowParams(ref.identifier,node); 142 | if ( 143 | // non-shadowed usage in function body? 144 | ( 145 | !isShadowedDef && 146 | !idInFuncParam 147 | ) || 148 | // usage in function parameters that's not 149 | // the original parameter definition? 150 | ( 151 | idInFuncParam && 152 | ref.identifier != paramDef.name 153 | ) 154 | ) { 155 | paramUsed = true; 156 | break; 157 | } 158 | } 159 | } 160 | 161 | if (paramUsed) { 162 | lastUsedParamName = varName; 163 | } 164 | else { 165 | unusedParamIds.push( 166 | allParamIds.find(function getParam(paramId){ 167 | return paramId.name == varName; 168 | }) 169 | ); 170 | } 171 | } 172 | } 173 | 174 | // check/report unused parameters 175 | let foundLastUsedParam = false; 176 | for (let paramId of allParamIds) { 177 | if (trailingUnusedMode && lastUsedParamName != null) { 178 | if (paramId.name == lastUsedParamName) { 179 | foundLastUsedParam = true; 180 | } 181 | 182 | // skip over any parameters until the last used one 183 | if (!foundLastUsedParam) { 184 | continue; 185 | } 186 | } 187 | 188 | // unused parameter that we need to report? 189 | if ( 190 | unusedParamIds.includes(paramId) && 191 | checkParamNames.includes(paramId.name) 192 | ) { 193 | context.report({ 194 | node: paramId, 195 | messageId: "unused", 196 | data: { param: paramId.name, }, 197 | }); 198 | } 199 | } 200 | } 201 | }, 202 | }; 203 | }, 204 | }, 205 | "name": { 206 | meta: { 207 | type: "problem", 208 | docs: { 209 | description: "Require arrow functions to receive inferenced names", 210 | category: "Best Practices", 211 | url: "https://github.com/getify/eslint-plugin-proper-arrows/#rule-name", 212 | }, 213 | schema: [ 214 | { 215 | type: "object", 216 | properties: { 217 | trivial: { 218 | type: "boolean", 219 | }, 220 | }, 221 | additionalProperties: false, 222 | }, 223 | ], 224 | messages: { 225 | noName: "Required name inference not possible for this arrow function", 226 | }, 227 | }, 228 | create(context) { 229 | var ignoreTrivial = !(context.options.length > 0 && context.options[0].trivial === true); 230 | 231 | return { 232 | "ArrowFunctionExpression": function exit(node) { 233 | // ignore a trivial arrow function? 234 | if (ignoreTrivial && isTrivialArrow(node)) { 235 | return; 236 | } 237 | 238 | if (!arrowHasInferredName(node)) { 239 | context.report({ 240 | node: node, 241 | messageId: "noName", 242 | }); 243 | } 244 | }, 245 | }; 246 | }, 247 | }, 248 | "where": { 249 | meta: { 250 | type: "problem", 251 | docs: { 252 | description: "Forbid arrow functions from various locations", 253 | category: "Best Practices", 254 | url: "https://github.com/getify/eslint-plugin-proper-arrows/#rule-where", 255 | }, 256 | schema: [ 257 | { 258 | type: "object", 259 | properties: { 260 | global: { 261 | type: "boolean", 262 | }, 263 | "global-declaration": { 264 | type: "boolean", 265 | }, 266 | property: { 267 | type: "boolean", 268 | }, 269 | export: { 270 | type: "boolean", 271 | }, 272 | trivial: { 273 | type: "boolean", 274 | }, 275 | }, 276 | additionalProperties: false, 277 | }, 278 | ], 279 | messages: { 280 | noGlobal: "Arrow function not allowed in global/top-level-module scope", 281 | noGlobalDeclaration: "Arrow function not allowed as declaration in global/top-level-module scope", 282 | noProperty: "Arrow function not allowed in object property", 283 | noExport: "Arrow function not allowed in 'export' statement", 284 | }, 285 | }, 286 | create(context) { 287 | var defaultsOnly = context.options.length == 0; 288 | var extraOptions = (!defaultsOnly) ? context.options[0] : null; 289 | var globalMode = defaultsOnly || !("global" in extraOptions) || extraOptions.global === true; 290 | var globalDeclarationMode = ( 291 | ( 292 | !defaultsOnly && 293 | "global-declaration" in extraOptions 294 | ) ? 295 | extraOptions["global-declaration"] : 296 | globalMode 297 | ); 298 | var propertyMode = defaultsOnly || !("property" in extraOptions) || extraOptions.property === true; 299 | var exportMode = defaultsOnly || !("export" in extraOptions) || extraOptions.export === true; 300 | var ignoreTrivial = !(extraOptions && extraOptions.trivial === true); 301 | 302 | return { 303 | "ArrowFunctionExpression": function exit(node) { 304 | // ignore a trivial arrow function? 305 | if (ignoreTrivial && isTrivialArrow(node)) { 306 | return; 307 | } 308 | 309 | var sourceCode = getSourceCode(context); 310 | var scope = getScope(context,sourceCode,node); 311 | var globalArrow = currentlyInGlobalScope(context.parserOptions,scope); 312 | var globalArrowDeclaration = ( 313 | globalArrow && 314 | node.parent.type == "VariableDeclarator" 315 | ); 316 | 317 | // handle "global" and "global-declaration" mode 318 | // permutations 319 | if ( 320 | globalDeclarationMode && 321 | globalArrowDeclaration 322 | ) { 323 | context.report({ 324 | node: node.parent, 325 | messageId: "noGlobalDeclaration", 326 | }); 327 | } 328 | else if ( 329 | globalMode && 330 | globalArrow 331 | ) { 332 | context.report({ 333 | node: node, 334 | messageId: "noGlobal", 335 | }); 336 | } 337 | 338 | // handle (object) "property" mode 339 | if ( 340 | propertyMode && 341 | node.parent.type == "Property" && 342 | node.parent.parent.type == "ObjectExpression" && 343 | node.parent.value == node 344 | ) { 345 | context.report({ 346 | node: node, 347 | messageId: "noProperty", 348 | }); 349 | } 350 | 351 | // handle "export" mode 352 | if ( 353 | exportMode && 354 | ( 355 | ( 356 | node.parent.type == "ExportDefaultDeclaration" && 357 | node.parent.declaration == node 358 | ) || 359 | ( 360 | node.parent.type == "VariableDeclarator" && 361 | node.parent.parent.type == "VariableDeclaration" && 362 | node.parent.parent.parent.type == "ExportNamedDeclaration" && 363 | node.parent.init == node 364 | ) 365 | ) 366 | ) { 367 | context.report({ 368 | node: node, 369 | messageId: "noExport", 370 | }); 371 | } 372 | }, 373 | }; 374 | }, 375 | }, 376 | "return": { 377 | meta: { 378 | type: "problem", 379 | docs: { 380 | description: "Control various aspects of arrow function returns to keep them readable", 381 | category: "Best Practices", 382 | url: "https://github.com/getify/eslint-plugin-proper-arrows/#rule-return", 383 | }, 384 | schema: [ 385 | { 386 | type: "object", 387 | properties: { 388 | object: { 389 | type: "boolean", 390 | }, 391 | ternary: { 392 | type: "integer", 393 | min: 0, 394 | }, 395 | chained: { 396 | type: "boolean", 397 | }, 398 | sequence: { 399 | type: "boolean", 400 | }, 401 | trivial: { 402 | type: "boolean", 403 | }, 404 | }, 405 | additionalProperties: false, 406 | }, 407 | ], 408 | messages: { 409 | noConciseObject: "Concise return of object literal not allowed here", 410 | noTernary: "Ternary/conditional ('? :') expression not allowed here", 411 | noChainedArrow: "Chained arrow function return needs visual delimiters '(' and ')'", 412 | noSequence: "Return of comma sequence expression not allowed here", 413 | }, 414 | }, 415 | create(context) { 416 | var defaultsOnly = context.options.length == 0; 417 | var extraOptions = (!defaultsOnly) ? context.options[0] : null; 418 | var conciseObjectMode = defaultsOnly || !("object" in extraOptions) || extraOptions.object === true; 419 | var ternaryLimit = (defaultsOnly || !("ternary" in extraOptions)) ? 0 : extraOptions.ternary; 420 | var chainedArrowMode = defaultsOnly || !("chained" in extraOptions) || extraOptions.chained === true; 421 | var sequenceMode = defaultsOnly || !("sequence" in extraOptions) || extraOptions.sequence === true; 422 | var ignoreTrivial = !(extraOptions && extraOptions.trivial === true); 423 | 424 | var sourceCode = getSourceCode(context); 425 | var ternaryBodyStack = new Map(); 426 | 427 | return { 428 | "ConditionalExpression:exit": function exit(node) { 429 | var ancestors = getAncestors(context,sourceCode,node); 430 | var parentArrow = getParentArrowFunction(ancestors,/*onlyFromBody=*/true); 431 | if (parentArrow) { 432 | if (!ternaryBodyStack.has(parentArrow)) { 433 | ternaryBodyStack.set(parentArrow,[]); 434 | } 435 | let stack = ternaryBodyStack.get(parentArrow); 436 | stack.unshift(node); 437 | } 438 | }, 439 | "ArrowFunctionExpression": function enter(node) { 440 | // ignore a trivial arrow function? 441 | if (ignoreTrivial && isTrivialArrow(node)) { 442 | return; 443 | } 444 | 445 | // handle "object" mode 446 | if ( 447 | conciseObjectMode && 448 | node.body.type == "ObjectExpression" 449 | ) { 450 | context.report({ 451 | node: node, 452 | loc: node.body.loc.start, 453 | messageId: "noConciseObject", 454 | }); 455 | } 456 | 457 | // handle "sequence" mode 458 | if ( 459 | sequenceMode && 460 | node.body.type == "SequenceExpression" 461 | ) { 462 | context.report({ 463 | node: node, 464 | loc: node.body.loc.start, 465 | messageId: "noSequence", 466 | }); 467 | } 468 | 469 | // handle "chained" mode 470 | if ( 471 | chainedArrowMode && 472 | node.body.type == "ArrowFunctionExpression" 473 | ) { 474 | // ignore a trivial arrow function? 475 | if (ignoreTrivial && isTrivialArrow(node.body)) { 476 | return; 477 | } 478 | 479 | let isAsync = node.body.async; 480 | let before = sourceCode.getTokenBefore(node.body); 481 | let after = sourceCode.getTokenAfter(node.body); 482 | 483 | if (!( 484 | before && 485 | before.type == "Punctuator" && 486 | before.value == "(" && 487 | after && 488 | after.type == "Punctuator" && 489 | after.value == ")" 490 | )) { 491 | context.report({ 492 | node: node, 493 | loc: node.body.loc.start, 494 | messageId: "noChainedArrow", 495 | }); 496 | } 497 | } 498 | }, 499 | "ArrowFunctionExpression:exit": function exit(node) { 500 | // ignore a trivial arrow function? 501 | if (ignoreTrivial && isTrivialArrow(node)) { 502 | return; 503 | } 504 | 505 | if (node.body.type != "BlockStatement") { 506 | // handle "ternary" limit mode 507 | let stack = ternaryBodyStack.get(node); 508 | if (stack) { 509 | if (stack.length > ternaryLimit) { 510 | context.report({ 511 | node: stack[ternaryLimit], 512 | messageId: "noTernary", 513 | }); 514 | } 515 | stack.length = 0; 516 | } 517 | } 518 | }, 519 | }; 520 | }, 521 | }, 522 | "this": { 523 | meta: { 524 | type: "problem", 525 | docs: { 526 | description: "Require arrow functions to reference the 'this' keyword", 527 | category: "Best Practices", 528 | url: "https://github.com/getify/eslint-plugin-proper-arrows/#rule-this", 529 | }, 530 | schema: [ 531 | { 532 | enum: [ "always", "nested", "never", "never-global", ], 533 | }, 534 | { 535 | type: "object", 536 | properties: { 537 | "no-global": { 538 | type: "boolean", 539 | }, 540 | trivial: { 541 | type: "boolean", 542 | }, 543 | }, 544 | additionalProperties: false, 545 | }, 546 | ], 547 | messages: { 548 | noThis: "Required 'this' not found in arrow function", 549 | noThisNested: "Required 'this' not found in arrow function (or nested arrow functions)", 550 | neverThis: "Forbidden 'this' found in arrow function", 551 | noGlobal: "Arrow function not allowed in global scope", 552 | neverGlobal: "Arrow function with 'this' not allowed in global scope", 553 | }, 554 | }, 555 | create(context) { 556 | var parserOptions = context.parserOptions; 557 | 558 | var nestedThis = (context.options[0] === "nested" || !("0" in context.options)); 559 | var neverGlobalThis = (context.options[0] === "never-global"); 560 | var alwaysThis = (context.options[0] === "always"); 561 | var neverThis = (context.options[0] === "never"); 562 | var noGlobal = ( 563 | ["always","nested",].includes(context.options[0]) && 564 | context.options[1] && 565 | context.options[1]["no-global"] === true 566 | ); 567 | var ignoreTrivial = !(context.options[1] && context.options[1].trivial === true); 568 | 569 | var thisFoundIn = new Set(); 570 | 571 | return { 572 | "ThisExpression": function enter(node) { 573 | var sourceCode = getSourceCode(context); 574 | var ancestors = getAncestors(context, sourceCode,node); 575 | var parentArrow = getParentArrowFunction(ancestors); 576 | thisFoundIn.add(parentArrow); 577 | }, 578 | "ArrowFunctionExpression:exit": function exit(node) { 579 | // ignore a trivial arrow function? 580 | if (ignoreTrivial && isTrivialArrow(node)) { 581 | return; 582 | } 583 | 584 | var sourceCode = getSourceCode(context); 585 | var scope = getScope(context, sourceCode, node); 586 | var globalArrow = currentlyInGlobalScope(context.parserOptions,scope); 587 | var foundThis = thisFoundIn.has(node); 588 | 589 | // `this` found in arrow function? 590 | if (foundThis) { 591 | // never mode? 592 | if (neverThis) { 593 | context.report({ 594 | node: node, 595 | messageId: "neverThis", 596 | }); 597 | } 598 | 599 | // arrow is in global scope? 600 | if (globalArrow) { 601 | // never-global mode? 602 | if (neverGlobalThis) { 603 | context.report({ 604 | node: node, 605 | messageId: "neverGlobal", 606 | }); 607 | } 608 | 609 | // no-global flag set? 610 | if (noGlobal) { 611 | context.report({ 612 | node: node, 613 | messageId: "noGlobal", 614 | }); 615 | } 616 | } 617 | 618 | // need to track nested `this`? 619 | if (nestedThis || neverGlobalThis) { 620 | let ancestors = getAncestors(context, sourceCode,node); 621 | let parentArrow = getParentArrowFunction(ancestors); 622 | if (parentArrow) { 623 | thisFoundIn.add(parentArrow); 624 | } 625 | } 626 | } 627 | // arrow without a `this` found, and not in one 628 | // of the two never modes? 629 | else if (!(neverThis || neverGlobalThis)) { 630 | let whichMsg = alwaysThis ? "noThis" : "noThisNested"; 631 | context.report({ 632 | node: node, 633 | messageId: whichMsg, 634 | }); 635 | } 636 | }, 637 | }; 638 | }, 639 | }, 640 | }, 641 | }; 642 | 643 | plugin.flatConfigs = { 644 | "getify-says": { 645 | name: "getify-says", 646 | plugins: { 647 | "@getify/proper-arrows": plugin 648 | }, 649 | rules: presetRulesConfig["getify-says"], 650 | } 651 | } 652 | 653 | 654 | // *************************** 655 | 656 | // adapted from: https://github.com/eslint/eslint/blob/7ad86dea02feceb7631943a7e1423cc8a113fcfe/lib/rules/func-names.js#L95-L105 657 | function isObjectOrClassMethod(node) { 658 | const parent = node.parent; 659 | 660 | return ( 661 | parent && 662 | ( 663 | parent.type === "MethodDefinition" || 664 | ( 665 | parent.type === "Property" && ( 666 | parent.method || 667 | parent.kind === "get" || 668 | parent.kind === "set" 669 | ) 670 | ) 671 | ) 672 | ); 673 | } 674 | 675 | // adapted from: https://github.com/eslint/eslint/blob/7ad86dea02feceb7631943a7e1423cc8a113fcfe/lib/rules/func-names.js#L113-L122 676 | function arrowHasInferredName(node) { 677 | var parent = node.parent; 678 | 679 | return ( 680 | ( 681 | parent.type === "VariableDeclarator" && 682 | parent.id.type === "Identifier" && 683 | parent.init === node 684 | ) || 685 | ( 686 | ["Property","ClassProperty",].includes(parent.type) && 687 | parent.value === node 688 | ) || 689 | ( 690 | ["AssignmentExpression","AssignmentPattern",].includes(parent.type) && 691 | parent.left.type === "Identifier" && 692 | parent.right === node 693 | ) || 694 | ( 695 | parent.type === "ExportDefaultDeclaration" && 696 | parent.declaration === node 697 | ) 698 | ); 699 | } 700 | 701 | function getParentArrowFunction(nodes,onlyFromBody = false) { 702 | var prevNode; 703 | for (let node of [...nodes,].reverse()) { 704 | // bail if we find a function boundary that's not an arrow 705 | if ( 706 | isObjectOrClassMethod(node) || 707 | node.type == "FunctionExpression" || 708 | node.type == "FunctionDeclaration" 709 | ) { 710 | return; 711 | } 712 | else if (node.type == "ArrowFunctionExpression") { 713 | if (!onlyFromBody || !prevNode || node.body == prevNode) { 714 | return node; 715 | } 716 | } 717 | prevNode = node; 718 | } 719 | } 720 | 721 | function getAllIdentifiers(nodes) { 722 | var ret = []; 723 | 724 | for (let node of nodes) { 725 | // skip elided elements 726 | if (!node) { 727 | continue; 728 | } 729 | 730 | if (node.type == "Identifier") { 731 | ret = [ ...ret, node, ]; 732 | } 733 | else if (node.type == "AssignmentPattern") { 734 | ret = [ ...ret, ...getAllIdentifiers([node.left,]), ]; 735 | } 736 | else if (node.type == "ArrayPattern") { 737 | ret = [ ...ret, ...getAllIdentifiers(node.elements), ]; 738 | } 739 | else if (node.type == "ObjectPattern") { 740 | ret = [ ...ret, ...getAllIdentifiers(node.properties), ]; 741 | } 742 | else { 743 | // NOTE: these contortions/comments here are because of an 744 | // annoying bug with Istanbul's code coverage: 745 | // https://github.com/gotwarlost/istanbul/issues/781 746 | // 747 | /* eslint-disable no-lonely-if */ 748 | /* istanbul ignore else */ 749 | if (node.type == "Property") { 750 | ret = [ ...ret, ...getAllIdentifiers([node.value,]), ]; 751 | } 752 | /* eslint-enable no-lonely-if */ 753 | } 754 | } 755 | 756 | return ret; 757 | } 758 | 759 | function inArrowParams(id,func) { 760 | var node = id; 761 | var prevNode; 762 | while (node && (node != func)) { 763 | prevNode = node; 764 | node = node.parent; 765 | } 766 | return ( 767 | node == func && 768 | prevNode && 769 | func.params.includes(prevNode) 770 | ); 771 | } 772 | 773 | function isTrivialArrow(node) { 774 | return ( 775 | node.type == "ArrowFunctionExpression" && 776 | node.params.length <= 1 && 777 | ( 778 | node.params.length == 0 || 779 | node.params[0].type == "Identifier" 780 | ) && 781 | ( 782 | // .. => {} 783 | ( 784 | node.body.type == "BlockStatement" && 785 | node.body.body.length == 0 786 | ) || 787 | // .. => --literal-- 788 | ( 789 | node.body.type == "Literal" && 790 | ["number","string","boolean",].includes(typeof node.body.value) 791 | ) || 792 | // .. => null 793 | ( 794 | node.body.type == "Literal" && 795 | node.body.value === null 796 | ) || 797 | // .. => undefined OR .. => x 798 | ( 799 | node.body.type == "Identifier" 800 | ) || 801 | // .. => void .. 802 | ( 803 | node.body.type == "UnaryExpression" && 804 | node.body.operator == "void" && 805 | node.body.argument.type == "Literal" 806 | ) 807 | ) 808 | ); 809 | } 810 | 811 | function currentlyInGlobalScope(parserOptions,scope) { 812 | var extraGlobalScope = parserOptions.ecmaFeatures && parserOptions.ecmaFeatures.globalReturn; 813 | return ( 814 | ( 815 | extraGlobalScope && 816 | scope.upper && 817 | scope.upper.upper && 818 | ["global","module"].includes(scope.upper.upper.type) 819 | ) || 820 | ( 821 | !extraGlobalScope && 822 | scope.upper && 823 | ["global","module"].includes(scope.upper.type) 824 | ) 825 | ); 826 | } 827 | 828 | var getSourceCode = context => { 829 | getSourceCode = ( 830 | context.sourceCode ? 831 | context => context.sourceCode : 832 | context => context.getSourceCode() 833 | ); 834 | return getSourceCode(context); 835 | }; 836 | 837 | var getScope = (context, sourceCode, node) => { 838 | getScope = ( 839 | sourceCode.getScope ? 840 | (context, sourceCode, node) => sourceCode.getScope(node) : 841 | (context, sourceCode, node) => context.getScope() 842 | ); 843 | return getScope(context, sourceCode, node); 844 | }; 845 | 846 | var getAncestors = (context, sourceCode, node) => { 847 | getAncestors = ( 848 | sourceCode.getAncestors ? 849 | (context, sourceCode, node) => sourceCode.getAncestors(node) : 850 | (context, sourceCode, node) => context.getAncestors() 851 | ); 852 | return getAncestors(context, sourceCode, node); 853 | }; 854 | 855 | module.exports = plugin; 856 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@getify/eslint-plugin-proper-arrows", 3 | "version": "12.0.0", 4 | "description": "ESLint rules to ensure proper arrow function definitions", 5 | "main": "./lib/index.js", 6 | "scripts": { 7 | "test": "node scripts/node-tests.js", 8 | "test:dist": "TEST_DIST=true npm test", 9 | "test:package": "TEST_PACKAGE=true npm test", 10 | "test:all": "npm test && npm run test:dist && npm run test:package", 11 | "coverage": "istanbul cover scripts/node-tests.js", 12 | "coverage:report": "npm run coverage && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", 13 | "build-core": "node scripts/build-core.js", 14 | "build": "npm run build-core", 15 | "prepare": "npm run build", 16 | "prepublish": "npm run build && npm run test:all", 17 | "publish": "npm run coverage:report" 18 | }, 19 | "devDependencies": { 20 | "coveralls": "~3.1.0", 21 | "eslint": "~9.17.0", 22 | "qunit": "~2.15.0", 23 | "terser": "~5.7.0" 24 | }, 25 | "peerDependencies": { 26 | "eslint": ">= 9.17.0" 27 | }, 28 | "repository": "getify/eslint-plugin-proper-arrows", 29 | "keywords": [ 30 | "eslint", 31 | "plugin", 32 | "eslintplugin", 33 | "rule", 34 | "arrow", 35 | "name inference", 36 | "concise object return", 37 | "this" 38 | ], 39 | "bugs": { 40 | "url": "https://github.com/getify/eslint-plugin-proper-arrows/issues", 41 | "email": "getify@gmail.com" 42 | }, 43 | "homepage": "https://github.com/getify/eslint-plugin-proper-arrows", 44 | "author": "Kyle Simpson ", 45 | "license": "MIT" 46 | } 47 | -------------------------------------------------------------------------------- /scripts/build-core.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | var fs = require("fs"), 6 | path = require("path"), 7 | terser = require("terser"), 8 | year = (new Date()).getFullYear(), 9 | 10 | ROOT_DIR = path.join(__dirname,".."), 11 | SRC_DIR = path.join(ROOT_DIR,"lib"), 12 | DIST_DIR = path.join(ROOT_DIR,"dist"), 13 | 14 | LIB_SRC = [ 15 | path.join(SRC_DIR,"index.js"), 16 | ], 17 | LIB_DIST = [ 18 | path.join(DIST_DIR,"eslint-plugin-proper-arrows.js"), 19 | ] 20 | ; 21 | 22 | 23 | // *************************** 24 | 25 | console.log("*** Building ESLint Plugin 'proper-arrows' ***"); 26 | 27 | (async function main(){ 28 | // try to make the dist directory, if needed 29 | try { 30 | fs.mkdirSync(DIST_DIR,0o755); 31 | } 32 | catch (err) { } 33 | 34 | // read version number from package.json 35 | var packageJSON = JSON.parse( 36 | fs.readFileSync( 37 | path.join(ROOT_DIR,"package.json"), 38 | { encoding: "utf8", } 39 | ) 40 | ); 41 | var version = packageJSON.version; 42 | 43 | // read copyright-header text, render with version and year 44 | var copyrightHeader = fs.readFileSync( 45 | path.join(SRC_DIR,"copyright-header.txt"), 46 | { encoding: "utf8", } 47 | ).replace(/`/g,""); 48 | copyrightHeader = Function("version","year",`return \`${copyrightHeader}\`;`)( version, year ); 49 | 50 | 51 | // *************************** 52 | 53 | for (let [idx,SRC,] of LIB_SRC.entries()) { 54 | let DIST = LIB_DIST[idx]; 55 | 56 | console.log(`Building: ${DIST}`); 57 | 58 | try { 59 | let result = ""; 60 | 61 | result += fs.readFileSync(SRC,{ encoding: "utf8", }); 62 | 63 | result = await terser.minify(result,{ 64 | mangle: { 65 | keep_fnames: true, 66 | }, 67 | compress: { 68 | keep_fnames: true, 69 | }, 70 | output: { 71 | comments: /^!/, 72 | }, 73 | }); 74 | 75 | // was compression successful? 76 | if (!(result && result.code)) { 77 | if (result.error) throw result.error; 78 | else throw result; 79 | } 80 | 81 | // append copyright-header text 82 | result = `${copyrightHeader}${result.code}`; 83 | 84 | // write dist 85 | fs.writeFileSync( DIST, result, { encoding: "utf8", } ); 86 | } 87 | catch (err) { 88 | console.error(err); 89 | process.exit(1); 90 | } 91 | } 92 | 93 | console.log("Complete."); 94 | })(); 95 | -------------------------------------------------------------------------------- /scripts/node-tests.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | var path = require("path"); 6 | 7 | var Linter = require("eslint").Linter; 8 | var eslinter = global.eslinter = new Linter({ configType: "eslintrc"}); 9 | var properArrows; 10 | 11 | /* istanbul ignore next */ 12 | if (process.env.TEST_DIST) { 13 | properArrows = require(path.join(__dirname,"..","dist","eslint-plugin-proper-arrows.js")); 14 | } 15 | /* istanbul ignore next */ 16 | else if (process.env.TEST_PACKAGE) { 17 | properArrows = require(path.join(__dirname,"..")); 18 | } 19 | else { 20 | properArrows = require(path.join(__dirname,"..","lib","index.js")); 21 | } 22 | 23 | eslinter.defineRule("@getify/proper-arrows/params",properArrows.rules.params); 24 | eslinter.defineRule("@getify/proper-arrows/name",properArrows.rules.name); 25 | eslinter.defineRule("@getify/proper-arrows/where",properArrows.rules.where); 26 | eslinter.defineRule("@getify/proper-arrows/return",properArrows.rules.return); 27 | eslinter.defineRule("@getify/proper-arrows/this",properArrows.rules.this); 28 | 29 | global.QUnit = require("qunit"); 30 | 31 | require(path.join("..","tests","qunit.config.js")); 32 | require(path.join("..","tests","tests.params.js")); 33 | require(path.join("..","tests","tests.name.js")); 34 | require(path.join("..","tests","tests.where.js")); 35 | require(path.join("..","tests","tests.return.js")); 36 | require(path.join("..","tests","tests.this.js")); 37 | require(path.join("..","tests","tests.trivial.js")); 38 | 39 | QUnit.start(); 40 | -------------------------------------------------------------------------------- /tests/qunit.config.js: -------------------------------------------------------------------------------- 1 | QUnit.config.requireExpects = true; 2 | 3 | QUnit.begin(begin); 4 | QUnit.log(testLog); 5 | QUnit.testDone(testDone); 6 | QUnit.done(done); 7 | 8 | var testLogEntries = {}; 9 | 10 | // ****************************** 11 | 12 | function begin(details){ 13 | printEnvNotification(); 14 | 15 | if (details.totalTests > 0) { 16 | console.log(`ESLint Plugin 'proper-arrows' Test Suite (${details.totalTests})`); 17 | console.log(""); 18 | } 19 | else { 20 | console.log("ESLint Plugin 'proper-arrows' Test Suite: empty!"); 21 | process.exit(1); 22 | } 23 | } 24 | 25 | function testLog(details) { 26 | var testId = details.testId; 27 | 28 | testLogEntries[testId] = testLogEntries[testId] || {}; 29 | testLogEntries[testId][details.message] = { ...details, }; 30 | } 31 | 32 | function testDone(results){ 33 | var testId = results.testId; 34 | 35 | if (results.failed > 0) { 36 | console.log(`Failed: '${results.name}' (${results.failed}/${results.total})`); 37 | for (let i = 0; i < results.assertions.length; i++) { 38 | if (results.assertions[i].result === false) { 39 | let { message, expected, actual, source, } = testLogEntries[testId][results.assertions[i].message]; 40 | console.log(` ${message}`); 41 | // is there a JS exception stack trace included? 42 | if (source && /^\w*Error: .+/.test(source)) { 43 | console.log(""); 44 | console.log(` ${source}`); 45 | console.log(""); 46 | } 47 | else { 48 | console.log(` expected: ${prettyPrint(expected)}`); 49 | console.log(` actual: ${prettyPrint(actual)}`); 50 | } 51 | } 52 | } 53 | } 54 | else if (results.passed > 0) { 55 | console.log(`Passed: '${results.name}' (${results.passed}/${results.total})`); 56 | } 57 | else { 58 | console.log(`No assertions run: '${results.name}'`); 59 | } 60 | } 61 | 62 | function done(results){ 63 | console.log(""); 64 | 65 | if (results.failed > 0) { 66 | console.log(`Failed (${results.failed}/${results.total})`); 67 | printEnvNotification(); 68 | process.exit(1); 69 | } 70 | else if (results.passed > 0) { 71 | console.log(`Passed (${results.passed}/${results.total})`); 72 | printEnvNotification(); 73 | process.exit(0); 74 | } 75 | else { 76 | console.log("No tests run!"); 77 | printEnvNotification(); 78 | process.exit(1); 79 | } 80 | } 81 | 82 | function prettyPrint(v) { 83 | if (Array.isArray(v)) { 84 | return `[${ v.map( prettyPrint ).toString() }]`; 85 | } 86 | else if (typeof v == "bigint") { 87 | return `${v}n`; 88 | } 89 | else if (v && typeof v == "object") { 90 | return JSON.stringify(v,function json(k,v){ 91 | if (v === undefined) { 92 | return null; 93 | } 94 | return v; 95 | }); 96 | } 97 | return String(v); 98 | } 99 | 100 | function printEnvNotification() { 101 | console.log(""); 102 | console.log("**********************************"); 103 | if (process.env.TEST_DIST) { 104 | console.log("********** TESTING DIST **********"); 105 | } 106 | else if (process.env.TEST_PACKAGE) { 107 | console.log("******** TESTING PACKAGE *********"); 108 | } 109 | else { 110 | console.log("********** TESTING SRC ***********"); 111 | } 112 | console.log("**********************************"); 113 | console.log(""); 114 | } 115 | -------------------------------------------------------------------------------- /tests/tests.name.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var linterOptions = { 4 | name: { 5 | parserOptions: { ecmaVersion: 2015, }, 6 | rules: { "@getify/proper-arrows/name": ["error",{trivial:true,},], }, 7 | }, 8 | }; 9 | 10 | QUnit.test( "NAME: arrow with variable declaration", function test(assert){ 11 | var code = ` 12 | var x = y => y; 13 | `; 14 | 15 | var results = eslinter.verify( code, linterOptions.name ); 16 | 17 | assert.expect( 1 ); 18 | assert.strictEqual( results.length, 0, "no errors" ); 19 | } ); 20 | 21 | QUnit.test( "NAME: arrow with variable assignment", function test(assert){ 22 | var code = ` 23 | x = y => y; 24 | `; 25 | 26 | var results = eslinter.verify( code, linterOptions.name ); 27 | 28 | assert.expect( 1 ); 29 | assert.strictEqual( results.length, 0, "no errors" ); 30 | } ); 31 | 32 | QUnit.test( "NAME: arrow with property name", function test(assert){ 33 | var code = ` 34 | var o = { x: y => y }; 35 | `; 36 | 37 | var results = eslinter.verify( code, linterOptions.name ); 38 | 39 | assert.expect( 1 ); 40 | assert.strictEqual( results.length, 0, "no errors" ); 41 | } ); 42 | 43 | QUnit.test( "NAME: arrow in default value clause", function test(assert){ 44 | var code = ` 45 | function f(x = y => y) {} 46 | [z = (w,q) => w * q] = arr; 47 | [[z] = [(a,b) => a * b]] = arr; 48 | `; 49 | 50 | var results = eslinter.verify( code, linterOptions.name ); 51 | var [{ ruleId, messageId, } = {},] = results || []; 52 | 53 | assert.expect( 3 ); 54 | assert.strictEqual( results.length, 1, "only 1 error" ); 55 | assert.strictEqual( ruleId, "@getify/proper-arrows/name", "ruleId" ); 56 | assert.strictEqual( messageId, "noName", "messageId" ); 57 | } ); 58 | 59 | QUnit.test( "NAME: arrow in default module export", function test(assert){ 60 | var code = ` 61 | export default x => x; 62 | `; 63 | 64 | var results = eslinter.verify( code, { ...linterOptions.name, parserOptions: { ...linterOptions.name.parserOptions, sourceType: "module", }, }, ); 65 | 66 | assert.expect( 1 ); 67 | assert.strictEqual( results.length, 0, "no errors" ); 68 | } ); 69 | 70 | QUnit.test( "NAME: arrow in expression", function test(assert){ 71 | var code = ` 72 | var x = 1 + (y => y); 73 | `; 74 | 75 | var results = eslinter.verify( code, linterOptions.name ); 76 | var [{ ruleId, messageId, } = {},] = results || []; 77 | 78 | assert.expect( 3 ); 79 | assert.strictEqual( results.length, 1, "only 1 error" ); 80 | assert.strictEqual( ruleId, "@getify/proper-arrows/name", "ruleId" ); 81 | assert.strictEqual( messageId, "noName", "messageId" ); 82 | } ); 83 | 84 | QUnit.test( "NAME: arrow as IIFE", function test(assert){ 85 | var code = ` 86 | (x => x)(3); 87 | `; 88 | 89 | var results = eslinter.verify( code, linterOptions.name ); 90 | var [{ ruleId, messageId, } = {},] = results || []; 91 | 92 | assert.expect( 3 ); 93 | assert.strictEqual( results.length, 1, "only 1 error" ); 94 | assert.strictEqual( ruleId, "@getify/proper-arrows/name", "ruleId" ); 95 | assert.strictEqual( messageId, "noName", "messageId" ); 96 | } ); 97 | 98 | QUnit.test( "NAME: arrows as returns", function test(assert){ 99 | var code = ` 100 | function x() { return y => y; } 101 | var z = w => r => r[w]; 102 | `; 103 | 104 | var results = eslinter.verify( code, linterOptions.name ); 105 | var [ 106 | { ruleId: ruleId1, messageId: messageId1, } = {}, 107 | { ruleId: ruleId2, messageId: messageId2, } = {}, 108 | ] = results || []; 109 | 110 | assert.expect( 5 ); 111 | assert.strictEqual( results.length, 2, "only 2 errors" ); 112 | assert.strictEqual( ruleId1, "@getify/proper-arrows/name", "ruleId1" ); 113 | assert.strictEqual( messageId1, "noName", "messageId1" ); 114 | assert.strictEqual( ruleId2, "@getify/proper-arrows/name", "ruleId2" ); 115 | assert.strictEqual( messageId2, "noName", "messageId2" ); 116 | } ); 117 | 118 | QUnit.test( "NAME: arrow as argument", function test(assert){ 119 | var code = ` 120 | x(y => y); 121 | `; 122 | 123 | var results = eslinter.verify( code, linterOptions.name ); 124 | var [{ ruleId, messageId, } = {},] = results || []; 125 | 126 | assert.expect( 3 ); 127 | assert.strictEqual( results.length, 1, "only 1 error" ); 128 | assert.strictEqual( ruleId, "@getify/proper-arrows/name", "ruleId" ); 129 | assert.strictEqual( messageId, "noName", "messageId" ); 130 | } ); 131 | 132 | QUnit.test( "NAME: arrow in array", function test(assert){ 133 | var code = ` 134 | var x = [y => y]; 135 | `; 136 | 137 | var results = eslinter.verify( code, linterOptions.name ); 138 | var [{ ruleId, messageId, } = {},] = results || []; 139 | 140 | assert.expect( 3 ); 141 | assert.strictEqual( results.length, 1, "only 1 error" ); 142 | assert.strictEqual( ruleId, "@getify/proper-arrows/name", "ruleId" ); 143 | assert.strictEqual( messageId, "noName", "messageId" ); 144 | } ); 145 | -------------------------------------------------------------------------------- /tests/tests.params.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var linterOptions = { 4 | paramsDefault: { 5 | parserOptions: { ecmaVersion: 2015, }, 6 | rules: { "@getify/proper-arrows/params": "error", }, 7 | }, 8 | paramsUnusedAllDefault: { 9 | parserOptions: { ecmaVersion: 2015, }, 10 | rules: { "@getify/proper-arrows/params": ["error",{count: 1000,length:0,trivial:true,},], }, 11 | }, 12 | paramsUnusedAll: { 13 | parserOptions: { ecmaVersion: 2015, }, 14 | rules: { "@getify/proper-arrows/params": ["error",{unused:"all",count:1000,length:0,trivial:true,},], }, 15 | }, 16 | paramsUnusedTrailing: { 17 | parserOptions: { ecmaVersion: 2015, }, 18 | rules: { "@getify/proper-arrows/params": ["error",{unused:"trailing",count:1000,length:0,trivial:true,},], }, 19 | }, 20 | paramsUnusedNone: { 21 | parserOptions: { ecmaVersion: 2015, }, 22 | rules: { "@getify/proper-arrows/params": ["error",{unused:"none",count:1000,length:0,trivial:true,},], }, 23 | }, 24 | paramsCountDefault: { 25 | parserOptions: { ecmaVersion: 2015, }, 26 | rules: { "@getify/proper-arrows/params": ["error",{unused:"none",length:0,trivial:true,},], }, 27 | }, 28 | paramsCount0: { 29 | parserOptions: { ecmaVersion: 2015, }, 30 | rules: { "@getify/proper-arrows/params": ["error",{unused:"none",count:0,length:0,trivial:true,},], }, 31 | }, 32 | paramsLengthDefault: { 33 | parserOptions: { ecmaVersion: 2015, }, 34 | rules: { "@getify/proper-arrows/params": ["error",{unused:"none",count:1000,trivial:true,},], }, 35 | }, 36 | paramsLength1: { 37 | parserOptions: { ecmaVersion: 2015, }, 38 | rules: { "@getify/proper-arrows/params": ["error",{unused:"none",count:1000,length:1,trivial:true,},], }, 39 | }, 40 | paramsAllowed: { 41 | parserOptions: { ecmaVersion: 2015, }, 42 | rules: { "@getify/proper-arrows/params": ["error",{allowed:[],trivial:true,},], }, 43 | }, 44 | }; 45 | 46 | QUnit.test( "PARAMS (default): conforming", function test(assert){ 47 | var code = ` 48 | var f = xx => xx * 2; 49 | var p = ([yy,,zz]) => yy * zz; 50 | var r = ({ ww, G: gg }) => ww * gg; 51 | `; 52 | 53 | var results = eslinter.verify( code, linterOptions.paramsDefault ); 54 | 55 | assert.expect( 1 ); 56 | assert.strictEqual( results.length, 0, "no errors" ); 57 | } ); 58 | 59 | QUnit.test( "PARAMS (default): violating", function test(assert){ 60 | var code = ` 61 | var f = (x,[y],z,{w}) => y + z; 62 | `; 63 | 64 | var results = eslinter.verify( code, linterOptions.paramsDefault ); 65 | var [ 66 | { ruleId: ruleId1, messageId: messageId1, } = {}, 67 | { ruleId: ruleId2, messageId: messageId2, } = {}, 68 | { ruleId: ruleId3, messageId: messageId3, } = {}, 69 | { ruleId: ruleId4, messageId: messageId4, } = {}, 70 | { ruleId: ruleId5, messageId: messageId5, } = {}, 71 | { ruleId: ruleId6, messageId: messageId6, } = {}, 72 | { ruleId: ruleId7, messageId: messageId7, } = {}, 73 | ] = results || []; 74 | 75 | assert.expect( 15 ); 76 | assert.strictEqual( results.length, 7, "only 7 errors" ); 77 | assert.strictEqual( ruleId1, "@getify/proper-arrows/params", "ruleId1" ); 78 | assert.strictEqual( messageId1, "tooShort", "messageId1" ); 79 | assert.strictEqual( ruleId2, "@getify/proper-arrows/params", "ruleId2" ); 80 | assert.strictEqual( messageId2, "unused", "messageId2" ); 81 | assert.strictEqual( ruleId3, "@getify/proper-arrows/params", "ruleId3" ); 82 | assert.strictEqual( messageId3, "tooShort", "messageId3" ); 83 | assert.strictEqual( ruleId4, "@getify/proper-arrows/params", "ruleId4" ); 84 | assert.strictEqual( messageId4, "tooShort", "messageId4" ); 85 | assert.strictEqual( ruleId5, "@getify/proper-arrows/params", "ruleId5" ); 86 | assert.strictEqual( messageId5, "tooMany", "messageId5" ); 87 | assert.strictEqual( ruleId6, "@getify/proper-arrows/params", "ruleId6" ); 88 | assert.strictEqual( messageId6, "tooShort", "messageId6" ); 89 | assert.strictEqual( ruleId7, "@getify/proper-arrows/params", "ruleId7" ); 90 | assert.strictEqual( messageId7, "unused", "messageId7" ); 91 | } ); 92 | 93 | QUnit.test( "PARAMS (unused: all, default): conforming", function test(assert){ 94 | var code = ` 95 | var f = (x,y,z,w) => x + y + z + w; 96 | var p = x => y => z => w => x + y + z + w; 97 | `; 98 | 99 | var results = eslinter.verify( code, linterOptions.paramsUnusedAllDefault ); 100 | 101 | assert.expect( 1 ); 102 | assert.strictEqual( results.length, 0, "no errors" ); 103 | } ); 104 | 105 | QUnit.test( "PARAMS (unused: all, default): violating", function test(assert){ 106 | var code = ` 107 | var f = (x,y,z,w) => y + z; 108 | var p = x => y => z => w => y + z; 109 | `; 110 | 111 | var results = eslinter.verify( code, linterOptions.paramsUnusedAllDefault ); 112 | var [ 113 | { ruleId: ruleId1, messageId: messageId1, } = {}, 114 | { ruleId: ruleId2, messageId: messageId2, } = {}, 115 | { ruleId: ruleId3, messageId: messageId3, } = {}, 116 | { ruleId: ruleId4, messageId: messageId4, } = {}, 117 | ] = results || []; 118 | 119 | assert.expect( 9 ); 120 | assert.strictEqual( results.length, 4, "only 4 errors" ); 121 | assert.strictEqual( ruleId1, "@getify/proper-arrows/params", "ruleId1" ); 122 | assert.strictEqual( messageId1, "unused", "messageId1" ); 123 | assert.strictEqual( ruleId2, "@getify/proper-arrows/params", "ruleId2" ); 124 | assert.strictEqual( messageId2, "unused", "messageId2" ); 125 | assert.strictEqual( ruleId3, "@getify/proper-arrows/params", "ruleId3" ); 126 | assert.strictEqual( messageId3, "unused", "messageId3" ); 127 | assert.strictEqual( ruleId4, "@getify/proper-arrows/params", "ruleId4" ); 128 | assert.strictEqual( messageId4, "unused", "messageId4" ); 129 | } ); 130 | 131 | QUnit.test( "PARAMS (unused: all): conforming | nesting, shadowing", function test(assert){ 132 | var code = ` 133 | var f; 134 | f = (x) => x; 135 | f = (x) => () => x; 136 | f = (x) => { x; }; 137 | f = (x, y = x) => { y; }; 138 | f = (x, y = x) => { var x; x = y; }; 139 | f = (x, y = x) => { var x = y; }; 140 | f = (x, y = () => x) => { y; }; 141 | f = (x, y = () => { x = 3; }) => { y; }; 142 | f = (x, y = (z = x) => z) => { y; }; 143 | `; 144 | 145 | var results = eslinter.verify( code, linterOptions.paramsUnusedAll ); 146 | 147 | assert.expect( 1 ); 148 | assert.strictEqual( results.length, 0, "no errors" ); 149 | } ); 150 | 151 | QUnit.test( "PARAMS (unused: all): violating | nesting, shadowing", function test(assert){ 152 | var code = ` 153 | var f; 154 | 155 | f = (x) => 3; 156 | f = (y = 3) => { var y; }; 157 | f = (z = 3) => { var z = 3; }; 158 | f = (w = 3) => { var w; w = 3; }; 159 | f = (r) => { var r = 3; }; 160 | f = (k, m = (k) => k) => m; 161 | f = (a, b = (k = 3) => { k = 3; }) => b; 162 | f = (s, t = () => { var s; }) => t; 163 | f = (d, e = () => { var d = 3; }) => e; 164 | f = (g, h = () => { var g; g = 3; }) => h; 165 | `; 166 | 167 | var results = eslinter.verify( code, linterOptions.paramsUnusedAll ); 168 | var [ 169 | { ruleId: ruleId1, messageId: messageId1, message: message1, } = {}, 170 | { ruleId: ruleId2, messageId: messageId2, message: message2, } = {}, 171 | { ruleId: ruleId3, messageId: messageId3, message: message3, } = {}, 172 | { ruleId: ruleId4, messageId: messageId4, message: message4, } = {}, 173 | { ruleId: ruleId5, messageId: messageId5, message: message5, } = {}, 174 | { ruleId: ruleId6, messageId: messageId6, message: message6, } = {}, 175 | { ruleId: ruleId7, messageId: messageId7, message: message7, } = {}, 176 | { ruleId: ruleId8, messageId: messageId8, message: message8, } = {}, 177 | { ruleId: ruleId9, messageId: messageId9, message: message9, } = {}, 178 | { ruleId: ruleId10, messageId: messageId10, message: message10, } = {}, 179 | ] = results || []; 180 | 181 | assert.expect( 31 ); 182 | assert.strictEqual( results.length, 10, "only 10 errors" ); 183 | assert.strictEqual( ruleId1, "@getify/proper-arrows/params", "ruleId1" ); 184 | assert.strictEqual( messageId1, "unused", "messageId1" ); 185 | assert.ok( message1.includes("`x`"), "message1" ); 186 | assert.strictEqual( ruleId2, "@getify/proper-arrows/params", "ruleId2" ); 187 | assert.strictEqual( messageId2, "unused", "messageId2" ); 188 | assert.ok( message2.includes("`y`"), "message2" ); 189 | assert.strictEqual( ruleId3, "@getify/proper-arrows/params", "ruleId3" ); 190 | assert.strictEqual( messageId3, "unused", "messageId3" ); 191 | assert.ok( message3.includes("`z`"), "message3" ); 192 | assert.strictEqual( ruleId4, "@getify/proper-arrows/params", "ruleId4" ); 193 | assert.strictEqual( messageId4, "unused", "messageId4" ); 194 | assert.ok( message4.includes("`w`"), "message4" ); 195 | assert.strictEqual( ruleId5, "@getify/proper-arrows/params", "ruleId5" ); 196 | assert.strictEqual( messageId5, "unused", "messageId5" ); 197 | assert.ok( message5.includes("`r`"), "message5" ); 198 | assert.strictEqual( ruleId6, "@getify/proper-arrows/params", "ruleId6" ); 199 | assert.strictEqual( messageId6, "unused", "messageId6" ); 200 | assert.ok( message6.includes("`k`"), "message6" ); 201 | assert.strictEqual( ruleId7, "@getify/proper-arrows/params", "ruleId7" ); 202 | assert.strictEqual( messageId7, "unused", "messageId7" ); 203 | assert.ok( message7.includes("`a`"), "message7" ); 204 | assert.strictEqual( ruleId8, "@getify/proper-arrows/params", "ruleId8" ); 205 | assert.strictEqual( messageId8, "unused", "messageId8" ); 206 | assert.ok( message8.includes("`s`"), "message8" ); 207 | assert.strictEqual( ruleId9, "@getify/proper-arrows/params", "ruleId9" ); 208 | assert.strictEqual( messageId9, "unused", "messageId9" ); 209 | assert.ok( message9.includes("`d`"), "message9" ); 210 | assert.strictEqual( ruleId10, "@getify/proper-arrows/params", "ruleId10" ); 211 | assert.strictEqual( messageId10, "unused", "messageId10" ); 212 | assert.ok( message10.includes("`g`"), "message10" ); 213 | } ); 214 | 215 | QUnit.test( "PARAMS (unused: trailing): conforming", function test(assert){ 216 | var code = ` 217 | var f = (x,y,z,w) => z + w; 218 | `; 219 | 220 | var results = eslinter.verify( code, linterOptions.paramsUnusedTrailing ); 221 | 222 | assert.expect( 1 ); 223 | assert.strictEqual( results.length, 0, "no errors" ); 224 | } ); 225 | 226 | QUnit.test( "PARAMS (unused: trailing): violating", function test(assert){ 227 | var code = ` 228 | var f = (x,y,z,w) => y + z; 229 | var p = (a,b,c,d) => a + b; 230 | var t = (s,t,u,v) => 1; 231 | `; 232 | 233 | var results = eslinter.verify( code, linterOptions.paramsUnusedTrailing ); 234 | var [ 235 | { ruleId: ruleId1, messageId: messageId1, message: message1, } = {}, 236 | { ruleId: ruleId2, messageId: messageId2, message: message2, } = {}, 237 | { ruleId: ruleId3, messageId: messageId3, message: message3, } = {}, 238 | { ruleId: ruleId4, messageId: messageId4, message: message4, } = {}, 239 | { ruleId: ruleId5, messageId: messageId5, message: message5, } = {}, 240 | { ruleId: ruleId6, messageId: messageId6, message: message6, } = {}, 241 | { ruleId: ruleId7, messageId: messageId7, message: message7, } = {}, 242 | ] = results || []; 243 | 244 | assert.expect( 21 ); 245 | assert.strictEqual( ruleId1, "@getify/proper-arrows/params", "ruleId1" ); 246 | assert.strictEqual( messageId1, "unused", "messageId1" ); 247 | assert.ok( message1.includes("`w`"), "message1" ); 248 | assert.strictEqual( ruleId2, "@getify/proper-arrows/params", "ruleId2" ); 249 | assert.strictEqual( messageId2, "unused", "messageId2" ); 250 | assert.ok( message2.includes("`c`"), "message2" ); 251 | assert.strictEqual( ruleId3, "@getify/proper-arrows/params", "ruleId3" ); 252 | assert.strictEqual( messageId3, "unused", "messageId3" ); 253 | assert.ok( message3.includes("`d`"), "message3" ); 254 | assert.strictEqual( ruleId4, "@getify/proper-arrows/params", "ruleId4" ); 255 | assert.strictEqual( messageId4, "unused", "messageId4" ); 256 | assert.ok( message4.includes("`s`"), "message4" ); 257 | assert.strictEqual( ruleId5, "@getify/proper-arrows/params", "ruleId5" ); 258 | assert.strictEqual( messageId5, "unused", "messageId5" ); 259 | assert.ok( message5.includes("`t`"), "message5" ); 260 | assert.strictEqual( ruleId6, "@getify/proper-arrows/params", "ruleId6" ); 261 | assert.strictEqual( messageId6, "unused", "messageId6" ); 262 | assert.ok( message6.includes("`u`"), "message6" ); 263 | assert.strictEqual( ruleId7, "@getify/proper-arrows/params", "ruleId7" ); 264 | assert.strictEqual( messageId7, "unused", "messageId7" ); 265 | assert.ok( message7.includes("`v`"), "message7" ); 266 | } ); 267 | 268 | QUnit.test( "PARAMS (unused: none): violating", function test(assert){ 269 | var code = ` 270 | var f = (x,y,z,w) => y + z; 271 | `; 272 | 273 | var results = eslinter.verify( code, linterOptions.paramsUnusedNone ); 274 | 275 | assert.expect( 1 ); 276 | assert.strictEqual( results.length, 0, "no errors" ); 277 | } ); 278 | 279 | QUnit.test( "PARAMS (count: 3, default): conforming", function test(assert){ 280 | var code = ` 281 | var f = (x,y,z) => x; 282 | `; 283 | 284 | var results = eslinter.verify( code, linterOptions.paramsCountDefault ); 285 | 286 | assert.expect( 1 ); 287 | assert.strictEqual( results.length, 0, "no errors" ); 288 | } ); 289 | 290 | QUnit.test( "PARAMS (count: 3, default): violating", function test(assert){ 291 | var code = ` 292 | var f = (x,y,z,w,g) => x; 293 | `; 294 | 295 | var results = eslinter.verify( code, linterOptions.paramsCountDefault ); 296 | var [{ ruleId, messageId, message, } = {},] = results || []; 297 | 298 | assert.expect( 4 ); 299 | assert.strictEqual( results.length, 1, "only 1 error" ); 300 | assert.strictEqual( ruleId, "@getify/proper-arrows/params", "ruleId" ); 301 | assert.strictEqual( messageId, "tooMany", "messageId" ); 302 | assert.ok( message.includes("`w`"), "message" ); 303 | } ); 304 | 305 | QUnit.test( "PARAMS (count: 0): conforming", function test(assert){ 306 | var code = ` 307 | var f = () => 1; 308 | `; 309 | 310 | var results = eslinter.verify( code, linterOptions.paramsCount0 ); 311 | 312 | assert.expect( 1 ); 313 | assert.strictEqual( results.length, 0, "no errors" ); 314 | } ); 315 | 316 | QUnit.test( "PARAMS (count: 0): violating", function test(assert){ 317 | var code = ` 318 | var f = (x,y) => x; 319 | `; 320 | 321 | var results = eslinter.verify( code, linterOptions.paramsCount0 ); 322 | var [{ ruleId, messageId, message, } = {},] = results || []; 323 | 324 | assert.expect( 4 ); 325 | assert.strictEqual( results.length, 1, "only 1 error" ); 326 | assert.strictEqual( ruleId, "@getify/proper-arrows/params", "ruleId" ); 327 | assert.strictEqual( messageId, "tooMany", "messageId" ); 328 | assert.ok( message.includes("`x`"), "message" ); 329 | } ); 330 | 331 | QUnit.test( "PARAMS (length: 2, default): conforming", function test(assert){ 332 | var code = ` 333 | var f = (xx,yy,zz) => xx; 334 | `; 335 | 336 | var results = eslinter.verify( code, linterOptions.paramsLengthDefault ); 337 | 338 | assert.expect( 1 ); 339 | assert.strictEqual( results.length, 0, "no errors" ); 340 | } ); 341 | 342 | QUnit.test( "PARAMS (length: 2, default): violating", function test(assert){ 343 | var code = ` 344 | var f = (xx,y,zz) => xx; 345 | `; 346 | 347 | var results = eslinter.verify( code, linterOptions.paramsLengthDefault ); 348 | var [{ ruleId, messageId, message, } = {},] = results || []; 349 | 350 | assert.expect( 4 ); 351 | assert.strictEqual( results.length, 1, "only 1 error" ); 352 | assert.strictEqual( ruleId, "@getify/proper-arrows/params", "ruleId" ); 353 | assert.strictEqual( messageId, "tooShort", "messageId" ); 354 | assert.ok( message.includes("`y`"), "message" ); 355 | } ); 356 | 357 | QUnit.test( "PARAMS (length: 1): conforming", function test(assert){ 358 | var code = ` 359 | var f = (x,y) => x; 360 | `; 361 | 362 | var results = eslinter.verify( code, linterOptions.paramsLength1 ); 363 | 364 | assert.expect( 1 ); 365 | assert.strictEqual( results.length, 0, "no errors" ); 366 | } ); 367 | 368 | QUnit.test( "PARAMS (allowed, defaults): conforming", function test(assert){ 369 | var code = ` 370 | var f = (x,y,z,w) => y + z; 371 | `; 372 | 373 | var config = copyObj(linterOptions.paramsAllowed); 374 | config.rules["@getify/proper-arrows/params"][1].allowed = ["x","y","z","w",]; 375 | 376 | var results = eslinter.verify( code, config ); 377 | 378 | assert.expect( 1 ); 379 | assert.strictEqual( results.length, 0, "no errors" ); 380 | } ); 381 | 382 | QUnit.test( "PARAMS (allowed, defaults): violating", function test(assert){ 383 | var code = ` 384 | var f = (x,y,z,w) => y + z; 385 | `; 386 | 387 | var config = copyObj(linterOptions.paramsAllowed); 388 | config.rules["@getify/proper-arrows/params"][1].allowed = ["x","z",]; 389 | 390 | var results = eslinter.verify( code, config ); 391 | var [ 392 | { ruleId: ruleId1, messageId: messageId1, } = {}, 393 | { ruleId: ruleId2, messageId: messageId2, } = {}, 394 | { ruleId: ruleId3, messageId: messageId3, } = {}, 395 | { ruleId: ruleId4, messageId: messageId4, } = {}, 396 | ] = results || []; 397 | 398 | assert.expect( 9 ); 399 | assert.strictEqual( results.length, 4, "only 4 errors" ); 400 | assert.strictEqual( ruleId1, "@getify/proper-arrows/params", "ruleId1" ); 401 | assert.strictEqual( messageId1, "tooShort", "messageId1" ); 402 | assert.strictEqual( ruleId2, "@getify/proper-arrows/params", "ruleId2" ); 403 | assert.strictEqual( messageId2, "tooMany", "messageId2" ); 404 | assert.strictEqual( ruleId3, "@getify/proper-arrows/params", "ruleId3" ); 405 | assert.strictEqual( messageId3, "tooShort", "messageId3" ); 406 | assert.strictEqual( ruleId4, "@getify/proper-arrows/params", "ruleId4" ); 407 | assert.strictEqual( messageId4, "unused", "messageId4" ); 408 | } ); 409 | 410 | QUnit.test( "PARAMS (allowed, unused:all): conforming", function test(assert){ 411 | var code = ` 412 | var f = (x,y,z,w) => y + z; 413 | `; 414 | 415 | var config = copyObj(linterOptions.paramsAllowed); 416 | config.rules["@getify/proper-arrows/params"][1].unused = "all"; 417 | config.rules["@getify/proper-arrows/params"][1].count = 1000; 418 | config.rules["@getify/proper-arrows/params"][1].length = 1; 419 | config.rules["@getify/proper-arrows/params"][1].allowed = ["x","w",]; 420 | 421 | var results = eslinter.verify( code, config ); 422 | 423 | assert.expect( 1 ); 424 | assert.strictEqual( results.length, 0, "no errors" ); 425 | } ); 426 | 427 | QUnit.test( "PARAMS (allowed, unused:all): violating", function test(assert){ 428 | var code = ` 429 | var f = (x,y,z,w) => y + z; 430 | `; 431 | 432 | var config = copyObj(linterOptions.paramsAllowed); 433 | config.rules["@getify/proper-arrows/params"][1].unused = "all"; 434 | config.rules["@getify/proper-arrows/params"][1].count = 1000; 435 | config.rules["@getify/proper-arrows/params"][1].length = 1; 436 | config.rules["@getify/proper-arrows/params"][1].allowed = ["x",]; 437 | 438 | var results = eslinter.verify( code, config ); 439 | var [{ ruleId, messageId, message, } = {},] = results || []; 440 | 441 | assert.expect( 4 ); 442 | assert.strictEqual( results.length, 1, "only 1 error" ); 443 | assert.strictEqual( ruleId, "@getify/proper-arrows/params", "ruleId" ); 444 | assert.strictEqual( messageId, "unused", "messageId" ); 445 | assert.ok( message.includes("`w`"), "message" ); 446 | } ); 447 | 448 | QUnit.test( "PARAMS (allowed, unused:trailing): conforming", function test(assert){ 449 | var code = ` 450 | var f = (x,y,z,w) => y + z; 451 | `; 452 | 453 | var config = copyObj(linterOptions.paramsAllowed); 454 | config.rules["@getify/proper-arrows/params"][1].unused = "trailing"; 455 | config.rules["@getify/proper-arrows/params"][1].count = 1000; 456 | config.rules["@getify/proper-arrows/params"][1].length = 1; 457 | config.rules["@getify/proper-arrows/params"][1].allowed = ["w",]; 458 | 459 | var results = eslinter.verify( code, config ); 460 | 461 | assert.expect( 1 ); 462 | assert.strictEqual( results.length, 0, "no errors" ); 463 | } ); 464 | 465 | QUnit.test( "PARAMS (allowed, unused:trailing): violating", function test(assert){ 466 | var code = ` 467 | var f = (x,y,z,w) => y + z; 468 | `; 469 | 470 | var config = copyObj(linterOptions.paramsAllowed); 471 | config.rules["@getify/proper-arrows/params"][1].unused = "trailing"; 472 | config.rules["@getify/proper-arrows/params"][1].count = 1000; 473 | config.rules["@getify/proper-arrows/params"][1].length = 1; 474 | config.rules["@getify/proper-arrows/params"][1].allowed = ["x",]; 475 | 476 | var results = eslinter.verify( code, config ); 477 | var [{ ruleId, messageId, message, } = {},] = results || []; 478 | 479 | assert.expect( 4 ); 480 | assert.strictEqual( results.length, 1, "only 1 error" ); 481 | assert.strictEqual( ruleId, "@getify/proper-arrows/params", "ruleId" ); 482 | assert.strictEqual( messageId, "unused", "messageId" ); 483 | assert.ok( message.includes("`w`"), "message" ); 484 | } ); 485 | 486 | QUnit.test( "PARAMS (allowed, count:0): conforming", function test(assert){ 487 | var code = ` 488 | var f = (x,y,z,w) => y + z; 489 | `; 490 | 491 | var config = copyObj(linterOptions.paramsAllowed); 492 | config.rules["@getify/proper-arrows/params"][1].unused = "none"; 493 | config.rules["@getify/proper-arrows/params"][1].count = 0; 494 | config.rules["@getify/proper-arrows/params"][1].length = 1; 495 | config.rules["@getify/proper-arrows/params"][1].allowed = ["x","y","z","w",]; 496 | 497 | var results = eslinter.verify( code, config ); 498 | 499 | assert.expect( 1 ); 500 | assert.strictEqual( results.length, 0, "no errors" ); 501 | } ); 502 | 503 | QUnit.test( "PARAMS (allowed, count:0): violating", function test(assert){ 504 | var code = ` 505 | var f = (x,y,z,w) => y + z; 506 | `; 507 | 508 | var config = copyObj(linterOptions.paramsAllowed); 509 | config.rules["@getify/proper-arrows/params"][1].unused = "none"; 510 | config.rules["@getify/proper-arrows/params"][1].count = 0; 511 | config.rules["@getify/proper-arrows/params"][1].length = 1; 512 | config.rules["@getify/proper-arrows/params"][1].allowed = ["x",]; 513 | 514 | var results = eslinter.verify( code, config ); 515 | var [{ ruleId, messageId, message, } = {},] = results || []; 516 | 517 | assert.expect( 4 ); 518 | assert.strictEqual( results.length, 1, "only 1 error" ); 519 | assert.strictEqual( ruleId, "@getify/proper-arrows/params", "ruleId" ); 520 | assert.strictEqual( messageId, "tooMany", "messageId" ); 521 | assert.ok( message.includes("`y`"), "message" ); 522 | } ); 523 | 524 | QUnit.test( "PARAMS (allowed, count:2): conforming", function test(assert){ 525 | var code = ` 526 | var f = (x,y,z,w) => y + z; 527 | `; 528 | 529 | var config = copyObj(linterOptions.paramsAllowed); 530 | config.rules["@getify/proper-arrows/params"][1].unused = "none"; 531 | config.rules["@getify/proper-arrows/params"][1].count = 2; 532 | config.rules["@getify/proper-arrows/params"][1].length = 1; 533 | config.rules["@getify/proper-arrows/params"][1].allowed = ["z","w",]; 534 | 535 | var results = eslinter.verify( code, config ); 536 | 537 | assert.expect( 1 ); 538 | assert.strictEqual( results.length, 0, "no errors" ); 539 | } ); 540 | 541 | QUnit.test( "PARAMS (allowed, count:2): violating", function test(assert){ 542 | var code = ` 543 | var f = (x,y,z,w) => y + z; 544 | `; 545 | 546 | var config = copyObj(linterOptions.paramsAllowed); 547 | config.rules["@getify/proper-arrows/params"][1].unused = "none"; 548 | config.rules["@getify/proper-arrows/params"][1].count = 2; 549 | config.rules["@getify/proper-arrows/params"][1].length = 1; 550 | config.rules["@getify/proper-arrows/params"][1].allowed = ["z",]; 551 | 552 | var results = eslinter.verify( code, config ); 553 | var [{ ruleId, messageId, message, } = {},] = results || []; 554 | 555 | assert.expect( 4 ); 556 | assert.strictEqual( results.length, 1, "only 1 error" ); 557 | assert.strictEqual( ruleId, "@getify/proper-arrows/params", "ruleId" ); 558 | assert.strictEqual( messageId, "tooMany", "messageId" ); 559 | assert.ok( message.includes("`w`"), "message" ); 560 | } ); 561 | 562 | QUnit.test( "PARAMS (allowed, length:2): conforming", function test(assert){ 563 | var code = ` 564 | var f = (xx,y,zz,w) => y + zz; 565 | `; 566 | 567 | var config = copyObj(linterOptions.paramsAllowed); 568 | config.rules["@getify/proper-arrows/params"][1].unused = "none"; 569 | config.rules["@getify/proper-arrows/params"][1].count = 1000; 570 | config.rules["@getify/proper-arrows/params"][1].length = 2; 571 | config.rules["@getify/proper-arrows/params"][1].allowed = ["y","w",]; 572 | 573 | var results = eslinter.verify( code, config ); 574 | 575 | assert.expect( 1 ); 576 | assert.strictEqual( results.length, 0, "no errors" ); 577 | } ); 578 | 579 | QUnit.test( "PARAMS (allowed, length:2): violating", function test(assert){ 580 | var code = ` 581 | var f = (xx,y,zz,w) => y + zz; 582 | `; 583 | 584 | var config = copyObj(linterOptions.paramsAllowed); 585 | config.rules["@getify/proper-arrows/params"][1].unused = "none"; 586 | config.rules["@getify/proper-arrows/params"][1].count = 1000; 587 | config.rules["@getify/proper-arrows/params"][1].length = 2; 588 | config.rules["@getify/proper-arrows/params"][1].allowed = ["y",]; 589 | 590 | var results = eslinter.verify( code, config ); 591 | var [{ ruleId, messageId, message, } = {},] = results || []; 592 | 593 | assert.expect( 4 ); 594 | assert.strictEqual( results.length, 1, "only 1 error" ); 595 | assert.strictEqual( ruleId, "@getify/proper-arrows/params", "ruleId" ); 596 | assert.strictEqual( messageId, "tooShort", "messageId" ); 597 | assert.ok( message.includes("`w`"), "message" ); 598 | } ); 599 | 600 | 601 | 602 | 603 | // ********************* 604 | 605 | function copyObj(obj) { 606 | return JSON.parse(JSON.stringify(obj)); 607 | } 608 | -------------------------------------------------------------------------------- /tests/tests.return.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var linterOptions = { 4 | returnDefault: { 5 | parserOptions: { ecmaVersion: 2015, }, 6 | rules: { "@getify/proper-arrows/return": "error", }, 7 | }, 8 | returnObjectDefault: { 9 | parserOptions: { ecmaVersion: 2015, }, 10 | rules: { "@getify/proper-arrows/return": ["error",{ternary:1000,chained:false,sequence:false,trivial:true,},], }, 11 | }, 12 | returnObject: { 13 | parserOptions: { ecmaVersion: 2015, }, 14 | rules: { "@getify/proper-arrows/return": ["error",{object:true,ternary:1000,chained:false,sequence:false,trivial:true,},], }, 15 | }, 16 | returnTernaryDefault: { 17 | parserOptions: { ecmaVersion: 2015, }, 18 | rules: { "@getify/proper-arrows/return": ["error",{object:false,chained:false,sequence:false,trivial:true,},], }, 19 | }, 20 | returnTernary1: { 21 | parserOptions: { ecmaVersion: 2015, }, 22 | rules: { "@getify/proper-arrows/return": ["error",{object:false,ternary:1,chained:false,sequence:false,trivial:true,},], }, 23 | }, 24 | returnTernary2: { 25 | parserOptions: { ecmaVersion: 2015, }, 26 | rules: { "@getify/proper-arrows/return": ["error",{object:false,ternary:2,chained:false,sequence:false,trivial:true,},], }, 27 | }, 28 | returnChainedDefault: { 29 | parserOptions: { ecmaVersion: 2015, }, 30 | rules: { "@getify/proper-arrows/return": ["error",{object:false,ternary:1000,sequence:false,trivial:true,},], }, 31 | }, 32 | returnChained: { 33 | parserOptions: { ecmaVersion: 2015, }, 34 | rules: { "@getify/proper-arrows/return": ["error",{object:false,ternary:1000,chained:true,sequence:false,trivial:true,},], }, 35 | }, 36 | returnSequenceDefault: { 37 | parserOptions: { ecmaVersion: 2015, }, 38 | rules: { "@getify/proper-arrows/return": ["error",{object:false,ternary:1000,chained:false,trivial:true,},], }, 39 | }, 40 | returnSequence: { 41 | parserOptions: { ecmaVersion: 2015, }, 42 | rules: { "@getify/proper-arrows/return": ["error",{object:false,ternary:1000,chained:false,sequence:true,trivial:true,},], }, 43 | }, 44 | }; 45 | 46 | QUnit.test( "RETURN (default): conforming", function test(assert){ 47 | var code = ` 48 | var x = y => { return { y }; }; 49 | var f = a => { return (1,a,2); }; 50 | var g = r => (k => r + k); 51 | `; 52 | 53 | var results = eslinter.verify( code, linterOptions.returnDefault ); 54 | 55 | assert.expect( 1 ); 56 | assert.strictEqual( results.length, 0, "no errors" ); 57 | } ); 58 | 59 | QUnit.test( "RETURN (default): violating", function test(assert){ 60 | var code = ` 61 | var x = y => z => ({ y, z }); 62 | var f = a => (1,a,2); 63 | `; 64 | 65 | var results = eslinter.verify( code, linterOptions.returnDefault ); 66 | var [ 67 | { ruleId: ruleId1, messageId: messageId1, } = {}, 68 | { ruleId: ruleId2, messageId: messageId2, } = {}, 69 | { ruleId: ruleId3, messageId: messageId3, } = {}, 70 | ] = results || []; 71 | 72 | assert.expect( 7 ); 73 | assert.strictEqual( results.length, 3, "only 3 errors" ); 74 | assert.strictEqual( ruleId1, "@getify/proper-arrows/return", "ruleId1" ); 75 | assert.strictEqual( messageId1, "noChainedArrow", "messageId1" ); 76 | assert.strictEqual( ruleId2, "@getify/proper-arrows/return", "ruleId2" ); 77 | assert.strictEqual( messageId2, "noConciseObject", "messageId2" ); 78 | assert.strictEqual( ruleId3, "@getify/proper-arrows/return", "ruleId3" ); 79 | assert.strictEqual( messageId3, "noSequence", "messageId3" ); 80 | } ); 81 | 82 | QUnit.test( "RETURN (object, default): conforming", function test(assert){ 83 | var code = ` 84 | var x = y => { return { y }; }; 85 | `; 86 | 87 | var results = eslinter.verify( code, linterOptions.returnObjectDefault ); 88 | 89 | assert.expect( 1 ); 90 | assert.strictEqual( results.length, 0, "no errors" ); 91 | } ); 92 | 93 | QUnit.test( "RETURN (object, default): violating", function test(assert){ 94 | var code = ` 95 | var x = y => ({ y }); 96 | `; 97 | 98 | var results = eslinter.verify( code, linterOptions.returnObjectDefault ); 99 | var [{ ruleId, messageId, } = {},] = results || []; 100 | 101 | assert.expect( 3 ); 102 | assert.strictEqual( results.length, 1, "only 1 error" ); 103 | assert.strictEqual( ruleId, "@getify/proper-arrows/return", "ruleId" ); 104 | assert.strictEqual( messageId, "noConciseObject", "messageId" ); 105 | } ); 106 | 107 | QUnit.test( "RETURN (ternary, default): conforming", function test(assert){ 108 | var code = ` 109 | var x = (y = a ? a : 1) => { return x ? x : y; }; 110 | var w = g ? g : 1; 111 | `; 112 | 113 | var results = eslinter.verify( code, linterOptions.returnTernaryDefault ); 114 | 115 | assert.expect( 1 ); 116 | assert.strictEqual( results.length, 0, "no errors" ); 117 | } ); 118 | 119 | QUnit.test( "RETURN (ternary, default): violating", function test(assert){ 120 | var code = ` 121 | var x = y => x ? x : y; 122 | `; 123 | 124 | var results = eslinter.verify( code, linterOptions.returnTernaryDefault ); 125 | var [{ ruleId, messageId, } = {},] = results || []; 126 | 127 | assert.expect( 3 ); 128 | assert.strictEqual( results.length, 1, "only 1 error" ); 129 | assert.strictEqual( ruleId, "@getify/proper-arrows/return", "ruleId" ); 130 | assert.strictEqual( messageId, "noTernary", "messageId" ); 131 | } ); 132 | 133 | QUnit.test( "RETURN (ternary:1): conforming", function test(assert){ 134 | var code = ` 135 | var x = y => x ? x : y; 136 | `; 137 | 138 | var results = eslinter.verify( code, linterOptions.returnTernary1 ); 139 | 140 | assert.expect( 1 ); 141 | assert.strictEqual( results.length, 0, "no errors" ); 142 | } ); 143 | 144 | QUnit.test( "RETURN (ternary:1): violating", function test(assert){ 145 | var code = ` 146 | var x = y => x ? x : y ? y : 42; 147 | `; 148 | 149 | var results = eslinter.verify( code, linterOptions.returnTernary1 ); 150 | var [{ ruleId, messageId, } = {},] = results || []; 151 | 152 | assert.expect( 3 ); 153 | assert.strictEqual( results.length, 1, "only 1 error" ); 154 | assert.strictEqual( ruleId, "@getify/proper-arrows/return", "ruleId" ); 155 | assert.strictEqual( messageId, "noTernary", "messageId" ); 156 | } ); 157 | 158 | QUnit.test( "RETURN (ternary:2): conforming", function test(assert){ 159 | var code = ` 160 | var x = y => x ? x : y ? y : 42; 161 | `; 162 | 163 | var results = eslinter.verify( code, linterOptions.returnTernary2 ); 164 | 165 | assert.expect( 1 ); 166 | assert.strictEqual( results.length, 0, "no errors" ); 167 | } ); 168 | 169 | QUnit.test( "RETURN (ternary:2): violating", function test(assert){ 170 | var code = ` 171 | var x = y => x ? x : y ? z ? z : 42 : null; 172 | `; 173 | 174 | var results = eslinter.verify( code, linterOptions.returnTernary2 ); 175 | var [{ ruleId, messageId, } = {},] = results || []; 176 | 177 | assert.expect( 3 ); 178 | assert.strictEqual( results.length, 1, "only 1 error" ); 179 | assert.strictEqual( ruleId, "@getify/proper-arrows/return", "ruleId" ); 180 | assert.strictEqual( messageId, "noTernary", "messageId" ); 181 | } ); 182 | 183 | QUnit.test( "RETURN (object): conforming", function test(assert){ 184 | var code = ` 185 | var x = y => { return { y }; }; 186 | `; 187 | 188 | var results = eslinter.verify( code, linterOptions.returnObject ); 189 | 190 | assert.expect( 1 ); 191 | assert.strictEqual( results.length, 0, "no errors" ); 192 | } ); 193 | 194 | QUnit.test( "RETURN (object): violating", function test(assert){ 195 | var code = ` 196 | var x = y => ({ y }); 197 | `; 198 | 199 | var results = eslinter.verify( code, linterOptions.returnObject ); 200 | var [{ ruleId, messageId, } = {},] = results || []; 201 | 202 | assert.expect( 3 ); 203 | assert.strictEqual( results.length, 1, "only 1 error" ); 204 | assert.strictEqual( ruleId, "@getify/proper-arrows/return", "ruleId" ); 205 | assert.strictEqual( messageId, "noConciseObject", "messageId" ); 206 | } ); 207 | 208 | QUnit.test( "RETURN (chained, default): conforming", function test(assert){ 209 | var code = ` 210 | var f; 211 | f = x => (y => x + y); 212 | f = x => { return y => x + y; }; 213 | f = x => (y => ({x,y})); 214 | f = x => ((y) => ({x,y})); 215 | f = x => (y => (z => x + y + z)); 216 | `; 217 | 218 | var results = eslinter.verify( code, linterOptions.returnChained ); 219 | 220 | assert.expect( 1 ); 221 | assert.strictEqual( results.length, 0, "no errors" ); 222 | } ); 223 | 224 | QUnit.test( "RETURN (chained, default): violating", function test(assert){ 225 | var code = ` 226 | var f; 227 | f = x => y => x + y; 228 | f = x => y => ({x,y}); 229 | f = x => (y) => ({x,y}); 230 | f = x => (y) => (y); 231 | f = x => y => (z => x + y + z); 232 | f = x => y => z => x + y + z; 233 | `; 234 | 235 | var results = eslinter.verify( code, linterOptions.returnChained ); 236 | var [ 237 | { ruleId: ruleId1, messageId: messageId1, } = {}, 238 | { ruleId: ruleId2, messageId: messageId2, } = {}, 239 | { ruleId: ruleId3, messageId: messageId3, } = {}, 240 | { ruleId: ruleId4, messageId: messageId4, } = {}, 241 | { ruleId: ruleId5, messageId: messageId5, } = {}, 242 | { ruleId: ruleId6, messageId: messageId6, } = {}, 243 | { ruleId: ruleId7, messageId: messageId7, } = {}, 244 | ] = results || []; 245 | 246 | assert.expect( 15 ); 247 | assert.strictEqual( results.length, 7, "only 7 errors" ); 248 | assert.strictEqual( ruleId1, "@getify/proper-arrows/return", "ruleId1" ); 249 | assert.strictEqual( messageId1, "noChainedArrow", "messageId1" ); 250 | assert.strictEqual( ruleId2, "@getify/proper-arrows/return", "ruleId2" ); 251 | assert.strictEqual( messageId2, "noChainedArrow", "messageId2" ); 252 | assert.strictEqual( ruleId3, "@getify/proper-arrows/return", "ruleId3" ); 253 | assert.strictEqual( messageId3, "noChainedArrow", "messageId3" ); 254 | assert.strictEqual( ruleId4, "@getify/proper-arrows/return", "ruleId4" ); 255 | assert.strictEqual( messageId4, "noChainedArrow", "messageId4" ); 256 | assert.strictEqual( ruleId5, "@getify/proper-arrows/return", "ruleId5" ); 257 | assert.strictEqual( messageId5, "noChainedArrow", "messageId5" ); 258 | assert.strictEqual( ruleId6, "@getify/proper-arrows/return", "ruleId6" ); 259 | assert.strictEqual( messageId6, "noChainedArrow", "messageId6" ); 260 | assert.strictEqual( ruleId7, "@getify/proper-arrows/return", "ruleId7" ); 261 | assert.strictEqual( messageId7, "noChainedArrow", "messageId7" ); 262 | } ); 263 | 264 | QUnit.test( "RETURN (sequence, default): conforming", function test(assert){ 265 | var code = ` 266 | var x = y => { return (1,y,2); }; 267 | `; 268 | 269 | var results = eslinter.verify( code, linterOptions.returnSequenceDefault ); 270 | 271 | assert.expect( 1 ); 272 | assert.strictEqual( results.length, 0, "no errors" ); 273 | } ); 274 | 275 | QUnit.test( "RETURN (sequence, default): violating", function test(assert){ 276 | var code = ` 277 | var x = y => (1,y,2); 278 | `; 279 | 280 | var results = eslinter.verify( code, linterOptions.returnSequenceDefault ); 281 | var [{ ruleId, messageId, } = {},] = results || []; 282 | 283 | assert.expect( 3 ); 284 | assert.strictEqual( results.length, 1, "only 1 error" ); 285 | assert.strictEqual( ruleId, "@getify/proper-arrows/return", "ruleId" ); 286 | assert.strictEqual( messageId, "noSequence", "messageId" ); 287 | } ); 288 | 289 | QUnit.test( "RETURN (sequence): conforming", function test(assert){ 290 | var code = ` 291 | var x = y => { return (1,y,2); }; 292 | `; 293 | 294 | var results = eslinter.verify( code, linterOptions.returnSequence ); 295 | 296 | assert.expect( 1 ); 297 | assert.strictEqual( results.length, 0, "no errors" ); 298 | } ); 299 | 300 | QUnit.test( "RETURN (sequence): violating", function test(assert){ 301 | var code = ` 302 | var x = y => (1,y,2); 303 | `; 304 | 305 | var results = eslinter.verify( code, linterOptions.returnSequence ); 306 | var [{ ruleId, messageId, } = {},] = results || []; 307 | 308 | assert.expect( 3 ); 309 | assert.strictEqual( results.length, 1, "only 1 error" ); 310 | assert.strictEqual( ruleId, "@getify/proper-arrows/return", "ruleId" ); 311 | assert.strictEqual( messageId, "noSequence", "messageId" ); 312 | } ); 313 | -------------------------------------------------------------------------------- /tests/tests.this.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var linterOptions = { 4 | thisDefault: { 5 | parserOptions: { ecmaVersion: 2015, }, 6 | rules: { "@getify/proper-arrows/this": "error", }, 7 | }, 8 | thisAlways: { 9 | parserOptions: { ecmaVersion: 2015, }, 10 | rules: { "@getify/proper-arrows/this": ["error","always",{trivial:true,},], }, 11 | }, 12 | thisNested: { 13 | parserOptions: { ecmaVersion: 2015, }, 14 | rules: { "@getify/proper-arrows/this": ["error","nested",{trivial:true,},], }, 15 | }, 16 | thisAlwaysNoGlobal: { 17 | parserOptions: { ecmaVersion: 2015, }, 18 | rules: { "@getify/proper-arrows/this": ["error","always",{"no-global": true,trivial:true,},], }, 19 | }, 20 | thisAlwaysNoGlobalNodeCommonJS: { 21 | parserOptions: { ecmaVersion: 2015, ecmaFeatures: { globalReturn: true, }, }, 22 | rules: { "@getify/proper-arrows/this": ["error","always",{"no-global": true,trivial:true,},], }, 23 | }, 24 | thisNestedNoGlobal: { 25 | parserOptions: { ecmaVersion: 2015, }, 26 | rules: { "@getify/proper-arrows/this": ["error","nested",{"no-global": true,trivial:true,},], }, 27 | }, 28 | thisNever: { 29 | parserOptions: { ecmaVersion: 2015, }, 30 | rules: { "@getify/proper-arrows/this": ["error","never",], }, 31 | }, 32 | thisNeverGlobal: { 33 | parserOptions: { ecmaVersion: 2015, }, 34 | rules: { "@getify/proper-arrows/this": ["error","never-global",], }, 35 | }, 36 | }; 37 | 38 | QUnit.test( "THIS (always): one arrow, this", function test(assert){ 39 | var code = ` 40 | var x = y => this.foo(y); 41 | `; 42 | 43 | var results = eslinter.verify( code, linterOptions.thisAlways ); 44 | 45 | assert.expect( 1 ); 46 | assert.strictEqual( results.length, 0, "no errors" ); 47 | } ); 48 | 49 | QUnit.test( "THIS (always): two nested arrows, both this", function test(assert){ 50 | var code = ` 51 | var x = y => this.foo(z => this.bar(z)); 52 | `; 53 | 54 | var results = eslinter.verify( code, linterOptions.thisAlways ); 55 | 56 | assert.expect( 1 ); 57 | assert.strictEqual( results.length, 0, "no errors" ); 58 | } ); 59 | 60 | QUnit.test( "THIS (always): one arrow with param arrow, both this", function test(assert){ 61 | var code = ` 62 | var x = (y = z => this.foo(z)) => this.bar(w); 63 | `; 64 | 65 | var results = eslinter.verify( code, linterOptions.thisAlways ); 66 | 67 | assert.expect( 1 ); 68 | assert.strictEqual( results.length, 0, "no errors" ); 69 | } ); 70 | 71 | QUnit.test( "THIS (always): two separate arrows, both this", function test(assert){ 72 | var code = ` 73 | var x = y => this.foo(y); 74 | var z = w => this.bar(w); 75 | `; 76 | 77 | var results = eslinter.verify( code, linterOptions.thisAlways ); 78 | 79 | assert.expect( 1 ); 80 | assert.strictEqual( results.length, 0, "no errors" ); 81 | } ); 82 | 83 | QUnit.test( "THIS (always): simple arrow, no this", function test(assert){ 84 | var code = ` 85 | var x = y => y; 86 | `; 87 | 88 | var results = eslinter.verify( code, linterOptions.thisAlways ); 89 | var [{ ruleId, messageId, } = {},] = results || []; 90 | 91 | assert.expect( 3 ); 92 | assert.strictEqual( results.length, 1, "only 1 error" ); 93 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 94 | assert.strictEqual( messageId, "noThis", "messageId" ); 95 | } ); 96 | 97 | QUnit.test( "THIS (always): two separate arrows, no this", function test(assert){ 98 | var code = ` 99 | var x = y => foo(y); 100 | var z = w => bar(w); 101 | `; 102 | 103 | var results = eslinter.verify( code, linterOptions.thisAlways ); 104 | var [ 105 | { ruleId: ruleId1, messageId: messageId1, } = {}, 106 | { ruleId: ruleId2, messageId: messageId2, } = {}, 107 | ] = results || []; 108 | 109 | assert.expect( 5 ); 110 | assert.strictEqual( results.length, 2, "only 2 errors" ); 111 | assert.strictEqual( ruleId1, "@getify/proper-arrows/this", "ruleId1" ); 112 | assert.strictEqual( messageId1, "noThis", "messageId1" ); 113 | assert.strictEqual( ruleId2, "@getify/proper-arrows/this", "ruleId2" ); 114 | assert.strictEqual( messageId2, "noThis", "messageId2" ); 115 | } ); 116 | 117 | QUnit.test( "THIS (always): two nested arrows, one this nested", function test(assert){ 118 | var code = ` 119 | var x = y => z => this.foo(z); 120 | `; 121 | 122 | var results = eslinter.verify( code, linterOptions.thisAlways ); 123 | var [{ ruleId, messageId, } = {},] = results || []; 124 | 125 | assert.expect( 3 ); 126 | assert.strictEqual( results.length, 1, "only 1 error" ); 127 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 128 | assert.strictEqual( messageId, "noThis", "messageId" ); 129 | } ); 130 | 131 | QUnit.test( "THIS (always): two nested arrows, one this not-nested", function test(assert){ 132 | var code = ` 133 | var x = y => this.foo(z => z); 134 | `; 135 | 136 | var results = eslinter.verify( code, linterOptions.thisAlways ); 137 | var [{ ruleId, messageId, } = {},] = results || []; 138 | 139 | assert.expect( 3 ); 140 | assert.strictEqual( results.length, 1, "only 1 error" ); 141 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 142 | assert.strictEqual( messageId, "noThis", "messageId" ); 143 | } ); 144 | 145 | QUnit.test( "THIS (always): two nested arrows, no this", function test(assert){ 146 | var code = ` 147 | var x = y => z => z; 148 | `; 149 | 150 | var results = eslinter.verify( code, linterOptions.thisAlways ); 151 | var [ 152 | { ruleId: ruleId1, messageId: messageId1, } = {}, 153 | { ruleId: ruleId2, messageId: messageId2, } = {}, 154 | ] = results || []; 155 | 156 | assert.expect( 5 ); 157 | assert.strictEqual( results.length, 2, "only 2 errors" ); 158 | assert.strictEqual( ruleId1, "@getify/proper-arrows/this", "ruleId1" ); 159 | assert.strictEqual( messageId1, "noThis", "messageId1" ); 160 | assert.strictEqual( ruleId2, "@getify/proper-arrows/this", "ruleId2" ); 161 | assert.strictEqual( messageId2, "noThis", "messageId2" ); 162 | } ); 163 | 164 | QUnit.test( "THIS (always): one arrow with param arrow, nested this", function test(assert){ 165 | var code = ` 166 | var x = (y = z => foo(z)) => this.bar(w); 167 | `; 168 | 169 | var results = eslinter.verify( code, linterOptions.thisAlways ); 170 | var [{ ruleId, messageId, } = {},] = results || []; 171 | 172 | assert.expect( 3 ); 173 | assert.strictEqual( results.length, 1, "only 1 error" ); 174 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 175 | assert.strictEqual( messageId, "noThis", "messageId" ); 176 | } ); 177 | 178 | QUnit.test( "THIS (always): one arrow with param arrow, param this", function test(assert){ 179 | var code = ` 180 | var x = (y = z => this.foo(z)) => bar(w); 181 | `; 182 | 183 | var results = eslinter.verify( code, linterOptions.thisAlways ); 184 | var [{ ruleId, messageId, } = {},] = results || []; 185 | 186 | assert.expect( 3 ); 187 | assert.strictEqual( results.length, 1, "only 1 error" ); 188 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 189 | assert.strictEqual( messageId, "noThis", "messageId" ); 190 | } ); 191 | 192 | QUnit.test( "THIS (always): one non-arrow and one arrow, nested this", function test(assert){ 193 | var code = ` 194 | var x = function(){ return y => this.foo(y); }; 195 | `; 196 | 197 | var results = eslinter.verify( code, linterOptions.thisAlways ); 198 | 199 | assert.expect( 1 ); 200 | assert.strictEqual( results.length, 0, "no errors" ); 201 | } ); 202 | 203 | // ********************************************** 204 | 205 | QUnit.test( "THIS (always + no-global): inner arrow, this", function test(assert){ 206 | var code = ` 207 | function x() { return y => this.foo(y); } 208 | `; 209 | 210 | var results = eslinter.verify( code, linterOptions.thisAlwaysNoGlobal ); 211 | 212 | assert.expect( 1 ); 213 | assert.strictEqual( results.length, 0, "no errors" ); 214 | } ); 215 | 216 | QUnit.test( "THIS (always + no-global): parameter arrow, this", function test(assert){ 217 | var code = ` 218 | function x(z = y => this.foo(y)) { } 219 | `; 220 | 221 | var results = eslinter.verify( code, linterOptions.thisAlwaysNoGlobal ); 222 | 223 | assert.expect( 1 ); 224 | assert.strictEqual( results.length, 0, "no errors" ); 225 | } ); 226 | 227 | QUnit.test( "THIS (always + no-global): outer arrow, this", function test(assert){ 228 | var code = ` 229 | var x = y => this.foo(y); 230 | `; 231 | 232 | var results = eslinter.verify( code, linterOptions.thisAlwaysNoGlobal ); 233 | var [{ ruleId, messageId, } = {},] = results || []; 234 | 235 | assert.expect( 3 ); 236 | assert.strictEqual( results.length, 1, "only 1 error" ); 237 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 238 | assert.strictEqual( messageId, "noGlobal", "messageId" ); 239 | } ); 240 | 241 | QUnit.test( "THIS (always + no-global): property arrow, this", function test(assert){ 242 | var code = ` 243 | var o = { x: y => this.foo(y) }; 244 | `; 245 | 246 | var results = eslinter.verify( code, linterOptions.thisAlwaysNoGlobal ); 247 | var [{ ruleId, messageId, } = {},] = results || []; 248 | 249 | assert.expect( 3 ); 250 | assert.strictEqual( results.length, 1, "only 1 error" ); 251 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 252 | assert.strictEqual( messageId, "noGlobal", "messageId" ); 253 | } ); 254 | 255 | QUnit.test( "THIS (always + no-global + node/commonjs): outer arrow, this", function test(assert){ 256 | var code = ` 257 | var x = y => this.foo(y); 258 | `; 259 | 260 | var results = eslinter.verify( code, linterOptions.thisAlwaysNoGlobalNodeCommonJS ); 261 | var [{ ruleId, messageId, } = {},] = results || []; 262 | 263 | assert.expect( 3 ); 264 | assert.strictEqual( results.length, 1, "only 1 error" ); 265 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 266 | assert.strictEqual( messageId, "noGlobal", "messageId" ); 267 | } ); 268 | 269 | 270 | // ********************************************** 271 | 272 | 273 | QUnit.test( "THIS (nested): one arrow, this", function test(assert){ 274 | var code = ` 275 | var x = y => this.foo(y); 276 | `; 277 | 278 | var results = eslinter.verify( code, linterOptions.thisNested ); 279 | 280 | assert.expect( 1 ); 281 | assert.strictEqual( results.length, 0, "no errors" ); 282 | } ); 283 | 284 | QUnit.test( "THIS (nested): two nested arrows, both this", function test(assert){ 285 | var code = ` 286 | var x = y => this.foo(z => this.bar(z)); 287 | `; 288 | 289 | var results = eslinter.verify( code, linterOptions.thisNested ); 290 | 291 | assert.expect( 1 ); 292 | assert.strictEqual( results.length, 0, "no errors" ); 293 | } ); 294 | 295 | QUnit.test( "THIS (nested): one arrow with param arrow, both this", function test(assert){ 296 | var code = ` 297 | var x = (y = z => this.foo(z)) => this.bar(w); 298 | `; 299 | 300 | var results = eslinter.verify( code, linterOptions.thisNested ); 301 | 302 | assert.expect( 1 ); 303 | assert.strictEqual( results.length, 0, "no errors" ); 304 | } ); 305 | 306 | QUnit.test( "THIS (nested): two separate arrows, both this", function test(assert){ 307 | var code = ` 308 | var x = y => this.foo(y); 309 | var z = w => this.bar(w); 310 | `; 311 | 312 | var results = eslinter.verify( code, linterOptions.thisNested ); 313 | 314 | assert.expect( 1 ); 315 | assert.strictEqual( results.length, 0, "no errors" ); 316 | } ); 317 | 318 | QUnit.test( "THIS (nested): simple arrow, no this", function test(assert){ 319 | var code = ` 320 | var x = y => y; 321 | `; 322 | 323 | var results = eslinter.verify( code, linterOptions.thisNested ); 324 | var [{ ruleId, messageId, } = {},] = results || []; 325 | 326 | assert.expect( 3 ); 327 | assert.strictEqual( results.length, 1, "only 1 error" ); 328 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 329 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 330 | } ); 331 | 332 | QUnit.test( "THIS (nested): two separate arrows, no this", function test(assert){ 333 | var code = ` 334 | var x = y => foo(y); 335 | var z = w => bar(w); 336 | `; 337 | 338 | var results = eslinter.verify( code, linterOptions.thisNested ); 339 | var [ 340 | { ruleId: ruleId1, messageId: messageId1, } = {}, 341 | { ruleId: ruleId2, messageId: messageId2, } = {}, 342 | ] = results || []; 343 | 344 | assert.expect( 5 ); 345 | assert.strictEqual( results.length, 2, "only 2 errors" ); 346 | assert.strictEqual( ruleId1, "@getify/proper-arrows/this", "ruleId1" ); 347 | assert.strictEqual( messageId1, "noThisNested", "messageId1" ); 348 | assert.strictEqual( ruleId2, "@getify/proper-arrows/this", "ruleId2" ); 349 | assert.strictEqual( messageId2, "noThisNested", "messageId2" ); 350 | } ); 351 | 352 | QUnit.test( "THIS (nested): two nested arrows, one this nested", function test(assert){ 353 | var code = ` 354 | var x = y => z => this.foo(z); 355 | `; 356 | 357 | var results = eslinter.verify( code, linterOptions.thisNested ); 358 | 359 | assert.expect( 1 ); 360 | assert.strictEqual( results.length, 0, "no errors" ); 361 | } ); 362 | 363 | QUnit.test( "THIS (nested): two nested arrows, one this not-nested", function test(assert){ 364 | var code = ` 365 | var x = y => this.foo(z => z); 366 | `; 367 | 368 | var results = eslinter.verify( code, linterOptions.thisNested ); 369 | var [{ ruleId, messageId, } = {},] = results || []; 370 | 371 | assert.expect( 3 ); 372 | assert.strictEqual( results.length, 1, "only 1 error" ); 373 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 374 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 375 | } ); 376 | 377 | QUnit.test( "THIS (nested): two nested arrows, no this", function test(assert){ 378 | var code = ` 379 | var x = y => z => z; 380 | `; 381 | 382 | var results = eslinter.verify( code, linterOptions.thisNested ); 383 | var [ 384 | { ruleId: ruleId1, messageId: messageId1, } = {}, 385 | { ruleId: ruleId2, messageId: messageId2, } = {}, 386 | ] = results || []; 387 | 388 | assert.expect( 5 ); 389 | assert.strictEqual( results.length, 2, "only 2 errors" ); 390 | assert.strictEqual( ruleId1, "@getify/proper-arrows/this", "ruleId1" ); 391 | assert.strictEqual( messageId1, "noThisNested", "messageId1" ); 392 | assert.strictEqual( ruleId2, "@getify/proper-arrows/this", "ruleId2" ); 393 | assert.strictEqual( messageId2, "noThisNested", "messageId2" ); 394 | } ); 395 | 396 | QUnit.test( "THIS (nested): one arrow with param arrow, nested this", function test(assert){ 397 | var code = ` 398 | var x = (y = z => foo(z)) => this.bar(w); 399 | `; 400 | 401 | var results = eslinter.verify( code, linterOptions.thisNested ); 402 | var [{ ruleId, messageId, } = {},] = results || []; 403 | 404 | assert.expect( 3 ); 405 | assert.strictEqual( results.length, 1, "only 1 error" ); 406 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 407 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 408 | } ); 409 | 410 | QUnit.test( "THIS (nested): one arrow with param arrow, param this", function test(assert){ 411 | var code = ` 412 | var x = (y = z => this.foo(z)) => bar(w); 413 | `; 414 | 415 | var results = eslinter.verify( code, linterOptions.thisNested ); 416 | 417 | assert.expect( 1 ); 418 | assert.strictEqual( results.length, 0, "no errors" ); 419 | } ); 420 | 421 | QUnit.test( "THIS (nested): two arrows with non-arrow between, both this", function test(assert){ 422 | var code = ` 423 | var x = y => this.foo(function(){ return z => this.bar(z); }); 424 | `; 425 | 426 | var results = eslinter.verify( code, linterOptions.thisNested ); 427 | 428 | assert.expect( 1 ); 429 | assert.strictEqual( results.length, 0, "no errors" ); 430 | } ); 431 | 432 | QUnit.test( "THIS (nested): two arrows with non-arrow between, nested this", function test(assert){ 433 | var code = ` 434 | var x = y => foo(function(){ return z => this.bar(z); }); 435 | `; 436 | 437 | var results = eslinter.verify( code, linterOptions.thisNested ); 438 | var [{ ruleId, messageId, } = {},] = results || []; 439 | 440 | assert.expect( 3 ); 441 | assert.strictEqual( results.length, 1, "only 1 error" ); 442 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 443 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 444 | } ); 445 | 446 | QUnit.test( "THIS (nested): two arrows with method between, nested this", function test(assert){ 447 | var code = ` 448 | var x = y => foo({ method(){ return z => this.bar(z); } }); 449 | `; 450 | 451 | var results = eslinter.verify( code, linterOptions.thisNested ); 452 | var [{ ruleId, messageId, } = {},] = results || []; 453 | 454 | assert.expect( 3 ); 455 | assert.strictEqual( results.length, 1, "only 1 error" ); 456 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 457 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 458 | } ); 459 | 460 | QUnit.test( "THIS (nested): two arrows with getter between, nested this", function test(assert){ 461 | var code = ` 462 | var x = y => foo({ get baz(){ return z => this.bar(z); } }); 463 | `; 464 | 465 | var results = eslinter.verify( code, linterOptions.thisNested ); 466 | var [{ ruleId, messageId, } = {},] = results || []; 467 | 468 | assert.expect( 3 ); 469 | assert.strictEqual( results.length, 1, "only 1 error" ); 470 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 471 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 472 | } ); 473 | 474 | QUnit.test( "THIS (nested): two arrows with setter between, nested this", function test(assert){ 475 | var code = ` 476 | var x = y => foo({ set baz(v){ return z => this.bar(z); } }); 477 | `; 478 | 479 | var results = eslinter.verify( code, linterOptions.thisNested ); 480 | var [{ ruleId, messageId, } = {},] = results || []; 481 | 482 | assert.expect( 3 ); 483 | assert.strictEqual( results.length, 1, "only 1 error" ); 484 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 485 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 486 | } ); 487 | 488 | QUnit.test( "THIS (nested): one arrow and non-arrow with arrow param, param this", function test(assert){ 489 | var code = ` 490 | var x = y => foo(function(z = w => this.bar(w)){ return bar(z); }); 491 | `; 492 | 493 | var results = eslinter.verify( code, linterOptions.thisNested ); 494 | var [{ ruleId, messageId, } = {},] = results || []; 495 | 496 | assert.expect( 3 ); 497 | assert.strictEqual( results.length, 1, "only 1 error" ); 498 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 499 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 500 | } ); 501 | 502 | QUnit.test( "THIS (nested): two arrows with non-arrow between, not-nested this", function test(assert){ 503 | var code = ` 504 | var x = y => this.foo(function(){ return z => bar(z); }); 505 | `; 506 | 507 | var results = eslinter.verify( code, linterOptions.thisNested ); 508 | var [{ ruleId, messageId, } = {},] = results || []; 509 | 510 | assert.expect( 3 ); 511 | assert.strictEqual( results.length, 1, "only 1 error" ); 512 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 513 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 514 | } ); 515 | 516 | QUnit.test( "THIS (nested): two arrows with non-arrow between, no this", function test(assert){ 517 | var code = ` 518 | var x = y => foo(function(){ return z => bar(z); }); 519 | `; 520 | 521 | var results = eslinter.verify( code, linterOptions.thisNested ); 522 | var [ 523 | { ruleId: ruleId1, messageId: messageId1, } = {}, 524 | { ruleId: ruleId2, messageId: messageId2, } = {}, 525 | ] = results || []; 526 | 527 | assert.expect( 5 ); 528 | assert.strictEqual( results.length, 2, "only 2 errors" ); 529 | assert.strictEqual( ruleId1, "@getify/proper-arrows/this", "ruleId1" ); 530 | assert.strictEqual( messageId1, "noThisNested", "messageId1" ); 531 | assert.strictEqual( ruleId2, "@getify/proper-arrows/this", "ruleId2" ); 532 | assert.strictEqual( messageId2, "noThisNested", "messageId2" ); 533 | } ); 534 | 535 | QUnit.test( "THIS (nested): one arrow and one non-arrow, both this", function test(assert){ 536 | var code = ` 537 | var x = y => this.foo(function(){ return this.bar(z); }); 538 | `; 539 | 540 | var results = eslinter.verify( code, linterOptions.thisNested ); 541 | 542 | assert.expect( 1 ); 543 | assert.strictEqual( results.length, 0, "no errors" ); 544 | } ); 545 | 546 | QUnit.test( "THIS (nested): one arrow and one non-arrow, nested this", function test(assert){ 547 | var code = ` 548 | var x = y => foo(function(){ return this.bar(z); }); 549 | `; 550 | 551 | var results = eslinter.verify( code, linterOptions.thisNested ); 552 | var [{ ruleId, messageId, } = {},] = results || []; 553 | 554 | assert.expect( 3 ); 555 | assert.strictEqual( results.length, 1, "only 1 error" ); 556 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 557 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 558 | } ); 559 | 560 | 561 | // ********************************************** 562 | 563 | 564 | QUnit.test( "THIS (nested + no-global): outer arrow, this", function test(assert){ 565 | var code = ` 566 | var x = y => z => this.foo(z); 567 | `; 568 | 569 | var results = eslinter.verify( code, linterOptions.thisNestedNoGlobal ); 570 | var [{ ruleId, messageId, } = {},] = results || []; 571 | 572 | assert.expect( 3 ); 573 | assert.strictEqual( results.length, 1, "only 1 error" ); 574 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 575 | assert.strictEqual( messageId, "noGlobal", "messageId" ); 576 | } ); 577 | 578 | QUnit.test( "THIS (nested + no-global): property arrow, this", function test(assert){ 579 | var code = ` 580 | var o = { x: y => z => this.foo(z) }; 581 | `; 582 | 583 | var results = eslinter.verify( code, linterOptions.thisNestedNoGlobal ); 584 | var [{ ruleId, messageId, } = {},] = results || []; 585 | 586 | assert.expect( 3 ); 587 | assert.strictEqual( results.length, 1, "only 1 error" ); 588 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 589 | assert.strictEqual( messageId, "noGlobal", "messageId" ); 590 | } ); 591 | 592 | 593 | // ********************************************** 594 | 595 | 596 | QUnit.test( "THIS (default: nested): one arrow, this", function test(assert){ 597 | var code = ` 598 | var x = y => this.foo(y); 599 | `; 600 | 601 | var results = eslinter.verify( code, linterOptions.thisDefault ); 602 | 603 | assert.expect( 1 ); 604 | assert.strictEqual( results.length, 0, "no errors" ); 605 | } ); 606 | 607 | QUnit.test( "THIS (default: nested): two nested arrows, both this", function test(assert){ 608 | var code = ` 609 | var x = y => this.foo(z => this.bar(z)); 610 | `; 611 | 612 | var results = eslinter.verify( code, linterOptions.thisDefault ); 613 | 614 | assert.expect( 1 ); 615 | assert.strictEqual( results.length, 0, "no errors" ); 616 | } ); 617 | 618 | QUnit.test( "THIS (default: nested): one arrow with param arrow, both this", function test(assert){ 619 | var code = ` 620 | var x = (y = z => this.foo(z)) => this.bar(w); 621 | `; 622 | 623 | var results = eslinter.verify( code, linterOptions.thisDefault ); 624 | 625 | assert.expect( 1 ); 626 | assert.strictEqual( results.length, 0, "no errors" ); 627 | } ); 628 | 629 | QUnit.test( "THIS (default: nested): two separate arrows, both this", function test(assert){ 630 | var code = ` 631 | var x = y => this.foo(y); 632 | var z = w => this.bar(w); 633 | `; 634 | 635 | var results = eslinter.verify( code, linterOptions.thisDefault ); 636 | 637 | assert.expect( 1 ); 638 | assert.strictEqual( results.length, 0, "no errors" ); 639 | } ); 640 | 641 | QUnit.test( "THIS (default: nested): simple arrow, no this", function test(assert){ 642 | var code = ` 643 | var x = y => y + 1; 644 | `; 645 | 646 | var results = eslinter.verify( code, linterOptions.thisDefault ); 647 | var [{ ruleId, messageId, } = {},] = results || []; 648 | 649 | assert.expect( 3 ); 650 | assert.strictEqual( results.length, 1, "only 1 error" ); 651 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 652 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 653 | } ); 654 | 655 | QUnit.test( "THIS (default: nested): two separate arrows, no this", function test(assert){ 656 | var code = ` 657 | var x = y => foo(y); 658 | var z = w => bar(w); 659 | `; 660 | 661 | var results = eslinter.verify( code, linterOptions.thisDefault ); 662 | var [ 663 | { ruleId: ruleId1, messageId: messageId1, } = {}, 664 | { ruleId: ruleId2, messageId: messageId2, } = {}, 665 | ] = results || []; 666 | 667 | assert.expect( 5 ); 668 | assert.strictEqual( results.length, 2, "only 2 errors" ); 669 | assert.strictEqual( ruleId1, "@getify/proper-arrows/this", "ruleId1" ); 670 | assert.strictEqual( messageId1, "noThisNested", "messageId1" ); 671 | assert.strictEqual( ruleId2, "@getify/proper-arrows/this", "ruleId2" ); 672 | assert.strictEqual( messageId2, "noThisNested", "messageId2" ); 673 | } ); 674 | 675 | QUnit.test( "THIS (default: nested): two nested arrows, one this nested", function test(assert){ 676 | var code = ` 677 | var x = y => z => this.foo(z); 678 | `; 679 | 680 | var results = eslinter.verify( code, linterOptions.thisDefault ); 681 | 682 | assert.expect( 1 ); 683 | assert.strictEqual( results.length, 0, "no errors" ); 684 | } ); 685 | 686 | QUnit.test( "THIS (default: nested): two nested arrows, one this not-nested", function test(assert){ 687 | var code = ` 688 | var x = y => this.foo(z => z + 1); 689 | `; 690 | 691 | var results = eslinter.verify( code, linterOptions.thisDefault ); 692 | var [{ ruleId, messageId, } = {},] = results || []; 693 | 694 | assert.expect( 3 ); 695 | assert.strictEqual( results.length, 1, "only 1 error" ); 696 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 697 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 698 | } ); 699 | 700 | QUnit.test( "THIS (default: nested): two nested arrows, no this", function test(assert){ 701 | var code = ` 702 | var x = y => z => z + 1; 703 | `; 704 | 705 | var results = eslinter.verify( code, linterOptions.thisDefault ); 706 | var [ 707 | { ruleId: ruleId1, messageId: messageId1, } = {}, 708 | { ruleId: ruleId2, messageId: messageId2, } = {}, 709 | ] = results || []; 710 | 711 | assert.expect( 5 ); 712 | assert.strictEqual( results.length, 2, "only 2 errors" ); 713 | assert.strictEqual( ruleId1, "@getify/proper-arrows/this", "ruleId1" ); 714 | assert.strictEqual( messageId1, "noThisNested", "messageId1" ); 715 | assert.strictEqual( ruleId2, "@getify/proper-arrows/this", "ruleId2" ); 716 | assert.strictEqual( messageId2, "noThisNested", "messageId2" ); 717 | } ); 718 | 719 | QUnit.test( "THIS (default: nested): one arrow with param arrow, nested this", function test(assert){ 720 | var code = ` 721 | var x = (y = z => foo(z)) => this.bar(w); 722 | `; 723 | 724 | var results = eslinter.verify( code, linterOptions.thisDefault ); 725 | var [{ ruleId, messageId, } = {},] = results || []; 726 | 727 | assert.expect( 3 ); 728 | assert.strictEqual( results.length, 1, "only 1 error" ); 729 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 730 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 731 | } ); 732 | 733 | QUnit.test( "THIS (default: nested): one arrow with param arrow, param this", function test(assert){ 734 | var code = ` 735 | var x = (y = z => this.foo(z)) => bar(w); 736 | `; 737 | 738 | var results = eslinter.verify( code, linterOptions.thisDefault ); 739 | 740 | assert.expect( 1 ); 741 | assert.strictEqual( results.length, 0, "no errors" ); 742 | } ); 743 | 744 | QUnit.test( "THIS (default: nested): two arrows with non-arrow between, both this", function test(assert){ 745 | var code = ` 746 | var x = y => this.foo(function(){ return z => this.bar(z); }); 747 | `; 748 | 749 | var results = eslinter.verify( code, linterOptions.thisDefault ); 750 | 751 | assert.expect( 1 ); 752 | assert.strictEqual( results.length, 0, "no errors" ); 753 | } ); 754 | 755 | QUnit.test( "THIS (default: nested): two arrows with non-arrow between, nested this", function test(assert){ 756 | var code = ` 757 | var x = y => foo(function(){ return z => this.bar(z); }); 758 | `; 759 | 760 | var results = eslinter.verify( code, linterOptions.thisDefault ); 761 | var [{ ruleId, messageId, } = {},] = results || []; 762 | 763 | assert.expect( 3 ); 764 | assert.strictEqual( results.length, 1, "only 1 error" ); 765 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 766 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 767 | } ); 768 | 769 | QUnit.test( "THIS (default: nested): two arrows with method between, nested this", function test(assert){ 770 | var code = ` 771 | var x = y => foo({ method(){ return z => this.bar(z); } }); 772 | `; 773 | 774 | var results = eslinter.verify( code, linterOptions.thisDefault ); 775 | var [{ ruleId, messageId, } = {},] = results || []; 776 | 777 | assert.expect( 3 ); 778 | assert.strictEqual( results.length, 1, "only 1 error" ); 779 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 780 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 781 | } ); 782 | 783 | QUnit.test( "THIS (default: nested): two arrows with getter between, nested this", function test(assert){ 784 | var code = ` 785 | var x = y => foo({ get baz(){ return z => this.bar(z); } }); 786 | `; 787 | 788 | var results = eslinter.verify( code, linterOptions.thisDefault ); 789 | var [{ ruleId, messageId, } = {},] = results || []; 790 | 791 | assert.expect( 3 ); 792 | assert.strictEqual( results.length, 1, "only 1 error" ); 793 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 794 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 795 | } ); 796 | 797 | QUnit.test( "THIS (default: nested): two arrows with setter between, nested this", function test(assert){ 798 | var code = ` 799 | var x = y => foo({ set baz(v){ return z => this.bar(z); } }); 800 | `; 801 | 802 | var results = eslinter.verify( code, linterOptions.thisDefault ); 803 | var [{ ruleId, messageId, } = {},] = results || []; 804 | 805 | assert.expect( 3 ); 806 | assert.strictEqual( results.length, 1, "only 1 error" ); 807 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 808 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 809 | } ); 810 | 811 | QUnit.test( "THIS (default: nested): one arrow and non-arrow with arrow param, param this", function test(assert){ 812 | var code = ` 813 | var x = y => foo(function(z = w => this.bar(w)){ return bar(z); }); 814 | `; 815 | 816 | var results = eslinter.verify( code, linterOptions.thisDefault ); 817 | var [{ ruleId, messageId, } = {},] = results || []; 818 | 819 | assert.expect( 3 ); 820 | assert.strictEqual( results.length, 1, "only 1 error" ); 821 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 822 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 823 | } ); 824 | 825 | QUnit.test( "THIS (default: nested): two arrows with non-arrow between, not-nested this", function test(assert){ 826 | var code = ` 827 | var x = y => this.foo(function(){ return z => bar(z); }); 828 | `; 829 | 830 | var results = eslinter.verify( code, linterOptions.thisDefault ); 831 | var [{ ruleId, messageId, } = {},] = results || []; 832 | 833 | assert.expect( 3 ); 834 | assert.strictEqual( results.length, 1, "only 1 error" ); 835 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 836 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 837 | } ); 838 | 839 | QUnit.test( "THIS (default: nested): two arrows with non-arrow between, no this", function test(assert){ 840 | var code = ` 841 | var x = y => foo(function(){ return z => bar(z); }); 842 | `; 843 | 844 | var results = eslinter.verify( code, linterOptions.thisDefault ); 845 | var [ 846 | { ruleId: ruleId1, messageId: messageId1, } = {}, 847 | { ruleId: ruleId2, messageId: messageId2, } = {}, 848 | ] = results || []; 849 | 850 | assert.expect( 5 ); 851 | assert.strictEqual( results.length, 2, "only 2 errors" ); 852 | assert.strictEqual( ruleId1, "@getify/proper-arrows/this", "ruleId1" ); 853 | assert.strictEqual( messageId1, "noThisNested", "messageId1" ); 854 | assert.strictEqual( ruleId2, "@getify/proper-arrows/this", "ruleId2" ); 855 | assert.strictEqual( messageId2, "noThisNested", "messageId2" ); 856 | } ); 857 | 858 | QUnit.test( "THIS (default: nested): one arrow and one non-arrow, both this", function test(assert){ 859 | var code = ` 860 | var x = y => this.foo(function(){ return this.bar(z); }); 861 | `; 862 | 863 | var results = eslinter.verify( code, linterOptions.thisDefault ); 864 | 865 | assert.expect( 1 ); 866 | assert.strictEqual( results.length, 0, "no errors" ); 867 | } ); 868 | 869 | QUnit.test( "THIS (default: nested): one arrow and one non-arrow, nested this", function test(assert){ 870 | var code = ` 871 | var x = y => foo(function(){ return this.bar(z); }); 872 | `; 873 | 874 | var results = eslinter.verify( code, linterOptions.thisDefault ); 875 | var [{ ruleId, messageId, } = {},] = results || []; 876 | 877 | assert.expect( 3 ); 878 | assert.strictEqual( results.length, 1, "only 1 error" ); 879 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 880 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 881 | } ); 882 | 883 | 884 | // ********************************************** 885 | 886 | 887 | QUnit.test( "THIS (never): one arrow, this", function test(assert){ 888 | var code = ` 889 | var x = y => this.foo(y); 890 | `; 891 | 892 | var results = eslinter.verify( code, linterOptions.thisNever ); 893 | var [{ ruleId, messageId, } = {},] = results || []; 894 | 895 | assert.expect( 3 ); 896 | assert.strictEqual( results.length, 1, "only 1 error" ); 897 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 898 | assert.strictEqual( messageId, "neverThis", "messageId" ); 899 | } ); 900 | 901 | QUnit.test( "THIS (never): two nested arrows, both this", function test(assert){ 902 | var code = ` 903 | var x = y => this.foo(z => this.bar(z)); 904 | `; 905 | 906 | var results = eslinter.verify( code, linterOptions.thisNever ); 907 | var [ 908 | { ruleId: ruleId1, messageId: messageId1, } = {}, 909 | { ruleId: ruleId2, messageId: messageId2, } = {}, 910 | ] = results || []; 911 | 912 | assert.expect( 5 ); 913 | assert.strictEqual( results.length, 2, "only 2 errors" ); 914 | assert.strictEqual( ruleId1, "@getify/proper-arrows/this", "ruleId1" ); 915 | assert.strictEqual( messageId1, "neverThis", "messageId1" ); 916 | assert.strictEqual( ruleId2, "@getify/proper-arrows/this", "ruleId2" ); 917 | assert.strictEqual( messageId2, "neverThis", "messageId2" ); 918 | } ); 919 | 920 | QUnit.test( "THIS (never): one arrow with param arrow, both this", function test(assert){ 921 | var code = ` 922 | var x = (y = z => this.foo(z)) => this.bar(w); 923 | `; 924 | 925 | var results = eslinter.verify( code, linterOptions.thisNever ); 926 | var [ 927 | { ruleId: ruleId1, messageId: messageId1, } = {}, 928 | { ruleId: ruleId2, messageId: messageId2, } = {}, 929 | ] = results || []; 930 | 931 | assert.expect( 5 ); 932 | assert.strictEqual( results.length, 2, "only 2 errors" ); 933 | assert.strictEqual( ruleId1, "@getify/proper-arrows/this", "ruleId1" ); 934 | assert.strictEqual( messageId1, "neverThis", "messageId1" ); 935 | assert.strictEqual( ruleId2, "@getify/proper-arrows/this", "ruleId2" ); 936 | assert.strictEqual( messageId2, "neverThis", "messageId2" ); 937 | } ); 938 | 939 | QUnit.test( "THIS (never): two separate arrows, both this", function test(assert){ 940 | var code = ` 941 | var x = y => this.foo(y); 942 | var z = w => this.bar(w); 943 | `; 944 | 945 | var results = eslinter.verify( code, linterOptions.thisNever ); 946 | var [ 947 | { ruleId: ruleId1, messageId: messageId1, } = {}, 948 | { ruleId: ruleId2, messageId: messageId2, } = {}, 949 | ] = results || []; 950 | 951 | assert.expect( 5 ); 952 | assert.strictEqual( results.length, 2, "only 2 errors" ); 953 | assert.strictEqual( ruleId1, "@getify/proper-arrows/this", "ruleId1" ); 954 | assert.strictEqual( messageId1, "neverThis", "messageId1" ); 955 | assert.strictEqual( ruleId2, "@getify/proper-arrows/this", "ruleId2" ); 956 | assert.strictEqual( messageId2, "neverThis", "messageId2" ); 957 | } ); 958 | 959 | QUnit.test( "THIS (never): simple arrow, no this", function test(assert){ 960 | var code = ` 961 | var x = y => y; 962 | `; 963 | 964 | var results = eslinter.verify( code, linterOptions.thisNever ); 965 | 966 | assert.expect( 1 ); 967 | assert.strictEqual( results.length, 0, "no errors" ); 968 | } ); 969 | 970 | QUnit.test( "THIS (never): two separate arrows, no this", function test(assert){ 971 | var code = ` 972 | var x = y => foo(y); 973 | var z = w => bar(w); 974 | `; 975 | 976 | var results = eslinter.verify( code, linterOptions.thisNever ); 977 | 978 | assert.expect( 1 ); 979 | assert.strictEqual( results.length, 0, "no errors" ); 980 | } ); 981 | 982 | QUnit.test( "THIS (never): two separate arrows, one this", function test(assert){ 983 | var code = ` 984 | var x = y => this.foo(y); 985 | var z = w => bar(w); 986 | `; 987 | 988 | var results = eslinter.verify( code, linterOptions.thisNever ); 989 | var [{ ruleId, messageId, } = {},] = results || []; 990 | 991 | assert.expect( 3 ); 992 | assert.strictEqual( results.length, 1, "only 1 error" ); 993 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 994 | assert.strictEqual( messageId, "neverThis", "messageId" ); 995 | } ); 996 | 997 | QUnit.test( "THIS (never): two nested arrows, one this nested", function test(assert){ 998 | var code = ` 999 | var x = y => foo(z => this.bar(z)); 1000 | `; 1001 | 1002 | var results = eslinter.verify( code, linterOptions.thisNever ); 1003 | var [{ ruleId, messageId, } = {},] = results || []; 1004 | 1005 | assert.expect( 3 ); 1006 | assert.strictEqual( results.length, 1, "only 1 error" ); 1007 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 1008 | assert.strictEqual( messageId, "neverThis", "messageId" ); 1009 | } ); 1010 | 1011 | QUnit.test( "THIS (never): two nested arrows, one this not-nested", function test(assert){ 1012 | var code = ` 1013 | var x = y => this.foo(z => z); 1014 | `; 1015 | 1016 | var results = eslinter.verify( code, linterOptions.thisNever ); 1017 | var [{ ruleId, messageId, } = {},] = results || []; 1018 | 1019 | assert.expect( 3 ); 1020 | assert.strictEqual( results.length, 1, "only 1 error" ); 1021 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 1022 | assert.strictEqual( messageId, "neverThis", "messageId" ); 1023 | } ); 1024 | 1025 | QUnit.test( "THIS (never): two nested arrows, no this", function test(assert){ 1026 | var code = ` 1027 | var x = y => z => z; 1028 | `; 1029 | 1030 | var results = eslinter.verify( code, linterOptions.thisNever ); 1031 | 1032 | assert.expect( 1 ); 1033 | assert.strictEqual( results.length, 0, "no errors" ); 1034 | } ); 1035 | 1036 | QUnit.test( "THIS (never): one arrow with param arrow, param this", function test(assert){ 1037 | var code = ` 1038 | var x = (y = z => this.foo(z)) => bar(w); 1039 | `; 1040 | 1041 | var results = eslinter.verify( code, linterOptions.thisNever ); 1042 | var [{ ruleId, messageId, } = {},] = results || []; 1043 | 1044 | assert.expect( 3 ); 1045 | assert.strictEqual( results.length, 1, "only 1 error" ); 1046 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 1047 | assert.strictEqual( messageId, "neverThis", "messageId" ); 1048 | } ); 1049 | 1050 | QUnit.test( "THIS (never): one arrow and one non-arrow, both this", function test(assert){ 1051 | var code = ` 1052 | var x = y => this.foo(function(){ return this.bar(z); }); 1053 | `; 1054 | 1055 | var results = eslinter.verify( code, linterOptions.thisNever ); 1056 | var [{ ruleId, messageId, } = {},] = results || []; 1057 | 1058 | assert.expect( 3 ); 1059 | assert.strictEqual( results.length, 1, "only 1 error" ); 1060 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 1061 | assert.strictEqual( messageId, "neverThis", "messageId" ); 1062 | } ); 1063 | 1064 | QUnit.test( "THIS (never): one arrow and one non-arrow, nested this", function test(assert){ 1065 | var code = ` 1066 | var x = y => foo(function(){ return this.bar(z); }); 1067 | `; 1068 | 1069 | var results = eslinter.verify( code, linterOptions.thisNever ); 1070 | 1071 | assert.expect( 1 ); 1072 | assert.strictEqual( results.length, 0, "no errors" ); 1073 | } ); 1074 | 1075 | // ********************************************** 1076 | 1077 | QUnit.test( "THIS (never-global): two nested arrows, one this nested", function test(assert){ 1078 | var code = ` 1079 | var x = y => z => this.foo(z); 1080 | `; 1081 | 1082 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1083 | var [{ ruleId, messageId, } = {},] = results || []; 1084 | 1085 | assert.expect( 3 ); 1086 | assert.strictEqual( results.length, 1, "only 1 error" ); 1087 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 1088 | assert.strictEqual( messageId, "neverGlobal", "messageId" ); 1089 | } ); 1090 | 1091 | QUnit.test( "THIS (never-global): arrow function as argument, this", function test(assert){ 1092 | var code = ` 1093 | var f = foo(() => this.bar()) 1094 | `; 1095 | 1096 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1097 | var [{ ruleId, messageId, } = {},] = results || []; 1098 | 1099 | assert.expect( 3 ); 1100 | assert.strictEqual( results.length, 1, "only 1 error" ); 1101 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 1102 | assert.strictEqual( messageId, "neverGlobal", "messageId" ); 1103 | } ); 1104 | 1105 | QUnit.test( "THIS (never-global): inner arrow, this", function test(assert){ 1106 | var code = ` 1107 | function x() { return y => this.foo(y); } 1108 | `; 1109 | 1110 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1111 | 1112 | assert.expect( 1 ); 1113 | assert.strictEqual( results.length, 0, "no errors" ); 1114 | } ); 1115 | 1116 | QUnit.test( "THIS (never-global): parameter arrow, this", function test(assert){ 1117 | var code = ` 1118 | function x(z = y => this.foo(y)) { } 1119 | `; 1120 | 1121 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1122 | 1123 | assert.expect( 1 ); 1124 | assert.strictEqual( results.length, 0, "no errors" ); 1125 | } ); 1126 | 1127 | QUnit.test( "THIS (never-global): outer arrow, this", function test(assert){ 1128 | var code = ` 1129 | var x = y => this.foo(y); 1130 | `; 1131 | 1132 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1133 | var [{ ruleId, messageId, } = {},] = results || []; 1134 | 1135 | assert.expect( 3 ); 1136 | assert.strictEqual( results.length, 1, "only 1 error" ); 1137 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 1138 | assert.strictEqual( messageId, "neverGlobal", "messageId" ); 1139 | } ); 1140 | 1141 | QUnit.test( "THIS (never-global): property arrow, this", function test(assert){ 1142 | var code = ` 1143 | var o = { x: y => this.foo(y) }; 1144 | `; 1145 | 1146 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1147 | var [{ ruleId, messageId, } = {},] = results || []; 1148 | 1149 | assert.expect( 3 ); 1150 | assert.strictEqual( results.length, 1, "only 1 error" ); 1151 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 1152 | assert.strictEqual( messageId, "neverGlobal", "messageId" ); 1153 | } ); 1154 | 1155 | QUnit.test( "THIS (never-global): simple arrow, no this", function test(assert){ 1156 | var code = ` 1157 | var x = y => y; 1158 | `; 1159 | 1160 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1161 | 1162 | assert.expect( 1 ); 1163 | assert.strictEqual( results.length, 0, "no errors" ); 1164 | } ); 1165 | 1166 | QUnit.test( "THIS (never-global): two separate arrows, no this", function test(assert){ 1167 | var code = ` 1168 | var x = y => foo(y); 1169 | var z = w => bar(w); 1170 | `; 1171 | 1172 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1173 | 1174 | assert.expect( 1 ); 1175 | assert.strictEqual( results.length, 0, "no errors" ); 1176 | } ); 1177 | 1178 | QUnit.test( "THIS (never-global): two nested arrows, no this", function test(assert){ 1179 | var code = ` 1180 | var x = y => z => z; 1181 | `; 1182 | 1183 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1184 | 1185 | assert.expect( 1 ); 1186 | assert.strictEqual( results.length, 0, "no errors" ); 1187 | } ); 1188 | 1189 | QUnit.test( "THIS (never-global): two arrows with non-arrow between, nested this", function test(assert){ 1190 | var code = ` 1191 | var x = y => foo(function(){ return z => this.bar(z); }); 1192 | `; 1193 | 1194 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1195 | 1196 | assert.expect( 1 ); 1197 | assert.strictEqual( results.length, 0, "no errors" ); 1198 | } ); 1199 | 1200 | QUnit.test( "THIS (never-global): two arrows with method between, nested this", function test(assert){ 1201 | var code = ` 1202 | var x = y => foo({ method(){ return z => this.bar(z); } }); 1203 | `; 1204 | 1205 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1206 | 1207 | assert.expect( 1 ); 1208 | assert.strictEqual( results.length, 0, "no errors" ); 1209 | } ); 1210 | 1211 | QUnit.test( "THIS (never-global): two arrows with getter between, nested this", function test(assert){ 1212 | var code = ` 1213 | var x = y => foo({ get baz(){ return z => this.bar(z); } }); 1214 | `; 1215 | 1216 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1217 | 1218 | assert.expect( 1 ); 1219 | assert.strictEqual( results.length, 0, "no errors" ); 1220 | } ); 1221 | 1222 | QUnit.test( "THIS (never-global): two arrows with setter between, nested this", function test(assert){ 1223 | var code = ` 1224 | var x = y => foo({ set baz(v){ return z => this.bar(z); } }); 1225 | `; 1226 | 1227 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1228 | 1229 | assert.expect( 1 ); 1230 | assert.strictEqual( results.length, 0, "no errors" ); 1231 | } ); 1232 | 1233 | QUnit.test( "THIS (never-global): one arrow and non-arrow with arrow param, param this", function test(assert){ 1234 | var code = ` 1235 | var x = y => foo(function(z = w => this.bar(w)){ return bar(z); }); 1236 | `; 1237 | 1238 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1239 | 1240 | assert.expect( 1 ); 1241 | assert.strictEqual( results.length, 0, "no errors" ); 1242 | } ); 1243 | 1244 | QUnit.test( "THIS (never-global): two arrows with non-arrow between, no this", function test(assert){ 1245 | var code = ` 1246 | var x = y => foo(function(){ return z => bar(z); }); 1247 | `; 1248 | 1249 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1250 | 1251 | assert.expect( 1 ); 1252 | assert.strictEqual( results.length, 0, "no errors" ); 1253 | } ); 1254 | 1255 | QUnit.test( "THIS (never-global): one arrow and one non-arrow, nested this", function test(assert){ 1256 | var code = ` 1257 | var x = y => foo(function(){ return this.bar(z); }); 1258 | `; 1259 | 1260 | var results = eslinter.verify( code, linterOptions.thisNeverGlobal ); 1261 | 1262 | assert.expect( 1 ); 1263 | assert.strictEqual( results.length, 0, "no errors" ); 1264 | } ); 1265 | -------------------------------------------------------------------------------- /tests/tests.trivial.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var linterOptions = { 4 | trivialDefault: { 5 | parserOptions: { ecmaVersion: 2015, }, 6 | rules: { 7 | "@getify/proper-arrows/params": "error", 8 | "@getify/proper-arrows/name": "error", 9 | "@getify/proper-arrows/where": "error", 10 | "@getify/proper-arrows/return": "error", 11 | "@getify/proper-arrows/this": "error", 12 | }, 13 | }, 14 | trivial: { 15 | parserOptions: { ecmaVersion: 2015, }, 16 | rules: { 17 | "@getify/proper-arrows/params": ["error",{trivial:true,},], 18 | "@getify/proper-arrows/name": ["error",{trivial:true,},], 19 | "@getify/proper-arrows/where": ["error",{trivial:true,},], 20 | "@getify/proper-arrows/return": ["error",{trivial:true,},], 21 | "@getify/proper-arrows/this": ["error","nested",{trivial:true,},], 22 | }, 23 | }, 24 | trivialModuleDefault: { 25 | parserOptions: { ecmaVersion: 2015, sourceType: "module", }, 26 | rules: { 27 | "@getify/proper-arrows/where": "error", 28 | }, 29 | }, 30 | trivialModule: { 31 | parserOptions: { ecmaVersion: 2015, sourceType: "module", }, 32 | rules: { 33 | "@getify/proper-arrows/where": ["error",{trivial:true,},], 34 | }, 35 | }, 36 | }; 37 | 38 | QUnit.test( "TRIVIAL (default): violating", function test(assert){ 39 | var code = ` 40 | function foo() { 41 | var f; 42 | f = () => () => {}; 43 | f(() => 1); 44 | f(() => e); 45 | f(j => {}); 46 | f(z => z); 47 | f(g => h); 48 | f(k => null); 49 | f(h => void 0); 50 | return { f: v => v }; 51 | } 52 | f = r => r; 53 | var g = s => s; 54 | `; 55 | 56 | var results = eslinter.verify( code, linterOptions.trivialDefault ); 57 | var [{ ruleId, messageId, } = {},] = results || []; 58 | 59 | assert.expect( 3 ); 60 | assert.strictEqual( results.length, 1, "only 1 error" ); 61 | assert.strictEqual( ruleId, "@getify/proper-arrows/this", "ruleId" ); 62 | assert.strictEqual( messageId, "noThisNested", "messageId" ); 63 | } ); 64 | 65 | QUnit.test( "TRIVIAL (trivial:true): violating", function test(assert){ 66 | var code = ` 67 | function foo() { 68 | var f; 69 | f = () => () => {}; 70 | f(() => 1); 71 | f(() => e); 72 | f(j => {}); 73 | f(z => z); 74 | f(g => h); 75 | f(k => null); 76 | f(h => void 0); 77 | return { f: v => v }; 78 | } 79 | f = r => r; 80 | var g = s => s; 81 | `; 82 | 83 | var results = eslinter.verify( code, linterOptions.trivial ); 84 | var [ 85 | { ruleId: ruleId1, messageId: messageId1, message: message1, } = {}, 86 | { ruleId: ruleId2, messageId: messageId2, message: message2, } = {}, 87 | { ruleId: ruleId3, messageId: messageId3, message: message3, } = {}, 88 | { ruleId: ruleId4, messageId: messageId4, message: message4, } = {}, 89 | { ruleId: ruleId5, messageId: messageId5, message: message5, } = {}, 90 | { ruleId: ruleId6, messageId: messageId6, message: message6, } = {}, 91 | { ruleId: ruleId7, messageId: messageId7, message: message7, } = {}, 92 | { ruleId: ruleId8, messageId: messageId8, message: message8, } = {}, 93 | { ruleId: ruleId9, messageId: messageId9, message: message9, } = {}, 94 | { ruleId: ruleId10, messageId: messageId10, message: message10, } = {}, 95 | { ruleId: ruleId11, messageId: messageId11, message: message11, } = {}, 96 | { ruleId: ruleId12, messageId: messageId12, message: message12, } = {}, 97 | { ruleId: ruleId13, messageId: messageId13, message: message13, } = {}, 98 | { ruleId: ruleId14, messageId: messageId14, message: message14, } = {}, 99 | { ruleId: ruleId15, messageId: messageId15, message: message15, } = {}, 100 | { ruleId: ruleId16, messageId: messageId16, message: message16, } = {}, 101 | { ruleId: ruleId17, messageId: messageId17, message: message17, } = {}, 102 | { ruleId: ruleId18, messageId: messageId18, message: message18, } = {}, 103 | { ruleId: ruleId19, messageId: messageId19, message: message19, } = {}, 104 | { ruleId: ruleId20, messageId: messageId20, message: message20, } = {}, 105 | { ruleId: ruleId21, messageId: messageId21, message: message21, } = {}, 106 | { ruleId: ruleId22, messageId: messageId22, message: message22, } = {}, 107 | { ruleId: ruleId23, messageId: messageId23, message: message23, } = {}, 108 | { ruleId: ruleId24, messageId: messageId24, message: message24, } = {}, 109 | { ruleId: ruleId25, messageId: messageId25, message: message25, } = {}, 110 | { ruleId: ruleId26, messageId: messageId26, message: message26, } = {}, 111 | { ruleId: ruleId27, messageId: messageId27, message: message27, } = {}, 112 | { ruleId: ruleId28, messageId: messageId28, message: message28, } = {}, 113 | { ruleId: ruleId29, messageId: messageId29, message: message29, } = {}, 114 | { ruleId: ruleId30, messageId: messageId30, message: message30, } = {}, 115 | { ruleId: ruleId31, messageId: messageId31, message: message31, } = {}, 116 | { ruleId: ruleId32, messageId: messageId32, message: message32, } = {}, 117 | { ruleId: ruleId33, messageId: messageId33, message: message33, } = {}, 118 | { ruleId: ruleId34, messageId: messageId34, message: message34, } = {}, 119 | { ruleId: ruleId35, messageId: messageId35, message: message35, } = {}, 120 | { ruleId: ruleId36, messageId: messageId36, message: message36, } = {}, 121 | ] = results || []; 122 | 123 | assert.expect( 85 ); 124 | assert.strictEqual( results.length, 36, "only 36 errors" ); 125 | assert.strictEqual( ruleId1, "@getify/proper-arrows/this", "ruleId1" ); 126 | assert.strictEqual( messageId1, "noThisNested", "messageId1" ); 127 | assert.strictEqual( ruleId2, "@getify/proper-arrows/return", "ruleId2" ); 128 | assert.strictEqual( messageId2, "noChainedArrow", "messageId2" ); 129 | assert.strictEqual( ruleId3, "@getify/proper-arrows/name", "ruleId3" ); 130 | assert.strictEqual( messageId3, "noName", "messageId3" ); 131 | assert.strictEqual( ruleId4, "@getify/proper-arrows/this", "ruleId4" ); 132 | assert.strictEqual( messageId4, "noThisNested", "messageId4" ); 133 | assert.strictEqual( ruleId5, "@getify/proper-arrows/name", "ruleId5" ); 134 | assert.strictEqual( messageId5, "noName", "messageId5" ); 135 | assert.strictEqual( ruleId6, "@getify/proper-arrows/this", "ruleId6" ); 136 | assert.strictEqual( messageId6, "noThisNested", "messageId6" ); 137 | assert.strictEqual( ruleId7, "@getify/proper-arrows/name", "ruleId7" ); 138 | assert.strictEqual( messageId7, "noName", "messageId7" ); 139 | assert.strictEqual( ruleId8, "@getify/proper-arrows/this", "ruleId8" ); 140 | assert.strictEqual( messageId8, "noThisNested", "messageId8" ); 141 | assert.strictEqual( ruleId9, "@getify/proper-arrows/name", "ruleId9" ); 142 | assert.strictEqual( messageId9, "noName", "messageId10" ); 143 | assert.strictEqual( ruleId10, "@getify/proper-arrows/params", "ruleId10" ); 144 | assert.strictEqual( messageId10, "tooShort", "messageId10" ); 145 | assert.ok( message10.includes("`j`"), "message10" ); 146 | assert.strictEqual( ruleId11, "@getify/proper-arrows/params", "ruleId11" ); 147 | assert.strictEqual( messageId11, "unused", "messageId11" ); 148 | assert.ok( message11.includes("`j`"), "message11" ); 149 | assert.strictEqual( ruleId12, "@getify/proper-arrows/this", "ruleId12" ); 150 | assert.strictEqual( messageId12, "noThisNested", "messageId12" ); 151 | assert.strictEqual( ruleId13, "@getify/proper-arrows/name", "ruleId13" ); 152 | assert.strictEqual( messageId13, "noName", "messageId13" ); 153 | assert.strictEqual( ruleId14, "@getify/proper-arrows/params", "ruleId14" ); 154 | assert.strictEqual( messageId14, "tooShort", "messageId14" ); 155 | assert.ok( message14.includes("`z`"), "message14" ); 156 | assert.strictEqual( ruleId15, "@getify/proper-arrows/this", "ruleId15" ); 157 | assert.strictEqual( messageId15, "noThisNested", "messageId15" ); 158 | assert.strictEqual( ruleId16, "@getify/proper-arrows/name", "ruleId16" ); 159 | assert.strictEqual( messageId16, "noName", "messageId16" ); 160 | assert.strictEqual( ruleId17, "@getify/proper-arrows/params", "ruleId17" ); 161 | assert.strictEqual( messageId17, "tooShort", "messageId17" ); 162 | assert.ok( message17.includes("`g`"), "message17" ); 163 | assert.strictEqual( ruleId18, "@getify/proper-arrows/params", "ruleId18" ); 164 | assert.strictEqual( messageId18, "unused", "messageId18" ); 165 | assert.ok( message18.includes("`g`"), "message18" ); 166 | assert.strictEqual( ruleId19, "@getify/proper-arrows/this", "ruleId19" ); 167 | assert.strictEqual( messageId19, "noThisNested", "messageId19" ); 168 | assert.strictEqual( ruleId20, "@getify/proper-arrows/name", "ruleId20" ); 169 | assert.strictEqual( messageId20, "noName", "messageId20" ); 170 | assert.strictEqual( ruleId21, "@getify/proper-arrows/params", "ruleId21" ); 171 | assert.strictEqual( messageId21, "tooShort", "messageId21" ); 172 | assert.ok( message21.includes("`k`"), "message21" ); 173 | assert.strictEqual( ruleId22, "@getify/proper-arrows/params", "ruleId22" ); 174 | assert.strictEqual( messageId22, "unused", "messageId22" ); 175 | assert.ok( message22.includes("`k`"), "message22" ); 176 | assert.strictEqual( ruleId23, "@getify/proper-arrows/this", "ruleId23" ); 177 | assert.strictEqual( messageId23, "noThisNested", "messageId23" ); 178 | assert.strictEqual( ruleId24, "@getify/proper-arrows/name", "ruleId24" ); 179 | assert.strictEqual( messageId24, "noName", "messageId24" ); 180 | assert.strictEqual( ruleId25, "@getify/proper-arrows/params", "ruleId25" ); 181 | assert.strictEqual( messageId25, "tooShort", "messageId25" ); 182 | assert.ok( message25.includes("`h`"), "message25" ); 183 | assert.strictEqual( ruleId26, "@getify/proper-arrows/params", "ruleId26" ); 184 | assert.strictEqual( messageId26, "unused", "messageId26" ); 185 | assert.ok( message26.includes("`h`"), "message26" ); 186 | assert.strictEqual( ruleId27, "@getify/proper-arrows/this", "ruleId27" ); 187 | assert.strictEqual( messageId27, "noThisNested", "messageId27" ); 188 | assert.strictEqual( ruleId28, "@getify/proper-arrows/where", "ruleId28" ); 189 | assert.strictEqual( messageId28, "noProperty", "messageId28" ); 190 | assert.strictEqual( ruleId29, "@getify/proper-arrows/params", "ruleId29" ); 191 | assert.strictEqual( messageId29, "tooShort", "messageId29" ); 192 | assert.ok( message29.includes("`v`"), "message29" ); 193 | assert.strictEqual( ruleId30, "@getify/proper-arrows/this", "ruleId30" ); 194 | assert.strictEqual( messageId30, "noThisNested", "messageId30" ); 195 | assert.strictEqual( ruleId31, "@getify/proper-arrows/where", "ruleId31" ); 196 | assert.strictEqual( messageId31, "noGlobal", "messageId31" ); 197 | assert.strictEqual( ruleId32, "@getify/proper-arrows/params", "ruleId32" ); 198 | assert.strictEqual( messageId32, "tooShort", "messageId32" ); 199 | assert.ok( message32.includes("`r`"), "message32" ); 200 | assert.strictEqual( ruleId33, "@getify/proper-arrows/this", "ruleId33" ); 201 | assert.strictEqual( messageId33, "noThisNested", "messageId33" ); 202 | assert.strictEqual( ruleId34, "@getify/proper-arrows/where", "ruleId34" ); 203 | assert.strictEqual( messageId34, "noGlobalDeclaration", "messageId34" ); 204 | assert.strictEqual( ruleId35, "@getify/proper-arrows/params", "ruleId35" ); 205 | assert.strictEqual( messageId35, "tooShort", "messageId35" ); 206 | assert.ok( message35.includes("`s`"), "message35" ); 207 | assert.strictEqual( ruleId36, "@getify/proper-arrows/this", "ruleId36" ); 208 | assert.strictEqual( messageId36, "noThisNested", "messageId36" ); 209 | } ); 210 | 211 | QUnit.test( "TRIVIAL (module-export, default): conforming", function test(assert){ 212 | var code = ` 213 | export default () => {}; 214 | f = () => {}; 215 | var g = () => {}; 216 | `; 217 | 218 | var results = eslinter.verify( code, linterOptions.trivialModuleDefault ); 219 | 220 | assert.expect( 1 ); 221 | assert.strictEqual( results.length, 0, "no errors" ); 222 | } ); 223 | 224 | QUnit.test( "TRIVIAL (module-export, trivial:true): violating", function test(assert){ 225 | var code = ` 226 | export default () => {}; 227 | f = () => {}; 228 | var g = () => {}; 229 | `; 230 | 231 | var results = eslinter.verify( code, linterOptions.trivialModule ); 232 | 233 | var [ 234 | { ruleId: ruleId1, messageId: messageId1, } = {}, 235 | { ruleId: ruleId2, messageId: messageId2, } = {}, 236 | { ruleId: ruleId3, messageId: messageId3, } = {}, 237 | { ruleId: ruleId4, messageId: messageId4, } = {}, 238 | ] = results || []; 239 | 240 | assert.expect( 9 ); 241 | assert.strictEqual( results.length, 4, "only 4 errors" ); 242 | assert.strictEqual( ruleId1, "@getify/proper-arrows/where", "ruleId" ); 243 | assert.strictEqual( messageId1, "noGlobal", "messageId1" ); 244 | assert.strictEqual( ruleId2, "@getify/proper-arrows/where", "ruleId" ); 245 | assert.strictEqual( messageId2, "noExport", "messageId2" ); 246 | assert.strictEqual( ruleId3, "@getify/proper-arrows/where", "ruleId" ); 247 | assert.strictEqual( messageId3, "noGlobal", "messageId3" ); 248 | assert.strictEqual( ruleId4, "@getify/proper-arrows/where", "ruleId" ); 249 | assert.strictEqual( messageId4, "noGlobalDeclaration", "messageId4" ); 250 | } ); 251 | -------------------------------------------------------------------------------- /tests/tests.where.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var linterOptions = { 4 | whereDefault: { 5 | parserOptions: { ecmaVersion: 2015, }, 6 | rules: { "@getify/proper-arrows/where": ["error",{trivial:true,},], }, 7 | }, 8 | whereGlobalDefault: { 9 | parserOptions: { ecmaVersion: 2015, }, 10 | rules: { "@getify/proper-arrows/where": ["error",{property:false,export:false,trivial:true,},], }, 11 | }, 12 | whereGlobalOn: { 13 | parserOptions: { ecmaVersion: 2015, }, 14 | rules: { "@getify/proper-arrows/where": ["error",{global:true,property:false,export:false,trivial:true,},], }, 15 | }, 16 | whereGlobalOff: { 17 | parserOptions: { ecmaVersion: 2015, }, 18 | rules: { "@getify/proper-arrows/where": ["error",{global:false,property:false,export:false,trivial:true,},], }, 19 | }, 20 | whereModuleGlobalDefault: { 21 | parserOptions: { ecmaVersion: 2015, sourceType: "module", }, 22 | rules: { "@getify/proper-arrows/where": ["error",{property:false,export:false,trivial:true,},], }, 23 | }, 24 | whereModuleGlobalOn: { 25 | parserOptions: { ecmaVersion: 2015, sourceType: "module", }, 26 | rules: { "@getify/proper-arrows/where": ["error",{global:true,property:false,export:false,trivial:true,},], }, 27 | }, 28 | whereModuleGlobalOff: { 29 | parserOptions: { ecmaVersion: 2015, sourceType: "module", }, 30 | rules: { "@getify/proper-arrows/where": ["error",{global:false,property:false,export:false,trivial:true,},], }, 31 | }, 32 | whereGlobalDefaultDeclarationOn: { 33 | parserOptions: { ecmaVersion: 2015, }, 34 | rules: { "@getify/proper-arrows/where": ["error",{"global-declaration":true,property:false,export:false,trivial:true,},], }, 35 | }, 36 | whereGlobalOnDeclarationOn: { 37 | parserOptions: { ecmaVersion: 2015, }, 38 | rules: { "@getify/proper-arrows/where": ["error",{global:true,"global-declaration":true,property:false,export:false,trivial:true,},], }, 39 | }, 40 | whereGlobalOffDeclarationOn: { 41 | parserOptions: { ecmaVersion: 2015, }, 42 | rules: { "@getify/proper-arrows/where": ["error",{global:false,"global-declaration":true,property:false,export:false,trivial:true,},], }, 43 | }, 44 | whereModuleGlobalDefaultDeclarationOn: { 45 | parserOptions: { ecmaVersion: 2015, sourceType: "module", }, 46 | rules: { "@getify/proper-arrows/where": ["error",{"global-declaration":true,property:false,export:false,trivial:true,},], }, 47 | }, 48 | whereModuleGlobalOnDeclarationOn: { 49 | parserOptions: { ecmaVersion: 2015, sourceType: "module", }, 50 | rules: { "@getify/proper-arrows/where": ["error",{global:true,"global-declaration":true,property:false,export:false,trivial:true,},], }, 51 | }, 52 | whereModuleGlobalOffDeclarationOn: { 53 | parserOptions: { ecmaVersion: 2015, sourceType: "module", }, 54 | rules: { "@getify/proper-arrows/where": ["error",{global:false,"global-declaration":true,property:false,export:false,trivial:true,},], }, 55 | }, 56 | whereGlobalOffDeclarationOff: { 57 | parserOptions: { ecmaVersion: 2015, }, 58 | rules: { "@getify/proper-arrows/where": ["error",{global:false,"global-declaration":false,property:false,export:false,trivial:true,},], }, 59 | }, 60 | whereModuleGlobalOffDeclarationOff: { 61 | parserOptions: { ecmaVersion: 2015, sourceType: "module", }, 62 | rules: { "@getify/proper-arrows/where": ["error",{global:false,"global-declaration":false,property:false,export:false,trivial:true,},], }, 63 | }, 64 | wherePropertyDefault: { 65 | parserOptions: { ecmaVersion: 2015, }, 66 | rules: { "@getify/proper-arrows/where": ["error",{global:false,export:false,trivial:true,},], }, 67 | }, 68 | whereProperty: { 69 | parserOptions: { ecmaVersion: 2015, }, 70 | rules: { "@getify/proper-arrows/where": ["error",{global:false,property:true,export:false,trivial:true,},], }, 71 | }, 72 | whereExportDefault: { 73 | parserOptions: { ecmaVersion: 2015, sourceType: "module", }, 74 | rules: { "@getify/proper-arrows/where": ["error",{global:false,property:false,trivial:true,},], }, 75 | }, 76 | whereExport: { 77 | parserOptions: { ecmaVersion: 2015, sourceType: "module", }, 78 | rules: { "@getify/proper-arrows/where": ["error",{global:false,property:false,export:true,trivial:true,},], }, 79 | }, 80 | }; 81 | 82 | QUnit.test( "WHERE (default): conforming", function test(assert){ 83 | var code = ` 84 | function foo() { 85 | var f = x => y; 86 | return { fn: f }; 87 | } 88 | `; 89 | 90 | var results = eslinter.verify( code, linterOptions.whereDefault ); 91 | 92 | assert.expect( 1 ); 93 | assert.strictEqual( results.length, 0, "no errors" ); 94 | } ); 95 | 96 | QUnit.test( "WHERE (default): violating", function test(assert){ 97 | var code = ` 98 | f = x => y; 99 | var o = { fn: z => z }; 100 | var g = w => w; 101 | `; 102 | 103 | var results = eslinter.verify( code, linterOptions.whereDefault ); 104 | var [ 105 | { ruleId: ruleId1, messageId: messageId1, } = {}, 106 | { ruleId: ruleId2, messageId: messageId2, } = {}, 107 | { ruleId: ruleId3, messageId: messageId3, } = {}, 108 | { ruleId: ruleId4, messageId: messageId4, } = {}, 109 | ] = results || []; 110 | 111 | assert.expect( 9 ); 112 | assert.strictEqual( results.length, 4, "only 4 errors" ); 113 | assert.strictEqual( ruleId1, "@getify/proper-arrows/where", "ruleId1" ); 114 | assert.strictEqual( messageId1, "noGlobal", "messageId1" ); 115 | assert.strictEqual( ruleId2, "@getify/proper-arrows/where", "ruleId2" ); 116 | assert.strictEqual( messageId2, "noGlobal", "messageId2" ); 117 | assert.strictEqual( ruleId3, "@getify/proper-arrows/where", "ruleId3" ); 118 | assert.strictEqual( messageId3, "noProperty", "messageId3" ); 119 | assert.strictEqual( ruleId4, "@getify/proper-arrows/where", "ruleId4" ); 120 | assert.strictEqual( messageId4, "noGlobalDeclaration", "messageId3" ); 121 | } ); 122 | 123 | QUnit.test( "WHERE (global/default, global-declaration/default): conforming", function test(assert){ 124 | var code = ` 125 | function foo() { 126 | f = x => y; 127 | var g = z => w; 128 | } 129 | `; 130 | 131 | var results = eslinter.verify( code, linterOptions.whereGlobalDefault ); 132 | 133 | assert.expect( 1 ); 134 | assert.strictEqual( results.length, 0, "no errors" ); 135 | } ); 136 | 137 | QUnit.test( "WHERE (global/default, global-declaration/default): violating", function test(assert){ 138 | var code = ` 139 | f = x => y; 140 | var g = z => w; 141 | `; 142 | 143 | var results = eslinter.verify( code, linterOptions.whereGlobalDefault ); 144 | var [ 145 | { ruleId: ruleId1, messageId: messageId1, } = {}, 146 | { ruleId: ruleId2, messageId: messageId2, } = {}, 147 | ] = results || []; 148 | 149 | assert.expect( 5 ); 150 | assert.strictEqual( results.length, 2, "only 2 errors" ); 151 | assert.strictEqual( ruleId1, "@getify/proper-arrows/where", "ruleId1" ); 152 | assert.strictEqual( messageId1, "noGlobal", "messageId1" ); 153 | assert.strictEqual( ruleId2, "@getify/proper-arrows/where", "ruleId2" ); 154 | assert.strictEqual( messageId2, "noGlobalDeclaration", "messageId1" ); 155 | } ); 156 | 157 | QUnit.test( "WHERE (global/on, global-declaration/default): conforming", function test(assert){ 158 | var code = ` 159 | function foo() { 160 | f = x => y; 161 | var g = z => w; 162 | } 163 | `; 164 | 165 | var results = eslinter.verify( code, linterOptions.whereGlobalOn ); 166 | 167 | assert.expect( 1 ); 168 | assert.strictEqual( results.length, 0, "no errors" ); 169 | } ); 170 | 171 | QUnit.test( "WHERE (global/on, global-declaration/default): violating", function test(assert){ 172 | var code = ` 173 | f = x => y; 174 | var g = z => w; 175 | `; 176 | 177 | var results = eslinter.verify( code, linterOptions.whereGlobalOn ); 178 | var [ 179 | { ruleId: ruleId1, messageId: messageId1, } = {}, 180 | { ruleId: ruleId2, messageId: messageId2, } = {}, 181 | ] = results || []; 182 | 183 | assert.expect( 5 ); 184 | assert.strictEqual( results.length, 2, "only 2 errors" ); 185 | assert.strictEqual( ruleId1, "@getify/proper-arrows/where", "ruleId1" ); 186 | assert.strictEqual( messageId1, "noGlobal", "messageId1" ); 187 | assert.strictEqual( ruleId2, "@getify/proper-arrows/where", "ruleId2" ); 188 | assert.strictEqual( messageId2, "noGlobalDeclaration", "messageId2" ); 189 | } ); 190 | 191 | QUnit.test( "WHERE (global/off, global-declaration/default): conforming", function test(assert){ 192 | var code = ` 193 | function foo() { 194 | f = x => y; 195 | var g = z => w; 196 | } 197 | f = x => y; 198 | var g = z => w; 199 | `; 200 | 201 | var results = eslinter.verify( code, linterOptions.whereGlobalOff ); 202 | 203 | assert.expect( 1 ); 204 | assert.strictEqual( results.length, 0, "no errors" ); 205 | } ); 206 | 207 | QUnit.test( "WHERE (module-global/default, global-declaration/default): conforming", function test(assert){ 208 | var code = ` 209 | function foo() { 210 | f = x => y; 211 | var g = z => w; 212 | } 213 | `; 214 | 215 | var results = eslinter.verify( code, linterOptions.whereModuleGlobalDefault ); 216 | 217 | assert.expect( 1 ); 218 | assert.strictEqual( results.length, 0, "no errors" ); 219 | } ); 220 | 221 | QUnit.test( "WHERE (module-global/default, global-declaration/default): violating", function test(assert){ 222 | var code = ` 223 | f = x => y; 224 | var g = z => w; 225 | `; 226 | 227 | var results = eslinter.verify( code, linterOptions.whereModuleGlobalDefault ); 228 | var [ 229 | { ruleId: ruleId1, messageId: messageId1, } = {}, 230 | { ruleId: ruleId2, messageId: messageId2, } = {}, 231 | ] = results || []; 232 | 233 | assert.expect( 5 ); 234 | assert.strictEqual( results.length, 2, "only 2 errors" ); 235 | assert.strictEqual( ruleId1, "@getify/proper-arrows/where", "ruleId1" ); 236 | assert.strictEqual( messageId1, "noGlobal", "messageId1" ); 237 | assert.strictEqual( ruleId2, "@getify/proper-arrows/where", "ruleId2" ); 238 | assert.strictEqual( messageId2, "noGlobalDeclaration", "messageId2" ); 239 | } ); 240 | 241 | QUnit.test( "WHERE (module-global/on, global-declaration/default): conforming", function test(assert){ 242 | var code = ` 243 | function foo() { 244 | f = x => y; 245 | var g = z => w; 246 | } 247 | `; 248 | 249 | var results = eslinter.verify( code, linterOptions.whereModuleGlobalOn ); 250 | 251 | assert.expect( 1 ); 252 | assert.strictEqual( results.length, 0, "no errors" ); 253 | } ); 254 | 255 | QUnit.test( "WHERE (module-global/on, global-declaration/default): violating", function test(assert){ 256 | var code = ` 257 | f = x => y; 258 | var g = z => w; 259 | `; 260 | 261 | var results = eslinter.verify( code, linterOptions.whereModuleGlobalOn ); 262 | var [ 263 | { ruleId: ruleId1, messageId: messageId1, } = {}, 264 | { ruleId: ruleId2, messageId: messageId2, } = {}, 265 | ] = results || []; 266 | 267 | assert.expect( 5 ); 268 | assert.strictEqual( results.length, 2, "only 2 errors" ); 269 | assert.strictEqual( ruleId1, "@getify/proper-arrows/where", "ruleId1" ); 270 | assert.strictEqual( messageId1, "noGlobal", "messageId1" ); 271 | assert.strictEqual( ruleId2, "@getify/proper-arrows/where", "ruleId2" ); 272 | assert.strictEqual( messageId2, "noGlobalDeclaration", "messageId2" ); 273 | } ); 274 | 275 | QUnit.test( "WHERE (module-global/off, global-declaration/default): conforming", function test(assert){ 276 | var code = ` 277 | function foo() { 278 | f = x => y; 279 | var g = z => w; 280 | } 281 | f = x => y; 282 | var g = z => w; 283 | `; 284 | 285 | var results = eslinter.verify( code, linterOptions.whereModuleGlobalOff ); 286 | 287 | assert.expect( 1 ); 288 | assert.strictEqual( results.length, 0, "no errors" ); 289 | } ); 290 | 291 | QUnit.test( "WHERE (global/default, global-declaration/on): conforming", function test(assert){ 292 | var code = ` 293 | function foo() { 294 | f = x => y; 295 | var g = z => w; 296 | } 297 | `; 298 | 299 | var results = eslinter.verify( code, linterOptions.whereGlobalDefaultDeclarationOn ); 300 | 301 | assert.expect( 1 ); 302 | assert.strictEqual( results.length, 0, "no errors" ); 303 | } ); 304 | 305 | QUnit.test( "WHERE (global/default, global-declaration/on): violating", function test(assert){ 306 | var code = ` 307 | f = x => y; 308 | var g = z => w; 309 | `; 310 | 311 | var results = eslinter.verify( code, linterOptions.whereGlobalDefaultDeclarationOn ); 312 | var [ 313 | { ruleId: ruleId1, messageId: messageId1, } = {}, 314 | { ruleId: ruleId2, messageId: messageId2, } = {}, 315 | ] = results || []; 316 | 317 | assert.expect( 5 ); 318 | assert.strictEqual( results.length, 2, "only 2 errors" ); 319 | assert.strictEqual( ruleId1, "@getify/proper-arrows/where", "ruleId1" ); 320 | assert.strictEqual( messageId1, "noGlobal", "messageId1" ); 321 | assert.strictEqual( ruleId2, "@getify/proper-arrows/where", "ruleId2" ); 322 | assert.strictEqual( messageId2, "noGlobalDeclaration", "messageId1" ); 323 | } ); 324 | 325 | QUnit.test( "WHERE (global/on, global-declaration/on): conforming", function test(assert){ 326 | var code = ` 327 | function foo() { 328 | f = x => y; 329 | var g = z => w; 330 | } 331 | `; 332 | 333 | var results = eslinter.verify( code, linterOptions.whereGlobalOnDeclarationOn ); 334 | 335 | assert.expect( 1 ); 336 | assert.strictEqual( results.length, 0, "no errors" ); 337 | } ); 338 | 339 | QUnit.test( "WHERE (global/on, global-declaration/on): violating", function test(assert){ 340 | var code = ` 341 | f = x => y; 342 | var g = z => w; 343 | `; 344 | 345 | var results = eslinter.verify( code, linterOptions.whereGlobalOnDeclarationOn ); 346 | var [ 347 | { ruleId: ruleId1, messageId: messageId1, } = {}, 348 | { ruleId: ruleId2, messageId: messageId2, } = {}, 349 | ] = results || []; 350 | 351 | assert.expect( 5 ); 352 | assert.strictEqual( results.length, 2, "only 2 errors" ); 353 | assert.strictEqual( ruleId1, "@getify/proper-arrows/where", "ruleId1" ); 354 | assert.strictEqual( messageId1, "noGlobal", "messageId1" ); 355 | assert.strictEqual( ruleId2, "@getify/proper-arrows/where", "ruleId2" ); 356 | assert.strictEqual( messageId2, "noGlobalDeclaration", "messageId2" ); 357 | } ); 358 | 359 | QUnit.test( "WHERE (global/off, global-declaration/on): conforming", function test(assert){ 360 | var code = ` 361 | function foo() { 362 | f = x => y; 363 | var g = z => w; 364 | } 365 | f = x => y; 366 | var g = z => w; 367 | `; 368 | 369 | var results = eslinter.verify( code, linterOptions.whereGlobalOffDeclarationOn ); 370 | 371 | var [ 372 | { ruleId: ruleId1, messageId: messageId1, } = {}, 373 | ] = results || []; 374 | 375 | assert.expect( 3 ); 376 | assert.strictEqual( results.length, 1, "only 1 error" ); 377 | assert.strictEqual( ruleId1, "@getify/proper-arrows/where", "ruleId1" ); 378 | assert.strictEqual( messageId1, "noGlobalDeclaration", "messageId1" ); 379 | } ); 380 | 381 | QUnit.test( "WHERE (module-global/default, global-declaration/on): conforming", function test(assert){ 382 | var code = ` 383 | function foo() { 384 | f = x => y; 385 | var g = z => w; 386 | } 387 | `; 388 | 389 | var results = eslinter.verify( code, linterOptions.whereModuleGlobalDefaultDeclarationOn ); 390 | 391 | assert.expect( 1 ); 392 | assert.strictEqual( results.length, 0, "no errors" ); 393 | } ); 394 | 395 | QUnit.test( "WHERE (module-global/default, global-declaration/on): violating", function test(assert){ 396 | var code = ` 397 | f = x => y; 398 | var g = z => w; 399 | `; 400 | 401 | var results = eslinter.verify( code, linterOptions.whereModuleGlobalDefaultDeclarationOn ); 402 | var [ 403 | { ruleId: ruleId1, messageId: messageId1, } = {}, 404 | { ruleId: ruleId2, messageId: messageId2, } = {}, 405 | ] = results || []; 406 | 407 | assert.expect( 5 ); 408 | assert.strictEqual( results.length, 2, "only 2 errors" ); 409 | assert.strictEqual( ruleId1, "@getify/proper-arrows/where", "ruleId1" ); 410 | assert.strictEqual( messageId1, "noGlobal", "messageId1" ); 411 | assert.strictEqual( ruleId2, "@getify/proper-arrows/where", "ruleId2" ); 412 | assert.strictEqual( messageId2, "noGlobalDeclaration", "messageId2" ); 413 | } ); 414 | 415 | QUnit.test( "WHERE (module-global/on, global-declaration/on): conforming", function test(assert){ 416 | var code = ` 417 | function foo() { 418 | f = x => y; 419 | var g = z => w; 420 | } 421 | `; 422 | 423 | var results = eslinter.verify( code, linterOptions.whereModuleGlobalOnDeclarationOn ); 424 | 425 | assert.expect( 1 ); 426 | assert.strictEqual( results.length, 0, "no errors" ); 427 | } ); 428 | 429 | QUnit.test( "WHERE (module-global/on, global-declaration/on): violating", function test(assert){ 430 | var code = ` 431 | f = x => y; 432 | var g = z => w; 433 | `; 434 | 435 | var results = eslinter.verify( code, linterOptions.whereModuleGlobalOnDeclarationOn ); 436 | var [ 437 | { ruleId: ruleId1, messageId: messageId1, } = {}, 438 | { ruleId: ruleId2, messageId: messageId2, } = {}, 439 | ] = results || []; 440 | 441 | assert.expect( 5 ); 442 | assert.strictEqual( results.length, 2, "only 2 errors" ); 443 | assert.strictEqual( ruleId1, "@getify/proper-arrows/where", "ruleId1" ); 444 | assert.strictEqual( messageId1, "noGlobal", "messageId1" ); 445 | assert.strictEqual( ruleId2, "@getify/proper-arrows/where", "ruleId2" ); 446 | assert.strictEqual( messageId2, "noGlobalDeclaration", "messageId2" ); 447 | } ); 448 | 449 | QUnit.test( "WHERE (module-global/off, global-declaration/on): conforming", function test(assert){ 450 | var code = ` 451 | function foo() { 452 | f = x => y; 453 | var g = z => w; 454 | } 455 | f = x => y; 456 | var g = z => w; 457 | `; 458 | 459 | var results = eslinter.verify( code, linterOptions.whereModuleGlobalOffDeclarationOn ); 460 | 461 | var [ 462 | { ruleId: ruleId1, messageId: messageId1, } = {}, 463 | ] = results || []; 464 | 465 | assert.expect( 3 ); 466 | assert.strictEqual( results.length, 1, "only 1 error" ); 467 | assert.strictEqual( ruleId1, "@getify/proper-arrows/where", "ruleId1" ); 468 | assert.strictEqual( messageId1, "noGlobalDeclaration", "messageId1" ); 469 | } ); 470 | 471 | QUnit.test( "WHERE (global/off, global-declaration/off): conforming", function test(assert){ 472 | var code = ` 473 | function foo() { 474 | f = x => y; 475 | var g = z => w; 476 | } 477 | f = x => y; 478 | var g = z => w; 479 | `; 480 | 481 | var results = eslinter.verify( code, linterOptions.whereGlobalOffDeclarationOff ); 482 | 483 | assert.expect( 1 ); 484 | assert.strictEqual( results.length, 0, "no errors" ); 485 | } ); 486 | 487 | QUnit.test( "WHERE (module-global/off, global-declaration/off): conforming", function test(assert){ 488 | var code = ` 489 | function foo() { 490 | f = x => y; 491 | var g = z => w; 492 | } 493 | f = x => y; 494 | var g = z => w; 495 | `; 496 | 497 | var results = eslinter.verify( code, linterOptions.whereModuleGlobalOffDeclarationOff ); 498 | 499 | assert.expect( 1 ); 500 | assert.strictEqual( results.length, 0, "no errors" ); 501 | } ); 502 | 503 | QUnit.test( "WHERE (property, default): violating", function test(assert){ 504 | var code = ` 505 | var o = { f: x => y }; 506 | `; 507 | 508 | var results = eslinter.verify( code, linterOptions.wherePropertyDefault ); 509 | var [{ ruleId, messageId, } = {},] = results || []; 510 | 511 | assert.expect( 3 ); 512 | assert.strictEqual( results.length, 1, "only 1 error" ); 513 | assert.strictEqual( ruleId, "@getify/proper-arrows/where", "ruleId" ); 514 | assert.strictEqual( messageId, "noProperty", "messageId" ); 515 | } ); 516 | 517 | QUnit.test( "WHERE (property): violating", function test(assert){ 518 | var code = ` 519 | var o = { f: x => y }; 520 | `; 521 | 522 | var results = eslinter.verify( code, linterOptions.whereProperty ); 523 | var [{ ruleId, messageId, } = {},] = results || []; 524 | 525 | assert.expect( 3 ); 526 | assert.strictEqual( results.length, 1, "only 1 error" ); 527 | assert.strictEqual( ruleId, "@getify/proper-arrows/where", "ruleId" ); 528 | assert.strictEqual( messageId, "noProperty", "messageId" ); 529 | } ); 530 | 531 | QUnit.test( "WHERE (default-export, default): violating", function test(assert){ 532 | var code = ` 533 | export default () => {}; 534 | `; 535 | 536 | var results = eslinter.verify( code, linterOptions.whereExportDefault ); 537 | var [{ ruleId, messageId, } = {},] = results || []; 538 | 539 | assert.expect( 3 ); 540 | assert.strictEqual( results.length, 1, "only 1 error" ); 541 | assert.strictEqual( ruleId, "@getify/proper-arrows/where", "ruleId" ); 542 | assert.strictEqual( messageId, "noExport", "messageId" ); 543 | } ); 544 | 545 | QUnit.test( "WHERE (default-export): violating", function test(assert){ 546 | var code = ` 547 | export default () => {}; 548 | `; 549 | 550 | var results = eslinter.verify( code, linterOptions.whereExport ); 551 | var [{ ruleId, messageId, } = {},] = results || []; 552 | 553 | assert.expect( 3 ); 554 | assert.strictEqual( results.length, 1, "only 1 error" ); 555 | assert.strictEqual( ruleId, "@getify/proper-arrows/where", "ruleId" ); 556 | assert.strictEqual( messageId, "noExport", "messageId" ); 557 | } ); 558 | 559 | QUnit.test( "WHERE (named-declaration-export, default): violating", function test(assert){ 560 | var code = ` 561 | export var x = () => {}; 562 | `; 563 | 564 | var results = eslinter.verify( code, linterOptions.whereExportDefault ); 565 | var [{ ruleId, messageId, } = {},] = results || []; 566 | 567 | assert.expect( 3 ); 568 | assert.strictEqual( results.length, 1, "only 1 error" ); 569 | assert.strictEqual( ruleId, "@getify/proper-arrows/where", "ruleId" ); 570 | assert.strictEqual( messageId, "noExport", "messageId" ); 571 | } ); 572 | 573 | QUnit.test( "WHERE (named-declaration-export): violating", function test(assert){ 574 | var code = ` 575 | export var x = () => {}; 576 | `; 577 | 578 | var results = eslinter.verify( code, linterOptions.whereExport ); 579 | var [{ ruleId, messageId, } = {},] = results || []; 580 | 581 | assert.expect( 3 ); 582 | assert.strictEqual( results.length, 1, "only 1 error" ); 583 | assert.strictEqual( ruleId, "@getify/proper-arrows/where", "ruleId" ); 584 | assert.strictEqual( messageId, "noExport", "messageId" ); 585 | } ); 586 | --------------------------------------------------------------------------------