@webtypen/webframez-react 0.0.24 → 0.0.27

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/index.cjs CHANGED
@@ -183,6 +183,62 @@ var import_node_fs = __toESM(require("node:fs"), 1);
183
183
  var import_node_module2 = __toESM(require("node:module"), 1);
184
184
  var import_node_path2 = __toESM(require("node:path"), 1);
185
185
 
186
+ // src/head.ts
187
+ var ABSOLUTE_ASSET_URL_PATTERN = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;
188
+ function normalizeAssetsBaseUrl(value) {
189
+ const trimmed = value?.trim();
190
+ if (!trimmed) {
191
+ return void 0;
192
+ }
193
+ if (trimmed === "/") {
194
+ return "/";
195
+ }
196
+ return trimmed.replace(/\/+$/, "");
197
+ }
198
+ function resolveHeadAssetUrl(assetUrl, assetsBaseUrl) {
199
+ const normalizedAssetUrl = assetUrl.trim();
200
+ const normalizedAssetsBaseUrl = normalizeAssetsBaseUrl(assetsBaseUrl);
201
+ if (!normalizedAssetUrl || !normalizedAssetsBaseUrl) {
202
+ return normalizedAssetUrl;
203
+ }
204
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
205
+ return normalizedAssetUrl;
206
+ }
207
+ if (normalizedAssetsBaseUrl !== "/" && (normalizedAssetUrl === normalizedAssetsBaseUrl || normalizedAssetUrl.startsWith(`${normalizedAssetsBaseUrl}/`))) {
208
+ return normalizedAssetUrl;
209
+ }
210
+ if (normalizedAssetsBaseUrl === "/") {
211
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
212
+ }
213
+ if (normalizedAssetUrl.startsWith("/")) {
214
+ return `${normalizedAssetsBaseUrl}${normalizedAssetUrl}`;
215
+ }
216
+ return `${normalizedAssetsBaseUrl}/${normalizedAssetUrl}`;
217
+ }
218
+ function normalizeHeadConfig(head, inheritedAssetsBaseUrl) {
219
+ if (!head) {
220
+ return void 0;
221
+ }
222
+ const effectiveAssetsBaseUrl = normalizeAssetsBaseUrl(head.assetsBaseUrl) ?? normalizeAssetsBaseUrl(inheritedAssetsBaseUrl);
223
+ const normalizedHead = {
224
+ ...head,
225
+ ...effectiveAssetsBaseUrl ? { assetsBaseUrl: effectiveAssetsBaseUrl } : {}
226
+ };
227
+ if (normalizedHead.favicon) {
228
+ normalizedHead.favicon = resolveHeadAssetUrl(
229
+ normalizedHead.favicon,
230
+ effectiveAssetsBaseUrl
231
+ );
232
+ }
233
+ if (normalizedHead.links) {
234
+ normalizedHead.links = normalizedHead.links.map((link) => ({
235
+ ...link,
236
+ href: resolveHeadAssetUrl(link.href, effectiveAssetsBaseUrl)
237
+ }));
238
+ }
239
+ return normalizedHead;
240
+ }
241
+
186
242
  // src/router-runtime.tsx
187
243
  var import_react = __toESM(require("react"), 1);
188
244
  var ROUTE_CHILDREN_TAG = "webframez-route-children";
@@ -398,7 +454,8 @@ function mergeHead(...configs) {
398
454
  meta: [],
399
455
  links: []
400
456
  };
