create-softeneers-app 0.1.0 → 0.2.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/README.html +56 -0
- package/README.md +16 -8
- package/dist/args.js +33 -4
- package/dist/args.js.map +1 -1
- package/dist/fragments.js +127 -0
- package/dist/fragments.js.map +1 -0
- package/dist/index.js +18 -7
- package/dist/index.js.map +1 -1
- package/dist/prompts.js +27 -1
- package/dist/prompts.js.map +1 -1
- package/dist/templates.js +33 -8
- package/dist/templates.js.map +1 -1
- package/package.json +2 -2
- package/templates/express-api/.env.example +13 -0
- package/templates/express-api/README.md +77 -0
- package/templates/express-api/docker-compose.yml +15 -0
- package/templates/express-api/package.json +36 -0
- package/templates/express-api/softeneers.template.json +17 -0
- package/templates/express-api/src/auth/auth.ts +11 -0
- package/templates/express-api/src/cars/routes.ts +58 -0
- package/templates/express-api/src/cars/store.ts +92 -0
- package/templates/express-api/src/cars/types.ts +8 -0
- package/templates/express-api/src/cars/validate.ts +16 -0
- package/templates/express-api/src/db.ts +13 -0
- package/templates/express-api/src/env.ts +23 -0
- package/templates/express-api/src/index.ts +55 -0
- package/templates/express-api/src/scripts/migrate.ts +13 -0
- package/templates/express-api/src/scripts/seed.ts +25 -0
- package/templates/express-api/test/validate.test.ts +25 -0
- package/templates/express-api/tsconfig.json +14 -0
- package/templates/hono-api/.env.example +13 -0
- package/templates/hono-api/README.md +77 -0
- package/templates/hono-api/docker-compose.yml +15 -0
- package/templates/hono-api/package.json +34 -0
- package/templates/hono-api/softeneers.template.json +17 -0
- package/templates/hono-api/src/auth/auth.ts +11 -0
- package/templates/hono-api/src/cars/routes.ts +43 -0
- package/templates/hono-api/src/cars/store.ts +92 -0
- package/templates/hono-api/src/cars/types.ts +8 -0
- package/templates/hono-api/src/cars/validate.ts +16 -0
- package/templates/hono-api/src/db.ts +13 -0
- package/templates/hono-api/src/env.ts +23 -0
- package/templates/hono-api/src/index.ts +44 -0
- package/templates/hono-api/src/scripts/migrate.ts +13 -0
- package/templates/hono-api/src/scripts/seed.ts +25 -0
- package/templates/hono-api/test/validate.test.ts +25 -0
- package/templates/hono-api/tsconfig.json +14 -0
- package/templates/minimal/.env.example +2 -0
- package/templates/minimal/README.md +33 -0
- package/templates/minimal/package.json +22 -0
- package/templates/minimal/src/index.ts +20 -0
- package/templates/minimal/test/greet.test.ts +12 -0
- package/templates/minimal/tsconfig.json +15 -0
- package/templates/tanstack-start/.env.example +11 -0
- package/templates/tanstack-start/README.md +73 -0
- package/templates/tanstack-start/docker-compose.yml +15 -0
- package/templates/tanstack-start/package.json +56 -0
- package/templates/tanstack-start/public/favicon.ico +0 -0
- package/templates/tanstack-start/public/logo192.png +0 -0
- package/templates/tanstack-start/public/logo512.png +0 -0
- package/templates/tanstack-start/public/manifest.json +25 -0
- package/templates/tanstack-start/public/robots.txt +3 -0
- package/templates/tanstack-start/softeneers.template.json +17 -0
- package/templates/tanstack-start/src/cars/types.ts +8 -0
- package/templates/tanstack-start/src/cars/validate.ts +16 -0
- package/templates/tanstack-start/src/router.tsx +19 -0
- package/templates/tanstack-start/src/routes/__root.tsx +54 -0
- package/templates/tanstack-start/src/routes/api/auth/$.ts +14 -0
- package/templates/tanstack-start/src/routes/cars.tsx +87 -0
- package/templates/tanstack-start/src/routes/index.tsx +20 -0
- package/templates/tanstack-start/src/server/auth.ts +11 -0
- package/templates/tanstack-start/src/server/cars.ts +27 -0
- package/templates/tanstack-start/src/server/db.ts +12 -0
- package/templates/tanstack-start/src/server/env.ts +22 -0
- package/templates/tanstack-start/src/server/scripts/migrate.ts +13 -0
- package/templates/tanstack-start/src/server/scripts/seed.ts +25 -0
- package/templates/tanstack-start/src/server/store.ts +79 -0
- package/templates/tanstack-start/src/styles.css +17 -0
- package/templates/tanstack-start/tsconfig.json +28 -0
- package/templates/tanstack-start/tsr.config.json +3 -0
- package/templates/tanstack-start/vite.config.ts +14 -0
package/README.html
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en"><head><meta charset="utf-8">
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
4
|
+
<title>create-softeneers-app (CLI) — Softeneers Framework</title>
|
|
5
|
+
<style>
|
|
6
|
+
:root { color-scheme: light dark; }
|
|
7
|
+
* { box-sizing: border-box; }
|
|
8
|
+
body { font: 16px/1.6 -apple-system, system-ui, sans-serif; max-width: 820px;
|
|
9
|
+
margin: 0 auto; padding: 2rem 1.25rem 4rem; color: #1a1a1a; background: #fff; }
|
|
10
|
+
@media (prefers-color-scheme: dark) { body { color: #e6e6e6; background: #141414; } }
|
|
11
|
+
.bar { display: flex; flex-wrap: wrap; gap: .9rem; align-items: baseline;
|
|
12
|
+
padding-bottom: 1rem; margin-bottom: 1.5rem; border-bottom: 1px solid #8884; font-size: .9rem; }
|
|
13
|
+
.bar .spacer { flex: 1; }
|
|
14
|
+
.bar a { text-decoration: none; }
|
|
15
|
+
.src { color: #8889; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .85rem; }
|
|
16
|
+
h1, h2, h3 { line-height: 1.25; }
|
|
17
|
+
h2 { margin-top: 2rem; border-bottom: 1px solid #8883; padding-bottom: .3rem; }
|
|
18
|
+
a { color: #2563eb; } @media (prefers-color-scheme: dark) { a { color: #6ea8fe; } }
|
|
19
|
+
code { background: #8882; padding: .1em .35em; border-radius: 4px; font-size: .9em; }
|
|
20
|
+
pre { background: #8881; padding: 1rem; border-radius: 8px; overflow-x: auto; }
|
|
21
|
+
pre code { background: none; padding: 0; }
|
|
22
|
+
table { border-collapse: collapse; width: 100%; margin: 1rem 0; font-size: .92rem; }
|
|
23
|
+
th, td { border: 1px solid #8884; padding: .4rem .6rem; text-align: left; }
|
|
24
|
+
blockquote { border-left: 3px solid #8886; margin: 1rem 0; padding: .2rem 1rem; color: #8889; }
|
|
25
|
+
footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #8884; font-size: .85rem; color: #8889; }
|
|
26
|
+
.groups { display: grid; gap: 1.5rem; }
|
|
27
|
+
.group h2 { margin-top: 1.5rem; }
|
|
28
|
+
.group ul { list-style: none; padding: 0; margin: .5rem 0; }
|
|
29
|
+
.group li { margin: .35rem 0; }
|
|
30
|
+
.group .meta { color: #8889; font-size: .85rem; }
|
|
31
|
+
</style></head>
|
|
32
|
+
<body>
|
|
33
|
+
<div class="bar"><a href="../../index.html">← All docs</a><span class="spacer"></span><a class="src" href="./README.md">view README.md source</a></div>
|
|
34
|
+
<main><h1>create-softeneers-app</h1>
|
|
35
|
+
<p>The Softeneers Framework project generator. Run it to scaffold a new project from one of the bundled templates:</p>
|
|
36
|
+
<pre><code>npx create-softeneers-app@latest my-app</code></pre>
|
|
37
|
+
<h2>Templates</h2>
|
|
38
|
+
<p>Five templates, each for a different kind of user, with composable toggles:</p>
|
|
39
|
+
<table><thead><tr><th>Template</th><th>What you get</th><th>Toggles</th></tr></thead><tbody>
|
|
40
|
+
<tr><td><code>next-fullstack</code></td><td>Next.js web + Express/Sequelize/MySQL API</td><td>(all-in)</td></tr>
|
|
41
|
+
<tr><td><code>express-api</code></td><td>Express 5 + TypeScript REST API (cars CRUD)</td><td>db · auth · docker</td></tr>
|
|
42
|
+
<tr><td><code>hono-api</code></td><td>Hono + TypeScript API (cars CRUD)</td><td>db · auth · docker</td></tr>
|
|
43
|
+
<tr><td><code>tanstack-start</code></td><td>TanStack Start fullstack React app</td><td>db · auth · docker</td></tr>
|
|
44
|
+
<tr><td><code>minimal</code></td><td>Zero-framework Node + TypeScript starter</td><td>(none)</td></tr>
|
|
45
|
+
</tbody></table>
|
|
46
|
+
<pre><code>npx create-softeneers-app@latest my-app # interactive
|
|
47
|
+
npx create-softeneers-app@latest api -t express-api --yes # all defaults
|
|
48
|
+
npx create-softeneers-app@latest api -t hono-api --no-auth --no-docker</code></pre>
|
|
49
|
+
<p>Flags: <code>--template <slug></code>, <code>--yes</code>/<code>-y</code>, <code>--db</code>/<code>--no-db</code>, <code>--auth</code>/<code>--no-auth</code>, <code>--docker</code>/<code>--no-docker</code>, <code>--no-install</code>, <code>--no-git</code>, <code>--pm <pnpm|npm|yarn></code>, <code>--help</code>, <code>--version</code>. See <a href="../../docs/CLI-SPEC.html"><code>../../docs/CLI-SPEC.md</code></a> for the full contract.</p>
|
|
50
|
+
<h2>Local development</h2>
|
|
51
|
+
<pre><code>npm run build -w create-softeneers-app # tsc + bundle templates → dist/ + templates/
|
|
52
|
+
node apps/cli/dist/index.js my-app --yes # run the built CLI
|
|
53
|
+
npm run dev -w create-softeneers-app -- my-app # run from source via tsx</code></pre></main>
|
|
54
|
+
<footer>Softeneers Framework — human view generated from the canonical Markdown.
|
|
55
|
+
Do not edit by hand; run <code>npm run build</code> (the <code>.md</code> is the source of truth).</footer>
|
|
56
|
+
</body></html>
|
package/README.md
CHANGED
|
@@ -7,19 +7,27 @@ from one of the bundled templates:
|
|
|
7
7
|
npx create-softeneers-app@latest my-app
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Templates
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
Five templates, each for a different kind of user, with composable toggles:
|
|
13
|
+
|
|
14
|
+
| Template | What you get | Toggles |
|
|
15
|
+
| ---------------- | ----------------------------------------- | ------------------ |
|
|
16
|
+
| `next-fullstack` | Next.js web + Express/Sequelize/MySQL API | (all-in) |
|
|
17
|
+
| `express-api` | Express 5 + TypeScript REST API (cars CRUD) | db · auth · docker |
|
|
18
|
+
| `hono-api` | Hono + TypeScript API (cars CRUD) | db · auth · docker |
|
|
19
|
+
| `tanstack-start` | TanStack Start fullstack React app | db · auth · docker |
|
|
20
|
+
| `minimal` | Zero-framework Node + TypeScript starter | (none) |
|
|
15
21
|
|
|
16
22
|
```bash
|
|
17
|
-
npx create-softeneers-app@latest my-app
|
|
18
|
-
npx create-softeneers-app@latest
|
|
23
|
+
npx create-softeneers-app@latest my-app # interactive
|
|
24
|
+
npx create-softeneers-app@latest api -t express-api --yes # all defaults
|
|
25
|
+
npx create-softeneers-app@latest api -t hono-api --no-auth --no-docker
|
|
19
26
|
```
|
|
20
27
|
|
|
21
|
-
Flags: `--template <slug>`, `--yes`/`-y`, `--no-
|
|
22
|
-
`--pm <pnpm|npm|yarn>`,
|
|
28
|
+
Flags: `--template <slug>`, `--yes`/`-y`, `--db`/`--no-db`, `--auth`/`--no-auth`,
|
|
29
|
+
`--docker`/`--no-docker`, `--no-install`, `--no-git`, `--pm <pnpm|npm|yarn>`,
|
|
30
|
+
`--help`, `--version`. See
|
|
23
31
|
[`../../docs/CLI-SPEC.md`](../../docs/CLI-SPEC.md) for the full contract.
|
|
24
32
|
|
|
25
33
|
## Local development
|
package/dist/args.js
CHANGED
|
@@ -36,6 +36,24 @@ export function parseArgs(argv) {
|
|
|
36
36
|
case "--no-git":
|
|
37
37
|
opts.git = false;
|
|
38
38
|
break;
|
|
39
|
+
case "--db":
|
|
40
|
+
opts.db = true;
|
|
41
|
+
break;
|
|
42
|
+
case "--no-db":
|
|
43
|
+
opts.db = false;
|
|
44
|
+
break;
|
|
45
|
+
case "--auth":
|
|
46
|
+
opts.auth = true;
|
|
47
|
+
break;
|
|
48
|
+
case "--no-auth":
|
|
49
|
+
opts.auth = false;
|
|
50
|
+
break;
|
|
51
|
+
case "--docker":
|
|
52
|
+
opts.docker = true;
|
|
53
|
+
break;
|
|
54
|
+
case "--no-docker":
|
|
55
|
+
opts.docker = false;
|
|
56
|
+
break;
|
|
39
57
|
case "--help":
|
|
40
58
|
case "-h":
|
|
41
59
|
opts.help = true;
|
|
@@ -80,17 +98,28 @@ Usage:
|
|
|
80
98
|
npx create-softeneers-app@latest [directory|.] [options]
|
|
81
99
|
|
|
82
100
|
Options:
|
|
83
|
-
-t, --template <name> Template to use (default: prompt
|
|
101
|
+
-t, --template <name> Template to use (default: prompt)
|
|
84
102
|
-y, --yes Accept defaults, no prompts
|
|
103
|
+
--db / --no-db Include a database layer (template default if unset)
|
|
104
|
+
--auth / --no-auth Include authentication (template default if unset)
|
|
105
|
+
--docker / --no-docker Include a Docker recipe (template default if unset)
|
|
85
106
|
--no-install Don't install dependencies
|
|
86
107
|
--no-git Don't run "git init"
|
|
87
108
|
--pm <npm|pnpm|yarn> Package manager (default: auto-detected, else npm)
|
|
88
109
|
-h, --help Show this help
|
|
89
110
|
-v, --version Show version
|
|
90
111
|
|
|
112
|
+
Templates:
|
|
113
|
+
next-fullstack Next.js web + Express/Sequelize/MySQL API (db, auth, docker)
|
|
114
|
+
express-api Express + TypeScript REST API (db, auth, docker)
|
|
115
|
+
hono-api Hono + TypeScript API (db, auth, docker)
|
|
116
|
+
tanstack-start TanStack Start fullstack React app (db, auth, docker)
|
|
117
|
+
minimal Zero-framework Node + TypeScript starter (no toggles)
|
|
118
|
+
|
|
91
119
|
Examples:
|
|
92
|
-
npx create-softeneers-app@latest my-app
|
|
93
|
-
npx create-softeneers-app@latest
|
|
94
|
-
|
|
120
|
+
npx create-softeneers-app@latest my-app # interactive
|
|
121
|
+
npx create-softeneers-app@latest api -t express-api --yes # all defaults
|
|
122
|
+
npx create-softeneers-app@latest api -t hono-api --no-auth --no-docker
|
|
123
|
+
npx create-softeneers-app@latest . # current directory
|
|
95
124
|
`;
|
|
96
125
|
//# sourceMappingURL=args.js.map
|
package/dist/args.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA,uEAAuE;
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Fragment/toggle engine. A template may ship a `softeneers.template.json`
|
|
2
|
+
// manifest declaring optional features (db / auth / docker). When a feature is
|
|
3
|
+
// toggled OFF, the scaffold removes that fragment's files, package.json deps and
|
|
4
|
+
// scripts, and strips `#if`/`#endif` marker blocks from text files. Templates
|
|
5
|
+
// without a manifest (e.g. `minimal`) support no toggles and are copied as-is.
|
|
6
|
+
import { basename, join } from "node:path";
|
|
7
|
+
import { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
8
|
+
export const TOGGLE_KEYS = ["db", "auth", "docker"];
|
|
9
|
+
const MANIFEST_NAME = "softeneers.template.json";
|
|
10
|
+
export function readManifest(dir) {
|
|
11
|
+
const p = join(dir, MANIFEST_NAME);
|
|
12
|
+
if (!existsSync(p))
|
|
13
|
+
return null;
|
|
14
|
+
return JSON.parse(readFileSync(p, "utf8"));
|
|
15
|
+
}
|
|
16
|
+
/** Supported toggles + their default values (empty object when no manifest). */
|
|
17
|
+
export function templateToggleDefaults(dir) {
|
|
18
|
+
return readManifest(dir)?.toggles ?? {};
|
|
19
|
+
}
|
|
20
|
+
const TEXT_EXT = new Set([
|
|
21
|
+
"ts", "tsx", "js", "jsx", "mjs", "cjs", "md", "mdx", "yml", "yaml",
|
|
22
|
+
"css", "scss", "html", "txt", "sh", "env", "example", "toml", "gitignore",
|
|
23
|
+
]);
|
|
24
|
+
function isTextFile(name) {
|
|
25
|
+
if (name === "Dockerfile" || name.startsWith(".env"))
|
|
26
|
+
return true;
|
|
27
|
+
const ext = name.includes(".") ? name.split(".").pop() : "";
|
|
28
|
+
return TEXT_EXT.has(ext);
|
|
29
|
+
}
|
|
30
|
+
function walkFiles(dir, out = []) {
|
|
31
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
32
|
+
if (entry.name === "node_modules" || entry.name === ".git")
|
|
33
|
+
continue;
|
|
34
|
+
const full = join(dir, entry.name);
|
|
35
|
+
if (entry.isDirectory())
|
|
36
|
+
walkFiles(full, out);
|
|
37
|
+
else
|
|
38
|
+
out.push(full);
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Strip conditional blocks from a text file. A block opens with a line
|
|
44
|
+
* containing `#if <key>` or `#if !<key>` and closes with `#endif` (the comment
|
|
45
|
+
* style around them is irrelevant — `//`, `#`, `<!-- -->` all work). The marker
|
|
46
|
+
* lines themselves are always removed; the body is kept only if the condition
|
|
47
|
+
* holds. Blocks nest.
|
|
48
|
+
*/
|
|
49
|
+
export function processMarkers(content, toggles) {
|
|
50
|
+
const lines = content.split("\n");
|
|
51
|
+
const out = [];
|
|
52
|
+
const stack = [];
|
|
53
|
+
const emitting = () => stack.every(Boolean);
|
|
54
|
+
for (const line of lines) {
|
|
55
|
+
const open = line.match(/#if\s+(!)?\s*([A-Za-z_]\w*)/);
|
|
56
|
+
if (open) {
|
|
57
|
+
const negated = open[1] === "!";
|
|
58
|
+
const value = toggles[open[2]] ?? false;
|
|
59
|
+
stack.push(negated ? !value : value);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (/#endif/.test(line)) {
|
|
63
|
+
stack.pop();
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (emitting())
|
|
67
|
+
out.push(line);
|
|
68
|
+
}
|
|
69
|
+
return out.join("\n");
|
|
70
|
+
}
|
|
71
|
+
function prunePackageJson(file, removeDeps, removeScripts) {
|
|
72
|
+
const pkg = JSON.parse(readFileSync(file, "utf8"));
|
|
73
|
+
for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
|
|
74
|
+
const deps = pkg[field];
|
|
75
|
+
if (deps)
|
|
76
|
+
for (const name of removeDeps)
|
|
77
|
+
delete deps[name];
|
|
78
|
+
}
|
|
79
|
+
if (pkg.scripts)
|
|
80
|
+
for (const name of removeScripts)
|
|
81
|
+
delete pkg.scripts[name];
|
|
82
|
+
writeFileSync(file, JSON.stringify(pkg, null, 2) + "\n");
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Apply the resolved toggles to a freshly-copied template: delete OFF fragments'
|
|
86
|
+
* paths, prune their deps/scripts from every package.json, strip marker blocks,
|
|
87
|
+
* and remove the manifest from the generated project.
|
|
88
|
+
*/
|
|
89
|
+
export function applyToggles(targetDir, toggles) {
|
|
90
|
+
const manifest = readManifest(targetDir);
|
|
91
|
+
const removeDeps = new Set();
|
|
92
|
+
const removeScripts = new Set();
|
|
93
|
+
if (manifest?.fragments) {
|
|
94
|
+
for (const key of TOGGLE_KEYS) {
|
|
95
|
+
if (toggles[key])
|
|
96
|
+
continue; // only prune a fragment when its toggle is OFF
|
|
97
|
+
const fragment = manifest.fragments[key];
|
|
98
|
+
if (!fragment)
|
|
99
|
+
continue;
|
|
100
|
+
for (const rel of fragment.removePaths ?? []) {
|
|
101
|
+
rmSync(join(targetDir, rel), { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
for (const dep of fragment.removeDeps ?? [])
|
|
104
|
+
removeDeps.add(dep);
|
|
105
|
+
for (const script of fragment.removeScripts ?? [])
|
|
106
|
+
removeScripts.add(script);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const file of walkFiles(targetDir)) {
|
|
110
|
+
const name = basename(file);
|
|
111
|
+
if (name === MANIFEST_NAME)
|
|
112
|
+
continue;
|
|
113
|
+
if (name === "package.json") {
|
|
114
|
+
if (removeDeps.size || removeScripts.size)
|
|
115
|
+
prunePackageJson(file, removeDeps, removeScripts);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (!isTextFile(name))
|
|
119
|
+
continue;
|
|
120
|
+
const content = readFileSync(file, "utf8");
|
|
121
|
+
if (!content.includes("#if") && !content.includes("#endif"))
|
|
122
|
+
continue;
|
|
123
|
+
writeFileSync(file, processMarkers(content, toggles));
|
|
124
|
+
}
|
|
125
|
+
rmSync(join(targetDir, MANIFEST_NAME), { force: true });
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=fragments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fragments.js","sourceRoot":"","sources":["../src/fragments.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,8 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
import { basename, resolve } from "node:path";
|
|
4
4
|
import { intro, log, note, outro, spinner } from "@clack/prompts";
|
|
5
5
|
import { CliError, HELP_TEXT, parseArgs } from "./args.js";
|
|
6
|
-
import {
|
|
6
|
+
import { applyToggles, templateToggleDefaults } from "./fragments.js";
|
|
7
|
+
import { promptProjectName, promptTemplate, promptToggles } from "./prompts.js";
|
|
7
8
|
import { assertTargetUsable, copyTemplate, gitInit, installDeps, toPackageName, transform, } from "./scaffold.js";
|
|
8
9
|
import { DEFAULT_TEMPLATE, findTemplate, resolveTemplateDir } from "./templates.js";
|
|
9
10
|
function version() {
|
|
@@ -44,12 +45,23 @@ async function main() {
|
|
|
44
45
|
if (!templateDir) {
|
|
45
46
|
throw new CliError(`Could not locate template files for "${slug}".`);
|
|
46
47
|
}
|
|
47
|
-
// 3.
|
|
48
|
+
// 3. Feature toggles (db / auth / docker) — only those the template supports.
|
|
49
|
+
const toggleDefaults = templateToggleDefaults(templateDir);
|
|
50
|
+
const overrides = { db: opts.db, auth: opts.auth, docker: opts.docker };
|
|
51
|
+
const toggles = opts.yes
|
|
52
|
+
? {
|
|
53
|
+
db: overrides.db ?? toggleDefaults.db ?? false,
|
|
54
|
+
auth: overrides.auth ?? toggleDefaults.auth ?? false,
|
|
55
|
+
docker: overrides.docker ?? toggleDefaults.docker ?? false,
|
|
56
|
+
}
|
|
57
|
+
: await promptToggles(toggleDefaults, overrides);
|
|
58
|
+
// 4. Generate.
|
|
48
59
|
assertTargetUsable(targetDir);
|
|
49
60
|
const pkgName = toPackageName(targetDir);
|
|
50
61
|
const s = spinner();
|
|
51
62
|
s.start(`Creating ${projectName}`);
|
|
52
63
|
copyTemplate(templateDir, targetDir);
|
|
64
|
+
applyToggles(targetDir, toggles);
|
|
53
65
|
transform(targetDir, projectName, pkgName, opts.packageManager);
|
|
54
66
|
s.stop(`Created ${projectName}`);
|
|
55
67
|
if (opts.git) {
|
|
@@ -68,18 +80,17 @@ async function main() {
|
|
|
68
80
|
inst.stop("Dependency install failed — run it manually.");
|
|
69
81
|
}
|
|
70
82
|
}
|
|
71
|
-
//
|
|
83
|
+
// 5. Next steps — conditional on the toggles that ended up enabled.
|
|
72
84
|
const pm = opts.packageManager;
|
|
73
85
|
const steps = [
|
|
74
86
|
...(inCurrentDir ? [] : [`cd ${rawTarget}`]),
|
|
75
87
|
...(opts.install ? [] : [`${pm} install`]),
|
|
76
|
-
"docker compose up -d # start
|
|
77
|
-
`${pm} run db:migrate`,
|
|
78
|
-
`${pm} run db:seed`,
|
|
88
|
+
...(toggles.docker ? ["docker compose up -d # start services"] : []),
|
|
89
|
+
...(toggles.db ? [`${pm} run db:migrate`, `${pm} run db:seed`] : []),
|
|
79
90
|
`${pm} run dev`,
|
|
80
91
|
].join("\n");
|
|
81
92
|
note(steps, "Next steps");
|
|
82
|
-
outro(
|
|
93
|
+
outro(template.outro ?? "Happy hacking!");
|
|
83
94
|
}
|
|
84
95
|
main().catch((err) => {
|
|
85
96
|
if (err instanceof CliError) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAElE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAElE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAgB,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,OAAO,EACP,WAAW,EACX,aAAa,EACb,SAAS,GACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpF,SAAS,OAAO;IACd,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,OAAQ,OAAO,CAAC,iBAAiB,CAAyB,CAAC,OAAO,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvB,OAAO;IACT,CAAC;IACD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACvB,OAAO;IACT,CAAC;IAED,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAE/B,6FAA6F;IAC7F,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,iBAAiB,EAAE,CAAC,CAAC;IACtF,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;IACpD,MAAM,YAAY,GAAG,SAAS,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC;IACjD,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IAExC,eAAe;IACf,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,cAAc,EAAE,CAAC,CAAC;IACrF,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,QAAQ,CAAC,qBAAqB,IAAI,IAAI,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,QAAQ,CAAC,aAAa,IAAI,+CAA+C,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,WAAW,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,QAAQ,CAAC,wCAAwC,IAAI,IAAI,CAAC,CAAC;IACvE,CAAC;IAED,8EAA8E;IAC9E,MAAM,cAAc,GAAG,sBAAsB,CAAC,WAAW,CAAC,CAAC;IAC3D,MAAM,SAAS,GAAqB,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAC1F,MAAM,OAAO,GAAY,IAAI,CAAC,GAAG;QAC/B,CAAC,CAAC;YACE,EAAE,EAAE,SAAS,CAAC,EAAE,IAAI,cAAc,CAAC,EAAE,IAAI,KAAK;YAC9C,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,cAAc,CAAC,IAAI,IAAI,KAAK;YACpD,MAAM,EAAE,SAAS,CAAC,MAAM,IAAI,cAAc,CAAC,MAAM,IAAI,KAAK;SAC3D;QACH,CAAC,CAAC,MAAM,aAAa,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;IAEnD,eAAe;IACf,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC9B,MAAM,OAAO,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAEzC,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IACpB,CAAC,CAAC,KAAK,CAAC,YAAY,WAAW,EAAE,CAAC,CAAC;IACnC,YAAY,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACrC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,CAAC,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAChE,CAAC,CAAC,IAAI,CAAC,WAAW,WAAW,EAAE,CAAC,CAAC;IAEjC,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,IAAI,OAAO,CAAC,SAAS,CAAC;YAAE,GAAG,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC;;YAChE,GAAG,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC,gCAAgC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;QAClE,IAAI,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;YAChD,IAAI,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,oEAAoE;IACpE,MAAM,EAAE,GAAG,IAAI,CAAC,cAAc,CAAC;IAC/B,MAAM,KAAK,GAAG;QACZ,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,SAAS,EAAE,CAAC,CAAC;QAC5C,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAC1C,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,8CAA8C,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,iBAAiB,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,GAAG,EAAE,UAAU;KAChB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAC1B,KAAK,CAAC,QAAQ,CAAC,KAAK,IAAI,gBAAgB,CAAC,CAAC;AAC5C,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACzC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;QAC1C,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
package/dist/prompts.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { cancel, isCancel, select, text } from "@clack/prompts";
|
|
1
|
+
import { cancel, confirm, isCancel, select, text } from "@clack/prompts";
|
|
2
|
+
import {} from "./fragments.js";
|
|
2
3
|
import { DEFAULT_TEMPLATE, TEMPLATES } from "./templates.js";
|
|
3
4
|
function bail(value) {
|
|
4
5
|
if (isCancel(value)) {
|
|
@@ -28,4 +29,29 @@ export async function promptTemplate() {
|
|
|
28
29
|
});
|
|
29
30
|
return bail(value);
|
|
30
31
|
}
|
|
32
|
+
const TOGGLE_LABELS = {
|
|
33
|
+
db: "Include a database? (CRUD persisted to MySQL via Sequelize)",
|
|
34
|
+
auth: "Include authentication? (email + password via better-auth)",
|
|
35
|
+
docker: "Include a Docker recipe? (docker-compose for local services)",
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Ask for each toggle the template supports (a key present in `defaults`).
|
|
39
|
+
* A toggle already fixed on the command line (`overrides[key] !== undefined`)
|
|
40
|
+
* skips its prompt. Unsupported toggles resolve to `false`.
|
|
41
|
+
*/
|
|
42
|
+
export async function promptToggles(defaults, overrides) {
|
|
43
|
+
const result = { db: false, auth: false, docker: false };
|
|
44
|
+
for (const key of Object.keys(defaults)) {
|
|
45
|
+
if (overrides[key] !== undefined) {
|
|
46
|
+
result[key] = overrides[key];
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const value = await confirm({
|
|
50
|
+
message: TOGGLE_LABELS[key],
|
|
51
|
+
initialValue: defaults[key] ?? false,
|
|
52
|
+
});
|
|
53
|
+
result[key] = bail(value);
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
31
57
|
//# sourceMappingURL=prompts.js.map
|
package/dist/prompts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEzE,OAAO,EAAgC,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE7D,SAAS,IAAI,CAAI,KAAiB;IAChC,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,MAAM,CAAC,YAAY,CAAC,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,KAAU,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAgB;IACtD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC;QACvB,OAAO,EAAE,eAAe;QACxB,WAAW,EAAE,QAAQ;QACrB,YAAY,EAAE,OAAO;QACrB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,SAAS,CAAC;KACtF,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC;QACzB,OAAO,EAAE,mCAAmC;QAC5C,YAAY,EAAE,gBAAgB;QAC9B,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC7B,KAAK,EAAE,CAAC,CAAC,IAAI;YACb,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,sBAAsB;SAC7D,CAAC,CAAC;KACJ,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,aAAa,GAA8B;IAC/C,EAAE,EAAE,6DAA6D;IACjE,IAAI,EAAE,4DAA4D;IAClE,MAAM,EAAE,8DAA8D;CACvE,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,QAA0B,EAC1B,SAA2B;IAE3B,MAAM,MAAM,GAAY,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAClE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAgB,EAAE,CAAC;QACvD,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;YACjC,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAE,CAAC;YAC9B,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC;YAC1B,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC;YAC3B,YAAY,EAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK;SACrC,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/templates.js
CHANGED
|
@@ -2,21 +2,46 @@ import { existsSync, statSync } from "node:fs";
|
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
/**
|
|
5
|
-
* The template registry.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* The template registry. Each entry targets a different kind of user; the
|
|
6
|
+
* db/auth/docker toggles (where supported) are declared per template in its
|
|
7
|
+
* `softeneers.template.json` manifest.
|
|
8
8
|
*/
|
|
9
9
|
export const TEMPLATES = [
|
|
10
10
|
{
|
|
11
11
|
slug: "next-fullstack",
|
|
12
12
|
label: "Fullstack (Next.js + Express + Sequelize + MySQL)",
|
|
13
|
-
hint: "web + server monorepo
|
|
13
|
+
hint: "web + server monorepo",
|
|
14
14
|
available: true,
|
|
15
|
+
outro: "Web: http://localhost:3000 API: http://localhost:4000",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
slug: "express-api",
|
|
19
|
+
label: "Express API (TypeScript REST)",
|
|
20
|
+
hint: "API only · db/auth/docker toggles",
|
|
21
|
+
available: true,
|
|
22
|
+
outro: "API: http://localhost:4000",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
slug: "hono-api",
|
|
26
|
+
label: "Hono API (TypeScript, fast)",
|
|
27
|
+
hint: "API only · db/auth/docker toggles",
|
|
28
|
+
available: true,
|
|
29
|
+
outro: "API: http://localhost:4000",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
slug: "tanstack-start",
|
|
33
|
+
label: "TanStack Start (fullstack React)",
|
|
34
|
+
hint: "app + server routes · db/auth/docker toggles",
|
|
35
|
+
available: true,
|
|
36
|
+
outro: "App: http://localhost:3000",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
slug: "minimal",
|
|
40
|
+
label: "Minimal (Node + TypeScript starter)",
|
|
41
|
+
hint: "zero-framework blank slate",
|
|
42
|
+
available: true,
|
|
43
|
+
outro: "Run: npm run dev",
|
|
15
44
|
},
|
|
16
|
-
{ slug: "tanstack-start", label: "TanStack Start", hint: "coming soon", available: false },
|
|
17
|
-
{ slug: "hono-api", label: "Hono API only", hint: "coming soon", available: false },
|
|
18
|
-
{ slug: "express-api", label: "Express API only", hint: "coming soon", available: false },
|
|
19
|
-
{ slug: "minimal", label: "Minimal", hint: "coming soon", available: false },
|
|
20
45
|
];
|
|
21
46
|
export const DEFAULT_TEMPLATE = "next-fullstack";
|
|
22
47
|
export function findTemplate(slug) {
|
package/dist/templates.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAYzC;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAmB;IACvC;QACE,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,mDAAmD;QAC1D,IAAI,EAAE,uBAAuB;QAC7B,SAAS,EAAE,IAAI;QACf,KAAK,EAAE,yDAAyD;KACjE;IACD;QACE,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,+BAA+B;QACtC,IAAI,EAAE,mCAAmC;QACzC,SAAS,EAAE,IAAI;QACf,KAAK,EAAE,4BAA4B;KACpC;IACD;QACE,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,6BAA6B;QACpC,IAAI,EAAE,mCAAmC;QACzC,SAAS,EAAE,IAAI;QACf,KAAK,EAAE,4BAA4B;KACpC;IACD;QACE,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,kCAAkC;QACzC,IAAI,EAAE,8CAA8C;QACpD,SAAS,EAAE,IAAI;QACf,KAAK,EAAE,4BAA4B;KACpC;IACD;QACE,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,qCAAqC;QAC5C,IAAI,EAAE,4BAA4B;QAClC,SAAS,EAAE,IAAI;QACf,KAAK,EAAE,kBAAkB;KAC1B;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,gBAAgB,CAAC;AAEjD,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAChD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,8BAA8B;IACpF,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,WAAW;IAChD,MAAM,UAAU,GAAG;QACjB,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC;QACnC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC;KAChD,CAAC;IACF,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAClF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-softeneers-app",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Scaffold a new Softeneers Framework project
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Scaffold a new Softeneers Framework project: 5 templates (next-fullstack, express-api, hono-api, tanstack-start, minimal) with db/auth/docker toggles.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"create-softeneers-app": "dist/index.js"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
PORT=4000
|
|
2
|
+
CORS_ORIGIN=http://localhost:3000
|
|
3
|
+
# #if db
|
|
4
|
+
DB_HOST=127.0.0.1
|
|
5
|
+
DB_PORT=3306
|
|
6
|
+
DB_NAME=app_dev
|
|
7
|
+
DB_USER=root
|
|
8
|
+
DB_PASSWORD=
|
|
9
|
+
# #endif
|
|
10
|
+
# #if auth
|
|
11
|
+
AUTH_SECRET=dev-secret-change-me-to-a-long-random-string
|
|
12
|
+
AUTH_BASE_URL=http://localhost:4000
|
|
13
|
+
# #endif
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
An **Express 5 + TypeScript** REST API generated by
|
|
4
|
+
[`create-softeneers-app`](https://www.npmjs.com/package/create-softeneers-app),
|
|
5
|
+
with a full cars CRUD example.
|
|
6
|
+
|
|
7
|
+
## Scripts
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm run dev # watch-run src/index.ts (tsx)
|
|
11
|
+
npm run build # type-check + emit dist/ (tsc)
|
|
12
|
+
npm start # run the built server
|
|
13
|
+
npm run typecheck # tsc --noEmit
|
|
14
|
+
npm test # node:test suite (via tsx)
|
|
15
|
+
# #if db
|
|
16
|
+
npm run db:migrate # create/sync tables
|
|
17
|
+
npm run db:seed # insert sample cars
|
|
18
|
+
npm run db:reset # drop, recreate, reseed
|
|
19
|
+
# #endif
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## API
|
|
23
|
+
|
|
24
|
+
| Method | Path | Description |
|
|
25
|
+
| ------ | ---------------- | ------------------ |
|
|
26
|
+
| GET | `/health` | Liveness check |
|
|
27
|
+
| GET | `/api/cars` | List cars |
|
|
28
|
+
| GET | `/api/cars/:id` | Get one car |
|
|
29
|
+
| POST | `/api/cars` | Create a car |
|
|
30
|
+
| PUT | `/api/cars/:id` | Update a car |
|
|
31
|
+
| DELETE | `/api/cars/:id` | Delete a car |
|
|
32
|
+
# #if auth
|
|
33
|
+
| ALL | `/api/auth/*` | better-auth routes |
|
|
34
|
+
# #endif
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
curl localhost:4000/api/cars
|
|
38
|
+
curl -X POST localhost:4000/api/cars -H 'content-type: application/json' \
|
|
39
|
+
-d '{"brand":"Tesla","model":"Model 3","year":2023}'
|
|
40
|
+
```
|
|
41
|
+
# #if db
|
|
42
|
+
|
|
43
|
+
## Database
|
|
44
|
+
|
|
45
|
+
Persistence is MySQL via Sequelize ([`@softeneers/db`](https://www.npmjs.com/package/@softeneers/db)).
|
|
46
|
+
Configure `DB_*` in `.env`, then:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# #if docker
|
|
50
|
+
docker compose up -d # start MySQL
|
|
51
|
+
# #endif
|
|
52
|
+
npm run db:migrate && npm run db:seed
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Toggle the database off at generation time (`--no-db`) to swap in a dependency-free
|
|
56
|
+
in-memory store — the CRUD API behaves identically.
|
|
57
|
+
# #endif
|
|
58
|
+
# #if auth
|
|
59
|
+
|
|
60
|
+
## Authentication
|
|
61
|
+
|
|
62
|
+
Email + password auth via better-auth ([`@softeneers/auth`](https://www.npmjs.com/package/@softeneers/auth)),
|
|
63
|
+
mounted at `/api/auth/*`. Set a strong `AUTH_SECRET` in `.env` before deploying.
|
|
64
|
+
# #endif
|
|
65
|
+
|
|
66
|
+
## Getting started
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm install
|
|
70
|
+
# #if docker
|
|
71
|
+
docker compose up -d
|
|
72
|
+
# #endif
|
|
73
|
+
# #if db
|
|
74
|
+
npm run db:migrate && npm run db:seed
|
|
75
|
+
# #endif
|
|
76
|
+
npm run dev
|
|
77
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
services:
|
|
2
|
+
db:
|
|
3
|
+
image: mysql:8
|
|
4
|
+
restart: unless-stopped
|
|
5
|
+
environment:
|
|
6
|
+
MYSQL_DATABASE: ${DB_NAME:-app_dev}
|
|
7
|
+
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-}
|
|
8
|
+
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
|
9
|
+
ports:
|
|
10
|
+
- "${DB_PORT:-3306}:3306"
|
|
11
|
+
volumes:
|
|
12
|
+
- db_data:/var/lib/mysql
|
|
13
|
+
|
|
14
|
+
volumes:
|
|
15
|
+
db_data:
|