From df7be0b5a32eca6ff40016f29432ca318f09b34c Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 7 Dec 2021 09:36:14 -0500 Subject: [PATCH 01/10] Add option to warn on undocumented items --- CHANGELOG.md | 4 + src/lib/application.ts | 9 ++ src/lib/utils/options/declaration.ts | 6 + src/lib/utils/options/sources/typedoc.ts | 115 ++++++++++++++---- src/lib/utils/ordinal-numbers.ts | 25 ++++ src/lib/validation/documentation.ts | 38 ++++++ src/test/utils/options/options.test.ts | 2 + .../utils/options/readers/arguments.test.ts | 5 + 8 files changed, 179 insertions(+), 25 deletions(-) create mode 100644 src/lib/utils/ordinal-numbers.ts create mode 100644 src/lib/validation/documentation.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 481171619..3469eaaf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Unreleased +### Features + +- Added `--validation.ensureDocumented` option to warn on items that are not documented + ### Bug Fixes - Fixed line height of `h1` and `h2` elements being too low, #1796. diff --git a/src/lib/application.ts b/src/lib/application.ts index 08645e558..5079140a3 100644 --- a/src/lib/application.ts +++ b/src/lib/application.ts @@ -34,6 +34,7 @@ import { } from "./utils/entry-point"; import { nicePath } from "./utils/paths"; import { hasBeenLoadedMultipleTimes } from "./utils/general"; +import { validateDocumentation } from "./validation/documentation"; // eslint-disable-next-line @typescript-eslint/no-var-requires const packageInfo = require("../../package.json") as { @@ -416,6 +417,14 @@ export class Application extends ChildableComponent< ); } + if (checks.notDocumented) { + validateDocumentation( + project, + this.logger, + this.options.getValue("requiredToBeDocumented") + ); + } + // checks.invalidLink is currently handled when rendering by the MarkedLinksPlugin. // It should really move here, but I'm putting that off until done refactoring the comment // parsing so that we don't have duplicate parse logic all over the place. diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index 5b600cba9..0fc5d2be2 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -3,6 +3,7 @@ import type { LogLevel } from "../loggers"; import type { SortStrategy } from "../sort"; import { isAbsolute, join, resolve } from "path"; import type { EntryPointStrategy } from "../entry-point"; +import type { ReflectionKind } from "../../models"; export const EmitStrategy = { true: true, // Alias for both, for backwards compatibility until 0.23 @@ -115,6 +116,7 @@ export interface TypeDocOptionMap { /** @deprecated use validation.invalidLink */ listInvalidSymbolLinks: boolean; validation: ValidationOptions; + requiredToBeDocumented: (keyof typeof ReflectionKind)[]; } export type ValidationOptions = { @@ -127,6 +129,10 @@ export type ValidationOptions = { * If set, TypeDoc will produce warnings about \{&link\} tags which will produce broken links. */ invalidLink: boolean; + /** + * If set, TypeDoc will produce warnings about declarations that do not have doc comments + */ + notDocumented: boolean; }; /** diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index 8d9ebd79d..b943940f5 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -4,19 +4,23 @@ import { ParameterType, ParameterHint, EmitStrategy } from "../declaration"; import { BUNDLED_THEMES, Theme } from "shiki"; import { SORT_STRATEGIES } from "../../sort"; import { EntryPointStrategy } from "../../entry-point"; +import { ReflectionKind } from "../../../models"; +import { toOrdinal } from "../../ordinal-numbers"; export function addTypeDocOptions(options: Pick) { options.addDeclaration({ type: ParameterType.Path, name: "options", - help: "Specify a json option file that should be loaded. If not specified TypeDoc will look for 'typedoc.json' in the current directory.", + help: + "Specify a json option file that should be loaded. If not specified TypeDoc will look for 'typedoc.json' in the current directory.", hint: ParameterHint.File, defaultValue: process.cwd(), }); options.addDeclaration({ type: ParameterType.Path, name: "tsconfig", - help: "Specify a TypeScript config file that should be loaded. If not specified TypeDoc will look for 'tsconfig.json' in the current directory.", + help: + "Specify a TypeScript config file that should be loaded. If not specified TypeDoc will look for 'tsconfig.json' in the current directory.", hint: ParameterHint.File, defaultValue: process.cwd(), }); @@ -27,7 +31,8 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "entryPointStrategy", - help: "The strategy to be used to convert entry points into documentation modules.", + help: + "The strategy to be used to convert entry points into documentation modules.", type: ParameterType.Map, map: EntryPointStrategy, defaultValue: EntryPointStrategy.Resolve, @@ -35,12 +40,14 @@ export function addTypeDocOptions(options: Pick) { options.addDeclaration({ name: "exclude", - help: "Define patterns to be excluded when expanding a directory that was specified as an entry point.", + help: + "Define patterns to be excluded when expanding a directory that was specified as an entry point.", type: ParameterType.GlobArray, }); options.addDeclaration({ name: "externalPattern", - help: "Define patterns for files that should be considered being external.", + help: + "Define patterns for files that should be considered being external.", type: ParameterType.GlobArray, defaultValue: ["**/node_modules/**"], }); @@ -51,12 +58,14 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "excludeNotDocumented", - help: "Prevent symbols that are not explicitly documented from appearing in the results.", + help: + "Prevent symbols that are not explicitly documented from appearing in the results.", type: ParameterType.Boolean, }); options.addDeclaration({ name: "excludeInternal", - help: "Prevent symbols that are marked with @internal from being documented.", + help: + "Prevent symbols that are marked with @internal from being documented.", type: ParameterType.Boolean, }); options.addDeclaration({ @@ -76,13 +85,15 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "includes", - help: "Specify the location to look for included documents (use [[include:FILENAME]] in comments).", + help: + "Specify the location to look for included documents (use [[include:FILENAME]] in comments).", type: ParameterType.Path, hint: ParameterHint.Directory, }); options.addDeclaration({ name: "media", - help: "Specify the location with media files that should be copied to the output directory.", + help: + "Specify the location with media files that should be copied to the output directory.", type: ParameterType.Path, hint: ParameterHint.Directory, }); @@ -94,7 +105,8 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "preserveWatchOutput", - help: "If set, TypeDoc will not clear the screen between compilation runs.", + help: + "If set, TypeDoc will not clear the screen between compilation runs.", type: ParameterType.Boolean, }); options.addDeclaration({ @@ -113,7 +125,8 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "json", - help: "Specify the location and filename a JSON file describing the project is written to.", + help: + "Specify the location and filename a JSON file describing the project is written to.", type: ParameterType.Path, hint: ParameterHint.File, }); @@ -175,7 +188,8 @@ export function addTypeDocOptions(options: Pick) { options.addDeclaration({ name: "name", - help: "Set the name of the project that will be used in the header of the template.", + help: + "Set the name of the project that will be used in the header of the template.", }); options.addDeclaration({ name: "includeVersion", @@ -189,17 +203,20 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "readme", - help: "Path to the readme file that should be displayed on the index page. Pass `none` to disable the index page and start the documentation on the globals page.", + help: + "Path to the readme file that should be displayed on the index page. Pass `none` to disable the index page and start the documentation on the globals page.", type: ParameterType.Path, }); options.addDeclaration({ name: "defaultCategory", - help: "Specify the default category for reflections without a category.", + help: + "Specify the default category for reflections without a category.", defaultValue: "Other", }); options.addDeclaration({ name: "categoryOrder", - help: "Specify the order in which categories appear. * indicates the relative order for categories not in the list.", + help: + "Specify the order in which categories appear. * indicates the relative order for categories not in the list.", type: ParameterType.Array, }); options.addDeclaration({ @@ -234,16 +251,19 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "gitRevision", - help: "Use specified revision instead of the last revision for linking to GitHub/Bitbucket source files.", + help: + "Use specified revision instead of the last revision for linking to GitHub/Bitbucket source files.", }); options.addDeclaration({ name: "gitRemote", - help: "Use the specified remote for linking to GitHub/Bitbucket source files.", + help: + "Use the specified remote for linking to GitHub/Bitbucket source files.", defaultValue: "origin", }); options.addDeclaration({ name: "gaID", - help: "Set the Google Analytics tracking ID and activate tracking code.", + help: + "Set the Google Analytics tracking ID and activate tracking code.", }); options.addDeclaration({ name: "gaSite", @@ -252,7 +272,8 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "githubPages", - help: "Generate a .nojekyll file to prevent 404 errors in GitHub Pages. Defaults to `true`.", + help: + "Generate a .nojekyll file to prevent 404 errors in GitHub Pages. Defaults to `true`.", type: ParameterType.Boolean, defaultValue: true, }); @@ -268,7 +289,8 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "cleanOutputDir", - help: "If set, TypeDoc will remove the output directory before writing output.", + help: + "If set, TypeDoc will remove the output directory before writing output.", type: ParameterType.Boolean, defaultValue: true, }); @@ -290,7 +312,8 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "plugin", - help: "Specify the npm plugins that should be loaded. Omit to load all installed plugins, set to 'none' to load no plugins.", + help: + "Specify the npm plugins that should be loaded. Omit to load all installed plugins, set to 'none' to load no plugins.", type: ParameterType.ModuleArray, }); options.addDeclaration({ @@ -308,7 +331,8 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "markedOptions", - help: "Specify the options passed to Marked, the Markdown parser used by TypeDoc.", + help: + "Specify the options passed to Marked, the Markdown parser used by TypeDoc.", type: ParameterType.Mixed, validate(value) { if ( @@ -330,22 +354,63 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "listInvalidSymbolLinks", - help: "Emit a list of broken symbol {@link navigation} links after documentation generation, DEPRECATED, prefer validation.invalidLink instead.", + help: + "Emit a list of broken symbol {@link navigation} links after documentation generation, DEPRECATED, prefer validation.invalidLink instead.", type: ParameterType.Boolean, }); options.addDeclaration({ name: "intentionallyNotExported", - help: "A list of types which should not produce 'referenced but not documented' warnings.", + help: + "A list of types which should not produce 'referenced but not documented' warnings.", type: ParameterType.Array, }); + options.addDeclaration({ + name: "requiredToBeDocumented", + help: "A list of reflection kinds that must be documented", + type: ParameterType.Array, + validate(values) { + const validValues = Object.values(ReflectionKind) + // this is good enough because the values of the ReflectionKind enum are all numbers + .filter((v) => typeof v === "string") + .join(", "); + for ( + let i = 0, kind = values[i]; + i < values.length; + i += 1, kind = values[i] + ) { + if (!(kind in ReflectionKind)) { + throw new Error( + `The ${toOrdinal( + i + 1 + )} 'requiredToBeDocumented' value is invalid. Must be one of: ${validValues}` + ); + } + } + }, + defaultValue: [ + "Enum", + "EnumMember", + "Variable", + "Function", + "Class", + "Interface", + "Property", + "Method", + "GetSignature", + "SetSignature", + "TypeAlias", + ], + }); options.addDeclaration({ name: "validation", - help: "Specify which validation steps TypeDoc should perform on your generated documentation.", + help: + "Specify which validation steps TypeDoc should perform on your generated documentation.", type: ParameterType.Flags, defaults: { notExported: true, invalidLink: false, + notDocumented: false, }, }); } diff --git a/src/lib/utils/ordinal-numbers.ts b/src/lib/utils/ordinal-numbers.ts new file mode 100644 index 000000000..93b79d8ec --- /dev/null +++ b/src/lib/utils/ordinal-numbers.ts @@ -0,0 +1,25 @@ +/** + * Format an integer value as an ordinal string. Throwing if the value is not a + * positive integer + * @param value The integer value to format as its ordinal version + */ +export function toOrdinal(value: number): string { + if (!Number.isInteger(value)) { + throw new TypeError("value must be an integer number"); + } + + if (value < 0) { + throw new TypeError("value must be a positive integer"); + } + + const onesDigit = value % 10; + const tensDigit = ((value % 100) - onesDigit) / 10; + + if (tensDigit === 1) { + return `${value}th`; + } + + const ordinal = onesDigit === 1 ? "st" : onesDigit === 2 ? "nd" : "th"; + + return `${value}${ordinal}`; +} diff --git a/src/lib/validation/documentation.ts b/src/lib/validation/documentation.ts new file mode 100644 index 000000000..a899725aa --- /dev/null +++ b/src/lib/validation/documentation.ts @@ -0,0 +1,38 @@ +import * as path from "path"; +import * as ts from "typescript"; +import { ProjectReflection, ReflectionKind } from "../models"; +import { Logger, normalizePath } from "../utils"; + +export function validateDocumentation( + project: ProjectReflection, + logger: Logger, + requiredToBeDocumented: readonly (keyof typeof ReflectionKind)[] +): void { + const kinds = requiredToBeDocumented.reduce( + (prev, cur) => (prev |= ReflectionKind[cur]), + 0 + ); + + for (const ref of project.getReflectionsByKind(kinds)) { + const symbol = project.getSymbolFromReflection(ref); + if (!ref.comment && symbol?.declarations) { + const decl = symbol.declarations[0]; + const sourceFile = decl.getSourceFile(); + const { line } = ts.getLineAndCharacterOfPosition( + sourceFile, + decl.getStart() + ); + const file = normalizePath( + path.relative(process.cwd(), sourceFile.fileName) + ); + + if (file.startsWith(`node_modules${path.delimiter}`)) { + continue; + } + + logger.warn( + `${ref.name}, defined at ${file}:${line}, does not have any documentation.` + ); + } + } +} diff --git a/src/test/utils/options/options.test.ts b/src/test/utils/options/options.test.ts index e7c34b7ca..91109fdce 100644 --- a/src/test/utils/options/options.test.ts +++ b/src/test/utils/options/options.test.ts @@ -115,12 +115,14 @@ describe("Options", () => { options.setValue("validation", true); equal(options.getValue("validation"), { notExported: true, + notDocumented: true, invalidLink: true, }); options.setValue("validation", false); equal(options.getValue("validation"), { notExported: false, + notDocumented: false, invalidLink: false, }); }); diff --git a/src/test/utils/options/readers/arguments.test.ts b/src/test/utils/options/readers/arguments.test.ts index aa3666145..c4d1768a8 100644 --- a/src/test/utils/options/readers/arguments.test.ts +++ b/src/test/utils/options/readers/arguments.test.ts @@ -164,6 +164,7 @@ describe("Options - ArgumentsReader", () => { equal(logger.hasWarnings(), false); equal(options.getValue("validation"), { notExported: true, + notDocumented: false, invalidLink: true, }); } @@ -182,6 +183,7 @@ describe("Options - ArgumentsReader", () => { equal(logger.hasWarnings(), false); equal(options.getValue("validation"), { notExported: false, + notDocumented: false, invalidLink: true, }); } @@ -195,6 +197,7 @@ describe("Options - ArgumentsReader", () => { equal(logger.hasWarnings(), false); equal(options.getValue("validation"), { notExported: true, + notDocumented: true, invalidLink: true, }); } @@ -208,6 +211,7 @@ describe("Options - ArgumentsReader", () => { equal(logger.hasWarnings(), false); equal(options.getValue("validation"), { notExported: true, + notDocumented: true, invalidLink: true, }); } @@ -221,6 +225,7 @@ describe("Options - ArgumentsReader", () => { equal(logger.hasWarnings(), false); equal(options.getValue("validation"), { notExported: false, + notDocumented: false, invalidLink: false, }); } From f6b6d9e3f2bb11ec732de8989aeb299abdd60c1e Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 7 Dec 2021 13:16:56 -0500 Subject: [PATCH 02/10] Fix skip node_modules --- src/lib/validation/documentation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/validation/documentation.ts b/src/lib/validation/documentation.ts index a899725aa..8a45d9c98 100644 --- a/src/lib/validation/documentation.ts +++ b/src/lib/validation/documentation.ts @@ -26,7 +26,7 @@ export function validateDocumentation( path.relative(process.cwd(), sourceFile.fileName) ); - if (file.startsWith(`node_modules${path.delimiter}`)) { + if (file.startsWith(`node_modules${path.sep}`)) { continue; } From 0724686c1a1c5fe611947b8333e07038ced3b51c Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 15 Dec 2021 11:16:19 -0500 Subject: [PATCH 03/10] fix off by one error Signed-off-by: Sebastian Malton --- src/lib/validation/exports.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/validation/exports.ts b/src/lib/validation/exports.ts index 2dc9e7c1e..3089ac0bd 100644 --- a/src/lib/validation/exports.ts +++ b/src/lib/validation/exports.ts @@ -107,7 +107,7 @@ export function validateExports( logger.warn( `${ type.name - }, defined at ${file}:${line}, is referenced by ${current!.getFullName()} but not included in the documentation.` + }, defined at ${file}:${line+1}, is referenced by ${current!.getFullName()} but not included in the documentation.` ); } } From f769611a116e0592b77a9b2f4527ccaa3094b779 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 15 Dec 2021 11:49:41 -0500 Subject: [PATCH 04/10] Fix off by one error on reporting --- src/lib/validation/documentation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/validation/documentation.ts b/src/lib/validation/documentation.ts index 8a45d9c98..59b5371c6 100644 --- a/src/lib/validation/documentation.ts +++ b/src/lib/validation/documentation.ts @@ -31,7 +31,7 @@ export function validateDocumentation( } logger.warn( - `${ref.name}, defined at ${file}:${line}, does not have any documentation.` + `${ref.name}, defined at ${file}:${line+1}, does not have any documentation.` ); } } From b563d92c9f1a24cb2374537023d3b9dd08702274 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 4 Jan 2022 16:52:36 -0500 Subject: [PATCH 05/10] Fix changelog, change ignore node_modules check Signed-off-by: Sebastian Malton --- CHANGELOG.md | 2 +- src/lib/utils/options/sources/typedoc.ts | 75 ++++++++---------------- src/lib/validation/documentation.ts | 5 +- 3 files changed, 29 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3469eaaf4..57b152dd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Features -- Added `--validation.ensureDocumented` option to warn on items that are not documented +- Added `--validation.notDocumented` option to warn on items that are not documented ### Bug Fixes diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index b943940f5..aff78993e 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -11,16 +11,14 @@ export function addTypeDocOptions(options: Pick) { options.addDeclaration({ type: ParameterType.Path, name: "options", - help: - "Specify a json option file that should be loaded. If not specified TypeDoc will look for 'typedoc.json' in the current directory.", + help: "Specify a json option file that should be loaded. If not specified TypeDoc will look for 'typedoc.json' in the current directory.", hint: ParameterHint.File, defaultValue: process.cwd(), }); options.addDeclaration({ type: ParameterType.Path, name: "tsconfig", - help: - "Specify a TypeScript config file that should be loaded. If not specified TypeDoc will look for 'tsconfig.json' in the current directory.", + help: "Specify a TypeScript config file that should be loaded. If not specified TypeDoc will look for 'tsconfig.json' in the current directory.", hint: ParameterHint.File, defaultValue: process.cwd(), }); @@ -31,8 +29,7 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "entryPointStrategy", - help: - "The strategy to be used to convert entry points into documentation modules.", + help: "The strategy to be used to convert entry points into documentation modules.", type: ParameterType.Map, map: EntryPointStrategy, defaultValue: EntryPointStrategy.Resolve, @@ -40,14 +37,12 @@ export function addTypeDocOptions(options: Pick) { options.addDeclaration({ name: "exclude", - help: - "Define patterns to be excluded when expanding a directory that was specified as an entry point.", + help: "Define patterns to be excluded when expanding a directory that was specified as an entry point.", type: ParameterType.GlobArray, }); options.addDeclaration({ name: "externalPattern", - help: - "Define patterns for files that should be considered being external.", + help: "Define patterns for files that should be considered being external.", type: ParameterType.GlobArray, defaultValue: ["**/node_modules/**"], }); @@ -58,14 +53,12 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "excludeNotDocumented", - help: - "Prevent symbols that are not explicitly documented from appearing in the results.", + help: "Prevent symbols that are not explicitly documented from appearing in the results.", type: ParameterType.Boolean, }); options.addDeclaration({ name: "excludeInternal", - help: - "Prevent symbols that are marked with @internal from being documented.", + help: "Prevent symbols that are marked with @internal from being documented.", type: ParameterType.Boolean, }); options.addDeclaration({ @@ -85,15 +78,13 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "includes", - help: - "Specify the location to look for included documents (use [[include:FILENAME]] in comments).", + help: "Specify the location to look for included documents (use [[include:FILENAME]] in comments).", type: ParameterType.Path, hint: ParameterHint.Directory, }); options.addDeclaration({ name: "media", - help: - "Specify the location with media files that should be copied to the output directory.", + help: "Specify the location with media files that should be copied to the output directory.", type: ParameterType.Path, hint: ParameterHint.Directory, }); @@ -105,8 +96,7 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "preserveWatchOutput", - help: - "If set, TypeDoc will not clear the screen between compilation runs.", + help: "If set, TypeDoc will not clear the screen between compilation runs.", type: ParameterType.Boolean, }); options.addDeclaration({ @@ -125,8 +115,7 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "json", - help: - "Specify the location and filename a JSON file describing the project is written to.", + help: "Specify the location and filename a JSON file describing the project is written to.", type: ParameterType.Path, hint: ParameterHint.File, }); @@ -188,8 +177,7 @@ export function addTypeDocOptions(options: Pick) { options.addDeclaration({ name: "name", - help: - "Set the name of the project that will be used in the header of the template.", + help: "Set the name of the project that will be used in the header of the template.", }); options.addDeclaration({ name: "includeVersion", @@ -203,20 +191,17 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "readme", - help: - "Path to the readme file that should be displayed on the index page. Pass `none` to disable the index page and start the documentation on the globals page.", + help: "Path to the readme file that should be displayed on the index page. Pass `none` to disable the index page and start the documentation on the globals page.", type: ParameterType.Path, }); options.addDeclaration({ name: "defaultCategory", - help: - "Specify the default category for reflections without a category.", + help: "Specify the default category for reflections without a category.", defaultValue: "Other", }); options.addDeclaration({ name: "categoryOrder", - help: - "Specify the order in which categories appear. * indicates the relative order for categories not in the list.", + help: "Specify the order in which categories appear. * indicates the relative order for categories not in the list.", type: ParameterType.Array, }); options.addDeclaration({ @@ -251,19 +236,16 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "gitRevision", - help: - "Use specified revision instead of the last revision for linking to GitHub/Bitbucket source files.", + help: "Use specified revision instead of the last revision for linking to GitHub/Bitbucket source files.", }); options.addDeclaration({ name: "gitRemote", - help: - "Use the specified remote for linking to GitHub/Bitbucket source files.", + help: "Use the specified remote for linking to GitHub/Bitbucket source files.", defaultValue: "origin", }); options.addDeclaration({ name: "gaID", - help: - "Set the Google Analytics tracking ID and activate tracking code.", + help: "Set the Google Analytics tracking ID and activate tracking code.", }); options.addDeclaration({ name: "gaSite", @@ -272,8 +254,7 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "githubPages", - help: - "Generate a .nojekyll file to prevent 404 errors in GitHub Pages. Defaults to `true`.", + help: "Generate a .nojekyll file to prevent 404 errors in GitHub Pages. Defaults to `true`.", type: ParameterType.Boolean, defaultValue: true, }); @@ -289,8 +270,7 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "cleanOutputDir", - help: - "If set, TypeDoc will remove the output directory before writing output.", + help: "If set, TypeDoc will remove the output directory before writing output.", type: ParameterType.Boolean, defaultValue: true, }); @@ -312,8 +292,7 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "plugin", - help: - "Specify the npm plugins that should be loaded. Omit to load all installed plugins, set to 'none' to load no plugins.", + help: "Specify the npm plugins that should be loaded. Omit to load all installed plugins, set to 'none' to load no plugins.", type: ParameterType.ModuleArray, }); options.addDeclaration({ @@ -331,8 +310,7 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "markedOptions", - help: - "Specify the options passed to Marked, the Markdown parser used by TypeDoc.", + help: "Specify the options passed to Marked, the Markdown parser used by TypeDoc.", type: ParameterType.Mixed, validate(value) { if ( @@ -354,14 +332,12 @@ export function addTypeDocOptions(options: Pick) { }); options.addDeclaration({ name: "listInvalidSymbolLinks", - help: - "Emit a list of broken symbol {@link navigation} links after documentation generation, DEPRECATED, prefer validation.invalidLink instead.", + help: "Emit a list of broken symbol {@link navigation} links after documentation generation, DEPRECATED, prefer validation.invalidLink instead.", type: ParameterType.Boolean, }); options.addDeclaration({ name: "intentionallyNotExported", - help: - "A list of types which should not produce 'referenced but not documented' warnings.", + help: "A list of types which should not produce 'referenced but not documented' warnings.", type: ParameterType.Array, }); options.addDeclaration({ @@ -404,8 +380,7 @@ export function addTypeDocOptions(options: Pick) { options.addDeclaration({ name: "validation", - help: - "Specify which validation steps TypeDoc should perform on your generated documentation.", + help: "Specify which validation steps TypeDoc should perform on your generated documentation.", type: ParameterType.Flags, defaults: { notExported: true, diff --git a/src/lib/validation/documentation.ts b/src/lib/validation/documentation.ts index 59b5371c6..5b657f99c 100644 --- a/src/lib/validation/documentation.ts +++ b/src/lib/validation/documentation.ts @@ -26,12 +26,13 @@ export function validateDocumentation( path.relative(process.cwd(), sourceFile.fileName) ); - if (file.startsWith(`node_modules${path.sep}`)) { + if (file.includes("node_modules")) { continue; } + const loc = `${file}:${line + 1}`; logger.warn( - `${ref.name}, defined at ${file}:${line+1}, does not have any documentation.` + `${ref.name}, defined at ${loc}, does not have any documentation.` ); } } From e6ce564f633d250353210355bbf6567442fa9473 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 7 Jan 2022 12:07:43 -0500 Subject: [PATCH 06/10] fix formatting Signed-off-by: Sebastian Malton --- src/lib/validation/exports.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/validation/exports.ts b/src/lib/validation/exports.ts index 3089ac0bd..947285a51 100644 --- a/src/lib/validation/exports.ts +++ b/src/lib/validation/exports.ts @@ -105,9 +105,9 @@ export function validateExports( ); logger.warn( - `${ - type.name - }, defined at ${file}:${line+1}, is referenced by ${current!.getFullName()} but not included in the documentation.` + `${type.name}, defined at ${file}:${ + line + 1 + }, is referenced by ${current!.getFullName()} but not included in the documentation.` ); } } From 9106fff3fed8f8a09b14b72e96eaa30d919368d0 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 10 Jan 2022 08:09:05 -0500 Subject: [PATCH 07/10] fix circular dep Signed-off-by: Sebastian Malton --- src/lib/utils/options/sources/typedoc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index aff78993e..1c43e0e04 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -4,7 +4,7 @@ import { ParameterType, ParameterHint, EmitStrategy } from "../declaration"; import { BUNDLED_THEMES, Theme } from "shiki"; import { SORT_STRATEGIES } from "../../sort"; import { EntryPointStrategy } from "../../entry-point"; -import { ReflectionKind } from "../../../models"; +import { ReflectionKind } from "../../../models/reflections"; import { toOrdinal } from "../../ordinal-numbers"; export function addTypeDocOptions(options: Pick) { From 76bad36a17e1b371ff88ab4d43a53578fe143787 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 22 Jan 2022 19:33:04 -0700 Subject: [PATCH 08/10] Fix circular reference --- src/lib/models/ReflectionGroup.ts | 2 +- src/lib/models/reflections/abstract.ts | 74 +------------------- src/lib/models/reflections/container.ts | 8 +-- src/lib/models/reflections/index.ts | 4 +- src/lib/models/reflections/kind.ts | 72 +++++++++++++++++++ src/lib/models/reflections/project.ts | 3 +- src/lib/models/reflections/reference.ts | 3 +- src/lib/models/reflections/signature.ts | 8 +-- src/lib/models/reflections/type-parameter.ts | 3 +- src/lib/utils/options/declaration.ts | 2 +- src/lib/utils/sort.ts | 2 +- 11 files changed, 88 insertions(+), 93 deletions(-) create mode 100644 src/lib/models/reflections/kind.ts diff --git a/src/lib/models/ReflectionGroup.ts b/src/lib/models/ReflectionGroup.ts index 2dbac5b8d..62e851683 100644 --- a/src/lib/models/ReflectionGroup.ts +++ b/src/lib/models/ReflectionGroup.ts @@ -1,4 +1,4 @@ -import type { ReflectionKind } from "./reflections/abstract"; +import type { ReflectionKind } from "./reflections/kind"; import type { ReflectionCategory } from "./ReflectionCategory"; import type { DeclarationReflection } from "."; diff --git a/src/lib/models/reflections/abstract.ts b/src/lib/models/reflections/abstract.ts index 681137113..51f7b62a7 100644 --- a/src/lib/models/reflections/abstract.ts +++ b/src/lib/models/reflections/abstract.ts @@ -5,6 +5,7 @@ import type { Comment } from "../comments/comment"; import { splitUnquotedString } from "./utils"; import type { ProjectReflection } from "./project"; import type { NeverIfInternal } from "../../utils"; +import { ReflectionKind } from "./kind"; /** * Holds all data models used by TypeDoc. @@ -32,79 +33,6 @@ export function resetReflectionID() { REFLECTION_ID = 0; } -/** - * Defines the available reflection kinds. - */ -export enum ReflectionKind { - Project = 0x1, - Module = 0x2, - Namespace = 0x4, - Enum = 0x8, - EnumMember = 0x10, - Variable = 0x20, - Function = 0x40, - Class = 0x80, - Interface = 0x100, - Constructor = 0x200, - Property = 0x400, - Method = 0x800, - CallSignature = 0x1000, - IndexSignature = 0x2000, - ConstructorSignature = 0x4000, - Parameter = 0x8000, - TypeLiteral = 0x10000, - TypeParameter = 0x20000, - Accessor = 0x40000, - GetSignature = 0x80000, - SetSignature = 0x100000, - ObjectLiteral = 0x200000, - TypeAlias = 0x400000, - Event = 0x800000, - Reference = 0x1000000, -} - -/** @hidden */ -export namespace ReflectionKind { - export const All = ReflectionKind.Reference * 2 - 1; - - export const ClassOrInterface = - ReflectionKind.Class | ReflectionKind.Interface; - export const VariableOrProperty = - ReflectionKind.Variable | ReflectionKind.Property; - export const FunctionOrMethod = - ReflectionKind.Function | ReflectionKind.Method; - export const ClassMember = - ReflectionKind.Accessor | - ReflectionKind.Constructor | - ReflectionKind.Method | - ReflectionKind.Property | - ReflectionKind.Event; - export const SomeSignature = - ReflectionKind.CallSignature | - ReflectionKind.IndexSignature | - ReflectionKind.ConstructorSignature | - ReflectionKind.GetSignature | - ReflectionKind.SetSignature; - export const SomeModule = ReflectionKind.Namespace | ReflectionKind.Module; - export const SomeType = - ReflectionKind.Interface | - ReflectionKind.TypeLiteral | - ReflectionKind.TypeParameter | - ReflectionKind.TypeAlias; - export const SomeValue = - ReflectionKind.Variable | - ReflectionKind.Function | - ReflectionKind.ObjectLiteral; - - /** @internal */ - export const Inheritable = - ReflectionKind.Accessor | - ReflectionKind.IndexSignature | - ReflectionKind.Property | - ReflectionKind.Method | - ReflectionKind.Constructor; -} - export enum ReflectionFlag { None = 0, Private = 1, diff --git a/src/lib/models/reflections/container.ts b/src/lib/models/reflections/container.ts index fefdd9d28..840e04060 100644 --- a/src/lib/models/reflections/container.ts +++ b/src/lib/models/reflections/container.ts @@ -1,12 +1,8 @@ -import { - Reflection, - ReflectionKind, - TraverseCallback, - TraverseProperty, -} from "./abstract"; +import { Reflection, TraverseCallback, TraverseProperty } from "./abstract"; import type { ReflectionCategory } from "../ReflectionCategory"; import type { ReflectionGroup } from "../ReflectionGroup"; import type { DeclarationReflection } from "./declaration"; +import type { ReflectionKind } from "./kind"; export class ContainerReflection extends Reflection { /** diff --git a/src/lib/models/reflections/index.ts b/src/lib/models/reflections/index.ts index 1a2196ce9..d893f3c5c 100644 --- a/src/lib/models/reflections/index.ts +++ b/src/lib/models/reflections/index.ts @@ -1,14 +1,14 @@ export { Reflection, - ReflectionKind, ReflectionFlag, - TraverseProperty, ReflectionFlags, + TraverseProperty, } from "./abstract"; export type { Decorator, TraverseCallback } from "./abstract"; export { ContainerReflection } from "./container"; export { DeclarationReflection } from "./declaration"; export type { DeclarationHierarchy } from "./declaration"; +export { ReflectionKind } from "./kind"; export { ParameterReflection } from "./parameter"; export { ProjectReflection } from "./project"; export { ReferenceReflection } from "./reference"; diff --git a/src/lib/models/reflections/kind.ts b/src/lib/models/reflections/kind.ts new file mode 100644 index 000000000..660ba0294 --- /dev/null +++ b/src/lib/models/reflections/kind.ts @@ -0,0 +1,72 @@ +/** + * Defines the available reflection kinds. + */ +export enum ReflectionKind { + Project = 0x1, + Module = 0x2, + Namespace = 0x4, + Enum = 0x8, + EnumMember = 0x10, + Variable = 0x20, + Function = 0x40, + Class = 0x80, + Interface = 0x100, + Constructor = 0x200, + Property = 0x400, + Method = 0x800, + CallSignature = 0x1000, + IndexSignature = 0x2000, + ConstructorSignature = 0x4000, + Parameter = 0x8000, + TypeLiteral = 0x10000, + TypeParameter = 0x20000, + Accessor = 0x40000, + GetSignature = 0x80000, + SetSignature = 0x100000, + ObjectLiteral = 0x200000, + TypeAlias = 0x400000, + Event = 0x800000, + Reference = 0x1000000, +} + +/** @hidden */ +export namespace ReflectionKind { + export const All = ReflectionKind.Reference * 2 - 1; + + export const ClassOrInterface = + ReflectionKind.Class | ReflectionKind.Interface; + export const VariableOrProperty = + ReflectionKind.Variable | ReflectionKind.Property; + export const FunctionOrMethod = + ReflectionKind.Function | ReflectionKind.Method; + export const ClassMember = + ReflectionKind.Accessor | + ReflectionKind.Constructor | + ReflectionKind.Method | + ReflectionKind.Property | + ReflectionKind.Event; + export const SomeSignature = + ReflectionKind.CallSignature | + ReflectionKind.IndexSignature | + ReflectionKind.ConstructorSignature | + ReflectionKind.GetSignature | + ReflectionKind.SetSignature; + export const SomeModule = ReflectionKind.Namespace | ReflectionKind.Module; + export const SomeType = + ReflectionKind.Interface | + ReflectionKind.TypeLiteral | + ReflectionKind.TypeParameter | + ReflectionKind.TypeAlias; + export const SomeValue = + ReflectionKind.Variable | + ReflectionKind.Function | + ReflectionKind.ObjectLiteral; + + /** @internal */ + export const Inheritable = + ReflectionKind.Accessor | + ReflectionKind.IndexSignature | + ReflectionKind.Property | + ReflectionKind.Method | + ReflectionKind.Constructor; +} diff --git a/src/lib/models/reflections/project.ts b/src/lib/models/reflections/project.ts index 02364c815..4c92c2d46 100644 --- a/src/lib/models/reflections/project.ts +++ b/src/lib/models/reflections/project.ts @@ -1,5 +1,5 @@ import { SourceFile, SourceDirectory } from "../sources/index"; -import { Reflection, ReflectionKind, TraverseProperty } from "./abstract"; +import { Reflection, TraverseProperty } from "./abstract"; import { ContainerReflection } from "./container"; import { splitUnquotedString } from "./utils"; import { ReferenceReflection } from "./reference"; @@ -10,6 +10,7 @@ import { IntrinsicType } from "../types"; import type { TypeParameterReflection } from "./type-parameter"; import { removeIfPresent } from "../../utils"; import type * as ts from "typescript"; +import { ReflectionKind } from "./kind"; /** * A reflection that represents the root of the project. diff --git a/src/lib/models/reflections/reference.ts b/src/lib/models/reflections/reference.ts index 417606d79..3edcde9b7 100644 --- a/src/lib/models/reflections/reference.ts +++ b/src/lib/models/reflections/reference.ts @@ -1,6 +1,7 @@ import type * as ts from "typescript"; -import { Reflection, ReflectionKind } from "./abstract"; +import { Reflection } from "./abstract"; import { DeclarationReflection } from "./declaration"; +import { ReflectionKind } from "./kind"; import type { ProjectReflection } from "./project"; /** diff --git a/src/lib/models/reflections/signature.ts b/src/lib/models/reflections/signature.ts index 9d4b836b3..1cae16971 100644 --- a/src/lib/models/reflections/signature.ts +++ b/src/lib/models/reflections/signature.ts @@ -1,13 +1,9 @@ import { Type, ReflectionType, ReferenceType } from "../types"; -import { - Reflection, - TraverseProperty, - TraverseCallback, - ReflectionKind, -} from "./abstract"; +import { Reflection, TraverseProperty, TraverseCallback } from "./abstract"; import type { ParameterReflection } from "./parameter"; import type { TypeParameterReflection } from "./type-parameter"; import type { DeclarationReflection } from "./declaration"; +import type { ReflectionKind } from "./kind"; export class SignatureReflection extends Reflection { /** diff --git a/src/lib/models/reflections/type-parameter.ts b/src/lib/models/reflections/type-parameter.ts index 516d44ece..e7c338da6 100644 --- a/src/lib/models/reflections/type-parameter.ts +++ b/src/lib/models/reflections/type-parameter.ts @@ -1,6 +1,7 @@ import type { Type } from "../types"; -import { Reflection, ReflectionKind } from "./abstract"; +import { Reflection } from "./abstract"; import type { DeclarationReflection } from "./declaration"; +import { ReflectionKind } from "./kind"; export class TypeParameterReflection extends Reflection { override parent?: DeclarationReflection; diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index 037439677..8a191cc00 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -3,7 +3,7 @@ import type { LogLevel } from "../loggers"; import type { SortStrategy } from "../sort"; import { isAbsolute, join, resolve } from "path"; import type { EntryPointStrategy } from "../entry-point"; -import type { ReflectionKind } from "../../models"; +import type { ReflectionKind } from "../../models/reflections/kind"; export const EmitStrategy = { true: true, // Alias for both, for backwards compatibility until 0.23 diff --git a/src/lib/utils/sort.ts b/src/lib/utils/sort.ts index b8fcf2518..0fb40a6a1 100644 --- a/src/lib/utils/sort.ts +++ b/src/lib/utils/sort.ts @@ -3,7 +3,7 @@ * @module */ -import { ReflectionKind } from "../models/reflections/abstract"; +import { ReflectionKind } from "../models/reflections/kind"; import type { DeclarationReflection } from "../models/reflections/declaration"; export const SORT_STRATEGIES = [ From a787195c89ea5c8519ee5953e1b65b7c1ce2becf Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 22 Jan 2022 20:00:58 -0700 Subject: [PATCH 09/10] Actually fix circular dep --- src/lib/utils/options/sources/typedoc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index a10cc09ee..3bc124f09 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -4,7 +4,7 @@ import { ParameterType, ParameterHint, EmitStrategy } from "../declaration"; import { BUNDLED_THEMES, Theme } from "shiki"; import { SORT_STRATEGIES } from "../../sort"; import { EntryPointStrategy } from "../../entry-point"; -import { ReflectionKind } from "../../../models/reflections"; +import { ReflectionKind } from "../../../models/reflections/kind"; import { toOrdinal } from "../../ordinal-numbers"; export function addTypeDocOptions(options: Pick) { From cd4d115d6d42054fa9a378e16b621ede258f1635 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 22 Jan 2022 20:55:13 -0700 Subject: [PATCH 10/10] Improve errors for invalid requiredToBeDocumented values --- .config/regconfig.json | 2 +- src/lib/utils/options/sources/typedoc.ts | 24 ++++++++++------------- src/lib/utils/ordinal-numbers.ts | 25 ------------------------ 3 files changed, 11 insertions(+), 40 deletions(-) delete mode 100644 src/lib/utils/ordinal-numbers.ts diff --git a/.config/regconfig.json b/.config/regconfig.json index 6d91aa6af..d15db1a85 100644 --- a/.config/regconfig.json +++ b/.config/regconfig.json @@ -2,7 +2,7 @@ "core": { "workingDir": "dist/tmp/.reg", "actualDir": "dist/tmp/__screenshots__", - "thresholdRate": 0.001, + "thresholdRate": 0.01, "addIgnore": true, "ximgdiff": { "invocationType": "client" diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index 3bc124f09..883b478da 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -5,7 +5,6 @@ import { BUNDLED_THEMES, Theme } from "shiki"; import { SORT_STRATEGIES } from "../../sort"; import { EntryPointStrategy } from "../../entry-point"; import { ReflectionKind } from "../../../models/reflections/kind"; -import { toOrdinal } from "../../ordinal-numbers"; export function addTypeDocOptions(options: Pick) { options.addDeclaration({ @@ -349,20 +348,17 @@ export function addTypeDocOptions(options: Pick) { help: "A list of reflection kinds that must be documented", type: ParameterType.Array, validate(values) { - const validValues = Object.values(ReflectionKind) - // this is good enough because the values of the ReflectionKind enum are all numbers - .filter((v) => typeof v === "string") - .join(", "); - for ( - let i = 0, kind = values[i]; - i < values.length; - i += 1, kind = values[i] - ) { - if (!(kind in ReflectionKind)) { + // this is good enough because the values of the ReflectionKind enum are all numbers + const validValues = Object.values(ReflectionKind).filter( + (v) => typeof v === "string" + ); + + for (const kind of values) { + if (validValues.includes(kind)) { throw new Error( - `The ${toOrdinal( - i + 1 - )} 'requiredToBeDocumented' value is invalid. Must be one of: ${validValues}` + `'${kind}' is an invalid value for 'requiredToBeDocumented'. Must be one of: ${validValues.join( + ", " + )}` ); } } diff --git a/src/lib/utils/ordinal-numbers.ts b/src/lib/utils/ordinal-numbers.ts deleted file mode 100644 index 93b79d8ec..000000000 --- a/src/lib/utils/ordinal-numbers.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Format an integer value as an ordinal string. Throwing if the value is not a - * positive integer - * @param value The integer value to format as its ordinal version - */ -export function toOrdinal(value: number): string { - if (!Number.isInteger(value)) { - throw new TypeError("value must be an integer number"); - } - - if (value < 0) { - throw new TypeError("value must be a positive integer"); - } - - const onesDigit = value % 10; - const tensDigit = ((value % 100) - onesDigit) / 10; - - if (tensDigit === 1) { - return `${value}th`; - } - - const ordinal = onesDigit === 1 ? "st" : onesDigit === 2 ? "nd" : "th"; - - return `${value}${ordinal}`; -}