gencow 0.1.116 → 0.1.118

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/bin/gencow.mjs CHANGED
@@ -16,7 +16,7 @@
16
16
  */
17
17
 
18
18
  import { execSync, spawn } from "child_process";
19
- import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync, cpSync, readdirSync, rmSync, statSync, symlinkSync, copyFileSync } from "fs";
19
+ import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync, unlinkSync, cpSync, readdirSync, rmSync, statSync, symlinkSync, copyFileSync } from "fs";
20
20
  import { resolve, dirname, basename } from "path";
21
21
  import { homedir } from "os";
22
22
  import { fileURLToPath } from "url";
@@ -232,17 +232,108 @@ function isStandaloneProject() {
232
232
  }
233
233
 
234
234
  /**
235
- * drizzle-kit CLI 명령어 빌드 — 로컬 바이너리 우선 사용.
235
+ * drizzle-kit CLI 명령어 빌드 — 로컬 바이너리 우선 사용 + esbuild 정합성 체크.
236
236
  * pnpm 모노레포에서 npx가 esbuild 네이티브 바이너리를 잘못 resolve하여
237
237
  * "Host version X does not match binary version Y" 에러 방지.
238
- * 로컬 ./node_modules/.bin/drizzle-kit이 있으면 직접 실행, 없으면 npx fallback.
238
+ *
239
+ * 반환: { cmd: string, env: Record<string, string> }
240
+ * - cmd: 실행할 drizzle-kit 명령어
241
+ * - env: esbuild 불일치 시 ESBUILD_BINARY_PATH를 포함한 환경변수
239
242
  */
