immosquare-cleaner 0.1.38 → 0.1.39

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/lib/immosquare-cleaner/version.rb +1 -1
  3. data/node_modules/@eslint/config-array/LICENSE +201 -0
  4. data/node_modules/@eslint/config-array/README.md +340 -0
  5. data/node_modules/@eslint/config-array/dist/cjs/index.cjs +1260 -0
  6. data/node_modules/@eslint/config-array/dist/cjs/index.d.cts +146 -0
  7. data/node_modules/@eslint/config-array/dist/cjs/types.ts +24 -0
  8. data/node_modules/@eslint/config-array/dist/esm/index.d.ts +146 -0
  9. data/node_modules/@eslint/config-array/dist/esm/index.js +1258 -0
  10. data/node_modules/@eslint/config-array/dist/esm/types.d.ts +19 -0
  11. data/node_modules/@eslint/config-array/dist/esm/types.ts +24 -0
  12. data/node_modules/@eslint/config-array/node_modules/minimatch/LICENSE +15 -0
  13. data/node_modules/@eslint/config-array/node_modules/minimatch/README.md +230 -0
  14. data/node_modules/@eslint/config-array/node_modules/minimatch/minimatch.js +947 -0
  15. data/node_modules/@eslint/config-array/node_modules/minimatch/node_modules/brace-expansion/LICENSE +21 -0
  16. data/node_modules/@eslint/config-array/node_modules/minimatch/node_modules/brace-expansion/README.md +129 -0
  17. data/node_modules/@eslint/config-array/node_modules/minimatch/node_modules/brace-expansion/index.js +201 -0
  18. data/node_modules/@eslint/config-array/node_modules/minimatch/node_modules/brace-expansion/package.json +47 -0
  19. data/node_modules/@eslint/config-array/node_modules/minimatch/package.json +33 -0
  20. data/node_modules/@eslint/config-array/package.json +60 -0
  21. data/node_modules/@eslint/js/package.json +1 -1
  22. data/node_modules/@eslint/object-schema/LICENSE +201 -0
  23. data/node_modules/@eslint/object-schema/README.md +224 -0
  24. data/node_modules/@eslint/object-schema/dist/cjs/index.cjs +455 -0
  25. data/node_modules/@eslint/object-schema/dist/cjs/index.d.cts +123 -0
  26. data/node_modules/@eslint/object-schema/dist/cjs/types.ts +55 -0
  27. data/node_modules/@eslint/object-schema/dist/esm/index.d.ts +123 -0
  28. data/node_modules/@eslint/object-schema/dist/esm/index.js +452 -0
  29. data/node_modules/@eslint/object-schema/dist/esm/types.d.ts +40 -0
  30. data/node_modules/@eslint/object-schema/dist/esm/types.ts +55 -0
  31. data/node_modules/@eslint/object-schema/package.json +56 -0
  32. data/node_modules/eslint/lib/cli.js +2 -2
  33. data/node_modules/eslint/lib/config/flat-config-array.js +1 -1
  34. data/node_modules/eslint/lib/eslint/eslint-helpers.js +7 -7
  35. data/node_modules/eslint/lib/eslint/eslint.js +1 -10
  36. data/node_modules/eslint/lib/rule-tester/rule-tester.js +1 -1
  37. data/node_modules/eslint/lib/rules/func-style.js +4 -4
  38. data/node_modules/eslint/lib/rules/no-constructor-return.js +1 -1
  39. data/node_modules/eslint/lib/rules/no-loop-func.js +161 -129
  40. data/node_modules/eslint/package.json +3 -3
  41. data/node_modules/prettier/LICENSE +56 -535
  42. data/node_modules/prettier/bin/prettier.cjs +4 -8
  43. data/node_modules/prettier/doc.js +20 -29
  44. data/node_modules/prettier/doc.mjs +20 -29
  45. data/node_modules/prettier/index.cjs +1 -1
  46. data/node_modules/prettier/index.d.ts +4 -2
  47. data/node_modules/prettier/index.mjs +3109 -4509
  48. data/node_modules/prettier/internal/cli.mjs +339 -3544
  49. data/node_modules/prettier/package.json +1 -1
  50. data/node_modules/prettier/plugins/acorn.js +11 -11
  51. data/node_modules/prettier/plugins/acorn.mjs +11 -11
  52. data/node_modules/prettier/plugins/angular.js +1 -1
  53. data/node_modules/prettier/plugins/angular.mjs +1 -1
  54. data/node_modules/prettier/plugins/babel.js +11 -11
  55. data/node_modules/prettier/plugins/babel.mjs +11 -11
  56. data/node_modules/prettier/plugins/estree.js +28 -28
  57. data/node_modules/prettier/plugins/estree.mjs +28 -28
  58. data/node_modules/prettier/plugins/flow.js +16 -17
  59. data/node_modules/prettier/plugins/flow.mjs +16 -17
  60. data/node_modules/prettier/plugins/glimmer.js +22 -22
  61. data/node_modules/prettier/plugins/glimmer.mjs +22 -22
  62. data/node_modules/prettier/plugins/graphql.js +4 -4
  63. data/node_modules/prettier/plugins/graphql.mjs +4 -4
  64. data/node_modules/prettier/plugins/html.js +15 -15
  65. data/node_modules/prettier/plugins/html.mjs +15 -15
  66. data/node_modules/prettier/plugins/markdown.js +42 -42
  67. data/node_modules/prettier/plugins/markdown.mjs +42 -42
  68. data/node_modules/prettier/plugins/meriyah.js +5 -5
  69. data/node_modules/prettier/plugins/meriyah.mjs +5 -5
  70. data/node_modules/prettier/plugins/postcss.js +26 -26
  71. data/node_modules/prettier/plugins/postcss.mjs +26 -26
  72. data/node_modules/prettier/plugins/typescript.js +20 -20
  73. data/node_modules/prettier/plugins/typescript.mjs +20 -20
  74. data/node_modules/prettier/plugins/yaml.js +32 -32
  75. data/node_modules/prettier/plugins/yaml.mjs +32 -32
  76. data/node_modules/prettier/standalone.js +26 -26
  77. data/node_modules/prettier/standalone.mjs +26 -26
  78. data/package.json +3 -3
  79. metadata +30 -2
