From 868083a0f0b7da75316743d5fa35bd86c7942119 Mon Sep 17 00:00:00 2001 From: Tim Date: Fri, 16 Jun 2023 10:52:19 +1200 Subject: [PATCH] chore: add doc generation for AI --- docs/modules/Configuration.ts.md | 2 ++ docs/modules/Domain.ts.md | 25 +++++++++++++ docs/modules/Markdown.ts.md | 15 ++++++++ schema.json | 5 +++ src/CLI.ts | 14 +++++++- src/Configuration.ts | 6 ++++ src/Core.ts | 44 +++++++++++++++++++++-- src/Domain.ts | 31 +++++++++++++++++ src/Markdown.ts | 60 +++++++++++++++++++++++++------- test/Parser.test.ts | 1 + 10 files changed, 187 insertions(+), 16 deletions(-) diff --git a/docs/modules/Configuration.ts.md b/docs/modules/Configuration.ts.md index c1773c1..78ea6ba 100644 --- a/docs/modules/Configuration.ts.md +++ b/docs/modules/Configuration.ts.md @@ -53,6 +53,7 @@ export declare const ConfigurationSchema: Schema.Struct<{ examplesCompilerOptions: Schema.optional< Schema.Union<[typeof Schema.String, Schema.Record$]> > + enableAI: Schema.optional }> ``` @@ -77,6 +78,7 @@ export interface ConfigurationShape { readonly exclude: ReadonlyArray readonly parseCompilerOptions: Record readonly examplesCompilerOptions: Record + readonly enableAI: boolean } ``` diff --git a/docs/modules/Domain.ts.md b/docs/modules/Domain.ts.md index 3545ecf..d764d88 100644 --- a/docs/modules/Domain.ts.md +++ b/docs/modules/Domain.ts.md @@ -12,6 +12,8 @@ Added in v1.0.0

Table of contents

