chaffjs 0.26.0 → 0.27.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 (97) hide show
  1. package/dist/ai-score/human-shares.js +59 -59
  2. package/dist/ai-score/human-shares.js.map +1 -1
  3. package/dist/capitals-with-small.d.ts +8 -0
  4. package/dist/capitals-with-small.d.ts.map +1 -0
  5. package/dist/capitals-with-small.js +20 -0
  6. package/dist/capitals-with-small.js.map +1 -0
  7. package/dist/derived/relative-weekdays.d.ts +16 -0
  8. package/dist/derived/relative-weekdays.d.ts.map +1 -0
  9. package/dist/derived/relative-weekdays.js +52 -0
  10. package/dist/derived/relative-weekdays.js.map +1 -0
  11. package/dist/detectors/change-rate.d.ts +3 -0
  12. package/dist/detectors/change-rate.d.ts.map +1 -0
  13. package/dist/detectors/change-rate.js +125 -0
  14. package/dist/detectors/change-rate.js.map +1 -0
  15. package/dist/detectors/derived-numbers.d.ts.map +1 -1
  16. package/dist/detectors/derived-numbers.js +3 -7
  17. package/dist/detectors/derived-numbers.js.map +1 -1
  18. package/dist/detectors/document-date.d.ts +4 -0
  19. package/dist/detectors/document-date.d.ts.map +1 -0
  20. package/dist/detectors/document-date.js +12 -0
  21. package/dist/detectors/document-date.js.map +1 -0
  22. package/dist/detectors/orthographic-variant.d.ts.map +1 -1
  23. package/dist/detectors/orthographic-variant.js +16 -11
  24. package/dist/detectors/orthographic-variant.js.map +1 -1
  25. package/dist/detectors/registry/change-rate.d.ts +3 -0
  26. package/dist/detectors/registry/change-rate.d.ts.map +1 -0
  27. package/dist/detectors/registry/change-rate.js +3 -0
  28. package/dist/detectors/registry/change-rate.js.map +1 -0
  29. package/dist/detectors/registry/version-mismatch.d.ts +3 -0
  30. package/dist/detectors/registry/version-mismatch.d.ts.map +1 -0
  31. package/dist/detectors/registry/version-mismatch.js +3 -0
  32. package/dist/detectors/registry/version-mismatch.js.map +1 -0
  33. package/dist/detectors/relative-dates.d.ts.map +1 -1
  34. package/dist/detectors/relative-dates.js +3 -1
  35. package/dist/detectors/relative-dates.js.map +1 -1
  36. package/dist/detectors/relative-day-words.d.ts +4 -0
  37. package/dist/detectors/relative-day-words.d.ts.map +1 -0
  38. package/dist/detectors/relative-day-words.js +105 -0
  39. package/dist/detectors/relative-day-words.js.map +1 -0
  40. package/dist/detectors/structure-tree.d.ts +4 -1
  41. package/dist/detectors/structure-tree.d.ts.map +1 -1
  42. package/dist/detectors/structure-tree.js +65 -3
  43. package/dist/detectors/structure-tree.js.map +1 -1
  44. package/dist/detectors/version-mismatch.d.ts +3 -0
  45. package/dist/detectors/version-mismatch.d.ts.map +1 -0
  46. package/dist/detectors/version-mismatch.js +19 -0
  47. package/dist/detectors/version-mismatch.js.map +1 -0
  48. package/dist/structure/change-rate.d.ts +51 -0
  49. package/dist/structure/change-rate.d.ts.map +1 -0
  50. package/dist/structure/change-rate.js +171 -0
  51. package/dist/structure/change-rate.js.map +1 -0
  52. package/dist/structure/list-numbering.d.ts +17 -0
  53. package/dist/structure/list-numbering.d.ts.map +1 -0
  54. package/dist/structure/list-numbering.js +27 -0
  55. package/dist/structure/list-numbering.js.map +1 -0
  56. package/dist/structure/percent-sum-prose.d.ts +7 -0
  57. package/dist/structure/percent-sum-prose.d.ts.map +1 -0
  58. package/dist/structure/percent-sum-prose.js +44 -0
  59. package/dist/structure/percent-sum-prose.js.map +1 -0
  60. package/dist/structure/version-mismatch.d.ts +23 -0
  61. package/dist/structure/version-mismatch.d.ts.map +1 -0
  62. package/dist/structure/version-mismatch.js +74 -0
  63. package/dist/structure/version-mismatch.js.map +1 -0
  64. package/dist/structure/weekday.d.ts +21 -2
  65. package/dist/structure/weekday.d.ts.map +1 -1
  66. package/dist/structure/weekday.js +70 -8
  67. package/dist/structure/weekday.js.map +1 -1
  68. package/package.json +3 -3
  69. package/rules/agreement-slip.yaml +2 -2
  70. package/rules/change-rate-mismatch.yaml +44 -0
  71. package/rules/date-weekday-mismatch.yaml +4 -4
  72. package/rules/max-sentence-length.yaml +1 -0
  73. package/rules/numbering-gap.yaml +4 -4
  74. package/rules/one-sentence-paragraph-run.yaml +1 -0
  75. package/rules/orthographic-variant.yaml +3 -3
  76. package/rules/percent-sum-mismatch.yaml +4 -4
  77. package/rules/relative-date-mismatch.yaml +4 -4
  78. package/rules/undefined-acronym.yaml +1 -0
  79. package/rules/version-mismatch.yaml +77 -0
  80. package/src/ai-score/human-shares.ts +59 -59
  81. package/src/capitals-with-small.ts +23 -0
  82. package/src/derived/relative-weekdays.ts +65 -0
  83. package/src/detectors/change-rate.ts +153 -0
  84. package/src/detectors/derived-numbers.ts +3 -11
  85. package/src/detectors/document-date.ts +18 -0
  86. package/src/detectors/orthographic-variant.ts +8 -4
  87. package/src/detectors/registry/change-rate.ts +4 -0
  88. package/src/detectors/registry/version-mismatch.ts +4 -0
  89. package/src/detectors/relative-dates.ts +3 -1
  90. package/src/detectors/relative-day-words.ts +126 -0
  91. package/src/detectors/structure-tree.ts +76 -4
  92. package/src/detectors/version-mismatch.ts +23 -0
  93. package/src/structure/change-rate.ts +223 -0
  94. package/src/structure/list-numbering.ts +34 -0
  95. package/src/structure/percent-sum-prose.ts +54 -0
  96. package/src/structure/version-mismatch.ts +103 -0
  97. package/src/structure/weekday.ts +83 -6
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-mismatch.d.ts","sourceRoot":"","sources":["../../src/detectors/version-mismatch.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAW,MAAM,cAAc,CAAC;AAKtD,eAAO,MAAM,eAAe,EAAE,QAc7B,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { versionMismatches } from "../structure/version-mismatch.js";
2
+ import { codeFences } from "./code-fences.js";
3
+ import { quoteAt } from "./structure-tree.js";
4
+ export const versionMismatch = (doc) => {
5
+ const words = {
6
+ ranges: (doc.lexicons["version-range"] ?? []).map((entry) => ({ word: entry.pattern, position: entry.position ?? "before" })),
7
+ versionWords: (doc.lexicons["version-lead"] ?? []).map((entry) => entry.pattern),
8
+ };
9
+ const blocks = codeFences(doc.source).map((fence) => ({ start: fence.start, code: fence.lines.join("\n") }));
10
+ return versionMismatches(doc.source, doc.prose ?? doc.source, blocks, words).map((issue) => ({
11
+ rule: "version-mismatch",
12
+ severity: "warning",
13
+ line: 0,
14
+ column: 0,
15
+ quote: quoteAt(doc.source, issue.offset),
16
+ values: { ...issue.values, offset: issue.offset },
17
+ }));
18
+ };
19
+ //# sourceMappingURL=version-mismatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-mismatch.js","sourceRoot":"","sources":["../../src/detectors/version-mismatch.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAqC,MAAM,kCAAkC,CAAC;AACxG,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C,MAAM,CAAC,MAAM,eAAe,GAAa,CAAC,GAAG,EAAa,EAAE;IAC1D,MAAM,KAAK,GAAiB;QAC1B,MAAM,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAa,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC;QACxI,YAAY,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;KACjF,CAAC;IACF,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7G,OAAO,iBAAiB,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC3F,IAAI,EAAE,kBAAkB;QACxB,QAAQ,EAAE,SAAS;QACnB,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;QACT,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;QACxC,MAAM,EAAE,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE;KAClD,CAAC,CAAC,CAAC;AACN,CAAC,CAAC"}
@@ -0,0 +1,51 @@
1
+ import type { Span } from "../plugin.ts";
2
+ import type { StructureIssue } from "./issues.ts";
3
+ /** A value with its unit as the sentence compares it (円, 社, $, companies) and the step it is written to (1 for 1,200; 1億 for 12億). */
4
+ export type Figure = Span & {
5
+ readonly value: number;
6
+ readonly unit: string;
7
+ readonly step: number;
8
+ };
9
+ /** A percentage, with the number of decimals written. */
10
+ export type Rate = Span & {
11
+ readonly value: number;
12
+ readonly decimals: number;
13
+ };
14
+ /** A word that says which way the value moved (増加, up: 1; 減少, down: -1). */
15
+ export type Direction = Span & {
16
+ readonly sign: 1 | -1;
17
+ };
18
+ /** A word that marks a value: the earlier one after it (「1,000社から」) or before it ("from 1,000"), the later one (「1,200社に」, "to 1,200"). */
19
+ export type BaseMark = Span & {
20
+ readonly position: "before" | "after";
21
+ };
22
+ /** A calendar year written in the sentence (「2025年」, "in 2025"). */
23
+ export type Period = Span & {
24
+ readonly year: number;
25
+ };
26
+ /** A word that starts another subject or clause; one that joins two values ("… in 2025 and …") is read only before the rate. */
27
+ export type Break = Span & {
28
+ readonly beforeRateOnly: boolean;
29
+ };
30
+ export type ChangeText = {
31
+ readonly sentences: readonly Span[];
32
+ readonly figures: readonly Figure[];
33
+ readonly rates: readonly Rate[];
34
+ readonly directions: readonly Direction[];
35
+ readonly marks: readonly BaseMark[];
36
+ /** Words that mark the later value ("to", 「に」). */
37
+ readonly targets: readonly BaseMark[];
38
+ readonly periods: readonly Period[];
39
+ /** Words that start another subject or clause (「利益は」, "and", ";"): a rate after one is not about the values before it. */
40
+ readonly breaks: readonly Break[];
41
+ /** The document's text, which the gaps of a pair are measured in. */
42
+ readonly source: string;
43
+ };
44
+ /** The length of a gap, a Latin word counting as one character: "an increase of" is as short as 「前年比」. */
45
+ export declare const gapLength: (source: string, gap: Span) => number;
46
+ /**
47
+ * Each sentence with one rate of change and its direction, and the two values it is computed from, that disagree: the
48
+ * earlier value marked beside the rate, or the two values written together right before it.
49
+ */
50
+ export declare const changeRateMismatches: (text: ChangeText) => StructureIssue[];
51
+ //# sourceMappingURL=change-rate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-rate.d.ts","sourceRoot":"","sources":["../../src/structure/change-rate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,sIAAsI;AACtI,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AACrG,yDAAyD;AACzD,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAChF,4EAA4E;AAC5E,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;CAAE,CAAC;AACzD,2IAA2I;AAC3I,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAA;CAAE,CAAC;AACxE,oEAAoE;AACpE,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AACtD,gIAAgI;AAChI,MAAM,MAAM,KAAK,GAAG,IAAI,GAAG;IAAE,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAA;CAAE,CAAC;AAEhE,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,SAAS,EAAE,SAAS,IAAI,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IACpC,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,0HAA0H;IAC1H,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AA+BF,0GAA0G;AAC1G,eAAO,MAAM,SAAS,GAAI,QAAQ,MAAM,EAAE,KAAK,IAAI,KAAG,MAA6E,CAAC;AA0JpI;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,UAAU,KAAG,cAAc,EAAmE,CAAC"}
@@ -0,0 +1,171 @@
1
+ /** How far a direction word may stand from the rate (「20%の増加」「20%増えました」, "an increase of 20%", "up 20%"). */
2
+ const DIRECTION_REACH = 8;
3
+ /** How far a base mark may stand from its value ("from $10" has the currency sign between). */
4
+ const MARK_GAP = 3;
5
+ /**
6
+ * How far the earlier value may stand from the rate (「1,000社から20%」「10億円に比べて20%」"20% from $10 million"). Further
7
+ * away, the rate is of something else in the sentence (「100店から120店に増え、売上は15%増えた」).
8
+ */
9
+ const BASE_REACH = 12;
10
+ /**
11
+ * The two values written together, the earlier then the later or the other way round (「昨年の100万円から今年は150万円」
12
+ * "from $1.0 million last year to $1.5 million"): how far apart they may stand, and how far after them the rate may
13
+ * (「150万円になり、前年比20%」", an increase of 20%"). Measured with a Latin word counting as one character.
14
+ */
15
+ const PAIR_GAP = 16;
16
+ const PAIR_REACH = 16;
17
+ /** How far a year may stand from the value it dates (「2025年の100万円」"$1.0 million in 2025"), a word counting as one. */
18
+ const PERIOD_GAP = 8;
19
+ const LATIN_WORD = /[A-Za-z]+/gu;
20
+ const PERCENT = 100;
21
+ const HALF = 0.5;
22
+ const DECIMAL_BASE = 10;
23
+ const within = (outer, inner) => inner.start >= outer.start && inner.end <= outer.end;
24
+ const distance = (left, right) => Math.max(left.start - right.end, right.start - left.end, 0);
25
+ const gapBetween = (left, right) => ({ start: left.end, end: right.start });
26
+ /** The length of a gap, a Latin word counting as one character: "an increase of" is as short as 「前年比」. */
27
+ export const gapLength = (source, gap) => source.slice(gap.start, gap.end).replaceAll(LATIN_WORD, "w").length;
28
+ const markOf = (figure, marks) => marks.find((mark) => {
29
+ const gap = mark.position === "after" ? mark.start - figure.end : figure.start - mark.end;
30
+ return gap >= 0 && gap <= MARK_GAP;
31
+ });
32
+ /** The earlier value: marked, with the value or its mark beside the rate ("up 25% compared with $1,000"). */
33
+ const isBaseFor = (figure, rate, marks) => {
34
+ const mark = markOf(figure, marks);
35
+ return mark !== undefined && Math.min(distance(figure, rate), distance(mark, rate)) <= BASE_REACH;
36
+ };
37
+ /**
38
+ * The later value is written before the rate (「1,200社となり、…20%増えました」"1,200 companies, up 20%") or marked as the
39
+ * value reached (「1,200社に」"to 1,200"). Another amount after the rate is about something else ("…, and revenue was $1,300").
40
+ */
41
+ const isCurrentFor = (figure, rate, targets) => figure.end <= rate.start || markOf(figure, targets) !== undefined;
42
+ /** The one sign the direction words beside the rate agree on. */
43
+ const signNear = (rate, directions) => {
44
+ const signs = new Set(directions.filter((direction) => distance(direction, rate) <= DIRECTION_REACH).map((direction) => direction.sign));
45
+ const [sign] = signs;
46
+ return signs.size === 1 ? sign : undefined;
47
+ };
48
+ /**
49
+ * The rates of change the two values allow. Each value may be off by half the step it is written to: 12億 is anything from
50
+ * 11.5億 to 12.5億, so 12億 from 10億 is a rise of 9.5% to 31.6%, and only a rate outside that is a mistake.
51
+ */
52
+ const rateRange = (base, current) => {
53
+ const [baseLow, baseHigh] = [base.value - base.step * HALF, base.value + base.step * HALF];
54
+ const [currentLow, currentHigh] = [current.value - current.step * HALF, current.value + current.step * HALF];
55
+ if (baseLow <= 0)
56
+ return undefined;
57
+ return [(currentLow / baseHigh - 1) * PERCENT, (currentHigh / baseLow - 1) * PERCENT];
58
+ };
59
+ /** A word between the two that starts another subject or clause (「100万円から150万円に増え、利益は20%」). */
60
+ const brokenBetween = (text, one, other) => {
61
+ const gap = one.end <= other.start ? gapBetween(one, other) : gapBetween(other, one);
62
+ return text.breaks.some((word) => within(gap, word));
63
+ };
64
+ /** The earlier value marked beside the rate, and the one value in its unit before the rate or marked as reached. */
65
+ const besideRate = (text) => {
66
+ const bases = text.figures.filter((figure) => isBaseFor(figure, text.rate, text.marks) && !brokenBetween(text, figure, text.rate));
67
+ const [base] = bases;
68
+ if (bases.length !== 1 || base === undefined)
69
+ return undefined;
70
+ const currents = text.figures.filter((figure) => figure !== base && figure.unit === base.unit && isCurrentFor(figure, text.rate, text.targets));
71
+ const [current] = currents;
72
+ return currents.length === 1 && current !== undefined ? { base, current } : undefined;
73
+ };
74
+ const yearsBeside = (text, figure, side, bound) => text.periods.filter((period) => {
75
+ const [left, right] = side === "before" ? [period, figure] : [figure, period];
76
+ return left.end <= right.start && within(bound, period) && gapLength(text.source, gapBetween(left, right)) <= PERIOD_GAP;
77
+ });
78
+ /**
79
+ * Of two values each dated by a year, the earlier year's. The years stand on the same side of both values: before them
80
+ * (「2025年は100万円、2026年は150万円」) or after them ("$1.0 million in 2025 and $1.5 million in 2026").
81
+ */
82
+ const datedEarlier = (text, first, second) => {
83
+ const middle = gapBetween(first, second);
84
+ const sides = [
85
+ [yearsBeside(text, first, "before", { start: 0, end: first.start }), yearsBeside(text, second, "before", middle)],
86
+ [yearsBeside(text, first, "after", middle), yearsBeside(text, second, "after", { start: second.end, end: text.source.length })],
87
+ ].filter(([early, late]) => early?.length === 1 && late?.length === 1);
88
+ const [[early, late] = []] = sides;
89
+ const [firstYear, secondYear] = [early?.[0]?.year, late?.[0]?.year];
90
+ if (sides.length !== 1 || firstYear === undefined || secondYear === undefined || firstYear === secondYear)
91
+ return undefined;
92
+ return firstYear < secondYear ? first : second;
93
+ };
94
+ /** Which of two values written together is the earlier: the one marked (「から」, "from", 「前年の」), else the one dated earlier. */
95
+ const pairOf = (text, first, second) => {
96
+ const marked = [first, second].filter((figure) => markOf(figure, text.marks) !== undefined);
97
+ if (marked.length > 1)
98
+ return undefined;
99
+ const base = marked[0] ?? datedEarlier(text, first, second);
100
+ if (base === undefined)
101
+ return undefined;
102
+ return { base, current: base === first ? second : first };
103
+ };
104
+ /** Nothing between the later value and the rate that makes the rate another one's: a value, another subject, another base. */
105
+ const leadsTo = (text, second) => {
106
+ const gap = gapBetween(second, text.rate);
107
+ if (gap.start > gap.end || gapLength(text.source, gap) > PAIR_REACH)
108
+ return false;
109
+ return ![...text.figures, ...text.breaks, ...text.marks].some((span) => within(gap, span));
110
+ };
111
+ /** Two values of one subject written close, with no rate between them (「売上は100万円、費用は150万円」 is two subjects). */
112
+ const standTogether = (text, first, second) => {
113
+ const gap = gapBetween(first, second);
114
+ const inGap = (span) => within(gap, span);
115
+ if (gapLength(text.source, gap) > PAIR_GAP || text.rates.some(inGap))
116
+ return false;
117
+ return !text.breaks.some((word) => !word.beforeRateOnly && inGap(word));
118
+ };
119
+ /** The two values written together right before the rate (「昨年の100万円から今年は150万円になり、前年比20%の増加」). */
120
+ const beforeRate = (text) => {
121
+ const pairs = text.figures.flatMap((first, index) => {
122
+ const second = text.figures.slice(index + 1).find((figure) => figure.unit === first.unit);
123
+ const pair = second !== undefined && standTogether(text, first, second) && leadsTo(text, second) ? pairOf(text, first, second) : undefined;
124
+ return pair === undefined ? [] : [pair];
125
+ });
126
+ const [pair] = pairs;
127
+ return pairs.length === 1 ? pair : undefined;
128
+ };
129
+ /**
130
+ * The sentence's one rate with a word of direction beside it, a word counting for the rate it stands nearest. A percentage
131
+ * without one (「20%の増加、利益率は10%」) is about something else.
132
+ */
133
+ const readingIn = (text, sentence) => {
134
+ const inSentence = (span) => within(sentence, span);
135
+ const directions = text.directions.filter(inSentence);
136
+ const rates = text.rates.filter(inSentence);
137
+ const nearestTo = (rate, direction) => rates.every((other) => distance(direction, rate) <= distance(direction, other));
138
+ const signed = rates.flatMap((rate) => {
139
+ const sign = signNear(rate, directions.filter((direction) => nearestTo(rate, direction)));
140
+ return sign === undefined ? [] : [{ rate, sign }];
141
+ });
142
+ const [only] = signed;
143
+ if (signed.length !== 1 || only === undefined)
144
+ return undefined;
145
+ const parts = {
146
+ figures: text.figures.filter(inSentence).toSorted((left, right) => left.start - right.start),
147
+ periods: text.periods.filter(inSentence),
148
+ breaks: text.breaks.filter(inSentence),
149
+ };
150
+ return { ...text, ...parts, ...only, rates };
151
+ };
152
+ const issueIn = (text, sentence) => {
153
+ const reading = readingIn(text, sentence);
154
+ const pair = reading === undefined ? undefined : (besideRate(reading) ?? beforeRate(reading));
155
+ const range = pair === undefined ? undefined : rateRange(pair.base, pair.current);
156
+ if (reading === undefined || pair === undefined || range === undefined)
157
+ return [];
158
+ const { rate, sign } = reading;
159
+ const written = sign * rate.value;
160
+ const slack = HALF * DECIMAL_BASE ** -rate.decimals;
161
+ if (written >= range[0] - slack && written <= range[1] + slack)
162
+ return [];
163
+ const computed = Math.abs((pair.current.value / pair.base.value - 1) * PERCENT).toFixed(rate.decimals);
164
+ return [{ offset: rate.start, values: { rate: String(rate.value), computed } }];
165
+ };
166
+ /**
167
+ * Each sentence with one rate of change and its direction, and the two values it is computed from, that disagree: the
168
+ * earlier value marked beside the rate, or the two values written together right before it.
169
+ */
170
+ export const changeRateMismatches = (text) => text.sentences.flatMap((sentence) => issueIn(text, sentence));
171
+ //# sourceMappingURL=change-rate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-rate.js","sourceRoot":"","sources":["../../src/structure/change-rate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { Span } from "../plugin.ts";
2
+ import type { StructureIssue } from "./issues.ts";
3
+ /** One item's written number: where the item starts, the number and the number as written (「4.」). */
4
+ export type WrittenNumber = {
5
+ readonly offset: number;
6
+ readonly number: number;
7
+ readonly label: string;
8
+ };
9
+ /** The number written at the start of each item, or undefined when an item has none (a bulleted list). */
10
+ export declare const writtenNumbers: (source: string, itemSpans: readonly Span[]) => WrittenNumber[] | undefined;
11
+ /**
12
+ * Each item whose number is not one more than the item before it. A list that writes one number on every item (「1.」 on
13
+ * each, which Markdown counts up) is numbered by the renderer, not by the writer, and is not read. An item numbered 1
14
+ * starts a new count, as the structure rule reads a 1 that follows other items.
15
+ */
16
+ export declare const listNumberBreaks: (items: readonly WrittenNumber[]) => StructureIssue[];
17
+ //# sourceMappingURL=list-numbering.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-numbering.d.ts","sourceRoot":"","sources":["../../src/structure/list-numbering.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,qGAAqG;AACrG,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzG,0GAA0G;AAC1G,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,EAAE,WAAW,SAAS,IAAI,EAAE,KAAG,aAAa,EAAE,GAAG,SAQ7F,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,SAAS,aAAa,EAAE,KAAG,cAAc,EAOhF,CAAC"}
@@ -0,0 +1,27 @@
1
+ const ITEM_NUMBER = /^(?<number>\d{1,9})(?<delimiter>[.)])[ \t]/u;
2
+ /** The number written at the start of each item, or undefined when an item has none (a bulleted list). */
3
+ export const writtenNumbers = (source, itemSpans) => {
4
+ const numbers = itemSpans.map((span) => {
5
+ const found = ITEM_NUMBER.exec(source.slice(span.start, span.end))?.groups;
6
+ return found === undefined
7
+ ? undefined
8
+ : { offset: span.start, number: Number(found["number"]), label: `${found["number"] ?? ""}${found["delimiter"] ?? ""}` };
9
+ });
10
+ return numbers.every((entry) => entry !== undefined) ? numbers : undefined;
11
+ };
12
+ /**
13
+ * Each item whose number is not one more than the item before it. A list that writes one number on every item (「1.」 on
14
+ * each, which Markdown counts up) is numbered by the renderer, not by the writer, and is not read. An item numbered 1
15
+ * starts a new count, as the structure rule reads a 1 that follows other items.
16
+ */
17
+ export const listNumberBreaks = (items) => {
18
+ if (new Set(items.map((item) => item.number)).size < 2)
19
+ return [];
20
+ return items.flatMap((item, index) => {
21
+ const previous = items[index - 1];
22
+ if (previous === undefined || item.number === previous.number + 1 || item.number === 1)
23
+ return [];
24
+ return [{ offset: item.offset, values: { previous: previous.label, label: item.label, expected: previous.number + 1, found: item.number } }];
25
+ });
26
+ };
27
+ //# sourceMappingURL=list-numbering.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-numbering.js","sourceRoot":"","sources":["../../src/structure/list-numbering.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,GAAG,6CAA6C,CAAC;AAKlE,0GAA0G;AAC1G,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAc,EAAE,SAA0B,EAA+B,EAAE;IACxG,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC;QAC3E,OAAO,KAAK,KAAK,SAAS;YACxB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC;IAC5H,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7E,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAA+B,EAAoB,EAAE;IACpF,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAClE,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACnC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAClC,IAAI,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAClG,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/I,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Span } from "../plugin.ts";
2
+ import type { StructureIssue } from "./issues.ts";
3
+ import type { Amount } from "./total.ts";
4
+ import type { ShareWords } from "./percent-sum.ts";
5
+ /** Each sentence that names a share word and three or more unsigned percentages whose sum misses 100% by more than rounding, and by no more than BAND. */
6
+ export declare const proseShareMismatches: (source: string, sentences: readonly Span[], amounts: readonly Amount[], words: ShareWords) => StructureIssue[];
7
+ //# sourceMappingURL=percent-sum-prose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"percent-sum-prose.d.ts","sourceRoot":"","sources":["../../src/structure/percent-sum-prose.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AA2CnD,0JAA0J;AAC1J,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,EAAE,WAAW,SAAS,IAAI,EAAE,EAAE,SAAS,SAAS,MAAM,EAAE,EAAE,OAAO,UAAU,KAAG,cAAc,EAI9I,CAAC"}
@@ -0,0 +1,44 @@
1
+ const WHOLE = 100;
2
+ /** A sentence names two shares in passing (「AとBで50%」); a breakdown in prose names at least three parts. */
3
+ const MIN_PARTS = 3;
4
+ /**
5
+ * How far from 100% a sentence may add up and still read as one whole broken down. Far below it, the sentence names only the
6
+ * larger parts (「Aが50%、Bが20%、Cが10%など」); far above it, the percentages are of different wholes ("55% of directors,
7
+ * 48% of managers") or of two periods ("45% (40% last year)"), or the total is among them (「合計100%」).
8
+ */
9
+ const BAND = 10;
10
+ const HALF = 0.5;
11
+ const DECIMAL_BASE = 10;
12
+ const DECIMALS = /\.(\d+)/u;
13
+ /** A sign before the number (+3%, ▲2%): a change, not a part. */
14
+ const SIGNS = "-+−▲△";
15
+ const includesAny = (text, words) => {
16
+ const lowered = text.toLowerCase();
17
+ return words.some((word) => lowered.includes(word.toLowerCase()));
18
+ };
19
+ const decimalsOf = (source, amount) => DECIMALS.exec(source.slice(amount.offset, amount.end))?.[1]?.length ?? 0;
20
+ const isSigned = (source, amount) => SIGNS.includes(source.slice(0, amount.offset).trimEnd().at(-1) ?? " ");
21
+ /** The rounding the parts allow: half the last digit each, at least one point. */
22
+ const toleranceOf = (source, shares) => Math.max(1, shares.length * HALF * DECIMAL_BASE ** -Math.max(0, ...shares.map((share) => decimalsOf(source, share))));
23
+ const isBroken = (sum, tolerance) => Math.abs(sum - WHOLE) > tolerance && Math.abs(sum - WHOLE) <= BAND;
24
+ const issueIn = (source, sentence, percents, words) => {
25
+ const text = source.slice(sentence.start, sentence.end);
26
+ if (!includesAny(text, words.labels) || includesAny(text, words.exceptions))
27
+ return [];
28
+ const shares = percents.filter((amount) => amount.offset >= sentence.start && amount.end <= sentence.end);
29
+ if (shares.length < MIN_PARTS || shares.some((share) => isSigned(source, share)))
30
+ return [];
31
+ const sum = shares.reduce((total, share) => total + share.value, 0);
32
+ const decimals = Math.max(0, ...shares.map((share) => decimalsOf(source, share)));
33
+ if (!isBroken(sum, toleranceOf(source, shares)))
34
+ return [];
35
+ return [{ offset: shares[0]?.offset ?? sentence.start, values: { sum: `${sum.toFixed(decimals)}${shares[0]?.unit ?? ""}` } }];
36
+ };
37
+ /** Each sentence that names a share word and three or more unsigned percentages whose sum misses 100% by more than rounding, and by no more than BAND. */
38
+ export const proseShareMismatches = (source, sentences, amounts, words) => {
39
+ if (words.labels.length === 0)
40
+ return [];
41
+ const percents = amounts.filter((amount) => words.units.includes(amount.unit));
42
+ return sentences.flatMap((sentence) => issueIn(source, sentence, percents, words));
43
+ };
44
+ //# sourceMappingURL=percent-sum-prose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"percent-sum-prose.js","sourceRoot":"","sources":["../../src/structure/percent-sum-prose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { StructureIssue } from "./issues.ts";
2
+ /** A word that makes a version a bound or a starting point, not the version installed: 「2.3.0 から」 "from 2.3.0", "20.0.0 or later". */
3
+ export type RangeWord = {
4
+ readonly word: string;
5
+ readonly position: "before" | "after";
6
+ };
7
+ /** What tells which versions are the block's: the bound words, and the Latin words that lead to the version installed. */
8
+ export type VersionWords = {
9
+ readonly ranges: readonly RangeWord[];
10
+ readonly versionWords: readonly string[];
11
+ };
12
+ /** One code block: where it starts and the text between its fences. */
13
+ export type CodeBlock = {
14
+ readonly start: number;
15
+ readonly code: string;
16
+ };
17
+ /**
18
+ * Each version in a code block's lead-in that differs from the one version the block shows. A block with no version, or
19
+ * with two (an upgrade from one to the other), says nothing to compare; a version marked as a bound, or named after another
20
+ * program, is not the one installed.
21
+ */
22
+ export declare const versionMismatches: (source: string, prose: string, blocks: readonly CodeBlock[], words: VersionWords) => StructureIssue[];
23
+ //# sourceMappingURL=version-mismatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-mismatch.d.ts","sourceRoot":"","sources":["../../src/structure/version-mismatch.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAQlD,sIAAsI;AACtI,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAA;CAAE,CAAC;AAEzF,0HAA0H;AAC1H,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAE/G,uEAAuE;AACvE,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAqE1E;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,EAAE,OAAO,MAAM,EAAE,QAAQ,SAAS,SAAS,EAAE,EAAE,OAAO,YAAY,KAAG,cAAc,EAU/H,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * A dotted version of three parts (2.4.0, v1.12.3, 3.0.0-beta.2). Two parts (22.04, 3.1) are too often a decimal, and a
3
+ * first part of four digits is too often a date (2024.01.15).
4
+ */
5
+ const VERSION = /(?<![\w.])v?(\d{1,3}\.\d{1,4}\.\d{1,4}(?:-[0-9A-Za-z.]+)?)(?![\w.]|\.\d)/gu;
6
+ /** How far a range word may stand from the version (a space, a "v"). */
7
+ const RANGE_GAP = 2;
8
+ const versionsIn = (text, offset = 0) => [...text.matchAll(VERSION)].map((match) => ({ start: offset + match.index, end: offset + match.index + match[0].length, version: match[1] ?? "" }));
9
+ const isBound = (prose, found, words) => words.some(({ word, position }) => {
10
+ if (word === "")
11
+ return false;
12
+ const near = position === "after"
13
+ ? prose.slice(found.end, found.end + RANGE_GAP + word.length)
14
+ : prose.slice(Math.max(0, found.start - RANGE_GAP - word.length), found.start);
15
+ return near.toLowerCase().includes(word.toLowerCase());
16
+ });
17
+ /** The names a block pins its version to: tidyq in tidyq@2.4.0, tidyq==2.4.0, tidyq=2.4.0, tidyq:2.4.0, "tidyq": "2.4.0". */
18
+ const PINNED = /^([\w.-]+)(?:@|==|=|:)[v^~]?\d/u;
19
+ const QUOTES = /["']/gu;
20
+ const OPENS_WITH_NUMBER = /^[v^~]?\d/u;
21
+ /** Each whitespace-separated word of the code, quotes dropped; "tidyq": "2.4.0" is read as tidyq:2.4.0. */
22
+ const codeWords = (code) => {
23
+ const words = code.split(/\s+/u).map((word) => word.replaceAll(QUOTES, "").replace(/,$/u, ""));
24
+ return words.map((word, index) => (word.endsWith(":") && OPENS_WITH_NUMBER.test(words[index + 1] ?? "") ? `${word}${words[index + 1] ?? ""}` : word));
25
+ };
26
+ const pinnedNames = (code) => new Set(codeWords(code).flatMap((word) => PINNED.exec(word)?.[1]?.toLowerCase() ?? []));
27
+ const LATIN = /^[A-Za-z]/u;
28
+ const WORD_CHAR = /[\w.+-]/u;
29
+ /** How far back the word before a version is looked for. */
30
+ const WORD_REACH = 40;
31
+ /** The Latin word right before a position, if the word there is Latin: "Node.js" in "Requires Node.js 18.17.0". */
32
+ const latinWordBefore = (text) => {
33
+ const last = [...(text.trimEnd().split(/\s/u).at(-1) ?? "")];
34
+ const word = last.slice(0, last.findLastIndex((char) => WORD_CHAR.test(char)) + 1).join("");
35
+ return LATIN.test(word) ? word.toLowerCase() : undefined;
36
+ };
37
+ /**
38
+ * Whether a lead-in version is about the block's software. A Latin word right before it must be one of the words that lead
39
+ * to the version installed (version, release, install; lexicon version-lead) or the pinned software: "Requires Node.js
40
+ * 18.17.0" over `npm install pkg@2.4.0` names another program. A version after no Latin word (「npm から 2.3.0」) is the block's.
41
+ */
42
+ const isAboutBlock = (prose, found, names, versionWords) => {
43
+ const word = latinWordBefore(prose.slice(Math.max(0, found.start - WORD_REACH), found.start));
44
+ return word === undefined || names.has(word) || versionWords.some((name) => name.toLowerCase() === word);
45
+ };
46
+ const BLANK_LINE = /\r?\n[ \t]*\r?\n/gu;
47
+ const NOT_PROSE = /^\s*(?:#|[-*+]\s|\d{1,3}[.)]\s|```|~~~|\|)/u;
48
+ const LETTER = /\p{L}/u;
49
+ /** The paragraph right above a code block: the lines up to the blank line before it. A heading, a list item, a table or a bare version above is not a lead-in. */
50
+ const leadInOf = (source, blockStart) => {
51
+ const lines = source.slice(0, blockStart).trimEnd();
52
+ const last = [...lines.matchAll(BLANK_LINE)].at(-1);
53
+ const start = last === undefined ? 0 : last.index + last[0].length;
54
+ const text = lines.slice(start);
55
+ const words = text.replace(VERSION, "");
56
+ return text === "" || NOT_PROSE.test(text) || !LETTER.test(words) ? undefined : { start, end: lines.length };
57
+ };
58
+ /**
59
+ * Each version in a code block's lead-in that differs from the one version the block shows. A block with no version, or
60
+ * with two (an upgrade from one to the other), says nothing to compare; a version marked as a bound, or named after another
61
+ * program, is not the one installed.
62
+ */
63
+ export const versionMismatches = (source, prose, blocks, words) => blocks.flatMap((block) => {
64
+ const shown = [...new Set(versionsIn(block.code).map((found) => found.version))];
65
+ const leadIn = leadInOf(source, block.start);
66
+ const [code] = shown;
67
+ if (shown.length !== 1 || code === undefined || leadIn === undefined)
68
+ return [];
69
+ const names = pinnedNames(block.code);
70
+ return versionsIn(prose.slice(leadIn.start, leadIn.end), leadIn.start)
71
+ .filter((found) => found.version !== code && !isBound(prose, found, words.ranges) && isAboutBlock(prose, found, names, words.versionWords))
72
+ .map((found) => ({ offset: found.start, values: { written: found.version, code } }));
73
+ });
74
+ //# sourceMappingURL=version-mismatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-mismatch.js","sourceRoot":"","sources":["../../src/structure/version-mismatch.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,25 @@ import type { StructureNode } from "../plugin.ts";
2
2
  import type { StructureIssue } from "./issues.ts";
3
3
  /** 暦の曜日。日曜日が 0。存在しない日(2月30日)は undefined。 */
4
4
  export declare const weekdayOf: (value: string) => number | undefined;
5
- /** 年まで書いた日付で、書いた曜日が暦と違うもの。年の無い日付は曜日を決められないので見ない。 */
6
- export declare const weekdayMismatches: (tree: StructureNode) => StructureIssue[];
5
+ /**
6
+ * 年の無い月日(「10-09」)に、年を書いた日付(anchor、「2026-10-06」)の前後の年のうち、anchor から 5 か月以内に収まる年を付ける。
7
+ * メールや議事録は、文書の日付の年を書いて、そのあとの日付の年を省く。12月の文書の「1月5日」は翌年。どの年も収まらなければ undefined。
8
+ */
9
+ export declare const withNearestYear: (monthDay: string, anchor: string) => string | undefined;
10
+ /**
11
+ * 年の無い日付の年を決める手がかり。stamp は文書の日付(「更新日:」の段落など)、sectionStarts は見出しで分けた節の始まり、
12
+ * yearHeadings はそのうち見出しが年を名指す節(「## 2024年度」)の始まり。anchorable は、年を決めるのに使ってよい日付か
13
+ * (引用符で日付だけを挙げた例は使わない)。
14
+ */
15
+ export type YearContext = {
16
+ readonly stamp?: string | undefined;
17
+ readonly sectionStarts: readonly number[];
18
+ readonly yearHeadings?: readonly number[];
19
+ readonly anchorable?: (node: StructureNode) => boolean;
20
+ };
21
+ /**
22
+ * 書いた曜日が暦と違う日付。年の無い日付は、年を書いた日付(anchorOf)から 5 か月以内に収まる年として比べる。
23
+ * 年を書いた日付が文書に無いか、どの年も収まらなければ、曜日を決められないので見ない。
24
+ */
25
+ export declare const weekdayMismatches: (tree: StructureNode, context?: YearContext) => StructureIssue[];
7
26
  //# sourceMappingURL=weekday.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"weekday.d.ts","sourceRoot":"","sources":["../../src/structure/weekday.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAOlD,4CAA4C;AAC5C,eAAO,MAAM,SAAS,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,SAMlD,CAAC;AAEF,oDAAoD;AACpD,eAAO,MAAM,iBAAiB,GAAI,MAAM,aAAa,KAAG,cAAc,EAMlE,CAAC"}
1
+ {"version":3,"file":"weekday.d.ts","sourceRoot":"","sources":["../../src/structure/weekday.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAQlD,4CAA4C;AAC5C,eAAO,MAAM,SAAS,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,SAMlD,CAAC;AAWF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,EAAE,QAAQ,MAAM,KAAG,MAAM,GAAG,SAW3E,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC;CACxD,CAAC;AAgCF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,aAAa,EAAE,UAAS,WAAmC,KAAG,cAAc,EAWnH,CAAC"}