eslint-plugin-lingui-typescript 1.0.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.
Files changed (74) hide show
  1. package/README.md +111 -0
  2. package/dist/index.d.ts +24 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +50 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/rules/consistent-plural-format.d.ts +5 -0
  7. package/dist/rules/consistent-plural-format.d.ts.map +1 -0
  8. package/dist/rules/consistent-plural-format.js +67 -0
  9. package/dist/rules/consistent-plural-format.js.map +1 -0
  10. package/dist/rules/consistent-plural-format.test.d.ts +2 -0
  11. package/dist/rules/consistent-plural-format.test.d.ts.map +1 -0
  12. package/dist/rules/consistent-plural-format.test.js +74 -0
  13. package/dist/rules/consistent-plural-format.test.js.map +1 -0
  14. package/dist/rules/no-complex-expressions-in-message.d.ts +7 -0
  15. package/dist/rules/no-complex-expressions-in-message.d.ts.map +1 -0
  16. package/dist/rules/no-complex-expressions-in-message.js +184 -0
  17. package/dist/rules/no-complex-expressions-in-message.js.map +1 -0
  18. package/dist/rules/no-complex-expressions-in-message.test.d.ts +2 -0
  19. package/dist/rules/no-complex-expressions-in-message.test.d.ts.map +1 -0
  20. package/dist/rules/no-complex-expressions-in-message.test.js +122 -0
  21. package/dist/rules/no-complex-expressions-in-message.test.js.map +1 -0
  22. package/dist/rules/no-nested-macros.d.ts +6 -0
  23. package/dist/rules/no-nested-macros.d.ts.map +1 -0
  24. package/dist/rules/no-nested-macros.js +109 -0
  25. package/dist/rules/no-nested-macros.js.map +1 -0
  26. package/dist/rules/no-nested-macros.test.d.ts +2 -0
  27. package/dist/rules/no-nested-macros.test.d.ts.map +1 -0
  28. package/dist/rules/no-nested-macros.test.js +93 -0
  29. package/dist/rules/no-nested-macros.test.js.map +1 -0
  30. package/dist/rules/no-single-tag-message.d.ts +2 -0
  31. package/dist/rules/no-single-tag-message.d.ts.map +1 -0
  32. package/dist/rules/no-single-tag-message.js +49 -0
  33. package/dist/rules/no-single-tag-message.js.map +1 -0
  34. package/dist/rules/no-single-tag-message.test.d.ts +2 -0
  35. package/dist/rules/no-single-tag-message.test.d.ts.map +1 -0
  36. package/dist/rules/no-single-tag-message.test.js +72 -0
  37. package/dist/rules/no-single-tag-message.test.js.map +1 -0
  38. package/dist/rules/no-single-variable-message.d.ts +2 -0
  39. package/dist/rules/no-single-variable-message.d.ts.map +1 -0
  40. package/dist/rules/no-single-variable-message.js +74 -0
  41. package/dist/rules/no-single-variable-message.js.map +1 -0
  42. package/dist/rules/no-single-variable-message.test.d.ts +2 -0
  43. package/dist/rules/no-single-variable-message.test.d.ts.map +1 -0
  44. package/dist/rules/no-single-variable-message.test.js +74 -0
  45. package/dist/rules/no-single-variable-message.test.js.map +1 -0
  46. package/dist/rules/no-unlocalized-strings.d.ts +9 -0
  47. package/dist/rules/no-unlocalized-strings.d.ts.map +1 -0
  48. package/dist/rules/no-unlocalized-strings.js +374 -0
  49. package/dist/rules/no-unlocalized-strings.js.map +1 -0
  50. package/dist/rules/no-unlocalized-strings.test.d.ts +2 -0
  51. package/dist/rules/no-unlocalized-strings.test.d.ts.map +1 -0
  52. package/dist/rules/no-unlocalized-strings.test.js +200 -0
  53. package/dist/rules/no-unlocalized-strings.test.js.map +1 -0
  54. package/dist/rules/text-restrictions.d.ts +8 -0
  55. package/dist/rules/text-restrictions.d.ts.map +1 -0
  56. package/dist/rules/text-restrictions.js +103 -0
  57. package/dist/rules/text-restrictions.js.map +1 -0
  58. package/dist/rules/text-restrictions.test.d.ts +2 -0
  59. package/dist/rules/text-restrictions.test.d.ts.map +1 -0
  60. package/dist/rules/text-restrictions.test.js +107 -0
  61. package/dist/rules/text-restrictions.test.js.map +1 -0
  62. package/dist/rules/valid-t-call-location.d.ts +5 -0
  63. package/dist/rules/valid-t-call-location.d.ts.map +1 -0
  64. package/dist/rules/valid-t-call-location.js +69 -0
  65. package/dist/rules/valid-t-call-location.js.map +1 -0
  66. package/dist/rules/valid-t-call-location.test.d.ts +2 -0
  67. package/dist/rules/valid-t-call-location.test.d.ts.map +1 -0
  68. package/dist/rules/valid-t-call-location.test.js +104 -0
  69. package/dist/rules/valid-t-call-location.test.js.map +1 -0
  70. package/dist/utils/create-rule.d.ts +6 -0
  71. package/dist/utils/create-rule.d.ts.map +1 -0
  72. package/dist/utils/create-rule.js +6 -0
  73. package/dist/utils/create-rule.js.map +1 -0
  74. package/package.json +89 -0
