-
-
Notifications
You must be signed in to change notification settings - Fork 84
/
commandLint.ts
219 lines (203 loc) · 10.5 KB
/
commandLint.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
import type { Command } from 'commander';
import { Option as CommanderOption } from 'commander';
import * as App from './application.js';
import type { LinterCliOptions } from './options.js';
import { DEFAULT_CACHE_LOCATION } from './util/cache/index.js';
import { CheckFailed } from './util/errors.js';
// interface InitOptions extends Options {}
const usage = `\
[options] [globs...] [file://<path> ...] [stdin[://<path>]]
Patterns:
- [globs...] Glob Patterns
- [stdin] Read from "stdin" assume text file.
- [stdin://<path>] Read from "stdin", use <path> for file type and config.
- [file://<path>] Check the file at <path>
Examples:
cspell . Recursively check all files.
cspell lint . The same as "cspell ."
cspell "*.js" Check all .js files in the current directory
cspell "**/*.js" Check all .js files recursively
cspell "src/**/*.js" Only check .js under src
cspell "**/*.txt" "**/*.js" Check both .js and .txt files.
cspell "**/*.{txt,js,md}" Check .txt, .js, and .md files.
cat LICENSE | cspell stdin Check stdin
cspell stdin://docs/doc.md Check stdin as if it was "./docs/doc.md"\
`;
const advanced = `
More Examples:
cspell "**/*.js" --reporter @cspell/cspell-json-reporter
This will spell check all ".js" files recursively and use
"@cspell/cspell-json-reporter".
cspell . --reporter default
This will force the default reporter to be used overriding
any reporters defined in the configuration.
cspell . --reporter ./<path>/reporter.cjs
Use a custom reporter. See API for details.
cspell "*.md" --exclude CHANGELOG.md --files README.md CHANGELOG.md
Spell check only check "README.md" but NOT "CHANGELOG.md".
cspell "/*.md" --no-must-find-files --files $FILES
Only spell check the "/*.md" files in $FILES,
where $FILES is a shell variable that contains the list of files.
References:
https://cspell.org
https://github.com/streetsidesoftware/cspell
`;
function collect(value: string | string[], previous: string[] | undefined): string[] {
const values = Array.isArray(value) ? value : [value];
return previous ? [...previous, ...values] : values;
}
export function commandLint(prog: Command): Command {
const spellCheckCommand = prog.command('lint', { isDefault: true });
spellCheckCommand
.description('Check spelling')
.option(
'-c, --config <cspell.json>',
'Configuration file to use. By default cspell looks for cspell.json in the current directory.',
)
.option('-v, --verbose', 'Display more information about the files being checked and the configuration.')
.option(
'--locale <locale>',
'Set language locales. i.e. "en,fr" for English and French, or "en-GB" for British English.',
)
.option('--language-id <language>', 'Force programming language for unknown extensions. i.e. "php" or "scala"')
.addOption(
crOpt(
'--languageId <language>',
'Force programming language for unknown extensions. i.e. "php" or "scala"',
).hideHelp(),
)
.option('--words-only', 'Only output the words not found in the dictionaries.')
.addOption(crOpt('--wordsOnly', 'Only output the words not found in the dictionaries.').hideHelp())
.option('-u, --unique', 'Only output the first instance of a word not found in the dictionaries.')
.option(
'-e, --exclude <glob>',
'Exclude files matching the glob pattern. This option can be used multiple times to add multiple globs. ',
collect,
)
// .option('--include <glob>', 'Include files matching the glob pattern. This option can be used multiple times.', collect)
.option(
'--file-list <path or stdin>',
'Specify a list of files to be spell checked.' +
' The list is filtered against the glob file patterns.' +
' Note: the format is 1 file path per line.',
collect,
)
.option('--file [file...]', 'Specify files to spell check. They are filtered by the [globs...].', collect)
.addOption(crOpt('--files [file...]', 'Files to spell check.', collect).hideHelp())
.option('--no-issues', 'Do not show the spelling errors.')
.option('--no-progress', 'Turn off progress messages')
.option('--no-summary', 'Turn off summary message in console.')
.option('-s, --silent', 'Silent mode, suppress error messages.')
.option('--no-exit-code', 'Do not return an exit code if issues are found.')
.addOption(
crOpt('--quiet', 'Only show spelling issues or errors.').implies({
summary: false,
progress: false,
}),
)
.option('--fail-fast', 'Exit after first file with an issue or error.')
.addOption(crOpt('--no-fail-fast', 'Process all files even if there is an error.').hideHelp())
.option('-r, --root <root folder>', 'Root directory, defaults to current directory.')
.addOption(crOpt('--relative', 'Issues are displayed relative to the root.').default(true).hideHelp())
.option('--no-relative', 'Issues are displayed with absolute path instead of relative to the root.')
.option('--show-context', 'Show the surrounding text around an issue.')
.option('--show-suggestions', 'Show spelling suggestions.')
.addOption(crOpt('--no-show-suggestions', 'Do not show spelling suggestions or fixes.').default(undefined))
.addOption(crOpt('--must-find-files', 'Error if no files are found.').default(true).hideHelp())
.option('--no-must-find-files', 'Do not error if no files are found.')
// The --filter-files option is still under design review.
// .option('--filter-files', 'Use the `files` configuration to filter files found.')
// .option(
// '--no-filter-files',
// 'Do NOT use the `files` configuration to filter files (Only applies to --files options).',
// )
// The following options are planned features
// .option('-w, --watch', 'Watch for any changes to the matching files and report any errors')
// .option('--force', 'Force the exit value to always be 0')
.addOption(crOpt('--legacy', 'Legacy output').hideHelp())
.addOption(crOpt('--local <local>', 'Deprecated -- Use: --locale').hideHelp())
.option('--cache', 'Use cache to only check changed files.')
.option('--no-cache', 'Do not use cache.')
.option('--cache-reset', 'Reset the cache file.')
.addOption(
crOpt('--cache-strategy <strategy>', 'Strategy to use for detecting changed files.').choices([
'metadata',
'content',
]),
)
.option(
'--cache-location <path>',
`Path to the cache file or directory. (default: "${DEFAULT_CACHE_LOCATION}")`,
)
.option('--dot', 'Include files and directories starting with `.` (period) when matching globs.')
.option('--gitignore', 'Ignore files matching glob patterns found in .gitignore files.')
.option('--no-gitignore', 'Do NOT use .gitignore files.')
.option('--gitignore-root <path>', 'Prevent searching for .gitignore files past root.', collect)
.option('--validate-directives', 'Validate in-document CSpell directives.')
.option('--no-validate-directives', 'Do not validate in-document CSpell directives.')
.option('--no-color', 'Turn off color.')
.option('--color', 'Force color.')
.addOption(crOpt('--default-configuration', 'Load the default configuration and dictionaries.').hideHelp())
.addOption(crOpt('--no-default-configuration', 'Do not load the default configuration and dictionaries.'))
.option('--debug', 'Output information useful for debugging cspell.json files.')
.option('--reporter <module|path>', 'Specify one or more reporters to use.', collect)
.addOption(
crOpt('--skip-validation', 'Collect and process documents, but do not spell check.')
.implies({ cache: false })
.hideHelp(),
)
.addOption(crOpt('--issues-summary-report', 'Output a summary of issues found.').hideHelp())
// Planned options
// .option('--dictionary <dictionary name>', 'Enable a dictionary by name.', collect)
// .option('--no-dictionary <dictionary name>', 'Disable a dictionary by name.', collect)
// .option('--import', 'Import a configuration file.', collect)
.usage(usage)
.addHelpText('after', advanced)
.arguments('[globs...]')
.action(async (fileGlobs: string[], options: LinterCliOptions) => {
// console.error('lint: %o', { fileGlobs, options });
const useExitCode = options.exitCode ?? true;
if (options.skipValidation) {
options.cache = false;
}
App.parseApplicationFeatureFlags(options.flag);
const { mustFindFiles, fileList, files, file } = options;
const result = await App.lint(fileGlobs, options);
if (!fileGlobs.length && !result.files && !result.errors && !fileList && !files?.length && !file?.length) {
spellCheckCommand.outputHelp();
throw new CheckFailed('outputHelp', 1);
}
if (result.errors || (mustFindFiles && !result.files)) {
throw new CheckFailed('check failed', 1);
}
if (result.issues) {
const exitCode = useExitCode ? 1 : 0;
throw new CheckFailed('check failed', exitCode);
}
return;
});
return spellCheckCommand;
}
/**
* Create Option - a helper function to create a commander option.
* @param name - the name of the option
* @param description - the description of the option
* @param parseArg - optional function to parse the argument
* @param defaultValue - optional default value
* @returns CommanderOption
*/
function crOpt<T>(
name: string,
description: string,
parseArg?: (value: string, previous: T) => T,
defaultValue?: T,
): CommanderOption {
const option = new CommanderOption(name, description);
if (parseArg) {
option.argParser(parseArg);
}
if (defaultValue !== undefined) {
option.default(defaultValue);
}
return option;
}