compmark-vue 0.2.5 → 0.2.6

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.
package/dist/cli.mjs CHANGED
@@ -1,7 +1,270 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
- import { join, resolve } from "node:path";
4
- import { compileScript, parse } from "@vue/compiler-sfc";
2
+ import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { babelParse, compileScript, parse } from "@vue/compiler-sfc";
5
+ //#region src/resolver.ts
6
+ function resolveImportPath(importSource, sfcDir) {
7
+ try {
8
+ if (!importSource.startsWith(".") && !importSource.startsWith("@/") && !importSource.startsWith("~/")) return null;
9
+ if (importSource.startsWith("./") || importSource.startsWith("../")) return tryResolveFile(resolve(sfcDir, importSource));
10
+ const tsconfig = findTsConfig(sfcDir);
11
+ if (!tsconfig) return null;
12
+ const { paths, baseUrl } = readTsConfigPaths(tsconfig);
13
+ if (!paths) return null;
14
+ const configDir = dirname(tsconfig);
15
+ const resolvedBaseUrl = baseUrl ? resolve(configDir, baseUrl) : configDir;
16
+ for (const [pattern, targets] of Object.entries(paths)) {
17
+ const prefix = pattern.replace(/\*$/, "");
18
+ if (!importSource.startsWith(prefix)) continue;
19
+ const remainder = importSource.slice(prefix.length);
20
+ for (const target of targets) {
21
+ const result = tryResolveFile(resolve(resolvedBaseUrl, target.replace(/\*$/, "") + remainder));
22
+ if (result) return result;
23
+ }
24
+ }
25
+ return null;
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+ function tryResolveFile(basePath) {
31
+ if (existsSync(basePath) && !isDirectory(basePath)) return basePath;
32
+ for (const ext of [".ts", ".js"]) {
33
+ const candidate = basePath + ext;
34
+ if (existsSync(candidate)) return candidate;
35
+ }
36
+ for (const ext of ["/index.ts", "/index.js"]) {
37
+ const candidate = basePath + ext;
38
+ if (existsSync(candidate)) return candidate;
39
+ }
40
+ return null;
41
+ }
42
+ function isDirectory(filePath) {
43
+ try {
44
+ return statSync(filePath).isDirectory();
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+ function findTsConfig(startDir) {
50
+ let dir = resolve(startDir);
51
+ const root = resolve("/");
52
+ while (dir !== root) {
53
+ const tsconfig = join(dir, "tsconfig.json");
54
+ if (existsSync(tsconfig)) return tsconfig;
55
+ const jsconfig = join(dir, "jsconfig.json");
56
+ if (existsSync(jsconfig)) return jsconfig;
57
+ const parent = dirname(dir);
58
+ if (parent === dir) break;
59
+ dir = parent;
60
+ }
61
+ return null;
62
+ }
63
+ function readTsConfigPaths(configPath) {
64
+ try {
65
+ const content = JSON.parse(readFileSync(configPath, "utf-8"));
66
+ let paths = content.compilerOptions?.paths ?? null;
67
+ let baseUrl = content.compilerOptions?.baseUrl;
68
+ if (content.extends) {
69
+ const parentPath = resolve(dirname(configPath), content.extends);
70
+ const parentConfigFile = parentPath.endsWith(".json") ? parentPath : parentPath + ".json";
71
+ if (existsSync(parentConfigFile)) try {
72
+ const parentContent = JSON.parse(readFileSync(parentConfigFile, "utf-8"));
73
+ const parentPaths = parentContent.compilerOptions?.paths;
74
+ const parentBaseUrl = parentContent.compilerOptions?.baseUrl;
75
+ if (!paths && parentPaths) paths = parentPaths;
76
+ if (!baseUrl && parentBaseUrl) baseUrl = parentBaseUrl;
77
+ } catch {}
78
+ }
79
+ return {
80
+ paths,
81
+ baseUrl
82
+ };
83
+ } catch {
84
+ return {
85
+ paths: null,
86
+ baseUrl: void 0
87
+ };
88
+ }
89
+ }
90
+ function resolveComposableTypes(filePath, exportName, variableNames) {
91
+ try {
92
+ const funcNode = findExportedFunction(babelParse(readFileSync(filePath, "utf-8"), {
93
+ plugins: ["typescript", "jsx"],
94
+ sourceType: "module"
95
+ }).program.body, exportName);
96
+ if (!funcNode) return /* @__PURE__ */ new Map();
97
+ const body = getFunctionBody(funcNode);
98
+ if (!body) return /* @__PURE__ */ new Map();
99
+ const returnProps = findReturnProperties(body);
100
+ if (!returnProps) return /* @__PURE__ */ new Map();
101
+ const result = /* @__PURE__ */ new Map();
102
+ const nameSet = new Set(variableNames);
103
+ for (const prop of returnProps) {
104
+ let propName = null;
105
+ if (prop.type === "ObjectProperty") propName = prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "StringLiteral" ? prop.key.value : null;
106
+ else if (prop.type === "ObjectMethod") {
107
+ propName = prop.key.type === "Identifier" ? prop.key.name : null;
108
+ if (propName && nameSet.has(propName)) result.set(propName, inferFunctionSignature(prop));
109
+ continue;
110
+ } else if (prop.type === "SpreadElement") continue;
111
+ if (!propName || !nameSet.has(propName)) continue;
112
+ if (prop.type === "ObjectProperty" && prop.shorthand) {
113
+ const type = traceVariableType(propName, body);
114
+ result.set(propName, type);
115
+ } else if (prop.type === "ObjectProperty") {
116
+ const type = inferType(prop.value);
117
+ result.set(propName, type);
118
+ }
119
+ }
120
+ return result;
121
+ } catch {
122
+ return /* @__PURE__ */ new Map();
123
+ }
124
+ }
125
+ function findExportedFunction(stmts, exportName) {
126
+ for (const stmt of stmts) {
127
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "FunctionDeclaration" && stmt.declaration.id?.name === exportName) return stmt.declaration;
128
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration") {
129
+ for (const decl of stmt.declaration.declarations) if (decl.id.type === "Identifier" && decl.id.name === exportName && decl.init && (decl.init.type === "ArrowFunctionExpression" || decl.init.type === "FunctionExpression")) return decl.init;
130
+ }
131
+ if (stmt.type === "ExportDefaultDeclaration" && stmt.declaration.type === "FunctionDeclaration" && stmt.declaration.id?.name === exportName) return stmt.declaration;
132
+ if (stmt.type === "ExportDefaultDeclaration" && stmt.declaration.type === "FunctionDeclaration" && !stmt.declaration.id) return stmt.declaration;
133
+ if (stmt.type === "ExportDefaultDeclaration" && (stmt.declaration.type === "ArrowFunctionExpression" || stmt.declaration.type === "FunctionExpression")) return stmt.declaration;
134
+ if (stmt.type === "FunctionDeclaration" && stmt.id?.name === exportName) {
135
+ if (stmts.some((s) => s.type === "ExportNamedDeclaration" && !s.declaration && s.specifiers.some((spec) => spec.type === "ExportSpecifier" && (spec.local.type === "Identifier" && spec.local.name === exportName || spec.exported.type === "Identifier" && spec.exported.name === exportName)))) return stmt;
136
+ }
137
+ if (stmt.type === "VariableDeclaration") {
138
+ for (const decl of stmt.declarations) if (decl.id.type === "Identifier" && decl.id.name === exportName && decl.init && (decl.init.type === "ArrowFunctionExpression" || decl.init.type === "FunctionExpression")) {
139
+ if (stmts.some((s) => s.type === "ExportNamedDeclaration" && !s.declaration && s.specifiers.some((spec) => spec.type === "ExportSpecifier" && (spec.local.type === "Identifier" && spec.local.name === exportName || spec.exported.type === "Identifier" && spec.exported.name === exportName)))) return decl.init;
140
+ }
141
+ }
142
+ }
143
+ return null;
144
+ }
145
+ function getFunctionBody(node) {
146
+ if (node.body.type === "BlockStatement") return node.body.body;
147
+ return null;
148
+ }
149
+ function findReturnProperties(body) {
150
+ for (let i = body.length - 1; i >= 0; i--) {
151
+ const stmt = body[i];
152
+ if (stmt.type === "ReturnStatement" && stmt.argument?.type === "ObjectExpression") return stmt.argument.properties;
153
+ }
154
+ return null;
155
+ }
156
+ function traceVariableType(name, body) {
157
+ for (let i = body.length - 1; i >= 0; i--) {
158
+ const stmt = body[i];
159
+ if (stmt.type === "FunctionDeclaration" && stmt.id?.name === name) return inferFunctionSignature(stmt);
160
+ if (stmt.type === "VariableDeclaration") {
161
+ for (const decl of stmt.declarations) if (decl.id.type === "Identifier" && decl.id.name === name && decl.init) return inferType(decl.init);
162
+ }
163
+ }
164
+ return "unknown";
165
+ }
166
+ function inferType(node) {
167
+ if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "ref") {
168
+ const typeParams = node.typeParameters;
169
+ if (typeParams?.params?.length > 0) return `Ref<${resolveTypeAnnotation(typeParams.params[0])}>`;
170
+ const arg = node.arguments[0];
171
+ if (!arg) return "Ref<unknown>";
172
+ return `Ref<${inferLiteralType(arg)}>`;
173
+ }
174
+ if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "computed") return "ComputedRef";
175
+ if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "reactive") return "Object";
176
+ if (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") return inferFunctionSignature(node);
177
+ if (node.type === "CallExpression" && node.callee.type === "Identifier" && /^use[A-Z]/.test(node.callee.name)) return "unknown";
178
+ return inferLiteralType(node);
179
+ }
180
+ function inferLiteralType(node) {
181
+ switch (node.type) {
182
+ case "NumericLiteral": return "number";
183
+ case "StringLiteral": return "string";
184
+ case "BooleanLiteral": return "boolean";
185
+ case "NullLiteral": return "null";
186
+ case "TemplateLiteral": return "string";
187
+ case "ArrayExpression": return "Array";
188
+ case "ObjectExpression": return "Object";
189
+ default: return "unknown";
190
+ }
191
+ }
192
+ function inferFunctionSignature(node) {
193
+ return `(${extractParams(node.params ?? [])}) => ${extractReturnType(node)}`;
194
+ }
195
+ function extractParams(params) {
196
+ return params.map((param) => {
197
+ if (param.type === "Identifier") {
198
+ const annotation = param.typeAnnotation?.typeAnnotation;
199
+ if (annotation) return `${param.name}: ${resolveTypeAnnotation(annotation)}`;
200
+ return param.name;
201
+ }
202
+ if (param.type === "AssignmentPattern") {
203
+ const left = param.left;
204
+ if (left.type === "Identifier") {
205
+ const annotation = left.typeAnnotation?.typeAnnotation;
206
+ if (annotation) return `${left.name}: ${resolveTypeAnnotation(annotation)}`;
207
+ return left.name;
208
+ }
209
+ return "arg";
210
+ }
211
+ if (param.type === "RestElement") {
212
+ const arg = param.argument;
213
+ if (arg.type === "Identifier") {
214
+ const annotation = arg.typeAnnotation?.typeAnnotation;
215
+ if (annotation) return `...${arg.name}: ${resolveTypeAnnotation(annotation)}`;
216
+ return `...${arg.name}`;
217
+ }
218
+ return "...args";
219
+ }
220
+ if (param.type === "ObjectPattern") return "options";
221
+ if (param.type === "ArrayPattern") return "args";
222
+ return "arg";
223
+ }).join(", ");
224
+ }
225
+ function extractReturnType(node) {
226
+ const annotation = node.returnType?.typeAnnotation ?? node.typeAnnotation?.typeAnnotation;
227
+ let baseType;
228
+ if (annotation) baseType = resolveTypeAnnotation(annotation);
229
+ else baseType = "void";
230
+ if (node.async && baseType !== "void") return `Promise<${baseType}>`;
231
+ if (node.async) return "Promise<void>";
232
+ return baseType;
233
+ }
234
+ function resolveTypeAnnotation(node) {
235
+ if (!node) return "unknown";
236
+ switch (node.type) {
237
+ case "TSStringKeyword": return "string";
238
+ case "TSNumberKeyword": return "number";
239
+ case "TSBooleanKeyword": return "boolean";
240
+ case "TSVoidKeyword": return "void";
241
+ case "TSAnyKeyword": return "any";
242
+ case "TSNullKeyword": return "null";
243
+ case "TSUndefinedKeyword": return "undefined";
244
+ case "TSObjectKeyword": return "object";
245
+ case "TSNeverKeyword": return "never";
246
+ case "TSUnknownKeyword": return "unknown";
247
+ case "TSTypeReference": {
248
+ const name = node.typeName?.type === "Identifier" ? node.typeName.name : node.typeName?.type === "TSQualifiedName" ? `${node.typeName.left?.name ?? ""}.${node.typeName.right?.name ?? ""}` : "unknown";
249
+ if (node.typeParameters?.params?.length > 0) return `${name}<${node.typeParameters.params.map((p) => resolveTypeAnnotation(p)).join(", ")}>`;
250
+ return name;
251
+ }
252
+ case "TSUnionType": return node.types.map((t) => resolveTypeAnnotation(t)).join(" | ");
253
+ case "TSIntersectionType": return node.types.map((t) => resolveTypeAnnotation(t)).join(" & ");
254
+ case "TSArrayType": return `${resolveTypeAnnotation(node.elementType)}[]`;
255
+ case "TSLiteralType":
256
+ if (node.literal.type === "StringLiteral") return `'${node.literal.value}'`;
257
+ if (node.literal.type === "NumericLiteral") return String(node.literal.value);
258
+ if (node.literal.type === "BooleanLiteral") return String(node.literal.value);
259
+ return "unknown";
260
+ case "TSFunctionType": return "Function";
261
+ case "TSTupleType": return `[${(node.elementTypes ?? []).map((t) => resolveTypeAnnotation(t)).join(", ")}]`;
262
+ case "TSParenthesizedType": return resolveTypeAnnotation(node.typeAnnotation);
263
+ case "TSTypeLiteral": return "object";
264
+ default: return "unknown";
265
+ }
266
+ }
267
+ //#endregion
5
268
  //#region src/parser.ts
