"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateElementProps = generateElementProps;
const CompilerDOM = require("@vue/compiler-dom");
const shared_1 = require("@vue/shared");
const minimatch_1 = require("minimatch");
const muggle_string_1 = require("muggle-string");
const shared_2 = require("../../utils/shared");
const common_1 = require("../common");
const camelized_1 = require("./camelized");
const elementEvents_1 = require("./elementEvents");
const interpolation_1 = require("./interpolation");
const objectProperty_1 = require("./objectProperty");
function* generateElementProps(options, ctx, node, props, enableCodeFeatures, propsFailedExps) {
    const isIntrinsicElement = node.tagType === CompilerDOM.ElementTypes.ELEMENT || node.tagType === CompilerDOM.ElementTypes.TEMPLATE;
    const canCamelize = node.tagType === CompilerDOM.ElementTypes.COMPONENT;
    for (const prop of props) {
        if (prop.type === CompilerDOM.NodeTypes.DIRECTIVE
            && prop.name === 'on') {
            if (prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
                && !prop.arg.loc.source.startsWith('[')
                && !prop.arg.loc.source.endsWith(']')) {
                if (isIntrinsicElement) {
                    yield `...{ `;
                    yield* (0, elementEvents_1.generateEventArg)(ctx, prop.arg, true);
                    yield `: `;
                    yield* (0, elementEvents_1.generateEventExpression)(options, ctx, prop);
                    yield `}, `;
                }
                else {
                    yield `...{ '${(0, shared_1.camelize)('on-' + prop.arg.loc.source)}': {} as any }, `;
                }
            }
            else if (prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
                && prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
                && prop.arg.loc.source.startsWith('[')
                && prop.arg.loc.source.endsWith(']')) {
                propsFailedExps?.push({ node: prop.arg, prefix: '(', suffix: ')' });
                propsFailedExps?.push({ node: prop.exp, prefix: '() => {', suffix: '}' });
            }
            else if (!prop.arg
                && prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
                propsFailedExps?.push({ node: prop.exp, prefix: '(', suffix: ')' });
            }
        }
    }
    for (const prop of props) {
        if (prop.type === CompilerDOM.NodeTypes.DIRECTIVE
            && ((prop.name === 'bind' && prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION)
                || prop.name === 'model')
            && (!prop.exp || prop.exp.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION)) {
            let propName;
            if (prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
                propName = prop.arg.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY
                    ? prop.arg.content
                    : prop.arg.loc.source;
            }
            else {
                propName = getModelValuePropName(node, options.vueCompilerOptions.target, options.vueCompilerOptions);
            }
            if (propName === undefined
                || options.vueCompilerOptions.dataAttributes.some(pattern => (0, minimatch_1.minimatch)(propName, pattern))) {
                if (prop.exp && prop.exp.constType !== CompilerDOM.ConstantTypes.CAN_STRINGIFY) {
                    propsFailedExps?.push({ node: prop.exp, prefix: '(', suffix: ')' });
                }
                continue;
            }
            if (prop.modifiers.some(m => m === 'prop' || m === 'attr')) {
                propName = propName.substring(1);
            }
            const shouldSpread = propName === 'style' || propName === 'class';
            const shouldCamelize = canCamelize
                && (!prop.arg || (prop.arg.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && prop.arg.isStatic)) // isStatic
                && (0, shared_2.hyphenateAttr)(propName) === propName
                && !options.vueCompilerOptions.htmlAttributes.some(pattern => (0, minimatch_1.minimatch)(propName, pattern));
            if (shouldSpread) {
                yield `...{ `;
            }
            const codes = (0, common_1.wrapWith)(prop.loc.start.offset, prop.loc.end.offset, ctx.codeFeatures.verification, ...(prop.arg
                ? (0, objectProperty_1.generateObjectProperty)(options, ctx, propName, prop.arg.loc.start.offset, {
                    ...ctx.codeFeatures.withoutHighlightAndCompletion,
                    navigation: ctx.codeFeatures.withoutHighlightAndCompletion.navigation
                        ? {
                            resolveRenameNewName: shared_1.camelize,
                            resolveRenameEditText: shouldCamelize ? shared_2.hyphenateAttr : undefined,
                        }
                        : false,
                }, prop.loc.name_2 ?? (prop.loc.name_2 = {}), shouldCamelize)
                : (0, common_1.wrapWith)(prop.loc.start.offset, prop.loc.start.offset + 'v-model'.length, ctx.codeFeatures.verification, propName)), `: (`, ...genereatePropExp(options, ctx, prop.exp, ctx.codeFeatures.all, prop.arg?.loc.start.offset === prop.exp?.loc.start.offset, enableCodeFeatures), `)`);
            if (!enableCodeFeatures) {
                yield (0, muggle_string_1.toString)([...codes]);
            }
            else {
                yield* codes;
            }
            if (shouldSpread) {
                yield ` }`;
            }
            yield `, `;
        }
        else if (prop.type === CompilerDOM.NodeTypes.ATTRIBUTE) {
            if (options.vueCompilerOptions.dataAttributes.some(pattern => (0, minimatch_1.minimatch)(prop.name, pattern))
                // Vue 2 Transition doesn't support "persisted" property but `@vue/compiler-dom always adds it (#3881)
                || (options.vueCompilerOptions.target < 3
                    && prop.name === 'persisted'
                    && node.tag.toLowerCase() === 'transition')) {
                continue;
            }
            const shouldSpread = prop.name === 'style' || prop.name === 'class';
            const shouldCamelize = canCamelize
                && (0, shared_2.hyphenateAttr)(prop.name) === prop.name
                && !options.vueCompilerOptions.htmlAttributes.some(pattern => (0, minimatch_1.minimatch)(prop.name, pattern));
            if (shouldSpread) {
                yield `...{ `;
            }
            const codes = (0, common_1.conditionWrapWith)(enableCodeFeatures, prop.loc.start.offset, prop.loc.end.offset, ctx.codeFeatures.verification, ...(0, objectProperty_1.generateObjectProperty)(options, ctx, prop.name, prop.loc.start.offset, shouldCamelize
                ? {
                    ...ctx.codeFeatures.withoutHighlightAndCompletion,
                    navigation: ctx.codeFeatures.withoutHighlightAndCompletion.navigation
                        ? {
                            resolveRenameNewName: shared_1.camelize,
                            resolveRenameEditText: shared_2.hyphenateAttr,
                        }
                        : false,
                }
                : ctx.codeFeatures.withoutHighlightAndCompletion, prop.loc.name_1 ?? (prop.loc.name_1 = {}), shouldCamelize), `: (`, ...(prop.value
                ? generateAttrValue(prop.value, ctx.codeFeatures.all)
                : [`true`]), `)`);
            if (!enableCodeFeatures) {
                yield (0, muggle_string_1.toString)([...codes]);
            }
            else {
                yield* codes;
            }
            if (shouldSpread) {
                yield ` }`;
            }
            yield `, `;
        }
        else if (prop.type === CompilerDOM.NodeTypes.DIRECTIVE
            && prop.name === 'bind'
            && !prop.arg
            && prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
            const codes = (0, common_1.conditionWrapWith)(enableCodeFeatures, prop.exp.loc.start.offset, prop.exp.loc.end.offset, ctx.codeFeatures.verification, `...`, ...(0, interpolation_1.generateInterpolation)(options, ctx, prop.exp.content, prop.exp.loc, prop.exp.loc.start.offset, ctx.codeFeatures.all, '(', ')'));
            if (!enableCodeFeatures) {
                yield (0, muggle_string_1.toString)([...codes]);
            }
            else {
                yield* codes;
            }
            yield `, `;
        }
        else {
            // 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(", ") + " */ ");
        }
    }
}
function* genereatePropExp(options, ctx, exp, features, isShorthand, inlayHints) {
    if (exp && exp.constType !== CompilerDOM.ConstantTypes.CAN_STRINGIFY) { // style='z-index: 2' will compile to {'z-index':'2'}
        if (!isShorthand) { // vue 3.4+
            yield* (0, interpolation_1.generateInterpolation)(options, ctx, exp.loc.source, exp.loc, exp.loc.start.offset, features, '(', ')');
        }
        else {
            const propVariableName = (0, shared_1.camelize)(exp.loc.source);
            if (common_1.variableNameRegex.test(propVariableName)) {
                if (!ctx.hasLocalVariable(propVariableName)) {
                    ctx.accessExternalVariable(propVariableName, exp.loc.start.offset);
                    yield `__VLS_ctx.`;
                }
                yield* (0, camelized_1.generateCamelized)(exp.loc.source, exp.loc.start.offset, features);
                if (inlayHints) {
                    yield [
                        '',
                        'template',
                        exp.loc.end.offset,
                        {
                            __hint: {
                                setting: 'vue.inlayHints.vBindShorthand',
                                label: `="${propVariableName}"`,
                                tooltip: [
                                    `This is a shorthand for \`${exp.loc.source}="${propVariableName}"\`.`,
                                    'To hide this hint, set `vue.inlayHints.vBindShorthand` to `false` in IDE settings.',
                                    '[More info](https://github.com/vuejs/core/pull/9451)',
                                ].join('\n\n'),
                            },
                        },
                    ];
                }
            }
        }
    }
    else {
        yield `{}`;
    }
}
function* generateAttrValue(attrNode, features) {
    const char = attrNode.loc.source.startsWith("'") ? "'" : '"';
    yield char;
    let start = attrNode.loc.start.offset;
    let end = attrNode.loc.end.offset;
    let content = attrNode.loc.source;
    if ((content.startsWith('"') && content.endsWith('"'))
        || (content.startsWith("'") && content.endsWith("'"))) {
        start++;
        end--;
        content = content.slice(1, -1);
    }
    if (needToUnicode(content)) {
        yield* (0, common_1.wrapWith)(start, end, features, toUnicode(content));
    }
    else {
        yield [content, 'template', start, features];
    }
    yield char;
}
function needToUnicode(str) {
    return str.includes('\\') || str.includes('\n');
}
function toUnicode(str) {
    return str.split('').map(value => {
        const temp = value.charCodeAt(0).toString(16).padStart(4, '0');
        if (temp.length > 2) {
            return '\\u' + temp;
        }
        return value;
    }).join('');
}
function getModelValuePropName(node, vueVersion, vueCompilerOptions) {
    for (const modelName in vueCompilerOptions.experimentalModelPropName) {
        const tags = vueCompilerOptions.experimentalModelPropName[modelName];
        for (const tag in tags) {
            if (node.tag === tag || node.tag === (0, shared_2.hyphenateTag)(tag)) {
                const v = tags[tag];
                if (typeof v === 'object') {
                    const arr = Array.isArray(v) ? v : [v];
                    for (const attrs of arr) {
                        let failed = false;
                        for (const attr in attrs) {
                            const attrNode = node.props.find(prop => prop.type === CompilerDOM.NodeTypes.ATTRIBUTE && prop.name === attr);
                            if (!attrNode || attrNode.value?.content !== attrs[attr]) {
                                failed = true;
                                break;
                            }
                        }
                        if (!failed) {
                            // all match
                            return modelName || undefined;
                        }
                    }
                }
            }
        }
    }
    for (const modelName in vueCompilerOptions.experimentalModelPropName) {
        const tags = vueCompilerOptions.experimentalModelPropName[modelName];
        for (const tag in tags) {
            if (node.tag === tag || node.tag === (0, shared_2.hyphenateTag)(tag)) {
                const attrs = tags[tag];
                if (attrs === true) {
                    return modelName || undefined;
                }
            }
        }
    }
    return vueVersion < 3 ? 'value' : 'modelValue';
}
//# sourceMappingURL=elementProps.js.map