fumadocs-typescript 5.1.1 → 5.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,5 @@
1
1
  import { ExportedDeclarations, Project, Symbol, Type } from "ts-morph";
2
2
 
3
- //#region src/create-project.d.ts
4
- interface TypescriptConfig {
5
- files?: string[];
6
- tsconfigPath?: string;
7
- /** A root directory to resolve relative path entries in the config file to. e.g. outDir */
8
- basePath?: string;
9
- }
10
- declare function createProject(options?: TypescriptConfig): Project;
11
- //#endregion
12
3
  //#region src/lib/type-table.d.ts
13
4
  interface BaseTypeTableProps {
14
5
  /**
@@ -51,20 +42,26 @@ interface GenerateTypeTableOptions extends GenerateOptions {
51
42
  //#endregion
52
43
  //#region src/cache/index.d.ts
53
44
  interface Cache {
54
- read: (key: string) => unknown | undefined | Promise<unknown | undefined>;
55
- write: (key: string, value: unknown) => void | Promise<void>;
45
+ read: (hash: string) => unknown | undefined | Promise<unknown | undefined>;
46
+ write: (hash: string, value: unknown) => void | Promise<void>;
56
47
  }
48
+ declare function generateHash(str: string): string;
57
49
  //#endregion
58
50
  //#region src/lib/base.d.ts
59
51
  interface GeneratedDoc {
52
+ /**
53
+ * unique ID generated from file name & export declaration.
54
+ */
55
+ id: string;
60
56
  name: string;
61
- description: string;
57
+ description?: string;
62
58
  entries: DocEntry[];
63
59
  }
