Skip to content

Commit 5874134

Browse files
committed
Add support for synonyms and the 'see' tag
1 parent 9e0c30a commit 5874134

20 files changed

+1185
-72
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/tsdoc-config",
5+
"comment": "Add support for configuring synonyms.",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@microsoft/tsdoc-config",
10+
"email": "[email protected]"
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/tsdoc",
5+
"comment": "Add support for synonyms and the 'see' tag.",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@microsoft/tsdoc",
10+
"email": "[email protected]"
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "eslint-plugin-tsdoc",
5+
"comment": "",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "eslint-plugin-tsdoc",
10+
"email": "[email protected]"
11+
}

tsdoc-config/src/TSDocConfigFile.ts

+55-1
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,24 @@ interface ITagConfigJson {
3131
tagName: string;
3232
syntaxKind: 'inline' | 'block' | 'modifier';
3333
allowMultiple?: boolean;
34+
synonyms?: string[];
35+
}
36+
37+
interface ISynonymConfigJson {
38+
add?: ISynonymSetJson;
39+
remove?: ISynonymSetJson;
40+
}
41+
42+
interface ISynonymSetJson {
43+
[tagName: string]: string[];
3444
}
3545

3646
interface IConfigJson {
3747
$schema: string;
3848
tsdocVersion: string;
3949
extends?: string[];
4050
tagDefinitions: ITagConfigJson[];
51+
synonyms?: ISynonymConfigJson;
4152
}
4253

