【问题标题】:Write custom webpack resolver编写自定义 webpack 解析器
【发布时间】:2017-10-13 15:57:25
【问题描述】:

我计划在我的 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 即可查看错误。

【问题讨论】:

    标签: javascript node.js webpack resolver


    【解决方案1】:

    更新:请注意,插件架构在 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 文档)

    文档缺少 doResolve() 方法的第三个参数 (message),导致使用此处显示的代码时出错。这就是为什么我在提出问题之前放弃了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
          )
        }
      })
    
    }
    

    【讨论】:

    • 这似乎只处理没有相对路径的文件。一旦我删除 # 字符,路径就会被解释为相对路径,并且解析器被绕过。有没有什么办法可以截取相对路径并进行修改?
    • 没问题,只要跳到else 分支,检查modified.path 并根据需要进行调整。
    • 同时我找到了NormalModuleReplacementPlugin。这个对我有用。我可以访问任何路径(相对或别名)。感谢您的快速回答!
    猜你喜欢
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 2019-08-28
    • 2016-06-19
    • 1970-01-01
    • 2014-03-27
    • 1970-01-01
    • 2011-11-28
    相关资源
    最近更新 更多