@symbo.ls/brender 3.7.3 → 3.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -24,6 +24,7 @@ __export(index_exports, {
24
24
  extractMetadata: () => import_metadata.extractMetadata,
25
25
  generateHeadHtml: () => import_metadata.generateHeadHtml,
26
26
  generateSitemap: () => import_sitemap.generateSitemap,
27
+ getAccumulatedEmotionCSS: () => import_render.getAccumulatedEmotionCSS,
27
28
  hydrate: () => import_hydrate.hydrate,
28
29
  injectPrefetchedState: () => import_prefetch.injectPrefetchedState,
29
30
  loadAndRenderAll: () => import_load.loadAndRenderAll,
@@ -34,6 +35,7 @@ __export(index_exports, {
34
35
  renderElement: () => import_render.renderElement,
35
36
  renderPage: () => import_render.renderPage,
36
37
  renderRoute: () => import_render.renderRoute,
38
+ replaceEmotionCSS: () => import_render.replaceEmotionCSS,
37
39
  resetGlobalCSSCache: () => import_render.resetGlobalCSSCache,
38
40
  resetKeys: () => import_keys.resetKeys
39
41
  });
@@ -58,6 +60,8 @@ var index_default = {
58
60
  renderRoute: import_render.renderRoute,
59
61
  renderPage: import_render.renderPage,
60
62
  resetGlobalCSSCache: import_render.resetGlobalCSSCache,
63
+ getAccumulatedEmotionCSS: import_render.getAccumulatedEmotionCSS,
64
+ replaceEmotionCSS: import_render.replaceEmotionCSS,
61
65
  extractMetadata: import_metadata.extractMetadata,
62
66
  generateHeadHtml: import_metadata.generateHeadHtml,
63
67
  collectBrNodes: import_hydrate.collectBrNodes,
package/dist/cjs/load.js CHANGED
@@ -103,17 +103,17 @@ const loadProject = async (projectPath) => {
103
103
  bundleAndImport((0, import_path.join)(symbolsDir, "files", "index.js"))
104
104
  ]);
105
105
  return {
106
- app: appModule?.default || {},
107
- state: stateModule?.default || {},
108
- dependencies: depsModule?.default || {},
109
- components: componentsModule || {},
110
- snippets: snippetsModule || {},
111
- pages: pagesModule?.default || {},
112
- functions: functionsModule || {},
113
- methods: methodsModule || {},
114
- designSystem: designSystemModule?.default || {},
115
- files: filesModule?.default || {},
116
- config: configModule?.default || {}
106
+ app: { ...appModule?.default || {} },
107
+ state: { ...stateModule?.default || {} },
108
+ dependencies: { ...depsModule?.default || {} },
109
+ components: { ...componentsModule || {} },
110
+ snippets: { ...snippetsModule || {} },
111
+ pages: { ...pagesModule?.default || {} },
112
+ functions: { ...functionsModule || {} },
113
+ methods: { ...methodsModule || {} },
114
+ designSystem: { ...designSystemModule?.default || {} },
115
+ files: { ...filesModule?.default || {} },
116
+ config: { ...configModule?.default || {} }
117
117
  };
118
118
  };
119
119
  const loadAndRenderAll = async (projectPath, renderFn) => {
@@ -27,6 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
28
  var prefetch_exports = {};
29
29
  __export(prefetch_exports, {
30
+ fetchSSRTranslations: () => fetchSSRTranslations,
30
31
  injectPrefetchedState: () => injectPrefetchedState,
31
32
  prefetchPageData: () => prefetchPageData
32
33
  });
@@ -98,15 +99,14 @@ const createSSRAdapter = async (dbConfig) => {
98
99
  if (adapter !== "supabase") return null;
99
100
  const supabaseUrl = url || projectId && `https://${projectId}.supabase.co`;
100
101
  if (!supabaseUrl || !key) return null;
101
- let clientFactory = createClient;
102
- if (!clientFactory) {
103
- try {
104
- const mod = await import("@supabase/supabase-js");
105
- clientFactory = mod.createClient;
106
- } catch {
107
- return null;
108
- }
102
+ let clientFactory;
103
+ try {
104
+ const mod = await import("@supabase/supabase-js");
105
+ clientFactory = mod.createClient;
106
+ } catch {
107
+ clientFactory = createClient;
109
108
  }
109
+ if (!clientFactory) return null;
110
110
  const client = clientFactory(supabaseUrl, key);
111
111
  return {
112
112
  rpc: ({ from, params }) => client.rpc(from, params),
@@ -159,7 +159,8 @@ const prefetchPageData = async (data, route = "/", options = {}) => {
159
159
  const pages = data.pages || {};
160
160
  const pageDef = pages[route];
161
161
  if (!pageDef) return /* @__PURE__ */ new Map();
162
- const dbConfig = data.config?.db || data.settings?.db || data.db;
162
+ const config = data.config || data.settings || {};
163
+ const dbConfig = config.fetch || data.fetch || config.db || data.db;
163
164
  if (!dbConfig) return /* @__PURE__ */ new Map();
164
165
  const adapter = await createSSRAdapter(dbConfig);
165
166
  if (!adapter) return /* @__PURE__ */ new Map();
@@ -167,17 +168,81 @@ const prefetchPageData = async (data, route = "/", options = {}) => {
167
168
  if (!declarations.length) return /* @__PURE__ */ new Map();
168
169
  const stateUpdates = /* @__PURE__ */ new Map();
169
170
  const results = await Promise.allSettled(
170
- declarations.map(async ({ config, stateKey, path }) => {
171
- const fetchedData = await executeSingle(adapter, config);
172
- if (fetchedData !== null && stateKey) {
171
+ declarations.map(async ({ config: config2, stateKey, path }) => {
172
+ const fetchedData = await executeSingle(adapter, config2);
173
+ if (fetchedData !== null) {
173
174
  const existing = stateUpdates.get(path) || {};
174
- existing[stateKey] = fetchedData;
175
+ if (stateKey) {
176
+ existing[stateKey] = fetchedData;
177
+ } else if (isObject(fetchedData)) {
178
+ Object.assign(existing, fetchedData);
179
+ }
175
180
  stateUpdates.set(path, existing);
176
181
  }
177
182
  })
178
183
  );
179
184
  return stateUpdates;
180
185
  };
186
+ const fetchSSRTranslations = async (data) => {
187
+ const config = data.config || {};
188
+ const polyglot = config.polyglot || data.polyglot;
189
+ if (!polyglot?.fetch) return null;
190
+ const dbConfig = config.fetch || data.fetch || config.db || data.db;
191
+ if (!dbConfig) return null;
192
+ const adapter = await createSSRAdapter(dbConfig);
193
+ if (!adapter) return null;
194
+ const fetchConfig = polyglot.fetch;
195
+ const rpcName = fetchConfig.rpc || fetchConfig.from || "get_translations_if_changed";
196
+ const languages = polyglot.languages || [polyglot.defaultLang || "en"];
197
+ const translations = {};
198
+ const results = await Promise.allSettled(
199
+ languages.map(async (lang) => {
200
+ try {
201
+ const res = await adapter.rpc({
202
+ from: rpcName,
203
+ params: { p_lang: lang, p_cached_version: 0 }
204
+ });
205
+ if (res.error || !res.data) return;
206
+ const result = res.data;
207
+ if (result.translations) {
208
+ translations[lang] = result.translations;
209
+ }
210
+ } catch {
211
+ }
212
+ })
213
+ );
214
+ return Object.keys(translations).length ? translations : null;
215
+ };
216
+ const preEvaluateChildren = (def, inheritedState) => {
217
+ if (!def || typeof def !== "object") return;
218
+ for (const key in def) {
219
+ if (key === "state" || key === "fetch" || key === "props" || key === "attr" || key === "on" || key === "define" || key === "childExtends" || key === "childProps" || key === "childrenAs") continue;
220
+ if (key.charAt(0) >= "A" && key.charAt(0) <= "Z" && isObject(def[key])) {
221
+ const child = def[key];
222
+ const effectiveState = child.state && typeof child.state === "object" ? { ...inheritedState, ...child.state } : inheritedState;
223
+ if (isFunction(child.children)) {
224
+ try {
225
+ const mockEl = {
226
+ state: effectiveState,
227
+ props: {},
228
+ call: (fn) => {
229
+ if (fn === "getActiveLang" || fn === "getLang") return effectiveState?.lang || "ka";
230
+ if (fn === "polyglot") return arguments[1] || "";
231
+ return void 0;
232
+ },
233
+ __ref: {}
234
+ };
235
+ const result = child.children(mockEl, effectiveState);
236
+ if (isArray(result) && result.length > 0) {
237
+ child.children = result;
238
+ }
239
+ } catch {
240
+ }
241
+ }
242
+ preEvaluateChildren(child, effectiveState);
243
+ }
244
+ }
245
+ };
181
246
  const injectPrefetchedState = (pageDef, stateUpdates) => {
182
247
  if (!stateUpdates || !stateUpdates.size) return;
183
248
  for (const [path, data] of stateUpdates) {
@@ -194,6 +259,7 @@ const injectPrefetchedState = (pageDef, stateUpdates) => {
194
259
  target.state = {};
195
260
  }
196
261
  Object.assign(target.state, data);
262
+ preEvaluateChildren(target, target.state);
197
263
  }
198
264
  }
199
265
  };
@@ -27,10 +27,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
28
  var render_exports = {};
29
29
  __export(render_exports, {
30
+ getAccumulatedEmotionCSS: () => getAccumulatedEmotionCSS,
30
31
  render: () => render,
31
32
  renderElement: () => renderElement,
32
33
  renderPage: () => renderPage,
33
34
  renderRoute: () => renderRoute,
35
+ replaceEmotionCSS: () => replaceEmotionCSS,
34
36
  resetGlobalCSSCache: () => resetGlobalCSSCache
35
37
  });
36
38
  module.exports = __toCommonJS(render_exports);
@@ -44,7 +46,53 @@ var import_metadata = require("./metadata.js");
44
46
  var import_hydrate = require("./hydrate.js");
45
47
  var import_prefetch = require("./prefetch.js");
46
48
  var import_linkedom = require("linkedom");
49
+ var import_create_instance = __toESM(require("@emotion/css/create-instance"), 1);
47
50
  const import_meta = {};
51
+ const ssrResolve = (map, key) => {
52
+ if (!map || !key) return void 0;
53
+ if (map[key] !== void 0) return map[key];
54
+ const parts = key.split(".");
55
+ let v = map;
56
+ for (const p of parts) {
57
+ if (v == null || typeof v !== "object") return void 0;
58
+ v = v[p];
59
+ }
60
+ return v;
61
+ };
62
+ const ssrTranslate = function(key, lang) {
63
+ if (!key) return "";
64
+ const ctx = this?.context;
65
+ const poly = ctx?.polyglot;
66
+ const activeLang = lang || poly?.defaultLang || "ka";
67
+ if (poly?.translations) {
68
+ const langMap = poly.translations[activeLang];
69
+ if (langMap) {
70
+ const val = ssrResolve(langMap, key);
71
+ if (val !== void 0) return val;
72
+ }
73
+ }
74
+ const root = this?.state?.root || ctx?.state?.root;
75
+ if (root?.translations) {
76
+ const langMap = root.translations[activeLang];
77
+ if (langMap) {
78
+ const val = ssrResolve(langMap, key);
79
+ if (val !== void 0) return val;
80
+ }
81
+ }
82
+ const defaultLang = poly?.defaultLang || "en";
83
+ if (defaultLang !== activeLang && poly?.translations) {
84
+ const fallback = poly.translations[defaultLang];
85
+ if (fallback) {
86
+ const val = ssrResolve(fallback, key);
87
+ if (val !== void 0) return val;
88
+ }
89
+ }
90
+ return key;
91
+ };
92
+ const ssrGetActiveLang = function() {
93
+ const ctx = this?.context;
94
+ return this?.state?.root?.lang || ctx?.polyglot?.defaultLang || "ka";
95
+ };
48
96
  const structuredCloneDeep = (obj, seen = /* @__PURE__ */ new WeakMap()) => {
49
97
  if (obj === null || typeof obj !== "object") return obj;
50
98
  if (seen.has(obj)) return seen.get(obj);
@@ -76,7 +124,7 @@ const safeJsonReplacer = () => {
76
124
  let _cachedCreateDomql = null;
77
125
  const bundleCreateDomql = async () => {
78
126
  if (_cachedCreateDomql) return _cachedCreateDomql;
79
- const brenderDir = new URL(".", import_meta.url).pathname;
127
+ const brenderDir = (0, import_fs.realpathSync)(new URL(".", import_meta.url).pathname);
80
128
  const monorepoRoot = (0, import_path.resolve)(brenderDir, "../..");
81
129
  const entry = (0, import_path.resolve)(monorepoRoot, "packages", "smbls", "src", "createDomql.js");
82
130
  const esbuild = await import("esbuild");
@@ -320,7 +368,16 @@ const UIKIT_STUBS = {
320
368
  Text: { tag: "span" }
321
369
  };
322
370
  const render = async (data, options = {}) => {
323
- const { route = "/", state: stateOverrides, context: contextOverrides, prefetch = false } = options;
371
+ const { route = "/", pathname, state: stateOverrides, context: contextOverrides, prefetch = false } = options;
372
+ const locationPath = pathname || route;
373
+ const _prevLocPrefetch = globalThis.location;
374
+ const _prevWinPrefetch = globalThis.window;
375
+ if (!globalThis.location || globalThis.location.pathname !== locationPath) {
376
+ globalThis.location = { pathname: locationPath, href: locationPath, search: "", hash: "", origin: "" };
377
+ }
378
+ if (!globalThis.window) {
379
+ globalThis.window = { location: globalThis.location };
380
+ }
324
381
  let prefetchedPages;
325
382
  if (prefetch) {
326
383
  try {
@@ -346,44 +403,102 @@ const render = async (data, options = {}) => {
346
403
  (0, import_prefetch.injectPrefetchedState)(pageDef, stateUpdates);
347
404
  prefetchedPages[route] = pageDef;
348
405
  }
349
- } catch {
406
+ } catch (prefetchErr) {
407
+ console.error("[brender] Prefetch error:", prefetchErr);
350
408
  prefetchedPages = data.pages;
351
409
  }
352
410
  }
411
+ let ssrTranslations;
412
+ if (prefetch) {
413
+ try {
414
+ ssrTranslations = await (0, import_prefetch.fetchSSRTranslations)(data);
415
+ } catch {
416
+ }
417
+ }
418
+ if (_prevLocPrefetch !== void 0) globalThis.location = _prevLocPrefetch;
419
+ else delete globalThis.location;
420
+ if (_prevWinPrefetch !== void 0) globalThis.window = _prevWinPrefetch;
421
+ else delete globalThis.window;
353
422
  const { window, document } = (0, import_env.createEnv)();
354
423
  const body = document.body;
355
- window.location.pathname = route;
424
+ window.location.pathname = locationPath;
356
425
  const _prevDoc = globalThis.document;
357
426
  const _prevLoc = globalThis.location;
358
427
  globalThis.document = document;
359
428
  globalThis.location = window.location;
360
429
  const { createDomqlElement } = await bundleCreateDomql();
361
430
  const app = structuredCloneDeep(data.app || {});
431
+ const config = { ...data.config || {} };
432
+ if (data.polyglot && !config.polyglot) config.polyglot = data.polyglot;
433
+ if (data.fetch && !config.fetch) config.fetch = data.fetch;
434
+ if (data.router && !config.router) config.router = data.router;
435
+ for (const k of ["useReset", "useVariable", "useFontImport", "useIconSprite", "useSvgSprite", "useDefaultConfig", "useDocumentTheme"]) {
436
+ if (data[k] != null && config[k] == null) config[k] = data[k];
437
+ }
438
+ const polyglotConfig = config.polyglot ? { ...config.polyglot } : void 0;
439
+ if (ssrTranslations && polyglotConfig) {
440
+ polyglotConfig.translations = {
441
+ ...polyglotConfig.translations || {},
442
+ ...ssrTranslations
443
+ };
444
+ }
445
+ const baseState = structuredCloneDeep(data.state || {});
446
+ if (ssrTranslations || polyglotConfig) {
447
+ if (!baseState.root) baseState.root = {};
448
+ if (polyglotConfig) {
449
+ baseState.root.lang = baseState.root.lang || polyglotConfig.defaultLang || "en";
450
+ }
451
+ if (ssrTranslations) {
452
+ baseState.root.translations = {
453
+ ...baseState.root.translations || {},
454
+ ...ssrTranslations
455
+ };
456
+ }
457
+ }
458
+ const ssrEmotion = (0, import_create_instance.default)({
459
+ key: "smbls",
460
+ container: document.head,
461
+ speedy: false
462
+ });
362
463
  const ctx = {
363
- state: structuredCloneDeep(data.state || {}),
364
- ...stateOverrides ? { state: { ...structuredCloneDeep(data.state || {}), ...stateOverrides } } : {},
464
+ state: baseState,
465
+ ...stateOverrides ? { state: { ...baseState, ...stateOverrides } } : {},
365
466
  dependencies: structuredCloneDeep(data.dependencies || {}),
366
467
  components: structuredCloneDeep(data.components || {}),
367
468
  snippets: structuredCloneDeep(data.snippets || {}),
368
469
  pages: structuredCloneDeep(prefetchedPages || data.pages || {}),
369
- functions: data.functions || {},
470
+ functions: {
471
+ ...data.functions || {},
472
+ // SSR polyglot functions — enable {{ key | polyglot }} resolution during render
473
+ polyglot: ssrTranslate,
474
+ getActiveLang: ssrGetActiveLang,
475
+ getLang: ssrGetActiveLang
476
+ },
370
477
  methods: data.methods || {},
371
478
  designSystem: structuredCloneDeep(data.designSystem || {}),
372
479
  files: data.files || {},
373
- ...data.config || data.settings || {},
480
+ ...config,
481
+ // Override polyglot with SSR-enriched version
482
+ ...polyglotConfig ? { polyglot: polyglotConfig } : {},
374
483
  // Virtual DOM environment
375
484
  document,
376
485
  window,
377
486
  parent: { node: body },
487
+ // Use SSR emotion instance (non-speedy) for proper @media rule extraction
488
+ initOptions: { emotion: ssrEmotion },
489
+ // Disable sourcemap tracking in SSR — it causes stack overflows
490
+ // when state contains large data arrays (articles, events, etc.)
491
+ domqlOptions: { sourcemap: false },
378
492
  // Caller overrides
379
493
  ...contextOverrides || {}
380
494
  };
381
495
  (0, import_keys.resetKeys)();
382
496
  const element = await createDomqlElement(app, ctx);
383
- await new Promise((r) => setTimeout(r, 50));
497
+ const flushDelay = prefetch ? 2e3 : 50;
498
+ await new Promise((r) => setTimeout(r, flushDelay));
384
499
  (0, import_keys.assignKeys)(body);
385
500
  const registry = (0, import_keys.mapKeysToElements)(element);
386
- const metadata = (0, import_metadata.extractMetadata)(data, route);
501
+ const metadata = (0, import_metadata.extractMetadata)(data, route, element, element?.state);
387
502
  const emotionCSS = [];
388
503
  const emotionInstance = ctx.emotion || element && element.context && element.context.emotion;
389
504
  if (emotionInstance && emotionInstance.cache) {
@@ -420,12 +535,20 @@ const render = async (data, options = {}) => {
420
535
  }
421
536
  }
422
537
  }
423
- const html = fixSvgContent(body.innerHTML);
538
+ let html = fixSvgContent(body.innerHTML);
539
+ if (ssrTranslations) {
540
+ const defaultLang = polyglotConfig?.defaultLang || "en";
541
+ const langMap = ssrTranslations[defaultLang] || Object.values(ssrTranslations)[0] || {};
542
+ html = html.replace(/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g, (match, key) => {
543
+ const trimmed = key.trim();
544
+ return langMap[trimmed] ?? match;
545
+ });
546
+ }
424
547
  if (_prevDoc !== void 0) globalThis.document = _prevDoc;
425
548
  else delete globalThis.document;
426
549
  if (_prevLoc !== void 0) globalThis.location = _prevLoc;
427
550
  else delete globalThis.location;
428
- return { html, metadata, registry, element, emotionCSS, document, window };
551
+ return { html, metadata, registry, element, emotionCSS, document, window, ssrTranslations, prefetchedPages };
429
552
  };
430
553
  const renderElement = async (elementDef, options = {}) => {
431
554
  const { context = {} } = options;
@@ -609,63 +732,82 @@ ${frameRules}
609
732
  return _cachedGlobalCSS;
610
733
  }
611
734
  };
612
- let _cachedEmotionCSS = null;
735
+ let _accumulatedEmotionCSS = /* @__PURE__ */ new Set();
613
736
  const resetGlobalCSSCache = () => {
614
737
  _cachedGlobalCSS = null;
615
- _cachedEmotionCSS = null;
738
+ _accumulatedEmotionCSS = /* @__PURE__ */ new Set();
739
+ };
740
+ const getAccumulatedEmotionCSS = () => Array.from(_accumulatedEmotionCSS).join("\n");
741
+ const replaceEmotionCSS = (html, newCSS) => {
742
+ return html.replace(
743
+ /<style data-emotion="smbls">[\s\S]*?<\/style>/,
744
+ newCSS ? `<style data-emotion="smbls">
745
+ ${newCSS}
746
+ </style>` : ""
747
+ );
616
748
  };
617
749
  const renderRoute = async (data, options = {}) => {
618
- const { route = "/" } = options;
750
+ const { route = "/", pathname } = options;
751
+ const result = await render(data, { route, pathname, prefetch: true });
752
+ if (!result) return null;
619
753
  const ds = data.designSystem || {};
620
- const pageDef = (data.pages || {})[route];
621
- if (!pageDef) return null;
622
- const result = await renderElement(pageDef, {
623
- context: {
624
- components: data.components || {},
625
- snippets: data.snippets || {},
626
- designSystem: ds,
627
- state: data.state || {},
628
- functions: data.functions || {},
629
- methods: data.methods || {}
630
- }
631
- });
632
- const { document: cssDoc } = (0, import_linkedom.parseHTML)(`<html><head></head><body>${result.html}</body></html>`);
633
- let emotionInstance;
754
+ const globalCSS = await generateGlobalCSS(ds, data.config || data.settings);
755
+ let prefetchedState = null;
756
+ let activeLang = null;
634
757
  try {
635
- const { default: createInstance } = await import("@emotion/css/create-instance");
636
- emotionInstance = createInstance({ key: "smbls", container: cssDoc.head });
637
- } catch {
758
+ const el = result.element;
759
+ const polyglot = el?.context?.polyglot || data.polyglot || data.config?.polyglot;
760
+ activeLang = el?.state?.root?.lang || polyglot?.defaultLang || "en";
761
+ if (result.prefetchedPages && result.prefetchedPages[route]) {
762
+ const pageDef = result.prefetchedPages[route];
763
+ const collectStates = (def, result2 = {}) => {
764
+ if (!def || typeof def !== "object") return result2;
765
+ if (def.state && typeof def.state === "object") {
766
+ for (const [k, v] of Object.entries(def.state)) {
767
+ if (v !== void 0 && v !== null && typeof v !== "function") {
768
+ result2[k] = v;
769
+ }
770
+ }
771
+ }
772
+ for (const [key, child] of Object.entries(def)) {
773
+ if (key === "state" || key === "props" || key === "attr" || key === "on" || key === "define" || key === "__ref" || key.startsWith("__")) continue;
774
+ if (child && typeof child === "object" && !Array.isArray(child)) {
775
+ collectStates(child, result2);
776
+ }
777
+ }
778
+ return result2;
779
+ };
780
+ prefetchedState = collectStates(pageDef);
781
+ }
782
+ } catch (e) {
638
783
  }
639
- (0, import_hydrate.hydrate)(result.element, {
640
- root: cssDoc.body,
641
- renderEvents: false,
642
- events: false,
643
- emotion: emotionInstance,
644
- designSystem: ds
645
- });
646
- const globalCSS = await generateGlobalCSS(ds, data.config || data.settings);
647
784
  return {
648
- html: cssDoc.body.innerHTML,
649
- css: extractCSS(result.element, ds),
785
+ html: result.html,
786
+ css: result.emotionCSS ? result.emotionCSS.join("\n") : "",
650
787
  globalCSS,
651
788
  resetCss: globalCSS.resetRules || generateResetCSS(ds.reset),
652
789
  fontLinks: generateFontLinks(ds),
653
- metadata: (0, import_metadata.extractMetadata)(data, route),
654
- brKeyCount: Object.keys(result.registry).length
790
+ metadata: result.metadata || (0, import_metadata.extractMetadata)(data, route),
791
+ brKeyCount: result.registry ? Object.keys(result.registry).length : 0,
792
+ ssrTranslations: result.ssrTranslations,
793
+ prefetchedState,
794
+ activeLang
655
795
  };
656
796
  };
657
797
  const renderPage = async (data, route = "/", options = {}) => {
658
- const { lang, themeColor, isr, prefetch = true } = options;
798
+ const { lang, themeColor, isr, hydrate: hydrate2 = true, prefetch = true } = options;
659
799
  const htmlLang = lang || data.state?.lang || data.app?.metadata?.lang || "en";
660
800
  const result = await render(data, { route, prefetch });
661
801
  if (!result) return null;
662
802
  const metadata = { ...result.metadata };
663
803
  if (themeColor) metadata["theme-color"] = themeColor;
664
804
  const headTags = (0, import_metadata.generateHeadHtml)(metadata);
665
- if (!_cachedEmotionCSS && result.emotionCSS && result.emotionCSS.length) {
666
- _cachedEmotionCSS = result.emotionCSS.join("\n");
805
+ if (result.emotionCSS && result.emotionCSS.length) {
806
+ for (const rule of result.emotionCSS) {
807
+ if (rule) _accumulatedEmotionCSS.add(rule);
808
+ }
667
809
  }
668
- const emotionCSS = _cachedEmotionCSS || (result.emotionCSS || []).join("\n");
810
+ const emotionCSS = Array.from(_accumulatedEmotionCSS).join("\n");
669
811
  const ds = data.designSystem || {};
670
812
  const globalCSS = await generateGlobalCSS(ds, data.config || data.settings);
671
813
  const fontLinks = generateFontLinks(ds);
@@ -674,7 +816,11 @@ const renderPage = async (data, route = "/", options = {}) => {
674
816
  if (isr && isr.clientScript) {
675
817
  const depth = route === "/" ? 0 : route.replace(/^\/|\/$/g, "").split("/").length;
676
818
  const prefix = depth > 0 ? "../".repeat(depth) : "./";
677
- isrBody = `<script type="module">
819
+ if (hydrate2) {
820
+ isrBody = `<script>window.__BRENDER__ = true<\/script>
821
+ <script type="module" src="${prefix}${isr.clientScript}"><\/script>`;
822
+ } else {
823
+ isrBody = `<script type="module">
678
824
  {
679
825
  const brEls = document.querySelectorAll('body > :not(script):not(style)')
680
826
  const observer = new MutationObserver((mutations) => {
@@ -692,11 +838,28 @@ const renderPage = async (data, route = "/", options = {}) => {
692
838
  }
693
839
  <\/script>
694
840
  <script type="module" src="${prefix}${isr.clientScript}"><\/script>`;
841
+ }
842
+ }
843
+ const headConfig = { ...data.config || {} };
844
+ if (data.polyglot && !headConfig.polyglot) headConfig.polyglot = data.polyglot;
845
+ const polyglotCfg = headConfig.polyglot;
846
+ let resolvedHeadTags = headTags;
847
+ if (polyglotCfg) {
848
+ const defaultLang = polyglotCfg.defaultLang || "en";
849
+ const translations = {
850
+ ...polyglotCfg.translations || {},
851
+ ...result.ssrTranslations || {}
852
+ };
853
+ const langMap = translations[defaultLang] || {};
854
+ resolvedHeadTags = headTags.replace(/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g, (match, key) => {
855
+ const trimmed = key.trim();
856
+ return langMap[trimmed] ?? match;
857
+ });
695
858
  }
696
859
  const html = `<!DOCTYPE html>
697
860
  <html lang="${htmlLang}">
698
861
  <head>
699
- ${headTags}
862
+ ${resolvedHeadTags}
700
863
  ${fontLinks}
701
864
  ${globalCSS.fontFaceCSS ? `<style>${globalCSS.fontFaceCSS}</style>` : ""}
702
865
  <style>
package/dist/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createEnv } from "./env.js";
2
2
  import { resetKeys, assignKeys, mapKeysToElements } from "./keys.js";
3
3
  import { loadProject, loadAndRenderAll } from "./load.js";
4
- import { render, renderElement, renderRoute, renderPage, resetGlobalCSSCache } from "./render.js";
4
+ import { render, renderElement, renderRoute, renderPage, resetGlobalCSSCache, getAccumulatedEmotionCSS, replaceEmotionCSS } from "./render.js";
5
5
  import { extractMetadata, generateHeadHtml } from "./metadata.js";
6
6
  import { collectBrNodes, hydrate } from "./hydrate.js";
7
7
  import { generateSitemap } from "./sitemap.js";
@@ -18,6 +18,8 @@ var index_default = {
18
18
  renderRoute,
19
19
  renderPage,
20
20
  resetGlobalCSSCache,
21
+ getAccumulatedEmotionCSS,
22
+ replaceEmotionCSS,
21
23
  extractMetadata,
22
24
  generateHeadHtml,
23
25
  collectBrNodes,
@@ -34,6 +36,7 @@ export {
34
36
  extractMetadata,
35
37
  generateHeadHtml,
36
38
  generateSitemap,
39
+ getAccumulatedEmotionCSS,
37
40
  hydrate,
38
41
  injectPrefetchedState,
39
42
  loadAndRenderAll,
@@ -44,6 +47,7 @@ export {
44
47
  renderElement,
45
48
  renderPage,
46
49
  renderRoute,
50
+ replaceEmotionCSS,
47
51
  resetGlobalCSSCache,
48
52
  resetKeys
49
53
  };
package/dist/esm/load.js CHANGED
@@ -70,17 +70,17 @@ const loadProject = async (projectPath) => {
70
70
  bundleAndImport(join(symbolsDir, "files", "index.js"))
71
71
  ]);
72
72
  return {
73
- app: appModule?.default || {},
74
- state: stateModule?.default || {},
75
- dependencies: depsModule?.default || {},
76
- components: componentsModule || {},
77
- snippets: snippetsModule || {},
78
- pages: pagesModule?.default || {},
79
- functions: functionsModule || {},
80
- methods: methodsModule || {},
81
- designSystem: designSystemModule?.default || {},
82
- files: filesModule?.default || {},
83
- config: configModule?.default || {}
73
+ app: { ...appModule?.default || {} },
74
+ state: { ...stateModule?.default || {} },
75
+ dependencies: { ...depsModule?.default || {} },
76
+ components: { ...componentsModule || {} },
77
+ snippets: { ...snippetsModule || {} },
78
+ pages: { ...pagesModule?.default || {} },
79
+ functions: { ...functionsModule || {} },
80
+ methods: { ...methodsModule || {} },
81
+ designSystem: { ...designSystemModule?.default || {} },
82
+ files: { ...filesModule?.default || {} },
83
+ config: { ...configModule?.default || {} }
84
84
  };
85
85
  };
86
86
  const loadAndRenderAll = async (projectPath, renderFn) => {