HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux spn-python 5.15.0-89-generic #99-Ubuntu SMP Mon Oct 30 20:42:41 UTC 2023 x86_64
User: arjun (1000)
PHP: 8.1.2-1ubuntu2.20
Disabled: NONE
Upload Files
File: //home/arjun/projects/buyercall/node_modules/html-webpack-plugin/lib/child-compiler.js
// @ts-check
'use strict';

/**
 * @file
 * This file uses webpack to compile a template with a child compiler.
 *
 * [TEMPLATE] -> [JAVASCRIPT]
 *
 */

/** @typedef {import("webpack").Chunk} Chunk */
/** @typedef {import("webpack").sources.Source} Source */
/** @typedef {{hash: string, entry: Chunk, content: string, assets: {[name: string]: { source: Source, info: import("webpack").AssetInfo }}}} ChildCompilationTemplateResult */

/**
 * The HtmlWebpackChildCompiler is a helper to allow reusing one childCompiler
 * for multiple HtmlWebpackPlugin instances to improve the compilation performance.
 */
class HtmlWebpackChildCompiler {
  /**
   *
   * @param {string[]} templates
   */
  constructor (templates) {
    /**
     * @type {string[]} templateIds
     * The template array will allow us to keep track which input generated which output
     */
    this.templates = templates;
    /** @type {Promise<{[templatePath: string]: ChildCompilationTemplateResult}>} */
    this.compilationPromise; // eslint-disable-line
    /** @type {number | undefined} */
    this.compilationStartedTimestamp; // eslint-disable-line
    /** @type {number | undefined} */
    this.compilationEndedTimestamp; // eslint-disable-line
    /**
     * All file dependencies of the child compiler
     * @type {{fileDependencies: string[], contextDependencies: string[], missingDependencies: string[]}}
     */
    this.fileDependencies = { fileDependencies: [], contextDependencies: [], missingDependencies: [] };
  }

  /**
   * Returns true if the childCompiler is currently compiling
   *
   * @returns {boolean}
   */
  isCompiling () {
    return !this.didCompile() && this.compilationStartedTimestamp !== undefined;
  }

  /**
   * Returns true if the childCompiler is done compiling
   *
   * @returns {boolean}
   */
  didCompile () {
    return this.compilationEndedTimestamp !== undefined;
  }

