Skip to content
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

refactor(api-markdown-documenter): Allow deeper customization of output folder structure #23366

Open
wants to merge 67 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 64 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
db9d9f1
WIP
Josmithr Dec 16, 2024
f80e4a6
chore: Add `DeepRequired` utility type
Josmithr Dec 16, 2024
ec1242f
chore: Type aliases and helper functions
Josmithr Dec 16, 2024
410049c
[WIP]
Josmithr Dec 16, 2024
9d3ecb2
style: Prettier
Josmithr Dec 16, 2024
b8c8f83
WIP
Josmithr Dec 17, 2024
a67cf98
More WIP
Josmithr Dec 18, 2024
964e4bf
docs: Fix comments
Josmithr Dec 18, 2024
600591a
fix: Correct handling of entry-points
Josmithr Dec 18, 2024
571c170
fix: Types
Josmithr Dec 18, 2024
63f253d
refactor: Rename interface
Josmithr Dec 18, 2024
824c7f0
fix: End-to-end tests
Josmithr Dec 18, 2024
cf91dc5
fix: Config generation
Josmithr Dec 18, 2024
3019028
refactor: Rename types
Josmithr Dec 18, 2024
dad5814
refactor: Rename file
Josmithr Dec 18, 2024
81994f4
fix: Restore support for document hierarchy for packages
Josmithr Dec 18, 2024
214ae3b
fix: Test configs
Josmithr Dec 18, 2024
9786a34
fix(test): Fix configurations
Josmithr Dec 18, 2024
8bce322
refactor: Move function
Josmithr Dec 18, 2024
fda21fa
test: Remove invalid test case
Josmithr Dec 18, 2024
3071431
fix: Document and folder naming
Josmithr Dec 18, 2024
60f6e74
Merge branch 'main' into api-markdown-documenter/hierarchy-config
Josmithr Dec 19, 2024
3e67819
Merge branch 'main' into api-markdown-documenter/hierarchy-config
Josmithr Dec 19, 2024
e58de30
Merge branch 'main' into api-markdown-documenter/hierarchy-config
Josmithr Dec 19, 2024
ef12ea3
Merge branch 'main' into api-markdown-documenter/hierarchy-config
Josmithr Dec 19, 2024
7206e37
Merge branch 'main' into api-markdown-documenter/hierarchy-config
Josmithr Dec 20, 2024
289c9eb
Merge branch 'main' into api-markdown-documenter/hierarchy-config
Josmithr Dec 20, 2024
e4f184c
docs: Fix comment
Josmithr Dec 20, 2024
25162b5
refactor: Remove unneeded type alias
Josmithr Dec 20, 2024
92ecd59
docs: Remove obsolete TODO
Josmithr Dec 20, 2024
7e2dbd5
docs: Remove TODOs
Josmithr Dec 20, 2024
1c6e34a
test: Update test structure
Josmithr Dec 20, 2024
2dc2447
remove: Unused utility function
Josmithr Dec 20, 2024
77f96c8
Merge branch 'main' into api-markdown-documenter/hierarchy-config
Josmithr Dec 20, 2024
56b58e8
test: Add deep config end-to-end tests
Josmithr Dec 20, 2024
5c7d60a
refactor: More hierarchy options flexibility
Josmithr Dec 21, 2024
21b1ea9
refactor: Simplify
Josmithr Dec 21, 2024
ba59e5a
Merge branch 'main' into api-markdown-documenter/hierarchy-config
Josmithr Dec 21, 2024
0381258
refactor: More simplifying
Josmithr Dec 21, 2024
f711e86
refactor: Give more hierarchy config flexibility
Josmithr Jan 6, 2025
42c752b
WIP: Better hierarchical control over naming policy
Josmithr Jan 7, 2025
338e12a
WIP: More input flexibility progress
Josmithr Jan 7, 2025
181c0c8
refactor: Rename properties
Josmithr Jan 7, 2025
9771f71
test: Add unit tests
Josmithr Jan 7, 2025
e6b39dc
refactor: Make input options optional
Josmithr Jan 7, 2025
1db0399
refactor: Variable renames and comment
Josmithr Jan 7, 2025
8119e7f
docs: Add missing copyright notice
Josmithr Jan 7, 2025
ff8de15
test: Add unit test
Josmithr Jan 7, 2025
46364b1
docs: Update comments
Josmithr Jan 7, 2025
0b316fb
docs: Remove stale TODO
Josmithr Jan 7, 2025
05e70b6
refactor: Further simplify hierarchy setup
Josmithr Jan 8, 2025
f5e7cf1
Merge branch 'main' into api-markdown-documenter/hierarchy-config
Josmithr Jan 8, 2025
1088e92
test: Remove new test case for now
Josmithr Jan 8, 2025
8f3396e
test: Fix tests
Josmithr Jan 8, 2025
9f42ec0
docs: Add comment
Josmithr Jan 8, 2025
aa78ec4
docs: Add changelog entry
Josmithr Jan 8, 2025
3d13e68
Merge branch 'api-markdown-documenter/hierarchy-config' of https://gi…
Josmithr Jan 8, 2025
90728fc
refactor: Make enum value strings upper-case for consistency
Josmithr Jan 8, 2025
13339cc
style: Prettier
Josmithr Jan 8, 2025
a453d31
refactor: Make properties readonly
Josmithr Jan 8, 2025
d77d928
Merge branch 'main' into api-markdown-documenter/hierarchy-config
Josmithr Jan 8, 2025
1920abd
refactor: Remove export of API that should not be needed externally
Josmithr Jan 8, 2025
bb91baa
remove: Unwanted new module
Josmithr Jan 8, 2025
0f9092a
Merge branch 'main' into api-markdown-documenter/hierarchy-config
Josmithr Jan 8, 2025
c2dcc77
revert: Unwanted utility refactoring
Josmithr Jan 8, 2025
a5a704d
Merge branch 'main' into api-markdown-documenter/hierarchy-config
Josmithr Jan 8, 2025
c773644
docs: Update comment
Josmithr Jan 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions tools/api-markdown-documenter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,127 @@ await MarkdownRenderer.renderApiModel({
});
```

#### Update pattern for controlling file-wise hierarchy

Previously, users could control certain aspects of the output documentation suite's file-system hierarchy via the `documentBoundaries` and `hierarchyBoundaries` properties of the transformation configuration.
One particular limitation of this setup was that items yielding folder-wise hierarchy (`hierarchyBoundaries`) could never place their own document _inside_ of their own hierarchy.
This naturally lent itself to a pattern where output would commonly be formatted as:

```
- foo.md
- foo
- bar.md
- baz.md
```

This pattern works fine for many site generation systems - a link to `/foo` will end up pointing `foo.md` and a link to `/foo/bar` will end up pointing to `foo/bar.md`.
But some systems (e.g. `Docusaurus`) don't handle this well, and instead prefer setups like the following:

```
- foo
- index.md
- bar.md
- baz.md
```

With the previous configuration options, this pattern was not possible, but now is.
Additionally, this pattern is _more_ commonly accepted, so lack of support for this was a real detriment.

Such patterns can now be produced via the consolidated `hierarchy` property, while still allowing full file-naming flexibility.

##### Related changes

For consistency / discoverability, the `DocumentationSuiteConfiguration.getFileNameForItem` property has also been moved under the new `hierarchy` property (`HierarchyConfiguration`) and renamed to `getDocumentName`.

Additionally, where previously that property controlled both the document _and_ folder naming corresponding to a given API item, folder naming can now be controlled independently via the `getFolderName` property.

##### Example migration

Consider the following configuration:

```typescript
const config = {
...
documentBoundaries: [
ApiItemKind.Class,
ApiItemKind.Interface,
ApiItemKind.Namespace,
],
hierarchyBoundaries: [
ApiItemKind.Namespace,
]
...
}
```

With this configuration, `Class`, `Interface`, and `Namespace` API items would yield their own documents (rather than being rendered to a parent item's document), and `Namespace` items would additionally generate folder hierarchy (child items rendered to their own documents would be placed under a sub-directory).

Output for this case might look something like the following:

```
- package.md
- class.md
- interface.md
- namespace.md
- namespace
- namespace-member-a.md
- namespace-member-b.md
```

This same behavior can now be configured via the following:

```typescript
const config = {
...
hierarchy: {
[ApiItemKind.Class]: HierarchyKind.Document,
[ApiItemKind.Interface]: HierarchyKind.Document,
[ApiItemKind.Namespace]: {
kind: HierarchyKind.Folder,
documentPlacement: FolderDocumentPlacement.Outside,
},
}
...
}
```

Further, if you would prefer to place the resulting `Namespace` documents _under_ their resulting folder, you could use a configuration like the following:

```typescript
const config = {
...
hierarchy: {
[ApiItemKind.Class]: HierarchyKind.Document,
[ApiItemKind.Interface]: HierarchyKind.Document,
[ApiItemKind.Namespace]: {
kind: HierarchyKind.Folder,
documentPlacement: FolderDocumentPlacement.Inside, // <=
},
getDocumentName: (apiItem) => {
switch(apiItem.kind) {
case ApiItemKind.Namespace:
return "index";
default:
...
}
}
}
...
}
```

Output for this updated case might look something like the following:

```
- package.md
- class.md
- interface.md
- namespace
- index.md
- namespace-member-a.md
- namespace-member-b.md
```

#### Type-renames

- `ApiItemTransformationOptions` -> `ApiItemTransformations`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export interface ApiItemTransformationConfigurationBase {
}

// @public
export interface ApiItemTransformationOptions extends ApiItemTransformationConfigurationBase, Partial<DocumentationSuiteConfiguration>, LoggingConfiguration {
export interface ApiItemTransformationOptions extends ApiItemTransformationConfigurationBase, DocumentationSuiteOptions, LoggingConfiguration {
readonly defaultSectionLayout?: (apiItem: ApiItem, childSections: SectionNode[] | undefined, config: ApiItemTransformationConfiguration) => SectionNode[];
readonly transformations?: Partial<ApiItemTransformations>;
}
Expand Down Expand Up @@ -106,7 +106,7 @@ export interface ApiItemTransformations {

declare namespace ApiItemUtilities {
export {
doesItemRequireOwnDocument,
createQualifiedDocumentNameForApiItem,
filterItems,
getHeadingForApiItem,
getLinkForApiItem,
Expand Down Expand Up @@ -186,6 +186,9 @@ function createExamplesSection(apiItem: ApiItem, config: ApiItemTransformationCo
// @public
function createParametersSection(apiFunctionLike: ApiFunctionLike, config: ApiItemTransformationConfiguration): SectionNode | undefined;

// @public
function createQualifiedDocumentNameForApiItem(apiItem: ApiItem, hierarchyConfig: HierarchyConfiguration): string;

// @public
function createRemarksSection(apiItem: ApiItem, config: ApiItemTransformationConfiguration): SectionNode | undefined;

Expand All @@ -211,17 +214,22 @@ function createTypeParametersSection(typeParameters: readonly TypeParameter[], c
export const defaultConsoleLogger: Logger;

// @public
export namespace DefaultDocumentationSuiteOptions {
const defaultDocumentBoundaries: ApiMemberKind[];
const defaultHierarchyBoundaries: ApiMemberKind[];
export namespace DefaultDocumentationSuiteConfiguration {
export function defaultGetAlertsForItem(apiItem: ApiItem): string[];
export function defaultGetFileNameForItem(apiItem: ApiItem): string;
export function defaultGetHeadingTextForItem(apiItem: ApiItem): string;
export function defaultGetLinkTextForItem(apiItem: ApiItem): string;
export function defaultGetUriBaseOverrideForItem(): string | undefined;
export function defaultSkipPackage(): boolean;
}

// @public @sealed
export type DocumentationHierarchyConfiguration = SectionHierarchyConfiguration | DocumentHierarchyConfiguration | FolderHierarchyConfiguration;

// @public @sealed
export interface DocumentationHierarchyConfigurationBase<THierarchyKind extends HierarchyKind> {
readonly kind: THierarchyKind;
}

// @public
export interface DocumentationLiteralNode<TValue = unknown> extends Literal<TValue>, DocumentationNode {
readonly isLiteral: true;
Expand Down Expand Up @@ -306,21 +314,24 @@ export abstract class DocumentationParentNodeBase<TDocumentationNode extends Doc

// @public
export interface DocumentationSuiteConfiguration {
readonly documentBoundaries: DocumentBoundaries;
readonly getAlertsForItem: (apiItem: ApiItem) => string[];
readonly getFileNameForItem: (apiItem: ApiItem) => string;
readonly getHeadingTextForItem: (apiItem: ApiItem) => string;
readonly getLinkTextForItem: (apiItem: ApiItem) => string;
readonly getUriBaseOverrideForItem: (apiItem: ApiItem) => string | undefined;
readonly hierarchyBoundaries: HierarchyBoundaries;
readonly hierarchy: HierarchyConfiguration;
readonly includeBreadcrumb: boolean;
readonly includeTopLevelDocumentHeading: boolean;
readonly minimumReleaseLevel: Exclude<ReleaseTag, ReleaseTag.None>;
readonly skipPackage: (apiPackage: ApiPackage) => boolean;
}

// @public
export type DocumentBoundaries = ApiMemberKind[];
export type DocumentationSuiteOptions = Omit<Partial<DocumentationSuiteConfiguration>, "hierarchy"> & {
readonly hierarchy?: HierarchyOptions;
};

// @public @sealed
export type DocumentHierarchyConfiguration = DocumentationHierarchyConfigurationBase<HierarchyKind.Document>;

// @public
export class DocumentNode implements Parent<SectionNode>, DocumentNodeProps {
Expand Down Expand Up @@ -359,9 +370,6 @@ export namespace DocumentWriter {
export function create(): DocumentWriter;
}

// @public
function doesItemRequireOwnDocument(apiItem: ApiItem, documentBoundaries: DocumentBoundaries): boolean;

// @public
export class FencedCodeBlockNode extends DocumentationParentNodeBase implements MultiLineDocumentationNode {
constructor(children: DocumentationNode[], language?: string);
Expand All @@ -380,6 +388,17 @@ export interface FileSystemConfiguration {
// @public
function filterItems(apiItems: readonly ApiItem[], config: ApiItemTransformationConfiguration): ApiItem[];

// @public
export enum FolderDocumentPlacement {
Inside = "Inside",
Outside = "Outside"
}

// @public @sealed
export type FolderHierarchyConfiguration = DocumentationHierarchyConfigurationBase<HierarchyKind.Folder> & {
readonly documentPlacement: FolderDocumentPlacement;
};

// @public
export function getApiItemTransformationConfigurationWithDefaults(options: ApiItemTransformationOptions): ApiItemTransformationConfiguration;

Expand Down Expand Up @@ -449,7 +468,39 @@ export class HeadingNode extends DocumentationParentNodeBase<SingleLineDocumenta
}

// @public
export type HierarchyBoundaries = ApiMemberKind[];
export type HierarchyConfiguration = {
/**
* Hierarchy configuration for the API item kind.
*/
readonly [Kind in Exclude<ValidApiItemKind, ApiItemKind.Model | ApiItemKind.EntryPoint | ApiItemKind.Package>]: DocumentationHierarchyConfiguration;
} & {
readonly [ApiItemKind.Model]: DocumentHierarchyConfiguration;
readonly [ApiItemKind.Package]: DocumentHierarchyConfiguration | FolderHierarchyConfiguration;
readonly [ApiItemKind.EntryPoint]: DocumentHierarchyConfiguration;
readonly getDocumentName: (apiItem: ApiItem, config: HierarchyConfiguration) => string;
readonly getFolderName: (apiItem: ApiItem, config: HierarchyConfiguration) => string;
};

// @public
export enum HierarchyKind {
Document = "Document",
Folder = "Folder",
Section = "Section"
}

// @public
export type HierarchyOptions = {
/**
* Hierarchy configuration for the API item kind.
*/
readonly [Kind in Exclude<ValidApiItemKind, ApiItemKind.Model | ApiItemKind.EntryPoint | ApiItemKind.Package>]?: HierarchyKind | DocumentationHierarchyConfiguration;
} & {
readonly [ApiItemKind.Model]?: HierarchyKind.Document | DocumentHierarchyConfiguration;
readonly [ApiItemKind.Package]?: HierarchyKind.Document | HierarchyKind.Folder | DocumentHierarchyConfiguration | FolderHierarchyConfiguration;
readonly [ApiItemKind.EntryPoint]?: HierarchyKind.Document | DocumentHierarchyConfiguration;
readonly getDocumentName?: (apiItem: ApiItem, config: HierarchyConfiguration) => string;
readonly getFolderName?: (apiItem: ApiItem, config: HierarchyConfiguration) => string;
};

// @public
export class HorizontalRuleNode implements MultiLineDocumentationNode {
Expand Down Expand Up @@ -699,6 +750,9 @@ function renderNode(node: DocumentationNode, writer: DocumentWriter, context: Ma
// @public
function renderNodes(children: DocumentationNode[], writer: DocumentWriter, childContext: MarkdownRenderContext): void;

// @public @sealed
export type SectionHierarchyConfiguration = DocumentationHierarchyConfigurationBase<HierarchyKind.Section>;

// @public
export class SectionNode extends DocumentationParentNodeBase implements MultiLineDocumentationNode {
constructor(children: DocumentationNode[], heading?: HeadingNode);
Expand Down
Loading
Loading