Skip to content

Commit c287bd5

Browse files
Rich-Harristrueadm
andauthored
Raw snippet alternative (#12425)
* feat: add createRawSnippet API * handle missing hydrate function, improve types * fix * tweak types * beef up test * build * types * oops this was temporary * typo * regenerate types * make mount/render optional, error if missing * move code to new module * test hydration * simpler createRawSnippet API * regenerate types * change signature * docs * h1 -> node * allow `setup` to return a teardown function --------- Co-authored-by: Dominic Gannaway <[email protected]>
1 parent 9666215 commit c287bd5

File tree

17 files changed

+216
-2
lines changed

17 files changed

+216
-2
lines changed

.changeset/fresh-zoos-burn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
feat: add createRawSnippet API

packages/svelte/src/index-client.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,5 @@ export {
190190
tick,
191191
untrack
192192
} from './internal/client/runtime.js';
193+
194+
export { createRawSnippet } from './internal/client/dom/blocks/snippet.js';

packages/svelte/src/index-server.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,5 @@ export function unmount() {
3535
export async function tick() {}
3636

3737
export { getAllContexts, getContext, hasContext, setContext } from './internal/server/context.js';
38+
39+
export { createRawSnippet } from './internal/server/blocks/snippet.js';

packages/svelte/src/internal/client/dom/blocks/snippet.js

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1+
/** @import { Snippet } from 'svelte' */
12
/** @import { Effect, TemplateNode } from '#client' */
3+
/** @import { Getters } from '#shared' */
24
import { add_snippet_symbol } from '../../../shared/validate.js';
35
import { EFFECT_TRANSPARENT } from '../../constants.js';
4-
import { branch, block, destroy_effect } from '../../reactivity/effects.js';
6+
import { branch, block, destroy_effect, teardown } from '../../reactivity/effects.js';
57
import {
68
dev_current_component_function,
79
set_dev_current_component_function
810
} from '../../runtime.js';
9-
import { hydrate_node, hydrating } from '../hydration.js';
11+
import { hydrate_next, hydrate_node, hydrating } from '../hydration.js';
12+
import { create_fragment_from_html } from '../reconciler.js';
13+
import { assign_nodes } from '../template.js';
1014

1115
/**
1216
* @template {(node: TemplateNode, ...args: any[]) => void} SnippetFn
@@ -60,3 +64,40 @@ export function wrap_snippet(component, fn) {
6064
}
6165
});
6266
}
67+
68+
/**
69+
* Create a snippet programmatically
70+
* @template {unknown[]} Params
71+
* @param {(...params: Getters<Params>) => {
72+
* render: () => string
73+
* setup?: (element: Element) => void
74+
* }} fn
75+
* @returns {Snippet<Params>}
76+
*/
77+
export function createRawSnippet(fn) {
78+
return add_snippet_symbol(
79+
(/** @type {TemplateNode} */ anchor, /** @type {Getters<Params>} */ ...params) => {
80+
var snippet = fn(...params);
81+
82+
/** @type {Element} */
83+
var element;
84+
85+
if (hydrating) {
86+
element = /** @type {Element} */ (hydrate_node);
87+
hydrate_next();
88+
} else {
89+
var html = snippet.render().trim();
90+
var fragment = create_fragment_from_html(html);
91+
element = /** @type {Element} */ (fragment.firstChild);
92+
anchor.before(element);
93+
}
94+
95+
const result = snippet.setup?.(element);
96+
assign_nodes(element, element);
97+
98+
if (typeof result === 'function') {
99+
teardown(result);
100+
}
101+
}
102+
);
103+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/** @import { Snippet } from 'svelte' */
2+
/** @import { Payload } from '#server' */
3+
/** @import { Getters } from '#shared' */
4+
import { add_snippet_symbol } from '../../shared/validate.js';
5+
6+
/**
7+
* Create a snippet programmatically
8+
* @template {unknown[]} Params
9+
* @param {(...params: Getters<Params>) => {
10+
* render: () => string
11+
* setup?: (element: Element) => void
12+
* }} fn
13+
* @returns {Snippet<Params>}
14+
*/
15+
export function createRawSnippet(fn) {
16+
return add_snippet_symbol((/** @type {Payload} */ payload, /** @type {Params} */ ...args) => {
17+
var getters = /** @type {Getters<Params>} */ (args.map((value) => () => value));
18+
payload.out += fn(...getters)
19+
.render()
20+
.trim();
21+
});
22+
}

packages/svelte/src/internal/shared/types.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,8 @@ export type SourceLocation =
77
| [line: number, column: number]
88
| [line: number, column: number, SourceLocation[]];
99

10+
export type Getters<T> = {
11+
[K in keyof T]: () => T[K];
12+
};
13+
1014
export type Snapshot<T> = ReturnType<typeof $state.snapshot<T>>;

packages/svelte/src/internal/shared/validate.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/** @import { TemplateNode } from '#client' */
2+
/** @import { Getters } from '#shared' */
13
import { is_void } from '../../constants.js';
24
import * as w from './warnings.js';
35
import * as e from './errors.js';
@@ -6,6 +8,7 @@ const snippet_symbol = Symbol.for('svelte.snippet');
68

79
/**
810
* @param {any} fn
11+
* @returns {import('svelte').Snippet}
912
*/
1013
export function add_snippet_symbol(fn) {
1114
fn[snippet_symbol] = true;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
snapshot(target) {
5+
return {
6+
p: target.querySelector('p')
7+
};
8+
}
9+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<!--[--><p>hydrated</p><!--]-->
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script>
2+
import { createRawSnippet } from 'svelte';
3+
4+
const snippet = createRawSnippet(() => ({
5+
render: () => `
6+
<p>rendered</p>
7+
`,
8+
setup(p) {
9+
p.textContent = 'hydrated';
10+
}
11+
}));
12+
</script>
13+
14+
{@render snippet()}

0 commit comments

Comments
 (0)