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 45903x 45903x 45903x 45903x 9692x 9692x 36211x 36211x 36211x 36211x 36211x 45903x 3x 45903x 1x 1x 36210x 36210x 45903x 13x 13x 36210x 45903x 11258x 11258x 11258x 1829x 11258x 1777x 1777x 1777x 1777x 1777x 132x 132x 132x 132x 132x 132x 132x 2x 1x 1x 1x 2x     1x 2x     1x 1x 1x 132x 1775x 1777x 2x 2x 1777x 11258x 36206x 36206x 36206x 45825x 24952x 68x 68x 24952x 24952x 59x 59x 24952x 36206x 45903x 33087x 10354x 10354x 10354x 33087x 33087x 33087x 33087x 33087x 652x 652x 652x 58x 39x 39x 39x 652x 652x 33087x 44x 33087x 31x 33087x 26x 26x 33087x 33087x 33087x 33087x 34x 33087x 3x 3x 33087x 45903x  
/** @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);
		}
	}
}