@apex-stack/core 0.1.16 → 0.1.18

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-YO4UXWIY.js";
8
+ } from "./chunk-VALQEQD3.js";
9
9
  import "./chunk-MZVLRU3R.js";
10
10
 
11
11
  // src/commands/build.ts
@@ -153,10 +153,27 @@ function storesInitialState(stores) {
153
153
  }
154
154
 
155
155
  // src/dev/renderPage.ts
156
- import { renderComponent, stateIsland } from "@apex-stack/kit";
156
+ import { renderComponent, renderFragment, stateIsland } from "@apex-stack/kit";
157
+ function escAttr(s) {
158
+ return String(s).replace(
159
+ /[&<>"]/g,
160
+ (c) => c === "&" ? "&amp;" : c === "<" ? "&lt;" : c === ">" ? "&gt;" : "&quot;"
161
+ );
162
+ }
163
+ function renderHead(head) {
164
+ const parts = [`<title>${head?.title ? escAttr(head.title) : "Apex JS"}</title>`];
165
+ for (const m of head?.meta ?? []) {
166
+ parts.push(`<meta ${Object.entries(m).map(([k, v]) => `${k}="${escAttr(v)}"`).join(" ")} />`);
167
+ }
168
+ for (const l of head?.link ?? []) {
169
+ parts.push(`<link ${Object.entries(l).map(([k, v]) => `${k}="${escAttr(v)}"`).join(" ")} />`);
170
+ }
171
+ return parts.join("\n ");
172
+ }
157
173
  async function renderPage(opts) {
158
174
  const mod = await opts.loadModule(opts.pageId);
159
175
  const loaderData = await mod.loader({ params: opts.params ?? {}, url: opts.url }) ?? {};
176
+ const head = mod.head ? await mod.head({ data: loaderData, params: opts.params ?? {}, url: opts.url }) : void 0;
160
177
  const stores = opts.stores ?? [];
161
178
  const { html } = renderComponent({
162
179
  template: mod.template,
@@ -168,18 +185,38 @@ async function renderPage(opts) {
168
185
  stores: storesInitialState(stores),
169
186
  authoredDefaults: mod.rootData ? mod.rootData() : void 0
170
187
  });
188
+ const available = opts.layouts ?? [];
189
+ const layoutName = mod.layout === false ? null : typeof mod.layout === "string" ? mod.layout : available.includes("default") ? "default" : null;
190
+ let body = html;
191
+ let layoutCss = "";
192
+ if (layoutName && available.includes(layoutName)) {
193
+ const layoutMod = await opts.loadModule(`/layouts/${layoutName}.alpine`);
194
+ const chrome = renderFragment(layoutMod.template, {}, layoutMod.scopeId, opts.registry);
195
+ body = /<slot\b[^>]*>[\s\S]*?<\/slot>/.test(chrome) ? chrome.replace(/<slot\b[^>]*>[\s\S]*?<\/slot>/, () => html) : chrome + html;
196
+ layoutCss = layoutMod.css;
197
+ }
171
198
  const doc = shell({
172
- body: html,
199
+ body,
173
200
  island: stateIsland(mod.componentId, loaderData),
174
- css: mod.css + (opts.componentCss ?? ""),
201
+ css: mod.css + layoutCss + (opts.componentCss ?? ""),
175
202
  pageId: opts.pageId,
176
203
  clientHref: opts.clientHref,
177
204
  storeIds: stores.map((s) => s.id),
178
- appCss: opts.appCss
205
+ appCss: opts.appCss,
206
+ headTags: renderHead(head)
179
207
  });
180
208
  return opts.transformHtml ? opts.transformHtml(opts.url, doc) : doc;
181
209
  }
182
- function shell({ body, island, css, pageId, clientHref, storeIds = [], appCss }) {
210
+ function shell({
211
+ body,
212
+ island,
213
+ css,
214
+ pageId,
215
+ clientHref,
216
+ storeIds = [],
217
+ appCss,
218
+ headTags = "<title>Apex JS</title>"
219
+ }) {
183
220
  const storeImports = storeIds.map((id, i) => ` import __s${i} from ${JSON.stringify(id)}`).join("\n");
184
221
  const storeRegs = storeIds.map((_, i) => ` Alpine.store(__s${i}.name, __s${i}.factory())`).join("\n");
185
222
  const clientScript = clientHref ? `<script type="module" src="${clientHref}"></script>` : `<script type="module">
@@ -196,7 +233,7 @@ ${storeRegs ? `${storeRegs}
196
233
  <head>
197
234
  <meta charset="utf-8" />
198
235
  <meta name="viewport" content="width=device-width, initial-scale=1" />
199
- <title>Apex JS</title>
236
+ ${headTags}
200
237
  <style>${css}</style>
201
238
  </head>
202
239
  <body>
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-PQRMJ7Z4.js").then((m) => m.devCommand),
113
- build: () => import("./build-UDFLTWY5.js").then((m) => m.buildCommand),
114
- start: () => import("./start-EYEWMZXS.js").then((m) => m.startCommand),
115
- make: () => import("./make-62PPHZQY.js").then((m) => m.makeCommand),
112
+ dev: () => import("./dev-W3D2FCEP.js").then((m) => m.devCommand),
113
+ build: () => import("./build-DXLV2X5C.js").then((m) => m.buildCommand),
114
+ start: () => import("./start-254YG2BY.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-GFQQABB4.js");
23
+ const { startDevServer } = await import("./server-VYF5SRVI.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-YO4UXWIY.js";
16
+ } from "./chunk-VALQEQD3.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-YO4UXWIY.js";
12
+ } from "./chunk-VALQEQD3.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.16",
3
+ "version": "0.1.18",
4
4
  "description": "The full-stack meta-framework for Alpine.js — CLI and runtime",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -46,7 +46,7 @@
46
46
  "vite": "^6.0.7",
47
47
  "zod": "^4.4.3",
48
48
  "@apex-stack/kit": "0.1.4",
49
- "@apex-stack/vite": "0.1.4"
49
+ "@apex-stack/vite": "0.1.5"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "alpinejs": "^3.14.0"