Skip to main content

2 posts tagged with "enhanced-resolve"

View All Tags

webpack: resolveLoader / alias with query / options

Sometimes you write a post for the ages. Sometimes you write one you hope is out of date before you hit "publish". This is one of those.

There's a bug in webpack's enhanced-resolve. It means that you cannot configure an aliased loader using the query (or options in the webpack 2 nomenclature). Let me illustrate; consider the following code:

module.exports = {
// ...
module: {
loaders: [
{
test: /\.ts$/,
loader: 'ts-loader',
query: {
entryFileIsJs: true
}
}
]
}
}
module.exports.resolveLoader = { alias: { 'ts-loader': require('path').join(__dirname, "../../index.js")

At the time of writing, if you alias a loader as above, then the query / options will *not* be passed along. This is bad, particularly given the requirement in webpack 2 that configuration is no longer possible through extending the webpack.config.js. So what to do? Well, when this was a problem previously the marvellous James Brantly had a workaround. I've taken that and run with it:

var config = {
// ...
module: {
loaders: [
{
test: /\.ts$/,
loader: 'ts-loader',
query: {
entryFileIsJs: true
}
}
]
}
}
module.exports = config;
var loaderAliasPath = require('path').join(__dirname, "../../../index.js");
var rules = config.module.loaders || config.module.rules;
rules.forEach(function(rule) {
var options = rule.query || rule.options;
rule.loader = rule.loader.replace('ts-loader', loaderAliasPath + (options ? '?' + JSON.stringify(options) : ''));
});

This approach stringifies the query / options and suffixes it to the aliased path. This works as long as the options you're passing are JSON-able (yes it's a word).

As I said earlier; hopefully by the time you read this the workaround will no longer be necessary again. But just in case....

webpack: syncing the enhanced-resolve

Like Captain Ahab I resolve to sync the white whale that is webpack's <a href="https://github.com/webpack/enhanced-resolve">enhanced-resolve</a>... English you say? Let me start again:

So, you're working on a webpack loader. (In my case the typescript loader; <a href="https://github.com/TypeStrong/ts-loader">ts-loader</a>) You have need of webpack's resolve capabilities. You dig around and you discover that that superpower is lodged in the very heart of the enhanced-resolve package. Fantastic. But wait, there's more: your needs are custom. You need a sync, not an async resolver. (Try saying that quickly.) You regard the description of enhanced-resolve with some concern:

"Offers an async require.resolve function. It's highly configurable."

Well that doesn't sound too promising. Let's have a look at the docs. Ah. Hmmm. You know how it goes with webpack. Why document anything clearly when people could just guess wildly until they near insanity and gibber? Right? It's well established that webpack's attitude to docs has been traditionally akin to Gordon Gecko's view on lunch.

In all fairness, things are beginning to change on that front. In fact the new docs look very promising. But regrettably, the docs on the enhanced-resolve repo are old school. Which is to say: opaque. However, I'm here to tell you that if a sync resolver is your baby then, contrary to appearances, enhanced-resolve has your back.

Sync, for lack of a better word, is good#

Nestled inside enhanced-resolve is the <a href="https://github.com/webpack/enhanced-resolve/blob/3f3f4cd1fcbafa1e98c3c6470fed1277817ed607/lib/ResolverFactory.js">ResolverFactory.js</a> which can be used to make a resolver. However, you can supply it with a million options and that's just like giving someone a gun with a predilection for feet.

What you want is an example of how you could make a sync resolver. Well, surprise surprise it's right in front of your nose. Tucked away in <a href="https://github.com/webpack/enhanced-resolve/blob/3f3f4cd1fcbafa1e98c3c6470fed1277817ed607/lib/node.js">node.js</a> (I do *not* get the name) is exactly what you're after. It contains a number of factory functions which will construct a ready-made resolver for you; sync or async. Perfect! So here's how I'm rolling:

const node = require("enhanced-resolve/lib/node");
function makeSyncResolver(options) {
return node.create.sync(options.resolve);
}
const resolveSync = makeSyncResolver(loader.options);

The loader options used above you'll be familiar with as the resolve section of your webpack.config.js. You can read more about them here and here.

What you're left with at this point is a function; a resolveSync function if you will that takes 3 arguments:

context
I don't know what this is. So when using the function I just supply undefined; and that seems to be OK. Weird, right?
path
This is the path to your code (I think). So, a valid value to supply - handily lifted from the ts-loader test pack - would be: C:\source\ts-loader\.test\babel-issue92
request
The actual module you're interested in; so using the same test the relevant value would be ./submodule/submodule

Put it all together and what have you got?

const resolvedFileName = resolveSync(
undefined,
'C:\source\ts-loader\.test\babel-issue92',
'./submodule/submodule'
);
// resolvedFileName: C:\source\ts-loader\.test\babel-issue92\submodule\submodule.tsx

Boom.