1/**
2 * Data models shared by the docgen parser, checker, and printer.
3 *
4 * @since 0.6.0
5 */
6
7import type * as Array from "effect/Array"
8import * as Context from "effect/Context"
9import * as Data from "effect/Data"
10import * as Effect from "effect/Effect"
11import * as Layer from "effect/Layer"
12import * as Order from "effect/Order"
13import * as Rec from "effect/Record"
14import * as String from "effect/String"
15import type * as Parser from "./Parser.ts"
16
17/**
18 * Base model for a named, documented declaration.
19 *
20 * @category models
21 * @since 0.6.0
22 */
23export class DocEntry {
24 readonly name: string
25 readonly doc: Doc
26 readonly signature: string
27 readonly position: Position
28 constructor(
29 name: string,
30 doc: Doc,
31 signature: string,
32 position: Position
33 ) {
34 this.name = name
35 this.doc = doc
36 this.signature = signature
37 this.position = position
38 }
39}
40
41/**
42 * Parsed JSDoc content attached to a declaration or module.
43 *
44 * @category models
45 * @since 0.6.0
46 */
47export class Doc {
48 readonly description: string | undefined
49 readonly since: ReadonlyArray<string>
50 readonly deprecated: ReadonlyArray<string>
51 readonly examples: ReadonlyArray<string>
52 readonly category: ReadonlyArray<string>
53 readonly throws: ReadonlyArray<string>
54 readonly sees: ReadonlyArray<string>
55 readonly tags: Record<string, ReadonlyArray<string> | undefined>
56 constructor(
57 description: string | undefined,
58 since: ReadonlyArray<string>,
59 deprecated: ReadonlyArray<string>,
60 examples: ReadonlyArray<string>,
61 category: ReadonlyArray<string>,
62 throws: ReadonlyArray<string>,
63 sees: ReadonlyArray<string>,
64 tags: Record<string, ReadonlyArray<string> | undefined>
65 ) {
66 this.description = description
67 this.since = since
68 this.deprecated = deprecated
69 this.examples = examples
70 this.category = category
71 this.throws = throws
72 this.sees = sees
73 this.tags = tags
74 }
75
76 modifyDescription(description: string | undefined): Doc {
77 return new Doc(
78 description,
79 this.since,
80 this.deprecated,
81 this.examples,
82 this.category,
83 this.throws,
84 this.sees,
85 this.tags
86 )
87 }
88}
89
90/**
91 * Parsed documentation model for one source module.
92 *
93 * @category models
94 * @since 0.6.0
95 */
96export class Module {
97 readonly source: Parser.SourceShape
98 readonly name: string
99 readonly doc: Doc
100 readonly path: Array.NonEmptyReadonlyArray<string>
101 readonly classes: ReadonlyArray<Class>
102 readonly interfaces: ReadonlyArray<Interface>
103 readonly functions: ReadonlyArray<Function>
104 readonly typeAliases: ReadonlyArray<TypeAlias>
105 readonly constants: ReadonlyArray<Constant>
106 readonly exports: ReadonlyArray<Export>
107 readonly namespaces: ReadonlyArray<Namespace>
108 constructor(
109 source: Parser.SourceShape,
110 name: string,
111 doc: Doc,
112 path: Array.NonEmptyReadonlyArray<string>,
113 classes: ReadonlyArray<Class>,
114 interfaces: ReadonlyArray<Interface>,
115 functions: ReadonlyArray<Function>,
116 typeAliases: ReadonlyArray<TypeAlias>,
117 constants: ReadonlyArray<Constant>,
118 exports: ReadonlyArray<Export>,
119 namespaces: ReadonlyArray<Namespace>
120 ) {
121 this.source = source
122 this.name = name
123 this.doc = doc
124 this.path = path
125 this.classes = classes
126 this.interfaces = interfaces
127 this.functions = functions
128 this.typeAliases = typeAliases
129 this.constants = constants
130 this.exports = exports
131 this.namespaces = namespaces
132 }
133}
134
135/**
136 * Parsed documentation model for a class and its documented members.
137 *
138 * @category models
139 * @since 0.6.0
140 */
141export class Class extends DocEntry {
142 readonly _tag = "Class"
143 readonly methods: ReadonlyArray<DocEntry>
144 readonly staticMethods: ReadonlyArray<DocEntry>
145 readonly properties: ReadonlyArray<DocEntry>
146 constructor(
147 name: string,
148 doc: Doc,
149 signature: string,
150 position: Position,
151 methods: ReadonlyArray<DocEntry>,
152 staticMethods: ReadonlyArray<DocEntry>,
153 properties: ReadonlyArray<DocEntry>
154 ) {
155 super(name, doc, signature, position)
156 this.methods = methods
157 this.staticMethods = staticMethods
158 this.properties = properties
159 }
160}
161
162/**
163 * Parsed documentation model for an interface.
164 *
165 * @category models
166 * @since 0.6.0
167 */
168export class Interface extends DocEntry {
169 readonly _tag = "Interface"
170}
171
172/**
173 * One-based source position used in diagnostics.
174 *
175 * @category models
176 * @since 0.6.0
177 */
178export interface Position {
179 readonly line: number
180 readonly column: number
181}
182
183/**
184 * Parsed documentation model for a function.
185 *
186 * @category models
187 * @since 0.6.0
188 */
189export class Function extends DocEntry {
190 readonly _tag = "Function"
191}
192
193/**
194 * Parsed documentation model for a type alias.
195 *
196 * @category models
197 * @since 0.6.0
198 */
199export class TypeAlias extends DocEntry {
200 readonly _tag = "TypeAlias"
201}
202
203/**
204 * Parsed documentation model for a constant.
205 *
206 * @category models
207 * @since 0.6.0
208 */
209export class Constant extends DocEntry {
210 readonly _tag = "Constant"
211}
212
213/**
214 * Parsed documentation model for an explicit named or namespace export.
215 *
216 * @category models
217 * @since 0.6.0
218 */
219export class Export extends DocEntry {
220 readonly _tag = "Export"
221 readonly isNamespaceExport: boolean
222 constructor(
223 name: string,
224 doc: Doc,
225 signature: string,
226 position: Position,
227 isNamespaceExport: boolean
228 ) {
229 super(name, doc, signature, position)
230 this.isNamespaceExport = isNamespaceExport
231 }
232}
233
234/**
235 * Parsed documentation model for a namespace and its nested declarations.
236 *
237 * @category models
238 * @since 0.6.0
239 */
240export class Namespace {
241 readonly _tag = "Namespace"
242 readonly name: string
243 readonly doc: Doc
244 readonly position: Position
245 readonly interfaces: ReadonlyArray<Interface>
246 readonly typeAliases: ReadonlyArray<TypeAlias>
247 readonly namespaces: ReadonlyArray<Namespace>
248 constructor(
249 name: string,
250 doc: Doc,
251 position: Position,
252 interfaces: ReadonlyArray<Interface>,
253 typeAliases: ReadonlyArray<TypeAlias>,
254 namespaces: ReadonlyArray<Namespace>
255 ) {
256 this.name = name
257 this.doc = doc
258 this.position = position
259 this.interfaces = interfaces
260 this.typeAliases = typeAliases
261 this.namespaces = namespaces
262 }
263}
264
265/**
266 * A comparator function for sorting `Module` objects by their file path, represented as a string.
267 * The file path is converted to lowercase before comparison.
268 *
269 * @category sorting
270 * @since 0.6.0
271 */
272export const ByPath: Order.Order<Module> = Order.mapInput(
273 String.Order,
274 (module: Module) => module.path.join("/").toLowerCase()
275)
276
277/**
278 * Represents a file which can be optionally overwriteable.
279 *
280 * @category models
281 * @since 0.6.0
282 */
283export class File {
284 readonly path: string
285 readonly content: string
286 readonly isOverwriteable: boolean
287 constructor(
288 path: string,
289 content: string,
290 isOverwriteable: boolean = false
291 ) {
292 this.path = path
293 this.content = content
294 this.isOverwriteable = isOverwriteable
295 }
296}
297
298/**
299 * Type ID for `DocgenError`.
300 *
301 * @category symbols
302 * @since 0.6.0
303 */
304export const DocgenErrorTypeId = Symbol.for("@effect/docgen/DocgenError")
305
306/**
307 * Type-level representation of `DocgenErrorTypeId`.
308 *
309 * @category symbols
310 * @since 0.6.0
311 */
312export type DocgenErrorTypeId = typeof DocgenErrorTypeId
313
314/**
315 * Error reported when documentation generation cannot continue.
316 *
317 * @category errors
318 * @since 0.6.0
319 */
320export class DocgenError extends Data.TaggedError("DocgenError")<{
321 readonly message: string
322}> {}
323
324/**
325 * Represents a handle to the currently executing process.
326 *
327 * @category services
328 * @since 0.6.0
329 */
330export class Process extends Context.Service<Process, {
331 readonly cwd: Effect.Effect<string>
332 readonly platform: Effect.Effect<NodeJS.Platform>
333 readonly argv: Effect.Effect<Array<string>>
334 readonly env: Effect.Effect<Record<string, string>>
335}>()("@effect/docgen/Process") {
336 static readonly layer = Layer.succeed(Process, {
337 cwd: Effect.sync(() => process.cwd()),
338 platform: Effect.sync(() => process.platform),
339 argv: Effect.sync(() => process.argv),
340 env: Effect.sync(() => {
341 const env: Record<string, string> = {}
342 for (const [key, value] of Object.entries(process.env)) {
343 if (value !== undefined) Rec.assignProperty(env, key, value)
344 }
345 return env
346 })
347 })
348}