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.
@@ -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, { spacing: "preserve" })) {
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: description ? description : undefined,
348
+ slot_description: slotDesc || undefined,
273
349
  });
274
350
  break;
275
- case "event":
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
- // Use the main comment description if available, otherwise use inline description
282
- currentEventDescription = commentDescription?.trim() || description || undefined;
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
- // Use inline description if present, otherwise use comment description only if not already used
314
- const trimmedCommentDesc = commentDescription?.trim();
315
- if (description) {
316
- currentTypedefDescription = description;
317
- }
318
- else if (!commentDescriptionUsed && trimmedCommentDesc && trimmedCommentDesc !== "}") {
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
- else {
323
- currentTypedefDescription = undefined;
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sveld",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Generate TypeScript definitions for your Svelte components.",
6
6
  "main": "./lib/index.js",