renderer.js 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244
  1. import { l as lifecycle_outside_component, i as invalid_csp, a as await_invalid, g as get_render_context } from "./render-context.js";
  2. import { clsx as clsx$1 } from "clsx";
  3. import * as devalue from "devalue";
  4. var ssr_context = null;
  5. function set_ssr_context(v) {
  6. ssr_context = v;
  7. }
  8. function getContext(key) {
  9. const context_map = get_or_init_context_map();
  10. const result = (
  11. /** @type {T} */
  12. context_map.get(key)
  13. );
  14. return result;
  15. }
  16. function setContext(key, context) {
  17. get_or_init_context_map().set(key, context);
  18. return context;
  19. }
  20. function get_or_init_context_map(name) {
  21. if (ssr_context === null) {
  22. lifecycle_outside_component();
  23. }
  24. return ssr_context.c ??= new Map(get_parent_context(ssr_context) || void 0);
  25. }
  26. function push(fn) {
  27. ssr_context = { p: ssr_context, c: null, r: null };
  28. }
  29. function pop() {
  30. ssr_context = /** @type {SSRContext} */
  31. ssr_context.p;
  32. }
  33. function get_parent_context(ssr_context2) {
  34. let parent = ssr_context2.p;
  35. while (parent !== null) {
  36. const context_map = parent.c;
  37. if (context_map !== null) {
  38. return context_map;
  39. }
  40. parent = parent.p;
  41. }
  42. return null;
  43. }
  44. var is_array = Array.isArray;
  45. var index_of = Array.prototype.indexOf;
  46. var includes = Array.prototype.includes;
  47. var array_from = Array.from;
  48. var define_property = Object.defineProperty;
  49. var get_descriptor = Object.getOwnPropertyDescriptor;
  50. var object_prototype = Object.prototype;
  51. var array_prototype = Array.prototype;
  52. var get_prototype_of = Object.getPrototypeOf;
  53. var is_extensible = Object.isExtensible;
  54. var has_own_property = Object.prototype.hasOwnProperty;
  55. const noop = () => {
  56. };
  57. function run_all(arr) {
  58. for (var i = 0; i < arr.length; i++) {
  59. arr[i]();
  60. }
  61. }
  62. function deferred() {
  63. var resolve;
  64. var reject;
  65. var promise = new Promise((res, rej) => {
  66. resolve = res;
  67. reject = rej;
  68. });
  69. return { promise, resolve, reject };
  70. }
  71. const DERIVED = 1 << 1;
  72. const EFFECT = 1 << 2;
  73. const RENDER_EFFECT = 1 << 3;
  74. const MANAGED_EFFECT = 1 << 24;
  75. const BLOCK_EFFECT = 1 << 4;
  76. const BRANCH_EFFECT = 1 << 5;
  77. const ROOT_EFFECT = 1 << 6;
  78. const BOUNDARY_EFFECT = 1 << 7;
  79. const CONNECTED = 1 << 9;
  80. const CLEAN = 1 << 10;
  81. const DIRTY = 1 << 11;
  82. const MAYBE_DIRTY = 1 << 12;
  83. const INERT = 1 << 13;
  84. const DESTROYED = 1 << 14;
  85. const REACTION_RAN = 1 << 15;
  86. const DESTROYING = 1 << 25;
  87. const EFFECT_TRANSPARENT = 1 << 16;
  88. const EAGER_EFFECT = 1 << 17;
  89. const HEAD_EFFECT = 1 << 18;
  90. const EFFECT_PRESERVED = 1 << 19;
  91. const USER_EFFECT = 1 << 20;
  92. const WAS_MARKED = 1 << 16;
  93. const REACTION_IS_UPDATING = 1 << 21;
  94. const ASYNC = 1 << 22;
  95. const ERROR_VALUE = 1 << 23;
  96. const STATE_SYMBOL = /* @__PURE__ */ Symbol("$state");
  97. const LEGACY_PROPS = /* @__PURE__ */ Symbol("legacy props");
  98. const STALE_REACTION = new class StaleReactionError extends Error {
  99. name = "StaleReactionError";
  100. message = "The reaction that called `getAbortSignal()` was re-run or destroyed";
  101. }();
  102. const COMMENT_NODE = 8;
  103. let controller = null;
  104. function abort() {
  105. controller?.abort(STALE_REACTION);
  106. controller = null;
  107. }
  108. const HYDRATION_START = "[";
  109. const HYDRATION_START_ELSE = "[!";
  110. const HYDRATION_START_FAILED = "[?";
  111. const HYDRATION_END = "]";
  112. const HYDRATION_ERROR = {};
  113. const ELEMENT_IS_NAMESPACED = 1;
  114. const ELEMENT_PRESERVE_ATTRIBUTE_CASE = 1 << 1;
  115. const ELEMENT_IS_INPUT = 1 << 2;
  116. const UNINITIALIZED = /* @__PURE__ */ Symbol();
  117. function unresolved_hydratable(key, stack) {
  118. {
  119. console.warn(`https://svelte.dev/e/unresolved_hydratable`);
  120. }
  121. }
  122. const BLOCK_OPEN = `<!--${HYDRATION_START}-->`;
  123. const BLOCK_CLOSE = `<!--${HYDRATION_END}-->`;
  124. const EMPTY_COMMENT = `<!---->`;
  125. const ATTR_REGEX = /[&"<]/g;
  126. const CONTENT_REGEX = /[&<]/g;
  127. function escape_html(value, is_attr) {
  128. const str = String(value ?? "");
  129. const pattern = is_attr ? ATTR_REGEX : CONTENT_REGEX;
  130. pattern.lastIndex = 0;
  131. let escaped = "";
  132. let last = 0;
  133. while (pattern.test(str)) {
  134. const i = pattern.lastIndex - 1;
  135. const ch = str[i];
  136. escaped += str.substring(last, i) + (ch === "&" ? "&amp;" : ch === '"' ? "&quot;" : "&lt;");
  137. last = i + 1;
  138. }
  139. return escaped + str.substring(last);
  140. }
  141. const replacements = {
  142. translate: /* @__PURE__ */ new Map([
  143. [true, "yes"],
  144. [false, "no"]
  145. ])
  146. };
  147. function attr(name, value, is_boolean = false) {
  148. if (name === "hidden" && value !== "until-found") {
  149. is_boolean = true;
  150. }
  151. if (value == null || !value && is_boolean) return "";
  152. const normalized = has_own_property.call(replacements, name) && replacements[name].get(value) || value;
  153. const assignment = is_boolean ? `=""` : `="${escape_html(normalized, true)}"`;
  154. return ` ${name}${assignment}`;
  155. }
  156. function clsx(value) {
  157. if (typeof value === "object") {
  158. return clsx$1(value);
  159. } else {
  160. return value ?? "";
  161. }
  162. }
  163. const whitespace = [..." \n\r\f \v\uFEFF"];
  164. function to_class(value, hash, directives) {
  165. var classname = value == null ? "" : "" + value;
  166. if (hash) {
  167. classname = classname ? classname + " " + hash : hash;
  168. }
  169. if (directives) {
  170. for (var key of Object.keys(directives)) {
  171. if (directives[key]) {
  172. classname = classname ? classname + " " + key : key;
  173. } else if (classname.length) {
  174. var len = key.length;
  175. var a = 0;
  176. while ((a = classname.indexOf(key, a)) >= 0) {
  177. var b = a + len;
  178. if ((a === 0 || whitespace.includes(classname[a - 1])) && (b === classname.length || whitespace.includes(classname[b]))) {
  179. classname = (a === 0 ? "" : classname.substring(0, a)) + classname.substring(b + 1);
  180. } else {
  181. a = b;
  182. }
  183. }
  184. }
  185. }
  186. }
  187. return classname === "" ? null : classname;
  188. }
  189. function append_styles(styles, important = false) {
  190. var separator = important ? " !important;" : ";";
  191. var css = "";
  192. for (var key of Object.keys(styles)) {
  193. var value = styles[key];
  194. if (value != null && value !== "") {
  195. css += " " + key + ": " + value + separator;
  196. }
  197. }
  198. return css;
  199. }
  200. function to_css_name(name) {
  201. if (name[0] !== "-" || name[1] !== "-") {
  202. return name.toLowerCase();
  203. }
  204. return name;
  205. }
  206. function to_style(value, styles) {
  207. if (styles) {
  208. var new_style = "";
  209. var normal_styles;
  210. var important_styles;
  211. if (Array.isArray(styles)) {
  212. normal_styles = styles[0];
  213. important_styles = styles[1];
  214. } else {
  215. normal_styles = styles;
  216. }
  217. if (value) {
  218. value = String(value).replaceAll(/\s*\/\*.*?\*\/\s*/g, "").trim();
  219. var in_str = false;
  220. var in_apo = 0;
  221. var in_comment = false;
  222. var reserved_names = [];
  223. if (normal_styles) {
  224. reserved_names.push(...Object.keys(normal_styles).map(to_css_name));
  225. }
  226. if (important_styles) {
  227. reserved_names.push(...Object.keys(important_styles).map(to_css_name));
  228. }
  229. var start_index = 0;
  230. var name_index = -1;
  231. const len = value.length;
  232. for (var i = 0; i < len; i++) {
  233. var c = value[i];
  234. if (in_comment) {
  235. if (c === "/" && value[i - 1] === "*") {
  236. in_comment = false;
  237. }
  238. } else if (in_str) {
  239. if (in_str === c) {
  240. in_str = false;
  241. }
  242. } else if (c === "/" && value[i + 1] === "*") {
  243. in_comment = true;
  244. } else if (c === '"' || c === "'") {
  245. in_str = c;
  246. } else if (c === "(") {
  247. in_apo++;
  248. } else if (c === ")") {
  249. in_apo--;
  250. }
  251. if (!in_comment && in_str === false && in_apo === 0) {
  252. if (c === ":" && name_index === -1) {
  253. name_index = i;
  254. } else if (c === ";" || i === len - 1) {
  255. if (name_index !== -1) {
  256. var name = to_css_name(value.substring(start_index, name_index).trim());
  257. if (!reserved_names.includes(name)) {
  258. if (c !== ";") {
  259. i++;
  260. }
  261. var property = value.substring(start_index, i).trim();
  262. new_style += " " + property + ";";
  263. }
  264. }
  265. start_index = i + 1;
  266. name_index = -1;
  267. }
  268. }
  269. }
  270. }
  271. if (normal_styles) {
  272. new_style += append_styles(normal_styles);
  273. }
  274. if (important_styles) {
  275. new_style += append_styles(important_styles, true);
  276. }
  277. new_style = new_style.trim();
  278. return new_style === "" ? null : new_style;
  279. }
  280. return value == null ? null : String(value);
  281. }
  282. const DOM_BOOLEAN_ATTRIBUTES = [
  283. "allowfullscreen",
  284. "async",
  285. "autofocus",
  286. "autoplay",
  287. "checked",
  288. "controls",
  289. "default",
  290. "disabled",
  291. "formnovalidate",
  292. "indeterminate",
  293. "inert",
  294. "ismap",
  295. "loop",
  296. "multiple",
  297. "muted",
  298. "nomodule",
  299. "novalidate",
  300. "open",
  301. "playsinline",
  302. "readonly",
  303. "required",
  304. "reversed",
  305. "seamless",
  306. "selected",
  307. "webkitdirectory",
  308. "defer",
  309. "disablepictureinpicture",
  310. "disableremoteplayback"
  311. ];
  312. function is_boolean_attribute(name) {
  313. return DOM_BOOLEAN_ATTRIBUTES.includes(name);
  314. }
  315. const PASSIVE_EVENTS = ["touchstart", "touchmove"];
  316. function is_passive_event(name) {
  317. return PASSIVE_EVENTS.includes(name);
  318. }
  319. const INVALID_ATTR_NAME_CHAR_REGEX = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u;
  320. function render(component, options = {}) {
  321. if (options.csp?.hash && options.csp.nonce) {
  322. invalid_csp();
  323. }
  324. return Renderer.render(
  325. /** @type {Component<Props>} */
  326. component,
  327. options
  328. );
  329. }
  330. function head(hash, renderer, fn) {
  331. renderer.head((renderer2) => {
  332. renderer2.push(`<!--${hash}-->`);
  333. renderer2.child(fn);
  334. renderer2.push(EMPTY_COMMENT);
  335. });
  336. }
  337. function attributes(attrs, css_hash, classes, styles, flags = 0) {
  338. if (styles) {
  339. attrs.style = to_style(attrs.style, styles);
  340. }
  341. if (attrs.class) {
  342. attrs.class = clsx(attrs.class);
  343. }
  344. if (css_hash || classes) {
  345. attrs.class = to_class(attrs.class, css_hash, classes);
  346. }
  347. let attr_str = "";
  348. let name;
  349. const is_html = (flags & ELEMENT_IS_NAMESPACED) === 0;
  350. const lowercase = (flags & ELEMENT_PRESERVE_ATTRIBUTE_CASE) === 0;
  351. const is_input = (flags & ELEMENT_IS_INPUT) !== 0;
  352. for (name of Object.keys(attrs)) {
  353. if (typeof attrs[name] === "function") continue;
  354. if (name[0] === "$" && name[1] === "$") continue;
  355. if (INVALID_ATTR_NAME_CHAR_REGEX.test(name)) continue;
  356. var value = attrs[name];
  357. var lower = name.toLowerCase();
  358. if (lowercase) name = lower;
  359. if (lower.length > 2 && lower.startsWith("on")) continue;
  360. if (is_input) {
  361. if (name === "defaultvalue" || name === "defaultchecked") {
  362. name = name === "defaultvalue" ? "value" : "checked";
  363. if (attrs[name]) continue;
  364. }
  365. }
  366. attr_str += attr(name, value, is_html && is_boolean_attribute(name));
  367. }
  368. return attr_str;
  369. }
  370. function stringify(value) {
  371. return typeof value === "string" ? value : value == null ? "" : value + "";
  372. }
  373. function attr_class(value, hash, directives) {
  374. var result = to_class(value, hash, directives);
  375. return result ? ` class="${escape_html(result, true)}"` : "";
  376. }
  377. function attr_style(value, directives) {
  378. var result = to_style(value, directives);
  379. return result ? ` style="${escape_html(result, true)}"` : "";
  380. }
  381. function slot(renderer, $$props, name, slot_props, fallback_fn) {
  382. var slot_fn = $$props.$$slots?.[name];
  383. if (slot_fn === true) {
  384. slot_fn = $$props["children"];
  385. }
  386. if (slot_fn !== void 0) {
  387. slot_fn(renderer, slot_props);
  388. }
  389. }
  390. function ensure_array_like(array_like_or_iterator) {
  391. if (array_like_or_iterator) {
  392. return array_like_or_iterator.length !== void 0 ? array_like_or_iterator : Array.from(array_like_or_iterator);
  393. }
  394. return [];
  395. }
  396. function once(get_value) {
  397. let value = (
  398. /** @type {V} */
  399. UNINITIALIZED
  400. );
  401. return () => {
  402. if (value === UNINITIALIZED) {
  403. value = get_value();
  404. }
  405. return value;
  406. };
  407. }
  408. function derived(fn) {
  409. const get_value = ssr_context === null ? fn : once(fn);
  410. let updated_value;
  411. return function(new_value) {
  412. if (arguments.length === 0) {
  413. return updated_value ?? get_value();
  414. }
  415. updated_value = new_value;
  416. return updated_value;
  417. };
  418. }
  419. let text_encoder;
  420. let crypto;
  421. const obfuscated_import = (module_name) => import(
  422. /* @vite-ignore */
  423. module_name
  424. );
  425. async function sha256(data) {
  426. text_encoder ??= new TextEncoder();
  427. crypto ??= globalThis.crypto?.subtle?.digest ? globalThis.crypto : (
  428. // @ts-ignore - we don't install node types in the prod build
  429. // don't use import('node:crypto') directly because static analysers will think we rely on node when we don't
  430. (await obfuscated_import("node:crypto")).webcrypto
  431. );
  432. const hash_buffer = await crypto.subtle.digest("SHA-256", text_encoder.encode(data));
  433. return base64_encode(hash_buffer);
  434. }
  435. function base64_encode(bytes) {
  436. if (globalThis.Buffer) {
  437. return globalThis.Buffer.from(bytes).toString("base64");
  438. }
  439. let binary = "";
  440. for (let i = 0; i < bytes.length; i++) {
  441. binary += String.fromCharCode(bytes[i]);
  442. }
  443. return btoa(binary);
  444. }
  445. class Renderer {
  446. /**
  447. * The contents of the renderer.
  448. * @type {RendererItem[]}
  449. */
  450. #out = [];
  451. /**
  452. * Any `onDestroy` callbacks registered during execution of this renderer.
  453. * @type {(() => void)[] | undefined}
  454. */
  455. #on_destroy = void 0;
  456. /**
  457. * Whether this renderer is a component body.
  458. * @type {boolean}
  459. */
  460. #is_component_body = false;
  461. /**
  462. * If set, this renderer is an error boundary. When async collection
  463. * of the children fails, the failed snippet is rendered instead.
  464. * @type {{
  465. * failed: (renderer: Renderer, error: unknown, reset: () => void) => void;
  466. * transformError: (error: unknown) => unknown;
  467. * context: SSRContext | null;
  468. * } | null}
  469. */
  470. #boundary = null;
  471. /**
  472. * The type of string content that this renderer is accumulating.
  473. * @type {RendererType}
  474. */
  475. type;
  476. /** @type {Renderer | undefined} */
  477. #parent;
  478. /**
  479. * Asynchronous work associated with this renderer
  480. * @type {Promise<void> | undefined}
  481. */
  482. promise = void 0;
  483. /**
  484. * State which is associated with the content tree as a whole.
  485. * It will be re-exposed, uncopied, on all children.
  486. * @type {SSRState}
  487. * @readonly
  488. */
  489. global;
  490. /**
  491. * State that is local to the branch it is declared in.
  492. * It will be shallow-copied to all children.
  493. *
  494. * @type {{ select_value: string | undefined }}
  495. */
  496. local;
  497. /**
  498. * @param {SSRState} global
  499. * @param {Renderer | undefined} [parent]
  500. */
  501. constructor(global, parent) {
  502. this.#parent = parent;
  503. this.global = global;
  504. this.local = parent ? { ...parent.local } : { select_value: void 0 };
  505. this.type = parent ? parent.type : "body";
  506. }
  507. /**
  508. * @param {(renderer: Renderer) => void} fn
  509. */
  510. head(fn) {
  511. const head2 = new Renderer(this.global, this);
  512. head2.type = "head";
  513. this.#out.push(head2);
  514. head2.child(fn);
  515. }
  516. /**
  517. * @param {Array<Promise<void>>} blockers
  518. * @param {(renderer: Renderer) => void} fn
  519. */
  520. async_block(blockers, fn) {
  521. this.#out.push(BLOCK_OPEN);
  522. this.async(blockers, fn);
  523. this.#out.push(BLOCK_CLOSE);
  524. }
  525. /**
  526. * @param {Array<Promise<void>>} blockers
  527. * @param {(renderer: Renderer) => void} fn
  528. */
  529. async(blockers, fn) {
  530. let callback = fn;
  531. if (blockers.length > 0) {
  532. const context = ssr_context;
  533. callback = (renderer) => {
  534. return Promise.all(blockers).then(() => {
  535. const previous_context = ssr_context;
  536. try {
  537. set_ssr_context(context);
  538. return fn(renderer);
  539. } finally {
  540. set_ssr_context(previous_context);
  541. }
  542. });
  543. };
  544. }
  545. this.child(callback);
  546. }
  547. /**
  548. * @param {Array<() => void>} thunks
  549. */
  550. run(thunks) {
  551. const context = ssr_context;
  552. let promise = Promise.resolve(thunks[0]());
  553. const promises = [promise];
  554. for (const fn of thunks.slice(1)) {
  555. promise = promise.then(() => {
  556. const previous_context = ssr_context;
  557. set_ssr_context(context);
  558. try {
  559. return fn();
  560. } finally {
  561. set_ssr_context(previous_context);
  562. }
  563. });
  564. promises.push(promise);
  565. }
  566. promise.catch(noop);
  567. this.promise = promise;
  568. return promises;
  569. }
  570. /**
  571. * @param {(renderer: Renderer) => MaybePromise<void>} fn
  572. */
  573. child_block(fn) {
  574. this.#out.push(BLOCK_OPEN);
  575. this.child(fn);
  576. this.#out.push(BLOCK_CLOSE);
  577. }
  578. /**
  579. * Create a child renderer. The child renderer inherits the state from the parent,
  580. * but has its own content.
  581. * @param {(renderer: Renderer) => MaybePromise<void>} fn
  582. */
  583. child(fn) {
  584. const child = new Renderer(this.global, this);
  585. this.#out.push(child);
  586. const parent = ssr_context;
  587. set_ssr_context({
  588. ...ssr_context,
  589. p: parent,
  590. c: null,
  591. r: child
  592. });
  593. const result = fn(child);
  594. set_ssr_context(parent);
  595. if (result instanceof Promise) {
  596. result.catch(noop);
  597. result.finally(() => set_ssr_context(null)).catch(noop);
  598. if (child.global.mode === "sync") {
  599. await_invalid();
  600. }
  601. child.promise = result;
  602. }
  603. return child;
  604. }
  605. /**
  606. * Render children inside an error boundary. If the children throw and the API-level
  607. * `transformError` transform handles the error (doesn't re-throw), the `failed` snippet is
  608. * rendered instead. Otherwise the error propagates.
  609. *
  610. * @param {{ failed?: (renderer: Renderer, error: unknown, reset: () => void) => void }} props
  611. * @param {(renderer: Renderer) => MaybePromise<void>} children_fn
  612. */
  613. boundary(props, children_fn) {
  614. const child = new Renderer(this.global, this);
  615. this.#out.push(child);
  616. const parent_context = ssr_context;
  617. if (props.failed) {
  618. child.#boundary = {
  619. failed: props.failed,
  620. transformError: this.global.transformError,
  621. context: parent_context
  622. };
  623. }
  624. set_ssr_context({
  625. ...ssr_context,
  626. p: parent_context,
  627. c: null,
  628. r: child
  629. });
  630. try {
  631. const result = children_fn(child);
  632. set_ssr_context(parent_context);
  633. if (result instanceof Promise) {
  634. if (child.global.mode === "sync") {
  635. await_invalid();
  636. }
  637. result.catch(noop);
  638. child.promise = result;
  639. }
  640. } catch (error) {
  641. set_ssr_context(parent_context);
  642. const failed_snippet = props.failed;
  643. if (!failed_snippet) throw error;
  644. const result = this.global.transformError(error);
  645. child.#out.length = 0;
  646. child.#boundary = null;
  647. if (result instanceof Promise) {
  648. if (this.global.mode === "sync") {
  649. await_invalid();
  650. }
  651. child.promise = /** @type {Promise<unknown>} */
  652. result.then((transformed) => {
  653. set_ssr_context(parent_context);
  654. child.#out.push(Renderer.#serialize_failed_boundary(transformed));
  655. failed_snippet(child, transformed, noop);
  656. child.#out.push(BLOCK_CLOSE);
  657. });
  658. child.promise.catch(noop);
  659. } else {
  660. child.#out.push(Renderer.#serialize_failed_boundary(result));
  661. failed_snippet(child, result, noop);
  662. child.#out.push(BLOCK_CLOSE);
  663. }
  664. }
  665. }
  666. /**
  667. * Create a component renderer. The component renderer inherits the state from the parent,
  668. * but has its own content. It is treated as an ordering boundary for ondestroy callbacks.
  669. * @param {(renderer: Renderer) => MaybePromise<void>} fn
  670. * @param {Function} [component_fn]
  671. * @returns {void}
  672. */
  673. component(fn, component_fn) {
  674. push();
  675. const child = this.child(fn);
  676. child.#is_component_body = true;
  677. pop();
  678. }
  679. /**
  680. * @param {Record<string, any>} attrs
  681. * @param {(renderer: Renderer) => void} fn
  682. * @param {string | undefined} [css_hash]
  683. * @param {Record<string, boolean> | undefined} [classes]
  684. * @param {Record<string, string> | undefined} [styles]
  685. * @param {number | undefined} [flags]
  686. * @param {boolean | undefined} [is_rich]
  687. * @returns {void}
  688. */
  689. select(attrs, fn, css_hash, classes, styles, flags, is_rich) {
  690. const { value, ...select_attrs } = attrs;
  691. this.push(`<select${attributes(select_attrs, css_hash, classes, styles, flags)}>`);
  692. this.child((renderer) => {
  693. renderer.local.select_value = value;
  694. fn(renderer);
  695. });
  696. this.push(`${is_rich ? "<!>" : ""}</select>`);
  697. }
  698. /**
  699. * @param {Record<string, any>} attrs
  700. * @param {string | number | boolean | ((renderer: Renderer) => void)} body
  701. * @param {string | undefined} [css_hash]
  702. * @param {Record<string, boolean> | undefined} [classes]
  703. * @param {Record<string, string> | undefined} [styles]
  704. * @param {number | undefined} [flags]
  705. * @param {boolean | undefined} [is_rich]
  706. */
  707. option(attrs, body, css_hash, classes, styles, flags, is_rich) {
  708. this.#out.push(`<option${attributes(attrs, css_hash, classes, styles, flags)}`);
  709. const close = (renderer, value, { head: head2, body: body2 }) => {
  710. if (has_own_property.call(attrs, "value")) {
  711. value = attrs.value;
  712. }
  713. if (value === this.local.select_value) {
  714. renderer.#out.push(' selected=""');
  715. }
  716. renderer.#out.push(`>${body2}${is_rich ? "<!>" : ""}</option>`);
  717. if (head2) {
  718. renderer.head((child) => child.push(head2));
  719. }
  720. };
  721. if (typeof body === "function") {
  722. this.child((renderer) => {
  723. const r = new Renderer(this.global, this);
  724. body(r);
  725. if (this.global.mode === "async") {
  726. return r.#collect_content_async().then((content) => {
  727. close(renderer, content.body.replaceAll("<!---->", ""), content);
  728. });
  729. } else {
  730. const content = r.#collect_content();
  731. close(renderer, content.body.replaceAll("<!---->", ""), content);
  732. }
  733. });
  734. } else {
  735. close(this, body, { body: escape_html(body) });
  736. }
  737. }
  738. /**
  739. * @param {(renderer: Renderer) => void} fn
  740. */
  741. title(fn) {
  742. const path = this.get_path();
  743. const close = (head2) => {
  744. this.global.set_title(head2, path);
  745. };
  746. this.child((renderer) => {
  747. const r = new Renderer(renderer.global, renderer);
  748. fn(r);
  749. if (renderer.global.mode === "async") {
  750. return r.#collect_content_async().then((content) => {
  751. close(content.head);
  752. });
  753. } else {
  754. const content = r.#collect_content();
  755. close(content.head);
  756. }
  757. });
  758. }
  759. /**
  760. * @param {string | (() => Promise<string>)} content
  761. */
  762. push(content) {
  763. if (typeof content === "function") {
  764. this.child(async (renderer) => renderer.push(await content()));
  765. } else {
  766. this.#out.push(content);
  767. }
  768. }
  769. /**
  770. * @param {() => void} fn
  771. */
  772. on_destroy(fn) {
  773. (this.#on_destroy ??= []).push(fn);
  774. }
  775. /**
  776. * @returns {number[]}
  777. */
  778. get_path() {
  779. return this.#parent ? [...this.#parent.get_path(), this.#parent.#out.indexOf(this)] : [];
  780. }
  781. /**
  782. * @deprecated this is needed for legacy component bindings
  783. */
  784. copy() {
  785. const copy = new Renderer(this.global, this.#parent);
  786. copy.#out = this.#out.map((item) => item instanceof Renderer ? item.copy() : item);
  787. copy.promise = this.promise;
  788. return copy;
  789. }
  790. /**
  791. * @param {Renderer} other
  792. * @deprecated this is needed for legacy component bindings
  793. */
  794. subsume(other) {
  795. if (this.global.mode !== other.global.mode) {
  796. throw new Error(
  797. "invariant: A renderer cannot switch modes. If you're seeing this, there's a compiler bug. File an issue!"
  798. );
  799. }
  800. this.local = other.local;
  801. this.#out = other.#out.map((item, i) => {
  802. const current = this.#out[i];
  803. if (current instanceof Renderer && item instanceof Renderer) {
  804. current.subsume(item);
  805. return current;
  806. }
  807. return item;
  808. });
  809. this.promise = other.promise;
  810. this.type = other.type;
  811. }
  812. get length() {
  813. return this.#out.length;
  814. }
  815. /**
  816. * Creates the hydration comment that marks the start of a failed boundary.
  817. * The error is JSON-serialized and embedded inside an HTML comment for the client
  818. * to parse during hydration. The JSON is escaped to prevent `-->` or `<!--` sequences
  819. * from breaking out of the comment (XSS). Uses unicode escapes which `JSON.parse()`
  820. * handles transparently.
  821. * @param {unknown} error
  822. * @returns {string}
  823. */
  824. static #serialize_failed_boundary(error) {
  825. var json = JSON.stringify(error);
  826. var escaped = json.replace(/>/g, "\\u003e").replace(/</g, "\\u003c");
  827. return `<!--${HYDRATION_START_FAILED}${escaped}-->`;
  828. }
  829. /**
  830. * Only available on the server and when compiling with the `server` option.
  831. * Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app.
  832. * @template {Record<string, any>} Props
  833. * @param {Component<Props>} component
  834. * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: Csp }} [options]
  835. * @returns {RenderOutput}
  836. */
  837. static render(component, options = {}) {
  838. let sync;
  839. const result = (
  840. /** @type {RenderOutput} */
  841. {}
  842. );
  843. Object.defineProperties(result, {
  844. html: {
  845. get: () => {
  846. return (sync ??= Renderer.#render(component, options)).body;
  847. }
  848. },
  849. head: {
  850. get: () => {
  851. return (sync ??= Renderer.#render(component, options)).head;
  852. }
  853. },
  854. body: {
  855. get: () => {
  856. return (sync ??= Renderer.#render(component, options)).body;
  857. }
  858. },
  859. hashes: {
  860. value: {
  861. script: ""
  862. }
  863. },
  864. then: {
  865. value: (
  866. /**
  867. * this is not type-safe, but honestly it's the best I can do right now, and it's a straightforward function.
  868. *
  869. * @template TResult1
  870. * @template [TResult2=never]
  871. * @param { (value: SyncRenderOutput) => TResult1 } onfulfilled
  872. * @param { (reason: unknown) => TResult2 } onrejected
  873. */
  874. (onfulfilled, onrejected) => {
  875. {
  876. const result2 = sync ??= Renderer.#render(component, options);
  877. const user_result = onfulfilled({
  878. head: result2.head,
  879. body: result2.body,
  880. html: result2.body,
  881. hashes: { script: [] }
  882. });
  883. return Promise.resolve(user_result);
  884. }
  885. }
  886. )
  887. }
  888. });
  889. return result;
  890. }
  891. /**
  892. * Collect all of the `onDestroy` callbacks registered during rendering. In an async context, this is only safe to call
  893. * after awaiting `collect_async`.
  894. *
  895. * Child renderers are "porous" and don't affect execution order, but component body renderers
  896. * create ordering boundaries. Within a renderer, callbacks run in order until hitting a component boundary.
  897. * @returns {Iterable<() => void>}
  898. */
  899. *#collect_on_destroy() {
  900. for (const component of this.#traverse_components()) {
  901. yield* component.#collect_ondestroy();
  902. }
  903. }
  904. /**
  905. * Performs a depth-first search of renderers, yielding the deepest components first, then additional components as we backtrack up the tree.
  906. * @returns {Iterable<Renderer>}
  907. */
  908. *#traverse_components() {
  909. for (const child of this.#out) {
  910. if (typeof child !== "string") {
  911. yield* child.#traverse_components();
  912. }
  913. }
  914. if (this.#is_component_body) {
  915. yield this;
  916. }
  917. }
  918. /**
  919. * @returns {Iterable<() => void>}
  920. */
  921. *#collect_ondestroy() {
  922. if (this.#on_destroy) {
  923. for (const fn of this.#on_destroy) {
  924. yield fn;
  925. }
  926. }
  927. for (const child of this.#out) {
  928. if (child instanceof Renderer && !child.#is_component_body) {
  929. yield* child.#collect_ondestroy();
  930. }
  931. }
  932. }
  933. /**
  934. * Render a component. Throws if any of the children are performing asynchronous work.
  935. *
  936. * @template {Record<string, any>} Props
  937. * @param {Component<Props>} component
  938. * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string }} options
  939. * @returns {AccumulatedContent}
  940. */
  941. static #render(component, options) {
  942. var previous_context = ssr_context;
  943. try {
  944. const renderer = Renderer.#open_render("sync", component, options);
  945. const content = renderer.#collect_content();
  946. return Renderer.#close_render(content, renderer);
  947. } finally {
  948. abort();
  949. set_ssr_context(previous_context);
  950. }
  951. }
  952. /**
  953. * Render a component.
  954. *
  955. * @template {Record<string, any>} Props
  956. * @param {Component<Props>} component
  957. * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: Csp }} options
  958. * @returns {Promise<AccumulatedContent & { hashes: { script: Sha256Source[] } }>}
  959. */
  960. static async #render_async(component, options) {
  961. const previous_context = ssr_context;
  962. try {
  963. const renderer = Renderer.#open_render("async", component, options);
  964. const content = await renderer.#collect_content_async();
  965. const hydratables = await renderer.#collect_hydratables();
  966. if (hydratables !== null) {
  967. content.head = hydratables + content.head;
  968. }
  969. return Renderer.#close_render(content, renderer);
  970. } finally {
  971. set_ssr_context(previous_context);
  972. abort();
  973. }
  974. }
  975. /**
  976. * Collect all of the code from the `out` array and return it as a string, or a promise resolving to a string.
  977. * @param {AccumulatedContent} content
  978. * @returns {AccumulatedContent}
  979. */
  980. #collect_content(content = { head: "", body: "" }) {
  981. for (const item of this.#out) {
  982. if (typeof item === "string") {
  983. content[this.type] += item;
  984. } else if (item instanceof Renderer) {
  985. item.#collect_content(content);
  986. }
  987. }
  988. return content;
  989. }
  990. /**
  991. * Collect all of the code from the `out` array and return it as a string.
  992. * @param {AccumulatedContent} content
  993. * @returns {Promise<AccumulatedContent>}
  994. */
  995. async #collect_content_async(content = { head: "", body: "" }) {
  996. await this.promise;
  997. for (const item of this.#out) {
  998. if (typeof item === "string") {
  999. content[this.type] += item;
  1000. } else if (item instanceof Renderer) {
  1001. if (item.#boundary) {
  1002. const boundary_content = { head: "", body: "" };
  1003. try {
  1004. await item.#collect_content_async(boundary_content);
  1005. content.head += boundary_content.head;
  1006. content.body += boundary_content.body;
  1007. } catch (error) {
  1008. const { context, failed, transformError } = item.#boundary;
  1009. set_ssr_context(context);
  1010. let transformed = await transformError(error);
  1011. const failed_renderer = new Renderer(item.global, item);
  1012. failed_renderer.type = item.type;
  1013. failed_renderer.#out.push(Renderer.#serialize_failed_boundary(transformed));
  1014. failed(failed_renderer, transformed, noop);
  1015. failed_renderer.#out.push(BLOCK_CLOSE);
  1016. await failed_renderer.#collect_content_async(content);
  1017. }
  1018. } else {
  1019. await item.#collect_content_async(content);
  1020. }
  1021. }
  1022. }
  1023. return content;
  1024. }
  1025. async #collect_hydratables() {
  1026. const ctx = get_render_context().hydratable;
  1027. for (const [_, key] of ctx.unresolved_promises) {
  1028. unresolved_hydratable(key, ctx.lookup.get(key)?.stack ?? "<missing stack trace>");
  1029. }
  1030. for (const comparison of ctx.comparisons) {
  1031. await comparison;
  1032. }
  1033. return await this.#hydratable_block(ctx);
  1034. }
  1035. /**
  1036. * @template {Record<string, any>} Props
  1037. * @param {'sync' | 'async'} mode
  1038. * @param {import('svelte').Component<Props>} component
  1039. * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: Csp; transformError?: (error: unknown) => unknown }} options
  1040. * @returns {Renderer}
  1041. */
  1042. static #open_render(mode, component, options) {
  1043. var previous_context = ssr_context;
  1044. try {
  1045. const renderer = new Renderer(
  1046. new SSRState(
  1047. mode,
  1048. options.idPrefix ? options.idPrefix + "-" : "",
  1049. options.csp,
  1050. options.transformError
  1051. )
  1052. );
  1053. const context = { p: null, c: options.context ?? null, r: renderer };
  1054. set_ssr_context(context);
  1055. renderer.push(BLOCK_OPEN);
  1056. component(renderer, options.props ?? {});
  1057. renderer.push(BLOCK_CLOSE);
  1058. return renderer;
  1059. } finally {
  1060. set_ssr_context(previous_context);
  1061. }
  1062. }
  1063. /**
  1064. * @param {AccumulatedContent} content
  1065. * @param {Renderer} renderer
  1066. * @returns {AccumulatedContent & { hashes: { script: Sha256Source[] } }}
  1067. */
  1068. static #close_render(content, renderer) {
  1069. for (const cleanup of renderer.#collect_on_destroy()) {
  1070. cleanup();
  1071. }
  1072. let head2 = content.head + renderer.global.get_title();
  1073. let body = content.body;
  1074. for (const { hash, code } of renderer.global.css) {
  1075. head2 += `<style id="${hash}">${code}</style>`;
  1076. }
  1077. return {
  1078. head: head2,
  1079. body,
  1080. hashes: {
  1081. script: renderer.global.csp.script_hashes
  1082. }
  1083. };
  1084. }
  1085. /**
  1086. * @param {HydratableContext} ctx
  1087. */
  1088. async #hydratable_block(ctx) {
  1089. if (ctx.lookup.size === 0) {
  1090. return null;
  1091. }
  1092. let entries = [];
  1093. let has_promises = false;
  1094. for (const [k, v] of ctx.lookup) {
  1095. if (v.promises) {
  1096. has_promises = true;
  1097. for (const p of v.promises) await p;
  1098. }
  1099. entries.push(`[${devalue.uneval(k)},${v.serialized}]`);
  1100. }
  1101. let prelude = `const h = (window.__svelte ??= {}).h ??= new Map();`;
  1102. if (has_promises) {
  1103. prelude = `const r = (v) => Promise.resolve(v);
  1104. ${prelude}`;
  1105. }
  1106. const body = `
  1107. {
  1108. ${prelude}
  1109. for (const [k, v] of [
  1110. ${entries.join(",\n ")}
  1111. ]) {
  1112. h.set(k, v);
  1113. }
  1114. }
  1115. `;
  1116. let csp_attr = "";
  1117. if (this.global.csp.nonce) {
  1118. csp_attr = ` nonce="${this.global.csp.nonce}"`;
  1119. } else if (this.global.csp.hash) {
  1120. const hash = await sha256(body);
  1121. this.global.csp.script_hashes.push(`sha256-${hash}`);
  1122. }
  1123. return `
  1124. <script${csp_attr}>${body}<\/script>`;
  1125. }
  1126. }
  1127. class SSRState {
  1128. /** @readonly @type {Csp & { script_hashes: Sha256Source[] }} */
  1129. csp;
  1130. /** @readonly @type {'sync' | 'async'} */
  1131. mode;
  1132. /** @readonly @type {() => string} */
  1133. uid;
  1134. /** @readonly @type {Set<{ hash: string; code: string }>} */
  1135. css = /* @__PURE__ */ new Set();
  1136. /**
  1137. * `transformError` passed to `render`. Called when an error boundary catches an error.
  1138. * Throws by default if unset in `render`.
  1139. * @type {(error: unknown) => unknown}
  1140. */
  1141. transformError;
  1142. /** @type {{ path: number[], value: string }} */
  1143. #title = { path: [], value: "" };
  1144. /**
  1145. * @param {'sync' | 'async'} mode
  1146. * @param {string} id_prefix
  1147. * @param {Csp} csp
  1148. * @param {((error: unknown) => unknown) | undefined} [transformError]
  1149. */
  1150. constructor(mode, id_prefix = "", csp = { hash: false }, transformError) {
  1151. this.mode = mode;
  1152. this.csp = { ...csp, script_hashes: [] };
  1153. this.transformError = transformError ?? ((error) => {
  1154. throw error;
  1155. });
  1156. let uid = 1;
  1157. this.uid = () => `${id_prefix}s${uid++}`;
  1158. }
  1159. get_title() {
  1160. return this.#title.value;
  1161. }
  1162. /**
  1163. * Performs a depth-first (lexicographic) comparison using the path. Rejects sets
  1164. * from earlier than or equal to the current value.
  1165. * @param {string} value
  1166. * @param {number[]} path
  1167. */
  1168. set_title(value, path) {
  1169. const current = this.#title.path;
  1170. let i = 0;
  1171. let l = Math.min(path.length, current.length);
  1172. while (i < l && path[i] === current[i]) i += 1;
  1173. if (path[i] === void 0) return;
  1174. if (current[i] === void 0 || path[i] > current[i]) {
  1175. this.#title.path = path;
  1176. this.#title.value = value;
  1177. }
  1178. }
  1179. }
  1180. export {
  1181. setContext as $,
  1182. ASYNC as A,
  1183. BOUNDARY_EFFECT as B,
  1184. COMMENT_NODE as C,
  1185. DIRTY as D,
  1186. ERROR_VALUE as E,
  1187. array_prototype as F,
  1188. get_descriptor as G,
  1189. HYDRATION_ERROR as H,
  1190. INERT as I,
  1191. get_prototype_of as J,
  1192. is_array as K,
  1193. is_extensible as L,
  1194. MAYBE_DIRTY as M,
  1195. HEAD_EFFECT as N,
  1196. DESTROYING as O,
  1197. USER_EFFECT as P,
  1198. REACTION_IS_UPDATING as Q,
  1199. REACTION_RAN as R,
  1200. STALE_REACTION as S,
  1201. index_of as T,
  1202. UNINITIALIZED as U,
  1203. define_property as V,
  1204. WAS_MARKED as W,
  1205. array_from as X,
  1206. is_passive_event as Y,
  1207. LEGACY_PROPS as Z,
  1208. render as _,
  1209. HYDRATION_END as a,
  1210. derived as a0,
  1211. head as a1,
  1212. attr_class as a2,
  1213. attr as a3,
  1214. clsx as a4,
  1215. ensure_array_like as a5,
  1216. attr_style as a6,
  1217. stringify as a7,
  1218. HYDRATION_START as b,
  1219. HYDRATION_START_ELSE as c,
  1220. EFFECT as d,
  1221. escape_html as e,
  1222. CONNECTED as f,
  1223. getContext as g,
  1224. CLEAN as h,
  1225. DERIVED as i,
  1226. BLOCK_EFFECT as j,
  1227. DESTROYED as k,
  1228. EAGER_EFFECT as l,
  1229. deferred as m,
  1230. noop as n,
  1231. RENDER_EFFECT as o,
  1232. MANAGED_EFFECT as p,
  1233. ROOT_EFFECT as q,
  1234. run_all as r,
  1235. slot as s,
  1236. BRANCH_EFFECT as t,
  1237. includes as u,
  1238. HYDRATION_START_FAILED as v,
  1239. EFFECT_TRANSPARENT as w,
  1240. EFFECT_PRESERVED as x,
  1241. STATE_SYMBOL as y,
  1242. object_prototype as z
  1243. };