eslint-plugin-formatjs 6.4.20 → 6.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +78 -79
- package/index.js.map +1 -1
- package/package.json +4 -4
- package/util.d.ts +8 -9
- package/util.js +1 -4
- package/util.js.map +1 -1
package/index.js
CHANGED
|
@@ -119,7 +119,6 @@ function extractMessageDescriptor(node) {
|
|
|
119
119
|
result.message.id = value;
|
|
120
120
|
result.idValueNode = valueNode;
|
|
121
121
|
result.idPropNode = prop;
|
|
122
|
-
break;
|
|
123
122
|
}
|
|
124
123
|
}
|
|
125
124
|
return result;
|
|
@@ -166,9 +165,7 @@ function extractMessageDescriptorFromJSXElement(node) {
|
|
|
166
165
|
result.idPropNode = prop;
|
|
167
166
|
if (value) result.message.id = value;
|
|
168
167
|
break;
|
|
169
|
-
case "values":
|
|
170
|
-
if (valueNode?.type === "JSXExpressionContainer" && valueNode.expression.type === "ObjectExpression") values = valueNode.expression;
|
|
171
|
-
break;
|
|
168
|
+
case "values": if (valueNode?.type === "JSXExpressionContainer" && valueNode.expression.type === "ObjectExpression") values = valueNode.expression;
|
|
172
169
|
}
|
|
173
170
|
}
|
|
174
171
|
if (!result.messagePropNode && !result.descriptionNode && !result.idPropNode && hasSpreadAttribute) return;
|
|
@@ -462,48 +459,50 @@ function checkNode$14(context, node, { idInterpolationPattern, idWhitelistRegexp
|
|
|
462
459
|
});
|
|
463
460
|
return;
|
|
464
461
|
}
|
|
465
|
-
if (idInterpolationPattern)
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
if (idWhitelistRegexps) {
|
|
484
|
-
messageId = "enforceIdMatchingAllowlisted";
|
|
485
|
-
messageData = {
|
|
486
|
-
...messageData,
|
|
487
|
-
idWhitelist: idWhitelistRegexps.map((r) => `"${r.toString()}"`).join(", ")
|
|
462
|
+
if (idInterpolationPattern) {
|
|
463
|
+
if (!defaultMessage) context.report({
|
|
464
|
+
node: messageDescriptorNode ?? node,
|
|
465
|
+
messageId: "enforceIdDefaultMessage"
|
|
466
|
+
});
|
|
467
|
+
else if (!description && descriptionNode) context.report({
|
|
468
|
+
node: messageDescriptorNode ?? node,
|
|
469
|
+
messageId: "enforceIdDescription"
|
|
470
|
+
});
|
|
471
|
+
else {
|
|
472
|
+
if (idWhitelistRegexps && id && idWhitelistRegexps.some((r) => r.test(id))) continue;
|
|
473
|
+
const correctId = interpolateName({ resourcePath: context.filename }, idInterpolationPattern, { content: description ? `${defaultMessage}#${description}` : defaultMessage });
|
|
474
|
+
if (id !== correctId) {
|
|
475
|
+
let messageId = "enforceIdMatching";
|
|
476
|
+
let messageData = {
|
|
477
|
+
idInterpolationPattern,
|
|
478
|
+
expected: correctId,
|
|
479
|
+
actual: id
|
|
488
480
|
};
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
fix(fixer) {
|
|
496
|
-
if (idPropNode) {
|
|
497
|
-
if (idPropNode.type === "JSXAttribute") return fixer.replaceText(idPropNode, `id="${correctId}"`);
|
|
498
|
-
return fixer.replaceText(idPropNode, `id: ${quote}${correctId}${quote}`);
|
|
499
|
-
}
|
|
500
|
-
if (messagePropNode) {
|
|
501
|
-
if (messagePropNode.type === "JSXAttribute") return fixer.insertTextAfter(messagePropNode, ` id="${correctId}"`);
|
|
502
|
-
return fixer.insertTextAfter(messagePropNode, `, id: ${quote}${correctId}${quote}`);
|
|
503
|
-
}
|
|
504
|
-
return null;
|
|
481
|
+
if (idWhitelistRegexps) {
|
|
482
|
+
messageId = "enforceIdMatchingAllowlisted";
|
|
483
|
+
messageData = {
|
|
484
|
+
...messageData,
|
|
485
|
+
idWhitelist: idWhitelistRegexps.map((r) => `"${r.toString()}"`).join(", ")
|
|
486
|
+
};
|
|
505
487
|
}
|
|
506
|
-
|
|
488
|
+
const quote = quoteStyle === "double" ? "\"" : "'";
|
|
489
|
+
context.report({
|
|
490
|
+
node: idPropNode ?? messageDescriptorNode ?? node,
|
|
491
|
+
messageId,
|
|
492
|
+
data: messageData,
|
|
493
|
+
fix(fixer) {
|
|
494
|
+
if (idPropNode) {
|
|
495
|
+
if (idPropNode.type === "JSXAttribute") return fixer.replaceText(idPropNode, `id="${correctId}"`);
|
|
496
|
+
return fixer.replaceText(idPropNode, `id: ${quote}${correctId}${quote}`);
|
|
497
|
+
}
|
|
498
|
+
if (messagePropNode) {
|
|
499
|
+
if (messagePropNode.type === "JSXAttribute") return fixer.insertTextAfter(messagePropNode, ` id="${correctId}"`);
|
|
500
|
+
return fixer.insertTextAfter(messagePropNode, `, id: ${quote}${correctId}${quote}`);
|
|
501
|
+
}
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
}
|
|
507
506
|
}
|
|
508
507
|
}
|
|
509
508
|
}
|
|
@@ -588,9 +587,7 @@ function collectPlaceholderNames(ast) {
|
|
|
588
587
|
placeholderNames.add(element.value);
|
|
589
588
|
for (const { value } of Object.values(element.options)) _traverse(value);
|
|
590
589
|
break;
|
|
591
|
-
default:
|
|
592
|
-
placeholderNames.add(element.value);
|
|
593
|
-
break;
|
|
590
|
+
default: placeholderNames.add(element.value);
|
|
594
591
|
}
|
|
595
592
|
}
|
|
596
593
|
}
|
|
@@ -857,9 +854,7 @@ function calculateComplexity(ast) {
|
|
|
857
854
|
case TYPE.tag:
|
|
858
855
|
complexity = _calculate(element.children, 0) * _calculate(ast, index + 1);
|
|
859
856
|
break;
|
|
860
|
-
default:
|
|
861
|
-
complexity = _calculate(ast, index + 1);
|
|
862
|
-
break;
|
|
857
|
+
default: complexity = _calculate(ast, index + 1);
|
|
863
858
|
}
|
|
864
859
|
complexityByNode.set(element, complexity);
|
|
865
860
|
return complexity;
|
|
@@ -3578,20 +3573,22 @@ function checkNode$9(context, node) {
|
|
|
3578
3573
|
if (emojiConfig?.versionAbove && isValidEmojiVersion(emojiConfig.versionAbove)) versionAbove = emojiConfig.versionAbove;
|
|
3579
3574
|
for (const [{ message: { defaultMessage }, messageNode }] of msgs) {
|
|
3580
3575
|
if (!defaultMessage || !messageNode) continue;
|
|
3581
|
-
if (hasEmoji(defaultMessage))
|
|
3582
|
-
|
|
3583
|
-
|
|
3576
|
+
if (hasEmoji(defaultMessage)) {
|
|
3577
|
+
if (versionAbove) {
|
|
3578
|
+
const filter = filterEmojis(versionAbove);
|
|
3579
|
+
for (const emoji of extractEmojis(defaultMessage)) if (!filter(emoji)) context.report({
|
|
3580
|
+
node: messageNode,
|
|
3581
|
+
messageId: "notAllowedAboveVersion",
|
|
3582
|
+
data: {
|
|
3583
|
+
versionAbove,
|
|
3584
|
+
emoji
|
|
3585
|
+
}
|
|
3586
|
+
});
|
|
3587
|
+
} else context.report({
|
|
3584
3588
|
node: messageNode,
|
|
3585
|
-
messageId: "
|
|
3586
|
-
data: {
|
|
3587
|
-
versionAbove,
|
|
3588
|
-
emoji
|
|
3589
|
-
}
|
|
3589
|
+
messageId: "notAllowed"
|
|
3590
3590
|
});
|
|
3591
|
-
}
|
|
3592
|
-
node: messageNode,
|
|
3593
|
-
messageId: "notAllowed"
|
|
3594
|
-
});
|
|
3591
|
+
}
|
|
3595
3592
|
}
|
|
3596
3593
|
}
|
|
3597
3594
|
const rule$12 = {
|
|
@@ -4030,9 +4027,7 @@ function trimMultiWhitespaces(message, ast) {
|
|
|
4030
4027
|
case TYPE.select:
|
|
4031
4028
|
for (const option of Object.values(element.options)) collectLiteralElements(option.value);
|
|
4032
4029
|
break;
|
|
4033
|
-
case TYPE.tag:
|
|
4034
|
-
collectLiteralElements(element.children);
|
|
4035
|
-
break;
|
|
4030
|
+
case TYPE.tag: collectLiteralElements(element.children);
|
|
4036
4031
|
}
|
|
4037
4032
|
};
|
|
4038
4033
|
collectLiteralElements(ast);
|
|
@@ -4290,10 +4285,7 @@ function verifyAst(context, messageNode, ast) {
|
|
|
4290
4285
|
case TYPE.select:
|
|
4291
4286
|
for (const { value } of Object.values(current.options)) _verifyAst(value);
|
|
4292
4287
|
break;
|
|
4293
|
-
case TYPE.tag:
|
|
4294
|
-
_verifyAst(current.children);
|
|
4295
|
-
break;
|
|
4296
|
-
default: break;
|
|
4288
|
+
case TYPE.tag: _verifyAst(current.children);
|
|
4297
4289
|
}
|
|
4298
4290
|
}
|
|
4299
4291
|
}
|
|
@@ -4311,10 +4303,7 @@ function verifyAst(context, messageNode, ast) {
|
|
|
4311
4303
|
case TYPE.tag:
|
|
4312
4304
|
_replacementArgumentWithPound(name, element.children);
|
|
4313
4305
|
break;
|
|
4314
|
-
case TYPE.select:
|
|
4315
|
-
for (const { value } of Object.values(element.options)) _replacementArgumentWithPound(name, value);
|
|
4316
|
-
break;
|
|
4317
|
-
default: break;
|
|
4306
|
+
case TYPE.select: for (const { value } of Object.values(element.options)) _replacementArgumentWithPound(name, value);
|
|
4318
4307
|
}
|
|
4319
4308
|
}
|
|
4320
4309
|
function _removeRangeAndPrependPluralClauses(rangeToRemoveStart, rangeToRemoveEnd, pluralElement, prependText) {
|
|
@@ -4450,18 +4439,27 @@ function getLastBoundaryElement(ast) {
|
|
|
4450
4439
|
if (last.type === TYPE.tag) return getLastBoundaryElement(last.children);
|
|
4451
4440
|
return last;
|
|
4452
4441
|
}
|
|
4453
|
-
function
|
|
4442
|
+
function isPlaceholder(element) {
|
|
4443
|
+
return element.type !== TYPE.literal && element.type !== TYPE.tag;
|
|
4444
|
+
}
|
|
4445
|
+
function findMessageIssues(ast) {
|
|
4454
4446
|
const issues = [];
|
|
4455
4447
|
const first = getFirstBoundaryElement(ast);
|
|
4456
4448
|
const last = getLastBoundaryElement(ast);
|
|
4457
4449
|
if (first === last && first?.type === TYPE.literal && ast.length === 1 && first.value.trim() === "") return issues;
|
|
4458
4450
|
if (first?.type === TYPE.literal && /^\s/.test(first.value)) issues.push("leadingWhitespace");
|
|
4459
4451
|
if (last?.type === TYPE.literal && /\s$/.test(last.value)) issues.push("trailingWhitespace");
|
|
4452
|
+
for (let index = 0; index < ast.length - 1; index++) {
|
|
4453
|
+
const current = ast[index];
|
|
4454
|
+
const next = ast[index + 1];
|
|
4455
|
+
if (current.type === TYPE.literal && /\p{L}$/u.test(current.value) && isPlaceholder(next) || isPlaceholder(current) && next.type === TYPE.literal && /^\p{L}/u.test(next.value)) issues.push("placeholderAdjacentToWord");
|
|
4456
|
+
}
|
|
4460
4457
|
for (const element of ast) switch (element.type) {
|
|
4461
4458
|
case TYPE.plural:
|
|
4462
4459
|
case TYPE.select:
|
|
4463
|
-
for (const option of Object.values(element.options)) issues.push(...
|
|
4460
|
+
for (const option of Object.values(element.options)) issues.push(...findMessageIssues(option.value));
|
|
4464
4461
|
break;
|
|
4462
|
+
case TYPE.tag: issues.push(...findMessageIssues(element.children));
|
|
4465
4463
|
}
|
|
4466
4464
|
return issues;
|
|
4467
4465
|
}
|
|
@@ -4480,7 +4478,7 @@ function checkNode(context, node) {
|
|
|
4480
4478
|
});
|
|
4481
4479
|
return;
|
|
4482
4480
|
}
|
|
4483
|
-
const issues =
|
|
4481
|
+
const issues = findMessageIssues(ast);
|
|
4484
4482
|
const seen = /* @__PURE__ */ new Set();
|
|
4485
4483
|
for (const issue of issues) {
|
|
4486
4484
|
if (seen.has(issue)) continue;
|
|
@@ -4503,7 +4501,8 @@ const rule = {
|
|
|
4503
4501
|
messages: {
|
|
4504
4502
|
...CORE_MESSAGES,
|
|
4505
4503
|
leadingWhitespace: "Messages should be full sentences — leading whitespace suggests string concatenation",
|
|
4506
|
-
trailingWhitespace: "Messages should be full sentences — trailing whitespace suggests string concatenation"
|
|
4504
|
+
trailingWhitespace: "Messages should be full sentences — trailing whitespace suggests string concatenation",
|
|
4505
|
+
placeholderAdjacentToWord: "Placeholders should not be joined directly to words or units — use ICU syntax or an Intl formatter"
|
|
4507
4506
|
},
|
|
4508
4507
|
schema: []
|
|
4509
4508
|
},
|
|
@@ -4538,7 +4537,7 @@ var package_exports = /* @__PURE__ */ __exportAll({
|
|
|
4538
4537
|
version: () => version$1
|
|
4539
4538
|
});
|
|
4540
4539
|
var name$1 = "eslint-plugin-formatjs";
|
|
4541
|
-
var version$1 = "6.
|
|
4540
|
+
var version$1 = "6.5.0";
|
|
4542
4541
|
var description = "ESLint plugin for formatjs";
|
|
4543
4542
|
var keywords = [
|
|
4544
4543
|
"eslint",
|
|
@@ -4564,7 +4563,7 @@ var dependencies = {
|
|
|
4564
4563
|
"@types/estree-jsx": "1",
|
|
4565
4564
|
"@types/picomatch": "4",
|
|
4566
4565
|
"@unicode/unicode-17.0.0": "1",
|
|
4567
|
-
"magic-string": "
|
|
4566
|
+
"magic-string": "1",
|
|
4568
4567
|
"picomatch": "2 || 3 || 4"
|
|
4569
4568
|
};
|
|
4570
4569
|
var peerDependencies = { "eslint": "9 || 10" };
|