buncargo 1.0.8 → 1.0.11

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.ts CHANGED
@@ -12,92 +12,18 @@
12
12
  */
13
13
 
14
14
  import { runCli } from "./cli";
15
- import { createDevEnvironment } from "./environment";
16
- import type { AppConfig, DevEnvironment, ServiceConfig } from "./types";
17
-
18
- // ═══════════════════════════════════════════════════════════════════════════
19
- // Config Discovery
20
- // ═══════════════════════════════════════════════════════════════════════════
21
-
22
- const CONFIG_FILES = [
23
- "dev.config.ts",
24
- "dev.config.js",
25
- "dev-tools.config.ts",
26
- "dev-tools.config.js",
27
- ];
15
+ import { loadDevEnv } from "./loader";
28
16
 
29
17
  /**
30
- * Find and load the dev config file from the current directory.
31
- * Returns the DevEnvironment created from the config.
18
+ * Load the dev environment with CLI-friendly error output.
32
19
  */
33
- async function loadEnv(): Promise<
34
- DevEnvironment<Record<string, ServiceConfig>, Record<string, AppConfig>>
35
- > {
36
- const cwd = process.cwd();
37
-
38
- for (const file of CONFIG_FILES) {
39
- const path = `${cwd}/${file}`;
40
- const exists = await Bun.file(path).exists();
41
-
42
- if (exists) {
43
- try {
44
- const mod = await import(path);
45
- const config = mod.default;
46
-
47
- if (!config) {
48
- console.error(
49
- `❌ Config file "${file}" found but no default export.`,
50
- );
51
- console.error("");
52
- console.error(" Export your config as default:");
53
- console.error("");
54
- console.error(" import { defineDevConfig } from 'buncargo'");
55
- console.error("");
56
- console.error(" export default defineDevConfig({ ... })");
57
- process.exit(1);
58
- }
59
-
60
- // Validate it looks like a config
61
- if (!config.projectPrefix || !config.services) {
62
- console.error(`❌ Config file "${file}" is not a valid dev config.`);
63
- console.error("");
64
- console.error(" Make sure to use defineDevConfig:");
65
- console.error("");
66
- console.error(" export default defineDevConfig({");
67
- console.error(" projectPrefix: 'myapp',");
68
- console.error(" services: { ... }");
69
- console.error(" })");
70
- process.exit(1);
71
- }
72
-
73
- // Create environment from config
74
- return createDevEnvironment(config);
75
- } catch (error) {
76
- console.error(`❌ Failed to load config file "${file}":`);
77
- console.error(error);
78
- process.exit(1);
79
- }
80
- }
81
- }
82
-
83
- console.error("❌ No config file found.");
84
- console.error("");
85
- console.error(" Create a dev.config.ts file in your project root:");
86
- console.error("");
87
- console.error(" import { defineDevConfig } from 'buncargo'");
88
- console.error("");
89
- console.error(" export default defineDevConfig({");
90
- console.error(" projectPrefix: 'myapp',");
91
- console.error(" services: {");
92
- console.error(' postgres: { port: 5432, healthCheck: "pg_isready" }');
93
- console.error(" }");
94
- console.error(" })");
95
- console.error("");
96
- console.error(" Supported config files:");
97
- for (const file of CONFIG_FILES) {
98
- console.error(` - ${file}`);
20
+ async function loadEnv() {
21
+ try {
22
+ return await loadDevEnv();
23
+ } catch (error) {
24
+ console.error(`❌ ${error instanceof Error ? error.message : error}`);
25
+ process.exit(1);
99
26
  }
100
- process.exit(1);
101
27
  }
102
28
 
103
29
  // ═══════════════════════════════════════════════════════════════════════════
package/dist/bin.js CHANGED
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env bun
2
2
  import {
3
3
  runCli
4
- } from "./index-xcx25j3c.js";
4
+ } from "./index-twc5zwja.js";
5
5
  import {
6
- createDevEnvironment
7
- } from "./index-cft124d3.js";
6
+ loadDevEnv
7
+ } from "./index-habjkmf5.js";
8
+ import"./index-hb4y6f08.js";
8
9
  import"./index-8gd8fkwx.js";
