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/.linter-bundle.js +51 -0
- package/CHANGELOG.md +26 -1
- package/README.md +372 -93
- package/TODO.md +10 -19
- package/eslint/index.js +6 -5
- package/eslint/overrides-gatsby.js +2 -2
- package/eslint/overrides-javascript.js +1 -1
- package/eslint/overrides-react.js +3 -2
- package/eslint/overrides-type-declarations.js +1 -1
- package/eslint/rules/no-global-undefined-check.md +1 -1
- package/eslint/rules/restricted-filenames.js +6 -2
- package/eslint/rules/restricted-filenames.md +20 -16
- package/files/index.js +100 -0
- package/helper/config.js +48 -0
- package/helper/get-git-files.js +52 -0
- package/helper/run-process.js +2 -1
- package/lint.js +330 -196
- package/package.json +3 -3
- package/stylelint/index.js +4 -2
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:
|
|
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
|
-
|
|
26
|
-
const npmOrYarn = isNpmOrYarn();
|
|
29
|
+
const npmOrYarn = isNpmOrYarn();
|
|
27
30
|
|
|
28
|
-
|
|
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,
|
|
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 '
|
|
69
|
-
|
|
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 '
|
|
111
|
-
|
|
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 '
|
|
135
|
-
|
|
45
|
+
case 'ts':
|
|
46
|
+
return runESLintTask(taskName, taskConfig);
|
|
136
47
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
stderr: 'No relevant files for Markdownlint changed.'
|
|
140
|
-
});
|
|
141
|
-
}
|
|
48
|
+
case 'sass':
|
|
49
|
+
return runStylelintTask(taskName, taskConfig);
|
|
142
50
|
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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 (
|
|
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 |
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
449
|
+
generalConfig[normalizedName].push(value);
|
|
358
450
|
}
|
|
359
451
|
else {
|
|
360
|
-
if (!(normalizedName in currentTask.
|
|
361
|
-
currentTask.
|
|
452
|
+
if (!(normalizedName in currentTask.taskConfig)) {
|
|
453
|
+
currentTask.taskConfig[normalizedName] = [];
|
|
362
454
|
}
|
|
363
455
|
|
|
364
|
-
|
|
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 {
|
|
466
|
+
* @param {TaskConfig} taskConfig - Linter configuration
|
|
377
467
|
* @param {string} pattern - Glob pattern
|
|
378
|
-
* @
|
|
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 (
|
|
382
|
-
const include =
|
|
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
|
-
|
|
475
|
+
if (taskConfig['git']?.[0]) {
|
|
476
|
+
const gitFiles = await getGitFiles();
|
|
385
477
|
|
|
386
|
-
|
|
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 {
|
|
415
|
-
* @param {
|
|
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,
|
|
510
|
+
function generateDummyJobOutput (taskName, taskConfig, output) {
|
|
420
511
|
return {
|
|
421
512
|
jobTitle: getJobTitle({
|
|
422
513
|
taskName,
|
|
423
|
-
|
|
514
|
+
taskConfig,
|
|
424
515
|
command: ''
|
|
425
516
|
}),
|
|
426
517
|
taskSetup: {
|
|
427
518
|
taskName,
|
|
428
|
-
|
|
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.
|
|
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
|
+
"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.
|
|
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",
|