diff --git a/.babelrc b/.babelrc index c4fb0a9..7cb2943 100644 --- a/.babelrc +++ b/.babelrc @@ -1,5 +1,8 @@ { "presets": ["es2015"], + "plugins": [ + "transform-object-rest-spread" + ], "env": { "test": { "plugins": ["istanbul"] diff --git a/.eslintrc b/.eslintrc index c391ddc..5644d57 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,10 @@ { "extends": "airbnb-base", + "parserOptions": { + "ecmaFeatures": { + "experimentalObjectRestSpread": true + } + }, "rules": { "comma-dangle": 0, "indent": [2, 4, {"SwitchCase": 1}], diff --git a/package.json b/package.json index 5eb9333..cfd93df 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "babel-cli": "^6.10.1", "babel-core": "^6.10.4", "babel-plugin-istanbul": "^2.0.0", + "babel-plugin-transform-object-rest-spread": "^6.8.0", "babel-preset-es2015": "^6.6.0", "babel-register": "^6.8.0", "cross-env": "^2.0.0", diff --git a/src/index.js b/src/index.js index 942b0be..9b308cc 100644 --- a/src/index.js +++ b/src/index.js @@ -2,6 +2,7 @@ import path from 'path'; import resolve from 'resolve'; import glob from 'glob'; import mapToRelative from './mapToRelative'; +import { toLocalPath } from './utils'; function createAliasFileMap(pluginOpts) { const alias = pluginOpts.alias || {}; @@ -36,7 +37,7 @@ export function mapModule(source, file, pluginOpts) { const sourceFileExt = path.extname(source); // map the source and keep its extension if the import/require had one const ext = realFileExt === sourceFileExt ? realFileExt : ''; - return mapToRelative(file, replaceExt(fileAbsPath, ext)); + return toLocalPath(replaceExt(mapToRelative(file, fileAbsPath), ext)); } catch (e) { // empty... } @@ -70,7 +71,7 @@ export function mapModule(source, file, pluginOpts) { return newPath; } // relative alias - return mapToRelative(file, newPath); + return toLocalPath(mapToRelative(file, newPath)); } diff --git a/src/mapToRelative.js b/src/mapToRelative.js index 9138911..1aedbf7 100644 --- a/src/mapToRelative.js +++ b/src/mapToRelative.js @@ -1,14 +1,11 @@ import path from 'path'; +import { toPosixPath } from './utils'; function resolve(filename) { if (path.isAbsolute(filename)) return filename; return path.resolve(process.cwd(), filename); } -function toPosixPath(modulePath) { - return modulePath.replace(/\\/g, '/'); -} - export default function mapToRelative(currentFile, module) { let from = path.dirname(currentFile); let to = path.normalize(module); @@ -16,11 +13,6 @@ export default function mapToRelative(currentFile, module) { from = resolve(from); to = resolve(to); - let moduleMapped = path.relative(from, to); - - moduleMapped = toPosixPath(moduleMapped); - - if (moduleMapped[0] !== '.') moduleMapped = `./${moduleMapped}`; - - return moduleMapped; + const moduleMapped = path.relative(from, to); + return toPosixPath(moduleMapped); } diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..b52e5d9 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,9 @@ +export function toPosixPath(modulePath) { + return modulePath.replace(/\\/g, '/'); +} + +export function toLocalPath(p) { + return (p[0] !== '.') + ? `./${p}` + : p; +} diff --git a/test/examples/foo/bar.js b/test/examples/foo/bar.js new file mode 100644 index 0000000..e69de29 diff --git a/test/examples/foo/bar/index.js b/test/examples/foo/bar/index.js new file mode 100644 index 0000000..e69de29 diff --git a/test/examples/foo/bar/x.js b/test/examples/foo/bar/x.js new file mode 100644 index 0000000..e69de29 diff --git a/test/index.js b/test/index.js index f36ff06..cbfc67b 100644 --- a/test/index.js +++ b/test/index.js @@ -1,109 +1,124 @@ /* eslint-env mocha */ -/* eslint-disable prefer-arrow-callback */ -/* eslint-disable func-names */ import assert from 'assert'; import { transform } from 'babel-core'; // eslint-disable-line import/no-extraneous-dependencies import plugin from '../src'; -function testRequireImport(source, output, transformerOpts) { - it('with a require statement', function () { - const code = `var something = require("${source}");`; - const result = transform(code, transformerOpts); +describe('module-resolver', () => { + function testRequireImport(source, output, transformerOpts) { + it('with a require statement', () => { + const code = `var something = require("${source}");`; + const result = transform(code, transformerOpts); - assert.strictEqual(result.code, `var something = require("${output}");`); - }); - - it('with an import statement', function () { - const code = `import something from "${source}";`; - const result = transform(code, transformerOpts); + assert.strictEqual(result.code, `var something = require("${output}");`); + }); - assert.strictEqual(result.code, `import something from "${output}";`); - }); -} - -describe('root', function () { - const transformerOpts = { - plugins: [ - [plugin, { - root: ['./test/examples/components'] - }] - ] - }; - - const transformerOptsGlob = { - plugins: [ - [plugin, { - root: ['./test/**/components'] - }] - ] - }; - - describe('should rewrite the file path inside a root directory', function () { - testRequireImport( - 'c1', - './test/examples/components/c1', - transformerOpts - ); - }); + it('with an import statement', () => { + const code = `import something from "${source}";`; + const result = transform(code, transformerOpts); - describe('should rewrite the sub file path inside a root directory', function () { - testRequireImport( - 'sub/sub1', - './test/examples/components/sub/sub1', - transformerOpts - ); - }); + assert.strictEqual(result.code, `import something from "${output}";`); + }); + } + + describe('root', () => { + const transformerOpts = { + babelrc: false, + plugins: [ + [plugin, { + root: [ + './test/examples/components', + './test/examples/foo' + ] + }] + ] + }; + + const transformerOptsGlob = { + plugins: [ + [plugin, { + root: ['./test/**/components'] + }] + ] + }; + + describe('should resolve the file path', () => { + testRequireImport( + 'c1', + './test/examples/components/c1', + transformerOpts + ); + }); - describe('should rewrite the file while keeping the extension', function () { - testRequireImport( - 'sub/sub1.css', - './test/examples/components/sub/sub1.css', - transformerOpts - ); - }); + describe('should resolve the sub file path', () => { + testRequireImport( + 'sub/sub1', + './test/examples/components/sub/sub1', + transformerOpts + ); + }); - describe('should rewrite the file with a filename containing a dot', function () { - testRequireImport( - 'sub/custom.modernizr3', - './test/examples/components/sub/custom.modernizr3', - transformerOpts - ); - }); + describe('should resolve the file path while keeping the extension', () => { + testRequireImport( + 'sub/sub1.css', + './test/examples/components/sub/sub1.css', + transformerOpts + ); + }); - describe('should not rewrite a path outisde of the root directory', function () { - testRequireImport( - 'example-file', - 'example-file', - transformerOpts - ); - }); + describe('should resolve the file path with a filename containing a dot', () => { + testRequireImport( + 'sub/custom.modernizr3', + './test/examples/components/sub/custom.modernizr3', + transformerOpts + ); + }); - describe('should rewrite the file path inside a root directory according to glob', function () { - testRequireImport( - 'c1', - './test/examples/components/c1', - transformerOptsGlob - ); - }); -}); + describe('should resolve the file path according to a glob', () => { + testRequireImport( + 'c1', + './test/examples/components/c1', + transformerOptsGlob + ); + }); -describe('alias', function () { - const transformerOpts = { - plugins: [ - [plugin, { - alias: { - utils: './src/mylib/subfolder/utils', - 'awesome/components': './src/components', - abstract: 'npm:concrete', - underscore: 'lodash' + describe('should resolve to a file instead of a directory', () => { + // When a file and a directory on the same level share the same name, + // the file has priority according to the Node require mechanism + testRequireImport( + 'bar', + '../bar', + { + ...transformerOpts, + filename: './test/examples/foo/bar/x.js' } - }] - ] - }; + ); + }); - describe('should alias a known path', function () { - describe('using a simple exposed name', function () { - describe('when requiring the exact name', function () { + describe('should not resolve a path outisde of the root directory', () => { + testRequireImport( + 'example-file', + 'example-file', + transformerOpts + ); + }); + }); + + describe('alias', () => { + const transformerOpts = { + plugins: [ + [plugin, { + alias: { + utils: './src/mylib/subfolder/utils', + 'awesome/components': './src/components', + abstract: 'npm:concrete', + underscore: 'lodash' + } + }] + ] + }; + + describe('with a simple alias', () => { + describe('should alias the file path', () => { testRequireImport( 'utils', './src/mylib/subfolder/utils', @@ -111,7 +126,7 @@ describe('alias', function () { ); }); - describe('when requiring a sub file of the exposed name', function () { + describe('should alias the sub file path', () => { testRequireImport( 'utils/my-util-file', './src/mylib/subfolder/utils/my-util-file', @@ -120,8 +135,8 @@ describe('alias', function () { }); }); - describe('using a "complex" exposed name', function () { - describe('when requiring the exact name', function () { + describe('with an alias containing a slash', () => { + describe('should alias the file path', () => { testRequireImport( 'awesome/components', './src/components', @@ -129,7 +144,7 @@ describe('alias', function () { ); }); - describe('when requiring a sub file of the exposed name', function () { + describe('should alias the sub file path', () => { testRequireImport( 'awesome/components/my-comp', './src/components/my-comp', @@ -138,54 +153,54 @@ describe('alias', function () { }); }); - describe('with a dot in the filename', function () { + describe('should alias a path containing a dot in the filename', () => { testRequireImport( 'utils/custom.modernizr3', './src/mylib/subfolder/utils/custom.modernizr3', transformerOpts ); }); - }); - - describe('should alias the path with its extension', function () { - testRequireImport( - 'awesome/components/my-comp.css', - './src/components/my-comp.css', - transformerOpts - ); - }); - describe('should not alias a unknown path', function () { - describe('when requiring a node module', function () { + describe('should alias the path with its extension', () => { testRequireImport( - 'other-lib', - 'other-lib', + 'awesome/components/my-comp.css', + './src/components/my-comp.css', transformerOpts ); }); - describe('when requiring a specific un-mapped file', function () { + describe('should not alias a unknown path', () => { + describe('when requiring a node module', () => { + testRequireImport( + 'other-lib', + 'other-lib', + transformerOpts + ); + }); + + describe('when requiring a specific un-mapped file', () => { + testRequireImport( + './l/otherLib', + './l/otherLib', + transformerOpts + ); + }); + }); + + describe('(legacy) should support aliasing a node module with "npm:"', () => { testRequireImport( - './l/otherLib', - './l/otherLib', + 'abstract/thing', + 'concrete/thing', transformerOpts ); }); - }); - - describe('(legacy) should support aliasing a node module with "npm:"', function () { - testRequireImport( - 'abstract/thing', - 'concrete/thing', - transformerOpts - ); - }); - describe('should support aliasing a node modules', function () { - testRequireImport( - 'underscore/map', - 'lodash/map', - transformerOpts - ); + describe('should support aliasing a node modules', () => { + testRequireImport( + 'underscore/map', + 'lodash/map', + transformerOpts + ); + }); }); });