<commit_before>import * as t from 'babel-types';
import { stripTag, template2Msgid, msgid2Orig,
    isValidQuasiExpression, ast2Str } from '../utils';
import { PO_PRIMITIVES } from '../defaults';
import tpl from 'babel-template';
import { hasTranslations, getPluralFunc, getNPlurals, pluralFnBody,
    makePluralFunc } from '../po-helpers';

const { MSGID, MSGSTR, MSGID_PLURAL } = PO_PRIMITIVES;

function validateExpresssions(expressions) {
    expressions.forEach((exp) => {
        if (!isValidQuasiExpression(exp)) {
            throw new Error(`You can not use ${exp.type} '\${${ast2Str(exp)}}' in localized strings`);
        }
    });
}

function validateNPlural(exp) {
    if (!t.isIdentifier(exp) && !t.isNumericLiteral(exp)) {
        throw new Error(`${exp.type} '${ast2Str(exp)}' can not be used as plural number argument`);
    }
}

const validate = (fn) => (path, ...args) => {
    const { node } = path;
    validateExpresssions(node.quasi.expressions);
    validateNPlural(node.tag.arguments[0]);
    return fn(path, ...args);
};

function ngettextTemplate(ngettext, pluralForm) {
    return tpl(`function NGETTEXT(n, args) { ${pluralFnBody(pluralForm)} }`)({ NGETTEXT: ngettext });
}

function getNgettextUID(state, pluralFunc) {
    /* eslint-disable no-underscore-dangle */
    if (! state.file.__ngettextUid) {
        const uid = state.file.scope.generateUidIdentifier('ngettext');
        state.file.path.unshiftContainer('body',
            ngettextTemplate(uid, pluralFunc));
        state.file.__ngettextUid = uid;
    }
    return state.file.__ngettextUid;
}

function extract({ node }, config) {
    const nplurals = getNPlurals(config.getHeaders());
    const nodeStr = template2Msgid(node);
    const translate = {
        [MSGID]: nodeStr,
        [MSGID_PLURAL]: nodeStr,
        [MSGSTR]: [],
    };

    for (let i = 0; i < nplurals; i++) {
        translate[MSGSTR][i] = '';
    }

    return translate;
}

function match({ node }, config) {
    return (t.isTaggedTemplateExpression(node) &&
        node.tag.callee &&
        node.tag.callee.name === config.getAliasFor('ngettext'));
}

function resolveDefault(nodePath) {
    return stripTag(nodePath);
}

function resolve(path, poData, config, state) {
    // TODO: handle when has no node argument.
    const { translations, headers } = poData;
    const { node } = path;
    const msgid = template2Msgid(node);
    const translationObj = translations[msgid];

    if (!translationObj || translationObj && !hasTranslations(translationObj)) {
        config.unresolvedAction(`No translation for "${msgid}" in "${config.getPoFilePath()}" file`);
        stripTag(path);
        return path;
    }

    const args = translations[msgid][MSGSTR];
    const tagArg = node.tag.arguments[0];
    const exprs = node.quasi.expressions.map(({ name }) => name);

    if (t.isIdentifier(tagArg)) {
        return path.replaceWith(tpl('NGETTEXT(N, ARGS)')({
            NGETTEXT: getNgettextUID(state, getPluralFunc(headers)),
            N: t.identifier(tagArg.name),
            ARGS: t.arrayExpression(args.map((l) => {
                const { expression: { quasis, expressions } } = tpl(msgid2Orig(l, exprs))();
                return t.templateLiteral(quasis, expressions);
            })),
        }));
    }

    if (t.isLiteral(tagArg)) {
        const pluralFn = makePluralFunc(getPluralFunc(headers));
        const orig = msgid2Orig(pluralFn(tagArg.value, args), exprs);
        return path.replaceWith(tpl(orig)());
    }
}

export default { match, extract: validate(extract), resolve: validate(resolve), resolveDefault };<commit_msg>fixlint<commit_after>import * as t from 'babel-types';
import { stripTag, template2Msgid, msgid2Orig,
    isValidQuasiExpression, ast2Str } from '../utils';