4354
/**
@@ -62,6 +73,8 @@ export class TSDocConfigFile {
6273
private _tsdocSchema: string;
6374
private readonly _extendsPaths: string[];
6475
private readonly _tagDefinitions: TSDocTagDefinition[];
76+
private readonly _synonymAdditions: Map<string, string[]>;
77+
private readonly _synonymDeletions: Map<string, string[]>;
6578

6679
private constructor() {
6780
this.log = new ParserMessageLog();
@@ -72,7 +85,9 @@ export class TSDocConfigFile {
7285
this._hasErrors = false;
7386
this._tsdocSchema = '';
7487
this._extendsPaths = [];
75-
this._tagDefinitions= [];
88+
this._tagDefinitions = [];
89+
this._synonymAdditions = new Map<string, string[]>();
90+
this._synonymDeletions = new Map<string, string[]>();
7691
}
7792

7893
/**
@@ -129,6 +144,14 @@ export class TSDocConfigFile {
129144
return this._tagDefinitions;
130145
}
131146

147+
public get synonymAdditions(): ReadonlyMap<string, ReadonlyArray<string>> {
148+
return this._synonymAdditions;
149+
}
150+
151+
public get synonymDeletions(): ReadonlyMap<string, ReadonlyArray<string>> {
152+
return this._synonymDeletions;
153+
}
154+
132155
private _reportError(parserMessageParameters: IParserMessageParameters): void {
133156
this.log.addMessage(new ParserMessage(parserMessageParameters));
134157
this._hasErrors = true;
@@ -181,9 +204,22 @@ export class TSDocConfigFile {
181204
this._tagDefinitions.push(new TSDocTagDefinition({
182205
tagName: jsonTagDefinition.tagName,
183206
syntaxKind: syntaxKind,
207+
synonyms: jsonTagDefinition.synonyms,
184208
allowMultiple: jsonTagDefinition.allowMultiple
185209
}));
186210
}
211+
if (configJson.synonyms) {
212+
if (configJson.synonyms.add) {
213+
for (const tagName of Object.keys(configJson.synonyms.add)) {
214+
this._synonymAdditions.set(tagName, configJson.synonyms.add[tagName]);
215+
}
216+
}
217+
if (configJson.synonyms.remove) {
218+
for (const tagName of Object.keys(configJson.synonyms.remove)) {
219+
this._synonymDeletions.set(tagName, configJson.synonyms.remove[tagName]);
220+
}
221+
}
222+
}
187223
}
188224

189225
private _loadWithExtends(configFilePath: string, referencingConfigFile: TSDocConfigFile | undefined,
@@ -329,5 +365,23 @@ export class TSDocConfigFile {
329365
for (const tagDefinition of this.tagDefinitions) {
330366
configuration.addTagDefinition(tagDefinition);
331367
}
368+
369+
this.synonymDeletions.forEach((synonyms, tagName) => {
370+
const tagDefinition: TSDocTagDefinition | undefined
371+
= configuration.tryGetTagDefinition(tagName);
372+
if (!tagDefinition) {
373+
throw new Error(`A tag with the name ${tagName} could not be found.`);
374+
}
375+
configuration.removeSynonym(tagDefinition, ...synonyms);
376+
});
377+
378+
this.synonymAdditions.forEach((synonyms, tagName) => {
379+
const tagDefinition: TSDocTagDefinition | undefined
380+
= configuration.tryGetTagDefinition(tagName);
381+
if (!tagDefinition) {
382+
throw new Error(`A tag with the name ${tagName} could not be found.`);
383+
}
384+
configuration.addSynonym(tagDefinition, ...synonyms);
385+
});
332386
}
333387
}

tsdoc-config/src/__tests__/TSDocConfigFile.test.ts

+72
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as path from 'path';
22

33
import { TSDocConfigFile } from '../TSDocConfigFile';
4+
import { TSDocSynonymCollection } from '@microsoft/tsdoc/lib/configuration/TSDocSynonymCollection';
45

56
function getRelativePath(testPath: string): string {
67
return path
@@ -23,10 +24,32 @@ expect.addSnapshotSerializer({
2324
extendsPaths: value.extendsPaths,
2425
extendsFiles: value.extendsFiles,
2526
tagDefinitions: value.tagDefinitions,
27+
synonymAdditions: Array.from(value.synonymAdditions).reduce<Record<string, ReadonlyArray<string>>>(
28+
(obj, [key, value]) => {
29+
obj[key] = value;
30+
return obj;
31+
},
32+
{}
33+
),
34+
synonymDeletions: Array.from(value.synonymDeletions).reduce<Record<string, ReadonlyArray<string>>>(
35+
(obj, [key, value]) => {
36+
obj[key] = value;
37+
return obj;
38+
},
39+
{}
40+
),
2641
messages: value.log.messages
2742
});
2843
}
2944
});
45+
expect.addSnapshotSerializer({
46+
test(value: unknown) {
47+
return value instanceof TSDocSynonymCollection;
48+
},
49+
print(value: TSDocSynonymCollection, serialize: (value: unknown) => string, indent: (str: string) => string): string {
50+
return serialize(value.synonyms);
51+
}
52+
});
3053

3154
function testLoadingFolder(assetPath: string): TSDocConfigFile {
3255
return TSDocConfigFile.loadForFolder(path.join(__dirname, assetPath));
@@ -40,6 +63,8 @@ test('Load p1', () => {
4063
"fileNotFound": false,
4164
"filePath": "assets/p1/tsdoc.json",
4265
"messages": Array [],
66+
"synonymAdditions": Object {},
67+
"synonymDeletions": Object {},
4368
"tagDefinitions": Array [],
4469
"tsdocSchema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
4570
}
@@ -66,6 +91,8 @@ test('Load p2', () => {
6691
"unformattedText": "File not found",
6792
},
6893
],
94+
"synonymAdditions": Object {},
95+
"synonymDeletions": Object {},
6996
"tagDefinitions": Array [],
7097
"tsdocSchema": "",
7198
}
@@ -81,8 +108,11 @@ test('Load p3', () => {
81108
"fileNotFound": false,
82109
"filePath": "assets/p3/base1/tsdoc-base1.json",
83110
"messages": Array [],
111+
"synonymAdditions": Object {},
112+
"synonymDeletions": Object {},
84113
"tagDefinitions": Array [
85114
TSDocTagDefinition {
115+
"_synonymCollection": Array [],
86116
"allowMultiple": false,
87117
"standardization": "None",
88118
"syntaxKind": 2,
@@ -98,8 +128,11 @@ test('Load p3', () => {
98128
"fileNotFound": false,
99129
"filePath": "assets/p3/base2/tsdoc-base2.json",
100130
"messages": Array [],
131+
"synonymAdditions": Object {},
132+
"synonymDeletions": Object {},
101133
"tagDefinitions": Array [
102134
TSDocTagDefinition {
135+
"_synonymCollection": Array [],
103136
"allowMultiple": false,
104137
"standardization": "None",
105138
"syntaxKind": 2,
@@ -117,8 +150,11 @@ test('Load p3', () => {
117150
"fileNotFound": false,
118151
"filePath": "assets/p3/tsdoc.json",
119152
"messages": Array [],
153+
"synonymAdditions": Object {},
154+
"synonymDeletions": Object {},
120155
"tagDefinitions": Array [
121156
TSDocTagDefinition {
157+
"_synonymCollection": Array [],
122158
"allowMultiple": false,
123159
"standardization": "None",
124160
"syntaxKind": 2,
@@ -140,8 +176,11 @@ test('Load p4', () => {
140176
"fileNotFound": false,
141177
"filePath": "assets/p4/node_modules/example-lib/dist/tsdoc-example.json",
142178
"messages": Array [],
179+
"synonymAdditions": Object {},
180+
"synonymDeletions": Object {},
143181
"tagDefinitions": Array [
144182
TSDocTagDefinition {
183+
"_synonymCollection": Array [],
145184
"allowMultiple": false,
146185
"standardization": "None",
147186
"syntaxKind": 2,
@@ -158,8 +197,11 @@ test('Load p4', () => {
158197
"fileNotFound": false,
159198
"filePath": "assets/p4/tsdoc.json",
160199
"messages": Array [],
200+
"synonymAdditions": Object {},
201+
"synonymDeletions": Object {},
161202
"tagDefinitions": Array [
162203
TSDocTagDefinition {
204+
"_synonymCollection": Array [],
163205
"allowMultiple": false,
164206
"standardization": "None",
165207
"syntaxKind": 2,
@@ -171,3 +213,33 @@ test('Load p4', () => {
171213
}
172214
`);
173215
});
216+
test('Load synonyms', () => {
217+
expect(testLoadingFolder('assets/synonyms')).toMatchInlineSnapshot(`
218+
Object {
219+
"extendsFiles": Array [],
220+
"extendsPaths": Array [],
221+
"fileNotFound": false,
222+
"filePath": "assets/synonyms/tsdoc.json",
223+
"messages": Array [],
224+
"synonymAdditions": Object {
225+
"@readonly": Array [
226+
"@readonly2",
227+
],
228+
},
229+
"synonymDeletions": Object {},
230+
"tagDefinitions": Array [
231+
TSDocTagDefinition {
232+
"_synonymCollection": Array [
233+
"@bar",
234+
],
235+
"allowMultiple": false,
236+
"standardization": "None",
237+
"syntaxKind": 1,
238+
"tagName": "@foo",
239+
"tagNameWithUpperCase": "@FOO",
240+
},
241+
],
242+
"tsdocSchema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
243+
}
244+
`);
245+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{
2+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
3+
"tagDefinitions": [
4+
{ "tagName": "@foo", "syntaxKind": "block", "synonyms": ["@bar"] }
5+
],
6+
"synonyms": {
7+
"add": {
8+
"@readonly": ["@readonly2"]
9+
}
10+
}
11+
}

tsdoc/schemas/tsdoc.schema.json

+34
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@
2222
"items": {
2323
"$ref": "#/definitions/tsdocTagDefinition"
2424
}
25+
},
26+
27+
"synonyms": {
28+
"description": "Additional synonyms to add or remove from built-in tag definitions.",
29+
"type": "object",
30+
"properties": {
31+
"add": {
32+
"description": "Synonyms to add.",
33+
"$ref": "#/definitions/synonymSet"
34+
},
35+
"remove": {
36+
"description": "Synonyms to remove.",
37+
"$ref": "#/definitions/synonymSet"
38+
}
39+
},
40+
"additionalProperties": false
2541
}
2642
},
2743
"required": [ "$schema" ],
@@ -44,10 +60,28 @@
4460
"allowMultiple": {
4561
"description": "If true, then this tag may appear multiple times in a doc comment. By default, a tag may only appear once.",
4662
"type": "boolean"
63+
},
64+
"synonyms": {
65+
"description": "Synonyms of the custom tag. TSDoc tag names start with an at-sign (@) followed by ASCII letters using camelCase capitalization.",
66+
"type": "array",
67+
"items": {
68+
"type": "string"
69+
}
4770
}
4871
},
4972
"required": ["tagName", "syntaxKind"],
5073
"additionalProperties": false
74+
},
75+
"synonymSet": {
76+
"description": "Provides the assocation between a tag and the synonyms to be added or removed.",
77+
"type": "object",
78+
"additionalProperties": {
79+
"description": "Synonyms of the tag. TSDoc tag names start with an at-sign (@) followed by ASCII letters using camelCase capitalization.",
80+
"type": "array",
81+
"items": {
82+
"type": "string"
83+
}
84+
}
5185
}
5286
}
5387
}

tsdoc/src/__tests__/ParsingBasics.test.ts

+15
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
TSDocTagSyntaxKind
88
} from '../index';
99
import { TestHelpers } from '../parser/__tests__/TestHelpers';
10+
import { StandardTags } from '../details/StandardTags';
1011

1112
test('01 Simple @beta and @internal extraction', () => {
1213
const parserContext: ParserContext = TestHelpers.parseAndMatchDocCommentSnapshot([
@@ -111,3 +112,17 @@ test('04 typeParam blocks', () => {
111112
' */'
112113
].join('\n'));
113114
});
115+
116+
test('05 synonyms', () => {
117+
const configuration: TSDocConfiguration = new TSDocConfiguration();
118+
configuration.addSynonym(StandardTags.readonly, "@readonly2");
119+
TestHelpers.parseAndMatchDocCommentSnapshot([
120+
'/**',
121+
' * @param a - description1',
122+
' * @arg b - description2',
123+
' * @argument c - description3',
124+
' * @return description4',
125+
' * @readonly2',
126+
' */'
127+
].join('\n'), configuration);
128+
});

0 commit comments

Comments
 (0)