├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── index.js ├── lib ├── compute-static-expression.js ├── contains-identifier.js ├── get-property-name.js ├── get-require-source.js ├── is-function-expression.js ├── is-promise.js └── is-static-require.js ├── license ├── package.json ├── readme.md └── test ├── compute-static-expression.js ├── contains-identifier.test.js ├── get-property-name.js ├── get-require-source.test.js ├── helpers ├── babel.js └── espree.js ├── is-function-expression.test.js ├── is-promise.test.js ├── is-static-require.test.js └── some-contain-identifier.test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nyc_output 3 | coverage 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '7' 4 | - '6' 5 | - '4' 6 | before_install: 7 | - "npm install -g npm@^3" 8 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const containsIdentifier = require('./lib/contains-identifier'); 4 | 5 | module.exports = { 6 | computeStaticExpression: require('./lib/compute-static-expression'), 7 | containsIdentifier: containsIdentifier.containsIdentifier, 8 | getPropertyName: require('./lib/get-property-name'), 9 | getRequireSource: require('./lib/get-require-source'), 10 | isFunctionExpression: require('./lib/is-function-expression'), 11 | isPromise: require('./lib/is-promise'), 12 | isStaticRequire: require('./lib/is-static-require'), 13 | someContainIdentifier: containsIdentifier.someContainIdentifier 14 | }; 15 | -------------------------------------------------------------------------------- /lib/compute-static-expression.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const zip = require('lodash.zip'); 4 | 5 | const toValue = value => ({value}); 6 | 7 | function computeTemplateLiteral(node) { 8 | const expressions = node.expressions.map(computeStaticExpression); 9 | 10 | if (expressions.some(expression => expression === undefined)) { 11 | return undefined; 12 | } 13 | 14 | const quasi = node.quasis.map(quasis => quasis.value.cooked); 15 | const value = zip(quasi, expressions.map(expr => expr.value)) 16 | .reduce((res, elts) => res.concat(elts)) 17 | .filter(Boolean) 18 | .join(''); 19 | 20 | return toValue(value); 21 | } 22 | 23 | function computeBinaryExpression(operator, leftExpr, rightExpr) { // eslint-disable-line complexity 24 | if (!leftExpr || !rightExpr) { 25 | return undefined; 26 | } 27 | 28 | const left = leftExpr.value; 29 | const right = rightExpr.value; 30 | 31 | switch (operator) { // eslint-disable-line default-case 32 | case '+': return toValue(left + right); 33 | case '-': return toValue(left - right); 34 | case '*': return toValue(left * right); 35 | case '/': return toValue(left / right); 36 | case '%': return toValue(left % right); 37 | case '**': return toValue(Math.pow(left, right)); 38 | case '<<': return toValue(left << right); 39 | case '>>': return toValue(left >> right); 40 | case '>>>': return toValue(left >>> right); 41 | case '&': return toValue(left & right); 42 | case '|': return toValue(left | right); 43 | case '^': return toValue(left | right); 44 | case '&&': return toValue(left && right); 45 | case '||': return toValue(left || right); 46 | case '===': return toValue(left === right); 47 | case '!==': return toValue(left !== right); 48 | case '==': return toValue(left == right); // eslint-disable-line eqeqeq 49 | case '!=': return toValue(left != right); // eslint-disable-line eqeqeq 50 | case '<': return toValue(left < right); 51 | case '>': return toValue(left > right); 52 | case '<=': return toValue(left <= right); 53 | case '>=': return toValue(left >= right); 54 | } 55 | } 56 | 57 | function applyUnaryOperator(operator, expr) { 58 | if (operator === 'void') { 59 | return toValue(undefined); 60 | } 61 | 62 | if (!expr) { 63 | return undefined; 64 | } 65 | 66 | const value = expr.value; 67 | 68 | switch (operator) { // eslint-disable-line default-case 69 | case '+': return toValue(+value); // eslint-disable-line no-implicit-coercion 70 | case '-': return toValue(-value); 71 | case '!': return toValue(!value); 72 | case '~': return toValue(~value); 73 | } 74 | } 75 | 76 | function computeConditionalExpression(test, consequent, alternate) { 77 | if (!test) { 78 | return undefined; 79 | } 80 | 81 | return test.value ? consequent : alternate; 82 | } 83 | 84 | function computeStaticExpression(node) { 85 | if (!node) { 86 | return undefined; 87 | } 88 | 89 | switch (node.type) { 90 | case 'Identifier': 91 | return node.name === 'undefined' ? toValue(undefined) : undefined; 92 | case 'Literal': 93 | return toValue(node.value); 94 | case 'TemplateLiteral': 95 | return computeTemplateLiteral(node); 96 | case 'UnaryExpression': 97 | return applyUnaryOperator(node.operator, computeStaticExpression(node.argument)); 98 | case 'BinaryExpression': { 99 | return computeBinaryExpression( 100 | node.operator, 101 | computeStaticExpression(node.left), 102 | computeStaticExpression(node.right) 103 | ); 104 | } 105 | case 'LogicalExpression': { 106 | return computeBinaryExpression( 107 | node.operator, 108 | computeStaticExpression(node.left), 109 | computeStaticExpression(node.right) 110 | ); 111 | } 112 | case 'ConditionalExpression': 113 | return computeConditionalExpression( 114 | computeStaticExpression(node.test), 115 | computeStaticExpression(node.consequent), 116 | computeStaticExpression(node.alternate) 117 | ); 118 | default: 119 | return undefined; 120 | } 121 | } 122 | 123 | module.exports = computeStaticExpression; 124 | -------------------------------------------------------------------------------- /lib/contains-identifier.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function introduces(name, node) { // eslint-disable-line complexity 4 | if (!node) { 5 | return false; 6 | } 7 | switch (node.type) { 8 | case 'Identifier': 9 | return node.name === name; 10 | case 'FunctionDeclaration': 11 | return introduces(name, node.id) || 12 | someIntroduce(name, node.params); 13 | case 'ArrowFunctionExpression': 14 | return someIntroduce(name, node.params); 15 | case 'FunctionExpression': 16 | return someIntroduce(name, node.params); 17 | case 'BlockStatement': 18 | return someIntroduce(name, node.body); 19 | case 'VariableDeclaration': 20 | return someIntroduce(name, node.declarations); 21 | case 'VariableDeclarator': 22 | return introduces(name, node.id); 23 | case 'ObjectPattern': 24 | return someIntroduce(name, node.properties); 25 | case 'ArrayPattern': 26 | return someIntroduce(name, node.elements); 27 | case 'Property': 28 | return introduces(name, node.value); 29 | case 'ExperimentalRestProperty': 30 | return introduces(name, node.argument); 31 | case 'ForStatement': 32 | return introduces(name, node.init); 33 | case 'ClassDeclaration': 34 | return introduces(name, node.id); 35 | case 'RestElement': 36 | return introduces(name, node.argument); 37 | case 'Program': 38 | return someIntroduce(name, node.body); 39 | case 'ImportDeclaration': 40 | return someIntroduce(name, node.specifiers); 41 | case 'ImportDefaultSpecifier': 42 | return introduces(name, node.local); 43 | case 'ImportSpecifier': 44 | return introduces(name, node.local); 45 | case 'ImportNamespaceSpecifier': 46 | return introduces(name, node.local); 47 | default: 48 | return false; 49 | } 50 | } 51 | 52 | function someIntroduce(name, array) { 53 | return Array.isArray(array) && array.some(item => { 54 | return introduces(name, item); 55 | }); 56 | } 57 | 58 | function containsIdentifier(name, node) { // eslint-disable-line complexity 59 | if (!node) { 60 | return false; 61 | } 62 | switch (node.type) { 63 | // Primitives 64 | case 'Identifier': 65 | return node.name === name; 66 | case 'Literal': 67 | return false; 68 | case 'ThisExpression': 69 | return false; 70 | 71 | // Objects / Arrays 72 | case 'ArrayExpression': 73 | return someContainIdentifier(name, node.elements); 74 | case 'ObjectExpression': 75 | return someContainIdentifier(name, node.properties); 76 | case 'ExperimentalSpreadProperty': 77 | return containsIdentifier(name, node.argument); 78 | case 'Property': 79 | return (node.computed && containsIdentifier(name, node.key)) || 80 | containsIdentifier(name, node.value); 81 | 82 | // Expressions 83 | case 'TemplateLiteral': 84 | return someContainIdentifier(name, node.expressions); 85 | case 'TaggedTemplateExpression': 86 | return containsIdentifier(name, node.tag) || containsIdentifier(name, node.quasi); 87 | case 'SequenceExpression': 88 | return someContainIdentifier(name, node.expressions); 89 | case 'CallExpression': 90 | return containsIdentifier(name, node.callee) || 91 | someContainIdentifier(name, node.arguments); 92 | case 'NewExpression': 93 | return containsIdentifier(name, node.callee) || 94 | someContainIdentifier(name, node.arguments); 95 | case 'MemberExpression': 96 | if (node.computed === false) { 97 | return containsIdentifier(name, node.object); 98 | } 99 | return containsIdentifier(name, node.property) || 100 | containsIdentifier(name, node.object); 101 | case 'ConditionalExpression': 102 | return containsIdentifier(name, node.test) || 103 | containsIdentifier(name, node.consequent) || 104 | containsIdentifier(name, node.alternate); 105 | case 'BinaryExpression': 106 | return containsIdentifier(name, node.left) || 107 | containsIdentifier(name, node.right); 108 | case 'LogicalExpression': 109 | return containsIdentifier(name, node.left) || 110 | containsIdentifier(name, node.right); 111 | case 'AssignmentExpression': 112 | return containsIdentifier(name, node.left) || 113 | containsIdentifier(name, node.right); 114 | case 'UpdateExpression': 115 | return containsIdentifier(name, node.argument); 116 | case 'UnaryExpression': 117 | return containsIdentifier(name, node.argument); 118 | case 'YieldExpression': 119 | return containsIdentifier(name, node.argument); 120 | case 'AwaitExpression': 121 | return containsIdentifier(name, node.argument); 122 | case 'ArrowFunctionExpression': 123 | if (node.params.some(param => param.type !== 'Identifier' && containsIdentifier(name, param))) { 124 | return true; 125 | } 126 | return !introduces(name, node) && containsIdentifier(name, node.body); 127 | case 'FunctionExpression': 128 | if (node.params.some(param => param.type !== 'Identifier' && containsIdentifier(name, param))) { 129 | return true; 130 | } 131 | return !introduces(name, node) && containsIdentifier(name, node.body); 132 | case 'SpreadElement': 133 | return containsIdentifier(name, node.argument); 134 | 135 | // Statements / control flow 136 | case 'ExpressionStatement': 137 | return containsIdentifier(name, node.expression); 138 | case 'ReturnStatement': 139 | return containsIdentifier(name, node.argument); 140 | case 'ThrowStatement': 141 | return containsIdentifier(name, node.argument); 142 | case 'IfStatement': 143 | return containsIdentifier(name, node.test) || 144 | containsIdentifier(name, node.consequent) || 145 | containsIdentifier(name, node.alternate); 146 | case 'BreakStatement': 147 | return false; 148 | case 'ContinueStatement': 149 | return false; 150 | case 'ForOfStatement': 151 | return containsIdentifier(name, node.left) || 152 | containsIdentifier(name, node.right) || 153 | containsIdentifier(name, node.body); 154 | case 'ForInStatement': 155 | return containsIdentifier(name, node.left) || 156 | containsIdentifier(name, node.right) || 157 | containsIdentifier(name, node.body); 158 | case 'ForStatement': 159 | return !introduces(name, node) && ( 160 | containsIdentifier(name, node.init) || 161 | containsIdentifier(name, node.test) || 162 | containsIdentifier(name, node.update) || 163 | containsIdentifier(name, node.body) 164 | ); 165 | case 'WhileStatement': 166 | return containsIdentifier(name, node.test) || 167 | containsIdentifier(name, node.body); 168 | case 'DoWhileStatement': 169 | return containsIdentifier(name, node.test) || 170 | containsIdentifier(name, node.body); 171 | case 'Program': 172 | return !introduces(name, node) && someContainIdentifier(name, node.body); 173 | case 'BlockStatement': 174 | return !introduces(name, node) && someContainIdentifier(name, node.body); 175 | case 'TryStatement': 176 | return containsIdentifier(name, node.block) || 177 | containsIdentifier(name, node.handler) || 178 | containsIdentifier(name, node.finalizer); 179 | case 'CatchClause': 180 | return !introduces(name, node.param) && containsIdentifier(name, node.body); 181 | case 'SwitchStatement': 182 | return containsIdentifier(name, node.discriminant) || someContainIdentifier(name, node.cases); 183 | case 'SwitchCase': 184 | return containsIdentifier(name, node.test) || someContainIdentifier(name, node.consequent); 185 | case 'LabeledStatement': 186 | return containsIdentifier(name, node.body); 187 | case 'DebuggerStatement': 188 | return false; 189 | case 'EmptyStatement': 190 | return false; 191 | 192 | // Assignment / Declaration 193 | case 'AssignmentPattern': 194 | return containsIdentifier(name, node.left) || 195 | containsIdentifier(name, node.right); 196 | case 'VariableDeclarator': 197 | if (node.id.type !== 'Identifier') { 198 | return containsIdentifier(name, node.id) || 199 | containsIdentifier(name, node.init); 200 | } 201 | return containsIdentifier(name, node.init); 202 | case 'ObjectPattern': 203 | return node.properties.some(prop => 204 | prop.type === 'Property' && prop.value.type !== 'Identifier' && containsIdentifier(name, prop.value) 205 | ); 206 | case 'FunctionDeclaration': 207 | if (node.params.some(param => param.type !== 'Identifier' && containsIdentifier(name, param))) { 208 | return true; 209 | } 210 | return !introduces(name, node) && containsIdentifier(name, node.body); 211 | case 'ArrayPattern': 212 | return node.elements.some(item => { 213 | return item && item.type !== 'Identifier' && containsIdentifier(name, item); 214 | }); 215 | case 'VariableDeclaration': 216 | return someContainIdentifier(name, node.declarations); 217 | case 'RestElement': 218 | return false; 219 | 220 | // Classes 221 | case 'ClassDeclaration': 222 | return !introduces(name, node) && ( 223 | containsIdentifier(name, node.superClass) || 224 | containsIdentifier(name, node.body) 225 | ); 226 | case 'ClassExpression': 227 | return containsIdentifier(name, node.superClass) || 228 | containsIdentifier(name, node.body); 229 | case 'ClassBody': 230 | return someContainIdentifier(name, node.body); 231 | case 'MethodDefinition': 232 | return containsIdentifier(name, node.value); 233 | case 'Super': 234 | return false; 235 | 236 | // Import / export 237 | case 'ImportDeclaration': 238 | return false; 239 | case 'ExportDefaultDeclaration': 240 | return containsIdentifier(name, node.declaration); 241 | case 'ExportNamedDeclaration': 242 | return containsIdentifier(name, node.declaration); 243 | 244 | // JSX 245 | case 'JSXIdentifier': 246 | return node.name === name; 247 | case 'JSXElement': 248 | return containsIdentifier(name, node.openingElement) || 249 | someContainIdentifier(name, node.children); 250 | case 'JSXOpeningElement': 251 | return containsIdentifier(name, node.name) || 252 | someContainIdentifier(name, node.attributes); 253 | case 'JSXExpressionContainer': 254 | return containsIdentifier(name, node.expression); 255 | case 'JSXSpreadAttribute': 256 | return containsIdentifier(name, node.argument); 257 | case 'JSXAttribute': 258 | return containsIdentifier(name, node.value); 259 | 260 | default: 261 | return false; 262 | } 263 | } 264 | 265 | function someContainIdentifier(name, array) { 266 | return Array.isArray(array) && array.some(item => { 267 | return containsIdentifier(name, item); 268 | }); 269 | } 270 | 271 | module.exports = { 272 | containsIdentifier, 273 | someContainIdentifier 274 | }; 275 | -------------------------------------------------------------------------------- /lib/get-property-name.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const computeStaticExpression = require('./compute-static-expression'); 4 | 5 | function getPropertyName(node) { 6 | if (!node || node.type !== 'MemberExpression') { 7 | return undefined; 8 | } 9 | 10 | if (node.property.type === 'Identifier' && node.computed === false) { 11 | return node.property.name; 12 | } 13 | 14 | const expression = computeStaticExpression(node.property); 15 | return expression && expression.value; 16 | } 17 | 18 | module.exports = getPropertyName; 19 | -------------------------------------------------------------------------------- /lib/get-require-source.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const get = require('lodash.get'); 4 | const isStaticRequire = require('./is-static-require'); 5 | 6 | function getRequireSource(node) { 7 | return isStaticRequire(node) ? get(node, 'arguments.0.value') : undefined; 8 | } 9 | 10 | module.exports = getRequireSource; 11 | -------------------------------------------------------------------------------- /lib/is-function-expression.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const functionExpressions = [ 4 | 'FunctionExpression', 5 | 'ArrowFunctionExpression' 6 | ]; 7 | 8 | function isFunctionExpression(node) { 9 | return Boolean(node) && 10 | functionExpressions.indexOf(node.type) !== -1; 11 | } 12 | 13 | module.exports = isFunctionExpression; 14 | -------------------------------------------------------------------------------- /lib/is-promise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getPropertyName = require('./get-property-name'); 4 | 5 | const prototypeMethods = ['then', 'catch']; 6 | const knownNotMethods = ['promisify', 'promisifyAll', 'cancel', 'is']; 7 | 8 | function containsThenOrCatch(node) { 9 | return Boolean(node) && 10 | node.type === 'CallExpression' && 11 | node.callee.type === 'MemberExpression' && 12 | prototypeMethods.indexOf(getPropertyName(node.callee)) !== -1; 13 | } 14 | 15 | function isPromiseStaticMethod(node) { 16 | return Boolean(node) && 17 | node.type === 'CallExpression' && 18 | node.callee.type === 'MemberExpression' && 19 | node.callee.object.type === 'Identifier' && 20 | node.callee.object.name === 'Promise' && 21 | knownNotMethods.indexOf(getPropertyName(node.callee)) === -1; 22 | } 23 | 24 | function isNewPromise(node) { 25 | return Boolean(node) && 26 | node.type === 'NewExpression' && 27 | node.callee.type === 'Identifier' && 28 | node.callee.name === 'Promise'; 29 | } 30 | 31 | function isPromise(node) { 32 | return containsThenOrCatch(node) || 33 | isPromiseStaticMethod(node) || 34 | isNewPromise(node); 35 | } 36 | 37 | module.exports = isPromise; 38 | -------------------------------------------------------------------------------- /lib/is-static-require.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function isStaticRequire(node) { 4 | return Boolean(node && 5 | node.callee && 6 | node.callee.type === 'Identifier' && 7 | node.callee.name === 'require' && 8 | node.arguments.length === 1 && 9 | node.arguments[0].type === 'Literal' && 10 | typeof node.arguments[0].value === 'string' 11 | ); 12 | } 13 | 14 | module.exports = isStaticRequire; 15 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Jeroen Engels (github.com/jfmengels) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-ast-utils", 3 | "version": "1.1.0", 4 | "description": "Utility library to manipulate ASTs", 5 | "license": "MIT", 6 | "repository": "jfmengels/eslint-ast-utils", 7 | "author": { 8 | "name": "Jeroen Engels", 9 | "email": "jfm.engels@gmail.com", 10 | "url": "github.com/jfmengels" 11 | }, 12 | "engines": { 13 | "node": ">=4" 14 | }, 15 | "scripts": { 16 | "test": "xo && nyc ava" 17 | }, 18 | "files": [ 19 | "index.js", 20 | "lib" 21 | ], 22 | "keywords": [ 23 | "eslint", 24 | "ast", 25 | "utils", 26 | "Utility" 27 | ], 28 | "dependencies": { 29 | "lodash.get": "^4.4.2", 30 | "lodash.zip": "^4.2.0" 31 | }, 32 | "devDependencies": { 33 | "ava": "^0.16.0", 34 | "babel-eslint": "^7.0.0", 35 | "espree": "^3.3.2", 36 | "nyc": "^7.1.0", 37 | "xo": "^0.17.0" 38 | }, 39 | "xo": { 40 | "esnext": true 41 | }, 42 | "nyc": { 43 | "reporter": [ 44 | "lcov", 45 | "text" 46 | ], 47 | "check-coverage": true, 48 | "lines": 100, 49 | "statements": 100, 50 | "functions": 100, 51 | "branches": 100 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # eslint-ast-utils [![Build Status](https://travis-ci.org/jfmengels/eslint-ast-utils.svg?branch=master)](https://travis-ci.org/jfmengels/eslint-ast-utils) 2 | 3 | > Utility library to manipulate ASTs for ESLint projects 4 | 5 | 6 | ## Install 7 | 8 | ``` 9 | $ npm install --save eslint-ast-utils 10 | ``` 11 | 12 | ## Usage 13 | 14 | ```js 15 | const astUtils = require('eslint-ast-utils'); 16 | ``` 17 | 18 | 19 | ## API 20 | 21 | ### astUtils.isStaticRequire(node) 22 | 23 | Checks whether `node` is a call to CommonJS's `require` function. 24 | 25 | Returns `true` if and only if: 26 | - `node` is a `CallExpression` 27 | - `node`'s callee is an `Identifier` named `require` 28 | - `node` has exactly 1 `Literal` argument whose value is a `string` 29 | 30 | Example: 31 | ```js 32 | require('lodash'); 33 | // => true 34 | require(foo); 35 | // => false 36 | foo('lodash'); 37 | // => false 38 | ``` 39 | 40 | Usage example: 41 | ```js 42 | function create(context) { 43 | return { 44 | CallExpression(node) { 45 | if (astUtils.isStaticRequire(node)) { 46 | context.report({ 47 | node: node, 48 | message: 'Use import syntax rather than `require`' 49 | }); 50 | } 51 | } 52 | }; 53 | } 54 | ``` 55 | 56 | ### astUtils.getRequireSource(node) 57 | 58 | Gets the source of a `require()` call. If `node` is not a `require` call (in the definition of [`isStaticRequire`](#astutilsisstaticrequirenode)), it will return `undefined`. 59 | 60 | Example: 61 | ```js 62 | require('lodash'); 63 | // => 'lodash' 64 | require('./foo'); 65 | // => './foo' 66 | ``` 67 | 68 | Usage example: 69 | ```js 70 | function create(context) { 71 | return { 72 | CallExpression(node) { 73 | if (astUtils.isStaticRequire(node) && astUtils.getRequireSource(node) === 'underscore') { 74 | context.report({ 75 | node: node, 76 | message: 'Use `lodash` instead of `underscore`' 77 | }); 78 | } 79 | } 80 | }; 81 | } 82 | ``` 83 | 84 | ### astUtils.containsIdentifier(name, node) 85 | 86 | Checks if there is a reference to a variable named `name` inside of `node`. 87 | 88 | Returns true if and only if: 89 | - There is an `Identifier` named `name` inside of `node` 90 | - That `Identifier` is a variable (i.e. not a static property name for instance) 91 | - That `Identifier` does not reference a different variable named `name` introduced in a sub-scope of `node`. 92 | 93 | Example: 94 | ```js 95 | foo(a); 96 | // containsIdentifier('a', node) // => true 97 | // containsIdentifier('b', node) // => true 98 | 99 | function foo(fn) { 100 | return function(a) { 101 | return fn(a); 102 | }; 103 | } 104 | // containsIdentifier('a', node) // => false 105 | ``` 106 | 107 | Usage example: 108 | ```js 109 | function create(context) { 110 | return { 111 | FunctionDeclaration(node) { 112 | node.params.forEach(param => { 113 | if (param.type === 'Identifier' && !astUtils.containsIdentifier(param.name, node.body)) { 114 | context.report({ 115 | node: node, 116 | message: `${name} is never used` 117 | }); 118 | } 119 | }); 120 | } 121 | }; 122 | } 123 | ``` 124 | 125 | ### astUtils.someContainIdentifier(name, nodes) 126 | 127 | Checks if there is a reference to a variable named `name` inside any node of the `nodes` array. Will return `false` if `nodes` is not an array. 128 | This is a shorthand version of [`containsIdentifier`](#astutilscontainsidentifier) that works for arrays. The following are equivalent: 129 | 130 | ```js 131 | [node1, node2, node3].some(node => astUtils.containsIdentifier('a', node)); 132 | // equivalent to 133 | astUtils.someContainIdentifier('a', [node1, node2, node3]); 134 | ``` 135 | 136 | ### astUtils.getPropertyName(node) 137 | 138 | Get the name of a `MemberExpression`'s property. Returns: 139 | - a `string` if the property is accessed through dot notation. 140 | - a `string` if the property is accessed through brackets and is a string. 141 | - a `number` if the property is accessed through brackets and is a number. 142 | - `undefined` if `node` is not a `MemberExpression` 143 | - `undefined` if the property name is a hard to compute expression. 144 | 145 | Example: 146 | ```js 147 | foo.bar 148 | // => 'bar' 149 | foo['bar'] 150 | // => 'bar' 151 | foo[bar] 152 | // => undefined 153 | foo[0] 154 | // => 0 # Number 155 | foo[null] 156 | // => null 157 | foo[undefined] 158 | // => undefined 159 | ``` 160 | 161 | Usage example: 162 | ```js 163 | function create(context) { 164 | return { 165 | MemberExpression(node) { 166 | if (astUtils.getPropertyName(node).startsWith('_')) { 167 | context.report({ 168 | node: node, 169 | message: 'Don\'t access "private" fields' 170 | }); 171 | } 172 | } 173 | }; 174 | } 175 | ``` 176 | 177 | ### astUtils.computeStaticExpression(node) 178 | 179 | Get the value of an expression that can be statically computed, i.e. without variables references or expressions too complex. 180 | 181 | Returns: 182 | - `undefined` if the value could not be statically computed. 183 | - An object with a `value` property containing the computed value. 184 | 185 | Example: 186 | ```js 187 | foo 188 | // => undefined 189 | 42 190 | // => {value: 42} 191 | 'foo' 192 | // => {value: 'foo'} 193 | undefined 194 | // => {value: undefined} 195 | null 196 | // => {value: null} 197 | 1 + 2 - 4 + (-1) 198 | // => {value: -2} 199 | true ? 1 : 2 200 | // => {value: 1} 201 | `foo ${'bar'}` 202 | // => {value: 'foo bar'} 203 | ``` 204 | 205 | Usage example: 206 | ```js 207 | function create(context) { 208 | return { 209 | TemplateLiteral(node) { 210 | const expression = astUtils.computeStaticExpression(node); 211 | if (expression) { 212 | context.report({ 213 | node: node, 214 | message: `You can replace this template literal by the regular string '${expression.value}'.` 215 | }); 216 | } 217 | } 218 | }; 219 | } 220 | ``` 221 | 222 | ### astUtils.isPromise(node) 223 | 224 | Checks whether `node` is a Promise. 225 | 226 | Returns `true` if and only if `node` is one of the following: 227 | - a call of an expression's `then` or `catch` properties 228 | - a call to a property of `Promise` (except `cancel`, `promisify`, `promisifyAll` and `is`) 229 | - a call to `new Promise` 230 | 231 | If `node` uses unknown properties of a value that would be considered a Promise, `node` itself would not be considered as a Promise. 232 | 233 | Example: 234 | ```js 235 | foo.then(fn); 236 | // => true 237 | foo.catch(fn); 238 | // => true 239 | foo.then(fn).catch(fn); 240 | // => true 241 | foo.then(fn).isFulfilled(fn); // isFulfilled(fn) may not return a Promise 242 | // => false 243 | 244 | Promise.resolve(value); 245 | // => true 246 | Promise.reject(value); 247 | // => true 248 | Promise.race(promises); 249 | // => true 250 | Promise.all(promises); 251 | // => true 252 | Promise.map(promises, fn); // Bluebird method 253 | // => true 254 | 255 | new Promise(fn); 256 | // => true 257 | new Promise.resolve(value); 258 | // => false 259 | ``` 260 | 261 | Usage example: 262 | ```js 263 | function create(context) { 264 | function reportIfPromise(node) { 265 | if (astUtils.isPromise(node)) { 266 | context.report({ 267 | node: node, 268 | message: 'Prefer using async/await' 269 | }); 270 | } 271 | } 272 | 273 | return { 274 | CallExpression: reportIfPromise, 275 | NewExpression: reportIfPromise 276 | }; 277 | } 278 | ``` 279 | 280 | ### astUtils.isFunctionExpression(node) 281 | 282 | Checks whether `node` is a function expression or an arrow function expression (not a function declaration). 283 | 284 | If `node` uses unknown properties of a value that would be considered a Promise, `node` itself would not be considered as a Promise. 285 | 286 | Example: 287 | ```js 288 | (function foo() {}) 289 | // => true 290 | () => {} 291 | // => true 292 | function foo() {} // function declaration 293 | // => false 294 | ``` 295 | 296 | Usage example: 297 | ```js 298 | function create(context) { 299 | return { 300 | CallExpression(node) { 301 | if (node.callee.type === 'Identifier' 302 | && node.callee.name === 'test' 303 | && !astUtils.isFunctionExpression(node.arguments[0]) 304 | && !astUtils.isFunctionExpression(node.arguments[1]) 305 | ) { 306 | context.report({ 307 | node: node, 308 | message: 'You need to pass a function to test()' 309 | }); 310 | } 311 | } 312 | }; 313 | } 314 | ``` 315 | 316 | ## License 317 | 318 | MIT © [Jeroen Engels](https://github.com/jfmengels) 319 | -------------------------------------------------------------------------------- /test/compute-static-expression.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import lib from '../'; 3 | import babel from './helpers/babel'; 4 | import espree from './helpers/espree'; 5 | 6 | const toValue = value => ({value}); 7 | 8 | [espree, babel].forEach(({name, utils}) => { 9 | test(`(${name}) should return undefined if node is a not an expression`, t => { 10 | t.true(undefined === lib.computeStaticExpression(null)); 11 | t.true(undefined === lib.computeStaticExpression(utils.statement(`foo = 2`))); 12 | t.true(undefined === lib.computeStaticExpression(utils.program(`foo = 2`))); 13 | }); 14 | 15 | test(`(${name}) should return undefined if node is a variable name`, t => { 16 | t.true(undefined === lib.computeStaticExpression(utils.expression(`foo`))); 17 | }); 18 | 19 | test(`(${name}) should return value undefined if node is the 'undefined' Literal`, t => { 20 | t.deepEqual(lib.computeStaticExpression(utils.expression(`undefined`)), toValue(undefined)); 21 | }); 22 | 23 | test(`(${name}) should return the node's value if node is a Literal`, t => { 24 | t.deepEqual(lib.computeStaticExpression(utils.expression(`null`)), toValue(null)); 25 | t.deepEqual(lib.computeStaticExpression(utils.expression(`'foo'`)), toValue('foo')); 26 | t.deepEqual(lib.computeStaticExpression(utils.expression(`0`)), toValue(0)); 27 | }); 28 | 29 | test(`(${name}) should return the value if node is a Literal`, t => { 30 | t.deepEqual(lib.computeStaticExpression(utils.expression(`null`)), toValue(null)); 31 | t.deepEqual(lib.computeStaticExpression(utils.expression(`'foo'`)), toValue('foo')); 32 | t.deepEqual(lib.computeStaticExpression(utils.expression(`'undefined'`)), toValue('undefined')); 33 | t.deepEqual(lib.computeStaticExpression(utils.expression(`'null'`)), toValue('null')); 34 | t.deepEqual(lib.computeStaticExpression(utils.expression(`0`)), toValue(0)); 35 | t.deepEqual(lib.computeStaticExpression(utils.expression(`1`)), toValue(1)); 36 | t.deepEqual(lib.computeStaticExpression(utils.expression(`0.5`)), toValue(0.5)); 37 | t.deepEqual(lib.computeStaticExpression(utils.expression(`.5`)), toValue(0.5)); 38 | t.deepEqual(lib.computeStaticExpression(utils.expression(`0x90`)), toValue(144)); 39 | t.deepEqual(lib.computeStaticExpression(utils.expression(`true`)), toValue(true)); 40 | t.deepEqual(lib.computeStaticExpression(utils.expression(`false`)), toValue(false)); 41 | }); 42 | 43 | test(`(${name}) should return the node's content if node is a TemplateLiteral without dynamic content`, t => { 44 | t.deepEqual(lib.computeStaticExpression(utils.expression('``')), toValue('')); 45 | t.deepEqual(lib.computeStaticExpression(utils.expression('`foo`')), toValue('foo')); 46 | t.deepEqual(lib.computeStaticExpression(utils.expression(`\`foo 47 | bar\``)), toValue('foo\nbar')); 48 | }); 49 | 50 | test(`(${name}) should return the node's content if node is a TemplateLiteral with dynamic content that is statically knowable`, t => { 51 | /* eslint-disable no-template-curly-in-string */ 52 | t.deepEqual(lib.computeStaticExpression(utils.expression('`foo ${"bar"}`')), toValue('foo bar')); 53 | t.deepEqual(lib.computeStaticExpression(utils.expression('`foo ${1 + 2}`')), toValue('foo 3')); 54 | /* eslint-enable no-template-curly-in-string */ 55 | }); 56 | 57 | test(`(${name}) should return undefined if node is a TemplateLiteral with dynamic content`, t => { 58 | /* eslint-disable no-template-curly-in-string */ 59 | t.true(undefined === lib.computeStaticExpression(utils.expression('`foo ${bar}`'))); 60 | t.true(undefined === lib.computeStaticExpression(utils.expression('`${foo}`'))); 61 | t.true(undefined === lib.computeStaticExpression(utils.expression('`${foo} ${"foo"}`'))); 62 | /* eslint-enable no-template-curly-in-string */ 63 | }); 64 | 65 | test(`(${name}) should return the value if node is a unary expression`, t => { 66 | t.deepEqual(lib.computeStaticExpression(utils.expression(`+1`)), toValue(+1)); 67 | t.deepEqual(lib.computeStaticExpression(utils.expression(`-1`)), toValue(-1)); 68 | t.deepEqual(lib.computeStaticExpression(utils.expression(`~1`)), toValue(-2)); 69 | t.deepEqual(lib.computeStaticExpression(utils.expression(`!1`)), toValue(false)); 70 | t.deepEqual(lib.computeStaticExpression(utils.expression(`!0`)), toValue(true)); 71 | t.deepEqual(lib.computeStaticExpression(utils.expression(`!!0`)), toValue(false)); 72 | }); 73 | 74 | test(`(${name}) should return the value of a number addition`, t => { 75 | t.deepEqual(lib.computeStaticExpression(utils.expression(`0 + 1`)), toValue(1)); 76 | t.deepEqual(lib.computeStaticExpression(utils.expression(`0 + -1`)), toValue(-1)); 77 | t.deepEqual(lib.computeStaticExpression(utils.expression(`0b110 + 1`)), toValue(7)); 78 | t.deepEqual(lib.computeStaticExpression(utils.expression(`0x90 + 1`)), toValue(145)); 79 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 + 1`)), toValue(101)); 80 | t.deepEqual(lib.computeStaticExpression(utils.expression(`1 + 2 + 3`)), toValue(6)); 81 | }); 82 | 83 | test(`(${name}) should return the value of a string concatenation`, t => { 84 | t.deepEqual(lib.computeStaticExpression(utils.expression(`'0' + '1'`)), toValue('01')); 85 | t.deepEqual(lib.computeStaticExpression(utils.expression(`'foo' + 'bar'`)), toValue('foobar')); 86 | }); 87 | 88 | test(`(${name}) should return the value of a mixed types addition`, t => { 89 | t.deepEqual(lib.computeStaticExpression(utils.expression(`'foo' + 0`)), toValue('foo0')); 90 | t.deepEqual(lib.computeStaticExpression(utils.expression(`0 + 'foo'`)), toValue('0foo')); 91 | }); 92 | 93 | test(`(${name}) should return the value of a number subtraction`, t => { 94 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 - 1`)), toValue(99)); 95 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 - 2 - 1`)), toValue(97)); 96 | }); 97 | 98 | test(`(${name}) should return the value of a logical expression`, t => { 99 | t.deepEqual(lib.computeStaticExpression(utils.expression(`true && false`)), toValue(false)); 100 | t.deepEqual(lib.computeStaticExpression(utils.expression(`true || false`)), toValue(true)); 101 | t.deepEqual(lib.computeStaticExpression(utils.expression(`false && true`)), toValue(false)); 102 | t.deepEqual(lib.computeStaticExpression(utils.expression(`false || true`)), toValue(true)); 103 | }); 104 | 105 | test(`(${name}) should return the value of other binary operators`, t => { 106 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 % 3`)), toValue(1)); 107 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 ** 2`)), toValue(10000)); 108 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 + 2 ** 2`)), toValue(104)); 109 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 << 2`)), toValue(400)); 110 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 >> 2`)), toValue(25)); 111 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 >>> 2`)), toValue(25)); 112 | t.deepEqual(lib.computeStaticExpression(utils.expression(`10 & 2`)), toValue(2)); 113 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 | 2`)), toValue(102)); 114 | t.deepEqual(lib.computeStaticExpression(utils.expression(`10 ^ 2`)), toValue(10)); 115 | t.deepEqual(lib.computeStaticExpression(utils.expression(`1 === 1`)), toValue(true)); 116 | t.deepEqual(lib.computeStaticExpression(utils.expression(`1 !== 1`)), toValue(false)); 117 | t.deepEqual(lib.computeStaticExpression(utils.expression(`1 == 1`)), toValue(true)); 118 | t.deepEqual(lib.computeStaticExpression(utils.expression(`1 != 1`)), toValue(false)); 119 | t.deepEqual(lib.computeStaticExpression(utils.expression(`1 < 2`)), toValue(true)); 120 | t.deepEqual(lib.computeStaticExpression(utils.expression(`1 > 2`)), toValue(false)); 121 | t.deepEqual(lib.computeStaticExpression(utils.expression(`1 <= 2`)), toValue(true)); 122 | t.deepEqual(lib.computeStaticExpression(utils.expression(`1 >= 2`)), toValue(false)); 123 | }); 124 | 125 | test(`(${name}) should return the value of multiplication`, t => { 126 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 * 2`)), toValue(200)); 127 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 * 2 + 1`)), toValue(201)); 128 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 + 2 * 1`)), toValue(102)); 129 | t.deepEqual(lib.computeStaticExpression(utils.expression(`(100 + 2) * 10`)), toValue(1020)); 130 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 - 2 * 1`)), toValue(98)); 131 | }); 132 | 133 | test(`(${name}) should return the value of division`, t => { 134 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 / 2`)), toValue(50)); 135 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 / 2 + 1`)), toValue(51)); 136 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 + 20 / 2`)), toValue(110)); 137 | t.deepEqual(lib.computeStaticExpression(utils.expression(`(100 + 20) / 2`)), toValue(60)); 138 | t.deepEqual(lib.computeStaticExpression(utils.expression(`100 + (20 / 2)`)), toValue(110)); 139 | t.deepEqual(lib.computeStaticExpression(utils.expression(`10 * 20 / 2`)), toValue(100)); 140 | }); 141 | 142 | test(`(${name}) should return the value of a ternary expression`, t => { 143 | t.deepEqual(lib.computeStaticExpression(utils.expression(`true ? 1 : 2`)), toValue(1)); 144 | t.deepEqual(lib.computeStaticExpression(utils.expression(`false ? 1 : 2`)), toValue(2)); 145 | t.deepEqual(lib.computeStaticExpression(utils.expression(`true ? 1 : foo`)), toValue(1)); 146 | t.deepEqual(lib.computeStaticExpression(utils.expression(`false ? foo : 2`)), toValue(2)); 147 | }); 148 | 149 | test(`(${name}) should return value undefined when using the 'void' unary operator`, t => { 150 | t.deepEqual(lib.computeStaticExpression(utils.expression(`void 2`)), toValue(undefined)); 151 | t.deepEqual(lib.computeStaticExpression(utils.expression(`void foo`)), toValue(undefined)); 152 | }); 153 | 154 | test(`(${name}) should undefined if one of the operands of a ternary expression is not statically knowable`, t => { 155 | t.true(undefined === lib.computeStaticExpression(utils.expression(`foo ? 1 : 2`))); 156 | t.true(undefined === lib.computeStaticExpression(utils.expression(`true ? foo : 2`))); 157 | t.true(undefined === lib.computeStaticExpression(utils.expression(`false ? 1 : foo`))); 158 | }); 159 | 160 | test(`(${name}) should return undefined if the value of a unary expression is not statically knowable`, t => { 161 | t.true(undefined === lib.computeStaticExpression(utils.expression(`!foo`))); 162 | }); 163 | 164 | test(`(${name}) should return undefined if one of the value of a binary expression is not statically knowable`, t => { 165 | t.true(undefined === lib.computeStaticExpression(utils.expression(`foo + 0`))); 166 | t.true(undefined === lib.computeStaticExpression(utils.expression(`0 + foo`))); 167 | }); 168 | 169 | test(`(${name}) should return undefined if one of the value of a logical expression is not statically knowable`, t => { 170 | t.true(undefined === lib.computeStaticExpression(utils.expression(`foo || true`))); 171 | t.true(undefined === lib.computeStaticExpression(utils.expression(`foo && true`))); 172 | t.true(undefined === lib.computeStaticExpression(utils.expression(`true || foo`))); 173 | t.true(undefined === lib.computeStaticExpression(utils.expression(`true && foo`))); 174 | }); 175 | }); 176 | -------------------------------------------------------------------------------- /test/contains-identifier.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import lib from '../'; 3 | import babel from './helpers/babel'; 4 | import espree from './helpers/espree'; 5 | 6 | [espree, babel].forEach(({name, utils}) => { 7 | test(`(${name}) should return false if node is nil`, t => { 8 | t.false(lib.containsIdentifier('foo', null)); 9 | t.false(lib.containsIdentifier('foo', undefined)); 10 | }); 11 | 12 | test(`(${name}) should return false if node is of an unknown type`, t => { 13 | t.false(lib.containsIdentifier('foo', { 14 | type: 'UNKNOWN_TYPE_FOO_BAR' 15 | })); 16 | }); 17 | 18 | test(`(${name}) if node is an Identifier`, t => { 19 | t.true(lib.containsIdentifier('foo', utils.expression(`foo`))); 20 | 21 | t.false(lib.containsIdentifier('foo', utils.expression(`bar`))); 22 | }); 23 | 24 | test(`(${name}) if node is a Literal`, t => { 25 | t.false(lib.containsIdentifier('foo', utils.expression(`'bar'`))); 26 | 27 | t.false(lib.containsIdentifier('foo', utils.expression(`3`))); 28 | }); 29 | 30 | test(`(${name}) if node is a BlockStatement`, t => { 31 | t.true(lib.containsIdentifier('foo', utils.statement(`{foo; bar;}`))); 32 | t.true(lib.containsIdentifier('bar', utils.statement(`{foo; bar;}`))); 33 | t.true(lib.containsIdentifier('foo', utils.statement(`{const {foo: baz} = bar; foo;}`))); 34 | 35 | t.false(lib.containsIdentifier('baz', utils.statement(`{foo; bar;}`))); 36 | t.false(lib.containsIdentifier('foo', utils.statement(`{const foo = 2;}`))); 37 | t.false(lib.containsIdentifier('foo', utils.statement(`{const foo = 2; foo;}`))); 38 | t.false(lib.containsIdentifier('foo', utils.statement(`{const bar = 3, foo = 2; foo;}`))); 39 | t.false(lib.containsIdentifier('foo', utils.statement(`{foo; const foo = 2;}`))); 40 | t.false(lib.containsIdentifier('foo', utils.statement(`{const {foo} = bar; foo;}`))); 41 | t.false(lib.containsIdentifier('foo', utils.statement(`{const {baz: foo} = bar; foo;}`))); 42 | t.false(lib.containsIdentifier('foo', utils.statement(`{const [foo] = bar; foo;}`))); 43 | t.false(lib.containsIdentifier('foo', utils.statement(`{const {...foo} = bar; foo;}`))); 44 | }); 45 | 46 | test(`(${name}) if node is a Program`, t => { 47 | t.true(lib.containsIdentifier('foo', utils.program(`foo`))); 48 | 49 | t.false(lib.containsIdentifier('bar', utils.program(`foo`))); 50 | }); 51 | 52 | test(`(${name}) if node is a MemberExpression`, t => { 53 | t.true(lib.containsIdentifier('foo', utils.expression(`foo.bar`))); 54 | t.true(lib.containsIdentifier('foo', utils.expression(`foo[bar]`))); 55 | t.true(lib.containsIdentifier('foo', utils.expression(`foo['bar']`))); 56 | t.true(lib.containsIdentifier('foo', utils.expression(`bar[foo]`))); 57 | t.true(lib.containsIdentifier('foo', utils.expression(`this[foo]`))); 58 | t.true(lib.containsIdentifier('foo', utils.expression(`foo.this.bar`))); 59 | 60 | t.false(lib.containsIdentifier('foo', utils.expression(`bar.baz`))); 61 | t.false(lib.containsIdentifier('foo', utils.expression(`bar.foo`))); 62 | t.false(lib.containsIdentifier('foo', utils.expression(`bar['foo']`))); 63 | t.false(lib.containsIdentifier('foo', utils.expression(`this.foo`))); 64 | t.false(lib.containsIdentifier('foo', utils.expression(`this['foo']`))); 65 | }); 66 | 67 | test(`(${name}) if node is an expression`, t => { 68 | t.true(lib.containsIdentifier('foo', utils.statement('foo'))); 69 | t.true(lib.containsIdentifier('foo', utils.statement('+foo'))); 70 | t.true(lib.containsIdentifier('foo', utils.statement('-foo'))); 71 | t.true(lib.containsIdentifier('foo', utils.statement('++foo'))); 72 | t.true(lib.containsIdentifier('foo', utils.statement('foo++'))); 73 | 74 | t.false(lib.containsIdentifier('foo', utils.statement('bar'))); 75 | t.false(lib.containsIdentifier('foo', utils.statement('+bar'))); 76 | t.false(lib.containsIdentifier('foo', utils.statement('-bar'))); 77 | t.false(lib.containsIdentifier('foo', utils.statement('++bar'))); 78 | t.false(lib.containsIdentifier('foo', utils.statement('bar++'))); 79 | }); 80 | 81 | test(`(${name}) if node is a complex expression`, t => { 82 | t.true(lib.containsIdentifier('foo', utils.statement('foo + bar'))); 83 | t.true(lib.containsIdentifier('foo', utils.statement('foo - bar'))); 84 | t.true(lib.containsIdentifier('foo', utils.statement('foo || bar'))); 85 | t.true(lib.containsIdentifier('foo', utils.statement('foo && bar'))); 86 | t.true(lib.containsIdentifier('foo', utils.statement('foo, bar'))); 87 | 88 | t.false(lib.containsIdentifier('foo', utils.statement('bar + baz'))); 89 | t.false(lib.containsIdentifier('foo', utils.statement('bar - baz'))); 90 | t.false(lib.containsIdentifier('foo', utils.statement('bar || baz'))); 91 | t.false(lib.containsIdentifier('foo', utils.statement('bar && baz'))); 92 | t.false(lib.containsIdentifier('foo', utils.statement('bar, baz'))); 93 | }); 94 | 95 | test(`(${name}) if node is a ConditionalExpression`, t => { 96 | const expression = utils.expression(`foo ? bar : baz`); 97 | 98 | t.true(lib.containsIdentifier('foo', expression)); 99 | t.true(lib.containsIdentifier('bar', expression)); 100 | t.true(lib.containsIdentifier('baz', expression)); 101 | 102 | t.false(lib.containsIdentifier('other', expression)); 103 | }); 104 | 105 | test(`(${name}) if node is an IfStatement`, t => { 106 | // Without an alternate 107 | let statement = utils.statement(`if (foo) { bar }`); 108 | t.true(lib.containsIdentifier('foo', statement)); 109 | t.true(lib.containsIdentifier('bar', statement)); 110 | 111 | t.false(lib.containsIdentifier('baz', statement)); 112 | 113 | // Without an alternate and without braces 114 | statement = utils.statement(`if (foo) bar`); 115 | t.true(lib.containsIdentifier('foo', statement)); 116 | t.true(lib.containsIdentifier('bar', statement)); 117 | 118 | t.false(lib.containsIdentifier('baz', statement)); 119 | 120 | // With an alternate 121 | statement = utils.statement(`if (foo) { bar } else { baz }`); 122 | t.true(lib.containsIdentifier('foo', statement)); 123 | t.true(lib.containsIdentifier('bar', statement)); 124 | t.true(lib.containsIdentifier('baz', statement)); 125 | 126 | t.false(lib.containsIdentifier('other', statement)); 127 | 128 | // With an alternate but without braces 129 | statement = utils.statement(` 130 | if (foo) 131 | bar 132 | else 133 | baz`); 134 | t.true(lib.containsIdentifier('foo', statement)); 135 | t.true(lib.containsIdentifier('bar', statement)); 136 | t.true(lib.containsIdentifier('baz', statement)); 137 | 138 | t.false(lib.containsIdentifier('other', statement)); 139 | }); 140 | 141 | test(`(${name}) if node is a TemplateLiteral`, t => { 142 | /* eslint-disable no-template-curly-in-string */ 143 | t.true(lib.containsIdentifier('foo', utils.expression('`${foo}`'))); 144 | t.true(lib.containsIdentifier('foo', utils.expression('`${foo + bar}`'))); 145 | t.true(lib.containsIdentifier('foo', utils.expression('`${foo} ${bar} ${baz}`'))); 146 | t.true(lib.containsIdentifier('bar', utils.expression('`${foo} ${bar} ${baz}`'))); 147 | t.true(lib.containsIdentifier('baz', utils.expression('`${foo} ${bar} ${baz}`'))); 148 | t.true(lib.containsIdentifier('foo', utils.expression('tag`${foo} ${bar} ${baz}`'))); 149 | t.true(lib.containsIdentifier('foo', utils.expression('foo`bar`'))); 150 | 151 | t.false(lib.containsIdentifier('foo', utils.expression('`foo`'))); 152 | t.false(lib.containsIdentifier('foo', utils.expression('`foo bar`'))); 153 | t.false(lib.containsIdentifier('foo', utils.expression('`foo bar ${baz}`'))); 154 | t.false(lib.containsIdentifier('foo', utils.expression('bar`baz`'))); 155 | /* eslint-enable no-template-curly-in-string */ 156 | }); 157 | 158 | test(`(${name}) if node is in an object`, t => { 159 | t.true(lib.containsIdentifier('foo', utils.expression('a = { b: foo }'))); 160 | t.true(lib.containsIdentifier('foo', utils.expression('a = { foo }'))); 161 | t.true(lib.containsIdentifier('foo', utils.expression('a = { "b": foo }'))); 162 | t.true(lib.containsIdentifier('foo', utils.expression('a = { "bar": 2, [foo]: b }'))); 163 | t.true(lib.containsIdentifier('foo', utils.expression('a = { ...foo, bar }'))); 164 | t.true(lib.containsIdentifier('foo', utils.expression('a = { ...baz = foo, bar }'))); 165 | 166 | t.false(lib.containsIdentifier('foo', utils.expression('a = { foo: b }'))); 167 | t.false(lib.containsIdentifier('foo', utils.expression('a = { [bar]: b }'))); 168 | t.false(lib.containsIdentifier('foo', utils.expression('a = { "foo": b }'))); 169 | t.false(lib.containsIdentifier('foo', utils.expression('a = { foo() {} }'))); 170 | t.false(lib.containsIdentifier('foo', utils.expression('a = { ...bar, baz }'))); 171 | t.false(lib.containsIdentifier('foo', utils.expression('a = { ...bar = other, baz }'))); 172 | }); 173 | 174 | test(`(${name}) if node is in an array`, t => { 175 | t.true(lib.containsIdentifier('foo', utils.expression('[1, 2, foo]'))); 176 | t.true(lib.containsIdentifier('foo', utils.expression('[,, foo]'))); 177 | t.true(lib.containsIdentifier('foo', utils.expression('[, [], [foo]]'))); 178 | 179 | t.false(lib.containsIdentifier('foo', utils.expression('[]'))); 180 | t.false(lib.containsIdentifier('foo', utils.expression('[1, 2, 3]'))); 181 | }); 182 | 183 | test(`(${name}) if node is a VariableDeclaration`, t => { 184 | t.true(lib.containsIdentifier('foo', utils.statement('var bar = foo;'))); 185 | t.true(lib.containsIdentifier('foo', utils.statement('let bar = foo;'))); 186 | t.true(lib.containsIdentifier('foo', utils.statement('const bar = foo;'))); 187 | t.true(lib.containsIdentifier('foo', utils.statement('const baz = 42, bar = foo;'))); 188 | t.true(lib.containsIdentifier('foo', utils.statement('const bar = foo, baz = 42;'))); 189 | t.true(lib.containsIdentifier('foo', utils.statement('const baz = 42, bar = foo;'))); 190 | t.true(lib.containsIdentifier('foo', utils.statement('const {bar} = foo;'))); 191 | t.true(lib.containsIdentifier('foo', utils.statement('const {bar = foo} = baz;'))); 192 | t.true(lib.containsIdentifier('foo', utils.statement('const {a: {bar = foo}} = baz;'))); 193 | t.true(lib.containsIdentifier('foo', utils.statement('const [bar = foo] = baz;'))); 194 | t.true(lib.containsIdentifier('foo', utils.statement('const [[bar = foo]] = baz;'))); 195 | t.true(lib.containsIdentifier('foo', utils.statement('const {...bar} = foo;'))); 196 | 197 | t.false(lib.containsIdentifier('foo', utils.statement('var foo;'))); 198 | t.false(lib.containsIdentifier('foo', utils.statement('let foo;'))); 199 | t.false(lib.containsIdentifier('foo', utils.statement('var foo, bar;'))); 200 | t.false(lib.containsIdentifier('foo', utils.statement('var bar, baz;'))); 201 | t.false(lib.containsIdentifier('foo', utils.statement('const bar = baz;'))); 202 | t.false(lib.containsIdentifier('foo', utils.statement('const foo = bar;'))); 203 | t.false(lib.containsIdentifier('foo', utils.statement('const [foo] = bar;'))); 204 | t.false(lib.containsIdentifier('foo', utils.statement('const [,,foo] = bar;'))); 205 | t.false(lib.containsIdentifier('foo', utils.statement('const {foo} = bar;'))); 206 | t.false(lib.containsIdentifier('foo', utils.statement('const {baz: foo} = bar;'))); 207 | t.false(lib.containsIdentifier('foo', utils.statement('const {foo: baz} = bar;'))); 208 | t.false(lib.containsIdentifier('foo', utils.statement('const {...foo} = bar;'))); 209 | }); 210 | 211 | test(`(${name}) if node is in an assignment`, t => { 212 | t.true(lib.containsIdentifier('foo', utils.statement('foo = bar;'))); 213 | t.true(lib.containsIdentifier('foo', utils.statement('bar = foo;'))); 214 | t.true(lib.containsIdentifier('foo', utils.statement('[foo.baz] = bar;'))); 215 | t.true(lib.containsIdentifier('foo', utils.statement('[baz.baz, foo.bar] = bar;'))); 216 | 217 | t.false(lib.containsIdentifier('foo', utils.statement('bar = baz;'))); 218 | t.false(lib.containsIdentifier('foo', utils.statement('[baz.foo] = bar;'))); 219 | t.false(lib.containsIdentifier('foo', utils.statement('[baz.other] = bar;'))); 220 | }); 221 | 222 | test(`(${name}) if node is a or in a function`, t => { 223 | t.true(lib.containsIdentifier('foo', utils.statement('function bar() { foo }'))); 224 | t.true(lib.containsIdentifier('foo', utils.statement('function bar() { return foo }'))); 225 | t.true(lib.containsIdentifier('foo', utils.statement('function bar(a, b = foo) {}'))); 226 | t.true(lib.containsIdentifier('foo', utils.statement('var a = function(a, b = foo) {}'))); 227 | t.true(lib.containsIdentifier('foo', utils.statement('var a = (a, b = foo) => {}'))); 228 | t.true(lib.containsIdentifier('foo', utils.statement('var a = () => foo'))); 229 | t.true(lib.containsIdentifier('foo', utils.statement('function bar(foo, b = foo) {}'))); 230 | t.true(lib.containsIdentifier('foo', utils.statement('function bar(b = foo, foo) {}'))); 231 | t.true(lib.containsIdentifier('foo', utils.statement('function bar({a = foo} = {}) {}'))); 232 | t.true(lib.containsIdentifier('foo', utils.statement('function bar({a = foo} = {foo}) {}'))); 233 | 234 | t.false(lib.containsIdentifier('foo', utils.statement('function bar() { baz }'))); 235 | t.false(lib.containsIdentifier('foo', utils.statement('function bar(a, b, c) {}'))); 236 | t.false(lib.containsIdentifier('foo', utils.statement('function foo() { foo }'))); 237 | t.false(lib.containsIdentifier('foo', utils.statement('function bar(foo) { foo }'))); 238 | t.false(lib.containsIdentifier('foo', utils.statement('function bar(...foo) { foo }'))); 239 | t.false(lib.containsIdentifier('foo', utils.statement('function bar({foo}) { foo }'))); 240 | t.false(lib.containsIdentifier('foo', utils.statement('function bar({a: foo}) { foo }'))); 241 | t.false(lib.containsIdentifier('foo', utils.statement('function bar({a: {foo}}) { foo }'))); 242 | t.false(lib.containsIdentifier('foo', utils.statement('function bar([, foo]) { foo }'))); 243 | t.false(lib.containsIdentifier('foo', utils.statement('function bar([, [foo]]) { foo }'))); 244 | t.false(lib.containsIdentifier('foo', utils.statement('var a = foo => foo'))); 245 | }); 246 | 247 | test(`(${name}) if node is a CallExpression`, t => { 248 | t.true(lib.containsIdentifier('foo', utils.expression(`foo(bar)`))); 249 | t.true(lib.containsIdentifier('foo', utils.expression(`foo.baz(bar)`))); 250 | t.true(lib.containsIdentifier('foo', utils.expression(`bar(foo)`))); 251 | t.true(lib.containsIdentifier('foo', utils.expression(`bar(baz, foo)`))); 252 | t.true(lib.containsIdentifier('foo', utils.expression(`bar(baz, {foo})`))); 253 | t.true(lib.containsIdentifier('foo', utils.expression(`bar(baz, [foo])`))); 254 | t.true(lib.containsIdentifier('foo', utils.expression(`bar(baz, ...foo)`))); 255 | 256 | t.false(lib.containsIdentifier('foo', utils.expression(`bar()`))); 257 | t.false(lib.containsIdentifier('foo', utils.expression(`bar.baz()`))); 258 | t.false(lib.containsIdentifier('foo', utils.expression(`bar.foo()`))); 259 | }); 260 | 261 | test(`(${name}) if node is a NewExpression`, t => { 262 | t.true(lib.containsIdentifier('foo', utils.expression(`new foo()`))); 263 | t.true(lib.containsIdentifier('foo', utils.expression(`new Bar(foo)`))); 264 | t.true(lib.containsIdentifier('foo', utils.expression(`new Bar(baz, foo, bar)`))); 265 | 266 | t.false(lib.containsIdentifier('foo', utils.expression(`new Bar(baz, bar)`))); 267 | }); 268 | 269 | test(`(${name}) if node is a ForStatement`, t => { 270 | t.true(lib.containsIdentifier('foo', utils.statement(`for(var i = 0; i < foo.length; i++) {}`))); 271 | t.true(lib.containsIdentifier('foo', utils.statement(`for(foo = 0; i < bar.length; i++) {}`))); 272 | t.true(lib.containsIdentifier('foo', utils.statement(`for(i = 0; i < bar.length; foo++) {}`))); 273 | t.true(lib.containsIdentifier('foo', utils.statement(`for(i = 0; i < bar.length; i++, foo++) {}`))); 274 | t.true(lib.containsIdentifier('foo', utils.statement(`for(var i = 0; i < bar.length; i++) { foo }`))); 275 | 276 | t.false(lib.containsIdentifier('foo', utils.statement(`for(var i = 0; i < bar.length; i++) {}`))); 277 | t.false(lib.containsIdentifier('foo', utils.statement(`for(var foo = 0; foo < bar.length; i++) {}`))); 278 | t.false(lib.containsIdentifier('foo', utils.statement(`for(let foo = 0; foo < bar.length; i++) {}`))); 279 | }); 280 | 281 | test(`(${name}) if node is a ForInStatement`, t => { 282 | t.true(lib.containsIdentifier('foo', utils.statement(`for(let i in foo) {}`))); 283 | t.true(lib.containsIdentifier('foo', utils.statement(`for(i in foo) {}`))); 284 | t.true(lib.containsIdentifier('foo', utils.statement(`for(let i in bar) { foo }`))); 285 | t.true(lib.containsIdentifier('foo', utils.statement(`for(foo in bar) {}`))); 286 | 287 | t.false(lib.containsIdentifier('foo', utils.statement(`for(let i in bar) { baz }`))); 288 | t.false(lib.containsIdentifier('foo', utils.statement(`for(let foo in bar) {}`))); 289 | }); 290 | 291 | test(`(${name}) if node is a ForOfStatement`, t => { 292 | t.true(lib.containsIdentifier('foo', utils.statement(`for(let i of foo) {}`))); 293 | t.true(lib.containsIdentifier('foo', utils.statement(`for(i of foo) {}`))); 294 | t.true(lib.containsIdentifier('foo', utils.statement(`for(let i of bar) { foo }`))); 295 | t.true(lib.containsIdentifier('foo', utils.statement(`for(foo of bar) {}`))); 296 | 297 | t.false(lib.containsIdentifier('foo', utils.statement(`for(let i of bar) { baz }`))); 298 | t.false(lib.containsIdentifier('foo', utils.statement(`for(let foo of bar) {}`))); 299 | }); 300 | 301 | test(`(${name}) if node is a loop control flow statement`, t => { 302 | t.false(lib.containsIdentifier('foo', utils.statement(`for(i of bar) { break; }`))); 303 | t.false(lib.containsIdentifier('foo', utils.statement(`foo: for(i of bar) { break foo; }`))); 304 | t.false(lib.containsIdentifier('foo', utils.statement(`for(i of bar) { continue; }`))); 305 | t.false(lib.containsIdentifier('foo', utils.statement(`foo: for(i of bar) { continue foo; }`))); 306 | }); 307 | 308 | test(`(${name}) if node is a WhileStatement`, t => { 309 | t.true(lib.containsIdentifier('foo', utils.statement(`while (foo) {}`))); 310 | t.true(lib.containsIdentifier('foo', utils.statement(`while (bar) { foo }`))); 311 | 312 | t.false(lib.containsIdentifier('foo', utils.statement(`while (bar) { baz }`))); 313 | }); 314 | 315 | test(`(${name}) if node is a DoWhileStatement`, t => { 316 | t.true(lib.containsIdentifier('foo', utils.statement(`do {} while (foo)`))); 317 | t.true(lib.containsIdentifier('foo', utils.statement(`do { foo } while (bar)`))); 318 | 319 | t.false(lib.containsIdentifier('foo', utils.statement(`do { baz } while (bar)`))); 320 | }); 321 | 322 | test(`(${name}) if node is a ThrowStatement`, t => { 323 | t.true(lib.containsIdentifier('foo', utils.statement(`throw foo`))); 324 | 325 | t.false(lib.containsIdentifier('foo', utils.statement(`throw bar`))); 326 | }); 327 | 328 | test(`(${name}) if node is a YieldExpression`, t => { 329 | t.true(lib.containsIdentifier('foo', utils.statement(`function *a() { yield foo; }`))); 330 | 331 | t.false(lib.containsIdentifier('foo', utils.statement(`function *a() { yield bar; }`))); 332 | }); 333 | 334 | test(`(${name}) if node is an await expression`, t => { 335 | t.true(lib.containsIdentifier('foo', utils.statement(`async function a() { await foo; }`))); 336 | 337 | t.false(lib.containsIdentifier('foo', utils.statement(`async function a() { await bar; }`))); 338 | }); 339 | 340 | test(`(${name}) if node is a try/catch`, t => { 341 | t.true(lib.containsIdentifier('foo', utils.statement(`try { foo } catch(e) {}`))); 342 | t.true(lib.containsIdentifier('foo', utils.statement(`try {} catch(e) { foo }`))); 343 | t.true(lib.containsIdentifier('foo', utils.statement(`try {} finally { foo }`))); 344 | 345 | t.false(lib.containsIdentifier('foo', utils.statement(`try { bar } catch(e) { baz } finally { other }`))); 346 | t.false(lib.containsIdentifier('foo', utils.statement(`try { bar } catch(foo) { foo }`))); 347 | }); 348 | 349 | test(`(${name}) if node is a switch case`, t => { 350 | t.true(lib.containsIdentifier('foo', utils.statement(`switch (foo) {}`))); 351 | t.true(lib.containsIdentifier('foo', utils.statement(`switch (bar) { case foo: break }`))); 352 | t.true(lib.containsIdentifier('foo', utils.statement(`switch (bar) { case baz: foo; break }`))); 353 | t.true(lib.containsIdentifier('foo', utils.statement(`switch (bar) { case 42: case baz: foo; break }`))); 354 | t.true(lib.containsIdentifier('foo', utils.statement(`switch (bar) { default: foo }`))); 355 | 356 | t.false(lib.containsIdentifier('foo', utils.statement(`switch (bar) { case baz: break; case 42: other; break; default: another; break;}`))); 357 | }); 358 | 359 | test(`(${name}) if node is a or in a class`, t => { 360 | t.true(lib.containsIdentifier('foo', utils.statement(`class Bar { baz() { foo } }`))); 361 | t.true(lib.containsIdentifier('foo', utils.statement(`class Bar { static baz() { foo } }`))); 362 | t.true(lib.containsIdentifier('foo', utils.statement(`class Bar extends foo { }`))); 363 | t.true(lib.containsIdentifier('foo', utils.statement(`var a = class extends foo { }`))); 364 | t.true(lib.containsIdentifier('foo', utils.statement(`var a = class { bar() { foo } }`))); 365 | t.true(lib.containsIdentifier('foo', utils.statement(`var a = class { bar() { super(foo) } }`))); 366 | 367 | t.false(lib.containsIdentifier('foo', utils.statement(`class foo {}`))); 368 | t.false(lib.containsIdentifier('foo', utils.statement(`class Bar { foo() { bar } }`))); 369 | t.false(lib.containsIdentifier('foo', utils.statement(`class Bar { static foo() { bar } }`))); 370 | t.false(lib.containsIdentifier('foo', utils.statement(`class foo extends foo { }`))); 371 | t.false(lib.containsIdentifier('foo', utils.statement(`class Bar extends Baz { }`))); 372 | t.false(lib.containsIdentifier('foo', utils.statement(`var a = class { bar() { baz } }`))); 373 | }); 374 | 375 | test(`(${name}) if node is a LabeledStatement`, t => { 376 | t.true(lib.containsIdentifier('foo', utils.statement(`bar: foo`))); 377 | 378 | t.false(lib.containsIdentifier('foo', utils.statement(`foo: bar`))); 379 | t.false(lib.containsIdentifier('foo', utils.statement(`bar: baz`))); 380 | }); 381 | 382 | test(`(${name}) if node is a DebuggerStatement`, t => { 383 | t.false(lib.containsIdentifier('foo', utils.statement(`debugger;`))); 384 | }); 385 | 386 | test(`(${name}) if node is an EmptyStatement`, t => { 387 | t.false(lib.containsIdentifier('foo', utils.statement(`;;;`))); 388 | }); 389 | 390 | test(`(${name}) if node is with import statement`, t => { 391 | t.true(lib.containsIdentifier('foo', utils.program(`import 'foo'; foo`))); 392 | t.true(lib.containsIdentifier('foo', utils.program(`import bar from 'foo'; foo`))); 393 | t.true(lib.containsIdentifier('foo', utils.program(`import * as bar from 'foo'; foo`))); 394 | t.true(lib.containsIdentifier('foo', utils.program(`import {foo as bar} from 'foo'; foo`))); 395 | t.true(lib.containsIdentifier('foo', utils.program(`import baz, {foo as bar} from 'foo'; foo`))); 396 | 397 | t.false(lib.containsIdentifier('foo', utils.program(`import foo from 'foo'; foo`))); 398 | t.false(lib.containsIdentifier('foo', utils.program(`import * as foo from 'foo'; foo`))); 399 | t.false(lib.containsIdentifier('foo', utils.program(`import {foo} from 'foo'; foo`))); 400 | t.false(lib.containsIdentifier('foo', utils.program(`import bar, {foo} from 'foo'; foo`))); 401 | t.false(lib.containsIdentifier('foo', utils.program(`import {bar as foo} from 'foo'; foo`))); 402 | }); 403 | 404 | test(`(${name}) if node is in export statement`, t => { 405 | t.true(lib.containsIdentifier('foo', utils.statement(`export default foo`))); 406 | t.true(lib.containsIdentifier('foo', utils.program(`export let foo; foo`))); 407 | 408 | // t.false(lib.containsIdentifier('foo', utils.statement(`export foo from 'foo'`))); 409 | t.false(lib.containsIdentifier('foo', utils.statement(`export {bar} from 'foo'`))); 410 | t.false(lib.containsIdentifier('foo', utils.program(`export let bar; bar`))); 411 | }); 412 | 413 | test(`(${name}) if node is in JSX`, t => { 414 | t.true(lib.containsIdentifier('foo', utils.expression(``))); 415 | t.true(lib.containsIdentifier('foo', utils.expression(``))); 416 | t.true(lib.containsIdentifier('foo', utils.expression(`{foo}`))); 417 | t.true(lib.containsIdentifier('foo', utils.expression(``))); 418 | t.true(lib.containsIdentifier('foo', utils.expression(``))); 419 | 420 | t.false(lib.containsIdentifier('foo', utils.expression(``))); 421 | t.false(lib.containsIdentifier('foo', utils.expression(``))); 422 | t.false(lib.containsIdentifier('foo', utils.expression(`foo`))); 423 | t.false(lib.containsIdentifier('foo', utils.expression(``))); 424 | t.false(lib.containsIdentifier('foo', utils.expression(``))); 425 | t.false(lib.containsIdentifier('foo', utils.expression(``))); 426 | }); 427 | }); 428 | -------------------------------------------------------------------------------- /test/get-property-name.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import lib from '../'; 3 | import babel from './helpers/babel'; 4 | import espree from './helpers/espree'; 5 | 6 | [espree, babel].forEach(({name, utils}) => { 7 | test(`(${name}) should return undefined if node is not a MemberExpression`, t => { 8 | t.true(undefined === lib.getPropertyName(null)); 9 | t.true(undefined === lib.getPropertyName(utils.expression(`null`))); 10 | t.true(undefined === lib.getPropertyName(utils.expression(`42`))); 11 | t.true(undefined === lib.getPropertyName(utils.expression('`42`'))); 12 | t.true(undefined === lib.getPropertyName(utils.expression('foo'))); 13 | t.true(undefined === lib.getPropertyName(utils.expression('foo()'))); 14 | t.true(undefined === lib.getPropertyName(utils.expression('foo.bar()'))); 15 | }); 16 | 17 | test(`(${name}) should return the name of the property if node is non-computed and property is an Identifier`, t => { 18 | t.true(lib.getPropertyName(utils.expression(`foo.bar`)) === 'bar'); 19 | t.true(lib.getPropertyName(utils.expression(`foo.bar.baz`)) === 'baz'); 20 | t.true(lib.getPropertyName(utils.expression(`foo().bar`)) === 'bar'); 21 | t.true(lib.getPropertyName(utils.expression(`foo().bar`)) === 'bar'); 22 | t.true(lib.getPropertyName(utils.expression(`foo.null`)) === 'null'); 23 | t.true(lib.getPropertyName(utils.expression(`foo.undefined`)) === 'undefined'); 24 | }); 25 | 26 | test(`(${name}) should return the value of the property if it is a Literal`, t => { 27 | t.true(lib.getPropertyName(utils.expression(`foo['bar']`)) === 'bar'); 28 | t.true(lib.getPropertyName(utils.expression(`foo.bar['baz']`)) === 'baz'); 29 | t.true(lib.getPropertyName(utils.expression(`foo()['bar']`)) === 'bar'); 30 | t.true(lib.getPropertyName(utils.expression(`foo[null]`)) === null); 31 | t.true(lib.getPropertyName(utils.expression(`foo[undefined]`)) === undefined); 32 | }); 33 | 34 | test(`(${name}) should return the index as a number of the property if it is a number`, t => { 35 | t.true(lib.getPropertyName(utils.expression(`foo[0]`)) === 0); 36 | }); 37 | 38 | test(`(${name}) should return the index as a number of the property is a statically knowable expression`, t => { 39 | t.true(lib.getPropertyName(utils.expression(`foo['th' + 'en']`)) === 'then'); 40 | t.true(lib.getPropertyName(utils.expression(`foo[1 + 2]`)) === 3); 41 | }); 42 | 43 | test(`(${name}) should return undefined if the property is an expression that is not statically knowable`, t => { 44 | t.true(undefined === lib.getPropertyName(utils.expression(`foo[bar]`))); 45 | t.true(undefined === lib.getPropertyName(utils.expression(`foo[bar()]`))); 46 | t.true(undefined === lib.getPropertyName(utils.expression(`foo[bar + baz]`))); 47 | t.true(undefined === lib.getPropertyName(utils.expression(`foo[0 + bar]`))); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/get-require-source.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import lib from '../'; 3 | import babel from './helpers/babel'; 4 | import espree from './helpers/espree'; 5 | 6 | [espree, babel].forEach(({name, utils}) => { 7 | test(`(${name}) should return undefined if node is not a require call`, t => { 8 | t.true(undefined === lib.getRequireSource(null)); 9 | t.true(undefined === lib.getRequireSource(utils.expression(`null`))); 10 | t.true(undefined === lib.getRequireSource(utils.expression(`42`))); 11 | t.true(undefined === lib.getRequireSource(utils.expression('`42`'))); 12 | t.true(undefined === lib.getRequireSource(utils.expression(`a('lodash')`))); 13 | t.true(undefined === lib.getRequireSource(utils.expression(`require.a('lodash')`))); 14 | t.true(undefined === lib.getRequireSource(utils.expression(`require(foo)`))); 15 | t.true(undefined === lib.getRequireSource(utils.expression(`require(foo + bar)`))); 16 | t.true(undefined === lib.getRequireSource(utils.expression(`require('lodash', 'underscore')`))); 17 | t.true(undefined === lib.getRequireSource(utils.expression(`require(['lodash'])`))); 18 | t.true(undefined === lib.getRequireSource(utils.expression(`require(42)`))); 19 | }); 20 | 21 | test(`(${name}) should return the value of the first argument if node is a proper require call`, t => { 22 | t.true(lib.getRequireSource(utils.expression(`require('lodash')`)) === 'lodash'); 23 | t.true(lib.getRequireSource(utils.expression(`require('async')`)) === 'async'); 24 | t.true(lib.getRequireSource(utils.expression(`require('./')`)) === './'); 25 | t.true(lib.getRequireSource(utils.expression(`require('@cycle/dom')`)) === '@cycle/dom'); 26 | t.true(lib.getRequireSource(utils.expression(`require('../path/to/over/there')`)) === '../path/to/over/there'); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/helpers/babel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const babel = require('babel-eslint'); 4 | 5 | function program(code) { 6 | return babel.parse(code); 7 | } 8 | 9 | function statement(code) { 10 | return program(code).body[0]; 11 | } 12 | 13 | function expression(code) { 14 | return statement(code).expression; 15 | } 16 | 17 | module.exports = { 18 | name: 'babel', 19 | utils: { 20 | expression, 21 | program, 22 | statement 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /test/helpers/espree.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const espree = require('espree'); 4 | 5 | function program(code) { 6 | return espree.parse(code, { 7 | sourceType: 'module', 8 | ecmaVersion: 8, 9 | ecmaFeatures: { 10 | jsx: true, 11 | experimentalObjectRestSpread: true 12 | } 13 | }); 14 | } 15 | 16 | function statement(code) { 17 | return program(code).body[0]; 18 | } 19 | 20 | function expression(code) { 21 | return statement(code).expression; 22 | } 23 | 24 | module.exports = { 25 | name: 'espree', 26 | utils: { 27 | expression, 28 | program, 29 | statement 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /test/is-function-expression.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import lib from '../'; 3 | import babel from './helpers/babel'; 4 | import espree from './helpers/espree'; 5 | 6 | [espree, babel].forEach(({name, utils}) => { 7 | test(`(${name}) should return false if node is not a function expression`, t => { 8 | t.false(lib.isFunctionExpression(null)); 9 | t.false(lib.isFunctionExpression(utils.expression(`null`))); 10 | t.false(lib.isFunctionExpression(utils.expression(`42`))); 11 | t.false(lib.isFunctionExpression(utils.expression('`42`'))); 12 | t.false(lib.isFunctionExpression(utils.expression(`(function() {})()`))); 13 | }); 14 | 15 | test(`(${name}) should return true if node is a function expression`, t => { 16 | t.true(lib.isFunctionExpression(utils.expression(`(function() {})`))); 17 | t.true(lib.isFunctionExpression(utils.expression(`(function foo() {})`))); 18 | }); 19 | 20 | test(`(${name}) should return true if node is an arrow function expression`, t => { 21 | t.true(lib.isFunctionExpression(utils.expression(`() => {}`))); 22 | t.true(lib.isFunctionExpression(utils.expression(`a => a`))); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/is-promise.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import lib from '../'; 3 | import babel from './helpers/babel'; 4 | import espree from './helpers/espree'; 5 | 6 | [espree, babel].forEach(({name, utils}) => { 7 | test(`(${name}) should return false if node is falsy or not a node`, t => { 8 | t.false(lib.isPromise(null)); 9 | t.false(lib.isPromise(undefined)); 10 | t.false(lib.isPromise({})); 11 | t.false(lib.isPromise('foo')); 12 | }); 13 | 14 | test(`(${name}) should return true if node is call expression which calls '.then()'`, t => { 15 | t.true(lib.isPromise(utils.expression(`foo.then(fn)`))); 16 | t.true(lib.isPromise(utils.expression(`foo().then(fn)`))); 17 | t.true(lib.isPromise(utils.expression(`foo['then'](fn)`))); 18 | t.true(lib.isPromise(utils.expression(`foo['th' + 'en'](fn)`))); 19 | 20 | t.false(lib.isPromise(utils.expression(`foo().bar(fn)`))); 21 | t.false(lib.isPromise(utils.expression(`then(fn)`))); 22 | t.false(lib.isPromise(utils.expression(`foo['bar'](fn)`))); 23 | t.false(lib.isPromise(utils.expression(`foo[0](fn)`))); 24 | }); 25 | 26 | test(`(${name}) should return true if node is call expression which calls '.catch()'`, t => { 27 | t.true(lib.isPromise(utils.expression(`foo.catch(fn)`))); 28 | t.true(lib.isPromise(utils.expression(`foo().catch(fn)`))); 29 | t.true(lib.isPromise(utils.expression(`foo['catch'](fn)`))); 30 | }); 31 | 32 | test(`(${name}) should return false when accessing a property of a Promise`, t => { 33 | t.false(lib.isPromise(utils.expression(`foo.then(fn).foo`))); 34 | t.false(lib.isPromise(utils.expression(`foo.catch(fn).foo`))); 35 | }); 36 | 37 | test(`(${name}) should return false when calling a property of a Promise`, t => { 38 | t.false(lib.isPromise(utils.expression(`foo.then(fn).map(fn)`))); 39 | t.false(lib.isPromise(utils.expression(`foo.catch(fn).map(fn)`))); 40 | }); 41 | 42 | test(`(${name}) should return true when expression is a call to 'Promise.{resolve|reject|race|all}()`, t => { 43 | t.true(lib.isPromise(utils.expression(`Promise.resolve(fn)`))); 44 | t.true(lib.isPromise(utils.expression(`Promise.resolve(fn).then(fn)`))); 45 | t.true(lib.isPromise(utils.expression(`Promise.resolve(fn).catch(fn)`))); 46 | t.true(lib.isPromise(utils.expression(`Promise.reject(fn)`))); 47 | t.true(lib.isPromise(utils.expression(`Promise.race(fn)`))); 48 | t.true(lib.isPromise(utils.expression(`Promise.all(fn)`))); 49 | t.true(lib.isPromise(utils.expression(`Promise['resolve'](fn)`))); 50 | t.true(lib.isPromise(utils.expression(`Promise['reject'](fn)`))); 51 | t.true(lib.isPromise(utils.expression(`Promise['race'](fn)`))); 52 | t.true(lib.isPromise(utils.expression(`Promise['all'](fn)`))); 53 | 54 | t.false(lib.isPromise(utils.expression(`bar.resolve(fn)`))); 55 | t.false(lib.isPromise(utils.expression(`bar.reject(fn)`))); 56 | t.false(lib.isPromise(utils.expression(`bar.race(fn)`))); 57 | t.false(lib.isPromise(utils.expression(`bar.all(fn)`))); 58 | t.false(lib.isPromise(utils.expression(`foo.Promise.resolve(fn)`))); 59 | t.false(lib.isPromise(utils.expression(`Promise.resolve(fn).map(fn)`))); 60 | }); 61 | 62 | test(`(${name}) should return false when expression is a call to a known non-Promise returning method of 'Promise`, t => { 63 | t.false(lib.isPromise(utils.expression(`Promise.is(fn)`))); 64 | t.false(lib.isPromise(utils.expression(`Promise.cancel(fn)`))); 65 | t.false(lib.isPromise(utils.expression(`Promise.promisify(fn)`))); 66 | t.false(lib.isPromise(utils.expression(`Promise.promisifyAll(obj)`))); 67 | }); 68 | 69 | // Mostly Bluebird methods 70 | test(`(${name}) should return true when expression is a call to 'Promise.{anything}() except exceptions`, t => { 71 | t.true(lib.isPromise(utils.expression(`Promise.map(input, fn)`))); 72 | t.true(lib.isPromise(utils.expression(`Promise.filter(input, fn)`))); 73 | t.true(lib.isPromise(utils.expression(`Promise.reduce(input, fn)`))); 74 | t.true(lib.isPromise(utils.expression(`Promise.each(input, fn)`))); 75 | t.true(lib.isPromise(utils.expression(`Promise.mapSeries(input, fn)`))); 76 | t.true(lib.isPromise(utils.expression(`Promise.filter(input, fn)`))); 77 | t.true(lib.isPromise(utils.expression(`Promise.fromCallback(fn)`))); 78 | t.true(lib.isPromise(utils.expression(`Promise.fromNode(fn)`))); 79 | t.true(lib.isPromise(utils.expression(`Promise.using(fn)`))); 80 | t.true(lib.isPromise(utils.expression(`Promise['map'](fn)`))); 81 | }); 82 | 83 | test(`(${name}) should return true when calling 'new Promise(fn)'`, t => { 84 | t.true(lib.isPromise(utils.expression(`new Promise(fn);`))); 85 | t.true(lib.isPromise(utils.expression(`new Promise(fn).then();`))); 86 | 87 | t.false(lib.isPromise(utils.expression(`Promise(fn)`))); 88 | t.false(lib.isPromise(utils.expression(`new Promise(fn).map(fn)`))); 89 | t.false(lib.isPromise(utils.expression(`new Foo(fn)`))); 90 | t.false(lib.isPromise(utils.expression(`new Foo.bar(fn)`))); 91 | t.false(lib.isPromise(utils.expression(`new Promise.bar(fn)`))); 92 | t.false(lib.isPromise(utils.expression(`new Promise.resolve(fn)`))); 93 | t.false(lib.isPromise(utils.expression(`new Foo.resolve(fn)`))); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /test/is-static-require.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import lib from '../'; 3 | import babel from './helpers/babel'; 4 | import espree from './helpers/espree'; 5 | 6 | [espree, babel].forEach(({name, utils}) => { 7 | test(`(${name}) should return false if node is falsy or not a node`, t => { 8 | t.false(lib.isStaticRequire(null)); 9 | t.false(lib.isStaticRequire(undefined)); 10 | t.false(lib.isStaticRequire({})); 11 | t.false(lib.isStaticRequire('foo')); 12 | }); 13 | 14 | test(`(${name}) should return false if node is not a CallExpression`, t => { 15 | t.false(lib.isStaticRequire(utils.expression(`'foo'`))); 16 | t.false(lib.isStaticRequire(utils.expression(`42`))); 17 | t.false(lib.isStaticRequire(utils.expression(`a.b`))); 18 | t.false(lib.isStaticRequire(utils.expression(`foo`))); 19 | }); 20 | 21 | test(`(${name}) should return false if node is a CallExpression without an Identifier as the callee`, t => { 22 | t.false(lib.isStaticRequire(utils.expression(`require.a('lodash')`))); 23 | t.false(lib.isStaticRequire(utils.expression(`a.require('lodash')`))); 24 | t.false(lib.isStaticRequire(utils.expression(`require()('lodash')`))); 25 | t.false(lib.isStaticRequire(utils.expression(`(a + b)('lodash')`))); 26 | }); 27 | 28 | test(`(${name}) should return false if node is a CallExpression with an Identifier not named \`require\``, t => { 29 | t.false(lib.isStaticRequire(utils.expression(`foo('lodash')`))); 30 | t.false(lib.isStaticRequire(utils.expression(`bar('lodash')`))); 31 | }); 32 | 33 | test(`(${name}) should return false if node is a CallExpression with more or less than 1 argument`, t => { 34 | t.false(lib.isStaticRequire(utils.expression(`require()`))); 35 | t.false(lib.isStaticRequire(utils.expression(`require('lodash', 'underscore')`))); 36 | t.false(lib.isStaticRequire(utils.expression(`require('lodash', 'underscore', 'async')`))); 37 | }); 38 | 39 | test(`(${name}) should return false if node is a CallExpression with an argument that is not a Literal string`, t => { 40 | t.false(lib.isStaticRequire(utils.expression(`require(['lodash'])`))); 41 | t.false(lib.isStaticRequire(utils.expression(`require(3)`))); 42 | t.false(lib.isStaticRequire(utils.expression(`require(a)`))); 43 | t.false(lib.isStaticRequire(utils.expression(`require(a + b)`))); 44 | }); 45 | 46 | test(`(${name}) should return true if node is a CallExpression on \`require\` with one string Literal argument`, t => { 47 | t.true(lib.isStaticRequire(utils.expression(`require('lodash')`))); 48 | t.true(lib.isStaticRequire(utils.expression(`require('async')`))); 49 | t.true(lib.isStaticRequire(utils.expression(`require('./')`))); 50 | t.true(lib.isStaticRequire(utils.expression(`require('@cycle/dom')`))); 51 | t.true(lib.isStaticRequire(utils.expression(`require('../path/to/over/there')`))); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /test/some-contain-identifier.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import lib from '../'; 3 | import babel from './helpers/babel'; 4 | import espree from './helpers/espree'; 5 | 6 | [espree, babel].forEach(({name, utils}) => { 7 | test(`(${name}) should return false if nodes is nil`, t => { 8 | t.false(lib.someContainIdentifier('foo', null)); 9 | t.false(lib.someContainIdentifier('foo', undefined)); 10 | }); 11 | 12 | test(`(${name}) should return false if nodes is not an array`, t => { 13 | t.false(lib.someContainIdentifier('foo', utils.expression(`foo`))); 14 | }); 15 | 16 | test(`(${name}) should return false if name is not found in any of the nodes`, t => { 17 | t.false(lib.someContainIdentifier('other', [ 18 | utils.expression(`foo`), 19 | utils.expression(`bar`), 20 | utils.expression(`baz`) 21 | ])); 22 | }); 23 | 24 | test(`(${name}) should return true if name is found in any of the nodes`, t => { 25 | t.true(lib.someContainIdentifier('foo', [ 26 | utils.expression(`foo`) 27 | ])); 28 | t.true(lib.someContainIdentifier('foo', [ 29 | utils.expression(`foo`), 30 | utils.expression(`foo`) 31 | ])); 32 | t.true(lib.someContainIdentifier('foo', [ 33 | utils.expression(`foo`), 34 | utils.expression(`bar`), 35 | utils.expression(`baz`) 36 | ])); 37 | t.true(lib.someContainIdentifier('foo', [ 38 | utils.expression(`bar`), 39 | utils.expression(`foo`), 40 | utils.expression(`baz`) 41 | ])); 42 | t.true(lib.someContainIdentifier('foo', [ 43 | utils.expression(`bar`), 44 | utils.expression(`baz`), 45 | utils.expression(`foo`) 46 | ])); 47 | }); 48 | }); 49 | --------------------------------------------------------------------------------