import { PO_PRIMITIVES } from '../defaults';
import tpl from 'babel-template';
import { hasTranslations, getPluralFunc, getNPlurals, pluralFnBody,
    makePluralFunc } from '../po-helpers';

const { MSGID, MSGSTR, MSGID_PLURAL } = PO_PRIMITIVES;

function validateExpresssions(expressions) {
    expressions.forEach((exp) => {
        if (!isValidQuasiExpression(exp)) {
            throw new Error(`You can not use ${exp.type} '\${${ast2Str(exp)}}' in localized strings`);
        }
    });
}

function validateNPlural(exp) {
    if (!t.isIdentifier(exp) && !t.isNumericLiteral(exp)) {
        throw new Error(`${exp.type} '${ast2Str(exp)}' can not be used as plural number argument`);
    }
}

const validate = (fn) => (path, ...args) => {
    const { node } = path;
    validateExpresssions(node.quasi.expressions);
    validateNPlural(node.tag.arguments[0]);
    return fn(path, ...args);
};

function ngettextTemplate(ngettext, pluralForm) {
    return tpl(`function NGETTEXT(n, args) { ${pluralFnBody(pluralForm)} }`)({ NGETTEXT: ngettext });
}

function getNgettextUID(state, pluralFunc) {
    /* eslint-disable no-underscore-dangle */
    if (! state.file.__ngettextUid) {
        const uid = state.file.scope.generateUidIdentifier('ngettext');
        state.file.path.unshiftContainer('body',
            ngettextTemplate(uid, pluralFunc));
        state.file.__ngettextUid = uid;
    }
    return state.file.__ngettextUid;
}

function extract({ node }, config) {
    const nplurals = getNPlurals(config.getHeaders());
    const nodeStr = template2Msgid(node);
    const translate = {
        [MSGID]: nodeStr,
        [MSGID_PLURAL]: nodeStr,
        [MSGSTR]: [],
    };

    for (let i = 0; i < nplurals; i++) {
        translate[MSGSTR][i] = '';
    }

    return translate;
}

function match({ node }, config) {
    return (t.isTaggedTemplateExpression(node) &&
        node.tag.callee &&
        node.tag.callee.name === config.getAliasFor('ngettext'));
}

function resolveDefault(nodePath) {
    return stripTag(nodePath);
}

function resolve(path, poData, config, state) {
    // TODO: handle when has no node argument.
    const { translations, headers } = poData;
    const { node } = path;
    const msgid = template2Msgid(node);
    const translationObj = translations[msgid];

    if (!translationObj || translationObj && !hasTranslations(translationObj)) {
        config.unresolvedAction(`No translation for "${msgid}" in "${config.getPoFilePath()}" file`);
        stripTag(path);
        return path;
    }

    const args = translations[msgid][MSGSTR];
    const tagArg = node.tag.arguments[0];
    const exprs = node.quasi.expressions.map(({ name }) => name);

    if (t.isIdentifier(tagArg)) {
        return path.replaceWith(tpl('NGETTEXT(N, ARGS)')({
            NGETTEXT: getNgettextUID(state, getPluralFunc(headers)),
            N: t.identifier(tagArg.name),
            ARGS: t.arrayExpression(args.map((l) => {
                const { expression: { quasis, expressions } } = tpl(msgid2Orig(l, exprs))();
                return t.templateLiteral(quasis, expressions);
            })),
        }));
    }

    if (t.isLiteral(tagArg)) {
        const pluralFn = makePluralFunc(getPluralFunc(headers));
        const orig = msgid2Orig(pluralFn(tagArg.value, args), exprs);
        return path.replaceWith(tpl(orig)());
    }
    return path;
}

export default { match, extract: validate(extract), resolve: validate(resolve), resolveDefault };
{'fraction_non_alphanumeric': 0.14573280022059837, 'fraction_numerical': 0.003308975596304977, 'mean_word_length': 3.2645502645502646, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1849716', 'n_tokens_mistral': 2315, 'n_tokens_neox': 2286, 'n_words': 478}