eslint-plugin-llm-core 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/README.md +99 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/filename-match-export.d.ts +5 -0
- package/dist/rules/filename-match-export.d.ts.map +1 -0
- package/dist/rules/filename-match-export.js +198 -0
- package/dist/rules/filename-match-export.js.map +1 -0
- package/dist/rules/index.d.ts +11 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +27 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/max-file-length.d.ts +12 -0
- package/dist/rules/max-file-length.d.ts.map +1 -0
- package/dist/rules/max-file-length.js +85 -0
- package/dist/rules/max-file-length.js.map +1 -0
- package/dist/rules/max-function-length.d.ts +11 -0
- package/dist/rules/max-function-length.d.ts.map +1 -0
- package/dist/rules/max-function-length.js +100 -0
- package/dist/rules/max-function-length.js.map +1 -0
- package/dist/rules/max-nesting-depth.d.ts +10 -0
- package/dist/rules/max-nesting-depth.d.ts.map +1 -0
- package/dist/rules/max-nesting-depth.js +106 -0
- package/dist/rules/max-nesting-depth.js.map +1 -0
- package/dist/rules/max-params.d.ts +11 -0
- package/dist/rules/max-params.d.ts.map +1 -0
- package/dist/rules/max-params.js +120 -0
- package/dist/rules/max-params.js.map +1 -0
- package/dist/rules/naming-conventions.d.ts +6 -0
- package/dist/rules/naming-conventions.d.ts.map +1 -0
- package/dist/rules/naming-conventions.js +79 -0
- package/dist/rules/naming-conventions.js.map +1 -0
- package/dist/rules/no-exported-function-expressions.d.ts +6 -0
- package/dist/rules/no-exported-function-expressions.d.ts.map +1 -0
- package/dist/rules/no-exported-function-expressions.js +130 -0
- package/dist/rules/no-exported-function-expressions.js.map +1 -0
- package/dist/rules/no-inline-disable.d.ts +5 -0
- package/dist/rules/no-inline-disable.d.ts.map +1 -0
- package/dist/rules/no-inline-disable.js +47 -0
- package/dist/rules/no-inline-disable.js.map +1 -0
- package/dist/rules/no-magic-numbers.d.ts +13 -0
- package/dist/rules/no-magic-numbers.d.ts.map +1 -0
- package/dist/rules/no-magic-numbers.js +162 -0
- package/dist/rules/no-magic-numbers.js.map +1 -0
- package/dist/rules/structured-logging.d.ts +11 -0
- package/dist/rules/structured-logging.d.ts.map +1 -0
- package/dist/rules/structured-logging.js +119 -0
- package/dist/rules/structured-logging.js.map +1 -0
- package/dist/utils/create-rule.d.ts +5 -0
- package/dist/utils/create-rule.d.ts.map +1 -0
- package/dist/utils/create-rule.js +6 -0
- package/dist/utils/create-rule.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
4
|
+
const create_rule_1 = require("../utils/create-rule");
|
|
5
|
+
exports.default = (0, create_rule_1.createRule)({
|
|
6
|
+
name: "max-function-length",
|
|
7
|
+
meta: {
|
|
8
|
+
type: "suggestion",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Enforce a maximum number of lines per function to encourage decomposition",
|
|
11
|
+
},
|
|
12
|
+
messages: {
|
|
13
|
+
maxFunctionLength: [
|
|
14
|
+
"Function '{{ name }}' has {{ lines }} lines, exceeding the maximum of {{ max }}.",
|
|
15
|
+
"",
|
|
16
|
+
"Why: Long functions are hard to understand, test, and maintain.",
|
|
17
|
+
"They often do too many things and hide bugs in nested logic.",
|
|
18
|
+
"",
|
|
19
|
+
"How to fix:",
|
|
20
|
+
" 1. Extract logical sections into named helper functions",
|
|
21
|
+
" 2. Each function should do one thing — if you can't name it clearly, it's doing too much",
|
|
22
|
+
" 3. Move setup/validation to the top, core logic in the middle, cleanup at the bottom",
|
|
23
|
+
" Then extract each section into its own function",
|
|
24
|
+
].join("\n"),
|
|
25
|
+
},
|
|
26
|
+
schema: [
|
|
27
|
+
{
|
|
28
|
+
type: "object",
|
|
29
|
+
properties: {
|
|
30
|
+
max: {
|
|
31
|
+
type: "integer",
|
|
32
|
+
minimum: 1,
|
|
33
|
+
description: "Maximum allowed lines per function (default: 50)",
|
|
34
|
+
},
|
|
35
|
+
skipBlankLines: {
|
|
36
|
+
type: "boolean",
|
|
37
|
+
description: "Whether to skip blank lines when counting (default: true)",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
additionalProperties: false,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
defaultOptions: [{ max: 50, skipBlankLines: true }],
|
|
45
|
+
create(context, [options]) {
|
|
46
|
+
const max = options.max ?? 50;
|
|
47
|
+
const skipBlankLines = options.skipBlankLines ?? true;
|
|
48
|
+
const sourceCode = context.sourceCode;
|
|
49
|
+
function getFunctionName(node) {
|
|
50
|
+
if (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration && node.id) {
|
|
51
|
+
return node.id.name;
|
|
52
|
+
}
|
|
53
|
+
if (node.type === utils_1.AST_NODE_TYPES.FunctionExpression && node.id) {
|
|
54
|
+
return node.id.name;
|
|
55
|
+
}
|
|
56
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
57
|
+
node.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
58
|
+
return node.parent.id.name;
|
|
59
|
+
}
|
|
60
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
|
61
|
+
node.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
62
|
+
return node.parent.key.name;
|
|
63
|
+
}
|
|
64
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.Property &&
|
|
65
|
+
node.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
66
|
+
return node.parent.key.name;
|
|
67
|
+
}
|
|
68
|
+
return "anonymous";
|
|
69
|
+
}
|
|
70
|
+
function countLines(node) {
|
|
71
|
+
const startLine = node.loc.start.line;
|
|
72
|
+
const endLine = node.loc.end.line;
|
|
73
|
+
if (!skipBlankLines) {
|
|
74
|
+
return endLine - startLine + 1;
|
|
75
|
+
}
|
|
76
|
+
const lines = sourceCode.getText(node).split("\n");
|
|
77
|
+
return lines.filter((line) => line.trim().length > 0).length;
|
|
78
|
+
}
|
|
79
|
+
function checkFunction(node) {
|
|
80
|
+
const lines = countLines(node);
|
|
81
|
+
if (lines <= max)
|
|
82
|
+
return;
|
|
83
|
+
context.report({
|
|
84
|
+
node,
|
|
85
|
+
messageId: "maxFunctionLength",
|
|
86
|
+
data: {
|
|
87
|
+
name: getFunctionName(node),
|
|
88
|
+
lines: String(lines),
|
|
89
|
+
max: String(max),
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
FunctionDeclaration: checkFunction,
|
|
95
|
+
FunctionExpression: checkFunction,
|
|
96
|
+
ArrowFunctionExpression: checkFunction,
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=max-function-length.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"max-function-length.js","sourceRoot":"","sources":["../../src/rules/max-function-length.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type Options = [
|
|
2
|
+
{
|
|
3
|
+
max?: number;
|
|
4
|
+
}
|
|
5
|
+
];
|
|
6
|
+
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<"maxNestingDepth", Options, unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener> & {
|
|
7
|
+
name: string;
|
|
8
|
+
};
|
|
9
|
+
export default _default;
|
|
10
|
+
//# sourceMappingURL=max-nesting-depth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"max-nesting-depth.d.ts","sourceRoot":"","sources":["../../src/rules/max-nesting-depth.ts"],"names":[],"mappings":"AAKA,KAAK,OAAO,GAAG;IACb;QACE,GAAG,CAAC,EAAE,MAAM,CAAC;KACd;CACF,CAAC;;;;AAEF,wBA8GG"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
4
|
+
const create_rule_1 = require("../utils/create-rule");
|
|
5
|
+
exports.default = (0, create_rule_1.createRule)({
|
|
6
|
+
name: "max-nesting-depth",
|
|
7
|
+
meta: {
|
|
8
|
+
type: "suggestion",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Enforce a maximum nesting depth for control flow statements to reduce cognitive complexity",
|
|
11
|
+
},
|
|
12
|
+
messages: {
|
|
13
|
+
maxNestingDepth: [
|
|
14
|
+
"Nesting depth {{ depth }} exceeds the maximum of {{ max }}.",
|
|
15
|
+
"",
|
|
16
|
+
"Why: Deeply nested code is harder to read, test, and maintain.",
|
|
17
|
+
"Each nesting level multiplies cognitive load for anyone reading the code.",
|
|
18
|
+
"",
|
|
19
|
+
"How to fix:",
|
|
20
|
+
" 1. Use guard clauses: invert the condition and return early",
|
|
21
|
+
" Before: if (user) { if (user.isActive) { doWork(); } }",
|
|
22
|
+
" After: if (!user) return; if (!user.isActive) return; doWork();",
|
|
23
|
+
" 2. Extract nested blocks into named helper functions",
|
|
24
|
+
" Before: if (a) { for (...) { if (b) { ... } } }",
|
|
25
|
+
" After: if (a) { processItems(items); }",
|
|
26
|
+
" 3. Replace nested if/else chains with early returns or switch/map lookups",
|
|
27
|
+
].join("\n"),
|
|
28
|
+
},
|
|
29
|
+
schema: [
|
|
30
|
+
{
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: {
|
|
33
|
+
max: {
|
|
34
|
+
type: "integer",
|
|
35
|
+
minimum: 1,
|
|
36
|
+
description: "Maximum allowed nesting depth (default: 3)",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
additionalProperties: false,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
defaultOptions: [{ max: 3 }],
|
|
44
|
+
create(context, [options]) {
|
|
45
|
+
const max = options.max ?? 3;
|
|
46
|
+
let depth = 0;
|
|
47
|
+
const reported = new Set();
|
|
48
|
+
function enterNesting(node) {
|
|
49
|
+
depth++;
|
|
50
|
+
if (depth > max && !reported.has(node)) {
|
|
51
|
+
reported.add(node);
|
|
52
|
+
context.report({
|
|
53
|
+
node,
|
|
54
|
+
messageId: "maxNestingDepth",
|
|
55
|
+
data: {
|
|
56
|
+
depth: String(depth),
|
|
57
|
+
max: String(max),
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function exitNesting() {
|
|
63
|
+
depth--;
|
|
64
|
+
}
|
|
65
|
+
function enterElseIf(node) {
|
|
66
|
+
// "else if" — the parent if's alternate is this if.
|
|
67
|
+
// Don't count it as additional nesting since it reads flat.
|
|
68
|
+
const parent = node.parent;
|
|
69
|
+
if (parent &&
|
|
70
|
+
parent.type === utils_1.AST_NODE_TYPES.IfStatement &&
|
|
71
|
+
parent.alternate === node) {
|
|
72
|
+
// This is an else-if, don't increment depth
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
enterNesting(node);
|
|
76
|
+
}
|
|
77
|
+
function exitElseIf(node) {
|
|
78
|
+
const parent = node.parent;
|
|
79
|
+
if (parent &&
|
|
80
|
+
parent.type === utils_1.AST_NODE_TYPES.IfStatement &&
|
|
81
|
+
parent.alternate === node) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
exitNesting();
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
IfStatement: enterElseIf,
|
|
88
|
+
"IfStatement:exit": exitElseIf,
|
|
89
|
+
ForStatement: enterNesting,
|
|
90
|
+
"ForStatement:exit": exitNesting,
|
|
91
|
+
ForInStatement: enterNesting,
|
|
92
|
+
"ForInStatement:exit": exitNesting,
|
|
93
|
+
ForOfStatement: enterNesting,
|
|
94
|
+
"ForOfStatement:exit": exitNesting,
|
|
95
|
+
WhileStatement: enterNesting,
|
|
96
|
+
"WhileStatement:exit": exitNesting,
|
|
97
|
+
DoWhileStatement: enterNesting,
|
|
98
|
+
"DoWhileStatement:exit": exitNesting,
|
|
99
|
+
SwitchStatement: enterNesting,
|
|
100
|
+
"SwitchStatement:exit": exitNesting,
|
|
101
|
+
TryStatement: enterNesting,
|
|
102
|
+
"TryStatement:exit": exitNesting,
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=max-nesting-depth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"max-nesting-depth.js","sourceRoot":"","sources":["../../src/rules/max-nesting-depth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type Options = [
|
|
2
|
+
{
|
|
3
|
+
max?: number;
|
|
4
|
+
maxConstructor?: number;
|
|
5
|
+
}
|
|
6
|
+
];
|
|
7
|
+
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<"maxParams", Options, unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener> & {
|
|
8
|
+
name: string;
|
|
9
|
+
};
|
|
10
|
+
export default _default;
|
|
11
|
+
//# sourceMappingURL=max-params.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"max-params.d.ts","sourceRoot":"","sources":["../../src/rules/max-params.ts"],"names":[],"mappings":"AAKA,KAAK,OAAO,GAAG;IACb;QACE,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB;CACF,CAAC;;;;AAEF,wBAwJG"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
4
|
+
const create_rule_1 = require("../utils/create-rule");
|
|
5
|
+
exports.default = (0, create_rule_1.createRule)({
|
|
6
|
+
name: "max-params",
|
|
7
|
+
meta: {
|
|
8
|
+
type: "suggestion",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Enforce a maximum number of function parameters to encourage object parameter patterns",
|
|
11
|
+
},
|
|
12
|
+
messages: {
|
|
13
|
+
maxParams: [
|
|
14
|
+
"Function '{{ name }}' has {{ count }} parameters, exceeding the maximum of {{ max }}.",
|
|
15
|
+
"",
|
|
16
|
+
"Why: Functions with many positional parameters are hard to call correctly.",
|
|
17
|
+
"Parameter order is easy to confuse, especially for callers unfamiliar with the API.",
|
|
18
|
+
"",
|
|
19
|
+
"How to fix:",
|
|
20
|
+
" Use a single options object with destructuring:",
|
|
21
|
+
"",
|
|
22
|
+
" Before: function {{ name }}({{ params }}) { ... }",
|
|
23
|
+
" After: function {{ name }}(options: { {{ params }} }) {",
|
|
24
|
+
" const { {{ paramNames }} } = options;",
|
|
25
|
+
" ...",
|
|
26
|
+
" }",
|
|
27
|
+
].join("\n"),
|
|
28
|
+
},
|
|
29
|
+
schema: [
|
|
30
|
+
{
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: {
|
|
33
|
+
max: {
|
|
34
|
+
type: "integer",
|
|
35
|
+
minimum: 1,
|
|
36
|
+
description: "Maximum allowed parameters for functions (default: 2)",
|
|
37
|
+
},
|
|
38
|
+
maxConstructor: {
|
|
39
|
+
type: "integer",
|
|
40
|
+
minimum: 1,
|
|
41
|
+
description: "Maximum allowed parameters for class constructors (default: 5)",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
additionalProperties: false,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
defaultOptions: [{ max: 2, maxConstructor: 5 }],
|
|
49
|
+
create(context, [options]) {
|
|
50
|
+
const max = options.max ?? 2;
|
|
51
|
+
const maxConstructor = options.maxConstructor ?? 5;
|
|
52
|
+
const sourceCode = context.sourceCode;
|
|
53
|
+
function getFunctionName(node) {
|
|
54
|
+
if (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration && node.id) {
|
|
55
|
+
return node.id.name;
|
|
56
|
+
}
|
|
57
|
+
if (node.type === utils_1.AST_NODE_TYPES.FunctionExpression && node.id) {
|
|
58
|
+
return node.id.name;
|
|
59
|
+
}
|
|
60
|
+
// Check if assigned to a variable
|
|
61
|
+
if (node.parent &&
|
|
62
|
+
node.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
63
|
+
node.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
64
|
+
return node.parent.id.name;
|
|
65
|
+
}
|
|
66
|
+
// Check if it's a method
|
|
67
|
+
if (node.parent &&
|
|
68
|
+
node.parent.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
|
69
|
+
node.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
70
|
+
return node.parent.key.name;
|
|
71
|
+
}
|
|
72
|
+
if (node.parent &&
|
|
73
|
+
node.parent.type === utils_1.AST_NODE_TYPES.Property &&
|
|
74
|
+
node.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
75
|
+
return node.parent.key.name;
|
|
76
|
+
}
|
|
77
|
+
return "anonymous";
|
|
78
|
+
}
|
|
79
|
+
function isConstructor(node) {
|
|
80
|
+
return (node.parent?.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
|
81
|
+
node.parent.kind === "constructor");
|
|
82
|
+
}
|
|
83
|
+
function checkParams(node) {
|
|
84
|
+
const limit = isConstructor(node) ? maxConstructor : max;
|
|
85
|
+
const count = node.params.length;
|
|
86
|
+
if (count <= limit)
|
|
87
|
+
return;
|
|
88
|
+
const name = getFunctionName(node);
|
|
89
|
+
const params = node.params.map((p) => sourceCode.getText(p)).join(", ");
|
|
90
|
+
const paramNames = node.params
|
|
91
|
+
.map((p) => {
|
|
92
|
+
if (p.type === utils_1.AST_NODE_TYPES.Identifier)
|
|
93
|
+
return p.name;
|
|
94
|
+
if (p.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
|
|
95
|
+
p.left.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
96
|
+
return p.left.name;
|
|
97
|
+
}
|
|
98
|
+
return sourceCode.getText(p);
|
|
99
|
+
})
|
|
100
|
+
.join(", ");
|
|
101
|
+
context.report({
|
|
102
|
+
node,
|
|
103
|
+
messageId: "maxParams",
|
|
104
|
+
data: {
|
|
105
|
+
name,
|
|
106
|
+
count: String(count),
|
|
107
|
+
max: String(limit),
|
|
108
|
+
params,
|
|
109
|
+
paramNames,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
FunctionDeclaration: checkParams,
|
|
115
|
+
FunctionExpression: checkParams,
|
|
116
|
+
ArrowFunctionExpression: checkParams,
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
//# sourceMappingURL=max-params.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"max-params.js","sourceRoot":"","sources":["../../src/rules/max-params.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type MessageIds = "missingBasePrefix" | "missingErrorSuffix";
|
|
2
|
+
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<MessageIds, [], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener> & {
|
|
3
|
+
name: string;
|
|
4
|
+
};
|
|
5
|
+
export default _default;
|
|
6
|
+
//# sourceMappingURL=naming-conventions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming-conventions.d.ts","sourceRoot":"","sources":["../../src/rules/naming-conventions.ts"],"names":[],"mappings":"AAGA,KAAK,UAAU,GAAG,mBAAmB,GAAG,oBAAoB,CAAC;;;;AAE7D,wBAmEG"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
4
|
+
const create_rule_1 = require("../utils/create-rule");
|
|
5
|
+
exports.default = (0, create_rule_1.createRule)({
|
|
6
|
+
name: "naming-conventions",
|
|
7
|
+
meta: {
|
|
8
|
+
type: "problem",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Enforce naming conventions: Base prefix for abstract classes, Error suffix for error classes",
|
|
11
|
+
},
|
|
12
|
+
messages: {
|
|
13
|
+
missingBasePrefix: [
|
|
14
|
+
"Abstract class '{{ className }}' must use the 'Base' prefix (e.g., 'Base{{ className }}').",
|
|
15
|
+
"",
|
|
16
|
+
"Why: The 'Base' prefix signals that a class is abstract and cannot be instantiated directly.",
|
|
17
|
+
"Without it, consumers may try to instantiate the class, causing runtime errors.",
|
|
18
|
+
"",
|
|
19
|
+
"How to fix:",
|
|
20
|
+
" Rename: abstract class {{ className }} { ... }",
|
|
21
|
+
" To: abstract class Base{{ className }} { ... }",
|
|
22
|
+
].join("\n"),
|
|
23
|
+
missingErrorSuffix: [
|
|
24
|
+
"Error class '{{ className }}' must use the 'Error' suffix (e.g., '{{ className }}Error').",
|
|
25
|
+
"",
|
|
26
|
+
"Why: The 'Error' suffix makes it immediately clear that a class represents an error condition.",
|
|
27
|
+
"Without it, catch blocks and error handling become harder to understand.",
|
|
28
|
+
"",
|
|
29
|
+
"How to fix:",
|
|
30
|
+
" Rename: class {{ className }} extends {{ superName }} { ... }",
|
|
31
|
+
" To: class {{ className }}Error extends {{ superName }} { ... }",
|
|
32
|
+
].join("\n"),
|
|
33
|
+
},
|
|
34
|
+
schema: [],
|
|
35
|
+
},
|
|
36
|
+
defaultOptions: [],
|
|
37
|
+
create(context) {
|
|
38
|
+
return {
|
|
39
|
+
ClassDeclaration(node) {
|
|
40
|
+
if (!node.id)
|
|
41
|
+
return;
|
|
42
|
+
const className = node.id.name;
|
|
43
|
+
// Abstract classes must have Base prefix
|
|
44
|
+
if (node.abstract && !className.startsWith("Base")) {
|
|
45
|
+
context.report({
|
|
46
|
+
node: node.id,
|
|
47
|
+
messageId: "missingBasePrefix",
|
|
48
|
+
data: { className },
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// Error classes must have Error suffix
|
|
52
|
+
if (node.superClass) {
|
|
53
|
+
const superName = getSuperClassName(node.superClass);
|
|
54
|
+
if (!superName)
|
|
55
|
+
return;
|
|
56
|
+
const isErrorClass = superName === "Error" || superName.endsWith("Error");
|
|
57
|
+
if (isErrorClass && !className.endsWith("Error")) {
|
|
58
|
+
context.report({
|
|
59
|
+
node: node.id,
|
|
60
|
+
messageId: "missingErrorSuffix",
|
|
61
|
+
data: { className, superName },
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
function getSuperClassName(node) {
|
|
70
|
+
if (node.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
71
|
+
return node.name;
|
|
72
|
+
}
|
|
73
|
+
if (node.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
74
|
+
node.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
75
|
+
return node.property.name;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=naming-conventions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming-conventions.js","sourceRoot":"","sources":["../../src/rules/naming-conventions.ts"],"names":[],"mappings":";;AAAA,oDAAoE;AACpE,sDAAkD;AAIlD,kBAAe,IAAA,wBAAU,EAAiB;IACxC,IAAI,EAAE,oBAAoB;IAC1B,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,8FAA8F;SACjG;QACD,QAAQ,EAAE;YACR,iBAAiB,EAAE;gBACjB,4FAA4F;gBAC5F,EAAE;gBACF,8FAA8F;gBAC9F,iFAAiF;gBACjF,EAAE;gBACF,aAAa;gBACb,kDAAkD;gBAClD,sDAAsD;aACvD,CAAC,IAAI,CAAC,IAAI,CAAC;YACZ,kBAAkB,EAAE;gBAClB,2FAA2F;gBAC3F,EAAE;gBACF,gGAAgG;gBAChG,0EAA0E;gBAC1E,EAAE;gBACF,aAAa;gBACb,iEAAiE;gBACjE,sEAAsE;aACvE,CAAC,IAAI,CAAC,IAAI,CAAC;SACb;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,gBAAgB,CAAC,IAAI;gBACnB,IAAI,CAAC,IAAI,CAAC,EAAE;oBAAE,OAAO;gBAErB,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;gBAE/B,yCAAyC;gBACzC,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;oBACnD,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI,EAAE,IAAI,CAAC,EAAE;wBACb,SAAS,EAAE,mBAAmB;wBAC9B,IAAI,EAAE,EAAE,SAAS,EAAE;qBACpB,CAAC,CAAC;gBACL,CAAC;gBAED,uCAAuC;gBACvC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;oBACpB,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;oBACrD,IAAI,CAAC,SAAS;wBAAE,OAAO;oBAEvB,MAAM,YAAY,GAChB,SAAS,KAAK,OAAO,IAAI,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;oBACvD,IAAI,YAAY,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;wBACjD,OAAO,CAAC,MAAM,CAAC;4BACb,IAAI,EAAE,IAAI,CAAC,EAAE;4BACb,SAAS,EAAE,oBAAoB;4BAC/B,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE;yBAC/B,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC;AAEH,SAAS,iBAAiB,CACxB,IAAqC;IAErC,IAAI,IAAI,CAAC,IAAI,KAAK,sBAAc,CAAC,UAAU,EAAE,CAAC;QAC5C,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IACD,IACE,IAAI,CAAC,IAAI,KAAK,sBAAc,CAAC,gBAAgB;QAC7C,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,sBAAc,CAAC,UAAU,EAChD,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC5B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type MessageIds = "noExportedFunctionExpression" | "noDefaultFunctionExpression" | "convertToDeclaration" | "convertDefaultToDeclaration";
|
|
2
|
+
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<MessageIds, [], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener> & {
|
|
3
|
+
name: string;
|
|
4
|
+
};
|
|
5
|
+
export default _default;
|
|
6
|
+
//# sourceMappingURL=no-exported-function-expressions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-exported-function-expressions.d.ts","sourceRoot":"","sources":["../../src/rules/no-exported-function-expressions.ts"],"names":[],"mappings":"AAGA,KAAK,UAAU,GACX,8BAA8B,GAC9B,6BAA6B,GAC7B,sBAAsB,GACtB,6BAA6B,CAAC;;;;AAOlC,wBAwJG"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
4
|
+
const create_rule_1 = require("../utils/create-rule");
|
|
5
|
+
exports.default = (0, create_rule_1.createRule)({
|
|
6
|
+
name: "no-exported-function-expressions",
|
|
7
|
+
meta: {
|
|
8
|
+
type: "suggestion",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Enforce that exported functions use function declarations instead of function expressions or arrow functions",
|
|
11
|
+
},
|
|
12
|
+
hasSuggestions: true,
|
|
13
|
+
messages: {
|
|
14
|
+
noExportedFunctionExpression: [
|
|
15
|
+
"Exported function '{{ name }}' must use a function declaration, not a function expression or arrow function.",
|
|
16
|
+
"",
|
|
17
|
+
"Why: Function declarations are hoisted, produce better stack traces, and signal clear intent.",
|
|
18
|
+
"LLMs default to arrow functions — this rule enforces the preferred pattern.",
|
|
19
|
+
"",
|
|
20
|
+
"How to fix:",
|
|
21
|
+
" Replace: export const {{ name }} = {{ async }}({{ params }}) => { ... }",
|
|
22
|
+
" With: export {{ async }}function {{ name }}({{ params }}) { ... }",
|
|
23
|
+
].join("\n"),
|
|
24
|
+
noDefaultFunctionExpression: [
|
|
25
|
+
"Default export must use a named function declaration, not a function expression or arrow function.",
|
|
26
|
+
"",
|
|
27
|
+
"Why: Named function declarations produce meaningful stack traces and are self-documenting.",
|
|
28
|
+
"Anonymous default exports make debugging harder and reduce code readability.",
|
|
29
|
+
"",
|
|
30
|
+
"How to fix:",
|
|
31
|
+
" Replace: export default {{ async }}({{ params }}) => { ... }",
|
|
32
|
+
" With: export default {{ async }}function myFunction({{ params }}) { ... }",
|
|
33
|
+
].join("\n"),
|
|
34
|
+
convertToDeclaration: "Convert to function declaration: export {{ async }}function {{ name }}({{ params }}) { ... }",
|
|
35
|
+
convertDefaultToDeclaration: "Convert to named function declaration: export default {{ async }}function functionName({{ params }}) { ... }",
|
|
36
|
+
},
|
|
37
|
+
schema: [],
|
|
38
|
+
},
|
|
39
|
+
defaultOptions: [],
|
|
40
|
+
create(context) {
|
|
41
|
+
const sourceCode = context.sourceCode;
|
|
42
|
+
function getParamsText(node) {
|
|
43
|
+
if (node.params.length === 0)
|
|
44
|
+
return "";
|
|
45
|
+
return node.params.map((p) => sourceCode.getText(p)).join(", ");
|
|
46
|
+
}
|
|
47
|
+
function getFunctionParts(node) {
|
|
48
|
+
const asyncStr = node.async ? "async " : "";
|
|
49
|
+
const params = getParamsText(node);
|
|
50
|
+
const typeParamsText = node.typeParameters
|
|
51
|
+
? sourceCode.getText(node.typeParameters)
|
|
52
|
+
: "";
|
|
53
|
+
const returnTypeText = node.returnType
|
|
54
|
+
? sourceCode.getText(node.returnType)
|
|
55
|
+
: "";
|
|
56
|
+
let bodyText;
|
|
57
|
+
if (node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
58
|
+
bodyText = sourceCode.getText(node.body);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
bodyText = `{\n return ${sourceCode.getText(node.body)};\n}`;
|
|
62
|
+
}
|
|
63
|
+
return { asyncStr, params, typeParamsText, returnTypeText, bodyText };
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
ExportNamedDeclaration(node) {
|
|
67
|
+
if (!node.declaration ||
|
|
68
|
+
node.declaration.type !== utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
for (const declarator of node.declaration.declarations) {
|
|
72
|
+
if (!declarator.init ||
|
|
73
|
+
(declarator.init.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
74
|
+
declarator.init.type !== utils_1.AST_NODE_TYPES.FunctionExpression)) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const name = declarator.id.type === utils_1.AST_NODE_TYPES.Identifier
|
|
78
|
+
? declarator.id.name
|
|
79
|
+
: "unknown";
|
|
80
|
+
const funcNode = declarator.init;
|
|
81
|
+
const { asyncStr, params, typeParamsText, returnTypeText, bodyText } = getFunctionParts(funcNode);
|
|
82
|
+
context.report({
|
|
83
|
+
node: declarator,
|
|
84
|
+
messageId: "noExportedFunctionExpression",
|
|
85
|
+
data: { name, async: asyncStr, params },
|
|
86
|
+
suggest: [
|
|
87
|
+
{
|
|
88
|
+
messageId: "convertToDeclaration",
|
|
89
|
+
data: { name, async: asyncStr, params },
|
|
90
|
+
fix(fixer) {
|
|
91
|
+
const declaration = `${asyncStr}function ${name}${typeParamsText}(${params})${returnTypeText} ${bodyText}`;
|
|
92
|
+
return fixer.replaceText(node, `export ${declaration}`);
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
ExportDefaultDeclaration(node) {
|
|
100
|
+
const declaration = node.declaration;
|
|
101
|
+
// Catch arrow functions, function expressions, and anonymous function declarations
|
|
102
|
+
const isArrowOrExpression = declaration.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
103
|
+
declaration.type === utils_1.AST_NODE_TYPES.FunctionExpression;
|
|
104
|
+
const isAnonymousDeclaration = declaration.type === utils_1.AST_NODE_TYPES.FunctionDeclaration &&
|
|
105
|
+
!declaration.id;
|
|
106
|
+
if (!isArrowOrExpression && !isAnonymousDeclaration) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const funcNode = declaration;
|
|
110
|
+
const { asyncStr, params, typeParamsText, returnTypeText, bodyText } = getFunctionParts(funcNode);
|
|
111
|
+
context.report({
|
|
112
|
+
node: declaration,
|
|
113
|
+
messageId: "noDefaultFunctionExpression",
|
|
114
|
+
data: { async: asyncStr, params },
|
|
115
|
+
suggest: [
|
|
116
|
+
{
|
|
117
|
+
messageId: "convertDefaultToDeclaration",
|
|
118
|
+
data: { async: asyncStr, params },
|
|
119
|
+
fix(fixer) {
|
|
120
|
+
const newDeclaration = `${asyncStr}function functionName${typeParamsText}(${params})${returnTypeText} ${bodyText}`;
|
|
121
|
+
return fixer.replaceText(node, `export default ${newDeclaration}`);
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
//# sourceMappingURL=no-exported-function-expressions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-exported-function-expressions.js","sourceRoot":"","sources":["../../src/rules/no-exported-function-expressions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
declare const _default: import("@typescript-eslint/utils/dist/ts-eslint").RuleModule<"noInlineDisable", [], unknown, import("@typescript-eslint/utils/dist/ts-eslint").RuleListener> & {
|
|
2
|
+
name: string;
|
|
3
|
+
};
|
|
4
|
+
export default _default;
|
|
5
|
+
//# sourceMappingURL=no-inline-disable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-inline-disable.d.ts","sourceRoot":"","sources":["../../src/rules/no-inline-disable.ts"],"names":[],"mappings":";;;AAIA,wBAgDG"}
|