├── .eslintrc ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── package.json ├── rules ├── check-function-inline.js ├── no-array-iterators.js ├── no-instanceof-guard.js └── no-self-in-constructor.js └── test └── index.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "perf-standard", 3 | "rules": { 4 | "spaced-comment": 0, 5 | "max-len": 0 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.a 8 | *.o 9 | *.so 10 | *.node 11 | 12 | # Node Waf Byproducts # 13 | ####################### 14 | .lock-wscript 15 | build/ 16 | autom4te.cache/ 17 | 18 | # Node Modules # 19 | ################ 20 | # Better to let npm install these from the package.json defintion 21 | # rather than maintain this manually 22 | node_modules/ 23 | 24 | # Packages # 25 | ############ 26 | # it's better to unpack these files and commit the raw source 27 | # git has its own built in compression methods 28 | *.7z 29 | *.dmg 30 | *.gz 31 | *.iso 32 | *.jar 33 | *.rar 34 | *.tar 35 | *.zip 36 | 37 | # Logs and databases # 38 | ###################### 39 | *.log 40 | dump.rdb 41 | *.tap 42 | *.xml 43 | 44 | # OS generated files # 45 | ###################### 46 | .DS_Store? 47 | .DS_Store 48 | ehthumbs.db 49 | Icon? 50 | Thumbs.db 51 | coverage 52 | 53 | # Text Editor Byproducts # 54 | ########################## 55 | *.sw? 56 | .idea/ 57 | 58 | # Python object code 59 | ########################## 60 | *.py[oc] 61 | 62 | # All translation files # 63 | ######################### 64 | static/translations-s3/ 65 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.8" 4 | - "0.10" 5 | - "0.11" 6 | before_install: npm i npm@latest -g 7 | script: npm run travis 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 eslint-plugin-perf-standard. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eslint-plugin-perf-standard 2 | 3 | 8 | 9 | 10 | 11 | A set of custom plugins to enforce high performance JS 12 | 13 | ## Example 14 | 15 | ```js 16 | { 17 | "rules": { 18 | "perf-standard/no-instanceof-guard": 2, 19 | "perf-standard/no-self-in-constructor": 2, 20 | "perf-standard/check-function-inline": 1 21 | } 22 | } 23 | ``` 24 | 25 | Currently the plugins we have are: 26 | 27 | ### `no-instanceof-gaurd` 28 | 29 | This disables the common anti-pattern of: 30 | 31 | ```js 32 | function Foo() { 33 | if (!(this instanceof Foo)) return new Foo() 34 | } 35 | ``` 36 | 37 | ### `no-self-in-constructor` 38 | 39 | This disables the ability to use `var self = this` in 40 | the body of a constructor function. 41 | 42 | For performance reasons we should use `this.foo = bar;` in 43 | constructors. 44 | 45 | Using `var self = this` in methods is fine. 46 | 47 | ### `check-function-inline` 48 | 49 | This lint rule checks to see if a function is between 600 50 | and 660 characters and then warns that the function will not 51 | be inlined in V8. 52 | 53 | ## Installation 54 | 55 | `npm install eslint-plugin-perf-standard` 56 | 57 | ## Tests 58 | 59 | `npm test` 60 | 61 | ## Contributors 62 | 63 | - Raynos 64 | 65 | ## MIT Licensed 66 | 67 | [build-png]: https://secure.travis-ci.org/Raynos/eslint-plugin-perf-standard.png 68 | [build]: https://travis-ci.org/Raynos/eslint-plugin-perf-standard 69 | [cover-png]: https://coveralls.io/repos/Raynos/eslint-plugin-perf-standard/badge.png 70 | [cover]: https://coveralls.io/r/Raynos/eslint-plugin-perf-standard 71 | [dep-png]: https://david-dm.org/Raynos/eslint-plugin-perf-standard.png 72 | [dep]: https://david-dm.org/Raynos/eslint-plugin-perf-standard 73 | [npm-png]: https://nodei.co/npm/eslint-plugin-perf-standard.png?stars&downloads 74 | [npm]: https://nodei.co/npm/eslint-plugin-perf-standard 75 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var noInstanceofGuard = require('./rules/no-instanceof-guard.js'); 4 | var noSelfInConstructor = require('./rules/no-self-in-constructor.js'); 5 | var checkFunctionInline = require('./rules/check-function-inline.js'); 6 | var noArrayIterators = require('./rules/no-array-iterators.js'); 7 | 8 | module.exports = { 9 | rules: { 10 | 'no-instanceof-guard': noInstanceofGuard, 11 | 'no-self-in-constructor': noSelfInConstructor, 12 | 'check-function-inline': checkFunctionInline, 13 | 'no-array-iterators': noArrayIterators 14 | }, 15 | rulesConfig: { 16 | 'no-instanceof-guard': 2, 17 | 'check-function-inline': [1, { 18 | maxCharacters: 660 19 | }], 20 | 'no-self-in-constructor': 2, 21 | 'no-array-iterators': 2 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-perf-standard", 3 | "version": "1.0.3", 4 | "description": "A set of eslint rules for performant JS", 5 | "keywords": [], 6 | "author": "Raynos ", 7 | "repository": "git://github.com/Raynos/eslint-plugin-perf-standard.git", 8 | "main": "index", 9 | "homepage": "https://github.com/Raynos/eslint-plugin-perf-standard", 10 | "bugs": { 11 | "url": "https://github.com/Raynos/eslint-plugin-perf-standard/issues", 12 | "email": "raynos2@gmail.com" 13 | }, 14 | "contributors": [ 15 | { 16 | "name": "Raynos" 17 | } 18 | ], 19 | "dependencies": {}, 20 | "devDependencies": { 21 | "eslint": "1.8.0", 22 | "eslint-config-perf-standard": "^2.0.0", 23 | "istanbul": "^0.3.5", 24 | "opn": "^1.0.1", 25 | "tape": "^3.4.0" 26 | }, 27 | "licenses": [ 28 | { 29 | "type": "MIT", 30 | "url": "http://github.com/Raynos/eslint-plugin-perf-standard/raw/master/LICENSE" 31 | } 32 | ], 33 | "scripts": { 34 | "test": "npm run jshint -s && node test/index.js", 35 | "unit-test": "node test/index.js | tap-spec", 36 | "jshint": "eslint $(git ls-files | grep '.js$') && echo '# linter passed'", 37 | "view-cover": "opn ./coverage/index.html" 38 | }, 39 | "engine": { 40 | "node": ">= 0.8.x" 41 | }, 42 | "pre-commit": [ 43 | "test" 44 | ], 45 | "pre-commit.silent": true, 46 | "ngen-version": "5.1.0" 47 | } 48 | -------------------------------------------------------------------------------- /rules/check-function-inline.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var DEFAULT_MIN_CHARACTERS = 600; 4 | var CONSIDERED_MAX_CHARACTERS = 660; 5 | 6 | module.exports = checkFunctionInline; 7 | 8 | function checkFunctionInline(context) { 9 | var maxChar = context.options[0] && context.options[0].maxCharacters; 10 | maxChar = maxChar || CONSIDERED_MAX_CHARACTERS; 11 | 12 | return { 13 | 'FunctionDeclaration': function checkDec(node) { 14 | checkInlineAbility(node); 15 | }, 16 | 'FunctionExpression': function checkExpr(node) { 17 | checkInlineAbility(node); 18 | } 19 | }; 20 | 21 | function checkInlineAbility(node) { 22 | var sourceLength = context.getSourceCode().getText(node).length; 23 | if (sourceLength > DEFAULT_MIN_CHARACTERS && 24 | sourceLength < maxChar 25 | ) { 26 | var message = 'Function exceeds default limit by ' + 27 | (sourceLength - DEFAULT_MIN_CHARACTERS) + ' characters'; 28 | context.report(node, message); 29 | } 30 | } 31 | } 32 | 33 | module.exports.schema = [{ 34 | 'type': 'object', 35 | 'properties': { 36 | 'maxCharacters': { 37 | 'type': 'string' 38 | } 39 | }, 40 | 'additionalProperties': false 41 | }]; 42 | -------------------------------------------------------------------------------- /rules/no-array-iterators.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var bannedMethods = { 4 | 'filter': true, 5 | 'forEach': true, 6 | 'some': true, 7 | 'every': true, 8 | 'map': true, 9 | 'reduce': true, 10 | 'reduceRight': true 11 | }; 12 | 13 | module.exports = noArrayIterators; 14 | 15 | function noArrayIterators(context) { 16 | return { 17 | 'CallExpression': function call(node) { 18 | var callee = node.callee; 19 | if (callee.type !== 'MemberExpression') { 20 | return; 21 | } 22 | 23 | var methodName = callee.property.name; 24 | if (!bannedMethods[methodName]) { 25 | return; 26 | } 27 | 28 | var args = node.arguments; 29 | if (args.length === 0) { 30 | return; 31 | } 32 | 33 | var argType = args[0].type; 34 | if (argType !== 'FunctionExpression' && 35 | argType !== 'Identifier' 36 | ) { 37 | return; 38 | } 39 | 40 | context.report(node, 'no array iterators allowed'); 41 | } 42 | }; 43 | } 44 | 45 | module.exports.schema = []; 46 | -------------------------------------------------------------------------------- /rules/no-instanceof-guard.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = noInstanceofGuard; 4 | 5 | function noInstanceofGuard(context) { 6 | return { 7 | 'BinaryExpression': function bin(node) { 8 | if (node.operator === 'instanceof' && 9 | (node.left && node.left.type === 'ThisExpression') && 10 | (node.parent && node.parent.operator === '!') 11 | ) { 12 | context.report( 13 | node, 'expected no instanceof guard in constructor' 14 | ); 15 | } 16 | } 17 | }; 18 | } 19 | 20 | module.exports.schema = []; 21 | -------------------------------------------------------------------------------- /rules/no-self-in-constructor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isUpperCaseLetter = /[A-Z]/; 4 | 5 | module.exports = noSelfInConstructor; 6 | 7 | function noSelfInConstructor(context) { 8 | return { 9 | 'AssignmentExpression': function checkSelf(node) { 10 | if (!isAssignmentToSelf(node)) { 11 | return; 12 | } 13 | 14 | var parent = findParent(node, [ 15 | 'FunctionDeclaration', 16 | 'FunctionExpression' 17 | ]); 18 | if (!parent) { 19 | return; 20 | } 21 | 22 | var name = parent.id.name; 23 | if (!isUpperCaseLetter.test(name[0])) { 24 | return; 25 | } 26 | 27 | var msg = 'expected no self.foo = bar in constructor ' + name; 28 | context.report(node, msg); 29 | } 30 | }; 31 | } 32 | 33 | module.exports.schema = []; 34 | 35 | function findParent(node, types) { 36 | var parent = node; 37 | var candidate = null; 38 | 39 | while (parent) { 40 | parent = parent.parent; 41 | 42 | if (types.indexOf(parent.type) >= 0) { 43 | candidate = parent; 44 | break; 45 | } 46 | } 47 | 48 | return candidate; 49 | } 50 | 51 | function isAssignmentToSelf(node) { 52 | return (node.left && node.left.type === 'MemberExpression') && 53 | (node.left.object && node.left.object.name === 'self'); 54 | } 55 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var console = require('console'); 4 | var RuleTester = require('eslint').RuleTester; 5 | 6 | var noInstanceofGuard = require('../rules/no-instanceof-guard.js'); 7 | var noSelfInConstructor = require('../rules/no-self-in-constructor.js'); 8 | var checkFunctionInline = require('../rules/check-function-inline.js'); 9 | var noArrayIterators = require('../rules/no-array-iterators.js'); 10 | 11 | var ruleTester = new RuleTester(); 12 | 13 | ruleTester.run('no-instanceof-gaurd', noInstanceofGuard, { 14 | valid: [ 15 | [ 16 | 'function Foo() {', 17 | ' this.bar = "bar";', 18 | '}' 19 | ].join('\n') 20 | ], 21 | invalid: [{ 22 | code: [ 23 | 'function Foo() {', 24 | ' if (!(this instanceof Foo)) return new Foo();', 25 | ' ', 26 | ' this.bar = "bar";', 27 | '}' 28 | ].join('\n'), 29 | errors: [{ 30 | message: 'expected no instanceof guard in constructor' 31 | }] 32 | }] 33 | }); 34 | 35 | var smallSrc = 'var foo = "' + buildStr(290) + '";'; 36 | var largeSrc = 'var foo = "' + buildStr(580) + '";'; 37 | 38 | ruleTester.run('check-function-inline', checkFunctionInline, { 39 | valid: [ 40 | [ 41 | 'function foo() {', 42 | ' ' + smallSrc, 43 | '}' 44 | ].join('\n') 45 | ], 46 | invalid: [{ 47 | code: [ 48 | 'function foo() {', 49 | ' ' + largeSrc, 50 | '}' 51 | ].join('\n'), 52 | errors: [{ 53 | message: 'Function exceeds default limit by 16 characters' 54 | }] 55 | }, { 56 | code: [ 57 | 'function foo() {', 58 | ' // ' + smallSrc, 59 | ' ' + smallSrc, 60 | '}' 61 | ].join('\n'), 62 | errors: [{ 63 | message: 'Function exceeds default limit by 37 characters' 64 | }] 65 | }] 66 | }); 67 | 68 | ruleTester.run('no-self-in-constructor', noSelfInConstructor, { 69 | valid: [ 70 | [ 71 | 'function Foo() {', 72 | ' this.foo = "bar";', 73 | '}' 74 | ].join('\n'), 75 | [ 76 | 'Foo.prototype.foo = function foo() {', 77 | ' var self = this;', 78 | ' self.bar = "bar";', 79 | '};' 80 | ].join('\n') 81 | ], 82 | invalid: [{ 83 | code: [ 84 | 'function Foo() {', 85 | ' var self = this;', 86 | ' self.foo = "bar";', 87 | '}' 88 | ].join('\n'), 89 | errors: [{ 90 | message: 'expected no self.foo = bar in constructor Foo' 91 | }] 92 | }, { 93 | code: [ 94 | 'module.exports = function Foo() {', 95 | ' var self = this;', 96 | ' self.foo = "bar";', 97 | '};' 98 | ].join('\n'), 99 | errors: [{ 100 | message: 'expected no self.foo = bar in constructor Foo' 101 | }] 102 | }] 103 | }); 104 | 105 | ruleTester.run('no-array-iterators', noArrayIterators, { 106 | valid: [ 107 | [ 108 | 'var arr = [1,2,3];', 109 | 'var result = [];', 110 | 'for (var i = 0; i < arr.length; i++) {', 111 | ' result.push(arr[i] * 2);', 112 | '}' 113 | ].join('\n'), 114 | [ 115 | 'var foo = new Foo()', 116 | 'foo.filter(42)' 117 | ].join('\n') 118 | ], 119 | invalid: [{ 120 | code: [ 121 | 'var arr = [1,2,3];', 122 | 'var result = arr.map(function pig(x) {', 123 | ' return x * 2;', 124 | '});' 125 | ].join('\n'), 126 | errors: [{ 127 | message: 'no array iterators allowed' 128 | }] 129 | }, { 130 | code: [ 131 | 'var arr = [1,2,3];', 132 | 'var result = arr.map(pig);' 133 | ].join('\n'), 134 | errors: [{ 135 | message: 'no array iterators allowed' 136 | }] 137 | }] 138 | }); 139 | 140 | /*eslint no-console: 0*/ 141 | console.log('ok'); 142 | 143 | function buildStr(len) { 144 | var chars = []; 145 | 146 | for (var i = 0; i < len; i++) { 147 | chars[i] = 'a'; 148 | } 149 | 150 | return chars.join(''); 151 | } 152 | --------------------------------------------------------------------------------