├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .travis.yml ├── History.md ├── Readme.md ├── example-app ├── app.js ├── controllers │ ├── User.js │ └── index.js ├── lib │ ├── relativeImport.js │ └── utils.js └── models │ ├── User.js │ └── data.json ├── lib ├── ResolverPlugin.js ├── getAbsoluteDirsFromInputDirs.js ├── getFilenameFromBabelOpts.js └── getPluginOptsFromBabelOpts.js ├── package.json └── test ├── babelPluginResolver.test.js └── mocha.opts /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": [["resolver", {"resolveDirs": ["example-app", "example-app/lib"]}]] 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | parser: babel-eslint 2 | 3 | extends: standard 4 | 5 | plugins: [ 6 | "babel" 7 | ] 8 | 9 | globals: 10 | describe: false 11 | it: false 12 | before: false 13 | 14 | rules: 15 | arrow-parens: 0 16 | babel/arrow-parens: [2, "as-needed"] 17 | 18 | no-undef: 2 19 | eqeqeq: 0 20 | no-var: 0 21 | operator-linebreak: 0 22 | prefer-arrow-callback: 0 23 | space-before-function-paren: [2, {anonymous: always, named: never}] 24 | no-unneeded-ternary: 0 25 | semi: [2, always] 26 | yoda: 0 27 | arrow-spacing: 2 28 | dot-location: [2, "property"] 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules 3 | coverage -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '4' 5 | script: 6 | - npm test 7 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | v1.1.0 / 2016-09-13 3 | =================== 4 | 5 | * bump babel-resolver 6 | 7 | v1.0.0 / 2016-08-20 8 | ================== 9 | 10 | * upgrade babel-resolver 11 | * spacing, minor version 12 | * Merge pull request #3 from jjt/lodash-upgrade 13 | * Merge pull request #5 from fetchak/fix_path_undefined 14 | * Merge pull request #6 from fetchak/safer_instructions 15 | * Make eslint happy 16 | * No need to recursive `rm` a single file 17 | * Check if file exists 18 | * Upgrade lodash, require specific lodash methods 19 | 20 | v0.0.7 / 2016-01-05 21 | =================== 22 | 23 | * v0.0.7 24 | * infer app root from babelOpts.filename 25 | * bump version 26 | * changelog 27 | 28 | v0.0.6 / 2016-01-05 29 | =================== 30 | 31 | * update babel-resolver dep 32 | 33 | v0.0.5 / 2016-01-05 34 | =================== 35 | 36 | * changelog 37 | * v0.0.5 38 | * rename main to ResolverPlugin 39 | * Merge pull request #1 from iuriikozuliak/patch-1 40 | * Update getPluginOptsFromBabelOpts.js 41 | * tweak copy 42 | * v0.0.4 43 | 44 | v0.0.4 / 2015-11-21 45 | =================== 46 | 47 | * upgrade deps, support '.jsx' 48 | * tweak error message 49 | * fix slashes in docs 50 | * disable coveralls for now 51 | * v0.0.3 52 | * bump version 53 | * fix lint errors 54 | * add changelog 55 | * bump version 56 | * fix readme 57 | * readme" 58 | * infer plugin opts, calc absolute dirs 59 | * initial commit 60 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Babel Resolver Plugin [![NPM version][npm-image]][npm-url] [![Build status][travis-image]][travis-url] 2 | 3 | Resolve modules from any directory. 4 | 5 | ```javascript 6 | import User from '../../models/User'; 7 | ``` 8 | Becomes: 9 | 10 | ```javascript 11 | import User from 'models/User'; 12 | ``` 13 | 14 | ## Usage 15 | 16 | ``` 17 | npm i babel-plugin-resolver --save 18 | rm -rf ~/.babel.json 19 | ``` 20 | 21 | *In .babelrc:* 22 | 23 | ```json 24 | { 25 | "presets": ["es2015"], 26 | "plugins": [["resolver", {"resolveDirs": ["src"]}]] 27 | } 28 | ``` 29 | 30 | Given the directory structure: 31 | 32 | ``` 33 | /app 34 | .babelrc 35 | /src 36 | /models 37 | User.js 38 | /controllers 39 | User.js 40 | ``` 41 | 42 | *In app/controllers/User.js:* 43 | 44 | ```javascript 45 | import UserModel from 'models/User'; 46 | // => resolves: "app/src/models/User.js" 47 | ``` 48 | 49 | This example uses [Babel 6](http://babeljs.io/). 50 | 51 | **Note:** Run `rm -rf ~/.babel.json` if you're seeing errors. 52 | 53 | **Note2:** This plugin is only called when you use `import`, not `require`. 54 | 55 | 56 | ## Resolving Multiple Directories 57 | 58 | *In .babelrc:* 59 | 60 | ```json 61 | { 62 | "presets": ["es2015"], 63 | "plugins": [["resolver", {"resolveDirs": ["src", "src/lib"]}]] 64 | } 65 | ``` 66 | 67 | Given the directory structure: 68 | 69 | ``` 70 | /app 71 | .babelrc 72 | /src 73 | /models 74 | User.js 75 | /controllers 76 | User.js 77 | /lib 78 | utils.js 79 | ``` 80 | 81 | *In app/controllers/User.js:* 82 | 83 | ```javascript 84 | import UserModel from 'models/User'; 85 | // => resolves: "app/src/models/User.js" 86 | import utils from 'utils'; 87 | // => resolves: "app/src/lib/utils.js" 88 | ``` 89 | 90 | ## Installation 91 | 92 | ``` 93 | npm i babel-plugin-resolver --save 94 | rm -f ~/.babel.json 95 | ``` 96 | 97 | ## Why not just set NODE_PATH? 98 | 99 | While setting `NODE_PATH=app` is a perfectly valid solution, `babel-resolver` is more explicit and lets you avoid mucking around with environment variables. 100 | 101 | ## License 102 | 103 | MIT 104 | 105 | [npm-image]: https://badge.fury.io/js/babel-plugin-resolver.svg 106 | [npm-url]: https://npmjs.org/package/babel-plugin-resolver 107 | [travis-image]: https://travis-ci.org/jshanson7/babel-plugin-resolver.svg 108 | [travis-url]: https://travis-ci.org/jshanson7/babel-plugin-resolver 109 | [coveralls-image]: https://coveralls.io/repos/jshanson7/babel-plugin-resolver/badge.svg?branch=master&service=github 110 | [coveralls-url]: https://coveralls.io/github/jshanson7/babel-plugin-resolver?branch=master 111 | -------------------------------------------------------------------------------- /example-app/app.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { User } from 'controllers'; 3 | 4 | assert(User === 'UserController'); 5 | -------------------------------------------------------------------------------- /example-app/controllers/User.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import utils from 'utils'; 3 | import UserModel from 'models/User'; 4 | 5 | assert(utils === 'utils'); 6 | assert(UserModel === 'UserModel'); 7 | 8 | export default 'UserController'; 9 | -------------------------------------------------------------------------------- /example-app/controllers/index.js: -------------------------------------------------------------------------------- 1 | import UserController from 'controllers/User'; 2 | 3 | export const User = UserController; 4 | -------------------------------------------------------------------------------- /example-app/lib/relativeImport.js: -------------------------------------------------------------------------------- 1 | export default 'relative works too'; 2 | -------------------------------------------------------------------------------- /example-app/lib/utils.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import relativeImport from './relativeImport'; 3 | 4 | assert(relativeImport === 'relative works too'); 5 | 6 | export default 'utils'; 7 | -------------------------------------------------------------------------------- /example-app/models/User.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import data from 'models/data'; 3 | 4 | assert(data.hello === 'world'); 5 | 6 | export default 'UserModel'; 7 | -------------------------------------------------------------------------------- /example-app/models/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world" 3 | } 4 | -------------------------------------------------------------------------------- /lib/ResolverPlugin.js: -------------------------------------------------------------------------------- 1 | var resolve = require('path').resolve; 2 | var createBabelResolver = require('babel-resolver'); 3 | var findNodeModulesPaths = require('babel-resolver/lib/findNodeModulesPaths'); 4 | var getFilenameFromBabelOpts = require('./getFilenameFromBabelOpts'); 5 | var getPluginOptsFromBabelOpts = require('./getPluginOptsFromBabelOpts'); 6 | var getAbsoluteDirsFromInputDirs = require('./getAbsoluteDirsFromInputDirs'); 7 | 8 | module.exports = function ResolverPlugin() { 9 | var resolver; 10 | return { 11 | manipulateOptions: function (babelOpts) { 12 | if (!resolver) { 13 | var filename = getFilenameFromBabelOpts(babelOpts); 14 | if (!filename) { return null; } 15 | 16 | var pluginOpts = getPluginOptsFromBabelOpts(babelOpts); 17 | var nodeModulesPaths = findNodeModulesPaths(filename); 18 | var appRootPath = resolve(nodeModulesPaths[0], '..'); 19 | var absoluteDirs = getAbsoluteDirsFromInputDirs(pluginOpts.resolveDirs, appRootPath); 20 | resolver = createBabelResolver.apply(null, absoluteDirs); 21 | } 22 | 23 | var previousResolver = babelOpts.resolveModuleSource; 24 | var hasPreviousResolver = typeof previousResolver === 'function'; 25 | 26 | babelOpts.resolveModuleSource = function (source) { 27 | var resolvedFromPrevious = hasPreviousResolver ? 28 | previousResolver.apply(null, arguments) : 29 | source; 30 | return resolver(resolvedFromPrevious); 31 | }; 32 | } 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/getAbsoluteDirsFromInputDirs.js: -------------------------------------------------------------------------------- 1 | var resolve = require('path').resolve; 2 | var pathExists = require('babel-resolver/lib/pathExists'); 3 | var pathIsRelative = require('babel-resolver/lib/pathIsRelative'); 4 | 5 | module.exports = function getAbsoluteDirsFromInputDirs(inputDirs, appRootPath) { 6 | return inputDirs.reduce(function (absoluteDirs, inputDir) { 7 | var fromAppRoot = resolve(appRootPath, inputDir); 8 | 9 | if (pathExists(fromAppRoot)) { 10 | return absoluteDirs.concat(fromAppRoot); 11 | } 12 | 13 | if (!pathIsRelative(inputDir) && pathExists(inputDir)) { 14 | return absoluteDirs.concat(inputDir); 15 | } 16 | 17 | throw new Error('ResolverPlugin Error: Unable to find path: "' + inputDir + '". Make sure it is either a valid relative path from your project\'s root directory or a valid absolute path.'); 18 | }, []); 19 | }; 20 | -------------------------------------------------------------------------------- /lib/getFilenameFromBabelOpts.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | module.exports = function getFilenameFromBabelOpts(babelOpts) { 4 | try { 5 | fs.lstatSync(babelOpts.filename); 6 | } catch (e) { 7 | // File doesn't exist 8 | return null; 9 | } 10 | 11 | return babelOpts.filename; 12 | }; 13 | -------------------------------------------------------------------------------- /lib/getPluginOptsFromBabelOpts.js: -------------------------------------------------------------------------------- 1 | var find = require('lodash/find'); 2 | var isArray = require('lodash/isArray'); 3 | var isObject = require('lodash/isObject'); 4 | 5 | // Sadly this is the only way to get a plugin's options from within `manipulateOptions`... 6 | // In `pre()`, they can be accessed by `this.opts`, but unfortunately `manipulateOptions` gets 7 | // called *before* `pre()` and isn't supplied with `this` 8 | module.exports = function getPluginOptsFromBabelOpts(babelOpts) { 9 | var plugin = find(babelOpts.plugins, function (plugin) { 10 | // hack: assume it's the correct plugin if it's options have a `resolveDirs` property 11 | return isArray(plugin) && 12 | isObject(plugin[1]) && 13 | isArray(plugin[1].resolveDirs); 14 | }); 15 | 16 | if (!plugin) { 17 | throw new Error('ResolverPlugin Error: Invalid (or no) options passed. Make sure your plugin config looks something like this: ["resolver", { "resolveDirs": ["lib"] }]'); 18 | } 19 | 20 | return plugin[1]; 21 | }; 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-resolver", 3 | "version": "1.1.0", 4 | "description": "Resolve modules from any directory.", 5 | "main": "lib/ResolverPlugin.js", 6 | "scripts": { 7 | "test": "npm run clear-cache && npm run lint && node_modules/.bin/mocha test/**/*.test.js", 8 | "lint": "./node_modules/.bin/eslint lib test", 9 | "cover": "npm run istanbul && npm run coveralls", 10 | "istanbul": "node node_modules/.bin/istanbul cover node_modules/.bin/_mocha --report lcovonly", 11 | "coveralls": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", 12 | "clear-cache": "rm -f ~/.babel.json", 13 | "link-self": "rm -rf node_modules/babel-plugin-resolver && ln -s . node_modules/babel-plugin-resolver" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/jshanson7/babel-plugin-resolver.git" 18 | }, 19 | "keywords": [ 20 | "babel", 21 | "resolve", 22 | "local", 23 | "module", 24 | "source", 25 | "resolveModuleSource", 26 | "NODE_PATH", 27 | "import" 28 | ], 29 | "author": "Jeff Hanson", 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/jshanson7/babel-plugin-resolver/issues" 33 | }, 34 | "homepage": "https://github.com/jshanson7/babel-plugin-resolver#readme", 35 | "dependencies": { 36 | "babel-resolver": "^1.1.0", 37 | "lodash": "^4.6.0" 38 | }, 39 | "devDependencies": { 40 | "babel-eslint": "^6.1.2", 41 | "babel-plugin-resolver": "file:.", 42 | "babel-preset-es2015": "^6.13.2", 43 | "babel-register": "^6.11.6", 44 | "coveralls": "^2.11.4", 45 | "eslint": "^1.9.0", 46 | "eslint-config-standard": "^4.4.0", 47 | "eslint-plugin-babel": "^2.1.1", 48 | "eslint-plugin-standard": "^1.3.1", 49 | "istanbul": "^0.4.0", 50 | "mocha": "^2.3.3" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/babelPluginResolver.test.js: -------------------------------------------------------------------------------- 1 | describe('babelResolver()', function () { 2 | it('works', function () { 3 | require('../example-app/app'); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --timeout 10000 2 | --require babel-register 3 | --------------------------------------------------------------------------------