@blinkk/root 1.0.0-rc.1 → 1.0.0-rc.11

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/render.js CHANGED
@@ -203,6 +203,46 @@ function parseAcceptLanguage(value) {
203
203
 
204
204
  // src/render/i18n-fallbacks.ts
205
205
  var UNKNOWN_COUNTRY = "zz";
206
+ var ES_419_COUNTRIES = [
207
+ "ar",
208
+ // Argentina
209
+ "bo",
210
+ // Bolivia
211
+ "cl",
212
+ // Chile
213
+ "co",
214
+ // Colombia
215
+ "cr",
216
+ // Costa Rica
217
+ "cu",
218
+ // Cuba
219
+ "do",
220
+ // Dominican Republic
221
+ "ec",
222
+ // Ecuador
223
+ "sv",
224
+ // El Salvador
225
+ "gt",
226
+ // Guatemala
227
+ "hn",
228
+ // Honduras
229
+ "mx",
230
+ // Mexico
231
+ "ni",
232
+ // Nicaragua
233
+ "pa",
234
+ // Panama
235
+ "py",
236
+ // Paraguay
237
+ "pe",
238
+ // Peru
239
+ "pr",
240
+ // Puerto Rico
241
+ "uy",
242
+ // Uruguay
243
+ "ve"
244
+ // Venezuela
245
+ ];
206
246
  function getFallbackLocales(req) {
207
247
  var _a, _b;
208
248
  const hl = getFirstQueryParam(req, "hl");
@@ -226,9 +266,14 @@ function getFallbackLocales(req) {
226
266
  locales.add(`${langCode}_${countryCode}`);
227
267
  });
228
268
  locales.add(`ALL_${countryCode}`);
269
+ const isEs419Country = test419Country(countryCode);
229
270
  langs.forEach((langCode) => {
230
271
  locales.add(`${langCode}_ALL`);
231
272
  locales.add(langCode);
273
+ if (langCode === "es" && isEs419Country) {
274
+ locales.add("es-419_ALL");
275
+ locales.add("es-419");
276
+ }
232
277
  });
233
278
  return Array.from(locales);
234
279
  }
