sveld 0.25.1 → 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.
@@ -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;
@@ -35,6 +35,7 @@ class ComponentParser {
35
35
  bindings = new Map();
36
36
  contexts = new Map();
37
37
  variableInfoCache = new Map();
38
+ sourceLinesCache;
38
39
  constructor(options) {
39
40
  this.options = options;
40
41
  }
@@ -54,6 +55,45 @@ class ComponentParser {
54
55
  }
55
56
  return formatted_comment;
56
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
+ }
57
97
  /**
58
98
  * Finds the last comment from an array of leading comments.
59
99
  * TypeScript directives are stripped before parsing, so we can safely take the last comment.
@@ -138,7 +178,9 @@ class ComponentParser {
138
178
  const name = default_slot ? DEFAULT_SLOT_NAME : (slot_name ?? "");
139
179
  const fallback = ComponentParser.assignValue(slot_fallback);
140
180
  const props = ComponentParser.assignValue(slot_props);
141
- const description = slot_description?.split("-").pop()?.trim();
181
+ const description = slot_description
182
+ ? slot_description.substring(slot_description.lastIndexOf("-") + 1).trim()
183
+ : undefined;
142
184
  if (this.slots.has(name)) {
143
185
  const existing_slot = this.slots.get(name);
144
186
  this.slots.set(name, {
@@ -167,7 +209,7 @@ class ComponentParser {
167
209
  * `@event` is not specified.
168
210
  */
169
211
  const default_detail = !has_argument && !detail ? "null" : ComponentParser.assignValue(detail);
