validate-language-options.js 4.8 KB

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