compmark-vue 0.2.3 → 0.2.5

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/README.md CHANGED
@@ -17,25 +17,64 @@ npx compmark-vue ./src/components/Button.vue
17
17
 
18
18
  This parses the component and creates `Button.md` in your current directory.
19
19
 
20
- ### Example Output
20
+ ## Features
21
21
 
22
- Given a component like:
22
+ - [Props](#props) runtime and TypeScript generic syntax
23
+ - [Emits](#emits) — array, TypeScript property, and call signature syntax
24
+ - [Slots](#slots) — `defineSlots` with typed bindings, template `<slot>` fallback
25
+ - [Expose](#expose) — `defineExpose` with JSDoc descriptions
26
+ - [Composables](#composables) — auto-detects `useX()` calls in `<script setup>`
27
+ - [JSDoc tags](#jsdoc-tags) — `@deprecated`, `@since`, `@example`, `@see`, `@default`
28
+ - [`@internal`](#internal-components) — exclude components from output
29
+ - [Options API](#options-api) — `export default { props, emits }` support
30
+ - Empty sections are skipped cleanly — no placeholder noise
31
+
32
+ ## Examples
33
+
34
+ ### Props
35
+
36
+ Runtime syntax, TypeScript generics, and `withDefaults` are all supported:
23
37
 
24
38
  ```vue
25
- <script setup>
26
- /**
27
- * @emit submit Emitted on form submit
28
- * @emit cancel Emitted on cancel
29
- */
30
- const emit = defineEmits(["submit", "cancel"]);
39
+ <script setup lang="ts">
40
+ const props = withDefaults(
41
+ defineProps<{
42
+ /** The label text */
43
+ label: string;
44
+ /** Visual theme */
45
+ theme?: "filled" | "outline";
46
+ disabled?: boolean;
47
+ }>(),
48
+ {
49
+ theme: "filled",
50
+ disabled: false,
51
+ },
52
+ );
53
+ </script>
54
+ ```
31
55
 
32
- const props = defineProps({
56
+ Output:
57
+
58
+ ```md
59
+ ## Props
60
+
61
+ | Name | Type | Required | Default | Description |
62
+ | -------- | --------------------- | -------- | ---------- | -------------- |
63
+ | label | string | Yes | - | The label text |
64
+ | theme | 'filled' \| 'outline' | No | `"filled"` | Visual theme |
65
+ | disabled | boolean | No | `false` | - |
66
+ ```
67
+
68
+ Runtime object syntax is also supported:
69
+
70
+ ```vue
71
+ <script setup>
72
+ defineProps({
33
73
  /** Title of the dialog */
34
74
  title: {
35
75
  type: String,
36
76
  required: true,
37
77
  },
38
- /** Whether the dialog is visible */
39
78
  visible: {
40
79
  type: Boolean,
41
80
  default: false,
@@ -44,24 +83,224 @@ const props = defineProps({
44
83
  </script>
45
84
  ```
46
85
 
47
- compmark-vue generates:
86
+ ### Emits
87
+
88
+ TypeScript generic syntax with payloads:
89
+
90
+ ```vue
91
+ <script setup lang="ts">
92
+ const emit = defineEmits<{
93
+ /** Emitted on save */
94
+ save: [data: Record<string, unknown>];
95
+ /** Emitted on cancel */
96
+ cancel: [];
97
+ }>();
98
+ </script>
99
+ ```
100
+
101
+ Output:
102
+
103
+ ```md
104
+ ## Emits
105
+
106
+ | Name | Payload | Description |
107
+ | ------ | ----------------------------- | ----------------- |
108
+ | save | data: Record<string, unknown> | Emitted on save |
109
+ | cancel | - | Emitted on cancel |
110
+ ```
111
+
112
+ Call signature syntax is also supported:
113
+
114
+ ```vue
115
+ <script setup lang="ts">
116
+ defineEmits<{
117
+ (e: "click", payload: MouseEvent): void;
118
+ (e: "submit"): void;
119
+ }>();
120
+ </script>
121
+ ```
122
+
123
+ Array syntax works too: `defineEmits(["click", "submit"])`.
124
+
125
+ ### Slots
126
+
127
+ `defineSlots` provides typed bindings:
128
+
129
+ ```vue
130
+ <script setup lang="ts">
131
+ defineSlots<{
132
+ /** Main content */
133
+ default(props: { msg: string }): any;
134
+ /** Header area */
135
+ header(props: { title: string; count: number }): any;
136
+ }>();
137
+ </script>
138
+ ```
139
+
140
+ Output:
48
141
 
49
142
  ```md
50
- # Dialog
143
+ ## Slots
144
+
145
+ | Name | Bindings | Description |
146
+ | ------- | ---------------------------- | ------------ |
147
+ | default | msg: string | Main content |
148
+ | header | title: string, count: number | Header area |
149
+ ```
150
+
151
+ If `defineSlots` is not used, slots are extracted from template `<slot>` elements as a fallback:
152
+
153
+ ```vue
154
+ <template>
155
+ <div>
156
+ <slot />
157
+ <slot name="header" :title="title" />
158
+ <slot name="footer" />
159
+ </div>
160
+ </template>
161
+ ```
162
+
163
+ ### Expose
164
+
165
+ ```vue
166
+ <script setup lang="ts">
167
+ defineExpose({
168
+ /** Focus the component */
169
+ focus,
170
+ /** Reset the component state */
171
+ reset,
172
+ });
173
+ </script>
174
+ ```
51
175
 
176
+ Output:
177
+
178
+ ```md
179
+ ## Exposed
180
+
181
+ | Name | Type | Description |
182
+ | ----- | ------- | ------------------------- |
183
+ | focus | unknown | Focus the component |
184
+ | reset | unknown | Reset the component state |
185
+ ```
186
+
187
+ ### Composables
188
+
189
+ Any `useX()` calls in `<script setup>` are automatically detected:
190
+
191
+ ```vue
192
+ <script setup lang="ts">
193
+ import { useRouter } from "vue-router";
194
+ import { useMouse } from "@vueuse/core";
195
+
196
+ const router = useRouter();
197
+ const { x, y } = useMouse();
198
+ </script>
199
+ ```
200
+
201
+ Output:
202
+
203
+ ```md
204
+ ## Composables Used
205
+
206
+ - `useRouter`
207
+ - `useMouse`
208
+ ```
209
+
210
+ ### JSDoc Tags
211
+
212
+ Props support `@deprecated`, `@since`, `@example`, and `@see`:
213
+
214
+ ```vue
215
+ <script setup lang="ts">
216
+ defineProps<{
217
+ /**
218
+ * The label text
219
+ * @deprecated Use `text` instead
220
+ * @since 1.0.0
221
+ * @example "Hello World"
222
+ * @see https://example.com/docs
223
+ */
224
+ label: string;
225
+ }>();
226
+ </script>
227
+ ```
228
+
229
+ Output:
230
+
231
+ ````md
52
232
  ## Props
53
233
 
54
- | Name | Type | Required | Default | Description |
55
- | ------- | ------- | -------- | ------- | ----------------------------- |
56
- | title | String | Yes | - | Title of the dialog |
57
- | visible | Boolean | No | `false` | Whether the dialog is visible |
234
+ | Name | Type | Required | Default | Description |
235
+ | ----- | ------ | -------- | ------- | ----------------------------------------------------------------------------------------------- |
236
+ | label | string | Yes | - | The label text **Deprecated**: Use `text` instead _(since 1.0.0)_ See: https://example.com/docs |
237
+
238
+ **`label` example:**
239
+
240
+ ```
241
+ "Hello World"
242
+ ```
243
+ ````
244
+
245
+ ### Internal Components
246
+
247
+ Mark a component with `@internal` to skip it during generation:
248
+
249
+ ```vue
250
+ <script setup lang="ts">
251
+ /**
252
+ * @internal
253
+ */
254
+ defineProps<{
255
+ value: string;
256
+ }>();
257
+ </script>
258
+ ```
259
+
260
+ ```sh
261
+ $ compmark InternalHelper.vue
262
+ Skipped InternalHelper.vue (marked @internal)
263
+ ```
264
+
265
+ ### Options API
266
+
267
+ Components using `export default {}` are supported:
268
+
269
+ ```vue
270
+ <script>
271
+ export default {
272
+ props: {
273
+ /** The title text */
274
+ title: {
275
+ type: String,
276
+ required: true,
277
+ },
278
+ count: {
279
+ type: Number,
280
+ default: 10,
281
+ },
282
+ },
283
+ emits: ["click", "update"],
284
+ };
285
+ </script>
286
+ ```
287
+
288
+ Output:
289
+
290
+ ```md
291
+ ## Props
292
+
293
+ | Name | Type | Required | Default | Description |
294
+ | ----- | ------ | -------- | ------- | -------------- |
295
+ | title | String | Yes | - | The title text |
296
+ | count | Number | No | `10` | - |
58
297
 
59
298
  ## Emits
60
299
 
61
- | Name | Description |
62
- | ------ | ---------------------- |
63
- | submit | Emitted on form submit |
64
- | cancel | Emitted on cancel |
300
+ | Name | Description |
301
+ | ------ | ----------- |
302
+ | click | - |
303
+ | update | - |
65
304
  ```
66
305
 
67
306
  ## Programmatic API
@@ -86,14 +325,6 @@ const doc = parseSFC(source, "Button.vue");
86
325
  const md = generateMarkdown(doc);
87
326
  ```
88
327
 
89
- ## Supported Syntax
90
-
91
- - `defineProps({ ... })` — shorthand (`String`), array type (`[String, Number]`), and full object syntax
92
- - `defineEmits([...])` — array syntax
93
- - JSDoc comments on props and emits (`/** ... */`)
94
- - `const props = defineProps(...)` variable assignment pattern
95
- - Default value extraction (string, number, boolean literals, arrow functions)
96
-
97
328
  ## Development
98
329
 
99
330
  <details>
package/dist/cli.mjs CHANGED
@@ -445,6 +445,9 @@ function stringifyDefault(node, source) {
445
445
  }
446
446
  //#endregion
447
447
  //#region src/markdown.ts
448
+ function esc(value) {
449
+ return value.replaceAll("|", "\\|");
450
+ }
448
451
  function generateMarkdown(doc) {
449
452
  const sections = [`# ${doc.name}`];
450
453
  if (doc.description) sections.push("", doc.description);
@@ -469,7 +472,7 @@ function generateMarkdown(doc) {
469
472
  if (p.since) desc += ` *(since ${p.since})*`;
470
473
  if (p.see) desc += ` See: ${p.see}`;
471
474
  const req = p.required ? "Yes" : "No";
472
- sections.push(`| ${p.name} | ${p.type} | ${req} | ${def} | ${desc} |`);
475
+ sections.push(`| ${esc(p.name)} | ${esc(p.type)} | ${req} | ${esc(def)} | ${esc(desc)} |`);
473
476
  if (p.example) examples.push({
474
477
  name: p.name,
475
478
  example: p.example
@@ -484,7 +487,7 @@ function generateMarkdown(doc) {
484
487
  for (const e of doc.emits) {
485
488
  const desc = e.description || "-";
486
489
  const payload = e.payload || "-";
487
- sections.push(`| ${e.name} | ${payload} | ${desc} |`);
490
+ sections.push(`| ${esc(e.name)} | ${esc(payload)} | ${esc(desc)} |`);
488
491
  }
489
492
  } else {
490
493
  sections.push("", "## Emits", "");
@@ -492,7 +495,7 @@ function generateMarkdown(doc) {
492
495
  sections.push("| --- | --- |");
493
496
  for (const e of doc.emits) {
494
497
  const desc = e.description || "-";
495
- sections.push(`| ${e.name} | ${desc} |`);
498
+ sections.push(`| ${esc(e.name)} | ${esc(desc)} |`);
496
499
  }
497
500
  }
498
501
  if (hasSlots) {
@@ -502,7 +505,7 @@ function generateMarkdown(doc) {
502
505
  for (const s of doc.slots) {
503
506
  const desc = s.description || "-";
504
507
  const bindings = s.bindings.length > 0 ? s.bindings.join(", ") : "-";
505
- sections.push(`| ${s.name} | ${bindings} | ${desc} |`);
508
+ sections.push(`| ${esc(s.name)} | ${esc(bindings)} | ${esc(desc)} |`);
506
509
  }
507
510
  }
508
511
  if (hasExposes) {
@@ -511,7 +514,7 @@ function generateMarkdown(doc) {
511
514
  sections.push("| --- | --- | --- |");
512
515
  for (const e of doc.exposes) {
513
516
  const desc = e.description || "-";
514
- sections.push(`| ${e.name} | ${e.type} | ${desc} |`);
517
+ sections.push(`| ${esc(e.name)} | ${esc(e.type)} | ${esc(desc)} |`);
515
518
  }
516
519
  }
517
520
  if (hasComposables) {
package/dist/index.mjs CHANGED
@@ -444,6 +444,9 @@ function stringifyDefault(node, source) {
444
444
  }
445
445
  //#endregion
446
446
  //#region src/markdown.ts
447
+ function esc(value) {
448
+ return value.replaceAll("|", "\\|");
449
+ }
447
450
  function generateMarkdown(doc) {
448
451
  const sections = [`# ${doc.name}`];
449
452
  if (doc.description) sections.push("", doc.description);
@@ -468,7 +471,7 @@ function generateMarkdown(doc) {
468
471
  if (p.since) desc += ` *(since ${p.since})*`;
469
472
  if (p.see) desc += ` See: ${p.see}`;
470
473
  const req = p.required ? "Yes" : "No";
471
- sections.push(`| ${p.name} | ${p.type} | ${req} | ${def} | ${desc} |`);
474
+ sections.push(`| ${esc(p.name)} | ${esc(p.type)} | ${req} | ${esc(def)} | ${esc(desc)} |`);
472
475
  if (p.example) examples.push({
473
476
  name: p.name,
474
477
  example: p.example
@@ -483,7 +486,7 @@ function generateMarkdown(doc) {
483
486
  for (const e of doc.emits) {
484
487
  const desc = e.description || "-";
485
488
  const payload = e.payload || "-";
486
- sections.push(`| ${e.name} | ${payload} | ${desc} |`);
489
+ sections.push(`| ${esc(e.name)} | ${esc(payload)} | ${esc(desc)} |`);
487
490
  }
488
491
  } else {
489
492
  sections.push("", "## Emits", "");
@@ -491,7 +494,7 @@ function generateMarkdown(doc) {
491
494
  sections.push("| --- | --- |");
492
495
  for (const e of doc.emits) {
493
496
  const desc = e.description || "-";
494
- sections.push(`| ${e.name} | ${desc} |`);
497
+ sections.push(`| ${esc(e.name)} | ${esc(desc)} |`);
495
498
  }
496
499
  }
497
500
  if (hasSlots) {
@@ -501,7 +504,7 @@ function generateMarkdown(doc) {
501
504
  for (const s of doc.slots) {
502
505
  const desc = s.description || "-";
503
506
  const bindings = s.bindings.length > 0 ? s.bindings.join(", ") : "-";
504
- sections.push(`| ${s.name} | ${bindings} | ${desc} |`);
507
+ sections.push(`| ${esc(s.name)} | ${esc(bindings)} | ${esc(desc)} |`);
505
508
  }
506
509
  }
507
510
  if (hasExposes) {
@@ -510,7 +513,7 @@ function generateMarkdown(doc) {
510
513
  sections.push("| --- | --- | --- |");
511
514
  for (const e of doc.exposes) {
512
515
  const desc = e.description || "-";
513
- sections.push(`| ${e.name} | ${e.type} | ${desc} |`);
516
+ sections.push(`| ${esc(e.name)} | ${esc(e.type)} | ${esc(desc)} |`);
514
517
  }
515
518
  }
516
519
  if (hasComposables) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compmark-vue",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Auto-generate Markdown documentation from Vue 3 SFCs",
5
5
  "license": "MIT",
6
6
  "repository": "noopurphalak/compmark-vue",