@apex-stack/core 0.1.17 → 0.1.19

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.
@@ -5,7 +5,7 @@ import {
5
5
  renderIslandsPage,
6
6
  renderPage,
7
7
  scanPages
8
- } from "./chunk-7GPCNGGO.js";
8
+ } from "./chunk-XSN6NDWP.js";
9
9
  import "./chunk-MZVLRU3R.js";
10
10
 
11
11
  // src/commands/build.ts
@@ -89,14 +89,17 @@ function scanPages(root) {
89
89
  const parts = rel.replace(/\.alpine$/, "").split("/");
90
90
  if (parts[parts.length - 1] === "index") parts.pop();
91
91
  const segments = parts.map((p) => {
92
+ const catchAll = /^\[\.\.\.(.+)\]$/.exec(p);
93
+ if (catchAll) return { catchAll: catchAll[1] };
92
94
  const m = /^\[(.+)\]$/.exec(p);
93
95
  return m ? { param: m[1] } : { literal: p };
94
96
  });
95
- const isDynamic = segments.some((s) => s.param !== void 0);
96
- const pattern = `/${segments.map((s) => s.param ? `:${s.param}` : s.literal).join("/")}`;
97
+ const isDynamic = segments.some((s) => s.param !== void 0 || s.catchAll !== void 0);
98
+ const pattern = `/${segments.map((s) => s.catchAll ? `:${s.catchAll}*` : s.param ? `:${s.param}` : s.literal).join("/")}`;
97
99
  return { pageId, pattern, segments, isDynamic };
98
100
  });
99
- return routes.sort((a, b) => Number(a.isDynamic) - Number(b.isDynamic));
101
+ const rank = (r) => r.segments.some((s) => s.catchAll) ? 2 : r.isDynamic ? 1 : 0;
102
+ return routes.sort((a, b) => rank(a) - rank(b));
100
103
  }
