linter-bundle 3.10.0 → 4.0.1

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/lint.js CHANGED
@@ -9,193 +9,50 @@ const tty = require('node:tty');
9
9
 
10
10
  const micromatch = require('micromatch');
11
11
 
12
+ const config = require('./helper/config.js');
12
13
  const { findMissingOverrides } = require('./helper/find-missing-overrides.js');
14
+ const { getGitFiles } = require('./helper/get-git-files.js');
13
15
  const { getStylelintPath } = require('./helper/get-stylelint-path.js');
14
16
  const { isNpmOrYarn } = require('./helper/is-npm-or-yarn.js');
15
17
  const { runProcess } = require('./helper/run-process.js');
16
18
  const { validatePackageOverrides } = require('./helper/validate-package-overrides.js');
17
19
 
20
+ /** @typedef {'files' | 'tsc' | 'ts' | 'sass' | 'md' | 'audit'} TaskNames */
21
+ /** @typedef {Partial<Record<string, (string | boolean)[]>>} TaskConfig */
18
22
  /** @typedef {import('./helper/run-process').ProcessResult} ProcessResult */
19
- /** @typedef {{ taskName: string; config: Partial<Record<string, (string | true)[]>>; }} TaskNameAndConfig */
23
+ /** @typedef {{ taskName: TaskNames; taskConfig: TaskConfig; }} TaskNameAndConfig */
20
24
  /** @typedef {TaskNameAndConfig & { command: string; options?: import('child_process').ExecOptions; }} TaskSetup */
21
25
  /** @typedef {{ jobTitle: string; taskSetup: TaskSetup; job: Promise<ProcessResult>; }} Job */
22
26
 
23
27
  const isTerminal = tty.isatty(1);
24
28
 
