UNPKG

37.1 kBJavaScriptView Raw
1"use strict";
2// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3// See LICENSE in the project root for license information.
4var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5 if (k2 === undefined) k2 = k;
6 var desc = Object.getOwnPropertyDescriptor(m, k);
7 if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8 desc = { enumerable: true, get: function() { return m[k]; } };
9 }
10 Object.defineProperty(o, k2, desc);
11}) : (function(o, m, k, k2) {
12 if (k2 === undefined) k2 = k;
13 o[k2] = m[k];
14}));
15var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16 Object.defineProperty(o, "default", { enumerable: true, value: v });
17}) : function(o, v) {
18 o["default"] = v;
19});
20var __importStar = (this && this.__importStar) || function (mod) {
21 if (mod && mod.__esModule) return mod;
22 var result = {};
23 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
24 __setModuleDefault(result, mod);
25 return result;
26};
27var __importDefault = (this && this.__importDefault) || function (mod) {
28 return (mod && mod.__esModule) ? mod : { "default": mod };
29};
30Object.defineProperty(exports, "__esModule", { value: true });
31exports.YamlDocumenter = void 0;
32const path = __importStar(require("path"));
33const yaml = require("js-yaml");
34const node_core_library_1 = require("@rushstack/node-core-library");
35const tsdoc_1 = require("@microsoft/tsdoc");
36const api_extractor_model_1 = require("@microsoft/api-extractor-model");
37const Utilities_1 = require("../utils/Utilities");
38const CustomMarkdownEmitter_1 = require("../markdown/CustomMarkdownEmitter");
39const ToSdpConvertHelper_1 = require("../utils/ToSdpConvertHelper");
40const typescript_schema_json_1 = __importDefault(require("../yaml/typescript.schema.json"));
41const yamlApiSchema = node_core_library_1.JsonSchema.fromLoadedObject(typescript_schema_json_1.default);
42/**
43 * Writes documentation in the Universal Reference YAML file format, as defined by typescript.schema.json.
44 */
45class YamlDocumenter {
46 constructor(apiModel, newDocfxNamespaces = false, yamlFormat = 'sdp') {
47 this._apiModel = apiModel;
48 this.newDocfxNamespaces = newDocfxNamespaces;
49 this._yamlFormat = yamlFormat;
50 this._markdownEmitter = new CustomMarkdownEmitter_1.CustomMarkdownEmitter(this._apiModel);
51 this._apiItemsByCanonicalReference = new Map();
52 this._initApiItems();
53 }
54 /** @virtual */
55 generateFiles(outputFolder) {
56 console.log();
57 this._deleteOldOutputFiles(outputFolder);
58 for (const apiPackage of this._apiModel.packages) {
59 console.log(`Writing ${apiPackage.name} package`);
60 this._visitApiItems(outputFolder, apiPackage, undefined);
61 }
62 if (this._yamlFormat === 'sdp') {
63 (0, ToSdpConvertHelper_1.convertUDPYamlToSDP)(outputFolder);
64 }
65 this._writeTocFile(outputFolder, this._apiModel.packages);
66 }
67 /** @virtual */
68 onGetTocRoot() {
69 return {
70 name: 'SharePoint Framework reference',
71 href: '~/overview/sharepoint.md',
72 items: []
73 };
74 }
75 /** @virtual */
76 onCustomizeYamlItem(yamlItem) {
77 // virtual
78 // (overridden by child class)
79 }
80 _visitApiItems(outputFolder, apiItem, parentYamlFile) {
81 let savedYamlReferences;
82 if (!this._shouldEmbed(apiItem.kind)) {
83 savedYamlReferences = this._yamlReferences;
84 this._yamlReferences = undefined;
85 }
86 const yamlItem = this._generateYamlItem(apiItem);
87 if (!yamlItem) {
88 return false;
89 }
90 this.onCustomizeYamlItem(yamlItem);
91 if (this._shouldEmbed(apiItem.kind)) {
92 if (!parentYamlFile) {
93 throw new node_core_library_1.InternalError('Missing file context');
94 }
95 parentYamlFile.items.push(yamlItem);
96 }
97 else {
98 const newYamlFile = {
99 items: []
100 };
101 newYamlFile.items.push(yamlItem);
102 const children = this._getLogicalChildren(apiItem);
103 for (const child of children) {
104 if (child instanceof api_extractor_model_1.ApiDocumentedItem) {
105 if (this._visitApiItems(outputFolder, child, newYamlFile)) {
106 if (!yamlItem.children) {
107 yamlItem.children = [];
108 }
109 yamlItem.children.push(this._getUid(child));
110 }
111 }
112 }
113 if (this._yamlReferences && this._yamlReferences.references.length > 0) {
114 newYamlFile.references = this._yamlReferences.references;
115 }
116 this._yamlReferences = savedYamlReferences;
117 const yamlFilePath = this._getYamlFilePath(outputFolder, apiItem);
118 if (apiItem.kind === api_extractor_model_1.ApiItemKind.Package) {
119 console.log('Writing ' + yamlFilePath);
120 }
121 this._writeYamlFile(newYamlFile, yamlFilePath, 'UniversalReference', yamlApiSchema);
122 if (parentYamlFile) {
123 // References should be recorded in the parent YAML file with the local name of the embedded item.
124 // This avoids unnecessary repetition when listing items inside of a namespace.
125 this._recordYamlReference(this._ensureYamlReferences(), this._getUid(apiItem), this._getYamlItemName(apiItem, {
126 includeNamespace: !this.newDocfxNamespaces,
127 includeSignature: true
128 }), this._getYamlItemName(apiItem, { includeNamespace: true, includeSignature: true }));
129 }
130 }
131 return true;
132 }
133 _getLogicalChildren(apiItem) {
134 const children = [];
135 if (apiItem.kind === api_extractor_model_1.ApiItemKind.Package) {
136 // Skip over the entry point, since it's not part of the documentation hierarchy
137 this._flattenNamespaces(apiItem.members[0].members, children, this.newDocfxNamespaces ? 0 /* FlattenMode.NestedNamespacesAndChildren */ : 3 /* FlattenMode.NestedChildren */);
138 }
139 else {
140 this._flattenNamespaces(apiItem.members, children, this.newDocfxNamespaces ? 2 /* FlattenMode.ImmediateChildren */ : 3 /* FlattenMode.NestedChildren */);
141 }
142 return children;
143 }
144 // Flattens nested namespaces into top level entries so that the following:
145 // namespace X { export namespace Y { export namespace Z { } }
146 // Is represented as:
147 // - X
148 // - X.Y
149 // - X.Y.Z
150 _flattenNamespaces(items, childrenOut, mode) {
151 let hasNonNamespaceChildren = false;
152 for (const item of items) {
153 if (item.kind === api_extractor_model_1.ApiItemKind.Namespace) {
154 switch (mode) {
155 case 3 /* FlattenMode.NestedChildren */:
156 // Include children of namespaces, but not the namespaces themselves. This matches existing legacy behavior.
157 this._flattenNamespaces(item.members, childrenOut, 3 /* FlattenMode.NestedChildren */);
158 break;
159 case 1 /* FlattenMode.NestedNamespacesOnly */:
160 case 0 /* FlattenMode.NestedNamespacesAndChildren */:
161 // At any level, always include a nested namespace if it has non-namespace children, but do not include its
162 // non-namespace children in the result.
163 // Record the offset at which the namespace is added in case we need to remove it later.
164 const index = childrenOut.length;
165 childrenOut.push(item);
166 if (!this._flattenNamespaces(item.members, childrenOut, 1 /* FlattenMode.NestedNamespacesOnly */)) {
167 // This namespace had no non-namespace children, remove it.
168 childrenOut.splice(index, 1);
169 }
170 break;
171 }
172 }
173 else if (this._shouldInclude(item.kind)) {
174 switch (mode) {
175 case 3 /* FlattenMode.NestedChildren */:
176 case 0 /* FlattenMode.NestedNamespacesAndChildren */:
177 case 2 /* FlattenMode.ImmediateChildren */:
178 // At the top level, include non-namespace children as well.
179 childrenOut.push(item);
180 break;
181 }
182 hasNonNamespaceChildren = true;
183 }
184 }
185 return hasNonNamespaceChildren;
186 }
187 /**
188 * Write the table of contents
189 */
190 _writeTocFile(outputFolder, apiItems) {
191 const tocFile = this.buildYamlTocFile(apiItems);
192 const tocFilePath = path.join(outputFolder, 'toc.yml');
193 console.log('Writing ' + tocFilePath);
194 this._writeYamlFile(tocFile, tocFilePath, '', undefined);
195 }
196 /** @virtual */
197 buildYamlTocFile(apiItems) {
198 const tocFile = {
199 items: []
200 };
201 const rootItem = this.onGetTocRoot();
202 tocFile.items.push(rootItem);
203 rootItem.items.push(...this._buildTocItems(apiItems));
204 return tocFile;
205 }
206 _buildTocItems(apiItems) {
207 const tocItems = [];
208 for (const apiItem of apiItems) {
209 let tocItem;
210 if (apiItem.kind === api_extractor_model_1.ApiItemKind.Namespace && !this.newDocfxNamespaces) {
211 tocItem = {
212 name: this._getTocItemName(apiItem)
213 };
214 }
215 else {
216 if (this._shouldEmbed(apiItem.kind)) {
217 // Don't generate table of contents items for embedded definitions
218 continue;
219 }
220 tocItem = {
221 name: this._getTocItemName(apiItem),
222 uid: this._getUid(apiItem)
223 };
224 }
225 tocItems.push(tocItem);
226 const children = this._getLogicalChildren(apiItem);
227 const childItems = this._buildTocItems(children);
228 if (childItems.length > 0) {
229 tocItem.items = childItems;
230 }
231 }
232 return tocItems;
233 }
234 /** @virtual */
235 _getTocItemName(apiItem) {
236 let name;
237 if (apiItem.kind === api_extractor_model_1.ApiItemKind.Package) {
238 name = node_core_library_1.PackageName.getUnscopedName(apiItem.displayName);
239 }
240 else {
241 name = this._getYamlItemName(apiItem);
242 }
243 if (name === apiItem.displayName && apiItem.getMergedSiblings().length > 1) {
244 name += ` (${apiItem.kind})`;
245 }
246 return name;
247 }
248 _shouldEmbed(apiItemKind) {
249 switch (apiItemKind) {
250 case api_extractor_model_1.ApiItemKind.Class:
251 case api_extractor_model_1.ApiItemKind.Package:
252 case api_extractor_model_1.ApiItemKind.Interface:
253 case api_extractor_model_1.ApiItemKind.Enum:
254 case api_extractor_model_1.ApiItemKind.TypeAlias:
255 return false;
256 case api_extractor_model_1.ApiItemKind.Namespace:
257 return !this.newDocfxNamespaces;
258 }
259 return true;
260 }
261 _shouldInclude(apiItemKind) {
262 // Filter out known items that are not yet supported
263 switch (apiItemKind) {
264 case api_extractor_model_1.ApiItemKind.CallSignature:
265 case api_extractor_model_1.ApiItemKind.ConstructSignature:
266 case api_extractor_model_1.ApiItemKind.IndexSignature:
267 return false;
268 }
269 return true;
270 }
271 _generateYamlItem(apiItem) {
272 // Filter out known items that are not yet supported
273 if (!this._shouldInclude(apiItem.kind)) {
274 return undefined;
275 }
276 const uid = this._getUidObject(apiItem);
277 const yamlItem = {
278 uid: uid.toString()
279 };
280 if (apiItem.tsdocComment) {
281 const tsdocComment = apiItem.tsdocComment;
282 if (tsdocComment.summarySection) {
283 const summary = this._renderMarkdown(tsdocComment.summarySection, apiItem);
284 if (summary) {
285 yamlItem.summary = summary;
286 }
287 }
288 if (tsdocComment.remarksBlock) {
289 const remarks = this._renderMarkdown(tsdocComment.remarksBlock.content, apiItem);
290 if (remarks) {
291 yamlItem.remarks = remarks;
292 }
293 }
294 if (tsdocComment) {
295 // Write the @example blocks
296 const exampleBlocks = tsdocComment.customBlocks.filter((x) => x.blockTag.tagNameWithUpperCase === tsdoc_1.StandardTags.example.tagNameWithUpperCase);
297 for (const exampleBlock of exampleBlocks) {
298 const example = this._renderMarkdown(exampleBlock.content, apiItem);
299 if (example) {
300 yamlItem.example = [...(yamlItem.example || []), example];
301 }
302 }
303 }
304 if (tsdocComment.deprecatedBlock) {
305 const deprecatedMessage = this._renderMarkdown(tsdocComment.deprecatedBlock.content, apiItem);
306 if (deprecatedMessage.length > 0) {
307 yamlItem.deprecated = { content: deprecatedMessage };
308 }
309 }
310 }
311 if (api_extractor_model_1.ApiReleaseTagMixin.isBaseClassOf(apiItem)) {
312 if (apiItem.releaseTag === api_extractor_model_1.ReleaseTag.Alpha || apiItem.releaseTag === api_extractor_model_1.ReleaseTag.Beta) {
313 yamlItem.isPreview = true;
314 }
315 }
316 yamlItem.name = this._getYamlItemName(apiItem, {
317 includeSignature: true,
318 includeNamespace: !this.newDocfxNamespaces
319 });
320 yamlItem.fullName = this._getYamlItemName(apiItem, { includeSignature: true, includeNamespace: true });
321 yamlItem.langs = ['typeScript'];
322 // Add the namespace of the item if it is contained in one.
323 // Do not add the namespace parent of a namespace as they are flattened in the documentation.
324 if (apiItem.kind !== api_extractor_model_1.ApiItemKind.Namespace &&
325 apiItem.parent &&
326 apiItem.parent.kind === api_extractor_model_1.ApiItemKind.Namespace &&
327 this.newDocfxNamespaces) {
328 yamlItem.namespace = apiItem.parent.canonicalReference.toString();
329 }
330 switch (apiItem.kind) {
331 case api_extractor_model_1.ApiItemKind.Enum:
332 yamlItem.type = 'enum';
333 break;
334 case api_extractor_model_1.ApiItemKind.EnumMember:
335 yamlItem.type = 'field';
336 const enumMember = apiItem;
337 if (enumMember.initializerExcerpt && enumMember.initializerExcerpt.text.length > 0) {
338 yamlItem.numericValue = enumMember.initializerExcerpt.text;
339 }
340 break;
341 case api_extractor_model_1.ApiItemKind.Class:
342 yamlItem.type = 'class';
343 this._populateYamlClassOrInterface(uid, yamlItem, apiItem);
344 break;
345 case api_extractor_model_1.ApiItemKind.Interface:
346 yamlItem.type = 'interface';
347 this._populateYamlClassOrInterface(uid, yamlItem, apiItem);
348 break;
349 case api_extractor_model_1.ApiItemKind.Method:
350 case api_extractor_model_1.ApiItemKind.MethodSignature:
351 yamlItem.type = 'method';
352 this._populateYamlFunctionLike(uid, yamlItem, apiItem);
353 break;
354 case api_extractor_model_1.ApiItemKind.Constructor:
355 yamlItem.type = 'constructor';
356 this._populateYamlFunctionLike(uid, yamlItem, apiItem);
357 break;
358 case api_extractor_model_1.ApiItemKind.Package:
359 yamlItem.type = 'package';
360 break;
361 case api_extractor_model_1.ApiItemKind.Namespace:
362 yamlItem.type = 'namespace';
363 break;
364 case api_extractor_model_1.ApiItemKind.Property:
365 case api_extractor_model_1.ApiItemKind.PropertySignature:
366 const apiProperty = apiItem;
367 if (apiProperty.isEventProperty) {
368 yamlItem.type = 'event';
369 }
370 else {
371 yamlItem.type = 'property';
372 }
373 this._populateYamlProperty(uid, yamlItem, apiProperty);
374 break;
375 case api_extractor_model_1.ApiItemKind.Function:
376 yamlItem.type = 'function';
377 this._populateYamlFunctionLike(uid, yamlItem, apiItem);
378 break;
379 case api_extractor_model_1.ApiItemKind.Variable:
380 yamlItem.type = 'variable';
381 this._populateYamlVariable(uid, yamlItem, apiItem);
382 break;
383 case api_extractor_model_1.ApiItemKind.TypeAlias:
384 yamlItem.type = 'typealias';
385 this._populateYamlTypeAlias(uid, yamlItem, apiItem);
386 break;
387 default:
388 throw new Error('Unimplemented item kind: ' + apiItem.kind);
389 }
390 if (apiItem.kind !== api_extractor_model_1.ApiItemKind.Package && !this._shouldEmbed(apiItem.kind)) {
391 const associatedPackage = apiItem.getAssociatedPackage();
392 if (!associatedPackage) {
393 throw new Error('Unable to determine associated package for ' + apiItem.displayName);
394 }
395 yamlItem.package = this._getUid(associatedPackage);
396 }
397 return yamlItem;
398 }
399 _populateYamlTypeParameters(contextUid, apiItem) {
400 const typeParameters = [];
401 for (const apiTypeParameter of apiItem.typeParameters) {
402 const typeParameter = {
403 id: apiTypeParameter.name
404 };
405 if (apiTypeParameter.tsdocTypeParamBlock) {
406 typeParameter.description = this._renderMarkdown(apiTypeParameter.tsdocTypeParamBlock.content, apiItem);
407 }
408 if (!apiTypeParameter.constraintExcerpt.isEmpty) {
409 typeParameter.type = [this._renderType(contextUid, apiTypeParameter.constraintExcerpt)];
410 }
411 typeParameters.push(typeParameter);
412 }
413 return typeParameters;
414 }
415 _populateYamlClassOrInterface(uid, yamlItem, apiItem) {
416 if (apiItem instanceof api_extractor_model_1.ApiClass) {
417 if (apiItem.extendsType) {
418 yamlItem.extends = [this._renderType(uid, apiItem.extendsType.excerpt)];
419 yamlItem.inheritance = this._renderInheritance(uid, [apiItem.extendsType]);
420 }
421 if (apiItem.implementsTypes.length > 0) {
422 yamlItem.implements = [];
423 for (const implementsType of apiItem.implementsTypes) {
424 yamlItem.implements.push(this._renderType(uid, implementsType.excerpt));
425 }
426 }
427 }
428 else if (apiItem instanceof api_extractor_model_1.ApiInterface) {
429 if (apiItem.extendsTypes.length > 0) {
430 yamlItem.extends = [];
431 for (const extendsType of apiItem.extendsTypes) {
432 yamlItem.extends.push(this._renderType(uid, extendsType.excerpt));
433 }
434 yamlItem.inheritance = this._renderInheritance(uid, apiItem.extendsTypes);
435 }
436 const typeParameters = this._populateYamlTypeParameters(uid, apiItem);
437 if (typeParameters.length) {
438 yamlItem.syntax = { typeParameters };
439 }
440 }
441 if (apiItem.tsdocComment) {
442 if (apiItem.tsdocComment.modifierTagSet.isSealed()) {
443 let sealedMessage;
444 if (apiItem.kind === api_extractor_model_1.ApiItemKind.Class) {
445 sealedMessage = 'This class is marked as `@sealed`. Subclasses should not extend it.';
446 }
447 else {
448 sealedMessage = 'This interface is marked as `@sealed`. Other interfaces should not extend it.';
449 }
450 if (!yamlItem.remarks) {
451 yamlItem.remarks = sealedMessage;
452 }
453 else {
454 yamlItem.remarks = sealedMessage + '\n\n' + yamlItem.remarks;
455 }
456 }
457 }
458 }
459 _populateYamlFunctionLike(uid, yamlItem, apiItem) {
460 const syntax = {
461 content: apiItem.getExcerptWithModifiers()
462 };
463 yamlItem.syntax = syntax;
464 if (api_extractor_model_1.ApiReturnTypeMixin.isBaseClassOf(apiItem)) {
465 const returnType = this._renderType(uid, apiItem.returnTypeExcerpt);
466 let returnDescription = '';
467 if (apiItem.tsdocComment && apiItem.tsdocComment.returnsBlock) {
468 returnDescription = this._renderMarkdown(apiItem.tsdocComment.returnsBlock.content, apiItem);
469 // temporary workaround for people who mistakenly add a hyphen, e.g. "@returns - blah"
470 returnDescription = returnDescription.replace(/^\s*-\s+/, '');
471 }
472 if (returnType || returnDescription) {
473 syntax.return = {
474 type: [returnType],
475 description: returnDescription
476 };
477 }
478 }
479 const parameters = [];
480 for (const apiParameter of apiItem.parameters) {
481 let parameterDescription = '';
482 if (apiParameter.tsdocParamBlock) {
483 parameterDescription = this._renderMarkdown(apiParameter.tsdocParamBlock.content, apiItem);
484 }
485 parameters.push({
486 id: apiParameter.name,
487 description: parameterDescription,
488 type: [this._renderType(uid, apiParameter.parameterTypeExcerpt)],
489 optional: apiParameter.isOptional
490 });
491 }
492 if (parameters.length) {
493 syntax.parameters = parameters;
494 }
495 if (api_extractor_model_1.ApiTypeParameterListMixin.isBaseClassOf(apiItem)) {
496 const typeParameters = this._populateYamlTypeParameters(uid, apiItem);
497 if (typeParameters.length) {
498 syntax.typeParameters = typeParameters;
499 }
500 }
501 }
502 _populateYamlProperty(uid, yamlItem, apiItem) {
503 const syntax = {
504 content: apiItem.getExcerptWithModifiers()
505 };
506 yamlItem.syntax = syntax;
507 if (apiItem.propertyTypeExcerpt.text) {
508 syntax.return = {
509 type: [this._renderType(uid, apiItem.propertyTypeExcerpt)]
510 };
511 }
512 }
513 _populateYamlVariable(uid, yamlItem, apiItem) {
514 const syntax = {
515 content: apiItem.getExcerptWithModifiers()
516 };
517 yamlItem.syntax = syntax;
518 if (apiItem.variableTypeExcerpt.text) {
519 syntax.return = {
520 type: [this._renderType(uid, apiItem.variableTypeExcerpt)]
521 };
522 }
523 }
524 _populateYamlTypeAlias(uid, yamlItem, apiItem) {
525 const syntax = {
526 content: apiItem.getExcerptWithModifiers()
527 };
528 yamlItem.syntax = syntax;
529 const typeParameters = this._populateYamlTypeParameters(uid, apiItem);
530 if (typeParameters.length) {
531 syntax.typeParameters = typeParameters;
532 }
533 if (apiItem.typeExcerpt.text) {
534 syntax.return = {
535 type: [this._renderType(uid, apiItem.typeExcerpt)]
536 };
537 }
538 }
539 _renderMarkdown(docSection, contextApiItem) {
540 const stringBuilder = new tsdoc_1.StringBuilder();
541 this._markdownEmitter.emit(stringBuilder, docSection, {
542 contextApiItem,
543 onGetFilenameForApiItem: (apiItem) => {
544 // NOTE: GitHub's markdown renderer does not resolve relative hyperlinks correctly
545 // unless they start with "./" or "../".
546 // To ensure the xref is properly escaped, we first encode the entire xref
547 // to handle escaping of reserved characters. Then we must replace '#' and '?'
548 // characters so that they are not interpreted as a querystring or hash.
549 // We must also backslash-escape unbalanced `(` and `)` characters as the
550 // markdown spec insists that they are only valid when balanced. To reduce
551 // the overhead we only support balanced parenthesis with a depth of 1.
552 return encodeURI(`xref:${this._getUid(apiItem)}`)
553 .replace(/[#?]/g, (s) => encodeURIComponent(s))
554 .replace(/(\([^(]*\))|[()]/g, (s, balanced) => balanced || '\\' + s);
555 }
556 });
557 return stringBuilder.toString().trim();
558 }
559 _writeYamlFile(dataObject, filePath, yamlMimeType, schema) {
560 node_core_library_1.JsonFile.validateNoUndefinedMembers(dataObject);
561 let stringified = yaml.safeDump(dataObject, {
562 lineWidth: 120
563 });
564 if (yamlMimeType) {
565 stringified = `### YamlMime:${yamlMimeType}\n` + stringified;
566 }
567 node_core_library_1.FileSystem.writeFile(filePath, stringified, {
568 convertLineEndings: node_core_library_1.NewlineKind.CrLf,
569 ensureFolderExists: true
570 });
571 if (schema) {
572 schema.validateObject(dataObject, filePath);
573 }
574 }
575 /**
576 * Calculate the DocFX "uid" for the ApiItem
577 * Example: `node-core-library!JsonFile#load`
578 */
579 _getUid(apiItem) {
580 return this._getUidObject(apiItem).toString();
581 }
582 _getUidObject(apiItem) {
583 return apiItem.canonicalReference;
584 }
585 /**
586 * Initialize the _apiItemsByCanonicalReference data structure.
587 */
588 _initApiItems() {
589 this._initApiItemsRecursive(this._apiModel);
590 }
591 /**
592 * Helper for _initApiItems()
593 */
594 _initApiItemsRecursive(apiItem) {
595 if (apiItem.canonicalReference && !apiItem.canonicalReference.isEmpty) {
596 this._apiItemsByCanonicalReference.set(apiItem.canonicalReference.toString(), apiItem);
597 }
598 // Recurse container members
599 if (api_extractor_model_1.ApiItemContainerMixin.isBaseClassOf(apiItem)) {
600 for (const apiMember of apiItem.members) {
601 this._initApiItemsRecursive(apiMember);
602 }
603 }
604 }
605 _ensureYamlReferences() {
606 if (!this._yamlReferences) {
607 this._yamlReferences = {
608 references: [],
609 typeNameToUid: new Map(),
610 uidTypeReferenceCounters: new Map()
611 };
612 }
613 return this._yamlReferences;
614 }
615 _renderInheritance(contextUid, heritageTypes) {
616 const result = [];
617 for (const heritageType of heritageTypes) {
618 const type = this._renderType(contextUid, heritageType.excerpt);
619 const yamlInheritance = { type };
620 const apiItem = this._apiItemsByCanonicalReference.get(type);
621 if (apiItem) {
622 if (apiItem instanceof api_extractor_model_1.ApiClass) {
623 if (apiItem.extendsType) {
624 yamlInheritance.inheritance = this._renderInheritance(this._getUidObject(apiItem), [
625 apiItem.extendsType
626 ]);
627 }
628 }
629 else if (apiItem instanceof api_extractor_model_1.ApiInterface) {
630 if (apiItem.extendsTypes.length > 0) {
631 yamlInheritance.inheritance = this._renderInheritance(this._getUidObject(apiItem), apiItem.extendsTypes);
632 }
633 }
634 }
635 result.push(yamlInheritance);
636 }
637 return result;
638 }
639 _renderType(contextUid, typeExcerpt) {
640 const excerptTokens = [...typeExcerpt.spannedTokens]; // copy the read-only array
641 if (excerptTokens.length === 0) {
642 return '';
643 }
644 // Remove the last token if it consists only of whitespace
645 const lastToken = excerptTokens[excerptTokens.length - 1];
646 if (lastToken.kind === api_extractor_model_1.ExcerptTokenKind.Content && !lastToken.text.trim()) {
647 excerptTokens.pop();
648 if (excerptTokens.length === 0) {
649 return '';
650 }
651 }
652 const typeName = typeExcerpt.text.trim();
653 // If there are no references to be used for a complex type, return the type name.
654 if (!excerptTokens.some((tok) => tok.kind === api_extractor_model_1.ExcerptTokenKind.Reference && !!tok.canonicalReference)) {
655 return typeName;
656 }
657 const yamlReferences = this._ensureYamlReferences();
658 const existingUid = yamlReferences.typeNameToUid.get(typeName);
659 // If this type has already been referenced for the current file, return its uid.
660 if (existingUid) {
661 return existingUid;
662 }
663 // If the excerpt consists of a single reference token, record the reference.
664 if (excerptTokens.length === 1 &&
665 excerptTokens[0].kind === api_extractor_model_1.ExcerptTokenKind.Reference &&
666 excerptTokens[0].canonicalReference) {
667 const excerptRef = excerptTokens[0].canonicalReference.toString();
668 const apiItem = this._apiItemsByCanonicalReference.get(excerptRef);
669 return this._recordYamlReference(yamlReferences, excerptTokens[0].canonicalReference.toString(), apiItem ? this._getYamlItemName(apiItem) : typeName, apiItem ? this._getYamlItemName(apiItem, { includeNamespace: true }) : typeName);
670 }
671 // Otherwise, the type is complex and consists of one or more reference tokens. Record a reference
672 // and return its uid.
673 const baseUid = contextUid.withMeaning(undefined).withOverloadIndex(undefined).toString();
674 // Keep track of the count for the base uid (without meaning or overload index) to ensure
675 // that each complex type reference is unique.
676 const counter = yamlReferences.uidTypeReferenceCounters.get(baseUid) || 0;
677 yamlReferences.uidTypeReferenceCounters.set(baseUid, counter + 1);
678 const uid = contextUid
679 .addNavigationStep("~" /* Navigation.Locals */, `${counter}`)
680 .withMeaning("complex" /* Meaning.ComplexType */)
681 .withOverloadIndex(undefined)
682 .toString();
683 return this._recordYamlReference(yamlReferences, uid, typeName, typeName, excerptTokens);
684 }
685 _recordYamlReference(yamlReferences, uid, name, fullName, excerptTokens) {
686 if (yamlReferences.references.some((ref) => ref.uid === uid)) {
687 return uid;
688 }
689 // Fill in the reference spec from the excerpt.
690 const specs = [];
691 if (excerptTokens) {
692 for (const token of excerptTokens) {
693 if (token.kind === api_extractor_model_1.ExcerptTokenKind.Reference) {
694 const spec = {};
695 const specUid = token.canonicalReference && token.canonicalReference.toString();
696 const apiItem = specUid
697 ? this._apiItemsByCanonicalReference.get(specUid)
698 : undefined;
699 if (specUid) {
700 spec.uid = specUid;
701 }
702 spec.name = token.text;
703 spec.fullName = apiItem
704 ? apiItem.getScopedNameWithinPackage()
705 : token.canonicalReference
706 ? token.canonicalReference
707 .withSource(undefined)
708 .withMeaning(undefined)
709 .withOverloadIndex(undefined)
710 .toString()
711 : token.text;
712 specs.push(spec);
713 }
714 else {
715 specs.push({
716 name: token.text,
717 fullName: token.text
718 });
719 }
720 }
721 }
722 const yamlReference = { uid };
723 if (specs.length > 0) {
724 yamlReference.name = specs
725 .map((s) => s.name)
726 .join('')
727 .trim();
728 yamlReference.fullName = specs
729 .map((s) => s.fullName || s.name)
730 .join('')
731 .trim();
732 yamlReference['spec.typeScript'] = specs;
733 }
734 else {
735 if (name !== uid) {
736 yamlReference.name = name;
737 }
738 if (fullName !== uid && fullName !== name) {
739 yamlReference.fullName = fullName;
740 }
741 }
742 yamlReferences.references.push(yamlReference);
743 return uid;
744 }
745 _getYamlItemName(apiItem, options = {}) {
746 const { includeSignature, includeNamespace } = options;
747 const baseName = includeSignature ? Utilities_1.Utilities.getConciseSignature(apiItem) : apiItem.displayName;
748 if ((includeNamespace || apiItem.kind === api_extractor_model_1.ApiItemKind.Namespace) &&
749 apiItem.parent &&
750 apiItem.parent.kind === api_extractor_model_1.ApiItemKind.Namespace) {
751 // If the immediate parent is a namespace, then add the namespaces to the name. For example:
752 //
753 // // Name: "N1"
754 // export namespace N1 {
755 // // Name: "N1.N2"
756 // export namespace N2 {
757 // // Name: "N1.N2.f(x,y)"
758 // export function f(x: string, y: string): string {
759 // return x + y;
760 // }
761 //
762 //
763 // // Name: "N1.N2.C"
764 // export class C {
765 // // Name: "member(x,y)" <===========
766 // public member(x: string, y: string): string {
767 // return x + y;
768 // }
769 // }
770 // }
771 // }
772 //
773 // In the above example, "member(x, y)" does not appear as "N1.N2.C.member(x,y)" because YamlDocumenter
774 // embeds this entry in the web page for "N1.N2.C", so the container is obvious. Whereas "N1.N2.f(x,y)"
775 // needs to be qualified because the DocFX template doesn't make pages for namespaces. Instead, they get
776 // flattened into the package's page.
777 const nameParts = [baseName];
778 for (let current = apiItem.parent; current; current = current.parent) {
779 if (current.kind !== api_extractor_model_1.ApiItemKind.Namespace) {
780 break;
781 }
782 nameParts.unshift(current.displayName);
783 }
784 return nameParts.join('.');
785 }
786 else {
787 return baseName;
788 }
789 }
790 _getYamlFilePath(outputFolder, apiItem) {
791 let result = '';
792 for (const current of apiItem.getHierarchy()) {
793 switch (current.kind) {
794 case api_extractor_model_1.ApiItemKind.Model:
795 case api_extractor_model_1.ApiItemKind.EntryPoint:
796 break;
797 case api_extractor_model_1.ApiItemKind.Package:
798 result += Utilities_1.Utilities.getSafeFilenameForName(node_core_library_1.PackageName.getUnscopedName(current.displayName));
799 break;
800 default:
801 if (current.parent && current.parent.kind === api_extractor_model_1.ApiItemKind.EntryPoint) {
802 result += '/';
803 }
804 else {
805 result += '.';
806 }
807 result += Utilities_1.Utilities.getSafeFilenameForName(current.displayName);
808 break;
809 }
810 }
811 let disambiguator = '';
812 if (apiItem.getMergedSiblings().length > 1) {
813 disambiguator = `-${apiItem.kind.toLowerCase()}`;
814 }
815 return path.join(outputFolder, result + disambiguator + '.yml');
816 }
817 _deleteOldOutputFiles(outputFolder) {
818 console.log('Deleting old output from ' + outputFolder);
819 node_core_library_1.FileSystem.ensureEmptyFolder(outputFolder);
820 }
821}
822exports.YamlDocumenter = YamlDocumenter;
823//# sourceMappingURL=YamlDocumenter.js.map
\No newline at end of file