mirror of https://github.com/sveltejs/svelte
commit
a72c3650c9
@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'svelte': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
perf: use `createElement` instead of `createElementNS` for HTML elements
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'svelte': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
perf: store `current_sources` as a `Set` for O(1) membership checks
|
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: correctly transform references to earlier declarators in a declaration tag (e.g. `{let a = $state(0), b = $derived(a * 2)}`)
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: avoid spurious `state_referenced_locally` warnings for `$derived` declarations in declaration tags
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: tolerate whitespace before `let`/`const` in declaration tags
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: prevent infinite loop when a tag's expression ends with a trailing `/` at the end of the input
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: more robust parsing of declaration tags with regards to `type`
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: preserve newlines in spread input values when the `type` attribute is applied after `value`
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: update `SvelteURLSearchParams` when setting duplicate keys to the same joined value
|
||||||
@ -1,7 +1,7 @@
|
|||||||
<!-- generated in ../../../../../packages/svelte/scripts/generate-browser-support.ts. do not edit -->
|
<!-- generated in ../../../../../packages/svelte/scripts/generate-browser-support.ts. do not edit -->
|
||||||
|
|
||||||
| Feature | Chrome/Edge | Firefox | Safari |
|
| Feature | Chrome/Edge | Firefox | Safari |
|
||||||
| --- | ---: | ---: | ---: |
|
| - | - | - | - |
|
||||||
| `$state.snapshot` | 98 | 94 | 15.4 |
|
| [`$state.snapshot`](/docs/svelte/$state#$state.snapshot) | 98 | 94 | 15.4 |
|
||||||
| `bind:devicePixelContentBoxSize` | <span style="color: var(--sk-fg-4)">—</span> | 93 | not supported |
|
| [`bind:devicePixelContentBoxSize`](/docs/svelte/bind#Dimensions) | <span style="color: var(--sk-fg-4)">—</span> | 93 | not supported |
|
||||||
| `flip` from `svelte/animate` | <span style="color: var(--sk-fg-4)">—</span> | 126 | <span style="color: var(--sk-fg-4)">—</span> |
|
| [`flip` from `svelte/animate`](/docs/svelte/svelte-animate#flip) | <span style="color: var(--sk-fg-4)">—</span> | 126 | <span style="color: var(--sk-fg-4)">—</span> |
|
||||||
|
|||||||
@ -0,0 +1,60 @@
|
|||||||
|
/** @import { AST } from '#compiler' */
|
||||||
|
/** @import { Context } from '../types' */
|
||||||
|
import * as b from '#compiler/builders';
|
||||||
|
import * as e from '../../../errors.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {AST.DeclarationTag} node
|
||||||
|
* @param {Context} context
|
||||||
|
*/
|
||||||
|
export function DeclarationTag(node, context) {
|
||||||
|
if (!context.state.analysis.runes && !context.state.analysis.maybe_runes) {
|
||||||
|
e.declaration_tag_no_legacy_mode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.visit(node.declaration, {
|
||||||
|
...context.state,
|
||||||
|
in_declaration_tag: true,
|
||||||
|
// the declaration lives in the fragment scope, which is one level deeper than the
|
||||||
|
// `function_depth` we're tracking here (`set_scope` doesn't update `function_depth`).
|
||||||
|
// align them so that `state_referenced_locally` warnings are calculated correctly
|
||||||
|
function_depth: context.state.scope.function_depth,
|
||||||
|
expression: node.metadata.expression
|
||||||
|
});
|
||||||
|
|
||||||
|
mark_async_declaration(context, node.metadata, node.declaration.declarations);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Context} context
|
||||||
|
* @param {AST.ConstTag['metadata'] | AST.DeclarationTag['metadata']} metadata
|
||||||
|
* @param {import('estree').VariableDeclarator[]} declarations
|
||||||
|
*/
|
||||||
|
export function mark_async_declaration(context, metadata, declarations) {
|
||||||
|
const has_await = metadata.expression.has_await;
|
||||||
|
const blockers = [...metadata.expression.dependencies]
|
||||||
|
.map((dep) => dep.blocker)
|
||||||
|
.filter((b) => b !== null && b.object !== context.state.async_consts?.id);
|
||||||
|
|
||||||
|
if (has_await || context.state.async_consts || blockers.length > 0) {
|
||||||
|
const run = (context.state.async_consts ??= {
|
||||||
|
id: context.state.analysis.root.unique('promises'),
|
||||||
|
declaration_count: 0
|
||||||
|
});
|
||||||
|
metadata.promises_id = run.id;
|
||||||
|
|
||||||
|
const bindings = declarations.flatMap((declaration) =>
|
||||||
|
context.state.scope.get_bindings(declaration)
|
||||||
|
);
|
||||||
|
|
||||||
|
// keep the counter in sync with the number of thunks pushed in transform
|
||||||
|
// TODO 6.0 once non-async and non-runes mode is gone investigate making this more robust
|
||||||
|
// via something like the approach in https://github.com/sveltejs/svelte/pull/18032
|
||||||
|
const length = run.declaration_count + (blockers.length > 0 ? 1 : 0);
|
||||||
|
run.declaration_count += blockers.length > 0 ? 2 : 1;
|
||||||
|
const blocker = b.member(run.id, b.literal(length), true);
|
||||||
|
for (const binding of bindings) {
|
||||||
|
binding.blocker = blocker;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
/** @import { Expression, Identifier, Pattern, Statement, ExpressionStatement, VariableDeclaration } from 'estree' */
|
||||||
|
/** @import { AST } from '#compiler' */
|
||||||
|
/** @import { ComponentContext } from '../types' */
|
||||||
|
import { extract_identifiers, has_await_expression } from '../../../../utils/ast.js';
|
||||||
|
import * as b from '#compiler/builders';
|
||||||
|
import { add_state_transformers } from './shared/declarations.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {AST.DeclarationTag} node
|
||||||
|
* @param {ComponentContext} context
|
||||||
|
*/
|
||||||
|
export function DeclarationTag(node, context) {
|
||||||
|
// register the transformers _before_ visiting the declaration, so that
|
||||||
|
// later declarators can reference earlier ones (e.g. `{let a = $state(0), b = $derived(a * 2)}`)
|
||||||
|
add_state_transformers(context);
|
||||||
|
const declaration = /** @type {Statement | undefined} */ (context.visit(node.declaration));
|
||||||
|
|
||||||
|
if (
|
||||||
|
node.metadata.promises_id &&
|
||||||
|
node.declaration.type === 'VariableDeclaration' &&
|
||||||
|
declaration?.type === 'VariableDeclaration'
|
||||||
|
) {
|
||||||
|
const { ids, assignments } = build_async_declaration_parts(declaration);
|
||||||
|
add_async_declaration(context, node.metadata, ids, assignments, declaration.kind);
|
||||||
|
} else {
|
||||||
|
context.state.consts.push(declaration ?? node.declaration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {VariableDeclaration} declaration
|
||||||
|
*/
|
||||||
|
export function build_async_declaration_parts(declaration) {
|
||||||
|
const ids = new Map();
|
||||||
|
for (const declarator of declaration.declarations) {
|
||||||
|
for (const id of extract_identifiers(declarator.id)) {
|
||||||
|
ids.set(id.name, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const assignments = declaration.declarations
|
||||||
|
.filter((declarator) => declarator.init !== null)
|
||||||
|
.map((declarator) =>
|
||||||
|
b.stmt(
|
||||||
|
b.assignment(
|
||||||
|
'=',
|
||||||
|
/** @type {Pattern} */ (declarator.id),
|
||||||
|
/** @type {Expression} */ (declarator.init)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return { ids: [...ids.values()], assignments };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ComponentContext} context
|
||||||
|
* @param {AST.ConstTag['metadata'] | AST.DeclarationTag['metadata']} metadata
|
||||||
|
* @param {Identifier[]} ids
|
||||||
|
* @param {ExpressionStatement[]} assignments
|
||||||
|
* @param {VariableDeclaration['kind']} [kind]
|
||||||
|
*/
|
||||||
|
export function add_async_declaration(context, metadata, ids, assignments, kind = 'let') {
|
||||||
|
const run = (context.state.async_consts ??= {
|
||||||
|
id: /** @type {Identifier} */ (metadata.promises_id),
|
||||||
|
thunks: []
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const id of ids) {
|
||||||
|
context.state.consts.push(kind === 'var' ? b.var(id.name) : b.let(id.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockers = [...metadata.expression.dependencies]
|
||||||
|
.map((dep) => dep.blocker)
|
||||||
|
.filter((b) => b !== null && b.object !== context.state.async_consts?.id);
|
||||||
|
|
||||||
|
if (blockers.length === 1) {
|
||||||
|
run.thunks.push(b.thunk(b.member(/** @type {Expression} */ (blockers[0]), 'promise')));
|
||||||
|
} else if (blockers.length > 0) {
|
||||||
|
run.thunks.push(b.thunk(b.call('$.wait', b.array(blockers))));
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep the number of thunks pushed in sync with analysis phase
|
||||||
|
const has_await =
|
||||||
|
metadata.expression.has_await ||
|
||||||
|
assignments.some((assignment) => has_await_expression(assignment));
|
||||||
|
const body = assignments.length === 1 ? assignments[0].expression : b.block(assignments);
|
||||||
|
run.thunks.push(b.thunk(body, has_await));
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
/** @import { Expression, Identifier, Pattern, Statement, ExpressionStatement, VariableDeclaration } from 'estree' */
|
||||||
|
/** @import { AST } from '#compiler' */
|
||||||
|
/** @import { ComponentContext } from '../types.js' */
|
||||||
|
import { extract_identifiers, has_await_expression } from '../../../../utils/ast.js';
|
||||||
|
import * as b from '#compiler/builders';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {AST.DeclarationTag} node
|
||||||
|
* @param {ComponentContext} context
|
||||||
|
*/
|
||||||
|
export function DeclarationTag(node, context) {
|
||||||
|
const declaration = /** @type {Statement} */ (context.visit(node.declaration));
|
||||||
|
|
||||||
|
if (
|
||||||
|
node.metadata.promises_id &&
|
||||||
|
node.declaration.type === 'VariableDeclaration' &&
|
||||||
|
declaration.type === 'VariableDeclaration'
|
||||||
|
) {
|
||||||
|
const { ids, assignments } = build_async_declaration_parts(declaration);
|
||||||
|
add_async_declaration(context, node.metadata, ids, assignments, declaration.kind);
|
||||||
|
} else {
|
||||||
|
context.state.init.push(declaration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {VariableDeclaration} declaration
|
||||||
|
*/
|
||||||
|
export function build_async_declaration_parts(declaration) {
|
||||||
|
const ids = new Map();
|
||||||
|
for (const declarator of declaration.declarations) {
|
||||||
|
for (const id of extract_identifiers(declarator.id)) {
|
||||||
|
ids.set(id.name, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const assignments = declaration.declarations
|
||||||
|
.filter((declarator) => declarator.init !== null)
|
||||||
|
.map((declarator) =>
|
||||||
|
b.stmt(
|
||||||
|
b.assignment(
|
||||||
|
'=',
|
||||||
|
/** @type {Pattern} */ (declarator.id),
|
||||||
|
/** @type {Expression} */ (declarator.init)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return { ids: [...ids.values()], assignments };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ComponentContext} context
|
||||||
|
* @param {AST.ConstTag['metadata'] | AST.DeclarationTag['metadata']} metadata
|
||||||
|
* @param {Identifier[]} ids
|
||||||
|
* @param {ExpressionStatement[]} assignments
|
||||||
|
* @param {VariableDeclaration['kind']} [kind]
|
||||||
|
*/
|
||||||
|
export function add_async_declaration(context, metadata, ids, assignments, kind = 'let') {
|
||||||
|
const run = (context.state.async_consts ??= {
|
||||||
|
id: /** @type {Identifier} */ (metadata.promises_id),
|
||||||
|
thunks: []
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const id of ids) {
|
||||||
|
context.state.init.push(kind === 'var' ? b.var(id.name) : b.let(id.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockers = [...metadata.expression.dependencies]
|
||||||
|
.map((dep) => dep.blocker)
|
||||||
|
.filter((b) => b !== null && b.object !== context.state.async_consts?.id);
|
||||||
|
|
||||||
|
if (blockers.length === 1) {
|
||||||
|
run.thunks.push(b.thunk(/** @type {Expression} */ (blockers[0])));
|
||||||
|
} else if (blockers.length > 0) {
|
||||||
|
run.thunks.push(b.thunk(b.call('Promise.all', b.array(blockers))));
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep the number of thunks pushed in sync with analysis phase
|
||||||
|
const has_await =
|
||||||
|
metadata.expression.has_await ||
|
||||||
|
assignments.some((assignment) => has_await_expression(assignment));
|
||||||
|
const body = assignments.length === 1 ? assignments[0].expression : b.block(assignments);
|
||||||
|
run.thunks.push(b.thunk(body, has_await));
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
// A tag whose expression ends with a bare `/` at the end of the input used to
|
||||||
|
// make `find_matching_bracket` loop forever; it should error instead of hanging.
|
||||||
|
export default test({
|
||||||
|
error: {
|
||||||
|
code: 'unexpected_eof',
|
||||||
|
message: 'Unexpected end of input',
|
||||||
|
position: [12, 12]
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1 @@
|
|||||||
|
{let x = a /
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
{#if true}
|
||||||
|
{let }
|
||||||
|
{const x = }
|
||||||
|
{let x = a / }
|
||||||
|
{/if}
|
||||||
@ -0,0 +1,145 @@
|
|||||||
|
{
|
||||||
|
"css": null,
|
||||||
|
"js": [],
|
||||||
|
"start": 0,
|
||||||
|
"end": 54,
|
||||||
|
"type": "Root",
|
||||||
|
"fragment": {
|
||||||
|
"type": "Fragment",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "IfBlock",
|
||||||
|
"elseif": false,
|
||||||
|
"start": 0,
|
||||||
|
"end": 54,
|
||||||
|
"test": {
|
||||||
|
"type": "Literal",
|
||||||
|
"start": 5,
|
||||||
|
"end": 9,
|
||||||
|
"loc": {
|
||||||
|
"start": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 5
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 9
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"value": true,
|
||||||
|
"raw": "true"
|
||||||
|
},
|
||||||
|
"consequent": {
|
||||||
|
"type": "Fragment",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "Text",
|
||||||
|
"start": 10,
|
||||||
|
"end": 12,
|
||||||
|
"raw": "\n\t",
|
||||||
|
"data": "\n\t"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "DeclarationTag",
|
||||||
|
"start": 12,
|
||||||
|
"end": 18,
|
||||||
|
"declaration": {
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"kind": "let",
|
||||||
|
"declarations": [
|
||||||
|
{
|
||||||
|
"type": "VariableDeclarator",
|
||||||
|
"id": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "",
|
||||||
|
"start": 17,
|
||||||
|
"end": 17
|
||||||
|
},
|
||||||
|
"init": null,
|
||||||
|
"start": 17,
|
||||||
|
"end": 17
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start": 13,
|
||||||
|
"end": 17
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Text",
|
||||||
|
"start": 18,
|
||||||
|
"end": 20,
|
||||||
|
"raw": "\n\t",
|
||||||
|
"data": "\n\t"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "DeclarationTag",
|
||||||
|
"start": 20,
|
||||||
|
"end": 32,
|
||||||
|
"declaration": {
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"kind": "const",
|
||||||
|
"declarations": [
|
||||||
|
{
|
||||||
|
"type": "VariableDeclarator",
|
||||||
|
"id": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "",
|
||||||
|
"start": 31,
|
||||||
|
"end": 31
|
||||||
|
},
|
||||||
|
"init": null,
|
||||||
|
"start": 31,
|
||||||
|
"end": 31
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start": 21,
|
||||||
|
"end": 31
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Text",
|
||||||
|
"start": 32,
|
||||||
|
"end": 34,
|
||||||
|
"raw": "\n\t",
|
||||||
|
"data": "\n\t"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "DeclarationTag",
|
||||||
|
"start": 34,
|
||||||
|
"end": 48,
|
||||||
|
"declaration": {
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"kind": "let",
|
||||||
|
"declarations": [
|
||||||
|
{
|
||||||
|
"type": "VariableDeclarator",
|
||||||
|
"id": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"name": "",
|
||||||
|
"start": 47,
|
||||||
|
"end": 47
|
||||||
|
},
|
||||||
|
"init": null,
|
||||||
|
"start": 47,
|
||||||
|
"end": 47
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start": 35,
|
||||||
|
"end": 47
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Text",
|
||||||
|
"start": 48,
|
||||||
|
"end": 49,
|
||||||
|
"raw": "\n",
|
||||||
|
"data": "\n"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"alternate": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": null
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
{type instanceof /* probe */ Object}
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"css": null,
|
||||||
|
"js": [],
|
||||||
|
"start": 0,
|
||||||
|
"end": 36,
|
||||||
|
"type": "Root",
|
||||||
|
"fragment": {
|
||||||
|
"type": "Fragment",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "ExpressionTag",
|
||||||
|
"start": 0,
|
||||||
|
"end": 36,
|
||||||
|
"expression": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"start": 1,
|
||||||
|
"end": 35,
|
||||||
|
"loc": {
|
||||||
|
"start": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 1
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 35
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start": 1,
|
||||||
|
"end": 5,
|
||||||
|
"loc": {
|
||||||
|
"start": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 1
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "type"
|
||||||
|
},
|
||||||
|
"operator": "instanceof",
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"start": 29,
|
||||||
|
"end": 35,
|
||||||
|
"loc": {
|
||||||
|
"start": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 29
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 35
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "Object",
|
||||||
|
"leadingComments": [
|
||||||
|
{
|
||||||
|
"type": "Block",
|
||||||
|
"value": " probe ",
|
||||||
|
"start": 17,
|
||||||
|
"end": 28
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": null,
|
||||||
|
"comments": [
|
||||||
|
{
|
||||||
|
"type": "Block",
|
||||||
|
"value": " probe ",
|
||||||
|
"start": 17,
|
||||||
|
"end": 28,
|
||||||
|
"loc": {
|
||||||
|
"start": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 17
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 1,
|
||||||
|
"column": 28
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
{#if visible}
|
||||||
|
{let count = 1}
|
||||||
|
{const doubled = count * 2}
|
||||||
|
{const label = 'count'}
|
||||||
|
{const format = (value) => `${label}: ${value}`}
|
||||||
|
<p>{format(doubled)}</p>
|
||||||
|
{/if}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
{#if visible}
|
||||||
|
{let count = 1}
|
||||||
|
{const doubled = count * 2}
|
||||||
|
{const label = 'count'}
|
||||||
|
{const format = (value) => `${label}: ${value}`}
|
||||||
|
<p>{format(doubled)}</p>
|
||||||
|
{/if}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { tick } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
mode: ['async-server', 'client', 'hydrate'],
|
||||||
|
ssrHtml: `<h1>Hello, world!</h1> 5 01234 5 sync 6 5 0 10`,
|
||||||
|
|
||||||
|
async test({ assert, target }) {
|
||||||
|
await tick();
|
||||||
|
|
||||||
|
assert.htmlEqual(target.innerHTML, `<h1>Hello, world!</h1> 5 01234 5 sync 6 5 0 10`);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
<script>
|
||||||
|
let name = $state('world');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:boundary>
|
||||||
|
{const sync = 'sync'}
|
||||||
|
{const number = await Promise.resolve(5)}
|
||||||
|
{const after_async =number + 1}
|
||||||
|
{const { length, 0: first } = await '01234'}
|
||||||
|
|
||||||
|
{#snippet greet()}
|
||||||
|
{const greeting = $derived(await `Hello, ${name}!`)}
|
||||||
|
<h1>{greeting}</h1>
|
||||||
|
{number}
|
||||||
|
{#if number > 4 && after_async && greeting}
|
||||||
|
{const length = $derived(await number)}
|
||||||
|
{#each { length }, index}
|
||||||
|
{const i = $derived(await index)}
|
||||||
|
{i}
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
{@render greet()}
|
||||||
|
{number} {sync} {after_async} {length} {first}
|
||||||
|
|
||||||
|
{#if sync}
|
||||||
|
{const double = $derived(number * 2)}
|
||||||
|
{double}
|
||||||
|
{/if}
|
||||||
|
</svelte:boundary>
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import { tick } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
async test({ assert, target }) {
|
||||||
|
await tick();
|
||||||
|
const [top, change] = target.querySelectorAll('button');
|
||||||
|
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<button>name</button>
|
||||||
|
<button>change name</button>
|
||||||
|
<p>Hello name</p>
|
||||||
|
<div><span>nested Hi name</span></div>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
top.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<button>other</button>
|
||||||
|
<button>change name</button>
|
||||||
|
<p>Hello name</p>
|
||||||
|
<div><span>nested Hi name</span></div>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
change.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`
|
||||||
|
<button>other</button>
|
||||||
|
<button>change name</button>
|
||||||
|
<p>Hello other</p>
|
||||||
|
<div><span>nested Hi other</span></div>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
<script>
|
||||||
|
const id = 'name';
|
||||||
|
const top_id = await 'name';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{let name = $state(top_id)}
|
||||||
|
<button onclick={() => name = 'other'}>{name}</button>
|
||||||
|
|
||||||
|
{#if id}
|
||||||
|
{let name = $state(await id)}
|
||||||
|
{let greeting = $derived(await `Hello ${name}`)}
|
||||||
|
|
||||||
|
<button onclick={() => name = 'other'}>change name</button>
|
||||||
|
<p>{greeting}</p>
|
||||||
|
<div>
|
||||||
|
{const nested = 'nested'}
|
||||||
|
{const greeting2 = $derived(await `Hi ${name}`)}
|
||||||
|
<span>{nested} {greeting2}</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import { tick } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
html: `<button>increment</button><p>count: 0</p><p>doubled: 0</p><p>quadrupled: 0</p>`,
|
||||||
|
async test({ assert, target }) {
|
||||||
|
const [button] = target.querySelectorAll('button');
|
||||||
|
|
||||||
|
button.click();
|
||||||
|
await tick();
|
||||||
|
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`<button>increment</button><p>count: 1</p><p>doubled: 2</p><p>quadrupled: 4</p>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
<!-- a later declarator can reference an earlier one within the same tag... -->
|
||||||
|
{let count = $state(0), doubled = $derived(count * 2)}
|
||||||
|
|
||||||
|
<!-- ...and leading whitespace is tolerated -->
|
||||||
|
{ let quadrupled = $derived(doubled * 2) }
|
||||||
|
|
||||||
|
<button onclick={() => (count += 1)}>increment</button>
|
||||||
|
<p>count: {count}</p>
|
||||||
|
<p>doubled: {doubled}</p>
|
||||||
|
<p>quadrupled: {quadrupled}</p>
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { tick } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
html: '<button>0 | 0</button>',
|
||||||
|
async test({ assert, target }) {
|
||||||
|
const [increment] = target.querySelectorAll('button');
|
||||||
|
|
||||||
|
increment.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(target.innerHTML, '<button>1 | 2</button>');
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
{let count = $state(0)}
|
||||||
|
{let doubled = $derived(count * 2)}
|
||||||
|
<button onclick={() => (count += 1)}>{count} | {doubled}</button>
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { tick } from 'svelte';
|
||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
html: `<button>top 2</button><button>toggle</button><button>2</button><p>4 total</p><div><span>nested</span></div><div><span>nested</span></div>`,
|
||||||
|
async test({ assert, target }) {
|
||||||
|
const [top, toggle, increment] = target.querySelectorAll('button');
|
||||||
|
|
||||||
|
top.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`<button>top 4</button><button>toggle</button><button>2</button><p>4 total</p><div><span>nested</span></div><div><span>nested</span></div>`
|
||||||
|
);
|
||||||
|
|
||||||
|
increment.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`<button>top 4</button><button>toggle</button><button>3</button><p>6 total</p><div><span>nested</span></div><div><span>nested</span></div>`
|
||||||
|
);
|
||||||
|
|
||||||
|
toggle.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`<button>top 4</button><button>toggle</button><div><span>nested</span></div>`
|
||||||
|
);
|
||||||
|
|
||||||
|
toggle.click();
|
||||||
|
await tick();
|
||||||
|
assert.htmlEqual(
|
||||||
|
target.innerHTML,
|
||||||
|
`<button>top 4</button><button>toggle</button><button>2</button><p>4 total</p><div><span>nested</span></div><div><span>nested</span></div>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
<script>
|
||||||
|
let visible = $state(true);
|
||||||
|
let initial = $state(2);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{let top = $state(1)}
|
||||||
|
{let top_doubled = $derived(top * 2)}
|
||||||
|
|
||||||
|
<button onclick={() => (top += 1)}>top {top_doubled}</button>
|
||||||
|
<button onclick={() => (visible = !visible)}>toggle</button>
|
||||||
|
|
||||||
|
{#if visible}
|
||||||
|
{let counter = $state({ value: initial })}
|
||||||
|
{let doubled = $derived(counter.value * 2)}
|
||||||
|
{const suffix = ' total'}
|
||||||
|
{const format = (value) => `${value}${suffix}`}
|
||||||
|
|
||||||
|
<button onclick={() => (counter.value += 1)}>{counter.value}</button>
|
||||||
|
<p>{format(doubled)}</p>
|
||||||
|
<div>
|
||||||
|
{const doubled = 'nested'}
|
||||||
|
<span>{doubled}</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{const nested = 'nested'}
|
||||||
|
<span>{nested}</span>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
const value = 'line1\nline2\nline3';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
mode: ['client'],
|
||||||
|
test({ assert, target }) {
|
||||||
|
const [spread_first, type_first] = target.querySelectorAll('input');
|
||||||
|
|
||||||
|
assert.equal(spread_first?.type, 'hidden');
|
||||||
|
assert.equal(spread_first?.value, value);
|
||||||
|
assert.equal(type_first?.type, 'hidden');
|
||||||
|
assert.equal(type_first?.value, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
<script>
|
||||||
|
const value = 'line1\nline2\nline3';
|
||||||
|
const spread = { name: 'field', value };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<input {...spread} type="hidden" />
|
||||||
|
<input type="hidden" {...spread} />
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
import 'svelte/internal/disclose-version';
|
||||||
|
import * as $ from 'svelte/internal/client';
|
||||||
|
|
||||||
|
var root = $.from_html(`<p class="x">hello</p>`);
|
||||||
|
var root_1 = $.from_html(`<!> <!>`, 1);
|
||||||
|
|
||||||
|
export default function Dedupe_templates($$anchor, $$props) {
|
||||||
|
var fragment = root_1();
|
||||||
|
var node = $.first_child(fragment);
|
||||||
|
|
||||||
|
{
|
||||||
|
var consequent = ($$anchor) => {
|
||||||
|
var p = root();
|
||||||
|
|
||||||
|
$.append($$anchor, p);
|
||||||
|
};
|
||||||
|
|
||||||
|
var alternate = ($$anchor) => {
|
||||||
|
var p_1 = root();
|
||||||
|
|
||||||
|
$.append($$anchor, p_1);
|
||||||
|
};
|
||||||
|
|
||||||
|
$.if(node, ($$render) => {
|
||||||
|
if ($$props.a) $$render(consequent); else $$render(alternate, -1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var node_1 = $.sibling(node, 2);
|
||||||
|
|
||||||
|
{
|
||||||
|
var consequent_1 = ($$anchor) => {
|
||||||
|
var p_2 = root();
|
||||||
|
|
||||||
|
$.append($$anchor, p_2);
|
||||||
|
};
|
||||||
|
|
||||||
|
$.if(node_1, ($$render) => {
|
||||||
|
if ($$props.b) $$render(consequent_1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$.append($$anchor, fragment);
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
import * as $ from 'svelte/internal/server';
|
||||||
|
|
||||||
|
export default function Dedupe_templates($$renderer, $$props) {
|
||||||
|
let { a, b } = $$props;
|
||||||
|
|
||||||
|
if (a) {
|
||||||
|
$$renderer.push('<!--[0-->');
|
||||||
|
$$renderer.push(`<p class="x">hello</p>`);
|
||||||
|
} else {
|
||||||
|
$$renderer.push('<!--[-1-->');
|
||||||
|
$$renderer.push(`<p class="x">hello</p>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
$$renderer.push(`<!--]--> `);
|
||||||
|
|
||||||
|
if (b) {
|
||||||
|
$$renderer.push('<!--[0-->');
|
||||||
|
$$renderer.push(`<p class="x">hello</p>`);
|
||||||
|
} else {
|
||||||
|
$$renderer.push('<!--[-1-->');
|
||||||
|
}
|
||||||
|
|
||||||
|
$$renderer.push(`<!--]-->`);
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
<script>
|
||||||
|
let { a, b } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- identical static markup repeated across branches should share one hoisted template -->
|
||||||
|
{#if a}
|
||||||
|
<p class="x">hello</p>
|
||||||
|
{:else}
|
||||||
|
<p class="x">hello</p>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if b}
|
||||||
|
<p class="x">hello</p>
|
||||||
|
{/if}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
{#if true}
|
||||||
|
{function foo() {}}
|
||||||
|
{/if}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"code": "declaration_tag_invalid_type",
|
||||||
|
"message": "Declaration tags must be `let` or `const` declarations",
|
||||||
|
"start": {
|
||||||
|
"line": 28,
|
||||||
|
"column": 2
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 28,
|
||||||
|
"column": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
<script lang="ts"></script>
|
||||||
|
|
||||||
|
{#if true}
|
||||||
|
<!-- check that these doesn't trigger already -->
|
||||||
|
{type}
|
||||||
|
{type }
|
||||||
|
{type && foo}
|
||||||
|
{type || bar}
|
||||||
|
{type % 2}
|
||||||
|
{type .x}
|
||||||
|
{type ?.x}
|
||||||
|
{type ()}
|
||||||
|
{type [1]}
|
||||||
|
{type `tag`}
|
||||||
|
{type === 'all' ? 'All types' : type}
|
||||||
|
{type !== 'all'}
|
||||||
|
{type == 'all'}
|
||||||
|
{type != 'all'}
|
||||||
|
{type + 1}
|
||||||
|
{type - 1}
|
||||||
|
{type * 2}
|
||||||
|
{type / 2}
|
||||||
|
{type > 1}
|
||||||
|
{type instanceof Foo}
|
||||||
|
{type instanceof /* comment */ Object}
|
||||||
|
{type in foo}
|
||||||
|
<!-- ... this one should trigger though -->
|
||||||
|
{type foo = boolean}
|
||||||
|
{/if}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"code": "declaration_tag_invalid_type",
|
||||||
|
"message": "Declaration tags must be `let` or `const` declarations",
|
||||||
|
"start": {
|
||||||
|
"line": 2,
|
||||||
|
"column": 2
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 2,
|
||||||
|
"column": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue