eslint-plugin-intlayer 9.3.1

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 (56) hide show
  1. package/README.md +310 -0
  2. package/dist/cjs/index.cjs +105 -0
  3. package/dist/cjs/index.cjs.map +1 -0
  4. package/dist/cjs/rules/enforceAdapterImport.cjs +81 -0
  5. package/dist/cjs/rules/enforceAdapterImport.cjs.map +1 -0
  6. package/dist/cjs/rules/noDynamicFieldAccess.cjs +130 -0
  7. package/dist/cjs/rules/noDynamicFieldAccess.cjs.map +1 -0
  8. package/dist/cjs/rules/noRawText.cjs +176 -0
  9. package/dist/cjs/rules/noRawText.cjs.map +1 -0
  10. package/dist/cjs/rules/staticDictionaryKey.cjs +89 -0
  11. package/dist/cjs/rules/staticDictionaryKey.cjs.map +1 -0
  12. package/dist/cjs/utils/ast.cjs +151 -0
  13. package/dist/cjs/utils/ast.cjs.map +1 -0
  14. package/dist/cjs/utils/callerMatcher.cjs +130 -0
  15. package/dist/cjs/utils/callerMatcher.cjs.map +1 -0
  16. package/dist/cjs/utils/createRule.cjs +26 -0
  17. package/dist/cjs/utils/createRule.cjs.map +1 -0
  18. package/dist/cjs/utils/templates.cjs +148 -0
  19. package/dist/cjs/utils/templates.cjs.map +1 -0
  20. package/dist/esm/index.mjs +101 -0
  21. package/dist/esm/index.mjs.map +1 -0
  22. package/dist/esm/rules/enforceAdapterImport.mjs +80 -0
  23. package/dist/esm/rules/enforceAdapterImport.mjs.map +1 -0
  24. package/dist/esm/rules/noDynamicFieldAccess.mjs +129 -0
  25. package/dist/esm/rules/noDynamicFieldAccess.mjs.map +1 -0
  26. package/dist/esm/rules/noRawText.mjs +175 -0
  27. package/dist/esm/rules/noRawText.mjs.map +1 -0
  28. package/dist/esm/rules/staticDictionaryKey.mjs +88 -0
  29. package/dist/esm/rules/staticDictionaryKey.mjs.map +1 -0
  30. package/dist/esm/utils/ast.mjs +143 -0
  31. package/dist/esm/utils/ast.mjs.map +1 -0
  32. package/dist/esm/utils/callerMatcher.mjs +126 -0
  33. package/dist/esm/utils/callerMatcher.mjs.map +1 -0
  34. package/dist/esm/utils/createRule.mjs +25 -0
  35. package/dist/esm/utils/createRule.mjs.map +1 -0
  36. package/dist/esm/utils/templates.mjs +145 -0
  37. package/dist/esm/utils/templates.mjs.map +1 -0
  38. package/dist/types/index.d.ts +67 -0
  39. package/dist/types/index.d.ts.map +1 -0
  40. package/dist/types/rules/enforceAdapterImport.d.ts +6 -0
  41. package/dist/types/rules/enforceAdapterImport.d.ts.map +1 -0
  42. package/dist/types/rules/noDynamicFieldAccess.d.ts +7 -0
  43. package/dist/types/rules/noDynamicFieldAccess.d.ts.map +1 -0
  44. package/dist/types/rules/noRawText.d.ts +25 -0
  45. package/dist/types/rules/noRawText.d.ts.map +1 -0
  46. package/dist/types/rules/staticDictionaryKey.d.ts +7 -0
  47. package/dist/types/rules/staticDictionaryKey.d.ts.map +1 -0
  48. package/dist/types/utils/ast.d.ts +105 -0
  49. package/dist/types/utils/ast.d.ts.map +1 -0
  50. package/dist/types/utils/callerMatcher.d.ts +58 -0
  51. package/dist/types/utils/callerMatcher.d.ts.map +1 -0
  52. package/dist/types/utils/createRule.d.ts +23 -0
  53. package/dist/types/utils/createRule.d.ts.map +1 -0
  54. package/dist/types/utils/templates.d.ts +71 -0
  55. package/dist/types/utils/templates.d.ts.map +1 -0
  56. package/package.json +105 -0
