Skip to content

Commit 078f9a0

Browse files
authored
feat: support <svelte:bounday> (#2598)
companion to sveltejs/svelte#14211 test skipped while feature is not merged yet
1 parent a1b4a64 commit 078f9a0

File tree

5 files changed

+57
-8
lines changed

5 files changed

+57
-8
lines changed

packages/language-server/src/plugins/html/dataProvider.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,17 @@ const svelteTags: ITagData[] = [
296296
'Named slots allow consumers to target specific areas. They can also have fallback content.'
297297
}
298298
]
299+
},
300+
{
301+
name: 'svelte:boundary',
302+
description:
303+
'Represents a boundary in the application. Can catch errors and show fallback UI',
304+
attributes: [
305+
{
306+
name: 'onerror',
307+
description: 'Called when an error occured within the boundary'
308+
}
309+
]
299310
}
300311
];
301312

@@ -419,6 +430,18 @@ export const svelteHtmlDataProvider = newHTMLDataProvider('svelte-builtin', {
419430
})) ?? []
420431
});
421432

433+
const originalProvideAttributes =
434+
svelteHtmlDataProvider.provideAttributes.bind(svelteHtmlDataProvider);
435+
436+
svelteHtmlDataProvider.provideAttributes = (tag: string) => {
437+
if (tag === 'svelte:boundary' || tag === 'svelte:options') {
438+
// We don't want the global attributes for these tags
439+
return svelteTags.find((t) => t.name === tag)?.attributes ?? [];
440+
}
441+
442+
return originalProvideAttributes(tag);
443+
};
444+
422445
function isEvent(attr: IAttributeData) {
423446
return attr.name.startsWith('on');
424447
}

packages/svelte2tsx/src/htmlxtojsx_v2/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,10 @@ export function convertHtmlxToJsx(
8888
handleSnippet(
8989
str,
9090
node,
91-
element instanceof InlineComponent &&
92-
estreeTypedParent.type === 'InlineComponent'
91+
(element instanceof InlineComponent &&
92+
estreeTypedParent.type === 'InlineComponent') ||
93+
(element instanceof Element &&
94+
element.tagName === 'svelte:boundary')
9395
? element
9496
: undefined
9597
);
@@ -133,6 +135,7 @@ export function convertHtmlxToJsx(
133135
case 'Title':
134136
case 'Document':
135137
case 'Body':
138+
case 'SvelteBoundary':
136139
case 'Slot':
137140
case 'SlotTemplate':
138141
if (node.name !== '!DOCTYPE') {
@@ -236,6 +239,7 @@ export function convertHtmlxToJsx(
236239
case 'Head':
237240
case 'Title':
238241
case 'Body':
242+
case 'SvelteBoundary':
239243
case 'Document':
240244
case 'Slot':
241245
case 'SlotTemplate':

packages/svelte2tsx/src/htmlxtojsx_v2/nodes/SnippetBlock.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { BaseNode } from '../../interfaces';
33
import { transform, TransformationArray } from '../utils/node-utils';
44
import { InlineComponent } from './InlineComponent';
55
import { IGNORE_POSITION_COMMENT, surroundWithIgnoreComments } from '../../utils/ignore';
6+
import { Element } from './Element';
67

78
/**
89
* Transform #snippet into a function
@@ -28,7 +29,7 @@ import { IGNORE_POSITION_COMMENT, surroundWithIgnoreComments } from '../../utils
2829
export function handleSnippet(
2930
str: MagicString,
3031
snippetBlock: BaseNode,
31-
component?: InlineComponent
32+
component?: InlineComponent | Element
3233
): void {
3334
const isImplicitProp = component !== undefined;
3435
const endSnippet = str.original.lastIndexOf('{', snippetBlock.end - 1);
@@ -64,6 +65,7 @@ export function handleSnippet(
6465
if (isImplicitProp) {
6566
str.overwrite(snippetBlock.start, snippetBlock.expression.start, '', { contentOnly: true });
6667
const transforms: TransformationArray = ['('];
68+
6769
if (parameters) {
6870
transforms.push(parameters);
6971
const [start, end] = parameters;
@@ -74,12 +76,21 @@ export function handleSnippet(
7476
} else {
7577
str.overwrite(snippetBlock.expression.end, startEnd, '', { contentOnly: true });
7678
}
79+
7780
transforms.push(')' + afterParameters);
7881
transforms.push([startEnd, snippetBlock.end]);
79-
component.addImplicitSnippetProp(
80-
[snippetBlock.expression.start, snippetBlock.expression.end],
81-
transforms
82-
);
82+
83+
if (component instanceof InlineComponent) {
84+
component.addImplicitSnippetProp(
85+
[snippetBlock.expression.start, snippetBlock.expression.end],
86+
transforms
87+
);
88+
} else {
89+
component.addAttribute(
90+
[[snippetBlock.expression.start, snippetBlock.expression.end]],
91+
transforms
92+
);
93+
}
8394
} else {
8495
const transforms: TransformationArray = [
8596
'const ',
@@ -149,7 +160,7 @@ export function handleImplicitChildren(componentNode: BaseNode, component: Inlin
149160
}
150161

151162
export function hoistSnippetBlock(str: MagicString, blockOrEl: BaseNode) {
152-
if (blockOrEl.type === 'InlineComponent') {
163+
if (blockOrEl.type === 'InlineComponent' || blockOrEl.type === 'SvelteBoundary') {
153164
// implicit props, handled in InlineComponent
154165
return;
155166
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{ svelteHTML.createElement("svelte:boundary", { "onerror":e => e,failed:(e) => { async ()/*Ωignore_positionΩ*/ => {
2+
{ svelteHTML.createElement("p", {}); e; }
3+
};return __sveltets_2_any(0)},}); { const $$_sliaFtahTtnenopmoC1C = __sveltets_2_ensureComponent(ComponentThatFails); new $$_sliaFtahTtnenopmoC1C({ target: __sveltets_2_any(), props: {}});}
4+
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<svelte:boundary onerror={e => e}>
2+
<ComponentThatFails />
3+
{#snippet failed(e)}
4+
<p>error: {e}</p>
5+
{/snippet}
6+
</svelte:boundary>

0 commit comments

Comments
 (0)