diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7cc1209b..d4a622c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,10 +31,7 @@ jobs: run: yarn prepack - name: Run unit tests - run: yarn test:unit - - - name: Run snapshot tests - run: yarn test:snapshot + run: yarn test # Only trigger deploy if previous steps pass and branch is main - name: Deploy docs diff --git a/package.json b/package.json index 5f3a1938..343effee 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,7 @@ "main": "./lib/index.js", "types": "./lib/index.d.ts", "scripts": { - "test:unit": "vitest -r tests", - "test:snapshot": "node tests/test-snapshot", + "test": "vitest -r tests", "test:integration": "node tests/test-integration", "format": "prettier --write \"{src,tests,playground}/**/*.{js,ts,svelte,md}\" --ignore-path \".gitignore\"", "prepack": "tsc" diff --git a/src/ComponentParser.ts b/src/ComponentParser.ts index 3db14a06..d7e8427f 100644 --- a/src/ComponentParser.ts +++ b/src/ComponentParser.ts @@ -1,11 +1,11 @@ // TODO: upgrading to Svelte 4 shows a lot of TS errors. Ignore for now but resolve. // @ts-nocheck -import { compile, walk, parse } from "svelte/compiler"; import * as commentParser from "comment-parser"; +import type { VariableDeclaration } from "estree"; +import { Node } from "estree-walker"; +import { compile, parse, walk } from "svelte/compiler"; import { Ast, TemplateNode, Var } from "svelte/types/compiler/interfaces"; import { getElementByTag } from "./element-tag-map"; -import { Node } from "estree-walker"; -import type { VariableDeclaration } from "estree"; interface CompiledSvelteCode { vars: Var[]; @@ -134,7 +134,7 @@ export default class ComponentParser { this.options = options; } - private static mapToArray(map: Map) { + private static mapToArray(map: Map) { return Array.from(map, ([key, value]) => value); } @@ -370,7 +370,7 @@ export default class ComponentParser { init.type === "ArrayExpression" || init.type === "ArrowFunctionExpression" ) { - value = this.sourceAtPos(init.start, init.end)?.replace(/\n/g, " "); + value = this.sourceAtPos(init.start, init.end)?.replace(/[\r\n]+/g, " "); type = value; isFunction = init.type === "ArrowFunctionExpression"; @@ -391,7 +391,7 @@ export default class ComponentParser { } if (declaration_type === "FunctionDeclaration") { - value = "() => " + this.sourceAtPos(body.start, body.end)?.replace(/\n/g, " "); + value = "() => " + this.sourceAtPos(body.start, body.end)?.replace(/[\r\n]+/g, " "); type = "() => any"; kind = "function"; isFunction = true; @@ -506,7 +506,7 @@ export default class ComponentParser { init.type === "ArrayExpression" || init.type === "ArrowFunctionExpression" ) { - value = this.sourceAtPos(init.start, init.end)?.replace(/\n/g, " "); + value = this.sourceAtPos(init.start, init.end)?.replace(/[\r\n]+/g, " "); type = value; isFunction = init.type === "ArrowFunctionExpression"; @@ -527,7 +527,7 @@ export default class ComponentParser { } if (declaration_type === "FunctionDeclaration") { - value = "() => " + this.sourceAtPos(body.start, body.end)?.replace(/\n/g, " "); + value = "() => " + this.sourceAtPos(body.start, body.end)?.replace(/[\r\n]+/g, " "); type = "() => any"; kind = "function"; isFunction = true; @@ -582,7 +582,7 @@ export default class ComponentParser { let data: string = node?.data?.trim() ?? ""; if (/^@component/.test(data)) { - this.componentComment = data.replace(/^@component/, ""); + this.componentComment = data.replace(/^@component/, "").replace(/\r/g, ""); } } diff --git a/tests/__snapshots__/fixtures.test.ts.snap b/tests/__snapshots__/fixtures.test.ts.snap new file mode 100644 index 00000000..56a76c0a --- /dev/null +++ b/tests/__snapshots__/fixtures.test.ts.snap @@ -0,0 +1,1730 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`fixtures > 'anchor-props/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{}" + } + ], + "events": [], + "typedefs": [], + "rest_props": { + "type": "Element", + "name": "a" + } +}" +`; + +exports[`fixtures > 'anchor-props/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; +import type { SvelteHTMLElements } from "svelte/elements"; + +type RestProps = SvelteHTMLElements["a"]; + +export interface AnchorPropsProps extends RestProps { + [key: \`data-\${string}\`]: any; +} + +export default class AnchorProps extends SvelteComponentTyped, { default: {} }> {} +" +`; + +exports[`fixtures > 'bind-this/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "ref", + "kind": "let", + "type": "null | HTMLButtonElement", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": true, + "constant": false, + "reactive": true + } + ], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{}" + } + ], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'bind-this/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface BindThisProps { + /** + * @default undefined + */ + ref: null | HTMLButtonElement; +} + +export default class BindThis extends SvelteComponentTyped, { default: {} }> {} +" +`; + +exports[`fixtures > 'bind-this-multiple/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "ref", + "kind": "let", + "type": "null | HTMLButtonElement | HTMLHeadingElement", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": true, + "constant": false, + "reactive": true + }, + { + "name": "ref2", + "kind": "let", + "type": "null | HTMLDivElement", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": true, + "constant": false, + "reactive": true + }, + { + "name": "propBool", + "kind": "let", + "type": "boolean", + "value": "false", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + } + ], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{}" + } + ], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'bind-this-multiple/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface BindThisMultipleProps { + /** + * @default undefined + */ + ref: null | HTMLButtonElement | HTMLHeadingElement; + + /** + * @default undefined + */ + ref2: null | HTMLDivElement; + + /** + * @default false + */ + propBool?: boolean; +} + +export default class BindThisMultiple extends SvelteComponentTyped< + BindThisMultipleProps, + Record, + { default: {} } +> {} +" +`; + +exports[`fixtures > 'component-comment-multi/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{}" + } + ], + "events": [], + "typedefs": [], + "componentComment": "\\n@example\\n
\\n Component comment\\n
" +}" +`; + +exports[`fixtures > 'component-comment-multi/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface ComponentCommentMultiProps {} + +/** + * @example + *
+ * Component comment + *
+ */ +export default class ComponentCommentMulti extends SvelteComponentTyped< + ComponentCommentMultiProps, + Record, + { default: {} } +> {} +" +`; + +exports[`fixtures > 'component-comment-single/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{}" + } + ], + "events": [], + "typedefs": [], + "componentComment": " Component comment" +}" +`; + +exports[`fixtures > 'component-comment-single/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface ComponentCommentSingleProps {} + +/** Component comment */ +export default class ComponentCommentSingle extends SvelteComponentTyped< + ComponentCommentSingleProps, + Record, + { default: {} } +> {} +" +`; + +exports[`fixtures > 'context-module/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "a", + "kind": "const", + "type": "string", + "value": "\\"\\"", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": true, + "reactive": false + } + ], + "moduleExports": [ + { + "name": "bool", + "kind": "const", + "type": "string", + "value": "\\"\\"", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": true, + "reactive": false + }, + { + "name": "a", + "kind": "const", + "type": "{ b: 4 }", + "value": "{ b: 4 }", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": true, + "reactive": false + }, + { + "name": "e", + "kind": "const", + "description": "Description for e", + "type": "{ [key: string]: any; }", + "value": "{ b: 4 }", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": true, + "reactive": false + }, + { + "name": "log", + "kind": "function", + "description": "Log something", + "type": "(message: string) => void", + "value": "() => { console.log(message); }", + "isFunction": true, + "isFunctionDeclaration": true, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "b", + "kind": "const", + "type": "() => {}", + "value": "() => {}", + "isFunction": true, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": true, + "reactive": false + }, + { + "name": "b2", + "kind": "const", + "type": "() => () => {}", + "value": "() => () => {}", + "isFunction": true, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": true, + "reactive": false + }, + { + "name": "b3", + "kind": "const", + "type": "() => () => false", + "value": "() => () => false", + "isFunction": true, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": true, + "reactive": false + } + ], + "slots": [], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'context-module/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export type bool = string; + +export type a = { b: 4 }; + +/** + * Description for e + */ +export type e = { [key: string]: any }; + +/** + * Log something + */ +export declare function log(message: string): void; + +export declare function b(): {}; + +export declare function b2(): () => {}; + +export declare function b3(): () => false; + +export interface ContextModuleProps {} + +export default class ContextModule extends SvelteComponentTyped, {}> { + a: string; +} +" +`; + +exports[`fixtures > 'dispatched-events/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{}" + } + ], + "events": [ + { + "type": "dispatched", + "name": "hover" + }, + { + "type": "dispatched", + "name": "destroy", + "detail": "null" + }, + { + "type": "dispatched", + "name": "destroy--component", + "detail": "null" + }, + { + "type": "dispatched", + "name": "destroy:component", + "detail": "null" + } + ], + "typedefs": [] +}" +`; + +exports[`fixtures > 'dispatched-events/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface DispatchedEventsProps {} + +export default class DispatchedEvents extends SvelteComponentTyped< + DispatchedEventsProps, + { + hover: CustomEvent; + destroy: CustomEvent; + ["destroy--component"]: CustomEvent; + ["destroy:component"]: CustomEvent; + }, + { default: {} } +> {} +" +`; + +exports[`fixtures > 'dispatched-events-dynamic/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [], + "events": [ + { + "type": "dispatched", + "name": "KEY", + "detail": "{key: string;}" + } + ], + "typedefs": [] +}" +`; + +exports[`fixtures > 'dispatched-events-dynamic/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface DispatchedEventsDynamicProps {} + +export default class DispatchedEventsDynamic extends SvelteComponentTyped< + DispatchedEventsDynamicProps, + { KEY: CustomEvent<{ key: string }> }, + {} +> {} +" +`; + +exports[`fixtures > 'dispatched-events-typed/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{}" + } + ], + "events": [ + { + "type": "dispatched", + "name": "hover", + "detail": "{ h1: boolean; }", + "description": "Fired on mouseover." + }, + { + "type": "dispatched", + "name": "destroy", + "detail": "null" + } + ], + "typedefs": [] +}" +`; + +exports[`fixtures > 'dispatched-events-typed/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface DispatchedEventsTypedProps {} + +export default class DispatchedEventsTyped extends SvelteComponentTyped< + DispatchedEventsTypedProps, + { + /** Fired on mouseover. */ hover: CustomEvent<{ h1: boolean }>; + destroy: CustomEvent; + }, + { default: {} } +> {} +" +`; + +exports[`fixtures > 'empty-export/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'empty-export/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface EmptyExportProps {} + +export default class EmptyExport extends SvelteComponentTyped, {}> {} +" +`; + +exports[`fixtures > 'forwarded-events/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{}" + } + ], + "events": [ + { + "type": "forwarded", + "name": "click", + "element": "button" + }, + { + "type": "forwarded", + "name": "focus", + "element": "button" + }, + { + "type": "forwarded", + "name": "blur", + "element": "button" + }, + { + "type": "forwarded", + "name": "mouseover", + "element": "h1" + } + ], + "typedefs": [] +}" +`; + +exports[`fixtures > 'forwarded-events/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface ForwardedEventsProps {} + +export default class ForwardedEvents extends SvelteComponentTyped< + ForwardedEventsProps, + { + click: WindowEventMap["click"]; + focus: WindowEventMap["focus"]; + blur: WindowEventMap["blur"]; + mouseover: WindowEventMap["mouseover"]; + }, + { default: {} } +> {} +" +`; + +exports[`fixtures > 'function-declaration/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "fnA", + "kind": "let", + "type": "() => {}", + "value": "() => {}", + "isFunction": true, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "fnB", + "kind": "const", + "type": "() => {}", + "value": "() => {}", + "isFunction": true, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": true, + "reactive": false + }, + { + "name": "add", + "kind": "function", + "type": "() => any", + "value": "() => { return a + b; }", + "isFunction": true, + "isFunctionDeclaration": true, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "multiply", + "kind": "function", + "description": "Multiplies two numbers", + "type": "(a: number, b: number) => number", + "value": "() => { return a * b; }", + "isFunction": true, + "isFunctionDeclaration": true, + "isRequired": false, + "constant": false, + "reactive": false + } + ], + "moduleExports": [], + "slots": [], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'function-declaration/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface FunctionDeclarationProps { + /** + * @default () => {} + */ + fnA?: () => {}; +} + +export default class FunctionDeclaration extends SvelteComponentTyped< + FunctionDeclarationProps, + Record, + {} +> { + fnB: () => {}; + + add: () => any; + + /** + * Multiplies two numbers + */ + multiply: (a: number, b: number) => number; +} +" +`; + +exports[`fixtures > 'infer-basic/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "ref", + "kind": "let", + "value": "null", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "propBool", + "kind": "let", + "type": "boolean", + "value": "true", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": true + }, + { + "name": "propString", + "kind": "let", + "type": "string", + "value": "\\"\\"", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "name", + "kind": "let", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": true, + "constant": false, + "reactive": false + }, + { + "name": "id", + "kind": "let", + "type": "string", + "value": "\\"\\" + Math.random().toString(36)", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "propConst", + "kind": "const", + "type": "{ [\\"1\\"]: true }", + "value": "{ [\\"1\\"]: true }", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": true, + "reactive": false + }, + { + "name": "fn", + "kind": "function", + "type": "() => any", + "value": "() => { localBool = !localBool; }", + "isFunction": true, + "isFunctionDeclaration": true, + "isRequired": false, + "constant": false, + "reactive": false + } + ], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "fallback": "{name}", + "slot_props": "{}" + } + ], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'infer-basic/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface InferBasicProps { + /** + * @default null + */ + ref?: undefined; + + /** + * @default true + */ + propBool?: boolean; + + /** + * @default "" + */ + propString?: string; + + /** + * @default undefined + */ + name: undefined; + + /** + * @default "" + Math.random().toString(36) + */ + id?: string; +} + +export default class InferBasic extends SvelteComponentTyped, { default: {} }> { + propConst: { ["1"]: true }; + + fn: () => any; +} +" +`; + +exports[`fixtures > 'infer-with-types/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "propBool", + "kind": "let", + "type": "boolean", + "value": "true", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": true + }, + { + "name": "propString", + "kind": "let", + "type": "string", + "value": "\\"\\"", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "name", + "kind": "let", + "type": "string", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": true, + "constant": false, + "reactive": false + }, + { + "name": "id", + "kind": "let", + "type": "string", + "value": "\\"\\" + Math.random().toString(36)", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "propConst", + "kind": "const", + "type": "{ [key: string]: boolean; }", + "value": "{ [\\"1\\"]: true }", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": true, + "reactive": false + }, + { + "name": "fn", + "kind": "function", + "type": "() => any", + "value": "() => { localBool = !localBool; }", + "isFunction": true, + "isFunctionDeclaration": true, + "isRequired": false, + "constant": false, + "reactive": false + } + ], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "fallback": "{name}", + "slot_props": "{}" + } + ], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'infer-with-types/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface InferWithTypesProps { + /** + * @default true + */ + propBool?: boolean; + + /** + * @default "" + */ + propString?: string; + + /** + * @default undefined + */ + name: string; + + /** + * @default "" + Math.random().toString(36) + */ + id?: string; +} + +export default class InferWithTypes extends SvelteComponentTyped< + InferWithTypesProps, + Record, + { default: {} } +> { + propConst: { [key: string]: boolean }; + + fn: () => any; +} +" +`; + +exports[`fixtures > 'input-events/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [], + "events": [ + { + "type": "forwarded", + "name": "input", + "element": "input" + }, + { + "type": "forwarded", + "name": "change", + "element": "input" + }, + { + "type": "forwarded", + "name": "paste", + "element": "input" + } + ], + "typedefs": [] +}" +`; + +exports[`fixtures > 'input-events/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface InputEventsProps {} + +export default class InputEvents extends SvelteComponentTyped< + InputEventsProps, + { + input: WindowEventMap["input"]; + change: WindowEventMap["change"]; + paste: DocumentAndElementEventHandlersEventMap["paste"]; + }, + {} +> {} +" +`; + +exports[`fixtures > 'mixed-events/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [], + "events": [ + { + "type": "dispatched", + "name": "custom-focus", + "detail": "FocusEvent | number" + }, + { + "type": "dispatched", + "name": "blur", + "detail": "FocusEvent | CustomEvent" + } + ], + "typedefs": [] +}" +`; + +exports[`fixtures > 'mixed-events/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface MixedEventsProps {} + +export default class MixedEvents extends SvelteComponentTyped< + MixedEventsProps, + { ["custom-focus"]: CustomEvent; blur: FocusEvent | CustomEvent }, + {} +> {} +" +`; + +exports[`fixtures > 'prop-comments/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "prop", + "kind": "let", + "description": "This is a comment.\\n@see https://github.com/\\n@deprecated this prop will be removed in the next major release.", + "type": "boolean | string", + "value": "true", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "prop1", + "kind": "let", + "description": "@see https://github.com/", + "type": "boolean", + "value": "true", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "prop2", + "kind": "let", + "description": "This is a comment.", + "type": "boolean | string", + "value": "true", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + } + ], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{ prop: boolean | string, prop1: boolean, prop2: boolean | string }" + } + ], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'prop-comments/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface PropCommentsProps { + /** + * This is a comment. + * @see https://github.com/ + * @deprecated this prop will be removed in the next major release. + * @default true + */ + prop?: boolean | string; + + /** + * @see https://github.com/ + * @default true + */ + prop1?: boolean; + + /** + * This is a comment. + * @default true + */ + prop2?: boolean | string; +} + +export default class PropComments extends SvelteComponentTyped< + PropCommentsProps, + Record, + { default: { prop: boolean | string; prop1: boolean; prop2: boolean | string } } +> {} +" +`; + +exports[`fixtures > 'renamed-props/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "class", + "kind": "let", + "description": "Just your average CSS class string.", + "type": "string|null", + "value": "\\"test\\"", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + } + ], + "moduleExports": [], + "slots": [], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'renamed-props/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface RenamedPropsProps { + /** + * Just your average CSS class string. + * @default "test" + */ + class?: string | null; +} + +export default class RenamedProps extends SvelteComponentTyped, {}> {} +" +`; + +exports[`fixtures > 'required/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "prop", + "kind": "let", + "description": "@required ", + "type": "boolean", + "value": "true", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "prop1", + "kind": "let", + "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 + } + ], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{ prop: boolean, prop1: boolean | string, prop2: any, prop3: boolean }" + } + ], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'required/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface RequiredProps { + /** + * @required + * @default true + */ + prop?: boolean; + + /** + * This is a comment. + * @required + * @default true + */ + prop1?: boolean | string; + + /** + * @default undefined + */ + prop2: undefined; + + /** + * @default undefined + */ + prop3: boolean; +} + +export default class Required extends SvelteComponentTyped< + RequiredProps, + Record, + { default: { prop: boolean; prop1: boolean | string; prop2: any; prop3: boolean } } +> {} +" +`; + +exports[`fixtures > 'rest-props/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [], + "events": [], + "typedefs": [], + "rest_props": { + "type": "Element", + "name": "h1" + } +}" +`; + +exports[`fixtures > 'rest-props/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; +import type { SvelteHTMLElements } from "svelte/elements"; + +type RestProps = SvelteHTMLElements["h1"]; + +export interface RestPropsProps extends RestProps { + [key: \`data-\${string}\`]: any; +} + +export default class RestProps extends SvelteComponentTyped, {}> {} +" +`; + +exports[`fixtures > 'rest-props-multiple/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "edit", + "kind": "let", + "type": "boolean", + "value": "false", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "heading", + "kind": "let", + "type": "boolean", + "value": "false", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + } + ], + "moduleExports": [], + "slots": [], + "events": [], + "typedefs": [], + "rest_props": { + "type": "Element", + "name": "h1 | span" + } +}" +`; + +exports[`fixtures > 'rest-props-multiple/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; +import type { SvelteHTMLElements } from "svelte/elements"; + +type RestProps = SvelteHTMLElements["h1"] & SvelteHTMLElements["span"]; + +export interface RestPropsMultipleProps extends RestProps { + /** + * @default false + */ + edit?: boolean; + + /** + * @default false + */ + heading?: boolean; + + [key: \`data-\${string}\`]: any; +} + +export default class RestPropsMultiple extends SvelteComponentTyped, {}> {} +" +`; + +exports[`fixtures > 'slots-named/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "text", + "kind": "let", + "type": "string", + "value": "\\"\\"", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + } + ], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "fallback": "Default text", + "slot_props": "{}" + }, + { + "name": "bold heading", + "default": false, + "fallback": "{text}", + "slot_props": "{ text: string }" + }, + { + "name": "subheading", + "default": false, + "fallback": "{text}", + "slot_props": "{ text: string }" + }, + { + "name": "text", + "default": false, + "fallback": "{text}", + "slot_props": "{ text: string }" + } + ], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'slots-named/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface SlotsNamedProps { + /** + * @default "" + */ + text?: string; +} + +export default class SlotsNamed extends SvelteComponentTyped< + SlotsNamedProps, + Record, + { default: {}; ["bold heading"]: { text: string }; subheading: { text: string }; text: { text: string } } +> {} +" +`; + +exports[`fixtures > 'slots-spread/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "fallback": "Default text", + "slot_props": "{}" + }, + { + "name": "text", + "default": false, + "slot_props": "{}" + } + ], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'slots-spread/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface SlotsSpreadProps {} + +export default class SlotsSpread extends SvelteComponentTyped< + SlotsSpreadProps, + Record, + { default: {}; text: {} } +> {} +" +`; + +exports[`fixtures > 'slots-spread-typed/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "fallback": "Default text", + "slot_props": "{ a: number; }" + }, + { + "name": "text", + "default": false, + "slot_props": "{ a: number; }" + } + ], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'slots-spread-typed/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface SlotsSpreadTypedProps {} + +export default class SlotsSpreadTyped extends SvelteComponentTyped< + SlotsSpreadTypedProps, + Record, + { default: { a: number }; text: { a: number } } +> {} +" +`; + +exports[`fixtures > 'svg-props/input.svelte' 1`] = ` +"{ + "props": [], + "moduleExports": [], + "slots": [], + "events": [], + "typedefs": [], + "rest_props": { + "type": "Element", + "name": "svg" + } +}" +`; + +exports[`fixtures > 'svg-props/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; +import type { SvelteHTMLElements } from "svelte/elements"; + +type RestProps = SvelteHTMLElements["svg"]; + +export interface SvgPropsProps extends RestProps { + [key: \`data-\${string}\`]: any; +} + +export default class SvgProps extends SvelteComponentTyped, {}> {} +" +`; + +exports[`fixtures > 'typed-props/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "prop1", + "kind": "let", + "description": "prop1 description 1\\nprop1 description 2", + "type": "string", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": true, + "constant": false, + "reactive": false + }, + { + "name": "prop2", + "kind": "let", + "description": "prop2 description 1\\nprop2 description 2", + "value": "null", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "prop3", + "kind": "let", + "type": "4 | '4'", + "value": "4", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "prop4", + "kind": "let", + "type": "\\"red\\" | \\"blue\\"", + "value": "\\"red\\"", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + } + ], + "moduleExports": [], + "slots": [], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'typed-props/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface TypedPropsProps { + /** + * prop1 description 1 + * prop1 description 2 + * @default undefined + */ + prop1: string; + + /** + * prop2 description 1 + * prop2 description 2 + * @default null + */ + prop2?: undefined; + + /** + * @default 4 + */ + prop3?: 4 | "4"; + + /** + * @default "red" + */ + prop4?: "red" | "blue"; +} + +export default class TypedProps extends SvelteComponentTyped, {}> {} +" +`; + +exports[`fixtures > 'typed-slots/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "prop", + "kind": "let", + "type": "number", + "value": "0", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + } + ], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{ prop: number; doubled: number; }" + }, + { + "name": "description", + "default": false, + "slot_props": "{ props: { class?: string; } }", + "description": "description" + } + ], + "events": [], + "typedefs": [] +}" +`; + +exports[`fixtures > 'typed-slots/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface TypedSlotsProps { + /** + * @default 0 + */ + prop?: number; +} + +export default class TypedSlots extends SvelteComponentTyped< + TypedSlotsProps, + Record, + { + default: { prop: number; doubled: number }; + /** description */ + description: { props: { class?: string } }; + } +> {} +" +`; + +exports[`fixtures > 'typedef/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "id", + "kind": "let", + "type": "string", + "value": "\\"id-\\" + Math.random().toString(36)", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "prop1", + "kind": "let", + "type": "MyTypedef", + "value": "{ [\\"1\\"]: true }", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + } + ], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{ prop1: MyTypedef }" + } + ], + "events": [], + "typedefs": [ + { + "type": "{ [key: string]: boolean; }", + "name": "MyTypedef", + "ts": "interface MyTypedef { [key: string]: boolean; }" + } + ] +}" +`; + +exports[`fixtures > 'typedef/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface MyTypedef { + [key: string]: boolean; +} + +export interface TypedefProps { + /** + * @default "id-" + Math.random().toString(36) + */ + id?: string; + + /** + * @default { ["1"]: true } + */ + prop1?: MyTypedef; +} + +export default class Typedef extends SvelteComponentTyped< + TypedefProps, + Record, + { default: { prop1: MyTypedef } } +> {} +" +`; + +exports[`fixtures > 'typedefs/input.svelte' 1`] = ` +"{ + "props": [ + { + "name": "prop1", + "kind": "let", + "type": "MyTypedef", + "value": "{ [\\"1\\"]: true }", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "prop2", + "kind": "let", + "type": "MyTypedefArray", + "value": "[]", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + } + ], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{ prop1: MyTypedef, prop2: MyTypedefArray }" + } + ], + "events": [], + "typedefs": [ + { + "type": "{ [key: string]: boolean; }", + "name": "MyTypedef", + "ts": "interface MyTypedef { [key: string]: boolean; }" + }, + { + "type": "MyTypedef[]", + "name": "MyTypedefArray", + "ts": "type MyTypedefArray = MyTypedef[]" + } + ] +}" +`; + +exports[`fixtures > 'typedefs/input.svelte' 2`] = ` +"import type { SvelteComponentTyped } from "svelte"; + +export interface MyTypedef { + [key: string]: boolean; +} + +export type MyTypedefArray = MyTypedef[]; + +export interface TypedefsProps { + /** + * @default { ["1"]: true } + */ + prop1?: MyTypedef; + + /** + * @default [] + */ + prop2?: MyTypedefArray; +} + +export default class Typedefs extends SvelteComponentTyped< + TypedefsProps, + Record, + { default: { prop1: MyTypedef; prop2: MyTypedefArray } } +> {} +" +`; diff --git a/tests/fixtures.test.ts b/tests/fixtures.test.ts new file mode 100644 index 00000000..d3dc2987 --- /dev/null +++ b/tests/fixtures.test.ts @@ -0,0 +1,45 @@ +import fg from "fast-glob"; +import fsp from "node:fs/promises"; +import path from "node:path"; +import { describe, expect, test } from "vitest"; +import ComponentParser from "../src/ComponentParser"; +import Writer from "../src/writer/Writer"; +import { writeTsDefinition } from "../src/writer/writer-ts-definitions"; + +const folder = path.join(process.cwd(), "tests", "fixtures"); + +const svelteFiles = fg.sync(["**/*.svelte"], { cwd: folder }); +const fixtures = await Promise.all( + svelteFiles.map(async (file) => { + return { + path: file, + source: await fsp.readFile(path.join(folder, file), "utf-8"), + }; + }) +); + +describe("fixtures", async () => { + const parser = new ComponentParser(); + const writer = new Writer({ parser: "typescript", printWidth: 120 }); + + test.each(fixtures)("$path", async (fixture) => { + const { dir } = path.parse(fixture.path); + const moduleName = dir + .split("-") + .map((s) => s.charAt(0).toUpperCase() + s.slice(1)) + .join(""); + const metadata = { moduleName, filePath: fixture.path }; + const parsed_component = parser.parseSvelteComponent(fixture.source, metadata); + + const api_json = JSON.stringify(parsed_component, null, 2); + const api_ts = writer.format(writeTsDefinition({ ...metadata, ...parsed_component })); + + // Snapshot the output; if the test fails, output has changed. + expect(api_json).toMatchSnapshot(); + expect(api_ts).toMatchSnapshot(); + + // Still write to disk to manually assert types as needed. + await fsp.writeFile(path.join(folder, "output.json"), api_json); + await fsp.writeFile(path.join(folder, "output.d.ts"), api_ts); + }); +}); diff --git a/tests/snapshots/anchor-props/input.svelte b/tests/fixtures/anchor-props/input.svelte similarity index 100% rename from tests/snapshots/anchor-props/input.svelte rename to tests/fixtures/anchor-props/input.svelte diff --git a/tests/snapshots/anchor-props/output.d.ts b/tests/fixtures/anchor-props/output.d.ts similarity index 100% rename from tests/snapshots/anchor-props/output.d.ts rename to tests/fixtures/anchor-props/output.d.ts diff --git a/tests/snapshots/anchor-props/output.json b/tests/fixtures/anchor-props/output.json similarity index 100% rename from tests/snapshots/anchor-props/output.json rename to tests/fixtures/anchor-props/output.json diff --git a/tests/snapshots/bind-this-multiple/input.svelte b/tests/fixtures/bind-this-multiple/input.svelte similarity index 100% rename from tests/snapshots/bind-this-multiple/input.svelte rename to tests/fixtures/bind-this-multiple/input.svelte diff --git a/tests/snapshots/bind-this-multiple/output.d.ts b/tests/fixtures/bind-this-multiple/output.d.ts similarity index 100% rename from tests/snapshots/bind-this-multiple/output.d.ts rename to tests/fixtures/bind-this-multiple/output.d.ts diff --git a/tests/snapshots/bind-this-multiple/output.json b/tests/fixtures/bind-this-multiple/output.json similarity index 100% rename from tests/snapshots/bind-this-multiple/output.json rename to tests/fixtures/bind-this-multiple/output.json diff --git a/tests/snapshots/bind-this/input.svelte b/tests/fixtures/bind-this/input.svelte similarity index 100% rename from tests/snapshots/bind-this/input.svelte rename to tests/fixtures/bind-this/input.svelte diff --git a/tests/snapshots/bind-this/output.d.ts b/tests/fixtures/bind-this/output.d.ts similarity index 100% rename from tests/snapshots/bind-this/output.d.ts rename to tests/fixtures/bind-this/output.d.ts diff --git a/tests/snapshots/bind-this/output.json b/tests/fixtures/bind-this/output.json similarity index 100% rename from tests/snapshots/bind-this/output.json rename to tests/fixtures/bind-this/output.json diff --git a/tests/snapshots/component-comment-multi/input.svelte b/tests/fixtures/component-comment-multi/input.svelte similarity index 100% rename from tests/snapshots/component-comment-multi/input.svelte rename to tests/fixtures/component-comment-multi/input.svelte diff --git a/tests/snapshots/component-comment-multi/output.d.ts b/tests/fixtures/component-comment-multi/output.d.ts similarity index 100% rename from tests/snapshots/component-comment-multi/output.d.ts rename to tests/fixtures/component-comment-multi/output.d.ts diff --git a/tests/snapshots/component-comment-multi/output.json b/tests/fixtures/component-comment-multi/output.json similarity index 100% rename from tests/snapshots/component-comment-multi/output.json rename to tests/fixtures/component-comment-multi/output.json diff --git a/tests/snapshots/component-comment-single/input.svelte b/tests/fixtures/component-comment-single/input.svelte similarity index 100% rename from tests/snapshots/component-comment-single/input.svelte rename to tests/fixtures/component-comment-single/input.svelte diff --git a/tests/snapshots/component-comment-single/output.d.ts b/tests/fixtures/component-comment-single/output.d.ts similarity index 100% rename from tests/snapshots/component-comment-single/output.d.ts rename to tests/fixtures/component-comment-single/output.d.ts diff --git a/tests/snapshots/component-comment-single/output.json b/tests/fixtures/component-comment-single/output.json similarity index 100% rename from tests/snapshots/component-comment-single/output.json rename to tests/fixtures/component-comment-single/output.json diff --git a/tests/snapshots/context-module/input.svelte b/tests/fixtures/context-module/input.svelte similarity index 100% rename from tests/snapshots/context-module/input.svelte rename to tests/fixtures/context-module/input.svelte diff --git a/tests/snapshots/context-module/output.d.ts b/tests/fixtures/context-module/output.d.ts similarity index 100% rename from tests/snapshots/context-module/output.d.ts rename to tests/fixtures/context-module/output.d.ts diff --git a/tests/snapshots/context-module/output.json b/tests/fixtures/context-module/output.json similarity index 100% rename from tests/snapshots/context-module/output.json rename to tests/fixtures/context-module/output.json diff --git a/tests/snapshots/context-module/types.ts b/tests/fixtures/context-module/types.ts similarity index 100% rename from tests/snapshots/context-module/types.ts rename to tests/fixtures/context-module/types.ts diff --git a/tests/snapshots/dispatched-events-dynamic/input.svelte b/tests/fixtures/dispatched-events-dynamic/input.svelte similarity index 100% rename from tests/snapshots/dispatched-events-dynamic/input.svelte rename to tests/fixtures/dispatched-events-dynamic/input.svelte diff --git a/tests/snapshots/dispatched-events-dynamic/output.d.ts b/tests/fixtures/dispatched-events-dynamic/output.d.ts similarity index 100% rename from tests/snapshots/dispatched-events-dynamic/output.d.ts rename to tests/fixtures/dispatched-events-dynamic/output.d.ts diff --git a/tests/snapshots/dispatched-events-dynamic/output.json b/tests/fixtures/dispatched-events-dynamic/output.json similarity index 100% rename from tests/snapshots/dispatched-events-dynamic/output.json rename to tests/fixtures/dispatched-events-dynamic/output.json diff --git a/tests/snapshots/dispatched-events-typed/input.svelte b/tests/fixtures/dispatched-events-typed/input.svelte similarity index 100% rename from tests/snapshots/dispatched-events-typed/input.svelte rename to tests/fixtures/dispatched-events-typed/input.svelte diff --git a/tests/snapshots/dispatched-events-typed/output.d.ts b/tests/fixtures/dispatched-events-typed/output.d.ts similarity index 100% rename from tests/snapshots/dispatched-events-typed/output.d.ts rename to tests/fixtures/dispatched-events-typed/output.d.ts diff --git a/tests/snapshots/dispatched-events-typed/output.json b/tests/fixtures/dispatched-events-typed/output.json similarity index 100% rename from tests/snapshots/dispatched-events-typed/output.json rename to tests/fixtures/dispatched-events-typed/output.json diff --git a/tests/snapshots/dispatched-events/input.svelte b/tests/fixtures/dispatched-events/input.svelte similarity index 100% rename from tests/snapshots/dispatched-events/input.svelte rename to tests/fixtures/dispatched-events/input.svelte diff --git a/tests/snapshots/dispatched-events/output.d.ts b/tests/fixtures/dispatched-events/output.d.ts similarity index 100% rename from tests/snapshots/dispatched-events/output.d.ts rename to tests/fixtures/dispatched-events/output.d.ts diff --git a/tests/snapshots/dispatched-events/output.json b/tests/fixtures/dispatched-events/output.json similarity index 100% rename from tests/snapshots/dispatched-events/output.json rename to tests/fixtures/dispatched-events/output.json diff --git a/tests/snapshots/empty-export/input.svelte b/tests/fixtures/empty-export/input.svelte similarity index 100% rename from tests/snapshots/empty-export/input.svelte rename to tests/fixtures/empty-export/input.svelte diff --git a/tests/snapshots/empty-export/output.d.ts b/tests/fixtures/empty-export/output.d.ts similarity index 100% rename from tests/snapshots/empty-export/output.d.ts rename to tests/fixtures/empty-export/output.d.ts diff --git a/tests/snapshots/empty-export/output.json b/tests/fixtures/empty-export/output.json similarity index 100% rename from tests/snapshots/empty-export/output.json rename to tests/fixtures/empty-export/output.json diff --git a/tests/snapshots/forwarded-events/input.svelte b/tests/fixtures/forwarded-events/input.svelte similarity index 100% rename from tests/snapshots/forwarded-events/input.svelte rename to tests/fixtures/forwarded-events/input.svelte diff --git a/tests/snapshots/forwarded-events/output.d.ts b/tests/fixtures/forwarded-events/output.d.ts similarity index 100% rename from tests/snapshots/forwarded-events/output.d.ts rename to tests/fixtures/forwarded-events/output.d.ts diff --git a/tests/snapshots/forwarded-events/output.json b/tests/fixtures/forwarded-events/output.json similarity index 100% rename from tests/snapshots/forwarded-events/output.json rename to tests/fixtures/forwarded-events/output.json diff --git a/tests/snapshots/function-declaration/input.svelte b/tests/fixtures/function-declaration/input.svelte similarity index 100% rename from tests/snapshots/function-declaration/input.svelte rename to tests/fixtures/function-declaration/input.svelte diff --git a/tests/snapshots/function-declaration/output.d.ts b/tests/fixtures/function-declaration/output.d.ts similarity index 100% rename from tests/snapshots/function-declaration/output.d.ts rename to tests/fixtures/function-declaration/output.d.ts diff --git a/tests/snapshots/function-declaration/output.json b/tests/fixtures/function-declaration/output.json similarity index 100% rename from tests/snapshots/function-declaration/output.json rename to tests/fixtures/function-declaration/output.json diff --git a/tests/snapshots/infer-basic/input.svelte b/tests/fixtures/infer-basic/input.svelte similarity index 100% rename from tests/snapshots/infer-basic/input.svelte rename to tests/fixtures/infer-basic/input.svelte diff --git a/tests/snapshots/infer-basic/output.d.ts b/tests/fixtures/infer-basic/output.d.ts similarity index 100% rename from tests/snapshots/infer-basic/output.d.ts rename to tests/fixtures/infer-basic/output.d.ts diff --git a/tests/snapshots/infer-basic/output.json b/tests/fixtures/infer-basic/output.json similarity index 100% rename from tests/snapshots/infer-basic/output.json rename to tests/fixtures/infer-basic/output.json diff --git a/tests/snapshots/infer-with-types/input.svelte b/tests/fixtures/infer-with-types/input.svelte similarity index 100% rename from tests/snapshots/infer-with-types/input.svelte rename to tests/fixtures/infer-with-types/input.svelte diff --git a/tests/snapshots/infer-with-types/output.d.ts b/tests/fixtures/infer-with-types/output.d.ts similarity index 100% rename from tests/snapshots/infer-with-types/output.d.ts rename to tests/fixtures/infer-with-types/output.d.ts diff --git a/tests/snapshots/infer-with-types/output.json b/tests/fixtures/infer-with-types/output.json similarity index 100% rename from tests/snapshots/infer-with-types/output.json rename to tests/fixtures/infer-with-types/output.json diff --git a/tests/snapshots/input-events/input.svelte b/tests/fixtures/input-events/input.svelte similarity index 100% rename from tests/snapshots/input-events/input.svelte rename to tests/fixtures/input-events/input.svelte diff --git a/tests/snapshots/input-events/output.d.ts b/tests/fixtures/input-events/output.d.ts similarity index 100% rename from tests/snapshots/input-events/output.d.ts rename to tests/fixtures/input-events/output.d.ts diff --git a/tests/snapshots/input-events/output.json b/tests/fixtures/input-events/output.json similarity index 100% rename from tests/snapshots/input-events/output.json rename to tests/fixtures/input-events/output.json diff --git a/tests/snapshots/mixed-events/input.svelte b/tests/fixtures/mixed-events/input.svelte similarity index 100% rename from tests/snapshots/mixed-events/input.svelte rename to tests/fixtures/mixed-events/input.svelte diff --git a/tests/snapshots/mixed-events/output.d.ts b/tests/fixtures/mixed-events/output.d.ts similarity index 100% rename from tests/snapshots/mixed-events/output.d.ts rename to tests/fixtures/mixed-events/output.d.ts diff --git a/tests/snapshots/mixed-events/output.json b/tests/fixtures/mixed-events/output.json similarity index 100% rename from tests/snapshots/mixed-events/output.json rename to tests/fixtures/mixed-events/output.json diff --git a/tests/fixtures/output.d.ts b/tests/fixtures/output.d.ts new file mode 100644 index 00000000..a5a72899 --- /dev/null +++ b/tests/fixtures/output.d.ts @@ -0,0 +1,25 @@ +import type { SvelteComponentTyped } from "svelte"; + +export interface MyTypedef { + [key: string]: boolean; +} + +export type MyTypedefArray = MyTypedef[]; + +export interface TypedefsProps { + /** + * @default { ["1"]: true } + */ + prop1?: MyTypedef; + + /** + * @default [] + */ + prop2?: MyTypedefArray; +} + +export default class Typedefs extends SvelteComponentTyped< + TypedefsProps, + Record, + { default: { prop1: MyTypedef; prop2: MyTypedefArray } } +> {} diff --git a/tests/snapshots/typedefs/output.json b/tests/fixtures/output.json similarity index 100% rename from tests/snapshots/typedefs/output.json rename to tests/fixtures/output.json diff --git a/tests/snapshots/prop-comments/input.svelte b/tests/fixtures/prop-comments/input.svelte similarity index 100% rename from tests/snapshots/prop-comments/input.svelte rename to tests/fixtures/prop-comments/input.svelte diff --git a/tests/snapshots/prop-comments/output.d.ts b/tests/fixtures/prop-comments/output.d.ts similarity index 100% rename from tests/snapshots/prop-comments/output.d.ts rename to tests/fixtures/prop-comments/output.d.ts diff --git a/tests/snapshots/prop-comments/output.json b/tests/fixtures/prop-comments/output.json similarity index 100% rename from tests/snapshots/prop-comments/output.json rename to tests/fixtures/prop-comments/output.json diff --git a/tests/snapshots/renamed-props/input.svelte b/tests/fixtures/renamed-props/input.svelte similarity index 100% rename from tests/snapshots/renamed-props/input.svelte rename to tests/fixtures/renamed-props/input.svelte diff --git a/tests/snapshots/renamed-props/output.d.ts b/tests/fixtures/renamed-props/output.d.ts similarity index 100% rename from tests/snapshots/renamed-props/output.d.ts rename to tests/fixtures/renamed-props/output.d.ts diff --git a/tests/snapshots/renamed-props/output.json b/tests/fixtures/renamed-props/output.json similarity index 100% rename from tests/snapshots/renamed-props/output.json rename to tests/fixtures/renamed-props/output.json diff --git a/tests/snapshots/required/input.svelte b/tests/fixtures/required/input.svelte similarity index 100% rename from tests/snapshots/required/input.svelte rename to tests/fixtures/required/input.svelte diff --git a/tests/snapshots/required/output.d.ts b/tests/fixtures/required/output.d.ts similarity index 100% rename from tests/snapshots/required/output.d.ts rename to tests/fixtures/required/output.d.ts diff --git a/tests/snapshots/required/output.json b/tests/fixtures/required/output.json similarity index 100% rename from tests/snapshots/required/output.json rename to tests/fixtures/required/output.json diff --git a/tests/snapshots/rest-props-multiple/input.svelte b/tests/fixtures/rest-props-multiple/input.svelte similarity index 100% rename from tests/snapshots/rest-props-multiple/input.svelte rename to tests/fixtures/rest-props-multiple/input.svelte diff --git a/tests/snapshots/rest-props-multiple/output.d.ts b/tests/fixtures/rest-props-multiple/output.d.ts similarity index 100% rename from tests/snapshots/rest-props-multiple/output.d.ts rename to tests/fixtures/rest-props-multiple/output.d.ts diff --git a/tests/snapshots/rest-props-multiple/output.json b/tests/fixtures/rest-props-multiple/output.json similarity index 100% rename from tests/snapshots/rest-props-multiple/output.json rename to tests/fixtures/rest-props-multiple/output.json diff --git a/tests/snapshots/rest-props/input.svelte b/tests/fixtures/rest-props/input.svelte similarity index 100% rename from tests/snapshots/rest-props/input.svelte rename to tests/fixtures/rest-props/input.svelte diff --git a/tests/snapshots/rest-props/output.d.ts b/tests/fixtures/rest-props/output.d.ts similarity index 100% rename from tests/snapshots/rest-props/output.d.ts rename to tests/fixtures/rest-props/output.d.ts diff --git a/tests/snapshots/rest-props/output.json b/tests/fixtures/rest-props/output.json similarity index 100% rename from tests/snapshots/rest-props/output.json rename to tests/fixtures/rest-props/output.json diff --git a/tests/snapshots/slots-named/input.svelte b/tests/fixtures/slots-named/input.svelte similarity index 100% rename from tests/snapshots/slots-named/input.svelte rename to tests/fixtures/slots-named/input.svelte diff --git a/tests/snapshots/slots-named/output.d.ts b/tests/fixtures/slots-named/output.d.ts similarity index 100% rename from tests/snapshots/slots-named/output.d.ts rename to tests/fixtures/slots-named/output.d.ts diff --git a/tests/snapshots/slots-named/output.json b/tests/fixtures/slots-named/output.json similarity index 100% rename from tests/snapshots/slots-named/output.json rename to tests/fixtures/slots-named/output.json diff --git a/tests/snapshots/slots-spread-typed/input.svelte b/tests/fixtures/slots-spread-typed/input.svelte similarity index 100% rename from tests/snapshots/slots-spread-typed/input.svelte rename to tests/fixtures/slots-spread-typed/input.svelte diff --git a/tests/snapshots/slots-spread-typed/output.d.ts b/tests/fixtures/slots-spread-typed/output.d.ts similarity index 100% rename from tests/snapshots/slots-spread-typed/output.d.ts rename to tests/fixtures/slots-spread-typed/output.d.ts diff --git a/tests/snapshots/slots-spread-typed/output.json b/tests/fixtures/slots-spread-typed/output.json similarity index 100% rename from tests/snapshots/slots-spread-typed/output.json rename to tests/fixtures/slots-spread-typed/output.json diff --git a/tests/snapshots/slots-spread/input.svelte b/tests/fixtures/slots-spread/input.svelte similarity index 100% rename from tests/snapshots/slots-spread/input.svelte rename to tests/fixtures/slots-spread/input.svelte diff --git a/tests/snapshots/slots-spread/output.d.ts b/tests/fixtures/slots-spread/output.d.ts similarity index 100% rename from tests/snapshots/slots-spread/output.d.ts rename to tests/fixtures/slots-spread/output.d.ts diff --git a/tests/snapshots/slots-spread/output.json b/tests/fixtures/slots-spread/output.json similarity index 100% rename from tests/snapshots/slots-spread/output.json rename to tests/fixtures/slots-spread/output.json diff --git a/tests/snapshots/svg-props/input.svelte b/tests/fixtures/svg-props/input.svelte similarity index 100% rename from tests/snapshots/svg-props/input.svelte rename to tests/fixtures/svg-props/input.svelte diff --git a/tests/snapshots/svg-props/output.d.ts b/tests/fixtures/svg-props/output.d.ts similarity index 100% rename from tests/snapshots/svg-props/output.d.ts rename to tests/fixtures/svg-props/output.d.ts diff --git a/tests/snapshots/svg-props/output.json b/tests/fixtures/svg-props/output.json similarity index 100% rename from tests/snapshots/svg-props/output.json rename to tests/fixtures/svg-props/output.json diff --git a/tests/snapshots/typed-props/input.svelte b/tests/fixtures/typed-props/input.svelte similarity index 100% rename from tests/snapshots/typed-props/input.svelte rename to tests/fixtures/typed-props/input.svelte diff --git a/tests/snapshots/typed-props/output.d.ts b/tests/fixtures/typed-props/output.d.ts similarity index 100% rename from tests/snapshots/typed-props/output.d.ts rename to tests/fixtures/typed-props/output.d.ts diff --git a/tests/snapshots/typed-props/output.json b/tests/fixtures/typed-props/output.json similarity index 100% rename from tests/snapshots/typed-props/output.json rename to tests/fixtures/typed-props/output.json diff --git a/tests/snapshots/typed-slots/input.svelte b/tests/fixtures/typed-slots/input.svelte similarity index 100% rename from tests/snapshots/typed-slots/input.svelte rename to tests/fixtures/typed-slots/input.svelte diff --git a/tests/snapshots/typed-slots/output.d.ts b/tests/fixtures/typed-slots/output.d.ts similarity index 100% rename from tests/snapshots/typed-slots/output.d.ts rename to tests/fixtures/typed-slots/output.d.ts diff --git a/tests/snapshots/typed-slots/output.json b/tests/fixtures/typed-slots/output.json similarity index 100% rename from tests/snapshots/typed-slots/output.json rename to tests/fixtures/typed-slots/output.json diff --git a/tests/snapshots/typedef/input.svelte b/tests/fixtures/typedef/input.svelte similarity index 100% rename from tests/snapshots/typedef/input.svelte rename to tests/fixtures/typedef/input.svelte diff --git a/tests/snapshots/typedef/output.d.ts b/tests/fixtures/typedef/output.d.ts similarity index 100% rename from tests/snapshots/typedef/output.d.ts rename to tests/fixtures/typedef/output.d.ts diff --git a/tests/snapshots/typedef/output.json b/tests/fixtures/typedef/output.json similarity index 100% rename from tests/snapshots/typedef/output.json rename to tests/fixtures/typedef/output.json diff --git a/tests/snapshots/typedefs/input.svelte b/tests/fixtures/typedefs/input.svelte similarity index 100% rename from tests/snapshots/typedefs/input.svelte rename to tests/fixtures/typedefs/input.svelte diff --git a/tests/snapshots/typedefs/output.d.ts b/tests/fixtures/typedefs/output.d.ts similarity index 100% rename from tests/snapshots/typedefs/output.d.ts rename to tests/fixtures/typedefs/output.d.ts diff --git a/tests/fixtures/typedefs/output.json b/tests/fixtures/typedefs/output.json new file mode 100644 index 00000000..651f6a98 --- /dev/null +++ b/tests/fixtures/typedefs/output.json @@ -0,0 +1,47 @@ +{ + "props": [ + { + "name": "prop1", + "kind": "let", + "type": "MyTypedef", + "value": "{ [\"1\"]: true }", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + }, + { + "name": "prop2", + "kind": "let", + "type": "MyTypedefArray", + "value": "[]", + "isFunction": false, + "isFunctionDeclaration": false, + "isRequired": false, + "constant": false, + "reactive": false + } + ], + "moduleExports": [], + "slots": [ + { + "name": "__default__", + "default": true, + "slot_props": "{ prop1: MyTypedef, prop2: MyTypedefArray }" + } + ], + "events": [], + "typedefs": [ + { + "type": "{ [key: string]: boolean; }", + "name": "MyTypedef", + "ts": "interface MyTypedef { [key: string]: boolean; }" + }, + { + "type": "MyTypedef[]", + "name": "MyTypedefArray", + "ts": "type MyTypedefArray = MyTypedef[]" + } + ] +} \ No newline at end of file diff --git a/tests/test-snapshot.js b/tests/test-snapshot.js deleted file mode 100644 index e5b736f9..00000000 --- a/tests/test-snapshot.js +++ /dev/null @@ -1,38 +0,0 @@ -const fsp = require("fs/promises"); -const path = require("path"); -const ComponentParser = require("../lib/ComponentParser").default; -const { writeTsDefinition } = require("../lib/writer/writer-ts-definitions"); -const Writer = require("../lib/writer/Writer").default; - -const writer = new Writer({ parser: "typescript", printWidth: 120 }); -const SNAPSHOTS_FOLDER = path.join(process.cwd(), "tests", "snapshots"); -const INPUT_FILE = "input.svelte"; -const OUTPUT_FILE = "output.json"; -const TS_DEF_FILE = "output.d.ts"; - -(async () => { - const input_files = await fsp.readdir(SNAPSHOTS_FOLDER); - const parser = new ComponentParser({ verbose: true }); - - const promises = input_files.map(async (file) => { - const input_path = path.join(SNAPSHOTS_FOLDER, file, INPUT_FILE); - const output_path = path.join(SNAPSHOTS_FOLDER, file, OUTPUT_FILE); - const ts_def_path = path.join(SNAPSHOTS_FOLDER, file, TS_DEF_FILE); - const source = await fsp.readFile(input_path, "utf-8"); - const parsed_component = parser.parseSvelteComponent(source, { - moduleName: "Input", - filePath: input_path, - }); - const component_api = { - moduleName: "Input", - filePath: input_path, - ...parsed_component, - }; - - await fsp.writeFile(output_path, JSON.stringify(parsed_component, null, 2)); - await writer.write(ts_def_path, writeTsDefinition(component_api)); - }); - - await Promise.all(promises); - parser.cleanup(); -})();