编写自定义webpack解析器

时间:2017-05-14 23:32:35

标签: javascript node.js webpack resolver

我打算使用一组更复杂的约定来导入我的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 installnpm run webpack即可查看错误。

1 个答案:

答案 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
      )
    }
  })

}