9
10
  import"./index-6qd90dkp.js";
10
11
  import"./index-tjqw9vtj.js";
11
- import"./index-tn0r1rx7.js";
12
+ import"./index-5hka0tff.js";
12
13
  import"./index-2fr3g85b.js";
13
14
  import"./index-6fm7mvwj.js";
14
15
  import"./index-08wa79cs.js";
@@ -20,7 +21,7 @@ import {
20
21
  var require_package = __commonJS((exports, module) => {
21
22
  module.exports = {
22
23
  name: "buncargo",
23
- version: "1.0.8",
24
+ version: "1.0.11",
24
25
  description: "A Bun-powered development environment CLI for managing Docker Compose services, dev servers, and environment variables",
25
26
  type: "module",
26
27
  module: "./dist/index.js",
@@ -156,74 +157,20 @@ var require_package = __commonJS((exports, module) => {
156
157
  typescript: "^5.7.0"
157
158
  },
158
159
  dependencies: {
160
+ "fast-glob": "^3.3.3",
159
161
  picocolors: "^1.1.1"
160
162
  }
161
163
  };
162
164
  });
163
165
 
164
166
  // bin.ts
165
- var CONFIG_FILES = [
166
- "dev.config.ts",
167
- "dev.config.js",
168
- "dev-tools.config.ts",
169
- "dev-tools.config.js"
170
- ];
171
167
  async function loadEnv() {
172
- const cwd = process.cwd();
173
- for (const file of CONFIG_FILES) {
174
- const path = `${cwd}/${file}`;
175
- const exists = await Bun.file(path).exists();
176
- if (exists) {
177
- try {
178
- const mod = await import(path);
179
- const config = mod.default;
180
- if (!config) {
181
- console.error(`❌ Config file "${file}" found but no default export.`);
182
- console.error("");
183
- console.error(" Export your config as default:");
184
- console.error("");
185
- console.error(" import { defineDevConfig } from 'buncargo'");
186
- console.error("");
187
- console.error(" export default defineDevConfig({ ... })");
188
- process.exit(1);
189
- }
190
- if (!config.projectPrefix || !config.services) {
191
- console.error(`❌ Config file "${file}" is not a valid dev config.`);
192
- console.error("");
193
- console.error(" Make sure to use defineDevConfig:");
194
- console.error("");
195
- console.error(" export default defineDevConfig({");
196
- console.error(" projectPrefix: 'myapp',");
197
- console.error(" services: { ... }");
198
- console.error(" })");
199
- process.exit(1);
200
- }
201
- return createDevEnvironment(config);
202
- } catch (error) {
203
- console.error(`❌ Failed to load config file "${file}":`);
204
- console.error(error);
205
- process.exit(1);
206
- }
207
- }
208
- }
209
- console.error("❌ No config file found.");
210
- console.error("");
211
- console.error(" Create a dev.config.ts file in your project root:");
212
- console.error("");
213
- console.error(" import { defineDevConfig } from 'buncargo'");
214
- console.error("");
215
- console.error(" export default defineDevConfig({");
216
- console.error(" projectPrefix: 'myapp',");
217
- console.error(" services: {");
218
- console.error(' postgres: { port: 5432, healthCheck: "pg_isready" }');
219
- console.error(" }");
220
- console.error(" })");
221
- console.error("");
222
- console.error(" Supported config files:");
223
- for (const file of CONFIG_FILES) {
224
- console.error(` - ${file}`);
168
+ try {
169
+ return await loadDevEnv();
170
+ } catch (error) {
171
+ console.error(`❌ ${error instanceof Error ? error.message : error}`);
172
+ process.exit(1);
225
173
  }
226
- process.exit(1);
227
174
  }
228
175
  async function handleDev(args) {
229
176
  const env = await loadEnv();
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  getFlagValue,
3
3
  hasFlag,
4
4
  runCli
5
- } from "./index-xcx25j3c.js";
5
+ } from "./index-twc5zwja.js";
6
6
  import"./index-8gd8fkwx.js";
7
7
  import"./index-6qd90dkp.js";
8
8
  import"./index-qnx9j3qa.js";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createDevEnvironment
3
- } from "./index-cft124d3.js";
3
+ } from "./index-hb4y6f08.js";
4
4
  import"./index-8gd8fkwx.js";
