编写自定义 webpack 解析器
Write custom webpack resolver
我正计划使用一组更复杂的约定在我的 webpack 项目中导入资产。所以我正在尝试编写一个插件,该插件应该重写请求的模块定位器的一部分,然后将其传递给解析器 waterfall.
假设我们只想
- 检查请求的模块是否以
#
字符和 开头
- 如果是,请将其替换为
./lib/
。默认解析器现在应该查找新的模块定位器。
这意味着当文件 /var/www/source.js
执行 require("#example")
时,它实际上应该得到 /var/www/lib/example.js
.
到目前为止我已经弄清楚我显然应该使用 the module
event hook for this purpose. That's also the way chosen 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
显然,这不是做事的方式。但是由于我在这件事上找不到太多例子 material,所以我有点不知所措。
为了使其更容易重现,我已将此确切配置放入 GitHub 存储库中。因此,如果您有兴趣提供帮助,您可以获取它:
git clone https://github.com/Loilo/webpack-custom-resolver.git
然后只需 运行 npm install
和 npm run webpack
即可查看错误。
Update: Note that the plugin architecture changed significantly in webpack 4. The code below will no longer work on current webpack versions.
If you're interested in a webpack 4 compliant version, leave a comment and I'll add it to this answer.
我找到了解决方案,它主要是通过阅读小 doResolve()
行 in the docs.
触发的
解决方案是一个多步骤过程:
1。 运行callback()
不足以继续瀑布
为了将解析任务传回 webpack,我需要替换
callback(null, modified)
和
this.doResolve(
'resolve',
modified,
`Looking up ${modified.request}`,
callback
)
(2.修复webpack文档)
文档缺少 doResolve()
方法的第三个参数 (message
),导致在使用此处显示的代码时出错。这就是为什么我在提出问题之前发现 doResolve()
方法时放弃了它。
我已经提出了拉取请求,文档应该会很快修复。
3。不要使用 Object.assign()
似乎不能通过 Object.assign()
复制原始请求对象(在问题中命名为 init
)以传递给解析器。
显然它包含诱使解析器查找错误路径的内部信息。
所以这一行
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
)
}
})
}
我正计划使用一组更复杂的约定在我的 webpack 项目中导入资产。所以我正在尝试编写一个插件,该插件应该重写请求的模块定位器的一部分,然后将其传递给解析器 waterfall.
假设我们只想
- 检查请求的模块是否以
#
字符和 开头
- 如果是,请将其替换为
./lib/
。默认解析器现在应该查找新的模块定位器。
这意味着当文件 /var/www/source.js
执行 require("#example")
时,它实际上应该得到 /var/www/lib/example.js
.
到目前为止我已经弄清楚我显然应该使用 the module
event hook for this purpose. That's also the way chosen 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
显然,这不是做事的方式。但是由于我在这件事上找不到太多例子 material,所以我有点不知所措。
为了使其更容易重现,我已将此确切配置放入 GitHub 存储库中。因此,如果您有兴趣提供帮助,您可以获取它:
git clone https://github.com/Loilo/webpack-custom-resolver.git
然后只需 运行 npm install
和 npm run webpack
即可查看错误。
Update: Note that the plugin architecture changed significantly in webpack 4. The code below will no longer work on current webpack versions.
If you're interested in a webpack 4 compliant version, leave a comment and I'll add it to this answer.
我找到了解决方案,它主要是通过阅读小 doResolve()
行 in the docs.
解决方案是一个多步骤过程:
1。 运行callback()
不足以继续瀑布
为了将解析任务传回 webpack,我需要替换
callback(null, modified)
和
this.doResolve(
'resolve',
modified,
`Looking up ${modified.request}`,
callback
)
(2.修复webpack文档)
文档缺少 doResolve()
方法的第三个参数 (message
),导致在使用此处显示的代码时出错。这就是为什么我在提出问题之前发现 doResolve()
方法时放弃了它。
我已经提出了拉取请求,文档应该会很快修复。
3。不要使用 Object.assign()
似乎不能通过 Object.assign()
复制原始请求对象(在问题中命名为 init
)以传递给解析器。
显然它包含诱使解析器查找错误路径的内部信息。
所以这一行
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
)
}
})
}