eslint-formatter-gitlab 5.1.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -11
- package/{index.js → lib/eslint-formatter-gitlab.js} +65 -61
- package/package.json +17 -14
- package/types/eslint-formatter-gitlab.d.ts +12 -0
- package/types/eslint-formatter-gitlab.d.ts.map +1 -0
- package/index.d.ts +0 -11
- package/index.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
# ESLint Formatter for GitLab
|
|
2
2
|
|
|
3
|
+
[](https://gitlab.com/remcohaszing/eslint-formatter-gitlab/-/pipelines)
|
|
4
|
+
[](https://gitlab.com/remcohaszing/eslint-formatter-gitlab/-/pipelines)
|
|
5
|
+
[](https://github.com/sponsors/remcohaszing)
|
|
6
|
+
[](https://www.npmjs.com/package/eslint-formatter-gitlab)
|
|
7
|
+
[](https://www.npmjs.com/package/eslint-formatter-gitlab)
|
|
8
|
+
|
|
9
|
+
<img alt="" height="256" src="https://gitlab.com/remcohaszing/eslint-formatter-gitlab/-/avatar">
|
|
10
|
+
|
|
3
11
|
Show ESLint results directly in the
|
|
4
|
-
[GitLab code quality](https://docs.gitlab.com/ee/
|
|
5
|
-
results.
|
|
12
|
+
[GitLab code quality](https://docs.gitlab.com/ee/ci/testing/code_quality.html) results.
|
|
6
13
|
|
|
7
14
|
## Table of Contents
|
|
8
15
|
|
|
9
|
-
- [Requirements](#requirements)
|
|
10
16
|
- [Installation](#installation)
|
|
11
17
|
- [Usage](#usage)
|
|
18
|
+
- [Programmatic usage](#programmatic-usage)
|
|
12
19
|
- [Example](#example)
|
|
13
20
|
- [Configuration](#configuration)
|
|
21
|
+
- [Compatibility](#compatibility)
|
|
14
22
|
- [License](#license)
|
|
15
23
|
|
|
16
|
-
## Requirements
|
|
17
|
-
|
|
18
|
-
This package requires at least Node.js 18 and ESLint 5.
|
|
19
|
-
|
|
20
24
|
## Installation
|
|
21
25
|
|
|
22
26
|
Install `eslint` and `eslint-formatter-gitlab` using your package manager.
|
|
@@ -29,7 +33,7 @@ npm install --save-dev eslint eslint-formatter-gitlab
|
|
|
29
33
|
|
|
30
34
|
Define a GitLab job to run `eslint`.
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
`.gitlab-ci.yml`:
|
|
33
37
|
|
|
34
38
|
```yaml
|
|
35
39
|
eslint:
|
|
@@ -43,7 +47,21 @@ eslint:
|
|
|
43
47
|
```
|
|
44
48
|
|
|
45
49
|
The formatter automatically detects a GitLab CI environment. It detects where to output the code
|
|
46
|
-
quality report based on the GitLab configuration file.
|
|
50
|
+
quality report based on the GitLab configuration file. It also prints ESLint issues to the GitLab
|
|
51
|
+
job console with links.
|
|
52
|
+
|
|
53
|
+
### Programmatic usage
|
|
54
|
+
|
|
55
|
+
The formatter can be used programmatically using ESLint.
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
import { ESLint } from 'eslint'
|
|
59
|
+
|
|
60
|
+
const eslint = new ESLint()
|
|
61
|
+
const formatter = await eslint.loadFormatter('gitlab')
|
|
62
|
+
const results = await eslint.lintFiles([])
|
|
63
|
+
const formatted = await formatter.format(results)
|
|
64
|
+
```
|
|
47
65
|
|
|
48
66
|
## Example
|
|
49
67
|
|
|
@@ -55,8 +73,20 @@ An example of the results can be seen in
|
|
|
55
73
|
|
|
56
74
|
ESLint formatters don’t take any configuration options. `eslint-formatter-gitlab` uses GitLab’s
|
|
57
75
|
[predefined environment variables](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html)
|
|
58
|
-
to configure the output.
|
|
59
|
-
|
|
76
|
+
to configure the output. The following predefined environment variables are used:
|
|
77
|
+
|
|
78
|
+
- `CI_COMMIT_SHORT_SHA` to generate a link in the console output.
|
|
79
|
+
- `CI_CONFIG_PATH` to determine the GitLab CI configuration file to use. (Default: `.gitlab-ci.yml`)
|
|
80
|
+
- `CI_JOB_NAME` to determine which job configuration to read the code quality report path from.
|
|
81
|
+
- `CI_PROJECT_DIR` To determine relative paths. (Default: current working directory)
|
|
82
|
+
- `CI_PROJECT_URL` to generate a link in the console output.
|
|
83
|
+
|
|
84
|
+
In addition, the environment variable `ESLINT_CODE_QUALITY_REPORT` is used to override the location
|
|
85
|
+
to store the code quality report.
|
|
86
|
+
|
|
87
|
+
## Compatibility
|
|
88
|
+
|
|
89
|
+
This package is compatible with Node.js 20 or greater and ESLint 9 or greater.
|
|
60
90
|
|
|
61
91
|
## License
|
|
62
92
|
|
|
@@ -1,20 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
CI_JOB_NAME,
|
|
13
|
-
CI_PROJECT_DIR = process.cwd(),
|
|
14
|
-
CI_PROJECT_URL,
|
|
15
|
-
ESLINT_CODE_QUALITY_REPORT,
|
|
16
|
-
GITLAB_CI
|
|
17
|
-
} = process.env
|
|
1
|
+
/**
|
|
2
|
+
* @import { Issue } from 'codeclimate-types'
|
|
3
|
+
* @import { ESLint, Linter } from 'eslint'
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createHash } from 'node:crypto'
|
|
7
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
|
8
|
+
import { dirname, join, relative, resolve } from 'node:path'
|
|
9
|
+
import { styleText } from 'node:util'
|
|
10
|
+
|
|
11
|
+
import yaml from 'yaml'
|
|
18
12
|
|
|
19
13
|
/** @type {yaml.CollectionTag} */
|
|
20
14
|
const reference = {
|
|
@@ -27,38 +21,45 @@ const reference = {
|
|
|
27
21
|
}
|
|
28
22
|
|
|
29
23
|
/**
|
|
30
|
-
* @
|
|
24
|
+
* @param {string} projectDir
|
|
25
|
+
* The GitLab project directory.
|
|
26
|
+
* @param {string | undefined} jobName
|
|
27
|
+
* The GitLab CI job name.
|
|
28
|
+
* @returns {Promise<string>}
|
|
31
29
|
* The output path of the code quality artifact.
|
|
32
30
|
*/
|
|
33
|
-
function getOutputPath() {
|
|
34
|
-
const configPath = join(
|
|
31
|
+
async function getOutputPath(projectDir, jobName) {
|
|
32
|
+
const configPath = join(projectDir, process.env.CI_CONFIG_PATH ?? '.gitlab-ci.yml')
|
|
35
33
|
// GitlabCI allows a custom configuration path which can be a URL or a path relative to another
|
|
36
34
|
// project. In these cases CI_CONFIG_PATH is empty and we'll have to require the user provide
|
|
37
35
|
// ESLINT_CODE_QUALITY_REPORT.
|
|
38
|
-
|
|
36
|
+
let configContents
|
|
37
|
+
try {
|
|
38
|
+
configContents = await readFile(configPath, 'utf8')
|
|
39
|
+
} catch {
|
|
39
40
|
throw new Error(
|
|
40
41
|
'Could not resolve .gitlab-ci.yml to automatically detect report artifact path.' +
|
|
41
42
|
' Please manually provide a path via the ESLINT_CODE_QUALITY_REPORT variable.'
|
|
42
43
|
)
|
|
43
44
|
}
|
|
44
|
-
const doc = yaml.parseDocument(
|
|
45
|
+
const doc = yaml.parseDocument(configContents, {
|
|
45
46
|
version: '1.1',
|
|
46
47
|
customTags: [reference]
|
|
47
48
|
})
|
|
48
|
-
const path = [
|
|
49
|
+
const path = [jobName, 'artifacts', 'reports', 'codequality']
|
|
49
50
|
const location = doc.getIn(path)
|
|
50
51
|
if (typeof location !== 'string' || !location) {
|
|
51
52
|
throw new TypeError(
|
|
52
53
|
`Expected ${path.join('.')} to be one exact path, got: ${JSON.stringify(location)}`
|
|
53
54
|
)
|
|
54
55
|
}
|
|
55
|
-
return resolve(
|
|
56
|
+
return resolve(projectDir, location)
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
/**
|
|
59
60
|
* @param {string} filePath
|
|
60
61
|
* The path to the linted file.
|
|
61
|
-
* @param {
|
|
62
|
+
* @param {Linter.LintMessage} message
|
|
62
63
|
* The ESLint report message.
|
|
63
64
|
* @param {Set<string>} hashes
|
|
64
65
|
* Hashes already encountered. Used to avoid duplicate hashes
|
|
@@ -91,25 +92,27 @@ function createFingerprint(filePath, message, hashes) {
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
/**
|
|
94
|
-
* @param {
|
|
95
|
+
* @param {ESLint.LintResult[]} results
|
|
95
96
|
* The ESLint report results.
|
|
96
|
-
* @param {
|
|
97
|
+
* @param {ESLint.LintResultData} data
|
|
97
98
|
* The ESLint report result data.
|
|
98
|
-
* @
|
|
99
|
+
* @param {string} projectDir
|
|
100
|
+
* The GitLab project directory.
|
|
101
|
+
* @returns {Issue[]}
|
|
99
102
|
* The ESLint messages in the form of a GitLab code quality report.
|
|
100
103
|
*/
|
|
101
|
-
function convert(results, data) {
|
|
102
|
-
/** @type {
|
|
104
|
+
function convert(results, data, projectDir) {
|
|
105
|
+
/** @type {Issue[]} */
|
|
103
106
|
const messages = []
|
|
104
107
|
|
|
105
108
|
/** @type {Set<string>} */
|
|
106
109
|
const hashes = new Set()
|
|
107
110
|
|
|
108
111
|
for (const result of results) {
|
|
109
|
-
const relativePath = relative(
|
|
112
|
+
const relativePath = relative(projectDir, result.filePath)
|
|
110
113
|
|
|
111
114
|
for (const message of result.messages) {
|
|
112
|
-
/** @type {
|
|
115
|
+
/** @type {Issue} */
|
|
113
116
|
const issue = {
|
|
114
117
|
type: 'issue',
|
|
115
118
|
categories: ['Style'],
|
|
@@ -175,23 +178,27 @@ function plural(count, text) {
|
|
|
175
178
|
}
|
|
176
179
|
|
|
177
180
|
/**
|
|
178
|
-
* @param {
|
|
181
|
+
* @param {ESLint.LintResult[]} results
|
|
179
182
|
* The ESLint report results.
|
|
183
|
+
* @param {string} projectDir
|
|
184
|
+
* The GitLab project directory.
|
|
180
185
|
* @returns {string}
|
|
181
186
|
* The ESLint messages converted to a format suitable as output in GitLab CI job logs.
|
|
182
187
|
*/
|
|
183
|
-
function gitlabConsoleFormatter(results) {
|
|
188
|
+
function gitlabConsoleFormatter(results, projectDir) {
|
|
184
189
|
// Severity labels manually padded to have equal lengths and end with spaces
|
|
185
|
-
const labelFatal = `${
|
|
186
|
-
const labelError = `${
|
|
187
|
-
const labelWarn = `${
|
|
190
|
+
const labelFatal = `${styleText('magenta', 'fatal')} `
|
|
191
|
+
const labelError = `${styleText('red', 'error')} `
|
|
192
|
+
const labelWarn = `${styleText('yellow', 'warn')} `
|
|
188
193
|
|
|
189
194
|
const lines = ['']
|
|
190
195
|
|
|
191
196
|
/** @type {string | undefined} */
|
|
192
197
|
let gitLabBaseURL
|
|
193
|
-
|
|
194
|
-
|
|
198
|
+
const projectUrl = process.env.CI_PROJECT_URL
|
|
199
|
+
const commitSha = process.env.CI_COMMIT_SHORT_SHA
|
|
200
|
+
if (projectUrl && commitSha) {
|
|
201
|
+
gitLabBaseURL = `${projectUrl}/-/blob/${commitSha}/`
|
|
195
202
|
}
|
|
196
203
|
|
|
197
204
|
let fatal = 0
|
|
@@ -214,7 +221,7 @@ function gitlabConsoleFormatter(results) {
|
|
|
214
221
|
|
|
215
222
|
for (const result of results) {
|
|
216
223
|
const { filePath, messages } = result
|
|
217
|
-
const repoFilePath = relative(
|
|
224
|
+
const repoFilePath = relative(projectDir, filePath)
|
|
218
225
|
|
|
219
226
|
for (const message of messages) {
|
|
220
227
|
let line = message.fatal ? labelFatal : message.severity === 1 ? labelWarn : labelError
|
|
@@ -227,7 +234,7 @@ function gitlabConsoleFormatter(results) {
|
|
|
227
234
|
if (message.endLine != null && message.endLine !== message.line) {
|
|
228
235
|
anchor += `-${message.endLine}`
|
|
229
236
|
}
|
|
230
|
-
line +=
|
|
237
|
+
line += styleText('blue', `${gitLabBaseURL}${repoFilePath}${anchor}`)
|
|
231
238
|
} else {
|
|
232
239
|
line += `${filePath}:${message.line}:${message.column}`
|
|
233
240
|
}
|
|
@@ -239,39 +246,36 @@ function gitlabConsoleFormatter(results) {
|
|
|
239
246
|
const total = warnings + errors + fatal
|
|
240
247
|
if (total > 0) {
|
|
241
248
|
const details = `(${fatal} fatal, ${plural(errors, 'error')}, ${plural(warnings, 'warning')})`
|
|
242
|
-
lines.push('', `${
|
|
249
|
+
lines.push('', `${styleText('red', '✖')} ${plural(total, 'problem')} ${details}`)
|
|
243
250
|
} else {
|
|
244
|
-
lines.push(`${
|
|
251
|
+
lines.push(`${styleText('green', '✔')} No problems found`)
|
|
245
252
|
}
|
|
246
253
|
|
|
247
254
|
lines.push('')
|
|
248
|
-
return lines.join(
|
|
255
|
+
return lines.join('\n')
|
|
249
256
|
}
|
|
250
257
|
|
|
251
258
|
/**
|
|
252
|
-
* @param {
|
|
259
|
+
* @param {ESLint.LintResult[]} results
|
|
253
260
|
* The ESLint report results.
|
|
254
|
-
* @param {
|
|
261
|
+
* @param {ESLint.LintResultData} data
|
|
255
262
|
* The ESLint report result data.
|
|
256
|
-
* @returns {string}
|
|
263
|
+
* @returns {Promise<string>}
|
|
257
264
|
* The ESLint output to print to the console.
|
|
258
265
|
*/
|
|
259
|
-
function eslintFormatterGitLab(results, data) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if (CI_JOB_NAME || ESLINT_CODE_QUALITY_REPORT) {
|
|
267
|
-
const issues = convert(results, data)
|
|
268
|
-
const outputPath = ESLINT_CODE_QUALITY_REPORT || getOutputPath()
|
|
266
|
+
async function eslintFormatterGitLab(results, data) {
|
|
267
|
+
let outputPath = process.env.ESLINT_CODE_QUALITY_REPORT
|
|
268
|
+
const projectDir = process.env.CI_PROJECT_DIR ?? data.cwd
|
|
269
|
+
const jobName = process.env.CI_JOB_NAME
|
|
270
|
+
if (jobName || outputPath) {
|
|
271
|
+
const issues = convert(results, data, projectDir)
|
|
272
|
+
outputPath ||= await getOutputPath(projectDir, jobName)
|
|
269
273
|
const dir = dirname(outputPath)
|
|
270
|
-
|
|
271
|
-
|
|
274
|
+
await mkdir(dir, { recursive: true })
|
|
275
|
+
await writeFile(outputPath, `${JSON.stringify(issues, null, 2)}\n`)
|
|
272
276
|
}
|
|
273
277
|
|
|
274
|
-
return gitlabConsoleFormatter(results)
|
|
278
|
+
return gitlabConsoleFormatter(results, projectDir)
|
|
275
279
|
}
|
|
276
280
|
|
|
277
|
-
|
|
281
|
+
export default eslintFormatterGitLab
|
package/package.json
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-formatter-gitlab",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"description": "Show ESLint results directly in the GitLab code quality results",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"author": "Remco Haszing <remcohaszing@gmail.com>",
|
|
6
7
|
"license": "MIT",
|
|
7
8
|
"homepage": "https://gitlab.com/remcohaszing/eslint-formatter-gitlab#readme",
|
|
8
9
|
"repository": "gitlab:remcohaszing/eslint-formatter-gitlab",
|
|
9
10
|
"funding": "https://github.com/sponsors/remcohaszing",
|
|
10
11
|
"bugs": "https://gitlab.com/remcohaszing/eslint-formatter-gitlab/-/issues",
|
|
11
|
-
"
|
|
12
|
+
"main": "./lib/eslint-formatter-gitlab.js",
|
|
13
|
+
"types": "./types/eslint-formatter-gitlab.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
"types": "./types/eslint-formatter-gitlab.d.ts",
|
|
16
|
+
"default": "./lib/eslint-formatter-gitlab.js"
|
|
17
|
+
},
|
|
12
18
|
"files": [
|
|
13
|
-
"
|
|
19
|
+
"lib",
|
|
20
|
+
"types"
|
|
14
21
|
],
|
|
15
22
|
"scripts": {
|
|
16
23
|
"prepack": "tsc --build",
|
|
17
|
-
"test": "c8 node --test --test-reporter
|
|
24
|
+
"test": "c8 node --test --test-reporter junit --test-reporter-destination=junit.xml --test-reporter spec --test-reporter-destination stdout"
|
|
18
25
|
},
|
|
19
26
|
"keywords": [
|
|
20
27
|
"eslint",
|
|
@@ -24,23 +31,19 @@
|
|
|
24
31
|
"gitlab-ci"
|
|
25
32
|
],
|
|
26
33
|
"dependencies": {
|
|
27
|
-
"chalk": "^4.0.0",
|
|
28
34
|
"yaml": "^2.0.0"
|
|
29
35
|
},
|
|
30
36
|
"peerDependencies": {
|
|
31
|
-
"eslint": ">=
|
|
37
|
+
"eslint": ">=9"
|
|
32
38
|
},
|
|
33
39
|
"devDependencies": {
|
|
34
|
-
"@
|
|
35
|
-
"@types/
|
|
36
|
-
"
|
|
37
|
-
"c8": "^8.0.0",
|
|
40
|
+
"@remcohaszing/eslint": "^11.0.0",
|
|
41
|
+
"@types/node": "^22.0.0",
|
|
42
|
+
"c8": "^10.0.0",
|
|
38
43
|
"codeclimate-types": "^0.3.0",
|
|
39
|
-
"eslint": "^8.0.0",
|
|
40
|
-
"eslint-config-remcohaszing": "^10.0.0",
|
|
41
44
|
"prettier": "^3.0.0",
|
|
42
|
-
"remark-cli": "^
|
|
43
|
-
"remark-preset-remcohaszing": "^
|
|
45
|
+
"remark-cli": "^12.0.0",
|
|
46
|
+
"remark-preset-remcohaszing": "^3.0.0",
|
|
44
47
|
"typescript": "^5.0.0"
|
|
45
48
|
}
|
|
46
49
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default eslintFormatterGitLab;
|
|
2
|
+
/**
|
|
3
|
+
* @param {ESLint.LintResult[]} results
|
|
4
|
+
* The ESLint report results.
|
|
5
|
+
* @param {ESLint.LintResultData} data
|
|
6
|
+
* The ESLint report result data.
|
|
7
|
+
* @returns {Promise<string>}
|
|
8
|
+
* The ESLint output to print to the console.
|
|
9
|
+
*/
|
|
10
|
+
declare function eslintFormatterGitLab(results: ESLint.LintResult[], data: ESLint.LintResultData): Promise<string>;
|
|
11
|
+
import type { ESLint } from 'eslint';
|
|
12
|
+
//# sourceMappingURL=eslint-formatter-gitlab.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eslint-formatter-gitlab.d.ts","sourceRoot":"","sources":["../lib/eslint-formatter-gitlab.js"],"names":[],"mappings":";AAiQA;;;;;;;GAOG;AACH,gDAPW,iBAAiB,EAAE,QAEnB,qBAAqB,GAEnB,OAAO,CAAC,MAAM,CAAC,CAgB3B;4BApRkC,QAAQ"}
|
package/index.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export = eslintFormatterGitLab;
|
|
2
|
-
/**
|
|
3
|
-
* @param {import('eslint').ESLint.LintResult[]} results
|
|
4
|
-
* The ESLint report results.
|
|
5
|
-
* @param {import('eslint').ESLint.LintResultData} data
|
|
6
|
-
* The ESLint report result data.
|
|
7
|
-
* @returns {string}
|
|
8
|
-
* The ESLint output to print to the console.
|
|
9
|
-
*/
|
|
10
|
-
declare function eslintFormatterGitLab(results: import('eslint').ESLint.LintResult[], data: import('eslint').ESLint.LintResultData): string;
|
|
11
|
-
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":";AA0PA;;;;;;;GAOG;AACH,gDAPW,OAAO,QAAQ,EAAE,MAAM,CAAC,UAAU,EAAE,QAEpC,OAAO,QAAQ,EAAE,MAAM,CAAC,cAAc,GAEpC,MAAM,CAmBlB"}
|