├── .gitignore ├── .npmignore ├── .travis.yml ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── helpers ├── getClassMethods.ts ├── getJsxAttributes.ts ├── getLeadingWhitespace.ts └── nodeIsKind.ts ├── package.json ├── prettier.config.js ├── rules ├── camelCaseLocalFunctionsRule.ts ├── classMethodNewlinesRule.ts ├── declareClassMethodsAfterUseRule.ts ├── index.json ├── jsxAttributeSpacingRule.ts ├── jsxExpressionSpacingRule.ts ├── jsxNoBracesForStringAttributesRule.ts ├── jsxNoClosingBracketNewlineRule.ts ├── noBracesForSingleLineArrowFunctionsRule.ts ├── noPropertyInitializersRule.ts ├── noUnnecessaryParensForArrowFunctionArgumentsRule.ts ├── preferEs6ImportsRule.ts ├── preferOrOperatorOverTernaryRule.ts ├── reactLifecycleOrderRule.ts └── sortImportsRule.ts ├── test ├── camelCaseLocalFunctionsRule │ ├── camelCaseLocalFunctionsRule.tsx.lint │ └── tslint.json ├── classMethodNewlinesRule │ ├── classMethodNewlinesRule.ts.fix │ ├── classMethodNewlinesRule.ts.lint │ └── tslint.json ├── declareClassMethodsAfterUseRule │ ├── declareClassMethodsAfterUseRule.ts.lint │ └── tslint.json ├── jsxAttributeSpacingRule │ ├── jsxAttributeSpacingRule.tsx.fix │ ├── jsxAttributeSpacingRule.tsx.lint │ └── tslint.json ├── jsxExpressionSpacingRule │ ├── jsxExpressionSpacingRule.tsx.fix │ ├── jsxExpressionSpacingRule.tsx.lint │ └── tslint.json ├── jsxNoBracesForStringAttributesRule │ ├── jsxNoBracesForStringAttributesRule.tsx.fix │ ├── jsxNoBracesForStringAttributesRule.tsx.lint │ └── tslint.json ├── jsxNoClosingBracketNewlineRule │ ├── jsxNoClosingBracketNewlineRule.tsx.fix │ ├── jsxNoClosingBracketNewlineRule.tsx.lint │ └── tslint.json ├── noBracesForSingleLineArrowFunctionsRule │ ├── noBracesForSingleLineArrowFunctionsRule.tsx.fix │ ├── noBracesForSingleLineArrowFunctionsRule.tsx.lint │ └── tslint.json ├── noPropertyInitializersRule │ ├── noPropertyInitializersRule.ts.lint │ └── tslint.json ├── noUnnecessaryParensForArrowFunctionArgumentsRule │ ├── noUnnecessaryParensForArrowFunctionArgumentsRule.ts.lint │ └── tslint.json ├── preferEs6ImportsRule │ ├── preferEs6ImportsRule.ts.lint │ └── tslint.json ├── preferOrOperatorOverTernaryRule │ ├── preferOrOperatorOverTernaryRule.ts.fix │ ├── preferOrOperatorOverTernaryRule.ts.lint │ └── tslint.json ├── reactLifecycleOrderRule │ ├── reactLifecycleOrderRule.tsx.lint │ └── tslint.json ├── runner.js ├── sortImportsRule │ ├── sortImportsRule.ts.fix │ ├── sortImportsRule.ts.lint │ └── tslint.json ├── sortImportsRuleWhitespaceInsensitive │ ├── sortImportsRule.ts.fix │ ├── sortImportsRule.ts.lint │ └── tslint.json └── watch.js ├── tsconfig.json ├── tslint.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | *.js.map 3 | *.log 4 | node_modules 5 | typings 6 | !test/runner.js 7 | !test/watch.js 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .travis.yml 2 | .vscode 3 | typings 4 | test 5 | *.ts 6 | *.log 7 | ts*.json 8 | yarn.lock 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 10 5 | - 8 6 | - 6 7 | 8 | env: 9 | - TYPESCRIPT_VERSION="3.1" 10 | - TYPESCRIPT_VERSION="3.0" 11 | - TYPESCRIPT_VERSION="2.9" 12 | - TYPESCRIPT_VERSION="2.8" 13 | - TYPESCRIPT_VERSION="2.7" 14 | - TYPESCRIPT_VERSION="2.6" 15 | - TYPESCRIPT_VERSION="2.5" 16 | - TYPESCRIPT_VERSION="2.4" 17 | - TYPESCRIPT_VERSION="2.3" 18 | - TYPESCRIPT_VERSION="2.2" 19 | - TYPESCRIPT_VERSION="2.1" 20 | 21 | script: 22 | - yarn lint 23 | - yarn add typescript@${TYPESCRIPT_VERSION} 24 | - yarn ci 25 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch", 6 | "type": "node", 7 | "protocol": "inspector", 8 | "request": "launch", 9 | "program": "${workspaceRoot}/node_modules/tslint/lib/tslint-cli.js", 10 | "stopOnEntry": false, 11 | "args": [ 12 | "--test", 13 | "test/ruleNameGoesHere" 14 | ], 15 | "cwd": "${workspaceRoot}", 16 | "preLaunchTask": "build", 17 | "runtimeExecutable": null, 18 | "runtimeArgs": [ 19 | "--nolazy" 20 | ], 21 | "env": { 22 | "NODE_ENV": "development" 23 | }, 24 | "sourceMaps": true 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/*.js": { 4 | "when": "$(basename).ts" 5 | } 6 | }, 7 | "typescript.tsdk": "./node_modules/typescript/lib", 8 | "tslint.autoFixOnSave": true, 9 | "editor.formatOnSave": true, 10 | "files.insertFinalNewline": true 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "npm", 4 | "isShellCommand": true, 5 | "showOutput": "always", 6 | "suppressTaskName": true, 7 | "tasks": [ 8 | { 9 | "taskName": "build", 10 | "args": ["run", "build"] 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Justin Bay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/jwbay/tslint-misc-rules.svg?branch=master)](https://travis-ci.org/jwbay/tslint-misc-rules) 2 | [![npm version](https://img.shields.io/npm/v/tslint-misc-rules.svg?style=flat-square)](https://www.npmjs.com/package/tslint-misc-rules) 3 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/jwbay/tslint-misc-rules/pulls) 4 | [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) 5 | # misc-tslint-rules 6 | 7 | Collection of miscellaneous TSLint rules 8 | 9 | 1. `$ npm install tslint-misc-rules --save-dev` 10 | 2. Add the path to it to your tslint.json and add some rules: 11 | 12 | ```json 13 | { 14 | "rules": { 15 | "sort-imports": true 16 | }, 17 | "rulesDirectory": [ 18 | "tslint-misc-rules" 19 | ] 20 | } 21 | ``` 22 | 23 | # Rules 24 | 25 | 1. [sort-imports](#1) [has autofix] 26 | 1. [prefer-es6-imports](#2) 27 | 1. [class-method-newlines](#3) [has autofix] 28 | 1. [jsx-attribute-spacing](#4) [has autofix] 29 | 1. [jsx-expression-spacing](#5) [has autofix] 30 | 1. [jsx-no-closing-bracket-newline](#6) [has autofix] 31 | 1. [jsx-no-braces-for-string-attributes](#7) [has autofix] 32 | 1. [react-lifecycle-order](#8) 33 | 1. [prefer-or-operator-over-ternary](#9) [has autofix] 34 | 1. [no-property-initializers](#10) 35 | 1. [camel-case-local-function](#11) 36 | 1. [declare-class-methods-after-use](#12) 37 | 1. [no-braces-for-single-line-arrow-functions](#13) [has autofix] 38 | 1. [no-unnecessary-parens-for-arrow-function-arguments](#14) 39 | 40 | ## "sort-imports" [↑](#TOC) 41 | Fails: 42 | ```ts 43 | import b from "b"; 44 | import a from "a"; 45 | ``` 46 | Passes: 47 | ```ts 48 | import a from "a"; 49 | import b from "b"; 50 | ``` 51 | Blocks are grouped, so this also passes: 52 | ```ts 53 | import a from "a"; 54 | import z from "z"; 55 | testSetup(); 56 | import c from "c"; 57 | import d from "d"; 58 | ``` 59 | Precedence is essentially `*`, `{`, then alpha, so: 60 | ```ts 61 | import * as a from "a"; 62 | import { b, c } from "bc"; 63 | import d from "d"; 64 | ``` 65 | This rule has one option, `whitespace-insensitive`, that collapses all whitespace spans (including line breaks) down to one space when sorting. This provides compatibility with formatters like Prettier, which may decide to turn a single-line import into a multi-line import when it grows too long. This could otherwise introduce a lint failure. Ex: 66 | 67 | ```json 68 | "sort-imports": [ true, "whitespace-insensitive" ] 69 | ``` 70 | Fails: 71 | ```ts 72 | import { 73 | x, 74 | y, 75 | } from "xy"; 76 | import { a } from "a"; 77 | ``` 78 | Passes: 79 | ```ts 80 | import { a } from "a"; 81 | import { 82 | x, 83 | y, 84 | } from "xy"; 85 | ``` 86 | 87 | ## "prefer-es6-imports" [↑](#TOC) 88 | With configuration (required): 89 | ```json 90 | { 91 | "prefer-es6-imports": [ 92 | true, 93 | "module-name" 94 | ] 95 | } 96 | ``` 97 | Fails: 98 | ```ts 99 | import mod = require("module-name"); 100 | import mod = require("path/to/module-name"); 101 | import mod = require("../module-name"); 102 | ``` 103 | 104 | ## "class-method-newlines" [↑](#TOC) 105 | Ensure each method in class is preceded by a newline. 106 | 107 | Fails: 108 | ```ts 109 | class foo { 110 | propertyOne: any; 111 | propertyTwo: any; 112 | one() { 113 | } 114 | two() { 115 | } 116 | } 117 | ``` 118 | 119 | Passes: 120 | ```ts 121 | class foo { 122 | propertyOne: any; 123 | propertyTwo: any; 124 | 125 | one() { 126 | } 127 | 128 | two() { 129 | } 130 | } 131 | ``` 132 | 133 | The first method is exempt, so this also passes: 134 | ```ts 135 | class foo { 136 | one() { 137 | } 138 | 139 | two() { 140 | } 141 | } 142 | ``` 143 | 144 | 145 | ## "jsx-attribute-spacing" [↑](#TOC) 146 | Fails: 147 | ```jsx 148 |
149 |
150 |
151 | ``` 152 | Passes: 153 | ```jsx 154 |
155 | ``` 156 | 157 | ## "jsx-expression-spacing" [↑](#TOC) 158 | Fails: 159 | ```jsx 160 |
161 |
162 |
163 |
164 | {value} 165 | { value} 166 | {value } 167 |
168 | ``` 169 | Passes: 170 | ```jsx 171 |
172 |
173 | { value } 174 |
175 | ``` 176 | 177 | ## "jsx-no-closing-bracket-newline" [↑](#TOC) 178 | Fails: 179 | ```jsx 180 | 183 | 184 |
188 | text 189 |
190 | ``` 191 | 192 | Passes: 193 | ```jsx 194 |
196 | 197 |
200 | text 201 |
202 | ``` 203 | 204 | ##
"jsx-no-braces-for-string-attributes" [↑](#TOC) 205 | Fails: 206 | ```jsx 207 |
208 | ``` 209 | Passes: 210 | ```jsx 211 |
212 | ``` 213 | 214 | ## "react-lifecycle-order" [↑](#TOC) 215 | With configuration (optional): 216 | ```json 217 | { 218 | "react-lifecycle-order": [ 219 | true, 220 | "componentWillMount", 221 | "render", 222 | "componentWillUnmount" 223 | ] 224 | } 225 | ``` 226 | Fails: 227 | ```ts 228 | class extends React.Component { 229 | componentWillMount() { 230 | } 231 | 232 | componentWillUnmount() { 233 | } 234 | 235 | render() { 236 | } 237 | } 238 | ``` 239 | 240 | Passes: 241 | ```ts 242 | class extends React.Component { 243 | componentWillMount() { 244 | } 245 | 246 | render() { 247 | } 248 | 249 | componentWillUnmount() { 250 | } 251 | } 252 | ``` 253 | 254 | If configuration is not specified, React's [invocation order](https://facebook.github.io/react/docs/component-specs.html#lifecycle-methods) is used. 255 | 256 | ## "prefer-or-operator-over-ternary" [↑](#TOC) 257 | Fails: 258 | ```ts 259 | const maybeFoo = foo ? foo : bar; 260 | ``` 261 | 262 | Passes: 263 | ```ts 264 | const maybeFoo = foo || bar; 265 | ``` 266 | 267 | ## "no-property-initializers" [↑](#TOC) 268 | Fails: 269 | ```ts 270 | class foo { 271 | bar = 42; 272 | } 273 | ``` 274 | 275 | Passes: 276 | ```ts 277 | class foo { 278 | bar: number; 279 | } 280 | ``` 281 | 282 | ## "camel-case-local-functions" [↑](#TOC) 283 | Due to React's Stateless Functional Components, this rule checks callsites rather than declarations. 284 | Fails: 285 | ```ts 286 | import FooImport from 'foo'; 287 | 288 | function FooDeclaration() { } 289 | 290 | FooImport(); 291 | FooDeclaration(); 292 | ``` 293 | 294 | Passes: 295 | ```jsx 296 | import fooImport from 'foo'; 297 | 298 | function fooDeclaration() { } 299 | function SomeSFC(props) { return null; } 300 | 301 | fooImport(); 302 | fooDeclaration(); 303 | const el = ; 304 | ``` 305 | 306 | ## "declare-class-methods-after-use" [↑](#TOC) 307 | Fails: 308 | ```ts 309 | class foo { 310 | bar() { 311 | } 312 | 313 | foo() { 314 | this.bar(); 315 | } 316 | } 317 | ``` 318 | 319 | Passes: 320 | ```ts 321 | class foo { 322 | foo() { 323 | this.bar(); 324 | } 325 | 326 | bar() { 327 | } 328 | } 329 | ``` 330 | 331 | ## "no-braces-for-single-line-arrow-functions" [↑](#TOC) 332 | 333 | Fails: 334 | ```ts 335 | const add = (x, y) => { return x + y }; 336 | const btn =