create-apexjs 0.0.0 → 0.1.0

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/index.js CHANGED
@@ -1,5 +1,48 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- console.log("create-apexjs is coming soon. Follow https://github.com/andrecorugda/apexjs");
5
- process.exit(0);
4
+ import { cpSync, existsSync, readdirSync, readFileSync, renameSync, writeFileSync } from "fs";
5
+ import { basename, join, resolve } from "path";
6
+ import { fileURLToPath } from "url";
7
+ import { defineCommand, runMain } from "citty";
8
+ var TEMPLATE_DIR = fileURLToPath(new URL("../templates/default", import.meta.url));
9
+ var main = defineCommand({
10
+ meta: {
11
+ name: "create-apexjs",
12
+ description: "Scaffold a new Apex JS app"
13
+ },
14
+ args: {
15
+ dir: { type: "positional", required: false, description: "Target directory", default: "apex-app" }
16
+ },
17
+ run({ args }) {
18
+ const target = resolve(process.cwd(), args.dir);
19
+ const name = basename(target);
20
+ if (existsSync(target) && readdirSync(target).length > 0) {
21
+ console.error(`
22
+ \u2717 Target directory is not empty: ${target}
23
+ `);
24
+ process.exit(1);
25
+ }
26
+ cpSync(TEMPLATE_DIR, target, { recursive: true });
27
+ const gitignore = join(target, "_gitignore");
28
+ if (existsSync(gitignore)) renameSync(gitignore, join(target, ".gitignore"));
29
+ for (const rel of ["package.json", "README.md"]) {
30
+ const file = join(target, rel);
31
+ if (existsSync(file)) {
32
+ writeFileSync(file, readFileSync(file, "utf8").replaceAll("{{name}}", name));
33
+ }
34
+ }
35
+ console.log(`
36
+ \x1B[36mApex JS\x1B[0m app created in ${args.dir}
37
+
38
+ Next steps:
39
+ cd ${args.dir}
40
+ npm install
41
+ npm run dev # http://localhost:3000
42
+ npm run dev:islands # static-first islands mode
43
+
44
+ Your server/api/*.ts routes are also MCP tools at /mcp.
45
+ `);
46
+ }
47
+ });
48
+ runMain(main);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-apexjs",
3
- "version": "0.0.0",
3
+ "version": "0.1.0",
4
4
  "description": "Scaffold a new Apex JS app",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -21,7 +21,8 @@
21
21
  "create-apexjs": "./dist/index.js"
22
22
  },
23
23
  "files": [
24
- "dist"
24
+ "dist",
25
+ "templates"
25
26
  ],
