cspell
Version:
A Spelling Checker for Code!
237 lines (225 loc) • 12.8 kB
JavaScript
import { Option as CommanderOption } from 'commander';
import * as App from './application.mjs';
import { DEFAULT_CACHE_LOCATION } from './util/cache/index.js';
import { CheckFailed } from './util/errors.js';
import { unindent } from './util/unindent.js';
// interface InitOptions extends Options {}
const usage = `\
[ ] [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, previous) {
const values = Array.isArray(value) ? value : [value];
return previous ? [...previous, ...values] : values;
}
export function commandLint(prog) {
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 <file-type>', 'Force programming language for unknown extensions. i.e. "php" or "scala"')
.addOption(crOpt('--languageId <file-type>', 'Alias of "--language-id". 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...]', 'Alias of "--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.')
.addOption(crOpt('--no-validate-directives', 'Do not validate in-document CSpell directives.').hideHelp())
.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())
.addOption(crOpt('--show-perf-summary', 'Output a performance summary report.').hideHelp())
.option('--issue-template [template]', 'Use a custom issue template. See --help --issue-template for details.')
// 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', augmentCommandHelp)
.arguments('[globs...]')
.action(async (fileGlobs, options) => {
// 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;
}
function helpIssueTemplate(opts) {
if (!('issueTemplate' in opts))
return '';
return unindent `
Issue Template:
Use "--issue-template" to set the template to use when reporting issues.
The template is a string that can contain the following placeholders:
- $filename - the file name
- $col - the column number
- $row - the row number
- $text - the word that is misspelled
- $message - the issues message: "unknown word", "word is misspelled", etc.
- $messageColored - the issues message with color based upon the message type.
- $uri - the URI of the file
- $suggestions - suggestions for the misspelled word (if requested)
- $quickFix - possible quick fixes for the misspelled word.
- $contextFull - the full context of the misspelled word.
- $contextLeft - the context to the left of the misspelled word.
- $contextRight - the context to the right of the misspelled word.
Color is supported using the following template pattern:
- \`{<style[.style]> <text>}\` - where \`<style>\` is a style name and \`<text>\` is the text to style.
Styles
- \`bold\`, \`italic\`, \`underline\`, \`strikethrough\`, \`dim\`, \`inverse\`
- \`black\`, \`red\`, \`green\`, \`yellow\`, \`blue\`, \`magenta\`, \`cyan\`, \`white\`
Example:
--issue-template '{green $filename}:{yellow $row}:{yellow $col} $message {red $text} $quickFix {dim $suggestions}'
`;
}
/**
* Add additional help text to the command.
* When the verbose flag is set, show the hidden options.
* @param context
* @returns
*/
function augmentCommandHelp(context) {
const output = [];
const command = context.command;
const opts = command.opts();
const showHidden = !!opts.verbose;
const hiddenHelp = [];
const help = command.createHelp();
const hiddenOptions = command.options.filter((opt) => opt.hidden && showHidden);
const flagColWidth = Math.max(...command.options.map((opt) => opt.flags.length), 0);
const indent = flagColWidth + 4;
for (const options of hiddenOptions) {
if (!hiddenHelp.length) {
hiddenHelp.push('\nHidden Options:');
}
hiddenHelp.push(help.wrap(` ${options.flags.padEnd(flagColWidth)} ${options.description}`, process.stdout.columns || 80, indent));
}
output.push(...hiddenHelp, advanced);
return helpIssueTemplate(opts) + output.join('\n');
}
/**
* 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(name, description, parseArg, defaultValue) {
const option = new CommanderOption(name, description);
if (parseArg) {
option.argParser(parseArg);
}
if (defaultValue !== undefined) {
option.default(defaultValue);
}
return option;
}
//# sourceMappingURL=commandLint.js.map