├── .gitignore ├── .npmignore ├── test ├── fixtures │ └── module.js └── index.js ├── LICENSE.md ├── package.json ├── index.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | test 7 | test.js 8 | demo/ 9 | .npmignore 10 | LICENSE.md -------------------------------------------------------------------------------- /test/fixtures/module.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var foo1 = require('object-assign') 3 | var another = require('object-assign').another 4 | import { foo } from './foo' 5 | import { foo as blah } from './blah' 6 | import * as _ from 'lodash' 7 | import defs from 'defaults' 8 | import 'side-effects' 9 | // import 'commented' 10 | /* import { foo } from 'commented2'; */ 11 | // require('commented3') 12 | require(__dirname + '/file.js') 13 | require(path.join(__dirname, '/file.js')) 14 | 15 | export default function () { 16 | var b = require('b') 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2015 Jam3 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "detect-import-require", 3 | "version": "2.0.0", 4 | "description": "list require and import paths from a JavaScript source", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Matt DesLauriers", 9 | "email": "dave.des@gmail.com", 10 | "url": "https://github.com/mattdesl" 11 | }, 12 | "standard": { 13 | "ignore": [ 14 | "test/fixtures/**/*.js" 15 | ] 16 | }, 17 | "dependencies": { 18 | "acorn": "^3.2.0", 19 | "ast-types": "^0.8.16", 20 | "defined": "^1.0.0", 21 | "escodegen": "^1.7.0", 22 | "is-buffer": "^1.1.3" 23 | }, 24 | "devDependencies": { 25 | "acorn-jsx": "^3.0.1", 26 | "faucet": "0.0.1", 27 | "standard": "^5.4.1", 28 | "tape": "^4.2.2" 29 | }, 30 | "scripts": { 31 | "test": "standard && node test/index.js | faucet" 32 | }, 33 | "keywords": [ 34 | "import", 35 | "cjs", 36 | "common", 37 | "js", 38 | "commonjs", 39 | "imports", 40 | "require", 41 | "requires", 42 | "detective", 43 | "node", 44 | "ast", 45 | "walk", 46 | "find", 47 | "module", 48 | "modules" 49 | ], 50 | "repository": { 51 | "type": "git", 52 | "url": "git://github.com/Jam3/detect-import-require.git" 53 | }, 54 | "homepage": "https://github.com/Jam3/detect-import-require", 55 | "bugs": { 56 | "url": "https://github.com/Jam3/detect-import-require/issues" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var detect = require('../') 2 | var test = require('tape') 3 | var fs = require('fs') 4 | var path = require('path') 5 | 6 | var filename = path.resolve(__dirname, 'fixtures', 'module.js') 7 | var src = fs.readFileSync(filename, 'utf8') 8 | 9 | test('like the detective module, but for CommonJS + imports', function (t) { 10 | t.deepEqual(detect(src), [ 11 | 'path', 'object-assign', 'object-assign', 12 | './foo', './blah', 'lodash', 'defaults', 13 | 'side-effects', 'b' 14 | ]) 15 | 16 | t.deepEqual(detect(src, { imports: false }), [ 17 | 'path', 'object-assign', 'object-assign', 'b' 18 | ]) 19 | 20 | t.deepEqual(detect(src, { imports: false, requires: false }), []) 21 | 22 | t.deepEqual(detect(src, { imports: true, requires: false }), [ 23 | './foo', './blah', 'lodash', 'defaults', 'side-effects' 24 | ]) 25 | t.end() 26 | }) 27 | 28 | test('advanced mode', function (t) { 29 | t.deepEqual(detect.find(src).expressions, [ 30 | "__dirname + '/file.js'", 31 | "path.join(__dirname, '/file.js')" 32 | ]) 33 | 34 | t.deepEqual(detect.find(src).strings, [ 35 | 'path', 'object-assign', 'object-assign', 36 | './foo', './blah', 'lodash', 'defaults', 37 | 'side-effects', 'b' 38 | ]) 39 | 40 | t.deepEqual(detect.find(src, { requires: false }).expressions, []) 41 | t.end() 42 | }) 43 | 44 | test('empty file', function (t) { 45 | t.deepEqual(detect(''), []) 46 | t.end() 47 | }) 48 | 49 | test('supports Buffer type', function (t) { 50 | var buf = fs.readFileSync(filename) 51 | t.deepEqual(detect(buf), [ 52 | 'path', 'object-assign', 'object-assign', 53 | './foo', './blah', 'lodash', 'defaults', 54 | 'side-effects', 'b' 55 | ]) 56 | t.end() 57 | }) 58 | 59 | test('supports custom AST input', function (t) { 60 | var jsx = "import 'foo'; ReactDOM.render(