6
269
  function parseJSDocTags(comments) {
7
270
  const result = { description: "" };
@@ -22,7 +285,7 @@ function parseJSDocTags(comments) {
22
285
  result.description = descLines.join(" ");
23
286
  return result;
24
287
  }
25
- function parseSFC(source, filename) {
288
+ function parseSFC(source, filename, sfcDir) {
26
289
  const doc = {
27
290
  name: filename.replace(/\.vue$/, "").split("/").pop() ?? "Unknown",
28
291
  props: [],
@@ -50,7 +313,7 @@ function parseSFC(source, filename) {
50
313
  else if (callee === "defineSlots" && typeParams?.params[0]?.type === "TSTypeLiteral") doc.slots = extractTypeSlots(typeParams.params[0]);
51
314
  else if (callee === "defineExpose" && args[0]?.type === "ObjectExpression") doc.exposes = extractExposes(args[0], scriptSource);
52
315
  }
53
- doc.composables = extractComposables(setupAst);
316
+ doc.composables = extractComposables(setupAst, buildImportMap(setupAst), sfcDir);
54
317
  }
55
318
  const scriptAst = compiled.scriptAst;
56
319
  if (scriptAst && doc.props.length === 0 && doc.emits.length === 0) {
@@ -331,26 +594,94 @@ function extractExposes(obj, _source) {
331
594
  }
332
595
  return exposes;
333
596
  }
334
- function extractComposables(ast) {
597
+ function buildImportMap(ast) {
598
+ const map = /* @__PURE__ */ new Map();
599
+ for (const stmt of ast) if (stmt.type === "ImportDeclaration") {
600
+ for (const spec of stmt.specifiers ?? []) if (spec.type === "ImportSpecifier" || spec.type === "ImportDefaultSpecifier") map.set(spec.local.name, stmt.source.value);
601
+ }
602
+ return map;
603
+ }
604
+ function extractVariablesFromPattern(decl) {
605
+ const id = decl.id;
606
+ if (!id) return [];
607
+ if (id.type === "Identifier") {
608
+ const v = { name: id.name };
609
+ if (id.typeAnnotation?.typeAnnotation) v.type = resolveTypeString(id.typeAnnotation.typeAnnotation);
610
+ return [v];
611
+ }
612
+ if (id.type === "ObjectPattern") {
613
+ const vars = [];
614
+ const typeAnnotation = id.typeAnnotation?.typeAnnotation;
615
+ const typeMembers = typeAnnotation?.type === "TSTypeLiteral" ? typeAnnotation.members : null;
616
+ for (const prop of id.properties) if (prop.type === "RestElement") {
617
+ const name = prop.argument?.name ?? "rest";
618
+ vars.push({ name });
619
+ } else if (prop.type === "ObjectProperty") {
620
+ const name = prop.value?.type === "Identifier" ? prop.value.name : prop.value?.type === "AssignmentPattern" && prop.value.left?.type === "Identifier" ? prop.value.left.name : prop.key?.type === "Identifier" ? prop.key.name : "";
621
+ if (!name) continue;
622
+ const v = { name };
623
+ if (typeMembers) {
624
+ const keyName = prop.key?.type === "Identifier" ? prop.key.name : "";
625
+ for (const member of typeMembers) if (member.type === "TSPropertySignature" && member.key?.type === "Identifier" && member.key.name === keyName && member.typeAnnotation?.typeAnnotation) {
626
+ v.type = resolveTypeString(member.typeAnnotation.typeAnnotation);
627
+ break;
628
+ }
629
+ }
630
+ vars.push(v);
631
+ }
632
+ return vars;
633
+ }
634
+ if (id.type === "ArrayPattern") {
635
+ const vars = [];
636
+ for (const el of id.elements) {
637
+ if (!el) continue;
638
+ if (el.type === "Identifier") vars.push({ name: el.name });
639
+ else if (el.type === "RestElement" && el.argument?.type === "Identifier") vars.push({ name: el.argument.name });
640
+ else if (el.type === "AssignmentPattern" && el.left?.type === "Identifier") vars.push({ name: el.left.name });
641
+ }
642
+ return vars;
643
+ }
644
+ return [];
645
+ }
646
+ function extractComposables(ast, importMap, sfcDir) {
335
647
  const seen = /* @__PURE__ */ new Set();
336
648
  const composables = [];
337
649
  for (const stmt of ast) {
338
- const callNames = extractComposableCallNames(stmt);
339
- for (const name of callNames) if (!seen.has(name)) {
340
- seen.add(name);
341
- composables.push({ name });
650
+ if (stmt.type === "ExpressionStatement" && stmt.expression.type === "CallExpression" && stmt.expression.callee.type === "Identifier" && /^use[A-Z]/.test(stmt.expression.callee.name)) {
651
+ const name = stmt.expression.callee.name;
652
+ if (!seen.has(name)) {
653
+ seen.add(name);
654
+ composables.push({
655
+ name,
656
+ source: importMap.get(name),
657
+ variables: []
658
+ });
659
+ }
660
+ }
661
+ if (stmt.type === "VariableDeclaration") {
662
+ for (const decl of stmt.declarations) if (decl.init?.type === "CallExpression" && decl.init.callee.type === "Identifier" && /^use[A-Z]/.test(decl.init.callee.name)) {
663
+ const name = decl.init.callee.name;
664
+ if (seen.has(name)) continue;
665
+ seen.add(name);
666
+ const variables = extractVariablesFromPattern(decl);
667
+ const source = importMap.get(name);
668
+ if (variables.some((v) => !v.type) && sfcDir && source) {
669
+ const resolvedPath = resolveImportPath(source, sfcDir);
670
+ if (resolvedPath) {
671
+ const typeMap = resolveComposableTypes(resolvedPath, name, variables.filter((v) => !v.type).map((v) => v.name));
672
+ for (const v of variables) if (!v.type && typeMap.has(v.name)) v.type = typeMap.get(v.name);
673
+ }
674
+ }
675
+ composables.push({
676
+ name,
677
+ source,
678
+ variables
679
+ });
680
+ }
342
681
  }
343
682
  }
344
683
  return composables;
345
684
  }
346
- function extractComposableCallNames(stmt) {
347
- const names = [];
348
- if (stmt.type === "ExpressionStatement" && stmt.expression.type === "CallExpression" && stmt.expression.callee.type === "Identifier" && /^use[A-Z]/.test(stmt.expression.callee.name)) names.push(stmt.expression.callee.name);
349
- if (stmt.type === "VariableDeclaration") {
350
- for (const decl of stmt.declarations) if (decl.init?.type === "CallExpression" && decl.init.callee.type === "Identifier" && /^use[A-Z]/.test(decl.init.callee.name)) names.push(decl.init.callee.name);
351
- }
352
- return names;
353
- }
354
685
  function extractTemplateSlots(templateAst) {
355
686
  const slots = [];
356
687
  walkTemplate(templateAst.children ?? [], slots);
@@ -448,6 +779,9 @@ function stringifyDefault(node, source) {
448
779
  function esc(value) {
449
780
  return value.replaceAll("|", "\\|");
450
781
  }
782
+ function escHtml(value) {
783
+ return value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
784
+ }
451
785
  function generateMarkdown(doc) {
452
786
  const sections = [`# ${doc.name}`];
453
787
  if (doc.description) sections.push("", doc.description);
@@ -518,8 +852,24 @@ function generateMarkdown(doc) {
518
852
  }
519
853
  }
520
854
  if (hasComposables) {
521
- sections.push("", "## Composables Used", "");
522
- for (const c of doc.composables) sections.push(`- \`${c.name}\``);
855
+ sections.push("", "## Composables Used");
856
+ for (const c of doc.composables) {
857
+ sections.push("", `### \`${c.name}\``);
858
+ if (c.source && (c.source.startsWith(".") || c.source.startsWith("@/"))) sections.push("", `*Source: \`${c.source}\`*`);
859
+ if (c.variables.length === 0) sections.push("", "Called for side effects.");
860
+ else if (!c.variables.some((v) => v.type) && c.variables.length <= 3) {
861
+ const vars = c.variables.map((v) => `\`${v.name}\``).join(", ");
862
+ sections.push("", `**Returns:** ${vars}`);
863
+ } else {
864
+ sections.push("");
865
+ sections.push("| Variable | Type |");
866
+ sections.push("| --- | --- |");
867
+ for (const v of c.variables) {
868
+ const type = v.type ? escHtml(esc(v.type)) : "-";
869
+ sections.push(`| ${esc(v.name)} | ${type} |`);
870
+ }
871
+ }
872
+ }
523
873
  }
524
874
  return sections.join("\n") + "\n";
525
875
  }
@@ -527,7 +877,7 @@ function generateMarkdown(doc) {
527
877
  //#region src/index.ts
528
878
  function parseComponent(filePath) {
529
879
  const abs = resolve(filePath);
530
- return parseSFC(readFileSync(abs, "utf-8"), abs.split("/").pop() ?? "Unknown.vue");
880
+ return parseSFC(readFileSync(abs, "utf-8"), abs.split("/").pop() ?? "Unknown.vue", abs.substring(0, abs.lastIndexOf("/")));
531
881
  }
532
882
  //#endregion
533
883
  //#region src/cli.ts
package/dist/index.d.mts CHANGED
@@ -25,8 +25,14 @@ interface ExposeDoc {
25
25
  type: string;
26
26
  description: string;
27
27
  }
28
+ interface ComposableVariable {
29
+ name: string;
30
+ type?: string;
31
+ }
28
32
  interface ComposableDoc {
29
33
  name: string;
34
+ source?: string;
35
+ variables: ComposableVariable[];
30
36
  }
31
37
  interface ComponentDoc {
32
38
  name: string;
@@ -40,7 +46,7 @@ interface ComponentDoc {
40
46
  }
41
47
  //#endregion
42
48
  //#region src/parser.d.ts
43
- declare function parseSFC(source: string, filename: string): ComponentDoc;
49
+ declare function parseSFC(source: string, filename: string, sfcDir?: string): ComponentDoc;
44
50
  //#endregion
45
51
  //#region src/markdown.d.ts
46
52
  declare function generateMarkdown(doc: ComponentDoc): string;
@@ -48,4 +54,4 @@ declare function generateMarkdown(doc: ComponentDoc): string;
48
54
  //#region src/index.d.ts
49
55
  declare function parseComponent(filePath: string): ComponentDoc;
50
56
  //#endregion
51
- export { type ComponentDoc, type ComposableDoc, type EmitDoc, type ExposeDoc, type PropDoc, type SlotDoc, generateMarkdown, parseComponent, parseSFC };
57
+ export { type ComponentDoc, type ComposableDoc, type ComposableVariable, type EmitDoc, type ExposeDoc, type PropDoc, type SlotDoc, generateMarkdown, parseComponent, parseSFC };
package/dist/index.mjs CHANGED
@@ -1,6 +1,269 @@
1
- import { readFileSync } from "node:fs";
2
- import { resolve } from "node:path";
3
- import { compileScript, parse } from "@vue/compiler-sfc";
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { babelParse, compileScript, parse } from "@vue/compiler-sfc";
4
+ //#region src/resolver.ts
5
+ function resolveImportPath(importSource, sfcDir) {
6
+ try {
7
+ if (!importSource.startsWith(".") && !importSource.startsWith("@/") && !importSource.startsWith("~/")) return null;
8
+ if (importSource.startsWith("./") || importSource.startsWith("../")) return tryResolveFile(resolve(sfcDir, importSource));
9
+ const tsconfig = findTsConfig(sfcDir);
10
+ if (!tsconfig) return null;
11
+ const { paths, baseUrl } = readTsConfigPaths(tsconfig);
12
+ if (!paths) return null;
13
+ const configDir = dirname(tsconfig);
14
+ const resolvedBaseUrl = baseUrl ? resolve(configDir, baseUrl) : configDir;
15
+ for (const [pattern, targets] of Object.entries(paths)) {
16
+ const prefix = pattern.replace(/\*$/, "");
17
+ if (!importSource.startsWith(prefix)) continue;
18
+ const remainder = importSource.slice(prefix.length);
19
+ for (const target of targets) {
20
+ const result = tryResolveFile(resolve(resolvedBaseUrl, target.replace(/\*$/, "") + remainder));
21
+ if (result) return result;
22
+ }
23
+ }
24
+ return null;
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+ function tryResolveFile(basePath) {
30
+ if (existsSync(basePath) && !isDirectory(basePath)) return basePath;
31
+ for (const ext of [".ts", ".js"]) {
32
+ const candidate = basePath + ext;
33
+ if (existsSync(candidate)) return candidate;
34
+ }
35
+ for (const ext of ["/index.ts", "/index.js"]) {
36
+ const candidate = basePath + ext;
37
+ if (existsSync(candidate)) return candidate;
38
+ }
39
+ return null;
40
+ }
41
+ function isDirectory(filePath) {
42
+ try {
43
+ return statSync(filePath).isDirectory();
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+ function findTsConfig(startDir) {
49
+ let dir = resolve(startDir);
50
+ const root = resolve("/");
51
+ while (dir !== root) {
52
+ const tsconfig = join(dir, "tsconfig.json");
53
+ if (existsSync(tsconfig)) return tsconfig;
54
+ const jsconfig = join(dir, "jsconfig.json");
55
+ if (existsSync(jsconfig)) return jsconfig;
56
+ const parent = dirname(dir);
57
+ if (parent === dir) break;
58
+ dir = parent;
59
+ }
60
+ return null;
61
+ }
62
+ function readTsConfigPaths(configPath) {
63
+ try {
64
+ const content = JSON.parse(readFileSync(configPath, "utf-8"));
65
+ let paths = content.compilerOptions?.paths ?? null;
66
+ let baseUrl = content.compilerOptions?.baseUrl;
67
+ if (content.extends) {
68
+ const parentPath = resolve(dirname(configPath), content.extends);
69
+ const parentConfigFile = parentPath.endsWith(".json") ? parentPath : parentPath + ".json";
70
+ if (existsSync(parentConfigFile)) try {
71
+ const parentContent = JSON.parse(readFileSync(parentConfigFile, "utf-8"));
72
+ const parentPaths = parentContent.compilerOptions?.paths;
73
+ const parentBaseUrl = parentContent.compilerOptions?.baseUrl;
74
+ if (!paths && parentPaths) paths = parentPaths;
75
+ if (!baseUrl && parentBaseUrl) baseUrl = parentBaseUrl;
76
+ } catch {}
77
+ }
78
+ return {
79
+ paths,
80
+ baseUrl
81
+ };
82
+ } catch {
83
+ return {
84
+ paths: null,
85
+ baseUrl: void 0
86
+ };
87
+ }
88
+ }
89
+ function resolveComposableTypes(filePath, exportName, variableNames) {
90
+ try {
91
+ const funcNode = findExportedFunction(babelParse(readFileSync(filePath, "utf-8"), {
92
+ plugins: ["typescript", "jsx"],
93
+ sourceType: "module"
94
+ }).program.body, exportName);
95
+ if (!funcNode) return /* @__PURE__ */ new Map();
96
+ const body = getFunctionBody(funcNode);
97
+ if (!body) return /* @__PURE__ */ new Map();
98
+ const returnProps = findReturnProperties(body);
99
+ if (!returnProps) return /* @__PURE__ */ new Map();
100
+ const result = /* @__PURE__ */ new Map();
101
+ const nameSet = new Set(variableNames);
102
+ for (const prop of returnProps) {
103
+ let propName = null;
104
+ if (prop.type === "ObjectProperty") propName = prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "StringLiteral" ? prop.key.value : null;
105
+ else if (prop.type === "ObjectMethod") {
106
+ propName = prop.key.type === "Identifier" ? prop.key.name : null;
107
+ if (propName && nameSet.has(propName)) result.set(propName, inferFunctionSignature(prop));
108
+ continue;
109
+ } else if (prop.type === "SpreadElement") continue;
110
+ if (!propName || !nameSet.has(propName)) continue;
111
+ if (prop.type === "ObjectProperty" && prop.shorthand) {
112
+ const type = traceVariableType(propName, body);
113
+ result.set(propName, type);
114
+ } else if (prop.type === "ObjectProperty") {
115
+ const type = inferType(prop.value);
116
+ result.set(propName, type);
117
+ }
118
+ }
119
+ return result;
120
+ } catch {
121
+ return /* @__PURE__ */ new Map();
122
+ }
123
+ }
124
+ function findExportedFunction(stmts, exportName) {
125
+ for (const stmt of stmts) {
126
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "FunctionDeclaration" && stmt.declaration.id?.name === exportName) return stmt.declaration;
127
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration?.type === "VariableDeclaration") {
128
+ for (const decl of stmt.declaration.declarations) if (decl.id.type === "Identifier" && decl.id.name === exportName && decl.init && (decl.init.type === "ArrowFunctionExpression" || decl.init.type === "FunctionExpression")) return decl.init;
129
+ }
130
+ if (stmt.type === "ExportDefaultDeclaration" && stmt.declaration.type === "FunctionDeclaration" && stmt.declaration.id?.name === exportName) return stmt.declaration;
131
+ if (stmt.type === "ExportDefaultDeclaration" && stmt.declaration.type === "FunctionDeclaration" && !stmt.declaration.id) return stmt.declaration;
132
+ if (stmt.type === "ExportDefaultDeclaration" && (stmt.declaration.type === "ArrowFunctionExpression" || stmt.declaration.type === "FunctionExpression")) return stmt.declaration;
133
+ if (stmt.type === "FunctionDeclaration" && stmt.id?.name === exportName) {
134
+ if (stmts.some((s) => s.type === "ExportNamedDeclaration" && !s.declaration && s.specifiers.some((spec) => spec.type === "ExportSpecifier" && (spec.local.type === "Identifier" && spec.local.name === exportName || spec.exported.type === "Identifier" && spec.exported.name === exportName)))) return stmt;
135
+ }
136
+ if (stmt.type === "VariableDeclaration") {
137
+ for (const decl of stmt.declarations) if (decl.id.type === "Identifier" && decl.id.name === exportName && decl.init && (decl.init.type === "ArrowFunctionExpression" || decl.init.type === "FunctionExpression")) {
138
+ if (stmts.some((s) => s.type === "ExportNamedDeclaration" && !s.declaration && s.specifiers.some((spec) => spec.type === "ExportSpecifier" && (spec.local.type === "Identifier" && spec.local.name === exportName || spec.exported.type === "Identifier" && spec.exported.name === exportName)))) return decl.init;
139
+ }
140
+ }
141
+ }
142
+ return null;
143
+ }
144
+ function getFunctionBody(node) {
145
+ if (node.body.type === "BlockStatement") return node.body.body;
146
+ return null;
147
+ }
148
+ function findReturnProperties(body) {
149
+ for (let i = body.length - 1; i >= 0; i--) {
150
+ const stmt = body[i];
151
+ if (stmt.type === "ReturnStatement" && stmt.argument?.type === "ObjectExpression") return stmt.argument.properties;
152
+ }
153
+ return null;
154
+ }
155
+ function traceVariableType(name, body) {
156
+ for (let i = body.length - 1; i >= 0; i--) {
157
+ const stmt = body[i];
158
+ if (stmt.type === "FunctionDeclaration" && stmt.id?.name === name) return inferFunctionSignature(stmt);
159
+ if (stmt.type === "VariableDeclaration") {
160
+ for (const decl of stmt.declarations) if (decl.id.type === "Identifier" && decl.id.name === name && decl.init) return inferType(decl.init);
161
+ }
162
+ }
163
+ return "unknown";
164
+ }
165
+ function inferType(node) {
166
+ if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "ref") {
167
+ const typeParams = node.typeParameters;
168
+ if (typeParams?.params?.length > 0) return `Ref<${resolveTypeAnnotation(typeParams.params[0])}>`;
169
+ const arg = node.arguments[0];
170
+ if (!arg) return "Ref<unknown>";
171
+ return `Ref<${inferLiteralType(arg)}>`;
172
+ }
173
+ if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "computed") return "ComputedRef";
174
+ if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "reactive") return "Object";
175
+ if (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") return inferFunctionSignature(node);
176
+ if (node.type === "CallExpression" && node.callee.type === "Identifier" && /^use[A-Z]/.test(node.callee.name)) return "unknown";
177
+ return inferLiteralType(node);
178
+ }
179
+ function inferLiteralType(node) {
180
+ switch (node.type) {
181
+ case "NumericLiteral": return "number";
182
+ case "StringLiteral": return "string";
183
+ case "BooleanLiteral": return "boolean";
184
+ case "NullLiteral": return "null";
185
+ case "TemplateLiteral": return "string";
186
+ case "ArrayExpression": return "Array";
187
+ case "ObjectExpression": return "Object";
188
+ default: return "unknown";
189
+ }
190
+ }
191
+ function inferFunctionSignature(node) {
192
+ return `(${extractParams(node.params ?? [])}) => ${extractReturnType(node)}`;
193
+ }
194
+ function extractParams(params) {
195
+ return params.map((param) => {
196
+ if (param.type === "Identifier") {
197
+ const annotation = param.typeAnnotation?.typeAnnotation;
198
+ if (annotation) return `${param.name}: ${resolveTypeAnnotation(annotation)}`;
199
+ return param.name;
200
+ }
201
+ if (param.type === "AssignmentPattern") {
202
+ const left = param.left;
203
+ if (left.type === "Identifier") {
204
+ const annotation = left.typeAnnotation?.typeAnnotation;
205
+ if (annotation) return `${left.name}: ${resolveTypeAnnotation(annotation)}`;
206
+ return left.name;
207
+ }
208
+ return "arg";
209
+ }
210
+ if (param.type === "RestElement") {
211
+ const arg = param.argument;
212
+ if (arg.type === "Identifier") {
213
+ const annotation = arg.typeAnnotation?.typeAnnotation;
214
+ if (annotation) return `...${arg.name}: ${resolveTypeAnnotation(annotation)}`;
215
+ return `...${arg.name}`;
216
+ }
217
+ return "...args";
218
+ }
219
+ if (param.type === "ObjectPattern") return "options";
220
+ if (param.type === "ArrayPattern") return "args";
221
+ return "arg";
222
+ }).join(", ");
223
+ }
224
+ function extractReturnType(node) {
225
+ const annotation = node.returnType?.typeAnnotation ?? node.typeAnnotation?.typeAnnotation;
226
+ let baseType;
227
+ if (annotation) baseType = resolveTypeAnnotation(annotation);
228
+ else baseType = "void";
229
+ if (node.async && baseType !== "void") return `Promise<${baseType}>`;
230
+ if (node.async) return "Promise<void>";
231
+ return baseType;
232
+ }
233
+ function resolveTypeAnnotation(node) {
234
+ if (!node) return "unknown";
235
+ switch (node.type) {
236
+ case "TSStringKeyword": return "string";
237
+ case "TSNumberKeyword": return "number";
238
+ case "TSBooleanKeyword": return "boolean";
239
+ case "TSVoidKeyword": return "void";
240
+ case "TSAnyKeyword": return "any";
241
+ case "TSNullKeyword": return "null";
242
+ case "TSUndefinedKeyword": return "undefined";
243
+ case "TSObjectKeyword": return "object";
244
+ case "TSNeverKeyword": return "never";
245
+ case "TSUnknownKeyword": return "unknown";
246
+ case "TSTypeReference": {
247
+ const name = node.typeName?.type === "Identifier" ? node.typeName.name : node.typeName?.type === "TSQualifiedName" ? `${node.typeName.left?.name ?? ""}.${node.typeName.right?.name ?? ""}` : "unknown";
248
+ if (node.typeParameters?.params?.length > 0) return `${name}<${node.typeParameters.params.map((p) => resolveTypeAnnotation(p)).join(", ")}>`;
249
+ return name;
250
+ }
251
+ case "TSUnionType": return node.types.map((t) => resolveTypeAnnotation(t)).join(" | ");
252
+ case "TSIntersectionType": return node.types.map((t) => resolveTypeAnnotation(t)).join(" & ");
253
+ case "TSArrayType": return `${resolveTypeAnnotation(node.elementType)}[]`;
254
+ case "TSLiteralType":
255
+ if (node.literal.type === "StringLiteral") return `'${node.literal.value}'`;
256
+ if (node.literal.type === "NumericLiteral") return String(node.literal.value);
257
+ if (node.literal.type === "BooleanLiteral") return String(node.literal.value);
258
+ return "unknown";
259
+ case "TSFunctionType": return "Function";
260
+ case "TSTupleType": return `[${(node.elementTypes ?? []).map((t) => resolveTypeAnnotation(t)).join(", ")}]`;
261
+ case "TSParenthesizedType": return resolveTypeAnnotation(node.typeAnnotation);
262
+ case "TSTypeLiteral": return "object";
263
+ default: return "unknown";
264
+ }
265
+ }
266
+ //#endregion
4
267
  //#region src/parser.ts
5
268
  function parseJSDocTags(comments) {
6
269
  const result = { description: "" };
@@ -21,7 +284,7 @@ function parseJSDocTags(comments) {
21
284
  result.description = descLines.join(" ");
22
285
  return result;
23
286
  }
24
- function parseSFC(source, filename) {
287
+ function parseSFC(source, filename, sfcDir) {
25
288
  const doc = {
26
289
  name: filename.replace(/\.vue$/, "").split("/").pop() ?? "Unknown",
27
290
  props: [],
@@ -49,7 +312,7 @@ function parseSFC(source, filename) {
49
312
  else if (callee === "defineSlots" && typeParams?.params[0]?.type === "TSTypeLiteral") doc.slots = extractTypeSlots(typeParams.params[0]);
50
313
  else if (callee === "defineExpose" && args[0]?.type === "ObjectExpression") doc.exposes = extractExposes(args[0], scriptSource);
51
314
  }
52
- doc.composables = extractComposables(setupAst);
315
+ doc.composables = extractComposables(setupAst, buildImportMap(setupAst), sfcDir);
53
316
  }
54
317
  const scriptAst = compiled.scriptAst;
55
318
  if (scriptAst && doc.props.length === 0 && doc.emits.length === 0) {
@@ -330,26 +593,94 @@ function extractExposes(obj, _source) {
330
593
  }
331
594
  return exposes;
332
595
  }
333
- function extractComposables(ast) {
596
+ function buildImportMap(ast) {
597
+ const map = /* @__PURE__ */ new Map();
598
+ for (const stmt of ast) if (stmt.type === "ImportDeclaration") {
599
+ for (const spec of stmt.specifiers ?? []) if (spec.type === "ImportSpecifier" || spec.type === "ImportDefaultSpecifier") map.set(spec.local.name, stmt.source.value);
600
+ }
601
+ return map;
602
+ }
603
+ function extractVariablesFromPattern(decl) {
604
+ const id = decl.id;
605
+ if (!id) return [];
606
+ if (id.type === "Identifier") {
607
+ const v = { name: id.name };
608
+ if (id.typeAnnotation?.typeAnnotation) v.type = resolveTypeString(id.typeAnnotation.typeAnnotation);
609
+ return [v];
610
+ }
611
+ if (id.type === "ObjectPattern") {
612
+ const vars = [];
613
+ const typeAnnotation = id.typeAnnotation?.typeAnnotation;
614
+ const typeMembers = typeAnnotation?.type === "TSTypeLiteral" ? typeAnnotation.members : null;
615
+ for (const prop of id.properties) if (prop.type === "RestElement") {
616
+ const name = prop.argument?.name ?? "rest";
617
+ vars.push({ name });
618
+ } else if (prop.type === "ObjectProperty") {
619
+ const name = prop.value?.type === "Identifier" ? prop.value.name : prop.value?.type === "AssignmentPattern" && prop.value.left?.type === "Identifier" ? prop.value.left.name : prop.key?.type === "Identifier" ? prop.key.name : "";
620
+ if (!name) continue;
621
+ const v = { name };
622
+ if (typeMembers) {
623
+ const keyName = prop.key?.type === "Identifier" ? prop.key.name : "";
624
+ for (const member of typeMembers) if (member.type === "TSPropertySignature" && member.key?.type === "Identifier" && member.key.name === keyName && member.typeAnnotation?.typeAnnotation) {
625
+ v.type = resolveTypeString(member.typeAnnotation.typeAnnotation);
626
+ break;
627
+ }
628
+ }
629
+ vars.push(v);
630
+ }
631
+ return vars;
632
+ }
633
+ if (id.type === "ArrayPattern") {
634
+ const vars = [];
635
+ for (const el of id.elements) {
636
+ if (!el) continue;
637
+ if (el.type === "Identifier") vars.push({ name: el.name });
638
+ else if (el.type === "RestElement" && el.argument?.type === "Identifier") vars.push({ name: el.argument.name });
639
+ else if (el.type === "AssignmentPattern" && el.left?.type === "Identifier") vars.push({ name: el.left.name });
640
+ }
641
+ return vars;
642
+ }
643
+ return [];
644
+ }
645
+ function extractComposables(ast, importMap, sfcDir) {
334
646
  const seen = /* @__PURE__ */ new Set();
335
647
  const composables = [];
336
648
  for (const stmt of ast) {
337
- const callNames = extractComposableCallNames(stmt);
338
- for (const name of callNames) if (!seen.has(name)) {
339
- seen.add(name);
340
- composables.push({ name });
649
+ if (stmt.type === "ExpressionStatement" && stmt.expression.type === "CallExpression" && stmt.expression.callee.type === "Identifier" && /^use[A-Z]/.test(stmt.expression.callee.name)) {
650
+ const name = stmt.expression.callee.name;
651
+ if (!seen.has(name)) {
652
+ seen.add(name);
653
+ composables.push({
654
+ name,
655
+ source: importMap.get(name),
656
+ variables: []
657
+ });
658
+ }
659
+ }
660
+ if (stmt.type === "VariableDeclaration") {
661
+ for (const decl of stmt.declarations) if (decl.init?.type === "CallExpression" && decl.init.callee.type === "Identifier" && /^use[A-Z]/.test(decl.init.callee.name)) {
662
+ const name = decl.init.callee.name;
663
+ if (seen.has(name)) continue;
664
+ seen.add(name);
665
+ const variables = extractVariablesFromPattern(decl);
666
+ const source = importMap.get(name);
667
+ if (variables.some((v) => !v.type) && sfcDir && source) {
668
+ const resolvedPath = resolveImportPath(source, sfcDir);
669
+ if (resolvedPath) {
670
+ const typeMap = resolveComposableTypes(resolvedPath, name, variables.filter((v) => !v.type).map((v) => v.name));
671
+ for (const v of variables) if (!v.type && typeMap.has(v.name)) v.type = typeMap.get(v.name);
672
+ }
673
+ }
674
+ composables.push({
675
+ name,
676
+ source,
677
+ variables
678
+ });
679
+ }
341
680
  }
342
681
  }
343
682
  return composables;
344
683
  }
345
- function extractComposableCallNames(stmt) {
346
- const names = [];
347
- if (stmt.type === "ExpressionStatement" && stmt.expression.type === "CallExpression" && stmt.expression.callee.type === "Identifier" && /^use[A-Z]/.test(stmt.expression.callee.name)) names.push(stmt.expression.callee.name);
348
- if (stmt.type === "VariableDeclaration") {
349
- for (const decl of stmt.declarations) if (decl.init?.type === "CallExpression" && decl.init.callee.type === "Identifier" && /^use[A-Z]/.test(decl.init.callee.name)) names.push(decl.init.callee.name);
350
- }
351
- return names;
352
- }
353
684
  function extractTemplateSlots(templateAst) {
354
685
  const slots = [];
355
686
  walkTemplate(templateAst.children ?? [], slots);
@@ -447,6 +778,9 @@ function stringifyDefault(node, source) {
447
778
  function esc(value) {
448
779
  return value.replaceAll("|", "\\|");
449
780
  }
781
+ function escHtml(value) {
782
+ return value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
783
+ }
450
784
  function generateMarkdown(doc) {
451
785
  const sections = [`# ${doc.name}`];
452
786
  if (doc.description) sections.push("", doc.description);
@@ -517,8 +851,24 @@ function generateMarkdown(doc) {
517
851
  }
518
852
  }
519
853
  if (hasComposables) {
520
- sections.push("", "## Composables Used", "");
521
- for (const c of doc.composables) sections.push(`- \`${c.name}\``);
854
+ sections.push("", "## Composables Used");
855
+ for (const c of doc.composables) {
856
+ sections.push("", `### \`${c.name}\``);
857
+ if (c.source && (c.source.startsWith(".") || c.source.startsWith("@/"))) sections.push("", `*Source: \`${c.source}\`*`);
858
+ if (c.variables.length === 0) sections.push("", "Called for side effects.");
859
+ else if (!c.variables.some((v) => v.type) && c.variables.length <= 3) {
860
+ const vars = c.variables.map((v) => `\`${v.name}\``).join(", ");
861
+ sections.push("", `**Returns:** ${vars}`);
862
+ } else {
863
+ sections.push("");
864
+ sections.push("| Variable | Type |");
865
+ sections.push("| --- | --- |");
866
+ for (const v of c.variables) {
867
+ const type = v.type ? escHtml(esc(v.type)) : "-";
868
+ sections.push(`| ${esc(v.name)} | ${type} |`);
869
+ }
870
+ }
871
+ }
522
872
  }
523
873
  return sections.join("\n") + "\n";
524
874
  }
@@ -526,7 +876,7 @@ function generateMarkdown(doc) {
526
876
  //#region src/index.ts
527
877
  function parseComponent(filePath) {
528
878
  const abs = resolve(filePath);
529
- return parseSFC(readFileSync(abs, "utf-8"), abs.split("/").pop() ?? "Unknown.vue");
879
+ return parseSFC(readFileSync(abs, "utf-8"), abs.split("/").pop() ?? "Unknown.vue", abs.substring(0, abs.lastIndexOf("/")));
530
880
  }
531
881
  //#endregion
532
882
  export { generateMarkdown, parseComponent, parseSFC };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compmark-vue",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Auto-generate Markdown documentation from Vue 3 SFCs",
5
5
  "license": "MIT",
6
6
  "repository": "noopurphalak/compmark-vue",