├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── docs └── rules │ └── import-order.md ├── index.js ├── package.json ├── rules └── import-order.js ├── test ├── import-order.js └── importType.js └── utils.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | - '0.10' 5 | deploy: 6 | provider: npm 7 | email: jfm.engels@gmail.com 8 | api_key: 9 | secure: IrpRRuP8LuJ5Y7rq5Mgxb0aC644y/v4UUy6e6tMzjbEGysW9DAMyP7jAGHZvgJg4C6sh5CzvZNAAcRKauvYxR9a29wO2UkVfnFNoIRpTZQ2kBZhaToxS7Q+TEiXqVgUN4hpCdJ9S0vLr4HL9ob3+FgxIJ+cV+sMTf6uVjvjs4AOwlNkupYyad0M9jX2NL+UnLzYwDWBCdHaR37Sn5LKkHKb/hq0hOiDzO0i3wbnHsxkWabNi/8/7/p7WJI64s5LdnftEXZraxxxXc8Se+vLHZIZazrcaWOFcLQW+4ZN/8OHYA1BEcmxjjxP9uEfmRYK4V3hTJKumyrnBAnWlCeFChG57HRP3Fpv38faMjxBVEBk0jVWBcK1njM0rk3sayINZOsT1yCMrsIpW5BxwaDMR+PVqJRhdtrmfggzMLiF6BaECjCri2bJKsrHciMADBKNM6geXuDBCXYJ9ZVTrJIo/AVicNub9rH6A4zWCDlZlOryJUfbIvAkV9zTApTFsTNpnGxDi8dgbEYV+wKiwmW/SJWwy3AljWSWYnOzayVI0jUM0bmj1rG9JeWnr4VcjnrV6S09c9r6NbLzvm/7tGMbaTXZwdOPWfpavUaKsuhPKXBwL2vPcAuYeslXRU3CV/Jmtq4A9T/iy6fq/6JjJoA7XQcpBM////CMKOYgiCwmAnQc= 10 | on: 11 | tags: true 12 | all_branches: true 13 | node: stable 14 | repo: jfmengels/eslint-plugin-import-order 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jeroen Engels 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 | # eslint-plugin-import-order 2 | 3 | [![version](https://img.shields.io/npm/v/eslint-plugin-import-order.svg)](http://npm.im/eslint-plugin-import-order) 4 | 5 | ESLint plugin to enforce the order of import/require statements. 6 | 7 | # Deprecation notice 8 | 9 | This plugin is no longer maintained, as its sole feature has been integrated and improved upon in [eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import), which does even more cool stuff. The corresponding rule is available under the name [`order`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs%2Frules%2Forder.md). 10 | 11 | 12 | ## Install 13 | 14 | ``` 15 | $ npm install --save-dev eslint eslint-plugin-import-order 16 | ``` 17 | 18 | 19 | ## Usage 20 | 21 | Configure it in `package.json`. 22 | 23 | ```json 24 | { 25 | "name": "my-awesome-project", 26 | "eslintConfig": { 27 | "env": { 28 | "es6": true 29 | }, 30 | "parserOptions": { 31 | "ecmaVersion": 6, 32 | "sourceType": "module" 33 | }, 34 | "plugins": [ 35 | "import-order" 36 | ], 37 | "rules": { 38 | "import-order/import-order": 2 39 | } 40 | } 41 | } 42 | ``` 43 | 44 | 45 | ## Rules 46 | 47 | - [import-order](docs/rules/import-order.md) - Enforce a convention in module import order. 48 | 49 | ## Recommended configuration 50 | 51 | This plugin exports a [`recommended` configuration](index.js#L8) that enforces good practices. 52 | 53 | To enable this configuration use the `extends` property in your `package.json`. 54 | 55 | ```json 56 | { 57 | "name": "my-awesome-project", 58 | "eslintConfig": { 59 | "extends": "plugin:import-order/recommended", 60 | "plugins": [ 61 | "import-order" 62 | ] 63 | } 64 | } 65 | ``` 66 | 67 | See [ESLint documentation](http://eslint.org/docs/user-guide/configuring#extending-configuration-files) for more information about extending configuration files. 68 | 69 | **Note**: This configuration will also enable the correct [parser options](http://eslint.org/docs/user-guide/configuring#specifying-parser-options) and [environment](http://eslint.org/docs/user-guide/configuring#specifying-environments). 70 | -------------------------------------------------------------------------------- /docs/rules/import-order.md: -------------------------------------------------------------------------------- 1 | # Enforce a convention in module import order 2 | 3 | Enforce a convention in the order of `require()` / `import` statements. The order is as shown in the following example: 4 | 5 | ```js 6 | // 1. node "builtins" 7 | var fs = require('fs'); 8 | var path = require('path'); 9 | // 2. "external" modules 10 | var _ = require('lodash'); 11 | var chalk = require('chalk'); 12 | // 3. modules from a "parent" directory 13 | var foo = require('../foo'); 14 | var qux = require('../../foo/qux'); 15 | // 4. "sibling" modules from the same or a sibling's directory 16 | var bar = require('./bar'); 17 | var baz = require('./bar/baz'); 18 | // 5. "index" of the current directory 19 | var main = require('./'); 20 | ``` 21 | 22 | Unassigned imports are not accounted for, as the order they are imported in may be important. 23 | 24 | 25 | ## Fail 26 | 27 | ```js 28 | var _ = require('lodash'); 29 | var path = require('path'); // `path` import should occur before import of `lodash` 30 | 31 | // ----- 32 | 33 | import _ from 'lodash'; 34 | import path from 'path'; // `path` import should occur before import of `lodash` 35 | ``` 36 | 37 | 38 | ## Pass 39 | 40 | ```js 41 | var path = require('path'); 42 | var _ = require('lodash'); 43 | 44 | // ----- 45 | 46 | import path from 'path'; 47 | import _ from 'lodash'; 48 | 49 | // ----- 50 | 51 | // Allowed as ̀`babel-register` is not assigned. 52 | require('babel-register'); 53 | var path = require('path'); 54 | ``` 55 | 56 | ## Options 57 | 58 | This rule supports the following options: 59 | 60 | `order`: The order to respect. It needs to contain only and all of the following elements: `"builtin", "external", "parent", "sibling", "index"`, which is the default value. 61 | 62 | You can set the options like this: 63 | 64 | ```js 65 | "import-order/import-order": [2, {"order": ["index", "sibling", "parent", "external", "builtin"]}] 66 | ``` 67 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | rules: { 5 | 'import-order': require('./rules/import-order') 6 | }, 7 | configs: { 8 | recommended: { 9 | env: { 10 | es6: true 11 | }, 12 | parserOptions: { 13 | ecmaVersion: 6, 14 | sourceType: 'module' 15 | }, 16 | rules: { 17 | 'import-order/import-order': 2 18 | } 19 | } 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-import-order", 3 | "version": "2.1.4", 4 | "description": "ESLint plugin to enforce the order of import/require statements.", 5 | "engines": { 6 | "node": ">=0.10.0" 7 | }, 8 | "scripts": { 9 | "test": "xo && ava" 10 | }, 11 | "files": [ 12 | "index.js", 13 | "utils.js", 14 | "rules" 15 | ], 16 | "repository": "jfmengels/eslint-plugin-import-order", 17 | "keywords": [ 18 | "eslint", 19 | "eslintplugin", 20 | "eslint-plugin", 21 | "import", 22 | "require", 23 | "order", 24 | "style", 25 | "convention" 26 | ], 27 | "author": { 28 | "name": "Jeroen Engels", 29 | "email": "jfm.engels@gmail.com" 30 | }, 31 | "license": "MIT", 32 | "devDependencies": { 33 | "ava": "^0.13.0", 34 | "eslint": "^2.4.0", 35 | "eslint-plugin-ava": "^1.3.0", 36 | "xo": "^0.13.0" 37 | }, 38 | "peerDependencies": { 39 | "eslint": ">=2" 40 | }, 41 | "dependencies": { 42 | "builtin-modules": "^1.1.1", 43 | "lodash.cond": "^4.2.0", 44 | "lodash.find": "^4.2.0" 45 | }, 46 | "xo": { 47 | "space": 2, 48 | "rules": { 49 | "camelcase": [ 50 | 2, 51 | { 52 | "properties": "never" 53 | } 54 | ] 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /rules/import-order.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var find = require('lodash.find'); 4 | var utils = require('../utils'); 5 | 6 | var defaultOrder = ['builtin', 'external', 'parent', 'sibling', 'index']; 7 | 8 | // REPORTING 9 | 10 | function reverse(array) { 11 | return array.map(function (v) { 12 | return { 13 | name: v.name, 14 | rank: -v.rank, 15 | node: v.node 16 | }; 17 | }).reverse(); 18 | } 19 | 20 | function findOutOfOrder(imported) { 21 | if (imported.length === 0) { 22 | return []; 23 | } 24 | var maxSeenRankNode = imported[0]; 25 | return imported.filter(function (importedModule) { 26 | var res = importedModule.rank < maxSeenRankNode.rank; 27 | if (maxSeenRankNode.rank < importedModule.rank) { 28 | maxSeenRankNode = importedModule; 29 | } 30 | return res; 31 | }); 32 | } 33 | 34 | function report(context, imported, outOfOrder, order) { 35 | outOfOrder.forEach(function (imp) { 36 | var found = find(imported, function hasHigherRank(importedItem) { 37 | return importedItem.rank > imp.rank; 38 | }); 39 | context.report(imp.node, '`' + imp.name + '` import should occur ' + order + ' import of `' + found.name + '`'); 40 | }); 41 | } 42 | 43 | function makeReport(context, imported) { 44 | var outOfOrder = findOutOfOrder(imported); 45 | if (!outOfOrder.length) { 46 | return; 47 | } 48 | // There are things to report. Try to minimize the number of reported errors. 49 | var reversedImported = reverse(imported); 50 | var reversedOrder = findOutOfOrder(reversedImported); 51 | if (reversedOrder.length < outOfOrder.length) { 52 | report(context, reversedImported, reversedOrder, 'after'); 53 | return; 54 | } 55 | report(context, imported, outOfOrder, 'before'); 56 | } 57 | 58 | // DETECTING 59 | 60 | function isStaticRequire(node) { 61 | return node && 62 | node.callee.type === 'Identifier' && 63 | node.callee.name === 'require' && 64 | node.arguments.length === 1 && 65 | node.arguments[0].type === 'Literal'; 66 | } 67 | 68 | function computeRank(order, name) { 69 | return order.indexOf(utils.importType(name)); 70 | } 71 | 72 | function registerNode(node, name, order, imported) { 73 | var rank = computeRank(order, name); 74 | if (rank !== -1) { 75 | imported.push({name: name, rank: rank, node: node}); 76 | } 77 | } 78 | 79 | function isInVariableDeclarator(node) { 80 | return node && 81 | (node.type === 'VariableDeclarator' || isInVariableDeclarator(node.parent)); 82 | } 83 | 84 | /* eslint quote-props: [2, "as-needed"] */ 85 | module.exports = function importOrderRule(context) { 86 | var imported = []; 87 | var options = context.options[0] || {}; 88 | var order = options.order || defaultOrder; 89 | var level = 0; 90 | 91 | function incrementLevel() { 92 | level++; 93 | } 94 | function decrementLevel() { 95 | level--; 96 | } 97 | 98 | return { 99 | ImportDeclaration: function handleImports(node) { 100 | if (node.specifiers.length) { // Ignoring unassigned imports 101 | var name = node.source.value; 102 | registerNode(node, name, order, imported); 103 | } 104 | }, 105 | CallExpression: function handleRequires(node) { 106 | if (level !== 0 || !isStaticRequire(node) || !isInVariableDeclarator(node.parent)) { 107 | return; 108 | } 109 | var name = node.arguments[0].value; 110 | registerNode(node, name, order, imported); 111 | }, 112 | 'Program:exit': function reportAndReset() { 113 | makeReport(context, imported); 114 | imported = []; 115 | }, 116 | FunctionDeclaration: incrementLevel, 117 | FunctionExpression: incrementLevel, 118 | ArrowFunctionExpression: incrementLevel, 119 | BlockStatement: incrementLevel, 120 | 'FunctionDeclaration:exit': decrementLevel, 121 | 'FunctionExpression:exit': decrementLevel, 122 | 'ArrowFunctionExpression:exit': decrementLevel, 123 | 'BlockStatement:exit': decrementLevel 124 | }; 125 | }; 126 | 127 | module.exports.schema = [ 128 | { 129 | type: 'object', 130 | properties: { 131 | order: { 132 | type: 'array', 133 | uniqueItems: true, 134 | length: 5, 135 | items: { 136 | enum: defaultOrder 137 | } 138 | } 139 | }, 140 | additionalProperties: false 141 | } 142 | ]; 143 | -------------------------------------------------------------------------------- /test/import-order.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import {RuleTester} from 'eslint'; 3 | import rule from '../rules/import-order'; 4 | 5 | const ruleTester = new RuleTester({ 6 | env: { 7 | es6: true 8 | }, 9 | parserOptions: { 10 | sourceType: 'module' 11 | } 12 | }); 13 | 14 | const ruleError = {ruleId: 'import-order'}; 15 | 16 | test(() => { 17 | ruleTester.run('import-order', rule, { 18 | valid: [ 19 | // Default order using require 20 | ` 21 | var fs = require('fs'); 22 | var async = require('async'); 23 | var relParent1 = require('../foo'); 24 | var relParent2 = require('../foo/bar'); 25 | var relParent3 = require('../'); 26 | var sibling = require('./foo'); 27 | var index = require('./'); 28 | `, 29 | // Default order using import 30 | ` 31 | import fs from 'fs'; 32 | import async, {foo1} from 'async'; 33 | import relParent1 from '../foo'; 34 | import relParent2, {foo2} from '../foo/bar'; 35 | import relParent3 from '../'; 36 | import sibling, {foo3} from './foo'; 37 | import index from './'; 38 | `, 39 | // Default order using both require and import 40 | ` 41 | var fs = require('fs'); 42 | import async, {foo1} from 'async'; 43 | var relParent1 = require('../foo'); 44 | import relParent2, {foo2} from '../foo/bar'; 45 | var relParent3 = require('../'); 46 | import sibling, {foo3} from './foo'; 47 | var index = require('./'); 48 | `, 49 | // Multiple module of the same rank next to each other 50 | ` 51 | var fs = require('fs'); 52 | var fs = require('fs'); 53 | var path = require('path'); 54 | var _ = require('lodash'); 55 | var async = require('async'); 56 | `, 57 | // Overriding order to be the reverse of the default order 58 | { 59 | code: ` 60 | var index = require('./'); 61 | var sibling = require('./foo'); 62 | var relParent3 = require('../'); 63 | var relParent2 = require('../foo/bar'); 64 | var relParent1 = require('../foo'); 65 | var async = require('async'); 66 | var fs = require('fs'); 67 | `, 68 | options: [{order: ['index', 'sibling', 'parent', 'external', 'builtin']}] 69 | }, 70 | // Ignore dynamic requires 71 | ` 72 | var path = require('path'); 73 | var _ = require('lodash'); 74 | var async = require('async'); 75 | var fs = require('f' + 's'); 76 | `, 77 | // Ignore non-require call expressions 78 | ` 79 | var path = require('path'); 80 | var result = add(1, 2); 81 | var _ = require('lodash'); 82 | `, 83 | // Ignore requires that are not at the top-level 84 | ` 85 | var index = require('./'); 86 | function foo() { 87 | var fs = require('fs'); 88 | } 89 | () => require('fs'); 90 | if (a) { 91 | require('fs'); 92 | } 93 | `, 94 | // Ignore unknown/invalid cases 95 | ` 96 | var unknown1 = require('/unknown1'); 97 | var fs = require('fs'); 98 | var unknown2 = require('/unknown2'); 99 | var async = require('async'); 100 | var unknown3 = require('/unknown3'); 101 | var foo = require('../foo'); 102 | var unknown4 = require('/unknown4'); 103 | var bar = require('../foo/bar'); 104 | var unknown5 = require('/unknown5'); 105 | var parent = require('../'); 106 | var unknown6 = require('/unknown6'); 107 | var foo = require('./foo'); 108 | var unknown7 = require('/unknown7'); 109 | var index = require('./'); 110 | var unknown8 = require('/unknown8'); 111 | `, 112 | // Ignoring unassigned values by default (require) 113 | ` 114 | require('./foo'); 115 | require('fs'); 116 | var path = require('path'); 117 | `, 118 | // Ignoring unassigned values by default (import) 119 | ` 120 | import './foo'; 121 | import 'fs'; 122 | import path from 'path'; 123 | `, 124 | // No imports 125 | ` 126 | function add(a, b) { 127 | return a + b; 128 | } 129 | var foo; 130 | ` 131 | ], 132 | invalid: [ 133 | // builtin before external module (require) 134 | { 135 | code: ` 136 | var async = require('async'); 137 | var fs = require('fs'); 138 | `, 139 | errors: [ 140 | {...ruleError, message: '`fs` import should occur before import of `async`'} 141 | ] 142 | }, 143 | // builtin before external module (import) 144 | { 145 | code: ` 146 | import async from 'async'; 147 | import fs from 'fs'; 148 | `, 149 | errors: [ 150 | {...ruleError, message: '`fs` import should occur before import of `async`'} 151 | ] 152 | }, 153 | // builtin before external module (mixed import and require) 154 | { 155 | code: ` 156 | var async = require('async'); 157 | import fs from 'fs'; 158 | `, 159 | errors: [ 160 | {...ruleError, message: '`fs` import should occur before import of `async`'} 161 | ] 162 | }, 163 | // external before parent 164 | { 165 | code: ` 166 | var parent = require('../parent'); 167 | var async = require('async'); 168 | `, 169 | errors: [ 170 | {...ruleError, message: '`async` import should occur before import of `../parent`'} 171 | ] 172 | }, 173 | // parent before sibling 174 | { 175 | code: ` 176 | var sibling = require('./sibling'); 177 | var parent = require('../parent'); 178 | `, 179 | errors: [ 180 | {...ruleError, message: '`../parent` import should occur before import of `./sibling`'} 181 | ] 182 | }, 183 | // sibling before index 184 | { 185 | code: ` 186 | var index = require('./'); 187 | var sibling = require('./sibling'); 188 | `, 189 | errors: [ 190 | {...ruleError, message: '`./sibling` import should occur before import of `./`'} 191 | ] 192 | }, 193 | // Multiple errors 194 | { 195 | code: ` 196 | var sibling = require('./sibling'); 197 | var async = require('async'); 198 | var fs = require('fs'); 199 | `, 200 | errors: [ 201 | {...ruleError, message: '`async` import should occur before import of `./sibling`'}, 202 | {...ruleError, message: '`fs` import should occur before import of `./sibling`'} 203 | ] 204 | }, 205 | // Uses 'after' wording if it creates less errors 206 | { 207 | code: ` 208 | var index = require('./'); 209 | var fs = require('fs'); 210 | var path = require('path'); 211 | var _ = require('lodash'); 212 | var foo = require('foo'); 213 | var bar = require('bar'); 214 | `, 215 | errors: [ 216 | {...ruleError, message: '`./` import should occur after import of `bar`'} 217 | ] 218 | }, 219 | // Overriding order to be the reverse of the default order 220 | { 221 | code: ` 222 | var fs = require('fs'); 223 | var index = require('./'); 224 | `, 225 | options: [{order: ['index', 'sibling', 'parent', 'external', 'builtin']}], 226 | errors: [ 227 | {...ruleError, message: '`./` import should occur before import of `fs`'} 228 | ] 229 | }, 230 | // member expression of require 231 | { 232 | code: ` 233 | var foo = require('./foo').bar; 234 | var fs = require('fs'); 235 | `, 236 | errors: [ 237 | {...ruleError, message: '`fs` import should occur before import of `./foo`'} 238 | ] 239 | }, 240 | // nested member expression of require 241 | { 242 | code: ` 243 | var foo = require('./foo').bar.bar.bar; 244 | var fs = require('fs'); 245 | `, 246 | errors: [ 247 | {...ruleError, message: '`fs` import should occur before import of `./foo`'} 248 | ] 249 | } 250 | ] 251 | }); 252 | }); 253 | -------------------------------------------------------------------------------- /test/importType.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import {importType} from '../utils'; 3 | 4 | test('should return "builtin" for node.js modules', t => { 5 | t.is(importType('fs'), 'builtin'); 6 | t.is(importType('path'), 'builtin'); 7 | }); 8 | 9 | test('should return "external" for non-builtin modules without a relative path', t => { 10 | t.is(importType('lodash'), 'external'); 11 | t.is(importType('async'), 'external'); 12 | t.is(importType('chalk'), 'external'); 13 | t.is(importType('foo'), 'external'); 14 | t.is(importType('lodash.find'), 'external'); 15 | }); 16 | 17 | test('should return parent for internal modules that go through the parent', t => { 18 | t.is(importType('../foo'), 'parent'); 19 | t.is(importType('../../foo'), 'parent'); 20 | t.is(importType('../bar/foo'), 'parent'); 21 | }); 22 | 23 | test('should return sibling for internal modules that are connected to one of the siblings', t => { 24 | t.is(importType('./foo'), 'sibling'); 25 | t.is(importType('./foo/bar'), 'sibling'); 26 | }); 27 | 28 | test('should return "index" for sibling index file', t => { 29 | t.is(importType('.'), 'index'); 30 | t.is(importType('./'), 'index'); 31 | t.is(importType('./index'), 'index'); 32 | t.is(importType('./index.js'), 'index'); 33 | }); 34 | 35 | test('should return "unknown" for any unhandled cases', t => { 36 | t.is(importType('/malformed'), 'unknown'); 37 | t.is(importType(' foo'), 'unknown'); 38 | }); 39 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var cond = require('lodash.cond'); 4 | var builtinModules = require('builtin-modules'); 5 | 6 | function constant(value) { 7 | return function returnValue() { 8 | return value; 9 | }; 10 | } 11 | 12 | function isBuiltIn(name) { 13 | return builtinModules.indexOf(name) !== -1; 14 | } 15 | 16 | var externalModuleRegExp = /^\w/; 17 | function isExternalModule(name) { 18 | return externalModuleRegExp.test(name); 19 | } 20 | 21 | function isRelativeToParent(name) { 22 | return name.indexOf('../') === 0; 23 | } 24 | 25 | function isIndex(name) { 26 | return name === '.' || 27 | name === './' || 28 | name === './index' || 29 | name === './index.js'; 30 | } 31 | 32 | function isRelativeToSibling(name) { 33 | return name.indexOf('./') === 0; 34 | } 35 | 36 | var importType = cond([ 37 | [isBuiltIn, constant('builtin')], 38 | [isExternalModule, constant('external')], 39 | [isRelativeToParent, constant('parent')], 40 | [isIndex, constant('index')], 41 | [isRelativeToSibling, constant('sibling')], 42 | [constant(true), constant('unknown')] 43 | ]); 44 | 45 | module.exports = { 46 | importType: importType 47 | }; 48 | --------------------------------------------------------------------------------