sveld 0.24.3 → 0.24.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 +91 -0
- package/lib/ComponentParser.d.ts +1 -0
- package/lib/ComponentParser.js +34 -2
- package/lib/create-exports.js +29 -7
- package/lib/writer/writer-ts-definitions-core.js +28 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -131,6 +131,7 @@ export default class Button extends SvelteComponentTyped<
|
|
|
131
131
|
- [@extends](#extends)
|
|
132
132
|
- [@generics](#generics)
|
|
133
133
|
- [@component comments](#component-comments)
|
|
134
|
+
- [Accessor Props](#accessor-props)
|
|
134
135
|
- [Contributing](#contributing)
|
|
135
136
|
- [License](#license)
|
|
136
137
|
|
|
@@ -988,6 +989,96 @@ export default class Button extends SvelteComponentTyped<
|
|
|
988
989
|
> {}
|
|
989
990
|
```
|
|
990
991
|
|
|
992
|
+
### Accessor Props
|
|
993
|
+
|
|
994
|
+
Exported functions and consts become accessor props in generated TypeScript definitions. Use `@type` to document function signatures, or use `@param` and `@returns` (or `@return`) JSDoc tags for richer documentation.
|
|
995
|
+
|
|
996
|
+
Note that `@type` tag annotations take precedence over `@param`/`@returns` tags.
|
|
997
|
+
|
|
998
|
+
Signature:
|
|
999
|
+
|
|
1000
|
+
```js
|
|
1001
|
+
/**
|
|
1002
|
+
* Function description
|
|
1003
|
+
* @param {Type} paramName - Parameter description
|
|
1004
|
+
* @param {Type} [optionalParam] - Optional parameter
|
|
1005
|
+
* @returns {ReturnType} Return value description
|
|
1006
|
+
*/
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
Example:
|
|
1010
|
+
|
|
1011
|
+
```svelte
|
|
1012
|
+
<script>
|
|
1013
|
+
/**
|
|
1014
|
+
* @typedef {object} NotificationData
|
|
1015
|
+
* @property {string} [id] - Optional id for deduplication
|
|
1016
|
+
* @property {"error" | "info" | "success"} [kind]
|
|
1017
|
+
*/
|
|
1018
|
+
|
|
1019
|
+
/**
|
|
1020
|
+
* Add a notification to the queue.
|
|
1021
|
+
* @param {NotificationData} notification
|
|
1022
|
+
* @returns {string} The notification id
|
|
1023
|
+
*/
|
|
1024
|
+
export function add(notification) {
|
|
1025
|
+
const id = notification.id ?? "id";
|
|
1026
|
+
return id;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Remove a notification by id.
|
|
1031
|
+
* @param {string} id
|
|
1032
|
+
* @returns {boolean} True if the notification was found and removed
|
|
1033
|
+
*/
|
|
1034
|
+
export function remove(id) {
|
|
1035
|
+
return true;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/**
|
|
1039
|
+
* Get notification count.
|
|
1040
|
+
* @returns {number} The number of notifications
|
|
1041
|
+
*/
|
|
1042
|
+
export function getCount() {
|
|
1043
|
+
return 0;
|
|
1044
|
+
}
|
|
1045
|
+
</script>
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
Output:
|
|
1049
|
+
|
|
1050
|
+
```ts
|
|
1051
|
+
export type NotificationData = {
|
|
1052
|
+
/** Optional id for deduplication */ id?: string;
|
|
1053
|
+
kind?: "error" | "info" | "success";
|
|
1054
|
+
};
|
|
1055
|
+
|
|
1056
|
+
export type ComponentProps = Record<string, never>;
|
|
1057
|
+
|
|
1058
|
+
export default class Component extends SvelteComponentTyped<
|
|
1059
|
+
ComponentProps,
|
|
1060
|
+
Record<string, any>,
|
|
1061
|
+
Record<string, never>
|
|
1062
|
+
> {
|
|
1063
|
+
/**
|
|
1064
|
+
* Add a notification to the queue.
|
|
1065
|
+
*/
|
|
1066
|
+
add: (notification: NotificationData) => string;
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Remove a notification by id.
|
|
1070
|
+
*/
|
|
1071
|
+
remove: (id: string) => boolean;
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* Get notification count.
|
|
1075
|
+
*/
|
|
1076
|
+
getCount: () => number;
|
|
1077
|
+
}
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
When only `@param` tags are present without `@returns`, the return type defaults to `any`. When only `@returns` is present without `@param`, the function signature is `() => returnType`.
|
|
1081
|
+
|
|
991
1082
|
## Contributing
|
|
992
1083
|
|
|
993
1084
|
Refer to the [contributing guidelines](CONTRIBUTING.md).
|
package/lib/ComponentParser.d.ts
CHANGED
package/lib/ComponentParser.js
CHANGED
|
@@ -696,6 +696,7 @@ class ComponentParser {
|
|
|
696
696
|
isFunctionDeclaration = true;
|
|
697
697
|
}
|
|
698
698
|
let params;
|
|
699
|
+
let returnType;
|
|
699
700
|
if (node.leadingComments) {
|
|
700
701
|
const jsdoc_comment = ComponentParser.findJSDocComment(node.leadingComments);
|
|
701
702
|
if (jsdoc_comment) {
|
|
@@ -718,9 +719,23 @@ class ComponentParser {
|
|
|
718
719
|
optional: tag.optional || false,
|
|
719
720
|
}));
|
|
720
721
|
}
|
|
722
|
+
// Extract @returns/@return tag
|
|
723
|
+
const returnsTag = comment[0]?.tags.find((t) => t.tag === "returns" || t.tag === "return");
|
|
724
|
+
if (returnsTag)
|
|
725
|
+
returnType = this.aliasType(returnsTag.type);
|
|
721
726
|
// Build description from comment description and non-param/non-type tags
|
|
722
727
|
const commentDescription = ComponentParser.assignValue(comment[0]?.description?.trim());
|
|
723
|
-
const additionalTags = comment[0]?.tags.filter((tag) => ![
|
|
728
|
+
const additionalTags = comment[0]?.tags.filter((tag) => ![
|
|
729
|
+
"type",
|
|
730
|
+
"param",
|
|
731
|
+
"returns",
|
|
732
|
+
"return",
|
|
733
|
+
"extends",
|
|
734
|
+
"restProps",
|
|
735
|
+
"slot",
|
|
736
|
+
"event",
|
|
737
|
+
"typedef",
|
|
738
|
+
].includes(tag.tag)) ?? [];
|
|
724
739
|
if (commentDescription || additionalTags.length > 0) {
|
|
725
740
|
description = commentDescription || "";
|
|
726
741
|
for (const tag of additionalTags) {
|
|
@@ -739,6 +754,7 @@ class ComponentParser {
|
|
|
739
754
|
type,
|
|
740
755
|
value,
|
|
741
756
|
params,
|
|
757
|
+
returnType,
|
|
742
758
|
isFunction,
|
|
743
759
|
isFunctionDeclaration,
|
|
744
760
|
isRequired: false,
|
|
@@ -866,6 +882,7 @@ class ComponentParser {
|
|
|
866
882
|
isFunctionDeclaration = true;
|
|
867
883
|
}
|
|
868
884
|
let params;
|
|
885
|
+
let returnType;
|
|
869
886
|
if (node.leadingComments) {
|
|
870
887
|
const jsdoc_comment = ComponentParser.findJSDocComment(node.leadingComments);
|
|
871
888
|
if (jsdoc_comment) {
|
|
@@ -888,9 +905,23 @@ class ComponentParser {
|
|
|
888
905
|
optional: tag.optional || false,
|
|
889
906
|
}));
|
|
890
907
|
}
|
|
908
|
+
// Extract @returns/@return tag
|
|
909
|
+
const returnsTag = comment[0]?.tags.find((t) => t.tag === "returns" || t.tag === "return");
|
|
910
|
+
if (returnsTag)
|
|
911
|
+
returnType = this.aliasType(returnsTag.type);
|
|
891
912
|
// Build description from comment description and non-param/non-type tags
|
|
892
913
|
const commentDescription = ComponentParser.assignValue(comment[0]?.description?.trim());
|
|
893
|
-
const additional_tags = comment[0]?.tags.filter((tag) => ![
|
|
914
|
+
const additional_tags = comment[0]?.tags.filter((tag) => ![
|
|
915
|
+
"type",
|
|
916
|
+
"param",
|
|
917
|
+
"returns",
|
|
918
|
+
"return",
|
|
919
|
+
"extends",
|
|
920
|
+
"restProps",
|
|
921
|
+
"slot",
|
|
922
|
+
"event",
|
|
923
|
+
"typedef",
|
|
924
|
+
].includes(tag.tag)) ?? [];
|
|
894
925
|
if (commentDescription || additional_tags.length > 0) {
|
|
895
926
|
description = commentDescription || "";
|
|
896
927
|
for (const tag of additional_tags) {
|
|
@@ -909,6 +940,7 @@ class ComponentParser {
|
|
|
909
940
|
type,
|
|
910
941
|
value,
|
|
911
942
|
params,
|
|
943
|
+
returnType,
|
|
912
944
|
isFunction,
|
|
913
945
|
isFunctionDeclaration,
|
|
914
946
|
isRequired,
|
package/lib/create-exports.js
CHANGED
|
@@ -16,33 +16,55 @@ function createExports(parsed_exports) {
|
|
|
16
16
|
const exportStatements = [];
|
|
17
17
|
for (const [source, exports] of groupedBySource) {
|
|
18
18
|
const isSvelteFile = SVELTE_EXT_REGEX.test(source);
|
|
19
|
+
// Check if this source has both default and non-default exports (indicates module context exports)
|
|
20
|
+
// Exclude id="default" from this check, as that's just re-exporting the default as-is
|
|
21
|
+
const hasDefaultExport = exports.some(({ id, exportee }) => id !== "default" && exportee.default);
|
|
22
|
+
const hasNonDefaultExport = exports.some(({ exportee }) => !exportee.default);
|
|
23
|
+
const hasMixedExports = isSvelteFile && hasDefaultExport && hasNonDefaultExport;
|
|
19
24
|
// Separate named exports from default exports
|
|
20
25
|
const namedExports = [];
|
|
21
26
|
const defaultExports = [];
|
|
27
|
+
const defaultAsExports = []; // Track "default as X" exports
|
|
22
28
|
for (const { id, exportee } of exports) {
|
|
23
29
|
// If id is "default", always export as default
|
|
24
30
|
if (id === "default") {
|
|
25
31
|
defaultExports.push(`export { default } from "${source}";`);
|
|
26
32
|
continue;
|
|
27
33
|
}
|
|
28
|
-
// If exportee.default is true,
|
|
29
|
-
if (exportee.default
|
|
34
|
+
// If exportee.default is true, we're re-exporting a default export with a new name
|
|
35
|
+
if (exportee.default) {
|
|
30
36
|
if (exportee.mixed) {
|
|
31
37
|
defaultExports.push(`export { default as ${id} } from "${source}";`);
|
|
32
38
|
defaultExports.push(`export { default } from "${source}";`);
|
|
33
39
|
}
|
|
34
40
|
else {
|
|
35
|
-
|
|
41
|
+
// Use "default as X" so they can be grouped with named exports from same source
|
|
42
|
+
defaultAsExports.push(`default as ${id}`);
|
|
36
43
|
}
|
|
37
44
|
}
|
|
45
|
+
else if (isSvelteFile && !hasMixedExports) {
|
|
46
|
+
// For Svelte files without mixed exports, treat as component (re-export default)
|
|
47
|
+
defaultAsExports.push(`default as ${id}`);
|
|
48
|
+
}
|
|
38
49
|
else {
|
|
39
|
-
// It's a named export
|
|
50
|
+
// It's a named export (including named exports from Svelte module context)
|
|
40
51
|
namedExports.push(id);
|
|
41
52
|
}
|
|
42
53
|
}
|
|
43
|
-
//
|
|
44
|
-
if (namedExports.length > 0) {
|
|
45
|
-
|
|
54
|
+
// Combine default-as-renamed and named exports from same source (for Svelte module exports)
|
|
55
|
+
if (defaultAsExports.length > 0 && namedExports.length > 0) {
|
|
56
|
+
// Combine into single statement: export { default as X, namedExport } from "source"
|
|
57
|
+
exportStatements.push(`export { ${[...defaultAsExports, ...namedExports].join(", ")} } from "${source}";`);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// Generate grouped named exports
|
|
61
|
+
if (namedExports.length > 0) {
|
|
62
|
+
exportStatements.push(`export { ${namedExports.join(", ")} } from "${source}";`);
|
|
63
|
+
}
|
|
64
|
+
// Add default-as exports
|
|
65
|
+
if (defaultAsExports.length > 0) {
|
|
66
|
+
exportStatements.push(`export { ${defaultAsExports.join(", ")} } from "${source}";`);
|
|
67
|
+
}
|
|
46
68
|
}
|
|
47
69
|
// Add default exports (these cannot be grouped)
|
|
48
70
|
exportStatements.push(...defaultExports);
|
|
@@ -320,9 +320,35 @@ function genAccessors(def) {
|
|
|
320
320
|
const prop_comments = [addCommentLine(prop.description?.replace(NEWLINE_TO_COMMENT_REGEX, "\n* "))]
|
|
321
321
|
.filter(Boolean)
|
|
322
322
|
.join("");
|
|
323
|
+
// Determine the function signature
|
|
324
|
+
let functionType;
|
|
325
|
+
// Check if this is the default function type (would be overridden by @type or params/returns)
|
|
326
|
+
const isDefaultFunctionType = prop.type === "() => any";
|
|
327
|
+
// If @type tag provides a custom function signature (contains => and is not the default), use it (highest priority)
|
|
328
|
+
if (prop.type && FUNCTION_TYPE_REGEX.test(prop.type) && !isDefaultFunctionType) {
|
|
329
|
+
functionType = prop.type;
|
|
330
|
+
}
|
|
331
|
+
else if (prop.params && prop.params.length > 0) {
|
|
332
|
+
// Build signature from @param tags
|
|
333
|
+
const paramStrings = prop.params.map((param) => {
|
|
334
|
+
const optional = param.optional ? "?" : "";
|
|
335
|
+
return `${param.name}${optional}: ${param.type}`;
|
|
336
|
+
});
|
|
337
|
+
const paramsString = paramStrings.join(", ");
|
|
338
|
+
const returnType = prop.returnType || ANY_TYPE;
|
|
339
|
+
functionType = `(${paramsString}) => ${returnType}`;
|
|
340
|
+
}
|
|
341
|
+
else if (prop.returnType) {
|
|
342
|
+
// Only @returns is present without @param
|
|
343
|
+
functionType = `() => ${prop.returnType}`;
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
// Fall back to current prop.type
|
|
347
|
+
functionType = prop.type || ANY_TYPE;
|
|
348
|
+
}
|
|
323
349
|
return `
|
|
324
350
|
${prop_comments.length > 0 ? `/**\n${prop_comments}*/` : EMPTY_STR}
|
|
325
|
-
${prop.name}: ${
|
|
351
|
+
${prop.name}: ${functionType};`;
|
|
326
352
|
})
|
|
327
353
|
.join("\n");
|
|
328
354
|
}
|
|
@@ -356,7 +382,7 @@ function genModuleExports(def) {
|
|
|
356
382
|
}
|
|
357
383
|
else if (prop.kind === "const") {
|
|
358
384
|
// For const exports from script context="module", use declare const instead of type
|
|
359
|
-
type_def = `export declare const ${prop.name}: ${prop.type || ANY_TYPE}
|
|
385
|
+
type_def = `export declare const ${prop.name}: ${prop.type || ANY_TYPE};\n`;
|
|
360
386
|
}
|
|
361
387
|
return `
|
|
362
388
|
${prop_comments.length > 0 ? `/**\n${prop_comments}*/` : EMPTY_STR}
|