Html-webpack-plugin: Some question on source code

Created on 14 Jan 2016  路  4Comments  路  Source: jantimon/html-webpack-plugin

Hi man,

I have some question on this part of source code

HtmlWebpackPlugin.prototype.compileTemplate = function(template, outputFilename, compilation) {
  // The entry file is just an empty helper as the dynamic template
  // require is added in "loader.js"
  var outputOptions = {
    filename: outputFilename,
    publicPath: compilation.outputOptions.publicPath
  };
  // Create an additional child compiler which takes the template
  // and turns it into an Node.JS html factory.
  // This allows us to use loaders during the compilation
  var compilerName = this.getCompilerName();
  console.log(compilerName);
  var childCompiler = compilation.createChildCompiler(compilerName, outputOptions);
  childCompiler.apply(
    new NodeTemplatePlugin(outputOptions),
    new NodeTargetPlugin(),
    new LibraryTemplatePlugin('HTML_WEBPACK_PLUGIN_RESULT', 'var'),
    new SingleEntryPlugin(this.context, template),
    new LoaderTargetPlugin('node'),
    new webpack.DefinePlugin({ HTML_WEBPACK_PLUGIN : 'true' })
  );

  // Create a subCache (copied from https://github.com/SanderSpies/extract-text-webpack-plugin/blob/master/loader.js)
  childCompiler.plugin('compilation', function(compilation) {
    if(compilation.cache) {
      if(!compilation.cache[compilerName]) {
        compilation.cache[compilerName] = {};
      }
      compilation.cache = compilation.cache[compilerName];
    }
  });
  // Compile and return a promise
  return new Promise(function (resolve, reject) {
    childCompiler.runAsChild(function(err, entries, childCompilation) {
      // Resolve / reject the promise
      if (childCompilation.errors && childCompilation.errors.length) {
        var errorDetails = childCompilation.errors.map(function(error) {
            return error.message + (error.error ? ':\n' + error.error: '');
          }).join('\n');

        reject('Child compilation failed:\n' + errorDetails);
      } else {
        resolve(compilation.assets[outputFilename]);
      }
    });
  });
};
  1. What is childCompilation used for?
  2. What are NodeTemplatePlugin, NodeTargetPlugin, LibraryTemplatePlugin, SingleEntryPlugin, LoaderTargetPlugin and webpack.DefinePlugin for respectively?

Best regards,

Lee

question

Most helpful comment

Okay so in the new version I moved this into it's own file:
https://github.com/ampedandwired/html-webpack-plugin/blob/feature/loaders/lib/compiler.js

/**
 * Compiles the template into a nodejs factory, adds its to the compilation.assets
 * and returns a promise of the result asset object.
 *
 * @param template relative path to the template file
 * @param context path context
 * @param outputFilename the file name
 * @param compilation The webpack compilation object
 *
 * Returns an object:
 * {
 *  hash: {String} - Base64 hash of the file
 *  content: {String} - Javascript executable code of the template
 * }
 *
 */
module.exports.compileTemplate = function compileTemplate(template, context, outputFilename, compilation) {
  // The entry file is just an empty helper as the dynamic template
  // require is added in "loader.js"
  var outputOptions = {
    filename: outputFilename,
    publicPath: compilation.outputOptions.publicPath
  };
  var cachedAsset = compilation.assets[outputOptions.filename];
  // Create an additional child compiler which takes the template
  // and turns it into an Node.JS html factory.
  // This allows us to use loaders during the compilation
  var compilerName = getCompilerName(context, outputFilename);
  var childCompiler = compilation.createChildCompiler(compilerName, outputOptions);
  childCompiler.apply(
    new NodeTemplatePlugin(outputOptions),
    new NodeTargetPlugin(),
    new LibraryTemplatePlugin('HTML_WEBPACK_PLUGIN_RESULT', 'var'),
    new SingleEntryPlugin(this.context, template),
    new LoaderTargetPlugin('node')
  );

  // Compile and return a promise
  return new Promise(function (resolve, reject) {
    childCompiler.runAsChild(function(err, entries, childCompilation) {
      compilation.assets[outputOptions.filename] = cachedAsset;
      if (cachedAsset === undefined) {
        delete compilation.assets[outputOptions.filename];
      }
      // Resolve / reject the promise
      if (childCompilation.errors && childCompilation.errors.length) {
        var errorDetails = childCompilation.errors.map(function(error) {
            return error.message + (error.error ? ':\n' + error.error: '');
          }).join('\n');
        reject(new Error('Child compilation failed:\n' + errorDetails));
      } else {
        resolve({
          // Hash of the template entry point
          hash: entries[0].hash,
          // Compiled code
          content: childCompilation.assets[outputOptions.filename].source()
        });
      }
    });
  });
};

1)
So what is this?
Webpack is a compiler - it takes javascript, typescript, es6, less, html, images... and creates a compilation of it.

