eslint-plugin-formatjs 4.7.2 → 4.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-formatjs",
3
- "version": "4.7.2",
3
+ "version": "4.8.0",
4
4
  "description": "ESLint plugin for formatjs",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -1 +1 @@
1
- {"version":3,"file":"enforce-placeholders.d.ts","sourceRoot":"","sources":["enforce-placeholders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,QAAQ,CAAA;AAiH3B,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UA6ChB,CAAA;AAED,eAAe,IAAI,CAAA"}
1
+ {"version":3,"file":"enforce-placeholders.d.ts","sourceRoot":"","sources":["enforce-placeholders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,QAAQ,CAAA;AAyH3B,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UA4ChB,CAAA;AAED,eAAe,IAAI,CAAA"}
@@ -2,52 +2,32 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const util_1 = require("../util");
4
4
  const icu_messageformat_parser_1 = require("@formatjs/icu-messageformat-parser");
5
- class PlaceholderEnforcement extends Error {
6
- constructor(message) {
7
- super();
8
- this.message = message;
9
- }
10
- }
11
- function keyExistsInExpression(key, values) {
12
- if (!values) {
13
- return false;
14
- }
15
- if (values.type !== 'ObjectExpression') {
16
- return true; // True bc we cannot evaluate this
17
- }
18
- if (values.properties.find(prop => prop.type === 'SpreadElement')) {
19
- return true; // True bc there's a spread element
20
- }
21
- return !!values.properties.find(prop => {
22
- if (prop.type !== 'Property') {
23
- return false;
24
- }
25
- switch (prop.key.type) {
26
- case 'Identifier':
27
- return prop.key.name === key;
28
- case 'Literal':
29
- return prop.key.value === key;
30
- }
31
- return false;
32
- });
33
- }
34
- function verifyAst(ast, values, ignoreList) {
35
- for (const el of ast) {
36
- if ((0, icu_messageformat_parser_1.isLiteralElement)(el) || (0, icu_messageformat_parser_1.isPoundElement)(el)) {
37
- continue;
38
- }
39
- const key = el.value;
40
- if (!ignoreList.has(key) && !keyExistsInExpression(key, values)) {
41
- throw new PlaceholderEnforcement(`Missing value for placeholder "${el.value}"`);
42
- }
43
- if ((0, icu_messageformat_parser_1.isPluralElement)(el) || (0, icu_messageformat_parser_1.isSelectElement)(el)) {
44
- for (const selector of Object.keys(el.options)) {
45
- verifyAst(el.options[selector].value, values, ignoreList);
5
+ function collectPlaceholderNames(ast) {
6
+ const placeholderNames = new Set();
7
+ _traverse(ast);
8
+ return placeholderNames;
9
+ function _traverse(ast) {
10
+ for (const element of ast) {
11
+ switch (element.type) {
12
+ case icu_messageformat_parser_1.TYPE.literal:
13
+ case icu_messageformat_parser_1.TYPE.pound:
14
+ break;
15
+ case icu_messageformat_parser_1.TYPE.tag:
16
+ placeholderNames.add(element.value);
17
+ _traverse(element.children);
18
+ break;
19
+ case icu_messageformat_parser_1.TYPE.plural:
20
+ case icu_messageformat_parser_1.TYPE.select:
21
+ placeholderNames.add(element.value);
22
+ for (const { value } of Object.values(element.options)) {
23
+ _traverse(value);
24
+ }
25
+ break;
26
+ default:
27
+ placeholderNames.add(element.value);
28
+ break;
46
29
  }
47
30
  }
48
- if ((0, icu_messageformat_parser_1.isTagElement)(el)) {
49
- verifyAst(el.children, values, ignoreList);
50
- }
51
31
  }
52
32
  }
53
33
  function checkNode(context, node) {
@@ -62,17 +42,49 @@ function checkNode(context, node) {
62
42
  if (!defaultMessage || !messageNode) {
63
43
  continue;
64
44
  }
65
- try {
66
- verifyAst((0, icu_messageformat_parser_1.parse)(defaultMessage, {
67
- ignoreTag: settings.ignoreTag,
68
- }), values, ignoreList);
45
+ if (values && values.type !== 'ObjectExpression') {
46
+ // cannot evaluate this
47
+ continue;
48
+ }
49
+ if (values?.properties.find(prop => prop.type === 'SpreadElement')) {
50
+ // cannot evaluate the spread element
51
+ continue;
52
+ }
53
+ const literalElementByLiteralKey = new Map();
54
+ if (values) {
55
+ for (const prop of values.properties) {
56
+ if ((prop.type === 'MethodDefinition' || prop.type === 'Property') &&
57
+ !prop.computed &&
58
+ prop.key.type !== 'PrivateIdentifier') {
59
+ const name = prop.key.type === 'Identifier'
60
+ ? prop.key.name
61
+ : String(prop.key.value);
62
+ literalElementByLiteralKey.set(name, prop);
63
+ }
64
+ }
69
65
  }
70
- catch (e) {
66
+ const ast = (0, icu_messageformat_parser_1.parse)(defaultMessage, { ignoreTag: settings.ignoreTag });
67
+ const placeholderNames = collectPlaceholderNames(ast);
68
+ const missingPlaceholders = [];
69
+ placeholderNames.forEach(name => {
70
+ if (!ignoreList.has(name) && !literalElementByLiteralKey.has(name)) {
71
+ missingPlaceholders.push(name);
72
+ }
73
+ });
74
+ if (missingPlaceholders.length > 0) {
71
75
  context.report({
72
76
  node: messageNode,
73
- message: e instanceof Error ? e.message : String(e),
77
+ message: `Missing value(s) for the following placeholder(s): ${missingPlaceholders.join(', ')}.`,
74
78
  });
75
79
  }
80
+ literalElementByLiteralKey.forEach((element, key) => {
81
+ if (!ignoreList.has(key) && !placeholderNames.has(key)) {
82
+ context.report({
83
+ node: element,
84
+ message: 'Value not used by the message.',
85
+ });
86
+ }
87
+ });
76
88
  }
77
89
  }
78
90
  const rule = {
@@ -84,7 +96,6 @@ const rule = {
84
96
  recommended: true,
85
97
  url: 'https://formatjs.io/docs/tooling/linter#enforce-placeholders',
86
98
  },
87
- fixable: 'code',
88
99
  schema: [
89
100
  {
90
101
  type: 'object',
@@ -1 +1 @@
1
- {"version":3,"file":"no-complex-selectors.d.ts","sourceRoot":"","sources":["no-complex-selectors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,QAAQ,CAAA;AAyE3B,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAiDhB,CAAA;AAED,eAAe,IAAI,CAAA"}
1
+ {"version":3,"file":"no-complex-selectors.d.ts","sourceRoot":"","sources":["no-complex-selectors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,QAAQ,CAAA;AAiF3B,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAiDhB,CAAA;AAED,eAAe,IAAI,CAAA"}
@@ -41,8 +41,17 @@ function checkNode(context, node) {
41
41
  });
42
42
  return;
43
43
  }
44
- const hoistedAst = (0, manipulator_1.hoistSelectors)(ast);
45
- const complexity = calculateComplexity(hoistedAst);
44
+ let complexity = 0;
45
+ try {
46
+ const hoistedAst = (0, manipulator_1.hoistSelectors)(ast);
47
+ complexity = calculateComplexity(hoistedAst);
48
+ }
49
+ catch (e) {
50
+ context.report({
51
+ node: messageNode,
52
+ message: e instanceof Error ? e.message : e,
53
+ });
54
+ }
46
55
  if (complexity > config.limit) {
47
56
  context.report({
48
57
  node: messageNode,