Hello World

, document.getElementById('root'));" 61 | var acorn = require('acorn-jsx/inject')(require('acorn')) 62 | var ast = acorn.parse(jsx, { 63 | ecmaVersion: 6, 64 | sourceType: 'module', 65 | allowReserved: true, 66 | allowReturnOutsideFunction: true, 67 | allowHashBang: true, 68 | plugins: { 69 | jsx: true 70 | } 71 | }) 72 | t.deepEqual(detect(ast), [ 73 | 'foo' 74 | ]) 75 | t.end() 76 | }) 77 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var acorn = require('acorn') 2 | var escodegen = require('escodegen') 3 | var defined = require('defined') 4 | var isBuffer = require('is-buffer') 5 | var types = require('ast-types') 6 | 7 | var regexRequire = /\brequire\b/ 8 | var regexImport = /\bimport\b/ 9 | var regexBoth = /\b(import|require)\b/ 10 | 11 | module.exports = detectImportRequire 12 | function detectImportRequire (src, opts) { 13 | return findImportRequire(src, opts).strings 14 | } 15 | 16 | module.exports.find = findImportRequire 17 | function findImportRequire (src, opts) { 18 | opts = opts || {} 19 | 20 | // allow Node Buffer 21 | if (isBuffer(src)) { 22 | src = src.toString() 23 | } 24 | 25 | if (typeof src !== 'string' && !src) { 26 | throw new Error('src option must be a string, Buffer or AST') 27 | } 28 | 29 | var imports = defined(opts.imports, true) 30 | var requires = defined(opts.requires, true) 31 | 32 | var results = { 33 | strings: [], 34 | expressions: [], 35 | nodes: [] 36 | } 37 | 38 | var ast 39 | if (typeof src === 'string') { 40 | // quick regex test before we parse entire AST 41 | src = (src || '') 42 | var regex = regexBoth 43 | if (imports && !requires) regex = regexImport 44 | else if (requires && !imports) regex = regexRequire 45 | if (!regex.test(src)) { 46 | return results 47 | } 48 | 49 | // now parse 50 | ast = acorn.parse(src, { 51 | ecmaVersion: 6, 52 | sourceType: 'module', 53 | allowReserved: true, 54 | allowReturnOutsideFunction: true, 55 | allowHashBang: true 56 | }) 57 | } else { 58 | // assume ast is given 59 | ast = src 60 | } 61 | 62 | var importDeclaration, callExpression 63 | if (imports) { 64 | importDeclaration = function (path) { 65 | var node = path.node 66 | if (node.source.type === 'Literal') { 67 | results.strings.push(node.source.value) 68 | } 69 | results.nodes.push(node) 70 | this.traverse(path) 71 | } 72 | } 73 | 74 | if (requires) { 75 | callExpression = function (path) { 76 | var node = path.node 77 | if (!isRequire(node)) return false 78 | if (node.arguments.length) { 79 | if (node.arguments[0].type === 'Literal') { 80 | results.strings.push(node.arguments[0].value) 81 | } else { 82 | results.expressions.push(escodegen.generate(node.arguments[0])) 83 | } 84 | } 85 | results.nodes.push(node) 86 | this.traverse(path) 87 | } 88 | } 89 | 90 | types.visit(ast, { 91 | visitImportDeclaration: importDeclaration, 92 | visitCallExpression: callExpression 93 | }) 94 | 95 | return results 96 | } 97 | 98 | function isRequire (node) { 99 | return node.callee.type === 'Identifier' && 100 | node.callee.name === 'require' 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # detect-import-require 2 | 3 | [![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) 4 | 5 | This is like [detective](https://www.npmjs.com/package/detective), but with a narrow focus and thin API, specifically aimed at supporting either `import` and/or `require` statements, and not much more. 6 | 7 | ## Install 8 | 9 | ```sh 10 | npm install detect-import-require --save 11 | ``` 12 | 13 | ## Example 14 | 15 | Given the following file: 16 | 17 | `source.js` 18 | ```js 19 | var foo = require('a').foo 20 | var bar = require('./blah.js') 21 | import { uniq } from 'lodash' 22 | import { resolve } from 'path' 23 | ``` 24 | 25 | ```js 26 | var fs = require('fs') 27 | var detect = require('detect-import-require') 28 | 29 | var src = fs.readFileSync('source.js', 'utf8') 30 | console.log(detect(src)) 31 | //=> [ 'a', './blah.js', 'lodash', 'path' ] 32 | ``` 33 | 34 | See [Custom AST](#custom-ast) for details on parsing additional language syntax, such as JSX or async/await. 35 | 36 | ## Usage 37 | 38 | [![NPM](https://nodei.co/npm/detect-import-require.png)](https://www.npmjs.com/package/detect-import-require) 39 | 40 | #### `modules = detect(src, [opt])` 41 | 42 | Returns an array of module names (require paths) from the given `src` String, Buffer or AST. By default, looks for `import` and `require` statements. Results are not de-duplicated, and are in the order they are found. 43 | 44 | Options: 45 | 46 | - `imports` (Boolean) - whether to look for `import` statements, default true 47 | - `requires` (Boolean) - whether to look for `require` statements, default true 48 | 49 | #### `modules = detect.find(src, [opt])` 50 | 51 | Takes the same options as above, but returns an object with the following additional data: 52 | 53 | ```js 54 | { 55 | strings: [], 56 | expressions: [], 57 | nodes: [] 58 | } 59 | ``` 60 | 61 | Where `strings` is the array of module names, `expressions` is an array of expressions from dynamic `require()` statements, and `nodes` is an array of AST nodes for each found require/import statement. 62 | 63 | Expressions do not appear in imports, and look like this: 64 | 65 | ```js 66 | [ 67 | "path.join(__dirname, '/foo.js')", 68 | "__dirname + '/file.js'" 69 | ] 70 | ``` 71 | 72 | ## Custom AST 73 | 74 | You can also pass a parsed AST, e.g. if you have a special build of acorn or want full control over parsing options. Here is an example with JSX: 75 | 76 | ```js 77 | var detect = require('detect-import-require') 78 | var acorn = require('acorn-jsx'); 79 | var jsx = [ 80 | "import 'foo';", 81 | "ReactDOM.render(", 82 | "

Hello World

,", 83 | "document.getElementById('root')", 84 | ");" 85 | ].join('\n'); 86 | 87 | var ast = acorn.parse(jsx, { 88 | ecmaVersion: 6, 89 | sourceType: 'module', 90 | allowReserved: true, 91 | allowReturnOutsideFunction: true, 92 | allowHashBang: true, 93 | plugins: { 94 | jsx: true 95 | } 96 | }) 97 | 98 | detect(ast) 99 | // => [ 'foo' ] 100 | ``` 101 | 102 | ## License 103 | 104 | MIT, see [LICENSE.md](http://github.com/Jam3/detect-import-require/blob/master/LICENSE.md) for details. 105 | --------------------------------------------------------------------------------