A child compiler is built in webpack and allows to start a second compilation which has access to all plugins and loaders of the main compiler. - This is exactly what we do here we compile the HTML template to be able to use all the webpack loaders.

2)
These plugins are the internal plugins which are used by webpack - let's take a look at the SingleEntryPlugin for example:
https://github.com/webpack/webpack/blob/259c346062cd315571719a5cfddd33af343a5abb/SingleEntryPlugin.js

Usually you have have a simple webpack configuration like

module.exports = {
  entry: './example.js',
  output: {
    path: __dirname + '/dist',
    publicPath: '',
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      { test: /\.css$/, loader: 'style-loader!css-loader' }
    ]
  }
};

Now the WebpackOptionsApply.js kicks in:
https://github.com/webpack/webpack/blob/259c346062cd315571719a5cfddd33af343a5abb/lib/WebpackOptionsApply.js#L227-L228

It starts the EntryOptionPlugin.js:
https://github.com/webpack/webpack/blob/259c346062cd315571719a5cfddd33af343a5abb/lib/EntryOptionPlugin.js#L17

Which then turns the ./example.js into a SingleEntryPlugin instance.

That's exactly what we do - we take the template and create a SingleEntryPlugin for it.

All 4 comments

Okay so in the new version I moved this into it's own file:
https://github.com/ampedandwired/html-webpack-plugin/blob/feature/loaders/lib/compiler.js

/**
 * Compiles the template into a nodejs factory, adds its to the compilation.assets
 * and returns a promise of the result asset object.
 *
 * @param template relative path to the template file
 * @param context path context
 * @param outputFilename the file name
 * @param compilation The webpack compilation object
 *
 * Returns an object:
 * {
 *  hash: {String} - Base64 hash of the file
 *  content: {String} - Javascript executable code of the template
 * }
 *
 */
module.exports.compileTemplate = function compileTemplate(template, context, outputFilename, compilation) {
  // The entry file is just an empty helper as the dynamic template
  // require is added in "loader.js"
  var outputOptions = {
    filename: outputFilename,
    publicPath: compilation.outputOptions.publicPath
  };
  var cachedAsset = compilation.assets[outputOptions.filename];
  // Create an additional child compiler which takes the template
  // and turns it into an Node.JS html factory.
  // This allows us to use loaders during the compilation
  var compilerName = getCompilerName(context, outputFilename);
  var childCompiler = compilation.createChildCompiler(compilerName, outputOptions);
  childCompiler.apply(
    new NodeTemplatePlugin(outputOptions),
    new NodeTargetPlugin(),
    new LibraryTemplatePlugin('HTML_WEBPACK_PLUGIN_RESULT', 'var'),
    new SingleEntryPlugin(this.context, template),
    new LoaderTargetPlugin('node')
  );

  // Compile and return a promise
  return new Promise(function (resolve, reject) {
    childCompiler.runAsChild(function(err, entries, childCompilation) {
      compilation.assets[outputOptions.filename] = cachedAsset;
      if (cachedAsset === undefined) {
        delete compilation.assets[outputOptions.filename];
      }
      // Resolve / reject the promise
      if (childCompilation.errors && childCompilation.errors.length) {
        var errorDetails = childCompilation.errors.map(function(error) {
            return error.message + (error.error ? ':\n' + error.error: '');
          }).join('\n');
        reject(new Error('Child compilation failed:\n' + errorDetails));
      } else {
        resolve({
          // Hash of the template entry point
          hash: entries[0].hash,
          // Compiled code
          content: childCompilation.assets[outputOptions.filename].source()
        });
      }
    });
  });
};

1)
So what is this?
Webpack is a compiler - it takes javascript, typescript, es6, less, html, images... and creates a compilation of it.

A child compiler is built in webpack and allows to start a second compilation which has access to all plugins and loaders of the main compiler. - This is exactly what we do here we compile the HTML template to be able to use all the webpack loaders.

2)
These plugins are the internal plugins which are used by webpack - let's take a look at the SingleEntryPlugin for example:
https://github.com/webpack/webpack/blob/259c346062cd315571719a5cfddd33af343a5abb/SingleEntryPlugin.js

Usually you have have a simple webpack configuration like

module.exports = {
  entry: './example.js',
  output: {
    path: __dirname + '/dist',
    publicPath: '',
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      { test: /\.css$/, loader: 'style-loader!css-loader' }
    ]
  }
};

Now the WebpackOptionsApply.js kicks in:
https://github.com/webpack/webpack/blob/259c346062cd315571719a5cfddd33af343a5abb/lib/WebpackOptionsApply.js#L227-L228

It starts the EntryOptionPlugin.js:
https://github.com/webpack/webpack/blob/259c346062cd315571719a5cfddd33af343a5abb/lib/EntryOptionPlugin.js#L17

Which then turns the ./example.js into a SingleEntryPlugin instance.

That's exactly what we do - we take the template and create a SingleEntryPlugin for it.

thx a lot. Let me spend some time on your answer.

Great question! Very enlightening answer!

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings