@mark1russell7/ste-lint 0.0.0-bootstrap.0 → 0.1.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 (227) hide show
  1. package/README.md +224 -1
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +9 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/config.d.ts +103 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +251 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/dictionary.d.ts +74 -0
  12. package/dist/dictionary.d.ts.map +1 -0
  13. package/dist/dictionary.js +169 -0
  14. package/dist/dictionary.js.map +1 -0
  15. package/dist/files.d.ts +35 -0
  16. package/dist/files.d.ts.map +1 -0
  17. package/dist/files.js +181 -0
  18. package/dist/files.js.map +1 -0
  19. package/dist/format.d.ts +21 -0
  20. package/dist/format.d.ts.map +1 -0
  21. package/dist/format.js +61 -0
  22. package/dist/format.js.map +1 -0
  23. package/dist/index.d.ts +17 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +17 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/lexicon.d.ts +33 -0
  28. package/dist/lexicon.d.ts.map +1 -0
  29. package/dist/lexicon.js +71 -0
  30. package/dist/lexicon.js.map +1 -0
  31. package/dist/lint.d.ts +28 -0
  32. package/dist/lint.d.ts.map +1 -0
  33. package/dist/lint.js +132 -0
  34. package/dist/lint.js.map +1 -0
  35. package/dist/rules/about-quantity.d.ts +3 -0
  36. package/dist/rules/about-quantity.d.ts.map +1 -0
  37. package/dist/rules/about-quantity.js +34 -0
  38. package/dist/rules/about-quantity.js.map +1 -0
  39. package/dist/rules/index.d.ts +8 -0
  40. package/dist/rules/index.d.ts.map +1 -0
  41. package/dist/rules/index.js +35 -0
  42. package/dist/rules/index.js.map +1 -0
  43. package/dist/rules/ing-verb.d.ts +9 -0
  44. package/dist/rules/ing-verb.d.ts.map +1 -0
  45. package/dist/rules/ing-verb.js +60 -0
  46. package/dist/rules/ing-verb.js.map +1 -0
  47. package/dist/rules/latin-abbreviation.d.ts +3 -0
  48. package/dist/rules/latin-abbreviation.d.ts.map +1 -0
  49. package/dist/rules/latin-abbreviation.js +48 -0
  50. package/dist/rules/latin-abbreviation.js.map +1 -0
  51. package/dist/rules/missing-subject.d.ts +16 -0
  52. package/dist/rules/missing-subject.d.ts.map +1 -0
  53. package/dist/rules/missing-subject.js +138 -0
  54. package/dist/rules/missing-subject.js.map +1 -0
  55. package/dist/rules/modal-verb.d.ts +3 -0
  56. package/dist/rules/modal-verb.d.ts.map +1 -0
  57. package/dist/rules/modal-verb.js +72 -0
  58. package/dist/rules/modal-verb.js.map +1 -0
  59. package/dist/rules/noun-cluster.d.ts +17 -0
  60. package/dist/rules/noun-cluster.d.ts.map +1 -0
  61. package/dist/rules/noun-cluster.js +83 -0
  62. package/dist/rules/noun-cluster.js.map +1 -0
  63. package/dist/rules/paragraph-length.d.ts +3 -0
  64. package/dist/rules/paragraph-length.d.ts.map +1 -0
  65. package/dist/rules/paragraph-length.js +19 -0
  66. package/dist/rules/paragraph-length.js.map +1 -0
  67. package/dist/rules/passive-instruction.d.ts +15 -0
  68. package/dist/rules/passive-instruction.d.ts.map +1 -0
  69. package/dist/rules/passive-instruction.js +38 -0
  70. package/dist/rules/passive-instruction.js.map +1 -0
  71. package/dist/rules/pronouns.d.ts +4 -0
  72. package/dist/rules/pronouns.d.ts.map +1 -0
  73. package/dist/rules/pronouns.js +57 -0
  74. package/dist/rules/pronouns.js.map +1 -0
  75. package/dist/rules/rule.d.ts +46 -0
  76. package/dist/rules/rule.d.ts.map +1 -0
  77. package/dist/rules/rule.js +31 -0
  78. package/dist/rules/rule.js.map +1 -0
  79. package/dist/rules/semicolon.d.ts +3 -0
  80. package/dist/rules/semicolon.d.ts.map +1 -0
  81. package/dist/rules/semicolon.js +20 -0
  82. package/dist/rules/semicolon.js.map +1 -0
  83. package/dist/rules/sentence-length.d.ts +3 -0
  84. package/dist/rules/sentence-length.d.ts.map +1 -0
  85. package/dist/rules/sentence-length.js +31 -0
  86. package/dist/rules/sentence-length.js.map +1 -0
  87. package/dist/rules/unknown-word.d.ts +9 -0
  88. package/dist/rules/unknown-word.d.ts.map +1 -0
  89. package/dist/rules/unknown-word.js +49 -0
  90. package/dist/rules/unknown-word.js.map +1 -0
  91. package/dist/rules/word-list-matcher.d.ts +25 -0
  92. package/dist/rules/word-list-matcher.d.ts.map +1 -0
  93. package/dist/rules/word-list-matcher.js +60 -0
  94. package/dist/rules/word-list-matcher.js.map +1 -0
  95. package/dist/rules/word-list.d.ts +3 -0
  96. package/dist/rules/word-list.d.ts.map +1 -0
  97. package/dist/rules/word-list.js +26 -0
  98. package/dist/rules/word-list.js.map +1 -0
  99. package/dist/run-cli.d.ts +27 -0
  100. package/dist/run-cli.d.ts.map +1 -0
  101. package/dist/run-cli.js +176 -0
  102. package/dist/run-cli.js.map +1 -0
  103. package/dist/source/blocks.d.ts +55 -0
  104. package/dist/source/blocks.d.ts.map +1 -0
  105. package/dist/source/blocks.js +464 -0
  106. package/dist/source/blocks.js.map +1 -0
  107. package/dist/source/inline.d.ts +42 -0
  108. package/dist/source/inline.d.ts.map +1 -0
  109. package/dist/source/inline.js +396 -0
  110. package/dist/source/inline.js.map +1 -0
  111. package/dist/source/jsx.d.ts +18 -0
  112. package/dist/source/jsx.d.ts.map +1 -0
  113. package/dist/source/jsx.js +137 -0
  114. package/dist/source/jsx.js.map +1 -0
  115. package/dist/source/lines.d.ts +34 -0
  116. package/dist/source/lines.d.ts.map +1 -0
  117. package/dist/source/lines.js +83 -0
  118. package/dist/source/lines.js.map +1 -0
  119. package/dist/source/markdown.d.ts +23 -0
  120. package/dist/source/markdown.d.ts.map +1 -0
  121. package/dist/source/markdown.js +42 -0
  122. package/dist/source/markdown.js.map +1 -0
  123. package/dist/source/suppressions.d.ts +38 -0
  124. package/dist/source/suppressions.d.ts.map +1 -0
  125. package/dist/source/suppressions.js +69 -0
  126. package/dist/source/suppressions.js.map +1 -0
  127. package/dist/source/tsdoc.d.ts +40 -0
  128. package/dist/source/tsdoc.d.ts.map +1 -0
  129. package/dist/source/tsdoc.js +176 -0
  130. package/dist/source/tsdoc.js.map +1 -0
  131. package/dist/text/grammar.d.ts +91 -0
  132. package/dist/text/grammar.d.ts.map +1 -0
  133. package/dist/text/grammar.js +289 -0
  134. package/dist/text/grammar.js.map +1 -0
  135. package/dist/text/phrases.d.ts +30 -0
  136. package/dist/text/phrases.d.ts.map +1 -0
  137. package/dist/text/phrases.js +65 -0
  138. package/dist/text/phrases.js.map +1 -0
  139. package/dist/text/sentences.d.ts +45 -0
  140. package/dist/text/sentences.d.ts.map +1 -0
  141. package/dist/text/sentences.js +141 -0
  142. package/dist/text/sentences.js.map +1 -0
  143. package/dist/text/tokens.d.ts +35 -0
  144. package/dist/text/tokens.d.ts.map +1 -0
  145. package/dist/text/tokens.js +113 -0
  146. package/dist/text/tokens.js.map +1 -0
  147. package/dist/text/words.d.ts +58 -0
  148. package/dist/text/words.d.ts.map +1 -0
  149. package/dist/text/words.js +159 -0
  150. package/dist/text/words.js.map +1 -0
  151. package/dist/types.d.ts +18 -0
  152. package/dist/types.d.ts.map +1 -0
  153. package/dist/types.js +20 -0
  154. package/dist/types.js.map +1 -0
  155. package/dist/units.d.ts +47 -0
  156. package/dist/units.d.ts.map +1 -0
  157. package/dist/units.js +42 -0
  158. package/dist/units.js.map +1 -0
  159. package/dist/word-list.d.ts +34 -0
  160. package/dist/word-list.d.ts.map +1 -0
  161. package/dist/word-list.js +90 -0
  162. package/dist/word-list.js.map +1 -0
  163. package/package.json +41 -4
  164. package/src/cli.ts +9 -0
  165. package/src/config.test.ts +188 -0
  166. package/src/config.ts +318 -0
  167. package/src/dictionary.test.ts +82 -0
  168. package/src/dictionary.ts +177 -0
  169. package/src/files.test.ts +107 -0
  170. package/src/files.ts +191 -0
  171. package/src/format.ts +78 -0
  172. package/src/index.test.ts +28 -0
  173. package/src/index.ts +34 -0
  174. package/src/lexicon.ts +92 -0
  175. package/src/lint.test.ts +182 -0
  176. package/src/lint.ts +135 -0
  177. package/src/rules/about-quantity.ts +33 -0
  178. package/src/rules/index.ts +40 -0
  179. package/src/rules/ing-verb.test.ts +48 -0
  180. package/src/rules/ing-verb.ts +54 -0
  181. package/src/rules/latin-abbreviation.ts +46 -0
  182. package/src/rules/missing-subject.test.ts +53 -0
  183. package/src/rules/missing-subject.ts +129 -0
  184. package/src/rules/modal-verb.test.ts +45 -0
  185. package/src/rules/modal-verb.ts +72 -0
  186. package/src/rules/noun-cluster.test.ts +49 -0
  187. package/src/rules/noun-cluster.ts +82 -0
  188. package/src/rules/paragraph-length.test.ts +30 -0
  189. package/src/rules/paragraph-length.ts +19 -0
  190. package/src/rules/passive-instruction.test.ts +38 -0
  191. package/src/rules/passive-instruction.ts +37 -0
  192. package/src/rules/pronouns.test.ts +38 -0
  193. package/src/rules/pronouns.ts +54 -0
  194. package/src/rules/punctuation.test.ts +59 -0
  195. package/src/rules/rule.ts +73 -0
  196. package/src/rules/semicolon.ts +19 -0
  197. package/src/rules/sentence-length.test.ts +52 -0
  198. package/src/rules/sentence-length.ts +32 -0
  199. package/src/rules/unknown-word.test.ts +81 -0
  200. package/src/rules/unknown-word.ts +43 -0
  201. package/src/rules/word-list-matcher.ts +68 -0
  202. package/src/rules/word-list.test.ts +110 -0
  203. package/src/rules/word-list.ts +25 -0
  204. package/src/run-cli.test.ts +189 -0
  205. package/src/run-cli.ts +193 -0
  206. package/src/source/blocks.ts +534 -0
  207. package/src/source/inline.test.ts +92 -0
  208. package/src/source/inline.ts +416 -0
  209. package/src/source/jsx.test.ts +46 -0
  210. package/src/source/jsx.ts +147 -0
  211. package/src/source/lines.test.ts +51 -0
  212. package/src/source/lines.ts +91 -0
  213. package/src/source/markdown.test.ts +210 -0
  214. package/src/source/markdown.ts +49 -0
  215. package/src/source/suppressions.ts +79 -0
  216. package/src/source/tsdoc.test.ts +137 -0
  217. package/src/source/tsdoc.ts +189 -0
  218. package/src/text/grammar.ts +304 -0
  219. package/src/text/phrases.ts +72 -0
  220. package/src/text/sentences.ts +148 -0
  221. package/src/text/text.test.ts +262 -0
  222. package/src/text/tokens.ts +135 -0
  223. package/src/text/words.ts +183 -0
  224. package/src/types.ts +39 -0
  225. package/src/units.ts +86 -0
  226. package/src/word-list.ts +128 -0
  227. package/ste.config.schema.json +87 -0
