sveld 0.24.9 → 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;
@@ -194,6 +197,64 @@ class ComponentParser {
194
197
  let currentTypedefType;
195
198
  let currentTypedefDescription;
196
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
+ };
197
258
  const finalizeEvent = () => {
198
259
  if (currentEventName !== undefined) {
199
260
  let detailType;
@@ -249,37 +310,63 @@ class ComponentParser {
249
310
  currentTypedefDescription = undefined;
250
311
  }
251
312
  };
252
- 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) {
253
314
  const type = this.aliasType(tagType);
315
+ // Get the description from the line immediately before this tag
316
+ const precedingDescription = getPrecedingDescription(tagSource);
254
317
  switch (tag) {
255
318
  case "extends":
256
319
  this.extends = {
257
320
  interface: name,
258
321
  import: type,
259
322
  };
323
+ if (isFirstTag)
324
+ isFirstTag = false;
260
325
  break;
261
326
  case "restProps":
262
327
  this.rest_props = {
263
328
  type: "Element",
264
329
  name: type,
265
330
  };
331
+ if (isFirstTag)
332
+ isFirstTag = false;
266
333
  break;
267
- 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;
268
345
  this.addSlot({
269
346
  slot_name: name,
270
347
  slot_props: type,
271
- slot_description: description ? description : undefined,
348
+ slot_description: slotDesc || undefined,
272
349
  });
273
350
  break;
274
- case "event":
351
+ }
352
+ case "event": {
275
353
  // Finalize any previous event being built
276
354
  finalizeEvent();
277
355
  // Start tracking new event
278
356
  currentEventName = name;
279
357
  currentEventType = type;
280
- // Use the main comment description if available, otherwise use inline description
281
- 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;
282
368
  break;
369
+ }
283
370
  case "type":
284
371
  // Track the @type tag for the current event
285
372
  if (currentEventName !== undefined) {
@@ -309,14 +396,22 @@ class ComponentParser {
309
396
  // Start tracking new typedef
310
397
  currentTypedefName = name;
311
398
  currentTypedefType = type;
312
- // Use inline description if present, otherwise use comment description
313
- const trimmedCommentDesc = commentDescription?.trim();
314
- currentTypedefDescription =
315
- description || (trimmedCommentDesc && trimmedCommentDesc !== "}" ? trimmedCommentDesc : undefined);
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;
405
+ commentDescriptionUsed = true;
406
+ }
407
+ if (isFirstTag)
408
+ isFirstTag = false;
316
409
  break;
317
410
  }
318
411
  case "generics":
319
412
  this.generics = [name, type];
413
+ if (isFirstTag)
414
+ isFirstTag = false;
320
415
  break;
321
416
  }
322
417
  }
@@ -26,7 +26,14 @@ function formatTsProps(props) {
26
26
  function getTypeDefs(def) {
27
27
  if (def.typedefs.length === 0)
28
28
  return EMPTY_STR;
29
- return def.typedefs.map((typedef) => `export ${typedef.ts}`).join("\n\n");
29
+ return def.typedefs
30
+ .map((typedef) => {
31
+ const typedefComment = typedef.description
32
+ ? `/**\n * ${typedef.description.replace(NEWLINE_TO_COMMENT_REGEX, "\n * ")}\n */\n`
33
+ : "";
34
+ return `${typedefComment}export ${typedef.ts}`;
35
+ })
36
+ .join("\n\n");
30
37
  }
31
38
  function getContextDefs(def) {
32
39
  if (!def.contexts || def.contexts.length === 0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sveld",
3
- "version": "0.24.9",
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",