我希望我的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;
答案 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