umberto 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/.eslintrc.js +2 -2
  2. package/LICENSE.md +1 -1
  3. package/hexo-shim.js +1 -1
  4. package/package.json +2 -1
  5. package/scripts/filter/after-post-render/basepath-replacer.js +1 -1
  6. package/scripts/filter/after-post-render/fix-code-samples.js +1 -1
  7. package/scripts/filter/after-post-render/gather-guides.js +1 -1
  8. package/scripts/filter/after-post-render/img-linker.js +1 -1
  9. package/scripts/filter/after-post-render/import-guide.js +1 -1
  10. package/scripts/filter/after-post-render/insert-error-codes.js +1 -1
  11. package/scripts/filter/after-post-render/linker.js +1 -1
  12. package/scripts/filter/after-post-render/parseicontag.js +1 -1
  13. package/scripts/filter/after-post-render/process-variables.js +1 -1
  14. package/scripts/filter/after-post-render/snippets.js +1 -1
  15. package/scripts/filter/after-post-render/time-end.js +1 -1
  16. package/scripts/filter/after-post-render/time-start.js +1 -1
  17. package/scripts/filter/after-post-render/validate-after-render.js +1 -1
  18. package/scripts/filter/before-post-render/add-project-info-to-page.js +1 -1
  19. package/scripts/filter/before-post-render/add-repo-url.js +1 -1
  20. package/scripts/filter/before-post-render/change-output-path.js +1 -1
  21. package/scripts/filter/before-post-render/escape-special-characters.js +1 -1
  22. package/scripts/filter/before-post-render/execute-and-insert-function-results.js +1 -1
  23. package/scripts/filter/before-post-render/infobox.js +1 -1
  24. package/scripts/filter/before-post-render/insertchangelog.js +1 -1
  25. package/scripts/filter/before-post-render/replace-macros.js +1 -1
  26. package/scripts/filter/before-post-render/set-layout.js +1 -1
  27. package/scripts/filter/before-post-render/validate-page-data.js +1 -1
  28. package/scripts/helper/find-main-category-page.js +1 -1
  29. package/scripts/helper/get-pages-for-nav-tree.js +1 -1
  30. package/scripts/helper/mark-empty-categories.js +1 -1
  31. package/scripts/helper/normalize-badges.js +1 -1
  32. package/scripts/helper/u-split-to-title-and-content.js +1 -1
  33. package/scripts/helper/u-toc.js +1 -1
  34. package/scripts/utils/execute-and-insert-function-results.js +1 -1
  35. package/scripts/utils/findmaincategorypage.js +1 -1
  36. package/scripts/utils/getreportissuewidgeturl.js +1 -1
  37. package/scripts/utils/insertchangelog.js +1 -1
  38. package/scripts/utils/normalizebadges.js +1 -1
  39. package/scripts/utils/parseicontag.js +1 -1
  40. package/scripts/utils/parselinks.js +61 -41
  41. package/scripts/utils/shoulddisplaynewindicator.js +1 -1
  42. package/scripts/utils/transforminfobox.js +1 -1
  43. package/scripts-dev/postinstall.js +1 -1
  44. package/src/api-builder/api-builder.js +28 -8
  45. package/src/api-builder/classes/description-parser.js +79 -63
  46. package/src/api-builder/classes/doc-data-factory.js +58 -5
  47. package/src/api-builder/classes/file-name-manager.js +1 -1
  48. package/src/api-builder/classes/html-file.js +1 -1
  49. package/src/api-builder/classes/navigation-tree.js +1 -1
  50. package/src/api-builder/classes/tree-node.js +1 -1
  51. package/src/api-builder/utils/findtargetdoclet.js +84 -0
  52. package/src/api-builder/utils/utils.js +41 -0
  53. package/src/data-converter/converters/jsdoc2umberto.js +8 -1
  54. package/src/data-converter/converters/jsduck2umberto.js +1 -1
  55. package/src/data-converter/converters/typedoc/abstractparser.js +364 -0
  56. package/src/data-converter/converters/typedoc/accessorparser.js +64 -0
  57. package/src/data-converter/converters/typedoc/classparser.js +49 -0
  58. package/src/data-converter/converters/typedoc/constantparser.js +45 -0
  59. package/src/data-converter/converters/typedoc/constructorparser.js +34 -0
  60. package/src/data-converter/converters/typedoc/errorparser.js +44 -0
  61. package/src/data-converter/converters/typedoc/eventparser.js +57 -0
  62. package/src/data-converter/converters/typedoc/functionparser.js +61 -0
  63. package/src/data-converter/converters/typedoc/interfaceparser.js +46 -0
  64. package/src/data-converter/converters/typedoc/methodparser.js +63 -0
  65. package/src/data-converter/converters/typedoc/moduleparser.js +31 -0
  66. package/src/data-converter/converters/typedoc/propertyparser.js +74 -0
  67. package/src/data-converter/converters/typedoc/typedoc.ts +297 -0
  68. package/src/data-converter/converters/typedoc/typedocconverter.js +742 -0
  69. package/src/data-converter/converters/typedoc/typeparser.js +71 -0
  70. package/src/data-converter/converters/typedoc2umberto.js +52 -0
  71. package/src/data-converter/data-collection.js +1 -1
  72. package/src/data-converter/data-provider.js +4 -2
  73. package/src/data-converter/doclet-collection.js +14 -2
  74. package/src/data-converter/middlewares/relation-fixer.js +1 -1
  75. package/src/helpers/capitalize.js +1 -1
  76. package/src/helpers/copy-file.js +1 -1
  77. package/src/helpers/create-filtering-data-attribs.js +1 -1
  78. package/src/helpers/doc-formats.js +3 -2
  79. package/src/helpers/escape-longname.js +1 -1
  80. package/src/helpers/extract-longname.js +1 -1
  81. package/src/helpers/get-api-infobox-tooltip.js +1 -1
  82. package/src/helpers/get-docsearch-config.js +1 -1
  83. package/src/helpers/get-file-patterns-to-process.js +1 -1
  84. package/src/helpers/get-short-module-path.js +1 -1
  85. package/src/helpers/github-url.js +1 -1
  86. package/src/helpers/glob-to-regexp.js +1 -1
  87. package/src/helpers/html-safe.js +1 -1
  88. package/src/helpers/is-non-empty-array.js +1 -1
  89. package/src/helpers/snippets.js +1 -1
  90. package/src/helpers/split-longname.js +101 -8
  91. package/src/hexo/filter/extend-config.js +1 -1
  92. package/src/hexo/filter/project-locals.js +1 -1
  93. package/src/hexo/get-repo-urls.js +1 -1
  94. package/src/hexo/helper/get-page-group.js +1 -1
  95. package/src/hexo/project-globals.js +1 -1
  96. package/src/hexo-manager.js +1 -1
  97. package/src/index.js +1 -1
  98. package/src/sdk-builder/get-sdk-sources.js +1 -1
  99. package/src/sdk-builder/sdk-builder.js +1 -1
  100. package/src/tasks/build-api-docs.js +4 -4
  101. package/src/tasks/build-documentation.js +3 -3
  102. package/src/tasks/build-sdk.js +1 -1
  103. package/src/tasks/build-snippets.js +1 -1
  104. package/src/tasks/cache-files.js +1 -1
  105. package/src/tasks/compile-sass.js +1 -1
  106. package/src/tasks/copy-assets.js +1 -1
  107. package/src/tasks/copy-files.js +1 -1
  108. package/src/tasks/copy-project-docs.js +1 -1
  109. package/src/tasks/copy-project-icons.js +1 -1
  110. package/src/tasks/create-redirect-page.js +1 -1
  111. package/src/tasks/create-sitemap.js +1 -1
  112. package/src/tasks/create-sym-links.js +1 -1
  113. package/src/tasks/execute-hooks.js +1 -1
  114. package/src/tasks/get-extra-files.js +1 -1
  115. package/src/tasks/get-hexo-config.js +1 -1
  116. package/src/tasks/get-main-config.js +1 -1
  117. package/src/tasks/get-project-config.js +3 -3
  118. package/src/tasks/macro-replacer.js +1 -1
  119. package/src/tasks/overwrite-api-guides.js +1 -1
  120. package/src/tasks/read-doc-sources.js +1 -1
  121. package/src/tasks/run-webpack.js +1 -1
  122. package/src/tasks/validate-html-w3c.js +1 -1
  123. package/src/tasks/validate-links.js +1 -1
  124. package/src/tasks/watcher.js +1 -1
  125. package/src/tasks/write-html-files.js +1 -1
  126. package/src/template/template-collection.js +1 -1
  127. package/themes/umberto/layout/_api-docs/_mixin/_api-see-source.pug +2 -1
  128. package/themes/umberto/layout/_api-docs/_mixin/_class-item.pug +0 -1
  129. package/themes/umberto/layout/_api-docs/_mixin/_dev-names.pug +9 -3
  130. package/themes/umberto/layout/_api-docs/_mixin/_fires-item.pug +2 -2
  131. package/themes/umberto/layout/_api-docs/_mixin/_hierarchy-item.pug +2 -2
  132. package/themes/umberto/layout/_api-docs/_mixin/_link-or-text.pug +40 -41
  133. package/themes/umberto/layout/_api-docs/_mixin/_method.pug +8 -2
  134. package/themes/umberto/layout/_api-docs/_mixin/_property.pug +1 -1
  135. package/themes/umberto/layout/_api-docs/_mixin/_related.pug +1 -1
  136. package/themes/umberto/layout/_api-docs/_mixin/_sidebox-list-item.pug +1 -1
  137. package/themes/umberto/layout/_api-docs/_mixin/_type.pug +175 -9
  138. package/themes/umberto/layout/_api-docs/_mixin/type-parameter.pug +18 -0
  139. package/themes/umberto/layout/_api-docs/_partial/filter.pug +16 -4
  140. package/themes/umberto/layout/_api-docs/_partial/type-parameters.pug +26 -0
  141. package/themes/umberto/layout/_api-docs/api-base.pug +2 -0
  142. package/themes/umberto/src/css/_api-content.scss +1 -1
  143. package/themes/umberto/src/css/_api-info-box.scss +1 -1
  144. package/themes/umberto/src/css/_api-props-filter.scss +1 -1
  145. package/themes/umberto/src/css/_api-see-source.scss +1 -1
  146. package/themes/umberto/src/css/_api-subheader.scss +1 -1
  147. package/themes/umberto/src/css/_api-tree.scss +1 -1
  148. package/themes/umberto/src/css/_badge.scss +1 -1
  149. package/themes/umberto/src/css/_collapsinglist.scss +1 -1
  150. package/themes/umberto/src/css/_content.scss +3 -2
  151. package/themes/umberto/src/css/_docsearch.scss +1 -1
  152. package/themes/umberto/src/css/_dropdown.scss +1 -1
  153. package/themes/umberto/src/css/_editor-icon.scss +1 -1
  154. package/themes/umberto/src/css/_feedback-widget.scss +1 -1
  155. package/themes/umberto/src/css/_footer.scss +1 -1
  156. package/themes/umberto/src/css/_formatted.scss +1 -1
  157. package/themes/umberto/src/css/_guide-content.scss +1 -1
  158. package/themes/umberto/src/css/_guide-sdk-tree.scss +1 -1
  159. package/themes/umberto/src/css/_loading-spinner.scss +1 -1
  160. package/themes/umberto/src/css/_main.scss +1 -1
  161. package/themes/umberto/src/css/_navigation.scss +1 -1
  162. package/themes/umberto/src/css/_notice.scss +1 -1
  163. package/themes/umberto/src/css/_print.scss +1 -1
  164. package/themes/umberto/src/css/_prism.scss +1 -1
  165. package/themes/umberto/src/css/_rwd-breadcrumbs.scss +1 -1
  166. package/themes/umberto/src/css/_rwd-menu.scss +1 -1
  167. package/themes/umberto/src/css/_secondary-navigation.scss +1 -1
  168. package/themes/umberto/src/css/_theme-dark.scss +1 -1
  169. package/themes/umberto/src/css/_toggler.scss +1 -1
  170. package/themes/umberto/src/css/_top.scss +1 -1
  171. package/themes/umberto/src/css/_tree.scss +7 -5
  172. package/themes/umberto/src/css/_warning-banner.scss +1 -1
  173. package/themes/umberto/src/css/components/_lightbox.scss +1 -1
  174. package/themes/umberto/src/css/helpers/_color.scss +1 -1
  175. package/themes/umberto/src/css/helpers/_font.scss +1 -1
  176. package/themes/umberto/src/css/helpers/_globals.scss +1 -1
  177. package/themes/umberto/src/css/helpers/_reset.scss +1 -1
  178. package/themes/umberto/src/css/helpers/_spacing.scss +1 -1
  179. package/themes/umberto/src/css/pages/_sdk.scss +1 -1
  180. package/themes/umberto/src/css/styles.scss +1 -1
  181. package/themes/umberto/src/js/_anchors.js +1 -1
  182. package/themes/umberto/src/js/_apisearch.js +1 -1
  183. package/themes/umberto/src/js/_apitree.js +1 -1
  184. package/themes/umberto/src/js/_collapsables.js +1 -1
  185. package/themes/umberto/src/js/_copymobileapinavigation.js +1 -1
  186. package/themes/umberto/src/js/_devnames.js +1 -1
  187. package/themes/umberto/src/js/_dropdowns.js +1 -1
  188. package/themes/umberto/src/js/_filtering.js +2 -1
  189. package/themes/umberto/src/js/_imageModal.js +1 -1
  190. package/themes/umberto/src/js/_pagenavigation.js +1 -1
  191. package/themes/umberto/src/js/_prism.js +1 -1
  192. package/themes/umberto/src/js/_rwdmenu.js +1 -1
  193. package/themes/umberto/src/js/_samplecode.js +1 -1
  194. package/themes/umberto/src/js/_sidenavigation.js +1 -1
  195. package/themes/umberto/src/js/_toc.js +1 -1
  196. package/themes/umberto/src/js/_tooltips.js +1 -1
  197. package/themes/umberto/src/js/_warningbanner.js +1 -1
  198. package/themes/umberto/src/js/app.js +1 -1