package/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # eslint-plugin-lingui-typescript
2
+
3
+ [![npm version](https://img.shields.io/npm/v/eslint-plugin-lingui-typescript.svg)](https://www.npmjs.com/package/eslint-plugin-lingui-typescript)
4
+ [![CI](https://github.com/sebastian-software/eslint-plugin-lingui-typescript/actions/workflows/ci.yml/badge.svg)](https://github.com/sebastian-software/eslint-plugin-lingui-typescript/actions/workflows/ci.yml)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ ESLint plugin for [Lingui](https://lingui.dev/) with TypeScript type-aware rules.
8
+
9
+ ## Why TypeScript?
10
+
11
+ Traditional i18n linters rely on heuristics and manual whitelists to distinguish user-visible text from technical strings. This leads to false positives and constant configuration tweaking.
12
+
13
+ This plugin leverages TypeScript's type system to **automatically** recognize technical strings:
14
+
15
+ ```ts
16
+ // ✅ Automatically ignored - TypeScript knows these are technical
17
+ document.createElement("div") // keyof HTMLElementTagNameMap
18
+ element.addEventListener("click", handler) // keyof GlobalEventHandlersEventMap
19
+ fetch(url, { mode: "cors" }) // RequestMode
20
+ date.toLocaleDateString("de-DE", { weekday: "long" }) // Intl.DateTimeFormatOptions
21
+
22
+ type Status = "idle" | "loading" | "error"
23
+ const status: Status = "loading" // String literal union
24
+
25
+ // ❌ Reported - actual user-visible text
26
+ const message = "Welcome to our app"
27
+ <button>Save changes</button>
28
+ ```
29
+
30
+ **No configuration needed** for DOM APIs, Intl methods, or your own string literal union types. TypeScript already knows!
31
+
32
+ ## Features
33
+
34
+ - 🔍 Detects incorrect usage of Lingui translation macros
35
+ - 📝 Enforces simple, safe expressions inside translated messages
36
+ - 🎯 Detects missing localization of user-visible text
37
+ - 🧠 Zero-config recognition of technical strings via TypeScript types
38
+
39
+ ## Requirements
40
+
41
+ - Node.js ≥ 24
42
+ - ESLint ≥ 9
43
+ - TypeScript ≥ 5
44
+ - `typescript-eslint` with type-aware linting enabled
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ npm install --save-dev eslint-plugin-lingui-typescript
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ This plugin requires TypeScript and type-aware linting. Configure your `eslint.config.ts`:
55
+
56
+ ```ts
57
+ import eslint from "@eslint/js"
58
+ import tseslint from "typescript-eslint"
59
+ import linguiPlugin from "eslint-plugin-lingui-typescript"
60
+
61
+ export default [
62
+ eslint.configs.recommended,
63
+ ...tseslint.configs.strictTypeChecked,
64
+ linguiPlugin.configs["flat/recommended"],
65
+ {
66
+ languageOptions: {
67
+ parserOptions: {
68
+ projectService: true,
69
+ tsconfigRootDir: import.meta.dirname
70
+ }
71
+ }
72
+ }
73
+ ]
74
+ ```
75
+
76
+ Or configure rules manually:
77
+
78
+ ```ts
79
+ {
80
+ plugins: {
81
+ "lingui-ts": linguiPlugin
82
+ },
83
+ rules: {
84
+ "lingui-ts/no-unlocalized-strings": "error",
85
+ "lingui-ts/no-single-variable-message": "error"
86
+ }
87
+ }
88
+ ```
89
+
90
+ ## Rules
91
+
92
+ | Rule | Description | Recommended |
93
+ |------|-------------|:-----------:|
94
+ | [no-unlocalized-strings](docs/rules/no-unlocalized-strings.md) | Detects user-visible strings not wrapped in Lingui macros. Uses TypeScript types to automatically ignore technical strings like string literal unions, DOM APIs, Intl methods, and discriminated union fields. | ✅ |
95
+ | [no-single-variable-message](docs/rules/no-single-variable-message.md) | Disallows messages that consist only of a single variable without surrounding text. Such messages provide no context for translators. | ✅ |
96
+ | [no-single-tag-message](docs/rules/no-single-tag-message.md) | Disallows `<Trans>` components that contain only a single JSX element without text. The wrapped element should be translated directly instead. | ✅ |
97
+ | [no-nested-macros](docs/rules/no-nested-macros.md) | Prevents nesting Lingui macros inside each other (e.g., `t` inside `<Trans>`). Nested macros create invalid message catalogs and confuse translators. | ✅ |
98
+ | [no-complex-expressions-in-message](docs/rules/no-complex-expressions-in-message.md) | Restricts embedded expressions in messages to simple identifiers and member access. Complex expressions like function calls or ternaries should be extracted to variables. | ✅ |
99
+ | [valid-t-call-location](docs/rules/valid-t-call-location.md) | Ensures `t` macro calls are inside functions, not at module scope. Module-level calls execute before i18n is initialized and won't update on locale change. | ✅ |
100
+ | [consistent-plural-format](docs/rules/consistent-plural-format.md) | Validates `<Plural>` component usage by ensuring required plural keys (`one`, `other`) are present. Helps maintain consistent pluralization across the codebase. | ✅ |
101
+ | [text-restrictions](docs/rules/text-restrictions.md) | Enforces project-specific text restrictions like disallowed patterns or minimum length. Requires configuration to be useful. | — |
102
+
103
+ ## Related Projects
104
+
105
+ - [Lingui](https://lingui.dev/) – The excellent i18n library this plugin is built for. Provides powerful macros like `t`, `<Trans>`, and `plural` for seamless internationalization.
106
+ - [eslint-plugin-lingui](https://github.com/lingui/eslint-plugin-lingui) – The official Lingui ESLint plugin. Great for JavaScript projects; this plugin extends the concept with TypeScript type-awareness.
107
+ - [typescript-eslint](https://typescript-eslint.io/) – The foundation that makes type-aware linting possible. This plugin builds on their excellent tooling.
108
+
109
+ ## License
110
+
111
+ [MIT](LICENSE)
@@ -0,0 +1,24 @@
1
+ /**
2
+ * ESLint Plugin for Lingui with TypeScript type-aware rules
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ declare const plugin: {
7
+ meta: {
8
+ name: string;
9
+ version: string;
10
+ };
11
+ rules: {
12
+ "consistent-plural-format": import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingPluralKey", [import("./rules/consistent-plural-format.js").Options], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
13
+ "no-complex-expressions-in-message": import("@typescript-eslint/utils/ts-eslint").RuleModule<"complexExpression", [import("./rules/no-complex-expressions-in-message.js").Options], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
14
+ "no-nested-macros": import("@typescript-eslint/utils/ts-eslint").RuleModule<"nestedMacro", [import("./rules/no-nested-macros.js").Options], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
15
+ "no-single-tag-message": import("@typescript-eslint/utils/ts-eslint").RuleModule<"singleTag", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
16
+ "no-single-variable-message": import("@typescript-eslint/utils/ts-eslint").RuleModule<"singleVariable", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
17
+ "no-unlocalized-strings": import("@typescript-eslint/utils/ts-eslint").RuleModule<"unlocalizedString", [import("./rules/no-unlocalized-strings.js").Options], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
18
+ "text-restrictions": import("@typescript-eslint/utils/ts-eslint").RuleModule<"forbiddenPattern" | "tooShort", [import("./rules/text-restrictions.js").Options], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
19
+ "valid-t-call-location": import("@typescript-eslint/utils/ts-eslint").RuleModule<"topLevelNotAllowed", [import("./rules/valid-t-call-location.js").Options], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
20
+ };
21
+ configs: Record<string, unknown>;
22
+ };
23
+ export default plugin;
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;aAeK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CACvC,CAAA;AAqBD,eAAe,MAAM,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * ESLint Plugin for Lingui with TypeScript type-aware rules
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { consistentPluralFormat } from "./rules/consistent-plural-format.js";
7
+ import { noComplexExpressionsInMessage } from "./rules/no-complex-expressions-in-message.js";
8
+ import { noNestedMacros } from "./rules/no-nested-macros.js";
9
+ import { noSingleTagMessage } from "./rules/no-single-tag-message.js";
10
+ import { noSingleVariableMessage } from "./rules/no-single-variable-message.js";
11
+ import { noUnlocalizedStrings } from "./rules/no-unlocalized-strings.js";
12
+ import { textRestrictions } from "./rules/text-restrictions.js";
13
+ import { validTCallLocation } from "./rules/valid-t-call-location.js";
14
+ const plugin = {
15
+ meta: {
16
+ name: "eslint-plugin-lingui-typescript",
17
+ version: "1.0.0"
18
+ },
19
+ rules: {
20
+ "consistent-plural-format": consistentPluralFormat,
21
+ "no-complex-expressions-in-message": noComplexExpressionsInMessage,
22
+ "no-nested-macros": noNestedMacros,
23
+ "no-single-tag-message": noSingleTagMessage,
24
+ "no-single-variable-message": noSingleVariableMessage,
25
+ "no-unlocalized-strings": noUnlocalizedStrings,
26
+ "text-restrictions": textRestrictions,
27
+ "valid-t-call-location": validTCallLocation
28
+ },
29
+ configs: {}
30
+ };
31
+ // Add flat config with self-reference
32
+ plugin.configs = {
33
+ "flat/recommended": {
34
+ plugins: {
35
+ "lingui-ts": plugin
36
+ },
37
+ rules: {
38
+ "lingui-ts/consistent-plural-format": "error",
39
+ "lingui-ts/no-complex-expressions-in-message": "error",
40
+ "lingui-ts/no-nested-macros": "error",
41
+ "lingui-ts/no-single-tag-message": "error",
42
+ "lingui-ts/no-single-variable-message": "error",
43
+ "lingui-ts/no-unlocalized-strings": "error",
44
+ "lingui-ts/valid-t-call-location": "error"
45
+ // text-restrictions not in recommended (requires configuration)
46
+ }
47
+ }
48
+ };
49
+ export default plugin;
50
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAA;AAC5E,OAAO,EAAE,6BAA6B,EAAE,MAAM,8CAA8C,CAAA;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAA;AAC/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAA;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAErE,MAAM,MAAM,GAAG;IACb,IAAI,EAAE;QACJ,IAAI,EAAE,iCAAiC;QACvC,OAAO,EAAE,OAAO;KACjB;IACD,KAAK,EAAE;QACL,0BAA0B,EAAE,sBAAsB;QAClD,mCAAmC,EAAE,6BAA6B;QAClE,kBAAkB,EAAE,cAAc;QAClC,uBAAuB,EAAE,kBAAkB;QAC3C,4BAA4B,EAAE,uBAAuB;QACrD,wBAAwB,EAAE,oBAAoB;QAC9C,mBAAmB,EAAE,gBAAgB;QACrC,uBAAuB,EAAE,kBAAkB;KAC5C;IACD,OAAO,EAAE,EAA6B;CACvC,CAAA;AAED,sCAAsC;AACtC,MAAM,CAAC,OAAO,GAAG;IACf,kBAAkB,EAAE;QAClB,OAAO,EAAE;YACP,WAAW,EAAE,MAAM;SACpB;QACD,KAAK,EAAE;YACL,oCAAoC,EAAE,OAAO;YAC7C,6CAA6C,EAAE,OAAO;YACtD,4BAA4B,EAAE,OAAO;YACrC,iCAAiC,EAAE,OAAO;YAC1C,sCAAsC,EAAE,OAAO;YAC/C,kCAAkC,EAAE,OAAO;YAC3C,iCAAiC,EAAE,OAAO;YAC1C,gEAAgE;SACjE;KACF;CACF,CAAA;AAED,eAAe,MAAM,CAAA"}
@@ -0,0 +1,5 @@
1
+ export interface Options {
2
+ requiredKeys: string[];
3
+ }
4
+ export declare const consistentPluralFormat: import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingPluralKey", [Options], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
5
+ //# sourceMappingURL=consistent-plural-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consistent-plural-format.d.ts","sourceRoot":"","sources":["../../src/rules/consistent-plural-format.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,OAAO;IACtB,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB;AAmBD,eAAO,MAAM,sBAAsB,4JAqDjC,CAAA"}
@@ -0,0 +1,67 @@
1
+ import { AST_NODE_TYPES } from "@typescript-eslint/utils";
2
+ import { createRule } from "../utils/create-rule.js";
3
+ const DEFAULT_REQUIRED_KEYS = ["one", "other"];
4
+ /**
5
+ * Extracts attribute names from a JSX opening element.
6
+ */
7
+ function getJSXAttributeNames(node) {
8
+ const names = [];
9
+ for (const attr of node.attributes) {
10
+ if (attr.type === AST_NODE_TYPES.JSXAttribute && attr.name.type === AST_NODE_TYPES.JSXIdentifier) {
11
+ names.push(attr.name.name);
12
+ }
13
+ }
14
+ return names;
15
+ }
16
+ export const consistentPluralFormat = createRule({
17
+ name: "consistent-plural-format",
18
+ meta: {
19
+ type: "problem",
20
+ docs: {
21
+ description: "Ensure <Plural> component has required plural category props"
22
+ },
23
+ messages: {
24
+ missingPluralKey: "<Plural> is missing required prop '{{key}}'"
25
+ },
26
+ schema: [
27
+ {
28
+ type: "object",
29
+ properties: {
30
+ requiredKeys: {
31
+ type: "array",
32
+ items: { type: "string" },
33
+ default: DEFAULT_REQUIRED_KEYS
34
+ }
35
+ },
36
+ additionalProperties: false
37
+ }
38
+ ]
39
+ },
40
+ defaultOptions: [
41
+ {
42
+ requiredKeys: DEFAULT_REQUIRED_KEYS
43
+ }
44
+ ],
45
+ create(context, [options]) {
46
+ return {
47
+ JSXElement(node) {
48
+ const openingElement = node.openingElement;
49
+ // Check for <Plural> component
50
+ if (openingElement.name.type !== AST_NODE_TYPES.JSXIdentifier || openingElement.name.name !== "Plural") {
51
+ return;
52
+ }
53
+ const providedProps = getJSXAttributeNames(openingElement);
54
+ for (const requiredKey of options.requiredKeys) {
55
+ if (!providedProps.includes(requiredKey)) {
56
+ context.report({
57
+ node,
58
+ messageId: "missingPluralKey",
59
+ data: { key: requiredKey }
60
+ });
61
+ }
62
+ }
63
+ }
64
+ };
65
+ }
66
+ });
67
+ //# sourceMappingURL=consistent-plural-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consistent-plural-format.js","sourceRoot":"","sources":["../../src/rules/consistent-plural-format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAiB,MAAM,0BAA0B,CAAA;AAExE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAQpD,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;AAE9C;;GAEG;AACH,SAAS,oBAAoB,CAAC,IAAgC;IAC5D,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,YAAY,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,aAAa,EAAE,CAAC;YACjG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC5B,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,UAAU,CAAuB;IACrE,IAAI,EAAE,0BAA0B;IAChC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,8DAA8D;SAC5E;QACD,QAAQ,EAAE;YACR,gBAAgB,EAAE,6CAA6C;SAChE;QACD,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,YAAY,EAAE;wBACZ,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;wBACzB,OAAO,EAAE,qBAAqB;qBAC/B;iBACF;gBACD,oBAAoB,EAAE,KAAK;aAC5B;SACF;KACF;IACD,cAAc,EAAE;QACd;YACE,YAAY,EAAE,qBAAqB;SACpC;KACF;IACD,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC;QACvB,OAAO;YACL,UAAU,CAAC,IAAI;gBACb,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAA;gBAE1C,+BAA+B;gBAC/B,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,aAAa,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACvG,OAAM;gBACR,CAAC;gBAED,MAAM,aAAa,GAAG,oBAAoB,CAAC,cAAc,CAAC,CAAA;gBAE1D,KAAK,MAAM,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;oBAC/C,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;wBACzC,OAAO,CAAC,MAAM,CAAC;4BACb,IAAI;4BACJ,SAAS,EAAE,kBAAkB;4BAC7B,IAAI,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE;yBAC3B,CAAC,CAAA;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=consistent-plural-format.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consistent-plural-format.test.d.ts","sourceRoot":"","sources":["../../src/rules/consistent-plural-format.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,74 @@
1
+ import { RuleTester } from "@typescript-eslint/rule-tester";
2
+ import { afterAll, describe, it } from "vitest";
3
+ import { consistentPluralFormat } from "./consistent-plural-format.js";
4
+ RuleTester.afterAll = afterAll;
5
+ RuleTester.describe = describe;
6
+ RuleTester.it = it;
7
+ const ruleTester = new RuleTester({
8
+ languageOptions: {
9
+ parserOptions: {
10
+ ecmaVersion: 2022,
11
+ sourceType: "module",
12
+ ecmaFeatures: {
13
+ jsx: true
14
+ }
15
+ }
16
+ }
17
+ });
18
+ ruleTester.run("consistent-plural-format", consistentPluralFormat, {
19
+ valid: [
20
+ // All required props present (default: one, other)
21
+ '<Plural value={count} one="# item" other="# items" />',
22
+ '<Plural value={count} one="One" other="Many" zero="None" />',
23
+ // With expressions
24
+ "<Plural value={count} one={oneMsg} other={otherMsg} />",
25
+ // Custom required keys
26
+ {
27
+ code: '<Plural value={count} other="items" />',
28
+ options: [{ requiredKeys: ["other"] }]
29
+ },
30
+ {
31
+ code: '<Plural value={count} one="#" other="#" zero="none" />',
32
+ options: [{ requiredKeys: ["one", "other", "zero"] }]
33
+ },
34
+ // Non-Plural components should be ignored
35
+ '<Select value={gender} male="He" female="She" other="They" />',
36
+ '<div one="x" />',
37
+ "<Trans>Hello</Trans>"
38
+ ],
39
+ invalid: [
40
+ // Missing 'other' (default required)
41
+ {
42
+ code: '<Plural value={count} one="# item" />',
43
+ errors: [{ messageId: "missingPluralKey", data: { key: "other" } }]
44
+ },
45
+ // Missing 'one' (default required)
46
+ {
47
+ code: '<Plural value={count} other="# items" />',
48
+ errors: [{ messageId: "missingPluralKey", data: { key: "one" } }]
49
+ },
50
+ // Missing both default required keys
51
+ {
52
+ code: '<Plural value={count} zero="None" />',
53
+ errors: [
54
+ { messageId: "missingPluralKey", data: { key: "one" } },
55
+ { messageId: "missingPluralKey", data: { key: "other" } }
56
+ ]
57
+ },
58
+ // Custom required keys missing
59
+ {
60
+ code: '<Plural value={count} one="#" other="#" />',
61
+ options: [{ requiredKeys: ["one", "other", "zero"] }],
62
+ errors: [{ messageId: "missingPluralKey", data: { key: "zero" } }]
63
+ },
64
+ // Only value prop
65
+ {
66
+ code: "<Plural value={count} />",
67
+ errors: [
68
+ { messageId: "missingPluralKey", data: { key: "one" } },
69
+ { messageId: "missingPluralKey", data: { key: "other" } }
70
+ ]
71
+ }
72
+ ]
73
+ });
74
+ //# sourceMappingURL=consistent-plural-format.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consistent-plural-format.test.js","sourceRoot":"","sources":["../../src/rules/consistent-plural-format.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAA;AAC3D,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAE/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AAEtE,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;AAElB,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC;IAChC,eAAe,EAAE;QACf,aAAa,EAAE;YACb,WAAW,EAAE,IAAI;YACjB,UAAU,EAAE,QAAQ;YACpB,YAAY,EAAE;gBACZ,GAAG,EAAE,IAAI;aACV;SACF;KACF;CACF,CAAC,CAAA;AAEF,UAAU,CAAC,GAAG,CAAC,0BAA0B,EAAE,sBAAsB,EAAE;IACjE,KAAK,EAAE;QACL,mDAAmD;QACnD,uDAAuD;QACvD,6DAA6D;QAE7D,mBAAmB;QACnB,wDAAwD;QAExD,uBAAuB;QACvB;YACE,IAAI,EAAE,wCAAwC;YAC9C,OAAO,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;SACvC;QACD;YACE,IAAI,EAAE,wDAAwD;YAC9D,OAAO,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC;SACtD;QAED,0CAA0C;QAC1C,+DAA+D;QAC/D,iBAAiB;QACjB,sBAAsB;KACvB;IACD,OAAO,EAAE;QACP,qCAAqC;QACrC;YACE,IAAI,EAAE,uCAAuC;YAC7C,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC;SACpE;QAED,mCAAmC;QACnC;YACE,IAAI,EAAE,0CAA0C;YAChD,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC;SAClE;QAED,qCAAqC;QACrC;YACE,IAAI,EAAE,sCAAsC;YAC5C,MAAM,EAAE;gBACN,EAAE,SAAS,EAAE,kBAAkB,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;gBACvD,EAAE,SAAS,EAAE,kBAAkB,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;aAC1D;SACF;QAED,+BAA+B;QAC/B;YACE,IAAI,EAAE,4CAA4C;YAClD,OAAO,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC;YACrD,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC;SACnE;QAED,kBAAkB;QAClB;YACE,IAAI,EAAE,0BAA0B;YAChC,MAAM,EAAE;gBACN,EAAE,SAAS,EAAE,kBAAkB,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;gBACvD,EAAE,SAAS,EAAE,kBAAkB,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;aAC1D;SACF;KACF;CACF,CAAC,CAAA"}
@@ -0,0 +1,7 @@
1
+ export interface Options {
2
+ allowedCallees: string[];
3
+ allowMemberExpressions: boolean;
4
+ maxExpressionDepth: number | null;
5
+ }
6
+ export declare const noComplexExpressionsInMessage: import("@typescript-eslint/utils/ts-eslint").RuleModule<"complexExpression", [Options], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
7
+ //# sourceMappingURL=no-complex-expressions-in-message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-complex-expressions-in-message.d.ts","sourceRoot":"","sources":["../../src/rules/no-complex-expressions-in-message.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,OAAO;IACtB,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,sBAAsB,EAAE,OAAO,CAAA;IAC/B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC;AA+GD,eAAO,MAAM,6BAA6B,6JA+FxC,CAAA"}
@@ -0,0 +1,184 @@
1
+ import { AST_NODE_TYPES } from "@typescript-eslint/utils";
2
+ import { createRule } from "../utils/create-rule.js";
3
+ const DEFAULT_ALLOWED_CALLEES = ["i18n.number", "i18n.date"];
4
+ /**
5
+ * Gets the string representation of a callee (e.g., "i18n.number", "Math.random").
6
+ */
7
+ function getCalleeName(node) {
8
+ if (node.type === AST_NODE_TYPES.Identifier) {
9
+ return node.name;
10
+ }
11
+ if (node.type === AST_NODE_TYPES.MemberExpression && !node.computed) {
12
+ const object = getCalleeName(node.object);
13
+ const property = node.property.type === AST_NODE_TYPES.Identifier ? node.property.name : null;
14
+ if (object !== null && property !== null) {
15
+ return `${object}.${property}`;
16
+ }
17
+ }
18
+ return null;
19
+ }
20
+ /**
21
+ * Gets the depth of a member expression chain.
22
+ */
23
+ function getMemberExpressionDepth(node) {
24
+ let depth = 1;
25
+ let current = node.object;
26
+ while (current.type === AST_NODE_TYPES.MemberExpression) {
27
+ depth++;
28
+ current = current.object;
29
+ }
30
+ return depth;
31
+ }
32
+ /**
33
+ * Checks if an expression is allowed within a Lingui message.
34
+ */
35
+ function isAllowedExpression(node, options) {
36
+ // Simple identifiers are always allowed: name, count
37
+ if (node.type === AST_NODE_TYPES.Identifier) {
38
+ return true;
39
+ }
40
+ // Member expressions: props.name, user.id
41
+ if (node.type === AST_NODE_TYPES.MemberExpression) {
42
+ if (!options.allowMemberExpressions) {
43
+ return false;
44
+ }
45
+ // Check depth limit
46
+ const depth = getMemberExpressionDepth(node);
47
+ if (options.maxExpressionDepth !== null && depth > options.maxExpressionDepth) {
48
+ return false;
49
+ }
50
+ // Optional chaining is not allowed
51
+ if (node.optional) {
52
+ return false;
53
+ }
54
+ return true;
55
+ }
56
+ // Call expressions: check if callee is whitelisted
57
+ if (node.type === AST_NODE_TYPES.CallExpression) {
58
+ const calleeName = getCalleeName(node.callee);
59
+ if (calleeName !== null && options.allowedCallees.includes(calleeName)) {
60
+ return true;
61
+ }
62
+ return false;
63
+ }
64
+ // All other expression types are not allowed
65
+ return false;
66
+ }
67
+ /**
68
+ * Gets a readable representation of an expression for error messages.
69
+ */
70
+ function getExpressionText(node) {
71
+ switch (node.type) {
72
+ case AST_NODE_TYPES.BinaryExpression:
73
+ return `binary expression (${node.operator})`;
74
+ case AST_NODE_TYPES.CallExpression: {
75
+ const name = getCalleeName(node.callee);
76
+ return name !== null ? `function call (${name})` : "function call";
77
+ }
78
+ case AST_NODE_TYPES.MemberExpression:
79
+ return "member expression";
80
+ case AST_NODE_TYPES.ConditionalExpression:
81
+ return "conditional expression";
82
+ case AST_NODE_TYPES.LogicalExpression:
83
+ return `logical expression (${node.operator})`;
84
+ case AST_NODE_TYPES.UnaryExpression:
85
+ return `unary expression (${node.operator})`;
86
+ case AST_NODE_TYPES.TemplateLiteral:
87
+ return "template literal";
88
+ case AST_NODE_TYPES.ArrayExpression:
89
+ return "array expression";
90
+ case AST_NODE_TYPES.ObjectExpression:
91
+ return "object expression";
92
+ default:
93
+ return node.type;
94
+ }
95
+ }
96
+ export const noComplexExpressionsInMessage = createRule({
97
+ name: "no-complex-expressions-in-message",
98
+ meta: {
99
+ type: "problem",
100
+ docs: {
101
+ description: "Disallow complex expressions in Lingui messages"
102
+ },
103
+ messages: {
104
+ complexExpression: "Complex expression '{{expression}}' in Lingui message. Use simple identifiers or extract to a variable."
105
+ },
106
+ schema: [
107
+ {
108
+ type: "object",
109
+ properties: {
110
+ allowedCallees: {
111
+ type: "array",
112
+ items: { type: "string" },
113
+ default: DEFAULT_ALLOWED_CALLEES
114
+ },
115
+ allowMemberExpressions: {
116
+ type: "boolean",
117
+ default: false
118
+ },
119
+ maxExpressionDepth: {
120
+ type: ["number", "null"],
121
+ default: 1
122
+ }
123
+ },
124
+ additionalProperties: false
125
+ }
126
+ ]
127
+ },
128
+ defaultOptions: [
129
+ {
130
+ allowedCallees: DEFAULT_ALLOWED_CALLEES,
131
+ allowMemberExpressions: false,
132
+ maxExpressionDepth: 1
133
+ }
134
+ ],
135
+ create(context, [options]) {
136
+ function checkExpressions(expressions) {
137
+ for (const expr of expressions) {
138
+ if (!isAllowedExpression(expr, options)) {
139
+ context.report({
140
+ node: expr,
141
+ messageId: "complexExpression",
142
+ data: {
143
+ expression: getExpressionText(expr)
144
+ }
145
+ });
146
+ }
147
+ }
148
+ }
149
+ function checkJSXChildren(children) {
150
+ for (const child of children) {
151
+ if (child.type === AST_NODE_TYPES.JSXExpressionContainer &&
152
+ child.expression.type !== AST_NODE_TYPES.JSXEmptyExpression) {
153
+ if (!isAllowedExpression(child.expression, options)) {
154
+ context.report({
155
+ node: child.expression,
156
+ messageId: "complexExpression",
157
+ data: {
158
+ expression: getExpressionText(child.expression)
159
+ }
160
+ });
161
+ }
162
+ }
163
+ }
164
+ }
165
+ return {
166
+ // Check t`Hello ${expr}` pattern
167
+ TaggedTemplateExpression(node) {
168
+ if (node.tag.type !== AST_NODE_TYPES.Identifier || node.tag.name !== "t") {
169
+ return;
170
+ }
171
+ checkExpressions(node.quasi.expressions);
172
+ },
173
+ // Check <Trans>Hello {expr}</Trans> pattern
174
+ JSXElement(node) {
175
+ const openingElement = node.openingElement;
176
+ if (openingElement.name.type !== AST_NODE_TYPES.JSXIdentifier || openingElement.name.name !== "Trans") {
177
+ return;
178
+ }
179
+ checkJSXChildren(node.children);
180
+ }
181
+ };
182
+ }
183
+ });
184
+ //# sourceMappingURL=no-complex-expressions-in-message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-complex-expressions-in-message.js","sourceRoot":"","sources":["../../src/rules/no-complex-expressions-in-message.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=no-complex-expressions-in-message.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-complex-expressions-in-message.test.d.ts","sourceRoot":"","sources":["../../src/rules/no-complex-expressions-in-message.test.ts"],"names":[],"mappings":""}