sveld 0.25.3 → 0.25.5

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/README.md CHANGED
@@ -332,21 +332,13 @@ export let kind = "primary";
332
332
  // inferred type: "string"
333
333
  ```
334
334
 
335
- For template literal default values, `sveld` automatically infers [template literal types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html) when possible:
335
+ For template literal default values, `sveld` infers the type as `string`:
336
336
 
337
337
  ```js
338
338
  export let id = `ccs-${Math.random().toString(36)}`;
339
- // inferred type: `ccs-${string}` & {}
340
-
341
- export let prefix = `prefix-`;
342
- // inferred type: `prefix-` & {}
343
-
344
- export let suffix = `-suffix`;
345
- // inferred type: `-suffix` & {}
339
+ // inferred type: "string"
346
340
  ```
347
341
 
348
- The `& {}` intersection type makes the template literal type more permissive, allowing regular strings to be assigned while preserving the template literal type for better autocomplete and type hints. This provides more precise type checking than a generic `string` type while remaining flexible. For complex expressions in template literals, the type defaults to `string` for safety.
349
-
350
342
  Use the `@type` tag to explicitly document the type. In the following example, the `kind` property has an enumerated (enum) type.
351
343
 
352
344
  Signature:
@@ -128,16 +128,6 @@ export default class ComponentParser {
128
128
  * (e.g., Number.POSITIVE_INFINITY, Math.PI, etc.)
129
129
  */
130
130
  private isNumericConstant;
131
- /**
132
- * Infers the TypeScript type for an expression in a template literal.
133
- * Returns a type string that can be used in a template literal type.
134
- */
135
- private inferExpressionType;
136
- /**
137
- * Infers a template literal type from a TemplateLiteral AST node.
138
- * Returns a TypeScript template literal type string like `prefix-${string}`.
139
- */
140
- private inferTemplateLiteralType;
141
131
  private sourceAtPos;
142
132
  private collectReactiveVars;
143
133
  private addProp;
@@ -131,119 +131,6 @@ class ComponentParser {
131
131
  }
132
132
  return false;
133
133
  }
134
- /**
135
- * Infers the TypeScript type for an expression in a template literal.
136
- * Returns a type string that can be used in a template literal type.
137
- */
138
- inferExpressionType(expr) {
139
- if (!expr || typeof expr !== "object" || !("type" in expr)) {
140
- return "string";
141
- }
142
- /**
143
- * @example `"hello"` → `"hello"`
144
- * @example `42` → `42`
145
- * @example `true` → `true`
146
- */
147
- if (expr.type === "Literal") {
148
- if (typeof expr.value === "string") {
149
- return `"${expr.value}"`;
150
- }
151
- if (typeof expr.value === "number") {
152
- return `${expr.value}`;
153
- }
154
- if (typeof expr.value === "boolean") {
155
- return `${expr.value}`;
156
- }
157
- return "string";
158
- }
159
- /**
160
- * @example `someVar` → `string`
161
- */
162
- if (expr.type === "Identifier") {
163
- // For variables, we can't know the exact type, so default to string
164
- // Users can add explicit @type annotations if they need more precision
165
- return "string";
166
- }
167
- /**
168
- * @example `Math.PI` → `number`
169
- * @example `Number.MAX_VALUE` → `number`
170
- * @example `Math.random().toString` → `string`
171
- */
172
- if (expr.type === "MemberExpression") {
173
- // Check if it's a well-known numeric constant
174
- if (this.isNumericConstant(expr)) {
175
- return "number";
176
- }
177
- // For other member expressions, default to string
178
- // Common cases like Math.random().toString(36) will be string
179
- return "string";
180
- }
181
- /**
182
- * @example `Math.random().toString(36)` → `string`
183
- * @example `someFunction()` → `string`
184
- */
185
- if (expr.type === "CallExpression") {
186
- // Most call expressions in template literals return strings or numbers
187
- // Default to string for safety
188
- return "string";
189
- }
190
- /**
191
- * @example `a + b` → `string`
192
- * @example `x * y` → `string`
193
- */
194
- if (expr.type === "BinaryExpression") {
195
- // For string concatenation or arithmetic, default to string
196
- return "string";
197
- }
198
- return "string";
199
- }
200
- /**
201
- * Infers a template literal type from a TemplateLiteral AST node.
202
- * Returns a TypeScript template literal type string like `prefix-${string}`.
203
- */
204
- inferTemplateLiteralType(templateLiteral) {
205
- if (!templateLiteral || typeof templateLiteral !== "object" || templateLiteral.type !== "TemplateLiteral") {
206
- return "string";
207
- }
208
- const quasis = templateLiteral.quasis || [];
209
- const expressions = templateLiteral.expressions || [];
210
- // If there are no expressions, it's a static string
211
- if (expressions.length === 0) {
212
- if (quasis.length === 1) {
213
- const staticValue = quasis[0].value?.cooked || quasis[0].value?.raw || "";
214
- // Escape backticks and backslashes in the static string
215
- const escaped = staticValue.replace(/\\/g, "\\\\").replace(/`/g, "\\`");
216
- const templateLiteralType = `\`${escaped}\``;
217
- // Use the (template-literal & {}) trick to make the type more permissive
218
- return `(${templateLiteralType} & {})`;
219
- }
220
- return "string";
221
- }
222
- // Build the template literal type
223
- const parts = [];
224
- for (let i = 0; i < quasis.length; i++) {
225
- const quasi = quasis[i];
226
- const staticValue = quasi.value?.cooked || quasi.value?.raw || "";
227
- // Escape backticks and backslashes in static parts
228
- const escaped = staticValue.replace(/\\/g, "\\\\").replace(/`/g, "\\`");
229
- if (escaped) {
230
- parts.push(escaped);
231
- }
232
- // Add the expression type if there's a corresponding expression
233
- if (i < expressions.length) {
234
- const exprType = this.inferExpressionType(expressions[i]);
235
- parts.push(`\${${exprType}}`);
236
- }
237
- }
238
- // If we couldn't build a meaningful template literal type, fall back to string
239
- if (parts.length === 0) {
240
- return "string";
241
- }
242
- const templateLiteralType = `\`${parts.join("")}\``;
243
- // Use the (template-literal & {}) trick to make the type more permissive
244
- // This allows regular strings to be assigned while preserving template literal type for autocomplete
245
- return `(${templateLiteralType} & {})`;
246
- }
247
134
  sourceAtPos(start, end) {
248
135
  return this.source?.slice(start, end);
249
136
  }
