Skip to content

Commit 01913b8

Browse files
committed
Add MarkdownDocumenterFeature.onFinished() and MarkdownDocumenterFeatureContext members
1 parent 7f34fa3 commit 01913b8

10 files changed

+152
-30
lines changed

apps/api-documenter/src/cli/GenerateAction.ts

+4-9
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,10 @@ import { ApiDocumenterCommandLine } from './ApiDocumenterCommandLine';
77
import { BaseAction } from './BaseAction';
88
import { DocumenterConfig } from '../documenters/DocumenterConfig';
99
import { ExperimentalYamlDocumenter } from '../documenters/ExperimentalYamlDocumenter';
10-
import { IConfigFile } from '../documenters/IConfigFile';
1110

1211
import { ApiModel } from '@microsoft/api-extractor-model';
1312
import { FileSystem } from '@microsoft/node-core-library';
1413
import { MarkdownDocumenter } from '../documenters/MarkdownDocumenter';
15-
import { PluginLoader } from '../plugin/PluginLoader';
1614

1715
export class GenerateAction extends BaseAction {
1816
constructor(parser: ApiDocumenterCommandLine) {
@@ -38,18 +36,15 @@ export class GenerateAction extends BaseAction {
3836
}
3937
}
4038

41-
const configFile: IConfigFile = DocumenterConfig.loadFile(configFilePath);
42-
43-
const pluginLoader: PluginLoader = new PluginLoader();
44-
pluginLoader.load(configFile.plugins || [], configFilePath);
39+
const documenterConfig: DocumenterConfig = DocumenterConfig.loadFile(configFilePath);
4540

4641
const apiModel: ApiModel = this.buildApiModel();
4742

48-
if (configFile.outputTarget === 'markdown') {
49-
const markdownDocumenter: MarkdownDocumenter = new MarkdownDocumenter(apiModel, pluginLoader);
43+
if (documenterConfig.configFile.outputTarget === 'markdown') {
44+
const markdownDocumenter: MarkdownDocumenter = new MarkdownDocumenter(apiModel, documenterConfig);
5045
markdownDocumenter.generateFiles(this.outputFolder);
5146
} else {
52-
const yamlDocumenter: ExperimentalYamlDocumenter = new ExperimentalYamlDocumenter(apiModel, configFile);
47+
const yamlDocumenter: ExperimentalYamlDocumenter = new ExperimentalYamlDocumenter(apiModel, documenterConfig);
5348
yamlDocumenter.generateFiles(this.outputFolder);
5449
}
5550

apps/api-documenter/src/cli/MarkdownAction.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { ApiDocumenterCommandLine } from './ApiDocumenterCommandLine';
55
import { BaseAction } from './BaseAction';
66
import { MarkdownDocumenter } from '../documenters/MarkdownDocumenter';
77
import { ApiModel } from '@microsoft/api-extractor-model';
8-
import { PluginLoader } from '../plugin/PluginLoader';
98

109
export class MarkdownAction extends BaseAction {
1110
constructor(parser: ApiDocumenterCommandLine) {
@@ -20,8 +19,7 @@ export class MarkdownAction extends BaseAction {
2019
protected onExecute(): Promise<void> { // override
2120
const apiModel: ApiModel = this.buildApiModel();
2221

23-
const pluginLoader: PluginLoader = new PluginLoader();
24-
const markdownDocumenter: MarkdownDocumenter = new MarkdownDocumenter(apiModel, pluginLoader);
22+
const markdownDocumenter: MarkdownDocumenter = new MarkdownDocumenter(apiModel, undefined);
2523
markdownDocumenter.generateFiles(this.outputFolder);
2624
return Promise.resolve();
2725
}

apps/api-documenter/src/documenters/DocumenterConfig.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import { IConfigFile } from './IConfigFile';
1111
* represents the raw JSON file structure.
1212
*/
1313
export class DocumenterConfig {
14+
public readonly configFilePath: string;
15+
public readonly configFile: IConfigFile;
16+
1417
/**
1518
* The JSON Schema for API Extractor config file (api-extractor.schema.json).
1619
*/
@@ -25,9 +28,14 @@ export class DocumenterConfig {
2528
/**
2629
* Load and validate an api-documenter.json file.
2730
*/
28-
public static loadFile(configFilePath: string): IConfigFile {
31+
public static loadFile(configFilePath: string): DocumenterConfig {
2932
const configFile: IConfigFile = JsonFile.loadAndValidate(configFilePath, DocumenterConfig.jsonSchema);
3033

31-
return configFile;
34+
return new DocumenterConfig(path.resolve(configFilePath), configFile);
35+
}
36+
37+
private constructor(filePath: string, configFile: IConfigFile) {
38+
this.configFilePath = filePath;
39+
this.configFile = configFile;
3240
}
3341
}

apps/api-documenter/src/documenters/ExperimentalYamlDocumenter.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import { PackageName } from '@microsoft/node-core-library';
55
import { DocComment, DocInlineTag } from '@microsoft/tsdoc';
66
import { ApiModel, ApiItem, ApiItemKind, ApiDocumentedItem } from '@microsoft/api-extractor-model';
77

8-
import { IConfigFile, IConfigTableOfContents } from './IConfigFile';
8+
import { IConfigTableOfContents } from './IConfigFile';
99
import { IYamlTocItem, IYamlTocFile } from '../yaml/IYamlTocFile';
1010
import { YamlDocumenter } from './YamlDocumenter';
11+
import { DocumenterConfig } from './DocumenterConfig';
1112

1213
/**
1314
* EXPERIMENTAL - This documenter is a prototype of a new config file driven mode of operation for
@@ -18,9 +19,9 @@ export class ExperimentalYamlDocumenter extends YamlDocumenter {
1819
private _tocPointerMap: { [key: string]: IYamlTocItem };
1920
private _catchAllPointer: IYamlTocItem;
2021

21-
public constructor(apiModel: ApiModel, configFile: IConfigFile) {
22+
public constructor(apiModel: ApiModel, documenterConfig: DocumenterConfig) {
2223
super(apiModel);
23-
this._config = configFile.tableOfContents!;
24+
this._config = documenterConfig.configFile.tableOfContents!;
2425

2526
this._tocPointerMap = {};
2627

apps/api-documenter/src/documenters/MarkdownDocumenter.ts

+30-4
Original file line numberDiff line numberDiff line change
@@ -52,36 +52,62 @@ import { DocNoteBox } from '../nodes/DocNoteBox';
5252
import { Utilities } from '../utils/Utilities';
5353
import { CustomMarkdownEmitter } from '../markdown/CustomMarkdownEmitter';
5454
import { PluginLoader } from '../plugin/PluginLoader';
55-
import { IMarkdownDocumenterFeatureOnBeforeWritePageArgs } from '../plugin/MarkdownDocumenterFeature';
55+
import {
56+
IMarkdownDocumenterFeatureOnBeforeWritePageArgs,
57+
MarkdownDocumenterFeatureContext
58+
} from '../plugin/MarkdownDocumenterFeature';
59+
import { DocumenterConfig } from './DocumenterConfig';
60+
import { MarkdownDocumenterAccessor } from '../plugin/MarkdownDocumenterAccessor';
5661

5762
/**
5863
* Renders API documentation in the Markdown file format.
5964
* For more info: https://en.wikipedia.org/wiki/Markdown
6065
*/
6166
export class MarkdownDocumenter {
6267
private readonly _apiModel: ApiModel;
63-
private readonly _pluginLoader: PluginLoader;
68+
private readonly _documenterConfig: DocumenterConfig | undefined;
6469
private readonly _tsdocConfiguration: TSDocConfiguration;
6570
private readonly _markdownEmitter: CustomMarkdownEmitter;
6671
private _outputFolder: string;
72+
private readonly _pluginLoader: PluginLoader;
6773

68-
public constructor(apiModel: ApiModel, pluginLoader: PluginLoader) {
74+
public constructor(apiModel: ApiModel, documenterConfig: DocumenterConfig | undefined) {
6975
this._apiModel = apiModel;
70-
this._pluginLoader = pluginLoader;
76+
this._documenterConfig = documenterConfig;
7177
this._tsdocConfiguration = CustomDocNodes.configuration;
7278
this._markdownEmitter = new CustomMarkdownEmitter(this._apiModel);
79+
80+
this._pluginLoader = new PluginLoader();
7381
}
7482

7583
public generateFiles(outputFolder: string): void {
7684
this._outputFolder = outputFolder;
7785

86+
if (this._documenterConfig) {
87+
this._pluginLoader.load(this._documenterConfig, () => {
88+
return new MarkdownDocumenterFeatureContext({
89+
apiModel: this._apiModel,
90+
outputFolder: outputFolder,
91+
documenter: new MarkdownDocumenterAccessor({
92+
getLinkForApiItem: (apiItem: ApiItem) => {
93+
return this._getLinkFilenameForApiItem(apiItem);
94+
}
95+
})
96+
});
97+
});
98+
}
99+
78100
console.log();
79101
this._deleteOldOutputFiles();
80102

81103
for (const apiPackage of this._apiModel.packages) {
82104
console.log(`Writing ${apiPackage.name} package`);
83105
this._writeApiItemPage(apiPackage);
84106
}
107+
108+
if (this._pluginLoader.markdownDocumenterFeature) {
109+
this._pluginLoader.markdownDocumenterFeature.onFinished({ });
110+
}
85111
}
86112

87113
private _writeApiItemPage(apiItem: ApiItem): void {

apps/api-documenter/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ export {
55
IFeatureDefinition,
66
IApiDocumenterPluginManifest
77
} from './plugin/IApiDocumenterPluginManifest';
8+
export { MarkdownDocumenterAccessor } from './plugin/MarkdownDocumenterAccessor';
89
export {
910
MarkdownDocumenterFeatureContext,
1011
IMarkdownDocumenterFeatureOnBeforeWritePageArgs,
12+
IMarkdownDocumenterFeatureOnFinishedArgs,
1113
MarkdownDocumenterFeature
1214
} from './plugin/MarkdownDocumenterFeature';
1315
export {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2+
// See LICENSE in the project root for license information.
3+
4+
import { ApiItem } from '@microsoft/api-extractor-model';
5+
6+
/** @internal */
7+
export interface IMarkdownDocumenterAccessorImplementation {
8+
getLinkForApiItem(apiItem: ApiItem): string | undefined;
9+
}
10+
11+
/**
12+
* Provides access to the documenter that is generating the output.
13+
*
14+
* @privateRemarks
15+
* This class is wrapper that provides access to the underlying MarkdownDocumenter, while hiding the implementation
16+
* details to ensure that the plugin API contract is stable.
17+
*
18+
* @public
19+
*/
20+
export class MarkdownDocumenterAccessor {
21+
private _implementation: IMarkdownDocumenterAccessorImplementation;
22+
23+
/** @internal */
24+
constructor (implementation: IMarkdownDocumenterAccessorImplementation) {
25+
this._implementation = implementation;
26+
}
27+
28+
/**
29+
* For a given `ApiItem`, return its markdown hyperlink.
30+
*
31+
* @returns The hyperlink, or `undefined` if the `ApiItem` object does not have a hyperlink.
32+
*/
33+
public getLinkForApiItem(apiItem: ApiItem): string | undefined {
34+
return this._implementation.getLinkForApiItem(apiItem);
35+
}
36+
}

apps/api-documenter/src/plugin/MarkdownDocumenterFeature.ts

+39-2
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,36 @@
22
// See LICENSE in the project root for license information.
33

44
import { PluginFeature } from './PluginFeature';
5-
import { ApiItem } from '@microsoft/api-extractor-model';
5+
import { ApiItem, ApiModel } from '@microsoft/api-extractor-model';
6+
import { MarkdownDocumenterAccessor } from './MarkdownDocumenterAccessor';
67

78
/**
89
* Context object for {@link MarkdownDocumenterFeature}.
910
*
1011
* @public
1112
*/
1213
export class MarkdownDocumenterFeatureContext {
14+
/** @internal */
15+
public constructor(options: MarkdownDocumenterFeatureContext) {
16+
this.apiModel = options.apiModel;
17+
this.outputFolder = options.outputFolder;
18+
this.documenter = options.documenter;
19+
}
20+
21+
/**
22+
* Provides access to the `ApiModel` for the documentation being generated.
23+
*/
24+
public readonly apiModel: ApiModel;
25+
26+
/**
27+
* The full path to the output folder.
28+
*/
29+
public readonly outputFolder: string;
1330

31+
/**
32+
* Exposes functionality of the documenter.
33+
*/
34+
public readonly documenter: MarkdownDocumenterAccessor;
1435
}
1536

1637
/**
@@ -34,6 +55,13 @@ export interface IMarkdownDocumenterFeatureOnBeforeWritePageArgs {
3455
readonly outputFilename: string;
3556
}
3657

58+
/**
59+
* Event arguments for MarkdownDocumenterFeature.onFinished()
60+
* @public
61+
*/
62+
export interface IMarkdownDocumenterFeatureOnFinishedArgs {
63+
}
64+
3765
/**
3866
* Inherit from this base class to implement an API Documenter plugin feature that customizes
3967
* the generation of markdown output.
@@ -44,10 +72,19 @@ export class MarkdownDocumenterFeature extends PluginFeature {
4472
public context: MarkdownDocumenterFeatureContext;
4573

4674
/**
47-
* This event function is called before writing a page.
75+
* This event occurs before each markdown file is written. It provides an opportunity to customize the
76+
* content of the file.
4877
* @virtual
4978
*/
5079
public onBeforeWritePage(eventArgs: IMarkdownDocumenterFeatureOnBeforeWritePageArgs): void {
5180
// (implemented by child class)
5281
}
82+
83+
/**
84+
* This event occurs after all output files have been written.
85+
* @virtual
86+
*/
87+
public onFinished(eventArgs: IMarkdownDocumenterFeatureOnFinishedArgs): void {
88+
// (implemented by child class)
89+
}
5390
}

apps/api-documenter/src/plugin/PluginLoader.ts

+5-7
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
import * as path from 'path';
55
import * as resolve from 'resolve';
66

7-
import { IConfigPlugin } from '../documenters/IConfigFile';
87
import { IApiDocumenterPluginManifest, IFeatureDefinition } from './IApiDocumenterPluginManifest';
98
import { MarkdownDocumenterFeature, MarkdownDocumenterFeatureContext } from './MarkdownDocumenterFeature';
109
import { PluginFeatureInitialization } from './PluginFeature';
10+
import { DocumenterConfig } from '../documenters/DocumenterConfig';
1111

1212
interface ILoadedPlugin {
1313
packageName: string;
@@ -17,9 +17,9 @@ interface ILoadedPlugin {
1717
export class PluginLoader {
1818
public markdownDocumenterFeature: MarkdownDocumenterFeature | undefined;
1919

20-
public load(configPlugins: IConfigPlugin[], configFilePath: string): void {
21-
const configFileFolder: string = path.dirname(configFilePath);
22-
for (const configPlugin of configPlugins) {
20+
public load(documenterConfig: DocumenterConfig, createContext: () => MarkdownDocumenterFeatureContext): void {
21+
const configFileFolder: string = path.dirname(documenterConfig.configFilePath);
22+
for (const configPlugin of (documenterConfig.configFile.plugins || [])) {
2323
try {
2424
// Look for the package name in the same place as the config file
2525
const resolvedEntryPointPath: string = resolve.sync(configPlugin.packageName, {
@@ -69,10 +69,8 @@ export class PluginLoader {
6969
throw new Error('A MarkdownDocumenterFeature is already loaded');
7070
}
7171

72-
const context: MarkdownDocumenterFeatureContext = new MarkdownDocumenterFeatureContext();
73-
7472
const initialization: PluginFeatureInitialization = new PluginFeatureInitialization();
75-
initialization._context = context;
73+
initialization._context = createContext();
7674

7775
let markdownDocumenterFeature: MarkdownDocumenterFeature | undefined = undefined;
7876
try {

common/reviews/api/api-documenter.api.md

+21
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
```ts
66

77
import { ApiItem } from '@microsoft/api-extractor-model';
8+
import { ApiModel } from '@microsoft/api-extractor-model';
89

910
// @public
1011
export interface IApiDocumenterPluginManifest {
@@ -28,16 +29,36 @@ export interface IMarkdownDocumenterFeatureOnBeforeWritePageArgs {
2829
pageContent: string;
2930
}
3031

32+
// @public
33+
export interface IMarkdownDocumenterFeatureOnFinishedArgs {
34+
}
35+
36+
// @public
37+
export class MarkdownDocumenterAccessor {
38+
// Warning: (ae-forgotten-export) The symbol "IMarkdownDocumenterAccessorImplementation" needs to be exported by the entry point index.d.ts
39+
//
40+
// @internal
41+
constructor(implementation: IMarkdownDocumenterAccessorImplementation);
42+
getLinkForApiItem(apiItem: ApiItem): string | undefined;
43+
}
44+
3145
// @public
3246
export class MarkdownDocumenterFeature extends PluginFeature {
3347
// (undocumented)
3448
context: MarkdownDocumenterFeatureContext;
3549
// @virtual
3650
onBeforeWritePage(eventArgs: IMarkdownDocumenterFeatureOnBeforeWritePageArgs): void;
51+
// @virtual
52+
onFinished(eventArgs: IMarkdownDocumenterFeatureOnFinishedArgs): void;
3753
}
3854

3955
// @public
4056
export class MarkdownDocumenterFeatureContext {
57+
// @internal
58+
constructor(options: MarkdownDocumenterFeatureContext);
59+
readonly apiModel: ApiModel;
60+
readonly documenter: MarkdownDocumenterAccessor;
61+
readonly outputFolder: string;
4162
}
4263

4364
// @public

0 commit comments

Comments
 (0)