@mark1russell7/ste-lint 0.0.0-stage → 0.1.2
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/README.md +246 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +9 -0
- package/dist/cli.js.map +1 -0
- package/dist/config-core.d.ts +92 -0
- package/dist/config-core.d.ts.map +1 -0
- package/dist/config-core.js +198 -0
- package/dist/config-core.js.map +1 -0
- package/dist/config.d.ts +29 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +68 -0
- package/dist/config.js.map +1 -0
- package/dist/core.d.ts +16 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +16 -0
- package/dist/core.js.map +1 -0
- package/dist/dictionary-core.d.ts +72 -0
- package/dist/dictionary-core.d.ts.map +1 -0
- package/dist/dictionary-core.js +150 -0
- package/dist/dictionary-core.js.map +1 -0
- package/dist/dictionary.d.ts +9 -0
- package/dist/dictionary.d.ts.map +1 -0
- package/dist/dictionary.js +26 -0
- package/dist/dictionary.js.map +1 -0
- package/dist/engine.d.ts +44 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +122 -0
- package/dist/engine.js.map +1 -0
- package/dist/files.d.ts +35 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +181 -0
- package/dist/files.js.map +1 -0
- package/dist/format.d.ts +21 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +61 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/lexicon.d.ts +33 -0
- package/dist/lexicon.d.ts.map +1 -0
- package/dist/lexicon.js +71 -0
- package/dist/lexicon.js.map +1 -0
- package/dist/lint.d.ts +18 -0
- package/dist/lint.d.ts.map +1 -0
- package/dist/lint.js +23 -0
- package/dist/lint.js.map +1 -0
- package/dist/rules/about-quantity.d.ts +3 -0
- package/dist/rules/about-quantity.d.ts.map +1 -0
- package/dist/rules/about-quantity.js +34 -0
- package/dist/rules/about-quantity.js.map +1 -0
- package/dist/rules/index.d.ts +8 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +35 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/ing-verb.d.ts +9 -0
- package/dist/rules/ing-verb.d.ts.map +1 -0
- package/dist/rules/ing-verb.js +60 -0
- package/dist/rules/ing-verb.js.map +1 -0
- package/dist/rules/latin-abbreviation.d.ts +3 -0
- package/dist/rules/latin-abbreviation.d.ts.map +1 -0
- package/dist/rules/latin-abbreviation.js +48 -0
- package/dist/rules/latin-abbreviation.js.map +1 -0
- package/dist/rules/missing-subject.d.ts +16 -0
- package/dist/rules/missing-subject.d.ts.map +1 -0
- package/dist/rules/missing-subject.js +138 -0
- package/dist/rules/missing-subject.js.map +1 -0
- package/dist/rules/modal-verb.d.ts +3 -0
- package/dist/rules/modal-verb.d.ts.map +1 -0
- package/dist/rules/modal-verb.js +72 -0
- package/dist/rules/modal-verb.js.map +1 -0
- package/dist/rules/noun-cluster.d.ts +17 -0
- package/dist/rules/noun-cluster.d.ts.map +1 -0
- package/dist/rules/noun-cluster.js +83 -0
- package/dist/rules/noun-cluster.js.map +1 -0
- package/dist/rules/paragraph-length.d.ts +3 -0
- package/dist/rules/paragraph-length.d.ts.map +1 -0
- package/dist/rules/paragraph-length.js +19 -0
- package/dist/rules/paragraph-length.js.map +1 -0
- package/dist/rules/passive-instruction.d.ts +15 -0
- package/dist/rules/passive-instruction.d.ts.map +1 -0
- package/dist/rules/passive-instruction.js +38 -0
- package/dist/rules/passive-instruction.js.map +1 -0
- package/dist/rules/pronouns.d.ts +4 -0
- package/dist/rules/pronouns.d.ts.map +1 -0
- package/dist/rules/pronouns.js +57 -0
- package/dist/rules/pronouns.js.map +1 -0
- package/dist/rules/rule.d.ts +46 -0
- package/dist/rules/rule.d.ts.map +1 -0
- package/dist/rules/rule.js +31 -0
- package/dist/rules/rule.js.map +1 -0
- package/dist/rules/semicolon.d.ts +3 -0
- package/dist/rules/semicolon.d.ts.map +1 -0
- package/dist/rules/semicolon.js +20 -0
- package/dist/rules/semicolon.js.map +1 -0
- package/dist/rules/sentence-length.d.ts +3 -0
- package/dist/rules/sentence-length.d.ts.map +1 -0
- package/dist/rules/sentence-length.js +31 -0
- package/dist/rules/sentence-length.js.map +1 -0
- package/dist/rules/unknown-word.d.ts +9 -0
- package/dist/rules/unknown-word.d.ts.map +1 -0
- package/dist/rules/unknown-word.js +49 -0
- package/dist/rules/unknown-word.js.map +1 -0
- package/dist/rules/word-list-matcher.d.ts +25 -0
- package/dist/rules/word-list-matcher.d.ts.map +1 -0
- package/dist/rules/word-list-matcher.js +60 -0
- package/dist/rules/word-list-matcher.js.map +1 -0
- package/dist/rules/word-list.d.ts +3 -0
- package/dist/rules/word-list.d.ts.map +1 -0
- package/dist/rules/word-list.js +26 -0
- package/dist/rules/word-list.js.map +1 -0
- package/dist/run-cli.d.ts +27 -0
- package/dist/run-cli.d.ts.map +1 -0
- package/dist/run-cli.js +176 -0
- package/dist/run-cli.js.map +1 -0
- package/dist/source/blocks.d.ts +55 -0
- package/dist/source/blocks.d.ts.map +1 -0
- package/dist/source/blocks.js +464 -0
- package/dist/source/blocks.js.map +1 -0
- package/dist/source/code.d.ts +9 -0
- package/dist/source/code.d.ts.map +1 -0
- package/dist/source/code.js +16 -0
- package/dist/source/code.js.map +1 -0
- package/dist/source/inline.d.ts +42 -0
- package/dist/source/inline.d.ts.map +1 -0
- package/dist/source/inline.js +396 -0
- package/dist/source/inline.js.map +1 -0
- package/dist/source/jsx.d.ts +18 -0
- package/dist/source/jsx.d.ts.map +1 -0
- package/dist/source/jsx.js +137 -0
- package/dist/source/jsx.js.map +1 -0
- package/dist/source/lines.d.ts +34 -0
- package/dist/source/lines.d.ts.map +1 -0
- package/dist/source/lines.js +83 -0
- package/dist/source/lines.js.map +1 -0
- package/dist/source/markdown.d.ts +23 -0
- package/dist/source/markdown.d.ts.map +1 -0
- package/dist/source/markdown.js +42 -0
- package/dist/source/markdown.js.map +1 -0
- package/dist/source/suppressions.d.ts +38 -0
- package/dist/source/suppressions.d.ts.map +1 -0
- package/dist/source/suppressions.js +69 -0
- package/dist/source/suppressions.js.map +1 -0
- package/dist/source/tsdoc.d.ts +40 -0
- package/dist/source/tsdoc.d.ts.map +1 -0
- package/dist/source/tsdoc.js +176 -0
- package/dist/source/tsdoc.js.map +1 -0
- package/dist/text/grammar.d.ts +91 -0
- package/dist/text/grammar.d.ts.map +1 -0
- package/dist/text/grammar.js +289 -0
- package/dist/text/grammar.js.map +1 -0
- package/dist/text/phrases.d.ts +30 -0
- package/dist/text/phrases.d.ts.map +1 -0
- package/dist/text/phrases.js +65 -0
- package/dist/text/phrases.js.map +1 -0
- package/dist/text/sentences.d.ts +45 -0
- package/dist/text/sentences.d.ts.map +1 -0
- package/dist/text/sentences.js +141 -0
- package/dist/text/sentences.js.map +1 -0
- package/dist/text/tokens.d.ts +35 -0
- package/dist/text/tokens.d.ts.map +1 -0
- package/dist/text/tokens.js +113 -0
- package/dist/text/tokens.js.map +1 -0
- package/dist/text/words.d.ts +58 -0
- package/dist/text/words.d.ts.map +1 -0
- package/dist/text/words.js +159 -0
- package/dist/text/words.js.map +1 -0
- package/dist/types.d.ts +18 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +20 -0
- package/dist/types.js.map +1 -0
- package/dist/units.d.ts +47 -0
- package/dist/units.d.ts.map +1 -0
- package/dist/units.js +42 -0
- package/dist/units.js.map +1 -0
- package/dist/word-list.d.ts +34 -0
- package/dist/word-list.d.ts.map +1 -0
- package/dist/word-list.js +90 -0
- package/dist/word-list.js.map +1 -0
- package/package.json +54 -3
- package/src/cli.ts +9 -0
- package/src/config-core.ts +258 -0
- package/src/config.test.ts +188 -0
- package/src/config.ts +83 -0
- package/src/core.test.ts +87 -0
- package/src/core.ts +32 -0
- package/src/dictionary-core.ts +158 -0
- package/src/dictionary.test.ts +82 -0
- package/src/dictionary.ts +26 -0
- package/src/engine.ts +138 -0
- package/src/files.test.ts +107 -0
- package/src/files.ts +191 -0
- package/src/format.ts +78 -0
- package/src/index.test.ts +28 -0
- package/src/index.ts +38 -0
- package/src/lexicon.ts +92 -0
- package/src/lint.test.ts +182 -0
- package/src/lint.ts +30 -0
- package/src/rules/about-quantity.ts +33 -0
- package/src/rules/index.ts +40 -0
- package/src/rules/ing-verb.test.ts +48 -0
- package/src/rules/ing-verb.ts +54 -0
- package/src/rules/latin-abbreviation.ts +46 -0
- package/src/rules/missing-subject.test.ts +53 -0
- package/src/rules/missing-subject.ts +129 -0
- package/src/rules/modal-verb.test.ts +45 -0
- package/src/rules/modal-verb.ts +72 -0
- package/src/rules/noun-cluster.test.ts +49 -0
- package/src/rules/noun-cluster.ts +82 -0
- package/src/rules/paragraph-length.test.ts +30 -0
- package/src/rules/paragraph-length.ts +19 -0
- package/src/rules/passive-instruction.test.ts +38 -0
- package/src/rules/passive-instruction.ts +37 -0
- package/src/rules/pronouns.test.ts +38 -0
- package/src/rules/pronouns.ts +54 -0
- package/src/rules/punctuation.test.ts +59 -0
- package/src/rules/rule.ts +73 -0
- package/src/rules/semicolon.ts +19 -0
- package/src/rules/sentence-length.test.ts +52 -0
- package/src/rules/sentence-length.ts +32 -0
- package/src/rules/unknown-word.test.ts +81 -0
- package/src/rules/unknown-word.ts +43 -0
- package/src/rules/word-list-matcher.ts +68 -0
- package/src/rules/word-list.test.ts +110 -0
- package/src/rules/word-list.ts +25 -0
- package/src/run-cli.test.ts +189 -0
- package/src/run-cli.ts +193 -0
- package/src/source/blocks.ts +534 -0
- package/src/source/code.ts +17 -0
- package/src/source/inline.test.ts +92 -0
- package/src/source/inline.ts +416 -0
- package/src/source/jsx.test.ts +46 -0
- package/src/source/jsx.ts +147 -0
- package/src/source/lines.test.ts +51 -0
- package/src/source/lines.ts +91 -0
- package/src/source/markdown.test.ts +210 -0
- package/src/source/markdown.ts +49 -0
- package/src/source/suppressions.ts +79 -0
- package/src/source/tsdoc.test.ts +137 -0
- package/src/source/tsdoc.ts +189 -0
- package/src/text/grammar.ts +304 -0
- package/src/text/phrases.ts +72 -0
- package/src/text/sentences.ts +148 -0
- package/src/text/text.test.ts +262 -0
- package/src/text/tokens.ts +135 -0
- package/src/text/words.ts +183 -0
- package/src/types.ts +39 -0
- package/src/units.ts +86 -0
- package/src/word-list.ts +128 -0
- package/ste.config.schema.json +87 -0
|
@@ -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":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAc,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAa,MAAM,WAAW,CAAC;AAEzE,gHAAgH;AAChH,MAAM,MAAM,GAAiC,IAAI,GAAG,CAAC;IACjD,CAAC,QAAQ,EAAE,qFAAqF,CAAC;IACjG,CAAC,SAAS,EAAE,uCAAuC,CAAC;IACpD,CAAC,KAAK,EAAE,mEAAmE,CAAC;IAC5E,CAAC,OAAO,EAAE,gCAAgC,CAAC;IAC3C,CAAC,OAAO,EAAE,2DAA2D,CAAC;IACtE,CAAC,QAAQ,EAAE,uDAAuD,CAAC;IACnE,CAAC,OAAO,EAAE,iBAAiB,CAAC;CAC/B,CAAC,CAAC;AAEH,MAAM,YAAY,GAAiC,IAAI,GAAG,CAAC;IACvD,CAAC,WAAW,EAAE,SAAS,CAAC;IACxB,CAAC,UAAU,EAAE,QAAQ,CAAC;IACtB,CAAC,UAAU,EAAE,OAAO,CAAC;IACrB,CAAC,QAAQ,EAAE,OAAO,CAAC;IACnB,CAAC,QAAQ,EAAE,KAAK,CAAC;CACpB,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,sEAAsE,CAAC;AAC/F,MAAM,cAAc,GAAG,gGAAgG,CAAC;AAExH,MAAM,OAAO,GAAiC,IAAI,GAAG,CAAC;IAClD,CAAC,OAAO,EAAE,iBAAiB,CAAC;IAC5B,CAAC,MAAM,EAAE,eAAe,CAAC;IACzB,CAAC,KAAK,EAAE,eAAe,CAAC;IACxB,CAAC,KAAK,EAAE,qDAAqD,CAAC;IAC9D,CAAC,QAAQ,EAAE,4DAA4D,CAAC;IACxE,CAAC,MAAM,EAAE,cAAc,CAAC;IACxB,CAAC,OAAO,EAAE,cAAc,CAAC;IACzB,CAAC,QAAQ,EAAE,yDAAyD,CAAC;CACxE,CAAC,CAAC;AAEH,yDAAyD;AACzD,SAAS,OAAO,CAAC,MAAyB,EAAE,KAAc;IACtD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAE,CAAC;IAC7B,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACvC,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;AACvG,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAU;IAC5B,EAAE,EAAG,YAAY;IACjB,WAAW,EAAG,uGAAuG;IACrH,GAAG,EAAG,eAAe;IACrB,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;gBAAE,OAAO;YAC3B,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC;YACzD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACrB,IAAI,GAAG,KAAK,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;oBAAE,OAAO;gBAChD,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAG,8BAA8B,KAAK,CAAC,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC;gBACpH,OAAO;YACX,CAAC;YACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAChC,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3B,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;gBAAE,OAAO;YACzE,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC/B,wDAAwD;YACxD,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,OAAO;YAC1F,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAG,eAAe,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,MAAM,EAAE,EAAE,CAAC,CAAC;QAC1H,CAAC,CAAC,CAAC;IACP,CAAC;CACJ,CAAC"}
|
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"paragraph-length.d.ts","sourceRoot":"","sources":["../../src/rules/paragraph-length.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,eAAO,MAAM,eAAe,EAAG,IAgB9B,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const paragraphLength = {
|
|
2
|
+
id: "paragraph-length",
|
|
3
|
+
description: "A paragraph can have no more than 6 sentences.",
|
|
4
|
+
ste: "6.6",
|
|
5
|
+
defaultSeverity: "warning",
|
|
6
|
+
scope: "paragraph",
|
|
7
|
+
check(unit, context) {
|
|
8
|
+
const limit = context.config.limits.paragraphSentences;
|
|
9
|
+
const count = unit.sentences.length;
|
|
10
|
+
if (count <= limit)
|
|
11
|
+
return;
|
|
12
|
+
context.report({
|
|
13
|
+
start: unit.start,
|
|
14
|
+
end: unit.end,
|
|
15
|
+
message: `This paragraph has ${count} sentences. Use no more than ${limit} sentences in a paragraph. Divide it into two paragraphs, each with one topic.`,
|
|
16
|
+
});
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=paragraph-length.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paragraph-length.js","sourceRoot":"","sources":["../../src/rules/paragraph-length.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,eAAe,GAAU;IAClC,EAAE,EAAG,kBAAkB;IACvB,WAAW,EAAG,gDAAgD;IAC9D,GAAG,EAAG,KAAK;IACX,eAAe,EAAG,SAAS;IAC3B,KAAK,EAAG,WAAW;IACnB,KAAK,CAAC,IAAI,EAAE,OAAO;QACf,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC;QACvD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;QACpC,IAAI,KAAK,IAAI,KAAK;YAAE,OAAO;QAC3B,OAAO,CAAC,MAAM,CAAC;YACX,KAAK,EAAG,IAAI,CAAC,KAAK;YAClB,GAAG,EAAG,IAAI,CAAC,GAAG;YACd,OAAO,EAAG,sBAAsB,KAAK,gCAAgC,KAAK,gFAAgF;SAC7J,CAAC,CAAC;IACP,CAAC;CACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Rule } from "./rule.js";
|
|
2
|
+
/**
|
|
3
|
+
* The rule examines these constructions:
|
|
4
|
+
*
|
|
5
|
+
* - An obligation in the passive voice ("must be set", "has to be called",
|
|
6
|
+
* "needs to be installed") in any sentence. This is an instruction in a
|
|
7
|
+
* passive form.
|
|
8
|
+
* - Any passive ("is cleared", "are triggered by CI") in a procedural
|
|
9
|
+
* sentence: an imperative sentence or an item of an ordered list. A
|
|
10
|
+
* participle after "be" in a condition clause ("if the file is deleted",
|
|
11
|
+
* "make sure that the server is started") shows a state, so the rule
|
|
12
|
+
* ignores it.
|
|
13
|
+
*/
|
|
14
|
+
export declare const passiveInstruction: Rule;
|
|
15
|
+
//# sourceMappingURL=passive-instruction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passive-instruction.d.ts","sourceRoot":"","sources":["../../src/rules/passive-instruction.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAE3D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,EAAG,IAqBjC,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { findPassives } from "../text/grammar.js";
|
|
2
|
+
import { surface, tokenRange } from "./rule.js";
|
|
3
|
+
/**
|
|
4
|
+
* The rule examines these constructions:
|
|
5
|
+
*
|
|
6
|
+
* - An obligation in the passive voice ("must be set", "has to be called",
|
|
7
|
+
* "needs to be installed") in any sentence. This is an instruction in a
|
|
8
|
+
* passive form.
|
|
9
|
+
* - Any passive ("is cleared", "are triggered by CI") in a procedural
|
|
10
|
+
* sentence: an imperative sentence or an item of an ordered list. A
|
|
11
|
+
* participle after "be" in a condition clause ("if the file is deleted",
|
|
12
|
+
* "make sure that the server is started") shows a state, so the rule
|
|
13
|
+
* ignores it.
|
|
14
|
+
*/
|
|
15
|
+
export const passiveInstruction = {
|
|
16
|
+
id: "passive-instruction",
|
|
17
|
+
description: "Find the passive voice in instructions. An instruction uses the active voice and the imperative.",
|
|
18
|
+
ste: "3.4, 3.6, 5.3",
|
|
19
|
+
defaultSeverity: "warning",
|
|
20
|
+
scope: "sentence",
|
|
21
|
+
check(unit, context) {
|
|
22
|
+
for (const sentence of unit.sentences) {
|
|
23
|
+
const procedural = sentence.instruction || unit.list?.ordered === true;
|
|
24
|
+
for (const passive of findPassives(sentence.tokens)) {
|
|
25
|
+
if (!passive.obligation && !(procedural && !passive.inCondition))
|
|
26
|
+
continue;
|
|
27
|
+
const from = passive.start;
|
|
28
|
+
const to = passive.participle + 1;
|
|
29
|
+
const phrase = surface(sentence.tokens, from, to);
|
|
30
|
+
const message = passive.obligation
|
|
31
|
+
? `"${phrase}" is an instruction in the passive voice. Write the instruction in the imperative, for example "Set the value before you start the monitor."`
|
|
32
|
+
: `"${phrase}" is in the passive voice. In an instruction, use the active voice: write the imperative, or tell who or what does the action.`;
|
|
33
|
+
context.report({ ...tokenRange(sentence.tokens, from, to), message });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=passive-instruction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passive-instruction.js","sourceRoot":"","sources":["../../src/rules/passive-instruction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAa,MAAM,WAAW,CAAC;AAE3D;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAU;IACrC,EAAE,EAAG,qBAAqB;IAC1B,WAAW,EAAG,kGAAkG;IAChH,GAAG,EAAG,eAAe;IACrB,eAAe,EAAG,SAAS;IAC3B,KAAK,EAAG,UAAU;IAClB,KAAK,CAAC,IAAI,EAAE,OAAO;QACf,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpC,MAAM,UAAU,GAAG,QAAQ,CAAC,WAAW,IAAI,IAAI,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;YACvE,KAAK,MAAM,OAAO,IAAI,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClD,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;oBAAE,SAAS;gBAC3E,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC;gBAC3B,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,GAAG,CAAC,CAAC;gBAClC,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;gBAClD,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU;oBAC9B,CAAC,CAAC,IAAI,MAAM,8IAA8I;oBAC1J,CAAC,CAAC,IAAI,MAAM,gIAAgI,CAAC;gBACjJ,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;YAC1E,CAAC;QACL,CAAC;IACL,CAAC;CACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pronouns.d.ts","sourceRoot":"","sources":["../../src/rules/pronouns.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAOhE,eAAO,MAAM,eAAe,EAAG,IAgB9B,CAAC;AAKF,eAAO,MAAM,WAAW,EAAG,IAwB1B,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { isWord } from "../text/tokens.js";
|
|
2
|
+
import { forEachToken, tokenRange } from "./rule.js";
|
|
3
|
+
const GENDERED = new Set([
|
|
4
|
+
"he", "she", "his", "her", "him", "hers", "himself", "herself",
|
|
5
|
+
"he's", "she's", "he'd", "she'd", "he'll", "she'll",
|
|
6
|
+
]);
|
|
7
|
+
export const genderedPronoun = {
|
|
8
|
+
id: "gendered-pronoun",
|
|
9
|
+
description: "Find gendered pronouns: he, she, his, her, him, hers, himself and herself.",
|
|
10
|
+
ste: "GR-3, GR-7",
|
|
11
|
+
defaultSeverity: "error",
|
|
12
|
+
scope: "word",
|
|
13
|
+
check(unit, context) {
|
|
14
|
+
forEachToken(unit, (tokens, i) => {
|
|
15
|
+
const token = tokens[i];
|
|
16
|
+
if (!isWord(token) || !GENDERED.has(token.lower))
|
|
17
|
+
return;
|
|
18
|
+
context.report({
|
|
19
|
+
...tokenRange(tokens, i, i + 1),
|
|
20
|
+
message: `Do not use the gendered pronoun "${token.text}". Write "they" or "it", or write the noun again.`,
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
const SINGULAR = new Set(["i", "me", "my", "mine", "myself", "i'm", "i've", "i'll", "i'd"]);
|
|
26
|
+
const PLURAL = new Set(["our", "ours", "ourselves"]);
|
|
27
|
+
export const firstPerson = {
|
|
28
|
+
id: "first-person",
|
|
29
|
+
description: "Find first-person pronouns other than \"we\": I, me, my, our and us. Use \"we\" only for the organization that writes the text.",
|
|
30
|
+
ste: "GR-3",
|
|
31
|
+
defaultSeverity: "warning",
|
|
32
|
+
scope: "word",
|
|
33
|
+
check(unit, context) {
|
|
34
|
+
forEachToken(unit, (tokens, i) => {
|
|
35
|
+
const token = tokens[i];
|
|
36
|
+
if (!isWord(token))
|
|
37
|
+
return;
|
|
38
|
+
const lower = token.lower;
|
|
39
|
+
let message = null;
|
|
40
|
+
if (SINGULAR.has(lower)) {
|
|
41
|
+
message = `Do not use "${token.text}". Write "you" for the reader, or "we" for the organization that writes the text.`;
|
|
42
|
+
}
|
|
43
|
+
else if (PLURAL.has(lower)) {
|
|
44
|
+
message = `Do not use "${token.text}". Write the name of the project or the product, or rewrite the sentence with "we" as the subject.`;
|
|
45
|
+
}
|
|
46
|
+
else if (lower === "us" && token.text !== "US") {
|
|
47
|
+
message = "Do not use \"us\". Rewrite the sentence with \"we\" as the subject.";
|
|
48
|
+
}
|
|
49
|
+
else if (lower === "let's") {
|
|
50
|
+
message = "Do not use \"let's\". Write the instruction in the imperative.";
|
|
51
|
+
}
|
|
52
|
+
if (message !== null)
|
|
53
|
+
context.report({ ...tokenRange(tokens, i, i + 1), message });
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=pronouns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pronouns.js","sourceRoot":"","sources":["../../src/rules/pronouns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAa,MAAM,WAAW,CAAC;AAEhE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC;IACrB,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS;IAC9D,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ;CACtD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAAU;IAClC,EAAE,EAAG,kBAAkB;IACvB,WAAW,EAAG,4EAA4E;IAC1F,GAAG,EAAG,YAAY;IAClB,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,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC;gBAAE,OAAO;YACzD,OAAO,CAAC,MAAM,CAAC;gBACX,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;gBAC/B,OAAO,EAAG,oCAAoC,KAAK,CAAC,IAAI,mDAAmD;aAC9G,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;CACJ,CAAC;AAEF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AAC5F,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AAErD,MAAM,CAAC,MAAM,WAAW,GAAU;IAC9B,EAAE,EAAG,cAAc;IACnB,WAAW,EAAG,iIAAiI;IAC/I,GAAG,EAAG,MAAM;IACZ,eAAe,EAAG,SAAS;IAC3B,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;gBAAE,OAAO;YAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;YAC1B,IAAI,OAAO,GAAmB,IAAI,CAAC;YACnC,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBACtB,OAAO,GAAG,eAAe,KAAK,CAAC,IAAI,mFAAmF,CAAC;YAC3H,CAAC;iBAAM,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3B,OAAO,GAAG,eAAe,KAAK,CAAC,IAAI,oGAAoG,CAAC;YAC5I,CAAC;iBAAM,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;gBAC/C,OAAO,GAAG,qEAAqE,CAAC;YACpF,CAAC;iBAAM,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;gBAC3B,OAAO,GAAG,gEAAgE,CAAC;YAC/E,CAAC;YACD,IAAI,OAAO,KAAK,IAAI;gBAAE,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QACvF,CAAC,CAAC,CAAC;IACP,CAAC;CACJ,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ResolvedConfig } from "../config.js";
|
|
2
|
+
import type { Lexicon } from "../lexicon.js";
|
|
3
|
+
import type { Token } from "../text/tokens.js";
|
|
4
|
+
import type { RuleId, Severity } from "../types.js";
|
|
5
|
+
import type { ProseUnit, Sentence } from "../units.js";
|
|
6
|
+
import type { WordListMatcher } from "./word-list-matcher.js";
|
|
7
|
+
/**
|
|
8
|
+
* Which units a rule examines. Headings and table cells get only the
|
|
9
|
+
* "word" rules. Paragraphs and list items get all rules.
|
|
10
|
+
*/
|
|
11
|
+
export type RuleScope = "word" | "sentence" | "paragraph";
|
|
12
|
+
export type Report = {
|
|
13
|
+
/** The file offset of the first character of the problem. */
|
|
14
|
+
readonly start: number;
|
|
15
|
+
/** The file offset after the last character of the problem. */
|
|
16
|
+
readonly end: number;
|
|
17
|
+
readonly message: string;
|
|
18
|
+
/** The severity of this finding, when it is not the default severity of the rule (for example a word list entry). */
|
|
19
|
+
readonly severity?: Severity;
|
|
20
|
+
};
|
|
21
|
+
export type RuleContext = {
|
|
22
|
+
readonly config: ResolvedConfig;
|
|
23
|
+
readonly lexicon: Lexicon;
|
|
24
|
+
readonly wordList: WordListMatcher;
|
|
25
|
+
report(report: Report): void;
|
|
26
|
+
};
|
|
27
|
+
export type Rule = {
|
|
28
|
+
readonly id: RuleId;
|
|
29
|
+
/** One sentence that tells what the rule finds. */
|
|
30
|
+
readonly description: string;
|
|
31
|
+
/** The related ASD-STE100 rule numbers. */
|
|
32
|
+
readonly ste: string;
|
|
33
|
+
readonly defaultSeverity: Severity;
|
|
34
|
+
readonly scope: RuleScope;
|
|
35
|
+
check(unit: ProseUnit, context: RuleContext): void;
|
|
36
|
+
};
|
|
37
|
+
/** The surface text of `tokens[from]` to `tokens[to - 1]`, with one space between tokens that had space between them. */
|
|
38
|
+
export declare function surface(tokens: readonly Token[], from: number, to: number): string;
|
|
39
|
+
/** A report for the tokens `from` to `to - 1` of a sentence. */
|
|
40
|
+
export declare function tokenRange(tokens: readonly Token[], from: number, to: number): {
|
|
41
|
+
start: number;
|
|
42
|
+
end: number;
|
|
43
|
+
};
|
|
44
|
+
/** This function gives each token of each sentence of the unit to `visit`. */
|
|
45
|
+
export declare function forEachToken(unit: ProseUnit, visit: (tokens: readonly Token[], index: number, sentence: Sentence) => void): void;
|
|
46
|
+
//# sourceMappingURL=rule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../src/rules/rule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,CAAC;AAE1D,MAAM,MAAM,MAAM,GAAG;IACjB,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAG,MAAM,CAAC;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,EAAG,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAG,MAAM,CAAC;IAC1B,qHAAqH;IACrH,QAAQ,CAAC,QAAQ,CAAC,EAAG,QAAQ,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACtB,QAAQ,CAAC,MAAM,EAAG,cAAc,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAG,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAG,eAAe,CAAC;IACpC,MAAM,CAAC,MAAM,EAAG,MAAM,GAAI,IAAI,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,IAAI,GAAG;IACf,QAAQ,CAAC,EAAE,EAAG,MAAM,CAAC;IACrB,mDAAmD;IACnD,QAAQ,CAAC,WAAW,EAAG,MAAM,CAAC;IAC9B,2CAA2C;IAC3C,QAAQ,CAAC,GAAG,EAAG,MAAM,CAAC;IACtB,QAAQ,CAAC,eAAe,EAAG,QAAQ,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAG,SAAS,CAAC;IAC3B,KAAK,CAAC,IAAI,EAAG,SAAS,EAAE,OAAO,EAAG,WAAW,GAAI,IAAI,CAAC;CACzD,CAAC;AAEF,yHAAyH;AACzH,wBAAgB,OAAO,CAAC,MAAM,EAAG,SAAS,KAAK,EAAE,EAAE,IAAI,EAAG,MAAM,EAAE,EAAE,EAAG,MAAM,GAAI,MAAM,CAQtF;AAYD,gEAAgE;AAChE,wBAAgB,UAAU,CAAC,MAAM,EAAG,SAAS,KAAK,EAAE,EAAE,IAAI,EAAG,MAAM,EAAE,EAAE,EAAG,MAAM,GAAI;IAAE,KAAK,EAAG,MAAM,CAAC;IAAC,GAAG,EAAG,MAAM,CAAA;CAAE,CAEnH;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,IAAI,EAAG,SAAS,EAAE,KAAK,EAAG,CAAC,MAAM,EAAG,SAAS,KAAK,EAAE,EAAE,KAAK,EAAG,MAAM,EAAE,QAAQ,EAAG,QAAQ,KAAK,IAAI,GAAI,IAAI,CAItI"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** The surface text of `tokens[from]` to `tokens[to - 1]`, with one space between tokens that had space between them. */
|
|
2
|
+
export function surface(tokens, from, to) {
|
|
3
|
+
let text = "";
|
|
4
|
+
for (let i = from; i < to; i++) {
|
|
5
|
+
const token = tokens[i];
|
|
6
|
+
const value = token.kind === "code" && token.placeholder !== null ? placeholderText(token) : token.text;
|
|
7
|
+
text += (i > from && token.spaceBefore ? " " : "") + value;
|
|
8
|
+
}
|
|
9
|
+
return text;
|
|
10
|
+
}
|
|
11
|
+
function placeholderText(token) {
|
|
12
|
+
const placeholder = token.placeholder;
|
|
13
|
+
switch (placeholder.kind) {
|
|
14
|
+
case "code": return `\`${placeholder.text}\``;
|
|
15
|
+
case "quote": return `"${placeholder.text}"`;
|
|
16
|
+
case "link": return `{${placeholder.text}}`;
|
|
17
|
+
default: return placeholder.text;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** A report for the tokens `from` to `to - 1` of a sentence. */
|
|
21
|
+
export function tokenRange(tokens, from, to) {
|
|
22
|
+
return { start: tokens[from].start, end: tokens[to - 1].end };
|
|
23
|
+
}
|
|
24
|
+
/** This function gives each token of each sentence of the unit to `visit`. */
|
|
25
|
+
export function forEachToken(unit, visit) {
|
|
26
|
+
for (const sentence of unit.sentences) {
|
|
27
|
+
for (let i = 0; i < sentence.tokens.length; i++)
|
|
28
|
+
visit(sentence.tokens, i, sentence);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=rule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule.js","sourceRoot":"","sources":["../../src/rules/rule.ts"],"names":[],"mappings":"AAyCA,yHAAyH;AACzH,MAAM,UAAU,OAAO,CAAC,MAAyB,EAAE,IAAa,EAAE,EAAW;IACzE,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;QACzB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;QACxG,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC;IAC/D,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IAClC,MAAM,WAAW,GAAG,KAAK,CAAC,WAAY,CAAC;IACvC,QAAQ,WAAW,CAAC,IAAI,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,CAAC,OAAO,KAAK,WAAW,CAAC,IAAI,IAAI,CAAC;QAC9C,KAAK,OAAO,CAAC,CAAC,OAAO,IAAI,WAAW,CAAC,IAAI,GAAG,CAAC;QAC7C,KAAK,MAAM,CAAC,CAAC,OAAO,IAAI,WAAW,CAAC,IAAI,GAAG,CAAC;QAC5C,OAAO,CAAC,CAAC,OAAO,WAAW,CAAC,IAAI,CAAC;IACrC,CAAC;AACL,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,UAAU,CAAC,MAAyB,EAAE,IAAa,EAAE,EAAW;IAC5E,OAAO,EAAE,KAAK,EAAG,MAAM,CAAC,IAAI,CAAE,CAAC,KAAK,EAAE,GAAG,EAAG,MAAM,CAAC,EAAE,GAAG,CAAC,CAAE,CAAC,GAAG,EAAE,CAAC;AACtE,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAC,IAAgB,EAAE,KAAgF;IAC3H,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE;YAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;IACzF,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semicolon.d.ts","sourceRoot":"","sources":["../../src/rules/semicolon.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAEhE,eAAO,MAAM,SAAS,EAAG,IAgBxB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { forEachToken, tokenRange } from "./rule.js";
|
|
2
|
+
export const semicolon = {
|
|
3
|
+
id: "semicolon",
|
|
4
|
+
description: "Find semicolons in prose. Semicolons in code spans are not prose.",
|
|
5
|
+
ste: "8.1",
|
|
6
|
+
defaultSeverity: "error",
|
|
7
|
+
scope: "word",
|
|
8
|
+
check(unit, context) {
|
|
9
|
+
forEachToken(unit, (tokens, i) => {
|
|
10
|
+
const token = tokens[i];
|
|
11
|
+
if (token.kind !== "punct" || (token.text !== ";" && token.text !== ";"))
|
|
12
|
+
return;
|
|
13
|
+
context.report({
|
|
14
|
+
...tokenRange(tokens, i, i + 1),
|
|
15
|
+
message: "Do not use a semicolon. Write two sentences, or use a comma and \"and\" or \"but\". For a list of items, use a vertical list.",
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=semicolon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semicolon.js","sourceRoot":"","sources":["../../src/rules/semicolon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAa,MAAM,WAAW,CAAC;AAEhE,MAAM,CAAC,MAAM,SAAS,GAAU;IAC5B,EAAE,EAAG,WAAW;IAChB,WAAW,EAAG,mEAAmE;IACjF,GAAG,EAAG,KAAK;IACX,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,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;gBAAE,OAAO;YACjF,OAAO,CAAC,MAAM,CAAC;gBACX,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;gBAC/B,OAAO,EAAG,+HAA+H;aAC5I,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;CACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentence-length.d.ts","sourceRoot":"","sources":["../../src/rules/sentence-length.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,eAAO,MAAM,cAAc,EAAG,IA4B7B,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { countWords } from "../text/sentences.js";
|
|
2
|
+
export const sentenceLength = {
|
|
3
|
+
id: "sentence-length",
|
|
4
|
+
description: "An instruction can have no more than 20 words. A description can have no more than 25 words.",
|
|
5
|
+
ste: "5.1, 6.3, 8.4-8.7",
|
|
6
|
+
defaultSeverity: "error",
|
|
7
|
+
scope: "sentence",
|
|
8
|
+
check(unit, context) {
|
|
9
|
+
const { instructionWords, descriptionWords } = context.config.limits;
|
|
10
|
+
for (const sentence of unit.sentences) {
|
|
11
|
+
const limit = sentence.instruction ? instructionWords : descriptionWords;
|
|
12
|
+
if (sentence.words > limit) {
|
|
13
|
+
const message = sentence.instruction
|
|
14
|
+
? `This instruction has ${sentence.words} words. Use no more than ${limit} words in an instruction. Divide it into shorter sentences.`
|
|
15
|
+
: `This sentence has ${sentence.words} words. Use no more than ${limit} words in a descriptive sentence. Divide it into shorter sentences.`;
|
|
16
|
+
context.report({ start: sentence.start, end: sentence.end, message });
|
|
17
|
+
}
|
|
18
|
+
for (const inner of sentence.parentheticals) {
|
|
19
|
+
const words = countWords(inner, { properNouns: context.lexicon.properNounPhrases }).words;
|
|
20
|
+
if (words > descriptionWords) {
|
|
21
|
+
context.report({
|
|
22
|
+
start: inner[0].start,
|
|
23
|
+
end: inner[inner.length - 1].end,
|
|
24
|
+
message: `The text in parentheses has ${words} words. It counts as a separate sentence. Use no more than ${descriptionWords} words, or write it as a sentence after this one.`,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=sentence-length.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentence-length.js","sourceRoot":"","sources":["../../src/rules/sentence-length.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGlD,MAAM,CAAC,MAAM,cAAc,GAAU;IACjC,EAAE,EAAG,iBAAiB;IACtB,WAAW,EAAG,8FAA8F;IAC5G,GAAG,EAAG,mBAAmB;IACzB,eAAe,EAAG,OAAO;IACzB,KAAK,EAAG,UAAU;IAClB,KAAK,CAAC,IAAI,EAAE,OAAO;QACf,MAAM,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;QACrE,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpC,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,gBAAgB,CAAC;YACzE,IAAI,QAAQ,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC;gBACzB,MAAM,OAAO,GAAG,QAAQ,CAAC,WAAW;oBAChC,CAAC,CAAC,wBAAwB,QAAQ,CAAC,KAAK,4BAA4B,KAAK,6DAA6D;oBACtI,CAAC,CAAC,qBAAqB,QAAQ,CAAC,KAAK,4BAA4B,KAAK,qEAAqE,CAAC;gBAChJ,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,EAAG,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAG,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YAC5E,CAAC;YACD,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;gBAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,EAAE,WAAW,EAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC,KAAK,CAAC;gBAC3F,IAAI,KAAK,GAAG,gBAAgB,EAAE,CAAC;oBAC3B,OAAO,CAAC,MAAM,CAAC;wBACX,KAAK,EAAG,KAAK,CAAC,CAAC,CAAE,CAAC,KAAK;wBACvB,GAAG,EAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,GAAG;wBAClC,OAAO,EAAG,+BAA+B,KAAK,8DAA8D,gBAAgB,mDAAmD;qBAClL,CAAC,CAAC;gBACP,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;CACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Rule } from "./rule.js";
|
|
2
|
+
/**
|
|
3
|
+
* This rule is active only when a local dictionary is configured. It does
|
|
4
|
+
* not examine numbers, abbreviations, words that look like code or glossary
|
|
5
|
+
* terms. It also does not examine a word with a capital letter after the
|
|
6
|
+
* start of a sentence, because that word is possibly a proper noun.
|
|
7
|
+
*/
|
|
8
|
+
export declare const unknownWord: Rule;
|
|
9
|
+
//# sourceMappingURL=unknown-word.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unknown-word.d.ts","sourceRoot":"","sources":["../../src/rules/unknown-word.ts"],"names":[],"mappings":"AAIA,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAElD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAG,IA8B1B,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { isGlossaryWord } from "../lexicon.js";
|
|
2
|
+
import { nextCounted } from "../text/grammar.js";
|
|
3
|
+
import { isAbbreviation, isCapitalized, isIdentifierLike, isNumber, isWord } from "../text/tokens.js";
|
|
4
|
+
import { coveredByPhrases } from "./ing-verb.js";
|
|
5
|
+
import { tokenRange } from "./rule.js";
|
|
6
|
+
/**
|
|
7
|
+
* This rule is active only when a local dictionary is configured. It does
|
|
8
|
+
* not examine numbers, abbreviations, words that look like code or glossary
|
|
9
|
+
* terms. It also does not examine a word with a capital letter after the
|
|
10
|
+
* start of a sentence, because that word is possibly a proper noun.
|
|
11
|
+
*/
|
|
12
|
+
export const unknownWord = {
|
|
13
|
+
id: "unknown-word",
|
|
14
|
+
description: "Find words that are not in the local dictionary and not in the project glossary.",
|
|
15
|
+
ste: "1.1, 1.5, 1.12",
|
|
16
|
+
defaultSeverity: "warning",
|
|
17
|
+
scope: "word",
|
|
18
|
+
check(unit, context) {
|
|
19
|
+
const { lexicon } = context;
|
|
20
|
+
const dictionary = lexicon.dictionary;
|
|
21
|
+
if (dictionary === null)
|
|
22
|
+
return;
|
|
23
|
+
for (const sentence of unit.sentences) {
|
|
24
|
+
const tokens = sentence.tokens;
|
|
25
|
+
const covered = coveredByPhrases(tokens, lexicon.glossaryPhrases);
|
|
26
|
+
const first = nextCounted(tokens, 0);
|
|
27
|
+
tokens.forEach((token, i) => {
|
|
28
|
+
if (!isWord(token) || covered.has(i))
|
|
29
|
+
return;
|
|
30
|
+
if (isNumber(token) || isAbbreviation(token) || isIdentifierLike(token) || /\d/.test(token.text))
|
|
31
|
+
return;
|
|
32
|
+
if (i !== first && isCapitalized(token))
|
|
33
|
+
return;
|
|
34
|
+
for (const part of token.lower.split("-")) {
|
|
35
|
+
if (part.length === 0 || isGlossaryWord(part, lexicon))
|
|
36
|
+
continue;
|
|
37
|
+
const lookup = dictionary.lookup(part);
|
|
38
|
+
if (lookup.status === "approved")
|
|
39
|
+
continue;
|
|
40
|
+
const message = lookup.status === "not-approved"
|
|
41
|
+
? `"${part}" is not an approved word. ${lookup.alternatives.length > 0 ? `Use ${lookup.alternatives.map((word) => `"${word}"`).join(" or ")}.` : "Use an approved word, or a different construction."}`
|
|
42
|
+
: `"${part}" is not in the dictionary or the project glossary. Use an approved word. If "${part}" is a technical term, add it to technicalNouns or technicalVerbs.`;
|
|
43
|
+
context.report({ ...tokenRange(tokens, i, i + 1), message });
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=unknown-word.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unknown-word.js","sourceRoot":"","sources":["../../src/rules/unknown-word.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACtG,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,UAAU,EAAa,MAAM,WAAW,CAAC;AAElD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAU;IAC9B,EAAE,EAAG,cAAc;IACnB,WAAW,EAAG,kFAAkF;IAChG,GAAG,EAAG,gBAAgB;IACtB,eAAe,EAAG,SAAS;IAC3B,KAAK,EAAG,MAAM;IACd,KAAK,CAAC,IAAI,EAAE,OAAO;QACf,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACtC,IAAI,UAAU,KAAK,IAAI;YAAE,OAAO;QAChC,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;YAC/B,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;YAClE,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YACrC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;gBACxB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;oBAAE,OAAO;gBAC7C,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,cAAc,CAAC,KAAK,CAAC,IAAI,gBAAgB,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;oBAAE,OAAO;gBACzG,IAAI,CAAC,KAAK,KAAK,IAAI,aAAa,CAAC,KAAK,CAAC;oBAAE,OAAO;gBAChD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;oBACxC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC;wBAAE,SAAS;oBACjE,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;oBACvC,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU;wBAAE,SAAS;oBAC3C,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,KAAK,cAAc;wBAC5C,CAAC,CAAC,IAAI,IAAI,8BAA8B,MAAM,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,oDAAoD,EAAE;wBACvM,CAAC,CAAC,IAAI,IAAI,iFAAiF,IAAI,oEAAoE,CAAC;oBACxK,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;gBACjE,CAAC;YACL,CAAC,CAAC,CAAC;QACP,CAAC;IACL,CAAC;CACJ,CAAC"}
|