zelari-code 1.0.3 → 1.1.1

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.
@@ -36,6 +36,7 @@ import {
36
36
  readFileSync,
37
37
  readlinkSync,
38
38
  statSync,
39
+ writeFileSync,
39
40
  } from 'node:fs';
40
41
  import { fileURLToPath } from 'node:url';
41
42
  import path from 'node:path';
@@ -43,6 +44,125 @@ import path from 'node:path';
43
44
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
44
45
  const pkgRoot = path.resolve(__dirname, '..');
45
46
 
47
+ /**
48
+ * Auto-repair a MISSING Windows bin shim.
49
+ *
50
+ * This is the single most common "zelari-code: command not found on some
51
+ * Windows PCs" failure: npm correctly unpacks the package into
52
+ * `<prefix>\node_modules\zelari-code\` but never (re)creates the three bin
53
+ * shims `<prefix>\zelari-code[.cmd|.ps1]`, so nothing on PATH resolves the
54
+ * command. Repairing this is safe — unlike overwriting a shim that points
55
+ * elsewhere (which could shadow another tool, hence why we never touch an
56
+ * existing shim), writing a shim under OUR own bin name, pointing at OUR
57
+ * own installed package, only fills a gap npm left behind.
58
+ *
59
+ * We write the exact same three files npm's own `cmd-shim` produces (cmd,
60
+ * PowerShell, and a POSIX sh wrapper for Git Bash/MSYS), each resolving to
61
+ * `%~dp0\node_modules\zelari-code\bin\zelari-code.js` relative to the shim
62
+ * dir — which is the global prefix, exactly where the package was unpacked.
63
+ *
64
+ * Opt out with `ZELARI_NO_SHIM_REPAIR=1`. Never throws; returns true only
65
+ * when at least the `.cmd` shim was written.
66
+ *
67
+ * @param {string} prefix npm global prefix (dir that holds the shims)
68
+ * @param {string} pkgName installed package name
69
+ * @returns {boolean} true if the .cmd shim was created
70
+ */
71
+ function repairWindowsShim(prefix, pkgName) {
72
+ if (process.env.ZELARI_NO_SHIM_REPAIR === '1') return false;
73
+ const rel = `node_modules\\${pkgName}\\bin\\zelari-code.js`;
74
+ const relPosix = `node_modules/${pkgName}/bin/zelari-code.js`;
75
+
76
+ const cmd = [
77
+ '@ECHO off',
78
+ 'GOTO start',
79
+ ':find_dp0',
80
+ 'SET dp0=%~dp0',
81
+ 'EXIT /b',
82
+ ':start',
83
+ 'SETLOCAL',
84
+ 'CALL :find_dp0',
85
+ '',
86
+ 'IF EXIST "%dp0%\\node.exe" (',
87
+ ' SET "_prog=%dp0%\\node.exe"',
88
+ ') ELSE (',
89
+ ' SET "_prog=node"',
90
+ ' SET PATHEXT=%PATHEXT:;.JS;=;%',
91
+ ')',
92
+ '',
93
+ `endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\${rel}" %*`,
94
+ '',
95
+ ].join('\r\n');
96
+
97
+ const ps1 = [
98
+ '#!/usr/bin/env pwsh',
99
+ '$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent',
100
+ '',
101
+ '$exe=""',
102
+ 'if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {',
103
+ ' $exe=".exe"',
104
+ '}',
105
+ '$ret=0',
106
+ 'if (Test-Path "$basedir/node$exe") {',
107
+ ' if ($MyInvocation.ExpectingInput) {',
108
+ ` $input | & "$basedir/node$exe" "$basedir/${relPosix}" $args`,
109
+ ' } else {',
110
+ ` & "$basedir/node$exe" "$basedir/${relPosix}" $args`,
111
+ ' }',
112
+ ' $ret=$LASTEXITCODE',
113
+ '} else {',
114
+ ' if ($MyInvocation.ExpectingInput) {',
115
+ ` $input | & "node$exe" "$basedir/${relPosix}" $args`,
116
+ ' } else {',
117
+ ` & "node$exe" "$basedir/${relPosix}" $args`,
118
+ ' }',
119
+ ' $ret=$LASTEXITCODE',
120
+ '}',
121
+ 'exit $ret',
122
+ '',
123
+ ].join('\n');
124
+
125
+ const sh = [
126
+ '#!/bin/sh',
127
+ 'basedir=$(dirname "$(echo "$0" | sed -e \'s,\\\\,/,g\')")',
128
+ '',
129
+ 'case `uname` in',
130
+ ' *CYGWIN*|*MINGW*|*MSYS*)',
131
+ ' if command -v cygpath > /dev/null 2>&1; then',
132
+ ' basedir=`cygpath -w "$basedir"`',
133
+ ' fi',
134
+ ' ;;',
135
+ 'esac',
136
+ '',
137
+ 'if [ -x "$basedir/node" ]; then',
138
+ ` exec "$basedir/node" "$basedir/${relPosix}" "$@"`,
139
+ 'else',
140
+ ` exec node "$basedir/${relPosix}" "$@"`,
141
+ 'fi',
142
+ '',
143
+ ].join('\n');
144
+
145
+ const targets = [
146
+ ['zelari-code.cmd', cmd],
147
+ ['zelari-code.ps1', ps1],
148
+ ['zelari-code', sh],
149
+ ];
150
+ let cmdWritten = false;
151
+ for (const [name, content] of targets) {
152
+ const dest = path.join(prefix, name);
153
+ // Never clobber an existing shim — only fill the gap npm left.
154
+ if (existsSync(dest)) continue;
155
+ try {
156
+ writeFileSync(dest, content, 'utf8');
157
+ if (name === 'zelari-code.cmd') cmdWritten = true;
158
+ } catch {
159
+ // Permission denied (prefix owned by admin) or read-only FS — the
160
+ // warning below still tells the user how to fix it manually.
161
+ }
162
+ }
163
+ return cmdWritten;
164
+ }
165
+
46
166
  const warn = (msg) => {
47
167
  // eslint-disable-next-line no-console
48
168
  console.warn(`[zelari-code postinstall] ${msg}`);
@@ -101,6 +221,17 @@ try {
101
221
 
102
222
  if (!existsSync(shimPath)) {
103
223
  shimReason = `shim file not found at ${shimPath}`;
224
+ // Auto-repair the most common Windows failure: the package unpacked but
225
+ // npm never created the bin shim. We only write shims that are MISSING,
226
+ // and only under our own bin name → no risk of shadowing another tool.
227
+ if (isWin) {
228
+ const repaired = repairWindowsShim(prefix, pkgName);
229
+ if (repaired && existsSync(shimPath)) {
230
+ note(`shim was missing — auto-created ${shimPath}`);
231
+ note('open a NEW terminal and run `zelari-code --version` to confirm.');
232
+ process.exit(0);
233
+ }
234
+ }
104
235
  } else {
105
236
  try {
106
237
  const st = statSync(shimPath);