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

100% Statements 94/94
97.22% Branches 35/36
100% Functions 1/1
100% Lines 92/92

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 932x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 3849x 3849x 3849x 3849x 3623x 3623x 3623x 3623x 9x 3623x 1x 1x 3622x 3622x 4118x 4182x 4182x 4182x 4182x 1x 1x 4181x 4182x 3x 3x 4182x 4114x 3618x 3844x 3849x 3747x 3747x 3747x 3711x 3711x 3562x 3711x 149x 149x 149x 149x 3711x 3562x 68x 68x 84x 84x 68x 3562x 3494x 3990x 4038x 4038x 4038x 3990x 3494x 3562x 3711x 3747x 3844x 3844x 71x 20x 20x 10x 20x 12x 12x 12x 12x 20x 8x 8x 8x 8x 8x 8x 20x 71x 3849x  
/** @import { ExportNamedDeclaration, Identifier, Node } from 'estree' */
/** @import { Binding } from '#compiler' */
/** @import { Context } from '../types' */
/** @import { Scope } from '../../scope' */
import * as e from '../../../errors.js';
import { extract_identifiers } from '../../../utils/ast.js';
 
/**
 * @param {ExportNamedDeclaration} node
 * @param {Context} context
 */
export function ExportNamedDeclaration(node, context) {
	// visit children, so bindings are correctly initialised
	context.next();
 
	if (node.declaration?.type === 'VariableDeclaration') {
		// in runes mode, forbid `export let`
		if (
			context.state.analysis.runes &&
			context.state.ast_type === 'instance' &&
			node.declaration.kind === 'let'
		) {
			e.legacy_export_invalid(node);
		}
 
		for (const declarator of node.declaration.declarations) {
			for (const id of extract_identifiers(declarator.id)) {
				const binding = context.state.scope.get(id.name);
				if (!binding) continue;
 
				if (binding.kind === 'derived') {
					e.derived_invalid_export(node);
				}
 
				if ((binding.kind === 'state' || binding.kind === 'frozen_state') && binding.reassigned) {
					e.state_invalid_export(node);
				}
			}
		}
	}
 
	if (context.state.ast_type === 'instance' && !context.state.analysis.runes) {
		context.state.analysis.needs_props = true;
 
		if (node.declaration) {
			if (
				node.declaration.type === 'FunctionDeclaration' ||
				node.declaration.type === 'ClassDeclaration'
			) {
				context.state.analysis.exports.push({
					name: /** @type {Identifier} */ (node.declaration.id).name,
					alias: null
				});
			} else if (node.declaration.type === 'VariableDeclaration') {
				if (node.declaration.kind === 'const') {
					for (const declarator of node.declaration.declarations) {
						for (const node of extract_identifiers(declarator.id)) {
							context.state.analysis.exports.push({ name: node.name, alias: null });
						}
					}
				} else {
					for (const declarator of node.declaration.declarations) {
						for (const id of extract_identifiers(declarator.id)) {
							const binding = /** @type {Binding} */ (context.state.scope.get(id.name));
							binding.kind = 'bindable_prop';
						}
					}
				}
			}
		}
	}
 
	if (context.state.analysis.runes) {
		if (node.declaration && context.state.ast_type === 'instance') {
			if (
				node.declaration.type === 'FunctionDeclaration' ||
				node.declaration.type === 'ClassDeclaration'
			) {
				context.state.analysis.exports.push({
					name: /** @type {Identifier} */ (node.declaration.id).name,
					alias: null
				});
			} else if (node.declaration.kind === 'const') {
				for (const declarator of node.declaration.declarations) {
					for (const node of extract_identifiers(declarator.id)) {
						context.state.analysis.exports.push({ name: node.name, alias: null });
					}
				}
			}
		}
	}
}