Skip to content

Commit c937f79

Browse files
fatfiszTommy Leunen
authored andcommitted
Feat: warn when the package from resolved alias is not available (#160)
Breaking change: The "npm:" prefix has been removed.
1 parent 52600af commit c937f79

7 files changed

Lines changed: 193 additions & 122 deletions

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
"jest": {
6363
"testRegex": "/test/.*\\.test\\.js$",
6464
"collectCoverageFrom": [
65-
"src/**/*.js"
65+
"src/**/*.js",
66+
"!src/log.js"
6667
]
6768
},
6869
"greenkeeper": {

src/getRealPath.js

Lines changed: 24 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
11
import path from 'path';
22

3-
import resolve from 'resolve';
3+
import { warn } from './log';
44
import mapToRelative from './mapToRelative';
5-
import { toLocalPath, toPosixPath, replaceExtension } from './utils';
5+
import { nodeResolvePath, replaceExtension, toLocalPath, toPosixPath } from './utils';
66

77

88
function findPathInRoots(sourcePath, { extensions, root }) {
99
// Search the source path inside every custom root directory
1010
let resolvedSourceFile;
1111

1212
root.some((basedir) => {
13-
try {
14-
// Check if the file exists (will throw if not)
15-
resolvedSourceFile = resolve.sync(`./${sourcePath}`, {
16-
basedir,
17-
extensions,
18-
});
19-
return true;
20-
} catch (e) {
21-
return false;
22-
}
13+
resolvedSourceFile = nodeResolvePath(`./${sourcePath}`, basedir, extensions);
14+
return resolvedSourceFile !== null;
2315
});
2416

2517
return resolvedSourceFile;
@@ -43,40 +35,17 @@ function getRealPathFromRootConfig(sourcePath, currentFile, opts) {
4335
)));
4436
}
4537

46-
function getRealPathFromAliasConfig(sourcePath, currentFile, { alias, cwd }) {
47-
const moduleSplit = sourcePath.split('/');
48-
let aliasPath;
49-
50-
while (moduleSplit.length) {
51-
const m = moduleSplit.join('/');
52-
if ({}.hasOwnProperty.call(alias, m)) {
53-
aliasPath = alias[m];
54-
break;
55-
}
56-
moduleSplit.pop();
57-
}
58-
59-
// no alias mapping found
60-
if (!aliasPath) {
61-
return null;
62-
}
63-
64-
// remove legacy "npm:" prefix for npm packages
65-
aliasPath = aliasPath.replace(/^(npm:)/, '');
66-
const newPath = sourcePath.replace(moduleSplit.join('/'), aliasPath);
67-
68-
// alias to npm module don't need relative mapping
69-
if (aliasPath[0] !== '.') {
70-
return newPath;
38+
function checkIfPackageExists(modulePath, currentFile, extensions) {
39+
const resolvedPath = nodeResolvePath(modulePath, currentFile, extensions);
40+
if (resolvedPath === null) {
41+
warn(`Could not resolve "${modulePath}" in file ${currentFile}.`);
7142
}
72-
73-
return toLocalPath(toPosixPath(mapToRelative(cwd, currentFile, newPath)));
7443
}
7544

76-
function getRealPathFromRegExpConfig(sourcePath, currentFile, { regExps }) {
45+
function getRealPathFromAliasConfig(sourcePath, currentFile, opts) {
7746
let aliasedSourceFile;
7847

79-
regExps.find(([regExp, substitute]) => {
48+
opts.alias.find(([regExp, substitute]) => {
8049
const execResult = regExp.exec(sourcePath);
8150

8251
if (execResult === null) {
@@ -87,13 +56,26 @@ function getRealPathFromRegExpConfig(sourcePath, currentFile, { regExps }) {
8756
return true;
8857
});
8958

59+
if (!aliasedSourceFile) {
60+
return null;
61+
}
62+
63+
if (aliasedSourceFile[0] === '.') {
64+
return toLocalPath(toPosixPath(
65+
mapToRelative(opts.cwd, currentFile, aliasedSourceFile)),
66+
);
67+
}
68+
69+
if (process.env.NODE_ENV !== 'production') {
70+
checkIfPackageExists(aliasedSourceFile, currentFile, opts.extensions);
71+
}
72+
9073
return aliasedSourceFile;
9174
}
9275

9376
const resolvers = [
9477
getRealPathFromRootConfig,
9578
getRealPathFromAliasConfig,
96-
getRealPathFromRegExpConfig,
9779
];
9880

9981
export default function getRealPath(sourcePath, { file, opts }) {

src/log.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// This module exists only for abstracting logging away and making testing easier
2+
3+
// eslint-disable-next-line import/prefer-default-export
4+
export function warn(...args) {
5+
// eslint-disable-next-line no-console
6+
console.warn(...args);
7+
}

src/normalizeOptions.js

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -60,29 +60,32 @@ function normalizeRoot(opts) {
6060
}
6161
}
6262

63-
function normalizeAlias(opts) {
64-
opts.regExps = [];
65-
66-
if (opts.alias) {
67-
Object.keys(opts.alias)
68-
.filter(isRegExp)
69-
.forEach((key) => {
70-
const parts = opts.alias[key].split('\\\\');
71-
72-
function substitute(execResult) {
73-
return parts
74-
.map(part =>
75-
part.replace(/\\\d+/g, number => execResult[number.slice(1)] || ''),
76-
)
77-
.join('\\');
78-
}
63+
function getAliasPair(key, value) {
64+
const parts = value.split('\\\\');
65+
66+
function substitute(execResult) {
67+
return parts
68+
.map(part =>
69+
part.replace(/\\\d+/g, number => execResult[number.slice(1)] || ''),
70+
)
71+
.join('\\');
72+
}
7973

80-
opts.regExps.push([new RegExp(key), substitute]);
74+
return [new RegExp(key), substitute];
75+
}
8176

82-
delete opts.alias[key];
83-
});
77+
function normalizeAlias(opts) {
78+
if (opts.alias) {
79+
const { alias } = opts;
80+
const aliasKeys = Object.keys(alias);
81+
82+
opts.alias = aliasKeys.map(key => (
83+
isRegExp(key) ?
84+
getAliasPair(key, alias[key]) :
85+
getAliasPair(`^${key}((?:/|).*)`, `${alias[key]}\\1`)
86+
));
8487
} else {
85-
opts.alias = {};
88+
opts.alias = [];
8689
}
8790
}
8891

src/utils.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
import path from 'path';
22

3+
import resolve from 'resolve';
34
import getRealPath from './getRealPath';
45

56

7+
export function nodeResolvePath(modulePath, basedir, extensions) {
8+
try {
9+
return resolve.sync(modulePath, { basedir, extensions });
10+
} catch (e) {
11+
return null;
12+
}
13+
}
14+
615
export function toPosixPath(modulePath) {
716
return modulePath.replace(/\\/g, '/');
817
}

test/import.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ describe('import and export statement', () => {
8484
root: './test/testproject/src',
8585
alias: {
8686
test: './test/testproject/test',
87+
'babel-core': 'babel-core/lib',
8788
},
8889
}],
8990
],
@@ -121,6 +122,15 @@ describe('import and export statement', () => {
121122
);
122123
});
123124

125+
describe('should only apply the alias once', () => {
126+
// If this test breaks, consider selecting another package used by the plugin
127+
testImports(
128+
'babel-core/store',
129+
'babel-core/lib/store',
130+
transformerOpts,
131+
);
132+
});
133+
124134
describe('should ignore the call if a non-import statement is used', () => {
125135
const code = stripIndent`
126136
function test() {

0 commit comments

Comments
 (0)