├── .c8rc.json ├── .editorconfig ├── .gitattributes ├── .github ├── codeql │ └── codeql-config.yml ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── codeql.yml │ └── lint.yml ├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package-lock.json ├── package.json └── test ├── amd-modules.test.js ├── es6.test.js ├── is-define-amd.test.js ├── is-define.test.js ├── is-exports.test.js ├── is-require.test.js └── utils.js /.c8rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "reporter": [ 3 | "html", 4 | "lcovonly", 5 | "text" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 2 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.github/codeql/codeql-config.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL config" 2 | paths-ignore: 3 | - "test/fixtures/**" 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | env: 11 | FORCE_COLOR: 2 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | test: 18 | name: Node ${{ matrix.node }} on ${{ matrix.os }} 19 | runs-on: ${{ matrix.os }} 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | os: [ubuntu-latest, windows-latest] 25 | node: [18, 20, 22] 26 | 27 | steps: 28 | - name: Clone repository 29 | uses: actions/checkout@v4 30 | with: 31 | persist-credentials: false 32 | 33 | - name: Set up Node.js 34 | uses: actions/setup-node@v4 35 | with: 36 | node-version: ${{ matrix.node }} 37 | cache: npm 38 | 39 | - name: Install npm dependencies 40 | run: npm ci 41 | 42 | - name: Run unit tests 43 | run: npm run test:ci 44 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | schedule: 11 | - cron: "0 0 * * 0" 12 | workflow_dispatch: 13 | 14 | jobs: 15 | analyze: 16 | name: Analyze 17 | runs-on: ubuntu-latest 18 | permissions: 19 | actions: read 20 | contents: read 21 | security-events: write 22 | 23 | steps: 24 | - name: Clone repository 25 | uses: actions/checkout@v4 26 | with: 27 | persist-credentials: false 28 | 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v3 31 | with: 32 | config-file: ./.github/codeql/codeql-config.yml 33 | languages: "javascript" 34 | queries: +security-and-quality 35 | 36 | - name: Autobuild 37 | uses: github/codeql-action/autobuild@v3 38 | 39 | - name: Perform CodeQL Analysis 40 | uses: github/codeql-action/analyze@v3 41 | with: 42 | category: "/language:javascript" 43 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | env: 11 | FORCE_COLOR: 2 12 | NODE: 20 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | lint: 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - name: Clone repository 23 | uses: actions/checkout@v4 24 | with: 25 | persist-credentials: false 26 | 27 | - name: Set up Node.js 28 | uses: actions/setup-node@v4 29 | with: 30 | node-version: ${{ env.NODE }} 31 | cache: npm 32 | 33 | - name: Install npm dependencies 34 | run: npm ci 35 | 36 | - name: Lint 37 | run: npm run lint 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage/ 2 | /node_modules/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Dependents 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ast-module-types 2 | 3 | [![CI](https://img.shields.io/github/actions/workflow/status/dependents/node-ast-module-types/ci.yml?branch=main&label=CI&logo=github)](https://github.com/dependents/node-ast-module-types/actions/workflows/ci.yml?query=branch%3Amain) 4 | [![npm version](https://img.shields.io/npm/v/ast-module-types?logo=npm&logoColor=fff)](https://www.npmjs.com/package/ast-module-types) 5 | [![npm downloads](https://img.shields.io/npm/dm/ast-module-types)](https://www.npmjs.com/package/ast-module-types) 6 | 7 | Collection of useful helper functions when trying to determine 8 | module type (CommonJS or AMD) properties of an AST node. 9 | 10 | **AST checks are based on the Esprima (Spidermonkey) format** 11 | 12 | ```sh 13 | npm install ast-module-types 14 | ``` 15 | 16 | ## API 17 | 18 | Each of these takes in a single AST node argument 19 | and returns a boolean. 20 | 21 | * `isDefineAMD`: if node matches any form of an AMD `define` function call 22 | * `isRequire`: if node matches a `require` function all (declaring a dependency) 23 | * `isTopLevelRequire`: if node matches a `require` at the very top of the file. 24 | * `isAMDDriverScriptRequire`: if node matches an AMD driver script's require call `require([deps], function)` 25 | * `isExports`: if the node matches CommonJS `module.exports` or `exports` (defining a module) 26 | 27 | Detecting the various forms of defining an AMD module 28 | 29 | * `isNamedForm`: if the node is a define call of the form: `define('name', [deps], func)` 30 | * `isDependencyForm`: if the node is a define call of the form: `define([deps], func)` 31 | * `isFactoryForm`: if the node is a define call of the form: `define(func(require))` 32 | * `isNoDependencyForm`: if the node is a define call of the form: `define({})` 33 | * `isREMForm`: if the node matches the form: `define(function(require, exports, module){});` 34 | 35 | ES6 Types 36 | 37 | *All types abide by the [EStree spec](https://github.com/estree/estree/blob/master/es2015.md)* 38 | 39 | * `isES6Import`: if the node is any of the es6 import forms 40 | * `isES6Export`: if the node is of any es6 export forms 41 | 42 | ## Usage 43 | 44 | ```js 45 | const types = require('ast-module-types'); 46 | 47 | // Assume node is some node of an AST that you parsed using esprima or esprima-fb 48 | // ... 49 | 50 | console.log(types.isDefineAMD(node)); 51 | ``` 52 | 53 | ## License 54 | 55 | [MIT](LICENSE) 56 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Deprecated 4 | // Whether or not the node represents a generic define() call 5 | // Note: this should not be used as it will have false positives. 6 | // It is mostly used to guide sniffs for other methods and will be made private eventually. 7 | module.exports.isDefine = function(node) { 8 | if (!node) return false; 9 | 10 | const c = node.callee; 11 | 12 | return c && 13 | node.type === 'CallExpression' && 14 | c.type === 'Identifier' && 15 | c.name === 'define'; 16 | }; 17 | 18 | // Whether or not the node represents any of the AMD define() forms 19 | module.exports.isDefineAMD = function(node) { 20 | if (!node) return false; 21 | 22 | const e = module.exports; 23 | 24 | return e.isNamedForm(node) || e.isDependencyForm(node) || 25 | e.isFactoryForm(node) || e.isNoDependencyForm(node) || 26 | e.isREMForm(node); 27 | }; 28 | 29 | // Whether or not the node represents a require function call 30 | module.exports.isRequire = function(node) { 31 | return this.isPlainRequire(node) || this.isMainScopedRequire(node); 32 | }; 33 | 34 | // Whether or not the node represents a plain require function call [require(...)] 35 | module.exports.isPlainRequire = function(node) { 36 | if (!node) return false; 37 | 38 | const c = node.callee; 39 | 40 | return c && 41 | node.type === 'CallExpression' && 42 | c.type === 'Identifier' && 43 | c.name === 'require'; 44 | }; 45 | 46 | // Whether or not the node represents main-scoped require function call [require.main.require(...)] 47 | module.exports.isMainScopedRequire = function(node) { 48 | if (!node) return false; 49 | 50 | const c = node.callee; 51 | 52 | return c && 53 | node.type === 'CallExpression' && 54 | c.type === 'MemberExpression' && 55 | c.object.type === 'MemberExpression' && 56 | c.object.object.type === 'Identifier' && 57 | c.object.object.name === 'require' && 58 | c.object.property.type === 'Identifier' && 59 | c.object.property.name === 'main' && 60 | c.property.type === 'Identifier' && 61 | c.property.name === 'require'; 62 | }; 63 | 64 | // Whether or not the node represents a require at the top of the module 65 | // Instead of trying to find the require then backtrack to the top, 66 | // just take the root and check its immediate child 67 | module.exports.isTopLevelRequire = function(node) { 68 | if (node.type !== 'Program' || !node.body || 69 | node.body.length === 0 || !node.body[0].expression) { 70 | return false; 71 | } 72 | 73 | return this.isRequire(node.body[0].expression); 74 | }; 75 | 76 | // Whether or not the node represents an AMD-style driver script's require 77 | // Example: require(deps, function) 78 | module.exports.isAMDDriverScriptRequire = function(node) { 79 | return this.isRequire(node) && 80 | node.arguments && 81 | node.arguments[0] && node.arguments[0].type && 82 | node.arguments[0].type === 'ArrayExpression'; 83 | }; 84 | 85 | function isExportsIdentifier(obj) { 86 | return obj.type && obj.type === 'Identifier' && obj.name === 'exports'; 87 | } 88 | 89 | function isModuleIdentifier(obj) { 90 | return obj.type && obj.type === 'Identifier' && obj.name === 'module'; 91 | } 92 | 93 | // module.exports.foo 94 | function isModuleExportsAttach(node) { 95 | if (!node.object || !node.object.object || !node.object.property) return false; 96 | 97 | return node.type === 'MemberExpression' && 98 | isModuleIdentifier(node.object.object) && 99 | isExportsIdentifier(node.object.property); 100 | } 101 | 102 | // module.exports 103 | function isModuleExportsAssign(node) { 104 | if (!node.object || !node.property) return false; 105 | 106 | return node.type === 'MemberExpression' && 107 | isModuleIdentifier(node.object) && 108 | isExportsIdentifier(node.property); 109 | } 110 | 111 | // exports 112 | function isExportsAssign(node) { 113 | return isExportsIdentifier(node); 114 | } 115 | 116 | // exports.foo 117 | function isExportsAttach(node) { 118 | return node.type === 'MemberExpression' && isExportsIdentifier(node.object); 119 | } 120 | 121 | // Whether or not the node represents the use of 122 | // assigning (and possibly attaching) something to module.exports or exports 123 | module.exports.isExports = function(node) { 124 | if (node.type !== 'AssignmentExpression') return; 125 | 126 | // Only the left side matters 127 | const leftNode = node.left; 128 | 129 | return isModuleExportsAttach(leftNode) || isModuleExportsAssign(leftNode) || 130 | isExportsAttach(leftNode) || isExportsAssign(leftNode); 131 | }; 132 | 133 | // define('name', [deps], func) 134 | module.exports.isNamedForm = function(node) { 135 | if (!this.isDefine(node)) return false; 136 | 137 | const args = node.arguments; 138 | 139 | return args && args.length === 3 && 140 | (args[0].type === 'Literal' || args[0].type === 'StringLiteral') && 141 | args[1].type === 'ArrayExpression' && 142 | args[2].type === 'FunctionExpression'; 143 | }; 144 | 145 | // define([deps], func) 146 | module.exports.isDependencyForm = function(node) { 147 | if (!this.isDefine(node)) return false; 148 | 149 | const args = node.arguments; 150 | 151 | return args && args.length === 2 && 152 | args[0].type === 'ArrayExpression' && 153 | args[1].type === 'FunctionExpression'; 154 | }; 155 | 156 | // define(func(require)) 157 | module.exports.isFactoryForm = function(node) { 158 | if (!this.isDefine(node)) return false; 159 | 160 | const args = node.arguments; 161 | const firstParamNode = args.length > 0 && args[0].params ? args[0].params[0] : null; 162 | 163 | // Node should have a function whose first param is 'require' 164 | return args && args.length === 1 && 165 | args[0].type === 'FunctionExpression' && 166 | firstParamNode && firstParamNode.type === 'Identifier' && 167 | firstParamNode.name === 'require'; 168 | }; 169 | 170 | // define({}) 171 | module.exports.isNoDependencyForm = function(node) { 172 | if (!this.isDefine(node)) return false; 173 | 174 | const args = node.arguments; 175 | 176 | return args && args.length === 1 && args[0].type === 'ObjectExpression'; 177 | }; 178 | 179 | // define(function(require, exports, module) 180 | module.exports.isREMForm = function(node) { 181 | if (!this.isDefine(node)) return false; 182 | 183 | const args = node.arguments; 184 | const params = args.length > 0 ? args[0].params : null; 185 | 186 | if (!args || args.length === 0 || args[0].type !== 'FunctionExpression' || params.length !== 3) { 187 | return false; 188 | } 189 | 190 | const [first, second, third] = params; 191 | 192 | return first.type === 'Identifier' && first.name === 'require' && 193 | second.type === 'Identifier' && second.name === 'exports' && 194 | third.type === 'Identifier' && third.name === 'module'; 195 | }; 196 | 197 | module.exports.isES6Import = function(node) { 198 | switch (node.type) { 199 | case 'Import': 200 | case 'ImportDeclaration': 201 | case 'ImportDefaultSpecifier': 202 | case 'ImportNamespaceSpecifier': { 203 | return true; 204 | } 205 | 206 | default: { 207 | return false; 208 | } 209 | } 210 | }; 211 | 212 | module.exports.isES6Export = function(node) { 213 | switch (node.type) { 214 | case 'ExportDeclaration': 215 | case 'ExportNamedDeclaration': 216 | case 'ExportSpecifier': 217 | case 'ExportDefaultDeclaration': 218 | case 'ExportAllDeclaration': { 219 | return true; 220 | } 221 | 222 | default: { 223 | return false; 224 | } 225 | } 226 | }; 227 | 228 | module.exports.isDynamicImport = function(node) { 229 | return node.callee && node.callee.type === 'Import' && node.arguments.length > 0; 230 | }; 231 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ast-module-types", 3 | "version": "6.0.1", 4 | "description": "Collection of useful helper functions when trying to determine module type (CommonJS or AMD) properties of an AST node.", 5 | "main": "index.js", 6 | "files": [ 7 | "index.js" 8 | ], 9 | "scripts": { 10 | "lint": "xo", 11 | "fix": "xo --fix", 12 | "uvu": "uvu test -i fixtures -i utils", 13 | "test": "npm run lint && npm run uvu", 14 | "test:ci": "c8 npm run uvu" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/dependents/node-ast-module-types.git" 19 | }, 20 | "keywords": [ 21 | "esprima", 22 | "module", 23 | "type", 24 | "define", 25 | "require", 26 | "factory" 27 | ], 28 | "author": "Joel Kemp (https://mrjoelkemp.com/)", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/dependents/node-ast-module-types/issues" 32 | }, 33 | "homepage": "https://github.com/dependents/node-ast-module-types", 34 | "engines": { 35 | "node": ">=18" 36 | }, 37 | "devDependencies": { 38 | "c8": "^10.1.3", 39 | "node-source-walk": "^7.0.1", 40 | "uvu": "^0.5.6", 41 | "xo": "^0.60.0" 42 | }, 43 | "xo": { 44 | "space": true, 45 | "ignores": [ 46 | "test/fixtures/*" 47 | ], 48 | "rules": { 49 | "arrow-body-style": "off", 50 | "capitalized-comments": "off", 51 | "comma-dangle": [ 52 | "error", 53 | "never" 54 | ], 55 | "curly": [ 56 | "error", 57 | "multi-line" 58 | ], 59 | "operator-linebreak": [ 60 | "error", 61 | "after" 62 | ], 63 | "object-curly-spacing": [ 64 | "error", 65 | "always" 66 | ], 67 | "space-before-function-paren": [ 68 | "error", 69 | "never" 70 | ], 71 | "unicorn/no-anonymous-default-export": "off", 72 | "unicorn/prefer-module": "off", 73 | "unicorn/prefer-node-protocol": "off", 74 | "unicorn/prefer-top-level-await": "off", 75 | "unicorn/prevent-abbreviations": "off" 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test/amd-modules.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { suite } = require('uvu'); 4 | const assert = require('uvu/assert'); 5 | const types = require('../index.js'); 6 | const check = require('./utils.js'); 7 | 8 | const testSuite = suite('isExports'); 9 | 10 | testSuite('detects driver scripts', () => { 11 | assert.ok(check('require(["a"], function(a){});', types.isAMDDriverScriptRequire)); 12 | }); 13 | 14 | testSuite('does not get confused with a commonjs require', () => { 15 | assert.not.ok(check('require("foo");', types.isAMDDriverScriptRequire)); 16 | }); 17 | 18 | // named form 19 | testSuite('detects named form', () => { 20 | assert.ok(check('define("foobar", ["a"], function(a){});', types.isNamedForm)); 21 | }); 22 | 23 | testSuite('needs 3 arguments', () => { 24 | assert.not.ok(check('define("foobar", ["a"]);', types.isNamedForm)); 25 | assert.not.ok(check('define("foobar", ["a"], function(a){}, "foo");', types.isNamedForm)); 26 | }); 27 | 28 | testSuite('needs the first argument to be a literal', () => { 29 | assert.not.ok(check('define(["foobar"], ["a"], function(a){});', types.isNamedForm)); 30 | }); 31 | 32 | testSuite('needs the second argument to be an array', () => { 33 | assert.not.ok(check('define("foobar", 123, function(a){});', types.isNamedForm)); 34 | }); 35 | 36 | testSuite('needs the third argument to be a function', () => { 37 | assert.not.ok(check('define("foobar", ["foo"], 123);', types.isNamedForm)); 38 | assert.not.ok(check('define("reset", [0, 0], "modifier");', types.isNamedForm)); 39 | }); 40 | // }); 41 | 42 | // dependency form 43 | testSuite('detects dependency form modules', () => { 44 | assert.ok(check('define(["a"], function(a){});', types.isDependencyForm)); 45 | }); 46 | 47 | testSuite('needs the first argument to be an array', () => { 48 | assert.not.ok(check('define(123, function(a){});', types.isDependencyForm)); 49 | }); 50 | 51 | testSuite('needs the second argument to be a function', () => { 52 | assert.not.ok(check('define(["a"], 123);', types.isDependencyForm)); 53 | }); 54 | 55 | testSuite('needs 2 arguments', () => { 56 | assert.not.ok(check('define(["a"], function(a){}, 123);', types.isDependencyForm)); 57 | }); 58 | 59 | // factory form 60 | testSuite('detects factory form modules', () => { 61 | assert.ok(check('define(function(require){});', types.isFactoryForm)); 62 | }); 63 | 64 | testSuite('needs one argument', () => { 65 | assert.not.ok(check('define(function(require){}, 123);', types.isFactoryForm)); 66 | }); 67 | 68 | testSuite('needs the first argument to be a function', () => { 69 | assert.not.ok(check('define(123);', types.isFactoryForm)); 70 | }); 71 | 72 | testSuite('detects REM form modules', () => { 73 | assert.ok(check('define(function(require, exports, module){});', types.isREMForm)); 74 | }); 75 | 76 | // no dependency form 77 | testSuite('detects no dependency form modules', () => { 78 | assert.ok(check('define({});', types.isNoDependencyForm)); 79 | }); 80 | 81 | testSuite('needs a aingle argument', () => { 82 | assert.not.ok(check('define({}, 123);', types.isNoDependencyForm)); 83 | }); 84 | 85 | testSuite('needs the first argument to be an object', () => { 86 | assert.not.ok(check('define(function(){});', types.isNoDependencyForm)); 87 | }); 88 | 89 | testSuite.run(); 90 | -------------------------------------------------------------------------------- /test/es6.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { suite } = require('uvu'); 4 | const assert = require('uvu/assert'); 5 | const types = require('../index.js'); 6 | const check = require('./utils.js'); 7 | 8 | const testSuite = suite('module-types'); 9 | 10 | testSuite('detects es6 imports', () => { 11 | assert.ok(check('import {foo, bar} from "mylib";', types.isES6Import, true)); 12 | assert.ok(check('import * as foo from "mod.js";', types.isES6Import, true)); 13 | assert.ok(check('import "mylib2";', types.isES6Import, true)); 14 | assert.ok(check('import foo from "mod.js";', types.isES6Import, true)); 15 | assert.ok(check('import("foo");', types.isES6Import, true)); 16 | }); 17 | 18 | testSuite('detects es6 exports', () => { 19 | assert.ok(check('export default 123;', types.isES6Export, true)); 20 | assert.ok(check('export {foo, bar}; function foo() {} function bar() {}', types.isES6Export, true)); 21 | assert.ok(check('export { D as default }; class D {}', types.isES6Export, true)); 22 | assert.ok(check('export function inc() { counter++; }', types.isES6Export, true)); 23 | assert.ok(check('export * from "mod";', types.isES6Export, true)); 24 | }); 25 | 26 | testSuite('detects dynamic imports', () => { 27 | assert.ok(check('import("./bar");', types.isDynamicImport, true)); 28 | assert.ok(check('function foo() { import("./bar"); }', types.isDynamicImport, true)); 29 | }); 30 | 31 | testSuite.run(); 32 | -------------------------------------------------------------------------------- /test/is-define-amd.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { suite } = require('uvu'); 4 | const assert = require('uvu/assert'); 5 | const types = require('../index.js'); 6 | const check = require('./utils.js'); 7 | 8 | const testSuite = suite('isDefineAMD'); 9 | 10 | testSuite('does not detect a generic define function call', () => { 11 | assert.not.ok(check('define();', types.isDefineAMD)); 12 | // Named form 13 | assert.ok(check('define("foobar", ["a"], function(a){});', types.isDefineAMD)); 14 | // Dependency form 15 | assert.ok(check('define(["a"], function(a){});', types.isDefineAMD)); 16 | // Factory form 17 | assert.ok(check('define(function(require){});', types.isDefineAMD)); 18 | // REM form 19 | assert.ok(check('define(function(require, exports, module){});', types.isDefineAMD)); 20 | // No-dependency form 21 | assert.ok(check('define({});', types.isDefineAMD)); 22 | }); 23 | 24 | testSuite('detects a named form AMD define function call', () => { 25 | assert.not.ok(check('define();', types.isDefineAMD)); 26 | }); 27 | 28 | testSuite.run(); 29 | -------------------------------------------------------------------------------- /test/is-define.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { suite } = require('uvu'); 4 | const assert = require('uvu/assert'); 5 | const types = require('../index.js'); 6 | const check = require('./utils.js'); 7 | 8 | const testSuite = suite('isDefine'); 9 | 10 | testSuite('detects define function calls', () => { 11 | assert.ok(check('define();', types.isDefine)); 12 | }); 13 | 14 | testSuite.run(); 15 | -------------------------------------------------------------------------------- /test/is-exports.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { suite } = require('uvu'); 4 | const assert = require('uvu/assert'); 5 | const types = require('../index.js'); 6 | const check = require('./utils.js'); 7 | 8 | const testSuite = suite('isExports'); 9 | 10 | testSuite('detects module.exports CJS style exports', () => { 11 | assert.ok(check('module.exports.foo = function() {};', types.isExports)); 12 | assert.ok(check('module.exports = function() {};', types.isExports)); 13 | assert.ok(check('module.exports = {};', types.isExports)); 14 | }); 15 | 16 | testSuite('detects plain exports CJS style exports', () => { 17 | assert.ok(check('exports = function() {};', types.isExports)); 18 | assert.ok(check('exports.foo = function() {};', types.isExports)); 19 | assert.ok(check('exports = {};', types.isExports)); 20 | }); 21 | 22 | testSuite.run(); 23 | -------------------------------------------------------------------------------- /test/is-require.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { suite } = require('uvu'); 4 | const assert = require('uvu/assert'); 5 | const types = require('../index.js'); 6 | const check = require('./utils.js'); 7 | 8 | const testSuite = suite('isRequire'); 9 | 10 | testSuite('detects require function calls', () => { 11 | assert.ok(check('require();', types.isRequire)); 12 | }); 13 | 14 | testSuite('detects require function calls', () => { 15 | assert.ok(check('require.main.require();', types.isRequire)); 16 | }); 17 | 18 | testSuite('detects top-level (i.e., top of file) require function calls', () => { 19 | assert.ok(check('require();', types.isTopLevelRequire)); 20 | assert.not.ok(check('var foo = 2; \nrequire([], function(){});', types.isTopLevelRequire)); 21 | assert.ok(check('require(["a"], function(a){});', types.isTopLevelRequire)); 22 | }); 23 | 24 | testSuite('does not fail on es6', () => { 25 | assert.not.throws(() => { 26 | check('import require from "mylib"; \nrequire();', types.isTopLevelRequire, true); 27 | }); 28 | }); 29 | 30 | testSuite.run(); 31 | -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Walker = require('node-source-walk'); 4 | const types = require('../index.js'); 5 | 6 | // Checks whether of not the checker succeeds on 7 | // a node in the AST of the given source code 8 | module.exports = function(code, checker, harmony) { 9 | const walker = new Walker({ esprimaHarmony: Boolean(harmony) }); 10 | let found = false; 11 | 12 | walker.walk(code, node => { 13 | // Use call to avoid .bind(types) everywhere 14 | if (checker.call(types, node)) { 15 | found = true; 16 | walker.stopWalking(); 17 | } 18 | }); 19 | 20 | return found; 21 | }; 22 | --------------------------------------------------------------------------------