@@ -0,0 +1,742 @@
1
+ /**
2
+ * @license Copyright (c) 2017-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const MarkdownIt = require( 'markdown-it' );
9
+ const markdown = new MarkdownIt( {
10
+ html: true
11
+ } );
12
+
13
+ const ISSUE_URL = 'https://github.com/cksource/umberto/issues/new?assignees=&labels=type:feature,squad:devops';
14
+
15
+ class TypedocConverter {
16
+ /**
17
+ * @param {Array.<AbstractParser>} parsers
18
+ */
19
+ constructor( parsers ) {
20
+ /**
21
+ * @protected
22
+ * @member {Array.<Object>}
23
+ */
24
+ this._doclets = [];
25
+
26
+ /**
27
+ * A map translating an identifier from Typedoc to a JsDoc doclet.
28
+ * It may contain an identifier from Typedoc that points to another identifier (a reference linked to another reference).
29
+ *
30
+ * @protected
31
+ * @member {Map.<Number, String|Number>}
32
+ */
33
+ this._moduleMap = new Map();
34
+
35
+ /**
36
+ * @protected
37
+ * @member {Array.<AbstractParser>}
38
+ */
39
+ this._parsers = parsers.map( Class => new Class() );
40
+
41
+ /**
42
+ * @protected
43
+ * @member {TypeConverter}
44
+ */
45
+ this._typeConverter = new TypeConverter( this._moduleMap );
46
+ }
47
+
48
+ /**
49
+ * @param {Array.<TypedocCommentItem>} content
50
+ * @returns {String}
51
+ */
52
+ static convertComment( content ) {
53
+ const value = content.reduce( ( value, { kind, text, tag } ) => {
54
+ if ( kind === 'inline-tag' ) {
55
+ text = `{${ tag } ${ text }}`;
56
+ }
57
+
58
+ return value + text;
59
+ }, '' );
60
+
61
+ return markdown.render( value ).trim();
62
+ }
63
+
64
+ /**
65
+ * @param {Object} projectReflection
66
+ * @param {Array.<TypedocReflection>} projectReflection.children
67
+ * @returns {Array.<Object>}
68
+ */
69
+ convertToJsDoc( projectReflection ) {
70
+ // Convert the entire project to JsDoc doclets.
71
+ for ( const child of projectReflection.children ) {
72
+ this._convertChild( child );
73
+ }
74
+
75
+ // Then, when all doclets are specified, let's align types.
76
+ for ( const doclet of this._doclets ) {
77
+ // Post-processing is possible only if a doclet contains its original signature.
78
+ if ( !doclet._signature ) {
79
+ continue;
80
+ }
81
+
82
+ // TODO: Could we get a deep list of all inheritance/derived?
83
+ // Map parent references of classes and interfaces...
84
+ if ( 'augmentsNested' in doclet ) {
85
+ const extendedTypes = doclet._signature.extendedTypes || [];
86
+
87
+ doclet.augmentsNested = extendedTypes.flatMap( getHierarchyCallback( this._typeConverter ) );
88
+ }
89
+
90
+ // ...but also, the derived instances...
91
+ if ( 'descendants' in doclet ) {
92
+ const extendedBy = doclet._signature.extendedBy || [];
93
+
94
+ doclet.descendants = extendedBy.flatMap( getHierarchyCallback( this._typeConverter ) );
95
+ }
96
+
97
+ // ...and implemented interfaces.
98
+ if ( 'implementsNested' in doclet ) {
99
+ const implementedTypes = doclet._signature.implementedTypes || [];
100
+
101
+ doclet.implementsNested = implementedTypes.flatMap( getHierarchyCallback( this._typeConverter ) );
102
+ }
103
+
104
+ // Unify the `_signature` tuple, so all doclets use the same algorithm.
105
+ if ( !Array.isArray( doclet._signature ) ) {
106
+ doclet._signature = [ doclet._signature, doclet._signature ];
107
+ }
108
+
109
+ const [ getSignature, setSignature ] = doclet._signature;
110
+ const returnTypes = this._convertReturnTypes( doclet, getSignature );
111
+
112
+ if ( returnTypes ) {
113
+ if ( 'returns' in doclet ) {
114
+ doclet.returns = returnTypes;
115
+ } else if ( 'type' in doclet ) {
116
+ doclet.type = returnTypes[ 0 ].type;
117
+ }
118
+ }
119
+
120
+ if ( 'params' in doclet ) {
121
+ doclet.params = this._convertParameters( doclet, setSignature );
122
+ }
123
+
124
+ if ( 'properties' in doclet ) {
125
+ doclet.properties = this._convertProperties( doclet, setSignature );
126
+ }
127
+
128
+ if ( 'typeParameters' in doclet ) {
129
+ // For all doclets, it does not matter whether we process a get or a set signature.
130
+ // Hence, let's use the first one.
131
+ doclet.typeParameters = this._typeConverter.convertTypeParameters(
132
+ // Methods/functions || classes/interfaces/typedefs.
133
+ getSignature.typeParameter || getSignature.typeParameters
134
+ );
135
+ }
136
+
137
+ // In the end, remove the original signature as it isn't needed anymore.
138
+ delete doclet._signature;
139
+ }
140
+
141
+ return this._doclets;
142
+ }
143
+
144
+ /**
145
+ * @protected
146
+ * @param {TypedocReflection} reflection
147
+ * @param {String|null} [parentName=null]
148
+ */
149
+ _convertChild( reflection, parentName = null ) {
150
+ const parser = this._parsers.find( parser => parser.canParse( reflection ) );
151
+
152
+ let doclets;
153
+
154
+ if ( parser ) {
155
+ doclets = parser.parse( reflection, parentName );
156
+ }
157
+
158
+ if ( doclets ) {
159
+ doclets = Array.isArray( doclets ) ? doclets : [ doclets ];
160
+
161
+ for ( const item of doclets ) {
162
+ this._doclets.push( item );
163
+ this._moduleMap.set( reflection.id, item.longname );
164
+ }
165
+ }
166
+
167
+ if ( reflection.kindString === 'Reference' ) {
168
+ if ( reflection.id !== reflection.target ) {
169
+ this._moduleMap.set( reflection.id, reflection.target );
170
+ } else {
171
+ console.warn( `Reference reflection "${ reflection.name }" points to itself, so it has been skipped.` );
172
+ }
173
+ }
174
+
175
+ if ( reflection.children ) {
176
+ if ( doclets ) {
177
+ parentName = doclets[ 0 ].longname;
178
+ } else {
179
+ parentName = '';
180
+ }
181
+
182
+ reflection.children.forEach( subItem => {
183
+ this._convertChild( subItem, parentName );
184
+ } );
185
+ }
186
+ }
187
+
188
+ /**
189
+ * @protected
190
+ * @param {Object} doclet
191
+ * @param {TypedocCallSignature} signature
192
+ * @returns {Array|null}
193
+ */
194
+ _convertParameters( doclet, signature ) {
195
+ if ( !signature.parameters ) {
196
+ return null;
197
+ }
198
+
199
+ return signature.parameters
200
+ .map( item => {
201
+ const response = this._convertTypeToJsDoc( item );
202
+
203
+ response.name = item.name;
204
+
205
+ if ( item.comment && item.comment.summary ) {
206
+ response.description = TypedocConverter.convertComment( item.comment.summary );
207
+ }
208
+
209
+ if ( item.flags.isOptional ) {
210
+ response.optional = true;
211
+ }
212
+
213
+ if ( item.defaultValue ) {
214
+ response.defaultvalue = item.defaultValue;
215
+ }
216
+
217
+ return response;
218
+ } );
219
+ }
220
+
221
+ /**
222
+ * @protected
223
+ * @param {Object} doclet
224
+ * @param {TypedocCallSignature} signature
225
+ * @returns {Array|null}
226
+ */
227
+ _convertProperties( doclet, signature ) {
228
+ const types = signature.type.type === 'union' ? signature.type.types : [ signature.type ];
229
+ const type = types.find( type => type.declaration && type.declaration.children );
230
+
231
+ if ( !type ) {
232
+ return null;
233
+ }
234
+
235
+ return type.declaration.children.map( child => {
236
+ const response = this._convertTypeToJsDoc( child );
237
+
238
+ response.name = child.name;
239
+
240
+ if ( signature.comment && signature.comment.blockTags ) {
241
+ const atProperty = signature.comment.blockTags.find( blockTag => {
242
+ return blockTag.tag === '@property' && blockTag.name === child.name;
243
+ } );
244
+
245
+ if ( atProperty ) {
246
+ response.description = TypedocConverter.convertComment( atProperty.content );
247
+ }
248
+ } else if ( child.comment && child.comment.summary ) {
249
+ response.description = TypedocConverter.convertComment( child.comment.summary );
250
+ }
251
+
252
+ if ( child.flags.isOptional ) {
253
+ response.optional = true;
254
+ }
255
+
256
+ if ( child.defaultValue ) {
257
+ response.defaultvalue = child.defaultValue;
258
+ }
259
+
260
+ return response;
261
+ } );
262
+ }
263
+
264
+ /**
265
+ * @protected
266
+ * @param {Object} doclet
267
+ * @param {TypedocReflectionType} signature
268
+ * @returns {Array|null}
269
+ */
270
+ _convertReturnTypes( doclet, signature ) {
271
+ if ( !signature.type ) {
272
+ return null;
273
+ }
274
+
275
+ const response = this._convertTypeToJsDoc( signature );
276
+
277
+ const comment = signature.comment || {};
278
+
279
+ if ( !comment.blockTags ) {
280
+ comment.blockTags = [];
281
+ }
282
+
283
+ const atReturns = comment.blockTags.find( blockTag => blockTag.tag === '@returns' );
284
+
285
+ if ( atReturns ) {
286
+ response.description = TypedocConverter.convertComment( atReturns.content );
287
+ }
288
+
289
+ return [ response ];
290
+ }
291
+
292
+ /**
293
+ * @protected
294
+ * @param {TypedocReflectionType} parameterReflection
295
+ * @returns {Object}
296
+ */
297
+ _convertTypeToJsDoc( parameterReflection ) {
298
+ let namesOrNames = this._typeConverter.convert( parameterReflection.type );
299
+
300
+ // Union.
301
+ if ( namesOrNames instanceof Set ) {
302
+ return {
303
+ type: {
304
+ names: [ ...namesOrNames ]
305
+ }
306
+ };
307
+ }
308
+ // Missing type. Use a name without linking it anywhere.
309
+ else if ( !namesOrNames ) {
310
+ namesOrNames = parameterReflection.type.name;
311
+ }
312
+
313
+ return {
314
+ type: {
315
+ // Convert items into an array to avoid having mixed structures in a view.
316
+ names: [ namesOrNames ]
317
+ }
318
+ };
319
+ }
320
+ }
321
+
322
+ /**
323
+ * A helper class for converting complex Typedoc reflections to less complicated JSDoc objects.
324
+ *
325
+ * @private
326
+ */
327
+ class TypeConverter {
328
+ /**
329
+ * @param {Map} moduleMap
330
+ */
331
+ constructor( moduleMap ) {
332
+ /**
333
+ * A map translating an identifier from Typedoc to a JsDoc doclet.
334
+ *
335
+ * @protected
336
+ * @member {Map.<Number, String>}
337
+ */
338
+ this._moduleMap = moduleMap;
339
+ }
340
+
341
+ /**
342
+ * @param {TypedocReflectionType} type
343
+ * @returns {Object|Array.<String>|String|null}
344
+ */
345
+ convert( type ) {
346
+ // Sometimes the `type` property of the reflection does not contain the actual type of the reflection, but it could hold another
347
+ // kind of information, like the `array` or `optional` values. In that case, the actual type is stored in the `elementType`
348
+ // property. To handle this condition comprehensively, it is enough to always try to take the actual reflection from the
349
+ // `elementType` property (if it exists).
350
+ const typeReflection = type.elementType || type;
351
+ const convertedType = this._convertSingleType( typeReflection );
352
+
353
+ if ( type.type === 'array' ) {
354
+ if ( !convertedType ) {
355
+ return 'Array';
356
+ }
357
+
358
+ if ( convertedType instanceof Set ) {
359
+ return [ ...convertedType ];
360
+ }
361
+
362
+ return [ convertedType ];
363
+ }
364
+
365
+ return convertedType;
366
+ }
367
+
368
+ /**
369
+ * Converts type parameters or class, interface, method or function to a format that can be rendered on API pages.
370
+ *
371
+ * The returned object contains a `value` property which must be an array.
372
+ * Thanks to that, it can be pass through the `type()` mixin.
373
+ *
374
+ * See: `themes/umberto/layout/_api-docs/_mixin/_type.pug.
375
+ *
376
+ * @param {Array.<TypedocReflectionTypeParameter>|null} parameters
377
+ * @returns {Array.<Object>|null}
378
+ */
379
+ convertTypeParameters( parameters ) {
380
+ if ( !Array.isArray( parameters ) ) {
381
+ return null;
382
+ }
383
+
384
+ return parameters.map( parameter => {
385
+ let description;
386
+
387
+ if ( parameter.comment && parameter.comment.summary ) {
388
+ description = TypedocConverter.convertComment( parameter.comment.summary );
389
+ }
390
+
391
+ // A type without a definition. Just a name.
392
+ if ( !parameter.type ) {
393
+ if ( parameter.default ) {
394
+ const value = this.convert( parameter.default );
395
+
396
+ return {
397
+ name: parameter.name,
398
+ value: [ value ],
399
+ description
400
+ };
401
+ }
402
+
403
+ return {
404
+ name: parameter.name,
405
+ description
406
+ };
407
+ }
408
+
409
+ // A type parameter describing an argument of a function or a returned type or a reference.
410
+ if ( typeof parameter.type === 'string' ) {
411
+ return {
412
+ name: this.convert( parameter ),
413
+ description
414
+ };
415
+ }
416
+
417
+ // A simple type....
418
+ if ( parameter.type.type === 'intrinsic' ) {
419
+ let value = parameter.type.name;
420
+
421
+ // ...with a default value.
422
+ if ( parameter.default ) {
423
+ value += ` = ${ this.convertIntrinsicType( parameter.default ) }`;
424
+ }
425
+
426
+ return {
427
+ name: parameter.name,
428
+ isExtending: true,
429
+ value: [ value ],
430
+ description
431
+ };
432
+ }
433
+
434
+ const type = this.convert( parameter.type );
435
+
436
+ // To avoid complex type parameters (they are available in typings provided by TypeScript),
437
+ // let's ignore unions.
438
+ if ( type instanceof Set ) {
439
+ return {
440
+ name: parameter.name,
441
+ description
442
+ };
443
+ }
444
+
445
+ return {
446
+ name: parameter.name,
447
+ isExtending: true,
448
+ value: [ type ],
449
+ description
450
+ };
451
+ } );
452
+ }
453
+
454
+ /**
455
+ * @param {TypedocTypeDetails} reference
456
+ * @returns {String}
457
+ */
458
+ convertReference( reference ) {
459
+ let moduleName;
460
+ let referenceId = reference.id;
461
+
462
+ if ( !referenceId ) {
463
+ return reference.name;
464
+ }
465
+
466
+ // The reference identifier in a module map may point to another reference identifier.
467
+ // The loop is needed to get the target (final) module name.
468
+ do {
469
+ moduleName = this._moduleMap.get( referenceId );
470
+ referenceId = typeof moduleName === 'number' ? moduleName : null;
471
+ } while ( referenceId );
472
+
473
+ if ( moduleName ) {
474
+ return moduleName;
475
+ }
476
+
477
+ return reference.name;
478
+ }
479
+
480
+ /**
481
+ * @param {TypedocTypeDetails} parameter
482
+ * @returns {Object}
483
+ */
484
+ convertIndexedAccess( parameter ) {
485
+ const convertIndex = reflection => {
486
+ if ( reflection.type === 'reference' ) {
487
+ return this.convertReference( reflection );
488
+ }
489
+
490
+ return this.convertIntrinsicType( reflection );
491
+ };
492
+
493
+ return {
494
+ type: 'generic',
495
+ name: this._convertSingleType( parameter.objectType ),
496
+ typeParameter: convertIndex( parameter.indexType )
497
+ };
498
+ }
499
+
500
+ /**
501
+ * @param {TypedocTypeDetails} typeReflection
502
+ * @returns {String|null|*}
503
+ */
504
+ convertIntrinsicType( typeReflection ) {
505
+ if ( typeReflection.type === 'literal' ) {
506
+ if ( typeof typeReflection.value === 'string' ) {
507
+ return `'${ typeReflection.value }'`;
508
+ }
509
+
510
+ return String( typeReflection.value );
511
+ }
512
+
513
+ return typeReflection.name;
514
+ }
515
+
516
+ /**
517
+ * @param {Array.<TypedocReflectionType>} types
518
+ * @returns {Set}
519
+ */
520
+ convertUnion( types ) {
521
+ const values = types.map( singleType => this.convert( singleType ) )
522
+ .filter( Boolean );
523
+
524
+ // Keep unique values only.
525
+ return new Set( values );
526
+ }
527
+
528
+ /**
529
+ * Converts a single type reflection produced by Typedoc to a simpler JSDoc-structure that Umberto handles out-of-the-box.
530
+ *
531
+ * It returns:
532
+ * * the `null` value when converting the `void` type,
533
+ * * a `module:...` string if a type is a reference to other module,
534
+ * * a string if given type is a template,
535
+ * * an instance of the `Set` class if given type is an union,
536
+ * * an object when converting predicates or inline types.
537
+ *
538
+ * When detecting an unsupported type, it prints a warning message on a console suggesting
539
+ * to report an issue and providing support for the missing structure.
540
+ *
541
+ * @protected
542
+ * @param {TypedocTypeDetails} typeReflection
543
+ * @returns {Object|String|Set|null}
544
+ */
545
+ _convertSingleType( typeReflection ) {
546
+ // See the `TypedocTypeDetails` type in the `typedoc.ts` file for checking the supported types.
547
+ //
548
+ // Results of this function is processed by Pug.
549
+ // See: `themes/umberto/layout/_api-docs/_mixin/_type.pug`.
550
+ //
551
+ // When introducing support for more TypeScript constructions,
552
+ // consider introducing a new structure that contains an opening symbol,
553
+ // a separator, and a closing symbol to avoid `instanceof` checks in the view.
554
+ //
555
+ // Examples:
556
+ //
557
+ // * Array: `Array<Type1 | Type2>`
558
+ // * Union: `Type1 | Type2 | Type3`
559
+ // * Intersection: `... & ...`
560
+ // * Tuple: `[ name1: Type1, name2: Type2 ]`
561
+ //
562
+ // TODO: When working on #1052, consider rewriting the function to always return an object
563
+ // containing type arguments. Then, `TypedocConverter#_convertTypeToJsDoc()` should not process
564
+ // them manually.
565
+ if ( typeReflection.type === 'reference' ) {
566
+ const reference = this.convertReference( typeReflection );
567
+
568
+ if ( !typeReflection.typeArguments ) {
569
+ return reference;
570
+ }
571
+
572
+ // An object representing a type containing type arguments.
573
+ return {
574
+ type: 'generic',
575
+ name: reference,
576
+ typeParameters: this.convertTypeParameters( typeReflection.typeArguments )
577
+ };
578
+ }
579
+
580
+ if ( typeReflection.type === 'indexedAccess' ) {
581
+ return this.convertIndexedAccess( typeReflection );
582
+ }
583
+
584
+ if ( typeReflection.type === 'literal' || typeReflection.type === 'intrinsic' ) {
585
+ return this.convertIntrinsicType( typeReflection );
586
+ }
587
+
588
+ // Inline type: an object or a function.
589
+ if ( typeReflection.type === 'reflection' ) {
590
+ if ( Array.isArray( typeReflection.declaration.signatures ) ) {
591
+ const [ signature ] = typeReflection.declaration.signatures;
592
+ const isClass = signature.kindString === 'Constructor signature';
593
+
594
+ const params = ( signature.parameters || [] )
595
+ .map( singleType => this.convert( singleType.type ) )
596
+ .filter( Boolean );
597
+
598
+ const returns = signature.type ? this.convert( signature.type ) : null;
599
+
600
+ return {
601
+ type: 'function',
602
+ params,
603
+ returns,
604
+ isClass
605
+ };
606
+ }
607
+
608
+ // The fields specified below describe an object.
609
+ //
610
+ // * `typeReflection.declaration.indexSignature` - a generic object,
611
+ // * `Array.isArray( typeReflection.declaration.children )` - an objet with the exact fields.
612
+ return 'object';
613
+ }
614
+
615
+ // A structure represented by an array.
616
+ if ( typeReflection.type === 'tuple' ) {
617
+ // It is an array with mixed types.
618
+ // The position of these types matter.
619
+ return 'tuple';
620
+ }
621
+
622
+ if ( typeReflection.type === 'named-tuple-member' ) {
623
+ return this.convert( typeReflection.element );
624
+ }
625
+
626
+ // A structure that mixes two and more different types.
627
+ if ( typeReflection.type === 'intersection' ) {
628
+ return 'object';
629
+ }
630
+
631
+ if ( typeReflection.type === 'query' ) {
632
+ return typeReflection.queryType.name;
633
+ }
634
+
635
+ // A modified object type.
636
+ // See: https://www.typescriptlang.org/docs/handbook/2/mapped-types.html.
637
+ if ( typeReflection.type === 'mapped' ) {
638
+ return [
639
+ '{',
640
+ '[',
641
+ typeReflection.parameter,
642
+ 'in',
643
+ typeReflection.parameterType.name,
644
+ ']?:', // TODO: Perhaps we should check `typeReflection.optionalModifier`.
645
+ typeReflection.templateType.name,
646
+ '}'
647
+ ].join( ' ' );
648
+ }
649
+
650
+ // A conditional type.
651
+ // Perhaps it could be rendered as an union.
652
+ // See: https://www.typescriptlang.org/docs/handbook/2/conditional-types.html.
653
+ if ( typeReflection.type === 'conditional' ) {
654
+ return 'object';
655
+ }
656
+
657
+ // See: https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html.
658
+ if ( typeReflection.type === 'template-literal' ) {
659
+ const output = typeReflection.tail.reduce( ( output, [ type, suffix ] ) => {
660
+ return output + '${ ' + this._convertSingleType( type ) + ' }' + suffix;
661
+ }, typeReflection.head );
662
+
663
+ return '`' + output + '`';
664
+ }
665
+
666
+ if ( typeReflection.type === 'typeOperator' ) {
667
+ return {
668
+ type: 'operator',
669
+ operator: typeReflection.operator,
670
+ values: this.convert( typeReflection.target )
671
+ };
672
+ }
673
+
674
+ // See: https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#unions.
675
+ if ( typeReflection.type === 'union' ) {
676
+ return this.convertUnion( typeReflection.types );
677
+ }
678
+
679
+ // See: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates.
680
+ /* istanbul ignore else */
681
+ if ( typeReflection.type === 'predicate' ) {
682
+ let typesOrType = this.convert( typeReflection.targetType );
683
+
684
+ if ( typesOrType instanceof Set ) {
685
+ typesOrType = [ ...typesOrType ];
686
+ }
687
+
688
+ if ( !Array.isArray( typesOrType ) ) {
689
+ // `typesOrType` can be an object representing a generic value.
690
+ // TODO: Missing CC here.
691
+ const value = typeof typesOrType === 'object' ? typesOrType.name : typesOrType;
692
+
693
+ if ( value.toLowerCase() === 'object' ) {
694
+ return 'boolean';
695
+ }
696
+
697
+ // Convert a single item into an array to avoid having mixed structures in a view.
698
+ typesOrType = [ typesOrType ];
699
+ }
700
+
701
+ return {
702
+ type: 'predicate',
703
+ name: typeReflection.name,
704
+ instances: typesOrType
705
+ };
706
+ }
707
+
708
+ // At this stage, the type cannot be converted. Perhaps, we missed something while developing
709
+ // the conversion. Let's ask users to create a new issue containing the non-supported structure.
710
+ //
711
+ // Note: The function is created only to hack Istanbul when generating the CC.
712
+ // There is no need for asserting it.
713
+ /* istanbul ignore next */
714
+ const displayMissingTypeAlert = () => {
715
+ console.warn( 'Found a type definition not supported by Umberto yet.' );
716
+ console.warn( 'Please, create a new issue: ' + ISSUE_URL );
717
+ console.log( '' );
718
+ console.warn( 'Ensure to include the following type definition in the issue to help us understand the structure.' );
719
+ console.log( '' );
720
+ console.log( '```json\n' + JSON.stringify( typeReflection, null, 2 ) + '\n```' );
721
+ };
722
+
723
+ /* istanbul ignore next */
724
+ displayMissingTypeAlert();
725
+ }
726
+ }
727
+
728
+ module.exports = TypedocConverter;
729
+
730
+ /**
731
+ * @param {TypeConverter} converter
732
+ * @returns {Function}
733
+ */
734
+ function getHierarchyCallback( converter ) {
735
+ return reference => {
736
+ if ( reference.type === 'intersection' ) {
737
+ return reference.types.map( ref => converter.convertReference( ref ) );
738
+ }
739
+
740
+ return converter.convertReference( reference );
741
+ };
742
+ }