25
- void (async () => {
26
- const npmOrYarn = isNpmOrYarn();
29
+ const npmOrYarn = isNpmOrYarn();
27
30
 
28
- if (!validateEnvironment(npmOrYarn)) {
31
+ void (async () => {
32
+ if (!validateEnvironment()) {
29
33
  return;
30
34
  }
31
35
 
32
- /** @type {{ diff: Promise<ProcessResult>; modified: Promise<ProcessResult>; deleted: Promise<ProcessResult>; } | undefined} */
33
- let gitFilesProcessPromise;
34
- /** @type {string[] | undefined} */
35
- let gitFiles;
36
-
37
36
  /** @type {Job[]} */
38
- const jobs = await Promise.all(getTasksToRun(process.argv.splice(2)).map(async ({ taskName, config }) => {
39
- if (config['git']) {
40
- if (!gitFilesProcessPromise) {
41
- gitFilesProcessPromise = {
42
- // Returns changed files, also stashed and committed
43
- diff: runProcess('git diff --name-only -z @{upstream}'),
44
- // Returns unstashed files (including deleted)
45
- modified: runProcess('git ls-files -o -m --exclude-standard --full-name --deduplicate -z'),
46
- // Returns unstashed, deleted files - @todo Is there a way to also get a list of deleted stashed/committed files?
47
- deleted: runProcess('git ls-files -d --exclude-standard --full-name --deduplicate -z')
48
- };
49
- }
50
-
51
- const gitProcessResult = {
52
- diff: await gitFilesProcessPromise.diff,
53
- modified: await gitFilesProcessPromise.modified,
54
- deleted: await gitFilesProcessPromise.deleted
55
- };
56
-
57
- if (!gitFiles) {
58
- const deletedFiles = gitProcessResult.deleted.stdout.trim().split('\0');
59
-
60
- gitFiles = [
61
- ...gitProcessResult.diff.stdout.trim().split('\0'),
62
- ...gitProcessResult.modified.stdout.trim().split('\0')
63
- ].filter((file, index, self) => !deletedFiles.includes(file) && self.indexOf(file) === index);
64
- }
65
- }
66
-
37
+ const jobs = await Promise.all(getTasksToRun(process.argv.splice(2)).map(async ({ taskName, taskConfig }) => {
67
38
  switch (taskName) {
68
- case 'tsc':
69
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- This is not a valid `tsc` property, so we need to remove it.
70
- delete config['git'];
71
-
72
- return runTask({
73
- taskName,
74
- config,
75
- command: [`node "${require.resolve('typescript/bin/tsc')}" --skipLibCheck --noEmit`, ...(config['tsconfig']?.[0] ? [`--project ${config['tsconfig'][0]}`] : [])].join(' ')
76
- });
77
-
78
- case 'ts': {
79
- const tsconfig = config['tsconfig']?.[0];
80
-
81
- const includes = getIncludes(gitFiles, './**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}', config);
82
-
83
- if (!includes) {
84
- return generateDummyJobOutput(taskName, config, {
85
- stderr: 'No relevant files for ESLint changed.'
86
- });
87
- }
88
-
89
- return runTask({
90
- taskName,
91
- command: [
92
- 'node',
93
- `"${path.join(path.dirname(require.resolve('eslint')), '../bin/eslint.js')}"`,
94
- includes,
95
- config['exclude']?.map((exclude) => `--ignore-pattern ${exclude}`).join(' '),
96
- `--rulesdir "${path.resolve(__dirname, './eslint/rules/')}"`,
97
- '--format unix',
98
- `--resolve-plugins-relative-to "${__dirname}"`
99
- ].filter((argument) => Boolean(argument)).join(' '),
100
- config,
101
- options: {
102
- env: {
103
- TIMING: '10',
104
- TSCONFIG: (typeof tsconfig === 'string' ? tsconfig : undefined)
105
- }
106
- }
107
- });
108
- }
39
+ case 'files':
40
+ return runFilesTask(taskName, taskConfig);
109
41
 
110
- case 'sass': {
111
- const includes = getIncludes(gitFiles, 'src/**/*.scss', config);
112
-
113
- if (!includes) {
114
- return generateDummyJobOutput(taskName, config, {
115
- stderr: 'No relevant files for Stylelint changed.'
116
- });
117
- }
118
-
119
- const stylelintBinPath = getStylelintPath();
120
-
121
- if (stylelintBinPath === null) {
122
- return generateDummyJobOutput(taskName, config, {
123
- stderr: 'Stylelint CLI script not found.'
124
- });
125
- }
126
-
127
- return runTask({
128
- taskName,
129
- config,
130
- command: `node "${stylelintBinPath}" ${includes} --formatter unix`
131
- });
132
- }
42
+ case 'tsc':
43
+ return runTypeScriptCompilerTask(taskName, taskConfig);
133
44
 
134
- case 'md': {
135
- const includes = getIncludes(gitFiles, '**/*.md', config);
45
+ case 'ts':
46
+ return runESLintTask(taskName, taskConfig);
136
47
 
137
- if (!includes) {
138
- return generateDummyJobOutput(taskName, config, {
139
- stderr: 'No relevant files for Markdownlint changed.'
140
- });
141
- }
48
+ case 'sass':
49
+ return runStylelintTask(taskName, taskConfig);
142
50
 
143
- return runTask({
144
- taskName,
145
- config,
146
- command: `node "${require.resolve('markdownlint-cli/markdownlint.js')}" ${includes} --ignore node_modules`
147
- });
148
- }
51
+ case 'md':
52
+ return runMarkdownTask(taskName, taskConfig);
149
53
 
150
54
  case 'audit':
151
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- This is not a valid `audit` property, so we need to remove it.
152
- delete config['git'];
153
-
154
- switch (npmOrYarn) {
155
- case 'npm':
156
- return runTask({
157
- taskName,
158
- config,
159
- command: [
160
- 'npx',
161
- '--yes',
162
- '--',
163
- 'better-npm-audit@1.9.1',
164
- 'audit',
165
- `-l ${config['min-severity'] ?? 'moderate'}`,
166
- '-p',
167
- config['exclude']?.map((exclude) => `-i ${exclude}`).join(' ')
168
- ].filter((argument) => Boolean(argument)).join(' ')
169
- });
170
-
171
- case 'yarn':
172
- return runTask({
173
- taskName,
174
- config,
175
- command: [
176
- 'npx',
177
- '--yes',
178
- '--',
179
- 'improved-yarn-audit@2.3.3',
180
- `--min-severity ${config['min-severity'] ?? 'moderate'}`,
181
- '--fail-on-missing-exclusions',
182
- '--ignore-dev-deps',
183
- config['exclude']?.map((exclude) => `--exclude ${exclude}`).join(' ')
184
- ].filter((argument) => Boolean(argument)).join(' ')
185
- });
186
-
187
- case 'both':
188
- return generateDummyJobOutput(taskName, config, {
189
- code: 1,
190
- stderr: 'A "package-lock.json" and "yarn.lock" have been found. Use only one package manager within the project to avoid potential conflicts.'
191
- });
192
-
193
- default:
194
- return generateDummyJobOutput(taskName, config, {
195
- code: 1,
196
- stderr: 'Neither a "package-lock.json" nor a "yarn.lock" have been found.'
197
- });
198
- }
55
+ return runAuditTask(taskName, taskConfig);
199
56
 
200
57
  default:
201
58
  }
@@ -214,7 +71,7 @@ void (async () => {
214
71
 
215
72
  const trimmedError = stderr.trim();
216
73
 
217
- if (code !== 0 || trimmedError !== '' || taskSetup.config['verbose']) {
74
+ if (code !== 0 || trimmedError !== '' || getConfigValue(taskSetup.taskName, taskSetup.taskConfig, 'verbose')?.[0]) {
218
75
  process.stdout.write('\n');
219
76
 
220
77
  if (stdout) {
@@ -226,7 +83,7 @@ void (async () => {
226
83
  }
227
84
  }
228
85
 
229
- if (code !== 0 && taskSetup.config['verbose']) {
86
+ if (code !== 0 && getConfigValue(taskSetup.taskName, taskSetup.taskConfig, 'verbose')?.[0]) {
230
87
  if (isTerminal) {
231
88
  process.stderr.write(`\n[lint ${taskSetup.taskName}] \u001B[31mProblems detected\u001B[39m\n`);
232
89
  }
@@ -235,7 +92,7 @@ void (async () => {
235
92
  }
236
93
  }
237
94
 
238
- if (taskSetup.config['timing']) {
95
+ if (getConfigValue(taskSetup.taskName, taskSetup.taskConfig, 'timing')?.[0]) {
239
96
  process.stdout.write(`\nJob finished after ${((runtime) / 1000).toFixed(1)}s\n`);
240
97
  }
241
98
  else {
@@ -254,13 +111,250 @@ void (async () => {
254
111
  process.stdout.write('\n');
255
112
  })();
256
113
 
114
+ /**
115
+ * Runs the `files` task.
116
+ *
117
+ * @param {TaskNameAndConfig['taskName']} taskName - Name of the task
118
+ * @param {TaskNameAndConfig['taskConfig']} taskConfig - Configuration of the task
119
+ * @returns {Promise<Job>} Job
120
+ */
121
+ async function runFilesTask (taskName, taskConfig) {
122
+ const newTaskConfig = {
123
+ include: getConfigValue(taskName, taskConfig, 'include'),
124
+ git: getConfigValue(taskName, taskConfig, 'git')
125
+ };
126
+
127
+ const includes = await getIncludes(newTaskConfig, '**');
128
+
129
+ if (!includes) {
130
+ return generateDummyJobOutput(taskName, newTaskConfig, {
131
+ stderr: 'No relevant files changed.'
132
+ });
133
+ }
134
+
135
+ return runTask({
136
+ taskName,
137
+ taskConfig: newTaskConfig,
138
+ command: `node "${path.resolve(__dirname, './files/index.js')}" ${includes}`
139
+ });
140
+ }
141
+
142
+ /**
143
+ * Runs the `tsc` task.
144
+ *
145
+ * @param {TaskNames} taskName - Name of the task
146
+ * @param {TaskNameAndConfig['taskConfig']} taskConfig - Configuration of the task
147
+ * @returns {Promise<Job>} Job
148
+ */
149
+ async function runTypeScriptCompilerTask (taskName, taskConfig) {
150
+ const newTaskConfig = {
151
+ tsconfig: getConfigValue(taskName, taskConfig, 'tsconfig'),
152
+ verbose: getConfigValue(taskName, taskConfig, 'verbose')
153
+ };
154
+
155
+ return runTask({
156
+ taskName,
157
+ taskConfig: newTaskConfig,
158
+ command: [
159
+ 'node',
160
+ `"${require.resolve('typescript/bin/tsc')}"`,
161
+ '--skipLibCheck',
162
+ '--noEmit',
163
+ (newTaskConfig.tsconfig?.[0] ? `--project ${newTaskConfig.tsconfig[0]}` : undefined),
164
+ (newTaskConfig.verbose?.[0] ? '--verbose' : undefined)
165
+ ].filter((argument) => Boolean(argument)).join(' ')
166
+ });
167
+ }
168
+
169
+ /**
170
+ * Runs the `ts` task.
171
+ *
172
+ * @param {TaskNameAndConfig['taskName']} taskName - Name of the task
173
+ * @param {TaskNameAndConfig['taskConfig']} taskConfig - Configuration of the task
174
+ * @returns {Promise<Job>} Job
175
+ */
176
+ async function runESLintTask (taskName, taskConfig) {
177
+ const newTaskConfig = {
178
+ tsconfig: getConfigValue(taskName, taskConfig, 'tsconfig'),
179
+ include: getConfigValue(taskName, taskConfig, 'include'),
180
+ exclude: getConfigValue(taskName, taskConfig, 'exclude'),
181
+ git: getConfigValue(taskName, taskConfig, 'git')
182
+ };
183
+
184
+ const includes = await getIncludes(newTaskConfig, './**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}');
185
+
186
+ if (!includes) {
187
+ return generateDummyJobOutput(taskName, newTaskConfig, {
188
+ stderr: 'No relevant files for ESLint changed.'
189
+ });
190
+ }
191
+
192
+ return runTask({
193
+ taskName,
194
+ command: [
195
+ 'node',
196
+ `"${path.join(path.dirname(require.resolve('eslint')), '../bin/eslint.js')}"`,
197
+ includes,
198
+ newTaskConfig.exclude?.map((exclude) => `--ignore-pattern ${exclude}`).join(' '),
199
+ `--rulesdir "${path.resolve(__dirname, './eslint/rules/')}"`,
200
+ '--format unix',
201
+ `--resolve-plugins-relative-to "${__dirname}"`
202
+ ].filter((argument) => Boolean(argument)).join(' '),
203
+ taskConfig: newTaskConfig,
204
+ options: {
205
+ env: {
206
+ TIMING: '10',
207
+ TSCONFIG: (typeof newTaskConfig.tsconfig?.[0] === 'string' ? newTaskConfig.tsconfig[0] : undefined)
208
+ }
209
+ }
210
+ });
211
+ }
212
+
213
+ /**
214
+ * Runs the `sass` task.
215
+ *
216
+ * @param {TaskNameAndConfig['taskName']} taskName - Name of the task
217
+ * @param {TaskNameAndConfig['taskConfig']} taskConfig - Configuration of the task
218
+ * @returns {Promise<Job>} Job
219
+ */
220
+ async function runStylelintTask (taskName, taskConfig) {
221
+ const newTaskConfig = {
222
+ include: getConfigValue(taskName, taskConfig, 'include'),
223
+ git: getConfigValue(taskName, taskConfig, 'git'),
224
+ verbose: getConfigValue(taskName, taskConfig, 'verbose')
225
+ };
226
+
227
+ const includes = await getIncludes(newTaskConfig, 'src/**/*.scss');
228
+
229
+ if (!includes) {
230
+ return generateDummyJobOutput(taskName, newTaskConfig, {
231
+ stderr: 'No relevant files for Stylelint changed.'
232
+ });
233
+ }
234
+
235
+ const stylelintBinPath = getStylelintPath();
236
+
237
+ if (stylelintBinPath === null) {
238
+ return generateDummyJobOutput(taskName, newTaskConfig, {
239
+ stderr: 'Stylelint CLI script not found.'
240
+ });
241
+ }
242
+
243
+ return runTask({
244
+ taskName,
245
+ taskConfig: newTaskConfig,
246
+ command: [
247
+ 'node',
248
+ `"${stylelintBinPath}"`,
249
+ includes,
250
+ (newTaskConfig.verbose?.[0] ? '--verbose' : undefined),
251
+ '--formatter unix'
252
+
253
+ ].filter((argument) => Boolean(argument)).join(' ')
254
+ });
255
+ }
256
+
257
+ /**
258
+ * Runs the `md` task.
259
+ *
260
+ * @param {TaskNameAndConfig['taskName']} taskName - Name of the task
261
+ * @param {TaskNameAndConfig['taskConfig']} taskConfig - Configuration of the task
262
+ * @returns {Promise<Job>} Job
263
+ */
264
+ async function runMarkdownTask (taskName, taskConfig) {
265
+ const newTaskConfig = {
266
+ include: getConfigValue(taskName, taskConfig, 'include'),
267
+ git: getConfigValue(taskName, taskConfig, 'git'),
268
+ verbose: getConfigValue(taskName, taskConfig, 'verbose')
269
+ };
270
+
271
+ const includes = await getIncludes(newTaskConfig, '**/*.md');
272
+
273
+ if (!includes) {
274
+ return generateDummyJobOutput(taskName, newTaskConfig, {
275
+ stderr: 'No relevant files for Markdownlint changed.'
276
+ });
277
+ }
278
+
279
+ return runTask({
280
+ taskName,
281
+ taskConfig: newTaskConfig,
282
+ command: [
283
+ 'node',
284
+ `"${require.resolve('markdownlint-cli/markdownlint.js')}"`,
285
+ includes,
286
+ (newTaskConfig.verbose?.[0] ? '--verbose' : undefined),
287
+ '--ignore node_modules'
288
+ ].filter((argument) => Boolean(argument)).join(' ')
289
+ });
290
+ }
291
+
292
+ /**
293
+ * Runs the `audit` task.
294
+ *
295
+ * @param {TaskNameAndConfig['taskName']} taskName - Name of the task
296
+ * @param {TaskNameAndConfig['taskConfig']} taskConfig - Configuration of the task
297
+ * @returns {Promise<Job>} Job
298
+ */
299
+ async function runAuditTask (taskName, taskConfig) {
300
+ const newTaskConfig = {
301
+ minSeverity: getConfigValue(taskName, taskConfig, 'minSeverity'),
302
+ exclude: getConfigValue(taskName, taskConfig, 'exclude')
303
+ };
304
+
305
+ switch (npmOrYarn) {
306
+ case 'npm':
307
+ return runTask({
308
+ taskName,
309
+ taskConfig: newTaskConfig,
310
+ command: [
311
+ 'npx',
312
+ '--yes',
313
+ '--',
314
+ 'better-npm-audit@1.9.1',
315
+ 'audit',
316
+ `-l ${newTaskConfig.minSeverity?.[0] ?? 'moderate'}`,
317
+ '-p',
318
+ newTaskConfig.exclude?.map((exclude) => `-i ${exclude}`).join(' ')
319
+ ].filter((argument) => Boolean(argument)).join(' ')
320
+ });
321
+
322
+ case 'yarn':
323
+ return runTask({
324
+ taskName,
325
+ taskConfig: newTaskConfig,
326
+ command: [
327
+ 'npx',
328
+ '--yes',
329
+ '--',
330
+ 'improved-yarn-audit@2.3.3',
331
+ `--min-severity ${newTaskConfig.minSeverity?.[0] ?? 'moderate'}`,
332
+ '--fail-on-missing-exclusions',
333
+ '--ignore-dev-deps',
334
+ newTaskConfig.exclude?.map((exclude) => `--exclude ${exclude}`).join(' ')
335
+ ].filter((argument) => Boolean(argument)).join(' ')
336
+ });
337
+
338
+ case 'both':
339
+ return generateDummyJobOutput(taskName, newTaskConfig, {
340
+ code: 1,
341
+ stderr: 'A "package-lock.json" and "yarn.lock" have been found. Use only one package manager within the project to avoid potential conflicts.'
342
+ });
343
+
344
+ default:
345
+ return generateDummyJobOutput(taskName, newTaskConfig, {
346
+ code: 1,
347
+ stderr: 'Neither a "package-lock.json" nor a "yarn.lock" have been found.'
348
+ });
349
+ }
350
+ }
351
+
257
352
  /**
258
353
  * Ensures that the environment in which the linter is running has the correct versions of the required dependencies.
259
354
  *
260
- * @param {ReturnType<isNpmOrYarn>} npmOrYarn - This should be the return value of `isNpmOrYarn()`.
261
355
  * @returns {boolean} Returns `true` if the environment is valid, otherwise `false` is returned.
262
356
  */
263
- function validateEnvironment (npmOrYarn) {
357
+ function validateEnvironment () {
264
358
  const outdatedOverrides = validatePackageOverrides();
265
359
 
266
360
  if (outdatedOverrides.overrides.length > 0 || outdatedOverrides.resolutions.length > 0) {
@@ -314,7 +408,7 @@ function validateEnvironment (npmOrYarn) {
314
408
  * @throws {Error} If no task has be specified in the arguments.
315
409
  */
316
410
  function getTasksToRun (argv) {
317
- const TASKS = new Set(['tsc', 'ts', 'sass', 'md', 'audit']);
411
+ const TASKS = new Set(['tsc', 'ts', 'sass', 'md', 'audit', 'files']);
318
412
  const ARG_REGEXP = /^--([^=]+)(?:=(.+))?$/u;
319
413
 
320
414
  /** @type {TaskNameAndConfig | null} */
@@ -323,14 +417,14 @@ function getTasksToRun (argv) {
323
417
  /** @type {TaskNameAndConfig[]} */
324
418
  const tasksToRun = [];
325
419
 
326
- /** @type {Record<string, (string | true)[]>} */
420
+ /** @type {Record<string, (string | boolean)[]>} */
327
421
  const generalConfig = {};
328
422
 
329
423
  for (const argument of argv) {
330
424
  if (TASKS.has(argument)) {
331
425
  currentTask = {
332
- taskName: argument,
333
- config: { ...generalConfig }
426
+ taskName: /** @type {TaskNames} */(argument),
427
+ taskConfig: { ...generalConfig }
334
428
  };
335
429
 
336
430
  tasksToRun.push(currentTask);
@@ -344,27 +438,23 @@ function getTasksToRun (argv) {
344
438
  throw new Error(`Unknown argument "${argument}"`);
345
439
  }
346
440
 
347
- const normalizedName = name.toLowerCase();
348
-
349
- /** @type {(string | true)[]} */
350
- let config;
441
+ // Converts e.g. "MIN-SEVERITY" into "minSeverity"
442
+ const normalizedName = name.toLowerCase().replace(/-./gu, (match) => match[1].toUpperCase());
351
443
 
352
444
  if (currentTask === null) {
353
445
  if (!(normalizedName in generalConfig)) {
354
446
  generalConfig[normalizedName] = [];
355
447
  }
356
448
 
357
- config = generalConfig[normalizedName];
449
+ generalConfig[normalizedName].push(value);
358
450
  }
359
451
  else {
360
- if (!(normalizedName in currentTask.config)) {
361
- currentTask.config[normalizedName] = [];
452
+ if (!(normalizedName in currentTask.taskConfig)) {
453
+ currentTask.taskConfig[normalizedName] = [];
362
454
  }
363
455
 
364
- config = /** @type {(string | true)[]} */(currentTask.config[normalizedName]);
456
+ /** @type {(string | boolean)[]} */(currentTask.taskConfig[normalizedName]).push(value);
365
457
  }
366
-
367
- config.push(value);
368
458
  }
369
459
 
370
460
  return tasksToRun;
@@ -373,18 +463,19 @@ function getTasksToRun (argv) {
373
463
  /**
374
464
  * Returns a list of changed files, based on the Git-diff result and the glob pattern to be used in the command-line.
375
465
  *
376
- * @param {string[] | undefined} list - File list
466
+ * @param {TaskConfig} taskConfig - Linter configuration
377
467
  * @param {string} pattern - Glob pattern
378
- * @param {Partial<Record<string, (string | true)[]>>} config - Linter configuration
379
- * @returns {string} Space-separated file names in double-quotes to be used in the command-line, or an empty string if no file matches.
468
+ * @returns {Promise<string>} Space-separated file names in double-quotes to be used in the command-line, or an empty string if no file matches.
380
469
  */
381
- function getIncludes (list, pattern, config) {
382
- const include = config['include']?.[0];
470
+ async function getIncludes (taskConfig, pattern) {
471
+ const include = taskConfig['include'];
472
+
473
+ let includedFiles = (Array.isArray(include) && include.length > 0 ? /** @type {string[]} */(include.filter((item) => typeof item === 'string')) : [pattern]);
383
474
 
384
- let includedFiles = [typeof include === 'string' ? include : pattern];
475
+ if (taskConfig['git']?.[0]) {
476
+ const gitFiles = await getGitFiles();
385
477
 
386
- if (config['git'] && list) {
387
- includedFiles = micromatch(list, includedFiles);
478
+ includedFiles = micromatch(gitFiles, includedFiles);
388
479
 
389
480
  if (includedFiles.length === 0) {
390
481
  return '';
@@ -411,21 +502,21 @@ function runTask (setup) {
411
502
  /**
412
503
  * Returns a job configuration which does not run any task, but just returns the given `output`.
413
504
  *
414
- * @param {string} taskName - The name of the task.
415
- * @param {Partial<Record<string, (string | true)[]>>} config - The configuration of the task.
505
+ * @param {TaskNames} taskName - The name of the task.
506
+ * @param {TaskConfig} taskConfig - The configuration of the task.
416
507
  * @param {{ code?: number; stdout?: string; stderr?: string; }} output - The output which should be returned as result of the job.
417
508
  * @returns {Job} Job
418
509
  */
419
- function generateDummyJobOutput (taskName, config, output) {
510
+ function generateDummyJobOutput (taskName, taskConfig, output) {
420
511
  return {
421
512
  jobTitle: getJobTitle({
422
513
  taskName,
423
- config,
514
+ taskConfig,
424
515
  command: ''
425
516
  }),
426
517
  taskSetup: {
427
518
  taskName,
428
- config,
519
+ taskConfig,
429
520
  command: ''
430
521
  },
431
522
  job: Promise.resolve({
@@ -446,7 +537,50 @@ function generateDummyJobOutput (taskName, config, output) {
446
537
  */
447
538
  function getJobTitle (setup) {
448
539
  /** @type {string} */
449
- const additionalArgumentString = Object.entries(setup.config).map(([name, values]) => (Array.isArray(values) ? values.map((value) => (value === true ? `--${name}` : `--${name}="${value}"`)).join(' ') : '')).join(' ');
540
+ const additionalArgumentString = Object.entries(setup.taskConfig).filter(([, values]) => values !== undefined).map(([name, values]) => (Array.isArray(values) ? values.map((value) => (value === true ? `--${name}` : `--${name}="${value}"`)).join(' ') : '')).join(' ');
450
541
 
451
542
  return `\n[lint ${setup.taskName}${(additionalArgumentString.length > 0 ? ` ${additionalArgumentString}` : '')}] ${setup.command}\n`;
452
543
  }
544
+
545
+ /**
546
+ * Returns a configuration option value based on the command line arguments and the `.linter-bundle.js` configuration.
547
+ *
548
+ * @param {TaskNames} taskName - Name of the task
549
+ * @param {TaskConfig} taskConfig - Configuration of a task
550
+ * @param {string} optionName - Configuration option name
551
+ * @returns {(string | boolean)[] | undefined} Configuration option value
552
+ */
553
+ function getConfigValue (taskName, taskConfig, optionName) {
554
+ if (optionName in taskConfig) {
555
+ return taskConfig[optionName];
556
+ }
557
+
558
+ if (taskName in config) {
559
+ const specificConfig = config[/** @type {keyof typeof config} */(taskName)];
560
+
561
+ if (typeof specificConfig === 'object' && optionName in specificConfig) {
562
+ // eslint-disable-next-line jsdoc/no-undefined-types -- False positive "The type 'specificConfig' is undefined."
563
+ const configValue = specificConfig[/** @type {keyof typeof specificConfig} */(optionName)];
564
+
565
+ if (Array.isArray(configValue)) {
566
+ return configValue;
567
+ }
568
+ else if (typeof configValue === 'boolean' || typeof configValue === 'string') {
569
+ return [configValue];
570
+ }
571
+ }
572
+ }
573
+
574
+ if (optionName in config) {
575
+ const configValue = config[/** @type {keyof typeof config} */(optionName)];
576
+
577
+ if (Array.isArray(configValue)) {
578
+ return configValue;
579
+ }
580
+ else if (typeof configValue === 'boolean' || typeof configValue === 'string') {
581
+ return [configValue];
582
+ }
583
+ }
584
+
585
+ return;
586
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linter-bundle",
3
- "version": "3.10.0",
3
+ "version": "4.0.1",
4
4
  "description": "Ready-to use bundle of linting tools, containing configurations for ESLint, stylelint and markdownlint.",
5
5
  "keywords": [
6
6
  "eslint",
@@ -32,7 +32,7 @@
32
32
  "publish:major": "npm version major",
33
33
  "publish:minor": "npm version minor",
34
34
  "publish:patch": "npm version patch",
35
- "lint": "npm run _validate-stylelint-options && npm run _stylelint-find-rules && node ./lint tsc ts md audit --min-severity=critical",
35
+ "lint": "npm run _validate-stylelint-options && npm run _stylelint-find-rules && node ./lint files tsc ts md audit --min-severity=critical",
36
36
  "preversion": "npm run check-outdated && npm run lint",
37
37
  "postversion": "git push && git push --tags && npm publish",
38
38
  "check-outdated": "npx --yes -- check-outdated --ignore-pre-releases",
@@ -50,7 +50,7 @@
50
50
  "eslint-plugin-functional": "6.0.0",
51
51
  "eslint-plugin-import": "2.28.1",
52
52
  "eslint-plugin-jest": "27.2.3",
53
- "eslint-plugin-jsdoc": "46.5.0",
53
+ "eslint-plugin-jsdoc": "46.5.1",
54
54
  "eslint-plugin-jsx-a11y": "6.7.1",
55
55
  "eslint-plugin-n": "16.0.2",
56
56
  "eslint-plugin-promise": "6.1.1",