Proxied state (#9739)

* magic objects

* read length eagerly — triggers reconciliation

* nested magic

* tests

* more tests

* fix array memory leak

* skipped, partially passing array test

* Fix each

revert bad changes

* more 1337

* eliminate closures

* maybe this is unnecessary?

* only create sources for own properties

* more

* rename stuff

* shuffle things around

* emit $.proxy

* remove proxy helper from public API

* only create sources for writable properties

* update test

* get tests passing

* fix

* remove state-not-mutated warning, which is no longer valid

* track reassignments separately from mutations

* update test - effects no longer fire on mutations unnecessarily

* move util into utils file

* move each logic into its own module; breathe sigh of relief

* tweaks

* more tweaks

* improve runtime

* fix mistake

* ensure we proxy when assigning to state

* fix test

* handle frozen

* create sources when reading proxy properties inside deriveds

* only mutate in legacy mode

* add immutable to transform state

* remove unused second argument to derived

* remove unused second argument to source, and runtime immutable option to createRoot (not sure what that was doing there?)

* oops, backwards

* dedicated binding.kind for legacy reactive imports

* avoid using prop_source in more cases. bit hacky, could be tidier, but it works

* distinguish between source and mutable_source

* remove immutable option from mount

* remove unused apparatus around immutable option

* deprecate immutable

* fix

* tweak

* better default value handling

* remove unnecessary exports

* whitespace is free

* remove obsolete symbol from proxy

* cleanup ts

* improve runtime perf by removing version reads in has()

* add missing proxy call

* tune perf of has() more

* ensure we only create sources in effect_active()

* fix proxy of computed fields

* simplify and fix issue with indexed each blocks

* fix compiler errors around exported state

* only create source for state that is reassigned

* temporary fix, we should come up with something better than this

* readonly props

* fix test

* add test for bind:

* make readonly dev-only

* docs

* forbid setPrototypeOf

* lol whoops

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Dominic Gannaway <dg@domgan.com>
pull/9753/head
Rich Harris 7 months ago committed by GitHub
parent f0c47c31bc
commit 75fc09a79e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -167,8 +167,8 @@ const runes = {
'invalid-legacy-export': () => `Cannot use \`export let\` in runes mode — use $props instead`,
/** @param {string} rune */
'invalid-rune-usage': (rune) => `Cannot use ${rune} rune in non-runes mode`,
/** @param {string} rune */
'invalid-rune-export': (rune) => `Cannot export value created with ${rune}`,
'invalid-state-export': () => `Cannot export state if it is reassigned`,
'invalid-derived-export': () => `Cannot export derived state`,
'invalid-props-id': () => `$props() can only be used with an object destructuring pattern`,
'invalid-props-pattern': () =>
`$props() assignment must not contain nested properties or computed keys`,

@ -183,7 +183,7 @@ function get_delegated_event(node, context) {
// or any normal not reactive bindings that are mutated.
binding.kind === 'normal' ||
// or any reactive imports (those are rewritten) (can only happen in legacy mode)
(binding.kind === 'state' && binding.declaration_kind === 'import')) &&
binding.kind === 'legacy_reactive_import') &&
binding.mutated))
) {
return non_hoistable;
@ -221,20 +221,13 @@ export function analyze_module(ast, options) {
merge(set_scope(scopes), validation_runes_js, runes_scope_js_tweaker)
);
// If we are in runes mode, then check for possible misuses of state runes
for (const [, scope] of scopes) {
for (const [name, binding] of scope.declarations) {
if (binding.kind === 'state' && !binding.mutated) {
warn(warnings, binding.node, [], 'state-not-mutated', name);
}
}
}
return {
module: { ast, scope, scopes },
name: options.filename || 'module',
warnings,
accessors: false
accessors: false,
runes: true,
immutable: true
};
}
@ -315,6 +308,10 @@ export function analyze_component(root, options) {
const component_name = get_component_name(options.filename ?? 'Component');
const runes =
options.runes ??
Array.from(module.scope.references).some(([name]) => Runes.includes(/** @type {any} */ (name)));
// TODO remove all the ?? stuff, we don't need it now that we're validating the config
/** @type {import('../types.js').ComponentAnalysis} */
const analysis = {
@ -331,11 +328,8 @@ export function analyze_component(root, options) {
component_name,
get_css_hash: options.cssHash
}),
runes:
options.runes ??
Array.from(module.scope.references).some(([name]) =>
Runes.includes(/** @type {any} */ (name))
),
runes,
immutable: runes || options.immutable,
exports: [],
uses_props: false,
uses_rest_props: false,
@ -382,15 +376,6 @@ export function analyze_component(root, options) {
merge(set_scope(scopes), validation_runes, runes_scope_tweaker, common_visitors)
);
}
// If we are in runes mode, then check for possible misuses of state runes
for (const [, scope] of instance.scopes) {
for (const [name, binding] of scope.declarations) {
if (binding.kind === 'state' && !binding.mutated) {
warn(warnings, binding.node, [], 'state-not-mutated', name);
}
}
}
} else {
instance.scope.declare(b.id('$$props'), 'prop', 'synthetic');
instance.scope.declare(b.id('$$restProps'), 'rest_prop', 'synthetic');
@ -553,7 +538,7 @@ const legacy_scope_tweaker = {
(state.reactive_statement || state.ast_type === 'template') &&
parent.type === 'MemberExpression'
) {
binding.kind = 'state';
binding.kind = 'legacy_reactive_import';
}
} else if (
binding.mutated &&

@ -649,8 +649,14 @@ export const validation_legacy = merge(validation, a11y_validators, {
*/
function validate_export(node, scope, name) {
const binding = scope.get(name);
if (binding && (binding.kind === 'derived' || binding.kind === 'state')) {
error(node, 'invalid-rune-export', `$${binding.kind}`);
if (!binding) return;
if (binding.kind === 'derived') {
error(node, 'invalid-derived-export');
}
if (binding.kind === 'state' && binding.reassigned) {
error(node, 'invalid-state-export');
}
}

@ -159,7 +159,7 @@ export function client_component(source, analysis, options) {
// Very very dirty way of making import statements reactive in legacy mode if needed
if (!analysis.runes) {
for (const [name, binding] of analysis.module.scope.declarations) {
if (binding.kind === 'state' && binding.declaration_kind === 'import') {
if (binding.kind === 'legacy_reactive_import') {
instance.body.unshift(
b.var('$$_import_' + name, b.call('$.reactive_import', b.thunk(b.id(name))))
);
@ -175,7 +175,7 @@ export function client_component(source, analysis, options) {
for (const [name, binding] of analysis.instance.scope.declarations) {
if (binding.kind === 'legacy_reactive') {
legacy_reactive_declarations.push(b.const(name, b.call('$.source')));
legacy_reactive_declarations.push(b.const(name, b.call('$.mutable_source')));
}
if (binding.kind === 'store_sub') {
if (store_setup.length === 0) {
@ -240,11 +240,12 @@ export function client_component(source, analysis, options) {
const properties = analysis.exports.map(({ name, alias }) => {
const binding = analysis.instance.scope.get(name);
const is_source =
binding?.kind === 'state' && (!state.analysis.immutable || binding.reassigned);
// TODO This is always a getter because the `renamed-instance-exports` test wants it that way.
// Should we for code size reasons make it an init in runes mode and/or non-dev mode?
return b.get(alias ?? name, [
b.return(binding?.kind === 'state' ? b.call('$.get', b.id(name)) : b.id(name))
]);
return b.get(alias ?? name, [b.return(is_source ? b.call('$.get', b.id(name)) : b.id(name))]);
});
if (analysis.accessors) {
@ -261,14 +262,7 @@ export function client_component(source, analysis, options) {
}
const component_block = b.block([
b.stmt(
b.call(
'$.push',
b.id('$$props'),
b.literal(analysis.runes),
...(options.immutable ? [b.literal(true)] : [])
)
),
b.stmt(b.call('$.push', b.id('$$props'), b.literal(analysis.runes))),
...store_setup,
...legacy_reactive_declarations,
...group_binding_declarations,

@ -1,5 +1,5 @@
import * as b from '../../../utils/builders.js';
import { extract_paths } from '../../../utils/ast.js';
import { extract_paths, is_simple_expression } from '../../../utils/ast.js';
import { error } from '../../../errors.js';
/**
@ -67,19 +67,20 @@ export function serialize_get_binding(node, state) {
if (
binding.kind === 'prop' &&
!binding.mutated &&
!(state.analysis.immutable ? binding.reassigned : binding.mutated) &&
!binding.initial &&
!state.analysis.accessors
) {
return b.call(node);
}
if (binding.kind === 'state' && binding.declaration_kind === 'import') {
if (binding.kind === 'legacy_reactive_import') {
return b.call('$$_import_' + node.name);
}
if (
binding.kind === 'state' ||
(binding.kind === 'state' &&
(!state.analysis.immutable || state.analysis.accessors || binding.reassigned)) ||
binding.kind === 'derived' ||
binding.kind === 'prop' ||
binding.kind === 'rest_prop' ||
@ -99,7 +100,7 @@ export function serialize_get_binding(node, state) {
* @returns {import('estree').Expression}
*/
export function serialize_set_binding(node, context, fallback) {
const { state, visit, path } = context;
const { state, visit } = context;
if (
node.left.type === 'ArrayPattern' ||
@ -152,7 +153,11 @@ export function serialize_set_binding(node, context, fallback) {
if (left.object.type === 'ThisExpression' && left.property.type === 'PrivateIdentifier') {
if (context.state.private_state.has(left.property.name) && !state.in_constructor) {
const value = get_assignment_value(node, context);
return b.call('$.set', left, value);
return b.call(
'$.set',
left,
context.state.analysis.runes && should_proxy(value) ? b.call('$.proxy', value) : value
);
}
}
// @ts-expect-error
@ -171,7 +176,7 @@ export function serialize_set_binding(node, context, fallback) {
return binding.mutation(node, context);
}
if (binding.kind === 'state' && binding.declaration_kind === 'import') {
if (binding.kind === 'legacy_reactive_import') {
return b.call(
'$$_import_' + binding.node.name,
b.assignment(
@ -202,7 +207,11 @@ export function serialize_set_binding(node, context, fallback) {
if (is_store) {
return b.call('$.store_set', serialize_get_binding(b.id(left_name), state), value);
} else {
return b.call('$.set', b.id(left_name), value);
return b.call(
'$.set',
b.id(left_name),
context.state.analysis.runes && should_proxy(value) ? b.call('$.proxy', value) : value
);
}
} else {
if (is_store) {
@ -216,7 +225,7 @@ export function serialize_set_binding(node, context, fallback) {
),
b.call('$' + left_name)
);
} else {
} else if (!state.analysis.runes) {
return b.call(
'$.mutate',
b.id(left_name),
@ -226,6 +235,12 @@ export function serialize_set_binding(node, context, fallback) {
value
)
);
} else {
return b.assignment(
node.operator,
/** @type {import('estree').Pattern} */ (visit(node.left)),
/** @type {import('estree').Expression} */ (visit(node.right))
);
}
}
};
@ -335,24 +350,38 @@ export function get_props_method(binding, state, name, default_value) {
/** @type {import('estree').Expression[]} */
const args = [b.id('$$props'), b.literal(name)];
// Use $.prop_source in the following cases:
// - accessors/mutated: needs to be able to set the prop value from within
// - default value: we set the fallback value only initially, and it's not possible to know this timing in $.prop
const needs_source =
default_value ||
state.analysis.accessors ||
(state.analysis.immutable ? binding.reassigned : binding.mutated);
if (needs_source) {
args.push(b.literal(state.analysis.immutable));
}
if (default_value) {
// To avoid eagerly evaluating the right-hand-side, we wrap it in a thunk if necessary
if (default_value.type !== 'Literal' && default_value.type !== 'Identifier') {
args.push(b.thunk(default_value));
args.push(b.true);
} else {
if (is_simple_expression(default_value)) {
args.push(default_value);
args.push(b.false);
} else {
if (
default_value.type === 'CallExpression' &&
default_value.callee.type === 'Identifier' &&
default_value.arguments.length === 0
) {
args.push(default_value.callee);
} else {
args.push(b.thunk(default_value));
}
args.push(b.true);
}
}
return b.call(
// Use $.prop_source in the following cases:
// - accessors/mutated: needs to be able to set the prop value from within
// - default value: we set the fallback value only initially, and it's not possible to know this timing in $.prop
binding.mutated || binding.initial || state.analysis.accessors ? '$.prop_source' : '$.prop',
...args
);
return b.call(needs_source ? '$.prop_source' : '$.prop', ...args);
}
/**
@ -364,7 +393,7 @@ export function get_props_method(binding, state, name, default_value) {
export function create_state_declarators(declarator, scope, value) {
// in the simple `let count = $state(0)` case, we rewrite `$state` as `$.source`
if (declarator.id.type === 'Identifier') {
return [b.declarator(declarator.id, b.call('$.source', value))];
return [b.declarator(declarator.id, b.call('$.mutable_source', value))];
}
const tmp = scope.generate('tmp');
@ -374,7 +403,25 @@ export function create_state_declarators(declarator, scope, value) {
...paths.map((path) => {
const value = path.expression?.(b.id(tmp));
const binding = scope.get(/** @type {import('estree').Identifier} */ (path.node).name);
return b.declarator(path.node, binding?.kind === 'state' ? b.call('$.source', value) : value);
return b.declarator(
path.node,
binding?.kind === 'state' ? b.call('$.mutable_source', value) : value
);
})
];
}
/** @param {import('estree').Expression} node */
export function should_proxy(node) {
if (
!node ||
node.type === 'Literal' ||
node.type === 'ArrowFunctionExpression' ||
node.type === 'FunctionExpression' ||
(node.type === 'Identifier' && node.name === 'undefined')
) {
return false;
}
return true;
}

@ -2,7 +2,7 @@ import { get_rune } from '../../../scope.js';
import { is_hoistable_function } from '../../utils.js';
import * as b from '../../../../utils/builders.js';
import * as assert from '../../../../utils/assert.js';
import { create_state_declarators, get_props_method } from '../utils.js';
import { create_state_declarators, get_props_method, should_proxy } from '../utils.js';
import { unwrap_ts_expression } from '../../../../utils/ast.js';
/** @type {import('../types.js').ComponentVisitors} */
@ -84,7 +84,7 @@ export const javascript_visitors_runes = {
value =
field.kind === 'state'
? b.call('$.source', init)
? b.call('$.source', should_proxy(init) ? b.call('$.proxy', init) : init)
: b.call('$.derived', b.thunk(init));
} else {
// if no arguments, we know it's state as `$derived()` is a compile error
@ -211,16 +211,28 @@ export const javascript_visitors_runes = {
}
const args = /** @type {import('estree').CallExpression} */ (init).arguments;
const value =
let value =
args.length === 0
? b.id('undefined')
: /** @type {import('estree').Expression} */ (visit(args[0]));
const opts = args[1] && /** @type {import('estree').Expression} */ (visit(args[1]));
if (declarator.id.type === 'Identifier') {
const callee = rune === '$state' ? '$.source' : '$.derived';
const arg = rune === '$state' ? value : b.thunk(value);
declarations.push(b.declarator(declarator.id, b.call(callee, arg, opts)));
if (rune === '$state') {
const binding = /** @type {import('#compiler').Binding} */ (
state.scope.get(declarator.id.name)
);
if (should_proxy(value)) {
value = b.call('$.proxy', value);
}
if (!state.analysis.immutable || state.analysis.accessors || binding.reassigned) {
value = b.call('$.source', value);
}
} else {
value = b.call('$.derived', b.thunk(value));
}
declarations.push(b.declarator(declarator.id, value));
continue;
}

@ -27,6 +27,7 @@ import {
DOMBooleanAttributes,
EACH_INDEX_REACTIVE,
EACH_IS_CONTROLLED,
EACH_IS_IMMUTABLE,
EACH_ITEM_REACTIVE,
EACH_KEYED
} from '../../../../../constants.js';
@ -767,18 +768,23 @@ function serialize_inline_component(node, component_name, context) {
const [, value] = serialize_attribute_value(attribute.value, context);
if (attribute.metadata.dynamic) {
let arg = value;
const contains_call_expression =
Array.isArray(attribute.value) &&
attribute.value.some((n) => {
return n.type === 'ExpressionTag' && n.metadata.contains_call_expression;
});
if (contains_call_expression) {
const id = b.id(context.state.scope.generate(attribute.name));
context.state.init.push(b.var(id, b.call('$.derived', b.thunk(value))));
push_prop(b.get(attribute.name, [b.return(b.call('$.get', id))]));
} else {
push_prop(b.get(attribute.name, [b.return(value)]));
arg = b.call('$.get', id);
}
if (context.state.options.dev) arg = b.call('$.readonly', arg);
push_prop(b.get(attribute.name, [b.return(arg)]));
} else {
push_prop(b.init(attribute.name, value));
}
@ -2128,14 +2134,13 @@ export const template_visitors = {
// array or use nested reactivity through runes.
// TODO this feels a bit "hidden performance boost"-style, investigate if there's a way
// to make this apply in more cases
/** @type {number} */
let each_type;
let each_type = 0;
if (
node.key &&
(node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index)
) {
each_type = EACH_KEYED;
each_type |= EACH_KEYED;
if (
node.key.type === 'Identifier' &&
node.context.type === 'Identifier' &&
@ -2152,12 +2157,17 @@ export const template_visitors = {
each_type |= EACH_INDEX_REACTIVE;
}
} else {
each_type = EACH_ITEM_REACTIVE;
each_type |= EACH_ITEM_REACTIVE;
}
if (each_node_meta.is_controlled) {
each_type |= EACH_IS_CONTROLLED;
}
if (context.state.analysis.immutable) {
each_type |= EACH_IS_IMMUTABLE;
}
// Find the parent each blocks which contain the arrays to invalidate
// TODO decide how much of this we want to keep for runes mode. For now we're bailing out below
const indirect_dependencies = collect_parent_each_blocks(context).flatMap((block) => {

@ -2094,7 +2094,7 @@ export function server_component(analysis, options) {
}
const component_block = b.block([
b.stmt(b.call('$.push', b.literal(analysis.runes), ...(options.immutable ? [b.true] : []))),
b.stmt(b.call('$.push', b.literal(analysis.runes))),
.../** @type {import('estree').Statement[]} */ (instance.body),
.../** @type {import('estree').Statement[]} */ (template.body),
b.stmt(b.call('$.pop'))

@ -105,7 +105,8 @@ export class Scope {
is_called: false,
prop_alias: null,
expression: null,
mutation: null
mutation: null,
reassigned: false
};
this.declarations.set(node.name, binding);
this.root.conflicts.add(node.name);
@ -632,7 +633,10 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
} else {
extract_identifiers(node).forEach((identifier) => {
const binding = scope.get(identifier.name);
if (binding) binding.mutated = true;
if (binding) {
binding.mutated = true;
binding.reassigned = true;
}
});
}
}

@ -46,6 +46,8 @@ export interface Analysis {
module: Js;
name: string; // TODO should this be filename? it's used in `compileModule` as well as `compile`
warnings: RawWarning[];
runes: boolean;
immutable: boolean;
// TODO figure out if we can move this to ComponentAnalysis
accessors: boolean;

@ -251,6 +251,7 @@ export interface Binding {
* - `each`: An each block context variable
* - `store_sub`: A $store value
* - `legacy_reactive`: A `$:` declaration
* - `legacy_reactive_import`: An imported binding that is mutated inside the component
*/
kind:
| 'normal'
@ -260,7 +261,8 @@ export interface Binding {
| 'derived'
| 'each'
| 'store_sub'
| 'legacy_reactive';
| 'legacy_reactive'
| 'legacy_reactive_import';
declaration_kind: DeclarationKind;
/**
* What the value was initialized with.
@ -270,6 +272,7 @@ export interface Binding {
is_called: boolean;
references: { node: Identifier; path: SvelteNode[] }[];
mutated: boolean;
reassigned: boolean;
scope: Scope;
/** For `legacy_reactive`: its reactive dependencies */
legacy_dependencies: Binding[];

@ -304,3 +304,36 @@ export function get_parent(path, at) {
}
return /** @type {T} */ (node);
}
/**
* Returns `true` if the expression is an identifier, a literal, a function expression,
* or a logical expression that only contains simple expressions. Used to determine whether
* something needs to be treated as though accessing it could have side-effects (i.e.
* reading signals prematurely)
* @param {import('estree').Expression} node
* @returns {boolean}
*/
export function is_simple_expression(node) {
if (
node.type === 'Literal' ||
node.type === 'Identifier' ||
node.type === 'ArrowFunctionExpression' ||
node.type === 'FunctionExpression'
) {
return true;
}
if (node.type === 'ConditionalExpression') {
return (
is_simple_expression(node.test) &&
is_simple_expression(node.consequent) &&
is_simple_expression(node.alternate)
);
}
if (node.type === 'BinaryExpression' || node.type === 'LogicalExpression') {
return is_simple_expression(node.left) && is_simple_expression(node.right);
}
return false;
}

@ -72,7 +72,10 @@ export const validate_component_options =
discloseVersion: boolean(true),
immutable: boolean(false),
immutable: deprecate(
'The immutable option has been deprecated. It has no effect in runes mode.',
boolean(false)
),
legacy: object({
componentApi: boolean(false)
@ -166,6 +169,18 @@ function warn_removed(message) {
};
}
/**
* @param {string} message
* @param {Validator} validator
* @returns {Validator}
*/
function deprecate(message, validator) {
return (input, keypath) => {
if (input !== undefined) warn(message);
return validator(input, keypath);
};
}
/**
* @param {Record<string, Validator>} children
* @param {boolean} [allow_unknown]

@ -22,9 +22,6 @@ const runes = {
`It looks like you're using the $${name} rune, but there is a local binding called ${name}. ` +
`Referencing a local variable with a $ prefix will create a store subscription. Please rename ${name} to avoid the ambiguity.`,
/** @param {string} name */
'state-not-mutated': (name) =>
`${name} is declared with $state(...) but is never updated. Did you mean to create a function that changes its value?`,
/** @param {string} name */
'non-state-reference': (name) =>
`${name} is updated, but is not declared with $state(...). Changing its value will not correctly trigger updates.`
};

@ -3,6 +3,7 @@ export const EACH_INDEX_REACTIVE = 1 << 1;
export const EACH_KEYED = 1 << 2;
export const EACH_IS_CONTROLLED = 1 << 3;
export const EACH_IS_ANIMATED = 1 << 4;
export const EACH_IS_IMMUTABLE = 1 << 6;
/** List of Element events that will be delegated */
export const DelegatedEvents = [

@ -0,0 +1,803 @@
import {
EACH_INDEX_REACTIVE,
EACH_IS_ANIMATED,
EACH_IS_CONTROLLED,
EACH_IS_IMMUTABLE,
EACH_ITEM_REACTIVE,
EACH_KEYED
} from '../../constants.js';
import { create_each_block, create_each_item_block } from './block.js';
import {
current_hydration_fragment,
get_hydration_fragment,
hydrate_block_anchor,
set_current_hydration_fragment
} from './hydration.js';
import { clear_text_content, map_get, map_set } from './operations.js';
import { STATE_SYMBOL } from './proxy/proxy.js';
import { insert, remove } from './reconciler.js';
import { empty } from './render.js';
import {
destroy_signal,
execute_effect,
is_lazy_property,
lazy_property,
mutable_source,
push_destroy_fn,
render_effect,
schedule_task,
set_signal_value,
source
} from './runtime.js';
import { trigger_transitions } from './transitions.js';
import { is_array } from './utils.js';
const NEW_BLOCK = -1;
const MOVED_BLOCK = 99999999;
const LIS_BLOCK = -2;
function no_op() {}
/**
* @template V
* @param {Element | Comment} anchor_node
* @param {() => V[]} collection
* @param {number} flags
* @param {null | ((item: V) => string)} key_fn
* @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal<number>) => void} render_fn
* @param {null | ((anchor: Node) => void)} fallback_fn
* @param {typeof reconcile_indexed_array | reconcile_tracked_array} reconcile_fn
* @returns {void}
*/
function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, reconcile_fn) {
const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
const block = create_each_block(flags, anchor_node);
/** @type {null | import('./types.js').Render} */
let current_fallback = null;
hydrate_block_anchor(anchor_node, is_controlled);
/** @type {V[]} */
let array;
/** @type {Array<string> | null} */
let keys = null;
/** @type {null | import('./types.js').EffectSignal} */
let render = null;
block.r =
/** @param {import('./types.js').Transition} transition */
(transition) => {
const fallback = /** @type {import('./types.js').Render} */ (current_fallback);
const transitions = fallback.s;
transitions.add(transition);
transition.f(() => {
transitions.delete(transition);
if (transitions.size === 0) {
if (fallback.e !== null) {
if (fallback.d !== null) {
remove(fallback.d);
fallback.d = null;
}
destroy_signal(fallback.e);
fallback.e = null;
}
}
});
};
const create_fallback_effect = () => {
/** @type {import('./types.js').Render} */
const fallback = {
d: null,
e: null,
s: new Set(),
p: current_fallback
};
// Managed effect
const effect = render_effect(
() => {
const dom = block.d;
if (dom !== null) {
remove(dom);
block.d = null;
}
let anchor = block.a;
const is_controlled = (block.f & EACH_IS_CONTROLLED) !== 0;
if (is_controlled) {
anchor = empty();
block.a.appendChild(anchor);
}
/** @type {(anchor: Node) => void} */ (fallback_fn)(anchor);
fallback.d = block.d;
block.d = null;
},
block,
true
);
fallback.e = effect;
current_fallback = fallback;
};
const each = render_effect(
() => {
/** @type {V[]} */
const maybe_array = collection();
array = is_array(maybe_array)
? maybe_array
: maybe_array == null
? []
: Array.from(maybe_array);
if (key_fn !== null) {
keys = array.map(key_fn);
} else if ((flags & EACH_KEYED) === 0) {
array.map(no_op);
}
const length = array.length;
if (fallback_fn !== null) {
if (length === 0) {
if (block.v.length !== 0 || render === null) {
create_fallback_effect();
}
} else if (block.v.length === 0 && current_fallback !== null) {
const fallback = current_fallback;
const transitions = fallback.s;
if (transitions.size === 0) {
if (fallback.d !== null) {
remove(fallback.d);
fallback.d = null;
}
} else {
trigger_transitions(transitions, 'out');
}
}
}
if (render !== null) {
execute_effect(render);
}
},
block,
false
);
render = render_effect(
/** @param {import('./types.js').EachBlock} block */
(block) => {
const flags = block.f;
const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
const anchor_node = block.a;
reconcile_fn(array, block, anchor_node, is_controlled, render_fn, flags, true, keys);
},
block,
true
);
push_destroy_fn(each, () => {
const flags = block.f;
const anchor_node = block.a;
const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
let fallback = current_fallback;
while (fallback !== null) {
const dom = fallback.d;
if (dom !== null) {
remove(dom);
}
const effect = fallback.e;
if (effect !== null) {
destroy_signal(effect);
}
fallback = fallback.p;
}
// Clear the array
reconcile_fn([], block, anchor_node, is_controlled, render_fn, flags, false, keys);
destroy_signal(/** @type {import('./types.js').EffectSignal} */ (render));
});
block.e = each;
}
/**
* @template V
* @param {Element | Comment} anchor_node
* @param {() => V[]} collection
* @param {number} flags
* @param {null | ((item: V) => string)} key_fn
* @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal<number>) => void} render_fn
* @param {null | ((anchor: Node) => void)} fallback_fn
* @returns {void}
*/
export function each_keyed(anchor_node, collection, flags, key_fn, render_fn, fallback_fn) {
each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, reconcile_tracked_array);
}
/**
* @template V
* @param {Element | Comment} anchor_node
* @param {() => V[]} collection
* @param {number} flags
* @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal<number>) => void} render_fn
* @param {null | ((anchor: Node) => void)} fallback_fn
* @returns {void}
*/
export function each_indexed(anchor_node, collection, flags, render_fn, fallback_fn) {
each(anchor_node, collection, flags, null, render_fn, fallback_fn, reconcile_indexed_array);
}
/**
* @template V
* @param {Array<V>} array
* @param {import('./types.js').EachBlock} each_block
* @param {Element | Comment | Text} dom
* @param {boolean} is_controlled
* @param {(anchor: null, item: V, index: number | import('./types.js').Signal<number>) => void} render_fn
* @param {number} flags
* @param {boolean} apply_transitions
* @returns {void}
*/
function reconcile_indexed_array(
array,
each_block,
dom,
is_controlled,
render_fn,
flags,
apply_transitions
) {
var is_proxied_array = STATE_SYMBOL in array;
var a_blocks = each_block.v;
var active_transitions = each_block.s;
if (is_proxied_array) {
flags &= ~EACH_ITEM_REACTIVE;
}
/** @type {number | void} */
var a = a_blocks.length;
/** @type {number} */
var b = array.length;
var length = Math.max(a, b);
var index = 0;
/** @type {Array<import('./types.js').EachItemBlock>} */
var b_blocks;
var block;
if (active_transitions.length !== 0) {
destroy_active_transition_blocks(active_transitions);
}
if (b === 0) {
b_blocks = [];
// Remove old blocks
if (is_controlled && a !== 0) {
clear_text_content(dom);
}
while (index < length) {
block = a_blocks[index++];
destroy_each_item_block(block, active_transitions, apply_transitions, is_controlled);
}
} else {
var item;
b_blocks = Array(b);
if (current_hydration_fragment !== null) {
/** @type {Node} */
var hydrating_node = current_hydration_fragment[0];
for (; index < length; index++) {
// Hydrate block
item = is_proxied_array ? lazy_property(array, index) : array[index];
var fragment = /** @type {Array<Text | Comment | Element>} */ (
get_hydration_fragment(hydrating_node)
);
set_current_hydration_fragment(fragment);
hydrating_node = /** @type {Node} */ (
/** @type {Node} */ (/** @type {Node} */ (fragment.at(-1)).nextSibling).nextSibling
);
block = each_item_block(item, null, index, render_fn, flags);
b_blocks[index] = block;
}
} else {
for (; index < length; index++) {
if (index >= a) {
// Add block
item = is_proxied_array ? lazy_property(array, index) : array[index];
block = each_item_block(item, null, index, render_fn, flags);
b_blocks[index] = block;
insert_each_item_block(block, dom, is_controlled, null);
} else if (index >= b) {
// Remove block
block = a_blocks[index];
destroy_each_item_block(block, active_transitions, apply_transitions);
} else {
// Update block
item = array[index];
block = a_blocks[index];
b_blocks[index] = block;
update_each_item_block(block, item, index, flags);
}
}
}
}
each_block.v = b_blocks;
}
// Reconcile arrays by the equality of the elements in the array. This algorithm
// is based on Ivi's reconcilation logic:
//
// https://github.com/localvoid/ivi/blob/9f1bd0918f487da5b131941228604763c5d8ef56/packages/ivi/src/client/core.ts#L968
//
/**
* @template V
* @param {Array<V>} array
* @param {import('./types.js').EachBlock} each_block
* @param {Element | Comment | Text} dom
* @param {boolean} is_controlled
* @param {(anchor: null, item: V, index: number | import('./types.js').Signal<number>) => void} render_fn
* @param {number} flags
* @param {boolean} apply_transitions
* @param {Array<string> | null} keys
* @returns {void}
*/
function reconcile_tracked_array(
array,
each_block,
dom,
is_controlled,
render_fn,
flags,
apply_transitions,
keys
) {
var a_blocks = each_block.v;
const is_computed_key = keys !== null;
var is_proxied_array = STATE_SYMBOL in array;
var active_transitions = each_block.s;
if (is_proxied_array) {
flags &= ~EACH_ITEM_REACTIVE;
}
/** @type {number | void} */
var a = a_blocks.length;
/** @type {number} */
var b = array.length;
/** @type {Array<import('./types.js').EachItemBlock>} */
var b_blocks;
var block;
if (active_transitions.length !== 0) {
destroy_active_transition_blocks(active_transitions);
}
if (b === 0) {
b_blocks = [];
// Remove old blocks
if (is_controlled && a !== 0) {
clear_text_content(dom);
}
while (a > 0) {
block = a_blocks[--a];
destroy_each_item_block(block, active_transitions, apply_transitions, is_controlled);
}
} else {
var a_end = a - 1;
var b_end = b - 1;
var key;
var item;
var idx;
b_blocks = Array(b);
if (current_hydration_fragment !== null) {
var fragment;
/** @type {Node} */
var hydrating_node = current_hydration_fragment[0];
while (b > 0) {
// Hydrate block
idx = b_end - --b;
item = array[idx];
key = is_computed_key ? keys[idx] : item;
fragment = /** @type {Array<Text | Comment | Element>} */ (
get_hydration_fragment(hydrating_node)
);
set_current_hydration_fragment(fragment);
// Get the <!--ssr:..--> tag of the next item in the list
// The fragment array can be empty if each block has no content
hydrating_node = /** @type {Node} */ (
/** @type {Node} */ ((fragment.at(-1) || hydrating_node).nextSibling).nextSibling
);
block = each_item_block(item, key, idx, render_fn, flags);
b_blocks[idx] = block;
}
} else if (a === 0) {
// Create new blocks
while (b > 0) {
idx = b_end - --b;
item = array[idx];
key = is_computed_key ? keys[idx] : item;
block = each_item_block(item, key, idx, render_fn, flags);
b_blocks[idx] = block;
insert_each_item_block(block, dom, is_controlled, null);
}
} else {
var should_update_block = (flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0;
var start = 0;
/** @type {null | Text | Element | Comment} */
var sibling = null;
item = array[b_end];
key = is_computed_key ? keys[b_end] : item;
// Step 1
outer: while (true) {
// From the end
while (a_blocks[a_end].k === key) {
block = a_blocks[a_end--];
item = array[b_end];
if (should_update_block) {
update_each_item_block(block, item, b_end, flags);
}
sibling = get_first_child(block);
b_blocks[b_end] = block;
if (start > --b_end || start > a_end) {
break outer;
}
key = is_computed_key ? keys[b_end] : item;
}
item = array[start];
key = is_computed_key ? keys[start] : item;
// At the start
while (start <= a_end && start <= b_end && a_blocks[start].k === key) {
item = array[start];
block = a_blocks[start];
if (should_update_block) {
update_each_item_block(block, item, start, flags);
}
b_blocks[start] = block;
++start;
key = is_computed_key ? keys[start] : array[start];
}
break;
}
// Step 2
if (start > a_end) {
while (b_end >= start) {
item = array[b_end];
key = is_computed_key ? keys[b_end] : item;
block = each_item_block(item, key, b_end, render_fn, flags);
b_blocks[b_end--] = block;
sibling = insert_each_item_block(block, dom, is_controlled, sibling);
}
} else if (start > b_end) {
b = start;
do {
if ((block = a_blocks[b++]) !== null) {
destroy_each_item_block(block, active_transitions, apply_transitions);
}
} while (b <= a_end);
} else {
// Step 3
var pos = 0;
var b_length = b_end - start + 1;
var sources = new Int32Array(b_length);
var item_index = new Map();
for (b = 0; b < b_length; ++b) {
a = b + start;
sources[b] = NEW_BLOCK;
item = array[a];
key = is_computed_key ? keys[a] : item;
map_set(item_index, key, a);
}
for (b = start; b <= a_end; ++b) {
a = map_get(item_index, /** @type {V} */ (a_blocks[b].k));
block = a_blocks[b];
if (a !== undefined) {
pos = pos < a ? a : MOVED_BLOCK;
sources[a - start] = b;
b_blocks[a] = block;
} else if (block !== null) {
destroy_each_item_block(block, active_transitions, apply_transitions);
}
}
// Step 4
if (pos === MOVED_BLOCK) {
mark_lis(sources);
}
// If keys are animated, we need to do updates before actual moves
var is_animated = (flags & EACH_IS_ANIMATED) !== 0;
var should_create;
if (is_animated) {
var i = b_length;
while (i-- > 0) {
b_end = i + start;
a = sources[i];
if (pos === MOVED_BLOCK && a !== LIS_BLOCK) {
block = b_blocks[b_end];
update_each_item_block(block, item, b_end, flags);
}
}
}
var last_block;
var last_sibling;
while (b_length-- > 0) {
b_end = b_length + start;
a = sources[b_length];
should_create = a === -1;
item = array[b_end];
if (should_create) {
key = is_computed_key ? keys[b_end] : item;
block = each_item_block(item, key, b_end, render_fn, flags);
} else {
block = b_blocks[b_end];
if (!is_animated && should_update_block) {
update_each_item_block(block, item, b_end, flags);
}
}
if (should_create || (pos === MOVED_BLOCK && a !== LIS_BLOCK)) {
last_sibling = last_block === undefined ? sibling : get_first_child(last_block);
sibling = insert_each_item_block(block, dom, is_controlled, last_sibling);
}
b_blocks[b_end] = block;
last_block = block;
}
}
}
}
each_block.v = b_blocks;
}
/**
* Longest Increased Subsequence algorithm
* @param {Int32Array} a
* @returns {void}
*/
function mark_lis(a) {
var length = a.length;
var parent = new Int32Array(length);
var index = new Int32Array(length);
var index_length = 0;
var i = 0;
/** @type {number} */
var j;
/** @type {number} */
var k;
/** @type {number} */
var lo;
/** @type {number} */
var hi;
// Skip -1 values at the start of the input array `a`.
for (; a[i] === NEW_BLOCK; ++i) {
/**/
}
index[0] = i++;
for (; i < length; ++i) {
k = a[i];
if (k !== NEW_BLOCK) {
// Ignore -1 values.
j = index[index_length];
if (a[j] < k) {
parent[i] = j;
index[++index_length] = i;
} else {
lo = 0;
hi = index_length;
while (lo < hi) {
j = (lo + hi) >> 1;
if (a[index[j]] < k) {
lo = j + 1;
} else {
hi = j;
}
}
if (k < a[index[lo]]) {
if (lo > 0) {
parent[i] = index[lo - 1];
}
index[lo] = i;
}
}
}
}
// Mutate input array `a` and assign -2 value to all nodes that are part of LIS.
j = index[index_length];
while (index_length-- >= 0) {
a[j] = LIS_BLOCK;
j = parent[j];
}
}
/**
* @param {import('./types.js').Block} block
* @param {Element | Comment | Text} dom
* @param {boolean} is_controlled
* @param {null | Text | Element | Comment} sibling
* @returns {Text | Element | Comment}
*/
function insert_each_item_block(block, dom, is_controlled, sibling) {
var current = /** @type {import('./types.js').TemplateNode} */ (block.d);
if (sibling === null) {
if (is_controlled) {
return insert(current, /** @type {Element} */ (dom), null);
} else {
return insert(current, /** @type {Element} */ (dom.parentNode), dom);
}
}
return insert(current, null, sibling);
}
/**
* @param {import('./types.js').Block} block
* @returns {Text | Element | Comment}
*/
function get_first_child(block) {
var current = block.d;
if (is_array(current)) {
return /** @type {Text | Element | Comment} */ (current[0]);
}
return /** @type {Text | Element | Comment} */ (current);
}
/**
* @param {Array<import('./types.js').EachItemBlock>} active_transitions
* @returns {void}
*/
function destroy_active_transition_blocks(active_transitions) {
var length = active_transitions.length;
if (length > 0) {
var i = 0;
var block;
var transition;
for (; i < length; i++) {
block = active_transitions[i];
transition = block.r;
if (transition !== null) {
block.r = null;
destroy_each_item_block(block, null, false);
}
}
active_transitions.length = 0;
}
}
/**
* @param {import('./types.js').Block} block
* @returns {Text | Element | Comment}
*/
function get_first_element(block) {
const current = block.d;
if (is_array(current)) {
for (let i = 0; i < current.length; i++) {
const node = current[i];
if (node.nodeType !== 8) {
return node;
}
}
}
return /** @type {Text | Element | Comment} */ (current);
}
/**
* @param {import('./types.js').EachItemBlock} block
* @param {any} item
* @param {number} index
* @param {number} type
* @returns {void}
*/
function update_each_item_block(block, item, index, type) {
if ((type & EACH_ITEM_REACTIVE) !== 0) {
set_signal_value(block.v, item);
} else if (is_lazy_property(block.v)) {
block.v.o[block.v.p] = item;
}
const transitions = block.s;
const index_is_reactive = (type & EACH_INDEX_REACTIVE) !== 0;
// Handle each item animations
if (transitions !== null && (type & EACH_KEYED) !== 0) {
let prev_index = block.i;
if (index_is_reactive) {
prev_index = /** @type {import('./types.js').Signal<number>} */ (prev_index).v;
}
const items = block.p.v;
if (prev_index !== index && /** @type {number} */ (index) < items.length) {
const from_dom = /** @type {Element} */ (get_first_element(block));
const from = from_dom.getBoundingClientRect();
schedule_task(() => {
trigger_transitions(transitions, 'key', from);
});
}
}
if (index_is_reactive) {
set_signal_value(/** @type {import('./types.js').Signal<number>} */ (block.i), index);
} else {
block.i = index;
}
}
/**
* @param {import('./types.js').EachItemBlock} block
* @param {null | Array<import('./types.js').Block>} transition_block
* @param {boolean} apply_transitions
* @param {any} controlled
* @returns {void}
*/
export function destroy_each_item_block(
block,
transition_block,
apply_transitions,
controlled = false
) {
const transitions = block.s;
if (apply_transitions && transitions !== null) {
trigger_transitions(transitions, 'out');
if (transition_block !== null) {
transition_block.push(block);
}
} else {
const dom = block.d;
if (!controlled && dom !== null) {
remove(dom);
}
destroy_signal(/** @type {import('./types.js').EffectSignal} */ (block.e));
}
}
/**
* @template V
* @template O
* @template P
* @param {V | import('./types.js').LazyProperty<O, P>} item
* @param {unknown} key
* @param {number} index
* @param {(anchor: null, item: V, index: number | import('./types.js').Signal<number>) => void} render_fn
* @param {number} flags
* @returns {import('./types.js').EachItemBlock}
*/
function each_item_block(item, key, index, render_fn, flags) {
const each_item_not_reactive = (flags & EACH_ITEM_REACTIVE) === 0;
const item_value = each_item_not_reactive
? item
: (flags & EACH_IS_IMMUTABLE) === 0
? mutable_source(item)
: source(item);
const index_value = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index);
const block = create_each_item_block(item_value, index_value, key);
const effect = render_effect(
/** @param {import('./types.js').EachItemBlock} block */
(block) => {
render_fn(null, block.v, block.i);
},
block,
true
);
block.e = effect;
return block;
}

@ -0,0 +1,176 @@
import { DEV } from 'esm-env';
import {
effect_active,
get,
set,
increment,
source,
updating_derived,
UNINITIALIZED
} from '../runtime.js';
import { define_property, get_descriptor, is_array } from '../utils.js';
import { READONLY_SYMBOL } from './readonly.js';
/** @typedef {{ s: Map<string | symbol, import('../types.js').SourceSignal<any>>; v: import('../types.js').SourceSignal<number>; a: boolean }} Metadata */
/** @typedef {Record<string | symbol, any> & { [STATE_SYMBOL]: Metadata }} StateObject */
export const STATE_SYMBOL = Symbol('$state');
const object_prototype = Object.prototype;
const array_prototype = Array.prototype;
const get_prototype_of = Object.getPrototypeOf;
const is_frozen = Object.isFrozen;
/**
* @template {StateObject} T
* @param {T} value
* @returns {T}
*/
export function proxy(value) {
if (typeof value === 'object' && value != null && !is_frozen(value) && !(STATE_SYMBOL in value)) {
const prototype = get_prototype_of(value);
// TODO handle Map and Set as well
if (prototype === object_prototype || prototype === array_prototype) {
define_property(value, STATE_SYMBOL, { value: init(value), writable: false });
// @ts-expect-error not sure how to fix this
return new Proxy(value, handler);
}
}
return value;
}
/**
* @param {StateObject} value
* @returns {Metadata}
*/
function init(value) {
return {
s: new Map(),
v: source(0),
a: is_array(value)
};
}
/** @type {ProxyHandler<StateObject>} */
const handler = {
defineProperty(target, prop, descriptor) {
if (descriptor.value) {
const metadata = target[STATE_SYMBOL];
const s = metadata.s.get(prop);
if (s !== undefined) set(s, proxy(descriptor.value));
}
return Reflect.defineProperty(target, prop, descriptor);
},
deleteProperty(target, prop) {
const metadata = target[STATE_SYMBOL];
const s = metadata.s.get(prop);
if (s !== undefined) set(s, UNINITIALIZED);
if (prop in target) increment(metadata.v);
return delete target[prop];
},
get(target, prop, receiver) {
if (prop === READONLY_SYMBOL) return target[READONLY_SYMBOL];
const metadata = target[STATE_SYMBOL];
let s = metadata.s.get(prop);
// if we're reading a property in a reactive context, create a source,
// but only if it's an own property and not a prototype property
if (
s === undefined &&
(effect_active() || updating_derived) &&
(!(prop in target) || get_descriptor(target, prop)?.writable)
) {
s = source(proxy(target[prop]));
metadata.s.set(prop, s);
}
const value = s !== undefined ? get(s) : Reflect.get(target, prop, receiver);
return value === UNINITIALIZED ? undefined : value;
},
getOwnPropertyDescriptor(target, prop) {
const descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
if (descriptor && 'value' in descriptor) {
const metadata = target[STATE_SYMBOL];
const s = metadata.s.get(prop);
if (s) {
descriptor.value = get(s);
}
}
return descriptor;
},
has(target, prop) {
if (prop === STATE_SYMBOL) {
return true;
}
const metadata = target[STATE_SYMBOL];
const has = Reflect.has(target, prop);
let s = metadata.s.get(prop);
if (s !== undefined || (effect_active() && (!has || get_descriptor(target, prop)?.writable))) {
if (s === undefined) {
s = source(has ? proxy(target[prop]) : UNINITIALIZED);
metadata.s.set(prop, s);
}
const value = get(s);
if (value === UNINITIALIZED) {
return false;
}
}
return has;
},
set(target, prop, value) {
if (prop === READONLY_SYMBOL) {
target[READONLY_SYMBOL] = value;
return true;
}
const metadata = target[STATE_SYMBOL];
const s = metadata.s.get(prop);
if (s !== undefined) set(s, proxy(value));
if (metadata.a && prop === 'length') {
for (let i = value; i < target.length; i += 1) {
const s = metadata.s.get(i + '');
if (s !== undefined) set(s, UNINITIALIZED);
}
}
if (!(prop in target)) increment(metadata.v);
// @ts-ignore
target[prop] = value;
return true;
},
ownKeys(target) {
const metadata = target[STATE_SYMBOL];
get(metadata.v);
return Reflect.ownKeys(target);
}
};
if (DEV) {
handler.setPrototypeOf = () => {
throw new Error('Cannot set prototype of $state object');
};
}
export { readonly } from './readonly.js';

@ -0,0 +1,65 @@
import { define_property, get_descriptor } from '../utils.js';
/**
* @template {Record<string | symbol, any>} T
* @typedef {T & { [READONLY_SYMBOL]: Proxy<T> }} StateObject
*/
export const READONLY_SYMBOL = Symbol('readonly');
const object_prototype = Object.prototype;
const array_prototype = Array.prototype;
const get_prototype_of = Object.getPrototypeOf;
const is_frozen = Object.isFrozen;
/**
* @template {Record<string | symbol, any>} T
* @template {StateObject<T>} U
* @param {U} value
* @returns {Proxy<U> | U}
*/
export function readonly(value) {
const proxy = value && value[READONLY_SYMBOL];
if (proxy) return proxy;
if (
typeof value === 'object' &&
value != null &&
!is_frozen(value) &&
!(READONLY_SYMBOL in value)
) {
const prototype = get_prototype_of(value);
// TODO handle Map and Set as well
if (prototype === object_prototype || prototype === array_prototype) {
const proxy = new Proxy(value, handler);
define_property(value, READONLY_SYMBOL, { value: proxy, writable: false });
return proxy;
}
}
return value;
}
/** @returns {never} */
const readonly_error = () => {
throw new Error(`Props are read-only, unless used with \`bind:\``);
};
/** @type {ProxyHandler<StateObject<any>>} */
const handler = {
defineProperty: readonly_error,
deleteProperty: readonly_error,
set: readonly_error,
get(target, prop, receiver) {
const value = Reflect.get(target, prop, receiver);
if (!(prop in target)) {
return readonly(value);
}
return value;
}
};

@ -1,17 +1,6 @@
import { append_child, map_get, map_set, clear_text_content } from './operations.js';
import {
current_hydration_fragment,
get_hydration_fragment,
hydrate_block_anchor,
set_current_hydration_fragment
} from './hydration.js';
import { append_child } from './operations.js';
import { current_hydration_fragment, hydrate_block_anchor } from './hydration.js';
import { is_array } from './utils.js';
import { each_item_block, destroy_each_item_block, update_each_item_block } from './render.js';
import { EACH_INDEX_REACTIVE, EACH_IS_ANIMATED, EACH_ITEM_REACTIVE } from '../../constants.js';
const NEW_BLOCK = -1;
const MOVED_BLOCK = 99999999;
const LIS_BLOCK = -2;
/** @param {string} html */
export function create_fragment_from_html(html) {
@ -103,429 +92,3 @@ export function reconcile_html(dom, value, svg) {
target.before(svg ? /** @type {Node} */ (clone.firstChild) : clone);
return /** @type {Array<Text | Comment | Element>} */ (frag_nodes);
}
/**
* @param {import('./types.js').Block} block
* @param {Element | Comment | Text} dom
* @param {boolean} is_controlled
* @param {null | Text | Element | Comment} sibling
* @returns {Text | Element | Comment}
*/
function insert_each_item_block(block, dom, is_controlled, sibling) {
var current = /** @type {import('./types.js').TemplateNode} */ (block.d);
if (sibling === null) {
if (is_controlled) {
return insert(current, /** @type {Element} */ (dom), null);
} else {
return insert(current, /** @type {Element} */ (dom.parentNode), dom);
}
}
return insert(current, null, sibling);
}
/**
* @param {import('./types.js').Block} block
* @returns {Text | Element | Comment}
*/
function get_first_child(block) {
var current = block.d;
if (is_array(current)) {
return /** @type {Text | Element | Comment} */ (current[0]);
}
return /** @type {Text | Element | Comment} */ (current);
}
/**
* @param {Array<import('./types.js').EachItemBlock>} active_transitions
* @returns {void}
*/
function destroy_active_transition_blocks(active_transitions) {
var length = active_transitions.length;
if (length > 0) {
var i = 0;
var block;
var transition;
for (; i < length; i++) {
block = active_transitions[i];
transition = block.r;
if (transition !== null) {
block.r = null;
destroy_each_item_block(block, null, false);
}
}
active_transitions.length = 0;
}
}
/**
* @template V
* @param {Array<V>} array
* @param {import('./types.js').EachBlock} each_block
* @param {Element | Comment | Text} dom
* @param {boolean} is_controlled
* @param {(anchor: null, item: V, index: number | import('./types.js').Signal<number>) => void} render_fn
* @param {number} flags
* @param {boolean} apply_transitions
* @returns {void}
*/
export function reconcile_indexed_array(
array,
each_block,
dom,
is_controlled,
render_fn,
flags,
apply_transitions
) {
var a_blocks = each_block.v;
var active_transitions = each_block.s;
/** @type {number | void} */
var a = a_blocks.length;
/** @type {number} */
var b = array.length;
var length = Math.max(a, b);
var index = 0;
/** @type {Array<import('./types.js').EachItemBlock>} */
var b_blocks;
var block;
if (active_transitions.length !== 0) {
destroy_active_transition_blocks(active_transitions);
}
if (b === 0) {
b_blocks = [];
// Remove old blocks
if (is_controlled && a !== 0) {
clear_text_content(dom);
}
while (index < length) {
block = a_blocks[index++];
destroy_each_item_block(block, active_transitions, apply_transitions, is_controlled);
}
} else {
var item;
b_blocks = Array(b);
if (current_hydration_fragment !== null) {
/** @type {Node} */
var hydrating_node = current_hydration_fragment[0];
for (; index < length; index++) {
// Hydrate block
item = array[index];
var fragment = /** @type {Array<Text | Comment | Element>} */ (
get_hydration_fragment(hydrating_node)
);
set_current_hydration_fragment(fragment);
hydrating_node = /** @type {Node} */ (
/** @type {Node} */ (/** @type {Node} */ (fragment.at(-1)).nextSibling).nextSibling
);
block = each_item_block(item, null, index, render_fn, flags);
b_blocks[index] = block;
}
} else {
for (; index < length; index++) {
if (index >= a) {
// Add block
item = array[index];
block = each_item_block(item, null, index, render_fn, flags);
b_blocks[index] = block;
insert_each_item_block(block, dom, is_controlled, null);
} else if (index >= b) {
// Remove block
block = a_blocks[index];
destroy_each_item_block(block, active_transitions, apply_transitions);
} else {
// Update block
item = array[index];
block = a_blocks[index];
b_blocks[index] = block;
update_each_item_block(block, item, index, flags);
}
}
}
}
each_block.v = b_blocks;
}
// Reconcile arrays by the equality of the elements in the array. This algorithm
// is based on Ivi's reconcilation logic:
//
// https://github.com/localvoid/ivi/blob/9f1bd0918f487da5b131941228604763c5d8ef56/packages/ivi/src/client/core.ts#L968
//
/**
* @template V
* @param {Array<V>} array
* @param {import('./types.js').EachBlock} each_block
* @param {Element | Comment | Text} dom
* @param {boolean} is_controlled
* @param {(anchor: null, item: V, index: number | import('./types.js').Signal<number>) => void} render_fn
* @param {number} flags
* @param {boolean} apply_transitions
* @param {Array<string> | null} keys
* @returns {void}
*/
export function reconcile_tracked_array(
array,
each_block,
dom,
is_controlled,
render_fn,
flags,
apply_transitions,
keys
) {
var a_blocks = each_block.v;
const is_computed_key = keys !== null;
var active_transitions = each_block.s;
/** @type {number | void} */
var a = a_blocks.length;
/** @type {number} */
var b = array.length;
/** @type {Array<import('./types.js').EachItemBlock>} */
var b_blocks;
var block;
if (active_transitions.length !== 0) {
destroy_active_transition_blocks(active_transitions);
}
if (b === 0) {
b_blocks = [];
// Remove old blocks
if (is_controlled && a !== 0) {
clear_text_content(dom);
}
while (a > 0) {
block = a_blocks[--a];
destroy_each_item_block(block, active_transitions, apply_transitions, is_controlled);
}
} else {
var a_end = a - 1;
var b_end = b - 1;
var key;
var item;
var idx;
b_blocks = Array(b);
if (current_hydration_fragment !== null) {
var fragment;
/** @type {Node} */
var hydrating_node = current_hydration_fragment[0];
while (b > 0) {
// Hydrate block
idx = b_end - --b;
item = array[idx];
key = is_computed_key ? keys[idx] : item;
fragment = /** @type {Array<Text | Comment | Element>} */ (
get_hydration_fragment(hydrating_node)
);
set_current_hydration_fragment(fragment);
// Get the <!--ssr:..--> tag of the next item in the list
// The fragment array can be empty if each block has no content
hydrating_node = /** @type {Node} */ (
/** @type {Node} */ ((fragment.at(-1) || hydrating_node).nextSibling).nextSibling
);
block = each_item_block(item, key, idx, render_fn, flags);
b_blocks[idx] = block;
}
} else if (a === 0) {
// Create new blocks
while (b > 0) {
idx = b_end - --b;
item = array[idx];
key = is_computed_key ? keys[idx] : item;
block = each_item_block(item, key, idx, render_fn, flags);
b_blocks[idx] = block;
insert_each_item_block(block, dom, is_controlled, null);
}
} else {
var should_update_block = (flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0;
var start = 0;
/** @type {null | Text | Element | Comment} */
var sibling = null;
item = array[b_end];
key = is_computed_key ? keys[b_end] : item;
// Step 1
outer: while (true) {
// From the end
while (a_blocks[a_end].k === key) {
block = a_blocks[a_end--];
item = array[b_end];
if (should_update_block) {
update_each_item_block(block, item, b_end, flags);
}
sibling = get_first_child(block);
b_blocks[b_end] = block;
if (start > --b_end || start > a_end) {
break outer;
}
key = is_computed_key ? keys[b_end] : item;
}
item = array[start];
key = is_computed_key ? keys[start] : item;
// At the start
while (start <= a_end && start <= b_end && a_blocks[start].k === key) {
item = array[start];
block = a_blocks[start];
if (should_update_block) {
update_each_item_block(block, item, start, flags);
}
b_blocks[start] = block;
++start;
key = is_computed_key ? keys[start] : array[start];
}
break;
}
// Step 2
if (start > a_end) {
while (b_end >= start) {
item = array[b_end];
key = is_computed_key ? keys[b_end] : item;
block = each_item_block(item, key, b_end, render_fn, flags);
b_blocks[b_end--] = block;
sibling = insert_each_item_block(block, dom, is_controlled, sibling);
}
} else if (start > b_end) {
b = start;
do {
if ((block = a_blocks[b++]) !== null) {
destroy_each_item_block(block, active_transitions, apply_transitions);
}
} while (b <= a_end);
} else {
// Step 3
var pos = 0;
var b_length = b_end - start + 1;
var sources = new Int32Array(b_length);
var item_index = new Map();
for (b = 0; b < b_length; ++b) {
a = b + start;
sources[b] = NEW_BLOCK;
item = array[a];
key = is_computed_key ? keys[a] : item;
map_set(item_index, key, a);
}
for (b = start; b <= a_end; ++b) {
a = map_get(item_index, /** @type {V} */ (a_blocks[b].k));
block = a_blocks[b];
if (a !== undefined) {
pos = pos < a ? a : MOVED_BLOCK;
sources[a - start] = b;
b_blocks[a] = block;
} else if (block !== null) {
destroy_each_item_block(block, active_transitions, apply_transitions);
}
}
// Step 4
if (pos === MOVED_BLOCK) {
mark_lis(sources);
}
// If keys are animated, we need to do updates before actual moves
var is_animated = (flags & EACH_IS_ANIMATED) !== 0;
var should_create;
if (is_animated) {
var i = b_length;
while (i-- > 0) {
b_end = i + start;
a = sources[i];
if (pos === MOVED_BLOCK && a !== LIS_BLOCK) {
block = b_blocks[b_end];
update_each_item_block(block, item, b_end, flags);
}
}
}
var last_block;
var last_sibling;
while (b_length-- > 0) {
b_end = b_length + start;
a = sources[b_length];
should_create = a === -1;
item = array[b_end];
if (should_create) {
key = is_computed_key ? keys[b_end] : item;
block = each_item_block(item, key, b_end, render_fn, flags);
} else {
block = b_blocks[b_end];
if (!is_animated && should_update_block) {
update_each_item_block(block, item, b_end, flags);
}
}
if (should_create || (pos === MOVED_BLOCK && a !== LIS_BLOCK)) {
last_sibling = last_block === undefined ? sibling : get_first_child(last_block);
sibling = insert_each_item_block(block, dom, is_controlled, last_sibling);
}
b_blocks[b_end] = block;
last_block = block;
}
}
}
}
each_block.v = b_blocks;
}
// Longest Increased Subsequence algorithm.
/**
* @param {Int32Array} a
* @returns {void}
*/
function mark_lis(a) {
var length = a.length;
var parent = new Int32Array(length);
var index = new Int32Array(length);
var index_length = 0;
var i = 0;
/** @type {number} */
var j;
/** @type {number} */
var k;
/** @type {number} */
var lo;
/** @type {number} */
var hi;
// Skip -1 values at the start of the input array `a`.
for (; a[i] === NEW_BLOCK; ++i) {
/**/
}
index[0] = i++;
for (; i < length; ++i) {
k = a[i];
if (k !== NEW_BLOCK) {
// Ignore -1 values.
j = index[index_length];
if (a[j] < k) {
parent[i] = j;
index[++index_length] = i;
} else {
lo = 0;
hi = index_length;
while (lo < hi) {
j = (lo + hi) >> 1;
if (a[index[j]] < k) {
lo = j + 1;
} else {
hi = j;
}
}
if (k < a[index[lo]]) {
if (lo > 0) {
parent[i] = index[lo - 1];
}
index[lo] = i;
}
}
}
}
// Mutate input array `a` and assign -2 value to all nodes that are part of LIS.
j = index[index_length];
while (index_length-- >= 0) {
a[j] = LIS_BLOCK;
j = parent[j];
}
}

@ -11,8 +11,6 @@ import {
} from './operations.js';
import {
create_root_block,
create_each_item_block,
create_each_block,
create_if_block,
create_key_block,
create_await_block,
@ -21,23 +19,8 @@ import {
create_dynamic_component_block,
create_snippet_block
} from './block.js';
import {
EACH_KEYED,
EACH_IS_CONTROLLED,
EACH_INDEX_REACTIVE,
EACH_ITEM_REACTIVE,
PassiveDelegatedEvents,
DelegatedEvents,
AttributeAliases
} from '../../constants.js';
import {
create_fragment_from_html,
insert,
reconcile_tracked_array,
reconcile_html,
remove,
reconcile_indexed_array
} from './reconciler.js';
import { PassiveDelegatedEvents, DelegatedEvents, AttributeAliases } from '../../constants.js';
import { create_fragment_from_html, insert, reconcile_html, remove } from './reconciler.js';
import {
render_effect,
destroy_signal,
@ -54,14 +37,13 @@ import {
expose,
safe_not_equal,
current_block,
set_signal_value,
source,
managed_effect,
safe_equal,
push,
current_component_context,
pop,
schedule_task
unwrap,
mutable_source
} from './runtime.js';
import {
current_hydration_fragment,
@ -2019,288 +2001,6 @@ export function key(anchor_node, key, render_fn) {
block.e = key_effect;
}
/**
* @param {import('./types.js').Block} block
* @returns {Text | Element | Comment}
*/
function get_first_element(block) {
const current = block.d;
if (is_array(current)) {
for (let i = 0; i < current.length; i++) {
const node = current[i];
if (node.nodeType !== 8) {
return node;
}
}
}
return /** @type {Text | Element | Comment} */ (current);
}
/**
* @param {import('./types.js').EachItemBlock} block
* @param {any} item
* @param {number} index
* @param {number} type
* @returns {void}
*/
export function update_each_item_block(block, item, index, type) {
if ((type & EACH_ITEM_REACTIVE) !== 0) {
set_signal_value(block.v, item);
}
const transitions = block.s;
const index_is_reactive = (type & EACH_INDEX_REACTIVE) !== 0;
// Handle each item animations
if (transitions !== null && (type & EACH_KEYED) !== 0) {
let prev_index = block.i;
if (index_is_reactive) {
prev_index = /** @type {import('./types.js').Signal<number>} */ (prev_index).v;
}
const items = block.p.v;
if (prev_index !== index && /** @type {number} */ (index) < items.length) {
const from_dom = /** @type {Element} */ (get_first_element(block));
const from = from_dom.getBoundingClientRect();
schedule_task(() => {
trigger_transitions(transitions, 'key', from);
});
}
}
if (index_is_reactive) {
set_signal_value(/** @type {import('./types.js').Signal<number>} */ (block.i), index);
} else {
block.i = index;
}
}
/**
* @param {import('./types.js').EachItemBlock} block
* @param {null | Array<import('./types.js').Block>} transition_block
* @param {boolean} apply_transitions
* @param {any} controlled
* @returns {void}
*/
export function destroy_each_item_block(
block,
transition_block,
apply_transitions,
controlled = false
) {
const transitions = block.s;
if (apply_transitions && transitions !== null) {
trigger_transitions(transitions, 'out');
if (transition_block !== null) {
transition_block.push(block);
}
} else {
const dom = block.d;
if (!controlled && dom !== null) {
remove(dom);
}
destroy_signal(/** @type {import('./types.js').EffectSignal} */ (block.e));
}
}
/**
* @template V
* @param {V} item
* @param {unknown} key
* @param {number} index
* @param {(anchor: null, item: V, index: number | import('./types.js').Signal<number>) => void} render_fn
* @param {number} flags
* @returns {import('./types.js').EachItemBlock}
*/
export function each_item_block(item, key, index, render_fn, flags) {
const item_value = (flags & EACH_ITEM_REACTIVE) === 0 ? item : source(item);
const index_value = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index);
const block = create_each_item_block(item_value, index_value, key);
const effect = render_effect(
/** @param {import('./types.js').EachItemBlock} block */
(block) => {
render_fn(null, block.v, block.i);
},
block,
true
);
block.e = effect;
return block;
}
/**
* @template V
* @param {Element | Comment} anchor_node
* @param {() => V[]} collection
* @param {number} flags
* @param {null | ((item: V) => string)} key_fn
* @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal<number>) => void} render_fn
* @param {null | ((anchor: Node) => void)} fallback_fn
* @param {typeof reconcile_indexed_array | reconcile_tracked_array} reconcile_fn
* @returns {void}
*/
function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, reconcile_fn) {
const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
const block = create_each_block(flags, anchor_node);
/** @type {null | import('./types.js').Render} */
let current_fallback = null;
hydrate_block_anchor(anchor_node, is_controlled);
/** @type {V[]} */
let array;
/** @type {Array<string> | null} */
let keys = null;
/** @type {null | import('./types.js').EffectSignal} */
let render = null;
block.r =
/** @param {import('./types.js').Transition} transition */
(transition) => {
const fallback = /** @type {import('./types.js').Render} */ (current_fallback);
const transitions = fallback.s;
transitions.add(transition);
transition.f(() => {
transitions.delete(transition);
if (transitions.size === 0) {
if (fallback.e !== null) {
if (fallback.d !== null) {
remove(fallback.d);
fallback.d = null;
}
destroy_signal(fallback.e);
fallback.e = null;
}
}
});
};
const create_fallback_effect = () => {
/** @type {import('./types.js').Render} */
const fallback = {
d: null,
e: null,
s: new Set(),
p: current_fallback
};
// Managed effect
const effect = render_effect(
() => {
const dom = block.d;
if (dom !== null) {
remove(dom);
block.d = null;
}
let anchor = block.a;
const is_controlled = (block.f & EACH_IS_CONTROLLED) !== 0;
if (is_controlled) {
anchor = empty();
block.a.appendChild(anchor);
}
/** @type {(anchor: Node) => void} */ (fallback_fn)(anchor);
fallback.d = block.d;
block.d = null;
},
block,
true
);
fallback.e = effect;
current_fallback = fallback;
};
const each = render_effect(
() => {
/** @type {V[]} */
const maybe_array = collection();
array = is_array(maybe_array)
? maybe_array
: maybe_array == null
? []
: Array.from(maybe_array);
if (key_fn !== null) {
keys = array.map(key_fn);
}
if (fallback_fn !== null) {
if (array.length === 0) {
if (block.v.length !== 0 || render === null) {
create_fallback_effect();
}
} else if (block.v.length === 0 && current_fallback !== null) {
const fallback = current_fallback;
const transitions = fallback.s;
if (transitions.size === 0) {
if (fallback.d !== null) {
remove(fallback.d);
fallback.d = null;
}
} else {
trigger_transitions(transitions, 'out');
}
}
}
if (render !== null) {
execute_effect(render);
}
},
block,
false
);
render = render_effect(
/** @param {import('./types.js').EachBlock} block */
(block) => {
const flags = block.f;
const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
const anchor_node = block.a;
reconcile_fn(array, block, anchor_node, is_controlled, render_fn, flags, true, keys);
},
block,
true
);
push_destroy_fn(each, () => {
const flags = block.f;
const anchor_node = block.a;
const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
let fallback = current_fallback;
while (fallback !== null) {
const dom = fallback.d;
if (dom !== null) {
remove(dom);
}
const effect = fallback.e;
if (effect !== null) {
destroy_signal(effect);
}
fallback = fallback.p;
}
// Clear the array
reconcile_fn([], block, anchor_node, is_controlled, render_fn, flags, false, keys);
destroy_signal(/** @type {import('./types.js').EffectSignal} */ (render));
});
block.e = each;
}
/**
* @template V
* @param {Element | Comment} anchor_node
* @param {() => V[]} collection
* @param {number} flags
* @param {null | ((item: V) => string)} key_fn
* @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal<number>) => void} render_fn
* @param {null | ((anchor: Node) => void)} fallback_fn
* @returns {void}
*/
export function each_keyed(anchor_node, collection, flags, key_fn, render_fn, fallback_fn) {
each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, reconcile_tracked_array);
}
/**
* @template V
* @param {Element | Comment} anchor_node
* @param {() => V[]} collection
* @param {number} flags
* @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal<number>) => void} render_fn
* @param {null | ((anchor: Node) => void)} fallback_fn
* @returns {void}
*/
export function each_indexed(anchor_node, collection, flags, render_fn, fallback_fn) {
each(anchor_node, collection, flags, null, render_fn, fallback_fn, reconcile_indexed_array);
}
/**
* @param {Element | Text | Comment} anchor
* @param {boolean} is_html
@ -2890,20 +2590,6 @@ export function spread_props(props) {
return merged_props;
}
/**
* @template V
* @param {V} value
* @returns {import('./types.js').UnwrappedSignal<V>}
*/
export function unwrap(value) {
if (is_signal(value)) {
// @ts-ignore
return get(value);
}
// @ts-ignore
return value;
}
/**
* Mounts the given component to the given target and returns a handle to the component's public accessors
* as well as a `$set` and `$destroy` method to update the props of the component or destroy it.
@ -2920,7 +2606,6 @@ export function unwrap(value) {
* events?: Events;
* context?: Map<any, any>;
* intro?: boolean;
* immutable?: boolean;
* recover?: false;
* }} options
* @returns {Exports & { $destroy: () => void; $set: (props: Partial<Props>) => void; }}
@ -2938,15 +2623,7 @@ export function createRoot(component, options) {
* @param {any} value
*/
function add_prop(name, value) {
const prop = source(
value,
options.immutable
? /**
* @param {any} a
* @param {any} b
*/ (a, b) => a === b
: safe_equal
);
const prop = source(value);
_sources[name] = prop;
define_property(_props, name, {
get() {
@ -2980,11 +2657,9 @@ export function createRoot(component, options) {
return _props[property];
}
});
const props_source = source(
props_proxy,
// We're resetting the same proxy instance for updates, therefore bypass equality checks
() => false
);
// We're resetting the same proxy instance for updates, therefore bypass equality checks
const props_source = mutable_source(props_proxy);
let [accessors, $destroy] = mount(component, {
...options,
@ -3040,7 +2715,6 @@ export function createRoot(component, options) {
* events?: Events;
* context?: Map<any, any>;
* intro?: boolean;
* immutable?: boolean;
* recover?: false;
* }} options
* @returns {[Exports, () => void]}

@ -1,7 +1,6 @@
import { DEV } from 'esm-env';
import { subscribe_to_store } from '../../store/utils.js';
import { EMPTY_FUNC, run_all } from '../common.js';
import { unwrap } from './render.js';
import { get_descriptors, is_array } from './utils.js';
export const SOURCE = 1;
@ -24,6 +23,7 @@ const FLUSH_MICROTASK = 0;
const FLUSH_SYNC = 1;
export const UNINITIALIZED = Symbol();
export const LAZY_PROPERTY = Symbol();
// Used for controlling the flush of effects.
let current_scheduler_mode = FLUSH_MICROTASK;
@ -87,6 +87,8 @@ export let current_block = null;
export let current_component_context = null;
export let is_ssr = false;
export let updating_derived = false;
/**
* @param {boolean} ssr
* @returns {void}
@ -108,8 +110,6 @@ export function create_component_context(props) {
c: null,
// effects
e: null,
// immutable
i: false,
// mounted
m: false,
// parent
@ -145,7 +145,7 @@ export function set_current_component_context(context_stack_item) {
* @param {unknown} b
* @returns {boolean}
*/
function default_equals(a, b) {
export function default_equals(a, b) {
return a === b;
}
@ -161,7 +161,7 @@ function create_source_signal(flags, value) {
// consumers
c: null,
// equals
e: null,
e: default_equals,
// flags
f: flags,
// value
@ -176,7 +176,7 @@ function create_source_signal(flags, value) {
// consumers
c: null,
// equals
e: null,
e: default_equals,
// flags
f: flags,
// value
@ -688,7 +688,10 @@ export async function tick() {
* @returns {void}
*/
function update_derived(signal, force_schedule) {
const previous_updating_derived = updating_derived;
updating_derived = true;
const value = execute_signal_fn(signal);
updating_derived = previous_updating_derived;
const status =
current_skip_consumer || (current_effect === null && (signal.f & UNOWNED) !== 0)
? DIRTY
@ -726,7 +729,7 @@ export function store_get(store, store_name, stores) {
entry = {
store: null,
last_value: null,
value: source(UNINITIALIZED),
value: mutable_source(UNINITIALIZED),
unsubscribe: EMPTY_FUNC
};
// TODO: can we remove this code? it was refactored out when we split up source/comptued signals
@ -1157,11 +1160,10 @@ export function destroy_signal(signal) {
/**
* @template V
* @param {() => V} init
* @param {import('./types.js').EqualsFunctions} [equals]
* @returns {import('./types.js').ComputationSignal<V>}
*/
/*#__NO_SIDE_EFFECTS__*/
export function derived(init, equals) {
export function derived(init) {
const is_unowned = current_effect === null;
const flags = is_unowned ? DERIVED | UNOWNED : DERIVED;
const signal = /** @type {import('./types.js').ComputationSignal<V>} */ (
@ -1169,7 +1171,7 @@ export function derived(init, equals) {
);
signal.i = init;
signal.x = current_component_context;
signal.e = get_equals_method(equals);
signal.e = default_equals;
if (!is_unowned) {
push_reference(/** @type {import('./types.js').EffectSignal} */ (current_effect), signal);
}
@ -1179,30 +1181,25 @@ export function derived(init, equals) {
/**
* @template V
* @param {V} initial_value
* @param {import('./types.js').EqualsFunctions<V>} [equals]
* @returns {import('./types.js').SourceSignal<V>}
*/
/*#__NO_SIDE_EFFECTS__*/
export function source(initial_value, equals) {
export function source(initial_value) {
const source = create_source_signal(SOURCE | CLEAN, initial_value);
source.x = current_component_context;
source.e = get_equals_method(equals);
return source;
}
/**
* @param {import('./types.js').EqualsFunctions} [equals]
* @returns {import('./types.js').EqualsFunctions}
* @template V
* @param {V} initial_value
* @returns {import('./types.js').SourceSignal<V>}
*/
function get_equals_method(equals) {
if (equals !== undefined) {
return equals;
}
const context = current_component_context;
if (context && !context.i) {
return safe_equal;
}
return default_equals;
/*#__NO_SIDE_EFFECTS__*/
export function mutable_source(initial_value) {
const s = source(initial_value);
s.e = safe_equal;
return s;
}
/**
@ -1425,6 +1422,20 @@ export function is_signal(val) {
);
}
/**
* @template O
* @template P
* @param {any} val
* @returns {val is import('./types.js').LazyProperty<O, P>}
*/
export function is_lazy_property(val) {
return (
typeof val === 'object' &&
val !== null &&
/** @type {import('./types.js').LazyProperty<O, P>} */ (val).t === LAZY_PROPERTY
);
}
/**
* @template V
* @param {unknown} val
@ -1452,11 +1463,12 @@ export function is_store(val) {
* @template V
* @param {import('./types.js').MaybeSignal<Record<string, unknown>>} props_obj
* @param {string} key
* @param {boolean} immutable
* @param {V | (() => V)} [default_value]
* @param {boolean} [call_default_value]
* @returns {import('./types.js').Signal<V> | (() => V)}
*/
export function prop_source(props_obj, key, default_value, call_default_value) {
export function prop_source(props_obj, key, immutable, default_value, call_default_value) {
const props = is_signal(props_obj) ? get(props_obj) : props_obj;
const possible_signal = /** @type {import('./types.js').MaybeSignal<V>} */ (
expose(() => props[key])
@ -1468,8 +1480,7 @@ export function prop_source(props_obj, key, default_value, call_default_value) {
if (
is_signal(possible_signal) &&
possible_signal.v === value &&
update_bound_prop === undefined &&
get_equals_method() === possible_signal.e
update_bound_prop === undefined
) {
if (should_set_default_value) {
set(
@ -1487,13 +1498,11 @@ export function prop_source(props_obj, key, default_value, call_default_value) {
call_default_value ? default_value() : default_value;
}
const source_signal = source(value);
const source_signal = immutable ? source(value) : mutable_source(value);
// Synchronize prop changes with source signal.
// Needs special equality checking because the prop in the
// parent could be changed through `foo.bar = 'new value'`.
const immutable = /** @type {import('./types.js').ComponentContext} */ (current_component_context)
.i;
let ignore_next1 = false;
let ignore_next2 = false;
let did_update_to_defined = !should_set_default_value;
@ -1552,16 +1561,12 @@ export function prop_source(props_obj, key, default_value, call_default_value) {
/**
* If the prop is readonly and has no fallback value, we can use this function, else we need to use `prop_source`.
* @template V
* @param {import('./types.js').MaybeSignal<Record<string, unknown>>} props_obj
* @param {string} key
* @returns {any}
*/
export function prop(props_obj, key) {
return () => {
const props = is_signal(props_obj) ? get(props_obj) : props_obj;
return /** @type {V} */ (props[key]);
};
return is_signal(props_obj) ? () => get(props_obj)[key] : () => props_obj[key];
}
/**
@ -1804,13 +1809,11 @@ export function onDestroy(fn) {
/**
* @param {import('./types.js').MaybeSignal<Record<string, unknown>>} props
* @param {any} runes
* @param {any} immutable
* @returns {void}
*/
export function push(props, runes = false, immutable = false) {
export function push(props, runes = false) {
const context_stack_item = create_component_context(props);
context_stack_item.r = runes;
context_stack_item.i = immutable;
current_component_context = context_stack_item;
}
@ -1901,3 +1904,35 @@ export function inspect(get_value, inspect = console.log) {
};
});
}
/**
* @template O
* @template P
* @param {O} o
* @param {P} p
* @returns {import('./types.js').LazyProperty<O, P>}
*/
export function lazy_property(o, p) {
return {
o,
p,
t: LAZY_PROPERTY
};
}
/**
* @template V
* @param {V} value
* @returns {import('./types.js').UnwrappedSignal<V>}
*/
export function unwrap(value) {
if (is_signal(value)) {
// @ts-ignore
return get(value);
}
if (is_lazy_property(value)) {
return value.o[value.p];
}
// @ts-ignore
return value;
}

@ -9,8 +9,9 @@ import {
KEY_BLOCK,
ROOT_BLOCK
} from './block.js';
import { destroy_each_item_block } from './each.js';
import { append_child } from './operations.js';
import { destroy_each_item_block, empty } from './render.js';
import { empty } from './render.js';
import {
current_block,
current_effect,

@ -10,7 +10,7 @@ import {
DYNAMIC_ELEMENT_BLOCK,
SNIPPET_BLOCK
} from './block.js';
import { DERIVED, EFFECT, RENDER_EFFECT, SOURCE, PRE_EFFECT } from './runtime.js';
import { DERIVED, EFFECT, RENDER_EFFECT, SOURCE, PRE_EFFECT, LAZY_PROPERTY } from './runtime.js';
// Put all internal types in this file. Once we convert to JSDoc, we can make this a d.ts file
@ -47,8 +47,6 @@ export type ComponentContext = {
p: null | ComponentContext;
/** context */
c: null | Map<unknown, unknown>;
/** immutable */
i: boolean;
/** runes */
r: boolean;
/** update_callbacks */
@ -116,6 +114,12 @@ export type MaybeSignal<T = unknown> = T | Signal<T>;
export type UnwrappedSignal<T> = T extends Signal<infer U> ? U : T;
export type LazyProperty<O, P> = {
o: O;
p: P;
t: typeof LAZY_PROPERTY;
};
export type EqualsFunctions<T = any> = (a: T, v: T) => boolean;
export type BlockType =

@ -7,6 +7,7 @@ export {
expose,
exposable,
source,
mutable_source,
derived,
prop,
prop_source,
@ -38,12 +39,15 @@ export {
reactive_import,
effect_active,
user_root_effect,
inspect
inspect,
unwrap
} from './client/runtime.js';
export * from './client/validate.js';
export * from './client/each.js';
export * from './client/render.js';
export * from './client/validate.js';
export { raf } from './client/timing.js';
export { proxy, readonly } from './client/proxy/proxy.js';
export { create_custom_element } from './client/custom-element.js';
@ -54,5 +58,3 @@ export {
$window as window,
$document as document
} from './client/operations.js';
export { raf } from './client/timing.js';

@ -115,10 +115,9 @@ export function render(component, options) {
/**
* @param {boolean} runes
* @param {boolean} [immutable]
*/
export function push(runes, immutable) {
$.push({}, runes, immutable);
export function push(runes) {
$.push({}, runes);
}
export function pop() {

@ -68,7 +68,6 @@ class Svelte4Component {
target: options.target,
props: { ...options.props, $$events: this.#events },
context: options.context,
immutable: options.immutable,
intro: options.intro,
recover: options.recover
});

@ -0,0 +1,9 @@
import { test } from '../../test';
export default test({
error: {
code: 'invalid-derived-export',
message: 'Cannot export derived state',
position: process.platform === 'win32' ? [26, 68] : [24, 66]
}
});

@ -0,0 +1,3 @@
let count = $state(0);
export const double = $derived(count * 2);

@ -0,0 +1,9 @@
import { test } from '../../test';
export default test({
error: {
code: 'invalid-state-export',
message: 'Cannot export state if it is reassigned',
position: process.platform === 'win32' ? [50, 90] : [46, 86]
}
});

@ -0,0 +1,13 @@
export const object = $state({
ok: true
});
export const primitive = $state('nope');
export function update_object() {
object.ok = !object.ok;
}
export function update_primitive() {
primitive = 'yep';
}

@ -37,11 +37,11 @@ const { test, run } = suite<CompilerErrorTest>((config, cwd) => {
}
}
if (fs.existsSync(`${cwd}/main.js`)) {
if (fs.existsSync(`${cwd}/main.svelte.js`)) {
let caught_error = false;
try {
compileModule(fs.readFileSync(`${cwd}/main.js`, 'utf-8'), {
compileModule(fs.readFileSync(`${cwd}/main.svelte.js`, 'utf-8'), {
generate: 'client'
});
} catch (e) {
@ -51,6 +51,10 @@ const { test, run } = suite<CompilerErrorTest>((config, cwd) => {
expect(error.code).toMatch(config.error.code);
expect(error.message).toMatch(config.error.message);
if (config.error.position) {
expect(error.position).toEqual(config.error.position);
}
}
if (!caught_error) {

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 35,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 35,
"type": "Element",
"name": "input",
"attributes": [
{

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 40,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 40,
"type": "Element",
"name": "input",
"attributes": [
{

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 29,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 29,
"type": "Element",
"name": "input",
"attributes": [
{

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 37,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 37,
"type": "Element",
"name": "input",
"attributes": [
{

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 21,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 21,
"type": "Element",
"name": "input",
"attributes": [
{

@ -1,34 +1,18 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 70,
"type": "Fragment",
"children": [
{
"type": "EachBlock",
"start": 0,
"end": 70,
"type": "EachBlock",
"expression": {
"type": "Identifier",
"start": 7,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 13
}
},
"name": "things"
},
"children": [
{
"type": "Element",
"start": 33,
"end": 62,
"type": "Element",
"name": "div",
"attributes": [
{
@ -42,9 +26,9 @@
],
"children": [
{
"type": "Text",
"start": 51,
"end": 56,
"type": "Text",
"raw": "flips",
"data": "flips"
}
@ -57,6 +41,22 @@
"start": 17,
"end": 22
},
"expression": {
"type": "Identifier",
"start": 7,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 13
}
},
"name": "things"
},
"key": {
"type": "Identifier",
"start": 24,

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 29,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 29,
"type": "Element",
"name": "div",
"attributes": [
{

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 41,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 41,
"type": "Element",
"name": "a",
"attributes": [
{
"type": "Attribute",
"start": 3,
"end": 30,
"type": "Attribute",
"name": "href",
"value": [
{
@ -28,9 +28,9 @@
],
"children": [
{
"type": "Text",
"start": 31,
"end": 37,
"type": "Text",
"raw": "Google",
"data": "Google"
}

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 18,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 18,
"type": "Element",
"name": "input",
"attributes": [
{
"type": "Attribute",
"start": 7,
"end": 15,
"type": "Attribute",
"name": "foo",
"value": [
{
@ -24,9 +24,9 @@
"data": "a"
},
{
"type": "MustacheTag",
"start": 12,
"end": 15,
"type": "MustacheTag",
"expression": {
"type": "Literal",
"start": 13,

@ -1,25 +1,25 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 41,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 41,
"type": "Element",
"name": "textarea",
"attributes": [
{
"type": "Attribute",
"start": 10,
"end": 29,
"type": "Attribute",
"name": "readonly",
"value": [
{
"type": "MustacheTag",
"start": 19,
"end": 29,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 20,

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 42,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 42,
"type": "Element",
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 5,
"end": 28,
"type": "Attribute",
"name": "style",
"value": [
{
@ -24,9 +24,9 @@
"data": "color: "
},
{
"type": "MustacheTag",
"start": 19,
"end": 26,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 20,
@ -56,9 +56,9 @@
],
"children": [
{
"type": "MustacheTag",
"start": 29,
"end": 36,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 30,

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 38,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 38,
"type": "Element",
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 5,
"end": 9,
"type": "Attribute",
"name": "a",
"value": [
{
@ -26,15 +26,15 @@
]
},
{
"type": "Attribute",
"start": 10,
"end": 16,
"type": "Attribute",
"name": "b",
"value": [
{
"type": "MustacheTag",
"start": 12,
"end": 16,
"type": "MustacheTag",
"expression": {
"type": "Literal",
"start": 13,
@ -56,9 +56,9 @@
]
},
{
"type": "Attribute",
"start": 17,
"end": 21,
"type": "Attribute",
"name": "c",
"value": [
{
@ -71,15 +71,15 @@
]
},
{
"type": "Attribute",
"start": 22,
"end": 30,
"type": "Attribute",
"name": "d",
"value": [
{
"type": "MustacheTag",
"start": 25,
"end": 29,
"type": "MustacheTag",
"expression": {
"type": "Literal",
"start": 26,

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 83,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 83,
"type": "Element",
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 5,
"end": 76,
"type": "Attribute",
"name": "data-foo",
"value": [
{

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 28,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 28,
"type": "Element",
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 5,
"end": 11,
"type": "Attribute",
"name": "id",
"value": [
{
@ -26,9 +26,9 @@
]
},
{
"type": "Attribute",
"start": 12,
"end": 21,
"type": "Attribute",
"name": "class",
"value": [
{

@ -1,25 +1,25 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 11,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 11,
"type": "Element",
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 5,
"end": 9,
"type": "Attribute",
"name": "id",
"value": [
{
"type": "AttributeShorthand",
"start": 6,
"end": 8,
"type": "AttributeShorthand",
"expression": {
"start": 6,
"end": 8,

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 30,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 30,
"type": "Element",
"name": "textarea",
"attributes": [
{
"type": "Attribute",
"start": 10,
"end": 18,
"type": "Attribute",
"name": "readonly",
"value": true
}

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 23,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 23,
"type": "Element",
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 5,
"end": 16,
"type": "Attribute",
"name": "class",
"value": [
{

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 43,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 43,
"type": "Element",
"name": "div",
"attributes": [
{
@ -18,9 +18,9 @@
"modifiers": ["important"],
"value": [
{
"type": "MustacheTag",
"start": 27,
"end": 36,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 28,

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 23,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 23,
"type": "Element",
"name": "div",
"attributes": [
{

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 252,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 29,
"type": "Element",
"name": "div",
"attributes": [
{
@ -30,24 +30,24 @@
"children": []
},
{
"type": "Text",
"start": 29,
"end": 30,
"type": "Text",
"raw": "\n",
"data": "\n"
},
{
"type": "Element",
"start": 30,
"end": 59,
"type": "Element",
"name": "div",
"attributes": [
{
"start": 35,
"end": 52,
"type": "StyleDirective",
"modifiers": [],
"name": "color",
"modifiers": [],
"value": [
{
"start": 48,
@ -62,16 +62,16 @@
"children": []
},
{
"type": "Text",
"start": 59,
"end": 60,
"type": "Text",
"raw": "\n",
"data": "\n"
},
{
"type": "Element",
"start": 60,
"end": 87,
"type": "Element",
"name": "div",
"attributes": [
{
@ -94,16 +94,16 @@
"children": []
},
{
"type": "Text",
"start": 87,
"end": 88,
"type": "Text",
"raw": "\n",
"data": "\n"
},
{
"type": "Element",
"start": 88,
"end": 127,
"type": "Element",
"name": "div",
"attributes": [
{
@ -121,9 +121,9 @@
"data": "red"
},
{
"type": "MustacheTag",
"start": 109,
"end": 119,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 110,
@ -147,16 +147,16 @@
"children": []
},
{
"type": "Text",
"start": 127,
"end": 128,
"type": "Text",
"raw": "\n",
"data": "\n"
},
{
"type": "Element",
"start": 128,
"end": 167,
"type": "Element",
"name": "div",
"attributes": [
{
@ -174,9 +174,9 @@
"data": "red"
},
{
"type": "MustacheTag",
"start": 149,
"end": 159,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 150,
@ -200,16 +200,16 @@
"children": []
},
{
"type": "Text",
"start": 167,
"end": 168,
"type": "Text",
"raw": "\n",
"data": "\n"
},
{
"type": "Element",
"start": 168,
"end": 205,
"type": "Element",
"name": "div",
"attributes": [
{
@ -227,9 +227,9 @@
"data": "red"
},
{
"type": "MustacheTag",
"start": 188,
"end": 198,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 189,
@ -253,16 +253,16 @@
"children": []
},
{
"type": "Text",
"start": 205,
"end": 206,
"type": "Text",
"raw": "\n",
"data": "\n"
},
{
"type": "Element",
"start": 206,
"end": 252,
"type": "Element",
"name": "div",
"attributes": [
{
@ -273,9 +273,9 @@
"modifiers": [],
"value": [
{
"type": "MustacheTag",
"start": 223,
"end": 245,
"type": "MustacheTag",
"expression": {
"type": "TemplateLiteral",
"start": 224,

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 33,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 33,
"type": "Element",
"name": "div",
"attributes": [
{
@ -18,9 +18,9 @@
"modifiers": [],
"value": [
{
"type": "MustacheTag",
"start": 17,
"end": 26,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 18,

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 34,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 34,
"type": "Element",
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 5,
"end": 24,
"type": "Attribute",
"name": "style",
"value": [
{
@ -28,9 +28,9 @@
],
"children": [
{
"type": "Text",
"start": 25,
"end": 28,
"type": "Text",
"raw": "red",
"data": "red"
}

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 21,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 21,
"type": "Element",
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 5,
"end": 14,
"type": "Attribute",
"name": "class",
"value": [
{

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 38,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 38,
"type": "Element",
"name": "button",
"attributes": [
{
@ -36,9 +36,9 @@
],
"children": [
{
"type": "Text",
"start": 24,
"end": 29,
"type": "Text",
"raw": "Click",
"data": "Click"
}

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 99,
"type": "Fragment",
"children": [
{
"type": "AwaitBlock",
"start": 0,
"end": 99,
"type": "AwaitBlock",
"expression": {
"type": "Identifier",
"start": 8,
@ -32,37 +32,37 @@
"end": 55
},
"pending": {
"type": "PendingBlock",
"start": 19,
"end": 39,
"type": "PendingBlock",
"children": [
{
"type": "Text",
"start": 19,
"end": 21,
"type": "Text",
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "Element",
"start": 21,
"end": 38,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 24,
"end": 34,
"type": "Text",
"raw": "loading...",
"data": "loading..."
}
]
},
{
"type": "Text",
"start": 38,
"end": 39,
"type": "Text",
"raw": "\n",
"data": "\n"
}
@ -70,42 +70,42 @@
"skip": false
},
"then": {
"type": "ThenBlock",
"start": null,
"end": null,
"type": "ThenBlock",
"children": [],
"skip": true
},
"catch": {
"type": "CatchBlock",
"start": 39,
"end": 91,
"type": "CatchBlock",
"children": [
{
"type": "Text",
"start": 56,
"end": 58,
"type": "Text",
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "Element",
"start": 58,
"end": 90,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 61,
"end": 68,
"type": "Text",
"raw": "oh no! ",
"data": "oh no! "
},
{
"type": "MustacheTag",
"start": 68,
"end": 86,
"type": "MustacheTag",
"expression": {
"type": "MemberExpression",
"start": 69,
@ -159,9 +159,9 @@
]
},
{
"type": "Text",
"start": 90,
"end": 91,
"type": "Text",
"raw": "\n",
"data": "\n"
}

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 148,
"type": "Fragment",
"children": [
{
"type": "AwaitBlock",
"start": 0,
"end": 148,
"type": "AwaitBlock",
"expression": {
"type": "Identifier",
"start": 8,
@ -37,37 +37,37 @@
"end": 104
},
"pending": {
"type": "PendingBlock",
"start": 19,
"end": 39,
"type": "PendingBlock",
"children": [
{
"type": "Text",
"start": 19,
"end": 21,
"type": "Text",
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "Element",
"start": 21,
"end": 38,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 24,
"end": 34,
"type": "Text",
"raw": "loading...",
"data": "loading..."
}
]
},
{
"type": "Text",
"start": 38,
"end": 39,
"type": "Text",
"raw": "\n",
"data": "\n"
}
@ -75,35 +75,35 @@
"skip": false
},
"then": {
"type": "ThenBlock",
"start": 39,
"end": 88,
"type": "ThenBlock",
"children": [
{
"type": "Text",
"start": 55,
"end": 57,
"type": "Text",
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "Element",
"start": 57,
"end": 87,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 60,
"end": 73,
"type": "Text",
"raw": "the value is ",
"data": "the value is "
},
{
"type": "MustacheTag",
"start": 73,
"end": 83,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 74,
@ -124,9 +124,9 @@
]
},
{
"type": "Text",
"start": 87,
"end": 88,
"type": "Text",
"raw": "\n",
"data": "\n"
}
@ -134,35 +134,35 @@
"skip": false
},
"catch": {
"type": "CatchBlock",
"start": 88,
"end": 140,
"type": "CatchBlock",
"children": [
{
"type": "Text",
"start": 105,
"end": 107,
"type": "Text",
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "Element",
"start": 107,
"end": 139,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 110,
"end": 117,
"type": "Text",
"raw": "oh no! ",
"data": "oh no! "
},
{
"type": "MustacheTag",
"start": 117,
"end": 135,
"type": "MustacheTag",
"expression": {
"type": "MemberExpression",
"start": 118,
@ -216,9 +216,9 @@
]
},
{
"type": "Text",
"start": 139,
"end": 140,
"type": "Text",
"raw": "\n",
"data": "\n"
}

@ -1,20 +1,20 @@
{
"html": {
"type": "Fragment",
"start": 30,
"end": 48,
"type": "Fragment",
"children": [
{
"type": "Text",
"start": 28,
"end": 30,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "InlineComponent",
"start": 30,
"end": 48,
"type": "InlineComponent",
"name": "Widget",
"attributes": [
{

@ -1,20 +1,20 @@
{
"html": {
"type": "Fragment",
"start": 31,
"end": 56,
"type": "Fragment",
"children": [
{
"type": "Text",
"start": 29,
"end": 31,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "Element",
"start": 31,
"end": 56,
"type": "Element",
"name": "input",
"attributes": [
{

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 30,
"type": "Fragment",
"children": [
{
"type": "Comment",
"start": 0,
"end": 30,
"type": "Comment",
"data": " svelte-ignore foo bar ",
"ignores": ["foo", "bar"]
}

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 18,
"type": "Fragment",
"children": [
{
"type": "Comment",
"start": 0,
"end": 18,
"type": "Comment",
"data": " a comment ",
"ignores": []
}

@ -1,16 +1,14 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 62,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 62,
"type": "InlineComponent",
"name": "svelte:component",
"attributes": [],
"children": [],
"start": 0,
"end": 62,
"expression": {
"type": "ConditionalExpression",
"start": 25,
@ -73,7 +71,9 @@
},
"name": "Bar"
}
}
},
"attributes": [],
"children": []
}
]
}

@ -1,20 +1,20 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 24,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 24,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 3,
"end": 20,
"type": "Text",
"raw": "Hello &amp; World",
"data": "Hello & World"
}

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 17,
"type": "Fragment",
"children": [
{
"type": "Text",
"start": 0,
"end": 17,
"type": "Text",
"raw": "Hello &amp; World",
"data": "Hello & World"
}

@ -1,29 +1,29 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 14,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 14,
"type": "Element",
"name": "div",
"attributes": [],
"children": [
{
"type": "Text",
"start": 5,
"end": 8,
"type": "Text",
"raw": "foo",
"data": "foo"
}
]
},
{
"type": "Text",
"start": 14,
"end": 16,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
}
@ -39,9 +39,13 @@
"type": "Rule",
"prelude": {
"type": "SelectorList",
"start": 25,
"end": 28,
"children": [
{
"type": "Selector",
"start": 25,
"end": 28,
"children": [
{
"type": "TypeSelector",
@ -49,27 +53,23 @@
"start": 25,
"end": 28
}
],
"start": 25,
"end": 28
]
}
],
"start": 25,
"end": 28
]
},
"block": {
"type": "Block",
"start": 29,
"end": 47,
"children": [
{
"type": "Declaration",
"property": "color",
"value": "red",
"start": 33,
"end": 43
"end": 43,
"property": "color",
"value": "red"
}
],
"start": 29,
"end": 47
]
},
"start": 25,
"end": 47

@ -1,30 +1,31 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 101,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 44,
"type": "Element",
"name": "svelte:element",
"start": 0,
"end": 44,
"tag": "div",
"attributes": [],
"children": [],
"tag": "div"
"children": []
},
{
"type": "Text",
"start": 44,
"end": 45,
"data": "\n",
"raw": "\n"
"raw": "\n",
"data": "\n"
},
{
"start": 45,
"end": 101,
"type": "Element",
"name": "svelte:element",
"start": 45,
"end": 101,
"tag": "div",
"attributes": [
{
"type": "Attribute",
@ -33,17 +34,16 @@
"name": "class",
"value": [
{
"type": "Text",
"start": 79,
"end": 82,
"data": "foo",
"raw": "foo"
"type": "Text",
"raw": "foo",
"data": "foo"
}
]
}
],
"children": [],
"tag": "div"
"children": []
}
]
}

@ -1,17 +1,16 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 101,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 44,
"type": "Element",
"name": "svelte:element",
"attributes": [],
"children": [],
"start": 0,
"end": 44,
"tag": {
"type": "Identifier",
"start": 22,
"end": 25,
"loc": {
@ -24,23 +23,39 @@
"column": 25
}
},
"name": "tag",
"type": "Identifier"
}
"name": "tag"
},
"attributes": [],
"children": []
},
{
"type": "Text",
"start": 44,
"end": 45,
"data": "\n",
"raw": "\n"
"raw": "\n",
"data": "\n"
},
{
"start": 45,
"end": 101,
"type": "Element",
"name": "svelte:element",
"children": [],
"start": 45,
"end": 101,
"tag": {
"type": "Identifier",
"start": 67,
"end": 70,
"loc": {
"start": {
"line": 2,
"column": 22
},
"end": {
"line": 2,
"column": 25
}
},
"name": "tag"
},
"attributes": [
{
"type": "Attribute",
@ -49,31 +64,16 @@
"name": "class",
"value": [
{
"type": "Text",
"start": 79,
"end": 82,
"data": "foo",
"raw": "foo"
"type": "Text",
"raw": "foo",
"data": "foo"
}
]
}
],
"tag": {
"start": 67,
"end": 70,
"loc": {
"start": {
"line": 2,
"column": 22
},
"end": {
"line": 2,
"column": 25
}
},
"name": "tag",
"type": "Identifier"
}
"children": []
}
]
}

@ -1,8 +1,8 @@
{
"html": {
"type": "Fragment",
"start": null,
"end": null,
"type": "Fragment",
"children": []
},
"instance": {

@ -1,48 +1,32 @@
{
"html": {
"type": "Fragment",
"start": 41,
"end": 112,
"type": "Fragment",
"children": [
{
"type": "Text",
"start": 39,
"end": 41,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "EachBlock",
"start": 41,
"end": 112,
"type": "EachBlock",
"expression": {
"type": "Identifier",
"start": 48,
"end": 55,
"loc": {
"start": {
"line": 5,
"column": 7
},
"end": {
"line": 5,
"column": 14
}
},
"name": "animals"
},
"children": [
{
"type": "Element",
"start": 83,
"end": 104,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "MustacheTag",
"start": 86,
"end": 91,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 87,
@ -61,16 +45,16 @@
}
},
{
"type": "Text",
"start": 91,
"end": 93,
"type": "Text",
"raw": ": ",
"data": ": "
},
{
"type": "MustacheTag",
"start": 93,
"end": 100,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 94,
@ -170,6 +154,22 @@
}
}
]
},
"expression": {
"type": "Identifier",
"start": 48,
"end": 55,
"loc": {
"start": {
"line": 5,
"column": 7
},
"end": {
"line": 5,
"column": 14
}
},
"name": "animals"
}
}
]

@ -1,41 +1,25 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 77,
"type": "Fragment",
"children": [
{
"type": "EachBlock",
"start": 0,
"end": 77,
"type": "EachBlock",
"expression": {
"type": "Identifier",
"start": 7,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 14
}
},
"name": "animals"
},
"children": [
{
"type": "Element",
"start": 27,
"end": 42,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "MustacheTag",
"start": 30,
"end": 38,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 31,
@ -62,22 +46,38 @@
"start": 18,
"end": 24
},
"expression": {
"type": "Identifier",
"start": 7,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 14
}
},
"name": "animals"
},
"else": {
"type": "ElseBlock",
"start": 50,
"end": 70,
"type": "ElseBlock",
"children": [
{
"type": "Element",
"start": 52,
"end": 69,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 55,
"end": 65,
"type": "Text",
"raw": "no animals",
"data": "no animals"
}

@ -1,41 +1,25 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 58,
"type": "Fragment",
"children": [
{
"type": "EachBlock",
"start": 0,
"end": 58,
"type": "EachBlock",
"expression": {
"type": "Identifier",
"start": 7,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 14
}
},
"name": "animals"
},
"children": [
{
"type": "Element",
"start": 30,
"end": 50,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "MustacheTag",
"start": 33,
"end": 36,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 34,
@ -54,16 +38,16 @@
}
},
{
"type": "Text",
"start": 36,
"end": 38,
"type": "Text",
"raw": ": ",
"data": ": "
},
{
"type": "MustacheTag",
"start": 38,
"end": 46,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 39,
@ -90,6 +74,22 @@
"start": 18,
"end": 24
},
"expression": {
"type": "Identifier",
"start": 7,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 14
}
},
"name": "animals"
},
"index": "i"
}
]

@ -1,41 +1,25 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 54,
"type": "Fragment",
"children": [
{
"type": "EachBlock",
"start": 0,
"end": 54,
"type": "EachBlock",
"expression": {
"type": "Identifier",
"start": 7,
"end": 12,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 12
}
},
"name": "todos"
},
"children": [
{
"type": "Element",
"start": 33,
"end": 46,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "MustacheTag",
"start": 36,
"end": 42,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 37,
@ -62,6 +46,22 @@
"start": 16,
"end": 20
},
"expression": {
"type": "Identifier",
"start": 7,
"end": 12,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 12
}
},
"name": "todos"
},
"key": {
"type": "MemberExpression",
"start": 22,

@ -1,41 +1,25 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 50,
"type": "Fragment",
"children": [
{
"type": "EachBlock",
"start": 0,
"end": 50,
"type": "EachBlock",
"expression": {
"type": "Identifier",
"start": 7,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 14
}
},
"name": "animals"
},
"children": [
{
"type": "Element",
"start": 27,
"end": 42,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "MustacheTag",
"start": 30,
"end": 38,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 31,
@ -61,6 +45,22 @@
"name": "animal",
"start": 18,
"end": 24
},
"expression": {
"type": "Identifier",
"start": 7,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 14
}
},
"name": "animals"
}
}
]

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 43,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 21,
"type": "Element",
"name": "span",
"attributes": [
{
"type": "Attribute",
"start": 6,
"end": 13,
"type": "Attribute",
"name": "attr",
"value": [
{
@ -29,22 +29,22 @@
"children": []
},
{
"type": "Text",
"start": 21,
"end": 22,
"type": "Text",
"raw": "\n",
"data": "\n"
},
{
"type": "Element",
"start": 22,
"end": 43,
"type": "Element",
"name": "span",
"attributes": [
{
"type": "Attribute",
"start": 28,
"end": 35,
"type": "Attribute",
"name": "attr",
"value": [
{

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 49,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 24,
"type": "Element",
"name": "span",
"attributes": [
{
"type": "Attribute",
"start": 6,
"end": 16,
"type": "Attribute",
"name": "attr",
"value": [
{
@ -29,22 +29,22 @@
"children": []
},
{
"type": "Text",
"start": 24,
"end": 25,
"type": "Text",
"raw": "\n",
"data": "\n"
},
{
"type": "Element",
"start": 25,
"end": 49,
"type": "Element",
"name": "span",
"attributes": [
{
"type": "Attribute",
"start": 31,
"end": 41,
"type": "Attribute",
"name": "attr",
"value": [
{

@ -1,27 +1,27 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 22,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 22,
"type": "Element",
"name": "h1",
"attributes": [],
"children": [
{
"type": "Text",
"start": 4,
"end": 10,
"type": "Text",
"raw": "hello ",
"data": "hello "
},
{
"type": "MustacheTag",
"start": 10,
"end": 16,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 11,
@ -40,9 +40,9 @@
}
},
{
"type": "Text",
"start": 16,
"end": 17,
"type": "Text",
"raw": "!",
"data": "!"
}

@ -1,20 +1,20 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 17,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 17,
"type": "Element",
"name": "span",
"attributes": [],
"children": [
{
"type": "Text",
"start": 6,
"end": 10,
"type": "Text",
"raw": "test",
"data": "test"
}

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 15,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 15,
"type": "Element",
"name": "!doctype",
"attributes": [
{
"type": "Attribute",
"start": 10,
"end": 14,
"type": "Attribute",
"name": "html",
"value": true
}

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 97,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 61,
"type": "Element",
"name": "button",
"attributes": [
{
@ -105,25 +105,25 @@
],
"children": [
{
"type": "Text",
"start": 46,
"end": 52,
"type": "Text",
"raw": "toggle",
"data": "toggle"
}
]
},
{
"type": "Text",
"start": 61,
"end": 63,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "IfBlock",
"start": 63,
"end": 97,
"type": "IfBlock",
"expression": {
"type": "Identifier",
"start": 68,
@ -142,16 +142,16 @@
},
"children": [
{
"type": "Element",
"start": 78,
"end": 91,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 81,
"end": 87,
"type": "Text",
"raw": "hello!",
"data": "hello!"
}

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 51,
"type": "Fragment",
"children": [
{
"type": "IfBlock",
"start": 0,
"end": 51,
"type": "IfBlock",
"expression": {
"type": "Identifier",
"start": 5,
@ -26,16 +26,16 @@
},
"children": [
{
"type": "Element",
"start": 11,
"end": 21,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 14,
"end": 17,
"type": "Text",
"raw": "foo",
"data": "foo"
}
@ -43,21 +43,21 @@
}
],
"else": {
"type": "ElseBlock",
"start": 29,
"end": 46,
"type": "ElseBlock",
"children": [
{
"type": "Element",
"start": 31,
"end": 45,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 34,
"end": 41,
"type": "Text",
"raw": "not foo",
"data": "not foo"
}

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 89,
"type": "Fragment",
"children": [
{
"type": "IfBlock",
"start": 0,
"end": 89,
"type": "IfBlock",
"expression": {
"type": "BinaryExpression",
"start": 5,
@ -59,16 +59,16 @@
},
"children": [
{
"type": "Element",
"start": 14,
"end": 41,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 17,
"end": 37,
"type": "Text",
"raw": "x is greater than 10",
"data": "x is greater than 10"
}
@ -76,15 +76,14 @@
}
],
"else": {
"type": "ElseBlock",
"start": 58,
"end": 84,
"type": "ElseBlock",
"children": [
{
"type": "IfBlock",
"start": 58,
"end": 89,
"type": "IfBlock",
"elseif": true,
"expression": {
"type": "BinaryExpression",
"start": 52,
@ -136,22 +135,23 @@
},
"children": [
{
"type": "Element",
"start": 60,
"end": 83,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 63,
"end": 79,
"type": "Text",
"raw": "x is less than 5",
"data": "x is less than 5"
}
]
}
]
],
"elseif": true
}
]
}

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 17,
"type": "Fragment",
"children": [
{
"type": "IfBlock",
"start": 0,
"end": 17,
"type": "IfBlock",
"expression": {
"type": "Identifier",
"start": 5,
@ -26,9 +26,9 @@
},
"children": [
{
"type": "Text",
"start": 9,
"end": 12,
"type": "Text",
"raw": "bar",
"data": "bar"
}

@ -1,66 +1,66 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 31,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 31,
"type": "Element",
"name": "ul",
"attributes": [],
"children": [
{
"type": "Text",
"start": 4,
"end": 6,
"type": "Text",
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "Element",
"start": 6,
"end": 13,
"type": "Element",
"name": "li",
"attributes": [],
"children": [
{
"type": "Text",
"start": 10,
"end": 13,
"type": "Text",
"raw": "a\n\t",
"data": "a\n\t"
}
]
},
{
"type": "Element",
"start": 13,
"end": 20,
"type": "Element",
"name": "li",
"attributes": [],
"children": [
{
"type": "Text",
"start": 17,
"end": 20,
"type": "Text",
"raw": "b\n\t",
"data": "b\n\t"
}
]
},
{
"type": "Element",
"start": 20,
"end": 26,
"type": "Element",
"name": "li",
"attributes": [],
"children": [
{
"type": "Text",
"start": 24,
"end": 26,
"type": "Text",
"raw": "c\n",
"data": "c\n"
}

@ -1,20 +1,20 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 19,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 19,
"type": "Element",
"name": "span",
"attributes": [],
"children": [
{
"type": "Text",
"start": 6,
"end": 12,
"type": "Text",
"raw": "&nbsp;",
"data": " "
}

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 148,
"type": "Fragment",
"children": [
{
"type": "IfBlock",
"start": 0,
"end": 33,
"type": "IfBlock",
"expression": {
"type": "Literal",
"start": 5,
@ -27,32 +27,32 @@
},
"children": [
{
"type": "Element",
"start": 12,
"end": 19,
"type": "Element",
"name": "input",
"attributes": [],
"children": []
}
],
"else": {
"type": "ElseBlock",
"start": 27,
"end": 28,
"type": "ElseBlock",
"children": []
}
},
{
"type": "Text",
"start": 33,
"end": 35,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "IfBlock",
"start": 35,
"end": 65,
"type": "IfBlock",
"expression": {
"type": "Literal",
"start": 40,
@ -72,32 +72,32 @@
},
"children": [
{
"type": "Element",
"start": 47,
"end": 51,
"type": "Element",
"name": "br",
"attributes": [],
"children": []
}
],
"else": {
"type": "ElseBlock",
"start": 59,
"end": 60,
"type": "ElseBlock",
"children": []
}
},
{
"type": "Text",
"start": 65,
"end": 67,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "AwaitBlock",
"start": 67,
"end": 108,
"type": "AwaitBlock",
"expression": {
"type": "Literal",
"start": 75,
@ -123,29 +123,29 @@
},
"error": null,
"pending": {
"type": "PendingBlock",
"start": 80,
"end": 90,
"type": "PendingBlock",
"children": [
{
"type": "Text",
"start": 80,
"end": 82,
"type": "Text",
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "Element",
"start": 82,
"end": 89,
"type": "Element",
"name": "input",
"attributes": [],
"children": []
},
{
"type": "Text",
"start": 89,
"end": 90,
"type": "Text",
"raw": "\n",
"data": "\n"
}
@ -153,14 +153,14 @@
"skip": false
},
"then": {
"type": "ThenBlock",
"start": 90,
"end": 100,
"type": "ThenBlock",
"children": [
{
"type": "Text",
"start": 99,
"end": 100,
"type": "Text",
"raw": "\n",
"data": "\n"
}
@ -168,24 +168,24 @@
"skip": false
},
"catch": {
"type": "CatchBlock",
"start": null,
"end": null,
"type": "CatchBlock",
"children": [],
"skip": true
}
},
{
"type": "Text",
"start": 108,
"end": 110,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "AwaitBlock",
"start": 110,
"end": 148,
"type": "AwaitBlock",
"expression": {
"type": "Literal",
"start": 118,
@ -211,29 +211,29 @@
},
"error": null,
"pending": {
"type": "PendingBlock",
"start": 123,
"end": 130,
"type": "PendingBlock",
"children": [
{
"type": "Text",
"start": 123,
"end": 125,
"type": "Text",
"raw": "\n\t",
"data": "\n\t"
},
{
"type": "Element",
"start": 125,
"end": 129,
"type": "Element",
"name": "br",
"attributes": [],
"children": []
},
{
"type": "Text",
"start": 129,
"end": 130,
"type": "Text",
"raw": "\n",
"data": "\n"
}
@ -241,14 +241,14 @@
"skip": false
},
"then": {
"type": "ThenBlock",
"start": 130,
"end": 140,
"type": "ThenBlock",
"children": [
{
"type": "Text",
"start": 139,
"end": 140,
"type": "Text",
"raw": "\n",
"data": "\n"
}
@ -256,9 +256,9 @@
"skip": false
},
"catch": {
"type": "CatchBlock",
"start": null,
"end": null,
"type": "CatchBlock",
"children": [],
"skip": true
}

@ -1,27 +1,27 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 34,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 34,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 3,
"end": 4,
"type": "Text",
"raw": " ",
"data": " "
},
{
"type": "RawMustacheTag",
"start": 4,
"end": 16,
"type": "RawMustacheTag",
"expression": {
"type": "Identifier",
"start": 11,
@ -40,16 +40,16 @@
}
},
{
"type": "Text",
"start": 16,
"end": 17,
"type": "Text",
"raw": " ",
"data": " "
},
{
"type": "RawMustacheTag",
"start": 17,
"end": 29,
"type": "RawMustacheTag",
"expression": {
"type": "Identifier",
"start": 24,
@ -68,9 +68,9 @@
}
},
{
"type": "Text",
"start": 29,
"end": 30,
"type": "Text",
"raw": " ",
"data": " "
}

@ -1,20 +1,20 @@
{
"html": {
"type": "Fragment",
"start": 30,
"end": 63,
"type": "Fragment",
"children": [
{
"type": "Text",
"start": 28,
"end": 30,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "Element",
"start": 30,
"end": 63,
"type": "Element",
"name": "canvas",
"attributes": [
{

@ -1,34 +1,34 @@
{
"html": {
"type": "Fragment",
"start": 79,
"end": 101,
"type": "Fragment",
"children": [
{
"type": "Text",
"start": 77,
"end": 79,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "Element",
"start": 79,
"end": 101,
"type": "Element",
"name": "h1",
"attributes": [],
"children": [
{
"type": "Text",
"start": 83,
"end": 89,
"type": "Text",
"raw": "Hello ",
"data": "Hello "
},
{
"type": "MustacheTag",
"start": 89,
"end": 95,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 90,
@ -47,9 +47,9 @@
}
},
{
"type": "Text",
"start": 95,
"end": 96,
"type": "Text",
"raw": "!",
"data": "!"
}

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 62,
"end": 60,
"type": "Fragment",
"children": [
{
"type": "Text",
"start": 60,
"end": 62,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
}

@ -1,34 +1,34 @@
{
"html": {
"type": "Fragment",
"start": 41,
"end": 63,
"type": "Fragment",
"children": [
{
"type": "Text",
"start": 39,
"end": 41,
"type": "Text",
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "Element",
"start": 41,
"end": 63,
"type": "Element",
"name": "h1",
"attributes": [],
"children": [
{
"type": "Text",
"start": 45,
"end": 51,
"type": "Text",
"raw": "Hello ",
"data": "Hello "
},
{
"type": "MustacheTag",
"start": 51,
"end": 57,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 52,
@ -47,9 +47,9 @@
}
},
{
"type": "Text",
"start": 57,
"end": 58,
"type": "Text",
"raw": "!",
"data": "!"
}

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 6,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 6,
"type": "Element",
"name": "div",
"attributes": [],
"children": []

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 57,
"type": "Fragment",
"children": [
{
"type": "IfBlock",
"start": 0,
"end": 57,
"type": "IfBlock",
"expression": {
"type": "BinaryExpression",
"start": 5,
@ -59,21 +59,21 @@
},
"children": [
{
"start": 17,
"end": 51,
"type": "InlineComponent",
"name": "svelte:self",
"start": 17,
"end": 51,
"attributes": [
{
"type": "Attribute",
"start": 30,
"end": 49,
"type": "Attribute",
"name": "depth",
"value": [
{
"type": "MustacheTag",
"start": 37,
"end": 48,
"type": "MustacheTag",
"expression": {
"type": "BinaryExpression",
"start": 38,

@ -1,26 +1,26 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 45,
"type": "Fragment",
"children": [
{
"type": "InlineComponent",
"start": 0,
"end": 45,
"type": "InlineComponent",
"name": "Component",
"attributes": [],
"children": [
{
"type": "Element",
"start": 11,
"end": 33,
"type": "Element",
"name": "div",
"attributes": [
{
"type": "Attribute",
"start": 16,
"end": 26,
"type": "Attribute",
"name": "slot",
"value": [
{

@ -1,27 +1,27 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 24,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 24,
"type": "Element",
"name": "p",
"attributes": [],
"children": [
{
"type": "Text",
"start": 3,
"end": 4,
"type": "Text",
"raw": " ",
"data": " "
},
{
"type": "MustacheTag",
"start": 4,
"end": 7,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 5,
@ -40,16 +40,16 @@
}
},
{
"type": "Text",
"start": 7,
"end": 8,
"type": "Text",
"raw": " ",
"data": " "
},
{
"type": "MustacheTag",
"start": 8,
"end": 11,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 9,
@ -68,16 +68,16 @@
}
},
{
"type": "Text",
"start": 11,
"end": 14,
"type": "Text",
"raw": " : ",
"data": " : "
},
{
"type": "MustacheTag",
"start": 14,
"end": 17,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 15,
@ -96,9 +96,9 @@
}
},
{
"type": "Text",
"start": 17,
"end": 20,
"type": "Text",
"raw": " : ",
"data": " : "
}

@ -1,19 +1,19 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 22,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 22,
"type": "Element",
"name": "div",
"attributes": [
{
"type": "Spread",
"start": 5,
"end": 15,
"type": "Spread",
"expression": {
"type": "Identifier",
"start": 9,

@ -1,27 +1,27 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 42,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 42,
"type": "Head",
"name": "svelte:head",
"start": 0,
"end": 42,
"attributes": [],
"children": [
{
"type": "Element",
"start": 13,
"end": 28,
"type": "Element",
"name": "style",
"attributes": [],
"children": [
{
"type": "Text",
"start": 20,
"end": 20,
"type": "Text",
"data": ""
}
]

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 61,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 61,
"type": "Element",
"name": "textarea",
"attributes": [],
"children": [
@ -19,9 +19,9 @@
"data": "\n\t<p>not actually an element. "
},
{
"type": "MustacheTag",
"start": 40,
"end": 45,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 41,

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 117,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 117,
"type": "Element",
"name": "textarea",
"attributes": [],
"children": [
@ -19,9 +19,9 @@
"data": "\n\t<p>not actu </textar ally an element. "
},
{
"type": "MustacheTag",
"start": 50,
"end": 55,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 51,

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 27,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 27,
"type": "Element",
"name": "div",
"attributes": [
{
@ -23,9 +23,9 @@
],
"children": [
{
"type": "Text",
"start": 13,
"end": 21,
"type": "Text",
"raw": "fades in",
"data": "fades in"
}

@ -1,13 +1,13 @@
{
"html": {
"type": "Fragment",
"start": 0,
"end": 45,
"type": "Fragment",
"children": [
{
"type": "Element",
"start": 0,
"end": 45,
"type": "Element",
"name": "div",
"attributes": [
{
@ -91,9 +91,9 @@
],
"children": [
{
"type": "Text",
"start": 31,
"end": 39,
"type": "Text",
"raw": "fades in",
"data": "fades in"
}

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

Loading…
Cancel
Save