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);
      },
    );
  }
apply is called by the webpack main compiler during the start phase
@param {Compiler} compiler