├── .editorconfig ├── .jshintrc ├── index.js ├── package.json ├── .eslintrc └── Readme.md /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | end_of_line = lf 10 | indent_style = space 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "camelcase": true, 3 | "curly": true, 4 | "eqeqeq": true, 5 | "freeze": true, 6 | "indent": 2, 7 | "newcap": true, 8 | "quotmark": "single", 9 | "maxdepth": 3, 10 | "maxstatements": 15, 11 | "maxlen": 80, 12 | "eqnull": true, 13 | "funcscope": true, 14 | "node": true 15 | } 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var readFile = require('fs').readFileSync; 4 | var pathJoin = require('path').join; 5 | 6 | module.exports = { 7 | eslintrc: loadJson('.eslintrc'), 8 | jshintrc: loadJson('.jshintrc') 9 | }; 10 | 11 | function loadJson(configFile) { 12 | var data = readFile(pathJoin(__dirname, configFile), 'utf-8'); 13 | return JSON.parse(data); 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-style-guide", 3 | "description": "A guide for styling your node.js / JavaScript code.", 4 | "version": "1.0.0", 5 | "author": "Felix Geisendörfer (http://debuggable.com/)", 6 | "bugs": { 7 | "url": "https://github.com/felixge/node-style-guide/issues" 8 | }, 9 | "homepage": "https://github.com/felixge/node-style-guide#readme", 10 | "keywords": [ 11 | "eslint", 12 | "jshint" 13 | ], 14 | "license": "CC-BY-SA-3.0", 15 | "main": "index.js", 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/felixge/node-style-guide.git" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true 4 | }, 5 | "rules": { 6 | "array-bracket-spacing": [2, "never"], 7 | "block-scoped-var": 2, 8 | "brace-style": [2, "1tbs"], 9 | "camelcase": 1, 10 | "computed-property-spacing": [2, "never"], 11 | "curly": 2, 12 | "eol-last": 2, 13 | "eqeqeq": [2, "smart"], 14 | "max-depth": [1, 3], 15 | "max-len": [1, 80], 16 | "max-statements": [1, 15], 17 | "new-cap": 1, 18 | "no-extend-native": 2, 19 | "no-mixed-spaces-and-tabs": 2, 20 | "no-trailing-spaces": 2, 21 | "no-unused-vars": 1, 22 | "no-use-before-define": [2, "nofunc"], 23 | "object-curly-spacing": [2, "never"], 24 | "quotes": [2, "single", "avoid-escape"], 25 | "semi": [2, "always"], 26 | "keyword-spacing": [2, {"before": true, "after": true}], 27 | "space-unary-ops": 2 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Node.js Style Guide 2 | 3 | This is a guide for writing consistent and aesthetically pleasing node.js code. 4 | It is inspired by what is popular within the community, and flavored with some 5 | personal opinions. 6 | 7 | There is a .jshintrc which enforces these rules as closely as possible. You can 8 | either use that and adjust it, or use 9 | [this script](https://gist.github.com/kentcdodds/11293570) to make your own. 10 | 11 | This guide was created by [Felix Geisendörfer](http://felixge.de/) and is 12 | licensed under the [CC BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/) 13 | license. You are encouraged to fork this repository and make adjustments 14 | according to your preferences. 15 | 16 | ![Creative Commons License](http://i.creativecommons.org/l/by-sa/3.0/88x31.png) 17 | 18 | ## Table of contents 19 | 20 | ### Formatting 21 | * [2 Spaces for indentation](#2-spaces-for-indentation) 22 | * [Newlines](#newlines) 23 | * [No trailing whitespace](#no-trailing-whitespace) 24 | * [Use Semicolons](#use-semicolons) 25 | * [80 characters per line](#80-characters-per-line) 26 | * [Use single quotes](#use-single-quotes) 27 | * [Opening braces go on the same line](#opening-braces-go-on-the-same-line) 28 | * [Declare one variable per var statement](#declare-one-variable-per-var-statement) 29 | 30 | ### Naming Conventions 31 | * [Use lowerCamelCase for variables, properties and function names](#use-lowercamelcase-for-variables-properties-and-function-names) 32 | * [Use UpperCamelCase for class names](#use-uppercamelcase-for-class-names) 33 | * [Use UPPERCASE for Constants](#use-uppercase-for-constants) 34 | 35 | ### Variables 36 | * [Object / Array creation](#object--array-creation) 37 | 38 | ### Conditionals 39 | * [Use the === operator](#use-the--operator) 40 | * [Use multi-line ternary operator](#use-multi-line-ternary-operator) 41 | * [Use descriptive conditions](#use-descriptive-conditions) 42 | 43 | ### Functions 44 | * [Write small functions](#write-small-functions) 45 | * [Return early from functions](#return-early-from-functions) 46 | * [Name your closures](#name-your-closures) 47 | * [No nested closures](#no-nested-closures) 48 | * [Method chaining](#method-chaining) 49 | 50 | ### Comments 51 | * [Use slashes for comments](#use-slashes-for-comments) 52 | 53 | ### Miscellaneous 54 | * [Object.freeze, Object.preventExtensions, Object.seal, with, eval](#objectfreeze-objectpreventextensions-objectseal-with-eval) 55 | * [Requires At Top](#requires-at-top) 56 | * [Getters and setters](#getters-and-setters) 57 | * [Do not extend built-in prototypes](#do-not-extend-built-in-prototypes) 58 | 59 | ## Formatting 60 | 61 | You may want to use [editorconfig.org](http://editorconfig.org/) to enforce the formatting settings in your editor. Use the [Node.js Style Guide .editorconfig file](.editorconfig) to have indentation, newslines and whitespace behavior automatically set to the rules set up below. 62 | 63 | ### 2 Spaces for indentation 64 | 65 | Use 2 spaces for indenting your code and swear an oath to never mix tabs and 66 | spaces - a special kind of hell is awaiting you otherwise. 67 | 68 | ### Newlines 69 | 70 | Use UNIX-style newlines (`\n`), and a newline character as the last character 71 | of a file. Windows-style newlines (`\r\n`) are forbidden inside any repository. 72 | 73 | ### No trailing whitespace 74 | 75 | Just like you brush your teeth after every meal, you clean up any trailing 76 | whitespace in your JS files before committing. Otherwise the rotten smell of 77 | careless neglect will eventually drive away contributors and/or co-workers. 78 | 79 | ### Use Semicolons 80 | 81 | According to [scientific research][hnsemicolons], the usage of semicolons is 82 | a core value of our community. Consider the points of [the opposition][], but 83 | be a traditionalist when it comes to abusing error correction mechanisms for 84 | cheap syntactic pleasures. 85 | 86 | [the opposition]: http://blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding 87 | [hnsemicolons]: http://news.ycombinator.com/item?id=1547647 88 | 89 | ### 80 characters per line 90 | 91 | Limit your lines to 80 characters. Yes, screens have gotten much bigger over the 92 | last few years, but your brain has not. Use the additional room for split screen, 93 | your editor supports that, right? 94 | 95 | ### Use single quotes 96 | 97 | Use single quotes, unless you are writing JSON. 98 | 99 | *Right:* 100 | 101 | ```js 102 | var foo = 'bar'; 103 | ``` 104 | 105 | *Wrong:* 106 | 107 | ```js 108 | var foo = "bar"; 109 | ``` 110 | 111 | ### Opening braces go on the same line 112 | 113 | Your opening braces go on the same line as the statement. 114 | 115 | *Right:* 116 | 117 | ```js 118 | if (true) { 119 | console.log('winning'); 120 | } 121 | ``` 122 | 123 | *Wrong:* 124 | 125 | ```js 126 | if (true) 127 | { 128 | console.log('losing'); 129 | } 130 | ``` 131 | 132 | Also, notice the use of whitespace before and after the condition statement. 133 | 134 | ### Declare one variable per var statement 135 | 136 | Declare one variable per var statement, it makes it easier to re-order the 137 | lines. However, ignore [Crockford][crockfordconvention] when it comes to 138 | declaring variables deeper inside a function, just put the declarations wherever 139 | they make sense. 140 | 141 | *Right:* 142 | 143 | ```js 144 | var keys = ['foo', 'bar']; 145 | var values = [23, 42]; 146 | 147 | var object = {}; 148 | while (keys.length) { 149 | var key = keys.pop(); 150 | object[key] = values.pop(); 151 | } 152 | ``` 153 | 154 | *Wrong:* 155 | 156 | ```js 157 | var keys = ['foo', 'bar'], 158 | values = [23, 42], 159 | object = {}, 160 | key; 161 | 162 | while (keys.length) { 163 | key = keys.pop(); 164 | object[key] = values.pop(); 165 | } 166 | ``` 167 | 168 | [crockfordconvention]: http://javascript.crockford.com/code.html 169 | 170 | ### Naming Conventions 171 | 172 | ### Use lowerCamelCase for variables, properties and function names 173 | 174 | Variables, properties and function names should use `lowerCamelCase`. They 175 | should also be descriptive. Single character variables and uncommon 176 | abbreviations should generally be avoided. 177 | 178 | *Right:* 179 | 180 | ```js 181 | var adminUser = db.query('SELECT * FROM users ...'); 182 | ``` 183 | 184 | *Wrong:* 185 | 186 | ```js 187 | var admin_user = db.query('SELECT * FROM users ...'); 188 | ``` 189 | 190 | ### Use UpperCamelCase for class names 191 | 192 | Class names should be capitalized using `UpperCamelCase`. 193 | 194 | *Right:* 195 | 196 | ```js 197 | function BankAccount() { 198 | } 199 | ``` 200 | 201 | *Wrong:* 202 | 203 | ```js 204 | function bank_Account() { 205 | } 206 | ``` 207 | 208 | ## Use UPPERCASE for Constants 209 | 210 | Constants should be declared as regular variables or static class properties, 211 | using all uppercase letters. 212 | 213 | *Right:* 214 | 215 | ```js 216 | var SECOND = 1 * 1000; 217 | 218 | function File() { 219 | } 220 | File.FULL_PERMISSIONS = 0777; 221 | ``` 222 | 223 | *Wrong:* 224 | 225 | ```js 226 | const SECOND = 1 * 1000; 227 | 228 | function File() { 229 | } 230 | File.fullPermissions = 0777; 231 | ``` 232 | 233 | [const]: https://developer.mozilla.org/en/JavaScript/Reference/Statements/const 234 | 235 | ## Variables 236 | 237 | ### Object / Array creation 238 | 239 | Use trailing commas and put *short* declarations on a single line. Only quote 240 | keys when your interpreter complains: 241 | 242 | *Right:* 243 | 244 | ```js 245 | var a = ['hello', 'world']; 246 | var b = { 247 | good: 'code', 248 | 'is generally': 'pretty', 249 | }; 250 | ``` 251 | 252 | *Wrong:* 253 | 254 | ```js 255 | var a = [ 256 | 'hello', 'world' 257 | ]; 258 | var b = {"good": 'code' 259 | , is generally: 'pretty' 260 | }; 261 | ``` 262 | 263 | ## Conditionals 264 | 265 | ### Use the === operator 266 | 267 | Programming is not about remembering [stupid rules][comparisonoperators]. Use 268 | the triple equality operator as it will work just as expected. 269 | 270 | *Right:* 271 | 272 | ```js 273 | var a = 0; 274 | if (a !== '') { 275 | console.log('winning'); 276 | } 277 | 278 | ``` 279 | 280 | *Wrong:* 281 | 282 | ```js 283 | var a = 0; 284 | if (a == '') { 285 | console.log('losing'); 286 | } 287 | ``` 288 | 289 | [comparisonoperators]: https://developer.mozilla.org/en/JavaScript/Reference/Operators/Comparison_Operators 290 | 291 | ### Use multi-line ternary operator 292 | 293 | The ternary operator should not be used on a single line. Split it up into multiple lines instead. 294 | 295 | *Right:* 296 | 297 | ```js 298 | var foo = (a === b) 299 | ? 1 300 | : 2; 301 | ``` 302 | 303 | *Wrong:* 304 | 305 | ```js 306 | var foo = (a === b) ? 1 : 2; 307 | ``` 308 | 309 | ### Use descriptive conditions 310 | 311 | Any non-trivial conditions should be assigned to a descriptively named variable or function: 312 | 313 | *Right:* 314 | 315 | ```js 316 | var isValidPassword = password.length >= 4 && /^(?=.*\d).{4,}$/.test(password); 317 | 318 | if (isValidPassword) { 319 | console.log('winning'); 320 | } 321 | ``` 322 | 323 | *Wrong:* 324 | 325 | ```js 326 | if (password.length >= 4 && /^(?=.*\d).{4,}$/.test(password)) { 327 | console.log('losing'); 328 | } 329 | ``` 330 | 331 | ## Functions 332 | 333 | ### Write small functions 334 | 335 | Keep your functions short. A good function fits on a slide that the people in 336 | the last row of a big room can comfortably read. So don't count on them having 337 | perfect vision and limit yourself to ~15 lines of code per function. 338 | 339 | ### Return early from functions 340 | 341 | To avoid deep nesting of if-statements, always return a function's value as early 342 | as possible. 343 | 344 | *Right:* 345 | 346 | ```js 347 | function isPercentage(val) { 348 | if (val < 0) { 349 | return false; 350 | } 351 | 352 | if (val > 100) { 353 | return false; 354 | } 355 | 356 | return true; 357 | } 358 | ``` 359 | 360 | *Wrong:* 361 | 362 | ```js 363 | function isPercentage(val) { 364 | if (val >= 0) { 365 | if (val < 100) { 366 | return true; 367 | } else { 368 | return false; 369 | } 370 | } else { 371 | return false; 372 | } 373 | } 374 | ``` 375 | 376 | Or for this particular example it may also be fine to shorten things even 377 | further: 378 | 379 | ```js 380 | function isPercentage(val) { 381 | var isInRange = (val >= 0 && val <= 100); 382 | return isInRange; 383 | } 384 | ``` 385 | 386 | ### Name your closures 387 | 388 | Feel free to give your closures a name. It shows that you care about them, and 389 | will produce better stack traces, heap and cpu profiles. 390 | 391 | *Right:* 392 | 393 | ```js 394 | req.on('end', function onEnd() { 395 | console.log('winning'); 396 | }); 397 | ``` 398 | 399 | *Wrong:* 400 | 401 | ```js 402 | req.on('end', function() { 403 | console.log('losing'); 404 | }); 405 | ``` 406 | 407 | ### No nested closures 408 | 409 | Use closures, but don't nest them. Otherwise your code will become a mess. 410 | 411 | *Right:* 412 | 413 | ```js 414 | setTimeout(function() { 415 | client.connect(afterConnect); 416 | }, 1000); 417 | 418 | function afterConnect() { 419 | console.log('winning'); 420 | } 421 | ``` 422 | 423 | *Wrong:* 424 | 425 | ```js 426 | setTimeout(function() { 427 | client.connect(function() { 428 | console.log('losing'); 429 | }); 430 | }, 1000); 431 | ``` 432 | 433 | 434 | ### Method chaining 435 | 436 | One method per line should be used if you want to chain methods. 437 | 438 | You should also indent these methods so it's easier to tell they are part of the same chain. 439 | 440 | *Right:* 441 | 442 | ```js 443 | User 444 | .findOne({ name: 'foo' }) 445 | .populate('bar') 446 | .exec(function(err, user) { 447 | return true; 448 | }); 449 | ```` 450 | 451 | *Wrong:* 452 | 453 | ```js 454 | User 455 | .findOne({ name: 'foo' }) 456 | .populate('bar') 457 | .exec(function(err, user) { 458 | return true; 459 | }); 460 | 461 | User.findOne({ name: 'foo' }) 462 | .populate('bar') 463 | .exec(function(err, user) { 464 | return true; 465 | }); 466 | 467 | User.findOne({ name: 'foo' }).populate('bar') 468 | .exec(function(err, user) { 469 | return true; 470 | }); 471 | 472 | User.findOne({ name: 'foo' }).populate('bar') 473 | .exec(function(err, user) { 474 | return true; 475 | }); 476 | ```` 477 | 478 | ## Comments 479 | 480 | ### Use slashes for comments 481 | 482 | Use slashes for both single line and multi line comments. Try to write 483 | comments that explain higher level mechanisms or clarify difficult 484 | segments of your code. Don't use comments to restate trivial things. 485 | 486 | *Right:* 487 | 488 | ```js 489 | // 'ID_SOMETHING=VALUE' -> ['ID_SOMETHING=VALUE', 'SOMETHING', 'VALUE'] 490 | var matches = item.match(/ID_([^\n]+)=([^\n]+)/)); 491 | 492 | // This function has a nasty side effect where a failure to increment a 493 | // redis counter used for statistics will cause an exception. This needs 494 | // to be fixed in a later iteration. 495 | function loadUser(id, cb) { 496 | // ... 497 | } 498 | 499 | var isSessionValid = (session.expires < Date.now()); 500 | if (isSessionValid) { 501 | // ... 502 | } 503 | ``` 504 | 505 | *Wrong:* 506 | 507 | ```js 508 | // Execute a regex 509 | var matches = item.match(/ID_([^\n]+)=([^\n]+)/); 510 | 511 | // Usage: loadUser(5, function() { ... }) 512 | function loadUser(id, cb) { 513 | // ... 514 | } 515 | 516 | // Check if the session is valid 517 | var isSessionValid = (session.expires < Date.now()); 518 | // If the session is valid 519 | if (isSessionValid) { 520 | // ... 521 | } 522 | ``` 523 | 524 | ## Miscellaneous 525 | 526 | ### Object.freeze, Object.preventExtensions, Object.seal, with, eval 527 | 528 | Crazy shit that you will probably never need. Stay away from it. 529 | 530 | ### Requires At Top 531 | 532 | Always put requires at top of file to clearly illustrate a file's dependencies. Besides giving an overview for others at a quick glance of dependencies and possible memory impact, it allows one to determine if they need a package.json file should they choose to use the file elsewhere. 533 | 534 | ### Getters and setters 535 | 536 | Do not use setters, they cause more problems for people who try to use your 537 | software than they can solve. 538 | 539 | Feel free to use getters that are free from [side effects][sideeffect], like 540 | providing a length property for a collection class. 541 | 542 | [sideeffect]: http://en.wikipedia.org/wiki/Side_effect_(computer_science) 543 | 544 | ### Do not extend built-in prototypes 545 | 546 | Do not extend the prototype of native JavaScript objects. Your future self will 547 | be forever grateful. 548 | 549 | *Right:* 550 | 551 | ```js 552 | var a = []; 553 | if (!a.length) { 554 | console.log('winning'); 555 | } 556 | ``` 557 | 558 | *Wrong:* 559 | 560 | ```js 561 | Array.prototype.empty = function() { 562 | return !this.length; 563 | } 564 | 565 | var a = []; 566 | if (a.empty()) { 567 | console.log('losing'); 568 | } 569 | ``` 570 | --------------------------------------------------------------------------------