+- [accessors](#accessors) + - [printablesFromModule](#printablesfrommodule) - [constructors](#constructors) - [createClass](#createclass) - [createConstant](#createconstant) @@ -37,6 +39,7 @@ Added in v1.0.0 - [Module (interface)](#module-interface) - [NamedDoc (interface)](#nameddoc-interface) - [Namespace (interface)](#namespace-interface) + - [Printable (type alias)](#printable-type-alias) - [Property (interface)](#property-interface) - [TypeAlias (interface)](#typealias-interface) - [sorting](#sorting) @@ -44,6 +47,18 @@ Added in v1.0.0 --- +# accessors + +## printablesFromModule + +**Signature** + +```ts +export declare const printablesFromModule: (module: Module) => ReadonlyArray +``` + +Added in v1.0.0 + # constructors ## createClass @@ -370,6 +385,16 @@ export interface Namespace extends NamedDoc { Added in v1.0.0 +## Printable (type alias) + +**Signature** + +```ts +export type Printable = Class | Constant | Export | Function | Interface | TypeAlias | Namespace +``` + +Added in v1.0.0 + ## Property (interface) **Signature** diff --git a/docs/modules/Markdown.ts.md b/docs/modules/Markdown.ts.md index 3f40593..dccfb79 100644 --- a/docs/modules/Markdown.ts.md +++ b/docs/modules/Markdown.ts.md @@ -14,6 +14,7 @@ Added in v1.0.0 - [printers](#printers) - [printModule](#printmodule) + - [printPrintableForAI](#printprintableforai) --- @@ -40,3 +41,17 @@ console.log(Markdown.printModule(m, 0)) ``` Added in v1.0.0 + +## printPrintableForAI + +**Signature** + +```ts +export declare const printPrintableForAI: ( + projectName: string, + module: Domain.Module, + printable: Domain.Printable +) => Effect.Effect +``` + +Added in v1.0.0 diff --git a/schema.json b/schema.json index e49e438..e1fcb9c 100644 --- a/schema.json +++ b/schema.json @@ -95,6 +95,11 @@ ], "description": "tsconfig for the examples options (or path to a tsconfig)", "default": {} + }, + "enableAI": { + "type": "boolean", + "description": "Whether or not to enable AI for the examples", + "default": true } }, "additionalProperties": false diff --git a/src/CLI.ts b/src/CLI.ts index e5cdda5..e92318d 100644 --- a/src/CLI.ts +++ b/src/CLI.ts @@ -156,6 +156,17 @@ const examplesCompilerOptions = Options.file("examples-tsconfig-file", { exists: Options.optional ) +const enableAI = Options.boolean("no-run-examples", { + ifPresent: false, + negationNames: ["run-examples"] +}).pipe( + Options.withFallbackConfig(Config.boolean("enableAI")), + Options.withDefault(true), + Options.withDescription( + "Whether or not to generate AI documentation for the project" + ) +) + const options = { projectHomepage, srcDir, @@ -168,7 +179,8 @@ const options = { runExamples, exclude, parseCompilerOptions, - examplesCompilerOptions + examplesCompilerOptions, + enableAI } /** @internal */ diff --git a/src/Configuration.ts b/src/Configuration.ts index bbc5349..8d8219b 100644 --- a/src/Configuration.ts +++ b/src/Configuration.ts @@ -79,6 +79,10 @@ export const ConfigurationSchema = Schema.Struct({ examplesCompilerOptions: Schema.optional(compilerOptionsSchema).annotations({ description: "tsconfig for the examples options (or path to a tsconfig)", default: {} + }), + enableAI: Schema.optional(Schema.Boolean).annotations({ + description: "Whether or not to enable AI for the examples", + default: true }) }).annotations({ identifier: "ConfigurationSchema" }) @@ -100,6 +104,7 @@ export interface ConfigurationShape { readonly exclude: ReadonlyArray readonly parseCompilerOptions: Record readonly examplesCompilerOptions: Record + readonly enableAI: boolean } /** @@ -244,6 +249,7 @@ export const load = (args: { readonly exclude: ReadonlyArray readonly parseCompilerOptions: Option.Option> readonly examplesCompilerOptions: Option.Option> + readonly enableAI: boolean }) => Effect.gen(function*(_) { // Extract the requisite services diff --git a/src/Core.ts b/src/Core.ts index 4f7a6c8..e5dede9 100644 --- a/src/Core.ts +++ b/src/Core.ts @@ -12,14 +12,15 @@ import { pipe } from "effect" import * as Array from "effect/Array" import * as Chunk from "effect/Chunk" import * as Effect from "effect/Effect" +import { pipe } from "effect/Function" import * as Stream from "effect/Stream" import * as String from "effect/String" import * as Glob from "glob" import * as Configuration from "./Configuration.js" -import type * as Domain from "./Domain.js" +import * as Domain from "./Domain.js" import { DocgenError } from "./Error.js" import * as File from "./File.js" -import { printModule } from "./Markdown.js" +import { printModule, printPrintableForAI } from "./Markdown.js" import * as Parser from "./Parser.js" import * as Process from "./Process.js" @@ -436,7 +437,8 @@ const getMarkdown = (modules: ReadonlyArray) => const index = yield* _(getMarkdownIndex) const yml = yield* _(getMarkdownConfigYML) const moduleFiles = yield* _(getModuleMarkdownFiles(modules)) - return [homepage, index, yml, ...moduleFiles] + const aiFiles = yield* _(maybeGetAIMarkdownFiles(modules)) + return [homepage, index, yml, ...moduleFiles, ...aiFiles] }) const getMarkdownHomepage = Effect.gen(function*(_) { @@ -538,6 +540,14 @@ const getModuleMarkdownOutputPath = (module: Domain.Module) => )) ) +const getAIMarkdownOutputPath = (module: Domain.Module, printable: Domain.Printable) => + Effect.map(Effect.all([Configuration.Configuration, Path.Path]), ([config, path]) => + path.join( + config.outDir, + "ai", + `${module.path.slice(1).join("-").replace(/\.ts$/, "")}-${printable.name}.md` + )) + const getModuleMarkdownFiles = (modules: ReadonlyArray) => Effect.forEach(modules, (module, order) => Effect.gen(function*(_) { @@ -546,6 +556,34 @@ const getModuleMarkdownFiles = (modules: ReadonlyArray) => return File.createFile(outputPath, content, true) })) +const getAIMarkdownFiles = (projectName: string, modules: ReadonlyArray) => + Effect.gen(function*(_) { + const aiModules = pipe( + modules, + Array.flatMap((module) => + pipe( + Domain.printablesFromModule(module), + Array.map((printable) => ({ module, printable })) + ) + ), + Array.filter(({ printable }) => printable.description._tag === "Some") + ) + + return yield* _(Effect.forEach(aiModules, ({ module, printable }) => + Effect.gen(function*(_) { + const outputPath = yield* _(getAIMarkdownOutputPath(module, printable)) + const content = yield* _(printPrintableForAI(projectName, module, printable)) + return File.createFile(outputPath, content, true) + }))) + }) + +const maybeGetAIMarkdownFiles = (modules: ReadonlyArray) => + Effect.flatMap( + Configuration.Configuration, + (config) => + config.enableAI ? getAIMarkdownFiles(config.projectName, modules) : Effect.succeed([]) + ) + const writeMarkdown = (files: ReadonlyArray) => Effect.gen(function*(_) { const config = yield* _(Configuration.Configuration) diff --git a/src/Domain.ts b/src/Domain.ts index 9c93e93..cc17199 100644 --- a/src/Domain.ts +++ b/src/Domain.ts @@ -147,6 +147,19 @@ export interface Namespace extends NamedDoc { readonly namespaces: ReadonlyArray } +/** + * @category model + * @since 1.0.0 + */ +export type Printable = + | Class + | Constant + | Export + | Function + | Interface + | TypeAlias + | Namespace + // ------------------------------------------------------------------------------------- // constructors // ------------------------------------------------------------------------------------- @@ -282,6 +295,24 @@ export const createExport = (doc: NamedDoc, signature: string): Export => ({ signature }) +// ------------------------------------------------------------------------------------- +// accessors +// ------------------------------------------------------------------------------------- + +/** + * @category accessors + * @since 1.0.0 + */ +export const printablesFromModule = (module: Module): ReadonlyArray => + [ + module.classes, + module.constants, + module.exports, + module.functions, + module.interfaces, + module.typeAliases + ].flat() + /** * @category constructors * @since 1.0.0 diff --git a/src/Markdown.ts b/src/Markdown.ts index 7e22038..61f1ca7 100644 --- a/src/Markdown.ts +++ b/src/Markdown.ts @@ -11,15 +11,6 @@ import * as String from "effect/String" import * as Prettier from "prettier" import type * as Domain from "./Domain.js" -type Printable = - | Domain.Class - | Domain.Constant - | Domain.Export - | Domain.Function - | Domain.Interface - | Domain.TypeAlias - | Domain.Namespace - const createHeaderPrinter = (level: number) => (content: string): string => "#".repeat(level) + " " + content + "\n\n" @@ -72,6 +63,28 @@ const printExamples = (es: ReadonlyArray): string => ) .join("\n\n") +const printImportDescription = ( + projectName: string, + module: Domain.Module, + method: string +): string => { + const namespace = module.path.slice(1).join("/").replace(/\.ts$/, "") + + return ( + MarkdownPrinter.paragraph( + `To import and use \`${method}\` from the "${module.name}" module:` + ) + + MarkdownPrinter.paragraph( + MarkdownPrinter.fence( + "ts", + `import * as ${module.name} from "${projectName}/${namespace}" + // Can be accessed like this + ${module.name}.${method}` + ) + ) + ) +} + const printStaticMethod = (m: Domain.Method): string => MarkdownPrinter.paragraph( MarkdownPrinter.h3(printTitle(m.name, m.deprecated, "(static method)")), @@ -229,7 +242,7 @@ export const printNamespace = (ns: Domain.Namespace, indentation: number): strin ) /** @internal */ -export const print = (p: Printable): string => { +export const print = (p: Domain.Printable): string => { switch (p._tag) { case "Class": return printClass(p) @@ -248,7 +261,7 @@ export const print = (p: Printable): string => { } } -const getPrintables = (module: Domain.Module): ReadonlyArray => +const getPrintables = (module: Domain.Module): ReadonlyArray => Array.flatten([ module.classes, module.constants, @@ -301,7 +314,7 @@ export const printModule = ( Array.sort( Order.mapInput( String.Order, - (printable: Printable) => printable.name + (printable: Domain.Printable) => printable.name ) ), Array.map(print) @@ -337,6 +350,29 @@ export const printModule = ( )) }) +/** + * @category printers + * @since 1.0.0 + */ +export const printPrintableForAI = ( + projectName: string, + module: Domain.Module, + printable: Domain.Printable +) => + prettify( + [ + MarkdownPrinter.h1(printable.name), + printDescription(printable.description), + printImportDescription(projectName, module, printable.name), + printExamples(printable.examples), + printable._tag === "Function" + ? printSignatures(printable.signatures) + : printable._tag === "Constant" + ? printSignature(printable.signature) + : "" + ].join("\n") + ) + const defaultPrettierOptions: Prettier.Options = { parser: "markdown", semi: false, diff --git a/test/Parser.test.ts b/test/Parser.test.ts index 5d0fa6a..b8715a5 100644 --- a/test/Parser.test.ts +++ b/test/Parser.test.ts @@ -21,6 +21,7 @@ const defaultConfig: Configuration.ConfigurationShape = { srcDir: "src", outDir: "docs", theme: "pmarsceill/just-the-docs", + enableAI: true, enableSearch: true, enforceDescriptions: false, enforceExamples: false,