Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,31 @@ You can reference the n-th matched group with `'\\n'` (`'\\0'` refers to the who

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

### Passing a substitute function

If you need even more power over the aliased path, you can pass a function in the alias configuration:

```js
module.exports = {
plugins: [
["module-resolver", {
alias: {
"foo": ([, name]) => path.join('bar', name)
"^@namespace/foo-(.+)": ([, name]) => path.join('packages', name)
}
}]
]
}
```

Using the config from this example:
* `'foo/baz'` will become `'bar/baz'` or `'bar\\baz'`
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we should use path.join as an example.

On windows or unix, all paths in node for the import/require files should in theory be in a posix format afaik... right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK Node allows me to use \ on Windows. I'll adjust the examples though, to make them more simple.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a second thought, manually joining path parts is a bad practice... I'd rather stick to path.join than use +.

Copy link
Copy Markdown
Owner

@tleunen tleunen Dec 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I agree that in general people should always use path.join to create a path. But in this case, inside a node/web project, I don't think it's a good idea to mix posix and windows paths.

What if a project is built on windows and provide paths with \? Would it run on a mac/linux pc? Will webpack understand the paths?
I feel it's risky to have this in our doc since most people should never use windows paths for their imports/require.

Maybe replacing the example to remove path.join would be best.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, will remove them.

* `'@namespace/foo-bar'` will become `'packages/bar'` or `'packages\\bar'`

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.

Because the function is only called when there is a match, the argument can never be `null`.

## extensions

An array of extensions used in the resolver.
Expand Down Expand Up @@ -95,7 +120,7 @@ Array of functions and methods that will have their first argument transformed.
"plugins": [
["module-resolver", {
"transformFunctions": [
"require",
"require",
"require.resolve",
"System.import",
"jest.genMockFromModule",
Expand Down Expand Up @@ -224,4 +249,4 @@ const realPath = resolvePath(sourcePath, currentFile, opts);

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

`currentFile` can be either a relative path (will be resolved with respect to the CWD, not `opts.cwd`), or an absolute path.
`currentFile` can be either a relative path (will be resolved with respect to the CWD, not `opts.cwd`), or an absolute path.
41 changes: 25 additions & 16 deletions src/normalizeOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,27 @@ function normalizeRoot(optsRoot, cwd) {
}, []);
}

function getAliasPair(key, value) {
const parts = value.split('\\\\');
function getAliasTarget(key, isKeyRegExp) {
const regExpPattern = isKeyRegExp ? key : `^${key}(/.*|)$`;
return new RegExp(regExpPattern);
}

function getAliasSubstitute(value, isKeyRegExp) {
if (typeof value === 'function') {
return value;
}

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

return [new RegExp(key), substitute];
const parts = value.split('\\\\');

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

function normalizeAlias(optsAlias) {
Expand All @@ -99,18 +108,18 @@ function normalizeAlias(optsAlias) {

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

return aliasArray.reduce((acc, alias) => {
return aliasArray.reduce((aliasPairs, alias) => {
const aliasKeys = Object.keys(alias);

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

acc.push(aliasPair);
const isKeyRegExp = isRegExp(key);
aliasPairs.push([
getAliasTarget(key, isKeyRegExp),
getAliasSubstitute(alias[key], isKeyRegExp),
]);
});

return acc;
return aliasPairs;
}, []);
}

Expand Down
61 changes: 61 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,67 @@ describe('module-resolver', () => {
});
});

describe('with a function', () => {
const mockSubstitute = jest.fn();
const regExpSubsituteOpts = {
babelrc: false,
plugins: [
[plugin, {
alias: {
'basic-function': mockSubstitute,
'^@regexp-function/(.+)': mockSubstitute,
},
}],
],
};

beforeEach(() => {
mockSubstitute.mockClear();
});

it('should call the substitute with the right arguments (basic)', () => {
mockSubstitute.mockReturnValue('./test/testproject/test');

testWithImport(
'basic-function/something',
'./test/testproject/test',
regExpSubsituteOpts,
);

expect(mockSubstitute.mock.calls.length).toBe(1);

const execResult = Object.assign(
['basic-function/something', '/something'],
{
index: 0,
input: 'basic-function/something',
},
);
expect(mockSubstitute).toBeCalledWith(execResult);
});

it('should call the substitute with the right arguments (regexp)', () => {
mockSubstitute.mockReturnValue('./test/testproject/test');

testWithImport(
'@regexp-function/something',
'./test/testproject/test',
regExpSubsituteOpts,
);

expect(mockSubstitute.mock.calls.length).toBe(1);

const execResult = Object.assign(
['@regexp-function/something', 'something'],
{
index: 0,
input: '@regexp-function/something',
},
);
expect(mockSubstitute).toBeCalledWith(execResult);
});
});

describe('with the plugin applied twice', () => {
const doubleAliasTransformerOpts = {
babelrc: false,
Expand Down