@kitql/eslint-config 0.5.4 → 0.5.5-next.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/cmd.js +226 -12
- package/package.json +5 -5
package/cmd.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from 'node:child_process'
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
import path from 'node:path'
|
|
3
5
|
import { Option, program } from 'commander'
|
|
4
6
|
import ora from 'ora'
|
|
5
7
|
|
|
6
|
-
import { bgBlueBright, bgGreen, bgRedBright, gray, red } from '@kitql/helpers'
|
|
8
|
+
import { bgBlueBright, bgGreen, bgRedBright, gray, green, red } from '@kitql/helpers'
|
|
7
9
|
|
|
8
10
|
import { findFileOrUp } from './helper/findFileOrUp.js'
|
|
9
11
|
|
|
12
|
+
// df
|
|
10
13
|
const spinner = ora({
|
|
11
14
|
// hideCursor: true,
|
|
12
15
|
prefixText: bgBlueBright(` kitql-lint `),
|
|
@@ -19,6 +22,12 @@ program.addOption(new Option('-g, --glob <type>', 'file/dir/glob (. by default)'
|
|
|
19
22
|
program.addOption(new Option('--eslint-only', 'only run eslint', false))
|
|
20
23
|
program.addOption(new Option('--prettier-only', 'only run prettier', false))
|
|
21
24
|
program.addOption(new Option('--verbose', 'add more logs', false))
|
|
25
|
+
program.addOption(
|
|
26
|
+
new Option('-d, --diff-only', 'only check files changed against base branch', false),
|
|
27
|
+
)
|
|
28
|
+
program.addOption(
|
|
29
|
+
new Option('--base-branch <type>', 'base branch to compare against (default: main)', 'main'),
|
|
30
|
+
)
|
|
22
31
|
program.addOption(
|
|
23
32
|
new Option(
|
|
24
33
|
'-p, --prefix <type>',
|
|
@@ -34,11 +43,13 @@ const pathPrettierIgnore = findFileOrUp('.prettierignore')
|
|
|
34
43
|
const pathPrettierMjs = findFileOrUp('.prettierrc.mjs')
|
|
35
44
|
|
|
36
45
|
const format = options_cli.format ?? false
|
|
37
|
-
|
|
46
|
+
let glob = options_cli.glob ?? '.'
|
|
38
47
|
const verbose = options_cli.verbose ?? false
|
|
39
48
|
const pre = options_cli.prefix ?? 'none'
|
|
40
49
|
const eslintOnly = options_cli.eslintOnly ?? false
|
|
41
50
|
const prettierOnly = options_cli.prettierOnly ?? false
|
|
51
|
+
const diffOnly = options_cli.diffOnly ?? false
|
|
52
|
+
const baseBranch = options_cli.baseBranch ?? 'main'
|
|
42
53
|
|
|
43
54
|
let preToUse = ''
|
|
44
55
|
if (pre === 'npm') {
|
|
@@ -78,10 +89,191 @@ async function customSpawn(cmd) {
|
|
|
78
89
|
return data
|
|
79
90
|
}
|
|
80
91
|
|
|
92
|
+
let filesLength = -1
|
|
93
|
+
async function getDiffFiles() {
|
|
94
|
+
spinner.text = verbose
|
|
95
|
+
? 'git diff ' + gray(`(getting changed files against ${baseBranch})`)
|
|
96
|
+
: 'git diff'
|
|
97
|
+
|
|
98
|
+
// First, get the git repository root
|
|
99
|
+
let gitRootPath = ''
|
|
100
|
+
try {
|
|
101
|
+
const gitRootCmd = 'git rev-parse --show-toplevel'
|
|
102
|
+
const gitRootChild = spawn(gitRootCmd, {
|
|
103
|
+
shell: true,
|
|
104
|
+
cwd: process.cwd(),
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
let rootData = ''
|
|
108
|
+
for await (const chunk of gitRootChild.stdout) {
|
|
109
|
+
rootData += chunk
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const gitRootExitCode = await new Promise((resolve) => {
|
|
113
|
+
gitRootChild.on('close', resolve)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
if (gitRootExitCode === 0) {
|
|
117
|
+
gitRootPath = rootData.trim()
|
|
118
|
+
} else {
|
|
119
|
+
spinner.warn('Could not determine git repository root')
|
|
120
|
+
return null
|
|
121
|
+
}
|
|
122
|
+
} catch (error) {
|
|
123
|
+
spinner.warn(`Error getting git root: ${error.message}`)
|
|
124
|
+
return null
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Try to find the best base branch to compare against
|
|
128
|
+
const possibleBranches = [baseBranch, 'main', 'HEAD~1']
|
|
129
|
+
let validBranch = null
|
|
130
|
+
|
|
131
|
+
for (const branch of possibleBranches) {
|
|
132
|
+
try {
|
|
133
|
+
// Check if the branch exists
|
|
134
|
+
const checkBranchCmd = `git rev-parse --verify ${branch}`
|
|
135
|
+
const checkBranchChild = spawn(checkBranchCmd, {
|
|
136
|
+
shell: true,
|
|
137
|
+
cwd: process.cwd(),
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
const branchExitCode = await new Promise((resolve) => {
|
|
141
|
+
checkBranchChild.on('close', resolve)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
if (branchExitCode === 0) {
|
|
145
|
+
validBranch = branch
|
|
146
|
+
if (verbose && branch !== baseBranch) {
|
|
147
|
+
spinner.info(`Using '${branch}' as base branch instead of '${baseBranch}'`)
|
|
148
|
+
}
|
|
149
|
+
break
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
// Continue to next branch
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!validBranch) {
|
|
157
|
+
// If in CI, try to use a different approach
|
|
158
|
+
if (process.env.CI) {
|
|
159
|
+
try {
|
|
160
|
+
// In CI, we can try to get all staged and unstaged changes
|
|
161
|
+
validBranch = 'HEAD'
|
|
162
|
+
if (verbose) {
|
|
163
|
+
spinner.info('In CI environment, checking all changes')
|
|
164
|
+
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
spinner.warn(`Could not find a valid base branch to compare against`)
|
|
167
|
+
return null
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
spinner.warn(`Could not find a valid base branch to compare against`)
|
|
171
|
+
return null
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Now get the changed files
|
|
176
|
+
const cmd = `git diff --name-only --diff-filter=ACMR ${validBranch}`
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const child = spawn(cmd, {
|
|
180
|
+
shell: true,
|
|
181
|
+
cwd: process.cwd(),
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
let data = ''
|
|
185
|
+
for await (const chunk of child.stdout) {
|
|
186
|
+
data += chunk
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let error = ''
|
|
190
|
+
for await (const chunk of child.stderr) {
|
|
191
|
+
error += chunk
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const exitCode = await new Promise((resolve) => {
|
|
195
|
+
child.on('close', resolve)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
if (exitCode) {
|
|
199
|
+
// If the diff command failed, try a fallback approach for CI environments
|
|
200
|
+
if (process.env.CI) {
|
|
201
|
+
try {
|
|
202
|
+
// In CI, we can try to get all tracked files that have changes
|
|
203
|
+
const fallbackCmd = 'git ls-files --modified --others --exclude-standard'
|
|
204
|
+
const fallbackChild = spawn(fallbackCmd, {
|
|
205
|
+
shell: true,
|
|
206
|
+
cwd: process.cwd(),
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
let fallbackData = ''
|
|
210
|
+
for await (const chunk of fallbackChild.stdout) {
|
|
211
|
+
fallbackData += chunk
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const fallbackExitCode = await new Promise((resolve) => {
|
|
215
|
+
fallbackChild.on('close', resolve)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
if (fallbackExitCode === 0 && fallbackData.trim()) {
|
|
219
|
+
data = fallbackData
|
|
220
|
+
if (verbose) {
|
|
221
|
+
spinner.info('Using fallback method to get changed files in CI')
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
spinner.warn(`Could not get changed files: ${error}`)
|
|
225
|
+
return null
|
|
226
|
+
}
|
|
227
|
+
} catch (fallbackError) {
|
|
228
|
+
spinner.warn(`Could not get changed files: ${error}`)
|
|
229
|
+
return null
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
spinner.warn(`Could not get changed files: ${error}`)
|
|
233
|
+
return null
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Get the current working directory
|
|
238
|
+
const cwd = process.cwd()
|
|
239
|
+
|
|
240
|
+
// Process the files to make them relative to the current working directory
|
|
241
|
+
const files = data
|
|
242
|
+
.trim()
|
|
243
|
+
.split('\n')
|
|
244
|
+
.filter(Boolean)
|
|
245
|
+
.map((file) => {
|
|
246
|
+
// Convert the git path (relative to git root) to an absolute path
|
|
247
|
+
const absolutePath = path.join(gitRootPath, file)
|
|
248
|
+
|
|
249
|
+
// Convert the absolute path to a path relative to the current working directory
|
|
250
|
+
const relativePath = path.relative(cwd, absolutePath)
|
|
251
|
+
|
|
252
|
+
// Check if the file exists and is at or below the current directory
|
|
253
|
+
if (fs.existsSync(relativePath) && !relativePath.startsWith('..')) {
|
|
254
|
+
return relativePath
|
|
255
|
+
}
|
|
256
|
+
return null
|
|
257
|
+
})
|
|
258
|
+
.filter(Boolean) // Remove null entries (files not at or below current directory)
|
|
259
|
+
|
|
260
|
+
filesLength = files.length
|
|
261
|
+
if (verbose) {
|
|
262
|
+
spinner.info(`Found ${filesLength} changed files at or below current directory`)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Format the files for the command line, wrapping each in quotes and joining with spaces
|
|
266
|
+
return files.length > 0 ? files.map((f) => `'${f}'`).join(' ') : null
|
|
267
|
+
} catch (error) {
|
|
268
|
+
spinner.warn(`Error getting changed files: ${error.message}`)
|
|
269
|
+
return null
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
81
273
|
async function eslintRun() {
|
|
82
274
|
const cmdEsLint =
|
|
83
275
|
preToUse +
|
|
84
|
-
`eslint` +
|
|
276
|
+
`eslint --no-warn-ignored` +
|
|
85
277
|
// format or not
|
|
86
278
|
`${format ? ' --fix' : ''}` +
|
|
87
279
|
// exec
|
|
@@ -116,11 +308,31 @@ async function prettierRun() {
|
|
|
116
308
|
}
|
|
117
309
|
|
|
118
310
|
const took = []
|
|
119
|
-
|
|
120
|
-
|
|
311
|
+
|
|
312
|
+
const display = (text, time) => {
|
|
313
|
+
return `${gray(text)} ${green((time / 1000).toFixed(3))}${gray('s')}`
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// If changed-only flag is set, get the list of changed files
|
|
317
|
+
if (diffOnly) {
|
|
318
|
+
spinner.text = 'Checking for changed files'
|
|
319
|
+
const changedFilesStart = performance.now()
|
|
320
|
+
const changedFiles = await getDiffFiles()
|
|
321
|
+
const changedFilesTook = performance.now() - changedFilesStart
|
|
322
|
+
took.push(display('diff', changedFilesTook))
|
|
323
|
+
|
|
324
|
+
if (changedFiles) {
|
|
325
|
+
glob = changedFiles
|
|
326
|
+
} else {
|
|
327
|
+
glob = ''
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (!prettierOnly && glob) {
|
|
332
|
+
const esLintStart = performance.now()
|
|
121
333
|
const eslintCode = await eslintRun()
|
|
122
|
-
const esLintTook =
|
|
123
|
-
took.push(
|
|
334
|
+
const esLintTook = performance.now() - esLintStart
|
|
335
|
+
took.push(display('eslint', esLintTook))
|
|
124
336
|
if (eslintCode.status) {
|
|
125
337
|
spinner.prefixText = bgRedBright(` kitql-lint `)
|
|
126
338
|
spinner.fail(red(`eslint failed, check logs above.`))
|
|
@@ -128,11 +340,11 @@ if (!prettierOnly) {
|
|
|
128
340
|
}
|
|
129
341
|
}
|
|
130
342
|
|
|
131
|
-
if (!eslintOnly) {
|
|
132
|
-
const prettierStart =
|
|
343
|
+
if (!eslintOnly && glob) {
|
|
344
|
+
const prettierStart = performance.now()
|
|
133
345
|
const prettierCode = await prettierRun()
|
|
134
|
-
const prettierTook =
|
|
135
|
-
took.push(
|
|
346
|
+
const prettierTook = performance.now() - prettierStart
|
|
347
|
+
took.push(display('prettier', prettierTook))
|
|
136
348
|
if (prettierCode.status) {
|
|
137
349
|
spinner.prefixText = bgRedBright(` kitql-lint `)
|
|
138
350
|
spinner.fail(red(`prettier failed, check logs above.`))
|
|
@@ -141,6 +353,8 @@ if (!eslintOnly) {
|
|
|
141
353
|
}
|
|
142
354
|
|
|
143
355
|
spinner.prefixText = bgGreen(` kitql-lint `)
|
|
144
|
-
spinner.succeed(
|
|
356
|
+
spinner.succeed(
|
|
357
|
+
`All good, ${glob === '' ? 'nothing to do!' : filesLength !== -1 ? `your ${filesLength} files looks great!` : 'your files looks great!'} ${gray('(')}${took.join(gray(', '))}${gray(')')}`,
|
|
358
|
+
)
|
|
145
359
|
spinner.stop()
|
|
146
360
|
process.exit(0)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kitql/eslint-config",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5-next.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"funding": "https://github.com/sponsors/jycouet",
|
|
6
6
|
"homepage": "https://www.kitql.dev/",
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"eslint-config"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@eslint/compat": "
|
|
34
|
-
"@eslint/js": "
|
|
33
|
+
"@eslint/compat": "1.1.1",
|
|
34
|
+
"@eslint/js": "9.10.0",
|
|
35
35
|
"@theguild/prettier-config": "3.0.0",
|
|
36
36
|
"@types/eslint": "9.6.1",
|
|
37
37
|
"@typescript-eslint/parser": "8.5.0",
|
|
38
38
|
"commander": "13.1.0",
|
|
39
|
-
"eslint": "
|
|
39
|
+
"eslint": "9.10.0",
|
|
40
40
|
"eslint-plugin-pnpm-catalogs": "0.0.2",
|
|
41
41
|
"eslint-plugin-svelte": "3.0.3",
|
|
42
42
|
"eslint-plugin-unused-imports": "4.1.4",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"sideEffects": false,
|
|
55
55
|
"scripts": {
|
|
56
|
-
"format": "node ./cmd.js -f",
|
|
56
|
+
"format": "node ./cmd.js -f -d --verbose",
|
|
57
57
|
"format:example": "kitql-lint --format",
|
|
58
58
|
"lint": "node ./cmd.js --verbose -p none",
|
|
59
59
|
"lint:example": "kitql-lint",
|