chaffjs 0.2.0 → 0.4.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/backends/anthropic.d.ts +24 -0
- package/dist/backends/anthropic.d.ts.map +1 -0
- package/dist/backends/anthropic.js +44 -0
- package/dist/backends/anthropic.js.map +1 -0
- package/dist/backends/openai.d.ts +29 -0
- package/dist/backends/openai.d.ts.map +1 -0
- package/dist/backends/openai.js +33 -0
- package/dist/backends/openai.js.map +1 -0
- package/dist/backends/types.d.ts +32 -0
- package/dist/backends/types.d.ts.map +1 -0
- package/dist/backends/types.js +18 -0
- package/dist/backends/types.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -5
- package/dist/cli.js.map +1 -1
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +5 -2
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/test.d.ts.map +1 -1
- package/dist/commands/test.js +62 -16
- package/dist/commands/test.js.map +1 -1
- package/dist/config/load.d.ts +8 -1
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +16 -3
- package/dist/config/load.js.map +1 -1
- package/dist/detectors/agentless-passive.d.ts +3 -0
- package/dist/detectors/agentless-passive.d.ts.map +1 -0
- package/dist/detectors/agentless-passive.js +31 -0
- package/dist/detectors/agentless-passive.js.map +1 -0
- package/dist/detectors/char-shape.d.ts +12 -0
- package/dist/detectors/char-shape.d.ts.map +1 -0
- package/dist/detectors/char-shape.js +39 -0
- package/dist/detectors/char-shape.js.map +1 -0
- package/dist/detectors/en-shape.d.ts +19 -0
- package/dist/detectors/en-shape.d.ts.map +1 -0
- package/dist/detectors/en-shape.js +176 -0
- package/dist/detectors/en-shape.js.map +1 -0
- package/dist/detectors/index.d.ts.map +1 -1
- package/dist/detectors/index.js +37 -0
- package/dist/detectors/index.js.map +1 -1
- package/dist/detectors/lexicon.d.ts +15 -0
- package/dist/detectors/lexicon.d.ts.map +1 -0
- package/dist/detectors/lexicon.js +109 -0
- package/dist/detectors/lexicon.js.map +1 -0
- package/dist/detectors/repeated-head.d.ts.map +1 -1
- package/dist/detectors/repeated-head.js +17 -5
- package/dist/detectors/repeated-head.js.map +1 -1
- package/dist/detectors/sentence-ending.d.ts +3 -0
- package/dist/detectors/sentence-ending.d.ts.map +1 -0
- package/dist/detectors/sentence-ending.js +34 -0
- package/dist/detectors/sentence-ending.js.map +1 -0
- package/dist/detectors/signals.d.ts +6 -0
- package/dist/detectors/signals.d.ts.map +1 -0
- package/dist/detectors/signals.js +183 -0
- package/dist/detectors/signals.js.map +1 -0
- package/dist/detectors/structure.d.ts +24 -0
- package/dist/detectors/structure.d.ts.map +1 -0
- package/dist/detectors/structure.js +111 -0
- package/dist/detectors/structure.js.map +1 -0
- package/dist/detectors/team.d.ts +17 -0
- package/dist/detectors/team.d.ts.map +1 -0
- package/dist/detectors/team.js +87 -0
- package/dist/detectors/team.js.map +1 -0
- package/dist/detectors/token-shape.d.ts +4 -0
- package/dist/detectors/token-shape.d.ts.map +1 -0
- package/dist/detectors/token-shape.js +62 -0
- package/dist/detectors/token-shape.js.map +1 -0
- package/dist/document.d.ts +11 -1
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +85 -10
- package/dist/document.js.map +1 -1
- package/dist/env.d.ts +12 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +20 -0
- package/dist/env.js.map +1 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +9 -6
- package/dist/init.js.map +1 -1
- package/dist/judge.d.ts +14 -16
- package/dist/judge.d.ts.map +1 -1
- package/dist/judge.js +0 -0
- package/dist/judge.js.map +1 -1
- package/dist/levels.d.ts +1 -9
- package/dist/levels.d.ts.map +1 -1
- package/dist/levels.js +15 -3
- package/dist/levels.js.map +1 -1
- package/dist/look-at.d.ts +18 -0
- package/dist/look-at.d.ts.map +1 -0
- package/dist/look-at.js +43 -0
- package/dist/look-at.js.map +1 -0
- package/dist/plugin.d.ts +47 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/render/explain.d.ts +1 -1
- package/dist/render/explain.d.ts.map +1 -1
- package/dist/render/explain.js +12 -4
- package/dist/render/explain.js.map +1 -1
- package/dist/render/rules-json.d.ts.map +1 -1
- package/dist/render/rules-json.js +13 -5
- package/dist/render/rules-json.js.map +1 -1
- package/dist/render/semantic.d.ts +2 -1
- package/dist/render/semantic.d.ts.map +1 -1
- package/dist/render/semantic.js +35 -0
- package/dist/render/semantic.js.map +1 -1
- package/dist/rule-load.d.ts.map +1 -1
- package/dist/rule-load.js +27 -1
- package/dist/rule-load.js.map +1 -1
- package/dist/run-semantic.d.ts +22 -1
- package/dist/run-semantic.d.ts.map +1 -1
- package/dist/run-semantic.js +21 -10
- package/dist/run-semantic.js.map +1 -1
- package/dist/run.d.ts +6 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +34 -2
- package/dist/run.js.map +1 -1
- package/dist/sentence-shape.d.ts +6 -0
- package/dist/sentence-shape.d.ts.map +1 -0
- package/dist/sentence-shape.js +15 -0
- package/dist/sentence-shape.js.map +1 -0
- package/package.json +4 -3
- package/rules/adverb-overuse.yaml +28 -0
- package/rules/agentless-passive.yaml +28 -0
- package/rules/ai-tell.yaml +27 -0
- package/rules/concrete-evidence-density.yaml +25 -0
- package/rules/cushion-phrase-density.yaml +26 -0
- package/rules/double-keigo.yaml +27 -0
- package/rules/emoji-density.yaml +26 -0
- package/rules/excessive-hedging.yaml +26 -0
- package/rules/expletive-construction.yaml +27 -0
- package/rules/hiragana-fukushi.yaml +27 -0
- package/rules/internal-jargon.yaml +27 -0
- package/rules/max-kanji-continuous.yaml +26 -0
- package/rules/max-paragraph-length.yaml +25 -0
- package/rules/max-sentence-length.yaml +22 -0
- package/rules/ngram-repetition.yaml +25 -0
- package/rules/no-doubled-joshi.yaml +28 -0
- package/rules/no-mixed-desumasu.yaml +29 -0
- package/rules/no-nakaguro-parallel.yaml +27 -0
- package/rules/oxford-comma-consistency.yaml +26 -0
- package/rules/paragraph-length-variance.yaml +25 -0
- package/rules/preamble-length.yaml +25 -0
- package/rules/proper-noun-density.yaml +26 -0
- package/rules/repeated-conjunction.yaml +26 -0
- package/rules/required-sections.yaml +26 -0
- package/rules/rule-of-three.yaml +25 -0
- package/rules/sasete-itadaku.yaml +27 -0
- package/rules/section-length-uniformity.yaml +25 -0
- package/rules/sentence-initial-conjunction-run.yaml +27 -0
- package/rules/taigen-dome-in-prose.yaml +27 -0
- package/rules/title-case-consistency.yaml +26 -0
- package/rules/undefined-acronym.yaml +25 -0
- package/rules/unqualified-superlative.yaml +26 -0
- package/src/backends/anthropic.ts +61 -0
- package/src/backends/openai.ts +46 -0
- package/src/backends/types.ts +38 -0
- package/src/cli.ts +9 -5
- package/src/commands/eval.ts +5 -2
- package/src/commands/test.ts +74 -16
- package/src/config/load.ts +22 -3
- package/src/detectors/agentless-passive.ts +34 -0
- package/src/detectors/char-shape.ts +47 -0
- package/src/detectors/en-shape.ts +187 -0
- package/src/detectors/index.ts +37 -0
- package/src/detectors/lexicon.ts +124 -0
- package/src/detectors/repeated-head.ts +17 -6
- package/src/detectors/sentence-ending.ts +37 -0
- package/src/detectors/signals.ts +199 -0
- package/src/detectors/structure.ts +119 -0
- package/src/detectors/team.ts +90 -0
- package/src/detectors/token-shape.ts +81 -0
- package/src/document.ts +95 -11
- package/src/env.ts +20 -0
- package/src/init.ts +9 -6
- package/src/judge.ts +0 -0
- package/src/levels.ts +16 -4
- package/src/look-at.ts +53 -0
- package/src/plugin.ts +48 -4
- package/src/render/explain.ts +12 -4
- package/src/render/rules-json.ts +16 -5
- package/src/render/semantic.ts +38 -1
- package/src/rule-load.ts +28 -1
- package/src/run-semantic.ts +32 -11
- package/src/run.ts +36 -3
- package/src/sentence-shape.ts +22 -0
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
const HEAD_CHARS = 6;
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
const HEAD_WORDS = 3;
|
|
3
|
+
/**
|
|
4
|
+
* 書き出しの「同じさ」を測る単位は言語で違う。日本語は文字、英語は語。
|
|
5
|
+
* 文字で切ると "We continued" が "Wecont" になり、引用がそのまま読み手に出る。
|
|
6
|
+
*/
|
|
7
|
+
const headOf = (sentence, unit) => {
|
|
8
|
+
const text = sentence.text.trim();
|
|
9
|
+
if (unit === "char")
|
|
10
|
+
return text.replace(/\s+/gu, "").slice(0, HEAD_CHARS);
|
|
11
|
+
return text.split(/\s+/u).slice(0, HEAD_WORDS).join(" ");
|
|
12
|
+
};
|
|
13
|
+
const runsOf = (sentences, unit) => sentences.reduce((acc, sentence) => {
|
|
14
|
+
const head = headOf(sentence, unit);
|
|
5
15
|
const last = acc.at(-1);
|
|
6
|
-
|
|
16
|
+
// 短すぎる書き出しは「同じ」と言えない。単位ぶん揃って初めて連なりと見る。
|
|
17
|
+
const full = unit === "char" ? head.length === HEAD_CHARS : head.split(" ").length === HEAD_WORDS;
|
|
18
|
+
if (last !== undefined && last.head === head && full) {
|
|
7
19
|
return [...acc.slice(0, -1), { head, members: [...last.members, sentence] }];
|
|
8
20
|
}
|
|
9
21
|
return [...acc, { head, members: [sentence] }];
|
|
10
22
|
}, []);
|
|
11
|
-
export const repeatedHead = (doc, options) => runsOf(doc.sentences)
|
|
23
|
+
export const repeatedHead = (doc, options) => runsOf(doc.sentences, doc.lengthUnit)
|
|
12
24
|
.filter((run) => run.members.length > options.limit)
|
|
13
25
|
.map((run) => ({
|
|
14
26
|
rule: "repeated-sentence-head",
|
|
@@ -1 +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,
|
|
1
|
+
{"version":3,"file":"repeated-head.js","sourceRoot":"","sources":["../../src/detectors/repeated-head.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAAG,CAAC,CAAC;AACrB,MAAM,UAAU,GAAG,CAAC,CAAC;AAErB;;;GAGG;AACH,MAAM,MAAM,GAAG,CAAC,QAAkB,EAAE,IAAgB,EAAU,EAAE;IAC9D,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAClC,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAC3E,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3D,CAAC,CAAC;AAIF,MAAM,MAAM,GAAG,CAAC,SAA8B,EAAE,IAAgB,EAAS,EAAE,CACzE,SAAS,CAAC,MAAM,CAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACxB,uCAAuC;IACvC,MAAM,IAAI,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC;IAClG,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,EAAE,CAAC;QACrD,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,EAAE,GAAG,CAAC,UAAU,CAAC;KAClC,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 @@
|
|
|
1
|
+
{"version":3,"file":"sentence-ending.d.ts","sourceRoot":"","sources":["../../src/detectors/sentence-ending.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,cAAc,CAAC;AAiBhE,eAAO,MAAM,cAAc,EAAE,QAkB5B,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { hasPredicate, isClosed } from "../sentence-shape.js";
|
|
2
|
+
/**
|
|
3
|
+
* 文末の調子が混ざっているかを見る。日本語の「ですます / である」がこれ。
|
|
4
|
+
*
|
|
5
|
+
* どちらが正しいかは決めない。決めると、方針の違う書き手にそのまま無視される。
|
|
6
|
+
* 見るのは文書の中での一貫性だけで、少数派のほうを指摘する。spec §12.3 の方針を日本語にも。
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* 述語を持たないものは文として数えない。見出しの下の `MaaSサービス` のような
|
|
10
|
+
* 名前だけの行がそのまま「である調」の少数派になり、実文書の誤検知はすべてこれだった。
|
|
11
|
+
*/
|
|
12
|
+
const isSentence = (sentence) => isClosed(sentence) && hasPredicate(sentence);
|
|
13
|
+
const isPolite = (sentence, polite) => (sentence.tokens ?? []).some((token) => polite.includes(token.lemma ?? token.surface));
|
|
14
|
+
export const sentenceEnding = (doc, options) => {
|
|
15
|
+
const polite = (options.lexicon ?? []).map((entry) => entry.pattern);
|
|
16
|
+
const judged = doc.sentences.filter(isSentence).map((sentence) => ({ sentence, polite: isPolite(sentence, polite) }));
|
|
17
|
+
const counts = { polite: judged.filter((entry) => entry.polite).length, plain: judged.length - judged.filter((entry) => entry.polite).length };
|
|
18
|
+
const minorityIsPolite = counts.polite <= counts.plain;
|
|
19
|
+
const few = minorityIsPolite ? counts.polite : counts.plain;
|
|
20
|
+
// 片方しか無ければ一貫している。少数派が閾値を超えて多ければ、混在ではなく別の文体。
|
|
21
|
+
if (few === 0 || few > options.limit)
|
|
22
|
+
return [];
|
|
23
|
+
return judged
|
|
24
|
+
.filter((entry) => entry.polite === minorityIsPolite)
|
|
25
|
+
.map(({ sentence }) => ({
|
|
26
|
+
rule: "no-mixed-desumasu",
|
|
27
|
+
severity: "warning",
|
|
28
|
+
line: 0,
|
|
29
|
+
column: 0,
|
|
30
|
+
quote: sentence.text.trim(),
|
|
31
|
+
values: { count: few, limit: options.limit, offset: sentence.span.start },
|
|
32
|
+
}));
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=sentence-ending.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentence-ending.js","sourceRoot":"","sources":["../../src/detectors/sentence-ending.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAG9D;;;;;GAKG;AACH;;;GAGG;AACH,MAAM,UAAU,GAAG,CAAC,QAAkB,EAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC;AAEjG,MAAM,QAAQ,GAAG,CAAC,QAAkB,EAAE,MAAyB,EAAW,EAAE,CAC1E,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;AAEzF,MAAM,CAAC,MAAM,cAAc,GAAa,CAAC,GAAG,EAAE,OAAO,EAAa,EAAE;IAClE,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IACtH,MAAM,MAAM,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC;IAC/I,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC;IACvD,MAAM,GAAG,GAAG,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;IAC5D,4CAA4C;IAC5C,IAAI,GAAG,KAAK,CAAC,IAAI,GAAG,GAAG,OAAO,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAChD,OAAO,MAAM;SACV,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,gBAAgB,CAAC;SACpD,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QACtB,IAAI,EAAE,mBAAmB;QACzB,QAAQ,EAAE,SAAS;QACnB,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;QACT,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE;QAC3B,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE;KAC1E,CAAC,CAAC,CAAC;AACR,CAAC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Detector } from "../plugin.ts";
|
|
2
|
+
export declare const emojiDensity: Detector;
|
|
3
|
+
export declare const ngramRepetition: Detector;
|
|
4
|
+
export declare const undefinedAcronym: Detector;
|
|
5
|
+
export declare const concreteEvidence: Detector;
|
|
6
|
+
//# sourceMappingURL=signals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../../src/detectors/signals.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAoC,MAAM,cAAc,CAAC;AA4B/E,eAAO,MAAM,YAAY,EAAE,QAe1B,CAAC;AAwCF,eAAO,MAAM,eAAe,EAAE,QAiB7B,CAAC;AAqDF,eAAO,MAAM,gBAAgB,EAAE,QAiB9B,CAAC;AAQF,eAAO,MAAM,gBAAgB,EAAE,QAkB9B,CAAC"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { proseText } from "../measure.js";
|
|
2
|
+
import { wordsOf } from "./structure.js";
|
|
3
|
+
const PER = 1000;
|
|
4
|
+
/**
|
|
5
|
+
* これより短い文書では密度が暴れる。1 個で「1000 あたり 100」になる。
|
|
6
|
+
* 単位は言語で違うので、床も分ける。英語の 200 語と日本語の 200 文字では長さが桁で違う。
|
|
7
|
+
*/
|
|
8
|
+
const FLOOR = { word: 200, char: 500 };
|
|
9
|
+
const bodyOf = (doc) => doc.sentences.map(proseText).join(" ");
|
|
10
|
+
const findIn = (doc, pattern) => doc.sentences.flatMap((sentence) => [...sentence.text.matchAll(pattern)].map((match) => ({ sentence, offset: sentence.span.start + (match.index ?? 0) })));
|
|
11
|
+
const density = (doc, count) => {
|
|
12
|
+
const length = wordsOf(doc);
|
|
13
|
+
return length === 0 ? 0 : Math.round((count / length) * PER);
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* 絵文字と装飾記号。1 つなら親しみだが、並ぶと文字そのものが読めなくなる。
|
|
17
|
+
* 件数ではなく密度で見る。長い文書ほど当たるのを避けるため(bold-density と同じ)。
|
|
18
|
+
*/
|
|
19
|
+
const EMOJI = /\p{Extended_Pictographic}|[←-⇿☀-➿]/gu;
|
|
20
|
+
export const emojiDensity = (doc, options) => {
|
|
21
|
+
const hits = findIn(doc, EMOJI);
|
|
22
|
+
const first = hits[0];
|
|
23
|
+
const rate = density(doc, hits.length);
|
|
24
|
+
if (wordsOf(doc) < FLOOR[doc.lengthUnit] || first === undefined || rate <= options.limit)
|
|
25
|
+
return [];
|
|
26
|
+
return [
|
|
27
|
+
{
|
|
28
|
+
rule: "emoji-density",
|
|
29
|
+
severity: "info",
|
|
30
|
+
line: 0,
|
|
31
|
+
column: 0,
|
|
32
|
+
quote: first.sentence.text.trim(),
|
|
33
|
+
values: { count: hits.length, density: rate, limit: options.limit, offset: first.offset },
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* 同じ言い回しの繰り返し。character n-gram で見るので、語の区切りが要らない。
|
|
39
|
+
* spec §10 が n-gram を選んだのはこのためで、新しい言語でそのまま動く。
|
|
40
|
+
*/
|
|
41
|
+
const GRAM = 8;
|
|
42
|
+
/** 短い n-gram は偶然一致する。8 文字は「〜することができます」のような言い回しの長さ。 */
|
|
43
|
+
const countGrams = (text) => {
|
|
44
|
+
const counts = new Map();
|
|
45
|
+
Array.from({ length: Math.max(0, text.length - GRAM + 1) }).forEach((_, index) => {
|
|
46
|
+
const gram = text.slice(index, index + GRAM);
|
|
47
|
+
counts.set(gram, (counts.get(gram) ?? 0) + 1);
|
|
48
|
+
});
|
|
49
|
+
return counts;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* 名前ではなく言い回しだけを数える。実文書で測ったら、上位は
|
|
53
|
+
* 「AGENTS.m」「シンギュラリティ」のような固有名詞と識別子だった。名前は繰り返して当たり前。
|
|
54
|
+
*
|
|
55
|
+
* 言い回しは、その言語の「つなぎの материал」を含む。日本語ならひらがな、英語なら語の切れ目。
|
|
56
|
+
* どちらを見るかは adapter が宣言する単位で決める。ひらがなを持たない char 単位の言語が
|
|
57
|
+
* 来たら、この見分けは効かなくなる(そのときは adapter 側に判定を移す)。
|
|
58
|
+
*/
|
|
59
|
+
const CONNECTIVE = { char: /[ぁ-ゖ]/u, word: / /u };
|
|
60
|
+
const isPhrasing = (gram, unit) => CONNECTIVE[unit].test(gram);
|
|
61
|
+
/** 文をまたいで数えない。文の終わりと次の文の始まりが繋がると、固有名詞が言い回しに見える。 */
|
|
62
|
+
const gramsOf = (doc) => {
|
|
63
|
+
const counts = new Map();
|
|
64
|
+
doc.sentences.forEach((sentence) => {
|
|
65
|
+
const text = doc.lengthUnit === "char" ? proseText(sentence).replace(/\s+/gu, "") : proseText(sentence);
|
|
66
|
+
countGrams(text).forEach((count, gram) => counts.set(gram, (counts.get(gram) ?? 0) + count));
|
|
67
|
+
});
|
|
68
|
+
return counts;
|
|
69
|
+
};
|
|
70
|
+
export const ngramRepetition = (doc, options) => {
|
|
71
|
+
if (wordsOf(doc) < FLOOR[doc.lengthUnit])
|
|
72
|
+
return [];
|
|
73
|
+
const worst = [...gramsOf(doc).entries()]
|
|
74
|
+
.filter(([gram]) => isPhrasing(gram, doc.lengthUnit))
|
|
75
|
+
.reduce((best, [gram, count]) => (count > best.count ? { gram, count } : best), { gram: "", count: 0 });
|
|
76
|
+
if (worst.count <= options.limit)
|
|
77
|
+
return [];
|
|
78
|
+
const at = doc.sentences.find((sentence) => sentence.text.includes(worst.gram));
|
|
79
|
+
return [
|
|
80
|
+
{
|
|
81
|
+
rule: "ngram-repetition",
|
|
82
|
+
severity: "info",
|
|
83
|
+
line: 0,
|
|
84
|
+
column: 0,
|
|
85
|
+
quote: at?.text.trim() ?? worst.gram,
|
|
86
|
+
values: { word: worst.gram, count: worst.count, limit: options.limit, offset: at?.span.start ?? 0 },
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* 略語が初出で展開されているか。「CI」だけでは、読む人によって指すものが違う。
|
|
92
|
+
* 展開は「略語の直前か直後の括弧」か「括弧の中の略語」で書かれる。
|
|
93
|
+
*/
|
|
94
|
+
const ACRONYM = /\b[A-Z]{2,6}\b/gu;
|
|
95
|
+
/** 読み手が説明なしで通じると見なしてよい語。展開すると逆に読みにくい。 */
|
|
96
|
+
const COMMON = new Set([
|
|
97
|
+
"OK",
|
|
98
|
+
"NG",
|
|
99
|
+
"URL",
|
|
100
|
+
"API",
|
|
101
|
+
"CSS",
|
|
102
|
+
"HTML",
|
|
103
|
+
"JSON",
|
|
104
|
+
"YAML",
|
|
105
|
+
"HTTP",
|
|
106
|
+
"HTTPS",
|
|
107
|
+
"PDF",
|
|
108
|
+
"CPU",
|
|
109
|
+
"GPU",
|
|
110
|
+
"RAM",
|
|
111
|
+
"USB",
|
|
112
|
+
"AI",
|
|
113
|
+
"ID",
|
|
114
|
+
"FAQ",
|
|
115
|
+
"PR",
|
|
116
|
+
"OS",
|
|
117
|
+
"CLI",
|
|
118
|
+
"UI",
|
|
119
|
+
"UX",
|
|
120
|
+
"SQL",
|
|
121
|
+
"CSV",
|
|
122
|
+
"XML",
|
|
123
|
+
"TODO",
|
|
124
|
+
"NOTE",
|
|
125
|
+
]);
|
|
126
|
+
/**
|
|
127
|
+
* 展開は略語の**すぐ隣**にあるときだけ認める。
|
|
128
|
+
* 60 文字も見ると、同じ文のどこかに括弧があるだけで「説明済み」になり、1 件も出なくなる。
|
|
129
|
+
*/
|
|
130
|
+
const AFTER = /^\s*[((]/u;
|
|
131
|
+
const BEFORE = /[))]\s*$/u;
|
|
132
|
+
const isExpanded = (body, acronym) => {
|
|
133
|
+
const at = body.indexOf(acronym);
|
|
134
|
+
if (at === -1)
|
|
135
|
+
return false;
|
|
136
|
+
return AFTER.test(body.slice(at + acronym.length, at + acronym.length + 3)) || BEFORE.test(body.slice(Math.max(0, at - 3), at));
|
|
137
|
+
};
|
|
138
|
+
export const undefinedAcronym = (doc, options) => {
|
|
139
|
+
const body = bodyOf(doc);
|
|
140
|
+
const seen = new Map();
|
|
141
|
+
findIn(doc, ACRONYM).forEach((hit) => {
|
|
142
|
+
const text = /^[A-Z]{2,6}/u.exec(doc.source.slice(hit.offset, hit.offset + 6))?.[0] ?? "";
|
|
143
|
+
if (text.length > 0 && !COMMON.has(text) && !seen.has(text))
|
|
144
|
+
seen.set(text, hit);
|
|
145
|
+
});
|
|
146
|
+
const bare = [...seen.entries()].filter(([acronym]) => !isExpanded(body, acronym));
|
|
147
|
+
if (bare.length < options.limit)
|
|
148
|
+
return [];
|
|
149
|
+
return bare.map(([acronym, hit]) => ({
|
|
150
|
+
rule: "undefined-acronym",
|
|
151
|
+
severity: "info",
|
|
152
|
+
line: 0,
|
|
153
|
+
column: 0,
|
|
154
|
+
quote: hit.sentence.text.trim(),
|
|
155
|
+
values: { word: acronym, count: bare.length, limit: options.limit, offset: hit.offset },
|
|
156
|
+
}));
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* 具体物の密度。数値・コード・リンク・引用が 1 つも無い節は、
|
|
160
|
+
* 読み終えても何も持ち帰れない。**少ないほうを指摘する**ので、他の密度 rule と向きが逆。
|
|
161
|
+
*/
|
|
162
|
+
const CONCRETE = /\d|`|https?:\/\//u;
|
|
163
|
+
export const concreteEvidence = (doc, options) => {
|
|
164
|
+
const sections = doc.sections.filter((section) => section.sentences.length >= 3);
|
|
165
|
+
const empty = sections.filter((section) => !CONCRETE.test(doc.source.slice(section.span.start, section.span.end)));
|
|
166
|
+
if (empty.length < options.limit)
|
|
167
|
+
return [];
|
|
168
|
+
return empty.map((section) => ({
|
|
169
|
+
rule: "concrete-evidence-density",
|
|
170
|
+
severity: "info",
|
|
171
|
+
line: 0,
|
|
172
|
+
column: 0,
|
|
173
|
+
quote: section.heading.length > 0 ? section.heading : (section.sentences[0]?.text.trim() ?? ""),
|
|
174
|
+
values: {
|
|
175
|
+
word: section.heading.length > 0 ? section.heading : "この節",
|
|
176
|
+
count: empty.length,
|
|
177
|
+
total: sections.length,
|
|
178
|
+
limit: options.limit,
|
|
179
|
+
offset: section.span.start,
|
|
180
|
+
},
|
|
181
|
+
}));
|
|
182
|
+
};
|
|
183
|
+
//# sourceMappingURL=signals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.js","sourceRoot":"","sources":["../../src/detectors/signals.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Detector, ProseDocument } from "../plugin.ts";
|
|
2
|
+
/**
|
|
3
|
+
* ばらつきは変動係数(標準偏差 ÷ 平均)で測る。
|
|
4
|
+
*
|
|
5
|
+
* 標準偏差そのものを見ると、長い文書ほど大きくなって閾値が効かない。
|
|
6
|
+
* 平均で割ると長さに依らなくなり、「揃いすぎ」と「ばらつきすぎ」を同じ尺度で言える。
|
|
7
|
+
*/
|
|
8
|
+
export declare const coefficientOfVariation: (values: readonly number[]) => number | undefined;
|
|
9
|
+
/** 1 段落に文を詰めすぎると、読み手はどこで息継ぎしていいか分からなくなる。 */
|
|
10
|
+
export declare const paragraphLength: Detector;
|
|
11
|
+
/** 段落の長さが揃いすぎている。人が書くと、言いたいことの重さで段落の長さは揺れる。 */
|
|
12
|
+
export declare const paragraphVariance: Detector;
|
|
13
|
+
/** 節の長さが揃いすぎている。見出しごとに機械的に埋めた文書で起きる。 */
|
|
14
|
+
export declare const sectionUniformity: Detector;
|
|
15
|
+
export declare const ruleOfThree: Detector;
|
|
16
|
+
/**
|
|
17
|
+
* 本題に入るまでが長い。業務文書では、読み手は結論を探しに来ている。
|
|
18
|
+
*
|
|
19
|
+
* 「本題」は最初の中見出し(深さ 2 以上)とする。表題(深さ 1)の直後から数えると、
|
|
20
|
+
* 表題しか無い文書で全文が前置きになる。中見出しが無い文書では何も言わない。
|
|
21
|
+
*/
|
|
22
|
+
export declare const preambleLength: Detector;
|
|
23
|
+
export declare const wordsOf: (doc: ProseDocument) => number;
|
|
24
|
+
//# sourceMappingURL=structure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.d.ts","sourceRoot":"","sources":["../../src/detectors/structure.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,QAAQ,EAAsB,aAAa,EAAW,MAAM,cAAc,CAAC;AAErG;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GAAI,QAAQ,SAAS,MAAM,EAAE,KAAG,MAAM,GAAG,SAM3E,CAAC;AAsBF,4CAA4C;AAC5C,eAAO,MAAM,eAAe,EAAE,QAUvB,CAAC;AAIR,+CAA+C;AAC/C,eAAO,MAAM,iBAAiB,EAAE,QAI/B,CAAC;AAIF,wCAAwC;AACxC,eAAO,MAAM,iBAAiB,EAAE,QAI/B,CAAC;AAQF,eAAO,MAAM,WAAW,EAAE,QAgBzB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,QAkB5B,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,KAAK,aAAa,KAAG,MAA8F,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { charLength, lengthOf } from "../measure.js";
|
|
2
|
+
/**
|
|
3
|
+
* ばらつきは変動係数(標準偏差 ÷ 平均)で測る。
|
|
4
|
+
*
|
|
5
|
+
* 標準偏差そのものを見ると、長い文書ほど大きくなって閾値が効かない。
|
|
6
|
+
* 平均で割ると長さに依らなくなり、「揃いすぎ」と「ばらつきすぎ」を同じ尺度で言える。
|
|
7
|
+
*/
|
|
8
|
+
export const coefficientOfVariation = (values) => {
|
|
9
|
+
if (values.length < 2)
|
|
10
|
+
return undefined;
|
|
11
|
+
const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
12
|
+
if (mean === 0)
|
|
13
|
+
return undefined;
|
|
14
|
+
const variance = values.reduce((sum, value) => sum + (value - mean) ** 2, 0) / values.length;
|
|
15
|
+
return Math.sqrt(variance) / mean;
|
|
16
|
+
};
|
|
17
|
+
/** 変動係数は 0..1 では読めないので、パーセントの整数にして閾値と突き合わせる。 */
|
|
18
|
+
const PERCENT = 100;
|
|
19
|
+
const asPercent = (value) => Math.round(value * PERCENT);
|
|
20
|
+
const uniformity = (doc, values, span, limit, rule) => {
|
|
21
|
+
const cv = coefficientOfVariation(values);
|
|
22
|
+
if (cv === undefined || asPercent(cv) >= limit)
|
|
23
|
+
return [];
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
rule,
|
|
27
|
+
severity: "info",
|
|
28
|
+
line: 0,
|
|
29
|
+
column: 0,
|
|
30
|
+
quote: doc.source.slice(span.start, span.start + 80).trim(),
|
|
31
|
+
values: { count: values.length, variance: asPercent(cv), limit, offset: span.start },
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
};
|
|
35
|
+
/** 1 段落に文を詰めすぎると、読み手はどこで息継ぎしていいか分からなくなる。 */
|
|
36
|
+
export const paragraphLength = (doc, options) => doc.paragraphs
|
|
37
|
+
.filter((paragraph) => paragraph.sentences.length > options.limit)
|
|
38
|
+
.map((paragraph) => ({
|
|
39
|
+
rule: "max-paragraph-length",
|
|
40
|
+
severity: "warning",
|
|
41
|
+
line: 0,
|
|
42
|
+
column: 0,
|
|
43
|
+
quote: doc.source.slice(paragraph.span.start, paragraph.span.start + 80).trim(),
|
|
44
|
+
values: { count: paragraph.sentences.length, limit: options.limit, offset: paragraph.span.start },
|
|
45
|
+
}));
|
|
46
|
+
const charsOf = (paragraph) => paragraph.sentences.reduce((sum, sentence) => sum + charLength(sentence), 0);
|
|
47
|
+
/** 段落の長さが揃いすぎている。人が書くと、言いたいことの重さで段落の長さは揺れる。 */
|
|
48
|
+
export const paragraphVariance = (doc, options) => {
|
|
49
|
+
const lengths = doc.paragraphs.map(charsOf).filter((length) => length > 0);
|
|
50
|
+
const first = doc.paragraphs[0];
|
|
51
|
+
return first === undefined ? [] : uniformity(doc, lengths, first.span, options.limit, "paragraph-length-variance");
|
|
52
|
+
};
|
|
53
|
+
const sectionChars = (section) => section.sentences.reduce((sum, sentence) => sum + charLength(sentence), 0);
|
|
54
|
+
/** 節の長さが揃いすぎている。見出しごとに機械的に埋めた文書で起きる。 */
|
|
55
|
+
export const sectionUniformity = (doc, options) => {
|
|
56
|
+
const lengths = doc.sections.map(sectionChars).filter((length) => length > 0);
|
|
57
|
+
const first = doc.sections[0];
|
|
58
|
+
return first === undefined ? [] : uniformity(doc, lengths, first.span, options.limit, "section-length-uniformity");
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* 3 項目の箇条書きばかりになっている。
|
|
62
|
+
* 3 つ並べると収まりがよいので、言いたいことが 2 つでも 4 つでも 3 つにされる。
|
|
63
|
+
*/
|
|
64
|
+
const isThree = (list) => list.items.length === 3;
|
|
65
|
+
export const ruleOfThree = (doc, options) => {
|
|
66
|
+
const lists = doc.lists;
|
|
67
|
+
const threes = lists.filter(isThree);
|
|
68
|
+
const share = lists.length === 0 ? 0 : Math.round((threes.length / lists.length) * PERCENT);
|
|
69
|
+
const first = threes[0];
|
|
70
|
+
if (lists.length < 3 || first === undefined || share <= options.limit)
|
|
71
|
+
return [];
|
|
72
|
+
return [
|
|
73
|
+
{
|
|
74
|
+
rule: "rule-of-three",
|
|
75
|
+
severity: "info",
|
|
76
|
+
line: 0,
|
|
77
|
+
column: 0,
|
|
78
|
+
quote: doc.source.slice(first.span.start, first.span.start + 80).trim(),
|
|
79
|
+
values: { count: threes.length, total: lists.length, share, limit: options.limit, offset: first.span.start },
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* 本題に入るまでが長い。業務文書では、読み手は結論を探しに来ている。
|
|
85
|
+
*
|
|
86
|
+
* 「本題」は最初の中見出し(深さ 2 以上)とする。表題(深さ 1)の直後から数えると、
|
|
87
|
+
* 表題しか無い文書で全文が前置きになる。中見出しが無い文書では何も言わない。
|
|
88
|
+
*/
|
|
89
|
+
export const preambleLength = (doc, options) => {
|
|
90
|
+
const body = doc.sections.find((section) => section.depth >= 2);
|
|
91
|
+
if (body === undefined)
|
|
92
|
+
return [];
|
|
93
|
+
const before = doc.paragraphs.filter((paragraph) => paragraph.span.start < body.span.start);
|
|
94
|
+
if (before.length <= options.limit)
|
|
95
|
+
return [];
|
|
96
|
+
const first = before[0];
|
|
97
|
+
return first === undefined
|
|
98
|
+
? []
|
|
99
|
+
: [
|
|
100
|
+
{
|
|
101
|
+
rule: "preamble-length",
|
|
102
|
+
severity: "info",
|
|
103
|
+
line: 0,
|
|
104
|
+
column: 0,
|
|
105
|
+
quote: doc.source.slice(first.span.start, first.span.start + 80).trim(),
|
|
106
|
+
values: { count: before.length, limit: options.limit, offset: first.span.start },
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
};
|
|
110
|
+
export const wordsOf = (doc) => doc.sentences.reduce((sum, sentence) => sum + lengthOf(sentence, doc.lengthUnit), 0);
|
|
111
|
+
//# sourceMappingURL=structure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.js","sourceRoot":"","sources":["../../src/detectors/structure.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Detector } from "../plugin.ts";
|
|
2
|
+
export declare const internalJargon: Detector;
|
|
3
|
+
/**
|
|
4
|
+
* この種類の文書に無いと困る見出し。チームが chaff.yaml で決める。
|
|
5
|
+
*
|
|
6
|
+
* 「提案書にはリスクの節がある」のような取り決めは、組織ごとに違う。
|
|
7
|
+
* chaff は必須の見出しを持たず、**書かれた取り決めを守れているかだけ**を見る。
|
|
8
|
+
*/
|
|
9
|
+
export declare const requiredSections: Detector;
|
|
10
|
+
/**
|
|
11
|
+
* 固有名詞の密度。製品名・社名・人名が続くと、知らない読者は文の骨組みを見失う。
|
|
12
|
+
*
|
|
13
|
+
* spec は未知語率としていたが、**品詞解析があれば PROPN を数えるだけで足りる**。
|
|
14
|
+
* 辞書を別に持つ必要はない。
|
|
15
|
+
*/
|
|
16
|
+
export declare const properNounDensity: Detector;
|
|
17
|
+
//# sourceMappingURL=team.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team.d.ts","sourceRoot":"","sources":["../../src/detectors/team.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAA4B,MAAM,cAAc,CAAC;AAcvE,eAAO,MAAM,cAAc,EAAE,QAgB5B,CAAC;AAIF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAgB9B,CAAC;AASF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAgB/B,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { proseText } from "../measure.js";
|
|
2
|
+
import { wordsOf } from "./structure.js";
|
|
3
|
+
/**
|
|
4
|
+
* チームの言葉。社内でしか通じない語を、チームが chaff.yaml に自分で並べる。
|
|
5
|
+
*
|
|
6
|
+
* 何が社内用語かは組織ごとに違うので、chaff は語を持たない。並べる場所だけを用意する。
|
|
7
|
+
* 語彙表が空なら何も言わない(「用語を登録してください」とも言わない。押しつけない)。
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* 表層でも原形でも当てる。利用者は辞書形で書く(「握る」)が、本文は活用している(「握った」)。
|
|
11
|
+
* 品詞解析があれば原形で当たり、無くても表層で当たる。要求はしない。
|
|
12
|
+
*/
|
|
13
|
+
const matches = (sentence, word) => sentence.text.includes(word) || (sentence.tokens ?? []).some((token) => token.lemma === word);
|
|
14
|
+
export const internalJargon = (doc, options) => {
|
|
15
|
+
const words = (options.lexicon ?? []).map((entry) => entry.pattern);
|
|
16
|
+
if (words.length === 0)
|
|
17
|
+
return [];
|
|
18
|
+
const hits = doc.sentences.flatMap((sentence) => {
|
|
19
|
+
const matched = words.find((word) => matches(sentence, word));
|
|
20
|
+
return matched === undefined ? [] : [{ sentence, matched }];
|
|
21
|
+
});
|
|
22
|
+
if (hits.length < options.limit)
|
|
23
|
+
return [];
|
|
24
|
+
return hits.map((hit) => ({
|
|
25
|
+
rule: "internal-jargon",
|
|
26
|
+
severity: "warning",
|
|
27
|
+
line: 0,
|
|
28
|
+
column: 0,
|
|
29
|
+
quote: hit.sentence.text.trim(),
|
|
30
|
+
values: { matched: hit.matched, count: hits.length, limit: options.limit, offset: hit.sentence.span.start },
|
|
31
|
+
}));
|
|
32
|
+
};
|
|
33
|
+
const has = (headings, wanted) => headings.some((heading) => heading.includes(wanted));
|
|
34
|
+
/**
|
|
35
|
+
* この種類の文書に無いと困る見出し。チームが chaff.yaml で決める。
|
|
36
|
+
*
|
|
37
|
+
* 「提案書にはリスクの節がある」のような取り決めは、組織ごとに違う。
|
|
38
|
+
* chaff は必須の見出しを持たず、**書かれた取り決めを守れているかだけ**を見る。
|
|
39
|
+
*/
|
|
40
|
+
export const requiredSections = (doc, options) => {
|
|
41
|
+
const wanted = doc.requiredSections;
|
|
42
|
+
if (wanted.length === 0)
|
|
43
|
+
return [];
|
|
44
|
+
const headings = doc.sections.map((section) => section.heading);
|
|
45
|
+
const missing = wanted.filter((name) => !has(headings, name));
|
|
46
|
+
if (missing.length < options.limit)
|
|
47
|
+
return [];
|
|
48
|
+
return [
|
|
49
|
+
{
|
|
50
|
+
rule: "required-sections",
|
|
51
|
+
severity: "error",
|
|
52
|
+
line: 0,
|
|
53
|
+
column: 0,
|
|
54
|
+
quote: headings.filter((heading) => heading.length > 0).join(" / "),
|
|
55
|
+
values: { word: missing.join("、"), count: missing.length, limit: options.limit, offset: 0 },
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
};
|
|
59
|
+
const PER = 1000;
|
|
60
|
+
/** 密度を見る rule は短い文書を測らない。単位は言語で違うので床も分ける。 */
|
|
61
|
+
const FLOOR = { word: 200, char: 500 };
|
|
62
|
+
const isProperNoun = (token) => token.pos === "PROPN";
|
|
63
|
+
/**
|
|
64
|
+
* 固有名詞の密度。製品名・社名・人名が続くと、知らない読者は文の骨組みを見失う。
|
|
65
|
+
*
|
|
66
|
+
* spec は未知語率としていたが、**品詞解析があれば PROPN を数えるだけで足りる**。
|
|
67
|
+
* 辞書を別に持つ必要はない。
|
|
68
|
+
*/
|
|
69
|
+
export const properNounDensity = (doc, options) => {
|
|
70
|
+
const hits = doc.sentences.flatMap((sentence) => (sentence.tokens ?? []).filter(isProperNoun).map((token) => ({ sentence, token })));
|
|
71
|
+
const length = wordsOf(doc);
|
|
72
|
+
const rate = length === 0 ? 0 : Math.round((hits.length / length) * PER);
|
|
73
|
+
const first = hits[0];
|
|
74
|
+
if (length < FLOOR[doc.lengthUnit] || first === undefined || rate <= options.limit)
|
|
75
|
+
return [];
|
|
76
|
+
return [
|
|
77
|
+
{
|
|
78
|
+
rule: "proper-noun-density",
|
|
79
|
+
severity: "info",
|
|
80
|
+
line: 0,
|
|
81
|
+
column: 0,
|
|
82
|
+
quote: proseText(first.sentence),
|
|
83
|
+
values: { count: hits.length, density: rate, limit: options.limit, offset: first.token.span.start },
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=team.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team.js","sourceRoot":"","sources":["../../src/detectors/team.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-shape.d.ts","sourceRoot":"","sources":["../../src/detectors/token-shape.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAwB,MAAM,cAAc,CAAC;AAanE,eAAO,MAAM,UAAU,EAAE,QAmBxB,CAAC;AAiCF,eAAO,MAAM,eAAe,EAAE,QAc7B,CAAC"}
|