@@ -251,6 +296,9 @@ function getFallbackLanguages(req) {
251
296
  parseAcceptLanguage(acceptLangHeader).forEach((lang) => {
252
297
  if (lang.region) {
253
298
  langs.add(`${lang.code}-${lang.region}`);
299
+ if (lang.code === "es" && test419Country(lang.region)) {
300
+ langs.add("es-419");
301
+ }
254
302
  }
255
303
  langs.add(lang.code);
256
304
  });
@@ -279,6 +327,9 @@ function isWebCrawler(req) {
279
327
  const userAgent = userAgentHeader.toLowerCase();
280
328
  return userAgent.includes("googlebot") || userAgent.includes("bingbot") || userAgent.includes("twitterbot");
281
329
  }
330
+ function test419Country(countryCode) {
331
+ return ES_419_COUNTRIES.includes(countryCode);
332
+ }
282
333
 
283
334
  // src/render/router.ts
284
335
  import path from "node:path";
@@ -298,21 +349,26 @@ var RouteTrie = class _RouteTrie {
298
349
  return;
299
350
  }
300
351
  const [head, tail] = this.splitPath(path2);
352
+ if (head.startsWith("[[...") && head.endsWith("]]")) {
353
+ const paramName = head.slice(5, -2);
354
+ this.optCatchAllNodes = new CatchAllNode(paramName, route);
355
+ return;
356
+ }
301
357
  if (head.startsWith("[...") && head.endsWith("]")) {
302
358
  const paramName = head.slice(4, -1);
303
- this.wildcardChild = new WildcardChild(paramName, route);
359
+ this.catchAllNodes = new CatchAllNode(paramName, route);
304
360
  return;
305
361
  }
306
362
  let nextNode;
307
363
  if (head.startsWith("[") && head.endsWith("]")) {
308
- if (!this.paramChildren) {
309
- this.paramChildren = {};
364
+ if (!this.paramNodes) {
365
+ this.paramNodes = {};
310
366
  }
311
367
  const paramName = head.slice(1, -1);
312
- if (!this.paramChildren[paramName]) {
313
- this.paramChildren[paramName] = new ParamChild(paramName);
368
+ if (!this.paramNodes[paramName]) {
369
+ this.paramNodes[paramName] = new ParamNode(paramName);
314
370
  }
315
- nextNode = this.paramChildren[paramName].trie;
371
+ nextNode = this.paramNodes[paramName].trie;
316
372
  } else {
317
373
  nextNode = this.children[head];
318
374
  if (!nextNode) {
@@ -344,8 +400,8 @@ var RouteTrie = class _RouteTrie {
344
400
  if (this.route) {
345
401
  addPromise(cb("/", this.route));
346
402
  }
347
- if (this.paramChildren) {
348
- Object.values(this.paramChildren).forEach((paramChild) => {
403
+ if (this.paramNodes) {
404
+ Object.values(this.paramNodes).forEach((paramChild) => {
349
405
  const param = `[${paramChild.name}]`;
350
406
  paramChild.trie.walk((childPath, route) => {
351
407
  const paramUrlPath = `/${param}${childPath}`;
@@ -353,9 +409,13 @@ var RouteTrie = class _RouteTrie {
353
409
  });
354
410
  });
355
411
  }
356
- if (this.wildcardChild) {
357
- const wildcardUrlPath = `/[...${this.wildcardChild.name}]`;
358
- addPromise(cb(wildcardUrlPath, this.wildcardChild.route));
412
+ if (this.catchAllNodes) {
413
+ const wildcardUrlPath = `/[...${this.catchAllNodes.name}]`;
414
+ addPromise(cb(wildcardUrlPath, this.catchAllNodes.route));
415
+ }
416
+ if (this.optCatchAllNodes) {
417
+ const wildcardUrlPath = `/[[...${this.optCatchAllNodes.name}]]`;
418
+ addPromise(cb(wildcardUrlPath, this.optCatchAllNodes.route));
359
419
  }
360
420
  for (const subpath of Object.keys(this.children)) {
361
421
  const childTrie = this.children[subpath];
@@ -371,14 +431,24 @@ var RouteTrie = class _RouteTrie {
371
431
  */
372
432
  clear() {
373
433
  this.children = {};
374
- this.paramChildren = void 0;
375
- this.wildcardChild = void 0;
434
+ this.paramNodes = void 0;
435
+ this.catchAllNodes = void 0;
436
+ this.optCatchAllNodes = void 0;
376
437
  this.route = void 0;
377
438
  }
378
439
  getRoute(urlPath, params) {
379
440
  urlPath = this.normalizePath(urlPath);
380
441
  if (urlPath === "") {
381
- return this.route;
442
+ if (this.route) {
443
+ return this.route;
444
+ }
445
+ if (this.optCatchAllNodes) {
446
+ if (urlPath) {
447
+ params[this.optCatchAllNodes.name] = urlPath;
448
+ }
449
+ return this.optCatchAllNodes.route;
450
+ }
451
+ return void 0;
382
452
  }
383
453
  const [head, tail] = this.splitPath(urlPath);
384
454
  const child = this.children[head];
@@ -388,8 +458,8 @@ var RouteTrie = class _RouteTrie {
388
458
  return route;
389
459
  }
390
460
  }
391
- if (this.paramChildren) {
392
- for (const paramChild of Object.values(this.paramChildren)) {
461
+ if (this.paramNodes) {
462
+ for (const paramChild of Object.values(this.paramNodes)) {
393
463
  const route = paramChild.trie.getRoute(tail, params);
394
464
  if (route) {
395
465
  params[paramChild.name] = head;
@@ -397,9 +467,13 @@ var RouteTrie = class _RouteTrie {
397
467
  }
398
468
  }
399
469
  }
400
- if (this.wildcardChild) {
401
- params[this.wildcardChild.name] = urlPath;
402
- return this.wildcardChild.route;
470
+ if (this.catchAllNodes) {
471
+ params[this.catchAllNodes.name] = urlPath;
472
+ return this.catchAllNodes.route;
473
+ }
474
+ if (this.optCatchAllNodes) {
475
+ params[this.optCatchAllNodes.name] = urlPath;
476
+ return this.optCatchAllNodes.route;
403
477
  }
404
478
  return void 0;
405
479
  }
@@ -407,7 +481,7 @@ var RouteTrie = class _RouteTrie {
407
481
  * Normalizes a path for inclusion into the route trie.
408
482
  */
409
483
  normalizePath(path2) {
410
- return path2.replace(/^\/+/g, "");
484
+ return path2.replace(/^\/+/g, "").replace(/\/+$/g, "");
411
485
  }
412
486
  /**
413
487
  * Splits the parent directory from its children, e.g.:
@@ -422,13 +496,13 @@ var RouteTrie = class _RouteTrie {
422
496
  return [path2.slice(0, i), path2.slice(i + 1)];
423
497
  }
424
498
  };
425
- var ParamChild = class {
499
+ var ParamNode = class {
426
500
  constructor(name) {
427
501
  this.trie = new RouteTrie();
428
502
  this.name = name;
429
503
  }
430
504
  };
431
- var WildcardChild = class {
505
+ var CatchAllNode = class {
432
506
  constructor(name, route) {
433
507
  this.name = name;
434
508
  this.route = route;
@@ -436,11 +510,11 @@ var WildcardChild = class {
436
510
  };
437
511
 
438
512
  // src/render/router.ts
439
- function getRoutes(config) {
440
- var _a, _b, _c;
441
- const locales = ((_a = config.i18n) == null ? void 0 : _a.locales) || [];
442
- const i18nUrlFormat = ((_b = config.i18n) == null ? void 0 : _b.urlFormat) || "/{locale}/{path}";
443
- const defaultLocale = ((_c = config.i18n) == null ? void 0 : _c.defaultLocale) || "en";
513
+ function getRoutes(rootConfig) {
514
+ var _a, _b;
515
+ const locales = ((_a = rootConfig.i18n) == null ? void 0 : _a.locales) || [];
516
+ const basePath = rootConfig.base || "/";
517
+ const defaultLocale = ((_b = rootConfig.i18n) == null ? void 0 : _b.defaultLocale) || "en";
444
518
  const routes = import.meta.glob(
445
519
  ["/routes/*.ts", "/routes/**/*.ts", "/routes/*.tsx", "/routes/**/*.tsx"],
446
520
  {
@@ -449,39 +523,58 @@ function getRoutes(config) {
449
523
  );
450
524
  const trie = new RouteTrie();
451
525
  Object.keys(routes).forEach((modulePath) => {
526
+ var _a2;
452
527
  const src = modulePath.slice(1);
453
- let routePath = modulePath.replace(/^\/routes/, "");
454
- const parts = path.parse(routePath);
528
+ let relativeRoutePath = modulePath.replace(/^\/routes/, "");
529
+ const parts = path.parse(relativeRoutePath);
455
530
  if (parts.name.startsWith("_")) {
456
531
  return;
457
532
  }
458
533
  if (parts.name === "index") {
459
- routePath = parts.dir;
534
+ relativeRoutePath = parts.dir;
460
535
  } else {
461
- routePath = path.join(parts.dir, parts.name);
536
+ relativeRoutePath = path.join(parts.dir, parts.name);
462
537
  }
463
- const localeRoutePath = i18nUrlFormat.replace("{locale}", "[locale]").replace("{path}", routePath.replace(/^\/*/, ""));
538
+ const urlFormat = "/[base]/[path]";
539
+ const i18nUrlFormat = toSquareBrackets(
540
+ ((_a2 = rootConfig.i18n) == null ? void 0 : _a2.urlFormat) || "/[locale]/[base]/[path]"
541
+ );
542
+ const placeholders = {
543
+ base: removeSlashes(basePath),
544
+ path: removeSlashes(relativeRoutePath)
545
+ };
546
+ const formatUrl = (format) => {
547
+ var _a3;
548
+ const url = format.replaceAll("[base]", placeholders.base).replaceAll("[path]", placeholders.path);
549
+ return normalizeUrlPath(url, {
550
+ trailingSlash: (_a3 = rootConfig.server) == null ? void 0 : _a3.trailingSlash
551
+ });
552
+ };
553
+ const routePath = formatUrl(urlFormat);
554
+ const localeRoutePath = formatUrl(i18nUrlFormat);
464
555
  trie.add(routePath, {
465
556
  src,
466
557
  module: routes[modulePath],
467
558
  locale: defaultLocale,
468
559
  isDefaultLocale: true,
469
- routePath: normalizeUrlPath(routePath),
470
- localeRoutePath: normalizeUrlPath(localeRoutePath)
471
- });
472
- locales.forEach((locale) => {
473
- const localePath = localeRoutePath.replace("[locale]", locale);
474
- if (localePath !== routePath) {
475
- trie.add(localePath, {
476
- src,
477
- module: routes[modulePath],
478
- locale,
479
- isDefaultLocale: false,
480
- routePath,
481
- localeRoutePath
482
- });
483
- }
560
+ routePath,
561
+ localeRoutePath
484
562
  });
563
+ if (i18nUrlFormat.includes("[locale]")) {
564
+ locales.forEach((locale) => {
565
+ const localePath = localeRoutePath.replace("[locale]", locale);
566
+ if (localePath !== relativeRoutePath) {
567
+ trie.add(localePath, {
568
+ src,
569
+ module: routes[modulePath],
570
+ locale,
571
+ isDefaultLocale: false,
572
+ routePath,
573
+ localeRoutePath
574
+ });
575
+ }
576
+ });
577
+ }
485
578
  });
486
579
  return trie;
487
580
  }
@@ -510,18 +603,24 @@ async function getAllPathsForRoute(urlPathFormat, route) {
510
603
  }
511
604
  );
512
605
  }
513
- } else if (pathContainsPlaceholders(urlPathFormat) && !routeModule.handle) {
606
+ } else if (routeModule.getStaticProps && !pathContainsPlaceholders(urlPathFormat)) {
607
+ urlPaths.push({ urlPath: normalizeUrlPath(urlPathFormat), params: {} });
608
+ } else if (!routeModule.handle && !pathContainsPlaceholders(urlPathFormat)) {
609
+ urlPaths.push({ urlPath: normalizeUrlPath(urlPathFormat), params: {} });
610
+ } else if (pathContainsPlaceholders(urlPathFormat) && !routeModule.handle && !routeModule.getStaticPaths) {
514
611
  console.warn(
515
- `path contains placeholders: ${urlPathFormat}, did you forget to define getStaticPaths()? more info: https://rootjs.dev/guide/routes#getStaticPaths`
612
+ [
613
+ `warning: path contains placeholders: ${urlPathFormat}.`,
614
+ `define either ssg getStaticPaths() or ssr handle() for route: ${route.src}.`,
615
+ "more info: https://rootjs.dev/guide/routes"
616
+ ].join("\n")
516
617
  );
517
- } else {
518
- urlPaths.push({ urlPath: normalizeUrlPath(urlPathFormat), params: {} });
519
618
  }
520
619
  return urlPaths;
521
620
  }
522
621
  function replaceParams(urlPathFormat, params) {
523
622
  const urlPath = urlPathFormat.replaceAll(
524
- /\[(\.\.\.)?([\w\-_]*)\]/g,
623
+ /\[\[?(\.\.\.)?([\w\-_]*)\]?\]/g,
525
624
  (match, _wildcard, key) => {
526
625
  const val = params[key];
527
626
  if (!val) {
@@ -532,10 +631,14 @@ function replaceParams(urlPathFormat, params) {
532
631
  );
533
632
  return urlPath;
534
633
  }
535
- function normalizeUrlPath(urlPath) {
536
- if (urlPath !== "/" && urlPath.endsWith("/")) {
634
+ function normalizeUrlPath(urlPath, options) {
635
+ urlPath = urlPath.replace(/\/+/g, "/");
636
+ if ((options == null ? void 0 : options.trailingSlash) === false && urlPath !== "/" && urlPath.endsWith("/")) {
537
637
  urlPath = urlPath.replace(/\/*$/g, "");
538
638
  }
639
+ if (!urlPath.startsWith("/")) {
640
+ urlPath = `/${urlPath}`;
641
+ }
539
642
  return urlPath;
540
643
  }
541
644
  function pathContainsPlaceholders(urlPath) {
@@ -544,6 +647,17 @@ function pathContainsPlaceholders(urlPath) {
544
647
  return segment.startsWith("[") && segment.endsWith("]");
545
648
  });
546
649
  }
650
+ function removeSlashes(str) {
651
+ return str.replace(/^\/*/g, "").replace(/\/*$/g, "");
652
+ }
653
+ function toSquareBrackets(str) {
654
+ if (str.includes("{") || str.includes("}")) {
655
+ const val = str.replaceAll("{", "[").replaceAll("}", "]");
656
+ console.warn(`"${str}" is a deprecated format, please switch to "${val}"`);
657
+ return val;
658
+ }
659
+ return str;
660
+ }
547
661
 
548
662
  // src/render/render.tsx
549
663
  import { jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
@@ -555,7 +669,7 @@ var Renderer = class {
555
669
  this.elementGraph = options.elementGraph;
556
670
  }
557
671
  async handle(req, res, next) {
558
- const url = req.path.toLowerCase();
672
+ const url = req.path;
559
673
  const [route, routeParams] = this.routes.get(url);
560
674
  if (!route) {
561
675
  next();
@@ -668,7 +782,12 @@ var Renderer = class {
668
782
  const routeAsset = await this.assetMap.get(route.src);
669
783
  if (routeAsset) {
670
784
  const routeCssDeps = await routeAsset.getCssDeps();
671
- routeCssDeps.forEach((dep) => cssDeps.add(dep));
785
+ routeCssDeps.forEach((dep) => {
786
+ if (dep.endsWith("?inline")) {
787
+ return;
788
+ }
789
+ cssDeps.add(dep);
790
+ });
672
791
  }
673
792
  await this.collectElementDeps(mainHtml, jsDeps, cssDeps);
674
793
  await Promise.all(
@@ -903,7 +1022,12 @@ ${renderToString(page)}
903
1022
  const assetJsDeps = await asset.getJsDeps();
904
1023
  assetJsDeps.forEach((dep) => jsDeps.add(dep));
905
1024
  const assetCssDeps = await asset.getCssDeps();
906
- assetCssDeps.forEach((dep) => cssDeps.add(dep));
1025
+ assetCssDeps.forEach((dep) => {
1026
+ if (dep.endsWith("?inline")) {
1027
+ return;
1028
+ }
1029
+ cssDeps.add(dep);
1030
+ });
907
1031
  })
908
1032
  );
909
1033
  return { jsDeps, cssDeps };