diff --git a/.gitignore b/.gitignore index e9444111333..6cebaca4f5b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ .DS_Store +.sass-cache .tscache/ /build/ +/docs/site/ /scripts/*.js /lib/ node_modules/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 234a4b438d0..c61ea64b319 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,24 @@ Change Log === +v4.0.0-dev.0 +--- +* TODO + +v3.15.1 +--- +* Enabled additional rules in `tslint:latest` configuration (#1506) + +v3.15.0 +--- +* Stable release containing changes from the last dev release (v3.15.0-dev.0) + v3.15.0-dev.0 --- * [enhancement] Rules can automatically fix errors (#1423) * [enhancement] Better error messages for invalid source files (#1480) * [new-rule] `adjacent-overload-signatures` rule (#1426) -* [new-rule] `file-header` rule (#1411) +* [new-rule] `file-header` rule (#1441) * [new-rule] `object-literal-shorthand` rule (#1488) * [new-rule-option] `allow-declarations` option for `only-arrow-functions` rule (#1452) * [new-rule-option] `import-sources-order` option for `ordered-imports` rule (#1466) diff --git a/README.md b/README.md index ebb21d3e945..6a8c8323968 100644 --- a/README.md +++ b/README.md @@ -391,7 +391,7 @@ Creating a new release ---------------------- [back to ToC ↑](#table-of-contents) -1. Bump the version number in `package.json` and `src/tslint.ts` +1. Bump the version number in `package.json` and `src/tslintMulti.ts` 2. Add release notes in `CHANGELOG.md` 3. Run `grunt` to build the latest sources 4. Commit with message `Prepare release ` diff --git a/docs/Gemfile b/docs/Gemfile index 598407a3858..053c27dc351 100644 --- a/docs/Gemfile +++ b/docs/Gemfile @@ -1,3 +1,2 @@ -ruby '2.2.3' source 'https://rubygems.org' gem 'github-pages' diff --git a/docs/_data/rules.json b/docs/_data/rules.json index 57a83746dba..1ea749d2770 100644 --- a/docs/_data/rules.json +++ b/docs/_data/rules.json @@ -1,4 +1,14 @@ [ + { + "ruleName": "adjacent-overload-signatures", + "description": "Enforces function overloads to be consecutive.", + "optionsDescription": "Not configurable.", + "options": null, + "optionExamples": [ + "true" + ], + "type": "typescript" + }, { "ruleName": "align", "description": "Enforces vertical alignment.", @@ -116,6 +126,18 @@ ], "type": "maintainability" }, + { + "ruleName": "file-header", + "description": "Enforces a certain header comment for all files, matched by a regular expression.", + "optionsDescription": "Regular expression to match the header.", + "options": { + "type": "string" + }, + "optionExamples": [ + "\"true\", \"Copyright \\d{4}\"" + ], + "type": "style" + }, { "ruleName": "forin", "description": "Requires a `for ... in` statement to be filtered with an `if` statement.", @@ -803,6 +825,15 @@ ], "type": "style" }, + { + "ruleName": "object-literal-shorthand", + "description": "Enforces use of ES6 object literal shorthand when possible.", + "options": null, + "optionExamples": [ + "true" + ], + "type": "style" + }, { "ruleName": "object-literal-sort-keys", "description": "Requires keys in object literals to be sorted alphabetically", @@ -863,10 +894,21 @@ "ruleName": "only-arrow-functions", "description": "Disallows traditional (non-arrow) function expressions.", "rationale": "Traditional functions don't bind lexical scope, which can lead to unexpected behavior when accessing 'this'.", - "optionsDescription": "Not configurable.", - "options": null, + "optionsDescription": "\nOne argument may be optionally provided:\n\n* `\"allow-declarations\"` allows standalone function declarations.\n ", + "options": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "allow-declarations" + ] + }, + "minLength": 0, + "maxLength": 1 + }, "optionExamples": [ - "true" + "true", + "[true, \"allow-declarations\"]" ], "type": "typescript" }, @@ -874,10 +916,18 @@ "ruleName": "ordered-imports", "description": "Requires that import statements be alphabetized.", "descriptionDetails": "\nEnforce a consistent ordering for ES6 imports:\n- Named imports must be alphabetized (i.e. \"import {A, B, C} from \"foo\";\")\n - The exact ordering can be controled by the named-imports-order option.\n - \"longName as name\" imports are ordered by \"longName\".\n- Import sources must be alphabetized within groups, i.e.:\n import * as foo from \"a\";\n import * as bar from \"b\";\n- Groups of imports are delineated by blank lines. You can use these to group imports\n however you like, e.g. by first- vs. third-party or thematically.", - "optionsDescription": "\nYou may set the `\"named-imports-order\"` option to control the ordering of named\nimports (the `{A, B, C}` in 'import {A, B, C} from \"foo\"`.)\n\nPossible values for `\"named-imports-order\"` are:\n\n* `\"case-insensitive'`: Correct order is `{A, b, C}`. (This is the default.)\n* `\"lowercase-first\"`: Correct order is `{b, A, C}`.\n* `\"lowercase-last\"`: Correct order is `{A, C, b}`.\n ", + "optionsDescription": "\nYou may set the `\"import-sources-order\"` option to control the ordering of source\nimports (the `\"foo\"` in `import {A, B, C} from \"foo\"`).\n\nPossible values for `\"import-sources-order\"` are:\n* `\"case-insensitive'`: Correct order is `\"Bar\"`, `\"baz\"`, `\"Foo\"`. (This is the default.)\n* `\"lowercase-first\"`: Correct order is `\"baz\"`, `\"Bar\"`, `\"Foo\"`.\n* `\"lowercase-last\"`: Correct order is `\"Bar\"`, `\"Foo\"`, `\"baz\"`.\n\nYou may set the `\"named-imports-order\"` option to control the ordering of named\nimports (the `{A, B, C}` in `import {A, B, C} from \"foo\"`).\n\nPossible values for `\"named-imports-order\"` are:\n\n* `\"case-insensitive'`: Correct order is `{A, b, C}`. (This is the default.)\n* `\"lowercase-first\"`: Correct order is `{b, A, C}`.\n* `\"lowercase-last\"`: Correct order is `{A, C, b}`.\n\n ", "options": { "type": "object", "properties": { + "import-sources-order": { + "type": "string", + "enum": [ + "case-insensitive", + "lowercase-first", + "lowercase-last" + ] + }, "named-imports-order": { "type": "string", "enum": [ @@ -891,7 +941,7 @@ }, "optionExamples": [ "true", - "[true, {\"named-imports-order\": \"lowercase-first\"}]" + "[true, {\"import-sources-order\": \"lowercase-last\", \"named-imports-order\": \"lowercase-first\"}]" ], "type": "style" }, @@ -1037,13 +1087,14 @@ { "ruleName": "typedef", "description": "Requires type definitions to exist.", - "optionsDescription": "\nSix arguments may be optionally provided:\n\n* `\"call-signature\"` checks return type of functions.\n* `\"parameter\"` checks type specifier of function parameters for non-arrow functions.\n* `\"arrow-parameter\"` checks type specifier of function parameters for arrow functions.\n* `\"property-declaration\"` checks return types of interface properties.\n* `\"variable-declaration\"` checks variable declarations.\n* `\"member-variable-declaration\"` checks member variable declarations.", + "optionsDescription": "\nSeven arguments may be optionally provided:\n\n* `\"call-signature\"` checks return type of functions.\n* `\"arrow-call-signature\"` checks return type of arrow functions.\n* `\"parameter\"` checks type specifier of function parameters for non-arrow functions.\n* `\"arrow-parameter\"` checks type specifier of function parameters for arrow functions.\n* `\"property-declaration\"` checks return types of interface properties.\n* `\"variable-declaration\"` checks variable declarations.\n* `\"member-variable-declaration\"` checks member variable declarations.", "options": { "type": "array", "items": { "type": "string", "enum": [ "call-signature", + "arrow-call-signature", "parameter", "arrow-parameter", "property-declaration", @@ -1052,7 +1103,7 @@ ] }, "minLength": 0, - "maxLength": 6 + "maxLength": 7 }, "optionExamples": [ "[true, \"call-signature\", \"parameter\", \"member-variable-declaration\"]" @@ -1237,7 +1288,7 @@ "check-decl", "check-operator", "check-module", - "check-seperator", + "check-separator", "check-type", "check-typecast" ] diff --git a/docs/_layouts/rule.html b/docs/_layouts/rule.html index 3ba1bc34b0e..de7b8ca3c8c 100644 --- a/docs/_layouts/rule.html +++ b/docs/_layouts/rule.html @@ -9,6 +9,10 @@
Rationale
{{page.rationale | markdownify}} {% endif %} +{% if page.requiresTypeInfo %} + Note: + This rule requires type info to run +{% endif %}

Config

@@ -24,4 +28,4 @@
Examples
Schema
 {{page.optionsJSON}}
-
\ No newline at end of file + diff --git a/docs/_site/2015/12/10/a-new-tslint-website.html b/docs/_site/2015/12/10/a-new-tslint-website.html deleted file mode 100644 index 08fbe1b202f..00000000000 --- a/docs/_site/2015/12/10/a-new-tslint-website.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - A New TSLint Website - - - - - - - - - - - -
- - -
- - -
-
- -
-

A New TSLint Website

- -
- -
-

As TSLint has grown in usage and popularity alongside of TypeScript, it also has -evolved in terms of functionality and complexity. Today, all sorts of projects and products, -from Angular 2 to the TypeScript compiler itself use TSLint -to help keep their code high-quality.

- -

Unfortunately, we’ve done a poor job of scaling the documentation and guides for TSLint as it has grown. -For example, the only good way to see the possible rules TSLint can enforce and what they can do is to scroll through the quite-long TSLint README. -Each rule is accompanied by a short description of its functionality, but nowhere does it explain why the rule is actually useful. -There’s also a short description of the rule’s options, but the syntax for specifying these options is often unclear.

- -

This website, in its current, very simple form, marks the beginning of a renewed focus on developer and user experience. But it’s just the tip of the iceberg in changes to come - other things in progress include:

- -
    -
  • A documentation overhaul that will provide -more comprehensive and clear documentation on TSLint and will make it easier to navigate that documentation.
  • -
  • A new --init feature in the TSLint CLI that will make it easier to -generate a sensible initial tslint.json config file.
  • -
  • An improved contributor experience that will make things easier for those who want to contribute code to TSLint.
  • -
- -

Feedback is always great, so please comment on any of the above GitHub issues and let us know what you would like to see to make TSLint user experience even better!

- - -
- -
- - - -
- - - - diff --git a/docs/_site/2016/03/31/sharable-configurations-rules.html b/docs/_site/2016/03/31/sharable-configurations-rules.html deleted file mode 100644 index 01965d0e7d4..00000000000 --- a/docs/_site/2016/03/31/sharable-configurations-rules.html +++ /dev/null @@ -1,210 +0,0 @@ - - - - - - - - - Sharable Configurations and Rules - - - - - - - - - - - -
- - -
- - -
-
- -
-

Sharable Configurations and Rules

- -
- -
-

With the release of TSLint v3.7.0 comes a few new features that will make configuration files (aka tslint.json files) -easier to maintain and share. The crux of the changes is a new extends field, which when provided indicates that a configuration -file augments another configuration file.

- -

Example

- -

Let’s imagine you’ve created some custom rules and want to share them with others. -You also have a couple of configurations for them you want to share.

- -

Here’s the layout of our NPM package, which we’ll call shared-tslint-rules. We have a directory with rules, -as well as a few different config files for TSLint.

- -

-shared-tslint-rules -├── package.json -├── rules -│   ├── noAdditionRule.js -│   ├── noErrorsRule.js -│   └── noExcessiveCommentingRule.js -├── tslint-base.json -├── tslint-config.json -└── tslint-crazy-config.js -

- -

Our starting-point config file just references the directory the custom rules are in -but doesn’t enable any of them:

- -

tslint-base.json:

- -

json -{ - "rulesDirectory": "./rules" -} -

- -

We also want to provide a sane default config for our rules. -Notice how it extends our base config, so we don’t have to redeclare rulesDirectory here:

- -

tslint-config.json:

- -

json -{ - "extends": "./tslint-base.json", - "rules": { - "no-errors": true, - "no-addition": false - } -} -

- -

Finally, we can even make a crazy config file for fun that gives you back a different config -each time you run TSLint. Notice how this is a .js file that exports an object:

- -

tslint-crazy-config.js

- -

js -module.exports = { - extends: "./tslint-base.json", - rules: { - "no-excessive-commenting": [true, {maxComments: Math.random() * 10}] - } -}; -

- -

Finally, we have our package.json file which references our base config file through its main field:

- -

package.json:

- -

json -{ - "name": "shared-tslint-rules", - "version": "1.0.0", - "description": "Some TSLint rules that are great!", - "main": "tslint-base.json", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "MIT" -} -

- -

We can publish our package on NPM to let the world use it!

- -
- -

Now let’s say we’re a user, and we want to use the custom rules above to lint our code. -First, we’ll make sure we have the necessary npm packages installed:

- -

-npm install -g tslint shared-tslint-rules -

- -

Then, in our tslint.json file for our project, we can reference the package of custom rules with extends:

- -

-{ - "extends": "shared-tslint-rules/tslint-config", - "rules": { - "no-addition": true - } -} -

- -

and that’s all we have to do to use the custom rules! -We can now run TSLint as we would normally and see any lint errors produced by the custom rules:

- -

-tslint -c path/to/tslint.json my/files/**/to/lint.ts -

- - -
- -
- - - -
- - - - diff --git a/docs/_site/Gemfile b/docs/_site/Gemfile deleted file mode 100644 index 598407a3858..00000000000 --- a/docs/_site/Gemfile +++ /dev/null @@ -1,3 +0,0 @@ -ruby '2.2.3' -source 'https://rubygems.org' -gem 'github-pages' diff --git a/docs/_site/circle.yml b/docs/_site/circle.yml deleted file mode 100644 index 1e8b5a738ae..00000000000 --- a/docs/_site/circle.yml +++ /dev/null @@ -1,7 +0,0 @@ -machine: - ruby: - # see available versions here: https://circleci.com/docs/build-image-precise/#ruby - version: 2.2.3 -test: - override: - - bundle exec jekyll build diff --git a/docs/_site/css/main.css b/docs/_site/css/main.css deleted file mode 100644 index 925296b1f92..00000000000 --- a/docs/_site/css/main.css +++ /dev/null @@ -1,909 +0,0 @@ -/*! normalize.css v3.0.2 | MIT License | git.io/normalize */ -/** - * 1. Set default font family to sans-serif. - * 2. Prevent iOS text size adjust after orientation change, without disabling - * user zoom. - */ -html { - font-family: sans-serif; - /* 1 */ - -ms-text-size-adjust: 100%; - /* 2 */ - -webkit-text-size-adjust: 100%; - /* 2 */ } - -/** - * Remove default margin. - */ -body { - margin: 0; } - -/* HTML5 display definitions - ========================================================================== */ -/** - * Correct `block` display not defined for any HTML5 element in IE 8/9. - * Correct `block` display not defined for `details` or `summary` in IE 10/11 - * and Firefox. - * Correct `block` display not defined for `main` in IE 11. - */ -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -main, -menu, -nav, -section, -summary { - display: block; } - -/** - * 1. Correct `inline-block` display not defined in IE 8/9. - * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. - */ -audio, -canvas, -progress, -video { - display: inline-block; - /* 1 */ - vertical-align: baseline; - /* 2 */ } - -/** - * Prevent modern browsers from displaying `audio` without controls. - * Remove excess height in iOS 5 devices. - */ -audio:not([controls]) { - display: none; - height: 0; } - -/** - * Address `[hidden]` styling not present in IE 8/9/10. - * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. - */ -[hidden], -template { - display: none; } - -/* Links - ========================================================================== */ -/** - * Remove the gray background color from active links in IE 10. - */ -a { - background-color: transparent; } - -/** - * Improve readability when focused and also mouse hovered in all browsers. - */ -a:active, -a:hover { - outline: 0; } - -/* Text-level semantics - ========================================================================== */ -/** - * Address styling not present in IE 8/9/10/11, Safari, and Chrome. - */ -abbr[title] { - border-bottom: 1px dotted; } - -/** - * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. - */ -b, -strong { - font-weight: bold; } - -/** - * Address styling not present in Safari and Chrome. - */ -dfn { - font-style: italic; } - -/** - * Address variable `h1` font-size and margin within `section` and `article` - * contexts in Firefox 4+, Safari, and Chrome. - */ -h1 { - font-size: 2em; - margin: 0.67em 0; } - -/** - * Address styling not present in IE 8/9. - */ -mark { - background: #ff0; - color: #000; } - -/** - * Address inconsistent and variable font size in all browsers. - */ -small { - font-size: 80%; } - -/** - * Prevent `sub` and `sup` affecting `line-height` in all browsers. - */ -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; } - -sup { - top: -0.5em; } - -sub { - bottom: -0.25em; } - -/* Embedded content - ========================================================================== */ -/** - * Remove border when inside `a` element in IE 8/9/10. - */ -img { - border: 0; } - -/** - * Correct overflow not hidden in IE 9/10/11. - */ -svg:not(:root) { - overflow: hidden; } - -/* Grouping content - ========================================================================== */ -/** - * Address margin not present in IE 8/9 and Safari. - */ -figure { - margin: 1em 40px; } - -/** - * Address differences between Firefox and other browsers. - */ -hr { - -moz-box-sizing: content-box; - box-sizing: content-box; - height: 0; } - -/** - * Contain overflow in all browsers. - */ -pre { - overflow: auto; } - -/** - * Address odd `em`-unit font size rendering in all browsers. - */ -code, -kbd, -pre, -samp { - font-family: monospace, monospace; - font-size: 1em; } - -/* Forms - ========================================================================== */ -/** - * Known limitation: by default, Chrome and Safari on OS X allow very limited - * styling of `select`, unless a `border` property is set. - */ -/** - * 1. Correct color not being inherited. - * Known issue: affects color of disabled elements. - * 2. Correct font properties not being inherited. - * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. - */ -button, -input, -optgroup, -select, -textarea { - color: inherit; - /* 1 */ - font: inherit; - /* 2 */ - margin: 0; - /* 3 */ } - -/** - * Address `overflow` set to `hidden` in IE 8/9/10/11. - */ -button { - overflow: visible; } - -/** - * Address inconsistent `text-transform` inheritance for `button` and `select`. - * All other form control elements do not inherit `text-transform` values. - * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. - * Correct `select` style inheritance in Firefox. - */ -button, -select { - text-transform: none; } - -/** - * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` - * and `video` controls. - * 2. Correct inability to style clickable `input` types in iOS. - * 3. Improve usability and consistency of cursor style between image-type - * `input` and others. - */ -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; - /* 2 */ - cursor: pointer; - /* 3 */ } - -/** - * Re-set default cursor for disabled elements. - */ -button[disabled], -html input[disabled] { - cursor: default; } - -/** - * Remove inner padding and border in Firefox 4+. - */ -button::-moz-focus-inner, -input::-moz-focus-inner { - border: 0; - padding: 0; } - -/** - * Address Firefox 4+ setting `line-height` on `input` using `!important` in - * the UA stylesheet. - */ -input { - line-height: normal; } - -/** - * It's recommended that you don't attempt to style these elements. - * Firefox's implementation doesn't respect box-sizing, padding, or width. - * - * 1. Address box sizing set to `content-box` in IE 8/9/10. - * 2. Remove excess padding in IE 8/9/10. - */ -input[type="checkbox"], -input[type="radio"] { - box-sizing: border-box; - /* 1 */ - padding: 0; - /* 2 */ } - -/** - * Fix the cursor style for Chrome's increment/decrement buttons. For certain - * `font-size` values of the `input`, it causes the cursor style of the - * decrement button to change from `default` to `text`. - */ -input[type="number"]::-webkit-inner-spin-button, -input[type="number"]::-webkit-outer-spin-button { - height: auto; } - -/** - * 1. Address `appearance` set to `searchfield` in Safari and Chrome. - * 2. Address `box-sizing` set to `border-box` in Safari and Chrome - * (include `-moz` to future-proof). - */ -input[type="search"] { - -webkit-appearance: textfield; - /* 1 */ - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; - /* 2 */ - box-sizing: content-box; } - -/** - * Remove inner padding and search cancel button in Safari and Chrome on OS X. - * Safari (but not Chrome) clips the cancel button when the search input has - * padding (and `textfield` appearance). - */ -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; } - -/** - * Define consistent border, margin, and padding. - */ -fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; } - -/** - * 1. Correct `color` not being inherited in IE 8/9/10/11. - * 2. Remove padding so people aren't caught out if they zero out fieldsets. - */ -legend { - border: 0; - /* 1 */ - padding: 0; - /* 2 */ } - -/** - * Remove default vertical scrollbar in IE 8/9/10/11. - */ -textarea { - overflow: auto; } - -/** - * Don't inherit the `font-weight` (applied by a rule above). - * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. - */ -optgroup { - font-weight: bold; } - -/* Tables - ========================================================================== */ -/** - * Remove most spacing between table cells. - */ -table { - border-collapse: collapse; - border-spacing: 0; } - -td, -th { - padding: 0; } - -/** - * Reset some basic elements - */ -body, h1, h2, h3, h4, h5, h6, -p, blockquote, pre, hr, -dl, dd, ol, ul, figure { - margin: 0; - padding: 0; } - -/** - * Set `margin-bottom` to maintain vertical rhythm - */ -h1, h2, h3, h4, h5, h6, -p, blockquote, pre, -ul, ol, dl, figure, -.highlight { - margin-bottom: 1rem; } - -/** - * Images - */ -img { - max-width: 100%; - vertical-align: middle; } - -/** - * Figures - */ -figure > img { - display: block; } - -figcaption { - font-size: 14px; } - -/** - * Clearfix - */ -.site-header:after, .page:after, .footer-col-wrapper:after { - content: ""; - display: table; - clear: both; } - -/** - * Icons - */ -.icon > svg { - display: inline-block; - width: 16px; - height: 16px; - vertical-align: middle; } - .icon > svg path { - fill: #606c71; } - -* { - box-sizing: border-box; } - -body { - padding: 0; - margin: 0; - font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 16px; - line-height: 1.5; - color: #606c71; } - -a { - color: #1e6bb8; - text-decoration: none; } - a:hover { - text-decoration: underline; } - -h1 { - font-size: 2.5rem; } - -h2 { - font-size: 2rem; } - -h3 { - font-size: 1.6rem; } - -h4 { - font-size: 1.4rem; } - -h5 { - font-size: 1.2rem; } - -h6 { - font-size: 1rem; } - -/** - * Site header - */ -.site-header { - border-bottom: 1px solid rgba(255, 255, 255, 0.2); - padding: 0 20px; - min-height: 56px; } - -.site-title { - font-size: 26px; - line-height: 56px; - margin-bottom: 0; - float: left; } - .site-title, .site-title:visited { - color: rgba(255, 255, 255, 0.7); } - .site-title:hover { - color: rgba(255, 255, 255, 0.8); - text-decoration: none; } - @media screen and (max-width: 36em) { - .site-title { - display: block; - text-align: left; } } - -.site-nav { - float: right; - line-height: 56px; } - .site-nav .page-link { - line-height: 1.5; } - .site-nav .page-link, .site-nav .page-link:visited { - color: rgba(255, 255, 255, 0.7); } - .site-nav .page-link:hover { - color: rgba(255, 255, 255, 0.8); - text-decoration: none; } - .site-nav .page-link:not(:first-child) { - margin-left: 20px; } - @media screen and (max-width: 36em) { - .site-nav .page-link { - display: block; - text-align: left; } - .site-nav .page-link:not(:first-child) { - margin-left: 0; } } - -.btn { - display: inline-block; - margin-bottom: 1rem; - background-color: rgba(255, 255, 255, 0.08); - border-color: rgba(255, 255, 255, 0.2); - border-style: solid; - border-width: 1px; - border-radius: 0.3rem; - transition: color 0.2s, background-color 0.2s, border-color 0.2s; } - .btn, .btn:visited { - color: rgba(255, 255, 255, 0.7); } - .btn:hover { - color: rgba(255, 255, 255, 0.8); - text-decoration: none; - background-color: rgba(255, 255, 255, 0.2); - border-color: rgba(255, 255, 255, 0.3); } - .btn + .btn { - margin-left: 1rem; } - @media screen and (min-width: 64em) { - .btn { - padding: 0.75rem 1rem; } } - @media screen and (min-width: 36em) and (max-width: 64em) { - .btn { - padding: 0.6rem 0.9rem; - font-size: 0.9rem; } } - @media screen and (max-width: 36em) { - .btn { - display: block; - width: 100%; - padding: 0.75rem; - font-size: 0.9rem; } - .btn + .btn { - margin-top: 1rem; - margin-left: 0; } } - -.header { - color: #fff; - text-align: center; - background-color: #159957; - background-image: linear-gradient(120deg, #155799, #159957); } - -@media screen and (min-width: 64em) { - .page-header { - padding: 3rem; } } -@media screen and (min-width: 36em) and (max-width: 64em) { - .page-header { - padding: 2rem; } } -@media screen and (max-width: 36em) { - .page-header { - padding: 1rem; } } - -.project-name { - margin-top: 0; - margin-bottom: 0.1rem; } - @media screen and (min-width: 64em) { - .project-name { - font-size: 3.25rem; } } - @media screen and (min-width: 36em) and (max-width: 64em) { - .project-name { - font-size: 2.25rem; } } - @media screen and (max-width: 36em) { - .project-name { - font-size: 1.75rem; } } - -.project-tagline { - margin-bottom: 2rem; - font-weight: normal; - opacity: 0.7; } - @media screen and (min-width: 64em) { - .project-tagline { - font-size: 1.25rem; } } - @media screen and (min-width: 36em) and (max-width: 64em) { - .project-tagline { - font-size: 1.15rem; } } - @media screen and (max-width: 36em) { - .project-tagline { - font-size: 1rem; } } - -.main-content :first-child { - margin-top: 0; } -@media screen and (min-width: 64em) { - .main-content { - max-width: 68rem; - padding: 2rem 6rem; - margin: 0 auto; - font-size: 1.1rem; } } -@media screen and (min-width: 36em) and (max-width: 64em) { - .main-content { - padding: 2rem 4rem; - font-size: 1.1rem; } } -@media screen and (max-width: 36em) { - .main-content { - padding: 2rem 1rem; - font-size: 1rem; } } -.main-content img { - max-width: 100%; } -.main-content h1, -.main-content h2, -.main-content h3, -.main-content h4, -.main-content h5, -.main-content h6 { - margin-top: 2rem; - margin-bottom: 1rem; - font-weight: normal; - color: #159957; } -.main-content p { - margin-bottom: 1rem; } -.main-content code { - padding: 2px 4px; - font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; - font-size: 0.9rem; - color: #383e41; - background-color: #f3f6fa; - border-radius: 0.3rem; } -.main-content pre { - padding: 0.8rem; - margin-top: 0; - margin-bottom: 1rem; - font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; - color: #567482; - word-wrap: normal; - background-color: #f3f6fa; - border: solid 1px #dce6f0; - border-radius: 0.3rem; } - .main-content pre > code { - padding: 0; - margin: 0; - font-size: 0.9rem; - color: #567482; - word-break: normal; - white-space: pre; - background: transparent; - border: 0; } -.main-content .highlight { - margin-bottom: 1rem; } - .main-content .highlight pre { - margin-bottom: 0; - word-break: normal; } -.main-content .highlight pre, -.main-content pre { - padding: 0.8rem; - overflow: auto; - font-size: 0.9rem; - line-height: 1.45; - border-radius: 0.3rem; - -webkit-overflow-scrolling: touch; } -.main-content pre code, -.main-content pre tt { - display: inline; - max-width: initial; - padding: 0; - margin: 0; - overflow: initial; - line-height: inherit; - word-wrap: normal; - background-color: transparent; - border: 0; } - .main-content pre code:before, .main-content pre code:after, - .main-content pre tt:before, - .main-content pre tt:after { - content: normal; } -.main-content ul, -.main-content ol { - margin-top: 0; - margin-left: 30px; - margin-bottom: 1rem; } - .main-content ul ul, - .main-content ul ol, - .main-content ol ul, - .main-content ol ol { - margin-bottom: 0; } -.main-content blockquote { - padding: 0 1rem; - margin-left: 0; - color: #819198; - border-left: 0.3rem solid #dce6f0; } - .main-content blockquote > :first-child { - margin-top: 0; } - .main-content blockquote > :last-child { - margin-bottom: 0; } -.main-content table { - display: block; - width: 100%; - overflow: auto; - word-break: normal; - word-break: keep-all; - -webkit-overflow-scrolling: touch; } - .main-content table th { - font-weight: bold; } - .main-content table th, - .main-content table td { - padding: 0.5rem 1rem; - border: 1px solid #e9ebec; } -.main-content dl { - padding: 0; } - .main-content dl dt { - padding: 0; - margin-top: 1rem; - font-size: 1rem; - font-weight: bold; } - .main-content dl dd { - padding: 0; - margin-bottom: 1rem; } -.main-content hr { - height: 2px; - padding: 0; - margin: 1rem 0; - background-color: #eff0f1; - border: 0; } - -.page { - width: 100%; } - -.page-content { - width: 80%; - padding: 1rem; - float: left; } - -.page-sidebar { - width: 20%; - padding: 1rem; - float: left; } - .page-sidebar .active { - font-style: italic; } - -.sidebar-title { - border-bottom: 1px solid #159957; } - -ul.sidebar-links { - list-style: none; - margin-left: 0; } - ul.sidebar-links h6 { - margin-bottom: 0.33rem; } - -/** - * Posts - */ -ul.post-list { - margin-left: 0; - list-style: none; } - ul.post-list > li { - margin-bottom: 1rem; } - -.post-meta { - font-size: 14px; - color: #828282; - font-style: italic; } - -.post-link { - display: inline-block; - color: inherit; } - -.post-header { - margin-bottom: 2rem; } - -.post-title { - letter-spacing: -1px; - line-height: 1; } - -/** - * Site footer - */ -.site-footer { - padding-top: 2rem; - margin-top: 2rem; - border-top: solid 1px #eff0f1; - font-size: 0.9rem; } - -ul.contact-list, -ul.social-media-list { - list-style: none; - margin-left: 0; } - -.footer-col { - float: left; } - -.footer-col-2 { - float: right; } - @media screen and (max-width: 36em) { - .footer-col-2 { - float: left; } } - -/** - * Syntax highlighting styles - */ -.highlight { - background: #fff; } - .highlight .c { - color: #998; - font-style: italic; } - .highlight .err { - color: #a61717; - background-color: #e3d2d2; } - .highlight .k { - font-weight: bold; } - .highlight .o { - font-weight: bold; } - .highlight .cm { - color: #998; - font-style: italic; } - .highlight .cp { - color: #999; - font-weight: bold; } - .highlight .c1 { - color: #998; - font-style: italic; } - .highlight .cs { - color: #999; - font-weight: bold; - font-style: italic; } - .highlight .gd { - color: #000; - background-color: #fdd; } - .highlight .gd .x { - color: #000; - background-color: #faa; } - .highlight .ge { - font-style: italic; } - .highlight .gr { - color: #a00; } - .highlight .gh { - color: #999; } - .highlight .gi { - color: #000; - background-color: #dfd; } - .highlight .gi .x { - color: #000; - background-color: #afa; } - .highlight .go { - color: #888; } - .highlight .gp { - color: #555; } - .highlight .gs { - font-weight: bold; } - .highlight .gu { - color: #aaa; } - .highlight .gt { - color: #a00; } - .highlight .kc { - font-weight: bold; } - .highlight .kd { - font-weight: bold; } - .highlight .kp { - font-weight: bold; } - .highlight .kr { - font-weight: bold; } - .highlight .kt { - color: #458; - font-weight: bold; } - .highlight .m { - color: #099; } - .highlight .s { - color: #d14; } - .highlight .na { - color: #008080; } - .highlight .nb { - color: #0086B3; } - .highlight .nc { - color: #458; - font-weight: bold; } - .highlight .no { - color: #008080; } - .highlight .ni { - color: #800080; } - .highlight .ne { - color: #900; - font-weight: bold; } - .highlight .nf { - color: #900; - font-weight: bold; } - .highlight .nn { - color: #555; } - .highlight .nt { - color: #000080; } - .highlight .nv { - color: #008080; } - .highlight .ow { - font-weight: bold; } - .highlight .w { - color: #bbb; } - .highlight .mf { - color: #099; } - .highlight .mh { - color: #099; } - .highlight .mi { - color: #099; } - .highlight .mo { - color: #099; } - .highlight .sb { - color: #d14; } - .highlight .sc { - color: #d14; } - .highlight .sd { - color: #d14; } - .highlight .s2 { - color: #d14; } - .highlight .se { - color: #d14; } - .highlight .sh { - color: #d14; } - .highlight .si { - color: #d14; } - .highlight .sx { - color: #d14; } - .highlight .sr { - color: #009926; } - .highlight .s1 { - color: #d14; } - .highlight .ss { - color: #990073; } - .highlight .bp { - color: #999; } - .highlight .vc { - color: #008080; } - .highlight .vg { - color: #008080; } - .highlight .vi { - color: #008080; } - .highlight .il { - color: #099; } diff --git a/docs/_site/develop/code-guidelines/index.html b/docs/_site/develop/code-guidelines/index.html deleted file mode 100644 index 8274dba44b8..00000000000 --- a/docs/_site/develop/code-guidelines/index.html +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - - - - Code Guidelines - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Code Guidelines

-

-
- - -

Rules

- -
Does the rule belong in TSLint?
- -
    -
  • The rule should enforce something applicable to TS in general and not something only applicable to a third-party library.
  • -
  • The rule should enforce something applicable to most TS developers.
  • -
  • Does the rule, in any possible configuration, conflict with any other rules? This should be avoided as much as possible.
  • -
- -
Code-review checklist for new rules
- -
    -
  • Is the name of the rule clear? Does it satisfy the naming guidelines? (TODO: document these naming guidelines)
  • -
  • Does the name of the rule allow for possible future ways it might be changed/improved?
  • -
  • Does the camelCase file name for the rule match match with the kebab-case name of the rule specified in the rule’s metadata, -in the config files for the rule’s tests, and directory name whcih contains the tests?
  • -
  • Does the rule have appropriate, thorough tests?
  • -
  • Should the rule be added to the tslint:latest configuration?
  • -
  • Are the names of the rule’s options clear and consistent?
  • -
  • Does the rule have a good failure message? (TODO: elaborate on what this means)
  • -
- -

Formatters

- -

General Code Style

- -
    -
  • Newlines should be at the end of all files
  • -
- -
-
- - - - - - - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/develop/contributing/index.html b/docs/_site/develop/contributing/index.html deleted file mode 100644 index af378004586..00000000000 --- a/docs/_site/develop/contributing/index.html +++ /dev/null @@ -1,168 +0,0 @@ - - - - - - - - - Contributing - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Contributing

-

-
- - -

To develop TSLint simply clone the repository, install dependencies and run grunt:

- -

bash -git clone git@github.com:palantir/tslint.git -npm install -grunt -

- -

next branch

- -

The next branch of the TSLint repo tracks the latest TypeScript -compiler as a devDependency. This allows you to develop the linter and its rules against the latest features of the -language. Releases from this branch are published to npm with the next dist-tag, so you can get the latest dev -version of TSLint via npm install tslint@next.

- -
-
- - - - - - - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/develop/custom-formatters/index.html b/docs/_site/develop/custom-formatters/index.html deleted file mode 100644 index d73743b9b66..00000000000 --- a/docs/_site/develop/custom-formatters/index.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - Custom Formatters - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Custom Formatters

-

-
- - -

Just like custom rules, additional formatters can also be supplied to TSLint via --formatters-dir on the CLI or formattersDirectory option on the library or grunt-tslint. Writing a new formatter is simpler than writing a new rule, as shown in the JSON formatter’s code.

- -

```ts -import * as ts from “typescript”; -import * as Lint from “tslint/lib/lint”;

- -

export class Formatter extends Lint.Formatters.AbstractFormatter { - public format(failures: Lint.RuleFailure[]): string { - var failuresJSON = failures.map((failure: Lint.RuleFailure) => failure.toJson()); - return JSON.stringify(failuresJSON); - } -} -```

- -

Such custom formatters can also be written in Javascript. Additionally, formatter files are always named with the suffix Formatter, and referenced from TSLint without its suffix.

- - -
-
- - - - - - - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/develop/custom-rules/index.html b/docs/_site/develop/custom-rules/index.html deleted file mode 100644 index 5ec2341c7f4..00000000000 --- a/docs/_site/develop/custom-rules/index.html +++ /dev/null @@ -1,209 +0,0 @@ - - - - - - - - - Custom Rules - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Custom Rules

-

-
- - -

TSLint ships with a set of core rules that can be configured. However, users are also enabled to write their own rules, which allows them to enforce specific behavior not covered by the core of TSLint. TSLint’s internal rules are itself written to be pluggable, so adding a new rule is as simple as creating a new rule file named by convention. New rules can be written in either TypeScript or Javascript; if written in TypeScript, the code must be compiled to Javascript before registering them with TSLint.

- -

Important conventions: Rule identifiers are always kebab-cased. Their implementation files are always camelCasedRule.ts and must contain the suffix Rule.

- -

Let us take the example of how to write a new rule to forbid all import statements (you know, for science). Let us name the rule file noImportsRule.ts. Rules are referenced in tslint.json with their kebab-cased identifer, so "no-imports": true would configure the rule.

- -

Now, let us first write the rule in TypeScript. A few things to note:

- -
    -
  • We import tslint/lib/lint to get the whole Lint namespace instead of just the Linter class.
  • -
  • The exported class must always be named Rule and extend from Lint.Rules.AbstractRule.
  • -
- -

```ts -import * as ts from “typescript”; -import * as Lint from “tslint/lib/lint”;

- -

export class Rule extends Lint.Rules.AbstractRule { - public static FAILURE_STRING = “import statement forbidden”;

- -
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
-    return this.applyWithWalker(new NoImportsWalker(sourceFile, this.getOptions()));
-} }
-
- -

// The walker takes care of all the work. -class NoImportsWalker extends Lint.RuleWalker { - public visitImportDeclaration(node: ts.ImportDeclaration) { - // create a failure at the current position - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING));

- -
    // call the base version of this visitor to actually parse this node
-    super.visitImportDeclaration(node);
-} } ```
-
- -

Given a walker, TypeScript’s parser visits the AST using the visitor pattern. So the rule walkers only need to override the appropriate visitor methods to enforce its checks. For reference, the base walker can be found in syntaxWalker.ts.

- -

We still need to hook up this new rule to TSLint. First make sure to compile noImportsRule.ts:

- -

bash -tsc --noImplicitAny noImportsRule.ts -

- -

Then, if using the CLI, provide the directory that contains this rule as an option to --rules-dir. If using TSLint as a library or via grunt-tslint, the options hash must contain "rulesDirectory": "...". If you run the linter, you’ll see that we have now successfully banned all import statements via TSLint!

- -

Finally, add a line to your tslint.json config file for each of your custom rules.

- -

Final notes:

- -
    -
  • Core rules cannot be overwritten with a custom implementation.
  • -
  • Custom rules can also take in options just like core rules (retrieved via this.getOptions()).
  • -
- - -
-
- - - - - - - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/develop/docs/index.html b/docs/_site/develop/docs/index.html deleted file mode 100644 index c4b0315b1a4..00000000000 --- a/docs/_site/develop/docs/index.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - - - - Docs Development - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Docs Development

-

-
- - -

This docs site is a Jekyll site hosted on GitHub pages. -It is maintained in the gh-pages branch of TSLint. -To contribute to the docs, whether it be better styling, functionality, or content, just create a PR targeted at the gh-pages branch.

- -

Creating New Pages

-

To create a new page, follow the pattern of existing pages. You’ll also need to add appropriate metadata in the _data/*_sidebar.json data file if you want it to show up in a sidebar.

- -

Creating News Posts

-

To create a new news post, simply add a new markdown file to the _posts directory, following the same pattern as existing ones.

- -

Updating Rule Documentation

- -

The documentation for rules is automatically generated from the metadata supplied by each rule. -If you’d like to help improve documentation for them, simply file a PR improving a rule’s metadata and a project collaborator will take care of regenerating the docs site once your PR is merged.

- -

There’s a little complexity in the automatic generation of rules documentation: code from either the master or next branch of the project needs to write files to the gh-pages branch. This is accomplished by writing into a sibling directory of the TSLint repo named tslint-gh-pages. (See the docs/buildDocs.ts script).

- -

If you have TSLint cloned somewhere on your machine and are using Git 2.5+, you can get this directory structure set up by using the git worktree command:

- -

-git worktree add -b gh-pages ../tslint-gh-pages origin/gh-pages -

- -

If you’re using an older version of git, you can simply clone the repo again into a sibling directory named tslint-gh-pages and then git checkout the gh-pages branch.

- -

Once you have the directories set up properly, go to your main TSLint directory, run grunt to build the source code and then grunt docs to regenerate the rules docs.

- - -
-
- - - - - - - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/develop/testing-rules/index.html b/docs/_site/develop/testing-rules/index.html deleted file mode 100644 index cfd574cc037..00000000000 --- a/docs/_site/develop/testing-rules/index.html +++ /dev/null @@ -1,274 +0,0 @@ - - - - - - - - - Testing Rules - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Testing Rules

-

-
- - -

Every TSLint rule has a corresponding directory inside test/rules/ which contains one or more test cases for the rule.

- -

Each test case contains:

- -
    -
  • A tslint.json file which specifies the configuration for TSLint to use
  • -
  • .ts.lint test files which contain TypeScript code and a special markup which indicate where lint failures should be found
  • -
- -

The test system lints the .ts.lint files with the settings specified in tslint.json and makes sure that failures generated by the TSLint match the failures marked in the .ts.lint files.

- -

Example

- -
Testing a New Rule
- -

Say we’re contributing a new rule to TSLint that bans the use of animal variable names and we now need to test it. First we create our configuration file which enables only the rule to be tested:

- -

test/rules/no-animal-variable-names/default/tslint.json:

- -

-{ - "rules": { - "no-animal-variable-names": true - } -} -

- -

In this case, we placed our configuration inside no-animal-variable-names/default to indicate that we’re testing the default configuration for our rule.

- -

Now let’s make the actual test file:

- -

test/rules/no-animal-variable-names/default/test.ts.lint:

- -

``` -const octopus = 5; - ~~~~~~~ [Variables named after animals are not allowed!]

- -

let giraffe: number, tiger: number; - ~~~~~~~ [Variables named after animals are not allowed!] - ~~~~~ [Variables named after animals are not allowed!]

- -

const tree = 5; -const skyscraper = 100; -```

- -

In the above file, ~ characters “underline” where our rule should generate a lint failure -and the message that should be produced appears in brackets afterwards.

- -

If multiple lint failures occur on the same line of TypeScript code, the markup for them is placed on consecutive lines, -as shown in the above example with the line let giraffe: number, tiger: number;

- -

Notice how we also include lines of code that shouldn’t generate lint failures. -This is important to ensure that the rule isn’t creating false-positives.

- -

We can now run grunt test to make sure that our rule produces the output we expect.

- -
Testing a New Rule Option
- -

Let’s look at one more example. Say we’ve added an also-no-plants option to our rule above that disallows variable names that are plants. We should add a test for this new option:

- -

test/rules/no-animal-variable-names/also-no-plants/tslint.json:

- -

-{ - "rules": { - "no-animal-variable-names": [true, "also-no-plants"] - } -} -

- -

test/rules/no-animal-variable-names/also-no-plants/test.ts.lint:

- -

``` -const octopus = 5; - ~~~~~~~ no-animal

- -

let giraffe: number, tiger: number; - ~~~~~~~ no-animal - ~~~~~ no-animal

- -

const tree = 5; - ~~~~ no-plant -const skyscraper = 100;

- -

```

- -

We’ve now used a special message shorthand syntax so we don’t have to type out the same failure message over and over. -Instead of writing out the full lint failure message after each occurance of it, we instead just specify a shortcut name. -(Shortcut names can only consist of letters, numbers, underscores, and hyphens.) -Then, at the bottom of our test file, we specify what full message each shortcut should expand to.

- -

Again, we can run grunt test to make sure our rule is producing the output we expect. If it isn’t we’ll see the difference between the output from the rule and the output we marked.

- -

Tips & Tricks

- -
    -
  • -

    You can use this system to test rules outside of the TSLint build! Use the tslint --test path/to/dir command to test your own custom rules. -The directory you pass should contain a tslint.json file and .ts.lint files. You can try this out on the TSLint rule test cases, for example, tslint --test path/to/tslint-code/test/rules/quotemark/single.

    -
  • -
  • -

    Lint failures sometimes span over multiple lines. To handle this case, don’t specify a message until the end of the error. For example:

    -
  • -
- -

-for (let key in obj) { -~~~~~~~~~~~~~~~~~~~~~~ - console.log(key); -~~~~~~~~~~~~~~~~~~~ -} -~ [for-in loops are not allowed] -

- -
    -
  • If for some weird reason your lint rule generates a failure that has zero width, you can use the ~nil mark to indicate this. -In all reality though, you shouldn’t have rules that do this, and this feature is more of a carryover to accomodate testing -some of TSLint’s legacy rules.
  • -
- - -
-
- - - - - - - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/feed.xml b/docs/_site/feed.xml deleted file mode 100644 index 0518506a96a..00000000000 --- a/docs/_site/feed.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - - TSLint - TSLint documentation. A linter for the TypeScript language. - - https://palantir.github.io//tslint/ - - Mon, 18 Jul 2016 14:56:16 -0400 - Mon, 18 Jul 2016 14:56:16 -0400 - Jekyll v2.4.0 - - - Sharable Configurations and Rules - <p>With the release of <a href="https://github.com/palantir/tslint/releases">TSLint v3.7.0</a> comes a few new features that will make configuration files (aka <a href="/tslint/usage/tslint-json"><code>tslint.json</code> files</a>) -easier to maintain and share. The crux of the changes is a new <code>extends</code> field, which when provided indicates that a configuration -file augments another configuration file.</p> - -<h3 id="example">Example</h3> - -<p>Let’s imagine you’ve created some custom rules and want to share them with others. -You also have a couple of configurations for them you want to share.</p> - -<p>Here’s the layout of our NPM package, which we’ll call <code>shared-tslint-rules</code>. We have a directory with rules, -as well as a few different config files for TSLint.</p> - -<p><code> -shared-tslint-rules -├── package.json -├── rules -│   ├── noAdditionRule.js -│   ├── noErrorsRule.js -│   └── noExcessiveCommentingRule.js -├── tslint-base.json -├── tslint-config.json -└── tslint-crazy-config.js -</code></p> - -<p>Our starting-point config file just references the directory the custom rules are in -but doesn’t enable any of them:</p> - -<p><strong>tslint-base.json</strong>:</p> - -<p><code>json -{ - "rulesDirectory": "./rules" -} -</code></p> - -<p>We also want to provide a sane default config for our rules. -Notice how it extends our base config, so we don’t have to redeclare <code>rulesDirectory</code> here:</p> - -<p><strong>tslint-config.json</strong>:</p> - -<p><code>json -{ - "extends": "./tslint-base.json", - "rules": { - "no-errors": true, - "no-addition": false - } -} -</code></p> - -<p>Finally, we can even make a crazy config file for fun that gives you back a different config -each time you run TSLint. Notice how this is a <code>.js</code> file that exports an object:</p> - -<p><strong>tslint-crazy-config.js</strong></p> - -<p><code>js -module.exports = { - extends: "./tslint-base.json", - rules: { - "no-excessive-commenting": [true, {maxComments: Math.random() * 10}] - } -}; -</code></p> - -<p>Finally, we have our <code>package.json</code> file which references our base config file through its <code>main</code> field:</p> - -<p><strong>package.json</strong>:</p> - -<p><code>json -{ - "name": "shared-tslint-rules", - "version": "1.0.0", - "description": "Some TSLint rules that are great!", - "main": "tslint-base.json", - "scripts": { - "test": "echo \"Error: no test specified\" &amp;&amp; exit 1" - }, - "author": "", - "license": "MIT" -} -</code></p> - -<p>We can publish our package on NPM to let the world use it!</p> - -<hr /> - -<p>Now let’s say we’re a user, and we want to use the custom rules above to lint our code. -First, we’ll make sure we have the necessary npm packages installed:</p> - -<p><code> -npm install -g tslint shared-tslint-rules -</code></p> - -<p>Then, in our <code>tslint.json</code> file for our project, we can reference the package of custom rules with <code>extends</code>:</p> - -<p><code> -{ - "extends": "shared-tslint-rules/tslint-config", - "rules": { - "no-addition": true - } -} -</code></p> - -<p>and that’s all we have to do to use the custom rules! -We can now run TSLint as we would normally and see any lint errors produced by the custom rules:</p> - -<p><code> -tslint -c path/to/tslint.json my/files/**/to/lint.ts -</code></p> - - - Thu, 31 Mar 2016 11:19:00 -0400 - https://palantir.github.io//tslint/2016/03/31/sharable-configurations-rules.html - https://palantir.github.io//tslint/2016/03/31/sharable-configurations-rules.html - - - - - - A New TSLint Website - <p>As TSLint has grown in usage and popularity alongside of TypeScript, it also has -evolved in terms of functionality and complexity. Today, all sorts of projects and products, -from <a href="https://angular.io/">Angular 2</a> to the <a href="https://github.com/Microsoft/TypeScript">TypeScript compiler itself</a> use TSLint -to help keep their code high-quality.</p> - -<p>Unfortunately, we’ve done a poor job of scaling the documentation and guides for TSLint as it has grown. -For example, the only good way to see the possible rules TSLint can enforce and what they can do is to scroll through the quite-long <a href="https://github.com/palantir/tslint/blob/409aa6e4aa4b63da11fd61e15b26b0100cf1e845/README.md">TSLint README</a>. -Each rule is accompanied by a short description of its functionality, but nowhere does it explain why the rule is actually useful. -There’s also a short description of the rule’s options, but the syntax for specifying these options is often unclear.</p> - -<p>This website, in its current, very simple form, marks the beginning of a renewed focus on developer and user experience. But it’s just the tip of the iceberg in changes to come - other things in progress include:</p> - -<ul> - <li><a href="https://github.com/palantir/tslint/issues/830">A documentation overhaul</a> that will provide -more comprehensive and clear documentation on TSLint and will make it easier to navigate that documentation.</li> - <li><a href="https://github.com/palantir/tslint/pull/871">A new <code>--init</code> feature</a> in the TSLint CLI that will make it easier to -generate a sensible initial <code>tslint.json</code> config file.</li> - <li><a href="https://github.com/palantir/tslint/issues/831">An improved contributor experience</a> that will make things easier for those who want to contribute code to TSLint.</li> -</ul> - -<p>Feedback is always great, so please comment on any of the above GitHub issues and let us know what you would like to see to make TSLint user experience even better!</p> - - - Thu, 10 Dec 2015 15:15:21 -0500 - https://palantir.github.io//tslint/2015/12/10/a-new-tslint-website.html - https://palantir.github.io//tslint/2015/12/10/a-new-tslint-website.html - - - - - - diff --git a/docs/_site/index.html b/docs/_site/index.html deleted file mode 100644 index cd58c4e0be9..00000000000 --- a/docs/_site/index.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - - TSLint - - - - - - - - - - - -
- - - - -
- - -
-

TSLint checks your TypeScript code for readability, maintainability, and functionality errors.

- -

Getting Started

- -

Local installation

- -

sh -$ npm install tslint typescript --save-dev -

- -

Global installation

- -

sh -$ npm install tslint typescript -g -

- -

Configuration

- -

Generate a skeleton tslint.json config file with the --init CLI flag:

- -

sh -cd path/to/project -tslint --init -

- -

Lint your TypeScript files!

- -

-// provide globs as strings -tslint -c path/to/tslint.json 'path/to/project/**/*.ts' -

- -

Check out the full usage guide to learn more.

- - - - -
- - - - diff --git a/docs/_site/news/index.html b/docs/_site/news/index.html deleted file mode 100644 index fde7f576e62..00000000000 --- a/docs/_site/news/index.html +++ /dev/null @@ -1,242 +0,0 @@ - - - - - - - - - News - - - - - - - - - - - -
- - -
- - -
-
-
- - - -
-

Sharable Configurations and Rules

- -
-
-

With the release of TSLint v3.7.0 comes a few new features that will make configuration files (aka tslint.json files) -easier to maintain and share. The crux of the changes is a new extends field, which when provided indicates that a configuration -file augments another configuration file.

- -

Example

- -

Let’s imagine you’ve created some custom rules and want to share them with others. -You also have a couple of configurations for them you want to share.

- -

Here’s the layout of our NPM package, which we’ll call shared-tslint-rules. We have a directory with rules, -as well as a few different config files for TSLint.

- -

-shared-tslint-rules -├── package.json -├── rules -│   ├── noAdditionRule.js -│   ├── noErrorsRule.js -│   └── noExcessiveCommentingRule.js -├── tslint-base.json -├── tslint-config.json -└── tslint-crazy-config.js -

- -

Our starting-point config file just references the directory the custom rules are in -but doesn’t enable any of them:

- -

tslint-base.json:

- -

json -{ - "rulesDirectory": "./rules" -} -

- -

We also want to provide a sane default config for our rules. -Notice how it extends our base config, so we don’t have to redeclare rulesDirectory here:

- -

tslint-config.json:

- -

json -{ - "extends": "./tslint-base.json", - "rules": { - "no-errors": true, - "no-addition": false - } -} -

- -

Finally, we can even make a crazy config file for fun that gives you back a different config -each time you run TSLint. Notice how this is a .js file that exports an object:

- -

tslint-crazy-config.js

- -

js -module.exports = { - extends: "./tslint-base.json", - rules: { - "no-excessive-commenting": [true, {maxComments: Math.random() * 10}] - } -}; -

- -

Finally, we have our package.json file which references our base config file through its main field:

- -

package.json:

- -

json -{ - "name": "shared-tslint-rules", - "version": "1.0.0", - "description": "Some TSLint rules that are great!", - "main": "tslint-base.json", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "MIT" -} -

- -

We can publish our package on NPM to let the world use it!

- -
- -

Now let’s say we’re a user, and we want to use the custom rules above to lint our code. -First, we’ll make sure we have the necessary npm packages installed:

- -

-npm install -g tslint shared-tslint-rules -

- -

Then, in our tslint.json file for our project, we can reference the package of custom rules with extends:

- -

-{ - "extends": "shared-tslint-rules/tslint-config", - "rules": { - "no-addition": true - } -} -

- -

and that’s all we have to do to use the custom rules! -We can now run TSLint as we would normally and see any lint errors produced by the custom rules:

- -

-tslint -c path/to/tslint.json my/files/**/to/lint.ts -

- - -
- -
- - -

Also Read...

- - - - -
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/align/index.html b/docs/_site/rules/align/index.html deleted file mode 100644 index 16f04d914aa..00000000000 --- a/docs/_site/rules/align/index.html +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - - - - Rule: align - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: align

-

-
- - -

Enforces vertical alignment.

- - - -
Rationale
-

Helps maintain a readable, consistent style in your codebase.

- - - - -

Config

- -

Three arguments may be optionally provided:

- -
    -
  • "parameters" checks alignment of function parameters.
  • -
  • "arguments" checks alignment of function call arguments.
  • -
  • "statements" checks alignment of statements.
  • -
- - -
Examples
- -
-"align": [true, "parameters", "statements"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "arguments",
-      "parameters",
-      "statements"
-    ]
-  },
-  "minLength": 1,
-  "maxLength": 3
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/ban/index.html b/docs/_site/rules/ban/index.html deleted file mode 100644 index 5264fccde95..00000000000 --- a/docs/_site/rules/ban/index.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - - - Rule: ban - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: ban

-

-
- - -

Bans the use of specific functions.

- - -

At this time, there is no way to disable global methods with this rule.

- - - - - -

Config

-

A list of ['object', 'method'] pairs which ban object.method().

- - -
Examples
- -
-"ban": [true, ["console", "log"], ["someObject", "someFunction"]]
-
- - -
Schema
-
-{
-  "type": "list",
-  "listType": {
-    "type": "array",
-    "arrayMembers": [
-      {
-        "type": "string"
-      },
-      {
-        "type": "string"
-      }
-    ]
-  }
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/class-name/index.html b/docs/_site/rules/class-name/index.html deleted file mode 100644 index 7443171d9fb..00000000000 --- a/docs/_site/rules/class-name/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - Rule: class-name - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: class-name

-

-
- - -

Enforces PascalCased class and interface names.

- - - -
Rationale
-

Makes it easy to differentitate classes from regular variables at a glance.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"class-name": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/comment-format/index.html b/docs/_site/rules/comment-format/index.html deleted file mode 100644 index 4c54fa06138..00000000000 --- a/docs/_site/rules/comment-format/index.html +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - - - - Rule: comment-format - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: comment-format

-

-
- - -

Enforces formatting rules for single-line comments.

- - - -
Rationale
-

Helps maintain a consistent, readable style in your codebase.

- - - - -

Config

- -

Three arguments may be optionally provided:

- -
    -
  • "check-space" requires that all single-line comments must begin with a space, as in // comment -
      -
    • note that comments starting with /// are also allowed, for things such as ///<reference>
    • -
    -
  • -
  • "check-lowercase" requires that the first non-whitespace character of a comment must be lowercase, if applicable.
  • -
  • "check-uppercase" requires that the first non-whitespace character of a comment must be uppercase, if applicable.
  • -
- - -
Examples
- -
-"comment-format": [true, "check-space", "check-lowercase"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "check-space",
-      "check-lowercase",
-      "check-uppercase"
-    ]
-  },
-  "minLength": 1,
-  "maxLength": 3
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/curly/index.html b/docs/_site/rules/curly/index.html deleted file mode 100644 index fd9671afc45..00000000000 --- a/docs/_site/rules/curly/index.html +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - - Rule: curly - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: curly

-

-
- - -

Enforces braces for if/for/do/while statements.

- - - -
Rationale
- -

ts -if (foo === bar) - foo++; - bar++; -

- -

In the code above, the author almost certainly meant for both foo++ and bar++ -to be executed only if foo === bar. However, he forgot braces and bar++ will be executed -no matter what. This rule could prevent such a mistake.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"curly": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/eofline/index.html b/docs/_site/rules/eofline/index.html deleted file mode 100644 index 5f3a4d4c5ab..00000000000 --- a/docs/_site/rules/eofline/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - Rule: eofline - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: eofline

-

-
- - -

Ensures the file ends with a newline.

- - - -
Rationale
-

It is a standard convention to end files with a newline.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"eofline": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/forin/index.html b/docs/_site/rules/forin/index.html deleted file mode 100644 index dec0b3f0a7c..00000000000 --- a/docs/_site/rules/forin/index.html +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - - - Rule: forin - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: forin

-

-
- - -

Requires a for ... in statement to be filtered with an if statement.

- - - -
Rationale
- -

ts -for (let key in someObject) { - if (someObject.hasOwnProperty(key)) { - // code here - } -} - -Prevents accidental interation over properties inherited from an object’s prototype. -See MDN’s for...in -documentation for more information about for...in loops.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"forin": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/indent/index.html b/docs/_site/rules/indent/index.html deleted file mode 100644 index 338feaa7f52..00000000000 --- a/docs/_site/rules/indent/index.html +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - - - Rule: indent - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: indent

-

-
- - -

Enforces indentation with tabs or spaces.

- - - -
Rationale
- -

Using only one of tabs or spaces for indentation leads to more consistent editor behavior, -cleaner diffs in version control, and easier programatic manipulation.

- - - - -

Config

- -

One of the following arguments must be provided:

- -
    -
  • "spaces" enforces consistent spaces.
  • -
  • "tabs" enforces consistent tabs.
  • -
- - -
Examples
- -
-"indent": [true, "spaces"]
-
- - -
Schema
-
-{
-  "type": "string",
-  "enum": [
-    "tabs",
-    "spaces"
-  ]
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/index.html b/docs/_site/rules/index.html deleted file mode 100644 index 10228097967..00000000000 --- a/docs/_site/rules/index.html +++ /dev/null @@ -1,330 +0,0 @@ - - - - - - - - - Rules - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rules

-

-
- - -

TypeScript Specific

-

These rules find errors related to TypeScript features:

-
    - - -
  • member-access - Requires explicit visibility declarations for class members. -
  • - -
  • member-ordering - Enforces member ordering. -
  • - -
  • no-any - Diallows usages of any as a type declaration. -
  • - -
  • no-inferrable-types - Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean. -
  • - -
  • no-internal-module - Disallows internal module -
  • - -
  • no-namespace - Disallows use of internal modules and namespaces. -
  • - -
  • no-reference - Disallows /// <reference path=> imports (use ES6-style imports instead). -
  • - -
  • no-var-requires - Disallows the use of require statements except in import statements. -
  • - -
  • typedef - Requires type definitions to exist. -
  • - -
  • typedef-whitespace - Requires or disallows whitespace for type definitions. -
  • - -
- -

Functionality

-

These rules catch common errors in JS programming or otherwise confusing constructs that are prone to producing bugs:

-
    - - -
  • ban - Bans the use of specific functions. -
  • - -
  • curly - Enforces braces for if/for/do/while statements. -
  • - -
  • forin - Requires a for ... in statement to be filtered with an if statement. -
  • - -
  • label-position - Only allows labels in sensible locations. -
  • - -
  • label-undefined - Checks that labels are defined before usage. -
  • - -
  • no-arg - Disallows use of arguments.callee. -
  • - -
  • no-bitwise - Disallows bitwise operators. -
  • - -
  • no-conditional-assignment - Disallows any type of assignment in conditionals. -
  • - -
  • no-console - Bans the use of specified console methods. -
  • - -
  • no-construct - Disallows access to the constructors of String, Number, and Boolean. -
  • - -
  • no-debugger - Disallows debugger statements. -
  • - -
  • no-duplicate-variable - Disallows duplicate variable declarations in the same block scope. -
  • - -
  • no-empty - Disallows empty blocks. -
  • - -
  • no-eval - Disallows eval function invocations. -
  • - -
  • no-invalid-this - Disallows using the this keyword outside of classes. -
  • - -
  • no-null-keyword - Disallows use of the null keyword literal. -
  • - -
  • no-shadowed-variable - Disallows shadowing variable declarations. -
  • - -
  • no-string-literal - Disallows object access via string literals. -
  • - -
  • no-switch-case-fall-through - Disallows falling through case statements. -
  • - -
  • no-unreachable - Disallows unreachable code after break, catch, throw, and return statements. -
  • - -
  • no-unused-expression - Disallows unused expression statements. -
  • - -
  • no-unused-variable - Disallows unused imports, variables, functions and private class members. -
  • - -
  • no-use-before-declare - Disallows usage of variables before their declaration. -
  • - -
  • no-var-keyword - Disallows usage of the var keyword. -
  • - -
  • radix - Requires the radix parameter to be specified when calling parseInt. -
  • - -
  • switch-default - Require a default case in all switch statements. -
  • - -
  • triple-equals - Requires === and !== in place of == and !=. -
  • - -
  • use-isnan - Enforces use of the isNaN() function to check for NaN references instead of a comparison to the NaN constant. -
  • - -
  • use-strict - Requires using ECMAScript 5’s strict mode. -
  • - -
- -

Maintainability

-

These rules make code maintenance easier:

- - -

Style

-

These rules enforce consistent style across your codebase:

-
    - - -
  • align - Enforces vertical alignment. -
  • - -
  • class-name - Enforces PascalCased class and interface names. -
  • - -
  • comment-format - Enforces formatting rules for single-line comments. -
  • - -
  • interface-name - Requires interface names to begin with a capital ‘I’ -
  • - -
  • jsdoc-format - Enforces basic format rules for JSDoc comments. -
  • - -
  • new-parens - Requires parentheses when invoking a constructor via the new keyword. -
  • - -
  • no-angle-bracket-type-assertion - Requires the use of as Type for type assertions instead of <Type>. -
  • - -
  • no-consecutive-blank-lines - Disallows more than one blank line in a row. -
  • - -
  • no-constructor-vars - Disallows parameter properties. -
  • - -
  • one-line - Requires the specified tokens to be on the same line as the expression preceding them. -
  • - -
  • one-variable-per-declaration - Disallows multiple variable definitions in the same declaration statement. -
  • - -
  • quotemark - Requires single or double quotes for string literals. -
  • - -
  • semicolon - Enforces consistent semicolon usage at the end of every statement. -
  • - -
  • variable-name - Checks variable names for various errors. -
  • - -
  • whitespace - Enforces whitespace style conventions. -
  • - -
- -
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/interface-name/index.html b/docs/_site/rules/interface-name/index.html deleted file mode 100644 index a1d8ba8de13..00000000000 --- a/docs/_site/rules/interface-name/index.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - - - Rule: interface-name - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: interface-name

-

-
- - -

Requires interface names to begin with a capital ‘I’

- - - -
Rationale
-

Makes it easy to differentitate interfaces from regular classes at a glance.

- - - - -

Config

- -

One of the following two options must be provided:

- -
    -
  • "always-prefix" requires interface names to start with an “I”
  • -
  • "never-prefix" requires interface names to not have an “I” prefix
  • -
- - -
Examples
- -
-"interface-name": [true, "always-prefix"]
-
- -
-"interface-name": [true, "never-prefix"]
-
- - -
Schema
-
-{
-  "type": "string",
-  "enum": [
-    "always-prefix",
-    "never-prefix"
-  ]
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/jsdoc-format/index.html b/docs/_site/rules/jsdoc-format/index.html deleted file mode 100644 index 928146ab52c..00000000000 --- a/docs/_site/rules/jsdoc-format/index.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - - - - Rule: jsdoc-format - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: jsdoc-format

-

-
- - -

Enforces basic format rules for JSDoc comments.

- - - -

The following rules are enforced for JSDoc comments (comments starting with /**):

- -
    -
  • each line contains an asterisk and asterisks must be aligned
  • -
  • each asterisk must be followed by either a space or a newline (except for the first and the last)
  • -
  • the only characters before the asterisk on each line must be whitespace characters
  • -
  • one line comments must start with /** and end with */
  • -
- - - -
Rationale
-

Helps maintain a consistent, readable style for JSDoc comments.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"jsdoc-format": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/label-position/index.html b/docs/_site/rules/label-position/index.html deleted file mode 100644 index 71daa69668d..00000000000 --- a/docs/_site/rules/label-position/index.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - Rule: label-position - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: label-position

-

-
- - -

Only allows labels in sensible locations.

- - -

This rule only allows labels to be on do/for/while/switch statements.

- - - -
Rationale
- -

Labels in JavaScript only can be used in conjunction with break or continue, -constructs meant to be used for loop flow control. While you can theoretically use -labels on any block statement in JS, it is considered poor code structure to do so.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"label-position": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/label-undefined/index.html b/docs/_site/rules/label-undefined/index.html deleted file mode 100644 index e476927c9d9..00000000000 --- a/docs/_site/rules/label-undefined/index.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - Rule: label-undefined - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: label-undefined

-

-
- - -

Checks that labels are defined before usage.

- - -

This rule is now implemented in the TypeScript compiler and does not need to be used.

- - - -
Rationale
-

Using break or continue to go to an out-of-scope label is an error in JS.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"label-undefined": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/linebreak-style/index.html b/docs/_site/rules/linebreak-style/index.html deleted file mode 100644 index ce4af76280c..00000000000 --- a/docs/_site/rules/linebreak-style/index.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - - - Rule: linebreak-style - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: linebreak-style

-

-
- - -

Enforces a consistent linebreak style.

- - - - - -

Config

- -

One of the following options must be provided:

- -
    -
  • "LF" requires LF (\n) linebreaks
  • -
  • "CRLF" requires CRLF (\r\n) linebreaks
  • -
- - -
Examples
- -
-"linebreak-style": [true, "LF"]
-
- -
-"linebreak-style": [true, "CRLF"]
-
- - -
Schema
-
-{
-  "type": "string",
-  "enum": [
-    "LF",
-    "CRLF"
-  ]
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/max-line-length/index.html b/docs/_site/rules/max-line-length/index.html deleted file mode 100644 index bf66d32e698..00000000000 --- a/docs/_site/rules/max-line-length/index.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - Rule: max-line-length - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: max-line-length

-

-
- - -

Requires lines to be under a certain max length.

- - - -
Rationale
- -

Limiting the length of a line of code improves code readability. -It also makes comparing code side-by-side easier and improves compatibility with -various editors, IDEs, and diff viewers.

- - - - -

Config

-

An integer indicating the max length of lines.

- - -
Examples
- -
-"max-line-length": [true, 120]
-
- - -
Schema
-
-{
-  "type": "number",
-  "minimum": "1"
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/member-access/index.html b/docs/_site/rules/member-access/index.html deleted file mode 100644 index 3b92d09f343..00000000000 --- a/docs/_site/rules/member-access/index.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - Rule: member-access - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: member-access

-

-
- - -

Requires explicit visibility declarations for class members.

- - - -
Rationale
-

Explicit visibility declarations can make code more readable and accessible for those new to TS.

- - - - -

Config

- -

Two arguments may be optionally provided:

- -
    -
  • "check-accessor" enforces explicit visibility on get/set accessors (can only be public)
  • -
  • "check-constructor" enforces explicit visibility on constructors (can only be public)
  • -
- - -
Examples
- -
-"member-access": true
-
- -
-"member-access": [true, "check-accessor"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "check-accessor",
-      "check-constructor"
-    ]
-  },
-  "minLength": 0,
-  "maxLength": 2
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/member-ordering/index.html b/docs/_site/rules/member-ordering/index.html deleted file mode 100644 index 9b6f741a703..00000000000 --- a/docs/_site/rules/member-ordering/index.html +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - - - - Rule: member-ordering - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: member-ordering

-

-
- - -

Enforces member ordering.

- - - -
Rationale
-

A consistent ordering for class members can make classes easier to read, navigate, and edit.

- - - - -

Config

- -

One argument, which is an object, must be provided. It should contain an order property. -The order property should have a value of one of the following strings:

- -
    -
  • fields-first
  • -
  • statics-first
  • -
  • instance-sandwich
  • -
- -

Alternatively, the value for order maybe be an array consisting of the following strings:

- -
    -
  • public-static-field
  • -
  • protected-static-field
  • -
  • private-static-field
  • -
  • public-instance-field
  • -
  • protected-instance-field
  • -
  • private-instance-field
  • -
  • constructor
  • -
  • public-static-method
  • -
  • protected-static-method
  • -
  • private-static-method
  • -
  • public-instance-method
  • -
  • protected-instance-method
  • -
  • private-instance-method
  • -
- -

This is useful if one of the preset orders does not meet your needs.

- - -
Examples
- -
-"member-ordering": [true, { "order": "fields-first" }]
-
- - -
Schema
-
-{
-  "type": "object",
-  "properties": {
-    "order": {
-      "oneOf": [
-        {
-          "type": "string",
-          "enum": [
-            "fields-first",
-            "statics-first",
-            "instance-sandwich"
-          ]
-        },
-        {
-          "type": "array",
-          "items": {
-            "type": "string",
-            "enum": [
-              "public-static-field",
-              "public-static-method",
-              "protected-static-field",
-              "protected-static-method",
-              "private-static-field",
-              "private-static-method",
-              "public-instance-field",
-              "protected-instance-field",
-              "private-instance-field",
-              "constructor",
-              "public-instance-method",
-              "protected-instance-method",
-              "private-instance-method"
-            ]
-          },
-          "maxLength": 13
-        }
-      ]
-    }
-  },
-  "additionalProperties": false
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/new-parens/index.html b/docs/_site/rules/new-parens/index.html deleted file mode 100644 index 9f037c7294b..00000000000 --- a/docs/_site/rules/new-parens/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - Rule: new-parens - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: new-parens

-

-
- - -

Requires parentheses when invoking a constructor via the new keyword.

- - - -
Rationale
-

Maintains stylistic consistency with other function calls.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"new-parens": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-angle-bracket-type-assertion/index.html b/docs/_site/rules/no-angle-bracket-type-assertion/index.html deleted file mode 100644 index ae362040fd8..00000000000 --- a/docs/_site/rules/no-angle-bracket-type-assertion/index.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - Rule: no-angle-bracket-type-assertion - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-angle-bracket-type-assertion

-

-
- - -

Requires the use of as Type for type assertions instead of <Type>.

- - - -
Rationale
- -

Both formats of type assertions have the same effect, but only as type assertions -work in .tsx files. This rule ensures that you have a consistent type assertion style -across your codebase.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-angle-bracket-type-assertion": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-any/index.html b/docs/_site/rules/no-any/index.html deleted file mode 100644 index c0340c6cfcb..00000000000 --- a/docs/_site/rules/no-any/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - Rule: no-any - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-any

-

-
- - -

Diallows usages of any as a type declaration.

- - - -
Rationale
-

Using any as a type declaration nullifies the compile-time benefits of the type system.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-any": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-arg/index.html b/docs/_site/rules/no-arg/index.html deleted file mode 100644 index 609683fddb8..00000000000 --- a/docs/_site/rules/no-arg/index.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - Rule: no-arg - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-arg

-

-
- - -

Disallows use of arguments.callee.

- - - -
Rationale
- -

Using arguments.callee makes various performance optimizations impossible. -See MDN -for more details on why to avoid arguments.callee.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-arg": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-bitwise/index.html b/docs/_site/rules/no-bitwise/index.html deleted file mode 100644 index dab000b9eaa..00000000000 --- a/docs/_site/rules/no-bitwise/index.html +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - - - Rule: no-bitwise - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-bitwise

-

-
- - -

Disallows bitwise operators.

- - - -

Specifically, the following bitwise operators are banned: -&, &=, |, |=, -^, ^=, <<, <<=, ->>, >>=, >>>, >>>=, and ~. -This rule does not ban the use of & and | for intersection and union types.

- - - -
Rationale
- -

Bitwise operators are often typos - for example bool1 & bool2 instead of bool1 && bool2. -They also can be an indicator of overly clever code which decreases maintainability.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-bitwise": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-conditional-assignment/index.html b/docs/_site/rules/no-conditional-assignment/index.html deleted file mode 100644 index 00a613c8c25..00000000000 --- a/docs/_site/rules/no-conditional-assignment/index.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - Rule: no-conditional-assignment - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-conditional-assignment

-

-
- - -

Disallows any type of assignment in conditionals.

- - -

This applies to do-while, for, if, and while statements.

- - - -
Rationale
- -

Assignments in conditionals are often typos: -for example if (var1 = var2) instead of if (var1 == var2). -They also can be an indicator of overly clever code which decreases maintainability.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-conditional-assignment": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-consecutive-blank-lines/index.html b/docs/_site/rules/no-consecutive-blank-lines/index.html deleted file mode 100644 index e0fc7f80eff..00000000000 --- a/docs/_site/rules/no-consecutive-blank-lines/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - Rule: no-consecutive-blank-lines - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-consecutive-blank-lines

-

-
- - -

Disallows more than one blank line in a row.

- - - -
Rationale
-

Helps maintain a readable style in your codebase.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-consecutive-blank-lines": true
-
- - -
Schema
-
-{}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-console/index.html b/docs/_site/rules/no-console/index.html deleted file mode 100644 index 2490b9acd97..00000000000 --- a/docs/_site/rules/no-console/index.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - Rule: no-console - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-console

-

-
- - -

Bans the use of specified console methods.

- - - -
Rationale
-

In general, console methods aren’t appropriate for production code.

- - - - -

Config

-

A list of method names to ban.

- - -
Examples
- -
-"no-console": [true, "log", "error"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string"
-  }
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-construct/index.html b/docs/_site/rules/no-construct/index.html deleted file mode 100644 index 4cbabb91edf..00000000000 --- a/docs/_site/rules/no-construct/index.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - Rule: no-construct - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-construct

-

-
- - -

Disallows access to the constructors of String, Number, and Boolean.

- - -

Disallows constructor use such as new Number(foo) but does not disallow Number(foo).

- - - -
Rationale
- -

There is little reason to use String, Number, or Boolean as constructors. -In almost all cases, the regular function-call version is more appropriate. -More details are available on StackOverflow.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-construct": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-constructor-vars/index.html b/docs/_site/rules/no-constructor-vars/index.html deleted file mode 100644 index 2bb8b865374..00000000000 --- a/docs/_site/rules/no-constructor-vars/index.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - Rule: no-constructor-vars - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-constructor-vars

-

-
- - -

Disallows parameter properties.

- - - -
Rationale
- -

Parameter properties can be confusing to those new to TS as they are less explicit -than other ways of declaring and initializing class members.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-constructor-vars": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-debugger/index.html b/docs/_site/rules/no-debugger/index.html deleted file mode 100644 index 89fdf171d0c..00000000000 --- a/docs/_site/rules/no-debugger/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - Rule: no-debugger - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-debugger

-

-
- - -

Disallows debugger statements.

- - - -
Rationale
-

In general, debugger statements aren’t appropriate for production code.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-debugger": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-default-export/index.html b/docs/_site/rules/no-default-export/index.html deleted file mode 100644 index a7b2c6e2118..00000000000 --- a/docs/_site/rules/no-default-export/index.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - Rule: no-default-export - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-default-export

-

-
- - -

Disallows default exports in ES6-style modules.

- - -

Use named exports instead.

- - - -
Rationale
- -

Named imports/exports promote clarity. -In addition, current tooling differs on the correct way to handle default imports/exports. -Avoiding them all together can help avoid tooling bugs and conflicts.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-default-export": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-duplicate-variable/index.html b/docs/_site/rules/no-duplicate-variable/index.html deleted file mode 100644 index cfba1ecab4b..00000000000 --- a/docs/_site/rules/no-duplicate-variable/index.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - Rule: no-duplicate-variable - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-duplicate-variable

-

-
- - -

Disallows duplicate variable declarations in the same block scope.

- - - -

This rule is only useful when using the var keyword - -the compiler will detect redeclarations of let and const variables.

- - - -
Rationale
- -

A variable can be reassigned if necessary - -there’s no good reason to have a duplicate variable declaration.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-duplicate-variable": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-empty/index.html b/docs/_site/rules/no-empty/index.html deleted file mode 100644 index a6f25115e1d..00000000000 --- a/docs/_site/rules/no-empty/index.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - Rule: no-empty - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-empty

-

-
- - -

Disallows empty blocks.

- - -

Blocks with a comment inside are not considered empty.

- - - -
Rationale
-

Empty blocks are often indicators of missing code.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-empty": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-eval/index.html b/docs/_site/rules/no-eval/index.html deleted file mode 100644 index ab76ca1ea37..00000000000 --- a/docs/_site/rules/no-eval/index.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - Rule: no-eval - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-eval

-

-
- - -

Disallows eval function invocations.

- - - -
Rationale
- -

eval() is dangerous as it allows arbitrary code execution with full privileges. There are -alternatives -for most of the use cases for eval().

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-eval": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-inferrable-types/index.html b/docs/_site/rules/no-inferrable-types/index.html deleted file mode 100644 index e70f95ce617..00000000000 --- a/docs/_site/rules/no-inferrable-types/index.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - - - - Rule: no-inferrable-types - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-inferrable-types

-

-
- - -

Disallows explicit type declarations for variables or parameters initialized to a number, string, or boolean.

- - - -
Rationale
-

Explicit types where they can be easily infered by the compiler make code more verbose.

- - - - -

Config

- -

One argument may be optionally provided:

- -
    -
  • ignore-params allows specifying an inferrable type annotation for function params. -This can be useful when combining with the typedef rule.
  • -
- - -
Examples
- -
-"no-inferrable-types": true
-
- -
-"no-inferrable-types": [true, "ignore-params"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "ignore-params"
-    ]
-  },
-  "minLength": 0,
-  "maxLength": 1
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-internal-module/index.html b/docs/_site/rules/no-internal-module/index.html deleted file mode 100644 index 5a566c7d680..00000000000 --- a/docs/_site/rules/no-internal-module/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - Rule: no-internal-module - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-internal-module

-

-
- - -

Disallows internal module

- - - -
Rationale
-

Using module leads to a confusion of concepts with external modules. Use the newer namespace keyword instead.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-internal-module": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-invalid-this/index.html b/docs/_site/rules/no-invalid-this/index.html deleted file mode 100644 index 63b521b961e..00000000000 --- a/docs/_site/rules/no-invalid-this/index.html +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - - - - Rule: no-invalid-this - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-invalid-this

-

-
- - -

Disallows using the this keyword outside of classes.

- - - -
Rationale
-

See the rule’s author’s rationale here.

- - - - -

Config

- -

One argument may be optionally provided:

- -
    -
  • check-function-in-method disallows using the this keyword in functions within class methods.
  • -
- - -
Examples
- -
-"no-invalid-this": true
-
- -
-"no-invalid-this": [true, "check-function-in-method"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "check-function-in-method"
-    ]
-  },
-  "minLength": 0,
-  "maxLength": 1
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-mergeable-namespace/index.html b/docs/_site/rules/no-mergeable-namespace/index.html deleted file mode 100644 index c5cd0907400..00000000000 --- a/docs/_site/rules/no-mergeable-namespace/index.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - - Rule: no-mergeable-namespace - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-mergeable-namespace

-

-
- - -

Disallows mergeable namespaces in the same file.

- - - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-mergeable-namespace": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-namespace/index.html b/docs/_site/rules/no-namespace/index.html deleted file mode 100644 index 90b51d007c8..00000000000 --- a/docs/_site/rules/no-namespace/index.html +++ /dev/null @@ -1,159 +0,0 @@ - - - - - - - - - Rule: no-namespace - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-namespace

-

-
- - -

Disallows use of internal modules and namespaces.

- - -

This rule still allows the use of declare module ... {}

- - - -
Rationale
- -

ES6-style external modules are the standard way to modularize code. -Using module {} and namespace {} are outdated ways to organize TypeScript code.

- - - - -

Config

- -

One argument may be optionally provided:

- -
    -
  • allow-declarations allows declare namespace ... {} to describe external APIs.
  • -
- - -
Examples
- -
-"no-namespace": true
-
- -
-"no-namespace": [true, "allow-declarations"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "allow-declarations"
-    ]
-  },
-  "minLength": 0,
-  "maxLength": 1
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-null-keyword/index.html b/docs/_site/rules/no-null-keyword/index.html deleted file mode 100644 index 4e2a01cb1f2..00000000000 --- a/docs/_site/rules/no-null-keyword/index.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - Rule: no-null-keyword - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-null-keyword

-

-
- - -

Disallows use of the null keyword literal.

- - - -
Rationale
- -

Instead of having the dual concepts of null andundefined in a codebase, -this rule ensures that only undefined is used.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-null-keyword": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-reference/index.html b/docs/_site/rules/no-reference/index.html deleted file mode 100644 index cb22c78ed15..00000000000 --- a/docs/_site/rules/no-reference/index.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - Rule: no-reference - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-reference

-

-
- - -

Disallows /// <reference path=> imports (use ES6-style imports instead).

- - - -
Rationale
- -

Using /// <reference path=> comments to load other files is outdated. -Use ES6-style imports to reference other files.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-reference": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-require-imports/index.html b/docs/_site/rules/no-require-imports/index.html deleted file mode 100644 index 0c4ae3c3c6b..00000000000 --- a/docs/_site/rules/no-require-imports/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - Rule: no-require-imports - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-require-imports

-

-
- - -

Disallows invocation of require().

- - - -
Rationale
-

Prefer the newer ES6-style imports over require().

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-require-imports": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-shadowed-variable/index.html b/docs/_site/rules/no-shadowed-variable/index.html deleted file mode 100644 index 22ce7bad5dc..00000000000 --- a/docs/_site/rules/no-shadowed-variable/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - Rule: no-shadowed-variable - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-shadowed-variable

-

-
- - -

Disallows shadowing variable declarations.

- - - -
Rationale
-

Shadowing a variable masks access to it and obscures to what value an identifier actually refers.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-shadowed-variable": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-string-literal/index.html b/docs/_site/rules/no-string-literal/index.html deleted file mode 100644 index f27176a8cf7..00000000000 --- a/docs/_site/rules/no-string-literal/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - Rule: no-string-literal - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-string-literal

-

-
- - -

Disallows object access via string literals.

- - - -
Rationale
-

Encourages using strongly-typed property access.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-string-literal": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-switch-case-fall-through/index.html b/docs/_site/rules/no-switch-case-fall-through/index.html deleted file mode 100644 index 988a45554b6..00000000000 --- a/docs/_site/rules/no-switch-case-fall-through/index.html +++ /dev/null @@ -1,162 +0,0 @@ - - - - - - - - - Rule: no-switch-case-fall-through - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-switch-case-fall-through

-

-
- - -

Disallows falling through case statements.

- - - -

For example, the following is not allowed:

- -

ts -switch(foo) { - case 1: - someFunc(foo); - case 2: - someOtherFunc(foo); -} -

- -

However, fall through is allowed when case statements are consecutive or -a magic /* falls through */ comment is present. The following is valid:

- -

ts -switch(foo) { - case 1: - someFunc(foo); - /* falls through */ - case 2: - case 3: - someOtherFunc(foo); -} -

- - - -
Rationale
-

Fall though in switch statements is often unintentional and a bug.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-switch-case-fall-through": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-trailing-whitespace/index.html b/docs/_site/rules/no-trailing-whitespace/index.html deleted file mode 100644 index 1e1114497a3..00000000000 --- a/docs/_site/rules/no-trailing-whitespace/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - Rule: no-trailing-whitespace - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-trailing-whitespace

-

-
- - -

Disallows trailing whitespace at the end of a line.

- - - -
Rationale
-

Keeps version control diffs clean as it prevents accidental whitespace from being committed.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-trailing-whitespace": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-unreachable/index.html b/docs/_site/rules/no-unreachable/index.html deleted file mode 100644 index 5188ab8d02d..00000000000 --- a/docs/_site/rules/no-unreachable/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - Rule: no-unreachable - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-unreachable

-

-
- - -

Disallows unreachable code after break, catch, throw, and return statements.

- - - -
Rationale
-

Unreachable code is often indication of a logic error.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-unreachable": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-unused-expression/index.html b/docs/_site/rules/no-unused-expression/index.html deleted file mode 100644 index 46e8c7642cf..00000000000 --- a/docs/_site/rules/no-unused-expression/index.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - Rule: no-unused-expression - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-unused-expression

-

-
- - -

Disallows unused expression statements.

- - - -

Unused expressions are expression statements which are not assignments or function calls -(and thus usually no-ops).

- - - -
Rationale
- -

Detects potential errors where an assignment or function call was intended. Also detects constructs such as -new SomeClass(), where a constructor is used solely for its side effects, which is considered poor style.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-unused-expression": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-unused-variable/index.html b/docs/_site/rules/no-unused-variable/index.html deleted file mode 100644 index 75f8a550e8f..00000000000 --- a/docs/_site/rules/no-unused-variable/index.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - - - - Rule: no-unused-variable - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-unused-variable

-

-
- - -

Disallows unused imports, variables, functions and private class members.

- - - - - -

Config

- -

Three optional arguments may be optionally provided:

- -
    -
  • "check-parameters" disallows unused function and constructor parameters. -
      -
    • NOTE: this option is experimental and does not work with classes - that use abstract method declarations, among other things.
    • -
    -
  • -
  • "react" relaxes the rule for a namespace import named React -(from either the module "react" or "react/addons"). -Any JSX expression in the file will be treated as a usage of React -(because it expands to React.createElement ).
  • -
  • {"ignore-pattern": "pattern"} where pattern is a case-sensitive regexp. -Variable names that match the pattern will be ignored.
  • -
- - -
Examples
- -
-"no-unused-variable": [true, "react"]
-
- -
-"no-unused-variable": [true, {"ignore-pattern": "^_"}]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "oneOf": [
-      {
-        "type": "string",
-        "enum": [
-          "check-parameters",
-          "react"
-        ]
-      },
-      {
-        "type": "object",
-        "properties": {
-          "ignore-pattern": {
-            "type": "string"
-          }
-        },
-        "additionalProperties": false
-      }
-    ]
-  },
-  "minLength": 0,
-  "maxLength": 3
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-use-before-declare/index.html b/docs/_site/rules/no-use-before-declare/index.html deleted file mode 100644 index 72670f1dfef..00000000000 --- a/docs/_site/rules/no-use-before-declare/index.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - Rule: no-use-before-declare - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-use-before-declare

-

-
- - -

Disallows usage of variables before their declaration.

- - - -

This rule is primarily useful when using the var keyword - -the compiler will detect if a let and const variable is used before it is declared.

- - - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-use-before-declare": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-var-keyword/index.html b/docs/_site/rules/no-var-keyword/index.html deleted file mode 100644 index 65632f6c870..00000000000 --- a/docs/_site/rules/no-var-keyword/index.html +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - - - - Rule: no-var-keyword - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-var-keyword

-

-
- - -

Disallows usage of the var keyword.

- - -

Use let or const instead.

- - - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-var-keyword": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/no-var-requires/index.html b/docs/_site/rules/no-var-requires/index.html deleted file mode 100644 index 34ec043f137..00000000000 --- a/docs/_site/rules/no-var-requires/index.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - Rule: no-var-requires - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: no-var-requires

-

-
- - -

Disallows the use of require statements except in import statements.

- - - -

In other words, the use of forms such as var module = require("module") are banned. -Instead use ES6 style imports or import foo = require('foo') imports.

- - - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"no-var-requires": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/object-literal-sort-keys/index.html b/docs/_site/rules/object-literal-sort-keys/index.html deleted file mode 100644 index b086e715801..00000000000 --- a/docs/_site/rules/object-literal-sort-keys/index.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - Rule: object-literal-sort-keys - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: object-literal-sort-keys

-

-
- - -

Requires keys in object literals to be sorted alphabetically

- - - -
Rationale
-

Useful in preventing merge conflicts

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"object-literal-sort-keys": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/one-line/index.html b/docs/_site/rules/one-line/index.html deleted file mode 100644 index e81d856e840..00000000000 --- a/docs/_site/rules/one-line/index.html +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - - - - Rule: one-line - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: one-line

-

-
- - -

Requires the specified tokens to be on the same line as the expression preceding them.

- - - - - -

Config

- -

Five arguments may be optionally provided:

- -
    -
  • "check-catch" checks that catch is on the same line as the closing brace for try.
  • -
  • "check-finally" checks that finally is on the same line as the closing brace for catch.
  • -
  • "check-else" checks that else is on the same line as the closing brace for if.
  • -
  • "check-open-brace" checks that an open brace falls on the same line as its preceding expression.
  • -
  • "check-whitespace" checks preceding whitespace for the specified tokens.
  • -
- - -
Examples
- -
-"one-line": [true, "check-catch", "check-finally", "check-else"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "check-catch",
-      "check-finally",
-      "check-else",
-      "check-open-brace",
-      "check-whitespace"
-    ]
-  },
-  "minLength": 0,
-  "maxLength": 5
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/one-variable-per-declaration/index.html b/docs/_site/rules/one-variable-per-declaration/index.html deleted file mode 100644 index 1d04ca16d8d..00000000000 --- a/docs/_site/rules/one-variable-per-declaration/index.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - - - - Rule: one-variable-per-declaration - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: one-variable-per-declaration

-

-
- - -

Disallows multiple variable definitions in the same declaration statement.

- - - - - -

Config

- -

One argument may be optionally provided:

- -
    -
  • ignore-for-loop allows multiple variable definitions in a for loop declaration.
  • -
- - -
Examples
- -
-"one-variable-per-declaration": true
-
- -
-"one-variable-per-declaration": [true, "ignore-for-loop"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "ignore-for-loop"
-    ]
-  },
-  "minLength": 0,
-  "maxLength": 1
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/quotemark/index.html b/docs/_site/rules/quotemark/index.html deleted file mode 100644 index 25822e950e5..00000000000 --- a/docs/_site/rules/quotemark/index.html +++ /dev/null @@ -1,159 +0,0 @@ - - - - - - - - - Rule: quotemark - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: quotemark

-

-
- - -

Requires single or double quotes for string literals.

- - - - - -

Config

- -

Five arguments may be optionally provided:

- -
    -
  • "single" enforces single quotes.
  • -
  • "double" enforces double quotes.
  • -
  • "jsx-single" enforces single quotes for JSX attributes.
  • -
  • "jsx-double" enforces double quotes for JSX attributes.
  • -
  • "avoid-escape" allows you to use the “other” quotemark in cases where escaping would normally be required. -For example, [true, "double", "avoid-escape"] would not report a failure on the string literal 'Hello "World"'.
  • -
- - -
Examples
- -
-"quotemark": [true, "single", "avoid-escape"]
-
- -
-"quotemark": [true, "single", "jsx-double"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "single",
-      "double",
-      "jsx-single",
-      "jsx-double",
-      "avoid-escape"
-    ]
-  },
-  "minLength": 0,
-  "maxLength": 5
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/radix/index.html b/docs/_site/rules/radix/index.html deleted file mode 100644 index 16f053f079a..00000000000 --- a/docs/_site/rules/radix/index.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - Rule: radix - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: radix

-

-
- - -

Requires the radix parameter to be specified when calling parseInt.

- - - -
Rationale
- -

From MDN: -> Always specify this parameter to eliminate reader confusion and to guarantee predictable behavior. -> Different implementations produce different results when a radix is not specified, usually defaulting the value to 10.

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"radix": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/semicolon/index.html b/docs/_site/rules/semicolon/index.html deleted file mode 100644 index 926e8cd9a8b..00000000000 --- a/docs/_site/rules/semicolon/index.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - - - - Rule: semicolon - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: semicolon

-

-
- - -

Enforces consistent semicolon usage at the end of every statement.

- - - - - -

Config

- -

One of the following arguments must be provided:

- -
    -
  • "always" enforces semicolons at the end of every statement.
  • -
  • "never" disallows semicolons at the end of every statement except for when they are necessary.
  • -
- -

The following arguments may be optionaly provided: -* "ignore-interfaces" skips checking semicolons at the end of interface members.

- - -
Examples
- -
-"semicolon": [true, "always"]
-
- -
-"semicolon": [true, "never"]
-
- -
-"semicolon": [true, "always", "ignore-interfaces"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": [
-    {
-      "type": "string",
-      "enum": [
-        "always",
-        "never"
-      ]
-    },
-    {
-      "type": "string",
-      "enum": [
-        "ignore-interfaces"
-      ]
-    }
-  ],
-  "additionalItems": false
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/switch-default/index.html b/docs/_site/rules/switch-default/index.html deleted file mode 100644 index cd6f0b4775c..00000000000 --- a/docs/_site/rules/switch-default/index.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - - Rule: switch-default - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: switch-default

-

-
- - -

Require a default case in all switch statements.

- - - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"switch-default": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/trailing-comma/index.html b/docs/_site/rules/trailing-comma/index.html deleted file mode 100644 index 79e68c83fc8..00000000000 --- a/docs/_site/rules/trailing-comma/index.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - - - - Rule: trailing-comma - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: trailing-comma

-

-
- - -

Requires or disallows trailing commas in array and object literals, destructuring assignments and named imports.

- - - - - -

Config

- -

One argument which is an object with the keys multiline and singleline. -Both should be set to either "always" or "never".

- -
    -
  • "multiline" checks multi-line object literals.
  • -
  • "singleline" checks single-line object literals.
  • -
- -

A array is considered “multiline” if its closing bracket is on a line -after the last array element. The same general logic is followed for -object literals and named import statements.

- - -
Examples
- -
-"trailing-comma": [true, {"multiline": "always", "singleline": "never"}]
-
- - -
Schema
-
-{
-  "type": "object",
-  "properties": {
-    "multiline": {
-      "type": "string",
-      "enum": [
-        "always",
-        "never"
-      ]
-    },
-    "singleline": {
-      "type": "string",
-      "enum": [
-        "always",
-        "never"
-      ]
-    }
-  },
-  "additionalProperties": false
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/triple-equals/index.html b/docs/_site/rules/triple-equals/index.html deleted file mode 100644 index d5fb9186f14..00000000000 --- a/docs/_site/rules/triple-equals/index.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - Rule: triple-equals - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: triple-equals

-

-
- - -

Requires === and !== in place of == and !=.

- - - - - -

Config

- -

Two arguments may be optionally provided:

- -
    -
  • "allow-null-check" allows == and != when comparing to null.
  • -
  • "allow-undefined-check" allows == and != when comparing to undefined.
  • -
- - -
Examples
- -
-"triple-equals": true
-
- -
-"triple-equals": [true, "allow-null-check"]
-
- -
-"triple-equals": [true, "allow-undefined-check"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "allow-null-check",
-      "allow-undefined-check"
-    ]
-  },
-  "minLength": 0,
-  "maxLength": 2
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/typedef-whitespace/index.html b/docs/_site/rules/typedef-whitespace/index.html deleted file mode 100644 index b4fa6a5d6a4..00000000000 --- a/docs/_site/rules/typedef-whitespace/index.html +++ /dev/null @@ -1,261 +0,0 @@ - - - - - - - - - Rule: typedef-whitespace - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: typedef-whitespace

-

-
- - -

Requires or disallows whitespace for type definitions.

- - -

Determines if a space is required or not before the colon in a type specifier.

- - - - - -

Config

- -

Two arguments which are both objects. -The first argument specifies how much space should be to the left of a typedef colon. -The second argument specifies how much space should be to the right of a typedef colon. -Each key should have a value of "space" or "nospace". -Possible keys are:

- -
    -
  • "call-signature" checks return type of functions.
  • -
  • "index-signature" checks index type specifier of indexers.
  • -
  • "parameter" checks function parameters.
  • -
  • "property-declaration" checks object property declarations.
  • -
  • "variable-declaration" checks variable declaration.
  • -
- - -
Examples
- -
-"typedef-whitespace": 
-[
-  true,
-  {
-    "call-signature": "nospace",
-    "index-signature": "nospace",
-    "parameter": "nospace",
-    "property-declaration": "nospace",
-    "variable-declaration": "nospace"
-  },
-  {
-    "call-signature": "onespace",
-    "index-signature": "onespace",
-    "parameter": "onespace",
-    "property-declaration": "onespace",
-    "variable-declaration": "onespace"
-  }
-]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": [
-    {
-      "type": "object",
-      "properties": {
-        "call-signature": {
-          "type": "string",
-          "enum": [
-            "nospace",
-            "onespace",
-            "space"
-          ]
-        },
-        "index-signature": {
-          "type": "string",
-          "enum": [
-            "nospace",
-            "onespace",
-            "space"
-          ]
-        },
-        "parameter": {
-          "type": "string",
-          "enum": [
-            "nospace",
-            "onespace",
-            "space"
-          ]
-        },
-        "property-declaration": {
-          "type": "string",
-          "enum": [
-            "nospace",
-            "onespace",
-            "space"
-          ]
-        },
-        "variable-declaration": {
-          "type": "string",
-          "enum": [
-            "nospace",
-            "onespace",
-            "space"
-          ]
-        }
-      },
-      "additionalProperties": false
-    },
-    {
-      "type": "object",
-      "properties": {
-        "call-signature": {
-          "type": "string",
-          "enum": [
-            "nospace",
-            "onespace",
-            "space"
-          ]
-        },
-        "index-signature": {
-          "type": "string",
-          "enum": [
-            "nospace",
-            "onespace",
-            "space"
-          ]
-        },
-        "parameter": {
-          "type": "string",
-          "enum": [
-            "nospace",
-            "onespace",
-            "space"
-          ]
-        },
-        "property-declaration": {
-          "type": "string",
-          "enum": [
-            "nospace",
-            "onespace",
-            "space"
-          ]
-        },
-        "variable-declaration": {
-          "type": "string",
-          "enum": [
-            "nospace",
-            "onespace",
-            "space"
-          ]
-        }
-      },
-      "additionalProperties": false
-    }
-  ],
-  "additionalItems": false
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/typedef/index.html b/docs/_site/rules/typedef/index.html deleted file mode 100644 index 4d0fb1927bd..00000000000 --- a/docs/_site/rules/typedef/index.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - Rule: typedef - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: typedef

-

-
- - -

Requires type definitions to exist.

- - - - - -

Config

- -

Six arguments may be optionally provided:

- -
    -
  • "call-signature" checks return type of functions.
  • -
  • "parameter" checks type specifier of function parameters for non-arrow functions.
  • -
  • "arrow-parameter" checks type specifier of function parameters for arrow functions.
  • -
  • "property-declaration" checks return types of interface properties.
  • -
  • "variable-declaration" checks variable declarations.
  • -
  • "member-variable-declaration" checks member variable declarations.
  • -
- - -
Examples
- -
-"typedef": [true, "call-signature", "parameter", "member-variable-declaration"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "call-signature",
-      "parameter",
-      "arrow-parameter",
-      "property-declaration",
-      "variable-declaration",
-      "member-variable-declaration"
-    ]
-  },
-  "minLength": 0,
-  "maxLength": 6
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/use-isnan/index.html b/docs/_site/rules/use-isnan/index.html deleted file mode 100644 index 87976cbc00e..00000000000 --- a/docs/_site/rules/use-isnan/index.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - Rule: use-isnan - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: use-isnan

-

-
- - -

Enforces use of the isNaN() function to check for NaN references instead of a comparison to the NaN constant.

- - - -
Rationale
- -

Since NaN !== NaN, comparisons with regular operators will produce unexpected results. -So, instead of if (myVar === NaN), do if (isNaN(myVar)).

- - - - -

Config

-

Not configurable.

- - -
Examples
- -
-"use-isnan": true
-
- - -
Schema
-
-null
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/use-strict/index.html b/docs/_site/rules/use-strict/index.html deleted file mode 100644 index 93c37d5bc97..00000000000 --- a/docs/_site/rules/use-strict/index.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - - - - Rule: use-strict - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: use-strict

-

-
- - -

Requires using ECMAScript 5’s strict mode.

- - - - - -

Config

- -

Two arguments may be optionally provided:

- -
    -
  • check-module checks that all top-level modules are using strict mode.
  • -
  • check-function checks that all top-level functions are using strict mode.
  • -
- - -
Examples
- -
-"use-strict": [true, "check-module"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "check-module",
-      "check-function"
-    ]
-  },
-  "minLength": 0,
-  "maxLength": 2
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/variable-name/index.html b/docs/_site/rules/variable-name/index.html deleted file mode 100644 index 703eb14603e..00000000000 --- a/docs/_site/rules/variable-name/index.html +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - - - - Rule: variable-name - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: variable-name

-

-
- - -

Checks variable names for various errors.

- - - - - -

Config

- -

Five arguments may be optionally provided:

- -
    -
  • "check-format": allows only camelCased or UPPER_CASED variable names -
      -
    • "allow-leading-underscore" allows underscores at the beginning (only has an effect if “check-format” specified)
    • -
    • "allow-trailing-underscore" allows underscores at the end. (only has an effect if “check-format” specified)
    • -
    • "allow-pascal-case allows PascalCase in addtion to camelCase.
    • -
    -
  • -
  • "ban-keywords": disallows the use of certain TypeScript keywords (any, Number, number, String, -string, Boolean, boolean, undefined) as variable or parameter names.
  • -
- - -
Examples
- -
-"variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "check-format",
-      "allow-leading-underscore",
-      "allow-trailing-underscore",
-      "allow-pascal-case",
-      "ban-keywords"
-    ]
-  },
-  "minLength": 0,
-  "maxLength": 5
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/rules/whitespace/index.html b/docs/_site/rules/whitespace/index.html deleted file mode 100644 index ba8425ca03f..00000000000 --- a/docs/_site/rules/whitespace/index.html +++ /dev/null @@ -1,162 +0,0 @@ - - - - - - - - - Rule: whitespace - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule: whitespace

-

-
- - -

Enforces whitespace style conventions.

- - - -
Rationale
-

Helps maintain a readable, consistent style in your codebase.

- - - - -

Config

- -

Seven arguments may be optionally provided:

- -
    -
  • "check-branch" checks branching statements (if/else/for/while) are followed by whitespace.
  • -
  • "check-decl"checks that variable declarations have whitespace around the equals token.
  • -
  • "check-operator" checks for whitespace around operator tokens.
  • -
  • "check-module" checks for whitespace in import & export statements.
  • -
  • "check-separator" checks for whitespace after separator tokens (,/;).
  • -
  • "check-type" checks for whitespace before a variable type specification.
  • -
  • "check-typecast" checks for whitespace between a typecast and its target.
  • -
- - -
Examples
- -
-"whitespace": [true, "check-branch", "check-operator", "check-typecast"]
-
- - -
Schema
-
-{
-  "type": "array",
-  "items": {
-    "type": "string",
-    "enum": [
-      "check-branch",
-      "check-decl",
-      "check-operator",
-      "check-module",
-      "check-seperator",
-      "check-type",
-      "check-typecast"
-    ]
-  },
-  "minLength": 0,
-  "maxLength": 7
-}
-
-
-
- - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/usage/cli/index.html b/docs/_site/usage/cli/index.html deleted file mode 100644 index 8bf6fe057a7..00000000000 --- a/docs/_site/usage/cli/index.html +++ /dev/null @@ -1,277 +0,0 @@ - - - - - - - - - CLI - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

CLI

-

-
- - -

Installation

- -

Local (in your project’s working directory):

- -

-$ npm install tslint typescript --save-dev -

- -

Global:

- -

-$ npm install tslint typescript -g -

- -
Peer dependencies
- -

The typescript module is a peer dependency of TSLint, which allows you to update the compiler independently from the -linter. This also means that tslint will have to use the same version of tsc used to actually compile your sources.

- -

Breaking changes in the latest dev release of typescript@next might break something in the linter if we haven’t built against that release yet. If this happens to you, you can try:

- -
    -
  1. picking up tslint@next, which may have some bugfixes not released in tslint@latest -(see release notes here).
  2. -
  3. rolling back typescript to a known working version.
  4. -
- -

Usage

- -

Please ensure that the TypeScript source files compile correctly before running the linter.

- -

Usage: tslint [options] [file ...]

- -

Options:

- -

--c, --config configuration file ---force return status code 0 even if there are lint errors --h, --help display detailed help --i, --init generate a tslint.json config file in the current working directory --o, --out output file --r, --rules-dir rules directory --s, --formatters-dir formatters directory --e, --exclude exclude globs from path expansion --t, --format output format (prose, json, verbose, pmd, msbuild, checkstyle, vso) [default: "prose"] ---test test that tslint produces the correct output for the specified directory --v, --version current version -

- -

By default, TSLint looks for a configuration file named tslint.json in the directory -of the file being linted and, if not found, searches ancestor directories. Check out the rules section for more details on what rules are available.

- -

tslint accepts the following command-line options:

- -

``` --c, –config: - The location of the configuration file that tslint will use to - determine which rules are activated and what options to provide - to the rules. If no option is specified, the config file named - tslint.json is used, so long as it exists in the path. - The format of the file is { rules: { /* rules list / } }, - where / rules list */ is a key: value comma-seperated list of - rulename: rule-options pairs. Rule-options can be either a - boolean true/false value denoting whether the rule is used or not, - or a list [boolean, …] where the boolean provides the same role - as in the non-list case, and the rest of the list are options passed - to the rule that will determine what it checks for (such as number - of characters for the max-line-length rule, or what functions to ban - for the ban rule).

- -

-e, –exclude: - A filename or glob which indicates files to exclude from linting. - This option can be supplied multiple times if you need multiple - globs to indicate which files to exclude.

- -

–force: - Return status code 0 even if there are any lint errors. - Useful while running as npm script.

- -

-i, –init: - Generates a tslint.json config file in the current working directory.

- -

-o, –out: - A filename to output the results to. By default, tslint outputs to - stdout, which is usually the console where you’re running it from.

- -

-r, –rules-dir: - An additional rules directory, for user-created rules. - tslint will always check its default rules directory, in - node_modules/tslint/lib/rules, before checking the user-provided - rules directory, so rules in the user-provided rules directory - with the same name as the base rules will not be loaded.

- -

-s, –formatters-dir: - An additional formatters directory, for user-created formatters. - Formatters are files that will format the tslint output, before - writing it to stdout or the file passed in –out. The default - directory, node_modules/tslint/build/formatters, will always be - checked first, so user-created formatters with the same names - as the base formatters will not be loaded.

- -

-t, –format: - The formatter to use to format the results of the linter before - outputting it to stdout or the file passed in –out. The core - formatters are prose (human readable), json (machine readable) - and verbose. prose is the default if this option is not used. - Other built-in options include pmd, msbuild, checkstyle, and vso. - Additonal formatters can be added and used if the –formatters-dir - option is set.

- -

–test: - Runs tslint on the specified directory and checks if tslint’s output matches - the expected output in .lint files. Automatically loads the tslint.json file in the - specified directory as the configuration file for the tests. See the - full tslint documentation for more details on how this can be used to test custom rules.

- -

-v, –version: - The current version of tslint.

- -

-h, –help: - Prints this help message. -```

- - -
-
- - - - - - - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/usage/custom-rules/index.html b/docs/_site/usage/custom-rules/index.html deleted file mode 100644 index 1eeed2c88b6..00000000000 --- a/docs/_site/usage/custom-rules/index.html +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - - - - Custom Rules - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Custom Rules

-

-
- - -

If we don’t have all the rules you’re looking for, -you can either write your own custom rules or use custom rules that others have developed.

- -

Then, when using the CLI, point it to a directory with your compiled custom rules like the following:

- -

-tslint --rules-dir path/to/directory-with-rules/ file/to/lint.ts -

- -

You can do similarly when using the library version by specifying a rulesDirectory field of your options object.

- -

Finally, you can specify the path to your custom rules inside of your tslint.json file.

- -

Custom Rules from the TypeScript Community

- -

The repos below are good sources of community-created TSLint rules:

- - - - -
-
- - - - - - - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/usage/library/index.html b/docs/_site/usage/library/index.html deleted file mode 100644 index 52b4c6efd5d..00000000000 --- a/docs/_site/usage/library/index.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - - - - Library - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Library

-

-
- - -

Installation

-
- -

-npm install tslint -npm install typescript -

- -
Peer dependencies
- -

The typescript module is a peer dependency of TSLint, which allows you to update the compiler independently from the -linter. This also means that tslint will have to use the same version of tsc used to actually compile your sources.

- -

Breaking changes in the latest dev release of typescript@next might break something in the linter if we haven’t built against that release yet. If this happens to you, you can try:

- -
    -
  1. picking up tslint@next, which may have some bugfixes not released in tslint@latest -(see release notes here).
  2. -
  3. rolling back typescript to a known working version.
  4. -
- -

Usage

-
- -

Please ensure that the TypeScript source files compile correctly before running the linter.

- -

```ts -var fileName = “Specify file name”;

- -

var configuration = { - rules: { - “variable-name”: true, - “quotemark”: [true, “double”] - } -};

- -

var options = { - formatter: “json”, - configuration: configuration, - rulesDirectory: “customRules/”, // can be an array of directories - formattersDirectory: “customFormatters/” -};

- -

var Linter = require(“tslint”); -var fs = require(“fs”); -var contents = fs.readFileSync(fileName, “utf8”);

- -

var ll = new Linter(fileName, contents, options); -var result = ll.lint(); -```

- -
-
- - - - - - - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/usage/rule-flags/index.html b/docs/_site/usage/rule-flags/index.html deleted file mode 100644 index 1d7ffa437a8..00000000000 --- a/docs/_site/usage/rule-flags/index.html +++ /dev/null @@ -1,194 +0,0 @@ - - - - - - - - - Rule Flags - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

Rule Flags

-

-
- - -

You can enable/disable TSLint or a subset of rules within a file with the following comment rule flags:

- -
    -
  • /* tslint:disable */ - Disable all rules for the rest of the file
  • -
  • /* tslint:enable */ - Enable all rules for the rest of the file
  • -
  • /* tslint:disable:rule1 rule2 rule3... */ - Disable the listed rules for the rest of the file
  • -
  • /* tslint:enable:rule1 rule2 rule3... */ - Enable the listed rules for the rest of the file
  • -
  • // tslint:disable-next-line - Disables all rules for the following line
  • -
  • someCode(); // tslint:disable-line - Disables all rules for the current line
  • -
  • // tslint:disable-next-line:rule1 rule2 rule3... - Disables the listed rules for the next line
  • -
  • etc.
  • -
- -

Rules flags enable or disable rules as they are parsed. Disabling an already disabled rule or enabling an already enabled rule has no effect.

- -

For example, imagine the directive /* tslint:disable */ on the first line of a file, /* tslint:enable:ban class-name */ on the 10th line and /* tslint:enable */ on the 20th. No rules will be checked between the 1st and 10th lines, only the ban and class-name rules will be checked between the 10th and 20th, and all rules will be checked for the remainder of the file.

- -

Here’s an example:

- -

```ts -function validRange (range: any) { - return range.min <= range.middle && range.middle <= range.max; -}

- -

/* tslint:disable:object-literal-sort-keys / -const range = { - min: 5, - middle: 10, // TSLint will *not warn about unsorted keys here - max: 20 -}; -/* tslint:enable:object-literal-sort-keys */

- -

const point = { - x: 3, - z: 5, // TSLint will warn about unsorted keys here - y: 4, -}

- -

console.log(validRange(range)); -```

- -
-
- - - - - - - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/usage/third-party-tools/index.html b/docs/_site/usage/third-party-tools/index.html deleted file mode 100644 index 73b4c517464..00000000000 --- a/docs/_site/usage/third-party-tools/index.html +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - - - - Third-Party Tools - - - - - - - - - - - -
- - -
- - -
-
- -
- - - - - - - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/_site/usage/tslint-json/index.html b/docs/_site/usage/tslint-json/index.html deleted file mode 100644 index ebec8342f8b..00000000000 --- a/docs/_site/usage/tslint-json/index.html +++ /dev/null @@ -1,213 +0,0 @@ - - - - - - - - - tslint.json - - - - - - - - - - - -
- - -
- - -
-
-
- -
-

tslint.json

-

-
- - -

When using the CLI or many third-party tools, a file named tslint.json is used to -configure which rules get run.

- -

tslint.json files can have the following fields specified:

- -
    -
  • extends?: string | string[]: -A path(s) to another configuration file which to extend. -This value is handled using node module resolution semantics. -For example a value of “tslint-config” would cause TSLint to try and load the main file of a module -named “tslint-config” as a configuration file. -A value of “./tslint-config”, on the other hand, would be treated as a relative path to file.
  • -
  • rulesDirectory?: string | string[]: -A path(s) to a directory of custom rules. This will always be treated as a relative or absolute path.
  • -
  • rules?: any: Pairs of keys and values where each key is a rule name and each value is the configuration for that rule. -If a rule takes no options, you can simply set its value to a boolean, either true or false, to enable or disable it. -If a rule takes options, you set its value to an array where the first value is a boolean indicating if the rule is enabled and the next values are options handled by the rule. -Not all possible rules are listed here, be sure to check out the full list.
  • -
- -

tslint.json configuration files may have JavaScript-style // single-line and /* multi-line */ comments in them (even though this is technically invalid JSON). If this confuses your syntax highlighter, you may want to switch it to JavaScript format.

- -

An example tslint.json file might look like this:

- -

ts -{ - "rulesDirectory": ["path/to/custom/rules/direcotry/", "another/path/"], - "rules": { - "class-name": true, - "comment-format": [true, "check-space"], - "indent": [true, "spaces"], - "no-duplicate-variable": true, - "no-eval": true, - "no-internal-module": true, - "no-trailing-whitespace": true, - "no-var-keyword": true, - "one-line": [true, "check-open-brace", "check-whitespace"], - "quotemark": [true, "double"], - "semicolon": false, - "triple-equals": [true, "allow-null-check"], - "typedef-whitespace": [true, { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }], - "variable-name": [true, "ban-keywords"], - "whitespace": [true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ] - } -} -

- - -
-
- - - - - - - - - - - - - - -
- -
- - - -
- - - - diff --git a/docs/rules/object-literal-shorthand/index.html b/docs/rules/object-literal-shorthand/index.html new file mode 100644 index 00000000000..3917afade21 --- /dev/null +++ b/docs/rules/object-literal-shorthand/index.html @@ -0,0 +1,11 @@ +--- +ruleName: object-literal-shorthand +description: Enforces use of ES6 object literal shorthand when possible. +options: null +optionExamples: + - 'true' +type: style +optionsJSON: 'null' +layout: rule +title: 'Rule: object-literal-shorthand' +--- \ No newline at end of file diff --git a/docs/rules/only-arrow-functions/index.html b/docs/rules/only-arrow-functions/index.html index 8e1ecc8ce4e..7d5adbd6f63 100644 --- a/docs/rules/only-arrow-functions/index.html +++ b/docs/rules/only-arrow-functions/index.html @@ -2,12 +2,36 @@ ruleName: only-arrow-functions description: Disallows traditional (non-arrow) function expressions. rationale: 'Traditional functions don''t bind lexical scope, which can lead to unexpected behavior when accessing ''this''.' -optionsDescription: Not configurable. -options: null +optionsDescription: |- + + One argument may be optionally provided: + + * `"allow-declarations"` allows standalone function declarations. + +options: + type: array + items: + type: string + enum: + - allow-declarations + minLength: 0 + maxLength: 1 optionExamples: - 'true' + - '[true, "allow-declarations"]' type: typescript -optionsJSON: 'null' +optionsJSON: |- + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "allow-declarations" + ] + }, + "minLength": 0, + "maxLength": 1 + } layout: rule title: 'Rule: only-arrow-functions' --- \ No newline at end of file diff --git a/docs/rules/ordered-imports/index.html b/docs/rules/ordered-imports/index.html index eed3ec59602..8a87a1b21b2 100644 --- a/docs/rules/ordered-imports/index.html +++ b/docs/rules/ordered-imports/index.html @@ -14,18 +14,33 @@ however you like, e.g. by first- vs. third-party or thematically. optionsDescription: |- + You may set the `"import-sources-order"` option to control the ordering of source + imports (the `"foo"` in `import {A, B, C} from "foo"`). + + Possible values for `"import-sources-order"` are: + * `"case-insensitive'`: Correct order is `"Bar"`, `"baz"`, `"Foo"`. (This is the default.) + * `"lowercase-first"`: Correct order is `"baz"`, `"Bar"`, `"Foo"`. + * `"lowercase-last"`: Correct order is `"Bar"`, `"Foo"`, `"baz"`. + You may set the `"named-imports-order"` option to control the ordering of named - imports (the `{A, B, C}` in 'import {A, B, C} from "foo"`.) + imports (the `{A, B, C}` in `import {A, B, C} from "foo"`). Possible values for `"named-imports-order"` are: * `"case-insensitive'`: Correct order is `{A, b, C}`. (This is the default.) * `"lowercase-first"`: Correct order is `{b, A, C}`. * `"lowercase-last"`: Correct order is `{A, C, b}`. + options: type: object properties: + import-sources-order: + type: string + enum: + - case-insensitive + - lowercase-first + - lowercase-last named-imports-order: type: string enum: @@ -35,12 +50,20 @@ additionalProperties: false optionExamples: - 'true' - - '[true, {"named-imports-order": "lowercase-first"}]' + - '[true, {"import-sources-order": "lowercase-last", "named-imports-order": "lowercase-first"}]' type: style optionsJSON: |- { "type": "object", "properties": { + "import-sources-order": { + "type": "string", + "enum": [ + "case-insensitive", + "lowercase-first", + "lowercase-last" + ] + }, "named-imports-order": { "type": "string", "enum": [ diff --git a/docs/rules/typedef/index.html b/docs/rules/typedef/index.html index cf038216b06..727f9ed08b6 100644 --- a/docs/rules/typedef/index.html +++ b/docs/rules/typedef/index.html @@ -3,9 +3,10 @@ description: Requires type definitions to exist. optionsDescription: |- - Six arguments may be optionally provided: + Seven arguments may be optionally provided: * `"call-signature"` checks return type of functions. + * `"arrow-call-signature"` checks return type of arrow functions. * `"parameter"` checks type specifier of function parameters for non-arrow functions. * `"arrow-parameter"` checks type specifier of function parameters for arrow functions. * `"property-declaration"` checks return types of interface properties. @@ -17,13 +18,14 @@ type: string enum: - call-signature + - arrow-call-signature - parameter - arrow-parameter - property-declaration - variable-declaration - member-variable-declaration minLength: 0 - maxLength: 6 + maxLength: 7 optionExamples: - '[true, "call-signature", "parameter", "member-variable-declaration"]' type: typescript @@ -34,6 +36,7 @@ "type": "string", "enum": [ "call-signature", + "arrow-call-signature", "parameter", "arrow-parameter", "property-declaration", @@ -42,7 +45,7 @@ ] }, "minLength": 0, - "maxLength": 6 + "maxLength": 7 } layout: rule title: 'Rule: typedef' diff --git a/docs/rules/whitespace/index.html b/docs/rules/whitespace/index.html index cf5b8613e94..4e591aa49a2 100644 --- a/docs/rules/whitespace/index.html +++ b/docs/rules/whitespace/index.html @@ -22,7 +22,7 @@ - check-decl - check-operator - check-module - - check-seperator + - check-separator - check-type - check-typecast minLength: 0 @@ -40,7 +40,7 @@ "check-decl", "check-operator", "check-module", - "check-seperator", + "check-separator", "check-type", "check-typecast" ] diff --git a/docs/usage/tslint-json/index.md b/docs/usage/tslint-json/index.md index 56302a008c5..4d2bd202f41 100644 --- a/docs/usage/tslint-json/index.md +++ b/docs/usage/tslint-json/index.md @@ -28,7 +28,7 @@ An example `tslint.json` file might look like this: ```ts { - "rulesDirectory": ["path/to/custom/rules/direcotry/", "another/path/"], + "rulesDirectory": ["path/to/custom/rules/directory/", "another/path/"], "rules": { "class-name": true, "comment-format": [true, "check-space"], diff --git a/package.json b/package.json index d4ba76101ac..fb0a1fb6564 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tslint", - "version": "3.15.0-dev.0", + "version": "4.0.0-dev.0", "description": "An extensible static analysis linter for the TypeScript language", "bin": { "tslint": "./bin/tslint" diff --git a/src/configs/latest.ts b/src/configs/latest.ts index e5d6f44497b..3579a7245e8 100644 --- a/src/configs/latest.ts +++ b/src/configs/latest.ts @@ -16,8 +16,14 @@ */ export const rules = { + "adjacent-overload-signatures": true, + "cyclomatic-complexity": false, "no-unsafe-finally": true, + "object-literal-key-quotes": [true, "as-needed"], + "object-literal-shorthand": true, + "only-arrow-functions": [true, "allow-declarations"], "ordered-imports": [true, { + "import-sources-order": "case-insensitive", "named-imports-order": "lowercase-last", }], }; diff --git a/src/configs/recommended.ts b/src/configs/recommended.ts index 323c6fd0be8..11f0dfa92d5 100644 --- a/src/configs/recommended.ts +++ b/src/configs/recommended.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +/* tslint:disable:object-literal-key-quotes */ export const rules = { "align": [true, "parameters", @@ -89,8 +90,8 @@ export const rules = { "switch-default": true, "trailing-comma": [true, { - "singleline": "never", "multiline": "always", + "singleline": "never", }, ], "triple-equals": [true, "allow-null-check"], @@ -125,3 +126,4 @@ export const rules = { "check-typecast", ], }; +/* tslint:enable:object-literal-key-quotes */ diff --git a/src/configuration.ts b/src/configuration.ts index 790fc241f5c..e9039169c90 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -32,6 +32,7 @@ export interface IConfigurationFile { } export const CONFIG_FILENAME = "tslint.json"; +/* tslint:disable:object-literal-key-quotes */ export const DEFAULT_CONFIG = { "rules": { "class-name": true, @@ -66,9 +67,7 @@ export const DEFAULT_CONFIG = { ], }, }; - -const PACKAGE_DEPRECATION_MSG = "Configuration of TSLint via package.json has been deprecated, " - + "please start using a tslint.json file instead (http://palantir.github.io/tslint/usage/tslint-json/)."; +/* tslint:enable:object-literal-key-quotes */ const BUILT_IN_CONFIG = /^tslint:(.*)$/; @@ -91,7 +90,7 @@ export function findConfiguration(configFile: string, inputFilePath: string): IC * the location of the config file is not known and you want to search for one. * @param inputFilePath A path to the current file being linted. This is the starting location * of the search for a configuration. - * @returns An absolute path to a tslint.json file, a path to a package.json file with a tslintConfig field + * @returns An absolute path to a tslint.json file * or undefined if neither can be found. */ export function findConfigurationPath(suppliedConfigFilePath: string, inputFilePath: string) { @@ -108,12 +107,6 @@ export function findConfigurationPath(suppliedConfigFilePath: string, inputFileP return path.resolve(configFilePath); } - // search for package.json with tslintConfig property - configFilePath = findup("package.json", { cwd: inputFilePath, nocase: true }); - if (configFilePath != null && require(configFilePath).tslintConfig != null) { - return path.resolve(configFilePath); - } - // search for tslint.json in home directory const homeDir = getHomeDir(); if (homeDir != null) { @@ -139,9 +132,6 @@ export function findConfigurationPath(suppliedConfigFilePath: string, inputFileP export function loadConfigurationFromPath(configFilePath: string): IConfigurationFile { if (configFilePath == null) { return DEFAULT_CONFIG; - } else if (path.basename(configFilePath) === "package.json") { - console.warn(PACKAGE_DEPRECATION_MSG); - return require(configFilePath).tslintConfig; } else { const resolvedConfigFilePath = resolveConfigurationPath(configFilePath); let configFile: IConfigurationFile; diff --git a/src/enableDisableRules.ts b/src/enableDisableRules.ts index a01657499b3..b67ca86b43a 100644 --- a/src/enableDisableRules.ts +++ b/src/enableDisableRules.ts @@ -89,7 +89,7 @@ export class EnableDisableRulesWalker extends SkippableTokenAwareRuleWalker { if (isCurrentLine) { // start at the beginning of the current line this.enableDisableRuleMap[ruleToAdd].push({ - isEnabled: isEnabled, + isEnabled, position: this.getStartOfLinePosition(node, startingPosition), }); // end at the beginning of the next line @@ -100,7 +100,7 @@ export class EnableDisableRulesWalker extends SkippableTokenAwareRuleWalker { } else { // start at the current position this.enableDisableRuleMap[ruleToAdd].push({ - isEnabled: isEnabled, + isEnabled, position: startingPosition, }); // end at the beginning of the line following the next line diff --git a/src/formatters/fileslistFormatter.ts b/src/formatters/fileslistFormatter.ts new file mode 100644 index 00000000000..27c26c7a7cf --- /dev/null +++ b/src/formatters/fileslistFormatter.ts @@ -0,0 +1,40 @@ +/** + * @license + * Copyright 2013 Palantir Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {AbstractFormatter} from "../language/formatter/abstractFormatter"; +import {RuleFailure} from "../language/rule/rule"; + +export class Formatter extends AbstractFormatter { + public format(failures: RuleFailure[]): string { + if (failures.length === 0) { + return ""; + } + + const files: string[] = []; + let currentFile: string; + + for (const failure of failures) { + const fileName = failure.getFileName(); + if (fileName !== currentFile) { + files.push(fileName); + currentFile = fileName; + } + } + + return files.join("\n") + "\n"; + } +} diff --git a/src/formatters/index.ts b/src/formatters/index.ts index eedf95a0b69..5f64b8dabba 100644 --- a/src/formatters/index.ts +++ b/src/formatters/index.ts @@ -20,3 +20,4 @@ export { Formatter as PmdFormatter } from "./pmdFormatter"; export { Formatter as ProseFormatter } from "./proseFormatter"; export { Formatter as VerboseFormatter } from "./verboseFormatter"; export { Formatter as StylishFormatter } from "./stylishFormatter"; +export { Formatter as FileslistFormatter } from "./fileslistFormatter"; diff --git a/src/formatters/proseFormatter.ts b/src/formatters/proseFormatter.ts index 9a0375dafdb..132623b0306 100644 --- a/src/formatters/proseFormatter.ts +++ b/src/formatters/proseFormatter.ts @@ -20,6 +20,10 @@ import {RuleFailure} from "../language/rule/rule"; export class Formatter extends AbstractFormatter { public format(failures: RuleFailure[]): string { + if (failures.length === 0) { + return ""; + } + const outputLines = failures.map((failure: RuleFailure) => { const fileName = failure.getFileName(); const failureString = failure.getFailure(); diff --git a/src/formatters/stylishFormatter.ts b/src/formatters/stylishFormatter.ts index 6bae6259278..bb35cf0d7f2 100644 --- a/src/formatters/stylishFormatter.ts +++ b/src/formatters/stylishFormatter.ts @@ -26,15 +26,22 @@ export class Formatter extends AbstractFormatter { return "\n"; } - const fileName = failures[0].getFileName(); - const positionMaxSize = this.getPositionMaxSize(failures); - const ruleMaxSize = this.getRuleMaxSize(failures); + const outputLines: string[] = []; + const positionMaxSize = this.getPositionMaxSize(failures); + const ruleMaxSize = this.getRuleMaxSize(failures); - const outputLines = [ - fileName, - ]; + let currentFile: string; for (const failure of failures) { + const fileName = failure.getFileName(); + + // Output the name of each file once + if (currentFile !== fileName) { + outputLines.push(""); + outputLines.push(fileName); + currentFile = fileName; + } + const failureString = failure.getFailure(); // Rule @@ -55,6 +62,11 @@ export class Formatter extends AbstractFormatter { outputLines.push(output); } + // Removes initial blank line + if (outputLines[0] === "") { + outputLines.shift(); + } + return outputLines.join("\n") + "\n\n"; } diff --git a/src/language/rule/abstractRule.ts b/src/language/rule/abstractRule.ts index 4e5af465c71..c05c1f25fb4 100644 --- a/src/language/rule/abstractRule.ts +++ b/src/language/rule/abstractRule.ts @@ -33,9 +33,9 @@ export abstract class AbstractRule implements IRule { } this.options = { - disabledIntervals: disabledIntervals, - ruleArguments: ruleArguments, - ruleName: ruleName, + disabledIntervals, + ruleArguments, + ruleName, }; } diff --git a/src/language/utils.ts b/src/language/utils.ts index 3891961885c..5982866821a 100644 --- a/src/language/utils.ts +++ b/src/language/utils.ts @@ -29,9 +29,9 @@ export function getSourceFile(fileName: string, source: string): ts.SourceFile { getCanonicalFileName: (filename: string) => filename, getCurrentDirectory: () => "", getDefaultLibFileName: () => "lib.d.ts", - getDirectories: () => [], + getDirectories: (path: string) => [], getNewLine: () => "\n", - getSourceFile: function (filenameToGet: string) { + getSourceFile: (filenameToGet: string) => { if (filenameToGet === normalizedName) { return ts.createSourceFile(filenameToGet, source, compilerOptions.target, true); } diff --git a/src/language/walker/syntaxWalker.ts b/src/language/walker/syntaxWalker.ts index 245bc0e6ae4..61470816f16 100644 --- a/src/language/walker/syntaxWalker.ts +++ b/src/language/walker/syntaxWalker.ts @@ -30,6 +30,10 @@ export class SyntaxWalker { this.walkChildren(node); } + protected visitArrayType(node: ts.ArrayTypeNode) { + this.walkChildren(node); + } + protected visitArrowFunction(node: ts.FunctionLikeDeclaration) { this.walkChildren(node); } @@ -82,6 +86,10 @@ export class SyntaxWalker { this.walkChildren(node); } + protected visitConstructSignature(node: ts.ConstructSignatureDeclaration) { + this.walkChildren(node); + } + protected visitConstructorDeclaration(node: ts.ConstructorDeclaration) { this.walkChildren(node); } @@ -190,6 +198,10 @@ export class SyntaxWalker { this.walkChildren(node); } + protected visitJsxSpreadAttribute(node: ts.JsxSpreadAttribute) { + this.walkChildren(node); + } + protected visitLabeledStatement(node: ts.LabeledStatement) { this.walkChildren(node); } @@ -286,6 +298,14 @@ export class SyntaxWalker { this.walkChildren(node); } + protected visitTupleType(node: ts.TupleTypeNode) { + this.walkChildren(node); + } + + protected visitTypeAliasDeclaration(node: ts.TypeAliasDeclaration) { + this.walkChildren(node); + } + protected visitTypeAssertionExpression(node: ts.TypeAssertion) { this.walkChildren(node); } @@ -328,6 +348,10 @@ export class SyntaxWalker { this.visitArrayLiteralExpression( node); break; + case ts.SyntaxKind.ArrayType: + this.visitArrayType( node); + break; + case ts.SyntaxKind.ArrowFunction: this.visitArrowFunction( node); break; @@ -376,6 +400,10 @@ export class SyntaxWalker { this.visitConditionalExpression( node); break; + case ts.SyntaxKind.ConstructSignature: + this.visitConstructSignature( node); + break; + case ts.SyntaxKind.Constructor: this.visitConstructorDeclaration( node); break; @@ -484,6 +512,10 @@ export class SyntaxWalker { this.visitJsxSelfClosingElement( node); break; + case ts.SyntaxKind.JsxSpreadAttribute: + this.visitJsxSpreadAttribute( node); + break; + case ts.SyntaxKind.LabeledStatement: this.visitLabeledStatement( node); break; @@ -584,6 +616,14 @@ export class SyntaxWalker { this.visitTryStatement( node); break; + case ts.SyntaxKind.TupleType: + this.visitTupleType( node); + break; + + case ts.SyntaxKind.TypeAliasDeclaration: + this.visitTypeAliasDeclaration( node); + break; + case ts.SyntaxKind.TypeAssertionExpression: this.visitTypeAssertionExpression( node); break; diff --git a/src/lint.ts b/src/lint.ts index ae58347c1f5..35f3a1af219 100644 --- a/src/lint.ts +++ b/src/lint.ts @@ -58,3 +58,9 @@ export interface ILinterOptions extends ILinterOptionsRaw { formatter: string | Function; rulesDirectory: string | string[]; } + +export interface IMultiLinterOptions { + formatter?: string | Function; + formattersDirectory?: string; + rulesDirectory?: string | string[]; +} diff --git a/src/rules/arrayTypeRule.ts b/src/rules/arrayTypeRule.ts new file mode 100644 index 00000000000..256e6cb1098 --- /dev/null +++ b/src/rules/arrayTypeRule.ts @@ -0,0 +1,114 @@ +import * as ts from "typescript"; + +import * as Lint from "../lint"; + +const OPTION_ARRAY = "array"; +const OPTION_GENERIC = "generic"; +const OPTION_ARRAY_SIMPLE = "array-simple"; + +export class Rule extends Lint.Rules.AbstractRule { + /* tslint:disable:object-literal-sort-keys */ + public static metadata: Lint.IRuleMetadata = { + ruleName: "array-type", + description: "Requires using either 'T[]' or 'Array' for arrays.", + optionsDescription: Lint.Utils.dedent` + One of the following arguments must be provided: + + * \`"${OPTION_ARRAY}"\` enforces use of \`T[]\` for all types T. + * \`"${OPTION_GENERIC}"\` enforces use of \`Array\` for all types T. + * \`"${OPTION_ARRAY_SIMPLE}"\` enforces use of \`T[]\` if \`T\` is a simple type (primitive or type reference).`, + options: { + type: "string", + enum: [OPTION_ARRAY, OPTION_GENERIC, OPTION_ARRAY_SIMPLE], + }, + optionExamples: [`[true, ${OPTION_ARRAY}]`, `[true, ${OPTION_GENERIC}]`, `[true, ${OPTION_ARRAY_SIMPLE}]`], + type: "style", + }; + /* tslint:enable:object-literal-sort-keys */ + + public static FAILURE_STRING_ARRAY = "Array type using 'Array' is forbidden. Use 'T[]' instead."; + public static FAILURE_STRING_GENERIC = "Array type using 'T[]' is forbidden. Use 'Array' instead."; + public static FAILURE_STRING_ARRAY_SIMPLE = "Array type using 'Array' is forbidden for simple types. Use 'T[]' instead."; + public static FAILURE_STRING_GENERIC_SIMPLE = "Array type using 'T[]' is forbidden for non-simple types. Use 'Array' instead."; + + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + const alignWalker = new ArrayTypeWalker(sourceFile, this.getOptions()); + return this.applyWithWalker(alignWalker); + } +} + +class ArrayTypeWalker extends Lint.RuleWalker { + public visitArrayType(node: ts.ArrayTypeNode) { + const typeName = node.elementType; + if (this.hasOption(OPTION_GENERIC) || this.hasOption(OPTION_ARRAY_SIMPLE) && !this.isSimpleType(typeName)) { + const failureString = this.hasOption(OPTION_GENERIC) ? Rule.FAILURE_STRING_GENERIC : Rule.FAILURE_STRING_GENERIC_SIMPLE; + const parens = typeName.kind === ts.SyntaxKind.ParenthesizedType ? 1 : 0; + // Add a space if the type is preceded by 'as' and the node has no leading whitespace + const space = !parens && node.parent.kind === ts.SyntaxKind.AsExpression && + node.getStart() === node.getFullStart() ? " " : ""; + const fix = new Lint.Fix(Rule.metadata.ruleName, [ + this.createReplacement(typeName.getStart(), parens, space + "Array<"), + // Delete the square brackets and replace with an angle bracket + this.createReplacement(typeName.getEnd() - parens, node.getEnd() - typeName.getEnd() + parens, ">"), + ]); + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), failureString, fix)); + } + + super.visitArrayType(node); + } + + public visitTypeReference(node: ts.TypeReferenceNode) { + const typeName = node.typeName.getText(); + if (typeName === "Array" && (this.hasOption(OPTION_ARRAY) || this.hasOption(OPTION_ARRAY_SIMPLE))) { + const failureString = this.hasOption(OPTION_ARRAY) ? Rule.FAILURE_STRING_ARRAY : Rule.FAILURE_STRING_ARRAY_SIMPLE; + const typeArgs = node.typeArguments; + if (!typeArgs || typeArgs.length === 0) { + // Create an 'any' array + const fix = new Lint.Fix(Rule.metadata.ruleName, [ + this.createReplacement(node.getStart(), node.getWidth(), "any[]"), + ]); + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), failureString, fix)); + } else if (typeArgs && typeArgs.length === 1 && (!this.hasOption(OPTION_ARRAY_SIMPLE) || this.isSimpleType(typeArgs[0]))) { + const type = typeArgs[0]; + const typeStart = type.getStart(); + const typeEnd = type.getEnd(); + const parens = type.kind === ts.SyntaxKind.UnionType || + type.kind === ts.SyntaxKind.FunctionType || type.kind === ts.SyntaxKind.IntersectionType; + const fix = new Lint.Fix(Rule.metadata.ruleName, [ + // Delete Array and the first angle bracket + this.createReplacement(node.getStart(), typeStart - node.getStart(), parens ? "(" : ""), + // Delete the last angle bracket and replace with square brackets + this.createReplacement(typeEnd, node.getEnd() - typeEnd, (parens ? ")" : "") + "[]"), + ]); + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), failureString, fix)); + } + } + + super.visitTypeReference(node); + } + + private isSimpleType(nodeType: ts.TypeNode) { + switch (nodeType.kind) { + case ts.SyntaxKind.AnyKeyword: + case ts.SyntaxKind.ArrayType: + case ts.SyntaxKind.BooleanKeyword: + case ts.SyntaxKind.NullKeyword: + case ts.SyntaxKind.NumberKeyword: + case ts.SyntaxKind.StringKeyword: + case ts.SyntaxKind.SymbolKeyword: + case ts.SyntaxKind.VoidKeyword: + return true; + case ts.SyntaxKind.TypeReference: + // TypeReferences must be non-generic or be another Array with a simple type + const node = nodeType as ts.TypeReferenceNode; + const typeArgs = node.typeArguments; + if (!typeArgs || typeArgs.length === 0 || node.typeName.getText() === "Array" && this.isSimpleType(typeArgs[0])) { + return true; + } else { + return false; + } + default: + return false; + } + } +} diff --git a/src/rules/banRule.ts b/src/rules/banRule.ts index c958b6cb81d..c0667ce45d8 100644 --- a/src/rules/banRule.ts +++ b/src/rules/banRule.ts @@ -23,21 +23,21 @@ export class Rule extends Lint.Rules.AbstractRule { /* tslint:disable:object-literal-sort-keys */ public static metadata: Lint.IRuleMetadata = { ruleName: "ban", - description: "Bans the use of specific functions.", - descriptionDetails: "At this time, there is no way to disable global methods with this rule.", - optionsDescription: "A list of `['object', 'method', 'optional explanation here']` which ban `object.method()`.", + description: "Bans the use of specific functions or global methods.", + optionsDescription: Lint.Utils.dedent` + A list of \`['object', 'method', 'optional explanation here']\` or \`['globalMethod']\` which ban \`object.method()\` + or respectively \`globalMethod()\`.`, options: { type: "list", listType: { type: "array", - arrayMembers: [ - { type: "string" }, - { type: "string" }, - { type: "string" }, - ], + items: {type: "string"}, + minLength: 1, + maxLength: 3, }, }, - optionExamples: [`[true, ["someObject", "someFunction"], ["someObject", "otherFunction", "Optional explanation"]]`], + optionExamples: [`[true, ["someGlobalMethod"], ["someObject", "someFunction"], + ["someObject", "otherFunction", "Optional explanation"]]`], type: "functionality", }; /* tslint:enable:object-literal-sort-keys */ @@ -56,17 +56,29 @@ export class Rule extends Lint.Rules.AbstractRule { } export class BanFunctionWalker extends Lint.RuleWalker { + private bannedGlobalFunctions: string[] = []; private bannedFunctions: string[][] = []; public addBannedFunction(bannedFunction: string[]) { - this.bannedFunctions.push(bannedFunction); + if (bannedFunction.length === 1) { + this.bannedGlobalFunctions.push(bannedFunction[0]); + } else if (bannedFunction.length >= 2) { + this.bannedFunctions.push(bannedFunction); + } } public visitCallExpression(node: ts.CallExpression) { const expression = node.expression; + this.checkForObjectMethodBan(expression); + this.checkForGlobalBan(expression); + + super.visitCallExpression(node); + } + + private checkForObjectMethodBan(expression: ts.LeftHandSideExpression) { if (expression.kind === ts.SyntaxKind.PropertyAccessExpression - && expression.getChildCount() >= 3) { + && expression.getChildCount() >= 3) { const firstToken = expression.getFirstToken(); const firstChild = expression.getChildAt(0); @@ -96,7 +108,16 @@ export class BanFunctionWalker extends Lint.RuleWalker { } } } + } - super.visitCallExpression(node); + private checkForGlobalBan(expression: ts.LeftHandSideExpression) { + if (expression.kind === ts.SyntaxKind.Identifier) { + const identifierName = ( expression).text; + if (this.bannedGlobalFunctions.indexOf(identifierName) !== -1) { + this.addFailure(this.createFailure(expression.getStart(), expression.getWidth(), + Rule.FAILURE_STRING_FACTORY(`${identifierName}`))); + } + + } } } diff --git a/src/rules/commentFormatRule.ts b/src/rules/commentFormatRule.ts index f43eadb2ea6..76c920d53c4 100644 --- a/src/rules/commentFormatRule.ts +++ b/src/rules/commentFormatRule.ts @@ -129,12 +129,18 @@ function startsWithSpace(commentText: string) { return true; // comment is "//"? Technically not a violation. } + const commentBody = commentText.substring(2); + // whitelist //#region and //#endregion - if ((/^#(end)?region/).test(commentText.substring(2))) { + if ((/^#(end)?region/).test(commentBody)) { + return true; + } + // whitelist JetBrains IDEs' "//noinspection ..." + if ((/^noinspection\s/).test(commentBody)) { return true; } - const firstCharacter = commentText.charAt(2); // first character after the space + const firstCharacter = commentBody.charAt(0); // three slashes (///) also works, to allow for /// return firstCharacter === " " || firstCharacter === "/"; } diff --git a/src/rules/curlyRule.ts b/src/rules/curlyRule.ts index dc8cb3560e6..8a053bd149f 100644 --- a/src/rules/curlyRule.ts +++ b/src/rules/curlyRule.ts @@ -61,12 +61,12 @@ class CurlyWalker extends Lint.RuleWalker { super.visitForInStatement(node); } - public visitForOfStatement(node: ts.ForInStatement) { + public visitForOfStatement(node: ts.ForOfStatement) { if (!isStatementBraced(node.statement)) { this.addFailureForNode(node, Rule.FOR_FAILURE_STRING); } - super.visitForInStatement(node); + super.visitForOfStatement(node); } public visitForStatement(node: ts.ForStatement) { diff --git a/src/rules/cyclomaticComplexityRule.ts b/src/rules/cyclomaticComplexityRule.ts new file mode 100644 index 00000000000..9e1f89bad7d --- /dev/null +++ b/src/rules/cyclomaticComplexityRule.ts @@ -0,0 +1,211 @@ +/** + * @license + * Copyright 2016 Palantir Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Lint from "../lint"; +import * as ts from "typescript"; + +export class Rule extends Lint.Rules.AbstractRule { + + public static DEFAULT_THRESHOLD = 20; + public static MINIMUM_THRESHOLD = 2; + + /* tslint:disable:object-literal-sort-keys */ + public static metadata: Lint.IRuleMetadata = { + ruleName: "cyclomatic-complexity", + description: "Enforces a threshold of cyclomatic complexity.", + descriptionDetails: Lint.Utils.dedent` + Cyclomatic complexity is assessed for each function of any type. A starting value of 1 + is assigned and this value is then incremented for every statement which can branch the + control flow within the function. The following statements and expressions contribute + to cyclomatic complexity: + * \`catch\` + * \`if\` and \`? :\` + * \`||\` and \`&&\` due to short-circuit evaluation + * \`for\`, \`for in\` and \`for of\` loops + * \`while\` and \`do while\` loops`, + rationale: Lint.Utils.dedent` + Cyclomatic complexity is a code metric which indicates the level of complexity in a + function. High cyclomatic complexity indicates confusing code which may be prone to + errors or difficult to modify.`, + optionsDescription: Lint.Utils.dedent` + An optional upper limit for cyclomatic complexity can be specified. If no limit option + is provided a default value of $(Rule.DEFAULT_THRESHOLD) will be used.`, + options: { + type: "number", + minimum: "$(Rule.MINIMUM_THRESHOLD)", + }, + optionExamples: ["true", "[true, 20]"], + type: "maintainability", + }; + /* tslint:enable:object-literal-sort-keys */ + + public static ANONYMOUS_FAILURE_STRING = (expected: number, actual: number) => + `The function has a cyclomatic complexity of ${actual} which is higher than the threshold of ${expected}`; + public static NAMED_FAILURE_STRING = (expected: number, actual: number, name: string) => + `The function ${name} has a cyclomatic complexity of ${actual} which is higher than the threshold of ${expected}`; + + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithWalker(new CyclomaticComplexityWalker(sourceFile, this.getOptions(), this.threshold)); + } + + public isEnabled(): boolean { + // Disable the rule if the option is provided but non-numeric or less than the minimum. + const isThresholdValid = typeof this.threshold === "number" && this.threshold >= Rule.MINIMUM_THRESHOLD; + return super.isEnabled() && isThresholdValid; + } + + private get threshold(): number { + return this.getOptions().ruleArguments[0] || Rule.DEFAULT_THRESHOLD; + } +} + +class CyclomaticComplexityWalker extends Lint.RuleWalker { + + private functions: number[] = []; + + public constructor(sourceFile: ts.SourceFile, options: Lint.IOptions, private threshold: number) { + super(sourceFile, options); + } + + protected visitArrowFunction(node: ts.FunctionLikeDeclaration) { + this.startFunction(); + super.visitArrowFunction(node); + this.endFunction(node); + } + + protected visitBinaryExpression(node: ts.BinaryExpression) { + switch (node.operatorToken.kind) { + case ts.SyntaxKind.BarBarToken: + case ts.SyntaxKind.AmpersandAmpersandToken: + this.incrementComplexity(); + break; + default: + break; + } + super.visitBinaryExpression(node); + } + + protected visitCaseClause(node: ts.CaseClause) { + this.incrementComplexity(); + super.visitCaseClause(node); + } + + protected visitCatchClause(node: ts.CatchClause) { + this.incrementComplexity(); + super.visitCatchClause(node); + } + + protected visitConditionalExpression(node: ts.ConditionalExpression) { + this.incrementComplexity(); + super.visitConditionalExpression(node); + } + + public visitConstructorDeclaration(node: ts.ConstructorDeclaration) { + this.startFunction(); + super.visitConstructorDeclaration(node); + this.endFunction(node); + } + + protected visitDoStatement(node: ts.DoStatement) { + this.incrementComplexity(); + super.visitDoStatement(node); + } + + protected visitForStatement(node: ts.ForStatement) { + this.incrementComplexity(); + super.visitForStatement(node); + } + + protected visitForInStatement(node: ts.ForInStatement) { + this.incrementComplexity(); + super.visitForInStatement(node); + } + + protected visitForOfStatement(node: ts.ForOfStatement) { + this.incrementComplexity(); + super.visitForOfStatement(node); + } + + protected visitFunctionDeclaration(node: ts.FunctionDeclaration) { + this.startFunction(); + super.visitFunctionDeclaration(node); + this.endFunction(node); + } + + protected visitFunctionExpression(node: ts.FunctionExpression) { + this.startFunction(); + super.visitFunctionExpression(node); + this.endFunction(node); + } + + protected visitGetAccessor(node: ts.AccessorDeclaration) { + this.startFunction(); + super.visitGetAccessor(node); + this.endFunction(node); + } + + protected visitIfStatement(node: ts.IfStatement) { + this.incrementComplexity(); + super.visitIfStatement(node); + } + + protected visitMethodDeclaration(node: ts.MethodDeclaration) { + this.startFunction(); + super.visitMethodDeclaration(node); + this.endFunction(node); + } + + protected visitSetAccessor(node: ts.AccessorDeclaration) { + this.startFunction(); + super.visitSetAccessor(node); + this.endFunction(node); + } + + protected visitWhileStatement(node: ts.WhileStatement) { + this.incrementComplexity(); + super.visitWhileStatement(node); + } + + private startFunction() { + // Push an initial complexity value to the stack for the new function. + this.functions.push(1); + } + + private endFunction(node: ts.FunctionLikeDeclaration) { + const complexity = this.functions.pop(); + + // Check for a violation. + if (complexity > this.threshold) { + let failureString: string; + + // Attempt to find a name for the function. + if (node.name && node.name.kind === ts.SyntaxKind.Identifier) { + failureString = Rule.NAMED_FAILURE_STRING(this.threshold, complexity, (node.name as ts.Identifier).text); + } else { + failureString = Rule.ANONYMOUS_FAILURE_STRING(this.threshold, complexity); + } + + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), failureString)); + } + } + + private incrementComplexity() { + if (this.functions.length) { + this.functions[this.functions.length - 1]++; + } + } +} diff --git a/src/rules/fileHeaderRule.ts b/src/rules/fileHeaderRule.ts index 73560f1e264..63d42683770 100644 --- a/src/rules/fileHeaderRule.ts +++ b/src/rules/fileHeaderRule.ts @@ -11,7 +11,7 @@ export class Rule extends Lint.Rules.AbstractRule { options: { type: "string", }, - optionExamples: ['"true", "Copyright \\d{4}"'], + optionExamples: ['[true, "Copyright \\\\d{4}"]'], type: "style", }; /* tslint:enable:object-literal-sort-keys */ diff --git a/src/rules/jsdocFormatRule.ts b/src/rules/jsdocFormatRule.ts index 460a612e618..d3b486d4919 100644 --- a/src/rules/jsdocFormatRule.ts +++ b/src/rules/jsdocFormatRule.ts @@ -76,8 +76,8 @@ class JsdocWalker extends Lint.SkippableTokenAwareRuleWalker { const firstLine = lines[0]; let jsdocPosition = currentPosition; - // regex is: start of string, followed by any amount of whitespace, followed by /** - const isJsdocMatch = firstLine.match(/^\s*\/\*\*/); + // regex is: start of string, followed by any amount of whitespace, followed by /** but not more than 2 ** + const isJsdocMatch = firstLine.match(/^\s*\/\*\*([^*]|$)/); if (isJsdocMatch != null) { if (lines.length === 1) { const firstLineMatch = firstLine.match(/^\s*\/\*\* (.* )?\*\/$/); diff --git a/src/rules/memberOrderingRule.ts b/src/rules/memberOrderingRule.ts index 886c5486636..614070f4f5b 100644 --- a/src/rules/memberOrderingRule.ts +++ b/src/rules/memberOrderingRule.ts @@ -42,13 +42,10 @@ const PRESET_ORDERS: { [preset: string]: string[] } = { "protected-instance-method", "private-instance-method", ], - "statics-first": [ + "instance-sandwich": [ "public-static-field", - "public-static-method", "protected-static-field", - "protected-static-method", "private-static-field", - "private-static-method", "public-instance-field", "protected-instance-field", "private-instance-field", @@ -56,11 +53,17 @@ const PRESET_ORDERS: { [preset: string]: string[] } = { "public-instance-method", "protected-instance-method", "private-instance-method", + "public-static-method", + "protected-static-method", + "private-static-method", ], - "instance-sandwich": [ + "statics-first": [ "public-static-field", + "public-static-method", "protected-static-field", + "protected-static-method", "private-static-field", + "private-static-method", "public-instance-field", "protected-instance-field", "private-instance-field", @@ -68,9 +71,6 @@ const PRESET_ORDERS: { [preset: string]: string[] } = { "public-instance-method", "protected-instance-method", "private-instance-method", - "public-static-method", - "protected-static-method", - "private-static-method", ], }; /* end new options */ @@ -144,7 +144,7 @@ interface IModifiers { function getModifiers(isMethod: boolean, modifiers?: ts.ModifiersArray): IModifiers { return { isInstance: !Lint.hasModifier(modifiers, ts.SyntaxKind.StaticKeyword), - isMethod: isMethod, + isMethod, isPrivate: Lint.hasModifier(modifiers, ts.SyntaxKind.PrivateKeyword), }; } diff --git a/src/rules/noConsecutiveBlankLinesRule.ts b/src/rules/noConsecutiveBlankLinesRule.ts index 69e737fd0f9..b3208e117ab 100644 --- a/src/rules/noConsecutiveBlankLinesRule.ts +++ b/src/rules/noConsecutiveBlankLinesRule.ts @@ -48,7 +48,7 @@ class NoConsecutiveBlankLinesWalker extends Lint.SkippableTokenAwareRuleWalker { // find all the lines that are blank or only contain whitespace let blankLineIndexes: number[] = []; - soureFileLines.forEach(function(txt, i){ + soureFileLines.forEach((txt, i) => { if (txt.trim() === "") { blankLineIndexes.push(i); } diff --git a/src/rules/noInvalidThisRule.ts b/src/rules/noInvalidThisRule.ts index 21511135a96..c98a22d7d45 100644 --- a/src/rules/noInvalidThisRule.ts +++ b/src/rules/noInvalidThisRule.ts @@ -65,7 +65,7 @@ class NoInvalidThisWalker extends Lint.ScopeAwareRuleWalker { let inFunction = node.kind === ts.SyntaxKind.FunctionDeclaration || node.kind === ts.SyntaxKind.FunctionExpression; return { inClass: isClassScope, - inFunction: inFunction, + inFunction, }; } diff --git a/src/rules/noVarKeywordRule.ts b/src/rules/noVarKeywordRule.ts index 79b9aa1ba0b..6b81c122ae8 100644 --- a/src/rules/noVarKeywordRule.ts +++ b/src/rules/noVarKeywordRule.ts @@ -42,9 +42,9 @@ export class Rule extends Lint.Rules.AbstractRule { class NoVarKeywordWalker extends Lint.RuleWalker { public visitVariableStatement(node: ts.VariableStatement) { - if (!Lint.hasModifier(node.modifiers, ts.SyntaxKind.ExportKeyword, ts.SyntaxKind.DeclareKeyword) + if (!Lint.hasModifier(node.modifiers, ts.SyntaxKind.DeclareKeyword) && !Lint.isBlockScopedVariable(node)) { - this.addFailure(this.createFailure(node.getStart(), "var".length, Rule.FAILURE_STRING)); + this.addFailure(this.createFailure(node.declarationList.getStart(), "var".length, Rule.FAILURE_STRING)); } super.visitVariableStatement(node); diff --git a/src/rules/objectLiteralShorthandRule.ts b/src/rules/objectLiteralShorthandRule.ts index 270d8065e8b..d1eca695246 100644 --- a/src/rules/objectLiteralShorthandRule.ts +++ b/src/rules/objectLiteralShorthandRule.ts @@ -4,7 +4,7 @@ import * as ts from "typescript"; export class Rule extends Lint.Rules.AbstractRule { /* tslint:disable:object-literal-sort-keys */ public static metadata: Lint.IRuleMetadata = { - ruleName: "object-shorthand", + ruleName: "object-literal-shorthand", description: "Enforces use of ES6 object literal shorthand when possible.", options: null, optionExamples: ["true"], diff --git a/src/rules/objectLiteralSortKeysRule.ts b/src/rules/objectLiteralSortKeysRule.ts index 01406838716..ccebcbe8167 100644 --- a/src/rules/objectLiteralSortKeysRule.ts +++ b/src/rules/objectLiteralSortKeysRule.ts @@ -56,14 +56,15 @@ class ObjectLiteralSortKeysWalker extends Lint.RuleWalker { public visitPropertyAssignment(node: ts.PropertyAssignment) { const sortedState = this.sortedStateStack[this.sortedStateStack.length - 1]; + // skip remainder of object literal scan if a previous key was found // in an unsorted position. This ensures only one error is thrown at // a time and keeps error output clean. if (sortedState) { const lastSortedKey = this.lastSortedKeyStack[this.lastSortedKeyStack.length - 1]; const keyNode = node.name; - if (keyNode.kind === ts.SyntaxKind.Identifier) { - const key = ( keyNode).text; + if (isIdentifierOrStringLiteral(keyNode)) { + const key = keyNode.text; if (key < lastSortedKey) { const failureString = Rule.FAILURE_STRING_FACTORY(key); this.addFailure(this.createFailure(keyNode.getStart(), keyNode.getWidth(), failureString)); @@ -76,3 +77,7 @@ class ObjectLiteralSortKeysWalker extends Lint.RuleWalker { super.visitPropertyAssignment(node); } } + +function isIdentifierOrStringLiteral(node: ts.Node): node is (ts.Identifier | ts.StringLiteral) { + return node.kind === ts.SyntaxKind.Identifier || node.kind === ts.SyntaxKind.StringLiteral; +} diff --git a/src/rules/onlyArrowFunctionsRule.ts b/src/rules/onlyArrowFunctionsRule.ts index 4643570531d..f29b191ece7 100644 --- a/src/rules/onlyArrowFunctionsRule.ts +++ b/src/rules/onlyArrowFunctionsRule.ts @@ -55,14 +55,16 @@ export class Rule extends Lint.Rules.AbstractRule { class OnlyArrowFunctionsWalker extends Lint.RuleWalker { public visitFunctionDeclaration(node: ts.FunctionDeclaration) { - if (!this.hasOption(OPTION_ALLOW_DECLARATIONS)) { + if (!node.asteriskToken && !this.hasOption(OPTION_ALLOW_DECLARATIONS)) { this.addFailure(this.createFailure(node.getStart(), "function".length, Rule.FAILURE_STRING)); } super.visitFunctionDeclaration(node); } public visitFunctionExpression(node: ts.FunctionExpression) { - this.addFailure(this.createFailure(node.getStart(), "function".length, Rule.FAILURE_STRING)); + if (!node.asteriskToken) { + this.addFailure(this.createFailure(node.getStart(), "function".length, Rule.FAILURE_STRING)); + } super.visitFunctionExpression(node); } } diff --git a/src/rules/preferForOfRule.ts b/src/rules/preferForOfRule.ts new file mode 100644 index 00000000000..cd1078cfb31 --- /dev/null +++ b/src/rules/preferForOfRule.ts @@ -0,0 +1,122 @@ +/** + * @license + * Copyright 2016 Palantir Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Lint from "../lint"; +import * as ts from "typescript"; + +export class Rule extends Lint.Rules.AbstractRule { + /* tslint:disable:object-literal-sort-keys */ + public static metadata: Lint.IRuleMetadata = { + ruleName: "prefer-for-of", + description: "Recommends a 'for-of' loop over a standard 'for' loop if the index is only used to access the array being iterated.", + rationale: "A for(... of ...) loop is easier to implement and read when the index is not needed.", + optionsDescription: "Not configurable.", + options: null, + optionExamples: ["true"], + type: "typescript", + }; + /* tslint:enable:object-literal-sort-keys */ + + public static FAILURE_STRING = "Expected a 'for-of' loop instead of a 'for' loop with this simple iteration"; + + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + const languageService = Lint.createLanguageService(sourceFile.fileName, sourceFile.getFullText()); + return this.applyWithWalker(new PreferForOfWalker(sourceFile, this.getOptions(), languageService)); + } +} + +class PreferForOfWalker extends Lint.RuleWalker { + constructor(sourceFile: ts.SourceFile, options: Lint.IOptions, private languageService: ts.LanguageService) { + super(sourceFile, options); + } + + public visitForStatement(node: ts.ForStatement) { + const arrayAccessNode = this.locateArrayNodeInForLoop(node); + + if (arrayAccessNode !== undefined) { + // Skip arrays thats just loop over a hard coded number + // If we are accessing the length of the array, then we are likely looping over it's values + if (arrayAccessNode.kind === ts.SyntaxKind.PropertyAccessExpression && arrayAccessNode.getLastToken().getText() === "length") { + let incrementorVariable = node.incrementor.getFirstToken(); + if (/\+|-/g.test(incrementorVariable.getText())) { + // If it's formatted as `++i` instead, we need to get the OTHER token + incrementorVariable = node.incrementor.getLastToken(); + } + const arrayToken = arrayAccessNode.getChildAt(0); + const loopSyntaxText = node.statement.getText(); + // Find all usages of the incrementor variable + const fileName = this.getSourceFile().fileName; + const highlights = this.languageService.getDocumentHighlights(fileName, incrementorVariable.getStart(), [fileName]); + + if (highlights && highlights.length > 0) { + // There are *usually* three usages when setting up the for loop, + // so remove those from the count to get the count inside the loop block + const incrementorCount = highlights[0].highlightSpans.length - 3; + + // Find `array[i]`-like usages by building up a regex + const arrayTokenForRegex = arrayToken.getText().replace(".", "\\."); + const incrementorForRegex = incrementorVariable.getText().replace(".", "\\."); + const regex = new RegExp(`${arrayTokenForRegex}\\[\\s*${incrementorForRegex}\\s*\\]`, "g"); + const accessMatches = loopSyntaxText.match(regex); + const matchCount = (accessMatches || []).length; + + // If there are more usages of the array item being access than the incrementor variable + // being used, then this loop could be replaced with a for-of loop instead. + // This means that the incrementor variable is not used on its own anywhere and is ONLY + // used to access the array item. + if (matchCount >= incrementorCount) { + const failure = this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING); + this.addFailure(failure); + } + } + } + } + + super.visitForStatement(node); + } + + private locateArrayNodeInForLoop(forLoop: ts.ForStatement): ts.Node { + // Some oddly formatted (yet still valid!) `for` loops might not have children in the condition + // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for + if (forLoop.condition !== undefined) { + let arrayAccessNode = forLoop.condition.getChildAt(2); + // If We haven't found it, maybe it's not a standard for loop, try looking in the initializer for the array + // Something like `for(var t=0, len=arr.length; t < len; t++)` + if (arrayAccessNode.kind !== ts.SyntaxKind.PropertyAccessExpression && forLoop.initializer !== undefined) { + for (let initNode of forLoop.initializer.getChildren()) { + // look in `var t=0, len=arr.length;` + if (initNode.kind === ts.SyntaxKind.SyntaxList) { + for (let initVar of initNode.getChildren()) { + // look in `t=0, len=arr.length;` + if (initVar.kind === ts.SyntaxKind.VariableDeclaration) { + for (let initVarPart of initVar.getChildren()) { + // look in `len=arr.length` + if (initVarPart.kind === ts.SyntaxKind.PropertyAccessExpression) { + arrayAccessNode = initVarPart; + } + } + } + } + } + } + } + return arrayAccessNode; + } else { + return undefined; + } + } +} diff --git a/src/rules/semicolonRule.ts b/src/rules/semicolonRule.ts index e6512c5e2ed..6ae0d810303 100644 --- a/src/rules/semicolonRule.ts +++ b/src/rules/semicolonRule.ts @@ -140,6 +140,18 @@ class SemicolonWalker extends Lint.RuleWalker { super.visitExportAssignment(node); } + public visitFunctionDeclaration(node: ts.FunctionDeclaration) { + if (!node.body) { + this.checkSemicolonAt(node); + } + super.visitFunctionDeclaration(node); + } + + public visitTypeAliasDeclaration(node: ts.TypeAliasDeclaration) { + this.checkSemicolonAt(node); + super.visitTypeAliasDeclaration(node); + } + private checkSemicolonAt(node: ts.Node) { const sourceFile = this.getSourceFile(); const children = node.getChildren(sourceFile); diff --git a/src/rules/trailingCommaRule.ts b/src/rules/trailingCommaRule.ts index 083922c592d..f6ef978a691 100644 --- a/src/rules/trailingCommaRule.ts +++ b/src/rules/trailingCommaRule.ts @@ -23,7 +23,9 @@ export class Rule extends Lint.Rules.AbstractRule { /* tslint:disable:object-literal-sort-keys */ public static metadata: Lint.IRuleMetadata = { ruleName: "trailing-comma", - description: "Requires or disallows trailing commas in array and object literals, destructuring assignments and named imports.", + description: Lint.Utils.dedent` + Requires or disallows trailing commas in array and object literals, destructuring assignments, function and tuple typings, + named imports and function parameters.`, optionsDescription: Lint.Utils.dedent` One argument which is an object with the keys \`multiline\` and \`singleline\`. Both should be set to either \`"always"\` or \`"never"\`. @@ -33,7 +35,8 @@ export class Rule extends Lint.Rules.AbstractRule { A array is considered "multiline" if its closing bracket is on a line after the last array element. The same general logic is followed for - object literals and named import statements.`, + object literals, function and tuple typings, named import statements + and function parameters.`, options: { type: "object", properties: { @@ -62,46 +65,195 @@ export class Rule extends Lint.Rules.AbstractRule { } class TrailingCommaWalker extends Lint.RuleWalker { + private static SYNTAX_LIST_WRAPPER_TOKENS: [ts.SyntaxKind, ts.SyntaxKind][] = [ + [ts.SyntaxKind.OpenBraceToken, ts.SyntaxKind.CloseBraceToken], + [ts.SyntaxKind.OpenBracketToken, ts.SyntaxKind.CloseBracketToken], + [ts.SyntaxKind.OpenParenToken, ts.SyntaxKind.CloseParenToken], + [ts.SyntaxKind.LessThanToken, ts.SyntaxKind.GreaterThanToken], + ]; + public visitArrayLiteralExpression(node: ts.ArrayLiteralExpression) { - this.lintNode(node); + this.lintChildNodeWithIndex(node, 1); super.visitArrayLiteralExpression(node); } + public visitArrowFunction(node: ts.FunctionLikeDeclaration) { + this.lintChildNodeWithIndex(node, 1); + super.visitArrowFunction(node); + } + public visitBindingPattern(node: ts.BindingPattern) { if (node.kind === ts.SyntaxKind.ArrayBindingPattern || node.kind === ts.SyntaxKind.ObjectBindingPattern) { - this.lintNode(node); + this.lintChildNodeWithIndex(node, 1); } super.visitBindingPattern(node); } - public visitNamedImports(node: ts.NamedImports) { + public visitCallExpression(node: ts.CallExpression) { + this.lintNode(node); + super.visitCallExpression(node); + } + + public visitClassDeclaration(node: ts.ClassDeclaration) { + this.lintNode(node); + super.visitClassDeclaration(node); + } + + public visitConstructSignature(node: ts.ConstructSignatureDeclaration) { + this.lintNode(node); + super.visitConstructSignature(node); + } + + public visitConstructorDeclaration(node: ts.ConstructorDeclaration) { + this.lintNode(node); + super.visitConstructorDeclaration(node); + } + + public visitConstructorType(node: ts.FunctionOrConstructorTypeNode) { + this.lintNode(node); + super.visitConstructorType(node); + } + + public visitEnumDeclaration(node: ts.EnumDeclaration) { + this.lintNode(node, true); + super.visitEnumDeclaration(node); + } + + public visitFunctionType(node: ts.FunctionOrConstructorTypeNode) { + this.lintChildNodeWithIndex(node, 1); + super.visitFunctionType(node); + } + + public visitFunctionDeclaration(node: ts.FunctionDeclaration) { + this.lintNode(node); + super.visitFunctionDeclaration(node); + } + + public visitFunctionExpression(node: ts.FunctionExpression) { + this.lintNode(node); + super.visitFunctionExpression(node); + } + + public visitInterfaceDeclaration(node: ts.InterfaceDeclaration) { + this.lintNode(node); + super.visitInterfaceDeclaration(node); + } + + public visitMethodDeclaration(node: ts.MethodDeclaration) { + this.lintNode(node); + super.visitMethodDeclaration(node); + } + + public visitMethodSignature(node: ts.SignatureDeclaration) { this.lintNode(node); + super.visitMethodSignature(node); + } + + public visitNamedImports(node: ts.NamedImports) { + this.lintChildNodeWithIndex(node, 1); super.visitNamedImports(node); } - public visitObjectLiteralExpression(node: ts.ObjectLiteralExpression) { + public visitNewExpression(node: ts.NewExpression) { this.lintNode(node); + super.visitNewExpression(node); + } + + public visitObjectLiteralExpression(node: ts.ObjectLiteralExpression) { + this.lintChildNodeWithIndex(node, 1); super.visitObjectLiteralExpression(node); } - private lintNode(node: ts.Node) { - const child = node.getChildAt(1); - if (child != null && child.kind === ts.SyntaxKind.SyntaxList) { + public visitSetAccessor(node: ts.AccessorDeclaration) { + this.lintNode(node); + super.visitSetAccessor(node); + } + + public visitTupleType(node: ts.TupleTypeNode) { + this.lintChildNodeWithIndex(node, 1); + super.visitTupleType(node); + } + + public visitTypeLiteral(node: ts.TypeLiteralNode) { + this.lintNode(node); + // object type literals need to be inspected separately because they + // have a different syntax list wrapper token, and they can be semicolon delimited + const children = node.getChildren(); + for (let i = 0; i < children.length - 2; i++) { + if (children[i].kind === ts.SyntaxKind.OpenBraceToken && + children[i + 1].kind === ts.SyntaxKind.SyntaxList && + children[i + 2].kind === ts.SyntaxKind.CloseBraceToken) { + const grandChildren = children[i + 1].getChildren(); + // the AST is different from the grammar spec. The semicolons are included as tokens as part of *Signature, + // as opposed to optionals alongside it. So instead of children[i + 1] having + // [ PropertySignature, Semicolon, PropertySignature, Semicolon ], the AST is + // [ PropertySignature, PropertySignature], where the Semicolons are under PropertySignature + const hasSemicolon = grandChildren.some(grandChild => { + return grandChild.getChildren().some(ggc => ggc.kind === ts.SyntaxKind.SemicolonToken); + }); + + if (!hasSemicolon) { + const endLineOfClosingElement = this.getSourceFile().getLineAndCharacterOfPosition(children[i + 2].getEnd()).line; + this.lintChildNodeWithIndex(children[i + 1], grandChildren.length - 1, endLineOfClosingElement); + } + } + } + super.visitTypeLiteral(node); + } + + public visitTypeReference(node: ts.TypeReferenceNode) { + this.lintNode(node); + super.visitTypeReference(node); + } + + private lintNode(node: ts.Node, includeBraces?: boolean) { + const children = node.getChildren(); + const syntaxListWrapperTokens = (includeBraces === true) ? + TrailingCommaWalker.SYNTAX_LIST_WRAPPER_TOKENS : TrailingCommaWalker.SYNTAX_LIST_WRAPPER_TOKENS.slice(1); + + for (let i = 0; i < children.length - 2; i++) { + syntaxListWrapperTokens.forEach(([openToken, closeToken]) => { + if (children[i].kind === openToken && + children[i + 1].kind === ts.SyntaxKind.SyntaxList && + children[i + 2].kind === closeToken) { + this.lintChildNodeWithIndex(node, i + 1); + } + }); + } + } + + private lintChildNodeWithIndex(node: ts.Node, childNodeIndex: number, endLineOfClosingElement?: number) { + const child = node.getChildAt(childNodeIndex); + if (child != null) { const grandChildren = child.getChildren(); if (grandChildren.length > 0) { const lastGrandChild = grandChildren[grandChildren.length - 1]; const hasTrailingComma = lastGrandChild.kind === ts.SyntaxKind.CommaToken; - const endLineOfNode = this.getSourceFile().getLineAndCharacterOfPosition(node.getEnd()).line; const endLineOfLastElement = this.getSourceFile().getLineAndCharacterOfPosition(lastGrandChild.getEnd()).line; - const isMultiline = endLineOfNode !== endLineOfLastElement; + if (endLineOfClosingElement === undefined) { + let closingElementNode = node.getChildAt(childNodeIndex + 1); + if (closingElementNode == null) { + closingElementNode = node; + } + endLineOfClosingElement = this.getSourceFile().getLineAndCharacterOfPosition(closingElementNode.getEnd()).line; + } + const isMultiline = endLineOfClosingElement !== endLineOfLastElement; const option = this.getOption(isMultiline ? "multiline" : "singleline"); if (hasTrailingComma && option === "never") { - this.addFailure(this.createFailure(lastGrandChild.getStart(), 1, Rule.FAILURE_STRING_NEVER)); + const failureStart = lastGrandChild.getStart(); + const fix = new Lint.Fix(Rule.metadata.ruleName, [ + this.deleteText(failureStart, 1), + ]); + this.addFailure(this.createFailure(failureStart, 1, Rule.FAILURE_STRING_NEVER, fix)); } else if (!hasTrailingComma && option === "always") { - this.addFailure(this.createFailure(lastGrandChild.getEnd() - 1, 1, Rule.FAILURE_STRING_ALWAYS)); + const failureStart = lastGrandChild.getEnd(); + const fix = new Lint.Fix(Rule.metadata.ruleName, [ + this.appendText(failureStart, ","), + ]); + this.addFailure(this.createFailure(failureStart - 1, 1, Rule.FAILURE_STRING_ALWAYS, fix)); } } } diff --git a/src/rules/typedefWhitespaceRule.ts b/src/rules/typedefWhitespaceRule.ts index 182388fc6c1..53e2aae6651 100644 --- a/src/rules/typedefWhitespaceRule.ts +++ b/src/rules/typedefWhitespaceRule.ts @@ -30,7 +30,7 @@ const SPACE_OBJECT = { properties: { "call-signature": SPACE_OPTIONS, "index-signature": SPACE_OPTIONS, - "parameter": SPACE_OPTIONS, + parameter: SPACE_OPTIONS, "property-declaration": SPACE_OPTIONS, "variable-declaration": SPACE_OPTIONS, }, @@ -249,12 +249,19 @@ class TypedefWhitespaceWalker extends Lint.RuleWalker { if (this.hasRightOption(option)) { let positionToCheck = colonPosition + 1 - node.getStart(); + // Don't enforce trailing spaces on newlines + // (https://github.com/palantir/tslint/issues/1354) + scanner.setTextPos(positionToCheck); + let kind = scanner.scan(); + if (kind === ts.SyntaxKind.NewLineTrivia) { + return; + } + let hasTrailingWhitespace: boolean; if (positionToCheck >= node.getWidth()) { hasTrailingWhitespace = false; } else { - scanner.setTextPos(positionToCheck); - hasTrailingWhitespace = scanner.scan() === ts.SyntaxKind.WhitespaceTrivia; + hasTrailingWhitespace = kind === ts.SyntaxKind.WhitespaceTrivia; } positionToCheck = colonPosition + 2 - node.getStart(); diff --git a/src/rules/variableNameRule.ts b/src/rules/variableNameRule.ts index bd6bf1e9d3d..76699ec737f 100644 --- a/src/rules/variableNameRule.ts +++ b/src/rules/variableNameRule.ts @@ -85,8 +85,12 @@ class VariableNameWalker extends Lint.RuleWalker { public visitBindingElement(node: ts.BindingElement) { if (node.name.kind === ts.SyntaxKind.Identifier) { const identifier = node.name; - this.handleVariableNameFormat(identifier); this.handleVariableNameKeyword(identifier); + // A destructuring pattern that does not rebind an expression is always an alias, e.g. `var {Foo} = ...;`. + // Only check if the name is rebound (`var {Foo: bar} = ...;`). + if (node.parent.kind !== ts.SyntaxKind.ObjectBindingPattern || node.propertyName) { + this.handleVariableNameFormat(identifier, node.initializer); + } } super.visitBindingElement(node); } @@ -94,7 +98,7 @@ class VariableNameWalker extends Lint.RuleWalker { public visitParameterDeclaration(node: ts.ParameterDeclaration) { if (node.name.kind === ts.SyntaxKind.Identifier) { const identifier = node.name; - this.handleVariableNameFormat(identifier); + this.handleVariableNameFormat(identifier, undefined /* parameters may not alias */); this.handleVariableNameKeyword(identifier); } super.visitParameterDeclaration(node); @@ -103,7 +107,7 @@ class VariableNameWalker extends Lint.RuleWalker { public visitPropertyDeclaration(node: ts.PropertyDeclaration) { if (node.name != null && node.name.kind === ts.SyntaxKind.Identifier) { const identifier = node.name; - this.handleVariableNameFormat(identifier); + this.handleVariableNameFormat(identifier, node.initializer); // do not check property declarations for keywords, they are allowed to be keywords } super.visitPropertyDeclaration(node); @@ -112,7 +116,7 @@ class VariableNameWalker extends Lint.RuleWalker { public visitVariableDeclaration(node: ts.VariableDeclaration) { if (node.name.kind === ts.SyntaxKind.Identifier) { const identifier = node.name; - this.handleVariableNameFormat(identifier); + this.handleVariableNameFormat(identifier, node.initializer); this.handleVariableNameKeyword(identifier); } super.visitVariableDeclaration(node); @@ -125,9 +129,21 @@ class VariableNameWalker extends Lint.RuleWalker { } } - private handleVariableNameFormat(name: ts.Identifier) { + private isAlias(name: ts.Identifier, initializer: ts.Expression): boolean { + if (initializer.kind === ts.SyntaxKind.PropertyAccessExpression) { + return (initializer as ts.PropertyAccessExpression).name.text === name.text; + } else if (initializer.kind === ts.SyntaxKind.Identifier) { + return (initializer as ts.Identifier).text === name.text; + } + } + + private handleVariableNameFormat(name: ts.Identifier, initializer: ts.Expression) { const variableName = name.text; + if (initializer && this.isAlias(name, initializer)) { + return; + } + if (this.shouldCheckFormat && !this.isCamelCase(variableName) && !isUpperCase(variableName)) { this.addFailure(this.createFailure(name.getStart(), name.getWidth(), Rule.FORMAT_FAILURE)); } diff --git a/src/test.ts b/src/test.ts index e28ab191232..ab61e21bf2b 100644 --- a/src/test.ts +++ b/src/test.ts @@ -41,7 +41,7 @@ export interface TestResult { fixesFromMarkup: string; markupFromLinter: string; markupFromMarkup: string; - } + }, }; } @@ -65,9 +65,9 @@ export function runTest(testDirectory: string, rulesDirectory?: string | string[ getCanonicalFileName: (filename: string) => filename, getCurrentDirectory: () => "", getDefaultLibFileName: () => ts.getDefaultLibFileName(compilerOptions), - getDirectories: () => [], + getDirectories: (path: string) => [], getNewLine: () => "\n", - getSourceFile: function (filenameToGet: string) { + getSourceFile(filenameToGet: string) { if (filenameToGet === this.getDefaultLibFileName()) { const fileText = fs.readFileSync(ts.getDefaultLibFilePath(compilerOptions)).toString(); return ts.createSourceFile(filenameToGet, fileText, compilerOptions.target); diff --git a/src/tsconfig.json b/src/tsconfig.json index dbd029c231b..1af071b6934 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -43,10 +43,12 @@ "test.ts", "tslint-cli.ts", "tslint.ts", + "tslintMulti.ts", "utils.ts", "configs/latest.ts", "configs/recommended.ts", "formatters/checkstyleFormatter.ts", + "formatters/fileslistFormatter.ts", "formatters/index.ts", "formatters/jsonFormatter.ts", "formatters/msbuildFormatter.ts", @@ -71,11 +73,13 @@ "language/walker/syntaxWalker.ts", "rules/adjacentOverloadSignaturesRule.ts", "rules/alignRule.ts", + "rules/arrayTypeRule.ts", "rules/arrowParensRule.ts", "rules/banRule.ts", "rules/classNameRule.ts", "rules/commentFormatRule.ts", "rules/curlyRule.ts", + "rules/cyclomaticComplexityRule.ts", "rules/eoflineRule.ts", "rules/fileHeaderRule.ts", "rules/forinRule.ts", @@ -97,8 +101,8 @@ "rules/noConditionalAssignmentRule.ts", "rules/noConsecutiveBlankLinesRule.ts", "rules/noConsoleRule.ts", - "rules/noConstructorVarsRule.ts", "rules/noConstructRule.ts", + "rules/noConstructorVarsRule.ts", "rules/noDebuggerRule.ts", "rules/noDefaultExportRule.ts", "rules/noDuplicateKeyRule.ts", @@ -133,6 +137,7 @@ "rules/oneVariablePerDeclarationRule.ts", "rules/onlyArrowFunctionsRule.ts", "rules/orderedImportsRule.ts", + "rules/preferForOfRule.ts", "rules/quotemarkRule.ts", "rules/radixRule.ts", "rules/restrictPlusOperandsRule.ts", diff --git a/src/tslint-cli.ts b/src/tslint-cli.ts index 8fd25ab4698..6819b3798a6 100644 --- a/src/tslint-cli.ts +++ b/src/tslint-cli.ts @@ -27,7 +27,7 @@ import { findConfiguration, } from "./configuration"; import {consoleTestResultHandler, runTest} from "./test"; -import * as Linter from "./tslint"; +import * as Linter from "./tslintMulti"; let processed = optimist .usage("Usage: $0 [options] file ...") @@ -42,53 +42,53 @@ let processed = optimist } }) .options({ - "c": { + c: { alias: "config", describe: "configuration file", }, - "force": { + e: { + alias: "exclude", + describe: "exclude globs from path expansion", + }, + force: { describe: "return status code 0 even if there are lint errors", - "type": "boolean", + type: "boolean", }, - "h": { + h: { alias: "help", describe: "display detailed help", }, - "i": { + i: { alias: "init", describe: "generate a tslint.json config file in the current working directory", }, - "o": { + o: { alias: "out", describe: "output file", }, - "r": { + project: { + describe: "tsconfig.json file", + }, + r: { alias: "rules-dir", describe: "rules directory", }, - "s": { + s: { alias: "formatters-dir", describe: "formatters directory", }, - "e": { - alias: "exclude", - describe: "exclude globs from path expansion", - }, - "t": { + t: { alias: "format", default: "prose", - describe: "output format (prose, json, stylish, verbose, pmd, msbuild, checkstyle, vso)", + describe: "output format (prose, json, stylish, verbose, pmd, msbuild, checkstyle, vso, fileslist)", }, - "test": { + test: { describe: "test that tslint produces the correct output for the specified directory", }, - "project": { - describe: "tsconfig.json file", - }, "type-check": { describe: "enable type checking when linting a project", }, - "v": { + v: { alias: "version", describe: "current version", }, @@ -217,45 +217,48 @@ if (argv.c && !fs.existsSync(argv.c)) { } const possibleConfigAbsolutePath = argv.c != null ? path.resolve(argv.c) : null; -const processFile = (file: string, program?: ts.Program) => { - if (!fs.existsSync(file)) { - console.error(`Unable to open file: ${file}`); - process.exit(1); - } +const processFiles = (files: string[], program?: ts.Program) => { - const buffer = new Buffer(256); - buffer.fill(0); - const fd = fs.openSync(file, "r"); - try { - fs.readSync(fd, buffer, 0, 256, null); - if (buffer.readInt8(0) === 0x47 && buffer.readInt8(188) === 0x47) { - // MPEG transport streams use the '.ts' file extension. They use 0x47 as the frame - // separator, repeating every 188 bytes. It is unlikely to find that pattern in - // TypeScript source, so tslint ignores files with the specific pattern. - console.warn(`${file}: ignoring MPEG transport stream`); - return; + const linter = new Linter({ + formatter: argv.t, + formattersDirectory: argv.s || "", + rulesDirectory: argv.r || "", + }, program); + + for (const file of files) { + if (!fs.existsSync(file)) { + console.error(`Unable to open file: ${file}`); + process.exit(1); } - } finally { - fs.closeSync(fd); - } - const contents = fs.readFileSync(file, "utf8"); - const configuration = findConfiguration(possibleConfigAbsolutePath, file); + const buffer = new Buffer(256); + buffer.fill(0); + const fd = fs.openSync(file, "r"); + try { + fs.readSync(fd, buffer, 0, 256, null); + if (buffer.readInt8(0) === 0x47 && buffer.readInt8(188) === 0x47) { + // MPEG transport streams use the '.ts' file extension. They use 0x47 as the frame + // separator, repeating every 188 bytes. It is unlikely to find that pattern in + // TypeScript source, so tslint ignores files with the specific pattern. + console.warn(`${file}: ignoring MPEG transport stream`); + return; + } + } finally { + fs.closeSync(fd); + } - const linter = new Linter(file, contents, { - configuration, - formatter: argv.t, - formattersDirectory: argv.s, - rulesDirectory: argv.r, - }, program); + const contents = fs.readFileSync(file, "utf8"); + const configuration = findConfiguration(possibleConfigAbsolutePath, file); + linter.lint(file, contents, configuration); + } - const lintResult = linter.lint(); + const lintResult = linter.getResult(); - if (lintResult.failureCount > 0) { - outputStream.write(lintResult.output, () => { + outputStream.write(lintResult.output, () => { + if (lintResult.failureCount > 0) { process.exit(argv.force ? 0 : 2); - }); - } + } + }); }; // if both files and tsconfig are present, use files @@ -293,6 +296,7 @@ if (argv.project != null) { } } -for (const file of files) { - glob.sync(file, { ignore: argv.e }).forEach((file) => processFile(file, program)); -} +files = files + .map((file: string) => glob.sync(file, { ignore: argv.e })) + .reduce((a: string[], b: string[]) => a.concat(b)); +processFiles(files, program); diff --git a/src/tslint.ts b/src/tslint.ts index f67693d3de9..2c91e51a192 100644 --- a/src/tslint.ts +++ b/src/tslint.ts @@ -21,22 +21,18 @@ import { DEFAULT_CONFIG, findConfiguration, findConfigurationPath, - getRelativePath, getRulesDirectories, loadConfigurationFromPath, } from "./configuration"; -import { EnableDisableRulesWalker } from "./enableDisableRules"; -import { findFormatter } from "./formatterLoader"; -import { IFormatter } from "./language/formatter/formatter"; -import { RuleFailure } from "./language/rule/rule"; -import { TypedRule } from "./language/rule/typedRule"; -import { getSourceFile } from "./language/utils"; import { ILinterOptions, ILinterOptionsRaw, LintResult } from "./lint"; -import { loadRules } from "./ruleLoader"; +import * as MultiLinter from "./tslintMulti"; import { arrayify } from "./utils"; +/** + * Linter that can lint exactly one file. + */ class Linter { - public static VERSION = "3.15.0-dev.0"; + public static VERSION = MultiLinter.VERSION; public static findConfiguration = findConfiguration; public static findConfigurationPath = findConfigurationPath; @@ -49,25 +45,7 @@ class Linter { * Creates a TypeScript program object from a tsconfig.json file path and optional project directory. */ public static createProgram(configFile: string, projectDirectory?: string): ts.Program { - if (projectDirectory === undefined) { - const lastSeparator = configFile.lastIndexOf("/"); - if (lastSeparator < 0) { - projectDirectory = "."; - } else { - projectDirectory = configFile.substring(0, lastSeparator + 1); - } - } - - const {config} = ts.readConfigFile(configFile, ts.sys.readFile); - const parsed = ts.parseJsonConfigFileContent(config, { - fileExists: (path: string) => true, - readDirectory: ts.sys.readDirectory, - useCaseSensitiveFileNames: false, - }, projectDirectory); - const host = ts.createCompilerHost(parsed.options, true); - const program = ts.createProgram(parsed.fileNames, parsed.options, host); - - return program; + return MultiLinter.createProgram(configFile, projectDirectory); } /** @@ -75,80 +53,20 @@ class Linter { * files and excludes declaration (".d.ts") files. */ public static getFileNames(program: ts.Program): string[] { - return program.getSourceFiles().map(s => s.fileName).filter(l => l.substr(-5) !== ".d.ts"); + return MultiLinter.getFileNames(program); } constructor(private fileName: string, private source: string, options: ILinterOptionsRaw, private program?: ts.Program) { - this.options = this.computeFullOptions(options); + this.options = this.computeFullOptions(options); } public lint(): LintResult { - const failures: RuleFailure[] = []; - let sourceFile: ts.SourceFile; - if (this.program) { - sourceFile = this.program.getSourceFile(this.fileName); - // check if the program has been type checked - if (!("resolvedModules" in sourceFile)) { - throw new Error("Program must be type checked before linting"); - } - } else { - sourceFile = getSourceFile(this.fileName, this.source); - } - - if (sourceFile === undefined) { - throw new Error(`Invalid source file: ${this.fileName}. Ensure that the files supplied to lint have a .ts or .tsx extension.`); - } - - // walk the code first to find all the intervals where rules are disabled - const rulesWalker = new EnableDisableRulesWalker(sourceFile, { - disabledIntervals: [], - ruleName: "", - }); - rulesWalker.walk(sourceFile); - const enableDisableRuleMap = rulesWalker.enableDisableRuleMap; - - const rulesDirectories = this.options.rulesDirectory; - const configuration = this.options.configuration.rules; - const configuredRules = loadRules(configuration, enableDisableRuleMap, rulesDirectories); - const enabledRules = configuredRules.filter((r) => r.isEnabled()); - for (let rule of enabledRules) { - let ruleFailures: RuleFailure[] = []; - if (this.program && rule instanceof TypedRule) { - ruleFailures = rule.applyWithProgram(sourceFile, this.program); - } else { - ruleFailures = rule.apply(sourceFile); - } - for (let ruleFailure of ruleFailures) { - if (!this.containsRule(failures, ruleFailure)) { - failures.push(ruleFailure); - } - } - } - - let formatter: IFormatter; - const formattersDirectory = getRelativePath(this.options.formattersDirectory); - - const Formatter = findFormatter(this.options.formatter, formattersDirectory); - if (Formatter) { - formatter = new Formatter(); - } else { - throw new Error(`formatter '${this.options.formatter}' not found`); - } - - const output = formatter.format(failures); - return { - failureCount: failures.length, - failures: failures, - format: this.options.formatter, - output: output, - }; - } - - private containsRule(rules: RuleFailure[], rule: RuleFailure) { - return rules.some((r) => r.equals(rule)); + const multiLinter: MultiLinter = new MultiLinter(this.options, this.program); + multiLinter.lint(this.fileName, this.source, this.options.configuration); + return multiLinter.getResult(); } private computeFullOptions(options: ILinterOptionsRaw = {}): ILinterOptions { @@ -161,10 +79,10 @@ class Linter { return { configuration: configuration || DEFAULT_CONFIG, formatter: formatter || "prose", - formattersDirectory: formattersDirectory, + formattersDirectory, rulesDirectory: arrayify(rulesDirectory).concat(arrayify(configuration.rulesDirectory)), }; - } + } } // tslint:disable-next-line:no-namespace diff --git a/src/tslintMulti.ts b/src/tslintMulti.ts new file mode 100644 index 00000000000..52bb4882b9e --- /dev/null +++ b/src/tslintMulti.ts @@ -0,0 +1,164 @@ +/** + * @license + * Copyright 2013 Palantir Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { existsSync } from "fs"; +import * as ts from "typescript"; + +import { + DEFAULT_CONFIG, + findConfiguration, + findConfigurationPath, + getRelativePath, + getRulesDirectories, + loadConfigurationFromPath, +} from "./configuration"; +import { EnableDisableRulesWalker } from "./enableDisableRules"; +import { findFormatter } from "./formatterLoader"; +import { IFormatter } from "./language/formatter/formatter"; +import { RuleFailure } from "./language/rule/rule"; +import { TypedRule } from "./language/rule/typedRule"; +import { getSourceFile } from "./language/utils"; +import { IMultiLinterOptions, LintResult } from "./lint"; +import { loadRules } from "./ruleLoader"; +import { arrayify } from "./utils"; + +/** + * Linter that can lint multiple files in consecutive runs. + */ +class MultiLinter { + public static VERSION = "4.0.0-dev.0"; + + public static findConfiguration = findConfiguration; + public static findConfigurationPath = findConfigurationPath; + public static getRulesDirectories = getRulesDirectories; + public static loadConfigurationFromPath = loadConfigurationFromPath; + + private failures: RuleFailure[] = []; + + /** + * Creates a TypeScript program object from a tsconfig.json file path and optional project directory. + */ + public static createProgram(configFile: string, projectDirectory?: string): ts.Program { + if (projectDirectory === undefined) { + const lastSeparator = configFile.lastIndexOf("/"); + if (lastSeparator < 0) { + projectDirectory = "."; + } else { + projectDirectory = configFile.substring(0, lastSeparator + 1); + } + } + + const { config } = ts.readConfigFile(configFile, ts.sys.readFile); + const parseConfigHost = { + fileExists: existsSync, + readDirectory: ts.sys.readDirectory, + useCaseSensitiveFileNames: true, + }; + const parsed = ts.parseJsonConfigFileContent(config, parseConfigHost, projectDirectory); + const host = ts.createCompilerHost(parsed.options, true); + const program = ts.createProgram(parsed.fileNames, parsed.options, host); + + return program; + } + + /** + * Returns a list of source file names from a TypeScript program. This includes all referenced + * files and excludes declaration (".d.ts") files. + */ + public static getFileNames(program: ts.Program): string[] { + return program.getSourceFiles().map(s => s.fileName).filter(l => l.substr(-5) !== ".d.ts"); + } + + constructor(private options: IMultiLinterOptions, private program?: ts.Program) { + // Empty + } + + public lint(fileName: string, source?: string, configuration: any = DEFAULT_CONFIG): void { + let sourceFile: ts.SourceFile; + if (this.program) { + sourceFile = this.program.getSourceFile(fileName); + // check if the program has been type checked + if (sourceFile && !("resolvedModules" in sourceFile)) { + throw new Error("Program must be type checked before linting"); + } + } else { + sourceFile = getSourceFile(fileName, source); + } + + if (sourceFile === undefined) { + throw new Error(`Invalid source file: ${fileName}. Ensure that the files supplied to lint have a .ts or .tsx extension.`); + } + + // walk the code first to find all the intervals where rules are disabled + const rulesWalker = new EnableDisableRulesWalker(sourceFile, { + disabledIntervals: [], + ruleName: "", + }); + rulesWalker.walk(sourceFile); + const enableDisableRuleMap = rulesWalker.enableDisableRuleMap; + + const rulesDirectories = arrayify(this.options.rulesDirectory) + .concat(arrayify(configuration.rulesDirectory)); + const configurationRules = configuration.rules; + const configuredRules = loadRules(configurationRules, enableDisableRuleMap, rulesDirectories); + const enabledRules = configuredRules.filter((r) => r.isEnabled()); + for (let rule of enabledRules) { + let ruleFailures: RuleFailure[] = []; + if (this.program && rule instanceof TypedRule) { + ruleFailures = rule.applyWithProgram(sourceFile, this.program); + } else { + ruleFailures = rule.apply(sourceFile); + } + for (let ruleFailure of ruleFailures) { + if (!this.containsRule(this.failures, ruleFailure)) { + this.failures.push(ruleFailure); + } + } + } + } + + public getResult(): LintResult { + let formatter: IFormatter; + const formattersDirectory = getRelativePath(this.options.formattersDirectory); + + const formatterName = this.options.formatter || "prose"; + const Formatter = findFormatter(formatterName, formattersDirectory); + if (Formatter) { + formatter = new Formatter(); + } else { + throw new Error(`formatter '${formatterName}' not found`); + } + + const output = formatter.format(this.failures); + + return { + failureCount: this.failures.length, + failures: this.failures, + format: formatterName, + output, + }; + } + + private containsRule(rules: RuleFailure[], rule: RuleFailure) { + return rules.some((r) => r.equals(rule)); + } +} + +// tslint:disable-next-line:no-namespace +namespace MultiLinter {} + +export = MultiLinter; diff --git a/test/configurationTests.ts b/test/configurationTests.ts index a6e25cc04f3..9e99caf5433 100644 --- a/test/configurationTests.ts +++ b/test/configurationTests.ts @@ -36,21 +36,21 @@ describe("Configuration", () => { }); assert.deepEqual(extendConfigurationFile({ rules: { - "a": 1, - "b": 2, + a: 1, + b: 2, }, rulesDirectory: ["foo", "bar"], }, { rules: { - "b": 1, - "c": 3, + b: 1, + c: 3, }, rulesDirectory: "baz", }), { rules: { - "a": 1, - "b": 2, - "c": 3, + a: 1, + b: 2, + c: 3, }, rulesDirectory: ["foo", "bar", "baz"], }); @@ -69,11 +69,13 @@ describe("Configuration", () => { let config = loadConfigurationFromPath("./test/config/tslint-extends-package.json"); assert.isArray(config.rulesDirectory); + /* tslint:disable:object-literal-sort-keys */ assert.deepEqual(config.rules, { "rule-one": true, "rule-two": true, "rule-three": false, }); + /* tslint:enable:object-literal-sort-keys */ }); it("extends with package without customization", () => { @@ -130,12 +132,14 @@ describe("Configuration", () => { assert.lengthOf(config.rulesDirectory, 2); assert.isTrue(fs.existsSync(config.rulesDirectory[0])); assert.isTrue(fs.existsSync(config.rulesDirectory[1])); + /* tslint:disable:object-literal-sort-keys */ assert.deepEqual(config.rules, { "always-fail": false, "rule-one": true, "rule-two": true, "rule-four": true, }); + /* tslint:enable:object-literal-sort-keys */ }); it("extends with array", () => { @@ -153,11 +157,13 @@ describe("Configuration", () => { it("can load .json files with comments", () => { const config = loadConfigurationFromPath("./test/config/tslint-with-comments.json"); + /* tslint:disable:object-literal-sort-keys */ assert.deepEqual(config.rules, { "rule-two": true, "rule-three": "//not a comment", "rule-four": "/*also not a comment*/", }); + /* tslint:enable:object-literal-sort-keys */ }); it("can load .json files with BOM", () => { diff --git a/test/files/formatters/fileslistFormatter.test.ts b/test/files/formatters/fileslistFormatter.test.ts new file mode 100644 index 00000000000..9b623dbfcb0 --- /dev/null +++ b/test/files/formatters/fileslistFormatter.test.ts @@ -0,0 +1,9 @@ +module FilesListModule { + export class FilesListClass { + name: string; + + constructor(name: string) { + this.name = name; + } + } +} diff --git a/test/formatters/externalFormatterTest.ts b/test/formatters/externalFormatterTests.ts similarity index 100% rename from test/formatters/externalFormatterTest.ts rename to test/formatters/externalFormatterTests.ts diff --git a/test/formatters/fileslistFormatterTests.ts b/test/formatters/fileslistFormatterTests.ts new file mode 100644 index 00000000000..e77e444afe6 --- /dev/null +++ b/test/formatters/fileslistFormatterTests.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2013 Palantir Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as ts from "typescript"; + +import {IFormatter, RuleFailure, TestUtils} from "../lint"; + +describe("Files-list Formatter", () => { + const TEST_FILE = "formatters/fileslistFormatter.test.ts"; + let sourceFile: ts.SourceFile; + let formatter: IFormatter; + + before(() => { + const Formatter = TestUtils.getFormatter("fileslist"); + sourceFile = TestUtils.getSourceFile(TEST_FILE); + formatter = new Formatter(); + }); + + it("formats failures", () => { + // this part really doesn't matter, as long as we get some failure` + const failures = [ + new RuleFailure(sourceFile, 0, 1, "first failure", "first-name"), + new RuleFailure(sourceFile, 32, 36, "last failure", "last-name"), + ]; + + // we only print file-names in this formatter + const expectedResult = TEST_FILE + "\n"; + + const actualResult = formatter.format(failures); + assert.equal(actualResult, expectedResult); + }); + + it("handles no failures", () => { + const result = formatter.format([]); + assert.equal(result, ""); + }); +}); diff --git a/test/formatters/proseFormatterTests.ts b/test/formatters/proseFormatterTests.ts index ecde9fc00d2..ccf8bc18907 100644 --- a/test/formatters/proseFormatterTests.ts +++ b/test/formatters/proseFormatterTests.ts @@ -49,7 +49,7 @@ describe("Prose Formatter", () => { it("handles no failures", () => { const result = formatter.format([]); - assert.equal(result, "\n"); + assert.equal(result, ""); }); function getPositionString(line: number, character: number) { diff --git a/test/ruleLoaderTests.ts b/test/ruleLoaderTests.ts index c477b879f1e..064cc642f93 100644 --- a/test/ruleLoaderTests.ts +++ b/test/ruleLoaderTests.ts @@ -21,11 +21,11 @@ describe("Rule Loader", () => { it("loads core rules", () => { const validConfiguration: {[name: string]: any} = { - "forin": false, - "quotemark": "single", - "eofline": true, "class-name": true, + eofline: true, + forin: false, "no-debugger": true, + quotemark: "single", }; const rules = loadRules(validConfiguration, {}, RULES_DIRECTORY); @@ -34,8 +34,8 @@ describe("Rule Loader", () => { it("throws if an invalid rule is found", () => { const invalidConfiguration: {[name: string]: any} = { - "invalidConfig1": true, - "invalidConfig2": false, + invalidConfig1: true, + invalidConfig2: false, }; assert.throws( @@ -45,12 +45,14 @@ describe("Rule Loader", () => { }); it("doesn't ignore leading or trailing underscores or dashes", () => { + /* tslint:disable:object-literal-sort-keys */ const invalidConfiguration: {[name: string]: any} = { - "_indent": 6, - "forin_": true, + _indent: 6, + forin_: true, "-quotemark": "single", "eofline-": true, }; + /* tslint:enable:object-literal-sort-keys */ assert.throws( () => loadRules(invalidConfiguration, {}, RULES_DIRECTORY), @@ -60,11 +62,11 @@ describe("Rule Loader", () => { it("works with rulesDirectory argument as an Array", () => { const validConfiguration: {[name: string]: any} = { - "forin": false, - "quotemark": "single", - "eofline": true, "class-name": true, + eofline: true, + forin: false, "no-debugger": true, + quotemark: "single", }; const rules = loadRules(validConfiguration, {}, [RULES_DIRECTORY]); diff --git a/test/rules/array-type/array-simple/test.ts.fix b/test/rules/array-type/array-simple/test.ts.fix new file mode 100644 index 00000000000..615798a9a68 --- /dev/null +++ b/test/rules/array-type/array-simple/test.ts.fix @@ -0,0 +1,53 @@ +let x: number[] = [1] as number[]; +let y: string[] = ["2"]; +let z: any[] = [3, "4"]; + +let xx: number[][] = [[1, 2], [3]]; +let yy: number[][] = [[4, 5], [6]]; + +let ya = [[1, "2"]] as Array<[number, string]>; + +type Arr = T[]; + +// Ignore user defined aliases +let yyyy: Arr>>> = [[[["2"]]]]; + +interface ArrayClass { + foo: T[]; + bar: T[]; + baz: Arr; +} + +function fooFunction(foo: Array>) { + return foo.map(e => e.foo); +} + +function barFunction(bar: Array>) { + return bar.map(e => e.bar); +} + +function bazFunction(baz: Arr>) { + return baz.map(e => e.baz); +} + +let fooVar: Array<(c: number) => number>; +let barVar: Array<(c: number) => number>; + +type fooUnion = Array; +type barUnion = Array; + +type fooIntersection = Array; +type barIntersection = Array; + +namespace fooName { + type BarType = { bar: string }; + type BazType = Arr; +} + +let v: fooName.BarType[] = [{ bar: "bar" }]; + +let w: Array> = [["baz"]]; + +interface FooInterface { + '.bar': {baz: string[];}; +} diff --git a/test/rules/array-type/array-simple/test.ts.lint b/test/rules/array-type/array-simple/test.ts.lint new file mode 100644 index 00000000000..4ed8efe181b --- /dev/null +++ b/test/rules/array-type/array-simple/test.ts.lint @@ -0,0 +1,68 @@ +let x: Array = [1] as number[]; + ~~~~~~~~~~~~~ [Array type using 'Array' is forbidden for simple types. Use 'T[]' instead.] +let y: string[] = >["2"]; + ~~~~~~~~~~~~~ [Array type using 'Array' is forbidden for simple types. Use 'T[]' instead.] +let z: Array = [3, "4"]; + ~~~~~ [Array type using 'Array' is forbidden for simple types. Use 'T[]' instead.] + +let xx: Array> = [[1, 2], [3]]; + ~~~~~~~~~~~~~~~~~~~~ [Array type using 'Array' is forbidden for simple types. Use 'T[]' instead.] + ~~~~~~~~~~~~~ [Array type using 'Array' is forbidden for simple types. Use 'T[]' instead.] +let yy: number[][] = [[4, 5], [6]]; + +let ya = [[1, "2"]] as[number, string][]; + ~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden for non-simple types. Use 'Array' instead.] + +type Arr = Array; + ~~~~~~~~ [Array type using 'Array' is forbidden for simple types. Use 'T[]' instead.] + +// Ignore user defined aliases +let yyyy: Arr>[]> = [[[["2"]]]]; + ~~~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden for non-simple types. Use 'Array' instead.] + +interface ArrayClass { + foo: Array; + ~~~~~~~~ [Array type using 'Array' is forbidden for simple types. Use 'T[]' instead.] + bar: T[]; + baz: Arr; +} + +function fooFunction(foo: Array>) { + return foo.map(e => e.foo); +} + +function barFunction(bar: ArrayClass[]) { + ~~~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden for non-simple types. Use 'Array' instead.] + return bar.map(e => e.bar); +} + +function bazFunction(baz: Arr>) { + return baz.map(e => e.baz); +} + +let fooVar: Array<(c: number) => number>; +let barVar: ((c: number) => number)[]; + ~~~~~~~~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden for non-simple types. Use 'Array' instead.] + +type fooUnion = Array; +type barUnion = (string|number|boolean)[]; + ~~~~~~~~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden for non-simple types. Use 'Array' instead.] + +type fooIntersection = Array; +type barIntersection = (string & number)[]; + ~~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden for non-simple types. Use 'Array' instead.] + +namespace fooName { + type BarType = { bar: string }; + type BazType = Arr; +} + +let v: Array = [{ bar: "bar" }]; + ~~~~~~~~~~~~~~~~~~~~~~ [Array type using 'Array' is forbidden for simple types. Use 'T[]' instead.] + +let w: fooName.BazType[] = [["baz"]]; + ~~~~~~~~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden for non-simple types. Use 'Array' instead.] + +interface FooInterface { + '.bar': {baz: string[];}; +} diff --git a/test/rules/array-type/array-simple/tslint.json b/test/rules/array-type/array-simple/tslint.json new file mode 100644 index 00000000000..a483323c7cb --- /dev/null +++ b/test/rules/array-type/array-simple/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "array-type": [true, "array-simple"] + } +} diff --git a/test/rules/array-type/array/test.ts.fix b/test/rules/array-type/array/test.ts.fix new file mode 100644 index 00000000000..6254a9c6e63 --- /dev/null +++ b/test/rules/array-type/array/test.ts.fix @@ -0,0 +1,44 @@ +let x: number[] = [1] as number[]; +let y: string[] = ["2"]; +let z: any[] = [3, "4"]; + +let xx: number[][] = [[1, 2], [3]]; +let yy: number[][] = [[4, 5], [6]]; + +let ya = [[1, "2"]] as[number, string][]; + +type Arr = T[]; + +// Ignore user defined aliases +let yyyy: Arr[][]> = [[[["2"]]]]; + +interface ArrayClass { + foo: T[]; + bar: T[]; + baz: Arr; +} + +function fooFunction(foo: ArrayClass[]) { + return foo.map(e => e.foo); +} + +function barFunction(bar: ArrayClass[]) { + return bar.map(e => e.bar); +} + +function bazFunction(baz: Arr>) { + return baz.map(e => e.baz); +} + +let fooVar: ((c: number) => number)[]; +let barVar: ((c: number) => number)[]; + +type fooUnion = (string|number|boolean)[]; +type barUnion = (string|number|boolean)[]; + +type fooIntersection = (string & number)[]; +type barIntersection = (string & number)[]; + +interface FooInterface { + '.bar': {baz: string[];}; +} diff --git a/test/rules/array-type/array/test.ts.lint b/test/rules/array-type/array/test.ts.lint new file mode 100644 index 00000000000..c1d47825d7b --- /dev/null +++ b/test/rules/array-type/array/test.ts.lint @@ -0,0 +1,56 @@ +let x: Array = [1] as number[]; + ~~~~~~~~~~~~~ [Array type using 'Array' is forbidden. Use 'T[]' instead.] +let y: string[] = >["2"]; + ~~~~~~~~~~~~~ [Array type using 'Array' is forbidden. Use 'T[]' instead.] +let z: Array = [3, "4"]; + ~~~~~ [Array type using 'Array' is forbidden. Use 'T[]' instead.] + +let xx: Array> = [[1, 2], [3]]; + ~~~~~~~~~~~~~~~~~~~~ [Array type using 'Array' is forbidden. Use 'T[]' instead.] + ~~~~~~~~~~~~~ [Array type using 'Array' is forbidden. Use 'T[]' instead.] +let yy: number[][] = [[4, 5], [6]]; + +let ya = [[1, "2"]] as[number, string][]; + +type Arr = Array; + ~~~~~~~~ [Array type using 'Array' is forbidden. Use 'T[]' instead.] + +// Ignore user defined aliases +let yyyy: Arr>[]> = [[[["2"]]]]; + ~~~~~~~~~~~~~~~~~~ [Array type using 'Array' is forbidden. Use 'T[]' instead.] + +interface ArrayClass { + foo: Array; + ~~~~~~~~ [Array type using 'Array' is forbidden. Use 'T[]' instead.] + bar: T[]; + baz: Arr; +} + +function fooFunction(foo: Array>) { + ~~~~~~~~~~~~~~~~~~~~~~~~~ [Array type using 'Array' is forbidden. Use 'T[]' instead.] + return foo.map(e => e.foo); +} + +function barFunction(bar: ArrayClass[]) { + return bar.map(e => e.bar); +} + +function bazFunction(baz: Arr>) { + return baz.map(e => e.baz); +} + +let fooVar: Array<(c: number) => number>; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [Array type using 'Array' is forbidden. Use 'T[]' instead.] +let barVar: ((c: number) => number)[]; + +type fooUnion = Array; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [Array type using 'Array' is forbidden. Use 'T[]' instead.] +type barUnion = (string|number|boolean)[]; + +type fooIntersection = Array; + ~~~~~~~~~~~~~~~~~~~~~~ [Array type using 'Array' is forbidden. Use 'T[]' instead.] +type barIntersection = (string & number)[]; + +interface FooInterface { + '.bar': {baz: string[];}; +} diff --git a/test/rules/array-type/array/tslint.json b/test/rules/array-type/array/tslint.json new file mode 100644 index 00000000000..184d4606431 --- /dev/null +++ b/test/rules/array-type/array/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "array-type": [true, "array"] + } +} diff --git a/test/rules/array-type/generic/test.ts.fix b/test/rules/array-type/generic/test.ts.fix new file mode 100644 index 00000000000..ff61e7c0a1b --- /dev/null +++ b/test/rules/array-type/generic/test.ts.fix @@ -0,0 +1,44 @@ +let x: Array = [1] as Array; +let y: Array = >["2"]; +let z: Array = [3, "4"]; + +let xx: Array> = [[1, 2], [3]]; +let yy: Array> = [[4, 5], [6]]; + +let ya = [[1, "2"]] as Array<[number, string]>; + +type Arr = Array; + +// Ignore user defined aliases +let yyyy: Arr>>> = [[[["2"]]]]; + +interface ArrayClass { + foo: Array; + bar: Array; + baz: Arr; +} + +function fooFunction(foo: Array>) { + return foo.map(e => e.foo); +} + +function barFunction(bar: Array>) { + return bar.map(e => e.bar); +} + +function bazFunction(baz: Arr>) { + return baz.map(e => e.baz); +} + +let fooVar: Array<(c: number) => number>; +let barVar: Array<(c: number) => number>; + +type fooUnion = Array; +type barUnion = Array; + +type fooIntersection = Array; +type barIntersection = Array; + +interface FooInterface { + '.bar': {baz: Array;}; +} diff --git a/test/rules/array-type/generic/test.ts.lint b/test/rules/array-type/generic/test.ts.lint new file mode 100644 index 00000000000..1db9953c3a1 --- /dev/null +++ b/test/rules/array-type/generic/test.ts.lint @@ -0,0 +1,56 @@ +let x: Array = [1] as number[]; + ~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array' instead.] +let y: string[] = >["2"]; + ~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array' instead.] +let z: Array = [3, "4"]; + +let xx: Array> = [[1, 2], [3]]; +let yy: number[][] = [[4, 5], [6]]; + ~~~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array' instead.] + ~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array' instead.] + +let ya = [[1, "2"]] as[number, string][]; + ~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array' instead.] + +type Arr = Array; + +// Ignore user defined aliases +let yyyy: Arr>[]> = [[[["2"]]]]; + ~~~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array' instead.] + +interface ArrayClass { + foo: Array; + bar: T[]; + ~~~ [Array type using 'T[]' is forbidden. Use 'Array' instead.] + baz: Arr; +} + +function fooFunction(foo: Array>) { + return foo.map(e => e.foo); +} + +function barFunction(bar: ArrayClass[]) { + ~~~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array' instead.] + return bar.map(e => e.bar); +} + +function bazFunction(baz: Arr>) { + return baz.map(e => e.baz); +} + +let fooVar: Array<(c: number) => number>; +let barVar: ((c: number) => number)[]; + ~~~~~~~~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array' instead.] + +type fooUnion = Array; +type barUnion = (string|number|boolean)[]; + ~~~~~~~~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array' instead.] + +type fooIntersection = Array; +type barIntersection = (string & number)[]; + ~~~~~~~~~~~~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array' instead.] + +interface FooInterface { + '.bar': {baz: string[];}; + ~~~~~~~~ [Array type using 'T[]' is forbidden. Use 'Array' instead.] +} diff --git a/test/rules/array-type/generic/tslint.json b/test/rules/array-type/generic/tslint.json new file mode 100644 index 00000000000..610c20100a8 --- /dev/null +++ b/test/rules/array-type/generic/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "array-type": [true, "generic"] + } +} diff --git a/test/rules/ban/test.ts.lint b/test/rules/ban/test.ts.lint index 3ac20772ce8..baa27411a05 100644 --- a/test/rules/ban/test.ts.lint +++ b/test/rules/ban/test.ts.lint @@ -12,4 +12,15 @@ _.keys(obj).forEach(fun); _.forEach(fun); ~~~~~~~~~ [Calls to '_.forEach' are not allowed.] _.filter(array); -~~~~~~~~ [Calls to '_.filter' are not allowed. Use the native JavaScript 'myArray.filter' instead.] \ No newline at end of file +~~~~~~~~ [Calls to '_.filter' are not allowed. Use the native JavaScript 'myArray.filter' instead.] +describe("some text", () => {}); +xdescribe("some text", () => {}); +~~~~~~~~~ [Calls to 'xdescribe' are not allowed.] +fdescribe("some text", () => {}); +~~~~~~~~~ [Calls to 'fdescribe' are not allowed.] +it("some text", () => {}); +xit("some text", () => {}); +~~~ [Calls to 'xit' are not allowed.] +fit("some text", () => {}); +~~~ [Calls to 'fit' are not allowed.] +someObject.fit() \ No newline at end of file diff --git a/test/rules/ban/tslint.json b/test/rules/ban/tslint.json index 0271539a492..ca4c09b14f7 100644 --- a/test/rules/ban/tslint.json +++ b/test/rules/ban/tslint.json @@ -1,8 +1,12 @@ { "rules": { "ban": [ - true, - ["window", "toString"], + true, + ["xit"], + ["fit"], + ["xdescribe"], + ["fdescribe"], + ["window", "toString"], ["_", "forEach"], ["_", "filter", "Use the native JavaScript 'myArray.filter' instead."] ] diff --git a/test/rules/comment-format/lower/test.ts.lint b/test/rules/comment-format/lower/test.ts.lint index 42d2730a105..6b0f7e4cfb2 100644 --- a/test/rules/comment-format/lower/test.ts.lint +++ b/test/rules/comment-format/lower/test.ts.lint @@ -18,5 +18,8 @@ class Clazz { // this comment is correct `${location.protocol}//${location.hostname}` +//noinspection JSUnusedGlobalSymbols +const unusedVar = 'unneeded value'; + [lower]: comment must start with lowercase letter [space]: comment must start with a space diff --git a/test/rules/cyclomatic-complexity/defaultThreshold/test.ts.lint b/test/rules/cyclomatic-complexity/defaultThreshold/test.ts.lint new file mode 100644 index 00000000000..a55df367b3b --- /dev/null +++ b/test/rules/cyclomatic-complexity/defaultThreshold/test.ts.lint @@ -0,0 +1,68 @@ +// Check that a default threshold is used if none is specified. + +function validThresholdPass() { + const condition1 = true ? "true" : "false"; + const condition2 = true ? "true" : "false"; + const condition3 = true ? "true" : "false"; + const condition4 = true ? "true" : "false"; + const condition5 = true ? "true" : "false"; + const condition6 = true ? "true" : "false"; + const condition7 = true ? "true" : "false"; + const condition8 = true ? "true" : "false"; + const condition9 = true ? "true" : "false"; + const condition10 = true ? "true" : "false"; + const condition11 = true ? "true" : "false"; + const condition12 = true ? "true" : "false"; + const condition13 = true ? "true" : "false"; + const condition14 = true ? "true" : "false"; + const condition15 = true ? "true" : "false"; + const condition16 = true ? "true" : "false"; + const condition17 = true ? "true" : "false"; + const condition18 = true ? "true" : "false"; + const condition19 = true ? "true" : "false"; +} + +function validThresholdFail() { +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition1 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition2 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition3 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition4 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition5 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition6 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition7 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition8 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition9 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition10 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition11 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition12 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition13 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition14 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition15 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition16 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition17 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition18 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition19 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition20 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +} +~ [The function validThresholdFail has a cyclomatic complexity of 21 which is higher than the threshold of 20] diff --git a/test/rules/cyclomatic-complexity/defaultThreshold/tslint.json b/test/rules/cyclomatic-complexity/defaultThreshold/tslint.json new file mode 100644 index 00000000000..aee113f4f0b --- /dev/null +++ b/test/rules/cyclomatic-complexity/defaultThreshold/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "cyclomatic-complexity": [true] + } +} diff --git a/test/rules/cyclomatic-complexity/invalidThreshold/test.ts.lint b/test/rules/cyclomatic-complexity/invalidThreshold/test.ts.lint new file mode 100644 index 00000000000..aa6f059cad0 --- /dev/null +++ b/test/rules/cyclomatic-complexity/invalidThreshold/test.ts.lint @@ -0,0 +1,25 @@ +// Check that rule is not applied for invalid config values by checking a +// function with complexity higher than the dafault threshold. + +function invalidThreshold() { + const condition1 = true ? "true" : "false"; + const condition2 = true ? "true" : "false"; + const condition3 = true ? "true" : "false"; + const condition4 = true ? "true" : "false"; + const condition5 = true ? "true" : "false"; + const condition6 = true ? "true" : "false"; + const condition7 = true ? "true" : "false"; + const condition8 = true ? "true" : "false"; + const condition9 = true ? "true" : "false"; + const condition10 = true ? "true" : "false"; + const condition11 = true ? "true" : "false"; + const condition12 = true ? "true" : "false"; + const condition13 = true ? "true" : "false"; + const condition14 = true ? "true" : "false"; + const condition15 = true ? "true" : "false"; + const condition16 = true ? "true" : "false"; + const condition17 = true ? "true" : "false"; + const condition18 = true ? "true" : "false"; + const condition19 = true ? "true" : "false"; + const condition20 = true ? "true" : "false"; +} diff --git a/test/rules/cyclomatic-complexity/invalidThreshold/tslint.json b/test/rules/cyclomatic-complexity/invalidThreshold/tslint.json new file mode 100644 index 00000000000..d8c5fbdd4c0 --- /dev/null +++ b/test/rules/cyclomatic-complexity/invalidThreshold/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "cyclomatic-complexity": [true, -5] + } +} diff --git a/test/rules/cyclomatic-complexity/specifiedThreshold/test.ts.lint b/test/rules/cyclomatic-complexity/specifiedThreshold/test.ts.lint new file mode 100644 index 00000000000..6276c52628a --- /dev/null +++ b/test/rules/cyclomatic-complexity/specifiedThreshold/test.ts.lint @@ -0,0 +1,381 @@ +// Test each function type. +const validFunctionExpression = function() { + const condition1 = true ? "true" : "false"; + const condition2 = true ? "true" : "false"; +} + +const invalidFunctionExpression = function() { + ~~~~~~~~~~~~ + const condition1 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition2 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition3 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +} +~ [The function has a cyclomatic complexity of 4 which is higher than the threshold of 3] + + +function validFunctionDeclaration() { + const condition1 = true ? "true" : "false"; + const condition2 = true ? "true" : "false"; +} + +function invalidFunctionDeclaration() { +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition1 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition2 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition3 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +} +~ [The function invalidFunctionDeclaration has a cyclomatic complexity of 4 which is higher than the threshold of 3] + +const validArrowFunction = () => { + const condition1 = true ? "true" : "false"; + const condition2 = true ? "true" : "false"; +} + +const invalidArrowFunction = () => { + ~~~~~~~ + const condition1 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition2 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition3 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +} +~ [The function has a cyclomatic complexity of 4 which is higher than the threshold of 3] + +class MyClass { + get validGet(): string { + const condition1 = true ? "true" : "false"; + const condition2 = true ? "true" : "false"; + return "test"; + } + + get invalidGet(): string { + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition1 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition2 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition3 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + return "test"; +~~~~~~~~~~~~~~~~~~~~~~ + } +~~~~~ [The function invalidGet has a cyclomatic complexity of 4 which is higher than the threshold of 3] + + set validSet(newValue: string) { + const condition1 = true ? "true" : "false"; + const condition2 = true ? "true" : "false"; + } + + set invalidSet(newValue: string) { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition1 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition2 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition3 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + } +~~~~~ [The function invalidSet has a cyclomatic complexity of 4 which is higher than the threshold of 3] + + validMethod() { + const condition1 = true ? "true" : "false"; + const condition2 = true ? "true" : "false"; + } + + invalidMethod() { + ~~~~~~~~~~~~~~~~~~ + const condition1 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition2 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition3 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + } +~~~~~ [The function invalidMethod has a cyclomatic complexity of 4 which is higher than the threshold of 3] +} + +class validConstructor { + constructor() { + const condition1 = true ? "true" : "false"; + const condition2 = true ? "true" : "false"; + } +} + +class invalidConstructor { + constructor() { + ~~~~~~~~~~~~~~~ + const condition1 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition2 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition3 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + } +~~~~~ [The function has a cyclomatic complexity of 4 which is higher than the threshold of 3] +} + +// Test each remaining expression type. +function validBinaryExpression() { + return first || second && third +} + +function invalidBinaryExpression() { +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + return first || second && third || fourth; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +} +~ [The function invalidBinaryExpression has a cyclomatic complexity of 4 which is higher than the threshold of 3] + +function validSwitch() { + switch(5) { + case 0: + return 0; + case 1: + return 1; + default: + return -1; + } +} + +function invalidSwitch() { +~~~~~~~~~~~~~~~~~~~~~~~~~~ + switch(5) { +~~~~~~~~~~~~~~~ + case 0: +~~~~~~~~~~~~~~~ + return 0; +~~~~~~~~~~~~~~~~~~~~~ + case 1: +~~~~~~~~~~~~~~~ + return 1; +~~~~~~~~~~~~~~~~~~~~~ + case 2: +~~~~~~~~~~~~~~~ + return 2; +~~~~~~~~~~~~~~~~~~~~~ + default: +~~~~~~~~~~~~~~~~ + return -1; +~~~~~~~~~~~~~~~~~~~~~~ + } +~~~~~ +} +~ [The function invalidSwitch has a cyclomatic complexity of 4 which is higher than the threshold of 3] + +function validCatch() { + try { } + catch(error) { } + try { } + catch(error) { } + try { } + finally { } +} + +function invalidCatch() { +~~~~~~~~~~~~~~~~~~~~~~~~~ + try { } +~~~~~~~~~~~ + catch(error) { } +~~~~~~~~~~~~~~~~~~~~ + try { } +~~~~~~~~~~~ + catch(error) { } +~~~~~~~~~~~~~~~~~~~~ + try { } +~~~~~~~~~~~ + catch(error) { } +~~~~~~~~~~~~~~~~~~~~ + try { } +~~~~~~~~~~~ + finally { } +~~~~~~~~~~~~~~~ +} +~ [The function invalidCatch has a cyclomatic complexity of 4 which is higher than the threshold of 3] + +function validDo() { + do { } + while (false) + do { } + while (false) +} + +function invalidDo() { +~~~~~~~~~~~~~~~~~~~~~~ + do { } +~~~~~~~~~~ + while (false) +~~~~~~~~~~~~~~~~~ + do { } +~~~~~~~~~~ + while (false) +~~~~~~~~~~~~~~~~~ + do { } +~~~~~~~~~~ + while (false) +~~~~~~~~~~~~~~~~~ +} +~ [The function invalidDo has a cyclomatic complexity of 4 which is higher than the threshold of 3] + +function validFor() { + for(;;) { } + for(;;) { } +} + +function invalidFor() { +~~~~~~~~~~~~~~~~~~~~~~~ + for(;;) { } +~~~~~~~~~~~~~~~ + for(;;) { } +~~~~~~~~~~~~~~~ + for(;;) { } +~~~~~~~~~~~~~~~ +} +~ [The function invalidFor has a cyclomatic complexity of 4 which is higher than the threshold of 3] + +function validForIn() { + for(let i in [0,1,2]) { } + for(let i in [0,1,2]) { } +} + +function invalidForIn() { +~~~~~~~~~~~~~~~~~~~~~~~~~ + for(let i in [0,1,2]) { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + for(let i in [0,1,2]) { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + for(let i in [0,1,2]) { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +} +~ [The function invalidForIn has a cyclomatic complexity of 4 which is higher than the threshold of 3] + +function validForOf() { + for(let i of [0,1,2]) { } + for(let i of [0,1,2]) { } +} + +function invalidForOf() { +~~~~~~~~~~~~~~~~~~~~~~~~~ + for(let i of [0,1,2]) { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + for(let i of [0,1,2]) { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + for(let i of [0,1,2]) { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +} +~ [The function invalidForOf has a cyclomatic complexity of 4 which is higher than the threshold of 3] + +let variable = 1; + +function validIf() { + if(variable) { } + if(variable) { } else { } +} + +function invalidIf() { +~~~~~~~~~~~~~~~~~~~~~~ + if(variable) { } +~~~~~~~~~~~~~~~~~~~~ + if(variable) { } else { } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if(variable) { } +~~~~~~~~~~~~~~~~~~~~ +} +~ [The function invalidIf has a cyclomatic complexity of 4 which is higher than the threshold of 3] + +function validWhile() { + while(false) { } + while(false) { } +} + +function invalidWhile() { +~~~~~~~~~~~~~~~~~~~~~~~~~ + while(false) { } +~~~~~~~~~~~~~~~~~~~~ + while(false) { } +~~~~~~~~~~~~~~~~~~~~ + while(false) { } +~~~~~~~~~~~~~~~~~~~~ +} +~ [The function invalidWhile has a cyclomatic complexity of 4 which is higher than the threshold of 3] + +// Test that the rule behaves correctly for nested functions. + +// Valid outer function, invalid inner function. +function outer1() { + const condition1 = true ? "true" : "false"; + const condition2 = true ? "true" : "false"; + function inner1() { + ~~~~~~~~~~~~~~~~~~~ + const condition3 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition4 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition5 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + } +~~~~~ [The function inner1 has a cyclomatic complexity of 4 which is higher than the threshold of 3] +} + +// Invalid outer function, valid inner function. +function outer2() { +~~~~~~~~~~~~~~~~~~~ + const condition1 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition2 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition3 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + function inner2() { +~~~~~~~~~~~~~~~~~~~~~~~ + const condition4 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition5 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + } +~~~~~ +} +~ [The function outer2 has a cyclomatic complexity of 4 which is higher than the threshold of 3] + +// Both valid. +function outer3() { + const condition1 = true ? "true" : "false"; + const condition2 = true ? "true" : "false"; + function inner3() { + const condition3 = true ? "true" : "false"; + const condition4 = true ? "true" : "false"; + } +} + +// Both invalid. +function outer4() { +~~~~~~~~~~~~~~~~~~~ + const condition1 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition2 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition3 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + function inner4() { +~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~ + const condition4 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition5 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + const condition6 = true ? "true" : "false"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + } +~~~~~ +~~~~~ [The function inner4 has a cyclomatic complexity of 4 which is higher than the threshold of 3] +} +~ [The function outer4 has a cyclomatic complexity of 4 which is higher than the threshold of 3] diff --git a/test/rules/cyclomatic-complexity/specifiedThreshold/tslint.json b/test/rules/cyclomatic-complexity/specifiedThreshold/tslint.json new file mode 100644 index 00000000000..b16ec05ded6 --- /dev/null +++ b/test/rules/cyclomatic-complexity/specifiedThreshold/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "cyclomatic-complexity": [true, 3] + } +} diff --git a/test/rules/jsdoc-format/jsdoc.ts.lint b/test/rules/jsdoc-format/jsdoc.ts.lint index 52444146974..e5bfe71884f 100644 --- a/test/rules/jsdoc-format/jsdoc.ts.lint +++ b/test/rules/jsdoc-format/jsdoc.ts.lint @@ -23,6 +23,10 @@ I've even got characters where I shouldn't. How fun! * */ } + /**********************************************************/ + /* this is just an arbitrary separator, not a jsdoc block */ + /**********************************************************/ + /** * this is also jsdoc *and it has a problem on this line diff --git a/test/rules/no-var-keyword/test.ts.lint b/test/rules/no-var-keyword/test.ts.lint index 31148269d6b..0579e31d389 100644 --- a/test/rules/no-var-keyword/test.ts.lint +++ b/test/rules/no-var-keyword/test.ts.lint @@ -20,6 +20,21 @@ for (var n1 in foo); for (var n2 of foo); ~~~ [Forbidden 'var' keyword, use 'let' or 'const' instead] +export var exportVar0 = 0; + ~~~ [Forbidden 'var' keyword, use 'let' or 'const' instead] +export var exportVar1 = 1; + ~~~ [Forbidden 'var' keyword, use 'let' or 'const' instead] +export var exportVar2 = 2, + ~~~ [Forbidden 'var' keyword, use 'let' or 'const' instead] + exportVar21 = 21; +export /* var tst */ var exportVar3 = 3; + ~~~ [Forbidden 'var' keyword, use 'let' or 'const' instead] + +module quz { + export var i; + ~~~ [Forbidden 'var' keyword, use 'let' or 'const' instead] +} + declare var tmp2: any; let bar; @@ -28,10 +43,6 @@ const qux; let k, l; -module quz { - export var i; -} - let [x, y] = [1, 2]; for (n; false;); @@ -40,3 +51,6 @@ for (let name in foo); for (let name of foo); for (const name in foo); for (const name of foo); + +export let exportLet = 1; +export const exportConst = 1; diff --git a/test/rules/object-literal-sort-keys/test.ts.lint b/test/rules/object-literal-sort-keys/test.ts.lint index 813815e14dd..5e8c9fccb10 100644 --- a/test/rules/object-literal-sort-keys/test.ts.lint +++ b/test/rules/object-literal-sort-keys/test.ts.lint @@ -83,3 +83,64 @@ var failG = { asdfn: function () {} ~~~~~ [The key 'asdfn' is not sorted alphabetically] }; + +var passH = { + a: 1, + 'b': 2, + c: 3 +} + +var failH = { + 'b': 2, + a: 1, + ~ [The key 'a' is not sorted alphabetically] + c: 3 +} + +var passI = { + 'Z': 1, + À: 2, + è: 3 +} + +var failI = { + À: 2, + 'Z': 1, + ~~~ [The key 'Z' is not sorted alphabetically] + è: 3, +} + +var passJ = { + 1: 1, + '11': 2 + 2: 4, + A: 3, +} + +var failJ = { + 1: 1, + 2: 4, + A: 3, + '11': 2 + ~~~~ [The key '11' is not sorted alphabetically] +} + +var passK = { + a: 1, + 'b': { + 'd': 4, + e: 5 + }, + c: 3 +} + +var failK = { + 'b': { + e: 5, + 'd': 4 + ~~~ [The key 'd' is not sorted alphabetically] + }, + a: 1, + ~ [The key 'a' is not sorted alphabetically] + c: 3 +} diff --git a/test/rules/only-arrow-functions/allow-declarations/test.ts.lint b/test/rules/only-arrow-functions/allow-declarations/test.ts.lint index 64c6da35f00..22dbf05bec4 100644 --- a/test/rules/only-arrow-functions/allow-declarations/test.ts.lint +++ b/test/rules/only-arrow-functions/allow-declarations/test.ts.lint @@ -17,4 +17,7 @@ function () { ((func) => func())(() => {}); +function* generator() {} +let generator = function*() {} + [0]: non-arrow functions are forbidden diff --git a/test/rules/only-arrow-functions/default/test.ts.lint b/test/rules/only-arrow-functions/default/test.ts.lint index 344df6717d4..ddacd8e2fc5 100644 --- a/test/rules/only-arrow-functions/default/test.ts.lint +++ b/test/rules/only-arrow-functions/default/test.ts.lint @@ -20,4 +20,7 @@ function () { ((func) => func())(() => {}); +function* generator() {} +let generator = function*() {} + [0]: non-arrow functions are forbidden diff --git a/test/rules/prefer-for-of/test.ts.lint b/test/rules/prefer-for-of/test.ts.lint new file mode 100644 index 00000000000..0457f270347 --- /dev/null +++ b/test/rules/prefer-for-of/test.ts.lint @@ -0,0 +1,109 @@ +function sampleFunc() { + + //This loop only uses the iterator to access the array item, so we can recommend a for-of loop here + for (var a = 0; a <= obj.arr.length; a++) { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + console.log(obj.arr[a]); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + } +~~~~~ [0] + + //Same as above, but no curly braces + for (var b = 0; b <= obj.arr.length; b++) console.log(obj.arr[b]); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [0] + + //the index is used by itself, so a normal for loop is allowed here + for (var c = 0; c <= arr.length; c++) { + doMath(c); + } + + //Same as above, but no curly braces + for (var d = 0; d <= arr.length; d++) doMath(d); + + //the index is used by itself, so a normal for loop is allowed here + for (var e = 0; e <= arr.length; e++) { + if(e > 5) { + doMath(e); + } + console.log(arr[e]); + } + + //This iterates off of a hard-coded number and should be allowed + for (var f = 0; f <= 40; f++) { + doMath(f); + } + + //Same as above, but no curly braces + for (var g = 0; g <= 40; g++) doMath(g); + + //Loop set up different, but uses the index alone - this is ok + for(var h=0, len=arr.length; h < len; h++) { + doMath(h); + } + + //Same as above, but no curly braces + for(var i=0, len=arr.length; i < len; i++) doMath(i); + + //Loop set up different, but uses the index alone - this is ok + for(var j=0, len=arr.length; j < len; j++){ + if(j > 5) { + doMath(j); + } + console.log(arr[j]); + } + + //Loop set up different, only uses the index to access the array - this should fail + for(var k=0, len=arr.length; k < len; k++) { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + console.log(arr[k]); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + } +~~~~~ [0] + + //Same as above, but no curly braces + for(var l=0, len=arr.length; l < len; l++) console.log(arr[l]); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [0] + + //Odd for loop setups + var m = 0; + for (;;) { + if (m > 3) break; + console.log(m); + m++; + } + + var n = 0; + for (; n < 9; n++) { + console.log(n); + } + + var o = 0; + for (; o < arr.length; o++) { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + console.log(arr[o]); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + } +~~~~~ [0] + + //Prefix incrementor + for(let p = 0; p < arr.length; ++p) { + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + arr[p].whatever(); +~~~~~~~~~~~~~~~~~~~~~~~~~~ + } +~~~~~ [0] + + //For in loops ARE allowed + for (var q in obj) { + if (obj.hasOwnProperty(q)) { + console.log(q); + } + } + + //For of loops ARE allowed + for (var r of arr) { + console.log(r); + } +} + +[0]: Expected a 'for-of' loop instead of a 'for' loop with this simple iteration diff --git a/test/rules/prefer-for-of/tslint.json b/test/rules/prefer-for-of/tslint.json new file mode 100644 index 00000000000..a7d6263e98a --- /dev/null +++ b/test/rules/prefer-for-of/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "prefer-for-of": true + } +} diff --git a/test/rules/semicolon/always/test.ts.fix b/test/rules/semicolon/always/test.ts.fix index 330a108ce2e..74281ce9438 100644 --- a/test/rules/semicolon/always/test.ts.fix +++ b/test/rules/semicolon/always/test.ts.fix @@ -30,6 +30,14 @@ debugger; import v = require("i"); module M { export var x; + export function f(s: string): string; + export function f(n: number): number; + export function f(x: any) { return x; } +} + +declare module "M" { + function f(): number; + function g(): number; } function useStrictMissingSemicolon() { @@ -62,4 +70,5 @@ export default LoginPage; export default LoginPage; export = Date; export = Date; - +type t = number; +type t = number; diff --git a/test/rules/semicolon/always/test.ts.lint b/test/rules/semicolon/always/test.ts.lint index badad2ddff7..58c0f436c88 100644 --- a/test/rules/semicolon/always/test.ts.lint +++ b/test/rules/semicolon/always/test.ts.lint @@ -44,6 +44,16 @@ import v = require("i") module M { export var x ~nil [Missing semicolon] + export function f(s: string): string; + export function f(n: number): number + ~nil [Missing semicolon] + export function f(x: any) { return x; } +} + +declare module "M" { + function f(): number; + function g(): number + ~nil [Missing semicolon] } function useStrictMissingSemicolon() { @@ -85,4 +95,6 @@ export default LoginPage export = Date; export = Date ~nil [Missing semicolon] - +type t = number; +type t = number + ~nil [Missing semicolon] diff --git a/test/rules/semicolon/enabled/test.ts.fix b/test/rules/semicolon/enabled/test.ts.fix index ec2189ca95e..87f1de6d87f 100644 --- a/test/rules/semicolon/enabled/test.ts.fix +++ b/test/rules/semicolon/enabled/test.ts.fix @@ -30,6 +30,14 @@ debugger; import v = require("i"); module M { export var x; + export function f(s: string): string; + export function f(n: number): number; + export function f(x: any) { return x; } +} + +declare module "M" { + function f(): number; + function g(): number; } function useStrictMissingSemicolon() { @@ -60,3 +68,5 @@ export default LoginPage; export default LoginPage; export = Date; export = Date; +type t = number; +type t = number; diff --git a/test/rules/semicolon/enabled/test.ts.lint b/test/rules/semicolon/enabled/test.ts.lint index 3b7c7f5df7b..5794f22dbf0 100644 --- a/test/rules/semicolon/enabled/test.ts.lint +++ b/test/rules/semicolon/enabled/test.ts.lint @@ -44,6 +44,16 @@ import v = require("i") module M { export var x ~nil [Missing semicolon] + export function f(s: string): string; + export function f(n: number): number + ~nil [Missing semicolon] + export function f(x: any) { return x; } +} + +declare module "M" { + function f(): number; + function g(): number + ~nil [Missing semicolon] } function useStrictMissingSemicolon() { @@ -83,3 +93,6 @@ export default LoginPage export = Date; export = Date ~nil [Missing semicolon] +type t = number; +type t = number + ~nil [Missing semicolon] diff --git a/test/rules/semicolon/ignore-interfaces/test.ts.fix b/test/rules/semicolon/ignore-interfaces/test.ts.fix index 9d28c5db66f..1bb2d36cedf 100644 --- a/test/rules/semicolon/ignore-interfaces/test.ts.fix +++ b/test/rules/semicolon/ignore-interfaces/test.ts.fix @@ -30,6 +30,14 @@ debugger; import v = require("i"); module M { export var x; + export function f(s: string): string; + export function f(n: number): number; + export function f(x: any) { return x; } +} + +declare module "M" { + function f(): number; + function g(): number; } function useStrictMissingSemicolon() { @@ -62,3 +70,5 @@ export default LoginPage; export default LoginPage; export = Date; export = Date; +type t = number; +type t = number; diff --git a/test/rules/semicolon/ignore-interfaces/test.ts.lint b/test/rules/semicolon/ignore-interfaces/test.ts.lint index 1ee2a304971..e2251344f0c 100644 --- a/test/rules/semicolon/ignore-interfaces/test.ts.lint +++ b/test/rules/semicolon/ignore-interfaces/test.ts.lint @@ -44,6 +44,16 @@ import v = require("i") module M { export var x ~nil [Missing semicolon] + export function f(s: string): string; + export function f(n: number): number + ~nil [Missing semicolon] + export function f(x: any) { return x; } +} + +declare module "M" { + function f(): number; + function g(): number + ~nil [Missing semicolon] } function useStrictMissingSemicolon() { @@ -83,3 +93,6 @@ export default LoginPage export = Date; export = Date ~nil [Missing semicolon] +type t = number; +type t = number + ~nil [Missing semicolon] diff --git a/test/rules/semicolon/never/test.ts.fix b/test/rules/semicolon/never/test.ts.fix index 085b171bf39..a6e29364501 100644 --- a/test/rules/semicolon/never/test.ts.fix +++ b/test/rules/semicolon/never/test.ts.fix @@ -30,6 +30,14 @@ debugger import v = require("i") module M { export var x + export function f(s: string): string + export function f(n: number): number + export function f(x: any) { return x } +} + +declare module "M" { + function f(): number + function g(): number } function useStrictUnnecessarySemicolon() { @@ -93,3 +101,6 @@ export default LoginPage export = Date export = Date + +type t = number +type t = number diff --git a/test/rules/semicolon/never/test.ts.lint b/test/rules/semicolon/never/test.ts.lint index 0cb9871f32e..bb4b4b02450 100644 --- a/test/rules/semicolon/never/test.ts.lint +++ b/test/rules/semicolon/never/test.ts.lint @@ -44,6 +44,17 @@ import v = require("i"); module M { export var x; ~ [Unnecessary semicolon] + export function f(s: string): string; + ~ [Unnecessary semicolon] + export function f(n: number): number + export function f(x: any) { return x; } + ~ [Unnecessary semicolon] +} + +declare module "M" { + function f(): number; + ~ [Unnecessary semicolon] + function g(): number } function useStrictUnnecessarySemicolon() { @@ -116,3 +127,7 @@ export default LoginPage export = Date; ~ [Unnecessary semicolon] export = Date + +type t = number; + ~ [Unnecessary semicolon] +type t = number diff --git a/test/rules/trailing-comma/multiline-always/test.ts.fix b/test/rules/trailing-comma/multiline-always/test.ts.fix new file mode 100644 index 00000000000..cbee7ac25f7 --- /dev/null +++ b/test/rules/trailing-comma/multiline-always/test.ts.fix @@ -0,0 +1,542 @@ +var v = [{ + a: 1, + b: 2, + d: 34, + c: (a + b), +},]; + +var x = [{ + a: 1, + b: 2, + d: 34, + c: (a + b), +}]; + +var s = { + sA: 6, +}; + +var t = { + tA: 7, +} + +var y = { + yA: 42, + yB: 24, +}; + +var z = { + zOne: 2, + zTwo: 1, +}; + +var ss = [ + 6, +]; + +var tt = [ + 7, +]; + +var yy = [ + 42, + 24, +]; + +var zz = [ + 2, + 1, +]; + +var a = [{a: 1, b: 2, d: 34, c: (a + b),},]; + +var b = [{a: 1, b: 2, d: 34, c: (a + b)}]; + +var c = {cA: 42, cB: 24,}; + +var d = {dOne: 2, dTwo: 1}; + +var cc = [42, 24,]; + +var dd = [2, 1]; + +var { + sA, +} = s; + +var { + tA, +} = t; + +var { + yA, + yB, +} = y; + +var { + zOne, + zTwo, +} = z; + +var [ + ssA, +] = ss; + +var [ + ttA, +] = tt; + +var [ + yyA, + yyB, +] = yy; + +var [ + zzOne, + zzTwo, +] = zz; + +var {cA, cB,} = c; + +var {dOne, dTwo} = d; + +var [ccA, ccB,] = cc; + +var [ddOne, ddTwo] = dd; + +var a: [string, number] = null; + +var a: [string, number,] = null; + +var a: [ + string, + number, +] = null; + +var a: [ + string, + number, +] = null; + +import { + ClassS, +} from "module"; + +import { + ClassT, +} from "module"; + +import { + ClassV, + ClassX, +} from "module"; + +import { + ClassY, + ClassZ, +} from "module"; + +import {ClassA, ClassB,} from "module"; + +import {ClassC, ClassD} from "module"; + +function foo(bar: string, baz: string); + +function foo(bar: string, baz: string,); + +function foo( + bar: string, + baz: string, +); + +function foo( + bar: string, + baz: string, +); + +var foo = function(bar: string, baz: string) { return 42; }; + +var foo = function(bar: string, baz: string,) { return 42; }; + +var foo = function( + bar: string, + baz: string, +) { + return 42; +}; + +var foo = function( + bar: string, + baz: string, +) { + return 42; +}; + +function foo(bar: string, baz: string) { return 42; } + +function foo(bar: string, baz: string,) { return 42; } + +function foo( + bar: string, + baz: string, +) { + return 42; +} + +function foo( + bar: string, + baz: string, +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string, +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string, +) { + return 42; +} + +var foo = (bar: string, baz: string) => 42; + +var foo = (bar: string, baz: string,) => 42; + +var foo = ( + bar: string, + baz: string, +) => 42; + +var foo = ( + bar: string, + baz: string, +) => 42; + +var foo: (bar: string, baz: string) => number = null; + +var foo: (bar: string, baz: string,) => number = null; + +var foo: ( + bar: string, + baz: string, +) => number = null; + +var foo: ( + bar: string, + baz: string, +) => number = null; + +var foo: (bar: string, baz: string) => number; + +var foo: (bar: string, baz: string,) => number; + +var foo: ( + bar: string, + baz: string, +) => number; + +var foo: ( + bar: string, + baz: string, +) => number; + +var foo: new (bar: string, baz: string) => Test; + +var foo: new (bar: string, baz: string,) => Test; + +var foo: new ( + bar: string, + baz: string, +) => Test; + +var foo: new ( + bar: string, + baz: string, +) => Test; + +var x = foo(42, 43); + +var x = foo(42, 43,); + +var x = foo( + 42, + 43, +); + +var x = foo( + 42, + 43, +); + +var x = new Test("foo"); + +var x = new Test("foo",); + +var x = new Test( + "foo", +); + +var x = new Test( + "foo", +); + +var x: { foo: string; bar: string }; + +var x: { foo: string; bar: string; }; + +var x: { + foo: string; + bar: string +}; + +var x: { + foo: string; + bar: string; +}; + +var x: { foo: string, bar: string }; + +var x: { foo: string, bar: string, }; + +var x: { + foo: string, + bar: string, +}; + +var x: { + foo: string, + bar: string, +}; + +class Test { + + constructor(bar: string) { } + + constructor(bar: string, baz: string,) { } + + constructor( + bar: string, + baz: string, + ack: string, + ) { } + + constructor( + bar: string, + baz: string, + ack: string, + foo: string, + ) { } + + public foo(bar: string, baz: string) { + return 42; + } + + private foo2(bar: string, baz: string,) { + return 42; + } + + public static foo3( + bar: string, + baz: string, + ) { + return 42; + } + + private static foo4( + bar: string, + baz: string, + ) { + return 42; + } + + foo5< + T extends Test<[A, A], [A, A,], A>, + U extends Test, + V extends Test< + [ + C, + C, + ], + [ + C, + C, + ] + C, + >, + >( + bar: A, + baz: T, + ) { + return 42; + } + + foo6< + T extends { foo: A; bar: B; }, + U extends { foo: A; bar: B; }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string, + ]; + }, + >() { return 42; } + + set bar(foo: string) { } + + set bar2(foo: string,) { } + + set bar3( + foo: string, + ) { } + + set bar4( + foo: string, + ) { } +} + +class Test2 { } + +class Test3< + A, + B, + C, +> { } + +class Test4< + A, + B, + C, +> { } + +interface ITest { + + new (bar: U): ITest; + + new (bar: U, baz: V,): ITest; + + new < + U, + V, + >( + bar: U, + baz: U, + ack: V, + ): ITest< + U, + U, + V, + >; + + new < + U, + V, + >( + bar: U, + baz: U, + ack: V, + foo: V, + ): ITest< + U, + U, + V, + >; + + foo(bar: string, baz: string); + + foo2(bar: string, baz: string,); + + foo3( + bar: string, + baz: string, + ); + + foo4( + bar: string, + baz: string, + ); + + foo5< + T extends Test<[A, A], [A, A,], A>, + U extends Test, + V extends Test< + [ + C, + C, + ], + [ + C, + C, + ] + C, + >, + >( + bar: A, + baz: T, + ); + + foo6< + T extends { foo: A; bar: B; }, + U extends { foo: A; bar: B; }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string, + ]; + }, + >(); +} + +interface ITest2 { } + +interface ITest3< + A, + B, + C, +> { } + +interface ITest4< + A, + B, + C, +> { } + +enum Foo { BAR, BAZ } + +enum Foo { BAR, BAZ, } + +enum Foo { + Bar, + Baz, +} + +enum Foo { + Bar, + Baz, +} + +const enum Foo { BAR = 1, BAZ = 2 } + +const enum Foo { BAR = 1, BAZ = 2, } + +const enum Foo { + Bar = 1, + Baz = 2, +} + +const enum Foo { + Bar = 1, + Baz = 2, +} + diff --git a/test/rules/trailing-comma/multiline-always/test.ts.lint b/test/rules/trailing-comma/multiline-always/test.ts.lint index ce112b7c9a5..64f72d12a5f 100644 --- a/test/rules/trailing-comma/multiline-always/test.ts.lint +++ b/test/rules/trailing-comma/multiline-always/test.ts.lint @@ -113,6 +113,21 @@ var [ccA, ccB,] = cc; var [ddOne, ddTwo] = dd; +var a: [string, number] = null; + +var a: [string, number,] = null; + +var a: [ + string, + number + ~ [Missing trailing comma] +] = null; + +var a: [ + string, + number, +] = null; + import { ClassS, } from "module"; @@ -136,3 +151,434 @@ import { import {ClassA, ClassB,} from "module"; import {ClassC, ClassD} from "module"; + +function foo(bar: string, baz: string); + +function foo(bar: string, baz: string,); + +function foo( + bar: string, + baz: string + ~ [Missing trailing comma] +); + +function foo( + bar: string, + baz: string, +); + +var foo = function(bar: string, baz: string) { return 42; }; + +var foo = function(bar: string, baz: string,) { return 42; }; + +var foo = function( + bar: string, + baz: string + ~ [Missing trailing comma] +) { + return 42; +}; + +var foo = function( + bar: string, + baz: string, +) { + return 42; +}; + +function foo(bar: string, baz: string) { return 42; } + +function foo(bar: string, baz: string,) { return 42; } + +function foo( + bar: string, + baz: string + ~ [Missing trailing comma] +) { + return 42; +} + +function foo( + bar: string, + baz: string, +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string + ~ [Missing trailing comma] +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string, +) { + return 42; +} + +var foo = (bar: string, baz: string) => 42; + +var foo = (bar: string, baz: string,) => 42; + +var foo = ( + bar: string, + baz: string + ~ [Missing trailing comma] +) => 42; + +var foo = ( + bar: string, + baz: string, +) => 42; + +var foo: (bar: string, baz: string) => number = null; + +var foo: (bar: string, baz: string,) => number = null; + +var foo: ( + bar: string, + baz: string + ~ [Missing trailing comma] +) => number = null; + +var foo: ( + bar: string, + baz: string, +) => number = null; + +var foo: (bar: string, baz: string) => number; + +var foo: (bar: string, baz: string,) => number; + +var foo: ( + bar: string, + baz: string + ~ [Missing trailing comma] +) => number; + +var foo: ( + bar: string, + baz: string, +) => number; + +var foo: new (bar: string, baz: string) => Test; + +var foo: new (bar: string, baz: string,) => Test; + +var foo: new ( + bar: string, + baz: string + ~ [Missing trailing comma] +) => Test; + +var foo: new ( + bar: string, + baz: string, +) => Test; + +var x = foo(42, 43); + +var x = foo(42, 43,); + +var x = foo( + 42, + 43 + ~ [Missing trailing comma] +); + +var x = foo( + 42, + 43, +); + +var x = new Test("foo"); + +var x = new Test("foo",); + +var x = new Test( + "foo" + ~ [Missing trailing comma] +); + +var x = new Test( + "foo", +); + +var x: { foo: string; bar: string }; + +var x: { foo: string; bar: string; }; + +var x: { + foo: string; + bar: string +}; + +var x: { + foo: string; + bar: string; +}; + +var x: { foo: string, bar: string }; + +var x: { foo: string, bar: string, }; + +var x: { + foo: string, + bar: string + ~ [Missing trailing comma] +}; + +var x: { + foo: string, + bar: string, +}; + +class Test { + + constructor(bar: string) { } + + constructor(bar: string, baz: string,) { } + + constructor( + bar: string, + baz: string, + ack: string + ~ [Missing trailing comma] + ) { } + + constructor( + bar: string, + baz: string, + ack: string, + foo: string, + ) { } + + public foo(bar: string, baz: string) { + return 42; + } + + private foo2(bar: string, baz: string,) { + return 42; + } + + public static foo3( + bar: string, + baz: string + ~ [Missing trailing comma] + ) { + return 42; + } + + private static foo4( + bar: string, + baz: string, + ) { + return 42; + } + + foo5< + T extends Test<[A, A], [A, A,], A>, + U extends Test, + V extends Test< + [ + C, + C + ~ [Missing trailing comma] + ], + [ + C, + C, + ] + C + ~ [Missing trailing comma] + > + ~ [Missing trailing comma] + >( + bar: A, + baz: T, + ) { + return 42; + } + + foo6< + T extends { foo: A; bar: B; }, + U extends { foo: A; bar: B; }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string, + ]; + }, + >() { return 42; } + + set bar(foo: string) { } + + set bar2(foo: string,) { } + + set bar3( + foo: string + ~ [Missing trailing comma] + ) { } + + set bar4( + foo: string, + ) { } +} + +class Test2 { } + +class Test3< + A, + B, + C + ~ [Missing trailing comma] +> { } + +class Test4< + A, + B, + C, +> { } + +interface ITest { + + new (bar: U): ITest; + + new (bar: U, baz: V,): ITest; + + new < + U, + V + ~ [Missing trailing comma] + >( + bar: U, + baz: U, + ack: V + ~ [Missing trailing comma] + ): ITest< + U, + U, + V + ~ [Missing trailing comma] + >; + + new < + U, + V, + >( + bar: U, + baz: U, + ack: V, + foo: V, + ): ITest< + U, + U, + V, + >; + + foo(bar: string, baz: string); + + foo2(bar: string, baz: string,); + + foo3( + bar: string, + baz: string + ~ [Missing trailing comma] + ); + + foo4( + bar: string, + baz: string, + ); + + foo5< + T extends Test<[A, A], [A, A,], A>, + U extends Test, + V extends Test< + [ + C, + C + ~ [Missing trailing comma] + ], + [ + C, + C, + ] + C + ~ [Missing trailing comma] + > + ~ [Missing trailing comma] + >( + bar: A, + baz: T, + ); + + foo6< + T extends { foo: A; bar: B; }, + U extends { foo: A; bar: B; }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string + ~ [Missing trailing comma] + ]; + } + ~ [Missing trailing comma] + >(); +} + +interface ITest2 { } + +interface ITest3< + A, + B, + C + ~ [Missing trailing comma] +> { } + +interface ITest4< + A, + B, + C, +> { } + +enum Foo { BAR, BAZ } + +enum Foo { BAR, BAZ, } + +enum Foo { + Bar, + Baz + ~ [Missing trailing comma] +} + +enum Foo { + Bar, + Baz, +} + +const enum Foo { BAR = 1, BAZ = 2 } + +const enum Foo { BAR = 1, BAZ = 2, } + +const enum Foo { + Bar = 1, + Baz = 2 + ~ [Missing trailing comma] +} + +const enum Foo { + Bar = 1, + Baz = 2, +} + diff --git a/test/rules/trailing-comma/multiline-never/test.ts.fix b/test/rules/trailing-comma/multiline-never/test.ts.fix new file mode 100644 index 00000000000..4240f80dbc5 --- /dev/null +++ b/test/rules/trailing-comma/multiline-never/test.ts.fix @@ -0,0 +1,498 @@ +var v = [{ + a: 1, + b: 2, + d: 34, + c: (a + b) +},]; + +var x = [{ + a: 1, + b: 2, + d: 34, + c: (a + b) +}]; + +var s = { + sA: 6 +}; + +var t = { + tA: 7 +} + +var y = { + yA: 42, + yB: 24 +}; + +var z = { + zOne: 2, + zTwo: 1 +}; + +var ss = [ + 6 +]; + +var tt = [ + 7 +]; + +var yy = [ + 42, + 24 +]; + +var zz = [ + 2, + 1 +]; + +var a = [{a: 1, b: 2, d: 34, c: (a + b),},]; + +var b = [{a: 1, b: 2, d: 34, c: (a + b)}]; + +var c = {cA: 42, cB: 24,}; + +var d = {dOne: 2, dTwo: 1}; + +var cc = [42, 24,]; + +var dd = [2, 1]; + +var { + sA +} = s; + +var { + tA +} = t; + +var { + yA, + yB +} = y; + +var { + zOne, + zTwo +} = z; + +var [ + ssA +] = ss; + +var [ + ttA +] = tt; + +var [ + yyA, + yyB +] = yy; + +var [ + zzOne, + zzTwo +] = zz; + +var {cA, cB,} = c; + +var {dOne, dTwo} = d; + +var [ccA, ccB,] = cc; + +var [ddOne, ddTwo] = dd; + +var a: [string, number] = null; + +var a: [string, number,] = null; + +var a: [ + string, + number +] = null; + +var a: [ + string, + number +] = null; + +import { + ClassS +} from "module"; + +import { + ClassT +} from "module"; + +import { + ClassV, + ClassX +} from "module"; + +import { + ClassY, + ClassZ +} from "module"; + +import {ClassA, ClassB,} from "module"; + +import {ClassC, ClassD} from "module"; + +function foo(bar: string, baz: string); + +function foo(bar: string, baz: string,); + +function foo( + bar: string, + baz: string +); + +function foo( + bar: string, + baz: string +); + +var foo = function(bar: string, baz: string) { return 42; }; + +var foo = function(bar: string, baz: string,) { return 42; }; + +var foo = function( + bar: string, + baz: string +) { + return 42; +}; + +var foo = function( + bar: string, + baz: string +) { + return 42; +}; + +function foo(bar: string, baz: string) { return 42; } + +function foo(bar: string, baz: string,) { return 42; } + +function foo( + bar: string, + baz: string +) { + return 42; +} + +function foo( + bar: string, + baz: string +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string +) { + return 42; +} + +var foo = (bar: string, baz: string) => 42; + +var foo = (bar: string, baz: string,) => 42; + +var foo = ( + bar: string, + baz: string +) => 42; + +var foo = ( + bar: string, + baz: string +) => 42; + +var foo: (bar: string, baz: string) => number = null; + +var foo: (bar: string, baz: string,) => number = null; + +var foo: ( + bar: string, + baz: string +) => number = null; + +var foo: ( + bar: string, + baz: string +) => number = null; + +var foo: (bar: string, baz: string) => number; + +var foo: (bar: string, baz: string,) => number; + +var foo: ( + bar: string, + baz: string +) => number; + +var foo: ( + bar: string, + baz: string +) => number; + +var foo: new (bar: string, baz: string) => Test; + +var foo: new (bar: string, baz: string,) => Test; + +var foo: new ( + bar: string, + baz: string +) => Test; + +var foo: new ( + bar: string, + baz: string +) => Test; + +var x = foo(42, 43); + +var x = foo(42, 43,); + +var x = foo( + 42, + 43 +); + +var x = foo( + 42, + 43 +); + +var x = new Test("foo"); + +var x = new Test("foo",); + +var x = new Test( + "foo" +); + +var x = new Test( + "foo" +); + +var x: { foo: string; bar: string }; + +var x: { foo: string; bar: string; }; + +var x: { + foo: string; + bar: string +}; + +var x: { + foo: string; + bar: string; +}; + +var x: { foo: string, bar: string }; + +var x: { foo: string, bar: string, }; + +var x: { + foo: string, + bar: string +}; + +var x: { + foo: string, + bar: string +}; + +class Test { + + constructor(bar: string) { } + + constructor(bar: string, baz: string,) { } + + constructor( + bar: string, + baz: string, + ack: string + ) { } + + constructor( + bar: string, + baz: string, + ack: string, + foo: string + ) { } + + public foo(bar: string, baz: string) { + return 42; + } + + private foo2(bar: string, baz: string,) { + return 42; + } + + public static foo3( + bar: string, + baz: string + ) { + return 42; + } + + private static foo4( + bar: string, + baz: string + ) { + return 42; + } + + foo5< + T extends Test<[A, A], [A, A,], A>, + U extends Test, + V extends Test< + [ + C, + C + ], + [ + C, + C + ] + C + > + >( + bar: A, + baz: T + ) { + return 42; + } + + foo6< + T extends { foo: A; bar: B; }, + U extends { foo: A; bar: B; }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string + ]; + } + >() { return 42; } +} + +class Test2 { } + +class Test3< + A, + B, + C +> { } + +class Test4< + A, + B, + C +> { } + +interface ITest { + foo(bar: string, baz: string); + + foo2(bar: string, baz: string,); + + foo3( + bar: string, + baz: string + ); + + foo4( + bar: string, + baz: string + ); + + foo5< + T extends Test<[A, A], [A, A,], A>, + U extends Test, + V extends Test< + [ + C, + C + ], + [ + C, + C + ] + C + > + >( + bar: A, + baz: T + ); + + foo6< + T extends { foo: A; bar: B; }, + U extends { foo: A; bar: B; }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string + ]; + } + >(); +} + +interface ITest2 { } + +interface ITest3< + A, + B, + C +> { } + +interface ITest4< + A, + B, + C +> { } + +enum Foo { BAR, BAZ } + +enum Foo { BAR, BAZ, } + +enum Foo { + Bar, + Baz +} + +enum Foo { + Bar, + Baz +} + +const enum Foo { BAR = 1, BAZ = 2 } + +const enum Foo { BAR = 1, BAZ = 2, } + +const enum Foo { + Bar = 1, + Baz = 2 +} + +const enum Foo { + Bar = 1, + Baz = 2 +} + diff --git a/test/rules/trailing-comma/multiline-never/test.ts.lint b/test/rules/trailing-comma/multiline-never/test.ts.lint index 594b93924f9..359d6063b36 100644 --- a/test/rules/trailing-comma/multiline-never/test.ts.lint +++ b/test/rules/trailing-comma/multiline-never/test.ts.lint @@ -113,6 +113,21 @@ var [ccA, ccB,] = cc; var [ddOne, ddTwo] = dd; +var a: [string, number] = null; + +var a: [string, number,] = null; + +var a: [ + string, + number +] = null; + +var a: [ + string, + number, + ~ [Unnecessary trailing comma] +] = null; + import { ClassS, ~ [Unnecessary trailing comma] @@ -136,3 +151,384 @@ import { import {ClassA, ClassB,} from "module"; import {ClassC, ClassD} from "module"; + +function foo(bar: string, baz: string); + +function foo(bar: string, baz: string,); + +function foo( + bar: string, + baz: string +); + +function foo( + bar: string, + baz: string, + ~ [Unnecessary trailing comma] +); + +var foo = function(bar: string, baz: string) { return 42; }; + +var foo = function(bar: string, baz: string,) { return 42; }; + +var foo = function( + bar: string, + baz: string +) { + return 42; +}; + +var foo = function( + bar: string, + baz: string, + ~ [Unnecessary trailing comma] +) { + return 42; +}; + +function foo(bar: string, baz: string) { return 42; } + +function foo(bar: string, baz: string,) { return 42; } + +function foo( + bar: string, + baz: string +) { + return 42; +} + +function foo( + bar: string, + baz: string, + ~ [Unnecessary trailing comma] +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string, + ~ [Unnecessary trailing comma] +) { + return 42; +} + +var foo = (bar: string, baz: string) => 42; + +var foo = (bar: string, baz: string,) => 42; + +var foo = ( + bar: string, + baz: string +) => 42; + +var foo = ( + bar: string, + baz: string, + ~ [Unnecessary trailing comma] +) => 42; + +var foo: (bar: string, baz: string) => number = null; + +var foo: (bar: string, baz: string,) => number = null; + +var foo: ( + bar: string, + baz: string +) => number = null; + +var foo: ( + bar: string, + baz: string, + ~ [Unnecessary trailing comma] +) => number = null; + +var foo: (bar: string, baz: string) => number; + +var foo: (bar: string, baz: string,) => number; + +var foo: ( + bar: string, + baz: string +) => number; + +var foo: ( + bar: string, + baz: string, + ~ [Unnecessary trailing comma] +) => number; + +var foo: new (bar: string, baz: string) => Test; + +var foo: new (bar: string, baz: string,) => Test; + +var foo: new ( + bar: string, + baz: string +) => Test; + +var foo: new ( + bar: string, + baz: string, + ~ [Unnecessary trailing comma] +) => Test; + +var x = foo(42, 43); + +var x = foo(42, 43,); + +var x = foo( + 42, + 43 +); + +var x = foo( + 42, + 43, + ~ [Unnecessary trailing comma] +); + +var x = new Test("foo"); + +var x = new Test("foo",); + +var x = new Test( + "foo" +); + +var x = new Test( + "foo", + ~ [Unnecessary trailing comma] +); + +var x: { foo: string; bar: string }; + +var x: { foo: string; bar: string; }; + +var x: { + foo: string; + bar: string +}; + +var x: { + foo: string; + bar: string; +}; + +var x: { foo: string, bar: string }; + +var x: { foo: string, bar: string, }; + +var x: { + foo: string, + bar: string +}; + +var x: { + foo: string, + bar: string, + ~ [Unnecessary trailing comma] +}; + +class Test { + + constructor(bar: string) { } + + constructor(bar: string, baz: string,) { } + + constructor( + bar: string, + baz: string, + ack: string + ) { } + + constructor( + bar: string, + baz: string, + ack: string, + foo: string, + ~ [Unnecessary trailing comma] + ) { } + + public foo(bar: string, baz: string) { + return 42; + } + + private foo2(bar: string, baz: string,) { + return 42; + } + + public static foo3( + bar: string, + baz: string + ) { + return 42; + } + + private static foo4( + bar: string, + baz: string, + ~ [Unnecessary trailing comma] + ) { + return 42; + } + + foo5< + T extends Test<[A, A], [A, A,], A>, + U extends Test, + V extends Test< + [ + C, + C + ], + [ + C, + C, + ~ [Unnecessary trailing comma] + ] + C + > + >( + bar: A, + baz: T, + ~ [Unnecessary trailing comma] + ) { + return 42; + } + + foo6< + T extends { foo: A; bar: B; }, + U extends { foo: A; bar: B; }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string, + ~ [Unnecessary trailing comma] + ]; + }, + ~ [Unnecessary trailing comma] + >() { return 42; } +} + +class Test2 { } + +class Test3< + A, + B, + C +> { } + +class Test4< + A, + B, + C, + ~ [Unnecessary trailing comma] +> { } + +interface ITest { + foo(bar: string, baz: string); + + foo2(bar: string, baz: string,); + + foo3( + bar: string, + baz: string + ); + + foo4( + bar: string, + baz: string, + ~ [Unnecessary trailing comma] + ); + + foo5< + T extends Test<[A, A], [A, A,], A>, + U extends Test, + V extends Test< + [ + C, + C + ], + [ + C, + C, + ~ [Unnecessary trailing comma] + ] + C + > + >( + bar: A, + baz: T, + ~ [Unnecessary trailing comma] + ); + + foo6< + T extends { foo: A; bar: B; }, + U extends { foo: A; bar: B; }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string + ]; + } + >(); +} + +interface ITest2 { } + +interface ITest3< + A, + B, + C +> { } + +interface ITest4< + A, + B, + C, + ~ [Unnecessary trailing comma] +> { } + +enum Foo { BAR, BAZ } + +enum Foo { BAR, BAZ, } + +enum Foo { + Bar, + Baz +} + +enum Foo { + Bar, + Baz, + ~ [Unnecessary trailing comma] +} + +const enum Foo { BAR = 1, BAZ = 2 } + +const enum Foo { BAR = 1, BAZ = 2, } + +const enum Foo { + Bar = 1, + Baz = 2 +} + +const enum Foo { + Bar = 1, + Baz = 2, + ~ [Unnecessary trailing comma] +} + diff --git a/test/rules/trailing-comma/singleline-always/test.ts.fix b/test/rules/trailing-comma/singleline-always/test.ts.fix new file mode 100644 index 00000000000..77c0559df1d --- /dev/null +++ b/test/rules/trailing-comma/singleline-always/test.ts.fix @@ -0,0 +1,540 @@ +var v = [{ + a: 1, + b: 2, + d: 34, + c: (a + b), +},]; + +var x = [{ + a: 1, + b: 2, + d: 34, + c: (a + b) +},]; + +var s = { + sA: 6, +}; + +var t = { + tA: 7 +} + +var y = { + yA: 42, + yB: 24, +}; + +var z = { + zOne: 2, + zTwo: 1 +}; + +var ss = [ + 6, +]; + +var tt = [ + 7 +]; + +var yy = [ + 42, + 24, +]; + +var zz = [ + 2, + 1 +]; + +var a = [{a: 1, b: 2, d: 34, c: (a + b),},]; + +var b = [{a: 1, b: 2, d: 34, c: (a + b),},]; + +var c = {cA: 42, cB: 24,}; + +var d = {dOne: 2, dTwo: 1,}; + +var cc = [42, 24,]; + +var dd = [2, 1,]; + +var { + sA, +} = s; + +var { + tA +} = t; + +var { + yA, + yB, +} = y; + +var { + zOne, + zTwo +} = z; + +var [ + ssA, +] = ss; + +var [ + ttA +] = tt; + +var [ + yyA, + yyB, +] = yy; + +var [ + zzOne, + zzTwo +] = zz; + +var {cA, cB,} = c; + +var {dOne, dTwo,} = d; + +var [ccA, ccB,] = cc; + +var [ddOne, ddTwo,] = dd; + +var a: [string, number,] = null; + +var a: [string, number,] = null; + +var a: [ + string, + number +] = null; + +var a: [ + string, + number, +] = null; + +import { + ClassS, +} from "module"; + +import { + ClassT +} from "module"; + +import { + ClassV, + ClassX, +} from "module"; + +import { + ClassY, + ClassZ +} from "module"; + +import {ClassA, ClassB,} from "module"; + +import {ClassC, ClassD,} from "module"; + +function foo(bar: string, baz: string,); + +function foo(bar: string, baz: string,); + +function foo( + bar: string, + baz: string +); + +function foo( + bar: string, + baz: string, +); + +var foo = function(bar: string, baz: string,) { return 42; }; + +var foo = function(bar: string, baz: string,) { return 42; }; + +var foo = function( + bar: string, + baz: string +) { + return 42; +}; + +var foo = function( + bar: string, + baz: string, +) { + return 42; +}; + +function foo(bar: string, baz: string,) { return 42; } + +function foo(bar: string, baz: string,) { return 42; } + +function foo( + bar: string, + baz: string +) { + return 42; +} + +function foo( + bar: string, + baz: string, +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string, +) { + return 42; +} + +var foo = (bar: string, baz: string,) => 42; + +var foo = (bar: string, baz: string,) => 42; + +var foo = ( + bar: string, + baz: string +) => 42; + +var foo = ( + bar: string, + baz: string, +) => 42; + +var foo: (bar: string, baz: string,) => number = null; + +var foo: (bar: string, baz: string,) => number = null; + +var foo: ( + bar: string, + baz: string +) => number = null; + +var foo: ( + bar: string, + baz: string, +) => number = null; + +var foo: (bar: string, baz: string,) => number; + +var foo: (bar: string, baz: string,) => number; + +var foo: ( + bar: string, + baz: string +) => number; + +var foo: ( + bar: string, + baz: string, +) => number; + +var foo: new (bar: string, baz: string,) => Test; + +var foo: new (bar: string, baz: string,) => Test; + +var foo: new ( + bar: string, + baz: string +) => Test; + +var foo: new ( + bar: string, + baz: string, +) => Test; + +var x = foo(42, 43,); + +var x = foo(42, 43,); + +var x = foo( + 42, + 43 +); + +var x = foo( + 42, + 43, +); + +var x = new Test("foo",); + +var x = new Test("foo",); + +var x = new Test( + "foo" +); + +var x = new Test( + "foo", +); + +var x: { foo: string; bar: string }; + +var x: { foo: string; bar: string; }; + +var x: { + foo: string; + bar: string +}; + +var x: { + foo: string; + bar: string; +}; + +var x: { foo: string, bar: string, }; + +var x: { foo: string, bar: string, }; + +var x: { + foo: string, + bar: string +}; + +var x: { + foo: string, + bar: string, +}; + +class Test { + + constructor(bar: string,) { } + + constructor(bar: string, baz: string,) { } + + constructor( + bar: string, + baz: string, + ack: string + ) { } + + constructor( + bar: string, + baz: string, + ack: string, + foo: string, + ) { } + + public foo(bar: string, baz: string,) { + return 42; + } + + private foo2(bar: string, baz: string,) { + return 42; + } + + public static foo3( + bar: string, + baz: string + ) { + return 42; + } + + private static foo4( + bar: string, + baz: string, + ) { + return 42; + } + + foo5< + T extends Test<[A, A,], [A, A,], A,>, + U extends Test, + V extends Test< + [ + C, + C + ], + [ + C, + C, + ] + C + > + >( + bar: A, + baz: T, + ) { + return 42; + } + + foo6< + T extends { foo: A; bar: B; }, + U extends { foo: A; bar: B; }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string, + ]; + }, + >() { return 42; } + + set bar(foo: string,) { } + + set bar2(foo: string,) { } + + set bar3( + foo: string + ) { } + + set bar4( + foo: string, + ) { } +} + +class Test2 { } + +class Test3< + A, + B, + C +> { } + +class Test4< + A, + B, + C, +> { } + +interface ITest { + + new (bar: U,): ITest; + + new < + U, + V + >( + bar: U, + baz: U, + ack: V + ): ITest< + U, + U, + V + >; + + new < + U, + V, + >( + bar: U, + baz: U, + ack: V, + foo: V, + ): ITest< + U, + U, + V, + >; + + foo(bar: string, baz: string,); + + foo2(bar: string, baz: string,); + + foo3( + bar: string, + baz: string + ); + + foo4( + bar: string, + baz: string, + ); + + foo5< + T extends Test<[A, A,], [A, A,], A,>, + U extends Test, + V extends Test< + [ + C, + C + ], + [ + C, + C, + ] + C + > + >( + bar: A, + baz: T, + ); + + foo6< + T extends { foo: A; bar: B; }, + U extends { foo: A; bar: B; }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string + ]; + } + >(); +} + +interface ITest2 { } + +interface ITest3< + A, + B, + C +> { } + +interface ITest4< + A, + B, + C, +> { } + +enum Foo { BAR, BAZ, } + +enum Foo { BAR, BAZ, } + +enum Foo { + Bar, + Baz +} + +enum Foo { + Bar, + Baz, +} + +const enum Foo { BAR = 1, BAZ = 2, } + +const enum Foo { BAR = 1, BAZ = 2, } + +const enum Foo { + Bar = 1, + Baz = 2 +} + +const enum Foo { + Bar = 1, + Baz = 2, +} + diff --git a/test/rules/trailing-comma/singleline-always/test.ts.lint b/test/rules/trailing-comma/singleline-always/test.ts.lint index aba942571dc..dba8dc83e84 100644 --- a/test/rules/trailing-comma/singleline-always/test.ts.lint +++ b/test/rules/trailing-comma/singleline-always/test.ts.lint @@ -111,6 +111,21 @@ var [ccA, ccB,] = cc; var [ddOne, ddTwo] = dd; ~ [Missing trailing comma] +var a: [string, number] = null; + ~ [Missing trailing comma] + +var a: [string, number,] = null; + +var a: [ + string, + number +] = null; + +var a: [ + string, + number, +] = null; + import { ClassS, } from "module"; @@ -133,3 +148,428 @@ import {ClassA, ClassB,} from "module"; import {ClassC, ClassD} from "module"; ~ [Missing trailing comma] + +function foo(bar: string, baz: string); + ~ [Missing trailing comma] + +function foo(bar: string, baz: string,); + +function foo( + bar: string, + baz: string +); + +function foo( + bar: string, + baz: string, +); + +var foo = function(bar: string, baz: string) { return 42; }; + ~ [Missing trailing comma] + +var foo = function(bar: string, baz: string,) { return 42; }; + +var foo = function( + bar: string, + baz: string +) { + return 42; +}; + +var foo = function( + bar: string, + baz: string, +) { + return 42; +}; + +function foo(bar: string, baz: string) { return 42; } + ~ [Missing trailing comma] + +function foo(bar: string, baz: string,) { return 42; } + +function foo( + bar: string, + baz: string +) { + return 42; +} + +function foo( + bar: string, + baz: string, +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string, +) { + return 42; +} + +var foo = (bar: string, baz: string) => 42; + ~ [Missing trailing comma] + +var foo = (bar: string, baz: string,) => 42; + +var foo = ( + bar: string, + baz: string +) => 42; + +var foo = ( + bar: string, + baz: string, +) => 42; + +var foo: (bar: string, baz: string) => number = null; + ~ [Missing trailing comma] + +var foo: (bar: string, baz: string,) => number = null; + +var foo: ( + bar: string, + baz: string +) => number = null; + +var foo: ( + bar: string, + baz: string, +) => number = null; + +var foo: (bar: string, baz: string) => number; + ~ [Missing trailing comma] + +var foo: (bar: string, baz: string,) => number; + +var foo: ( + bar: string, + baz: string +) => number; + +var foo: ( + bar: string, + baz: string, +) => number; + +var foo: new (bar: string, baz: string) => Test; + ~ [Missing trailing comma] + +var foo: new (bar: string, baz: string,) => Test; + +var foo: new ( + bar: string, + baz: string +) => Test; + +var foo: new ( + bar: string, + baz: string, +) => Test; + +var x = foo(42, 43); + ~ [Missing trailing comma] + +var x = foo(42, 43,); + +var x = foo( + 42, + 43 +); + +var x = foo( + 42, + 43, +); + +var x = new Test("foo"); + ~ [Missing trailing comma] + +var x = new Test("foo",); + +var x = new Test( + "foo" +); + +var x = new Test( + "foo", +); + +var x: { foo: string; bar: string }; + +var x: { foo: string; bar: string; }; + +var x: { + foo: string; + bar: string +}; + +var x: { + foo: string; + bar: string; +}; + +var x: { foo: string, bar: string }; + ~ [Missing trailing comma] + +var x: { foo: string, bar: string, }; + +var x: { + foo: string, + bar: string +}; + +var x: { + foo: string, + bar: string, +}; + +class Test { + ~ [Missing trailing comma] + + constructor(bar: string) { } + ~ [Missing trailing comma] + + constructor(bar: string, baz: string,) { } + + constructor( + bar: string, + baz: string, + ack: string + ) { } + + constructor( + bar: string, + baz: string, + ack: string, + foo: string, + ) { } + + public foo(bar: string, baz: string) { + ~ [Missing trailing comma] + return 42; + } + + private foo2(bar: string, baz: string,) { + return 42; + } + + public static foo3( + bar: string, + baz: string + ) { + return 42; + } + + private static foo4( + bar: string, + baz: string, + ) { + return 42; + } + + foo5< + T extends Test<[A, A], [A, A,], A>, + ~ [Missing trailing comma] + ~ [Missing trailing comma] + U extends Test, + V extends Test< + [ + C, + C + ], + [ + C, + C, + ] + C + > + >( + bar: A, + baz: T, + ) { + return 42; + } + + foo6< + T extends { foo: A; bar: B; }, + U extends { foo: A; bar: B; }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string, + ]; + }, + >() { return 42; } + + set bar(foo: string) { } + ~ [Missing trailing comma] + + set bar2(foo: string,) { } + + set bar3( + foo: string + ) { } + + set bar4( + foo: string, + ) { } +} + +class Test2 { } + +class Test3< + A, + B, + C +> { } + +class Test4< + A, + B, + C, +> { } + +interface ITest { + ~ [Missing trailing comma] + + new (bar: U): ITest; + ~ [Missing trailing comma] + ~ [Missing trailing comma] + ~ [Missing trailing comma] + + new < + U, + V + >( + bar: U, + baz: U, + ack: V + ): ITest< + U, + U, + V + >; + + new < + U, + V, + >( + bar: U, + baz: U, + ack: V, + foo: V, + ): ITest< + U, + U, + V, + >; + + foo(bar: string, baz: string); + ~ [Missing trailing comma] + + foo2(bar: string, baz: string,); + + foo3( + bar: string, + baz: string + ); + + foo4( + bar: string, + baz: string, + ); + + foo5< + T extends Test<[A, A], [A, A,], A>, + ~ [Missing trailing comma] + ~ [Missing trailing comma] + U extends Test, + V extends Test< + [ + C, + C + ], + [ + C, + C, + ] + C + > + >( + bar: A, + baz: T, + ); + + foo6< + T extends { foo: A; bar: B; }, + U extends { foo: A; bar: B; }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string + ]; + } + >(); +} + +interface ITest2 { } + ~ [Missing trailing comma] + +interface ITest3< + A, + B, + C +> { } + +interface ITest4< + A, + B, + C, +> { } + +enum Foo { BAR, BAZ } + ~ [Missing trailing comma] + +enum Foo { BAR, BAZ, } + +enum Foo { + Bar, + Baz +} + +enum Foo { + Bar, + Baz, +} + +const enum Foo { BAR = 1, BAZ = 2 } + ~ [Missing trailing comma] + +const enum Foo { BAR = 1, BAZ = 2, } + +const enum Foo { + Bar = 1, + Baz = 2 +} + +const enum Foo { + Bar = 1, + Baz = 2, +} + diff --git a/test/rules/trailing-comma/singleline-never/test.ts.fix b/test/rules/trailing-comma/singleline-never/test.ts.fix new file mode 100644 index 00000000000..a4420614d63 --- /dev/null +++ b/test/rules/trailing-comma/singleline-never/test.ts.fix @@ -0,0 +1,500 @@ +var v = [{ + a: 1, + b: 2, + d: 34, + c: (a + b), +}]; + +var x = [{ + a: 1, + b: 2, + d: 34, + c: (a + b) +}]; + +var s = { + sA: 6, +}; + +var t = { + tA: 7 +} + +var y = { + yA: 42, + yB: 24, +}; + +var z = { + zOne: 2, + zTwo: 1 +}; + +var ss = [ + 6, +]; + +var tt = [ + 7 +]; + +var yy = [ + 42, + 24, +]; + +var zz = [ + 2, + 1 +]; + +var a = [{a: 1, b: 2, d: 34, c: (a + b)}]; + +var b = [{a: 1, b: 2, d: 34, c: (a + b)}]; + +var c = {cA: 42, cB: 24}; + +var d = {dOne: 2, dTwo: 1}; + +var cc = [42, 24]; + +var dd = [2, 1]; + +var { + sA, +} = s; + +var { + tA +} = t; + +var { + yA, + yB, +} = y; + +var { + zOne, + zTwo +} = z; + +var [ + ssA, +] = ss; + +var [ + ttA +] = tt; + +var [ + yyA, + yyB, +] = yy; + +var [ + zzOne, + zzTwo +] = zz; + +var {cA, cB} = c; + +var {dOne, dTwo} = d; + +var [ccA, ccB] = cc; + +var [ddOne, ddTwo] = dd; + +var a: [string, number] = null; + +var a: [string, number] = null; + +var a: [ + string, + number +] = null; + +var a: [ + string, + number, +] = null; + + +import { + ClassS, +} from "module"; + +import { + ClassT +} from "module"; + +import { + ClassV, + ClassX, +} from "module"; + +import { + ClassY, + ClassZ +} from "module"; + +import {ClassA, ClassB} from "module"; + +import {ClassC, ClassD} from "module"; + +function foo(bar: string, baz: string); + +function foo(bar: string, baz: string); + +function foo( + bar: string, + baz: string +); + +function foo( + bar: string, + baz: string, +); + +var foo = function(bar: string, baz: string) { return 42; }; + +var foo = function(bar: string, baz: string) { return 42; }; + +var foo = function( + bar: string, + baz: string +) { + return 42; +}; + +var foo = function( + bar: string, + baz: string, +) { + return 42; +}; + +function foo(bar: string, baz: string) { return 42; } + +function foo(bar: string, baz: string) { return 42; } + +function foo( + bar: string, + baz: string +) { + return 42; +} + +function foo( + bar: string, + baz: string, +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string, +) { + return 42; +} + +var foo = (bar: string, baz: string) => 42; + +var foo = (bar: string, baz: string) => 42; + +var foo = ( + bar: string, + baz: string +) => 42; + +var foo = ( + bar: string, + baz: string, +) => 42; + +var foo: (bar: string, baz: string) => number = null; + +var foo: (bar: string, baz: string) => number = null; + +var foo: ( + bar: string, + baz: string +) => number = null; + +var foo: ( + bar: string, + baz: string, +) => number = null; + +var foo: (bar: string, baz: string) => number; + +var foo: (bar: string, baz: string) => number; + +var foo: ( + bar: string, + baz: string +) => number; + +var foo: ( + bar: string, + baz: string, +) => number; + +var foo: new (bar: string, baz: string) => Test; + +var foo: new (bar: string, baz: string) => Test; + +var foo: new ( + bar: string, + baz: string +) => Test; + +var foo: new ( + bar: string, + baz: string, +) => Test; + +var x = foo(42, 43); + +var x = foo(42, 43); + +var x = foo( + 42, + 43 +); + +var x = foo( + 42, + 43, +); + +var x = new Test("foo"); + +var x = new Test("foo"); + +var x = new Test( + "foo" +); + +var x = new Test( + "foo", +); + +var x: { foo: string; bar: string }; + +var x: { foo: string; bar: string; }; + +var x: { + foo: string; + bar: string +}; + +var x: { + foo: string; + bar: string; +}; + +var x: { foo: string, bar: string }; + +var x: { foo: string, bar: string }; + +var x: { + foo: string, + bar: string +}; + +var x: { + foo: string, + bar: string, +}; + +class Test { + + constructor(bar: string) { } + + constructor(bar: string, baz: string) { } + + constructor( + bar: string, + baz: string, + ack: string + ) { } + + constructor( + bar: string, + baz: string, + ack: string, + foo: string, + ) { } + + public foo(bar: string, baz: string) { + return 42; + } + + private foo2(bar: string, baz: string) { + return 42; + } + + public static foo3( + bar: string, + baz: string + ) { + return 42; + } + + private static foo4( + bar: string, + baz: string, + ) { + return 42; + } + + foo5< + T extends Test<[A, A], [A, A], A>, + U extends Test, + V extends Test< + [ + C, + C + ], + [ + C, + C, + ] + C + > + >( + bar: A, + baz: T, + ) { + return 42; + } + + foo6< + T extends { foo: A, bar: B }, + U extends { foo: A, bar: B }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string, + ]; + }, + >() { return 42; } +} + +class Test2 { } + + +class Test3< + A, + B, + C +> { } + +class Test4< + A, + B, + C, +> { } + +interface ITest { + foo(bar: string, baz: string); + + foo2(bar: string, baz: string); + + foo3( + bar: string, + baz: string + ); + + foo4( + bar: string, + baz: string, + ); + + foo5< + T extends Test<[A, A], [A, A], A>, + U extends Test, + V extends Test< + [ + C, + C + ], + [ + C, + C, + ] + C + > + >( + bar: A, + baz: T, + ); + + foo6< + T extends { foo: A, bar: B }, + U extends { foo: A, bar: B }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string + ]; + } + >(); +} + +interface ITest2 { } + +interface ITest3< + A, + B, + C +> { } + +interface ITest4< + A, + B, + C, +> { } + +enum Foo { BAR, BAZ } + +enum Foo { BAR, BAZ } + +enum Foo { + Bar, + Baz +} + +enum Foo { + Bar, + Baz, +} + +const enum Foo { BAR = 1, BAZ = 2 } + +const enum Foo { BAR = 1, BAZ = 2 } + +const enum Foo { + Bar = 1, + Baz = 2 +} + +const enum Foo { + Bar = 1, + Baz = 2, +} + diff --git a/test/rules/trailing-comma/singleline-never/test.ts.lint b/test/rules/trailing-comma/singleline-never/test.ts.lint index 0ecc929bb2f..4c42d04a8d3 100644 --- a/test/rules/trailing-comma/singleline-never/test.ts.lint +++ b/test/rules/trailing-comma/singleline-never/test.ts.lint @@ -111,6 +111,22 @@ var [ccA, ccB,] = cc; var [ddOne, ddTwo] = dd; +var a: [string, number] = null; + +var a: [string, number,] = null; + ~ [Unnecessary trailing comma] + +var a: [ + string, + number +] = null; + +var a: [ + string, + number, +] = null; + + import { ClassS, } from "module"; @@ -133,3 +149,381 @@ import {ClassA, ClassB,} from "module"; ~ [Unnecessary trailing comma] import {ClassC, ClassD} from "module"; + +function foo(bar: string, baz: string); + +function foo(bar: string, baz: string,); + ~ [Unnecessary trailing comma] + +function foo( + bar: string, + baz: string +); + +function foo( + bar: string, + baz: string, +); + +var foo = function(bar: string, baz: string) { return 42; }; + +var foo = function(bar: string, baz: string,) { return 42; }; + ~ [Unnecessary trailing comma] + +var foo = function( + bar: string, + baz: string +) { + return 42; +}; + +var foo = function( + bar: string, + baz: string, +) { + return 42; +}; + +function foo(bar: string, baz: string) { return 42; } + +function foo(bar: string, baz: string,) { return 42; } + ~ [Unnecessary trailing comma] + +function foo( + bar: string, + baz: string +) { + return 42; +} + +function foo( + bar: string, + baz: string, +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string +) { + return 42; +} + +function foo(bar: string, baz: string, + ack: string, +) { + return 42; +} + +var foo = (bar: string, baz: string) => 42; + +var foo = (bar: string, baz: string,) => 42; + ~ [Unnecessary trailing comma] + +var foo = ( + bar: string, + baz: string +) => 42; + +var foo = ( + bar: string, + baz: string, +) => 42; + +var foo: (bar: string, baz: string) => number = null; + +var foo: (bar: string, baz: string,) => number = null; + ~ [Unnecessary trailing comma] + +var foo: ( + bar: string, + baz: string +) => number = null; + +var foo: ( + bar: string, + baz: string, +) => number = null; + +var foo: (bar: string, baz: string) => number; + +var foo: (bar: string, baz: string,) => number; + ~ [Unnecessary trailing comma] + +var foo: ( + bar: string, + baz: string +) => number; + +var foo: ( + bar: string, + baz: string, +) => number; + +var foo: new (bar: string, baz: string) => Test; + +var foo: new (bar: string, baz: string,) => Test; + ~ [Unnecessary trailing comma] + +var foo: new ( + bar: string, + baz: string +) => Test; + +var foo: new ( + bar: string, + baz: string, +) => Test; + +var x = foo(42, 43); + +var x = foo(42, 43,); + ~ [Unnecessary trailing comma] + +var x = foo( + 42, + 43 +); + +var x = foo( + 42, + 43, +); + +var x = new Test("foo"); + +var x = new Test("foo",); + ~ [Unnecessary trailing comma] + +var x = new Test( + "foo" +); + +var x = new Test( + "foo", +); + +var x: { foo: string; bar: string }; + +var x: { foo: string; bar: string; }; + +var x: { + foo: string; + bar: string +}; + +var x: { + foo: string; + bar: string; +}; + +var x: { foo: string, bar: string }; + +var x: { foo: string, bar: string, }; + ~ [Unnecessary trailing comma] + +var x: { + foo: string, + bar: string +}; + +var x: { + foo: string, + bar: string, +}; + +class Test { + + constructor(bar: string) { } + + constructor(bar: string, baz: string,) { } + ~ [Unnecessary trailing comma] + + constructor( + bar: string, + baz: string, + ack: string + ) { } + + constructor( + bar: string, + baz: string, + ack: string, + foo: string, + ) { } + + public foo(bar: string, baz: string) { + return 42; + } + + private foo2(bar: string, baz: string,) { + ~ [Unnecessary trailing comma] + return 42; + } + + public static foo3( + bar: string, + baz: string + ) { + return 42; + } + + private static foo4( + bar: string, + baz: string, + ) { + return 42; + } + + foo5< + T extends Test<[A, A], [A, A,], A>, + ~ [Unnecessary trailing comma] + U extends Test, + ~ [Unnecessary trailing comma] + V extends Test< + [ + C, + C + ], + [ + C, + C, + ] + C + > + >( + bar: A, + baz: T, + ) { + return 42; + } + + foo6< + T extends { foo: A, bar: B }, + U extends { foo: A, bar: B }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string, + ]; + }, + >() { return 42; } +} + +class Test2 { } + ~ [Unnecessary trailing comma] + + +class Test3< + A, + B, + C +> { } + +class Test4< + A, + B, + C, +> { } + +interface ITest { + foo(bar: string, baz: string); + + foo2(bar: string, baz: string,); + ~ [Unnecessary trailing comma] + + foo3( + bar: string, + baz: string + ); + + foo4( + bar: string, + baz: string, + ); + + foo5< + T extends Test<[A, A], [A, A,], A>, + ~ [Unnecessary trailing comma] + U extends Test, + ~ [Unnecessary trailing comma] + V extends Test< + [ + C, + C + ], + [ + C, + C, + ] + C + > + >( + bar: A, + baz: T, + ); + + foo6< + T extends { foo: A, bar: B }, + U extends { foo: A, bar: B }, + V extends { + foo: A; + bar: B; + }, + W extends { + foo: [ + string, + string + ]; + } + >(); +} + +interface ITest2 { } + +interface ITest3< + A, + B, + C +> { } + +interface ITest4< + A, + B, + C, +> { } + +enum Foo { BAR, BAZ } + +enum Foo { BAR, BAZ, } + ~ [Unnecessary trailing comma] + +enum Foo { + Bar, + Baz +} + +enum Foo { + Bar, + Baz, +} + +const enum Foo { BAR = 1, BAZ = 2 } + +const enum Foo { BAR = 1, BAZ = 2, } + ~ [Unnecessary trailing comma] + +const enum Foo { + Bar = 1, + Baz = 2 +} + +const enum Foo { + Bar = 1, + Baz = 2, +} + diff --git a/test/rules/typedef-whitespace/both/nospace/test.ts.lint b/test/rules/typedef-whitespace/both/nospace/test.ts.lint index 894547e1e0c..bc354a2de99 100644 --- a/test/rules/typedef-whitespace/both/nospace/test.ts.lint +++ b/test/rules/typedef-whitespace/both/nospace/test.ts.lint @@ -84,6 +84,16 @@ var withPreceedingSpacesWithSubsequentSpacesObject = { } }; +var noPreceedingSpaceWithSubsequentNewLineObject = { + _Prop: + "some property", + + get Prop(): + string { + return this._Prop; + } +}; + // endregion // region Interface @@ -136,6 +146,11 @@ interface WithPreceedingSpacesWithSubsequentSpacesInterface { ~ [expected nospace after colon in property-declaration] } +interface NoPreceedingSpaceNoSubsequentSpaceAndNewLineInterface { + Prop: + string; +} + // endregion // region Function @@ -311,6 +326,23 @@ function withPreceedingSpacesWithSubsequentSpacesFn(a : number, b : numb } } +function noPreceedingSpaceNoSubsequentSpaceAndNewLineFn(a: + number, b: + number): + number { + var c: + number = a + b; + var d: + number = a - b; + + try { + return c / d; + } catch (ex: + Exception) { + console.log(ex); + } +} + // endregion // region Function multi-line signature @@ -547,6 +579,9 @@ function withPreceedingSpacesWithSubsequentSpacesSignature( ) : {}; ~ [expected nospace before colon in call-signature] ~ [expected nospace after colon in call-signature] +function noPreceedingSpaceNoSubsequentSpaceAndNewLineSignature( ): + {}; + // endregion // region Class @@ -812,4 +847,29 @@ class WithPreceedingSpacesWithSubsequentSpacesClass { ~ [expected nospace after colon in call-signature] } +class NoPreceedingSpaceNoSubsequentSpaceAndNewLineClass { + [index:number]: + string + + Prop: + string = "some property"; + + public get name(): + string { + return "some name"; + } + + public set name(a: + string): + void {} + + public shemp( + a: + number, + b: + number + ): + void {} +} + // endregion diff --git a/test/rules/typedef-whitespace/both/nospace/tslint.json b/test/rules/typedef-whitespace/both/nospace/tslint.json index 7dafa80c60d..459b9de0890 100644 --- a/test/rules/typedef-whitespace/both/nospace/tslint.json +++ b/test/rules/typedef-whitespace/both/nospace/tslint.json @@ -1,5 +1,6 @@ { "rules": { + "no-trailing-whitespace": true, "typedef-whitespace": [true, { "call-signature": "nospace", "index-signature": "nospace", diff --git a/test/rules/typedef-whitespace/both/onespace/test.ts.lint b/test/rules/typedef-whitespace/both/onespace/test.ts.lint index dfb5ea46e93..0aa554e5d73 100644 --- a/test/rules/typedef-whitespace/both/onespace/test.ts.lint +++ b/test/rules/typedef-whitespace/both/onespace/test.ts.lint @@ -84,6 +84,28 @@ var withPreceedingSpacesWithSubsequentSpacesObject = { } }; +var withPreceedingSpaceWithSubsequentNewLineObject = { + _Prop : + "some property", + + get Prop() : + string { + return this._Prop; + } +}; + +var noPreceedingSpaceWithSubsequentSpaceAndNewLineObject = { + _Prop : + ~ [trailing whitespace] + "some property", + + get Prop() : + ~ [trailing whitespace] + string { + return this._Prop; + } +}; + // endregion // region Interface @@ -136,6 +158,17 @@ interface WithPreceedingSpacesWithSubsequentSpacesInterface { ~ [expected onespace after colon in property-declaration] } +interface WithPreceedingSpaceNoSubsequentSpaceAndNewLineInterface { + Prop : + string; +} + +interface WithPreceedingSpaceWithSubsequentSpaceAndNewLineInterface { + Prop : + ~ [trailing whitespace] + string; +} + // endregion // region Function @@ -311,6 +344,46 @@ function withPreceedingSpacesWithSubsequentSpacesFn(a : number, b : numb } } +function withPreceedingSpaceNoSubsequentSpaceAndNewLineFn(a : + number, b : + number) : + number { + var c : + number = a + b; + var d : + number = a - b; + + try { + return c / d; + } catch (ex : + Exception) { + console.log(ex); + } +} + +function withPreceedingSpaceWithSubsequentSpaceAndNewLineFn(a : + ~ [trailing whitespace] + number, b : + ~ [trailing whitespace] + number) : + ~ [trailing whitespace] +number { + var c : + ~ [trailing whitespace] + number = a + b; + var d : + ~ [trailing whitespace] + number = a - b; + + try { + return c / d; + } catch (ex : + ~ [trailing whitespace] + Exception) { + console.log(ex); + } +} + // endregion // region Function multi-line signature @@ -547,6 +620,13 @@ function withPreceedingSpacesWithSubsequentSpacesSignature( ) : {}; ~ [expected onespace before colon in call-signature] ~ [expected onespace after colon in call-signature] +function withPreceedingSpaceNoSubsequentSpaceAndNewLineSignature( ) : + {}; + +function withPreceedingSpaceWithSubsequentSpaceAndNewLineSignature( ) : + ~ [trailing whitespace] + {}; + // endregion // region Class @@ -812,4 +892,65 @@ class WithPreceedingSpacesWithSubsequentSpacesClass { ~ [expected onespace after colon in call-signature] } +class WithPreceedingSpaceNoSubsequentSpaceAndNewLineClass { + [index : + number] : + string + + Prop : + string = "some property"; + + public get name() : + string { + return "some name"; + } + + public set name(a : + string) : + void {} + + public shemp( + a : + number, + b : + number + ) : + void {} +} + +class WithPreceedingSpaceWithSubsequentSpaceAndNewLineClass { + [index : + ~ [trailing whitespace] + number] : + ~ [trailing whitespace] + string + + Prop : + ~ [trailing whitespace] + string = "some property"; + + public get name() : + ~ [trailing whitespace] + string { + return "some name"; + } + + public set name(a : + ~ [trailing whitespace] + string) : + ~ [trailing whitespace] + void {} + + public shemp( + a : + ~ [trailing whitespace] + number, + b : + ~ [trailing whitespace] + number + ) : + ~ [trailing whitespace] + void {} +} + // endregion diff --git a/test/rules/typedef-whitespace/both/onespace/tslint.json b/test/rules/typedef-whitespace/both/onespace/tslint.json index 273d5eba134..fcf1d6674eb 100644 --- a/test/rules/typedef-whitespace/both/onespace/tslint.json +++ b/test/rules/typedef-whitespace/both/onespace/tslint.json @@ -1,5 +1,6 @@ { "rules": { + "no-trailing-whitespace": true, "typedef-whitespace": [true, { "call-signature": "onespace", "index-signature": "onespace", diff --git a/test/rules/typedef-whitespace/both/space/test.ts.lint b/test/rules/typedef-whitespace/both/space/test.ts.lint index 8f155111827..b4ce7addfbc 100644 --- a/test/rules/typedef-whitespace/both/space/test.ts.lint +++ b/test/rules/typedef-whitespace/both/space/test.ts.lint @@ -78,6 +78,28 @@ var withPreceedingSpacesWithSubsequentSpacesObject = { } }; +var withPreceedingSpaceWithSubsequentNewLineObject = { + _Prop : + "some property", + + get Prop() : + string { + return this._Prop; + } +}; + +var noPreceedingSpaceWithSubsequentSpaceAndNewLineObject = { + _Prop : + ~ [trailing whitespace] + "some property", + + get Prop() : + ~ [trailing whitespace] + string { + return this._Prop; + } +}; + // endregion // region Interface @@ -124,6 +146,17 @@ interface WithPreceedingSpacesWithSubsequentSpacesInterface { Prop : string; } +interface WithPreceedingSpaceNoSubsequentSpaceAndNewLineInterface { + Prop : + string; +} + +interface WithPreceedingSpaceWithSubsequentSpaceAndNewLineInterface { + Prop : + ~ [trailing whitespace] + string; +} + // endregion // region Function @@ -263,6 +296,46 @@ function withPreceedingSpacesWithSubsequentSpacesFn(a : number, b : numb } } +function withPreceedingSpaceNoSubsequentSpaceAndNewLineFn(a : + number, b : + number) : + number { + var c : + number = a + b; + var d : + number = a - b; + + try { + return c / d; + } catch (ex : + Exception) { + console.log(ex); + } +} + +function withPreceedingSpaceWithSubsequentSpaceAndNewLineFn(a : + ~ [trailing whitespace] + number, b : + ~ [trailing whitespace] + number) : + ~ [trailing whitespace] +number { + var c : + ~ [trailing whitespace] + number = a + b; + var d : + ~ [trailing whitespace] + number = a - b; + + try { + return c / d; + } catch (ex : + ~ [trailing whitespace] + Exception) { + console.log(ex); + } +} + // endregion // region Function multi-line signature @@ -457,6 +530,13 @@ function withPreceedingSpacesWithSubsequentSpaceSignature( ) : {}; function withPreceedingSpacesWithSubsequentSpacesSignature( ) : {}; +function withPreceedingSpaceNoSubsequentSpaceAndNewLineSignature( ) : + {}; + +function withPreceedingSpaceWithSubsequentSpaceAndNewLineSignature( ) : + ~ [trailing whitespace] + {}; + // endregion // region Class @@ -668,4 +748,65 @@ class WithPreceedingSpacesWithSubsequentSpacesClass { ) : void {} } +class WithPreceedingSpaceNoSubsequentSpaceAndNewLineClass { + [index : + number] : + string + + Prop : + string = "some property"; + + public get name() : + string { + return "some name"; + } + + public set name(a : + string) : + void {} + + public shemp( + a : + number, + b : + number + ) : + void {} +} + +class WithPreceedingSpaceWithSubsequentSpaceAndNewLineClass { + [index : + ~ [trailing whitespace] + number] : + ~ [trailing whitespace] + string + + Prop : + ~ [trailing whitespace] + string = "some property"; + + public get name() : + ~ [trailing whitespace] + string { + return "some name"; + } + + public set name(a : + ~ [trailing whitespace] + string) : + ~ [trailing whitespace] + void {} + + public shemp( + a : + ~ [trailing whitespace] + number, + b : + ~ [trailing whitespace] + number + ) : + ~ [trailing whitespace] + void {} +} + // endregion diff --git a/test/rules/typedef-whitespace/both/space/tslint.json b/test/rules/typedef-whitespace/both/space/tslint.json index 1c2c192c9ab..44bbc56c01f 100644 --- a/test/rules/typedef-whitespace/both/space/tslint.json +++ b/test/rules/typedef-whitespace/both/space/tslint.json @@ -1,5 +1,6 @@ { "rules": { + "no-trailing-whitespace": true, "typedef-whitespace": [true, { "call-signature": "space", "index-signature": "space", diff --git a/test/rules/typedef-whitespace/right/nospace/test.ts.lint b/test/rules/typedef-whitespace/right/nospace/test.ts.lint index a62a9210c68..745f3aaa706 100644 --- a/test/rules/typedef-whitespace/right/nospace/test.ts.lint +++ b/test/rules/typedef-whitespace/right/nospace/test.ts.lint @@ -78,6 +78,16 @@ var withPreceedingSpacesWithSubsequentSpacesObject = { } }; +var noPreceedingSpaceWithSubsequentNewLineObject = { + _Prop: + "some property", + + get Prop(): + string { + return this._Prop; + } +}; + // endregion // region Interface @@ -124,6 +134,11 @@ interface WithPreceedingSpacesWithSubsequentSpacesInterface { ~ [expected nospace after colon in property-declaration] } +interface NoPreceedingSpaceNoSubsequentSpaceAndNewLineInterface { + Prop: + string; +} + // endregion // region Function @@ -263,6 +278,23 @@ function withPreceedingSpacesWithSubsequentSpacesFn(a : number, b : numb } } +function noPreceedingSpaceNoSubsequentSpaceAndNewLineFn(a: + number, b: + number): + number { + var c: + number = a + b; + var d: + number = a - b; + + try { + return c / d; + } catch (ex: + Exception) { + console.log(ex); + } +} + // endregion // region Function multi-line signature @@ -457,6 +489,9 @@ function withPreceedingSpacesWithSubsequentSpaceSignature( ) : {}; function withPreceedingSpacesWithSubsequentSpacesSignature( ) : {}; ~ [expected nospace after colon in call-signature] +function noPreceedingSpaceNoSubsequentSpaceAndNewLineSignature( ): + {}; + // endregion // region Class @@ -668,4 +703,29 @@ class WithPreceedingSpacesWithSubsequentSpacesClass { ~ [expected nospace after colon in call-signature] } +class NoPreceedingSpaceNoSubsequentSpaceAndNewLineClass { + [index:number]: + string + + Prop: + string = "some property"; + + public get name(): + string { + return "some name"; + } + + public set name(a: + string): + void {} + + public shemp( + a: + number, + b: + number + ): + void {} +} + // endregion diff --git a/test/rules/typedef-whitespace/right/nospace/tslint.json b/test/rules/typedef-whitespace/right/nospace/tslint.json index 1737bc4c2f9..779f842dcc4 100644 --- a/test/rules/typedef-whitespace/right/nospace/tslint.json +++ b/test/rules/typedef-whitespace/right/nospace/tslint.json @@ -1,5 +1,6 @@ { "rules": { + "no-trailing-whitespace": true, "typedef-whitespace": [true, {}, { "call-signature": "nospace", "index-signature": "nospace", diff --git a/test/rules/typedef-whitespace/right/onespace/test.ts.lint b/test/rules/typedef-whitespace/right/onespace/test.ts.lint index 43c7c1c669c..9284ea215e8 100644 --- a/test/rules/typedef-whitespace/right/onespace/test.ts.lint +++ b/test/rules/typedef-whitespace/right/onespace/test.ts.lint @@ -78,6 +78,28 @@ var withPreceedingSpacesWithSubsequentSpacesObject = { } }; +var noPreceedingSpaceWithSubsequentNewLineObject = { + _Prop: + "some property", + + get Prop(): + string { + return this._Prop; + } +}; + +var noPreceedingSpaceWithSubsequentSpaceAndNewLineObject = { + _Prop: + ~ [trailing whitespace] + "some property", + + get Prop(): + ~ [trailing whitespace] + string { + return this._Prop; + } +}; + // endregion // region Interface @@ -124,6 +146,17 @@ interface WithPreceedingSpacesWithSubsequentSpacesInterface { ~ [expected onespace after colon in property-declaration] } +interface NoPreceedingSpaceNoSubsequentSpaceAndNewLineInterface { + Prop: + string; +} + +interface NoPreceedingSpaceWithSubsequentSpaceAndNewLineInterface { + Prop: + ~ [trailing whitespace] + string; +} + // endregion // region Function @@ -263,6 +296,46 @@ function withPreceedingSpacesWithSubsequentSpacesFn(a : number, b : numb } } +function noPreceedingSpaceNoSubsequentSpaceAndNewLineFn(a: + number, b: + number): + number { + var c: + number = a + b; + var d: + number = a - b; + + try { + return c / d; + } catch (ex: + Exception) { + console.log(ex); + } +} + +function withPreceedingSpaceWithSubsequentSpaceAndNewLineFn(a : + ~ [trailing whitespace] + number, b : + ~ [trailing whitespace] + number) : + ~ [trailing whitespace] +number { + var c : + ~ [trailing whitespace] + number = a + b; + var d : + ~ [trailing whitespace] + number = a - b; + + try { + return c / d; + } catch (ex : + ~ [trailing whitespace] + Exception) { + console.log(ex); + } +} + // endregion // region Function multi-line signature @@ -457,6 +530,13 @@ function withPreceedingSpacesWithSubsequentSpaceSignature( ) : {}; function withPreceedingSpacesWithSubsequentSpacesSignature( ) : {}; ~ [expected onespace after colon in call-signature] +function noPreceedingSpaceNoSubsequentSpaceAndNewLineSignature( ): + {}; + +function withPreceedingSpaceWithSubsequentSpaceAndNewLineSignature( ) : + ~ [trailing whitespace] + {}; + // endregion // region Class @@ -668,4 +748,65 @@ class WithPreceedingSpacesWithSubsequentSpacesClass { ~ [expected onespace after colon in call-signature] } +class NoPreceedingSpaceNoSubsequentSpaceAndNewLineClass { + [index: + number]: + string + + Prop: + string = "some property"; + + public get name(): + string { + return "some name"; + } + + public set name(a: + string): + void {} + + public shemp( + a: + number, + b: + number + ): + void {} +} + +class WithPreceedingSpaceWithSubsequentSpaceAndNewLineClass { + [index : + ~ [trailing whitespace] + number] : + ~ [trailing whitespace] + string + + Prop : + ~ [trailing whitespace] + string = "some property"; + + public get name() : + ~ [trailing whitespace] + string { + return "some name"; + } + + public set name(a : + ~ [trailing whitespace] + string) : + ~ [trailing whitespace] + void {} + + public shemp( + a : + ~ [trailing whitespace] + number, + b : + ~ [trailing whitespace] + number + ) : + ~ [trailing whitespace] + void {} +} + // endregion diff --git a/test/rules/typedef-whitespace/right/onespace/tslint.json b/test/rules/typedef-whitespace/right/onespace/tslint.json index 2404ceb1cf0..ec35766ca4d 100644 --- a/test/rules/typedef-whitespace/right/onespace/tslint.json +++ b/test/rules/typedef-whitespace/right/onespace/tslint.json @@ -1,5 +1,6 @@ { "rules": { + "no-trailing-whitespace": true, "typedef-whitespace": [true, {}, { "call-signature": "onespace", "index-signature": "onespace", diff --git a/test/rules/typedef-whitespace/right/space/test.ts.lint b/test/rules/typedef-whitespace/right/space/test.ts.lint index dac38a392f9..dde2a10ac52 100644 --- a/test/rules/typedef-whitespace/right/space/test.ts.lint +++ b/test/rules/typedef-whitespace/right/space/test.ts.lint @@ -75,6 +75,29 @@ var withPreceedingSpacesWithSubsequentSpacesObject = { } }; +var noPreceedingSpaceWithSubsequentNewLineObject = { + _Prop: + "some property", + + get Prop(): + string { + return this._Prop; + } +}; + +var noPreceedingSpaceWithSubsequentSpaceAndNewLineObject = { + _Prop: + ~ [trailing whitespace] + "some property", + + get Prop(): + ~ [trailing whitespace] + string { + return this._Prop; + } +}; + + // endregion // region Interface @@ -118,6 +141,17 @@ interface WithPreceedingSpacesWithSubsequentSpacesInterface { Prop : string; } +interface NoPreceedingSpaceNoSubsequentSpaceAndNewLineInterface { + Prop: + string; +} + +interface NoPreceedingSpaceWithSubsequentSpaceAndNewLineInterface { + Prop: + ~ [trailing whitespace] + string; +} + // endregion // region Function @@ -239,6 +273,46 @@ function withPreceedingSpacesWithSubsequentSpacesFn(a : number, b : numb } } +function noPreceedingSpaceNoSubsequentSpaceAndNewLineFn(a: + number, b: + number): + number { + var c: + number = a + b; + var d: + number = a - b; + + try { + return c / d; + } catch (ex: + Exception) { + console.log(ex); + } +} + +function withPreceedingSpaceWithSubsequentSpaceAndNewLineFn(a : + ~ [trailing whitespace] + number, b : + ~ [trailing whitespace] + number) : + ~ [trailing whitespace] +number { + var c : + ~ [trailing whitespace] + number = a + b; + var d : + ~ [trailing whitespace] + number = a - b; + + try { + return c / d; + } catch (ex : + ~ [trailing whitespace] + Exception) { + console.log(ex); + } +} + // endregion // region Function multi-line signature @@ -412,6 +486,13 @@ function withPreceedingSpacesWithSubsequentSpaceSignature( ) : {}; function withPreceedingSpacesWithSubsequentSpacesSignature( ) : {}; +function noPreceedingSpaceNoSubsequentSpaceAndNewLineSignature( ): + {}; + +function withPreceedingSpaceWithSubsequentSpaceAndNewLineSignature( ) : + ~ [trailing whitespace] + {}; + // endregion // region Class @@ -596,4 +677,65 @@ class WithPreceedingSpacesWithSubsequentSpacesClass { ) : void {} } +class NoPreceedingSpaceNoSubsequentSpaceAndNewLineClass { + [index: + number]: + string + + Prop: + string = "some property"; + + public get name(): + string { + return "some name"; + } + + public set name(a: + string): + void {} + + public shemp( + a: + number, + b: + number + ): + void {} +} + +class WithPreceedingSpaceWithSubsequentSpaceAndNewLineClass { + [index : + ~ [trailing whitespace] + number] : + ~ [trailing whitespace] + string + + Prop : + ~ [trailing whitespace] + string = "some property"; + + public get name() : + ~ [trailing whitespace] + string { + return "some name"; + } + + public set name(a : + ~ [trailing whitespace] + string) : + ~ [trailing whitespace] + void {} + + public shemp( + a : + ~ [trailing whitespace] + number, + b : + ~ [trailing whitespace] + number + ) : + ~ [trailing whitespace] + void {} +} + // endregion diff --git a/test/rules/typedef-whitespace/right/space/tslint.json b/test/rules/typedef-whitespace/right/space/tslint.json index ff2b42ba4df..2a839372c24 100644 --- a/test/rules/typedef-whitespace/right/space/tslint.json +++ b/test/rules/typedef-whitespace/right/space/tslint.json @@ -1,5 +1,6 @@ { "rules": { + "no-trailing-whitespace": true, "typedef-whitespace": [true, {}, { "call-signature": "space", "index-signature": "space", diff --git a/test/rules/variable-name/default/test.ts.lint b/test/rules/variable-name/default/test.ts.lint index 03d063e88b2..135275f70d3 100644 --- a/test/rules/variable-name/default/test.ts.lint +++ b/test/rules/variable-name/default/test.ts.lint @@ -48,3 +48,18 @@ let optionallyValid_ = "bar"; ~~~~~~~~~~~~~~~~ [variable name must be in camelcase or uppercase] let _$httpBackend_ = "leading and trailing"; ~~~~~~~~~~~~~~ [variable name must be in camelcase or uppercase] + +// Aliases. +class X { + ValidAlias = ValidAlias; +} + +var ValidAlias = some.ValidAlias; +var ValidAlias = some.InValidAlias; + ~~~~~~~~~~ [variable name must be in camelcase or uppercase] + +var someObject = {MoreValidAlias: 1}; +var {MoreValidAlias: localVar} = someObject; +var {MoreValidAlias: LocalVar} = someObject; + ~~~~~~~~ [variable name must be in camelcase or uppercase] +var {MoreValidAlias} = someObject; diff --git a/test/tsconfig.json b/test/tsconfig.json index 394ad763dbf..31529f2ee29 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -40,6 +40,7 @@ "../src/formatterLoader.ts", "../src/formatters.ts", "../src/formatters/checkstyleFormatter.ts", + "../src/formatters/fileslistFormatter.ts", "../src/formatters/index.ts", "../src/formatters/jsonFormatter.ts", "../src/formatters/msbuildFormatter.ts", @@ -67,11 +68,13 @@ "../src/rules.ts", "../src/rules/adjacentOverloadSignaturesRule.ts", "../src/rules/alignRule.ts", + "../src/rules/arrayTypeRule.ts", "../src/rules/arrowParensRule.ts", "../src/rules/banRule.ts", "../src/rules/classNameRule.ts", "../src/rules/commentFormatRule.ts", "../src/rules/curlyRule.ts", + "../src/rules/cyclomaticComplexityRule.ts", "../src/rules/eoflineRule.ts", "../src/rules/fileHeaderRule.ts", "../src/rules/forinRule.ts", @@ -93,8 +96,8 @@ "../src/rules/noConditionalAssignmentRule.ts", "../src/rules/noConsecutiveBlankLinesRule.ts", "../src/rules/noConsoleRule.ts", - "../src/rules/noConstructorVarsRule.ts", "../src/rules/noConstructRule.ts", + "../src/rules/noConstructorVarsRule.ts", "../src/rules/noDebuggerRule.ts", "../src/rules/noDefaultExportRule.ts", "../src/rules/noDuplicateKeyRule.ts", @@ -129,6 +132,7 @@ "../src/rules/oneVariablePerDeclarationRule.ts", "../src/rules/onlyArrowFunctionsRule.ts", "../src/rules/orderedImportsRule.ts", + "../src/rules/preferForOfRule.ts", "../src/rules/quotemarkRule.ts", "../src/rules/radixRule.ts", "../src/rules/restrictPlusOperandsRule.ts", @@ -149,6 +153,7 @@ "../src/test/utils.ts", "../src/tslint-cli.ts", "../src/tslint.ts", + "../src/tslintMulti.ts", "../src/utils.ts", "assert.ts", "configurationTests.ts", @@ -159,7 +164,8 @@ "utils.ts", "utilsTests.ts", "formatters/checkstyleFormatterTests.ts", - "formatters/externalFormatterTest.ts", + "formatters/fileslistFormatterTests.ts", + "formatters/externalFormatterTests.ts", "formatters/jsonFormatterTests.ts", "formatters/msbuildFormatterTests.ts", "formatters/pmdFormatterTests.ts", diff --git a/test/utils.ts b/test/utils.ts index d3f3c580105..f3c80a751ee 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -55,7 +55,7 @@ export function createFailure(fileName: string, start: number[], end: number[], // return a partial on createFailure export function createFailuresOnFile(fileName: string, failure: string) { - return function(start: number[], end: number[]) { + return (start: number[], end: number[]) => { return createFailure(fileName, start, end, failure); }; }