@angular/core
Version: 
Angular - the core framework
1,180 lines (1,166 loc) • 886 kB
JavaScript
'use strict';
/**
 * @license Angular v19.2.7
 * (c) 2010-2025 Google LLC. https://angular.io/
 * License: MIT
 */
'use strict';
var checker = require('./checker-BNmiXJIJ.js');
var ts = require('typescript');
var p = require('path');
require('os');
function _interopNamespaceDefault(e) {
    var n = Object.create(null);
    if (e) {
        Object.keys(e).forEach(function (k) {
            if (k !== 'default') {
                var d = Object.getOwnPropertyDescriptor(e, k);
                Object.defineProperty(n, k, d.get ? d : {
                    enumerable: true,
                    get: function () { return e[k]; }
                });
            }
        });
    }
    n.default = e;
    return Object.freeze(n);
}
var p__namespace = /*#__PURE__*/_interopNamespaceDefault(p);
class XmlTagDefinition {
    closedByParent = false;
    implicitNamespacePrefix = null;
    isVoid = false;
    ignoreFirstLf = false;
    canSelfClose = true;
    preventNamespaceInheritance = false;
    requireExtraParent(currentParent) {
        return false;
    }
    isClosedByChild(name) {
        return false;
    }
    getContentType() {
        return checker.TagContentType.PARSABLE_DATA;
    }
}
const _TAG_DEFINITION = new XmlTagDefinition();
function getXmlTagDefinition(tagName) {
    return _TAG_DEFINITION;
}
class XmlParser extends checker.Parser {
    constructor() {
        super(getXmlTagDefinition);
    }
    parse(source, url, options = {}) {
        // Blocks and let declarations aren't supported in an XML context.
        return super.parse(source, url, { ...options, tokenizeBlocks: false, tokenizeLet: false });
    }
}
const _VERSION$1 = '1.2';
const _XMLNS$1 = 'urn:oasis:names:tc:xliff:document:1.2';
// TODO(vicb): make this a param (s/_/-/)
const _DEFAULT_SOURCE_LANG$1 = 'en';
const _PLACEHOLDER_TAG$1 = 'x';
const _MARKER_TAG$1 = 'mrk';
const _FILE_TAG = 'file';
const _SOURCE_TAG$1 = 'source';
const _SEGMENT_SOURCE_TAG = 'seg-source';
const _ALT_TRANS_TAG = 'alt-trans';
const _TARGET_TAG$1 = 'target';
const _UNIT_TAG$1 = 'trans-unit';
const _CONTEXT_GROUP_TAG = 'context-group';
const _CONTEXT_TAG = 'context';
// https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
// https://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html
class Xliff extends checker.Serializer {
    write(messages, locale) {
        const visitor = new _WriteVisitor$1();
        const transUnits = [];
        messages.forEach((message) => {
            let contextTags = [];
            message.sources.forEach((source) => {
                let contextGroupTag = new checker.Tag(_CONTEXT_GROUP_TAG, { purpose: 'location' });
                contextGroupTag.children.push(new checker.CR(10), new checker.Tag(_CONTEXT_TAG, { 'context-type': 'sourcefile' }, [
                    new checker.Text$1(source.filePath),
                ]), new checker.CR(10), new checker.Tag(_CONTEXT_TAG, { 'context-type': 'linenumber' }, [
                    new checker.Text$1(`${source.startLine}`),
                ]), new checker.CR(8));
                contextTags.push(new checker.CR(8), contextGroupTag);
            });
            const transUnit = new checker.Tag(_UNIT_TAG$1, { id: message.id, datatype: 'html' });
            transUnit.children.push(new checker.CR(8), new checker.Tag(_SOURCE_TAG$1, {}, visitor.serialize(message.nodes)), ...contextTags);
            if (message.description) {
                transUnit.children.push(new checker.CR(8), new checker.Tag('note', { priority: '1', from: 'description' }, [
                    new checker.Text$1(message.description),
                ]));
            }
            if (message.meaning) {
                transUnit.children.push(new checker.CR(8), new checker.Tag('note', { priority: '1', from: 'meaning' }, [new checker.Text$1(message.meaning)]));
            }
            transUnit.children.push(new checker.CR(6));
            transUnits.push(new checker.CR(6), transUnit);
        });
        const body = new checker.Tag('body', {}, [...transUnits, new checker.CR(4)]);
        const file = new checker.Tag('file', {
            'source-language': locale || _DEFAULT_SOURCE_LANG$1,
            datatype: 'plaintext',
            original: 'ng2.template',
        }, [new checker.CR(4), body, new checker.CR(2)]);
        const xliff = new checker.Tag('xliff', { version: _VERSION$1, xmlns: _XMLNS$1 }, [
            new checker.CR(2),
            file,
            new checker.CR(),
        ]);
        return checker.serialize([
            new checker.Declaration({ version: '1.0', encoding: 'UTF-8' }),
            new checker.CR(),
            xliff,
            new checker.CR(),
        ]);
    }
    load(content, url) {
        // xliff to xml nodes
        const xliffParser = new XliffParser();
        const { locale, msgIdToHtml, errors } = xliffParser.parse(content, url);
        // xml nodes to i18n nodes
        const i18nNodesByMsgId = {};
        const converter = new XmlToI18n$1();
        Object.keys(msgIdToHtml).forEach((msgId) => {
            const { i18nNodes, errors: e } = converter.convert(msgIdToHtml[msgId], url);
            errors.push(...e);
            i18nNodesByMsgId[msgId] = i18nNodes;
        });
        if (errors.length) {
            throw new Error(`xliff parse errors:\n${errors.join('\n')}`);
        }
        return { locale: locale, i18nNodesByMsgId };
    }
    digest(message) {
        return checker.digest(message);
    }
}
let _WriteVisitor$1 = class _WriteVisitor {
    visitText(text, context) {
        return [new checker.Text$1(text.value)];
    }
    visitContainer(container, context) {
        const nodes = [];
        container.children.forEach((node) => nodes.push(...node.visit(this)));
        return nodes;
    }
    visitIcu(icu, context) {
        const nodes = [new checker.Text$1(`{${icu.expressionPlaceholder}, ${icu.type}, `)];
        Object.keys(icu.cases).forEach((c) => {
            nodes.push(new checker.Text$1(`${c} {`), ...icu.cases[c].visit(this), new checker.Text$1(`} `));
        });
        nodes.push(new checker.Text$1(`}`));
        return nodes;
    }
    visitTagPlaceholder(ph, context) {
        const ctype = getCtypeForTag(ph.tag);
        if (ph.isVoid) {
            // void tags have no children nor closing tags
            return [
                new checker.Tag(_PLACEHOLDER_TAG$1, { id: ph.startName, ctype, 'equiv-text': `<${ph.tag}/>` }),
            ];
        }
        const startTagPh = new checker.Tag(_PLACEHOLDER_TAG$1, {
            id: ph.startName,
            ctype,
            'equiv-text': `<${ph.tag}>`,
        });
        const closeTagPh = new checker.Tag(_PLACEHOLDER_TAG$1, {
            id: ph.closeName,
            ctype,
            'equiv-text': `</${ph.tag}>`,
        });
        return [startTagPh, ...this.serialize(ph.children), closeTagPh];
    }
    visitPlaceholder(ph, context) {
        return [new checker.Tag(_PLACEHOLDER_TAG$1, { id: ph.name, 'equiv-text': `{{${ph.value}}}` })];
    }
    visitBlockPlaceholder(ph, context) {
        const ctype = `x-${ph.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
        const startTagPh = new checker.Tag(_PLACEHOLDER_TAG$1, {
            id: ph.startName,
            ctype,
            'equiv-text': `@${ph.name}`,
        });
        const closeTagPh = new checker.Tag(_PLACEHOLDER_TAG$1, { id: ph.closeName, ctype, 'equiv-text': `}` });
        return [startTagPh, ...this.serialize(ph.children), closeTagPh];
    }
    visitIcuPlaceholder(ph, context) {
        const equivText = `{${ph.value.expression}, ${ph.value.type}, ${Object.keys(ph.value.cases)
            .map((value) => value + ' {...}')
            .join(' ')}}`;
        return [new checker.Tag(_PLACEHOLDER_TAG$1, { id: ph.name, 'equiv-text': equivText })];
    }
    serialize(nodes) {
        return [].concat(...nodes.map((node) => node.visit(this)));
    }
};
// TODO(vicb): add error management (structure)
// Extract messages as xml nodes from the xliff file
class XliffParser {
    // using non-null assertions because they're re(set) by parse()
    _unitMlString;
    _errors;
    _msgIdToHtml;
    _locale = null;
    parse(xliff, url) {
        this._unitMlString = null;
        this._msgIdToHtml = {};
        const xml = new XmlParser().parse(xliff, url);
        this._errors = xml.errors;
        checker.visitAll(this, xml.rootNodes, null);
        return {
            msgIdToHtml: this._msgIdToHtml,
            errors: this._errors,
            locale: this._locale,
        };
    }
    visitElement(element, context) {
        switch (element.name) {
            case _UNIT_TAG$1:
                this._unitMlString = null;
                const idAttr = element.attrs.find((attr) => attr.name === 'id');
                if (!idAttr) {
                    this._addError(element, `<${_UNIT_TAG$1}> misses the "id" attribute`);
                }
                else {
                    const id = idAttr.value;
                    if (this._msgIdToHtml.hasOwnProperty(id)) {
                        this._addError(element, `Duplicated translations for msg ${id}`);
                    }
                    else {
                        checker.visitAll(this, element.children, null);
                        if (typeof this._unitMlString === 'string') {
                            this._msgIdToHtml[id] = this._unitMlString;
                        }
                        else {
                            this._addError(element, `Message ${id} misses a translation`);
                        }
                    }
                }
                break;
            // ignore those tags
            case _SOURCE_TAG$1:
            case _SEGMENT_SOURCE_TAG:
            case _ALT_TRANS_TAG:
                break;
            case _TARGET_TAG$1:
                const innerTextStart = element.startSourceSpan.end.offset;
                const innerTextEnd = element.endSourceSpan.start.offset;
                const content = element.startSourceSpan.start.file.content;
                const innerText = content.slice(innerTextStart, innerTextEnd);
                this._unitMlString = innerText;
                break;
            case _FILE_TAG:
                const localeAttr = element.attrs.find((attr) => attr.name === 'target-language');
                if (localeAttr) {
                    this._locale = localeAttr.value;
                }
                checker.visitAll(this, element.children, null);
                break;
            default:
                // TODO(vicb): assert file structure, xliff version
                // For now only recurse on unhandled nodes
                checker.visitAll(this, element.children, null);
        }
    }
    visitAttribute(attribute, context) { }
    visitText(text, context) { }
    visitComment(comment, context) { }
    visitExpansion(expansion, context) { }
    visitExpansionCase(expansionCase, context) { }
    visitBlock(block, context) { }
    visitBlockParameter(parameter, context) { }
    visitLetDeclaration(decl, context) { }
    _addError(node, message) {
        this._errors.push(new checker.I18nError(node.sourceSpan, message));
    }
}
// Convert ml nodes (xliff syntax) to i18n nodes
let XmlToI18n$1 = class XmlToI18n {
    // using non-null assertion because it's re(set) by convert()
    _errors;
    convert(message, url) {
        const xmlIcu = new XmlParser().parse(message, url, { tokenizeExpansionForms: true });
        this._errors = xmlIcu.errors;
        const i18nNodes = this._errors.length > 0 || xmlIcu.rootNodes.length == 0
            ? []
            : [].concat(...checker.visitAll(this, xmlIcu.rootNodes));
        return {
            i18nNodes: i18nNodes,
            errors: this._errors,
        };
    }
    visitText(text, context) {
        return new checker.Text$2(text.value, text.sourceSpan);
    }
    visitElement(el, context) {
        if (el.name === _PLACEHOLDER_TAG$1) {
            const nameAttr = el.attrs.find((attr) => attr.name === 'id');
            if (nameAttr) {
                return new checker.Placeholder('', nameAttr.value, el.sourceSpan);
            }
            this._addError(el, `<${_PLACEHOLDER_TAG$1}> misses the "id" attribute`);
            return null;
        }
        if (el.name === _MARKER_TAG$1) {
            return [].concat(...checker.visitAll(this, el.children));
        }
        this._addError(el, `Unexpected tag`);
        return null;
    }
    visitExpansion(icu, context) {
        const caseMap = {};
        checker.visitAll(this, icu.cases).forEach((c) => {
            caseMap[c.value] = new checker.Container(c.nodes, icu.sourceSpan);
        });
        return new checker.Icu(icu.switchValue, icu.type, caseMap, icu.sourceSpan);
    }
    visitExpansionCase(icuCase, context) {
        return {
            value: icuCase.value,
            nodes: checker.visitAll(this, icuCase.expression),
        };
    }
    visitComment(comment, context) { }
    visitAttribute(attribute, context) { }
    visitBlock(block, context) { }
    visitBlockParameter(parameter, context) { }
    visitLetDeclaration(decl, context) { }
    _addError(node, message) {
        this._errors.push(new checker.I18nError(node.sourceSpan, message));
    }
};
function getCtypeForTag(tag) {
    switch (tag.toLowerCase()) {
        case 'br':
            return 'lb';
        case 'img':
            return 'image';
        default:
            return `x-${tag}`;
    }
}
const _VERSION = '2.0';
const _XMLNS = 'urn:oasis:names:tc:xliff:document:2.0';
// TODO(vicb): make this a param (s/_/-/)
const _DEFAULT_SOURCE_LANG = 'en';
const _PLACEHOLDER_TAG = 'ph';
const _PLACEHOLDER_SPANNING_TAG = 'pc';
const _MARKER_TAG = 'mrk';
const _XLIFF_TAG = 'xliff';
const _SOURCE_TAG = 'source';
const _TARGET_TAG = 'target';
const _UNIT_TAG = 'unit';
// https://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html
class Xliff2 extends checker.Serializer {
    write(messages, locale) {
        const visitor = new _WriteVisitor();
        const units = [];
        messages.forEach((message) => {
            const unit = new checker.Tag(_UNIT_TAG, { id: message.id });
            const notes = new checker.Tag('notes');
            if (message.description || message.meaning) {
                if (message.description) {
                    notes.children.push(new checker.CR(8), new checker.Tag('note', { category: 'description' }, [new checker.Text$1(message.description)]));
                }
                if (message.meaning) {
                    notes.children.push(new checker.CR(8), new checker.Tag('note', { category: 'meaning' }, [new checker.Text$1(message.meaning)]));
                }
            }
            message.sources.forEach((source) => {
                notes.children.push(new checker.CR(8), new checker.Tag('note', { category: 'location' }, [
                    new checker.Text$1(`${source.filePath}:${source.startLine}${source.endLine !== source.startLine ? ',' + source.endLine : ''}`),
                ]));
            });
            notes.children.push(new checker.CR(6));
            unit.children.push(new checker.CR(6), notes);
            const segment = new checker.Tag('segment');
            segment.children.push(new checker.CR(8), new checker.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)), new checker.CR(6));
            unit.children.push(new checker.CR(6), segment, new checker.CR(4));
            units.push(new checker.CR(4), unit);
        });
        const file = new checker.Tag('file', { 'original': 'ng.template', id: 'ngi18n' }, [
            ...units,
            new checker.CR(2),
        ]);
        const xliff = new checker.Tag(_XLIFF_TAG, { version: _VERSION, xmlns: _XMLNS, srcLang: locale || _DEFAULT_SOURCE_LANG }, [new checker.CR(2), file, new checker.CR()]);
        return checker.serialize([
            new checker.Declaration({ version: '1.0', encoding: 'UTF-8' }),
            new checker.CR(),
            xliff,
            new checker.CR(),
        ]);
    }
    load(content, url) {
        // xliff to xml nodes
        const xliff2Parser = new Xliff2Parser();
        const { locale, msgIdToHtml, errors } = xliff2Parser.parse(content, url);
        // xml nodes to i18n nodes
        const i18nNodesByMsgId = {};
        const converter = new XmlToI18n();
        Object.keys(msgIdToHtml).forEach((msgId) => {
            const { i18nNodes, errors: e } = converter.convert(msgIdToHtml[msgId], url);
            errors.push(...e);
            i18nNodesByMsgId[msgId] = i18nNodes;
        });
        if (errors.length) {
            throw new Error(`xliff2 parse errors:\n${errors.join('\n')}`);
        }
        return { locale: locale, i18nNodesByMsgId };
    }
    digest(message) {
        return checker.decimalDigest(message);
    }
}
class _WriteVisitor {
    _nextPlaceholderId = 0;
    visitText(text, context) {
        return [new checker.Text$1(text.value)];
    }
    visitContainer(container, context) {
        const nodes = [];
        container.children.forEach((node) => nodes.push(...node.visit(this)));
        return nodes;
    }
    visitIcu(icu, context) {
        const nodes = [new checker.Text$1(`{${icu.expressionPlaceholder}, ${icu.type}, `)];
        Object.keys(icu.cases).forEach((c) => {
            nodes.push(new checker.Text$1(`${c} {`), ...icu.cases[c].visit(this), new checker.Text$1(`} `));
        });
        nodes.push(new checker.Text$1(`}`));
        return nodes;
    }
    visitTagPlaceholder(ph, context) {
        const type = getTypeForTag(ph.tag);
        if (ph.isVoid) {
            const tagPh = new checker.Tag(_PLACEHOLDER_TAG, {
                id: (this._nextPlaceholderId++).toString(),
                equiv: ph.startName,
                type: type,
                disp: `<${ph.tag}/>`,
            });
            return [tagPh];
        }
        const tagPc = new checker.Tag(_PLACEHOLDER_SPANNING_TAG, {
            id: (this._nextPlaceholderId++).toString(),
            equivStart: ph.startName,
            equivEnd: ph.closeName,
            type: type,
            dispStart: `<${ph.tag}>`,
            dispEnd: `</${ph.tag}>`,
        });
        const nodes = [].concat(...ph.children.map((node) => node.visit(this)));
        if (nodes.length) {
            nodes.forEach((node) => tagPc.children.push(node));
        }
        else {
            tagPc.children.push(new checker.Text$1(''));
        }
        return [tagPc];
    }
    visitPlaceholder(ph, context) {
        const idStr = (this._nextPlaceholderId++).toString();
        return [
            new checker.Tag(_PLACEHOLDER_TAG, {
                id: idStr,
                equiv: ph.name,
                disp: `{{${ph.value}}}`,
            }),
        ];
    }
    visitBlockPlaceholder(ph, context) {
        const tagPc = new checker.Tag(_PLACEHOLDER_SPANNING_TAG, {
            id: (this._nextPlaceholderId++).toString(),
            equivStart: ph.startName,
            equivEnd: ph.closeName,
            type: 'other',
            dispStart: `@${ph.name}`,
            dispEnd: `}`,
        });
        const nodes = [].concat(...ph.children.map((node) => node.visit(this)));
        if (nodes.length) {
            nodes.forEach((node) => tagPc.children.push(node));
        }
        else {
            tagPc.children.push(new checker.Text$1(''));
        }
        return [tagPc];
    }
    visitIcuPlaceholder(ph, context) {
        const cases = Object.keys(ph.value.cases)
            .map((value) => value + ' {...}')
            .join(' ');
        const idStr = (this._nextPlaceholderId++).toString();
        return [
            new checker.Tag(_PLACEHOLDER_TAG, {
                id: idStr,
                equiv: ph.name,
                disp: `{${ph.value.expression}, ${ph.value.type}, ${cases}}`,
            }),
        ];
    }
    serialize(nodes) {
        this._nextPlaceholderId = 0;
        return [].concat(...nodes.map((node) => node.visit(this)));
    }
}
// Extract messages as xml nodes from the xliff file
class Xliff2Parser {
    // using non-null assertions because they're all (re)set by parse()
    _unitMlString;
    _errors;
    _msgIdToHtml;
    _locale = null;
    parse(xliff, url) {
        this._unitMlString = null;
        this._msgIdToHtml = {};
        const xml = new XmlParser().parse(xliff, url);
        this._errors = xml.errors;
        checker.visitAll(this, xml.rootNodes, null);
        return {
            msgIdToHtml: this._msgIdToHtml,
            errors: this._errors,
            locale: this._locale,
        };
    }
    visitElement(element, context) {
        switch (element.name) {
            case _UNIT_TAG:
                this._unitMlString = null;
                const idAttr = element.attrs.find((attr) => attr.name === 'id');
                if (!idAttr) {
                    this._addError(element, `<${_UNIT_TAG}> misses the "id" attribute`);
                }
                else {
                    const id = idAttr.value;
                    if (this._msgIdToHtml.hasOwnProperty(id)) {
                        this._addError(element, `Duplicated translations for msg ${id}`);
                    }
                    else {
                        checker.visitAll(this, element.children, null);
                        if (typeof this._unitMlString === 'string') {
                            this._msgIdToHtml[id] = this._unitMlString;
                        }
                        else {
                            this._addError(element, `Message ${id} misses a translation`);
                        }
                    }
                }
                break;
            case _SOURCE_TAG:
                // ignore source message
                break;
            case _TARGET_TAG:
                const innerTextStart = element.startSourceSpan.end.offset;
                const innerTextEnd = element.endSourceSpan.start.offset;
                const content = element.startSourceSpan.start.file.content;
                const innerText = content.slice(innerTextStart, innerTextEnd);
                this._unitMlString = innerText;
                break;
            case _XLIFF_TAG:
                const localeAttr = element.attrs.find((attr) => attr.name === 'trgLang');
                if (localeAttr) {
                    this._locale = localeAttr.value;
                }
                const versionAttr = element.attrs.find((attr) => attr.name === 'version');
                if (versionAttr) {
                    const version = versionAttr.value;
                    if (version !== '2.0') {
                        this._addError(element, `The XLIFF file version ${version} is not compatible with XLIFF 2.0 serializer`);
                    }
                    else {
                        checker.visitAll(this, element.children, null);
                    }
                }
                break;
            default:
                checker.visitAll(this, element.children, null);
        }
    }
    visitAttribute(attribute, context) { }
    visitText(text, context) { }
    visitComment(comment, context) { }
    visitExpansion(expansion, context) { }
    visitExpansionCase(expansionCase, context) { }
    visitBlock(block, context) { }
    visitBlockParameter(parameter, context) { }
    visitLetDeclaration(decl, context) { }
    _addError(node, message) {
        this._errors.push(new checker.I18nError(node.sourceSpan, message));
    }
}
// Convert ml nodes (xliff syntax) to i18n nodes
class XmlToI18n {
    // using non-null assertion because re(set) by convert()
    _errors;
    convert(message, url) {
        const xmlIcu = new XmlParser().parse(message, url, { tokenizeExpansionForms: true });
        this._errors = xmlIcu.errors;
        const i18nNodes = this._errors.length > 0 || xmlIcu.rootNodes.length == 0
            ? []
            : [].concat(...checker.visitAll(this, xmlIcu.rootNodes));
        return {
            i18nNodes,
            errors: this._errors,
        };
    }
    visitText(text, context) {
        return new checker.Text$2(text.value, text.sourceSpan);
    }
    visitElement(el, context) {
        switch (el.name) {
            case _PLACEHOLDER_TAG:
                const nameAttr = el.attrs.find((attr) => attr.name === 'equiv');
                if (nameAttr) {
                    return [new checker.Placeholder('', nameAttr.value, el.sourceSpan)];
                }
                this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "equiv" attribute`);
                break;
            case _PLACEHOLDER_SPANNING_TAG:
                const startAttr = el.attrs.find((attr) => attr.name === 'equivStart');
                const endAttr = el.attrs.find((attr) => attr.name === 'equivEnd');
                if (!startAttr) {
                    this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "equivStart" attribute`);
                }
                else if (!endAttr) {
                    this._addError(el, `<${_PLACEHOLDER_TAG}> misses the "equivEnd" attribute`);
                }
                else {
                    const startId = startAttr.value;
                    const endId = endAttr.value;
                    const nodes = [];
                    return nodes.concat(new checker.Placeholder('', startId, el.sourceSpan), ...el.children.map((node) => node.visit(this, null)), new checker.Placeholder('', endId, el.sourceSpan));
                }
                break;
            case _MARKER_TAG:
                return [].concat(...checker.visitAll(this, el.children));
            default:
                this._addError(el, `Unexpected tag`);
        }
        return null;
    }
    visitExpansion(icu, context) {
        const caseMap = {};
        checker.visitAll(this, icu.cases).forEach((c) => {
            caseMap[c.value] = new checker.Container(c.nodes, icu.sourceSpan);
        });
        return new checker.Icu(icu.switchValue, icu.type, caseMap, icu.sourceSpan);
    }
    visitExpansionCase(icuCase, context) {
        return {
            value: icuCase.value,
            nodes: [].concat(...checker.visitAll(this, icuCase.expression)),
        };
    }
    visitComment(comment, context) { }
    visitAttribute(attribute, context) { }
    visitBlock(block, context) { }
    visitBlockParameter(parameter, context) { }
    visitLetDeclaration(decl, context) { }
    _addError(node, message) {
        this._errors.push(new checker.I18nError(node.sourceSpan, message));
    }
}
function getTypeForTag(tag) {
    switch (tag.toLowerCase()) {
        case 'br':
        case 'b':
        case 'i':
        case 'u':
            return 'fmt';
        case 'img':
            return 'image';
        case 'a':
            return 'link';
        default:
            return 'other';
    }
}
/**
 * A container for message extracted from the templates.
 */
class MessageBundle {
    _htmlParser;
    _implicitTags;
    _implicitAttrs;
    _locale;
    _preserveWhitespace;
    _messages = [];
    constructor(_htmlParser, _implicitTags, _implicitAttrs, _locale = null, _preserveWhitespace = true) {
        this._htmlParser = _htmlParser;
        this._implicitTags = _implicitTags;
        this._implicitAttrs = _implicitAttrs;
        this._locale = _locale;
        this._preserveWhitespace = _preserveWhitespace;
    }
    updateFromTemplate(source, url, interpolationConfig) {
        const htmlParserResult = this._htmlParser.parse(source, url, {
            tokenizeExpansionForms: true,
            interpolationConfig,
        });
        if (htmlParserResult.errors.length) {
            return htmlParserResult.errors;
        }
        // Trim unnecessary whitespace from extracted messages if requested. This
        // makes the messages more durable to trivial whitespace changes without
        // affected message IDs.
        const rootNodes = this._preserveWhitespace
            ? htmlParserResult.rootNodes
            : checker.visitAllWithSiblings(new checker.WhitespaceVisitor(/* preserveSignificantWhitespace */ false), htmlParserResult.rootNodes);
        const i18nParserResult = checker.extractMessages(rootNodes, interpolationConfig, this._implicitTags, this._implicitAttrs, 
        /* preserveSignificantWhitespace */ this._preserveWhitespace);
        if (i18nParserResult.errors.length) {
            return i18nParserResult.errors;
        }
        this._messages.push(...i18nParserResult.messages);
        return [];
    }
    // Return the message in the internal format
    // The public (serialized) format might be different, see the `write` method.
    getMessages() {
        return this._messages;
    }
    write(serializer, filterSources) {
        const messages = {};
        const mapperVisitor = new MapPlaceholderNames();
        // Deduplicate messages based on their ID
        this._messages.forEach((message) => {
            const id = serializer.digest(message);
            if (!messages.hasOwnProperty(id)) {
                messages[id] = message;
            }
            else {
                messages[id].sources.push(...message.sources);
            }
        });
        // Transform placeholder names using the serializer mapping
        const msgList = Object.keys(messages).map((id) => {
            const mapper = serializer.createNameMapper(messages[id]);
            const src = messages[id];
            const nodes = mapper ? mapperVisitor.convert(src.nodes, mapper) : src.nodes;
            let transformedMessage = new checker.Message(nodes, {}, {}, src.meaning, src.description, id);
            transformedMessage.sources = src.sources;
            if (filterSources) {
                transformedMessage.sources.forEach((source) => (source.filePath = filterSources(source.filePath)));
            }
            return transformedMessage;
        });
        return serializer.write(msgList, this._locale);
    }
}
// Transform an i18n AST by renaming the placeholder nodes with the given mapper
class MapPlaceholderNames extends checker.CloneVisitor {
    convert(nodes, mapper) {
        return mapper ? nodes.map((n) => n.visit(this, mapper)) : nodes;
    }
    visitTagPlaceholder(ph, mapper) {
        const startName = mapper.toPublicName(ph.startName);
        const closeName = ph.closeName ? mapper.toPublicName(ph.closeName) : ph.closeName;
        const children = ph.children.map((n) => n.visit(this, mapper));
        return new checker.TagPlaceholder(ph.tag, ph.attrs, startName, closeName, children, ph.isVoid, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
    }
    visitBlockPlaceholder(ph, mapper) {
        const startName = mapper.toPublicName(ph.startName);
        const closeName = ph.closeName ? mapper.toPublicName(ph.closeName) : ph.closeName;
        const children = ph.children.map((n) => n.visit(this, mapper));
        return new checker.BlockPlaceholder(ph.name, ph.parameters, startName, closeName, children, ph.sourceSpan, ph.startSourceSpan, ph.endSourceSpan);
    }
    visitPlaceholder(ph, mapper) {
        return new checker.Placeholder(ph.value, mapper.toPublicName(ph.name), ph.sourceSpan);
    }
    visitIcuPlaceholder(ph, mapper) {
        return new checker.IcuPlaceholder(ph.value, mapper.toPublicName(ph.name), ph.sourceSpan);
    }
}
function compileClassMetadata(metadata) {
    const fnCall = internalCompileClassMetadata(metadata);
    return checker.arrowFn([], [checker.devOnlyGuardedExpression(fnCall).toStmt()]).callFn([]);
}
/** Compiles only the `setClassMetadata` call without any additional wrappers. */
function internalCompileClassMetadata(metadata) {
    return checker.importExpr(checker.Identifiers.setClassMetadata)
        .callFn([
        metadata.type,
        metadata.decorators,
        metadata.ctorParameters ?? checker.literal(null),
        metadata.propDecorators ?? checker.literal(null),
    ]);
}
/**
 * Wraps the `setClassMetadata` function with extra logic that dynamically
 * loads dependencies from `@defer` blocks.
 *
 * Generates a call like this:
 * ```ts
 * setClassMetadataAsync(type, () => [
 *   import('./cmp-a').then(m => m.CmpA);
 *   import('./cmp-b').then(m => m.CmpB);
 * ], (CmpA, CmpB) => {
 *   setClassMetadata(type, decorators, ctorParameters, propParameters);
 * });
 * ```
 *
 * Similar to the `setClassMetadata` call, it's wrapped into the `ngDevMode`
 * check to tree-shake away this code in production mode.
 */
function compileComponentClassMetadata(metadata, dependencies) {
    if (dependencies === null || dependencies.length === 0) {
        // If there are no deferrable symbols - just generate a regular `setClassMetadata` call.
        return compileClassMetadata(metadata);
    }
    return internalCompileSetClassMetadataAsync(metadata, dependencies.map((dep) => new checker.FnParam(dep.symbolName, checker.DYNAMIC_TYPE)), compileComponentMetadataAsyncResolver(dependencies));
}
/**
 * Internal logic used to compile a `setClassMetadataAsync` call.
 * @param metadata Class metadata for the internal `setClassMetadata` call.
 * @param wrapperParams Parameters to be set on the callback that wraps `setClassMetata`.
 * @param dependencyResolverFn Function to resolve the deferred dependencies.
 */
function internalCompileSetClassMetadataAsync(metadata, wrapperParams, dependencyResolverFn) {
    // Omit the wrapper since it'll be added around `setClassMetadataAsync` instead.
    const setClassMetadataCall = internalCompileClassMetadata(metadata);
    const setClassMetaWrapper = checker.arrowFn(wrapperParams, [setClassMetadataCall.toStmt()]);
    const setClassMetaAsync = checker.importExpr(checker.Identifiers.setClassMetadataAsync)
        .callFn([metadata.type, dependencyResolverFn, setClassMetaWrapper]);
    return checker.arrowFn([], [checker.devOnlyGuardedExpression(setClassMetaAsync).toStmt()]).callFn([]);
}
/**
 * Compiles the function that loads the dependencies for the
 * entire component in `setClassMetadataAsync`.
 */
function compileComponentMetadataAsyncResolver(dependencies) {
    const dynamicImports = dependencies.map(({ symbolName, importPath, isDefaultImport }) => {
        // e.g. `(m) => m.CmpA`
        const innerFn = 
        // Default imports are always accessed through the `default` property.
        checker.arrowFn([new checker.FnParam('m', checker.DYNAMIC_TYPE)], checker.variable('m').prop(isDefaultImport ? 'default' : symbolName));
        // e.g. `import('./cmp-a').then(...)`
        return new checker.DynamicImportExpr(importPath).prop('then').callFn([innerFn]);
    });
    // e.g. `() => [ ... ];`
    return checker.arrowFn([], checker.literalArr(dynamicImports));
}
/**
 * Generate an ngDevMode guarded call to setClassDebugInfo with the debug info about the class
 * (e.g., the file name in which the class is defined)
 */
function compileClassDebugInfo(debugInfo) {
    const debugInfoObject = {
        className: debugInfo.className,
    };
    // Include file path and line number only if the file relative path is calculated successfully.
    if (debugInfo.filePath) {
        debugInfoObject.filePath = debugInfo.filePath;
        debugInfoObject.lineNumber = debugInfo.lineNumber;
    }
    // Include forbidOrphanRendering only if it's set to true (to reduce generated code)
    if (debugInfo.forbidOrphanRendering) {
        debugInfoObject.forbidOrphanRendering = checker.literal(true);
    }
    const fnCall = checker.importExpr(checker.Identifiers.setClassDebugInfo)
        .callFn([debugInfo.type, checker.mapLiteral(debugInfoObject)]);
    const iife = checker.arrowFn([], [checker.devOnlyGuardedExpression(fnCall).toStmt()]);
    return iife.callFn([]);
}
/*!
 * @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
 */
/**
 * Compiles the expression that initializes HMR for a class.
 * @param meta HMR metadata extracted from the class.
 */
function compileHmrInitializer(meta) {
    const moduleName = 'm';
    const dataName = 'd';
    const timestampName = 't';
    const idName = 'id';
    const importCallbackName = `${meta.className}_HmrLoad`;
    const namespaces = meta.namespaceDependencies.map((dep) => {
        return new checker.ExternalExpr({ moduleName: dep.moduleName, name: null });
    });
    // m.default
    const defaultRead = checker.variable(moduleName).prop('default');
    // ɵɵreplaceMetadata(Comp, m.default, [...namespaces], [...locals], import.meta, id);
    const replaceCall = checker.importExpr(checker.Identifiers.replaceMetadata)
        .callFn([
        meta.type,
        defaultRead,
        checker.literalArr(namespaces),
        checker.literalArr(meta.localDependencies.map((l) => l.runtimeRepresentation)),
        checker.variable('import').prop('meta'),
        checker.variable(idName),
    ]);
    // (m) => m.default && ɵɵreplaceMetadata(...)
    const replaceCallback = checker.arrowFn([new checker.FnParam(moduleName)], defaultRead.and(replaceCall));
    // '<url>?c=' + id + '&t=' + encodeURIComponent(t)
    const urlValue = checker.literal(`./@ng/component?c=`)
        .plus(checker.variable(idName))
        .plus(checker.literal('&t='))
        .plus(checker.variable('encodeURIComponent').callFn([checker.variable(timestampName)]));
    // import.meta.url
    const urlBase = checker.variable('import').prop('meta').prop('url');
    // new URL(urlValue, urlBase).href
    const urlHref = new checker.InstantiateExpr(checker.variable('URL'), [urlValue, urlBase]).prop('href');
    // function Cmp_HmrLoad(t) {
    //   import(/* @vite-ignore */ urlHref).then((m) => m.default && replaceMetadata(...));
    // }
    const importCallback = new checker.DeclareFunctionStmt(importCallbackName, [new checker.FnParam(timestampName)], [
        // The vite-ignore special comment is required to prevent Vite from generating a superfluous
        // warning for each usage within the development code. If Vite provides a method to
        // programmatically avoid this warning in the future, this added comment can be removed here.
        new checker.DynamicImportExpr(urlHref, null, '@vite-ignore')
            .prop('then')
            .callFn([replaceCallback])
            .toStmt(),
    ], null, checker.StmtModifier.Final);
    // (d) => d.id === id && Cmp_HmrLoad(d.timestamp)
    const updateCallback = checker.arrowFn([new checker.FnParam(dataName)], checker.variable(dataName)
        .prop('id')
        .identical(checker.variable(idName))
        .and(checker.variable(importCallbackName).callFn([checker.variable(dataName).prop('timestamp')])));
    // Cmp_HmrLoad(Date.now());
    // Initial call to kick off the loading in order to avoid edge cases with components
    // coming from lazy chunks that change before the chunk has loaded.
    const initialCall = checker.variable(importCallbackName)
        .callFn([checker.variable('Date').prop('now').callFn([])]);
    // import.meta.hot
    const hotRead = checker.variable('import').prop('meta').prop('hot');
    // import.meta.hot.on('angular:component-update', () => ...);
    const hotListener = hotRead
        .clone()
        .prop('on')
        .callFn([checker.literal('angular:component-update'), updateCallback]);
    return checker.arrowFn([], [
        // const id = <id>;
        new checker.DeclareVarStmt(idName, checker.literal(encodeURIComponent(`${meta.filePath}@${meta.className}`)), null, checker.StmtModifier.Final),
        // function Cmp_HmrLoad() {...}.
        importCallback,
        // ngDevMode && Cmp_HmrLoad(Date.now());
        checker.devOnlyGuardedExpression(initialCall).toStmt(),
        // ngDevMode && import.meta.hot && import.meta.hot.on(...)
        checker.devOnlyGuardedExpression(hotRead.and(hotListener)).toStmt(),
    ])
        .callFn([]);
}
/**
 * Compiles the HMR update callback for a class.
 * @param definitions Compiled definitions for the class (e.g. `defineComponent` calls).
 * @param constantStatements Supporting constants statements that were generated alongside
 *  the definition.
 * @param meta HMR metadata extracted from the class.
 */
function compileHmrUpdateCallback(definitions, constantStatements, meta) {
    const namespaces = 'ɵɵnamespaces';
    const params = [meta.className, namespaces].map((name) => new checker.FnParam(name, checker.DYNAMIC_TYPE));
    const body = [];
    for (const local of meta.localDependencies) {
        params.push(new checker.FnParam(local.name));
    }
    // Declare variables that read out the individual namespaces.
    for (let i = 0; i < meta.namespaceDependencies.length; i++) {
        body.push(new checker.DeclareVarStmt(meta.namespaceDependencies[i].assignedName, checker.variable(namespaces).key(checker.literal(i)), checker.DYNAMIC_TYPE, checker.StmtModifier.Final));
    }
    body.push(...constantStatements);
    for (const field of definitions) {
        if (field.initializer !== null) {
            body.push(checker.variable(meta.className).prop(field.name).set(field.initializer).toStmt());
            for (const stmt of field.statements) {
                body.push(stmt);
            }
        }
    }
    return new checker.DeclareFunctionStmt(`${meta.className}_UpdateMetadata`, params, body, null, checker.StmtModifier.Final);
}
/**
 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we
 * must update this constant to prevent old partial-linkers from incorrectly processing the
 * declaration.
 *
 * Do not include any prerelease in these versions as they are ignored.
 */
const MINIMUM_PARTIAL_LINKER_VERSION$5 = '12.0.0';
/**
 * Minimum version at which deferred blocks are supported in the linker.
 */
const MINIMUM_PARTIAL_LINKER_DEFER_SUPPORT_VERSION = '18.0.0';
function compileDeclareClassMetadata(metadata) {
    const definitionMap = new checker.DefinitionMap();
    definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
    definitionMap.set('version', checker.literal('19.2.7'));
    definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
    definitionMap.set('type', metadata.type);
    definitionMap.set('decorators', metadata.decorators);
    definitionMap.set('ctorParameters', metadata.ctorParameters);
    definitionMap.set('propDecorators', metadata.propDecorators);
    return checker.importExpr(checker.Identifiers.declareClassMetadata).callFn([definitionMap.toLiteralMap()]);
}
function compileComponentDeclareClassMetadata(metadata, dependencies) {
    if (dependencies === null || dependencies.length === 0) {
        return compileDeclareClassMetadata(metadata);
    }
    const definitionMap = new checker.DefinitionMap();
    const callbackReturnDefinitionMap = new checker.DefinitionMap();
    callbackReturnDefinitionMap.set('decorators', metadata.decorators);
    callbackReturnDefinitionMap.set('ctorParameters', metadata.ctorParameters ?? checker.literal(null));
    callbackReturnDefinitionMap.set('propDecorators', metadata.propDecorators ?? checker.literal(null));
    definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_DEFER_SUPPORT_VERSION));
    definitionMap.set('version', checker.literal('19.2.7'));
    definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
    definitionMap.set('type', metadata.type);
    definitionMap.set('resolveDeferredDeps', compileComponentMetadataAsyncResolver(dependencies));
    definitionMap.set('resolveMetadata', checker.arrowFn(dependencies.map((dep) => new checker.FnParam(dep.symbolName, checker.DYNAMIC_TYPE)), callbackReturnDefinitionMap.toLiteralMap()));
    return checker.importExpr(checker.Identifiers.declareClassMetadataAsync).callFn([definitionMap.toLiteralMap()]);
}
/**
 * Creates an array literal expression from the given array, mapping all values to an expression
 * using the provided mapping function. If the array is empty or null, then null is returned.
 *
 * @param values The array to transfer into literal array expression.
 * @param mapper The logic to use for creating an expression for the array's values.
 * @returns An array literal expression representing `values`, or null if `values` is empty or
 * is itself null.
 */
function toOptionalLiteralArray(values, mapper) {
    if (values === null || values.length === 0) {
        return null;
    }
    return checker.literalArr(values.map((value) => mapper(value)));
}
/**
 * Creates an object literal expression from the given object, mapping all values to an expression
 * using the provided mapping function. If the object has no keys, then null is returned.
 *
 * @param object The object to transfer into an object literal expression.
 * @param mapper The logic to use for creating an expression for the object's values.
 * @returns An object literal expression representing `object`, or null if `object` does not have
 * any keys.
 */
function toOptionalLiteralMap(object, mapper) {
    const entries = Object.keys(object).map((key) => {
        const value = object[key];
        return { key, value: mapper(value), quoted: true };
    });
    if (entries.length > 0) {
        return checker.literalMap(entries);
    }
    else {
        return null;
    }
}
function compileDependencies(deps) {
    if (deps === 'invalid') {
        // The `deps` can be set to the string "invalid"  by the `unwrapConstructorDependencies()`
        // function, which tries to convert `ConstructorDeps` into `R3DependencyMetadata[]`.
        return checker.literal('invalid');
    }
    else if (deps === null) {
        return checker.literal(null);
    }
    else {
        return checker.literalArr(deps.map(compileDependency));
    }
}
function compileDependency(dep) {
    const depMeta = new checker.DefinitionMap();
    depMeta.set('token', dep.token);
    if (dep.attributeNameType !== null) {
        depMeta.set('attribute', checker.literal(true));
    }
    if (dep.host) {
        depMeta.set('host', checker.literal(true));
    }
    if (dep.optional) {
        depMeta.set('optional', checker.literal(true));
    }
    if (dep.self) {
        depMeta.set('self', checker.literal(true));
    }
    if (dep.skipSelf) {
        depMeta.set('skipSelf', checker.literal(true));
    }
    return depMeta.toLiteralMap();
}
/**
 * Compile a directive declaration defined by the `R3DirectiveMetadata`.
 */
function compileDeclareDirectiveFromMetadata(meta) {
    const definitionMap = createDirectiveDefinitionMap(meta);
    const expression = checker.importExpr(checker.Identifiers.declareDirective).callFn([definitionMap.toLiteralMap()]);
    const type = checker.createDirectiveType(meta);
    return { expression, type, statements: [] };
}
/**
 * Gathers the declaration fields for a directive into a `DefinitionMap`. This allows for reusing
 * this logic for components, as they extend the directive metadata.
 */
function createDirectiveDefinitionMap(meta) {
    const definitionMap = new checker.DefinitionMap();
    const minVersion = getMinimumVersionForPartialOutput(meta);
    definitionMap.set('minVersion', checker.literal(minVersion));
    definitionMap.set('version', checker.literal('19.2.7'));
    // e.g. `type: MyDirective`
    definitionMap.set('type', meta.type.value);
    if (meta.isStandalone !== undefined) {
        definitionMap.set('isStandalone', checker.literal(meta.isStandalone));
    }
    if (meta.isSignal) {
        definitionMap.set('isSignal', checker.literal(meta.isSignal));
    }
    // e.g. `selector: 'some-dir'`
    if (meta.selector !== null) {
        definitionMap.set('selector', checker.literal(meta.selector));
    }
    definitionMap.set('inputs', needsNewInputPartialOutput(meta)
        ? createInputsPartialMetadata(meta.inputs)
        : legacyInputsPartialMetadata(meta.inputs));
    definitionMap.set('outputs', checker.conditionallyCreateDirectiveBindingLiteral(meta.outputs));
    definitionMap.set('host', compileHostMetadata(meta.host));
    definitionMap.set('providers', meta.providers);
    if (meta.queries.length > 0) {
        definitionMap.set('queries', checker.literalArr(meta.queries.map(compileQuery)));
    }
    if (meta.viewQueries.length > 0) {
        definitionMap.set('viewQueries', checker.literalArr(meta.viewQueries.map(compileQuery)));
    }
    if (meta.exportAs !== null) {
        definitionMap.set('exportAs', checker.asLiteral(meta.exportAs));
    }
    if (meta.usesInheritance) {
        definitionMap.set('usesInheritance', checker.literal(true));
    }
    if (meta.lifecycle.usesOnChanges) {
        definitionMap.set('usesOnChanges', checker.literal(true));
    }
    if (meta.hostDirectives?.length) {
        definitionMap.set('hostDirectives', createHostDirectives(meta.hostDirectives));
    }
    definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
    return definitionMap;
}
/**
 * Determines the minimum linker version for the partial output
 * generated for this directive.
 *
 * Every time we make a breaking change to the declaration interface or partial-linker
 * behavior, we must update the minimum versions to prevent old partial-linkers from
 * incorrectly processing the declaration.
 *
 * NOTE: Do not include any prerelease in these versions as they are ignored.
 */
function getMinimumVersionForPartialOutput(meta) {
    // We are starting with the oldest minimum version that can work for common
    // directive partial compilation output. As we discover usages of new features
    // that require a newer partial output emit, we bump the `minVersion`. Our goal
    // is to keep libraries as much compatible with older linker versions as possible.
    let minVersion = '14.0.0';
    // Note: in order to allow consuming Angular libraries that have been compiled with 16.1+ in
    // Angular 16.0, we only force a minimum version of 16.1 if input transform feature as