eslint-plugin-formatjs
Version:
ESLint plugin for formatjs
104 lines (103 loc) • 3.45 kB
JavaScript
import { isPluralElement, parse, } from '@formatjs/icu-messageformat-parser';
import { getParserServices } from '../context-compat.js';
import { extractMessages, getSettings } from '../util.js';
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,
};
},
};