@apex-stack/core 0.1.1 → 0.1.2

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.
@@ -20,11 +20,18 @@ async function renderPage(opts) {
20
20
  body: html,
21
21
  island: stateIsland(mod.componentId, loaderData),
22
22
  css: mod.css + (opts.componentCss ?? ""),
23
- pageId: opts.pageId
23
+ pageId: opts.pageId,
24
+ clientHref: opts.clientHref
24
25
  });
25
26
  return opts.transformHtml ? opts.transformHtml(opts.url, doc) : doc;
26
27
  }
27
- function shell({ body, island, css, pageId }) {
28
+ function shell({ body, island, css, pageId, clientHref }) {
29
+ const clientScript = clientHref ? `<script type="module" src="${clientHref}"></script>` : `<script type="module">
30
+ import Alpine from 'alpinejs'
31
+ import ${JSON.stringify(pageId)}
32
+ window.Alpine = Alpine
33
+ Alpine.start()
34
+ </script>`;
28
35
  return `<!DOCTYPE html>
29
36
  <html lang="en">
30
37
  <head>
@@ -36,12 +43,7 @@ function shell({ body, island, css, pageId }) {
36
43
  <body>
37
44
  ${body}
38
45
  ${island}
39
- <script type="module">
40
- import Alpine from 'alpinejs'
41
- import ${JSON.stringify(pageId)}
42
- window.Alpine = Alpine
43
- Alpine.start()
44
- </script>
46
+ ${clientScript}
45
47
  </body>
46
48
  </html>`;
47
49
  }
@@ -408,6 +410,9 @@ function notFoundPage(url, routes) {
408
410
 
409
411
  export {
410
412
  isApexResource,
413
+ loadComponents,
414
+ renderIslandsPage,
415
+ scanPages,
411
416
  renderPage,
412
417
  startDevServer
413
418
  };
package/dist/cli.js CHANGED
@@ -1,15 +1,141 @@
1
1
  import {
2
+ loadComponents,
3
+ renderIslandsPage,
4
+ renderPage,
5
+ scanPages,
2
6
  startDevServer
3
- } from "./chunk-XB5ZYPPE.js";
7
+ } from "./chunk-GC3WNE6M.js";
4
8
 
5
9
  // src/cli.ts
6
- import { resolve as resolve3 } from "path";
7
- import { defineCommand as defineCommand4, runMain } from "citty";
10
+ import { resolve as resolve4 } from "path";
11
+ import { defineCommand as defineCommand5, runMain } from "citty";
8
12
 
9
- // src/commands/make.ts
10
- import { existsSync, mkdirSync, writeFileSync } from "fs";
11
- import { dirname, join, resolve } from "path";
13
+ // src/commands/build.ts
14
+ import { cpSync, existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
15
+ import { dirname, join as join2, resolve } from "path";
16
+ import { apex as apex2 } from "@apex-stack/vite";
12
17
  import { defineCommand } from "citty";
18
+ import { createServer as createViteServer } from "vite";
19
+
20
+ // src/build/buildClient.ts
21
+ import { readFileSync } from "fs";
22
+ import { join } from "path";
23
+ import { apex } from "@apex-stack/vite";
24
+ import { build } from "vite";
25
+ var VIRT = "virtual:apex-client:";
26
+ function entryName(pageId) {
27
+ return pageId.replace(/^\/pages\//, "").replace(/\.alpine$/, "").replace(/[^a-zA-Z0-9]+/g, "_");
28
+ }
29
+ async function buildClient(root, routes, outDir) {
30
+ const input = {};
31
+ for (const r of routes) input[entryName(r.pageId)] = `${VIRT}${r.pageId}`;
32
+ const entryPlugin = {
33
+ name: "apex:client-entries",
34
+ resolveId(id) {
35
+ if (id.startsWith(VIRT)) return `\0${id}`;
36
+ },
37
+ load(id) {
38
+ if (id.startsWith(`\0${VIRT}`)) {
39
+ const pageId = id.slice(`\0${VIRT}`.length);
40
+ return [
41
+ `import Alpine from 'alpinejs'`,
42
+ `import ${JSON.stringify(pageId)}`,
43
+ `window.Alpine = Alpine`,
44
+ `Alpine.start()`
45
+ ].join("\n");
46
+ }
47
+ }
48
+ };
49
+ await build({
50
+ root,
51
+ logLevel: "warn",
52
+ plugins: [apex({ clientRuntime: "@apex-stack/core/client" }), entryPlugin],
53
+ build: {
54
+ outDir,
55
+ emptyOutDir: false,
56
+ manifest: true,
57
+ rollupOptions: { input }
58
+ }
59
+ });
60
+ const manifest = JSON.parse(readFileSync(join(outDir, ".vite", "manifest.json"), "utf8"));
61
+ const hrefs = /* @__PURE__ */ new Map();
62
+ for (const r of routes) {
63
+ const virt = `${VIRT}${r.pageId}`;
64
+ const entry = Object.values(manifest).find(
65
+ (m) => m.isEntry && (m.src === virt || m.src === `\0${virt}`)
66
+ );
67
+ if (entry) hrefs.set(r.pageId, `/${entry.file}`);
68
+ }
69
+ return hrefs;
70
+ }
71
+
72
+ // src/commands/build.ts
73
+ function outFile(pattern) {
74
+ const clean = pattern.replace(/^\//, "");
75
+ return clean === "" ? "index.html" : `${clean}/index.html`;
76
+ }
77
+ var buildCommand = defineCommand({
78
+ meta: { name: "build", description: "Prerender pages to deployable HTML + client bundles" },
79
+ args: {
80
+ root: { type: "positional", required: false, description: "Project root", default: "." },
81
+ outDir: { type: "string", description: "Output directory", default: "dist" },
82
+ islands: { type: "boolean", description: "Static-first islands mode (zero-JS static)", default: false }
83
+ },
84
+ async run({ args }) {
85
+ const root = resolve(process.cwd(), args.root);
86
+ const outDir = resolve(root, args.outDir);
87
+ rmSync(outDir, { recursive: true, force: true });
88
+ const routes = scanPages(root);
89
+ const staticRoutes = routes.filter((r) => !r.isDynamic);
90
+ const dynamic = routes.filter((r) => r.isDynamic);
91
+ const hrefs = args.islands ? /* @__PURE__ */ new Map() : await buildClient(root, staticRoutes, outDir);
92
+ const vite = await createViteServer({
93
+ root,
94
+ appType: "custom",
95
+ server: { middlewareMode: true },
96
+ plugins: [apex2({ clientRuntime: "@apex-stack/core/client" })]
97
+ });
98
+ try {
99
+ const { registry, css: componentCss } = await loadComponents(
100
+ root,
101
+ (id) => vite.ssrLoadModule(id)
102
+ );
103
+ for (const route of staticRoutes) {
104
+ const common = {
105
+ loadModule: (id) => vite.ssrLoadModule(id),
106
+ pageId: route.pageId,
107
+ url: route.pattern,
108
+ registry,
109
+ componentCss
110
+ };
111
+ const html = args.islands ? await renderIslandsPage(common) : await renderPage({ ...common, clientHref: hrefs.get(route.pageId) });
112
+ const dest = join2(outDir, outFile(route.pattern));
113
+ mkdirSync(dirname(dest), { recursive: true });
114
+ writeFileSync(dest, html);
115
+ console.log(` \u2713 ${route.pattern} \u2192 ${outFile(route.pattern)}`);
116
+ }
117
+ const pub = join2(root, "public");
118
+ if (existsSync(pub)) cpSync(pub, outDir, { recursive: true });
119
+ console.log(
120
+ `
121
+ Built ${staticRoutes.length} page(s) \u2192 ${args.outDir}/` + (args.islands ? " (islands / static-first)" : " (prerendered + hydrated)")
122
+ );
123
+ if (dynamic.length) {
124
+ console.log(
125
+ ` Skipped ${dynamic.length} dynamic route(s): ${dynamic.map((r) => r.pattern).join(", ")} (server target on the roadmap).`
126
+ );
127
+ }
128
+ console.log();
129
+ } finally {
130
+ await vite.close();
131
+ }
132
+ }
133
+ });
134
+
135
+ // src/commands/make.ts
136
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
137
+ import { dirname as dirname2, join as join3, resolve as resolve2 } from "path";
138
+ import { defineCommand as defineCommand2 } from "citty";
13
139
  function pageTemplate(name) {
14
140
  return `<script server lang="ts">
15
141
  export function loader() {
@@ -55,14 +181,14 @@ export default defineApexRoute({
55
181
  function plan(kind, name, root) {
56
182
  switch (kind) {
57
183
  case "page":
58
- return { path: join(root, "pages", `${name}.alpine`), contents: pageTemplate(name) };
184
+ return { path: join3(root, "pages", `${name}.alpine`), contents: pageTemplate(name) };
59
185
  case "component":
60
- return { path: join(root, "components", `${name}.alpine`), contents: componentTemplate() };
186
+ return { path: join3(root, "components", `${name}.alpine`), contents: componentTemplate() };
61
187
  case "api":
62
- return { path: join(root, "server", "api", `${name}.ts`), contents: apiTemplate(name) };
188
+ return { path: join3(root, "server", "api", `${name}.ts`), contents: apiTemplate(name) };
63
189
  }
64
190
  }
65
- var makeCommand = defineCommand({
191
+ var makeCommand = defineCommand2({
66
192
  meta: { name: "make", description: "Generate a page, component, or API route" },
67
193
  args: {
68
194
  kind: { type: "positional", required: true, description: "page | component | api" },
@@ -77,16 +203,16 @@ var makeCommand = defineCommand({
77
203
  `);
78
204
  process.exit(1);
79
205
  }
80
- const root = resolve(process.cwd(), args.root);
206
+ const root = resolve2(process.cwd(), args.root);
81
207
  const { path, contents } = plan(kind, args.name, root);
82
- if (existsSync(path)) {
208
+ if (existsSync2(path)) {
83
209
  console.error(`
84
210
  \u2717 Already exists: ${path}
85
211
  `);
86
212
  process.exit(1);
87
213
  }
88
- mkdirSync(dirname(path), { recursive: true });
89
- writeFileSync(path, contents);
214
+ mkdirSync2(dirname2(path), { recursive: true });
215
+ writeFileSync2(path, contents);
90
216
  console.log(`
91
217
  \u2713 Created ${path.replace(`${root}/`, "")}
92
218
  `);
@@ -94,8 +220,8 @@ var makeCommand = defineCommand({
94
220
  });
95
221
 
96
222
  // src/commands/mcp.ts
97
- import { defineCommand as defineCommand2 } from "citty";
98
- var mcpCommand = defineCommand2({
223
+ import { defineCommand as defineCommand3 } from "citty";
224
+ var mcpCommand = defineCommand3({
99
225
  meta: { name: "mcp", description: "Inspect the local MCP server (list or call tools)" },
100
226
  args: {
101
227
  url: { type: "string", description: "MCP endpoint URL", default: "http://localhost:3000/mcp" },
@@ -149,10 +275,10 @@ var mcpCommand = defineCommand2({
149
275
 
150
276
  // src/commands/migrate.ts
151
277
  import { createRequire } from "module";
152
- import { join as join2, resolve as resolve2 } from "path";
278
+ import { join as join4, resolve as resolve3 } from "path";
153
279
  import { pathToFileURL } from "url";
154
- import { defineCommand as defineCommand3 } from "citty";
155
- var migrateCommand = defineCommand3({
280
+ import { defineCommand as defineCommand4 } from "citty";
281
+ var migrateCommand = defineCommand4({
156
282
  meta: { name: "migrate", description: "Apply pending SQL migrations (db/migrations/*.sql)" },
157
283
  args: {
158
284
  db: { type: "string", description: "SQLite file path", default: "data.db" },
@@ -160,17 +286,17 @@ var migrateCommand = defineCommand3({
160
286
  root: { type: "string", description: "Project root", default: "." }
161
287
  },
162
288
  async run({ args }) {
163
- const root = resolve2(process.cwd(), args.root);
289
+ const root = resolve3(process.cwd(), args.root);
164
290
  let data;
165
291
  try {
166
- const require2 = createRequire(join2(root, "package.json"));
292
+ const require2 = createRequire(join4(root, "package.json"));
167
293
  data = await import(pathToFileURL(require2.resolve("@apex-stack/data")).href);
168
294
  } catch {
169
295
  console.error("\n @apex-stack/data is not installed in this project. Run: npm i @apex-stack/data\n");
170
296
  process.exit(1);
171
297
  }
172
- const { sqlite } = data.createDb(resolve2(root, args.db));
173
- const applied = data.applyMigrations(sqlite, resolve2(root, args.dir));
298
+ const { sqlite } = data.createDb(resolve3(root, args.db));
299
+ const applied = data.applyMigrations(sqlite, resolve3(root, args.dir));
174
300
  console.log(
175
301
  applied.length ? `
176
302
  \u2713 Applied ${applied.length} migration(s): ${applied.join(", ")}
@@ -180,7 +306,7 @@ var migrateCommand = defineCommand3({
180
306
  });
181
307
 
182
308
  // src/cli.ts
183
- var dev = defineCommand4({
309
+ var dev = defineCommand5({
184
310
  meta: { name: "dev", description: "Start the Apex JS development server" },
185
311
  args: {
186
312
  root: { type: "positional", required: false, description: "Project root", default: "." },
@@ -188,7 +314,7 @@ var dev = defineCommand4({
188
314
  islands: { type: "boolean", description: "Render in islands mode (static-first)", default: false }
189
315
  },
190
316
  async run({ args }) {
191
- const root = resolve3(process.cwd(), args.root);
317
+ const root = resolve4(process.cwd(), args.root);
192
318
  const port = Number(args.port);
193
319
  const { port: actual } = await startDevServer({ root, port, islands: args.islands });
194
320
  console.log(`
@@ -197,11 +323,17 @@ var dev = defineCommand4({
197
323
  `);
198
324
  }
199
325
  });
200
- var main = defineCommand4({
326
+ var main = defineCommand5({
201
327
  meta: {
202
328
  name: "apex",
203
329
  description: "The full-stack meta-framework for Alpine.js"
204
330
  },
205
- subCommands: { dev, make: makeCommand, migrate: migrateCommand, mcp: mcpCommand }
331
+ subCommands: {
332
+ dev,
333
+ build: buildCommand,
334
+ make: makeCommand,
335
+ migrate: migrateCommand,
336
+ mcp: mcpCommand
337
+ }
206
338
  });
207
339
  runMain(main);
package/dist/index.d.ts CHANGED
@@ -121,6 +121,9 @@ interface RenderPageOptions {
121
121
  componentCss?: string;
122
122
  /** Post-process the shell HTML (dev: vite.transformIndexHtml). */
123
123
  transformHtml?: (url: string, html: string) => string | Promise<string>;
124
+ /** In a production build, the href of the built client bundle for this page.
125
+ * When set, the shell references it instead of the inline dev module. */
126
+ clientHref?: string;
124
127
  }
125
128
  /**
126
129
  * The framework's render seam — deliberately dev-server-agnostic so it can move
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  isApexResource,
3
3
  renderPage,
4
4
  startDevServer
5
- } from "./chunk-XB5ZYPPE.js";
5
+ } from "./chunk-GC3WNE6M.js";
6
6
 
7
7
  // src/api/defineRoute.ts
8
8
  function defineApexRoute(config) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apex-stack/core",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "The full-stack meta-framework for Alpine.js — CLI and runtime",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,8 +40,8 @@
40
40
  "h3": "^1.13.0",
41
41
  "vite": "^6.0.7",
42
42
  "zod": "^4.4.3",
43
- "@apex-stack/vite": "0.1.1",
44
- "@apex-stack/kit": "0.1.1"
43
+ "@apex-stack/kit": "0.1.1",
44
+ "@apex-stack/vite": "0.1.1"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "alpinejs": "^3.14.0"