Index: frontend/node_modules/html-webpack-plugin/lib/cached-child-compiler.js
===================================================================
--- frontend/node_modules/html-webpack-plugin/lib/cached-child-compiler.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/html-webpack-plugin/lib/cached-child-compiler.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,482 @@
+// @ts-check
+/**
+ * @file
+ * Helper plugin manages the cached state of the child compilation
+ *
+ * To optimize performance the child compilation is running asynchronously.
+ * Therefore it needs to be started in the compiler.make phase and ends after
+ * the compilation.afterCompile phase.
+ *
+ * To prevent bugs from blocked hooks there is no promise or event based api
+ * for this plugin.
+ *
+ * Example usage:
+ *
+ * ```js
+    const childCompilerPlugin = new PersistentChildCompilerPlugin();
+    childCompilerPlugin.addEntry('./src/index.js');
+    compiler.hooks.afterCompile.tapAsync('MyPlugin', (compilation, callback) => {
+      console.log(childCompilerPlugin.getCompilationResult()['./src/index.js']));
+      return true;
+    });
+ * ```
+ */
+"use strict";
+
+// Import types
+/** @typedef {import("webpack").Compiler} Compiler */
+/** @typedef {import("webpack").Compilation} Compilation */
+/** @typedef {import("webpack/lib/FileSystemInfo").Snapshot} Snapshot */
+/** @typedef {import("./child-compiler").ChildCompilationTemplateResult} ChildCompilationTemplateResult */
+/** @typedef {{fileDependencies: string[], contextDependencies: string[], missingDependencies: string[]}} FileDependencies */
+/** @typedef {{
+  dependencies: FileDependencies,
+  compiledEntries: {[entryName: string]: ChildCompilationTemplateResult}
+} | {
+  dependencies: FileDependencies,
+  error: Error
+}} ChildCompilationResult */
+
+const { HtmlWebpackChildCompiler } = require("./child-compiler");
+
+/**
+ * This plugin is a singleton for performance reasons.
+ * To keep track if a plugin does already exist for the compiler they are cached
+ * in this map
+ * @type {WeakMap<Compiler, PersistentChildCompilerSingletonPlugin>}}
+ */
+const compilerMap = new WeakMap();
+
+class CachedChildCompilation {
+  /**
+   * @param {Compiler} compiler
+   */
+  constructor(compiler) {
+    /**
+     * @private
+     * @type {Compiler}
+     */
+    this.compiler = compiler;
+    // Create a singleton instance for the compiler
+    // if there is none
+    if (compilerMap.has(compiler)) {
+      return;
+    }
+    const persistentChildCompilerSingletonPlugin =
+      new PersistentChildCompilerSingletonPlugin();
+    compilerMap.set(compiler, persistentChildCompilerSingletonPlugin);
+    persistentChildCompilerSingletonPlugin.apply(compiler);
+  }
+
+  /**
+   * apply is called by the webpack main compiler during the start phase
+   * @param {string} entry
+   */
+  addEntry(entry) {
+    const persistentChildCompilerSingletonPlugin = compilerMap.get(
+      this.compiler,
+    );
+    if (!persistentChildCompilerSingletonPlugin) {
+      throw new Error(
+        "PersistentChildCompilerSingletonPlugin instance not found.",
+      );
+    }
+    persistentChildCompilerSingletonPlugin.addEntry(entry);
+  }
+
+  getCompilationResult() {
+    const persistentChildCompilerSingletonPlugin = compilerMap.get(
+      this.compiler,
+    );
+    if (!persistentChildCompilerSingletonPlugin) {
+      throw new Error(
+        "PersistentChildCompilerSingletonPlugin instance not found.",
+      );
+    }
+    return persistentChildCompilerSingletonPlugin.getLatestResult();
+  }
+
+  /**
+   * Returns the result for the given entry
+   * @param {string} entry
+   * @returns {
+      | { mainCompilationHash: string, error: Error }
+      | { mainCompilationHash: string, compiledEntry: ChildCompilationTemplateResult }
+    }
+   */
+  getCompilationEntryResult(entry) {
+    const latestResult = this.getCompilationResult();
+    const compilationResult = latestResult.compilationResult;
+    return "error" in compilationResult
+      ? {
+          mainCompilationHash: latestResult.mainCompilationHash,
+          error: compilationResult.error,
+        }
+      : {
+          mainCompilationHash: latestResult.mainCompilationHash,
+          compiledEntry: compilationResult.compiledEntries[entry],
+        };
+  }
+}
+
+class PersistentChildCompilerSingletonPlugin {
+  /**
+   *
+   * @param {{fileDependencies: string[], contextDependencies: string[], missingDependencies: string[]}} fileDependencies
+   * @param {Compilation} mainCompilation
+   * @param {number} startTime
+   */
+  static createSnapshot(fileDependencies, mainCompilation, startTime) {
+    return new Promise((resolve, reject) => {
+      mainCompilation.fileSystemInfo.createSnapshot(
+        startTime,
+        fileDependencies.fileDependencies,
+        fileDependencies.contextDependencies,
+        fileDependencies.missingDependencies,
+        // @ts-ignore
+        null,
+        (err, snapshot) => {
+          if (err) {
+            return reject(err);
+          }
+          resolve(snapshot);
+        },
+      );
+    });
+  }
+
+  /**
+   * Returns true if the files inside this snapshot
+   * have not been changed
+   *
+   * @param {Snapshot} snapshot
+   * @param {Compilation} mainCompilation
+   * @returns {Promise<boolean | undefined>}
+   */
+  static isSnapshotValid(snapshot, mainCompilation) {
+    return new Promise((resolve, reject) => {
+      mainCompilation.fileSystemInfo.checkSnapshotValid(
+        snapshot,
+        (err, isValid) => {
+          if (err) {
+            reject(err);
+          }
+          resolve(isValid);
+        },
+      );
+    });
+  }
+
+  static watchFiles(mainCompilation, fileDependencies) {
+    Object.keys(fileDependencies).forEach((dependencyType) => {
+      fileDependencies[dependencyType].forEach((fileDependency) => {
+        mainCompilation[dependencyType].add(fileDependency);
+      });
+    });
+  }
+
+  constructor() {
+    /**
+     * @private
+     * @type {
+      | {
+        isCompiling: false,
+        isVerifyingCache: false,
+        entries: string[],
+        compiledEntries: string[],
+        mainCompilationHash: string,
+        compilationResult: ChildCompilationResult
+      }
+    | Readonly<{
+      isCompiling: false,
+      isVerifyingCache: true,
+      entries: string[],
+      previousEntries: string[],
+      previousResult: ChildCompilationResult
+    }>
+    | Readonly <{
+      isVerifyingCache: false,
+      isCompiling: true,
+      entries: string[],
+    }>
+  } the internal compilation state */
+    this.compilationState = {
+      isCompiling: false,
+      isVerifyingCache: false,
+      entries: [],
+      compiledEntries: [],
+      mainCompilationHash: "initial",
+      compilationResult: {
+        dependencies: {
+          fileDependencies: [],
+          contextDependencies: [],
+          missingDependencies: [],
+        },
+        compiledEntries: {},
+      },
+    };
+  }
+
+  /**
+   * apply is called by the webpack main compiler during the start phase
+   * @param {Compiler} compiler
+   */
+  apply(compiler) {
+    /** @type Promise<ChildCompilationResult> */
+    let childCompilationResultPromise = Promise.resolve({
+      dependencies: {
+        fileDependencies: [],
+        contextDependencies: [],
+        missingDependencies: [],
+      },
+      compiledEntries: {},
+    });
+    /**
+     * The main compilation hash which will only be updated
+     * if the childCompiler changes
+     */
+    /** @type {string} */
+    let mainCompilationHashOfLastChildRecompile = "";
+    /** @type {Snapshot | undefined} */
+    let previousFileSystemSnapshot;
+    let compilationStartTime = new Date().getTime();
+
+    compiler.hooks.make.tapAsync(
+      "PersistentChildCompilerSingletonPlugin",
+      (mainCompilation, callback) => {
+        if (
+          this.compilationState.isCompiling ||
+          this.compilationState.isVerifyingCache
+        ) {
+          return callback(new Error("Child compilation has already started"));
+        }
+
+        // Update the time to the current compile start time
+        compilationStartTime = new Date().getTime();
+
+        // The compilation starts - adding new templates is now not possible anymore
+        this.compilationState = {
+          isCompiling: false,
+          isVerifyingCache: true,
+          previousEntries: this.compilationState.compiledEntries,
+          previousResult: this.compilationState.compilationResult,
+          entries: this.compilationState.entries,
+        };
+
+        // Validate cache:
+        const isCacheValidPromise = this.isCacheValid(
+          previousFileSystemSnapshot,
+          mainCompilation,
+        );
+
+        let cachedResult = childCompilationResultPromise;
+        childCompilationResultPromise = isCacheValidPromise.then(
+          (isCacheValid) => {
+            // Reuse cache
+            if (isCacheValid) {
+              return cachedResult;
+            }
+            // Start the compilation
+            const compiledEntriesPromise = this.compileEntries(
+              mainCompilation,
+              this.compilationState.entries,
+            );
+            // Update snapshot as soon as we know the fileDependencies
+            // this might possibly cause bugs if files were changed between
+            // compilation start and snapshot creation
+            compiledEntriesPromise
+              .then((childCompilationResult) => {
+                return PersistentChildCompilerSingletonPlugin.createSnapshot(
+                  childCompilationResult.dependencies,
+                  mainCompilation,
+                  compilationStartTime,
+                );
+              })
+              .then((snapshot) => {
+                previousFileSystemSnapshot = snapshot;
+              });
+            return compiledEntriesPromise;
+          },
+        );
+
+        // Add files to compilation which needs to be watched:
+        mainCompilation.hooks.optimizeTree.tapAsync(
+          "PersistentChildCompilerSingletonPlugin",
+          (chunks, modules, callback) => {
+            const handleCompilationDonePromise =
+              childCompilationResultPromise.then((childCompilationResult) => {
+                this.watchFiles(
+                  mainCompilation,
+                  childCompilationResult.dependencies,
+                );
+              });
+            handleCompilationDonePromise.then(
+              // @ts-ignore
+              () => callback(null, chunks, modules),
+              callback,
+            );
+          },
+        );
+
+        // Store the final compilation once the main compilation hash is known
+        mainCompilation.hooks.additionalAssets.tapAsync(
+          "PersistentChildCompilerSingletonPlugin",
+          (callback) => {
+            const didRecompilePromise = Promise.all([
+              childCompilationResultPromise,
+              cachedResult,
+            ]).then(([childCompilationResult, cachedResult]) => {
+              // Update if childCompilation changed
+              return cachedResult !== childCompilationResult;
+            });
+
+            const handleCompilationDonePromise = Promise.all([
+              childCompilationResultPromise,
+              didRecompilePromise,
+            ]).then(([childCompilationResult, didRecompile]) => {
+              // Update hash and snapshot if childCompilation changed
+              if (didRecompile) {
+                mainCompilationHashOfLastChildRecompile =
+                  /** @type {string} */ (mainCompilation.hash);
+              }
+              this.compilationState = {
+                isCompiling: false,
+                isVerifyingCache: false,
+                entries: this.compilationState.entries,
+                compiledEntries: this.compilationState.entries,
+                compilationResult: childCompilationResult,
+                mainCompilationHash: mainCompilationHashOfLastChildRecompile,
+              };
+            });
+            handleCompilationDonePromise.then(() => callback(null), callback);
+          },
+        );
+
+        // Continue compilation:
+        callback(null);
+      },
+    );
+  }
+
+  /**
+   * Add a new entry to the next compile run
+   * @param {string} entry
+   */
+  addEntry(entry) {
+    if (
+      this.compilationState.isCompiling ||
+      this.compilationState.isVerifyingCache
+    ) {
+      throw new Error(
+        "The child compiler has already started to compile. " +
+          "Please add entries before the main compiler 'make' phase has started or " +
+          "after the compilation is done.",
+      );
+    }
+    if (this.compilationState.entries.indexOf(entry) === -1) {
+      this.compilationState.entries = [...this.compilationState.entries, entry];
+    }
+  }
+
+  getLatestResult() {
+    if (
+      this.compilationState.isCompiling ||
+      this.compilationState.isVerifyingCache
+    ) {
+      throw new Error(
+        "The child compiler is not done compiling. " +
+          "Please access the result after the compiler 'make' phase has started or " +
+          "after the compilation is done.",
+      );
+    }
+    return {
+      mainCompilationHash: this.compilationState.mainCompilationHash,
+      compilationResult: this.compilationState.compilationResult,
+    };
+  }
+
+  /**
+   * Verify that the cache is still valid
+   * @private
+   * @param {Snapshot | undefined} snapshot
+   * @param {Compilation} mainCompilation
+   * @returns {Promise<boolean | undefined>}
+   */
+  isCacheValid(snapshot, mainCompilation) {
+    if (!this.compilationState.isVerifyingCache) {
+      return Promise.reject(
+        new Error(
+          "Cache validation can only be done right before the compilation starts",
+        ),
+      );
+    }
+    // If there are no entries we don't need a new child compilation
+    if (this.compilationState.entries.length === 0) {
+      return Promise.resolve(true);
+    }
+    // If there are new entries the cache is invalid
+    if (
+      this.compilationState.entries !== this.compilationState.previousEntries
+    ) {
+      return Promise.resolve(false);
+    }
+    // Mark the cache as invalid if there is no snapshot
+    if (!snapshot) {
+      return Promise.resolve(false);
+    }
+
+    return PersistentChildCompilerSingletonPlugin.isSnapshotValid(
+      snapshot,
+      mainCompilation,
+    );
+  }
+
+  /**
+   * Start to compile all templates
+   *
+   * @private
+   * @param {Compilation} mainCompilation
+   * @param {string[]} entries
+   * @returns {Promise<ChildCompilationResult>}
+   */
+  compileEntries(mainCompilation, entries) {
+    const compiler = new HtmlWebpackChildCompiler(entries);
+    return compiler.compileTemplates(mainCompilation).then(
+      (result) => {
+        return {
+          // The compiled sources to render the content
+          compiledEntries: result,
+          // The file dependencies to find out if a
+          // recompilation is required
+          dependencies: compiler.fileDependencies,
+          // The main compilation hash can be used to find out
+          // if this compilation was done during the current compilation
+          mainCompilationHash: mainCompilation.hash,
+        };
+      },
+      (error) => ({
+        // The compiled sources to render the content
+        error,
+        // The file dependencies to find out if a
+        // recompilation is required
+        dependencies: compiler.fileDependencies,
+        // The main compilation hash can be used to find out
+        // if this compilation was done during the current compilation
+        mainCompilationHash: mainCompilation.hash,
+      }),
+    );
+  }
+
+  /**
+   * @private
+   * @param {Compilation} mainCompilation
+   * @param {FileDependencies} files
+   */
+  watchFiles(mainCompilation, files) {
+    PersistentChildCompilerSingletonPlugin.watchFiles(mainCompilation, files);
+  }
+}
+
+module.exports = {
+  CachedChildCompilation,
+};
Index: frontend/node_modules/html-webpack-plugin/lib/child-compiler.js
===================================================================
--- frontend/node_modules/html-webpack-plugin/lib/child-compiler.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/html-webpack-plugin/lib/child-compiler.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,264 @@
+// @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 errorDetailsArray = [];
+          for (const error of childCompilation.errors) {
+            let message = error.message;
+            if (error.stack) {
+              message += "\n" + error.stack;
+            }
+            errorDetailsArray.push(message);
+          }
+          const errorDetails = errorDetailsArray.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,
+};
Index: frontend/node_modules/html-webpack-plugin/lib/chunksorter.js
===================================================================
--- frontend/node_modules/html-webpack-plugin/lib/chunksorter.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/html-webpack-plugin/lib/chunksorter.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,45 @@
+// @ts-check
+"use strict";
+
+/** @typedef {import("webpack").Compilation} Compilation */
+
+/**
+ * @type {{[sortmode: string] : (entryPointNames: Array<string>, compilation: Compilation, htmlWebpackPluginOptions: any) => Array<string> }}
+ * This file contains different sort methods for the entry chunks names
+ */
+module.exports = {};
+
+/**
+ * Performs identity mapping (no-sort).
+ * @param  {Array<string>} chunks the chunks to sort
+ * @return {Array<string>} The sorted chunks
+ */
+module.exports.none = (chunks) => chunks;
+
+/**
+ * Sort manually by the chunks
+ * @param {string[]} entryPointNames the chunks to sort
+ * @param {Compilation} compilation the webpack compilation
+ * @param {any} htmlWebpackPluginOptions the plugin options
+ * @return {string[]} The sorted chunks
+ */
+module.exports.manual = (
+  entryPointNames,
+  compilation,
+  htmlWebpackPluginOptions,
+) => {
+  const chunks = htmlWebpackPluginOptions.chunks;
+  if (!Array.isArray(chunks)) {
+    return entryPointNames;
+  }
+  // Remove none existing entries from
+  // htmlWebpackPluginOptions.chunks
+  return chunks.filter((entryPointName) => {
+    return compilation.entrypoints.has(entryPointName);
+  });
+};
+
+/**
+ * Defines the default sorter.
+ */
+module.exports.auto = module.exports.none;
Index: frontend/node_modules/html-webpack-plugin/lib/errors.js
===================================================================
--- frontend/node_modules/html-webpack-plugin/lib/errors.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/html-webpack-plugin/lib/errors.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,42 @@
+// @ts-nocheck
+"use strict";
+
+let prettyError;
+
+function getPrettyError() {
+  if (!prettyError) {
+    // lazily require to improve startup time since pretty-error is rather heavy package
+    const PrettyError = require("pretty-error");
+    prettyError = new PrettyError();
+    prettyError.withoutColors();
+    prettyError.skipPackage("html-plugin-evaluation");
+    prettyError.skipNodeFiles();
+    prettyError.skip(function (traceLine) {
+      return traceLine.path === "html-plugin-evaluation";
+    });
+  }
+  return prettyError;
+}
+
+module.exports = function (err, context) {
+  return {
+    toHtml: function () {
+      return "Html Webpack Plugin:\n<pre>\n" + this.toString() + "</pre>";
+    },
+    toJsonHtml: function () {
+      return JSON.stringify(this.toHtml());
+    },
+    toString: function () {
+      try {
+        return getPrettyError()
+          .render(err)
+          .replace(/webpack:\/\/\/\./g, context);
+      } catch (e) {
+        // This can sometimes fail. We don't know why, but returning the
+        // original error is better than returning the error thrown by
+        // pretty-error.
+        return err;
+      }
+    },
+  };
+};
Index: frontend/node_modules/html-webpack-plugin/lib/hooks.js
===================================================================
--- frontend/node_modules/html-webpack-plugin/lib/hooks.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/html-webpack-plugin/lib/hooks.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,7 @@
+// TODO remove in the next major release
+const plugin = require("../index");
+
+module.exports = {
+  getHtmlWebpackPluginHooks: (compilation) =>
+    plugin.getCompilationHooks(compilation),
+};
Index: frontend/node_modules/html-webpack-plugin/lib/html-tags.js
===================================================================
--- frontend/node_modules/html-webpack-plugin/lib/html-tags.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/html-webpack-plugin/lib/html-tags.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,127 @@
+// @ts-check
+/** @typedef {import("../typings").HtmlTagObject} HtmlTagObject */
+/**
+ * @file
+ * This file provides to helper to create html as a object representation as
+ * those objects are easier to modify than pure string representations
+ *
+ * Usage:
+ * ```
+ * const element = createHtmlTagObject('h1', {class: 'demo'}, 'Hello World');
+ * const html = htmlTagObjectToString(element);
+ * console.log(html) // -> <h1 class="demo">Hello World</h1>
+ * ```
+ */
+
+/**
+ * All html tag elements which must not contain innerHTML
+ * @see https://www.w3.org/TR/html5/syntax.html#void-elements
+ */
+const voidTags = [
+  "area",
+  "base",
+  "br",
+  "col",
+  "embed",
+  "hr",
+  "img",
+  "input",
+  "keygen",
+  "link",
+  "meta",
+  "param",
+  "source",
+  "track",
+  "wbr",
+];
+
+/**
+ * Turn a tag definition into a html string
+ * @param {HtmlTagObject} tagDefinition
+ *  A tag element according to the htmlWebpackPlugin object notation
+ *
+ * @param xhtml {boolean}
+ *   Whether the generated html should add closing slashes to be xhtml compliant
+ */
+function htmlTagObjectToString(tagDefinition, xhtml) {
+  const attributes = Object.keys(tagDefinition.attributes || {})
+    .filter(function (attributeName) {
+      return (
+        tagDefinition.attributes[attributeName] === "" ||
+        tagDefinition.attributes[attributeName]
+      );
+    })
+    .map(function (attributeName) {
+      if (tagDefinition.attributes[attributeName] === true) {
+        return xhtml
+          ? attributeName + '="' + attributeName + '"'
+          : attributeName;
+      }
+      return (
+        attributeName + '="' + tagDefinition.attributes[attributeName] + '"'
+      );
+    });
+  return (
+    "<" +
+    [tagDefinition.tagName].concat(attributes).join(" ") +
+    (tagDefinition.voidTag && xhtml ? "/" : "") +
+    ">" +
+    (tagDefinition.innerHTML || "") +
+    (tagDefinition.voidTag ? "" : "</" + tagDefinition.tagName + ">")
+  );
+}
+
+/**
+ * Static helper to create a tag object to be get injected into the dom
+ *
+ * @param {string} tagName
+ * the name of the tag e.g. 'div'
+ *
+ * @param {{[attributeName: string]: string|boolean|null|undefined}} [attributes]
+ * tag attributes e.g. `{ 'class': 'example', disabled: true }`
+ *
+ * @param {string} [innerHTML]
+ *
+ * @param {{[attributeName: string]: string|boolean|null|undefined}} [meta]
+ * meta information about the tag e.g. `{ 'plugin': 'html-webpack-plugin' }`
+ *
+ * @returns {HtmlTagObject}
+ */
+function createHtmlTagObject(tagName, attributes, innerHTML, meta) {
+  return {
+    tagName: tagName,
+    voidTag: voidTags.indexOf(tagName) !== -1,
+    attributes: attributes || {},
+    meta: meta || {},
+    innerHTML: innerHTML,
+  };
+}
+
+/**
+ * The `HtmlTagArray Array with a custom `.toString()` method.
+ *
+ * This allows the following:
+ * ```
+ *   const tags = HtmlTagArray.from([tag1, tag2]);
+ *   const scriptTags = tags.filter((tag) => tag.tagName === 'script');
+ *   const html = scriptTags.toString();
+ * ```
+ *
+ * Or inside a string literal:
+ * ```
+ *   const tags = HtmlTagArray.from([tag1, tag2]);
+ *   const html = `<html><body>${tags.filter((tag) => tag.tagName === 'script')}</body></html>`;
+ * ```
+ *
+ */
+class HtmlTagArray extends Array {
+  toString() {
+    return this.join("");
+  }
+}
+
+module.exports = {
+  HtmlTagArray: HtmlTagArray,
+  createHtmlTagObject: createHtmlTagObject,
+  htmlTagObjectToString: htmlTagObjectToString,
+};
Index: frontend/node_modules/html-webpack-plugin/lib/loader.js
===================================================================
--- frontend/node_modules/html-webpack-plugin/lib/loader.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/html-webpack-plugin/lib/loader.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,55 @@
+/* This loader renders the template with underscore if no other loader was found */
+// @ts-nocheck
+"use strict";
+const _template = require("lodash/template");
+
+module.exports = function (source) {
+  // Get templating options
+  const options = this.getOptions();
+  const force = options.force || false;
+
+  const allLoadersButThisOne = this.loaders.filter(
+    (loader) => loader.normal !== module.exports,
+  );
+
+  // This loader shouldn't kick in if there is any other loader (unless it's explicitly enforced)
+  if (allLoadersButThisOne.length > 0 && !force) {
+    return source;
+  }
+
+  // Allow only one html-webpack-plugin loader to allow loader options in the webpack config
+  const htmlWebpackPluginLoaders = this.loaders.filter(
+    (loader) => loader.normal === module.exports,
+  );
+  const lastHtmlWebpackPluginLoader =
+    htmlWebpackPluginLoaders[htmlWebpackPluginLoaders.length - 1];
+  if (this.loaders[this.loaderIndex] !== lastHtmlWebpackPluginLoader) {
+    return source;
+  }
+
+  // Skip .js files (unless it's explicitly enforced)
+  if (/\.(c|m)?js$/.test(this.resourcePath) && !force) {
+    return source;
+  }
+
+  // The following part renders the template with lodash as a minimalistic loader
+  //
+  const template = _template(source, {
+    interpolate: /<%=([\s\S]+?)%>/g,
+    variable: "data",
+    ...options,
+  });
+  // Use `eval("require")("lodash")` to enforce using the native nodejs require
+  // during template execution
+  return (
+    'var _ = eval("require")(' +
+    JSON.stringify(require.resolve("lodash")) +
+    ");" +
+    "module.exports = function (templateParams) { with(templateParams) {" +
+    // Execute the lodash template
+    "return (" +
+    template.source +
+    ")();" +
+    "}}"
+  );
+};
