Skip to content

Commit

Permalink
Add support for synonyms
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Dec 19, 2019
1 parent 9e0c30a commit eac58d3
Show file tree
Hide file tree
Showing 20 changed files with 1,156 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@microsoft/tsdoc-config",
"comment": "Add support for configuring synonyms.",
"type": "minor"
}
],
"packageName": "@microsoft/tsdoc-config",
"email": "ron.buckton@microsoft.com"
}
11 changes: 11 additions & 0 deletions common/changes/@microsoft/tsdoc/synonyms_2019-12-19-22-47.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@microsoft/tsdoc",
"comment": "Add support for synonyms and the 'see' tag.",
"type": "minor"
}
],
"packageName": "@microsoft/tsdoc",
"email": "ron.buckton@microsoft.com"
}
11 changes: 11 additions & 0 deletions common/changes/eslint-plugin-tsdoc/synonyms_2019-12-19-22-47.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "eslint-plugin-tsdoc",
"comment": "",
"type": "none"
}
],
"packageName": "eslint-plugin-tsdoc",
"email": "ron.buckton@microsoft.com"
}
56 changes: 55 additions & 1 deletion tsdoc-config/src/TSDocConfigFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,24 @@ interface ITagConfigJson {
tagName: string;
syntaxKind: 'inline' | 'block' | 'modifier';
allowMultiple?: boolean;
synonyms?: string[];
}

interface ISynonymConfigJson {
add?: ISynonymSetJson;
remove?: ISynonymSetJson;
}

interface ISynonymSetJson {
[tagName: string]: string[];
}

interface IConfigJson {
$schema: string;
tsdocVersion: string;
extends?: string[];
tagDefinitions: ITagConfigJson[];
synonyms?: ISynonymConfigJson;
}

