From 0f23c1b565ac2ce25f525440148c59ef1d05a9d7 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Sun, 17 Jan 2021 16:40:22 -0800 Subject: [PATCH 1/8] Implement "supportForTags" field in tsdoc.json --- tsdoc-config/src/TSDocConfigFile.ts | 31 ++++++++++++++++++- .../src/__tests__/TSDocConfigFile.test.ts | 23 ++++++++++++++ .../assets/p3/base1/tsdoc-base1.json | 5 ++- .../assets/p3/base2/tsdoc-base2.json | 5 ++- .../src/__tests__/assets/p3/tsdoc.json | 5 ++- tsdoc/schemas/tsdoc.schema.json | 11 +++++++ tsdoc/src/parser/TSDocMessageId.ts | 7 +++++ 7 files changed, 83 insertions(+), 4 deletions(-) diff --git a/tsdoc-config/src/TSDocConfigFile.ts b/tsdoc-config/src/TSDocConfigFile.ts index 51a2e340..845e94a3 100644 --- a/tsdoc-config/src/TSDocConfigFile.ts +++ b/tsdoc-config/src/TSDocConfigFile.ts @@ -38,7 +38,8 @@ interface IConfigJson { $schema: string; tsdocVersion: string; extends?: string[]; - tagDefinitions: ITagConfigJson[]; + tagDefinitions?: ITagConfigJson[]; + supportForTags?: { [tagName: string]: boolean }; } /** @@ -64,6 +65,7 @@ export class TSDocConfigFile { private _tsdocSchema: string; private readonly _extendsPaths: string[]; private readonly _tagDefinitions: TSDocTagDefinition[]; + private readonly _supportForTags: Map; private constructor() { this.log = new ParserMessageLog(); @@ -76,6 +78,7 @@ export class TSDocConfigFile { this._tsdocSchema = ''; this._extendsPaths = []; this._tagDefinitions = []; + this._supportForTags = new Map(); } /** @@ -132,6 +135,10 @@ export class TSDocConfigFile { return this._tagDefinitions; } + public get supportForTags(): ReadonlyMap { + return this._supportForTags; + } + /** * This can be used for cache eviction. It returns true if the modification timestamp has changed for * any of the files that were read when loading this `TSDocConfigFile`, which indicates that the file should be @@ -238,6 +245,14 @@ export class TSDocConfigFile { }) ); } + + if (configJson.supportForTags) { + for (const tagName of Object.keys(configJson.supportForTags)) { + const supported: boolean = configJson.supportForTags[tagName]; + + this._supportForTags.set(tagName, supported); + } + } } private _loadWithExtends( @@ -398,5 +413,19 @@ export class TSDocConfigFile { for (const tagDefinition of this.tagDefinitions) { configuration.addTagDefinition(tagDefinition); } + + this.supportForTags.forEach((supported: boolean, tagName: string) => { + const tagDefinition: TSDocTagDefinition | undefined = configuration.tryGetTagDefinition(tagName); + if (tagDefinition) { + // Note that setSupportForTag() automatically enables configuration.validation.reportUnsupportedTags + configuration.setSupportForTag(tagDefinition, supported); + } else { + this._reportError({ + messageId: TSDocMessageId.ConfigFileUndefinedTag, + messageText: `The "supportForTags" field refers to an undefined tag ${JSON.stringify(tagName)}.`, + textRange: TextRange.empty, + }); + } + }); } } diff --git a/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts b/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts index 33854d0c..4454da72 100644 --- a/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts +++ b/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts @@ -21,6 +21,7 @@ expect.addSnapshotSerializer({ extendsPaths: configFile.extendsPaths, extendsFiles: configFile.extendsFiles, tagDefinitions: configFile.tagDefinitions, + supportForTags: Array.from(configFile.supportForTags).map(([tagName, supported]) => ({ tagName, supported })), messages: configFile.log.messages, }); }, @@ -38,6 +39,7 @@ test('Load p1', () => { "fileNotFound": false, "filePath": "assets/p1/tsdoc.json", "messages": Array [], + "supportForTags": Array [], "tagDefinitions": Array [], "tsdocSchema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", } @@ -64,6 +66,7 @@ test('Load p2', () => { "unformattedText": "File not found", }, ], + "supportForTags": Array [], "tagDefinitions": Array [], "tsdocSchema": "", } @@ -79,6 +82,12 @@ test('Load p3', () => { "fileNotFound": false, "filePath": "assets/p3/base1/tsdoc-base1.json", "messages": Array [], + "supportForTags": Array [ + Object { + "supported": true, + "tagName": "@base1", + }, + ], "tagDefinitions": Array [ TSDocTagDefinition { "allowMultiple": false, @@ -96,6 +105,12 @@ test('Load p3', () => { "fileNotFound": false, "filePath": "assets/p3/base2/tsdoc-base2.json", "messages": Array [], + "supportForTags": Array [ + Object { + "supported": false, + "tagName": "@base2", + }, + ], "tagDefinitions": Array [ TSDocTagDefinition { "allowMultiple": false, @@ -115,6 +130,12 @@ test('Load p3', () => { "fileNotFound": false, "filePath": "assets/p3/tsdoc.json", "messages": Array [], + "supportForTags": Array [ + Object { + "supported": true, + "tagName": "@base2", + }, + ], "tagDefinitions": Array [ TSDocTagDefinition { "allowMultiple": false, @@ -138,6 +159,7 @@ test('Load p4', () => { "fileNotFound": false, "filePath": "assets/p4/node_modules/example-lib/dist/tsdoc-example.json", "messages": Array [], + "supportForTags": Array [], "tagDefinitions": Array [ TSDocTagDefinition { "allowMultiple": false, @@ -156,6 +178,7 @@ test('Load p4', () => { "fileNotFound": false, "filePath": "assets/p4/tsdoc.json", "messages": Array [], + "supportForTags": Array [], "tagDefinitions": Array [ TSDocTagDefinition { "allowMultiple": false, diff --git a/tsdoc-config/src/__tests__/assets/p3/base1/tsdoc-base1.json b/tsdoc-config/src/__tests__/assets/p3/base1/tsdoc-base1.json index c2f4560a..cbfbe9c2 100644 --- a/tsdoc-config/src/__tests__/assets/p3/base1/tsdoc-base1.json +++ b/tsdoc-config/src/__tests__/assets/p3/base1/tsdoc-base1.json @@ -5,5 +5,8 @@ "tagName": "@base1", "syntaxKind": "modifier" } - ] + ], + "supportForTags": { + "@base1": true + } } diff --git a/tsdoc-config/src/__tests__/assets/p3/base2/tsdoc-base2.json b/tsdoc-config/src/__tests__/assets/p3/base2/tsdoc-base2.json index 52c66531..e0739f32 100644 --- a/tsdoc-config/src/__tests__/assets/p3/base2/tsdoc-base2.json +++ b/tsdoc-config/src/__tests__/assets/p3/base2/tsdoc-base2.json @@ -5,5 +5,8 @@ "tagName": "@base2", "syntaxKind": "modifier" } - ] + ], + "supportForTags": { + "@base2": false + } } diff --git a/tsdoc-config/src/__tests__/assets/p3/tsdoc.json b/tsdoc-config/src/__tests__/assets/p3/tsdoc.json index 8afb55e1..f92d2666 100644 --- a/tsdoc-config/src/__tests__/assets/p3/tsdoc.json +++ b/tsdoc-config/src/__tests__/assets/p3/tsdoc.json @@ -6,5 +6,8 @@ "tagName": "@root", "syntaxKind": "modifier" } - ] + ], + "supportForTags": { + "@base2": true + } } diff --git a/tsdoc/schemas/tsdoc.schema.json b/tsdoc/schemas/tsdoc.schema.json index e8638a48..08c114b0 100644 --- a/tsdoc/schemas/tsdoc.schema.json +++ b/tsdoc/schemas/tsdoc.schema.json @@ -22,6 +22,17 @@ "items": { "$ref": "#/definitions/tsdocTagDefinition" } + }, + + "supportForTags": { + "description": "A collection of key/value pairs. The key is a TSDoc tag name (e.g. \"@myTag\") that must be defined in this configuration. The value is a boolean indicating whether the tag is supported. The TSDoc parser may report warnings when unsupported tags are encountered. If \"supportForTags\" is specified for at least one tag, then the \"reportUnsupportedTags\" validation check is enabled by default.", + "type": "object", + "patternProperties": { + "@[a-zA-Z][a-zA-Z0-9]*$": { + "type": "boolean" + } + }, + "additionalItems": false } }, "required": ["$schema"], diff --git a/tsdoc/src/parser/TSDocMessageId.ts b/tsdoc/src/parser/TSDocMessageId.ts index 232cb88e..2aeccb60 100644 --- a/tsdoc/src/parser/TSDocMessageId.ts +++ b/tsdoc/src/parser/TSDocMessageId.ts @@ -46,6 +46,11 @@ export const enum TSDocMessageId { */ ConfigFileUnresolvedExtends = 'tsdoc-config-unresolved-extends', + /** + * The "supportForTags" field refers to an undefined tag "___". + */ + ConfigFileUndefinedTag = 'tsdoc-config-undefined-tag', + /** * Expecting a `/**` comment. * Unexpected end of input. @@ -389,6 +394,8 @@ export const allTsdocMessageIds: string[] = [ 'tsdoc-config-schema-error', 'tsdoc-config-cyclic-extends', 'tsdoc-config-unresolved-extends', + 'tsdoc-config-undefined-tag', + 'tsdoc-comment-not-found', 'tsdoc-comment-missing-opening-delimiter', 'tsdoc-comment-missing-closing-delimiter', From 612763b1fa24261c545877ad10c5acf7ba662e0f Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Sun, 17 Jan 2021 17:56:27 -0800 Subject: [PATCH 2/8] Implement TSDocConfigFile.saveFile() and saveToObject() --- tsdoc-config/src/TSDocConfigFile.ts | 61 ++++++++++++++++++- .../src/__tests__/TSDocConfigFile.test.ts | 21 +++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/tsdoc-config/src/TSDocConfigFile.ts b/tsdoc-config/src/TSDocConfigFile.ts index 845e94a3..785b80a6 100644 --- a/tsdoc-config/src/TSDocConfigFile.ts +++ b/tsdoc-config/src/TSDocConfigFile.ts @@ -36,7 +36,6 @@ interface ITagConfigJson { interface IConfigJson { $schema: string; - tsdocVersion: string; extends?: string[]; tagDefinitions?: ITagConfigJson[]; supportForTags?: { [tagName: string]: boolean }; @@ -372,6 +371,66 @@ export class TSDocConfigFile { return configFile; } + /** + * Writes the config file content to a JSON file with the specified file path. + */ + public saveFile(jsonFilePath: string): void { + const jsonObject: unknown = this.saveToObject(); + const jsonContent: string = JSON.stringify(jsonObject, undefined, 2); + fs.writeFileSync(jsonFilePath, jsonContent); + } + + /** + * Writes the object state into a JSON-serializable object. + */ + public saveToObject(): unknown { + const configJson: IConfigJson = { + $schema: TSDocConfigFile.CURRENT_SCHEMA_URL, + }; + + if (this.tagDefinitions.length > 0) { + configJson.tagDefinitions = []; + for (const tagDefinition of this.tagDefinitions) { + configJson.tagDefinitions.push(TSDocConfigFile._serializeTagDefinition(tagDefinition)); + } + } + + if (this.supportForTags.size > 0) { + configJson.supportForTags = {}; + this.supportForTags.forEach((supported, tagName) => { + configJson.supportForTags![tagName] = supported; + }); + } + + return configJson; + } + + private static _serializeTagDefinition(tagDefinition: TSDocTagDefinition): ITagConfigJson { + let syntaxKind: 'inline' | 'block' | 'modifier' | undefined; + switch (tagDefinition.syntaxKind) { + case TSDocTagSyntaxKind.InlineTag: + syntaxKind = 'inline'; + break; + case TSDocTagSyntaxKind.BlockTag: + syntaxKind = 'block'; + break; + case TSDocTagSyntaxKind.ModifierTag: + syntaxKind = 'modifier'; + break; + default: + throw new Error('Unimplemented TSDocTagSyntaxKind'); + } + + const tagConfigJson: ITagConfigJson = { + tagName: tagDefinition.tagName, + syntaxKind, + }; + if (tagDefinition.allowMultiple) { + tagConfigJson.allowMultiple = true; + } + return tagConfigJson; + } + /** * Returns a report of any errors that occurred while attempting to load this file or any files * referenced via the "extends" field. diff --git a/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts b/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts index 4454da72..ed49c4d9 100644 --- a/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts +++ b/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts @@ -45,6 +45,7 @@ test('Load p1', () => { } `); }); + test('Load p2', () => { expect(testLoadingFolder('assets/p2')).toMatchInlineSnapshot(` Object { @@ -72,6 +73,7 @@ test('Load p2', () => { } `); }); + test('Load p3', () => { expect(testLoadingFolder('assets/p3')).toMatchInlineSnapshot(` Object { @@ -149,6 +151,7 @@ test('Load p3', () => { } `); }); + test('Load p4', () => { expect(testLoadingFolder('assets/p4')).toMatchInlineSnapshot(` Object { @@ -192,3 +195,21 @@ test('Load p4', () => { } `); }); + +test('Re-serialize p2', () => { + const configFile: TSDocConfigFile = TSDocConfigFile.loadForFolder(path.join(__dirname, 'assets/p3')); + expect(configFile.saveToObject()).toMatchInlineSnapshot(` + Object { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "supportForTags": Object { + "@base2": true, + }, + "tagDefinitions": Array [ + Object { + "syntaxKind": "modifier", + "tagName": "@root", + }, + ], + } + `); +}); From 8013d9162614cee911790a4533a6e5087a1a33ff Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Sun, 17 Jan 2021 18:34:54 -0800 Subject: [PATCH 3/8] Implement TSDocConfigFile.loadFromParser() and some methods for modifying the TSDocConfigFile state --- tsdoc-config/src/TSDocConfigFile.ts | 103 ++++++- .../src/__tests__/TSDocConfigFile.test.ts | 256 ++++++++++++++++++ tsdoc/src/configuration/TSDocTagDefinition.ts | 7 + tsdoc/src/parser/TSDocMessageId.ts | 17 ++ 4 files changed, 376 insertions(+), 7 deletions(-) diff --git a/tsdoc-config/src/TSDocConfigFile.ts b/tsdoc-config/src/TSDocConfigFile.ts index 785b80a6..308e135a 100644 --- a/tsdoc-config/src/TSDocConfigFile.ts +++ b/tsdoc-config/src/TSDocConfigFile.ts @@ -7,12 +7,14 @@ import { ParserMessage, TextRange, IParserMessageParameters, + ITSDocTagDefinitionParameters, } from '@microsoft/tsdoc'; import * as fs from 'fs'; import * as resolve from 'resolve'; import * as path from 'path'; import Ajv from 'ajv'; import * as jju from 'jju'; +import { config } from 'process'; const ajv: Ajv.Ajv = new Ajv({ verbose: true }); @@ -64,6 +66,7 @@ export class TSDocConfigFile { private _tsdocSchema: string; private readonly _extendsPaths: string[]; private readonly _tagDefinitions: TSDocTagDefinition[]; + private readonly _tagDefinitionNames: Set; private readonly _supportForTags: Map; private constructor() { @@ -77,6 +80,7 @@ export class TSDocConfigFile { this._tsdocSchema = ''; this._extendsPaths = []; this._tagDefinitions = []; + this._tagDefinitionNames = new Set(); this._supportForTags = new Map(); } @@ -138,6 +142,71 @@ export class TSDocConfigFile { return this._supportForTags; } + /** + * Removes all items from the `tagDefinitions` array. + */ + public clearTagDefinitions(): void { + this._tagDefinitions.length = 0; + this._tagDefinitionNames.clear(); + } + + /** + * Adds a new item to the `tagDefinitions` array. + */ + public addTagDefinition(parameters: ITSDocTagDefinitionParameters): void { + // This validates the tag name + const tagDefinition: TSDocTagDefinition = new TSDocTagDefinition(parameters); + + if (this._tagDefinitionNames.has(tagDefinition.tagNameWithUpperCase)) { + throw new Error(`A tag defintion was already added with the tag name "${parameters.tagName}"`); + } + this._tagDefinitionNames.add(tagDefinition.tagName); + + this._tagDefinitions.push(tagDefinition); + } + + // Similar to addTagDefinition() but reports errors using _reportError() + private _addTagDefinitionForLoad(parameters: ITSDocTagDefinitionParameters): void { + let tagDefinition: TSDocTagDefinition; + try { + // This validates the tag name + tagDefinition = new TSDocTagDefinition(parameters); + } catch (error) { + this._reportError({ + messageId: TSDocMessageId.ConfigFileInvalidTagName, + messageText: error.message, + textRange: TextRange.empty, + }); + return; + } + + if (this._tagDefinitionNames.has(tagDefinition.tagNameWithUpperCase)) { + this._reportError({ + messageId: TSDocMessageId.ConfigFileDuplicateTagName, + messageText: `The "tagDefinitions" field specifies more than one tag with the name "${parameters.tagName}"`, + textRange: TextRange.empty, + }); + } + this._tagDefinitionNames.add(tagDefinition.tagName); + + this._tagDefinitions.push(tagDefinition); + } + + /** + * Removes all entries from the "supportForTags" map. + */ + public clearSupportForTags(): void { + this._supportForTags.clear(); + } + + /** + * Sets an entry in the "supportForTags" map. + */ + public setSupportForTag(tagName: string, supported: boolean): void { + TSDocTagDefinition.validateTSDocTagName(tagName); + this._supportForTags.set(tagName, supported); + } + /** * This can be used for cache eviction. It returns true if the modification timestamp has changed for * any of the files that were read when loading this `TSDocConfigFile`, which indicates that the file should be @@ -236,13 +305,12 @@ export class TSDocConfigFile { // The JSON schema should have caught this error throw new Error('Unexpected tag kind'); } - this._tagDefinitions.push( - new TSDocTagDefinition({ - tagName: jsonTagDefinition.tagName, - syntaxKind: syntaxKind, - allowMultiple: jsonTagDefinition.allowMultiple, - }) - ); + + this._addTagDefinitionForLoad({ + tagName: jsonTagDefinition.tagName, + syntaxKind: syntaxKind, + allowMultiple: jsonTagDefinition.allowMultiple, + }); } if (configJson.supportForTags) { @@ -371,6 +439,27 @@ export class TSDocConfigFile { return configFile; } + /** + * Initializes a TSDocConfigFile object using the state from the provided `TSDocConfiguration` object. + */ + public static loadFromParser(configuration: TSDocConfiguration): TSDocConfigFile { + const configFile: TSDocConfigFile = new TSDocConfigFile(); + + for (const tagDefinition of configuration.tagDefinitions) { + configFile.addTagDefinition({ + syntaxKind: tagDefinition.syntaxKind, + tagName: tagDefinition.tagName, + allowMultiple: tagDefinition.allowMultiple, + }); + } + + for (const tagDefinition of configuration.supportedTagDefinitions) { + configFile.setSupportForTag(tagDefinition.tagName, true); + } + + return configFile; + } + /** * Writes the config file content to a JSON file with the specified file path. */ diff --git a/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts b/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts index ed49c4d9..dcd3865a 100644 --- a/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts +++ b/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts @@ -1,4 +1,6 @@ +import { TSDocConfiguration } from '@microsoft/tsdoc'; import * as path from 'path'; +import { config } from 'process'; import { TSDocConfigFile } from '../TSDocConfigFile'; @@ -198,6 +200,7 @@ test('Load p4', () => { test('Re-serialize p2', () => { const configFile: TSDocConfigFile = TSDocConfigFile.loadForFolder(path.join(__dirname, 'assets/p3')); + // This is the data from p3/tsdoc.json, ignoring its "extends" field. expect(configFile.saveToObject()).toMatchInlineSnapshot(` Object { "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", @@ -212,4 +215,257 @@ test('Re-serialize p2', () => { ], } `); + + const configuration: TSDocConfiguration = new TSDocConfiguration(); + + const defaultsConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromParser(configuration); + // This is the default configuration created by the TSDocConfigFile constructor. + expect(defaultsConfigFile.saveToObject()).toMatchInlineSnapshot(` + Object { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "tagDefinitions": Array [ + Object { + "syntaxKind": "modifier", + "tagName": "@alpha", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@beta", + }, + Object { + "syntaxKind": "block", + "tagName": "@defaultValue", + }, + Object { + "allowMultiple": true, + "syntaxKind": "block", + "tagName": "@decorator", + }, + Object { + "syntaxKind": "block", + "tagName": "@deprecated", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@eventProperty", + }, + Object { + "allowMultiple": true, + "syntaxKind": "block", + "tagName": "@example", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@experimental", + }, + Object { + "syntaxKind": "inline", + "tagName": "@inheritDoc", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@internal", + }, + Object { + "syntaxKind": "inline", + "tagName": "@label", + }, + Object { + "allowMultiple": true, + "syntaxKind": "inline", + "tagName": "@link", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@override", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@packageDocumentation", + }, + Object { + "allowMultiple": true, + "syntaxKind": "block", + "tagName": "@param", + }, + Object { + "syntaxKind": "block", + "tagName": "@privateRemarks", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@public", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@readonly", + }, + Object { + "syntaxKind": "block", + "tagName": "@remarks", + }, + Object { + "syntaxKind": "block", + "tagName": "@returns", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@sealed", + }, + Object { + "syntaxKind": "block", + "tagName": "@see", + }, + Object { + "allowMultiple": true, + "syntaxKind": "block", + "tagName": "@throws", + }, + Object { + "allowMultiple": true, + "syntaxKind": "block", + "tagName": "@typeParam", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@virtual", + }, + ], + } + `); + + configFile.configureParser(configuration); + const roundtripConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromParser(configuration); + + // This is the result of merging p3/tsdoc.json, tsdoc-base1.json, tsdoc-base2.json, and + // the TSDocConfiguration defaults. + expect(roundtripConfigFile.saveToObject()).toMatchInlineSnapshot(` + Object { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "supportForTags": Object { + "@base1": true, + "@base2": true, + }, + "tagDefinitions": Array [ + Object { + "syntaxKind": "modifier", + "tagName": "@alpha", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@beta", + }, + Object { + "syntaxKind": "block", + "tagName": "@defaultValue", + }, + Object { + "allowMultiple": true, + "syntaxKind": "block", + "tagName": "@decorator", + }, + Object { + "syntaxKind": "block", + "tagName": "@deprecated", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@eventProperty", + }, + Object { + "allowMultiple": true, + "syntaxKind": "block", + "tagName": "@example", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@experimental", + }, + Object { + "syntaxKind": "inline", + "tagName": "@inheritDoc", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@internal", + }, + Object { + "syntaxKind": "inline", + "tagName": "@label", + }, + Object { + "allowMultiple": true, + "syntaxKind": "inline", + "tagName": "@link", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@override", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@packageDocumentation", + }, + Object { + "allowMultiple": true, + "syntaxKind": "block", + "tagName": "@param", + }, + Object { + "syntaxKind": "block", + "tagName": "@privateRemarks", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@public", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@readonly", + }, + Object { + "syntaxKind": "block", + "tagName": "@remarks", + }, + Object { + "syntaxKind": "block", + "tagName": "@returns", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@sealed", + }, + Object { + "syntaxKind": "block", + "tagName": "@see", + }, + Object { + "allowMultiple": true, + "syntaxKind": "block", + "tagName": "@throws", + }, + Object { + "allowMultiple": true, + "syntaxKind": "block", + "tagName": "@typeParam", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@virtual", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@base1", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@base2", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@root", + }, + ], + } + `); }); diff --git a/tsdoc/src/configuration/TSDocTagDefinition.ts b/tsdoc/src/configuration/TSDocTagDefinition.ts index 8c827421..49d90a34 100644 --- a/tsdoc/src/configuration/TSDocTagDefinition.ts +++ b/tsdoc/src/configuration/TSDocTagDefinition.ts @@ -81,4 +81,11 @@ export class TSDocTagDefinition { (parameters as ITSDocTagDefinitionInternalParameters).standardization || Standardization.None; this.allowMultiple = !!parameters.allowMultiple; } + + /** + * Throws an exception if `tagName` is not a valid TSDoc tag name. + */ + public static validateTSDocTagName(tagName: string): void { + StringChecks.validateTSDocTagName(tagName); + } } diff --git a/tsdoc/src/parser/TSDocMessageId.ts b/tsdoc/src/parser/TSDocMessageId.ts index 2aeccb60..050c1660 100644 --- a/tsdoc/src/parser/TSDocMessageId.ts +++ b/tsdoc/src/parser/TSDocMessageId.ts @@ -48,9 +48,25 @@ export const enum TSDocMessageId { /** * The "supportForTags" field refers to an undefined tag "___". + * @remarks + * Reported by the `@microsoft/tsdoc-config` package when loading the tsdoc.json config file. */ ConfigFileUndefinedTag = 'tsdoc-config-undefined-tag', + /** + * The "tagDefinitions" field specifies more than one tag with the name "___". + * @remarks + * Reported by the `@microsoft/tsdoc-config` package when loading the tsdoc.json config file. + */ + ConfigFileDuplicateTagName = 'tsdoc-config-duplicate-tag-name', + + /** + * A TSDoc tag name must start with a letter and contain only letters and numbers. + * @remarks + * Reported by the `@microsoft/tsdoc-config` package when loading the tsdoc.json config file. + */ + ConfigFileInvalidTagName = 'tsdoc-config-invalid-tag-name', + /** * Expecting a `/**` comment. * Unexpected end of input. @@ -395,6 +411,7 @@ export const allTsdocMessageIds: string[] = [ 'tsdoc-config-cyclic-extends', 'tsdoc-config-unresolved-extends', 'tsdoc-config-undefined-tag', + 'tsdoc-config-duplicate-tag-name', 'tsdoc-comment-not-found', 'tsdoc-comment-missing-opening-delimiter', From 83ff3d39250bef38620e0d4a05f685981e683bb6 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Sun, 17 Jan 2021 18:49:09 -0800 Subject: [PATCH 4/8] Implement TSDocConfiguration.clear() --- .../src/__tests__/TSDocConfigFile.test.ts | 58 +++++++++++++++++-- tsdoc/src/configuration/TSDocConfiguration.ts | 21 ++++++- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts b/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts index dcd3865a..a60dd422 100644 --- a/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts +++ b/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts @@ -215,10 +215,56 @@ test('Re-serialize p2', () => { ], } `); +}); + +test('Re-serialize p2 without defaults', () => { + const parserConfiguration: TSDocConfiguration = new TSDocConfiguration(); + parserConfiguration.clear(true); + + const defaultsConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromParser(parserConfiguration); + // This is the default configuration created by the TSDocConfigFile constructor. + expect(defaultsConfigFile.saveToObject()).toMatchInlineSnapshot(` + Object { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + } + `); + + const configFile: TSDocConfigFile = TSDocConfigFile.loadForFolder(path.join(__dirname, 'assets/p3')); + configFile.configureParser(parserConfiguration); + + const mergedConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromParser(parserConfiguration); + + // This is the result of merging p3/tsdoc.json, tsdoc-base1.json, tsdoc-base2.json, and + // the TSDocConfiguration defaults. + expect(mergedConfigFile.saveToObject()).toMatchInlineSnapshot(` + Object { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "supportForTags": Object { + "@base1": true, + "@base2": true, + }, + "tagDefinitions": Array [ + Object { + "syntaxKind": "modifier", + "tagName": "@base1", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@base2", + }, + Object { + "syntaxKind": "modifier", + "tagName": "@root", + }, + ], + } + `); +}); - const configuration: TSDocConfiguration = new TSDocConfiguration(); +test('Re-serialize p2 with defaults', () => { + const parserConfiguration: TSDocConfiguration = new TSDocConfiguration(); - const defaultsConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromParser(configuration); + const defaultsConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromParser(parserConfiguration); // This is the default configuration created by the TSDocConfigFile constructor. expect(defaultsConfigFile.saveToObject()).toMatchInlineSnapshot(` Object { @@ -334,12 +380,14 @@ test('Re-serialize p2', () => { } `); - configFile.configureParser(configuration); - const roundtripConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromParser(configuration); + const configFile: TSDocConfigFile = TSDocConfigFile.loadForFolder(path.join(__dirname, 'assets/p3')); + configFile.configureParser(parserConfiguration); + + const mergedConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromParser(parserConfiguration); // This is the result of merging p3/tsdoc.json, tsdoc-base1.json, tsdoc-base2.json, and // the TSDocConfiguration defaults. - expect(roundtripConfigFile.saveToObject()).toMatchInlineSnapshot(` + expect(mergedConfigFile.saveToObject()).toMatchInlineSnapshot(` Object { "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", "supportForTags": Object { diff --git a/tsdoc/src/configuration/TSDocConfiguration.ts b/tsdoc/src/configuration/TSDocConfiguration.ts index 5598f5d7..f4760458 100644 --- a/tsdoc/src/configuration/TSDocConfiguration.ts +++ b/tsdoc/src/configuration/TSDocConfiguration.ts @@ -22,13 +22,30 @@ export class TSDocConfiguration { this._validation = new TSDocValidationConfiguration(); this._docNodeManager = new DocNodeManager(); - // Define all the standard tags - this.addTagDefinitions(StandardTags.allDefinitions); + this.clear(false); // Register the built-in node kinds BuiltInDocNodes.register(this); } + /** + * Resets the `TSDocConfiguration` object to its initial empty state. + * @param noStandardTags - The `TSDocConfiguration` constructor normally adds definitions for the + * standard TSDoc tags. Set `noStandardTags` to true for a completely empty `tagDefinitions` collection. + */ + public clear(noStandardTags: boolean = false): void { + this._tagDefinitions.length = 0; + this._tagDefinitionsByName.clear(); + this._supportedTagDefinitions.clear(); + this._validation.ignoreUndefinedTags = false; + this._validation.reportUnsupportedTags = false; + + if (!noStandardTags) { + // Define all the standard tags + this.addTagDefinitions(StandardTags.allDefinitions); + } + } + /** * The TSDoc tags that are defined in this configuration. * From d93b8ee73ef6792bb95371f50eb6090ad60eda25 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Sun, 17 Jan 2021 19:20:46 -0800 Subject: [PATCH 5/8] Add missing item to allTsdocMessageIds --- tsdoc/src/parser/TSDocMessageId.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tsdoc/src/parser/TSDocMessageId.ts b/tsdoc/src/parser/TSDocMessageId.ts index 050c1660..338ce5d4 100644 --- a/tsdoc/src/parser/TSDocMessageId.ts +++ b/tsdoc/src/parser/TSDocMessageId.ts @@ -412,6 +412,7 @@ export const allTsdocMessageIds: string[] = [ 'tsdoc-config-unresolved-extends', 'tsdoc-config-undefined-tag', 'tsdoc-config-duplicate-tag-name', + 'tsdoc-config-invalid-tag-name', 'tsdoc-comment-not-found', 'tsdoc-comment-missing-opening-delimiter', From a9a813ee562f81b2280860c07ed92b5adb0d3bb9 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Sun, 17 Jan 2021 19:22:51 -0800 Subject: [PATCH 6/8] rush change --- ...togonz-tsdoc-config-features_2021-01-18-03-22.json | 11 +++++++++++ ...togonz-tsdoc-config-features_2021-01-18-03-22.json | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 common/changes/@microsoft/tsdoc-config/octogonz-tsdoc-config-features_2021-01-18-03-22.json create mode 100644 common/changes/@microsoft/tsdoc/octogonz-tsdoc-config-features_2021-01-18-03-22.json diff --git a/common/changes/@microsoft/tsdoc-config/octogonz-tsdoc-config-features_2021-01-18-03-22.json b/common/changes/@microsoft/tsdoc-config/octogonz-tsdoc-config-features_2021-01-18-03-22.json new file mode 100644 index 00000000..1c1cd3d4 --- /dev/null +++ b/common/changes/@microsoft/tsdoc-config/octogonz-tsdoc-config-features_2021-01-18-03-22.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@microsoft/tsdoc-config", + "comment": "Add new \"supportForTags\" field to tsdoc.json schema", + "type": "minor" + } + ], + "packageName": "@microsoft/tsdoc-config", + "email": "4673363+octogonz@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@microsoft/tsdoc/octogonz-tsdoc-config-features_2021-01-18-03-22.json b/common/changes/@microsoft/tsdoc/octogonz-tsdoc-config-features_2021-01-18-03-22.json new file mode 100644 index 00000000..9f4fac93 --- /dev/null +++ b/common/changes/@microsoft/tsdoc/octogonz-tsdoc-config-features_2021-01-18-03-22.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@microsoft/tsdoc", + "comment": "Add new APIs: TSDocConfiguration.clear() and TSDocTagDefinition.validateTSDocTagName()", + "type": "minor" + } + ], + "packageName": "@microsoft/tsdoc", + "email": "4673363+octogonz@users.noreply.github.com" +} \ No newline at end of file From ca3495c794776e3e837342f82b459ef1719246f3 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Sun, 17 Jan 2021 19:24:52 -0800 Subject: [PATCH 7/8] rush change --- ...togonz-tsdoc-config-features_2021-01-18-03-24.json | 11 +++++++++++ ...togonz-tsdoc-config-features_2021-01-18-03-24.json | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 common/changes/@microsoft/tsdoc-config/octogonz-tsdoc-config-features_2021-01-18-03-24.json create mode 100644 common/changes/@microsoft/tsdoc/octogonz-tsdoc-config-features_2021-01-18-03-24.json diff --git a/common/changes/@microsoft/tsdoc-config/octogonz-tsdoc-config-features_2021-01-18-03-24.json b/common/changes/@microsoft/tsdoc-config/octogonz-tsdoc-config-features_2021-01-18-03-24.json new file mode 100644 index 00000000..7cf4b780 --- /dev/null +++ b/common/changes/@microsoft/tsdoc-config/octogonz-tsdoc-config-features_2021-01-18-03-24.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@microsoft/tsdoc-config", + "comment": "Add new APIs: TSDocConfigFile.supportForTags, .clearTagDefinitions(), .addTagDefinition(), .clearSupportForTags(), .setSupportForTag(), .loadFromParser(), .saveFile(), .saveToObject()", + "type": "minor" + } + ], + "packageName": "@microsoft/tsdoc-config", + "email": "4673363+octogonz@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@microsoft/tsdoc/octogonz-tsdoc-config-features_2021-01-18-03-24.json b/common/changes/@microsoft/tsdoc/octogonz-tsdoc-config-features_2021-01-18-03-24.json new file mode 100644 index 00000000..b587b959 --- /dev/null +++ b/common/changes/@microsoft/tsdoc/octogonz-tsdoc-config-features_2021-01-18-03-24.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@microsoft/tsdoc", + "comment": "Add new \"supportForTags\" field to tsdoc.json schema", + "type": "minor" + } + ], + "packageName": "@microsoft/tsdoc", + "email": "4673363+octogonz@users.noreply.github.com" +} \ No newline at end of file From 86016919a6f73ccda61a2569b236012be7756140 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Thu, 21 Jan 2021 17:10:12 -0800 Subject: [PATCH 8/8] PR feedback --- tsdoc-config/src/TSDocConfigFile.ts | 1 - tsdoc-config/src/__tests__/TSDocConfigFile.test.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/tsdoc-config/src/TSDocConfigFile.ts b/tsdoc-config/src/TSDocConfigFile.ts index 308e135a..3197bc6e 100644 --- a/tsdoc-config/src/TSDocConfigFile.ts +++ b/tsdoc-config/src/TSDocConfigFile.ts @@ -14,7 +14,6 @@ import * as resolve from 'resolve'; import * as path from 'path'; import Ajv from 'ajv'; import * as jju from 'jju'; -import { config } from 'process'; const ajv: Ajv.Ajv = new Ajv({ verbose: true }); diff --git a/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts b/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts index a60dd422..25ac5dc9 100644 --- a/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts +++ b/tsdoc-config/src/__tests__/TSDocConfigFile.test.ts @@ -1,6 +1,5 @@ import { TSDocConfiguration } from '@microsoft/tsdoc'; import * as path from 'path'; -import { config } from 'process'; import { TSDocConfigFile } from '../TSDocConfigFile';