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 3845x 3845x 3845x 3845x 3621x 3621x 3621x 3621x 9x 3621x 1x 1x 3620x 3620x 4116x 4180x 4180x 4180x 4180x 1x 1x 4179x 4180x 3x 3x 4180x 4112x 3616x 3840x 3845x 3745x 3745x 3745x 3709x 3709x 3560x 3709x 149x 149x 149x 149x 3709x 3560x 68x 68x 84x 84x 68x 3560x 3492x 3988x 4036x 4036x 4036x 3988x 3492x 3560x 3709x 3745x 3840x 3840x 69x 20x 20x 10x 20x 12x 12x 12x 12x 20x 8x 8x 8x 8x 8x 8x 20x 69x 3845x  
/** @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 });
					}
				}
			}
		}
	}
}