├── .github └── workflows │ ├── node-aught.yml │ ├── node-tens.yml │ └── nodejs.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── CHANGELOG.md ├── LICENSE ├── bench ├── detect.js └── esprima_v_acorn.txt ├── bin └── detective.js ├── example ├── strings.js └── strings_src.js ├── index.js ├── package.json ├── readme.markdown └── test ├── both.js ├── chained.js ├── complicated.js ├── es2019.js ├── es6-module.js ├── files ├── both.js ├── chained.js ├── es6-module.js ├── for-await.js ├── generators.js ├── isrequire.js ├── nested.js ├── optional-catch.js ├── rest-spread.js ├── set-in-object-pattern.js ├── shebang.js ├── sparse-array.js ├── strings.js ├── word.js └── yield.js ├── generators.js ├── isrequire.js ├── nested.js ├── noargs.js ├── parseopts.js ├── rest-spread.js ├── return.js ├── set-in-object-pattern.js ├── shebang.js ├── sparse-array.js ├── strings.js ├── word.js └── yield.js /.github/workflows/node-aught.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: node.js < 10' 2 | 3 | on: [pull_request, push] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | tests: 10 | uses: ljharb/actions/.github/workflows/node.yml@main 11 | with: 12 | range: '< 10' 13 | type: minors 14 | command: npm run tests-only 15 | 16 | node: 17 | name: 'node < 10' 18 | needs: [tests] 19 | runs-on: ubuntu-latest 20 | steps: 21 | - run: 'echo tests completed' 22 | -------------------------------------------------------------------------------- /.github/workflows/node-tens.yml: -------------------------------------------------------------------------------- 1 | name: 'Tests: node.js >= 10' 2 | 3 | on: [pull_request, push] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | tests: 10 | uses: ljharb/actions/.github/workflows/node.yml@main 11 | with: 12 | range: '>= 10' 13 | type: minors 14 | command: npm run tests-only 15 | 16 | node: 17 | name: 'node >= 10' 18 | needs: [tests] 19 | runs-on: ubuntu-latest 20 | steps: 21 | - run: 'echo tests completed' 22 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | matrix: 7 | runs-on: ubuntu-latest 8 | outputs: 9 | latest: ${{ steps.set-matrix.outputs.requireds }} 10 | nonlatest: ${{ steps.set-matrix.outputs.optionals }} 11 | steps: 12 | - uses: ljharb/actions/node/matrix@main 13 | id: set-matrix 14 | with: 15 | versionsAsRoot: true 16 | type: majors 17 | preset: ">= 0.8" 18 | 19 | latest: 20 | needs: [matrix] 21 | name: 'latest majors' 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | os: [windows-latest, macos-latest] 26 | node-version: ${{ fromJson(needs.matrix.outputs.latest) }} 27 | exclude: 28 | - os: windows-latest 29 | node-version: '3' 30 | - os: windows-latest 31 | node-version: '2' 32 | - os: windows-latest 33 | node-version: '1' 34 | - os: windows-latest 35 | node-version: '0.8' 36 | 37 | runs-on: ${{matrix.os}} 38 | 39 | steps: 40 | - uses: actions/checkout@v3 41 | 42 | - uses: ljharb/actions/node/install@main 43 | name: 'nvm install ${{ matrix.node-version }} && npm install' 44 | with: 45 | node-version: ${{ matrix.node-version }} 46 | skip-ls-check: true 47 | if: matrix.os != 'windows-latest' 48 | 49 | - name: Use Node.js ${{ matrix.node-version }} 50 | uses: actions/setup-node@v3 51 | with: 52 | node-version: ${{ matrix.node-version }} 53 | if: matrix.os == 'windows-latest' 54 | 55 | - run: npm install 56 | env: 57 | NPM_CONFIG_STRICT_SSL: false 58 | if: matrix.os == 'windows-latest' 59 | 60 | - run: npm run tests-only 61 | - uses: codecov/codecov-action@v3 62 | 63 | nonlatest: 64 | needs: [matrix, latest] 65 | name: 'non-latest majors' 66 | continue-on-error: true 67 | if: ${{ needs.matrix.outputs.nonlatest != '[]' && (!github.head_ref || !startsWith(github.head_ref, 'renovate')) }} 68 | strategy: 69 | fail-fast: false 70 | matrix: 71 | os: [windows-latest, macos-latest] 72 | node-version: ${{ fromJson(needs.matrix.outputs.nonlatest) }} 73 | exclude: 74 | - os: windows-latest 75 | node-version: '0.8' 76 | 77 | runs-on: ${{matrix.os}} 78 | 79 | steps: 80 | - uses: actions/checkout@v3 81 | 82 | - uses: ljharb/actions/node/install@main 83 | name: 'nvm install ${{ matrix.node-version }} && npm install' 84 | with: 85 | node-version: ${{ matrix.node-version }} 86 | skip-ls-check: true 87 | if: matrix.os != 'windows-latest' 88 | 89 | - name: Use Node.js ${{ matrix.node-version }} 90 | uses: actions/setup-node@v3 91 | with: 92 | node-version: ${{ matrix.node-version }} 93 | if: matrix.os == 'windows-latest' 94 | 95 | - run: npm install 96 | env: 97 | NPM_CONFIG_STRICT_SSL: false 98 | if: matrix.os == 'windows-latest' 99 | 100 | - run: npm run tests-only 101 | - uses: codecov/codecov-action@v3 102 | 103 | node: 104 | name: 'node majors, windows/mac' 105 | needs: [latest, nonlatest] 106 | runs-on: ubuntu-latest 107 | steps: 108 | - run: 'echo tests completed' 109 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bench/src/jquery.js 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bench/src/jquery.js 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # detective Change Log 2 | All notable changes to this project will be documented in this file. 3 | This project adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | ## x.y.z - unreleased 6 | * update acorn-node to 1.8.2 (make use of acorn 7.x) 7 | 8 | ## 5.2.1 - 2022-05-27 9 | * Update to "minimist": "^1.2.6" from "^1.1.1" to quiet down dependabot security theater. 10 | 11 | ## 5.2.0 - 2019-01-28 12 | * Use acorn-node's option defaults, adds support for new ES features (https://github.com/browserify/detective/pull/81) 13 | 14 | ## 5.1.0 - 2018-02-28 15 | * Use acorn-node parser, which matches latest Node syntax support (https://github.com/browserify/detective/pull/78) 16 | * Add basic cli: `detective index.js` outputs dependency names (https://github.com/browserify/detective/pull/51) 17 | 18 | ## 5.0.2 - 2018-01-06 19 | * Extend support back to 0.8 until we can determine a LTS plan. 20 | 21 | ## 5.0.1 - 2018-01-02 22 | * Add engines field set to `>=4.0.0`. 23 | 24 | ## 5.0.0 - 2018-01-02 25 | * Fix: Don't crash on files with the spread operator (https://github.com/browserify/detective/pull/75) 26 | * Breaking: Drop support for node 0.12 (https://github.com/browserify/detective/pull/75) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is released under the MIT license: 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | 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, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /bench/detect.js: -------------------------------------------------------------------------------- 1 | var detective = require('../'); 2 | var fs = require('fs'); 3 | 4 | var src = fs.readFileSync(__dirname + '/src/jquery.js', 'utf8'); 5 | var t0 = Date.now(); 6 | var requires = detective(src); 7 | console.log(Date.now() - t0); 8 | -------------------------------------------------------------------------------- /bench/esprima_v_acorn.txt: -------------------------------------------------------------------------------- 1 | esprima: 2 | 3 | $ for i in {1..5}; do node detect.js; done 4 | 704 5 | 702 6 | 704 7 | 704 8 | 697 9 | 10 | acorn: 11 | 12 | $ for i in {1..5}; do node detect.js; done 13 | 555 14 | 552 15 | 585 16 | 549 17 | 583 18 | 19 | -------------------------------------------------------------------------------- /bin/detective.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var detective = require('../'); 4 | var argv = require('minimist')(process.argv.slice(2)); 5 | var fs = require('fs'); 6 | 7 | argv._.forEach(function(file) { 8 | var src = fs.readFileSync(file, 'utf8'); 9 | var requires = detective(src, argv); 10 | console.log(requires.join('\n')); 11 | }); 12 | -------------------------------------------------------------------------------- /example/strings.js: -------------------------------------------------------------------------------- 1 | var detective = require('../'); 2 | var fs = require('fs'); 3 | 4 | var src = fs.readFileSync(__dirname + '/strings_src.js'); 5 | var requires = detective(src); 6 | console.dir(requires); 7 | -------------------------------------------------------------------------------- /example/strings_src.js: -------------------------------------------------------------------------------- 1 | var a = require('a'); 2 | var b = require('b'); 3 | var c = require('c'); 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var acorn = require('acorn-node'); 2 | var walk = require('acorn-node/walk'); 3 | var defined = require('defined'); 4 | 5 | var requireRe = /\brequire\b/; 6 | 7 | function parse (src, opts) { 8 | if (!opts) opts = {}; 9 | var acornOpts = { 10 | ranges: defined(opts.ranges, opts.range), 11 | locations: defined(opts.locations, opts.loc), 12 | allowReserved: defined(opts.allowReserved, true), 13 | allowImportExportEverywhere: defined(opts.allowImportExportEverywhere, false) 14 | }; 15 | 16 | // Use acorn-node's defaults for the rest. 17 | if (opts.ecmaVersion != null) acornOpts.ecmaVersion = opts.ecmaVersion; 18 | if (opts.sourceType != null) acornOpts.sourceType = opts.sourceType; 19 | if (opts.allowHashBang != null) acornOpts.allowHashBang = opts.allowHashBang; 20 | if (opts.allowReturnOutsideFunction != null) acornOpts.allowReturnOutsideFunction = opts.allowReturnOutsideFunction; 21 | 22 | return acorn.parse(src, acornOpts); 23 | } 24 | 25 | var exports = module.exports = function (src, opts) { 26 | return exports.find(src, opts).strings; 27 | }; 28 | 29 | exports.find = function (src, opts) { 30 | if (!opts) opts = {}; 31 | 32 | var word = opts.word === undefined ? 'require' : opts.word; 33 | if (typeof src !== 'string') src = String(src); 34 | 35 | var isRequire = opts.isRequire || function (node) { 36 | return node.callee.type === 'Identifier' 37 | && node.callee.name === word 38 | ; 39 | }; 40 | 41 | var modules = { strings : [], expressions : [] }; 42 | if (opts.nodes) modules.nodes = []; 43 | 44 | var wordRe = word === 'require' ? requireRe : RegExp('\\b' + word + '\\b'); 45 | if (!wordRe.test(src)) return modules; 46 | 47 | var ast = parse(src, opts.parse); 48 | 49 | function visit(node, st, c) { 50 | var hasRequire = wordRe.test(src.slice(node.start, node.end)); 51 | if (!hasRequire) return; 52 | walk.base[node.type](node, st, c); 53 | if (node.type !== 'CallExpression') return; 54 | if (isRequire(node)) { 55 | if (node.arguments.length) { 56 | var arg = node.arguments[0]; 57 | if (arg.type === 'Literal') { 58 | modules.strings.push(arg.value); 59 | } 60 | else if (arg.type === 'TemplateLiteral' 61 | && arg.quasis.length === 1 62 | && arg.expressions.length === 0) { 63 | 64 | modules.strings.push(arg.quasis[0].value.raw); 65 | } 66 | else { 67 | modules.expressions.push(src.slice(arg.start, arg.end)); 68 | } 69 | } 70 | if (opts.nodes) modules.nodes.push(node); 71 | } 72 | } 73 | 74 | walk.recursive(ast, null, { 75 | Statement: visit, 76 | Expression: visit 77 | }); 78 | 79 | return modules; 80 | }; 81 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "detective", 3 | "description": "find all require() calls by walking the AST", 4 | "version": "5.2.1", 5 | "author": { 6 | "name": "James Halliday", 7 | "email": "mail@substack.net", 8 | "url": "http://substack.net" 9 | }, 10 | "bin": "bin/detective.js", 11 | "dependencies": { 12 | "acorn-node": "^1.8.2", 13 | "defined": "^1.0.0", 14 | "minimist": "^1.2.6" 15 | }, 16 | "engines": { 17 | "node": ">=0.8.0" 18 | }, 19 | "keywords": [ 20 | "analyze", 21 | "ast", 22 | "require", 23 | "source" 24 | ], 25 | "license": "MIT", 26 | "main": "index.js", 27 | "repository": { 28 | "type": "git", 29 | "url": "git://github.com/browserify/detective.git" 30 | }, 31 | "scripts": { 32 | "tests-only": "tape test/*.js", 33 | "test": "npm run tests-only" 34 | }, 35 | "devDependencies": { 36 | "tape": "^5.6.1" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /readme.markdown: -------------------------------------------------------------------------------- 1 | # detective 2 | 3 | find all calls to `require()` by walking the AST 4 | 5 | [![build status](https://secure.travis-ci.org/browserify/detective.png)](http://travis-ci.org/browserify/detective) 6 | 7 | # example 8 | 9 | ## strings 10 | 11 | strings_src.js: 12 | 13 | ``` js 14 | var a = require('a'); 15 | var b = require('b'); 16 | var c = require('c'); 17 | ``` 18 | 19 | strings.js: 20 | 21 | ``` js 22 | var detective = require('detective'); 23 | var fs = require('fs'); 24 | 25 | var src = fs.readFileSync(__dirname + '/strings_src.js'); 26 | var requires = detective(src); 27 | console.dir(requires); 28 | ``` 29 | 30 | output: 31 | 32 | ``` 33 | $ node examples/strings.js 34 | [ 'a', 'b', 'c' ] 35 | ``` 36 | 37 | # methods 38 | 39 | ``` js 40 | var detective = require('detective'); 41 | ``` 42 | 43 | ## detective(src, opts) 44 | 45 | Give some source body `src`, return an array of all the `require()` calls with 46 | string arguments. 47 | 48 | The options parameter `opts` is passed along to `detective.find()`. 49 | 50 | ## var found = detective.find(src, opts) 51 | 52 | Give some source body `src`, return `found` with: 53 | 54 | * `found.strings` - an array of each string found in a `require()` 55 | * `found.expressions` - an array of each stringified expression found in a 56 | `require()` call 57 | * `found.nodes` (when `opts.nodes === true`) - an array of AST nodes for each 58 | argument found in a `require()` call 59 | 60 | Optionally: 61 | 62 | * `opts.word` - specify a different function name instead of `"require"` 63 | * `opts.nodes` - when `true`, populate `found.nodes` 64 | * `opts.isRequire(node)` - a function returning whether an AST `CallExpression` 65 | node is a require call 66 | * `opts.parse` - supply options directly to 67 | [acorn](https://npmjs.org/package/acorn) with some support for esprima-style 68 | options `range` and `loc` 69 | * `opts.ecmaVersion` - default: 9 70 | 71 | # install 72 | 73 | With [npm](https://npmjs.org) do: 74 | 75 | ``` 76 | npm install detective 77 | ``` 78 | 79 | # license 80 | 81 | MIT 82 | -------------------------------------------------------------------------------- /test/both.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/both.js'); 5 | 6 | test('both', function (t) { 7 | var modules = detective.find(src); 8 | t.deepEqual(modules.strings, [ 'a', 'b' ]); 9 | t.deepEqual(modules.expressions, [ "'c' + x", "'d' + y" ]); 10 | t.notOk(modules.nodes, 'has no nodes'); 11 | t.end(); 12 | }); 13 | 14 | test('both with nodes specified in opts', function (t) { 15 | var modules = detective.find(src, { nodes: true }); 16 | t.deepEqual(modules.strings, [ 'a', 'b' ]); 17 | t.deepEqual(modules.expressions, [ "'c' + x", "'d' + y" ]); 18 | t.deepEqual( 19 | modules.nodes.map(function (n) { 20 | var arg = n.arguments[0]; 21 | return arg.value || arg.left.value; 22 | }), 23 | [ 'a', 'b', 'c', 'd' ], 24 | 'has a node for each require'); 25 | t.end(); 26 | }); 27 | -------------------------------------------------------------------------------- /test/chained.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/chained.js'); 5 | 6 | test('chained', function (t) { 7 | t.deepEqual(detective(src), [ 'c', 'b', 'a' ]); 8 | t.end(); 9 | }); 10 | -------------------------------------------------------------------------------- /test/complicated.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | 4 | var sources = [ 5 | 'require("a")', 6 | "require('a')", 7 | 'require(`a`)', 8 | ';require("a")', 9 | ' require("a")', 10 | 'void require("a")', 11 | '+require("a")', 12 | '!require("a")', 13 | '/*comments*/require("a")', 14 | '(require("a"))', 15 | 16 | 'require/*comments*/("a")', 17 | ';require/*comments*/("a")', 18 | ' require/*comments*/("a")', 19 | 'void require/*comments*/("a")', 20 | '+require/*comments*/("a")', 21 | '!require/*comments*/("a")', 22 | '/*comments*/require/*comments*/("a")', 23 | '(require/*comments*/("a"))', 24 | 25 | 'require /*comments*/ ("a")', 26 | ';require /*comments*/ ("a")', 27 | ' require /*comments*/ ("a")', 28 | 'void require /*comments*/ ("a")', 29 | '+require /*comments*/ ("a")', 30 | '!require /*comments*/ ("a")', 31 | ' /*comments*/ require /*comments*/ ("a")', 32 | '(require /*comments*/ ("a"))', 33 | 34 | 'require /*comments*/ /*more comments*/ ("a")', 35 | ';require /*comments*/ /*more comments*/ ("a")', 36 | ' require /*comments*/ /*more comments*/ ("a")', 37 | 'void require /*comments*/ /*more comments*/ ("a")', 38 | '+require /*comments*/ /*more comments*/ ("a")', 39 | '!require /*comments*/ /*more comments*/ ("a")', 40 | ' /*comments*/ /*more comments*/ require /*comments*/ /*more comments*/ ("a")', 41 | '(require /*comments*/ /*more comments*/ ("a"))', 42 | 43 | 'require//comments\n("a")', 44 | ';require//comments\n("a")', 45 | ' require//comments\n("a")', 46 | 'void require//comments\n("a")', 47 | '+require//comments\n("a")', 48 | '!require//comments\n("a")', 49 | ' require//comments\n("a")', 50 | '(require//comments\n("a"))' 51 | ]; 52 | 53 | test('complicated', function (t) { 54 | t.plan(sources.length); 55 | sources.forEach(function(src) { 56 | t.deepEqual(detective(src), [ 'a' ]); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/es2019.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | 5 | test('es2019 - for-await', function (t) { 6 | var src = fs.readFileSync(__dirname + '/files/for-await.js'); 7 | t.doesNotThrow(detective.bind(detective, src), 'Files with `for await()` do not throw') 8 | t.end(); 9 | }); 10 | 11 | test('es2019 - optional-catch', function (t) { 12 | var src = fs.readFileSync(__dirname + '/files/optional-catch.js'); 13 | t.doesNotThrow(detective.bind(detective, src), 'Files with omitted catch binding do not throw') 14 | t.end(); 15 | }); 16 | -------------------------------------------------------------------------------- /test/es6-module.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/es6-module.js'); 5 | 6 | test('es6-module', function (t) { 7 | t.plan(1); 8 | t.deepEqual(detective(src, {parse: {sourceType: 'module'}}), [ 'a', 'b' ]); 9 | }); 10 | -------------------------------------------------------------------------------- /test/files/both.js: -------------------------------------------------------------------------------- 1 | require('a'); 2 | require('b'); 3 | require('c' + x); 4 | var moo = require('d' + y).moo; 5 | -------------------------------------------------------------------------------- /test/files/chained.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | require('c').hello().goodbye() 4 | require('b').hello() 5 | require('a') 6 | -------------------------------------------------------------------------------- /test/files/es6-module.js: -------------------------------------------------------------------------------- 1 | var a = require('a'); 2 | 3 | export default function () { 4 | var b = require('b'); 5 | } 6 | -------------------------------------------------------------------------------- /test/files/for-await.js: -------------------------------------------------------------------------------- 1 | async function main () { 2 | for await (const _ of (async function* () {})()) { 3 | require(_) 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/files/generators.js: -------------------------------------------------------------------------------- 1 | var a = require('a'); 2 | 3 | function *gen() { 4 | yield require('b'); 5 | } -------------------------------------------------------------------------------- /test/files/isrequire.js: -------------------------------------------------------------------------------- 1 | var a = require.async('a'); 2 | var b = require.async('b'); 3 | var c = require.async('c'); 4 | var abc = a.b(c); 5 | 6 | var EventEmitter = require.async('events').EventEmitter; 7 | 8 | var x = require.async('doom')(5,6,7); 9 | x(8,9); 10 | c.load('notthis'); 11 | var y = require.async('y') * 100; 12 | 13 | var EventEmitter2 = require.async('events2').EventEmitter(); 14 | 15 | -------------------------------------------------------------------------------- /test/files/nested.js: -------------------------------------------------------------------------------- 1 | 2 | if (true) { 3 | (function () { 4 | require('a'); 5 | })(); 6 | } 7 | if (false) { 8 | (function () { 9 | var x = 10; 10 | switch (x) { 11 | case 1 : require('b'); break; 12 | default : break; 13 | } 14 | })() 15 | } 16 | 17 | function qqq () { 18 | require 19 | ( 20 | "c" 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /test/files/optional-catch.js: -------------------------------------------------------------------------------- 1 | try { 2 | require; 3 | } catch { 4 | } 5 | -------------------------------------------------------------------------------- /test/files/rest-spread.js: -------------------------------------------------------------------------------- 1 | var a = require('a'); 2 | var b = require('b'); 3 | var c = require('c'); 4 | 5 | 6 | var obj = { foo: 'bar', bee: 'bop' } 7 | var spread = { ...obj } 8 | var { foo, ...rest } = obj 9 | 10 | -------------------------------------------------------------------------------- /test/files/set-in-object-pattern.js: -------------------------------------------------------------------------------- 1 | var a = load('a'); 2 | var b = load('b'); 3 | var c = load('c'); 4 | var abc = a.b(c); 5 | 6 | function load2({set = 'hello'}) { 7 | return load('tt'); 8 | } 9 | 10 | var loadUse = load2(); 11 | -------------------------------------------------------------------------------- /test/files/shebang.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var a = require('a'); 4 | var b = require('b'); 5 | var c = require('c'); 6 | -------------------------------------------------------------------------------- /test/files/sparse-array.js: -------------------------------------------------------------------------------- 1 | var o = [,,,,] 2 | 3 | require('./foo') 4 | -------------------------------------------------------------------------------- /test/files/strings.js: -------------------------------------------------------------------------------- 1 | var a = require('a'); 2 | var b = require('b'); 3 | var c = require('c'); 4 | var abc = a.b(c); 5 | 6 | var EventEmitter = require('events').EventEmitter; 7 | 8 | var x = require('doom')(5,6,7); 9 | x(8,9); 10 | c.require('notthis'); 11 | var y = require('y') * 100; 12 | 13 | var EventEmitter2 = require('events2').EventEmitter(); -------------------------------------------------------------------------------- /test/files/word.js: -------------------------------------------------------------------------------- 1 | var a = load('a'); 2 | var b = load('b'); 3 | var c = load('c'); 4 | var abc = a.b(c); 5 | 6 | var EventEmitter = load('events').EventEmitter; 7 | 8 | var x = load('doom')(5,6,7); 9 | x(8,9); 10 | c.load('notthis'); 11 | var y = load('y') * 100; 12 | 13 | var EventEmitter2 = load('events2').EventEmitter(); 14 | -------------------------------------------------------------------------------- /test/files/yield.js: -------------------------------------------------------------------------------- 1 | (function * () { 2 | var a = require('a'); 3 | var b = yield require('c')(a); 4 | })(); 5 | -------------------------------------------------------------------------------- /test/generators.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/generators.js'); 5 | 6 | test('generators', function (t) { 7 | t.plan(1); 8 | t.deepEqual(detective(src), [ 'a', 'b' ]); 9 | }); 10 | -------------------------------------------------------------------------------- /test/isrequire.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/isrequire.js'); 5 | 6 | test('word', function (t) { 7 | t.deepEqual( 8 | detective(src, { isRequire: function(node) { 9 | return (node.type === 'CallExpression' && 10 | node.callee.type === 'MemberExpression' && 11 | node.callee.object.type == 'Identifier' && 12 | node.callee.object.name == 'require' && 13 | node.callee.property.type == 'Identifier' && 14 | node.callee.property.name == 'async') 15 | } }), 16 | [ 'a', 'b', 'c', 'events', 'doom', 'y', 'events2' ] 17 | ); 18 | t.end(); 19 | }); 20 | 21 | -------------------------------------------------------------------------------- /test/nested.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/nested.js'); 5 | 6 | test('nested', function (t) { 7 | t.deepEqual(detective(src), [ 'a', 'b', 'c' ]); 8 | t.end(); 9 | }); 10 | -------------------------------------------------------------------------------- /test/noargs.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | 5 | // in order to use detective to find any function 6 | // it needs to properly handle functions called without args 7 | var src = [ 'fn();', 'otherfn();', 'fn();' ].join('\n') 8 | 9 | test('noargs', function (t) { 10 | t.plan(1); 11 | t.deepEqual(detective(src, { word: 'fn' }).length, 0, 'finds no arg id'); 12 | }); 13 | 14 | test('find noargs with nodes', function (t) { 15 | t.plan(4); 16 | var modules = detective.find(src, { word: 'fn', nodes: true }); 17 | t.equal(modules.strings.length, 0, 'finds no arg id'); 18 | t.equal(modules.expressions.length, 0, 'finds no expressions'); 19 | t.equal(modules.nodes.length, 2, 'finds a node for each matching function call'); 20 | t.equal( 21 | modules.nodes.filter(function (x) { 22 | return x.callee.name === 'fn' 23 | }).length, 2, 24 | 'all matches are correct' 25 | ); 26 | }); 27 | -------------------------------------------------------------------------------- /test/parseopts.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/both.js'); 5 | 6 | test('nodes specified in opts and parseopts { range: true }', function (t) { 7 | var modules = detective.find(src, { nodes: true, parse: { range: true } }); 8 | t.deepEqual(modules.strings, [ 'a', 'b' ]); 9 | t.deepEqual(modules.expressions, [ "'c' + x", "'d' + y" ]); 10 | t.deepEqual( 11 | modules.nodes.map(function (n) { 12 | var arg = n.arguments[0]; 13 | return arg.value || arg.left.value; 14 | }), 15 | [ 'a', 'b', 'c', 'd' ], 16 | 'has a node for each require'); 17 | 18 | var range = modules.nodes[0].range; 19 | t.equal(range[0], 0, 'includes range start'); 20 | t.equal(range[1], 12, 'includes range end'); 21 | t.end(); 22 | }); 23 | 24 | test('nodes specified in opts and parseopts { range: false }', function (t) { 25 | var modules = detective.find(src, { nodes: true, parse: { range: false } }); 26 | t.deepEqual(modules.strings, [ 'a', 'b' ]); 27 | t.deepEqual(modules.expressions, [ "'c' + x", "'d' + y" ]); 28 | t.deepEqual( 29 | modules.nodes.map(function (n) { 30 | var arg = n.arguments[0]; 31 | return arg.value || arg.left.value; 32 | }), 33 | [ 'a', 'b', 'c', 'd' ], 34 | 'has a node for each require'); 35 | 36 | t.notOk(modules.nodes[0].range, 'includes no ranges'); 37 | t.end(); 38 | }); 39 | 40 | test('nodes specified in opts and parseopts { range: true, loc: true }', function (t) { 41 | var modules = detective.find(src, { nodes: true, parse: { range: true, loc: true } }); 42 | t.deepEqual(modules.strings, [ 'a', 'b' ]); 43 | t.deepEqual(modules.expressions, [ "'c' + x", "'d' + y" ]); 44 | t.deepEqual( 45 | modules.nodes.map(function (n) { 46 | var arg = n.arguments[0]; 47 | return arg.value || arg.left.value; 48 | }), 49 | [ 'a', 'b', 'c', 'd' ], 50 | 'has a node for each require'); 51 | 52 | var range = modules.nodes[0].range; 53 | t.equal(range[0], 0, 'includes range start'); 54 | t.equal(range[1], 12, 'includes range end'); 55 | 56 | var loc = modules.nodes[0].loc; 57 | t.equal(loc.start.line, 1, 'includes start line'); 58 | t.equal(loc.start.column, 0, 'includes start column'); 59 | t.equal(loc.end.line, 1, 'includes end line'); 60 | t.equal(loc.end.column, 12, 'includes end column'); 61 | t.end(); 62 | }); 63 | -------------------------------------------------------------------------------- /test/rest-spread.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/rest-spread.js'); 5 | 6 | test('rest-spread', function (t) { 7 | t.doesNotThrow(detective.bind(detective, src), 'Files with rest or spread do not throw') 8 | t.end(); 9 | }); 10 | -------------------------------------------------------------------------------- /test/return.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = [ 'require("a")\nreturn' ]; 5 | 6 | test('return', function (t) { 7 | t.plan(1); 8 | t.deepEqual(detective(src), [ 'a' ]); 9 | }); 10 | -------------------------------------------------------------------------------- /test/set-in-object-pattern.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/set-in-object-pattern.js'); 5 | 6 | test('set in object pattern', function (t) { 7 | t.deepEqual( 8 | detective(src, { word : 'load' }), 9 | [ 'a', 'b', 'c', 'tt' ] 10 | ); 11 | t.end(); 12 | }); 13 | -------------------------------------------------------------------------------- /test/shebang.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/shebang.js'); 5 | 6 | test('shebang', function (t) { 7 | t.plan(1); 8 | t.deepEqual(detective(src), [ 'a', 'b', 'c' ]); 9 | }); 10 | -------------------------------------------------------------------------------- /test/sparse-array.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/sparse-array.js'); 5 | 6 | test('sparse-array', function (t) { 7 | //just check that this does not crash. 8 | t.doesNotThrow(function () { 9 | detective(src) 10 | }) 11 | t.end(); 12 | }); 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/strings.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/strings.js'); 5 | 6 | test('single', function (t) { 7 | t.deepEqual(detective(src), [ 'a', 'b', 'c', 'events', 'doom', 'y', 'events2' ]); 8 | t.end(); 9 | }); 10 | -------------------------------------------------------------------------------- /test/word.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/word.js'); 5 | 6 | test('word', function (t) { 7 | t.deepEqual( 8 | detective(src, { word : 'load' }), 9 | [ 'a', 'b', 'c', 'events', 'doom', 'y', 'events2' ] 10 | ); 11 | t.end(); 12 | }); 13 | -------------------------------------------------------------------------------- /test/yield.js: -------------------------------------------------------------------------------- 1 | var test = require('tape'); 2 | var detective = require('../'); 3 | var fs = require('fs'); 4 | var src = fs.readFileSync(__dirname + '/files/yield.js'); 5 | 6 | test('yield', function (t) { 7 | t.plan(1); 8 | t.deepEqual(detective(src), [ 'a', 'c' ]); 9 | }); 10 | --------------------------------------------------------------------------------