@@ -0,0 +1,34 @@
1
+ import { isNumber, isWord } from "../text/tokens.js";
2
+ import { UNITS } from "../text/words.js";
3
+ import { forEachToken, tokenRange } from "./rule.js";
4
+ /** Words after "about a" or "about an" that make a quantity: "about a second", "about an hour". */
5
+ const QUANTITY_AFTER_ARTICLE = new Set([
6
+ "dozen", "few", "hundred", "thousand", "million", "billion", "third", "quarter", "half", "factor", "tenth",
7
+ ]);
8
+ export const aboutQuantity = {
9
+ id: "about-quantity",
10
+ description: "Find \"about\" before a number or a quantity, where the word means \"approximately\".",
11
+ ste: "1.3, 9.2",
12
+ defaultSeverity: "error",
13
+ scope: "word",
14
+ check(unit, context) {
15
+ forEachToken(unit, (tokens, i) => {
16
+ const token = tokens[i];
17
+ if (!isWord(token) || token.lower !== "about")
18
+ return;
19
+ const next = tokens[i + 1];
20
+ const after = tokens[i + 2];
21
+ const quantity = isNumber(next)
22
+ || (next !== undefined && next.text === "~")
23
+ || (isWord(next) && /^\d/.test(next.text))
24
+ || (isWord(next) && (next.lower === "a" || next.lower === "an") && isWord(after) && (QUANTITY_AFTER_ARTICLE.has(after.lower) || UNITS.has(after.lower)));
25
+ if (!quantity)
26
+ return;
27
+ context.report({
28
+ ...tokenRange(tokens, i, i + 1),
29
+ message: "Do not use \"about\" for a quantity. Write \"approximately\". Use \"about\" only for a topic, for example \"about the monitor\".",
30
+ });
31
+ });
32
+ },
33
+ };
34
+ //# sourceMappingURL=about-quantity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"about-quantity.js","sourceRoot":"","sources":["../../src/rules/about-quantity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAa,MAAM,WAAW,CAAC;AAEhE,mGAAmG;AACnG,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IACnC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO;CAC7G,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAU;IAChC,EAAE,EAAG,gBAAgB;IACrB,WAAW,EAAG,uFAAuF;IACrG,GAAG,EAAG,UAAU;IAChB,eAAe,EAAG,OAAO;IACzB,KAAK,EAAG,MAAM;IACd,KAAK,CAAC,IAAI,EAAE,OAAO;QACf,YAAY,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO;gBAAE,OAAO;YACtD,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5B,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC;mBACxB,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC;mBACzC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;mBACvC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,GAAG,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC7J,IAAI,CAAC,QAAQ;gBAAE,OAAO;YACtB,OAAO,CAAC,MAAM,CAAC;gBACX,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;gBAC/B,OAAO,EAAG,kIAAkI;aAC/I,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;CACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { RuleId } from "../types.js";
2
+ import type { Rule } from "./rule.js";
3
+ /** All rules, in the order of the documentation. */
4
+ export declare const RULES: readonly Rule[];
5
+ /** The rule with the ID `id`. */
6
+ export declare function getRule(id: RuleId): Rule;
7
+ export type { Report, Rule, RuleContext, RuleScope } from "./rule.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAU1C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAMtC,oDAAoD;AACpD,eAAO,MAAM,KAAK,EAAG,SAAS,IAAI,EAejC,CAAC;AAEF,iCAAiC;AACjC,wBAAgB,OAAO,CAAC,EAAE,EAAG,MAAM,GAAI,IAAI,CAE1C;AAED,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { aboutQuantity } from "./about-quantity.js";
2
+ import { ingVerb } from "./ing-verb.js";
3
+ import { latinAbbreviation } from "./latin-abbreviation.js";
4
+ import { missingSubjectRule } from "./missing-subject.js";
5
+ import { modalVerb } from "./modal-verb.js";
6
+ import { nounCluster } from "./noun-cluster.js";
7
+ import { paragraphLength } from "./paragraph-length.js";
8
+ import { passiveInstruction } from "./passive-instruction.js";
9
+ import { firstPerson, genderedPronoun } from "./pronouns.js";
10
+ import { semicolon } from "./semicolon.js";
11
+ import { sentenceLength } from "./sentence-length.js";
12
+ import { unknownWord } from "./unknown-word.js";
13
+ import { wordList } from "./word-list.js";
14
+ /** All rules, in the order of the documentation. */
15
+ export const RULES = [
16
+ sentenceLength,
17
+ paragraphLength,
18
+ modalVerb,
19
+ semicolon,
20
+ latinAbbreviation,
21
+ aboutQuantity,
22
+ wordList,
23
+ genderedPronoun,
24
+ firstPerson,
25
+ passiveInstruction,
26
+ nounCluster,
27
+ missingSubjectRule,
28
+ ingVerb,
29
+ unknownWord,
30
+ ];
31
+ /** The rule with the ID `id`. */
32
+ export function getRule(id) {
33
+ return RULES.find((rule) => rule.id === id);
34
+ }
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAE7D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,oDAAoD;AACpD,MAAM,CAAC,MAAM,KAAK,GAAqB;IACnC,cAAc;IACd,eAAe;IACf,SAAS;IACT,SAAS;IACT,iBAAiB;IACjB,aAAa;IACb,QAAQ;IACR,eAAe;IACf,WAAW;IACX,kBAAkB;IAClB,WAAW;IACX,kBAAkB;IAClB,OAAO;IACP,WAAW;CACd,CAAC;AAEF,iCAAiC;AACjC,MAAM,UAAU,OAAO,CAAC,EAAW;IAC/B,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAE,CAAC;AACjD,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { PhraseMatcher } from "../text/phrases.js";
2
+ import { type Token } from "../text/tokens.js";
3
+ import { type Rule } from "./rule.js";
4
+ /** The token indices that a multi-word glossary phrase covers. */
5
+ export declare function coveredByPhrases(tokens: readonly Token[], phrases: PhraseMatcher): Set<number>;
6
+ /** The base form of an -ing word when it is a known verb ("running" gives "run"), or null. */
7
+ export declare function baseForm(word: string, verbs: ReadonlySet<string>): string | null;
8
+ export declare const ingVerb: Rule;
9
+ //# sourceMappingURL=ing-verb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ing-verb.d.ts","sourceRoot":"","sources":["../../src/rules/ing-verb.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAqB,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAElE,OAAO,EAA4B,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAEhE,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,MAAM,EAAG,SAAS,KAAK,EAAE,EAAE,OAAO,EAAG,aAAa,GAAI,GAAG,CAAC,MAAM,CAAC,CAQjG;AAED,8FAA8F;AAC9F,wBAAgB,QAAQ,CAAC,IAAI,EAAG,MAAM,EAAE,KAAK,EAAG,WAAW,CAAC,MAAM,CAAC,GAAI,MAAM,GAAG,IAAI,CAEnF;AAED,eAAO,MAAM,OAAO,EAAG,IA+BtB,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { isIngVerbUse, lemmas } from "../text/grammar.js";
2
+ import { isCounted, isWord } from "../text/tokens.js";
3
+ import { NON_VERB_ING } from "../text/words.js";
4
+ import { forEachToken, tokenRange } from "./rule.js";
5
+ /** The token indices that a multi-word glossary phrase covers. */
6
+ export function coveredByPhrases(tokens, phrases) {
7
+ const covered = new Set();
8
+ if (phrases.empty)
9
+ return covered;
10
+ for (let i = 0; i < tokens.length; i++) {
11
+ const length = phrases.longestMatch(tokens, i);
12
+ if (length > 1)
13
+ for (let k = i; k < i + length; k++)
14
+ covered.add(k);
15
+ }
16
+ return covered;
17
+ }
18
+ /** The base form of an -ing word when it is a known verb ("running" gives "run"), or null. */
19
+ export function baseForm(word, verbs) {
20
+ return lemmas(word).find((form) => form !== word && verbs.has(form)) ?? null;
21
+ }
22
+ export const ingVerb = {
23
+ id: "ing-verb",
24
+ description: "Find -ing forms used as verbs. Headings, technical nouns and allowed words are not examined.",
25
+ ste: "3.2, 3.5",
26
+ defaultSeverity: "warning",
27
+ scope: "word",
28
+ check(unit, context) {
29
+ if (unit.kind === "heading")
30
+ return;
31
+ const { lexicon, wordList } = context;
32
+ let covered = new Set();
33
+ let current = null;
34
+ forEachToken(unit, (tokens, i) => {
35
+ if (tokens !== current) {
36
+ current = tokens;
37
+ covered = coveredByPhrases(tokens, lexicon.technicalNounPhrases);
38
+ }
39
+ const token = tokens[i];
40
+ if (!isWord(token) || !isCounted(token))
41
+ return;
42
+ const lower = token.lower;
43
+ if (lower.length < 5 || !/^[a-z]+ing$/.test(lower))
44
+ return;
45
+ if (NON_VERB_ING.has(lower) || lexicon.technicalNouns.has(lower) || lexicon.properNouns.has(lower) || lexicon.allowedWords.has(lower))
46
+ return;
47
+ if (wordList.words.has(lower) || covered.has(i))
48
+ return;
49
+ if (!isIngVerbUse(tokens, i))
50
+ return;
51
+ const base = baseForm(lower, lexicon.verbs);
52
+ const example = base === null ? "" : `, for example "when you ${base} ..." or "to ${base} ..."`;
53
+ context.report({
54
+ ...tokenRange(tokens, i, i + 1),
55
+ message: `Do not use "${token.text}" as a verb. Use a simple tense, the infinitive or a noun${example}. If "${lower}" is a technical noun, add it to technicalNouns.`,
56
+ });
57
+ });
58
+ },
59
+ };
60
+ //# sourceMappingURL=ing-verb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ing-verb.js","sourceRoot":"","sources":["../../src/rules/ing-verb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,EAAE,SAAS,EAAE,MAAM,EAAc,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAa,MAAM,WAAW,CAAC;AAEhE,kEAAkE;AAClE,MAAM,UAAU,gBAAgB,CAAC,MAAyB,EAAE,OAAuB;IAC/E,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,IAAI,OAAO,CAAC,KAAK;QAAE,OAAO,OAAO,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/C,IAAI,MAAM,GAAG,CAAC;YAAE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE;gBAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,QAAQ,CAAC,IAAa,EAAE,KAA2B;IAC/D,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC;AACjF,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAU;IAC1B,EAAE,EAAG,UAAU;IACf,WAAW,EAAG,8FAA8F;IAC5G,GAAG,EAAG,UAAU;IAChB,eAAe,EAAG,SAAS;IAC3B,KAAK,EAAG,MAAM;IACd,KAAK,CAAC,IAAI,EAAE,OAAO;QACf,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO;QACpC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QACtC,IAAI,OAAO,GAAiB,IAAI,GAAG,EAAE,CAAC;QACtC,IAAI,OAAO,GAA6B,IAAI,CAAC;QAC7C,YAAY,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YAC7B,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;gBACrB,OAAO,GAAG,MAAM,CAAC;gBACjB,OAAO,GAAG,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;YACrE,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;gBAAE,OAAO;YAChD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;YAC1B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC;gBAAE,OAAO;YAC3D,IAAI,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,OAAO;YAC9I,IAAI,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,OAAO;YACxD,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;gBAAE,OAAO;YACrC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YAC5C,MAAM,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,2BAA2B,IAAI,gBAAgB,IAAI,OAAO,CAAC;YAChG,OAAO,CAAC,MAAM,CAAC;gBACX,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;gBAC/B,OAAO,EAAG,eAAe,KAAK,CAAC,IAAI,4DAA4D,OAAO,SAAS,KAAK,kDAAkD;aACzK,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;CACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { type Rule } from "./rule.js";
2
+ export declare const latinAbbreviation: Rule;
3
+ //# sourceMappingURL=latin-abbreviation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"latin-abbreviation.d.ts","sourceRoot":"","sources":["../../src/rules/latin-abbreviation.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAiBhE,eAAO,MAAM,iBAAiB,EAAG,IA2BhC,CAAC"}
@@ -0,0 +1,48 @@
1
+ import { isWord } from "../text/tokens.js";
2
+ import { forEachToken, tokenRange } from "./rule.js";
3
+ // A map, not an object literal, so that a word such as "constructor" does not find an Object property.
4
+ const HELP = new Map([
5
+ ["e.g", { name: "e.g.", help: "Write \"for example\"." }],
6
+ ["eg", { name: "eg.", help: "Write \"for example\"." }],
7
+ ["i.e", { name: "i.e.", help: "Write \"that is\", or write a separate sentence." }],
8
+ ["ie", { name: "ie.", help: "Write \"that is\", or write a separate sentence." }],
9
+ ["etc", { name: "etc.", help: "Give all the items, or write \"for example\" before the items." }],
10
+ ["vs", { name: "vs.", help: "Write \"compared to\", \"and\" or \"or\"." }],
11
+ ["viz", { name: "viz.", help: "Write \"that is\" or \"for example\"." }],
12
+ ["cf", { name: "cf.", help: "Write \"compare with\" or \"refer to\"." }],
13
+ ]);
14
+ /** Abbreviations that are a problem only with a period after them, because the letters alone can be another word. */
15
+ const NEEDS_PERIOD = new Set(["eg", "ie", "cf"]);
16
+ export const latinAbbreviation = {
17
+ id: "latin-abbreviation",
18
+ description: "Find Latin abbreviations: e.g., i.e., etc., vs., viz., cf. and et al.",
19
+ ste: "GR-6",
20
+ defaultSeverity: "error",
21
+ scope: "word",
22
+ check(unit, context) {
23
+ forEachToken(unit, (tokens, i) => {
24
+ const token = tokens[i];
25
+ if (!isWord(token))
26
+ return;
27
+ const next = tokens[i + 1];
28
+ const period = next !== undefined && next.kind === "punct" && next.text === "." && !next.spaceBefore;
29
+ const end = period ? i + 2 : i + 1;
30
+ if (token.lower === "et" && isWord(next) && next.lower === "al") {
31
+ const after = tokens[i + 2];
32
+ const withPeriod = after !== undefined && after.text === "." && !after.spaceBefore;
33
+ context.report({ ...tokenRange(tokens, i, withPeriod ? i + 3 : i + 2), message: "Do not use \"et al.\". Write \"and others\", or give the names." });
34
+ return;
35
+ }
36
+ const entry = HELP.get(token.lower);
37
+ if (entry === undefined)
38
+ return;
39
+ // "VS" in capital letters is a name (for example "VS Code"), not "versus".
40
+ if (token.lower === "vs" && token.text === "VS")
41
+ return;
42
+ if (NEEDS_PERIOD.has(token.lower) && (!period || token.text !== token.lower))
43
+ return;
44
+ context.report({ ...tokenRange(tokens, i, end), message: `Do not use the Latin abbreviation "${entry.name}". ${entry.help}` });
45
+ });
46
+ },
47
+ };
48
+ //# sourceMappingURL=latin-abbreviation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"latin-abbreviation.js","sourceRoot":"","sources":["../../src/rules/latin-abbreviation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Lexicon } from "../lexicon.js";
2
+ import { type Token } from "../text/tokens.js";
3
+ import { type Rule } from "./rule.js";
4
+ /** The message when the sentence at `tokens` starts without a subject, or null. */
5
+ export declare function missingSubject(tokens: readonly Token[], lexicon: Lexicon): {
6
+ index: number;
7
+ message: string;
8
+ } | null;
9
+ /**
10
+ * The rule examines the summary and the `@remarks` text of doc comments.
11
+ * For example, "Returns the value." and "Called when the page is hidden."
12
+ * omit the subject. Text after `@param` and `@returns`, and list items, can
13
+ * be fragments, so the rule does not examine them.
14
+ */
15
+ export declare const missingSubjectRule: Rule;
16
+ //# sourceMappingURL=missing-subject.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-subject.d.ts","sourceRoot":"","sources":["../../src/rules/missing-subject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAoC,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAEjF,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AA6ElD,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,MAAM,EAAG,SAAS,KAAK,EAAE,EAAE,OAAO,EAAG,OAAO,GAAI;IAAE,KAAK,EAAG,MAAM,CAAC;IAAC,OAAO,EAAG,MAAM,CAAA;CAAE,GAAG,IAAI,CAuBzH;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAG,IAejC,CAAC"}
@@ -0,0 +1,138 @@
1
+ import { isParticiple, nextCounted, startsObject } from "../text/grammar.js";
2
+ import { isCapitalized, isCounted, isWord } from "../text/tokens.js";
3
+ import { BE_FORMS, CONJUNCTIONS, DETERMINERS, HAVE_FORMS, MODALS, PREPOSITIONS, SUBJECT_PRONOUNS } from "../text/words.js";
4
+ import { tokenRange } from "./rule.js";
5
+ /** Auxiliary verbs that start a sentence without a subject: "Is true when ...", "Can be null.". */
6
+ const AUXILIARY_STARTS = new Set(["is", "are", "was", "were", "has", "have", "had", "does", "can", "cannot", "must", "will", "should", "may", "might", "would", "could"]);
7
+ /** Words that end in -s but are not verbs. */
8
+ const NOT_VERBS = new Set([
9
+ "this", "its", "always", "perhaps", "sometimes", "whereas", "unless", "plus", "thus", "yes", "less", "across",
10
+ "towards", "afterwards", "besides", "nevertheless", "regardless", "various", "numerous", "previous", "analysis",
11
+ "basis", "bias", "canvas", "status", "series", "species", "atlas", "alias", "chaos", "corpus", "focus", "bonus",
12
+ ]);
13
+ /** Words after a participle that show a missing subject: "Called when ...", "Used by ...". */
14
+ const AFTER_PARTICIPLE = new Set([...PREPOSITIONS, "when", "if", "once", "only", "while", "as", "whenever", "unless"]);
15
+ function stems(word) {
16
+ const forms = [word.slice(0, -1)];
17
+ if (word.endsWith("es"))
18
+ forms.push(word.slice(0, -2));
19
+ if (word.endsWith("ies"))
20
+ forms.push(word.slice(0, -3) + "y");
21
+ return forms;
22
+ }
23
+ /** True when a comma comes later and a subject comes after it: "Based on X, the monitor ...". */
24
+ function hasLaterSubject(tokens, from) {
25
+ for (let i = from; i < tokens.length; i++) {
26
+ const token = tokens[i];
27
+ if (token.kind !== "punct" || token.text !== ",")
28
+ continue;
29
+ const next = tokens[nextCounted(tokens, i + 1)];
30
+ if (next === undefined)
31
+ return false;
32
+ return next.kind === "code" || isCapitalized(next) || (isWord(next) && (DETERMINERS.has(next.lower) || SUBJECT_PRONOUNS.has(next.lower)));
33
+ }
34
+ return false;
35
+ }
36
+ const PLURAL_AUXILIARIES = new Set(["are", "were", "have", "do", "don't", "aren't", "weren't", "can", "cannot", "must", "will"]);
37
+ /**
38
+ * True when a verb for a plural subject comes later, after a noun:
39
+ * "Samples at or above this value wait ...", "Values below zero are ...".
40
+ * Then the first word is a plural noun, not a verb.
41
+ */
42
+ function hasPluralVerbLater(tokens, from, lexicon) {
43
+ for (let i = from; i < tokens.length; i++) {
44
+ const token = tokens[i];
45
+ const previous = tokens[i - 1];
46
+ if (!isWord(token) || !isWord(previous))
47
+ continue;
48
+ if (DETERMINERS.has(previous.lower) || PREPOSITIONS.has(previous.lower) || MODALS.has(previous.lower) || previous.lower === "not")
49
+ continue;
50
+ if (PLURAL_AUXILIARIES.has(token.lower))
51
+ return true;
52
+ if (lexicon.verbs.has(token.lower) && !token.lower.endsWith("s"))
53
+ return true;
54
+ }
55
+ return false;
56
+ }
57
+ /** Relative pronouns. A word before one of them is a noun: "Tags whose content ...". */
58
+ const RELATIVE_PRONOUNS = new Set(["whose", "which", "who", "whom"]);
59
+ /**
60
+ * True when the words after the first word show that the first word is a
61
+ * plural noun, not a verb. These words show a noun:
62
+ *
63
+ * - An auxiliary verb, a conjunction or "of": "Values are ...", "Values of ..."
64
+ * - "per" or a relative pronoun: "Samples per round", "Tags whose ..."
65
+ * - A base form of a verb: "Monitors ask ..."
66
+ * - A participle and a preposition: "Handles returned by ...".
67
+ */
68
+ function isNounSubject(tokens, index, lexicon) {
69
+ const next = tokens[index + 1];
70
+ if (!isWord(next))
71
+ return false;
72
+ const lower = next.lower;
73
+ if (BE_FORMS.has(lower) || MODALS.has(lower) || HAVE_FORMS.has(lower) || CONJUNCTIONS.has(lower) || lower === "of")
74
+ return true;
75
+ if (lower === "per" || RELATIVE_PRONOUNS.has(lower))
76
+ return true;
77
+ const after = tokens[index + 2];
78
+ const objectAfter = after === undefined || after.kind === "punct" || startsObject(after) || (isWord(after) && PREPOSITIONS.has(after.lower));
79
+ if (lexicon.verbs.has(lower) && !lower.endsWith("s") && !DETERMINERS.has(lower) && objectAfter)
80
+ return true;
81
+ return isParticiple(lower) && isWord(after) && PREPOSITIONS.has(after.lower);
82
+ }
83
+ /** The message when the sentence at `tokens` starts without a subject, or null. */
84
+ export function missingSubject(tokens, lexicon) {
85
+ const k = nextCounted(tokens, 0);
86
+ const first = tokens[k];
87
+ if (!isWord(first) || !isCapitalized(first))
88
+ return null;
89
+ const lower = first.lower;
90
+ const next = tokens[k + 1];
91
+ const last = [...tokens].reverse().find((token) => token.kind === "punct");
92
+ if (AUXILIARY_STARTS.has(lower)) {
93
+ if (last?.text === "?")
94
+ return null;
95
+ return { index: k, message: `This sentence has no subject. Write the subject before "${first.text}", for example "This value ${lower} ...".` };
96
+ }
97
+ if (/^[a-z]{3,}s$/.test(lower) && !/(ss|us|is|ous|ics|ness)$/.test(lower) && !NOT_VERBS.has(lower)) {
98
+ const knownVerb = stems(lower).some((stem) => lexicon.verbs.has(stem));
99
+ if (next?.text === "," || isNounSubject(tokens, k, lexicon))
100
+ return null;
101
+ if (!knownVerb && !startsObject(next))
102
+ return null;
103
+ if (isWord(next) && PREPOSITIONS.has(next.lower) && hasPluralVerbLater(tokens, k + 2, lexicon))
104
+ return null;
105
+ const example = lower === "returns" ? "This function gives ..." : `This function ${lower} ...`;
106
+ return { index: k, message: `"${first.text}" has no subject. Do not omit the subject. Write a full sentence, for example "${example}".` };
107
+ }
108
+ if (isParticiple(lower) && isWord(next) && AFTER_PARTICIPLE.has(next.lower) && !hasLaterSubject(tokens, k + 1)) {
109
+ return { index: k, message: `"${first.text}" starts a sentence that has no subject. Write a full sentence with a subject in the active voice, for example "The monitor uses this value when ...".` };
110
+ }
111
+ return null;
112
+ }
113
+ /**
114
+ * The rule examines the summary and the `@remarks` text of doc comments.
115
+ * For example, "Returns the value." and "Called when the page is hidden."
116
+ * omit the subject. Text after `@param` and `@returns`, and list items, can
117
+ * be fragments, so the rule does not examine them.
118
+ */
119
+ export const missingSubjectRule = {
120
+ id: "missing-subject",
121
+ description: "Find doc comment sentences that have no subject, for example \"Returns the value.\".",
122
+ ste: "4.2",
123
+ defaultSeverity: "error",
124
+ scope: "sentence",
125
+ check(unit, context) {
126
+ if (unit.origin.source !== "tsdoc" || unit.origin.tsdocSection === "tag" || unit.list !== null)
127
+ return;
128
+ for (const sentence of unit.sentences) {
129
+ if (!sentence.tokens.some(isCounted))
130
+ continue;
131
+ const found = missingSubject(sentence.tokens, context.lexicon);
132
+ if (found === null)
133
+ continue;
134
+ context.report({ ...tokenRange(sentence.tokens, found.index, found.index + 1), message: found.message });
135
+ }
136
+ },
137
+ };
138
+ //# sourceMappingURL=missing-subject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-subject.js","sourceRoot":"","sources":["../../src/rules/missing-subject.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type Rule } from "./rule.js";
2
+ export declare const modalVerb: Rule;
3
+ //# sourceMappingURL=modal-verb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modal-verb.d.ts","sourceRoot":"","sources":["../../src/rules/modal-verb.ts"],"names":[],"mappings":"AAGA,OAAO,EAAqC,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AA0CzE,eAAO,MAAM,SAAS,EAAG,IA0BxB,CAAC"}
@@ -0,0 +1,72 @@
1
+ import { isClauseStart } from "../text/grammar.js";
2
+ import { isNumber, isWord } from "../text/tokens.js";
3
+ import { DETERMINERS } from "../text/words.js";
4
+ import { forEachToken, surface, tokenRange } from "./rule.js";
5
+ // These tables are maps, not object literals, so a word such as "constructor" does not find an Object property.
6
+ const SINGLE = new Map([
7
+ ["should", "Write \"must\" for an obligation, or \"if\" for a condition (\"If X occurs, ...\")."],
8
+ ["shouldn", "Write \"must not\" for an obligation."],
9
+ ["may", "Write \"can\" for a possibility or a permission, or \"possibly\"."],
10
+ ["might", "Write \"can\" or \"possibly\"."],
11
+ ["would", "Write \"can\", or use a simple tense (\"is\", \"gives\")."],
12
+ ["wouldn", "Write \"cannot\", or use a simple tense (\"is not\")."],
13
+ ["shall", "Write \"must\"."],
14
+ ]);
15
+ const CONTRACTIONS = new Map([
16
+ ["shouldn't", "shouldn"],
17
+ ["wouldn't", "wouldn"],
18
+ ["mightn't", "might"],
19
+ ["shan't", "shall"],
20
+ ["mayn't", "may"],
21
+ ]);
22
+ const IMPERATIVE_HELP = "Write the instruction in the imperative (\"Do X\"), or use \"must\".";
23
+ const NECESSARY_HELP = "Write the instruction in the imperative (\"Do X\"), or use \"must\" or \"it is necessary to\".";
24
+ const WITH_TO = new Map([
25
+ ["ought", "Write \"must\"."],
26
+ ["have", IMPERATIVE_HELP],
27
+ ["has", IMPERATIVE_HELP],
28
+ ["had", "Use the simple past tense of the verb, or \"must\"."],
29
+ ["having", "Rewrite the sentence with \"must\" or with the imperative."],
30
+ ["need", NECESSARY_HELP],
31
+ ["needs", NECESSARY_HELP],
32
+ ["needed", "Write \"it was necessary to\", or rewrite the sentence."],
33
+ ]);
34
+ /** True when "May" is the month: "May 2026", "3 May". */
35
+ function isMonth(tokens, index) {
36
+ const token = tokens[index];
37
+ if (token.text !== "May")
38
+ return false;
39
+ return !isClauseStart(tokens, index) || isNumber(tokens[index + 1]) || isNumber(tokens[index - 1]);
40
+ }
41
+ export const modalVerb = {
42
+ id: "modal-verb",
43
+ description: "Find modal verbs that are not approved: should, may, might, would, shall, ought to, have to, need to.",
44
+ ste: "3.1, 3.4, 5.3",
45
+ defaultSeverity: "error",
46
+ scope: "word",
47
+ check(unit, context) {
48
+ forEachToken(unit, (tokens, i) => {
49
+ const token = tokens[i];
50
+ if (!isWord(token))
51
+ return;
52
+ const key = CONTRACTIONS.get(token.lower) ?? token.lower;
53
+ const help = SINGLE.get(key);
54
+ if (help !== undefined) {
55
+ if (key === "may" && isMonth(tokens, i))
56
+ return;
57
+ context.report({ ...tokenRange(tokens, i, i + 1), message: `Do not use the modal verb "${token.text}". ${help}` });
58
+ return;
59
+ }
60
+ const withTo = WITH_TO.get(key);
61
+ const next = tokens[i + 1];
62
+ if (withTo === undefined || !isWord(next) || next.lower !== "to")
63
+ return;
64
+ const previous = tokens[i - 1];
65
+ // "the need to ..." is a noun phrase, not a modal verb.
66
+ if (key.startsWith("need") && isWord(previous) && DETERMINERS.has(previous.lower))
67
+ return;
68
+ context.report({ ...tokenRange(tokens, i, i + 2), message: `Do not use "${surface(tokens, i, i + 2)}". ${withTo}` });
69
+ });
70
+ },
71
+ };
72
+ //# sourceMappingURL=modal-verb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modal-verb.js","sourceRoot":"","sources":["../../src/rules/modal-verb.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { type Rule } from "./rule.js";
2
+ /**
3
+ * The rule counts a group of nouns in sequence, with no article,
4
+ * preposition or punctuation between them. Only these known nouns count:
5
+ *
6
+ * - Technical nouns and proper nouns from the glossary
7
+ * - Nouns from the dictionary
8
+ * - A built-in list of common software nouns
9
+ * - Abbreviations and code.
10
+ *
11
+ * Only the last noun of a group can be plural. Without a part-of-speech
12
+ * tagger, the rule cannot know if other words are nouns, so it does not
13
+ * count them. Thus it misses some clusters, but it seldom reports a false
14
+ * one.
15
+ */
16
+ export declare const nounCluster: Rule;
17
+ //# sourceMappingURL=noun-cluster.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noun-cluster.d.ts","sourceRoot":"","sources":["../../src/rules/noun-cluster.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAiC3D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,WAAW,EAAG,IAgC1B,CAAC"}
@@ -0,0 +1,83 @@
1
+ import { isAbbreviation, isWord } from "../text/tokens.js";
2
+ import { surface, tokenRange } from "./rule.js";
3
+ function singular(word) {
4
+ const forms = [];
5
+ if (word.endsWith("ies"))
6
+ forms.push(word.slice(0, -3) + "y");
7
+ if (word.endsWith("es"))
8
+ forms.push(word.slice(0, -2));
9
+ if (word.endsWith("s"))
10
+ forms.push(word.slice(0, -1));
11
+ return forms;
12
+ }
13
+ /** The noun at `tokens[index]`, or null when the token is not a known noun. */
14
+ function nounAt(tokens, index, lexicon) {
15
+ const token = tokens[index];
16
+ if (token === undefined)
17
+ return null;
18
+ if (token.kind === "code") {
19
+ const kind = token.placeholder?.kind;
20
+ return kind === "code" || kind === "link" ? { length: 1, plural: false } : null;
21
+ }
22
+ if (!isWord(token))
23
+ return null;
24
+ const phrase = lexicon.technicalNounPhrases.longestMatch(tokens, index);
25
+ if (phrase > 1)
26
+ return { length: phrase, plural: tokens[index + phrase - 1].lower.endsWith("s") };
27
+ if (isAbbreviation(token))
28
+ return { length: 1, plural: /[a-z]s$/.test(token.text) };
29
+ if (lexicon.nouns.has(token.lower))
30
+ return { length: 1, plural: false };
31
+ if (singular(token.lower).some((form) => lexicon.nouns.has(form)))
32
+ return { length: 1, plural: true };
33
+ return null;
34
+ }
35
+ /**
36
+ * The rule counts a group of nouns in sequence, with no article,
37
+ * preposition or punctuation between them. Only these known nouns count:
38
+ *
39
+ * - Technical nouns and proper nouns from the glossary
40
+ * - Nouns from the dictionary
41
+ * - A built-in list of common software nouns
42
+ * - Abbreviations and code.
43
+ *
44
+ * Only the last noun of a group can be plural. Without a part-of-speech
45
+ * tagger, the rule cannot know if other words are nouns, so it does not
46
+ * count them. Thus it misses some clusters, but it seldom reports a false
47
+ * one.
48
+ */
49
+ export const nounCluster = {
50
+ id: "noun-cluster",
51
+ description: "Find groups of more than 3 nouns in sequence (noun clusters).",
52
+ ste: "2.1",
53
+ defaultSeverity: "warning",
54
+ scope: "word",
55
+ check(unit, context) {
56
+ const max = context.config.limits.nounClusterNouns;
57
+ for (const sentence of unit.sentences) {
58
+ const tokens = sentence.tokens;
59
+ let i = 0;
60
+ while (i < tokens.length) {
61
+ let count = 0;
62
+ let j = i;
63
+ let plural = false;
64
+ while (!plural) {
65
+ const noun = nounAt(tokens, j, context.lexicon);
66
+ if (noun === null)
67
+ break;
68
+ count++;
69
+ j += noun.length;
70
+ plural = noun.plural;
71
+ }
72
+ if (count > max) {
73
+ context.report({
74
+ ...tokenRange(tokens, i, j),
75
+ message: `"${surface(tokens, i, j)}" has ${count} nouns in sequence. Use no more than ${max}. Use a preposition to divide the group, for example "the interval of the heartbeat" instead of "heartbeat interval".`,
76
+ });
77
+ }
78
+ i = count > 0 ? j : i + 1;
79
+ }
80
+ }
81
+ },
82
+ };
83
+ //# sourceMappingURL=noun-cluster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noun-cluster.js","sourceRoot":"","sources":["../../src/rules/noun-cluster.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from "./rule.js";
2
+ export declare const paragraphLength: Rule;
3
+ //# sourceMappingURL=paragraph-length.d.ts.map