eslint-plugin-code-style 1.17.0 → 1.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/index.js +63 -16
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [1.17.1] - 2026-02-09
|
|
11
|
+
|
|
12
|
+
**Fix: Index File Behavior in Wrapped Folders**
|
|
13
|
+
|
|
14
|
+
**Version Range:** v1.17.0 → v1.17.1
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **`index-exports-only`** - Enforce dual index file behavior for wrapped folder structure
|
|
19
|
+
- Root module index (`views/index.ts`) → barrel only (re-exports)
|
|
20
|
+
- Subfolder index (`views/assessment/index.tsx`) → must contain component code, not just re-exports
|
|
21
|
+
- Only one barrel per module — subfolder index files that simulate a barrel are flagged
|
|
22
|
+
- **`no-redundant-folder-suffix`** - Detect file name matching parent folder name (e.g., `assessment/assessment.tsx` → use `assessment/index.tsx`)
|
|
23
|
+
|
|
24
|
+
**Full Changelog:** [v1.17.0...v1.17.1](https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.17.0...v1.17.1)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
10
28
|
## [1.17.0] - 2026-02-09
|
|
11
29
|
|
|
12
30
|
**New Rule + Enhancements to Naming & Import Rules**
|
|
@@ -1806,6 +1824,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1806
1824
|
|
|
1807
1825
|
---
|
|
1808
1826
|
|
|
1827
|
+
[1.17.1]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.17.0...v1.17.1
|
|
1809
1828
|
[1.17.0]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.16.0...v1.17.0
|
|
1810
1829
|
[1.16.0]: https://github.com/Mohamed-Elhawary/eslint-plugin-code-style/compare/v1.15.0...v1.16.0
|
|
1811
1830
|
[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
|
},
|
package/package.json
CHANGED