@lv00/new 0.0.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/.config/ai/manifest.ts +20 -0
- package/.config/ai/rules/api.md +5 -0
- package/.config/ai/rules/db.md +7 -0
- package/.config/ai/rules/stack.md +9 -0
- package/.config/ai/rules/style.md +7 -0
- package/.config/ai/rules/svelte.md +7 -0
- package/.config/ai.rules.md +3 -0
- package/.config/dev.env.schema.ts +3 -0
- package/.config/standards.md +3 -0
- package/.scripts/generators/api-route.ts +36 -0
- package/.scripts/generators/job.ts +15 -0
- package/.scripts/generators/project.ts +184 -0
- package/.scripts/generators/script.ts +13 -0
- package/.scripts/manage.ts +95 -0
- package/.scripts/prompt.ts +40 -0
- package/README.md +43 -0
- package/bunfig.toml +5 -0
- package/package.json +27 -0
- package/packages/shared/package.json +8 -0
- package/packages/shared/src/config/index.ts +1 -0
- package/packages/shared/src/env/index.ts +1 -0
- package/packages/shared/src/errors/index.ts +1 -0
- package/packages/shared/src/index.ts +4 -0
- package/packages/shared/src/utils/index.ts +1 -0
- package/packages/ui/package.json +6 -0
- package/packages/ui/src/index.ts +1 -0
- package/projects/caca/api/package.json +6 -0
- package/projects/caca/api/src/app.ts +6 -0
- package/projects/caca/api/src/index.ts +3 -0
- package/projects/caca/api/src/plugins/.gitkeep +0 -0
- package/projects/caca/api/src/routes/health.ts +3 -0
- package/projects/caca/api/src/schemas/.gitkeep +0 -0
- package/projects/caca/infra/caddy/.gitkeep +0 -0
- package/projects/caca/infra/docker/.gitkeep +0 -0
- package/projects/caca/jobs/src/caca.job.ts +4 -0
- package/projects/caca/project.config.ts +6 -0
- package/projects/caca/scripts/caca.script.ts +2 -0
- package/projects/caca/web/package.json +16 -0
- package/projects/caca/web/src/App.svelte +5 -0
- package/projects/caca/web/src/app.html +12 -0
- package/projects/caca/web/src/main.ts +4 -0
- package/projects/caca/web/svelte.config.js +5 -0
- package/projects/example/api/package.json +6 -0
- package/projects/example/api/src/app.ts +6 -0
- package/projects/example/api/src/index.ts +3 -0
- package/projects/example/api/src/plugins/.gitkeep +0 -0
- package/projects/example/api/src/routes/health.ts +3 -0
- package/projects/example/api/src/schemas/.gitkeep +0 -0
- package/projects/example/jobs/src/example.job.ts +4 -0
- package/projects/example/project.config.ts +6 -0
- package/projects/example/scripts/example.script.ts +2 -0
- package/projects/example/web/package.json +16 -0
- package/projects/example/web/src/App.svelte +5 -0
- package/projects/example/web/src/app.html +12 -0
- package/projects/example/web/src/main.ts +4 -0
- package/projects/example/web/svelte.config.js +5 -0
- package/projects/myapp/api/package.json +6 -0
- package/projects/myapp/api/src/app.ts +6 -0
- package/projects/myapp/api/src/index.ts +3 -0
- package/projects/myapp/api/src/plugins/.gitkeep +0 -0
- package/projects/myapp/api/src/routes/health.ts +3 -0
- package/projects/myapp/api/src/schemas/.gitkeep +0 -0
- package/projects/myapp/infra/caddy/.gitkeep +0 -0
- package/projects/myapp/infra/docker/.gitkeep +0 -0
- package/projects/myapp/jobs/src/myapp.job.ts +4 -0
- package/projects/myapp/project.config.ts +6 -0
- package/projects/myapp/scripts/myapp.script.ts +2 -0
- package/projects/myapp/web/package.json +16 -0
- package/projects/myapp/web/src/App.svelte +5 -0
- package/projects/myapp/web/src/app.html +12 -0
- package/projects/myapp/web/src/main.ts +4 -0
- package/projects/myapp/web/svelte.config.js +5 -0
- package/projects/test/api/package.json +6 -0
- package/projects/test/api/src/app.ts +6 -0
- package/projects/test/api/src/index.ts +3 -0
- package/projects/test/api/src/plugins/.gitkeep +0 -0
- package/projects/test/api/src/routes/health.ts +3 -0
- package/projects/test/api/src/schemas/.gitkeep +0 -0
- package/projects/test/infra/caddy/.gitkeep +0 -0
- package/projects/test/infra/docker/.gitkeep +0 -0
- package/projects/test/jobs/src/test.job.ts +4 -0
- package/projects/test/project.config.ts +6 -0
- package/projects/test/scripts/test.script.ts +2 -0
- package/projects/test/web/package.json +16 -0
- package/projects/test/web/src/App.svelte +5 -0
- package/projects/test/web/src/app.html +12 -0
- package/projects/test/web/src/main.ts +4 -0
- package/projects/test/web/svelte.config.js +5 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const AI_RULES: { id: string; label: string; hint?: string }[] = [
|
|
2
|
+
{ id: "stack", label: "Stack (Bun, Svelte 5, Drizzle, ElysiaJS)", hint: "recommandé" },
|
|
3
|
+
{ id: "style", label: "Style (minimal, clarté)" },
|
|
4
|
+
{ id: "db", label: "Base de données (Drizzle, SQLite)" },
|
|
5
|
+
{ id: "svelte", label: "Svelte 5 (runes)" },
|
|
6
|
+
{ id: "api", label: "API (ElysiaJS)" },
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
export const AI_EDITORS = [
|
|
10
|
+
{ value: "cursor" as const, label: "Cursor", hint: ".cursor/rules/" },
|
|
11
|
+
{ value: "codex" as const, label: "Codex", hint: ".codex/rules/" },
|
|
12
|
+
{ value: "none" as const, label: "Aucun" },
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
export type AiEditor = (typeof AI_EDITORS)[number]["value"];
|
|
16
|
+
|
|
17
|
+
export const EDITOR_RULES_DIR: Record<Exclude<AiEditor, "none">, string> = {
|
|
18
|
+
cursor: ".cursor/rules",
|
|
19
|
+
codex: ".codex/rules",
|
|
20
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Stack
|
|
2
|
+
|
|
3
|
+
- Runtime : Bun uniquement
|
|
4
|
+
- Frontend : Svelte 5 (runes, syntaxe moderne)
|
|
5
|
+
- Backend : Bun natif avec ElysiaJS (pas Node fallback)
|
|
6
|
+
- ORM : Drizzle ORM exclusivement (drizzle-kit pour les migrations)
|
|
7
|
+
- Base : SQLite par défaut
|
|
8
|
+
|
|
9
|
+
Interdictions : Node.js, npm, yarn, pnpm ; Svelte ≤4 ; Prisma, TypeORM ; PostgreSQL/MySQL/MongoDB sauf instruction explicite.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
|
|
5
|
+
function toCamel(s: string) {
|
|
6
|
+
return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function generateApiRoute(templateRoot: string, projectName: string, name: string) {
|
|
10
|
+
const routePath = join(templateRoot, "projects", projectName, "api", "src", "routes", `${name}.ts`);
|
|
11
|
+
const appPath = join(templateRoot, "projects", projectName, "api", "src", "app.ts");
|
|
12
|
+
|
|
13
|
+
const routeVar = toCamel(name);
|
|
14
|
+
await writeFile(
|
|
15
|
+
routePath,
|
|
16
|
+
`import { Hono } from "hono";
|
|
17
|
+
|
|
18
|
+
export const ${routeVar} = new Hono().get("/", (c) => c.json({ ${routeVar}: true }));
|
|
19
|
+
`
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const appContent = await readFile(appPath, "utf-8");
|
|
23
|
+
if (appContent.includes(`from "./routes/${name}"`)) return;
|
|
24
|
+
|
|
25
|
+
const newImport = `import { ${routeVar} } from "./routes/${name}";`;
|
|
26
|
+
const lastRouteImport = appContent.match(/import \{ \w+ \} from "\.\/routes\/[^"]+";/g)?.pop();
|
|
27
|
+
const withImport = lastRouteImport
|
|
28
|
+
? appContent.replace(lastRouteImport, `${lastRouteImport}\n${newImport}`)
|
|
29
|
+
: appContent.replace("import { Hono } from \"hono\";", `import { Hono } from "hono";\n${newImport}`);
|
|
30
|
+
|
|
31
|
+
const lastAppRoute = withImport.match(/app\.route\("[^"]+", \w+\);/g)?.pop();
|
|
32
|
+
const withRoute = lastAppRoute
|
|
33
|
+
? withImport.replace(lastAppRoute, `${lastAppRoute}\napp.route("/${name}", ${routeVar});`)
|
|
34
|
+
: withImport.replace("const app = new Hono();", `const app = new Hono();\napp.route("/${name}", ${routeVar});`);
|
|
35
|
+
await writeFile(appPath, withRoute);
|
|
36
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { writeFile } from "node:fs/promises";
|
|
4
|
+
|
|
5
|
+
export async function generateJob(templateRoot: string, projectName: string, name: string) {
|
|
6
|
+
const jobPath = join(templateRoot, "projects", projectName, "jobs", "src", `${name}.job.ts`);
|
|
7
|
+
await writeFile(
|
|
8
|
+
jobPath,
|
|
9
|
+
`#!/usr/bin/env bun
|
|
10
|
+
export async function run() {
|
|
11
|
+
console.log("${name} job");
|
|
12
|
+
}
|
|
13
|
+
`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { EDITOR_RULES_DIR } from "../../.config/ai/manifest";
|
|
5
|
+
|
|
6
|
+
const EXAMPLE = "example";
|
|
7
|
+
|
|
8
|
+
type AiConfig = { type: "cursor" | "codex"; ruleIds: string[] };
|
|
9
|
+
|
|
10
|
+
export async function generateProject(
|
|
11
|
+
templateRoot: string,
|
|
12
|
+
name: string,
|
|
13
|
+
destinationRoot: string,
|
|
14
|
+
options?: { editor?: AiConfig }
|
|
15
|
+
) {
|
|
16
|
+
const targetDir = join(destinationRoot, name);
|
|
17
|
+
|
|
18
|
+
if (await Bun.file(join(targetDir, "project.config.ts")).exists()) {
|
|
19
|
+
throw new Error(`Le projet ${name} existe déjà.`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const repl = (s: string) => s.replaceAll(EXAMPLE, name).replaceAll("example", name);
|
|
23
|
+
|
|
24
|
+
await mkdir(join(targetDir, "api", "src", "routes"), { recursive: true });
|
|
25
|
+
await mkdir(join(targetDir, "api", "src", "plugins"), { recursive: true });
|
|
26
|
+
await mkdir(join(targetDir, "api", "src", "schemas"), { recursive: true });
|
|
27
|
+
await mkdir(join(targetDir, "web", "src"), { recursive: true });
|
|
28
|
+
await mkdir(join(targetDir, "jobs", "src"), { recursive: true });
|
|
29
|
+
await mkdir(join(targetDir, "scripts"), { recursive: true });
|
|
30
|
+
await mkdir(join(targetDir, "infra", "docker"), { recursive: true });
|
|
31
|
+
await mkdir(join(targetDir, "infra", "caddy"), { recursive: true });
|
|
32
|
+
|
|
33
|
+
await writeFile(
|
|
34
|
+
join(targetDir, "project.config.ts"),
|
|
35
|
+
repl(
|
|
36
|
+
`export default {
|
|
37
|
+
name: "${name}",
|
|
38
|
+
org: "groundZero",
|
|
39
|
+
ports: { api: 3001, web: 3000 },
|
|
40
|
+
env: ["DATABASE_URL", "JWT_SECRET"]
|
|
41
|
+
}
|
|
42
|
+
`
|
|
43
|
+
)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
await writeFile(
|
|
47
|
+
join(targetDir, "api", "package.json"),
|
|
48
|
+
JSON.stringify({ name: `${name}-api`, version: "0.0.0", private: true, type: "module" }, null, 2)
|
|
49
|
+
);
|
|
50
|
+
await writeFile(
|
|
51
|
+
join(targetDir, "api", "bunfig.toml"),
|
|
52
|
+
`[run]
|
|
53
|
+
preload = ["./src/index.ts"]
|
|
54
|
+
`
|
|
55
|
+
);
|
|
56
|
+
await writeFile(
|
|
57
|
+
join(targetDir, "api", "src", "index.ts"),
|
|
58
|
+
`import { app } from "./app";
|
|
59
|
+
|
|
60
|
+
export default app;
|
|
61
|
+
`
|
|
62
|
+
);
|
|
63
|
+
await writeFile(
|
|
64
|
+
join(targetDir, "api", "src", "app.ts"),
|
|
65
|
+
repl(
|
|
66
|
+
`import { Hono } from "hono";
|
|
67
|
+
import { health } from "./routes/health";
|
|
68
|
+
|
|
69
|
+
const app = new Hono();
|
|
70
|
+
app.route("/health", health);
|
|
71
|
+
export { app };
|
|
72
|
+
`
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
await writeFile(
|
|
76
|
+
join(targetDir, "api", "src", "routes", "health.ts"),
|
|
77
|
+
`import { Hono } from "hono";
|
|
78
|
+
|
|
79
|
+
export const health = new Hono().get("/", (c) => c.json({ ok: true }));
|
|
80
|
+
`
|
|
81
|
+
);
|
|
82
|
+
await writeFile(join(targetDir, "api", "src", "plugins", ".gitkeep"), "");
|
|
83
|
+
await writeFile(join(targetDir, "api", "src", "schemas", ".gitkeep"), "");
|
|
84
|
+
|
|
85
|
+
await writeFile(
|
|
86
|
+
join(targetDir, "web", "package.json"),
|
|
87
|
+
JSON.stringify(
|
|
88
|
+
{
|
|
89
|
+
name: `${name}-web`,
|
|
90
|
+
version: "0.0.0",
|
|
91
|
+
private: true,
|
|
92
|
+
type: "module",
|
|
93
|
+
scripts: { dev: "vite", build: "vite build", preview: "vite preview" },
|
|
94
|
+
devDependencies: {
|
|
95
|
+
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
|
96
|
+
svelte: "^5.0.0",
|
|
97
|
+
vite: "^6.0.0",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
null,
|
|
101
|
+
2
|
|
102
|
+
)
|
|
103
|
+
);
|
|
104
|
+
await writeFile(
|
|
105
|
+
join(targetDir, "web", "svelte.config.js"),
|
|
106
|
+
`import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
|
|
107
|
+
|
|
108
|
+
export default {
|
|
109
|
+
preprocess: vitePreprocess(),
|
|
110
|
+
};
|
|
111
|
+
`
|
|
112
|
+
);
|
|
113
|
+
await writeFile(
|
|
114
|
+
join(targetDir, "web", "src", "app.html"),
|
|
115
|
+
repl(
|
|
116
|
+
`<!DOCTYPE html>
|
|
117
|
+
<html lang="fr">
|
|
118
|
+
<head>
|
|
119
|
+
<meta charset="utf-8" />
|
|
120
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
121
|
+
<title>${name}</title>
|
|
122
|
+
</head>
|
|
123
|
+
<body>
|
|
124
|
+
<div id="app"></div>
|
|
125
|
+
<script type="module" src="./main.ts"></script>
|
|
126
|
+
</body>
|
|
127
|
+
</html>
|
|
128
|
+
`
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
await writeFile(
|
|
132
|
+
join(targetDir, "web", "src", "main.ts"),
|
|
133
|
+
`import App from "./App.svelte";
|
|
134
|
+
|
|
135
|
+
const app = new App({ target: document.getElementById("app")! });
|
|
136
|
+
export default app;
|
|
137
|
+
`
|
|
138
|
+
);
|
|
139
|
+
await writeFile(
|
|
140
|
+
join(targetDir, "web", "src", "App.svelte"),
|
|
141
|
+
`<script lang="ts">
|
|
142
|
+
let count = $state(0);
|
|
143
|
+
</script>
|
|
144
|
+
|
|
145
|
+
<button onclick={() => count++}>clicks: {count}</button>
|
|
146
|
+
`
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
await writeFile(
|
|
150
|
+
join(targetDir, "jobs", "bunfig.toml"),
|
|
151
|
+
`[run]
|
|
152
|
+
preload = []
|
|
153
|
+
`
|
|
154
|
+
);
|
|
155
|
+
await writeFile(
|
|
156
|
+
join(targetDir, "jobs", "src", `${name}.job.ts`),
|
|
157
|
+
`#!/usr/bin/env bun
|
|
158
|
+
export async function run() {
|
|
159
|
+
console.log("${name} job");
|
|
160
|
+
}
|
|
161
|
+
`
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
await writeFile(
|
|
165
|
+
join(targetDir, "scripts", `${name}.script.ts`),
|
|
166
|
+
`#!/usr/bin/env bun
|
|
167
|
+
console.log("${name} script");
|
|
168
|
+
`
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
await writeFile(join(targetDir, "infra", "docker", ".gitkeep"), "");
|
|
172
|
+
await writeFile(join(targetDir, "infra", "caddy", ".gitkeep"), "");
|
|
173
|
+
|
|
174
|
+
if (options?.editor && options.editor.ruleIds.length > 0) {
|
|
175
|
+
const rulesDir = join(targetDir, EDITOR_RULES_DIR[options.editor.type]);
|
|
176
|
+
await mkdir(rulesDir, { recursive: true });
|
|
177
|
+
const templateRulesDir = join(templateRoot, ".config", "ai", "rules");
|
|
178
|
+
for (const id of options.editor.ruleIds) {
|
|
179
|
+
const src = join(templateRulesDir, `${id}.md`);
|
|
180
|
+
const content = await readFile(src, "utf-8").catch(() => null);
|
|
181
|
+
if (content) await writeFile(join(rulesDir, `${id}.md`), content);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { writeFile } from "node:fs/promises";
|
|
4
|
+
|
|
5
|
+
export async function generateScript(templateRoot: string, projectName: string, name: string) {
|
|
6
|
+
const scriptPath = join(templateRoot, "projects", projectName, "scripts", `${name}.script.ts`);
|
|
7
|
+
await writeFile(
|
|
8
|
+
scriptPath,
|
|
9
|
+
`#!/usr/bin/env bun
|
|
10
|
+
console.log("${name} script");
|
|
11
|
+
`
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import * as p from "@clack/prompts";
|
|
6
|
+
import { ask, choose, multiselect, say } from "./prompt";
|
|
7
|
+
import { generateProject } from "./generators/project";
|
|
8
|
+
import { AI_EDITORS, AI_RULES, EDITOR_RULES_DIR } from "../.config/ai/manifest";
|
|
9
|
+
import { generateApiRoute } from "./generators/api-route";
|
|
10
|
+
import { generateJob } from "./generators/job";
|
|
11
|
+
import { generateScript } from "./generators/script";
|
|
12
|
+
|
|
13
|
+
const templateRoot = existsSync(join(process.cwd(), ".scripts", "manage.ts"))
|
|
14
|
+
? process.cwd()
|
|
15
|
+
: join(import.meta.dir, "..");
|
|
16
|
+
|
|
17
|
+
const ACTIONS = [
|
|
18
|
+
{ value: "project" as const, label: "Nouveau projet" },
|
|
19
|
+
{ value: "api-route" as const, label: "Nouvelle route API" },
|
|
20
|
+
{ value: "job" as const, label: "Nouveau job" },
|
|
21
|
+
{ value: "script" as const, label: "Nouveau script" },
|
|
22
|
+
{ value: "exit" as const, label: "Quitter" },
|
|
23
|
+
] as const satisfies { value: string; label: string }[];
|
|
24
|
+
|
|
25
|
+
async function main() {
|
|
26
|
+
p.intro("manage");
|
|
27
|
+
|
|
28
|
+
const action = await choose("Que veux-tu faire ?", ACTIONS);
|
|
29
|
+
if (action === "exit") {
|
|
30
|
+
p.outro("À bientôt.");
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (action === "project") {
|
|
35
|
+
const name = await ask("Nom du projet ?");
|
|
36
|
+
const destRaw = await ask("Où créer le projet ?", {
|
|
37
|
+
placeholder: process.cwd(),
|
|
38
|
+
validate: (v) => undefined,
|
|
39
|
+
});
|
|
40
|
+
const destExpanded = destRaw.trim().startsWith("~")
|
|
41
|
+
? join(homedir(), destRaw.trim().slice(1))
|
|
42
|
+
: destRaw.trim();
|
|
43
|
+
const destinationRoot = destExpanded === "" ? process.cwd() : resolve(process.cwd(), destExpanded);
|
|
44
|
+
|
|
45
|
+
const editor = await choose("Editor IA pour les règles ?", [...AI_EDITORS]);
|
|
46
|
+
let ruleIds: string[] = [];
|
|
47
|
+
if (editor !== "none") {
|
|
48
|
+
const selected = await multiselect(
|
|
49
|
+
"Quelles règles inclure ?",
|
|
50
|
+
AI_RULES.map((r) => ({ value: r.id, label: r.label, ...(r.hint && { hint: r.hint }) }))
|
|
51
|
+
);
|
|
52
|
+
ruleIds = selected;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
await generateProject(templateRoot, name, destinationRoot, {
|
|
56
|
+
editor: editor === "none" ? undefined : { type: editor, ruleIds },
|
|
57
|
+
});
|
|
58
|
+
say(`Projet ${name} créé dans ${join(destinationRoot, name)}`, "ok");
|
|
59
|
+
if (editor !== "none" && ruleIds.length > 0) {
|
|
60
|
+
say(`${ruleIds.length} règle(s) copiée(s) dans ${EDITOR_RULES_DIR[editor]}`, "ok");
|
|
61
|
+
}
|
|
62
|
+
p.outro("Done.");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const projectName = await ask("Nom du projet (ex. example) ?");
|
|
67
|
+
|
|
68
|
+
switch (action) {
|
|
69
|
+
case "api-route": {
|
|
70
|
+
const name = await ask("Nom de la route (ex. users) ?");
|
|
71
|
+
await generateApiRoute(templateRoot, projectName, name);
|
|
72
|
+
say(`Route ${name} ajoutée dans projects/${projectName}/api`, "ok");
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case "job": {
|
|
76
|
+
const name = await ask("Nom du job (ex. sync) ?");
|
|
77
|
+
await generateJob(templateRoot, projectName, name);
|
|
78
|
+
say(`Job ${name} créé dans projects/${projectName}/jobs`, "ok");
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case "script": {
|
|
82
|
+
const name = await ask("Nom du script (ex. seed) ?");
|
|
83
|
+
await generateScript(templateRoot, projectName, name);
|
|
84
|
+
say(`Script ${name} créé dans projects/${projectName}/scripts`, "ok");
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
p.outro("Done.");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
main().catch((e) => {
|
|
93
|
+
p.log.error(e instanceof Error ? e.message : String(e));
|
|
94
|
+
process.exit(1);
|
|
95
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
|
|
3
|
+
function orExit<T>(value: T | symbol): T {
|
|
4
|
+
if (p.isCancel(value)) {
|
|
5
|
+
p.cancel("Annulé.");
|
|
6
|
+
process.exit(0);
|
|
7
|
+
}
|
|
8
|
+
return value as T;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function ask(message: string, opts?: { placeholder?: string; validate?: (v: string) => string | void }): Promise<string> {
|
|
12
|
+
const v = p.text({
|
|
13
|
+
message,
|
|
14
|
+
placeholder: opts?.placeholder,
|
|
15
|
+
validate: opts?.validate ?? ((v) => (v.trim() ? undefined : "Requis.")),
|
|
16
|
+
});
|
|
17
|
+
return Promise.resolve(orExit(v));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function choose<T>(message: string, options: { value: T; label: string; hint?: string }[]): Promise<T> {
|
|
21
|
+
const v = p.select({
|
|
22
|
+
message,
|
|
23
|
+
options: options.map((o) => ({ value: o.value, label: o.label, hint: o.hint })),
|
|
24
|
+
});
|
|
25
|
+
return Promise.resolve(orExit(v));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function multiselect<T>(message: string, options: { value: T; label: string; hint?: string }[]): Promise<T[]> {
|
|
29
|
+
const v = p.multiselect({
|
|
30
|
+
message,
|
|
31
|
+
options: options.map((o) => ({ value: o.value, label: o.label, hint: o.hint })),
|
|
32
|
+
required: false,
|
|
33
|
+
});
|
|
34
|
+
return Promise.resolve(orExit(v) as T[]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function say(msg: string, style: "ok" | "dim" = "dim") {
|
|
38
|
+
if (style === "ok") p.log.success(msg);
|
|
39
|
+
else p.log.info(msg);
|
|
40
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Template
|
|
2
|
+
|
|
3
|
+
Monorepo Bun · Svelte 5 · Drizzle · SQLite.
|
|
4
|
+
|
|
5
|
+
## Installation globale (sur ta machine)
|
|
6
|
+
|
|
7
|
+
Depuis la racine du repo :
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun link
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
La commande `manage` est alors disponible partout. Pour la retirer : `bun unlink template`.
|
|
14
|
+
|
|
15
|
+
## Installation via package publié (plus tard)
|
|
16
|
+
|
|
17
|
+
Après publication sur npm (ou autre registry) :
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bunx template
|
|
21
|
+
# ou
|
|
22
|
+
bun install -g template
|
|
23
|
+
manage
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Avant de publier : enlève `"private": true` si présent, choisis un nom dispo (ex. `@ton-user/template`), incrémente `version`, puis `npm publish` ou `bun publish`.
|
|
27
|
+
|
|
28
|
+
## Structure
|
|
29
|
+
|
|
30
|
+
- `.config/` — règles IA, schéma env, standards
|
|
31
|
+
- `.scripts/` — manage.ts et générateurs
|
|
32
|
+
- `packages/shared` — config, env, errors, utils
|
|
33
|
+
- `packages/ui` — composants partagés
|
|
34
|
+
- `projects/<name>/` — api (Hono), web (Svelte), jobs, scripts, infra
|
|
35
|
+
|
|
36
|
+
## Usage (dans le repo)
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
bun install
|
|
40
|
+
bun run manage
|
|
41
|
+
# ou après bun link :
|
|
42
|
+
manage
|
|
43
|
+
```
|
package/bunfig.toml
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lv00/new",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "CLI monorepo Bun · Svelte 5 · Drizzle",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"manage": ".scripts/manage.ts"
|
|
8
|
+
},
|
|
9
|
+
"workspaces": ["packages/*"],
|
|
10
|
+
"files": [
|
|
11
|
+
".config",
|
|
12
|
+
".scripts",
|
|
13
|
+
"packages",
|
|
14
|
+
"projects",
|
|
15
|
+
"bunfig.toml"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"manage": "bun run .scripts/manage.ts"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@clack/prompts": "^0.8.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"bun-types": "latest",
|
|
25
|
+
"@types/node": "latest"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "caca-web",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
|
13
|
+
"svelte": "^5.0.0",
|
|
14
|
+
"vite": "^6.0.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="fr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>caca</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="app"></div>
|
|
10
|
+
<script type="module" src="./main.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "example-web",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
|
13
|
+
"svelte": "^5.0.0",
|
|
14
|
+
"vite": "^6.0.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="fr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>example</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="app"></div>
|
|
10
|
+
<script type="module" src="./main.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "myapp-web",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
|
13
|
+
"svelte": "^5.0.0",
|
|
14
|
+
"vite": "^6.0.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="fr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>myapp</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="app"></div>
|
|
10
|
+
<script type="module" src="./main.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "test-web",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
|
13
|
+
"svelte": "^5.0.0",
|
|
14
|
+
"vite": "^6.0.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="fr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>test</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="app"></div>
|
|
10
|
+
<script type="module" src="./main.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|