PageRenderTime 104ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/node_modules/jsdoc/lib/jsdoc/tag/type.js

https://gitlab.com/josephm9/304CEM-CW
JavaScript | 311 lines | 185 code | 38 blank | 88 comment | 26 complexity | 2c1e3f2949957e43ad5de1f7c425478a MD5 | raw file
  1. /**
  2. * @module jsdoc/tag/type
  3. *
  4. * @author Michael Mathews <micmath@gmail.com>
  5. * @author Jeff Williams <jeffrey.l.williams@gmail.com>
  6. * @license Apache License 2.0 - See file 'LICENSE.md' in this project.
  7. */
  8. 'use strict';
  9. var catharsis = require('catharsis');
  10. var jsdoc = {
  11. name: require('jsdoc/name'),
  12. tag: {
  13. inline: require('jsdoc/tag/inline')
  14. },
  15. util: {
  16. cast: require('jsdoc/util/cast')
  17. }
  18. };
  19. var util = require('util');
  20. /**
  21. * Information about a type expression extracted from tag text.
  22. *
  23. * @typedef TypeExpressionInfo
  24. * @memberof module:jsdoc/tag/type
  25. * @property {string} expression - The type expression.
  26. * @property {string} text - The updated tag text.
  27. */
  28. /** @private */
  29. function unescapeBraces(text) {
  30. return text.replace(/\\\{/g, '{')
  31. .replace(/\\\}/g, '}');
  32. }
  33. /**
  34. * Extract a type expression from the tag text.
  35. *
  36. * @private
  37. * @param {string} string - The tag text.
  38. * @return {module:jsdoc/tag/type.TypeExpressionInfo} The type expression and updated tag text.
  39. */
  40. function extractTypeExpression(string) {
  41. var completeExpression;
  42. var count = 0;
  43. var position = 0;
  44. var expression = '';
  45. var startIndex = string.search(/\{[^@]/);
  46. var textStartIndex;
  47. if (startIndex !== -1) {
  48. // advance to the first character in the type expression
  49. position = textStartIndex = startIndex + 1;
  50. count++;
  51. while (position < string.length) {
  52. switch (string[position]) {
  53. case '\\':
  54. // backslash is an escape character, so skip the next character
  55. position++;
  56. break;
  57. case '{':
  58. count++;
  59. break;
  60. case '}':
  61. count--;
  62. break;
  63. default:
  64. // do nothing
  65. }
  66. if (count === 0) {
  67. completeExpression = string.slice(startIndex, position + 1);
  68. expression = string.slice(textStartIndex, position).trim();
  69. break;
  70. }
  71. position++;
  72. }
  73. }
  74. string = completeExpression ? string.replace(completeExpression, '') : string;
  75. return {
  76. expression: unescapeBraces(expression),
  77. newString: string.trim()
  78. };
  79. }
  80. /** @private */
  81. function getTagInfo(tagValue, canHaveName, canHaveType) {
  82. var name = '';
  83. var typeExpression = '';
  84. var text = tagValue;
  85. var expressionAndText;
  86. var nameAndDescription;
  87. var typeOverride;
  88. if (canHaveType) {
  89. expressionAndText = extractTypeExpression(text);
  90. typeExpression = expressionAndText.expression;
  91. text = expressionAndText.newString;
  92. }
  93. if (canHaveName) {
  94. nameAndDescription = jsdoc.name.splitName(text);
  95. name = nameAndDescription.name;
  96. text = nameAndDescription.description;
  97. }
  98. // an inline @type tag, like {@type Foo}, overrides the type expression
  99. if (canHaveType) {
  100. typeOverride = jsdoc.tag.inline.extractInlineTag(text, 'type');
  101. if (typeOverride.tags && typeOverride.tags[0]) {
  102. typeExpression = typeOverride.tags[0].text;
  103. }
  104. text = typeOverride.newString;
  105. }
  106. return {
  107. name: name,
  108. typeExpression: typeExpression,
  109. text: text
  110. };
  111. }
  112. /**
  113. * Information provided in a JSDoc tag.
  114. *
  115. * @typedef {Object} TagInfo
  116. * @memberof module:jsdoc/tag/type
  117. * @property {string} TagInfo.defaultvalue - The default value of the member.
  118. * @property {string} TagInfo.name - The name of the member (for example, `myParamName`).
  119. * @property {boolean} TagInfo.nullable - Indicates whether the member can be set to `null` or
  120. * `undefined`.
  121. * @property {boolean} TagInfo.optional - Indicates whether the member is optional.
  122. * @property {string} TagInfo.text - Descriptive text for the member (for example, `The user's email
  123. * address.`).
  124. * @property {Array.<string>} TagInfo.type - The type or types that the member can contain (for
  125. * example, `string` or `MyNamespace.MyClass`).
  126. * @property {string} TagInfo.typeExpression - The type expression that was parsed to identify the
  127. * types.
  128. * @property {boolean} TagInfo.variable - Indicates whether the number of members that are provided
  129. * can vary (for example, in a function that accepts any number of parameters).
  130. */
  131. // TODO: move to module:jsdoc/name?
  132. /**
  133. * Extract JSDoc-style type information from the name specified in the tag info, including the
  134. * member name; whether the member is optional; and the default value of the member.
  135. *
  136. * @private
  137. * @param {module:jsdoc/tag/type.TagInfo} tagInfo - Information contained in the tag.
  138. * @return {module:jsdoc/tag/type.TagInfo} Updated information from the tag.
  139. */
  140. function parseName(tagInfo) {
  141. // like '[foo]' or '[ foo ]' or '[foo=bar]' or '[ foo=bar ]' or '[ foo = bar ]'
  142. // or 'foo=bar' or 'foo = bar'
  143. if ( /^(\[)?\s*(.+?)\s*(\])?$/.test(tagInfo.name) ) {
  144. tagInfo.name = RegExp.$2;
  145. // were the "optional" brackets present?
  146. if (RegExp.$1 && RegExp.$3) {
  147. tagInfo.optional = true;
  148. }
  149. // like 'foo=bar' or 'foo = bar'
  150. if ( /^(.+?)\s*=\s*(.+)$/.test(tagInfo.name) ) {
  151. tagInfo.name = RegExp.$1;
  152. tagInfo.defaultvalue = jsdoc.util.cast.cast(RegExp.$2);
  153. }
  154. }
  155. return tagInfo;
  156. }
  157. /** @private */
  158. function getTypeStrings(parsedType, isOutermostType) {
  159. var applications;
  160. var typeString;
  161. var types = [];
  162. var TYPES = catharsis.Types;
  163. switch (parsedType.type) {
  164. case TYPES.AllLiteral:
  165. types.push('*');
  166. break;
  167. case TYPES.FunctionType:
  168. types.push('function');
  169. break;
  170. case TYPES.NameExpression:
  171. types.push(parsedType.name);
  172. break;
  173. case TYPES.NullLiteral:
  174. types.push('null');
  175. break;
  176. case TYPES.RecordType:
  177. types.push('Object');
  178. break;
  179. case TYPES.TypeApplication:
  180. // if this is the outermost type, we strip the modifiers; otherwise, we keep them
  181. if (isOutermostType) {
  182. applications = parsedType.applications.map(function(application) {
  183. return catharsis.stringify(application);
  184. }).join(', ');
  185. typeString = util.format( '%s.<%s>', getTypeStrings(parsedType.expression),
  186. applications );
  187. types.push(typeString);
  188. }
  189. else {
  190. types.push( catharsis.stringify(parsedType) );
  191. }
  192. break;
  193. case TYPES.TypeUnion:
  194. parsedType.elements.forEach(function(element) {
  195. types = types.concat( getTypeStrings(element) );
  196. });
  197. break;
  198. case TYPES.UndefinedLiteral:
  199. types.push('undefined');
  200. break;
  201. case TYPES.UnknownLiteral:
  202. types.push('?');
  203. break;
  204. default:
  205. // this shouldn't happen
  206. throw new Error( util.format('unrecognized type %s in parsed type: %j', parsedType.type,
  207. parsedType) );
  208. }
  209. return types;
  210. }
  211. /**
  212. * Extract JSDoc-style and Closure Compiler-style type information from the type expression
  213. * specified in the tag info.
  214. *
  215. * @private
  216. * @param {module:jsdoc/tag/type.TagInfo} tagInfo - Information contained in the tag.
  217. * @return {module:jsdoc/tag/type.TagInfo} Updated information from the tag.
  218. */
  219. function parseTypeExpression(tagInfo) {
  220. var errorMessage;
  221. var parsedType;
  222. // don't try to parse empty type expressions
  223. if (!tagInfo.typeExpression) {
  224. return tagInfo;
  225. }
  226. try {
  227. parsedType = catharsis.parse(tagInfo.typeExpression, {jsdoc: true});
  228. }
  229. catch (e) {
  230. // always re-throw so the caller has a chance to report which file was bad
  231. throw new Error( util.format('Invalid type expression "%s": %s', tagInfo.typeExpression,
  232. e.message) );
  233. }
  234. tagInfo.type = tagInfo.type.concat( getTypeStrings(parsedType, true) );
  235. tagInfo.parsedType = parsedType;
  236. // Catharsis and JSDoc use the same names for 'optional' and 'nullable'...
  237. ['optional', 'nullable'].forEach(function(key) {
  238. if (parsedType[key] !== null && parsedType[key] !== undefined) {
  239. tagInfo[key] = parsedType[key];
  240. }
  241. });
  242. // ...but not 'variable'.
  243. if (parsedType.repeatable !== null && parsedType.repeatable !== undefined) {
  244. tagInfo.variable = parsedType.repeatable;
  245. }
  246. return tagInfo;
  247. }
  248. // TODO: allow users to add/remove type parsers (perhaps via plugins)
  249. var typeParsers = [parseName, parseTypeExpression];
  250. /**
  251. * Parse the value of a JSDoc tag.
  252. *
  253. * @param {string} tagValue - The value of the tag. For example, the tag `@param {string} name` has
  254. * a value of `{string} name`.
  255. * @param {boolean} canHaveName - Indicates whether the value can include a symbol name.
  256. * @param {boolean} canHaveType - Indicates whether the value can include a type expression that
  257. * describes the symbol.
  258. * @return {module:jsdoc/tag/type.TagInfo} Information obtained from the tag.
  259. * @throws {Error} Thrown if a type expression cannot be parsed.
  260. */
  261. exports.parse = function(tagValue, canHaveName, canHaveType) {
  262. if (typeof tagValue !== 'string') { tagValue = ''; }
  263. var tagInfo = getTagInfo(tagValue, canHaveName, canHaveType);
  264. tagInfo.type = tagInfo.type || [];
  265. typeParsers.forEach(function(parser) {
  266. tagInfo = parser.call(this, tagInfo);
  267. });
  268. // if we wanted a type, but the parsers didn't add any type names, use the type expression
  269. if (canHaveType && !tagInfo.type.length && tagInfo.typeExpression) {
  270. tagInfo.type = [tagInfo.typeExpression];
  271. }
  272. return tagInfo;
  273. };