Skip to content

Commit 3dd29a0

Browse files
authored
feat: Add support for custom root directories (#69)
* feat: Add support for custom root directories Fixes #46 BREAKING CHANGE: There's a new way to specify alias in the plugin options. See README for more info
1 parent e5ef1f6 commit 3dd29a0

13 files changed

Lines changed: 245 additions & 219 deletions

File tree

.editorconfig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@ indent_style = space
66
indent_size = 4
77

88
[*.{json,yml}]
9-
indent_style = space
9+
indent_size = 2
10+
11+
[.*]
1012
indent_size = 2

.eslintrc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
2-
"extends": "airbnb-base",
3-
"rules": {
4-
"comma-dangle": 0,
5-
"indent": [2, 4, {"SwitchCase": 1}],
6-
"max-len": 0,
7-
}
2+
"extends": "airbnb-base",
3+
"rules": {
4+
"comma-dangle": 0,
5+
"indent": [2, 4, {"SwitchCase": 1}],
6+
"max-len": 0
7+
}
88
}

README.md

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
# babel-plugin-module-alias
1+
# babel-plugin-module-resolver
22
[![npm][npm-version-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Coverage Status][coverage-image]][coverage-url]
33

4-
A [babel](http://babeljs.io) plugin to rewrite (map, alias, resolve) directories as different directories during the Babel process. It's particularly useful when you have files you don't want to use with relative paths (especially in big projects).
4+
A [babel](http://babeljs.io) plugin to add a new resolver for your modules when compiling your code using Babel. The plugin allows you to add new "root" directories that contains your modules. It also allows your to setup custom alias which can also be directories or specific files, or even other npm modules.
55

66
## Description
77

8-
Instead of using relative paths in your project, you'll be able to use an alias. Here an simple example:
8+
The reason of this plugin is to simplify the require/import paths in your project. Therefore, instead of using complex relative paths like `../../../../utils/my-utils`, you would be able to write `utils/my-utils`. It will allow you to work faster since you won't need to calculate how many levels of directory you have to go up before accessing the file.
9+
10+
Here's a full example:
911
```js
1012
// Instead of using this;
1113
import MyUtilFn from '../../../../utils/MyUtilFn';
1214
// Use that:
1315
import MyUtilFn from 'utils/MyUtilFn';
1416
```
15-
With this plugin, you'll be able to map files or directories to the path you want.
1617

1718
_Note:_ It also work for `require()`.
1819

@@ -24,34 +25,45 @@ _Note 2:_ You can use the `npm:` prefix in your plugin configuration to map a no
2425
Install the plugin
2526

2627
```
27-
$ npm install --save-dev babel babel-plugin-module-alias
28+
$ npm install --save-dev babel-plugin-module-resolver
2829
```
2930

30-
Specify the plugin in your `.babelrc` with the custom mapping.
31+
32+
Specify the plugin in your `.babelrc` with the custom root or alias. Here's an example:
3133
```json
3234
{
3335
"plugins": [
34-
["module-alias", [
35-
{ "src": "./src/utils", "expose": "utils" },
36-
{ "src": "./src/components", "expose": "awesome/components" },
37-
{ "src": "npm:lodash", "expose": "underscore" }
38-
]]
39-
]
36+
"transform-object-rest-spread",
37+
["module-resolver", {
38+
"root": ["./src"],
39+
"alias": {
40+
"test": "./test"
41+
}
42+
}]
43+
]
4044
}
4145
```
4246

43-
If you're using [eslint-plugin-import][eslint-plugin-import], you should use [eslint-import-resolver-babel-module-alias][resolver-module-alias] to avoid having false errors.
47+
## ESLint plugin
48+
49+
If you're using ESLint, you should use the [eslint-plugin-import][eslint-plugin-import], and this [eslint-import-resolver-babel-module-resolver][eslint-import-resolver-babel-module-resolver] in order to remove falsy unresolved modules.
50+
51+
## Editors autocompletion
52+
53+
- Atom: Uses [atom-autocomplete-modules][atom-autocomplete-modules] and enable the `babel-plugin-module-resolver` option.
54+
- IntelliJ/WebStorm: You can add custom resources root directories, make sure it matches what you have in this plugin.
4455

4556
## License
4657

4758
MIT, see [LICENSE.md](/LICENSE.md) for details.
4859

4960

50-
[ci-image]: https://circleci.com/gh/tleunen/babel-plugin-module-alias.svg?style=shield
51-
[ci-url]: https://circleci.com/gh/tleunen/babel-plugin-module-alias
52-
[coverage-image]: https://codecov.io/gh/tleunen/babel-plugin-module-alias/branch/master/graph/badge.svg
53-
[coverage-url]: https://codecov.io/gh/tleunen/babel-plugin-module-alias
54-
[npm-version-image]: https://img.shields.io/npm/v/babel-plugin-module-alias.svg
55-
[npm-url]: https://www.npmjs.com/package/babel-plugin-module-alias
56-
[resolver-module-alias]: https://github.com/tleunen/eslint-import-resolver-babel-module-alias
61+
[ci-image]: https://circleci.com/gh/tleunen/babel-plugin-module-resolver.svg?style=shield
62+
[ci-url]: https://circleci.com/gh/tleunen/babel-plugin-module-resolver
63+
[coverage-image]: https://codecov.io/gh/tleunen/babel-plugin-module-resolver/branch/master/graph/badge.svg
64+
[coverage-url]: https://codecov.io/gh/tleunen/babel-plugin-module-resolver
65+
[npm-version-image]: https://img.shields.io/npm/v/babel-plugin-module-resolver.svg
66+
[npm-url]: https://www.npmjs.com/package/babel-plugin-module-resolver
67+
[eslint-import-resolver-babel-module-resolver]: https://github.com/tleunen/eslint-import-resolver-babel-module-resolver
5768
[eslint-plugin-import]: https://github.com/benmosher/eslint-plugin-import
69+
[atom-autocomplete-modules]: https://github.com/nkt/atom-autocomplete-modules

codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
comment:
2-
layout: "header, diff, tree"
2+
layout: "header, tree"

package.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
2-
"name": "babel-plugin-module-alias",
2+
"name": "babel-plugin-module-resolver",
33
"version": "1.6.0",
44
"main": "lib/index.js",
55
"description": "Babel plugin to rewrite the path in require() and ES6 import",
66
"repository": {
77
"type": "git",
8-
"url": "https://github.com/tleunen/babel-plugin-module-alias.git"
8+
"url": "https://github.com/tleunen/babel-plugin-module-resolver.git"
99
},
1010
"author": {
1111
"name": "Tommy Leunen",
@@ -17,6 +17,7 @@
1717
"babel",
1818
"babel-plugin",
1919
"module",
20+
"resolver",
2021
"alias",
2122
"rewrite",
2223
"resolve",
@@ -25,19 +26,18 @@
2526
"require",
2627
"import"
2728
],
28-
"dependencies": {},
2929
"devDependencies": {
3030
"babel-cli": "^6.10.1",
3131
"babel-core": "^6.10.4",
32-
"babel-plugin-istanbul": "^1.1.0",
32+
"babel-plugin-istanbul": "^2.0.0",
3333
"babel-preset-es2015": "^6.6.0",
3434
"babel-register": "^6.8.0",
3535
"cross-env": "^2.0.0",
36-
"eslint": "^3.0.1",
37-
"eslint-config-airbnb-base": "^5.0.0",
38-
"eslint-plugin-import": "^1.10.2",
39-
"mocha": "^3.0.0",
40-
"nyc": "^7.0.0",
36+
"eslint": "^3.3.0",
37+
"eslint-config-airbnb-base": "^5.0.2",
38+
"eslint-plugin-import": "^1.13.0",
39+
"mocha": "^3.0.2",
40+
"nyc": "^8.1.0",
4141
"standard-version": "^2.4.0"
4242
},
4343
"scripts": {

src/index.js

Lines changed: 36 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,54 @@
1-
const path = require('path');
2-
3-
function createFilesMap(state) {
4-
const result = {};
5-
const opts = Array.isArray(state.opts)
6-
? state.opts
7-
: [state.opts];
8-
9-
opts.forEach(moduleMapData => {
10-
result[moduleMapData.expose] = moduleMapData.src;
11-
});
12-
13-
return result;
14-
}
15-
16-
function resolve(filename) {
17-
if (path.isAbsolute(filename)) return filename;
18-
return path.resolve(process.cwd(), filename);
19-
}
20-
21-
function toPosixPath(modulePath) {
22-
return modulePath.replace(/\\/g, '/');
1+
import path from 'path';
2+
import mapToRelative from './mapToRelative';
3+
4+
function createAliasFileMap(pluginOpts) {
5+
const alias = pluginOpts.alias || {};
6+
return Object.keys(alias).reduce((memo, expose) => (
7+
Object.assign(memo, {
8+
[expose]: alias[expose]
9+
})
10+
), {});
2311
}
2412

25-
export function mapToRelative(currentFile, module) {
26-
let from = path.dirname(currentFile);
27-
let to = path.normalize(module);
28-
29-
from = resolve(from);
30-
to = resolve(to);
31-
32-
let moduleMapped = path.relative(from, to);
33-
34-
moduleMapped = toPosixPath(moduleMapped);
35-
36-
// Support npm modules instead of directories
37-
if (moduleMapped.indexOf('npm:') !== -1) {
38-
const [, npmModuleName] = moduleMapped.split('npm:');
39-
return npmModuleName;
13+
export function mapModule(source, file, pluginOpts) {
14+
// Do not map source starting with a dot
15+
if (source[0] === '.') {
16+
return null;
4017
}
4118

42-
if (moduleMapped[0] !== '.') moduleMapped = `./${moduleMapped}`;
43-
44-
return moduleMapped;
45-
}
19+
// Search the file under the custom root directories
20+
const rootDirs = pluginOpts.root || [];
21+
for (let i = 0; i < rootDirs.length; i++) {
22+
try {
23+
// check if the file exists (will throw if not)
24+
const p = path.resolve(rootDirs[i], source);
25+
require.resolve(p);
26+
return mapToRelative(file, p);
27+
} catch (e) {
28+
// empty...
29+
}
30+
}
4631

47-
export function mapModule(source, file, filesMap) {
32+
// The source file wasn't found in any of the root directories. Lets try the alias
33+
const aliasMapping = createAliasFileMap(pluginOpts);
4834
const moduleSplit = source.split('/');
4935

50-
let src;
36+
let aliasPath;
5137
while (moduleSplit.length) {
5238
const m = moduleSplit.join('/');
53-
if ({}.hasOwnProperty.call(filesMap, m)) {
54-
src = filesMap[m];
39+
if ({}.hasOwnProperty.call(aliasMapping, m)) {
40+
aliasPath = aliasMapping[m];
5541
break;
5642
}
5743
moduleSplit.pop();
5844
}
5945

60-
if (!moduleSplit.length) {
61-
// no mapping available
46+
// no alias mapping found
47+
if (!aliasPath) {
6248
return null;
6349
}
6450

65-
const newPath = source.replace(moduleSplit.join('/'), src);
51+
const newPath = source.replace(moduleSplit.join('/'), aliasPath);
6652
return mapToRelative(file, newPath);
6753
}
6854

@@ -81,8 +67,7 @@ export default ({ types: t }) => {
8167

8268
const moduleArg = nodePath.node.arguments[0];
8369
if (moduleArg && moduleArg.type === 'StringLiteral') {
84-
const filesMap = createFilesMap(state);
85-
const modulePath = mapModule(moduleArg.value, state.file.opts.filename, filesMap);
70+
const modulePath = mapModule(moduleArg.value, state.file.opts.filename, state.opts);
8671
if (modulePath) {
8772
nodePath.replaceWith(t.callExpression(
8873
nodePath.node.callee, [t.stringLiteral(modulePath)]
@@ -94,8 +79,7 @@ export default ({ types: t }) => {
9479
function transformImportCall(nodePath, state) {
9580
const moduleArg = nodePath.node.source;
9681
if (moduleArg && moduleArg.type === 'StringLiteral') {
97-
const filesMap = createFilesMap(state);
98-
const modulePath = mapModule(moduleArg.value, state.file.opts.filename, filesMap);
82+
const modulePath = mapModule(moduleArg.value, state.file.opts.filename, state.opts);
9983
if (modulePath) {
10084
nodePath.replaceWith(t.importDeclaration(
10185
nodePath.node.specifiers,

src/mapToRelative.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import path from 'path';
2+
3+
function resolve(filename) {
4+
if (path.isAbsolute(filename)) return filename;
5+
return path.resolve(process.cwd(), filename);
6+
}
7+
8+
function toPosixPath(modulePath) {
9+
return modulePath.replace(/\\/g, '/');
10+
}
11+
12+
export default function mapToRelative(currentFile, module) {
13+
let from = path.dirname(currentFile);
14+
let to = path.normalize(module);
15+
16+
from = resolve(from);
17+
to = resolve(to);
18+
19+
let moduleMapped = path.relative(from, to);
20+
21+
moduleMapped = toPosixPath(moduleMapped);
22+
23+
// Support npm modules instead of directories
24+
if (moduleMapped.indexOf('npm:') !== -1) {
25+
const [, npmModuleName] = moduleMapped.split('npm:');
26+
return npmModuleName;
27+
}
28+
29+
if (moduleMapped[0] !== '.') moduleMapped = `./${moduleMapped}`;
30+
31+
return moduleMapped;
32+
}

test/examples/components/c1.js

Whitespace-only changes.

test/examples/components/c2.js

Whitespace-only changes.

test/examples/components/sub/sub1.js

Whitespace-only changes.

0 commit comments

Comments
 (0)