26
27
  "dependencies": {
27
28
  "citty": "^0.1.6"
@@ -0,0 +1,33 @@
1
+ # {{name}}
2
+
3
+ An [Apex JS](https://github.com/andrecorugda/apexjs) app — a meta-framework for
4
+ Alpine.js that renders on the server and hydrates in the browser.
5
+
6
+ ## Getting started
7
+
8
+ ```bash
9
+ npm install
10
+ npm run dev
11
+ ```
12
+
13
+ Then open [http://localhost:3000](http://localhost:3000).
14
+
15
+ ## Islands mode
16
+
17
+ Ship interactive JavaScript only where you need it:
18
+
19
+ ```bash
20
+ apex dev --islands
21
+ ```
22
+
23
+ ## Project structure
24
+
25
+ - `pages/*.alpine` — single-file components. The `<script server>` block runs on
26
+ the server; its `loader()` return value becomes the Alpine `x-data` scope.
27
+ - `server/api/*.ts` — API routes defined with `defineApexRoute`.
28
+
29
+ ## AI-native API
30
+
31
+ Every route in `server/api/*.ts` is a REST endpoint **and** an MCP tool at the
32
+ same time. Set `mcp: true` on a route (see `server/api/hello.ts`) and it is
33
+ automatically exposed to AI agents at the `/mcp` endpoint — no extra wiring.
@@ -0,0 +1,3 @@
1
+ node_modules
2
+ dist
3
+ *.log
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "apex dev",
7
+ "dev:islands": "apex dev --islands"
8
+ },
9
+ "dependencies": {
10
+ "alpinejs": "^3.14.8",
11
+ "apexjs-core": "latest",
12
+ "zod": "^4.0.0"
13
+ }
14
+ }
@@ -0,0 +1,107 @@
1
+ <script server lang="ts">
2
+ export function loader() {
3
+ return {
4
+ title: 'Welcome to Apex JS',
5
+ tagline: 'The meta-framework for Alpine.js — server-rendered, then hydrated.',
6
+ }
7
+ }
8
+ </script>
9
+
10
+ <template x-data="{ open: false }">
11
+ <main>
12
+ <h1 x-text="title"></h1>
13
+ <p class="tagline" x-text="tagline"></p>
14
+
15
+ <section class="card">
16
+ <p>
17
+ This page was rendered on the server from
18
+ <code>pages/index.alpine</code>, then hydrated by Alpine in the browser.
19
+ Edit it and save to see your changes live.
20
+ </p>
21
+
22
+ <button type="button" @click="open = !open" x-text="open ? 'Hide the details' : 'Show me how'"></button>
23
+
24
+ <div x-show="open" x-transition class="details">
25
+ <p>
26
+ The <code>loader()</code> in the server block runs on the server and
27
+ hands its data straight to Alpine's <code>x-data</code> scope — no
28
+ fetch, no boilerplate.
29
+ </p>
30
+ <p>
31
+ Next, open <code>server/api/hello.ts</code>: it's a REST endpoint
32
+ <em>and</em> an MCP tool at <code>/mcp</code> at the same time.
33
+ </p>
34
+ </div>
35
+ </section>
36
+
37
+ <p class="hint">
38
+ Run <code>apex dev --islands</code> to ship interactive islands only where
39
+ you need them.
40
+ </p>
41
+ </main>
42
+ </template>
43
+
44
+ <style scoped>
45
+ main {
46
+ max-width: 40rem;
47
+ margin: 4rem auto;
48
+ padding: 0 1.5rem;
49
+ font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
50
+ line-height: 1.6;
51
+ color: #1e293b;
52
+ }
53
+
54
+ h1 {
55
+ color: #2563eb;
56
+ font-size: 2.5rem;
57
+ margin-bottom: 0.5rem;
58
+ }
59
+
60
+ .tagline {
61
+ font-size: 1.15rem;
62
+ color: #475569;
63
+ margin-top: 0;
64
+ }
65
+
66
+ .card {
67
+ margin-top: 2rem;
68
+ padding: 1.5rem;
69
+ border: 1px solid #e2e8f0;
70
+ border-radius: 0.75rem;
71
+ background: #f8fafc;
72
+ }
73
+
74
+ button {
75
+ margin-top: 0.5rem;
76
+ padding: 0.6rem 1.1rem;
77
+ font-size: 1rem;
78
+ font-weight: 600;
79
+ color: #fff;
80
+ background: #2563eb;
81
+ border: none;
82
+ border-radius: 0.5rem;
83
+ cursor: pointer;
84
+ transition: background 0.15s ease;
85
+ }
86
+
87
+ button:hover {
88
+ background: #1d4ed8;
89
+ }
90
+
91
+ .details {
92
+ margin-top: 1rem;
93
+ }
94
+
95
+ code {
96
+ padding: 0.1rem 0.35rem;
97
+ font-size: 0.9em;
98
+ background: #e2e8f0;
99
+ border-radius: 0.35rem;
100
+ }
101
+
102
+ .hint {
103
+ margin-top: 2rem;
104
+ font-size: 0.95rem;
105
+ color: #64748b;
106
+ }
107
+ </style>
@@ -0,0 +1,10 @@
1
+ import { defineApexRoute } from 'apexjs-core'
2
+ import { z } from 'zod'
3
+
4
+ export default defineApexRoute({
5
+ method: 'GET',
6
+ description: 'Say hello to someone by name',
7
+ input: { name: z.string() },
8
+ mcp: true,
9
+ handler: ({ input }) => ({ message: `Hello, ${input.name}!` }),
10
+ })