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/CHANGELOG.md b/CHANGELOG.md index 15d49d099..3c896b749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,10 @@ ### Features +- Added `--validation.notDocumented` option to warn on items that are not documented, #1817. +- Added new `cname` option for GitHub Pages custom domain support, #1803. - `ReferenceType`s which reference an external symbol will now include `qualifiedName` and `package` in their serialized JSON. - Added clickable anchor link for member titles, #1842. -- Added new `cname` option for GitHub Pages custom domain support, #1803 ### Bug Fixes 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/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 3f6669509..8a191cc00 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/reflections/kind"; export const EmitStrategy = { true: true, // Alias for both, for backwards compatibility until 0.23 @@ -116,6 +117,7 @@ export interface TypeDocOptionMap { /** @deprecated use validation.invalidLink */ listInvalidSymbolLinks: boolean; validation: ValidationOptions; + requiredToBeDocumented: (keyof typeof ReflectionKind)[]; } export type ValidationOptions = { @@ -128,6 +130,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 55432d93d..883b478da 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -4,6 +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/kind"; export function addTypeDocOptions(options: Pick) { options.addDeclaration({ @@ -342,6 +343,40 @@ export function addTypeDocOptions(options: Pick) { 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) { + // 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( + `'${kind}' is an invalid value for 'requiredToBeDocumented'. Must be one of: ${validValues.join( + ", " + )}` + ); + } + } + }, + defaultValue: [ + "Enum", + "EnumMember", + "Variable", + "Function", + "Class", + "Interface", + "Property", + "Method", + "GetSignature", + "SetSignature", + "TypeAlias", + ], + }); options.addDeclaration({ name: "validation", @@ -350,6 +385,7 @@ export function addTypeDocOptions(options: Pick) { defaults: { notExported: true, invalidLink: false, + notDocumented: false, }, }); } 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 = [ diff --git a/src/lib/validation/documentation.ts b/src/lib/validation/documentation.ts new file mode 100644 index 000000000..5b657f99c --- /dev/null +++ b/src/lib/validation/documentation.ts @@ -0,0 +1,39 @@ +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.includes("node_modules")) { + continue; + } + + const loc = `${file}:${line + 1}`; + logger.warn( + `${ref.name}, defined at ${loc}, does not have any documentation.` + ); + } + } +} diff --git a/src/lib/validation/exports.ts b/src/lib/validation/exports.ts index 2dc9e7c1e..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}, 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.` ); } } 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, }); }