feat: add createRawSnippet API

pull/12425/head
Dominic Gannaway 2 years ago
parent 36a6a6b789
commit 116de3eb8e

@ -0,0 +1,5 @@
---
'svelte': patch
---
feat: add createRawSnippet API

@ -190,3 +190,5 @@ export {
tick,
untrack
} from './internal/client/runtime.js';
export { createRawSnippet } from './internal/client/dom/blocks/snippet.js';

@ -35,3 +35,5 @@ export function unmount() {
export async function tick() {}
export { getAllContexts, getContext, hasContext, setContext } from './internal/server/context.js';
export { createRawSnippet } from './internal/server/index.js';

@ -6,7 +6,8 @@ import {
dev_current_component_function,
set_dev_current_component_function
} from '../../runtime.js';
import { hydrate_node, hydrating } from '../hydration.js';
import { hydrate_next, hydrate_node, hydrating } from '../hydration.js';
import { assign_nodes } from '../template.js';
/**
* @template {(node: TemplateNode, ...args: any[]) => void} SnippetFn
@ -60,3 +61,29 @@ export function wrap_snippet(component, fn) {
}
});
}
/**
* Create a snippet imperatively using mount, hyrdate and render functions.
* @param {{
* mount: (...params: any[]) => Element,
* hydrate?: (element: Element, ...params: any[]) => void,
* render: (...params: any[]) => string
* }} options
*/
export function createRawSnippet({ mount, hydrate }) {
var snippet_fn = (/** @type {TemplateNode} */ anchor, /** @type {any[]} */ ...params) => {
var element;
if (hydrating) {
element = hydrate_node;
hydrate_next();
if (hydrate !== undefined) hydrate(/** @type {Element} */ (element), ...params);
} else {
element = mount(...params);
anchor.before(element);
}
assign_nodes(element, element);
};
add_snippet_symbol(snippet_fn);
return snippet_fn;
}

@ -13,7 +13,7 @@ import { escape_html } from '../../escaping.js';
import { DEV } from 'esm-env';
import { current_component, pop, push } from './context.js';
import { EMPTY_COMMENT, BLOCK_CLOSE, BLOCK_OPEN } from './hydration.js';
import { validate_store } from '../shared/validate.js';
import { add_snippet_symbol, validate_store } from '../shared/validate.js';
// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
// https://infra.spec.whatwg.org/#noncharacter
@ -155,6 +155,22 @@ export function head(payload, fn) {
head_payload.out += BLOCK_CLOSE;
}
/**
* Create a snippet imperatively using mount, hyrdate and render functions.
* @param {{
* mount: (...params: any[]) => Element,
* hydrate?: (element: Element, ...params: any[]) => void,
* render: (...params: any[]) => string
* }} options
*/
export function createRawSnippet({ render }) {
const snippet_fn = (/** @type {Payload} */ payload, /** @type {any[]} */ ...args) => {
payload.out += render(...args);
};
add_snippet_symbol(snippet_fn);
return snippet_fn;
}
/**
* @template V
* @param {string} name

@ -0,0 +1,17 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
compileOptions: {
dev: true // Render in dev mode to check that the validation error is not thrown
},
html: `<div><div>0</div></div><button>+</button>`,
test({ assert, target }) {
const [b1] = target.querySelectorAll('button');
b1?.click();
flushSync();
assert.htmlEqual(target.innerHTML, `<div><div>1</div></div><button>+</button>`);
}
});

@ -0,0 +1,32 @@
<script>
import { createRawSnippet } from 'svelte';
let count = $state(0);
const snippet = createRawSnippet({
mount(count) {
const div = document.createElement('div');
$effect(() => {
div.textContent = count();
});
return div;
},
hydrate(element, count) {
$effect(() => {
element.textContent = count();
});
},
render(count) {
return `<div>${count}</div>`;
}
});
</script>
<div>
{@render snippet(count)}
</div>
<button onclick={() => count++}>+</button>

@ -0,0 +1,8 @@
import { test } from '../../test';
export default test({
compileOptions: {
dev: true // Render in dev mode to check that the validation error is not thrown
},
html: `<p>hello world</p>`
});

@ -0,0 +1,16 @@
<script>
import { createRawSnippet } from 'svelte';
const hello = createRawSnippet({
mount() {
const p = document.createElement('p')
p.textContent = 'hello world';
return p;
},
render() {
return '<p>hello world</p>';
}
});
</script>
{@render hello()}

@ -365,12 +365,20 @@ declare module 'svelte' {
export function flushSync(fn?: (() => void) | undefined): void;
/** Anything except a function */
type NotFunction<T> = T extends Function ? never : T;
/**
* Create a snippet imperatively using mount, hyrdate and render functions.
* */
export function createRawSnippet({ mount, hydrate }: {
mount: (...params: any[]) => Element;
hydrate?: (element: Element, ...params: any[]) => void;
render: (...params: any[]) => string;
}): (anchor: TemplateNode, ...params: any[]) => void;
/**
* Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component.
* Transitions will play during the initial render unless the `intro` option is set to `false`.
*
* */
export function mount<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: {} extends Props ? {
function mount_1<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: {} extends Props ? {
target: Document | Element | ShadowRoot;
anchor?: Node;
props?: Props;
@ -389,7 +397,7 @@ declare module 'svelte' {
* Hydrates a component on the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component
*
* */
export function hydrate<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: {} extends Props ? {
function hydrate_1<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: {} extends Props ? {
target: Document | Element | ShadowRoot;
props?: Props;
events?: Record<string, (e: any) => any>;
@ -450,8 +458,9 @@ declare module 'svelte' {
* https://svelte.dev/docs/svelte#getallcontexts
* */
export function getAllContexts<T extends Map<any, any> = Map<any, any>>(): T;
type TemplateNode = Text | Element | Comment;
export {};
export { hydrate_1 as hydrate, mount_1 as mount };
}
declare module 'svelte/action' {

Loading…
Cancel
Save