eslint-plugin-formatjs
Version:
ESLint plugin for formatjs
104 lines (103 loc) • 4.31 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.rule = exports.name = void 0;
const tslib_1 = require("tslib");
const icu_messageformat_parser_1 = require("@formatjs/icu-messageformat-parser");
const magic_string_1 = tslib_1.__importDefault(require("magic-string"));
const context_compat_1 = require("../context-compat");
const util_1 = require("../util");
exports.name = 'no-missing-icu-plural-one-placeholders';
function verifyAst(context, messageNode, ast) {
const patches = [];
_verifyAstAndReplace(ast);
if (patches.length > 0) {
const patchedMessage = (0, util_1.patchMessage)(messageNode, ast, content => {
return patches
.reduce((magicString, patch) => {
switch (patch.type) {
case 'prependLeft':
return magicString.prependLeft(patch.index, patch.content);
case 'remove':
return magicString.remove(patch.start, patch.end);
case 'update':
return magicString.update(patch.start, patch.end, patch.content);
}
}, new magic_string_1.default(content))
.toString();
});
context.report({
node: messageNode,
messageId: 'noMissingIcuPluralOnePlaceholders',
fix: patchedMessage !== null
? fixer => fixer.replaceText(messageNode, patchedMessage)
: null,
});
}
function _verifyAstAndReplace(ast, root = true) {
for (const el of ast) {
if ((0, icu_messageformat_parser_1.isPluralElement)(el) && el.options['one']) {
_verifyAstAndReplace(el.options['one'].value, false);
}
else if ((0, icu_messageformat_parser_1.isSelectElement)(el)) {
for (const { value } of Object.values(el.options)) {
_verifyAstAndReplace(value, root);
}
}
else if ((0, icu_messageformat_parser_1.isTagElement)(el)) {
_verifyAstAndReplace(el.children, root);
}
else if (!root && (0, icu_messageformat_parser_1.isLiteralElement)(el)) {
const match = el.value.match(/\b1\b/);
if (match && el.location) {
patches.push({
type: 'update',
start: el.location.start.offset,
end: el.location.end.offset,
content: el.value.replace(match[0], '#'),
});
}
}
}
}
}
function checkNode(context, node) {
const msgs = (0, util_1.extractMessages)(node);
for (const [{ message: { defaultMessage }, messageNode, },] of msgs) {
if (!defaultMessage || !messageNode) {
continue;
}
verifyAst(context, messageNode, (0, icu_messageformat_parser_1.parse)(defaultMessage, { captureLocation: true }));
}
}
exports.rule = {
meta: {
type: 'problem',
docs: {
description: 'We use `one {# item}` instead of `one {1 item}` in ICU messages as some locales use the `one` formatting for other similar numbers.',
url: 'https://formatjs.github.io/docs/tooling/linter#no-explicit-icu-plural',
},
fixable: 'code',
messages: {
noMissingIcuPluralOnePlaceholders: 'Use `one {# item}` instead of `one {1 item}` in ICU messages.',
},
schema: [],
},
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,
};
},
};