state-onchange
Rich Harris 5 months ago
commit cfd8ef2768

@ -37,7 +37,7 @@ On the other side, inside `MyComponent.svelte`, we can receive props with the `$
## Fallback values ## Fallback values
Destructuring allows us to declare fallback values, which are used if the parent component does not set a given prop: Destructuring allows us to declare fallback values, which are used if the parent component does not set a given prop (or the value is `undefined`):
```js ```js
let { adjective = 'happy' } = $props(); let { adjective = 'happy' } = $props();
@ -219,4 +219,4 @@ This is useful for linking elements via attributes like `for` and `aria-labelled
<label for="{uid}-lastname">Last Name: </label> <label for="{uid}-lastname">Last Name: </label>
<input id="{uid}-lastname" type="text" /> <input id="{uid}-lastname" type="text" />
</form> </form>
``` ```

@ -154,6 +154,8 @@ A JavaScript expression can be included as text by surrounding it with curly bra
{expression} {expression}
``` ```
Expressions that are `null` or `undefined` will be omitted; all others are [coerced to strings](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#string_coercion).
Curly braces can be included in a Svelte template by using their [HTML entity](https://developer.mozilla.org/docs/Glossary/Entity) strings: `&lbrace;`, `&lcub;`, or `&#123;` for `{` and `&rbrace;`, `&rcub;`, or `&#125;` for `}`. Curly braces can be included in a Svelte template by using their [HTML entity](https://developer.mozilla.org/docs/Glossary/Entity) strings: `&lbrace;`, `&lcub;`, or `&#123;` for `{` and `&rbrace;`, `&rcub;`, or `&#125;` for `}`.
If you're using a regular expression (`RegExp`) [literal notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#literal_notation_and_constructor), you'll need to wrap it in parentheses. If you're using a regular expression (`RegExp`) [literal notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#literal_notation_and_constructor), you'll need to wrap it in parentheses.

@ -112,6 +112,8 @@ Snippets can reference themselves and each other ([demo](/playground/untitled#H4
## Passing snippets to components ## Passing snippets to components
### Explicit props
Within the template, snippets are values just like any other. As such, they can be passed to components as props ([demo](/playground/untitled#H4sIAAAAAAAAE3VS247aMBD9lZGpBGwDASRegonaPvQL2qdlH5zYEKvBNvbQLbL875VzAcKyj3PmzJnLGU8UOwqSkd8KJdaCk4TsZS0cyV49wYuJuQiQpGd-N2bu_ooaI1YwJ57hpVYoFDqSEepKKw3mO7VDeTTaIvxiRS1gb_URxvO0ibrS8WanIrHUyiHs7Vmigy28RmyHHmKvDMbMmFq4cQInvGSwTsBYWYoMVhCSB2rBFFPsyl0uruTlR3JZCWvlTXl1Yy_mawiR_rbZKZrellJ-5JQ0RiBUgnFhJ9OGR7HKmwVoilXeIye8DOJGfYCgRlZ3iE876TBsZPX7hPdteO75PC4QaIo8vwNPePmANQ2fMeEFHrLD7rR1jTNkW986E8C3KwfwVr8HSHOSEBT_kGRozyIkn_zQveXDL3rIfPJHtUDwzShJd_Qk3gQCbOGLsdq4yfTRJopRuin3I7nv6kL7ARRjmLdBDG3uv1mhuLA3V2mKtqNEf_oCn8p9aN-WYqH5peP4kWBl1UwJzAEPT9U7K--0fRrrWnPTXpCm1_EVdXjpNmlA8G1hPPyM1fKgMqjFHjctXGjLhZ05w0qpDhksGrybuNEHtJnCalZWsuaTlfq6nPaaBSv_HKw-K57BjzOiVj9ZKQYKzQjZodYFqydYTRN4gPhVzTDO2xnma3HsVWjaLjT8nbfwHy7Q5f2dBAAA)): Within the template, snippets are values just like any other. As such, they can be passed to components as props ([demo](/playground/untitled#H4sIAAAAAAAAE3VS247aMBD9lZGpBGwDASRegonaPvQL2qdlH5zYEKvBNvbQLbL875VzAcKyj3PmzJnLGU8UOwqSkd8KJdaCk4TsZS0cyV49wYuJuQiQpGd-N2bu_ooaI1YwJ57hpVYoFDqSEepKKw3mO7VDeTTaIvxiRS1gb_URxvO0ibrS8WanIrHUyiHs7Vmigy28RmyHHmKvDMbMmFq4cQInvGSwTsBYWYoMVhCSB2rBFFPsyl0uruTlR3JZCWvlTXl1Yy_mawiR_rbZKZrellJ-5JQ0RiBUgnFhJ9OGR7HKmwVoilXeIye8DOJGfYCgRlZ3iE876TBsZPX7hPdteO75PC4QaIo8vwNPePmANQ2fMeEFHrLD7rR1jTNkW986E8C3KwfwVr8HSHOSEBT_kGRozyIkn_zQveXDL3rIfPJHtUDwzShJd_Qk3gQCbOGLsdq4yfTRJopRuin3I7nv6kL7ARRjmLdBDG3uv1mhuLA3V2mKtqNEf_oCn8p9aN-WYqH5peP4kWBl1UwJzAEPT9U7K--0fRrrWnPTXpCm1_EVdXjpNmlA8G1hPPyM1fKgMqjFHjctXGjLhZ05w0qpDhksGrybuNEHtJnCalZWsuaTlfq6nPaaBSv_HKw-K57BjzOiVj9ZKQYKzQjZodYFqydYTRN4gPhVzTDO2xnma3HsVWjaLjT8nbfwHy7Q5f2dBAAA)):
```svelte ```svelte
@ -144,6 +146,8 @@ Within the template, snippets are values just like any other. As such, they can
Think about it like passing content instead of data to a component. The concept is similar to slots in web components. Think about it like passing content instead of data to a component. The concept is similar to slots in web components.
### Implicit props
As an authoring convenience, snippets declared directly _inside_ a component implicitly become props _on_ the component ([demo](/playground/untitled#H4sIAAAAAAAAE3VSTa_aMBD8Kyu_SkAbCA-JSzBR20N_QXt6vIMTO8SqsY29tI2s_PcqTiB8vaPHs7MzuxuIZgdBMvJLo0QlOElIJZXwJHsLBBvb_XUASc7Mb9Yu_B-hsMMK5sUzvDQahUZPMkJ96aTFfKd3KA_WOISfrFACKmcOMFmk8TWUTjY73RFLoz1C5U4SPWzhrcN2GKDrlcGEWauEnyRwxCaDdQLWyVJksII2uaMWTDPNLtzX5YX8-kgua-GcHJVXI3u5WEPb0d83O03TMZSmfRzOkG1Db7mNacOL19JagVALxoWbztq-H8U6j0SaYp2P2BGbOyQ2v8PQIFMXLKRDk177pq0zf6d8bMrzwBdd0pamyPMb-IjNEzS2f86Gz_Dwf-2F9nvNSUJQ_EOSoTuJNvngqK5v4Pas7n4-OCwlEEJcQTIMO-nSQwtb-GSdsX46e9gbRoP9yGQ11I0rEuycunu6PHx1QnPhxm3SFN15MOlYEFJZtf0dUywMbwZOeBGsrKNLYB54-1R9WNqVdki7usim6VmQphf7mnpshiQRhNAXdoOfMyX3OgMlKtz0cGEcF27uLSul3mewjPjgOOoDukxjPS9rqfh0pb-8zs6aBSt_7505aZ7B9xOi0T9YKW4UooVsr0zB1BTrWQJ3EL-oWcZ572GxFoezCk37QLe3897-B2i2U62uBAAA)): As an authoring convenience, snippets declared directly _inside_ a component implicitly become props _on_ the component ([demo](/playground/untitled#H4sIAAAAAAAAE3VSTa_aMBD8Kyu_SkAbCA-JSzBR20N_QXt6vIMTO8SqsY29tI2s_PcqTiB8vaPHs7MzuxuIZgdBMvJLo0QlOElIJZXwJHsLBBvb_XUASc7Mb9Yu_B-hsMMK5sUzvDQahUZPMkJ96aTFfKd3KA_WOISfrFACKmcOMFmk8TWUTjY73RFLoz1C5U4SPWzhrcN2GKDrlcGEWauEnyRwxCaDdQLWyVJksII2uaMWTDPNLtzX5YX8-kgua-GcHJVXI3u5WEPb0d83O03TMZSmfRzOkG1Db7mNacOL19JagVALxoWbztq-H8U6j0SaYp2P2BGbOyQ2v8PQIFMXLKRDk177pq0zf6d8bMrzwBdd0pamyPMb-IjNEzS2f86Gz_Dwf-2F9nvNSUJQ_EOSoTuJNvngqK5v4Pas7n4-OCwlEEJcQTIMO-nSQwtb-GSdsX46e9gbRoP9yGQ11I0rEuycunu6PHx1QnPhxm3SFN15MOlYEFJZtf0dUywMbwZOeBGsrKNLYB54-1R9WNqVdki7usim6VmQphf7mnpshiQRhNAXdoOfMyX3OgMlKtz0cGEcF27uLSul3mewjPjgOOoDukxjPS9rqfh0pb-8zs6aBSt_7505aZ7B9xOi0T9YKW4UooVsr0zB1BTrWQJ3EL-oWcZ572GxFoezCk37QLe3897-B2i2U62uBAAA)):
```svelte ```svelte
@ -165,6 +169,8 @@ As an authoring convenience, snippets declared directly _inside_ a component imp
</Table> </Table>
``` ```
### Implicit `children` snippet
Any content inside the component tags that is _not_ a snippet declaration implicitly becomes part of the `children` snippet ([demo](/playground/untitled#H4sIAAAAAAAAE3WOQQrCMBBFrzIMggql3ddY1Du4si5sOmIwnYRkFKX07lKqglqX8_7_w2uRDw1hjlsWI5ZqTPBoLEXMdy3K3fdZDzB5Ndfep_FKVnpWHSKNce1YiCVijirqYLwUJQOYxrsgsLmIOIZjcA1M02w4n-PpomSVvTclqyEutDX6DA2pZ7_ABIVugrmEC3XJH92P55_G39GodCmWBFrQJ2PrQAwdLGHig_NxNv9xrQa1dhWIawrv1Wzeqawa8953D-8QOmaEAQAA)): Any content inside the component tags that is _not_ a snippet declaration implicitly becomes part of the `children` snippet ([demo](/playground/untitled#H4sIAAAAAAAAE3WOQQrCMBBFrzIMggql3ddY1Du4si5sOmIwnYRkFKX07lKqglqX8_7_w2uRDw1hjlsWI5ZqTPBoLEXMdy3K3fdZDzB5Ndfep_FKVnpWHSKNce1YiCVijirqYLwUJQOYxrsgsLmIOIZjcA1M02w4n-PpomSVvTclqyEutDX6DA2pZ7_ABIVugrmEC3XJH92P55_G39GodCmWBFrQJ2PrQAwdLGHig_NxNv9xrQa1dhWIawrv1Wzeqawa8953D-8QOmaEAQAA)):
```svelte ```svelte
@ -184,6 +190,8 @@ Any content inside the component tags that is _not_ a snippet declaration implic
> [!NOTE] Note that you cannot have a prop called `children` if you also have content inside the component — for this reason, you should avoid having props with that name > [!NOTE] Note that you cannot have a prop called `children` if you also have content inside the component — for this reason, you should avoid having props with that name
### Optional snippet props
You can declare snippet props as being optional. You can either use optional chaining to not render anything if the snippet isn't set... You can declare snippet props as being optional. You can either use optional chaining to not render anything if the snippet isn't set...
```svelte ```svelte

@ -235,7 +235,31 @@ A top-level `:global {...}` block can only contain rules, not declarations
### css_global_block_invalid_list ### css_global_block_invalid_list
``` ```
A `:global` selector cannot be part of a selector list with more than one item A `:global` selector cannot be part of a selector list with entries that don't contain `:global`
```
The following CSS is invalid:
```css
:global, x {
y {
color: red;
}
}
```
This is mixing a `:global` block, which means "everything in here is unscoped", with a scoped selector (`x` in this case). As a result it's not possible to transform the inner selector (`y` in this case) into something that satisfies both requirements. You therefore have to split this up into two selectors:
```css
:global {
y {
color: red;
}
}
x y {
color: red;
}
``` ```
### css_global_block_invalid_modifier ### css_global_block_invalid_modifier

@ -60,6 +60,43 @@ Certain lifecycle methods can only be used during component initialisation. To f
<button onclick={handleClick}>click me</button> <button onclick={handleClick}>click me</button>
``` ```
### snippet_without_render_tag
```
Attempted to render a snippet without a `{@render}` block. This would cause the snippet code to be stringified instead of its content being rendered to the DOM. To fix this, change `{snippet}` to `{@render snippet()}`.
```
A component throwing this error will look something like this (`children` is not being rendered):
```svelte
<script>
let { children } = $props();
</script>
{children}
```
...or like this (a parent component is passing a snippet where a non-snippet value is expected):
```svelte
<!--- file: Parent.svelte --->
<ChildComponent>
{#snippet label()}
<span>Hi!</span>
{/snippet}
</ChildComponent>
```
```svelte
<!--- file: Child.svelte --->
<script>
let { label } = $props();
</script>
<!-- This component doesn't expect a snippet, but the parent provided one -->
<p>{label}</p>
```
### store_invalid_shape ### store_invalid_shape
``` ```

@ -2,24 +2,6 @@
title: svelte/reactivity title: svelte/reactivity
--- ---
Svelte provides reactive versions of various built-ins like `SvelteMap`, `SvelteSet` and `SvelteURL`. These can be imported from `svelte/reactivity` and used just like their native counterparts. Svelte provides reactive versions of various built-ins like [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), [`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) and [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) that can be used just like their native counterparts, as well as a handful of additional utilities for handling reactivity.
```svelte
<script>
import { SvelteURL } from 'svelte/reactivity';
const url = new SvelteURL('https://example.com/path');
</script>
<!-- changes to these... -->
<input bind:value={url.protocol} />
<input bind:value={url.hostname} />
<input bind:value={url.pathname} />
<hr />
<!-- will update `href` and vice versa -->
<input bind:value={url.href} />
```
> MODULE: svelte/reactivity > MODULE: svelte/reactivity

@ -1,5 +1,49 @@
# svelte # svelte
## 5.27.2
### Patch Changes
- chore: use pkg.imports for common modules ([#15787](https://github.com/sveltejs/svelte/pull/15787))
## 5.27.1
### Patch Changes
- chore: default params for html blocks ([#15778](https://github.com/sveltejs/svelte/pull/15778))
- fix: correct suggested type for custom events without detail ([#15763](https://github.com/sveltejs/svelte/pull/15763))
- fix: Throw on unrendered snippets in `dev` ([#15766](https://github.com/sveltejs/svelte/pull/15766))
- fix: avoid unnecessary read version increments ([#15777](https://github.com/sveltejs/svelte/pull/15777))
## 5.27.0
### Minor Changes
- feat: partially evaluate certain expressions ([#15494](https://github.com/sveltejs/svelte/pull/15494))
### Patch Changes
- fix: relax `:global` selector list validation ([#15762](https://github.com/sveltejs/svelte/pull/15762))
## 5.26.3
### Patch Changes
- fix: correctly validate head snippets on the server ([#15755](https://github.com/sveltejs/svelte/pull/15755))
- fix: ignore mutation validation for props that are not proxies in more cases ([#15759](https://github.com/sveltejs/svelte/pull/15759))
- fix: allow self-closing tags within math namespace ([#15761](https://github.com/sveltejs/svelte/pull/15761))
## 5.26.2
### Patch Changes
- fix: correctly validate `undefined` snippet params with default value ([#15750](https://github.com/sveltejs/svelte/pull/15750))
## 5.26.1 ## 5.26.1
### Patch Changes ### Patch Changes

@ -16,7 +16,31 @@
## css_global_block_invalid_list ## css_global_block_invalid_list
> A `:global` selector cannot be part of a selector list with more than one item > A `:global` selector cannot be part of a selector list with entries that don't contain `:global`
The following CSS is invalid:
```css
:global, x {
y {
color: red;
}
}
```
This is mixing a `:global` block, which means "everything in here is unscoped", with a scoped selector (`x` in this case). As a result it's not possible to transform the inner selector (`y` in this case) into something that satisfies both requirements. You therefore have to split this up into two selectors:
```css
:global {
y {
color: red;
}
}
x y {
color: red;
}
```
## css_global_block_invalid_modifier ## css_global_block_invalid_modifier

@ -52,6 +52,41 @@ Certain lifecycle methods can only be used during component initialisation. To f
<button onclick={handleClick}>click me</button> <button onclick={handleClick}>click me</button>
``` ```
## snippet_without_render_tag
> Attempted to render a snippet without a `{@render}` block. This would cause the snippet code to be stringified instead of its content being rendered to the DOM. To fix this, change `{snippet}` to `{@render snippet()}`.
A component throwing this error will look something like this (`children` is not being rendered):
```svelte
<script>
let { children } = $props();
</script>
{children}
```
...or like this (a parent component is passing a snippet where a non-snippet value is expected):
```svelte
<!--- file: Parent.svelte --->
<ChildComponent>
{#snippet label()}
<span>Hi!</span>
{/snippet}
</ChildComponent>
```
```svelte
<!--- file: Child.svelte --->
<script>
let { label } = $props();
</script>
<!-- This component doesn't expect a snippet, but the parent provided one -->
<p>{label}</p>
```
## store_invalid_shape ## store_invalid_shape
> `%name%` is not a store with a `subscribe` method > `%name%` is not a store with a `subscribe` method

@ -2,7 +2,7 @@
"name": "svelte", "name": "svelte",
"description": "Cybernetically enhanced web apps", "description": "Cybernetically enhanced web apps",
"license": "MIT", "license": "MIT",
"version": "5.26.1", "version": "5.27.2",
"type": "module", "type": "module",
"types": "./types/index.d.ts", "types": "./types/index.d.ts",
"engines": { "engines": {
@ -103,6 +103,17 @@
"default": "./src/events/index.js" "default": "./src/events/index.js"
} }
}, },
"imports": {
"#client": "./src/internal/client/types.d.ts",
"#client/constants": "./src/internal/client/constants.js",
"#compiler": {
"types": "./src/compiler/private.d.ts",
"default": "./src/compiler/index.js"
},
"#compiler/builders": "./src/compiler/utils/builders.js",
"#server": "./src/internal/server/types.d.ts",
"#shared": "./src/internal/shared/types.d.ts"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/sveltejs/svelte.git", "url": "git+https://github.com/sveltejs/svelte.git",

@ -24,7 +24,12 @@ await createBundle({
output: `${dir}/types/index.d.ts`, output: `${dir}/types/index.d.ts`,
compilerOptions: { compilerOptions: {
// so that types/properties with `@internal` (and its dependencies) are removed from the output // so that types/properties with `@internal` (and its dependencies) are removed from the output
stripInternal: true stripInternal: true,
paths: Object.fromEntries(
Object.entries(pkg.imports).map(([key, value]) => {
return [key, [value.types ?? value.default ?? value]];
})
)
}, },
modules: { modules: {
[pkg.name]: `${dir}/src/index.d.ts`, [pkg.name]: `${dir}/src/index.d.ts`,

@ -555,12 +555,12 @@ export function css_global_block_invalid_declaration(node) {
} }
/** /**
* A `:global` selector cannot be part of a selector list with more than one item * A `:global` selector cannot be part of a selector list with entries that don't contain `:global`
* @param {null | number | NodeLike} node * @param {null | number | NodeLike} node
* @returns {never} * @returns {never}
*/ */
export function css_global_block_invalid_list(node) { export function css_global_block_invalid_list(node) {
e(node, 'css_global_block_invalid_list', `A \`:global\` selector cannot be part of a selector list with more than one item\nhttps://svelte.dev/e/css_global_block_invalid_list`); e(node, 'css_global_block_invalid_list', `A \`:global\` selector cannot be part of a selector list with entries that don't contain \`:global\`\nhttps://svelte.dev/e/css_global_block_invalid_list`);
} }
/** /**

@ -1,7 +1,7 @@
/** @import { VariableDeclarator, Node, Identifier, AssignmentExpression, LabeledStatement, ExpressionStatement } from 'estree' */ /** @import { VariableDeclarator, Node, Identifier, AssignmentExpression, LabeledStatement, ExpressionStatement } from 'estree' */
/** @import { Visitors } from 'zimmerframe' */ /** @import { Visitors } from 'zimmerframe' */
/** @import { ComponentAnalysis } from '../phases/types.js' */ /** @import { ComponentAnalysis } from '../phases/types.js' */
/** @import { Scope, ScopeRoot } from '../phases/scope.js' */ /** @import { Scope } from '../phases/scope.js' */
/** @import { AST, Binding, ValidatedCompileOptions } from '#compiler' */ /** @import { AST, Binding, ValidatedCompileOptions } from '#compiler' */
import MagicString from 'magic-string'; import MagicString from 'magic-string';
import { walk } from 'zimmerframe'; import { walk } from 'zimmerframe';

@ -1,7 +1,7 @@
/** @import { Context, Visitors } from 'zimmerframe' */ /** @import { Context, Visitors } from 'zimmerframe' */
/** @import { FunctionExpression, FunctionDeclaration } from 'estree' */ /** @import { FunctionExpression, FunctionDeclaration } from 'estree' */
import { walk } from 'zimmerframe'; import { walk } from 'zimmerframe';
import * as b from '../../utils/builders.js'; import * as b from '#compiler/builders';
import * as e from '../../errors.js'; import * as e from '../../errors.js';
/** /**

@ -193,10 +193,12 @@ const css_visitors = {
Rule(node, context) { Rule(node, context) {
node.metadata.parent_rule = context.state.rule; node.metadata.parent_rule = context.state.rule;
node.metadata.is_global_block = node.prelude.children.some((selector) => { // We gotta allow :global x, :global y because CSS preprocessors might generate that from :global { x, y {...} }
for (const complex_selector of node.prelude.children) {
let is_global_block = false; let is_global_block = false;
for (const child of selector.children) { for (let selector_idx = 0; selector_idx < complex_selector.children.length; selector_idx++) {
const child = complex_selector.children[selector_idx];
const idx = child.selectors.findIndex(is_global_block_selector); const idx = child.selectors.findIndex(is_global_block_selector);
if (is_global_block) { if (is_global_block) {
@ -204,58 +206,56 @@ const css_visitors = {
child.metadata.is_global_like = true; child.metadata.is_global_like = true;
} }
if (idx !== -1) { if (idx === 0) {
is_global_block = true; if (
child.selectors.length > 1 &&
selector_idx === 0 &&
node.metadata.parent_rule === null
) {
e.css_global_block_invalid_modifier_start(child.selectors[1]);
} else {
// `child` starts with `:global`
node.metadata.is_global_block = is_global_block = true;
for (let i = 1; i < child.selectors.length; i++) {
walk(/** @type {AST.CSS.Node} */ (child.selectors[i]), null, {
ComplexSelector(node) {
node.metadata.used = true;
}
});
}
for (let i = idx + 1; i < child.selectors.length; i++) { if (child.combinator && child.combinator.name !== ' ') {
walk(/** @type {AST.CSS.Node} */ (child.selectors[i]), null, { e.css_global_block_invalid_combinator(child, child.combinator.name);
ComplexSelector(node) { }
node.metadata.used = true;
}
});
}
}
}
return is_global_block; const declaration = node.block.children.find((child) => child.type === 'Declaration');
}); const is_lone_global =
complex_selector.children.length === 1 &&
complex_selector.children[0].selectors.length === 1; // just `:global`, not e.g. `:global x`
if (node.metadata.is_global_block) { if (is_lone_global && node.prelude.children.length > 1) {
if (node.prelude.children.length > 1) { // `:global, :global x { z { ... } }` would become `x { z { ... } }` which means `z` is always
e.css_global_block_invalid_list(node.prelude); // constrained by `x`, which is not what the user intended
} e.css_global_block_invalid_list(node.prelude);
}
const complex_selector = node.prelude.children[0]; if (
const global_selector = complex_selector.children.find((r, selector_idx) => { declaration &&
const idx = r.selectors.findIndex(is_global_block_selector); // :global { color: red; } is invalid, but foo :global { color: red; } is valid
if (idx === 0) { node.prelude.children.length === 1 &&
if (r.selectors.length > 1 && selector_idx === 0 && node.metadata.parent_rule === null) { is_lone_global
e.css_global_block_invalid_modifier_start(r.selectors[1]); ) {
e.css_global_block_invalid_declaration(declaration);
}
} }
return true;
} else if (idx !== -1) { } else if (idx !== -1) {
e.css_global_block_invalid_modifier(r.selectors[idx]); e.css_global_block_invalid_modifier(child.selectors[idx]);
} }
});
if (!global_selector) {
throw new Error('Internal error: global block without :global selector');
}
if (global_selector.combinator && global_selector.combinator.name !== ' ') {
e.css_global_block_invalid_combinator(global_selector, global_selector.combinator.name);
} }
const declaration = node.block.children.find((child) => child.type === 'Declaration'); if (node.metadata.is_global_block && !is_global_block) {
e.css_global_block_invalid_list(node.prelude);
if (
declaration &&
// :global { color: red; } is invalid, but foo :global { color: red; } is valid
node.prelude.children.length === 1 &&
node.prelude.children[0].children.length === 1 &&
node.prelude.children[0].children[0].selectors.length === 1
) {
e.css_global_block_invalid_declaration(declaration);
} }
} }

