diff --git a/.gitignore b/.gitignore index 5d2aa7bffa74..9dc454418417 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ yarn-debug.log* yarn-error.log* # Website +docs/packages/*/generated packages/website/.docusaurus packages/website/.cache-loader packages/website/build diff --git a/docs/packages/Type_Utils.mdx b/docs/packages/Type_Utils.mdx index fe5f382255ab..2f04ea6340c7 100644 --- a/docs/packages/Type_Utils.mdx +++ b/docs/packages/Type_Utils.mdx @@ -1,15 +1,18 @@ --- id: type-utils sidebar_label: type-utils +toc_max_heading_level: 3 --- +import GeneratedDocs from './type-utils/generated/index.md'; + # `@typescript-eslint/type-utils` -> Type utilities for working with TypeScript within ESLint rules. ✨ +> Type utilities for working with TypeScript types ✨ -This package contains public utilities for working with TypeScript types that ESLint rules often use. +This package contains public utilities for working with TypeScript types. Rules declared in [`@typescript-eslint/eslint-plugin`](./ESLint_Plugin.mdx) use these utility functions. The utilities in this package are both: @@ -17,4 +20,12 @@ The utilities in this package are both: - More generally ESLint-focused than the broad TypeScript utilities in [`ts-api-utils`](https://npmjs.com/package/ts-api-utils) - Separated from [`@typescript-eslint/utils`](./Utils.mdx) so that that package does not require a dependency on `typescript` -> See [Custom Rules](../developers/Custom_Rules.mdx) for documentation on creating your own custom ESLint rules for TypeScript code. +:::tip +See [Custom Rules](../developers/Custom_Rules.mdx) for documentation on creating your own custom ESLint rules for TypeScript code. +::: + +--- + +The following documentation is auto-generated from source code. + + diff --git a/docs/packages/Utils.mdx b/docs/packages/Utils.mdx index ea7020466324..1511d0733472 100644 --- a/docs/packages/Utils.mdx +++ b/docs/packages/Utils.mdx @@ -1,6 +1,7 @@ --- id: utils sidebar_label: utils +toc_max_heading_level: 3 --- # `@typescript-eslint/utils` diff --git a/docs/packages/typescript-estree/AST_Spec.mdx b/docs/packages/typescript-estree/AST_Spec.mdx new file mode 100644 index 000000000000..21e1aa1e33d3 --- /dev/null +++ b/docs/packages/typescript-estree/AST_Spec.mdx @@ -0,0 +1,13 @@ +--- +id: ast-spec +sidebar_label: AST Specification +toc_max_heading_level: 3 +--- + +import GeneratedDocs from '../ast-spec/generated/index.md'; + +# AST Specification + +The following auto-generated documentation describes the Abstract Syntax Tree (AST) generated by [`@typescript-eslint/typescript-estree`](../TypeScript_ESTree.mdx) for parsers such as [`@typescript-eslint/parser`](../Parser.mdx). + + diff --git a/knip.ts b/knip.ts index 75ad64af1229..b4babdda0330 100644 --- a/knip.ts +++ b/knip.ts @@ -97,6 +97,9 @@ export default { '@generated/docusaurus.config', '^@theme/.*', '^@theme-original/.*', + 'docusaurus-plugin-typedoc', + 'typedoc', + 'typedoc-plugin-markdown', ], }, 'packages/website-eslint': { diff --git a/packages/ast-spec/src/ast-node-types.ts b/packages/ast-spec/src/ast-node-types.ts index 7a637489eb0d..050bc6f61f34 100644 --- a/packages/ast-spec/src/ast-node-types.ts +++ b/packages/ast-spec/src/ast-node-types.ts @@ -88,9 +88,9 @@ export enum AST_NODE_TYPES { WhileStatement = 'WhileStatement', WithStatement = 'WithStatement', YieldExpression = 'YieldExpression', - /** - * TS-prefixed nodes - */ + + // TS_prefixed nodes + TSAbstractAccessorProperty = 'TSAbstractAccessorProperty', TSAbstractKeyword = 'TSAbstractKeyword', TSAbstractMethodDefinition = 'TSAbstractMethodDefinition', diff --git a/packages/ast-spec/src/base/ClassBase.ts b/packages/ast-spec/src/base/ClassBase.ts index 595d1393ac48..34f6d2a49e41 100644 --- a/packages/ast-spec/src/base/ClassBase.ts +++ b/packages/ast-spec/src/base/ClassBase.ts @@ -10,8 +10,9 @@ import type { BaseNode } from './BaseNode'; export interface ClassBase extends BaseNode { /** * Whether the class is an abstract class. - * ``` - * abstract class Foo {...} + * @example + * ```ts + * abstract class Foo {} * ``` */ abstract: boolean; @@ -21,16 +22,18 @@ export interface ClassBase extends BaseNode { body: ClassBody; /** * Whether the class has been `declare`d: - * ``` - * declare class Foo {...} + * @example + * ```ts + * declare class Foo {} * ``` */ declare: boolean; /** * The decorators declared for the class. - * ``` + * @example + * ```ts * @deco - * class Foo {...} + * class Foo {} * ``` */ decorators: Decorator[]; diff --git a/packages/ast-spec/src/base/FunctionBase.ts b/packages/ast-spec/src/base/FunctionBase.ts index ac4570222c1a..c6e88dcf9df4 100644 --- a/packages/ast-spec/src/base/FunctionBase.ts +++ b/packages/ast-spec/src/base/FunctionBase.ts @@ -10,9 +10,9 @@ export interface FunctionBase extends BaseNode { /** * Whether the function is async: * ``` - * async function foo(...) {...} - * const x = async function (...) {...} - * const x = async (...) => {...} + * async function foo() {} + * const x = async function () {} + * const x = async () => {} * ``` */ async: boolean; @@ -27,7 +27,7 @@ export interface FunctionBase extends BaseNode { /** * This is only `true` if and only if the node is a `TSDeclareFunction` and it has `declare`: * ``` - * declare function foo(...) {...} + * declare function foo() {} * ``` */ declare: boolean; @@ -42,8 +42,8 @@ export interface FunctionBase extends BaseNode { /** * Whether the function is a generator function: * ``` - * function *foo(...) {...} - * const x = function *(...) {...} + * function *foo() {} + * const x = function *() {} * ``` * This is always `false` for arrow functions as they cannot be generators. */ diff --git a/packages/ast-spec/src/base/NodeOrTokenData.ts b/packages/ast-spec/src/base/NodeOrTokenData.ts index 013f96002fc4..3f6e83919bf6 100644 --- a/packages/ast-spec/src/base/NodeOrTokenData.ts +++ b/packages/ast-spec/src/base/NodeOrTokenData.ts @@ -6,14 +6,9 @@ export interface NodeOrTokenData { * The source location information of the node. * * The loc property is defined as nullable by ESTree, but ESLint requires this property. - * - * @see {SourceLocation} */ loc: SourceLocation; - /** - * @see {Range} - */ range: Range; type: string; diff --git a/packages/ast-spec/src/declaration/ExportAllDeclaration/spec.ts b/packages/ast-spec/src/declaration/ExportAllDeclaration/spec.ts index aa3420051f42..5c397c3ceb02 100644 --- a/packages/ast-spec/src/declaration/ExportAllDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/ExportAllDeclaration/spec.ts @@ -9,16 +9,18 @@ export interface ExportAllDeclaration extends BaseNode { type: AST_NODE_TYPES.ExportAllDeclaration; /** * The assertions declared for the export. - * ``` - * export * from 'mod' assert { type: 'json' }; + * @example + * ```ts + * export * from 'mod' assert \{ type: 'json' \}; * ``` * @deprecated Replaced with {@link `attributes`}. */ assertions: ImportAttribute[]; /** * The attributes declared for the export. - * ``` - * export * from 'mod' with { type: 'json' }; + * @example + * ```ts + * export * from 'mod' with \{ type: 'json' \}; * ``` */ attributes: ImportAttribute[]; diff --git a/packages/ast-spec/src/declaration/ExportNamedDeclaration/spec.ts b/packages/ast-spec/src/declaration/ExportNamedDeclaration/spec.ts index b1b64b9e8075..79c266d9b5fc 100644 --- a/packages/ast-spec/src/declaration/ExportNamedDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/ExportNamedDeclaration/spec.ts @@ -10,8 +10,9 @@ interface ExportNamedDeclarationBase extends BaseNode { type: AST_NODE_TYPES.ExportNamedDeclaration; /** * The assertions declared for the export. - * ``` - * export { foo } from 'mod' assert { type: 'json' }; + * @example + * ```ts + * export { foo } from 'mod' assert \{ type: 'json' \}; * ``` * This will be an empty array if `source` is `null` * @deprecated Replaced with {@link `attributes`}. @@ -19,15 +20,17 @@ interface ExportNamedDeclarationBase extends BaseNode { assertions: ImportAttribute[]; /** * The attributes declared for the export. - * ``` - * export { foo } from 'mod' with { type: 'json' }; + * @example + * ```ts + * export { foo } from 'mod' with \{ type: 'json' \}; * ``` * This will be an empty array if `source` is `null` */ attributes: ImportAttribute[]; /** * The exported declaration. - * ``` + * @example + * ```ts * export const x = 1; * ``` * This will be `null` if `source` is not `null`, or if there are `specifiers` @@ -43,7 +46,8 @@ interface ExportNamedDeclarationBase extends BaseNode { source: StringLiteral | null; /** * The specifiers being exported. - * ``` + * @example + * ```ts * export { a, b }; * ``` * This will be an empty array if `declaration` is not `null` diff --git a/packages/ast-spec/src/declaration/ImportDeclaration/spec.ts b/packages/ast-spec/src/declaration/ImportDeclaration/spec.ts index efbee1ed8677..6ef26630fbb7 100644 --- a/packages/ast-spec/src/declaration/ImportDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/ImportDeclaration/spec.ts @@ -9,16 +9,18 @@ export interface ImportDeclaration extends BaseNode { type: AST_NODE_TYPES.ImportDeclaration; /** * The assertions declared for the export. - * ``` - * import * from 'mod' assert { type: 'json' }; + * @example + * ```ts + * import * from 'mod' assert \{ type: 'json' \}; * ``` * @deprecated Replaced with {@link `attributes`}. */ assertions: ImportAttribute[]; /** * The attributes declared for the export. - * ``` - * import * from 'mod' with { type: 'json' }; + * @example + * ```ts + * import * from 'mod' with \{ type: 'json' \}; * ``` */ attributes: ImportAttribute[]; diff --git a/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts index 1625063426c8..fe1ae6d40b76 100644 --- a/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts @@ -7,15 +7,17 @@ export interface TSEnumDeclaration extends BaseNode { type: AST_NODE_TYPES.TSEnumDeclaration; /** * Whether this is a `const` enum. - * ``` - * const enum Foo {...} + * @example + * ```ts + * const enum Foo {} * ``` */ const: boolean; /** * Whether this is a `declare`d enum. - * ``` - * declare enum Foo {...} + * @example + * ```ts + * declare enum Foo {} * ``` */ declare: boolean; diff --git a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/spec.ts index 65d2f0ff512f..617a020b8368 100644 --- a/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSImportEqualsDeclaration/spec.ts @@ -13,7 +13,8 @@ interface TSImportEqualsDeclarationBase extends BaseNode { id: Identifier; /** * The value being aliased. - * ``` + * @example + * ```ts * import F1 = A; * import F2 = A.B.C; * import F3 = require('mod'); diff --git a/packages/ast-spec/src/declaration/TSModuleDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSModuleDeclaration/spec.ts index 0606f6818525..e745b1015c94 100644 --- a/packages/ast-spec/src/declaration/TSModuleDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSModuleDeclaration/spec.ts @@ -26,6 +26,10 @@ interface TSModuleDeclarationBase extends BaseNode { body?: TSModuleBlock; /** * Whether this is a global declaration + * @example + * ```ts + * declare global {} + * ``` * * @deprecated Use {@link kind} instead */ @@ -33,7 +37,8 @@ interface TSModuleDeclarationBase extends BaseNode { global: boolean; /** * Whether the module is `declare`d - * ``` + * @example + * ```ts * declare namespace F {} * ``` */ @@ -41,7 +46,8 @@ interface TSModuleDeclarationBase extends BaseNode { /** * The keyword used to define this module declaration - * ``` + * @example + * ```ts * namespace Foo {} * ^^^^^^^^^ * diff --git a/packages/ast-spec/src/declaration/TSTypeAliasDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSTypeAliasDeclaration/spec.ts index daeec1edb501..b2a9d248b73e 100644 --- a/packages/ast-spec/src/declaration/TSTypeAliasDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSTypeAliasDeclaration/spec.ts @@ -8,7 +8,8 @@ export interface TSTypeAliasDeclaration extends BaseNode { type: AST_NODE_TYPES.TSTypeAliasDeclaration; /** * Whether the type was `declare`d. - * ``` + * @example + * ```ts * declare type T = 1; * ``` */ diff --git a/packages/ast-spec/src/declaration/VariableDeclaration/spec.ts b/packages/ast-spec/src/declaration/VariableDeclaration/spec.ts index 23a04b683248..a3e8afba2325 100644 --- a/packages/ast-spec/src/declaration/VariableDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/VariableDeclaration/spec.ts @@ -11,7 +11,8 @@ export interface LetOrConstOrVarDeclaration extends BaseNode { /** * The variables declared by this declaration. * Note that there may be 0 declarations (i.e. `const;`). - * ``` + * @example + * ```ts * let x; * let y, z; * ``` @@ -20,14 +21,16 @@ export interface LetOrConstOrVarDeclaration extends BaseNode { declarations: LetOrConstOrVarDeclarator[]; /** * Whether the declaration is `declare`d - * ``` + * @example + * ```ts * declare const x = 1; * ``` */ declare: boolean; /** * The keyword used to declare the variable(s) - * ``` + * @example + * ```ts * const x = 1; * let y = 2; * var z = 3; @@ -41,7 +44,8 @@ export interface UsingInNormalContextDeclaration extends BaseNode { /** * The variables declared by this declaration. * Note that there may be 0 declarations (i.e. `const;`). - * ``` + * @example + * ```ts * using x = 1; * using y =1, z = 2; * ``` @@ -55,7 +59,8 @@ export interface UsingInNormalContextDeclaration extends BaseNode { declare: false; /** * The keyword used to declare the variable(s) - * ``` + * @example + * ```ts * using x = 1; * await using y = 2; * ``` @@ -68,7 +73,8 @@ export interface UsingInForOfDeclaration extends BaseNode { /** * The variables declared by this declaration. * Note that there may be 0 declarations (i.e. `const;`). - * ``` + * @example + * ```ts * for(using x of y){} * ``` */ @@ -81,7 +87,8 @@ export interface UsingInForOfDeclaration extends BaseNode { declare: false; /** * The keyword used to declare the variable(s) - * ``` + * @example + * ```ts * for(using x of y){} * for(await using x of y){} * ``` diff --git a/packages/ast-spec/src/element/TSEnumMember/spec.ts b/packages/ast-spec/src/element/TSEnumMember/spec.ts index 9dd1ddeee696..ba5b81a13b1a 100644 --- a/packages/ast-spec/src/element/TSEnumMember/spec.ts +++ b/packages/ast-spec/src/element/TSEnumMember/spec.ts @@ -18,13 +18,16 @@ interface TSEnumMemberBase extends BaseNode { /** * this should only really happen in semantically invalid code (errors 1164 and 2452) * - * VALID: + * @example + * ```ts + * // VALID: * enum Foo { ['a'] } * - * INVALID: + * // INVALID: * const x = 'a'; * enum Foo { [x] } * enum Bar { ['a' + 'b'] } + * ``` */ export interface TSEnumMemberComputedName extends TSEnumMemberBase { id: PropertyNameComputed; diff --git a/packages/eslint-plugin/src/rules/consistent-type-imports.ts b/packages/eslint-plugin/src/rules/consistent-type-imports.ts index f2aa28297ae1..b0428aa71645 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-imports.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-imports.ts @@ -347,8 +347,9 @@ export default createRule({ ) { /** * checks if import has type assertions - * ``` - * import * as type from 'mod' assert { type: 'json' }; + * @example + * ```ts + * import * as type from 'mod' assert \{ type: 'json' \}; * ``` * https://github.com/typescript-eslint/typescript-eslint/issues/7527 */ diff --git a/packages/utils/src/ts-eslint/RuleTester.ts b/packages/utils/src/ts-eslint/RuleTester.ts index 76e1a2bfad73..f6dda9f0b45e 100644 --- a/packages/utils/src/ts-eslint/RuleTester.ts +++ b/packages/utils/src/ts-eslint/RuleTester.ts @@ -158,7 +158,7 @@ declare class RuleTesterBase { * Adds a new rule test to execute. * @param ruleName The name of the rule to run. * @param rule The rule to test. - * @param test The collection of tests to run. + * @param tests The collection of tests to run. */ run( ruleName: string, diff --git a/packages/utils/src/ts-eslint/SourceCode.ts b/packages/utils/src/ts-eslint/SourceCode.ts index 28f37a58ac42..c9b6974a9e7b 100644 --- a/packages/utils/src/ts-eslint/SourceCode.ts +++ b/packages/utils/src/ts-eslint/SourceCode.ts @@ -42,7 +42,7 @@ declare class TokenStore { /** * Gets the first token of the given node. * @param node The AST node. - * @param option The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`. + * @param options The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`. * @returns An object representing the token. */ getFirstToken( @@ -53,7 +53,7 @@ declare class TokenStore { * Gets the first token between two non-overlapping nodes. * @param left Node before the desired token range. * @param right Node after the desired token range. - * @param option The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`. + * @param options The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`. * @returns An object representing the token. */ getFirstTokenBetween( @@ -85,7 +85,7 @@ declare class TokenStore { /** * Gets the last token of the given node. * @param node The AST node. - * @param option The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`. + * @param options The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`. * @returns An object representing the token. */ getLastToken( @@ -96,7 +96,7 @@ declare class TokenStore { * Gets the last token between two non-overlapping nodes. * @param left Node before the desired token range. * @param right Node after the desired token range. - * @param option The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`. + * @param options The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`. * @returns An object representing the token. */ getLastTokenBetween( @@ -128,7 +128,7 @@ declare class TokenStore { /** * Gets the token that follows a given node or token. * @param node The AST node or token. - * @param option The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`. + * @param options The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`. * @returns An object representing the token. */ getTokenAfter( @@ -148,7 +148,7 @@ declare class TokenStore { /** * Gets the token starting at the specified index. * @param offset Index of the start of the token's range. - * @param option The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`. + * @param options The option object. If this is a number then it's `options.skip`. If this is a function then it's `options.filter`. * @returns The token starting at index, or null if no such token. */ getTokenByRangeStart( @@ -232,7 +232,7 @@ declare class SourceCodeBase extends TokenStore { getAllComments(): TSESTree.Comment[]; /** * Converts a (line, column) pair into a range index. - * @param loc A line/column location + * @param location A line/column location * @returns The range index of the location in the file. */ getIndexFromLoc(location: TSESTree.Position): number; diff --git a/packages/website/docusaurus.config.mts b/packages/website/docusaurus.config.mts index 369a6fd7210b..bda78fa8f505 100644 --- a/packages/website/docusaurus.config.mts +++ b/packages/website/docusaurus.config.mts @@ -342,6 +342,28 @@ const config: Config = { rules: rulesMeta, }, plugins: [ + ...['ast-spec', 'type-utils'].map(packageName => [ + 'docusaurus-plugin-typedoc', + { + entryPoints: [`../${packageName}/src/index.ts`], + enumMembersFormat: 'table', + exclude: '**/*.d.ts', + excludeExternals: true, + groupOrder: ['Functions', 'Variables', '*'], + hidePageTitle: true, + id: `typedoc-generated-${packageName}`, + indexFormat: 'table', + out: `../../docs/packages/${packageName}/generated`, + outputFileStrategy: 'modules', + parametersFormat: 'table', + plugin: [require.resolve('./tools/typedoc-plugin-no-inherit-fork.mjs')], + propertiesFormat: 'table', + readme: 'none', + tsconfig: `../${packageName}/tsconfig.json`, + typeDeclarationFormat: 'table', + useCodeBlocks: true, + }, + ]), require.resolve('./webpack.plugin'), ['@docusaurus/plugin-content-docs', pluginContentDocsOptions], ['@docusaurus/plugin-pwa', pluginPwaOptions], diff --git a/packages/website/package.json b/packages/website/package.json index e76f72e7fa1a..a8dc59380571 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -27,6 +27,7 @@ "@typescript-eslint/website-eslint": "7.17.0", "@uiw/react-shields": "2.0.1", "clsx": "^2.1.0", + "docusaurus-plugin-typedoc": "^1.0.1", "eslint": "*", "json5": "^2.2.3", "konamimojisplosion": "^0.5.2", @@ -37,6 +38,8 @@ "react-dom": "^18.2.0", "react-resizable-panels": "^0.0.63", "semver": "^7.6.0", + "typedoc": "^0.25.13", + "typedoc-plugin-markdown": "^4.0.3", "typescript": "*" }, "resolutions": { diff --git a/packages/website/sidebars/sidebar.base.js b/packages/website/sidebars/sidebar.base.js index 8655d2c29834..c3fcffdd97ad 100644 --- a/packages/website/sidebars/sidebar.base.js +++ b/packages/website/sidebars/sidebar.base.js @@ -106,7 +106,16 @@ module.exports = { 'packages/rule-tester', 'packages/scope-manager', 'packages/type-utils', - 'packages/typescript-estree', + { + collapsible: false, + items: ['packages/typescript-estree/ast-spec'], + label: 'typescript-estree', + link: { + id: 'packages/typescript-estree', + type: 'doc', + }, + type: 'category', + }, 'packages/typescript-eslint', 'packages/utils', ], diff --git a/packages/website/tools/generate-website-dts.ts b/packages/website/tools/generate-website-dts.ts index 48b7d6fa1dc3..4806d5e2b750 100644 --- a/packages/website/tools/generate-website-dts.ts +++ b/packages/website/tools/generate-website-dts.ts @@ -33,7 +33,7 @@ async function getFileAndStoreLocally( const config = await prettier.resolveConfig(path); - let contents = await response.text(); + let contents = (await response.text()) as string; contents = [...banner, '', editFunc(contents)].join('\n'); contents = await prettier.format(contents, { parser: 'typescript', diff --git a/packages/website/tools/typedoc-plugin-no-inherit-fork.mjs b/packages/website/tools/typedoc-plugin-no-inherit-fork.mjs new file mode 100644 index 000000000000..a174b28fdef8 --- /dev/null +++ b/packages/website/tools/typedoc-plugin-no-inherit-fork.mjs @@ -0,0 +1,186 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-unnecessary-condition, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/restrict-plus-operands */ +// Internal fork of https://github.com/jonchardy/typedoc-plugin-no-inherit, +// pending https://github.com/jonchardy/typedoc-plugin-no-inherit/issues/34 +// https://github.com/jonchardy/typedoc-plugin-no-inherit/tree/c799761733e31198107db87d33aea0e673a996c3 + +import { + Converter, + DeclarationReflection, + Reflection, + ReflectionKind, +} from 'typedoc'; + +export function load(app) { + new NoInheritPlugin().initialize(app); +} + +/** + * A handler that deals with inherited reflections. + */ +class NoInheritPlugin { + /** + * Create a new NoInheritPlugin instance. + */ + initialize(app) { + app.converter.on(Converter.EVENT_BEGIN, this.onBegin.bind(this)); + app.converter.on( + Converter.EVENT_CREATE_DECLARATION, + this.onDeclaration.bind(this), + null, + -1100, + ); // after ImplementsPlugin + app.converter.on( + Converter.EVENT_RESOLVE_BEGIN, + this.onBeginResolve.bind(this), + ); + this.logger = app.logger; + } + + /** + * Triggered when the converter begins converting a project. + * + * @param context The context object describing the current state the converter is in. + */ + onBegin() { + this.noInherit = []; + this.inheritedReflections = []; + } + + /** + * Triggered when the converter has created a declaration or signature reflection. + * + * Builds the list of classes/interfaces that don't inherit docs and + * the list of reflections that are inherited that could end up being removed. + * + * @param context The context object describing the current state the converter is in. + * @param reflection The reflection that is currently processed. + * @param node The node that is currently processed if available. + */ + onDeclaration(context, reflection) { + if (reflection instanceof DeclarationReflection) { + // class or interface that won't inherit docs + if ( + reflection.kindOf(ReflectionKind.ClassOrInterface) + // Fork: always add reflections, regardless of a @noInheritDoc tag + // && + // reflection.comment && + // reflection.comment.getTag('@noInheritDoc') + ) { + this.noInherit.push(reflection); + // Fork: we don't use the @noInheritDoc tag + // reflection.comment.removeTags('@noInheritDoc'); + } + // class or interface member inherited from a super + if ( + reflection.inheritedFrom && + reflection.parent && + reflection.parent.kindOf(ReflectionKind.ClassOrInterface) && + (!reflection.overwrites || + (reflection.overwrites && + reflection.overwrites !== reflection.inheritedFrom)) + ) { + this.inheritedReflections.push(reflection); + } + } + } + + /** + * Triggered when the converter begins resolving a project. + * + * Goes over the list of inherited reflections and removes any that are down the hierarchy + * from a class that doesn't inherit docs. + * + * @param context The context object describing the current state the converter is in. + */ + onBeginResolve(context) { + if (this.noInherit) { + const project = context.project; + const removals = []; + + this.inheritedReflections.forEach(reflection => { + // Look through the inheritance chain for a reflection that is flagged as noInherit for this reflection + if (this.isNoInheritRecursive(context, reflection, 0)) { + removals.push(reflection); + } + }); + + removals.forEach(removal => { + project.removeReflection(removal); + }); + } + } + + /** + * Checks whether some DeclarationReflection is in the noInherit list. + * @param search The DeclarationReflection to search for in the list. + */ + isNoInherit(search) { + if ( + this.noInherit.find(no => no.id === search.id && no.name === search.name) + ) { + return true; + } + return false; + } + + /** + * Checks whether some Reflection is in the inheritedReflections list. + * @param search The Reflection to search for in the list. + */ + isInherited(search) { + if ( + this.inheritedReflections.find( + inh => inh.id === search.id && inh.name === search.name, + ) + ) { + return true; + } + return false; + } + + /** + * Checks whether some reflection's inheritance chain is broken by a class or interface that doesn't inherit docs. + * @param context The context object describing the current state the converter is in. + * @param current The current reflection being evaluated for non-inheritance. + * @param depth The current recursion depth, used for stopping on excessively long inheritance chains. + */ + isNoInheritRecursive(context, current, depth) { + if (depth > 20) { + this.logger.warn( + `Found inheritance chain with depth > 20, stopping no inherit check: ${current.getFullName()}`, + ); + return false; // stop if we've recursed more than 20 times + } + + // As we move up the chain, check if the reflection parent is in the noInherit list + const parent = current.parent; + if (!parent) { + return false; + } + if ( + this.isNoInherit(parent) && + (depth === 0 || this.isInherited(current)) + ) { + return true; + } + + const checkExtended = type => { + const extended = type?.reflection; + if (extended instanceof Reflection) { + const upLevel = extended.getChildByName(current.name); + if (upLevel && this.isNoInheritRecursive(context, upLevel, depth + 1)) { + return true; + } + } + return false; + }; + + if (parent.extendedTypes) { + if (parent.extendedTypes.some(checkExtended)) { + return true; + } + } + + return false; + } +} diff --git a/yarn.lock b/yarn.lock index f6a564172d7e..807ad6de5d76 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6529,6 +6529,13 @@ __metadata: languageName: node linkType: hard +"ansi-sequence-parser@npm:^1.1.0": + version: 1.1.1 + resolution: "ansi-sequence-parser@npm:1.1.1" + checksum: ead5b15c596e8e85ca02951a844366c6776769dcc9fd1bd3a0db11bb21364554822c6a439877fb599e7e1ffa0b5f039f1e5501423950457f3dcb2f480c30b188 + languageName: node + linkType: hard + "ansi-styles@npm:^3.2.1": version: 3.2.1 resolution: "ansi-styles@npm:3.2.1" @@ -9047,6 +9054,15 @@ __metadata: languageName: node linkType: hard +"docusaurus-plugin-typedoc@npm:^1.0.1": + version: 1.0.1 + resolution: "docusaurus-plugin-typedoc@npm:1.0.1" + peerDependencies: + typedoc-plugin-markdown: ">=4.0.0" + checksum: 0a1f0720f3b62d37cc7cacfa8100769e8354785215b84f33951f38d65951a04d7a3b719089717a1d5dbd653f029931a7e624908334519f16fa60798a5d8224ca + languageName: node + linkType: hard + "dom-converter@npm:^0.2.0": version: 0.2.0 resolution: "dom-converter@npm:0.2.0" @@ -13842,6 +13858,13 @@ __metadata: languageName: node linkType: hard +"lunr@npm:^2.3.9": + version: 2.3.9 + resolution: "lunr@npm:2.3.9" + checksum: 176719e24fcce7d3cf1baccce9dd5633cd8bdc1f41ebe6a180112e5ee99d80373fe2454f5d4624d437e5a8319698ca6837b9950566e15d2cae5f2a543a3db4b8 + languageName: node + linkType: hard + "lz-string@npm:^1.5.0": version: 1.5.0 resolution: "lz-string@npm:1.5.0" @@ -13983,6 +14006,15 @@ __metadata: languageName: node linkType: hard +"marked@npm:^4.3.0": + version: 4.3.0 + resolution: "marked@npm:4.3.0" + bin: + marked: bin/marked.js + checksum: 0db6817893952c3ec710eb9ceafb8468bf5ae38cb0f92b7b083baa13d70b19774674be04db5b817681fa7c5c6a088f61300815e4dd75a59696f4716ad69f6260 + languageName: node + linkType: hard + "marked@npm:^5.1.2": version: 5.1.2 resolution: "marked@npm:5.1.2" @@ -14972,7 +15004,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.4, minimatch@npm:~9.0.4": +"minimatch@npm:^9.0.3, minimatch@npm:^9.0.4, minimatch@npm:~9.0.4": version: 9.0.4 resolution: "minimatch@npm:9.0.4" dependencies: @@ -18051,6 +18083,18 @@ __metadata: languageName: node linkType: hard +"shiki@npm:^0.14.7": + version: 0.14.7 + resolution: "shiki@npm:0.14.7" + dependencies: + ansi-sequence-parser: ^1.1.0 + jsonc-parser: ^3.2.0 + vscode-oniguruma: ^1.7.0 + vscode-textmate: ^8.0.0 + checksum: 2aec3b3519df977c4391df9e1825cb496e9a4d7e11395f05a0da77e4fa2f7c3d9d6e6ee94029ac699533017f2b25637ee68f6d39f05f311535c2704d0329b520 + languageName: node + linkType: hard + "side-channel@npm:^1.0.4, side-channel@npm:^1.0.6": version: 1.0.6 resolution: "side-channel@npm:1.0.6" @@ -19404,6 +19448,31 @@ __metadata: languageName: node linkType: hard +"typedoc-plugin-markdown@npm:^4.0.3": + version: 4.0.3 + resolution: "typedoc-plugin-markdown@npm:4.0.3" + peerDependencies: + typedoc: 0.25.x + checksum: 46239ad6da69721626dc255c0dca9fd7600bf49a78ff7ee319fa600bdac4f405af2a6f549302b0b10439b6fb77710260a6a60ba57fd25d6388f5c081b44a173c + languageName: node + linkType: hard + +"typedoc@npm:^0.25.13": + version: 0.25.13 + resolution: "typedoc@npm:0.25.13" + dependencies: + lunr: ^2.3.9 + marked: ^4.3.0 + minimatch: ^9.0.3 + shiki: ^0.14.7 + peerDependencies: + typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x + bin: + typedoc: bin/typedoc + checksum: 703d1f48137300b0ef3df1998a25ae745db3ca0b126f8dd1f7262918f11243a94d24dfc916cdba2baeb5a7d85d5a94faac811caf7f4fa6b7d07144dc02f7639f + languageName: node + linkType: hard + "typescript-eslint@workspace:^, typescript-eslint@workspace:packages/typescript-eslint": version: 0.0.0-use.local resolution: "typescript-eslint@workspace:packages/typescript-eslint" @@ -19866,6 +19935,20 @@ __metadata: languageName: node linkType: hard +"vscode-oniguruma@npm:^1.7.0": + version: 1.7.0 + resolution: "vscode-oniguruma@npm:1.7.0" + checksum: 53519d91d90593e6fb080260892e87d447e9b200c4964d766772b5053f5699066539d92100f77f1302c91e8fc5d9c772fbe40fe4c90f3d411a96d5a9b1e63f42 + languageName: node + linkType: hard + +"vscode-textmate@npm:^8.0.0": + version: 8.0.0 + resolution: "vscode-textmate@npm:8.0.0" + checksum: 127780dfea89559d70b8326df6ec344cfd701312dd7f3f591a718693812b7852c30b6715e3cfc8b3200a4e2515b4c96f0843c0eacc0a3020969b5de262c2a4bb + languageName: node + linkType: hard + "vscode-uri@npm:^3.0.8": version: 3.0.8 resolution: "vscode-uri@npm:3.0.8" @@ -20112,6 +20195,7 @@ __metadata: clsx: ^2.1.0 copy-webpack-plugin: ^12.0.0 cross-fetch: "*" + docusaurus-plugin-typedoc: ^1.0.1 eslint: "*" history: ^4.9.0 json5: ^2.2.3 @@ -20133,6 +20217,8 @@ __metadata: stylelint-config-standard: ^36.0.0 stylelint-order: ^6.0.4 tsx: "*" + typedoc: ^0.25.13 + typedoc-plugin-markdown: ^4.0.3 typescript: "*" unified: ^11.0.4 vfile: ^6.0.1