"use strict";
/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.dev/license
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupJitPluginCallbacks = setupJitPluginCallbacks;
const promises_1 = require("node:fs/promises");
const node_path_1 = require("node:path");
const uri_1 = require("../../angular/uri");
const load_result_cache_1 = require("../load-result-cache");
/**
 * Loads/extracts the contents from a load callback Angular JIT entry.
 * An Angular JIT entry represents either a file path for a component resource or base64
 * encoded data for an inline component resource.
 * @param entry The value that represents content to load.
 * @param root The absolute path for the root of the build (typically the workspace root).
 * @param skipRead If true, do not attempt to read the file; if false, read file content from disk.
 * This option has no effect if the entry does not originate from a file. Defaults to false.
 * @returns An object containing the absolute path of the contents and optionally the actual contents.
 * For inline entries the contents will always be provided.
 */
async function loadEntry(entry, root, skipRead) {
    if (entry.startsWith('inline:')) {
        const [importer, data] = entry.slice(7).split(';', 2);
        return {
            path: (0, node_path_1.join)(root, importer),
            contents: Buffer.from(data, 'base64').toString(),
        };
    }
    const path = (0, node_path_1.join)(root, entry);
    return {
        path,
        contents: skipRead ? undefined : await (0, promises_1.readFile)(path, 'utf-8'),
    };
}
/**
 * Sets up esbuild resolve and load callbacks to support Angular JIT mode processing
 * for both Component stylesheets and templates. These callbacks work alongside the JIT
 * resource TypeScript transformer to convert and then bundle Component resources as
 * static imports.
 * @param build An esbuild {@link PluginBuild} instance used to add callbacks.
 * @param styleOptions The options to use when bundling stylesheets.
 * @param additionalResultFiles A Map where stylesheet resources will be added.
 */
function setupJitPluginCallbacks(build, stylesheetBundler, additionalResultFiles, loadCache) {
    const root = build.initialOptions.absWorkingDir ?? '';
    // Add a resolve callback to capture and parse any JIT URIs that were added by the
    // JIT resource TypeScript transformer.
    // Resources originating from a file are resolved as relative from the containing file (importer).
    build.onResolve({ filter: uri_1.JIT_NAMESPACE_REGEXP }, (args) => {
        const parsed = (0, uri_1.parseJitUri)(args.path);
        if (!parsed) {
            return undefined;
        }
        const { namespace, origin, specifier } = parsed;
        if (origin === 'file') {
            return {
                // Use a relative path to prevent fully resolved paths in the metafile (JSON stats file).
                // This is only necessary for custom namespaces. esbuild will handle the file namespace.
                path: (0, node_path_1.relative)(root, (0, node_path_1.join)((0, node_path_1.dirname)(args.importer), specifier)),
                namespace,
            };
        }
        else {
            // Inline data may need the importer to resolve imports/references within the content
            const importer = (0, node_path_1.relative)(root, args.importer);
            return {
                path: `inline:${importer};${specifier}`,
                namespace,
            };
        }
    });
    // Add a load callback to handle Component stylesheets (both inline and external)
    build.onLoad({ filter: /./, namespace: uri_1.JIT_STYLE_NAMESPACE }, (0, load_result_cache_1.createCachedLoad)(loadCache, async (args) => {
        // skipRead is used here because the stylesheet bundling will read a file stylesheet
        // directly either via a preprocessor or esbuild itself.
        const entry = await loadEntry(args.path, root, true /* skipRead */);
        let stylesheetResult;
        // Stylesheet contents only exist for internal stylesheets
        if (entry.contents === undefined) {
            stylesheetResult = await stylesheetBundler.bundleFile(entry.path);
        }
        else {
            stylesheetResult = await stylesheetBundler.bundleInline(entry.contents, entry.path);
        }
        const { errors, warnings, referencedFiles } = stylesheetResult;
        if (stylesheetResult.errors) {
            return {
                errors,
                warnings,
                watchFiles: referencedFiles && [...referencedFiles],
            };
        }
        const { contents, outputFiles, metafile } = stylesheetResult;
        additionalResultFiles.set(entry.path, { outputFiles, metafile });
        return {
            errors,
            warnings,
            contents,
            loader: 'text',
            watchFiles: referencedFiles && [...referencedFiles],
        };
    }));
    // Add a load callback to handle Component templates
    // NOTE: While this callback supports both inline and external templates, the transformer
    // currently only supports generating URIs for external templates.
    build.onLoad({ filter: /./, namespace: uri_1.JIT_TEMPLATE_NAMESPACE }, (0, load_result_cache_1.createCachedLoad)(loadCache, async (args) => {
        const { contents, path } = await loadEntry(args.path, root);
        return {
            contents,
            loader: 'text',
            watchFiles: [path],
        };
    }));
}
