UNPKG

eslint-plugin-formatjs

Version:
104 lines (103 loc) 3.44 kB
import { isPluralElement, parse, } from '@formatjs/icu-messageformat-parser'; import { getParserServices } from '../context-compat'; import { extractMessages, getSettings } from '../util'; var LDML; (function (LDML) { LDML["zero"] = "zero"; LDML["one"] = "one"; LDML["two"] = "two"; LDML["few"] = "few"; LDML["many"] = "many"; LDML["other"] = "other"; })(LDML || (LDML = {})); function verifyAst(plConfig, ast) { const errors = []; for (const el of ast) { if (isPluralElement(el)) { const rules = Object.keys(plConfig); for (const rule of rules) { if (plConfig[rule] && !el.options[rule]) { errors.push({ messageId: 'missingPlural', data: { rule } }); } if (!plConfig[rule] && el.options[rule]) { errors.push({ messageId: 'forbidden', data: { rule } }); } } const { options } = el; for (const selector of Object.keys(options)) { errors.push(...verifyAst(plConfig, options[selector].value)); } } } return errors; } function checkNode(context, node) { const settings = getSettings(context); const msgs = extractMessages(node, settings); if (!msgs.length) { return; } const plConfig = context.options[0]; if (!plConfig) { return; } for (const [{ message: { defaultMessage }, messageNode, },] of msgs) { if (!defaultMessage || !messageNode) { continue; } const errors = verifyAst(plConfig, parse(defaultMessage, { ignoreTag: settings.ignoreTag, })); for (const error of errors) { context.report({ node: messageNode, ...error, }); } } } export const name = 'enforce-plural-rules'; export const rule = { meta: { type: 'problem', docs: { description: 'Enforce plural rules to always specify certain categories like `one`/`other`', url: 'https://formatjs.github.io/docs/tooling/linter#enforce-plural-rules', }, fixable: 'code', schema: [ { type: 'object', properties: Object.keys(LDML).reduce((schema, k) => { schema[k] = { type: 'boolean', }; return schema; }, {}), additionalProperties: false, }, ], messages: { missingPlural: `Missing plural rule "{{rule}}"`, forbidden: `Plural rule "{{rule}}" is forbidden`, }, }, defaultOptions: [], create(context) { const callExpressionVisitor = (node) => checkNode(context, node); const parserServices = getParserServices(context); //@ts-expect-error defineTemplateBodyVisitor exists in Vue parser if (parserServices?.defineTemplateBodyVisitor) { //@ts-expect-error return parserServices.defineTemplateBodyVisitor({ CallExpression: callExpressionVisitor, }, { CallExpression: callExpressionVisitor, }); } return { JSXOpeningElement: (node) => checkNode(context, node), CallExpression: callExpressionVisitor, }; }, };