@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
package/src/run-cli.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The command-line interface. `runCli` does not use `process` directly, so
|
|
3
|
+
* the tests can give it arguments, an environment and output functions.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { relative, resolve, sep } from "node:path";
|
|
8
|
+
import { ConfigError, DICTIONARY_ENV, loadConfig, type ResolvedConfig } from "./config.js";
|
|
9
|
+
import { DictionaryError, loadDictionary, type Dictionary } from "./dictionary.js";
|
|
10
|
+
import { findFiles } from "./files.js";
|
|
11
|
+
import { formatJson, formatText, summarize } from "./format.js";
|
|
12
|
+
import { buildLexicon } from "./lexicon.js";
|
|
13
|
+
import { fileKind, Linter } from "./lint.js";
|
|
14
|
+
import type { Finding } from "./types.js";
|
|
15
|
+
|
|
16
|
+
export type CliIo = {
|
|
17
|
+
readonly cwd : string;
|
|
18
|
+
readonly env : Readonly<Record<string, string | undefined>>;
|
|
19
|
+
readonly stdout : (text : string) => void;
|
|
20
|
+
readonly stderr : (text : string) => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type CliOptions = {
|
|
24
|
+
readonly patterns : readonly string[];
|
|
25
|
+
readonly format : "text" | "json";
|
|
26
|
+
readonly maxWarnings : number | null;
|
|
27
|
+
readonly configPath : string | null;
|
|
28
|
+
readonly help : boolean;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** An incorrect command line. */
|
|
32
|
+
export class UsageError extends Error {
|
|
33
|
+
override readonly name : string = "UsageError";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const USAGE : string = `Usage: ste-lint [options] [patterns...]
|
|
37
|
+
|
|
38
|
+
ste-lint examines the prose in Markdown files, MDX files and TypeScript doc
|
|
39
|
+
comments. It finds text that does not obey the writing rules of ASD-STE100
|
|
40
|
+
Simplified Technical English. The tool is an automated approximation of
|
|
41
|
+
these rules. It does not certify that a text obeys the standard.
|
|
42
|
+
|
|
43
|
+
Without patterns, ste-lint uses the "include" patterns of ste.config.json,
|
|
44
|
+
or the default patterns. Patterns are relative to the working directory.
|
|
45
|
+
|
|
46
|
+
Options:
|
|
47
|
+
--format <text|json> The output format. The default is "text".
|
|
48
|
+
--max-warnings <n> Exit with code 1 when there are more than n warnings.
|
|
49
|
+
--config <path> Use this configuration file.
|
|
50
|
+
-h, --help Show this help.
|
|
51
|
+
|
|
52
|
+
Exit codes:
|
|
53
|
+
0 No errors, and not more warnings than the maximum.
|
|
54
|
+
1 One or more errors, or more warnings than the maximum.
|
|
55
|
+
2 An incorrect command line or configuration, or no files.
|
|
56
|
+
|
|
57
|
+
Environment:
|
|
58
|
+
${DICTIONARY_ENV} The path of a local dictionary file. This path has
|
|
59
|
+
priority over "dictionaryPath" in ste.config.json.
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
function optionValue(argv : readonly string[], index : number, name : string) : string {
|
|
63
|
+
const value = argv[index + 1];
|
|
64
|
+
if (value === undefined || value.startsWith("--")) throw new UsageError(`The option ${name} needs a value.`);
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** This function reads the command-line arguments. */
|
|
69
|
+
export function parseArgs(argv : readonly string[]) : CliOptions {
|
|
70
|
+
const patterns : string[] = [];
|
|
71
|
+
let format : CliOptions["format"] = "text";
|
|
72
|
+
let maxWarnings : number | null = null;
|
|
73
|
+
let configPath : string | null = null;
|
|
74
|
+
let help = false;
|
|
75
|
+
for (let i = 0; i < argv.length; i++) {
|
|
76
|
+
const argument = argv[i]!;
|
|
77
|
+
const [name, inline] = argument.startsWith("--") && argument.includes("=") ? [argument.slice(0, argument.indexOf("=")), argument.slice(argument.indexOf("=") + 1)] : [argument, undefined];
|
|
78
|
+
const value = () : string => {
|
|
79
|
+
if (inline !== undefined) return inline;
|
|
80
|
+
const next = optionValue(argv, i, name);
|
|
81
|
+
i++;
|
|
82
|
+
return next;
|
|
83
|
+
};
|
|
84
|
+
switch (name) {
|
|
85
|
+
case "-h":
|
|
86
|
+
case "--help":
|
|
87
|
+
help = true;
|
|
88
|
+
break;
|
|
89
|
+
case "--format": {
|
|
90
|
+
const text = value();
|
|
91
|
+
if (text !== "text" && text !== "json") throw new UsageError(`The format must be "text" or "json", not "${text}".`);
|
|
92
|
+
format = text;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
case "--max-warnings": {
|
|
96
|
+
const text = value();
|
|
97
|
+
const count = Number(text);
|
|
98
|
+
if (!/^\d+$/.test(text) || !Number.isSafeInteger(count)) throw new UsageError(`--max-warnings must be an integer of 0 or more, not "${text}".`);
|
|
99
|
+
maxWarnings = count;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case "--config":
|
|
103
|
+
configPath = value();
|
|
104
|
+
break;
|
|
105
|
+
default:
|
|
106
|
+
if (argument.startsWith("-") && argument !== "-") throw new UsageError(`"${argument}" is not a known option. Use --help to show the options.`);
|
|
107
|
+
patterns.push(argument);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { patterns, format, maxWarnings, configPath, help };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function displayPath(path : string, cwd : string) : string {
|
|
114
|
+
const shown = relative(cwd, path);
|
|
115
|
+
return (shown.length > 0 && !shown.startsWith("..") ? shown : path).split(sep).join("/");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function loadOptionalDictionary(config : ResolvedConfig, io : CliIo) : Dictionary | null {
|
|
119
|
+
if (config.dictionaryPath === null) return null;
|
|
120
|
+
if (!existsSync(config.dictionaryPath)) {
|
|
121
|
+
const from = config.dictionarySource === "env" ? DICTIONARY_ENV : "dictionaryPath";
|
|
122
|
+
io.stderr(`ste-lint: the dictionary file ${config.dictionaryPath} (from ${from}) does not exist. The unknown-word rule is off.\n`);
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
return loadDictionary(config.dictionaryPath);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** This function starts the linter with command-line arguments. The result is the exit code. */
|
|
129
|
+
export function runCli(argv : readonly string[], io : CliIo) : number {
|
|
130
|
+
let options : CliOptions;
|
|
131
|
+
try {
|
|
132
|
+
options = parseArgs(argv);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
io.stderr(`ste-lint: ${(error as Error).message}\n`);
|
|
135
|
+
return 2;
|
|
136
|
+
}
|
|
137
|
+
if (options.help) {
|
|
138
|
+
io.stdout(USAGE);
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let config : ResolvedConfig;
|
|
143
|
+
let dictionary : Dictionary | null;
|
|
144
|
+
try {
|
|
145
|
+
config = loadConfig({ cwd : io.cwd, env : io.env, ...(options.configPath === null ? {} : { configPath : options.configPath }) });
|
|
146
|
+
dictionary = loadOptionalDictionary(config, io);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (error instanceof ConfigError || error instanceof DictionaryError) {
|
|
149
|
+
io.stderr(`ste-lint: ${error.message}\n`);
|
|
150
|
+
return 2;
|
|
151
|
+
}
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const explicit = options.patterns.length > 0;
|
|
156
|
+
let files : string[];
|
|
157
|
+
try {
|
|
158
|
+
// The patterns of the command line are relative to the working directory. The ignore patterns are relative to the configuration.
|
|
159
|
+
files = (explicit
|
|
160
|
+
? findFiles({ root : io.cwd, include : options.patterns, ignore : config.ignore, ignoreRoot : config.root })
|
|
161
|
+
: findFiles({ root : config.root, include : config.include, ignore : config.ignore })
|
|
162
|
+
).filter((file) => fileKind(file) !== null);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
// For example, a directory that the user cannot read (EACCES)
|
|
165
|
+
io.stderr(`ste-lint: cannot read the files: ${(error as Error).message}\n`);
|
|
166
|
+
return 2;
|
|
167
|
+
}
|
|
168
|
+
if (files.length === 0) {
|
|
169
|
+
io.stderr(explicit ? "ste-lint: no files match the patterns.\n" : "ste-lint: no files to examine.\n");
|
|
170
|
+
return explicit ? 2 : 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const linter = new Linter(config, buildLexicon(config, dictionary));
|
|
174
|
+
const findings : Finding[] = [];
|
|
175
|
+
let failed = false;
|
|
176
|
+
for (const file of files) {
|
|
177
|
+
try {
|
|
178
|
+
findings.push(...linter.lintFile(file, displayPath(file, resolve(io.cwd))));
|
|
179
|
+
} catch (error) {
|
|
180
|
+
io.stderr(`ste-lint: cannot examine ${file}: ${(error as Error).message}\n`);
|
|
181
|
+
failed = true;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const summary = summarize(findings, files.length);
|
|
186
|
+
io.stdout(options.format === "json" ? formatJson(findings, summary) : formatText(findings, summary));
|
|
187
|
+
const tooManyWarnings = options.maxWarnings !== null && summary.warnings > options.maxWarnings;
|
|
188
|
+
if (tooManyWarnings && options.format === "text") {
|
|
189
|
+
io.stderr(`ste-lint: ${summary.warnings} warnings. The maximum is ${options.maxWarnings}.\n`);
|
|
190
|
+
}
|
|
191
|
+
if (failed) return 2;
|
|
192
|
+
return summary.errors > 0 || tooManyWarnings ? 1 : 0;
|
|
193
|
+
}
|