@yuu1111/comment-check 2.0.0 → 2.2.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.ja.md +15 -6
- package/README.md +17 -10
- package/dist/cli.js +83 -13
- package/dist/scan.js +70 -9
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -24,26 +24,35 @@ src/queue.ts:18:2 undocumented-directive TypeScript directive needs a descriptio
|
|
|
24
24
|
Checked 42 files: 1 new, 0 resolved, 3 baselined
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
日本語の文末の `。` も止めるProjectはopt-inのruleを指定する
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
comment-check --enable japanese-period .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
複数行のcommentを直前の行へ続けず空行で区切るProjectは、もう一つのopt-inのruleを指定する
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
comment-check --enable cramped-comment .
|
|
37
|
+
```
|
|
38
|
+
|
|
27
39
|
## Rules
|
|
28
40
|
|
|
29
41
|
| Rule | 検出対象 |
|
|
30
42
|
|------|---------|
|
|
31
43
|
| `broad-suppression` | `biome-ignore-all`、`@ts-nocheck`、ruleを書いていない `eslint-disable` |
|
|
44
|
+
| `cramped-comment` | 直前の行へ空行なしで続く複数行comment(opt-in) |
|
|
32
45
|
| `undocumented-directive` | 説明の無い `@ts-ignore` と `@ts-expect-error` |
|
|
33
46
|
| `placeholder-comment` | `TODO`、`FIXME`、`XXX`、`HACK` |
|
|
34
47
|
| `separator-comment` | 記号だけで作った装飾comment |
|
|
48
|
+
| `japanese-period` | 日本語の文を終える `。` を含むcomment(opt-in) |
|
|
35
49
|
|
|
36
50
|
## Options
|
|
37
51
|
|
|
38
52
|
| Option | Description |
|
|
39
53
|
|--------|-------------|
|
|
40
54
|
| `--baseline <path>` | 読み書きするbaseline file(既定は `comment-baseline.json`) |
|
|
55
|
+
| `--enable <rule>` | opt-in ruleを実行する 複数指定できる 未知の名前は設定error |
|
|
41
56
|
| `--ignore <path>` | 検査から外すpath 複数指定できる |
|
|
42
57
|
| `--update-baseline` | baselineを現在の検出で置き換える |
|
|
43
58
|
| `--json` | 新規と解消済みの検出をJSONで出力する |
|
|
44
|
-
|
|
45
|
-
## Notes
|
|
46
|
-
|
|
47
|
-
生成物のディレクトリなどProjectが持つpathは `--ignore` で検査から外す 例えば `--ignore src/generated`
|
|
48
|
-
|
|
49
|
-
baselineはrule、file、comment本文をキーにするため、行が動いてもそのcommentを新規とは報告しない Biomeは `biome-ignore` の理由と未使用の抑制を既に扱うため、このcheckerはBiomeが読まないcommentの細部だけを担当する
|
package/README.md
CHANGED
|
@@ -25,30 +25,37 @@ src/queue.ts:18:2 undocumented-directive TypeScript directive needs a descriptio
|
|
|
25
25
|
Checked 42 files: 1 new, 0 resolved, 3 baselined
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
A project that also keeps Japanese sentences free of a trailing `。` names the
|
|
29
|
+
opt-in rule:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
comment-check --enable japanese-period .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
A project that keeps a blank line before every multi-line comment names the
|
|
36
|
+
other opt-in rule:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
comment-check --enable cramped-comment .
|
|
40
|
+
```
|
|
41
|
+
|
|
28
42
|
## Rules
|
|
29
43
|
|
|
30
44
|
| Rule | Detects |
|
|
31
45
|
|------|---------|
|
|
32
46
|
| `broad-suppression` | `biome-ignore-all`, `@ts-nocheck`, and rule-less `eslint-disable` |
|
|
47
|
+
| `cramped-comment` | a multi-line block comment written directly under the previous line (opt-in) |
|
|
33
48
|
| `undocumented-directive` | `@ts-ignore` or `@ts-expect-error` without a description |
|
|
34
49
|
| `placeholder-comment` | `TODO`, `FIXME`, `XXX`, `HACK` |
|
|
35
50
|
| `separator-comment` | decorative comments made only of punctuation |
|
|
51
|
+
| `japanese-period` | a Japanese sentence in a comment that ends with `。` (opt-in) |
|
|
36
52
|
|
|
37
53
|
## Options
|
|
38
54
|
|
|
39
55
|
| Option | Description |
|
|
40
56
|
|--------|-------------|
|
|
41
57
|
| `--baseline <path>` | Baseline file to read or write (default `comment-baseline.json`) |
|
|
58
|
+
| `--enable <rule>` | Run an opt-in rule, repeatable; an unknown name is a configuration error |
|
|
42
59
|
| `--ignore <path>` | Path to leave out, repeatable |
|
|
43
60
|
| `--update-baseline` | Replace the baseline with the current findings |
|
|
44
61
|
| `--json` | Print new and resolved findings as JSON |
|
|
45
|
-
|
|
46
|
-
## Notes
|
|
47
|
-
|
|
48
|
-
Generated directories and other paths that the project owns stay out of the
|
|
49
|
-
check through `--ignore`, for example `--ignore src/generated`.
|
|
50
|
-
|
|
51
|
-
The baseline keys on the rule, the file, and the comment text, so moving a line
|
|
52
|
-
does not report the comment as new. Biome already requires a reason on
|
|
53
|
-
`biome-ignore` and reports unused suppressions, so this checker only covers the
|
|
54
|
-
comment trivia that Biome does not read.
|
package/dist/cli.js
CHANGED
|
@@ -286,9 +286,44 @@ function extractComments(source) {
|
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
// src/rules.ts
|
|
289
|
+
var OPT_IN_RULE_IDS = ["cramped-comment", "japanese-period"];
|
|
289
290
|
var PLACEHOLDER_PATTERN = /\b(TODO|FIXME|XXX|HACK)\b/;
|
|
290
291
|
var SEPARATOR_PATTERN = /^[-=*_#~+./\\|]{4,}$/;
|
|
291
292
|
var DIRECTIVE_PATTERN = /^@ts-(?:ignore|expect-error)\b([\s\S]*)$/;
|
|
293
|
+
var JAPANESE_PERIOD = "\u3002";
|
|
294
|
+
var BLOCK_OPENER = /(?:=>|[{(,[])\s*$/;
|
|
295
|
+
var COMMENT_CONTINUATION = /^(?:\/\/|\*|\/\*)/;
|
|
296
|
+
function parseEnabledRules(values) {
|
|
297
|
+
const enabled = [];
|
|
298
|
+
for (const value of values) {
|
|
299
|
+
if (!OPT_IN_RULE_IDS.includes(value)) {
|
|
300
|
+
throw new Error(`unknown rule: ${value}`);
|
|
301
|
+
}
|
|
302
|
+
const rule = value;
|
|
303
|
+
if (!enabled.includes(rule)) {
|
|
304
|
+
enabled.push(rule);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return enabled;
|
|
308
|
+
}
|
|
309
|
+
function findJapanesePeriod(text) {
|
|
310
|
+
return text.indexOf(JAPANESE_PERIOD);
|
|
311
|
+
}
|
|
312
|
+
function isCrampedComment(source, start) {
|
|
313
|
+
const lineStart = source.lastIndexOf(`
|
|
314
|
+
`, start - 1) + 1;
|
|
315
|
+
if (lineStart === 0 || source.slice(lineStart, start).trim() !== "") {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
const previousEnd = lineStart - 1;
|
|
319
|
+
const previousStart = source.lastIndexOf(`
|
|
320
|
+
`, previousEnd - 1) + 1;
|
|
321
|
+
const previous = source.slice(previousStart, previousEnd).trim();
|
|
322
|
+
if (previous === "" || previous.endsWith("*/")) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
return !(COMMENT_CONTINUATION.test(previous) || BLOCK_OPENER.test(previous));
|
|
326
|
+
}
|
|
292
327
|
function normalizeComment(body) {
|
|
293
328
|
return body.split(`
|
|
294
329
|
`).map((line) => line.replace(/^\s*\*+\s?/, "")).join(" ").replace(/\s+/g, " ").trim();
|
|
@@ -337,31 +372,57 @@ function positionAt(source, offset) {
|
|
|
337
372
|
}
|
|
338
373
|
return { line, column: offset - lineStart + 1 };
|
|
339
374
|
}
|
|
340
|
-
function scanSource(source, file) {
|
|
375
|
+
function scanSource(source, file, enabled = []) {
|
|
341
376
|
const findings = [];
|
|
342
377
|
for (const comment of extractComments(source)) {
|
|
378
|
+
const text = normalizeComment(comment.text);
|
|
343
379
|
const rule = classifyComment(comment.text);
|
|
344
|
-
if (rule
|
|
380
|
+
if (rule !== null) {
|
|
381
|
+
const position2 = positionAt(source, comment.start);
|
|
382
|
+
findings.push({
|
|
383
|
+
column: position2.column,
|
|
384
|
+
file,
|
|
385
|
+
line: position2.line,
|
|
386
|
+
rule,
|
|
387
|
+
text
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
if (enabled.includes("cramped-comment") && comment.kind === "block" && source.slice(comment.start, comment.end).includes(`
|
|
391
|
+
`) && isCrampedComment(source, comment.start)) {
|
|
392
|
+
const position2 = positionAt(source, comment.start);
|
|
393
|
+
findings.push({
|
|
394
|
+
column: position2.column,
|
|
395
|
+
file,
|
|
396
|
+
line: position2.line,
|
|
397
|
+
rule: "cramped-comment",
|
|
398
|
+
text
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
if (!enabled.includes("japanese-period")) {
|
|
345
402
|
continue;
|
|
346
403
|
}
|
|
347
|
-
const
|
|
404
|
+
const offset = findJapanesePeriod(comment.text);
|
|
405
|
+
if (offset < 0) {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
const position = positionAt(source, comment.start + 2 + offset);
|
|
348
409
|
findings.push({
|
|
349
410
|
column: position.column,
|
|
350
411
|
file,
|
|
351
412
|
line: position.line,
|
|
352
|
-
rule,
|
|
353
|
-
text
|
|
413
|
+
rule: "japanese-period",
|
|
414
|
+
text
|
|
354
415
|
});
|
|
355
416
|
}
|
|
356
417
|
return findings;
|
|
357
418
|
}
|
|
358
|
-
function scanFile(file, cwd = process.cwd()) {
|
|
359
|
-
return scanSource(readFileSync(file, "utf8"), normalizePath(relative2(cwd, file)));
|
|
419
|
+
function scanFile(file, cwd = process.cwd(), enabled = []) {
|
|
420
|
+
return scanSource(readFileSync(file, "utf8"), normalizePath(relative2(cwd, file)), enabled);
|
|
360
421
|
}
|
|
361
|
-
function scanFiles(files, cwd = process.cwd()) {
|
|
422
|
+
function scanFiles(files, cwd = process.cwd(), enabled = []) {
|
|
362
423
|
const findings = [];
|
|
363
424
|
for (const file of files) {
|
|
364
|
-
findings.push(...scanFile(file, cwd));
|
|
425
|
+
findings.push(...scanFile(file, cwd, enabled));
|
|
365
426
|
}
|
|
366
427
|
return findings.sort(compareFindings);
|
|
367
428
|
}
|
|
@@ -531,18 +592,21 @@ function runCli(main) {
|
|
|
531
592
|
var DEFAULT_BASELINE = "comment-baseline.json";
|
|
532
593
|
var RULE_MESSAGES = {
|
|
533
594
|
"broad-suppression": "file-wide suppression hides too much",
|
|
595
|
+
"cramped-comment": "a multi-line comment needs a blank line before it",
|
|
596
|
+
"japanese-period": "a Japanese sentence in a comment does not end with a period",
|
|
534
597
|
"placeholder-comment": "placeholder comment should be resolved or tracked",
|
|
535
598
|
"separator-comment": "decorative separator comment adds no information",
|
|
536
599
|
"undocumented-directive": "TypeScript directive needs a description"
|
|
537
600
|
};
|
|
538
|
-
var USAGE = "Usage: comment-check [--baseline <path>] [--ignore <path>] [--update-baseline] [--json] [path...]";
|
|
601
|
+
var USAGE = "Usage: comment-check [--baseline <path>] [--enable <rule>] [--ignore <path>] [--update-baseline] [--json] [path...]";
|
|
539
602
|
function parseArguments(argv) {
|
|
540
603
|
const parsed = parseArgv(argv, {
|
|
541
604
|
flags: ["json", "update-baseline"],
|
|
542
|
-
values: ["baseline", "ignore"]
|
|
605
|
+
values: ["baseline", "enable", "ignore"]
|
|
543
606
|
});
|
|
544
607
|
return {
|
|
545
608
|
baselinePath: parsed.values.get("baseline")?.at(-1) ?? DEFAULT_BASELINE,
|
|
609
|
+
enabled: parseEnabledRules(parsed.values.get("enable") ?? []),
|
|
546
610
|
ignores: (parsed.values.get("ignore") ?? []).map(normalizePath),
|
|
547
611
|
json: parsed.flags.has("json"),
|
|
548
612
|
targets: parsed.targets.length > 0 ? parsed.targets : ["."],
|
|
@@ -564,7 +628,7 @@ function main(argv) {
|
|
|
564
628
|
extensions: SUPPORTED_EXTENSIONS,
|
|
565
629
|
ignores: options.ignores
|
|
566
630
|
});
|
|
567
|
-
const findings = scanFiles(files);
|
|
631
|
+
const findings = scanFiles(files, process.cwd(), options.enabled);
|
|
568
632
|
if (options.update) {
|
|
569
633
|
const baseline2 = createBaseline(findings);
|
|
570
634
|
writeBaseline(options.baselinePath, baseline2);
|
|
@@ -583,4 +647,10 @@ function main(argv) {
|
|
|
583
647
|
}
|
|
584
648
|
return comparison.added.length > 0 ? 1 : 0;
|
|
585
649
|
}
|
|
586
|
-
|
|
650
|
+
if (import.meta.main) {
|
|
651
|
+
runCli(main);
|
|
652
|
+
}
|
|
653
|
+
export {
|
|
654
|
+
parseArguments,
|
|
655
|
+
main
|
|
656
|
+
};
|
package/dist/scan.js
CHANGED
|
@@ -284,9 +284,44 @@ function extractComments(source) {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
// src/rules.ts
|
|
287
|
+
var OPT_IN_RULE_IDS = ["cramped-comment", "japanese-period"];
|
|
287
288
|
var PLACEHOLDER_PATTERN = /\b(TODO|FIXME|XXX|HACK)\b/;
|
|
288
289
|
var SEPARATOR_PATTERN = /^[-=*_#~+./\\|]{4,}$/;
|
|
289
290
|
var DIRECTIVE_PATTERN = /^@ts-(?:ignore|expect-error)\b([\s\S]*)$/;
|
|
291
|
+
var JAPANESE_PERIOD = "\u3002";
|
|
292
|
+
var BLOCK_OPENER = /(?:=>|[{(,[])\s*$/;
|
|
293
|
+
var COMMENT_CONTINUATION = /^(?:\/\/|\*|\/\*)/;
|
|
294
|
+
function parseEnabledRules(values) {
|
|
295
|
+
const enabled = [];
|
|
296
|
+
for (const value of values) {
|
|
297
|
+
if (!OPT_IN_RULE_IDS.includes(value)) {
|
|
298
|
+
throw new Error(`unknown rule: ${value}`);
|
|
299
|
+
}
|
|
300
|
+
const rule = value;
|
|
301
|
+
if (!enabled.includes(rule)) {
|
|
302
|
+
enabled.push(rule);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return enabled;
|
|
306
|
+
}
|
|
307
|
+
function findJapanesePeriod(text) {
|
|
308
|
+
return text.indexOf(JAPANESE_PERIOD);
|
|
309
|
+
}
|
|
310
|
+
function isCrampedComment(source, start) {
|
|
311
|
+
const lineStart = source.lastIndexOf(`
|
|
312
|
+
`, start - 1) + 1;
|
|
313
|
+
if (lineStart === 0 || source.slice(lineStart, start).trim() !== "") {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
const previousEnd = lineStart - 1;
|
|
317
|
+
const previousStart = source.lastIndexOf(`
|
|
318
|
+
`, previousEnd - 1) + 1;
|
|
319
|
+
const previous = source.slice(previousStart, previousEnd).trim();
|
|
320
|
+
if (previous === "" || previous.endsWith("*/")) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
return !(COMMENT_CONTINUATION.test(previous) || BLOCK_OPENER.test(previous));
|
|
324
|
+
}
|
|
290
325
|
function normalizeComment(body) {
|
|
291
326
|
return body.split(`
|
|
292
327
|
`).map((line) => line.replace(/^\s*\*+\s?/, "")).join(" ").replace(/\s+/g, " ").trim();
|
|
@@ -335,31 +370,57 @@ function positionAt(source, offset) {
|
|
|
335
370
|
}
|
|
336
371
|
return { line, column: offset - lineStart + 1 };
|
|
337
372
|
}
|
|
338
|
-
function scanSource(source, file) {
|
|
373
|
+
function scanSource(source, file, enabled = []) {
|
|
339
374
|
const findings = [];
|
|
340
375
|
for (const comment of extractComments(source)) {
|
|
376
|
+
const text = normalizeComment(comment.text);
|
|
341
377
|
const rule = classifyComment(comment.text);
|
|
342
|
-
if (rule
|
|
378
|
+
if (rule !== null) {
|
|
379
|
+
const position2 = positionAt(source, comment.start);
|
|
380
|
+
findings.push({
|
|
381
|
+
column: position2.column,
|
|
382
|
+
file,
|
|
383
|
+
line: position2.line,
|
|
384
|
+
rule,
|
|
385
|
+
text
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
if (enabled.includes("cramped-comment") && comment.kind === "block" && source.slice(comment.start, comment.end).includes(`
|
|
389
|
+
`) && isCrampedComment(source, comment.start)) {
|
|
390
|
+
const position2 = positionAt(source, comment.start);
|
|
391
|
+
findings.push({
|
|
392
|
+
column: position2.column,
|
|
393
|
+
file,
|
|
394
|
+
line: position2.line,
|
|
395
|
+
rule: "cramped-comment",
|
|
396
|
+
text
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
if (!enabled.includes("japanese-period")) {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
const offset = findJapanesePeriod(comment.text);
|
|
403
|
+
if (offset < 0) {
|
|
343
404
|
continue;
|
|
344
405
|
}
|
|
345
|
-
const position = positionAt(source, comment.start);
|
|
406
|
+
const position = positionAt(source, comment.start + 2 + offset);
|
|
346
407
|
findings.push({
|
|
347
408
|
column: position.column,
|
|
348
409
|
file,
|
|
349
410
|
line: position.line,
|
|
350
|
-
rule,
|
|
351
|
-
text
|
|
411
|
+
rule: "japanese-period",
|
|
412
|
+
text
|
|
352
413
|
});
|
|
353
414
|
}
|
|
354
415
|
return findings;
|
|
355
416
|
}
|
|
356
|
-
function scanFile(file, cwd = process.cwd()) {
|
|
357
|
-
return scanSource(readFileSync(file, "utf8"), normalizePath(relative2(cwd, file)));
|
|
417
|
+
function scanFile(file, cwd = process.cwd(), enabled = []) {
|
|
418
|
+
return scanSource(readFileSync(file, "utf8"), normalizePath(relative2(cwd, file)), enabled);
|
|
358
419
|
}
|
|
359
|
-
function scanFiles(files, cwd = process.cwd()) {
|
|
420
|
+
function scanFiles(files, cwd = process.cwd(), enabled = []) {
|
|
360
421
|
const findings = [];
|
|
361
422
|
for (const file of files) {
|
|
362
|
-
findings.push(...scanFile(file, cwd));
|
|
423
|
+
findings.push(...scanFile(file, cwd, enabled));
|
|
363
424
|
}
|
|
364
425
|
return findings.sort(compareFindings);
|
|
365
426
|
}
|