Webpack子编译器更改配置

时间:2016-07-08 22:06:21

标签: webpack

我希望我的webpack构建的输出在编译我的服务工作者时被定义为变量。

我想使用子编译功能来编译放入不同路径的服务工作者。我需要从webpack编译发出的输出来正确编译服务工作者。

我最初的玩法是使用与创建子编译器的offline-plugin相同的策略,但我需要能够更改服务工作者的输出路径。服务工作者路径很重要,因为它定义了范围。

我想知道如何使用子编译器API来完成下面的工作,以防止我的构建产生这种副作用(希望它能给我webpack-dev-server支持。

var webpack = require('webpack');

function ServiceWorkerPlugin(options) {
    this.options = options;
}

ServiceWorkerPlugin.prototype.apply = function(compiler) {
    compiler.plugin('emit', (compilation, callback) => {
    const stats = compilation.getStats().toJson();
    const child = webpack(this.options);

    child.apply(
    new webpack.DefinePlugin({
    assets: stats.assets
  })
);

child.run((err, swStats) => {
     if (err) {
         callback(err);
     }
     const swStatsJson = swStats.toJson();

     if(swStatsJson.errors.length > 0) {
         console.log(swStatsJson.errors);
     }

     if(swStatsJson.warnings.length > 0) {
         console.log(swStatsJson.warnings);
     }

     callback();
});

module.exports = ServiceWorkerPlugin;

1 个答案:

答案 0 :(得分:10)

首先,您所描述的所有内容都在offline-plugin实施中,所以现在我将向您展示我是如何做到的。

当你需要在其中使用子编译compilation.assets时,webpack中的一切都变得有点棘手。问题是必须在complier.plugin('make')事件上创建子编译,但compilation.assets仅在compiler.plugin('emit')事件上可用,该事件几乎在编译结束时被触发

以下是子编译实现的样板:

注意:代码在ES2015版本中

import SingleEntryPlugin from 'webpack/lib/SingleEntryPlugin';

export default class AwesomePlugin {
  constructor() {
    // Define compilation name and output name
    this.childCompilerName = 'awesome-plugin-compilation';
    this.outputFileName = 'custom-file.js';
    // To make child compiler work, you have to have a entry in the file system
    this.compilationEntry = 'entry-file.js';
  }

  apply(compiler) {
    // Listen to `make` event
    compiler.plugin('make', (compilation, callback) => {
      // Creating child compiler with params
      const childCompiler = compilation.createChildCompiler(this.childCompilerName, {
        filename: this.outputFileName
      });

      // Everyone plugin does this, I don't know why
      childCompiler.context = compiler.context;

      // Add SingleEntryPlugin to make all this work
      childCompiler.apply(new SingleEntryPlugin(compiler.context, this.compilationEntry, this.outputFileName));

      // Needed for HMR. Even if your plugin don't support HMR,
      // this code seems to be always needed just in case to prevent possible errors
      childCompiler.plugin('compilation', (compilation) => {
        if (compilation.cache) {
          if (!compilation.cache[name]) {
            compilation.cache[name] = {};
          }

          compilation.cache = compilation.cache[name];
        }
      });

      // Run child compilation
      childCompiler.runAsChild((err, entries, childCompilation) => {
        callback(err);
      });
    });
  }
}

这将使您的条目编译成单独的文件,您可以根据需要命名。接下来,您需要在compilation.assets事件'emit'上进行一些hacky操作:

compiler.plugin('emit', function(compilation, callback) {
  // Get our output asset
  const asset = compilation.assets[this.outputFileName];

  // Delete delete our asset from output
  delete compilation.assets[this.outputFileName];

  // Collect all output assets
  const assets = Object.keys(compilation.assets);

  // Combine collected assets and child compilation output into new source.
  // Note: `globalAssets` is global variable
  let source = `
    var globalAssets = ${ JSON.stringify(assets) }

    ${ asset.source() }
  `;

  // Add out asset back to the output
  compilation.assets[this.outputFileName] = {
    source() {
      return source;
    },
    size() {
      return Buffer.byteLength(source, 'utf8');
    }
  };
});

编辑:您可能在要插入资源列表的条目中有一些特殊的位置。但要小心,如果你将使用常规模板语法,那么JS加载器将无法解析你的文件。因此,您可以放置​​类似__INSERT_WEBPACK_ASSETS_DATA__之类的内容,然后使用String#replace将其替换为实际数据。

基本上就是这样。现在,您应该能够将带有compilation.assets的变量注入子编译输出。请注意,在offline-plugin中,我在创建它时使用编译名称,然后在'emit'事件上将其重命名为真实文件名。我不记得确切的原因,但我记得我有它们。所以,你可能需要自己试验一下。

以下是此样板的完整代码(包含'make''emit'个事件):https://gist.github.com/NekR/f85d297fe4f1ea3c168827b305c13844