"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateInterpolation = generateInterpolation;
exports.shouldIdentifierSkipped = shouldIdentifierSkipped;
const shared_1 = require("@vue/shared");
const collectBindings_1 = require("../../utils/collectBindings");
const shared_2 = require("../../utils/shared");
const codeFeatures_1 = require("../codeFeatures");
const names_1 = require("../names");
const utils_1 = require("../utils");
const boundary_1 = require("../utils/boundary");
// https://github.com/vuejs/core/blob/fb0c3ca519f1fccf52049cd6b8db3a67a669afe9/packages/compiler-core/src/transforms/transformExpression.ts#L47
const isLiteralWhitelisted = /*@__PURE__*/ (0, shared_1.makeMap)('true,false,null,this');
function* generateInterpolation({ typescript, setupRefs }, ctx, block, data, code, start, prefix = '', suffix = '') {
    if (prefix) {
        yield prefix;
    }
    let prevEnd = 0;
    for (const [name, offset, isShorthand] of forEachIdentifiers(typescript, ctx, block, code, prefix, suffix)) {
        if (isShorthand) {
            yield [
                code.slice(prevEnd, offset + name.length),
                block.name,
                start + prevEnd,
                data,
            ];
            yield `: `;
        }
        else if (prevEnd < offset) {
            yield [
                code.slice(prevEnd, offset),
                block.name,
                start + prevEnd,
                data,
            ];
        }
        if (setupRefs.has(name)) {
            yield [
                name,
                block.name,
                start + offset,
                data,
            ];
            yield `.value`;
        }
        else {
            // #1205, #1264
            const boundary = yield* boundary_1.Boundary.start(block.name, start + offset, codeFeatures_1.codeFeatures.verification);
            if (ctx.dollarVars.has(name)) {
                yield names_1.names.dollars;
            }
            else {
                ctx.accessVariable(block.name, name, start + offset);
                yield names_1.names.ctx;
            }
            yield `.`;
            yield [
                name,
                block.name,
                start + offset,
                isShorthand
                    ? { ...data, __shorthandExpression: 'js' }
                    : data,
            ];
            yield boundary.end(start + offset + name.length);
        }
        prevEnd = offset + name.length;
    }
    if (prevEnd < code.length) {
        yield [
            code.slice(prevEnd),
            block.name,
            start + prevEnd,
            data,
        ];
    }
    if (suffix) {
        yield suffix;
    }
}
function* forEachIdentifiers(ts, ctx, block, code, prefix, suffix) {
    if (utils_1.identifierRE.test(code) && !shouldIdentifierSkipped(ctx, code)) {
        yield [code, 0, false];
        return;
    }
    const scope = ctx.scope();
    const ast = (0, utils_1.getTypeScriptAST)(ts, block, prefix + code + suffix);
    for (const [id, isShorthand] of forEachDeclarations(ts, ast, ast, ctx, scope)) {
        const text = (0, shared_2.getNodeText)(ts, id, ast);
        if (shouldIdentifierSkipped(ctx, text)) {
            continue;
        }
        yield [text, (0, shared_2.getStartEnd)(ts, id, ast).start - prefix.length, isShorthand];
    }
    scope.end();
}
function* forEachDeclarations(ts, node, ast, ctx, scope) {
    if (ts.isIdentifier(node)) {
        yield [node, false];
    }
    else if (ts.isShorthandPropertyAssignment(node)) {
        yield [node.name, true];
    }
    else if (ts.isPropertyAccessExpression(node)) {
        yield* forEachDeclarations(ts, node.expression, ast, ctx, scope);
    }
    else if (ts.isVariableDeclaration(node)) {
        scope.declare(...(0, collectBindings_1.collectBindingNames)(ts, node.name, ast));
        yield* forEachDeclarationsInBinding(ts, node, ast, ctx, scope);
    }
    else if (ts.isArrayBindingPattern(node) || ts.isObjectBindingPattern(node)) {
        for (const element of node.elements) {
            if (ts.isBindingElement(element)) {
                yield* forEachDeclarationsInBinding(ts, element, ast, ctx, scope);
            }
        }
    }
    else if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
        yield* forEachDeclarationsInFunction(ts, node, ast, ctx);
    }
    else if (ts.isObjectLiteralExpression(node)) {
        for (const prop of node.properties) {
            if (ts.isPropertyAssignment(prop)) {
                // fix https://github.com/vuejs/language-tools/issues/1176
                if (ts.isComputedPropertyName(prop.name)) {
                    yield* forEachDeclarations(ts, prop.name.expression, ast, ctx, scope);
                }
                yield* forEachDeclarations(ts, prop.initializer, ast, ctx, scope);
            }
            // fix https://github.com/vuejs/language-tools/issues/1156
            else if (ts.isShorthandPropertyAssignment(prop)) {
                yield* forEachDeclarations(ts, prop, ast, ctx, scope);
            }
            // fix https://github.com/vuejs/language-tools/issues/1148#issuecomment-1094378126
            else if (ts.isSpreadAssignment(prop)) {
                // TODO: cannot report "Spread types may only be created from object types.ts(2698)"
                yield* forEachDeclarations(ts, prop.expression, ast, ctx, scope);
            }
            // fix https://github.com/vuejs/language-tools/issues/4604
            else if (ts.isFunctionLike(prop) && prop.body) {
                yield* forEachDeclarationsInFunction(ts, prop, ast, ctx);
            }
        }
    }
    // fix https://github.com/vuejs/language-tools/issues/1422
    else if (ts.isTypeNode(node)) {
        yield* forEachDeclarationsInTypeNode(ts, node);
    }
    else if (ts.isBlock(node)) {
        const scope = ctx.scope();
        for (const child of (0, utils_1.forEachNode)(ts, node)) {
            yield* forEachDeclarations(ts, child, ast, ctx, scope);
        }
        scope.end();
    }
    else {
        for (const child of (0, utils_1.forEachNode)(ts, node)) {
            yield* forEachDeclarations(ts, child, ast, ctx, scope);
        }
    }
}
function* forEachDeclarationsInBinding(ts, node, ast, ctx, scope) {
    if ('type' in node && node.type) {
        yield* forEachDeclarationsInTypeNode(ts, node.type);
    }
    if (!ts.isIdentifier(node.name)) {
        yield* forEachDeclarations(ts, node.name, ast, ctx, scope);
    }
    if (node.initializer) {
        yield* forEachDeclarations(ts, node.initializer, ast, ctx, scope);
    }
}
function* forEachDeclarationsInFunction(ts, node, ast, ctx) {
    const scope = ctx.scope();
    for (const param of node.parameters) {
        scope.declare(...(0, collectBindings_1.collectBindingNames)(ts, param.name, ast));
        yield* forEachDeclarationsInBinding(ts, param, ast, ctx, scope);
    }
    if (node.body) {
        yield* forEachDeclarations(ts, node.body, ast, ctx, scope);
    }
    scope.end();
}
function* forEachDeclarationsInTypeNode(ts, node) {
    if (ts.isTypeQueryNode(node)) {
        let id = node.exprName;
        while (!ts.isIdentifier(id)) {
            id = id.left;
        }
        yield [id, false];
    }
    else {
        for (const child of (0, utils_1.forEachNode)(ts, node)) {
            yield* forEachDeclarationsInTypeNode(ts, child);
        }
    }
}
function shouldIdentifierSkipped(ctx, text) {
    return ctx.scopes.some(scope => scope.has(text))
        // https://github.com/vuejs/core/blob/245230e135152900189f13a4281302de45fdcfaa/packages/compiler-core/src/transforms/transformExpression.ts#L342-L352
        || (0, shared_1.isGloballyAllowed)(text)
        || isLiteralWhitelisted(text)
        || text === 'require'
        || text.startsWith('__VLS_');
}
//# sourceMappingURL=interpolation.js.map