All files / src/compiler/phases/2-analyze/visitors Identifier.js

96.94% Statements 127/131
93.47% Branches 43/46
100% Functions 1/1
96.8% Lines 121/125

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 1262x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 46451x 46451x 46451x 46451x 9794x 9794x 36657x 36657x 36657x 36657x 36657x 46451x 3x 46451x 1x 1x 36656x 36656x 46451x 16x 16x 36656x 46451x 11394x 11394x 11394x 1838x 11394x 1786x 1786x 1786x 1786x 1786x 132x 132x 132x 132x 132x 132x 132x 2x 1x 1x 1x 2x     1x 2x     1x 1x 1x 132x 1784x 1786x 2x 2x 1786x 11394x 36652x 36652x 36652x 46318x 25262x 68x 68x 25262x 25262x 63x 63x 25262x 36652x 46451x 33473x 10384x 10384x 10384x 33473x 33473x 33473x 33473x 33473x 666x 666x 666x 60x 41x 41x 41x 666x 666x 33473x 44x 33473x 31x 33473x 26x 26x 33473x 33473x 33473x 33473x 34x 33473x 3x 3x 33473x 46451x  
/** @import { Expression, Identifier } from 'estree' */
/** @import { EachBlock } from '#compiler' */
/** @import { Context } from '../types' */
import is_reference from 'is-reference';
import { should_proxy } from '../../3-transform/client/utils.js';
import * as e from '../../../errors.js';
import * as w from '../../../warnings.js';
import { is_rune } from '../../../../utils.js';
import { mark_subtree_dynamic } from './shared/fragment.js';
 
/**
 * @param {Identifier} node
 * @param {Context} context
 */
export function Identifier(node, context) {
	let i = context.path.length;
	let parent = /** @type {Expression} */ (context.path[--i]);
 
	if (!is_reference(node, parent)) {
		return;
	}
 
	mark_subtree_dynamic(context.path);
 
	// If we are using arguments outside of a function, then throw an error
	if (
		node.name === 'arguments' &&
		!context.path.some((n) => n.type === 'FunctionDeclaration' || n.type === 'FunctionExpression')
	) {
		e.invalid_arguments_usage(node);
	}
 
	// `$$slots` exists even in runes mode
	if (node.name === '$$slots') {
		context.state.analysis.uses_slots = true;
	}
 
	if (context.state.analysis.runes) {
		if (
			is_rune(node.name) &&
			context.state.scope.get(node.name) === null &&
			context.state.scope.get(node.name.slice(1)) === null
		) {
			/** @type {Expression} */
			let current = node;
			let name = node.name;
 
			while (parent.type === 'MemberExpression') {
				if (parent.computed) e.rune_invalid_computed_property(parent);
				name += `.${/** @type {Identifier} */ (parent.property).name}`;
 
				current = parent;
				parent = /** @type {Expression} */ (context.path[--i]);
 
				if (!is_rune(name)) {
					if (name === '$effect.active') {
						e.rune_renamed(parent, '$effect.active', '$effect.tracking');
					}
 
					if (name === '$state.frozen') {
						e.rune_renamed(parent, '$state.frozen', '$state.raw');
					}
 
					if (name === '$state.is') {
						e.rune_removed(parent, '$state.is');
					}
 
					e.rune_invalid_name(parent, name);
				}
			}
 
			if (parent.type !== 'CallExpression') {
				e.rune_missing_parentheses(current);
			}
		}
	}
 
	let binding = context.state.scope.get(node.name);
 
	if (!context.state.analysis.runes) {
		if (node.name === '$$props') {
			context.state.analysis.uses_props = true;
		}
 
		if (node.name === '$$restProps') {
			context.state.analysis.uses_rest_props = true;
		}
	}
 
	if (binding) {
		if (context.state.expression) {
			context.state.expression.dependencies.add(binding);
			context.state.expression.has_state ||= binding.kind !== 'normal';
		}
 
		if (
			context.state.analysis.runes &&
			node !== binding.node &&
			context.state.function_depth === binding.scope.function_depth &&
			// If we have $state that can be proxied or frozen and isn't re-assigned, then that means
			// it's likely not using a primitive value and thus this warning isn't that helpful.
			((binding.kind === 'state' &&
				(binding.reassigned ||
					(binding.initial?.type === 'CallExpression' &&
						binding.initial.arguments.length === 1 &&
						binding.initial.arguments[0].type !== 'SpreadElement' &&
						!should_proxy(binding.initial.arguments[0], context.state.scope)))) ||
				binding.kind === 'raw_state' ||
				binding.kind === 'derived') &&
			// We're only concerned with reads here
			(parent.type !== 'AssignmentExpression' || parent.left !== node) &&
			parent.type !== 'UpdateExpression'
		) {
			w.state_referenced_locally(node);
		}
 
		if (
			context.state.reactive_statement &&
			binding.scope === context.state.analysis.module.scope &&
			binding.reassigned
		) {
			w.reactive_declaration_module_script_dependency(node);
		}
	}
}