From 643f5903fef61a8e78f8a3abfb91db8c3c006dd6 Mon Sep 17 00:00:00 2001 From: Michael Novotny Date: Fri, 10 Jul 2026 15:37:01 -0500 Subject: [PATCH] chore(repo): Derive ESLint jsdoc definedTags from Typedoc custom tags Co-Authored-By: Claude Fable 5 --- .changeset/eslint-typedoc-tag-sync.md | 2 ++ .typedoc/custom-tags.mjs | 37 +++++++++++++++++++++++++++ eslint.config.mjs | 8 +++++- typedoc.config.mjs | 32 +++-------------------- 4 files changed, 49 insertions(+), 30 deletions(-) create mode 100644 .changeset/eslint-typedoc-tag-sync.md create mode 100644 .typedoc/custom-tags.mjs diff --git a/.changeset/eslint-typedoc-tag-sync.md b/.changeset/eslint-typedoc-tag-sync.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/eslint-typedoc-tag-sync.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.typedoc/custom-tags.mjs b/.typedoc/custom-tags.mjs new file mode 100644 index 00000000000..ea2f5abb8d7 --- /dev/null +++ b/.typedoc/custom-tags.mjs @@ -0,0 +1,37 @@ +/** + * The custom JSDoc tags our Typedoc tooling defines, in Typedoc form (leading `@`). + * + * `typedoc.config.mjs` registers these with Typedoc, and `eslint.config.mjs` derives + * `jsdoc/check-tag-names`'s `definedTags` from them, so adding a tag here keeps the docs + * tooling and the linter in sync. + */ + +export const CUSTOM_BLOCK_TAGS = [ + '@unionReturnHeadings', + '@displayFunctionSignature', + '@paramExtension', + '@experimental', + '@hideReturns', +]; + +export const CUSTOM_MODIFIER_TAGS = [ + /** Suppresses the Parameters table in `.typedoc/extract-methods.mjs` method MDX. */ + '@skipParametersSection', + /** + * On a reference-object property whose value is an inline object type: omit the parent from the main Properties table; + * extract each callable member as `methods/-.mdx` and each non-callable object member as a nested heading + property table (see `.typedoc/extract-methods.mjs`). + */ + '@extractMethods', + /** Type-only / router hints; not user-facing prose (see `notRenderedTags` in `typedoc.config.mjs`). */ + '@inline', + '@inlineType', + /** With `@inline`, still emit a standalone `.mdx` page (see `.typedoc/standalone-page-tag.mjs`). */ + '@standalonePage', + /** Self-documenting placeholder for declarations intentionally left without a description. */ + '@generateWithEmptyComment', + /** + * On a generic wrapper type (e.g. `ClerkPaginationRequest`), opts every alias of the form `Foo<{...}>` into a single merged properties table that includes the wrapper's own properties. Without this, typedoc-plugin-markdown renders such aliases as empty pages because the resolved type is a ReferenceType with no inline declaration. + * Handled by `.typedoc/custom-plugin.mjs`. + */ + '@expandProperties', +]; diff --git a/eslint.config.mjs b/eslint.config.mjs index 51d8cf2af92..5598e5426cf 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -14,6 +14,8 @@ import pluginYml from 'eslint-plugin-yml'; import globals from 'globals'; import tseslint from 'typescript-eslint'; +import { CUSTOM_BLOCK_TAGS, CUSTOM_MODIFIER_TAGS } from './.typedoc/custom-tags.mjs'; + const ECMA_VERSION = 2021, JAVASCRIPT_FILES = ['**/*.cjs', '**/*.js', '**/*.jsx', '**/*.mjs'], TEST_FILES = ['**/*.test.js', '**/*.test.jsx', '**/*.test.ts', '**/*.test.tsx', '**/test/**', '**/__tests__/**'], @@ -663,7 +665,11 @@ export default tseslint.config([ 'jsdoc/informative-docs': 'warn', 'jsdoc/check-tag-names': [ 'warn', - { definedTags: ['inline', 'unionReturnHeadings', 'displayFunctionSignature', 'paramExtension'], typed: false }, + { + // Derived from the Typedoc tooling's custom tags (`.typedoc/custom-tags.mjs`) so the two cannot drift. + definedTags: [...CUSTOM_BLOCK_TAGS, ...CUSTOM_MODIFIER_TAGS].map(tag => tag.slice(1)), + typed: false, + }, ], 'jsdoc/require-hyphen-before-param-description': 'warn', 'jsdoc/require-description': 'off', diff --git a/typedoc.config.mjs b/typedoc.config.mjs index 0f3353cb398..1b919627e00 100644 --- a/typedoc.config.mjs +++ b/typedoc.config.mjs @@ -1,12 +1,6 @@ import { OptionDefaults } from 'typedoc'; -const CUSTOM_BLOCK_TAGS = [ - '@unionReturnHeadings', - '@displayFunctionSignature', - '@paramExtension', - '@experimental', - '@hideReturns', -]; +import { CUSTOM_BLOCK_TAGS, CUSTOM_MODIFIER_TAGS } from './.typedoc/custom-tags.mjs'; /** @type {import("typedoc-plugin-markdown").PluginOptions} */ const typedocPluginMarkdownOptions = { @@ -106,28 +100,8 @@ const config = { excludeNotDocumented: true, gitRevision: 'main', blockTags: [...OptionDefaults.blockTags, ...CUSTOM_BLOCK_TAGS], - modifierTags: [ - ...OptionDefaults.modifierTags.filter(tag => tag !== '@experimental'), - /** Suppresses the Parameters table in `.typedoc/extract-methods.mjs` method MDX. */ - '@skipParametersSection', - /** - * On a reference-object property whose value is an inline object type: omit the parent from the main Properties table; - * extract each callable member as `methods/-.mdx` and each non-callable object member as a nested heading + property table (see `.typedoc/extract-methods.mjs`). - */ - '@extractMethods', - /** Type-only / router hints; not user-facing prose (see `notRenderedTags`). */ - '@inline', - '@inlineType', - /** With `@inline`, still emit a standalone `.mdx` page (see `.typedoc/standalone-page-tag.mjs`). */ - '@standalonePage', - /** Self-documenting placeholder for declarations intentionally left without a description. */ - '@generateWithEmptyComment', - /** - * On a generic wrapper type (e.g. `ClerkPaginationRequest`), opts every alias of the form `Foo<{...}>` into a single merged properties table that includes the wrapper's own properties. Without this, typedoc-plugin-markdown renders such aliases as empty pages because the resolved type is a ReferenceType with no inline declaration. - * Handled by `.typedoc/custom-plugin.mjs`. - */ - '@expandProperties', - ], + // `@experimental` ships as a default modifier tag; we re-classify it as a block tag via CUSTOM_BLOCK_TAGS. + modifierTags: [...OptionDefaults.modifierTags.filter(tag => tag !== '@experimental'), ...CUSTOM_MODIFIER_TAGS], /** * Keep `@inline` / `@inlineType` / `@standalonePage` in the model so the custom router and theme can read them. */