我打算使用一组更复杂的约定来导入我的webpack项目中的资产。所以我正在尝试编写一个插件,该插件应该重写部分请求的模块定位器,然后将其传递给解析器waterfall。
我们假设我们只想
#
字符和./lib/
。现在应该通过默认解析器查找新模块定位器。这意味着当文件/var/www/source.js
执行require("#example")
时,它应该实际获得/var/www/lib/example.js
。
到目前为止,我已经发现我显然应该使用the module
event hook来达到这个目的。这也是选择by other answers的方式,遗憾的是这对我没有多大帮助。
所以这是我对自定义解析插件的看法,它非常简单:
function MyResolver () {}
MyResolver.prototype.apply = function (compiler) {
compiler.plugin('module', function (init, callback) {
// Check if rewrite is necessary
if (init.request.startsWith('#')) {
// Create a new payload
const modified = Object.assign({}, init, {
request: './lib/' + init.request.slice(1)
})
// Continue the waterfall with modified payload
callback(null, modified)
} else {
// Continue the waterfall with original payload
callback(null, init)
}
})
}
但是,使用此(在resolve.plugins
中)不起作用。运行webpack,我收到以下错误:
ERROR in .
Module build failed: Error: EISDIR: illegal operation on a directory, read
@ ./source.js 1:0-30
显然,这不是做事的方式。但由于我在这个问题上找不到很多示例材料,我有点想法。
为了使这更容易重现,我将这个确切的配置放入GitHub仓库。所以如果你有兴趣帮忙,你可能只是去取得它:
git clone https://github.com/Loilo/webpack-custom-resolver.git
然后只需运行npm install
和npm run webpack
即可查看错误。
答案 0 :(得分:3)
更新:请注意,插件架构在webpack 4中发生了显着变化。下面的代码将不再适用于当前的webpack版本。
如果您对webpack 4兼容版本感兴趣,请发表评论,我会将其添加到此答案中。
我找到了解决方案,主要是通过阅读小doResolve()
行in the docs来触发。
解决方案是一个多步骤的过程:
<强> 1。运行callback()
不足以继续瀑布。
要将解析任务传递回webpack,我需要替换
callback(null, modified)
与
this.doResolve(
'resolve',
modified,
`Looking up ${modified.request}`,
callback
)
(2.修复webpack文档)
文档缺少message
方法的第三个参数(doResolve()
),导致使用此处显示的代码时出错。这就是为什么我在把问题提到SO之前就已经放弃doResolve()
方法了。
我已经发出拉取请求,文档应该很快修复。
第3。请勿使用Object.assign()
似乎不能通过init
复制原始请求对象(问题中名为Object.assign()
)以传递给解析器。
显然它包含的内部信息会欺骗解析器查找错误的路径。
所以这一行
const modified = Object.assign({}, init, {
request: './lib/' + init.request.slice(1)
})
需要替换为:
const modified = {
path: init.path,
request: './lib/' + init.request.slice(1),
query: init.query,
directory: init.directory
}
就是这样。为了更清楚一点,这里是上面的整个MyResolver
插件,现在使用上述修改:
function MyResolver () {}
MyResolver.prototype.apply = function (compiler) {
compiler.plugin('module', function (init, callback) {
// Check if rewrite is necessary
if (init.request.startsWith('#')) {
// Create a new payload
const modified = {
path: init.path,
request: './lib/' + init.request.slice(1),
query: init.query,
directory: init.directory
}
// Continue the waterfall with modified payload
this.doResolve(
// "resolve" just re-runs the whole resolving of this module,
// but this time with our modified request.
'resolve',
modified,
`Looking up ${modified.request}`,
callback
)
} else {
this.doResolve(
// Using "resolve" here would cause an infinite recursion,
// use an array of the possibilities instead.
[ 'module', 'file', 'directory' ],
modified,
`Looking up ${init.request}`,
callback
)
}
})
}