diff --git a/@commitlint/cli/src/cli.test.ts b/@commitlint/cli/src/cli.test.ts index 7182a12761..f3107a6d5e 100644 --- a/@commitlint/cli/src/cli.test.ts +++ b/@commitlint/cli/src/cli.test.ts @@ -644,6 +644,7 @@ test("should print help", async () => { -q, --quiet toggle console output [boolean] [default: false] -t, --to upper end of the commit range to lint; applies if edit=false [string] -V, --verbose enable verbose output for reports without problems [boolean] + --legacy-output use the legacy input output format (single-line 'input: ...') [boolean] -s, --strict enable strict mode; result code 2 for warnings, 3 for errors [boolean] --options path to a JSON file or Common.js module containing CLI options -v, --version display version information [boolean] diff --git a/@commitlint/cli/src/cli.ts b/@commitlint/cli/src/cli.ts index dabedae66f..89bcdbc041 100644 --- a/@commitlint/cli/src/cli.ts +++ b/@commitlint/cli/src/cli.ts @@ -135,6 +135,11 @@ const cli = yargs(process.argv.slice(2)) type: "boolean", description: "enable verbose output for reports without problems", }, + "legacy-output": { + description: + "use the legacy input output format (single-line 'input: ...')", + type: "boolean", + }, strict: { alias: "s", type: "boolean", @@ -398,6 +403,7 @@ async function main(args: MainArgs): Promise { color: flags.color, verbose: flags.verbose, helpUrl, + legacyOutput: flags["legacy-output"], }); if (!flags.quiet && output !== "") { diff --git a/@commitlint/cli/src/types.ts b/@commitlint/cli/src/types.ts index cbc9a8956a..818772ab9e 100644 --- a/@commitlint/cli/src/types.ts +++ b/@commitlint/cli/src/types.ts @@ -20,6 +20,7 @@ export interface CliFlags { /** @type {'' | 'text' | 'json'} */ "print-config"?: string; strict?: boolean; + "legacy-output"?: boolean; _: (string | number)[]; $0: string; } diff --git a/@commitlint/config-lerna-scopes/readme.md b/@commitlint/config-lerna-scopes/readme.md index 3d9d9ee3ee..e9393b65bf 100644 --- a/@commitlint/config-lerna-scopes/readme.md +++ b/@commitlint/config-lerna-scopes/readme.md @@ -31,16 +31,19 @@ packages └── web ❯ echo "build(api): change something in api's build" | commitlint -⧗ input: build(api): change something in api's build +⧗ --- input --- +build(api): change something in api's build ✔ found 0 problems, 0 warnings ❯ echo "test(foo): this won't pass" | commitlint -⧗ input: test(foo): this won't pass +⧗ --- input --- +test(foo): this won't pass ✖ scope must be one of [api, app, web] [scope-enum] ✖ found 1 problems, 0 warnings ❯ echo "ci: do some general maintenance" | commitlint -⧗ input: ci: do some general maintenance +⧗ --- input --- +ci: do some general maintenance ✔ found 0 problems, 0 warnings ``` diff --git a/@commitlint/config-nx-scopes/readme.md b/@commitlint/config-nx-scopes/readme.md index 2890f85872..15d4a3645c 100644 --- a/@commitlint/config-nx-scopes/readme.md +++ b/@commitlint/config-nx-scopes/readme.md @@ -97,16 +97,19 @@ packages └── web ❯ echo "build(api): change something in api's build" | commitlint -⧗ input: build(api): change something in api's build +⧗ --- input --- +build(api): change something in api's build ✔ found 0 problems, 0 warnings ❯ echo "test(foo): this won't pass" | commitlint -⧗ input: test(foo): this won't pass +⧗ --- input --- +test(foo): this won't pass ✖ scope must be one of [api, app, web] [scope-enum] ✖ found 1 problems, 0 warnings ❯ echo "ci: do some general maintenance" | commitlint -⧗ input: ci: do some general maintenance +⧗ --- input --- +ci: do some general maintenance ✔ found 0 problems, 0 warnings ``` diff --git a/@commitlint/config-pnpm-scopes/readme.md b/@commitlint/config-pnpm-scopes/readme.md index 45d65af9dc..e393c4d1aa 100644 --- a/@commitlint/config-pnpm-scopes/readme.md +++ b/@commitlint/config-pnpm-scopes/readme.md @@ -28,16 +28,19 @@ packages └── web ❯ echo "build(api): change something in api's build" | commitlint -⧗ input: build(api): change something in api's build +⧗ --- input --- +build(api): change something in api's build ✔ found 0 problems, 0 warnings ❯ echo "test(foo): this won't pass" | commitlint -⧗ input: test(foo): this won't pass +⧗ --- input --- +test(foo): this won't pass ✖ scope must be one of [api, app, web] [scope-enum] ✖ found 1 problems, 0 warnings ❯ echo "ci: do some general maintenance" | commitlint -⧗ input: ci: do some general maintenance +⧗ --- input --- +ci: do some general maintenance ✔ found 0 problems, 0 warnings ``` diff --git a/@commitlint/config-rush-scopes/readme.md b/@commitlint/config-rush-scopes/readme.md index 505b681720..a3145e6cae 100644 --- a/@commitlint/config-rush-scopes/readme.md +++ b/@commitlint/config-rush-scopes/readme.md @@ -28,16 +28,19 @@ packages └── web ❯ echo "build(api): change something in api's build" | commitlint -⧗ input: build(api): change something in api's build +⧗ --- input --- +build(api): change something in api's build ✔ found 0 problems, 0 warnings ❯ echo "test(foo): this won't pass" | commitlint -⧗ input: test(foo): this won't pass +⧗ --- input --- +test(foo): this won't pass ✖ scope must be one of [api, app, web] [scope-enum] ✖ found 1 problems, 0 warnings ❯ echo "ci: do some general maintenance" | commitlint -⧗ input: ci: do some general maintenance +⧗ --- input --- +ci: do some general maintenance ✔ found 0 problems, 0 warnings ``` diff --git a/@commitlint/format/src/format.test.ts b/@commitlint/format/src/format.test.ts index 3388e96afb..b856853574 100644 --- a/@commitlint/format/src/format.test.ts +++ b/@commitlint/format/src/format.test.ts @@ -53,11 +53,94 @@ test("returns empty summary with full commit message if verbose", () => { }, ); + expect(actual).toStrictEqual( + "⧗ --- input ---\nfeat(cli): this is a valid header\n\nThis is a valid body\n\nSigned-off-by: tester\n✔ found 0 problems, 0 warnings", + ); +}); + +test("returns legacy output with full commit message if verbose and legacyOutput", () => { + const actual = format( + { + results: [ + { + errors: [], + warnings: [], + input: + "feat(cli): this is a valid header\n\nThis is a valid body\n\nSigned-off-by: tester", + }, + ], + }, + { + verbose: true, + color: false, + legacyOutput: true, + }, + ); + expect(actual).toStrictEqual( "⧗ input: feat(cli): this is a valid header\n\nThis is a valid body\n\nSigned-off-by: tester\n✔ found 0 problems, 0 warnings", ); }); +test("returns input banner with errors and multi-line input", () => { + const actual = format( + { + results: [ + { + errors: [ + { + level: 2, + name: "type-enum", + message: + "type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test]", + }, + ], + warnings: [], + input: + "foo: this is an invalid header\n\nThis is a body\n\nSigned-off-by: tester", + }, + ], + }, + { + color: false, + }, + ); + + expect(actual).toStrictEqual( + "⧗ --- input ---\nfoo: this is an invalid header\n\nThis is a body\n\nSigned-off-by: tester\n✖ type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum]\n\n✖ found 1 problems, 0 warnings\n", + ); +}); + +test("returns legacy input banner with errors and multi-line input", () => { + const actual = format( + { + results: [ + { + errors: [ + { + level: 2, + name: "type-enum", + message: + "type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test]", + }, + ], + warnings: [], + input: + "foo: this is an invalid header\n\nThis is a body\n\nSigned-off-by: tester", + }, + ], + }, + { + color: false, + legacyOutput: true, + }, + ); + + expect(actual).toStrictEqual( + "⧗ input: foo: this is an invalid header\n\nThis is a body\n\nSigned-off-by: tester\n✖ type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum]\n\n✖ found 1 problems, 0 warnings\n", + ); +}); + test("returns a correct summary of empty .errors and .warnings", () => { const actualError = format({ results: [ diff --git a/@commitlint/format/src/format.ts b/@commitlint/format/src/format.ts index 2496d7272e..998cfc9b2b 100644 --- a/@commitlint/format/src/format.ts +++ b/@commitlint/format/src/format.ts @@ -37,7 +37,7 @@ function formatInput( result: FormattableResult & WithInput, options: FormatOptions = {}, ): string[] { - const { color: enabled = true } = options; + const { color: enabled = true, legacyOutput = false } = options; const { errors = [], warnings = [], input = "" } = result; if (!input) { @@ -50,9 +50,16 @@ function formatInput( const decoratedInput = enabled ? pc.bold(input) : input; const hasProblems = errors.length > 0 || warnings.length > 0; - return options.verbose || hasProblems - ? [`${decoration} input: ${decoratedInput}`] - : []; + if (!(options.verbose || hasProblems)) { + return []; + } + + if (legacyOutput) { + // legacy: single line with 'input: ' prefix, no extra newlines or dashes + return [`${decoration} input: ${decoratedInput}`]; + } + + return [`${decoration} --- input ---\n${decoratedInput}`]; } export function formatResult( diff --git a/@commitlint/types/src/format.ts b/@commitlint/types/src/format.ts index ca9f87495a..31742e649e 100644 --- a/@commitlint/types/src/format.ts +++ b/@commitlint/types/src/format.ts @@ -41,4 +41,5 @@ export interface FormatOptions { colors?: readonly [PicocolorsColor, PicocolorsColor, PicocolorsColor]; verbose?: boolean; helpUrl?: string; + legacyOutput?: boolean; } diff --git a/docs/guides/local-setup.md b/docs/guides/local-setup.md index 8da8d26353..b74bb32521 100644 --- a/docs/guides/local-setup.md +++ b/docs/guides/local-setup.md @@ -271,7 +271,8 @@ You can test the hook by simply committing. You should see something like this i git commit -m "foo: this will fail" # husky > commit-msg No staged files match any of provided globs. -⧗ input: foo: this will fail +⧗ --- input --- +foo: this will fail ✖ type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum] ✖ found 1 problems, 0 warnings @@ -290,4 +291,7 @@ No staged files match any of provided globs. # husky > commit-msg ``` +Since [v22.0.0](https://github.com/conventional-changelog/commitlint/releases/tag/v22.0.0) `commitlint` will output the commit message after a new line (EOL) instead of after a colon.\ +(You can use the `--legacy-output` flag to get the previous output format used in older versions) + Local linting is fine for fast feedback but can easily be tinkered with. To ensure all commits are linted you'll want to check commits on an automated CI Server too. Learn how to in the [CI Setup guide](/guides/ci-setup).