@cnwenf/occ 2.1.268 → 2.1.269

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 (3) hide show
  1. package/bin/occ.cjs +204 -0
  2. package/dist/cli.js +13436 -11193
  3. package/package.json +7 -3
package/bin/occ.cjs ADDED
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env node
2
+ /*
3
+ * OCC launcher shim.
4
+ *
5
+ * WHY THIS EXISTS
6
+ * OCC ships a Bun-target bundle at dist/cli.js (built with
7
+ * `bun build --target bun`, shebang `#!/usr/bin/env bun`). When a user
8
+ * installs OCC via `npm i -g @cnwenf/occ` on a machine that has npm but
9
+ * NOT Bun, the kernel's shebang resolution fails with
10
+ * `/usr/bin/env: 'bun': No such file or directory` — so the `occ`
11
+ * command never even starts.
12
+ *
13
+ * This shim is a Node script (npm guarantees Node is present), so it
14
+ * ALWAYS runs. It locates a Bun binary, then spawns
15
+ * `bun <package>/dist/cli.js <args…>`. If no Bun is available it prints
16
+ * a clear install instruction instead of a cryptic env error.
17
+ *
18
+ * BUN RESOLUTION ORDER
19
+ * 1. `$BUN_PATH` env (power-user override)
20
+ * 2. `bun` on PATH (the common case: `bun` is an OCC optionalDependency
21
+ * whose postinstall puts the real binary on PATH)
22
+ * 3. `~/.bun/bin/bun` (user installed Bun via bun.sh installer)
23
+ * 4. The optional-dep platform binary directly, e.g.
24
+ * `@oven/bun-linux-x64/bin/bun`. This path works EVEN WHEN npm was
25
+ * run with `--ignore-scripts` (so `bun`'s postinstall never ran and
26
+ * `bun` is not on PATH), because the `@oven/bun-*` packages ship the
27
+ * real ELF/Mach-O/PE binary in the tarball — no postinstall needed.
28
+ * 5. The `bun` meta-package's `bin/bun.exe` (real only if its
29
+ * postinstall ran; redundant with #2 but harmless).
30
+ *
31
+ * If none resolve, print install instructions and exit 1.
32
+ */
33
+ 'use strict'
34
+
35
+ const { existsSync } = require('node:fs')
36
+ const { spawn } = require('node:child_process')
37
+ const path = require('node:path')
38
+ const os = require('node:os')
39
+
40
+ const pkgRoot = path.join(__dirname, '..')
41
+ const cliJs = path.join(pkgRoot, 'dist', 'cli.js')
42
+ const isWin = process.platform === 'win32'
43
+ const bunExeName = isWin ? 'bun.exe' : 'bun'
44
+
45
+ if (!existsSync(cliJs)) {
46
+ console.error(
47
+ 'occ: dist/cli.js not found — the package install is incomplete or corrupt.',
48
+ )
49
+ console.error('Reinstall with: npm i -g @cnwenf/occ')
50
+ process.exit(1)
51
+ }
52
+
53
+ /**
54
+ * The @oven/bun-<platform>-<arch> package(s) that match the current
55
+ * process, ordered by preference (glibc before musl, non-baseline before
56
+ * baseline). Each ships the real Bun binary at `bin/bun` (or `bin/bun.exe`
57
+ * on Windows). List gleaned from the `bun` npm meta-package's
58
+ * optionalDependencies (@oven 1.3.14).
59
+ */
60
+ function platformPackages() {
61
+ const p = process.platform
62
+ const a = process.arch
63
+ if (p === 'darwin' && a === 'x64') return ['@oven/bun-darwin-x64']
64
+ if (p === 'darwin' && a === 'arm64') return ['@oven/bun-darwin-aarch64']
65
+ if (p === 'linux' && a === 'x64')
66
+ return [
67
+ '@oven/bun-linux-x64',
68
+ '@oven/bun-linux-x64-musl',
69
+ '@oven/bun-linux-x64-baseline',
70
+ '@oven/bun-linux-x64-musl-baseline',
71
+ ]
72
+ if (p === 'linux' && a === 'arm64')
73
+ return ['@oven/bun-linux-aarch64', '@oven/bun-linux-aarch64-musl']
74
+ if (p === 'win32' && a === 'x64') return ['@oven/bun-windows-x64']
75
+ if (p === 'win32' && a === 'arm64') return ['@oven/bun-windows-aarch64']
76
+ return []
77
+ }
78
+
79
+ /** Resolve a path inside a dependency package, or null if not installed. */
80
+ function resolveDepFile(spec) {
81
+ try {
82
+ return require.resolve(spec)
83
+ } catch (_) {
84
+ return null
85
+ }
86
+ }
87
+
88
+ /** Ordered candidate Bun binaries. Bare 'bun' (PATH) validated by findOnPath. */
89
+ function bunCandidates() {
90
+ const cands = []
91
+
92
+ // 1. Power-user override.
93
+ if (process.env.BUN_PATH && existsSync(process.env.BUN_PATH)) {
94
+ cands.push(process.env.BUN_PATH)
95
+ }
96
+
97
+ // 2. `bun` on PATH — verified by findBun via an explicit PATH walk. If not
98
+ // present, fall through to #3/#4/#5 instead of returning a phantom
99
+ // candidate that would ENOENT at spawn and skip the better fallbacks.
100
+ cands.push('bun')
101
+
102
+ // 3. ~/.bun/bin/bun (bun.sh installer default location).
103
+ const homeBun = path.join(os.homedir(), '.bun', 'bin', bunExeName)
104
+ if (existsSync(homeBun)) cands.push(homeBun)
105
+
106
+ // 4. Optional-dep platform binary (robust under --ignore-scripts).
107
+ for (const pkg of platformPackages()) {
108
+ const resolved = resolveDepFile(path.posix.join(pkg, 'bin', bunExeName))
109
+ if (resolved && existsSync(resolved)) cands.push(resolved)
110
+ }
111
+
112
+ // 5. `bun` meta-package bin (real only if its postinstall ran).
113
+ const metaBin = resolveDepFile('bun/bin/bun.exe')
114
+ if (metaBin && existsSync(metaBin)) cands.push(metaBin)
115
+
116
+ return cands
117
+ }
118
+
119
+ /**
120
+ * Walk PATH for an executable named `name` (with Windows PATHEXT extensions
121
+ * on win32). Returns the absolute path if found, else null. This is what
122
+ * `bun:bundle`'s bare-candidate check needs — without it, returning 'bun'
123
+ * unconditionally makes the shim skip #3/#4/#5 fallbacks when bun isn't on
124
+ * PATH (the exact scenario the shim exists to handle).
125
+ */
126
+ function findOnPath(name) {
127
+ const pathVar = process.env.PATH || process.env.Path
128
+ if (!pathVar) return null
129
+ const sep = isWin ? ';' : ':'
130
+ const exts = isWin
131
+ ? (process.env.PATHEXT ? process.env.PATHEXT.split(';') : ['.exe', '.cmd', '.bat', ''])
132
+ : ['']
133
+ for (const dir of pathVar.split(sep)) {
134
+ if (!dir) continue
135
+ for (const ext of exts) {
136
+ const candidate = path.join(dir, name + ext)
137
+ if (existsSync(candidate)) return candidate
138
+ }
139
+ }
140
+ return null
141
+ }
142
+
143
+ function findBun() {
144
+ const tried = []
145
+ for (const c of bunCandidates()) {
146
+ if (!c) continue
147
+ tried.push(c)
148
+ // Bare 'bun' is only usable if it's actually on PATH. Verify now so the
149
+ // #3/#4/#5 fallbacks get a chance when it isn't — otherwise we'd return
150
+ // a phantom 'bun' that ENOENTs at spawn, defeating the shim's purpose.
151
+ if (c === 'bun') {
152
+ if (findOnPath(bunExeName)) return { bin: c, viaShell: isWin, tried }
153
+ continue
154
+ }
155
+ if (existsSync(c)) return { bin: c, viaShell: false, tried }
156
+ }
157
+ return { bin: null, viaShell: false, tried }
158
+ }
159
+
160
+ const { bin: bunBin, viaShell, tried } = findBun()
161
+
162
+ if (!bunBin) {
163
+ console.error('')
164
+ console.error('occ: Bun runtime not found.')
165
+ console.error('')
166
+ console.error('OCC requires Bun (>= 1.3.11) to run. Install it with one of:')
167
+ console.error(' npm i -g bun')
168
+ console.error(' curl -fsSL https://bun.sh/install | bash')
169
+ console.error('Then run `occ` again.')
170
+ console.error('')
171
+ console.error('Tried: ' + tried.join(', '))
172
+ process.exit(1)
173
+ }
174
+
175
+ const child = spawn(bunBin, [cliJs, ...process.argv.slice(2)], {
176
+ stdio: 'inherit',
177
+ env: process.env,
178
+ shell: viaShell,
179
+ windowsHide: false,
180
+ })
181
+
182
+ child.on('error', (err) => {
183
+ if (err && err.code === 'ENOENT' && bunBin === 'bun') {
184
+ console.error('')
185
+ console.error('occ: `bun` is on PATH but not executable. Reinstall Bun:')
186
+ console.error(' npm i -g bun')
187
+ console.error(' curl -fsSL https://bun.sh/install | bash')
188
+ process.exit(1)
189
+ }
190
+ console.error('occ: failed to launch bun:', err ? err.message : 'unknown error')
191
+ process.exit(1)
192
+ })
193
+
194
+ child.on('exit', (code, signal) => {
195
+ if (signal) {
196
+ try {
197
+ process.kill(process.pid, signal)
198
+ } catch (_) {
199
+ process.exit(1)
200
+ }
201
+ return
202
+ }
203
+ process.exit(code ?? 0)
204
+ })