Skip to content
This repository has been archived by the owner on Aug 4, 2021. It is now read-only.

Add RegExp support and strict order of entries (+unit-tests) #53

Merged
merged 19 commits into from
Aug 22, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
35 changes: 15 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,31 @@ For Webpack users: This is a plugin to mimic the `resolve.alias` functionality i
```
$ npm install rollup-plugin-alias
```

#
Copy link
Member

Choose a reason for hiding this comment

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

Is this intended?

## Usage
```javascript
// rollup.config.js
import alias from 'rollup-plugin-alias';

export default {
input: './src/index.js',
plugins: [alias({
somelibrary: './mylocallibrary'
})],
plugins: [
alias({
resolve: ['.jsx', '.js'], //optional, by default this will just look for .js files or folders
entries:[
{find:'something', replacement: '../../../something'}, //the initial example
{find:'somelibrary-1.0.0', replacement: './mylocallibrary-1.5.0'}, //remap a library with a specific version
{find:/^i18n\!(.*)/, replacement: '$1.js'}, //remove something in front of the import and append an extension (e.g. loaders, for files that were previously transpiled via the AMD module, to properly handle them in rollup as internals now)
//for whatever reason, replace all .js extensions with .wasm
{find:/^(.*)\.js$/, replacement: '$1.wasm'}
]
})
],
};
```
The order of the entries is important, in that the first rules are applied first.

An optional `resolve` array with file extensions can be provided.
If present local aliases beginning with `./` will be resolved to existing files:

```javascript
// rollup.config.js
import alias from 'rollup-plugin-alias';

export default {
input: './src/index.js',
plugins: [alias({
resolve: ['.jsx', '.js'],
foo: './bar' // Will check for ./bar.jsx and ./bar.js
})],
};
```
If not given local aliases will be resolved with a `.js` extension.
You can use either simple Strings or Regular Expressions to search in a more distinct and complex manner (e.g. to do partial replacements via subpattern-matching, see aboves example).
Copy link
Member

Choose a reason for hiding this comment

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

Looks good 👍


## License
MIT, see `LICENSE` for more information
34 changes: 15 additions & 19 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ const IS_WINDOWS = platform() === 'win32';

