mirror of https://github.com/sveltejs/svelte
parent
b145035a00
commit
81bd13173b
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': minor
|
||||
---
|
||||
|
||||
feat: add `<svelte:html>` element
|
||||
@ -0,0 +1,11 @@
|
||||
---
|
||||
title: <svelte:html>
|
||||
---
|
||||
|
||||
```svelte
|
||||
<svelte:html attribute={value} onevent={handler} />
|
||||
```
|
||||
|
||||
Similarly to `<svelte:body>`, this element allows you to add properties and listeners to events on `document.documentElement`. This is useful for attributes such as `lang` which influence how the browser interprets the content.
|
||||
|
||||
As with `<svelte:window>`, `<svelte:document>` and `<svelte:body>`, this element may only appear the top level of your component and must never be inside a block or element.
|
||||
@ -0,0 +1,21 @@
|
||||
/** @import { AST } from '#compiler' */
|
||||
/** @import { Context } from '../types' */
|
||||
import * as e from '../../../errors.js';
|
||||
|
||||
/**
|
||||
* @param {AST.SvelteHTML} node
|
||||
* @param {Context} context
|
||||
*/
|
||||
export function SvelteHTML(node, context) {
|
||||
for (const attribute of node.attributes) {
|
||||
if (attribute.type !== 'Attribute') {
|
||||
e.svelte_html_illegal_attribute(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
if (node.fragment.nodes.length > 0) {
|
||||
e.svelte_meta_invalid_content(node, node.name);
|
||||
}
|
||||
|
||||
context.next();
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/** @import { ExpressionStatement } from 'estree' */
|
||||
/** @import { AST } from '#compiler' */
|
||||
/** @import { ComponentContext } from '../types' */
|
||||
import { is_dom_property, normalize_attribute } from '../../../../../utils.js';
|
||||
import { is_ignored } from '../../../../state.js';
|
||||
import { is_event_attribute } from '../../../../utils/ast.js';
|
||||
import * as b from '../../../../utils/builders.js';
|
||||
import { build_attribute_value } from './shared/element.js';
|
||||
import { visit_event_attribute } from './shared/events.js';
|
||||
|
||||
/**
|
||||
* @param {AST.SvelteHTML} element
|
||||
* @param {ComponentContext} context
|
||||
*/
|
||||
export function SvelteHTML(element, context) {
|
||||
const node_id = b.id('$.document.documentElement');
|
||||
|
||||
for (const attribute of element.attributes) {
|
||||
if (attribute.type === 'Attribute') {
|
||||
if (is_event_attribute(attribute)) {
|
||||
visit_event_attribute(attribute, context);
|
||||
} else {
|
||||
const name = normalize_attribute(attribute.name);
|
||||
const { value, has_state } = build_attribute_value(attribute.value, context);
|
||||
|
||||
/** @type {ExpressionStatement} */
|
||||
let update;
|
||||
|
||||
if (name === 'class') {
|
||||
update = b.stmt(b.call('$.set_class', node_id, value));
|
||||
} else if (is_dom_property(name)) {
|
||||
update = b.stmt(b.assignment('=', b.member(node_id, name), value));
|
||||
} else {
|
||||
update = b.stmt(
|
||||
b.call(
|
||||
'$.set_attribute',
|
||||
node_id,
|
||||
b.literal(name),
|
||||
value,
|
||||
is_ignored(element, 'hydration_attribute_changed') && b.true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (has_state) {
|
||||
context.state.update.push(update);
|
||||
} else {
|
||||
context.state.init.push(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
/** @import { Property } from 'estree' */
|
||||
/** @import { AST } from '#compiler' */
|
||||
/** @import { ComponentContext } from '../types.js' */
|
||||
import { normalize_attribute } from '../../../../../utils.js';
|
||||
import { is_event_attribute } from '../../../../utils/ast.js';
|
||||
import * as b from '../../../../utils/builders.js';
|
||||
import { build_attribute_value } from './shared/utils.js';
|
||||
|
||||
/**
|
||||
* @param {AST.SvelteHTML} element
|
||||
* @param {ComponentContext} context
|
||||
*/
|
||||
export function SvelteHTML(element, context) {
|
||||
/** @type {Property[]} */
|
||||
const attributes = [];
|
||||
|
||||
for (const attribute of element.attributes) {
|
||||
if (attribute.type === 'Attribute' && !is_event_attribute(attribute)) {
|
||||
const name = normalize_attribute(attribute.name);
|
||||
const value = build_attribute_value(attribute.value, context);
|
||||
attributes.push(b.init(name, value));
|
||||
}
|
||||
}
|
||||
|
||||
context.state.template.push(
|
||||
b.stmt(b.call('$.svelte_html', b.id('$$payload'), b.object(attributes)))
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
/** @import { Payload } from '#server' */
|
||||
|
||||
import { escape } from '..';
|
||||
|
||||
/**
|
||||
* @param {Payload} payload
|
||||
* @param {Record<string, string>} attributes
|
||||
*/
|
||||
export function svelte_html(payload, attributes) {
|
||||
for (const name in attributes) {
|
||||
payload.htmlAttributes.set(name, escape(attributes[name], true));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async test({ assert }) {
|
||||
assert.deepEqual(document.documentElement.lang, 'de');
|
||||
}
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
<svelte:html lang="de"></svelte:html>
|
||||
@ -0,0 +1 @@
|
||||
<svelte:html foo="bar"></svelte:html>
|
||||
@ -0,0 +1,5 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
htmlAttributes: 'foo="bar"'
|
||||
});
|
||||
@ -0,0 +1,8 @@
|
||||
<script>
|
||||
import Nested from './Nested.svelte';
|
||||
let ignored;
|
||||
</script>
|
||||
|
||||
<svelte:html foo="foo" onevent={ignored}></svelte:html>
|
||||
|
||||
<Nested/>
|
||||
Loading…
Reference in new issue