eslint-plugin-formatjs 5.2.14 → 5.3.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
@@ -19,6 +19,7 @@ const no_offset_1 = require("./rules/no-offset");
19
19
  const no_useless_message_1 = require("./rules/no-useless-message");
20
20
  const prefer_formatted_message_1 = require("./rules/prefer-formatted-message");
21
21
  const prefer_pound_in_plural_1 = require("./rules/prefer-pound-in-plural");
22
+ const no_literal_string_in_object_1 = require("./rules/no-literal-string-in-object");
22
23
  const package_json_1 = require("./package.json");
23
24
  // All rules
24
25
  const rules = {
@@ -60,6 +61,8 @@ const rules = {
60
61
  [prefer_pound_in_plural_1.name]: prefer_pound_in_plural_1.rule,
61
62
  // @ts-expect-error
62
63
  [no_missing_icu_plural_one_placeholders_1.name]: no_missing_icu_plural_one_placeholders_1.rule,
64
+ // @ts-expect-error
65
+ [no_literal_string_in_object_1.name]: no_literal_string_in_object_1.rule,
63
66
  };
64
67
  // Base plugin
65
68
  const plugin = {
@@ -17,6 +17,7 @@ import { rule as noOffset, name as noOffsetName } from './rules/no-offset';
17
17
  import { rule as noUselessMessage, name as noUselessMessageName, } from './rules/no-useless-message';
18
18
  import { rule as preferFormattedMessage, name as preferFormattedMessageName, } from './rules/prefer-formatted-message';
19
19
  import { rule as preferPoundInPlural, name as preferPoundInPluralName, } from './rules/prefer-pound-in-plural';
20
+ import { rule as noLiteralStringInObject, name as noLiteralStringInObjectName, } from './rules/no-literal-string-in-object';
20
21
  import { name, version } from './package.json';
21
22
  // All rules
22
23
  const rules = {
@@ -58,6 +59,8 @@ const rules = {
58
59
  [preferPoundInPluralName]: preferPoundInPlural,
59
60
  // @ts-expect-error
60
61
  [noMissingIcuPluralOnePlaceholdersName]: noMissingIcuPluralOnePlaceholders,
62
+ // @ts-expect-error
63
+ [noLiteralStringInObjectName]: noLiteralStringInObject,
61
64
  };
62
65
  // Base plugin
63
66
  const plugin = {
@@ -1,36 +1,31 @@
1
1
  {
2
2
  "name": "eslint-plugin-formatjs",
3
- "version": "5.2.14",
4
3
  "description": "ESLint plugin for formatjs",
5
- "main": "index.js",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+ssh://git@github.com/formatjs/formatjs.git"
9
- },
10
- "keywords": [
11
- "eslint",
12
- "eslintplugin",
13
- "i18n",
14
- "formatjs"
15
- ],
16
- "author": "Long Ho <holevietlong@gmail.com>",
4
+ "version": "5.3.0",
17
5
  "license": "MIT",
18
- "bugs": {
19
- "url": "https://github.com/formatjs/formatjs/issues"
20
- },
21
- "homepage": "https://github.com/formatjs/formatjs#readme",
6
+ "author": "Long Ho <holevietlong@gmail.com>",
22
7
  "dependencies": {
23
8
  "@formatjs/icu-messageformat-parser": "workspace:*",
24
9
  "@formatjs/ts-transformer": "workspace:*",
25
- "@types/eslint": "9",
26
- "@types/picomatch": "3",
27
- "@typescript-eslint/utils": "8.20.0",
10
+ "@types/eslint": "^9.6.1",
11
+ "@types/picomatch": "^3",
12
+ "@typescript-eslint/utils": "^8.27.0",
28
13
  "magic-string": "^0.30.0",
29
14
  "picomatch": "2 || 3 || 4",
30
- "tslib": "2",
15
+ "tslib": "^2.8.0",
31
16
  "unicode-emoji-utils": "^1.2.0"
32
17
  },
33
18
  "peerDependencies": {
34
- "eslint": "9"
35
- }
19
+ "eslint": "^9.23.0"
20
+ },
21
+ "bugs": "https://github.com/formatjs/formatjs/issues",
22
+ "homepage": "https://github.com/formatjs/formatjs#readme",
23
+ "keywords": [
24
+ "eslint",
25
+ "eslintplugin",
26
+ "formatjs",
27
+ "i18n"
28
+ ],
29
+ "main": "index.js",
30
+ "repository": "formatjs/formatjs.git"
36
31
  }
@@ -0,0 +1,9 @@
1
+ import { RuleModule } from '@typescript-eslint/utils/ts-eslint';
2
+ type MessageIds = 'untranslatedProperty';
3
+ type PropertyConfig = {
4
+ include: string[];
5
+ };
6
+ type Options = [PropertyConfig?];
7
+ export declare const name = "no-literal-string-in-object";
8
+ export declare const rule: RuleModule<MessageIds, Options>;
9
+ export {};
@@ -0,0 +1,90 @@
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
+ import { getParserServices } from '../context-compat';
3
+ export const name = 'no-literal-string-in-object';
4
+ export const rule = {
5
+ meta: {
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Enforce translation of specific object properties',
9
+ url: 'https://formatjs.github.io/docs/tooling/linter#no-literal-string-in-object',
10
+ },
11
+ schema: [
12
+ {
13
+ type: 'object',
14
+ properties: {
15
+ include: {
16
+ type: 'array',
17
+ items: { type: 'string' },
18
+ default: ['label'],
19
+ },
20
+ },
21
+ additionalProperties: false,
22
+ },
23
+ ],
24
+ messages: {
25
+ untranslatedProperty: 'Object property: `{{propertyKey}}` might contain an untranslated literal string',
26
+ },
27
+ },
28
+ defaultOptions: [],
29
+ create(context) {
30
+ const propertyVisitor = (node) => {
31
+ checkProperty(context, node);
32
+ };
33
+ const parserServices = getParserServices(context);
34
+ //@ts-expect-error defineTemplateBodyVisitor exists in Vue parser
35
+ if (parserServices?.defineTemplateBodyVisitor) {
36
+ //@ts-expect-error
37
+ return parserServices.defineTemplateBodyVisitor({
38
+ Property: propertyVisitor,
39
+ }, {
40
+ Property: propertyVisitor,
41
+ });
42
+ }
43
+ return {
44
+ Property: propertyVisitor,
45
+ };
46
+ },
47
+ };
48
+ function checkProperty(context, node) {
49
+ const config = {
50
+ include: ['label'],
51
+ ...(context.options[0] || {}),
52
+ };
53
+ const propertyKey = node.key.type === TSESTree.AST_NODE_TYPES.Identifier
54
+ ? node.key.name
55
+ : node.key.type === TSESTree.AST_NODE_TYPES.Literal &&
56
+ typeof node.key.value === 'string'
57
+ ? node.key.value
58
+ : null;
59
+ if (!propertyKey || !config.include.includes(propertyKey)) {
60
+ return;
61
+ }
62
+ checkPropertyValue(context, node.value, propertyKey);
63
+ }
64
+ function checkPropertyValue(context, node, propertyKey) {
65
+ if ((node.type === 'Literal' &&
66
+ typeof node.value === 'string' &&
67
+ node.value.length > 0) ||
68
+ (node.type === 'TemplateLiteral' &&
69
+ (node.quasis.length > 1 || node.quasis[0].value.raw.length > 0))) {
70
+ context.report({
71
+ node: node,
72
+ messageId: 'untranslatedProperty',
73
+ data: {
74
+ propertyKey: propertyKey,
75
+ },
76
+ });
77
+ }
78
+ else if (node.type === 'BinaryExpression' && node.operator === '+') {
79
+ checkPropertyValue(context, node.left, propertyKey);
80
+ checkPropertyValue(context, node.right, propertyKey);
81
+ }
82
+ else if (node.type === 'ConditionalExpression') {
83
+ checkPropertyValue(context, node.consequent, propertyKey);
84
+ checkPropertyValue(context, node.alternate, propertyKey);
85
+ }
86
+ else if (node.type === 'LogicalExpression') {
87
+ checkPropertyValue(context, node.left, propertyKey);
88
+ checkPropertyValue(context, node.right, propertyKey);
89
+ }
90
+ }
package/package.json CHANGED
@@ -1,36 +1,31 @@
1
1
  {
2
2
  "name": "eslint-plugin-formatjs",
3
- "version": "5.2.14",
4
3
  "description": "ESLint plugin for formatjs",
5
- "main": "index.js",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+ssh://git@github.com/formatjs/formatjs.git"
9
- },
10
- "keywords": [
11
- "eslint",
12
- "eslintplugin",
13
- "i18n",
14
- "formatjs"
15
- ],
16
- "author": "Long Ho <holevietlong@gmail.com>",
4
+ "version": "5.3.0",
17
5
  "license": "MIT",
18
- "bugs": {
19
- "url": "https://github.com/formatjs/formatjs/issues"
20
- },
21
- "homepage": "https://github.com/formatjs/formatjs#readme",
6
+ "author": "Long Ho <holevietlong@gmail.com>",
22
7
  "dependencies": {
23
- "@types/eslint": "9",
24
- "@types/picomatch": "3",
25
- "@typescript-eslint/utils": "8.20.0",
8
+ "@types/eslint": "^9.6.1",
9
+ "@types/picomatch": "^3",
10
+ "@typescript-eslint/utils": "^8.27.0",
26
11
  "magic-string": "^0.30.0",
27
12
  "picomatch": "2 || 3 || 4",
28
- "tslib": "2",
13
+ "tslib": "^2.8.0",
29
14
  "unicode-emoji-utils": "^1.2.0",
30
- "@formatjs/icu-messageformat-parser": "2.11.1",
31
- "@formatjs/ts-transformer": "3.13.32"
15
+ "@formatjs/icu-messageformat-parser": "2.11.2",
16
+ "@formatjs/ts-transformer": "3.13.33"
32
17
  },
33
18
  "peerDependencies": {
34
- "eslint": "9"
35
- }
19
+ "eslint": "^9.23.0"
20
+ },
21
+ "bugs": "https://github.com/formatjs/formatjs/issues",
22
+ "homepage": "https://github.com/formatjs/formatjs#readme",
23
+ "keywords": [
24
+ "eslint",
25
+ "eslintplugin",
26
+ "formatjs",
27
+ "i18n"
28
+ ],
29
+ "main": "index.js",
30
+ "repository": "formatjs/formatjs.git"
36
31
  }