170
- const event_description = description?.split("-").pop()?.trim();
212
+ const event_description = description ? description.substring(description.lastIndexOf("-") + 1).trim() : undefined;
171
213
  if (this.events.has(name)) {
172
214
  const existing_event = this.events.get(name);
173
215
  this.events.set(name, {
@@ -453,7 +495,10 @@ class ComponentParser {
453
495
  buildVariableInfoCache() {
454
496
  if (!this.source)
455
497
  return;
456
- const lines = this.source.split("\n");
498
+ if (!this.sourceLinesCache) {
499
+ this.sourceLinesCache = this.source.split("\n");
500
+ }
501
+ const lines = this.sourceLinesCache;
457
502
  for (let i = 0; i < lines.length; i++) {
458
503
  const line = lines[i].trim();
459
504
  // Match variable declarations
@@ -477,14 +522,12 @@ class ComponentParser {
477
522
  const commentBlock = commentLines.join("\n");
478
523
  // Parse the JSDoc
479
524
  const parsed = (0, comment_parser_1.parse)(commentBlock, { spacing: "preserve" });
480
- if (parsed[0]?.tags) {
481
- const typeTag = parsed[0].tags.find((t) => t.tag === "type");
482
- if (typeTag) {
483
- this.variableInfoCache.set(varName, {
484
- type: this.aliasType(typeTag.type),
485
- description: parsed[0].description || typeTag.description,
486
- });
487
- }
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
+ });
488
531
  }
489
532
  break;
490
533
  }
@@ -492,6 +535,20 @@ class ComponentParser {
492
535
  }
493
536
  }
494
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
+ }
495
552
  findVariableTypeAndDescription(varName) {
496
553
  const cached = this.variableInfoCache.get(varName);
497
554
  if (cached) {
@@ -500,15 +557,18 @@ class ComponentParser {
500
557
  // Search through the source code directly for JSDoc comments
501
558
  if (!this.source)
502
559
  return null;
503
- // Build a map of variable names to their types by looking at the source
504
- const lines = this.source.split("\n");
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);
505
565
  for (let i = 0; i < lines.length; i++) {
506
566
  const line = lines[i].trim();
507
567
  // Check if this line declares our variable
508
568
  // Match patterns like: const varName = ..., let varName = ..., function varName
509
- const constMatch = line.match(new RegExp(`const\\s+${varName}\\s*=`));
510
- const letMatch = line.match(new RegExp(`let\\s+${varName}\\s*=`));
511
- const funcMatch = line.match(new RegExp(`function\\s+${varName}\\s*\\(`));
569
+ const constMatch = line.match(constRegex);
570
+ const letMatch = line.match(letRegex);
571
+ const funcMatch = line.match(funcRegex);
512
572
  if (constMatch || letMatch || funcMatch) {
513
573
  // Look backwards for JSDoc comment
514
574
  for (let j = i - 1; j >= 0; j--) {
@@ -527,14 +587,12 @@ class ComponentParser {
527
587
  const commentBlock = commentLines.join("\n");
528
588
  // Parse the JSDoc
529
589
  const parsed = (0, comment_parser_1.parse)(commentBlock, { spacing: "preserve" });
530
- if (parsed[0]?.tags) {
531
- const typeTag = parsed[0].tags.find((t) => t.tag === "type");
532
- if (typeTag) {
533
- return {
534
- type: this.aliasType(typeTag.type),
535
- description: parsed[0].description || typeTag.description,
536
- };
537
- }
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
+ };
538
596
  }
539
597
  break;
540
598
  }
@@ -685,6 +743,7 @@ class ComponentParser {
685
743
  this.bindings.clear();
686
744
  this.contexts.clear();
687
745
  this.variableInfoCache.clear();
746
+ this.sourceLinesCache = undefined;
688
747
  }
689
748
  // Pre-compiled regexes for better performance
690
749
  static SCRIPT_BLOCK_REGEX = /(<script[^>]*>)([\s\S]*?)(<\/script>)/gi;
@@ -718,6 +777,7 @@ class ComponentParser {
718
777
  // The compile result includes the parsed AST
719
778
  this.parsed = compiled.ast || (0, compiler_1.parse)(cleanedSource);
720
779
  this.collectReactiveVars();
780
+ this.sourceLinesCache = this.source.split("\n");
721
781
  this.buildVariableInfoCache();
722
782
  this.parseCustomTypes();
723
783
  if (this.parsed?.module) {
@@ -798,12 +858,11 @@ class ComponentParser {
798
858
  const comment = (0, comment_parser_1.parse)(ComponentParser.formatComment(jsdoc_comment.value), {
799
859
  spacing: "preserve",
800
860
  });
861
+ const { type: typeTag, param: paramTags, returns: returnsTag, additional: additionalTags, description: commentDescription, } = this.getCommentTags(comment);
801
862
  // Extract @type tag
802
- const typeTag = comment[0]?.tags.find((t) => t.tag === "type");
803
863
  if (typeTag)
804
864
  type = this.aliasType(typeTag.type);
805
865
  // Extract @param tags
806
- const paramTags = comment[0]?.tags.filter((t) => t.tag === "param") ?? [];
807
866
  if (paramTags.length > 0) {
808
867
  params = paramTags
809
868
  .filter((tag) => !tag.name.includes(".")) // Exclude nested params like "options.expand"
@@ -815,27 +874,20 @@ class ComponentParser {
815
874
  }));
816
875
  }
817
876
  // Extract @returns/@return tag
818
- const returnsTag = comment[0]?.tags.find((t) => t.tag === "returns" || t.tag === "return");
819
877
  if (returnsTag)
820
878
  returnType = this.aliasType(returnsTag.type);
821
879
  // Build description from comment description and non-param/non-type tags
822
- const commentDescription = ComponentParser.assignValue(comment[0]?.description?.trim());
823
- const additionalTags = comment[0]?.tags.filter((tag) => ![
824
- "type",
825
- "param",
826
- "returns",
827
- "return",
828
- "extends",
829
- "restProps",
830
- "slot",
831
- "event",
832
- "typedef",
833
- ].includes(tag.tag)) ?? [];
834
- if (commentDescription || additionalTags.length > 0) {
835
- 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
+ }
836
886
  for (const tag of additionalTags) {
837
- description += `${description ? "\n" : ""}@${tag.tag}${tag.name ? ` ${tag.name}` : ""}${tag.description ? ` ${tag.description}` : ""}`;
887
+ const tagStr = `@${tag.tag}${tag.name ? ` ${tag.name}` : ""}${tag.description ? ` ${tag.description}` : ""}`;
888
+ descriptionParts.push(tagStr);
838
889
  }
890
+ description = descriptionParts.join("\n");
839
891
  }
840
892
  }
841
893
  }
@@ -994,12 +1046,11 @@ class ComponentParser {
994
1046
  const comment = (0, comment_parser_1.parse)(ComponentParser.formatComment(jsdoc_comment.value), {
995
1047
  spacing: "preserve",
996
1048
  });
1049
+ const { type: typeTag, param: paramTags, returns: returnsTag, additional: additional_tags, description: commentDescription, } = this.getCommentTags(comment);
997
1050
  // Extract @type tag
998
- const typeTag = comment[0]?.tags.find((t) => t.tag === "type");
999
1051
  if (typeTag)
1000
1052
  type = this.aliasType(typeTag.type);
1001
1053
  // Extract @param tags
1002
- const paramTags = comment[0]?.tags.filter((t) => t.tag === "param") ?? [];
1003
1054
  if (paramTags.length > 0) {
1004
1055
  params = paramTags
1005
1056
  .filter((tag) => !tag.name.includes(".")) // Exclude nested params like "options.expand"
@@ -1011,27 +1062,20 @@ class ComponentParser {
1011
1062
  }));
1012
1063
  }
1013
1064
  // Extract @returns/@return tag
1014
- const returnsTag = comment[0]?.tags.find((t) => t.tag === "returns" || t.tag === "return");
1015
1065
  if (returnsTag)
1016
1066
  returnType = this.aliasType(returnsTag.type);
1017
1067
  // Build description from comment description and non-param/non-type tags
1018
- const commentDescription = ComponentParser.assignValue(comment[0]?.description?.trim());
1019
- const additional_tags = comment[0]?.tags.filter((tag) => ![
1020
- "type",
1021
- "param",
1022
- "returns",
1023
- "return",
1024
- "extends",
1025
- "restProps",
1026
- "slot",
1027
- "event",
1028
- "typedef",
1029
- ].includes(tag.tag)) ?? [];
1030
- if (commentDescription || additional_tags.length > 0) {
1031
- 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
+ }
1032
1074
  for (const tag of additional_tags) {
1033
- description += `${description ? "\n" : ""}@${tag.tag}${tag.name ? ` ${tag.name}` : ""}${tag.description ? ` ${tag.description}` : ""}`;
1075
+ const tagStr = `@${tag.tag}${tag.name ? ` ${tag.name}` : ""}${tag.description ? ` ${tag.description}` : ""}`;
1076
+ descriptionParts.push(tagStr);
1034
1077
  }
1078
+ description = descriptionParts.join("\n");
1035
1079
  }
1036
1080
  }
1037
1081
  }
