eslint-plugin-conventions 4.0.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 +80 -0
- package/package.json +55 -0
- package/src/index.d.ts +198 -0
- package/src/index.js +60 -0
- package/src/index.js.map +1 -0
- package/src/lib/eslint-plugin-conventions.d.ts +1 -0
- package/src/lib/eslint-plugin-conventions.js +7 -0
- package/src/lib/eslint-plugin-conventions.js.map +1 -0
- package/src/rules/conventions/consistent-existence-index-check.d.ts +19 -0
- package/src/rules/conventions/consistent-existence-index-check.js +141 -0
- package/src/rules/conventions/consistent-existence-index-check.js.map +1 -0
- package/src/rules/conventions/expiring-todo-comments.d.ts +24 -0
- package/src/rules/conventions/expiring-todo-comments.js +307 -0
- package/src/rules/conventions/expiring-todo-comments.js.map +1 -0
- package/src/rules/conventions/filename-case.d.ts +32 -0
- package/src/rules/conventions/filename-case.js +264 -0
- package/src/rules/conventions/filename-case.js.map +1 -0
- package/src/rules/conventions/no-commented-code.d.ts +26 -0
- package/src/rules/conventions/no-commented-code.js +278 -0
- package/src/rules/conventions/no-commented-code.js.map +1 -0
- package/src/rules/conventions/no-console-spaces.d.ts +13 -0
- package/src/rules/conventions/no-console-spaces.js +128 -0
- package/src/rules/conventions/no-console-spaces.js.map +1 -0
- package/src/rules/conventions/prefer-code-point.d.ts +13 -0
- package/src/rules/conventions/prefer-code-point.js +95 -0
- package/src/rules/conventions/prefer-code-point.js.map +1 -0
- package/src/rules/conventions/prefer-dependency-version-strategy.d.ts +29 -0
- package/src/rules/conventions/prefer-dependency-version-strategy.js +226 -0
- package/src/rules/conventions/prefer-dependency-version-strategy.js.map +1 -0
- package/src/rules/conventions/prefer-dom-node-text-content.d.ts +13 -0
- package/src/rules/conventions/prefer-dom-node-text-content.js +147 -0
- package/src/rules/conventions/prefer-dom-node-text-content.js.map +1 -0
- package/src/rules/deprecation/no-deprecated-api.d.ts +30 -0
- package/src/rules/deprecation/no-deprecated-api.js +174 -0
- package/src/rules/deprecation/no-deprecated-api.js.map +1 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2025 Ofri Peretz
|
|
4
|
+
* Licensed under the MIT License. Use of this source code is governed by the
|
|
5
|
+
* MIT license that can be found in the LICENSE file.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.expiringTodoComments = void 0;
|
|
9
|
+
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
|
+
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
11
|
+
const eslint_devkit_3 = require("@interlace/eslint-devkit");
|
|
12
|
+
const eslint_devkit_4 = require("@interlace/eslint-devkit");
|
|
13
|
+
exports.expiringTodoComments = (0, eslint_devkit_1.createRule)({
|
|
14
|
+
name: 'expiring-todo-comments',
|
|
15
|
+
meta: {
|
|
16
|
+
type: 'problem',
|
|
17
|
+
docs: {
|
|
18
|
+
description: 'Add expiration conditions to TODO comments to prevent forgotten tasks',
|
|
19
|
+
},
|
|
20
|
+
hasSuggestions: false,
|
|
21
|
+
messages: {
|
|
22
|
+
expiringTodoComment: (0, eslint_devkit_2.formatLLMMessage)({
|
|
23
|
+
icon: eslint_devkit_2.MessageIcons.WARNING,
|
|
24
|
+
issueName: 'Expiring TODO Comment',
|
|
25
|
+
description: 'TODO comment has expired condition',
|
|
26
|
+
severity: 'HIGH',
|
|
27
|
+
fix: 'Address the TODO or update/remove the condition',
|
|
28
|
+
documentationLink: 'https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/expiring-todo-comments.md',
|
|
29
|
+
}),
|
|
30
|
+
invalidTodoCondition: (0, eslint_devkit_2.formatLLMMessage)({
|
|
31
|
+
icon: eslint_devkit_2.MessageIcons.WARNING,
|
|
32
|
+
issueName: 'Invalid TODO Condition',
|
|
33
|
+
description: '{{term}} TODO comment has invalid condition format',
|
|
34
|
+
severity: 'MEDIUM',
|
|
35
|
+
fix: 'Fix the condition format or remove the condition',
|
|
36
|
+
documentationLink: 'https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/expiring-todo-comments.md',
|
|
37
|
+
}),
|
|
38
|
+
multipleTodoConditions: (0, eslint_devkit_2.formatLLMMessage)({
|
|
39
|
+
icon: eslint_devkit_2.MessageIcons.WARNING,
|
|
40
|
+
issueName: 'Multiple TODO Conditions',
|
|
41
|
+
description: 'TODO comment has multiple conditions',
|
|
42
|
+
severity: 'MEDIUM',
|
|
43
|
+
fix: 'Use only one condition per TODO comment',
|
|
44
|
+
documentationLink: 'https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/expiring-todo-comments.md',
|
|
45
|
+
}),
|
|
46
|
+
},
|
|
47
|
+
schema: [
|
|
48
|
+
{
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
terms: {
|
|
52
|
+
type: 'array',
|
|
53
|
+
items: { type: 'string' },
|
|
54
|
+
default: ['TODO', 'FIXME', 'XXX'],
|
|
55
|
+
},
|
|
56
|
+
dateFormat: {
|
|
57
|
+
type: 'string',
|
|
58
|
+
default: 'YYYY-MM-DD',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
additionalProperties: false,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
defaultOptions: [
|
|
66
|
+
{ terms: ['TODO', 'FIXME', 'XXX'], dateFormat: 'YYYY-MM-DD' },
|
|
67
|
+
],
|
|
68
|
+
create(context) {
|
|
69
|
+
const [options] = context.options;
|
|
70
|
+
const { terms = ['TODO', 'FIXME', 'XXX'] } = options || {};
|
|
71
|
+
// Cache package.json data
|
|
72
|
+
let packageJson = null;
|
|
73
|
+
function loadPackageJson() {
|
|
74
|
+
if (packageJson !== null) {
|
|
75
|
+
return packageJson;
|
|
76
|
+
}
|
|
77
|
+
const packageJsonPath = (0, eslint_devkit_4.resolvePath)(process.cwd(), 'package.json');
|
|
78
|
+
packageJson = (0, eslint_devkit_3.readJsonFileSync)(packageJsonPath);
|
|
79
|
+
return packageJson;
|
|
80
|
+
}
|
|
81
|
+
function parseSemver(version) {
|
|
82
|
+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
83
|
+
if (!match)
|
|
84
|
+
return null;
|
|
85
|
+
return {
|
|
86
|
+
major: parseInt(match[1], 10),
|
|
87
|
+
minor: parseInt(match[2], 10),
|
|
88
|
+
patch: parseInt(match[3], 10),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function compareVersions(version1, version2) {
|
|
92
|
+
const v1 = parseSemver(version1);
|
|
93
|
+
const v2 = parseSemver(version2);
|
|
94
|
+
if (!v1 || !v2)
|
|
95
|
+
return 0;
|
|
96
|
+
if (v1.major !== v2.major)
|
|
97
|
+
return v1.major - v2.major;
|
|
98
|
+
if (v1.minor !== v2.minor)
|
|
99
|
+
return v1.minor - v2.minor;
|
|
100
|
+
return v1.patch - v2.patch;
|
|
101
|
+
}
|
|
102
|
+
function checkDateCondition(dateStr) {
|
|
103
|
+
try {
|
|
104
|
+
const conditionDate = new Date(dateStr + 'T00:00:00Z'); // UTC
|
|
105
|
+
const now = new Date();
|
|
106
|
+
return now >= conditionDate;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function checkPackageVersionCondition(operator, targetVersion) {
|
|
113
|
+
const pkg = loadPackageJson();
|
|
114
|
+
if (!pkg?.version)
|
|
115
|
+
return false;
|
|
116
|
+
const currentVersion = pkg.version;
|
|
117
|
+
const comparison = compareVersions(currentVersion, targetVersion);
|
|
118
|
+
switch (operator) {
|
|
119
|
+
case '>':
|
|
120
|
+
return comparison > 0;
|
|
121
|
+
case '>=':
|
|
122
|
+
return comparison >= 0;
|
|
123
|
+
case '<':
|
|
124
|
+
return comparison < 0;
|
|
125
|
+
case '<=':
|
|
126
|
+
return comparison <= 0;
|
|
127
|
+
case '=':
|
|
128
|
+
case '==':
|
|
129
|
+
return comparison === 0;
|
|
130
|
+
default:
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function checkEngineVersionCondition(engine, operator, targetVersion) {
|
|
135
|
+
if (engine !== 'node')
|
|
136
|
+
return false;
|
|
137
|
+
const pkg = loadPackageJson();
|
|
138
|
+
const nodeVersion = pkg?.engines?.node;
|
|
139
|
+
if (!nodeVersion)
|
|
140
|
+
return false;
|
|
141
|
+
// Simple version comparison for engines
|
|
142
|
+
const currentVersion = nodeVersion.replace(/^[\^~>=<]+/, '');
|
|
143
|
+
const comparison = compareVersions(currentVersion, targetVersion);
|
|
144
|
+
switch (operator) {
|
|
145
|
+
case '>':
|
|
146
|
+
return comparison > 0;
|
|
147
|
+
case '>=':
|
|
148
|
+
return comparison >= 0;
|
|
149
|
+
case '<':
|
|
150
|
+
return comparison < 0;
|
|
151
|
+
case '<=':
|
|
152
|
+
return comparison <= 0;
|
|
153
|
+
default:
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function checkDependencyCondition(packageName, present) {
|
|
158
|
+
const pkg = loadPackageJson();
|
|
159
|
+
if (!pkg)
|
|
160
|
+
return false;
|
|
161
|
+
const allDeps = {
|
|
162
|
+
...pkg.dependencies,
|
|
163
|
+
...pkg.devDependencies,
|
|
164
|
+
...pkg.peerDependencies,
|
|
165
|
+
};
|
|
166
|
+
const hasPackage = packageName in allDeps;
|
|
167
|
+
return present ? hasPackage : hasPackage;
|
|
168
|
+
}
|
|
169
|
+
function parseTodoCondition(commentValue, term) {
|
|
170
|
+
// Match patterns like: TODO [condition]: message
|
|
171
|
+
// or TODO (@author) [condition]: message
|
|
172
|
+
// Also handle block comments with * prefix: * TODO [condition]: message
|
|
173
|
+
const todoRegex = new RegExp(`\\*?\\s*${term}\\s*(?:\\([^)]+\\))?\\s*\\[([^\\]]+)\\]\\s*:\\s*(.+)$`, 'im');
|
|
174
|
+
const match = commentValue.match(todoRegex);
|
|
175
|
+
if (!match)
|
|
176
|
+
return null;
|
|
177
|
+
const conditionStr = match[1];
|
|
178
|
+
const rest = match[2];
|
|
179
|
+
// Split multiple conditions (shouldn't happen, but handle gracefully)
|
|
180
|
+
const conditions = conditionStr.split(/\s*,\s*/);
|
|
181
|
+
return { conditions, rest };
|
|
182
|
+
}
|
|
183
|
+
function validateCondition(condition) {
|
|
184
|
+
// Date condition: YYYY-MM-DD
|
|
185
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(condition)) {
|
|
186
|
+
return { type: 'date', value: condition };
|
|
187
|
+
}
|
|
188
|
+
// Package version condition: >=1.0.0, >2.0.0, etc.
|
|
189
|
+
const packageVersionMatch = condition.match(/^([><]=?|=|==)\s*([\d.]+)$/);
|
|
190
|
+
if (packageVersionMatch) {
|
|
191
|
+
return {
|
|
192
|
+
type: 'package-version',
|
|
193
|
+
operator: packageVersionMatch[1],
|
|
194
|
+
value: packageVersionMatch[2],
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
// Engine version condition: engine:node@>=8, engine:node@>20.0.0
|
|
198
|
+
const engineVersionMatch = condition.match(/^engine:(\w+)@([><]=?)\s*([\d.]+)$/);
|
|
199
|
+
if (engineVersionMatch) {
|
|
200
|
+
const engine = engineVersionMatch[1];
|
|
201
|
+
// Validate engine name
|
|
202
|
+
if (!['node', 'npm', 'yarn', 'pnpm'].includes(engine)) {
|
|
203
|
+
return null; // Invalid engine, let it be caught as invalid condition
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
type: 'engine-version',
|
|
207
|
+
value: `${engine}@${engineVersionMatch[3]}`,
|
|
208
|
+
operator: engineVersionMatch[2],
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
// Dependency condition: +package-name or -package-name
|
|
212
|
+
const depMatch = condition.match(/^([+-])(.+)$/);
|
|
213
|
+
if (depMatch) {
|
|
214
|
+
return {
|
|
215
|
+
type: 'dependency',
|
|
216
|
+
value: depMatch[2],
|
|
217
|
+
operator: depMatch[1],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
function checkCondition(condition) {
|
|
223
|
+
const parsed = validateCondition(condition);
|
|
224
|
+
if (!parsed)
|
|
225
|
+
return false;
|
|
226
|
+
switch (parsed.type) {
|
|
227
|
+
case 'date':
|
|
228
|
+
return checkDateCondition(parsed.value);
|
|
229
|
+
case 'package-version':
|
|
230
|
+
return parsed.operator
|
|
231
|
+
? checkPackageVersionCondition(parsed.operator, parsed.value)
|
|
232
|
+
: false;
|
|
233
|
+
case 'engine-version': {
|
|
234
|
+
const [engine, version] = parsed.value.split('@');
|
|
235
|
+
return parsed.operator
|
|
236
|
+
? checkEngineVersionCondition(engine, parsed.operator, version)
|
|
237
|
+
: false;
|
|
238
|
+
}
|
|
239
|
+
case 'dependency':
|
|
240
|
+
return checkDependencyCondition(parsed.value, parsed.operator === '+');
|
|
241
|
+
default:
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function checkComment(comment) {
|
|
246
|
+
const commentValue = comment.value;
|
|
247
|
+
for (const term of terms) {
|
|
248
|
+
const parsed = parseTodoCondition(commentValue, term);
|
|
249
|
+
if (!parsed)
|
|
250
|
+
continue;
|
|
251
|
+
const { conditions, rest } = parsed;
|
|
252
|
+
// Check for multiple conditions
|
|
253
|
+
if (conditions.length > 1) {
|
|
254
|
+
context.report({
|
|
255
|
+
loc: comment.loc,
|
|
256
|
+
messageId: 'multipleTodoConditions',
|
|
257
|
+
data: {
|
|
258
|
+
term,
|
|
259
|
+
conditions: conditions.join(', '),
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
const condition = conditions[0];
|
|
265
|
+
// Validate condition format
|
|
266
|
+
const parsedCondition = validateCondition(condition);
|
|
267
|
+
if (!parsedCondition) {
|
|
268
|
+
context.report({
|
|
269
|
+
loc: comment.loc,
|
|
270
|
+
messageId: 'invalidTodoCondition',
|
|
271
|
+
data: {
|
|
272
|
+
condition,
|
|
273
|
+
term,
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
// Check if condition has expired
|
|
279
|
+
if (checkCondition(condition)) {
|
|
280
|
+
context.report({
|
|
281
|
+
loc: comment.loc,
|
|
282
|
+
messageId: 'expiringTodoComment',
|
|
283
|
+
data: {
|
|
284
|
+
term,
|
|
285
|
+
condition,
|
|
286
|
+
message: rest,
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
// Only process one term per comment
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
Program() {
|
|
296
|
+
const sourceCode = context.sourceCode;
|
|
297
|
+
const comments = sourceCode.getAllComments();
|
|
298
|
+
for (const comment of comments) {
|
|
299
|
+
if (comment.type === 'Line' || comment.type === 'Block') {
|
|
300
|
+
checkComment(comment);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
//# sourceMappingURL=expiring-todo-comments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expiring-todo-comments.js","sourceRoot":"","sources":["../../../../../../packages/eslint-plugin-conventions/src/rules/conventions/expiring-todo-comments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Ofri Peretz
|
|
3
|
+
* Licensed under the MIT License. Use of this source code is governed by the
|
|
4
|
+
* MIT license that can be found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* ESLint Rule: filename-case
|
|
8
|
+
* Enforce filename case conventions
|
|
9
|
+
*/
|
|
10
|
+
import type { TSESLint } from '@interlace/eslint-devkit';
|
|
11
|
+
export type CaseType = 'camelCase' | 'kebabCase' | 'pascalCase' | 'snakeCase';
|
|
12
|
+
export interface Options {
|
|
13
|
+
/** Case convention to enforce */
|
|
14
|
+
case?: CaseType;
|
|
15
|
+
/** List of patterns to ignore completely */
|
|
16
|
+
ignore?: (string | RegExp)[];
|
|
17
|
+
/** List of uppercase filenames to allow (without extension). Set to empty array to disable. */
|
|
18
|
+
allowedUppercaseFiles?: string[];
|
|
19
|
+
/** List of filenames allowed to use kebab-case regardless of the global case setting */
|
|
20
|
+
allowedKebabCase?: string[];
|
|
21
|
+
/** List of filenames allowed to use snake_case regardless of the global case setting */
|
|
22
|
+
allowedSnakeCase?: string[];
|
|
23
|
+
/** List of filenames allowed to use camelCase regardless of the global case setting */
|
|
24
|
+
allowedCamelCase?: string[];
|
|
25
|
+
/** List of filenames allowed to use PascalCase regardless of the global case setting */
|
|
26
|
+
allowedPascalCase?: string[];
|
|
27
|
+
}
|
|
28
|
+
type RuleOptions = [Options?];
|
|
29
|
+
export declare const filenameCase: TSESLint.RuleModule<"filenameCase", RuleOptions, unknown, TSESLint.RuleListener> & {
|
|
30
|
+
name: string;
|
|
31
|
+
};
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2025 Ofri Peretz
|
|
4
|
+
* Licensed under the MIT License. Use of this source code is governed by the
|
|
5
|
+
* MIT license that can be found in the LICENSE file.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.filenameCase = void 0;
|
|
9
|
+
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
|
+
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
11
|
+
const eslint_devkit_3 = require("@interlace/eslint-devkit");
|
|
12
|
+
/**
|
|
13
|
+
* Default uppercase filenames that are conventionally valid across all case conventions
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_ALLOWED_UPPERCASE_FILES = [
|
|
16
|
+
'README',
|
|
17
|
+
'LICENSE',
|
|
18
|
+
'CHANGELOG',
|
|
19
|
+
'CONTRIBUTING',
|
|
20
|
+
'AUTHORS',
|
|
21
|
+
'COPYING',
|
|
22
|
+
'SECURITY',
|
|
23
|
+
'NOTICE',
|
|
24
|
+
'PATENTS',
|
|
25
|
+
'VERSION',
|
|
26
|
+
];
|
|
27
|
+
exports.filenameCase = (0, eslint_devkit_1.createRule)({
|
|
28
|
+
name: 'filename-case',
|
|
29
|
+
meta: {
|
|
30
|
+
type: 'problem',
|
|
31
|
+
docs: {
|
|
32
|
+
description: 'Enforce filename case conventions for consistency',
|
|
33
|
+
},
|
|
34
|
+
hasSuggestions: true,
|
|
35
|
+
messages: {
|
|
36
|
+
filenameCase: (0, eslint_devkit_2.formatLLMMessage)({
|
|
37
|
+
icon: eslint_devkit_2.MessageIcons.WARNING,
|
|
38
|
+
issueName: 'Filename Case Convention',
|
|
39
|
+
description: 'Filename "{{current}}" violates {{case}} naming convention',
|
|
40
|
+
severity: 'MEDIUM',
|
|
41
|
+
fix: 'Rename file from "{{current}}" to "{{suggested}}" following {{case}} convention',
|
|
42
|
+
documentationLink: 'https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/filename-case.md',
|
|
43
|
+
}),
|
|
44
|
+
},
|
|
45
|
+
schema: [
|
|
46
|
+
{
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
case: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
enum: ['camelCase', 'kebabCase', 'pascalCase', 'snakeCase'],
|
|
52
|
+
default: 'kebabCase',
|
|
53
|
+
},
|
|
54
|
+
ignore: {
|
|
55
|
+
type: 'array',
|
|
56
|
+
items: {
|
|
57
|
+
anyOf: [
|
|
58
|
+
{ type: 'string' },
|
|
59
|
+
{ type: 'object' },
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
default: [],
|
|
63
|
+
},
|
|
64
|
+
allowedUppercaseFiles: {
|
|
65
|
+
type: 'array',
|
|
66
|
+
items: { type: 'string' },
|
|
67
|
+
description: 'List of uppercase filenames to allow (without extension). Defaults to common files like README, LICENSE, etc.',
|
|
68
|
+
},
|
|
69
|
+
allowedKebabCase: {
|
|
70
|
+
type: 'array',
|
|
71
|
+
items: { type: 'string' },
|
|
72
|
+
description: 'List of filenames allowed to use kebab-case regardless of the global case setting.',
|
|
73
|
+
default: [],
|
|
74
|
+
},
|
|
75
|
+
allowedSnakeCase: {
|
|
76
|
+
type: 'array',
|
|
77
|
+
items: { type: 'string' },
|
|
78
|
+
description: 'List of filenames allowed to use snake_case regardless of the global case setting.',
|
|
79
|
+
default: [],
|
|
80
|
+
},
|
|
81
|
+
allowedCamelCase: {
|
|
82
|
+
type: 'array',
|
|
83
|
+
items: { type: 'string' },
|
|
84
|
+
description: 'List of filenames allowed to use camelCase regardless of the global case setting.',
|
|
85
|
+
default: [],
|
|
86
|
+
},
|
|
87
|
+
allowedPascalCase: {
|
|
88
|
+
type: 'array',
|
|
89
|
+
items: { type: 'string' },
|
|
90
|
+
description: 'List of filenames allowed to use PascalCase regardless of the global case setting.',
|
|
91
|
+
default: [],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
additionalProperties: false,
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
defaultOptions: [{
|
|
99
|
+
case: 'kebabCase',
|
|
100
|
+
ignore: [],
|
|
101
|
+
allowedUppercaseFiles: DEFAULT_ALLOWED_UPPERCASE_FILES,
|
|
102
|
+
allowedKebabCase: [],
|
|
103
|
+
allowedSnakeCase: [],
|
|
104
|
+
allowedCamelCase: [],
|
|
105
|
+
allowedPascalCase: [],
|
|
106
|
+
}],
|
|
107
|
+
create(context) {
|
|
108
|
+
const [options] = context.options;
|
|
109
|
+
const { case: caseType = 'kebabCase', ignore = [], allowedUppercaseFiles = DEFAULT_ALLOWED_UPPERCASE_FILES, allowedKebabCase = [], allowedSnakeCase = [], allowedCamelCase = [], allowedPascalCase = [], } = options || {};
|
|
110
|
+
// Convert to different case formats
|
|
111
|
+
function toCamelCase(str) {
|
|
112
|
+
return str.replace(/[-_](.)/g, (_, letter) => letter.toUpperCase());
|
|
113
|
+
}
|
|
114
|
+
function toKebabCase(str) {
|
|
115
|
+
return str
|
|
116
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
117
|
+
.replace(/[\s_]+/g, '-')
|
|
118
|
+
.toLowerCase();
|
|
119
|
+
}
|
|
120
|
+
function toPascalCase(str) {
|
|
121
|
+
const camel = toCamelCase(str);
|
|
122
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
123
|
+
}
|
|
124
|
+
function toSnakeCase(str) {
|
|
125
|
+
return str
|
|
126
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
127
|
+
.replace(/[\s-]+/g, '_')
|
|
128
|
+
.toLowerCase();
|
|
129
|
+
}
|
|
130
|
+
// Check if filename matches the expected case pattern
|
|
131
|
+
function isCamelCase(str) {
|
|
132
|
+
// camelCase: starts with lowercase, no separators, can have uppercase after first char
|
|
133
|
+
return /^[a-z][a-zA-Z0-9]*$/.test(str);
|
|
134
|
+
}
|
|
135
|
+
function isKebabCase(str) {
|
|
136
|
+
// kebab-case: all lowercase with hyphens as separators
|
|
137
|
+
return /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(str);
|
|
138
|
+
}
|
|
139
|
+
function isPascalCase(str) {
|
|
140
|
+
// PascalCase: starts with uppercase, no separators
|
|
141
|
+
return /^[A-Z][a-zA-Z0-9]*$/.test(str);
|
|
142
|
+
}
|
|
143
|
+
function isSnakeCase(str) {
|
|
144
|
+
// snake_case: all lowercase with underscores as separators
|
|
145
|
+
return /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(str);
|
|
146
|
+
}
|
|
147
|
+
function matchesCase(str, expectedCase) {
|
|
148
|
+
switch (expectedCase) {
|
|
149
|
+
case 'camelCase':
|
|
150
|
+
return isCamelCase(str);
|
|
151
|
+
case 'kebabCase':
|
|
152
|
+
return isKebabCase(str);
|
|
153
|
+
case 'pascalCase':
|
|
154
|
+
return isPascalCase(str);
|
|
155
|
+
case 'snakeCase':
|
|
156
|
+
return isSnakeCase(str);
|
|
157
|
+
default:
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Generate suggested filename
|
|
162
|
+
function getSuggestedName(basename, nameWithoutExt, targetCase) {
|
|
163
|
+
let transformed;
|
|
164
|
+
switch (targetCase) {
|
|
165
|
+
case 'camelCase':
|
|
166
|
+
transformed = toCamelCase(nameWithoutExt);
|
|
167
|
+
break;
|
|
168
|
+
case 'kebabCase':
|
|
169
|
+
transformed = toKebabCase(nameWithoutExt);
|
|
170
|
+
break;
|
|
171
|
+
case 'pascalCase':
|
|
172
|
+
transformed = toPascalCase(nameWithoutExt);
|
|
173
|
+
break;
|
|
174
|
+
case 'snakeCase':
|
|
175
|
+
transformed = toSnakeCase(nameWithoutExt);
|
|
176
|
+
break;
|
|
177
|
+
default:
|
|
178
|
+
transformed = nameWithoutExt;
|
|
179
|
+
}
|
|
180
|
+
// Add back the extension
|
|
181
|
+
const ext = basename.slice(nameWithoutExt.length);
|
|
182
|
+
return transformed + ext;
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
Program(node) {
|
|
186
|
+
// Get filename from context
|
|
187
|
+
const filename = context.getFilename();
|
|
188
|
+
if (!filename) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
// Get just the filename part (without directory)
|
|
192
|
+
const basename = (0, eslint_devkit_3.getBasename)(filename);
|
|
193
|
+
// Remove all extensions for checking (handles .spec.ts, .test.tsx, etc.)
|
|
194
|
+
const nameWithoutExt = basename.replace(/(\.(spec|test|stories|story|e2e|d))*\.[^.]+$/, '');
|
|
195
|
+
// Skip if no name part (e.g., just extension or dotfile)
|
|
196
|
+
if (!nameWithoutExt) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
// Check if filename should be ignored
|
|
200
|
+
for (const ignorePattern of ignore) {
|
|
201
|
+
if (typeof ignorePattern === 'string') {
|
|
202
|
+
if (basename === ignorePattern) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else if (ignorePattern instanceof RegExp) {
|
|
207
|
+
if (ignorePattern.test(basename)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Skip allowed uppercase files (README, LICENSE, etc.) as they are conventionally valid
|
|
213
|
+
if (allowedUppercaseFiles.includes(nameWithoutExt)) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
// Skip dotfiles (.eslintrc, .gitignore, etc.)
|
|
217
|
+
if (basename.startsWith('.')) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
// Check if filename is allowed via case-specific overrides
|
|
221
|
+
// These allow specific files to use a different case than the global setting
|
|
222
|
+
if (allowedKebabCase.includes(nameWithoutExt) && matchesCase(nameWithoutExt, 'kebabCase')) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (allowedSnakeCase.includes(nameWithoutExt) && matchesCase(nameWithoutExt, 'snakeCase')) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (allowedCamelCase.includes(nameWithoutExt) && matchesCase(nameWithoutExt, 'camelCase')) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (allowedPascalCase.includes(nameWithoutExt) && matchesCase(nameWithoutExt, 'pascalCase')) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
// Check if filename matches the expected case
|
|
235
|
+
if (!matchesCase(nameWithoutExt, caseType)) {
|
|
236
|
+
const suggestedName = getSuggestedName(basename, nameWithoutExt, caseType);
|
|
237
|
+
context.report({
|
|
238
|
+
node, // Report on the Program node
|
|
239
|
+
messageId: 'filenameCase',
|
|
240
|
+
data: {
|
|
241
|
+
case: caseType,
|
|
242
|
+
current: basename,
|
|
243
|
+
suggested: suggestedName,
|
|
244
|
+
},
|
|
245
|
+
suggest: [
|
|
246
|
+
{
|
|
247
|
+
messageId: 'filenameCase',
|
|
248
|
+
data: {
|
|
249
|
+
suggestedName,
|
|
250
|
+
},
|
|
251
|
+
fix() {
|
|
252
|
+
// This is a file rename operation that can't be auto-fixed
|
|
253
|
+
// Just provide the suggestion
|
|
254
|
+
return null;
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
//# sourceMappingURL=filename-case.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filename-case.js","sourceRoot":"","sources":["../../../../../../packages/eslint-plugin-conventions/src/rules/conventions/filename-case.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Ofri Peretz
|
|
3
|
+
* Licensed under the MIT License. Use of this source code is governed by the
|
|
4
|
+
* MIT license that can be found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* ESLint Rule: no-commented-code
|
|
8
|
+
* Detects commented-out code blocks
|
|
9
|
+
*
|
|
10
|
+
* @see https://rules.sonarsource.com/javascript/RSPEC-125/
|
|
11
|
+
*/
|
|
12
|
+
import type { TSESLint } from '@interlace/eslint-devkit';
|
|
13
|
+
type MessageIds = 'commentedCode' | 'removeCode' | 'useVersionControl';
|
|
14
|
+
export interface Options {
|
|
15
|
+
/** Ignore single-line comments. Default: false */
|
|
16
|
+
ignoreSingleLine?: boolean;
|
|
17
|
+
/** Ignore comments in test files. Default: true */
|
|
18
|
+
ignoreInTests?: boolean;
|
|
19
|
+
/** Minimum lines of commented code to trigger. Default: 1 */
|
|
20
|
+
minLines?: number;
|
|
21
|
+
}
|
|
22
|
+
type RuleOptions = [Options?];
|
|
23
|
+
export declare const noCommentedCode: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener> & {
|
|
24
|
+
name: string;
|
|
25
|
+
};
|
|
26
|
+
export {};
|