massive update, probably broken
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / config / flat-config-schema.js
1 /**
2  * @fileoverview Flat config schema
3  * @author Nicholas C. Zakas
4  */
5
6 "use strict";
7
8 //-----------------------------------------------------------------------------
9 // Type Definitions
10 //-----------------------------------------------------------------------------
11
12 /**
13  * @typedef ObjectPropertySchema
14  * @property {Function|string} merge The function or name of the function to call
15  *      to merge multiple objects with this property.
16  * @property {Function|string} validate The function or name of the function to call
17  *      to validate the value of this property.
18  */
19
20 //-----------------------------------------------------------------------------
21 // Helpers
22 //-----------------------------------------------------------------------------
23
24 const ruleSeverities = new Map([
25     [0, 0], ["off", 0],
26     [1, 1], ["warn", 1],
27     [2, 2], ["error", 2]
28 ]);
29
30 const globalVariablesValues = new Set([
31     true, "true", "writable", "writeable",
32     false, "false", "readonly", "readable", null,
33     "off"
34 ]);
35
36 /**
37  * Check if a value is a non-null object.
38  * @param {any} value The value to check.
39  * @returns {boolean} `true` if the value is a non-null object.
40  */
41 function isNonNullObject(value) {
42     return typeof value === "object" && value !== null;
43 }
44
45 /**
46  * Check if a value is undefined.
47  * @param {any} value The value to check.
48  * @returns {boolean} `true` if the value is undefined.
49  */
50 function isUndefined(value) {
51     return typeof value === "undefined";
52 }
53
54 /**
55  * Deeply merges two objects.
56  * @param {Object} first The base object.
57  * @param {Object} second The overrides object.
58  * @returns {Object} An object with properties from both first and second.
59  */
60 function deepMerge(first = {}, second = {}) {
61
62     /*
63      * If the second value is an array, just return it. We don't merge
64      * arrays because order matters and we can't know the correct order.
65      */
66     if (Array.isArray(second)) {
67         return second;
68     }
69
70     /*
71      * First create a result object where properties from the second object
72      * overwrite properties from the first. This sets up a baseline to use
73      * later rather than needing to inspect and change every property
74      * individually.
75      */
76     const result = {
77         ...first,
78         ...second
79     };
80
81     for (const key of Object.keys(second)) {
82
83         // avoid hairy edge case
84         if (key === "__proto__") {
85             continue;
86         }
87
88         const firstValue = first[key];
89         const secondValue = second[key];
90
91         if (isNonNullObject(firstValue)) {
92             result[key] = deepMerge(firstValue, secondValue);
93         } else if (isUndefined(firstValue)) {
94             if (isNonNullObject(secondValue)) {
95                 result[key] = deepMerge(
96                     Array.isArray(secondValue) ? [] : {},
97                     secondValue
98                 );
99             } else if (!isUndefined(secondValue)) {
100                 result[key] = secondValue;
101             }
102         }
103     }
104
105     return result;
106
107 }
108
109 /**
110  * Normalizes the rule options config for a given rule by ensuring that
111  * it is an array and that the first item is 0, 1, or 2.
112  * @param {Array|string|number} ruleOptions The rule options config.
113  * @returns {Array} An array of rule options.
114  */
115 function normalizeRuleOptions(ruleOptions) {
116
117     const finalOptions = Array.isArray(ruleOptions)
118         ? ruleOptions.slice(0)
119         : [ruleOptions];
120
121     finalOptions[0] = ruleSeverities.get(finalOptions[0]);
122     return finalOptions;
123 }
124
125 //-----------------------------------------------------------------------------
126 // Assertions
127 //-----------------------------------------------------------------------------
128
129 /**
130  * Validates that a value is a valid rule options entry.
131  * @param {any} value The value to check.
132  * @returns {void}
133  * @throws {TypeError} If the value isn't a valid rule options.
134  */
135 function assertIsRuleOptions(value) {
136
137     if (typeof value !== "string" && typeof value !== "number" && !Array.isArray(value)) {
138         throw new TypeError("Expected a string, number, or array.");
139     }
140 }
141
142 /**
143  * Validates that a value is valid rule severity.
144  * @param {any} value The value to check.
145  * @returns {void}
146  * @throws {TypeError} If the value isn't a valid rule severity.
147  */
148 function assertIsRuleSeverity(value) {
149     const severity = typeof value === "string"
150         ? ruleSeverities.get(value.toLowerCase())
151         : ruleSeverities.get(value);
152
153     if (typeof severity === "undefined") {
154         throw new TypeError("Expected severity of \"off\", 0, \"warn\", 1, \"error\", or 2.");
155     }
156 }
157
158 /**
159  * Validates that a given string is the form pluginName/objectName.
160  * @param {string} value The string to check.
161  * @returns {void}
162  * @throws {TypeError} If the string isn't in the correct format.
163  */
164 function assertIsPluginMemberName(value) {
165     if (!/[@a-z0-9-_$]+(?:\/(?:[a-z0-9-_$]+))+$/iu.test(value)) {
166         throw new TypeError(`Expected string in the form "pluginName/objectName" but found "${value}".`);
167     }
168 }
169
170 /**
171  * Validates that a value is an object.
172  * @param {any} value The value to check.
173  * @returns {void}
174  * @throws {TypeError} If the value isn't an object.
175  */
176 function assertIsObject(value) {
177     if (!isNonNullObject(value)) {
178         throw new TypeError("Expected an object.");
179     }
180 }
181
182 /**
183  * Validates that a value is an object or a string.
184  * @param {any} value The value to check.
185  * @returns {void}
186  * @throws {TypeError} If the value isn't an object or a string.
187  */
188 function assertIsObjectOrString(value) {
189     if ((!value || typeof value !== "object") && typeof value !== "string") {
190         throw new TypeError("Expected an object or string.");
191     }
192 }
193
194 //-----------------------------------------------------------------------------
195 // Low-Level Schemas
196 //-----------------------------------------------------------------------------
197
198
199 /** @type {ObjectPropertySchema} */
200 const numberSchema = {
201     merge: "replace",
202     validate: "number"
203 };
204
205 /** @type {ObjectPropertySchema} */
206 const booleanSchema = {
207     merge: "replace",
208     validate: "boolean"
209 };
210
211 /** @type {ObjectPropertySchema} */
212 const deepObjectAssignSchema = {
213     merge(first = {}, second = {}) {
214         return deepMerge(first, second);
215     },
216     validate: "object"
217 };
218
219 //-----------------------------------------------------------------------------
220 // High-Level Schemas
221 //-----------------------------------------------------------------------------
222
223 /** @type {ObjectPropertySchema} */
224 const globalsSchema = {
225     merge: "assign",
226     validate(value) {
227
228         assertIsObject(value);
229
230         for (const key of Object.keys(value)) {
231
232             // avoid hairy edge case
233             if (key === "__proto__") {
234                 continue;
235             }
236
237             if (key !== key.trim()) {
238                 throw new TypeError(`Global "${key}" has leading or trailing whitespace.`);
239             }
240
241             if (!globalVariablesValues.has(value[key])) {
242                 throw new TypeError(`Key "${key}": Expected "readonly", "writable", or "off".`);
243             }
244         }
245     }
246 };
247
248 /** @type {ObjectPropertySchema} */
249 const parserSchema = {
250     merge: "replace",
251     validate(value) {
252         assertIsObjectOrString(value);
253
254         if (typeof value === "object" && typeof value.parse !== "function" && typeof value.parseForESLint !== "function") {
255             throw new TypeError("Expected object to have a parse() or parseForESLint() method.");
256         }
257
258         if (typeof value === "string") {
259             assertIsPluginMemberName(value);
260         }
261     }
262 };
263
264 /** @type {ObjectPropertySchema} */
265 const pluginsSchema = {
266     merge(first = {}, second = {}) {
267         const keys = new Set([...Object.keys(first), ...Object.keys(second)]);
268         const result = {};
269
270         // manually validate that plugins are not redefined
271         for (const key of keys) {
272
273             // avoid hairy edge case
274             if (key === "__proto__") {
275                 continue;
276             }
277
278             if (key in first && key in second && first[key] !== second[key]) {
279                 throw new TypeError(`Cannot redefine plugin "${key}".`);
280             }
281
282             result[key] = second[key] || first[key];
283         }
284
285         return result;
286     },
287     validate(value) {
288
289         // first check the value to be sure it's an object
290         if (value === null || typeof value !== "object") {
291             throw new TypeError("Expected an object.");
292         }
293
294         // second check the keys to make sure they are objects
295         for (const key of Object.keys(value)) {
296
297             // avoid hairy edge case
298             if (key === "__proto__") {
299                 continue;
300             }
301
302             if (value[key] === null || typeof value[key] !== "object") {
303                 throw new TypeError(`Key "${key}": Expected an object.`);
304             }
305         }
306     }
307 };
308
309 /** @type {ObjectPropertySchema} */
310 const processorSchema = {
311     merge: "replace",
312     validate(value) {
313         if (typeof value === "string") {
314             assertIsPluginMemberName(value);
315         } else if (value && typeof value === "object") {
316             if (typeof value.preprocess !== "function" || typeof value.postprocess !== "function") {
317                 throw new TypeError("Object must have a preprocess() and a postprocess() method.");
318             }
319         } else {
320             throw new TypeError("Expected an object or a string.");
321         }
322     }
323 };
324
325 /** @type {ObjectPropertySchema} */
326 const rulesSchema = {
327     merge(first = {}, second = {}) {
328
329         const result = {
330             ...first,
331             ...second
332         };
333
334         for (const ruleId of Object.keys(result)) {
335
336             // avoid hairy edge case
337             if (ruleId === "__proto__") {
338
339                 /* eslint-disable-next-line no-proto */
340                 delete result.__proto__;
341                 continue;
342             }
343
344             result[ruleId] = normalizeRuleOptions(result[ruleId]);
345
346             /*
347              * If either rule config is missing, then the correct
348              * config is already present and we just need to normalize
349              * the severity.
350              */
351             if (!(ruleId in first) || !(ruleId in second)) {
352                 continue;
353             }
354
355             const firstRuleOptions = normalizeRuleOptions(first[ruleId]);
356             const secondRuleOptions = normalizeRuleOptions(second[ruleId]);
357
358             /*
359              * If the second rule config only has a severity (length of 1),
360              * then use that severity and keep the rest of the options from
361              * the first rule config.
362              */
363             if (secondRuleOptions.length === 1) {
364                 result[ruleId] = [secondRuleOptions[0], ...firstRuleOptions.slice(1)];
365                 continue;
366             }
367
368             /*
369              * In any other situation, then the second rule config takes
370              * precedence. That means the value at `result[ruleId]` is
371              * already correct and no further work is necessary.
372              */
373         }
374
375         return result;
376     },
377
378     validate(value) {
379         assertIsObject(value);
380
381         let lastRuleId;
382
383         // Performance: One try-catch has less overhead than one per loop iteration
384         try {
385
386             /*
387              * We are not checking the rule schema here because there is no
388              * guarantee that the rule definition is present at this point. Instead
389              * we wait and check the rule schema during the finalization step
390              * of calculating a config.
391              */
392             for (const ruleId of Object.keys(value)) {
393
394                 // avoid hairy edge case
395                 if (ruleId === "__proto__") {
396                     continue;
397                 }
398
399                 lastRuleId = ruleId;
400
401                 const ruleOptions = value[ruleId];
402
403                 assertIsRuleOptions(ruleOptions);
404
405                 if (Array.isArray(ruleOptions)) {
406                     assertIsRuleSeverity(ruleOptions[0]);
407                 } else {
408                     assertIsRuleSeverity(ruleOptions);
409                 }
410             }
411         } catch (error) {
412             error.message = `Key "${lastRuleId}": ${error.message}`;
413             throw error;
414         }
415     }
416 };
417
418 /** @type {ObjectPropertySchema} */
419 const sourceTypeSchema = {
420     merge: "replace",
421     validate(value) {
422         if (typeof value !== "string" || !/^(?:script|module|commonjs)$/u.test(value)) {
423             throw new TypeError("Expected \"script\", \"module\", or \"commonjs\".");
424         }
425     }
426 };
427
428 //-----------------------------------------------------------------------------
429 // Full schema
430 //-----------------------------------------------------------------------------
431
432 exports.flatConfigSchema = {
433     settings: deepObjectAssignSchema,
434     linterOptions: {
435         schema: {
436             noInlineConfig: booleanSchema,
437             reportUnusedDisableDirectives: booleanSchema
438         }
439     },
440     languageOptions: {
441         schema: {
442             ecmaVersion: numberSchema,
443             sourceType: sourceTypeSchema,
444             globals: globalsSchema,
445             parser: parserSchema,
446             parserOptions: deepObjectAssignSchema
447         }
448     },
449     processor: processorSchema,
450     plugins: pluginsSchema,
451     rules: rulesSchema
452 };