@kazupon/eslint-plugin 0.4.0 → 0.5.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/lib/index.js +111 -8
- package/package.json +29 -29
package/lib/index.js
CHANGED
|
@@ -12,7 +12,7 @@ const name = "@kazupon/eslint-plugin";
|
|
|
12
12
|
/**
|
|
13
13
|
* The plugin version.
|
|
14
14
|
*/
|
|
15
|
-
const version = "0.
|
|
15
|
+
const version = "0.5.0";
|
|
16
16
|
/**
|
|
17
17
|
* The namespace for rules
|
|
18
18
|
*/
|
|
@@ -200,7 +200,7 @@ function stripJSDocPrefix(line) {
|
|
|
200
200
|
*/
|
|
201
201
|
function detectTag(text, tags) {
|
|
202
202
|
for (const tag of tags) {
|
|
203
|
-
const tagRegex = new RegExp(`^${tag}\\b`);
|
|
203
|
+
const tagRegex = /* @__PURE__ */ new RegExp(`^${tag}\\b`);
|
|
204
204
|
const match = text.match(tagRegex);
|
|
205
205
|
if (match) {
|
|
206
206
|
const afterTag = text.slice(tag.length);
|
|
@@ -350,6 +350,14 @@ const DEFAULT_TAGS = [
|
|
|
350
350
|
"BUG",
|
|
351
351
|
"NOTE"
|
|
352
352
|
];
|
|
353
|
+
const DEFAULT_DIRECTIVES = [
|
|
354
|
+
"eslint-disable",
|
|
355
|
+
"eslint-disable-next-line",
|
|
356
|
+
"eslint-disable-line",
|
|
357
|
+
"@ts-expect-error",
|
|
358
|
+
"@ts-ignore",
|
|
359
|
+
"@ts-nocheck"
|
|
360
|
+
];
|
|
353
361
|
const rule = createRule({
|
|
354
362
|
name: "prefer-scope-on-tag-comment",
|
|
355
363
|
meta: {
|
|
@@ -363,18 +371,27 @@ const rule = createRule({
|
|
|
363
371
|
messages: { missingScope: "Tag comment '{{tag}}' is missing a scope. Use format: {{tag}}(scope)" },
|
|
364
372
|
schema: [{
|
|
365
373
|
type: "object",
|
|
366
|
-
properties: {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
374
|
+
properties: {
|
|
375
|
+
tags: {
|
|
376
|
+
type: "array",
|
|
377
|
+
items: { type: "string" },
|
|
378
|
+
minItems: 1,
|
|
379
|
+
uniqueItems: true
|
|
380
|
+
},
|
|
381
|
+
directives: {
|
|
382
|
+
type: "array",
|
|
383
|
+
items: { type: "string" },
|
|
384
|
+
minItems: 1,
|
|
385
|
+
uniqueItems: true
|
|
386
|
+
}
|
|
387
|
+
},
|
|
372
388
|
additionalProperties: false
|
|
373
389
|
}]
|
|
374
390
|
},
|
|
375
391
|
create(ctx) {
|
|
376
392
|
const options = ctx.options[0] || { tags: DEFAULT_TAGS };
|
|
377
393
|
const tags = options.tags || DEFAULT_TAGS;
|
|
394
|
+
const directives = options.directives || DEFAULT_DIRECTIVES;
|
|
378
395
|
const sourceCode = ctx.sourceCode;
|
|
379
396
|
/**
|
|
380
397
|
* Report a missing scope violation
|
|
@@ -409,11 +426,59 @@ const rule = createRule({
|
|
|
409
426
|
});
|
|
410
427
|
}
|
|
411
428
|
/**
|
|
429
|
+
* Check if comment starts with a directive and extract the description
|
|
430
|
+
* @returns Object with directive match info or null
|
|
431
|
+
*/
|
|
432
|
+
function parseDirectiveComment(text) {
|
|
433
|
+
const trimmedText = text.trim();
|
|
434
|
+
for (const directive of directives) if (trimmedText.startsWith(directive)) {
|
|
435
|
+
const afterDirective = trimmedText.slice(directive.length);
|
|
436
|
+
const separatorIndex = afterDirective.indexOf("--");
|
|
437
|
+
if (separatorIndex !== -1) {
|
|
438
|
+
const description = afterDirective.slice(separatorIndex + 2).trim();
|
|
439
|
+
const separatorPos = text.indexOf("--");
|
|
440
|
+
const afterSeparator = text.slice(separatorPos + 2);
|
|
441
|
+
const descriptionStart = separatorPos + 2 + (afterSeparator.length - afterSeparator.trimStart().length);
|
|
442
|
+
return {
|
|
443
|
+
directive,
|
|
444
|
+
description,
|
|
445
|
+
descriptionStart
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
if (afterDirective.trim()) {
|
|
449
|
+
const spaceMatch = afterDirective.match(/^\s+/);
|
|
450
|
+
if (spaceMatch) {
|
|
451
|
+
const description = afterDirective.trim();
|
|
452
|
+
const directiveIndex = text.indexOf(directive);
|
|
453
|
+
const descriptionStart = directiveIndex + directive.length + spaceMatch[0].length;
|
|
454
|
+
return {
|
|
455
|
+
directive,
|
|
456
|
+
description,
|
|
457
|
+
descriptionStart
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
412
465
|
* Check a comment for missing scope
|
|
413
466
|
*/
|
|
414
467
|
function checkComment(comment) {
|
|
415
468
|
const { value, type } = comment;
|
|
416
469
|
if (type === "Line") {
|
|
470
|
+
const directiveInfo$1 = parseDirectiveComment(value);
|
|
471
|
+
if (directiveInfo$1) {
|
|
472
|
+
const tagInfo$1 = detectTag(directiveInfo$1.description, tags);
|
|
473
|
+
if (tagInfo$1 && !tagInfo$1.hasScope) {
|
|
474
|
+
const tagIndex = directiveInfo$1.description.indexOf(tagInfo$1.tag);
|
|
475
|
+
if (tagIndex !== -1) reportMissingScope(comment, tagInfo$1.tag, {
|
|
476
|
+
line: comment.loc.start.line,
|
|
477
|
+
column: comment.loc.start.column + 2 + directiveInfo$1.descriptionStart + tagIndex
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
417
482
|
const tagInfo = detectTag(value.trim(), tags);
|
|
418
483
|
if (tagInfo && !tagInfo.hasScope) {
|
|
419
484
|
const tagIndex = value.indexOf(tagInfo.tag);
|
|
@@ -425,6 +490,44 @@ const rule = createRule({
|
|
|
425
490
|
return;
|
|
426
491
|
}
|
|
427
492
|
const lines = value.split("\n");
|
|
493
|
+
const directiveInfo = parseDirectiveComment(value);
|
|
494
|
+
if (directiveInfo) {
|
|
495
|
+
const tagInfo = detectTag(directiveInfo.description, tags);
|
|
496
|
+
if (tagInfo && !tagInfo.hasScope) if (lines.length === 1) {
|
|
497
|
+
const tagIndexInDesc = directiveInfo.description.indexOf(tagInfo.tag);
|
|
498
|
+
if (tagIndexInDesc !== -1) {
|
|
499
|
+
const tagIndexInValue = directiveInfo.descriptionStart + tagIndexInDesc;
|
|
500
|
+
const location = {
|
|
501
|
+
line: comment.loc.start.line,
|
|
502
|
+
column: comment.loc.start.column + 2 + tagIndexInValue
|
|
503
|
+
};
|
|
504
|
+
reportMissingScope(comment, tagInfo.tag, location);
|
|
505
|
+
}
|
|
506
|
+
} else {
|
|
507
|
+
const descLines = directiveInfo.description.split("\n");
|
|
508
|
+
for (const [descLineIndex, descLine] of descLines.entries()) {
|
|
509
|
+
const lineTagInfo = detectTag(descLine.trim(), tags);
|
|
510
|
+
if (lineTagInfo && !lineTagInfo.hasScope) {
|
|
511
|
+
const descStartInComment = value.indexOf(directiveInfo.description);
|
|
512
|
+
const linesBeforeDesc = value.slice(0, descStartInComment).split("\n").length - 1;
|
|
513
|
+
const actualLineIndex = linesBeforeDesc + descLineIndex;
|
|
514
|
+
if (actualLineIndex < lines.length) {
|
|
515
|
+
const actualLine = lines[actualLineIndex];
|
|
516
|
+
const tagIndex = actualLine.indexOf(lineTagInfo.tag);
|
|
517
|
+
if (tagIndex !== -1) {
|
|
518
|
+
const location = {
|
|
519
|
+
line: comment.loc.start.line + actualLineIndex,
|
|
520
|
+
column: actualLineIndex === 0 ? comment.loc.start.column + 2 + tagIndex : tagIndex
|
|
521
|
+
};
|
|
522
|
+
reportMissingScope(comment, lineTagInfo.tag, location);
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
428
531
|
for (const [i, line] of lines.entries()) {
|
|
429
532
|
const trimmedLine = line.trim();
|
|
430
533
|
if (!trimmedLine) continue;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kazupon/eslint-plugin",
|
|
3
3
|
"description": "ESLint plugin for @kazupon",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"funding": "https://github.com/sponsors/kazupon",
|
|
7
7
|
"bugs": {
|
|
@@ -47,48 +47,48 @@
|
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@es-joy/jsdoccomment": "^0.
|
|
51
|
-
"@eslint/core": "^0.
|
|
50
|
+
"@es-joy/jsdoccomment": "^0.52.0",
|
|
51
|
+
"@eslint/core": "^0.15.1"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"eslint": "^9.0.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@eslint/compat": "^1.
|
|
58
|
-
"@eslint/markdown": "^
|
|
59
|
-
"@kazupon/eslint-config": "^0.
|
|
57
|
+
"@eslint/compat": "^1.3.1",
|
|
58
|
+
"@eslint/markdown": "^7.0.0",
|
|
59
|
+
"@kazupon/eslint-config": "^0.32.0",
|
|
60
60
|
"@kazupon/prettier-config": "^0.1.1",
|
|
61
|
-
"@shikijs/vitepress-twoslash": "^3.
|
|
62
|
-
"@types/node": "^22.
|
|
63
|
-
"@vitest/eslint-plugin": "^1.
|
|
64
|
-
"bumpp": "^10.
|
|
65
|
-
"eslint": "^9.
|
|
66
|
-
"eslint-config-prettier": "^10.1.
|
|
67
|
-
"eslint-import-resolver-typescript": "^4.4.
|
|
68
|
-
"eslint-plugin-import": "^2.
|
|
61
|
+
"@shikijs/vitepress-twoslash": "^3.8.1",
|
|
62
|
+
"@types/node": "^22.16.5",
|
|
63
|
+
"@vitest/eslint-plugin": "^1.3.4",
|
|
64
|
+
"bumpp": "^10.2.0",
|
|
65
|
+
"eslint": "^9.32.0",
|
|
66
|
+
"eslint-config-prettier": "^10.1.8",
|
|
67
|
+
"eslint-import-resolver-typescript": "^4.4.4",
|
|
68
|
+
"eslint-plugin-import": "^2.32.0",
|
|
69
69
|
"eslint-plugin-jsonc": "^2.20.1",
|
|
70
70
|
"eslint-plugin-module-interop": "^0.3.1",
|
|
71
71
|
"eslint-plugin-promise": "^7.2.1",
|
|
72
|
-
"eslint-plugin-regexp": "^2.
|
|
73
|
-
"eslint-plugin-unicorn": "^
|
|
72
|
+
"eslint-plugin-regexp": "^2.9.0",
|
|
73
|
+
"eslint-plugin-unicorn": "^60.0.0",
|
|
74
74
|
"eslint-plugin-unused-imports": "^4.1.4",
|
|
75
75
|
"eslint-plugin-yml": "^1.18.0",
|
|
76
76
|
"eslint-vitest-rule-tester": "^2.2.0",
|
|
77
77
|
"gh-changelogen": "^0.2.8",
|
|
78
|
-
"knip": "^5.
|
|
79
|
-
"lint-staged": "^16.
|
|
80
|
-
"pkg-pr-new": "^0.0.
|
|
81
|
-
"prettier": "^3.
|
|
78
|
+
"knip": "^5.62.0",
|
|
79
|
+
"lint-staged": "^16.1.2",
|
|
80
|
+
"pkg-pr-new": "^0.0.54",
|
|
81
|
+
"prettier": "^3.6.2",
|
|
82
82
|
"publint": "^0.3.12",
|
|
83
|
-
"tsdown": "^0.
|
|
84
|
-
"tsx": "^4.
|
|
85
|
-
"twoslash-eslint": "^0.3.
|
|
83
|
+
"tsdown": "^0.13.0",
|
|
84
|
+
"tsx": "^4.20.3",
|
|
85
|
+
"twoslash-eslint": "^0.3.3",
|
|
86
86
|
"typescript": "^5.8.3",
|
|
87
|
-
"typescript-eslint": "^8.
|
|
88
|
-
"vite-plugin-eslint4b": "^0.
|
|
87
|
+
"typescript-eslint": "^8.38.0",
|
|
88
|
+
"vite-plugin-eslint4b": "^0.6.0",
|
|
89
89
|
"vitepress": "^1.6.3",
|
|
90
|
-
"vitepress-plugin-group-icons": "^1.6.
|
|
91
|
-
"vitest": "^3.2.
|
|
90
|
+
"vitepress-plugin-group-icons": "^1.6.1",
|
|
91
|
+
"vitest": "^3.2.4"
|
|
92
92
|
},
|
|
93
93
|
"prettier": "@kazupon/prettier-config",
|
|
94
94
|
"lint-staged": {
|
|
@@ -118,11 +118,11 @@
|
|
|
118
118
|
"fix": "pnpm run --stream --color \"/^fix:/\"",
|
|
119
119
|
"fix:eslint": "eslint . --fix",
|
|
120
120
|
"fix:knip": "knip --fix --no-exit-code",
|
|
121
|
-
"fix:prettier": "prettier . --write",
|
|
121
|
+
"fix:prettier": "prettier . --write --experimental-cli",
|
|
122
122
|
"lint": "pnpm run --stream --color \"/^lint:/\"",
|
|
123
123
|
"lint:eslint": "eslint .",
|
|
124
124
|
"lint:knip": "knip",
|
|
125
|
-
"lint:prettier": "prettier . --check",
|
|
125
|
+
"lint:prettier": "prettier . --check --experimental-cli",
|
|
126
126
|
"release": "bumpp --commit \"release: v%s\" --all --push --tag",
|
|
127
127
|
"test": "vitest run",
|
|
128
128
|
"typecheck": "pnpm run --stream --color \"/^typecheck:/\"",
|