// Helper functions
const noop = () => null;
const matches = (key, importee) => {
if (importee.length < key.length) {
const matches = (pattern, importee) => {
if (pattern instanceof RegExp) {
return pattern.test(importee);
}
if (importee.length < pattern.length) {
return false;
}
if (importee === key) {
if (importee === pattern) {
return true;
}
const importeeStartsWithKey = (importee.indexOf(key) === 0);
const importeeHasSlashAfterKey = (importee.substring(key.length)[0] === '/');
const importeeStartsWithKey = (importee.indexOf(pattern) === 0);
const importeeHasSlashAfterKey = (importee.substring(pattern.length)[0] === '/');
return importeeStartsWithKey && importeeHasSlashAfterKey;
};
const endsWith = (needle, haystack) => haystack.slice(-needle.length) === needle;
Expand All @@ -34,18 +37,15 @@ const normalizeId = (id) => {
if ((IS_WINDOWS && typeof id === 'string') || VOLUME.test(id)) {
return slash(id.replace(VOLUME, ''));
}

return id;
};

export default function alias(options = {}) {
const hasResolve = Array.isArray(options.resolve);
const resolve = hasResolve ? options.resolve : ['.js'];
const aliasKeys = hasResolve
? Object.keys(options).filter(k => k !== 'resolve') : Object.keys(options);
const resolve = Array.isArray(options.resolve) ? options.resolve : ['.js'];
const entries = options.entries?options.entries:[];

// No aliases?
if (!aliasKeys.length) {
if (!entries || entries.length === 0) {
return {
resolveId: noop,
};
Expand All @@ -57,15 +57,12 @@ export default function alias(options = {}) {
const importerId = normalizeId(importer);

// First match is supposed to be the correct one
const toReplace = aliasKeys.find(key => matches(key, importeeId));

if (!toReplace || !importerId) {
const matchedEntry = entries.find(entry => matches(entry.find, importeeId));
if (!matchedEntry || !importerId) {
return null;
}

const entry = options[toReplace];

let updatedId = normalizeId(importeeId.replace(toReplace, entry));
let updatedId = normalizeId(importeeId.replace(matchedEntry.find, matchedEntry.replacement));

if (isFilePath(updatedId)) {
const directory = posix.dirname(importerId);
Expand All @@ -89,10 +86,9 @@ export default function alias(options = {}) {
// if alias is windows absoulate path return resolved path or
// rollup on windows will throw:
// [TypeError: Cannot read property 'specifier' of undefined]
if (VOLUME.test(entry)) {
if (VOLUME.test(matchedEntry.replacement)) {
return path.resolve(updatedId);
}

return updatedId;
},
};
Expand Down
86 changes: 66 additions & 20 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ test('defaults', (t) => {

test('Simple aliasing', (t) => {
const result = alias({
foo: 'bar',
pony: 'paradise',
'./local': 'global',
entries: [
{find:'foo', replacement:'bar'},
{find:'pony', replacement:'paradise'},
{find:'./local',replacement:'global'}
]
});

const resolved = result.resolveId('foo', '/src/importer.js');
Expand All @@ -39,10 +41,34 @@ test('Simple aliasing', (t) => {
t.is(resolved3, 'global');
});

test('RegExp aliasing', (t) => {
const result = alias({
entries: [
{find:/f(o+)bar/, replacement:'f$1bar2019'},
{find:new RegExp('.*pony.*'), replacement:'i/am/a/barbie/girl'},
{find:/^test\/$/, replacement:'this/is/strict'}
]
});

const resolved = result.resolveId('fooooooooobar', '/src/importer.js');
const resolved2 = result.resolveId('im/a/little/pony/yes', '/src/importer.js');
const resolved3 = result.resolveId('./test', '/src/importer.js');
const resolved4 = result.resolveId('test', '/src/importer.js');
const resolved5 = result.resolveId('test/', '/src/importer.js');

t.is(resolved, 'fooooooooobar2019');
t.is(resolved2, 'i/am/a/barbie/girl');
t.is(resolved3, null);
t.is(resolved4, null);
t.is(resolved5, 'this/is/strict');
});

test('Will not confuse modules with similar names', (t) => {
const result = alias({
foo: 'bar',
'./foo': 'bar',
entries:[
{find:'foo', replacement:'bar'},
{find:'./foo', replacement:'bar'},
]
});

const resolved = result.resolveId('foo2', '/src/importer.js');
Expand All @@ -56,8 +82,10 @@ test('Will not confuse modules with similar names', (t) => {

test('Local aliasing', (t) => {
const result = alias({
foo: './bar',
pony: './par/a/di/se',
entries:[
{find:'foo', replacement:'./bar'},
{find:'pony', replacement:'./par/a/di/se'}
]
});

const resolved = result.resolveId('foo', '/src/importer.js');
Expand All @@ -73,8 +101,10 @@ test('Local aliasing', (t) => {

test('Absolute local aliasing', (t) => {
const result = alias({
foo: '/bar',
pony: '/par/a/di/se.js',
entries:[
{find:'foo', replacement:'/bar'},
{find:'pony', replacement:'/par/a/di/se.js'}
]
});

const resolved = result.resolveId('foo', '/src/importer.js');
Expand All @@ -90,7 +120,9 @@ test('Absolute local aliasing', (t) => {

test('Leaves entry file untouched if matches alias', (t) => {
const result = alias({
abacaxi: './abacaxi',
entries:[
{find:'abacaxi', replacement:'./abacaxi'}
]
});

const resolved = result.resolveId('abacaxi/entry.js', undefined);
Expand All @@ -100,8 +132,10 @@ test('Leaves entry file untouched if matches alias', (t) => {

test('Test for the resolve property', (t) => {
const result = alias({
ember: './folder/hipster',
resolve: ['.js', '.jsx'],
entries:[
{find:'ember', replacement: './folder/hipster'},
]
});

const resolved = result.resolveId('ember', posix.resolve(DIRNAME, './files/index.js'));
Expand All @@ -111,7 +145,9 @@ test('Test for the resolve property', (t) => {

test('i/am/a/file', (t) => {
const result = alias({
resolve: 'i/am/a/file',
entries:[
{find:'resolve', replacement: 'i/am/a/file'}
]
});

const resolved = result.resolveId('resolve', '/src/import.js');
Expand All @@ -121,7 +157,9 @@ test('i/am/a/file', (t) => {

test('i/am/a/local/file', (t) => {
const result = alias({
resolve: './i/am/a/local/file',
entries:[
{find:'resolve', replacement: './i/am/a/local/file'}
]
});

const resolved = result.resolveId('resolve', posix.resolve(DIRNAME, './files/index.js'));
Expand All @@ -132,7 +170,9 @@ test('i/am/a/local/file', (t) => {
test('Platform path.resolve(\'file-without-extension\') aliasing', (t) => {
// this what used in React and Vue
const result = alias({
test: path.resolve('./test/files/aliasMe'),
entries:[
{find:'test', replacement:path.resolve('./test/files/aliasMe')}
]
});

const resolved = result.resolveId('test', posix.resolve(DIRNAME, './files/index.js'));
Expand All @@ -142,7 +182,9 @@ test('Platform path.resolve(\'file-without-extension\') aliasing', (t) => {

test('Windows absolute path aliasing', (t) => {
const result = alias({
resolve: 'E:\\react\\node_modules\\fbjs\\lib\\warning',
entries:[
{find:'resolve', replacement:'E:\\react\\node_modules\\fbjs\\lib\\warning'}
]
});

const resolved = result.resolveId('resolve', posix.resolve(DIRNAME, './files/index.js'));
Expand All @@ -155,7 +197,9 @@ test('Windows absolute path aliasing', (t) => {

test('Platform path.resolve(\'file-with.ext\') aliasing', (t) => {
const result = alias({
test: path.resolve('./test/files/folder/hipster.jsx'),
entries:[
{find:'test', replacement:path.resolve('./test/files/folder/hipster.jsx')},
],
resolve: ['.js', '.jsx'],
});

Expand All @@ -181,10 +225,12 @@ const getModuleIdsFromBundle = (bundle) => {
test('Works in rollup', t => rollup({
input: './test/files/index.js',
plugins: [alias({
fancyNumber: './aliasMe',
'./anotherFancyNumber': './localAliasMe',
numberFolder: './folder',
'./numberFolder': './folder',
entries:[
{find:'fancyNumber', replacement:'./aliasMe'},
{find:'./anotherFancyNumber', replacement: './localAliasMe'},
{find:'numberFolder', replacement:'./folder'},
{find:'./numberFolder', replacement: './folder'}
]
})],
}).then(getModuleIdsFromBundle)
.then((moduleIds) => {
Expand Down