@@ -0,0 +1,145 @@
1
+ //#region src/utils/templates.ts
2
+ /**
3
+ * Element node types whose text content is never user-facing copy, regardless
4
+ * of the `ignoreElements` option (which matches on tag name, and these carry
5
+ * none in some parsers).
6
+ */
7
+ const NON_CONTENT_ELEMENT_TYPES = /* @__PURE__ */ new Set(["SvelteScriptElement", "SvelteStyleElement"]);
8
+ /** Element node types, per parser, that carry a tag name. */
9
+ const ELEMENT_TYPES = /* @__PURE__ */ new Set([
10
+ "VElement",
11
+ "SvelteElement",
12
+ "Element",
13
+ "JSXElement",
14
+ "JSXOpeningElement"
15
+ ]);
16
+ /**
17
+ * Read the tag name off an element node, normalising the three shapes parsers
18
+ * use: a plain string (Vue, Angular), a `SvelteName` node (Svelte), and a JSX
19
+ * name node.
20
+ *
21
+ * @param element - An element node.
22
+ */
23
+ const readElementName = (element) => {
24
+ const name = element["name"];
25
+ if (typeof name === "string") return name;
26
+ if (name && typeof name === "object") {
27
+ const nameNode = name;
28
+ if (typeof nameNode["name"] === "string") return nameNode["name"];
29
+ }
30
+ const openingElement = element["openingElement"];
31
+ if (openingElement) return readElementName(openingElement);
32
+ return null;
33
+ };
34
+ /**
35
+ * Walk up to the nearest enclosing element and return its tag name, or the
36
+ * sentinel `false` when the text sits inside a `<script>`/`<style>` block that
37
+ * should never be reported.
38
+ *
39
+ * @param node - The text or attribute node to start from.
40
+ */
41
+ const resolveEnclosingElement = (node) => {
42
+ let current = node?.parent;
43
+ while (current) {
44
+ if (NON_CONTENT_ELEMENT_TYPES.has(current.type)) return false;
45
+ if (ELEMENT_TYPES.has(current.type)) return readElementName(current);
46
+ current = current.parent;
47
+ }
48
+ return null;
49
+ };
50
+ /**
51
+ * Report a finding unless it sits inside a non-content element.
52
+ *
53
+ * @param report - The rule's reporter.
54
+ * @param node - Node to report on.
55
+ * @param text - The literal text.
56
+ * @param attributeName - Attribute name, or `null` for element text.
57
+ */
58
+ const reportWithElement = (report, node, text, attributeName) => {
59
+ const elementName = resolveEnclosingElement(node);
60
+ if (elementName === false) return;
61
+ report({
62
+ node,
63
+ text,
64
+ attributeName,
65
+ elementName
66
+ });
67
+ };
68
+ /**
69
+ * Visitors covering every non-JSX template language Intlayer supports.
70
+ *
71
+ * JSX (React, Preact, Solid, Astro, Lit templates compiled through JSX) is
72
+ * handled by the rule itself, since `JSXText`/`JSXAttribute` also need the
73
+ * non-template code paths.
74
+ *
75
+ * @param report - Called for each literal text node found.
76
+ */
77
+ const createTemplateVisitor = (report) => ({
78
+ VText: (node) => {
79
+ const astNode = node;
80
+ reportWithElement(report, astNode, astNode["value"], null);
81
+ },
82
+ VAttribute: (node) => {
83
+ const astNode = node;
84
+ if (astNode["directive"] === true) return;
85
+ const key = astNode["key"];
86
+ const value = astNode["value"];
87
+ const attributeName = key?.["name"];
88
+ const text = value?.["value"];
89
+ if (typeof attributeName !== "string" || typeof text !== "string") return;
90
+ reportWithElement(report, astNode, text, attributeName);
91
+ },
92
+ SvelteText: (node) => {
93
+ const astNode = node;
94
+ reportWithElement(report, astNode, astNode["value"], null);
95
+ },
96
+ SvelteAttribute: (node) => {
97
+ const astNode = node;
98
+ const attributeName = astNode["key"]?.["name"];
99
+ if (typeof attributeName !== "string") return;
100
+ const parts = astNode["value"];
101
+ if (parts?.length !== 1) return;
102
+ const part = parts[0];
103
+ if (part?.type !== "SvelteLiteral") return;
104
+ const text = part["value"];
105
+ if (typeof text !== "string") return;
106
+ reportWithElement(report, astNode, text, attributeName);
107
+ },
108
+ Text: (node) => {
109
+ const astNode = node;
110
+ const text = astNode["value"];
111
+ if (typeof text !== "string") return;
112
+ reportWithElement(report, astNode, text, null);
113
+ },
114
+ TextAttribute: (node) => {
115
+ const astNode = node;
116
+ const attributeName = astNode["name"];
117
+ const text = astNode["value"];
118
+ if (typeof attributeName !== "string" || typeof text !== "string") return;
119
+ reportWithElement(report, astNode, text, attributeName);
120
+ }
121
+ });
122
+ /**
123
+ * Combine a script visitor and a template visitor into the single object the
124
+ * active parser expects.
125
+ *
126
+ * Vue keeps its template in a side AST reachable only through
127
+ * `defineTemplateBodyVisitor`; every other parser puts markup in the main AST,
128
+ * where a merged visitor object is enough.
129
+ *
130
+ * @param parserServices - `context.sourceCode.parserServices`.
131
+ * @param scriptVisitor - Handlers for ordinary script nodes.
132
+ * @param templateVisitor - Handlers for markup nodes.
133
+ */
134
+ const combineVisitors = (parserServices, scriptVisitor, templateVisitor) => {
135
+ const services = parserServices;
136
+ if (typeof services?.defineTemplateBodyVisitor === "function") return services.defineTemplateBodyVisitor(templateVisitor, scriptVisitor);
137
+ return {
138
+ ...scriptVisitor,
139
+ ...templateVisitor
140
+ };
141
+ };
142
+
143
+ //#endregion
144
+ export { combineVisitors, createTemplateVisitor, resolveEnclosingElement };
145
+ //# sourceMappingURL=templates.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.mjs","names":[],"sources":["../../../src/utils/templates.ts"],"sourcesContent":["import type { AstNode, AstNodeWithParent } from './ast';\n\n/**\n * Template-language support for text-coverage rules.\n *\n * Intlayer ships integrations for React, Vue, Svelte, Angular, Astro, Solid,\n * Preact and Lit, and each ecosystem's ESLint parser models markup differently:\n *\n * | Framework | Parser | Text node | Attribute node | Lives in |\n * | --------- | ------------------------------- | ----------- | --------------- | --------------- |\n * | React/JSX | espree, typescript-eslint | `JSXText` | `JSXAttribute` | main AST |\n * | Astro | astro-eslint-parser | `JSXText` | `JSXAttribute` | main AST |\n * | Svelte | svelte-eslint-parser | `SvelteText`| `SvelteAttribute`| main AST |\n * | Angular | @angular-eslint/template-parser | `Text` | `TextAttribute` | main AST |\n * | Vue | vue-eslint-parser | `VText` | `VAttribute` | `templateBody` |\n *\n * Vue is the only one that hides its template behind\n * `parserServices.defineTemplateBodyVisitor`; every other parser exposes markup\n * in the AST a normal visitor already walks. Node type names do not collide\n * across parsers, so one merged visitor is safe to hand to any of them — a\n * Svelte file simply never emits a `VText`.\n */\n\n/** One piece of literal text found in markup. */\nexport type TemplateTextFinding = {\n /** Node to report on. */\n node: AstNode;\n /** The literal text. */\n text: string;\n /** Attribute name when the text is an attribute value, else `null`. */\n attributeName: string | null;\n /** Tag name of the enclosing element, when resolvable. */\n elementName: string | null;\n};\n\n/** Callback invoked for every literal text node the visitors find. */\nexport type TemplateTextReporter = (finding: TemplateTextFinding) => void;\n\n/** A visitor object as returned from a rule's `create`. */\nexport type TemplateVisitor = Record<string, (node: never) => void>;\n\n/**\n * Element node types whose text content is never user-facing copy, regardless\n * of the `ignoreElements` option (which matches on tag name, and these carry\n * none in some parsers).\n */\nconst NON_CONTENT_ELEMENT_TYPES = new Set([\n 'SvelteScriptElement',\n 'SvelteStyleElement',\n]);\n\n/** Element node types, per parser, that carry a tag name. */\nconst ELEMENT_TYPES = new Set([\n 'VElement',\n 'SvelteElement',\n 'Element',\n 'JSXElement',\n 'JSXOpeningElement',\n]);\n\n/**\n * Read the tag name off an element node, normalising the three shapes parsers\n * use: a plain string (Vue, Angular), a `SvelteName` node (Svelte), and a JSX\n * name node.\n *\n * @param element - An element node.\n */\nconst readElementName = (element: AstNode): string | null => {\n const name = element['name'];\n\n if (typeof name === 'string') return name;\n\n if (name && typeof name === 'object') {\n const nameNode = name as AstNode;\n\n if (typeof nameNode['name'] === 'string') return nameNode['name'];\n }\n\n // JSXElement holds its name on the opening element.\n const openingElement = element['openingElement'] as AstNode | undefined;\n\n if (openingElement) return readElementName(openingElement);\n\n return null;\n};\n\n/**\n * Walk up to the nearest enclosing element and return its tag name, or the\n * sentinel `false` when the text sits inside a `<script>`/`<style>` block that\n * should never be reported.\n *\n * @param node - The text or attribute node to start from.\n */\nexport const resolveEnclosingElement = (\n node: AstNodeWithParent | undefined\n): string | null | false => {\n let current = node?.parent;\n\n while (current) {\n if (NON_CONTENT_ELEMENT_TYPES.has(current.type)) return false;\n\n if (ELEMENT_TYPES.has(current.type)) return readElementName(current);\n\n current = current.parent;\n }\n\n return null;\n};\n\n/**\n * Report a finding unless it sits inside a non-content element.\n *\n * @param report - The rule's reporter.\n * @param node - Node to report on.\n * @param text - The literal text.\n * @param attributeName - Attribute name, or `null` for element text.\n */\nconst reportWithElement = (\n report: TemplateTextReporter,\n node: AstNodeWithParent,\n text: string,\n attributeName: string | null\n): void => {\n const elementName = resolveEnclosingElement(node);\n\n if (elementName === false) return;\n\n report({ node, text, attributeName, elementName });\n};\n\n/**\n * Visitors covering every non-JSX template language Intlayer supports.\n *\n * JSX (React, Preact, Solid, Astro, Lit templates compiled through JSX) is\n * handled by the rule itself, since `JSXText`/`JSXAttribute` also need the\n * non-template code paths.\n *\n * @param report - Called for each literal text node found.\n */\nexport const createTemplateVisitor = (\n report: TemplateTextReporter\n): TemplateVisitor => ({\n // ── Vue ────────────────────────────────────────────────────────────────\n VText: (node: never) => {\n const astNode = node as AstNodeWithParent;\n\n reportWithElement(report, astNode, astNode['value'] as string, null);\n },\n\n VAttribute: (node: never) => {\n const astNode = node as AstNodeWithParent;\n\n // Directives (`:title`, `v-bind:title`) hold an expression, not literal text.\n if (astNode['directive'] === true) return;\n\n const key = astNode['key'] as AstNode | undefined;\n const value = astNode['value'] as AstNode | undefined;\n const attributeName = key?.['name'];\n const text = value?.['value'];\n\n if (typeof attributeName !== 'string' || typeof text !== 'string') return;\n\n reportWithElement(report, astNode, text, attributeName);\n },\n\n // ── Svelte ─────────────────────────────────────────────────────────────\n SvelteText: (node: never) => {\n const astNode = node as AstNodeWithParent;\n\n reportWithElement(report, astNode, astNode['value'] as string, null);\n },\n\n SvelteAttribute: (node: never) => {\n const astNode = node as AstNodeWithParent;\n const key = astNode['key'] as AstNode | undefined;\n const attributeName = key?.['name'];\n\n if (typeof attributeName !== 'string') return;\n\n // The value is a list of parts; a single literal part means a static value,\n // anything else interpolates and is not raw copy.\n const parts = astNode['value'] as AstNode[] | undefined;\n\n if (parts?.length !== 1) return;\n\n const part = parts[0];\n\n if (part?.type !== 'SvelteLiteral') return;\n\n const text = part['value'];\n\n if (typeof text !== 'string') return;\n\n reportWithElement(report, astNode, text, attributeName);\n },\n\n // ── Angular ────────────────────────────────────────────────────────────\n Text: (node: never) => {\n const astNode = node as AstNodeWithParent;\n const text = astNode['value'];\n\n if (typeof text !== 'string') return;\n\n reportWithElement(report, astNode, text, null);\n },\n\n TextAttribute: (node: never) => {\n const astNode = node as AstNodeWithParent;\n const attributeName = astNode['name'];\n const text = astNode['value'];\n\n if (typeof attributeName !== 'string' || typeof text !== 'string') return;\n\n reportWithElement(report, astNode, text, attributeName);\n },\n});\n\n/** Parser services that expose a Vue template body. */\ntype VueParserServices = {\n defineTemplateBodyVisitor?: (\n templateVisitor: TemplateVisitor,\n scriptVisitor: TemplateVisitor\n ) => TemplateVisitor;\n};\n\n/**\n * Combine a script visitor and a template visitor into the single object the\n * active parser expects.\n *\n * Vue keeps its template in a side AST reachable only through\n * `defineTemplateBodyVisitor`; every other parser puts markup in the main AST,\n * where a merged visitor object is enough.\n *\n * @param parserServices - `context.sourceCode.parserServices`.\n * @param scriptVisitor - Handlers for ordinary script nodes.\n * @param templateVisitor - Handlers for markup nodes.\n */\nexport const combineVisitors = (\n parserServices: unknown,\n scriptVisitor: TemplateVisitor,\n templateVisitor: TemplateVisitor\n): TemplateVisitor => {\n const services = parserServices as VueParserServices | undefined;\n\n if (typeof services?.defineTemplateBodyVisitor === 'function') {\n return services.defineTemplateBodyVisitor(templateVisitor, scriptVisitor);\n }\n\n return { ...scriptVisitor, ...templateVisitor };\n};\n"],"mappings":";;;;;;AA8CA,MAAM,4CAA4B,IAAI,IAAI,CACxC,uBACA,oBACF,CAAC;;AAGD,MAAM,gCAAgB,IAAI,IAAI;CAC5B;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;;;AASD,MAAM,mBAAmB,YAAoC;CAC3D,MAAM,OAAO,QAAQ;CAErB,IAAI,OAAO,SAAS,UAAU,OAAO;CAErC,IAAI,QAAQ,OAAO,SAAS,UAAU;EACpC,MAAM,WAAW;EAEjB,IAAI,OAAO,SAAS,YAAY,UAAU,OAAO,SAAS;CAC5D;CAGA,MAAM,iBAAiB,QAAQ;CAE/B,IAAI,gBAAgB,OAAO,gBAAgB,cAAc;CAEzD,OAAO;AACT;;;;;;;;AASA,MAAa,2BACX,SAC0B;CAC1B,IAAI,UAAU,MAAM;CAEpB,OAAO,SAAS;EACd,IAAI,0BAA0B,IAAI,QAAQ,IAAI,GAAG,OAAO;EAExD,IAAI,cAAc,IAAI,QAAQ,IAAI,GAAG,OAAO,gBAAgB,OAAO;EAEnE,UAAU,QAAQ;CACpB;CAEA,OAAO;AACT;;;;;;;;;AAUA,MAAM,qBACJ,QACA,MACA,MACA,kBACS;CACT,MAAM,cAAc,wBAAwB,IAAI;CAEhD,IAAI,gBAAgB,OAAO;CAE3B,OAAO;EAAE;EAAM;EAAM;EAAe;CAAY,CAAC;AACnD;;;;;;;;;;AAWA,MAAa,yBACX,YACqB;CAErB,QAAQ,SAAgB;EACtB,MAAM,UAAU;EAEhB,kBAAkB,QAAQ,SAAS,QAAQ,UAAoB,IAAI;CACrE;CAEA,aAAa,SAAgB;EAC3B,MAAM,UAAU;EAGhB,IAAI,QAAQ,iBAAiB,MAAM;EAEnC,MAAM,MAAM,QAAQ;EACpB,MAAM,QAAQ,QAAQ;EACtB,MAAM,gBAAgB,MAAM;EAC5B,MAAM,OAAO,QAAQ;EAErB,IAAI,OAAO,kBAAkB,YAAY,OAAO,SAAS,UAAU;EAEnE,kBAAkB,QAAQ,SAAS,MAAM,aAAa;CACxD;CAGA,aAAa,SAAgB;EAC3B,MAAM,UAAU;EAEhB,kBAAkB,QAAQ,SAAS,QAAQ,UAAoB,IAAI;CACrE;CAEA,kBAAkB,SAAgB;EAChC,MAAM,UAAU;EAEhB,MAAM,gBADM,QAAQ,MACK,GAAG;EAE5B,IAAI,OAAO,kBAAkB,UAAU;EAIvC,MAAM,QAAQ,QAAQ;EAEtB,IAAI,OAAO,WAAW,GAAG;EAEzB,MAAM,OAAO,MAAM;EAEnB,IAAI,MAAM,SAAS,iBAAiB;EAEpC,MAAM,OAAO,KAAK;EAElB,IAAI,OAAO,SAAS,UAAU;EAE9B,kBAAkB,QAAQ,SAAS,MAAM,aAAa;CACxD;CAGA,OAAO,SAAgB;EACrB,MAAM,UAAU;EAChB,MAAM,OAAO,QAAQ;EAErB,IAAI,OAAO,SAAS,UAAU;EAE9B,kBAAkB,QAAQ,SAAS,MAAM,IAAI;CAC/C;CAEA,gBAAgB,SAAgB;EAC9B,MAAM,UAAU;EAChB,MAAM,gBAAgB,QAAQ;EAC9B,MAAM,OAAO,QAAQ;EAErB,IAAI,OAAO,kBAAkB,YAAY,OAAO,SAAS,UAAU;EAEnE,kBAAkB,QAAQ,SAAS,MAAM,aAAa;CACxD;AACF;;;;;;;;;;;;;AAsBA,MAAa,mBACX,gBACA,eACA,oBACoB;CACpB,MAAM,WAAW;CAEjB,IAAI,OAAO,UAAU,8BAA8B,YACjD,OAAO,SAAS,0BAA0B,iBAAiB,aAAa;CAG1E,OAAO;EAAE,GAAG;EAAe,GAAG;CAAgB;AAChD"}
@@ -0,0 +1,67 @@
1
+ import { IntlayerRule } from "./utils/createRule.js";
2
+ import { NoRawTextOptions } from "./rules/noRawText.js";
3
+ import "./index.js";
4
+ import { ESLint, Linter } from "eslint";
5
+ //#region src/index.d.ts
6
+ /**
7
+ * Every rule this plugin ships, keyed by the name used in configuration.
8
+ *
9
+ * The rules split into two families:
10
+ * - **Coverage** (`no-raw-text`) — user-facing copy that never reached a
11
+ * dictionary. Nothing else in the toolchain reports it.
12
+ * - **Compiler contract** (`static-dictionary-key`, `no-dynamic-field-access`)
13
+ * — call shapes that type-check and run, but that the Babel/SWC passes cannot
14
+ * analyse, so they silently lose the optimize pass or read a purged field.
15
+ *
16
+ * Deliberately absent: unknown dictionary keys, unknown field paths and missing
17
+ * required locales. The generated `__DictionaryRegistry` module augmentation
18
+ * already makes all three compile errors, and `@intlayer/lsp` reports the first
19
+ * as an editor diagnostic — a third report would only add noise.
20
+ */
21
+ declare const rules: {
22
+ 'no-raw-text': IntlayerRule<[NoRawTextOptions?], "rawText" | "rawAttribute" | "rawStringLiteral">;
23
+ 'static-dictionary-key': IntlayerRule<[], "dynamicKey" | "dynamicTemplateKey" | "dynamicJsxKey">;
24
+ 'no-dynamic-field-access': IntlayerRule<[], "dynamicField" | "dynamicMessageId">;
25
+ 'enforce-adapter-import': IntlayerRule<[], "useAdapterImport">;
26
+ };
27
+ /** Rule names this plugin exposes. */
28
+ type IntlayerRuleName = keyof typeof rules;
29
+ declare const meta: {
30
+ name: string;
31
+ version: string;
32
+ };
33
+ /** The flat configs this plugin ships. */
34
+ type IntlayerConfigName = 'recommended' | 'strict' | 'contract-only';
35
+ /**
36
+ * Flat configs, for `eslint.config.js`:
37
+ *
38
+ * ```js
39
+ * import intlayer from 'eslint-plugin-intlayer';
40
+ *
41
+ * export default [intlayer.configs.recommended];
42
+ * ```
43
+ *
44
+ * - `recommended` — the compiler-contract rules as errors, plus `no-raw-text`
45
+ * as a warning so an existing codebase is not blocked on day one.
46
+ * - `strict` — everything as an error, including string literals outside JSX.
47
+ * - `contract-only` — just the rules that protect the build output; use this
48
+ * when translation coverage is tracked by `intlayer test` instead.
49
+ *
50
+ * `enforce-adapter-import` is opt-in: importing from the original package is
51
+ * perfectly valid once the bundler alias is set up, so a project that has done
52
+ * that should not be nagged. Only `strict` turns it on.
53
+ */
54
+ declare const configs: Record<IntlayerConfigName, Linter.Config>;
55
+ /** The plugin object, as consumed by `eslint.config.js`. */
56
+ type IntlayerPlugin = ESLint.Plugin & {
57
+ meta: {
58
+ name: string;
59
+ version: string;
60
+ };
61
+ rules: typeof rules;
62
+ configs: Record<IntlayerConfigName, Linter.Config>;
63
+ };
64
+ declare const intlayerPlugin: IntlayerPlugin;
65
+ //#endregion
66
+ export { IntlayerConfigName, IntlayerPlugin, IntlayerRuleName, type NoRawTextOptions, configs, intlayerPlugin as default, meta, rules };
67
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;cAwBa;+BAAA;;;;;;KAQD,gCAAgC;cAEtC;EAAQ;EAAc;;;KAQhB;;;;;;;;;;;;;;;;;;;;cAyCC,SAAS,OAAO,oBAAoB,OAAO;;KAiC5C,iBAAiB,OAAO;EAClC;IAAQ;IAAc;;EACtB,cAAc;EACd,SAAS,OAAO,oBAAoB,OAAO;;cAGvC,gBAAgB"}
@@ -0,0 +1,6 @@
1
+ import { IntlayerRule } from "../utils/createRule.js";
2
+ //#region src/rules/enforceAdapterImport.d.ts
3
+ declare const enforceAdapterImport: IntlayerRule<[], "useAdapterImport">;
4
+ //#endregion
5
+ export { enforceAdapterImport };
6
+ //# sourceMappingURL=enforceAdapterImport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enforceAdapterImport.d.ts","names":[],"sources":["../../../src/rules/enforceAdapterImport.ts"],"mappings":";;cAuDa,sBAAA"}
@@ -0,0 +1,7 @@
1
+ import { IntlayerRule } from "../utils/createRule.js";
2
+ //#region src/rules/noDynamicFieldAccess.d.ts
3
+ type MessageIds = 'dynamicField' | 'dynamicMessageId';
4
+ declare const noDynamicFieldAccess: IntlayerRule<[], MessageIds>;
5
+ //#endregion
6
+ export { noDynamicFieldAccess };
7
+ //# sourceMappingURL=noDynamicFieldAccess.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noDynamicFieldAccess.d.ts","names":[],"sources":["../../../src/rules/noDynamicFieldAccess.ts"],"mappings":";;KAcK;cAiFQ,sBAAoB,iBAAA"}
@@ -0,0 +1,25 @@
1
+ import { IntlayerRule } from "../utils/createRule.js";
2
+ //#region src/rules/noRawText.d.ts
3
+ type NoRawTextOptions = {
4
+ /**
5
+ * JSX attributes whose string value is user-facing copy.
6
+ * Defaults to the compiler's own list, so the linter flags exactly what
7
+ * `intlayer extract` would rewrite.
8
+ */
9
+ attributes?: string[];
10
+ /** JSX elements whose children are never translatable. */
11
+ ignoreElements?: string[];
12
+ /** Regular-expression sources for text that should never be reported. */
13
+ ignorePatterns?: string[];
14
+ /**
15
+ * Also report string literals outside JSX (function arguments, object values,
16
+ * …). Off by default: outside JSX the false-positive rate is much higher, and
17
+ * the compiler's own extractor is the better first pass.
18
+ */
19
+ includeStringLiterals?: boolean;
20
+ };
21
+ type MessageIds = 'rawText' | 'rawAttribute' | 'rawStringLiteral';
22
+ declare const noRawText: IntlayerRule<[NoRawTextOptions?], MessageIds>;
23
+ //#endregion
24
+ export { NoRawTextOptions, noRawText };
25
+ //# sourceMappingURL=noRawText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noRawText.d.ts","names":[],"sources":["../../../src/rules/noRawText.ts"],"mappings":";;KAwBY;;;;;;EAMV;;EAEA;;EAEA;;;;;;EAMA;;KAGG;cAsEQ,WAAS,cAAA,oBAAA"}
@@ -0,0 +1,7 @@
1
+ import { IntlayerRule } from "../utils/createRule.js";
2
+ //#region src/rules/staticDictionaryKey.d.ts
3
+ type MessageIds = 'dynamicKey' | 'dynamicTemplateKey' | 'dynamicJsxKey';
4
+ declare const staticDictionaryKey: IntlayerRule<[], MessageIds>;
5
+ //#endregion
6
+ export { staticDictionaryKey };
7
+ //# sourceMappingURL=staticDictionaryKey.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staticDictionaryKey.d.ts","names":[],"sources":["../../../src/rules/staticDictionaryKey.ts"],"mappings":";;KAkBK;cAsBQ,qBAAmB,iBAAA"}
@@ -0,0 +1,105 @@
1
+ import { Node } from "/Users/aymericpineau/Documents/intlayer_/node_modules/.bun/@types+estree@1.0.9/node_modules/@types/estree/index.js";
2
+ //#region src/utils/ast.d.ts
3
+ /**
4
+ * Minimal structural models for the nodes this plugin inspects that are not
5
+ * part of the ESTree spec — JSX (all parsers) and the TypeScript-only wrapper
6
+ * expressions emitted by `@typescript-eslint/parser`.
7
+ *
8
+ * They are declared here rather than imported from `@typescript-eslint/types`
9
+ * so the plugin stays parser-agnostic: the same rules run under `espree`,
10
+ * `@typescript-eslint/parser`, `vue-eslint-parser` and oxlint's JS plugin
11
+ * bridge, none of which agree on a type package.
12
+ */
13
+ /** Any AST node, whatever the parser. Narrowed through the guards below. */
14
+ type AstNode = {
15
+ type: string;
16
+ [key: string]: unknown;
17
+ };
18
+ /** An AST node as ESLint hands it to a visitor — always linked to its parent. */
19
+ type AstNodeWithParent = AstNode & {
20
+ parent?: AstNodeWithParent;
21
+ };
22
+ /** `<div>Some text</div>` — the raw text between two JSX tags. */
23
+ type JsxTextNode = AstNode & {
24
+ type: 'JSXText';
25
+ value: string;
26
+ };
27
+ /** `title="Some text"` on a JSX element. */
28
+ type JsxAttributeNode = AstNode & {
29
+ type: 'JSXAttribute';
30
+ name: AstNode;
31
+ value: AstNode | null;
32
+ };
33
+ /** The opening tag of a JSX element, holding its name and attributes. */
34
+ type JsxOpeningElementNode = AstNode & {
35
+ type: 'JSXOpeningElement';
36
+ name: AstNode;
37
+ attributes: AstNode[];
38
+ };
39
+ /**
40
+ * Strip TypeScript-only and parenthesised wrappers to reach the expression that
41
+ * actually produces the value.
42
+ *
43
+ * @param node - Expression to unwrap; `null`/`undefined` passes straight through.
44
+ */
45
+ declare const unwrapExpression: <T extends AstNode>(node: T | null | undefined) => T | null;
46
+ /**
47
+ * Read the compile-time string value of an expression, or `null` when the value
48
+ * can only be known at runtime.
49
+ *
50
+ * Deliberately does **not** resolve identifiers, even ones bound to a string
51
+ * constant: the Babel/SWC passes read the argument node itself, so a value that
52
+ * needs constant folding is a value the compiler will not see.
53
+ *
54
+ * @param node - Expression to evaluate.
55
+ */
56
+ declare const getStaticStringValue: (node: AstNode | null | undefined) => string | null;
57
+ /**
58
+ * Name of the function being called, plus whether it was reached as a method.
59
+ *
60
+ * `matchAsMethod` descriptors (`i18n.getFixedT(…)`, `intl.formatMessage(…)`)
61
+ * only match the member form, so the two cases must stay distinguishable.
62
+ *
63
+ * @param callee - The `callee` of a CallExpression.
64
+ */
65
+ declare const getCalleeName: (callee: AstNode | null | undefined) => {
66
+ name: string;
67
+ isMethod: boolean;
68
+ } | null;
69
+ /**
70
+ * Read a string-valued property from an object-literal argument, e.g. the
71
+ * `namespace` of `getTranslations({ namespace: 'home' })`.
72
+ *
73
+ * Returns the property's value node even when it is not static, so callers can
74
+ * tell "absent" (`null`) from "present but dynamic" (a node, no string).
75
+ *
76
+ * @param node - The argument expected to be an object expression.
77
+ * @param propertyName - Property to look up.
78
+ */
79
+ declare const getObjectPropertyValueNode: (node: AstNode | null | undefined, propertyName: string) => AstNode | null;
80
+ /**
81
+ * Name of a JSX attribute, or `null` for spread attributes and namespaced
82
+ * names the plugin does not target.
83
+ *
84
+ * @param attribute - A member of a JSX opening element's `attributes`.
85
+ */
86
+ declare const getJsxAttributeName: (attribute: AstNode | null | undefined) => string | null;
87
+ /**
88
+ * Value node of a JSX attribute, unwrapping `{'…'}` expression containers so
89
+ * `title="Text"` and `title={'Text'}` are treated alike.
90
+ *
91
+ * @param attribute - A JSX attribute node.
92
+ */
93
+ declare const getJsxAttributeValueNode: (attribute: AstNode | null | undefined) => AstNode | null;
94
+ /**
95
+ * Tag name of the JSX element an attribute or child belongs to, e.g. `'code'`
96
+ * for `<code>`. Member and namespaced element names are joined with `.`.
97
+ *
98
+ * @param openingElement - A JSXOpeningElement node.
99
+ */
100
+ declare const getJsxElementName: (openingElement: AstNode | null | undefined) => string | null;
101
+ /** Narrow an ESLint visitor node to the loose {@link AstNode} shape. */
102
+ declare const toAstNode: (node: Node | AstNode) => AstNodeWithParent;
103
+ //#endregion
104
+ export { AstNode, AstNodeWithParent, JsxAttributeNode, JsxOpeningElementNode, JsxTextNode, getCalleeName, getJsxAttributeName, getJsxAttributeValueNode, getJsxElementName, getObjectPropertyValueNode, getStaticStringValue, toAstNode, unwrapExpression };
105
+ //# sourceMappingURL=ast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.d.ts","names":[],"sources":["../../../src/utils/ast.ts"],"mappings":";;;;;;;;;;;;;KAcY;EACV;GACC;;;KAIS,oBAAoB;EAC9B,SAAS;;;KAIC,cAAc;EACxB;EACA;;;KAIU,mBAAmB;EAC7B;EACA,MAAM;EACN,OAAO;;;KAIG,wBAAwB;EAClC;EACA,MAAM;EACN,YAAY;;;;;;;;cAsBD,mBAAoB,UAAU,SACzC,MAAM,yBACL;;;;;;;;;;;cAwBU,uBACX,MAAM;;;;;;;;;cAoCK,gBACX,QAAQ;EACL;EAAc;;;;;;;;;;;;cA8BN,6BACX,MAAM,4BACN,yBACC;;;;;;;cAkCU,sBACX,WAAW;;;;;;;cAeA,2BACX,WAAW,+BACV;;;;;;;cAgBU,oBACX,gBAAgB;;cAqBL,YAAa,MAAM,OAAa,YAAU"}
@@ -0,0 +1,58 @@
1
+ import { AstNode } from "./ast.js";
2
+ import { CallerDescriptor, CallerValueSource } from "@intlayer/config/callers";
3
+ //#region src/utils/callerMatcher.d.ts
4
+ /**
5
+ * Where the dictionary key of a matched call site lives, and whether it is
6
+ * statically knowable.
7
+ */
8
+ type NamespaceResolution = {
9
+ /** The node holding the key, or `null` when the call omits it entirely. */
10
+ node: AstNode | null;
11
+ /** The compile-time key, or `null` when it can only be known at runtime. */
12
+ value: string | null;
13
+ /** The registry entry that described where to look. */
14
+ source: CallerValueSource;
15
+ };
16
+ /** A call expression recognised as an Intlayer content read. */
17
+ type CallerMatch = {
18
+ descriptor: CallerDescriptor;
19
+ /** Resolved namespace, or `null` for callers that take no namespace here. */
20
+ namespace: NamespaceResolution | null;
21
+ };
22
+ /**
23
+ * Module specifiers imported by a file, as they are written in source.
24
+ *
25
+ * `isCallerActive` gates generic caller names (lingui's `t` / `_`) on the file
26
+ * actually importing the library, so this must be collected before matching.
27
+ *
28
+ * @param program - The Program node of the analysed file.
29
+ */
30
+ declare const collectImportSources: (program: AstNode) => Set<string>;
31
+ /**
32
+ * The registry entries that may match inside a file, given what it imports.
33
+ *
34
+ * @param importSources - Specifiers collected by {@link collectImportSources}.
35
+ */
36
+ declare const getActiveDescriptors: (importSources: ReadonlySet<string>) => CallerDescriptor[];
37
+ /**
38
+ * Match a call expression against the shared caller registry.
39
+ *
40
+ * Tries every `namespaceSources` entry in declaration order and keeps the first
41
+ * that yields a node or a value — mirroring how `@intlayer/babel` picks an
42
+ * overload, so the linter and the compiler agree on what a call site means.
43
+ *
44
+ * @param callNode - A CallExpression (or TaggedTemplateExpression) node.
45
+ * @param activeDescriptors - Result of {@link getActiveDescriptors}.
46
+ */
47
+ declare const matchCaller: (callNode: AstNode, activeDescriptors: CallerDescriptor[]) => CallerMatch | null;
48
+ /**
49
+ * Match a JSX element against the registry's JSX-shaped callers,
50
+ * e.g. `<FormattedMessage id="home.title" />` or `<Trans ns="home" …/>`.
51
+ *
52
+ * @param elementName - Tag name of the element.
53
+ * @param activeDescriptors - Result of {@link getActiveDescriptors}.
54
+ */
55
+ declare const matchJsxCaller: (elementName: string | null, activeDescriptors: CallerDescriptor[]) => CallerDescriptor | null;
56
+ //#endregion
57
+ export { CallerMatch, NamespaceResolution, collectImportSources, getActiveDescriptors, matchCaller, matchJsxCaller };
58
+ //# sourceMappingURL=callerMatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callerMatcher.d.ts","names":[],"sources":["../../../src/utils/callerMatcher.ts"],"mappings":";;;;;;;KAkBY;;EAEV,MAAM;;EAEN;;EAEA,QAAQ;;;KAIE;EACV,YAAY;;EAEZ,WAAW;;;;;;;;;;cAWA,uBAAwB,SAAS,YAAU;;;;;;cA2B3C,uBACX,eAAe,wBACd;;;;;;;;;;;cAkFU,cACX,UAAU,SACV,mBAAmB,uBAClB;;;;;;;;cAuDU,iBACX,4BACA,mBAAmB,uBAClB"}
@@ -0,0 +1,23 @@
1
+ import { JSRuleDefinition } from "eslint";
2
+ //#region src/utils/createRule.d.ts
3
+ /**
4
+ * A rule of this plugin, with its option tuple and message ids pinned so
5
+ * `context.options` and `context.report({ messageId })` are checked rather than
6
+ * inferred as `any[]` / `string`.
7
+ */
8
+ type IntlayerRule<Options extends readonly unknown[] = [], MessageIds extends string = string> = JSRuleDefinition<{
9
+ RuleOptions: [...Options];
10
+ MessageIds: MessageIds;
11
+ }>;
12
+ /**
13
+ * Build a rule with the plugin's shared metadata conventions applied: a
14
+ * documentation anchor derived from the rule name, and `meta.schema` defaulting
15
+ * to "no options" so an unknown option is rejected instead of ignored.
16
+ *
17
+ * @param name - The rule name as it appears in config, e.g. `'no-raw-text'`.
18
+ * @param rule - The rule definition.
19
+ */
20
+ declare const createRule: <Options extends readonly unknown[] = [], MessageIds extends string = string>(name: string, rule: IntlayerRule<Options, MessageIds>) => IntlayerRule<Options, MessageIds>;
21
+ //#endregion
22
+ export { IntlayerRule, createRule };
23
+ //# sourceMappingURL=createRule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createRule.d.ts","names":[],"sources":["../../../src/utils/createRule.ts"],"mappings":";;;;;;;KAUY,aACV,yCACA,sCACE;EACF,iBAAiB;EACjB,YAAY;;;;;;;;;;cAWD,aACX,yCACA,oCAEA,cACA,MAAM,aAAa,SAAS,gBAC3B,aAAa,SAAS"}
@@ -0,0 +1,71 @@
1
+ import { AstNode, AstNodeWithParent } from "./ast.js";
2
+ //#region src/utils/templates.d.ts
3
+ /**
4
+ * Template-language support for text-coverage rules.
5
+ *
6
+ * Intlayer ships integrations for React, Vue, Svelte, Angular, Astro, Solid,
7
+ * Preact and Lit, and each ecosystem's ESLint parser models markup differently:
8
+ *
9
+ * | Framework | Parser | Text node | Attribute node | Lives in |
10
+ * | --------- | ------------------------------- | ----------- | --------------- | --------------- |
11
+ * | React/JSX | espree, typescript-eslint | `JSXText` | `JSXAttribute` | main AST |
12
+ * | Astro | astro-eslint-parser | `JSXText` | `JSXAttribute` | main AST |
13
+ * | Svelte | svelte-eslint-parser | `SvelteText`| `SvelteAttribute`| main AST |
14
+ * | Angular | @angular-eslint/template-parser | `Text` | `TextAttribute` | main AST |
15
+ * | Vue | vue-eslint-parser | `VText` | `VAttribute` | `templateBody` |
16
+ *
17
+ * Vue is the only one that hides its template behind
18
+ * `parserServices.defineTemplateBodyVisitor`; every other parser exposes markup
19
+ * in the AST a normal visitor already walks. Node type names do not collide
20
+ * across parsers, so one merged visitor is safe to hand to any of them — a
21
+ * Svelte file simply never emits a `VText`.
22
+ */
23
+ /** One piece of literal text found in markup. */
24
+ type TemplateTextFinding = {
25
+ /** Node to report on. */
26
+ node: AstNode;
27
+ /** The literal text. */
28
+ text: string;
29
+ /** Attribute name when the text is an attribute value, else `null`. */
30
+ attributeName: string | null;
31
+ /** Tag name of the enclosing element, when resolvable. */
32
+ elementName: string | null;
33
+ };
34
+ /** Callback invoked for every literal text node the visitors find. */
35
+ type TemplateTextReporter = (finding: TemplateTextFinding) => void;
36
+ /** A visitor object as returned from a rule's `create`. */
37
+ type TemplateVisitor = Record<string, (node: never) => void>;
38
+ /**
39
+ * Walk up to the nearest enclosing element and return its tag name, or the
40
+ * sentinel `false` when the text sits inside a `<script>`/`<style>` block that
41
+ * should never be reported.
42
+ *
43
+ * @param node - The text or attribute node to start from.
44
+ */
45
+ declare const resolveEnclosingElement: (node: AstNodeWithParent | undefined) => string | null | false;
46
+ /**
47
+ * Visitors covering every non-JSX template language Intlayer supports.
48
+ *
49
+ * JSX (React, Preact, Solid, Astro, Lit templates compiled through JSX) is
50
+ * handled by the rule itself, since `JSXText`/`JSXAttribute` also need the
51
+ * non-template code paths.
52
+ *
53
+ * @param report - Called for each literal text node found.
54
+ */
55
+ declare const createTemplateVisitor: (report: TemplateTextReporter) => TemplateVisitor;
56
+ /**
57
+ * Combine a script visitor and a template visitor into the single object the
58
+ * active parser expects.
59
+ *
60
+ * Vue keeps its template in a side AST reachable only through
61
+ * `defineTemplateBodyVisitor`; every other parser puts markup in the main AST,
62
+ * where a merged visitor object is enough.
63
+ *
64
+ * @param parserServices - `context.sourceCode.parserServices`.
65
+ * @param scriptVisitor - Handlers for ordinary script nodes.
66
+ * @param templateVisitor - Handlers for markup nodes.
67
+ */
68
+ declare const combineVisitors: (parserServices: unknown, scriptVisitor: TemplateVisitor, templateVisitor: TemplateVisitor) => TemplateVisitor;
69
+ //#endregion
70
+ export { TemplateTextFinding, TemplateTextReporter, TemplateVisitor, combineVisitors, createTemplateVisitor, resolveEnclosingElement };
71
+ //# sourceMappingURL=templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.d.ts","names":[],"sources":["../../../src/utils/templates.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;KAwBY;;EAEV,MAAM;;EAEN;;EAEA;;EAEA;;;KAIU,wBAAwB,SAAS;;KAGjC,kBAAkB,gBAAgB;;;;;;;;cAsDjC,0BACX,MAAM;;;;;;;;;;cA6CK,wBACX,QAAQ,yBACP;;;;;;;;;;;;;cAgGU,kBACX,yBACA,eAAe,iBACf,iBAAiB,oBAChB"}