Skip to content

Commit b9e93c6

Browse files
Support min/max limits in root selectors (#50)
* v3.14.0, Support min/max limits in root selectors
1 parent 3b3e647 commit b9e93c6

File tree

6 files changed

+136
-6
lines changed

6 files changed

+136
-6
lines changed

fileComposition.schema.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,23 @@
6161
}
6262
]
6363
},
64-
"limit": { "type": "number" }
64+
"limit": {
65+
"oneOf": [
66+
{ "type": "number" },
67+
{
68+
"type": "object",
69+
"additionalProperties": false,
70+
"properties": {
71+
"min": {
72+
"type": "number"
73+
},
74+
"max": {
75+
"type": "number"
76+
}
77+
}
78+
}
79+
]
80+
}
6581
},
6682
"required": ["limit", "selector"]
6783
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"author": "Igor Kowalski (Igorkowalski94)",
33
"name": "eslint-plugin-project-structure",
4-
"version": "3.13.3",
4+
"version": "3.14.0",
55
"license": "MIT",
66
"description": "Powerful ESLint plugin with rules to help you achieve a scalable, consistent, and well-structured project. Create your own framework! Define your folder structure, file composition, advanced naming conventions, and create independent modules. Take your project to the next level and save time by automating the review of key principles of a healthy project! react folder structure react file structure react project structure react conventions architecture react next.js angular node solid vue svelte",
77
"keywords": [

src/rules/fileComposition/fileComposition.types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ type CustomErrors = Partial<Record<SelectorType, string>>;
6666

6767
export interface RootSelectorLimit {
6868
selector: SelectorType | SelectorType[];
69-
limit: number;
69+
limit: number | { max?: number; min?: number };
7070
}
7171

7272
export interface AllowOnlySpecifiedSelectors {

src/rules/fileComposition/helpers/getFileCompositionConfig/getFileCompositionConfig.consts.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,23 @@ export const FILE_COMPOSITION_SCHEMA: JSONSchema4 = {
6363
},
6464
],
6565
},
66-
limit: { type: "number" },
66+
limit: {
67+
oneOf: [
68+
{ type: "number" },
69+
{
70+
type: "object",
71+
additionalProperties: false,
72+
properties: {
73+
min: {
74+
type: "number",
75+
},
76+
max: {
77+
type: "number",
78+
},
79+
},
80+
},
81+
],
82+
},
6783
},
6884
required: ["limit", "selector"],
6985
},

src/rules/fileComposition/helpers/validateRootSelectorsLimits/validateRootSelectorsLimits.test.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,83 @@ describe("validateRootSelectorsLimits", () => {
105105
},
106106
});
107107
});
108+
109+
describe("Limit objects", () => {
110+
test("Should throw error when !!error", () => {
111+
(getSelectorsCount as jest.Mock).mockReturnValue({
112+
variable: 2,
113+
variableExpression: 2,
114+
arrowFunction: 2,
115+
function: 3,
116+
class: 3,
117+
type: 2,
118+
interface: 3,
119+
});
120+
121+
const reportMock = jest.fn();
122+
123+
validateRootSelectorsLimits({
124+
node: { body: {} } as TSESTree.Program,
125+
report: reportMock,
126+
rootSelectorsLimits: [
127+
{
128+
selector: ["interface", "type"],
129+
limit: { min: 2, max: 3 },
130+
},
131+
{
132+
selector: "variable",
133+
limit: { max: 1 },
134+
},
135+
{
136+
selector: "enum",
137+
limit: { min: 3 },
138+
},
139+
],
140+
});
141+
142+
expect(reportMock).toHaveBeenCalledWith({
143+
node: { body: {} },
144+
messageId: "rootSelectorsLimits",
145+
data: {
146+
error:
147+
"\nSelector: 'interface', 'type', min = 2, max = 3, occurrences = 5." +
148+
"\nSelector: 'variable', max = 1, occurrences = 2." +
149+
"\nSelector: 'enum', min = 3, occurrences = 0.",
150+
},
151+
});
152+
});
153+
154+
test("Should return undefined when !error", () => {
155+
(getSelectorsCount as jest.Mock).mockReturnValue({
156+
variable: 2,
157+
variableExpression: 2,
158+
arrowFunction: 2,
159+
function: 3,
160+
class: 3,
161+
type: 2,
162+
interface: 3,
163+
});
164+
165+
expect(
166+
validateRootSelectorsLimits({
167+
node: { body: {} } as TSESTree.Program,
168+
report: jest.fn(),
169+
rootSelectorsLimits: [
170+
{
171+
selector: ["interface", "type"],
172+
limit: { min: 2, max: 5 },
173+
},
174+
{
175+
selector: "variable",
176+
limit: { max: 2 },
177+
},
178+
{
179+
selector: "class",
180+
limit: { min: 3 },
181+
},
182+
],
183+
}),
184+
).toEqual(undefined);
185+
});
186+
});
108187
});

src/rules/fileComposition/helpers/validateRootSelectorsLimits/validateRootSelectorsLimits.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,27 @@ export const validateRootSelectorsLimits = ({
2828
0,
2929
);
3030

31-
if (occurrences > limit)
32-
return (acc += `\nSelector: ${selectorArray.map((s) => `'${s}'`).join(", ")}, limit = ${String(limit)}, occurrences = ${String(occurrences)}.`);
31+
if (typeof limit === "number") {
32+
if (occurrences > limit)
33+
return (acc += `\nSelector: ${selectorArray.map((s) => `'${s}'`).join(", ")}, limit = ${String(limit)}, occurrences = ${String(occurrences)}.`);
34+
}
35+
36+
if (typeof limit === "object") {
37+
const { min, max } = limit;
38+
39+
if (
40+
typeof min === "number" &&
41+
typeof max === "number" &&
42+
(occurrences < min || occurrences > max)
43+
)
44+
return (acc += `\nSelector: ${selectorArray.map((s) => `'${s}'`).join(", ")}, min = ${String(min)}, max = ${String(max)}, occurrences = ${String(occurrences)}.`);
45+
46+
if (typeof max === "number" && occurrences > max)
47+
return (acc += `\nSelector: ${selectorArray.map((s) => `'${s}'`).join(", ")}, max = ${String(max)}, occurrences = ${String(occurrences)}.`);
48+
49+
if (typeof min === "number" && occurrences < min)
50+
return (acc += `\nSelector: ${selectorArray.map((s) => `'${s}'`).join(", ")}, min = ${String(min)}, occurrences = ${String(occurrences)}.`);
51+
}
3352

3453
return acc;
3554
}, "");

0 commit comments

Comments
 (0)