gulp-eslint-new 1.1.2 → 1.2.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.
Files changed (4) hide show
  1. package/README.md +105 -100
  2. package/index.js +59 -62
  3. package/package.json +11 -9
  4. package/util.js +196 -211
package/README.md CHANGED
@@ -12,7 +12,7 @@ npm i -D gulp-eslint-new
12
12
 
13
13
  ## Migrating
14
14
 
15
- If you are migrating from gulp-eslint, you probably won't need to change any settings in your Gulp task.
15
+ If you are migrating from [gulp-eslint][gulp-eslint], you probably won't need to change any settings in your gulp task.
16
16
  gulp-eslint-new can handle most of the options used with gulp-eslint, although some of them are now deprecated in favor of a new name or format.
17
17
 
18
18
  Anyway, since gulp-eslint-new uses ESLint 8 while gulp-eslint sticks to ESLint 6, you may need to make some changes to your project to address incompatibilities between the versions of ESLint.
@@ -24,27 +24,27 @@ You can find more information at the links below.
24
24
 
25
25
  ```javascript
26
26
  const { src } = require('gulp');
27
- const eslint = require('gulp-eslint-new');
27
+ const gulpESLintNew = require('gulp-eslint-new');
28
28
 
29
29
  // Define the default gulp task.
30
30
  exports.default =
31
31
  () => src(['scripts/*.js'])
32
- // eslint() attaches the lint output to the "eslint" property of
33
- // the file object so it can be used by other modules.
34
- .pipe(eslint())
35
- // eslint.format() outputs the lint results to the console.
36
- // Alternatively use eslint.formatEach() (see docs).
37
- .pipe(eslint.format())
38
- // To have the process exit with an error code (1) on lint error,
39
- // return the stream and pipe to failAfterError last.
40
- .pipe(eslint.failAfterError());
32
+ // gulpESLintNew() attaches the lint output to the "eslint" property of the
33
+ // file object so it can be used by other modules.
34
+ .pipe(gulpESLintNew())
35
+ // gulpESLintNew.format() outputs the lint results to the console.
36
+ // Alternatively use gulpESLintNew.formatEach() (see docs).
37
+ .pipe(gulpESLintNew.format())
38
+ // To have the process exit with an error code (1) on lint error, return the
39
+ // stream and pipe to failAfterError last.
40
+ .pipe(gulpESLintNew.failAfterError());
41
41
  ```
42
42
 
43
43
  Or use the plugin API to do things like:
44
44
 
45
45
  ```javascript
46
46
  gulp.src(['**/*.js', '!node_modules/**'])
47
- .pipe(eslint({
47
+ .pipe(gulpESLintNew({
48
48
  overrideConfig: {
49
49
  rules: {
50
50
  'my-custom-rule': 1,
@@ -60,22 +60,22 @@ gulp.src(['**/*.js', '!node_modules/**'])
60
60
  },
61
61
  warnIgnored: true
62
62
  }))
63
- .pipe(eslint.formatEach('compact', process.stderr));
63
+ .pipe(gulpESLintNew.formatEach('compact', process.stderr));
64
64
  ```
65
65
 