@ -5,8 +5,8 @@
import { walk } from 'zimmerframe'; import { walk } from 'zimmerframe';
import * as e from '../../errors.js'; import * as e from '../../errors.js';
import * as w from '../../warnings.js'; import * as w from '../../warnings.js';
import { extract_identifiers, is_text_attribute } from '../../utils/ast.js'; import { extract_identifiers } from '../../utils/ast.js';
import * as b from '../../utils/builders.js'; import * as b from '#compiler/builders';
import { Scope, ScopeRoot, create_scopes, get_rune, set_scope } from '../scope.js'; import { Scope, ScopeRoot, create_scopes, get_rune, set_scope } from '../scope.js';
import check_graph_for_cycles from './utils/check_graph_for_cycles.js'; import check_graph_for_cycles from './utils/check_graph_for_cycles.js';
import { create_attribute, is_custom_element_node } from '../nodes.js'; import { create_attribute, is_custom_element_node } from '../nodes.js';

@ -3,10 +3,10 @@
/** @import { Context } from '../types' */ /** @import { Context } from '../types' */
import { get_rune } from '../../scope.js'; import { get_rune } from '../../scope.js';
import * as e from '../../../errors.js'; import * as e from '../../../errors.js';
import { get_parent, unwrap_optional } from '../../../utils/ast.js'; import { get_parent } from '../../../utils/ast.js';
import { is_pure, is_safe_identifier } from './shared/utils.js'; import { is_pure, is_safe_identifier } from './shared/utils.js';
import { dev, locate_node, source } from '../../../state.js'; import { dev, locate_node, source } from '../../../state.js';
import * as b from '../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {CallExpression} node * @param {CallExpression} node

