Reputation: 4953
I'm configuring a bunch of module aliases through webpack via resolve.alias
. Then, in my app code, I would like to require one of these modules using a variable that contains an alias name:
var module = require(moduleAlias);
Unfortunately, this creates a "context module" containing everything in the script's directory and its descendants which isn't what I was after in this particular case. Also, since nowhere in my code am I explicitly requiring all the aliased modules, they don't get built into my app.
Two questions:
Thanks!
Upvotes: 9
Views: 6202
Reputation: 684
The cleanest solution I've found is to overwrite the default module Id system. Webpack seems to use array index by default. I do a check to see if the file path is in my alias module, then set its id to that.
That way in my code where I needed to do synchronous dynamic requires with an alias, I could do __webpack_require__(alias)
This is a total hack using private methods (__webpack_require__
), but I see this as a temporary fix until I can migrate our codebase to either properly async dynamic require or properly use paths instead of alias everywhere like many requireJS codebases do.
var path = require('path');
var _ = require('lodash');
function NamedAliasModules(){};
NamedAliasModules.prototype.apply = function(compiler){
compiler.plugin('compilation', function(compilation){
compilation.plugin("before-module-ids", function(modules) {
modules.forEach(function(module) {
if(module.id === null && module.libIdent) {
var id = module.libIdent({
context: compiler.options.context
});
var fullpath = path.resolve(__dirname, id);
if (_.has(aliasLookup, fullpath) || _.has(aliasLookup, fullpath.replace(/\.js$/,''))){
id = aliasLookup[fullpath] || aliasLookup[fullpath.replace(/\.js$/, '')];
module.libIdent = function(){
return id;
}
}
module.id = id;
}
}, this);
}.bind(this));
}.bind(this));
}
Upvotes: 6
Reputation: 12607
This only answers the second part of your question: if you have bundled module with alias and you want those aliases to be requirable from a context:
To my knowledge, there is no official way of doing it with webpack. I created a plugin, working with Node 4 (you can adapt if you want to use pure ES5), that will add to any context the list of aliases:
'use strict';
class AddToContextPlugin {
constructor(extras) {
this.extras = extras || [];
}
apply(compiler) {
compiler.plugin('context-module-factory', (cmf) => {
cmf.plugin('after-resolve', (result, callback) => {
this.newContext = true;
return callback(null, result);
});
// this method is called for every path in the ctx
// we just add our extras the first call
cmf.plugin('alternatives', (result, callback) => {
if (this.newContext) {
this.newContext = false;
const extras = this.extras.map((ext) => {
return {
context: result[0].context,
request: ext
};
});
result.push.apply(result, extras);
}
return callback(null, result);
});
});
}
}
module.exports = AddToContextPlugin;
This is how you can use it:
webpack({
/*...*/
resolve: {
alias: {
'alias1': 'absolute-path-to-rsc1',
'alias2$': 'absolute-path-to-rsc2'
}
},
plugins: [
new AddToContextPlugin(['alias1', 'alias2'])
]
})
And it result as the following code generated:
function(module, exports, __webpack_require__) {
var map = {
"./path/to/a/rsc": 2,
"./path/to/a/rsc.js": 2,
"./path/to/another/rsc.js": 301,
"./path/to/another/rsc.js": 301,
"alias1": 80,
"alias2": 677
};
function webpackContext(req) {
return __webpack_require__(webpackContextResolve(req));
};
function webpackContextResolve(req) {
return map[req] || (function() { throw new Error("Cannot find module '" + req + "'.") }());
};
webpackContext.keys = function webpackContextKeys() {
return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id = 1;
}
Upvotes: 10