From 06997cf569968da978f3eef2f00407d6489d0d1c Mon Sep 17 00:00:00 2001 From: metonym Date: Sat, 21 May 2022 13:11:32 -0700 Subject: [PATCH] breaking: required props should be inferred, remove `@required` tag (#91) * fix: uninitialized props should be required Fixes #22 * Run "yarn test:snapshot" * chore: prop is required if kind is `let` and init is `null` * breaking: remove `@required` tag * Run "yarn test:snapshot" * Run "yarn test:integration" --- README.md | 31 ------------- integration/carbon/COMPONENT_API.json | 4 +- integration/carbon/COMPONENT_INDEX.md | 44 +++++++++---------- .../types/Dropdown/Dropdown.svelte.d.ts | 3 +- src/ComponentParser.ts | 18 ++++---- .../snapshots/bind-this-multiple/output.d.ts | 4 +- .../snapshots/bind-this-multiple/output.json | 4 +- tests/snapshots/bind-this/output.d.ts | 2 +- tests/snapshots/bind-this/output.json | 2 +- tests/snapshots/infer-basic/output.d.ts | 2 +- tests/snapshots/infer-basic/output.json | 2 +- tests/snapshots/infer-with-types/output.d.ts | 2 +- tests/snapshots/infer-with-types/output.json | 2 +- tests/snapshots/required/input.svelte | 7 ++- tests/snapshots/required/output.d.ts | 18 ++++++-- tests/snapshots/required/output.json | 27 ++++++++++-- tests/snapshots/typed-props/output.d.ts | 2 +- tests/snapshots/typed-props/output.json | 2 +- 18 files changed, 90 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index f98bdd5..a379a1a 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,6 @@ export default class Button extends SvelteComponentTyped< - [API Reference](#api-reference) - [@type](#type) - [@typedef](#typedef) - - [@required](#required) - [@slot](#slot) - [@event](#event) - [@restProps](#restprops) @@ -351,36 +350,6 @@ export let author = {}; export let authors = []; ``` -### `@required` - -By default, all props are typed as optional. - -Use the `@required` tag to denote a component prop as required. - -Example: - -```js -/** - * This prop is required - * @required - */ -export let isRequired = true; -``` - -TypeScript output: - -```ts -export interface ComponentProps { - /** - * This prop is required - * @default true - */ - isRequired: boolean; -} -``` - -Because `@required` is non-standard JSDoc tag, it is omitted from the prop comment in the TypeScript definitions. - ### `@slot` Use the `@slot` tag for typing component slots. Note that `@slot` is a non-standard JSDoc tag. diff --git a/integration/carbon/COMPONENT_API.json b/integration/carbon/COMPONENT_API.json index 161bd00..e4298c0 100644 --- a/integration/carbon/COMPONENT_API.json +++ b/integration/carbon/COMPONENT_API.json @@ -2596,12 +2596,12 @@ { "name": "itemToString", "kind": "let", - "description": "Override the display of a dropdown item", + "description": "Override the display of a dropdown item\n@required ", "type": "(item: DropdownItem) => string", "value": "(item) => item.text || item.id", "isFunction": true, "isFunctionDeclaration": false, - "isRequired": true, + "isRequired": false, "constant": false, "reactive": false }, diff --git a/integration/carbon/COMPONENT_INDEX.md b/integration/carbon/COMPONENT_INDEX.md index 7d2edca..cd5770d 100644 --- a/integration/carbon/COMPONENT_INDEX.md +++ b/integration/carbon/COMPONENT_INDEX.md @@ -969,28 +969,28 @@ export interface DropdownItem { ### Props -| Prop name | Required | Kind | Reactive | Type | Default value | Description | -| :-------------- | :------- | :--------------- | :------- | ------------------------------------------- | ----------------------------------------------------- | --------------------------------------------- | -| ref | No | let | Yes | null | HTMLButtonElement | null | Obtain a reference to the button HTML element | -| inline | No | let | Yes | boolean | false | Set to `true` to use the inline variant | -| open | No | let | Yes | boolean | false | Set to `true` to open the dropdown | -| selectedIndex | No | let | Yes | number | -1 | Specify the selected item index | -| items | No | let | No | DropdownItem[] | [] | Set the dropdown items | -| itemToString | Yes | let | No | (item: DropdownItem) => string | (item) => item.text || item.id | Override the display of a dropdown item | -| type | No | let | No | "default" | "inline" | "default" | Specify the type of dropdown | -| size | No | let | No | "sm" | "lg" | "xl" | undefined | Specify the size of the dropdown field | -| light | No | let | No | boolean | false | Set to `true` to enable the light variant | -| disabled | No | let | No | boolean | false | Set to `true` to disable the dropdown | -| titleText | No | let | No | string | "" | Specify the title text | -| invalid | No | let | No | boolean | false | Set to `true` to indicate an invalid state | -| invalidText | No | let | No | string | "" | Specify the invalid state text | -| warn | No | let | No | boolean | false | Set to `true` to indicate an warning state | -| warnText | No | let | No | string | "" | Specify the warning state text | -| helperText | No | let | No | string | "" | Specify the helper text | -| label | No | let | No | string | undefined | Specify the list box label | -| translateWithId | No | let | No | (id: any) => string | undefined | Override the default translation ids | -| id | No | let | No | string | "ccs-" + Math.random().toString(36) | Set an id for the list box component | -| name | No | let | No | string | undefined | Specify a name attribute for the list box | +| Prop name | Required | Kind | Reactive | Type | Default value | Description | +| :-------------- | :------- | :--------------- | :------- | ------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------ | +| ref | No | let | Yes | null | HTMLButtonElement | null | Obtain a reference to the button HTML element | +| inline | No | let | Yes | boolean | false | Set to `true` to use the inline variant | +| open | No | let | Yes | boolean | false | Set to `true` to open the dropdown | +| selectedIndex | No | let | Yes | number | -1 | Specify the selected item index | +| items | No | let | No | DropdownItem[] | [] | Set the dropdown items | +| itemToString | No | let | No | (item: DropdownItem) => string | (item) => item.text || item.id | Override the display of a dropdown item
@required | +| type | No | let | No | "default" | "inline" | "default" | Specify the type of dropdown | +| size | No | let | No | "sm" | "lg" | "xl" | undefined | Specify the size of the dropdown field | +| light | No | let | No | boolean | false | Set to `true` to enable the light variant | +| disabled | No | let | No | boolean | false | Set to `true` to disable the dropdown | +| titleText | No | let | No | string | "" | Specify the title text | +| invalid | No | let | No | boolean | false | Set to `true` to indicate an invalid state | +| invalidText | No | let | No | string | "" | Specify the invalid state text | +| warn | No | let | No | boolean | false | Set to `true` to indicate an warning state | +| warnText | No | let | No | string | "" | Specify the warning state text | +| helperText | No | let | No | string | "" | Specify the helper text | +| label | No | let | No | string | undefined | Specify the list box label | +| translateWithId | No | let | No | (id: any) => string | undefined | Override the default translation ids | +| id | No | let | No | string | "ccs-" + Math.random().toString(36) | Set an id for the list box component | +| name | No | let | No | string | undefined | Specify a name attribute for the list box | ### Slots diff --git a/integration/carbon/types/Dropdown/Dropdown.svelte.d.ts b/integration/carbon/types/Dropdown/Dropdown.svelte.d.ts index 1295f78..5e42e45 100644 --- a/integration/carbon/types/Dropdown/Dropdown.svelte.d.ts +++ b/integration/carbon/types/Dropdown/Dropdown.svelte.d.ts @@ -20,9 +20,10 @@ export interface DropdownProps /** * Override the display of a dropdown item + * @required * @default (item) => item.text || item.id */ - itemToString: (item: DropdownItem) => string; + itemToString?: (item: DropdownItem) => string; /** * Specify the selected item index diff --git a/src/ComponentParser.ts b/src/ComponentParser.ts index 6e9c9fa..d3a84d2 100644 --- a/src/ComponentParser.ts +++ b/src/ComponentParser.ts @@ -458,7 +458,7 @@ export default class ComponentParser { let description: undefined | string = undefined; let isFunction = false; let isFunctionDeclaration = false; - let isRequired = false; + let isRequired = kind === "let" && init == null; if (init != null) { if ( @@ -505,21 +505,19 @@ export default class ComponentParser { if (tag?.tag === "type") type = this.aliasType(tag.type); description = ComponentParser.assignValue(comment[0]?.description?.trim()); - const additional_tags = comment[0]?.tags.filter( - (tag) => !["type", "extends", "restProps", "slot", "event", "typedef"].includes(tag.tag) - ) ?? []; + const additional_tags = + comment[0]?.tags.filter( + (tag) => !["type", "extends", "restProps", "slot", "event", "typedef"].includes(tag.tag) + ) ?? []; if (additional_tags.length > 0 && description === undefined) { description = ""; } additional_tags.forEach((tag) => { - isRequired = tag.tag === "required"; - if (!isRequired) { - description += `${description ? "\n" : ""}@${tag.tag} ${tag.name}${ - tag.description ? ` ${tag.description}` : "" - }`; - } + description += `${description ? "\n" : ""}@${tag.tag} ${tag.name}${ + tag.description ? ` ${tag.description}` : "" + }`; }); } diff --git a/tests/snapshots/bind-this-multiple/output.d.ts b/tests/snapshots/bind-this-multiple/output.d.ts index 4c479b5..183b25d 100644 --- a/tests/snapshots/bind-this-multiple/output.d.ts +++ b/tests/snapshots/bind-this-multiple/output.d.ts @@ -5,12 +5,12 @@ export interface InputProps { /** * @default undefined */ - ref?: null | HTMLButtonElement | HTMLHeadingElement; + ref: null | HTMLButtonElement | HTMLHeadingElement; /** * @default undefined */ - ref2?: null | HTMLDivElement; + ref2: null | HTMLDivElement; /** * @default false diff --git a/tests/snapshots/bind-this-multiple/output.json b/tests/snapshots/bind-this-multiple/output.json index 433f93b..695c795 100644 --- a/tests/snapshots/bind-this-multiple/output.json +++ b/tests/snapshots/bind-this-multiple/output.json @@ -6,7 +6,7 @@ "type": "null | HTMLButtonElement | HTMLHeadingElement", "isFunction": false, "isFunctionDeclaration": false, - "isRequired": false, + "isRequired": true, "constant": false, "reactive": true }, @@ -16,7 +16,7 @@ "type": "null | HTMLDivElement", "isFunction": false, "isFunctionDeclaration": false, - "isRequired": false, + "isRequired": true, "constant": false, "reactive": true }, diff --git a/tests/snapshots/bind-this/output.d.ts b/tests/snapshots/bind-this/output.d.ts index f8441b1..b1ffa3b 100644 --- a/tests/snapshots/bind-this/output.d.ts +++ b/tests/snapshots/bind-this/output.d.ts @@ -5,7 +5,7 @@ export interface InputProps { /** * @default undefined */ - ref?: null | HTMLButtonElement; + ref: null | HTMLButtonElement; } export default class Input extends SvelteComponentTyped {} diff --git a/tests/snapshots/bind-this/output.json b/tests/snapshots/bind-this/output.json index fc7428c..da94e2e 100644 --- a/tests/snapshots/bind-this/output.json +++ b/tests/snapshots/bind-this/output.json @@ -6,7 +6,7 @@ "type": "null | HTMLButtonElement", "isFunction": false, "isFunctionDeclaration": false, - "isRequired": false, + "isRequired": true, "constant": false, "reactive": true } diff --git a/tests/snapshots/infer-basic/output.d.ts b/tests/snapshots/infer-basic/output.d.ts index 5ef8350..3017152 100644 --- a/tests/snapshots/infer-basic/output.d.ts +++ b/tests/snapshots/infer-basic/output.d.ts @@ -20,7 +20,7 @@ export interface InputProps { /** * @default undefined */ - name?: undefined; + name: undefined; /** * @default "" + Math.random().toString(36) diff --git a/tests/snapshots/infer-basic/output.json b/tests/snapshots/infer-basic/output.json index 91d2569..a250bb9 100644 --- a/tests/snapshots/infer-basic/output.json +++ b/tests/snapshots/infer-basic/output.json @@ -37,7 +37,7 @@ "kind": "let", "isFunction": false, "isFunctionDeclaration": false, - "isRequired": false, + "isRequired": true, "constant": false, "reactive": false }, diff --git a/tests/snapshots/infer-with-types/output.d.ts b/tests/snapshots/infer-with-types/output.d.ts index 6620700..4cb568f 100644 --- a/tests/snapshots/infer-with-types/output.d.ts +++ b/tests/snapshots/infer-with-types/output.d.ts @@ -15,7 +15,7 @@ export interface InputProps { /** * @default undefined */ - name?: string; + name: string; /** * @default "" + Math.random().toString(36) diff --git a/tests/snapshots/infer-with-types/output.json b/tests/snapshots/infer-with-types/output.json index 4668466..b835c8f 100644 --- a/tests/snapshots/infer-with-types/output.json +++ b/tests/snapshots/infer-with-types/output.json @@ -28,7 +28,7 @@ "type": "string", "isFunction": false, "isFunctionDeclaration": false, - "isRequired": false, + "isRequired": true, "constant": false, "reactive": false }, diff --git a/tests/snapshots/required/input.svelte b/tests/snapshots/required/input.svelte index e08186e..e93b65a 100644 --- a/tests/snapshots/required/input.svelte +++ b/tests/snapshots/required/input.svelte @@ -10,6 +10,11 @@ * @type {boolean | string} */ export let prop1 = true; + + export let prop2; + + /** @type {boolean} */ + export let prop3; - + diff --git a/tests/snapshots/required/output.d.ts b/tests/snapshots/required/output.d.ts index ffebd3d..5370fbf 100644 --- a/tests/snapshots/required/output.d.ts +++ b/tests/snapshots/required/output.d.ts @@ -3,19 +3,31 @@ import type { SvelteComponentTyped } from "svelte"; export interface InputProps { /** + * @required * @default true */ - prop: boolean; + prop?: boolean; /** * This is a comment. + * @required * @default true */ - prop1: boolean | string; + prop1?: boolean | string; + + /** + * @default undefined + */ + prop2: undefined; + + /** + * @default undefined + */ + prop3: boolean; } export default class Input extends SvelteComponentTyped< InputProps, {}, - { default: { prop: boolean; prop1: boolean | string } } + { default: { prop: boolean; prop1: boolean | string; prop2: any; prop3: boolean } } > {} diff --git a/tests/snapshots/required/output.json b/tests/snapshots/required/output.json index 28e6ea9..dc58e82 100644 --- a/tests/snapshots/required/output.json +++ b/tests/snapshots/required/output.json @@ -3,23 +3,42 @@ { "name": "prop", "kind": "let", - "description": "", + "description": "@required ", "type": "boolean", "value": "true", "isFunction": false, "isFunctionDeclaration": false, - "isRequired": true, + "isRequired": false, "constant": false, "reactive": false }, { "name": "prop1", "kind": "let", - "description": "This is a comment.", + "description": "This is a comment.\n@required ", "type": "boolean | string", "value": "true", "isFunction": false, "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "prop2", + "kind": "let", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": true, + "constant": false, + "reactive": false + }, + { + "name": "prop3", + "kind": "let", + "type": "boolean", + "isFunction": false, + "isFunctionDeclaration": false, "isRequired": true, "constant": false, "reactive": false @@ -30,7 +49,7 @@ { "name": "__default__", "default": true, - "slot_props": "{ prop: boolean, prop1: boolean | string }" + "slot_props": "{ prop: boolean, prop1: boolean | string, prop2: any, prop3: boolean }" } ], "events": [], diff --git a/tests/snapshots/typed-props/output.d.ts b/tests/snapshots/typed-props/output.d.ts index 100f278..e913ae4 100644 --- a/tests/snapshots/typed-props/output.d.ts +++ b/tests/snapshots/typed-props/output.d.ts @@ -7,7 +7,7 @@ export interface InputProps { * prop1 description 2 * @default undefined */ - prop1?: string; + prop1: string; /** * prop2 description 1 diff --git a/tests/snapshots/typed-props/output.json b/tests/snapshots/typed-props/output.json index 12e593f..fd616d7 100644 --- a/tests/snapshots/typed-props/output.json +++ b/tests/snapshots/typed-props/output.json @@ -7,7 +7,7 @@ "type": "string", "isFunction": false, "isFunctionDeclaration": false, - "isRequired": false, + "isRequired": true, "constant": false, "reactive": false },