sveld 0.24.4 → 0.24.6

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
@@ -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).
@@ -19,6 +19,7 @@ interface ComponentProp {
19
19
  value?: string;
20
20
  description?: string;
21
21
  params?: ComponentPropParam[];
22
+ returnType?: string;
22
23
  isFunction: boolean;
23
24
  isFunctionDeclaration: boolean;
24
25
  isRequired: boolean;
@@ -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) => !["type", "param", "extends", "restProps", "slot", "event", "typedef"].includes(tag.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) => !["type", "param", "extends", "restProps", "slot", "event", "typedef"].includes(tag.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,
@@ -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}: ${prop.type};`;
351
+ ${prop.name}: ${functionType};`;
326
352
  })
327
353
  .join("\n");
328
354
  }
@@ -349,7 +375,29 @@ function genModuleExports(def) {
349
375
  .join("");
350
376
  let type_def = `export type ${prop.name} = ${prop.type || ANY_TYPE};`;
351
377
  const is_function = prop.type && FUNCTION_TYPE_REGEX.test(prop.type);
352
- if (is_function && prop.type) {
378
+ const isDefaultFunctionType = prop.type === "() => any";
379
+ if (is_function && prop.type && !isDefaultFunctionType) {
380
+ // @type tag provides a custom function signature (highest priority)
381
+ const [first, second, ...rest] = prop.type.split("=>");
382
+ const rest_type = rest.map((item) => `=>${item}`).join("");
383
+ type_def = `export declare function ${prop.name}${first}:${second}${rest_type};`;
384
+ }
385
+ else if (prop.params && prop.params.length > 0) {
386
+ // Build signature from @param tags
387
+ const paramStrings = prop.params.map((param) => {
388
+ const optional = param.optional ? "?" : "";
389
+ return `${param.name}${optional}: ${param.type}`;
390
+ });
391
+ const paramsString = paramStrings.join(", ");
392
+ const returnType = prop.returnType || ANY_TYPE;
393
+ type_def = `export declare function ${prop.name}(${paramsString}): ${returnType};`;
394
+ }
395
+ else if (prop.returnType) {
396
+ // Only @returns is present without @param
397
+ type_def = `export declare function ${prop.name}(): ${prop.returnType};`;
398
+ }
399
+ else if (is_function && prop.type) {
400
+ // Fall back to existing function type handling
353
401
  const [first, second, ...rest] = prop.type.split("=>");
354
402
  const rest_type = rest.map((item) => `=>${item}`).join("");
355
403
  type_def = `export declare function ${prop.name}${first}:${second}${rest_type};`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sveld",
3
- "version": "0.24.4",
3
+ "version": "0.24.6",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Generate TypeScript definitions for your Svelte components.",
6
6
  "main": "./lib/index.js",