66
66
  For additional examples, look through the [example directory](https://github.com/fasttime/gulp-eslint-new/tree/main/example).
67
67
 
68
68
  ## API
69
69
 
70
- ### `eslint()`
70
+ ### `gulpESLintNew()`
71
71
 
72
72
  *No explicit configuration.* A `.eslintrc` file may be resolved relative to each linted file.
73
73
 
74
- ### `eslint(options)`
74
+ ### `gulpESLintNew(options)`
75
75
 
76
76
  Param type: `Object`
77
77
 
78
- Supported options include all [linting options](https://eslint.org/docs/developer-guide/nodejs-api#linting) and [autofix options](https://eslint.org/docs/developer-guide/nodejs-api#autofix) of the `ESLint` constructor.
78
+ Supported options include all [linting options][linting options] and [autofix options](https://eslint.org/docs/developer-guide/nodejs-api#autofix) of the `ESLint` constructor.
79
79
  Please, refer to the ESLint documentation for information about the usage of those options.
80
80
  Check also the notes about the [Autofix Function](#autofix-function).
81
81
  Additionally, gulp-eslint-new supports the options listed below.
@@ -96,7 +96,7 @@ The location of the files to be linted is not related to the working directory.
96
96
 
97
97
  Type: `boolean`
98
98
 
99
- When `false`, .eslintignore files or ignore patterns in your configurations will not be respected.
99
+ When `false`, ESLint will not respect `.eslintignore` files or ignore patterns in your configurations.
100
100
 
101
101
  ##### `options.ignorePath`
102
102
 
@@ -110,7 +110,7 @@ Type: `boolean`
110
110
 
111
111
  When `true`, this option will filter warning messages from ESLint results. This mimics the ESLint CLI [`--quiet` option](https://eslint.org/docs/user-guide/command-line-interface#--quiet).
112
112
 
113
- Type: `(message, index, list) => boolean`
113
+ Type: `(message: string, index: number, list: Object[]) => unknown`
114
114
 
115
115
  When a function is provided, it will be used to filter ESLint result messages, removing any messages that do not return a `true` (or truthy) value.
116
116
 
@@ -118,123 +118,107 @@ When a function is provided, it will be used to filter ESLint result messages, r
118
118
 
119
119
  Type: `boolean`
120
120
 
121
- When `true`, add a result warning when ESLint ignores a file. This can be used to find files that are needlessly being loaded by `gulp.src`. For example, since ESLint automatically ignores "node_modules" file paths and `gulp.src` does not, a gulp task may take seconds longer just reading files from the "node_modules" directory.
121
+ When `true`, add a result warning when ESLint ignores a file.
122
+ This can be used to find files that are needlessly being loaded by `gulp.src`.
123
+ For example, since ESLint automatically ignores file paths inside a `node_modules` directory but `gulp.src` does not, a gulp task may take seconds longer just reading files from `node_modules`.
122
124
 
123
125
  #### Legacy Options
124
126
 
125
- The following options are provided for backward compatibility with [gulp-eslint](https://github.com/adametry/gulp-eslint).
127
+ The following options are provided for backward compatibility with [gulp-eslint][gulp-eslint].
126
128
  Their usage is discouraged because preferable alternatives exist, that are more in line with the present ESLint conventions.
127
129
 
128
130
  ##### `options.configFile`
129
131
 
130
132
  Type: `string`
131
133
 
132
- _A legacy synonym for `options.overrideConfigFile`._
134
+ _A legacy synonym for `options.overrideConfigFile` (see [linting options][linting options])._
133
135
 
134
136
  ##### `options.envs`
135
137
 
136
138
  Type: `string[]`
137
139
 
138
- Specify a list of [environments](https://eslint.org/docs/user-guide/configuring/language-options#specifying-environments) to be applied.
140
+ Specify a list of environments to be applied.
139
141
 
140
- _Prefer using `options.overrideConfig.env` instead. Note the different option name and format._
142
+ _Prefer using [`options.overrideConfig.env`](https://eslint.org/docs/user-guide/configuring/language-options#specifying-environments) instead. Note the different option name and format._
141
143
 
142
144
  ##### `options.globals`
143
145
 
144
146
  Type: `string[]`
145
147
 
146
- Specify [global variables](https://eslint.org/docs/user-guide/configuring/language-options#specifying-globals) to declare.
148
+ Specify a list of global variables to declare.
149
+ Variables declared with this option are considered readonly.
147
150
 
148
- ```javascript
149
- {
150
- "globals": [
151
- "jQuery",
152
- "$"
153
- ]
154
- }
155
- ```
156
-
157
- _Prefer using `options.overrideConfig.globals` instead. Note the different format._
151
+ _Prefer using [`options.overrideConfig.globals`](https://eslint.org/docs/user-guide/configuring/language-options#specifying-globals) instead. Note the different format._
158
152
 
159
153
  ##### `options.parser`
160
154
 
161
155
  Type: `string`
162
156
 
163
- _Prefer using `options.overrideConfig.parser` instead._
157
+ _Prefer using [`options.overrideConfig.parser`](https://eslint.org/docs/user-guide/configuring/plugins#specifying-parser) instead._
164
158
 
165
159
  ##### `options.parserOptions`
166
160
 
167
161
  Type: `Object`
168
162
 
169
- _Prefer using `options.overrideConfig.parserOptions` instead._
163
+ _Prefer using [`options.overrideConfig.parserOptions`](https://eslint.org/docs/user-guide/configuring/language-options#specifying-parser-options) instead._
170
164
 
171
165
  ##### `options.rules`
172
166
 
173
167
  Type: `Object`
174
168
 
175
- Set [configuration](https://eslint.org/docs/user-guide/configuring/rules#configuring-rules) of [rules](https://eslint.org/docs/rules/).
176
-
177
- ```javascript
178
- {
179
- "rules": {
180
- "camelcase": 1,
181
- "comma-dangle": 2,
182
- "quotes": 0
183
- }
184
- }
185
- ```
186
-
187
- _Prefer using `options.overrideConfig.rules` instead._
169
+ _Prefer using [`options.overrideConfig.rules`](https://eslint.org/docs/user-guide/configuring/rules) instead._
188
170
 
189
171
  ##### `options.warnFileIgnored`
190
172
 
191
173
  Type: `boolean`
192
174
 
193
- _A legacy synonym for `options.warnIgnored`._
175
+ _A legacy synonym for [`options.warnIgnored`](#optionswarnignored)._
194
176
 
195
177
  #### Autofix Function
196
178
 
197
179
  When the `fix` option is specified, fixes are applied to the gulp stream.
198
- The fixed content can be saved to file using `gulp.dest` (See [example/fix.js](https://github.com/fasttime/gulp-eslint-new/blob/main/example/fix.js)).
180
+ The fixed content can be saved to file using [`gulpESLintNew.fix()`](#gulpeslintnewfix) (See [example/fix.js](https://github.com/fasttime/gulp-eslint-new/blob/main/example/fix.js)).
199
181
  Rules that are fixable can be found in ESLint's [rules list](https://eslint.org/docs/rules/).
200
182
  When fixes are applied, a "fixed" property is set to `true` on the fixed file's ESLint result.
201
183
 
202
- ### `eslint(overrideConfigFile)`
184
+ ### `gulpESLintNew(overrideConfigFile)`
203
185
 
204
186
  Param type: `string`
205
187
 
206
188
  Shorthand for defining `options.overrideConfigFile`.
207
189
 
208
- ### `eslint.result(action)`
190
+ ### `gulpESLintNew.result(action)`
209
191
 
210
- Param type: `(result) => void`
192
+ Param type: `(result: Object) => void`
211
193
 
212
194
  Call a function for each ESLint file result. No returned value is expected. If an error is thrown, it will be wrapped in a gulp `PluginError` and emitted from the stream.
213
195
 
214
196
  ```javascript
215
- gulp.src(['**/*.js','!node_modules/**'])
216
- .pipe(eslint())
217
- .pipe(eslint.result(result => {
197
+ gulp.src(['**/*.js', '!node_modules/**'])
198
+ .pipe(gulpESLintNew())
199
+ .pipe(gulpESLintNew.result(result => {
218
200
  // Called for each ESLint result.
219
201
  console.log(`ESLint result: ${result.filePath}`);
220
202
  console.log(`# Messages: ${result.messages.length}`);
221
- console.log(`# Warnings: ${result.warningCount} (${result.fixableWarningCount} fixable)`);
222
- console.log(`# Errors: ${result.errorCount} (${result.fixableErrorCount} fixable, ${
203
+ console.log(`# Warnings: ${result.warningCount} (${
204
+ result.fixableWarningCount} fixable)`);
205
+ console.log(`# Errors: ${result.errorCount} (${
206
+ result.fixableErrorCount} fixable, ${
223
207
  result.fatalErrorCount} fatal)`);
224
208
  }));
225
209
  ```
226
210
 
227
- Type: `(result, callback) => void`
211
+ Type: `(result: Object, callback: Function) => void`
228
212
 
229
213
  Call an asynchronous, Node-style callback-based function for each ESLint file result. The callback must be called for the stream to finish. If an error is passed to the callback, it will be wrapped in a gulp `PluginError` and emitted from the stream.
230
214
 
231
- Type: `result => Promise<void>`
215
+ Type: `(result: Object) => Promise<void>`
232
216
 
233
217
  Call an asynchronous, promise-based function for each ESLint file result. If the promise is rejected, the rejection reason will be wrapped in a gulp `PluginError` and emitted from the stream.
234
218
 
235
- ### `eslint.results(action)`
219
+ ### `gulpESLintNew.results(action)`
236
220
 
237
- Param type: `(results) => void`
221
+ Param type: `(results: Object[]) => void`
238
222
 
239
223
  Call a function once for all ESLint file results before a stream finishes. No returned value is expected. If an error is thrown, it will be wrapped in a gulp `PluginError` and emitted from the stream.
240
224
 
@@ -264,94 +248,115 @@ The results list has additional properties that indicate the number of messages
264
248
  </table>
265
249
 
266
250
  ```javascript
267
- gulp.src(['**/*.js','!node_modules/**'])
268
- .pipe(eslint())
269
- .pipe(eslint.results(results => {
251
+ gulp.src(['**/*.js', '!node_modules/**'])
252
+ .pipe(gulpESLintNew())
253
+ .pipe(gulpESLintNew.results(results => {
270
254
  // Called once for all ESLint results.
271
255
  console.log(`Total Results: ${results.length}`);
272
256
  console.log(`Total Warnings: ${results.warningCount} (${
273
257
  results.fixableWarningCount} fixable)`);
274
- console.log(`Total Errors: ${results.errorCount} (${results.fixableErrorCount} fixable, ${
258
+ console.log(`Total Errors: ${results.errorCount} (${
259
+ results.fixableErrorCount} fixable, ${
275
260
  results.fatalErrorCount} fatal)`);
276
261
  }));
277
262
  ```
278
263
 
279
- Param type: `(results, callback) => void`
264
+ Param type: `(results: Object[], callback: Function) => void`
280
265
 
281
266
  Call an asynchronous, Node-style callback-based function once for all ESLint file results before a stream finishes. The callback must be called for the stream to finish. If an error is passed to the callback, it will be wrapped in a gulp `PluginError` and emitted from the stream.
282
267
 
283
- Param type: `results => Promise<void>`
268
+ Param type: `(results: Object[]) => Promise<void>`
284
269
 
285
270
  Call an asynchronous, promise-based function once for all ESLint file results before a stream finishes. If the promise is rejected, the rejection reason will be wrapped in a gulp `PluginError` and emitted from the stream.
286
271
 
287
- ### `eslint.failOnError()`
272
+ ### `gulpESLintNew.failOnError()`
288
273
 
289
274
  Stop a task/stream if an ESLint error has been reported for any file.
290
275
 
291
276
  ```javascript
292
- // Cause the stream to stop (fail) before copying an invalid JS file to the output directory.
293
- gulp.src(['**/*.js','!node_modules/**'])
294
- .pipe(eslint())
295
- .pipe(eslint.failOnError());
277
+ // Cause the stream to stop (fail) without processing more files.
278
+ gulp.src(['**/*.js', '!node_modules/**'])
279
+ .pipe(gulpESLintNew())
280
+ .pipe(gulpESLintNew.failOnError());
296
281
  ```
297
282
 
298
- ### `eslint.failAfterError()`
283
+ ### `gulpESLintNew.failAfterError()`
299
284
 
300
285
  Stop a task/stream if an ESLint error has been reported for any file, but wait for all of them to be processed first.
301
286
 
302
287
  ```javascript
303
- // Cause the stream to stop(/fail) when the stream ends if any ESLint error(s) occurred.
304
- gulp.src(['**/*.js','!node_modules/**'])
305
- .pipe(eslint())
306
- .pipe(eslint.failAfterError());
288
+ // Cause the stream to stop (fail) when the stream ends if any ESLint error(s)
289
+ // occurred.
290
+ gulp.src(['**/*.js', '!node_modules/**'])
291
+ .pipe(gulpESLintNew())
292
+ .pipe(gulpESLintNew.failAfterError());
307
293
  ```
308
294
 
309
- ### `eslint.format(formatter, output)`
295
+ ### `gulpESLintNew.format(formatter, output)`
310
296
 
311
- Format all linted files once. This should be used in the stream after piping through `eslint`; otherwise, this will find no ESLint results to format.
297
+ Format all linted files once.
298
+ This should be used in the stream after piping through `gulpESLintNew`; otherwise, this will find no ESLint results to format.
312
299
 
313
- The `formatter` argument may be a `string`, `Function`, or `undefined`. As a `string`, a formatter module by that name or path will be resolved as a module, relative to the current working directory, or as one of the [built-in ESLint formatters](https://eslint.org/docs/user-guide/formatters/#eslint-formatters). If `undefined`, the ESLint "stylish" formatter will be resolved. A `Function` will be called with an `Array` of file linting results to format.
300
+ The `formatter` argument may be a `string`, `Function`, or `undefined`.
301
+ As a `string`, a formatter module by that name or path will be resolved.
302
+ The resolved formatter will be either one of the [built-in ESLint formatters](https://eslint.org/docs/user-guide/formatters/#eslint-formatters), or a formatter exported by a module with the specied path (located relative to the ESLint working directory), or a formatter exported by a package installed as a dependency (the prefix "eslint-formatter-" in the package name can be omitted).
303
+ If `undefined`, the ESLint "stylish" formatter will be resolved.
304
+ A `Function` will be called with an `Array` of file linting results to format.
314
305
 
315
306
  ```javascript
316
- // use the default "stylish" ESLint formatter
317
- eslint.format()
307
+ // Use the default "stylish" ESLint formatter.
308
+ gulpESLintNew.format()
318
309
 
319
- // use the "checkstyle" ESLint formatter
320
- eslint.format('checkstyle')
310
+ // Use the "checkstyle" ESLint formatter.
311
+ gulpESLintNew.format('checkstyle')
321
312
 
322
- // use the "eslint-path-formatter" module formatter
323
- // (@see https://github.com/Bartvds/eslint-path-formatter)
324
- eslint.format('node_modules/eslint-path-formatter')
313
+ // Use "eslint-formatter-pretty" as a formatter (must be installed with `npm`).
314
+ // See https://github.com/sindresorhus/eslint-formatter-pretty.
315
+ gulpESLintNew.format('pretty')
325
316
  ```
326
317
 
327
- The `output` argument may be a `WritableStream`, `Function`, or `undefined`. As a `WritableStream`, the formatter results will be written to the stream. If `undefined`, the formatter results will be written to [gulp's log](https://github.com/gulpjs/fancy-log#logmsg). A `Function` will be called with the formatter results as the only parameter.
318
+ The `output` argument may be a writable stream, `Function`, or `undefined`. As a writable stream, the formatter results will be written to the stream.
319
+ If `undefined`, the formatter results will be written to [gulp's log](https://github.com/gulpjs/fancy-log#logmsg).
320
+ A `Function` will be called with the formatter results as the only parameter.
328
321
 
329
322
  ```javascript
330
323
  // write to gulp's log (default)
331
- eslint.format();
324
+ gulpESLintNew.format()
332
325
 
333
326
  // write messages to stdout
334
- eslint.format('junit', process.stdout)
327
+ gulpESLintNew.format('junit', process.stdout)
335
328
  ```
336
329
 
337
- ### `eslint.formatEach(formatter, output)`
330
+ ### `gulpESLintNew.formatEach(formatter, output)`
338
331
 
339
- Format each linted file individually. This should be used in the stream after piping through `eslint`; otherwise, this will find no ESLint results to format.
332
+ Format each linted file individually.
333
+ This should be used in the stream after piping through `gulpESLintNew`; otherwise, this will find no ESLint results to format.
340
334
 
341
335
  The arguments for `formatEach` are the same as the arguments for `format`.
342
336
 
337
+ ### `gulpESLintNew.fix()`
338
+
339
+ Overwrite files with the fixed content provided by ESLint.
340
+ This should be used in conjunction with the option `fix` in [`gulpESLintNew(options)`](#gulpeslintnewoptions).
341
+ Files without a fixed content will be ignored.
342
+
343
+ ```javascript
344
+ gulp.src(['**/*.js', '!node_modules/**'])
345
+ .pipe(gulpESLintNew({ fix: true }))
346
+ .pipe(gulpESLintNew.fix());
347
+ ```
348
+
343
349
  ## Configuration
344
350
 
345
351
  ESLint may be configured explicity by using any of the supported [configuration options](https://eslint.org/docs/user-guide/configuring/). Unless the `useEslintrc` option is set to `false`, ESLint will attempt to resolve a file by the name of `.eslintrc` within the same directory as the file to be linted. If not found there, parent directories will be searched until `.eslintrc` is found or the directory root is reached.
346
352
 
347
353
  ## Custom Extensions
348
354
 
349
- ESLint results are attached as an `eslint` property to the Vinyl files that pass through a gulp stream pipeline. This is available to streams that follow the initial gulp-eslint-new stream. The [`eslint.result`](#eslintresultaction) and [`eslint.results`](#eslintresultsaction) methods are made available to support extensions and custom handling of ESLint results.
350
-
351
- ### Extension Packages
352
-
353
- * [gulp-eslint-if-fixed](https://github.com/lukeapage/gulp-eslint-if-fixed)
354
- * [gulp-eslint-threshold](https://github.com/krmbkt/gulp-eslint-threshold)
355
+ ESLint results are attached as an `eslint` property to the Vinyl files that pass through a gulp stream pipeline.
356
+ This is available to streams that follow the initial gulp-eslint-new stream.
357
+ The [`gulpESLintNew.result`](#gulpeslintnewresultaction) and [`gulpESLintNew.results`](#gulpeslintnewresultsaction) methods are made available to support extensions and custom handling of ESLint results.
355
358
 
359
+ [gulp-eslint]: https://github.com/adametry/gulp-eslint
360
+ [linting options]: https://eslint.org/docs/developer-guide/nodejs-api#linting
356
361
  [npm badge]: https://badge.fury.io/js/gulp-eslint-new.svg
357
362
  [npm URL]: https://www.npmjs.com/package/gulp-eslint-new
package/index.js CHANGED
@@ -3,17 +3,18 @@
3
3
  const {
4
4
  createIgnoreResult,
5
5
  createPluginError,
6
+ createTransform,
6
7
  filterResult,
7
- firstResultMessage,
8
+ fix,
8
9
  hasOwn,
9
10
  isErrorMessage,
10
11
  migrateOptions,
11
12
  resolveWritable,
12
- createTransform,
13
13
  writeResults
14
14
  } = require('./util');
15
15
  const { ESLint } = require('eslint');
16
16
  const { promisify } = require('util');
17
+ const { dest } = require('vinyl-fs');
17
18
 
18
19
  function getESLintInfo(file) {
19
20
  const eslintInfo = file._eslintInfo;
@@ -23,6 +24,16 @@ function getESLintInfo(file) {
23
24
  throw createPluginError({ fileName: file.path, message: 'ESLint information not available' });
24
25
  }
25
26
 
27
+ function wrapAction(action) {
28
+ if (typeof action !== 'function') {
29
+ throw TypeError('Argument is not a function');
30
+ }
31
+ if (action.length > 1) {
32
+ action = promisify(action);
33
+ }
34
+ return action;
35
+ }
36
+
26
37
  async function lintFile(eslintInfo, file, quiet, warnIgnored) {
27
38
  if (file.isNull()) {
28
39
  return;
@@ -39,8 +50,6 @@ async function lintFile(eslintInfo, file, quiet, warnIgnored) {
39
50
  // Note: ESLint doesn't adjust file paths relative to an ancestory .eslintignore path.
40
51
  // E.g., If ../.eslintignore has "foo/*.js", ESLint will ignore ./foo/*.js, instead of
41
52
  // ../foo/*.js.
42
- // ESLint rolls this into `ESLint.prototype.lintText`. So, gulp-eslint-new must account for
43
- // this limitation.
44
53
  if (!warnIgnored) {
45
54
  return;
46
55
  }
@@ -66,38 +75,28 @@ async function lintFile(eslintInfo, file, quiet, warnIgnored) {
66
75
  file._eslintInfo = eslintInfo;
67
76
  }
68
77
 
69
- function gulpEslint(options) {
78
+ module.exports = exports = options => {
70
79
  const { eslintOptions, quiet, warnIgnored } = migrateOptions(options);
71
80
  const cwd = eslintOptions.cwd || process.cwd();
72
81
  const eslint = new ESLint(eslintOptions);
73
82
  const eslintInfo = { cwd, eslint };
74
- return createTransform(
75
- file => lintFile(eslintInfo, file, quiet, warnIgnored)
76
- );
77
- }
83
+ return createTransform(file => lintFile(eslintInfo, file, quiet, warnIgnored));
84
+ };
78
85
 
79
- gulpEslint.result = action => {
80
- if (typeof action !== 'function') {
81
- throw Error('Expected callable argument');
82
- }
83
- if (action.length > 1) {
84
- action = promisify(action);
85
- }
86
- return createTransform(async file => {
87
- const { eslint } = file;
88
- if (eslint) {
89
- await action(eslint);
86
+ exports.result = action => {
87
+ action = wrapAction(action);
88
+ return createTransform(
89
+ async file => {
90
+ const { eslint } = file;
91
+ if (eslint) {
92
+ await action(eslint);
93
+ }
90
94
  }
91
- });
95
+ );
92
96
  };
93
97
 
94
- gulpEslint.results = function (action) {
95
- if (typeof action !== 'function') {
96
- throw Error('Expected callable argument');
97
- }
98
- if (action.length > 1) {
99
- action = promisify(action);
100
- }
98
+ exports.results = action => {
99
+ action = wrapAction(action);
101
100
  const results = [];
102
101
  results.errorCount = 0;
103
102
  results.warningCount = 0;
@@ -105,8 +104,7 @@ gulpEslint.results = function (action) {
105
104
  results.fixableWarningCount = 0;
106
105
  results.fatalErrorCount = 0;
107
106
  return createTransform(
108
- file => {
109
- const { eslint } = file;
107
+ ({ eslint }) => {
110
108
  if (eslint) {
111
109
  results.push(eslint);
112
110
  // Collect total error/warning count.
@@ -116,52 +114,51 @@ gulpEslint.results = function (action) {
116
114
  results.fixableWarningCount += eslint.fixableWarningCount;
117
115
  results.fatalErrorCount += eslint.fatalErrorCount;
118
116
  }
119
- }, async () => {
117
+ },
118
+ async () => {
120
119
  await action(results);
121
120
  }
122
121
  );
123
122
  };
124
123
 
125
- gulpEslint.failOnError = () => {
126
- return gulpEslint.result(result => {
127
- const error = firstResultMessage(result, isErrorMessage);
128
- if (!error) {
129
- return;
124
+ exports.failOnError = () => exports.result(result => {
125
+ const { messages } = result;
126
+ if (messages) {
127
+ const error = messages.find(isErrorMessage);
128
+ if (error) {
129
+ throw createPluginError({
130
+ name: 'ESLintError',
131
+ fileName: result.filePath,
132
+ message: error.message,
133
+ lineNumber: error.line
134
+ });
130
135
  }
131
- throw createPluginError({
132
- name: 'ESLintError',
133
- fileName: result.filePath,
134
- message: error.message,
135
- lineNumber: error.line
136
- });
137
- });
138
- };
136
+ }
137
+ });
139
138
 
140
- gulpEslint.failAfterError = () => {
141
- return gulpEslint.results(results => {
142
- const count = results.errorCount;
143
- if (!count) {
144
- return;
145
- }
139
+ exports.failAfterError = () => exports.results(({ errorCount }) => {
140
+ if (errorCount) {
146
141
  throw createPluginError({
147
142
  name: 'ESLintError',
148
- message: `Failed with ${count} ${count === 1 ? 'error' : 'errors'}`
143
+ message: `Failed with ${errorCount} ${errorCount === 1 ? 'error' : 'errors'}`
149
144
  });
150
- });
151
- };
145
+ }
146
+ });
152
147
 
153
- gulpEslint.formatEach = (formatter, writable) => {
148
+ exports.formatEach = (formatter, writable) => {
154
149
  writable = resolveWritable(writable);
155
- return createTransform(async file => {
156
- const { eslint } = file;
157
- if (eslint) {
158
- const eslintInfo = getESLintInfo(file);
159
- await writeResults([eslint], eslintInfo, formatter, writable);
150
+ return createTransform(
151
+ async file => {
152
+ const { eslint } = file;
153
+ if (eslint) {
154
+ const eslintInfo = getESLintInfo(file);
155
+ await writeResults([eslint], eslintInfo, formatter, writable);
156
+ }
160
157
  }
161
- });
158
+ );
162
159
  };
163
160
 
164
- gulpEslint.format = (formatter, writable) => {
161
+ exports.format = (formatter, writable) => {
165
162
  writable = resolveWritable(writable);
166
163
  const results = [];
167
164
  let commonInfo;
@@ -192,4 +189,4 @@ gulpEslint.format = (formatter, writable) => {
192
189
  );
193
190
  };
194
191
 
195
- module.exports = gulpEslint;
192
+ exports.fix = () => fix(dest);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gulp-eslint-new",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "A gulp plugin to lint code with ESLint 8",
5
5
  "keywords": [
6
6
  "gulpplugin",
@@ -38,24 +38,26 @@
38
38
  "test": "mocha --check-leaks test/*.spec.js"
39
39
  },
40
40
  "dependencies": {
41
- "@types/eslint": "^8.2.2",
42
- "@types/node": "^17.0.8",
43
- "eslint": "^8.6.0",
41
+ "@types/eslint": "^8.4.1",
42
+ "@types/node": "^17.0.13",
43
+ "eslint": "^8.8.0",
44
44
  "fancy-log": "^2.0.0",
45
- "plugin-error": "^1.0.1"
45
+ "plugin-error": "^1.0.1",
46
+ "ternary-stream": "^3.0.0",
47
+ "vinyl-fs": "^3.0.3"
46
48
  },
47
49
  "devDependencies": {
48
- "@typescript-eslint/eslint-plugin": "^5.9.0",
49
- "@typescript-eslint/parser": "^5.9.0",
50
+ "@typescript-eslint/eslint-plugin": "^5.10.0",
51
+ "@typescript-eslint/parser": "^5.10.0",
50
52
  "c8": "^7.11.0",
51
53
  "eslint-plugin-eslint-comments": "^3.2.0",
52
54
  "gulp": "^4.0.2",
53
- "mocha": "^9.1.3",
55
+ "mocha": "^9.1.4",
54
56
  "typescript": "^4.5.4",
55
57
  "vinyl": "^2.2.1"
56
58
  },
57
59
  "engines": {
58
- "node": ">=12.0.0"
60
+ "node": "12 >=12.20 || 14 >=14.13 || >=16"
59
61
  },
60
62
  "exports": {
61
63
  ".": "./index.js",
package/util.js CHANGED
@@ -4,6 +4,17 @@ const fancyLog = require('fancy-log');
4
4
  const { relative } = require('path');
5
5
  const PluginError = require('plugin-error');
6
6
  const { Transform } = require('stream');
7
+ const ternaryStream = require('ternary-stream');
8
+
9
+ function compareResultsByFilePath({ filePath: filePath1 }, { filePath: filePath2 }) {
10
+ if (filePath1 > filePath2) {
11
+ return 1;
12
+ }
13
+ if (filePath1 < filePath2) {
14
+ return -1;
15
+ }
16
+ return 0;
17
+ }
7
18
 
8
19
  function createPluginError(error) {
9
20
  if (error instanceof PluginError) {
@@ -14,52 +25,80 @@ function createPluginError(error) {
14
25
  }
15
26
  return new PluginError('gulp-eslint-new', error);
16
27
  }
17
- exports.createPluginError = createPluginError;
18
28
 
19
- async function awaitHandler(handler, data, done) {
20
- try {
21
- await handler();
22
- } catch (err) {
23
- done(createPluginError(err));
24
- return;
25
- }
26
- done(null, data);
29
+ const { defineProperty } = Object;
30
+
31
+ /** Determine if the specified object has the indicated property as its own property. */
32
+ const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
33
+
34
+ /**
35
+ * Determine if a message is an error.
36
+ *
37
+ * @param {Linter.LintMessage} { severity } - An ESLint message.
38
+ * @returns {boolean} Whether the message is an error message.
39
+ */
40
+ function isErrorMessage({ severity }) {
41
+ return severity > 1;
27
42
  }
28
43
 
29
44
  /**
30
- * Create a transform stream in object mode from synchronous or asynchronous handler functions.
31
- * All files are passed through the stream.
32
- * Errors thrown by the handlers will be wrapped inside a `PluginError` and emitted from the stream.
45
+ * Determine if a message is a warning.
33
46
  *
34
- * @param {Function} handleFile
35
- * A function that is called for each file, with the file object as the only parameter.
36
- * If the function returns a promise, the file will be passed through the stream after the promise
37
- * is resolved.
47
+ * @param {Linter.LintMessage} { severity } - An ESLint message.
48
+ * @returns {boolean} Whether the message is a warning message.
49
+ */
50
+ function isWarningMessage({ severity }) {
51
+ return severity === 1;
52
+ }
53
+
54
+ /**
55
+ * Resolve formatter from string.
56
+ * If a function is specified, it will be treated as an ESLint 6 style formatter function and
57
+ * wrapped into an object appropriately.
38
58
  *
39
- * @param {Function} [handleFinal]
40
- * A function that is called with no parameters before closing the stream.
41
- * If the function returns a promise, the stream will be closed after the promise is resolved.
59
+ * @param {{ cwd: string, eslint: ESLint }} eslintInfo
60
+ * Current directory and instance of ESLint used to load and configure the formatter.
42
61
  *
43
- * @returns {Stream} A transform stream.
62
+ * @param {string|Function} [formatter]
63
+ * A name or path of a formatter, or an ESLint 6 style formatter function to resolve as a formatter.
64
+ *
65
+ * @returns {Promise<ESLint.Formatter>} An ESLint formatter.
44
66
  */
45
- exports.createTransform = (handleFile, handleFinal) => {
46
- const transform = (file, enc, done) => void awaitHandler(() => handleFile(file), file, done);
47
- const final = handleFinal ? done => void awaitHandler(handleFinal, null, done) : undefined;
48
- return new Transform({ objectMode: true, transform, final });
49
- };
67
+ function resolveFormatter({ cwd, eslint }, formatter) {
68
+ if (typeof formatter === 'function') {
69
+ return {
70
+ format: results => {
71
+ results.sort(compareResultsByFilePath);
72
+ return formatter(
73
+ results,
74
+ {
75
+ cwd,
76
+ get rulesMeta() {
77
+ const rulesMeta = eslint.getRulesMetaForResults(results);
78
+ defineProperty(this, 'rulesMeta', { value: rulesMeta });
79
+ return rulesMeta;
80
+ }
81
+ }
82
+ );
83
+ }
84
+ };
85
+ }
86
+ // Use ESLint to look up formatter references.
87
+ return eslint.loadFormatter(formatter);
88
+ }
89
+
90
+ exports.compareResultsByFilePath = compareResultsByFilePath;
50
91
 
51
92
  const isHiddenRegExp = /(?<![^/\\])\.(?!\.)/u;
52
93
  const isInNodeModulesRegExp = /(?<![^/\\])node_modules[/\\]/u;
53
94
 
54
95
  /**
55
- * This is a remake of the CLI object `createIgnoreResult` function with no reference to ESLint
56
- * CLI options and with a better detection of the ignore reason in some edge cases.
57
- * Additionally, this function addresses an issue in ESLint that consists in the property
58
- * `fatalErrorCount` not being set in the result.
96
+ * This is a remake of the CLI engine `createIgnoreResult` function with no reference to ESLint CLI
97
+ * options and with a better detection of the ignore reason in some edge cases.
59
98
  *
60
99
  * @param {string} filePath - Absolute path of checked code file.
61
100
  * @param {string} baseDir - Absolute path of base directory.
62
- * @returns {LintResult} Result with warning by ignore settings.
101
+ * @returns {ESLint.LintResult} Result with warning by ignore settings.
63
102
  */
64
103
  exports.createIgnoreResult = (filePath, baseDir) => {
65
104
  let message;
@@ -88,10 +127,134 @@ exports.createIgnoreResult = (filePath, baseDir) => {
88
127
  };
89
128
  };
90
129
 
91
- /* Determine if the specified object has the indicated property as its own property. */
92
- const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
130
+ exports.createPluginError = createPluginError;
131
+
132
+ async function awaitHandler(handler, data, done) {
133
+ try {
134
+ await handler();
135
+ } catch (err) {
136
+ done(createPluginError(err));
137
+ return;
138
+ }
139
+ done(null, data);
140
+ }
141
+
142
+ /**
143
+ * Create a transform stream in object mode from synchronous or asynchronous handler functions.
144
+ * All files are passed through the stream.
145
+ * Errors thrown by the handlers will be wrapped inside a `PluginError` and emitted from the stream.
146
+ *
147
+ * @param {Function} handleFile
148
+ * A function that is called for each file, with the file object as the only parameter.
149
+ * If the function returns a promise, the file will be passed through the stream after the promise
150
+ * is resolved.
151
+ *
152
+ * @param {Function} [handleFinal]
153
+ * A function that is called with no parameters before closing the stream.
154
+ * If the function returns a promise, the stream will be closed after the promise is resolved.
155
+ *
156
+ * @returns {Stream} A transform stream.
157
+ */
158
+ exports.createTransform = (handleFile, handleFinal) => {
159
+ const transform = (file, enc, done) => void awaitHandler(() => handleFile(file), file, done);
160
+ const final = handleFinal ? done => void awaitHandler(handleFinal, null, done) : undefined;
161
+ return new Transform({ objectMode: true, transform, final });
162
+ };
163
+
164
+ /**
165
+ * Increment count if message is an error.
166
+ *
167
+ * @param {number} count - Number of errors.
168
+ * @param {Linter.LintMessage} message - An ESLint message.
169
+ * @returns {number} The number of errors, message included.
170
+ */
171
+ function countErrorMessage(count, message) {
172
+ return count + Number(isErrorMessage(message));
173
+ }
174
+
175
+ /**
176
+ * Increment count if message is a warning.
177
+ *
178
+ * @param {number} count - Number of warnings.
179
+ * @param {Linter.LintMessage} message - An ESLint message.
180
+ * @returns {number} The number of warnings, message included.
181
+ */
182
+ function countWarningMessage(count, message) {
183
+ return count + Number(isWarningMessage(message));
184
+ }
185
+
186
+ /**
187
+ * Increment count if message is a fixable error.
188
+ *
189
+ * @param {number} count - Number of fixable errors.
190
+ * @param {Linter.LintMessage} message - An ESLint message.
191
+ * @returns {number} The number of fixable errors, message included.
192
+ */
193
+ function countFixableErrorMessage(count, message) {
194
+ return count + Number(isErrorMessage(message) && message.fix !== undefined);
195
+ }
196
+
197
+ /**
198
+ * Increment count if message is a fixable warning.
199
+ *
200
+ * @param {Number} count - Number of fixable warnings.
201
+ * @param {Linter.LintMessage} message - An ESLint message.
202
+ * @returns {Number} The number of fixable warnings, message included.
203
+ */
204
+ function countFixableWarningMessage(count, message) {
205
+ return count + Number(isWarningMessage(message) && message.fix !== undefined);
206
+ }
207
+
208
+ /**
209
+ * Increment count if message is a fatal error.
210
+ *
211
+ * @param {Number} count - Number of fatal errors.
212
+ * @param {Linter.LintMessage} message - An ESLint message.
213
+ * @returns {Number} The number of fatal errors, message included.
214
+ */
215
+ function countFatalErrorMessage(count, message) {
216
+ return count + Number(isErrorMessage(message) && !!message.fatal);
217
+ }
218
+
219
+ /**
220
+ * Filter result messages, update error and warning counts.
221
+ *
222
+ * @param {ESLint.LintResult} result - An ESLint result.
223
+ * @param {Function} filter - A function that evaluates what messages to keep.
224
+ * @returns {ESLint.LintResult} A filtered ESLint result.
225
+ */
226
+ exports.filterResult = (result, filter) => {
227
+ const { messages, ...newResult } = result;
228
+ const newMessages = messages.filter(filter, result);
229
+ newResult.messages = newMessages;
230
+ newResult.errorCount = newMessages.reduce(countErrorMessage, 0);
231
+ newResult.warningCount = newMessages.reduce(countWarningMessage, 0);
232
+ newResult.fixableErrorCount = newMessages.reduce(countFixableErrorMessage, 0);
233
+ newResult.fixableWarningCount = newMessages.reduce(countFixableWarningMessage, 0);
234
+ newResult.fatalErrorCount = newMessages.reduce(countFatalErrorMessage, 0);
235
+ return newResult;
236
+ };
237
+
238
+ const isFixed = ({ eslint }) => eslint && eslint.fixed;
239
+ const getBase = ({ base }) => base;
240
+ exports.fix = dest => ternaryStream(isFixed, dest(getBase));
241
+
93
242
  exports.hasOwn = hasOwn;
94
243
 
244
+ exports.isErrorMessage = isErrorMessage;
245
+
246
+ exports.isWarningMessage = isWarningMessage;
247
+
248
+ const forbiddenOptions = [
249
+ 'cache',
250
+ 'cacheFile',
251
+ 'cacheLocation',
252
+ 'cacheStrategy',
253
+ 'errorOnUnmatchedPattern',
254
+ 'extensions',
255
+ 'globInputPaths'
256
+ ];
257
+
95
258
  /**
96
259
  * Throws an error about invalid options passed to gulp-eslint-new.
97
260
  *
@@ -131,20 +294,10 @@ function toBooleanMap(keys, defaultValue, displayName) {
131
294
  }
132
295
  }
133
296
 
134
- const forbiddenOptions = [
135
- 'cache',
136
- 'cacheFile',
137
- 'cacheLocation',
138
- 'cacheStrategy',
139
- 'errorOnUnmatchedPattern',
140
- 'extensions',
141
- 'globInputPaths'
142
- ];
143
-
144
297
  /**
145
298
  * Create config helper to merge various config sources.
146
299
  *
147
- * @param {Object} options - Options to migrate.
300
+ * @param {Object} [options] - Options to migrate.
148
301
  * @returns {Object} Migrated options.
149
302
  */
150
303
  exports.migrateOptions = (options = { }) => {
@@ -202,174 +355,6 @@ exports.migrateOptions = (options = { }) => {
202
355
  return returnValue;
203
356
  };
204
357
 
205
- /**
206
- * Get first message in an ESLint result to meet a condition.
207
- *
208
- * @param {LintResult} result
209
- * An ESLint result.
210
- * @param {Function} condition
211
- * A condition function that is passed a message and returns a boolean.
212
- * @returns {Object} The first message to pass the condition or null.
213
- */
214
- exports.firstResultMessage = (result, condition) => {
215
- if (!result.messages) {
216
- return null;
217
- }
218
- return result.messages.find(condition);
219
- };
220
-
221
- /**
222
- * Determine if a message is an error.
223
- *
224
- * @param {Object} message - An ESLint message.
225
- * @returns {boolean} Whether the message is an error message.
226
- */
227
- function isErrorMessage({ severity }) {
228
- return severity > 1;
229
- }
230
- exports.isErrorMessage = isErrorMessage;
231
-
232
- /**
233
- * Determine if a message is a warning.
234
- *
235
- * @param {Object} message - An ESLint message.
236
- * @returns {boolean} Whether the message is a warning message.
237
- */
238
- function isWarningMessage({ severity }) {
239
- return severity === 1;
240
- }
241
- exports.isWarningMessage = isWarningMessage;
242
-
243
- /**
244
- * Increment count if message is an error.
245
- *
246
- * @param {number} count - Number of errors.
247
- * @param {Object} message - An ESLint message.
248
- * @returns {number} The number of errors, message included.
249
- */
250
- function countErrorMessage(count, message) {
251
- return count + Number(isErrorMessage(message));
252
- }
253
-
254
- /**
255
- * Increment count if message is a warning.
256
- *
257
- * @param {number} count - Number of warnings.
258
- * @param {Object} message - An ESLint message.
259
- * @returns {number} The number of warnings, message included.
260
- */
261
- function countWarningMessage(count, message) {
262
- return count + Number(isWarningMessage(message));
263
- }
264
-
265
- /**
266
- * Increment count if message is a fixable error.
267
- *
268
- * @param {number} count - Number of fixable errors.
269
- * @param {Object} message - An ESLint message.
270
- * @returns {number} The number of fixable errors, message included.
271
- */
272
- function countFixableErrorMessage(count, message) {
273
- return count + Number(isErrorMessage(message) && message.fix !== undefined);
274
- }
275
-
276
- /**
277
- * Increment count if message is a fixable warning.
278
- *
279
- * @param {Number} count - Number of fixable warnings.
280
- * @param {Object} message - An ESLint message.
281
- * @returns {Number} The number of fixable warnings, message included.
282
- */
283
- function countFixableWarningMessage(count, message) {
284
- return count + Number(isWarningMessage(message) && message.fix !== undefined);
285
- }
286
-
287
- /**
288
- * Increment count if message is a fatal error.
289
- *
290
- * @param {Number} count - Number of fatal errors.
291
- * @param {Object} message - An ESLint message.
292
- * @returns {Number} The number of fatal errors, message included.
293
- */
294
- function countFatalErrorMessage(count, message) {
295
- return count + Number(isErrorMessage(message) && !!message.fatal);
296
- }
297
-
298
- /**
299
- * Filter result messages, update error and warning counts.
300
- *
301
- * @param {LintResult} result - An ESLint result.
302
- * @param {Function} filter - A function that evaluates what messages to keep.
303
- * @returns {LintResult} A filtered ESLint result.
304
- */
305
- exports.filterResult = (result, filter) => {
306
- const messages = result.messages.filter(filter, result);
307
- const newResult = {
308
- filePath: result.filePath,
309
- messages: messages,
310
- errorCount: messages.reduce(countErrorMessage, 0),
311
- warningCount: messages.reduce(countWarningMessage, 0),
312
- fixableErrorCount: messages.reduce(countFixableErrorMessage, 0),
313
- fixableWarningCount: messages.reduce(countFixableWarningMessage, 0),
314
- fatalErrorCount: messages.reduce(countFatalErrorMessage, 0)
315
- };
316
- if ('output' in result) {
317
- newResult.output = result.output;
318
- }
319
- if ('source' in result) {
320
- newResult.source = result.source;
321
- }
322
- return newResult;
323
- };
324
-
325
- function compareResultsByFilePath({ filePath: filePath1 }, { filePath: filePath2 }) {
326
- if (filePath1 > filePath2) {
327
- return 1;
328
- }
329
- if (filePath1 < filePath2) {
330
- return -1;
331
- }
332
- return 0;
333
- }
334
- exports.compareResultsByFilePath = compareResultsByFilePath;
335
-
336
- const { defineProperty } = Object;
337
-
338
- /**
339
- * Resolve formatter from string.
340
- * If a function is specified, it will be treated as an ESLint 6 style formatter function and
341
- * wrapped into an object appropriately.
342
- *
343
- * @param {{ cwd: string, eslint: ESLint }} eslintInfo
344
- * Current directory and instance of ESLint used to load and configure the formatter.
345
- *
346
- * @param {string|Function} [formatter]
347
- * A name or path of a formatter, or an ESLint 6 style formatter function to resolve as a formatter.
348
- *
349
- * @returns {Promise<ESLint.Formatter>} An ESLint formatter.
350
- */
351
- async function resolveFormatter({ cwd, eslint }, formatter) {
352
- if (typeof formatter === 'function') {
353
- return {
354
- format: results => {
355
- results.sort(compareResultsByFilePath);
356
- return formatter(
357
- results,
358
- {
359
- cwd,
360
- get rulesMeta() {
361
- const rulesMeta = eslint.getRulesMetaForResults(results);
362
- defineProperty(this, 'rulesMeta', { value: rulesMeta });
363
- return rulesMeta;
364
- }
365
- }
366
- );
367
- }
368
- };
369
- }
370
- // Use ESLint to look up formatter references.
371
- return eslint.loadFormatter(formatter);
372
- }
373
358
  exports.resolveFormatter = resolveFormatter;
374
359
 
375
360
  /**