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 +2 -10
- package/lib/ComponentParser.d.ts +0 -10
- package/lib/ComponentParser.js +4 -117
- package/lib/writer/writer-ts-definitions-core.js +15 -1
- package/package.json +1 -1
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`
|
|
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:
|
|
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:
|
package/lib/ComponentParser.d.ts
CHANGED
|
@@ -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;
|
package/lib/ComponentParser.js
CHANGED
|
@@ -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 -
|
|
839
|
+
// Handle template literals - they always evaluate to strings
|
|
953
840
|
value = this.sourceAtPos(init.start, init.end);
|
|
954
|
-
type =
|
|
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 -
|
|
1032
|
+
// Handle template literals - they always evaluate to strings
|
|
1146
1033
|
value = this.sourceAtPos(init.start, init.end);
|
|
1147
|
-
type =
|
|
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
|
-
|
|
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;
|