@@ -0,0 +1,9 @@
1
+ import { RuleModule } from '@typescript-eslint/utils/ts-eslint';
2
+ type MessageIds = 'untranslatedProperty';
3
+ type PropertyConfig = {
4
+ include: string[];
5
+ };
6
+ type Options = [PropertyConfig?];
7
+ export declare const name = "no-literal-string-in-object";
8
+ export declare const rule: RuleModule<MessageIds, Options>;
9
+ export {};
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rule = exports.name = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const context_compat_1 = require("../context-compat");
6
+ exports.name = 'no-literal-string-in-object';
7
+ exports.rule = {
8
+ meta: {
9
+ type: 'problem',
10
+ docs: {
11
+ description: 'Enforce translation of specific object properties',
12
+ url: 'https://formatjs.github.io/docs/tooling/linter#no-literal-string-in-object',
13
+ },
14
+ schema: [
15
+ {
16
+ type: 'object',
17
+ properties: {
18
+ include: {
19
+ type: 'array',
20
+ items: { type: 'string' },
21
+ default: ['label'],
22
+ },
23
+ },
24
+ additionalProperties: false,
25
+ },
26
+ ],
27
+ messages: {
28
+ untranslatedProperty: 'Object property: `{{propertyKey}}` might contain an untranslated literal string',
29
+ },
30
+ },
31
+ defaultOptions: [],
32
+ create(context) {
33
+ const propertyVisitor = (node) => {
34
+ checkProperty(context, node);
35
+ };
36
+ const parserServices = (0, context_compat_1.getParserServices)(context);
37
+ //@ts-expect-error defineTemplateBodyVisitor exists in Vue parser
38
+ if (parserServices?.defineTemplateBodyVisitor) {
39
+ //@ts-expect-error
40
+ return parserServices.defineTemplateBodyVisitor({
41
+ Property: propertyVisitor,
42
+ }, {
43
+ Property: propertyVisitor,
44
+ });
45
+ }
46
+ return {
47
+ Property: propertyVisitor,
48
+ };
49
+ },
50
+ };
51
+ function checkProperty(context, node) {
52
+ const config = {
53
+ include: ['label'],
54
+ ...(context.options[0] || {}),
55
+ };
56
+ const propertyKey = node.key.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier
57
+ ? node.key.name
58
+ : node.key.type === utils_1.TSESTree.AST_NODE_TYPES.Literal &&
59
+ typeof node.key.value === 'string'
60
+ ? node.key.value
61
+ : null;
62
+ if (!propertyKey || !config.include.includes(propertyKey)) {
63
+ return;
64
+ }
65
+ checkPropertyValue(context, node.value, propertyKey);
66
+ }
67
+ function checkPropertyValue(context, node, propertyKey) {
68
+ if ((node.type === 'Literal' &&
69
+ typeof node.value === 'string' &&
70
+ node.value.length > 0) ||
71
+ (node.type === 'TemplateLiteral' &&
72
+ (node.quasis.length > 1 || node.quasis[0].value.raw.length > 0))) {
73
+ context.report({
74
+ node: node,
75
+ messageId: 'untranslatedProperty',
76
+ data: {
77
+ propertyKey: propertyKey,
78
+ },
79
+ });
80
+ }
81
+ else if (node.type === 'BinaryExpression' && node.operator === '+') {
82
+ checkPropertyValue(context, node.left, propertyKey);
83
+ checkPropertyValue(context, node.right, propertyKey);
84
+ }
85
+ else if (node.type === 'ConditionalExpression') {
86
+ checkPropertyValue(context, node.consequent, propertyKey);
87
+ checkPropertyValue(context, node.alternate, propertyKey);
88
+ }
89
+ else if (node.type === 'LogicalExpression') {
90
+ checkPropertyValue(context, node.left, propertyKey);
91
+ checkPropertyValue(context, node.right, propertyKey);
92
+ }
93
+ }