Webpack插件:如何在编译后修改和重新解析模块?

Bre*_*non 22 webpack

我正在开发一个webpack插件,无法弄清楚如何在构建期间修改模块.我正在做的事情:

  • 通过自定义加载器收集数据(精细)
  • 加载完所有模块后,收集我的加载器收集的数据(罚款)
  • 将我生成的代码插入到构建中的现有模块中(按照下面的描述执行此操作,不确定它是否是最佳方法)
  • "更新"该模块,以便我添加的代码得到解析并将其'require'变为webpack require调用(无法弄清楚如何正确执行此操作)

目前我正在编译器上进行'this-compilation',然后在编译中加入'additional-chunk-assets'.抓住第一个块(目前唯一的一个,因为我还在开发中),遍历该块中的模块以找到我想要修改的块.然后:

  • 将生成的源附加到模块的_cachedSource.source._source._value(我也尝试附加到模块的._source._value)
  • 将._cachedSource.hash设置为空字符串(因为这似乎是下一步工作所必需的)
  • 我将模块传递给.rebuildModule()

看起来rebuildModule应该重新解析源代码,重新建立依赖关系等等,但它不会解析我的require语句并将它们更改为webpack需要.构建的文件包含我修改的源,但require('...')语句未经修改.

如何使我修改的模块"更新",以便webpack将处理我添加的源与原始解析的源相同?除了rebuildModule()之外我还需要做些什么吗?我在构建过程中做得太晚了吗?或者我是以错误的方式去做的?

Bre*_*non 18

我想出了如何以一种非常轻松的方式做到这一点.

我错了:

  • 可能挂得太晚了?最早的插件,你可以完成这个是编译的'密封'插件.尽管有这个名字,但这个插件钩子作为密封功能的第一行执行,所以还没有发生密封.此时所有模块都已加载.
  • rebuildModule()这不是一个好主意,因为这会从头开始重新加载模块:文件的源被加载并通过任何适用的加载器传递,并且当该过程完成时,最终会重新分配对象的_source属性module.
    • rebuildModule()如果有一种方法可以修改模块源,因为在此调用中加载模块源(即动态分配仅用于此重建的加载器函数),此时使用实际上会很棒.然后我们就可以利用加载模块源时发生的sourceMap行为(见下文)

我是如何工作的:

  • 挂进compilation's'seal'插件,遍历编辑的modules并找到你想要的那个
  • 修改模块的源,例如 module._source._value += extraCode;
  • 重新解析模块:

    module.parse.parse(module._source.source(), {
      current: module, 
      module.module,
      compilation: compilation,
      options: compilation.options
    });
    
    Run Code Online (Sandbox Code Playgroud)

解析来自NormalModule的build方法,在正常模块构建过程中加载源之后,该方法会被或多或少地立即调用.

此实现将修改和解析的源转换为我的最终输出.由于在NormalModuleMixin的doBuild方法中有一些sourceMap内容,并且由于我在调用这些函数后添加到源代码,因此我假设sourceMap现在会搞乱.因此,下一步是让sourceMap反映代码添加.不确定是否尝试手动更新sourceMap或查看上面的想法,尝试动态应用加载器并调用rebuildModule()而不是解析.

如果您知道更好的方法,请告诉我们!


Tre*_*ham 5

基于对Webpack官方插件(例如DefinePlugin)的修改方式,我相信最好的方法是:

  1. 创建一个自定义的“依赖项”类和一个相应的“模板”类。
  2. 响应于附加的依赖性类的实例给每个模块,例如buildModule,与module.addDependency()
  3. 向中注册依赖项模板compilation.dependencyTemplates.set()
  4. 在模板的apply方法中,使用source.replace()source.insert()进行修改(source第二个参数在哪里)—请参阅ReplaceSource文档。

在编译挂钩方面,模板在beforeChunkAssets之后立即调用。以这种方式修改源将保留SourceMap。

Webpack 4示例

const Dependency = require('webpack/lib/Dependency');

class MyDependency extends Dependency {
  // Use the constructor to save any information you need for later
  constructor(module) {
    super();
    this.module = module;
  }
}

MyDependency.Template = class MyDependencyTemplate {
  apply(dep, source) {
    // dep is the MyDependency instance, so the module is dep.module
    source.insert(0, 'console.log("Hello, plugin world!");');
  }
};

module.exports = class MyPlugin {
  apply(compiler) {
    compiler.hooks.compilation.tap('MyPluginName', compilation => {
      compilation.dependencyTemplates.set(
        MyDependency,
        new MyDependency.Template()
      );
      compilation.hooks.buildModule.tap('MyPluginName', module => {
        module.addDependency(new MyDependency(module));
      });
    });
  }
};
Run Code Online (Sandbox Code Playgroud)

  • 这很有帮助,谢谢! (2认同)