chiwaa 0.1.11 → 0.1.12

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.
Files changed (2) hide show
  1. package/bin/chiwaa.js +55 -3
  2. package/package.json +1 -1
package/bin/chiwaa.js CHANGED
@@ -236,6 +236,7 @@ function buildIfNeeded(dir, args) {
236
236
  const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
237
237
  const found = OUTPUTS.find(([n]) => deps[n]);
238
238
  let outName = args.dir || (found ? found[1] : "dist");
239
+ if (found && found[0] === "next" && !args.dir) checkNext(dir);
239
240
  if (args["no-build"]) {
240
241
  console.log(c.dim("빌드는 건너뜁니다(--no-build)."));
241
242
  } else {
@@ -249,10 +250,60 @@ function buildIfNeeded(dir, args) {
249
250
  out = path.join(dir, alt);
250
251
  }
251
252
  if (found && found[0] === "next" && !fs.existsSync(path.join(out, "index.html")))
252
- fail("Next.js 는 next.config 에 output: 'export' 를 넣어야 올릴 수 있습니다.");
253
+ fail(NEXT_EXPORT_MSG);
253
254
  return { out, built: true };
254
255
  }
255
256
 
257
+ // Next.js: 서버에서 돌아가는 기능(API 주소·middleware·서버 액션·서버 렌더링)은 chiwaa 에서 돌지 않는다 — 빌드 전에 알린다
258
+ // (빌드 서버 chiwaa-deployd 의 next_server_features 와 같은 기준)
259
+ const AI_GUIDE = "안내: https://chiwaa.kr/llms.txt";
260
+ const NEXT_EXPORT_MSG =
261
+ "Next.js 는 next.config 에 output: 'export' 를 넣어야 올릴 수 있습니다(예: const nextConfig = { output: \"export\", images: { unoptimized: true } }).\n" +
262
+ `AI 에게 「이 Next.js 앱을 chiwaa 에 올릴 수 있게 output: 'export' 정적 사이트로 바꿔 줘. ${AI_GUIDE}」라고 말해 보세요.`;
263
+ function nextServerFeatures(dir) {
264
+ const found = [];
265
+ const pat = /['"]use server['"]|getServerSideProps|from\s+['"]next\/headers['"]/;
266
+ (function walk(d, rel) {
267
+ for (const e of fs.readdirSync(d, { withFileTypes: true })) {
268
+ if (e.isSymbolicLink() || e.name.startsWith(".")) continue;
269
+ const r = rel ? `${rel}/${e.name}` : e.name;
270
+ const full = path.join(d, e.name);
271
+ if (e.isDirectory()) {
272
+ if (!["node_modules", "out", "public"].includes(e.name)) walk(full, r);
273
+ continue;
274
+ }
275
+ const m = e.name.match(/^(.*)\.(js|jsx|ts|tsx|mjs)$/);
276
+ if (!m) continue;
277
+ if ((m[1] === "middleware" && ["", "src"].includes(rel)) ||
278
+ (m[1] === "route" && /(^|\/)app(\/|$)/.test(rel)) ||
279
+ /^(src\/)?pages\/api(\/|$)/.test(rel)) found.push(r);
280
+ else if (fs.statSync(full).size <= 300 * 1024 && pat.test(fs.readFileSync(full, "utf8"))) found.push(r);
281
+ }
282
+ })(dir, "");
283
+ return found;
284
+ }
285
+ function checkNext(dir) {
286
+ const server = nextServerFeatures(dir);
287
+ if (server.length) {
288
+ const shown = server.slice(0, 4).join(", ") + (server.length > 4 ? ` 등 ${server.length}곳` : "");
289
+ fail(`이 Next.js 앱은 서버에서 돌아가는 기능을 씁니다(${shown}). chiwaa 는 화면 파일(HTML·JS)을 올리는 곳이라 이대로는 열리지 않습니다.\n` +
290
+ `AI 에게 「이 Next.js 앱을 chiwaa 에 올릴 수 있게 output: 'export' 정적 사이트로 바꾸고, API·middleware 같은 서버 기능은 맨 위 api/ 폴더의 chiwaa 서버 함수로 옮겨 줘. ${AI_GUIDE}」라고 말해 보세요.`);
291
+ }
292
+ const conf = ["next.config.js", "next.config.mjs", "next.config.cjs", "next.config.ts"].find((n) => fs.existsSync(path.join(dir, n)));
293
+ if (!conf || !/output\s*:\s*['"]export['"]/.test(fs.readFileSync(path.join(dir, conf), "utf8"))) fail(NEXT_EXPORT_MSG);
294
+ }
295
+
296
+ // 첫 화면(index.html)이 없는 폴더를 올리려 하면, 앱이나 HTML 이 들어 있는 하위 폴더를 알려 주고 멈춘다
297
+ function noIndexMessage(dir) {
298
+ const subs = fs.readdirSync(dir, { withFileTypes: true })
299
+ .filter((e) => e.isDirectory() && !e.name.startsWith(".") && !SKIP.has(e.name) &&
300
+ ["package.json", "index.html"].some((f) => fs.existsSync(path.join(dir, e.name, f))))
301
+ .map((e) => e.name);
302
+ return "첫 화면 파일(index.html)이 없어 올리지 않았습니다(소스가 그대로 공개되지 않게).\n" +
303
+ (subs.length ? `앱이 하위 폴더에 있는 것 같습니다. 그 폴더에서 올려 보세요: ${subs.slice(0, 3).map((s) => `cd ${s} && npx chiwaa deploy`).join(" / ")}\n` : "") +
304
+ `AI 에게 「이 프로젝트를 chiwaa 에 올릴 수 있게 index.html 이 나오는 정적 사이트로 만들어 줘. ${AI_GUIDE}」라고 말해 보세요.`;
305
+ }
306
+
256
307
  // ---------- 사이트 정하기 ----------
257
308
  // 사용자가 직접 정한 이름은 바꾸지 않는다 — 규칙에 안 맞으면 거절한다(대문자만 소문자로)
258
309
  const NAME_RULE = "사이트 이름은 영문 소문자·숫자·하이픈(-)으로 2~30자, 첫 글자는 영문이나 숫자여야 합니다(하이픈 두 개 연속은 안 됨). 예: my-shop, cafe2026";
@@ -382,8 +433,7 @@ async function deploy(args) {
382
433
  }
383
434
  const fnCount = files.filter((f) => !f.dir && /^api\/[^/_][^/]*\.(js|ts|mjs)$/.test(f.rel)).length;
384
435
  if (fnCount) console.log(c.dim(`서버 함수 ${fnCount}개(api/) 함께 올림 → https://<사이트>/api/<이름>`));
385
- if (!files.some((f) => !f.dir && /^index\.html?$/i.test(f.rel)))
386
- console.log(c.dim("참고: 맨 위에 index.html 이 없습니다. 주소로 열면 첫 화면이 안 보일 수 있습니다."));
436
+ if (!files.some((f) => !f.dir && f.rel === "index.html")) fail(noIndexMessage(out));
387
437
  const fileCount = files.filter((f) => !f.dir).length;
388
438
  console.log(c.dim(`올리는 중: ${path.relative(process.cwd(), out) || "."} — 파일 ${fileCount}개, ${(total / 1048576).toFixed(1)}MB`));
389
439
  const gz = makeTarGz(files);
@@ -395,6 +445,8 @@ async function deploy(args) {
395
445
  });
396
446
  if (r.status !== 200 || !r.data.ok) fail(r.data.message || r.data.error || `배포하지 못했습니다 (${r.status}).`);
397
447
  console.log(c.green("✓"), `${preview ? "미리보기 " : ""}배포 완료 ${c.dim(`(${((Date.now() - t0) / 1000).toFixed(1)}초)`)}`);
448
+ const dropped = String(r.data.message || "").indexOf("뺀 파일:");
449
+ if (dropped >= 0) console.log(c.dim(r.data.message.slice(dropped).replace(/\)\s*$/, "")));
398
450
  console.log(`\n ${c.bold(c.cyan(r.data.url))}\n`);
399
451
  if (preview) console.log(c.dim("운영 주소는 그대로입니다. 괜찮으면 --preview 없이 다시 deploy 하세요. (미리보기는 검색엔진에 나오지 않음)\n"));
400
452
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chiwaa",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "chiwaa.kr 에 사이트를 터미널에서 바로 올리는 도구",
5
5
  "bin": {
6
6
  "chiwaa": "bin/chiwaa.js"