diff --git a/packages/language-server/package.json b/packages/language-server/package.json
index d1543e688..b088b07e2 100644
--- a/packages/language-server/package.json
+++ b/packages/language-server/package.json
@@ -51,9 +51,9 @@
"estree-walker": "^2.0.1",
"lodash": "^4.17.19",
"prettier": "2.2.1",
- "prettier-plugin-svelte": "~2.1.0",
+ "prettier-plugin-svelte": "~2.2.0",
"source-map": "^0.7.3",
- "svelte": "~3.32.1",
+ "svelte": "~3.35.0",
"svelte-preprocess": "~4.6.1",
"svelte2tsx": "*",
"typescript": "*",
diff --git a/packages/language-server/src/plugins/html/dataProvider.ts b/packages/language-server/src/plugins/html/dataProvider.ts
index 14947face..047d76803 100644
--- a/packages/language-server/src/plugins/html/dataProvider.ts
+++ b/packages/language-server/src/plugins/html/dataProvider.ts
@@ -176,6 +176,17 @@ const svelteTags: ITagData[] = [
}
]
},
+ {
+ name: 'svelte:fragment',
+ description:
+ 'This element is useful if you want to assign a component to a named slot without creating a wrapper DOM element.',
+ attributes: [
+ {
+ name: 'slot',
+ description: 'The name of the named slot that should be targeted.'
+ }
+ ]
+ },
{
name: 'slot',
description:
diff --git a/packages/svelte2tsx/README.md b/packages/svelte2tsx/README.md
index f15f4a000..0870c0405 100644
--- a/packages/svelte2tsx/README.md
+++ b/packages/svelte2tsx/README.md
@@ -23,7 +23,6 @@ Input.svelte
hello {world}
-
```
will produce this ugly but type checkable TSX
diff --git a/packages/svelte2tsx/package.json b/packages/svelte2tsx/package.json
index 085eda340..dcd5ad31a 100644
--- a/packages/svelte2tsx/package.json
+++ b/packages/svelte2tsx/package.json
@@ -32,7 +32,7 @@
"@rollup/plugin-typescript": "^6.0.0",
"source-map": "^0.6.1",
"source-map-support": "^0.5.16",
- "svelte": "~3.32.1",
+ "svelte": "~3.35.0",
"tiny-glob": "^0.2.6",
"tslib": "^1.10.0",
"typescript": "^4.2.2"
diff --git a/packages/svelte2tsx/src/htmlxtojsx/index.ts b/packages/svelte2tsx/src/htmlxtojsx/index.ts
index 059c903d6..b5eb8ad87 100644
--- a/packages/svelte2tsx/src/htmlxtojsx/index.ts
+++ b/packages/svelte2tsx/src/htmlxtojsx/index.ts
@@ -11,6 +11,7 @@ import { handleBinding } from './nodes/binding';
import { handleClassDirective } from './nodes/class-directive';
import { handleComment } from './nodes/comment';
import { handleComponent } from './nodes/component';
+import { handleSlot } from './nodes/slot';
import { handleDebug } from './nodes/debug';
import { handleEach } from './nodes/each';
import { handleElement } from './nodes/element';
@@ -20,6 +21,7 @@ import { handleRawHtml } from './nodes/raw-html';
import { handleSvelteTag } from './nodes/svelte-tag';
import { handleTransitionDirective } from './nodes/transition-directive';
import { handleText } from './nodes/text';
+import { getSlotName } from '../utils/svelteAst';
type Walker = (node: Node, parent: Node, prop: string, index: number) => void;
@@ -72,10 +74,10 @@ export function convertHtmlxToJsx(
handleDebug(htmlx, str, node);
break;
case 'InlineComponent':
- handleComponent(htmlx, str, node);
+ handleComponent(htmlx, str, node, parent);
break;
case 'Element':
- handleElement(htmlx, str, node);
+ handleElement(htmlx, str, node, parent);
break;
case 'Comment':
handleComment(str, node);
@@ -113,6 +115,10 @@ export function convertHtmlxToJsx(
case 'Body':
handleSvelteTag(htmlx, str, node);
break;
+ case 'SlotTemplate':
+ handleSvelteTag(htmlx, str, node);
+ handleSlot(htmlx, str, node, parent, getSlotName(node) || 'default');
+ break;
case 'Text':
handleText(str, node);
break;
diff --git a/packages/svelte2tsx/src/htmlxtojsx/nodes/component.ts b/packages/svelte2tsx/src/htmlxtojsx/nodes/component.ts
index 5c8fc4803..cfb611ac8 100644
--- a/packages/svelte2tsx/src/htmlxtojsx/nodes/component.ts
+++ b/packages/svelte2tsx/src/htmlxtojsx/nodes/component.ts
@@ -1,13 +1,12 @@
import MagicString from 'magic-string';
import { Node } from 'estree-walker';
import { getSlotName } from '../../utils/svelteAst';
-import { beforeStart } from '../utils/node-utils';
-import { getSingleSlotDef } from '../../svelte2tsx/nodes/slot';
+import { handleSlot } from './slot';
/**
* Handle `` and slot-specific transformations.
*/
-export function handleComponent(htmlx: string, str: MagicString, el: Node): void {
+export function handleComponent(htmlx: string, str: MagicString, el: Node, parent: Node): void {
//we need to remove : if it is a svelte component
if (el.name.startsWith('svelte:')) {
const colon = htmlx.indexOf(':', el.start);
@@ -20,73 +19,7 @@ export function handleComponent(htmlx: string, str: MagicString, el: Node): void
}
}
- //we only need to do something if there is a let or slot
- handleSlot(htmlx, str, el, el, 'default');
-
- //walk the direct children looking for slots. We do this here because we need the name of our component for handleSlot
- //we could lean on leave/enter, but I am lazy
- if (!el.children) return;
- for (const child of el.children) {
- const slotName = getSlotName(child);
- if (slotName) {
- handleSlot(htmlx, str, child, el, slotName);
- }
- }
-}
-
-function handleSlot(
- htmlx: string,
- str: MagicString,
- slotEl: Node,
- component: Node,
- slotName: string
-): void {
- //collect "let" definitions
- const slotElIsComponent = slotEl === component;
- let hasMoved = false;
- let slotDefInsertionPoint: number;
- for (const attr of slotEl.attributes) {
- if (attr.type != 'Let') {
- continue;
- }
-
- if (slotElIsComponent && slotEl.children.length == 0) {
- //no children anyway, just wipe out the attribute
- str.remove(attr.start, attr.end);
- continue;
- }
-
- slotDefInsertionPoint =
- slotDefInsertionPoint ||
- (slotElIsComponent
- ? htmlx.lastIndexOf('>', slotEl.children[0].start) + 1
- : slotEl.start);
-
- str.move(attr.start, attr.end, slotDefInsertionPoint);
-
- //remove let:
- if (hasMoved) {
- str.overwrite(attr.start, attr.start + 'let:'.length, ', ');
- } else {
- str.remove(attr.start, attr.start + 'let:'.length);
- }
- hasMoved = true;
- if (attr.expression) {
- //overwrite the = as a :
- const equalSign = htmlx.lastIndexOf('=', attr.expression.start);
- const curly = htmlx.lastIndexOf('{', beforeStart(attr.expression.start));
- str.overwrite(equalSign, curly + 1, ':');
- str.remove(attr.expression.end, attr.end);
- }
- }
- if (!hasMoved) {
- return;
- }
- str.appendLeft(slotDefInsertionPoint, '{() => { let {');
- str.appendRight(slotDefInsertionPoint, `} = ${getSingleSlotDef(component, slotName)}` + ';<>');
-
- const closeSlotDefInsertionPoint = slotElIsComponent
- ? htmlx.lastIndexOf('<', slotEl.end - 1)
- : slotEl.end;
- str.appendLeft(closeSlotDefInsertionPoint, '>}}');
+ // Handle possible slot
+ const slotName = getSlotName(el) || 'default';
+ handleSlot(htmlx, str, el, slotName === 'default' ? el : parent, slotName);
}
diff --git a/packages/svelte2tsx/src/htmlxtojsx/nodes/element.ts b/packages/svelte2tsx/src/htmlxtojsx/nodes/element.ts
index dbd0a6335..79094ad0c 100644
--- a/packages/svelte2tsx/src/htmlxtojsx/nodes/element.ts
+++ b/packages/svelte2tsx/src/htmlxtojsx/nodes/element.ts
@@ -1,10 +1,17 @@
import MagicString from 'magic-string';
import { Node } from 'estree-walker';
+import { getSlotName } from '../../utils/svelteAst';
+import { handleSlot } from './slot';
/**
* Special treatment for self-closing / void tags to make them conform to JSX.
*/
-export function handleElement(htmlx: string, str: MagicString, node: Node): void {
+export function handleElement(htmlx: string, str: MagicString, node: Node, parent: Node): void {
+ const slotName = getSlotName(node);
+ if (slotName) {
+ handleSlot(htmlx, str, node, parent, slotName);
+ }
+
//we just have to self close void tags since jsx always wants the />
const voidTags = 'area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr'.split(
','
diff --git a/packages/svelte2tsx/src/htmlxtojsx/nodes/slot.ts b/packages/svelte2tsx/src/htmlxtojsx/nodes/slot.ts
new file mode 100644
index 000000000..5e6634119
--- /dev/null
+++ b/packages/svelte2tsx/src/htmlxtojsx/nodes/slot.ts
@@ -0,0 +1,61 @@
+import MagicString from 'magic-string';
+import { Node } from 'estree-walker';
+import { beforeStart } from '../utils/node-utils';
+import { getSingleSlotDef } from '../../svelte2tsx/nodes/slot';
+
+export function handleSlot(
+ htmlx: string,
+ str: MagicString,
+ slotEl: Node,
+ component: Node,
+ slotName: string
+): void {
+ //collect "let" definitions
+ const slotElIsComponent = slotEl === component;
+ let hasMoved = false;
+ let slotDefInsertionPoint: number;
+ for (const attr of slotEl.attributes) {
+ if (attr.type != 'Let') {
+ continue;
+ }
+
+ if (slotElIsComponent && slotEl.children.length == 0) {
+ //no children anyway, just wipe out the attribute
+ str.remove(attr.start, attr.end);
+ continue;
+ }
+
+ slotDefInsertionPoint =
+ slotDefInsertionPoint ||
+ (slotElIsComponent
+ ? htmlx.lastIndexOf('>', slotEl.children[0].start) + 1
+ : slotEl.start);
+
+ str.move(attr.start, attr.end, slotDefInsertionPoint);
+
+ //remove let:
+ if (hasMoved) {
+ str.overwrite(attr.start, attr.start + 'let:'.length, ', ');
+ } else {
+ str.remove(attr.start, attr.start + 'let:'.length);
+ }
+ hasMoved = true;
+ if (attr.expression) {
+ //overwrite the = as a :
+ const equalSign = htmlx.lastIndexOf('=', attr.expression.start);
+ const curly = htmlx.lastIndexOf('{', beforeStart(attr.expression.start));
+ str.overwrite(equalSign, curly + 1, ':');
+ str.remove(attr.expression.end, attr.end);
+ }
+ }
+ if (!hasMoved) {
+ return;
+ }
+ str.appendLeft(slotDefInsertionPoint, '{() => { let {');
+ str.appendRight(slotDefInsertionPoint, `} = ${getSingleSlotDef(component, slotName)}` + ';<>');
+
+ const closeSlotDefInsertionPoint = slotElIsComponent
+ ? htmlx.lastIndexOf('<', slotEl.end - 1)
+ : slotEl.end;
+ str.appendLeft(closeSlotDefInsertionPoint, '>}}');
+}
diff --git a/packages/svelte2tsx/src/htmlxtojsx/nodes/svelte-tag.ts b/packages/svelte2tsx/src/htmlxtojsx/nodes/svelte-tag.ts
index 19f0c063c..1474f8da2 100644
--- a/packages/svelte2tsx/src/htmlxtojsx/nodes/svelte-tag.ts
+++ b/packages/svelte2tsx/src/htmlxtojsx/nodes/svelte-tag.ts
@@ -3,7 +3,7 @@ import { Node } from 'estree-walker';
/**
* `...` ----> `...`
- * (same for :head, :body, :options)
+ * (same for :head, :body, :options, :fragment)
*/
export function handleSvelteTag(htmlx: string, str: MagicString, node: Node): void {
const colon = htmlx.indexOf(':', node.start);
diff --git a/packages/svelte2tsx/svelte-jsx.d.ts b/packages/svelte2tsx/svelte-jsx.d.ts
index af890c268..3f0364bbf 100644
--- a/packages/svelte2tsx/svelte-jsx.d.ts
+++ b/packages/svelte2tsx/svelte-jsx.d.ts
@@ -28,8 +28,9 @@ declare namespace svelte.JSX {
type NativeElement = HTMLElement;
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
- interface IntrinsicAttributes {}
+ interface IntrinsicAttributes {
+ slot?: string;
+ }
// TypeScript SVGElement has no `dataset` (Chrome 55+, Firefox 51+).
type Element = NativeElement & {
@@ -952,6 +953,7 @@ declare namespace svelte.JSX {
// Svelte specific
sveltewindow: HTMLProps & SvelteWindowProps;
sveltebody: HTMLProps;
+ sveltefragment: { slot?: string; };
[name: string]: { [name: string]: any };
}
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/component-named-slot/expected.jsx b/packages/svelte2tsx/test/htmlx2jsx/samples/component-named-slot/expected.jsx
new file mode 100644
index 000000000..aa99fcbcd
--- /dev/null
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/component-named-slot/expected.jsx
@@ -0,0 +1,8 @@
+<>{() => { let {foo, bar:baz} = __sveltets_instanceOf(Parent).$$slot_def['default'];<>
+ {() => { let {bla} = __sveltets_instanceOf(Parent).$$slot_def['named'];<>
+ {foo} {baz} {bla}
+ >}}
+ {() => { let {blubb} = __sveltets_instanceOf(Component).$$slot_def['default'];<>
+ {blubb}
+ >}}
+>}}>
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/component-named-slot/input.svelte b/packages/svelte2tsx/test/htmlx2jsx/samples/component-named-slot/input.svelte
new file mode 100644
index 000000000..e31be2e58
--- /dev/null
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/component-named-slot/input.svelte
@@ -0,0 +1,8 @@
+
+
+ {foo} {baz} {bla}
+
+
+ {blubb}
+
+
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/svelte-fragment/expected.jsx b/packages/svelte2tsx/test/htmlx2jsx/samples/svelte-fragment/expected.jsx
new file mode 100644
index 000000000..564e2275a
--- /dev/null
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/svelte-fragment/expected.jsx
@@ -0,0 +1,19 @@
+<>
+
+ hi
+
+
+
+ hi
+
+
+
+
+ {() => { let {foo, bar:baz} = __sveltets_instanceOf(Component).$$slot_def['default'];<>
+ {foo} {baz}
+ >}}
+
+ {() => { let {foo, bar:baz} = __sveltets_instanceOf(Component).$$slot_def['named'];<>
+ {foo} {baz}
+ >}}
+>
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/svelte-fragment/input.svelte b/packages/svelte2tsx/test/htmlx2jsx/samples/svelte-fragment/input.svelte
new file mode 100644
index 000000000..c65321779
--- /dev/null
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/svelte-fragment/input.svelte
@@ -0,0 +1,19 @@
+
+
+ hi
+
+
+
+ hi
+
+
+
+
+
+ {foo} {baz}
+
+
+
+ {foo} {baz}
+
+
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/uses-svelte-components/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/uses-svelte-components/expected.tsx
index 02e12227e..4f692d2bb 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/uses-svelte-components/expected.tsx
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/uses-svelte-components/expected.tsx
@@ -11,7 +11,8 @@
Hi
->
+
+>
return { props: {}, slots: {}, getters: {}, events: {} }}
export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) {
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/uses-svelte-components/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/uses-svelte-components/input.svelte
index c6f0b29cd..addcbd904 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/uses-svelte-components/input.svelte
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/uses-svelte-components/input.svelte
@@ -9,4 +9,5 @@
Hi
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 8f441c8e3..deb51db2c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2108,10 +2108,10 @@ prelude-ls@^1.2.1:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
-prettier-plugin-svelte@~2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-2.1.0.tgz#61b930107cf4eb8bdae0e9d416e47fb36ee6b53c"
- integrity sha512-AeGJWicKCU9CbPKj9Wzk7apdCJwB8gzFHOMMqJh1X4LiwkMLHUjjysowH+SZfHdg69Hjv5rw5M7uJn0WobFhRQ==
+prettier-plugin-svelte@~2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-2.2.0.tgz#4bd94992fa5b76413a8a5556f90b128c4fdaf7a6"
+ integrity sha512-Xdmqgr71tAuMqqzNCK52/v94g/Yv7V7lz+nmbO9NEA+9ol15VV3uUHOfydMNOo3SWvFaVlBcp947ebEaMWqVfQ==
prettier@2.2.1:
version "2.2.1"
@@ -2508,10 +2508,10 @@ svelte-preprocess@~4.6.1:
detect-indent "^6.0.0"
strip-indent "^3.0.0"
-svelte@~3.32.1:
- version "3.32.1"
- resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.32.1.tgz#c4b6e35517d0ed77e652cc8964ef660afa2f70f3"
- integrity sha512-j1KmD2ZOU0RGq1/STDXjwfh0/eJ/Deh2NXyuz1bpR9eOcz9yImn4CGxXdbSAN7cMTm9a7IyPUIbuBCzu/pXK0g==
+svelte@~3.35.0:
+ version "3.35.0"
+ resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.35.0.tgz#e0d0ba60c4852181c2b4fd851194be6fda493e65"
+ integrity sha512-gknlZkR2sXheu/X+B7dDImwANVvK1R0QGQLd8CNIfxxGPeXBmePnxfzb6fWwTQRsYQG7lYkZXvpXJvxvpsoB7g==
table@^5.2.3:
version "5.4.6"