Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: set up initial behavior #1

Merged
merged 13 commits into from Mar 2, 2023
4 changes: 2 additions & 2 deletions .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
2 changes: 1 addition & 1 deletion .github/workflows/build-node.yml
Expand Up @@ -11,4 +11,4 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/prepare
- run: pnpm run build-node
- run: pnpm run build:node
9 changes: 4 additions & 5 deletions .github/workflows/release.yml
Expand Up @@ -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 }}
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -2,5 +2,6 @@ node_modules/

/coverage/
/dist/
/docs/

*.log
64 changes: 55 additions & 9 deletions README.md
@@ -1,13 +1,13 @@
<div align="center">

# 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)

Expand All @@ -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
}
]
}
}
```
16 changes: 11 additions & 5 deletions 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": {
Expand Down Expand Up @@ -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"
}
48 changes: 48 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

121 changes: 118 additions & 3 deletions 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<keyof typeof ReflectionKind>;
tags?: string | string[];
summary?: boolean;
};

export function load(app: Readonly<Application>) {
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<ProjectReflection>) => {
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<number, Requirements>(
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<Reflection>();

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.`
);
}
}
}
}
}
);
}