'use strict';
/**
 * @license Angular v21.2.1
 * (c) 2010-2026 Google LLC. https://angular.dev/
 * License: MIT
 */
'use strict';

var ts = require('typescript');
require('@angular/compiler-cli');
require('@angular/compiler-cli/private/migrations');
require('node:path');
var project_paths = require('./project_paths-D2V-Uh2L.cjs');
var ng_component_template = require('./ng_component_template-BOuKAnQd.cjs');
var compiler = require('@angular/compiler');
var parse_html = require('./parse_html-C8eKA9px.cjs');
require('@angular-devkit/core');
require('node:path/posix');
require('@angular-devkit/schematics');
require('./project_tsconfig_paths-DkkMibv-.cjs');
require('./ng_decorators-DYy6II6x.cjs');
require('./imports-CVmcbVA9.cjs');
require('./property_name-BCpALNpZ.cjs');

function migrateTemplateToSelfClosingTags(template) {
    let parsed = parse_html.parseTemplate(template);
    if (parsed.tree === undefined) {
        return { migrated: template, changed: false, replacementCount: 0 };
    }
    const visitor = new AngularElementCollector();
    compiler.visitAll(visitor, parsed.tree.rootNodes);
    let newTemplate = template;
    let changedOffset = 0;
    let replacementCount = 0;
    for (let element of visitor.elements) {
        const { start, end, tagName } = element;
        const currentLength = newTemplate.length;
        const templatePart = newTemplate.slice(start + changedOffset, end + changedOffset);
        const convertedTemplate = replaceWithSelfClosingTag(templatePart, tagName);
        // if the template has changed, replace the original template with the new one
        if (convertedTemplate.length !== templatePart.length) {
            newTemplate = replaceTemplate(newTemplate, convertedTemplate, start, end, changedOffset);
            changedOffset += newTemplate.length - currentLength;
            replacementCount++;
        }
    }
    return { migrated: newTemplate, changed: changedOffset !== 0, replacementCount };
}
function replaceWithSelfClosingTag(html, tagName) {
    const pattern = new RegExp(`<\\s*${tagName}\\s*([^>]*?(?:"[^"]*"|'[^']*'|[^'">])*)\\s*>([\\s\\S]*?)<\\s*/\\s*${tagName}\\s*>`, 'gi');
    return html.replace(pattern, (_, content) => `<${tagName}${content ? ` ${content}` : ''} />`);
}
/**
 * Replace the value in the template with the new value based on the start and end position + offset
 */
function replaceTemplate(template, replaceValue, start, end, offset) {
    return template.slice(0, start + offset) + replaceValue + template.slice(end + offset);
}
const ALL_HTML_TAGS = new compiler.DomElementSchemaRegistry().allKnownElementNames();
class AngularElementCollector extends compiler.RecursiveVisitor {
    elements = [];
    constructor() {
        super();
    }
    visitElement(element) {
        if (!element.isSelfClosing &&
            !ALL_HTML_TAGS.includes(element.name) &&
            this.elementHasNoContent(element)) {
            this.elements.push({
                tagName: element.name,
                start: element.sourceSpan.start.offset,
                end: element.sourceSpan.end.offset,
            });
        }
        return super.visitElement(element, null);
    }
    elementHasNoContent(element) {
        if (!element.children?.length) {
            return true;
        }
        if (element.children.length === 1) {
            const child = element.children[0];
            return child instanceof compiler.Text && /^\s*$/.test(child.value);
        }
        return false;
    }
}