@@ -0,0 +1,1260 @@
1
+ 'use strict';
2
+
3
+ var path = require('node:path');
4
+ var minimatch = require('minimatch');
5
+ var createDebug = require('debug');
6
+ var objectSchema = require('@eslint/object-schema');
7
+
8
+ /**
9
+ * @fileoverview ConfigSchema
10
+ * @author Nicholas C. Zakas
11
+ */
12
+
13
+ //------------------------------------------------------------------------------
14
+ // Types
15
+ //------------------------------------------------------------------------------
16
+
17
+ /** @typedef {import("@eslint/object-schema").PropertyDefinition} PropertyDefinition */
18
+ /** @typedef {import("@eslint/object-schema").ObjectDefinition} ObjectDefinition */
19
+
20
+ //------------------------------------------------------------------------------
21
+ // Helpers
22
+ //------------------------------------------------------------------------------
23
+
24
+ /**
25
+ * A strategy that does nothing.
26
+ * @type {PropertyDefinition}
27
+ */
28
+ const NOOP_STRATEGY = {
29
+ required: false,
30
+ merge() {
31
+ return undefined;
32
+ },
33
+ validate() {},
34
+ };
35
+
36
+ //------------------------------------------------------------------------------
37
+ // Exports
38
+ //------------------------------------------------------------------------------
39
+
40
+ /**
41
+ * The base schema that every ConfigArray uses.
42
+ * @type {ObjectDefinition}
43
+ */
44
+ const baseSchema = Object.freeze({
45
+ name: {
46
+ required: false,
47
+ merge() {
48
+ return undefined;
49
+ },
50
+ validate(value) {
51
+ if (typeof value !== "string") {
52
+ throw new TypeError("Property must be a string.");
53
+ }
54
+ },
55
+ },
56
+ files: NOOP_STRATEGY,
57
+ ignores: NOOP_STRATEGY,
58
+ });
59
+
60
+ /**
61
+ * @fileoverview ConfigSchema
62
+ * @author Nicholas C. Zakas
63
+ */
64
+
65
+ //------------------------------------------------------------------------------
66
+ // Types
67
+ //------------------------------------------------------------------------------
68
+
69
+
70
+ //------------------------------------------------------------------------------
71
+ // Helpers
72
+ //------------------------------------------------------------------------------
73
+
74
+ /**
75
+ * Asserts that a given value is an array.
76
+ * @param {*} value The value to check.
77
+ * @returns {void}
78
+ * @throws {TypeError} When the value is not an array.
79
+ */
80
+ function assertIsArray(value) {
81
+ if (!Array.isArray(value)) {
82
+ throw new TypeError("Expected value to be an array.");
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Asserts that a given value is an array containing only strings and functions.
88
+ * @param {*} value The value to check.
89
+ * @returns {void}
90
+ * @throws {TypeError} When the value is not an array of strings and functions.
91
+ */
92
+ function assertIsArrayOfStringsAndFunctions(value) {
93
+ assertIsArray(value);
94
+
95
+ if (
96
+ value.some(
97
+ item => typeof item !== "string" && typeof item !== "function",
98
+ )
99
+ ) {
100
+ throw new TypeError(
101
+ "Expected array to only contain strings and functions.",
102
+ );
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Asserts that a given value is a non-empty array.
108
+ * @param {*} value The value to check.
109
+ * @returns {void}
110
+ * @throws {TypeError} When the value is not an array or an empty array.
111
+ */
112
+ function assertIsNonEmptyArray(value) {
113
+ if (!Array.isArray(value) || value.length === 0) {
114
+ throw new TypeError("Expected value to be a non-empty array.");
115
+ }
116
+ }
117
+
118
+ //------------------------------------------------------------------------------
119
+ // Exports
120
+ //------------------------------------------------------------------------------
121
+
122
+ /**
123
+ * The schema for `files` and `ignores` that every ConfigArray uses.
124
+ * @type {ObjectDefinition}
125
+ */
126
+ const filesAndIgnoresSchema = Object.freeze({
127
+ files: {
128
+ required: false,
129
+ merge() {
130
+ return undefined;
131
+ },
132
+ validate(value) {
133
+ // first check if it's an array
134
+ assertIsNonEmptyArray(value);
135
+
136
+ // then check each member
137
+ value.forEach(item => {
138
+ if (Array.isArray(item)) {
139
+ assertIsArrayOfStringsAndFunctions(item);
140
+ } else if (
141
+ typeof item !== "string" &&
142
+ typeof item !== "function"
143
+ ) {
144
+ throw new TypeError(
145
+ "Items must be a string, a function, or an array of strings and functions.",
146
+ );
147
+ }
148
+ });
149
+ },
150
+ },
151
+ ignores: {
152
+ required: false,
153
+ merge() {
154
+ return undefined;
155
+ },
156
+ validate: assertIsArrayOfStringsAndFunctions,
157
+ },
158
+ });
159
+
160
+ /**
161
+ * @fileoverview ConfigArray
162
+ * @author Nicholas C. Zakas
163
+ */
164
+
165
+
166
+ //------------------------------------------------------------------------------
167
+ // Types
168
+ //------------------------------------------------------------------------------
169
+
170
+ /** @typedef {import("./types.ts").ConfigObject} ConfigObject */
171
+ /** @typedef {import("minimatch").IMinimatchStatic} IMinimatchStatic */
172
+ /** @typedef {import("minimatch").IMinimatch} IMinimatch */
173
+
174
+ /*
175
+ * This is a bit of a hack to make TypeScript happy with the Rollup-created
176
+ * CommonJS file. Rollup doesn't do object destructuring for imported files
177
+ * and instead imports the default via `require()`. This messes up type checking
178
+ * for `ObjectSchema`. To work around that, we just import the type manually
179
+ * and give it a different name to use in the JSDoc comments.
180
+ */
181
+ /** @typedef {import("@eslint/object-schema").ObjectSchema} ObjectSchemaInstance */
182
+
183
+ //------------------------------------------------------------------------------
184
+ // Helpers
185
+ //------------------------------------------------------------------------------
186
+
187
+ const Minimatch = minimatch.Minimatch;
188
+ const debug = createDebug("@eslint/config-array");
189
+
190
+ /**
191
+ * A cache for minimatch instances.
192
+ * @type {Map<string, IMinimatch>}
193
+ */
194
+ const minimatchCache = new Map();
195
+
196
+ /**
197
+ * A cache for negated minimatch instances.
198
+ * @type {Map<string, IMinimatch>}
199
+ */
200
+ const negatedMinimatchCache = new Map();
201
+
202
+ /**
203
+ * Options to use with minimatch.
204
+ * @type {Object}
205
+ */
206
+ const MINIMATCH_OPTIONS = {
207
+ // matchBase: true,
208
+ dot: true,
209
+ };
210
+
211
+ /**
212
+ * The types of config objects that are supported.
213
+ * @type {Set<string>}
214
+ */
215
+ const CONFIG_TYPES = new Set(["array", "function"]);
216
+
217
+ /**
218
+ * Fields that are considered metadata and not part of the config object.
219
+ * @type {Set<string>}
220
+ */
221
+ const META_FIELDS = new Set(["name"]);
222
+
223
+ /**
224
+ * A schema containing just files and ignores for early validation.
225
+ * @type {ObjectSchemaInstance}
226
+ */
227
+ const FILES_AND_IGNORES_SCHEMA = new objectSchema.ObjectSchema(filesAndIgnoresSchema);
228
+
229
+ // Precomputed constant objects returned by `ConfigArray.getConfigWithStatus`.
230
+
231
+ const CONFIG_WITH_STATUS_EXTERNAL = Object.freeze({ status: "external" });
232
+ const CONFIG_WITH_STATUS_IGNORED = Object.freeze({ status: "ignored" });
233
+ const CONFIG_WITH_STATUS_UNCONFIGURED = Object.freeze({
234
+ status: "unconfigured",
235
+ });
236
+
237
+ /**
238
+ * Wrapper error for config validation errors that adds a name to the front of the
239
+ * error message.
240
+ */
241
+ class ConfigError extends Error {
242
+ /**
243
+ * Creates a new instance.
244
+ * @param {string} name The config object name causing the error.
245
+ * @param {number} index The index of the config object in the array.
246
+ * @param {Object} options The options for the error.
247
+ * @param {Error} [options.cause] The error that caused this error.
248
+ * @param {string} [options.message] The message to use for the error.
249
+ */
250
+ constructor(name, index, { cause, message }) {
251
+ const finalMessage = message || cause.message;
252
+
253
+ super(`Config ${name}: ${finalMessage}`, { cause });
254
+
255
+ // copy over custom properties that aren't represented
256
+ if (cause) {
257
+ for (const key of Object.keys(cause)) {
258
+ if (!(key in this)) {
259
+ this[key] = cause[key];
260
+ }
261
+ }
262
+ }
263
+
264
+ /**
265
+ * The name of the error.
266
+ * @type {string}
267
+ * @readonly
268
+ */
269
+ this.name = "ConfigError";
270
+
271
+ /**
272
+ * The index of the config object in the array.
273
+ * @type {number}
274
+ * @readonly
275
+ */
276
+ this.index = index;
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Gets the name of a config object.
282
+ * @param {ConfigObject} config The config object to get the name of.
283
+ * @returns {string} The name of the config object.
284
+ */
285
+ function getConfigName(config) {
286
+ if (config && typeof config.name === "string" && config.name) {
287
+ return `"${config.name}"`;
288
+ }
289
+
290
+ return "(unnamed)";
291
+ }
292
+
293
+ /**
294
+ * Rethrows a config error with additional information about the config object.
295
+ * @param {object} config The config object to get the name of.
296
+ * @param {number} index The index of the config object in the array.
297
+ * @param {Error} error The error to rethrow.
298
+ * @throws {ConfigError} When the error is rethrown for a config.
299
+ */
300
+ function rethrowConfigError(config, index, error) {
301
+ const configName = getConfigName(config);
302
+ throw new ConfigError(configName, index, { cause: error });
303
+ }
304
+
305
+ /**
306
+ * Shorthand for checking if a value is a string.
307
+ * @param {any} value The value to check.
308
+ * @returns {boolean} True if a string, false if not.
309
+ */
310
+ function isString(value) {
311
+ return typeof value === "string";
312
+ }
313
+
314
+ /**
315
+ * Creates a function that asserts that the config is valid
316
+ * during normalization. This checks that the config is not nullish
317
+ * and that files and ignores keys of a config object are valid as per base schema.
318
+ * @param {Object} config The config object to check.
319
+ * @param {number} index The index of the config object in the array.
320
+ * @returns {void}
321
+ * @throws {ConfigError} If the files and ignores keys of a config object are not valid.
322
+ */
323
+ function assertValidBaseConfig(config, index) {
324
+ if (config === null) {
325
+ throw new ConfigError(getConfigName(config), index, {
326
+ message: "Unexpected null config.",
327
+ });
328
+ }
329
+
330
+ if (config === undefined) {
331
+ throw new ConfigError(getConfigName(config), index, {
332
+ message: "Unexpected undefined config.",
333
+ });
334
+ }
335
+
336
+ if (typeof config !== "object") {
337
+ throw new ConfigError(getConfigName(config), index, {
338
+ message: "Unexpected non-object config.",
339
+ });
340
+ }
341
+
342
+ const validateConfig = {};
343
+
344
+ if ("files" in config) {
345
+ validateConfig.files = config.files;
346
+ }
347
+
348
+ if ("ignores" in config) {
349
+ validateConfig.ignores = config.ignores;
350
+ }
351
+
352
+ try {
353
+ FILES_AND_IGNORES_SCHEMA.validate(validateConfig);
354
+ } catch (validationError) {
355
+ rethrowConfigError(config, index, validationError);
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Wrapper around minimatch that caches minimatch patterns for
361
+ * faster matching speed over multiple file path evaluations.
362
+ * @param {string} filepath The file path to match.
363
+ * @param {string} pattern The glob pattern to match against.
364
+ * @param {object} options The minimatch options to use.
365
+ * @returns
366
+ */
367
+ function doMatch(filepath, pattern, options = {}) {
368
+ let cache = minimatchCache;
369
+
370
+ if (options.flipNegate) {
371
+ cache = negatedMinimatchCache;
372
+ }
373
+
374
+ let matcher = cache.get(pattern);
375
+
376
+ if (!matcher) {
377
+ matcher = new Minimatch(
378
+ pattern,
379
+ Object.assign({}, MINIMATCH_OPTIONS, options),
380
+ );
381
+ cache.set(pattern, matcher);
382
+ }
383
+
384
+ return matcher.match(filepath);
385
+ }
386
+
387
+ /**
388
+ * Normalizes a `ConfigArray` by flattening it and executing any functions
389
+ * that are found inside.
390
+ * @param {Array} items The items in a `ConfigArray`.
391
+ * @param {Object} context The context object to pass into any function
392
+ * found.
393
+ * @param {Array<string>} extraConfigTypes The config types to check.
394
+ * @returns {Promise<Array>} A flattened array containing only config objects.
395
+ * @throws {TypeError} When a config function returns a function.
396
+ */
397
+ async function normalize(items, context, extraConfigTypes) {
398
+ const allowFunctions = extraConfigTypes.includes("function");
399
+ const allowArrays = extraConfigTypes.includes("array");
400
+
401
+ async function* flatTraverse(array) {
402
+ for (let item of array) {
403
+ if (typeof item === "function") {
404
+ if (!allowFunctions) {
405
+ throw new TypeError("Unexpected function.");
406
+ }
407
+
408
+ item = item(context);
409
+ if (item.then) {
410
+ item = await item;
411
+ }
412
+ }
413
+
414
+ if (Array.isArray(item)) {
415
+ if (!allowArrays) {
416
+ throw new TypeError("Unexpected array.");
417
+ }
418
+ yield* flatTraverse(item);
419
+ } else if (typeof item === "function") {
420
+ throw new TypeError(
421
+ "A config function can only return an object or array.",
422
+ );
423
+ } else {
424
+ yield item;
425
+ }
426
+ }
427
+ }
428
+
429
+ /*
430
+ * Async iterables cannot be used with the spread operator, so we need to manually
431
+ * create the array to return.
432
+ */
433
+ const asyncIterable = await flatTraverse(items);
434
+ const configs = [];
435
+
436
+ for await (const config of asyncIterable) {
437
+ configs.push(config);
438
+ }
439
+
440
+ return configs;
441
+ }
442
+
443
+ /**
444
+ * Normalizes a `ConfigArray` by flattening it and executing any functions
445
+ * that are found inside.
446
+ * @param {Array} items The items in a `ConfigArray`.
447
+ * @param {Object} context The context object to pass into any function
448
+ * found.
449
+ * @param {Array<string>} extraConfigTypes The config types to check.
450
+ * @returns {Array} A flattened array containing only config objects.
451
+ * @throws {TypeError} When a config function returns a function.
452
+ */
453
+ function normalizeSync(items, context, extraConfigTypes) {
454
+ const allowFunctions = extraConfigTypes.includes("function");
455
+ const allowArrays = extraConfigTypes.includes("array");
456
+
457
+ function* flatTraverse(array) {
458
+ for (let item of array) {
459
+ if (typeof item === "function") {
460
+ if (!allowFunctions) {
461
+ throw new TypeError("Unexpected function.");
462
+ }
463
+
464
+ item = item(context);
465
+ if (item.then) {
466
+ throw new TypeError(
467
+ "Async config functions are not supported.",
468
+ );
469
+ }
470
+ }
471
+
472
+ if (Array.isArray(item)) {
473
+ if (!allowArrays) {
474
+ throw new TypeError("Unexpected array.");
475
+ }
476
+
477
+ yield* flatTraverse(item);
478
+ } else if (typeof item === "function") {
479
+ throw new TypeError(
480
+ "A config function can only return an object or array.",
481
+ );
482
+ } else {
483
+ yield item;
484
+ }
485
+ }
486
+ }
487
+
488
+ return [...flatTraverse(items)];
489
+ }
490
+
491
+ /**
492
+ * Determines if a given file path should be ignored based on the given
493
+ * matcher.
494
+ * @param {Array<string|((string) => boolean)>} ignores The ignore patterns to check.
495
+ * @param {string} filePath The absolute path of the file to check.
496
+ * @param {string} relativeFilePath The relative path of the file to check.
497
+ * @returns {boolean} True if the path should be ignored and false if not.
498
+ */
499
+ function shouldIgnorePath(ignores, filePath, relativeFilePath) {
500
+ // all files outside of the basePath are ignored
501
+ if (relativeFilePath.startsWith("..")) {
502
+ return true;
503
+ }
504
+
505
+ return ignores.reduce((ignored, matcher) => {
506
+ if (!ignored) {
507
+ if (typeof matcher === "function") {
508
+ return matcher(filePath);
509
+ }
510
+
511
+ // don't check negated patterns because we're not ignored yet
512
+ if (!matcher.startsWith("!")) {
513
+ return doMatch(relativeFilePath, matcher);
514
+ }
515
+
516
+ // otherwise we're still not ignored
517
+ return false;
518
+ }
519
+
520
+ // only need to check negated patterns because we're ignored
521
+ if (typeof matcher === "string" && matcher.startsWith("!")) {
522
+ return !doMatch(relativeFilePath, matcher, {
523
+ flipNegate: true,
524
+ });
525
+ }
526
+
527
+ return ignored;
528
+ }, false);
529
+ }
530
+
531
+ /**
532
+ * Determines if a given file path is matched by a config based on
533
+ * `ignores` only.
534
+ * @param {string} filePath The absolute file path to check.
535
+ * @param {string} basePath The base path for the config.
536
+ * @param {Object} config The config object to check.
537
+ * @returns {boolean} True if the file path is matched by the config,
538
+ * false if not.
539
+ */
540
+ function pathMatchesIgnores(filePath, basePath, config) {
541
+ /*
542
+ * For both files and ignores, functions are passed the absolute
543
+ * file path while strings are compared against the relative
544
+ * file path.
545
+ */
546
+ const relativeFilePath = path.relative(basePath, filePath);
547
+
548
+ return (
549
+ Object.keys(config).filter(key => !META_FIELDS.has(key)).length > 1 &&
550
+ !shouldIgnorePath(config.ignores, filePath, relativeFilePath)
551
+ );
552
+ }
553
+
554
+ /**
555
+ * Determines if a given file path is matched by a config. If the config
556
+ * has no `files` field, then it matches; otherwise, if a `files` field
557
+ * is present then we match the globs in `files` and exclude any globs in
558
+ * `ignores`.
559
+ * @param {string} filePath The absolute file path to check.
560
+ * @param {string} basePath The base path for the config.
561
+ * @param {Object} config The config object to check.
562
+ * @returns {boolean} True if the file path is matched by the config,
563
+ * false if not.
564
+ */
565
+ function pathMatches(filePath, basePath, config) {
566
+ /*
567
+ * For both files and ignores, functions are passed the absolute
568
+ * file path while strings are compared against the relative
569
+ * file path.
570
+ */
571
+ const relativeFilePath = path.relative(basePath, filePath);
572
+
573
+ // match both strings and functions
574
+ const match = pattern => {
575
+ if (isString(pattern)) {
576
+ return doMatch(relativeFilePath, pattern);
577
+ }
578
+
579
+ if (typeof pattern === "function") {
580
+ return pattern(filePath);
581
+ }
582
+
583
+ throw new TypeError(`Unexpected matcher type ${pattern}.`);
584
+ };
585
+
586
+ // check for all matches to config.files
587
+ let filePathMatchesPattern = config.files.some(pattern => {
588
+ if (Array.isArray(pattern)) {
589
+ return pattern.every(match);
590
+ }
591
+
592
+ return match(pattern);
593
+ });
594
+
595
+ /*
596
+ * If the file path matches the config.files patterns, then check to see
597
+ * if there are any files to ignore.
598
+ */
599
+ if (filePathMatchesPattern && config.ignores) {
600
+ filePathMatchesPattern = !shouldIgnorePath(
601
+ config.ignores,
602
+ filePath,
603
+ relativeFilePath,
604
+ );
605
+ }
606
+
607
+ return filePathMatchesPattern;
608
+ }
609
+
610
+ /**
611
+ * Ensures that a ConfigArray has been normalized.
612
+ * @param {ConfigArray} configArray The ConfigArray to check.
613
+ * @returns {void}
614
+ * @throws {Error} When the `ConfigArray` is not normalized.
615
+ */
616
+ function assertNormalized(configArray) {
617
+ // TODO: Throw more verbose error
618
+ if (!configArray.isNormalized()) {
619
+ throw new Error(
620
+ "ConfigArray must be normalized to perform this operation.",
621
+ );
622
+ }
623
+ }
624
+
625
+ /**
626
+ * Ensures that config types are valid.
627
+ * @param {Array<string>} extraConfigTypes The config types to check.
628
+ * @returns {void}
629
+ * @throws {Error} When the config types array is invalid.
630
+ */
631
+ function assertExtraConfigTypes(extraConfigTypes) {
632
+ if (extraConfigTypes.length > 2) {
633
+ throw new TypeError(
634
+ "configTypes must be an array with at most two items.",
635
+ );
636
+ }
637
+
638
+ for (const configType of extraConfigTypes) {
639
+ if (!CONFIG_TYPES.has(configType)) {
640
+ throw new TypeError(
641
+ `Unexpected config type "${configType}" found. Expected one of: "object", "array", "function".`,
642
+ );
643
+ }
644
+ }
645
+ }
646
+
647
+ //------------------------------------------------------------------------------
648
+ // Public Interface
649
+ //------------------------------------------------------------------------------
650
+
651
+ const ConfigArraySymbol = {
652
+ isNormalized: Symbol("isNormalized"),
653
+ configCache: Symbol("configCache"),
654
+ schema: Symbol("schema"),
655
+ finalizeConfig: Symbol("finalizeConfig"),
656
+ preprocessConfig: Symbol("preprocessConfig"),
657
+ };
658
+
659
+ // used to store calculate data for faster lookup
660
+ const dataCache = new WeakMap();
661
+
662
+ /**
663
+ * Represents an array of config objects and provides method for working with
664
+ * those config objects.
665
+ */
666
+ class ConfigArray extends Array {
667
+ /**
668
+ * Creates a new instance of ConfigArray.
669
+ * @param {Iterable|Function|Object} configs An iterable yielding config
670
+ * objects, or a config function, or a config object.
671
+ * @param {Object} options The options for the ConfigArray.
672
+ * @param {string} [options.basePath=""] The path of the config file
673
+ * @param {boolean} [options.normalized=false] Flag indicating if the
674
+ * configs have already been normalized.
675
+ * @param {Object} [options.schema] The additional schema
676
+ * definitions to use for the ConfigArray schema.
677
+ * @param {Array<string>} [options.extraConfigTypes] List of config types supported.
678
+ */
679
+ constructor(
680
+ configs,
681
+ {
682
+ basePath = "",
683
+ normalized = false,
684
+ schema: customSchema,
685
+ extraConfigTypes = [],
686
+ } = {},
687
+ ) {
688
+ super();
689
+
690
+ /**
691
+ * Tracks if the array has been normalized.
692
+ * @property isNormalized
693
+ * @type {boolean}
694
+ * @private
695
+ */
696
+ this[ConfigArraySymbol.isNormalized] = normalized;
697
+
698
+ /**
699
+ * The schema used for validating and merging configs.
700
+ * @property schema
701
+ * @type {ObjectSchemaInstance}
702
+ * @private
703
+ */
704
+ this[ConfigArraySymbol.schema] = new objectSchema.ObjectSchema(
705
+ Object.assign({}, customSchema, baseSchema),
706
+ );
707
+
708
+ /**
709
+ * The path of the config file that this array was loaded from.
710
+ * This is used to calculate filename matches.
711
+ * @property basePath
712
+ * @type {string}
713
+ */
714
+ this.basePath = basePath;
715
+
716
+ assertExtraConfigTypes(extraConfigTypes);
717
+
718
+ /**
719
+ * The supported config types.
720
+ * @type {Array<string>}
721
+ */
722
+ this.extraConfigTypes = [...extraConfigTypes];
723
+ Object.freeze(this.extraConfigTypes);
724
+
725
+ /**
726
+ * A cache to store calculated configs for faster repeat lookup.
727
+ * @property configCache
728
+ * @type {Map<string, Object>}
729
+ * @private
730
+ */
731
+ this[ConfigArraySymbol.configCache] = new Map();
732
+
733
+ // init cache
734
+ dataCache.set(this, {
735
+ explicitMatches: new Map(),
736
+ directoryMatches: new Map(),
737
+ files: undefined,
738
+ ignores: undefined,
739
+ });
740
+
741
+ // load the configs into this array
742
+ if (Array.isArray(configs)) {
743
+ this.push(...configs);
744
+ } else {
745
+ this.push(configs);
746
+ }
747
+ }
748
+
749
+ /**
750
+ * Prevent normal array methods from creating a new `ConfigArray` instance.
751
+ * This is to ensure that methods such as `slice()` won't try to create a
752
+ * new instance of `ConfigArray` behind the scenes as doing so may throw
753
+ * an error due to the different constructor signature.
754
+ * @type {ArrayConstructor} The `Array` constructor.
755
+ */
756
+ static get [Symbol.species]() {
757
+ return Array;
758
+ }
759
+
760
+ /**
761
+ * Returns the `files` globs from every config object in the array.
762
+ * This can be used to determine which files will be matched by a
763
+ * config array or to use as a glob pattern when no patterns are provided
764
+ * for a command line interface.
765
+ * @returns {Array<string|Function>} An array of matchers.
766
+ */
767
+ get files() {
768
+ assertNormalized(this);
769
+
770
+ // if this data has been cached, retrieve it
771
+ const cache = dataCache.get(this);
772
+
773
+ if (cache.files) {
774
+ return cache.files;
775
+ }
776
+
777
+ // otherwise calculate it
778
+
779
+ const result = [];
780
+
781
+ for (const config of this) {
782
+ if (config.files) {
783
+ config.files.forEach(filePattern => {
784
+ result.push(filePattern);
785
+ });
786
+ }
787
+ }
788
+
789
+ // store result
790
+ cache.files = result;
791
+ dataCache.set(this, cache);
792
+
793
+ return result;
794
+ }
795
+
796
+ /**
797
+ * Returns ignore matchers that should always be ignored regardless of
798
+ * the matching `files` fields in any configs. This is necessary to mimic
799
+ * the behavior of things like .gitignore and .eslintignore, allowing a
800
+ * globbing operation to be faster.
801
+ * @returns {string[]} An array of string patterns and functions to be ignored.
802
+ */
803
+ get ignores() {
804
+ assertNormalized(this);
805
+
806
+ // if this data has been cached, retrieve it
807
+ const cache = dataCache.get(this);
808
+
809
+ if (cache.ignores) {
810
+ return cache.ignores;
811
+ }
812
+
813
+ // otherwise calculate it
814
+
815
+ const result = [];
816
+
817
+ for (const config of this) {
818
+ /*
819
+ * We only count ignores if there are no other keys in the object.
820
+ * In this case, it acts list a globally ignored pattern. If there
821
+ * are additional keys, then ignores act like exclusions.
822
+ */
823
+ if (
824
+ config.ignores &&
825
+ Object.keys(config).filter(key => !META_FIELDS.has(key))
826
+ .length === 1
827
+ ) {
828
+ result.push(...config.ignores);
829
+ }
830
+ }
831
+
832
+ // store result
833
+ cache.ignores = result;
834
+ dataCache.set(this, cache);
835
+
836
+ return result;
837
+ }
838
+
839
+ /**
840
+ * Indicates if the config array has been normalized.
841
+ * @returns {boolean} True if the config array is normalized, false if not.
842
+ */
843
+ isNormalized() {
844
+ return this[ConfigArraySymbol.isNormalized];
845
+ }
846
+
847
+ /**
848
+ * Normalizes a config array by flattening embedded arrays and executing
849
+ * config functions.
850
+ * @param {Object} [context] The context object for config functions.
851
+ * @returns {Promise<ConfigArray>} The current ConfigArray instance.
852
+ */
853
+ async normalize(context = {}) {
854
+ if (!this.isNormalized()) {
855
+ const normalizedConfigs = await normalize(
856
+ this,
857
+ context,
858
+ this.extraConfigTypes,
859
+ );
860
+ this.length = 0;
861
+ this.push(
862
+ ...normalizedConfigs.map(
863
+ this[ConfigArraySymbol.preprocessConfig].bind(this),
864
+ ),
865
+ );
866
+ this.forEach(assertValidBaseConfig);
867
+ this[ConfigArraySymbol.isNormalized] = true;
868
+
869
+ // prevent further changes
870
+ Object.freeze(this);
871
+ }
872
+
873
+ return this;
874
+ }
875
+
876
+ /**
877
+ * Normalizes a config array by flattening embedded arrays and executing
878
+ * config functions.
879
+ * @param {Object} [context] The context object for config functions.
880
+ * @returns {ConfigArray} The current ConfigArray instance.
881
+ */
882
+ normalizeSync(context = {}) {
883
+ if (!this.isNormalized()) {
884
+ const normalizedConfigs = normalizeSync(
885
+ this,
886
+ context,
887
+ this.extraConfigTypes,
888
+ );
889
+ this.length = 0;
890
+ this.push(
891
+ ...normalizedConfigs.map(
892
+ this[ConfigArraySymbol.preprocessConfig].bind(this),
893
+ ),
894
+ );
895
+ this.forEach(assertValidBaseConfig);
896
+ this[ConfigArraySymbol.isNormalized] = true;
897
+
898
+ // prevent further changes
899
+ Object.freeze(this);
900
+ }
901
+
902
+ return this;
903
+ }
904
+
905
+ /**
906
+ * Finalizes the state of a config before being cached and returned by
907
+ * `getConfig()`. Does nothing by default but is provided to be
908
+ * overridden by subclasses as necessary.
909
+ * @param {Object} config The config to finalize.
910
+ * @returns {Object} The finalized config.
911
+ */
912
+ [ConfigArraySymbol.finalizeConfig](config) {
913
+ return config;
914
+ }
915
+
916
+ /**
917
+ * Preprocesses a config during the normalization process. This is the
918
+ * method to override if you want to convert an array item before it is
919
+ * validated for the first time. For example, if you want to replace a
920
+ * string with an object, this is the method to override.
921
+ * @param {Object} config The config to preprocess.
922
+ * @returns {Object} The config to use in place of the argument.
923
+ */
924
+ [ConfigArraySymbol.preprocessConfig](config) {
925
+ return config;
926
+ }
927
+
928
+ /**
929
+ * Determines if a given file path explicitly matches a `files` entry
930
+ * and also doesn't match an `ignores` entry. Configs that don't have
931
+ * a `files` property are not considered an explicit match.
932
+ * @param {string} filePath The complete path of a file to check.
933
+ * @returns {boolean} True if the file path matches a `files` entry
934
+ * or false if not.
935
+ */
936
+ isExplicitMatch(filePath) {
937
+ assertNormalized(this);
938
+
939
+ const cache = dataCache.get(this);
940
+
941
+ // first check the cache to avoid duplicate work
942
+ let result = cache.explicitMatches.get(filePath);
943
+
944
+ if (typeof result == "boolean") {
945
+ return result;
946
+ }
947
+
948
+ // TODO: Maybe move elsewhere? Maybe combine with getConfig() logic?
949
+ const relativeFilePath = path.relative(this.basePath, filePath);
950
+
951
+ if (shouldIgnorePath(this.ignores, filePath, relativeFilePath)) {
952
+ debug(`Ignoring ${filePath}`);
953
+
954
+ // cache and return result
955
+ cache.explicitMatches.set(filePath, false);
956
+ return false;
957
+ }
958
+
959
+ // filePath isn't automatically ignored, so try to find a match
960
+
961
+ for (const config of this) {
962
+ if (!config.files) {
963
+ continue;
964
+ }
965
+
966
+ if (pathMatches(filePath, this.basePath, config)) {
967
+ debug(`Matching config found for ${filePath}`);
968
+ cache.explicitMatches.set(filePath, true);
969
+ return true;
970
+ }
971
+ }
972
+
973
+ return false;
974
+ }
975
+
976
+ /**
977
+ * Returns the config object for a given file path and a status that can be used to determine why a file has no config.
978
+ * @param {string} filePath The complete path of a file to get a config for.
979
+ * @returns {{ config?: Object, status: "ignored"|"external"|"unconfigured"|"matched" }}
980
+ * An object with an optional property `config` and property `status`.
981
+ * `config` is the config object for the specified file as returned by {@linkcode ConfigArray.getConfig},
982
+ * `status` a is one of the constants returned by {@linkcode ConfigArray.getConfigStatus}.
983
+ */
984
+ getConfigWithStatus(filePath) {
985
+ assertNormalized(this);
986
+
987
+ const cache = this[ConfigArraySymbol.configCache];
988
+
989
+ // first check the cache for a filename match to avoid duplicate work
990
+ if (cache.has(filePath)) {
991
+ return cache.get(filePath);
992
+ }
993
+
994
+ // check to see if the file is outside the base path
995
+
996
+ const relativeFilePath = path.relative(this.basePath, filePath);
997
+
998
+ if (relativeFilePath.startsWith("..")) {
999
+ debug(`No config for file ${filePath} outside of base path`);
1000
+
1001
+ // cache and return result
1002
+ cache.set(filePath, CONFIG_WITH_STATUS_EXTERNAL);
1003
+ return CONFIG_WITH_STATUS_EXTERNAL;
1004
+ }
1005
+
1006
+ // next check to see if the file should be ignored
1007
+
1008
+ // check if this should be ignored due to its directory
1009
+ if (this.isDirectoryIgnored(path.dirname(filePath))) {
1010
+ debug(`Ignoring ${filePath} based on directory pattern`);
1011
+
1012
+ // cache and return result
1013
+ cache.set(filePath, CONFIG_WITH_STATUS_IGNORED);
1014
+ return CONFIG_WITH_STATUS_IGNORED;
1015
+ }
1016
+
1017
+ if (shouldIgnorePath(this.ignores, filePath, relativeFilePath)) {
1018
+ debug(`Ignoring ${filePath} based on file pattern`);
1019
+
1020
+ // cache and return result
1021
+ cache.set(filePath, CONFIG_WITH_STATUS_IGNORED);
1022
+ return CONFIG_WITH_STATUS_IGNORED;
1023
+ }
1024
+
1025
+ // filePath isn't automatically ignored, so try to construct config
1026
+
1027
+ const matchingConfigIndices = [];
1028
+ let matchFound = false;
1029
+ const universalPattern = /\/\*{1,2}$/;
1030
+
1031
+ this.forEach((config, index) => {
1032
+ if (!config.files) {
1033
+ if (!config.ignores) {
1034
+ debug(`Anonymous universal config found for ${filePath}`);
1035
+ matchingConfigIndices.push(index);
1036
+ return;
1037
+ }
1038
+
1039
+ if (pathMatchesIgnores(filePath, this.basePath, config)) {
1040
+ debug(
1041
+ `Matching config found for ${filePath} (based on ignores: ${config.ignores})`,
1042
+ );
1043
+ matchingConfigIndices.push(index);
1044
+ return;
1045
+ }
1046
+
1047
+ debug(
1048
+ `Skipped config found for ${filePath} (based on ignores: ${config.ignores})`,
1049
+ );
1050
+ return;
1051
+ }
1052
+
1053
+ /*
1054
+ * If a config has a files pattern ending in /** or /*, and the
1055
+ * filePath only matches those patterns, then the config is only
1056
+ * applied if there is another config where the filePath matches
1057
+ * a file with a specific extensions such as *.js.
1058
+ */
1059
+
1060
+ const universalFiles = config.files.filter(pattern =>
1061
+ universalPattern.test(pattern),
1062
+ );
1063
+
1064
+ // universal patterns were found so we need to check the config twice
1065
+ if (universalFiles.length) {
1066
+ debug("Universal files patterns found. Checking carefully.");
1067
+
1068
+ const nonUniversalFiles = config.files.filter(
1069
+ pattern => !universalPattern.test(pattern),
1070
+ );
1071
+
1072
+ // check that the config matches without the non-universal files first
1073
+ if (
1074
+ nonUniversalFiles.length &&
1075
+ pathMatches(filePath, this.basePath, {
1076
+ files: nonUniversalFiles,
1077
+ ignores: config.ignores,
1078
+ })
1079
+ ) {
1080
+ debug(`Matching config found for ${filePath}`);
1081
+ matchingConfigIndices.push(index);
1082
+ matchFound = true;
1083
+ return;
1084
+ }
1085
+
1086
+ // if there wasn't a match then check if it matches with universal files
1087
+ if (
1088
+ universalFiles.length &&
1089
+ pathMatches(filePath, this.basePath, {
1090
+ files: universalFiles,
1091
+ ignores: config.ignores,
1092
+ })
1093
+ ) {
1094
+ debug(`Matching config found for ${filePath}`);
1095
+ matchingConfigIndices.push(index);
1096
+ return;
1097
+ }
1098
+
1099
+ // if we make here, then there was no match
1100
+ return;
1101
+ }
1102
+
1103
+ // the normal case
1104
+ if (pathMatches(filePath, this.basePath, config)) {
1105
+ debug(`Matching config found for ${filePath}`);
1106
+ matchingConfigIndices.push(index);
1107
+ matchFound = true;
1108
+ return;
1109
+ }
1110
+ });
1111
+
1112
+ // if matching both files and ignores, there will be no config to create
1113
+ if (!matchFound) {
1114
+ debug(`No matching configs found for ${filePath}`);
1115
+
1116
+ // cache and return result
1117
+ cache.set(filePath, CONFIG_WITH_STATUS_UNCONFIGURED);
1118
+ return CONFIG_WITH_STATUS_UNCONFIGURED;
1119
+ }
1120
+
1121
+ // check to see if there is a config cached by indices
1122
+ const indicesKey = matchingConfigIndices.toString();
1123
+ let configWithStatus = cache.get(indicesKey);
1124
+
1125
+ if (configWithStatus) {
1126
+ // also store for filename for faster lookup next time
1127
+ cache.set(filePath, configWithStatus);
1128
+
1129
+ return configWithStatus;
1130
+ }
1131
+
1132
+ // otherwise construct the config
1133
+
1134
+ let finalConfig = matchingConfigIndices.reduce((result, index) => {
1135
+ try {
1136
+ return this[ConfigArraySymbol.schema].merge(
1137
+ result,
1138
+ this[index],
1139
+ );
1140
+ } catch (validationError) {
1141
+ rethrowConfigError(this[index], index, validationError);
1142
+ }
1143
+ }, {});
1144
+
1145
+ finalConfig = this[ConfigArraySymbol.finalizeConfig](finalConfig);
1146
+
1147
+ configWithStatus = Object.freeze({
1148
+ config: finalConfig,
1149
+ status: "matched",
1150
+ });
1151
+ cache.set(filePath, configWithStatus);
1152
+ cache.set(indicesKey, configWithStatus);
1153
+
1154
+ return configWithStatus;
1155
+ }
1156
+
1157
+ /**
1158
+ * Returns the config object for a given file path.
1159
+ * @param {string} filePath The complete path of a file to get a config for.
1160
+ * @returns {Object|undefined} The config object for this file or `undefined`.
1161
+ */
1162
+ getConfig(filePath) {
1163
+ return this.getConfigWithStatus(filePath).config;
1164
+ }
1165
+
1166
+ /**
1167
+ * Determines whether a file has a config or why it doesn't.
1168
+ * @param {string} filePath The complete path of the file to check.
1169
+ * @returns {"ignored"|"external"|"unconfigured"|"matched"} One of the following values:
1170
+ * * `"ignored"`: the file is ignored
1171
+ * * `"external"`: the file is outside the base path
1172
+ * * `"unconfigured"`: the file is not matched by any config
1173
+ * * `"matched"`: the file has a matching config
1174
+ */
1175
+ getConfigStatus(filePath) {
1176
+ return this.getConfigWithStatus(filePath).status;
1177
+ }
1178
+
1179
+ /**
1180
+ * Determines if the given filepath is ignored based on the configs.
1181
+ * @param {string} filePath The complete path of a file to check.
1182
+ * @returns {boolean} True if the path is ignored, false if not.
1183
+ * @deprecated Use `isFileIgnored` instead.
1184
+ */
1185
+ isIgnored(filePath) {
1186
+ return this.isFileIgnored(filePath);
1187
+ }
1188
+
1189
+ /**
1190
+ * Determines if the given filepath is ignored based on the configs.
1191
+ * @param {string} filePath The complete path of a file to check.
1192
+ * @returns {boolean} True if the path is ignored, false if not.
1193
+ */
1194
+ isFileIgnored(filePath) {
1195
+ return this.getConfigStatus(filePath) === "ignored";
1196
+ }
1197
+
1198
+ /**
1199
+ * Determines if the given directory is ignored based on the configs.
1200
+ * This checks only default `ignores` that don't have `files` in the
1201
+ * same config. A pattern such as `/foo` be considered to ignore the directory
1202
+ * while a pattern such as `/foo/**` is not considered to ignore the
1203
+ * directory because it is matching files.
1204
+ * @param {string} directoryPath The complete path of a directory to check.
1205
+ * @returns {boolean} True if the directory is ignored, false if not. Will
1206
+ * return true for any directory that is not inside of `basePath`.
1207
+ * @throws {Error} When the `ConfigArray` is not normalized.
1208
+ */
1209
+ isDirectoryIgnored(directoryPath) {
1210
+ assertNormalized(this);
1211
+
1212
+ const relativeDirectoryPath = path
1213
+ .relative(this.basePath, directoryPath)
1214
+ .replace(/\\/g, "/");
1215
+
1216
+ if (relativeDirectoryPath.startsWith("..")) {
1217
+ return true;
1218
+ }
1219
+
1220
+ // first check the cache
1221
+ const cache = dataCache.get(this).directoryMatches;
1222
+
1223
+ if (cache.has(relativeDirectoryPath)) {
1224
+ return cache.get(relativeDirectoryPath);
1225
+ }
1226
+
1227
+ const directoryParts = relativeDirectoryPath.split("/");
1228
+ let relativeDirectoryToCheck = "";
1229
+ let result = false;
1230
+
1231
+ /*
1232
+ * In order to get the correct gitignore-style ignores, where an
1233
+ * ignored parent directory cannot have any descendants unignored,
1234
+ * we need to check every directory starting at the parent all
1235
+ * the way down to the actual requested directory.
1236
+ *
1237
+ * We aggressively cache all of this info to make sure we don't
1238
+ * have to recalculate everything for every call.
1239
+ */
1240
+ do {
1241
+ relativeDirectoryToCheck += directoryParts.shift() + "/";
1242
+
1243
+ result = shouldIgnorePath(
1244
+ this.ignores,
1245
+ path.join(this.basePath, relativeDirectoryToCheck),
1246
+ relativeDirectoryToCheck,
1247
+ );
1248
+
1249
+ cache.set(relativeDirectoryToCheck, result);
1250
+ } while (!result && directoryParts.length);
1251
+
1252
+ // also cache the result for the requested path
1253
+ cache.set(relativeDirectoryPath, result);
1254
+
1255
+ return result;
1256
+ }
1257
+ }
1258
+
1259
+ exports.ConfigArray = ConfigArray;
1260
+ exports.ConfigArraySymbol = ConfigArraySymbol;