validate-language-options.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /**
  2. * @fileoverview The schema to validate language options
  3. * @author Nicholas C. Zakas
  4. */
  5. "use strict";
  6. //-----------------------------------------------------------------------------
  7. // Data
  8. //-----------------------------------------------------------------------------
  9. const globalVariablesValues = new Set([
  10. true,
  11. "true",
  12. "writable",
  13. "writeable",
  14. false,
  15. "false",
  16. "readonly",
  17. "readable",
  18. null,
  19. "off",
  20. ]);
  21. //------------------------------------------------------------------------------
  22. // Helpers
  23. //------------------------------------------------------------------------------
  24. /**
  25. * Check if a value is a non-null object.
  26. * @param {any} value The value to check.
  27. * @returns {boolean} `true` if the value is a non-null object.
  28. */
  29. function isNonNullObject(value) {
  30. return typeof value === "object" && value !== null;
  31. }
  32. /**
  33. * Check if a value is a non-null non-array object.
  34. * @param {any} value The value to check.
  35. * @returns {boolean} `true` if the value is a non-null non-array object.
  36. */
  37. function isNonArrayObject(value) {
  38. return isNonNullObject(value) && !Array.isArray(value);
  39. }
  40. /**
  41. * Check if a value is undefined.
  42. * @param {any} value The value to check.
  43. * @returns {boolean} `true` if the value is undefined.
  44. */
  45. function isUndefined(value) {
  46. return typeof value === "undefined";
  47. }
  48. //-----------------------------------------------------------------------------
  49. // Schemas
  50. //-----------------------------------------------------------------------------
  51. /**
  52. * Validates the ecmaVersion property.
  53. * @param {string|number} ecmaVersion The value to check.
  54. * @returns {void}
  55. * @throws {TypeError} If the value is invalid.
  56. */
  57. function validateEcmaVersion(ecmaVersion) {
  58. if (isUndefined(ecmaVersion)) {
  59. throw new TypeError(
  60. 'Key "ecmaVersion": Expected an "ecmaVersion" property.',
  61. );
  62. }
  63. if (typeof ecmaVersion !== "number" && ecmaVersion !== "latest") {
  64. throw new TypeError(
  65. 'Key "ecmaVersion": Expected a number or "latest".',
  66. );
  67. }
  68. }
  69. /**
  70. * Validates the sourceType property.
  71. * @param {string} sourceType The value to check.
  72. * @returns {void}
  73. * @throws {TypeError} If the value is invalid.
  74. */
  75. function validateSourceType(sourceType) {
  76. if (
  77. typeof sourceType !== "string" ||
  78. !/^(?:script|module|commonjs)$/u.test(sourceType)
  79. ) {
  80. throw new TypeError(
  81. 'Key "sourceType": Expected "script", "module", or "commonjs".',
  82. );
  83. }
  84. }
  85. /**
  86. * Validates the globals property.
  87. * @param {Object} globals The value to check.
  88. * @returns {void}
  89. * @throws {TypeError} If the value is invalid.
  90. */
  91. function validateGlobals(globals) {
  92. if (!isNonArrayObject(globals)) {
  93. throw new TypeError('Key "globals": Expected an object.');
  94. }
  95. for (const key of Object.keys(globals)) {
  96. // avoid hairy edge case
  97. if (key === "__proto__") {
  98. continue;
  99. }
  100. if (key !== key.trim()) {
  101. throw new TypeError(
  102. `Key "globals": Global "${key}" has leading or trailing whitespace.`,
  103. );
  104. }
  105. if (!globalVariablesValues.has(globals[key])) {
  106. throw new TypeError(
  107. `Key "globals": Key "${key}": Expected "readonly", "writable", or "off".`,
  108. );
  109. }
  110. }
  111. }
  112. /**
  113. * Validates the parser property.
  114. * @param {Object} parser The value to check.
  115. * @returns {void}
  116. * @throws {TypeError} If the value is invalid.
  117. */
  118. function validateParser(parser) {
  119. if (
  120. !parser ||
  121. typeof parser !== "object" ||
  122. (typeof parser.parse !== "function" &&
  123. typeof parser.parseForESLint !== "function")
  124. ) {
  125. throw new TypeError(
  126. 'Key "parser": Expected object with parse() or parseForESLint() method.',
  127. );
  128. }
  129. }
  130. /**
  131. * Validates the language options.
  132. * @param {Object} languageOptions The language options to validate.
  133. * @returns {void}
  134. * @throws {TypeError} If the language options are invalid.
  135. */
  136. function validateLanguageOptions(languageOptions) {
  137. if (!isNonArrayObject(languageOptions)) {
  138. throw new TypeError("Expected an object.");
  139. }
  140. const {
  141. ecmaVersion,
  142. sourceType,
  143. globals,
  144. parser,
  145. parserOptions,
  146. ...otherOptions
  147. } = languageOptions;
  148. if ("ecmaVersion" in languageOptions) {
  149. validateEcmaVersion(ecmaVersion);
  150. }
  151. if ("sourceType" in languageOptions) {
  152. validateSourceType(sourceType);
  153. }
  154. if ("globals" in languageOptions) {
  155. validateGlobals(globals);
  156. }
  157. if ("parser" in languageOptions) {
  158. validateParser(parser);
  159. }
  160. if ("parserOptions" in languageOptions) {
  161. if (!isNonArrayObject(parserOptions)) {
  162. throw new TypeError('Key "parserOptions": Expected an object.');
  163. }
  164. }
  165. const otherOptionKeys = Object.keys(otherOptions);
  166. if (otherOptionKeys.length > 0) {
  167. throw new TypeError(`Unexpected key "${otherOptionKeys[0]}" found.`);
  168. }
  169. }
  170. module.exports = { validateLanguageOptions };