├── .editorconfig ├── .gitignore ├── .lintignore ├── .travis.yml ├── README.md ├── attenuate-severity.js ├── bin └── lint-trap.js ├── checkstyle-reporter.js ├── compact-reporter.js ├── dedupe-transform.js ├── docs ├── README.md ├── code-editor-support.md ├── configuration.md ├── eslint │ ├── camelcase.md │ ├── complexity.md │ ├── env.md │ ├── eqeqeq.md │ ├── func-names.md │ ├── global-strict.md │ ├── globals.md │ ├── guard-for-in.md │ ├── max-depth.md │ ├── max-params.md │ ├── new-cap.md │ ├── no-alert.md │ ├── no-bitwise.md │ ├── no-caller.md │ ├── no-empty.md │ ├── no-extend-native.md │ ├── no-new.md │ ├── no-plusplus.md │ ├── no-undef.md │ ├── no-unused-vars.md │ ├── no-use-before-define.md │ └── wrap-iife.md ├── indentation.md ├── jscs │ ├── disallow-empty-blocks.md │ ├── disallow-keywords-on-new-line.md │ ├── disallow-keywords.md │ ├── disallow-mixed-spaces-and-tabs.md │ ├── disallow-multiple-line-breaks.md │ ├── disallow-multiple-var-decl.md │ ├── disallow-space-after-object-keys.md │ ├── disallow-space-after-prefix-unary-operators.md │ ├── disallow-trailing-comma.md │ ├── disallow-trailing-white-space.md │ ├── maximum-line-length.md │ ├── require-blocks-on-newline.md │ ├── require-comma-before-line-break.md │ ├── require-curly-braces.md │ ├── require-dot-notation.md │ ├── require-line-feed-at-file-end.md │ ├── require-space-after-keywords.md │ ├── require-spaces-before-after-binary-operators.md │ ├── require-spaces-in-conditional-expression.md │ ├── spaces-in-functions.md │ ├── validate-indentation.md │ ├── validate-line-breaks.md │ └── validate-quote-marks.md ├── jshint │ └── jshint.md ├── legacy-project-tips.md └── philosophy.md ├── dupe-map.json ├── error-meter.js ├── error-to-file-transform.js ├── extras └── linttrap.vim ├── get-javascript-files.js ├── group-file-messages-transform.js ├── json-reporter.js ├── lint-stream.js ├── lint-trap.js ├── load-lintrc.js ├── package.json ├── rc ├── .eslintrc ├── .jscsrc └── .jshintrc ├── reporters ├── eslint.js ├── jscs.js └── jshint.js ├── scripts └── post-install.js ├── set-rules.js ├── severity-transform.js ├── stylish-reporter.js └── test ├── attenuate-severity.tape.js ├── checkstyle-reporter.tape.js ├── cli.tape.js ├── compact-reporter.tape.js ├── dedupe-transform.tape.js ├── error-meter.tape.js ├── error-to-file-transform.tape.js ├── fixtures ├── lintrc │ ├── .lintrc │ ├── fixture.js │ ├── fixture.stdout │ ├── output.stdout │ └── subfolder │ │ ├── .lintrc │ │ └── fixture.js ├── rules │ ├── func-names-invalid.js │ ├── func-names-valid.js │ ├── globals-invalid.js │ ├── globals-valid.js │ ├── output.json │ └── output.stdout └── unit │ ├── checkstyle.stdout │ ├── compact.stdout │ ├── error-to-file-stream.input.json │ ├── error-to-file-stream.output.json │ ├── json.stdout │ └── stylish.stdout ├── get-javascript-files.tape.js ├── group-file-messages-transform.tape.js ├── helpers ├── make-error-stream-fixture.js ├── make-error-stream.js └── make-file-error-stream.js ├── json-reporter.tape.js ├── lint-stream.tape.js ├── lint-trap.tape.js ├── lintrc.tape.js ├── load-lintrc.tape.js ├── post-install.tape.js ├── set-rules.tape.js ├── severity-transform.tape.js └── stylish-reporter.tape.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | 16 | [*.markdown] 17 | trim_trailing_whitespace = false 18 | 19 | [*.js] 20 | indent_style = space 21 | indent_size = 4 22 | 23 | [*.json] 24 | indent_style = space 25 | indent_size = 4 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | node_modules/ 3 | test/fixtures/*.git/ 4 | /.jscsrc 5 | /.jshintrc 6 | /.eslintrc 7 | *.log 8 | -------------------------------------------------------------------------------- /.lintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | test/fixtures/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | 5 | notifications: 6 | email: 7 | - aandrade@uber.com 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lint-trap 2 | ========= 3 | 4 | This module contains standardized linting rules to be used 5 | across all projects at Uber that contain JavaScript. 6 | 7 | 8 | Usage 9 | ----- 10 | 11 | npm install --save-dev lint-trap 12 | ./node_modules/.bin/lint-trap 13 | 14 | It is recommended that you add lint-trap to the `scripts` 15 | property of your project's `package.json` manifest file, 16 | like so: 17 | 18 | "scripts": { 19 | "lint": "lint-trap", 20 | } 21 | 22 | ... and then you can invoke it by executing `npm run lint`. 23 | 24 | 25 | Tips and Tricks for Legacy Projects 26 | ----------------------------------- 27 | 28 | Using lint-trap to get a legacy project with many thousands 29 | of lines of code in line with the standards enforced by 30 | lint-trap can be a daunting task. To make the process as 31 | painless as possible, [this guide][legacy-tips] will give 32 | you practical advice on tackling all your lint errors and 33 | warnings in bite-size chunks. 34 | 35 | 36 | Linting Support in Text Editors 37 | ------------------------------- 38 | 39 | lint-trap is very new so support for text editor plugin is 40 | still immature. Plugin support already exists for 41 | SublimeText 3 and Syntastic (vim). Plugin support for 42 | flycheck in emacs is planned. 43 | 44 | For more information and workarounds if your editor is not 45 | yet supported, see the documentation on 46 | [code editor support][code-editor-support]. 47 | 48 | 49 | Attenuating Rules 50 | ----------------- 51 | 52 | Since lint-trap is meant to enforce good coding style and 53 | consistency across many projects from the same organization, 54 | you cannot turn rules off completely. However, when using 55 | lint-trap in legacy projects without any linter or with 56 | different linting rules, it is useful to be able to 57 | downgrade the warning severity from `error` to `warning` so 58 | you can pay down linting technical debt over several 59 | commits. Because of this, lint-trap supports adding a 60 | `.lintrc` file to your project. 61 | 62 | For more information on configuring a .lintrc file or 63 | command-line options, see the [configuration 64 | docs][configuration]. 65 | 66 | If you're using lint-trap in a legacy project, you should 67 | also check out the [legacy project tips][legacy-tips]. 68 | 69 | 70 | Indentation 71 | ----------- 72 | 73 | lint-trap dynamically detects whether it should enforce a 74 | 2-space or 4-space softtab indent rule. It does this by 75 | inspecting a reference file to detect the indentation used 76 | in that reference file, and then enforces the detected 77 | indentation on all the files it is linting. 78 | 79 | For more information, see the documentation on 80 | [indentation][indentation] 81 | 82 | 83 | Warnings vs. Errors 84 | ------------------- 85 | 86 | Almost all lint-trap rules produce errors. If you see an 87 | error, you should fix it. However, a few rules, notably 88 | `max-statements` and `complexity` produce warnings. The 89 | reason these rules produce warnings is because their goal is 90 | to highlight code where there might be too much going on 91 | within a single block of code. 92 | 93 | Long, complex code blocks are likely to be less readable and 94 | therefore less maintainable. When you see a warning, you 95 | should investigate the code in question and deliberately 96 | determine if the code should be refactored or if the code is 97 | okay as is. There exist cases where lint-trap will return a 98 | warning, but where the code is acceptable as is. For 99 | example, you might find that in some tests you have many 100 | assertions and you go beyond the maximum number of 101 | statements allowed. In this situation you might determine 102 | that many assertions are not only acceptable but necessary 103 | without reducing readability and comprehension. If you 104 | encounter code that should be considered acceptable, you can 105 | add an inline linter directive to tell lint-trap to ignore 106 | that code for that particular warning. 107 | 108 | Since all the lint rules that produce warnings are currently 109 | handled by eslint all you need to do is add the following 110 | two lines around your code, where `lint-rule-id` is the 111 | string eslint uses to identify a particular rule such as 112 | `max-statements`: 113 | 114 | /*eslint-disable lint-rule-id*/ 115 | /* the code producing the warning here */ 116 | /*eslint-enable lint-rule-id*/ 117 | 118 | 119 | Contributing 120 | ------------ 121 | 122 | Contributions to lint-trap are welcome, but since lint-trap 123 | is effectively a module that encapsulates a set of opinions 124 | and throws errors and warnings at you when you violate those 125 | opinions, there is a lot of room to debate over 126 | [what color to paint our bikeshed][bikeshed]. 127 | 128 | Before you begin filing an issue to argue why you think your 129 | color of paint is superior, it's worth knowing how the 130 | current set of rules were determined. Javascript enginerrs 131 | from several teams with different needs (both front-end 132 | engineers and back-end NodeJS engineers) were the first to 133 | go through all the rules, try them out and debate the merit 134 | of each. This group is consists of developers that 135 | collectively have seen tons of code and tons of bugs in 136 | production systems at scale that arose from poor choice of 137 | coding style and conventions. 138 | 139 | The rules and the reasoning behind each should all be 140 | documented or will be over time. Before we bikeshed over a 141 | rule, please check the rules [documentation][docs]. If a 142 | rule hasn't been documented or hasn't yet been documented 143 | adequately, open an issue asking for clarification and 144 | better documentation *first*. If a rule has been documented 145 | and you still disagree, there is one task you must perform 146 | before you are allowed to bikeshed. You must first read Clay 147 | Shirky's essay 148 | [A Group is its Own Worst Enemy][group-enemy]. 149 | At the end of the day, we all love bikeshedding, but 150 | we would like to keep it to a minimum, so we can all get 151 | work done. 152 | 153 | 154 | Why lint-trap 155 | ------------- 156 | 157 | If there are all these other linters out there like JSHint, 158 | ESLint and jscs, why does lint-trap exist? 159 | 160 | Uber like many large companies using JavaScript has a 161 | **LOT** of projects all with their own set of .rc files 162 | (pronounced __dot·arc__) for each linter. These 163 | configuration files are almost always copypasta-ed from a a 164 | previous project and they all evolve and mutate over time as 165 | each developer gets their hands on a project and make their 166 | own changes. This means that over time, every project ends 167 | up with its own style adding unnecessary friction to 168 | collaboration and working across many projects. 169 | 170 | lint-trap aims to be a zero-configuration linter as much as 171 | possible, and requires no configuration in brand new 172 | projects. The few configuration options it offers exist 173 | solely to help legacy projects reach 100% linting 174 | conformance piecemeal. 175 | 176 | For more information on why we think lint-trap is valuable 177 | see the documentation describing the 178 | [philosopy behind lint-trap][philosophy] 179 | 180 | 181 | Using lint-trap in non-Uber projects 182 | ------------------------------------ 183 | 184 | lint-trap currently includes hard-coded lint rules in use at 185 | Uber. If you work at Uber and work with JavaScript, you 186 | should have lint-trap in your project. If you don't work at 187 | Uber, you're welcome to use lint-trap in your project as is. 188 | 189 | If you want lint-trap to support a different rules for your 190 | organization, we first ask that you try out the rules with 191 | which lint-trap already comes. The rules have been discussed 192 | at length by many engineers and are well thought out for any 193 | environment where lots of devs need to work together and 194 | need to feel at home in each others' code. Spend some time 195 | with the existing rules and reading the explanation for 196 | their existance in the docs and we're pretty sure you'll 197 | find them to be a pretty good balance for large engineering 198 | organizations. 199 | 200 | If, after trying out our lint-rules for some time, you feel 201 | that you still need to support different rules, please 202 | comment on [this issue][orgrules-issue]. We will be glad to 203 | work with you to add support for this lint-trap. 204 | 205 | 206 | License 207 | ------- 208 | 209 | The MIT License (MIT) 210 | 211 | Copyright (c) 2014 Uber Technologies, Inc. 212 | 213 | Permission is hereby granted, free of charge, to any person 214 | obtaining a copy of this software and associated 215 | documentation files (the "Software"), to deal in the 216 | Software without restriction, including without limitation 217 | the rights to use, copy, modify, merge, publish, distribute, 218 | sublicense, and/or sell copies of the Software, and to 219 | permit persons to whom the Software is furnished to do so, 220 | subject to the following conditions: 221 | 222 | The above copyright notice and this permission notice shall 223 | be included in all copies or substantial portions of the 224 | Software. 225 | 226 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 227 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 228 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 229 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 230 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 231 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 232 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 233 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 234 | 235 | 236 | [legacy-tips]: https://github.com/uber/lint-trap/blob/master/docs/legacy-project-tips.md 237 | [indentation]: https://github.com/uber/lint-trap/blob/master/docs/indentation.md 238 | [code-editor-support]: https://github.com/uber/lint-trap/blob/master/docs/code-editor-support.md 239 | [configuration]: https://github.com/uber/lint-trap/blob/master/docs/configuration.md 240 | [philosophy]: https://github.com/uber/lint-trap/blob/master/docs/philosophy.md 241 | 242 | [orgrules-issue]: https://github.com/uber/lint-trap/issues/54 243 | 244 | [file-an-issue]: https://github.com/uber/lint-trap/issues/new 245 | [docs]: https://github.com/uber/lint-trap/tree/master/docs 246 | [wadlers-law]: http://www.haskell.org/haskellwiki/Wadler's_Law 247 | [bikeshed]: http://red.bikeshed.com/ 248 | [group-enemy]: http://www.shirky.com/writings/herecomeseverybody/group_enemy.html 249 | -------------------------------------------------------------------------------- /attenuate-severity.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function attenuateSeverity(message, lintrc) { 4 | 5 | var typeRules = lintrc[message.linter]; 6 | if (typeRules) { 7 | if (typeRules[message.rule] === false) { 8 | message.type = 'warning'; 9 | } 10 | } 11 | } 12 | 13 | module.exports = attenuateSeverity; 14 | -------------------------------------------------------------------------------- /bin/lint-trap.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /*eslint no-console:0 no-process-exit:0 */ 3 | 'use strict'; 4 | var process = require('process'); 5 | var console = require('console'); 6 | var argv = require('minimist')(process.argv.slice(2)); 7 | var lintTrap = require('../lint-trap'); 8 | var fmt = require('util').format; 9 | var setTimeout = require('timers').setTimeout; 10 | var path = require('path'); 11 | 12 | // hack. sad. 13 | setTimeout(function stdinTimeout() { 14 | if (readFromStdin(argv)) { 15 | process.exit(1); 16 | } 17 | }, 5000); 18 | 19 | var files = argv._.length === 0 ? [process.cwd()] : absolutePaths(argv._); 20 | 21 | function absolutePaths(args) { 22 | return args.map(function absolutePath(file) { 23 | return file.charAt(0) === '/' || file === '-' ? 24 | file : path.resolve(process.cwd(), file); 25 | }); 26 | } 27 | 28 | if (argv.v || argv.version) { 29 | printVersion(); 30 | } else if (argv.h || argv.help) { 31 | printHelp(); 32 | } else { 33 | var opts = { 34 | lineLength: argv['line-length'] || 80, 35 | reporter: argv.reporter || argv.r || 'stylish', 36 | files: files, 37 | stdin: readFromStdin(argv) 38 | }; 39 | lintTrap(opts, run); 40 | } 41 | 42 | function readFromStdin(minimistArgv) { 43 | return minimistArgv._.length === 1 && minimistArgv._[0] === '-'; 44 | } 45 | 46 | function run(err) { 47 | if (err) { 48 | if (err.message !== 'Lint errors encountered') { 49 | console.error(err.message); 50 | } 51 | return process.exit(1); 52 | } 53 | process.exit(0); 54 | } 55 | 56 | function printHelp() { 57 | var helpMsg = [ 58 | 'lint-trap', 59 | '', 60 | 'usage:', 61 | ' lint-trap ', 62 | '', 63 | 'options:', 64 | ' -h --help Print help information', 65 | ' -v --version Print version', 66 | ' --line-length Set line-length limit to ', 67 | '', 68 | 'example:', 69 | ' lint-trap "test/*.js" --line-length 120', 70 | '', 71 | ' (nb: quote globstar patterns to prevent shell expansion)', 72 | '' 73 | ].join('\n'); 74 | process.stdout.write(helpMsg); 75 | process.exit(0); 76 | } 77 | 78 | function printVersion() { 79 | var pkg = require('../package.json'); 80 | console.error(fmt('lint-trap v%s', pkg.version)); 81 | process.exit(0); 82 | } 83 | -------------------------------------------------------------------------------- /checkstyle-reporter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fmt = require('util').format; 3 | 4 | // ==== begin attribution ===================================================== 5 | // The pairs object and encode function are borrowed from: 6 | // github.com/jshint/jshint src/reporters/checkstyle.js 7 | // Author: Boy Baukema, http://github.com/relaxnow 8 | // License MIT 9 | // This code should be replaced in a future commit of checkstyle-reporter.js 10 | var pairs = { 11 | '&': '&', 12 | '"': '"', 13 | '\'': ''', 14 | '<': '<', 15 | '>': '>' 16 | }; 17 | 18 | function encode(s) { 19 | for (var r in pairs) { 20 | if (typeof s !== 'undefined') { 21 | s = s.replace(new RegExp(r, 'g'), pairs[r]); 22 | } 23 | } 24 | return s || ''; 25 | } 26 | // ==== end attribution ======================================================= 27 | 28 | function printCheckstyle(fileMessages) { 29 | fileMessages = fileMessages.filter(removeEmpty); 30 | 31 | function removeEmpty(fileMessage) { 32 | return fileMessage.errors.length > 0; 33 | } 34 | 35 | return [ 36 | '', 37 | '', 38 | fileMessages.map(printFile).join('\n'), 39 | '' 40 | ].join('\n') + '\n'; 41 | } 42 | 43 | function printFile(message) { 44 | return fmt('\t\n%s\n\t', 45 | encode(message.file), 46 | printErrors(message) 47 | ); 48 | } 49 | 50 | function printErrors(message) { 51 | return message.errors.map(printError).join('\n'); 52 | } 53 | 54 | function printError(message) { 55 | return fmt( 56 | '\t\t', 58 | message.line, 59 | message.column, 60 | message.type, 61 | encode(message.message), 62 | encode([message.linter, message.rule].join('.')) 63 | ); 64 | } 65 | 66 | module.exports = printCheckstyle; 67 | -------------------------------------------------------------------------------- /compact-reporter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fmt = require('util').format; 3 | 4 | function capitalize(str) { 5 | return str.charAt(0).toUpperCase() + str.substr(1); 6 | } 7 | 8 | function printCompact(fileMessages) { 9 | 10 | var output = []; 11 | 12 | fileMessages.forEach(function printFile(fileMessage) { 13 | 14 | fileMessage.errors.forEach(printError); 15 | 16 | function printError(error) { 17 | output.push(fmt('%s: line %d, col %d, %s - %s (%s)', 18 | fileMessage.file, 19 | error.line, 20 | error.column, 21 | capitalize(error.type), 22 | error.message, 23 | [error.linter, error.rule].join('.') 24 | )); 25 | } 26 | }); 27 | 28 | return output.join('\n') + '\n'; 29 | } 30 | 31 | module.exports = printCompact; 32 | -------------------------------------------------------------------------------- /dedupe-transform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var through = require('through2'); 3 | var dedupeMap = require('./dupe-map.json'); 4 | var partial = require('partial'); 5 | 6 | function findPossibleDupes(memo, error, index, array) { 7 | var key = [error.linter, error.rule].join('.'); 8 | var duplicate = dedupeMap[key]; 9 | if (duplicate) { 10 | if (!memo[error.line]) { 11 | memo[error.line] = {}; 12 | } 13 | memo[error.line][duplicate] = key; 14 | } 15 | 16 | return memo; 17 | } 18 | 19 | function dedupe(possibleDupes, error, index, array) { 20 | var isDupe = false; 21 | var maybeDupes = possibleDupes[error.line]; 22 | var key = [error.linter, error.rule].join('.'); 23 | if (maybeDupes) { 24 | isDupe = maybeDupes[key] && 25 | dedupeMap[maybeDupes[key]] && 26 | error.linter !== 'eslint'; 27 | } 28 | isDupe = !!isDupe; 29 | return !isDupe; 30 | } 31 | 32 | function makeDedupeTransform() { 33 | 34 | function transform(fileMessage, enc, callback) { 35 | 36 | var possibleDupes = fileMessage.errors.reduce(findPossibleDupes, {}); 37 | 38 | fileMessage.errors = fileMessage.errors 39 | .filter(partial(dedupe, possibleDupes)); 40 | 41 | this.push(fileMessage); 42 | callback(); 43 | } 44 | 45 | return through.obj(transform); 46 | } 47 | 48 | module.exports = makeDedupeTransform; 49 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Rules 2 | 3 | lint-trap combines rules from eslint, jscs and jshint. There is significant 4 | overlap between these three linters. When a rule can be implemented in more 5 | than one linter, the preference is to implement it in eslint first and jscs 6 | second. jshint is legacy and implemented solely for consistent indentation 7 | checking until eslint supports proper indentation checking. 8 | 9 | ## ESLint 10 | 11 | * [globals][globals] List of allowed globals and workarounds for common globals we disallow 12 | 13 | ### errors 14 | 15 | * [no-alert][no-alert] - disallow the use of `alert`, `confirm`, and `prompt` 16 | * [no-array-constructor][no-array-constructor] - disallow use of the `Array` constructor 17 | * [no-caller][no-caller] - disallow use of `arguments.caller` or `arguments.callee` 18 | * [no-catch-shadow][no-catch-shadow] - disallow the catch clause parameter name being the same as a variable in the outer scope 19 | * [no-comma-dangle][no-comma-dangle] - disallow trailing commas in object literals 20 | * [no-cond-assign][no-cond-assign] - disallow assignment in conditional expressions 21 | * [no-console][no-console] - disallow use of `console` 22 | * [no-constant-condition][no-constant-condition] - disallow use of constant expressions in conditions 23 | * [no-control-regex][no-control-regex] - disallow control characters in regular expressions 24 | * [no-debugger][no-debugger] - disallow use of `debugger` 25 | * [no-dupe-keys][no-dupe-keys] - disallow duplicate keys when creating object literals 26 | * [no-empty][no-empty] - disallow empty statements 27 | * [no-empty-class][no-empty-class] - disallow the use of empty character classes in regular expressions 28 | * [no-empty-label][no-empty-label] - disallow use of labels for anything other then loops and switches 29 | * [no-eval][no-eval] - disallow use of `eval()` 30 | * [no-ex-assign][no-ex-assign] - disallow assigning to the exception in a `catch` block 31 | * [no-extend-native][no-extend-native] - disallow adding to native types 32 | * [no-extra-bind][no-extra-bind] - disallow unnecessary function binding 33 | * [no-extra-boolean-cast][no-extra-boolean-cast] - disallow double-negation boolean casts in a boolean context 34 | * [no-extra-semi][no-extra-semi] - disallow unnecessary semicolons 35 | * [no-extra-strict][no-extra-strict] - disallow unnecessary use of `"use strict";` when already in strict mode 36 | * [no-fallthrough][no-fallthrough] - disallow fallthrough of `case` statements 37 | * [no-floating-decimal][no-floating-decimal] - disallow the use of leading or trailing decimal points in numeric literals 38 | * [no-func-assign][no-func-assign] - disallow overwriting functions written as function declarations 39 | * [no-implied-eval][no-implied-eval] - disallow use of `eval()`-like methods 40 | * [no-inline-comments][no-inline-comments] - disallow comments inline after code 41 | * [no-inner-declarations][no-inner-declarations] - disallow function or variable declarations in nested blocks 42 | * [no-invalid-regexp][no-invalid-regexp] - disallow invalid regular expression strings in the `RegExp` constructor 43 | * [no-irregular-whitespace][no-irregular-whitespace] - disallow irregular whitespace outside of strings and comments 44 | * [no-iterator][no-iterator] - disallow usage of `__iterator__` property 45 | * [no-label-var][no-label-var] - disallow labels that share a name with a variable 46 | * [no-labels][no-labels] - disallow use of labeled statements 47 | * [no-lone-blocks][no-lone-blocks] - disallow unnecessary nested blocks 48 | * [no-lonely-if][no-lonely-if] - disallow if as the only statement in an else block 49 | * [no-loop-func][no-loop-func] - disallow creation of functions within loops 50 | * [no-mixed-spaces-and-tabs][no-mixed-spaces-and-tabs] - disallow mixed spaces and tabs for indentation 51 | * [no-multi-spaces][no-multi-spaces] - disallow use of multiple spaces 52 | * [no-multi-str][no-multi-str] - disallow use of multiline strings 53 | * [no-multiple-empty-lines][no-multiple-empty-lines] - disallow multiple empty lines 54 | * [no-native-reassign][no-native-reassign] - disallow reassignments of native objects 55 | * [no-negated-in-lhs][no-negated-in-lhs] - disallow negation of the left operand of an `in` expression 56 | * [no-new][no-new] - disallow use of new operator when not part of the assignment or comparison 57 | * [no-new-func][no-new-func] - disallow use of new operator for `Function` object 58 | * [no-new-object][no-new-object] - disallow use of the `Object` constructor 59 | * [no-new-require][no-new-require] - disallow use of new operator with the `require` function 60 | * [no-new-wrappers][no-new-wrappers] - disallows creating new instances of `String`,`Number`, and `Boolean` 61 | * [no-obj-calls][no-obj-calls] - disallow the use of object properties of the global object (`Math` and `JSON`) as functions 62 | * [no-octal][no-octal] - disallow use of octal literals 63 | * [no-octal-escape][no-octal-escape] - disallow use of octal escape sequences in string literals, such as `var foo = "Copyright \251";` 64 | * [no-path-concat][no-path-concat] - disallow string concatenation with `__dirname` and `__filename` 65 | * [no-process-env][no-process-env] - disallow use of `process.env` 66 | * [no-process-exit][no-process-exit] - disallow `process.exit()` 67 | * [no-proto][no-proto] - disallow usage of `__proto__` property 68 | * [no-redeclare][no-redeclare] - disallow declaring the same variable more then once 69 | * [no-regex-spaces][no-regex-spaces] - disallow multiple spaces in a regular expression literal 70 | * [no-return-assign][no-return-assign] - disallow use of assignment in `return` statement 71 | * [no-script-url][no-script-url] - disallow use of javascript: urls. 72 | * [no-self-compare][no-self-compare] - disallow comparisons where both sides are exactly the same 73 | * [no-sequences][no-sequences] - disallow use of comma operator 74 | * [no-shadow][no-shadow] - disallow declaration of variables already declared in the outer scope 75 | * [no-shadow-restricted-names][no-shadow-restricted-names] - disallow shadowing of names such as `arguments` 76 | * [no-space-before-semi][no-space-before-semi] - disallow space before semicolon 77 | * [no-spaced-func][no-spaced-func] - disallow space between function identifier and application 78 | * [no-sparse-arrays][no-sparse-arrays] - disallow sparse arrays 79 | * [no-trailing-spaces][no-trailing-spaces] - disallow trailing whitespace at the end of lines 80 | * [no-undef][no-undef] - disallow use of undeclared variables unless mentioned in a `/*global */` block 81 | * [no-undef-init][no-undef-init] - disallow use of undefined when initializing variables 82 | * [no-underscore-dangle][no-underscore-dangle] - disallow dangling underscores in identifiers 83 | * [no-unreachable][no-unreachable] - disallow unreachable statements after a return, throw, continue, or break statement 84 | * [no-unused-expressions][no-unused-expressions] - disallow usage of expressions in statement position 85 | * [no-unused-vars][no-unused-vars] - disallow declaration of variables that are not used in the code 86 | * [no-use-before-define][no-use-before-define] - disallow use of variables before they are defined 87 | * [no-with][no-with] - disallow use of the `with` statement 88 | * [no-wrap-func][no-wrap-func] - disallow wrapping of non-IIFE statements in parens 89 | * [brace-style][brace-style] - enforce one true brace style 90 | * [camelcase][camelcase] - require camel case names 91 | * [comma-spacing][comma-spacing] - enforce spacing before and after comma 92 | * [comma-style][comma-style] - enforce one true comma style 93 | * [complexity][complexity] - specify the maximum cyclomatic complexity allowed in a program 94 | * [consistent-return][consistent-return] - require `return` statements to either always or never specify values 95 | * [consistent-this][consistent-this] - enforces consistent naming when capturing the current execution context 96 | * [curly][curly] - specify curly brace conventions for all control statements 97 | * [default-case][default-case] - require `default` case in `switch` statements 98 | * [dot-notation][dot-notation] - encourages use of dot notation whenever possible 99 | * [eol-last][eol-last] - enforce newline at the end of file, with no multiple empty lines 100 | * [eqeqeq][eqeqeq] - require the use of `===` and `!==` 101 | * [func-names][func-names] - require function expressions to have a name (off by default) 102 | * [global-strict][global-strict] - require or disallow the `"use strict"` pragma in the global scope 103 | * [guard-for-in][guard-for-in] - make sure `for-in` loops have an `if` statement 104 | * [handle-callback-err][handle-callback-err] - enforces error handling in callbacks 105 | * [key-spacing][key-spacing] - enforces spacing between keys and values in object literal properties 106 | * [max-depth][max-depth] - specify the maximum depth that blocks can be nested 107 | * [max-len][max-len] - specify the maximum length of a line in your program 108 | * [max-nested-callbacks][max-nested-callbacks] - specify the maximum depth callbacks can be nested 109 | * [max-params][max-params] - limits the number of parameters that can be used in the function declaration 110 | * [max-statements][max-statements] - specify the maximum number of statement allowed in a function 111 | * [new-cap][new-cap] - require a capital letter for constructors 112 | * [new-parens][new-parens] - disallow the omission of parentheses when invoking a constructor with no arguments 113 | * [quotes][quotes] - specify whether double or single quotes should be used 114 | * [radix][radix] - require use of the second argument for `parseInt()` 115 | * [semi][semi] - require or disallow use of semicolons instead of ASI 116 | * [space-after-function-name][space-after-function-name] - disallow a space after function names 117 | * [space-after-keywords][space-after-keywords] - require a space after certain keywords 118 | * [space-before-blocks][space-before-blocks] - disallow space before blocks 119 | * [space-in-parens][space-in-parens] - disallow spaces inside parentheses 120 | * [space-infix-ops][space-infix-ops] - require spaces around operators 121 | * [space-return-throw-case][space-return-throw-case] - require a space after `return`, `throw`, and `case` 122 | * [space-unary-ops][space-unary-ops] - Require or disallow spaces before/after unary operators (words on, nonwords off) 123 | * [spaced-line-comment][spaced-line-comment] - require or disallow a space immediately following the `//` in a line comment 124 | * [strict][strict] - require that all functions are run in strict mode 125 | * [use-isnan][use-isnan] - disallow comparisons with the value `NaN` 126 | * [valid-jsdoc][valid-jsdoc] - Ensure JSDoc comments are valid 127 | * [valid-typeof][valid-typeof] - Ensure that the results of typeof are compared against a valid string 128 | * [wrap-iife][wrap-iife] - require immediate function invocation to be wrapped in parentheses 129 | * [yoda][yoda] - require or disallow Yoda conditions 130 | 131 | ### warnings 132 | 133 | ### disabled 134 | 135 | * [env][env] - all env presets are disabled 136 | * [no-bitwise][no-bitwise] - disallow use of bitwise operators 137 | * [no-div-regex][no-div-regex] - disallow division operators explicitly at beginning of regular expression 138 | * [no-else-return][no-else-return] - disallow `else` after a `return` in an `if` 139 | * [no-eq-null][no-eq-null] - disallow comparisons to null without a type-checking operator 140 | * [no-extra-parens][no-extra-parens] - disallow unnecessary parentheses 141 | * [no-mixed-requires][no-mixed-requires] - disallow mixing regular variable and require declarations 142 | * [no-nested-ternary][no-nested-ternary] - disallow nested ternary expressions 143 | * [no-plusplus][no-plusplus] - disallow use of unary operators, `++` and `--` 144 | * [no-reserved-keys][no-reserved-keys] - disallow reserved words being used as object literal keys 145 | * [no-restricted-modules][no-restricted-modules] - restrict usage of specified node modules 146 | * [no-sync][no-sync] - disallow use of synchronous methods 147 | * [no-ternary][no-ternary] - disallow the use of ternary operators 148 | * [no-undefined][no-undefined] - disallow use of `undefined` variable 149 | * [no-void][no-void] - disallow use of `void` operator 150 | * [no-warning-comments][no-warning-comments] - disallow usage of configurable warning terms in comments - e.g. `TODO` or `FIXME` 151 | * [block-scoped-var][block-scoped-var] - treat `var` statements as if they were block scoped 152 | 153 | * [func-style][func-style] - enforces use of function declarations or expressions 154 | * [one-var][one-var] - allow just one var statement per function 155 | * [operator-assignment][operator-assignment] - require assignment operator shorthand where possible or prohibit it entirely 156 | * [padded-blocks][padded-blocks] - enforce padding within blocks 157 | * [quote-props][quote-props] - require quotes around object literal property names 158 | * [sort-vars][sort-vars] - sort variables within the same declaration block 159 | * [space-in-brackets][space-in-brackets] - require or disallow spaces inside brackets 160 | * [vars-on-top][vars-on-top] - requires to declare all vars on top of their containing scope 161 | * [wrap-regex][wrap-regex] - require regex literals to be wrapped in parentheses 162 | 163 | 164 | ## jscs 165 | 166 | * [validateIndentation][jscs-validate-indentation] 167 | * [maximumLineLength][jscs-maximum-line-lenth] 168 | * [requireSpaceAfterKeywords][jscs-require-space-after-keywords] 169 | * [disallowSpacesInFunctionDeclaration][jscs-spaces-in-functions] 170 | * [disallowSpacesInNamedFunctionExpression][jscs-spaces-in-functions] 171 | * [requireSpacesInFunctionDeclaration][jscs-spaces-in-functions] 172 | * [requireSpacesInNamedFunctionExpression][jscs-spaces-in-functions] 173 | * [requireSpacesInConditionalExpression][jscs-require-spaces-in-conditional-expression] 174 | * [disallowTrailingComma][jscs-disallow-trailing-comma] 175 | * [requireBlocksOnNewline][jscs-require-blocks-on-newline] 176 | * [requireCurlyBraces][jscs-require-curly-braces] 177 | * [disallowMultipleVarDecl][jscs-disallow-multiple-var-decl] 178 | * [disallowEmptyBlocks][jscs-disallow-empty-blocks] 179 | * [disallowSpaceAfterObjectKeys][jscs-disallow-space-after-object-keys] 180 | * [requireCommaBeforeLineBreak][jscs-require-comma-before-line-break] 181 | * [requireSpaceBeforeBinaryOperators][jscs-require-spaces-before-after-binary-operators] 182 | * [disallowSpaceAfterPrefixUnaryOperators][jscs-disallow-space-after-prefix-unary-operators] 183 | * [disallowKeywords][jscs-disallow-keywords] 184 | * [disallowMultipleLineBreaks][jscs-disallow-multiple-line-breaks] 185 | * [validateLineBreaks][jscs-validate-line-breaks] 186 | * [validateQuoteMarks][jscs-validate-quote-marks] 187 | * [disallowMixedSpacesAndTabs][jscs-disallow-mixed-spaces-and-tabs] 188 | * [disallowTrailingWhitespace][jscs-disallow-trailing-white-space] 189 | * [disallowKeywordsOnNewLine][jscs-disallow-keywords-on-new-line] 190 | * [requireLineFeedAtFileEnd][jscs-require-line-feed-at-file-end] 191 | * [requireDotNotation][jscs-require-dot-notation] 192 | 193 | ## JSHint 194 | 195 | JSHint is currently deprecated in favor of ESLint. It is only included in 196 | lint-trap because eslint does not yet support indentation-checking and 197 | indentation-checking appears to be less restrictive in jscs than desired. 198 | Once one of these other tools properly checks indentation, JSHint will be 199 | removed. 200 | 201 | There are only a few items set in our `.jshintrc` rules file. These settings 202 | are documented in our [JSHint Documentation][jshint-docs]. 203 | 204 | 205 | [globals]: eslint/globals.md 206 | [env]: eslint/env.md 207 | [no-alert]: eslint/no-alert.md 208 | [no-array-constructor]: https://github.com/eslint/eslint/blob/master/docs/rules/no-array-constructor.md 209 | [no-bitwise]: eslint/no-bitwise.md 210 | [no-caller]: eslint/no-caller.md 211 | [no-catch-shadow]: https://github.com/eslint/eslint/blob/master/docs/rules/no-catch-shadow.md 212 | [no-comma-dangle]: https://github.com/eslint/eslint/blob/master/docs/rules/no-comma-dangle.md 213 | [no-cond-assign]: https://github.com/eslint/eslint/blob/master/docs/rules/no-cond-assign.md 214 | [no-console]: https://github.com/eslint/eslint/blob/master/docs/rules/no-console.md 215 | [no-constant-condition]: https://github.com/eslint/eslint/blob/master/docs/rules/no-constant-condition.md 216 | [no-control-regex]: https://github.com/eslint/eslint/blob/master/docs/rules/no-control-regex.md 217 | [no-debugger]: https://github.com/eslint/eslint/blob/master/docs/rules/no-debugger.md 218 | [no-div-regex]: https://github.com/eslint/eslint/blob/master/docs/rules/no-div-regex.md 219 | [no-dupe-keys]: https://github.com/eslint/eslint/blob/master/docs/rules/no-dupe-keys.md 220 | [no-else-return]: https://github.com/eslint/eslint/blob/master/docs/rules/no-else-return.md 221 | [no-empty]: eslint/no-empty.md 222 | [no-empty-class]: https://github.com/eslint/eslint/blob/master/docs/rules/no-empty-class.md 223 | [no-empty-label]: https://github.com/eslint/eslint/blob/master/docs/rules/no-empty-label.md 224 | [no-eq-null]: https://github.com/eslint/eslint/blob/master/docs/rules/no-eq-null.md 225 | [no-eval]: https://github.com/eslint/eslint/blob/master/docs/rules/no-eval.md 226 | [no-ex-assign]: https://github.com/eslint/eslint/blob/master/docs/rules/no-ex-assign.md 227 | [no-extend-native]: eslint/no-extend-native.md 228 | [no-extra-bind]: https://github.com/eslint/eslint/blob/master/docs/rules/no-extra-bind.md 229 | [no-extra-boolean-cast]: https://github.com/eslint/eslint/blob/master/docs/rules/no-extra-boolean-cast.md 230 | [no-extra-parens]: https://github.com/eslint/eslint/blob/master/docs/rules/no-extra-parens.md 231 | [no-extra-semi]: https://github.com/eslint/eslint/blob/master/docs/rules/no-extra-semi.md 232 | [no-extra-strict]: https://github.com/eslint/eslint/blob/master/docs/rules/no-extra-strict.md 233 | [no-fallthrough]: https://github.com/eslint/eslint/blob/master/docs/rules/no-fallthrough.md 234 | [no-floating-decimal]: https://github.com/eslint/eslint/blob/master/docs/rules/no-floating-decimal.md 235 | [no-func-assign]: https://github.com/eslint/eslint/blob/master/docs/rules/no-func-assign.md 236 | [no-implied-eval]: https://github.com/eslint/eslint/blob/master/docs/rules/no-implied-eval.md 237 | [no-inline-comments]: https://github.com/eslint/eslint/blob/master/docs/rules/no-inline-comments.md 238 | [no-inner-declarations]: https://github.com/eslint/eslint/blob/master/docs/rules/no-inner-declarations.md 239 | [no-invalid-regexp]: https://github.com/eslint/eslint/blob/master/docs/rules/no-invalid-regexp.md 240 | [no-irregular-whitespace]: https://github.com/eslint/eslint/blob/master/docs/rules/no-irregular-whitespace.md 241 | [no-iterator]: https://github.com/eslint/eslint/blob/master/docs/rules/no-iterator.md 242 | [no-label-var]: https://github.com/eslint/eslint/blob/master/docs/rules/no-label-var.md 243 | [no-labels]: https://github.com/eslint/eslint/blob/master/docs/rules/no-labels.md 244 | [no-lone-blocks]: https://github.com/eslint/eslint/blob/master/docs/rules/no-lone-blocks.md 245 | [no-lonely-if]: https://github.com/eslint/eslint/blob/master/docs/rules/no-lonely-if.md 246 | [no-loop-func]: https://github.com/eslint/eslint/blob/master/docs/rules/no-loop-func.md 247 | [no-mixed-requires]: https://github.com/eslint/eslint/blob/master/docs/rules/no-mixed-requires.md 248 | [no-mixed-spaces-and-tabs]: https://github.com/eslint/eslint/blob/master/docs/rules/no-mixed-spaces-and-tabs.md 249 | [no-multi-spaces]: https://github.com/eslint/eslint/blob/master/docs/rules/no-multi-spaces.md 250 | [no-multi-str]: https://github.com/eslint/eslint/blob/master/docs/rules/no-multi-str.md 251 | [no-multiple-empty-lines]: https://github.com/eslint/eslint/blob/master/docs/rules/no-multiple-empty-lines.md 252 | [no-native-reassign]: https://github.com/eslint/eslint/blob/master/docs/rules/no-native-reassign.md 253 | [no-negated-in-lhs]: https://github.com/eslint/eslint/blob/master/docs/rules/no-negated-in-lhs.md 254 | [no-nested-ternary]: https://github.com/eslint/eslint/blob/master/docs/rules/no-nested-ternary.md 255 | [no-new]: eslint/no-new.md 256 | [no-new-func]: https://github.com/eslint/eslint/blob/master/docs/rules/no-new-func.md 257 | [no-new-object]: https://github.com/eslint/eslint/blob/master/docs/rules/no-new-object.md 258 | [no-new-require]: https://github.com/eslint/eslint/blob/master/docs/rules/no-new-require.md 259 | [no-new-wrappers]: https://github.com/eslint/eslint/blob/master/docs/rules/no-new-wrappers.md 260 | [no-obj-calls]: https://github.com/eslint/eslint/blob/master/docs/rules/no-obj-calls.md 261 | [no-octal]: https://github.com/eslint/eslint/blob/master/docs/rules/no-octal.md 262 | [no-octal-escape]: https://github.com/eslint/eslint/blob/master/docs/rules/no-octal-escape.md 263 | [no-path-concat]: https://github.com/eslint/eslint/blob/master/docs/rules/no-path-concat.md 264 | [no-plusplus]: eslint/no-plusplus.md 265 | [no-process-env]: https://github.com/eslint/eslint/blob/master/docs/rules/no-process-env.md 266 | [no-process-exit]: https://github.com/eslint/eslint/blob/master/docs/rules/no-process-exit.md 267 | [no-proto]: https://github.com/eslint/eslint/blob/master/docs/rules/no-proto.md 268 | [no-redeclare]: https://github.com/eslint/eslint/blob/master/docs/rules/no-redeclare.md 269 | [no-regex-spaces]: https://github.com/eslint/eslint/blob/master/docs/rules/no-regex-spaces.md 270 | [no-reserved-keys]: https://github.com/eslint/eslint/blob/master/docs/rules/no-reserved-keys.md 271 | [no-restricted-modules]: https://github.com/eslint/eslint/blob/master/docs/rules/no-restricted-modules.md 272 | [no-return-assign]: https://github.com/eslint/eslint/blob/master/docs/rules/no-return-assign.md 273 | [no-script-url]: https://github.com/eslint/eslint/blob/master/docs/rules/no-script-url.md 274 | [no-self-compare]: https://github.com/eslint/eslint/blob/master/docs/rules/no-self-compare.md 275 | [no-sequences]: https://github.com/eslint/eslint/blob/master/docs/rules/no-sequences.md 276 | [no-shadow]: https://github.com/eslint/eslint/blob/master/docs/rules/no-shadow.md 277 | [no-shadow-restricted-names]: https://github.com/eslint/eslint/blob/master/docs/rules/no-shadow-restricted-names.md 278 | [no-space-before-semi]: https://github.com/eslint/eslint/blob/master/docs/rules/no-space-before-semi.md 279 | [no-spaced-func]: https://github.com/eslint/eslint/blob/master/docs/rules/no-spaced-func.md 280 | [no-sparse-arrays]: https://github.com/eslint/eslint/blob/master/docs/rules/no-sparse-arrays.md 281 | [no-sync]: https://github.com/eslint/eslint/blob/master/docs/rules/no-sync.md 282 | [no-ternary]: https://github.com/eslint/eslint/blob/master/docs/rules/no-ternary.md 283 | [no-trailing-spaces]: https://github.com/eslint/eslint/blob/master/docs/rules/no-trailing-spaces.md 284 | [no-undef]: eslint/no-undef.md 285 | [no-undef-init]: https://github.com/eslint/eslint/blob/master/docs/rules/no-undef-init.md 286 | [no-undefined]: https://github.com/eslint/eslint/blob/master/docs/rules/no-undefined.md 287 | [no-underscore-dangle]: https://github.com/eslint/eslint/blob/master/docs/rules/no-underscore-dangle.md 288 | [no-unreachable]: https://github.com/eslint/eslint/blob/master/docs/rules/no-unreachable.md 289 | [no-unused-expressions]: https://github.com/eslint/eslint/blob/master/docs/rules/no-unused-expressions.md 290 | [no-unused-vars]: eslint/no-unused-vars.md 291 | [no-use-before-define]: eslint/no-use-before-define.md 292 | [no-void]: https://github.com/eslint/eslint/blob/master/docs/rules/no-void.md 293 | [no-warning-comments]: https://github.com/eslint/eslint/blob/master/docs/rules/no-warning-comments.md 294 | [no-with]: https://github.com/eslint/eslint/blob/master/docs/rules/no-with.md 295 | [no-wrap-func]: https://github.com/eslint/eslint/blob/master/docs/rules/no-wrap-func.md 296 | [block-scoped-var]: https://github.com/eslint/eslint/blob/master/docs/rules/block-scoped-var.md 297 | [brace-style]: https://github.com/eslint/eslint/blob/master/docs/rules/brace-style.md 298 | [camelcase]: eslint/camelcase.md 299 | [comma-spacing]: https://github.com/eslint/eslint/blob/master/docs/rules/comma-spacing.md 300 | [comma-style]: https://github.com/eslint/eslint/blob/master/docs/rules/comma-style.md 301 | [complexity]: eslint/complexity.md 302 | [consistent-return]: https://github.com/eslint/eslint/blob/master/docs/rules/consistent-return.md 303 | [consistent-this]: https://github.com/eslint/eslint/blob/master/docs/rules/consistent-this.md 304 | [curly]: https://github.com/eslint/eslint/blob/master/docs/rules/curly.md 305 | [default-case]: https://github.com/eslint/eslint/blob/master/docs/rules/default-case.md 306 | [dot-notation]: https://github.com/eslint/eslint/blob/master/docs/rules/dot-notation.md 307 | [eol-last]: https://github.com/eslint/eslint/blob/master/docs/rules/eol-last.md 308 | [eqeqeq]: eslint/eqeqeq.md 309 | [func-names]: eslint/func-names.md 310 | [func-style]: https://github.com/eslint/eslint/blob/master/docs/rules/func-style.md 311 | [global-strict]: eslint/global-strict.md 312 | [guard-for-in]: eslint/guard-for-in.md 313 | [handle-callback-err]: https://github.com/eslint/eslint/blob/master/docs/rules/handle-callback-err.md 314 | [key-spacing]: https://github.com/eslint/eslint/blob/master/docs/rules/key-spacing.md 315 | [max-depth]: eslint/max-depth.md 316 | [max-len]: https://github.com/eslint/eslint/blob/master/docs/rules/max-len.md 317 | [max-nested-callbacks]: https://github.com/eslint/eslint/blob/master/docs/rules/max-nested-callbacks.md 318 | [max-params]: eslint/max-params.md 319 | [max-statements]: https://github.com/eslint/eslint/blob/master/docs/rules/max-statements.md 320 | [new-cap]: eslint/new-cap.md 321 | [new-parens]: https://github.com/eslint/eslint/blob/master/docs/rules/new-parens.md 322 | [one-var]: https://github.com/eslint/eslint/blob/master/docs/rules/one-var.md 323 | [operator-assignment]: https://github.com/eslint/eslint/blob/master/docs/rules/operator-assignment.md 324 | [padded-blocks]: https://github.com/eslint/eslint/blob/master/docs/rules/padded-blocks.md 325 | [quote-props]: https://github.com/eslint/eslint/blob/master/docs/rules/quote-props.md 326 | [quotes]: https://github.com/eslint/eslint/blob/master/docs/rules/quotes.md 327 | [radix]: https://github.com/eslint/eslint/blob/master/docs/rules/radix.md 328 | [semi]: https://github.com/eslint/eslint/blob/master/docs/rules/semi.md 329 | [sort-vars]: https://github.com/eslint/eslint/blob/master/docs/rules/sort-vars.md 330 | [space-after-function-name]: https://github.com/eslint/eslint/blob/master/docs/rules/space-after-function-name.md 331 | [space-after-keywords]: https://github.com/eslint/eslint/blob/master/docs/rules/space-after-keywords.md 332 | [space-before-blocks]: https://github.com/eslint/eslint/blob/master/docs/rules/space-before-blocks.md 333 | [space-in-brackets]: https://github.com/eslint/eslint/blob/master/docs/rules/space-in-brackets.md 334 | [space-in-parens]: https://github.com/eslint/eslint/blob/master/docs/rules/space-in-parens.md 335 | [space-infix-ops]: https://github.com/eslint/eslint/blob/master/docs/rules/space-infix-ops.md 336 | [space-return-throw-case]: https://github.com/eslint/eslint/blob/master/docs/rules/space-return-throw-case.md 337 | [space-unary-ops]: https://github.com/eslint/eslint/blob/master/docs/rules/space-unary-ops.md 338 | [spaced-line-comment]: https://github.com/eslint/eslint/blob/master/docs/rules/spaced-line-comment.md 339 | [strict]: https://github.com/eslint/eslint/blob/master/docs/rules/strict.md 340 | [use-isnan]: https://github.com/eslint/eslint/blob/master/docs/rules/use-isnan.md 341 | [valid-jsdoc]: https://github.com/eslint/eslint/blob/master/docs/rules/valid-jsdoc.md 342 | [valid-typeof]: https://github.com/eslint/eslint/blob/master/docs/rules/valid-typeof.md 343 | [vars-on-top]: https://github.com/eslint/eslint/blob/master/docs/rules/vars-on-top.md 344 | [wrap-iife]: eslint/wrap-iife.md 345 | [wrap-regex]: https://github.com/eslint/eslint/blob/master/docs/rules/wrap-regex.md 346 | [yoda]: https://github.com/eslint/eslint/blob/master/docs/rules/yoda.md 347 | 348 | 349 | [jscs-validate-indentation]: jscs/validate-indentation.md 350 | [jscs-maximum-line-length]: jscs/maximum-line-length.md 351 | [jscs-require-space-after-keywords]: jscs/require-space-after-keywords.md 352 | [jscs-spaces-in-functions]: jscs/spaces-in-functions.md 353 | [jscs-require-spaces-in-conditional-expression]: jscs/require-spaces-in-conditional-expression.md 354 | [jscs-disallow-trailing-comma]: jscs/disallow-trailing-comma.md 355 | [jscs-require-blocks-on-newline]: jscs/require-blocks-on-newline.md 356 | [jscs-require-curly-braces]: jscs/require-curly-braces.md 357 | [jscs-disallow-multiple-var-decl]: jscs/disallow-multiple-var-decl.md 358 | [jscs-disallow-empty-blocks]: jscs/disallow-empty-blocks.md 359 | [jscs-disallow-space-after-object-keys]: jscs/disallow-space-after-object-keys.md 360 | [jscs-require-comma-before-line-break]: jscs/require-comma-before-line-break.md 361 | [jscs-require-spaces-before-after-binary-operators]: jscs/require-spaces-before-after-binary-operators.md 362 | [jscs-disallow-space-after-prefix-unary-operators]: jscs/disallow-space-after-prefix-unary-operators.md 363 | [jscs-disallow-keywords]: jscs/disallow-keywords.md 364 | [jscs-disallow-multiple-line-breaks]: jscs/disallow-multiple-line-breaks.md 365 | [jscs-validate-line-breaks]: jscs/validate-line-breaks.md 366 | [jscs-validate-quote-marks]: jscs/validate-quote-marks.md 367 | [jscs-disallow-mixed-spaces-and-tabs]: jscs/disallow-mixed-spaces-and-tabs.md 368 | [jscs-disallow-trailing-white-space]: jscs/disallow-trailing-white-space.md 369 | [jscs-disallow-keywords-on-new-line]: jscs/disallow-keywords-on-new-line.md 370 | [jscs-require-line-feed-at-file-end]: jscs/require-line-feed-at-file-end.md 371 | [jscs-require-dot-notation]: jscs/require-dot-notation.md 372 | 373 | 374 | [jshint-docs]: jshint/jshint.md 375 | -------------------------------------------------------------------------------- /docs/code-editor-support.md: -------------------------------------------------------------------------------- 1 | lint-trap plugin support in code editors 2 | ======================================== 3 | 4 | lint-trap is relatively new, but there already is a lint 5 | plugin available for SublimeLinter3 and a lint plugin is in 6 | the works for Syntastic. A Flycheck lint plugin for emacs 7 | will be developed next. If you work with emacs and 8 | JavaScript [email me][email-aandrade] to let me know so I 9 | can prioritize. 10 | 11 | ## SublimeText - SublimeLinter3 12 | 13 | - [SublimeLinter-contrib-lint-trap][sublimeLinter-contrib-lint-trap] 14 | 15 | The SublimeLinter plugin is not published yet in the 16 | PackageControl channel for SublimeLinter. In the meantime, 17 | you can install it by git cloning it to the Plugins folder: 18 | 19 | ``` 20 | cd ~/Library/Application\ Support/Sublime\ Text\ 3/Packages/ 21 | git clone git@github.com:uber/SublimeLinter-contrib-lint-trap.git 22 | ``` 23 | 24 | ## vim - syntastic 25 | 26 | There is a plugin for syntastic, but it is not yet in syntastic. To use it: 27 | - copy [`linttrap.vim`][linttrap.vim] to the `syntax\_checkers/javascript/` 28 | folder within the syntastic plugin folder 29 | - if you're using pathogen, this should be 30 | `~/.vim/bundle/syntastic/syntax\_checkers/javascript/` 31 | - enable the linttrap checker by adding this line to your vimrc: `let g:syntastic\_javascript\_checkers = ["linttrap"]` 32 | - make sure you have lint trap installed globally: `npm i -g lint-trap` 33 | 34 | ## Other Editors 35 | 36 | With linting rules moved to an npm module, other linters 37 | such as [flycheck][fc] for emacs, will be unable to to find 38 | the lint-trap linting rules for the project. 39 | 40 | In the short term this can be fixed by copying those files 41 | from lint-trap to your project and adding them to your 42 | `.gitignore`. From the root of your project: 43 | 44 | cp ./node_modules/lint-trap/rc/.{jscs,eslint,jshint}rc . 45 | rc=( .{jscs,eslint,jshint}rc ) 46 | for c in "${rc[@]}"; do echo $c >> .gitignore; done; 47 | 48 | Symlinking was preferred here, but SublimeLinter won't load 49 | symlinked linter configuration files. 50 | 51 | In the future, we will have lint-trap plugin for all the 52 | linting engines so that you don't need to install plugins 53 | for all the three linters that lint-trap supports. In the 54 | meantime, you should consult the documentation for the 55 | linting engine available for your code editor to discover 56 | how to enable support for jscs, jshint and eslint. 57 | 58 | 59 | [sublimeLinter-contrib-lint-trap]: https://github.com/uber/SublimeLinter-contrib-lint-trap 60 | [linttrap.vim]: https://github.com/uber/lint-trap/blob/master/extras/linttrap.vim 61 | 62 | [sl]: http://sublimelinter.readthedocs.org/ 63 | [syn]: https://github.com/scrooloose/syntastic 64 | [fc]: http://flycheck.readthedocs.org/ 65 | 66 | [sl-jshint]: https://github.com/SublimeLinter/SublimeLinter-jshint 67 | [sl-jscs]: https://github.com/SublimeLinter/SublimeLinter-jscs/ 68 | [sl-eslint]: https://github.com/roadhump/SublimeLinter-eslint 69 | 70 | [syn-jshint]: https://github.com/scrooloose/syntastic/wiki/JavaScript%3A---jshint 71 | [syn-jscs]: https://github.com/scrooloose/syntastic/wiki/JavaScript%3A---jscs 72 | [syn-eslint]: https://github.com/scrooloose/syntastic/wiki/JavaScript%3A---eslint 73 | 74 | [fs-javascript]: http://flycheck.readthedocs.org/en/latest/guide/languages.html#javascript 75 | -------------------------------------------------------------------------------- /docs/configuration.md: -------------------------------------------------------------------------------- 1 | Overriding Rules 2 | ================ 3 | 4 | Since lint-trap is meant to enforce good coding style and 5 | consistency across many projects from the same organization, 6 | you cannot turn rules off completely. However, when lint- 7 | trap to legacy projects without any linting or with 8 | different linting rules, it is useful to be able to 9 | downgrade the warning severity from `error` to `warning` so 10 | you can pay down linting technical debt over several 11 | commits. Because of this, lint-trap supports adding a 12 | `.lintrc` file to your project. 13 | 14 | The `.lintrc` file is a JSON file with a key for each linter 15 | with rules for which you wish to attenuate the lint message 16 | severity. 17 | 18 | For example, after adding lint-trap to a project, you should 19 | try to satisfy all the linting rules that are quick and easy 20 | to fix. Some linting violations might be so common that you 21 | want to deal with them in a later commit. One common lint- 22 | rule that is likely to affect many lines of code is eslint's 23 | [`func-names`][func-names] rule. If you want to attenuate 24 | this rule, simply add a `.lintrc` file to the root of your 25 | project: 26 | 27 | { 28 | "eslint": { 29 | "func-names": false 30 | } 31 | } 32 | 33 | `.lintrc` files can be added to subfolders and you can turn 34 | rules back to error level by setting the value for that rule 35 | to true. While `.lintrc` files can be put in subfolders, you 36 | really shouldn't have any projects that are so large that 37 | you need to use this feature. 38 | 39 | Rules cannot be turned off entirely in `.lintrc`, only 40 | attenuated. If there are rules that you feel you absolutely 41 | must override, you can do so within the files producing the 42 | error. If you find yourself ignoring or modifying a specific 43 | rule on a file by file basis very frequently, please check 44 | the [rules documentation][docs] to understand the technical 45 | reasons for why that rule has been encluded and enforced. If 46 | you disagree with the justifications outlined in the docs, 47 | [check the issues][issues] to see whether or not someone has 48 | already filed an issue raising the same concerns as you. If 49 | you still think your exception has merit, and no one has yes 50 | raised that issue, please see the Contributing section at 51 | the end of this README. 52 | 53 | If you want to override the rules for a specific linter 54 | inline, refer to the documentation for the linter producing 55 | the error or warning thrown. 56 | 57 | - [Configuring ESLint][configuring-eslint] 58 | - [Configuring JSHint][configuring-jshint] 59 | - [Configuring JSCS][configuring-jscs] 60 | 61 | 62 | [configuring-eslint]: http://eslint.org/docs/configuring/ 63 | [configuring-jshint]: http://www.jshint.com/docs/ 64 | [configuring-jscs]: https://github.com/jscs-dev/node-jscs#error-suppression 65 | [issues]: https://github.com/uber/lint-trap/issues 66 | 67 | [func-names]: https://github.com/eslint/eslint/blob/master/docs/rules/func-names.md 68 | -------------------------------------------------------------------------------- /docs/eslint/camelcase.md: -------------------------------------------------------------------------------- 1 | # camelcase 2 | 3 | "camelcase": 2 4 | 5 | Combining multiple styles together is a source of 6 | frustration in having to remember what the property names 7 | are. 8 | 9 | JavaScript does not have a good auto complete so knowing 10 | what the casing of a property is makes working with 11 | JavaScript easier. 12 | 13 | We should enforce camelcase. For the few use cases where you 14 | need to access keys on JSON blobs coming from an external 15 | process that has snake case you can use an exclude 16 | statement. 17 | 18 | [Official `camelcase` ESLint Rule Documentation][camelcase-docs] 19 | 20 | [camelcase-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/camelcase.md 21 | -------------------------------------------------------------------------------- /docs/eslint/complexity.md: -------------------------------------------------------------------------------- 1 | # complexity 2 | 3 | "complexity": [1, 10] 4 | 5 | Default cyclomatic complexity is 11, but it is only a 6 | warning. Cyclomatic complexity is not something that should 7 | be enforced per se but it's a great guideline to prompt the 8 | application developer to either break his code into smaller 9 | functions or add an exclusion comment. 10 | 11 | [Official `complexity` ESLint Rule Documentation][complexity-docs] 12 | 13 | [complexity-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/complexity.md 14 | -------------------------------------------------------------------------------- /docs/eslint/env.md: -------------------------------------------------------------------------------- 1 | # env 2 | 3 | "env": { 4 | "browser": false, 5 | "node": false, 6 | "amd": false, 7 | "mocha": false, 8 | "jasmine": false 9 | } 10 | 11 | None of the environment meta variables are used because we 12 | proscribe our own style, which applies to both server side 13 | and browser side code. The most important issue to be aware 14 | of here is how we handle globals explained below. 15 | 16 | [Official `env` ESLint Rule Documentation][env-docs] 17 | 18 | [env-docs]: http://eslint.org/docs/configuring/ 19 | -------------------------------------------------------------------------------- /docs/eslint/eqeqeq.md: -------------------------------------------------------------------------------- 1 | # eqeqeq 2 | 3 | "eqeqeq": 2 4 | 5 | Type coercion is a bad part of JavaScript and leads to real 6 | production bugs where you accidentally treat numbers and 7 | strings the same and then accidentally send a string instead 8 | of a number down a response. 9 | 10 | There are no good use cases for `==`. 11 | 12 | [Official `eqeqeq` ESLint Rule Documentation][eqeqeq-docs] 13 | 14 | [eqeqeq-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/eqeqeq.md 15 | -------------------------------------------------------------------------------- /docs/eslint/func-names.md: -------------------------------------------------------------------------------- 1 | # func-names 2 | 3 | "func-names": 2 4 | 5 | This rules requires you to name every function. The purpose 6 | of forcing you to always name every function is two-fold. 7 | First, this produces much more readable stack traces, since 8 | anonymous function calls require you to go to the source 9 | code and figure out what a function is trying to do. Naming 10 | the function makes it clear what it is trying to do. Second, 11 | naming your functions, makes it easy to pull out the 12 | function from where is is being called, thereby keeping 13 | indentation levels and line lengths low. 14 | 15 | One thing to keep in mind with this rule is that named 16 | function expressions are not hoisted into the current scope. 17 | For example, in the following line of code only `foo` will 18 | be available in the top-level scope, and `bar` will only be 19 | available within its own scope. 20 | 21 | var foo = function bar() {}; 22 | 23 | Furthermore, when you write inline functions (which are 24 | oftern anonymous functions), those functions are function 25 | expressions, not function declarations. What this means is 26 | that in a test file, it is perfectly acceptable to do the 27 | following since the function expressions with name `t` do 28 | not collide in the same scope as the token `test`: 29 | 30 | var test = require('tape'); 31 | 32 | test('foo', function t() { /* test foo */ }); 33 | test('bar', function t() { /* test bar */ }); 34 | test('baz', function t() { /* test baz */ }); 35 | 36 | If appropriate, you can also name those function expressions 37 | more explicitly, like `tFoo`, `tBar` and `tBaz`, but this is 38 | not necessary. 39 | 40 | In the case of anonymous callback functions used for the 41 | sole purpose of handling the first error argument, before 42 | calling another function with the remaining arguments, you 43 | can name it something like `handleError` or you can even use 44 | a helper function like the following to wrap the error if 45 | you find yourself writing lots of `handleError` callback 46 | functions in the same file: 47 | 48 | function wrapError(callbackFn, nextFn) { 49 | return function handleError(err) { 50 | if (err) { 51 | return callbackFn(err); 52 | } 53 | var args = arguments.slice(1); 54 | args.push(callbackFn); 55 | nextFn.apply(null, args); 56 | } 57 | } 58 | 59 | Overall, this rule that requires that all functions be named 60 | may same tedious at first, but it almost invariably forces 61 | you to refactor a lot of your code for the better, since it 62 | prevents you from abusing lambdas. If you encounter a use 63 | case where you think it is particularly challenging to 64 | satisfy this rule, feel free to open up an issue, and we'll 65 | help you figure out how refactor your code. 66 | 67 | [Official `func-names` ESLint Rule Documentation][func-names-docs] 68 | 69 | [func-names-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/func-names.md 70 | -------------------------------------------------------------------------------- /docs/eslint/global-strict.md: -------------------------------------------------------------------------------- 1 | # global-strict 2 | 3 | "global-strict": [2, "always"] 4 | 5 | The way commonJS works is that it wraps your file in a 6 | function expression. This means that something which looks 7 | like a global strict statement is actually isolated by a 8 | function expression. 9 | 10 | This means that the globalstrict bug is actually impossible 11 | and should not be an error. 12 | 13 | Strict mode also causes modifications to objects frozen with 14 | `Object.freeze` to throw TypeErrors, which is desirable. 15 | 16 | If you need a quick and easy way to add `'use strict'` to 17 | every JavaScript file in your project, you can use the 18 | execute the following shell script at the root of your 19 | project: 20 | 21 | git ls-files | grep \.js$ | 22 | while read f; do 23 | if (head -n3 "$f" | grep -Fv 'use strict' > /dev/null); then 24 | echo "$f" 25 | if (head -n1 "$f" | grep -Fv '#!/' > /dev/null); then 26 | sed -i '' -e '1 i\ 27 | '\''use strict'\''; 28 | ' "$f" 29 | else 30 | sed -i '' -e '2 i\ 31 | '\''use strict'\''; 32 | ' "$f" 33 | fi 34 | fi 35 | done 36 | 37 | Just save that in a file, `chmod +x` it, and then execute 38 | it. I recommend you double check that everything worked 39 | correctly with `git diff` before you lint again and commit 40 | your changes. 41 | 42 | [Official `global-strict` ESLint Rule Documentation][global-strict-docs] 43 | 44 | [global-strict-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/global-strict.md 45 | -------------------------------------------------------------------------------- /docs/eslint/globals.md: -------------------------------------------------------------------------------- 1 | # globals 2 | 3 | "globals": { 4 | "__dirname": true, 5 | "__filename": true, 6 | "require": true, 7 | "module": true 8 | }, 9 | 10 | This is the minimum set of globals required both in the 11 | browser and in NodeJS. Restricting globals to this set has 12 | numerous benefits: 13 | 14 | - It trains developers to never rely on global scope 15 | - It reduces the length of the scope chain that needs to be 16 | traversed since pointers to a global object are 17 | encountered sooner in the chain. 18 | - Allows globals to be mocked in testing environments. 19 | 20 | The only whitelisted globals are `__dirname`, `__filename`, 21 | `module` and `require`. The former two are the only globals 22 | that cannot be provided by the module system and the latter 23 | two are the only ones needed in order to use the module 24 | system. 25 | 26 | `exports` is not whitelisted. You should instead always 27 | access `exports` via a property on `module`. e.g. 28 | `module.exports = someObject;` 29 | 30 | For other globals such as `process`, `global`, `window`, 31 | `document`, you should include the following two modules in 32 | your project if needed. 33 | 34 | * [raynos/global][raynos-global] => `global`, `window` and `document` 35 | * [defunctzombie/node-process][defunctzombie-node-process] => `process` 36 | 37 | If you want any other globals you can require them. i.e. 38 | 39 | * `require('timers').setTimeout;` 40 | * `require('timers').clearTimeout;` 41 | * `require('timers').setInterval;` 42 | * `require('timers').clearInterval;` 43 | * `require('buffer').Buffer;` 44 | * `require('process');` 45 | * `require('global');` 46 | * `require('console');` 47 | 48 | [Official `globals` ESLint Rule Documentation][globals-docs] 49 | 50 | [globals-docs]: http://eslint.org/docs/configuring/ 51 | [raynos-global]: https://github.com/Raynos/global 52 | [defunctzombie-node-process]: https://github.com/defunctzombie/node-process 53 | -------------------------------------------------------------------------------- /docs/eslint/guard-for-in.md: -------------------------------------------------------------------------------- 1 | # guard-for-in 2 | 3 | "guard-for-in": 2 4 | 5 | Not using `Object.hasOwnProperty` can lead to subtle bugs. 6 | It's a good practice to make up for javascripts for loop 7 | semantics. 8 | 9 | However it should be noted that using `Object.keys(o)` is 10 | preferred instead of the for in loop. 11 | 12 | [Official `guard-for-in` ESLint Rule Documentation][guard-for-in-docs] 13 | 14 | [guard-for-in-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/guard-for-in.md 15 | -------------------------------------------------------------------------------- /docs/eslint/max-depth.md: -------------------------------------------------------------------------------- 1 | # max-depth 2 | 3 | "max-depth": [2, 4] 4 | 5 | Maximum depth is set to force you to not indent forever. 6 | Deeply indented code is generally a smell of bad code. 7 | 8 | You should break code out into helper functions. 9 | 10 | [Official `max-depth` ESLint Rule Documentation][max-depth-docs] 11 | 12 | [max-depth-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/max-depth.md 13 | -------------------------------------------------------------------------------- /docs/eslint/max-params.md: -------------------------------------------------------------------------------- 1 | # max-params 2 | 3 | "max-params": [2, 4] 4 | 5 | A function with more than 4 parameters does far too much. 6 | Developers should instead be carefully designing their 7 | functions via techniques such as currying/schonfinkelization 8 | or using an options object, especially if many parameters 9 | are optional. It is difficult to remember the interface of a 10 | function with too many positional parameters. 11 | 12 | [Official `max-params` ESLint Rule Documentation][max-params-docs] 13 | 14 | [max-params-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/max-params.md 15 | -------------------------------------------------------------------------------- /docs/eslint/new-cap.md: -------------------------------------------------------------------------------- 1 | # new-cap 2 | 3 | "new-cap": [2, { newIsCap: true, capIsNew: false }] 4 | 5 | All functions called with `new` must begin with a capital 6 | letter, but all functions that begin with a capital letter 7 | is need not be a constructor. 8 | 9 | Since our [global-strict][global-strict] rule requires that 10 | all files use strict mode via a singular `'use strict'` at 11 | the top of each file, forgetting to call a constructor with 12 | `new` will correctly lead to trying to assign a property to 13 | `this`, i.e. `undefined` which is a thrown exception. 14 | 15 | We should also discourage that people use `new` as part of 16 | their public interface, having to remember whether to call a 17 | module with `new` or not is frustrating in the same sense 18 | that having to remember whether a property is camelCase or 19 | snake_case. 20 | 21 | The `Error` function is one example of a function that need 22 | not be called with `new`. 23 | 24 | [Official `new-cap` ESLint Rule Documentation][new-cap-docs] 25 | 26 | [new-cap-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/new-cap.md 27 | [global-strict]: global-strict.md 28 | -------------------------------------------------------------------------------- /docs/eslint/no-alert.md: -------------------------------------------------------------------------------- 1 | # no-alert 2 | 3 | "no-alert": 2 4 | 5 | The `alert` function blocks JavaScript execution. Also, 6 | modal dialogs where the user can only ever press OK to 7 | acknowledge them is a huge UI anti-pattern. 8 | 9 | [Official `no-alert` ESLint Rule Documentation][no-alert-docs] 10 | 11 | [no-alert-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/no-alert.md 12 | -------------------------------------------------------------------------------- /docs/eslint/no-bitwise.md: -------------------------------------------------------------------------------- 1 | # no-bitwise 2 | 3 | "no-bitwise": 0, 4 | 5 | Bitwise operations are valuable sometimes. The hinting rules 6 | should not ban them as bitwise operators are not a source of 7 | bugs. 8 | 9 | [Official `no-bitwise` ESLint Rule Documentation][no-bitwise-docs] 10 | 11 | [no-bitwise-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/no-bitwise.md 12 | -------------------------------------------------------------------------------- /docs/eslint/no-caller.md: -------------------------------------------------------------------------------- 1 | # no-caller 2 | 3 | "no-caller": 2 4 | 5 | Banning calls to `arguments.caller` or `arguments.callee` is 6 | general performance hygiene. These features are banned in 7 | strict mode as well. 8 | 9 | [Official `no-caller` ESLint Rule Documentation][no-caller-docs] 10 | 11 | [no-caller-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/no-caller.md 12 | -------------------------------------------------------------------------------- /docs/eslint/no-empty.md: -------------------------------------------------------------------------------- 1 | # no-empty 2 | 3 | "no-empty": 2 4 | 5 | Warning about empty blocks helps with code refactors and 6 | dead code removal. 7 | 8 | It also helps you to catch empty `catch` blocks as those 9 | should be banned. 10 | 11 | [Official `no-empty` ESLint Rule Documentation][no-empty-docs] 12 | 13 | [no-empty-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/no-empty.md 14 | -------------------------------------------------------------------------------- /docs/eslint/no-extend-native.md: -------------------------------------------------------------------------------- 1 | # no-extend-native 2 | 3 | "no-extend-native": 2 4 | 5 | Extending native prototype or mutating them in any way is 6 | banned. Native prototype mutation is a terrible idea and 7 | breaks the npm modularity system by running into name 8 | collisions. 9 | 10 | If there are certain ES6 features you want polyfilled such 11 | as [`Array.prototype.find()`][array-prototype-find], you 12 | should use correctly written polyfills such as those written 13 | by [Paul Miller][paulmillr]. 14 | 15 | Below is a list of well-written polyfills. Feel free to add 16 | to this list, but please only add polyfills, whose code you 17 | have actually investigated so we can guarantee 18 | quality/correctness here. 19 | 20 | * [Array.prototype.find() Polyfill][array-prototype-find-polyfill] 21 | * [Array.prototype.findIndex() Polyfill][array-prototype-find-index-polyfill] 22 | 23 | [Official `no-extend-native` ESLint Rule Documentation][no-extend-native-docs] 24 | 25 | [no-extend-native-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/no-extend-native.md 26 | [paulmillr]: https://github.com/paulmillr 27 | [array-prototype-find-polyfill]: https://github.com/paulmillr/Array.prototype.find 28 | [array-prototype-find-index-polyfill]: https://github.com/paulmillr/Array.prototype.findIndex 29 | -------------------------------------------------------------------------------- /docs/eslint/no-new.md: -------------------------------------------------------------------------------- 1 | # no-new 2 | 3 | "no-new": 2 4 | 5 | If you find yourself calling a constructor without using the 6 | result then that's a bad constructor. 7 | 8 | A constructor function just allocates an object with a fixed 9 | set of fields, if it has a side effect then something went 10 | terribly wrong. 11 | 12 | [Official `no-new` ESLint Rule Documentation][no-new-docs] 13 | 14 | [no-new-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/no-new.md 15 | -------------------------------------------------------------------------------- /docs/eslint/no-plusplus.md: -------------------------------------------------------------------------------- 1 | # no-plusplus 2 | 3 | "no-plusplus": 0 4 | 5 | Banning `++` and `--` is a stylistic thing that has little 6 | value. It makes writing plain for loops far more tedious. 7 | 8 | [Official `no-plusplus` ESLint Rule Documentation][no-plusplus-docs] 9 | 10 | [no-plusplus-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/no-plusplus.md 11 | -------------------------------------------------------------------------------- /docs/eslint/no-undef.md: -------------------------------------------------------------------------------- 1 | # no-undef 2 | 3 | "no-undef": 2 4 | 5 | This one is really important. If your using an undefined 6 | variable then that is a typo and a bug. Having the hinter 7 | catch that reduces bugs significantly. 8 | 9 | Using global variables that are not defined in the file is 10 | also an anti-pattern. With `require` you can 11 | [`require('global')`][npm-global] if you need access to the 12 | global scope. More information on globals can be found in 13 | our [globals documentation][globals-docs]. 14 | 15 | Being explicit is always better. 16 | 17 | [Official `no-undef` ESLint Rule Documentation][no-undef-docs] 18 | 19 | [no-undef-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/no-undef.md 20 | [npm-global]: https://github.com/Raynos/global 21 | [globals-docs]: globals.md 22 | -------------------------------------------------------------------------------- /docs/eslint/no-unused-vars.md: -------------------------------------------------------------------------------- 1 | # no-unused-vars 2 | 3 | "no-unused-vars": [2, {"vars": "all", "args": "none"}] 4 | 5 | Checking for unused variables is very useful when it comes 6 | to refactoring. 7 | 8 | This allows you to remove dead code. 9 | 10 | Note that `unused` is set to `"vars"` only. Unused 11 | parameters are useful to define if you pass a callback or 12 | listener to another module and only use one out of N 13 | arguments. 14 | 15 | An `unused` function parameter is not actually a bug and nor 16 | is it something that needs to be cleaned up. 17 | 18 | [Official `no-unused-vars` ESLint Rule Documentation][no-unused-vars-docs] 19 | 20 | [no-unused-vars-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/no-unused-vars.md 21 | -------------------------------------------------------------------------------- /docs/eslint/no-use-before-define.md: -------------------------------------------------------------------------------- 1 | # no-use-before-define 2 | 3 | "no-use-before-define": [2, "nofunc"] 4 | 5 | Variables should be declared before they are used. 6 | JavaScript hoists all variable and function declarations to 7 | the top of the scope before it begins execution. For 8 | functions, this behavior works fine, but any variables 9 | hoisted before they are defined have their value set to 10 | `undefined`, which is always the wrong behavior. 11 | 12 | We exclude function declarations as those are hoisted with 13 | their value. i.e. if you use a function declaration before 14 | it's defined then you will always reference said function. 15 | The function declaration hoisting technique can also be used 16 | to avoid circular dependencies. 17 | 18 | [Official `no-use-before-define` ESLint Rule Documentation][no-use-before-define-docs] 19 | 20 | [no-use-before-define-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/no-use-before-define.md 21 | -------------------------------------------------------------------------------- /docs/eslint/wrap-iife.md: -------------------------------------------------------------------------------- 1 | # wrap-iife 2 | 3 | "wrap-iife": 2 4 | 5 | Having an immediately invoked function expression that is 6 | 200 lines long can be very easily mistaked for a function 7 | declaration. 8 | 9 | Forcing people to wrap it in braces distinquishes it from a 10 | function declaration and makes reasoning about code easier. 11 | 12 | [Official `wrap-iife` ESLint Rule Documentation][wrap-iife-docs] 13 | 14 | [wrap-iife-docs]: https://github.com/eslint/eslint/blob/master/docs/rules/wrap-iife.md 15 | -------------------------------------------------------------------------------- /docs/indentation.md: -------------------------------------------------------------------------------- 1 | Dynamic Indentation 2 | =================== 3 | 4 | lint-trap dynamically detects the indentation used in your 5 | project so you don't have to configure indentation settings 6 | manually. 7 | 8 | The decision to dynamically detect and support indentation 9 | on a project by project basis was done after lots of 10 | deliberation on which rules had technical merit (see 11 | [documentation][docs]) and which ones are merely opinions 12 | that are that should be consistently enforced. The choice 13 | between 2-spaces and 4-spaces was the most contentious rule 14 | and is one that results in endless bikeshedding discussions 15 | devoid of arguments with technical merit in favor of either 16 | preference. 17 | 18 | The algorithm used to determine the reference file from 19 | which indentation is detected is as follows: 20 | 21 | - use the file defined in the `main` property of 22 | `package.json` in the current working directory. 23 | - use index.js in the current working directory 24 | - use the first file resolved when expanding the path 25 | arguments with which lint-trap was run. 26 | 27 | If your `"main"` property in package.json is not properly 28 | configured, lint-trap is likely to error out. The algos 29 | for determining indentation of a project is still a bit 30 | flaky. This algorithm should become more stable over time 31 | and may even support support using settings defined in a 32 | .editorconfig 33 | See [set-rules.js][set-rules.js] for the implementation. 34 | 35 | [set-rules.js]: https://github.com/uber/lint-trap/blob/master/set-rules.js 36 | -------------------------------------------------------------------------------- /docs/jscs/disallow-empty-blocks.md: -------------------------------------------------------------------------------- 1 | # disallowEmptyBlocks 2 | 3 | "disallowEmptyBlocks": true 4 | 5 | Missing blocks are either a sign that a developer intended 6 | to do something, but forgot to implement it or its a sign 7 | that a developer is not following the [YAGNI][yagni] rule. 8 | The only case where it's appropriate to have an empty block 9 | is `function noop() {};`. Empty blocks are generally bad. 10 | Especially if you have an empty catch block. 11 | 12 | [yagni]: http://en.wikipedia.org/wiki/You_aren't_gonna_need_it 13 | -------------------------------------------------------------------------------- /docs/jscs/disallow-keywords-on-new-line.md: -------------------------------------------------------------------------------- 1 | # disallowKeywordsOnNewLine 2 | 3 | "disallowKeywordsOnNewLine": [ 4 | "else" 5 | ] 6 | 7 | Since the `requireBlocksOnNewline` rule is already enforced, 8 | allowing `else` to occur on a newline would reduce 9 | readability. Our if statements should be consistent. 10 | 11 | Favour: 12 | 13 | if (foo) { 14 | x 15 | } else { 16 | y 17 | } 18 | 19 | Do not use: 20 | 21 | if (foo) { 22 | x 23 | } 24 | else { 25 | y 26 | } 27 | -------------------------------------------------------------------------------- /docs/jscs/disallow-keywords.md: -------------------------------------------------------------------------------- 1 | # disallowKeywords 2 | 3 | "disallowKeywords": [ 4 | "with", 5 | "try", 6 | "catch", 7 | "finally" 8 | ] 9 | 10 | The `with` keyword is generally [considered harmful][with- 11 | considered-harmful], although there are a few extreme cases 12 | where it might be useful. [1][with1] [2][with2]. Also, 13 | `with` is banned in strict mode and thus banned in JSCS. 14 | 15 | In JavaScript, and especially NodeJS, you should only ever 16 | throw an error when a programmer has made and error (i.e. a 17 | bug). As such, `try`, `catch`, `finally` are also banned. In 18 | JavaScript one should not use try catch for control flow. 19 | For operational errors, the error should be returned to the 20 | callee, who can then determine how to handle it. 21 | 22 | The only exception where `try`/`catch`/`finally` are 23 | unavoidable (only because it is too late to go back and 24 | change history) is using `JSON.parse` with malformed JSON or 25 | `JSON.stringify` on an object with circular dependencies. 26 | 27 | In the case of parsing and stringifying JSON, we recommend 28 | that you use the [raynos/safe-json-parse][safe-json-parse]. 29 | If you are parsing after reading a JSON file or stringifying 30 | before writing a JSON file, you may want to use the 31 | [jprichardson/node-jsonfile][jsonfile] module to simplify 32 | things further. 33 | 34 | If you find yourself using a library that is throwing on 35 | anything other than programmer errors (logic errors), then 36 | it is a good sign that that library is poorly written and 37 | you should be using something else. 38 | 39 | [with-considered-harmful]: http://yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/ 40 | [with1]: http://webreflection.blogspot.com/2009/12/with-worlds-most-misunderstood.html 41 | [with2]: http://webreflection.blogspot.com/2009/12/with-some-good-example.html 42 | [safe-json-parse]: https://github.com/Raynos/safe-json-parse 43 | -------------------------------------------------------------------------------- /docs/jscs/disallow-mixed-spaces-and-tabs.md: -------------------------------------------------------------------------------- 1 | # disallowMixedSpacesAndTabs 2 | 3 | "disallowMixedSpacesAndTabs": true 4 | 5 | This rule requires no explanation. People that mix spaces 6 | and tabs are evil. 7 | -------------------------------------------------------------------------------- /docs/jscs/disallow-multiple-line-breaks.md: -------------------------------------------------------------------------------- 1 | # disallowMultipleLineBreaks 2 | 3 | "disallowMultipleLineBreaks": true 4 | 5 | If you require multiple line breaks (beyond one empty line), 6 | it is almost always an extra unnecessary line or a sign that 7 | you should include comments to explain in words why two 8 | sections are different. Whitespace alone conveys that one 9 | thing is separate from another, but does not convery *why*. 10 | -------------------------------------------------------------------------------- /docs/jscs/disallow-multiple-var-decl.md: -------------------------------------------------------------------------------- 1 | # disallowMultipleVarDecl 2 | 3 | "disallowMultipleVarDecl": true 4 | 5 | Var statements should be one per line. This rule enforces 6 | consistency and eliminates a source of runtime bugs when a 7 | comma-separated variable declaration list is modified, 8 | resulting in a missing comma or extraneous comma. This makes 9 | variable declarations easier to move around and refactor 10 | without messing around with commas and semicolons. 11 | -------------------------------------------------------------------------------- /docs/jscs/disallow-space-after-object-keys.md: -------------------------------------------------------------------------------- 1 | # disallowSpaceAfterObjectKeys 2 | 3 | "disallowSpaceAfterObjectKeys": true 4 | 5 | This rule enforces consistency that is pretty much the 6 | status quo in the JavaScript community and the format that 7 | pretty much every JSON stringifier uses. Object literals 8 | should be consistent in terms of whitespace. 9 | -------------------------------------------------------------------------------- /docs/jscs/disallow-space-after-prefix-unary-operators.md: -------------------------------------------------------------------------------- 1 | # disallowSpaceAfterPrefixUnaryOperators 2 | 3 | "disallowSpaceAfterPrefixUnaryOperators": [ 4 | "++", 5 | "--", 6 | "+", 7 | "-", 8 | "~", 9 | "!" 10 | ] 11 | 12 | Prefix unary operators are more readable when they are in 13 | close proximity to the object they operate on. 14 | -------------------------------------------------------------------------------- /docs/jscs/disallow-trailing-comma.md: -------------------------------------------------------------------------------- 1 | # disallowTrailingComma 2 | 3 | "disallowTrailingComma": true 4 | 5 | Trailing commas are always unnecessary and can cause parsing 6 | issues in some tools. Furthermore, trailing commas break in 7 | JSON, they also break in older browsers. We should not use 8 | them to make authoring JS & JSON consistent. 9 | -------------------------------------------------------------------------------- /docs/jscs/disallow-trailing-white-space.md: -------------------------------------------------------------------------------- 1 | # disallowTrailingWhitespace 2 | 3 | "disallowTrailingWhitespace" : true 4 | 5 | Trailing whitespaces are unnecessary and disallowing them 6 | altogether helps eliminate a common source of unnecessary 7 | diffs in git commits. 8 | -------------------------------------------------------------------------------- /docs/jscs/maximum-line-length.md: -------------------------------------------------------------------------------- 1 | # maximumLineLength 2 | 3 | "maximumLineLength": { 4 | "value": 80 5 | } 6 | 7 | We should have a line limit for consistency. Long lines make 8 | code harder to read and are usually indicative of poor 9 | quality code. Also, since 80-character lines has been a 10 | common standard across many languages for so long, many 11 | developers have optimized their development workflow to use 12 | 80-character code editor windows and panes. It's incredibly 13 | useful for being able to view multiple buffers side by side 14 | in either your editor or terminal. Writing code within 80 15 | characters is not hard. People have been doing it 16 | effectively for decades. Furthermore, research also suggest 17 | that the optimum line length for reading text is between 50 18 | and 60 characters. 19 | 20 | 21 | "maximumLineLength": { 22 | "allowUrlComments": true 23 | } 24 | 25 | Many URLs are longer than 80 characters long. Breaking them 26 | across multiple lines doesn't make sense because they no 27 | longer can easily be copied and pasted. Ideally 28 | 29 | If you have a URL that is longer than 80 characters, you can 30 | use a URL shortener like http://t.uber.com/ 31 | 32 | 33 | "maximumLineLength": { 34 | "allowUrlComments": false 35 | } 36 | 37 | Like lines of code, comments are also harder to read and may 38 | extend beyond the edges of a programmer's code editor 39 | windows/panes if they are too long. 40 | 41 | 42 | "maximumLineLength": { 43 | "allowRegex": false 44 | } 45 | 46 | If a regex is longer than 80 characters, it is too long to 47 | understand. No Regex should ever be this long. If you have a 48 | long Regex that you got from somewhere, it is recommended 49 | that you break it up over several lines with comments like 50 | the one on this page for detecting UTF-8: 51 | 52 | http://www.w3.org/International/questions/qa-forms-utf-8.en.php 53 | -------------------------------------------------------------------------------- /docs/jscs/require-blocks-on-newline.md: -------------------------------------------------------------------------------- 1 | # requireBlocksOnNewline 2 | 3 | "requireBlocksOnNewline": true 4 | 5 | This improves readability in 99% of cases. If your going to 6 | use a block then use a new line. putting a block in a single 7 | line encourages terse and unreadable code.Pretty much the 8 | only case in which readability is reduced is single-line 9 | `if` statements, and even then some will argue that `if` 10 | statements with only one line in its block is more readable 11 | with newlines. 12 | -------------------------------------------------------------------------------- /docs/jscs/require-comma-before-line-break.md: -------------------------------------------------------------------------------- 1 | # requireCommaBeforeLineBreak 2 | 3 | "requireCommaBeforeLineBreak": true 4 | 5 | This rule enforces consistency that is pretty much the 6 | status quo in the JavaScript community and the format that 7 | pretty much every JSON stringifier uses. 8 | -------------------------------------------------------------------------------- /docs/jscs/require-curly-braces.md: -------------------------------------------------------------------------------- 1 | # requireCurlyBraces 2 | 3 | "requireCurlyBraces": [ 4 | "if", 5 | "else", 6 | "for", 7 | "while", 8 | "do", 9 | "try", 10 | "catch", 11 | "default" 12 | ] 13 | 14 | Requiring curly braces makes explicit what code is being 15 | handled by the special keywords above, removing any 16 | potential ambiguities. Furthermore, requiring curly braces 17 | eliminates a common source of errors where code is 18 | inadvertedly included or excluded from code block due to 19 | indentation/formatting errors. We should encourage using 20 | curly braces as it avoids the multiple statements bug and 21 | also discourages long terse lines 22 | -------------------------------------------------------------------------------- /docs/jscs/require-dot-notation.md: -------------------------------------------------------------------------------- 1 | # requireDotNotation 2 | 3 | "requireDotNotation": true 4 | 5 | Property access should be consistent. All objects should be 6 | accessed via dot notation unless the object key cannot be 7 | expressed in dot notation. This makes code more readable and 8 | maximally minifiable. The only legitimate reason to revisit 9 | this rule is to allow us to use Google's Closure Compiler in 10 | advanced compression mode. 11 | -------------------------------------------------------------------------------- /docs/jscs/require-line-feed-at-file-end.md: -------------------------------------------------------------------------------- 1 | # requireLineFeedAtFileEnd 2 | 3 | "requireLineFeedAtFileEnd": true 4 | 5 | This improves readability so the last line is not at the 6 | edge of your code editor buffer right next to your modeline 7 | or window status bar without some whitespace. 8 | -------------------------------------------------------------------------------- /docs/jscs/require-space-after-keywords.md: -------------------------------------------------------------------------------- 1 | # requireSpaceAfterKeywords 2 | 3 | "requireSpaceAfterKeywords": [ 4 | "if", 5 | "else", 6 | "for", 7 | "while", 8 | "do", 9 | "switch", 10 | "return", 11 | "try", 12 | "catch" 13 | ] 14 | 15 | Requiring spaces after the keywords above makes the code 16 | more readable. 17 | -------------------------------------------------------------------------------- /docs/jscs/require-spaces-before-after-binary-operators.md: -------------------------------------------------------------------------------- 1 | # requireSpace(Before|After)BinaryOperators 2 | 3 | "requireSpaceBeforeBinaryOperators": [ 4 | "+", 5 | "-", 6 | "/", 7 | "*", 8 | "=", 9 | "==", 10 | "===", 11 | "!=", 12 | "!==" 13 | ], 14 | "requireSpaceAfterBinaryOperators": [ 15 | "+", 16 | "-", 17 | "/", 18 | "*", 19 | "=", 20 | "==", 21 | "===", 22 | "!=", 23 | "!==", 24 | "," 25 | ] 26 | 27 | Collectively, the above rules about requiring spacing around 28 | infix binary operators introduces whitespace that improves 29 | the readability of mathematical expressions and equality 30 | checks. 31 | -------------------------------------------------------------------------------- /docs/jscs/require-spaces-in-conditional-expression.md: -------------------------------------------------------------------------------- 1 | # requireSpacesInConditionalExpression 2 | 3 | "requireSpacesInConditionalExpression": true, 4 | -------------------------------------------------------------------------------- /docs/jscs/spaces-in-functions.md: -------------------------------------------------------------------------------- 1 | # Spaces in function expressions and function declarations 2 | 3 | "disallowSpacesInFunctionDeclaration": { 4 | "beforeOpeningRoundBrace": true 5 | }, 6 | "disallowSpacesInNamedFunctionExpression": { 7 | "beforeOpeningRoundBrace": true 8 | }, 9 | "requireSpacesInFunctionDeclaration": { 10 | "beforeOpeningCurlyBrace": true 11 | }, 12 | "requireSpacesInNamedFunctionExpression": { 13 | "beforeOpeningCurlyBrace": true 14 | }, 15 | 16 | Together with the eslint rule `func-names`, the above rules 17 | make sure that every single function (declaration or 18 | expression) is named and that there is a space between the 19 | `function` keyword and your function name, no space between 20 | your function name and the opening parenthesis and that 21 | there is always a space between the closing arguments 22 | parenthesis and the opening function body curly bracket. 23 | -------------------------------------------------------------------------------- /docs/jscs/validate-indentation.md: -------------------------------------------------------------------------------- 1 | # validateIndentation 2 | 3 | "validateIndentation": 4 4 | 5 | While the default value is 4, this rule is dynamically 6 | generated based on the indentation style already used in a 7 | project. 8 | 9 | **We may want to re-evaluate** this in the near future. 10 | Raynos recommends that we change to 8 spaces. 8 spaces 11 | forces you to allocated and abuse closures willy nilly. It 12 | also forces you to break nested functions out into function 13 | declarations. 14 | 15 | Making it harder to write messy code raises the average code 16 | quality. Making it harder to write closures removes memory 17 | leaks from real production code. 18 | -------------------------------------------------------------------------------- /docs/jscs/validate-line-breaks.md: -------------------------------------------------------------------------------- 1 | # validateLineBreaks 2 | 3 | "validateLineBreaks": "LF" 4 | 5 | All line breaks should follow the Multics, Unix and Unix- 6 | like system standard for newlines. 7 | -------------------------------------------------------------------------------- /docs/jscs/validate-quote-marks.md: -------------------------------------------------------------------------------- 1 | # validateQuoteMarks 2 | 3 | "validateQuoteMarks": { 4 | "mark": "'", 5 | "escape": true 6 | } 7 | 8 | We should have consistent quote marks. Mixing the two causes 9 | overhead and confusion. All strings should use single 10 | quotes, however double quotes are allowed only in cases 11 | where the string contains single quotes and the developer 12 | would like to avoid having to escape them. The goal is 13 | consistency with an exception made for readability. 14 | -------------------------------------------------------------------------------- /docs/jshint/jshint.md: -------------------------------------------------------------------------------- 1 | # JShint guidelines. 2 | 3 | JSHint is deprecated and will be removed entirely once 4 | ESLint or jscs properly checks indentation for a whole 5 | project. 6 | 7 | #### `"globalstrict": true` 8 | 9 | ESLint already takes care of this linting issue, but we need 10 | to keep this in the JSHint file to override the JSHint 11 | defaults. 12 | 13 | #### `"indent": 4` 14 | 15 | Indentation validation is handled by jscs. For whatever 16 | reason, JSHint does not properly validate indentation. 17 | 18 | #### `"globals": { ... }` 19 | 20 | We manually whitelist/blacklist the set of globals as the 21 | ones build into JSHint are either wrong or too aggressive. 22 | The only allowed globals are: `__dirname`, `__filename`, 23 | `require` and `module`. 24 | 25 | #### `"undef": true` 26 | 27 | This rule should be disabled. Unfortunately, there is a bug 28 | in JSHint that does not allow this to be turned off. 29 | -------------------------------------------------------------------------------- /docs/legacy-project-tips.md: -------------------------------------------------------------------------------- 1 | Tips for adding lint-trap to legacy projects 2 | ============================================ 3 | 4 | Adding lint-trap to legacy projects can feel distressing 5 | because lint trap will highlight dozens to hundreds of lint 6 | errors where your current code does not conform to the rules 7 | that lint-trap enforces. To make things easier, this guide 8 | provides some tips and tricks for fixing up a project 9 | gradually. 10 | 11 | 12 | Adding lint-trap to your project 13 | -------------------------------- 14 | 15 | To add lint-trap to your project: 16 | 17 | $ npm install --save-dev lint-trap 18 | 19 | Then add the "lint" property to the "scripts" property in 20 | your package.json. 21 | 22 | "scripts": { 23 | "lint": "lint-trap" 24 | } 25 | 26 | If you have any references in "scripts" to jshint or any 27 | other linters, like jscs and eslint, remove them. For all 28 | the entries in files like .jshintignore and .eslintignore 29 | and the entries in the `"excludeFiles"` property of your 30 | .jscsrc file. You can also remove all remove all the lint 31 | .rc files like .jscsrc, .jshintrc and .eslintrc. 32 | 33 | Don't forgot to update the `"test"` script and any other 34 | `"scripts"` entries that make reference to `npm run lint`. 35 | 36 | If your project's continuous integration is managed by 37 | jenkins and hosted on phabricator, you should check out the 38 | ./templates/web/content/package.json file in the `web/web- 39 | templates` repo on Phabricator. It is a solid example of 40 | best practices when setting up a project. 41 | 42 | 43 | Running lint-trap for the first time 44 | ------------------------------------ 45 | 46 | Once lint-trap is installed and your package.json is set up 47 | properly, you should be able to run lint-trap like so. 48 | 49 | $ npm run lint 50 | 51 | If all goes according well, you will probably be greeted by 52 | dozens to hundreds of lint errors depending on the size of 53 | your project. If you encounter any errors, double check that 54 | the `"main"` property in your package.json file is properly 55 | configured. lint-trap relies on the main property to find a 56 | reference file in your project and figure out the default 57 | indentation settings to use. 58 | 59 | Fixing all these issues will be daunting if you try to 60 | resolve them all at once and trying to fix them all at once 61 | will make merging and rebasing difficult. To help make the 62 | linting process more manageable, we're going to provide a 63 | some tips of tricks to break things down into bite size 64 | chunks. 65 | 66 | Pre-commit hook to lint only changed files 67 | ------------------------------------------ 68 | 69 | Using npm-scripts, some bash-fu and the [husky][husky] 70 | module, you can have lint-trap only lint changed *.js files 71 | as a pre-commit hook. To do this, first install husky as a 72 | dev dependency: 73 | 74 | $ npm install --save-dev husky 75 | 76 | Then add the following to your `package.json` file: 77 | 78 | "scripts": { 79 | ... 80 | "lint-changed": "lint-trap `git diff HEAD --name-only --diff-filter=ACMRTU | awk '/\.js$/' | awk -v path=$(git rev-parse --show-toplevel)/ '{print path$1}'`", 81 | "precommit": "npm run lint-changed", 82 | ... 83 | }, 84 | 85 | Husky will install git-hooks, including a pre-commit hook 86 | that will run `npm run precommit`, which in turn runs the 87 | `lint-changed` script. 88 | 89 | This script works like so: 90 | 91 | # show the files names of staged and unstaged changes 92 | # not including deleted files. 93 | git diff HEAD --name-only --diff-filter=ACMRTU 94 | 95 | # pipe that list to an awk filter that reduces that list 96 | # to javascript files only (*.js) 97 | | awk '/\.js$/' 98 | 99 | # pipe that list to an awk filter that expands the file 100 | # names to the full path from the top-level git directory. 101 | | awk -v path=$(git rev-parse --show-toplevel)/ '{print path$1}'` 102 | 103 | 104 | Use a .lintignore file 105 | ---------------------- 106 | 107 | After running lint-trap for the first time and seeing 108 | hundreds of errors, what you should not do is try to fix all 109 | those files at the same time. Instead, create a .lintignore 110 | file at the root of your project and add entires for every 111 | folder in your project. Use comments denoted by lines 112 | starting with `#` to seperate your permanent ignore rules 113 | like `node_modules/` or `coverage` from temporary rules to 114 | make linting more manageable like `lib/` and `test/`. 115 | 116 | Now if you run lint-trap again, you should see only the 117 | javascript files in the root of your project get linted 118 | (note: this will break finding your `"main"` file if it is 119 | not at your root). 120 | 121 | If you don't have too many files at your root, fixing them 122 | up and committing them should be a manageable task. Start 123 | there. 124 | 125 | After making your first commit, your team can basically 126 | adopt one of two basic approaches or a blend of both. The 127 | first and obvious option is to remove or modify rules in the 128 | .lintignore file one at a time until so that the errors from 129 | those additional files show up when you run lint-trap. Fix 130 | them and commit once again. Repeat until most code is 131 | fixedfiles one at a time. The second option is to adopt a 132 | rule with your team that the .lintignore file should be 133 | modifed so that lint-errors for any files touched that are 134 | altered or staged for commit should be fixed in the same 135 | commit. The reasoning here is that it's much easier to 136 | modify and fix a piece of code with which you've been 137 | editing recently and with which you are currently familiar. 138 | 139 | 140 | Use a lint plugins for your code editor 141 | --------------------------------------- 142 | 143 | lint-trap can be use in any code editor. A plugin already 144 | exists for SublimeLinter3 in Sublime Text 3 and a plugin for 145 | syntastic in vim is in progress. Plugins for flycheck in 146 | emacs and for WebStorm are planned. 147 | 148 | For more information on existing code editor plugins and 149 | workarounds for code editors for there isn't yet support, 150 | please see out [code editor support][code-editor-tools] 151 | docs. 152 | 153 | Use an .editorconfig file 154 | ------------------------- 155 | 156 | Consider adding an [editorconfig][editorconfig] file to your 157 | project and setting up your code editor to be 158 | [.editorconfig aware.] This plugin goes a long way in terms 159 | of making your test editor fix up whitespace issues in every 160 | file you open and save. 161 | You won't regret adding such a tool to your project. 162 | 163 | 164 | [husky]: https://github.com/typicode/husky 165 | [editorconfig]: http://editorconfig.org/ 166 | [code editor support]: code-editor-support.md 167 | [sublimeLinter-contrib-lint-trap]: https://github.com/uber/SublimeLinter-contrib-lint-trap 168 | [email-aandrade]: mailto:aandrade@uber.com?subject=lint-trap-emacs-flycheck-support 169 | -------------------------------------------------------------------------------- /docs/philosophy.md: -------------------------------------------------------------------------------- 1 | Philosophy 2 | ========== 3 | 4 | [A group eventually becomes its own worst enemy][group-enemy]. 5 | Every engineer that joins a company brings with them their 6 | own opinion of how code in some programming language should 7 | be written. Some languages are far more syntactically 8 | flexible that the breadth of opinions can get out of 9 | control. JavaScript is one of those languages. Furthermore, 10 | JavaScript has a lot of semantics and syntax warts that need 11 | to be controlled. 12 | 13 | When this linter was first written, Uber had approximately 14 | 300 engineers. A year prior there were 60 engineers. It's 15 | impossible to say how many engineers will be at Uber in a 16 | years time, but its safe to say that there will be many more 17 | engineers. The number of opinions grows with each engineer 18 | joining the company and rarely does any engineer joining the 19 | company have enough time to assimiliate the code styling of 20 | the group they are joining. The proliferation of many 21 | different coding styles while lots of code is being written 22 | and committed to production results in code which looks 23 | familiar only to the developer that first wrote it and 24 | unfamiliar to all the others. And as more developers touch a 25 | piece of code, it's likely to morph into something 26 | unfamiliar to everyone. lint-trap solves this problem. 27 | 28 | PEP-8 for Python and gofmt for golang make sure that all 29 | idiomatic code written in either of those two languages 30 | looks familiar to anyone fluent in each language, 31 | respectively. lint-trap aims to do the same for all 32 | JavaScript code written at Uber. Making sure all code feels 33 | familiar has many benefits such as making it easily to 34 | jump into an unfamiliar code base to create a hotfix when 35 | you're on call or making sure that the diffs for any commit 36 | is composed largely of changes in logic and code 37 | organization instead of changes in style and whitespace. 38 | 39 | [group-enemy]: http://www.shirky.com/writings/herecomeseverybody/group_enemy.html 40 | -------------------------------------------------------------------------------- /dupe-map.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.eol-last": "jscs.requireLineFeedAtFileEnd", 3 | "eslint.space-before-blocks": "jscs.requireSpacesInFunctionDeclaration", 4 | "eslint.no-extra-semi": "jshint.W032", 5 | "eslint.no-undef": "jshint.W117", 6 | "eslint.no-trailing-spaces": "jscs.disallowTrailingWhitespace", 7 | "eslint.semi": "jshint.W033", 8 | "eslint.no-multiple-empty-lines": "jscs.disallowMultipleLineBreaks", 9 | "eslint.max-len": "jscs.maximumLineLength", 10 | "eslint.space-after-function-name": "jscs.disallowSpacesInNamedFunctionExpression" 11 | } 12 | -------------------------------------------------------------------------------- /error-meter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var spy = require('through2-spy'); 3 | var extend = require('xtend'); 4 | 5 | function only(severity) { 6 | return function filter(msg) { 7 | return msg.type === severity; 8 | }; 9 | } 10 | 11 | function makeErrorMeter() { 12 | 13 | var metrics = { 14 | errorCode: 0, 15 | errorCount: 0, 16 | warningCount: 0 17 | }; 18 | 19 | var errors = only('error'); 20 | var warnings = only('warning'); 21 | 22 | function observer(message) { 23 | metrics.errorCount += message.errors.filter(errors).length; 24 | metrics.warningCount += message.errors.filter(warnings).length; 25 | } 26 | 27 | function getMetrics() { 28 | if (metrics.errorCount > 0) { 29 | metrics.errorCode = 1; 30 | } 31 | return extend(metrics); 32 | } 33 | 34 | var s = spy.obj(observer); 35 | 36 | s.on('finish', function onFinish() { 37 | s.emit('end'); 38 | }); 39 | 40 | return { 41 | meter: s, 42 | getMetrics: getMetrics 43 | }; 44 | } 45 | 46 | module.exports = makeErrorMeter; 47 | -------------------------------------------------------------------------------- /error-to-file-transform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var through = require('through2'); 3 | 4 | function makeFileStream(type, files) { 5 | var currentFile = null; 6 | var fileErrors = []; 7 | 8 | function pushEmpty(file) { 9 | this.push({ 10 | type: 'file', 11 | file: file, 12 | linter: type, 13 | errors: [] 14 | }); 15 | } 16 | 17 | function pushUpToAndIncludingCurrentFile() { 18 | // Emit a file message for all previous files that did not contain 19 | // errors 20 | var index = files.indexOf(currentFile); 21 | files.slice(0, index).forEach(pushEmpty.bind(this)); 22 | 23 | // Emit a file message for the current file with errors 24 | 25 | this.push({ 26 | type: 'file', 27 | file: currentFile, 28 | linter: type, 29 | errors: fileErrors 30 | }); 31 | 32 | // Assign files to the remaining files. 33 | files = files.slice(index + 1); 34 | 35 | // Reset the accumulated current file errors array. 36 | fileErrors = []; 37 | } 38 | 39 | function transform(errorMessage, enc, callback) { 40 | // currentFile is undefined for the first message received. 41 | if (!currentFile) { 42 | currentFile = errorMessage.file; 43 | } 44 | 45 | // If we've moved on to a different file, emit stream data for the 46 | // previous file. 47 | if (errorMessage.file !== currentFile) { 48 | pushUpToAndIncludingCurrentFile.call(this); 49 | currentFile = errorMessage.file; 50 | } 51 | 52 | // If the error is for the previous file, just add it to the current 53 | // accumulator array. 54 | if (errorMessage.file === currentFile) { 55 | fileErrors.push(errorMessage); 56 | } 57 | 58 | currentFile = errorMessage.file; 59 | callback(); 60 | } 61 | 62 | function flush(callback) { 63 | if (currentFile) { 64 | pushUpToAndIncludingCurrentFile.call(this); 65 | } 66 | // Emit a file message for all remaining files that do not contain 67 | // errors 68 | files.forEach(pushEmpty.bind(this)); 69 | 70 | // Terminate stream 71 | this.push(null); 72 | callback(); 73 | } 74 | 75 | return through({objectMode: true}, transform, flush); 76 | } 77 | 78 | module.exports = makeFileStream; 79 | -------------------------------------------------------------------------------- /extras/linttrap.vim: -------------------------------------------------------------------------------- 1 | "============================================================================ 2 | "File: linttrap.vim 3 | "Description: Zero-config JavaScript syntax checker (eslint, jscs, jshint) 4 | "Maintainer: Andrew de Andrade 5 | "License: This program is free software. It comes without any warranty, 6 | " to the extent permitted by applicable law. You can redistribute 7 | " it and/or modify it under the terms of the Do What The Fuck You 8 | " Want To Public License, Version 2, as published by Sam Hocevar. 9 | " See http://sam.zoy.org/wtfpl/COPYING for more details. 10 | "============================================================================ 11 | 12 | if exists('g:loaded_syntastic_javascript_linttrap_checker') 13 | finish 14 | endif 15 | let g:loaded_syntastic_javascript_linttrap_checker = 1 16 | 17 | if !exists('g:syntastic_javascript_linttrap_sort') 18 | let g:syntastic_javascript_linttrap_sort = 1 19 | endif 20 | 21 | let s:save_cpo = &cpo 22 | set cpo&vim 23 | 24 | function! SyntaxCheckers_javascript_linttrap_IsAvailable() dict 25 | if !executable(self.getExec()) 26 | return 0 27 | endif 28 | 29 | let ver = self.getVersion() 30 | 31 | return syntastic#util#versionIsAtLeast(ver, [0, 4, 0]) 32 | endfunction 33 | 34 | function! SyntaxCheckers_javascript_linttrap_GetLocList() dict 35 | 36 | let makeprg = self.makeprgBuild({ 'args_before': '--reporter=compact' }) 37 | 38 | if !exists('b:syntastic_checkers') 39 | let b:syntastic_checkers = ["linttrap"] 40 | endif 41 | 42 | let errorformat = 43 | \ '%E%f: line %l\, col %c\, Error - %m,' . 44 | \ '%W%f: line %l\, col %c\, Warning - %m' 45 | 46 | let loclist = SyntasticMake({ 47 | \ 'makeprg': makeprg, 48 | \ 'errorformat': errorformat, 49 | \ 'postprocess': ['guards'] }) 50 | 51 | for e in loclist 52 | let e['col'] += 1 53 | endfor 54 | 55 | return loclist 56 | endfunction 57 | 58 | call g:SyntasticRegistry.CreateAndRegisterChecker({ 59 | \ 'filetype': 'javascript', 60 | \ 'name': 'linttrap', 61 | \ 'exec': 'lint-trap' }) 62 | 63 | let &cpo = s:save_cpo 64 | unlet s:save_cpo 65 | 66 | " vim: set et sts=4 sw=4: 67 | -------------------------------------------------------------------------------- /get-javascript-files.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var createFileStream = require('fstream-ignore'); 4 | var fs = require('fs'); 5 | 6 | var jsFileRE = /\.js$/; 7 | var ignoreFiles = [ 8 | '.lintignore', 9 | '.gitignore', 10 | '.jshintignore', 11 | '.eslintignore', 12 | '.jscsignore' 13 | ]; 14 | 15 | var dotGitFileRE = /^\.git/; 16 | 17 | function getJavaScriptFiles(folderPath, callback) { 18 | 19 | fs.stat(folderPath, function statCallback(err, stat) { 20 | if (err) { 21 | return callback(err); 22 | } 23 | if (stat.isDirectory()) { 24 | var files = []; 25 | 26 | var fstream = createFileStream({ 27 | path: folderPath, 28 | ignoreFiles: ignoreFiles 29 | }); 30 | 31 | fstream.addIgnoreRules(['node_modules/']); 32 | 33 | fstream.on('child', function onChild(c) { 34 | var f = c.path.substr(c.root.path.length + 1); 35 | if (jsFileRE.test(f) && !dotGitFileRE.test(f)) { 36 | files.push(f); 37 | } 38 | }); 39 | 40 | fstream.on('end', function onEnd() { 41 | files = files.map(function expandFilePath(f) { 42 | return path.resolve(folderPath, f); 43 | }); 44 | 45 | callback(null, files); 46 | }); 47 | 48 | fstream.on('error', callback); 49 | } else { 50 | callback(null, folderPath); 51 | } 52 | }); 53 | } 54 | 55 | module.exports = getJavaScriptFiles; 56 | -------------------------------------------------------------------------------- /group-file-messages-transform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var through = require('through2'); 3 | 4 | /** 5 | * Through stream that takes messages with all the errors on a per linter and 6 | * per file basis and when if receives all the file error messages from all 7 | * linters, it emits a consolidated file error message. 8 | * 9 | * e.g. given 3 linters X, Y and Z and 3 files A, B and C, this takes the 10 | * following 9 input messages: 11 | * XA, XB, XC, YA, YB, YC, ZA, ZB, ZC 12 | * 13 | * ... and returns a stream that emits the following 3 messages: 14 | * A[XYZ], B[XYZ], C[XYZ] 15 | * 16 | * @param {[type]} linters [description] 17 | * @return {[type]} [description] 18 | */ 19 | function clusterFileMessages(linters) { 20 | 21 | var fileMessages = {}; 22 | 23 | function transform(message, enc, callback) { 24 | 25 | function acc(memo, fileMessage) { 26 | return memo.concat(fileMessage.errors); 27 | } 28 | 29 | if (fileMessages[message.file]) { 30 | fileMessages[message.file].push(message); 31 | 32 | if (fileMessages[message.file].length === linters.length) { 33 | 34 | var allFileErrors = fileMessages[message.file].reduce(acc, []); 35 | 36 | this.push({ 37 | type: 'file', 38 | file: message.file, 39 | errors: allFileErrors 40 | }); 41 | 42 | delete fileMessages[message.file]; 43 | } 44 | } else { 45 | fileMessages[message.file] = [message]; 46 | } 47 | callback(); 48 | } 49 | 50 | function flush(callback) { 51 | this.push(null); 52 | callback(); 53 | } 54 | 55 | return through.obj(transform, flush); 56 | } 57 | 58 | module.exports = clusterFileMessages; 59 | -------------------------------------------------------------------------------- /json-reporter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function printJSON(fileMessages) { 4 | fileMessages = fileMessages.filter(removeEmpty); 5 | 6 | function removeEmpty(fileMessage) { 7 | return fileMessage.errors.length > 0; 8 | } 9 | 10 | var json = { 11 | files: fileMessages 12 | }; 13 | 14 | return JSON.stringify(json, null, 4) + '\n'; 15 | } 16 | 17 | module.exports = printJSON; 18 | -------------------------------------------------------------------------------- /lint-stream.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var spawn = require('child_process').spawn; 3 | var JSONStream = require('jsonstream2'); 4 | var path = require('path'); 5 | var es = require('event-stream'); 6 | var which = require('npm-which'); 7 | var through2 = require('through2'); 8 | var makeFileStream = require('./error-to-file-transform'); 9 | var clusterFileMessages = require('./group-file-messages-transform'); 10 | var severityTransform = require('./severity-transform'); 11 | var process = require('process'); 12 | var makeDedupeTransform = require('./dedupe-transform'); 13 | var console = require('console'); 14 | 15 | function makeArgs(type, files, readFromStdin) { 16 | var args = [ 17 | '--config', 18 | path.resolve(__dirname, 'rc', '.' + type + 'rc'), 19 | (type === 'eslint') ? '--format' : '--reporter', 20 | path.resolve(__dirname, 'reporters', type + '.js') 21 | ]; 22 | 23 | if (readFromStdin) { 24 | args.push(type === 'eslint' ? '--stdin' : '-'); 25 | } else { 26 | args = args.concat(files); 27 | } 28 | 29 | return args; 30 | } 31 | 32 | function getBinPath(type, callback) { 33 | which(type, {cwd: __dirname}, callback); 34 | } 35 | 36 | function makeRelativePathFn(dir) { 37 | return function makeRelativePath(file) { 38 | return (dir === file) ? 39 | path.relative(path.dirname(dir), file) : 40 | path.relative(dir, file); 41 | }; 42 | } 43 | 44 | function makeRelativePathTransform(makeRelativePath) { 45 | return through2.obj(function transform(message, enc, callback) { 46 | // If commondir and message.file are the same, we are linting a 47 | // single file. This makes sure that the file name is printed in 48 | // that case. 49 | message.file = makeRelativePath(message.file); 50 | this.push(message); 51 | callback(); 52 | }); 53 | } 54 | 55 | function execLinter(type, dir, files, opts) { 56 | 57 | var makeRelativePath = makeRelativePathFn(dir); 58 | 59 | var lintMessages = makeRelativePathTransform(makeRelativePath); 60 | 61 | getBinPath(type, getBinPathCallback); 62 | 63 | function getBinPathCallback(err, binPath) { 64 | if (err) { 65 | return lintMessages.emit('error', err); 66 | } 67 | 68 | var args = makeArgs(type, files, opts.stdin); 69 | var lintProcess = spawn(binPath, args); 70 | 71 | if (opts.stdin) { 72 | process.stdin.pipe(lintProcess.stdin); 73 | 74 | process.stdin.once('end', function onStdinEnd() { 75 | lintProcess.stdin.end(); 76 | }); 77 | } 78 | 79 | lintProcess.stdout 80 | .pipe(JSONStream.parse('*')) 81 | .pipe(severityTransform()) 82 | .pipe(lintMessages) 83 | .on('error', onError); 84 | 85 | lintProcess.stderr.on('data', onError); 86 | 87 | function onError(linterErr) { 88 | /*eslint-disable*/ 89 | console.error('linting failed for', type); 90 | console.error(linterErr.toString()); 91 | process.exit(1); 92 | /*eslint-enable*/ 93 | } 94 | } 95 | 96 | return lintMessages; 97 | } 98 | 99 | function lintStream(type, dir, files, opts) { 100 | var fileStream = makeFileStream(type, files.map(makeRelativePathFn(dir))); 101 | execLinter(type, dir, files, opts).pipe(fileStream); 102 | return fileStream; 103 | } 104 | 105 | function lintTrapStream(linters) { 106 | linters = linters || ['jscs', 'jshint', 'eslint']; 107 | 108 | return function lint(files, dir, opts) { 109 | var streams = linters.map(function initLinter(linterName) { 110 | return lintStream(linterName, dir, files, opts); 111 | }); 112 | 113 | var mergedLintStream = es.merge.apply(es, streams); 114 | var finalStream = makeDedupeTransform(); 115 | 116 | mergedLintStream 117 | .pipe(clusterFileMessages(linters)) 118 | .pipe(finalStream); 119 | 120 | return finalStream; 121 | }; 122 | } 123 | 124 | module.exports = lintTrapStream; 125 | -------------------------------------------------------------------------------- /lint-trap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var lintStream = require('./lint-stream')(); 3 | var printStylish = require('./stylish-reporter'); 4 | var printCheckstyle = require('./checkstyle-reporter'); 5 | var printJSON = require('./json-reporter'); 6 | var async = require('async'); 7 | var getJavaScriptFiles = require('./get-javascript-files'); 8 | var es = require('event-stream'); 9 | var process = require('process'); 10 | var extend = require('xtend'); 11 | var makeErrorMeter = require('./error-meter'); 12 | var partial = require('partial'); 13 | var printCompact = require('./compact-reporter'); 14 | var path = require('path'); 15 | var commondir = require('commondir'); 16 | var setRules = require('./set-rules'); 17 | 18 | function findFiles(paths, callback) { 19 | async.reduce(paths, [], function accumulator(memo, pathArg, done) { 20 | getJavaScriptFiles(pathArg, function findJSFilesCallback(err, jsfiles) { 21 | if (err) { 22 | return done(err); 23 | } 24 | done(null, memo.concat(jsfiles)); 25 | }); 26 | }, callback); 27 | } 28 | 29 | function isRelativePath(p) { 30 | return p.charAt(0) === '/'; 31 | } 32 | 33 | function makeWriter(printer, callback) { 34 | function writer(err, fileMessages) { 35 | if (err) { 36 | return callback(err); 37 | } 38 | 39 | // Convert absolute paths to relative paths so that 40 | // syntastic plugin works properly. 41 | var files = fileMessages.map(function getFilepath(msg) { 42 | return msg.file; 43 | }); 44 | 45 | if (files.every(isRelativePath)) { 46 | var dir = commondir(files); 47 | 48 | fileMessages.forEach(function fixPaths(msg) { 49 | var p = path.relative(dir, msg.file); 50 | msg.file = p.length > 0 ? p : path.basename(msg.file); 51 | }); 52 | } 53 | 54 | var output = printer(fileMessages); 55 | process.stdout.write(output); 56 | } 57 | return es.writeArray(writer); 58 | } 59 | 60 | function onEnd(errorMeter, callback) { 61 | var metrics = errorMeter.getMetrics(); 62 | var err = metrics.errorCode === 1 ? 63 | new Error('Lint errors encountered') : null; 64 | callback(err); 65 | } 66 | 67 | function lint(jsfiles, opts, callback) { 68 | jsfiles.sort(); 69 | var dir = commondir(jsfiles); 70 | setRules(dir, opts.lineLength, onRulesSet); 71 | 72 | function onRulesSet(err) { 73 | if (err) { 74 | return callback(err); 75 | } 76 | 77 | var uberLintStream = lintStream(jsfiles, dir, opts); 78 | var errorMeter = makeErrorMeter(); 79 | var writer; 80 | var r = opts.reporter; 81 | 82 | writer = (r === 'stylish') ? makeWriter(printStylish, callback) : 83 | (r === 'checkstyle') ? makeWriter(printCheckstyle, callback) : 84 | (r === 'json') ? makeWriter(printJSON, callback) : 85 | (r === 'compact') ? makeWriter(printCompact, callback) : null; 86 | 87 | if (!writer) { 88 | return callback(new Error('Unknown reporter: ' + r)); 89 | } 90 | 91 | uberLintStream 92 | .pipe(errorMeter.meter) 93 | .pipe(writer); 94 | 95 | uberLintStream.once('end', partial(onEnd, errorMeter, callback)); 96 | } 97 | } 98 | 99 | function run(opts, callback) { 100 | opts = extend({ 101 | files: [], 102 | reporter: 'stylish', 103 | stdin: false, 104 | lineLength: 80 105 | }, opts); 106 | 107 | if (opts.lineLength > 120) { 108 | var errMsg = 'Line length limit cannot be greater than 120 characters'; 109 | return callback(new Error(errMsg)); 110 | } 111 | 112 | if (opts.stdin) { 113 | lint(['stdin'], opts, callback); 114 | } else { 115 | findFiles(opts.files, function fileFilesCallback(err, files) { 116 | if (err) { 117 | return callback(err); 118 | } 119 | if (files.length === 0) { 120 | return callback(new Error('no files found')); 121 | } 122 | lint(files, opts, callback); 123 | }); 124 | } 125 | } 126 | 127 | module.exports = run; 128 | -------------------------------------------------------------------------------- /load-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var findParentDir = require('find-parent-dir'); 4 | var jf = require('jsonfile'); 5 | var async = require('async'); 6 | var deepExtend = require('deep-extend'); 7 | 8 | var memoizedReadJSONFile = async.memoize(jf.readFile); 9 | 10 | function loadSeverityConfig(folder, callback) { 11 | 12 | findParentDir(folder, '.lintrc', function findCallback(findErr, dir) { 13 | if (findErr) { 14 | return callback(findErr); 15 | } 16 | 17 | if (!dir) { 18 | return callback(null, {}); 19 | } 20 | 21 | function readJSONCallback(jsonErr, config) { 22 | if (jsonErr) { 23 | return callback(jsonErr); 24 | } 25 | var parent = path.dirname(dir); 26 | 27 | // null condition 28 | if (parent === '.' || parent === '/') { 29 | return callback(null, config); 30 | } 31 | 32 | function recurCallback(recurErr, parentConfig) { 33 | if (recurErr) { 34 | return callback(recurErr); 35 | } 36 | 37 | callback(null, deepExtend(parentConfig, config)); 38 | } 39 | 40 | // recurse 41 | loadSeverityConfig(parent, recurCallback); 42 | } 43 | 44 | memoizedReadJSONFile(path.join(dir, '.lintrc'), readJSONCallback); 45 | }); 46 | } 47 | 48 | module.exports = async.memoize(loadSeverityConfig); 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lint-trap", 3 | "version": "1.0.1", 4 | "description": "JavaScript linter module for Uber projects", 5 | "author": "Andrew de Andrade ", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/uber/lint-trap" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/uber/lint-trap/issues" 12 | }, 13 | "main": "./lint-trap.js", 14 | "keywords": [ 15 | "lint", 16 | "jshint", 17 | "jscs", 18 | "eslint", 19 | "uber" 20 | ], 21 | "bin": { 22 | "lint-trap": "./bin/lint-trap.js" 23 | }, 24 | "dependencies": { 25 | "async": "~0.9.0", 26 | "chalk": "^0.5.1", 27 | "commondir": "0.0.1", 28 | "debug": "^2.1.1", 29 | "deep-extend": "~0.3.2", 30 | "dotty": "0.0.2", 31 | "editorconfig": "^0.12.1", 32 | "eslint": "~0.13.0", 33 | "event-stream": "^3.2.2", 34 | "extend": "^2.0.0", 35 | "find-parent-dir": "^0.3.0", 36 | "fstream-ignore": "~1.0.2", 37 | "jscs": "~1.11.0", 38 | "jshint": "~2.6.0", 39 | "jsonfile": "~2.0.0", 40 | "jsonstream2": "^1.1.0", 41 | "minimist": "~1.1.0", 42 | "npm-which": "^2.0.0", 43 | "partial": "0.0.3", 44 | "pluralize": "^1.1.2", 45 | "process": "^0.10.0", 46 | "text-table": "^0.2.0", 47 | "through2": "^0.6.3", 48 | "through2-spy": "^1.2.0", 49 | "xtend": "^4.0.0" 50 | }, 51 | "devDependencies": { 52 | "istanbul": "^0.3.5", 53 | "mkdirp": "^0.5.0", 54 | "mock-fs": "^2.3.2", 55 | "opn": "^1.0.1", 56 | "pre-commit": "1.0.2", 57 | "rimraf": "^2.2.8", 58 | "tape": "~3.5.0", 59 | "temp": "^0.8.1", 60 | "which": "^1.0.8" 61 | }, 62 | "scripts": { 63 | "cover": "istanbul cover --report cobertura --print detail tape -- test/**/*.tape.js", 64 | "lint": "./bin/lint-trap.js", 65 | "test": "npm run lint && tape test/**/*.tape.js", 66 | "view-cover": "istanbul report html && opn ./coverage/index.html" 67 | }, 68 | "pre-commit": [ 69 | "test" 70 | ], 71 | "license": "MIT" 72 | } 73 | -------------------------------------------------------------------------------- /rc/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "node": false, 5 | "amd": false, 6 | "mocha": false, 7 | "jasmine": false 8 | }, 9 | 10 | "globals": { 11 | "__dirname": false, 12 | "__filename": false, 13 | "require": false, 14 | "module": false 15 | }, 16 | 17 | "rules": { 18 | "no-alert": 2, 19 | "no-array-constructor": 2, 20 | "no-bitwise": 0, 21 | "no-caller": 2, 22 | "no-catch-shadow": 0, 23 | "no-comma-dangle": 2, 24 | "no-cond-assign": 2, 25 | "no-console": 1, 26 | "no-constant-condition": 2, 27 | "no-control-regex": 2, 28 | "no-debugger": 2, 29 | "no-delete-var": 2, 30 | "no-div-regex": 0, 31 | "no-dupe-keys": 2, 32 | "no-else-return": 0, 33 | "no-empty": 2, 34 | "no-empty-class": 2, 35 | "no-empty-label": 2, 36 | "no-eq-null": 0, 37 | "no-eval": 2, 38 | "no-ex-assign": 2, 39 | "no-extend-native": 2, 40 | "no-extra-bind": 2, 41 | "no-extra-boolean-cast": 2, 42 | "no-extra-parens": 0, 43 | "no-extra-semi": 2, 44 | "no-extra-strict": 2, 45 | "no-fallthrough": 2, 46 | "no-floating-decimal": 2, 47 | "no-func-assign": 2, 48 | "no-implied-eval": 2, 49 | "no-inline-comments": 0, 50 | "no-inner-declarations": [2, "functions"], 51 | "no-invalid-regexp": 2, 52 | "no-irregular-whitespace": 2, 53 | "no-iterator": 2, 54 | "no-label-var": 2, 55 | "no-labels": 2, 56 | "no-lone-blocks": 2, 57 | "no-lonely-if": 1, 58 | "no-loop-func": 2, 59 | "no-mixed-requires": [0, false], 60 | "no-mixed-spaces-and-tabs": [2, false], 61 | "no-multi-spaces": 2, 62 | "no-multi-str": 2, 63 | "no-multiple-empty-lines": [2, {"max": 1}], 64 | "no-native-reassign": 2, 65 | "no-negated-in-lhs": 2, 66 | "no-nested-ternary": 0, 67 | "no-new": 2, 68 | "no-new-func": 2, 69 | "no-new-object": 2, 70 | "no-new-require": 2, 71 | "no-new-wrappers": 2, 72 | "no-obj-calls": 2, 73 | "no-octal": 2, 74 | "no-octal-escape": 2, 75 | "no-path-concat": 2, 76 | "no-plusplus": 0, 77 | "no-process-env": 2, 78 | "no-process-exit": 2, 79 | "no-proto": 2, 80 | "no-redeclare": 2, 81 | "no-regex-spaces": 2, 82 | "no-reserved-keys": 0, 83 | "no-restricted-modules": 0, 84 | "no-return-assign": 2, 85 | "no-script-url": 2, 86 | "no-self-compare": 2, 87 | "no-sequences": 2, 88 | "no-shadow": 2, 89 | "no-shadow-restricted-names": 2, 90 | "no-space-before-semi": 2, 91 | "no-spaced-func": 2, 92 | "no-sparse-arrays": 2, 93 | "no-sync": 0, 94 | "no-ternary": 0, 95 | "no-trailing-spaces": 2, 96 | "no-undef": 2, 97 | "no-undef-init": 2, 98 | "no-undefined": 0, 99 | "no-underscore-dangle": 0, 100 | "no-unreachable": 2, 101 | "no-unused-expressions": 2, 102 | "no-unused-vars": [2, {"vars": "all", "args": "none"}], 103 | "no-use-before-define": [2, "nofunc"], 104 | "no-void": 0, 105 | "no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }], 106 | "no-with": 2, 107 | "no-wrap-func": 2, 108 | "block-scoped-var": 0, 109 | "brace-style": [2, "1tbs"], 110 | "camelcase": 2, 111 | "comma-spacing": [2, {"before": false, "after": true}], 112 | "comma-style": [2, "last"], 113 | "complexity": [1, 11], 114 | "consistent-return": 2, 115 | "consistent-this": [2, "self"], 116 | "curly": [2, "all"], 117 | "default-case": 2, 118 | "dot-notation": 2, 119 | "eol-last": 2, 120 | "eqeqeq": 2, 121 | "func-names": 2, 122 | "func-style": [0, "declaration"], 123 | "global-strict": [2, "always"], 124 | "guard-for-in": 2, 125 | "handle-callback-err": [2, "^(err|error|anySpecificError)$" ], 126 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 127 | "max-depth": [2, 4], 128 | "max-len": [2, 80, 4], 129 | "max-nested-callbacks": [2, 3], 130 | "max-params": [2, 4], 131 | "max-statements": [1, 15], 132 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 133 | "new-parens": 2, 134 | "one-var": 0, 135 | "operator-assignment": [0, "always"], 136 | "padded-blocks": 0, 137 | "quote-props": 0, 138 | "quotes": [2, "single"], 139 | "radix": 2, 140 | "semi": 2, 141 | "sort-vars": 0, 142 | "space-after-function-name": [2, "never"], 143 | "space-after-keywords": [1, "always"], 144 | "space-before-blocks": [1, "always"], 145 | "space-in-brackets": [1, "never"], 146 | "space-in-parens": [2, "never"], 147 | "space-infix-ops": 2, 148 | "space-return-throw-case": 2, 149 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 150 | "spaced-line-comment": [2, "always"], 151 | "strict": 2, 152 | "use-isnan": 2, 153 | "valid-jsdoc": [2, { "requireReturn": false, "requireParamDescription": false }], 154 | "valid-typeof": 2, 155 | "vars-on-top": 0, 156 | "wrap-iife": 2, 157 | "wrap-regex": 0, 158 | "yoda": [2, "never"] 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /rc/.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "validateIndentation": 4, 3 | "maximumLineLength": { 4 | "value": 120, 5 | "allowUrlComments": true, 6 | "allowComments": false, 7 | "allowRegex": false 8 | }, 9 | "requireSpaceAfterKeywords": [ 10 | "if", 11 | "else", 12 | "for", 13 | "while", 14 | "do", 15 | "switch", 16 | "return", 17 | "try", 18 | "catch" 19 | ], 20 | "disallowSpacesInFunctionDeclaration": { 21 | "beforeOpeningRoundBrace": true 22 | }, 23 | "disallowSpacesInNamedFunctionExpression": { 24 | "beforeOpeningRoundBrace": true 25 | }, 26 | "requireSpacesInFunctionDeclaration": { 27 | "beforeOpeningCurlyBrace": true 28 | }, 29 | "requireSpacesInNamedFunctionExpression": { 30 | "beforeOpeningCurlyBrace": true 31 | }, 32 | "disallowTrailingComma": true, 33 | "requireBlocksOnNewline": true, 34 | "excludeFiles": [], 35 | "requireCurlyBraces": [ 36 | "if", 37 | "else", 38 | "for", 39 | "while", 40 | "do", 41 | "try", 42 | "catch" 43 | ], 44 | "disallowMultipleVarDecl": true, 45 | "disallowEmptyBlocks": true, 46 | "disallowSpaceAfterObjectKeys": true, 47 | "requireCommaBeforeLineBreak": true, 48 | "requireSpaceBeforeBinaryOperators": [ 49 | "+", 50 | "-", 51 | "/", 52 | "*", 53 | "=", 54 | "==", 55 | "===", 56 | "!=", 57 | "!==" 58 | ], 59 | "requireSpacesInConditionalExpression": true, 60 | "requireSpaceAfterBinaryOperators": [ 61 | "+", 62 | "-", 63 | "/", 64 | "*", 65 | "=", 66 | "==", 67 | "===", 68 | "!=", 69 | "!==", 70 | "," 71 | ], 72 | "disallowSpaceAfterPrefixUnaryOperators": [ 73 | "++", 74 | "--", 75 | "+", 76 | "-", 77 | "~", 78 | "!" 79 | ], 80 | "disallowKeywords": [ 81 | "with", 82 | "try", 83 | "catch", 84 | "finally" 85 | ], 86 | "disallowMultipleLineBreaks": true, 87 | "validateLineBreaks": "LF", 88 | "validateQuoteMarks": { 89 | "mark": "'", 90 | "escape": true 91 | }, 92 | "disallowMixedSpacesAndTabs": true, 93 | "disallowTrailingWhitespace": true, 94 | "disallowKeywordsOnNewLine": [ 95 | "else" 96 | ], 97 | "requireLineFeedAtFileEnd": true, 98 | "requireDotNotation": true 99 | } 100 | -------------------------------------------------------------------------------- /rc/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globalstrict": true, 3 | "indent": 4, 4 | "undef": true, 5 | "validthis": true, 6 | "newcap": false, 7 | "globals": { 8 | "__dirname": false, 9 | "__filename": false, 10 | "require": false, 11 | "module": false, 12 | "console": true, 13 | "exports": true, 14 | "global": true, 15 | "process": true, 16 | "Buffer": true, 17 | "setTimeout": true, 18 | "clearTimeout": true, 19 | "setInterval": true, 20 | "clearInterval": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /reporters/eslint.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var process = require('process'); 3 | 4 | function getErrorType(error) { 5 | return (error.fatal || error.severity === 2) ? 'error' : 'warning'; 6 | } 7 | 8 | function writeError(file, error) { 9 | process.stdout.write(JSON.stringify({ 10 | type: getErrorType(error), 11 | linter: 'eslint', 12 | file: file === '' ? 'stdin' : file, 13 | line: error.line || 0, 14 | column: error.column || 0, 15 | rule: error.ruleId || '', 16 | message: error.message 17 | })); 18 | // if (index !== array.length - 1) { 19 | // process.stdout.write(','); 20 | // } 21 | process.stdout.write(','); 22 | } 23 | 24 | function writeFileErrors(fileErrors) { 25 | fileErrors.messages.forEach(writeError.bind(null, fileErrors.filePath)); 26 | } 27 | 28 | function reporter(results) { 29 | process.stdout.write('['); 30 | results.forEach(writeFileErrors); 31 | process.stdout.write(']'); 32 | return ''; 33 | } 34 | 35 | module.exports = reporter; 36 | -------------------------------------------------------------------------------- /reporters/jscs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var process = require('process'); 3 | 4 | function writeError(file, error) { 5 | // console.log(errors.explainError(error, true) + '\n'); 6 | process.stdout.write(JSON.stringify({ 7 | type: 'error', 8 | linter: 'jscs', 9 | file: file === 'input' ? 'stdin' : file, 10 | line: error.line, 11 | column: error.column, 12 | rule: error.rule, 13 | message: error.message 14 | })); 15 | process.stdout.write(','); 16 | } 17 | 18 | function writeFileErrors(errors) { 19 | /*eslint-disable */ 20 | var file = errors._file._filename; 21 | // _errorList, _file, _currentRule, _verbose 22 | /*eslint-enable */ 23 | 24 | if (!errors.isEmpty()) { 25 | errors.getErrorList().forEach(writeError.bind(null, file)); 26 | } 27 | } 28 | 29 | function reporter(results) { 30 | process.stdout.write('['); 31 | results.forEach(writeFileErrors); 32 | process.stdout.write(']'); 33 | } 34 | 35 | module.exports = reporter; 36 | -------------------------------------------------------------------------------- /reporters/jshint.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var process = require('process'); 3 | 4 | function writeError(result) { 5 | var error = result.error; 6 | 7 | // JSHint gives no option to disable rule W002. Just skip it here. 8 | // Hack. sad. :( 9 | if (error.code !== 'W002') { 10 | process.stdout.write(JSON.stringify({ 11 | type: 'error', 12 | linter: 'jshint', 13 | file: result.file, 14 | error: error, 15 | line: error.line, 16 | column: error.character, 17 | rule: error.code, 18 | message: error.reason 19 | })); 20 | process.stdout.write(','); 21 | } 22 | } 23 | 24 | function reporter(results, data, opts) { 25 | process.stdout.write('['); 26 | results.forEach(writeError); 27 | process.stdout.write(']'); 28 | } 29 | 30 | module.exports = { 31 | reporter: reporter 32 | }; 33 | -------------------------------------------------------------------------------- /scripts/post-install.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var jsonfile = require('jsonfile'); 5 | var async = require('async'); 6 | var debug = require('debug')('post-install'); 7 | 8 | // Path to lint-trap 9 | var rootPath = path.resolve(__dirname, '..'); 10 | 11 | // Linters to look for and remove 12 | var linterNames = ['jscs', 'jshint', 'eslint']; 13 | 14 | // Linter configuration files to remove 15 | var linterConfigurationFiles = ['.jscsrc', '.jshintrc', '.eslintrc']; 16 | 17 | // Linter ignore files to consolidate 18 | // var linterIgnoreFiles = ['.jshintignore', '.eslintignore']; 19 | 20 | /** 21 | * Removes linters from package.json 22 | * 23 | * @param {Object} manifest package.json loaded as a JSON object 24 | * @return {Object} modified package.json JSON object 25 | */ 26 | function removeDependenciesFromManifest(manifest) { 27 | linterNames.forEach(function removeDependencies(ln) { 28 | if (manifest.dependencies[ln]) { 29 | delete manifest.dependencies[ln]; 30 | } 31 | 32 | if (manifest.devDependencies[ln]) { 33 | delete manifest.devDependencies[ln]; 34 | } 35 | }); 36 | return manifest; 37 | } 38 | 39 | function deleteFile(folderPath, filename, done) { 40 | var filePath = path.join(folderPath, filename); 41 | debug('deleting %s', filePath); 42 | fs.exists(filePath, function existsCallback(exists) { 43 | if (exists) { 44 | fs.unlink(filePath, done); 45 | } else { 46 | done(); 47 | } 48 | }); 49 | } 50 | 51 | function deleteLinterConfigurationFiles(repoPath, cb) { 52 | async.each(linterConfigurationFiles, deleteFile.bind(null, repoPath), cb); 53 | } 54 | 55 | function symlinkLinterConfigurationFiles(modulePath, cb) { 56 | var lintTrapRCFilesPath = path.resolve(rootPath, './rc/'); 57 | 58 | async.each(linterConfigurationFiles, function symlinkFile(filename, done) { 59 | var target = path.resolve(lintTrapRCFilesPath, filename); 60 | var linkName = path.resolve(modulePath, filename); 61 | debug('symlink created: %s ==> %s', linkName, target); 62 | fs.symlink(target, linkName, done); 63 | }, cb); 64 | } 65 | 66 | // function deleteLinterIgnoreFiles(repoPath, cb) { 67 | // async.each(linterIgnoreFiles, deleteFile.bind(null, repoPath), cb); 68 | // } 69 | 70 | // function removeLintersFromManifestScripts (packageJSON) { 71 | // if (packageJSON.scripts) { 72 | // Object.keys(packageJSON.scripts).forEach(function (scriptName) { 73 | // }); 74 | // } 75 | // } 76 | 77 | /** 78 | * Take a repo and remove all the linting features: 79 | * - remove jshint, eslint and jscs from the package.json 80 | * - remove the config files for the above linters 81 | * 82 | * @param {String} modulePath file path to the repo to which lint-trap was 83 | * installed 84 | * @param {Function} callback callback function 85 | */ 86 | function cleanRepo(modulePath, callback) { 87 | var manifestPath = path.join(modulePath, 'package.json'); 88 | 89 | fs.exists(manifestPath, function existsCallback(exists) { 90 | if (exists) { 91 | jsonfile.readFile(manifestPath, function cleanFile(err, manifest) { 92 | if (err) { 93 | return callback(err); 94 | } 95 | manifest = removeDependenciesFromManifest(manifest); 96 | // deleteLinterConfigurationFiles(modulePath); 97 | // deleteLinterIgnoreFiles(modulePath); 98 | 99 | fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 100 | 'utf8', callback); 101 | }); 102 | } 103 | }); 104 | } 105 | 106 | function finished(err) { 107 | if (err) { 108 | return debug(err); 109 | } 110 | debug('finished'); 111 | } 112 | 113 | function deleteFilesCallback(err, parentModulePath) { 114 | if (err) { 115 | return debug(err); 116 | } 117 | debug('deleted linter .rc files in %s', parentModulePath); 118 | symlinkLinterConfigurationFiles(parentModulePath, finished); 119 | } 120 | 121 | function cleanRepoCallback(err, parentModulePath) { 122 | if (err) { 123 | return debug(err); 124 | } 125 | debug('Cleaned manifest in %s', parentModulePath); 126 | var cb = deleteFilesCallback.bind(null, parentModulePath); 127 | deleteLinterConfigurationFiles(parentModulePath, cb); 128 | } 129 | 130 | function main() { 131 | // Path to which lint-trap is installed 132 | var parentPath = path.dirname(rootPath); 133 | if (path.basename(parentPath) === 'node_modules') { 134 | var parentModulePath = path.dirname(parentPath); 135 | var cb = cleanRepoCallback.bind(null, parentModulePath); 136 | cleanRepo(parentModulePath, cb); 137 | } else { 138 | symlinkLinterConfigurationFiles(rootPath, finished); 139 | } 140 | } 141 | 142 | main(); 143 | -------------------------------------------------------------------------------- /set-rules.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var jf = require('jsonfile'); 4 | var dotty = require('dotty'); 5 | var findParentDir = require('find-parent-dir'); 6 | var editorConfigParse = require('editorconfig/lib/ini').parse; 7 | 8 | jf.spaces = 4; 9 | 10 | function updateJSON(jsonPath, diffs) { 11 | // Synchronous method to be safe since multiple rules might touch the 12 | // same file. 13 | var content = jf.readFileSync(jsonPath); 14 | Object.keys(diffs).forEach(function applyDiff(diffKey) { 15 | var value = diffs[diffKey]; 16 | dotty.put(content, diffKey, value); 17 | }); 18 | jf.writeFileSync(jsonPath, content); 19 | } 20 | 21 | function setIndentRule(rootPath, callback) { 22 | findParentDir(rootPath, '.editorconfig', onEditorConfigDir); 23 | 24 | function onEditorConfigDir(err, editorConfigDir) { 25 | if (err || !editorConfigDir) { 26 | // If no editorconfig found, swallow the error and default to 4. 27 | return callback(); 28 | } 29 | var editorConfigPath = path.join(editorConfigDir, '.editorconfig'); 30 | editorConfigParse(editorConfigPath, onEditorConfigParse); 31 | } 32 | 33 | function onEditorConfigParse(parseErr, parsed) { 34 | if (parseErr) { 35 | // If we can't parse .editorconfig, throw an error since there may 36 | // be a *.js indent rule we want to respect. 37 | return callback(parseErr); 38 | } 39 | var rules = getRuleset(parsed, '*.js') || getRuleset(parsed, '*'); 40 | 41 | if (!rules || rules[0].indent_size) { 42 | // There is no ruleset that applies to *.js files or there is no 43 | // indent_size defined, don't change indent rule. 44 | return callback(); 45 | } 46 | 47 | var jscsrcPath = path.resolve(__dirname, './rc/.jscsrc'); 48 | var indent = parseInt(rules[1].indent_size, 10); 49 | if (!indent) { 50 | var indentError = new Error('Invalid indent from editorconfig'); 51 | return callback(indentError); 52 | } 53 | var diff = {validateIndentation: indent}; 54 | updateJSON(jscsrcPath, diff); 55 | callback(); 56 | } 57 | 58 | function getRuleset(parsed, id) { 59 | return parsed.filter(function getJsRuleSet(ruleset) { 60 | return ruleset[0] === id; 61 | })[0]; 62 | } 63 | } 64 | 65 | function setLineLengthRule(lineLength, callback) { 66 | var eslintrcPath = path.resolve(__dirname, './rc/.eslintrc'); 67 | var eslintdiff = { 68 | 'rules.max-len': [2, lineLength, 4] 69 | }; 70 | updateJSON(eslintrcPath, eslintdiff); 71 | callback(); 72 | } 73 | 74 | function setRules(dir, lineLength, callback) { 75 | 76 | setIndentRule(dir, function setIndentRuleCallback(indentErr) { 77 | if (indentErr) { 78 | return callback(indentErr); 79 | } 80 | 81 | setLineLengthRule(lineLength, function setLineLengthRuleCallback(err) { 82 | if (err) { 83 | return callback(err); 84 | } 85 | callback(); 86 | }); 87 | }); 88 | } 89 | 90 | module.exports = setRules; 91 | -------------------------------------------------------------------------------- /severity-transform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var through = require('through2'); 3 | var loadLintRC = require('./load-lintrc'); 4 | var attenuateSeverity = require('./attenuate-severity'); 5 | 6 | function makeSeverityTransform() { 7 | 8 | function transform(message, enc, callback) { 9 | 10 | loadLintRC(message.file, function loadLintRCCallback(err, lintrc) { 11 | if (err) { 12 | return callback(err); 13 | } 14 | attenuateSeverity(message, lintrc); 15 | this.push(message); 16 | callback(); 17 | }.bind(this)); 18 | } 19 | return through.obj(transform); 20 | } 21 | 22 | module.exports = makeSeverityTransform; 23 | -------------------------------------------------------------------------------- /stylish-reporter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var table = require('text-table'); 3 | var process = require('process'); 4 | var isTTY = process.stdout.isTTY; 5 | var chalk = require('chalk'); 6 | 7 | function createTableRow(message) { 8 | var linter = (message.linter + ' ').slice(0, 6); 9 | // pad number with spaces in front 10 | var line = (' ' + message.line).slice(-4); 11 | // pad number with spaces behind 12 | var column = (message.column + ' ').slice(0, 3); 13 | var location = [line, column].join(':'); 14 | 15 | return [ 16 | ' ', 17 | message.type, 18 | linter, 19 | location, 20 | message.message, 21 | message.rule 22 | ]; 23 | } 24 | 25 | function printFilePath(filePath) { 26 | var s = !isTTY ? filePath : chalk.underline.cyan((filePath)); 27 | return s + '\n'; 28 | } 29 | 30 | function printTable(data) { 31 | var output = ''; 32 | if (!isTTY) { 33 | output = table(data) + '\n'; 34 | } else { 35 | data = data.map(function colorize(row) { 36 | return [ 37 | row[0], 38 | row[1] === 'warning' ? chalk.yellow(row[1]) : chalk.red(row[1]), 39 | chalk.magenta(row[2]), 40 | row[3], 41 | chalk.white(row[4]), 42 | chalk.yellow(row[5]) 43 | ]; 44 | }); 45 | output = table(data) + '\n'; 46 | } 47 | return output; 48 | } 49 | 50 | function printFileErrorTable(message) { 51 | var output = ''; 52 | var fileTableData = message.errors.map(createTableRow); 53 | output += printFilePath(message.file); 54 | output += printTable(fileTableData); 55 | return output; 56 | } 57 | 58 | function printStylish(fileMessages) { 59 | isTTY = process.stdout.isTTY; 60 | fileMessages = fileMessages.filter(removeEmpty); 61 | 62 | function removeEmpty(fileMessage) { 63 | return fileMessage.errors.length > 0; 64 | } 65 | return fileMessages.map(printFileErrorTable).join('\n'); 66 | } 67 | 68 | module.exports = printStylish; 69 | -------------------------------------------------------------------------------- /test/attenuate-severity.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var attenuateSeverity = require('../attenuate-severity'); 4 | 5 | test('attenuate severity in single file with single lintrc', function tape(t) { 6 | t.plan(2); 7 | 8 | var lintrc = { 9 | eslint: { 10 | 'func-names': false 11 | } 12 | }; 13 | 14 | var messageA = { 15 | type: 'error', 16 | linter: 'eslint', 17 | rule: 'func-names', 18 | severity: 'error' 19 | }; 20 | 21 | attenuateSeverity(messageA, lintrc); 22 | t.equal(messageA.type, 'warning', 'Downgraded severity to warning'); 23 | 24 | var messageB = { 25 | type: 'error', 26 | linter: 'eslint', 27 | rule: 'not-func-names', 28 | severity: 'error' 29 | }; 30 | 31 | attenuateSeverity(messageB, lintrc); 32 | t.equal(messageB.type, 'error', 'Did not downgrade severity to warning'); 33 | }); 34 | -------------------------------------------------------------------------------- /test/checkstyle-reporter.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var checkstyleReporter = require('../checkstyle-reporter'); 4 | var makeFileErrorStream = require('./helpers/make-file-error-stream'); 5 | var es = require('event-stream'); 6 | var fs = require('fs'); 7 | var path = require('path'); 8 | 9 | test('Checkstyle Reporter', function runTests(t) { 10 | t.plan(2); 11 | t.ok(checkstyleReporter, 'Module exists'); 12 | 13 | var fixture = path.join(__dirname, 'fixtures/unit/checkstyle.stdout'); 14 | 15 | var expectedStdout = fs.readFileSync(fixture).toString(); 16 | 17 | makeFileErrorStream().pipe(es.writeArray(function print(err, array) { 18 | if (err) { 19 | t.fail(); 20 | } 21 | 22 | t.equal(checkstyleReporter(array), expectedStdout, 'Correct stdout'); 23 | })); 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /test/cli.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var test = require('tape'); 5 | 6 | var execFile = require('child_process').execFile; 7 | 8 | var fixturesPath = path.join(__dirname, 'fixtures/rules'); 9 | var binPath = path.resolve(__dirname, '../bin/lint-trap.js'); 10 | 11 | var expectedStdoutPath = path.join(fixturesPath, 'output.stdout'); 12 | var expectedStdout = fs.readFileSync(expectedStdoutPath, 'utf8'); 13 | 14 | test('Command Line Interface w/ lint errors', function testCLI(t) { 15 | t.plan(4); 16 | 17 | var args = [fixturesPath]; 18 | var opts = {}; 19 | 20 | execFile(binPath, args, opts, function callback(err, stdout, stderr) { 21 | t.ok(err, 'Non-zero exit'); 22 | t.equal(err.code, 1, 'Returned error code 1'); 23 | t.equal(stderr, '', 'No output on stderr'); 24 | t.equal(stdout, expectedStdout, 'Correct output on stdout'); 25 | }); 26 | }); 27 | 28 | test('Command Line Interface w/o lint errors', function testCLI(t) { 29 | t.plan(2); 30 | 31 | var args = [path.join(fixturesPath, './globals-valid.js')]; 32 | var opts = {}; 33 | 34 | execFile(binPath, args, opts, function callback(err, stdout, stderr) { 35 | if (err) { 36 | t.fail(err); 37 | } 38 | 39 | t.equal(stderr, '', 'No output on stderr'); 40 | t.equal(stdout, '', 'No output on stdout'); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/compact-reporter.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var compactReporter = require('../compact-reporter'); 4 | var makeFileErrorStream = require('./helpers/make-file-error-stream'); 5 | var es = require('event-stream'); 6 | var fs = require('fs'); 7 | var path = require('path'); 8 | 9 | test('Compact Reporter', function runTests(t) { 10 | t.plan(2); 11 | t.ok(compactReporter, 'Module exists'); 12 | 13 | var fixture = path.join(__dirname, 'fixtures/unit/compact.stdout'); 14 | 15 | var expectedStdout = fs.readFileSync(fixture).toString(); 16 | 17 | makeFileErrorStream().pipe(es.writeArray(function print(err, array) { 18 | if (err) { 19 | t.fail(); 20 | } 21 | t.equal(compactReporter(array), expectedStdout, 'Correct stdout'); 22 | })); 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /test/dedupe-transform.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var es = require('event-stream'); 4 | var dedupeTransformStream = require('../dedupe-transform'); 5 | var dedupeMap = require('../dupe-map.json'); 6 | var partial = require('partial'); 7 | 8 | function makeFixture(expected) { 9 | return Object.keys(dedupeMap).map(partial(makeFileErrorObject, expected)); 10 | } 11 | 12 | function makeFileErrorObject(expected, key, index, array) { 13 | var errorToKeep = key.split('.'); 14 | var errorToRemove = dedupeMap[key].split('.'); 15 | var errors = [ 16 | errorToRemove, 17 | errorToKeep, 18 | ['jscs', 'unique-rule'], 19 | ['eslint', 'unique-rule'], 20 | ['eslint', 'unique-rule'] 21 | ].map(makeError); 22 | 23 | return { 24 | file: String.fromCharCode(97 + index) + '.js', 25 | errors: expected ? errors.slice(1) : errors 26 | }; 27 | } 28 | 29 | function makeError(error) { 30 | return { 31 | linter: error[0], 32 | rule: error[1] 33 | }; 34 | } 35 | 36 | test('Error Messages to File Messages Transform Stream', function runTests(t) { 37 | t.plan(2); 38 | t.ok(dedupeTransformStream, 'Transform Function exists'); 39 | var fileErrors = makeFixture(); 40 | 41 | es.readArray(fileErrors) 42 | .pipe(dedupeTransformStream()) 43 | .pipe(es.writeArray(collectOutput)); 44 | 45 | function collectOutput(err, actualErrors) { 46 | if (err) { 47 | return t.fail(err.message); 48 | } 49 | var expectedErrors = makeFixture(true); 50 | t.deepEqual(actualErrors, expectedErrors); 51 | } 52 | }); 53 | -------------------------------------------------------------------------------- /test/error-meter.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var errorMeter = require('../error-meter'); 4 | 5 | test('Error Meter Spy Stream', function runTests(t) { 6 | t.plan(1); 7 | t.ok(errorMeter, 'Module exists'); 8 | }); 9 | -------------------------------------------------------------------------------- /test/error-to-file-transform.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var errorsToFilesTransformStream = require('../error-to-file-transform'); 4 | 5 | test('Error Messages to File Messages Transform Stream', function runTests(t) { 6 | t.plan(1); 7 | t.ok(errorsToFilesTransformStream, 'Transform Function exists'); 8 | }); 9 | -------------------------------------------------------------------------------- /test/fixtures/lintrc/.lintrc: -------------------------------------------------------------------------------- 1 | { 2 | "eslint": { 3 | "func-names": false 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/lintrc/fixture.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var foo = function() {}; 4 | 5 | module.exports = foo; 6 | -------------------------------------------------------------------------------- /test/fixtures/lintrc/fixture.stdout: -------------------------------------------------------------------------------- 1 | fixture.js 2 | warning eslint 3:10 Missing function expression name. func-names 3 | -------------------------------------------------------------------------------- /test/fixtures/lintrc/output.stdout: -------------------------------------------------------------------------------- 1 | fixture.js 2 | warning eslint 3:10 Missing function expression name. func-names 3 | 4 | subfolder/fixture.js 5 | error eslint 5:10 Missing function expression name. func-names 6 | -------------------------------------------------------------------------------- /test/fixtures/lintrc/subfolder/.lintrc: -------------------------------------------------------------------------------- 1 | { 2 | "eslint": { 3 | "no-underscore-dangle": false, 4 | "func-names": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/lintrc/subfolder/fixture.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _foo = true; 4 | 5 | var bar = function () { 6 | return _foo; 7 | }; 8 | 9 | module.exports = bar; 10 | -------------------------------------------------------------------------------- /test/fixtures/rules/func-names-invalid.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // should produce eslint `func-name` error 4 | var foo = function () {}; 5 | 6 | // should produce jscs whitespace errors 7 | var bar = function baz () {}; 8 | 9 | module.exports = { 10 | foo: foo, 11 | bar: bar 12 | }; 13 | -------------------------------------------------------------------------------- /test/fixtures/rules/func-names-valid.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | 5 | var foo = function bar() {}; 6 | 7 | test('baz', function baz() {}); 8 | 9 | module.exports = foo; 10 | -------------------------------------------------------------------------------- /test/fixtures/rules/globals-invalid.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function invalidGlobals() { 4 | return [ 5 | __dirname, 6 | __filename, 7 | module, 8 | require, 9 | process, 10 | global, 11 | exports, 12 | console, 13 | clearInterval, 14 | setInterval, 15 | clearTimeout, 16 | setTimeout, 17 | Buffer 18 | ]; 19 | } 20 | 21 | module.exports = invalidGlobals; 22 | -------------------------------------------------------------------------------- /test/fixtures/rules/globals-valid.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var global = require('global'); 4 | var process = require('process'); 5 | var window = require('global/window'); 6 | var document = require('global/document'); 7 | var console = require('console'); 8 | var exports = module.exports; 9 | 10 | function validGlobals() { 11 | return [ 12 | __dirname, 13 | __filename, 14 | module, 15 | require, 16 | process, 17 | global, 18 | exports, 19 | console, 20 | window, 21 | document 22 | ]; 23 | } 24 | 25 | module.exports = validGlobals; 26 | -------------------------------------------------------------------------------- /test/fixtures/rules/output.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "file", 4 | "file": "func-names-invalid.js", 5 | "errors": [ 6 | { 7 | "type": "error", 8 | "linter": "eslint", 9 | "file": "func-names-invalid.js", 10 | "line": 4, 11 | "column": 10, 12 | "rule": "func-names", 13 | "message": "Missing function expression name." 14 | }, 15 | { 16 | "type": "error", 17 | "linter": "eslint", 18 | "file": "func-names-invalid.js", 19 | "line": 7, 20 | "column": 10, 21 | "rule": "space-after-function-name", 22 | "message": "Function name \"baz\" must not be followed by whitespace." 23 | } 24 | ] 25 | }, 26 | { 27 | "type": "file", 28 | "file": "func-names-valid.js", 29 | "errors": [] 30 | }, 31 | { 32 | "type": "file", 33 | "file": "globals-invalid.js", 34 | "errors": [ 35 | { 36 | "type": "error", 37 | "linter": "eslint", 38 | "file": "globals-invalid.js", 39 | "line": 9, 40 | "column": 8, 41 | "rule": "no-undef", 42 | "message": "'process' is not defined." 43 | }, 44 | { 45 | "type": "error", 46 | "linter": "eslint", 47 | "file": "globals-invalid.js", 48 | "line": 10, 49 | "column": 8, 50 | "rule": "no-undef", 51 | "message": "'global' is not defined." 52 | }, 53 | { 54 | "type": "error", 55 | "linter": "eslint", 56 | "file": "globals-invalid.js", 57 | "line": 11, 58 | "column": 8, 59 | "rule": "no-undef", 60 | "message": "'exports' is not defined." 61 | }, 62 | { 63 | "type": "error", 64 | "linter": "eslint", 65 | "file": "globals-invalid.js", 66 | "line": 12, 67 | "column": 8, 68 | "rule": "no-undef", 69 | "message": "'console' is not defined." 70 | }, 71 | { 72 | "type": "error", 73 | "linter": "eslint", 74 | "file": "globals-invalid.js", 75 | "line": 13, 76 | "column": 8, 77 | "rule": "no-undef", 78 | "message": "'clearInterval' is not defined." 79 | }, 80 | { 81 | "type": "error", 82 | "linter": "eslint", 83 | "file": "globals-invalid.js", 84 | "line": 14, 85 | "column": 8, 86 | "rule": "no-undef", 87 | "message": "'setInterval' is not defined." 88 | }, 89 | { 90 | "type": "error", 91 | "linter": "eslint", 92 | "file": "globals-invalid.js", 93 | "line": 15, 94 | "column": 8, 95 | "rule": "no-undef", 96 | "message": "'clearTimeout' is not defined." 97 | }, { 98 | "type": "error", 99 | "linter": "eslint", 100 | "file": "globals-invalid.js", 101 | "line": 16, 102 | "column": 8, 103 | "rule": "no-undef", 104 | "message": "'setTimeout' is not defined." 105 | }, 106 | { 107 | "type": "error", 108 | "linter": "eslint", 109 | "file": "globals-invalid.js", 110 | "line": 17, 111 | "column": 8, 112 | "rule": "no-undef", 113 | "message": "'Buffer' is not defined." 114 | } 115 | ] 116 | }, 117 | { 118 | "type": "file", 119 | "file": "globals-valid.js", 120 | "errors": [] 121 | } 122 | ] 123 | -------------------------------------------------------------------------------- /test/fixtures/rules/output.stdout: -------------------------------------------------------------------------------- 1 | func-names-invalid.js 2 | error eslint 4:10 Missing function expression name. func-names 3 | error eslint 7:10 Function name "baz" must not be followed by whitespace. space-after-function-name 4 | 5 | globals-invalid.js 6 | error eslint 9:8 'process' is not defined. no-undef 7 | error eslint 10:8 'global' is not defined. no-undef 8 | error eslint 11:8 'exports' is not defined. no-undef 9 | error eslint 12:8 'console' is not defined. no-undef 10 | error eslint 13:8 'clearInterval' is not defined. no-undef 11 | error eslint 14:8 'setInterval' is not defined. no-undef 12 | error eslint 15:8 'clearTimeout' is not defined. no-undef 13 | error eslint 16:8 'setTimeout' is not defined. no-undef 14 | error eslint 17:8 'Buffer' is not defined. no-undef 15 | -------------------------------------------------------------------------------- /test/fixtures/unit/checkstyle.stdout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /test/fixtures/unit/compact.stdout: -------------------------------------------------------------------------------- 1 | a.js: line 0, col 0, Warning - no-alert message (eslint.no-alert) 2 | a.js: line 0, col 0, Warning - indent message (jshint.indent) 3 | a.js: line 0, col 0, Warning - validateIndentation message (jscs.validateIndentation) 4 | b.js: line 1, col 1, Error - no-array-constructor message (eslint.no-array-constructor) 5 | b.js: line 1, col 1, Error - undef message (jshint.undef) 6 | b.js: line 1, col 1, Error - maximumLineLength message (jscs.maximumLineLength) 7 | c.js: line 2, col 2, Warning - no-bitwise message (eslint.no-bitwise) 8 | c.js: line 2, col 2, Warning - validthis message (jshint.validthis) 9 | c.js: line 2, col 2, Warning - requireSpaceAfterKeywords message (jscs.requireSpaceAfterKeywords) 10 | d.js: line 3, col 3, Error - no-caller message (eslint.no-caller) 11 | d.js: line 3, col 3, Error - newcap message (jshint.newcap) 12 | d.js: line 3, col 3, Error - disallowSpacesInFunctionDeclaration message (jscs.disallowSpacesInFunctionDeclaration) 13 | e.js: line 4, col 4, Warning - no-catch-shadow message (eslint.no-catch-shadow) 14 | e.js: line 4, col 4, Warning - indent message (jshint.indent) 15 | e.js: line 4, col 4, Warning - disallowSpacesInNamedFunctionExpression message (jscs.disallowSpacesInNamedFunctionExpression) 16 | f.js: line 5, col 5, Error - no-comma-dangle message (eslint.no-comma-dangle) 17 | f.js: line 5, col 5, Error - undef message (jshint.undef) 18 | f.js: line 5, col 5, Error - requireSpacesInFunctionDeclaration message (jscs.requireSpacesInFunctionDeclaration) 19 | g.js: line 6, col 6, Warning - no-cond-assign message (eslint.no-cond-assign) 20 | g.js: line 6, col 6, Warning - validthis message (jshint.validthis) 21 | g.js: line 6, col 6, Warning - requireSpacesInNamedFunctionExpression message (jscs.requireSpacesInNamedFunctionExpression) 22 | h.js: line 7, col 7, Error - no-console message (eslint.no-console) 23 | h.js: line 7, col 7, Error - newcap message (jshint.newcap) 24 | h.js: line 7, col 7, Error - disallowTrailingComma message (jscs.disallowTrailingComma) 25 | i.js: line 8, col 8, Warning - no-constant-condition message (eslint.no-constant-condition) 26 | i.js: line 8, col 8, Warning - indent message (jshint.indent) 27 | i.js: line 8, col 8, Warning - requireBlocksOnNewline message (jscs.requireBlocksOnNewline) 28 | j.js: line 9, col 9, Error - no-control-regex message (eslint.no-control-regex) 29 | j.js: line 9, col 9, Error - undef message (jshint.undef) 30 | j.js: line 9, col 9, Error - excludeFiles message (jscs.excludeFiles) 31 | k.js: line 10, col 10, Warning - no-debugger message (eslint.no-debugger) 32 | k.js: line 10, col 10, Warning - validthis message (jshint.validthis) 33 | k.js: line 10, col 10, Warning - requireCurlyBraces message (jscs.requireCurlyBraces) 34 | l.js: line 11, col 11, Error - no-delete-var message (eslint.no-delete-var) 35 | l.js: line 11, col 11, Error - newcap message (jshint.newcap) 36 | l.js: line 11, col 11, Error - disallowMultipleVarDecl message (jscs.disallowMultipleVarDecl) 37 | m.js: line 12, col 12, Warning - no-div-regex message (eslint.no-div-regex) 38 | m.js: line 12, col 12, Warning - indent message (jshint.indent) 39 | m.js: line 12, col 12, Warning - disallowEmptyBlocks message (jscs.disallowEmptyBlocks) 40 | n.js: line 13, col 13, Error - no-dupe-keys message (eslint.no-dupe-keys) 41 | n.js: line 13, col 13, Error - undef message (jshint.undef) 42 | n.js: line 13, col 13, Error - disallowSpaceAfterObjectKeys message (jscs.disallowSpaceAfterObjectKeys) 43 | o.js: line 14, col 14, Warning - no-else-return message (eslint.no-else-return) 44 | o.js: line 14, col 14, Warning - validthis message (jshint.validthis) 45 | o.js: line 14, col 14, Warning - requireCommaBeforeLineBreak message (jscs.requireCommaBeforeLineBreak) 46 | p.js: line 15, col 15, Error - no-empty message (eslint.no-empty) 47 | p.js: line 15, col 15, Error - newcap message (jshint.newcap) 48 | p.js: line 15, col 15, Error - requireSpaceBeforeBinaryOperators message (jscs.requireSpaceBeforeBinaryOperators) 49 | q.js: line 16, col 16, Warning - no-empty-class message (eslint.no-empty-class) 50 | q.js: line 16, col 16, Warning - indent message (jshint.indent) 51 | q.js: line 16, col 16, Warning - requireSpacesInConditionalExpression message (jscs.requireSpacesInConditionalExpression) 52 | r.js: line 17, col 17, Error - no-empty-label message (eslint.no-empty-label) 53 | r.js: line 17, col 17, Error - undef message (jshint.undef) 54 | r.js: line 17, col 17, Error - requireSpaceAfterBinaryOperators message (jscs.requireSpaceAfterBinaryOperators) 55 | s.js: line 18, col 18, Warning - no-eq-null message (eslint.no-eq-null) 56 | s.js: line 18, col 18, Warning - validthis message (jshint.validthis) 57 | s.js: line 18, col 18, Warning - disallowSpaceAfterPrefixUnaryOperators message (jscs.disallowSpaceAfterPrefixUnaryOperators) 58 | t.js: line 19, col 19, Error - no-eval message (eslint.no-eval) 59 | t.js: line 19, col 19, Error - newcap message (jshint.newcap) 60 | t.js: line 19, col 19, Error - disallowKeywords message (jscs.disallowKeywords) 61 | u.js: line 20, col 20, Warning - no-ex-assign message (eslint.no-ex-assign) 62 | u.js: line 20, col 20, Warning - indent message (jshint.indent) 63 | u.js: line 20, col 20, Warning - disallowMultipleLineBreaks message (jscs.disallowMultipleLineBreaks) 64 | v.js: line 21, col 21, Error - no-extend-native message (eslint.no-extend-native) 65 | v.js: line 21, col 21, Error - undef message (jshint.undef) 66 | v.js: line 21, col 21, Error - validateLineBreaks message (jscs.validateLineBreaks) 67 | w.js: line 22, col 22, Warning - no-extra-bind message (eslint.no-extra-bind) 68 | w.js: line 22, col 22, Warning - validthis message (jshint.validthis) 69 | w.js: line 22, col 22, Warning - validateQuoteMarks message (jscs.validateQuoteMarks) 70 | x.js: line 23, col 23, Error - no-extra-boolean-cast message (eslint.no-extra-boolean-cast) 71 | x.js: line 23, col 23, Error - newcap message (jshint.newcap) 72 | x.js: line 23, col 23, Error - disallowMixedSpacesAndTabs message (jscs.disallowMixedSpacesAndTabs) 73 | y.js: line 24, col 24, Warning - no-extra-parens message (eslint.no-extra-parens) 74 | y.js: line 24, col 24, Warning - indent message (jshint.indent) 75 | y.js: line 24, col 24, Warning - disallowTrailingWhitespace message (jscs.disallowTrailingWhitespace) 76 | z.js: line 25, col 25, Error - no-extra-semi message (eslint.no-extra-semi) 77 | z.js: line 25, col 25, Error - undef message (jshint.undef) 78 | z.js: line 25, col 25, Error - disallowKeywordsOnNewLine message (jscs.disallowKeywordsOnNewLine) 79 | -------------------------------------------------------------------------------- /test/fixtures/unit/error-to-file-stream.input.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uber-archive/lint-trap/5dfd4b493a7cb4d7d8afa9171ec59bf6cf90dec9/test/fixtures/unit/error-to-file-stream.input.json -------------------------------------------------------------------------------- /test/fixtures/unit/error-to-file-stream.output.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uber-archive/lint-trap/5dfd4b493a7cb4d7d8afa9171ec59bf6cf90dec9/test/fixtures/unit/error-to-file-stream.output.json -------------------------------------------------------------------------------- /test/fixtures/unit/stylish.stdout: -------------------------------------------------------------------------------- 1 | a.js 2 | warning eslint 0:0 no-alert message no-alert 3 | warning jshint 0:0 indent message indent 4 | warning jscs 0:0 validateIndentation message validateIndentation 5 | 6 | b.js 7 | error eslint 1:1 no-array-constructor message no-array-constructor 8 | error jshint 1:1 undef message undef 9 | error jscs 1:1 maximumLineLength message maximumLineLength 10 | 11 | c.js 12 | warning eslint 2:2 no-bitwise message no-bitwise 13 | warning jshint 2:2 validthis message validthis 14 | warning jscs 2:2 requireSpaceAfterKeywords message requireSpaceAfterKeywords 15 | 16 | d.js 17 | error eslint 3:3 no-caller message no-caller 18 | error jshint 3:3 newcap message newcap 19 | error jscs 3:3 disallowSpacesInFunctionDeclaration message disallowSpacesInFunctionDeclaration 20 | 21 | e.js 22 | warning eslint 4:4 no-catch-shadow message no-catch-shadow 23 | warning jshint 4:4 indent message indent 24 | warning jscs 4:4 disallowSpacesInNamedFunctionExpression message disallowSpacesInNamedFunctionExpression 25 | 26 | f.js 27 | error eslint 5:5 no-comma-dangle message no-comma-dangle 28 | error jshint 5:5 undef message undef 29 | error jscs 5:5 requireSpacesInFunctionDeclaration message requireSpacesInFunctionDeclaration 30 | 31 | g.js 32 | warning eslint 6:6 no-cond-assign message no-cond-assign 33 | warning jshint 6:6 validthis message validthis 34 | warning jscs 6:6 requireSpacesInNamedFunctionExpression message requireSpacesInNamedFunctionExpression 35 | 36 | h.js 37 | error eslint 7:7 no-console message no-console 38 | error jshint 7:7 newcap message newcap 39 | error jscs 7:7 disallowTrailingComma message disallowTrailingComma 40 | 41 | i.js 42 | warning eslint 8:8 no-constant-condition message no-constant-condition 43 | warning jshint 8:8 indent message indent 44 | warning jscs 8:8 requireBlocksOnNewline message requireBlocksOnNewline 45 | 46 | j.js 47 | error eslint 9:9 no-control-regex message no-control-regex 48 | error jshint 9:9 undef message undef 49 | error jscs 9:9 excludeFiles message excludeFiles 50 | 51 | k.js 52 | warning eslint 10:10 no-debugger message no-debugger 53 | warning jshint 10:10 validthis message validthis 54 | warning jscs 10:10 requireCurlyBraces message requireCurlyBraces 55 | 56 | l.js 57 | error eslint 11:11 no-delete-var message no-delete-var 58 | error jshint 11:11 newcap message newcap 59 | error jscs 11:11 disallowMultipleVarDecl message disallowMultipleVarDecl 60 | 61 | m.js 62 | warning eslint 12:12 no-div-regex message no-div-regex 63 | warning jshint 12:12 indent message indent 64 | warning jscs 12:12 disallowEmptyBlocks message disallowEmptyBlocks 65 | 66 | n.js 67 | error eslint 13:13 no-dupe-keys message no-dupe-keys 68 | error jshint 13:13 undef message undef 69 | error jscs 13:13 disallowSpaceAfterObjectKeys message disallowSpaceAfterObjectKeys 70 | 71 | o.js 72 | warning eslint 14:14 no-else-return message no-else-return 73 | warning jshint 14:14 validthis message validthis 74 | warning jscs 14:14 requireCommaBeforeLineBreak message requireCommaBeforeLineBreak 75 | 76 | p.js 77 | error eslint 15:15 no-empty message no-empty 78 | error jshint 15:15 newcap message newcap 79 | error jscs 15:15 requireSpaceBeforeBinaryOperators message requireSpaceBeforeBinaryOperators 80 | 81 | q.js 82 | warning eslint 16:16 no-empty-class message no-empty-class 83 | warning jshint 16:16 indent message indent 84 | warning jscs 16:16 requireSpacesInConditionalExpression message requireSpacesInConditionalExpression 85 | 86 | r.js 87 | error eslint 17:17 no-empty-label message no-empty-label 88 | error jshint 17:17 undef message undef 89 | error jscs 17:17 requireSpaceAfterBinaryOperators message requireSpaceAfterBinaryOperators 90 | 91 | s.js 92 | warning eslint 18:18 no-eq-null message no-eq-null 93 | warning jshint 18:18 validthis message validthis 94 | warning jscs 18:18 disallowSpaceAfterPrefixUnaryOperators message disallowSpaceAfterPrefixUnaryOperators 95 | 96 | t.js 97 | error eslint 19:19 no-eval message no-eval 98 | error jshint 19:19 newcap message newcap 99 | error jscs 19:19 disallowKeywords message disallowKeywords 100 | 101 | u.js 102 | warning eslint 20:20 no-ex-assign message no-ex-assign 103 | warning jshint 20:20 indent message indent 104 | warning jscs 20:20 disallowMultipleLineBreaks message disallowMultipleLineBreaks 105 | 106 | v.js 107 | error eslint 21:21 no-extend-native message no-extend-native 108 | error jshint 21:21 undef message undef 109 | error jscs 21:21 validateLineBreaks message validateLineBreaks 110 | 111 | w.js 112 | warning eslint 22:22 no-extra-bind message no-extra-bind 113 | warning jshint 22:22 validthis message validthis 114 | warning jscs 22:22 validateQuoteMarks message validateQuoteMarks 115 | 116 | x.js 117 | error eslint 23:23 no-extra-boolean-cast message no-extra-boolean-cast 118 | error jshint 23:23 newcap message newcap 119 | error jscs 23:23 disallowMixedSpacesAndTabs message disallowMixedSpacesAndTabs 120 | 121 | y.js 122 | warning eslint 24:24 no-extra-parens message no-extra-parens 123 | warning jshint 24:24 indent message indent 124 | warning jscs 24:24 disallowTrailingWhitespace message disallowTrailingWhitespace 125 | 126 | z.js 127 | error eslint 25:25 no-extra-semi message no-extra-semi 128 | error jshint 25:25 undef message undef 129 | error jscs 25:25 disallowKeywordsOnNewLine message disallowKeywordsOnNewLine 130 | -------------------------------------------------------------------------------- /test/get-javascript-files.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var getJavaScriptFiles = require('../get-javascript-files'); 4 | 5 | test('Get Javascript Files', function runTests(t) { 6 | t.plan(1); 7 | t.ok(getJavaScriptFiles, 'Module exists'); 8 | }); 9 | -------------------------------------------------------------------------------- /test/group-file-messages-transform.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var groupFileMessagesTransform = require('../group-file-messages-transform'); 4 | var makeErrorStream = require('./helpers/make-error-stream'); 5 | var es = require('event-stream'); 6 | 7 | test('Group File Messages Transform', function runTests(t) { 8 | t.plan(3); 9 | t.ok(groupFileMessagesTransform, 'Module exists'); 10 | 11 | var linters = ['eslint', 'jshint', 'jscs']; 12 | var errorStreams = linters.map(makeErrorStream); 13 | var mergedStream = es.merge.apply(es, errorStreams); 14 | 15 | mergedStream 16 | .pipe(groupFileMessagesTransform(linters)) 17 | .pipe(es.writeArray(testStream)); 18 | 19 | function testStream(err, fileErrors) { 20 | if (err) { 21 | t.fail(); 22 | } 23 | t.equal(fileErrors.length, 26, 'Correct quantity of file errors'); 24 | 25 | var pass = fileErrors.every(function checkErrorQuantity(fileMessage) { 26 | return fileMessage.errors.length === 3; 27 | }); 28 | 29 | t.ok(pass, 'All fileError messages have 3 errors each'); 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /test/helpers/make-error-stream-fixture.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var mockfs = require('mock-fs'); 4 | var async = require('async'); 5 | var mkdirp = require('mkdirp'); 6 | 7 | // The following are strings that will produce an error for only 8 | var baseContent = 'alert("foo");'; 9 | 10 | var disable = { 11 | jscs: '/* jscs: disable */', 12 | eslint: '/*eslint-disable */', 13 | jshint: '// jshint ignore: start' 14 | }; 15 | 16 | // filename, jscs, jshint, eslint 17 | var data = [ 18 | ['a', 1, 0, 0], 19 | ['b', 0, 0, 0], 20 | ['c', 0, 1, 1], 21 | ['d', 0, 1, 0], 22 | ['e', 0, 0, 0], 23 | ['f', 0, 0, 1], 24 | ['g', 0, 0, 0], 25 | ['h', 0, 1, 0], 26 | ['i', 0, 0, 0], 27 | ['j', 0, 0, 0], 28 | ['k', 1, 1, 1], 29 | ['l', 1, 0, 1], 30 | ['m', 0, 0, 0], 31 | ['n', 0, 0, 0], 32 | ['o', 0, 1, 1], 33 | ['p', 0, 0, 0], 34 | ['q', 0, 0, 0], 35 | ['r', 0, 0, 0], 36 | ['s', 1, 1, 0], 37 | ['t', 0, 1, 1], 38 | ['u', 0, 0, 0], 39 | ['v', 0, 0, 1], 40 | ['w', 1, 0, 0], 41 | ['x', 0, 1, 1], 42 | ['y', 0, 0, 0], 43 | ['z', 0, 0, 0] 44 | ]; 45 | 46 | function createJavaScriptFixtures(destination, callback) { 47 | var xfs = mockfs.fs(); 48 | var fixtureFolder = path.resolve(destination); 49 | xfs.exists(fixtureFolder, function existsCallback(exists) { 50 | if (exists) { 51 | return callback(new Error('destination folder exists')); 52 | } 53 | mkdirp(fixtureFolder, {fs: xfs, mode: '0755'}, mkdirpCallback); 54 | }); 55 | 56 | function mkdirpCallback(err) { 57 | if (err) { 58 | return callback(err); 59 | } 60 | async.each(data, writeFixture, function asyncEachEnd(asyncErr) { 61 | if (asyncErr) { 62 | return callback(err); 63 | } 64 | callback(null, xfs); 65 | }); 66 | } 67 | 68 | function writeFixture(file, done) { 69 | var filename = file[0] + '.js'; 70 | var filePath = path.resolve(fixtureFolder, filename); 71 | var fileContent = [ 72 | file[1] ? '' : disable.jscs, 73 | file[2] ? '' : disable.jshint, 74 | file[3] ? '' : disable.eslint, 75 | baseContent 76 | ].join('\n'); 77 | xfs.writeFile(filePath, fileContent, 'utf8', done); 78 | } 79 | } 80 | 81 | module.exports = createJavaScriptFixtures; 82 | -------------------------------------------------------------------------------- /test/helpers/make-error-stream.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var jf = require('jsonfile'); 4 | var partial = require('partial'); 5 | var es = require('event-stream'); 6 | var makeFileStream = require('../../error-to-file-transform'); 7 | 8 | var eslintrc = jf.readFileSync(path.join(__dirname, '../../rc/.eslintrc')); 9 | var jshintrc = jf.readFileSync(path.join(__dirname, '../../rc/.jshintrc')); 10 | var jscsrc = jf.readFileSync(path.join(__dirname, '../../rc/.jscsrc')); 11 | 12 | var rules = { 13 | eslint: Object.keys(eslintrc.rules), 14 | jscs: Object.keys(jscsrc), 15 | jshint: Object.keys(jshintrc) 16 | }; 17 | 18 | rules.jshint = rules.jshint.slice(1, rules.jshint.indexOf('globals')); 19 | 20 | function makeFiles() { 21 | // one more than the quantity you want. 22 | return Array(26 + 1).join(' ').split('').map(generateFilename); 23 | } 24 | 25 | function generateFilename(value, index) { 26 | return String.fromCharCode(97 + index) + '.js'; 27 | } 28 | 29 | function isOdd(num) { 30 | return num % 2; 31 | } 32 | 33 | function makeError(linter, filename, index, array) { 34 | return { 35 | type: isOdd(index) ? 'error' : 'warning', 36 | linter: linter, 37 | file: filename, 38 | line: index, 39 | column: index, 40 | rule: rules[linter][index % rules[linter].length], 41 | message: rules[linter][index % rules[linter].length] + ' message' 42 | }; 43 | } 44 | 45 | function makeErrorStream(linter) { 46 | var files = makeFiles(); 47 | var errorsArray = files.map(partial(makeError, linter)); 48 | return es.readArray(errorsArray).pipe(makeFileStream(linter, files)); 49 | } 50 | 51 | module.exports = makeErrorStream; 52 | -------------------------------------------------------------------------------- /test/helpers/make-file-error-stream.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var makeErrorStream = require('./make-error-stream'); 3 | var groupFileMessagesTransform = require('../../group-file-messages-transform'); 4 | var es = require('event-stream'); 5 | 6 | function makeFileErrorStream() { 7 | var linters = ['eslint', 'jshint', 'jscs']; 8 | var errorStreams = linters.map(makeErrorStream); 9 | var mergedStream = es.merge.apply(es, errorStreams); 10 | 11 | return mergedStream 12 | .pipe(groupFileMessagesTransform(linters)); 13 | } 14 | 15 | module.exports = makeFileErrorStream; 16 | -------------------------------------------------------------------------------- /test/json-reporter.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var jsonReporter = require('../json-reporter'); 4 | var makeFileErrorStream = require('./helpers/make-file-error-stream'); 5 | var es = require('event-stream'); 6 | var fs = require('fs'); 7 | var path = require('path'); 8 | 9 | test('JSON Reporter', function runTests(t) { 10 | t.plan(2); 11 | t.ok(jsonReporter, 'Module exists'); 12 | 13 | var fixture = path.join(__dirname, 'fixtures/unit/json.stdout'); 14 | 15 | var expectedStdout = fs.readFileSync(fixture).toString(); 16 | 17 | makeFileErrorStream().pipe(es.writeArray(function print(err, array) { 18 | if (err) { 19 | t.fail(); 20 | } 21 | 22 | t.equal(jsonReporter(array), expectedStdout, 'Correct stdout'); 23 | })); 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /test/lint-stream.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var makeLintStream = require('../lint-stream')(); 3 | var path = require('path'); 4 | var getJavaScriptFiles = require('../get-javascript-files'); 5 | var fixturesPath = path.join(__dirname, 'fixtures/rules'); 6 | var test = require('tape'); 7 | var testResults = require(path.join(fixturesPath, 'output.json')); 8 | var root = path.resolve(__dirname, '..'); 9 | var commondir = require('commondir'); 10 | 11 | test('lint-trap JSON stream results', function testStream(t) { 12 | t.plan(testResults.length + 1); 13 | 14 | getJavaScriptFiles(fixturesPath, function lintFilesCallback(err, jsfiles) { 15 | if (err) { 16 | return t.fail(err); 17 | } 18 | var streamMessages = []; 19 | var opts = {stdin: false, lineLength: 80}; 20 | jsfiles.sort(); 21 | var dir = commondir(jsfiles); 22 | var lintStream = makeLintStream(jsfiles, dir, opts); 23 | 24 | lintStream.on('data', streamMessages.push.bind(streamMessages)); 25 | lintStream.on('error', t.fail.bind(t)); 26 | lintStream.on('end', onEnd); 27 | 28 | function onEnd() { 29 | t.equal(streamMessages.length, testResults.length, 30 | 'Correct number of lint messages'); 31 | testResults.forEach(checkTestResult); 32 | } 33 | 34 | function checkTestResult(expected) { 35 | var actual = streamMessages.filter(function match(message) { 36 | return message.file === expected.file; 37 | })[0]; 38 | t.deepEqual(actual, expected, path.relative(root, expected.file)); 39 | } 40 | 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/lint-trap.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var lintTrap = require('../lint-trap'); 4 | 5 | test('Lint Trap', function runTests(t) { 6 | t.plan(1); 7 | t.ok(lintTrap, 'Module exists'); 8 | }); 9 | -------------------------------------------------------------------------------- /test/lintrc.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | var execFile = require('child_process').execFile; 5 | var path = require('path'); 6 | var fs = require('fs'); 7 | 8 | var binPath = path.resolve(__dirname, '../bin/lint-trap.js'); 9 | var lintrcFixture = path.resolve(__dirname, './fixtures/lintrc'); 10 | 11 | test('lintrc file allows downgrading errors to warnings', function tape(t) { 12 | t.plan(3); 13 | 14 | var args = [path.join(lintrcFixture, './fixture.js')]; 15 | var opts = {}; 16 | 17 | var expectedStdoutPath = path.resolve(lintrcFixture, './fixture.stdout'); 18 | var expectedStdout = fs.readFileSync(expectedStdoutPath, 'utf8'); 19 | 20 | execFile(binPath, args, opts, function cb(err, stdout, stderr) { 21 | t.error(err, 'No error code'); 22 | t.equal(stdout, expectedStdout, 'Errors reduced to warnings'); 23 | t.equal(stderr, '', 'No output on stdout'); 24 | }); 25 | }); 26 | 27 | test('lintrc file allows downgrading errors to warnings', function tape(t) { 28 | t.plan(4); 29 | 30 | var args = [lintrcFixture]; 31 | var opts = {}; 32 | var expectedStdoutPath = path.resolve(lintrcFixture, './output.stdout'); 33 | var expectedStdout = fs.readFileSync(expectedStdoutPath, 'utf8'); 34 | 35 | execFile(binPath, args, opts, function cb(err, stdout, stderr) { 36 | t.ok(err, 'Error'); 37 | t.equal(err.code, 1, 'Error code is 1'); 38 | t.equal(stdout, expectedStdout, 'Errors reduced to warnings'); 39 | t.equal(stderr, '', 'No output on stdout'); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/load-lintrc.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var path = require('path'); 4 | var loadLintRC = require('../load-lintrc'); 5 | 6 | var fixturesPath = path.join(__dirname, './fixtures/lintrc'); 7 | 8 | test('no .lintrc files in parent folders', function tape(t) { 9 | t.plan(2); 10 | 11 | function loadLintRCCallback(err, lintrc) { 12 | t.error(err, 'No error if no lintrc found'); 13 | 14 | // If the following test fails, then you need to make sure there isn't 15 | // a lintrc in any of the parent directories that will modify the 16 | // results of subsequent tests. 17 | t.deepEqual(lintrc, {}, 'lintrc is an empty object.'); 18 | } 19 | 20 | loadLintRC(path.dirname(fixturesPath), loadLintRCCallback); 21 | }); 22 | 23 | test('single .lintrc loads correctly', function tape(t) { 24 | t.plan(2); 25 | 26 | var expectedLintRC = { 27 | eslint: { 28 | 'func-names': false 29 | } 30 | }; 31 | 32 | function loadLintRCCallback(err, lintrc) { 33 | t.error(err, 'No error'); 34 | t.deepEqual(lintrc, expectedLintRC, 'Correct .lintrc configuration'); 35 | } 36 | 37 | loadLintRC(fixturesPath, loadLintRCCallback); 38 | }); 39 | 40 | test('nested .lintrc files load correctly', function tape(t) { 41 | t.plan(2); 42 | 43 | var expectedLintRC = { 44 | eslint: { 45 | 'func-names': true, 46 | 'no-underscore-dangle': false 47 | } 48 | }; 49 | 50 | function loadLintRCCallback(err, lintrc) { 51 | t.error(err, 'No error'); 52 | t.deepEqual(lintrc, expectedLintRC, 'Correct .lintrc configuration'); 53 | } 54 | 55 | loadLintRC(path.join(fixturesPath, './subfolder'), loadLintRCCallback); 56 | }); 57 | -------------------------------------------------------------------------------- /test/post-install.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var exec = require('child_process').exec; 3 | var execFile = require('child_process').execFile; 4 | var async = require('async'); 5 | var path = require('path'); 6 | var fs = require('fs'); 7 | // var jsonfile = require('jsonfile'); 8 | var which = require('which'); 9 | var test = require('tape'); 10 | var debug = require('debug')('post-install'); 11 | var rimraf = require('rimraf'); 12 | var temp = require('temp'); 13 | temp.track(); 14 | var testFolder = temp.path({prefix: 'lint-trap-post-install'}); 15 | 16 | function makeFixture(f, cb) { 17 | debug('cloning fixture %s ...', f); 18 | 19 | var folderName = path.basename(f); 20 | var repoPath = path.join(testFolder, folderName); 21 | fs.exists(repoPath, function existsCallback(exists) { 22 | if (exists) { 23 | debug('fixture %s already exists. skipping clone.', f); 24 | cb(null, repoPath); 25 | } else { 26 | var cmd = ['git clone', f, repoPath].join(' '); 27 | 28 | exec(cmd, function handleCommandOutput(err, stdout, stderr) { 29 | if (err) { 30 | debug('Cloning failed\n', stderr); 31 | return cb(err); 32 | } 33 | debug('Cloned fixture %s', f); 34 | cb(err, repoPath); 35 | 36 | }); 37 | } 38 | }); 39 | } 40 | 41 | function makeFixtures(fixtures, cb) { 42 | async.map(fixtures, makeFixture, cb); 43 | } 44 | 45 | function installLintTrap(fixturePath, cb) { 46 | function npmInstallExecFileCallback(err, stdout, stderr) { 47 | if (err) { 48 | debug('Installing failed\n', stderr); 49 | return cb(err); 50 | } 51 | cb(); 52 | } 53 | 54 | debug('installing lint-tap into %s', fixturePath); 55 | which('npm', function whichNPMCallback(err, npmPath) { 56 | if (err) { 57 | return cb(err); 58 | } 59 | var rootPath = path.resolve(__dirname, '..'); 60 | 61 | var args = ['install', '--save-dev', rootPath]; 62 | var opts = {cwd: fixturePath}; 63 | 64 | execFile(npmPath, args, opts, npmInstallExecFileCallback); 65 | }); 66 | } 67 | 68 | function main(cb) { 69 | var fixtures = [ 70 | 'git@github.com:uber/sirvice.git' 71 | ]; 72 | 73 | makeFixtures(fixtures, function lintFixtures(fixturesErr, fixturePaths) { 74 | if (fixturesErr) { 75 | return cb(fixturesErr); 76 | } 77 | async.eachSeries(fixturePaths, installLintTrap, eachCallback); 78 | 79 | function eachCallback(err) { 80 | cb(err, fixturePaths); 81 | } 82 | }); 83 | } 84 | 85 | function testSymlink(t, fixturePath, linter) { 86 | var rcFile = '.' + linter + 'rc'; 87 | var rcPath = path.join(fixturePath, rcFile); 88 | var symlinkPath = './node_modules/lint-trap/rc/'; 89 | var symlinkDest = path.join(fixturePath, symlinkPath, rcFile); 90 | var lstat = fs.lstatSync(rcPath); 91 | 92 | var isSymlinkMsg = './' + rcFile + ' is a symlink'; 93 | t.ok(lstat.isSymbolicLink(), isSymlinkMsg); 94 | 95 | var correctSymlinkMsg = './' + rcFile + ' is symlinked to ' + 96 | symlinkPath + rcFile; 97 | t.equal(fs.readlinkSync(rcPath), symlinkDest, correctSymlinkMsg); 98 | } 99 | 100 | function testSymlinks(t, fixturePath) { 101 | var eachFn = testSymlink.bind(null, t, fixturePath); 102 | ['jshint', 'eslint', 'jscs'].forEach(eachFn); 103 | } 104 | 105 | // skip the post-install test for now. 106 | var skip = true; 107 | 108 | test('postinstall script', function testPostInstall(t) { 109 | 110 | if (skip) { 111 | t.plan(1); 112 | return t.ok(true); 113 | } 114 | 115 | main(function runTests(err, fixturePaths) { 116 | if (err) { 117 | debug('main', err); 118 | return test.fail(); 119 | } 120 | debug('running tests ...'); 121 | 122 | t.plan(6); 123 | fixturePaths.forEach(testSymlinks.bind(null, t)); 124 | async.each(fixturePaths, rimraf, function eachCallback() { 125 | t.end(); 126 | }); 127 | }); 128 | }); 129 | -------------------------------------------------------------------------------- /test/set-rules.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var setRules = require('../set-rules'); 4 | 5 | test('Set Rules', function runTests(t) { 6 | t.plan(1); 7 | t.ok(setRules, 'Module exists'); 8 | }); 9 | -------------------------------------------------------------------------------- /test/severity-transform.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var severityTransform = require('../severity-transform'); 4 | 5 | test('Severity Transform', function runTests(t) { 6 | t.plan(1); 7 | t.ok(severityTransform, 'Module exists'); 8 | }); 9 | -------------------------------------------------------------------------------- /test/stylish-reporter.tape.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var test = require('tape'); 3 | var stylishReporter = require('../stylish-reporter'); 4 | var makeFileErrorStream = require('./helpers/make-file-error-stream'); 5 | var es = require('event-stream'); 6 | var fs = require('fs'); 7 | var path = require('path'); 8 | var process = require('process'); 9 | 10 | test('Stylish Reporter', function runTests(t) { 11 | t.plan(2); 12 | t.ok(stylishReporter, 'Module exists'); 13 | 14 | var fixture = path.join(__dirname, 'fixtures/unit/stylish.stdout'); 15 | 16 | var expectedStdout = fs.readFileSync(fixture).toString(); 17 | 18 | makeFileErrorStream().pipe(es.writeArray(function print(err, array) { 19 | if (err) { 20 | t.fail(); 21 | } 22 | 23 | process.stdout.isTTY = false; 24 | t.equal(stylishReporter(array), expectedStdout, 'Correct stdout'); 25 | })); 26 | }); 27 | --------------------------------------------------------------------------------