diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 607c82f..60787aa 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,3 +1,3 @@
-issuehunt: RebeccaStevens/typedoc-plugin-require-tags
+issuehunt: RebeccaStevens/typedoc-plugin-custom-validation
ko_fi: rebeccastevens
-custom: https://github.com/RebeccaStevens/typedoc-plugin-require-tags/blob/main/DONATIONS.md
+custom: https://github.com/RebeccaStevens/typedoc-plugin-custom-validation/blob/main/DONATIONS.md
diff --git a/.github/workflows/build-node.yml b/.github/workflows/build-node.yml
index f7d44ee..d699ad3 100644
--- a/.github/workflows/build-node.yml
+++ b/.github/workflows/build-node.yml
@@ -11,4 +11,4 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/prepare
- - run: pnpm run build-node
+ - run: pnpm run build:node
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index af4a8c4..edded09 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -49,8 +49,7 @@ jobs:
run: pnpm run build
- name: Release
- run: echo "Do Release Here"
- # run: pnpm run semantic-release
- # env:
- # GITHUB_TOKEN: ${{ github.token }}
- # NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+ run: pnpm run semantic-release
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 501cab1..63cb2a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,6 @@ node_modules/
/coverage/
/dist/
+/docs/
*.log
diff --git a/README.md b/README.md
index 58ca137..2d83d24 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
-# typedoc-plugin-require-tags
+# typedoc-plugin-custom-validation
-[![npm version](https://img.shields.io/npm/v/typedoc-plugin-require-tags.svg)](https://www.npmjs.com/package/typedoc-plugin-require-tags)
-[![CI](https://github.com/RebeccaStevens/typedoc-plugin-require-tags/actions/workflows/release.yml/badge.svg)](https://github.com/RebeccaStevens/typedoc-plugin-require-tags/actions/workflows/release.yml)
-[![Coverage Status](https://codecov.io/gh/RebeccaStevens/typedoc-plugin-require-tags/branch/main/graph/badge.svg?token=MVpR1oAbIT)](https://codecov.io/gh/RebeccaStevens/typedoc-plugin-require-tags)\
+[![npm version](https://img.shields.io/npm/v/typedoc-plugin-custom-validation.svg)](https://www.npmjs.com/package/typedoc-plugin-custom-validation)
+[![CI](https://github.com/RebeccaStevens/typedoc-plugin-custom-validation/actions/workflows/release.yml/badge.svg)](https://github.com/RebeccaStevens/typedoc-plugin-custom-validation/actions/workflows/release.yml)
+[![Coverage Status](https://codecov.io/gh/RebeccaStevens/typedoc-plugin-custom-validation/branch/main/graph/badge.svg?token=MVpR1oAbIT)](https://codecov.io/gh/RebeccaStevens/typedoc-plugin-custom-validation)\
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
-[![GitHub Discussions](https://img.shields.io/github/discussions/RebeccaStevens/typedoc-plugin-require-tags?style=flat-square)](https://github.com/RebeccaStevens/typedoc-plugin-require-tags/discussions)
-[![BSD 3 Clause license](https://img.shields.io/github/license/RebeccaStevens/typedoc-plugin-require-tags.svg?style=flat-square)](https://opensource.org/licenses/BSD-3-Clause)
+[![GitHub Discussions](https://img.shields.io/github/discussions/RebeccaStevens/typedoc-plugin-custom-validation?style=flat-square)](https://github.com/RebeccaStevens/typedoc-plugin-custom-validation/discussions)
+[![BSD 3 Clause license](https://img.shields.io/github/license/RebeccaStevens/typedoc-plugin-custom-validation.svg?style=flat-square)](https://opensource.org/licenses/BSD-3-Clause)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg?style=flat-square)](https://commitizen.github.io/cz-cli/)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)](https://github.com/semantic-release/semantic-release)
@@ -21,11 +21,57 @@
```sh
# Install with npm
-npm install -D typedoc-plugin-require-tags
+npm install -D typedoc-plugin-custom-validation
# Install with pnpm
-pnpm add -D typedoc-plugin-require-tags
+pnpm add -D typedoc-plugin-custom-validation
# Install with yarn
-yarn add -D typedoc-plugin-require-tags
+yarn add -D typedoc-plugin-custom-validation
+```
+
+## Usage
+
+All options are configured in the `customValidation` option.
+
+### `byKind`
+
+This option is for specifying requirements for each kind of node.
+
+Example: Require all functions to have a summary and have an `@example` tag.
+
+```json
+{
+ "plugin": ["typedoc-plugin-custom-validation"],
+ "customValidation": {
+ "byKind": [
+ {
+ "kinds": "Function",
+ "summary": true,
+ "tags": ["example"]
+ }
+ ]
+ }
+}
+```
+
+### My Tags Don't Exists?
+
+Due to the way typedoc works, some tags may be move to other nodes than the one they were defined on.
+
+For example, `@param` tags are removed from the `Function` node they are defined on and its content is put onto the corresponding `Parameter` node.
+You can require parameters to be documented with:
+
+```json
+{
+ "plugin": ["typedoc-plugin-custom-validation"],
+ "customValidation": {
+ "byKind": [
+ {
+ "kinds": "Parameter",
+ "summary": true
+ }
+ ]
+ }
+}
```
diff --git a/package.json b/package.json
index ee7b899..930512b 100644
--- a/package.json
+++ b/package.json
@@ -1,16 +1,18 @@
{
- "name": "typedoc-plugin-require-tags",
+ "name": "typedoc-plugin-custom-validation",
"version": "0.0.0-development",
"private": true,
"description": "",
- "keywords": [],
- "homepage": "https://github.com/RebeccaStevens/typedoc-plugin-require-tags#readme",
+ "keywords": [
+ "typedoc-plugin"
+ ],
+ "homepage": "https://github.com/RebeccaStevens/typedoc-plugin-custom-validation#readme",
"bugs": {
- "url": "https://github.com/RebeccaStevens/typedoc-plugin-require-tags/issues"
+ "url": "https://github.com/RebeccaStevens/typedoc-plugin-custom-validation/issues"
},
"repository": {
"type": "git",
- "url": "git+https://github.com/RebeccaStevens/typedoc-plugin-require-tags"
+ "url": "git+https://github.com/RebeccaStevens/typedoc-plugin-custom-validation"
},
"license": "BSD-3-Clause",
"author": {
@@ -98,7 +100,11 @@
"rollup-plugin-dts": "5.2.0",
"semantic-release": "20.1.0",
"ts-node": "10.9.1",
+ "typedoc": "0.23.26",
"typescript": "4.9.5"
},
+ "peerDependencies": {
+ "typedoc": "^0.23.26"
+ },
"packageManager": "pnpm@7.27.0"
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3b42d7a..a5542d7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -48,6 +48,7 @@ specifiers:
rollup-plugin-dts: 5.2.0
semantic-release: 20.1.0
ts-node: 10.9.1
+ typedoc: 0.23.26
typescript: 4.9.5
devDependencies:
@@ -98,6 +99,7 @@ devDependencies:
rollup-plugin-dts: 5.2.0_vi3xdhr63abcxdtwtptol35g5u
semantic-release: 20.1.0
ts-node: 10.9.1_5lyanyhwyggrikgtn7dsw3wuei
+ typedoc: 0.23.26_typescript@4.9.5
typescript: 4.9.5
packages:
@@ -1585,6 +1587,10 @@ packages:
engines: {node: '>=12'}
dev: true
+ /ansi-sequence-parser/1.1.0:
+ resolution: {integrity: sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==}
+ dev: true
+
/ansi-styles/3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
@@ -4787,6 +4793,10 @@ packages:
engines: {node: '>=12'}
dev: true
+ /lunr/2.3.9:
+ resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==}
+ dev: true
+
/magic-string/0.16.0:
resolution: {integrity: sha512-c4BEos3y6G2qO0B9X7K0FVLOPT9uGrjYwYRLFmDqyl5YMboUviyecnXWp94fJTSMwPw2/sf+CEYt5AGpmklkkQ==}
dependencies:
@@ -5030,6 +5040,13 @@ packages:
brace-expansion: 2.0.1
dev: true
+ /minimatch/7.4.2:
+ resolution: {integrity: sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA==}
+ engines: {node: '>=10'}
+ dependencies:
+ brace-expansion: 2.0.1
+ dev: true
+
/minimist-options/4.1.0:
resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==}
engines: {node: '>= 6'}
@@ -6174,6 +6191,15 @@ packages:
resolution: {integrity: sha512-lT297f1WLAdq0A4O+AknIFRP6kkiI3s8C913eJ0XqBxJbZPGWUNkRQk2u8zk4bEAjUJ5i+fSLwB6z1HzeT+DEg==}
dev: true
+ /shiki/0.14.1:
+ resolution: {integrity: sha512-+Jz4nBkCBe0mEDqo1eKRcCdjRtrCjozmcbTUjbPTX7OOJfEbTZzlUWlZtGe3Gb5oV1/jnojhG//YZc3rs9zSEw==}
+ dependencies:
+ ansi-sequence-parser: 1.1.0
+ jsonc-parser: 3.2.0
+ vscode-oniguruma: 1.7.0
+ vscode-textmate: 8.0.0
+ dev: true
+
/side-channel/1.0.4:
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
dependencies:
@@ -6745,6 +6771,20 @@ packages:
is-typedarray: 1.0.0
dev: true
+ /typedoc/0.23.26_typescript@4.9.5:
+ resolution: {integrity: sha512-5m4KwR5tOLnk0OtMaRn9IdbeRM32uPemN9kur7YK9wFqx8U0CYrvO9aVq6ysdZSV1c824BTm+BuQl2Ze/k1HtA==}
+ engines: {node: '>= 14.14'}
+ hasBin: true
+ peerDependencies:
+ typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x
+ dependencies:
+ lunr: 2.3.9
+ marked: 4.2.12
+ minimatch: 7.4.2
+ shiki: 0.14.1
+ typescript: 4.9.5
+ dev: true
+
/typescript/4.9.5:
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
engines: {node: '>=4.2.0'}
@@ -6842,6 +6882,14 @@ packages:
resolution: {integrity: sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q==}
dev: true
+ /vscode-oniguruma/1.7.0:
+ resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==}
+ dev: true
+
+ /vscode-textmate/8.0.0:
+ resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
+ dev: true
+
/vscode-uri/3.0.7:
resolution: {integrity: sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA==}
dev: true
diff --git a/src/index.ts b/src/index.ts
index d4a9007..fee229c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,4 +1,119 @@
-/* eslint-disable eslint-comments/disable-enable-pair */
-/* eslint-disable unicorn/no-empty-file */
+import type { ProjectReflection, Reflection } from "typedoc";
+import { Application, ParameterType, ReflectionKind } from "typedoc";
-// Write me
+export type CustomValidationOptions = {
+ byKind: ByKindEntry[];
+};
+
+export type ByKindEntry = {
+ kinds: keyof typeof ReflectionKind | Array
;
+ tags?: string | string[];
+ summary?: boolean;
+};
+
+export function load(app: Readonly) {
+ app.options.addDeclaration({
+ name: "customValidation",
+ help: "The configuration object of the require-tags plugin.",
+ type: ParameterType.Object,
+ });
+
+ app.on(
+ Application.EVENT_VALIDATE_PROJECT,
+ (project: Readonly) => {
+ const customValidationOptions = app.options.getValue(
+ "customValidation"
+ ) as CustomValidationOptions;
+
+ let m_kinds = customValidationOptions.byKind
+ .flatMap((by) => by.kinds)
+ .map((kind) => ReflectionKind[kind])
+ .reduce((p, c) => p | c);
+
+ const reflectionKindReplacements: Array<
+ [oldKind: number, newKind: number]
+ > = [
+ [ReflectionKind.FunctionOrMethod, ReflectionKind.CallSignature],
+ [ReflectionKind.Constructor, ReflectionKind.ConstructorSignature],
+ [
+ ReflectionKind.Accessor,
+ ReflectionKind.GetSignature | ReflectionKind.SetSignature,
+ ],
+ ];
+
+ for (const [oldKind, newKind] of reflectionKindReplacements) {
+ m_kinds = (m_kinds | newKind) & ~oldKind;
+ }
+
+ type Requirements = { tags: string[]; summary: boolean };
+
+ const requirementsByKind = new Map(
+ customValidationOptions.byKind.flatMap(({ kinds, tags, summary }) =>
+ (Array.isArray(kinds) ? kinds : [kinds]).map(
+ (kindString): [number, Requirements] => {
+ const kind = ReflectionKind[kindString];
+ const realKind =
+ reflectionKindReplacements.find(
+ ([oldKind]) => (oldKind & kind) !== 0
+ )?.[1] ?? kind;
+
+ return [
+ realKind,
+ {
+ tags:
+ tags === undefined
+ ? []
+ : Array.isArray(tags)
+ ? tags
+ : [tags],
+ summary: summary ?? false,
+ },
+ ];
+ }
+ )
+ )
+ );
+
+ const reflections = project.getReflectionsByKind(m_kinds);
+ const seen = new Set();
+
+ for (const reflection of reflections) {
+ if (seen.has(reflection)) {
+ continue;
+ }
+ seen.add(reflection);
+
+ if (!reflection.hasComment() || reflection.comment!.isEmpty()) {
+ app.logger.warn(
+ `${reflection.getFriendlyFullName()} does not have any documentation.`
+ );
+ continue;
+ }
+
+ const requirements = requirementsByKind.get(reflection.kind);
+ if (requirements !== undefined) {
+ if (
+ requirements.summary &&
+ reflection.comment!.summary.length === 0
+ ) {
+ app.logger.warn(
+ `${reflection.getFriendlyFullName()} does not have a summary.`
+ );
+ }
+
+ for (const tagName of requirements.tags) {
+ const tag: `@${string}` = tagName.startsWith("@")
+ ? (tagName as `@${string}`)
+ : `@${tagName}`;
+
+ if (reflection.comment!.getTags(tag).length === 0) {
+ app.logger.warn(
+ `${reflection.getFriendlyFullName()} does not have any ${tag} tags.`
+ );
+ }
+ }
+ }
+ }
+ }
+ );
+}