eslint-plugin-formatjs
Version:
ESLint plugin for formatjs
136 lines (135 loc) • 5.12 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.rule = exports.Element = exports.name = void 0;
const icu_messageformat_parser_1 = require("@formatjs/icu-messageformat-parser");
const utils_1 = require("@typescript-eslint/utils");
const context_compat_1 = require("../context-compat");
const util_1 = require("../util");
exports.name = 'blocklist-elements';
function getMessage(type) {
return {
messageId: 'blocklist',
data: { type },
};
}
var Element;
(function (Element) {
Element["literal"] = "literal";
Element["argument"] = "argument";
Element["number"] = "number";
Element["date"] = "date";
Element["time"] = "time";
Element["select"] = "select";
Element["selectordinal"] = "selectordinal";
Element["plural"] = "plural";
Element["tag"] = "tag";
})(Element || (exports.Element = Element = {}));
function verifyAst(blocklist, ast) {
const errors = [];
for (const el of ast) {
if ((0, icu_messageformat_parser_1.isLiteralElement)(el) && blocklist.includes(Element.literal)) {
errors.push(getMessage(Element.literal));
}
if ((0, icu_messageformat_parser_1.isArgumentElement)(el) && blocklist.includes(Element.argument)) {
errors.push(getMessage(Element.argument));
}
if ((0, icu_messageformat_parser_1.isNumberElement)(el) && blocklist.includes(Element.number)) {
errors.push(getMessage(Element.number));
}
if ((0, icu_messageformat_parser_1.isDateElement)(el) && blocklist.includes(Element.date)) {
errors.push(getMessage(Element.date));
}
if ((0, icu_messageformat_parser_1.isTimeElement)(el) && blocklist.includes(Element.time)) {
errors.push(getMessage(Element.time));
}
if ((0, icu_messageformat_parser_1.isSelectElement)(el) && blocklist.includes(Element.select)) {
errors.push(getMessage(Element.select));
}
if ((0, icu_messageformat_parser_1.isTagElement)(el) && blocklist.includes(Element.tag)) {
errors.push(getMessage(Element.tag));
}
if ((0, icu_messageformat_parser_1.isPluralElement)(el)) {
if (blocklist.includes(Element.plural)) {
errors.push(getMessage(Element.argument));
}
if (el.pluralType === 'ordinal' &&
blocklist.includes(Element.selectordinal)) {
errors.push(getMessage(Element.selectordinal));
}
}
if ((0, icu_messageformat_parser_1.isSelectElement)(el) || (0, icu_messageformat_parser_1.isPluralElement)(el)) {
const { options } = el;
for (const selector of Object.keys(options)) {
verifyAst(blocklist, options[selector].value);
}
}
}
return errors;
}
function checkNode(context, node) {
const settings = (0, util_1.getSettings)(context);
const msgs = (0, util_1.extractMessages)(node, settings);
if (!msgs.length) {
return;
}
const blocklist = context.options[0];
if (!Array.isArray(blocklist) || !blocklist.length) {
return;
}
for (const [{ message: { defaultMessage }, messageNode, },] of msgs) {
if (!defaultMessage || !messageNode) {
continue;
}
const errors = verifyAst(blocklist, (0, icu_messageformat_parser_1.parse)(defaultMessage, {
ignoreTag: settings.ignoreTag,
}));
for (const error of errors) {
context.report({
node,
...error,
});
}
}
}
const createRule = utils_1.ESLintUtils.RuleCreator(_ => 'https://formatjs.github.io/docs/tooling/linter#blocklist-elements');
exports.rule = createRule({
name: exports.name,
meta: {
type: 'problem',
docs: {
description: 'Disallow specific elements in ICU message format',
url: 'https://formatjs.github.io/docs/tooling/linter#blocklist-elements',
},
fixable: 'code',
schema: [
{
type: 'array',
items: {
type: 'string',
enum: Object.keys(Element),
},
},
],
messages: {
blocklist: `{{type}} element is blocklisted`,
},
},
defaultOptions: [],
create(context) {
const callExpressionVisitor = node => checkNode(context, node);
const parserServices = (0, context_compat_1.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,
};
},
});