compile.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. const loaderUtils = require('loader-utils');
  2. const WebWorkerTemplatePlugin = require('webpack/lib/webworker/WebWorkerTemplatePlugin');
  3. const ExternalsPlugin = require('webpack/lib/ExternalsPlugin');
  4. const NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin');
  5. const LoaderTargetPlugin = require('webpack/lib/LoaderTargetPlugin');
  6. const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');
  7. const COMPILATION_METADATA = Symbol('COMPILATION_METADATA');
  8. module.exports.COMPILATION_METADATA = COMPILATION_METADATA;
  9. module.exports.pitch = function pitch(remainingRequest) {
  10. const { target, plugins = [], output, emit } = loaderUtils.getOptions(this) || {};
  11. if (target !== 'worker') {
  12. throw new Error(`Unsupported compile target: ${JSON.stringify(target)}`);
  13. }
  14. this.cacheable(false);
  15. const { filename, options = {} } = getOutputFilename(output, { target });
  16. // eslint-disable-next-line no-underscore-dangle
  17. const currentCompilation = this._compilation;
  18. const outputFilename = loaderUtils.interpolateName(this, filename, {
  19. context: options.context || currentCompilation.options.context,
  20. regExp: options.regExp,
  21. });
  22. const outputOptions = {
  23. filename: outputFilename,
  24. chunkFilename: `${outputFilename}.[id]`,
  25. namedChunkFilename: null,
  26. };
  27. const compilerOptions = currentCompilation.compiler.options;
  28. const childCompiler = currentCompilation.createChildCompiler('worker', outputOptions, [
  29. // https://github.com/webpack/webpack/blob/master/lib/WebpackOptionsApply.js
  30. new WebWorkerTemplatePlugin(outputOptions),
  31. new LoaderTargetPlugin('webworker'),
  32. ...((this.target === 'web') || (this.target === 'webworker') ? [] : [new NodeTargetPlugin()]),
  33. // https://github.com/webpack-contrib/worker-loader/issues/95#issuecomment-352856617
  34. ...(compilerOptions.externals ? [new ExternalsPlugin(compilerOptions.externals)] : []),
  35. ...plugins,
  36. new SingleEntryPlugin(this.context, `!!${remainingRequest}`, 'main'),
  37. ]);
  38. const subCache = `subcache ${__dirname} ${remainingRequest}`;
  39. childCompiler.plugin('compilation', (compilation) => {
  40. if (!compilation.cache) { return; }
  41. if (!(subCache in compilation.cache)) { Object.assign(compilation.cache, { [subCache]: {} }); }
  42. Object.assign(compilation, { cache: compilation.cache[subCache] });
  43. });
  44. const callback = this.async();
  45. childCompiler.runAsChild((error, entries, compilation) => {
  46. if (error) { return callback(error); }
  47. if (entries.length === 0) { return callback(null, null); }
  48. const mainFilename = entries[0].files[0];
  49. if (emit === false) { delete currentCompilation.assets[mainFilename]; }
  50. callback(null, compilation.assets[mainFilename].source(), null, {
  51. [COMPILATION_METADATA]: entries[0].files,
  52. });
  53. });
  54. };
  55. function getOutputFilename(options, { target }) {
  56. if (!options) { return { filename: `[hash].${target}.js`, options: undefined }; }
  57. if (typeof options === 'string') { return { filename: options, options: undefined }; }
  58. if (typeof options === 'object') {
  59. return {
  60. filename: options.filename,
  61. options: {
  62. context: options.context,
  63. regExp: options.regExp,
  64. },
  65. };
  66. }
  67. throw new Error(`Invalid compile output options: ${options}`);
  68. }