240
243
  function _drizzleKitCmd(subcmd) {
241
244
  const localBin = resolve(process.cwd(), "node_modules/.bin/drizzle-kit");
242
- if (existsSync(localBin)) {
243
- return `"${localBin}" ${subcmd}`;
244
- }
245
- return `npx drizzle-kit ${subcmd}`;
245
+ const cmd = existsSync(localBin) ? `"${localBin}" ${subcmd}` : `npx drizzle-kit ${subcmd}`;
246
+ const env = _ensureEsbuildConsistency();
247
+ return { cmd, env };
248
+ }
249
+
250
+ /**
251
+ * esbuild JS host와 네이티브 바이너리 버전 정합성 체크.
252
+ * 불일치 감지 시:
253
+ * 1) 즉시: ESBUILD_BINARY_PATH로 올바른 바이너리 지정 (이번 실행 성공)
254
+ * 2) 영구: .npmrc에 hoisting 차단 규칙 추가 (다음 pnpm install 시 완전 해결)
255
+ *
256
+ * 결과는 프로세스당 1회만 계산하여 캐싱 (watch 모드에서 반복 파일 I/O 방지).
257
+ *
258
+ * @returns {Record<string, string>} 환경변수 (정상이면 빈 객체)
259
+ */
260
+ let _esbuildEnvCache = null;
261
+ function _ensureEsbuildConsistency() {
262
+ if (_esbuildEnvCache !== null) return _esbuildEnvCache;
263
+ try {
264
+ const cwd = process.cwd();
265
+ const esbuildPkg = resolve(cwd, "node_modules/esbuild/package.json");
266
+ if (!existsSync(esbuildPkg)) return (_esbuildEnvCache = {});
267
+
268
+ const hostVersion = JSON.parse(readFileSync(esbuildPkg, "utf8")).version;
269
+ const platform = process.platform;
270
+ const arch = process.arch;
271
+ const binaryPkg = resolve(cwd, `node_modules/@esbuild/${platform}-${arch}/package.json`);
272
+ if (!existsSync(binaryPkg)) return (_esbuildEnvCache = {});
273
+
274
+ const binaryVersion = JSON.parse(readFileSync(binaryPkg, "utf8")).version;
275
+ if (hostVersion === binaryVersion) return (_esbuildEnvCache = {});
276
+
277
+ // ── 불일치 감지 → 2단계 수정 ──
278
+ warn(`esbuild 버전 불일치 감지: host=${hostVersion}, binary=${binaryVersion}`);
279
+
280
+ const env = {};
281
+
282
+ // 1) 즉시 우회: pnpm virtual store에서 올바른 바이너리 resolve
283
+ try {
284
+ const req = createRequire(resolve(cwd, "node_modules/drizzle-kit/package.json"));
285
+ const esbuildDir = dirname(req.resolve("esbuild/package.json"));
286
+ const esbuildVer = JSON.parse(readFileSync(resolve(esbuildDir, "package.json"), "utf8")).version;
287
+ const reqFromEsbuild = createRequire(resolve(esbuildDir, "package.json"));
288
+ try {
289
+ const platformPkgDir = dirname(reqFromEsbuild.resolve(`@esbuild/${platform}-${arch}/package.json`));
290
+ const binary = resolve(platformPkgDir, "bin/esbuild");
291
+ if (existsSync(binary)) {
292
+ env.ESBUILD_BINARY_PATH = binary;
293
+ info(`ESBUILD_BINARY_PATH → esbuild@${esbuildVer} 바이너리 사용`);
294
+ }
295
+ } catch { /* platform binary resolve 실패 — fallback 없이 계속 */ }
296
+ } catch { /* drizzle-kit esbuild resolve 실패 */ }
297
+
298
+ // 2) 영구 수정: .npmrc에 hoisting 차단 규칙 추가
299
+ _patchNpmrcForEsbuild(cwd);
300
+
301
+ return (_esbuildEnvCache = env);
302
+ } catch { return (_esbuildEnvCache = {}); }
303
+ }
304
+
305
+ /**
306
+ * 모노레포 루트의 .npmrc에 esbuild hoisting 차단 규칙을 추가.
307
+ * 이미 존재하면 스킵.
308
+ */
309
+ function _patchNpmrcForEsbuild(cwd) {
310
+ try {
311
+ // pnpm-workspace.yaml이 있는 디렉토리를 모노레포 루트로 판단
312
+ let dir = cwd;
313
+ for (let i = 0; i < 10; i++) {
314
+ if (existsSync(resolve(dir, "pnpm-workspace.yaml"))) {
315
+ const npmrcPath = resolve(dir, ".npmrc");
316
+ const content = existsSync(npmrcPath) ? readFileSync(npmrcPath, "utf8") : "";
317
+ if (!content.includes("hoist-pattern[]=!@esbuild/*")) {
318
+ const patch = [
319
+ "",
320
+ "# [gencow] esbuild 네이티브 바이너리 hoisting 충돌 방지 (자동 추가)",
321
+ "# 📄 docs/analysis/analysis-deploy-esbuild-version-mismatch.md",
322
+ "hoist-pattern[]=!@esbuild/*",
323
+ "hoist-pattern[]=!esbuild",
324
+ "",
325
+ ].join("\n");
326
+ appendFileSync(npmrcPath, patch);
327
+ info(".npmrc에 esbuild hoisting 차단 규칙을 추가했습니다.");
328
+ info("다음 pnpm install 시 영구 적용됩니다.");
329
+ }
330
+ return;
331
+ }
332
+ const parent = resolve(dir, "..");
333
+ if (parent === dir) break;
334
+ dir = parent;
335
+ }
336
+ } catch { /* .npmrc 수정 실패 — 무시 (즉시 우회로 이번 실행은 문제 없음) */ }
246
337
  }
247
338
 
