diff --git a/cspell.schema.json b/cspell.schema.json index c0b5a2a928ac..91fbb9b63530 100644 --- a/cspell.schema.json +++ b/cspell.schema.json @@ -1075,6 +1075,11 @@ }, "type": "array" }, + "failFast": { + "default": false, + "description": "Exit with non-zero code as soon as an issue/error encountered (useful for CI or git hooks)", + "type": "boolean" + }, "features": { "$ref": "#/definitions/Features", "description": "Configure CSpell features.\n\n- Added with `v5.16.0`." diff --git a/packages/cspell-lib/src/__snapshots__/index.test.ts.snap b/packages/cspell-lib/src/__snapshots__/index.test.ts.snap index 4aff7c1178af..a4e2792c5be6 100644 --- a/packages/cspell-lib/src/__snapshots__/index.test.ts.snap +++ b/packages/cspell-lib/src/__snapshots__/index.test.ts.snap @@ -21,6 +21,7 @@ Object { "enableGlobDot": "enableGlobDot", "enabled": "enabled", "enabledLanguageIds": "enabledLanguageIds", + "failFast": "failFast", "features": "features", "files": "files", "flagWords": "flagWords", diff --git a/packages/cspell-types/cspell.schema.json b/packages/cspell-types/cspell.schema.json index c0b5a2a928ac..91fbb9b63530 100644 --- a/packages/cspell-types/cspell.schema.json +++ b/packages/cspell-types/cspell.schema.json @@ -1075,6 +1075,11 @@ }, "type": "array" }, + "failFast": { + "default": false, + "description": "Exit with non-zero code as soon as an issue/error encountered (useful for CI or git hooks)", + "type": "boolean" + }, "features": { "$ref": "#/definitions/Features", "description": "Configure CSpell features.\n\n- Added with `v5.16.0`." diff --git a/packages/cspell-types/src/CSpellSettingsDef.ts b/packages/cspell-types/src/CSpellSettingsDef.ts index 0727194e9681..2a1debe158cd 100644 --- a/packages/cspell-types/src/CSpellSettingsDef.ts +++ b/packages/cspell-types/src/CSpellSettingsDef.ts @@ -266,6 +266,11 @@ export interface CommandLineSettings { * Define cache settings. */ cache?: CacheSettings; + /** + * Exit with non-zero code as soon as an issue/error encountered (useful for CI or git hooks) + * @default false + */ + failFast?: boolean; } /** diff --git a/packages/cspell-types/src/configFields.ts b/packages/cspell-types/src/configFields.ts index 5b449056bd0d..cd8085aa2b8b 100644 --- a/packages/cspell-types/src/configFields.ts +++ b/packages/cspell-types/src/configFields.ts @@ -17,6 +17,7 @@ export const ConfigFields: CSpellUserSettingsFields = { enabledLanguageIds: 'enabledLanguageIds', enableFiletypes: 'enableFiletypes', enableGlobDot: 'enableGlobDot', + failFast: 'failFast', features: 'features', files: 'files', flagWords: 'flagWords', diff --git a/packages/cspell/samples/fail-fast/cspell.json b/packages/cspell/samples/fail-fast/cspell.json new file mode 100644 index 000000000000..9c4337806c60 --- /dev/null +++ b/packages/cspell/samples/fail-fast/cspell.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", + "version": "0.2", + "language": "en", + "files": ["**/*.txt"] +} diff --git a/packages/cspell/samples/fail-fast/fail-fast-cspell.json b/packages/cspell/samples/fail-fast/fail-fast-cspell.json new file mode 100644 index 000000000000..0429921995e3 --- /dev/null +++ b/packages/cspell/samples/fail-fast/fail-fast-cspell.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", + "import": "./cspell.json", + "failFast": true +} diff --git a/packages/cspell/samples/fail-fast/first-fail.txt b/packages/cspell/samples/fail-fast/first-fail.txt new file mode 100644 index 000000000000..e44a66c2115a --- /dev/null +++ b/packages/cspell/samples/fail-fast/first-fail.txt @@ -0,0 +1 @@ +iamtypo diff --git a/packages/cspell/samples/fail-fast/second-fail.txt b/packages/cspell/samples/fail-fast/second-fail.txt new file mode 100644 index 000000000000..963891671617 --- /dev/null +++ b/packages/cspell/samples/fail-fast/second-fail.txt @@ -0,0 +1 @@ +iamtypotoo diff --git a/packages/cspell/src/lint/lint.test.ts b/packages/cspell/src/lint/lint.test.ts index 04e1682590f6..9e4d0335e6dd 100644 --- a/packages/cspell/src/lint/lint.test.ts +++ b/packages/cspell/src/lint/lint.test.ts @@ -6,6 +6,7 @@ import { runLint } from './lint'; const root = path.resolve(__dirname, '../..'); const samples = path.resolve(root, 'samples'); const latexSamples = path.resolve(samples, 'latex'); +const failFastSamples = path.resolve(samples, 'fail-fast'); const hiddenSamples = path.resolve(samples, 'hidden-test'); const filesToCheck = path.resolve(root, 'fixtures/features/file-list/files-to-check.txt'); const filesToCheckWithMissing = path.resolve(root, 'fixtures/features/file-list/files-to-check-missing.txt'); @@ -27,6 +28,8 @@ describe('Linter Validation Tests', () => { test.each` files | options | expectedRunResult | expectedReport ${[]} | ${{ root: latexSamples }} | ${oc({ errors: 0, files: 4 })} | ${oc({ errorCount: 0, errors: [], issues: [oc({ text: 'Tufte' })] })} + ${['*.txt']} | ${{ root: failFastSamples }} | ${oc({ errors: 0, files: 2 })} | ${oc({ errorCount: 0, errors: [], issues: oc({ length: 2 }) })} + ${['*.txt']} | ${{ root: failFastSamples, config: j(samples, 'fail-fast', 'fail-fast-cspell.json') }} | ${oc({ errors: 0, files: 1 })} | ${oc({ errorCount: 0, errors: [], issues: oc({ length: 1 }) })} ${['**/ebook.tex']} | ${{ root: latexSamples }} | ${oc({ errors: 0, files: 1 })} | ${oc({ errorCount: 0, errors: [], issues: [] })} ${['**/ebook.tex']} | ${{ root: latexSamples, gitignore: true }} | ${oc({ errors: 0, files: 1 })} | ${oc({ errorCount: 0, errors: [], issues: [] })} ${['**/hidden.md']} | ${{ root: hiddenSamples }} | ${oc({ errors: 0, files: 0 })} | ${oc({ errorCount: 0, errors: [], issues: [] })} diff --git a/packages/cspell/src/lint/lint.ts b/packages/cspell/src/lint/lint.ts index 362e8c3c9e5c..02beb3e2be97 100644 --- a/packages/cspell/src/lint/lint.ts +++ b/packages/cspell/src/lint/lint.ts @@ -160,6 +160,9 @@ export async function runLint(cfg: LintRequest): Promise { status.filesWithIssues.add(filename); status.issues += result.issues.length; status.errors += result.errors; + if (configInfo.config.failFast === true) { + return status; + } } status.errors += result.configErrors; }