编写自定义 webpack 解析器

Write custom webpack resolver

我正计划使用一组更复杂的约定在我的 webpack 项目中导入资产。所以我正在尝试编写一个插件,该插件应该重写请求的模块定位器的一部分,然后将其传递给解析器 waterfall.


假设我们只想

这意味着当文件 /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 installnpm 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
      )
    }
  })

}