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