Skip to content

Commit 9299d9a

Browse files
fatfisztleunen
authored andcommitted
feat: Allow using a function to define the result of an alias (#245)
1 parent 0fa7b3a commit 9299d9a

File tree

3 files changed

+114
-18
lines changed

3 files changed

+114
-18
lines changed

DOCS.md

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,32 @@ You can reference the n-th matched group with `'\\n'` (`'\\0'` refers to the who
6666

6767
To use the backslash character (`\`) just escape it like so: `'\\\\'` (double escape is needed because of JSON already using `\` for escaping).
6868

69+
### Passing a substitute function
70+
71+
If you need even more power over the aliased path, you can pass a function in the alias configuration:
72+
73+
```js
74+
module.exports = {
75+
plugins: [
76+
["module-resolver", {
77+
alias: {
78+
"foo": ([, name]) => `bar${name}`,
79+
"^@namespace/foo-(.+)": ([, name]) => `packages/${name}`
80+
}
81+
}]
82+
]
83+
}
84+
```
85+
86+
Using the config from this example:
87+
* `'foo'` will become `'bar'` (`name` is empty)
88+
* `'foo/baz'` will become `'bar/baz'` (`name` includes the slash in this case)
89+
* `'@namespace/foo-bar'` will become `'packages/bar'`
90+
91+
The only argument is the result of calling `RegExp.prototype.exec` on the matched path. It's an array with the matched string and all matched groups.
92+
93+
Because the function is only called when there is a match, the argument can never be `null`.
94+
6995
## extensions
7096

7197
An array of extensions used in the resolver.
@@ -95,7 +121,7 @@ Array of functions and methods that will have their first argument transformed.
95121
"plugins": [
96122
["module-resolver", {
97123
"transformFunctions": [
98-
"require",
124+
"require",
99125
"require.resolve",
100126
"System.import",
101127
"jest.genMockFromModule",
@@ -224,4 +250,4 @@ const realPath = resolvePath(sourcePath, currentFile, opts);
224250

225251
For each path in the file you can use `resolvePath` to get the same path that module-resolver will output.
226252

227-
`currentFile` can be either a relative path (will be resolved with respect to the CWD, not `opts.cwd`), or an absolute path.
253+
`currentFile` can be either a relative path (will be resolved with respect to the CWD, not `opts.cwd`), or an absolute path.

src/normalizeOptions.js

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,27 @@ function normalizeRoot(optsRoot, cwd) {
7878
}, []);
7979
}
8080

81-
function getAliasPair(key, value) {
82-
const parts = value.split('\\\\');
81+
function getAliasTarget(key, isKeyRegExp) {
82+
const regExpPattern = isKeyRegExp ? key : `^${key}(/.*|)$`;
83+
return new RegExp(regExpPattern);
84+
}
85+
86+
function getAliasSubstitute(value, isKeyRegExp) {
87+
if (typeof value === 'function') {
88+
return value;
89+
}
8390

84-
function substitute(execResult) {
85-
return parts
86-
.map(part =>
87-
part.replace(/\\\d+/g, number => execResult[number.slice(1)] || ''),
88-
)
89-
.join('\\');
91+
if (!isKeyRegExp) {
92+
return ([, match]) => `${value}${match}`;
9093
}
9194

92-
return [new RegExp(key), substitute];
95+
const parts = value.split('\\\\');
96+
97+
return execResult => parts
98+
.map(part =>
99+
part.replace(/\\\d+/g, number => execResult[number.slice(1)] || ''),
100+
)
101+
.join('\\');
93102
}
94103

95104
function normalizeAlias(optsAlias) {
@@ -99,18 +108,18 @@ function normalizeAlias(optsAlias) {
99108

100109
const aliasArray = Array.isArray(optsAlias) ? optsAlias : [optsAlias];
101110

102-
return aliasArray.reduce((acc, alias) => {
111+
return aliasArray.reduce((aliasPairs, alias) => {
103112
const aliasKeys = Object.keys(alias);
104113

105114
aliasKeys.forEach((key) => {
106-
const aliasPair = isRegExp(key)
107-
? getAliasPair(key, alias[key])
108-
: getAliasPair(`^${key}(/.*|)$`, `${alias[key]}\\1`);
109-
110-
acc.push(aliasPair);
115+
const isKeyRegExp = isRegExp(key);
116+
aliasPairs.push([
117+
getAliasTarget(key, isKeyRegExp),
118+
getAliasSubstitute(alias[key], isKeyRegExp),
119+
]);
111120
});
112121

113-
return acc;
122+
return aliasPairs;
114123
}, []);
115124
}
116125

test/index.test.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,67 @@ describe('module-resolver', () => {
492492
});
493493
});
494494

495+
describe('with a function', () => {
496+
const mockSubstitute = jest.fn();
497+
const regExpSubsituteOpts = {
498+
babelrc: false,
499+
plugins: [
500+
[plugin, {
501+
alias: {
502+
'basic-function': mockSubstitute,
503+
'^@regexp-function/(.+)': mockSubstitute,
504+
},
505+
}],
506+
],
507+
};
508+
509+
beforeEach(() => {
510+
mockSubstitute.mockClear();
511+
});
512+
513+
it('should call the substitute with the right arguments (basic)', () => {
514+
mockSubstitute.mockReturnValue('./test/testproject/test');
515+
516+
testWithImport(
517+
'basic-function/something',
518+
'./test/testproject/test',
519+
regExpSubsituteOpts,
520+
);
521+
522+
expect(mockSubstitute.mock.calls.length).toBe(1);
523+
524+
const execResult = Object.assign(
525+
['basic-function/something', '/something'],
526+
{
527+
index: 0,
528+
input: 'basic-function/something',
529+
},
530+
);
531+
expect(mockSubstitute).toBeCalledWith(execResult);
532+
});
533+
534+
it('should call the substitute with the right arguments (regexp)', () => {
535+
mockSubstitute.mockReturnValue('./test/testproject/test');
536+
537+
testWithImport(
538+
'@regexp-function/something',
539+
'./test/testproject/test',
540+
regExpSubsituteOpts,
541+
);
542+
543+
expect(mockSubstitute.mock.calls.length).toBe(1);
544+
545+
const execResult = Object.assign(
546+
['@regexp-function/something', 'something'],
547+
{
548+
index: 0,
549+
input: '@regexp-function/something',
550+
},
551+
);
552+
expect(mockSubstitute).toBeCalledWith(execResult);
553+
});
554+
});
555+
495556
describe('with the plugin applied twice', () => {
496557
const doubleAliasTransformerOpts = {
497558
babelrc: false,

0 commit comments

Comments
 (0)