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 47624x 47624x 47624x 47624x 10066x 10066x 37558x 37558x 37558x 37558x 37558x 47624x 3x 47624x 1x 1x 37557x 37557x 47624x 30x 30x 37557x 47624x 11916x 11916x 11916x 1920x 11916x 1868x 1868x 1868x 1868x 1868x 136x 136x 136x 136x 136x 136x 136x 2x 1x 1x 1x 2x     1x 2x     1x 1x 1x 136x 1866x 1868x 2x 2x 1868x 11916x 37553x 37553x 37553x 47424x 25641x 74x 74x 25641x 25641x 65x 65x 25641x 37553x 47624x 34187x 10592x 10592x 10592x 34187x 34187x 34187x 34187x 34187x 700x 700x 700x 60x 41x 41x 41x 700x 700x 34187x 44x 34187x 31x 34187x 26x 26x 34187x 34187x 34187x 34187x 34x 34187x 3x 3x 34187x 47624x  
/** @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);
		}
	}
}