1 | import { Option as CommanderOption } from 'commander';
|
2 | import * as App from './application.mjs';
|
3 | import { DEFAULT_CACHE_LOCATION } from './util/cache/index.js';
|
4 | import { CheckFailed } from './util/errors.js';
|
5 | import { unindent } from './util/unindent.js';
|
6 |
|
7 | const usage = `\
|
8 | [options] [globs...] [file://<path> ...] [stdin[://<path>]]
|
9 |
|
10 | Patterns:
|
11 | - [globs...] Glob Patterns
|
12 | - [stdin] Read from "stdin" assume text file.
|
13 | - [stdin://<path>] Read from "stdin", use <path> for file type and config.
|
14 | - [file://<path>] Check the file at <path>
|
15 |
|
16 | Examples:
|
17 | cspell . Recursively check all files.
|
18 | cspell lint . The same as "cspell ."
|
19 | cspell "*.js" Check all .js files in the current directory
|
20 | cspell "**/*.js" Check all .js files recursively
|
21 | cspell "src/**/*.js" Only check .js under src
|
22 | cspell "**/*.txt" "**/*.js" Check both .js and .txt files.
|
23 | cspell "**/*.{txt,js,md}" Check .txt, .js, and .md files.
|
24 | cat LICENSE | cspell stdin Check stdin
|
25 | cspell stdin://docs/doc.md Check stdin as if it was "./docs/doc.md"\
|
26 | `;
|
27 | const advanced = `
|
28 | More Examples:
|
29 |
|
30 | cspell "**/*.js" --reporter @cspell/cspell-json-reporter
|
31 | This will spell check all ".js" files recursively and use
|
32 | "@cspell/cspell-json-reporter".
|
33 |
|
34 | cspell . --reporter default
|
35 | This will force the default reporter to be used overriding
|
36 | any reporters defined in the configuration.
|
37 |
|
38 | cspell . --reporter ./<path>/reporter.cjs
|
39 | Use a custom reporter. See API for details.
|
40 |
|
41 | cspell "*.md" --exclude CHANGELOG.md --files README.md CHANGELOG.md
|
42 | Spell check only check "README.md" but NOT "CHANGELOG.md".
|
43 |
|
44 | cspell "/*.md" --no-must-find-files --files $FILES
|
45 | Only spell check the "/*.md" files in $FILES,
|
46 | where $FILES is a shell variable that contains the list of files.
|
47 |
|
48 | References:
|
49 | https://cspell.org
|
50 | https://github.com/streetsidesoftware/cspell
|
51 | `;
|
52 | function collect(value, previous) {
|
53 | const values = Array.isArray(value) ? value : [value];
|
54 | return previous ? [...previous, ...values] : values;
|
55 | }
|
56 | export function commandLint(prog) {
|
57 | const spellCheckCommand = prog.command('lint', { isDefault: true });
|
58 | spellCheckCommand
|
59 | .description('Check spelling')
|
60 | .option('-c, --config <cspell.json>', 'Configuration file to use. By default cspell looks for cspell.json in the current directory.')
|
61 | .option('-v, --verbose', 'Display more information about the files being checked and the configuration.')
|
62 | .option('--locale <locale>', 'Set language locales. i.e. "en,fr" for English and French, or "en-GB" for British English.')
|
63 | .option('--language-id <file-type>', 'Force programming language for unknown extensions. i.e. "php" or "scala"')
|
64 | .addOption(crOpt('--languageId <file-type>', 'Alias of "--language-id". Force programming language for unknown extensions. i.e. "php" or "scala"').hideHelp())
|
65 | .option('--words-only', 'Only output the words not found in the dictionaries.')
|
66 | .addOption(crOpt('--wordsOnly', 'Only output the words not found in the dictionaries.').hideHelp())
|
67 | .option('-u, --unique', 'Only output the first instance of a word not found in the dictionaries.')
|
68 | .option('-e, --exclude <glob>', 'Exclude files matching the glob pattern. This option can be used multiple times to add multiple globs. ', collect)
|
69 |
|
70 | .option('--file-list <path or stdin>', 'Specify a list of files to be spell checked.' +
|
71 | ' The list is filtered against the glob file patterns.' +
|
72 | ' Note: the format is 1 file path per line.', collect)
|
73 | .option('--file [file...]', 'Specify files to spell check. They are filtered by the [globs...].', collect)
|
74 | .addOption(crOpt('--files [file...]', 'Alias of "--file". Files to spell check.', collect).hideHelp())
|
75 | .option('--no-issues', 'Do not show the spelling errors.')
|
76 | .option('--no-progress', 'Turn off progress messages')
|
77 | .option('--no-summary', 'Turn off summary message in console.')
|
78 | .option('-s, --silent', 'Silent mode, suppress error messages.')
|
79 | .option('--no-exit-code', 'Do not return an exit code if issues are found.')
|
80 | .addOption(crOpt('--quiet', 'Only show spelling issues or errors.').implies({
|
81 | summary: false,
|
82 | progress: false,
|
83 | }))
|
84 | .option('--fail-fast', 'Exit after first file with an issue or error.')
|
85 | .addOption(crOpt('--no-fail-fast', 'Process all files even if there is an error.').hideHelp())
|
86 | .option('-r, --root <root folder>', 'Root directory, defaults to current directory.')
|
87 | .addOption(crOpt('--relative', 'Issues are displayed relative to the root.').default(true).hideHelp())
|
88 | .option('--no-relative', 'Issues are displayed with absolute path instead of relative to the root.')
|
89 | .option('--show-context', 'Show the surrounding text around an issue.')
|
90 | .option('--show-suggestions', 'Show spelling suggestions.')
|
91 | .addOption(crOpt('--no-show-suggestions', 'Do not show spelling suggestions or fixes.').default(undefined))
|
92 | .addOption(crOpt('--must-find-files', 'Error if no files are found.').default(true).hideHelp())
|
93 | .option('--no-must-find-files', 'Do not error if no files are found.')
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 | .addOption(crOpt('--legacy', 'Legacy output').hideHelp())
|
104 | .addOption(crOpt('--local <local>', 'Deprecated -- Use: --locale').hideHelp())
|
105 | .option('--cache', 'Use cache to only check changed files.')
|
106 | .option('--no-cache', 'Do not use cache.')
|
107 | .option('--cache-reset', 'Reset the cache file.')
|
108 | .addOption(crOpt('--cache-strategy <strategy>', 'Strategy to use for detecting changed files.').choices([
|
109 | 'metadata',
|
110 | 'content',
|
111 | ]))
|
112 | .option('--cache-location <path>', `Path to the cache file or directory. (default: "${DEFAULT_CACHE_LOCATION}")`)
|
113 | .option('--dot', 'Include files and directories starting with `.` (period) when matching globs.')
|
114 | .option('--gitignore', 'Ignore files matching glob patterns found in .gitignore files.')
|
115 | .option('--no-gitignore', 'Do NOT use .gitignore files.')
|
116 | .option('--gitignore-root <path>', 'Prevent searching for .gitignore files past root.', collect)
|
117 | .option('--validate-directives', 'Validate in-document CSpell directives.')
|
118 | .addOption(crOpt('--no-validate-directives', 'Do not validate in-document CSpell directives.').hideHelp())
|
119 | .option('--no-color', 'Turn off color.')
|
120 | .option('--color', 'Force color.')
|
121 | .addOption(crOpt('--default-configuration', 'Load the default configuration and dictionaries.').hideHelp())
|
122 | .addOption(crOpt('--no-default-configuration', 'Do not load the default configuration and dictionaries.'))
|
123 | .option('--debug', 'Output information useful for debugging cspell.json files.')
|
124 | .option('--reporter <module|path>', 'Specify one or more reporters to use.', collect)
|
125 | .addOption(crOpt('--skip-validation', 'Collect and process documents, but do not spell check.')
|
126 | .implies({ cache: false })
|
127 | .hideHelp())
|
128 | .addOption(crOpt('--issues-summary-report', 'Output a summary of issues found.').hideHelp())
|
129 | .addOption(crOpt('--show-perf-summary', 'Output a performance summary report.').hideHelp())
|
130 | .option('--issue-template [template]', 'Use a custom issue template. See --help --issue-template for details.')
|
131 |
|
132 |
|
133 |
|
134 |
|
135 | .usage(usage)
|
136 | .addHelpText('after', augmentCommandHelp)
|
137 | .arguments('[globs...]')
|
138 | .action(async (fileGlobs, options) => {
|
139 |
|
140 | const useExitCode = options.exitCode ?? true;
|
141 | if (options.skipValidation) {
|
142 | options.cache = false;
|
143 | }
|
144 | App.parseApplicationFeatureFlags(options.flag);
|
145 | const { mustFindFiles, fileList, files, file } = options;
|
146 | const result = await App.lint(fileGlobs, options);
|
147 | if (!fileGlobs.length && !result.files && !result.errors && !fileList && !files?.length && !file?.length) {
|
148 | spellCheckCommand.outputHelp();
|
149 | throw new CheckFailed('outputHelp', 1);
|
150 | }
|
151 | if (result.errors || (mustFindFiles && !result.files)) {
|
152 | throw new CheckFailed('check failed', 1);
|
153 | }
|
154 | if (result.issues) {
|
155 | const exitCode = useExitCode ? 1 : 0;
|
156 | throw new CheckFailed('check failed', exitCode);
|
157 | }
|
158 | return;
|
159 | });
|
160 | return spellCheckCommand;
|
161 | }
|
162 | function helpIssueTemplate(opts) {
|
163 | if (!('issueTemplate' in opts))
|
164 | return '';
|
165 | return unindent `
|
166 | Issue Template:
|
167 | Use "--issue-template" to set the template to use when reporting issues.
|
168 |
|
169 | The template is a string that can contain the following placeholders:
|
170 | - $filename - the file name
|
171 | - $col - the column number
|
172 | - $row - the row number
|
173 | - $text - the word that is misspelled
|
174 | - $message - the issues message: "unknown word", "word is misspelled", etc.
|
175 | - $messageColored - the issues message with color based upon the message type.
|
176 | - $uri - the URI of the file
|
177 | - $suggestions - suggestions for the misspelled word (if requested)
|
178 | - $quickFix - possible quick fixes for the misspelled word.
|
179 | - $contextFull - the full context of the misspelled word.
|
180 | - $contextLeft - the context to the left of the misspelled word.
|
181 | - $contextRight - the context to the right of the misspelled word.
|
182 |
|
183 | Color is supported using the following template pattern:
|
184 | - \`{<style[.style]> <text>}\` - where \`<style>\` is a style name and \`<text>\` is the text to style.
|
185 |
|
186 | Styles
|
187 | - \`bold\`, \`italic\`, \`underline\`, \`strikethrough\`, \`dim\`, \`inverse\`
|
188 | - \`black\`, \`red\`, \`green\`, \`yellow\`, \`blue\`, \`magenta\`, \`cyan\`, \`white\`
|
189 |
|
190 | Example:
|
191 | --issue-template '{green $filename}:{yellow $row}:{yellow $col} $message {red $text} $quickFix {dim $suggestions}'
|
192 | `;
|
193 | }
|
194 |
|
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
200 | function augmentCommandHelp(context) {
|
201 | const output = [];
|
202 | const command = context.command;
|
203 | const opts = command.opts();
|
204 | const showHidden = !!opts.verbose;
|
205 | const hiddenHelp = [];
|
206 | const help = command.createHelp();
|
207 | const hiddenOptions = command.options.filter((opt) => opt.hidden && showHidden);
|
208 | const flagColWidth = Math.max(...command.options.map((opt) => opt.flags.length), 0);
|
209 | const indent = flagColWidth + 4;
|
210 | for (const options of hiddenOptions) {
|
211 | if (!hiddenHelp.length) {
|
212 | hiddenHelp.push('\nHidden Options:');
|
213 | }
|
214 | hiddenHelp.push(help.wrap(` ${options.flags.padEnd(flagColWidth)} ${options.description}`, process.stdout.columns || 80, indent));
|
215 | }
|
216 | output.push(...hiddenHelp, advanced);
|
217 | return helpIssueTemplate(opts) + output.join('\n');
|
218 | }
|
219 |
|
220 |
|
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 |
|
227 | function crOpt(name, description, parseArg, defaultValue) {
|
228 | const option = new CommanderOption(name, description);
|
229 | if (parseArg) {
|
230 | option.argParser(parseArg);
|
231 | }
|
232 | if (defaultValue !== undefined) {
|
233 | option.default(defaultValue);
|
234 | }
|
235 | return option;
|
236 | }
|
237 |
|
\ | No newline at end of file |