@asterflow/cli 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/README.md +52 -0
- package/dist/cjs/index.cjs +637 -0
- package/dist/cjs/package.json +3 -0
- package/dist/mjs/index.js +644 -0
- package/dist/mjs/package.json +3 -0
- package/dist/types/commands/add.d.ts +5 -0
- package/dist/types/commands/generate.d.ts +6 -0
- package/dist/types/commands/init.d.ts +16 -0
- package/dist/types/commands/list.d.ts +1 -0
- package/dist/types/generators/appEntry.d.ts +8 -0
- package/dist/types/generators/packageJson.d.ts +8 -0
- package/dist/types/generators/routes.d.ts +9 -0
- package/dist/types/helpers/copyTemplate.d.ts +4 -0
- package/dist/types/helpers/git.d.ts +2 -0
- package/dist/types/helpers/isFolderEmpty.d.ts +2 -0
- package/dist/types/helpers/isWriteable.d.ts +1 -0
- package/dist/types/helpers/validateProjectName.d.ts +7 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/registry/adapters.d.ts +16 -0
- package/dist/types/registry/plugins.d.ts +14 -0
- package/dist/types/registry/templates.d.ts +11 -0
- package/dist/types/utils/log.d.ts +7 -0
- package/dist/types/utils/packageManager.d.ts +7 -0
- package/package.json +57 -0
- package/templates/gitignore +3 -0
- package/templates/routes/global.d.ts +7 -0
- package/templates/routes/index.ts +7 -0
- package/templates/routes/tsconfig.json +10 -0
- package/templates/routes/upload.ts +15 -0
- package/templates/tsconfig.json +17 -0
- package/tsconfig.json +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# @asterflow/cli
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
> CLI for scaffolding AsterFlow projects, adding plugins, and generating the static fs-routing manifest.
|
|
14
|
+
|
|
15
|
+
## 📦 Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bunx @asterflow/cli init my-app
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### ✨ Features
|
|
22
|
+
|
|
23
|
+
- **Full project scaffolding**: `init` writes `package.json`, `tsconfig.json`, `.gitignore`, an app entry for the chosen adapter (Bun, Node, Express or Fastify), and example route files - interactively or entirely via flags.
|
|
24
|
+
- **Non-interactive by default when it needs to be**: any `--flag`, CI, or `--yes` skips prompting and fills in documented defaults instead, so agents and CI never hang waiting on input.
|
|
25
|
+
- **Empty-directory check**: refuses to scaffold into a non-empty directory (ignoring things like `.git`/`.gitignore`/an IDE folder).
|
|
26
|
+
- **Plugin management**: `add` merges plugins into an existing project's `package.json`, installs them with the detected package manager, and prints the `.use(...)` snippet to wire each one up.
|
|
27
|
+
- **Route templates**: `hello` (a minimal `GET /`) and `upload` (a file upload via `@asterflow/multipart`, only offered when the `multipart` plugin is selected).
|
|
28
|
+
- **Static route manifest generation**: `generate` calls `@asterflow/fs`'s `generateRouteManifest` to turn a `routes/` directory into a manifest of static imports, with a `--watch` mode to regenerate on change.
|
|
29
|
+
|
|
30
|
+
## ❓ How to Use
|
|
31
|
+
|
|
32
|
+
Scaffold a new project - interactively, or fully via flags for CI/agents:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
bunx @asterflow/cli init my-app --adapter bun --plugins fs,multipart --routes hello,upload --yes
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Manage an existing project with the other commands:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
asterflow add fs,multipart # add plugins to package.json, install them, print .use(...) wiring
|
|
42
|
+
asterflow list # print available plugins
|
|
43
|
+
asterflow generate --watch # regenerate src/routes.gen.ts whenever src/routes/ changes
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 🔗 Related Packages
|
|
47
|
+
|
|
48
|
+
- [@asterflow/fs](https://www.npmjs.com/package/@asterflow/fs) - its `generateRouteManifest` powers the `generate` command.
|
|
49
|
+
|
|
50
|
+
## 📄 License
|
|
51
|
+
|
|
52
|
+
This project is licensed under the [MIT License](../../LICENSE).
|
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var Ae = Object.create;
|
|
4
|
+
var q = Object.defineProperty;
|
|
5
|
+
var Re = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var Ne = Object.getOwnPropertyNames;
|
|
7
|
+
var Te = Object.getPrototypeOf, Ce = Object.prototype.hasOwnProperty;
|
|
8
|
+
var De = (e, t, r, o) => {
|
|
9
|
+
if (t && typeof t == "object" || typeof t == "function")
|
|
10
|
+
for (let l of Ne(t))
|
|
11
|
+
!Ce.call(e, l) && l !== r && q(e, l, { get: () => t[l], enumerable: !(o = Re(t, l)) || o.enumerable });
|
|
12
|
+
return e;
|
|
13
|
+
};
|
|
14
|
+
var $ = (e, t, r) => (r = e != null ? Ae(Te(e)) : {}, De(
|
|
15
|
+
t || !e || !e.__esModule ? q(r, "default", { value: e, enumerable: !0 }) : r,
|
|
16
|
+
e
|
|
17
|
+
));
|
|
18
|
+
// packages/cli/src/index.ts
|
|
19
|
+
var Ie = $(require("yargs"), 1), Ee = require("yargs/helpers");
|
|
20
|
+
// packages/cli/src/commands/add.ts
|
|
21
|
+
var ee = require("child_process"), te = require("fs"), T = require("fs/promises"), C = require("path"), re = require("util");
|
|
22
|
+
// packages/cli/src/registry/plugins.ts
|
|
23
|
+
var b = {
|
|
24
|
+
fs: {
|
|
25
|
+
id: "fs",
|
|
26
|
+
packageName: "@asterflow/fs",
|
|
27
|
+
version: "^1.0.7",
|
|
28
|
+
description: "File system-based routing - routes are discovered from a routes/ directory.",
|
|
29
|
+
importName: "fsRoutingPlugin",
|
|
30
|
+
useSnippet: ".use(fsRoutingPlugin, { routes })",
|
|
31
|
+
usageNote: "Run `asterflow generate` (or `asterflow generate --watch`) after adding/removing files under src/routes/ - it writes src/routes.gen.ts, which the app entry imports as `routes`."
|
|
32
|
+
},
|
|
33
|
+
multipart: {
|
|
34
|
+
id: "multipart",
|
|
35
|
+
packageName: "@asterflow/multipart",
|
|
36
|
+
version: "^1.0.0",
|
|
37
|
+
description: "multipart/form-data parsing (file uploads), with per-route type-safe field criteria.",
|
|
38
|
+
importName: "multipartPlugin",
|
|
39
|
+
useSnippet: ".use(multipartPlugin, { limits: { fileSize: 10 * 1024 * 1024 } })",
|
|
40
|
+
usageNote: "Build upload routes with Method.create(Method.POST).multipart({...}).handler(...) - see src/routes/upload.ts for a working example."
|
|
41
|
+
}
|
|
42
|
+
}, Je = Object.keys(b);
|
|
43
|
+
// packages/cli/src/utils/log.ts
|
|
44
|
+
var j = $(require("chalk"), 1), d = {
|
|
45
|
+
info: (e) => console.log(j.default.cyan(e)),
|
|
46
|
+
success: (e) => console.log(j.default.green(e)),
|
|
47
|
+
warn: (e) => console.log(j.default.yellow(e)),
|
|
48
|
+
error: (e) => console.error(j.default.red(e)),
|
|
49
|
+
step: (e) => console.log(j.default.gray(` ${e}`))
|
|
50
|
+
};
|
|
51
|
+
// packages/cli/src/utils/packageManager.ts
|
|
52
|
+
var K = require("fs"), H = require("path"), Oe = {
|
|
53
|
+
"bun.lock": "bun",
|
|
54
|
+
"bun.lockb": "bun",
|
|
55
|
+
"pnpm-lock.yaml": "pnpm",
|
|
56
|
+
"yarn.lock": "yarn",
|
|
57
|
+
"package-lock.json": "npm"
|
|
58
|
+
};
|
|
59
|
+
function G(e) {
|
|
60
|
+
for (let [t, r] of Object.entries(Oe))
|
|
61
|
+
if ((0, K.existsSync)((0, H.join)(e, t))) return r;
|
|
62
|
+
return "bun";
|
|
63
|
+
}
|
|
64
|
+
function Q(e, t) {
|
|
65
|
+
return e ?? G(t);
|
|
66
|
+
}
|
|
67
|
+
function X(e) {
|
|
68
|
+
return e === "yarn" ? "yarn" : `${e} install`;
|
|
69
|
+
}
|
|
70
|
+
function Z(e, t) {
|
|
71
|
+
let r = t.join(" ");
|
|
72
|
+
switch (e) {
|
|
73
|
+
case "bun":
|
|
74
|
+
return `bun add ${r}`;
|
|
75
|
+
case "pnpm":
|
|
76
|
+
return `pnpm add ${r}`;
|
|
77
|
+
case "yarn":
|
|
78
|
+
return `yarn add ${r}`;
|
|
79
|
+
case "npm":
|
|
80
|
+
return `npm install ${r}`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// packages/cli/src/commands/add.ts
|
|
84
|
+
var Fe = (0, re.promisify)(ee.exec);
|
|
85
|
+
async function ne({ names: e, directory: t }) {
|
|
86
|
+
let r = (0, C.resolve)(t ?? "."), o = (0, C.join)(r, "package.json"), l = e.split(",").map((a) => a.trim()).filter(Boolean);
|
|
87
|
+
if (l.length === 0) {
|
|
88
|
+
d.error("No plugin names given. Usage: asterflow add fs,multipart"), process.exitCode = 1;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
let h = l.filter((a) => !b[a]);
|
|
92
|
+
if (h.length > 0) {
|
|
93
|
+
d.error(`Unknown plugin${h.length > 1 ? "s" : ""}: ${h.join(", ")}`), d.step(`Available plugins: ${Object.keys(b).join(", ")}`), process.exitCode = 1;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!(0, te.existsSync)(o)) {
|
|
97
|
+
d.error(`No package.json found at ${o}. Run this from an AsterFlow project (or \`asterflow init\` first).`), process.exitCode = 1;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
let u = l.map((a) => b[a]), p = await (0, T.readFile)(o, "utf-8"), c = JSON.parse(p);
|
|
101
|
+
c.dependencies ??= {};
|
|
102
|
+
let g = u.filter((a) => c.dependencies[a.packageName]), y = u.filter((a) => !c.dependencies[a.packageName]);
|
|
103
|
+
for (let a of y) c.dependencies[a.packageName] = a.version;
|
|
104
|
+
if (c.dependencies = Object.fromEntries(Object.entries(c.dependencies).sort(([a], [w]) => a.localeCompare(w))), y.length > 0) {
|
|
105
|
+
await (0, T.writeFile)(o, `${JSON.stringify(c, null, 2)}
|
|
106
|
+
`), d.success(`Added to package.json: ${y.map((x) => x.packageName).join(", ")}`);
|
|
107
|
+
let a = G(r), w = Z(a, y.map((x) => x.packageName));
|
|
108
|
+
d.info(`Installing (${w})...`);
|
|
109
|
+
try {
|
|
110
|
+
await Fe(w, { cwd: r }), d.success("Dependencies installed.");
|
|
111
|
+
} catch (x) {
|
|
112
|
+
d.error(`Install failed - run manually: ${w}`), d.step(x instanceof Error ? x.message : String(x));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
g.length > 0 && d.warn(`Already present, skipped: ${g.map((a) => a.packageName).join(", ")}`), d.info("Wire it up:");
|
|
116
|
+
for (let a of u)
|
|
117
|
+
d.step(`import { ${a.importName} } from '${a.packageName}'`), d.step(`app${a.useSnippet}`), d.step(a.usageNote);
|
|
118
|
+
}
|
|
119
|
+
// packages/cli/src/commands/generate.ts
|
|
120
|
+
var oe = require("@asterflow/fs"), se = require("fs"), I = require("path");
|
|
121
|
+
var Me = "src/routes", ie = "src/routes.gen.ts";
|
|
122
|
+
async function ae({ directory: e, out: t, watch: r }) {
|
|
123
|
+
let o = (0, I.resolve)(e ?? Me), l = (0, I.resolve)(t ?? ie), h = async () => {
|
|
124
|
+
try {
|
|
125
|
+
await (0, oe.generateRouteManifest)({ routesDir: o, outFile: l }), d.success(`Generated ${(0, I.join)(t ?? ie)}`);
|
|
126
|
+
} catch (p) {
|
|
127
|
+
d.error(p instanceof Error ? p.message : String(p)), r || (process.exitCode = 1);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
if (await h(), !r) return;
|
|
131
|
+
d.info(`Watching ${o} for changes...`);
|
|
132
|
+
let u;
|
|
133
|
+
(0, se.watch)(o, { recursive: !0 }, () => {
|
|
134
|
+
clearTimeout(u), u = setTimeout(h, 100);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// packages/cli/src/commands/init.ts
|
|
138
|
+
var i = $(require("@clack/prompts"), 1), V = $(require("ci-info"), 1), ve = require("child_process"), k = require("fs/promises"), m = require("path"), Pe = require("util");
|
|
139
|
+
// packages/cli/src/generators/appEntry.ts
|
|
140
|
+
function le({ adapter: e, plugins: t, stub: r }) {
|
|
141
|
+
let o = t.some((p) => p.id === "fs"), l = [
|
|
142
|
+
"import { AsterFlow } from 'asterflow'",
|
|
143
|
+
"import { adapters } from '@asterflow/adapter'",
|
|
144
|
+
...e.imports,
|
|
145
|
+
...t.map((p) => `import { ${p.importName} } from '${p.packageName}'`),
|
|
146
|
+
...o ? ["import routes from './routes.gen'"] : []
|
|
147
|
+
], h = [
|
|
148
|
+
...t.map((p) => p.useSnippet.replace(/^\./, "")),
|
|
149
|
+
...!o && r ? [`method('get', {
|
|
150
|
+
path: '/',
|
|
151
|
+
handler({ response }) {
|
|
152
|
+
return response.success({ message: 'Hello from AsterFlow!' })
|
|
153
|
+
}
|
|
154
|
+
})`] : [],
|
|
155
|
+
`listen${e.listenCall}`
|
|
156
|
+
], u = e.preamble ? `${e.preamble}
|
|
157
|
+
` : "";
|
|
158
|
+
return `${l.join(`
|
|
159
|
+
`)}
|
|
160
|
+
${u}export default new AsterFlow({ driver: ${e.driverExpression} })
|
|
161
|
+
.${h.join(`
|
|
162
|
+
.`)}
|
|
163
|
+
`;
|
|
164
|
+
}
|
|
165
|
+
// packages/cli/src/generators/packageJson.ts
|
|
166
|
+
function pe({ name: e, adapter: t, plugins: r }) {
|
|
167
|
+
let o = r.some((g) => g.id === "fs"), l = {
|
|
168
|
+
asterflow: "^0.0.5",
|
|
169
|
+
"@asterflow/adapter": "^1.0.0",
|
|
170
|
+
zod: "^3.25.67"
|
|
171
|
+
};
|
|
172
|
+
t.dependency && (l[t.dependency] = "latest");
|
|
173
|
+
for (let g of r) l[g.packageName] = g.version;
|
|
174
|
+
let h = Object.fromEntries(Object.entries(l).sort(([g], [y]) => g.localeCompare(y))), u = o ? {
|
|
175
|
+
predev: "asterflow generate",
|
|
176
|
+
dev: "bun run src/index.ts",
|
|
177
|
+
prebuild: "asterflow generate"
|
|
178
|
+
} : { dev: "bun run src/index.ts" }, p = {
|
|
179
|
+
typescript: "^5.8.3",
|
|
180
|
+
"@types/bun": "latest"
|
|
181
|
+
};
|
|
182
|
+
return o && (p["@asterflow/cli"] = "^0.1.0"), `${JSON.stringify({
|
|
183
|
+
name: e,
|
|
184
|
+
version: "0.1.0",
|
|
185
|
+
type: "module",
|
|
186
|
+
private: !0,
|
|
187
|
+
scripts: u,
|
|
188
|
+
dependencies: h,
|
|
189
|
+
devDependencies: p
|
|
190
|
+
}, null, 2)}
|
|
191
|
+
`;
|
|
192
|
+
}
|
|
193
|
+
// packages/cli/src/helpers/copyTemplate.ts
|
|
194
|
+
var D = require("fs/promises"), L = require("path"), _e = (0, L.join)(__dirname, "..", "..", "templates");
|
|
195
|
+
async function U(e) {
|
|
196
|
+
return (0, D.readFile)((0, L.join)(_e, e), "utf-8");
|
|
197
|
+
}
|
|
198
|
+
async function B(e, t) {
|
|
199
|
+
let r = await U(e);
|
|
200
|
+
await (0, D.writeFile)(t, r);
|
|
201
|
+
}
|
|
202
|
+
// packages/cli/src/registry/templates.ts
|
|
203
|
+
var ce = {
|
|
204
|
+
hello: {
|
|
205
|
+
id: "hello",
|
|
206
|
+
path: "/",
|
|
207
|
+
source: "routes/index.ts",
|
|
208
|
+
summary: "Minimal GET route at the root path."
|
|
209
|
+
},
|
|
210
|
+
upload: {
|
|
211
|
+
id: "upload",
|
|
212
|
+
path: "/upload",
|
|
213
|
+
source: "routes/upload.ts",
|
|
214
|
+
plugin: "multipart",
|
|
215
|
+
summary: "File upload handled by @asterflow/multipart."
|
|
216
|
+
}
|
|
217
|
+
}, E = Object.keys(ce);
|
|
218
|
+
function O(e) {
|
|
219
|
+
let t = new Set(e);
|
|
220
|
+
return E.map((r) => ce[r]).filter((r) => !r.plugin || t.has(r.plugin));
|
|
221
|
+
}
|
|
222
|
+
function de(e) {
|
|
223
|
+
let t = e.path.replace(/^\/+|\/+$/g, "");
|
|
224
|
+
return t.length === 0 ? "index.ts" : `${t}/index.ts`;
|
|
225
|
+
}
|
|
226
|
+
// packages/cli/src/generators/routes.ts
|
|
227
|
+
async function me({ pluginIds: e, templateIds: t }) {
|
|
228
|
+
if (!e.includes("fs")) return [];
|
|
229
|
+
let r = O(e).filter((o) => t.includes(o.id));
|
|
230
|
+
return Promise.all(r.map(async (o) => ({
|
|
231
|
+
fileName: de(o),
|
|
232
|
+
content: await U(o.source)
|
|
233
|
+
})));
|
|
234
|
+
}
|
|
235
|
+
// packages/cli/src/helpers/git.ts
|
|
236
|
+
var P = require("child_process"), ue = require("fs"), ge = require("path");
|
|
237
|
+
function Ge(e) {
|
|
238
|
+
try {
|
|
239
|
+
return (0, P.execSync)("git rev-parse --is-inside-work-tree", { cwd: e, stdio: "ignore" }), !0;
|
|
240
|
+
} catch {
|
|
241
|
+
return !1;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function Le(e) {
|
|
245
|
+
try {
|
|
246
|
+
return (0, P.execSync)("git config init.defaultBranch", { cwd: e, stdio: "ignore" }), !0;
|
|
247
|
+
} catch {
|
|
248
|
+
return !1;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function fe(e) {
|
|
252
|
+
let t = !1;
|
|
253
|
+
try {
|
|
254
|
+
return (0, P.execSync)("git --version", { stdio: "ignore" }), Ge(e) ? !1 : ((0, P.execSync)("git init", { cwd: e, stdio: "ignore" }), t = !0, Le(e) || (0, P.execSync)("git checkout -b main", { cwd: e, stdio: "ignore" }), (0, P.execSync)("git add -A", { cwd: e, stdio: "ignore" }), (0, P.execSync)('git commit -m "Initial commit from create-asterflow-app"', { cwd: e, stdio: "ignore" }), !0);
|
|
255
|
+
} catch {
|
|
256
|
+
if (t)
|
|
257
|
+
try {
|
|
258
|
+
(0, ue.rmSync)((0, ge.join)(e, ".git"), { recursive: !0, force: !0 });
|
|
259
|
+
} catch {
|
|
260
|
+
}
|
|
261
|
+
return !1;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// packages/cli/src/helpers/isFolderEmpty.ts
|
|
265
|
+
var F = require("fs"), ye = require("path"), W = $(require("chalk"), 1), Ue = new Set([
|
|
266
|
+
".DS_Store",
|
|
267
|
+
".git",
|
|
268
|
+
".gitattributes",
|
|
269
|
+
".gitignore",
|
|
270
|
+
".gitlab-ci.yml",
|
|
271
|
+
".hg",
|
|
272
|
+
".hgcheck",
|
|
273
|
+
".hgignore",
|
|
274
|
+
".idea",
|
|
275
|
+
".npmignore",
|
|
276
|
+
".vscode",
|
|
277
|
+
"LICENSE",
|
|
278
|
+
"Thumbs.db"
|
|
279
|
+
]);
|
|
280
|
+
function be(e, t) {
|
|
281
|
+
let r = (0, F.readdirSync)(e).filter((o) => !Ue.has(o) && !o.endsWith(".iml"));
|
|
282
|
+
if (r.length === 0) return !0;
|
|
283
|
+
console.log(`The directory ${W.default.green(t)} contains files that could conflict:`), console.log();
|
|
284
|
+
for (let o of r) {
|
|
285
|
+
let l = (0, F.lstatSync)((0, ye.join)(e, o)).isDirectory();
|
|
286
|
+
console.log(` ${W.default.cyan(o)}${l ? "/" : ""}`);
|
|
287
|
+
}
|
|
288
|
+
return console.log(), console.log("Either try using a new directory name, or remove the files listed above."), console.log(), !1;
|
|
289
|
+
}
|
|
290
|
+
// packages/cli/src/helpers/isWriteable.ts
|
|
291
|
+
var he = require("fs"), we = require("fs/promises");
|
|
292
|
+
async function ke(e) {
|
|
293
|
+
try {
|
|
294
|
+
return await (0, we.access)(e, he.constants.W_OK), !0;
|
|
295
|
+
} catch {
|
|
296
|
+
return !1;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// packages/cli/src/helpers/validateProjectName.ts
|
|
300
|
+
var xe = $(require("validate-npm-package-name"), 1);
|
|
301
|
+
function Y(e) {
|
|
302
|
+
let t = (0, xe.default)(e);
|
|
303
|
+
return t.validForNewPackages ? { valid: !0 } : {
|
|
304
|
+
valid: !1,
|
|
305
|
+
problems: [...t.errors ?? [], ...t.warnings ?? []]
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
// packages/cli/src/registry/adapters.ts
|
|
309
|
+
var S = {
|
|
310
|
+
bun: {
|
|
311
|
+
id: "bun",
|
|
312
|
+
label: "Bun (native Bun.serve)",
|
|
313
|
+
dependency: null,
|
|
314
|
+
imports: [],
|
|
315
|
+
driverExpression: "adapters.bun",
|
|
316
|
+
preamble: null,
|
|
317
|
+
listenCall: `({ port: 3333 }, (err) => {
|
|
318
|
+
if (err) {
|
|
319
|
+
console.error(err)
|
|
320
|
+
process.exit(1)
|
|
321
|
+
}
|
|
322
|
+
console.log('Server listening on http:
|
|
323
|
+
})`
|
|
324
|
+
},
|
|
325
|
+
node: {
|
|
326
|
+
id: "node",
|
|
327
|
+
label: "Node.js (native http)",
|
|
328
|
+
dependency: null,
|
|
329
|
+
imports: [],
|
|
330
|
+
driverExpression: "adapters.node",
|
|
331
|
+
preamble: null,
|
|
332
|
+
listenCall: `({ port: 3333 }, (err) => {
|
|
333
|
+
if (err) {
|
|
334
|
+
console.error(err)
|
|
335
|
+
process.exit(1)
|
|
336
|
+
}
|
|
337
|
+
console.log('Server listening on http:
|
|
338
|
+
})`
|
|
339
|
+
},
|
|
340
|
+
express: {
|
|
341
|
+
id: "express",
|
|
342
|
+
label: "Express",
|
|
343
|
+
dependency: "express",
|
|
344
|
+
imports: ["import express from 'express'"],
|
|
345
|
+
driverExpression: "adapters.express",
|
|
346
|
+
preamble: "const server = express()",
|
|
347
|
+
listenCall: `(server, 3333, () => {
|
|
348
|
+
console.log('Server listening on http:
|
|
349
|
+
})`
|
|
350
|
+
},
|
|
351
|
+
fastify: {
|
|
352
|
+
id: "fastify",
|
|
353
|
+
label: "Fastify",
|
|
354
|
+
dependency: "fastify",
|
|
355
|
+
imports: ["import fastify from 'fastify'"],
|
|
356
|
+
driverExpression: "adapters.fastify",
|
|
357
|
+
preamble: "const server = fastify()",
|
|
358
|
+
listenCall: `(server, { port: 3333 }, (err) => {
|
|
359
|
+
if (err) {
|
|
360
|
+
console.error(err)
|
|
361
|
+
process.exit(1)
|
|
362
|
+
}
|
|
363
|
+
console.log('Server listening on http:
|
|
364
|
+
})`
|
|
365
|
+
}
|
|
366
|
+
}, A = Object.keys(S);
|
|
367
|
+
// packages/cli/src/commands/init.ts
|
|
368
|
+
var Be = (0, Pe.promisify)(ve.exec), v = {
|
|
369
|
+
adapter: "bun",
|
|
370
|
+
plugins: ["fs", "multipart"],
|
|
371
|
+
install: !0,
|
|
372
|
+
git: !0
|
|
373
|
+
};
|
|
374
|
+
async function $e(e) {
|
|
375
|
+
i.intro("Create a new AsterFlow project");
|
|
376
|
+
let t = (0, m.resolve)(e.directory ?? "."), r = (0, m.basename)(t) === "." ? "my-asterflow-app" : (0, m.basename)(t);
|
|
377
|
+
if (!await ke((0, m.dirname)(t))) {
|
|
378
|
+
i.cancel(`The path ${t} is not writable. Check folder permissions and try again.`), process.exitCode = 1;
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (await (0, k.mkdir)(t, { recursive: !0 }), !be(t, (0, m.basename)(t))) {
|
|
382
|
+
process.exitCode = 1;
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
let o = process.argv.some((n) => n.startsWith("--")), l = V.default.isCI || !!e.yes || o, h = o && !e.yes && !V.default.isCI;
|
|
386
|
+
if (e.name) {
|
|
387
|
+
let n = Y(e.name);
|
|
388
|
+
if (!n.valid) {
|
|
389
|
+
i.cancel(`Invalid project name "${e.name}":
|
|
390
|
+
${n.problems.map((s) => ` - ${s}`).join(`
|
|
391
|
+
`)}`), process.exitCode = 1;
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
let u = e.name;
|
|
396
|
+
if (u === void 0)
|
|
397
|
+
if (l)
|
|
398
|
+
u = r;
|
|
399
|
+
else {
|
|
400
|
+
let n = await i.text({
|
|
401
|
+
message: "Project name",
|
|
402
|
+
placeholder: r,
|
|
403
|
+
defaultValue: r,
|
|
404
|
+
validate: (s) => {
|
|
405
|
+
let f = Y(s || r);
|
|
406
|
+
return f.valid ? void 0 : `Invalid project name: ${f.problems[0]}`;
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
if (i.isCancel(n)) return i.cancel("Cancelled.");
|
|
410
|
+
u = n;
|
|
411
|
+
}
|
|
412
|
+
if (e.adapter && !S[e.adapter]) {
|
|
413
|
+
i.cancel(`Unknown adapter "${e.adapter}". Available: ${A.join(", ")}`), process.exitCode = 1;
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
let p = e.adapter;
|
|
417
|
+
if (!p)
|
|
418
|
+
if (l)
|
|
419
|
+
p = v.adapter;
|
|
420
|
+
else {
|
|
421
|
+
let n = await i.select({
|
|
422
|
+
message: "Which adapter do you want to use?",
|
|
423
|
+
options: Object.values(S).map((s) => ({ value: s.id, label: s.label })),
|
|
424
|
+
initialValue: v.adapter
|
|
425
|
+
});
|
|
426
|
+
if (i.isCancel(n)) return i.cancel("Cancelled.");
|
|
427
|
+
p = n;
|
|
428
|
+
}
|
|
429
|
+
let c;
|
|
430
|
+
if (e.plugins !== void 0) {
|
|
431
|
+
c = e.plugins.split(",").map((s) => s.trim()).filter(Boolean);
|
|
432
|
+
let n = c.filter((s) => !b[s]);
|
|
433
|
+
if (n.length > 0) {
|
|
434
|
+
i.cancel(`Unknown plugin${n.length > 1 ? "s" : ""}: ${n.join(", ")}. Available: ${Object.keys(b).join(", ")}`), process.exitCode = 1;
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
} else if (l)
|
|
438
|
+
c = v.plugins;
|
|
439
|
+
else {
|
|
440
|
+
let n = await i.multiselect({
|
|
441
|
+
message: "Which plugins do you want to install? (space to select, enter to confirm)",
|
|
442
|
+
options: Object.values(b).map((s) => ({ value: s.id, label: s.packageName, hint: s.description })),
|
|
443
|
+
initialValues: v.plugins,
|
|
444
|
+
required: !1
|
|
445
|
+
});
|
|
446
|
+
if (i.isCancel(n)) return i.cancel("Cancelled.");
|
|
447
|
+
c = n;
|
|
448
|
+
}
|
|
449
|
+
c.length === 0 && (c = v.plugins);
|
|
450
|
+
let g = O(c), y;
|
|
451
|
+
if (e.routes !== void 0) {
|
|
452
|
+
let n = e.routes.split(",").map((f) => f.trim()).filter(Boolean);
|
|
453
|
+
y = n.includes("none") ? [] : n;
|
|
454
|
+
let s = y.filter((f) => !g.some((Se) => Se.id === f));
|
|
455
|
+
if (s.length > 0) {
|
|
456
|
+
i.cancel(`Unknown route template${s.length > 1 ? "s" : ""}: ${s.join(", ")}. Available: ${g.map((f) => f.id).join(", ")}`), process.exitCode = 1;
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
} else if (l)
|
|
460
|
+
y = g.map((n) => n.id);
|
|
461
|
+
else {
|
|
462
|
+
let n = await i.multiselect({
|
|
463
|
+
message: "Which example routes do you want to scaffold? (space to select, enter to confirm)",
|
|
464
|
+
options: g.map((s) => ({ value: s.id, label: s.path, hint: s.summary })),
|
|
465
|
+
initialValues: g.map((s) => s.id),
|
|
466
|
+
required: !1
|
|
467
|
+
});
|
|
468
|
+
if (i.isCancel(n)) return i.cancel("Cancelled.");
|
|
469
|
+
y = n;
|
|
470
|
+
}
|
|
471
|
+
let a = e.install;
|
|
472
|
+
if (a === void 0)
|
|
473
|
+
if (l)
|
|
474
|
+
a = v.install;
|
|
475
|
+
else {
|
|
476
|
+
let n = await i.confirm({ message: "Install dependencies now?", initialValue: v.install });
|
|
477
|
+
if (i.isCancel(n)) return i.cancel("Cancelled.");
|
|
478
|
+
a = n;
|
|
479
|
+
}
|
|
480
|
+
let w = e.git;
|
|
481
|
+
if (w === void 0)
|
|
482
|
+
if (l)
|
|
483
|
+
w = v.git;
|
|
484
|
+
else {
|
|
485
|
+
let n = await i.confirm({ message: "Initialize a git repository?", initialValue: v.git });
|
|
486
|
+
if (i.isCancel(n)) return i.cancel("Cancelled.");
|
|
487
|
+
w = n;
|
|
488
|
+
}
|
|
489
|
+
h && We({
|
|
490
|
+
adapter: e.adapter === void 0,
|
|
491
|
+
plugins: e.plugins === void 0,
|
|
492
|
+
routes: e.routes === void 0,
|
|
493
|
+
install: e.install === void 0,
|
|
494
|
+
git: e.git === void 0
|
|
495
|
+
}, { adapterId: p, pluginIds: c, templateIds: y, install: a, git: w });
|
|
496
|
+
let x = S[p], N = c.map((n) => b[n]), z = i.spinner();
|
|
497
|
+
z.start("Scaffolding project");
|
|
498
|
+
let M = (0, m.join)(t, "src");
|
|
499
|
+
await (0, k.mkdir)(M, { recursive: !0 }), await (0, k.writeFile)((0, m.join)(t, "package.json"), pe({ name: u, adapter: x, plugins: N })), await B("tsconfig.json", (0, m.join)(t, "tsconfig.json")), await (0, k.writeFile)((0, m.join)(M, "index.ts"), le({ adapter: x, plugins: N, stub: y.length > 0 })), await B("gitignore", (0, m.join)(t, ".gitignore")), N.some((n) => n.id === "fs") && await (0, k.appendFile)((0, m.join)(t, ".gitignore"), `src/routes.gen.ts
|
|
500
|
+
`);
|
|
501
|
+
let J = await me({ pluginIds: c, templateIds: y });
|
|
502
|
+
if (J.length > 0) {
|
|
503
|
+
let n = (0, m.join)(M, "routes");
|
|
504
|
+
await (0, k.mkdir)(n, { recursive: !0 });
|
|
505
|
+
for (let s of J) {
|
|
506
|
+
let f = (0, m.join)(n, s.fileName);
|
|
507
|
+
await (0, k.mkdir)((0, m.dirname)(f), { recursive: !0 }), await (0, k.writeFile)(f, s.content);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
z.stop("Project scaffolded");
|
|
511
|
+
let _ = Q(e.packageManager, t);
|
|
512
|
+
if (a) {
|
|
513
|
+
let n = X(_), s = i.spinner();
|
|
514
|
+
s.start(`Installing dependencies (${n})`);
|
|
515
|
+
try {
|
|
516
|
+
await Be(n, { cwd: t }), s.stop("Dependencies installed");
|
|
517
|
+
} catch (f) {
|
|
518
|
+
s.stop("Failed to install dependencies"), i.log.error(f instanceof Error ? f.message : String(f));
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
w && fe(t) && i.log.success("Initialized a git repository.");
|
|
522
|
+
for (let n of N)
|
|
523
|
+
i.log.info(`${n.packageName}: ${n.usageNote}`);
|
|
524
|
+
Ye({ name: u, targetDir: t, packageManager: _, useYarn: _ === "yarn" });
|
|
525
|
+
}
|
|
526
|
+
function We(e, t) {
|
|
527
|
+
let r = [];
|
|
528
|
+
e.adapter && r.push(` --adapter <id> ${t.adapterId} (available: ${A.join(", ")})`), e.plugins && r.push(` --plugins <ids> ${t.pluginIds.length > 0 ? t.pluginIds.join(",") : "none"} (available: ${Object.keys(b).join(", ")})`), e.routes && r.push(` --routes <ids> ${t.templateIds.length > 0 ? t.templateIds.join(",") : "none"} (available: ${E.join(", ")})`), e.install && r.push(` --no-install skip installing dependencies (default: ${t.install ? "installs" : "skips"})`), e.git && r.push(` --no-git skip git init (default: ${t.git ? "inits" : "skips"})`), r.length !== 0 && i.note(r.join(`
|
|
529
|
+
`), "Using defaults for unprovided options");
|
|
530
|
+
}
|
|
531
|
+
function Ye({ name: e, targetDir: t, packageManager: r, useYarn: o }) {
|
|
532
|
+
let l = `${r} ${o ? "" : "run "}dev`;
|
|
533
|
+
i.note(`cd ${t}
|
|
534
|
+
${l}`, "Next steps"), i.outro(`Success! Created ${e} at ${t}`);
|
|
535
|
+
}
|
|
536
|
+
// packages/cli/src/commands/list.ts
|
|
537
|
+
var R = $(require("chalk"), 1);
|
|
538
|
+
function je() {
|
|
539
|
+
console.log(R.default.bold(`
|
|
540
|
+
Available AsterFlow plugins:
|
|
541
|
+
`));
|
|
542
|
+
for (let e of Object.values(b))
|
|
543
|
+
console.log(` ${R.default.cyan(e.id.padEnd(12))} ${R.default.gray(e.packageName)}`), console.log(` ${" ".repeat(12)} ${e.description}
|
|
544
|
+
`);
|
|
545
|
+
console.log(R.default.gray(`Install with: asterflow add <name>[,<name>...]
|
|
546
|
+
`));
|
|
547
|
+
}
|
|
548
|
+
// packages/cli/src/index.ts
|
|
549
|
+
(async () => {
|
|
550
|
+
await (0, Ie.default)((0, Ee.hideBin)(process.argv)).scriptName("asterflow").command(
|
|
551
|
+
"init [directory]",
|
|
552
|
+
"Scaffold a new AsterFlow project",
|
|
553
|
+
(e) => e.positional("directory", {
|
|
554
|
+
type: "string",
|
|
555
|
+
describe: "Directory to create the project in (defaults to the current directory)"
|
|
556
|
+
}).option("name", {
|
|
557
|
+
type: "string",
|
|
558
|
+
describe: "Project name (defaults to the directory name)"
|
|
559
|
+
}).option("adapter", {
|
|
560
|
+
type: "string",
|
|
561
|
+
describe: `Adapter to use (${A.join(", ")})`
|
|
562
|
+
}).option("plugins", {
|
|
563
|
+
type: "string",
|
|
564
|
+
describe: "Comma-separated plugin ids, e.g. fs,multipart"
|
|
565
|
+
}).option("routes", {
|
|
566
|
+
type: "string",
|
|
567
|
+
describe: `Comma-separated route template ids to scaffold, or "none" (${E.join(", ")})`
|
|
568
|
+
}).option("install", {
|
|
569
|
+
type: "boolean",
|
|
570
|
+
describe: "Install dependencies (default: yes)"
|
|
571
|
+
}).option("skip-install", {
|
|
572
|
+
type: "boolean",
|
|
573
|
+
describe: "Alias for --no-install"
|
|
574
|
+
}).option("git", {
|
|
575
|
+
type: "boolean",
|
|
576
|
+
describe: "Initialize a git repository (default: yes)"
|
|
577
|
+
}).option("use-npm", { type: "boolean", describe: "Install dependencies with npm" }).option("use-pnpm", { type: "boolean", describe: "Install dependencies with pnpm" }).option("use-yarn", { type: "boolean", describe: "Install dependencies with yarn" }).option("use-bun", { type: "boolean", describe: "Install dependencies with bun" }).option("yes", {
|
|
578
|
+
type: "boolean",
|
|
579
|
+
describe: "Accept defaults for anything not explicitly set, without prompting"
|
|
580
|
+
}),
|
|
581
|
+
async (e) => {
|
|
582
|
+
let t = e.useNpm ? "npm" : e.usePnpm ? "pnpm" : e.useYarn ? "yarn" : e.useBun ? "bun" : void 0;
|
|
583
|
+
await $e({
|
|
584
|
+
directory: e.directory,
|
|
585
|
+
name: e.name,
|
|
586
|
+
adapter: e.adapter,
|
|
587
|
+
plugins: e.plugins,
|
|
588
|
+
routes: e.routes,
|
|
589
|
+
install: e.skipInstall ? !1 : e.install,
|
|
590
|
+
git: e.git,
|
|
591
|
+
packageManager: t,
|
|
592
|
+
yes: e.yes
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
).command(
|
|
596
|
+
"add <names>",
|
|
597
|
+
"Add one or more plugins to the current project",
|
|
598
|
+
(e) => e.positional("names", {
|
|
599
|
+
type: "string",
|
|
600
|
+
describe: "Comma-separated plugin names, e.g. fs,multipart",
|
|
601
|
+
demandOption: !0
|
|
602
|
+
}).option("directory", {
|
|
603
|
+
alias: "C",
|
|
604
|
+
type: "string",
|
|
605
|
+
describe: "Project directory (defaults to the current directory)"
|
|
606
|
+
}),
|
|
607
|
+
async (e) => {
|
|
608
|
+
await ne({ names: e.names, directory: e.directory });
|
|
609
|
+
}
|
|
610
|
+
).command(
|
|
611
|
+
"list",
|
|
612
|
+
"List available plugins",
|
|
613
|
+
() => {
|
|
614
|
+
},
|
|
615
|
+
() => je()
|
|
616
|
+
).command(
|
|
617
|
+
"generate [directory]",
|
|
618
|
+
"Generate the static route manifest consumed by @asterflow/fs (fs-routing)",
|
|
619
|
+
(e) => e.positional("directory", {
|
|
620
|
+
type: "string",
|
|
621
|
+
describe: "Routes directory to scan (defaults to src/routes)"
|
|
622
|
+
}).option("out", {
|
|
623
|
+
alias: "o",
|
|
624
|
+
type: "string",
|
|
625
|
+
describe: "Output file for the generated manifest (defaults to src/routes.gen.ts)"
|
|
626
|
+
}).option("watch", {
|
|
627
|
+
alias: "w",
|
|
628
|
+
type: "boolean",
|
|
629
|
+
describe: "Regenerate whenever a file under the routes directory changes"
|
|
630
|
+
}),
|
|
631
|
+
async (e) => {
|
|
632
|
+
await ae({ directory: e.directory, out: e.out, watch: e.watch });
|
|
633
|
+
}
|
|
634
|
+
).demandCommand(1, "Specify a command: init, add, list or generate").strict().help().alias("help", "h").parse();
|
|
635
|
+
})().catch((e) => {
|
|
636
|
+
console.error(e instanceof Error ? e.message : e), process.exitCode = 1;
|
|
637
|
+
});
|