"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InlineChunkHtmlPlugin = void 0;
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 * modified from https://github.com/facebook/create-react-app/blob/master/packages/react-dev-utils/InlineChunkHtmlPlugin.js
 */
const path_1 = require("path");
const utils_1 = require("@modern-js/utils");
const utils_2 = require("../utils");
const util_1 = require("./util");
class InlineChunkHtmlPlugin {
    constructor(htmlPlugin, { tests, distPath }) {
        this.name = 'InlineChunkHtmlPlugin';
        this.tests = tests;
        this.distPath = distPath;
        this.inlinedAssets = new Set();
        this.htmlPlugin = htmlPlugin;
    }
    /**
     * If we inlined the chunk to HTML,we should update the value of sourceMappingURL,
     * because the relative path of source code has been changed.
     * @param source
     */
    updateSourceMappingURL({ source, compilation, publicPath, type, }) {
        const { devtool } = compilation.options;
        if (devtool &&
            // If the source map is inlined, we do not need to update the sourceMappingURL
            !devtool.includes('inline') &&
            source.includes('# sourceMappingURL')) {
            const prefix = (0, utils_2.addTrailingSlash)((0, path_1.join)(publicPath, this.distPath[type] || ''));
            return source.replace(/# sourceMappingURL=/, `# sourceMappingURL=${prefix}`);
        }
        return source;
    }
    getInlinedScriptTag(publicPath, tag, compilation) {
        const { assets } = compilation;
        if (!((tag === null || tag === void 0 ? void 0 : tag.attributes.src) && (0, utils_1.isString)(tag.attributes.src))) {
            return tag;
        }
        const { src, ...otherAttrs } = tag.attributes;
        const scriptName = publicPath ? src.replace(publicPath, '') : src;
        if (!this.tests.some(test => test.exec(scriptName))) {
            return tag;
        }
        const asset = assets[scriptName];
        if (asset == null) {
            return tag;
        }
        const source = asset.source().toString();
        const ret = {
            tagName: 'script',
            innerHTML: this.updateSourceMappingURL({
                source,
                compilation,
                publicPath,
                type: 'js',
            }),
            attributes: {
                ...otherAttrs,
            },
            closeTag: true,
        };
        // mark asset has already been inlined
        this.inlinedAssets.add(scriptName);
        return ret;
    }
    getInlinedCSSTag(publicPath, tag, compilation) {
        const { assets } = compilation;
        if (!(tag.attributes.href && (0, utils_1.isString)(tag.attributes.href))) {
            return tag;
        }
        const linkName = publicPath
            ? tag.attributes.href.replace(publicPath, '')
            : tag.attributes.href;
        if (!this.tests.some(test => test.exec(linkName))) {
            return tag;
        }
        const asset = assets[linkName];
        const source = asset.source().toString();
        const ret = {
            tagName: 'style',
            innerHTML: this.updateSourceMappingURL({
                source,
                compilation,
                publicPath,
                type: 'css',
            }),
            closeTag: true,
        };
        // mark asset has already been inlined
        this.inlinedAssets.add(linkName);
        return ret;
    }
    getInlinedTag(publicPath, tag, compilation) {
        if (tag.tagName === 'script') {
            return this.getInlinedScriptTag(publicPath, tag, compilation);
        }
        if (tag.tagName === 'link' &&
            tag.attributes &&
            tag.attributes.rel === 'stylesheet') {
            return this.getInlinedCSSTag(publicPath, tag, compilation);
        }
        return tag;
    }
    apply(compiler) {
        compiler.hooks.compilation.tap(this.name, (compilation) => {
            const publicPath = typeof compiler.options.output.publicPath === 'string'
                ? (0, utils_2.addTrailingSlash)(compiler.options.output.publicPath)
                : '/';
            const tagFunction = (tag) => this.getInlinedTag(publicPath, tag, compilation);
            const hooks = this.htmlPlugin.getHooks(compilation);
            hooks.alterAssetTagGroups.tap(this.name, assets => {
                assets.headTags = assets.headTags.map(tagFunction);
                assets.bodyTags = assets.bodyTags.map(tagFunction);
                return assets;
            });
            compilation.hooks.processAssets.tap({
                name: 'InlineChunkHtmlPlugin',
                /**
                 * Remove marked inline assets in summarize stage,
                 * which should be later than the emitting of html-webpack-plugin
                 */
                stage: util_1.COMPILATION_PROCESS_STAGE.PROCESS_ASSETS_STAGE_SUMMARIZE,
            }, () => {
                this.inlinedAssets.forEach(name => {
                    // use delete instead of compilation.deleteAsset
                    // because we want to preserve the related files such as source map
                    delete compilation.assets[name];
                });
                this.inlinedAssets.clear();
            });
        });
    }
}
exports.InlineChunkHtmlPlugin = InlineChunkHtmlPlugin;
