drizzle-auto 1.1.25 → 1.1.26

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/index.js +124 -109
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * 🍵 Drizzle-Auto v2.1 - Fail-Fast Edition
5
- * 🔹 Strictly catches WebSocket/Neon ENOTFOUND errors
6
- * 🔹 Stops immediately on any failure
7
- * 🔹 Debounced watcher to prevent race conditions
4
+ * 🍵 Drizzle-Auto v2.3 - Flexible Config + Fail-Fast
5
+ * 🔹 Detects any drizzle.config.{js,ts,mjs,cjs,mts} automatically
6
+ * 🔹 Stops immediately if config or schema missing
7
+ * 🔹 Beginner-friendly logs
8
8
  */
9
9
 
10
10
  const { spawn } = require("child_process");
11
11
  const path = require("path");
12
12
  const fs = require("fs");
13
13
 
14
- // Optional TS support for config parsing
15
- try { require("ts-node").register({ transpileOnly: true }); } catch (e) {}
14
+ // TS support
15
+ try { require("ts-node").register({ transpileOnly: true }); } catch(e){}
16
16
 
17
- // ---------------------- COLORS & UI ----------------------
17
+ // ---------------- COLORS ----------------
18
18
  const T = {
19
19
  reset: "\x1b[0m",
20
20
  bold: "\x1b[1m",
@@ -25,152 +25,167 @@ const T = {
25
25
  gray: "\x1b[38;5;244m",
26
26
  };
27
27
 
28
- const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
29
- let spinIdx = 0, spinInterval;
28
+ let spinInterval;
29
+ const frames = ["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];
30
+ let spinIdx = 0;
30
31
 
31
- function startLoading(msg) {
32
+ function startLoading(msg){
32
33
  stopLoading();
33
- spinInterval = setInterval(() => {
34
+ spinInterval = setInterval(()=>{
34
35
  process.stdout.write(`\r${T.cyan}${frames[spinIdx++ % frames.length]}${T.reset} ${T.bold}${msg}${T.reset} `);
35
- }, 80);
36
+ },80);
36
37
  }
37
38
 
38
- function stopLoading() {
39
- if (spinInterval) {
39
+ function stopLoading(){
40
+ if(spinInterval){
40
41
  clearInterval(spinInterval);
41
- spinInterval = null;
42
+ spinInterval=null;
42
43
  process.stdout.write("\r\x1b[K");
43
44
  }
44
45
  }
45
46
 
46
- // ---------------------- UTILS ----------------------
47
- const PKG_PATH = path.join(process.cwd(), "package.json");
48
- function injectScript() {
49
- if (!fs.existsSync(PKG_PATH)) return;
50
- try {
51
- const pkg = JSON.parse(fs.readFileSync(PKG_PATH, "utf8"));
47
+ // ---------------- ADD "tea" SCRIPT ----------------
48
+ const PKG = path.join(process.cwd(),"package.json");
49
+ function addTeaScript(){
50
+ if(!fs.existsSync(PKG)) return;
51
+ try{
52
+ const pkg = JSON.parse(fs.readFileSync(PKG,"utf8"));
52
53
  pkg.scripts ||= {};
53
- if (!pkg.scripts.tea) {
54
+ if(!pkg.scripts.tea){
54
55
  pkg.scripts.tea = "drizzle-auto";
55
- fs.writeFileSync(PKG_PATH, JSON.stringify(pkg, null, 2));
56
- console.log(`${T.lime}🍵 Script added → npm run tea${T.reset}`);
56
+ fs.writeFileSync(PKG, JSON.stringify(pkg,null,2));
57
+ console.log(`${T.lime}🍵 Added script → npm run tea${T.reset}`);
57
58
  }
58
- } catch (e) {}
59
+ } catch(e){}
59
60
  }
60
61
 
61
- // ---------------------- COMMAND RUNNER (STRICT) ----------------------
62
- function runCommand(cmd, label) {
63
- return new Promise((resolve, reject) => {
62
+ // ---------------- FIND ANY DRIZZLE CONFIG ----------------
63
+ function findConfig(){
64
+ const allowedExts = ["js","ts","mjs","cjs","mts"];
65
+ const rootFiles = fs.readdirSync(process.cwd());
66
+ for(const file of rootFiles){
67
+ const match = file.match(/^drizzle\.config\.(.+)$/i);
68
+ if(match && allowedExts.includes(match[1])) return path.join(process.cwd(),file);
69
+ }
70
+ return null;
71
+ }
72
+
73
+ // ---------------- CHECK SCHEMA ----------------
74
+ function checkSchema(configPath){
75
+ try{
76
+ const cfg = require(configPath);
77
+ const schemaPath = cfg.default?.schema || cfg.schema;
78
+ if(!schemaPath){
79
+ console.log(`${T.yellow}⚠ Warning: schema not defined in ${path.basename(configPath)}${T.reset}`);
80
+ return true; // optional
81
+ }
82
+ const fullSchema = path.resolve(path.dirname(configPath), schemaPath);
83
+ if(!fs.existsSync(fullSchema)){
84
+ console.log(`${T.red}❌ ERROR: Schema file missing → ${fullSchema}${T.reset}`);
85
+ process.exit(1);
86
+ }
87
+ return true;
88
+ } catch(e){
89
+ console.log(`${T.red}❌ ERROR: Cannot read config → ${e.message}${T.reset}`);
90
+ process.exit(1);
91
+ }
92
+ }
93
+
94
+ // ---------------- RUN SHELL COMMAND ----------------
95
+ function runCommand(cmd,label){
96
+ return new Promise((resolve,reject)=>{
64
97
  startLoading(label);
65
-
66
- // We pipe stderr to catch hidden WebSocket/Neon errors that don't trigger exit codes
67
- const child = spawn(cmd, { shell: true, stdio: ["inherit", "inherit", "pipe"] });
68
- let errorOutput = "";
69
-
70
- child.stderr.on("data", (data) => {
71
- const str = data.toString();
72
- errorOutput += str;
73
- process.stderr.write(str); // Still show the error to the user
98
+ const child = spawn(cmd,{shell:true,stdio:["inherit","inherit","pipe"]});
99
+ let stderrData = "";
100
+ child.stderr.on("data", d=>{
101
+ stderrData += d.toString();
102
+ process.stderr.write(d);
74
103
  });
75
-
76
- child.on("close", (code) => {
104
+ child.on("close", code=>{
77
105
  stopLoading();
78
-
79
- // Strict failure check:
80
- // 1. Non-zero exit code
81
- // 2. Presence of "ErrorEvent" or "ENOTFOUND" in stderr (Neon driver specific)
82
- const hasDriverError = /ErrorEvent|ENOTFOUND|failed to connect/i.test(errorOutput);
83
-
84
- if (code !== 0 || hasDriverError) {
85
- reject(new Error(`${label} failed execution.`));
86
- } else {
87
- resolve();
88
- }
106
+ const hasError = code!==0 || /ErrorEvent|ENOTFOUND|failed to connect/i.test(stderrData);
107
+ if(hasError) reject(new Error(`${label} failed!`));
108
+ else resolve();
89
109
  });
90
-
91
- child.on("error", (err) => {
110
+ child.on("error", err=>{
92
111
  stopLoading();
93
112
  reject(err);
94
113
  });
95
114
  });
96
115
  }
97
116
 
98
- // ---------------------- ENGINE ----------------------
99
- let isRunning = false;
100
- let isBroken = false;
117
+ // ---------------- ENGINE ----------------
118
+ let isRunning=false;
119
+ async function triggerEngine(){
120
+ if(isRunning) return;
121
+ isRunning=true;
122
+
123
+ const configPath = findConfig();
124
+ if(!configPath){
125
+ console.log(`${T.red}❌ ERROR: No drizzle config found!${T.reset}`);
126
+ console.log(`${T.yellow}Expected one of: drizzle.config.js/ts/mjs/cjs/mts${T.reset}`);
127
+ process.exit(1);
128
+ }
101
129
 
102
- async function triggerEngine() {
103
- if (isRunning) return;
104
- isRunning = true;
130
+ checkSchema(configPath);
105
131
 
106
- try {
107
- const steps = [
108
- { name: "Integrity Check", cmd: "npx drizzle-kit check" },
109
- { name: "Generate Migration", cmd: "npx drizzle-kit generate" },
110
- { name: "Push Database", cmd: "npx drizzle-kit push" }
111
- ];
132
+ const steps = [
133
+ {name:"Integrity Check", cmd:"npx drizzle-kit check"},
134
+ {name:"Generate Migration", cmd:"npx drizzle-kit generate"},
135
+ {name:"Push Database", cmd:"npx drizzle-kit push"}
136
+ ];
112
137
 
113
- for (const step of steps) {
114
- await runCommand(step.cmd, step.name);
115
- }
116
-
117
- console.log(`\n${T.lime}${T.bold}🎉 SUCCESS: Database synced.${T.reset}\n`);
118
- isBroken = false;
119
- } catch (err) {
120
- isBroken = true;
121
- console.log(`\n${T.red}${T.bold}🚨 CRITICAL STOP:${T.reset} ${err.message}`);
122
- console.log(`${T.yellow}Execution paused. Fix the error and save any file to retry.${T.reset}\n`);
123
-
124
- if (process.argv.includes('--postinstall')) {
125
- process.exit(1);
138
+ try{
139
+ for(const step of steps){
140
+ await runCommand(step.cmd,step.name);
126
141
  }
127
- } finally {
128
- isRunning = false;
142
+ console.log(`\n${T.lime}🎉 SUCCESS: All steps finished.${T.reset}\n`);
143
+ } catch(e){
144
+ console.log(`\n${T.red}🚨 CRITICAL STOP: ${e.message}${T.reset}`);
145
+ process.exit(1);
146
+ } finally{
147
+ isRunning=false;
129
148
  }
130
149
  }
131
150
 
132
- // ---------------------- MAIN / WATCHER ----------------------
133
- injectScript();
151
+ // ---------------- MAIN ----------------
152
+ addTeaScript();
134
153
 
135
- const skipWatcher = process.argv.includes('--postinstall');
154
+ const skipWatcher = process.argv.includes("--postinstall");
136
155
 
137
- if (skipWatcher) {
156
+ if(skipWatcher){
138
157
  triggerEngine();
139
- } else {
158
+ }else{
140
159
  triggerEngine();
141
160
 
142
- try {
161
+ try{
143
162
  const chokidar = require("chokidar");
144
- let debounceTimer;
145
-
146
- const watcher = chokidar.watch(".", {
147
- ignored: [/node_modules/, /\.git/, /\.next/, /dist/, /out/],
148
- ignoreInitial: true,
149
- usePolling: true,
150
- interval: 300
151
- });
152
-
153
- watcher.on("all", (event, path) => {
154
- clearTimeout(debounceTimer);
155
- debounceTimer = setTimeout(() => {
156
- if (isBroken) console.log(`${T.cyan}🔄 Change detected in ${path}. Retrying...${T.reset}`);
163
+ let timer;
164
+ chokidar.watch(".",{
165
+ ignored:[/node_modules/,/\.git/,/\.next/,/dist/,/out/],
166
+ ignoreInitial:true,
167
+ usePolling:true,
168
+ interval:300
169
+ }).on("all",(e,f)=>{
170
+ clearTimeout(timer);
171
+ timer=setTimeout(()=>{
172
+ console.log(`${T.gray}🔄 Change detected, re-running...${T.reset}`);
157
173
  triggerEngine();
158
- }, 500);
174
+ },500);
159
175
  });
160
-
161
- console.log(`${T.gray}👀 Watching for schema changes...${T.reset}`);
162
- } catch (e) {
163
- console.log(`${T.yellow}⚠ Chokidar not found. Watch mode disabled.${T.reset}`);
176
+ console.log(`${T.gray}👀 Watching for changes in config/schema...${T.reset}`);
177
+ } catch(e){
178
+ console.log(`${T.yellow} Chokidar not installed. Watch mode disabled.${T.reset}`);
164
179
  }
165
180
  }
166
181
 
167
- // ---------------------- GLOBAL ERROR CATCH ----------------------
168
- process.on("uncaughtException", (err) => {
169
- console.log(`\n${T.red}${T.bold}🛡️ UNCAUGHT ERROR:${T.reset} ${err.message}`);
182
+ // ---------------- GLOBAL ERROR CATCH ----------------
183
+ process.on("uncaughtException",err=>{
184
+ console.log(`${T.red}🛡️ UNCAUGHT ERROR: ${err.message}${T.reset}`);
170
185
  process.exit(1);
171
186
  });
172
187
 
173
- process.on("SIGINT", () => {
174
- console.log(`\n${T.yellow}Shutting down Drizzle-Auto...${T.reset}`);
188
+ process.on("SIGINT",()=>{
189
+ console.log(`${T.yellow}Shutting down Drizzle-Auto...${T.reset}`);
175
190
  process.exit(0);
176
- });
191
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drizzle-auto",
3
- "version": "1.1.25",
3
+ "version": "1.1.26",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "pavel ahmmed hridoy",