| /** |
| * @fileoverview The schema to validate language options |
| * @author Nicholas C. Zakas |
| */ |
| |
| "use strict"; |
| |
| //----------------------------------------------------------------------------- |
| // Data |
| //----------------------------------------------------------------------------- |
| |
| const globalVariablesValues = new Set([ |
| true, |
| "true", |
| "writable", |
| "writeable", |
| false, |
| "false", |
| "readonly", |
| "readable", |
| null, |
| "off", |
| ]); |
| |
| //------------------------------------------------------------------------------ |
| // Helpers |
| //------------------------------------------------------------------------------ |
| |
| /** |
| * Check if a value is a non-null object. |
| * @param {any} value The value to check. |
| * @returns {boolean} `true` if the value is a non-null object. |
| */ |
| function isNonNullObject(value) { |
| return typeof value === "object" && value !== null; |
| } |
| |
| /** |
| * Check if a value is a non-null non-array object. |
| * @param {any} value The value to check. |
| * @returns {boolean} `true` if the value is a non-null non-array object. |
| */ |
| function isNonArrayObject(value) { |
| return isNonNullObject(value) && !Array.isArray(value); |
| } |
| |
| /** |
| * Check if a value is undefined. |
| * @param {any} value The value to check. |
| * @returns {boolean} `true` if the value is undefined. |
| */ |
| function isUndefined(value) { |
| return typeof value === "undefined"; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Schemas |
| //----------------------------------------------------------------------------- |
| |
| /** |
| * Validates the ecmaVersion property. |
| * @param {string|number} ecmaVersion The value to check. |
| * @returns {void} |
| * @throws {TypeError} If the value is invalid. |
| */ |
| function validateEcmaVersion(ecmaVersion) { |
| if (isUndefined(ecmaVersion)) { |
| throw new TypeError( |
| 'Key "ecmaVersion": Expected an "ecmaVersion" property.', |
| ); |
| } |
| |
| if (typeof ecmaVersion !== "number" && ecmaVersion !== "latest") { |
| throw new TypeError( |
| 'Key "ecmaVersion": Expected a number or "latest".', |
| ); |
| } |
| } |
| |
| /** |
| * Validates the sourceType property. |
| * @param {string} sourceType The value to check. |
| * @returns {void} |
| * @throws {TypeError} If the value is invalid. |
| */ |
| function validateSourceType(sourceType) { |
| if ( |
| typeof sourceType !== "string" || |
| !/^(?:script|module|commonjs)$/u.test(sourceType) |
| ) { |
| throw new TypeError( |
| 'Key "sourceType": Expected "script", "module", or "commonjs".', |
| ); |
| } |
| } |
| |
| /** |
| * Validates the globals property. |
| * @param {Object} globals The value to check. |
| * @returns {void} |
| * @throws {TypeError} If the value is invalid. |
| */ |
| function validateGlobals(globals) { |
| if (!isNonArrayObject(globals)) { |
| throw new TypeError('Key "globals": Expected an object.'); |
| } |
| |
| for (const key of Object.keys(globals)) { |
| // avoid hairy edge case |
| if (key === "__proto__") { |
| continue; |
| } |
| |
| if (key !== key.trim()) { |
| throw new TypeError( |
| `Key "globals": Global "${key}" has leading or trailing whitespace.`, |
| ); |
| } |
| |
| if (!globalVariablesValues.has(globals[key])) { |
| throw new TypeError( |
| `Key "globals": Key "${key}": Expected "readonly", "writable", or "off".`, |
| ); |
| } |
| } |
| } |
| |
| /** |
| * Validates the parser property. |
| * @param {Object} parser The value to check. |
| * @returns {void} |
| * @throws {TypeError} If the value is invalid. |
| */ |
| function validateParser(parser) { |
| if ( |
| !parser || |
| typeof parser !== "object" || |
| (typeof parser.parse !== "function" && |
| typeof parser.parseForESLint !== "function") |
| ) { |
| throw new TypeError( |
| 'Key "parser": Expected object with parse() or parseForESLint() method.', |
| ); |
| } |
| } |
| |
| /** |
| * Validates the language options. |
| * @param {Object} languageOptions The language options to validate. |
| * @returns {void} |
| * @throws {TypeError} If the language options are invalid. |
| */ |
| function validateLanguageOptions(languageOptions) { |
| if (!isNonArrayObject(languageOptions)) { |
| throw new TypeError("Expected an object."); |
| } |
| |
| const { |
| ecmaVersion, |
| sourceType, |
| globals, |
| parser, |
| parserOptions, |
| ...otherOptions |
| } = languageOptions; |
| |
| if ("ecmaVersion" in languageOptions) { |
| validateEcmaVersion(ecmaVersion); |
| } |
| |
| if ("sourceType" in languageOptions) { |
| validateSourceType(sourceType); |
| } |
| |
| if ("globals" in languageOptions) { |
| validateGlobals(globals); |
| } |
| |
| if ("parser" in languageOptions) { |
| validateParser(parser); |
| } |
| |
| if ("parserOptions" in languageOptions) { |
| if (!isNonArrayObject(parserOptions)) { |
| throw new TypeError('Key "parserOptions": Expected an object.'); |
| } |
| } |
| |
| const otherOptionKeys = Object.keys(otherOptions); |
| |
| if (otherOptionKeys.length > 0) { |
| throw new TypeError(`Unexpected key "${otherOptionKeys[0]}" found.`); |
| } |
| } |
| |
| module.exports = { validateLanguageOptions }; |