@ -1,7 +1,5 @@
/** @import { ExportNamedDeclaration, Identifier, Node } from 'estree' */ /** @import { ExportNamedDeclaration, Identifier } from 'estree' */
/** @import { Binding } from '#compiler' */
/** @import { Context } from '../types' */ /** @import { Context } from '../types' */
/** @import { Scope } from '../../scope' */
import * as e from '../../../errors.js'; import * as e from '../../../errors.js';
import { extract_identifiers } from '../../../utils/ast.js'; import { extract_identifiers } from '../../../utils/ast.js';

@ -1,5 +1,4 @@
/** @import { Expression, Identifier } from 'estree' */ /** @import { Expression, Identifier } from 'estree' */
/** @import { EachBlock } from '#compiler' */
/** @import { Context } from '../types' */ /** @import { Context } from '../types' */
import is_reference from 'is-reference'; import is_reference from 'is-reference';
import { should_proxy } from '../../3-transform/client/utils.js'; import { should_proxy } from '../../3-transform/client/utils.js';

@ -1,10 +1,7 @@
/** @import { MemberExpression, Node } from 'estree' */ /** @import { MemberExpression } from 'estree' */
/** @import { Context } from '../types' */ /** @import { Context } from '../types' */
import * as e from '../../../errors.js'; import * as e from '../../../errors.js';
import * as w from '../../../warnings.js';
import { object } from '../../../utils/ast.js';
import { is_pure, is_safe_identifier } from './shared/utils.js'; import { is_pure, is_safe_identifier } from './shared/utils.js';
import { mark_subtree_dynamic } from './shared/fragment.js';
/** /**
* @param {MemberExpression} node * @param {MemberExpression} node

@ -173,7 +173,8 @@ export function RegularElement(node, context) {
if ( if (
context.state.analysis.source[node.end - 2] === '/' && context.state.analysis.source[node.end - 2] === '/' &&
!is_void(node_name) && !is_void(node_name) &&
!is_svg(node_name) !is_svg(node_name) &&
!is_mathml(node_name)
) { ) {
w.element_invalid_self_closing_tag(node, node.name); w.element_invalid_self_closing_tag(node, node.name);
} }

@ -1,4 +1,4 @@
/** @import { AssignmentExpression, Expression, Identifier, Literal, Node, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */ /** @import { AssignmentExpression, Expression, Literal, Node, Pattern, Super, UpdateExpression, VariableDeclarator } from 'estree' */
/** @import { AST, Binding } from '#compiler' */ /** @import { AST, Binding } from '#compiler' */
/** @import { AnalysisState, Context } from '../../types' */ /** @import { AnalysisState, Context } from '../../types' */
/** @import { Scope } from '../../../scope' */ /** @import { Scope } from '../../../scope' */
@ -6,7 +6,7 @@
import * as e from '../../../../errors.js'; import * as e from '../../../../errors.js';
import { extract_identifiers } from '../../../../utils/ast.js'; import { extract_identifiers } from '../../../../utils/ast.js';
import * as w from '../../../../warnings.js'; import * as w from '../../../../warnings.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { get_rune } from '../../../scope.js'; import { get_rune } from '../../../scope.js';
/** /**

@ -3,7 +3,7 @@
/** @import { ComponentAnalysis, Analysis } from '../../types' */ /** @import { ComponentAnalysis, Analysis } from '../../types' */
/** @import { Visitors, ComponentClientTransformState, ClientTransformState } from './types' */ /** @import { Visitors, ComponentClientTransformState, ClientTransformState } from './types' */
import { walk } from 'zimmerframe'; import { walk } from 'zimmerframe';
import * as b from '../../../utils/builders.js'; import * as b from '#compiler/builders';
import { build_getter, is_state_source } from './utils.js'; import { build_getter, is_state_source } from './utils.js';
import { render_stylesheet } from '../css/index.js'; import { render_stylesheet } from '../css/index.js';
import { dev, filename } from '../../../state.js'; import { dev, filename } from '../../../state.js';

