Repository: sveltejs/language-tools (ts)

Issue:
Incorrect type generation for components without script tag
<!-- Before you submit a bug, please make sure that:
- you have searched and found no existing open issue with the problem at hand
- you don't have   "files.associations": {"*.svelte": "html"  }   inside your VSCode settings (if you can't remember ever doing that, you don't have that)
- you are using Svelte for Vscode (NOT the old "Svelte" by James Birtles) and have disabled all other Svelte-related extensions to reproduce the bug
- if it's a preprocessor related bug like "can't use typescript", did you setup `svelte-preprocess` and/or `svelte.config.js`? See the docs for more info.
-->

**Describe the bug**
If a component doesn't have a script tag or has nothing in a script tag, the generated type class doesn't extend from `SvelteComponentTyped`. This causes the vscode extension to complain "cannot be used as JSX component" (Screenshot below).

**To Reproduce**
Steps to reproduce the behavior:

I'm using `svelte2tsx` `emitDts()` to generate the types.

**Given:** Section.svelte

```svelte
<section class="my-6">
	<slot />
</section>
```

**Output:**: Section.svelte.d.ts

```ts
/** @typedef {typeof __propDef.props}  SectionProps */
/** @typedef {typeof __propDef.events}  SectionEvents */
/** @typedef {typeof __propDef.slots}  SectionSlots */
export default class Section {
}
export type SectionProps = typeof __propDef.props;
export type SectionEvents = typeof __propDef.events;
export type SectionSlots = typeof __propDef.slots;
declare const __propDef: {
    props: {};
    events: {
        [evt: string]: CustomEvent<any>;
    };
    slots: {
        default: {};
    };
};
export {};
```

**Expected behavior**

```diff
/** @typedef {typeof __propDef.props}  SectionProps */
/** @typedef {typeof __propDef.events}  SectionEvents */
/** @typedef {typeof __propDef.slots}  SectionSlots */
- export default class Section {
+ export default class Section extends SvelteComponentTyped<{
+ }, {
+  ​[evt: string]: CustomEvent<any>;
+ }, {
+  default: {};
​+ }> {
}
export type SectionProps = typeof __propDef.props;
export type SectionEvents = typeof __propDef.events;
export type SectionSlots = typeof __propDef.slots;
+ import { SvelteComponentTyped } from "svelte";
declare const __propDef: {
    props: {};
    events: {
        [evt: string]: CustomEvent<any>;
    };
    slots: {
        default: {};
    };
};
export {};
```