248
339
  function findServerRoot() {
@@ -1310,9 +1401,10 @@ ${hasPrompt ? `
1310
1401
  });
1311
1402
  } else {
1312
1403
  // Standalone: use npx drizzle-kit directly
1313
- execSync(_drizzleKitCmd("generate"), {
1404
+ const dk = _drizzleKitCmd("generate");
1405
+ execSync(dk.cmd, {
1314
1406
  cwd: process.cwd(),
1315
- env: genEnv,
1407
+ env: { ...genEnv, ...dk.env },
1316
1408
  stdio: "inherit", // 프롬프트 패스스루 (rename 등)
1317
1409
  });
1318
1410
  }
@@ -1494,8 +1586,10 @@ ${hasPrompt ? `
1494
1586
  // 🆕 로컬 drizzle-kit generate 실행 (프롬프트 패스스루)
1495
1587
  info("스키마 마이그레이션 생성 중...");
1496
1588
  try {
1497
- execSync(_drizzleKitCmd("generate"), {
1589
+ const dk = _drizzleKitCmd("generate");
1590
+ execSync(dk.cmd, {
1498
1591
  cwd: process.cwd(),
1592
+ env: { ...process.env, ...dk.env },
1499
1593
  stdio: "inherit",
1500
1594
  });
1501
1595
  success("마이그레이션 생성 완료");
@@ -1562,9 +1656,10 @@ ${hasPrompt ? `
1562
1656
  runInServer("pnpm db:generate", buildEnv(config));
1563
1657
  } else {
1564
1658
  // Standalone: npx drizzle-kit generate (프롬프트 패스스루)
1565
- execSync(_drizzleKitCmd("generate"), {
1659
+ const dk = _drizzleKitCmd("generate");
1660
+ execSync(dk.cmd, {
1566
1661
  cwd: process.cwd(),
1567
- env: buildEnv(config),
1662
+ env: { ...buildEnv(config), ...dk.env },
1568
1663
  stdio: "inherit",
1569
1664
  });
1570
1665
  }
@@ -2046,8 +2141,10 @@ ${BOLD}Examples:${RESET}
2046
2141
  });
2047
2142
  } else {
2048
2143
  // Standalone: npx drizzle-kit generate (프롬프트 패스스루 — rename 감지 대화형 포함)
2049
- execGen(_drizzleKitCmd("generate"), {
2144
+ const dk = _drizzleKitCmd("generate");
2145
+ execGen(dk.cmd, {
2050
2146
  cwd: process.cwd(),
2147
+ env: { ...process.env, ...dk.env },
2051
2148
  stdio: "inherit",
2052
2149
  });
2053
2150
  }
@@ -2572,8 +2669,10 @@ ${BOLD}Examples:${RESET}
2572
2669
  if (existsSync(schemaPath)) {
2573
2670
  info("스키마 마이그레이션 생성 중...");
2574
2671
  try {
2575
- execSync(_drizzleKitCmd("generate"), {
2672
+ const dk = _drizzleKitCmd("generate");
2673
+ execSync(dk.cmd, {
2576
2674
  cwd: backendRoot,
2675
+ env: { ...process.env, ...dk.env },
2577
2676
  stdio: "inherit", // ← 프롬프트 패스스루!
2578
2677
  });
2579
2678
  success("마이그레이션 생성 완료");
@@ -4333,8 +4432,10 @@ process.exit(0);
4333
4432
  }
4334
4433
  try {
4335
4434
  const { execSync } = await import("child_process");
4336
- execSync(_drizzleKitCmd("generate"), {
4435
+ const dk = _drizzleKitCmd("generate");
4436
+ execSync(dk.cmd, {
4337
4437
  cwd: process.cwd(),
4438
+ env: { ...process.env, ...dk.env },
4338
4439
  stdio: "inherit",
4339
4440
  });
4340
4441
  log(`${DIM}${ts}${RESET} ${GREEN}[migrate]${RESET} ✔ 마이그레이션 생성 완료`);
package/core/index.js CHANGED
@@ -1800,11 +1800,13 @@ function parseArgs(schema, args) {
1800
1800
  }
1801
1801
  }
1802
1802
  if (typeof schema === "object" && schema !== null) {
1803
+ const schemaKeys = Object.keys(schema);
1804
+ if (schemaKeys.length === 0) return args;
1803
1805
  if (typeof args !== "object" || args === null) {
1804
1806
  throw new GencowValidationError("Expected an object for arguments");
1805
1807
  }
1806
1808
  const result = {};
1807
- for (const key in schema) {
1809
+ for (const key of schemaKeys) {
1808
1810
  const validator = schema[key];
1809
1811
  if (validator && typeof validator.parse === "function") {
1810
1812
  try {