sveld 0.25.0 → 0.25.1
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.js +104 -18
- package/package.json +1 -1
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 = /(\}|\};)$/;
|
|
@@ -185,7 +186,9 @@ class ComponentParser {
|
|
|
185
186
|
}
|
|
186
187
|
}
|
|
187
188
|
parseCustomTypes() {
|
|
188
|
-
for (const { tags, description: commentDescription } of (0, comment_parser_1.parse)(this.source, {
|
|
189
|
+
for (const { tags, description: commentDescription, source: blockSource } of (0, comment_parser_1.parse)(this.source, {
|
|
190
|
+
spacing: "preserve",
|
|
191
|
+
})) {
|
|
189
192
|
let currentEventName;
|
|
190
193
|
let currentEventType;
|
|
191
194
|
let currentEventDescription;
|
|
@@ -193,8 +196,65 @@ class ComponentParser {
|
|
|
193
196
|
let currentTypedefName;
|
|
194
197
|
let currentTypedefType;
|
|
195
198
|
let currentTypedefDescription;
|
|
196
|
-
let commentDescriptionUsed = false;
|
|
197
199
|
const typedefProperties = [];
|
|
200
|
+
// Track if we've used the comment block description for any tag in this block
|
|
201
|
+
// Only the first tag (that needs a description) should use the comment block description
|
|
202
|
+
let commentDescriptionUsed = false;
|
|
203
|
+
let isFirstTag = true;
|
|
204
|
+
// Build a map of line numbers to their description content (for lines without tags)
|
|
205
|
+
const lineDescriptions = new Map();
|
|
206
|
+
// Track line numbers that contain tags
|
|
207
|
+
const tagLineNumbers = new Set();
|
|
208
|
+
for (const tagInfo of tags) {
|
|
209
|
+
if (tagInfo.source && tagInfo.source.length > 0) {
|
|
210
|
+
tagLineNumbers.add(tagInfo.source[0].number);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
for (const line of blockSource) {
|
|
214
|
+
// Only track lines that have a description but no tag
|
|
215
|
+
// Also filter out lines that are just "}" (artifact from some comment formats)
|
|
216
|
+
if (!line.tokens.tag && line.tokens.description && line.tokens.description.trim() !== "}") {
|
|
217
|
+
lineDescriptions.set(line.number, line.tokens.description);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Helper to get the description from lines preceding a tag
|
|
221
|
+
// Look backwards from the tag until we hit another tag, collecting description lines
|
|
222
|
+
// Stop after finding the first contiguous block of description lines
|
|
223
|
+
const getPrecedingDescription = (tagSource) => {
|
|
224
|
+
if (!tagSource || tagSource.length === 0)
|
|
225
|
+
return undefined;
|
|
226
|
+
const tagLineNumber = tagSource[0].number;
|
|
227
|
+
// Look backwards from the tag line to find the immediately preceding description
|
|
228
|
+
const descLines = [];
|
|
229
|
+
let foundDescriptionBlock = false;
|
|
230
|
+
for (let lineNum = tagLineNumber - 1; lineNum >= 1; lineNum--) {
|
|
231
|
+
// Stop if we hit a tag line
|
|
232
|
+
if (tagLineNumbers.has(lineNum)) {
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
// Check if this line has a description
|
|
236
|
+
const desc = lineDescriptions.get(lineNum);
|
|
237
|
+
if (desc) {
|
|
238
|
+
descLines.unshift(desc); // Add to beginning to maintain order
|
|
239
|
+
foundDescriptionBlock = true;
|
|
240
|
+
}
|
|
241
|
+
else if (foundDescriptionBlock) {
|
|
242
|
+
// We've already found description lines and now hit a non-description line
|
|
243
|
+
// Check if it's blank - if so, continue; if not, stop
|
|
244
|
+
const sourceLine = blockSource.find((l) => l.number === lineNum);
|
|
245
|
+
const isBlank = !sourceLine ||
|
|
246
|
+
(!sourceLine.tokens.tag &&
|
|
247
|
+
(!sourceLine.tokens.description || sourceLine.tokens.description.trim() === ""));
|
|
248
|
+
if (!isBlank) {
|
|
249
|
+
// Non-blank non-description line - stop here
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
// Blank line - continue (blank lines can separate descriptions from tags)
|
|
253
|
+
}
|
|
254
|
+
// If we haven't found any description yet, continue looking backwards
|
|
255
|
+
}
|
|
256
|
+
return descLines.length > 0 ? descLines.join("\n").trim() : undefined;
|
|
257
|
+
};
|
|
198
258
|
const finalizeEvent = () => {
|
|
199
259
|
if (currentEventName !== undefined) {
|
|
200
260
|
let detailType;
|
|
@@ -250,37 +310,63 @@ class ComponentParser {
|
|
|
250
310
|
currentTypedefDescription = undefined;
|
|
251
311
|
}
|
|
252
312
|
};
|
|
253
|
-
for (const { tag, type: tagType, name, description, optional, default: defaultValue } of tags) {
|
|
313
|
+
for (const { tag, type: tagType, name, description, optional, default: defaultValue, source: tagSource, } of tags) {
|
|
254
314
|
const type = this.aliasType(tagType);
|
|
315
|
+
// Get the description from the line immediately before this tag
|
|
316
|
+
const precedingDescription = getPrecedingDescription(tagSource);
|
|
255
317
|
switch (tag) {
|
|
256
318
|
case "extends":
|
|
257
319
|
this.extends = {
|
|
258
320
|
interface: name,
|
|
259
321
|
import: type,
|
|
260
322
|
};
|
|
323
|
+
if (isFirstTag)
|
|
324
|
+
isFirstTag = false;
|
|
261
325
|
break;
|
|
262
326
|
case "restProps":
|
|
263
327
|
this.rest_props = {
|
|
264
328
|
type: "Element",
|
|
265
329
|
name: type,
|
|
266
330
|
};
|
|
331
|
+
if (isFirstTag)
|
|
332
|
+
isFirstTag = false;
|
|
267
333
|
break;
|
|
268
|
-
case "slot":
|
|
334
|
+
case "slot": {
|
|
335
|
+
// Prefer inline description, fall back to preceding line description,
|
|
336
|
+
// then fall back to the comment block description (only for first tag if not already used)
|
|
337
|
+
const inlineSlotDesc = description?.replace(COMMENT_BLOCK_DESCRIPTION_REGEX, "").trim();
|
|
338
|
+
let slotDesc = inlineSlotDesc || precedingDescription;
|
|
339
|
+
if (!slotDesc && isFirstTag && !commentDescriptionUsed && commentDescription) {
|
|
340
|
+
slotDesc = commentDescription;
|
|
341
|
+
commentDescriptionUsed = true;
|
|
342
|
+
}
|
|
343
|
+
if (isFirstTag)
|
|
344
|
+
isFirstTag = false;
|
|
269
345
|
this.addSlot({
|
|
270
346
|
slot_name: name,
|
|
271
347
|
slot_props: type,
|
|
272
|
-
slot_description:
|
|
348
|
+
slot_description: slotDesc || undefined,
|
|
273
349
|
});
|
|
274
350
|
break;
|
|
275
|
-
|
|
351
|
+
}
|
|
352
|
+
case "event": {
|
|
276
353
|
// Finalize any previous event being built
|
|
277
354
|
finalizeEvent();
|
|
278
355
|
// Start tracking new event
|
|
279
356
|
currentEventName = name;
|
|
280
357
|
currentEventType = type;
|
|
281
|
-
//
|
|
282
|
-
|
|
358
|
+
// Prefer inline description (e.g., "@event {type} name - description"),
|
|
359
|
+
// fall back to preceding line, then fall back to comment block description (only for first tag if not already used)
|
|
360
|
+
const inlineEventDesc = description?.replace(COMMENT_BLOCK_DESCRIPTION_REGEX, "").trim();
|
|
361
|
+
currentEventDescription = inlineEventDesc || precedingDescription;
|
|
362
|
+
if (!currentEventDescription && isFirstTag && !commentDescriptionUsed && commentDescription) {
|
|
363
|
+
currentEventDescription = commentDescription;
|
|
364
|
+
commentDescriptionUsed = true;
|
|
365
|
+
}
|
|
366
|
+
if (isFirstTag)
|
|
367
|
+
isFirstTag = false;
|
|
283
368
|
break;
|
|
369
|
+
}
|
|
284
370
|
case "type":
|
|
285
371
|
// Track the @type tag for the current event
|
|
286
372
|
if (currentEventName !== undefined) {
|
|
@@ -310,22 +396,22 @@ class ComponentParser {
|
|
|
310
396
|
// Start tracking new typedef
|
|
311
397
|
currentTypedefName = name;
|
|
312
398
|
currentTypedefType = type;
|
|
313
|
-
//
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
currentTypedefDescription = trimmedCommentDesc;
|
|
399
|
+
// Prefer inline description, fall back to preceding line description,
|
|
400
|
+
// then fall back to comment block description (only for first tag if not already used)
|
|
401
|
+
const inlineTypedefDesc = description?.replace(COMMENT_BLOCK_DESCRIPTION_REGEX, "").trim();
|
|
402
|
+
currentTypedefDescription = inlineTypedefDesc || precedingDescription;
|
|
403
|
+
if (!currentTypedefDescription && isFirstTag && !commentDescriptionUsed && commentDescription) {
|
|
404
|
+
currentTypedefDescription = commentDescription;
|
|
320
405
|
commentDescriptionUsed = true;
|
|
321
406
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
}
|
|
407
|
+
if (isFirstTag)
|
|
408
|
+
isFirstTag = false;
|
|
325
409
|
break;
|
|
326
410
|
}
|
|
327
411
|
case "generics":
|
|
328
412
|
this.generics = [name, type];
|
|
413
|
+
if (isFirstTag)
|
|
414
|
+
isFirstTag = false;
|
|
329
415
|
break;
|
|
330
416
|
}
|
|
331
417
|
}
|