eslint-plugin-conventions 4.2.2 → 4.2.4
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/package.json +19 -19
- package/src/index.d.ts +15 -173
- package/src/index.js +1 -8
- package/src/lib/eslint-plugin-conventions.js +0 -1
- package/src/oxlint.d.ts +17 -20
- package/src/oxlint.js +0 -19
- package/src/rules/conventions/analytics-event-naming.js +0 -11
- package/src/rules/conventions/consistent-existence-index-check.js +0 -15
- package/src/rules/conventions/expiring-todo-comments.js +2 -35
- package/src/rules/conventions/filename-case.js +1 -40
- package/src/rules/conventions/no-commented-code.js +1 -43
- package/src/rules/conventions/no-console-spaces.js +0 -20
- package/src/rules/conventions/no-json-schema-tags.js +1 -28
- package/src/rules/conventions/no-magic-numbers.js +1 -21
- package/src/rules/conventions/no-raw-cross-property-href.js +0 -10
- package/src/rules/conventions/prefer-code-point.js +0 -18
- package/src/rules/conventions/prefer-dependency-version-strategy.js +0 -26
- package/src/rules/conventions/prefer-dom-node-text-content.js +0 -20
- package/src/rules/conventions/require-data-testid.js +1 -31
- package/src/rules/conventions/utm-taxonomy.js +0 -11
- package/src/rules/deprecation/no-deprecated-api.js +1 -19
- package/CHANGELOG.md +0 -164
- package/src/index.js.map +0 -1
- package/src/lib/eslint-plugin-conventions.d.ts +0 -1
- package/src/lib/eslint-plugin-conventions.js.map +0 -1
- package/src/oxlint.js.map +0 -1
- package/src/rules/conventions/analytics-event-naming.d.ts +0 -41
- package/src/rules/conventions/analytics-event-naming.js.map +0 -1
- package/src/rules/conventions/consistent-existence-index-check.d.ts +0 -19
- package/src/rules/conventions/consistent-existence-index-check.js.map +0 -1
- package/src/rules/conventions/expiring-todo-comments.d.ts +0 -65
- package/src/rules/conventions/expiring-todo-comments.js.map +0 -1
- package/src/rules/conventions/filename-case.d.ts +0 -32
- package/src/rules/conventions/filename-case.js.map +0 -1
- package/src/rules/conventions/no-commented-code.d.ts +0 -26
- package/src/rules/conventions/no-commented-code.js.map +0 -1
- package/src/rules/conventions/no-console-spaces.d.ts +0 -13
- package/src/rules/conventions/no-console-spaces.js.map +0 -1
- package/src/rules/conventions/no-json-schema-tags.d.ts +0 -29
- package/src/rules/conventions/no-json-schema-tags.js.map +0 -1
- package/src/rules/conventions/no-magic-numbers.d.ts +0 -41
- package/src/rules/conventions/no-magic-numbers.js.map +0 -1
- package/src/rules/conventions/no-raw-cross-property-href.d.ts +0 -24
- package/src/rules/conventions/no-raw-cross-property-href.js.map +0 -1
- package/src/rules/conventions/prefer-code-point.d.ts +0 -13
- package/src/rules/conventions/prefer-code-point.js.map +0 -1
- package/src/rules/conventions/prefer-dependency-version-strategy.d.ts +0 -29
- package/src/rules/conventions/prefer-dependency-version-strategy.js.map +0 -1
- package/src/rules/conventions/prefer-dom-node-text-content.d.ts +0 -13
- package/src/rules/conventions/prefer-dom-node-text-content.js.map +0 -1
- package/src/rules/conventions/require-data-testid.d.ts +0 -56
- package/src/rules/conventions/require-data-testid.js.map +0 -1
- package/src/rules/conventions/utm-taxonomy.d.ts +0 -25
- package/src/rules/conventions/utm-taxonomy.js.map +0 -1
- package/src/rules/deprecation/no-deprecated-api.d.ts +0 -30
- package/src/rules/deprecation/no-deprecated-api.js.map +0 -1
|
@@ -1,17 +1,9 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.filenameCase = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
5
|
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
11
6
|
const eslint_devkit_3 = require("@interlace/eslint-devkit");
|
|
12
|
-
/**
|
|
13
|
-
* Default uppercase filenames that are conventionally valid across all case conventions
|
|
14
|
-
*/
|
|
15
7
|
const DEFAULT_ALLOWED_UPPERCASE_FILES = [
|
|
16
8
|
'README',
|
|
17
9
|
'LICENSE',
|
|
@@ -108,12 +100,9 @@ exports.filenameCase = (0, eslint_devkit_1.createRule)({
|
|
|
108
100
|
create(context) {
|
|
109
101
|
const [options] = context.options;
|
|
110
102
|
const { case: caseType = 'kebabCase', ignore = [], allowedUppercaseFiles = DEFAULT_ALLOWED_UPPERCASE_FILES, allowedKebabCase = [], allowedSnakeCase = [], allowedCamelCase = [], allowedPascalCase = [], } = options || {};
|
|
111
|
-
// Convert to different case formats
|
|
112
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
113
103
|
function toCamelCase(str) {
|
|
114
104
|
return str.replace(/[-_](.)/g, (_, letter) => letter.toUpperCase());
|
|
115
105
|
}
|
|
116
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
117
106
|
function toKebabCase(str) {
|
|
118
107
|
return str
|
|
119
108
|
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
@@ -124,32 +113,22 @@ exports.filenameCase = (0, eslint_devkit_1.createRule)({
|
|
|
124
113
|
const camel = toCamelCase(str);
|
|
125
114
|
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
126
115
|
}
|
|
127
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
128
116
|
function toSnakeCase(str) {
|
|
129
117
|
return str
|
|
130
118
|
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
131
119
|
.replace(/[\s-]+/g, '_')
|
|
132
120
|
.toLowerCase();
|
|
133
121
|
}
|
|
134
|
-
// Check if filename matches the expected case pattern
|
|
135
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
136
122
|
function isCamelCase(str) {
|
|
137
|
-
// camelCase: starts with lowercase, no separators, can have uppercase after first char
|
|
138
123
|
return /^[a-z][a-zA-Z0-9]*$/.test(str);
|
|
139
124
|
}
|
|
140
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
141
125
|
function isKebabCase(str) {
|
|
142
|
-
// kebab-case: all lowercase with hyphens as separators
|
|
143
126
|
return /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(str);
|
|
144
127
|
}
|
|
145
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
146
128
|
function isPascalCase(str) {
|
|
147
|
-
// PascalCase: starts with uppercase, no separators
|
|
148
129
|
return /^[A-Z][a-zA-Z0-9]*$/.test(str);
|
|
149
130
|
}
|
|
150
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
151
131
|
function isSnakeCase(str) {
|
|
152
|
-
// snake_case: all lowercase with underscores as separators
|
|
153
132
|
return /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/.test(str);
|
|
154
133
|
}
|
|
155
134
|
function matchesCase(str, expectedCase) {
|
|
@@ -166,11 +145,7 @@ exports.filenameCase = (0, eslint_devkit_1.createRule)({
|
|
|
166
145
|
return true;
|
|
167
146
|
}
|
|
168
147
|
}
|
|
169
|
-
// Generate suggested filename
|
|
170
148
|
function getSuggestedName(basename, nameWithoutExt, targetCase) {
|
|
171
|
-
// Initialized to the untransformed name; every valid CaseType below
|
|
172
|
-
// overwrites it. (An unknown case never reaches here — matchesCase
|
|
173
|
-
// returns true for unknown cases, so no rename is ever suggested.)
|
|
174
149
|
let transformed = nameWithoutExt;
|
|
175
150
|
switch (targetCase) {
|
|
176
151
|
case 'camelCase':
|
|
@@ -186,26 +161,20 @@ exports.filenameCase = (0, eslint_devkit_1.createRule)({
|
|
|
186
161
|
transformed = toSnakeCase(nameWithoutExt);
|
|
187
162
|
break;
|
|
188
163
|
}
|
|
189
|
-
// Add back the extension
|
|
190
164
|
const ext = basename.slice(nameWithoutExt.length);
|
|
191
165
|
return transformed + ext;
|
|
192
166
|
}
|
|
193
167
|
return {
|
|
194
168
|
Program(node) {
|
|
195
|
-
// Get filename from context
|
|
196
169
|
const filename = context.filename;
|
|
197
170
|
if (!filename) {
|
|
198
171
|
return;
|
|
199
172
|
}
|
|
200
|
-
// Get just the filename part (without directory)
|
|
201
173
|
const basename = (0, eslint_devkit_3.getBasename)(filename);
|
|
202
|
-
// Remove all extensions for checking (handles .spec.ts, .test.tsx, etc.)
|
|
203
174
|
const nameWithoutExt = basename.replace(/(\.(spec|test|stories|story|e2e|d))*\.[^.]+$/, '');
|
|
204
|
-
// Skip if no name part (e.g., just extension or dotfile)
|
|
205
175
|
if (!nameWithoutExt) {
|
|
206
176
|
return;
|
|
207
177
|
}
|
|
208
|
-
// Check if filename should be ignored
|
|
209
178
|
for (const ignorePattern of ignore) {
|
|
210
179
|
if (typeof ignorePattern === 'string') {
|
|
211
180
|
if (basename === ignorePattern) {
|
|
@@ -218,16 +187,12 @@ exports.filenameCase = (0, eslint_devkit_1.createRule)({
|
|
|
218
187
|
}
|
|
219
188
|
}
|
|
220
189
|
}
|
|
221
|
-
// Skip allowed uppercase files (README, LICENSE, etc.) as they are conventionally valid
|
|
222
190
|
if (allowedUppercaseFiles.includes(nameWithoutExt)) {
|
|
223
191
|
return;
|
|
224
192
|
}
|
|
225
|
-
// Skip dotfiles (.eslintrc, .gitignore, etc.)
|
|
226
193
|
if (basename.startsWith('.')) {
|
|
227
194
|
return;
|
|
228
195
|
}
|
|
229
|
-
// Check if filename is allowed via case-specific overrides
|
|
230
|
-
// These allow specific files to use a different case than the global setting
|
|
231
196
|
if (allowedKebabCase.includes(nameWithoutExt) && matchesCase(nameWithoutExt, 'kebabCase')) {
|
|
232
197
|
return;
|
|
233
198
|
}
|
|
@@ -240,11 +205,10 @@ exports.filenameCase = (0, eslint_devkit_1.createRule)({
|
|
|
240
205
|
if (allowedPascalCase.includes(nameWithoutExt) && matchesCase(nameWithoutExt, 'pascalCase')) {
|
|
241
206
|
return;
|
|
242
207
|
}
|
|
243
|
-
// Check if filename matches the expected case
|
|
244
208
|
if (!matchesCase(nameWithoutExt, caseType)) {
|
|
245
209
|
const suggestedName = getSuggestedName(basename, nameWithoutExt, caseType);
|
|
246
210
|
context.report({
|
|
247
|
-
node,
|
|
211
|
+
node,
|
|
248
212
|
messageId: 'filenameCase',
|
|
249
213
|
data: {
|
|
250
214
|
case: caseType,
|
|
@@ -258,8 +222,6 @@ exports.filenameCase = (0, eslint_devkit_1.createRule)({
|
|
|
258
222
|
suggestedName,
|
|
259
223
|
},
|
|
260
224
|
fix() {
|
|
261
|
-
// This is a file rename operation that can't be auto-fixed
|
|
262
|
-
// Just provide the suggestion
|
|
263
225
|
return null;
|
|
264
226
|
},
|
|
265
227
|
},
|
|
@@ -270,4 +232,3 @@ exports.filenameCase = (0, eslint_devkit_1.createRule)({
|
|
|
270
232
|
};
|
|
271
233
|
},
|
|
272
234
|
});
|
|
273
|
-
//# sourceMappingURL=filename-case.js.map
|
|
@@ -1,36 +1,23 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.noCommentedCode = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
5
|
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
11
|
-
/**
|
|
12
|
-
* Check if a comment block contains code-like patterns
|
|
13
|
-
*/
|
|
14
6
|
function looksLikeCode(comment, isBlockComment) {
|
|
15
|
-
// Remove comment markers for pattern matching
|
|
16
7
|
let text = comment;
|
|
17
8
|
if (isBlockComment) {
|
|
18
|
-
// Remove /* and */ markers and any leading * on each line
|
|
19
9
|
text = text.replace(/^\s*\/\*+/, '').replace(/\*+\/\s*$/, '');
|
|
20
|
-
// Remove leading * from each line in block comments
|
|
21
10
|
text = text
|
|
22
11
|
.split('\n')
|
|
23
12
|
.map((line) => line.replace(/^\s*\*+\s*/, ''))
|
|
24
13
|
.join('\n');
|
|
25
14
|
}
|
|
26
15
|
else {
|
|
27
|
-
// Remove // markers
|
|
28
16
|
text = text
|
|
29
17
|
.split('\n')
|
|
30
18
|
.map((line) => line.replace(/^\s*\/\/+\s*/, ''))
|
|
31
19
|
.join('\n');
|
|
32
20
|
}
|
|
33
|
-
// Split into lines and check each line
|
|
34
21
|
const lines = text
|
|
35
22
|
.split('\n')
|
|
36
23
|
.map((line) => line.trim())
|
|
@@ -44,36 +31,24 @@ function looksLikeCode(comment, isBlockComment) {
|
|
|
44
31
|
/^[a-zA-Z_$][a-zA-Z0-9_$]*\s*\(/,
|
|
45
32
|
/^[{}[\]]/,
|
|
46
33
|
];
|
|
47
|
-
// Count how many lines look like code
|
|
48
34
|
let codeLikeLines = 0;
|
|
49
|
-
// Check if any line looks like code (but not a TODO comment)
|
|
50
35
|
for (const line of lines) {
|
|
51
|
-
// Skip TODO/FIXME comments
|
|
52
36
|
if (/^(TODO|FIXME|HACK|XXX)/i.test(line)) {
|
|
53
37
|
continue;
|
|
54
38
|
}
|
|
55
|
-
// Check if line matches code patterns
|
|
56
39
|
for (const pattern of codePatterns) {
|
|
57
40
|
if (pattern.test(line)) {
|
|
58
41
|
codeLikeLines++;
|
|
59
|
-
break;
|
|
42
|
+
break;
|
|
60
43
|
}
|
|
61
44
|
}
|
|
62
45
|
}
|
|
63
|
-
// Consider it code if at least one line looks like code
|
|
64
46
|
return codeLikeLines > 0;
|
|
65
47
|
}
|
|
66
|
-
/**
|
|
67
|
-
* Count lines in a comment
|
|
68
|
-
* For single-line comments, the value doesn't include newlines, so we count 1
|
|
69
|
-
* For multi-line comments, we count the newlines in the value + 1
|
|
70
|
-
*/
|
|
71
48
|
function countCommentLines(comment) {
|
|
72
49
|
if (!comment)
|
|
73
50
|
return 0;
|
|
74
51
|
const newlineCount = (comment.match(/\n/g) || []).length;
|
|
75
|
-
// If there are newlines, it's a multi-line comment (newlineCount + 1 lines)
|
|
76
|
-
// If no newlines, it's a single-line comment (1 line)
|
|
77
52
|
return newlineCount > 0 ? newlineCount + 1 : 1;
|
|
78
53
|
}
|
|
79
54
|
exports.noCommentedCode = (0, eslint_devkit_2.createRule)({
|
|
@@ -151,23 +126,17 @@ exports.noCommentedCode = (0, eslint_devkit_2.createRule)({
|
|
|
151
126
|
return {};
|
|
152
127
|
}
|
|
153
128
|
const sourceCode = context.sourceCode;
|
|
154
|
-
/**
|
|
155
|
-
* Check comment nodes
|
|
156
|
-
*/
|
|
157
129
|
function checkComment(node) {
|
|
158
130
|
try {
|
|
159
131
|
const commentText = node.value || '';
|
|
160
132
|
const isBlockComment = node.type === 'Block';
|
|
161
133
|
const lines = countCommentLines(commentText);
|
|
162
|
-
// Skip single-line comments if configured
|
|
163
134
|
if (ignoreSingleLine && lines === 1 && !isBlockComment) {
|
|
164
135
|
return;
|
|
165
136
|
}
|
|
166
|
-
// Skip if below minimum lines
|
|
167
137
|
if (lines < minLines) {
|
|
168
138
|
return;
|
|
169
139
|
}
|
|
170
|
-
// Check if it looks like code
|
|
171
140
|
if (looksLikeCode(commentText, isBlockComment)) {
|
|
172
141
|
context.report({
|
|
173
142
|
node,
|
|
@@ -180,7 +149,6 @@ exports.noCommentedCode = (0, eslint_devkit_2.createRule)({
|
|
|
180
149
|
messageId: 'removeCode',
|
|
181
150
|
fix: (fixer) => {
|
|
182
151
|
try {
|
|
183
|
-
// Remove the entire comment
|
|
184
152
|
return fixer.remove(node);
|
|
185
153
|
}
|
|
186
154
|
catch {
|
|
@@ -197,44 +165,36 @@ exports.noCommentedCode = (0, eslint_devkit_2.createRule)({
|
|
|
197
165
|
}
|
|
198
166
|
}
|
|
199
167
|
catch {
|
|
200
|
-
// Silently skip if there's an error processing the comment
|
|
201
168
|
return;
|
|
202
169
|
}
|
|
203
170
|
}
|
|
204
171
|
return {
|
|
205
172
|
Program() {
|
|
206
173
|
const comments = sourceCode.getAllComments();
|
|
207
|
-
// Group consecutive comments that look like code
|
|
208
174
|
const groupedComments = [];
|
|
209
175
|
let currentGroup = [];
|
|
210
176
|
for (let i = 0; i < comments.length; i++) {
|
|
211
177
|
const comment = comments[i];
|
|
212
178
|
const commentText = comment.value || '';
|
|
213
179
|
const isBlockComment = comment.type === 'Block';
|
|
214
|
-
// Check if this comment looks like code
|
|
215
180
|
if (looksLikeCode(commentText, isBlockComment)) {
|
|
216
181
|
currentGroup.push(comment);
|
|
217
182
|
}
|
|
218
183
|
else {
|
|
219
|
-
// If current group has comments, add it to grouped comments
|
|
220
184
|
if (currentGroup.length > 0) {
|
|
221
185
|
groupedComments.push([...currentGroup]);
|
|
222
186
|
currentGroup = [];
|
|
223
187
|
}
|
|
224
188
|
}
|
|
225
189
|
}
|
|
226
|
-
// Handle any remaining group
|
|
227
190
|
if (currentGroup.length > 0) {
|
|
228
191
|
groupedComments.push(currentGroup);
|
|
229
192
|
}
|
|
230
|
-
// Process each group
|
|
231
193
|
groupedComments.forEach((group) => {
|
|
232
194
|
if (group.length === 1) {
|
|
233
|
-
// Single comment - use the standard checkComment function
|
|
234
195
|
checkComment(group[0]);
|
|
235
196
|
}
|
|
236
197
|
else {
|
|
237
|
-
// Multiple consecutive comments - report as one error
|
|
238
198
|
const firstComment = group[0];
|
|
239
199
|
const totalLines = group.reduce((sum, comment) => {
|
|
240
200
|
const commentText = comment.value || '';
|
|
@@ -252,7 +212,6 @@ exports.noCommentedCode = (0, eslint_devkit_2.createRule)({
|
|
|
252
212
|
messageId: 'removeCode',
|
|
253
213
|
fix: (fixer) => {
|
|
254
214
|
try {
|
|
255
|
-
// Remove the entire range of consecutive comments
|
|
256
215
|
return fixer.removeRange([
|
|
257
216
|
group[0].range[0],
|
|
258
217
|
group[group.length - 1].range[1],
|
|
@@ -276,4 +235,3 @@ exports.noCommentedCode = (0, eslint_devkit_2.createRule)({
|
|
|
276
235
|
};
|
|
277
236
|
},
|
|
278
237
|
});
|
|
279
|
-
//# sourceMappingURL=no-commented-code.js.map
|
|
@@ -1,9 +1,4 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.noConsoleSpaces = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -32,7 +27,6 @@ exports.noConsoleSpaces = (0, eslint_devkit_1.createRule)({
|
|
|
32
27
|
},
|
|
33
28
|
defaultOptions: [],
|
|
34
29
|
create(context) {
|
|
35
|
-
// Console methods that join parameters with spaces
|
|
36
30
|
const consoleMethods = new Set([
|
|
37
31
|
'log',
|
|
38
32
|
'debug',
|
|
@@ -44,13 +38,10 @@ exports.noConsoleSpaces = (0, eslint_devkit_1.createRule)({
|
|
|
44
38
|
'group',
|
|
45
39
|
'groupCollapsed',
|
|
46
40
|
]);
|
|
47
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
48
41
|
function isInAllowedContext() {
|
|
49
|
-
// For simplicity, we'll skip the allow option for now
|
|
50
42
|
return false;
|
|
51
43
|
}
|
|
52
44
|
function isConsoleMethodCall(node) {
|
|
53
|
-
// Check if this is a call to a console method
|
|
54
45
|
if (node.callee.type === 'MemberExpression' &&
|
|
55
46
|
node.callee.object.type === 'Identifier' &&
|
|
56
47
|
node.callee.object.name === 'console' &&
|
|
@@ -60,31 +51,23 @@ exports.noConsoleSpaces = (0, eslint_devkit_1.createRule)({
|
|
|
60
51
|
}
|
|
61
52
|
return false;
|
|
62
53
|
}
|
|
63
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
64
54
|
function getConsoleMethodName(node) {
|
|
65
|
-
// Safe extraction of console method name
|
|
66
55
|
if (node.callee.type === 'MemberExpression' &&
|
|
67
56
|
node.callee.property.type === 'Identifier') {
|
|
68
57
|
return node.callee.property.name;
|
|
69
58
|
}
|
|
70
59
|
return 'console';
|
|
71
60
|
}
|
|
72
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
73
61
|
function hasLeadingOrTrailingSpaces(text) {
|
|
74
|
-
// Check if string starts or ends with whitespace, but not if it's only whitespace
|
|
75
|
-
// Only flag if there's actual content with leading/trailing spaces
|
|
76
62
|
const trimmed = text.trim();
|
|
77
63
|
return trimmed.length > 0 && /^\s|\s$/.test(text);
|
|
78
64
|
}
|
|
79
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
80
65
|
function hasLeadingOrTrailingSpacesInTemplate(text) {
|
|
81
|
-
// For template literals, even whitespace-only quasis should be flagged
|
|
82
66
|
return /^\s|\s$/.test(text);
|
|
83
67
|
}
|
|
84
68
|
return {
|
|
85
69
|
CallExpression(node) {
|
|
86
70
|
if (isConsoleMethodCall(node) && !isInAllowedContext()) {
|
|
87
|
-
// Check each argument for leading/trailing spaces
|
|
88
71
|
for (const arg of node.arguments) {
|
|
89
72
|
if (arg.type === 'Literal' && typeof arg.value === 'string') {
|
|
90
73
|
if (hasLeadingOrTrailingSpaces(arg.value)) {
|
|
@@ -103,7 +86,6 @@ exports.noConsoleSpaces = (0, eslint_devkit_1.createRule)({
|
|
|
103
86
|
}
|
|
104
87
|
}
|
|
105
88
|
else if (arg.type === 'TemplateLiteral') {
|
|
106
|
-
// Check template literal quasi values for leading/trailing spaces
|
|
107
89
|
let hasSpacesInTemplate = false;
|
|
108
90
|
for (const quasi of arg.quasis) {
|
|
109
91
|
if (hasLeadingOrTrailingSpacesInTemplate(quasi.value.raw)) {
|
|
@@ -119,7 +101,6 @@ exports.noConsoleSpaces = (0, eslint_devkit_1.createRule)({
|
|
|
119
101
|
method: getConsoleMethodName(node),
|
|
120
102
|
arg: 'template literal with spaces',
|
|
121
103
|
},
|
|
122
|
-
// Template literals are harder to fix automatically
|
|
123
104
|
fix: null,
|
|
124
105
|
});
|
|
125
106
|
}
|
|
@@ -130,4 +111,3 @@ exports.noConsoleSpaces = (0, eslint_devkit_1.createRule)({
|
|
|
130
111
|
};
|
|
131
112
|
},
|
|
132
113
|
});
|
|
133
|
-
//# sourceMappingURL=no-console-spaces.js.map
|
|
@@ -1,17 +1,7 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.noJsonSchemaTags = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
|
-
/**
|
|
11
|
-
* JSON Schema keywords that are commonly misused as JSDoc tags.
|
|
12
|
-
* These have precise semantics in JSON Schema but are invalid
|
|
13
|
-
* (or meaningless) in a JSDoc context.
|
|
14
|
-
*/
|
|
15
5
|
const DEFAULT_FORBIDDEN_TAGS = new Set([
|
|
16
6
|
'minimum',
|
|
17
7
|
'maximum',
|
|
@@ -32,13 +22,8 @@ const DEFAULT_FORBIDDEN_TAGS = new Set([
|
|
|
32
22
|
'contentMediaType',
|
|
33
23
|
'contentEncoding',
|
|
34
24
|
]);
|
|
35
|
-
/**
|
|
36
|
-
* Parse a block comment to find `@tagName` occurrences and their positions.
|
|
37
|
-
*/
|
|
38
25
|
function findForbiddenTags(commentText, forbiddenSet) {
|
|
39
26
|
const results = [];
|
|
40
|
-
// Match @tagName patterns. We don't anchor at line start because block
|
|
41
|
-
// comments can have `* @tag …` or simply `@tag …` on any line.
|
|
42
27
|
const tagRegex = /@([a-zA-Z][a-zA-Z0-9]*)\b/g;
|
|
43
28
|
let match;
|
|
44
29
|
while ((match = tagRegex.exec(commentText)) !== null) {
|
|
@@ -49,10 +34,6 @@ function findForbiddenTags(commentText, forbiddenSet) {
|
|
|
49
34
|
}
|
|
50
35
|
return results;
|
|
51
36
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Build a human-readable suggestion for how to express the constraint
|
|
54
|
-
* in description text instead of a JSON Schema tag.
|
|
55
|
-
*/
|
|
56
37
|
function getSuggestionText(tag) {
|
|
57
38
|
const suggestions = {
|
|
58
39
|
minimum: 'Include range in description, e.g. "(min: 1)" or "Range: 1-100"',
|
|
@@ -116,7 +97,6 @@ exports.noJsonSchemaTags = (0, eslint_devkit_1.createRule)({
|
|
|
116
97
|
defaultOptions: [{ additionalForbiddenTags: [] }],
|
|
117
98
|
create(context, [options = {}]) {
|
|
118
99
|
const { additionalForbiddenTags = [] } = options;
|
|
119
|
-
// Build the full set of forbidden tags
|
|
120
100
|
const forbiddenTags = new Set(DEFAULT_FORBIDDEN_TAGS);
|
|
121
101
|
for (const tag of additionalForbiddenTags) {
|
|
122
102
|
forbiddenTags.add(tag);
|
|
@@ -126,20 +106,17 @@ exports.noJsonSchemaTags = (0, eslint_devkit_1.createRule)({
|
|
|
126
106
|
Program() {
|
|
127
107
|
const comments = sourceCode.getAllComments();
|
|
128
108
|
for (const comment of comments) {
|
|
129
|
-
// Only inspect block comments (JSDoc is always /* … */)
|
|
130
109
|
if (comment.type !== 'Block') {
|
|
131
110
|
continue;
|
|
132
111
|
}
|
|
133
112
|
const commentText = comment.value;
|
|
134
113
|
const hits = findForbiddenTags(commentText, forbiddenTags);
|
|
135
114
|
for (const { tag, offset } of hits) {
|
|
136
|
-
// Compute absolute source position.
|
|
137
|
-
// comment.range[0] → opening `/*`, content starts at +2.
|
|
138
115
|
const absoluteStart = comment
|
|
139
116
|
.range[0] +
|
|
140
117
|
2 +
|
|
141
118
|
offset;
|
|
142
|
-
const absoluteEnd = absoluteStart + 1 + tag.length;
|
|
119
|
+
const absoluteEnd = absoluteStart + 1 + tag.length;
|
|
143
120
|
const suggestion = getSuggestionText(tag);
|
|
144
121
|
context.report({
|
|
145
122
|
loc: {
|
|
@@ -156,8 +133,6 @@ exports.noJsonSchemaTags = (0, eslint_devkit_1.createRule)({
|
|
|
156
133
|
messageId: 'moveToDescription',
|
|
157
134
|
data: { tag },
|
|
158
135
|
fix: (fixer) => {
|
|
159
|
-
// Remove the entire @tag and its value (to end of line)
|
|
160
|
-
// Find the line boundaries
|
|
161
136
|
const fullSource = sourceCode.getText();
|
|
162
137
|
let lineStart = absoluteStart;
|
|
163
138
|
while (lineStart > 0 && fullSource[lineStart - 1] !== '\n') {
|
|
@@ -168,7 +143,6 @@ exports.noJsonSchemaTags = (0, eslint_devkit_1.createRule)({
|
|
|
168
143
|
fullSource[lineEnd] !== '\n') {
|
|
169
144
|
lineEnd++;
|
|
170
145
|
}
|
|
171
|
-
// Include the trailing newline if present
|
|
172
146
|
if (lineEnd < fullSource.length && fullSource[lineEnd] === '\n') {
|
|
173
147
|
lineEnd++;
|
|
174
148
|
}
|
|
@@ -183,4 +157,3 @@ exports.noJsonSchemaTags = (0, eslint_devkit_1.createRule)({
|
|
|
183
157
|
};
|
|
184
158
|
},
|
|
185
159
|
});
|
|
186
|
-
//# sourceMappingURL=no-json-schema-tags.js.map
|
|
@@ -1,26 +1,14 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.noMagicNumbers = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
5
|
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
11
|
-
/** Numbers that are universally idiomatic in JS/TS and need no naming. */
|
|
12
6
|
const DEFAULT_IGNORE = new Set([-1, 0, 1, 2]);
|
|
13
|
-
/** Build a SCREAMING_SNAKE_CASE const name from a numeric value. */
|
|
14
7
|
function constNameFor(value) {
|
|
15
|
-
// e.g. 5000 → MAGIC_5000 · -3 → MAGIC_NEG_3 · 1.5 → MAGIC_1_5
|
|
16
8
|
const prefix = value < 0 ? 'MAGIC_NEG_' : 'MAGIC_';
|
|
17
9
|
const digits = String(Math.abs(value)).replace('.', '_');
|
|
18
10
|
return `${prefix}${digits}`;
|
|
19
11
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Walk up the AST to find the nearest statement ancestor that can be used
|
|
22
|
-
* as the insertion point for a const declaration.
|
|
23
|
-
*/
|
|
24
12
|
function nearestStatement(node) {
|
|
25
13
|
const STATEMENT_TYPES = new Set([
|
|
26
14
|
'ExpressionStatement', 'VariableDeclaration', 'ReturnStatement',
|
|
@@ -119,12 +107,10 @@ exports.noMagicNumbers = (0, eslint_devkit_1.createRule)({
|
|
|
119
107
|
BITWISE_OPS.has(parent.operator));
|
|
120
108
|
}
|
|
121
109
|
function isVariableDeclarator(node) {
|
|
122
|
-
// const FOO = 42 — the literal is the named constant itself
|
|
123
110
|
const parent = node.parent;
|
|
124
111
|
return parent?.type === 'VariableDeclarator';
|
|
125
112
|
}
|
|
126
113
|
function isExportedConst(node) {
|
|
127
|
-
// export const FOO = 42
|
|
128
114
|
let current = node;
|
|
129
115
|
while (current) {
|
|
130
116
|
if (current.type === 'ExportNamedDeclaration')
|
|
@@ -139,7 +125,6 @@ exports.noMagicNumbers = (0, eslint_devkit_1.createRule)({
|
|
|
139
125
|
return false;
|
|
140
126
|
}
|
|
141
127
|
function isPropertyKey(node) {
|
|
142
|
-
// { 42: 'value' } — numeric key in object literal
|
|
143
128
|
const parent = node.parent;
|
|
144
129
|
return (parent?.type === 'Property' &&
|
|
145
130
|
parent.key === node);
|
|
@@ -149,15 +134,12 @@ exports.noMagicNumbers = (0, eslint_devkit_1.createRule)({
|
|
|
149
134
|
if (typeof node.value !== 'number')
|
|
150
135
|
return;
|
|
151
136
|
const value = node.value;
|
|
152
|
-
// Skip universally ignored values
|
|
153
137
|
if (isIgnoredNumber(value))
|
|
154
138
|
return;
|
|
155
|
-
// Skip NaN / Infinity (not numeric literals per se)
|
|
156
139
|
if (!isFinite(value))
|
|
157
140
|
return;
|
|
158
|
-
// Context-based skips
|
|
159
141
|
if (isVariableDeclarator(node))
|
|
160
|
-
return;
|
|
142
|
+
return;
|
|
161
143
|
if (isExportedConst(node))
|
|
162
144
|
return;
|
|
163
145
|
if (isArrayIndex(node))
|
|
@@ -184,7 +166,6 @@ exports.noMagicNumbers = (0, eslint_devkit_1.createRule)({
|
|
|
184
166
|
const stmt = nearestStatement(node);
|
|
185
167
|
if (!stmt)
|
|
186
168
|
return null;
|
|
187
|
-
// Determine indentation from the statement's first token.
|
|
188
169
|
const firstToken = sourceCode.getFirstToken(stmt);
|
|
189
170
|
if (!firstToken)
|
|
190
171
|
return null;
|
|
@@ -202,4 +183,3 @@ exports.noMagicNumbers = (0, eslint_devkit_1.createRule)({
|
|
|
202
183
|
};
|
|
203
184
|
},
|
|
204
185
|
});
|
|
205
|
-
//# sourceMappingURL=no-magic-numbers.js.map
|
|
@@ -1,16 +1,8 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.noRawCrossPropertyHref = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
5
|
const eslint_devkit_2 = require("@interlace/eslint-devkit");
|
|
11
|
-
// Fixed list of cross-property hosts. Mirrors UTM_PHILOSOPHY.md's
|
|
12
|
-
// cross-property travel matrix and ANALYTICS_PHILOSOPHY.md principle 1's
|
|
13
|
-
// list of measured surfaces. Edit there and here together.
|
|
14
6
|
const CROSS_PROPERTY_HOSTS = new Set([
|
|
15
7
|
'ofriperetz.dev',
|
|
16
8
|
'interlace.tools',
|
|
@@ -44,7 +36,6 @@ exports.noRawCrossPropertyHref = (0, eslint_devkit_1.createRule)({
|
|
|
44
36
|
defaultOptions: [],
|
|
45
37
|
create(context) {
|
|
46
38
|
function parseHost(value) {
|
|
47
|
-
// Only absolute URLs with a hostname.
|
|
48
39
|
if (!/^https?:\/\//i.test(value))
|
|
49
40
|
return null;
|
|
50
41
|
try {
|
|
@@ -83,4 +74,3 @@ exports.noRawCrossPropertyHref = (0, eslint_devkit_1.createRule)({
|
|
|
83
74
|
};
|
|
84
75
|
},
|
|
85
76
|
});
|
|
86
|
-
//# sourceMappingURL=no-raw-cross-property-href.js.map
|
|
@@ -1,9 +1,4 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.preferCodePoint = void 0;
|
|
9
4
|
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
@@ -36,22 +31,14 @@ exports.preferCodePoint = (0, eslint_devkit_1.createRule)({
|
|
|
36
31
|
},
|
|
37
32
|
defaultOptions: [],
|
|
38
33
|
create(context) {
|
|
39
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars, consistent-function-scoping
|
|
40
34
|
function isInAllowedContext(node) {
|
|
41
|
-
// For simplicity, we'll skip the allow option for now
|
|
42
|
-
// This would require more complex logic to check comments/code context
|
|
43
35
|
return false;
|
|
44
36
|
}
|
|
45
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
46
37
|
function shouldIgnoreCall(node) {
|
|
47
|
-
// The unicorn rule flags ALL charCodeAt calls, but allows some contexts
|
|
48
38
|
if (node.callee.type === 'MemberExpression') {
|
|
49
|
-
// Allow optional chaining
|
|
50
39
|
if (node.callee.optional) {
|
|
51
40
|
return true;
|
|
52
41
|
}
|
|
53
|
-
// Allow computed property access where the property is a variable (not literal)
|
|
54
|
-
// This means obj[method] is ignored, but obj['charCodeAt'] is still flagged
|
|
55
42
|
if (node.callee.computed &&
|
|
56
43
|
node.callee.property.type === 'Identifier') {
|
|
57
44
|
return true;
|
|
@@ -59,16 +46,12 @@ exports.preferCodePoint = (0, eslint_devkit_1.createRule)({
|
|
|
59
46
|
}
|
|
60
47
|
return false;
|
|
61
48
|
}
|
|
62
|
-
// oxlint-disable-next-line consistent-function-scoping
|
|
63
49
|
function isCharCodeAtCall(node) {
|
|
64
|
-
// Check if this is a call to charCodeAt method
|
|
65
50
|
if (node.callee.type === 'MemberExpression') {
|
|
66
|
-
// Direct property access: obj.charCodeAt
|
|
67
51
|
if (node.callee.property.type === 'Identifier' &&
|
|
68
52
|
node.callee.property.name === 'charCodeAt') {
|
|
69
53
|
return true;
|
|
70
54
|
}
|
|
71
|
-
// Computed property access: obj['charCodeAt']
|
|
72
55
|
if (node.callee.computed &&
|
|
73
56
|
node.callee.property.type === 'Literal' &&
|
|
74
57
|
node.callee.property.value === 'charCodeAt') {
|
|
@@ -95,4 +78,3 @@ exports.preferCodePoint = (0, eslint_devkit_1.createRule)({
|
|
|
95
78
|
};
|
|
96
79
|
},
|
|
97
80
|
});
|
|
98
|
-
//# sourceMappingURL=prefer-code-point.js.map
|