create-apexjs 0.5.3 → 0.6.1
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
|
@@ -4,9 +4,15 @@
|
|
|
4
4
|
import { spawnSync } from "child_process";
|
|
5
5
|
import { cpSync, existsSync, readdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
6
6
|
import { basename, join, resolve } from "path";
|
|
7
|
+
import { createInterface } from "readline";
|
|
7
8
|
import { fileURLToPath } from "url";
|
|
8
9
|
import { defineCommand, runMain } from "citty";
|
|
9
10
|
var TEMPLATE_DIR = fileURLToPath(new URL("../templates/default", import.meta.url));
|
|
11
|
+
var FEATURES = [
|
|
12
|
+
{ key: "data", title: "Data & models" },
|
|
13
|
+
{ key: "auth", title: "Auth" },
|
|
14
|
+
{ key: "i18n", title: "i18n" }
|
|
15
|
+
];
|
|
10
16
|
function substituteName(dir, name) {
|
|
11
17
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
12
18
|
const p = join(dir, entry.name);
|
|
@@ -28,11 +34,21 @@ function run(cmd, cmdArgs, cwd, quiet = false) {
|
|
|
28
34
|
const res = spawnSync(cmd, cmdArgs, {
|
|
29
35
|
cwd,
|
|
30
36
|
stdio: quiet ? "ignore" : "inherit",
|
|
31
|
-
// On Windows, npm/pnpm/yarn are .cmd shims that need a shell.
|
|
37
|
+
// On Windows, npm/pnpm/yarn/npx are .cmd shims that need a shell.
|
|
32
38
|
shell: process.platform === "win32"
|
|
33
39
|
});
|
|
34
40
|
return res.status === 0;
|
|
35
41
|
}
|
|
42
|
+
function promptYesNo(question, def = false) {
|
|
43
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
44
|
+
return new Promise((res) => {
|
|
45
|
+
rl.question(`${question} ${c.dim(def ? "(Y/n)" : "(y/N)")} `, (ans) => {
|
|
46
|
+
rl.close();
|
|
47
|
+
const a = ans.trim().toLowerCase();
|
|
48
|
+
res(a === "" ? def : a === "y" || a === "yes");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
36
52
|
var c = {
|
|
37
53
|
cyan: (s) => `\x1B[36m${s}\x1B[0m`,
|
|
38
54
|
dim: (s) => `\x1B[2m${s}\x1B[0m`,
|
|
@@ -60,9 +76,21 @@ var main = defineCommand({
|
|
|
60
76
|
type: "boolean",
|
|
61
77
|
default: true,
|
|
62
78
|
description: "Initialize a git repository (use --no-git to skip)"
|
|
79
|
+
},
|
|
80
|
+
data: {
|
|
81
|
+
type: "boolean",
|
|
82
|
+
description: "Include the data/models feature (skips the prompt)"
|
|
83
|
+
},
|
|
84
|
+
auth: {
|
|
85
|
+
type: "boolean",
|
|
86
|
+
description: "Include the auth feature (skips the prompt)"
|
|
87
|
+
},
|
|
88
|
+
i18n: {
|
|
89
|
+
type: "boolean",
|
|
90
|
+
description: "Include the i18n feature (skips the prompt)"
|
|
63
91
|
}
|
|
64
92
|
},
|
|
65
|
-
run({ args }) {
|
|
93
|
+
async run({ args }) {
|
|
66
94
|
const target = resolve(process.cwd(), args.dir);
|
|
67
95
|
const name = basename(target);
|
|
68
96
|
if (existsSync(target) && readdirSync(target).length > 0) {
|
|
@@ -78,14 +106,24 @@ var main = defineCommand({
|
|
|
78
106
|
console.log(`
|
|
79
107
|
${c.cyan("Apex JS")} app created in ${args.dir}`);
|
|
80
108
|
const pm = detectPackageManager();
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
109
|
+
const selected = [];
|
|
110
|
+
let headerShown = false;
|
|
111
|
+
for (const f of FEATURES) {
|
|
112
|
+
const flag = args[f.key];
|
|
113
|
+
let want = false;
|
|
114
|
+
if (flag !== void 0) {
|
|
115
|
+
want = flag;
|
|
116
|
+
} else if (process.stdin.isTTY) {
|
|
117
|
+
if (!headerShown) {
|
|
118
|
+
console.log(
|
|
119
|
+
`
|
|
120
|
+
${c.cyan("Optional features")} ${c.dim("(add later anytime with 'apex extend <name>')")}`
|
|
121
|
+
);
|
|
122
|
+
headerShown = true;
|
|
123
|
+
}
|
|
124
|
+
want = await promptYesNo(` Add ${f.title}?`, false);
|
|
88
125
|
}
|
|
126
|
+
if (want) selected.push(f.key);
|
|
89
127
|
}
|
|
90
128
|
let installed = false;
|
|
91
129
|
if (args.install) {
|
|
@@ -104,16 +142,52 @@ var main = defineCommand({
|
|
|
104
142
|
);
|
|
105
143
|
}
|
|
106
144
|
}
|
|
145
|
+
const applied = [];
|
|
146
|
+
if (selected.length) {
|
|
147
|
+
if (installed) {
|
|
148
|
+
console.log(`
|
|
149
|
+
Adding features: ${c.cyan(selected.join(", "))}\u2026
|
|
150
|
+
`);
|
|
151
|
+
for (const key of selected) {
|
|
152
|
+
if (run("npx", ["apex", "extend", key], target)) applied.push(key);
|
|
153
|
+
else
|
|
154
|
+
console.log(
|
|
155
|
+
`
|
|
156
|
+
${c.yellow("\u26A0")} Could not add ${key} \u2014 run ${c.cyan(`npx apex extend ${key}`)} yourself.
|
|
157
|
+
`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
console.log(
|
|
162
|
+
`
|
|
163
|
+
${c.yellow("\u26A0")} Features ${selected.join(", ")} need dependencies \u2014 after installing, run: ${c.cyan(
|
|
164
|
+
selected.map((k) => `apex extend ${k}`).join(" && ")
|
|
165
|
+
)}
|
|
166
|
+
`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
let gitOk = false;
|
|
171
|
+
if (args.git) {
|
|
172
|
+
const hasGit = spawnSync("git", ["--version"], { stdio: "ignore", shell: process.platform === "win32" }).status === 0;
|
|
173
|
+
if (hasGit && run("git", ["init", "-q"], target, true)) {
|
|
174
|
+
run("git", ["add", "-A"], target, true);
|
|
175
|
+
run("git", ["commit", "-m", "Initial commit from Apex JS", "--no-gpg-sign"], target, true);
|
|
176
|
+
gitOk = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
107
179
|
const runPrefix = pm === "npm" ? "npm run" : pm;
|
|
108
180
|
const steps = [`cd ${args.dir}`];
|
|
109
181
|
if (!installed) steps.push(pm === "yarn" ? "yarn" : `${pm} install`);
|
|
110
182
|
steps.push(
|
|
111
183
|
`${runPrefix} dev ${c.dim("# start the dev server \u2192 http://localhost:3000")}`
|
|
112
184
|
);
|
|
185
|
+
const featuresNote = applied.length ? `
|
|
186
|
+
${c.green("Features added:")} ${c.cyan(applied.join(", "))} ${c.dim("(add more with 'apex extend <name>')")}` : "";
|
|
113
187
|
console.log(`
|
|
114
188
|
${installed ? c.green("Ready.") : "Next steps:"}
|
|
115
189
|
${steps.map((s) => ` ${s}`).join("\n")}
|
|
116
|
-
|
|
190
|
+
${featuresNote}
|
|
117
191
|
${c.yellow("Run the CLI with:")} ${c.cyan(`${runPrefix} dev`)} ${c.dim("(or npx apex dev)")}
|
|
118
192
|
A bare ${c.cyan("apex")} won't resolve \u2014 it's a local dependency, like ${c.cyan("next")} or ${c.cyan("vite")}.
|
|
119
193
|
${c.dim("Prefer a global command? ")}${c.cyan("npm i -g @apex-stack/core")}${c.dim(" \u2192 then `apex dev` works anywhere.")}
|
package/package.json
CHANGED
|
@@ -11,7 +11,8 @@ an MCP tool**. This file tells you (the agent) how to work here effectively.
|
|
|
11
11
|
2. **Everything has one place.** Follow the structure below; don't invent folders.
|
|
12
12
|
3. **Security is the framework's job, not yours.** Use `auth`/`can`/`scope` — never
|
|
13
13
|
hand-roll auth. Defaults are fail-closed.
|
|
14
|
-
4. **Verify your work.**
|
|
14
|
+
4. **Verify your work.** After changing code, run `apex check` (type gate) then
|
|
15
|
+
`apex test`. Add tests with `createTestApp`. Fix type errors before moving on.
|
|
15
16
|
5. **TypeScript is strict.** If it compiles, it's probably right; lean on the types.
|
|
16
17
|
|
|
17
18
|
## Do things with the CLI (this is the fast path)
|
|
@@ -26,6 +27,7 @@ apex extend data # add a DB-backed model + /guestbook demo (Sup
|
|
|
26
27
|
apex extend i18n # add locales/*.json + /fr routing
|
|
27
28
|
apex add button card modal # copy themeable UI components in
|
|
28
29
|
apex migrate # apply DB migrations
|
|
30
|
+
apex check # type-check (tsc --noEmit; fast with the native compiler)
|
|
29
31
|
apex build --preset vercel # build + Vercel config (also: netlify, docker); then deploy
|
|
30
32
|
apex test # run Vitest
|
|
31
33
|
```
|
|
@@ -79,7 +81,8 @@ The full stability contract of public APIs is in the framework's `API.md` —
|
|
|
79
81
|
|
|
80
82
|
## Drive Apex as MCP tools (optional, powerful)
|
|
81
83
|
Apex ships an MCP server for its own CLI — run `apex mcp-server` (stdio). Point your
|
|
82
|
-
agent host at it to get `apex_make`, `apex_extend`, `apex_add`, `apex_build`,
|
|
83
|
-
`
|
|
84
|
+
agent host at it to get `apex_make`, `apex_extend`, `apex_add`, `apex_build`, `apex_list`,
|
|
85
|
+
`apex_project_info`, `apex_check`, and `apex_test` as structured tools, so you scaffold,
|
|
86
|
+
type-check, and test by calling tools instead of shelling out.
|
|
84
87
|
|
|
85
88
|
Docs: https://apexjs.site/docs/
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// Dynamic route: pages/blog/[slug].alpine → /blog/:slug. The matched param
|
|
7
7
|
// arrives in loader({ params }).
|
|
8
8
|
export function loader({ params }: { params: Record<string, string> }) {
|
|
9
|
-
const post = posts.bySlug(params.slug)
|
|
9
|
+
const post = params.slug ? posts.bySlug(params.slug) : null
|
|
10
10
|
return { post: post ?? null }
|
|
11
11
|
}
|
|
12
12
|
|