Skip to content

Commit

Permalink
feat: Process alias and regexps together (#5)
Browse files Browse the repository at this point in the history
Breaking change: "npm:" support is dropped.
Breaking change: package-like aliases are additionally resolved using
root and cwd.
Breaking change: relative aliases are left as-is to be consistend with
how `getRealPath` treats relative paths.
  • Loading branch information
fatfisz committed Apr 1, 2017
1 parent 2ba5eaf commit 9dfd7b8
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 165 deletions.
85 changes: 27 additions & 58 deletions src/getRealPath.js
Expand Up @@ -6,12 +6,12 @@ import mapToRelative from 'mapToRelative';
import { toLocalPath, toPosixPath, replaceExtension } from 'utils';


function findPathInRoots(sourcePath, rootDirs, extensions) {
// Search the source path inside every custom root directory
function findPathInRoots(sourcePath, { extensions, root }) {
let resolvedSourceFile;
rootDirs.some((basedir) => {

root.some((basedir) => {
try {
// check if the file exists (will throw if not)
// Check if the file exists (will throw if not)
resolvedSourceFile = requireResolve.sync(`./${sourcePath}`, {
basedir,
extensions,
Expand All @@ -25,8 +25,8 @@ function findPathInRoots(sourcePath, rootDirs, extensions) {
return resolvedSourceFile;
}

function getRealPathFromRootConfig(sourcePath, absCurrentFile, rootDirs, cwd, extensions) {
const absFileInRoot = findPathInRoots(sourcePath, rootDirs, extensions);
function getRealPathFromRootConfig(sourcePath, currentFile, opts) {
const absFileInRoot = findPathInRoots(sourcePath, opts);

if (!absFileInRoot) {
return null;
Expand All @@ -35,48 +35,18 @@ function getRealPathFromRootConfig(sourcePath, absCurrentFile, rootDirs, cwd, ex
const realSourceFileExtension = extname(absFileInRoot);
const sourceFileExtension = extname(sourcePath);

// map the source and keep its extension if the import/require had one
// Map the source and keep its extension if the import/require had one
const ext = realSourceFileExtension === sourceFileExtension ? realSourceFileExtension : '';
return toLocalPath(toPosixPath(replaceExtension(
mapToRelative(cwd, absCurrentFile, absFileInRoot),
mapToRelative(opts.cwd, currentFile, absFileInRoot),
ext,
)));
}

function getRealPathFromAliasConfig(sourcePath, absCurrentFile, alias, cwd) {
const moduleSplit = sourcePath.split('/');

let aliasPath;
while (moduleSplit.length) {
const m = moduleSplit.join('/');
if ({}.hasOwnProperty.call(alias, m)) {
aliasPath = alias[m];
break;
}
moduleSplit.pop();
}

// no alias mapping found
if (!aliasPath) {
return null;
}

// remove legacy "npm:" prefix for npm packages
aliasPath = aliasPath.replace(/^(npm:)/, '');
const newPath = sourcePath.replace(moduleSplit.join('/'), aliasPath);

// alias to npm module don't need relative mapping
if (aliasPath[0] !== '.') {
return newPath;
}

return toLocalPath(toPosixPath(mapToRelative(cwd, absCurrentFile, newPath)));
}

function getRealPathFromRegExpConfig(sourcePath, regExps) {
function getRealPathFromAliasConfig(sourcePath, currentFile, opts) {
let aliasedSourceFile;

regExps.find(([regExp, substitute]) => {
opts.alias.find(([regExp, substitute]) => {
const execResult = regExp.exec(sourcePath);

if (execResult === null) {
Expand All @@ -87,6 +57,19 @@ function getRealPathFromRegExpConfig(sourcePath, regExps) {
return true;
});

if (!aliasedSourceFile) {
return null;
}

if (aliasedSourceFile[0] === '.') {
return aliasedSourceFile;
}

const realPathFromRoot = getRealPathFromRootConfig(aliasedSourceFile, currentFile, opts);
if (realPathFromRoot) {
return realPathFromRoot;
}

return aliasedSourceFile;
}

Expand All @@ -95,33 +78,19 @@ export default function getRealPath(sourcePath, { file, opts }) {
return sourcePath;
}

// file param is a relative path from the environment current working directory
// File param is a relative path from the environment current working directory
// (not from cwd param)
const currentFile = file.opts.filename;
const absCurrentFile = resolve(currentFile);

const { cwd, root, extensions, alias, regExps } = opts;
const currentFile = resolve(file.opts.filename);

const sourceFileFromRoot = getRealPathFromRootConfig(
sourcePath, absCurrentFile, root, cwd, extensions,
);
const sourceFileFromRoot = getRealPathFromRootConfig(sourcePath, currentFile, opts);
if (sourceFileFromRoot) {
return sourceFileFromRoot;
}

const sourceFileFromAlias = getRealPathFromAliasConfig(
sourcePath, absCurrentFile, alias, cwd,
);
const sourceFileFromAlias = getRealPathFromAliasConfig(sourcePath, currentFile, opts);
if (sourceFileFromAlias) {
return sourceFileFromAlias;
}

const sourceFileFromRegExp = getRealPathFromRegExpConfig(
sourcePath, regExps,
);
if (sourceFileFromRegExp) {
return sourceFileFromRegExp;
}

return sourcePath;
}
94 changes: 4 additions & 90 deletions src/index.js
@@ -1,96 +1,8 @@
import fs from 'fs';
import { dirname, resolve } from 'path';

import findBabelConfig from 'find-babel-config';
import glob from 'glob';

import normalizeOptions from 'normalizeOptions';
import transformCall from 'transformers/call';
import transformImport from 'transformers/import';


const defaultExtensions = ['.js', '.jsx', '.es', '.es6'];

function isRegExp(string) {
return string.startsWith('^') || string.endsWith('$');
}

function normalizeCwd(file) {
const { opts } = this;

if (opts.cwd === 'babelrc') {
const startPath = (file.opts.filename === 'unknown')
? './'
: file.opts.filename;

const { file: babelPath } = findBabelConfig.sync(startPath);

opts.cwd = babelPath
? dirname(babelPath)
: null;
}

if (!opts.cwd) {
opts.cwd = process.cwd();
}
}

function normalizePluginOptions(file) {
const { opts } = this;

normalizeCwd.call(this, file);

if (opts.root) {
if (!Array.isArray(opts.root)) {
opts.root = [opts.root];
}

opts.root = opts.root
.map(dirPath => resolve(opts.cwd, dirPath))
.reduce((resolvedDirs, absDirPath) => {
if (glob.hasMagic(absDirPath)) {
const roots = glob.sync(absDirPath)
.filter(path => fs.lstatSync(path).isDirectory());

return [...resolvedDirs, ...roots];
}

return [...resolvedDirs, absDirPath];
}, []);
} else {
opts.root = [];
}

opts.regExps = [];

if (opts.alias) {
Object.keys(opts.alias)
.filter(isRegExp)
.forEach((key) => {
const parts = opts.alias[key].split('\\\\');

function substitute(execResult) {
return parts
.map(part =>
part.replace(/\\\d+/g, number => execResult[number.slice(1)] || ''),
)
.join('\\');
}

opts.regExps.push([new RegExp(key), substitute]);

delete opts.alias[key];
});
} else {
opts.alias = {};
}

if (!opts.extensions) {
opts.extensions = defaultExtensions;
}

return opts;
}

export default ({ types }) => {
const importVisitors = {
CallExpression(nodePath, state) {
Expand All @@ -105,7 +17,9 @@ export default ({ types }) => {
};

return {
pre: normalizePluginOptions,
pre(file) {
normalizeOptions(this.opts, file);
},

visitor: {
Program: {
Expand Down
96 changes: 96 additions & 0 deletions src/normalizeOptions.js
@@ -0,0 +1,96 @@
import fs from 'fs';
import { dirname, resolve } from 'path';

import findBabelConfig from 'find-babel-config';
import glob from 'glob';


const defaultExtensions = ['.js', '.jsx', '.es', '.es6'];

function isRegExp(string) {
return string.startsWith('^') || string.endsWith('$');
}

function normalizeCwd(opts, file) {
if (opts.cwd === 'babelrc') {
const startPath = (file.opts.filename === 'unknown')
? './'
: file.opts.filename;

const { file: babelPath } = findBabelConfig.sync(startPath);

opts.cwd = babelPath
? dirname(babelPath)
: null;
}

if (!opts.cwd) {
opts.cwd = process.cwd();
}
}

function normalizeRoot(opts) {
if (opts.root) {
if (!Array.isArray(opts.root)) {
opts.root = [opts.root];
}

opts.root = opts.root
.map(dirPath => resolve(opts.cwd, dirPath))
.reduce((resolvedDirs, absDirPath) => {
if (glob.hasMagic(absDirPath)) {
const roots = glob.sync(absDirPath)
.filter(path => fs.lstatSync(path).isDirectory());

return [...resolvedDirs, ...roots];
}

return [...resolvedDirs, absDirPath];
}, []);
} else {
opts.root = [];
}
}

function getAliasPair(key, value) {
const parts = value.split('\\\\');

function substitute(execResult) {
return parts
.map(part =>
part.replace(/\\\d+/g, number => execResult[number.slice(1)] || ''),
)
.join('\\');
}

return [new RegExp(key), substitute];
}

function normalizeAlias(opts) {
if (opts.alias) {
const { alias } = opts;
const aliasKeys = Object.keys(alias);

const nonRegExpAliases = aliasKeys
.filter(key => !isRegExp(key))
.map(key => getAliasPair(`^${key}((?:/|).*)`, `${alias[key]}\\1`));

const regExpAliases = aliasKeys
.filter(isRegExp)
.map(key => getAliasPair(key, alias[key]));

opts.alias = [...nonRegExpAliases, ...regExpAliases];
} else {
opts.alias = [];
}
}

export default function normalizeOptions(opts, file) {
normalizeCwd(opts, file);
normalizeRoot(opts);
normalizeAlias(opts);

if (!opts.extensions) {
opts.extensions = defaultExtensions;
}
}

0 comments on commit 9dfd7b8

Please sign in to comment.