sveld 0.25.0 → 0.25.2
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/lib/ComponentParser.d.ts +4 -0
- package/lib/ComponentParser.js +264 -124
- package/lib/resolve-alias.js +11 -6
- package/lib/rollup-plugin.js +36 -27
- package/lib/writer/WriterMarkdown.d.ts +2 -1
- package/lib/writer/WriterMarkdown.js +13 -12
- package/lib/writer/writer-markdown-core.d.ts +2 -1
- package/lib/writer/writer-markdown-core.js +13 -12
- package/package.json +1 -1
package/lib/ComponentParser.d.ts
CHANGED
|
@@ -112,10 +112,12 @@ export default class ComponentParser {
|
|
|
112
112
|
private readonly bindings;
|
|
113
113
|
private readonly contexts;
|
|
114
114
|
private variableInfoCache;
|
|
115
|
+
private sourceLinesCache?;
|
|
115
116
|
constructor(options?: ComponentParserOptions);
|
|
116
117
|
private static mapToArray;
|
|
117
118
|
private static assignValue;
|
|
118
119
|
private static formatComment;
|
|
120
|
+
private getCommentTags;
|
|
119
121
|
/**
|
|
120
122
|
* Finds the last comment from an array of leading comments.
|
|
121
123
|
* TypeScript directives are stripped before parsing, so we can safely take the last comment.
|
|
@@ -137,6 +139,8 @@ export default class ComponentParser {
|
|
|
137
139
|
private buildEventDetailFromProperties;
|
|
138
140
|
private generateContextTypeName;
|
|
139
141
|
private buildVariableInfoCache;
|
|
142
|
+
private static readonly VAR_NAME_REGEX_CACHE;
|
|
143
|
+
private static getVarNameRegexes;
|
|
140
144
|
private findVariableTypeAndDescription;
|
|
141
145
|
private parseContextValue;
|
|
142
146
|
private parseSetContextCall;
|
package/lib/ComponentParser.js
CHANGED
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
5
5
|
const comment_parser_1 = require("comment-parser");
|
|
6
6
|
const compiler_1 = require("svelte/compiler");
|
|
7
7
|
const element_tag_map_1 = require("./element-tag-map");
|
|
8
|
+
const COMMENT_BLOCK_DESCRIPTION_REGEX = /^-\s*/;
|
|
8
9
|
const VAR_DECLARATION_REGEX = /(?:const|let|function)\s+(\w+)\s*[=(]/;
|
|
9
10
|
const DEFAULT_SLOT_NAME = null;
|
|
10
11
|
const TYPEDEF_END_REGEX = /(\}|\};)$/;
|
|
@@ -34,6 +35,7 @@ class ComponentParser {
|
|
|
34
35
|
bindings = new Map();
|
|
35
36
|
contexts = new Map();
|
|
36
37
|
variableInfoCache = new Map();
|
|
38
|
+
sourceLinesCache;
|
|
37
39
|
constructor(options) {
|
|
38
40
|
this.options = options;
|
|
39
41
|
}
|
|
@@ -53,6 +55,45 @@ class ComponentParser {
|
|
|
53
55
|
}
|
|
54
56
|
return formatted_comment;
|
|
55
57
|
}
|
|
58
|
+
getCommentTags(parsed) {
|
|
59
|
+
const tags = parsed[0]?.tags ?? [];
|
|
60
|
+
const excludedTags = new Set([
|
|
61
|
+
"type",
|
|
62
|
+
"param",
|
|
63
|
+
"returns",
|
|
64
|
+
"return",
|
|
65
|
+
"extends",
|
|
66
|
+
"restProps",
|
|
67
|
+
"slot",
|
|
68
|
+
"event",
|
|
69
|
+
"typedef",
|
|
70
|
+
]);
|
|
71
|
+
let typeTag;
|
|
72
|
+
const paramTags = [];
|
|
73
|
+
let returnsTag;
|
|
74
|
+
const additionalTags = [];
|
|
75
|
+
for (const tag of tags) {
|
|
76
|
+
if (tag.tag === "type") {
|
|
77
|
+
typeTag = tag;
|
|
78
|
+
}
|
|
79
|
+
else if (tag.tag === "param") {
|
|
80
|
+
paramTags.push(tag);
|
|
81
|
+
}
|
|
82
|
+
else if (tag.tag === "returns" || tag.tag === "return") {
|
|
83
|
+
returnsTag = tag;
|
|
84
|
+
}
|
|
85
|
+
else if (!excludedTags.has(tag.tag)) {
|
|
86
|
+
additionalTags.push(tag);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
type: typeTag,
|
|
91
|
+
param: paramTags,
|
|
92
|
+
returns: returnsTag,
|
|
93
|
+
additional: additionalTags,
|
|
94
|
+
description: parsed[0]?.description,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
56
97
|
/**
|
|
57
98
|
* Finds the last comment from an array of leading comments.
|
|
58
99
|
* TypeScript directives are stripped before parsing, so we can safely take the last comment.
|
|
@@ -137,7 +178,9 @@ class ComponentParser {
|
|
|
137
178
|
const name = default_slot ? DEFAULT_SLOT_NAME : (slot_name ?? "");
|
|
138
179
|
const fallback = ComponentParser.assignValue(slot_fallback);
|
|
139
180
|
const props = ComponentParser.assignValue(slot_props);
|
|
140
|
-
const description = slot_description
|
|
181
|
+
const description = slot_description
|
|
182
|
+
? slot_description.substring(slot_description.lastIndexOf("-") + 1).trim()
|
|
183
|
+
: undefined;
|
|
141
184
|
if (this.slots.has(name)) {
|
|
142
185
|
const existing_slot = this.slots.get(name);
|
|
143
186
|
this.slots.set(name, {
|
|
@@ -166,7 +209,7 @@ class ComponentParser {
|
|
|
166
209
|
* `@event` is not specified.
|
|
167
210
|
*/
|
|
168
211
|
const default_detail = !has_argument && !detail ? "null" : ComponentParser.assignValue(detail);
|
|
169
|
-
const event_description = description
|
|
212
|
+
const event_description = description ? description.substring(description.lastIndexOf("-") + 1).trim() : undefined;
|
|
170
213
|
if (this.events.has(name)) {
|
|
171
214
|
const existing_event = this.events.get(name);
|
|
172
215
|
this.events.set(name, {
|
|
@@ -185,7 +228,9 @@ class ComponentParser {
|
|
|
185
228
|
}
|
|
186
229
|
}
|
|
187
230
|
parseCustomTypes() {
|
|
188
|
-
for (const { tags, description: commentDescription } of (0, comment_parser_1.parse)(this.source, {
|
|
231
|
+
for (const { tags, description: commentDescription, source: blockSource } of (0, comment_parser_1.parse)(this.source, {
|
|
232
|
+
spacing: "preserve",
|
|
233
|
+
})) {
|
|
189
234
|
let currentEventName;
|
|
190
235
|
let currentEventType;
|
|
191
236
|
let currentEventDescription;
|
|
@@ -193,8 +238,65 @@ class ComponentParser {
|
|
|
193
238
|
let currentTypedefName;
|
|
194
239
|
let currentTypedefType;
|
|
195
240
|
let currentTypedefDescription;
|
|
196
|
-
let commentDescriptionUsed = false;
|
|
197
241
|
const typedefProperties = [];
|
|
242
|
+
// Track if we've used the comment block description for any tag in this block
|
|
243
|
+
// Only the first tag (that needs a description) should use the comment block description
|
|
244
|
+
let commentDescriptionUsed = false;
|
|
245
|
+
let isFirstTag = true;
|
|
246
|
+
// Build a map of line numbers to their description content (for lines without tags)
|
|
247
|
+
const lineDescriptions = new Map();
|
|
248
|
+
// Track line numbers that contain tags
|
|
249
|
+
const tagLineNumbers = new Set();
|
|
250
|
+
for (const tagInfo of tags) {
|
|
251
|
+
if (tagInfo.source && tagInfo.source.length > 0) {
|
|
252
|
+
tagLineNumbers.add(tagInfo.source[0].number);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
for (const line of blockSource) {
|
|
256
|
+
// Only track lines that have a description but no tag
|
|
257
|
+
// Also filter out lines that are just "}" (artifact from some comment formats)
|
|
258
|
+
if (!line.tokens.tag && line.tokens.description && line.tokens.description.trim() !== "}") {
|
|
259
|
+
lineDescriptions.set(line.number, line.tokens.description);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Helper to get the description from lines preceding a tag
|
|
263
|
+
// Look backwards from the tag until we hit another tag, collecting description lines
|
|
264
|
+
// Stop after finding the first contiguous block of description lines
|
|
265
|
+
const getPrecedingDescription = (tagSource) => {
|
|
266
|
+
if (!tagSource || tagSource.length === 0)
|
|
267
|
+
return undefined;
|
|
268
|
+
const tagLineNumber = tagSource[0].number;
|
|
269
|
+
// Look backwards from the tag line to find the immediately preceding description
|
|
270
|
+
const descLines = [];
|
|
271
|
+
let foundDescriptionBlock = false;
|
|
272
|
+
for (let lineNum = tagLineNumber - 1; lineNum >= 1; lineNum--) {
|
|
273
|
+
// Stop if we hit a tag line
|
|
274
|
+
if (tagLineNumbers.has(lineNum)) {
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
// Check if this line has a description
|
|
278
|
+
const desc = lineDescriptions.get(lineNum);
|
|
279
|
+
if (desc) {
|
|
280
|
+
descLines.unshift(desc); // Add to beginning to maintain order
|
|
281
|
+
foundDescriptionBlock = true;
|
|
282
|
+
}
|
|
283
|
+
else if (foundDescriptionBlock) {
|
|
284
|
+
// We've already found description lines and now hit a non-description line
|
|
285
|
+
// Check if it's blank - if so, continue; if not, stop
|
|
286
|
+
const sourceLine = blockSource.find((l) => l.number === lineNum);
|
|
287
|
+
const isBlank = !sourceLine ||
|
|
288
|
+
(!sourceLine.tokens.tag &&
|
|
289
|
+
(!sourceLine.tokens.description || sourceLine.tokens.description.trim() === ""));
|
|
290
|
+
if (!isBlank) {
|
|
291
|
+
// Non-blank non-description line - stop here
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
// Blank line - continue (blank lines can separate descriptions from tags)
|
|
295
|
+
}
|
|
296
|
+
// If we haven't found any description yet, continue looking backwards
|
|
297
|
+
}
|
|
298
|
+
return descLines.length > 0 ? descLines.join("\n").trim() : undefined;
|
|
299
|
+
};
|
|
198
300
|
const finalizeEvent = () => {
|
|
199
301
|
if (currentEventName !== undefined) {
|
|
200
302
|
let detailType;
|
|
@@ -250,37 +352,63 @@ class ComponentParser {
|
|
|
250
352
|
currentTypedefDescription = undefined;
|
|
251
353
|
}
|
|
252
354
|
};
|
|
253
|
-
for (const { tag, type: tagType, name, description, optional, default: defaultValue } of tags) {
|
|
355
|
+
for (const { tag, type: tagType, name, description, optional, default: defaultValue, source: tagSource, } of tags) {
|
|
254
356
|
const type = this.aliasType(tagType);
|
|
357
|
+
// Get the description from the line immediately before this tag
|
|
358
|
+
const precedingDescription = getPrecedingDescription(tagSource);
|
|
255
359
|
switch (tag) {
|
|
256
360
|
case "extends":
|
|
257
361
|
this.extends = {
|
|
258
362
|
interface: name,
|
|
259
363
|
import: type,
|
|
260
364
|
};
|
|
365
|
+
if (isFirstTag)
|
|
366
|
+
isFirstTag = false;
|
|
261
367
|
break;
|
|
262
368
|
case "restProps":
|
|
263
369
|
this.rest_props = {
|
|
264
370
|
type: "Element",
|
|
265
371
|
name: type,
|
|
266
372
|
};
|
|
373
|
+
if (isFirstTag)
|
|
374
|
+
isFirstTag = false;
|
|
267
375
|
break;
|
|
268
|
-
case "slot":
|
|
376
|
+
case "slot": {
|
|
377
|
+
// Prefer inline description, fall back to preceding line description,
|
|
378
|
+
// then fall back to the comment block description (only for first tag if not already used)
|
|
379
|
+
const inlineSlotDesc = description?.replace(COMMENT_BLOCK_DESCRIPTION_REGEX, "").trim();
|
|
380
|
+
let slotDesc = inlineSlotDesc || precedingDescription;
|
|
381
|
+
if (!slotDesc && isFirstTag && !commentDescriptionUsed && commentDescription) {
|
|
382
|
+
slotDesc = commentDescription;
|
|
383
|
+
commentDescriptionUsed = true;
|
|
384
|
+
}
|
|
385
|
+
if (isFirstTag)
|
|
386
|
+
isFirstTag = false;
|
|
269
387
|
this.addSlot({
|
|
270
388
|
slot_name: name,
|
|
271
389
|
slot_props: type,
|
|
272
|
-
slot_description:
|
|
390
|
+
slot_description: slotDesc || undefined,
|
|
273
391
|
});
|
|
274
392
|
break;
|
|
275
|
-
|
|
393
|
+
}
|
|
394
|
+
case "event": {
|
|
276
395
|
// Finalize any previous event being built
|
|
277
396
|
finalizeEvent();
|
|
278
397
|
// Start tracking new event
|
|
279
398
|
currentEventName = name;
|
|
280
399
|
currentEventType = type;
|
|
281
|
-
//
|
|
282
|
-
|
|
400
|
+
// Prefer inline description (e.g., "@event {type} name - description"),
|
|
401
|
+
// fall back to preceding line, then fall back to comment block description (only for first tag if not already used)
|
|
402
|
+
const inlineEventDesc = description?.replace(COMMENT_BLOCK_DESCRIPTION_REGEX, "").trim();
|
|
403
|
+
currentEventDescription = inlineEventDesc || precedingDescription;
|
|
404
|
+
if (!currentEventDescription && isFirstTag && !commentDescriptionUsed && commentDescription) {
|
|
405
|
+
currentEventDescription = commentDescription;
|
|
406
|
+
commentDescriptionUsed = true;
|
|
407
|
+
}
|
|
408
|
+
if (isFirstTag)
|
|
409
|
+
isFirstTag = false;
|
|
283
410
|
break;
|
|
411
|
+
}
|
|
284
412
|
case "type":
|
|
285
413
|
// Track the @type tag for the current event
|
|
286
414
|
if (currentEventName !== undefined) {
|
|
@@ -310,22 +438,22 @@ class ComponentParser {
|
|
|
310
438
|
// Start tracking new typedef
|
|
311
439
|
currentTypedefName = name;
|
|
312
440
|
currentTypedefType = type;
|
|
313
|
-
//
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
currentTypedefDescription = trimmedCommentDesc;
|
|
441
|
+
// Prefer inline description, fall back to preceding line description,
|
|
442
|
+
// then fall back to comment block description (only for first tag if not already used)
|
|
443
|
+
const inlineTypedefDesc = description?.replace(COMMENT_BLOCK_DESCRIPTION_REGEX, "").trim();
|
|
444
|
+
currentTypedefDescription = inlineTypedefDesc || precedingDescription;
|
|
445
|
+
if (!currentTypedefDescription && isFirstTag && !commentDescriptionUsed && commentDescription) {
|
|
446
|
+
currentTypedefDescription = commentDescription;
|
|
320
447
|
commentDescriptionUsed = true;
|
|
321
448
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
}
|
|
449
|
+
if (isFirstTag)
|
|
450
|
+
isFirstTag = false;
|
|
325
451
|
break;
|
|
326
452
|
}
|
|
327
453
|
case "generics":
|
|
328
454
|
this.generics = [name, type];
|
|
455
|
+
if (isFirstTag)
|
|
456
|
+
isFirstTag = false;
|
|
329
457
|
break;
|
|
330
458
|
}
|
|
331
459
|
}
|
|
@@ -367,7 +495,10 @@ class ComponentParser {
|
|
|
367
495
|
buildVariableInfoCache() {
|
|
368
496
|
if (!this.source)
|
|
369
497
|
return;
|
|
370
|
-
|
|
498
|
+
if (!this.sourceLinesCache) {
|
|
499
|
+
this.sourceLinesCache = this.source.split("\n");
|
|
500
|
+
}
|
|
501
|
+
const lines = this.sourceLinesCache;
|
|
371
502
|
for (let i = 0; i < lines.length; i++) {
|
|
372
503
|
const line = lines[i].trim();
|
|
373
504
|
// Match variable declarations
|
|
@@ -391,14 +522,12 @@ class ComponentParser {
|
|
|
391
522
|
const commentBlock = commentLines.join("\n");
|
|
392
523
|
// Parse the JSDoc
|
|
393
524
|
const parsed = (0, comment_parser_1.parse)(commentBlock, { spacing: "preserve" });
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
this.
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
});
|
|
401
|
-
}
|
|
525
|
+
const { type: typeTag, description } = this.getCommentTags(parsed);
|
|
526
|
+
if (typeTag) {
|
|
527
|
+
this.variableInfoCache.set(varName, {
|
|
528
|
+
type: this.aliasType(typeTag.type),
|
|
529
|
+
description: description || typeTag.description,
|
|
530
|
+
});
|
|
402
531
|
}
|
|
403
532
|
break;
|
|
404
533
|
}
|
|
@@ -406,6 +535,20 @@ class ComponentParser {
|
|
|
406
535
|
}
|
|
407
536
|
}
|
|
408
537
|
}
|
|
538
|
+
static VAR_NAME_REGEX_CACHE = new Map();
|
|
539
|
+
static getVarNameRegexes(varName) {
|
|
540
|
+
let cached = ComponentParser.VAR_NAME_REGEX_CACHE.get(varName);
|
|
541
|
+
if (!cached) {
|
|
542
|
+
const escaped = varName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
543
|
+
cached = [
|
|
544
|
+
new RegExp(`const\\s+${escaped}\\s*=`),
|
|
545
|
+
new RegExp(`let\\s+${escaped}\\s*=`),
|
|
546
|
+
new RegExp(`function\\s+${escaped}\\s*\\(`),
|
|
547
|
+
];
|
|
548
|
+
ComponentParser.VAR_NAME_REGEX_CACHE.set(varName, cached);
|
|
549
|
+
}
|
|
550
|
+
return cached;
|
|
551
|
+
}
|
|
409
552
|
findVariableTypeAndDescription(varName) {
|
|
410
553
|
const cached = this.variableInfoCache.get(varName);
|
|
411
554
|
if (cached) {
|
|
@@ -414,15 +557,18 @@ class ComponentParser {
|
|
|
414
557
|
// Search through the source code directly for JSDoc comments
|
|
415
558
|
if (!this.source)
|
|
416
559
|
return null;
|
|
417
|
-
|
|
418
|
-
|
|
560
|
+
if (!this.sourceLinesCache) {
|
|
561
|
+
this.sourceLinesCache = this.source.split("\n");
|
|
562
|
+
}
|
|
563
|
+
const lines = this.sourceLinesCache;
|
|
564
|
+
const [constRegex, letRegex, funcRegex] = ComponentParser.getVarNameRegexes(varName);
|
|
419
565
|
for (let i = 0; i < lines.length; i++) {
|
|
420
566
|
const line = lines[i].trim();
|
|
421
567
|
// Check if this line declares our variable
|
|
422
568
|
// Match patterns like: const varName = ..., let varName = ..., function varName
|
|
423
|
-
const constMatch = line.match(
|
|
424
|
-
const letMatch = line.match(
|
|
425
|
-
const funcMatch = line.match(
|
|
569
|
+
const constMatch = line.match(constRegex);
|
|
570
|
+
const letMatch = line.match(letRegex);
|
|
571
|
+
const funcMatch = line.match(funcRegex);
|
|
426
572
|
if (constMatch || letMatch || funcMatch) {
|
|
427
573
|
// Look backwards for JSDoc comment
|
|
428
574
|
for (let j = i - 1; j >= 0; j--) {
|
|
@@ -441,14 +587,12 @@ class ComponentParser {
|
|
|
441
587
|
const commentBlock = commentLines.join("\n");
|
|
442
588
|
// Parse the JSDoc
|
|
443
589
|
const parsed = (0, comment_parser_1.parse)(commentBlock, { spacing: "preserve" });
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
};
|
|
451
|
-
}
|
|
590
|
+
const { type: typeTag, description } = this.getCommentTags(parsed);
|
|
591
|
+
if (typeTag) {
|
|
592
|
+
return {
|
|
593
|
+
type: this.aliasType(typeTag.type),
|
|
594
|
+
description: description || typeTag.description,
|
|
595
|
+
};
|
|
452
596
|
}
|
|
453
597
|
break;
|
|
454
598
|
}
|
|
@@ -599,6 +743,7 @@ class ComponentParser {
|
|
|
599
743
|
this.bindings.clear();
|
|
600
744
|
this.contexts.clear();
|
|
601
745
|
this.variableInfoCache.clear();
|
|
746
|
+
this.sourceLinesCache = undefined;
|
|
602
747
|
}
|
|
603
748
|
// Pre-compiled regexes for better performance
|
|
604
749
|
static SCRIPT_BLOCK_REGEX = /(<script[^>]*>)([\s\S]*?)(<\/script>)/gi;
|
|
@@ -632,6 +777,7 @@ class ComponentParser {
|
|
|
632
777
|
// The compile result includes the parsed AST
|
|
633
778
|
this.parsed = compiled.ast || (0, compiler_1.parse)(cleanedSource);
|
|
634
779
|
this.collectReactiveVars();
|
|
780
|
+
this.sourceLinesCache = this.source.split("\n");
|
|
635
781
|
this.buildVariableInfoCache();
|
|
636
782
|
this.parseCustomTypes();
|
|
637
783
|
if (this.parsed?.module) {
|
|
@@ -712,12 +858,11 @@ class ComponentParser {
|
|
|
712
858
|
const comment = (0, comment_parser_1.parse)(ComponentParser.formatComment(jsdoc_comment.value), {
|
|
713
859
|
spacing: "preserve",
|
|
714
860
|
});
|
|
861
|
+
const { type: typeTag, param: paramTags, returns: returnsTag, additional: additionalTags, description: commentDescription, } = this.getCommentTags(comment);
|
|
715
862
|
// Extract @type tag
|
|
716
|
-
const typeTag = comment[0]?.tags.find((t) => t.tag === "type");
|
|
717
863
|
if (typeTag)
|
|
718
864
|
type = this.aliasType(typeTag.type);
|
|
719
865
|
// Extract @param tags
|
|
720
|
-
const paramTags = comment[0]?.tags.filter((t) => t.tag === "param") ?? [];
|
|
721
866
|
if (paramTags.length > 0) {
|
|
722
867
|
params = paramTags
|
|
723
868
|
.filter((tag) => !tag.name.includes(".")) // Exclude nested params like "options.expand"
|
|
@@ -729,27 +874,20 @@ class ComponentParser {
|
|
|
729
874
|
}));
|
|
730
875
|
}
|
|
731
876
|
// Extract @returns/@return tag
|
|
732
|
-
const returnsTag = comment[0]?.tags.find((t) => t.tag === "returns" || t.tag === "return");
|
|
733
877
|
if (returnsTag)
|
|
734
878
|
returnType = this.aliasType(returnsTag.type);
|
|
735
879
|
// Build description from comment description and non-param/non-type tags
|
|
736
|
-
const
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
"extends",
|
|
743
|
-
"restProps",
|
|
744
|
-
"slot",
|
|
745
|
-
"event",
|
|
746
|
-
"typedef",
|
|
747
|
-
].includes(tag.tag)) ?? [];
|
|
748
|
-
if (commentDescription || additionalTags.length > 0) {
|
|
749
|
-
description = commentDescription || "";
|
|
880
|
+
const formattedDescription = ComponentParser.assignValue(commentDescription?.trim());
|
|
881
|
+
if (formattedDescription || additionalTags.length > 0) {
|
|
882
|
+
const descriptionParts = [];
|
|
883
|
+
if (formattedDescription) {
|
|
884
|
+
descriptionParts.push(formattedDescription);
|
|
885
|
+
}
|
|
750
886
|
for (const tag of additionalTags) {
|
|
751
|
-
|
|
887
|
+
const tagStr = `@${tag.tag}${tag.name ? ` ${tag.name}` : ""}${tag.description ? ` ${tag.description}` : ""}`;
|
|
888
|
+
descriptionParts.push(tagStr);
|
|
752
889
|
}
|
|
890
|
+
description = descriptionParts.join("\n");
|
|
753
891
|
}
|
|
754
892
|
}
|
|
755
893
|
}
|
|
@@ -908,12 +1046,11 @@ class ComponentParser {
|
|
|
908
1046
|
const comment = (0, comment_parser_1.parse)(ComponentParser.formatComment(jsdoc_comment.value), {
|
|
909
1047
|
spacing: "preserve",
|
|
910
1048
|
});
|
|
1049
|
+
const { type: typeTag, param: paramTags, returns: returnsTag, additional: additional_tags, description: commentDescription, } = this.getCommentTags(comment);
|
|
911
1050
|
// Extract @type tag
|
|
912
|
-
const typeTag = comment[0]?.tags.find((t) => t.tag === "type");
|
|
913
1051
|
if (typeTag)
|
|
914
1052
|
type = this.aliasType(typeTag.type);
|
|
915
1053
|
// Extract @param tags
|
|
916
|
-
const paramTags = comment[0]?.tags.filter((t) => t.tag === "param") ?? [];
|
|
917
1054
|
if (paramTags.length > 0) {
|
|
918
1055
|
params = paramTags
|
|
919
1056
|
.filter((tag) => !tag.name.includes(".")) // Exclude nested params like "options.expand"
|
|
@@ -925,27 +1062,20 @@ class ComponentParser {
|
|
|
925
1062
|
}));
|
|
926
1063
|
}
|
|
927
1064
|
// Extract @returns/@return tag
|
|
928
|
-
const returnsTag = comment[0]?.tags.find((t) => t.tag === "returns" || t.tag === "return");
|
|
929
1065
|
if (returnsTag)
|
|
930
1066
|
returnType = this.aliasType(returnsTag.type);
|
|
931
1067
|
// Build description from comment description and non-param/non-type tags
|
|
932
|
-
const
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
"extends",
|
|
939
|
-
"restProps",
|
|
940
|
-
"slot",
|
|
941
|
-
"event",
|
|
942
|
-
"typedef",
|
|
943
|
-
].includes(tag.tag)) ?? [];
|
|
944
|
-
if (commentDescription || additional_tags.length > 0) {
|
|
945
|
-
description = commentDescription || "";
|
|
1068
|
+
const formattedDescription = ComponentParser.assignValue(commentDescription?.trim());
|
|
1069
|
+
if (formattedDescription || additional_tags.length > 0) {
|
|
1070
|
+
const descriptionParts = [];
|
|
1071
|
+
if (formattedDescription) {
|
|
1072
|
+
descriptionParts.push(formattedDescription);
|
|
1073
|
+
}
|
|
946
1074
|
for (const tag of additional_tags) {
|
|
947
|
-
|
|
1075
|
+
const tagStr = `@${tag.tag}${tag.name ? ` ${tag.name}` : ""}${tag.description ? ` ${tag.description}` : ""}`;
|
|
1076
|
+
descriptionParts.push(tagStr);
|
|
948
1077
|
}
|
|
1078
|
+
description = descriptionParts.join("\n");
|
|
949
1079
|
}
|
|
950
1080
|
}
|
|
951
1081
|
}
|
|
@@ -1027,7 +1157,9 @@ class ComponentParser {
|
|
|
1027
1157
|
const existing_event = this.events.get(node.name);
|
|
1028
1158
|
// Check if this event has a JSDoc description
|
|
1029
1159
|
const description = this.eventDescriptions.get(node.name);
|
|
1030
|
-
const event_description = description
|
|
1160
|
+
const event_description = description
|
|
1161
|
+
? description.substring(description.lastIndexOf("-") + 1).trim()
|
|
1162
|
+
: undefined;
|
|
1031
1163
|
if (!existing_event) {
|
|
1032
1164
|
// Add new forwarded event
|
|
1033
1165
|
this.events.set(node.name, {
|
|
@@ -1097,7 +1229,9 @@ class ComponentParser {
|
|
|
1097
1229
|
// If event is marked as dispatched but is NOT actually dispatched, convert it to forwarded
|
|
1098
1230
|
if (event && event.type === "dispatched" && !actuallyDispatchedEvents.has(eventName)) {
|
|
1099
1231
|
const description = this.eventDescriptions.get(eventName);
|
|
1100
|
-
const event_description = description
|
|
1232
|
+
const event_description = description
|
|
1233
|
+
? description.substring(description.lastIndexOf("-") + 1).trim()
|
|
1234
|
+
: undefined;
|
|
1101
1235
|
const forwardedEvent = {
|
|
1102
1236
|
type: "forwarded",
|
|
1103
1237
|
name: eventName,
|
|
@@ -1113,56 +1247,62 @@ class ComponentParser {
|
|
|
1113
1247
|
this.events.set(eventName, forwardedEvent);
|
|
1114
1248
|
}
|
|
1115
1249
|
});
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
.
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
if (slot_props[key].replace && slot_props[key].value !== undefined) {
|
|
1139
|
-
slot_props[key].value = this.props.get(slot_props[key].value)?.type;
|
|
1140
|
-
}
|
|
1141
|
-
if (slot_props[key].value === undefined)
|
|
1142
|
-
slot_props[key].value = "any";
|
|
1143
|
-
new_props.push(`${key}: ${slot_props[key].value}`);
|
|
1250
|
+
const processedProps = ComponentParser.mapToArray(this.props).map((prop) => {
|
|
1251
|
+
if (this.bindings.has(prop.name)) {
|
|
1252
|
+
const elementTypes = this.bindings
|
|
1253
|
+
.get(prop.name)
|
|
1254
|
+
?.elements.sort()
|
|
1255
|
+
.map((element) => (0, element_tag_map_1.getElementByTag)(element))
|
|
1256
|
+
.join(" | ");
|
|
1257
|
+
return {
|
|
1258
|
+
...prop,
|
|
1259
|
+
type: `null | ${elementTypes}`,
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
return prop;
|
|
1263
|
+
});
|
|
1264
|
+
const processedSlots = ComponentParser.mapToArray(this.slots)
|
|
1265
|
+
.map((slot) => {
|
|
1266
|
+
try {
|
|
1267
|
+
const slot_props = JSON.parse(slot.slot_props);
|
|
1268
|
+
const new_props = [];
|
|
1269
|
+
for (const key of Object.keys(slot_props)) {
|
|
1270
|
+
if (slot_props[key].replace && slot_props[key].value !== undefined) {
|
|
1271
|
+
slot_props[key].value = this.props.get(slot_props[key].value)?.type;
|
|
1144
1272
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1273
|
+
if (slot_props[key].value === undefined)
|
|
1274
|
+
slot_props[key].value = "any";
|
|
1275
|
+
new_props.push(`${key}: ${slot_props[key].value}`);
|
|
1147
1276
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
return
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1277
|
+
const formatted_slot_props = new_props.length === 0 ? "Record<string, never>" : `{ ${new_props.join(", ")} }`;
|
|
1278
|
+
return { ...slot, slot_props: formatted_slot_props };
|
|
1279
|
+
}
|
|
1280
|
+
catch (_e) {
|
|
1281
|
+
return slot;
|
|
1282
|
+
}
|
|
1283
|
+
})
|
|
1284
|
+
.sort((a, b) => {
|
|
1285
|
+
if (a.name < b.name)
|
|
1286
|
+
return -1;
|
|
1287
|
+
if (a.name > b.name)
|
|
1288
|
+
return 1;
|
|
1289
|
+
return 0;
|
|
1290
|
+
});
|
|
1291
|
+
const moduleExportsArray = ComponentParser.mapToArray(this.moduleExports);
|
|
1292
|
+
const eventsArray = ComponentParser.mapToArray(this.events);
|
|
1293
|
+
const typedefsArray = ComponentParser.mapToArray(this.typedefs);
|
|
1294
|
+
const contextsArray = ComponentParser.mapToArray(this.contexts);
|
|
1295
|
+
return {
|
|
1296
|
+
props: processedProps,
|
|
1297
|
+
moduleExports: moduleExportsArray,
|
|
1298
|
+
slots: processedSlots,
|
|
1299
|
+
events: eventsArray,
|
|
1300
|
+
typedefs: typedefsArray,
|
|
1161
1301
|
generics: this.generics,
|
|
1162
1302
|
rest_props: this.rest_props,
|
|
1163
1303
|
extends: this.extends,
|
|
1164
1304
|
componentComment: this.componentComment,
|
|
1165
|
-
contexts:
|
|
1305
|
+
contexts: contextsArray,
|
|
1166
1306
|
};
|
|
1167
1307
|
}
|
|
1168
1308
|
}
|
package/lib/resolve-alias.js
CHANGED
|
@@ -7,6 +7,7 @@ const node_fs_1 = require("node:fs");
|
|
|
7
7
|
const node_path_1 = require("node:path");
|
|
8
8
|
const path_1 = require("./path");
|
|
9
9
|
const configCache = new Map();
|
|
10
|
+
const pathPatternRegexCache = new Map();
|
|
10
11
|
const COMMENT_PATTERN = /\/\*[\s\S]*?\*\/|\/\/.*/g;
|
|
11
12
|
const REGEX_SPECIAL_CHARS = /[.+?^${}()|[\]\\]/g;
|
|
12
13
|
function clearConfigCache() {
|
|
@@ -83,12 +84,16 @@ function resolvePathAliasAbsolute(importPath, fromDir) {
|
|
|
83
84
|
// e.g., "$lib/*" -> /^\$lib\/(.*)$/
|
|
84
85
|
// e.g., "$lib" -> /^\$lib$/
|
|
85
86
|
// e.g., "@components/*" -> /^@components\/(.*)$/
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
let regex = pathPatternRegexCache.get(pattern);
|
|
88
|
+
if (!regex) {
|
|
89
|
+
// Escape special regex chars but keep * for replacement
|
|
90
|
+
const escapedPattern = pattern
|
|
91
|
+
.split("*")
|
|
92
|
+
.map((part) => part.replace(REGEX_SPECIAL_CHARS, "\\$&"))
|
|
93
|
+
.join("(.*)");
|
|
94
|
+
regex = new RegExp(`^${escapedPattern}$`);
|
|
95
|
+
pathPatternRegexCache.set(pattern, regex);
|
|
96
|
+
}
|
|
92
97
|
const match = importPath.match(regex);
|
|
93
98
|
if (match) {
|
|
94
99
|
// Use the first mapping (TypeScript uses the first match)
|
package/lib/rollup-plugin.js
CHANGED
|
@@ -69,42 +69,47 @@ async function generateBundle(input, glob) {
|
|
|
69
69
|
const allComponentsForTypes = new Map();
|
|
70
70
|
const exportEntries = Object.entries(exports);
|
|
71
71
|
const allComponentEntries = Object.entries(allComponents);
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
const uniqueFilePaths = new Set();
|
|
73
|
+
for (const [, entry] of exportEntries) {
|
|
74
74
|
const filePath = entry.source;
|
|
75
|
-
const { ext
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
moduleName = name;
|
|
75
|
+
const { ext } = (0, node_path_1.parse)(filePath);
|
|
76
|
+
if (ext === ".svelte") {
|
|
77
|
+
uniqueFilePaths.add((0, node_path_1.resolve)(dir, filePath));
|
|
79
78
|
}
|
|
79
|
+
}
|
|
80
|
+
for (const [, entry] of allComponentEntries) {
|
|
81
|
+
const filePath = entry.source;
|
|
82
|
+
const { ext } = (0, node_path_1.parse)(filePath);
|
|
80
83
|
if (ext === ".svelte") {
|
|
81
|
-
|
|
82
|
-
const { code: processed } = await (0, compiler_1.preprocess)(source, [(0, svelte_preprocess_1.typescript)(), (0, svelte_preprocess_1.replace)([[STYLE_TAG_REGEX, ""]])], {
|
|
83
|
-
filename: (0, node_path_1.basename)(filePath),
|
|
84
|
-
});
|
|
85
|
-
const parser = new ComponentParser_1.default();
|
|
86
|
-
const parsed = parser.parseSvelteComponent(processed, {
|
|
87
|
-
moduleName,
|
|
88
|
-
filePath,
|
|
89
|
-
});
|
|
90
|
-
return {
|
|
91
|
-
moduleName,
|
|
92
|
-
filePath,
|
|
93
|
-
...parsed,
|
|
94
|
-
};
|
|
84
|
+
uniqueFilePaths.add((0, node_path_1.resolve)(dir, filePath));
|
|
95
85
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
86
|
+
}
|
|
87
|
+
const fileContents = await Promise.all(Array.from(uniqueFilePaths).map(async (filePath) => {
|
|
88
|
+
try {
|
|
89
|
+
const content = await (0, promises_1.readFile)(filePath, "utf-8");
|
|
90
|
+
return { path: filePath, content };
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.warn(`Warning: Failed to read file ${filePath}:`, error);
|
|
94
|
+
return { path: filePath, content: null };
|
|
95
|
+
}
|
|
96
|
+
}));
|
|
97
|
+
const fileMap = new Map(fileContents.map(({ path, content }) => [path, content]));
|
|
98
|
+
// Helper function to process a single component
|
|
99
|
+
const processComponent = async ([exportName, entry], entries, fileMap) => {
|
|
100
100
|
const filePath = entry.source;
|
|
101
101
|
const { ext, name } = (0, node_path_1.parse)(filePath);
|
|
102
102
|
let moduleName = exportName;
|
|
103
|
-
if (
|
|
103
|
+
if (entries.length === 1 && exportName === "default") {
|
|
104
104
|
moduleName = name;
|
|
105
105
|
}
|
|
106
106
|
if (ext === ".svelte") {
|
|
107
|
-
const
|
|
107
|
+
const resolvedPath = (0, node_path_1.resolve)(dir, filePath);
|
|
108
|
+
const source = fileMap.get(resolvedPath);
|
|
109
|
+
if (source === null || source === undefined) {
|
|
110
|
+
// File was not found or failed to read, skip this component
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
108
113
|
const { code: processed } = await (0, compiler_1.preprocess)(source, [(0, svelte_preprocess_1.typescript)(), (0, svelte_preprocess_1.replace)([[STYLE_TAG_REGEX, ""]])], {
|
|
109
114
|
filename: (0, node_path_1.basename)(filePath),
|
|
110
115
|
});
|
|
@@ -120,7 +125,11 @@ async function generateBundle(input, glob) {
|
|
|
120
125
|
};
|
|
121
126
|
}
|
|
122
127
|
return null;
|
|
123
|
-
}
|
|
128
|
+
};
|
|
129
|
+
// Process exported components (for metadata/JSON/Markdown)
|
|
130
|
+
const componentPromises = exportEntries.map((entry) => processComponent(entry, exportEntries, fileMap));
|
|
131
|
+
// Process all components (for .d.ts generation)
|
|
132
|
+
const allComponentPromises = allComponentEntries.map((entry) => processComponent(entry, allComponentEntries, fileMap));
|
|
124
133
|
const [results, allResults] = await Promise.all([Promise.all(componentPromises), Promise.all(allComponentPromises)]);
|
|
125
134
|
for (const result of results) {
|
|
126
135
|
if (result) {
|
|
@@ -10,10 +10,11 @@ interface TocLine {
|
|
|
10
10
|
}
|
|
11
11
|
export default class WriterMarkdown extends Writer {
|
|
12
12
|
onAppend?: OnAppend;
|
|
13
|
-
|
|
13
|
+
private sourceParts;
|
|
14
14
|
hasToC: boolean;
|
|
15
15
|
toc: TocLine[];
|
|
16
16
|
constructor(options: MarkdownOptions);
|
|
17
|
+
get source(): string;
|
|
17
18
|
appendLineBreaks(): this;
|
|
18
19
|
append(type: AppendType, raw?: string): this;
|
|
19
20
|
tableOfContents(): this;
|
|
@@ -8,15 +8,18 @@ const BACKTICK_REGEX = /`/g;
|
|
|
8
8
|
const WHITESPACE_REGEX = /\s+/g;
|
|
9
9
|
class WriterMarkdown extends Writer_1.default {
|
|
10
10
|
onAppend;
|
|
11
|
-
|
|
11
|
+
sourceParts = [];
|
|
12
12
|
hasToC = false;
|
|
13
13
|
toc = [];
|
|
14
14
|
constructor(options) {
|
|
15
15
|
super({ parser: "markdown", printWidth: 80 });
|
|
16
16
|
this.onAppend = options.onAppend;
|
|
17
17
|
}
|
|
18
|
+
get source() {
|
|
19
|
+
return this.sourceParts.join("");
|
|
20
|
+
}
|
|
18
21
|
appendLineBreaks() {
|
|
19
|
-
this.
|
|
22
|
+
this.sourceParts.push("\n\n");
|
|
20
23
|
return this;
|
|
21
24
|
}
|
|
22
25
|
append(type, raw) {
|
|
@@ -28,9 +31,7 @@ class WriterMarkdown extends Writer_1.default {
|
|
|
28
31
|
case "h5":
|
|
29
32
|
case "h6": {
|
|
30
33
|
const length = Number(type.slice(-1));
|
|
31
|
-
this.
|
|
32
|
-
.map((_) => "#")
|
|
33
|
-
.join("")} ${raw}`;
|
|
34
|
+
this.sourceParts.push(`${"#".repeat(length)} ${raw}`);
|
|
34
35
|
if (this.hasToC && type === "h2") {
|
|
35
36
|
this.toc.push({
|
|
36
37
|
array: Array.from({ length: (length - 1) * 2 }),
|
|
@@ -40,16 +41,16 @@ class WriterMarkdown extends Writer_1.default {
|
|
|
40
41
|
break;
|
|
41
42
|
}
|
|
42
43
|
case "quote":
|
|
43
|
-
this.
|
|
44
|
+
this.sourceParts.push(`> ${raw}`);
|
|
44
45
|
break;
|
|
45
46
|
case "p":
|
|
46
|
-
this.
|
|
47
|
+
this.sourceParts.push(raw ?? "");
|
|
47
48
|
break;
|
|
48
49
|
case "divider":
|
|
49
|
-
this.
|
|
50
|
+
this.sourceParts.push("---");
|
|
50
51
|
break;
|
|
51
52
|
case "raw":
|
|
52
|
-
this.
|
|
53
|
+
this.sourceParts.push(raw ?? "");
|
|
53
54
|
break;
|
|
54
55
|
}
|
|
55
56
|
if (type !== "raw")
|
|
@@ -58,18 +59,18 @@ class WriterMarkdown extends Writer_1.default {
|
|
|
58
59
|
return this;
|
|
59
60
|
}
|
|
60
61
|
tableOfContents() {
|
|
61
|
-
this.
|
|
62
|
+
this.sourceParts.push("<!-- __TOC__ -->");
|
|
62
63
|
this.hasToC = true;
|
|
63
64
|
this.appendLineBreaks();
|
|
64
65
|
return this;
|
|
65
66
|
}
|
|
66
67
|
end() {
|
|
67
|
-
|
|
68
|
+
const source = this.sourceParts.join("");
|
|
69
|
+
return source.replace("<!-- __TOC__ -->", this.toc
|
|
68
70
|
.map(({ array, raw }) => {
|
|
69
71
|
return `${array.join(" ")} - [${raw}](#${raw.toLowerCase().replace(BACKTICK_REGEX, "").replace(WHITESPACE_REGEX, "-")})`;
|
|
70
72
|
})
|
|
71
73
|
.join("\n"));
|
|
72
|
-
return this.source;
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
exports.default = WriterMarkdown;
|
|
@@ -10,10 +10,11 @@ interface TocLine {
|
|
|
10
10
|
}
|
|
11
11
|
export declare class BrowserWriterMarkdown {
|
|
12
12
|
onAppend?: OnAppend;
|
|
13
|
-
|
|
13
|
+
private sourceParts;
|
|
14
14
|
hasToC: boolean;
|
|
15
15
|
toc: TocLine[];
|
|
16
16
|
constructor(options: MarkdownOptions);
|
|
17
|
+
get source(): string;
|
|
17
18
|
appendLineBreaks(): this;
|
|
18
19
|
append(type: AppendType, raw?: string): this;
|
|
19
20
|
tableOfContents(): this;
|
|
@@ -8,14 +8,17 @@ const WHITESPACE_REGEX = /\s+/g;
|
|
|
8
8
|
// Browser-compatible WriterMarkdown that doesn't extend Writer
|
|
9
9
|
class BrowserWriterMarkdown {
|
|
10
10
|
onAppend;
|
|
11
|
-
|
|
11
|
+
sourceParts = [];
|
|
12
12
|
hasToC = false;
|
|
13
13
|
toc = [];
|
|
14
14
|
constructor(options) {
|
|
15
15
|
this.onAppend = options.onAppend;
|
|
16
16
|
}
|
|
17
|
+
get source() {
|
|
18
|
+
return this.sourceParts.join("");
|
|
19
|
+
}
|
|
17
20
|
appendLineBreaks() {
|
|
18
|
-
this.
|
|
21
|
+
this.sourceParts.push("\n\n");
|
|
19
22
|
return this;
|
|
20
23
|
}
|
|
21
24
|
append(type, raw) {
|
|
@@ -27,9 +30,7 @@ class BrowserWriterMarkdown {
|
|
|
27
30
|
case "h5":
|
|
28
31
|
case "h6": {
|
|
29
32
|
const length = Number(type.slice(-1));
|
|
30
|
-
this.
|
|
31
|
-
.map((_) => "#")
|
|
32
|
-
.join("")} ${raw}`;
|
|
33
|
+
this.sourceParts.push(`${"#".repeat(length)} ${raw}`);
|
|
33
34
|
if (this.hasToC && type === "h2") {
|
|
34
35
|
this.toc.push({
|
|
35
36
|
array: Array.from({ length: (length - 1) * 2 }),
|
|
@@ -39,16 +40,16 @@ class BrowserWriterMarkdown {
|
|
|
39
40
|
break;
|
|
40
41
|
}
|
|
41
42
|
case "quote":
|
|
42
|
-
this.
|
|
43
|
+
this.sourceParts.push(`> ${raw}`);
|
|
43
44
|
break;
|
|
44
45
|
case "p":
|
|
45
|
-
this.
|
|
46
|
+
this.sourceParts.push(raw ?? "");
|
|
46
47
|
break;
|
|
47
48
|
case "divider":
|
|
48
|
-
this.
|
|
49
|
+
this.sourceParts.push("---");
|
|
49
50
|
break;
|
|
50
51
|
case "raw":
|
|
51
|
-
this.
|
|
52
|
+
this.sourceParts.push(raw ?? "");
|
|
52
53
|
break;
|
|
53
54
|
}
|
|
54
55
|
if (type !== "raw")
|
|
@@ -57,18 +58,18 @@ class BrowserWriterMarkdown {
|
|
|
57
58
|
return this;
|
|
58
59
|
}
|
|
59
60
|
tableOfContents() {
|
|
60
|
-
this.
|
|
61
|
+
this.sourceParts.push("<!-- __TOC__ -->");
|
|
61
62
|
this.hasToC = true;
|
|
62
63
|
this.appendLineBreaks();
|
|
63
64
|
return this;
|
|
64
65
|
}
|
|
65
66
|
end() {
|
|
66
|
-
|
|
67
|
+
const source = this.sourceParts.join("");
|
|
68
|
+
return source.replace("<!-- __TOC__ -->", this.toc
|
|
67
69
|
.map(({ array, raw }) => {
|
|
68
70
|
return `${array.join(" ")} - [${raw}](#${raw.toLowerCase().replace(BACKTICK_REGEX, "").replace(WHITESPACE_REGEX, "-")})`;
|
|
69
71
|
})
|
|
70
72
|
.join("\n"));
|
|
71
|
-
return this.source;
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
exports.BrowserWriterMarkdown = BrowserWriterMarkdown;
|