64
60
  interface DocEntry {
65
61
  name: string;
66
62
  description: string;
67
63
  type: string;
64
+ typeHref?: string;
68
65
  simplifiedType: string;
69
66
  tags: RawTag[];
70
67
  required: boolean;
@@ -74,9 +71,8 @@ interface RawTag {
74
71
  name: string;
75
72
  text: string;
76
73
  }
77
- interface EntryContext {
74
+ interface EntryContext extends GenerateOptions {
78
75
  program: Project;
79
- transform?: Transformer;
80
76
  type: Type;
81
77
  declaration: ExportedDeclarations;
82
78
  }
@@ -103,7 +99,11 @@ interface GeneratorOptions extends TypescriptConfig {
103
99
  cache?: Cache | false;
104
100
  project?: Project;
105
101
  }
106
- declare function createGenerator(config?: GeneratorOptions | Project): {
102
+ interface TypescriptConfig {
103
+ tsconfigPath?: string;
104
+ }
105
+ declare function createProject(options?: TypescriptConfig): Promise<Project>;
106
+ declare function createGenerator(options?: GeneratorOptions): {
107
107
  generateDocumentation(file: {
108
108
  path: string;
109
109
  content?: string;
@@ -111,4 +111,4 @@ declare function createGenerator(config?: GeneratorOptions | Project): {
111
111
  generateTypeTable(props: BaseTypeTableProps, options?: GenerateTypeTableOptions): Promise<GeneratedDoc[]>;
112
112
  };
113
113
  //#endregion
114
- export { GeneratorOptions as a, Cache as c, createProject as d, Generator as i, BaseTypeTableProps as l, GenerateOptions as n, RawTag as o, GeneratedDoc as r, createGenerator as s, DocEntry as t, GenerateTypeTableOptions as u };
114
+ export { GeneratorOptions as a, createGenerator as c, generateHash as d, BaseTypeTableProps as f, Generator as i, createProject as l, GenerateOptions as n, RawTag as o, GenerateTypeTableOptions as p, GeneratedDoc as r, TypescriptConfig as s, DocEntry as t, Cache as u };
@@ -0,0 +1,45 @@
1
+ import { TypeFormatFlags } from "ts-morph";
2
+
3
+ //#region src/lib/get-simple-form.ts
4
+ function getSimpleForm(type, checker, noUndefined = false, location) {
5
+ if (type.isUndefined() && noUndefined) return "";
6
+ const alias = type.getAliasSymbol();
7
+ if (alias) {
8
+ const args = type.getAliasTypeArguments();
9
+ if (args.length === 0) return alias.getName();
10
+ return `${alias.getName()}<${args.map((arg) => getSimpleForm(arg, checker)).join(", ")}>`;
11
+ }
12
+ if (type.isUnion()) {
13
+ const types = [];
14
+ for (const t of type.getUnionTypes()) {
15
+ const str = getSimpleForm(t, checker, noUndefined);
16
+ if (str.length > 0 && str !== "never") types.unshift(str);
17
+ }
18
+ return types.length > 0 ? dedupe(types).join(" | ").replace("true | false", "boolean") : "never";
19
+ }
20
+ if (type.isIntersection()) {
21
+ const types = [];
22
+ for (const t of type.getIntersectionTypes()) {
23
+ const str = getSimpleForm(t, checker, noUndefined);
24
+ if (str.length > 0 && str !== "never") types.unshift(str);
25
+ }
26
+ return dedupe(types).join(" & ");
27
+ }
28
+ if (type.isTuple()) return `[${type.getTupleElements().map((t) => getSimpleForm(t, checker)).join(", ")}]`;
29
+ if (type.isArray() || type.isReadonlyArray()) return "array";
30
+ if (type.getCallSignatures().length > 0) return "function";
31
+ if (type.isClassOrInterface() || type.isObject()) return "object";
32
+ return type.getText(location, TypeFormatFlags.UseAliasDefinedOutsideCurrentScope);
33
+ }
34
+ function dedupe(arr) {
35
+ const dedupe = /* @__PURE__ */ new Set();
36
+ const out = [];
37
+ for (const item of arr) if (!dedupe.has(item)) {
38
+ out.push(item);
39
+ dedupe.add(item);
40
+ }
41
+ return out;
42
+ }
43
+
44
+ //#endregion
45
+ export { getSimpleForm };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as GeneratorOptions, c as Cache, d as createProject, i as Generator, l as BaseTypeTableProps, n as GenerateOptions, o as RawTag, r as GeneratedDoc, s as createGenerator, t as DocEntry, u as GenerateTypeTableOptions } from "./base-Dvhn4vZx.js";
1
+ import { a as GeneratorOptions, c as createGenerator, d as generateHash, f as BaseTypeTableProps, i as Generator, l as createProject, n as GenerateOptions, o as RawTag, p as GenerateTypeTableOptions, r as GeneratedDoc, s as TypescriptConfig, t as DocEntry, u as Cache } from "./base-CLqyTLv7.js";
2
2
  import { ResolvedShikiConfig } from "fumadocs-core/highlight/config";
3
3
  import { Nodes } from "hast";
4
4
  import { Root } from "mdast";
@@ -31,7 +31,7 @@ interface RemarkAutoTypeTableOptions {
31
31
  */
32
32
  options?: GenerateTypeTableOptions;
33
33
  /**
34
- * generate required `value` property for `remark-stringify`
34
+ * generate the stringified form of props (useful for `remark-stringify` etc).
35
35
  */
36
36
  remarkStringify?: boolean;
37
37
  generator?: Generator;
@@ -49,4 +49,4 @@ declare function remarkAutoTypeTable(config?: RemarkAutoTypeTableOptions): Trans
49
49
  //#region src/cache/fs-cache.d.ts
50
50
  declare function createFileSystemGeneratorCache(dir: string): Cache;
51
51
  //#endregion
52
- export { Cache, DocEntry, GenerateOptions, GeneratedDoc, Generator, GeneratorOptions, type MarkdownRenderer, RawTag, RemarkAutoTypeTableOptions, TypeTableProps, createFileSystemGeneratorCache, createGenerator, createProject, remarkAutoTypeTable };
52
+ export { Cache, DocEntry, GenerateOptions, GeneratedDoc, Generator, GeneratorOptions, type MarkdownRenderer, RawTag, RemarkAutoTypeTableOptions, TypeTableProps, TypescriptConfig, createFileSystemGeneratorCache, createGenerator, createProject, generateHash, remarkAutoTypeTable };
package/dist/index.js CHANGED
@@ -1,29 +1,17 @@
1
- import { n as markdownRenderer, t as parseTags } from "./parse-tags-B09hzgIX.js";
2
- import * as ts$1 from "ts-morph";
3
- import { Project, ts } from "ts-morph";
4
- import * as fs$1 from "node:fs/promises";
1
+ import { n as markdownRenderer, t as parseTags } from "./parse-tags-D9KSwbHI.js";
5
2
  import fs from "node:fs/promises";
6
- import path, { join } from "node:path";
3
+ import path from "node:path";
4
+ import { createHash } from "node:crypto";
7
5
  import { valueToEstree } from "estree-util-value-to-estree";
8
6
  import { visit } from "unist-util-visit";
9
7
  import { toEstree } from "hast-util-to-estree";
10
- import { createHash } from "node:crypto";
11
8
 
12
- //#region src/create-project.ts
13
- function createProject(options = {}) {
14
- return new Project({
15
- tsConfigFilePath: options.tsconfigPath ?? "./tsconfig.json",
16
- skipAddingFilesFromTsConfig: true
17
- });
18
- }
19
-
20
- //#endregion
21
9
  //#region src/lib/type-table.ts
22
10
  async function getTypeTableOutput(gen, { name, type, ...props }, options) {
23
- const file = props.path && options?.basePath ? join(options.basePath, props.path) : props.path;
11
+ const file = props.path && options?.basePath ? path.join(options.basePath, props.path) : props.path;
24
12
  let typeName = name;
25
13
  let content = "";
26
- if (file) content = (await fs$1.readFile(file)).toString();
14
+ if (file) content = (await fs.readFile(file)).toString();
27
15
  if (type && type.split("\n").length > 1) content += `\n${type}`;
28
16
  else if (type) {
29
17
  typeName ??= "$Fumadocs";
@@ -38,125 +26,136 @@ async function getTypeTableOutput(gen, { name, type, ...props }, options) {
38
26
  }
39
27
 
40
28
  //#endregion
41
- //#region src/lib/get-simple-form.ts
42
- function getSimpleForm(type, checker, noUndefined = false, location) {
43
- if (type.isUndefined() && noUndefined) return "";
44
- const alias = type.getAliasSymbol();
45
- if (alias) {
46
- const args = type.getAliasTypeArguments();
47
- if (args.length === 0) return alias.getName();
48
- return `${alias.getName()}<${args.map((arg) => getSimpleForm(arg, checker)).join(", ")}>`;
49
- }
50
- if (type.isUnion()) {
51
- const types = [];
52
- for (const t of type.getUnionTypes()) {
53
- const str = getSimpleForm(t, checker, noUndefined);
54
- if (str.length > 0 && str !== "never") types.unshift(str);
55
- }
56
- return types.length > 0 ? dedupe(types).join(" | ").replace("true | false", "boolean") : "never";
57
- }
58
- if (type.isIntersection()) {
59
- const types = [];
60
- for (const t of type.getIntersectionTypes()) {
61
- const str = getSimpleForm(t, checker, noUndefined);
62
- if (str.length > 0 && str !== "never") types.unshift(str);
63
- }
64
- return dedupe(types).join(" & ");
65
- }
66
- if (type.isTuple()) return `[${type.getTupleElements().map((t) => getSimpleForm(t, checker)).join(", ")}]`;
67
- if (type.isArray() || type.isReadonlyArray()) return "array";
68
- if (type.getCallSignatures().length > 0) return "function";
69
- if (type.isClassOrInterface() || type.isObject()) return "object";
70
- return type.getText(location, ts$1.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope);
71
- }
72
- function dedupe(arr) {
73
- const dedupe$1 = /* @__PURE__ */ new Set();
74
- const out = [];
75
- for (const item of arr) if (!dedupe$1.has(item)) {
76
- out.push(item);
77
- dedupe$1.add(item);
78
- }
79
- return out;
29
+ //#region src/cache/index.ts
30
+ function generateHash(str) {
31
+ return createHash("SHA256").update(str).digest("hex").slice(0, 12);
80
32
  }
81
33
 
34
+ //#endregion
35
+ //#region package.json
36
+ var version = "5.1.3";
37
+
82
38
  //#endregion
83
39
  //#region src/lib/base.ts
84
- function createGenerator(config) {
85
- const options = config instanceof Project ? { project: config } : config;
40
+ async function createProject(options = {}) {
41
+ const { Project } = await import("ts-morph");
42
+ return new Project({
43
+ tsConfigFilePath: options.tsconfigPath ?? "./tsconfig.json",
44
+ skipAddingFilesFromTsConfig: true
45
+ });
46
+ }
47
+ function createGenerator(options = {}) {
86
48
  const cache = options?.cache ? options.cache : null;
87
- let instance;
49
+ let instance = options?.project;
88
50
  function getProject() {
89
- instance ??= options?.project ?? createProject(options);
90
- return instance;
51
+ if (instance) return instance;
52
+ return instance = createProject(options);
53
+ }
54
+ function getSourceFile(project, filePath, fileContent) {
55
+ const ext = path.extname(filePath);
56
+ const fileBase = filePath.slice(0, -ext.length);
57
+ let i = 0;
58
+ let sourceFile = project.getSourceFile(filePath);
59
+ while (sourceFile && sourceFile.getFullText() !== fileContent) {
60
+ filePath = `${fileBase}.${i++}${ext}`;
61
+ sourceFile = project.getSourceFile(filePath);
62
+ }
63
+ if (sourceFile) return sourceFile;
64
+ return project.createSourceFile(filePath, fileContent, { overwrite: true });
91
65
  }
92
66
  return {
93
- async generateDocumentation(file, name, options$1) {
94
- const content = file.content ?? (await fs.readFile(path.resolve(file.path))).toString();
95
- const cacheKey = `${file.path}:${name}:${content}`;
67
+ async generateDocumentation(file, name, options = {}) {
68
+ const fullPath = path.resolve(file.path);
69
+ const content = file.content ?? (await fs.readFile(fullPath)).toString();
70
+ let cacheKey;
96
71
  if (cache) {
72
+ cacheKey = generateHash(`${file.path}:${name}:${content}:${version}`);
97
73
  const cached = await cache.read(cacheKey);
98
74
  if (cached) return cached;
99
75
  }
100
- const sourceFile = getProject().createSourceFile(file.path, content, { overwrite: true });
76
+ const project = await getProject();
77
+ const sourceFile = getSourceFile(project, fullPath, content);
101
78
  const out = [];
102
79
  for (const [k, d] of sourceFile.getExportedDeclarations()) {
103
- if (name && name !== k) continue;
80
+ if (d.length === 0 || !name || name !== k) continue;
104
81
  if (d.length > 1) console.warn(`export ${k} should not have more than one type declaration.`);
105
- out.push(generate(getProject(), k, d[0], options$1));
82
+ const declaration = d[0];
83
+ const entryContext = {
84
+ ...options,
85
+ program: project,
86
+ type: declaration.getType(),
87
+ declaration
88
+ };
89
+ out.push(await generate(encodeURI(`${path.basename(file.path)}-${name}`), k, entryContext));
106
90
  }
107
- cache?.write(cacheKey, out);
91
+ if (cache && cacheKey) await cache.write(cacheKey, out);
108
92
  return out;
109
93
  },
110
- generateTypeTable(props, options$1) {
111
- return getTypeTableOutput(this, props, options$1);
94
+ generateTypeTable(props, options) {
95
+ return getTypeTableOutput(this, props, options);
112
96
  }
113
97
  };
114
98
  }
115
- function generate(program, name, declaration, { allowInternal = false, transform } = {}) {
116
- const entryContext = {
117
- transform,
118
- program,
119
- type: declaration.getType(),
120
- declaration
121
- };
99
+ async function generate(id, name, entryContext) {
100
+ const { ts } = await import("ts-morph");
101
+ const { declaration, program } = entryContext;
122
102
  const comment = declaration.getSymbol()?.compilerSymbol.getDocumentationComment(program.getTypeChecker().compilerObject);
103
+ const entries = [];
104
+ for (const prop of declaration.getType().getProperties()) {
105
+ const out = await getDocEntry(prop, entryContext);
106
+ if (out) entries.push(out);
107
+ }
123
108
  return {
109
+ id,
124
110
  name,
125
- description: comment ? ts.displayPartsToString(comment) : "",
126
- entries: declaration.getType().getProperties().map((prop) => getDocEntry(prop, entryContext)).filter((entry) => entry && (allowInternal || !("internal" in entry.tags)))
111
+ description: comment ? ts.displayPartsToString(comment) : void 0,
112
+ entries
127
113
  };
128
114
  }
129
- function getDocEntry(prop, context) {
130
- const { transform, program } = context;
115
+ async function getDocEntry(prop, context) {
116
+ const { ts } = await import("ts-morph");
117
+ const { getSimpleForm } = await import("./get-simple-form-B8HWDNe0.js");
118
+ const { transform, allowInternal = false, program } = context;
131
119
  if (context.type.isClass() && prop.getName().startsWith("#")) return;
132
120
  const subType = prop.getTypeAtLocation(context.declaration);
133
121
  const isOptional = prop.isOptional();
134
- const tags = prop.getJsDocTags().map((tag) => ({
135
- name: tag.getName(),
136
- text: ts.displayPartsToString(tag.getText())
137
- }));
138
- let type = subType.getText(context.declaration, ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope | ts.TypeFormatFlags.NoTruncation);
139
- let simplifiedType = getSimpleForm(subType, program.getTypeChecker(), isOptional, context.declaration);
140
- for (const tag of tags) {
141
- if (tag.name === "fumadocsType") {
142
- const match = /`(?<name>.+)`$/.exec(tag.text)?.[1];
143
- if (match) type = match;
144
- continue;
145
- }
146
- if (tag.name === "remarks") {
147
- const match = /^`(?<name>.+)`/.exec(tag.text)?.[1];
148
- if (match) simplifiedType = match;
149
- }
122
+ const tags = [];
123
+ for (const tag of prop.getJsDocTags()) {
124
+ if (!allowInternal && tag.getName() === "internal") return;
125
+ tags.push({
126
+ name: tag.getName(),
127
+ text: ts.displayPartsToString(tag.getText())
128
+ });
150
129
  }
151
130
  const entry = {
152
131
  name: prop.getName(),
153
132
  description: ts.displayPartsToString(prop.compilerSymbol.getDocumentationComment(program.getTypeChecker().compilerObject)),
154
133
  tags,
155
- type,
156
- simplifiedType,
134
+ type: subType.getText(context.declaration, ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope | ts.TypeFormatFlags.NoTruncation),
135
+ simplifiedType: getSimpleForm(subType, program.getTypeChecker(), isOptional, context.declaration),
157
136
  required: !isOptional,
158
- deprecated: tags.some((tag) => tag.name === "deprecated")
137
+ deprecated: false
159
138
  };
139
+ for (const tag of tags) switch (tag.name) {
140
+ case "fumadocsType": {
141
+ const match = /`(?<name>.+)`$/.exec(tag.text)?.[1];
142
+ if (match) entry.type = match;
143
+ break;
144
+ }
145
+ case "remarks": {
146
+ const match = /^`(?<name>.+)`/.exec(tag.text)?.[1];
147
+ if (match) entry.simplifiedType = match;
148
+ break;
149
+ }
150
+ case "fumadocsHref": {
151
+ const content = tag.text.trim();
152
+ if (content.length > 0) entry.typeHref = content;
153
+ break;
154
+ }
155
+ case "deprecated":
156
+ entry.deprecated = true;
157
+ break;
158
+ }
160
159
  transform?.call(context, entry, subType, prop);
161
160
  return entry;
162
161
  }
@@ -176,8 +175,8 @@ function objectBuilder() {
176
175
  shorthand: false,
177
176
  computed: false,
178
177
  key: {
179
- type: "Identifier",
180
- name: key
178
+ type: "Literal",
179
+ value: key
181
180
  },
182
181
  kind: "init",
183
182
  value: expression
@@ -199,6 +198,7 @@ async function buildTypeProp(entries, renderer) {
199
198
  node.addJsxProperty("type", await renderer.renderTypeToHast(entry.simplifiedType));
200
199
  node.addJsxProperty("typeDescription", await renderer.renderTypeToHast(entry.type));
201
200
  node.addExpressionNode("required", valueToEstree(entry.required));
201
+ if (entry.typeHref) node.addExpressionNode("typeDescriptionLink", valueToEstree(entry.typeHref));
202
202
  if (tags.default) node.addJsxProperty("default", await renderer.renderTypeToHast(tags.default));
203
203
  if (tags.returns) node.addJsxProperty("returns", await renderer.renderMarkdownToHast(tags.returns));
204
204
  if (tags.params) node.addExpressionNode("parameters", {
@@ -239,55 +239,79 @@ function remarkAutoTypeTable(config = {}) {
239
239
  if (renderMarkdown) renderer.renderMarkdownToHast = renderMarkdown;
240
240
  if (renderType) renderer.renderTypeToHast = renderType;
241
241
  }
242
+ async function generate(file, props, attributes) {
243
+ let basePath = props.cwd ? file.cwd : generateOptions.basePath;
244
+ if (file.dirname) basePath ??= file.dirname;
245
+ const output = await generator.generateTypeTable(props, {
246
+ ...generateOptions,
247
+ basePath
248
+ });
249
+ const rendered = [];
250
+ for (const doc of output) rendered.push({
251
+ type: "mdxJsxFlowElement",
252
+ name: outputName,
253
+ attributes: [
254
+ {
255
+ type: "mdxJsxAttribute",
256
+ name: "id",
257
+ value: `type-table-${doc.id}`
258
+ },
259
+ {
260
+ type: "mdxJsxAttribute",
261
+ name: "type",
262
+ value: {
263
+ type: "mdxJsxAttributeValueExpression",
264
+ value: remarkStringify ? JSON.stringify(doc, null, 2) : "",
265
+ data: { estree: {
266
+ type: "Program",
267
+ sourceType: "module",
268
+ body: [{
269
+ type: "ExpressionStatement",
270
+ expression: await buildTypeProp(doc.entries, renderer)
271
+ }]
272
+ } }
273
+ }
274
+ },
275
+ ...attributes
276
+ ],
277
+ children: []
278
+ });
279
+ return rendered;
280
+ }
242
281
  return async (tree, file) => {
243
282
  const queue = [];
244
- async function run(node, props) {
245
- let basePath = props.cwd ? file.cwd : generateOptions.basePath;
246
- if (file.dirname) basePath ??= file.dirname;
247
- const rendered = (await generator.generateTypeTable(props, {
248
- ...generateOptions,
249
- basePath
250
- })).map(async (doc) => {
251
- return {
252
- type: "mdxJsxFlowElement",
253
- name: outputName,
254
- attributes: [{
255
- type: "mdxJsxAttribute",
256
- name: "type",
257
- value: {
258
- type: "mdxJsxAttributeValueExpression",
259
- value: remarkStringify ? JSON.stringify(doc, null, 2) : "",
260
- data: { estree: {
261
- type: "Program",
262
- sourceType: "module",
263
- body: [{
264
- type: "ExpressionStatement",
265
- expression: await buildTypeProp(doc.entries, renderer)
266
- }]
267
- } }
268
- }
269
- }],
270
- children: []
271
- };
272
- });
273
- Object.assign(node, {
274
- type: "root",
275
- attributes: [],
276
- children: await Promise.all(rendered)
277
- });
278
- }
279
283
  visit(tree, "mdxJsxFlowElement", (node) => {
280
284
  if (node.name !== name) return;
281
285
  const props = {};
286
+ const attributes = [];
282
287
  const onError = (message, cause) => {
283
288
  const location = node.position ? `${file.path}:${node.position.start.line}:${node.position.start.column}` : file.path;
284
289
  throw new Error(`${location} from <auto-type-table>: ${message}`, { cause });
285
290
  };
286
- for (const attr of node.attributes) if (attr.type !== "mdxJsxAttribute") onError("only named attributes are allowed.");
287
- else if (typeof attr.value === "string") props[attr.name] = attr.value;
288
- else if (attr.value === null) props[attr.name] = true;
289
- else onError("only string & boolean attributes are allowed.");
290
- queue.push(run(node, props).catch((err) => {
291
+ for (const attr of node.attributes) {
292
+ if (attr.type !== "mdxJsxAttribute") {
293
+ attributes.push(attr);
294
+ continue;
295
+ }
296
+ switch (attr.name) {
297
+ case "cwd":
298
+ props.cwd = true;
299
+ break;
300
+ case "path":
301
+ case "name":
302
+ case "type":
303
+ if (typeof attr.value === "string") props[attr.name] = attr.value;
304
+ else onError(`invalid type for attribute ${attr.name}: ${typeof attr.value}, expected: string`);
305
+ break;
306
+ default: attributes.push(attr);
307
+ }
308
+ }
309
+ queue.push(generate(file, props, attributes).then((children) => {
310
+ Object.assign(node, {
311
+ type: "root",
312
+ children
313
+ });
314
+ }).catch((err) => {
291
315
  onError("failed to generate type table", err);
292
316
  }));
293
317
  return "skip";
@@ -302,13 +326,11 @@ function createFileSystemGeneratorCache(dir) {
302
326
  dir = path.resolve(dir);
303
327
  const initDirPromise = fs.mkdir(dir, { recursive: true }).catch(() => {});
304
328
  return {
305
- async write(input, data) {
306
- const hash = createHash("SHA256").update(input).digest("hex").slice(0, 12);
329
+ async write(hash, data) {
307
330
  await initDirPromise;
308
331
  await fs.writeFile(path.join(dir, `${hash}.json`), JSON.stringify(data));
309
332
  },
310
- async read(input) {
311
- const hash = createHash("SHA256").update(input).digest("hex").slice(0, 12);
333
+ async read(hash) {
312
334
  try {
313
335
  return JSON.parse((await fs.readFile(path.join(dir, `${hash}.json`))).toString());
314
336
  } catch {
@@ -319,4 +341,4 @@ function createFileSystemGeneratorCache(dir) {
319
341
  }
320
342
 
321
343
  //#endregion
322
- export { createFileSystemGeneratorCache, createGenerator, createProject, remarkAutoTypeTable };
344
+ export { createFileSystemGeneratorCache, createGenerator, createProject, generateHash, remarkAutoTypeTable };
@@ -1,19 +1,17 @@
1
- import { i as Generator, l as BaseTypeTableProps, u as GenerateTypeTableOptions } from "../base-Dvhn4vZx.js";
1
+ import { f as BaseTypeTableProps, i as Generator, p as GenerateTypeTableOptions } from "../base-CLqyTLv7.js";
2
2
  import * as runtime from "react/jsx-runtime";
3
3
  import "server-only";
4
4
  import { ResolvedShikiConfig } from "fumadocs-core/highlight/config";
5
- import { ReactNode } from "react";
5
+ import { ComponentProps, ReactNode } from "react";
6
6
 
7
7
  //#region src/ui/auto-type-table.d.ts
8
- interface JSXMarkdownRenderer {
9
- renderMarkdown: (md: string) => Promise<ReactNode>;
10
- renderType: (type: string) => Promise<ReactNode>;
11
- }
12
- interface AutoTypeTableProps extends BaseTypeTableProps, Partial<JSXMarkdownRenderer> {
8
+ interface AutoTypeTableProps extends BaseTypeTableProps, ComponentProps<'div'> {
13
9
  generator: Generator;
14
10
  /** Shiki configuration when using default `renderMarkdown` & `renderType` */
15
11
  shiki?: ResolvedShikiConfig;
16
12
  options?: GenerateTypeTableOptions;
13
+ renderMarkdown?: (md: string) => Promise<ReactNode>;
14
+ renderType?: (type: string) => Promise<ReactNode>;
17
15
  }
18
16
  declare function AutoTypeTable({
19
17
  generator,
@@ -21,6 +19,9 @@ declare function AutoTypeTable({
21
19
  renderType,
22
20
  renderMarkdown,
23
21
  shiki,
22
+ name,
23
+ path,
24
+ type,
24
25
  ...props
25
26
  }: AutoTypeTableProps): Promise<Promise<runtime.JSX.Element>[]>;
26
27
  //#endregion
package/dist/ui/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as markdownRenderer, t as parseTags } from "../parse-tags-B09hzgIX.js";
1
+ import { n as markdownRenderer, t as parseTags } from "../parse-tags-D9KSwbHI.js";
2
2
  import { TypeTable } from "fumadocs-ui/components/type-table";
3
3
  import { toJsxRuntime } from "hast-util-to-jsx-runtime";
4
4
  import * as runtime from "react/jsx-runtime";
@@ -7,13 +7,17 @@ import defaultMdxComponents from "fumadocs-ui/mdx";
7
7
  import "server-only";
8
8
 
9
9
  //#region src/ui/auto-type-table.tsx
10
- async function AutoTypeTable({ generator, options = {}, renderType, renderMarkdown, shiki, ...props }) {
10
+ async function AutoTypeTable({ generator, options, renderType, renderMarkdown, shiki, name, path, type, ...props }) {
11
11
  if (!renderType || !renderMarkdown) {
12
12
  const renderer = markdownRenderer(shiki);
13
13
  renderType ??= async (v) => toJsx(await renderer.renderTypeToHast(v));
14
14
  renderMarkdown ??= async (v) => toJsx(await renderer.renderMarkdownToHast(v));
15
15
  }
16
- return (await generator.generateTypeTable(props, options)).map(async (item) => {
16
+ return (await generator.generateTypeTable({
17
+ name,
18
+ path,
19
+ type
20
+ }, options)).map(async (item) => {
17
21
  const entries = item.entries.map(async (entry) => {
18
22
  const tags = parseTags(entry.tags);
19
23
  const paramNodes = [];
@@ -24,6 +28,7 @@ async function AutoTypeTable({ generator, options = {}, renderType, renderMarkdo
24
28
  return [entry.name, {
25
29
  type: await renderType(entry.simplifiedType),
26
30
  typeDescription: await renderType(entry.type),
31
+ typeDescriptionLink: entry.typeHref,
27
32
  description: await renderMarkdown(entry.description),
28
33
  default: tags.default ? await renderType(tags.default) : void 0,
29
34
  parameters: paramNodes,
@@ -32,7 +37,11 @@ async function AutoTypeTable({ generator, options = {}, renderType, renderMarkdo
32
37
  returns: tags.returns ? await renderMarkdown(tags.returns) : void 0
33
38
  }];
34
39
  });
35
- return /* @__PURE__ */ jsx(TypeTable, { type: Object.fromEntries(await Promise.all(entries)) }, item.name);
40
+ return /* @__PURE__ */ jsx(TypeTable, {
41
+ id: `type-table-${item.id}`,
42
+ type: Object.fromEntries(await Promise.all(entries)),
43
+ ...props
44
+ }, item.name);
36
45
  });
37
46
  }
38
47
  function toJsx(hast) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-typescript",
3
- "version": "5.1.1",
3
+ "version": "5.1.3",
4
4
  "description": "Typescript Integration for Fumadocs",
5
5
  "keywords": [
6
6
  "Docs",
@@ -36,7 +36,6 @@
36
36
  "hast-util-to-jsx-runtime": "^2.3.6",
37
37
  "remark": "^15.0.1",
38
38
  "remark-rehype": "^11.1.2",
39
- "tinyglobby": "^0.2.15",
40
39
  "ts-morph": "^27.0.2",
41
40
  "unified": "^11.0.5",
42
41
  "unist-util-visit": "^5.1.0"
@@ -46,17 +45,22 @@
46
45
  "@types/estree": "^1.0.8",
47
46
  "@types/hast": "^3.0.4",
48
47
  "@types/mdast": "^4.0.4",
49
- "@types/node": "25.1.0",
50
- "@types/react": "^19.2.10",
48
+ "@types/node": "25.2.3",
49
+ "@types/react": "^19.2.14",
51
50
  "@types/react-dom": "^19.2.3",
52
- "tsdown": "^0.19.0",
51
+ "mdast-util-mdx": "^3.0.0",
52
+ "tsdown": "^0.20.3",
53
53
  "typescript": "^5.9.3",
54
+ "vfile": "^6.0.3",
54
55
  "eslint-config-custom": "0.0.0",
55
- "fumadocs-core": "16.5.0",
56
- "fumadocs-ui": "16.5.0",
56
+ "fumadocs-core": "16.6.1",
57
+ "fumadocs-ui": "16.6.1",
57
58
  "tsconfig": "0.0.0"
58
59
  },
59
60
  "peerDependencies": {
61
+ "@types/estree": "*",
62
+ "@types/hast": "*",
63
+ "@types/mdast": "*",
60
64
  "@types/react": "*",
61
65
  "fumadocs-core": "^16.5.0",
62
66
  "fumadocs-ui": "^16.5.0",
@@ -64,6 +68,15 @@
64
68
  "typescript": "*"
65
69
  },
66
70
  "peerDependenciesMeta": {
71
+ "@types/estree": {
72
+ "optional": true
73
+ },
74
+ "@types/hast": {
75
+ "optional": true
76
+ },
77
+ "@types/mdast": {
78
+ "optional": true
79
+ },
67
80
  "fumadocs-ui": {
68
81
  "optional": true
69
82
  },