/**
Expand All @@ -62,6 +73,8 @@ export class TSDocConfigFile {
private _tsdocSchema: string;
private readonly _extendsPaths: string[];
private readonly _tagDefinitions: TSDocTagDefinition[];
private readonly _synonymAdditions: Map<string, string[]>;
private readonly _synonymDeletions: Map<string, string[]>;

private constructor() {
this.log = new ParserMessageLog();
Expand All @@ -72,7 +85,9 @@ export class TSDocConfigFile {
this._hasErrors = false;
this._tsdocSchema = '';
this._extendsPaths = [];
this._tagDefinitions= [];
this._tagDefinitions = [];
this._synonymAdditions = new Map<string, string[]>();
this._synonymDeletions = new Map<string, string[]>();
}

/**
Expand Down Expand Up @@ -129,6 +144,14 @@ export class TSDocConfigFile {
return this._tagDefinitions;
}

public get synonymAdditions(): ReadonlyMap<string, ReadonlyArray<string>> {
return this._synonymAdditions;
}

public get synonymDeletions(): ReadonlyMap<string, ReadonlyArray<string>> {
return this._synonymDeletions;
}

private _reportError(parserMessageParameters: IParserMessageParameters): void {
this.log.addMessage(new ParserMessage(parserMessageParameters));
this._hasErrors = true;
Expand Down Expand Up @@ -181,9 +204,22 @@ export class TSDocConfigFile {
this._tagDefinitions.push(new TSDocTagDefinition({
tagName: jsonTagDefinition.tagName,
syntaxKind: syntaxKind,
synonyms: jsonTagDefinition.synonyms,
allowMultiple: jsonTagDefinition.allowMultiple
}));
}
if (configJson.synonyms) {
if (configJson.synonyms.add) {
for (const tagName of Object.keys(configJson.synonyms.add)) {
this._synonymAdditions.set(tagName, configJson.synonyms.add[tagName]);
}
}
if (configJson.synonyms.remove) {
for (const tagName of Object.keys(configJson.synonyms.remove)) {
this._synonymDeletions.set(tagName, configJson.synonyms.remove[tagName]);
}
}
}
}

private _loadWithExtends(configFilePath: string, referencingConfigFile: TSDocConfigFile | undefined,
Expand Down Expand Up @@ -329,5 +365,23 @@ export class TSDocConfigFile {
for (const tagDefinition of this.tagDefinitions) {
configuration.addTagDefinition(tagDefinition);
}

this.synonymDeletions.forEach((synonyms, tagName) => {
const tagDefinition: TSDocTagDefinition | undefined
= configuration.tryGetTagDefinition(tagName);
if (!tagDefinition) {
throw new Error(`A tag with the name ${tagName} could not be found.`);
}
configuration.removeSynonym(tagDefinition, ...synonyms);
});

this.synonymAdditions.forEach((synonyms, tagName) => {
const tagDefinition: TSDocTagDefinition | undefined
= configuration.tryGetTagDefinition(tagName);
if (!tagDefinition) {
throw new Error(`A tag with the name ${tagName} could not be found.`);
}
configuration.addSynonym(tagDefinition, ...synonyms);
});
}
}
72 changes: 72 additions & 0 deletions tsdoc-config/src/__tests__/TSDocConfigFile.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as path from 'path';

import { TSDocConfigFile } from '../TSDocConfigFile';
import { TSDocSynonymCollection } from '@microsoft/tsdoc/lib/configuration/TSDocSynonymCollection';

function getRelativePath(testPath: string): string {
return path
Expand All @@ -23,10 +24,32 @@ expect.addSnapshotSerializer({
extendsPaths: value.extendsPaths,
extendsFiles: value.extendsFiles,
tagDefinitions: value.tagDefinitions,
synonymAdditions: Array.from(value.synonymAdditions).reduce<Record<string, ReadonlyArray<string>>>(
(obj, [key, value]) => {
obj[key] = value;
return obj;
},
{}
),
synonymDeletions: Array.from(value.synonymDeletions).reduce<Record<string, ReadonlyArray<string>>>(
(obj, [key, value]) => {
obj[key] = value;
return obj;
},
{}
),
messages: value.log.messages
});
}
});
expect.addSnapshotSerializer({
test(value: unknown) {
return value instanceof TSDocSynonymCollection;
},
print(value: TSDocSynonymCollection, serialize: (value: unknown) => string, indent: (str: string) => string): string {
return serialize(value.synonyms);
}
});

function testLoadingFolder(assetPath: string): TSDocConfigFile {
return TSDocConfigFile.loadForFolder(path.join(__dirname, assetPath));
Expand All @@ -40,6 +63,8 @@ test('Load p1', () => {
"fileNotFound": false,
"filePath": "assets/p1/tsdoc.json",
"messages": Array [],
"synonymAdditions": Object {},
"synonymDeletions": Object {},
"tagDefinitions": Array [],
"tsdocSchema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
}
Expand All @@ -66,6 +91,8 @@ test('Load p2', () => {
"unformattedText": "File not found",
},
],
"synonymAdditions": Object {},
"synonymDeletions": Object {},
"tagDefinitions": Array [],
"tsdocSchema": "",
}
Expand All @@ -81,8 +108,11 @@ test('Load p3', () => {
"fileNotFound": false,
"filePath": "assets/p3/base1/tsdoc-base1.json",
"messages": Array [],
"synonymAdditions": Object {},
"synonymDeletions": Object {},
"tagDefinitions": Array [
TSDocTagDefinition {
"_synonymCollection": Array [],
"allowMultiple": false,
"standardization": "None",
"syntaxKind": 2,
Expand All @@ -98,8 +128,11 @@ test('Load p3', () => {
"fileNotFound": false,
"filePath": "assets/p3/base2/tsdoc-base2.json",
"messages": Array [],
"synonymAdditions": Object {},
"synonymDeletions": Object {},
"tagDefinitions": Array [
TSDocTagDefinition {
"_synonymCollection": Array [],
"allowMultiple": false,
"standardization": "None",
"syntaxKind": 2,
Expand All @@ -117,8 +150,11 @@ test('Load p3', () => {
"fileNotFound": false,
"filePath": "assets/p3/tsdoc.json",
"messages": Array [],
"synonymAdditions": Object {},
"synonymDeletions": Object {},
"tagDefinitions": Array [
TSDocTagDefinition {
"_synonymCollection": Array [],
"allowMultiple": false,
"standardization": "None",
"syntaxKind": 2,
Expand All @@ -140,8 +176,11 @@ test('Load p4', () => {
"fileNotFound": false,
"filePath": "assets/p4/node_modules/example-lib/dist/tsdoc-example.json",
"messages": Array [],
"synonymAdditions": Object {},
"synonymDeletions": Object {},
"tagDefinitions": Array [
TSDocTagDefinition {
"_synonymCollection": Array [],
"allowMultiple": false,
"standardization": "None",
"syntaxKind": 2,
Expand All @@ -158,8 +197,11 @@ test('Load p4', () => {
"fileNotFound": false,
"filePath": "assets/p4/tsdoc.json",
"messages": Array [],
"synonymAdditions": Object {},
"synonymDeletions": Object {},
"tagDefinitions": Array [
TSDocTagDefinition {
"_synonymCollection": Array [],
"allowMultiple": false,
"standardization": "None",
"syntaxKind": 2,
Expand All @@ -171,3 +213,33 @@ test('Load p4', () => {
}
`);
});
test('Load synonyms', () => {
expect(testLoadingFolder('assets/synonyms')).toMatchInlineSnapshot(`
Object {
"extendsFiles": Array [],
"extendsPaths": Array [],
"fileNotFound": false,
"filePath": "assets/synonyms/tsdoc.json",
"messages": Array [],
"synonymAdditions": Object {
"@readonly": Array [
"@readonly2",
],
},
"synonymDeletions": Object {},
"tagDefinitions": Array [
TSDocTagDefinition {
"_synonymCollection": Array [
"@bar",
],
"allowMultiple": false,
"standardization": "None",
"syntaxKind": 1,
"tagName": "@foo",
"tagNameWithUpperCase": "@FOO",
},
],
"tsdocSchema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
}
`);
});
2 changes: 2 additions & 0 deletions tsdoc-config/src/__tests__/assets/synonyms/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
11 changes: 11 additions & 0 deletions tsdoc-config/src/__tests__/assets/synonyms/tsdoc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
"tagDefinitions": [
{ "tagName": "@foo", "syntaxKind": "block", "synonyms": ["@bar"] }
],
"synonyms": {
"add": {
"@readonly": ["@readonly2"]
}
}
}
34 changes: 34 additions & 0 deletions tsdoc/schemas/tsdoc.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@
"items": {
"$ref": "#/definitions/tsdocTagDefinition"
}
},

"synonyms": {
"description": "Additional synonyms to add or remove from built-in tag definitions.",
"type": "object",
"properties": {
"add": {
"description": "Synonyms to add.",
"$ref": "#/definitions/synonymSet"
},
"remove": {
"description": "Synonyms to remove.",
"$ref": "#/definitions/synonymSet"
}
},
"additionalProperties": false
}
},
"required": [ "$schema" ],
Expand All @@ -44,10 +60,28 @@
"allowMultiple": {
"description": "If true, then this tag may appear multiple times in a doc comment. By default, a tag may only appear once.",
"type": "boolean"
},
"synonyms": {
"description": "Synonyms of the custom tag. TSDoc tag names start with an at-sign (@) followed by ASCII letters using camelCase capitalization.",
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["tagName", "syntaxKind"],
"additionalProperties": false
},
"synonymSet": {
"description": "Provides the assocation between a tag and the synonyms to be added or removed.",
"type": "object",
"additionalProperties": {
"description": "Synonyms of the tag. TSDoc tag names start with an at-sign (@) followed by ASCII letters using camelCase capitalization.",
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
15 changes: 15 additions & 0 deletions tsdoc/src/__tests__/ParsingBasics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
TSDocTagSyntaxKind
} from '../index';
import { TestHelpers } from '../parser/__tests__/TestHelpers';
import { StandardTags } from '../details/StandardTags';

test('01 Simple @beta and @internal extraction', () => {
const parserContext: ParserContext = TestHelpers.parseAndMatchDocCommentSnapshot([
Expand Down Expand Up @@ -111,3 +112,17 @@ test('04 typeParam blocks', () => {
' */'
].join('\n'));
});

test('05 synonyms', () => {
const configuration: TSDocConfiguration = new TSDocConfiguration();
configuration.addSynonym(StandardTags.readonly, "@readonly2");
TestHelpers.parseAndMatchDocCommentSnapshot([
'/**',
' * @param a - description1',
' * @arg b - description2',
' * @argument c - description3',
' * @return description4',
' * @readonly2',
' */'
].join('\n'), configuration);
});
Loading

0 comments on commit eac58d3

Please sign in to comment.