chaffjs 0.1.0 → 0.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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +13 -63
- package/dist/cli.js.map +1 -1
- package/dist/commands/eval.d.ts +15 -0
- package/dist/commands/eval.d.ts.map +1 -0
- package/dist/commands/eval.js +44 -0
- package/dist/commands/eval.js.map +1 -0
- package/dist/commands/test.d.ts +21 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +70 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/config/by-path.d.ts +15 -0
- package/dist/config/by-path.d.ts.map +1 -0
- package/dist/config/by-path.js +41 -0
- package/dist/config/by-path.js.map +1 -0
- package/dist/config/load.d.ts +4 -0
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +21 -0
- package/dist/config/load.js.map +1 -1
- package/dist/detectors/count-per-section.d.ts +8 -1
- package/dist/detectors/count-per-section.d.ts.map +1 -1
- package/dist/detectors/count-per-section.js +26 -5
- package/dist/detectors/count-per-section.js.map +1 -1
- package/dist/detectors/sentence-length.d.ts.map +1 -1
- package/dist/detectors/sentence-length.js +3 -9
- package/dist/detectors/sentence-length.js.map +1 -1
- package/dist/detectors/sentence-rhythm.d.ts.map +1 -1
- package/dist/detectors/sentence-rhythm.js +3 -2
- package/dist/detectors/sentence-rhythm.js.map +1 -1
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +21 -6
- package/dist/document.js.map +1 -1
- package/dist/eval.d.ts +31 -0
- package/dist/eval.d.ts.map +1 -0
- package/dist/eval.js +62 -0
- package/dist/eval.js.map +1 -0
- package/dist/genre.d.ts.map +1 -1
- package/dist/genre.js +8 -1
- package/dist/genre.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +2 -0
- package/dist/init.js.map +1 -1
- package/dist/measure.d.ts +15 -0
- package/dist/measure.d.ts.map +1 -0
- package/dist/measure.js +16 -0
- package/dist/measure.js.map +1 -0
- package/dist/render/eval.d.ts +7 -0
- package/dist/render/eval.d.ts.map +1 -0
- package/dist/render/eval.js +59 -0
- package/dist/render/eval.js.map +1 -0
- package/package.json +3 -3
- package/rules/bold-density.yaml +8 -6
- package/rules/empty-intensifier.yaml +1 -1
- package/rules/heading-echo.yaml +1 -1
- package/rules/max-sentence-length.yaml +1 -1
- package/rules/repeated-sentence-head.yaml +1 -1
- package/src/cli.ts +11 -74
- package/src/commands/eval.ts +64 -0
- package/src/commands/test.ts +89 -0
- package/src/config/by-path.ts +47 -0
- package/src/config/load.ts +25 -0
- package/src/detectors/count-per-section.ts +30 -6
- package/src/detectors/sentence-length.ts +4 -10
- package/src/detectors/sentence-rhythm.ts +3 -2
- package/src/document.ts +22 -6
- package/src/eval.ts +80 -0
- package/src/genre.ts +8 -1
- package/src/init.ts +2 -0
- package/src/measure.ts +21 -0
- package/src/render/eval.ts +62 -0
package/src/init.ts
CHANGED
package/src/measure.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Sentence } from "./plugin.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 文の長さ。**覆った部分の空白を数えない。**
|
|
5
|
+
*
|
|
6
|
+
* 非 prose は同じ長さの空白で覆っている(オフセットを保つため)。そのまま数えると、
|
|
7
|
+
* URL を 1 つ含む文が「245 文字」になる。実文書で max-sentence-length が指摘の
|
|
8
|
+
* 8 割を占めていた原因はこれで、読み手が長いと感じる長さではなかった。
|
|
9
|
+
*
|
|
10
|
+
* 日本語は文字数、英語は語数。単位は adapter が宣言する。
|
|
11
|
+
*/
|
|
12
|
+
export const proseText = (sentence: Sentence): string => sentence.text.replace(/\s+/gu, " ").trim();
|
|
13
|
+
|
|
14
|
+
export const charLength = (sentence: Sentence): number => sentence.text.replace(/\s+/gu, "").length;
|
|
15
|
+
|
|
16
|
+
export const wordLength = (sentence: Sentence): number =>
|
|
17
|
+
proseText(sentence)
|
|
18
|
+
.split(" ")
|
|
19
|
+
.filter((word) => word.length > 0).length;
|
|
20
|
+
|
|
21
|
+
export const lengthOf = (sentence: Sentence, unit: "char" | "word"): number => (unit === "char" ? charLength(sentence) : wordLength(sentence));
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { MIN_CORPUS, TARGET_HIT_RATE, type RuleReport } from "../eval.ts";
|
|
2
|
+
|
|
3
|
+
const pct = (part: number, whole: number): string => `${((part / Math.max(1, whole)) * 100).toFixed(1)}%`;
|
|
4
|
+
|
|
5
|
+
const mark = (report: RuleReport, limit: number): string => {
|
|
6
|
+
if (limit === report.current && limit === report.recommended) return " ← 現在 / 推奨";
|
|
7
|
+
if (limit === report.current) return " ← 現在";
|
|
8
|
+
if (limit === report.recommended) return " ← 推奨";
|
|
9
|
+
return "";
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const sweepLines = (report: RuleReport): string[] =>
|
|
13
|
+
report.sweep.map((point) => {
|
|
14
|
+
const limit = String(point.limit).padStart(6);
|
|
15
|
+
const docs = `${String(point.documents).padStart(3)} 文書`;
|
|
16
|
+
const density = `1万字あたり ${point.per10k.toFixed(1)}`;
|
|
17
|
+
return ` ${limit} ${docs} (${pct(point.documents, report.total).padStart(6)}) 指摘 ${String(point.findings).padStart(3)} 件 ${density}${mark(report, point.limit)}`;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const verdict = (report: RuleReport): string[] => {
|
|
21
|
+
const now = report.sweep.find((point) => point.limit === report.current);
|
|
22
|
+
const rate = now === undefined ? 0 : now.documents / Math.max(1, report.total);
|
|
23
|
+
if (rate <= TARGET_HIT_RATE) return [` いまの閾値で目標(${TARGET_HIT_RATE * 100}% 未満)を満たしています。`];
|
|
24
|
+
if (report.recommended === undefined) {
|
|
25
|
+
return [` どの閾値でも目標を満たしません。この corpus に対して rule 自体が合っていない可能性があります。`];
|
|
26
|
+
}
|
|
27
|
+
return [
|
|
28
|
+
` いまの閾値では ${pct(now?.documents ?? 0, report.total)} の文書が該当し、目標(${TARGET_HIT_RATE * 100}% 未満)を超えています。`,
|
|
29
|
+
` 推奨: ${report.recommended}`,
|
|
30
|
+
"",
|
|
31
|
+
` chaff.yaml に書くなら: rules:\n ${report.rule}: ${report.recommended}`,
|
|
32
|
+
];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const block = (report: RuleReport): string[] => ["", ` ${report.name} (${report.rule})`, "", ...sweepLines(report), "", ...verdict(report), ""];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 閾値は自動で書き換えない。提示して選ばせる。
|
|
39
|
+
* 較正は corpus に対する答えであって、正解ではないため。
|
|
40
|
+
*/
|
|
41
|
+
export const renderEval = (reports: readonly RuleReport[], total: number, paths: number): string =>
|
|
42
|
+
[
|
|
43
|
+
"",
|
|
44
|
+
` ${paths} ファイルを corpus として ${reports.length} 本の rule を測りました。`,
|
|
45
|
+
"",
|
|
46
|
+
" ここにある文書は「人間が書いて公開した良い文書」として扱っています。",
|
|
47
|
+
` そこで多く発火する rule は、閾値が現実に合っていない疑いがあります(目標: ${TARGET_HIT_RATE * 100}% 未満)。`,
|
|
48
|
+
...(total < MIN_CORPUS
|
|
49
|
+
? [
|
|
50
|
+
"",
|
|
51
|
+
` ※ corpus が ${total} 文書しかありません。${TARGET_HIT_RATE * 100}% を表すには ${MIN_CORPUS} 文書以上が要ります。`,
|
|
52
|
+
" いまは割合が「0 か全部」に振れるので、右端の密度(1万字あたりの指摘数)のほうを見てください。",
|
|
53
|
+
]
|
|
54
|
+
: []),
|
|
55
|
+
...reports.flatMap(block),
|
|
56
|
+
" " + "─".repeat(58),
|
|
57
|
+
"",
|
|
58
|
+
` 閾値は自動で書き換えていません。corpus が変われば答えも変わるためです。`,
|
|
59
|
+
"",
|
|
60
|
+
]
|
|
61
|
+
.join("\n")
|
|
62
|
+
.replace(/\{total\}/gu, String(total));
|