diff --git a/docs/user-guide/usage/options.md b/docs/user-guide/usage/options.md index a198b4e1a1..b4dff0ac93 100644 --- a/docs/user-guide/usage/options.md +++ b/docs/user-guide/usage/options.md @@ -81,6 +81,7 @@ Specify the formatter to format your results. Options are: - `compact` - generates output similar to ESLint's compact formatter +- `github` - generates messages via [workflow commands for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions) - `json` (default for Node API) - generates [JSON](https://www.json.org) that can be consumed by another tool - `string` (default for CLI) - generates human-readable strings - `tap` - generates [Test Anything Protocol](http://testanything.org/) output diff --git a/lib/__tests__/__snapshots__/cli.test.js.snap b/lib/__tests__/__snapshots__/cli.test.js.snap index 22fd4407e1..f7ae931980 100644 --- a/lib/__tests__/__snapshots__/cli.test.js.snap +++ b/lib/__tests__/__snapshots__/cli.test.js.snap @@ -89,7 +89,7 @@ exports[`CLI --help 1`] = ` --formatter, -f [default: \\"string\\"] - The output formatter: \\"compact\\", \\"json\\", \\"string\\", \\"tap\\", \\"unix\\" or \\"verbose\\". + The output formatter: \\"compact\\", \\"github\\", \\"json\\", \\"string\\", \\"tap\\", \\"unix\\" or \\"verbose\\". --custom-formatter diff --git a/lib/__tests__/standalone-formatter.test.js b/lib/__tests__/standalone-formatter.test.js index dfaca42b7e..fc13fdd6c7 100644 --- a/lib/__tests__/standalone-formatter.test.js +++ b/lib/__tests__/standalone-formatter.test.js @@ -43,6 +43,6 @@ it('standalone with invalid formatter option', async () => { formatter: 'invalid', }), ).rejects.toThrow( - 'You must use a valid formatter option: "compact", "json", "string", "tap", "unix", "verbose" or a function', + 'You must use a valid formatter option: "compact", "github", "json", "string", "tap", "unix", "verbose" or a function', ); }); diff --git a/lib/formatters/__tests__/githubFormatter.test.js b/lib/formatters/__tests__/githubFormatter.test.js new file mode 100644 index 0000000000..b2e88296f6 --- /dev/null +++ b/lib/formatters/__tests__/githubFormatter.test.js @@ -0,0 +1,38 @@ +'use strict'; + +const githubFormatter = require('../githubFormatter'); + +test('githubFormatter', () => { + const results = [ + { + source: 'path/to/file.css', + warnings: [ + { + line: 1, + column: 2, + endLine: 1, + endColumn: 5, + rule: 'foo', + severity: 'error', + text: 'Unexpected "foo" (foo)', + }, + ], + }, + { + source: 'a.css', + warnings: [ + { + line: 10, + column: 20, + rule: 'bar', + severity: 'warning', + text: 'Unexpected "bar" (bar)', + }, + ], + }, + ]; + + expect(githubFormatter(results)) + .toBe(`::error file=path/to/file.css,line=1,col=2,endLine=1,endColumn=5,title=Stylelint problem::Unexpected "foo" (foo) +::warning file=a.css,line=10,col=20,title=Stylelint problem::Unexpected "bar" (bar)`); +}); diff --git a/lib/formatters/githubFormatter.js b/lib/formatters/githubFormatter.js new file mode 100644 index 0000000000..604e7110db --- /dev/null +++ b/lib/formatters/githubFormatter.js @@ -0,0 +1,20 @@ +'use strict'; + +/** + * @see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions + * + * @type {import('stylelint').Formatter} + */ +module.exports = function githubFormatter(results) { + const title = 'Stylelint problem'; + + return results + .flatMap(({ source, warnings }) => + warnings.map(({ line, column, endLine, endColumn, text, severity }) => { + return endLine === undefined + ? `::${severity} file=${source},line=${line},col=${column},title=${title}::${text}` + : `::${severity} file=${source},line=${line},col=${column},endLine=${endLine},endColumn=${endColumn},title=${title}::${text}`; + }), + ) + .join('\n'); +}; diff --git a/lib/formatters/index.js b/lib/formatters/index.js index aee1fa4f1c..8f92833d25 100644 --- a/lib/formatters/index.js +++ b/lib/formatters/index.js @@ -7,6 +7,7 @@ const importLazy = _importLazy(require); /** @type {typeof import('stylelint').formatters} */ const formatters = { compact: importLazy('./compactFormatter'), + github: importLazy('./githubFormatter'), json: importLazy('./jsonFormatter'), string: importLazy('./stringFormatter'), tap: importLazy('./tapFormatter'), diff --git a/lib/utils/__tests__/getFormatterOptionsText.test.js b/lib/utils/__tests__/getFormatterOptionsText.test.js index 824cf1a163..93e003648b 100644 --- a/lib/utils/__tests__/getFormatterOptionsText.test.js +++ b/lib/utils/__tests__/getFormatterOptionsText.test.js @@ -3,8 +3,10 @@ const getFormatterOptionsText = require('../getFormatterOptionsText'); it('getFormatterOptionsText', () => { - expect(getFormatterOptionsText()).toBe('"compact", "json", "string", "tap", "unix", "verbose"'); + expect(getFormatterOptionsText()).toBe( + '"compact", "github", "json", "string", "tap", "unix", "verbose"', + ); expect(getFormatterOptionsText({ useOr: true })).toBe( - '"compact", "json", "string", "tap", "unix" or "verbose"', + '"compact", "github", "json", "string", "tap", "unix" or "verbose"', ); }); diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts index 2ed74b819b..7496d8dd3b 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -128,7 +128,14 @@ declare module 'stylelint' { export type Formatter = (results: LintResult[], returnValue?: LinterResult) => string; - export type FormatterType = 'compact' | 'json' | 'string' | 'tap' | 'unix' | 'verbose'; + export type FormatterType = + | 'compact' + | 'github' + | 'json' + | 'string' + | 'tap' + | 'unix' + | 'verbose'; export type CustomSyntax = string | PostCSS.Syntax;