catharsis.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /**
  2. * Catharsis
  3. * A parser for Google Closure Compiler type expressions, powered by PEG.js.
  4. *
  5. * @author Jeff Williams <jeffrey.l.williams@gmail.com>
  6. * @license MIT
  7. */
  8. const describe = require('./lib/describe');
  9. const { parse } = require('./lib/parser');
  10. const stringify = require('./lib/stringify');
  11. const typeExpressionCache = {
  12. normal: new Map(),
  13. jsdoc: new Map()
  14. };
  15. const parsedTypeCache = {
  16. normal: new Map(),
  17. htmlSafe: new Map()
  18. };
  19. const descriptionCache = {
  20. normal: new Map()
  21. };
  22. function getTypeExpressionCache({useCache, jsdoc}) {
  23. if (useCache === false) {
  24. return null;
  25. } else if (jsdoc === true) {
  26. return typeExpressionCache.jsdoc;
  27. } else {
  28. return typeExpressionCache.normal;
  29. }
  30. }
  31. function getParsedTypeCache({useCache, links, htmlSafe}) {
  32. if (useCache === false || links !== null || links !== undefined) {
  33. return null;
  34. } else if (htmlSafe === true) {
  35. return parsedTypeCache.htmlSafe;
  36. } else {
  37. return parsedTypeCache.normal;
  38. }
  39. }
  40. function getDescriptionCache({useCache, links}) {
  41. if (useCache === false || links !== null || links !== undefined) {
  42. return null;
  43. } else {
  44. return descriptionCache.normal;
  45. }
  46. }
  47. // can't return the original if any of the following are true:
  48. // 1. restringification was requested
  49. // 2. htmlSafe option was requested
  50. // 3. links option was provided
  51. // 4. typeExpression property is missing
  52. function canReturnOriginalExpression(parsedType, {restringify, htmlSafe, links}) {
  53. return restringify !== true && htmlSafe !== true &&
  54. (links === null || links === undefined) &&
  55. Object.prototype.hasOwnProperty.call(parsedType, 'typeExpression');
  56. }
  57. // Add non-enumerable properties to a result object, then freeze it.
  58. function prepareFrozenObject(obj, expr, {jsdoc}) {
  59. Object.defineProperty(obj, 'jsdoc', {
  60. value: jsdoc === true ? jsdoc : false
  61. });
  62. if (expr) {
  63. Object.defineProperty(obj, 'typeExpression', {
  64. value: expr
  65. });
  66. }
  67. return Object.freeze(obj);
  68. }
  69. function cachedParse(expr, options) {
  70. const cache = getTypeExpressionCache(options);
  71. let parsedType = cache ? cache.get(expr) : null;
  72. if (parsedType) {
  73. return parsedType;
  74. } else {
  75. parsedType = parse(expr, options);
  76. parsedType = prepareFrozenObject(parsedType, expr, options);
  77. if (cache) {
  78. cache.set(expr, parsedType);
  79. }
  80. return parsedType;
  81. }
  82. }
  83. function cachedStringify(parsedType, options) {
  84. const cache = getParsedTypeCache(options);
  85. let stringified;
  86. if (canReturnOriginalExpression(parsedType, options)) {
  87. return parsedType.typeExpression;
  88. } else if (cache) {
  89. stringified = cache.get(parsedType);
  90. if (!stringified) {
  91. stringified = stringify(parsedType, options);
  92. cache.set(parsedType, stringified);
  93. }
  94. return stringified;
  95. } else {
  96. return stringify(parsedType, options);
  97. }
  98. }
  99. function cachedDescribe(parsedType, options) {
  100. const cache = getDescriptionCache(options);
  101. let description = cache ? cache.get(parsedType) : null;
  102. if (description) {
  103. return description;
  104. } else {
  105. description = describe(parsedType, options);
  106. description = prepareFrozenObject(description, null, options);
  107. if (cache) {
  108. cache.set(parsedType, description);
  109. }
  110. return description;
  111. }
  112. }
  113. /* eslint-disable class-methods-use-this */
  114. class Catharsis {
  115. constructor() {
  116. this.Types = require('./lib/types');
  117. }
  118. parse(typeExpr, options = {}) {
  119. typeExpr = typeExpr.replace(/[\r\n]/g, '')
  120. .replace(/\s+/g, ' ')
  121. .trim();
  122. return cachedParse(typeExpr, options);
  123. }
  124. stringify(parsedType, options) {
  125. let result;
  126. options = options || {};
  127. result = cachedStringify(parsedType, options);
  128. if (options.validate) {
  129. this.parse(result, options);
  130. }
  131. return result;
  132. }
  133. describe(parsedType, options = {}) {
  134. return cachedDescribe(parsedType, options);
  135. }
  136. }
  137. /* eslint-enable class-methods-use-this */
  138. module.exports = new Catharsis();