Skip to content

Commit

Permalink
refactor: Refactor normalizeOptions and the resolver pipeline (#159)
Browse files Browse the repository at this point in the history
* Extract normalizeOptions from index.js
* Use the same signature for the resolvers and simplify the pipeline
* Add types to the state instead of passing it around
* Move visitors out of the exported function
* Add more tests documenting the current aliasing behavior
  • Loading branch information
fatfisz authored and tleunen committed Apr 9, 2017
1 parent ff641a0 commit e9f9bf6
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 161 deletions.
63 changes: 26 additions & 37 deletions src/getRealPath.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import mapToRelative from './mapToRelative';
import { toLocalPath, toPosixPath, replaceExtension } from './utils';


function findPathInRoots(sourcePath, rootDirs, extensions) {
function findPathInRoots(sourcePath, { extensions, root }) {
// Search the source path inside every custom root directory
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 = resolve.sync(`./${sourcePath}`, {
basedir,
extensions,
Expand All @@ -24,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 @@ -34,18 +35,18 @@ function getRealPathFromRootConfig(sourcePath, absCurrentFile, rootDirs, cwd, ex
const realSourceFileExtension = path.extname(absFileInRoot);
const sourceFileExtension = path.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) {
function getRealPathFromAliasConfig(sourcePath, currentFile, { alias, cwd }) {
const moduleSplit = sourcePath.split('/');

let aliasPath;

while (moduleSplit.length) {
const m = moduleSplit.join('/');
if ({}.hasOwnProperty.call(alias, m)) {
Expand All @@ -69,10 +70,10 @@ function getRealPathFromAliasConfig(sourcePath, absCurrentFile, alias, cwd) {
return newPath;
}

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

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

regExps.find(([regExp, substitute]) => {
Expand All @@ -89,38 +90,26 @@ function getRealPathFromRegExpConfig(sourcePath, regExps) {
return aliasedSourceFile;
}

const resolvers = [
getRealPathFromRootConfig,
getRealPathFromAliasConfig,
getRealPathFromRegExpConfig,
];

export default function getRealPath(sourcePath, { file, opts }) {
if (sourcePath[0] === '.') {
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 = path.resolve(currentFile);

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

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

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

const sourceFileFromRegExp = getRealPathFromRegExpConfig(
sourcePath, regExps,
);
if (sourceFileFromRegExp) {
return sourceFileFromRegExp;
}
resolvers.some((resolver) => {
resolvedPath = resolver(sourcePath, currentFile, opts);
return resolvedPath !== null;
});

return sourcePath;
return resolvedPath;
}
126 changes: 18 additions & 108 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,116 +1,26 @@
import fs from 'fs';
import path 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
? path.dirname(babelPath)
: null;
}

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

function normalizeOptions(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 => path.resolve(opts.cwd, dirPath))
.reduce((resolvedDirs, absDirPath) => {
if (glob.hasMagic(absDirPath)) {
const roots = glob.sync(absDirPath)
.filter(resolvedPath => fs.lstatSync(resolvedPath).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;
}
const importVisitors = {
CallExpression: transformCall,
'ImportDeclaration|ExportDeclaration': transformImport,
};

export default ({ types }) => {
const importVisitors = {
CallExpression(nodePath, state) {
transformCall(types, nodePath, state);
},
ImportDeclaration(nodePath, state) {
transformImport(types, nodePath, state);
},
ExportDeclaration(nodePath, state) {
transformImport(types, nodePath, state);
const visitor = {
Program: {
exit(programPath, state) {
programPath.traverse(importVisitors, state);
},
};
},
};

return {
pre: normalizeOptions,
export default ({ types }) => ({
pre(file) {
this.types = types;
normalizeOptions(this.opts, file);
},

visitor: {
Program: {
exit(programPath, state) {
programPath.traverse(importVisitors, state);
},
},
},
};
};
visitor,
});
88 changes: 88 additions & 0 deletions src/normalizeOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import fs from 'fs';
import path 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
? path.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 => path.resolve(opts.cwd, dirPath))
.reduce((resolvedDirs, absDirPath) => {
if (glob.hasMagic(absDirPath)) {
const roots = glob.sync(absDirPath)
.filter(resolvedPath => fs.lstatSync(resolvedPath).isDirectory());

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

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

function normalizeAlias(opts) {
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 = {};
}
}

export default function normalizeOptions(opts, file) {
normalizeCwd(opts, file); // This has to go first because other options rely on cwd
normalizeRoot(opts);
normalizeAlias(opts);

if (!opts.extensions) {
opts.extensions = defaultExtensions;
}
}
6 changes: 3 additions & 3 deletions src/transformers/call.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ const patterns = [
'jest.dontMock',
];

export default function transformCall(types, nodePath, state) {
export default function transformCall(nodePath, state) {
const calleePath = nodePath.get('callee');

if (patterns.some(pattern => matchesPattern(types, calleePath, pattern))) {
mapPathString(types, nodePath.get('arguments.0'), state);
if (patterns.some(pattern => matchesPattern(state.types, calleePath, pattern))) {
mapPathString(nodePath.get('arguments.0'), state);
}
}
4 changes: 2 additions & 2 deletions src/transformers/import.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { mapPathString } from '../utils';


export default function transformImport(types, nodePath, state) {
mapPathString(types, nodePath.get('source'), state);
export default function transformImport(nodePath, state) {
mapPathString(nodePath.get('source'), state);
}
6 changes: 3 additions & 3 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ export function matchesPattern(types, calleePath, pattern) {
return node.name === name;
}

export function mapPathString(types, nodePath, state) {
if (!types.isStringLiteral(nodePath)) {
export function mapPathString(nodePath, state) {
if (!state.types.isStringLiteral(nodePath)) {
return;
}

const modulePath = getRealPath(nodePath.node.value, state);
if (modulePath) {
nodePath.replaceWith(types.stringLiteral(modulePath));
nodePath.replaceWith(state.types.stringLiteral(modulePath));
}
}
Loading

0 comments on commit e9f9bf6

Please sign in to comment.