svelte
Advanced tools
Comparing version 5.0.0-next.176 to 5.0.0-next.177
@@ -5,3 +5,3 @@ { | ||
"license": "MIT", | ||
"version": "5.0.0-next.176", | ||
"version": "5.0.0-next.177", | ||
"type": "module", | ||
@@ -8,0 +8,0 @@ "types": "./types/index.d.ts", |
@@ -734,2 +734,12 @@ /* This file is generated by scripts/process-messages/index.js. Do not edit! */ | ||
/** | ||
* Expected a `%character%` character immediately following the opening bracket | ||
* @param {null | number | NodeLike} node | ||
* @param {string} character | ||
* @returns {never} | ||
*/ | ||
export function block_unexpected_character(node, character) { | ||
e(node, "block_unexpected_character", `Expected a \`${character}\` character immediately following the opening bracket`); | ||
} | ||
/** | ||
* Unexpected block closing tag | ||
@@ -736,0 +746,0 @@ * @param {null | number | NodeLike} node |
@@ -42,3 +42,4 @@ import read_pattern from '../read/context.js'; | ||
function open(parser) { | ||
const start = parser.index - 2; | ||
let start = parser.index - 2; | ||
while (parser.template[start] !== '{') start -= 1; | ||
@@ -347,5 +348,8 @@ if (parser.eat('if')) { | ||
let elseif_start = start - 1; | ||
while (parser.template[elseif_start] !== '{') elseif_start -= 1; | ||
/** @type {ReturnType<typeof parser.append<import('#compiler').IfBlock>>} */ | ||
const child = parser.append({ | ||
start: start - 1, | ||
start: elseif_start, | ||
end: -1, | ||
@@ -352,0 +356,0 @@ type: 'IfBlock', |
@@ -1013,2 +1013,5 @@ import is_reference from 'is-reference'; | ||
}); | ||
const binding = state.scope.get(node.local.name); | ||
if (binding) binding.reassigned = true; | ||
}, | ||
@@ -1015,0 +1018,0 @@ ExportNamedDeclaration(node, { next, state }) { |
@@ -1094,2 +1094,16 @@ import is_reference from 'is-reference'; | ||
/** | ||
* Validates that the opening of a control flow block is `{` immediately followed by the expected character. | ||
* In legacy mode whitespace is allowed inbetween. TODO remove once legacy mode is gone and move this into parser instead. | ||
* @param {{start: number; end: number}} node | ||
* @param {import('./types.js').AnalysisState} state | ||
* @param {string} expected | ||
*/ | ||
function validate_opening_tag(node, state, expected) { | ||
if (state.analysis.source[node.start + 1] !== expected) { | ||
// avoid a sea of red and only mark the first few characters | ||
e.block_unexpected_character({ start: node.start, end: node.start + 5 }, expected); | ||
} | ||
} | ||
/** | ||
* @param {import('estree').AssignmentExpression | import('estree').UpdateExpression} node | ||
@@ -1221,2 +1235,4 @@ * @param {import('estree').Pattern | import('estree').Expression} argument | ||
EachBlock(node, { next, state }) { | ||
validate_opening_tag(node, state, '#'); | ||
const context = node.context; | ||
@@ -1231,2 +1247,47 @@ if ( | ||
}, | ||
IfBlock(node, { state, path }) { | ||
const parent = path.at(-1); | ||
const expected = | ||
path.at(-2)?.type === 'IfBlock' && parent?.type === 'Fragment' && parent.nodes.length === 1 | ||
? ':' | ||
: '#'; | ||
validate_opening_tag(node, state, expected); | ||
}, | ||
AwaitBlock(node, { state }) { | ||
validate_opening_tag(node, state, '#'); | ||
if (node.value) { | ||
const start = /** @type {number} */ (node.value.start); | ||
const match = state.analysis.source.substring(start - 10, start).match(/{(\s*):then\s+$/); | ||
if (match && match[1] !== '') { | ||
e.block_unexpected_character({ start: start - 10, end: start }, ':'); | ||
} | ||
} | ||
if (node.error) { | ||
const start = /** @type {number} */ (node.error.start); | ||
const match = state.analysis.source.substring(start - 10, start).match(/{(\s*):catch\s+$/); | ||
if (match && match[1] !== '') { | ||
e.block_unexpected_character({ start: start - 10, end: start }, ':'); | ||
} | ||
} | ||
}, | ||
KeyBlock(node, { state }) { | ||
validate_opening_tag(node, state, '#'); | ||
}, | ||
SnippetBlock(node, { state }) { | ||
validate_opening_tag(node, state, '#'); | ||
}, | ||
ConstTag(node, { state }) { | ||
validate_opening_tag(node, state, '@'); | ||
}, | ||
HtmlTag(node, { state }) { | ||
validate_opening_tag(node, state, '@'); | ||
}, | ||
DebugTag(node, { state }) { | ||
validate_opening_tag(node, state, '@'); | ||
}, | ||
RenderTag(node, { state }) { | ||
validate_opening_tag(node, state, '@'); | ||
}, | ||
VariableDeclarator(node, { state }) { | ||
@@ -1233,0 +1294,0 @@ ensure_no_module_import_conflict(node, state); |
@@ -201,10 +201,34 @@ /** @import * as ESTree from 'estree' */ | ||
/** @type {Array<ESTree.Property | ESTree.SpreadElement>} */ | ||
const component_returned_object = analysis.exports.map(({ name, alias }) => { | ||
const component_returned_object = analysis.exports.flatMap(({ name, alias }) => { | ||
const binding = instance_state.scope.get(name); | ||
const expression = serialize_get_binding(b.id(name), instance_state); | ||
const getter = b.get(alias ?? name, [b.return(expression)]); | ||
if (expression.type === 'Identifier' && !options.dev) { | ||
return b.init(alias ?? name, expression); | ||
if (expression.type === 'Identifier') { | ||
if (binding?.declaration_kind === 'let' || binding?.declaration_kind === 'var') { | ||
return [ | ||
getter, | ||
b.set(alias ?? name, [b.stmt(b.assignment('=', expression, b.id('$$value')))]) | ||
]; | ||
} else if (!options.dev) { | ||
return b.init(alias ?? name, expression); | ||
} | ||
} | ||
return b.get(alias ?? name, [b.return(expression)]); | ||
if (binding?.kind === 'state' || binding?.kind === 'frozen_state') { | ||
return [ | ||
getter, | ||
b.set(alias ?? name, [ | ||
b.stmt( | ||
b.call( | ||
'$.set', | ||
b.id(name), | ||
b.call(binding.kind === 'state' ? '$.proxy' : '$.freeze', b.id('$$value')) | ||
) | ||
) | ||
]) | ||
]; | ||
} | ||
return getter; | ||
}); | ||
@@ -211,0 +235,0 @@ |
@@ -457,3 +457,2 @@ import * as b from '../../../utils/builders.js'; | ||
value, | ||
b.true, | ||
b.null, | ||
@@ -460,0 +459,0 @@ typeof proxy_reference === 'string' |
/** @import { Source, Effect } from '#client' */ | ||
import { empty } from '../dom/operations.js'; | ||
import { block, branch, destroy_effect } from '../reactivity/effects.js'; | ||
import { set_should_intro } from '../render.js'; | ||
import { get } from '../runtime.js'; | ||
import { check_target } from './legacy.js'; | ||
@@ -23,2 +21,4 @@ /** | ||
let ran = false; | ||
block(() => { | ||
@@ -34,3 +34,5 @@ const component = get(source); | ||
effect = branch(() => { | ||
set_should_intro(false); | ||
// when the component is invalidated, replace it without transitions | ||
if (ran) set_should_intro(false); | ||
// preserve getters/setters | ||
@@ -44,8 +46,11 @@ Object.defineProperties( | ||
); | ||
set_should_intro(true); | ||
if (ran) set_should_intro(true); | ||
}); | ||
}); | ||
ran = true; | ||
return instance; | ||
}; | ||
} |
@@ -158,2 +158,3 @@ import { DEV } from 'esm-env'; | ||
var current = prev || {}; | ||
var is_option_element = element.tagName === 'OPTION'; | ||
@@ -182,2 +183,22 @@ for (var key in prev) { | ||
let value = next[key]; | ||
// Up here because we want to do this for the initial value, too, even if it's undefined, | ||
// and this wouldn't be reached in case of undefined because of the equality check below | ||
if (is_option_element && key === 'value' && value == null) { | ||
// The <option> element is a special case because removing the value attribute means | ||
// the value is set to the text content of the option element, and setting the value | ||
// to null or undefined means the value is set to the string "null" or "undefined". | ||
// To align with how we handle this case in non-spread-scenarios, this logic is needed. | ||
// There's a super-edge-case bug here that is left in in favor of smaller code size: | ||
// Because of the "set missing props to null" logic above, we can't differentiate | ||
// between a missing value and an explicitly set value of null or undefined. That means | ||
// that once set, the value attribute of an <option> element can't be removed. This is | ||
// a very rare edge case, and removing the attribute altogether isn't possible either | ||
// for the <option value={undefined}> case, so we're not losing any functionality here. | ||
// @ts-ignore | ||
element.value = element.__value = ''; | ||
current[key] = value; | ||
continue; | ||
} | ||
var prev_value = current[key]; | ||
@@ -184,0 +205,0 @@ if (value === prev_value) continue; |
@@ -14,3 +14,3 @@ import { DEV } from 'esm-env'; | ||
import { check_ownership, widen_ownership } from './dev/ownership.js'; | ||
import { mutable_source, source, set } from './reactivity/sources.js'; | ||
import { source, set } from './reactivity/sources.js'; | ||
import { STATE_FROZEN_SYMBOL, STATE_SYMBOL } from './constants.js'; | ||
@@ -23,3 +23,2 @@ import { UNINITIALIZED } from '../../constants.js'; | ||
* @param {T} value | ||
* @param {boolean} [immutable] | ||
* @param {import('#client').ProxyMetadata | null} [parent] | ||
@@ -29,3 +28,3 @@ * @param {import('#client').Source<T>} [prev] dev mode only | ||
*/ | ||
export function proxy(value, immutable = true, parent = null, prev) { | ||
export function proxy(value, parent = null, prev) { | ||
if ( | ||
@@ -65,3 +64,2 @@ typeof value === 'object' && | ||
a: is_array(value), | ||
i: immutable, | ||
p: proxy, | ||
@@ -176,3 +174,3 @@ t: value | ||
const s = metadata.s.get(prop); | ||
if (s !== undefined) set(s, proxy(descriptor.value, metadata.i, metadata)); | ||
if (s !== undefined) set(s, proxy(descriptor.value, metadata)); | ||
} | ||
@@ -223,3 +221,3 @@ | ||
if (s === undefined && (!(prop in target) || get_descriptor(target, prop)?.writable)) { | ||
s = (metadata.i ? source : mutable_source)(proxy(target[prop], metadata.i, metadata)); | ||
s = source(proxy(target[prop], metadata)); | ||
metadata.s.set(prop, s); | ||
@@ -265,5 +263,3 @@ } | ||
if (s === undefined) { | ||
s = (metadata.i ? source : mutable_source)( | ||
has ? proxy(target[prop], metadata.i, metadata) : UNINITIALIZED | ||
); | ||
s = source(has ? proxy(target[prop], metadata) : UNINITIALIZED); | ||
metadata.s.set(prop, s); | ||
@@ -293,3 +289,3 @@ } | ||
if (s !== undefined) { | ||
set(s, proxy(value, metadata.i, metadata)); | ||
set(s, proxy(value, metadata)); | ||
} | ||
@@ -296,0 +292,0 @@ const is_array = metadata.a; |
@@ -27,4 +27,4 @@ import { DEV } from 'esm-env'; | ||
* This is normally true — block effects should run their intro transitions — | ||
* but is false during hydration and mounting (unless `options.intro` is `true`) | ||
* and when creating the children of a `<svelte:element>` that just changed tag | ||
* but is false during hydration (unless `options.intro` is `true`) and | ||
* when creating the children of a `<svelte:element>` that just changed tag | ||
*/ | ||
@@ -70,3 +70,4 @@ export let should_intro = true; | ||
/** | ||
* Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component | ||
* Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component. | ||
* Transitions will play during the initial render unless the `intro` option is set to `false`. | ||
* | ||
@@ -131,2 +132,3 @@ * @template {Record<string, any>} Props | ||
options.intro = options.intro ?? false; | ||
const target = options.target; | ||
@@ -196,3 +198,3 @@ const previous_hydrate_nodes = hydrate_nodes; | ||
*/ | ||
function _mount(Component, { target, anchor, props = {}, events, context, intro = false }) { | ||
function _mount(Component, { target, anchor, props = {}, events, context, intro = true }) { | ||
init_operations(); | ||
@@ -199,0 +201,0 @@ |
@@ -183,4 +183,2 @@ import type { Store } from '#shared'; | ||
a: boolean; | ||
/** Immutable: Whether to use a source or mutable source under the hood */ | ||
i: boolean; | ||
/** The associated proxy */ | ||
@@ -187,0 +185,0 @@ p: ProxyStateObject<T>; |
/** @import { ComponentConstructorOptions, ComponentType, SvelteComponent, Component } from 'svelte' */ | ||
import { proxy } from '../internal/client/proxy.js'; | ||
import { mutable_source, get, set } from 'svelte/internal/client'; | ||
import { user_pre_effect } from '../internal/client/reactivity/effects.js'; | ||
import { hydrate, mount, unmount } from '../internal/client/render.js'; | ||
import { define_property } from '../internal/client/utils.js'; | ||
import { safe_not_equal } from '../internal/client/reactivity/equality.js'; | ||
@@ -72,6 +73,34 @@ /** | ||
constructor(options) { | ||
// Using proxy state here isn't completely mirroring the Svelte 4 behavior, because mutations to a property | ||
// cause fine-grained updates to only the places where that property is used, and not the entire property. | ||
// Reactive statements and actions (the things where this matters) are handling this properly regardless, so it should be fine in practise. | ||
const props = proxy({ ...(options.props || {}), $$events: {} }, false); | ||
var sources = new Map(); | ||
/** | ||
* @param {string | symbol} key | ||
* @param {unknown} value | ||
*/ | ||
var add_source = (key, value) => { | ||
var s = mutable_source(value); | ||
sources.set(key, s); | ||
return s; | ||
}; | ||
// Replicate coarse-grained props through a proxy that has a version source for | ||
// each property, which is increment on updates to the property itself. Do not | ||
// use our $state proxy because that one has fine-grained reactivity. | ||
const props = new Proxy( | ||
{ ...(options.props || {}), $$events: {} }, | ||
{ | ||
get(target, prop) { | ||
return get(sources.get(prop) ?? add_source(prop, Reflect.get(target, prop))); | ||
}, | ||
has(target, prop) { | ||
get(sources.get(prop) ?? add_source(prop, Reflect.get(target, prop))); | ||
return Reflect.has(target, prop); | ||
}, | ||
set(target, prop, value) { | ||
set(sources.get(prop) ?? add_source(prop, value), value); | ||
return Reflect.set(target, prop, value); | ||
} | ||
} | ||
); | ||
this.#instance = (options.hydrate ? hydrate : mount)(options.component, { | ||
@@ -81,3 +110,3 @@ target: options.target, | ||
context: options.context, | ||
intro: options.intro, | ||
intro: options.intro ?? false, | ||
recover: options.recover | ||
@@ -105,2 +134,3 @@ }); | ||
}; | ||
this.#instance.$destroy = () => { | ||
@@ -107,0 +137,0 @@ unmount(this.#instance); |
@@ -9,3 +9,3 @@ // generated during release, do not modify | ||
*/ | ||
export const VERSION = '5.0.0-next.176'; | ||
export const VERSION = '5.0.0-next.177'; | ||
export const PUBLIC_VERSION = '5'; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2124833
46597