All files / src/internal/server dev.js

97.87% Statements 92/94
82.35% Branches 14/17
100% Functions 5/5
97.87% Lines 92/94

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 981x 1x       1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 7x 7x 7x 7x 1x 1x 1x 1x 1x 1x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 1x 1x 2858x 2858x 2858x 2837x 2858x 2858x 1x 1x 1x 1x 1x 1x 1x 1x 170x 170x 170x 170x 25x 25x 25x 25x 3x 3x 25x 25x 4x 4x     4x 4x 169x 1x 1x 170x 170x 170x 1x 1x 168x 168x  
/** @import { Component, Payload } from '#server' */
import { FILENAME } from '../../constants.js';
import {
	is_tag_valid_with_ancestor,
	is_tag_valid_with_parent
} from '../../html-tree-validation.js';
import { current_component } from './context.js';
 
/**
 * @typedef {{
 * 	tag: string;
 * 	parent: null | Element;
 *  filename: null | string;
 *  line: number;
 *  column: number;
 * }} Element
 */
 
/**
 * @type {Element | null}
 */
let parent = null;
 
/** @type {Set<string>} */
let seen;
 
/**
 * @param {Element} element
 */
function stringify(element) {
	if (element.filename === null) return `\`<${element.tag}>\``;
	return `\`<${element.tag}>\` (${element.filename}:${element.line}:${element.column})`;
}
 
/**
 * @param {Payload} payload
 * @param {Element | null} parent
 * @param {Element} child
 */
function print_error(payload, parent, child) {
	var message =
		(parent === null
			? `node_invalid_placement_ssr: ${stringify(child)} needs a valid parent element\n\n`
			: `node_invalid_placement_ssr: ${stringify(parent)} cannot contain ${stringify(child)}\n\n`) +
		'This can cause content to shift around as the browser repairs the HTML, and will likely result in a `hydration_mismatch` warning.';
 
	if ((seen ??= new Set()).has(message)) return;
	seen.add(message);
 
	// eslint-disable-next-line no-console
	console.error(message);
	payload.head.out += `<script>console.error(${JSON.stringify(message)})</script>`;
}
 
export function reset_elements() {
	let old_parent = parent;
	parent = null;
	return () => {
		parent = old_parent;
	};
}
 
/**
 * @param {Payload} payload
 * @param {string} tag
 * @param {number} line
 * @param {number} column
 */
export function push_element(payload, tag, line, column) {
	var filename = /** @type {Component} */ (current_component).function[FILENAME];
	var child = { tag, parent, filename, line, column };
 
	if (parent !== null) {
		var ancestor = parent.parent;
		var ancestors = [parent.tag];
 
		if (!is_tag_valid_with_parent(tag, parent.tag)) {
			print_error(payload, parent, child);
		}
 
		while (ancestor != null) {
			ancestors.push(ancestor.tag);
			if (!is_tag_valid_with_ancestor(tag, ancestors)) {
				print_error(payload, ancestor, child);
			}
			ancestor = ancestor.parent;
		}
	} else if (!is_tag_valid_with_parent(tag, null)) {
		print_error(payload, null, child);
	}
 
	parent = child;
}
 
export function pop_element() {
	parent = /** @type {Element} */ (parent).parent;
}