chaffjs 0.0.1

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.
Files changed (182) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +76 -0
  3. package/bin/chaff.js +4 -0
  4. package/dist/adapter-load.d.ts +9 -0
  5. package/dist/adapter-load.d.ts.map +1 -0
  6. package/dist/adapter-load.js +38 -0
  7. package/dist/adapter-load.js.map +1 -0
  8. package/dist/baseline.d.ts +23 -0
  9. package/dist/baseline.d.ts.map +1 -0
  10. package/dist/baseline.js +41 -0
  11. package/dist/baseline.js.map +1 -0
  12. package/dist/cli.d.ts +2 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +234 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/config/load.d.ts +13 -0
  17. package/dist/config/load.d.ts.map +1 -0
  18. package/dist/config/load.js +26 -0
  19. package/dist/config/load.js.map +1 -0
  20. package/dist/config/write.d.ts +12 -0
  21. package/dist/config/write.d.ts.map +1 -0
  22. package/dist/config/write.js +60 -0
  23. package/dist/config/write.js.map +1 -0
  24. package/dist/detect-language.d.ts +3 -0
  25. package/dist/detect-language.d.ts.map +1 -0
  26. package/dist/detect-language.js +26 -0
  27. package/dist/detect-language.js.map +1 -0
  28. package/dist/detect.d.ts +7 -0
  29. package/dist/detect.d.ts.map +1 -0
  30. package/dist/detect.js +13 -0
  31. package/dist/detect.js.map +1 -0
  32. package/dist/detectors/count-per-section.d.ts +4 -0
  33. package/dist/detectors/count-per-section.d.ts.map +1 -0
  34. package/dist/detectors/count-per-section.js +13 -0
  35. package/dist/detectors/count-per-section.js.map +1 -0
  36. package/dist/detectors/heading-echo.d.ts +3 -0
  37. package/dist/detectors/heading-echo.d.ts.map +1 -0
  38. package/dist/detectors/heading-echo.js +36 -0
  39. package/dist/detectors/heading-echo.js.map +1 -0
  40. package/dist/detectors/index.d.ts +4 -0
  41. package/dist/detectors/index.d.ts.map +1 -0
  42. package/dist/detectors/index.js +16 -0
  43. package/dist/detectors/index.js.map +1 -0
  44. package/dist/detectors/phrase-match.d.ts +7 -0
  45. package/dist/detectors/phrase-match.d.ts.map +1 -0
  46. package/dist/detectors/phrase-match.js +38 -0
  47. package/dist/detectors/phrase-match.js.map +1 -0
  48. package/dist/detectors/repeated-head.d.ts +3 -0
  49. package/dist/detectors/repeated-head.d.ts.map +1 -0
  50. package/dist/detectors/repeated-head.js +21 -0
  51. package/dist/detectors/repeated-head.js.map +1 -0
  52. package/dist/detectors/sentence-length.d.ts +3 -0
  53. package/dist/detectors/sentence-length.d.ts.map +1 -0
  54. package/dist/detectors/sentence-length.js +19 -0
  55. package/dist/detectors/sentence-length.js.map +1 -0
  56. package/dist/detectors/sentence-rhythm.d.ts +3 -0
  57. package/dist/detectors/sentence-rhythm.d.ts.map +1 -0
  58. package/dist/detectors/sentence-rhythm.js +28 -0
  59. package/dist/detectors/sentence-rhythm.js.map +1 -0
  60. package/dist/document.d.ts +3 -0
  61. package/dist/document.d.ts.map +1 -0
  62. package/dist/document.js +138 -0
  63. package/dist/document.js.map +1 -0
  64. package/dist/files.d.ts +12 -0
  65. package/dist/files.d.ts.map +1 -0
  66. package/dist/files.js +30 -0
  67. package/dist/files.js.map +1 -0
  68. package/dist/genre.d.ts +8 -0
  69. package/dist/genre.d.ts.map +1 -0
  70. package/dist/genre.js +35 -0
  71. package/dist/genre.js.map +1 -0
  72. package/dist/index.d.ts +5 -0
  73. package/dist/index.d.ts.map +1 -0
  74. package/dist/index.js +3 -0
  75. package/dist/index.js.map +1 -0
  76. package/dist/init.d.ts +3 -0
  77. package/dist/init.d.ts.map +1 -0
  78. package/dist/init.js +71 -0
  79. package/dist/init.js.map +1 -0
  80. package/dist/levels.d.ts +20 -0
  81. package/dist/levels.d.ts.map +1 -0
  82. package/dist/levels.js +24 -0
  83. package/dist/levels.js.map +1 -0
  84. package/dist/mask.d.ts +3 -0
  85. package/dist/mask.d.ts.map +1 -0
  86. package/dist/mask.js +26 -0
  87. package/dist/mask.js.map +1 -0
  88. package/dist/plugin.d.ts +116 -0
  89. package/dist/plugin.d.ts.map +1 -0
  90. package/dist/plugin.js +4 -0
  91. package/dist/plugin.js.map +1 -0
  92. package/dist/position.d.ts +8 -0
  93. package/dist/position.d.ts.map +1 -0
  94. package/dist/position.js +7 -0
  95. package/dist/position.js.map +1 -0
  96. package/dist/render/compact.d.ts +5 -0
  97. package/dist/render/compact.d.ts.map +1 -0
  98. package/dist/render/compact.js +15 -0
  99. package/dist/render/compact.js.map +1 -0
  100. package/dist/render/explain.d.ts +4 -0
  101. package/dist/render/explain.d.ts.map +1 -0
  102. package/dist/render/explain.js +29 -0
  103. package/dist/render/explain.js.map +1 -0
  104. package/dist/render/friendly.d.ts +4 -0
  105. package/dist/render/friendly.d.ts.map +1 -0
  106. package/dist/render/friendly.js +55 -0
  107. package/dist/render/friendly.js.map +1 -0
  108. package/dist/render/rules-json.d.ts +6 -0
  109. package/dist/render/rules-json.d.ts.map +1 -0
  110. package/dist/render/rules-json.js +39 -0
  111. package/dist/render/rules-json.js.map +1 -0
  112. package/dist/render/summary.d.ts +10 -0
  113. package/dist/render/summary.d.ts.map +1 -0
  114. package/dist/render/summary.js +18 -0
  115. package/dist/render/summary.js.map +1 -0
  116. package/dist/render/suppressions.d.ts +13 -0
  117. package/dist/render/suppressions.d.ts.map +1 -0
  118. package/dist/render/suppressions.js +50 -0
  119. package/dist/render/suppressions.js.map +1 -0
  120. package/dist/render/text.d.ts +6 -0
  121. package/dist/render/text.d.ts.map +1 -0
  122. package/dist/render/text.js +8 -0
  123. package/dist/render/text.js.map +1 -0
  124. package/dist/rule-load.d.ts +3 -0
  125. package/dist/rule-load.d.ts.map +1 -0
  126. package/dist/rule-load.js +68 -0
  127. package/dist/rule-load.js.map +1 -0
  128. package/dist/run.d.ts +14 -0
  129. package/dist/run.d.ts.map +1 -0
  130. package/dist/run.js +47 -0
  131. package/dist/run.js.map +1 -0
  132. package/dist/stet.d.ts +19 -0
  133. package/dist/stet.d.ts.map +1 -0
  134. package/dist/stet.js +61 -0
  135. package/dist/stet.js.map +1 -0
  136. package/dist/watch.d.ts +12 -0
  137. package/dist/watch.d.ts.map +1 -0
  138. package/dist/watch.js +40 -0
  139. package/dist/watch.js.map +1 -0
  140. package/package.json +63 -0
  141. package/rules/bold-density.yaml +28 -0
  142. package/rules/closing-cliche.yaml +30 -0
  143. package/rules/empty-intensifier.yaml +29 -0
  144. package/rules/heading-echo.yaml +30 -0
  145. package/rules/max-sentence-length.yaml +28 -0
  146. package/rules/padded-intro.yaml +30 -0
  147. package/rules/repeated-sentence-head.yaml +28 -0
  148. package/rules/sentence-rhythm.yaml +30 -0
  149. package/src/adapter-load.ts +31 -0
  150. package/src/baseline.ts +51 -0
  151. package/src/cli.ts +259 -0
  152. package/src/config/load.ts +38 -0
  153. package/src/config/write.ts +63 -0
  154. package/src/detect-language.ts +28 -0
  155. package/src/detect.ts +15 -0
  156. package/src/detectors/count-per-section.ts +15 -0
  157. package/src/detectors/heading-echo.ts +40 -0
  158. package/src/detectors/index.ts +17 -0
  159. package/src/detectors/phrase-match.ts +41 -0
  160. package/src/detectors/repeated-head.ts +29 -0
  161. package/src/detectors/sentence-length.ts +21 -0
  162. package/src/detectors/sentence-rhythm.ts +28 -0
  163. package/src/document.ts +154 -0
  164. package/src/files.ts +34 -0
  165. package/src/genre.ts +36 -0
  166. package/src/index.ts +4 -0
  167. package/src/init.ts +76 -0
  168. package/src/levels.ts +27 -0
  169. package/src/mask.ts +33 -0
  170. package/src/plugin.ts +124 -0
  171. package/src/position.ts +9 -0
  172. package/src/render/compact.ts +18 -0
  173. package/src/render/explain.ts +31 -0
  174. package/src/render/friendly.ts +62 -0
  175. package/src/render/rules-json.ts +46 -0
  176. package/src/render/summary.ts +22 -0
  177. package/src/render/suppressions.ts +57 -0
  178. package/src/render/text.ts +13 -0
  179. package/src/rule-load.ts +78 -0
  180. package/src/run.ts +61 -0
  181. package/src/stet.ts +79 -0
  182. package/src/watch.ts +52 -0
