create-wakii-stack 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -81,7 +81,7 @@ function resolveOptions(options, ctx) {
81
81
  }
82
82
 
83
83
  // src/scaffold.ts
84
- import { mkdir, readFile, readdir, rm, writeFile } from "fs/promises";
84
+ import { chmod, mkdir, readFile, readdir, rm, stat, writeFile } from "fs/promises";
85
85
  import { existsSync } from "fs";
86
86
  import { dirname, join, resolve } from "path";
87
87
  import { fileURLToPath } from "url";
@@ -186,7 +186,20 @@ function pruneEnv(text, keys) {
186
186
  }).join("\n").replace(/\n{3,}/g, "\n\n");
187
187
  }
188
188
  function renderReadme(manifest, selection) {
189
- const ports = Object.entries(manifest.ports ?? {}).map(([name, url]) => `- ${name}: ${url}`).join("\n");
189
+ const gatewayUrl = manifest.ports?.gateway ?? "http://localhost:8080";
190
+ const chosen = new Set(selection.services);
191
+ const ports = Object.entries(manifest.ports ?? {}).filter(([name]) => chosen.has(name)).map(([name, url]) => {
192
+ const note = name === "gateway" ? " \u2014 entry duy nh\u1EA5t: `/` = FE shell, `/api/*` = services" : name === "shell" ? " \u2014 static-only (login \u1EDF \u0111\xE2y b\xE1o 405 \u2014 d\xF9ng gateway)" : "";
193
+ return `- ${name}: ${url}${note}`;
194
+ }).join("\n");
195
+ const access = chosen.has("gateway") ? `## Truy c\u1EADp (sau khi up)
196
+
197
+ M\u1EDF **${gatewayUrl}** (gateway) \u2014 entry duy nh\u1EA5t: \`/\` l\xE0 FE shell, \`/api/*\` l\xE0 c\xE1c service.
198
+
199
+ - Ch\u01B0a c\xF3 t\xE0i kho\u1EA3n? auth-svc KH\xD4NG seed user \u2014 d\xF9ng tab **\u0110\u0103ng k\xFD** tr\u01B0\u1EDBc, r\u1ED3i \u0110\u0103ng nh\u1EADp.
200
+ - KH\xD4NG m\u1EDF port FE tr\u1EF1c ti\u1EBFp (vd \`:3000\`): \u0111\xF3 l\xE0 static server, m\u1ECDi \`POST /api/*\` tr\u1EA3 **405**.
201
+
202
+ ` : "";
190
203
  return `# ${selection.appName}
191
204
 
192
205
  Stack generated by \`create-wakii-stack\` (Hybrid C \u2014 full template pruned to your selection).
@@ -195,7 +208,7 @@ Stack generated by \`create-wakii-stack\` (Hybrid C \u2014 full template pruned
195
208
 
196
209
  ${selection.services.map((s) => `- ${s} _(${manifest.services[s]?.layer ?? "?"})_`).join("\n")}
197
210
 
198
- ## Run
211
+ ${access}## Run
199
212
 
200
213
  \`\`\`bash
201
214
  cd ${selection.appName}
@@ -209,7 +222,7 @@ ${manifest.smoke ? `## Smoke
209
222
  ${manifest.smoke}
210
223
  \`\`\`
211
224
  ` : ""}
