eslint-plugin-formatjs 6.4.19 → 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 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) if (!defaultMessage) context.report({
466
- node: messageDescriptorNode ?? node,
467
- messageId: "enforceIdDefaultMessage"
468
- });
469
- else if (!description && descriptionNode) context.report({
470
- node: messageDescriptorNode ?? node,
471
- messageId: "enforceIdDescription"
472
- });
473
- else {
474
- if (idWhitelistRegexps && id && idWhitelistRegexps.some((r) => r.test(id))) continue;
475
- const correctId = interpolateName({ resourcePath: context.filename }, idInterpolationPattern, { content: description ? `${defaultMessage}#${description}` : defaultMessage });
476
- if (id !== correctId) {
477
- let messageId = "enforceIdMatching";
478
- let messageData = {
479
- idInterpolationPattern,
480
- expected: correctId,
481
- actual: id
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
- const quote = quoteStyle === "double" ? "\"" : "'";
491
- context.report({
492
- node: idPropNode ?? messageDescriptorNode ?? node,
493
- messageId,
494
- data: messageData,
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)) if (versionAbove) {
3582
- const filter = filterEmojis(versionAbove);
3583
- for (const emoji of extractEmojis(defaultMessage)) if (!filter(emoji)) context.report({
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: "notAllowedAboveVersion",
3586
- data: {
3587
- versionAbove,
3588
- emoji
3589
- }
3589
+ messageId: "notAllowed"
3590
3590
  });
3591
- } else context.report({
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 findWhitespaceIssues(ast) {
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(...findWhitespaceIssues(option.value));
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 = findWhitespaceIssues(ast);
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.4.19";
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": "0",
4566
+ "magic-string": "1",
4568
4567
  "picomatch": "2 || 3 || 4"
4569
4568
  };
4570
4569
  var peerDependencies = { "eslint": "9 || 10" };