sunnah 1.1.2 → 1.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.

Potentially problematic release.


This version of sunnah might be problematic. Click here for more details.

Files changed (2) hide show
  1. package/bin/index.js +43 -11
  2. package/package.json +1 -1
package/bin/index.js CHANGED
@@ -12,6 +12,10 @@ const pkg = JSON.parse(
12
12
  fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8"),
13
13
  );
14
14
 
15
+ // ── Windows compatibility ─────────────────────────────────────────────────────
16
+ const isWin = process.platform === "win32";
17
+ const NPM = isWin ? "npm.cmd" : "npm";
18
+
15
19
  // ── Colors ────────────────────────────────────────────────────────────────────
16
20
  const c = {
17
21
  reset: "\x1b[0m",
@@ -117,7 +121,6 @@ function animateInstall(pkgName) {
117
121
  let stageIdx = 0;
118
122
  let npmDone = false;
119
123
 
120
- // Print initial bar on its own line
121
124
  process.stdout.write(drawBar(stages[0].label, 0) + "\n");
122
125
 
123
126
  const tick = () => {
@@ -128,14 +131,12 @@ function animateInstall(pkgName) {
128
131
  const step = (stage.end - prevEnd) / 22;
129
132
  percent = Math.min(percent + step, stage.end);
130
133
 
131
- // Overwrite the bar line
132
134
  process.stdout.write("\r\x1b[K");
133
135
  process.stdout.write(drawBar(stage.label, percent));
134
136
 
135
137
  if (percent >= stage.end) {
136
138
  stageIdx++;
137
139
  if (stageIdx >= stages.length) {
138
- // All visual stages done — wait for npm
139
140
  const poll = setInterval(() => {
140
141
  if (npmDone) {
141
142
  clearInterval(poll);
@@ -154,26 +155,48 @@ function animateInstall(pkgName) {
154
155
 
155
156
  setTimeout(tick, stages[0].ms);
156
157
 
157
- // Actually run npm install -g
158
- const proc = spawn("npm", ["install", "-g", pkgName], {
158
+ // spawn npm.cmd on Windows, npm on Unix
159
+ const proc = spawn(NPM, ["install", "-g", pkgName], {
159
160
  stdio: ["ignore", "pipe", "pipe"],
161
+ shell: isWin,
162
+ });
163
+
164
+ proc.on("error", () => {
165
+ // resolve anyway so the UI doesn't hang on spawn failure
166
+ npmDone = true;
160
167
  });
168
+
161
169
  proc.on("close", () => {
162
170
  npmDone = true;
163
171
  });
164
172
  });
165
173
  }
166
174
 
167
- // ── Check if a package is already installed globally ─────────────────────────
168
- function isInstalled(name) {
175
+ // ── Check installed status ONCE at startup, cache forever ───────────────────
176
+ // Calling npm on every render was freezing the terminal. We run one
177
+ // 'npm list -g' at startup, parse the output, and never shell out again.
178
+ function buildInstalledCache() {
179
+ const cache = new Map();
180
+ let out = "";
169
181
  try {
170
- execSync(`npm list -g ${name} --depth=0 2>/dev/null`, { stdio: "ignore" });
171
- return true;
172
- } catch {
173
- return false;
182
+ out = execSync(`${NPM} list -g --depth=0`, {
183
+ encoding: "utf8",
184
+ shell: isWin,
185
+ timeout: 8000,
186
+ stdio: ["ignore", "pipe", "ignore"],
187
+ });
188
+ } catch (e) {
189
+ out = e.stdout || "";
174
190
  }
191
+ for (const p of PACKAGES) {
192
+ cache.set(p.name, out.includes(p.name));
193
+ }
194
+ return cache;
175
195
  }
176
196
 
197
+ let installedCache = new Map();
198
+ const isInstalled = (name) => installedCache.get(name) ?? false;
199
+
177
200
  // ── Render the interactive list ───────────────────────────────────────────────
178
201
  const DIV_W = () => Math.min(W() - 2, 70);
179
202
 
@@ -315,6 +338,7 @@ async function main() {
315
338
 
316
339
  // --list
317
340
  if (flags.some((f) => f === "--list" || f === "-l")) {
341
+ installedCache = buildInstalledCache();
318
342
  const div = gray("─".repeat(60));
319
343
  console.log("");
320
344
  console.log(div);
@@ -342,6 +366,13 @@ async function main() {
342
366
  process.exit(1);
343
367
  }
344
368
 
369
+ // Build installed cache once — never call npm during rendering
370
+ process.stdout.write(
371
+ "\n " + "\x1b[90m" + "Checking installed packages…" + "\x1b[0m",
372
+ );
373
+ installedCache = buildInstalledCache();
374
+ process.stdout.write("\r\x1b[K");
375
+
345
376
  readline.emitKeypressEvents(process.stdin);
346
377
  process.stdin.setRawMode(true);
347
378
  hideCursor();
@@ -443,6 +474,7 @@ async function main() {
443
474
  console.log("");
444
475
 
445
476
  await animateInstall(p.name);
477
+ installedCache.set(p.name, true); // update cache
446
478
 
447
479
  console.log(
448
480
  ` ${green("✓")} ${bold(green(p.label))} installed successfully`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sunnah",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Interactive CLI installer for Sunnah hadith npm packages",
5
5
  "bin": {
6
6
  "sunnah": "./bin/index.js"