**Screenshots**
![Screenshot from 2021-07-23 15-23-24](https://user-images.githubusercontent.com/34116392/126749898-b03dd0a1-1fb1-4a41-bcee-c1088ea334f0.png)


**System (please complete the following information):**
 - OS: Ubuntu 21.04
 - IDE:VSCode
 - Plugin/Package: `svelte2tsx`

**Additional context**
I'm able to reproduce with local repo of `language-tools`. Trying to figure out why.

<!-- Want to help us out? Read this to get started:
https://github.com/sveltejs/language-tools#development
-->

Pull request:
(fix) emitDts for components with no script
Fixes #1112

Explanation: The previous code that strips TSX doesn't take into account for the generated code without any script tag, which cuts off the first 4 characters. I've fixed this by applying a regex that strips of any TSX fragments generated.


Patch:
diff --git a/packages/svelte2tsx/src/svelte2tsx/index.ts b/packages/svelte2tsx/src/svelte2tsx/index.ts
index b3898bcd..fea34351 100644
--- a/packages/svelte2tsx/src/svelte2tsx/index.ts
+++ b/packages/svelte2tsx/src/svelte2tsx/index.ts
@@ -401,12 +401,14 @@ declare function __sveltets_1_createSvelteComponentTyped<Props, Events, Slots>(
         );
         let code = str.toString();
         // Remove all tsx occurences and the template part from the output
-        code =
-            code
-                .substr(0, code.indexOf('\n() => (<>'))
-                // prepended before each script block
-                .replace('<></>;', '')
-                .replace('<></>;', '') + code.substr(code.lastIndexOf('</>);') + '</>);'.length);
+        code = code
+            // prepended before each script block
+            .replace('<></>;', '')
+            .replace('<></>;', '')
+            // tsx in render function
+            .replace(/<>.*<\/>/s, '')
+            .replace('\n() => ();', '');
+
         return {
             code
         };


Test patch:
diff --git a/packages/svelte2tsx/test/emitDts/samples/javascript/expected/TestNoScript.svelte.d.ts b/packages/svelte2tsx/test/emitDts/samples/javascript/expected/TestNoScript.svelte.d.ts
new file mode 100644
index 00000000..f102c232
--- /dev/null
+++ b/packages/svelte2tsx/test/emitDts/samples/javascript/expected/TestNoScript.svelte.d.ts
@@ -0,0 +1,27 @@
+/** @typedef {typeof __propDef.props}  TestNoScriptProps */
+/** @typedef {typeof __propDef.events}  TestNoScriptEvents */
+/** @typedef {typeof __propDef.slots}  TestNoScriptSlots */
+export default class TestNoScript extends SvelteComponentTyped<{}, {
+    click: MouseEvent;
+} & {
+    [evt: string]: CustomEvent<any>;
+}, {
+    default: {};
+}> {
+}
+export type TestNoScriptProps = typeof __propDef.props;
+export type TestNoScriptEvents = typeof __propDef.events;
+export type TestNoScriptSlots = typeof __propDef.slots;
+import { SvelteComponentTyped } from "svelte";
+declare const __propDef: {
+    props: {};
+    events: {
+        click: MouseEvent;
+    } & {
+        [evt: string]: CustomEvent<any>;
+    };
+    slots: {
+        default: {};
+    };
+};
+export {};
diff --git a/packages/svelte2tsx/test/emitDts/samples/javascript/src/TestNoScript.svelte b/packages/svelte2tsx/test/emitDts/samples/javascript/src/TestNoScript.svelte
new file mode 100644
index 00000000..652e1fa9
--- /dev/null
+++ b/packages/svelte2tsx/test/emitDts/samples/javascript/src/TestNoScript.svelte
@@ -0,0 +1,3 @@
+<div on:click>
+    <slot />
+</div>
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/creates-no-script-dts/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/creates-no-script-dts/expected.tsx
new file mode 100644
index 00000000..9fa8f915
--- /dev/null
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/creates-no-script-dts/expected.tsx
@@ -0,0 +1,16 @@
+import { SvelteComponentTyped } from "svelte"
+
+declare function __sveltets_1_createSvelteComponentTyped<Props, Events, Slots>(
+    render: {props: Props, events: Events, slots: Slots }
+): SvelteComponentConstructor<SvelteComponentTyped<Props, Events, Slots>,Svelte2TsxComponentConstructorParameters<Props>>;
+
+function render() {
+
+return { props: {}, slots: {'default': {}}, getters: {}, events: {'click':__sveltets_1_mapElementEvent('click')} }}
+const __propDef = __sveltets_1_partial(__sveltets_1_with_any_event(render()));
+/** @typedef {typeof __propDef.props}  InputProps */
+/** @typedef {typeof __propDef.events}  InputEvents */
+/** @typedef {typeof __propDef.slots}  InputSlots */
+
+export default class Input extends __sveltets_1_createSvelteComponentTyped(__sveltets_1_partial(__sveltets_1_with_any_event(render()))) {
+}
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/creates-no-script-dts/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/creates-no-script-dts/input.svelte
new file mode 100644
index 00000000..71f606ac
--- /dev/null
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/creates-no-script-dts/input.svelte
@@ -0,0 +1,4 @@
+<button on:click><slot /></button>
+{#await Promise.resolve(0) then n}
+  {n}
+{/await}
\ No newline at end of file