401
- for (const config of configs) {
457
+ for (const candidate of configs) {
458
+ const config = normalizeHeadConfig(candidate, merged.assetsBaseUrl);
402
459
  if (!config) {
403
460
  continue;
404
461
  }
@@ -408,6 +465,9 @@ function mergeHead(...configs) {
408
465
  if (config.description) {
409
466
  merged.description = config.description;
410
467
  }
468
+ if (config.assetsBaseUrl) {
469
+ merged.assetsBaseUrl = config.assetsBaseUrl;
470
+ }
411
471
  if (config.favicon) {
412
472
  merged.favicon = config.favicon;
413
473
  }
@@ -421,22 +481,23 @@ function mergeHead(...configs) {
421
481
  return merged;
422
482
  }
423
483
  function renderHeadToString(head) {
484
+ const normalizedHead = normalizeHeadConfig(head) ?? head;
424
485
  const tags = [];
425
- if (head.description) {
486
+ if (normalizedHead.description) {
426
487
  tags.push(
427
- `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
488
+ `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(normalizedHead.description)}" />`
428
489
  );
429
490
  }
430
- if (head.favicon) {
491
+ if (normalizedHead.favicon) {
431
492
  tags.push(
432
- `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
493
+ `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(normalizedHead.favicon)}" />`
433
494
  );
434
495
  }
435
- for (const meta of head.meta ?? []) {
496
+ for (const meta of normalizedHead.meta ?? []) {
436
497
  const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
437
498
  tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
438
499
  }
439
- for (const link of head.links ?? []) {
500
+ for (const link of normalizedHead.links ?? []) {
440
501
  const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
441
502
  tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
442
503
  }
@@ -769,7 +830,21 @@ globalThis.__webpack_require__ = function __webframezNodeRequire(id) {
769
830
  }
770
831
 
771
832
  if (id.startsWith("./")) {
772
- return require(path.resolve(process.cwd(), id.slice(2)));
833
+ const relativeId = id.slice(2);
834
+ const candidates = [
835
+ path.resolve(process.cwd(), relativeId),
836
+ path.resolve(process.cwd(), "..", relativeId)
837
+ ];
838
+ for (const candidate of candidates) {
839
+ try {
840
+ return require(candidate);
841
+ } catch (error) {
842
+ const missingCandidate = error && error.code === "MODULE_NOT_FOUND" && typeof error.message === "string" && error.message.includes("'" + candidate + "'");
843
+ if (!missingCandidate) {
844
+ throw error;
845
+ }
846
+ }
847
+ }
773
848
  }
774
849
 
775
850
  return require(id);
@@ -1073,22 +1148,64 @@ function normalizeClientManifest(manifest, options) {
1073
1148
  }
1074
1149
  function createServerConsumerManifest(manifest) {
1075
1150
  const consumerManifest = {};
1151
+ const normalizeWorkerModuleId = (requestKey, rawModuleId) => {
1152
+ if (typeof rawModuleId === "string" && rawModuleId.trim() !== "") {
1153
+ return rawModuleId;
1154
+ }
1155
+ if (typeof rawModuleId !== "number" || !Number.isFinite(rawModuleId)) {
1156
+ return null;
1157
+ }
1158
+ if (!requestKey || requestKey.trim() === "") {
1159
+ return String(rawModuleId);
1160
+ }
1161
+ if (requestKey.startsWith("file://")) {
1162
+ try {
1163
+ return (0, import_node_url.fileURLToPath)(requestKey);
1164
+ } catch {
1165
+ return String(rawModuleId);
1166
+ }
1167
+ }
1168
+ if (requestKey.startsWith("/")) {
1169
+ return requestKey;
1170
+ }
1171
+ if (requestKey.startsWith("./")) {
1172
+ return requestKey;
1173
+ }
1174
+ if (requestKey.startsWith("node_modules/") || requestKey.startsWith("build/")) {
1175
+ return `./${requestKey}`;
1176
+ }
1177
+ return requestKey;
1178
+ };
1179
+ const addReference = (lookupKey, reference) => {
1180
+ if (!lookupKey || lookupKey.trim() === "") {
1181
+ return;
1182
+ }
1183
+ if (lookupKey in consumerManifest) {
1184
+ return;
1185
+ }
1186
+ consumerManifest[lookupKey] = {
1187
+ "*": reference
1188
+ };
1189
+ };
1076
1190
  for (const [key, value] of Object.entries(manifest)) {
1077
1191
  if (!value || typeof value !== "object") {
1078
1192
  continue;
1079
1193
  }
1080
1194
  const entry = value;
1081
- if (typeof entry.id !== "string" || entry.id.trim() === "") {
1195
+ const workerModuleId = normalizeWorkerModuleId(key, entry.id);
1196
+ if (!workerModuleId) {
1082
1197
  continue;
1083
1198
  }
1084
- consumerManifest[key] = {
1085
- "*": {
1086
- id: entry.id,
1087
- chunks: [],
1088
- name: entry.name ?? "*",
1089
- ...entry.async ? { async: entry.async } : {}
1090
- }
1199
+ const reference = {
1200
+ id: workerModuleId,
1201
+ chunks: Array.isArray(entry.chunks) ? entry.chunks : [],
1202
+ name: entry.name ?? "*",
1203
+ ...entry.async ? { async: entry.async } : {}
1091
1204
  };
1205
+ addReference(key, reference);
1206
+ if (typeof entry.id === "number" && Number.isFinite(entry.id)) {
1207
+ addReference(String(entry.id), reference);
1208
+ }
1092
1209
  }
1093
1210
  return consumerManifest;
1094
1211
  }
package/dist/index.js CHANGED
@@ -147,6 +147,62 @@ import fs from "node:fs";
147
147
  import Module from "node:module";
148
148
  import path2 from "node:path";
149
149
 
150
+ // src/head.ts
151
+ var ABSOLUTE_ASSET_URL_PATTERN = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;
152
+ function normalizeAssetsBaseUrl(value) {
153
+ const trimmed = value?.trim();
154
+ if (!trimmed) {
155
+ return void 0;
156
+ }
157
+ if (trimmed === "/") {
158
+ return "/";
159
+ }
160
+ return trimmed.replace(/\/+$/, "");
161
+ }
162
+ function resolveHeadAssetUrl(assetUrl, assetsBaseUrl) {
163
+ const normalizedAssetUrl = assetUrl.trim();
164
+ const normalizedAssetsBaseUrl = normalizeAssetsBaseUrl(assetsBaseUrl);
165
+ if (!normalizedAssetUrl || !normalizedAssetsBaseUrl) {
166
+ return normalizedAssetUrl;
167
+ }
168
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
169
+ return normalizedAssetUrl;
170
+ }
171
+ if (normalizedAssetsBaseUrl !== "/" && (normalizedAssetUrl === normalizedAssetsBaseUrl || normalizedAssetUrl.startsWith(`${normalizedAssetsBaseUrl}/`))) {
172
+ return normalizedAssetUrl;
173
+ }
174
+ if (normalizedAssetsBaseUrl === "/") {
175
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
176
+ }
177
+ if (normalizedAssetUrl.startsWith("/")) {
178
+ return `${normalizedAssetsBaseUrl}${normalizedAssetUrl}`;
179
+ }
180
+ return `${normalizedAssetsBaseUrl}/${normalizedAssetUrl}`;
181
+ }
182
+ function normalizeHeadConfig(head, inheritedAssetsBaseUrl) {
183
+ if (!head) {
184
+ return void 0;
185
+ }
186
+ const effectiveAssetsBaseUrl = normalizeAssetsBaseUrl(head.assetsBaseUrl) ?? normalizeAssetsBaseUrl(inheritedAssetsBaseUrl);
187
+ const normalizedHead = {
188
+ ...head,
189
+ ...effectiveAssetsBaseUrl ? { assetsBaseUrl: effectiveAssetsBaseUrl } : {}
190
+ };
191
+ if (normalizedHead.favicon) {
192
+ normalizedHead.favicon = resolveHeadAssetUrl(
193
+ normalizedHead.favicon,
194
+ effectiveAssetsBaseUrl
195
+ );
196
+ }
197
+ if (normalizedHead.links) {
198
+ normalizedHead.links = normalizedHead.links.map((link) => ({
199
+ ...link,
200
+ href: resolveHeadAssetUrl(link.href, effectiveAssetsBaseUrl)
201
+ }));
202
+ }
203
+ return normalizedHead;
204
+ }
205
+
150
206
  // src/router-runtime.tsx
151
207
  import React from "react";
152
208
  var ROUTE_CHILDREN_TAG = "webframez-route-children";
@@ -362,7 +418,8 @@ function mergeHead(...configs) {
362
418
  meta: [],
363
419
  links: []
364
420
  };
365
- for (const config of configs) {
421
+ for (const candidate of configs) {
422
+ const config = normalizeHeadConfig(candidate, merged.assetsBaseUrl);
366
423
  if (!config) {
367
424
  continue;
368
425
  }
@@ -372,6 +429,9 @@ function mergeHead(...configs) {
372
429
  if (config.description) {
373
430
  merged.description = config.description;
374
431
  }
432
+ if (config.assetsBaseUrl) {
433
+ merged.assetsBaseUrl = config.assetsBaseUrl;
434
+ }
375
435
  if (config.favicon) {
376
436
  merged.favicon = config.favicon;
377
437
  }
@@ -385,22 +445,23 @@ function mergeHead(...configs) {
385
445
  return merged;
386
446
  }
387
447
  function renderHeadToString(head) {
448
+ const normalizedHead = normalizeHeadConfig(head) ?? head;
388
449
  const tags = [];
389
- if (head.description) {
450
+ if (normalizedHead.description) {
390
451
  tags.push(
391
- `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
452
+ `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(normalizedHead.description)}" />`
392
453
  );
393
454
  }
394
- if (head.favicon) {
455
+ if (normalizedHead.favicon) {
395
456
  tags.push(
396
- `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
457
+ `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(normalizedHead.favicon)}" />`
397
458
  );
398
459
  }
399
- for (const meta of head.meta ?? []) {
460
+ for (const meta of normalizedHead.meta ?? []) {
400
461
  const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
401
462
  tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
402
463
  }
403
- for (const link of head.links ?? []) {
464
+ for (const link of normalizedHead.links ?? []) {
404
465
  const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
405
466
  tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
406
467
  }
@@ -733,7 +794,21 @@ globalThis.__webpack_require__ = function __webframezNodeRequire(id) {
733
794
  }
734
795
 
735
796
  if (id.startsWith("./")) {
736
- return require(path.resolve(process.cwd(), id.slice(2)));
797
+ const relativeId = id.slice(2);
798
+ const candidates = [
799
+ path.resolve(process.cwd(), relativeId),
800
+ path.resolve(process.cwd(), "..", relativeId)
801
+ ];
802
+ for (const candidate of candidates) {
803
+ try {
804
+ return require(candidate);
805
+ } catch (error) {
806
+ const missingCandidate = error && error.code === "MODULE_NOT_FOUND" && typeof error.message === "string" && error.message.includes("'" + candidate + "'");
807
+ if (!missingCandidate) {
808
+ throw error;
809
+ }
810
+ }
811
+ }
737
812
  }
738
813
 
739
814
  return require(id);
@@ -1037,22 +1112,64 @@ function normalizeClientManifest(manifest, options) {
1037
1112
  }
1038
1113
  function createServerConsumerManifest(manifest) {
1039
1114
  const consumerManifest = {};
1115
+ const normalizeWorkerModuleId = (requestKey, rawModuleId) => {
1116
+ if (typeof rawModuleId === "string" && rawModuleId.trim() !== "") {
1117
+ return rawModuleId;
1118
+ }
1119
+ if (typeof rawModuleId !== "number" || !Number.isFinite(rawModuleId)) {
1120
+ return null;
1121
+ }
1122
+ if (!requestKey || requestKey.trim() === "") {
1123
+ return String(rawModuleId);
1124
+ }
1125
+ if (requestKey.startsWith("file://")) {
1126
+ try {
1127
+ return fileURLToPath(requestKey);
1128
+ } catch {
1129
+ return String(rawModuleId);
1130
+ }
1131
+ }
1132
+ if (requestKey.startsWith("/")) {
1133
+ return requestKey;
1134
+ }
1135
+ if (requestKey.startsWith("./")) {
1136
+ return requestKey;
1137
+ }
1138
+ if (requestKey.startsWith("node_modules/") || requestKey.startsWith("build/")) {
1139
+ return `./${requestKey}`;
1140
+ }
1141
+ return requestKey;
1142
+ };
1143
+ const addReference = (lookupKey, reference) => {
1144
+ if (!lookupKey || lookupKey.trim() === "") {
1145
+ return;
1146
+ }
1147
+ if (lookupKey in consumerManifest) {
1148
+ return;
1149
+ }
1150
+ consumerManifest[lookupKey] = {
1151
+ "*": reference
1152
+ };
1153
+ };
1040
1154
  for (const [key, value] of Object.entries(manifest)) {
1041
1155
  if (!value || typeof value !== "object") {
1042
1156
  continue;
1043
1157
  }
1044
1158
  const entry = value;
1045
- if (typeof entry.id !== "string" || entry.id.trim() === "") {
1159
+ const workerModuleId = normalizeWorkerModuleId(key, entry.id);
1160
+ if (!workerModuleId) {
1046
1161
  continue;
1047
1162
  }
1048
- consumerManifest[key] = {
1049
- "*": {
1050
- id: entry.id,
1051
- chunks: [],
1052
- name: entry.name ?? "*",
1053
- ...entry.async ? { async: entry.async } : {}
1054
- }
1163
+ const reference = {
1164
+ id: workerModuleId,
1165
+ chunks: Array.isArray(entry.chunks) ? entry.chunks : [],
1166
+ name: entry.name ?? "*",
1167
+ ...entry.async ? { async: entry.async } : {}
1055
1168
  };
1169
+ addReference(key, reference);
1170
+ if (typeof entry.id === "number" && Number.isFinite(entry.id)) {
1171
+ addReference(String(entry.id), reference);
1172
+ }
1056
1173
  }
1057
1174
  return consumerManifest;
1058
1175
  }
package/dist/router.cjs CHANGED
@@ -41,6 +41,62 @@ var import_node_fs = __toESM(require("node:fs"), 1);
41
41
  var import_node_module = __toESM(require("node:module"), 1);
42
42
  var import_node_path = __toESM(require("node:path"), 1);
43
43
 
44
+ // src/head.ts
45
+ var ABSOLUTE_ASSET_URL_PATTERN = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;
46
+ function normalizeAssetsBaseUrl(value) {
47
+ const trimmed = value?.trim();
48
+ if (!trimmed) {
49
+ return void 0;
50
+ }
51
+ if (trimmed === "/") {
52
+ return "/";
53
+ }
54
+ return trimmed.replace(/\/+$/, "");
55
+ }
56
+ function resolveHeadAssetUrl(assetUrl, assetsBaseUrl) {
57
+ const normalizedAssetUrl = assetUrl.trim();
58
+ const normalizedAssetsBaseUrl = normalizeAssetsBaseUrl(assetsBaseUrl);
59
+ if (!normalizedAssetUrl || !normalizedAssetsBaseUrl) {
60
+ return normalizedAssetUrl;
61
+ }
62
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
63
+ return normalizedAssetUrl;
64
+ }
65
+ if (normalizedAssetsBaseUrl !== "/" && (normalizedAssetUrl === normalizedAssetsBaseUrl || normalizedAssetUrl.startsWith(`${normalizedAssetsBaseUrl}/`))) {
66
+ return normalizedAssetUrl;
67
+ }
68
+ if (normalizedAssetsBaseUrl === "/") {
69
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
70
+ }
71
+ if (normalizedAssetUrl.startsWith("/")) {
72
+ return `${normalizedAssetsBaseUrl}${normalizedAssetUrl}`;
73
+ }
74
+ return `${normalizedAssetsBaseUrl}/${normalizedAssetUrl}`;
75
+ }
76
+ function normalizeHeadConfig(head, inheritedAssetsBaseUrl) {
77
+ if (!head) {
78
+ return void 0;
79
+ }
80
+ const effectiveAssetsBaseUrl = normalizeAssetsBaseUrl(head.assetsBaseUrl) ?? normalizeAssetsBaseUrl(inheritedAssetsBaseUrl);
81
+ const normalizedHead = {
82
+ ...head,
83
+ ...effectiveAssetsBaseUrl ? { assetsBaseUrl: effectiveAssetsBaseUrl } : {}
84
+ };
85
+ if (normalizedHead.favicon) {
86
+ normalizedHead.favicon = resolveHeadAssetUrl(
87
+ normalizedHead.favicon,
88
+ effectiveAssetsBaseUrl
89
+ );
90
+ }
91
+ if (normalizedHead.links) {
92
+ normalizedHead.links = normalizedHead.links.map((link) => ({
93
+ ...link,
94
+ href: resolveHeadAssetUrl(link.href, effectiveAssetsBaseUrl)
95
+ }));
96
+ }
97
+ return normalizedHead;
98
+ }
99
+
44
100
  // src/router-runtime.tsx
45
101
  var import_react = __toESM(require("react"), 1);
46
102
  var ROUTE_CHILDREN_TAG = "webframez-route-children";
@@ -256,7 +312,8 @@ function mergeHead(...configs) {
256
312
  meta: [],
257
313
  links: []
258
314
  };
259
- for (const config of configs) {
315
+ for (const candidate of configs) {
316
+ const config = normalizeHeadConfig(candidate, merged.assetsBaseUrl);
260
317
  if (!config) {
261
318
  continue;
262
319
  }
@@ -266,6 +323,9 @@ function mergeHead(...configs) {
266
323
  if (config.description) {
267
324
  merged.description = config.description;
268
325
  }
326
+ if (config.assetsBaseUrl) {
327
+ merged.assetsBaseUrl = config.assetsBaseUrl;
328
+ }
269
329
  if (config.favicon) {
270
330
  merged.favicon = config.favicon;
271
331
  }
@@ -279,22 +339,23 @@ function mergeHead(...configs) {
279
339
  return merged;
280
340
  }
281
341
  function renderHeadToString(head) {
342
+ const normalizedHead = normalizeHeadConfig(head) ?? head;
282
343
  const tags = [];
283
- if (head.description) {
344
+ if (normalizedHead.description) {
284
345
  tags.push(
285
- `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
346
+ `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(normalizedHead.description)}" />`
286
347
  );
287
348
  }
288
- if (head.favicon) {
349
+ if (normalizedHead.favicon) {
289
350
  tags.push(
290
- `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
351
+ `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(normalizedHead.favicon)}" />`
291
352
  );
292
353
  }
293
- for (const meta of head.meta ?? []) {
354
+ for (const meta of normalizedHead.meta ?? []) {
294
355
  const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
295
356
  tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
296
357
  }
297
- for (const link of head.links ?? []) {
358
+ for (const link of normalizedHead.links ?? []) {
298
359
  const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
299
360
  tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
300
361
  }
package/dist/router.js CHANGED
@@ -11,6 +11,62 @@ import fs from "node:fs";
11
11
  import Module from "node:module";
12
12
  import path from "node:path";
13
13
 
14
+ // src/head.ts
15
+ var ABSOLUTE_ASSET_URL_PATTERN = /^(?:[a-zA-Z][a-zA-Z\d+\-.]*:|\/\/|#)/;
16
+ function normalizeAssetsBaseUrl(value) {
17
+ const trimmed = value?.trim();
18
+ if (!trimmed) {
19
+ return void 0;
20
+ }
21
+ if (trimmed === "/") {
22
+ return "/";
23
+ }
24
+ return trimmed.replace(/\/+$/, "");
25
+ }
26
+ function resolveHeadAssetUrl(assetUrl, assetsBaseUrl) {
27
+ const normalizedAssetUrl = assetUrl.trim();
28
+ const normalizedAssetsBaseUrl = normalizeAssetsBaseUrl(assetsBaseUrl);
29
+ if (!normalizedAssetUrl || !normalizedAssetsBaseUrl) {
30
+ return normalizedAssetUrl;
31
+ }
32
+ if (ABSOLUTE_ASSET_URL_PATTERN.test(normalizedAssetUrl)) {
33
+ return normalizedAssetUrl;
34
+ }
35
+ if (normalizedAssetsBaseUrl !== "/" && (normalizedAssetUrl === normalizedAssetsBaseUrl || normalizedAssetUrl.startsWith(`${normalizedAssetsBaseUrl}/`))) {
36
+ return normalizedAssetUrl;
37
+ }
38
+ if (normalizedAssetsBaseUrl === "/") {
39
+ return normalizedAssetUrl.startsWith("/") ? normalizedAssetUrl : `/${normalizedAssetUrl}`;
40
+ }
41
+ if (normalizedAssetUrl.startsWith("/")) {
42
+ return `${normalizedAssetsBaseUrl}${normalizedAssetUrl}`;
43
+ }
44
+ return `${normalizedAssetsBaseUrl}/${normalizedAssetUrl}`;
45
+ }
46
+ function normalizeHeadConfig(head, inheritedAssetsBaseUrl) {
47
+ if (!head) {
48
+ return void 0;
49
+ }
50
+ const effectiveAssetsBaseUrl = normalizeAssetsBaseUrl(head.assetsBaseUrl) ?? normalizeAssetsBaseUrl(inheritedAssetsBaseUrl);
51
+ const normalizedHead = {
52
+ ...head,
53
+ ...effectiveAssetsBaseUrl ? { assetsBaseUrl: effectiveAssetsBaseUrl } : {}
54
+ };
55
+ if (normalizedHead.favicon) {
56
+ normalizedHead.favicon = resolveHeadAssetUrl(
57
+ normalizedHead.favicon,
58
+ effectiveAssetsBaseUrl
59
+ );
60
+ }
61
+ if (normalizedHead.links) {
62
+ normalizedHead.links = normalizedHead.links.map((link) => ({
63
+ ...link,
64
+ href: resolveHeadAssetUrl(link.href, effectiveAssetsBaseUrl)
65
+ }));
66
+ }
67
+ return normalizedHead;
68
+ }
69
+
14
70
  // src/router-runtime.tsx
15
71
  import React from "react";
16
72
  var ROUTE_CHILDREN_TAG = "webframez-route-children";
@@ -226,7 +282,8 @@ function mergeHead(...configs) {
226
282
  meta: [],
227
283
  links: []
228
284
  };
229
- for (const config of configs) {
285
+ for (const candidate of configs) {
286
+ const config = normalizeHeadConfig(candidate, merged.assetsBaseUrl);
230
287
  if (!config) {
231
288
  continue;
232
289
  }
@@ -236,6 +293,9 @@ function mergeHead(...configs) {
236
293
  if (config.description) {
237
294
  merged.description = config.description;
238
295
  }
296
+ if (config.assetsBaseUrl) {
297
+ merged.assetsBaseUrl = config.assetsBaseUrl;
298
+ }
239
299
  if (config.favicon) {
240
300
  merged.favicon = config.favicon;
241
301
  }
@@ -249,22 +309,23 @@ function mergeHead(...configs) {
249
309
  return merged;
250
310
  }
251
311
  function renderHeadToString(head) {
312
+ const normalizedHead = normalizeHeadConfig(head) ?? head;
252
313
  const tags = [];
253
- if (head.description) {
314
+ if (normalizedHead.description) {
254
315
  tags.push(
255
- `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(head.description)}" />`
316
+ `<meta ${createManagedAttributes()} name="description" content="${escapeHtml(normalizedHead.description)}" />`
256
317
  );
257
318
  }
258
- if (head.favicon) {
319
+ if (normalizedHead.favicon) {
259
320
  tags.push(
260
- `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(head.favicon)}" />`
321
+ `<link ${createManagedAttributes()} rel="icon" href="${escapeHtml(normalizedHead.favicon)}" />`
261
322
  );
262
323
  }
263
- for (const meta of head.meta ?? []) {
324
+ for (const meta of normalizedHead.meta ?? []) {
264
325
  const attrs = Object.entries(meta).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
265
326
  tags.push(`<meta ${createManagedAttributes()} ${attrs} />`);
266
327
  }
267
- for (const link of head.links ?? []) {
328
+ for (const link of normalizedHead.links ?? []) {
268
329
  const attrs = Object.entries(link).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}="${escapeHtml(String(value))}"`).join(" ");
269
330
  tags.push(`<link ${createManagedAttributes()} ${attrs} />`);
270
331
  }
package/dist/types.d.ts CHANGED
@@ -9,7 +9,7 @@ export type AbortRouteOptions = {
9
9
  payload?: unknown;
10
10
  };
11
11
 
12
- export type RouteContext<TData = unknown> = {
12
+ export type RouteContext<TData = any> = {
13
13
  pathname: string;
14
14
  params: RouteParams;
15
15
  searchParams: RouteSearchParams;
@@ -22,11 +22,11 @@ export type InferPageData<TDataResolver> =
22
22
  TDataResolver extends (...args: any[]) => any
23
23
  ? Awaited<ReturnType<TDataResolver>>
24
24
  : TDataResolver;
25
- export type PageProps<TData = unknown> = RouteContext<InferPageData<TData>>;
25
+ export type PageProps<TData = any> = RouteContext<InferPageData<TData>>;
26
26
  export type PagePropsFromData<TDataResolver extends (...args: any[]) => any> =
27
27
  PageProps<InferPageData<TDataResolver>>;
28
28
 
29
- export type ErrorPageProps<TData = unknown> = RouteContext<TData> & {
29
+ export type ErrorPageProps<TData = any> = RouteContext<TData> & {
30
30
  statusCode: number;
31
31
  message: string;
32
32
  payload?: unknown;
@@ -51,6 +51,7 @@ export type HeadLinkTag = {
51
51
  export type HeadConfig = {
52
52
  title?: string;
53
53
  description?: string;
54
+ assetsBaseUrl?: string;
54
55
  favicon?: string;
55
56
  meta?: HeadMetaTag[];
56
57
  links?: HeadLinkTag[];
@@ -65,18 +66,18 @@ export type HeadResolver<TContext = RouteContext> = (
65
66
  context: TContext
66
67
  ) => HeadConfig | Promise<HeadConfig>;
67
68
 
68
- export type PageDataResolver<TData = unknown> = (
69
+ export type PageDataResolver<TData = any> = (
69
70
  context: RouteContext
70
71
  ) => TData | Promise<TData>;
71
72
 
72
73
  export type RouteMiddlewareResult = Record<string, unknown> | undefined | void;
73
- export type RouteMiddlewareResolver<TData = unknown> = (
74
+ export type RouteMiddlewareResolver<TData = any> = (
74
75
  context: RouteContext<TData>
75
76
  ) => RouteMiddlewareResult | Promise<RouteMiddlewareResult>;
76
77
  export type RouteMiddlewareRegistry = Record<string, RouteMiddlewareResolver<any>>;
77
78
  export type RouteMiddlewareConfig = string | string[];
78
79
 
79
- export type PageModule<TData = unknown> = {
80
+ export type PageModule<TData = any> = {
80
81
  default: (props: PageProps<TData>) => ReactNode | Promise<ReactNode>;
81
82
  Head?: HeadResolver<PageProps<TData>>;
82
83
  Data?: PageDataResolver<TData>;