|
|
|
|
@ -14,7 +14,6 @@ import {
|
|
|
|
|
} from '../utils/ast.js';
|
|
|
|
|
import { is_reserved, is_rune } from '../../utils.js';
|
|
|
|
|
import { determine_slot } from '../utils/slot.js';
|
|
|
|
|
import { validate_identifier_name } from './2-analyze/visitors/shared/utils.js';
|
|
|
|
|
|
|
|
|
|
const UNKNOWN = Symbol('unknown');
|
|
|
|
|
/** Includes `BigInt` */
|
|
|
|
|
@ -1233,6 +1232,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
|
|
|
|
|
|
|
|
|
|
EachBlock(node, { state, visit }) {
|
|
|
|
|
visit(node.expression);
|
|
|
|
|
if (node.fallback) visit(node.fallback);
|
|
|
|
|
|
|
|
|
|
// context and children are a new scope
|
|
|
|
|
const scope = state.scope.child();
|
|
|
|
|
@ -1278,7 +1278,6 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
|
|
|
|
|
for (const child of node.body.nodes) {
|
|
|
|
|
visit(child, { scope });
|
|
|
|
|
}
|
|
|
|
|
if (node.fallback) visit(node.fallback, { scope });
|
|
|
|
|
|
|
|
|
|
node.metadata = {
|
|
|
|
|
expression: new ExpressionMetadata(),
|
|
|
|
|
@ -1469,3 +1468,79 @@ function get_global_keypath(node, scope) {
|
|
|
|
|
|
|
|
|
|
return n.name + joined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Expression} node
|
|
|
|
|
* @param {Scope | null} scope
|
|
|
|
|
*/
|
|
|
|
|
export function should_proxy(node, scope) {
|
|
|
|
|
if (
|
|
|
|
|
!node ||
|
|
|
|
|
node.type === 'Literal' ||
|
|
|
|
|
node.type === 'TemplateLiteral' ||
|
|
|
|
|
node.type === 'ArrowFunctionExpression' ||
|
|
|
|
|
node.type === 'FunctionExpression' ||
|
|
|
|
|
node.type === 'UnaryExpression' ||
|
|
|
|
|
node.type === 'BinaryExpression' ||
|
|
|
|
|
(node.type === 'Identifier' && node.name === 'undefined')
|
|
|
|
|
) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (node.type === 'Identifier' && scope !== null) {
|
|
|
|
|
const binding = scope.get(node.name);
|
|
|
|
|
// Let's see if the reference is something that can be proxied
|
|
|
|
|
if (
|
|
|
|
|
binding !== null &&
|
|
|
|
|
!binding.reassigned &&
|
|
|
|
|
binding.initial !== null &&
|
|
|
|
|
binding.initial.type !== 'FunctionDeclaration' &&
|
|
|
|
|
binding.initial.type !== 'ClassDeclaration' &&
|
|
|
|
|
binding.initial.type !== 'ImportDeclaration' &&
|
|
|
|
|
binding.initial.type !== 'EachBlock' &&
|
|
|
|
|
binding.initial.type !== 'SnippetBlock'
|
|
|
|
|
) {
|
|
|
|
|
return should_proxy(binding.initial, null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks if the name is valid, which it is when it's not starting with (or is) a dollar sign or if it's a function parameter.
|
|
|
|
|
* The second argument is the depth of the scope, which is there for backwards compatibility reasons: In Svelte 4, you
|
|
|
|
|
* were allowed to define `$`-prefixed variables anywhere below the top level of components. Once legacy mode is gone, this
|
|
|
|
|
* argument can be removed / the call sites adjusted accordingly.
|
|
|
|
|
* @param {Binding | null} binding
|
|
|
|
|
* @param {number | undefined} [function_depth]
|
|
|
|
|
*/
|
|
|
|
|
export function validate_identifier_name(binding, function_depth) {
|
|
|
|
|
if (!binding) return;
|
|
|
|
|
|
|
|
|
|
const declaration_kind = binding.declaration_kind;
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
declaration_kind !== 'synthetic' &&
|
|
|
|
|
declaration_kind !== 'param' &&
|
|
|
|
|
declaration_kind !== 'rest_param' &&
|
|
|
|
|
(!function_depth || function_depth <= 1)
|
|
|
|
|
) {
|
|
|
|
|
const node = binding.node;
|
|
|
|
|
|
|
|
|
|
if (node.name === '$') {
|
|
|
|
|
e.dollar_binding_invalid(node);
|
|
|
|
|
} else if (
|
|
|
|
|
node.name.startsWith('$') &&
|
|
|
|
|
// import type { $Type } from "" - these are normally already filtered out,
|
|
|
|
|
// but for the migration they aren't, and throwing here is preventing the migration to complete
|
|
|
|
|
// TODO -> once migration script is gone we can remove this check
|
|
|
|
|
!(
|
|
|
|
|
binding.initial?.type === 'ImportDeclaration' &&
|
|
|
|
|
/** @type {any} */ (binding.initial).importKind === 'type'
|
|
|
|
|
)
|
|
|
|
|
) {
|
|
|
|
|
e.dollar_prefix_invalid(node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|