@ -3,7 +3,7 @@
/** @import { ClientTransformState, ComponentClientTransformState, ComponentContext } from './types.js' */ /** @import { ClientTransformState, ComponentClientTransformState, ComponentContext } from './types.js' */
/** @import { Analysis } from '../../types.js' */ /** @import { Analysis } from '../../types.js' */
/** @import { Scope } from '../../scope.js' */ /** @import { Scope } from '../../scope.js' */
import * as b from '../../../utils/builders.js'; import * as b from '#compiler/builders';
import { is_simple_expression } from '../../../utils/ast.js'; import { is_simple_expression } from '../../../utils/ast.js';
import { import {
PROPS_IS_LAZY_INITIAL, PROPS_IS_LAZY_INITIAL,

@ -1,7 +1,7 @@
/** @import { Expression } from 'estree' */ /** @import { Expression } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { parse_directive_name } from './shared/utils.js'; import { parse_directive_name } from './shared/utils.js';
/** /**

@ -1,7 +1,7 @@
/** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, Pattern } from 'estree' */ /** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, Pattern } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { Context } from '../types.js' */ /** @import { Context } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { import {
build_assignment_value, build_assignment_value,
get_attribute_expression, get_attribute_expression,

@ -2,7 +2,7 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */
import { extract_identifiers } from '../../../../utils/ast.js'; import { extract_identifiers } from '../../../../utils/ast.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { create_derived } from '../utils.js'; import { create_derived } from '../utils.js';
import { get_value } from './shared/declarations.js'; import { get_value } from './shared/declarations.js';

@ -1,7 +1,7 @@
/** @import { Expression, BinaryExpression } from 'estree' */ /** @import { Expression, BinaryExpression } from 'estree' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { dev } from '../../../../state.js'; import { dev } from '../../../../state.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {BinaryExpression} node * @param {BinaryExpression} node

@ -3,7 +3,7 @@
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { dev, is_ignored } from '../../../../state.js'; import { dev, is_ignored } from '../../../../state.js';
import { is_text_attribute } from '../../../../utils/ast.js'; import { is_text_attribute } from '../../../../utils/ast.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { binding_properties } from '../../../bindings.js'; import { binding_properties } from '../../../bindings.js';
import { build_attribute_value } from './shared/element.js'; import { build_attribute_value } from './shared/element.js';
import { build_bind_this, validate_binding } from './shared/utils.js'; import { build_bind_this, validate_binding } from './shared/utils.js';

@ -1,7 +1,7 @@
/** @import { ArrowFunctionExpression, BlockStatement, CallExpression, Expression, FunctionDeclaration, FunctionExpression, Statement } from 'estree' */ /** @import { ArrowFunctionExpression, BlockStatement, Expression, FunctionDeclaration, FunctionExpression, Statement } from 'estree' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { add_state_transformers } from './shared/declarations.js'; import { add_state_transformers } from './shared/declarations.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {BlockStatement} node * @param {BlockStatement} node

@ -1,6 +1,6 @@
/** @import { BreakStatement } from 'estree' */ /** @import { BreakStatement } from 'estree' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {BreakStatement} node * @param {BreakStatement} node

@ -1,7 +1,7 @@
/** @import { CallExpression, Expression } from 'estree' */ /** @import { CallExpression, Expression } from 'estree' */
/** @import { Context } from '../types' */ /** @import { Context } from '../types' */
import { dev, is_ignored } from '../../../../state.js'; import { dev, is_ignored } from '../../../../state.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { get_rune } from '../../../scope.js'; import { get_rune } from '../../../scope.js';
import { transform_inspect_rune } from '../../utils.js'; import { transform_inspect_rune } from '../../utils.js';

@ -1,8 +1,6 @@
/** @import { ClassBody, Expression, Identifier, Literal, MethodDefinition, PrivateIdentifier, PropertyDefinition } from 'estree' */ /** @import { ClassBody, Expression, Identifier, Literal, MethodDefinition, PrivateIdentifier, PropertyDefinition } from 'estree' */
/** @import { } from '#compiler' */
/** @import { Context, StateField } from '../types' */ /** @import { Context, StateField } from '../types' */
import { dev, is_ignored } from '../../../../state.js'; import * as b from '#compiler/builders';
import * as b from '../../../../utils/builders.js';
import { regex_invalid_identifier_chars } from '../../../patterns.js'; import { regex_invalid_identifier_chars } from '../../../patterns.js';
import { get_rune } from '../../../scope.js'; import { get_rune } from '../../../scope.js';
import { should_proxy } from '../utils.js'; import { should_proxy } from '../utils.js';

@ -1,7 +1,7 @@
/** @import { Expression } from 'estree' */ /** @import { Expression } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { build_component } from './shared/component.js'; import { build_component } from './shared/component.js';
/** /**

@ -3,7 +3,7 @@
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { dev } from '../../../../state.js'; import { dev } from '../../../../state.js';
import { extract_identifiers } from '../../../../utils/ast.js'; import { extract_identifiers } from '../../../../utils/ast.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { create_derived } from '../utils.js'; import { create_derived } from '../utils.js';
import { get_value } from './shared/declarations.js'; import { get_value } from './shared/declarations.js';

@ -1,7 +1,7 @@
/** @import { Expression} from 'estree' */ /** @import { Expression} from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.DebugTag} node * @param {AST.DebugTag} node

@ -11,7 +11,7 @@ import {
} from '../../../../../constants.js'; } from '../../../../../constants.js';
import { dev } from '../../../../state.js'; import { dev } from '../../../../state.js';
import { extract_paths, object } from '../../../../utils/ast.js'; import { extract_paths, object } from '../../../../utils/ast.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { build_getter } from '../utils.js'; import { build_getter } from '../utils.js';
import { get_value } from './shared/declarations.js'; import { get_value } from './shared/declarations.js';

@ -1,6 +1,6 @@
/** @import { ExportNamedDeclaration } from 'estree' */ /** @import { ExportNamedDeclaration } from 'estree' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {ExportNamedDeclaration} node * @param {ExportNamedDeclaration} node

@ -1,6 +1,6 @@
/** @import { Expression, ExpressionStatement } from 'estree' */ /** @import { Expression, ExpressionStatement } from 'estree' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { get_rune } from '../../../scope.js'; import { get_rune } from '../../../scope.js';
/** /**

@ -4,7 +4,7 @@
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */
import { TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../../../constants.js'; import { TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../../../constants.js';
import { dev } from '../../../../state.js'; import { dev } from '../../../../state.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { sanitize_template_string } from '../../../../utils/sanitize_template_string.js'; import { sanitize_template_string } from '../../../../utils/sanitize_template_string.js';
import { clean_nodes, infer_namespace } from '../../utils.js'; import { clean_nodes, infer_namespace } from '../../utils.js';
import { process_children } from './shared/fragment.js'; import { process_children } from './shared/fragment.js';

@ -1,7 +1,7 @@
/** @import { FunctionDeclaration } from 'estree' */ /** @import { FunctionDeclaration } from 'estree' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { build_hoisted_params } from '../utils.js'; import { build_hoisted_params } from '../utils.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {FunctionDeclaration} node * @param {FunctionDeclaration} node

@ -2,7 +2,7 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { is_ignored } from '../../../../state.js'; import { is_ignored } from '../../../../state.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.HtmlTag} node * @param {AST.HtmlTag} node
@ -11,17 +11,22 @@ import * as b from '../../../../utils/builders.js';
export function HtmlTag(node, context) { export function HtmlTag(node, context) {
context.state.template.push('<!>'); context.state.template.push('<!>');
// push into init, so that bindings run afterwards, which might trigger another run and override hydration const expression = /** @type {Expression} */ (context.visit(node.expression));
context.state.init.push(
b.stmt( const is_svg = context.state.metadata.namespace === 'svg';
b.call( const is_mathml = context.state.metadata.namespace === 'mathml';
'$.html',
context.state.node, const statement = b.stmt(
b.thunk(/** @type {Expression} */ (context.visit(node.expression))), b.call(
b.literal(context.state.metadata.namespace === 'svg'), '$.html',
b.literal(context.state.metadata.namespace === 'mathml'), context.state.node,
is_ignored(node, 'hydration_html_changed') && b.true b.thunk(expression),
) is_svg && b.true,
is_mathml && b.true,
is_ignored(node, 'hydration_html_changed') && b.true
) )
); );
// push into init, so that bindings run afterwards, which might trigger another run and override hydration
context.state.init.push(statement);
} }

@ -1,7 +1,7 @@
/** @import { Identifier, Node } from 'estree' */ /** @import { Identifier, Node } from 'estree' */
/** @import { Context } from '../types' */ /** @import { Context } from '../types' */
import is_reference from 'is-reference'; import is_reference from 'is-reference';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { build_getter } from '../utils.js'; import { build_getter } from '../utils.js';
/** /**

@ -1,7 +1,7 @@
/** @import { BlockStatement, Expression, Identifier } from 'estree' */ /** @import { BlockStatement, Expression } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.IfBlock} node * @param {AST.IfBlock} node

@ -1,6 +1,6 @@
/** @import { ImportDeclaration } from 'estree' */ /** @import { ImportDeclaration } from 'estree' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {ImportDeclaration} node * @param {ImportDeclaration} node

@ -1,7 +1,7 @@
/** @import { Expression } from 'estree' */ /** @import { Expression } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.KeyBlock} node * @param {AST.KeyBlock} node

@ -1,9 +1,7 @@
/** @import { Location } from 'locate-character' */
/** @import { Expression, LabeledStatement, Statement } from 'estree' */ /** @import { Expression, LabeledStatement, Statement } from 'estree' */
/** @import { ReactiveStatement } from '#compiler' */ /** @import { ReactiveStatement } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { dev, is_ignored, locator } from '../../../../state.js'; import * as b from '#compiler/builders';
import * as b from '../../../../utils/builders.js';
import { build_getter } from '../utils.js'; import { build_getter } from '../utils.js';
/** /**

@ -1,7 +1,7 @@
/** @import { Expression } from 'estree' */ /** @import { Expression } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { create_derived } from '../utils.js'; import { create_derived } from '../utils.js';
/** /**

@ -1,6 +1,6 @@
/** @import { MemberExpression } from 'estree' */ /** @import { MemberExpression } from 'estree' */
/** @import { Context } from '../types' */ /** @import { Context } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {MemberExpression} node * @param {MemberExpression} node

@ -1,6 +1,6 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { build_event, build_event_handler } from './shared/events.js'; import { build_event, build_event_handler } from './shared/events.js';
const modifiers = [ const modifiers = [

@ -1,7 +1,7 @@
/** @import { Expression, ImportDeclaration, MemberExpression, Program } from 'estree' */ /** @import { Expression, ImportDeclaration, MemberExpression, Program } from 'estree' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { build_getter, is_prop_source } from '../utils.js'; import { build_getter, is_prop_source } from '../utils.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { add_state_transformers } from './shared/declarations.js'; import { add_state_transformers } from './shared/declarations.js';
/** /**

@ -1,4 +1,4 @@
/** @import { ArrayExpression, Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */ /** @import { ArrayExpression, Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { SourceLocation } from '#shared' */ /** @import { SourceLocation } from '#shared' */
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */
@ -13,7 +13,7 @@ import {
import { escape_html } from '../../../../../escaping.js'; import { escape_html } from '../../../../../escaping.js';
import { dev, is_ignored, locator } from '../../../../state.js'; import { dev, is_ignored, locator } from '../../../../state.js';
import { is_event_attribute, is_text_attribute } from '../../../../utils/ast.js'; import { is_event_attribute, is_text_attribute } from '../../../../utils/ast.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { is_custom_element_node } from '../../../nodes.js'; import { is_custom_element_node } from '../../../nodes.js';
import { clean_nodes, determine_namespace_for_children } from '../../utils.js'; import { clean_nodes, determine_namespace_for_children } from '../../utils.js';
import { build_getter } from '../utils.js'; import { build_getter } from '../utils.js';
@ -685,14 +685,13 @@ function build_element_special_value_attribute(element, node_id, attribute, cont
: value : value
); );
const evaluated = context.state.scope.evaluate(value);
const assignment = b.assignment('=', b.member(node_id, '__value'), value);
const inner_assignment = b.assignment( const inner_assignment = b.assignment(
'=', '=',
b.member(node_id, 'value'), b.member(node_id, 'value'),
b.conditional( evaluated.is_defined ? assignment : b.logical('??', assignment, b.literal(''))
b.binary('==', b.null, b.assignment('=', b.member(node_id, '__value'), value)),
b.literal(''), // render null/undefined values as empty string to support placeholder options
value
)
); );
const update = b.stmt( const update = b.stmt(

@ -2,7 +2,7 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { unwrap_optional } from '../../../../utils/ast.js'; import { unwrap_optional } from '../../../../utils/ast.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.RenderTag} node * @param {AST.RenderTag} node

@ -1,7 +1,7 @@
/** @import { BlockStatement, Expression, ExpressionStatement, Literal, Property } from 'estree' */ /** @import { BlockStatement, Expression, ExpressionStatement, Literal, Property } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { build_attribute_value } from './shared/element.js'; import { build_attribute_value } from './shared/element.js';
import { memoize_expression } from './shared/utils.js'; import { memoize_expression } from './shared/utils.js';

@ -3,7 +3,7 @@
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { dev } from '../../../../state.js'; import { dev } from '../../../../state.js';
import { extract_paths } from '../../../../utils/ast.js'; import { extract_paths } from '../../../../utils/ast.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { get_value } from './shared/declarations.js'; import { get_value } from './shared/declarations.js';
/** /**
@ -21,6 +21,10 @@ export function SnippetBlock(node, context) {
/** @type {Statement[]} */ /** @type {Statement[]} */
const declarations = []; const declarations = [];
if (dev) {
declarations.push(b.stmt(b.call('$.validate_snippet_args', b.spread(b.id('arguments')))));
}
const transform = { ...context.state.transform }; const transform = { ...context.state.transform };
const child_state = { ...context.state, transform }; const child_state = { ...context.state, transform };
@ -30,12 +34,7 @@ export function SnippetBlock(node, context) {
if (!argument) continue; if (!argument) continue;
if (argument.type === 'Identifier') { if (argument.type === 'Identifier') {
args.push({ args.push(b.assignment_pattern(argument, b.id('$.noop')));
type: 'AssignmentPattern',
left: argument,
right: b.id('$.noop')
});
transform[argument.name] = { read: b.call }; transform[argument.name] = { read: b.call };
continue; continue;
@ -66,29 +65,16 @@ export function SnippetBlock(node, context) {
} }
} }
} }
if (dev) {
declarations.unshift(
b.stmt(
b.call(
'$.validate_snippet_args',
.../** @type {Identifier[]} */ (
args.map((arg) => (arg?.type === 'Identifier' ? arg : arg?.left))
)
)
)
);
}
body = b.block([ body = b.block([
...declarations, ...declarations,
.../** @type {BlockStatement} */ (context.visit(node.body, child_state)).body .../** @type {BlockStatement} */ (context.visit(node.body, child_state)).body
]); ]);
/** @type {Expression} */ // in dev we use a FunctionExpression (not arrow function) so we can use `arguments`
let snippet = b.arrow(args, body); let snippet = dev
? b.call('$.wrap_snippet', b.id(context.state.analysis.name), b.function(null, args, body))
if (dev) { : b.arrow(args, body);
snippet = b.call('$.wrap_snippet', b.id(context.state.analysis.name), snippet);
}
const declaration = b.const(node.expression, snippet); const declaration = b.const(node.expression, snippet);

@ -2,7 +2,7 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { dev } from '../../../../state.js'; import { dev } from '../../../../state.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.SvelteBoundary} node * @param {AST.SvelteBoundary} node

@ -3,10 +3,10 @@
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { dev, locator } from '../../../../state.js'; import { dev, locator } from '../../../../state.js';
import { is_text_attribute } from '../../../../utils/ast.js'; import { is_text_attribute } from '../../../../utils/ast.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { determine_namespace_for_children } from '../../utils.js'; import { determine_namespace_for_children } from '../../utils.js';
import { build_attribute_value, build_set_attributes, build_set_class } from './shared/element.js'; import { build_attribute_value, build_set_attributes, build_set_class } from './shared/element.js';
import { build_render_statement, get_expression_id } from './shared/utils.js'; import { build_render_statement } from './shared/utils.js';
/** /**
* @param {AST.SvelteElement} node * @param {AST.SvelteElement} node

@ -1,7 +1,7 @@
/** @import { BlockStatement } from 'estree' */ /** @import { BlockStatement } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.SvelteHead} node * @param {AST.SvelteHead} node

@ -1,6 +1,6 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { build_template_chunk } from './shared/utils.js'; import { build_template_chunk } from './shared/utils.js';
/** /**

@ -2,7 +2,7 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { TRANSITION_GLOBAL, TRANSITION_IN, TRANSITION_OUT } from '../../../../../constants.js'; import { TRANSITION_GLOBAL, TRANSITION_IN, TRANSITION_OUT } from '../../../../../constants.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { parse_directive_name } from './shared/utils.js'; import { parse_directive_name } from './shared/utils.js';
/** /**

@ -1,7 +1,7 @@
/** @import { AssignmentExpression, Expression, UpdateExpression } from 'estree' */ /** @import { AssignmentExpression, Expression, UpdateExpression } from 'estree' */
/** @import { Context } from '../types' */ /** @import { Context } from '../types' */
import { object } from '../../../../utils/ast.js'; import { object } from '../../../../utils/ast.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { validate_mutation } from './shared/utils.js'; import { validate_mutation } from './shared/utils.js';
/** /**

@ -1,7 +1,7 @@
/** @import { Expression } from 'estree' */ /** @import { Expression } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { parse_directive_name } from './shared/utils.js'; import { parse_directive_name } from './shared/utils.js';
/** /**

@ -3,7 +3,7 @@
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */
import { dev } from '../../../../state.js'; import { dev } from '../../../../state.js';
import { extract_paths } from '../../../../utils/ast.js'; import { extract_paths } from '../../../../utils/ast.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import * as assert from '../../../../utils/assert.js'; import * as assert from '../../../../utils/assert.js';
import { get_rune } from '../../../scope.js'; import { get_rune } from '../../../scope.js';
import { get_prop_source, is_prop_source, is_state_source, should_proxy } from '../utils.js'; import { get_prop_source, is_prop_source, is_state_source, should_proxy } from '../utils.js';

@ -3,7 +3,7 @@
/** @import { ComponentContext } from '../../types.js' */ /** @import { ComponentContext } from '../../types.js' */
import { dev, is_ignored } from '../../../../../state.js'; import { dev, is_ignored } from '../../../../../state.js';
import { get_attribute_chunks, object } from '../../../../../utils/ast.js'; import { get_attribute_chunks, object } from '../../../../../utils/ast.js';
import * as b from '../../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { build_bind_this, memoize_expression, validate_binding } from '../shared/utils.js'; import { build_bind_this, memoize_expression, validate_binding } from '../shared/utils.js';
import { build_attribute_value } from '../shared/element.js'; import { build_attribute_value } from '../shared/element.js';
import { build_event_handler } from './events.js'; import { build_event_handler } from './events.js';

@ -1,7 +1,7 @@
/** @import { Identifier } from 'estree' */ /** @import { Identifier } from 'estree' */
/** @import { ComponentContext, Context } from '../../types' */ /** @import { ComponentContext, Context } from '../../types' */
import { is_state_source } from '../../utils.js'; import { is_state_source } from '../../utils.js';
import * as b from '../../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* Turns `foo` into `$.get(foo)` * Turns `foo` into `$.get(foo)`

@ -1,11 +1,11 @@
/** @import { ArrayExpression, Expression, Identifier, ObjectExpression } from 'estree' */ /** @import { ArrayExpression, Expression, Identifier, ObjectExpression } from 'estree' */
/** @import { AST, ExpressionMetadata } from '#compiler' */ /** @import { AST, ExpressionMetadata } from '#compiler' */
/** @import { ComponentClientTransformState, ComponentContext } from '../../types' */ /** @import { ComponentContext } from '../../types' */
import { escape_html } from '../../../../../../escaping.js'; import { escape_html } from '../../../../../../escaping.js';
import { normalize_attribute } from '../../../../../../utils.js'; import { normalize_attribute } from '../../../../../../utils.js';
import { is_ignored } from '../../../../../state.js'; import { is_ignored } from '../../../../../state.js';
import { is_event_attribute } from '../../../../../utils/ast.js'; import { is_event_attribute } from '../../../../../utils/ast.js';
import * as b from '../../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { build_class_directives_object, build_style_directives_object } from '../RegularElement.js'; import { build_class_directives_object, build_style_directives_object } from '../RegularElement.js';
import { build_template_chunk, get_expression_id } from './utils.js'; import { build_template_chunk, get_expression_id } from './utils.js';

@ -3,7 +3,7 @@
/** @import { ComponentContext } from '../../types' */ /** @import { ComponentContext } from '../../types' */
import { is_capture_event, is_passive_event } from '../../../../../../utils.js'; import { is_capture_event, is_passive_event } from '../../../../../../utils.js';
import { dev, locator } from '../../../../../state.js'; import { dev, locator } from '../../../../../state.js';
import * as b from '../../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.Attribute} node * @param {AST.Attribute} node

@ -3,7 +3,7 @@
/** @import { ComponentContext } from '../../types' */ /** @import { ComponentContext } from '../../types' */
import { cannot_be_set_statically } from '../../../../../../utils.js'; import { cannot_be_set_statically } from '../../../../../../utils.js';
import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js'; import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js';
import * as b from '../../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { is_custom_element_node } from '../../../../nodes.js'; import { is_custom_element_node } from '../../../../nodes.js';
import { build_template_chunk } from './utils.js'; import { build_template_chunk } from './utils.js';

@ -1,7 +1,7 @@
/** @import { Expression } from 'estree' */ /** @import { Expression } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../../types' */ /** @import { ComponentContext } from '../../types' */
import * as b from '../../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* *

@ -3,7 +3,7 @@
/** @import { ComponentClientTransformState, Context } from '../../types' */ /** @import { ComponentClientTransformState, Context } from '../../types' */
import { walk } from 'zimmerframe'; import { walk } from 'zimmerframe';
import { object } from '../../../../../utils/ast.js'; import { object } from '../../../../../utils/ast.js';
import * as b from '../../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { sanitize_template_string } from '../../../../../utils/sanitize_template_string.js'; import { sanitize_template_string } from '../../../../../utils/sanitize_template_string.js';
import { regex_is_valid_identifier } from '../../../../patterns.js'; import { regex_is_valid_identifier } from '../../../../patterns.js';
import is_reference from 'is-reference'; import is_reference from 'is-reference';
@ -89,21 +89,21 @@ export function build_template_chunk(
} }
} }
const is_defined = const evaluated = state.scope.evaluate(value);
value.type === 'BinaryExpression' ||
(value.type === 'UnaryExpression' && value.operator !== 'void') ||
(value.type === 'LogicalExpression' && value.right.type === 'Literal') ||
(value.type === 'Identifier' && value.name === state.analysis.props_id?.name);
if (!is_defined) { if (evaluated.is_known) {
// add `?? ''` where necessary (TODO optimise more cases) quasi.value.cooked += evaluated.value + '';
value = b.logical('??', value, b.literal('')); } else {
} if (!evaluated.is_defined) {
// add `?? ''` where necessary
value = b.logical('??', value, b.literal(''));
}
expressions.push(value); expressions.push(value);
quasi = b.quasi('', i + 1 === values.length); quasi = b.quasi('', i + 1 === values.length);
quasis.push(quasi); quasis.push(quasi);
}
} }
} }

@ -170,7 +170,11 @@ const visitors = {
if (node.metadata.is_global_block) { if (node.metadata.is_global_block) {
const selector = node.prelude.children[0]; const selector = node.prelude.children[0];
if (selector.children.length === 1 && selector.children[0].selectors.length === 1) { if (
node.prelude.children.length === 1 &&
selector.children.length === 1 &&
selector.children[0].selectors.length === 1
) {
// `:global {...}` // `:global {...}`
if (state.minify) { if (state.minify) {
state.code.remove(node.start, node.block.start + 1); state.code.remove(node.start, node.block.start + 1);
@ -194,7 +198,7 @@ const visitors = {
SelectorList(node, { state, next, path }) { SelectorList(node, { state, next, path }) {
// Only add comments if we're not inside a complex selector that itself is unused or a global block // Only add comments if we're not inside a complex selector that itself is unused or a global block
if ( if (
!is_in_global_block(path) && (!is_in_global_block(path) || node.children.length > 1) &&
!path.find((n) => n.type === 'ComplexSelector' && !n.metadata.used) !path.find((n) => n.type === 'ComplexSelector' && !n.metadata.used)
) { ) {
const children = node.children; const children = node.children;
@ -282,13 +286,24 @@ const visitors = {
const global = /** @type {AST.CSS.PseudoClassSelector} */ (relative_selector.selectors[0]); const global = /** @type {AST.CSS.PseudoClassSelector} */ (relative_selector.selectors[0]);
remove_global_pseudo_class(global, relative_selector.combinator, context.state); remove_global_pseudo_class(global, relative_selector.combinator, context.state);
if ( const parent_rule = node.metadata.rule?.metadata.parent_rule;
node.metadata.rule?.metadata.parent_rule && if (parent_rule && global.args === null) {
global.args === null && if (relative_selector.combinator === null) {
relative_selector.combinator === null // div { :global.x { ... } } becomes div { &.x { ... } }
) { context.state.code.prependRight(global.start, '&');
// div { :global.x { ... } } becomes div { &.x { ... } } }
context.state.code.prependRight(global.start, '&');
// In case of multiple :global selectors in a selector list we gotta delete the comma, too, but only if
// the next selector is used; if it's unused then the comma deletion happens as part of removal of that next selector
if (
parent_rule.prelude.children.length > 1 &&
node.children.length === node.children.findIndex((s) => s === relative_selector) - 1
) {
const next_selector = parent_rule.prelude.children.find((s) => s.start > global.end);
if (next_selector && next_selector.metadata.used) {
context.state.code.update(global.end, next_selector.start, '');
}
}
} }
continue; continue;
} else { } else {
@ -380,7 +395,9 @@ function remove_global_pseudo_class(selector, combinator, state) {
// div :global.x becomes div.x // div :global.x becomes div.x
while (/\s/.test(state.code.original[start - 1])) start--; while (/\s/.test(state.code.original[start - 1])) start--;
} }
state.code.remove(start, selector.start + ':global'.length);
// update(...), not remove(...) because there could be a closing unused comment at the end
state.code.update(start, selector.start + ':global'.length, '');
} else { } else {
state.code state.code
.remove(selector.start, selector.start + ':global('.length) .remove(selector.start, selector.start + ':global('.length)

@ -5,7 +5,7 @@
import { walk } from 'zimmerframe'; import { walk } from 'zimmerframe';
import { set_scope } from '../../scope.js'; import { set_scope } from '../../scope.js';
import { extract_identifiers } from '../../../utils/ast.js'; import { extract_identifiers } from '../../../utils/ast.js';
import * as b from '../../../utils/builders.js'; import * as b from '#compiler/builders';
import { dev, filename } from '../../../state.js'; import { dev, filename } from '../../../state.js';
import { render_stylesheet } from '../css/index.js'; import { render_stylesheet } from '../css/index.js';
import { AssignmentExpression } from './visitors/AssignmentExpression.js'; import { AssignmentExpression } from './visitors/AssignmentExpression.js';

@ -1,7 +1,7 @@
/** @import { AssignmentExpression, AssignmentOperator, Expression, Pattern } from 'estree' */ /** @import { AssignmentExpression, AssignmentOperator, Expression, Pattern } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { Context, ServerTransformState } from '../types.js' */ /** @import { Context, ServerTransformState } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { build_assignment_value } from '../../../../utils/ast.js'; import { build_assignment_value } from '../../../../utils/ast.js';
import { visit_assignment_expression } from '../../shared/assignments.js'; import { visit_assignment_expression } from '../../shared/assignments.js';

@ -1,7 +1,7 @@
/** @import { BlockStatement, Expression, Pattern } from 'estree' */ /** @import { BlockStatement, Expression, Pattern } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { block_close } from './shared/utils.js'; import { block_close } from './shared/utils.js';
/** /**

@ -1,7 +1,7 @@
/** @import { CallExpression, Expression } from 'estree' */ /** @import { CallExpression, Expression } from 'estree' */
/** @import { Context } from '../types.js' */ /** @import { Context } from '../types.js' */
import { is_ignored } from '../../../../state.js'; import { is_ignored } from '../../../../state.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { get_rune } from '../../../scope.js'; import { get_rune } from '../../../scope.js';
import { transform_inspect_rune } from '../../utils.js'; import { transform_inspect_rune } from '../../utils.js';

@ -2,7 +2,7 @@
/** @import { Context } from '../types.js' */ /** @import { Context } from '../types.js' */
/** @import { StateField } from '../../client/types.js' */ /** @import { StateField } from '../../client/types.js' */
import { dev } from '../../../../state.js'; import { dev } from '../../../../state.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { get_rune } from '../../../scope.js'; import { get_rune } from '../../../scope.js';
/** /**

@ -1,6 +1,6 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { build_inline_component } from './shared/component.js'; import { build_inline_component } from './shared/component.js';
/** /**

@ -1,7 +1,7 @@
/** @import { Expression, Pattern } from 'estree' */ /** @import { Expression, Pattern } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.ConstTag} node * @param {AST.ConstTag} node

@ -1,7 +1,7 @@
/** @import { Expression } from 'estree' */ /** @import { Expression } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.DebugTag} node * @param {AST.DebugTag} node

@ -1,8 +1,8 @@
/** @import { BlockStatement, Expression, Pattern, Statement } from 'estree' */ /** @import { BlockStatement, Expression, Statement } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import { BLOCK_OPEN_ELSE } from '../../../../../internal/server/hydration.js'; import { BLOCK_OPEN_ELSE } from '../../../../../internal/server/hydration.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { block_close, block_open } from './shared/utils.js'; import { block_close, block_open } from './shared/utils.js';
/** /**

@ -1,6 +1,6 @@
/** @import { ExpressionStatement } from 'estree' */ /** @import { ExpressionStatement } from 'estree' */
/** @import { Context } from '../types.js' */ /** @import { Context } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { get_rune } from '../../../scope.js'; import { get_rune } from '../../../scope.js';
/** /**

@ -1,7 +1,7 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext, ComponentServerTransformState } from '../types.js' */ /** @import { ComponentContext, ComponentServerTransformState } from '../types.js' */
import { clean_nodes, infer_namespace } from '../../utils.js'; import { clean_nodes, infer_namespace } from '../../utils.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { empty_comment, process_children, build_template } from './shared/utils.js'; import { empty_comment, process_children, build_template } from './shared/utils.js';
/** /**

@ -1,7 +1,7 @@
/** @import { Expression } from 'estree' */ /** @import { Expression } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.HtmlTag} node * @param {AST.HtmlTag} node

@ -1,7 +1,7 @@
/** @import { Identifier, Node } from 'estree' */ /** @import { Identifier, Node } from 'estree' */
/** @import { Context } from '../types.js' */ /** @import { Context } from '../types.js' */
import is_reference from 'is-reference'; import is_reference from 'is-reference';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { build_getter } from './shared/utils.js'; import { build_getter } from './shared/utils.js';
/** /**

@ -1,8 +1,8 @@
/** @import { BlockStatement, Expression, IfStatement } from 'estree' */ /** @import { BlockStatement, Expression } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import { BLOCK_OPEN_ELSE } from '../../../../../internal/server/hydration.js'; import { BLOCK_OPEN_ELSE } from '../../../../../internal/server/hydration.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { block_close, block_open } from './shared/utils.js'; import { block_close, block_open } from './shared/utils.js';
/** /**

@ -1,6 +1,6 @@
/** @import { ExpressionStatement, LabeledStatement } from 'estree' */ /** @import { ExpressionStatement, LabeledStatement } from 'estree' */
/** @import { Context } from '../types.js' */ /** @import { Context } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {LabeledStatement} node * @param {LabeledStatement} node

@ -1,6 +1,6 @@
/** @import { MemberExpression } from 'estree' */ /** @import { MemberExpression } from 'estree' */
/** @import { Context } from '../types.js' */ /** @import { Context } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {MemberExpression} node * @param {MemberExpression} node

@ -1,6 +1,6 @@
/** @import { Expression, PropertyDefinition } from 'estree' */ /** @import { Expression, PropertyDefinition } from 'estree' */
/** @import { Context } from '../types.js' */ /** @import { Context } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { get_rune } from '../../../scope.js'; import { get_rune } from '../../../scope.js';
/** /**

@ -4,7 +4,7 @@
/** @import { Scope } from '../../../scope.js' */ /** @import { Scope } from '../../../scope.js' */
import { is_void } from '../../../../../utils.js'; import { is_void } from '../../../../../utils.js';
import { dev, locator } from '../../../../state.js'; import { dev, locator } from '../../../../state.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { clean_nodes, determine_namespace_for_children } from '../../utils.js'; import { clean_nodes, determine_namespace_for_children } from '../../utils.js';
import { build_element_attributes } from './shared/element.js'; import { build_element_attributes } from './shared/element.js';
import { process_children, build_template } from './shared/utils.js'; import { process_children, build_template } from './shared/utils.js';

@ -2,7 +2,7 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import { unwrap_optional } from '../../../../utils/ast.js'; import { unwrap_optional } from '../../../../utils/ast.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { empty_comment } from './shared/utils.js'; import { empty_comment } from './shared/utils.js';
/** /**

@ -1,7 +1,7 @@
/** @import { BlockStatement, Expression, Literal, Property } from 'estree' */ /** @import { BlockStatement, Expression, Literal, Property } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { empty_comment, build_attribute_value } from './shared/utils.js'; import { empty_comment, build_attribute_value } from './shared/utils.js';
/** /**

@ -1,28 +1,35 @@
/** @import { BlockStatement } from 'estree' */ /** @import { ArrowFunctionExpression, BlockStatement, CallExpression } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import { dev } from '../../../../state.js'; import { dev } from '../../../../state.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.SnippetBlock} node * @param {AST.SnippetBlock} node
* @param {ComponentContext} context * @param {ComponentContext} context
*/ */
export function SnippetBlock(node, context) { export function SnippetBlock(node, context) {
const fn = b.function_declaration( const body = /** @type {BlockStatement} */ (context.visit(node.body));
node.expression,
[b.id('$$payload'), ...node.parameters],
/** @type {BlockStatement} */ (context.visit(node.body))
);
if (dev) { if (dev) {
fn.body.body.unshift(b.stmt(b.call('$.validate_snippet_args', b.id('$$payload')))); body.body.unshift(b.stmt(b.call('$.validate_snippet_args', b.id('$$payload'))));
} }
/** @type {ArrowFunctionExpression | CallExpression} */
let fn = b.arrow([b.id('$$payload'), ...node.parameters], body);
if (dev) {
fn = b.call('$.prevent_snippet_stringification', fn);
}
const declaration = b.declaration('const', [b.declarator(node.expression, fn)]);
// @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone // @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone
fn.___snippet = true; fn.___snippet = true;
if (node.metadata.can_hoist) { if (node.metadata.can_hoist) {
context.state.hoisted.push(fn); context.state.hoisted.push(declaration);
} else { } else {
context.state.init.push(fn); context.state.init.push(declaration);
} }
} }

@ -2,7 +2,7 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import { BLOCK_CLOSE, BLOCK_OPEN } from '../../../../../internal/server/hydration.js'; import { BLOCK_CLOSE, BLOCK_OPEN } from '../../../../../internal/server/hydration.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.SvelteBoundary} node * @param {AST.SvelteBoundary} node

@ -3,7 +3,7 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import { dev, locator } from '../../../../state.js'; import { dev, locator } from '../../../../state.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { determine_namespace_for_children } from '../../utils.js'; import { determine_namespace_for_children } from '../../utils.js';
import { build_element_attributes } from './shared/element.js'; import { build_element_attributes } from './shared/element.js';
import { build_template } from './shared/utils.js'; import { build_template } from './shared/utils.js';

@ -1,7 +1,7 @@
/** @import { BlockStatement } from 'estree' */ /** @import { BlockStatement } from 'estree' */
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {AST.SvelteHead} node * @param {AST.SvelteHead} node

@ -1,6 +1,6 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { build_inline_component } from './shared/component.js'; import { build_inline_component } from './shared/component.js';
/** /**

@ -1,6 +1,6 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */ /** @import { ComponentContext } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { process_children, build_template } from './shared/utils.js'; import { process_children, build_template } from './shared/utils.js';
/** /**

@ -1,6 +1,6 @@
/** @import { UpdateExpression } from 'estree' */ /** @import { UpdateExpression } from 'estree' */
/** @import { Context } from '../types.js' */ /** @import { Context } from '../types.js' */
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
/** /**
* @param {UpdateExpression} node * @param {UpdateExpression} node

@ -3,7 +3,7 @@
/** @import { Context } from '../types.js' */ /** @import { Context } from '../types.js' */
/** @import { Scope } from '../../../scope.js' */ /** @import { Scope } from '../../../scope.js' */
import { build_fallback, extract_paths } from '../../../../utils/ast.js'; import { build_fallback, extract_paths } from '../../../../utils/ast.js';
import * as b from '../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { get_rune } from '../../../scope.js'; import { get_rune } from '../../../scope.js';
import { walk } from 'zimmerframe'; import { walk } from 'zimmerframe';

@ -2,8 +2,9 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../../types.js' */ /** @import { ComponentContext } from '../../types.js' */
import { empty_comment, build_attribute_value } from './utils.js'; import { empty_comment, build_attribute_value } from './utils.js';
import * as b from '../../../../../utils/builders.js'; import * as b from '#compiler/builders';
import { is_element_node } from '../../../../nodes.js'; import { is_element_node } from '../../../../nodes.js';
import { dev } from '../../../../../state.js';
/** /**
* @param {AST.Component | AST.SvelteComponent | AST.SvelteSelf} node * @param {AST.Component | AST.SvelteComponent | AST.SvelteSelf} node
@ -238,7 +239,13 @@ export function build_inline_component(node, expression, context) {
) )
) { ) {
// create `children` prop... // create `children` prop...
push_prop(b.prop('init', b.id('children'), slot_fn)); push_prop(
b.prop(
'init',
b.id('children'),
dev ? b.call('$.prevent_snippet_stringification', slot_fn) : slot_fn
)
);
// and `$$slots.default: true` so that `<slot>` on the child works // and `$$slots.default: true` so that `<slot>` on the child works
serialized_slots.push(b.init(slot_name, b.true)); serialized_slots.push(b.init(slot_name, b.true));

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save