@@ -949,9 +836,9 @@ class ComponentParser {
949
836
  // Otherwise, don't infer type, just preserve existing type annotation.
950
837
  }
951
838
  else if (init.type === "TemplateLiteral") {
952
- // Handle template literals - infer template literal type when possible
839
+ // Handle template literals - they always evaluate to strings
953
840
  value = this.sourceAtPos(init.start, init.end);
954
- type = this.inferTemplateLiteralType(init);
841
+ type = "string";
955
842
  }
956
843
  else {
957
844
  value = init.raw;
@@ -1142,9 +1029,9 @@ class ComponentParser {
1142
1029
  // Otherwise, don't infer type, just preserve existing type annotation.
1143
1030
  }
1144
1031
  else if (init.type === "TemplateLiteral") {
1145
- // Handle template literals - infer template literal type when possible
1032
+ // Handle template literals - they always evaluate to strings
1146
1033
  value = this.sourceAtPos(init.start, init.end);
1147
- type = this.inferTemplateLiteralType(init);
1034
+ type = "string";
1148
1035
  }
1149
1036
  else {
1150
1037
  value = init.raw;
@@ -68,6 +68,8 @@ function addCommentLine(value, returnValue) {
68
68
  return `* ${returnValue || value}\n`;
69
69
  }
70
70
  function genPropDef(def) {
71
+ // Collect existing prop names to avoid conflicts with snippet props
72
+ const existingPropNames = new Set(def.props.filter((prop) => !prop.isFunctionDeclaration && prop.kind !== "const").map((prop) => prop.name));
71
73
  const initial_props = def.props
72
74
  .filter((prop) => !prop.isFunctionDeclaration && prop.kind !== "const")
73
75
  .map((prop) => {
@@ -91,7 +93,18 @@ function genPropDef(def) {
91
93
  ${prop_comments.length > 0 ? `/**\n${prop_comments}*/` : EMPTY_STR}
92
94
  ${prop.name}${prop.isRequired ? "" : "?"}: ${prop_value};`;
93
95
  });
94
- const props = initial_props.join("\n");
96
+ // Generate snippet props for named slots (Svelte 5 compatibility)
97
+ // Skip default slots and slots that conflict with existing prop names
98
+ const snippet_props = (def.slots || [])
99
+ .filter((slot) => !slot.default && slot.name != null && !existingPropNames.has(slot.name))
100
+ .map((slot) => {
101
+ const slotName = slot.name;
102
+ const key = clampKey(slotName);
103
+ const description = slot.description ? `/** ${slot.description} */\n ` : "";
104
+ return `
105
+ ${description}${key}?: () => void;`;
106
+ });
107
+ const props = [...initial_props, ...snippet_props].join("\n");
95
108
  const props_name = `${def.moduleName}Props`;
96
109
  let prop_def = EMPTY_STR;
97
110
  const genericsName = def.generics ? `<${def.generics[0]}>` : "";
@@ -443,6 +456,7 @@ function writeTsDefinition(component) {
443
456
  rest_props,
444
457
  extends: _extends,
445
458
  generics,
459
+ slots,
446
460
  });
447
461
  const generic = generics ? `<${generics[1]}>` : "";
448
462
  const genericProps = generics ? `${props_name}<${generics[0]}>` : props_name;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sveld",
3
- "version": "0.25.3",
3
+ "version": "0.25.5",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Generate TypeScript definitions for your Svelte components.",
6
6
  "main": "./lib/index.js",