101
104
  function pathSegments(url) {
102
105
  const path = url.split("?")[0] ?? "/";
@@ -105,6 +108,26 @@ function pathSegments(url) {
105
108
  function matchRoute(routes, url) {
106
109
  const segs = pathSegments(url);
107
110
  for (const route of routes) {
111
+ const last = route.segments[route.segments.length - 1];
112
+ const isCatchAll = Boolean(last?.catchAll);
113
+ if (isCatchAll) {
114
+ const lead = route.segments.slice(0, -1);
115
+ if (segs.length < lead.length + 1) continue;
116
+ const params2 = {};
117
+ let ok2 = true;
118
+ for (let i = 0; i < lead.length; i++) {
119
+ const rs = lead[i];
120
+ const value = segs[i];
121
+ if (rs.param) params2[rs.param] = decodeURIComponent(value);
122
+ else if (rs.literal !== value) {
123
+ ok2 = false;
124
+ break;
125
+ }
126
+ }
127
+ if (!ok2) continue;
128
+ params2[last?.catchAll] = segs.slice(lead.length).map(decodeURIComponent).join("/");
129
+ return { pageId: route.pageId, params: params2 };
130
+ }
108
131
  if (route.segments.length !== segs.length) continue;
109
132
  const params = {};
110
133
  let ok = true;
@@ -153,7 +176,7 @@ function storesInitialState(stores) {
153
176
  }
154
177
 
155
178
  // src/dev/renderPage.ts
156
- import { renderComponent, stateIsland } from "@apex-stack/kit";
179
+ import { renderComponent, renderFragment, stateIsland } from "@apex-stack/kit";
157
180
  function escAttr(s) {
158
181
  return String(s).replace(
159
182
  /[&<>"]/g,
@@ -185,10 +208,20 @@ async function renderPage(opts) {
185
208
  stores: storesInitialState(stores),
186
209
  authoredDefaults: mod.rootData ? mod.rootData() : void 0
187
210
  });
211
+ const available = opts.layouts ?? [];
212
+ const layoutName = mod.layout === false ? null : typeof mod.layout === "string" ? mod.layout : available.includes("default") ? "default" : null;
213
+ let body = html;
214
+ let layoutCss = "";
215
+ if (layoutName && available.includes(layoutName)) {
216
+ const layoutMod = await opts.loadModule(`/layouts/${layoutName}.alpine`);
217
+ const chrome = renderFragment(layoutMod.template, {}, layoutMod.scopeId, opts.registry);
218
+ body = /<slot\b[^>]*>[\s\S]*?<\/slot>/.test(chrome) ? chrome.replace(/<slot\b[^>]*>[\s\S]*?<\/slot>/, () => html) : chrome + html;
219
+ layoutCss = layoutMod.css;
220
+ }
188
221
  const doc = shell({
189
- body: html,
222
+ body,
190
223
  island: stateIsland(mod.componentId, loaderData),
191
- css: mod.css + (opts.componentCss ?? ""),
224
+ css: mod.css + layoutCss + (opts.componentCss ?? ""),
192
225
  pageId: opts.pageId,
193
226
  clientHref: opts.clientHref,
194
227
  storeIds: stores.map((s) => s.id),
package/dist/cli.js CHANGED
@@ -109,10 +109,10 @@ var main = defineCommand2({
109
109
  },
110
110
  subCommands: {
111
111
  new: newCommand,
112
- dev: () => import("./dev-WS52CGSE.js").then((m) => m.devCommand),
113
- build: () => import("./build-KJF4LJWP.js").then((m) => m.buildCommand),
114
- start: () => import("./start-XYVCDMPJ.js").then((m) => m.startCommand),
115
- make: () => import("./make-62PPHZQY.js").then((m) => m.makeCommand),
112
+ dev: () => import("./dev-OCVQRCCE.js").then((m) => m.devCommand),
113
+ build: () => import("./build-J47A3B4Y.js").then((m) => m.buildCommand),
114
+ start: () => import("./start-AUJJ7HAY.js").then((m) => m.startCommand),
115
+ make: () => import("./make-JAW22LQZ.js").then((m) => m.makeCommand),
116
116
  migrate: () => import("./migrate-NOGFOFV2.js").then((m) => m.migrateCommand),
117
117
  mcp: () => import("./mcp-DL4J6JFJ.js").then((m) => m.mcpCommand)
118
118
  },
@@ -20,7 +20,7 @@ var devCommand = defineCommand({
20
20
  process.stdout.write(banner());
21
21
  const sp = spinner(`Starting dev server${args.islands ? " (islands mode)" : ""}\u2026`);
22
22
  try {
23
- const { startDevServer } = await import("./server-5QQGUF3R.js");
23
+ const { startDevServer } = await import("./server-L3V34B5X.js");
24
24
  const { port: actual } = await startDevServer({ root, port, islands: Boolean(args.islands) });
25
25
  sp.succeed("Dev server ready");
26
26
  ready([
@@ -33,6 +33,26 @@ function componentTemplate() {
33
33
  </style>
34
34
  `;
35
35
  }
36
+ function layoutTemplate() {
37
+ return `<template>
38
+ <header class="site-header">
39
+ <nav><a href="/">Home</a></nav>
40
+ </header>
41
+ <main>
42
+ <slot></slot>
43
+ </main>
44
+ <footer class="site-footer">
45
+ <p>Built with Apex JS</p>
46
+ </footer>
47
+ </template>
48
+
49
+ <style scoped>
50
+ .site-header { padding: 1rem 1.5rem; border-bottom: 1px solid #e5e7eb; }
51
+ main { max-width: 60rem; margin: 0 auto; padding: 2rem 1.5rem; }
52
+ .site-footer { padding: 1.5rem; border-top: 1px solid #e5e7eb; color: #6b7280; }
53
+ </style>
54
+ `;
55
+ }
36
56
  function storeTemplate(name) {
37
57
  return `import { defineStore } from '@apex-stack/core'
38
58
 
@@ -69,20 +89,22 @@ function plan(kind, name, root) {
69
89
  return { path: join(root, "server", "api", `${name}.ts`), contents: apiTemplate(name) };
70
90
  case "store":
71
91
  return { path: join(root, "stores", `${name}.ts`), contents: storeTemplate(name) };
92
+ case "layout":
93
+ return { path: join(root, "layouts", `${name}.alpine`), contents: layoutTemplate() };
72
94
  }
73
95
  }
74
96
  var makeCommand = defineCommand({
75
- meta: { name: "make", description: "Generate a page, component, API route, or store" },
97
+ meta: { name: "make", description: "Generate a page, component, API route, store, or layout" },
76
98
  args: {
77
- kind: { type: "positional", required: true, description: "page | component | api | store" },
99
+ kind: { type: "positional", required: true, description: "page | component | api | store | layout" },
78
100
  name: { type: "positional", required: true, description: "Name (about, Counter, todos, \u2026)" },
79
101
  root: { type: "string", description: "Project root", default: "." }
80
102
  },
81
103
  run({ args }) {
82
104
  const kind = args.kind;
83
- if (kind !== "page" && kind !== "component" && kind !== "api" && kind !== "store") {
105
+ if (kind !== "page" && kind !== "component" && kind !== "api" && kind !== "store" && kind !== "layout") {
84
106
  console.error(`
85
- Unknown type "${args.kind}". Use: page | component | api | store
107
+ Unknown type "${args.kind}". Use: page | component | api | store | layout
86
108
  `);
87
109
  process.exit(1);
88
110
  }
@@ -13,11 +13,11 @@ import {
13
13
  renderIslandsPage,
14
14
  renderPage,
15
15
  scanPages
16
- } from "./chunk-7GPCNGGO.js";
16
+ } from "./chunk-XSN6NDWP.js";
17
17
  import "./chunk-MZVLRU3R.js";
18
18
 
19
19
  // src/dev/server.ts
20
- import { existsSync as existsSync2 } from "fs";
20
+ import { existsSync as existsSync2, readdirSync } from "fs";
21
21
  import { createServer as createHttpServer } from "http";
22
22
  import { createRequire } from "module";
23
23
  import { join } from "path";
@@ -213,6 +213,8 @@ async function startDevServer(options) {
213
213
  (id) => ssrLoad(id)
214
214
  );
215
215
  const stores = await loadStores(options.root, (id) => ssrLoad(id));
216
+ const layoutsDir = join(options.root, "layouts");
217
+ const layouts = existsSync2(layoutsDir) ? readdirSync(layoutsDir).filter((f) => f.endsWith(".alpine")).map((f) => f.replace(/\.alpine$/, "")) : [];
216
218
  const render = options.islands ? renderIslandsPage : renderPage;
217
219
  const html = await render({
218
220
  loadModule: (id) => ssrLoad(id),
@@ -223,6 +225,7 @@ async function startDevServer(options) {
223
225
  componentCss,
224
226
  stores,
225
227
  appCss,
228
+ layouts,
226
229
  transformHtml: (u, doc) => vite.transformIndexHtml(u, doc)
227
230
  });
228
231
  setResponseHeader(event, "Content-Type", "text/html");
@@ -9,7 +9,7 @@ import {
9
9
  matchRoute,
10
10
  renderIslandsPage,
11
11
  renderPage
12
- } from "./chunk-7GPCNGGO.js";
12
+ } from "./chunk-XSN6NDWP.js";
13
13
  import "./chunk-MZVLRU3R.js";
14
14
 
15
15
  // src/commands/start.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apex-stack/core",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "The full-stack meta-framework for Alpine.js — CLI and runtime",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -45,7 +45,7 @@
45
45
  "h3": "^1.13.0",
46
46
  "vite": "^6.0.7",
47
47
  "zod": "^4.4.3",
48
- "@apex-stack/kit": "0.1.4",
48
+ "@apex-stack/kit": "0.1.5",
49
49
  "@apex-stack/vite": "0.1.5"
50
50
  },
51
51
  "peerDependencies": {