  /**
   * This function will start the template compilation
   * once it is started no more templates can be added
   *
   * @param {import('webpack').Compilation} mainCompilation
   * @returns {Promise<{[templatePath: string]: ChildCompilationTemplateResult}>}
   */
  compileTemplates (mainCompilation) {
    const webpack = mainCompilation.compiler.webpack;
    const Compilation = webpack.Compilation;

    const NodeTemplatePlugin = webpack.node.NodeTemplatePlugin;
    const NodeTargetPlugin = webpack.node.NodeTargetPlugin;
    const LoaderTargetPlugin = webpack.LoaderTargetPlugin;
    const EntryPlugin = webpack.EntryPlugin;

    // To prevent multiple compilations for the same template
    // the compilation is cached in a promise.
    // If it already exists return
    if (this.compilationPromise) {
      return this.compilationPromise;
    }

    const outputOptions = {
      filename: '__child-[name]',
      publicPath: '',
      library: {
        type: 'var',
        name: 'HTML_WEBPACK_PLUGIN_RESULT'
      },
      scriptType: /** @type {'text/javascript'} */('text/javascript'),
      iife: true
    };
    const compilerName = 'HtmlWebpackCompiler';
    // 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
    const childCompiler = mainCompilation.createChildCompiler(compilerName, outputOptions, [
      // Compile the template to nodejs javascript
      new NodeTargetPlugin(),
      new NodeTemplatePlugin(),
      new LoaderTargetPlugin('node'),
      new webpack.library.EnableLibraryPlugin('var')
    ]);
    // The file path context which webpack uses to resolve all relative files to
    childCompiler.context = mainCompilation.compiler.context;

    // Generate output file names
    const temporaryTemplateNames = this.templates.map((template, index) => `__child-HtmlWebpackPlugin_${index}-${template}`);

    // Add all templates
    this.templates.forEach((template, index) => {
      new EntryPlugin(childCompiler.context, 'data:text/javascript,__webpack_public_path__ = __webpack_base_uri__ = htmlWebpackPluginPublicPath;', `HtmlWebpackPlugin_${index}-${template}`).apply(childCompiler);
      new EntryPlugin(childCompiler.context, template, `HtmlWebpackPlugin_${index}-${template}`).apply(childCompiler);
    });

    // The templates are compiled and executed by NodeJS - similar to server side rendering
    // Unfortunately this causes issues as some loaders require an absolute URL to support ES Modules
    // The following config enables relative URL support for the child compiler
    childCompiler.options.module = { ...childCompiler.options.module };
    childCompiler.options.module.parser = { ...childCompiler.options.module.parser };
    childCompiler.options.module.parser.javascript = {
      ...childCompiler.options.module.parser.javascript,
      url: 'relative'
    };

    this.compilationStartedTimestamp = new Date().getTime();
    /** @type {Promise<{[templatePath: string]: ChildCompilationTemplateResult}>} */
    this.compilationPromise = new Promise((resolve, reject) => {
      /** @type {Source[]} */
      const extractedAssets = [];

      childCompiler.hooks.thisCompilation.tap('HtmlWebpackPlugin', (compilation) => {
        compilation.hooks.processAssets.tap(
          {
            name: 'HtmlWebpackPlugin',
            stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
          },
          (assets) => {
            temporaryTemplateNames.forEach((temporaryTemplateName) => {
              if (assets[temporaryTemplateName]) {
                extractedAssets.push(assets[temporaryTemplateName]);

                compilation.deleteAsset(temporaryTemplateName);
              }
            });
          }
        );
      });

      childCompiler.runAsChild((err, entries, childCompilation) => {
        // Extract templates
        // TODO fine a better way to store entries and results, to avoid duplicate chunks and assets
        const compiledTemplates = entries
          ? extractedAssets.map((asset) => asset.source())
          : [];

        // Extract file dependencies
        if (entries && childCompilation) {
          this.fileDependencies = { fileDependencies: Array.from(childCompilation.fileDependencies), contextDependencies: Array.from(childCompilation.contextDependencies), missingDependencies: Array.from(childCompilation.missingDependencies) };
        }

        // Reject the promise if the childCompilation contains error
        if (childCompilation && childCompilation.errors && childCompilation.errors.length) {
          const errorDetails = childCompilation.errors.map(error => {
            let message = error.message;
            if (error.stack) {
              message += '\n' + error.stack;
            }
            return message;
          }).join('\n');

          reject(new Error('Child compilation failed:\n' + errorDetails));

          return;
        }

        // Reject if the error object contains errors
        if (err) {
          reject(err);
          return;
        }

        if (!childCompilation || !entries) {
          reject(new Error('Empty child compilation'));
          return;
        }

        /**
         * @type {{[templatePath: string]: ChildCompilationTemplateResult}}
         */
        const result = {};

        /** @type {{[name: string]: { source: Source, info: import("webpack").AssetInfo }}} */
        const assets = {};

        for (const asset of childCompilation.getAssets()) {
          assets[asset.name] = { source: asset.source, info: asset.info };
        }

        compiledTemplates.forEach((templateSource, entryIndex) => {
          // The compiledTemplates are generated from the entries added in
          // the addTemplate function.
          // Therefore, the array index of this.templates should be the as entryIndex.
          result[this.templates[entryIndex]] = {
            // TODO, can we have Buffer here?
            content: /** @type {string} */ (templateSource),
            hash: childCompilation.hash || 'XXXX',
            entry: entries[entryIndex],
            assets
          };
        });

        this.compilationEndedTimestamp = new Date().getTime();

        resolve(result);
      });
    });

    return this.compilationPromise;
  }
}

module.exports = {
  HtmlWebpackChildCompiler
};