eslint-plugin-code-style 1.17.0 → 1.17.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/CHANGELOG.md +37 -0
- package/index.js +97 -29
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [1.17.2] - 2026-02-09
|
|
11
|
+
|
|
12
|
+
**Fix: CamelCase Naming Auto-Fix & Prefix Enforcement**
|
|
13
|
+
|
|
14
|
+
**Version Range:** v1.17.1 → v1.17.2
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **`folder-based-naming-convention`** - Fix camelCase naming enforcement for constants, data, reducers, services, and strings folders
|
|
19
|
+
- Auto-fix missing suffix: `common` → `commonConstants` on save (all camelCase folders)
|
|
20
|
+
- Near-match prefix enforcement: `routeConstants` → `routesConstants` when file is `routes.ts`
|
|
21
|
+
- Multi-export files with unrelated prefixes (e.g., `buttonTypeData` in `data/app.ts`) are not flagged
|
|
22
|
+
|
|
23
|
+
**Full Changelog:** [v1.17.1...v1.17.2](https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.17.1...v1.17.2)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## [1.17.1] - 2026-02-09
|
|
28
|
+
|
|
29
|
+
**Fix: Index File Behavior in Wrapped Folders**
|
|
30
|
+
|
|
31
|
+
**Version Range:** v1.17.0 → v1.17.1
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **`index-exports-only`** - Enforce dual index file behavior for wrapped folder structure
|
|
36
|
+
- Root module index (`views/index.ts`) → barrel only (re-exports)
|
|
37
|
+
- Subfolder index (`views/assessment/index.tsx`) → must contain component code, not just re-exports
|
|
38
|
+
- Only one barrel per module — subfolder index files that simulate a barrel are flagged
|
|
39
|
+
- **`no-redundant-folder-suffix`** - Detect file name matching parent folder name (e.g., `assessment/assessment.tsx` → use `assessment/index.tsx`)
|
|
40
|
+
|
|
41
|
+
**Full Changelog:** [v1.17.0...v1.17.1](https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.17.0...v1.17.1)
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
10
45
|
## [1.17.0] - 2026-02-09
|
|
11
46
|
|
|
12
47
|
**New Rule + Enhancements to Naming & Import Rules**
|
|
@@ -1806,6 +1841,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1806
1841
|
|
|
1807
1842
|
---
|
|
1808
1843
|
|
|
1844
|
+
[1.17.2]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.17.1...v1.17.2
|
|
1845
|
+
[1.17.1]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.17.0...v1.17.1
|
|
1809
1846
|
[1.17.0]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.16.0...v1.17.0
|
|
1810
1847
|
[1.16.0]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.15.0...v1.16.0
|
|
1811
1848
|
[1.15.0]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.14.4...v1.15.0
|
package/index.js
CHANGED
|
@@ -8022,23 +8022,37 @@ const indexExportsOnly = {
|
|
|
8022
8022
|
|
|
8023
8023
|
if (!isIndexFile) return {};
|
|
8024
8024
|
|
|
8025
|
-
//
|
|
8026
|
-
|
|
8025
|
+
// Determine if this is a subfolder index inside a module folder
|
|
8026
|
+
// e.g., views/assessment/index.tsx (depth >= 2 from module folder) = subfolder index
|
|
8027
|
+
// vs views/index.ts (depth == 1 from module folder) = root barrel
|
|
8028
|
+
const moduleFolders = [
|
|
8029
|
+
"actions", "apis", "assets", "atoms", "components", "config", "configs",
|
|
8030
|
+
"constants", "contexts", "data", "enums", "helpers", "hooks", "interfaces",
|
|
8031
|
+
"layouts", "lib", "middlewares", "pages", "providers", "reducers", "redux",
|
|
8032
|
+
"requests", "routes", "schemas", "services", "store", "strings", "styles",
|
|
8033
|
+
"theme", "themes", "thunks", "types", "ui", "utils", "utilities", "views",
|
|
8034
|
+
];
|
|
8035
|
+
const parts = normalizedFilename.split("/");
|
|
8036
|
+
const indexPos = parts.length - 1;
|
|
8037
|
+
let isSubfolderIndex = false;
|
|
8038
|
+
|
|
8039
|
+
for (let i = 0; i < indexPos; i++) {
|
|
8040
|
+
if (moduleFolders.includes(parts[i])) {
|
|
8041
|
+
if (indexPos - i >= 2) isSubfolderIndex = true;
|
|
8042
|
+
|
|
8043
|
+
break;
|
|
8044
|
+
}
|
|
8045
|
+
}
|
|
8046
|
+
|
|
8047
|
+
// Helper to check if a node is an import or re-export (no inline declarations)
|
|
8048
|
+
const isImportOrReexportHandler = (node) => {
|
|
8027
8049
|
const { type } = node;
|
|
8028
8050
|
|
|
8029
|
-
// Import statements
|
|
8030
8051
|
if (type === "ImportDeclaration") return true;
|
|
8031
8052
|
|
|
8032
|
-
|
|
8033
|
-
if (type === "ExportNamedDeclaration") {
|
|
8034
|
-
// Only allow re-exports (export { x } from "./module" or export { x })
|
|
8035
|
-
// If it has a declaration, it's defining something (not allowed)
|
|
8036
|
-
return !node.declaration;
|
|
8037
|
-
}
|
|
8053
|
+
if (type === "ExportNamedDeclaration") return !node.declaration;
|
|
8038
8054
|
|
|
8039
8055
|
if (type === "ExportDefaultDeclaration") {
|
|
8040
|
-
// Allow export default <identifier> (re-exporting)
|
|
8041
|
-
// Disallow export default function/class/object (defining something)
|
|
8042
8056
|
return node.declaration && node.declaration.type === "Identifier";
|
|
8043
8057
|
}
|
|
8044
8058
|
|
|
@@ -8047,6 +8061,21 @@ const indexExportsOnly = {
|
|
|
8047
8061
|
return false;
|
|
8048
8062
|
};
|
|
8049
8063
|
|
|
8064
|
+
// Helper to check if a node contains actual code (declarations, logic)
|
|
8065
|
+
const hasCodeDeclarationHandler = (node) => {
|
|
8066
|
+
const { type } = node;
|
|
8067
|
+
|
|
8068
|
+
if (type === "VariableDeclaration" || type === "FunctionDeclaration" || type === "ClassDeclaration") return true;
|
|
8069
|
+
|
|
8070
|
+
if (type === "ExportNamedDeclaration" && node.declaration) return true;
|
|
8071
|
+
|
|
8072
|
+
if (type === "ExportDefaultDeclaration") {
|
|
8073
|
+
return node.declaration && node.declaration.type !== "Identifier";
|
|
8074
|
+
}
|
|
8075
|
+
|
|
8076
|
+
return false;
|
|
8077
|
+
};
|
|
8078
|
+
|
|
8050
8079
|
// Get a friendly description of what the disallowed node is
|
|
8051
8080
|
const getNodeDescriptionHandler = (node) => {
|
|
8052
8081
|
switch (node.type) {
|
|
@@ -8063,9 +8092,7 @@ const indexExportsOnly = {
|
|
|
8063
8092
|
case "ClassDeclaration":
|
|
8064
8093
|
return "Class declaration";
|
|
8065
8094
|
case "ExportNamedDeclaration":
|
|
8066
|
-
if (node.declaration)
|
|
8067
|
-
return getNodeDescriptionHandler(node.declaration);
|
|
8068
|
-
}
|
|
8095
|
+
if (node.declaration) return getNodeDescriptionHandler(node.declaration);
|
|
8069
8096
|
|
|
8070
8097
|
return "Export with inline declaration";
|
|
8071
8098
|
case "ExportDefaultDeclaration":
|
|
@@ -8077,8 +8104,28 @@ const indexExportsOnly = {
|
|
|
8077
8104
|
|
|
8078
8105
|
return {
|
|
8079
8106
|
Program(programNode) {
|
|
8107
|
+
if (isSubfolderIndex) {
|
|
8108
|
+
// Subfolder index (e.g., views/assessment/index.tsx):
|
|
8109
|
+
// Must contain component code — must NOT be a barrel (re-exports only)
|
|
8110
|
+
// Only one barrel per module (the root index)
|
|
8111
|
+
const hasCode = programNode.body.some((node) => hasCodeDeclarationHandler(node));
|
|
8112
|
+
|
|
8113
|
+
if (!hasCode) {
|
|
8114
|
+
const subfolder = parts[indexPos - 1];
|
|
8115
|
+
|
|
8116
|
+
context.report({
|
|
8117
|
+
message: `Subfolder index file "${subfolder}/index" should contain component code, not just re-exports. Only the module root index file should be a barrel for imports and re-exports.`,
|
|
8118
|
+
node: programNode,
|
|
8119
|
+
});
|
|
8120
|
+
}
|
|
8121
|
+
|
|
8122
|
+
return;
|
|
8123
|
+
}
|
|
8124
|
+
|
|
8125
|
+
// Root module index (e.g., views/index.ts):
|
|
8126
|
+
// Must be barrel only — no code declarations allowed
|
|
8080
8127
|
for (const node of programNode.body) {
|
|
8081
|
-
if (!
|
|
8128
|
+
if (!isImportOrReexportHandler(node)) {
|
|
8082
8129
|
const description = getNodeDescriptionHandler(node);
|
|
8083
8130
|
|
|
8084
8131
|
context.report({
|
|
@@ -8091,7 +8138,7 @@ const indexExportsOnly = {
|
|
|
8091
8138
|
};
|
|
8092
8139
|
},
|
|
8093
8140
|
meta: {
|
|
8094
|
-
docs: { description: "
|
|
8141
|
+
docs: { description: "Enforce index files as barrels (re-exports only) at module root, and as component entry points (with code) in subfolders" },
|
|
8095
8142
|
schema: [],
|
|
8096
8143
|
type: "suggestion",
|
|
8097
8144
|
},
|
|
@@ -19281,17 +19328,38 @@ const folderBasedNamingConvention = {
|
|
|
19281
19328
|
// Check if name starts with lowercase (camelCase)
|
|
19282
19329
|
const isCamelCaseHandler = (name) => name && /^[a-z]/.test(name);
|
|
19283
19330
|
|
|
19284
|
-
//
|
|
19285
|
-
const
|
|
19286
|
-
|
|
19287
|
-
// Check camelCase naming (suffix-only enforcement for camelCase folders)
|
|
19288
|
-
const checkCamelCaseHandler = (name, folder, suffix, identifierNode) => {
|
|
19289
|
-
// For camelCase folders, only enforce the suffix (not full chained name)
|
|
19290
|
-
// because these folders often have multiple exports per file
|
|
19291
|
-
// Suffix stays PascalCase even in camelCase names (e.g., buttonTypeData)
|
|
19331
|
+
// Check camelCase naming for camelCase folders (constants, data, reducers, services, strings)
|
|
19332
|
+
const checkCamelCaseHandler = (name, folder, suffix, identifierNode, scopeNode, moduleInfo) => {
|
|
19292
19333
|
if (!name.endsWith(suffix)) {
|
|
19334
|
+
// Missing suffix — auto-fix by appending suffix
|
|
19335
|
+
const fixedName = name + suffix;
|
|
19336
|
+
|
|
19337
|
+
context.report({
|
|
19338
|
+
fix: createRenameFixer(scopeNode, name, fixedName, identifierNode),
|
|
19339
|
+
message: `"${name}" in "${folder}" folder must end with "${suffix}" suffix (should be "${fixedName}")`,
|
|
19340
|
+
node: identifierNode,
|
|
19341
|
+
});
|
|
19342
|
+
|
|
19343
|
+
return;
|
|
19344
|
+
}
|
|
19345
|
+
|
|
19346
|
+
// Has correct suffix — check if prefix is a near-match of expected file-based name
|
|
19347
|
+
// This catches cases like "routeConstants" → "routesConstants" (file is routes.ts)
|
|
19348
|
+
// but allows unrelated names like "buttonTypeData" in data/app.ts
|
|
19349
|
+
const expectedName = buildExpectedNameHandler(moduleInfo);
|
|
19350
|
+
|
|
19351
|
+
if (!expectedName || name === expectedName) return;
|
|
19352
|
+
|
|
19353
|
+
const actualPrefix = name.slice(0, -suffix.length);
|
|
19354
|
+
const expectedPrefix = expectedName.slice(0, -suffix.length);
|
|
19355
|
+
|
|
19356
|
+
const isNearMatch = (expectedPrefix.startsWith(actualPrefix) && (expectedPrefix.length - actualPrefix.length) <= 2)
|
|
19357
|
+
|| (actualPrefix.startsWith(expectedPrefix) && (actualPrefix.length - expectedPrefix.length) <= 2);
|
|
19358
|
+
|
|
19359
|
+
if (isNearMatch) {
|
|
19293
19360
|
context.report({
|
|
19294
|
-
|
|
19361
|
+
fix: createRenameFixer(scopeNode, name, expectedName, identifierNode),
|
|
19362
|
+
message: `"${name}" in "${folder}" folder should be "${expectedName}" to match the file name`,
|
|
19295
19363
|
node: identifierNode,
|
|
19296
19364
|
});
|
|
19297
19365
|
}
|
|
@@ -19311,11 +19379,11 @@ const folderBasedNamingConvention = {
|
|
|
19311
19379
|
|
|
19312
19380
|
const { folder, suffix } = moduleInfo;
|
|
19313
19381
|
|
|
19314
|
-
// For camelCase folders,
|
|
19382
|
+
// For camelCase folders, enforce suffix + near-match prefix check
|
|
19315
19383
|
if (camelCaseFolders.has(folder)) {
|
|
19316
19384
|
if (!isCamelCaseHandler(name) || !suffix) return;
|
|
19317
19385
|
|
|
19318
|
-
checkCamelCaseHandler(name, folder, suffix, identifierNode);
|
|
19386
|
+
checkCamelCaseHandler(name, folder, suffix, identifierNode, node, moduleInfo);
|
|
19319
19387
|
|
|
19320
19388
|
return;
|
|
19321
19389
|
}
|
|
@@ -19357,11 +19425,11 @@ const folderBasedNamingConvention = {
|
|
|
19357
19425
|
|
|
19358
19426
|
const name = node.id.name;
|
|
19359
19427
|
|
|
19360
|
-
// For camelCase folders,
|
|
19428
|
+
// For camelCase folders, enforce suffix + near-match prefix check
|
|
19361
19429
|
if (camelCaseFolders.has(folder)) {
|
|
19362
19430
|
if (!isCamelCaseHandler(name) || !suffix) return;
|
|
19363
19431
|
|
|
19364
|
-
checkCamelCaseHandler(name, folder, suffix, node.id);
|
|
19432
|
+
checkCamelCaseHandler(name, folder, suffix, node.id, node, moduleInfo);
|
|
19365
19433
|
|
|
19366
19434
|
return;
|
|
19367
19435
|
}
|
package/package.json
CHANGED