@@ -0,0 +1,60 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { Scalar, YAMLMap, isMap, isScalar, parseDocument } from "yaml";
3
+ import { definedLevels } from "../levels.js";
4
+ import { localized } from "../render/text.js";
5
+ const TEMPLATE = `# chaff.yaml — このチームの文章規範
6
+ #
7
+ # ここに書くのは「既定から変えたもの」だけです。
8
+ # 値は strict / normal / relaxed / off の 4 つから選びます。数字は要りません。
9
+
10
+ rules:
11
+ `;
12
+ const today = () => new Date().toISOString().slice(0, 10);
13
+ const reasonOf = (value) => {
14
+ if (!isScalar(value) || typeof value.comment !== "string")
15
+ return undefined;
16
+ const text = value.comment.trim();
17
+ return text.length === 0 ? undefined : text;
18
+ };
19
+ const ruleComment = (rule, language) => ` ${localized(rule.name, language)}\n ${localized(rule.why, language)}`;
20
+ const load = (path) => parseDocument(existsSync(path) ? readFileSync(path, "utf8") : TEMPLATE);
21
+ /**
22
+ * 設定を機械的に書き換えるが、**既存のコメントを壊さない**。
23
+ * 設定ファイルがチームの規範そのものである以上、コメントが失われることは規範が失われること。
24
+ * spec §19.4。
25
+ */
26
+ export const applyLevel = (path, rule, level, why, language, who) => {
27
+ if (!definedLevels(rule).includes(level)) {
28
+ return { ok: false, message: `${rule.id} に ${level} はありません。normal と同じ設定です。\n設定は変更しませんでした。` };
29
+ }
30
+ const doc = load(path);
31
+ const rules = doc.get("rules");
32
+ const map = isMap(rules) ? rules : new YAMLMap();
33
+ // 人が読んで書き足すファイルなので、必ず block style にする。flow style({ a: b })は読めない。
34
+ map.flow = false;
35
+ if (!isMap(rules))
36
+ doc.set("rules", map);
37
+ const existing = map.items.find((pair) => String(pair.key) === rule.id);
38
+ const previous = reasonOf(existing?.value);
39
+ // 古い理由が新しい値に付いたまま残ると、履歴が嘘になる。
40
+ if (previous !== undefined && why === undefined) {
41
+ return { ok: false, message: `${rule.id} には既に理由が書かれています:\n ${previous}\n値を変えるときは --why で新しい理由を書いてください。` };
42
+ }
43
+ // 取り除いて足し直すと、元の rule に付いていた説明コメントが行き場を失って残る。
44
+ // 既にある鍵は、その場で値だけ差し替える。
45
+ const value = new Scalar(level);
46
+ if (why !== undefined)
47
+ value.comment = ` ${today()} ${why} / ${who}`;
48
+ if (existing === undefined) {
49
+ map.items = [...map.items, doc.createPair(rule.id, value)];
50
+ const added = map.items.at(-1);
51
+ if (added !== undefined && isScalar(added.key))
52
+ added.key.commentBefore = ruleComment(rule, language);
53
+ }
54
+ else {
55
+ existing.value = value;
56
+ }
57
+ writeFileSync(path, String(doc), "utf8");
58
+ return { ok: true, message: `${rule.id} を ${level} にしました(${path})` };
59
+ };
60
+ //# sourceMappingURL=write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/config/write.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare const japaneseRatio: (text: string) => number;
2
+ export declare const latinRatio: (text: string) => number;
3
+ //# sourceMappingURL=detect-language.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-language.d.ts","sourceRoot":"","sources":["../src/detect-language.ts"],"names":[],"mappings":"AAyBA,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,MAA+B,CAAC;AAE7E,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,MAAmC,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * core が「どのアダプタを取りに行くか」を決めるためだけの当て推量。spec §17.2。
3
+ *
4
+ * アダプタを読み込む前に動く必要があるので、ここは辞書も依存も持てない。
5
+ * アダプタ側の detect() とは層が違う。あちらは読み込み済みの候補から選ぶための
6
+ * 権威ある点数で、こちらは何も読み込んでいない状態での見当付け。
7
+ * 似た計算を両方が持つのは、この順序から来る避けられない重複。
8
+ *
9
+ * 呼ぶ側が、コードブロック・インラインコード・URL・front matter を先に落としておくこと。
10
+ * 技術文書は英語のコードを大量に含むため、落とさないと日本語文書が英語と判定される。
11
+ */
12
+ const JAPANESE = /[぀-ゟ゠-ヿ一-鿿]/gu;
13
+ const LATIN_LETTER = /[a-z]/giu;
14
+ const COUNTABLE = /\S/gu;
15
+ // 共有した /g 正規表現を exec で回すと lastIndex が持ち越されて数え落とす。
16
+ // matchAll は内部で複製するので、その事故が起きない。
17
+ const count = (text, pattern) => [...text.matchAll(pattern)].length;
18
+ const ratio = (text, pattern) => {
19
+ const total = count(text, COUNTABLE);
20
+ if (total === 0)
21
+ return 0;
22
+ return count(text, pattern) / total;
23
+ };
24
+ export const japaneseRatio = (text) => ratio(text, JAPANESE);
25
+ export const latinRatio = (text) => ratio(text, LATIN_LETTER);
26
+ //# sourceMappingURL=detect-language.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-language.js","sourceRoot":"","sources":["../src/detect-language.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,QAAQ,GAAG,eAAe,CAAC;AACjC,MAAM,YAAY,GAAG,UAAU,CAAC;AAChC,MAAM,SAAS,GAAG,MAAM,CAAC;AAEzB,kDAAkD;AAClD,iCAAiC;AACjC,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,OAAe,EAAU,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AAE5F,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,OAAe,EAAU,EAAE;IACtD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACrC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC1B,OAAO,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC;AACtC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAE7E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ export type LanguageGuess = {
2
+ readonly language: string;
3
+ readonly confidence: number;
4
+ readonly from: string;
5
+ };
6
+ export declare const guessLanguage: (source: string) => LanguageGuess;
7
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAQ9G,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,KAAG,aAI9C,CAAC"}
package/dist/detect.js ADDED
@@ -0,0 +1,13 @@
1
+ import { japaneseRatio, latinRatio } from "./detect-language.js";
2
+ /**
3
+ * 技術文書の日本語は英字を大量に含む(コマンド名、識別子、製品名)。
4
+ * 仮名と漢字が 15% あれば日本語と見る。corpus で測るまでの暫定値。spec §26。
5
+ */
6
+ const JAPANESE_FLOOR = 0.15;
7
+ export const guessLanguage = (source) => {
8
+ const japanese = japaneseRatio(source);
9
+ if (japanese >= JAPANESE_FLOOR)
10
+ return { language: "ja", confidence: japanese, from: "content" };
11
+ return { language: "en", confidence: latinRatio(source), from: "content" };
12
+ };
13
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIjE;;;GAGG;AACH,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAc,EAAiB,EAAE;IAC7D,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,QAAQ,IAAI,cAAc;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IACjG,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAC7E,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../plugin.ts";
2
+ /** 節あたりの強調の数。bold-density が使う。 */
3
+ export declare const countPerSection: Detector;
4
+ //# sourceMappingURL=count-per-section.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"count-per-section.d.ts","sourceRoot":"","sources":["../../src/detectors/count-per-section.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAW,MAAM,cAAc,CAAC;AAEtD,kCAAkC;AAClC,eAAO,MAAM,eAAe,EAAE,QAWvB,CAAC"}
@@ -0,0 +1,13 @@
1
+ /** 節あたりの強調の数。bold-density が使う。 */
2
+ export const countPerSection = (doc, options) => doc.sections
3
+ .filter((section) => section.strongCount > options.limit)
4
+ .map((section) => ({
5
+ rule: "bold-density",
6
+ severity: "warning",
7
+ line: 0,
8
+ column: 0,
9
+ // 節全体を引くと段落をまたいで読めなくなる。最初の文だけ見せて場所を示す。
10
+ quote: section.firstSentence?.text.trim() ?? "",
11
+ values: { count: section.strongCount, limit: options.limit, offset: section.firstSentence?.span.start ?? section.span.start },
12
+ }));
13
+ //# sourceMappingURL=count-per-section.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"count-per-section.js","sourceRoot":"","sources":["../../src/detectors/count-per-section.ts"],"names":[],"mappings":"AAEA,kCAAkC;AAClC,MAAM,CAAC,MAAM,eAAe,GAAa,CAAC,GAAG,EAAE,OAAO,EAAa,EAAE,CACnE,GAAG,CAAC,QAAQ;KACT,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC;KACxD,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACjB,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,SAAS;IACnB,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,uCAAuC;IACvC,KAAK,EAAE,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE;IAC/C,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE;CAC9H,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Detector } from "../plugin.ts";
2
+ export declare const headingEcho: Detector;
3
+ //# sourceMappingURL=heading-echo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heading-echo.d.ts","sourceRoot":"","sources":["../../src/detectors/heading-echo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAW,MAAM,cAAc,CAAC;AA2BtD,eAAO,MAAM,WAAW,EAAE,QAYnB,CAAC"}
@@ -0,0 +1,36 @@
1
+ /** 見出しが短すぎると、偶然の一致で 100% になる。これ未満の見出しは見ない。 */
2
+ const MIN_GRAMS = 4;
3
+ /**
4
+ * 文字 3-gram で測る。
5
+ * 語 n-gram にすると wordSplit capability が要り、L1(言語を問わず動く)から外れる。
6
+ */
7
+ const trigrams = (text) => {
8
+ const clean = [...text.replace(/\s+/gu, "")];
9
+ return new Set(clean.slice(0, Math.max(0, clean.length - 2)).map((__char, index) => clean.slice(index, index + 3).join("")));
10
+ };
11
+ /**
12
+ * Jaccard ではなく包含率を使う。
13
+ *
14
+ * この rule が測りたいのは「見出しのどれだけが繰り返されたか」であって、
15
+ * 両者がどれだけ似ているかではない。見出しを丸ごと含んだうえで説明を続ける文は、
16
+ * 文が長いというだけで Jaccard が下がってしまい、いちばん典型的な反復を取り逃す。
17
+ * (「## キャッシュの仕組み」→「キャッシュの仕組みについて説明します。」で Jaccard 44%)
18
+ */
19
+ const containment = (heading, sentence) => {
20
+ if (heading.size < MIN_GRAMS || sentence.size === 0)
21
+ return 0;
22
+ return [...heading].filter((gram) => sentence.has(gram)).length / heading.size;
23
+ };
24
+ export const headingEcho = (doc, options) => doc.sections
25
+ .filter((section) => section.heading.length > 0 && section.firstSentence !== undefined)
26
+ .map((section) => ({ section, overlap: Math.round(containment(trigrams(section.heading), trigrams(section.firstSentence?.text ?? "")) * 100) }))
27
+ .filter(({ overlap }) => overlap >= options.limit)
28
+ .map(({ section, overlap }) => ({
29
+ rule: "heading-echo",
30
+ severity: "warning",
31
+ line: 0,
32
+ column: 0,
33
+ quote: section.firstSentence?.text.trim() ?? "",
34
+ values: { heading: section.heading, count: overlap, limit: options.limit, offset: section.firstSentence?.span.start ?? section.span.start },
35
+ }));
36
+ //# sourceMappingURL=heading-echo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heading-echo.js","sourceRoot":"","sources":["../../src/detectors/heading-echo.ts"],"names":[],"mappings":"AAEA,8CAA8C;AAC9C,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB;;;GAGG;AACH,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAe,EAAE;IAC7C,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IAC7C,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/H,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,WAAW,GAAG,CAAC,OAAoB,EAAE,QAAqB,EAAU,EAAE;IAC1E,IAAI,OAAO,CAAC,IAAI,GAAG,SAAS,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;AACjF,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAa,CAAC,GAAG,EAAE,OAAO,EAAa,EAAE,CAC/D,GAAG,CAAC,QAAQ;KACT,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,CAAC;KACtF,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;KAC/I,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC;KACjD,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IAC9B,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,SAAS;IACnB,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,KAAK,EAAE,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE;IAC/C,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE;CAC5I,CAAC,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Detector } from "../plugin.ts";
2
+ /** rule 定義の how_to_find がここを引く。rule 側は実装を知らない。 */
3
+ export declare const DETECTORS: Readonly<Record<string, Detector>>;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/detectors/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAQ7C,kDAAkD;AAClD,eAAO,MAAM,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAOxD,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { countPerSection } from "./count-per-section.js";
2
+ import { sentenceLength } from "./sentence-length.js";
3
+ import { headingEcho } from "./heading-echo.js";
4
+ import { repeatedHead } from "./repeated-head.js";
5
+ import { sentenceRhythm } from "./sentence-rhythm.js";
6
+ import { phraseMatch } from "./phrase-match.js";
7
+ /** rule 定義の how_to_find がここを引く。rule 側は実装を知らない。 */
8
+ export const DETECTORS = {
9
+ "count-per-section": countPerSection,
10
+ "sentence-length": sentenceLength,
11
+ "heading-echo": headingEcho,
12
+ "repeated-head": repeatedHead,
13
+ "sentence-rhythm": sentenceRhythm,
14
+ "phrase-match": phraseMatch,
15
+ };
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/detectors/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,kDAAkD;AAClD,MAAM,CAAC,MAAM,SAAS,GAAuC;IAC3D,mBAAmB,EAAE,eAAe;IACpC,iBAAiB,EAAE,cAAc;IACjC,cAAc,EAAE,WAAW;IAC3B,eAAe,EAAE,YAAY;IAC7B,iBAAiB,EAAE,cAAc;IACjC,cAAc,EAAE,WAAW;CAC5B,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Detector } from "../plugin.ts";
2
+ /**
3
+ * 語彙表との照合。detector は共通で、語彙だけが言語別に差し替わる。spec §11。
4
+ * これが L2 の全体像で、新しい言語は語彙表を書くだけで動く。
5
+ */
6
+ export declare const phraseMatch: Detector;
7
+ //# sourceMappingURL=phrase-match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phrase-match.d.ts","sourceRoot":"","sources":["../../src/detectors/phrase-match.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAoC,MAAM,cAAc,CAAC;AAuB/E;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,QAazB,CAAC"}
@@ -0,0 +1,38 @@
1
+ /** 冒頭は先頭 2 段落ぶん。「どの記事にも当てはまる書き出し」は冒頭にあるときだけ問題。 */
2
+ const OPENING_SENTENCES = 4;
3
+ /**
4
+ * 見る範囲を絞る。同じ語でも、どこにあるかで意味が変わる。
5
+ * 「まとめると」は結びなら定型だが、本文の途中なら普通の接続。
6
+ */
7
+ const inScope = (doc, where) => {
8
+ if (where === "opening")
9
+ return doc.sentences.slice(0, OPENING_SENTENCES);
10
+ if (where === "closing")
11
+ return doc.sections.at(-1)?.sentences ?? [];
12
+ return doc.sentences;
13
+ };
14
+ const hitsIn = (sentences, patterns) => sentences.flatMap((sentence) => {
15
+ const matched = patterns.find((pattern) => sentence.text.toLowerCase().includes(pattern.toLowerCase()));
16
+ return matched === undefined ? [] : [{ sentence, matched }];
17
+ });
18
+ /**
19
+ * 語彙表との照合。detector は共通で、語彙だけが言語別に差し替わる。spec §11。
20
+ * これが L2 の全体像で、新しい言語は語彙表を書くだけで動く。
21
+ */
22
+ export const phraseMatch = (doc, options) => {
23
+ const patterns = (options.lexicon ?? []).map((entry) => entry.pattern);
24
+ if (patterns.length === 0)
25
+ return [];
26
+ const hits = hitsIn(inScope(doc, options.where), patterns);
27
+ if (hits.length < options.limit)
28
+ return [];
29
+ return hits.map((hit) => ({
30
+ rule: "",
31
+ severity: "warning",
32
+ line: 0,
33
+ column: 0,
34
+ quote: hit.sentence.text.trim(),
35
+ values: { matched: hit.matched, count: hits.length, limit: options.limit, offset: hit.sentence.span.start },
36
+ }));
37
+ };
38
+ //# sourceMappingURL=phrase-match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phrase-match.js","sourceRoot":"","sources":["../../src/detectors/phrase-match.ts"],"names":[],"mappings":"AAEA,kDAAkD;AAClD,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B;;;GAGG;AACH,MAAM,OAAO,GAAG,CAAC,GAAkB,EAAE,KAAyB,EAAuB,EAAE;IACrF,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAC1E,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;IACrE,OAAO,GAAG,CAAC,SAAS,CAAC;AACvB,CAAC,CAAC;AAIF,MAAM,MAAM,GAAG,CAAC,SAA8B,EAAE,QAA2B,EAAS,EAAE,CACpF,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;IAC7B,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACxG,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;AAC9D,CAAC,CAAC,CAAC;AAEL;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAa,CAAC,GAAG,EAAE,OAAO,EAAa,EAAE;IAC/D,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC3D,IAAI,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACxB,IAAI,EAAE,EAAE;QACR,QAAQ,EAAE,SAAS;QACnB,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;QACT,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE;QAC/B,MAAM,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE;KAC5G,CAAC,CAAC,CAAC;AACN,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Detector } from "../plugin.ts";
2
+ export declare const repeatedHead: Detector;
3
+ //# sourceMappingURL=repeated-head.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repeated-head.d.ts","sourceRoot":"","sources":["../../src/detectors/repeated-head.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,cAAc,CAAC;AAkBhE,eAAO,MAAM,YAAY,EAAE,QAUpB,CAAC"}
@@ -0,0 +1,21 @@
1
+ const HEAD_CHARS = 6;
2
+ const headOf = (sentence) => sentence.text.trim().replace(/\s+/gu, "").slice(0, HEAD_CHARS);
3
+ const runsOf = (sentences) => sentences.reduce((acc, sentence) => {
4
+ const head = headOf(sentence);
5
+ const last = acc.at(-1);
6
+ if (last !== undefined && last.head === head && head.length === HEAD_CHARS) {
7
+ return [...acc.slice(0, -1), { head, members: [...last.members, sentence] }];
8
+ }
9
+ return [...acc, { head, members: [sentence] }];
10
+ }, []);
11
+ export const repeatedHead = (doc, options) => runsOf(doc.sentences)
12
+ .filter((run) => run.members.length > options.limit)
13
+ .map((run) => ({
14
+ rule: "repeated-sentence-head",
15
+ severity: "warning",
16
+ line: 0,
17
+ column: 0,
18
+ quote: run.members.map((sentence) => sentence.text.trim()).join(" "),
19
+ values: { head: run.head, count: run.members.length, limit: options.limit, offset: run.members[0]?.span.start ?? 0 },
20
+ }));
21
+ //# sourceMappingURL=repeated-head.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repeated-head.js","sourceRoot":"","sources":["../../src/detectors/repeated-head.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAAG,CAAC,CAAC;AAErB,MAAM,MAAM,GAAG,CAAC,QAAkB,EAAU,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;AAI9G,MAAM,MAAM,GAAG,CAAC,SAA8B,EAAS,EAAE,CACvD,SAAS,CAAC,MAAM,CAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACxB,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC3E,OAAO,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,CAAC,GAAG,GAAG,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACjD,CAAC,EAAE,EAAE,CAAC,CAAC;AAET,MAAM,CAAC,MAAM,YAAY,GAAa,CAAC,GAAG,EAAE,OAAO,EAAa,EAAE,CAChE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;KAClB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;KACnD,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACb,IAAI,EAAE,wBAAwB;IAC9B,QAAQ,EAAE,SAAS;IACnB,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;IACpE,MAAM,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC,EAAE;CACrH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Detector } from "../plugin.ts";
2
+ export declare const sentenceLength: Detector;
3
+ //# sourceMappingURL=sentence-length.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentence-length.d.ts","sourceRoot":"","sources":["../../src/detectors/sentence-length.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAoC,MAAM,cAAc,CAAC;AAS/E,eAAO,MAAM,cAAc,EAAE,QAWtB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /** 日本語は文字、英語は語。単位は adapter が宣言する。spec §7。 */
2
+ const measure = (doc, sentence) => {
3
+ const text = sentence.text.trim();
4
+ if (doc.lengthUnit === "char")
5
+ return text.length;
6
+ return text.split(/\s+/u).filter((word) => word.length > 0).length;
7
+ };
8
+ export const sentenceLength = (doc, options) => doc.sentences
9
+ .map((sentence) => ({ sentence, size: measure(doc, sentence) }))
10
+ .filter(({ size }) => size > options.limit)
11
+ .map(({ sentence, size }) => ({
12
+ rule: "max-sentence-length",
13
+ severity: "warning",
14
+ line: 0,
15
+ column: 0,
16
+ quote: sentence.text.trim(),
17
+ values: { count: size, limit: options.limit, offset: sentence.span.start },
18
+ }));
19
+ //# sourceMappingURL=sentence-length.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentence-length.js","sourceRoot":"","sources":["../../src/detectors/sentence-length.ts"],"names":[],"mappings":"AAEA,6CAA6C;AAC7C,MAAM,OAAO,GAAG,CAAC,GAAkB,EAAE,QAAkB,EAAU,EAAE;IACjE,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAClC,IAAI,GAAG,CAAC,UAAU,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC;IAClD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AACrE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAa,CAAC,GAAG,EAAE,OAAO,EAAa,EAAE,CAClE,GAAG,CAAC,SAAS;KACV,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;KAC/D,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC;KAC1C,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5B,IAAI,EAAE,qBAAqB;IAC3B,QAAQ,EAAE,SAAS;IACnB,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE;IAC3B,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE;CAC3E,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Detector } from "../plugin.ts";
2
+ export declare const sentenceRhythm: Detector;
3
+ //# sourceMappingURL=sentence-rhythm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentence-rhythm.d.ts","sourceRoot":"","sources":["../../src/detectors/sentence-rhythm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAW,MAAM,cAAc,CAAC;AAYtD,eAAO,MAAM,cAAc,EAAE,QAe5B,CAAC"}
@@ -0,0 +1,28 @@
1
+ /** 文が少ないと変動係数は当てにならない。ここを下回る文書は見ない。 */
2
+ const MIN_SENTENCES = 8;
3
+ const coefficientOfVariation = (sizes) => {
4
+ const mean = sizes.reduce((sum, size) => sum + size, 0) / sizes.length;
5
+ if (mean === 0)
6
+ return 0;
7
+ const variance = sizes.reduce((sum, size) => sum + (size - mean) ** 2, 0) / sizes.length;
8
+ return Math.sqrt(variance) / mean;
9
+ };
10
+ export const sentenceRhythm = (doc, options) => {
11
+ const sizes = doc.sentences.map((sentence) => sentence.text.trim().length);
12
+ if (sizes.length < MIN_SENTENCES)
13
+ return [];
14
+ const spread = Math.round(coefficientOfVariation(sizes) * 100);
15
+ if (spread >= options.limit)
16
+ return [];
17
+ return [
18
+ {
19
+ rule: "sentence-rhythm",
20
+ severity: "warning",
21
+ line: 0,
22
+ column: 0,
23
+ quote: doc.sentences[0]?.text.trim() ?? "",
24
+ values: { count: spread, limit: options.limit, offset: doc.sentences[0]?.span.start ?? 0 },
25
+ },
26
+ ];
27
+ };
28
+ //# sourceMappingURL=sentence-rhythm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentence-rhythm.js","sourceRoot":"","sources":["../../src/detectors/sentence-rhythm.ts"],"names":[],"mappings":"AAEA,uCAAuC;AACvC,MAAM,aAAa,GAAG,CAAC,CAAC;AAExB,MAAM,sBAAsB,GAAG,CAAC,KAAwB,EAAU,EAAE;IAClE,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;IACvE,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACzB,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;IACzF,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;AACpC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAa,CAAC,GAAG,EAAE,OAAO,EAAa,EAAE;IAClE,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC;IAC3E,IAAI,KAAK,CAAC,MAAM,GAAG,aAAa;QAAE,OAAO,EAAE,CAAC;IAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;IAC/D,IAAI,MAAM,IAAI,OAAO,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACvC,OAAO;QACL;YACE,IAAI,EAAE,iBAAiB;YACvB,QAAQ,EAAE,SAAS;YACnB,IAAI,EAAE,CAAC;YACP,MAAM,EAAE,CAAC;YACT,KAAK,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE;YAC1C,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC,EAAE;SAC3F;KACF,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { LanguageAdapter, ProseDocument } from "./plugin.ts";
2
+ export declare const buildDocument: (path: string, source: string, adapter: LanguageAdapter) => ProseDocument;
3
+ //# sourceMappingURL=document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAA2B,MAAM,aAAa,CAAC;AAsI3F,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,EAAE,QAAQ,MAAM,EAAE,SAAS,eAAe,KAAG,aAatF,CAAC"}
@@ -0,0 +1,138 @@
1
+ import { fromMarkdown } from "mdast-util-from-markdown";
2
+ import { gfmTable } from "micromark-extension-gfm-table";
3
+ import { gfmTableFromMarkdown } from "mdast-util-gfm-table";
4
+ import { frontmatter } from "micromark-extension-frontmatter";
5
+ import { frontmatterFromMarkdown } from "mdast-util-frontmatter";
6
+ import { maskSpans } from "./mask.js";
7
+ /** 本文として数えないもの。コードは文章ではなく、表は文章の形をしていない。 */
8
+ const NOT_PROSE = new Set(["code", "inlineCode", "html", "yaml", "toml", "table", "thematicBreak", "definition", "image", "imageReference"]);
9
+ const spanOf = (node) => {
10
+ const start = node.position?.start.offset;
11
+ const end = node.position?.end.offset;
12
+ return start === undefined || end === undefined ? undefined : { start, end };
13
+ };
14
+ const walk = (node, visit) => {
15
+ visit(node);
16
+ (node.children ?? []).forEach((child) => walk(child, visit));
17
+ };
18
+ const parse = (source) => fromMarkdown(source, { extensions: [gfmTable(), frontmatter(["yaml"])], mdastExtensions: [gfmTableFromMarkdown(), frontmatterFromMarkdown(["yaml"])] });
19
+ /** link は `[text](url)` の外側だけを覆う。表示される文字は本文なので残す。 */
20
+ const linkChrome = (node) => {
21
+ const whole = spanOf(node);
22
+ const first = node.children?.[0];
23
+ const last = node.children?.at(-1);
24
+ const inner = first === undefined || last === undefined ? undefined : { start: spanOf(first)?.start, end: spanOf(last)?.end };
25
+ if (whole === undefined || inner?.start === undefined || inner.end === undefined)
26
+ return whole === undefined ? [] : [whole];
27
+ return [
28
+ { start: whole.start, end: inner.start },
29
+ { start: inner.end, end: whole.end },
30
+ ];
31
+ };
32
+ const collectMasks = (root) => {
33
+ const spans = [];
34
+ walk(root, (node) => {
35
+ if (node.type === "link" || node.type === "linkReference") {
36
+ spans.push(...linkChrome(node));
37
+ return;
38
+ }
39
+ if (!NOT_PROSE.has(node.type) && node.type !== "heading")
40
+ return;
41
+ const span = spanOf(node);
42
+ if (span !== undefined)
43
+ spans.push(span);
44
+ });
45
+ return spans;
46
+ };
47
+ const textOf = (node, source) => {
48
+ const parts = [];
49
+ walk(node, (child) => {
50
+ if (child.type !== "text" && child.type !== "inlineCode")
51
+ return;
52
+ const span = spanOf(child);
53
+ if (span !== undefined)
54
+ parts.push(source.slice(span.start, span.end));
55
+ });
56
+ return parts.join("");
57
+ };
58
+ const headingsOf = (root, source) => {
59
+ const found = [];
60
+ walk(root, (node) => {
61
+ if (node.type !== "heading")
62
+ return;
63
+ const span = spanOf(node);
64
+ const depth = typeof node === "object" && "depth" in node && typeof node.depth === "number" ? node.depth : 1;
65
+ if (span !== undefined)
66
+ found.push({ depth, text: textOf(node, source).trim(), start: span.start, end: span.end });
67
+ });
68
+ return found;
69
+ };
70
+ const strongSpans = (root) => {
71
+ const found = [];
72
+ walk(root, (node) => {
73
+ if (node.type !== "strong")
74
+ return;
75
+ const span = spanOf(node);
76
+ if (span !== undefined)
77
+ found.push(span);
78
+ });
79
+ return found;
80
+ };
81
+ const within = (span, from, to) => span.start >= from && span.start < to;
82
+ const paragraphSpans = (root) => {
83
+ const found = [];
84
+ walk(root, (node) => {
85
+ if (node.type !== "paragraph")
86
+ return;
87
+ const span = spanOf(node);
88
+ if (span !== undefined)
89
+ found.push(span);
90
+ });
91
+ return found;
92
+ };
93
+ /**
94
+ * 文の分割は**段落ごと**に行う。
95
+ *
96
+ * 文書全体を一度に渡すと、句点で終わらない行(「条件:」のような見出し的な行)が、
97
+ * 覆った表やコードブロックを越えて次の句点まで飲み込む。段落は文が跨がない境界なので、
98
+ * ここで切れば構造的に起きない。
99
+ */
100
+ const sentencesOf = (prose, paragraphs, adapter) => paragraphs.flatMap((paragraph) => adapter
101
+ .segment(prose.slice(paragraph.start, paragraph.end))
102
+ .sentences.filter((sentence) => sentence.text.trim().length > 0)
103
+ .map((sentence) => ({
104
+ span: { start: paragraph.start + sentence.span.start, end: paragraph.start + sentence.span.end },
105
+ text: sentence.text,
106
+ })));
107
+ const sectionsOf = (headings, sentences, strongs, length) => {
108
+ const bounds = headings.map((heading, index) => ({ heading, from: heading.end, to: headings[index + 1]?.start ?? length }));
109
+ const lead = { heading: { depth: 0, text: "", start: 0, end: 0 }, from: 0, to: headings[0]?.start ?? length };
110
+ return [lead, ...bounds]
111
+ .filter((bound) => bound.to > bound.from)
112
+ .map(({ heading, from, to }) => {
113
+ const inside = sentences.filter((sentence) => within(sentence.span, from, to));
114
+ return {
115
+ depth: heading.depth,
116
+ heading: heading.text,
117
+ span: { start: from, end: to },
118
+ sentences: inside,
119
+ strongCount: strongs.filter((span) => within(span, from, to)).length,
120
+ firstSentence: inside[0],
121
+ };
122
+ });
123
+ };
124
+ export const buildDocument = (path, source, adapter) => {
125
+ const root = parse(source);
126
+ const prose = maskSpans(source, collectMasks(root));
127
+ const sentences = sentencesOf(prose, paragraphSpans(root), adapter);
128
+ return {
129
+ path,
130
+ source,
131
+ language: adapter.id,
132
+ lengthUnit: adapter.capabilities.lengthUnit,
133
+ sections: sectionsOf(headingsOf(root, source), sentences, strongSpans(root), source.length),
134
+ sentences,
135
+ lexicons: adapter.lexicons,
136
+ };
137
+ };
138
+ //# sourceMappingURL=document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.js","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 与えられたファイル・ディレクトリ・glob を、検査する Markdown の一覧にする。
3
+ * 0 件は成功にしない(§14)。glob が何にもマッチしないまま CI が緑になると、
4
+ * 「通っているが何も検証していない」状態が延々と続く。
5
+ */
6
+ /**
7
+ * ロケールを明示する。既定のロケールに任せると、同じ入力でも機械によって順序が変わる。
8
+ * 指摘の並びは CI のログにも baseline にも入るので、順序が揺れてはいけない。
9
+ */
10
+ export declare const byPath: (left: string, right: string) => number;
11
+ export declare const collectTargets: (targets: readonly string[]) => string[];
12
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"AAmBA;;;;GAIG;AACH;;;GAGG;AACH,eAAO,MAAM,MAAM,GAAI,MAAM,MAAM,EAAE,OAAO,MAAM,KAAG,MAAyC,CAAC;AAE/F,eAAO,MAAM,cAAc,GAAI,SAAS,SAAS,MAAM,EAAE,KAAG,MAAM,EAGjE,CAAC"}