class SelfClosingTagsMigration extends project_paths.TsurgeFunnelMigration {
    config;
    constructor(config = {}) {
        super();
        this.config = config;
    }
    async analyze(info) {
        const { sourceFiles, program } = info;
        const typeChecker = program.getTypeChecker();
        const tagReplacements = [];
        for (const sf of sourceFiles) {
            ts.forEachChild(sf, (node) => {
                // Skipping any non component declarations
                if (!ts.isClassDeclaration(node)) {
                    return;
                }
                const file = project_paths.projectFile(node.getSourceFile(), info);
                if (this.config.shouldMigrate && this.config.shouldMigrate(file) === false) {
                    return;
                }
                const templateVisitor = new ng_component_template.NgComponentTemplateVisitor(typeChecker);
                templateVisitor.visitNode(node);
                templateVisitor.resolvedTemplates.forEach((template) => {
                    const { migrated, changed, replacementCount } = migrateTemplateToSelfClosingTags(template.content);
                    if (!changed) {
                        return;
                    }
                    const fileToMigrate = template.inline
                        ? file
                        : project_paths.projectFile(template.filePath, info);
                    const end = template.start + template.content.length;
                    const replacements = [
                        prepareTextReplacement(fileToMigrate, migrated, template.start, end),
                    ];
                    const fileReplacements = tagReplacements.find((tagReplacement) => tagReplacement.file === file);
                    if (fileReplacements) {
                        fileReplacements.replacements.push(...replacements);
                        fileReplacements.replacementCount += replacementCount;
                    }
                    else {
                        tagReplacements.push({ file, replacements, replacementCount });
                    }
                });
            });
        }
        return project_paths.confirmAsSerializable({ tagReplacements });
    }
    async combine(unitA, unitB) {
        return project_paths.confirmAsSerializable({
            tagReplacements: [...unitA.tagReplacements, ...unitB.tagReplacements],
        });
    }
    async globalMeta(combinedData) {
        const globalMeta = {
            tagReplacements: combinedData.tagReplacements,
        };
        return project_paths.confirmAsSerializable(globalMeta);
    }
    async stats(globalMetadata) {
        const touchedFilesCount = globalMetadata.tagReplacements.length;
        const replacementCount = globalMetadata.tagReplacements.reduce((acc, cur) => acc + cur.replacementCount, 0);
        return project_paths.confirmAsSerializable({
            touchedFilesCount,
            replacementCount,
        });
    }
    async migrate(globalData) {
        return { replacements: globalData.tagReplacements.flatMap(({ replacements }) => replacements) };
    }
}
function prepareTextReplacement(file, replacement, start, end) {
    return new project_paths.Replacement(file, new project_paths.TextUpdate({
        position: start,
        end: end,
        toInsert: replacement,
    }));
}

function migrate(options) {
    return async (tree, context) => {
        await project_paths.runMigrationInDevkit({
            tree,
            getMigration: (fs) => new SelfClosingTagsMigration({
                shouldMigrate: (file) => {
                    return (file.rootRelativePath.startsWith(fs.normalize(options.path)) &&
                        !/(^|\/)node_modules\//.test(file.rootRelativePath));
                },
            }),
            beforeProgramCreation: (tsconfigPath, stage) => {
                if (stage === project_paths.MigrationStage.Analysis) {
                    context.logger.info(`Preparing analysis for: ${tsconfigPath}...`);
                }
                else {
                    context.logger.info(`Running migration for: ${tsconfigPath}...`);
                }
            },
            beforeUnitAnalysis: (tsconfigPath) => {
                context.logger.info(`Scanning for component tags: ${tsconfigPath}...`);
            },
            afterAllAnalyzed: () => {
                context.logger.info(``);
                context.logger.info(`Processing analysis data between targets...`);
                context.logger.info(``);
            },
            afterAnalysisFailure: () => {
                context.logger.error('Migration failed unexpectedly with no analysis data');
            },
            whenDone: ({ touchedFilesCount, replacementCount }) => {
                context.logger.info('');
                context.logger.info(`Successfully migrated to self-closing tags 🎉`);
                context.logger.info(`  -> Migrated ${replacementCount} components to self-closing tags in ${touchedFilesCount} component files.`);
            },
        });
    };
}

exports.migrate = migrate;