5
5
  import"./index-6qd90dkp.js";
6
6
  import"./index-tjqw9vtj.js";
7
- import"./index-tn0r1rx7.js";
7
+ import"./index-5hka0tff.js";
8
8
  import"./index-2fr3g85b.js";
9
9
  import"./index-6fm7mvwj.js";
10
10
  import"./index-08wa79cs.js";
@@ -5,7 +5,8 @@ import {
5
5
  } from "./index-2fr3g85b.js";
6
6
 
7
7
  // prisma.ts
8
- import { $ } from "bun";
8
+ import { spawn } from "node:child_process";
9
+ import { join } from "node:path";
9
10
  function createPrismaRunner(env, config) {
10
11
  const {
11
12
  cwd = "packages/prisma",
@@ -69,12 +70,27 @@ Examples:
69
70
  `);
70
71
  await ensureDatabase();
71
72
  const envVars = env.buildEnvVars();
72
- $.env({ ...process.env, ...envVars, [urlEnvVar]: getDatabaseUrl() });
73
- $.cwd(`${env.root}/${cwd}`);
73
+ const workingDir = join(env.root, cwd);
74
+ const fullEnv = {
75
+ ...process.env,
76
+ ...envVars,
77
+ [urlEnvVar]: getDatabaseUrl()
78
+ };
74
79
  console.log(`\uD83D\uDD04 Running: prisma ${args.join(" ")}
75
80
  `);
76
- const result = await $`bunx prisma ${args}`.nothrow();
77
- return result.exitCode;
81
+ return new Promise((resolve) => {
82
+ const proc = spawn("bunx", ["prisma", ...args], {
83
+ cwd: workingDir,
84
+ env: fullEnv,
85
+ stdio: "inherit"
86
+ });
87
+ proc.on("close", (code) => {
88
+ resolve(code ?? 0);
89
+ });
90
+ proc.on("error", () => {
91
+ resolve(1);
92
+ });
93
+ });
78
94
  }
79
95
  return { run, getDatabaseUrl, ensureDatabase };
80
96
  }
@@ -1,5 +1,8 @@
1
1
  // lint.ts
2
- import { $, Glob } from "bun";
2
+ import { execSync } from "node:child_process";
3
+ import { readFileSync, unlinkSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import fg from "fast-glob";
3
6
  var DEFAULT_PATTERNS = ["apps/*", "packages/*", "modules"];
4
7
  var DEFAULT_CONCURRENCY = 1;
5
8
  var CORRUPTED_CACHE_PATTERNS = [
@@ -15,16 +18,31 @@ async function clearTsBuildInfo(workspace, verbose) {
15
18
  if (verbose) {
16
19
  console.log(`\uD83E\uDDF9 Clearing corrupted tsbuildinfo cache for ${workspace}...`);
17
20
  }
18
- await $`find ${workspace} -name '*.tsbuildinfo' -type f -delete`.nothrow().quiet();
19
- await $`find ${workspace} -path '*/.cache/tsbuildinfo.json' -type f -delete`.nothrow().quiet();
21
+ try {
22
+ const tsbuildInfoFiles = await fg(`${workspace}/**/*.tsbuildinfo`, {
23
+ absolute: true
24
+ });
25
+ for (const file of tsbuildInfoFiles) {
26
+ try {
27
+ unlinkSync(file);
28
+ } catch {}
29
+ }
30
+ const cacheFiles = await fg(`${workspace}/**/.cache/tsbuildinfo.json`, {
31
+ absolute: true
32
+ });
33
+ for (const file of cacheFiles) {
34
+ try {
35
+ unlinkSync(file);
36
+ } catch {}
37
+ }
38
+ } catch {}
20
39
  }
21
40
  async function countTypeScriptFiles(pkgPath, root) {
22
- let count = 0;
23
- const tsGlob = new Glob(`${pkgPath}/**/*.{ts,tsx}`);
24
- for await (const _ of tsGlob.scan(root)) {
25
- count++;
26
- }
27
- return count;
41
+ const files = await fg(`${pkgPath}/**/*.{ts,tsx}`, {
42
+ cwd: root,
43
+ ignore: ["**/node_modules/**"]
44
+ });
45
+ return files.length;
28
46
  }
29
47
  function formatErrorOutput(output) {
30
48
  return output.split(`
@@ -38,15 +56,28 @@ async function runSingleTypecheck(workspace, fileCount, root, verbose, isRetry =
38
56
  if (verbose) {
39
57
  console.log(`Running typecheck in ${workspace} (${fileCount} files)${isRetry ? " (retry)" : ""}...`);
40
58
  }
41
- const workspacePath = `${root}/${workspace}`;
42
- const result = await $`cd ${workspacePath} && bun run typecheck`.nothrow().quiet();
59
+ const workspacePath = join(root, workspace);
60
+ let success = false;
61
+ let stdout = "";
62
+ let stderr = "";
63
+ try {
64
+ const output = execSync("bun run typecheck", {
65
+ cwd: workspacePath,
66
+ encoding: "utf-8",
67
+ stdio: ["pipe", "pipe", "pipe"]
68
+ });
69
+ stdout = output ?? "";
70
+ success = true;
71
+ } catch (error) {
72
+ const execError = error;
73
+ stdout = execError.stdout ?? "";
74
+ stderr = execError.stderr ?? "";
75
+ success = false;
76
+ }
43
77
  const duration = Number(((performance.now() - startTime) / 1000).toFixed(2));
44
- const success = result.exitCode === 0;
45
78
  let errorOutput;
46
79
  if (!success) {
47
- const stdout = result.stdout.toString().trim();
48
- const stderr = result.stderr.toString().trim();
49
- const parts = [stdout, stderr].filter(Boolean);
80
+ const parts = [stdout.trim(), stderr.trim()].filter(Boolean);
50
81
  errorOutput = parts.length > 0 ? parts.join(`
51
82
  `) : undefined;
52
83
  if (!isRetry && errorOutput && isCorruptedCacheError(errorOutput)) {
@@ -59,14 +90,17 @@ async function runSingleTypecheck(workspace, fileCount, root, verbose, isRetry =
59
90
  async function discoverWorkspaces(patterns, root) {
60
91
  const workspaces = [];
61
92
  for (const pattern of patterns) {
62
- const glob = new Glob(`${pattern}/package.json`);
63
- for await (const match of glob.scan(root)) {
93
+ const matches = await fg(`${pattern}/package.json`, { cwd: root });
94
+ for (const match of matches) {
64
95
  const pkgPath = match.replace("/package.json", "");
65
- const pkgJson = await Bun.file(`${root}/${match}`).json();
66
- if (pkgJson.scripts?.typecheck) {
67
- const fileCount = await countTypeScriptFiles(pkgPath, root);
68
- workspaces.push({ path: pkgPath, fileCount });
69
- }
96
+ try {
97
+ const pkgJsonContent = readFileSync(join(root, match), "utf-8");
98
+ const pkgJson = JSON.parse(pkgJsonContent);
99
+ if (pkgJson.scripts?.typecheck) {
100
+ const fileCount = await countTypeScriptFiles(pkgPath, root);
101
+ workspaces.push({ path: pkgPath, fileCount });
102
+ }
103
+ } catch {}
70
104
  }
71
105
  }
72
106
  workspaces.sort((a, b) => a.fileCount - b.fileCount);
@@ -0,0 +1,58 @@
1
+ import {
2
+ createDevEnvironment
3
+ } from "./index-hb4y6f08.js";
4
+
5
+ // loader.ts
6
+ import { existsSync } from "node:fs";
7
+ import { dirname, join } from "node:path";
8
+ var CONFIG_FILES = [
9
+ "dev.config.ts",
10
+ "dev.config.js",
11
+ "dev-tools.config.ts",
12
+ "dev-tools.config.js"
13
+ ];
14
+ function findConfigFile(startDir) {
15
+ let currentDir = startDir;
16
+ while (true) {
17
+ for (const file of CONFIG_FILES) {
18
+ const configPath = join(currentDir, file);
19
+ if (existsSync(configPath)) {
20
+ return configPath;
21
+ }
22
+ }
23
+ const parentDir = dirname(currentDir);
24
+ if (parentDir === currentDir) {
25
+ return null;
26
+ }
27
+ currentDir = parentDir;
28
+ }
29
+ }
30
+ var cachedEnv = null;
31
+ async function loadDevEnv(options) {
32
+ if (cachedEnv && !options?.reload) {
33
+ return cachedEnv;
34
+ }
35
+ const cwd = options?.cwd ?? process.cwd();
36
+ const configPath = findConfigFile(cwd);
37
+ if (configPath) {
38
+ const mod = await import(configPath);
39
+ const config = mod.default;
40
+ if (!config?.projectPrefix || !config?.services) {
41
+ throw new Error(`Invalid config in "${configPath}". Use defineDevConfig() and export as default.`);
42
+ }
43
+ cachedEnv = createDevEnvironment(config);
44
+ return cachedEnv;
45
+ }
46
+ throw new Error(`No config file found. Create dev.config.ts with: export default defineDevConfig({ ... })`);
47
+ }
48
+ function getDevEnv() {
49
+ if (!cachedEnv) {
50
+ throw new Error("Dev environment not loaded. Call loadDevEnv() first.");
51
+ }
52
+ return cachedEnv;
53
+ }
54
+ function clearDevEnvCache() {
55
+ cachedEnv = null;
56
+ }
57
+
58
+ export { CONFIG_FILES, findConfigFile, loadDevEnv, getDevEnv, clearDevEnvCache };
@@ -15,7 +15,7 @@ import {
15
15
  } from "./index-tjqw9vtj.js";
16
16
  import {
17
17
  createPrismaRunner
18
- } from "./index-tn0r1rx7.js";
18
+ } from "./index-5hka0tff.js";
19
19
  import {
20
20
  areContainersRunning,
21
21
  startContainers,
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  createDevEnvironment
3
- } from "./index-cft124d3.js";
3
+ } from "./index-hb4y6f08.js";
4
4
 
5
5
  // loader.ts
6
+ import { existsSync } from "node:fs";
6
7
  var CONFIG_FILES = [
7
8
  "dev.config.ts",
8
9
  "dev.config.js",
@@ -17,7 +18,7 @@ async function loadDevEnv(options) {
17
18
  const cwd = options?.cwd ?? process.cwd();
18
19
  for (const file of CONFIG_FILES) {
19
20
  const path = `${cwd}/${file}`;
20
- const exists = await Bun.file(path).exists();
21
+ const exists = existsSync(path);
21
22
  if (exists) {
22
23
  const mod = await import(path);
23
24
  const config = mod.default;
package/dist/index.js CHANGED
@@ -2,18 +2,18 @@ import {
2
2
  getFlagValue,
3
3
  hasFlag,
4
4
  runCli
5
- } from "./index-xcx25j3c.js";
5
+ } from "./index-twc5zwja.js";
6
6
  import {
7
7
  runWorkspaceTypecheck
8
- } from "./index-j482am5b.js";
8
+ } from "./index-d9efy0n4.js";
9
9
  import {
10
10
  clearDevEnvCache,
11
11
  getDevEnv,
12
12
  loadDevEnv
13
- } from "./index-etqw2czs.js";
13
+ } from "./index-habjkmf5.js";
14
14
  import {
15
15
  createDevEnvironment
16
- } from "./index-cft124d3.js";
16
+ } from "./index-hb4y6f08.js";
17
17
  import {
18
18
  getHeartbeatFile,
19
19
  getWatchdogPidFile,
@@ -32,7 +32,7 @@ import {
32
32
  mergeConfigs,
33
33
  validateConfig
34
34
  } from "./index-tjqw9vtj.js";
35
- import"./index-tn0r1rx7.js";
35
+ import"./index-5hka0tff.js";
36
36
  import {
37
37
  MAX_ATTEMPTS,
38
38
  POLL_INTERVAL,
package/dist/lint.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runWorkspaceTypecheck
3
- } from "./index-j482am5b.js";
3
+ } from "./index-d9efy0n4.js";
4
4
  import"./index-qnx9j3qa.js";
5
5
  export {
6
6
  runWorkspaceTypecheck
package/dist/loader.d.ts CHANGED
@@ -1,4 +1,10 @@
1
1
  import type { AppConfig, DevEnvironment, ServiceConfig } from "./types";
2
+ export declare const CONFIG_FILES: string[];
3
+ /**
4
+ * Find a config file by traversing up from the starting directory.
5
+ * Returns the full path to the config file, or null if not found.
6
+ */
7
+ export declare function findConfigFile(startDir: string): string | null;
2
8
  /**
3
9
  * Load the dev environment from the config file.
4
10
  * Caches the result for subsequent calls.
package/dist/loader.js CHANGED
@@ -1,13 +1,15 @@
1
1
  import {
2
+ CONFIG_FILES,
2
3
  clearDevEnvCache,
4
+ findConfigFile,
3
5
  getDevEnv,
4
6
  loadDevEnv
5
- } from "./index-etqw2czs.js";
6
- import"./index-cft124d3.js";
7
+ } from "./index-habjkmf5.js";
8
+ import"./index-hb4y6f08.js";
7
9
  import"./index-8gd8fkwx.js";
8
10
  import"./index-6qd90dkp.js";
9
11
  import"./index-tjqw9vtj.js";
10
- import"./index-tn0r1rx7.js";
12
+ import"./index-5hka0tff.js";
11
13
  import"./index-2fr3g85b.js";
12
14
  import"./index-6fm7mvwj.js";
13
15
  import"./index-08wa79cs.js";
@@ -15,5 +17,7 @@ import"./index-qnx9j3qa.js";
15
17
  export {
16
18
  loadDevEnv,
17
19
  getDevEnv,
18
- clearDevEnvCache
20
+ findConfigFile,
21
+ clearDevEnvCache,
22
+ CONFIG_FILES
19
23
  };
package/dist/prisma.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createPrismaRunner
3
- } from "./index-tn0r1rx7.js";
3
+ } from "./index-5hka0tff.js";
4
4
  import"./index-2fr3g85b.js";
5
5
  import"./index-6fm7mvwj.js";
6
6
  import"./index-08wa79cs.js";
package/lint.ts CHANGED
@@ -1,4 +1,7 @@
1
- import { $, Glob } from "bun";
1
+ import { execSync } from "node:child_process";
2
+ import { readFileSync, unlinkSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import fg from "fast-glob";
2
5
 
3
6
  // ═══════════════════════════════════════════════════════════════════════════
4
7
  // Types
@@ -76,24 +79,42 @@ async function clearTsBuildInfo(
76
79
  console.log(`🧹 Clearing corrupted tsbuildinfo cache for ${workspace}...`);
77
80
  }
78
81
  // Clear both old tsbuildinfo and new tsgo cache
79
- await $`find ${workspace} -name '*.tsbuildinfo' -type f -delete`
80
- .nothrow()
81
- .quiet();
82
- await $`find ${workspace} -path '*/.cache/tsbuildinfo.json' -type f -delete`
83
- .nothrow()
84
- .quiet();
82
+ try {
83
+ const tsbuildInfoFiles = await fg(`${workspace}/**/*.tsbuildinfo`, {
84
+ absolute: true,
85
+ });
86
+ for (const file of tsbuildInfoFiles) {
87
+ try {
88
+ unlinkSync(file);
89
+ } catch {
90
+ // Ignore errors
91
+ }
92
+ }
93
+
94
+ const cacheFiles = await fg(`${workspace}/**/.cache/tsbuildinfo.json`, {
95
+ absolute: true,
96
+ });
97
+ for (const file of cacheFiles) {
98
+ try {
99
+ unlinkSync(file);
100
+ } catch {
101
+ // Ignore errors
102
+ }
103
+ }
104
+ } catch {
105
+ // Ignore errors
106
+ }
85
107
  }
86
108
 
87
109
  async function countTypeScriptFiles(
88
110
  pkgPath: string,
89
111
  root: string,
90
112
  ): Promise<number> {
91
- let count = 0;
92
- const tsGlob = new Glob(`${pkgPath}/**/*.{ts,tsx}`);
93
- for await (const _ of tsGlob.scan(root)) {
94
- count++;
95
- }
96
- return count;
113
+ const files = await fg(`${pkgPath}/**/*.{ts,tsx}`, {
114
+ cwd: root,
115
+ ignore: ["**/node_modules/**"],
116
+ });
117
+ return files.length;
97
118
  }
98
119
 
99
120
  function formatErrorOutput(output: string): string {
@@ -122,18 +143,36 @@ async function runSingleTypecheck(
122
143
  );
123
144
  }
124
145
 
125
- const workspacePath = `${root}/${workspace}`;
126
- const result = await $`cd ${workspacePath} && bun run typecheck`
127
- .nothrow()
128
- .quiet();
146
+ const workspacePath = join(root, workspace);
147
+
148
+ let success = false;
149
+ let stdout = "";
150
+ let stderr = "";
151
+
152
+ try {
153
+ const output = execSync("bun run typecheck", {
154
+ cwd: workspacePath,
155
+ encoding: "utf-8",
156
+ stdio: ["pipe", "pipe", "pipe"],
157
+ });
158
+ stdout = output ?? "";
159
+ success = true;
160
+ } catch (error) {
161
+ const execError = error as {
162
+ status?: number;
163
+ stdout?: string;
164
+ stderr?: string;
165
+ };
166
+ stdout = execError.stdout ?? "";
167
+ stderr = execError.stderr ?? "";
168
+ success = false;
169
+ }
170
+
129
171
  const duration = Number(((performance.now() - startTime) / 1000).toFixed(2));
130
- const success = result.exitCode === 0;
131
172
 
132
173
  let errorOutput: string | undefined;
133
174
  if (!success) {
134
- const stdout = result.stdout.toString().trim();
135
- const stderr = result.stderr.toString().trim();
136
- const parts = [stdout, stderr].filter(Boolean);
175
+ const parts = [stdout.trim(), stderr.trim()].filter(Boolean);
137
176
  errorOutput = parts.length > 0 ? parts.join("\n") : undefined;
138
177
 
139
178
  // Check for corrupted cache and retry once
@@ -153,13 +192,18 @@ async function discoverWorkspaces(
153
192
  const workspaces: Workspace[] = [];
154
193
 
155
194
  for (const pattern of patterns) {
156
- const glob = new Glob(`${pattern}/package.json`);
157
- for await (const match of glob.scan(root)) {
195
+ const matches = await fg(`${pattern}/package.json`, { cwd: root });
196
+ for (const match of matches) {
158
197
  const pkgPath = match.replace("/package.json", "");
159
- const pkgJson = await Bun.file(`${root}/${match}`).json();
160
- if (pkgJson.scripts?.typecheck) {
161
- const fileCount = await countTypeScriptFiles(pkgPath, root);
162
- workspaces.push({ path: pkgPath, fileCount });
198
+ try {
199
+ const pkgJsonContent = readFileSync(join(root, match), "utf-8");
200
+ const pkgJson = JSON.parse(pkgJsonContent);
201
+ if (pkgJson.scripts?.typecheck) {
202
+ const fileCount = await countTypeScriptFiles(pkgPath, root);
203
+ workspaces.push({ path: pkgPath, fileCount });
204
+ }
205
+ } catch {
206
+ // Skip invalid package.json files
163
207
  }
164
208
  }
165
209
  }
package/loader.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
1
3
  import { createDevEnvironment } from "./environment";
2
4
  import type { AppConfig, DevEnvironment, ServiceConfig } from "./types";
3
5
 
@@ -5,13 +7,41 @@ import type { AppConfig, DevEnvironment, ServiceConfig } from "./types";
5
7
  // Config Loader
6
8
  // ═══════════════════════════════════════════════════════════════════════════
7
9
 
8
- const CONFIG_FILES = [
10
+ export const CONFIG_FILES = [
9
11
  "dev.config.ts",
10
12
  "dev.config.js",
11
13
  "dev-tools.config.ts",
12
14
  "dev-tools.config.js",
13
15
  ];
14
16
 
17
+ /**
18
+ * Find a config file by traversing up from the starting directory.
19
+ * Returns the full path to the config file, or null if not found.
20
+ */
21
+ export function findConfigFile(startDir: string): string | null {
22
+ let currentDir = startDir;
23
+
24
+ while (true) {
25
+ // Check for any config file in the current directory
26
+ for (const file of CONFIG_FILES) {
27
+ const configPath = join(currentDir, file);
28
+ if (existsSync(configPath)) {
29
+ return configPath;
30
+ }
31
+ }
32
+
33
+ // Move to parent directory
34
+ const parentDir = dirname(currentDir);
35
+
36
+ // Stop if we've reached the root (parent equals current)
37
+ if (parentDir === currentDir) {
38
+ return null;
39
+ }
40
+
41
+ currentDir = parentDir;
42
+ }
43
+ }
44
+
15
45
  let cachedEnv: DevEnvironment<
16
46
  Record<string, ServiceConfig>,
17
47
  Record<string, AppConfig>
@@ -43,24 +73,20 @@ export async function loadDevEnv(options?: {
43
73
  }
44
74
 
45
75
  const cwd = options?.cwd ?? process.cwd();
76
+ const configPath = findConfigFile(cwd);
46
77
 
47
- for (const file of CONFIG_FILES) {
48
- const path = `${cwd}/${file}`;
49
- const exists = await Bun.file(path).exists();
50
-
51
- if (exists) {
52
- const mod = await import(path);
53
- const config = mod.default;
78
+ if (configPath) {
79
+ const mod = await import(configPath);
80
+ const config = mod.default;
54
81
 
55
- if (!config?.projectPrefix || !config?.services) {
56
- throw new Error(
57
- `Invalid config in "${file}". Use defineDevConfig() and export as default.`,
58
- );
59
- }
60
-
61
- cachedEnv = createDevEnvironment(config);
62
- return cachedEnv;
82
+ if (!config?.projectPrefix || !config?.services) {
83
+ throw new Error(
84
+ `Invalid config in "${configPath}". Use defineDevConfig() and export as default.`,
85
+ );
63
86
  }
87
+
88
+ cachedEnv = createDevEnvironment(config);
89
+ return cachedEnv;
64
90
  }
65
91
 
66
92
  throw new Error(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "buncargo",
3
- "version": "1.0.8",
3
+ "version": "1.0.11",
4
4
  "description": "A Bun-powered development environment CLI for managing Docker Compose services, dev servers, and environment variables",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
@@ -136,6 +136,7 @@
136
136
  "typescript": "^5.7.0"
137
137
  },
138
138
  "dependencies": {
139
+ "fast-glob": "^3.3.3",
139
140
  "picocolors": "^1.1.1"
140
141
  }
141
142
  }
package/prisma.ts CHANGED
@@ -22,7 +22,8 @@
22
22
  * @internal This module is used internally by createDevEnvironment.
23
23
  */
24
24
 
25
- import { $ } from "bun";
25
+ import { spawn } from "node:child_process";
26
+ import { join } from "node:path";
26
27
  import {
27
28
  isContainerRunning,
28
29
  startService,
@@ -124,14 +125,30 @@ Examples:
124
125
  await ensureDatabase();
125
126
 
126
127
  const envVars = env.buildEnvVars();
127
-
128
- $.env({ ...process.env, ...envVars, [urlEnvVar]: getDatabaseUrl() });
129
- $.cwd(`${env.root}/${cwd}`);
128
+ const workingDir = join(env.root, cwd);
129
+ const fullEnv = {
130
+ ...process.env,
131
+ ...envVars,
132
+ [urlEnvVar]: getDatabaseUrl(),
133
+ };
130
134
 
131
135
  console.log(`🔄 Running: prisma ${args.join(" ")}\n`);
132
136
 
133
- const result = await $`bunx prisma ${args}`.nothrow();
134
- return result.exitCode;
137
+ return new Promise((resolve) => {
138
+ const proc = spawn("bunx", ["prisma", ...args], {
139
+ cwd: workingDir,
140
+ env: fullEnv,
141
+ stdio: "inherit",
142
+ });
143
+
144
+ proc.on("close", (code) => {
145
+ resolve(code ?? 0);
146
+ });
147
+
148
+ proc.on("error", () => {
149
+ resolve(1);
150
+ });
151
+ });
135
152
  }
136
153
 
137
154
  return { run, getDatabaseUrl, ensureDatabase };
File without changes