Object.defineProperty(exports, "__esModule", { value: true });
exports.getPatchForSlotNode = exports.walkElementNodes = exports.generate = exports.isIntrinsicElement = exports.transformContext = void 0;
const SourceMaps = require("@volar/source-map");
const code_gen_1 = require("@volar/code-gen");
const shared_1 = require("@vue/shared");
const CompilerCore = require("@vue/compiler-core");
const transform_1 = require("../utils/transform");
const scriptSetupRanges_1 = require("../parsers/scriptSetupRanges");
const string_1 = require("../utils/string");
const capabilitiesSet = {
    all: { basic: true, diagnostic: true, references: true, definitions: true, rename: true, completion: true, semanticTokens: true },
    noDiagnostic: { basic: true, references: true, definitions: true, rename: true, completion: true, semanticTokens: true },
    diagnosticOnly: { diagnostic: true },
    tagHover: { basic: true },
    event: { basic: true, diagnostic: true },
    tagReference: { references: true, definitions: true, rename: { in: false, out: true } },
    attr: { basic: true, diagnostic: true, references: true, definitions: true, rename: true },
    attrReference: { references: true, definitions: true, rename: true },
    scopedClassName: { references: true, definitions: true, rename: true, completion: true },
    slotName: { basic: true, diagnostic: true, references: true, definitions: true, completion: true },
    slotNameExport: { basic: true, diagnostic: true, references: true, definitions: true, /* referencesCodeLens: true */ },
    refAttr: { references: true, definitions: true, rename: true },
};
const formatBrackets = {
    empty: ['', ''],
    round: ['(', ')'],
    // fix https://github.com/johnsoncodehk/volar/issues/1210
    curly: ['({ __VLS_foo:', '})'],
    square: ['[', ']'],
};
const validTsVar = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;
// @ts-ignore
exports.transformContext = {
    onError: () => { },
    helperString: str => str.toString(),
    replaceNode: node => { },
    cacheHandlers: false,
    prefixIdentifiers: false,
    scopes: {
        vFor: 0,
        vOnce: 0,
        vPre: 0,
        vSlot: 0,
    },
    expressionPlugins: ['typescript'],
};
function _isHTMLTag(tag) {
    return (0, shared_1.isHTMLTag)(tag)
        // fix https://github.com/johnsoncodehk/volar/issues/1340
        || tag === 'hgroup'
        || tag === 'slot'
        || tag === 'component';
}
function isIntrinsicElement(runtimeMode, tag) {
    return runtimeMode === 'runtime-dom' ? (_isHTMLTag(tag) || (0, shared_1.isSVGTag)(tag)) : ['block', 'component', 'template', 'slot'].includes(tag);
}
exports.isIntrinsicElement = isIntrinsicElement;
function generate(ts, vueCompilerOptions, sourceTemplate, sourceLang, templateAst, hasScriptSetup, cssScopedClasses = []) {
    const tsCodeGen = new code_gen_1.CodeGen();
    const tsFormatCodeGen = new code_gen_1.CodeGen();
    const cssCodeGen = new code_gen_1.CodeGen();
    const slots = new Map();
    const slotExps = new Map();
    const cssScopedClassesSet = new Set(cssScopedClasses);
    const tagOffsetsMap = {};
    const tagResolves = {};
    const localVars = {};
    const tempVars = [];
    const identifiers = new Set();
    const scopedClasses = [];
    const blockConditions = [];
    let slotsNum = 0;
    tsFormatCodeGen.addText('export { };\n');
    let elementIndex = 0;
    walkElementNodes(templateAst, node => {
        if (!tagOffsetsMap[node.tag]) {
            tagOffsetsMap[node.tag] = [];
        }
        const offsets = tagOffsetsMap[node.tag];
        offsets.push(node.loc.start.offset + node.loc.source.indexOf(node.tag)); // start tag
        if (!node.isSelfClosing && sourceLang === 'html') {
            offsets.push(node.loc.start.offset + node.loc.source.lastIndexOf(node.tag)); // end tag
        }
    });
    for (const tagName in tagOffsetsMap) {
        if (isIntrinsicElement(vueCompilerOptions.experimentalRuntimeMode, tagName))
            continue;
        const tagOffsets = tagOffsetsMap[tagName];
        const tagRanges = tagOffsets.map(offset => ({ start: offset, end: offset + tagName.length }));
        const isNamespacedTag = tagName.indexOf('.') >= 0;
        const var_componentVar = isNamespacedTag ? `__VLS_ctx.${tagName}` : (0, shared_1.capitalize)((0, shared_1.camelize)(tagName.replace(/:/g, '-')));
        const var_emit = `__VLS_${elementIndex++}`;
        if (isNamespacedTag) {
            identifiers.add(tagName.split('.')[0]);
            for (let i = 0; i < tagRanges.length; i++) {
                const tagRange = tagRanges[i];
                tsCodeGen.addText(`declare const __VLS_${elementIndex++}: typeof __VLS_ctx.`);
                writeCode(tagName, tagRange, SourceMaps.Mode.Offset, {
                    vueTag: 'template',
                    capabilities: capabilitiesSet.all,
                });
                tsCodeGen.addText(`;\n`);
            }
        }
        else {
            const names = new Set([
                tagName,
                (0, shared_1.camelize)(tagName),
                (0, shared_1.capitalize)((0, shared_1.camelize)(tagName)),
            ]);
            tsCodeGen.addText(`declare const ${var_componentVar}: `);
            if (!vueCompilerOptions.strictTemplates)
                tsCodeGen.addText(`__VLS_types.ConvertInvalidJsxElement<`);
            for (const name of names) {
                tsCodeGen.addText(`\n'${name}' extends keyof typeof __VLS_components ? typeof __VLS_components['${name}'] : `);
            }
            for (const name of names) {
                tsCodeGen.addText(`\n'${name}' extends keyof typeof __VLS_ctx ? typeof __VLS_ctx['${name}'] : `);
            }
            tsCodeGen.addText(`unknown`);
            if (!vueCompilerOptions.strictTemplates)
                tsCodeGen.addText(`>`);
            tsCodeGen.addText(`;\n`);
            for (const vlsVar of ['__VLS_components', '__VLS_ctx']) {
                for (const tagRange of tagRanges) {
                    for (const name of names) {
                        tsCodeGen.addText(vlsVar);
                        writePropertyAccess2(name, [tagRange], {
                            vueTag: 'template',
                            capabilities: capabilitiesSet.tagReference,
                            normalizeNewName: tagName === name ? undefined : unHyphenatComponentName,
                            applyNewName: keepHyphenateName,
                        });
                        tsCodeGen.addText(';');
                    }
                    tsCodeGen.addText('\n');
                }
            }
        }
        tsCodeGen.addText(`declare const ${var_emit}: __VLS_types.ExtractEmit2<typeof ${var_componentVar}>;\n`);
        const name1 = tagName; // hello-world
        const name2 = isIntrinsicElement(vueCompilerOptions.experimentalRuntimeMode, tagName) ? tagName : (0, shared_1.camelize)(tagName); // helloWorld
        const name3 = isIntrinsicElement(vueCompilerOptions.experimentalRuntimeMode, tagName) ? tagName : (0, shared_1.capitalize)(name2); // HelloWorld
        const componentNames = new Set([name1, name2, name3]);
        /* Completion */
        tsCodeGen.addText('/* Completion: Emits */\n');
        for (const name of componentNames) {
            tsCodeGen.addText('// @ts-ignore\n');
            tsCodeGen.addText(`${var_emit}('${string_1.SearchTexts.EmitCompletion(name)}');\n`);
        }
        tsCodeGen.addText('/* Completion: Props */\n');
        for (const name of componentNames) {
            tsCodeGen.addText('// @ts-ignore\n');
            tsCodeGen.addText(`(<${isIntrinsicElement(vueCompilerOptions.experimentalRuntimeMode, tagName) ? tagName : var_componentVar} ${string_1.SearchTexts.PropsCompletion(name)}/>);\n`);
        }
        tagResolves[tagName] = {
            component: var_componentVar,
            isNamespacedTag,
            emit: var_emit,
            offsets: tagOffsets,
        };
    }
    for (const childNode of templateAst.children) {
        visitNode(childNode, undefined);
    }
    tsCodeGen.addText(`if (typeof __VLS_styleScopedClasses === 'object' && !Array.isArray(__VLS_styleScopedClasses)) {\n`);
    for (const { className, offset } of scopedClasses) {
        tsCodeGen.addText(`__VLS_styleScopedClasses[`);
        writeCodeWithQuotes(className, {
            start: offset,
            end: offset + className.length,
        }, {
            vueTag: 'template',
            capabilities: Object.assign(Object.assign({}, capabilitiesSet.scopedClassName), { displayWithLink: cssScopedClassesSet.has(className) }),
        });
        tsCodeGen.addText(`];\n`);
    }
    tsCodeGen.addText('}\n');
    tsCodeGen.addText(`declare var __VLS_slots:\n`);
    for (const [exp, slot] of slotExps) {
        tsCodeGen.addText(`Record<NonNullable<typeof ${exp}>, typeof ${slot.varName}> &\n`);
    }
    tsCodeGen.addText(`{\n`);
    for (const [name, slot] of slots) {
        slotsNum++;
        writeObjectProperty(name, slot.loc, SourceMaps.Mode.Expand, {
            vueTag: 'template',
            capabilities: Object.assign(Object.assign({}, capabilitiesSet.slotNameExport), { referencesCodeLens: hasScriptSetup }),
        }, slot.nodeLoc);
        tsCodeGen.addText(`: (_: typeof ${slot.varName}) => any,\n`);
    }
    tsCodeGen.addText(`};\n`);
    writeInterpolationVarsExtraCompletion();
    return {
        codeGen: tsCodeGen,
        formatCodeGen: tsFormatCodeGen,
        cssCodeGen: cssCodeGen,
        tagNames: tagOffsetsMap,
        identifiers,
        slotsNum,
    };
    function createTsAst(cacheTo, text) {
        if (cacheTo.__volar_ast_text !== text) {
            cacheTo.__volar_ast_text = text;
            cacheTo.__volar_ast = ts.createSourceFile('/a.ts', text, ts.ScriptTarget.ESNext);
        }
        return cacheTo.__volar_ast;
    }
    function visitNode(node, parentEl) {
        var _a, _b, _c, _d;
        if (node.type === 1 /* CompilerDOM.NodeTypes.ELEMENT */) {
            visitElementNode(node, parentEl);
        }
        else if (node.type === 12 /* CompilerDOM.NodeTypes.TEXT_CALL */) {
            // {{ var }}
            visitNode(node.content, parentEl);
        }
        else if (node.type === 8 /* CompilerDOM.NodeTypes.COMPOUND_EXPRESSION */) {
            // {{ ... }} {{ ... }}
            for (const childNode of node.children) {
                if (typeof childNode === 'object') {
                    visitNode(childNode, parentEl);
                }
            }
        }
        else if (node.type === 5 /* CompilerDOM.NodeTypes.INTERPOLATION */) {
            // {{ ... }}
            let content = node.content.loc.source;
            let start = node.content.loc.start.offset;
            let leftCharacter;
            let rightCharacter;
            // fix https://github.com/johnsoncodehk/volar/issues/1787
            while ((leftCharacter = sourceTemplate.substring(start - 1, start)).trim() === '' && leftCharacter.length) {
                start--;
                content = leftCharacter + content;
            }
            while ((rightCharacter = sourceTemplate.substring(start + content.length, start + content.length + 1)).trim() === '' && rightCharacter.length) {
                content = content + rightCharacter;
            }
            writeInterpolation(content, start, {
                vueTag: 'template',
                capabilities: capabilitiesSet.all,
            }, '(', ');\n', node.content.loc);
            writeInterpolationVarsExtraCompletion();
            writeFormatCode(content, start, formatBrackets.curly);
        }
        else if (node.type === 9 /* CompilerDOM.NodeTypes.IF */) {
            // v-if / v-else-if / v-else
            let originalBlockConditionsLength = blockConditions.length;
            for (let i = 0; i < node.branches.length; i++) {
                const branch = node.branches[i];
                if (i === 0)
                    tsCodeGen.addText('if');
                else if (branch.condition)
                    tsCodeGen.addText('else if');
                else
                    tsCodeGen.addText('else');
                let addedBlockCondition = false;
                if (((_a = branch.condition) === null || _a === void 0 ? void 0 : _a.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */) {
                    tsCodeGen.addText(` `);
                    writeInterpolation(branch.condition.content, branch.condition.loc.start.offset, {
                        vueTag: 'template',
                        capabilities: capabilitiesSet.all,
                    }, '(', ')', branch.condition.loc);
                    writeFormatCode(branch.condition.content, branch.condition.loc.start.offset, formatBrackets.round);
                    if (vueCompilerOptions.experimentalAllowTypeNarrowingInInlineHandlers) {
                        blockConditions.push(branch.condition.content);
                        addedBlockCondition = true;
                    }
                }
                tsCodeGen.addText(` {\n`);
                writeInterpolationVarsExtraCompletion();
                for (const childNode of branch.children) {
                    visitNode(childNode, parentEl);
                }
                tsCodeGen.addText('}\n');
                if (addedBlockCondition) {
                    blockConditions[blockConditions.length - 1] = `!(${blockConditions[blockConditions.length - 1]})`;
                }
            }
            blockConditions.length = originalBlockConditionsLength;
        }
        else if (node.type === 11 /* CompilerDOM.NodeTypes.FOR */) {
            // v-for
            const { source, value, key, index } = node.parseResult;
            const leftExpressionRange = value ? { start: ((_b = value !== null && value !== void 0 ? value : key) !== null && _b !== void 0 ? _b : index).loc.start.offset, end: ((_c = index !== null && index !== void 0 ? index : key) !== null && _c !== void 0 ? _c : value).loc.end.offset } : undefined;
            const leftExpressionText = leftExpressionRange ? node.loc.source.substring(leftExpressionRange.start - node.loc.start.offset, leftExpressionRange.end - node.loc.start.offset) : undefined;
            const forBlockVars = [];
            tsCodeGen.addText(`for (const [`);
            if (leftExpressionRange && leftExpressionText) {
                const collentAst = createTsAst(node.parseResult, `const [${leftExpressionText}]`);
                (0, transform_1.colletVars)(ts, collentAst, forBlockVars);
                for (const varName of forBlockVars)
                    localVars[varName] = ((_d = localVars[varName]) !== null && _d !== void 0 ? _d : 0) + 1;
                writeCode(leftExpressionText, leftExpressionRange, SourceMaps.Mode.Offset, {
                    vueTag: 'template',
                    capabilities: capabilitiesSet.all,
                });
                writeFormatCode(leftExpressionText, leftExpressionRange.start, formatBrackets.square);
            }
            tsCodeGen.addText(`] of __VLS_types.getVforSourceType`);
            if (source.type === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */) {
                writeInterpolation(source.content, source.loc.start.offset, {
                    vueTag: 'template',
                    capabilities: capabilitiesSet.all,
                }, '(', ')', source.loc);
                writeFormatCode(source.content, source.loc.start.offset, formatBrackets.empty);
                tsCodeGen.addText(`) {\n`);
                writeInterpolationVarsExtraCompletion();
                for (const childNode of node.children) {
                    visitNode(childNode, parentEl);
                }
                tsCodeGen.addText('}\n');
            }
            for (const varName of forBlockVars)
                localVars[varName]--;
        }
        else if (node.type === 2 /* CompilerDOM.NodeTypes.TEXT */) {
            // not needed progress
        }
        else if (node.type === 3 /* CompilerDOM.NodeTypes.COMMENT */) {
            // not needed progress
        }
        else {
            tsCodeGen.addText(`// Unprocessed node type: ${node.type} json: ${JSON.stringify(node.loc)}\n`);
        }
    }
    ;
    function visitElementNode(node, parentEl) {
        var _a, _b, _c, _d, _e;
        const patchForNode = getPatchForSlotNode(node);
        if (patchForNode) {
            visitNode(patchForNode, parentEl);
            return;
        }
        if (node.tag !== 'template') {
            parentEl = node;
        }
        if (node.tag === 'vls-sr') {
            const startTagEnd = node.loc.source.indexOf('>') + 1;
            const endTagStart = node.loc.source.lastIndexOf('</');
            const scriptCode = node.loc.source.substring(startTagEnd, endTagStart);
            const collentAst = createTsAst(node, scriptCode);
            const bindings = (0, scriptSetupRanges_1.parseBindingRanges)(ts, collentAst, false);
            const scriptVars = bindings.map(binding => scriptCode.substring(binding.start, binding.end));
            for (const varName of scriptVars)
                localVars[varName] = ((_a = localVars[varName]) !== null && _a !== void 0 ? _a : 0) + 1;
            writeCode(scriptCode, {
                start: node.loc.start.offset + startTagEnd,
                end: node.loc.start.offset + startTagEnd + scriptCode.length,
            }, SourceMaps.Mode.Offset, {
                vueTag: 'template',
                capabilities: capabilitiesSet.all,
            });
            return;
        }
        tsCodeGen.addText(`{\n`);
        {
            const _isIntrinsicElement = isIntrinsicElement(vueCompilerOptions.experimentalRuntimeMode, node.tag);
            const tagText = (_c = (_b = tagResolves[node.tag]) === null || _b === void 0 ? void 0 : _b.component) !== null && _c !== void 0 ? _c : node.tag;
            const fullTagStart = tsCodeGen.getText().length;
            const tagCapabilities = Object.assign(Object.assign(Object.assign({}, capabilitiesSet.diagnosticOnly), (((_d = tagResolves[node.tag]) === null || _d === void 0 ? void 0 : _d.isNamespacedTag) ? {} : capabilitiesSet.tagHover)), (_isIntrinsicElement ? capabilitiesSet.tagReference : {}));
            const endTagOffset = !node.isSelfClosing && sourceLang === 'html' ? node.loc.start.offset + node.loc.source.lastIndexOf(node.tag) : undefined;
            tsCodeGen.addText(`<`);
            writeCode(tagText, {
                start: node.loc.start.offset + node.loc.source.indexOf(node.tag),
                end: node.loc.start.offset + node.loc.source.indexOf(node.tag) + node.tag.length,
            }, SourceMaps.Mode.Offset, {
                vueTag: 'template',
                capabilities: tagCapabilities,
            });
            tsCodeGen.addText(` `);
            const { hasRemainStyleOrClass, failedExps } = writeProps(node, false, 'props');
            if (endTagOffset === undefined) {
                tsCodeGen.addText(`/>`);
            }
            else {
                tsCodeGen.addText(`></`);
                writeCode(tagText, {
                    start: endTagOffset,
                    end: endTagOffset + node.tag.length,
                }, SourceMaps.Mode.Offset, {
                    vueTag: 'template',
                    capabilities: tagCapabilities,
                });
                tsCodeGen.addText(`>;\n`);
            }
            // fix https://github.com/johnsoncodehk/volar/issues/1775
            for (const failedExp of failedExps) {
                writeInterpolation(failedExp.loc.source, failedExp.loc.start.offset, {
                    vueTag: 'template',
                    capabilities: capabilitiesSet.all,
                }, '(', ')', failedExp.loc);
                const fb = formatBrackets.round;
                if (fb) {
                    writeFormatCode(failedExp.loc.source, failedExp.loc.start.offset, fb);
                }
                tsCodeGen.addText(';\n');
            }
            // fix https://github.com/johnsoncodehk/volar/issues/705#issuecomment-974773353
            let startTagEnd;
            if (node.loc.source.endsWith('/>')) {
                startTagEnd = node.loc.end.offset;
            }
            else if (node.children.length) {
                startTagEnd = node.loc.start.offset + node.loc.source.substring(0, node.children[0].loc.start.offset - node.loc.start.offset).lastIndexOf('>') + 1;
            }
            else {
                startTagEnd = node.loc.start.offset + node.loc.source.substring(0, node.loc.source.lastIndexOf('</')).lastIndexOf('>') + 1;
            }
            tsCodeGen.addMapping2({
                sourceRange: {
                    start: node.loc.start.offset,
                    end: startTagEnd,
                },
                mappedRange: {
                    start: fullTagStart,
                    end: tsCodeGen.getText().length,
                },
                mode: SourceMaps.Mode.Totally,
                data: {
                    vueTag: 'template',
                    capabilities: capabilitiesSet.diagnosticOnly,
                },
            });
            tsCodeGen.addText(`\n`);
            if (hasRemainStyleOrClass) {
                tsCodeGen.addText(`<${tagText} `);
                writeProps(node, true, 'props');
                tsCodeGen.addText(`/>\n`);
            }
            let slotBlockVars;
            writeInlineCss(node);
            slotBlockVars = [];
            writeImportSlots(node, parentEl, slotBlockVars);
            for (const varName of slotBlockVars)
                localVars[varName] = ((_e = localVars[varName]) !== null && _e !== void 0 ? _e : 0) + 1;
            const vScope = node.props.find(prop => prop.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */ && (prop.name === 'scope' || prop.name === 'data'));
            let inScope = false;
            let originalConditionsNum = blockConditions.length;
            if ((vScope === null || vScope === void 0 ? void 0 : vScope.type) === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */ && vScope.exp) {
                const scopeVar = `__VLS_${elementIndex++}`;
                const condition = `__VLS_types.withScope(__VLS_ctx, ${scopeVar})`;
                tsCodeGen.addText(`const ${scopeVar} = `);
                writeCode(vScope.exp.loc.source, {
                    start: vScope.exp.loc.start.offset,
                    end: vScope.exp.loc.end.offset,
                }, SourceMaps.Mode.Offset, {
                    vueTag: 'template',
                    capabilities: capabilitiesSet.all,
                });
                tsCodeGen.addText(';\n');
                tsCodeGen.addText(`if (${condition}) {\n`);
                blockConditions.push(condition);
                inScope = true;
            }
            writeDirectives(node);
            writeElReferences(node); // <el ref="foo" />
            if (cssScopedClasses.length)
                writeClassScopeds(node);
            writeEvents(node);
            writeSlots(node);
            for (const childNode of node.children) {
                visitNode(childNode, parentEl);
            }
            if (slotBlockVars) {
                for (const varName of slotBlockVars)
                    localVars[varName]--;
            }
            if (inScope) {
                tsCodeGen.addText('}\n');
                blockConditions.length = originalConditionsNum;
            }
        }
        tsCodeGen.addText(`}\n`);
        function writeEvents(node) {
            var _a, _b;
            const varComponentInstance = `__VLS_${elementIndex++}`;
            let writedInstance = false;
            for (const prop of node.props) {
                if (prop.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */
                    && prop.name === 'on'
                    && ((_a = prop.arg) === null || _a === void 0 ? void 0 : _a.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */) {
                    tryWriteInstance();
                    const tag = tagResolves[node.tag];
                    const varInstanceProps = `__VLS_${elementIndex++}`;
                    if (tag) {
                        tsCodeGen.addText(`type ${varInstanceProps} = typeof ${varComponentInstance} extends { $props: infer Props } ? Props & Record<string, unknown> : typeof ${tag.component} & Record<string, unknown>;\n`);
                    }
                    tsCodeGen.addText(`const __VLS_${elementIndex++}: {\n`);
                    tsCodeGen.addText(`'${prop.arg.loc.source}': __VLS_types.FillingEventArg<\n`);
                    {
                        const key_2 = (0, shared_1.camelize)('on-' + prop.arg.loc.source); // onClickOutside
                        const key_3 = 'on' + (0, shared_1.capitalize)(prop.arg.loc.source); // onClick-outside
                        if (tag) {
                            tsCodeGen.addText(`__VLS_types.FirstFunction<\n`);
                            {
                                tsCodeGen.addText(`__VLS_types.EmitEvent<typeof ${tag.component}, '${prop.arg.loc.source}'>,\n`);
                            }
                            {
                                tsCodeGen.addText(`${varInstanceProps}[`);
                                writeCodeWithQuotes(key_2, [{ start: prop.arg.loc.start.offset, end: prop.arg.loc.end.offset }], {
                                    vueTag: 'template',
                                    capabilities: capabilitiesSet.attrReference,
                                    normalizeNewName(newName) {
                                        return (0, shared_1.camelize)('on-' + newName);
                                    },
                                    applyNewName(oldName, newName) {
                                        const hName = (0, shared_1.hyphenate)(newName);
                                        if ((0, shared_1.hyphenate)(newName).startsWith('on-')) {
                                            return (0, shared_1.camelize)(hName.slice('on-'.length));
                                        }
                                        return newName;
                                    },
                                });
                                tsCodeGen.addText(`],\n`);
                            }
                            {
                                if (key_3 !== key_2) {
                                    tsCodeGen.addText(`${varInstanceProps}[`);
                                    writeCodeWithQuotes(key_3, [{ start: prop.arg.loc.start.offset, end: prop.arg.loc.end.offset }], {
                                        vueTag: 'template',
                                        capabilities: capabilitiesSet.attrReference,
                                        normalizeNewName(newName) {
                                            return 'on' + (0, shared_1.capitalize)(newName);
                                        },
                                        applyNewName(oldName, newName) {
                                            const hName = (0, shared_1.hyphenate)(newName);
                                            if ((0, shared_1.hyphenate)(newName).startsWith('on-')) {
                                                return (0, shared_1.camelize)(hName.slice('on-'.length));
                                            }
                                            return newName;
                                        },
                                    });
                                    tsCodeGen.addText(`],\n`);
                                }
                            }
                            {
                                tsCodeGen.addText(`typeof ${varComponentInstance} extends { $emit: infer Emit } ? __VLS_types.EmitEvent2<Emit, '${prop.arg.loc.source}'> : unknown,\n`);
                            }
                        }
                        {
                            tsCodeGen.addText(`__VLS_types.GlobalAttrs[`);
                            writeCodeWithQuotes(key_2, [{ start: prop.arg.loc.start.offset, end: prop.arg.loc.end.offset }], {
                                vueTag: 'template',
                                capabilities: capabilitiesSet.attrReference,
                                normalizeNewName(newName) {
                                    return (0, shared_1.camelize)('on-' + newName);
                                },
                                applyNewName(oldName, newName) {
                                    const hName = (0, shared_1.hyphenate)(newName);
                                    if ((0, shared_1.hyphenate)(newName).startsWith('on-')) {
                                        return (0, shared_1.camelize)(hName.slice('on-'.length));
                                    }
                                    return newName;
                                },
                            });
                            tsCodeGen.addText(`],\n`);
                        }
                        if (tag) {
                            tsCodeGen.addText(`>\n`);
                        }
                    }
                    tsCodeGen.addText(`>\n`);
                    tsCodeGen.addText(`} = {\n`);
                    {
                        writeObjectProperty(prop.arg.loc.source, {
                            start: prop.arg.loc.start.offset,
                            end: prop.arg.loc.end.offset,
                        }, SourceMaps.Mode.Offset, {
                            vueTag: 'template',
                            capabilities: capabilitiesSet.event,
                        }, prop.arg.loc);
                        tsCodeGen.addText(`: `);
                        appendExpressionNode(prop);
                    }
                    tsCodeGen.addText(`};\n`);
                    writeInterpolationVarsExtraCompletion();
                }
                else if (prop.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */
                    && prop.name === 'on'
                    && ((_b = prop.exp) === null || _b === void 0 ? void 0 : _b.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */) {
                    // for vue 2 nameless event
                    // https://github.com/johnsoncodehk/vue-tsc/issues/67
                    writeInterpolation(prop.exp.content, prop.exp.loc.start.offset, {
                        vueTag: 'template',
                        capabilities: capabilitiesSet.all,
                    }, '$event => {(', ')}', prop.exp.loc);
                    writeFormatCode(prop.exp.content, prop.exp.loc.start.offset, formatBrackets.round);
                    tsCodeGen.addText(`;\n`);
                }
                function appendExpressionNode(prop) {
                    var _a;
                    if (((_a = prop.exp) === null || _a === void 0 ? void 0 : _a.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */) {
                        const ast = createTsAst(prop.exp, prop.exp.content);
                        let isCompoundExpession = true;
                        if (ast.getChildCount() === 2) { // with EOF 
                            ast.forEachChild(child_1 => {
                                if (ts.isExpressionStatement(child_1)) {
                                    child_1.forEachChild(child_2 => {
                                        if (ts.isArrowFunction(child_2)) {
                                            isCompoundExpession = false;
                                        }
                                        else if (ts.isIdentifier(child_2)) {
                                            isCompoundExpession = false;
                                        }
                                    });
                                }
                                else if (ts.isFunctionDeclaration(child_1)) {
                                    isCompoundExpession = false;
                                }
                            });
                        }
                        let prefix = '(';
                        let suffix = ')';
                        if (isCompoundExpession) {
                            prefix = '$event => {\n';
                            if (blockConditions.length) {
                                for (const blockCondition of blockConditions) {
                                    prefix += `if (!(${blockCondition})) return;\n`;
                                }
                            }
                            suffix = '\n}';
                        }
                        writeInterpolation(prop.exp.content, prop.exp.loc.start.offset, {
                            vueTag: 'template',
                            capabilities: capabilitiesSet.all,
                        }, prefix, suffix, prop.exp.loc);
                        writeFormatCode(prop.exp.content, prop.exp.loc.start.offset, formatBrackets.round);
                    }
                    else {
                        tsCodeGen.addText(`undefined`);
                    }
                }
            }
            writeInterpolationVarsExtraCompletion();
            function tryWriteInstance() {
                if (writedInstance)
                    return;
                const tag = tagResolves[node.tag];
                if (tag) {
                    tsCodeGen.addText(`const ${varComponentInstance} = new ${tag.component}({ `);
                    writeProps(node, false, 'slots');
                    tsCodeGen.addText(`});\n`);
                }
                writedInstance = true;
            }
        }
    }
    function writeProps(node, forRemainStyleOrClass, mode) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
        let styleCount = 0;
        let classCount = 0;
        const failedExps = [];
        for (const prop of node.props) {
            if (prop.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */
                && (prop.name === 'bind' || prop.name === 'model')
                && (prop.name === 'model' || ((_a = prop.arg) === null || _a === void 0 ? void 0 : _a.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */)
                && (!prop.exp || prop.exp.type === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */)) {
                const isStatic = !prop.arg || (prop.arg.type === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */ && prop.arg.isStatic);
                let propName_1 = ((_b = prop.arg) === null || _b === void 0 ? void 0 : _b.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */
                    ? prop.arg.constType === 3 /* CompilerDOM.ConstantTypes.CAN_STRINGIFY */
                        ? prop.arg.content
                        : prop.arg.loc.source
                    : getModelValuePropName(node, vueCompilerOptions.target);
                if (propName_1 === undefined) {
                    if (prop.exp) {
                        failedExps.push(prop.exp);
                    }
                    continue;
                }
                if (prop.modifiers.some(m => m === 'prop' || m === 'attr')) {
                    propName_1 = propName_1.substring(1);
                }
                const propName_2 = !isStatic ? propName_1 : (0, shared_1.hyphenate)(propName_1) === propName_1 ? (0, shared_1.camelize)(propName_1) : propName_1;
                if (vueCompilerOptions.strictTemplates) {
                    propName_1 = propName_2;
                }
                if (forRemainStyleOrClass && propName_2 !== 'style' && propName_2 !== 'class')
                    continue;
                if (propName_2 === 'style' || propName_2 === 'class') {
                    const index = propName_2 === 'style' ? styleCount++ : classCount++;
                    if (index >= 1 !== forRemainStyleOrClass)
                        continue;
                }
                // camelize name
                writePropStart(isStatic);
                const diagStart = tsCodeGen.getText().length;
                if (!prop.arg) {
                    writePropName(propName_1, isStatic, {
                        start: prop.loc.start.offset,
                        end: prop.loc.start.offset + 'v-model'.length,
                    }, {
                        vueTag: 'template',
                        capabilities: getCaps(capabilitiesSet.attr),
                    }, (_c = prop.loc.name_1) !== null && _c !== void 0 ? _c : (prop.loc.name_1 = {}));
                }
                else if (((_d = prop.exp) === null || _d === void 0 ? void 0 : _d.constType) === 3 /* CompilerDOM.ConstantTypes.CAN_STRINGIFY */) {
                    writePropName(propName_2, isStatic, {
                        start: prop.arg.loc.start.offset,
                        end: prop.arg.loc.start.offset + propName_1.length, // patch style attr
                    }, {
                        vueTag: 'template',
                        capabilities: getCaps(capabilitiesSet.attr),
                        normalizeNewName: shared_1.camelize,
                        applyNewName: keepHyphenateName,
                    }, (_e = prop.loc.name_2) !== null && _e !== void 0 ? _e : (prop.loc.name_2 = {}));
                }
                else {
                    writePropName(propName_2, isStatic, {
                        start: prop.arg.loc.start.offset,
                        end: prop.arg.loc.end.offset,
                    }, {
                        vueTag: 'template',
                        capabilities: getCaps(capabilitiesSet.attr),
                        normalizeNewName: shared_1.camelize,
                        applyNewName: keepHyphenateName,
                    }, (_f = prop.loc.name_2) !== null && _f !== void 0 ? _f : (prop.loc.name_2 = {}));
                }
                writePropValuePrefix(isStatic);
                if (prop.exp && !(prop.exp.constType === 3 /* CompilerDOM.ConstantTypes.CAN_STRINGIFY */)) { // style='z-index: 2' will compile to {'z-index':'2'}
                    writeInterpolation(prop.exp.loc.source, prop.exp.loc.start.offset, {
                        vueTag: 'template',
                        capabilities: getCaps(capabilitiesSet.all),
                    }, '(', ')', prop.exp.loc);
                    const fb = getFormatBrackets(formatBrackets.round);
                    if (fb) {
                        writeFormatCode(prop.exp.loc.source, prop.exp.loc.start.offset, fb);
                    }
                }
                else {
                    tsCodeGen.addText('{}');
                }
                writePropValueSuffix(isStatic);
                tsCodeGen.addMapping2({
                    sourceRange: {
                        start: prop.loc.start.offset,
                        end: prop.loc.end.offset,
                    },
                    mappedRange: {
                        start: diagStart,
                        end: tsCodeGen.getText().length,
                    },
                    mode: SourceMaps.Mode.Totally,
                    data: {
                        vueTag: 'template',
                        capabilities: getCaps(capabilitiesSet.diagnosticOnly),
                    },
                });
                writePropEnd(isStatic);
                // original name
                if (prop.arg && propName_1 !== propName_2) {
                    writePropStart(isStatic);
                    writePropName(propName_1, isStatic, {
                        start: prop.arg.loc.start.offset,
                        end: prop.arg.loc.end.offset,
                    }, {
                        vueTag: 'template',
                        capabilities: getCaps(capabilitiesSet.attr),
                        normalizeNewName: shared_1.camelize,
                        applyNewName: keepHyphenateName,
                    }, (_g = prop.loc.name_1) !== null && _g !== void 0 ? _g : (prop.loc.name_1 = {}));
                    writePropValuePrefix(isStatic);
                    if (prop.exp) {
                        writeInterpolation(prop.exp.loc.source, undefined, undefined, '(', ')', prop.exp.loc);
                    }
                    else {
                        tsCodeGen.addText('undefined');
                    }
                    writePropValueSuffix(isStatic);
                    writePropEnd(isStatic);
                }
            }
            else if (prop.type === 6 /* CompilerDOM.NodeTypes.ATTRIBUTE */) {
                const propName = (0, shared_1.hyphenate)(prop.name) === prop.name ? (0, shared_1.camelize)(prop.name) : prop.name;
                let propName2 = prop.name;
                if (vueCompilerOptions.strictTemplates) {
                    propName2 = propName;
                }
                if (forRemainStyleOrClass && propName !== 'style' && propName !== 'class')
                    continue;
                if (propName === 'style' || propName === 'class') {
                    const index = propName === 'style' ? styleCount++ : classCount++;
                    if (index >= 1 !== forRemainStyleOrClass)
                        continue;
                }
                // camelize name
                writePropStart(true);
                const diagStart = tsCodeGen.getText().length;
                writePropName(propName, true, {
                    start: prop.loc.start.offset,
                    end: prop.loc.start.offset + propName2.length,
                }, {
                    vueTag: 'template',
                    capabilities: getCaps(capabilitiesSet.attr),
                    normalizeNewName: shared_1.camelize,
                    applyNewName: keepHyphenateName,
                }, (_h = prop.loc.name_1) !== null && _h !== void 0 ? _h : (prop.loc.name_1 = {}));
                writePropValuePrefix(true);
                if (prop.value) {
                    writeAttrValue(prop.value);
                }
                else {
                    tsCodeGen.addText('true');
                }
                writePropValueSuffix(true);
                writePropEnd(true);
                const diagEnd = tsCodeGen.getText().length;
                tsCodeGen.addMapping2({
                    sourceRange: {
                        start: prop.loc.start.offset,
                        end: prop.loc.end.offset,
                    },
                    mappedRange: {
                        start: diagStart,
                        end: diagEnd,
                    },
                    mode: SourceMaps.Mode.Totally,
                    data: {
                        vueTag: 'template',
                        capabilities: getCaps(capabilitiesSet.diagnosticOnly),
                    },
                });
                // original name
                if (propName2 !== propName) {
                    writePropStart(true);
                    writePropName(propName2, true, {
                        start: prop.loc.start.offset,
                        end: prop.loc.start.offset + propName2.length,
                    }, {
                        vueTag: 'template',
                        capabilities: getCaps(capabilitiesSet.attr),
                        normalizeNewName: shared_1.camelize,
                        applyNewName: keepHyphenateName,
                    }, (_j = prop.loc.name_2) !== null && _j !== void 0 ? _j : (prop.loc.name_2 = {}));
                    writePropValuePrefix(true);
                    if (prop.value) {
                        writeAttrValue(prop.value);
                    }
                    else {
                        tsCodeGen.addText('true');
                    }
                    writePropValueSuffix(true);
                    writePropEnd(true);
                }
            }
            else if (prop.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */
                && prop.name === 'bind'
                && !prop.arg
                && ((_k = prop.exp) === null || _k === void 0 ? void 0 : _k.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */) {
                if (forRemainStyleOrClass) {
                    continue;
                }
                if (mode === 'props')
                    tsCodeGen.addText('{...');
                else
                    tsCodeGen.addText('...');
                writeInterpolation(prop.exp.content, prop.exp.loc.start.offset, {
                    vueTag: 'template',
                    capabilities: getCaps(capabilitiesSet.all),
                }, '(', ')', prop.exp.loc);
                const fb = getFormatBrackets(formatBrackets.round);
                if (fb) {
                    writeFormatCode(prop.exp.content, prop.exp.loc.start.offset, fb);
                }
                if (mode === 'props')
                    tsCodeGen.addText('} ');
                else
                    tsCodeGen.addText(', ');
            }
            else {
                if (forRemainStyleOrClass) {
                    continue;
                }
                // comment this line to avoid affecting comments in prop expressions
                // tsCodeGen.addText("/* " + [prop.type, prop.name, prop.arg?.loc.source, prop.exp?.loc.source, prop.loc.source].join(", ") + " */ ");
            }
        }
        return {
            hasRemainStyleOrClass: styleCount >= 2 || classCount >= 2,
            failedExps,
        };
        function writePropName(name, isStatic, sourceRange, data, cacheOn) {
            if (mode === 'props' && isStatic) {
                writeCode(name, sourceRange, SourceMaps.Mode.Offset, data);
            }
            else {
                writeObjectProperty(name, sourceRange, SourceMaps.Mode.Offset, data, cacheOn);
            }
        }
        function writePropValuePrefix(isStatic) {
            if (mode === 'props' && isStatic) {
                tsCodeGen.addText('={');
            }
            else {
                tsCodeGen.addText(': (');
            }
        }
        function writePropValueSuffix(isStatic) {
            if (mode === 'props' && isStatic) {
                tsCodeGen.addText('}');
            }
            else {
                tsCodeGen.addText(')');
            }
        }
        function writePropStart(isStatic) {
            if (mode === 'props' && !isStatic) {
                tsCodeGen.addText('{...{');
            }
        }
        function writePropEnd(isStatic) {
            if (mode === 'props' && isStatic) {
                tsCodeGen.addText(' ');
            }
            else if (mode === 'props' && !isStatic) {
                tsCodeGen.addText('}} ');
            }
            else {
                tsCodeGen.addText(', ');
            }
        }
        function getCaps(caps) {
            if (mode === 'props') {
                return caps;
            }
            else {
                return {
                    references: caps.references,
                    rename: caps.rename,
                };
            }
        }
        function getFormatBrackets(b) {
            if (mode === 'props') {
                return b;
            }
            else {
                return undefined;
            }
        }
        function writeAttrValue(attrNode) {
            tsCodeGen.addText('"');
            let start = attrNode.loc.start.offset;
            let end = attrNode.loc.end.offset;
            if (end - start > attrNode.content.length) {
                start++;
                end--;
            }
            writeCode(toUnicode(attrNode.content), { start, end }, SourceMaps.Mode.Offset, {
                vueTag: 'template',
                capabilities: getCaps(capabilitiesSet.all)
            });
            tsCodeGen.addText('"');
        }
    }
    function writeInlineCss(node) {
        var _a, _b;
        for (const prop of node.props) {
            if (prop.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */
                && prop.name === 'bind'
                && ((_a = prop.arg) === null || _a === void 0 ? void 0 : _a.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */
                && ((_b = prop.exp) === null || _b === void 0 ? void 0 : _b.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */
                && prop.arg.content === 'style'
                && prop.exp.constType === 3 /* CompilerDOM.ConstantTypes.CAN_STRINGIFY */) {
                const endCrt = prop.arg.loc.source[prop.arg.loc.source.length - 1]; // " | '
                const start = prop.arg.loc.source.indexOf(endCrt) + 1;
                const end = prop.arg.loc.source.lastIndexOf(endCrt);
                const content = prop.arg.loc.source.substring(start, end);
                cssCodeGen.addText(`${node.tag} { `);
                cssCodeGen.addCode2(content, prop.arg.loc.start.offset + start, {
                    vueTag: 'template',
                    capabilities: {
                        basic: true,
                        references: true,
                        definitions: true,
                        diagnostic: true,
                        rename: true,
                        completion: true,
                        semanticTokens: true,
                    },
                });
                cssCodeGen.addText(` }\n`);
            }
        }
    }
    function writeImportSlots(node, parentEl, slotBlockVars) {
        var _a, _b, _c;
        const tag = parentEl ? tagResolves[parentEl.tag] : undefined;
        for (const prop of node.props) {
            if (prop.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */
                && prop.name === 'slot') {
                const varComponentInstance = `__VLS_${elementIndex++}`;
                const varSlots = `__VLS_${elementIndex++}`;
                if (tag && parentEl) {
                    tsCodeGen.addText(`const ${varComponentInstance} = new ${tag.component}({ `);
                    writeProps(parentEl, false, 'slots');
                    tsCodeGen.addText(`});\n`);
                    writeInterpolationVarsExtraCompletion();
                    tsCodeGen.addText(`declare const ${varSlots}: __VLS_types.ExtractComponentSlots<typeof ${varComponentInstance}>;\n`);
                }
                if (((_a = prop.exp) === null || _a === void 0 ? void 0 : _a.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */) {
                    tsCodeGen.addText(`const `);
                    const collentAst = createTsAst(prop, `const ${prop.exp.content}`);
                    (0, transform_1.colletVars)(ts, collentAst, slotBlockVars);
                    writeCode(prop.exp.content, {
                        start: prop.exp.loc.start.offset,
                        end: prop.exp.loc.end.offset,
                    }, SourceMaps.Mode.Offset, {
                        vueTag: 'template',
                        capabilities: capabilitiesSet.all,
                    });
                    writeFormatCode(prop.exp.content, prop.exp.loc.start.offset, formatBrackets.round);
                    tsCodeGen.addText(` = `);
                }
                if (!tag || !parentEl) {
                    // fix https://github.com/johnsoncodehk/volar/issues/1425
                    tsCodeGen.addText(`{} as any;\n`);
                    continue;
                }
                let slotName = 'default';
                let isStatic = true;
                if (((_b = prop.arg) === null || _b === void 0 ? void 0 : _b.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */ && prop.arg.content !== '') {
                    isStatic = prop.arg.isStatic;
                    slotName = prop.arg.content;
                }
                const diagStart = tsCodeGen.getText().length;
                tsCodeGen.addText(varSlots);
                const argRange = prop.arg
                    ? {
                        start: prop.arg.loc.start.offset,
                        end: prop.arg.loc.end.offset,
                    } : {
                    start: prop.loc.start.offset,
                    end: prop.loc.start.offset + prop.loc.source.split('=')[0].length,
                };
                if (isStatic) {
                    writePropertyAccess(slotName, argRange, {
                        vueTag: 'template',
                        capabilities: Object.assign(Object.assign({}, capabilitiesSet.slotName), { completion: !!prop.arg }),
                    }, false);
                }
                else {
                    tsCodeGen.addText(`[`);
                    writeInterpolation(slotName, argRange.start + 1, {
                        vueTag: 'template',
                        capabilities: capabilitiesSet.all,
                    }, '', '', (_c = prop.loc.slot_name) !== null && _c !== void 0 ? _c : (prop.loc.slot_name = {}));
                    tsCodeGen.addText(`]`);
                    writeInterpolationVarsExtraCompletion();
                }
                const diagEnd = tsCodeGen.getText().length;
                tsCodeGen.addMapping2({
                    mappedRange: {
                        start: diagStart,
                        end: diagEnd,
                    },
                    sourceRange: argRange,
                    mode: SourceMaps.Mode.Totally,
                    data: {
                        vueTag: 'template',
                        capabilities: capabilitiesSet.diagnosticOnly,
                    },
                });
                tsCodeGen.addText(`;\n`);
                if (isStatic && !prop.arg) {
                    let offset = prop.loc.start.offset;
                    if (prop.loc.source.startsWith('#'))
                        offset += '#'.length;
                    else if (prop.loc.source.startsWith('v-slot:'))
                        offset += 'v-slot:'.length;
                    tsCodeGen.addText(varSlots);
                    tsCodeGen.addText(`['`);
                    writeCode('', { start: offset, end: offset }, SourceMaps.Mode.Offset, {
                        vueTag: 'template',
                        capabilities: {
                            completion: true,
                        },
                    });
                    tsCodeGen.addText(`'];\n`);
                }
            }
        }
    }
    function writeDirectives(node) {
        var _a;
        for (const prop of node.props) {
            if (prop.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */
                && prop.name !== 'slot'
                && prop.name !== 'on'
                && prop.name !== 'model'
                && prop.name !== 'bind'
                && (prop.name !== 'scope' && prop.name !== 'data')) {
                const diagStart = tsCodeGen.getText().length;
                tsCodeGen.addText(`__VLS_types.directiveFunction(__VLS_ctx.`);
                writeCode((0, shared_1.camelize)('v-' + prop.name), {
                    start: prop.loc.start.offset,
                    end: prop.loc.start.offset + 'v-'.length + prop.name.length,
                }, SourceMaps.Mode.Offset, {
                    vueTag: 'template',
                    capabilities: capabilitiesSet.noDiagnostic,
                    normalizeNewName: shared_1.camelize,
                    applyNewName: keepHyphenateName,
                });
                identifiers.add((0, shared_1.camelize)('v-' + prop.name));
                tsCodeGen.addText(`)(`);
                if (((_a = prop.exp) === null || _a === void 0 ? void 0 : _a.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */) {
                    writeInterpolation(prop.exp.content, prop.exp.loc.start.offset, {
                        vueTag: 'template',
                        capabilities: capabilitiesSet.all,
                    }, '(', ')', prop.exp.loc);
                    writeFormatCode(prop.exp.content, prop.exp.loc.start.offset, formatBrackets.round);
                }
                tsCodeGen.addText(`)`);
                tsCodeGen.addMapping2({
                    sourceRange: {
                        start: prop.loc.start.offset,
                        end: prop.loc.end.offset,
                    },
                    mappedRange: {
                        start: diagStart,
                        end: tsCodeGen.getText().length,
                    },
                    mode: SourceMaps.Mode.Totally,
                    data: {
                        vueTag: 'template',
                        capabilities: capabilitiesSet.diagnosticOnly,
                    },
                });
                tsCodeGen.addText(`;\n`);
                writeInterpolationVarsExtraCompletion();
            }
        }
    }
    function writeElReferences(node) {
        for (const prop of node.props) {
            if (prop.type === 6 /* CompilerDOM.NodeTypes.ATTRIBUTE */
                && prop.name === 'ref'
                && prop.value) {
                tsCodeGen.addText(`// @ts-ignore\n`);
                writeInterpolation(prop.value.content, prop.value.loc.start.offset + 1, {
                    vueTag: 'template',
                    capabilities: capabilitiesSet.refAttr,
                }, '(', ')', prop.value.loc);
                tsCodeGen.addText(`;\n`);
                writeInterpolationVarsExtraCompletion();
            }
        }
    }
    function writeClassScopeds(node) {
        var _a, _b;
        for (const prop of node.props) {
            if (prop.type === 6 /* CompilerDOM.NodeTypes.ATTRIBUTE */
                && prop.name === 'class'
                && prop.value) {
                let startOffset = prop.value.loc.start.offset;
                let tempClassName = '';
                for (const char of (prop.value.loc.source + ' ')) {
                    if (char.trim() === '' || char === '"' || char === "'") {
                        if (tempClassName !== '') {
                            scopedClasses.push({ className: tempClassName, offset: startOffset });
                            startOffset += tempClassName.length;
                            tempClassName = '';
                        }
                        startOffset += char.length;
                    }
                    else {
                        tempClassName += char;
                    }
                }
            }
            else if (prop.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */
                && ((_a = prop.arg) === null || _a === void 0 ? void 0 : _a.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */
                && ((_b = prop.exp) === null || _b === void 0 ? void 0 : _b.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */
                && prop.arg.content === 'class') {
                tsCodeGen.addText(`__VLS_styleScopedClasses = (`);
                writeCode(prop.exp.content, {
                    start: prop.exp.loc.start.offset,
                    end: prop.exp.loc.end.offset,
                }, SourceMaps.Mode.Offset, {
                    vueTag: 'template',
                    capabilities: capabilitiesSet.scopedClassName,
                });
                tsCodeGen.addText(`);\n`);
            }
        }
    }
    function writeSlots(node) {
        var _a, _b, _c;
        if (node.tag !== 'slot')
            return;
        const varDefaultBind = `__VLS_${elementIndex++}`;
        const varBinds = `__VLS_${elementIndex++}`;
        const varSlot = `__VLS_${elementIndex++}`;
        const slotName = getSlotName();
        const slotNameExp = getSlotNameExp();
        let hasDefaultBind = false;
        for (const prop of node.props) {
            if (prop.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */
                && !prop.arg
                && ((_a = prop.exp) === null || _a === void 0 ? void 0 : _a.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */) {
                hasDefaultBind = true;
                tsCodeGen.addText(`const ${varDefaultBind} = `);
                writeInterpolation(prop.exp.content, prop.exp.loc.start.offset, {
                    vueTag: 'template',
                    capabilities: capabilitiesSet.attrReference,
                }, '(', ')', prop.exp.loc);
                tsCodeGen.addText(`;\n`);
                writeInterpolationVarsExtraCompletion();
                break;
            }
        }
        tsCodeGen.addText(`const ${varBinds} = {\n`);
        for (const prop of node.props) {
            if (prop.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */
                && ((_b = prop.arg) === null || _b === void 0 ? void 0 : _b.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */
                && ((_c = prop.exp) === null || _c === void 0 ? void 0 : _c.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */
                && prop.arg.content !== 'name') {
                writeObjectProperty(prop.arg.content, {
                    start: prop.arg.loc.start.offset,
                    end: prop.arg.loc.end.offset,
                }, SourceMaps.Mode.Offset, {
                    vueTag: 'template',
                    normalizeNewName: shared_1.camelize,
                    applyNewName: keepHyphenateName,
                    capabilities: capabilitiesSet.attrReference,
                }, prop.arg.loc);
                tsCodeGen.addText(`: `);
                writeInterpolation(prop.exp.content, prop.exp.loc.start.offset, {
                    vueTag: 'template',
                    capabilities: capabilitiesSet.attrReference,
                }, '(', ')', prop.exp.loc);
                tsCodeGen.addText(`,\n`);
            }
            else if (prop.type === 6 /* CompilerDOM.NodeTypes.ATTRIBUTE */
                && prop.name !== 'name' // slot name
            ) {
                const propValue = prop.value !== undefined ? `"${toUnicode(prop.value.content)}"` : 'true';
                writeObjectProperty(prop.name, {
                    start: prop.loc.start.offset,
                    end: prop.loc.start.offset + prop.name.length
                }, SourceMaps.Mode.Offset, {
                    vueTag: 'template',
                    normalizeNewName: shared_1.camelize,
                    applyNewName: keepHyphenateName,
                    capabilities: capabilitiesSet.attr,
                }, prop.loc);
                tsCodeGen.addText(`: (`);
                tsCodeGen.addText(propValue);
                tsCodeGen.addText(`),\n`);
            }
        }
        tsCodeGen.addText(`};\n`);
        writeInterpolationVarsExtraCompletion();
        if (hasDefaultBind) {
            tsCodeGen.addText(`var ${varSlot}!: typeof ${varDefaultBind} & typeof ${varBinds};\n`);
        }
        else {
            tsCodeGen.addText(`var ${varSlot}!: typeof ${varBinds};\n`);
        }
        if (slotNameExp) {
            const varSlotExp = `__VLS_${elementIndex++}`;
            const varSlotExp2 = `__VLS_${elementIndex++}`;
            tsCodeGen.addText(`const ${varSlotExp} = ${slotNameExp};\n`);
            tsCodeGen.addText(`var ${varSlotExp2}!: typeof ${varSlotExp};\n`);
            slotExps.set(varSlotExp2, {
                varName: varSlot,
                loc: {
                    start: node.loc.start.offset + node.loc.source.indexOf(node.tag),
                    end: node.loc.start.offset + node.loc.source.indexOf(node.tag) + node.tag.length,
                },
            });
        }
        else {
            slots.set(slotName, {
                varName: varSlot,
                loc: {
                    start: node.loc.start.offset + node.loc.source.indexOf(node.tag),
                    end: node.loc.start.offset + node.loc.source.indexOf(node.tag) + node.tag.length,
                },
                nodeLoc: node.loc,
            });
        }
        function getSlotName() {
            for (const prop2 of node.props) {
                if (prop2.name === 'name' && prop2.type === 6 /* CompilerDOM.NodeTypes.ATTRIBUTE */ && prop2.value) {
                    if (prop2.value.content) {
                        return prop2.value.content;
                    }
                }
            }
            return 'default';
        }
        function getSlotNameExp() {
            var _a, _b;
            for (const prop2 of node.props) {
                if (prop2.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */ && prop2.name === 'bind' && ((_a = prop2.arg) === null || _a === void 0 ? void 0 : _a.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */ && prop2.arg.content === 'name') {
                    if (((_b = prop2.exp) === null || _b === void 0 ? void 0 : _b.type) === 4 /* CompilerDOM.NodeTypes.SIMPLE_EXPRESSION */) {
                        return prop2.exp.content;
                    }
                    else {
                        return `'default'`;
                    }
                }
            }
        }
    }
    function writeObjectProperty(mapCode, sourceRange, mapMode, data, cacheOn) {
        if (validTsVar.test(mapCode)) {
            writeCode(mapCode, sourceRange, mapMode, data);
            return 1;
        }
        else if (mapCode.startsWith('[') && mapCode.endsWith(']')) {
            writeInterpolation(mapCode, sourceRange.start, data, '', '', cacheOn);
            return 1;
        }
        else {
            writeCodeWithQuotes(mapCode, sourceRange, data);
            return 2;
        }
    }
    function writePropertyAccess2(mapCode, sourceRanges, data) {
        const sourceRange = sourceRanges[0];
        const mode = writePropertyAccess(mapCode, sourceRange, data);
        for (let i = 1; i < sourceRanges.length; i++) {
            const sourceRange = sourceRanges[i];
            if (mode === 1 || mode === 2) {
                tsCodeGen.addMapping2({
                    sourceRange,
                    mappedRange: {
                        start: tsCodeGen.getText().length - mapCode.length,
                        end: tsCodeGen.getText().length,
                    },
                    mode: sourceRange.end - sourceRange.start === mapCode.length ? SourceMaps.Mode.Offset : SourceMaps.Mode.Expand,
                    data,
                });
            }
            else if (mode === 3) {
                tsCodeGen.addMapping2({
                    sourceRange,
                    mappedRange: {
                        start: tsCodeGen.getText().length - `['${mapCode}']`.length,
                        end: tsCodeGen.getText().length - `']`.length,
                    },
                    mode: SourceMaps.Mode.Offset,
                    additional: [
                        {
                            sourceRange,
                            mappedRange: {
                                start: tsCodeGen.getText().length - `'${mapCode}']`.length,
                                end: tsCodeGen.getText().length - `]`.length,
                            },
                            mode: SourceMaps.Mode.Totally,
                        }
                    ],
                    data,
                });
            }
        }
    }
    function writePropertyAccess(mapCode, sourceRange, data, checkValid = true) {
        if (checkValid && validTsVar.test(mapCode)) {
            tsCodeGen.addText(`.`);
            if (sourceRange.end - sourceRange.start === mapCode.length) {
                writeCode(mapCode, sourceRange, SourceMaps.Mode.Offset, data);
            }
            else {
                writeCode(mapCode, sourceRange, SourceMaps.Mode.Expand, data);
            }
            return 1;
        }
        else if (mapCode.startsWith('[') && mapCode.endsWith(']')) {
            writeCode(mapCode, sourceRange, SourceMaps.Mode.Offset, data);
            return 2;
        }
        else {
            tsCodeGen.addText(`[`);
            writeCodeWithQuotes(mapCode, sourceRange, data);
            tsCodeGen.addText(`]`);
            return 3;
        }
    }
    function writeCodeWithQuotes(mapCode, sourceRanges, data) {
        const addText = `'${mapCode}'`;
        for (const sourceRange of 'length' in sourceRanges ? sourceRanges : [sourceRanges]) {
            tsCodeGen.addMapping2({
                sourceRange,
                mappedRange: {
                    start: tsCodeGen.getText().length + 1,
                    end: tsCodeGen.getText().length + addText.length - 1,
                },
                mode: SourceMaps.Mode.Offset,
                additional: [
                    {
                        sourceRange,
                        mappedRange: {
                            start: tsCodeGen.getText().length,
                            end: tsCodeGen.getText().length + addText.length,
                        },
                        mode: SourceMaps.Mode.Totally,
                    }
                ],
                data,
            });
        }
        tsCodeGen.addText(addText);
    }
    function writeInterpolation(mapCode, sourceOffset, data, prefix, suffix, cacheOn) {
        const ast = createTsAst(cacheOn, prefix + mapCode + suffix);
        const vars = (0, transform_1.walkInterpolationFragment)(ts, prefix + mapCode + suffix, ast, (frag, fragOffset, isJustForErrorMapping) => {
            if (fragOffset === undefined) {
                tsCodeGen.addText(frag);
            }
            else {
                fragOffset -= prefix.length;
                let addSubfix = '';
                const overLength = fragOffset + frag.length - mapCode.length;
                if (overLength > 0) {
                    addSubfix = frag.substring(frag.length - overLength);
                    frag = frag.substring(0, frag.length - overLength);
                }
                if (fragOffset < 0) {
                    tsCodeGen.addText(frag.substring(0, -fragOffset));
                    frag = frag.substring(-fragOffset);
                    fragOffset = 0;
                }
                if (sourceOffset !== undefined && data !== undefined) {
                    writeCode(frag, {
                        start: sourceOffset + fragOffset,
                        end: sourceOffset + fragOffset + frag.length,
                    }, SourceMaps.Mode.Offset, isJustForErrorMapping
                        ? {
                            vueTag: data.vueTag,
                            capabilities: {
                                diagnostic: true,
                            },
                        }
                        : data);
                }
                else {
                    tsCodeGen.addText(frag);
                }
                tsCodeGen.addText(addSubfix);
            }
        }, localVars, identifiers);
        if (sourceOffset !== undefined) {
            for (const v of vars) {
                v.offset = sourceOffset + v.offset - prefix.length;
            }
            if (vars.length) {
                tempVars.push(vars);
            }
        }
    }
    function writeInterpolationVarsExtraCompletion() {
        if (!tempVars.length)
            return;
        tsCodeGen.addText('[');
        for (const _vars of tempVars) {
            for (const v of _vars) {
                tsCodeGen.addCode2(v.text, v.offset, {
                    vueTag: 'template',
                    capabilities: {
                        completion: {
                            additional: true,
                        },
                    },
                });
                tsCodeGen.addText(',');
            }
        }
        tsCodeGen.addText('];\n');
        tempVars.length = 0;
    }
    function writeFormatCode(mapCode, sourceOffset, formatWrapper) {
        tsFormatCodeGen.addText(formatWrapper[0]);
        const targetRange = tsFormatCodeGen.addText(mapCode);
        tsFormatCodeGen.addMapping2({
            mappedRange: targetRange,
            sourceRange: {
                start: sourceOffset,
                end: sourceOffset + mapCode.length,
            },
            mode: SourceMaps.Mode.Offset,
            data: {
                vueTag: 'template',
                capabilities: {},
            },
        });
        tsFormatCodeGen.addText(formatWrapper[1]);
        tsFormatCodeGen.addText(`\n;\n`);
    }
    function writeCode(mapCode, sourceRange, mode, data) {
        const targetRange = tsCodeGen.addText(mapCode);
        tsCodeGen.addMapping2({
            sourceRange,
            mappedRange: targetRange,
            mode,
            data,
        });
    }
}
exports.generate = generate;
;
function walkElementNodes(node, cb) {
    if (node.type === 0 /* CompilerDOM.NodeTypes.ROOT */) {
        for (const child of node.children) {
            walkElementNodes(child, cb);
        }
    }
    else if (node.type === 1 /* CompilerDOM.NodeTypes.ELEMENT */) {
        const patchForNode = getPatchForSlotNode(node);
        if (patchForNode) {
            walkElementNodes(patchForNode, cb);
            return;
        }
        cb(node);
        for (const child of node.children) {
            walkElementNodes(child, cb);
        }
    }
    else if (node.type === 9 /* CompilerDOM.NodeTypes.IF */) {
        // v-if / v-else-if / v-else
        for (let i = 0; i < node.branches.length; i++) {
            const branch = node.branches[i];
            for (const childNode of branch.children) {
                walkElementNodes(childNode, cb);
            }
        }
    }
    else if (node.type === 11 /* CompilerDOM.NodeTypes.FOR */) {
        // v-for
        for (const child of node.children) {
            walkElementNodes(child, cb);
        }
    }
}
exports.walkElementNodes = walkElementNodes;
function toUnicode(str) {
    return str.split('').map(value => {
        var temp = value.charCodeAt(0).toString(16).padStart(4, '0');
        if (temp.length > 2) {
            return '\\u' + temp;
        }
        return value;
    }).join('');
}
function unHyphenatComponentName(newName) {
    return (0, shared_1.camelize)('-' + newName);
}
function keepHyphenateName(oldName, newName) {
    if (oldName === (0, shared_1.hyphenate)(oldName)) {
        return (0, shared_1.hyphenate)(newName);
    }
    return newName;
}
// https://github.com/vuejs/vue-next/blob/master/packages/compiler-dom/src/transforms/vModel.ts#L49-L51
// https://v3.vuejs.org/guide/forms.html#basic-usage
function getModelValuePropName(node, vueVersion) {
    var _a;
    const tag = node.tag;
    const typeAttr = node.props.find(prop => prop.type === 6 /* CompilerDOM.NodeTypes.ATTRIBUTE */ && prop.name === 'type');
    const type = (_a = typeAttr === null || typeAttr === void 0 ? void 0 : typeAttr.value) === null || _a === void 0 ? void 0 : _a.content;
    if (tag === 'input' && type === 'checkbox')
        return 'checked';
    if (tag === 'input' && type === 'radio')
        return undefined;
    if (tag === 'input' ||
        tag === 'textarea' ||
        tag === 'select' ||
        vueVersion < 3)
        return 'value';
    return 'modelValue';
}
// TODO: track https://github.com/vuejs/vue-next/issues/3498
function getPatchForSlotNode(node) {
    const forDirective = node.props.find((prop) => prop.type === 7 /* CompilerDOM.NodeTypes.DIRECTIVE */
        && prop.name === 'for');
    if (forDirective) {
        let forNode;
        CompilerCore.processFor(node, forDirective, exports.transformContext, _forNode => {
            forNode = Object.assign({}, _forNode);
            return undefined;
        });
        if (forNode) {
            forNode.children = [Object.assign(Object.assign({}, node), { props: node.props.filter(prop => prop !== forDirective) })];
            return forNode;
        }
    }
}
exports.getPatchForSlotNode = getPatchForSlotNode;
//# sourceMappingURL=template.js.map