@@ -1113,7 +1157,9 @@ class ComponentParser {
1113
1157
  const existing_event = this.events.get(node.name);
1114
1158
  // Check if this event has a JSDoc description
1115
1159
  const description = this.eventDescriptions.get(node.name);
1116
- const event_description = description?.split("-").pop()?.trim();
1160
+ const event_description = description
1161
+ ? description.substring(description.lastIndexOf("-") + 1).trim()
1162
+ : undefined;
1117
1163
  if (!existing_event) {
1118
1164
  // Add new forwarded event
1119
1165
  this.events.set(node.name, {
@@ -1183,7 +1229,9 @@ class ComponentParser {
1183
1229
  // If event is marked as dispatched but is NOT actually dispatched, convert it to forwarded
1184
1230
  if (event && event.type === "dispatched" && !actuallyDispatchedEvents.has(eventName)) {
1185
1231
  const description = this.eventDescriptions.get(eventName);
1186
- const event_description = description?.split("-").pop()?.trim();
1232
+ const event_description = description
1233
+ ? description.substring(description.lastIndexOf("-") + 1).trim()
1234
+ : undefined;
1187
1235
  const forwardedEvent = {
1188
1236
  type: "forwarded",
1189
1237
  name: eventName,
@@ -1199,56 +1247,62 @@ class ComponentParser {
1199
1247
  this.events.set(eventName, forwardedEvent);
1200
1248
  }
1201
1249
  });
1202
- return {
1203
- props: ComponentParser.mapToArray(this.props).map((prop) => {
1204
- if (this.bindings.has(prop.name)) {
1205
- return {
1206
- ...prop,
1207
- type: "null | " +
1208
- this.bindings
1209
- .get(prop.name)
1210
- ?.elements.sort()
1211
- .map((element) => (0, element_tag_map_1.getElementByTag)(element))
1212
- .join(" | "),
1213
- };
1214
- }
1215
- return prop;
1216
- }),
1217
- moduleExports: ComponentParser.mapToArray(this.moduleExports),
1218
- slots: ComponentParser.mapToArray(this.slots)
1219
- .map((slot) => {
1220
- try {
1221
- const slot_props = JSON.parse(slot.slot_props);
1222
- const new_props = [];
1223
- for (const key of Object.keys(slot_props)) {
1224
- if (slot_props[key].replace && slot_props[key].value !== undefined) {
1225
- slot_props[key].value = this.props.get(slot_props[key].value)?.type;
1226
- }
1227
- if (slot_props[key].value === undefined)
1228
- slot_props[key].value = "any";
1229
- 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;
1230
1272
  }
1231
- const formatted_slot_props = new_props.length === 0 ? "Record<string, never>" : `{ ${new_props.join(", ")} }`;
1232
- return { ...slot, slot_props: formatted_slot_props };
1233
- }
1234
- catch (_e) {
1235
- return slot;
1273
+ if (slot_props[key].value === undefined)
1274
+ slot_props[key].value = "any";
1275
+ new_props.push(`${key}: ${slot_props[key].value}`);
1236
1276
  }
1237
- })
1238
- .sort((a, b) => {
1239
- if (a.name < b.name)
1240
- return -1;
1241
- if (a.name > b.name)
1242
- return 1;
1243
- return 0;
1244
- }),
1245
- events: ComponentParser.mapToArray(this.events),
1246
- typedefs: ComponentParser.mapToArray(this.typedefs),
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,
1247
1301
  generics: this.generics,
1248
1302
  rest_props: this.rest_props,
1249
1303
  extends: this.extends,
1250
1304
  componentComment: this.componentComment,
1251
- contexts: ComponentParser.mapToArray(this.contexts),
1305
+ contexts: contextsArray,
1252
1306
  };
1253
1307
  }
1254
1308
  }
@@ -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
- // Escape special regex chars but keep * for replacement
87
- const escapedPattern = pattern
88
- .split("*")
89
- .map((part) => part.replace(REGEX_SPECIAL_CHARS, "\\$&"))
90
- .join("(.*)");
91
- const regex = new RegExp(`^${escapedPattern}$`);
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)
@@ -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
- // Process exported components (for metadata/JSON/Markdown)
73
- const componentPromises = exportEntries.map(async ([exportName, entry]) => {
72
+ const uniqueFilePaths = new Set();
73
+ for (const [, entry] of exportEntries) {
74
74
  const filePath = entry.source;
75
- const { ext, name } = (0, node_path_1.parse)(filePath);
76
- let moduleName = exportName;
77
- if (exportEntries.length === 1 && exportName === "default") {
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
- const source = await (0, promises_1.readFile)((0, node_path_1.resolve)(dir, filePath), "utf-8");
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
- return null;
97
- });
98
- // Process all components (for .d.ts generation)
99
- const allComponentPromises = allComponentEntries.map(async ([exportName, entry]) => {
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 (allComponentEntries.length === 1 && exportName === "default") {
103
+ if (entries.length === 1 && exportName === "default") {
104
104
  moduleName = name;
105
105
  }
106
106
  if (ext === ".svelte") {
107
- const source = await (0, promises_1.readFile)((0, node_path_1.resolve)(dir, filePath), "utf-8");
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
- source: string;
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
- source = "";
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.source += "\n\n";
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.source += `${Array.from({ length })
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.source += `> ${raw}`;
44
+ this.sourceParts.push(`> ${raw}`);
44
45
  break;
45
46
  case "p":
46
- this.source += raw;
47
+ this.sourceParts.push(raw ?? "");
47
48
  break;
48
49
  case "divider":
49
- this.source += "---";
50
+ this.sourceParts.push("---");
50
51
  break;
51
52
  case "raw":
52
- this.source += raw;
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.source += "<!-- __TOC__ -->";
62
+ this.sourceParts.push("<!-- __TOC__ -->");
62
63
  this.hasToC = true;
63
64
  this.appendLineBreaks();
64
65
  return this;
65
66
  }
66
67
  end() {
67
- this.source = this.source.replace("<!-- __TOC__ -->", this.toc
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
- source: string;
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
- source = "";
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.source += "\n\n";
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.source += `${Array.from({ length })
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.source += `> ${raw}`;
43
+ this.sourceParts.push(`> ${raw}`);
43
44
  break;
44
45
  case "p":
45
- this.source += raw;
46
+ this.sourceParts.push(raw ?? "");
46
47
  break;
47
48
  case "divider":
48
- this.source += "---";
49
+ this.sourceParts.push("---");
49
50
  break;
50
51
  case "raw":
51
- this.source += raw;
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.source += "<!-- __TOC__ -->";
61
+ this.sourceParts.push("<!-- __TOC__ -->");
61
62
  this.hasToC = true;
62
63
  this.appendLineBreaks();
63
64
  return this;
64
65
  }
65
66
  end() {
66
- this.source = this.source.replace("<!-- __TOC__ -->", this.toc
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sveld",
3
- "version": "0.25.1",
3
+ "version": "0.25.2",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Generate TypeScript definitions for your Svelte components.",
6
6
  "main": "./lib/index.js",