212
- ${ports ? `## URLs
225
+ ${ports ? `## Ports
213
226
 
214
227
  ${ports}
215
228
  ` : ""}
@@ -228,6 +241,14 @@ async function scaffold(input) {
228
241
  await copyTree(templateRoot, dest, { stackName: selection.appName, files });
229
242
  const allServices = Object.keys(manifest.services);
230
243
  const pruned = allServices.filter((s) => !selection.services.includes(s));
244
+ const ALWAYS_PRUNE = ["app/create-wakii-app", "app/create-wakii-stack", "app/starter-templates", "app/stack-template", "app/spike"];
245
+ for (const relPath of ALWAYS_PRUNE) {
246
+ const target = join(dest, relPath);
247
+ if (existsSync(target)) {
248
+ await rm(target, { recursive: true, force: true });
249
+ log2?.(`Pruned tooling: ${relPath}`);
250
+ }
251
+ }
231
252
  for (const name of pruned) {
232
253
  for (const rawPath of manifest.services[name].paths ?? []) {
233
254
  const relPath = rawPath.endsWith("/**") ? rawPath.slice(0, -3) : rawPath;
@@ -263,8 +284,32 @@ async function scaffold(input) {
263
284
  }
264
285
  }
265
286
  await writeFile(join(dest, "README.md"), renderReadme(manifest, selection));
287
+ const execFixed = await fixExecutableBits(dest);
288
+ if (execFixed > 0) log2?.(`chmod +x: ${execFixed} script`);
266
289
  return { files: [...files.map((f) => f.slice(dest.length + 1)), "README.md"], prunedServices: pruned };
267
290
  }
291
+ async function fixExecutableBits(root) {
292
+ let count = 0;
293
+ const walk = async (dir) => {
294
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
295
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
296
+ const full = join(dir, entry.name);
297
+ if (entry.isDirectory()) {
298
+ await walk(full);
299
+ continue;
300
+ }
301
+ if (/\.(sh)$/.test(entry.name) || entry.name === "mvnw" || entry.name === "gradlew") {
302
+ const st = await stat(full);
303
+ if (!(st.mode & 73)) {
304
+ await chmod(full, st.mode | 493);
305
+ count += 1;
306
+ }
307
+ }
308
+ }
309
+ };
310
+ await walk(root);
311
+ return count;
312
+ }
268
313
 
269
314
  // src/index.ts
270
315
  var USAGE = `create-wakii-stack \u2014 scaffold full-stack FE+BE (Hybrid C: full-copy + prune)
@@ -280,16 +325,18 @@ Flags:
280
325
 
281
326
  Rules t\u1EA7ng 1: \u22651 BE t\u1EF1 k\xE8m postgres+kafka+gateway+auth-svc; \u22651 FE t\u1EF1 k\xE8m gateway.
282
327
  `;
283
- function printNextSteps(selection, templateRoot) {
284
- const steps = [`cd ${selection.appName}`];
285
- if (existsSync2(join2(templateRoot, "platform/.env.example")) || true) {
286
- steps.push("cp platform/.env.example platform/.env # \u0111i\u1EC1n secret tr\u01B0\u1EDBc khi up");
287
- }
288
- steps.push("docker compose --env-file platform/.env up -d");
328
+ function printNextSteps(selection, gatewayUrl) {
329
+ const steps = [
330
+ `cd ${selection.appName}`,
331
+ "cp platform/.env.example platform/.env # \u0111i\u1EC1n secret tr\u01B0\u1EDBc khi up",
332
+ "docker compose --env-file platform/.env up -d"
333
+ ];
289
334
  if (!selection.skipInstall) steps.push("# FE apps: pnpm install trong t\u1EEBng app mu\u1ED1n ch\u1EA1y build c\u1EE5c b\u1ED9");
290
335
  p.log.info("Next steps:");
291
336
  for (const step of steps) p.log.step(` ${step}`);
292
- p.log.info("URLs: gateway http://localhost:8080 \xB7 shell http://localhost:3000 (n\u1EBFu ch\u1ECDn FE)");
337
+ const entry = gatewayUrl ?? "http://localhost:8080";
338
+ p.log.info(`M\u1EDF ${entry} (gateway \u2014 entry duy nh\u1EA5t: / l\xE0 FE shell, /api/* l\xE0 services). \u0110\u0103ng k\xFD t\xE0i kho\u1EA3n tr\u01B0\u1EDBc khi login (auth-svc kh\xF4ng seed user).`);
339
+ p.log.warn("Port FE tr\u1EF1c ti\u1EBFp (vd :3000) ch\u1EC9 l\xE0 static server \u2014 login \u1EDF \u0111\xF3 b\xE1o HTTP 405.");
293
340
  p.log.info("FE-only SPA ngo\xE0i stack? pnpm create wakii-app");
294
341
  }
295
342
  async function main() {
@@ -383,7 +430,7 @@ async function main() {
383
430
  return;
384
431
  }
385
432
  p.outro("Stack s\u1EB5n s\xE0ng! \u{1F389}");
386
- printNextSteps(selection, DEFAULT_TEMPLATE_ROOT);
433
+ printNextSteps(selection, manifest.ports?.gateway);
387
434
  }
388
435
  main().catch((err) => {
389
436
  p.log.error(err instanceof Error ? err.message : String(err));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-wakii-stack",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "CLI scaffold full-stack (FE+BE) dùng @wakii/ui-kit — full-copy + prune theo manifest (spec Hybrid C)",
6
6
  "bin": {
@@ -279,5 +279,16 @@
279
279
  "when": "không chọn service be nào và không chọn option fe nào",
280
280
  "action": "engine phải dừng với lỗi rõ ràng — stack trống không hợp lệ"
281
281
  }
282
+ },
283
+ "ports": {
284
+ "gateway": "http://localhost:8080",
285
+ "shell": "http://localhost:3000",
286
+ "remote-tasks": "http://localhost:4001",
287
+ "remote-activity": "http://localhost:4002",
288
+ "auth-svc": "http://localhost:8090",
289
+ "node-svc": "http://localhost:3001",
290
+ "spring-svc": "http://localhost:8081",
291
+ "go-svc": "http://localhost:8082",
292
+ "postgres": "localhost:5432"
282
293
  }
283
294
  }