webspresso 0.0.69 → 0.0.71

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/index.d.ts CHANGED
@@ -90,7 +90,10 @@ export function setAppContext(partial: { db?: DatabaseInstance | null }): void;
90
90
  export function mountPages(
91
91
  app: Application,
92
92
  options: Record<string, unknown>
93
- ): unknown;
93
+ ): {
94
+ routeMetadata: unknown[];
95
+ registerDynamicFileRoutes: () => void;
96
+ };
94
97
 
95
98
  export function filePathToRoute(filePath: string, pagesDir: string): string;
96
99
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webspresso",
3
- "version": "0.0.69",
3
+ "version": "0.0.71",
4
4
  "description": "Minimal, production-ready SSR framework for Node.js with file-based routing, Nunjucks templating, built-in i18n, and CLI tooling",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -1484,14 +1484,15 @@ function formatCellValue(value, col) {
1484
1484
  }
1485
1485
  return String(value);
1486
1486
 
1487
- case 'text':
1487
+ case 'text': {
1488
1488
  const textStr = String(value);
1489
1489
  return textStr.length > 50 ? textStr.substring(0, 50) + '...' : textStr;
1490
+ }
1490
1491
 
1491
1492
  case 'file': {
1492
1493
  const s = String(value);
1493
1494
  const short = s.length > 72 ? s.substring(0, 72) + '…' : s;
1494
- if (/^https?:\/\//.test(s) || s.startsWith('/')) {
1495
+ if (/^https?:\\/\\//.test(s) || s.startsWith('/')) {
1495
1496
  return m('a.text-indigo-600.dark:text-indigo-400.hover:underline.break-all', {
1496
1497
  href: s,
1497
1498
  target: '_blank',
@@ -1501,9 +1502,10 @@ function formatCellValue(value, col) {
1501
1502
  return short || m('span.text-gray-400', '—');
1502
1503
  }
1503
1504
 
1504
- default:
1505
+ default: {
1505
1506
  const str = String(value);
1506
1507
  return str.length > 100 ? str.substring(0, 100) + '...' : str;
1508
+ }
1507
1509
  }
1508
1510
  }
1509
1511
 
@@ -506,9 +506,17 @@ function mountPages(app, options) {
506
506
 
507
507
  // Sort routes: static before dynamic before catch-all; then more literal segments, then deeper paths
508
508
  const sortRoutes = (routes) => routes.sort(compareRouteRegistrationOrder);
509
-
510
- // Register API routes
511
- for (const route of sortRoutes(apiRoutes)) {
509
+ sortRoutes(apiRoutes);
510
+ sortRoutes(ssrRoutes);
511
+
512
+ const apiStatic = apiRoutes.filter((r) => routeRegistrationMeta(r.routePath).tier === 0);
513
+ const apiDynamic = apiRoutes.filter((r) => routeRegistrationMeta(r.routePath).tier !== 0);
514
+ const ssrStatic = ssrRoutes.filter((r) => routeRegistrationMeta(r.routePath).tier === 0);
515
+ const ssrDynamic = ssrRoutes.filter((r) => routeRegistrationMeta(r.routePath).tier !== 0);
516
+
517
+ /** Register API routes (shared by static phase and dynamic phase). */
518
+ const registerApiRoutes = (routes) => {
519
+ for (const route of routes) {
512
520
  const handler = require(route.fullPath);
513
521
  const handlerFn = typeof handler === 'function' ? handler : handler.default || handler.handler;
514
522
  const routeMiddleware = handler.middleware;
@@ -582,10 +590,12 @@ function mountPages(app, options) {
582
590
  });
583
591
 
584
592
  log(` ${route.method.toUpperCase()} ${route.routePath} -> ${route.file}`);
585
- }
586
-
587
- // Register SSR routes
588
- for (const route of sortRoutes(ssrRoutes)) {
593
+ }
594
+ };
595
+
596
+ /** Register SSR GET routes (shared by static phase and dynamic phase). */
597
+ const registerSsrRoutes = (routes) => {
598
+ for (const route of routes) {
589
599
  app.get(route.routePath, async (req, res, next) => {
590
600
  try {
591
601
  // Detect locale
@@ -714,8 +724,19 @@ function mountPages(app, options) {
714
724
  });
715
725
 
716
726
  log(` GET ${route.routePath} -> ${route.file}`);
717
- }
718
-
727
+ }
728
+ };
729
+
730
+ // Static / literal file routes first so plugins can register reserved paths (e.g. /_admin)
731
+ // before catch-all dynamics like /:slug shadow them.
732
+ registerApiRoutes(apiStatic);
733
+ registerSsrRoutes(ssrStatic);
734
+
735
+ const registerDynamicFileRoutes = () => {
736
+ registerApiRoutes(apiDynamic);
737
+ registerSsrRoutes(ssrDynamic);
738
+ };
739
+
719
740
  // Return route metadata for plugins
720
741
  const routeMetadata = [
721
742
  ...ssrRoutes.map(r => ({
@@ -733,8 +754,8 @@ function mountPages(app, options) {
733
754
  isDynamic: r.routePath.includes(':') || r.routePath.includes('*')
734
755
  }))
735
756
  ];
736
-
737
- return routeMetadata;
757
+
758
+ return { routeMetadata, registerDynamicFileRoutes };
738
759
  }
739
760
 
740
761
  module.exports = {
package/src/server.js CHANGED
@@ -440,7 +440,7 @@ function createApp(options = {}) {
440
440
  if (!isTest) {
441
441
  console.log('\nMounting routes:');
442
442
  }
443
- const routeMetadata = mountPages(app, {
443
+ const { routeMetadata, registerDynamicFileRoutes } = mountPages(app, {
444
444
  pagesDir,
445
445
  nunjucks: nunjucksEnv,
446
446
  middlewares,
@@ -449,7 +449,7 @@ function createApp(options = {}) {
449
449
  db: options.db ?? null,
450
450
  clientRuntime,
451
451
  });
452
-
452
+
453
453
  // Set route metadata in plugin manager
454
454
  pluginManager.setRoutes(routeMetadata);
455
455
 
@@ -491,7 +491,11 @@ function createApp(options = {}) {
491
491
  clientRuntime,
492
492
  });
493
493
  }
494
-
494
+
495
+ // Dynamic / catch-all file routes after plugins and setupRoutes so paths like /_admin
496
+ // or custom /login are not shadowed by pages/[slug].njk (/:slug).
497
+ registerDynamicFileRoutes();
498
+
495
499
  // Helper to create error page context with fsy
496
500
  function createErrorContext(req, extraData = {}) {
497
501
  const baseUrl = process.env.BASE_URL || `http://localhost:${process.env.PORT || 3000}`;