@rnzeus/eslint-plugin 0.1.1 → 0.1.2
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/dist/configs/styles.cjs +37 -8
- package/dist/configs/styles.cjs.map +1 -1
- package/dist/plugin.cjs +37 -8
- package/dist/plugin.cjs.map +1 -1
- package/package.json +1 -1
package/dist/configs/styles.cjs
CHANGED
|
@@ -29,7 +29,8 @@ var DEFAULTS = {
|
|
|
29
29
|
stylesSuffix: ".styles",
|
|
30
30
|
stylesExtensions: ["ts", "tsx"],
|
|
31
31
|
directiveTag: "rnzeus-styles-used",
|
|
32
|
-
stylesObjectName: "styles"
|
|
32
|
+
stylesObjectName: "styles",
|
|
33
|
+
styleProps: ["style", "contentContainerStyle"]
|
|
33
34
|
};
|
|
34
35
|
var stylesKeysCache = /* @__PURE__ */ new Map();
|
|
35
36
|
var MAX_CACHE = 200;
|
|
@@ -40,7 +41,7 @@ function cacheSet(file, value) {
|
|
|
40
41
|
}
|
|
41
42
|
stylesKeysCache.set(file, value);
|
|
42
43
|
}
|
|
43
|
-
function fileProbablyUsesStyles(sourceCode, stylesSuffix) {
|
|
44
|
+
function fileProbablyUsesStyles(sourceCode, stylesSuffix, stylesObjectName) {
|
|
44
45
|
const ast = sourceCode.ast;
|
|
45
46
|
const body = ast?.body ?? [];
|
|
46
47
|
for (const n of body) {
|
|
@@ -48,7 +49,8 @@ function fileProbablyUsesStyles(sourceCode, stylesSuffix) {
|
|
|
48
49
|
const v = n.source?.value;
|
|
49
50
|
if (typeof v === "string" && v.includes(stylesSuffix)) return true;
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
+
const text = sourceCode.getText();
|
|
53
|
+
return text.includes(`${stylesObjectName}.`);
|
|
52
54
|
}
|
|
53
55
|
function extractStyleKeysFromText(text) {
|
|
54
56
|
const keys = /* @__PURE__ */ new Set();
|
|
@@ -141,6 +143,24 @@ function findBestReportNode(sourceCode) {
|
|
|
141
143
|
}
|
|
142
144
|
return ast;
|
|
143
145
|
}
|
|
146
|
+
function uniqueStrings(items) {
|
|
147
|
+
const out = [];
|
|
148
|
+
const seen = /* @__PURE__ */ new Set();
|
|
149
|
+
for (const s of items) {
|
|
150
|
+
if (!s || typeof s !== "string") continue;
|
|
151
|
+
if (seen.has(s)) continue;
|
|
152
|
+
seen.add(s);
|
|
153
|
+
out.push(s);
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
function buildStyleProps(defaults, incoming, mode) {
|
|
158
|
+
if (!incoming || incoming.length === 0) return new Set(defaults);
|
|
159
|
+
if (mode === "override") {
|
|
160
|
+
return new Set(uniqueStrings(incoming));
|
|
161
|
+
}
|
|
162
|
+
return new Set(uniqueStrings([...defaults, ...incoming]));
|
|
163
|
+
}
|
|
144
164
|
var rule = {
|
|
145
165
|
meta: {
|
|
146
166
|
type: "problem",
|
|
@@ -154,7 +174,9 @@ var rule = {
|
|
|
154
174
|
stylesSuffix: { type: "string" },
|
|
155
175
|
stylesExtensions: { type: "array", items: { type: "string" } },
|
|
156
176
|
directiveTag: { type: "string" },
|
|
157
|
-
stylesObjectName: { type: "string" }
|
|
177
|
+
stylesObjectName: { type: "string" },
|
|
178
|
+
styleProps: { type: "array", items: { type: "string" } },
|
|
179
|
+
type: { type: "string", enum: ["merge", "override"] }
|
|
158
180
|
},
|
|
159
181
|
additionalProperties: false
|
|
160
182
|
}
|
|
@@ -171,9 +193,16 @@ var rule = {
|
|
|
171
193
|
const stylesExtensions = opt.stylesExtensions ?? DEFAULTS.stylesExtensions;
|
|
172
194
|
const directiveTag = opt.directiveTag ?? DEFAULTS.directiveTag;
|
|
173
195
|
const stylesObjectName = opt.stylesObjectName ?? DEFAULTS.stylesObjectName;
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
|
|
196
|
+
const mode = opt.type ?? "merge";
|
|
197
|
+
const STYLE_PROPS = buildStyleProps(
|
|
198
|
+
DEFAULTS.styleProps,
|
|
199
|
+
opt.styleProps,
|
|
200
|
+
mode
|
|
201
|
+
);
|
|
202
|
+
const filename = context.filename;
|
|
203
|
+
const sourceCode = context.sourceCode;
|
|
204
|
+
if (!fileProbablyUsesStyles(sourceCode, stylesSuffix, stylesObjectName))
|
|
205
|
+
return {};
|
|
177
206
|
const ext = import_node_path.default.extname(filename);
|
|
178
207
|
if (ext !== ".ts" && ext !== ".tsx") return {};
|
|
179
208
|
const stylesPath = resolveSiblingStylesFile(
|
|
@@ -236,7 +265,7 @@ var rule = {
|
|
|
236
265
|
}
|
|
237
266
|
}
|
|
238
267
|
function onJSXAttribute(node) {
|
|
239
|
-
if (node.name?.
|
|
268
|
+
if (node.name?.type === "JSXIdentifier" && STYLE_PROPS.has(node.name.name)) {
|
|
240
269
|
const expr = node.value?.expression;
|
|
241
270
|
if (expr) visitExpression(expr);
|
|
242
271
|
}
|
|
@@ -1 +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"]}
|
|
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 * JSX props that should be treated as “style-like props”.\n * Example: [\"style\", \"contentContainerStyle\", \"columnWrapperStyle\"]\n */\n styleProps?: string[];\n\n /**\n * How to apply styleProps relative to defaults.\n * - \"merge\" (default): DEFAULTS.styleProps + styleProps (unique)\n * - \"override\": only styleProps\n */\n type?: \"merge\" | \"override\";\n }\n];\n\nconst DEFAULTS = {\n stylesSuffix: \".styles\",\n stylesExtensions: [\"ts\", \"tsx\"],\n directiveTag: \"rnzeus-styles-used\",\n stylesObjectName: \"styles\",\n\n styleProps: [\"style\", \"contentContainerStyle\"],\n} as const;\n\nconst stylesKeysCache = new Map<\n string,\n { mtimeMs: number; keys: Set<string> }\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 stylesObjectName: 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 const text = sourceCode.getText();\n return text.includes(`${stylesObjectName}.`);\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\nfunction uniqueStrings(items: readonly string[]): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n for (const s of items) {\n if (!s || typeof s !== \"string\") continue;\n if (seen.has(s)) continue;\n seen.add(s);\n out.push(s);\n }\n return out;\n}\n\nfunction buildStyleProps(\n defaults: readonly string[],\n incoming: string[] | undefined,\n mode: \"merge\" | \"override\"\n): Set<string> {\n if (!incoming || incoming.length === 0) return new Set(defaults);\n\n if (mode === \"override\") {\n return new Set(uniqueStrings(incoming));\n }\n\n // merge (default): defaults + incoming (unique)\n return new Set(uniqueStrings([...defaults, ...incoming]));\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 styleProps: { type: \"array\", items: { type: \"string\" } },\n type: { type: \"string\", enum: [\"merge\", \"override\"] },\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 mode: \"merge\" | \"override\" = opt.type ?? \"merge\";\n const STYLE_PROPS = buildStyleProps(\n DEFAULTS.styleProps,\n opt.styleProps,\n mode\n );\n\n const filename = context.filename;\n const sourceCode = context.sourceCode;\n\n if (!fileProbablyUsesStyles(sourceCode, stylesSuffix, stylesObjectName))\n 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 // <Child styles={styles} /> — запрещаем\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 (\n node.name?.type === \"JSXIdentifier\" &&\n STYLE_PROPS.has(node.name.name)\n ) {\n const expr = node.value?.expression;\n if (expr) visitExpression(expr);\n }\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"]}
|
package/dist/plugin.cjs
CHANGED
|
@@ -41,7 +41,8 @@ var DEFAULTS = {
|
|
|
41
41
|
stylesSuffix: ".styles",
|
|
42
42
|
stylesExtensions: ["ts", "tsx"],
|
|
43
43
|
directiveTag: "rnzeus-styles-used",
|
|
44
|
-
stylesObjectName: "styles"
|
|
44
|
+
stylesObjectName: "styles",
|
|
45
|
+
styleProps: ["style", "contentContainerStyle"]
|
|
45
46
|
};
|
|
46
47
|
var stylesKeysCache = /* @__PURE__ */ new Map();
|
|
47
48
|
var MAX_CACHE = 200;
|
|
@@ -52,7 +53,7 @@ function cacheSet(file, value) {
|
|
|
52
53
|
}
|
|
53
54
|
stylesKeysCache.set(file, value);
|
|
54
55
|
}
|
|
55
|
-
function fileProbablyUsesStyles(sourceCode, stylesSuffix) {
|
|
56
|
+
function fileProbablyUsesStyles(sourceCode, stylesSuffix, stylesObjectName) {
|
|
56
57
|
const ast = sourceCode.ast;
|
|
57
58
|
const body = ast?.body ?? [];
|
|
58
59
|
for (const n of body) {
|
|
@@ -60,7 +61,8 @@ function fileProbablyUsesStyles(sourceCode, stylesSuffix) {
|
|
|
60
61
|
const v = n.source?.value;
|
|
61
62
|
if (typeof v === "string" && v.includes(stylesSuffix)) return true;
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
+
const text = sourceCode.getText();
|
|
65
|
+
return text.includes(`${stylesObjectName}.`);
|
|
64
66
|
}
|
|
65
67
|
function extractStyleKeysFromText(text) {
|
|
66
68
|
const keys = /* @__PURE__ */ new Set();
|
|
@@ -153,6 +155,24 @@ function findBestReportNode(sourceCode) {
|
|
|
153
155
|
}
|
|
154
156
|
return ast;
|
|
155
157
|
}
|
|
158
|
+
function uniqueStrings(items) {
|
|
159
|
+
const out = [];
|
|
160
|
+
const seen = /* @__PURE__ */ new Set();
|
|
161
|
+
for (const s of items) {
|
|
162
|
+
if (!s || typeof s !== "string") continue;
|
|
163
|
+
if (seen.has(s)) continue;
|
|
164
|
+
seen.add(s);
|
|
165
|
+
out.push(s);
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
function buildStyleProps(defaults, incoming, mode) {
|
|
170
|
+
if (!incoming || incoming.length === 0) return new Set(defaults);
|
|
171
|
+
if (mode === "override") {
|
|
172
|
+
return new Set(uniqueStrings(incoming));
|
|
173
|
+
}
|
|
174
|
+
return new Set(uniqueStrings([...defaults, ...incoming]));
|
|
175
|
+
}
|
|
156
176
|
var rule = {
|
|
157
177
|
meta: {
|
|
158
178
|
type: "problem",
|
|
@@ -166,7 +186,9 @@ var rule = {
|
|
|
166
186
|
stylesSuffix: { type: "string" },
|
|
167
187
|
stylesExtensions: { type: "array", items: { type: "string" } },
|
|
168
188
|
directiveTag: { type: "string" },
|
|
169
|
-
stylesObjectName: { type: "string" }
|
|
189
|
+
stylesObjectName: { type: "string" },
|
|
190
|
+
styleProps: { type: "array", items: { type: "string" } },
|
|
191
|
+
type: { type: "string", enum: ["merge", "override"] }
|
|
170
192
|
},
|
|
171
193
|
additionalProperties: false
|
|
172
194
|
}
|
|
@@ -183,9 +205,16 @@ var rule = {
|
|
|
183
205
|
const stylesExtensions = opt.stylesExtensions ?? DEFAULTS.stylesExtensions;
|
|
184
206
|
const directiveTag = opt.directiveTag ?? DEFAULTS.directiveTag;
|
|
185
207
|
const stylesObjectName = opt.stylesObjectName ?? DEFAULTS.stylesObjectName;
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
|
|
208
|
+
const mode = opt.type ?? "merge";
|
|
209
|
+
const STYLE_PROPS = buildStyleProps(
|
|
210
|
+
DEFAULTS.styleProps,
|
|
211
|
+
opt.styleProps,
|
|
212
|
+
mode
|
|
213
|
+
);
|
|
214
|
+
const filename = context.filename;
|
|
215
|
+
const sourceCode = context.sourceCode;
|
|
216
|
+
if (!fileProbablyUsesStyles(sourceCode, stylesSuffix, stylesObjectName))
|
|
217
|
+
return {};
|
|
189
218
|
const ext = import_node_path.default.extname(filename);
|
|
190
219
|
if (ext !== ".ts" && ext !== ".tsx") return {};
|
|
191
220
|
const stylesPath = resolveSiblingStylesFile(
|
|
@@ -248,7 +277,7 @@ var rule = {
|
|
|
248
277
|
}
|
|
249
278
|
}
|
|
250
279
|
function onJSXAttribute(node) {
|
|
251
|
-
if (node.name?.
|
|
280
|
+
if (node.name?.type === "JSXIdentifier" && STYLE_PROPS.has(node.name.name)) {
|
|
252
281
|
const expr = node.value?.expression;
|
|
253
282
|
if (expr) visitExpression(expr);
|
|
254
283
|
}
|
package/dist/plugin.cjs.map
CHANGED
|
@@ -1 +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"]}
|
|
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 * JSX props that should be treated as “style-like props”.\n * Example: [\"style\", \"contentContainerStyle\", \"columnWrapperStyle\"]\n */\n styleProps?: string[];\n\n /**\n * How to apply styleProps relative to defaults.\n * - \"merge\" (default): DEFAULTS.styleProps + styleProps (unique)\n * - \"override\": only styleProps\n */\n type?: \"merge\" | \"override\";\n }\n];\n\nconst DEFAULTS = {\n stylesSuffix: \".styles\",\n stylesExtensions: [\"ts\", \"tsx\"],\n directiveTag: \"rnzeus-styles-used\",\n stylesObjectName: \"styles\",\n\n styleProps: [\"style\", \"contentContainerStyle\"],\n} as const;\n\nconst stylesKeysCache = new Map<\n string,\n { mtimeMs: number; keys: Set<string> }\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 stylesObjectName: 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 const text = sourceCode.getText();\n return text.includes(`${stylesObjectName}.`);\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\nfunction uniqueStrings(items: readonly string[]): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n for (const s of items) {\n if (!s || typeof s !== \"string\") continue;\n if (seen.has(s)) continue;\n seen.add(s);\n out.push(s);\n }\n return out;\n}\n\nfunction buildStyleProps(\n defaults: readonly string[],\n incoming: string[] | undefined,\n mode: \"merge\" | \"override\"\n): Set<string> {\n if (!incoming || incoming.length === 0) return new Set(defaults);\n\n if (mode === \"override\") {\n return new Set(uniqueStrings(incoming));\n }\n\n // merge (default): defaults + incoming (unique)\n return new Set(uniqueStrings([...defaults, ...incoming]));\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 styleProps: { type: \"array\", items: { type: \"string\" } },\n type: { type: \"string\", enum: [\"merge\", \"override\"] },\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 mode: \"merge\" | \"override\" = opt.type ?? \"merge\";\n const STYLE_PROPS = buildStyleProps(\n DEFAULTS.styleProps,\n opt.styleProps,\n mode\n );\n\n const filename = context.filename;\n const sourceCode = context.sourceCode;\n\n if (!fileProbablyUsesStyles(sourceCode, stylesSuffix, stylesObjectName))\n 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 // <Child styles={styles} /> — запрещаем\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 (\n node.name?.type === \"JSXIdentifier\" &&\n STYLE_PROPS.has(node.name.name)\n ) {\n const expr = node.value?.expression;\n if (expr) visitExpression(expr);\n }\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"]}
|