-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
Describe the problem
This is a super niche problem to have, but it's one I've faced on two recent projects. Currently, SvelteKit knows which bit of markup to hydrate because of tight coupling between the config...
target: '#svelte' |
<div id="svelte">%svelte.body%</div> |
The tight coupling is unfortunate in any case, but where this gets really tricky is if you're using SvelteKit to generate markup that will be embedded multiple times on a page, as in this pseudo-code:
// src/app.html wraps the %svelte.head% and %svelte.body% with these tags
const head_start = '<!-- START:HEAD -->';
const head_end = '<!-- END:HEAD -->';
const body_start = '<!-- START:BODY -->';
const body_end = '<!-- END:BODY -->';
// after building app with adapter-static
for (const file of glob('build/**/index.html')) {
const html = fs.readFileSync(file);
const head = contents.slice(contents.indexOf(head_start) + head_start.length, contents.indexOf(head_end));
const body = contents.slice(contents.indexOf(body_start) + body_start.length, contents.indexOf(body_end));
const slug = file.slice('build/'.length, -'/index.html'.length);
const metadata = get_metadata(slug);
await cms.upload({ ...metadata, head, body });
}
The script ends up hydrating the wrong markup unless you use a placeholder selector instead of #svelte
and rewrite it to be unique:
-target: '#svelte',
+target: '#PLACEHOLDER',
-<div id="svelte">%svelte.body%</div>
+<div id="PLACEHOLDER">%svelte.body%</div>
-const html = fs.readFileSync(file);
+const html = fs.readFileSync(file).replace(/PLACEHOLDER/g, `svelte-${Math.round(Math.random() * 1e9)}`);
That's pretty ugly (and brittle, since there's a non-zero chance of collisions. We're currently using page slugs instead of random numbers, which avoids collisions, but it turns out that sometimes the same embed will be used multiple times in the NYT live blog...), and involves deep knowledge about how SvelteKit works.
Describe the proposed solution
What if <div>%svelte.body%</div>
turned into this?
<div>
<script type="module">
import { start } from ".../start-81fdb15d.js";
start({
target: document.currentScript.parentNode,
paths: {...},
// ...
});
</script>
<!-- the server-rendered markup (if using SSR) -->
</div>
Reasons to do this:
- we're currently polluting the template with an
id="svelte"
or similar (which is only used for hydration, but you wouldn't necessarily understand that — it looks like something important that you shouldn't muck about with) - tight coupling is bad
- getting rid of config options is good
- the markup SvelteKit generates is less portable than it should be
Reasons not to do this:
- the
<script>
itself will get hydrated away. maybe that's confusing? - IE11 doesn't support
document.currentScript
, so if we intend to support that browser eventually, this would stand in the way. we could continue to support thetarget
option (if unspecified, defaults todocument.currentScript.parentNode
), but that would be annoying - the browser will encounter the import marginally later. in browsers that don't implement
modulepreload
that could result in a microscopic startup penalty
Alternatives considered
Alternative is to continue using the (perfectly viable, if finicky and annoying) userland solutions.
Importance
nice to have
Additional Information
No response