-
Notifications
You must be signed in to change notification settings - Fork 683
[api-documenter] Generate DocFX UIDs using new DeclarationReference algorithm #1450
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,7 +28,6 @@ import { | |
| ApiEnumMember, | ||
| ApiClass, | ||
| ApiInterface, | ||
| ApiParameterListMixin, | ||
| ApiMethod, | ||
| ApiMethodSignature, | ||
| ApiConstructor, | ||
|
|
@@ -551,36 +550,10 @@ export class YamlDocumenter { | |
|
|
||
| /** | ||
| * Calculate the DocFX "uid" for the ApiItem | ||
| * Example: node-core-library.JsonFile.load | ||
| * Example: `node-core-library!JsonFile#load` | ||
| */ | ||
| protected _getUid(apiItem: ApiItem): string { | ||
| let result: string = ''; | ||
| for (const hierarchyItem of apiItem.getHierarchy()) { | ||
|
|
||
| // For overloaded methods, add a suffix such as "MyClass.myMethod_2". | ||
| let qualifiedName: string = hierarchyItem.displayName; | ||
| if (ApiParameterListMixin.isBaseClassOf(hierarchyItem)) { | ||
| if (hierarchyItem.overloadIndex > 1) { | ||
| // Subtract one for compatibility with earlier releases of API Documenter. | ||
| // (This will get revamped when we fix GitHub issue #1308) | ||
| qualifiedName += `_${hierarchyItem.overloadIndex - 1}`; | ||
| } | ||
| } | ||
|
|
||
| switch (hierarchyItem.kind) { | ||
| case ApiItemKind.Model: | ||
| case ApiItemKind.EntryPoint: | ||
| break; | ||
| case ApiItemKind.Package: | ||
| result += PackageName.getUnscopedName(hierarchyItem.displayName); | ||
| break; | ||
| default: | ||
| result += '.'; | ||
| result += qualifiedName; | ||
| break; | ||
| } | ||
| } | ||
| return result; | ||
| return apiItem.canonicalReference.toString(); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -677,11 +650,46 @@ export class YamlDocumenter { | |
|
|
||
| private _getYamlItemName(apiItem: ApiItem): string { | ||
| if (apiItem.parent && apiItem.parent.kind === ApiItemKind.Namespace) { | ||
| // For members a namespace, show the full name excluding the package part: | ||
| // Example: excel.Excel.Binding --> Excel.Binding | ||
| return this._getUid(apiItem).replace(/^[^.]+\./, ''); | ||
| // If the immediate parent is a namespace, then add the namespaces to the name. For example: | ||
| // | ||
| // // Name: "N1" | ||
| // export namespace N1 { | ||
| // // Name: "N1.N2" | ||
| // export namespace N2 { | ||
| // // Name: "N1.N2.f(x,y)" | ||
| // export function f(x: string, y: string): string { | ||
| // return x + y; | ||
| // } | ||
| // | ||
| // | ||
| // // Name: "N1.N2.C" | ||
| // export class C { | ||
| // // Name: "member(x,y)" <=========== | ||
| // public member(x: string, y: string): string { | ||
| // return x + y; | ||
| // } | ||
| // } | ||
| // } | ||
| // } | ||
| // | ||
| // In the above example, "member(x, y)" does not appear as "N1.N2.C.member(x,y)" because YamlDocumenter | ||
| // embeds this entry in the web page for "N1.N2.C", so the container is obvious. Whereas "N1.N2.f(x,y)" | ||
| // needs to be qualified because the DocFX template doesn't make pages for namespaces. Instead, they get | ||
| // flattened into the package's page. | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rbuckton Does the DocFX template have support for namespaces yet? If so, then we could eliminate this awkward workaround and generate proper pages for them. For an example of how this is currently getting rendered, take a look at: https://docs.microsoft.com/en-us/javascript/api/office?view=excel-js-preview#office-initialize There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it does not. We would have to submit a PR to dotnet/docfx for this change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've mentioned this before, but I have a workaround I use for https://github.com/esfx/esfx that involves changes to support namespaces properly:
Example: https://esfx.js.org/esfx/api/equatable/comparable_namespace.html#equatable_Comparable_Namespace
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you considered opening a PR for DocFX itself? I think everyone who uses TypeScript namespaces would be greatly appreciative of this change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I planned to do so eventually, but not until my schedule has settled down. |
||
| const nameParts: string[] = [ Utilities.getConciseSignature(apiItem) ]; | ||
|
|
||
| for (let current: ApiItem | undefined = apiItem.parent; current; current = current.parent) { | ||
| if (current.kind !== ApiItemKind.Namespace) { | ||
| break; | ||
| } | ||
|
|
||
| nameParts.unshift(current.displayName); | ||
| } | ||
|
|
||
| return nameParts.join('.'); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not really a
@rbuckton In order to integrate this into the TSDoc parser, we will need to integrate it into NodeParser, and the objects need to replace the earlier DocDeclarationReference AST node type. Ideally we would do that in a way that eliminates the need for a standalone There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No worries, was just thinking about whether the DeclarationReference could be reused for this because it already supports the dot-qualified formatting, but I wouldn't worry about changing anything here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My thought had been to add a few members to DeclRef and related objects:
Then you could have done something like: const namespaceName = apiItem.canonicalReference.namespaceName
return (namespaceName ? namespaceName + '.' : '') + Utilities.getConciseSignature(apiItem);
// or, if `Utilities.getConciseSignature` is updated to return only the argument list...
return apiItem.canonicalReference.namespaceQualifiedName + Utilities.getConciseArgumentList(apiItem);
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It might be nice to have a standardized API for rendering names, especially if they may contain problematic characters like ECMAScript symbols, quoted strings, or overloads. However, I'm unsure whether the If we have
The api-extractor-model API might be a better place to implement this, since it has access to information like whether the parent is a namespace or not, and what are the argument names. |
||
| } else { | ||
| return Utilities.getConciseSignature(apiItem); | ||
| } | ||
| return Utilities.getConciseSignature(apiItem); | ||
| } | ||
|
|
||
| private _getYamlFilePath(apiItem: ApiItem): string { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| ### YamlMime:UniversalReference | ||
| items: | ||
| - uid: api-documenter-test.DocClass1 | ||
| - uid: 'api-documenter-test!DocClass1:class' | ||
| summary: This is an example class. | ||
| remarks: >- | ||
| These are some remarks. | ||
|
|
@@ -14,22 +14,22 @@ items: | |
| - typeScript | ||
| type: class | ||
| extends: | ||
| - api-documenter-test.DocBaseClass | ||
| - 'api-documenter-test!DocBaseClass:class' | ||
| implements: | ||
| - api-documenter-test.IDocInterface1 | ||
| - api-documenter-test.IDocInterface2 | ||
| package: api-documenter-test | ||
| - 'api-documenter-test!IDocInterface1:interface' | ||
| - 'api-documenter-test!IDocInterface2:interface' | ||
| package: api-documenter-test! | ||
| children: | ||
| - api-documenter-test.DocClass1.deprecatedExample | ||
| - api-documenter-test.DocClass1.exampleFunction | ||
| - api-documenter-test.DocClass1.exampleFunction_1 | ||
| - api-documenter-test.DocClass1.interestingEdgeCases | ||
| - api-documenter-test.DocClass1.malformedEvent | ||
| - api-documenter-test.DocClass1.modifiedEvent | ||
| - api-documenter-test.DocClass1.regularProperty | ||
| - api-documenter-test.DocClass1.sumWithExample | ||
| - api-documenter-test.DocClass1.tableExample | ||
| - uid: api-documenter-test.DocClass1.deprecatedExample | ||
| - 'api-documenter-test!DocClass1#deprecatedExample:member(1)' | ||
| - 'api-documenter-test!DocClass1#exampleFunction:member(1)' | ||
| - 'api-documenter-test!DocClass1#exampleFunction:member(2)' | ||
| - 'api-documenter-test!DocClass1#interestingEdgeCases:member(1)' | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even though they can't collide, looking at this kind of makes me want to have
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We want this to be the same notation that humans use when creating Most of the selector names are easy to learn because they correspond to the TypeScript keyword that introduces them (e.g. |
||
| - 'api-documenter-test!DocClass1#malformedEvent:member' | ||
| - 'api-documenter-test!DocClass1#modifiedEvent:member' | ||
| - 'api-documenter-test!DocClass1#regularProperty:member' | ||
| - 'api-documenter-test!DocClass1.sumWithExample:member(1)' | ||
| - 'api-documenter-test!DocClass1#tableExample:member(1)' | ||
| - uid: 'api-documenter-test!DocClass1#deprecatedExample:member(1)' | ||
| deprecated: | ||
| content: Use `otherThing()` instead. | ||
| name: deprecatedExample() | ||
|
|
@@ -43,7 +43,7 @@ items: | |
| type: | ||
| - void | ||
| description: '' | ||
| - uid: api-documenter-test.DocClass1.exampleFunction | ||
| - uid: 'api-documenter-test!DocClass1#exampleFunction:member(1)' | ||
| summary: This is an overloaded function. | ||
| name: 'exampleFunction(a, b)' | ||
| fullName: 'exampleFunction(a, b)' | ||
|
|
@@ -65,7 +65,7 @@ items: | |
| description: the second string | ||
| type: | ||
| - string | ||
| - uid: api-documenter-test.DocClass1.exampleFunction_1 | ||
| - uid: 'api-documenter-test!DocClass1#exampleFunction:member(2)' | ||
| summary: This is also an overloaded function. | ||
| name: exampleFunction(x) | ||
| fullName: exampleFunction(x) | ||
|
|
@@ -83,7 +83,7 @@ items: | |
| description: the number | ||
| type: | ||
| - number | ||
| - uid: api-documenter-test.DocClass1.interestingEdgeCases | ||
| - uid: 'api-documenter-test!DocClass1#interestingEdgeCases:member(1)' | ||
| summary: |- | ||
| Example: "<!-- -->{ \\<!-- -->"maxItemsToShow<!-- -->\\<!-- -->": 123 }<!-- -->" | ||
|
|
||
|
|
@@ -99,7 +99,7 @@ items: | |
| type: | ||
| - void | ||
| description: '' | ||
| - uid: api-documenter-test.DocClass1.malformedEvent | ||
| - uid: 'api-documenter-test!DocClass1#malformedEvent:member' | ||
| summary: This event should have been marked as readonly. | ||
| name: malformedEvent | ||
| fullName: malformedEvent | ||
|
|
@@ -110,8 +110,8 @@ items: | |
| content: 'malformedEvent: SystemEvent;' | ||
| return: | ||
| type: | ||
| - api-documenter-test.SystemEvent | ||
| - uid: api-documenter-test.DocClass1.modifiedEvent | ||
| - 'api-documenter-test!SystemEvent:class' | ||
| - uid: 'api-documenter-test!DocClass1#modifiedEvent:member' | ||
| summary: This event is fired whenever the object is modified. | ||
| name: modifiedEvent | ||
| fullName: modifiedEvent | ||
|
|
@@ -122,8 +122,8 @@ items: | |
| content: 'readonly modifiedEvent: SystemEvent;' | ||
| return: | ||
| type: | ||
| - api-documenter-test.SystemEvent | ||
| - uid: api-documenter-test.DocClass1.regularProperty | ||
| - 'api-documenter-test!SystemEvent:class' | ||
| - uid: 'api-documenter-test!DocClass1#regularProperty:member' | ||
| summary: This is a regular property that happens to use the SystemEvent type. | ||
| name: regularProperty | ||
| fullName: regularProperty | ||
|
|
@@ -134,8 +134,8 @@ items: | |
| content: 'regularProperty: SystemEvent;' | ||
| return: | ||
| type: | ||
| - api-documenter-test.SystemEvent | ||
| - uid: api-documenter-test.DocClass1.sumWithExample | ||
| - 'api-documenter-test!SystemEvent:class' | ||
| - uid: 'api-documenter-test!DocClass1.sumWithExample:member(1)' | ||
| summary: Returns the sum of two numbers. | ||
| remarks: This illustrates usage of the `@example` block tag. | ||
| name: 'sumWithExample(x, y)' | ||
|
|
@@ -158,7 +158,7 @@ items: | |
| description: the second number to add | ||
| type: | ||
| - number | ||
| - uid: api-documenter-test.DocClass1.tableExample | ||
| - uid: 'api-documenter-test!DocClass1#tableExample:member(1)' | ||
| summary: 'An example with tables:' | ||
| remarks: <table> <tr> <td>John</td> <td>Doe</td> </tr> </table> | ||
| name: tableExample() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any concern with TOC collisions with
class C { static x; x; }? Both would have the TOC display name ofxwith no disambiguation.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does DocFX create TOC entries for class members? I didn't think it did.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure at the moment, but you may have to disambiguate other items in the TOC, such as merging a function or a variable and an interface with the same name. I had to workaround this for esfx: https://github.com/esfx/esfx/blob/bbc36650efa6a88c9d998b0dafb53e8421513898/scripts/docs/yamlDocumenter.js#L214-L216