@rnzeus/eslint-plugin 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 rnzeus
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,165 @@
1
+ # @rnzeus/eslint-plugin-style
2
+
3
+ ESLint plugin for React / React Native projects focused on **style discipline**.
4
+
5
+ This plugin is designed to solve real-world problems that appear during refactoring:
6
+ - unused styles left in `*.styles.ts` files
7
+ - inconsistent style naming
8
+ - unsafe dynamic style usage
9
+ - passing whole `styles` objects to components
10
+
11
+ The plugin is **static**, **runtime-free**, and optimized for **ESLint Flat Config (ESLint v9+)**.
12
+
13
+ ---
14
+
15
+ ## Features
16
+
17
+ ### ✅ `rnzeus/styles-usage`
18
+ Ensures that all styles declared in `*.styles.ts(x)` files are actually used in the corresponding component file.
19
+
20
+ **What it checks:**
21
+ - detects unused style keys
22
+ - forbids passing the whole `styles` object (`styles={styles}`)
23
+ - supports arrays: `style={[styles.foo, styles.bar]}`
24
+ - supports conditional styles
25
+ - supports dynamic styles **only with explicit directive comments**
26
+
27
+ **File convention (required):**
28
+ ```
29
+ Component.tsx
30
+ Component.styles.ts
31
+ ```
32
+
33
+ ---
34
+
35
+ ### ✅ `rnzeus/styles-naming`
36
+ Enforces **snake_case** naming for style keys.
37
+
38
+ **Example:**
39
+ ```ts
40
+ // ❌ bad
41
+ itemName: {}
42
+
43
+ // ✅ good
44
+ item_name: {}
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ npm install -D @rnzeus/eslint-plugin-style
53
+ # or
54
+ yarn add -D @rnzeus/eslint-plugin-style
55
+ ```
56
+
57
+ > Peer dependency: `eslint >= 9`
58
+
59
+ ---
60
+
61
+ ## Usage (ESLint Flat Config)
62
+
63
+ This plugin exports **ready-to-use flat config presets**.
64
+ No `.default` access and no manual plugin wiring is required.
65
+
66
+ ### Import preset
67
+
68
+ ```js
69
+ const styles = require('@rnzeus/eslint-plugin-style/configs/styles');
70
+ ```
71
+
72
+ ### Minimal setup
73
+
74
+ ```js
75
+ module.exports = [
76
+ ...styles,
77
+ ];
78
+ ```
79
+
80
+ ### With other presets
81
+
82
+ ```js
83
+ const rnfsd = require('@rnzeus/themis/eslint/rnfsd');
84
+ const styles = require('@rnzeus/eslint-plugin-style/configs/styles');
85
+
86
+ module.exports = [
87
+ ...rnfsd,
88
+ ...styles,
89
+ ];
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Dynamic styles (important)
95
+
96
+ Dynamic access like this is **not allowed by default**:
97
+
98
+ ```tsx
99
+ <View style={styles[variant]} />
100
+ ```
101
+
102
+ You **must** explicitly document allowed keys using a directive comment:
103
+
104
+ ```tsx
105
+ // rnzeus-styles-used: primary | secondary
106
+ <View style={styles[variant]} />
107
+ ```
108
+
109
+ Supported comment forms:
110
+ ```ts
111
+ // rnzeus-styles-used: a | b
112
+ /* rnzeus-styles-used: a | b */
113
+ ```
114
+
115
+ This makes dynamic styles:
116
+ - explicit
117
+ - reviewable
118
+ - safe during refactors
119
+
120
+ ---
121
+
122
+ ## Rules
123
+
124
+ ### `rnzeus/styles-usage`
125
+
126
+ | Check | Status |
127
+ |-----|------|
128
+ Unused styles | ✅ |
129
+ Whole styles object | ❌ |
130
+ Array styles | ✅ |
131
+ Conditional styles | ✅ |
132
+ Dynamic styles | ⚠️ (directive required) |
133
+
134
+ ---
135
+
136
+ ### `rnzeus/styles-naming`
137
+
138
+ | Rule | Status |
139
+ |----|----|
140
+ snake_case only | ✅ |
141
+ camelCase | ❌ |
142
+ PascalCase | ❌ |
143
+
144
+ Regex used:
145
+ ```ts
146
+ /^[a-z][a-z0-9_]*$/
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Philosophy
152
+
153
+ This plugin intentionally:
154
+ - avoids runtime helpers
155
+ - avoids TypeScript compiler APIs
156
+ - avoids project-wide analysis
157
+ - favors strict conventions over heuristics
158
+
159
+ The goal is **predictability**, **performance**, and **refactor safety**.
160
+
161
+ ---
162
+
163
+ ## License
164
+
165
+ MIT
@@ -0,0 +1,406 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/rules/styles-usage.ts
26
+ var import_node_fs = __toESM(require("fs"));
27
+ var import_node_path = __toESM(require("path"));
28
+ var DEFAULTS = {
29
+ stylesSuffix: ".styles",
30
+ stylesExtensions: ["ts", "tsx"],
31
+ directiveTag: "rnzeus-styles-used",
32
+ stylesObjectName: "styles"
33
+ };
34
+ var stylesKeysCache = /* @__PURE__ */ new Map();
35
+ var MAX_CACHE = 200;
36
+ function cacheSet(file, value) {
37
+ if (stylesKeysCache.size >= MAX_CACHE) {
38
+ const firstKey = stylesKeysCache.keys().next().value;
39
+ if (firstKey) stylesKeysCache.delete(firstKey);
40
+ }
41
+ stylesKeysCache.set(file, value);
42
+ }
43
+ function fileProbablyUsesStyles(sourceCode, stylesSuffix) {
44
+ const ast = sourceCode.ast;
45
+ const body = ast?.body ?? [];
46
+ for (const n of body) {
47
+ if (n.type !== "ImportDeclaration") continue;
48
+ const v = n.source?.value;
49
+ if (typeof v === "string" && v.includes(stylesSuffix)) return true;
50
+ }
51
+ return sourceCode.getText().includes("styles.");
52
+ }
53
+ function extractStyleKeysFromText(text) {
54
+ const keys = /* @__PURE__ */ new Set();
55
+ const idx = text.indexOf("StyleSheet.create");
56
+ if (idx === -1) return keys;
57
+ const braceStart = text.indexOf("{", idx);
58
+ if (braceStart === -1) return keys;
59
+ let depth = 0;
60
+ let readingKey = false;
61
+ let currentKey = "";
62
+ for (let i = braceStart; i < text.length; i++) {
63
+ const ch = text[i];
64
+ if (ch === "{") {
65
+ depth++;
66
+ continue;
67
+ }
68
+ if (ch === "}") {
69
+ depth--;
70
+ if (depth === 0) break;
71
+ continue;
72
+ }
73
+ if (depth !== 1) continue;
74
+ if (!readingKey && /[A-Za-z_$]/.test(ch)) {
75
+ readingKey = true;
76
+ currentKey = ch;
77
+ continue;
78
+ }
79
+ if (readingKey) {
80
+ if (/[\w$]/.test(ch)) {
81
+ currentKey += ch;
82
+ continue;
83
+ }
84
+ if (ch === ":") {
85
+ keys.add(currentKey);
86
+ readingKey = false;
87
+ currentKey = "";
88
+ continue;
89
+ }
90
+ readingKey = false;
91
+ currentKey = "";
92
+ }
93
+ }
94
+ return keys;
95
+ }
96
+ function resolveSiblingStylesFile(filename, stylesSuffix, stylesExtensions) {
97
+ const dir = import_node_path.default.dirname(filename);
98
+ const ext = import_node_path.default.extname(filename);
99
+ const base = import_node_path.default.basename(filename, ext);
100
+ for (const e of stylesExtensions) {
101
+ const candidate = import_node_path.default.join(dir, `${base}${stylesSuffix}.${e}`);
102
+ if (import_node_fs.default.existsSync(candidate)) return candidate;
103
+ }
104
+ return null;
105
+ }
106
+ function parseDirectiveKeys(sourceCode, directiveTag) {
107
+ const used = /* @__PURE__ */ new Set();
108
+ const tag = directiveTag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
109
+ const re = new RegExp(`${tag}\\s*:\\s*([^\\n\\r*]+)`, "i");
110
+ for (const c of sourceCode.getAllComments()) {
111
+ const match = c.value.match(re);
112
+ if (!match) continue;
113
+ match[1].split("|").map((s) => s.trim()).filter(Boolean).forEach((k) => used.add(k));
114
+ }
115
+ return used;
116
+ }
117
+ function isIdentifier(node, name) {
118
+ return node?.type === "Identifier" && node.name === name;
119
+ }
120
+ function isStylesMemberExpression(node, stylesObjectName) {
121
+ if (node?.type !== "MemberExpression") return null;
122
+ if (!isIdentifier(node.object, stylesObjectName)) return null;
123
+ if (node.computed) {
124
+ if (node.property?.type === "Literal" && typeof node.property.value === "string") {
125
+ return { key: node.property.value, computed: true };
126
+ }
127
+ return { key: "", computed: true };
128
+ }
129
+ if (node.property?.type === "Identifier") {
130
+ return { key: node.property.name, computed: false };
131
+ }
132
+ return null;
133
+ }
134
+ function findBestReportNode(sourceCode) {
135
+ const ast = sourceCode.ast;
136
+ const body = ast?.body ?? [];
137
+ for (const n of body) {
138
+ if (n.type !== "ImportDeclaration") continue;
139
+ const v = n.source?.value;
140
+ if (typeof v === "string" && v.includes(".styles")) return n;
141
+ }
142
+ return ast;
143
+ }
144
+ var rule = {
145
+ meta: {
146
+ type: "problem",
147
+ docs: {
148
+ description: "Ensures styles declared in sibling <File>.styles.ts(x) are used in <File>.ts(x)."
149
+ },
150
+ schema: [
151
+ {
152
+ type: "object",
153
+ properties: {
154
+ stylesSuffix: { type: "string" },
155
+ stylesExtensions: { type: "array", items: { type: "string" } },
156
+ directiveTag: { type: "string" },
157
+ stylesObjectName: { type: "string" }
158
+ },
159
+ additionalProperties: false
160
+ }
161
+ ],
162
+ messages: {
163
+ unusedStyles: "Unused styles in {{stylesFile}}: {{keys}}",
164
+ noWholeStylesObject: "Do not pass the whole styles object. Use style={styles.foo}.",
165
+ dynamicNeedsDirective: "Dynamic styles access ({{stylesName}}[...]) requires directive: {{tag}}"
166
+ }
167
+ },
168
+ create(context) {
169
+ const opt = context.options?.[0] ?? {};
170
+ const stylesSuffix = opt.stylesSuffix ?? DEFAULTS.stylesSuffix;
171
+ const stylesExtensions = opt.stylesExtensions ?? DEFAULTS.stylesExtensions;
172
+ const directiveTag = opt.directiveTag ?? DEFAULTS.directiveTag;
173
+ const stylesObjectName = opt.stylesObjectName ?? DEFAULTS.stylesObjectName;
174
+ const filename = context.getFilename();
175
+ const sourceCode = context.getSourceCode();
176
+ if (!fileProbablyUsesStyles(sourceCode, stylesSuffix)) return {};
177
+ const ext = import_node_path.default.extname(filename);
178
+ if (ext !== ".ts" && ext !== ".tsx") return {};
179
+ const stylesPath = resolveSiblingStylesFile(
180
+ filename,
181
+ stylesSuffix,
182
+ stylesExtensions
183
+ );
184
+ if (!stylesPath) return {};
185
+ let declaredKeys = /* @__PURE__ */ new Set();
186
+ try {
187
+ const stat = import_node_fs.default.statSync(stylesPath);
188
+ const cached = stylesKeysCache.get(stylesPath);
189
+ if (!cached || cached.mtimeMs !== stat.mtimeMs) {
190
+ const text = import_node_fs.default.readFileSync(stylesPath, "utf8");
191
+ const keys = extractStyleKeysFromText(text);
192
+ cacheSet(stylesPath, { mtimeMs: stat.mtimeMs, keys });
193
+ declaredKeys = keys;
194
+ } else {
195
+ declaredKeys = cached.keys;
196
+ }
197
+ } catch {
198
+ declaredKeys = /* @__PURE__ */ new Set();
199
+ }
200
+ const usedKeys = /* @__PURE__ */ new Set();
201
+ const directiveKeys = parseDirectiveKeys(sourceCode, directiveTag);
202
+ directiveKeys.forEach((k) => usedKeys.add(k));
203
+ let sawDynamic = false;
204
+ let firstDynamicNode = null;
205
+ function recordFromNode(node) {
206
+ const info = isStylesMemberExpression(node, stylesObjectName);
207
+ if (!info) return;
208
+ if (info.key) {
209
+ usedKeys.add(info.key);
210
+ } else {
211
+ sawDynamic = true;
212
+ if (!firstDynamicNode) firstDynamicNode = node;
213
+ }
214
+ }
215
+ function checkNoWholeStylesObject(node) {
216
+ if (node.name?.type !== "JSXIdentifier") return;
217
+ if (node.name.name !== "styles") return;
218
+ const expr = node.value?.expression;
219
+ if (isIdentifier(expr, stylesObjectName)) {
220
+ context.report({ node, messageId: "noWholeStylesObject" });
221
+ }
222
+ }
223
+ function visitExpression(expr) {
224
+ if (!expr) return;
225
+ recordFromNode(expr);
226
+ if (expr.type === "ArrayExpression") {
227
+ expr.elements?.forEach(visitExpression);
228
+ } else if (expr.type === "ObjectExpression") {
229
+ expr.properties?.forEach((p) => visitExpression(p.value));
230
+ } else if (expr.type === "ConditionalExpression") {
231
+ visitExpression(expr.consequent);
232
+ visitExpression(expr.alternate);
233
+ } else if (expr.type === "LogicalExpression") {
234
+ visitExpression(expr.left);
235
+ visitExpression(expr.right);
236
+ }
237
+ }
238
+ function onJSXAttribute(node) {
239
+ if (node.name?.name === "style") {
240
+ const expr = node.value?.expression;
241
+ if (expr) visitExpression(expr);
242
+ }
243
+ checkNoWholeStylesObject(node);
244
+ }
245
+ function finish() {
246
+ if (sawDynamic && directiveKeys.size === 0 && firstDynamicNode) {
247
+ context.report({
248
+ node: firstDynamicNode,
249
+ messageId: "dynamicNeedsDirective",
250
+ data: { tag: directiveTag, stylesName: stylesObjectName }
251
+ });
252
+ }
253
+ const unused = [...declaredKeys].filter((k) => !usedKeys.has(k));
254
+ if (unused.length) {
255
+ context.report({
256
+ node: findBestReportNode(sourceCode),
257
+ messageId: "unusedStyles",
258
+ data: {
259
+ stylesFile: import_node_path.default.basename(stylesPath),
260
+ keys: unused.sort().join(", ")
261
+ }
262
+ });
263
+ }
264
+ }
265
+ return {
266
+ JSXAttribute: onJSXAttribute,
267
+ "Program:exit": finish
268
+ };
269
+ }
270
+ };
271
+ var styles_usage_default = rule;
272
+
273
+ // src/rules/styles-naming.ts
274
+ var import_node_path2 = __toESM(require("path"));
275
+ var DEFAULTS2 = {
276
+ stylesSuffix: ".styles",
277
+ stylesExtensions: ["ts", "tsx"]
278
+ };
279
+ var SNAKE_CASE_RE = /^[a-z][a-z0-9_]*$/;
280
+ function extractTopLevelStyleKeys(text) {
281
+ const result = [];
282
+ const idx = text.indexOf("StyleSheet.create");
283
+ if (idx === -1) return result;
284
+ const braceStart = text.indexOf("{", idx);
285
+ if (braceStart === -1) return result;
286
+ let depth = 0;
287
+ let readingKey = false;
288
+ let currentKey = "";
289
+ let keyStartIndex = -1;
290
+ for (let i = braceStart; i < text.length; i++) {
291
+ const ch = text[i];
292
+ if (ch === "{") {
293
+ depth++;
294
+ continue;
295
+ }
296
+ if (ch === "}") {
297
+ depth--;
298
+ if (depth === 0) break;
299
+ continue;
300
+ }
301
+ if (depth !== 1) continue;
302
+ if (!readingKey && /[A-Za-z_$]/.test(ch)) {
303
+ readingKey = true;
304
+ currentKey = ch;
305
+ keyStartIndex = i;
306
+ continue;
307
+ }
308
+ if (readingKey) {
309
+ if (/[\w$]/.test(ch)) {
310
+ currentKey += ch;
311
+ continue;
312
+ }
313
+ if (ch === ":") {
314
+ result.push({ key: currentKey, index: keyStartIndex });
315
+ readingKey = false;
316
+ currentKey = "";
317
+ keyStartIndex = -1;
318
+ continue;
319
+ }
320
+ readingKey = false;
321
+ currentKey = "";
322
+ keyStartIndex = -1;
323
+ }
324
+ }
325
+ return result;
326
+ }
327
+ function isStylesFile(filename, stylesSuffix, stylesExtensions) {
328
+ const ext = import_node_path2.default.extname(filename);
329
+ if (!stylesExtensions.includes(ext.replace(".", ""))) return false;
330
+ return import_node_path2.default.basename(filename).includes(stylesSuffix);
331
+ }
332
+ var rule2 = {
333
+ meta: {
334
+ type: "problem",
335
+ docs: {
336
+ description: "Enforce snake_case naming for style keys in *.styles.ts(x) files."
337
+ },
338
+ schema: [
339
+ {
340
+ type: "object",
341
+ properties: {
342
+ stylesSuffix: { type: "string" },
343
+ stylesExtensions: { type: "array", items: { type: "string" } }
344
+ },
345
+ additionalProperties: false
346
+ }
347
+ ],
348
+ messages: {
349
+ invalidName: 'Style name "{{name}}" must be snake_case (e.g. "{{suggestion}}").'
350
+ }
351
+ },
352
+ create(context) {
353
+ const opt = context.options?.[0] ?? {};
354
+ const stylesSuffix = opt.stylesSuffix ?? DEFAULTS2.stylesSuffix;
355
+ const stylesExtensions = opt.stylesExtensions ?? DEFAULTS2.stylesExtensions;
356
+ const filename = context.getFilename();
357
+ if (!isStylesFile(filename, stylesSuffix, stylesExtensions)) {
358
+ return {};
359
+ }
360
+ const sourceCode = context.getSourceCode();
361
+ const text = sourceCode.getText();
362
+ const keys = extractTopLevelStyleKeys(text);
363
+ function toSnakeCase(input) {
364
+ return input.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[-\s]+/g, "_").toLowerCase();
365
+ }
366
+ for (const { key, index } of keys) {
367
+ if (!SNAKE_CASE_RE.test(key)) {
368
+ const loc = sourceCode.getLocFromIndex(index);
369
+ context.report({
370
+ loc: {
371
+ start: loc,
372
+ end: { line: loc.line, column: loc.column + key.length }
373
+ },
374
+ messageId: "invalidName",
375
+ data: {
376
+ name: key,
377
+ suggestion: toSnakeCase(key)
378
+ }
379
+ });
380
+ }
381
+ }
382
+ return {};
383
+ }
384
+ };
385
+ var styles_naming_default = rule2;
386
+
387
+ // src/plugin.ts
388
+ var plugin = {
389
+ rules: {
390
+ "styles-usage": styles_usage_default,
391
+ "styles-naming": styles_naming_default
392
+ }
393
+ };
394
+ var plugin_default = plugin;
395
+
396
+ // src/configs/styles.ts
397
+ module.exports = [
398
+ {
399
+ plugins: { rnzeus: plugin_default },
400
+ rules: {
401
+ "rnzeus/styles-usage": "error",
402
+ "rnzeus/styles-naming": "error"
403
+ }
404
+ }
405
+ ];
406
+ //# sourceMappingURL=styles.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/rules/styles-usage.ts","../../src/rules/styles-naming.ts","../../src/plugin.ts","../../src/configs/styles.ts"],"sourcesContent":["import type { Rule, SourceCode } from \"eslint\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\ntype Options = [\n {\n stylesSuffix?: string;\n stylesExtensions?: string[];\n directiveTag?: string;\n stylesObjectName?: string;\n }\n];\n\nconst DEFAULTS = {\n stylesSuffix: \".styles\",\n stylesExtensions: [\"ts\", \"tsx\"],\n directiveTag: \"rnzeus-styles-used\",\n stylesObjectName: \"styles\",\n} as const;\n\n/**\n * Cache: stylesPath -> { mtimeMs, keys }\n */\nconst stylesKeysCache = new Map<\n string,\n { mtimeMs: number; keys: Set<string> }\n>();\n\nconst MAX_CACHE = 200;\n\nfunction cacheSet(file: string, value: { mtimeMs: number; keys: Set<string> }) {\n if (stylesKeysCache.size >= MAX_CACHE) {\n const firstKey = stylesKeysCache.keys().next().value;\n if (firstKey) stylesKeysCache.delete(firstKey);\n }\n stylesKeysCache.set(file, value);\n}\n\nfunction fileProbablyUsesStyles(\n sourceCode: SourceCode,\n stylesSuffix: string\n): boolean {\n const ast: any = sourceCode.ast;\n const body: any[] = ast?.body ?? [];\n\n for (const n of body) {\n if (n.type !== \"ImportDeclaration\") continue;\n const v = n.source?.value;\n if (typeof v === \"string\" && v.includes(stylesSuffix)) return true;\n }\n\n // cheap fallback\n return sourceCode.getText().includes(\"styles.\");\n}\n\nfunction extractStyleKeysFromText(text: string): Set<string> {\n const keys = new Set<string>();\n\n const idx = text.indexOf(\"StyleSheet.create\");\n if (idx === -1) return keys;\n\n const braceStart = text.indexOf(\"{\", idx);\n if (braceStart === -1) return keys;\n\n let depth = 0;\n let readingKey = false;\n let currentKey = \"\";\n\n for (let i = braceStart; i < text.length; i++) {\n const ch = text[i];\n\n if (ch === \"{\") {\n depth++;\n continue;\n }\n\n if (ch === \"}\") {\n depth--;\n if (depth === 0) break;\n continue;\n }\n\n if (depth !== 1) continue;\n\n if (!readingKey && /[A-Za-z_$]/.test(ch)) {\n readingKey = true;\n currentKey = ch;\n continue;\n }\n\n if (readingKey) {\n if (/[\\w$]/.test(ch)) {\n currentKey += ch;\n continue;\n }\n\n if (ch === \":\") {\n keys.add(currentKey);\n readingKey = false;\n currentKey = \"\";\n continue;\n }\n\n readingKey = false;\n currentKey = \"\";\n }\n }\n\n return keys;\n}\n\nfunction resolveSiblingStylesFile(\n filename: string,\n stylesSuffix: string,\n stylesExtensions: readonly string[]\n): string | null {\n const dir = path.dirname(filename);\n const ext = path.extname(filename);\n const base = path.basename(filename, ext);\n\n for (const e of stylesExtensions) {\n const candidate = path.join(dir, `${base}${stylesSuffix}.${e}`);\n if (fs.existsSync(candidate)) return candidate;\n }\n\n return null;\n}\n\nfunction parseDirectiveKeys(\n sourceCode: SourceCode,\n directiveTag: string\n): Set<string> {\n const used = new Set<string>();\n const tag = directiveTag.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const re = new RegExp(`${tag}\\\\s*:\\\\s*([^\\\\n\\\\r*]+)`, \"i\");\n\n for (const c of sourceCode.getAllComments()) {\n const match = c.value.match(re);\n if (!match) continue;\n\n match[1]\n .split(\"|\")\n .map((s) => s.trim())\n .filter(Boolean)\n .forEach((k) => used.add(k));\n }\n\n return used;\n}\n\nfunction isIdentifier(node: any, name: string): boolean {\n return node?.type === \"Identifier\" && node.name === name;\n}\n\nfunction isStylesMemberExpression(\n node: any,\n stylesObjectName: string\n): { key: string; computed: boolean } | null {\n if (node?.type !== \"MemberExpression\") return null;\n if (!isIdentifier(node.object, stylesObjectName)) return null;\n\n if (node.computed) {\n if (\n node.property?.type === \"Literal\" &&\n typeof node.property.value === \"string\"\n ) {\n return { key: node.property.value, computed: true };\n }\n return { key: \"\", computed: true };\n }\n\n if (node.property?.type === \"Identifier\") {\n return { key: node.property.name, computed: false };\n }\n\n return null;\n}\n\nfunction findBestReportNode(sourceCode: SourceCode): any {\n const ast: any = sourceCode.ast;\n const body: any[] = ast?.body ?? [];\n\n for (const n of body) {\n if (n.type !== \"ImportDeclaration\") continue;\n const v = n.source?.value;\n if (typeof v === \"string\" && v.includes(\".styles\")) return n;\n }\n\n return ast;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Ensures styles declared in sibling <File>.styles.ts(x) are used in <File>.ts(x).\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n stylesSuffix: { type: \"string\" },\n stylesExtensions: { type: \"array\", items: { type: \"string\" } },\n directiveTag: { type: \"string\" },\n stylesObjectName: { type: \"string\" },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n unusedStyles: \"Unused styles in {{stylesFile}}: {{keys}}\",\n noWholeStylesObject:\n \"Do not pass the whole styles object. Use style={styles.foo}.\",\n dynamicNeedsDirective:\n \"Dynamic styles access ({{stylesName}}[...]) requires directive: {{tag}}\",\n },\n },\n\n create(context) {\n const opt = (context.options?.[0] ?? {}) as Options[0];\n\n const stylesSuffix = opt.stylesSuffix ?? DEFAULTS.stylesSuffix;\n const stylesExtensions = opt.stylesExtensions ?? DEFAULTS.stylesExtensions;\n const directiveTag = opt.directiveTag ?? DEFAULTS.directiveTag;\n const stylesObjectName = opt.stylesObjectName ?? DEFAULTS.stylesObjectName;\n\n const filename = context.getFilename();\n const sourceCode = context.getSourceCode();\n\n if (!fileProbablyUsesStyles(sourceCode, stylesSuffix)) return {};\n\n const ext = path.extname(filename);\n if (ext !== \".ts\" && ext !== \".tsx\") return {};\n\n const stylesPath = resolveSiblingStylesFile(\n filename,\n stylesSuffix,\n stylesExtensions\n );\n if (!stylesPath) return {};\n\n let declaredKeys: Set<string> = new Set();\n\n try {\n const stat = fs.statSync(stylesPath);\n const cached = stylesKeysCache.get(stylesPath);\n\n if (!cached || cached.mtimeMs !== stat.mtimeMs) {\n const text = fs.readFileSync(stylesPath, \"utf8\");\n const keys = extractStyleKeysFromText(text);\n cacheSet(stylesPath, { mtimeMs: stat.mtimeMs, keys });\n declaredKeys = keys;\n } else {\n declaredKeys = cached.keys;\n }\n } catch {\n declaredKeys = new Set();\n }\n\n const usedKeys = new Set<string>();\n const directiveKeys = parseDirectiveKeys(sourceCode, directiveTag);\n directiveKeys.forEach((k) => usedKeys.add(k));\n\n let sawDynamic = false;\n let firstDynamicNode: any | null = null;\n\n function recordFromNode(node: any) {\n const info = isStylesMemberExpression(node, stylesObjectName);\n if (!info) return;\n\n if (info.key) {\n usedKeys.add(info.key);\n } else {\n sawDynamic = true;\n if (!firstDynamicNode) firstDynamicNode = node;\n }\n }\n\n function checkNoWholeStylesObject(node: any) {\n if (node.name?.type !== \"JSXIdentifier\") return;\n if (node.name.name !== \"styles\") return;\n\n const expr = node.value?.expression;\n if (isIdentifier(expr, stylesObjectName)) {\n context.report({ node, messageId: \"noWholeStylesObject\" });\n }\n }\n\n function visitExpression(expr: any) {\n if (!expr) return;\n\n recordFromNode(expr);\n\n if (expr.type === \"ArrayExpression\") {\n expr.elements?.forEach(visitExpression);\n } else if (expr.type === \"ObjectExpression\") {\n expr.properties?.forEach((p: any) => visitExpression(p.value));\n } else if (expr.type === \"ConditionalExpression\") {\n visitExpression(expr.consequent);\n visitExpression(expr.alternate);\n } else if (expr.type === \"LogicalExpression\") {\n visitExpression(expr.left);\n visitExpression(expr.right);\n }\n }\n\n function onJSXAttribute(node: any) {\n if (node.name?.name === \"style\") {\n const expr = node.value?.expression;\n if (expr) visitExpression(expr);\n }\n checkNoWholeStylesObject(node);\n }\n\n function finish() {\n if (sawDynamic && directiveKeys.size === 0 && firstDynamicNode) {\n context.report({\n node: firstDynamicNode,\n messageId: \"dynamicNeedsDirective\",\n data: { tag: directiveTag, stylesName: stylesObjectName },\n });\n }\n\n const unused = [...declaredKeys].filter((k) => !usedKeys.has(k));\n if (unused.length) {\n context.report({\n node: findBestReportNode(sourceCode),\n messageId: \"unusedStyles\",\n data: {\n stylesFile: path.basename(stylesPath as string),\n keys: unused.sort().join(\", \"),\n },\n });\n }\n }\n\n return {\n JSXAttribute: onJSXAttribute,\n \"Program:exit\": finish,\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from \"eslint\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\ntype Options = [\n {\n /**\n * Suffix for styles files.\n * Default: \".styles\"\n */\n stylesSuffix?: string;\n\n /**\n * Allowed extensions.\n * Default: [\"ts\", \"tsx\"]\n */\n stylesExtensions?: string[];\n }\n];\n\nconst DEFAULTS = {\n stylesSuffix: \".styles\",\n stylesExtensions: [\"ts\", \"tsx\"],\n} as const;\n\nconst SNAKE_CASE_RE = /^[a-z][a-z0-9_]*$/;\n\nfunction extractTopLevelStyleKeys(\n text: string\n): { key: string; index: number }[] {\n const result: { key: string; index: number }[] = [];\n\n const idx = text.indexOf(\"StyleSheet.create\");\n if (idx === -1) return result;\n\n const braceStart = text.indexOf(\"{\", idx);\n if (braceStart === -1) return result;\n\n let depth = 0;\n let readingKey = false;\n let currentKey = \"\";\n let keyStartIndex = -1;\n\n for (let i = braceStart; i < text.length; i++) {\n const ch = text[i];\n\n if (ch === \"{\") {\n depth++;\n continue;\n }\n\n if (ch === \"}\") {\n depth--;\n if (depth === 0) break;\n continue;\n }\n\n if (depth !== 1) continue;\n\n if (!readingKey && /[A-Za-z_$]/.test(ch)) {\n readingKey = true;\n currentKey = ch;\n keyStartIndex = i;\n continue;\n }\n\n if (readingKey) {\n if (/[\\w$]/.test(ch)) {\n currentKey += ch;\n continue;\n }\n\n if (ch === \":\") {\n result.push({ key: currentKey, index: keyStartIndex });\n readingKey = false;\n currentKey = \"\";\n keyStartIndex = -1;\n continue;\n }\n\n readingKey = false;\n currentKey = \"\";\n keyStartIndex = -1;\n }\n }\n\n return result;\n}\n\nfunction isStylesFile(\n filename: string,\n stylesSuffix: string,\n stylesExtensions: readonly string[]\n): boolean {\n const ext = path.extname(filename);\n if (!stylesExtensions.includes(ext.replace(\".\", \"\"))) return false;\n return path.basename(filename).includes(stylesSuffix);\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Enforce snake_case naming for style keys in *.styles.ts(x) files.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n stylesSuffix: { type: \"string\" },\n stylesExtensions: { type: \"array\", items: { type: \"string\" } },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n invalidName:\n 'Style name \"{{name}}\" must be snake_case (e.g. \"{{suggestion}}\").',\n },\n },\n\n create(context) {\n const opt = (context.options?.[0] ?? {}) as Options[0];\n\n const stylesSuffix = opt.stylesSuffix ?? DEFAULTS.stylesSuffix;\n const stylesExtensions = opt.stylesExtensions ?? DEFAULTS.stylesExtensions;\n\n const filename = context.getFilename();\n if (!isStylesFile(filename, stylesSuffix, stylesExtensions)) {\n return {};\n }\n\n const sourceCode = context.getSourceCode();\n const text = sourceCode.getText();\n\n const keys = extractTopLevelStyleKeys(text);\n\n function toSnakeCase(input: string): string {\n return input\n .replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\n .replace(/[-\\s]+/g, \"_\")\n .toLowerCase();\n }\n\n for (const { key, index } of keys) {\n if (!SNAKE_CASE_RE.test(key)) {\n const loc = sourceCode.getLocFromIndex(index);\n\n context.report({\n loc: {\n start: loc,\n end: { line: loc.line, column: loc.column + key.length },\n },\n messageId: \"invalidName\",\n data: {\n name: key,\n suggestion: toSnakeCase(key),\n },\n });\n }\n }\n\n return {};\n },\n};\n\nexport default rule;\n","import stylesUsageRule from \"./rules/styles-usage\";\nimport stylesNamingRule from \"./rules/styles-naming\";\n\nconst plugin = {\n rules: {\n \"styles-usage\": stylesUsageRule,\n \"styles-naming\": stylesNamingRule,\n },\n};\n\nexport default plugin;\n","import plugin from \"../plugin\";\n\nexport = [\n {\n plugins: { rnzeus: plugin },\n rules: {\n \"rnzeus/styles-usage\": \"error\",\n \"rnzeus/styles-naming\": \"error\",\n },\n },\n];\n"],"mappings":"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","names":["path","fs","import_node_path","DEFAULTS","path","rule"]}
@@ -0,0 +1,407 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/plugin.ts
31
+ var plugin_exports = {};
32
+ __export(plugin_exports, {
33
+ default: () => plugin_default
34
+ });
35
+ module.exports = __toCommonJS(plugin_exports);
36
+
37
+ // src/rules/styles-usage.ts
38
+ var import_node_fs = __toESM(require("fs"));
39
+ var import_node_path = __toESM(require("path"));
40
+ var DEFAULTS = {
41
+ stylesSuffix: ".styles",
42
+ stylesExtensions: ["ts", "tsx"],
43
+ directiveTag: "rnzeus-styles-used",
44
+ stylesObjectName: "styles"
45
+ };
46
+ var stylesKeysCache = /* @__PURE__ */ new Map();
47
+ var MAX_CACHE = 200;
48
+ function cacheSet(file, value) {
49
+ if (stylesKeysCache.size >= MAX_CACHE) {
50
+ const firstKey = stylesKeysCache.keys().next().value;
51
+ if (firstKey) stylesKeysCache.delete(firstKey);
52
+ }
53
+ stylesKeysCache.set(file, value);
54
+ }
55
+ function fileProbablyUsesStyles(sourceCode, stylesSuffix) {
56
+ const ast = sourceCode.ast;
57
+ const body = ast?.body ?? [];
58
+ for (const n of body) {
59
+ if (n.type !== "ImportDeclaration") continue;
60
+ const v = n.source?.value;
61
+ if (typeof v === "string" && v.includes(stylesSuffix)) return true;
62
+ }
63
+ return sourceCode.getText().includes("styles.");
64
+ }
65
+ function extractStyleKeysFromText(text) {
66
+ const keys = /* @__PURE__ */ new Set();
67
+ const idx = text.indexOf("StyleSheet.create");
68
+ if (idx === -1) return keys;
69
+ const braceStart = text.indexOf("{", idx);
70
+ if (braceStart === -1) return keys;
71
+ let depth = 0;
72
+ let readingKey = false;
73
+ let currentKey = "";
74
+ for (let i = braceStart; i < text.length; i++) {
75
+ const ch = text[i];
76
+ if (ch === "{") {
77
+ depth++;
78
+ continue;
79
+ }
80
+ if (ch === "}") {
81
+ depth--;
82
+ if (depth === 0) break;
83
+ continue;
84
+ }
85
+ if (depth !== 1) continue;
86
+ if (!readingKey && /[A-Za-z_$]/.test(ch)) {
87
+ readingKey = true;
88
+ currentKey = ch;
89
+ continue;
90
+ }
91
+ if (readingKey) {
92
+ if (/[\w$]/.test(ch)) {
93
+ currentKey += ch;
94
+ continue;
95
+ }
96
+ if (ch === ":") {
97
+ keys.add(currentKey);
98
+ readingKey = false;
99
+ currentKey = "";
100
+ continue;
101
+ }
102
+ readingKey = false;
103
+ currentKey = "";
104
+ }
105
+ }
106
+ return keys;
107
+ }
108
+ function resolveSiblingStylesFile(filename, stylesSuffix, stylesExtensions) {
109
+ const dir = import_node_path.default.dirname(filename);
110
+ const ext = import_node_path.default.extname(filename);
111
+ const base = import_node_path.default.basename(filename, ext);
112
+ for (const e of stylesExtensions) {
113
+ const candidate = import_node_path.default.join(dir, `${base}${stylesSuffix}.${e}`);
114
+ if (import_node_fs.default.existsSync(candidate)) return candidate;
115
+ }
116
+ return null;
117
+ }
118
+ function parseDirectiveKeys(sourceCode, directiveTag) {
119
+ const used = /* @__PURE__ */ new Set();
120
+ const tag = directiveTag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
121
+ const re = new RegExp(`${tag}\\s*:\\s*([^\\n\\r*]+)`, "i");
122
+ for (const c of sourceCode.getAllComments()) {
123
+ const match = c.value.match(re);
124
+ if (!match) continue;
125
+ match[1].split("|").map((s) => s.trim()).filter(Boolean).forEach((k) => used.add(k));
126
+ }
127
+ return used;
128
+ }
129
+ function isIdentifier(node, name) {
130
+ return node?.type === "Identifier" && node.name === name;
131
+ }
132
+ function isStylesMemberExpression(node, stylesObjectName) {
133
+ if (node?.type !== "MemberExpression") return null;
134
+ if (!isIdentifier(node.object, stylesObjectName)) return null;
135
+ if (node.computed) {
136
+ if (node.property?.type === "Literal" && typeof node.property.value === "string") {
137
+ return { key: node.property.value, computed: true };
138
+ }
139
+ return { key: "", computed: true };
140
+ }
141
+ if (node.property?.type === "Identifier") {
142
+ return { key: node.property.name, computed: false };
143
+ }
144
+ return null;
145
+ }
146
+ function findBestReportNode(sourceCode) {
147
+ const ast = sourceCode.ast;
148
+ const body = ast?.body ?? [];
149
+ for (const n of body) {
150
+ if (n.type !== "ImportDeclaration") continue;
151
+ const v = n.source?.value;
152
+ if (typeof v === "string" && v.includes(".styles")) return n;
153
+ }
154
+ return ast;
155
+ }
156
+ var rule = {
157
+ meta: {
158
+ type: "problem",
159
+ docs: {
160
+ description: "Ensures styles declared in sibling <File>.styles.ts(x) are used in <File>.ts(x)."
161
+ },
162
+ schema: [
163
+ {
164
+ type: "object",
165
+ properties: {
166
+ stylesSuffix: { type: "string" },
167
+ stylesExtensions: { type: "array", items: { type: "string" } },
168
+ directiveTag: { type: "string" },
169
+ stylesObjectName: { type: "string" }
170
+ },
171
+ additionalProperties: false
172
+ }
173
+ ],
174
+ messages: {
175
+ unusedStyles: "Unused styles in {{stylesFile}}: {{keys}}",
176
+ noWholeStylesObject: "Do not pass the whole styles object. Use style={styles.foo}.",
177
+ dynamicNeedsDirective: "Dynamic styles access ({{stylesName}}[...]) requires directive: {{tag}}"
178
+ }
179
+ },
180
+ create(context) {
181
+ const opt = context.options?.[0] ?? {};
182
+ const stylesSuffix = opt.stylesSuffix ?? DEFAULTS.stylesSuffix;
183
+ const stylesExtensions = opt.stylesExtensions ?? DEFAULTS.stylesExtensions;
184
+ const directiveTag = opt.directiveTag ?? DEFAULTS.directiveTag;
185
+ const stylesObjectName = opt.stylesObjectName ?? DEFAULTS.stylesObjectName;
186
+ const filename = context.getFilename();
187
+ const sourceCode = context.getSourceCode();
188
+ if (!fileProbablyUsesStyles(sourceCode, stylesSuffix)) return {};
189
+ const ext = import_node_path.default.extname(filename);
190
+ if (ext !== ".ts" && ext !== ".tsx") return {};
191
+ const stylesPath = resolveSiblingStylesFile(
192
+ filename,
193
+ stylesSuffix,
194
+ stylesExtensions
195
+ );
196
+ if (!stylesPath) return {};
197
+ let declaredKeys = /* @__PURE__ */ new Set();
198
+ try {
199
+ const stat = import_node_fs.default.statSync(stylesPath);
200
+ const cached = stylesKeysCache.get(stylesPath);
201
+ if (!cached || cached.mtimeMs !== stat.mtimeMs) {
202
+ const text = import_node_fs.default.readFileSync(stylesPath, "utf8");
203
+ const keys = extractStyleKeysFromText(text);
204
+ cacheSet(stylesPath, { mtimeMs: stat.mtimeMs, keys });
205
+ declaredKeys = keys;
206
+ } else {
207
+ declaredKeys = cached.keys;
208
+ }
209
+ } catch {
210
+ declaredKeys = /* @__PURE__ */ new Set();
211
+ }
212
+ const usedKeys = /* @__PURE__ */ new Set();
213
+ const directiveKeys = parseDirectiveKeys(sourceCode, directiveTag);
214
+ directiveKeys.forEach((k) => usedKeys.add(k));
215
+ let sawDynamic = false;
216
+ let firstDynamicNode = null;
217
+ function recordFromNode(node) {
218
+ const info = isStylesMemberExpression(node, stylesObjectName);
219
+ if (!info) return;
220
+ if (info.key) {
221
+ usedKeys.add(info.key);
222
+ } else {
223
+ sawDynamic = true;
224
+ if (!firstDynamicNode) firstDynamicNode = node;
225
+ }
226
+ }
227
+ function checkNoWholeStylesObject(node) {
228
+ if (node.name?.type !== "JSXIdentifier") return;
229
+ if (node.name.name !== "styles") return;
230
+ const expr = node.value?.expression;
231
+ if (isIdentifier(expr, stylesObjectName)) {
232
+ context.report({ node, messageId: "noWholeStylesObject" });
233
+ }
234
+ }
235
+ function visitExpression(expr) {
236
+ if (!expr) return;
237
+ recordFromNode(expr);
238
+ if (expr.type === "ArrayExpression") {
239
+ expr.elements?.forEach(visitExpression);
240
+ } else if (expr.type === "ObjectExpression") {
241
+ expr.properties?.forEach((p) => visitExpression(p.value));
242
+ } else if (expr.type === "ConditionalExpression") {
243
+ visitExpression(expr.consequent);
244
+ visitExpression(expr.alternate);
245
+ } else if (expr.type === "LogicalExpression") {
246
+ visitExpression(expr.left);
247
+ visitExpression(expr.right);
248
+ }
249
+ }
250
+ function onJSXAttribute(node) {
251
+ if (node.name?.name === "style") {
252
+ const expr = node.value?.expression;
253
+ if (expr) visitExpression(expr);
254
+ }
255
+ checkNoWholeStylesObject(node);
256
+ }
257
+ function finish() {
258
+ if (sawDynamic && directiveKeys.size === 0 && firstDynamicNode) {
259
+ context.report({
260
+ node: firstDynamicNode,
261
+ messageId: "dynamicNeedsDirective",
262
+ data: { tag: directiveTag, stylesName: stylesObjectName }
263
+ });
264
+ }
265
+ const unused = [...declaredKeys].filter((k) => !usedKeys.has(k));
266
+ if (unused.length) {
267
+ context.report({
268
+ node: findBestReportNode(sourceCode),
269
+ messageId: "unusedStyles",
270
+ data: {
271
+ stylesFile: import_node_path.default.basename(stylesPath),
272
+ keys: unused.sort().join(", ")
273
+ }
274
+ });
275
+ }
276
+ }
277
+ return {
278
+ JSXAttribute: onJSXAttribute,
279
+ "Program:exit": finish
280
+ };
281
+ }
282
+ };
283
+ var styles_usage_default = rule;
284
+
285
+ // src/rules/styles-naming.ts
286
+ var import_node_path2 = __toESM(require("path"));
287
+ var DEFAULTS2 = {
288
+ stylesSuffix: ".styles",
289
+ stylesExtensions: ["ts", "tsx"]
290
+ };
291
+ var SNAKE_CASE_RE = /^[a-z][a-z0-9_]*$/;
292
+ function extractTopLevelStyleKeys(text) {
293
+ const result = [];
294
+ const idx = text.indexOf("StyleSheet.create");
295
+ if (idx === -1) return result;
296
+ const braceStart = text.indexOf("{", idx);
297
+ if (braceStart === -1) return result;
298
+ let depth = 0;
299
+ let readingKey = false;
300
+ let currentKey = "";
301
+ let keyStartIndex = -1;
302
+ for (let i = braceStart; i < text.length; i++) {
303
+ const ch = text[i];
304
+ if (ch === "{") {
305
+ depth++;
306
+ continue;
307
+ }
308
+ if (ch === "}") {
309
+ depth--;
310
+ if (depth === 0) break;
311
+ continue;
312
+ }
313
+ if (depth !== 1) continue;
314
+ if (!readingKey && /[A-Za-z_$]/.test(ch)) {
315
+ readingKey = true;
316
+ currentKey = ch;
317
+ keyStartIndex = i;
318
+ continue;
319
+ }
320
+ if (readingKey) {
321
+ if (/[\w$]/.test(ch)) {
322
+ currentKey += ch;
323
+ continue;
324
+ }
325
+ if (ch === ":") {
326
+ result.push({ key: currentKey, index: keyStartIndex });
327
+ readingKey = false;
328
+ currentKey = "";
329
+ keyStartIndex = -1;
330
+ continue;
331
+ }
332
+ readingKey = false;
333
+ currentKey = "";
334
+ keyStartIndex = -1;
335
+ }
336
+ }
337
+ return result;
338
+ }
339
+ function isStylesFile(filename, stylesSuffix, stylesExtensions) {
340
+ const ext = import_node_path2.default.extname(filename);
341
+ if (!stylesExtensions.includes(ext.replace(".", ""))) return false;
342
+ return import_node_path2.default.basename(filename).includes(stylesSuffix);
343
+ }
344
+ var rule2 = {
345
+ meta: {
346
+ type: "problem",
347
+ docs: {
348
+ description: "Enforce snake_case naming for style keys in *.styles.ts(x) files."
349
+ },
350
+ schema: [
351
+ {
352
+ type: "object",
353
+ properties: {
354
+ stylesSuffix: { type: "string" },
355
+ stylesExtensions: { type: "array", items: { type: "string" } }
356
+ },
357
+ additionalProperties: false
358
+ }
359
+ ],
360
+ messages: {
361
+ invalidName: 'Style name "{{name}}" must be snake_case (e.g. "{{suggestion}}").'
362
+ }
363
+ },
364
+ create(context) {
365
+ const opt = context.options?.[0] ?? {};
366
+ const stylesSuffix = opt.stylesSuffix ?? DEFAULTS2.stylesSuffix;
367
+ const stylesExtensions = opt.stylesExtensions ?? DEFAULTS2.stylesExtensions;
368
+ const filename = context.getFilename();
369
+ if (!isStylesFile(filename, stylesSuffix, stylesExtensions)) {
370
+ return {};
371
+ }
372
+ const sourceCode = context.getSourceCode();
373
+ const text = sourceCode.getText();
374
+ const keys = extractTopLevelStyleKeys(text);
375
+ function toSnakeCase(input) {
376
+ return input.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[-\s]+/g, "_").toLowerCase();
377
+ }
378
+ for (const { key, index } of keys) {
379
+ if (!SNAKE_CASE_RE.test(key)) {
380
+ const loc = sourceCode.getLocFromIndex(index);
381
+ context.report({
382
+ loc: {
383
+ start: loc,
384
+ end: { line: loc.line, column: loc.column + key.length }
385
+ },
386
+ messageId: "invalidName",
387
+ data: {
388
+ name: key,
389
+ suggestion: toSnakeCase(key)
390
+ }
391
+ });
392
+ }
393
+ }
394
+ return {};
395
+ }
396
+ };
397
+ var styles_naming_default = rule2;
398
+
399
+ // src/plugin.ts
400
+ var plugin = {
401
+ rules: {
402
+ "styles-usage": styles_usage_default,
403
+ "styles-naming": styles_naming_default
404
+ }
405
+ };
406
+ var plugin_default = plugin;
407
+ //# sourceMappingURL=plugin.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugin.ts","../src/rules/styles-usage.ts","../src/rules/styles-naming.ts"],"sourcesContent":["import stylesUsageRule from \"./rules/styles-usage\";\nimport stylesNamingRule from \"./rules/styles-naming\";\n\nconst plugin = {\n rules: {\n \"styles-usage\": stylesUsageRule,\n \"styles-naming\": stylesNamingRule,\n },\n};\n\nexport default plugin;\n","import type { Rule, SourceCode } from \"eslint\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\ntype Options = [\n {\n stylesSuffix?: string;\n stylesExtensions?: string[];\n directiveTag?: string;\n stylesObjectName?: string;\n }\n];\n\nconst DEFAULTS = {\n stylesSuffix: \".styles\",\n stylesExtensions: [\"ts\", \"tsx\"],\n directiveTag: \"rnzeus-styles-used\",\n stylesObjectName: \"styles\",\n} as const;\n\n/**\n * Cache: stylesPath -> { mtimeMs, keys }\n */\nconst stylesKeysCache = new Map<\n string,\n { mtimeMs: number; keys: Set<string> }\n>();\n\nconst MAX_CACHE = 200;\n\nfunction cacheSet(file: string, value: { mtimeMs: number; keys: Set<string> }) {\n if (stylesKeysCache.size >= MAX_CACHE) {\n const firstKey = stylesKeysCache.keys().next().value;\n if (firstKey) stylesKeysCache.delete(firstKey);\n }\n stylesKeysCache.set(file, value);\n}\n\nfunction fileProbablyUsesStyles(\n sourceCode: SourceCode,\n stylesSuffix: string\n): boolean {\n const ast: any = sourceCode.ast;\n const body: any[] = ast?.body ?? [];\n\n for (const n of body) {\n if (n.type !== \"ImportDeclaration\") continue;\n const v = n.source?.value;\n if (typeof v === \"string\" && v.includes(stylesSuffix)) return true;\n }\n\n // cheap fallback\n return sourceCode.getText().includes(\"styles.\");\n}\n\nfunction extractStyleKeysFromText(text: string): Set<string> {\n const keys = new Set<string>();\n\n const idx = text.indexOf(\"StyleSheet.create\");\n if (idx === -1) return keys;\n\n const braceStart = text.indexOf(\"{\", idx);\n if (braceStart === -1) return keys;\n\n let depth = 0;\n let readingKey = false;\n let currentKey = \"\";\n\n for (let i = braceStart; i < text.length; i++) {\n const ch = text[i];\n\n if (ch === \"{\") {\n depth++;\n continue;\n }\n\n if (ch === \"}\") {\n depth--;\n if (depth === 0) break;\n continue;\n }\n\n if (depth !== 1) continue;\n\n if (!readingKey && /[A-Za-z_$]/.test(ch)) {\n readingKey = true;\n currentKey = ch;\n continue;\n }\n\n if (readingKey) {\n if (/[\\w$]/.test(ch)) {\n currentKey += ch;\n continue;\n }\n\n if (ch === \":\") {\n keys.add(currentKey);\n readingKey = false;\n currentKey = \"\";\n continue;\n }\n\n readingKey = false;\n currentKey = \"\";\n }\n }\n\n return keys;\n}\n\nfunction resolveSiblingStylesFile(\n filename: string,\n stylesSuffix: string,\n stylesExtensions: readonly string[]\n): string | null {\n const dir = path.dirname(filename);\n const ext = path.extname(filename);\n const base = path.basename(filename, ext);\n\n for (const e of stylesExtensions) {\n const candidate = path.join(dir, `${base}${stylesSuffix}.${e}`);\n if (fs.existsSync(candidate)) return candidate;\n }\n\n return null;\n}\n\nfunction parseDirectiveKeys(\n sourceCode: SourceCode,\n directiveTag: string\n): Set<string> {\n const used = new Set<string>();\n const tag = directiveTag.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const re = new RegExp(`${tag}\\\\s*:\\\\s*([^\\\\n\\\\r*]+)`, \"i\");\n\n for (const c of sourceCode.getAllComments()) {\n const match = c.value.match(re);\n if (!match) continue;\n\n match[1]\n .split(\"|\")\n .map((s) => s.trim())\n .filter(Boolean)\n .forEach((k) => used.add(k));\n }\n\n return used;\n}\n\nfunction isIdentifier(node: any, name: string): boolean {\n return node?.type === \"Identifier\" && node.name === name;\n}\n\nfunction isStylesMemberExpression(\n node: any,\n stylesObjectName: string\n): { key: string; computed: boolean } | null {\n if (node?.type !== \"MemberExpression\") return null;\n if (!isIdentifier(node.object, stylesObjectName)) return null;\n\n if (node.computed) {\n if (\n node.property?.type === \"Literal\" &&\n typeof node.property.value === \"string\"\n ) {\n return { key: node.property.value, computed: true };\n }\n return { key: \"\", computed: true };\n }\n\n if (node.property?.type === \"Identifier\") {\n return { key: node.property.name, computed: false };\n }\n\n return null;\n}\n\nfunction findBestReportNode(sourceCode: SourceCode): any {\n const ast: any = sourceCode.ast;\n const body: any[] = ast?.body ?? [];\n\n for (const n of body) {\n if (n.type !== \"ImportDeclaration\") continue;\n const v = n.source?.value;\n if (typeof v === \"string\" && v.includes(\".styles\")) return n;\n }\n\n return ast;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Ensures styles declared in sibling <File>.styles.ts(x) are used in <File>.ts(x).\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n stylesSuffix: { type: \"string\" },\n stylesExtensions: { type: \"array\", items: { type: \"string\" } },\n directiveTag: { type: \"string\" },\n stylesObjectName: { type: \"string\" },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n unusedStyles: \"Unused styles in {{stylesFile}}: {{keys}}\",\n noWholeStylesObject:\n \"Do not pass the whole styles object. Use style={styles.foo}.\",\n dynamicNeedsDirective:\n \"Dynamic styles access ({{stylesName}}[...]) requires directive: {{tag}}\",\n },\n },\n\n create(context) {\n const opt = (context.options?.[0] ?? {}) as Options[0];\n\n const stylesSuffix = opt.stylesSuffix ?? DEFAULTS.stylesSuffix;\n const stylesExtensions = opt.stylesExtensions ?? DEFAULTS.stylesExtensions;\n const directiveTag = opt.directiveTag ?? DEFAULTS.directiveTag;\n const stylesObjectName = opt.stylesObjectName ?? DEFAULTS.stylesObjectName;\n\n const filename = context.getFilename();\n const sourceCode = context.getSourceCode();\n\n if (!fileProbablyUsesStyles(sourceCode, stylesSuffix)) return {};\n\n const ext = path.extname(filename);\n if (ext !== \".ts\" && ext !== \".tsx\") return {};\n\n const stylesPath = resolveSiblingStylesFile(\n filename,\n stylesSuffix,\n stylesExtensions\n );\n if (!stylesPath) return {};\n\n let declaredKeys: Set<string> = new Set();\n\n try {\n const stat = fs.statSync(stylesPath);\n const cached = stylesKeysCache.get(stylesPath);\n\n if (!cached || cached.mtimeMs !== stat.mtimeMs) {\n const text = fs.readFileSync(stylesPath, \"utf8\");\n const keys = extractStyleKeysFromText(text);\n cacheSet(stylesPath, { mtimeMs: stat.mtimeMs, keys });\n declaredKeys = keys;\n } else {\n declaredKeys = cached.keys;\n }\n } catch {\n declaredKeys = new Set();\n }\n\n const usedKeys = new Set<string>();\n const directiveKeys = parseDirectiveKeys(sourceCode, directiveTag);\n directiveKeys.forEach((k) => usedKeys.add(k));\n\n let sawDynamic = false;\n let firstDynamicNode: any | null = null;\n\n function recordFromNode(node: any) {\n const info = isStylesMemberExpression(node, stylesObjectName);\n if (!info) return;\n\n if (info.key) {\n usedKeys.add(info.key);\n } else {\n sawDynamic = true;\n if (!firstDynamicNode) firstDynamicNode = node;\n }\n }\n\n function checkNoWholeStylesObject(node: any) {\n if (node.name?.type !== \"JSXIdentifier\") return;\n if (node.name.name !== \"styles\") return;\n\n const expr = node.value?.expression;\n if (isIdentifier(expr, stylesObjectName)) {\n context.report({ node, messageId: \"noWholeStylesObject\" });\n }\n }\n\n function visitExpression(expr: any) {\n if (!expr) return;\n\n recordFromNode(expr);\n\n if (expr.type === \"ArrayExpression\") {\n expr.elements?.forEach(visitExpression);\n } else if (expr.type === \"ObjectExpression\") {\n expr.properties?.forEach((p: any) => visitExpression(p.value));\n } else if (expr.type === \"ConditionalExpression\") {\n visitExpression(expr.consequent);\n visitExpression(expr.alternate);\n } else if (expr.type === \"LogicalExpression\") {\n visitExpression(expr.left);\n visitExpression(expr.right);\n }\n }\n\n function onJSXAttribute(node: any) {\n if (node.name?.name === \"style\") {\n const expr = node.value?.expression;\n if (expr) visitExpression(expr);\n }\n checkNoWholeStylesObject(node);\n }\n\n function finish() {\n if (sawDynamic && directiveKeys.size === 0 && firstDynamicNode) {\n context.report({\n node: firstDynamicNode,\n messageId: \"dynamicNeedsDirective\",\n data: { tag: directiveTag, stylesName: stylesObjectName },\n });\n }\n\n const unused = [...declaredKeys].filter((k) => !usedKeys.has(k));\n if (unused.length) {\n context.report({\n node: findBestReportNode(sourceCode),\n messageId: \"unusedStyles\",\n data: {\n stylesFile: path.basename(stylesPath as string),\n keys: unused.sort().join(\", \"),\n },\n });\n }\n }\n\n return {\n JSXAttribute: onJSXAttribute,\n \"Program:exit\": finish,\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from \"eslint\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\ntype Options = [\n {\n /**\n * Suffix for styles files.\n * Default: \".styles\"\n */\n stylesSuffix?: string;\n\n /**\n * Allowed extensions.\n * Default: [\"ts\", \"tsx\"]\n */\n stylesExtensions?: string[];\n }\n];\n\nconst DEFAULTS = {\n stylesSuffix: \".styles\",\n stylesExtensions: [\"ts\", \"tsx\"],\n} as const;\n\nconst SNAKE_CASE_RE = /^[a-z][a-z0-9_]*$/;\n\nfunction extractTopLevelStyleKeys(\n text: string\n): { key: string; index: number }[] {\n const result: { key: string; index: number }[] = [];\n\n const idx = text.indexOf(\"StyleSheet.create\");\n if (idx === -1) return result;\n\n const braceStart = text.indexOf(\"{\", idx);\n if (braceStart === -1) return result;\n\n let depth = 0;\n let readingKey = false;\n let currentKey = \"\";\n let keyStartIndex = -1;\n\n for (let i = braceStart; i < text.length; i++) {\n const ch = text[i];\n\n if (ch === \"{\") {\n depth++;\n continue;\n }\n\n if (ch === \"}\") {\n depth--;\n if (depth === 0) break;\n continue;\n }\n\n if (depth !== 1) continue;\n\n if (!readingKey && /[A-Za-z_$]/.test(ch)) {\n readingKey = true;\n currentKey = ch;\n keyStartIndex = i;\n continue;\n }\n\n if (readingKey) {\n if (/[\\w$]/.test(ch)) {\n currentKey += ch;\n continue;\n }\n\n if (ch === \":\") {\n result.push({ key: currentKey, index: keyStartIndex });\n readingKey = false;\n currentKey = \"\";\n keyStartIndex = -1;\n continue;\n }\n\n readingKey = false;\n currentKey = \"\";\n keyStartIndex = -1;\n }\n }\n\n return result;\n}\n\nfunction isStylesFile(\n filename: string,\n stylesSuffix: string,\n stylesExtensions: readonly string[]\n): boolean {\n const ext = path.extname(filename);\n if (!stylesExtensions.includes(ext.replace(\".\", \"\"))) return false;\n return path.basename(filename).includes(stylesSuffix);\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Enforce snake_case naming for style keys in *.styles.ts(x) files.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n stylesSuffix: { type: \"string\" },\n stylesExtensions: { type: \"array\", items: { type: \"string\" } },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n invalidName:\n 'Style name \"{{name}}\" must be snake_case (e.g. \"{{suggestion}}\").',\n },\n },\n\n create(context) {\n const opt = (context.options?.[0] ?? {}) as Options[0];\n\n const stylesSuffix = opt.stylesSuffix ?? DEFAULTS.stylesSuffix;\n const stylesExtensions = opt.stylesExtensions ?? DEFAULTS.stylesExtensions;\n\n const filename = context.getFilename();\n if (!isStylesFile(filename, stylesSuffix, stylesExtensions)) {\n return {};\n }\n\n const sourceCode = context.getSourceCode();\n const text = sourceCode.getText();\n\n const keys = extractTopLevelStyleKeys(text);\n\n function toSnakeCase(input: string): string {\n return input\n .replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\n .replace(/[-\\s]+/g, \"_\")\n .toLowerCase();\n }\n\n for (const { key, index } of keys) {\n if (!SNAKE_CASE_RE.test(key)) {\n const loc = sourceCode.getLocFromIndex(index);\n\n context.report({\n loc: {\n start: loc,\n end: { line: loc.line, column: loc.column + key.length },\n },\n messageId: \"invalidName\",\n data: {\n name: key,\n suggestion: toSnakeCase(key),\n },\n });\n }\n }\n\n return {};\n },\n};\n\nexport default rule;\n"],"mappings":"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","names":["path","fs","import_node_path","DEFAULTS","path","rule"]}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@rnzeus/eslint-plugin",
3
+ "version": "0.1.0",
4
+ "type": "commonjs",
5
+ "main": "dist/plugin.cjs",
6
+ "exports": {
7
+ ".": "./dist/plugin.cjs",
8
+ "./configs/styles": "./dist/configs/styles.cjs"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "peerDependencies": {
14
+ "eslint": "^9.0.0"
15
+ },
16
+ "peerDependenciesMeta": {
17
+ "eslint": {
18
+ "optional": false
19
+ }
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^25.0.2",
23
+ "eslint": "^9.39.2",
24
+ "tsup": "^8.0.0",
25
+ "typescript": "^5.0.0"
26
+ },
27
+ "scripts": {
28
+ "build": "tsup",
29
+ "lint": "eslint ."
30
+ }
31
+ }