source: imaps-frontend/node_modules/eslint/lib/shared/config-validator.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 11.3 KB
Line 
1/*
2 * STOP!!! DO NOT MODIFY.
3 *
4 * This file is part of the ongoing work to move the eslintrc-style config
5 * system into the @eslint/eslintrc package. This file needs to remain
6 * unchanged in order for this work to proceed.
7 *
8 * If you think you need to change this file, please contact @nzakas first.
9 *
10 * Thanks in advance for your cooperation.
11 */
12
13/**
14 * @fileoverview Validates configs.
15 * @author Brandon Mills
16 */
17
18"use strict";
19
20//------------------------------------------------------------------------------
21// Requirements
22//------------------------------------------------------------------------------
23
24const
25 util = require("util"),
26 configSchema = require("../../conf/config-schema"),
27 BuiltInRules = require("../rules"),
28 {
29 Legacy: {
30 ConfigOps,
31 environments: BuiltInEnvironments
32 }
33 } = require("@eslint/eslintrc"),
34 { emitDeprecationWarning } = require("./deprecation-warnings");
35
36const ajv = require("./ajv")();
37const ruleValidators = new WeakMap();
38const noop = Function.prototype;
39
40//------------------------------------------------------------------------------
41// Private
42//------------------------------------------------------------------------------
43let validateSchema;
44const severityMap = {
45 error: 2,
46 warn: 1,
47 off: 0
48};
49
50/**
51 * Gets a complete options schema for a rule.
52 * @param {{create: Function, schema: (Array|null)}} rule A new-style rule object
53 * @returns {Object} JSON Schema for the rule's options.
54 */
55function getRuleOptionsSchema(rule) {
56 if (!rule) {
57 return null;
58 }
59
60 const schema = rule.schema || rule.meta && rule.meta.schema;
61
62 // Given a tuple of schemas, insert warning level at the beginning
63 if (Array.isArray(schema)) {
64 if (schema.length) {
65 return {
66 type: "array",
67 items: schema,
68 minItems: 0,
69 maxItems: schema.length
70 };
71 }
72 return {
73 type: "array",
74 minItems: 0,
75 maxItems: 0
76 };
77
78 }
79
80 // Given a full schema, leave it alone
81 return schema || null;
82}
83
84/**
85 * Validates a rule's severity and returns the severity value. Throws an error if the severity is invalid.
86 * @param {options} options The given options for the rule.
87 * @throws {Error} Wrong severity value.
88 * @returns {number|string} The rule's severity value
89 */
90function validateRuleSeverity(options) {
91 const severity = Array.isArray(options) ? options[0] : options;
92 const normSeverity = typeof severity === "string" ? severityMap[severity.toLowerCase()] : severity;
93
94 if (normSeverity === 0 || normSeverity === 1 || normSeverity === 2) {
95 return normSeverity;
96 }
97
98 throw new Error(`\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '${util.inspect(severity).replace(/'/gu, "\"").replace(/\n/gu, "")}').\n`);
99
100}
101
102/**
103 * Validates the non-severity options passed to a rule, based on its schema.
104 * @param {{create: Function}} rule The rule to validate
105 * @param {Array} localOptions The options for the rule, excluding severity
106 * @throws {Error} Any rule validation errors.
107 * @returns {void}
108 */
109function validateRuleSchema(rule, localOptions) {
110 if (!ruleValidators.has(rule)) {
111 const schema = getRuleOptionsSchema(rule);
112
113 if (schema) {
114 ruleValidators.set(rule, ajv.compile(schema));
115 }
116 }
117
118 const validateRule = ruleValidators.get(rule);
119
120 if (validateRule) {
121 validateRule(localOptions);
122 if (validateRule.errors) {
123 throw new Error(validateRule.errors.map(
124 error => `\tValue ${JSON.stringify(error.data)} ${error.message}.\n`
125 ).join(""));
126 }
127 }
128}
129
130/**
131 * Validates a rule's options against its schema.
132 * @param {{create: Function}|null} rule The rule that the config is being validated for
133 * @param {string} ruleId The rule's unique name.
134 * @param {Array|number} options The given options for the rule.
135 * @param {string|null} source The name of the configuration source to report in any errors. If null or undefined,
136 * no source is prepended to the message.
137 * @throws {Error} Upon any bad rule configuration.
138 * @returns {void}
139 */
140function validateRuleOptions(rule, ruleId, options, source = null) {
141 try {
142 const severity = validateRuleSeverity(options);
143
144 if (severity !== 0) {
145 validateRuleSchema(rule, Array.isArray(options) ? options.slice(1) : []);
146 }
147 } catch (err) {
148 const enhancedMessage = `Configuration for rule "${ruleId}" is invalid:\n${err.message}`;
149
150 if (typeof source === "string") {
151 throw new Error(`${source}:\n\t${enhancedMessage}`);
152 } else {
153 throw new Error(enhancedMessage);
154 }
155 }
156}
157
158/**
159 * Validates an environment object
160 * @param {Object} environment The environment config object to validate.
161 * @param {string} source The name of the configuration source to report in any errors.
162 * @param {(envId:string) => Object} [getAdditionalEnv] A map from strings to loaded environments.
163 * @returns {void}
164 */
165function validateEnvironment(
166 environment,
167 source,
168 getAdditionalEnv = noop
169) {
170
171 // not having an environment is ok
172 if (!environment) {
173 return;
174 }
175
176 Object.keys(environment).forEach(id => {
177 const env = getAdditionalEnv(id) || BuiltInEnvironments.get(id) || null;
178
179 if (!env) {
180 const message = `${source}:\n\tEnvironment key "${id}" is unknown\n`;
181
182 throw new Error(message);
183 }
184 });
185}
186
187/**
188 * Validates a rules config object
189 * @param {Object} rulesConfig The rules config object to validate.
190 * @param {string} source The name of the configuration source to report in any errors.
191 * @param {(ruleId:string) => Object} getAdditionalRule A map from strings to loaded rules
192 * @returns {void}
193 */
194function validateRules(
195 rulesConfig,
196 source,
197 getAdditionalRule = noop
198) {
199 if (!rulesConfig) {
200 return;
201 }
202
203 Object.keys(rulesConfig).forEach(id => {
204 const rule = getAdditionalRule(id) || BuiltInRules.get(id) || null;
205
206 validateRuleOptions(rule, id, rulesConfig[id], source);
207 });
208}
209
210/**
211 * Validates a `globals` section of a config file
212 * @param {Object} globalsConfig The `globals` section
213 * @param {string|null} source The name of the configuration source to report in the event of an error.
214 * @returns {void}
215 */
216function validateGlobals(globalsConfig, source = null) {
217 if (!globalsConfig) {
218 return;
219 }
220
221 Object.entries(globalsConfig)
222 .forEach(([configuredGlobal, configuredValue]) => {
223 try {
224 ConfigOps.normalizeConfigGlobal(configuredValue);
225 } catch (err) {
226 throw new Error(`ESLint configuration of global '${configuredGlobal}' in ${source} is invalid:\n${err.message}`);
227 }
228 });
229}
230
231/**
232 * Validate `processor` configuration.
233 * @param {string|undefined} processorName The processor name.
234 * @param {string} source The name of config file.
235 * @param {(id:string) => Processor} getProcessor The getter of defined processors.
236 * @throws {Error} For invalid processor configuration.
237 * @returns {void}
238 */
239function validateProcessor(processorName, source, getProcessor) {
240 if (processorName && !getProcessor(processorName)) {
241 throw new Error(`ESLint configuration of processor in '${source}' is invalid: '${processorName}' was not found.`);
242 }
243}
244
245/**
246 * Formats an array of schema validation errors.
247 * @param {Array} errors An array of error messages to format.
248 * @returns {string} Formatted error message
249 */
250function formatErrors(errors) {
251 return errors.map(error => {
252 if (error.keyword === "additionalProperties") {
253 const formattedPropertyPath = error.dataPath.length ? `${error.dataPath.slice(1)}.${error.params.additionalProperty}` : error.params.additionalProperty;
254
255 return `Unexpected top-level property "${formattedPropertyPath}"`;
256 }
257 if (error.keyword === "type") {
258 const formattedField = error.dataPath.slice(1);
259 const formattedExpectedType = Array.isArray(error.schema) ? error.schema.join("/") : error.schema;
260 const formattedValue = JSON.stringify(error.data);
261
262 return `Property "${formattedField}" is the wrong type (expected ${formattedExpectedType} but got \`${formattedValue}\`)`;
263 }
264
265 const field = error.dataPath[0] === "." ? error.dataPath.slice(1) : error.dataPath;
266
267 return `"${field}" ${error.message}. Value: ${JSON.stringify(error.data)}`;
268 }).map(message => `\t- ${message}.\n`).join("");
269}
270
271/**
272 * Validates the top level properties of the config object.
273 * @param {Object} config The config object to validate.
274 * @param {string} source The name of the configuration source to report in any errors.
275 * @throws {Error} For any config invalid per the schema.
276 * @returns {void}
277 */
278function validateConfigSchema(config, source = null) {
279 validateSchema = validateSchema || ajv.compile(configSchema);
280
281 if (!validateSchema(config)) {
282 throw new Error(`ESLint configuration in ${source} is invalid:\n${formatErrors(validateSchema.errors)}`);
283 }
284
285 if (Object.hasOwnProperty.call(config, "ecmaFeatures")) {
286 emitDeprecationWarning(source, "ESLINT_LEGACY_ECMAFEATURES");
287 }
288}
289
290/**
291 * Validates an entire config object.
292 * @param {Object} config The config object to validate.
293 * @param {string} source The name of the configuration source to report in any errors.
294 * @param {(ruleId:string) => Object} [getAdditionalRule] A map from strings to loaded rules.
295 * @param {(envId:string) => Object} [getAdditionalEnv] A map from strings to loaded envs.
296 * @returns {void}
297 */
298function validate(config, source, getAdditionalRule, getAdditionalEnv) {
299 validateConfigSchema(config, source);
300 validateRules(config.rules, source, getAdditionalRule);
301 validateEnvironment(config.env, source, getAdditionalEnv);
302 validateGlobals(config.globals, source);
303
304 for (const override of config.overrides || []) {
305 validateRules(override.rules, source, getAdditionalRule);
306 validateEnvironment(override.env, source, getAdditionalEnv);
307 validateGlobals(config.globals, source);
308 }
309}
310
311const validated = new WeakSet();
312
313/**
314 * Validate config array object.
315 * @param {ConfigArray} configArray The config array to validate.
316 * @returns {void}
317 */
318function validateConfigArray(configArray) {
319 const getPluginEnv = Map.prototype.get.bind(configArray.pluginEnvironments);
320 const getPluginProcessor = Map.prototype.get.bind(configArray.pluginProcessors);
321 const getPluginRule = Map.prototype.get.bind(configArray.pluginRules);
322
323 // Validate.
324 for (const element of configArray) {
325 if (validated.has(element)) {
326 continue;
327 }
328 validated.add(element);
329
330 validateEnvironment(element.env, element.name, getPluginEnv);
331 validateGlobals(element.globals, element.name);
332 validateProcessor(element.processor, element.name, getPluginProcessor);
333 validateRules(element.rules, element.name, getPluginRule);
334 }
335}
336
337//------------------------------------------------------------------------------
338// Public Interface
339//------------------------------------------------------------------------------
340
341module.exports = {
342 getRuleOptionsSchema,
343 validate,
344 validateConfigArray,
345 validateConfigSchema,
346 validateRuleOptions
347};
Note: See TracBrowser for help on using the repository browser.