Socket
Socket
Sign inDemoInstall

svelte

Package Overview
Dependencies
Maintainers
3
Versions
752
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

svelte - npm Package Compare versions

Comparing version 5.0.0-next.262 to 5.0.0-next.263

2

package.json

@@ -5,3 +5,3 @@ {

"license": "MIT",
"version": "5.0.0-next.262",
"version": "5.0.0-next.263",
"type": "module",

@@ -8,0 +8,0 @@ "types": "./types/index.d.ts",

@@ -1,2 +0,2 @@

/** @import { VariableDeclarator, Node, Identifier } from 'estree' */
/** @import { VariableDeclarator, Node, Identifier, AssignmentExpression, LabeledStatement, ExpressionStatement } from 'estree' */
/** @import { Visitors } from 'zimmerframe' */

@@ -13,3 +13,7 @@ /** @import { ComponentAnalysis } from '../phases/types.js' */

import { reset, reset_warning_filter } from '../state.js';
import { extract_identifiers } from '../utils/ast.js';
import {
extract_identifiers,
extract_all_identifiers_from_expression,
is_text_attribute
} from '../utils/ast.js';
import { migrate_svelte_ignore } from '../utils/extract_svelte_ignore.js';

@@ -27,5 +31,6 @@ import { validate_component_options } from '../validate-options.js';

* @param {string} source
* @param {{filename?: string}} [options]
* @returns {{ code: string; }}
*/
export function migrate(source) {
export function migrate(source, { filename } = {}) {
try {

@@ -42,3 +47,3 @@ // Blank CSS, could contain SCSS or similar that needs a preprocessor.

reset_warning_filter(() => false);
reset(source, { filename: 'migrate.svelte' });
reset(source, { filename: filename ?? 'migrate.svelte' });

@@ -70,2 +75,3 @@ let parsed = parse(source);

analysis,
filename,
str,

@@ -93,7 +99,10 @@ indent,

passive: analysis.root.unique('passive').name,
nonpassive: analysis.root.unique('nonpassive').name
nonpassive: analysis.root.unique('nonpassive').name,
svelte_self: analysis.root.unique('SvelteSelf').name
},
legacy_imports: new Set(),
script_insertions: new Set(),
derived_components: new Map()
derived_components: new Map(),
derived_labeled_statements: new Set(),
has_svelte_self: false
};

@@ -125,3 +134,4 @@

analysis.uses_rest_props ||
analysis.uses_props;
analysis.uses_props ||
state.has_svelte_self;

@@ -132,2 +142,10 @@ if (!parsed.instance && need_script) {

if (state.has_svelte_self && filename) {
const file = filename.split('/').pop();
str.appendRight(
insertion_point,
`\n${indent}import ${state.names.svelte_self} from './${file}';`
);
}
const specifiers = [...state.legacy_imports].map((imported) => {

@@ -202,3 +220,3 @@ const local = state.names[imported];

if (analysis.uses_props || analysis.uses_rest_props) {
type = `{Record<string, any>}`;
type = `Record<string, any>`;
} else {

@@ -304,4 +322,5 @@ type = `{${state.props

* analysis: ComponentAnalysis;
* filename?: string;
* indent: string;
* props: Array<{ local: string; exported: string; init: string; bindable: boolean; slot_name?: string; optional: boolean; type: string; comment?: string, type_only?: boolean }>;
* props: Array<{ local: string; exported: string; init: string; bindable: boolean; slot_name?: string; optional: boolean; type: string; comment?: string; type_only?: boolean; needs_refine_type?: boolean; }>;
* props_insertion_point: number;

@@ -313,3 +332,5 @@ * has_props_rune: boolean;

* script_insertions: Set<string>;
* derived_components: Map<string, string>
* derived_components: Map<string, string>;
* derived_labeled_statements: Set<LabeledStatement>;
* has_svelte_self: boolean;
* }} State

@@ -362,3 +383,3 @@ */

},
VariableDeclaration(node, { state, path }) {
VariableDeclaration(node, { state, path, visit }) {
if (state.scope !== state.analysis.instance.scope) {

@@ -484,6 +505,114 @@ return;

} else {
state.str.prependLeft(
/** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
' = $state()'
/**
* @type {AssignmentExpression | undefined}
*/
let assignment_in_labeled;
/**
* @type {LabeledStatement | undefined}
*/
let labeled_statement;
// Analyze declaration bindings to see if they're exclusively updated within a single reactive statement
const possible_derived = bindings.every((binding) =>
binding.references.every((reference) => {
const declaration = reference.path.find((el) => el.type === 'VariableDeclaration');
const assignment = reference.path.find((el) => el.type === 'AssignmentExpression');
const update = reference.path.find((el) => el.type === 'UpdateExpression');
const labeled = reference.path.find(
(el) => el.type === 'LabeledStatement' && el.label.name === '$'
);
if (assignment && labeled) {
if (assignment_in_labeled) return false;
assignment_in_labeled = /** @type {AssignmentExpression} */ (assignment);
labeled_statement = /** @type {LabeledStatement} */ (labeled);
}
return !update && (declaration || (labeled && assignment) || (!labeled && !assignment));
})
);
const labeled_has_single_assignment =
labeled_statement?.body.type === 'BlockStatement' &&
labeled_statement.body.body.length === 1;
const is_expression_assignment =
labeled_statement?.body.type === 'ExpressionStatement' &&
labeled_statement.body.expression.type === 'AssignmentExpression';
let should_be_state = false;
if (is_expression_assignment) {
const body = /**@type {ExpressionStatement}*/ (labeled_statement?.body);
const expression = /**@type {AssignmentExpression}*/ (body.expression);
const [, ids] = extract_all_identifiers_from_expression(expression.right);
if (ids.length === 0) {
should_be_state = true;
state.derived_labeled_statements.add(
/** @type {LabeledStatement} */ (labeled_statement)
);
}
}
if (
!should_be_state &&
possible_derived &&
assignment_in_labeled &&
labeled_statement &&
(labeled_has_single_assignment || is_expression_assignment)
) {
// Someone wrote a `$: { ... }` statement which we can turn into a `$derived`
state.str.appendRight(
/** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
' = $derived('
);
visit(assignment_in_labeled.right);
state.str.appendRight(
/** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
state.str
.snip(
/** @type {number} */ (assignment_in_labeled.right.start),
/** @type {number} */ (assignment_in_labeled.right.end)
)
.toString()
);
state.str.remove(
/** @type {number} */ (labeled_statement.start),
/** @type {number} */ (labeled_statement.end)
);
state.str.appendRight(
/** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
')'
);
state.derived_labeled_statements.add(labeled_statement);
} else {
state.str.prependLeft(
/** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
' = $state('
);
if (should_be_state) {
// someone wrote a `$: foo = ...` statement which we can turn into `let foo = $state(...)`
state.str.appendRight(
/** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
state.str
.snip(
/** @type {number} */ (
/** @type {AssignmentExpression} */ (assignment_in_labeled).right.start
),
/** @type {number} */ (
/** @type {AssignmentExpression} */ (assignment_in_labeled).right.end
)
)
.toString()
);
state.str.remove(
/** @type {number} */ (/** @type {LabeledStatement} */ (labeled_statement).start),
/** @type {number} */ (/** @type {LabeledStatement} */ (labeled_statement).end)
);
}
state.str.appendRight(
/** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
')'
);
}
}

@@ -519,2 +648,3 @@ }

if (node.label.name !== '$') return;
if (state.derived_labeled_statements.has(node)) return;

@@ -528,2 +658,5 @@ next();

const ids = extract_identifiers(node.body.expression.left);
const [, expression_ids] = extract_all_identifiers_from_expression(
node.body.expression.right
);
const bindings = ids.map((id) => state.scope.get(id.name));

@@ -559,10 +692,20 @@ const reassigned_bindings = bindings.filter((b) => b?.reassigned);

for (const binding of reassigned_bindings) {
if (binding && ids.includes(binding.node)) {
if (binding && (ids.includes(binding.node) || expression_ids.length === 0)) {
const init =
binding.kind === 'state'
? ' = $state()'
: expression_ids.length === 0
? ` = $state(${state.str.original.substring(/** @type {number} */ (node.body.expression.right.start), node.body.expression.right.end)})`
: '';
// implicitly-declared variable which we need to make explicit
state.str.prependRight(
state.str.prependLeft(
/** @type {number} */ (node.start),
`let ${binding.node.name}${binding.kind === 'state' ? ' = $state()' : ''};\n${state.indent}`
`let ${binding.node.name}${init};\n${state.indent}`
);
}
}
if (expression_ids.length === 0 && !bindings.some((b) => b?.kind === 'store_sub')) {
state.str.remove(/** @type {number} */ (node.start), /** @type {number} */ (node.end));
return;
}
}

@@ -605,3 +748,4 @@ }

},
RegularElement(node, { state, next }) {
RegularElement(node, { state, path, next }) {
migrate_slot_usage(node, path, state);
handle_events(node, state);

@@ -619,3 +763,40 @@ // Strip off any namespace from the beginning of the node name.

},
SvelteElement(node, { state, next }) {
SvelteSelf(node, { state, next }) {
const source = state.str.original.substring(node.start, node.end);
if (!state.filename) {
const indent = guess_indent(source);
state.str.prependRight(
node.start,
`<!-- @migration-task: svelte:self is deprecated, import this Svelte file into itself instead -->\n${indent}`
);
next();
return;
}
// overwrite the open tag
state.str.overwrite(
node.start + 1,
node.start + 1 + 'svelte:self'.length,
`${state.names.svelte_self}`
);
// if it has a fragment we need to overwrite the closing tag too
if (node.fragment.nodes.length > 0) {
state.str.overwrite(
state.str.original.lastIndexOf('<', node.end) + 2,
node.end - 1,
`${state.names.svelte_self}`
);
} else if (!source.endsWith('/>')) {
// special case for case `<svelte:self></svelte:self>` it has no fragment but
// we still need to overwrite the end tag
state.str.overwrite(
node.start + source.lastIndexOf('</', node.end) + 2,
node.end - 1,
`${state.names.svelte_self}`
);
}
state.has_svelte_self = true;
next();
},
SvelteElement(node, { state, path, next }) {
migrate_slot_usage(node, path, state);
if (node.tag.type === 'Literal') {

@@ -644,5 +825,11 @@ let is_static = true;

},
Component(node, { state, path, next }) {
next();
migrate_slot_usage(node, path, state);
},
SvelteComponent(node, { state, next, path }) {
next();
migrate_slot_usage(node, path, state);
let expression = state.str

@@ -686,3 +873,3 @@ .snip(

);
state.str.prependLeft(
state.str.appendRight(
position,

@@ -714,2 +901,6 @@ `{@const ${expression} = ${current_expression}}\n${indent}`

},
SvelteFragment(node, { state, path, next }) {
migrate_slot_usage(node, path, state);
next();
},
SvelteWindow(node, { state, next }) {

@@ -727,3 +918,5 @@ handle_events(node, state);

},
SlotElement(node, { state, next }) {
SlotElement(node, { state, path, next, visit }) {
migrate_slot_usage(node, path, state);
if (state.analysis.custom_element) return;

@@ -743,9 +936,18 @@ let name = 'children';

attr.value === true || Array.isArray(attr.value) ? attr.value : [attr.value];
const value =
attr_value !== true
? state.str.original.substring(
attr_value[0].start,
attr_value[attr_value.length - 1].end
)
: 'true';
let value = 'true';
if (attr_value !== true) {
const first = attr_value[0];
const last = attr_value[attr_value.length - 1];
for (const attr of attr_value) {
visit(attr);
}
value = state.str
.snip(
first.type === 'Text'
? first.start - 1
: /** @type {number} */ (first.expression.start),
last.type === 'Text' ? last.end + 1 : /** @type {number} */ (last.expression.end)
)
.toString();
}
slot_props += value === attr.name ? `${value}, ` : `${attr.name}: ${value}, `;

@@ -778,2 +980,5 @@ }

});
} else if (existing_prop.needs_refine_type) {
existing_prop.type = `import('svelte').${slot_props ? 'Snippet<[any]>' : 'Snippet'}`;
existing_prop.needs_refine_type = false;
}

@@ -786,7 +991,11 @@

node.fragment.nodes[0].start,
`{#if ${name}}{@render ${name}(${slot_props})}{:else}`
`{#if ${name}}{@render ${state.analysis.uses_props ? `${state.names.props}.` : ''}${name}(${slot_props})}{:else}`
);
state.str.update(node.fragment.nodes[node.fragment.nodes.length - 1].end, node.end, '{/if}');
} else {
state.str.update(node.start, node.end, `{@render ${name}?.(${slot_props})}`);
state.str.update(
node.start,
node.end,
`{@render ${state.analysis.uses_props ? `${state.names.props}.` : ''}${name}?.(${slot_props})}`
);
}

@@ -803,2 +1012,125 @@ },

/**
* @param {AST.RegularElement | AST.SvelteElement | AST.SvelteComponent | AST.Component | AST.SlotElement | AST.SvelteFragment} node
* @param {SvelteNode[]} path
* @param {State} state
*/
function migrate_slot_usage(node, path, state) {
const parent = path.at(-2);
// Bail on custom element slot usage
if (
parent?.type !== 'Component' &&
parent?.type !== 'SvelteComponent' &&
node.type !== 'Component' &&
node.type !== 'SvelteComponent'
) {
return;
}
let snippet_name = 'children';
let snippet_props = [];
for (let attribute of node.attributes) {
if (
attribute.type === 'Attribute' &&
attribute.name === 'slot' &&
is_text_attribute(attribute)
) {
snippet_name = attribute.value[0].data;
state.str.remove(attribute.start, attribute.end);
}
if (attribute.type === 'LetDirective') {
snippet_props.push(
attribute.name +
(attribute.expression
? `: ${state.str.original.substring(/** @type {number} */ (attribute.expression.start), /** @type {number} */ (attribute.expression.end))}`
: '')
);
state.str.remove(attribute.start, attribute.end);
}
}
if (node.type === 'SvelteFragment' && node.fragment.nodes.length > 0) {
// remove node itself, keep content
state.str.remove(node.start, node.fragment.nodes[0].start);
state.str.remove(node.fragment.nodes[node.fragment.nodes.length - 1].end, node.end);
}
const props = snippet_props.length > 0 ? `{ ${snippet_props.join(', ')} }` : '';
if (snippet_name === 'children' && node.type !== 'SvelteFragment') {
if (snippet_props.length === 0) return; // nothing to do
let inner_start = 0;
let inner_end = 0;
for (let i = 0; i < node.fragment.nodes.length; i++) {
const inner = node.fragment.nodes[i];
const is_empty_text = inner.type === 'Text' && !inner.data.trim();
if (
(inner.type === 'RegularElement' ||
inner.type === 'SvelteElement' ||
inner.type === 'Component' ||
inner.type === 'SvelteComponent' ||
inner.type === 'SlotElement' ||
inner.type === 'SvelteFragment') &&
inner.attributes.some((attr) => attr.type === 'Attribute' && attr.name === 'slot')
) {
if (inner_start && !inner_end) {
// End of default slot content
inner_end = inner.start;
}
} else if (!inner_start && !is_empty_text) {
// Start of default slot content
inner_start = inner.start;
} else if (inner_end && !is_empty_text) {
// There was default slot content before, then some named slot content, now some default slot content again.
// We're moving the last character back by one to avoid the closing {/snippet} tag inserted afterwards
// to come before the opening {#snippet} tag of the named slot.
state.str.update(inner_end - 1, inner_end, '');
state.str.prependLeft(inner_end - 1, state.str.original[inner_end - 1]);
state.str.move(inner.start, inner.end, inner_end - 1);
}
}
if (!inner_end) {
inner_end = node.fragment.nodes[node.fragment.nodes.length - 1].end;
}
state.str.appendLeft(
inner_start,
`{#snippet ${snippet_name}(${props})}\n${state.indent.repeat(path.length)}`
);
state.str.indent(state.indent, {
exclude: [
[0, inner_start],
[inner_end, state.str.original.length]
]
});
if (inner_end < node.fragment.nodes[node.fragment.nodes.length - 1].end) {
// Named slots coming afterwards
state.str.prependLeft(inner_end, `{/snippet}\n${state.indent.repeat(path.length)}`);
} else {
// No named slots coming afterwards
state.str.prependLeft(
inner_end,
`${state.indent.repeat(path.length)}{/snippet}\n${state.indent.repeat(path.length - 1)}`
);
}
} else {
// Named slot or `svelte:fragment`: wrap element itself in a snippet
state.str.prependLeft(
node.start,
`{#snippet ${snippet_name}(${props})}\n${state.indent.repeat(path.length - 2)}`
);
state.str.indent(state.indent, {
exclude: [
[0, node.start],
[node.end, state.str.original.length]
]
});
state.str.appendLeft(node.end, `\n${state.indent.repeat(path.length - 2)}{/snippet}`);
}
}
/**
* @param {VariableDeclarator} declarator

@@ -986,3 +1318,3 @@ * @param {MagicString} str

if (state.analysis.uses_props) {
if (state.analysis.uses_props && node.name !== '$$slots') {
if (node.name === '$$props' || node.name === '$$restProps') {

@@ -1009,6 +1341,38 @@ // not 100% correct for $$restProps but it'll do

if (parent?.type === 'MemberExpression') {
state.str.update(/** @type {number} */ (node.start), parent.property.start, '');
if (parent.property.name === 'default') {
state.str.update(parent.property.start, parent.property.end, 'children');
if (state.analysis.custom_element) return;
let name = parent.property.type === 'Literal' ? parent.property.value : parent.property.name;
let slot_name = name;
const existing_prop = state.props.find((prop) => prop.slot_name === name);
if (existing_prop) {
name = existing_prop.local;
} else if (name !== 'default') {
name = state.scope.generate(name);
}
name = name === 'default' ? 'children' : name;
if (!existing_prop) {
state.props.push({
local: name,
exported: name,
init: '',
bindable: false,
optional: true,
slot_name,
// if it's the first time we encounter this slot
// we start with any and delegate to when the slot
// is actually rendered (it might not happen in that case)
// any is still a safe bet
type: `import('svelte').Snippet<[any]>}`,
needs_refine_type: true
});
}
state.str.update(
/** @type {number} */ (node.start),
parent.property.start,
state.analysis.uses_props ? `${state.names.props}.` : ''
);
state.str.update(parent.property.start, parent.end, name);
}

@@ -1015,0 +1379,0 @@ // else passed as identifier, we don't know what to do here, so let it error

@@ -401,3 +401,6 @@ /** @import { EachItem, EachState, Effect, MaybeSource, Source, TemplateNode, TransitionManager, Value } from '#client' */

while (current !== null) {
to_destroy.push(current);
// Inert effects are currently outroing and will be removed once the transition is finished
if ((current.e.f & INERT) === 0) {
to_destroy.push(current);
}
current = current.next;

@@ -404,0 +407,0 @@ }

@@ -141,8 +141,6 @@ import { hydrating } from '../../hydration.js';

export function bind_paused(media, get, set = get) {
var mounted = hydrating;
var paused = get();
var callback = () => {
var update = () => {
if (paused !== media.paused) {
paused = media.paused;
set((paused = media.paused));

@@ -152,42 +150,16 @@ }

if (paused == null) {
callback();
}
// If someone switches the src while media is playing, the player will pause.
// Listen to the canplay event to get notified of this situation.
listen(media, ['play', 'pause', 'canplay'], update, paused == null);
// Defer listening if not mounted yet so that the first canplay event doesn't cause a potentially wrong update
if (mounted) {
// If someone switches the src while media is playing, the player will pause.
// Listen to the canplay event to get notified of this situation.
listen(media, ['play', 'pause', 'canplay'], callback, false);
}
render_effect(() => {
paused = !!get();
if (paused !== media.paused) {
var toggle = () => {
mounted = true;
if (paused) {
media.pause();
} else {
media.play().catch(() => {
set((paused = true));
});
}
};
if (mounted) {
toggle();
// Needs to be an effect to ensure media element is mounted: else, if paused is `false` (i.e. should play right away)
// a "The play() request was interrupted by a new load request" error would be thrown because the resource isn't loaded yet.
effect(() => {
if ((paused = !!get()) !== media.paused) {
if (paused) {
media.pause();
} else {
// If this is the first invocation in dom mode, the media element isn't mounted yet,
// and therefore its resource isn't loaded yet. We need to wait for the canplay event
// in this case or else we'll get a "The play() request was interrupted by a new load request" error.
media.addEventListener(
'canplay',
() => {
listen(media, ['play', 'pause', 'canplay'], callback, false);
toggle();
},
{ once: true }
);
media.play().catch(() => {
set((paused = true));
});
}

@@ -194,0 +166,0 @@ }

@@ -410,2 +410,6 @@ /** @import { ComponentContext, ComponentContextLegacy, Derived, Effect, Reaction, TemplateNode, TransitionManager } from '#client' */

if (DEV) {
effect.component_function = null;
}
// `first` and `child` are nulled out in destroy_effect_children

@@ -412,0 +416,0 @@ effect.next =

@@ -51,1 +51,17 @@ /** @import { SvelteComponent } from '../index.js' */

}
const noop = () => {};
// event stuff, no need to worry about it for SSR but needs to be there or it will crash
export {
noop as handlers,
noop as createBubbler,
noop as once,
noop as preventDefault,
noop as self,
noop as stopImmediatePropagation,
noop as stopPropagation,
noop as trusted,
noop as passive,
noop as nonpassive
};
/** @import { Source } from '#client' */
import { DESTROYED } from '../internal/client/constants.js';
import { derived } from '../internal/client/index.js';

@@ -45,3 +46,3 @@ import { source, set } from '../internal/client/reactivity/sources.js';

if (d === undefined) {
if (d === undefined || (d.f & DESTROYED) !== 0) {
d = derived(() => {

@@ -48,0 +49,0 @@ get(this.#time);

@@ -9,3 +9,3 @@ // generated during release, do not modify

*/
export const VERSION = '5.0.0-next.262';
export const VERSION = '5.0.0-next.263';
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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc