@lpm-registry/cli 0.5.0 → 0.7.0
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/CHANGELOG.md +24 -0
- package/bin/lpm.js +157 -213
- package/index.d.ts +2 -2
- package/lib/commands/quality.js +3 -3
- package/lib/quality/checks.js +37 -37
- package/lib/quality/display.js +4 -4
- package/lib/quality/score.js +5 -5
- package/lib/quality/swift-checks.js +18 -18
- package/package.json +6 -1
- package/scripts/install-binary.js +178 -37
|
@@ -1,32 +1,62 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* LPM CLI Binary Installer — 3-tier fallback + hard-link optimization.
|
|
5
5
|
*
|
|
6
|
-
* Runs as a postinstall hook.
|
|
7
|
-
* lpm.dev/releases and places it in the package's bin/ directory.
|
|
6
|
+
* Runs as a postinstall hook. Resolution order:
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* 1. optionalDependencies — platform package installed by npm (fastest, cached)
|
|
9
|
+
* 2. GitHub Releases — direct download if optionalDependencies missing (--no-optional)
|
|
10
|
+
* 3. Graceful fallback — JS CLI handles commands if binary unavailable
|
|
11
|
+
*
|
|
12
|
+
* After resolving the binary, hard-links it over bin/lpm.js so subsequent
|
|
13
|
+
* runs execute the native binary directly (zero Node.js overhead).
|
|
14
|
+
*
|
|
15
|
+
* Follows the esbuild pattern: https://github.com/evanw/esbuild
|
|
11
16
|
*/
|
|
12
17
|
|
|
13
18
|
import { execSync } from "node:child_process"
|
|
14
19
|
import fs from "node:fs"
|
|
15
20
|
import http from "node:http"
|
|
16
21
|
import https from "node:https"
|
|
22
|
+
import { createRequire } from "node:module"
|
|
23
|
+
import os from "node:os"
|
|
17
24
|
import path from "node:path"
|
|
18
25
|
import { fileURLToPath } from "node:url"
|
|
19
26
|
|
|
20
27
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
21
28
|
const pkgDir = path.join(__dirname, "..")
|
|
22
29
|
const binDir = path.join(pkgDir, "bin")
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
const require = createRequire(import.meta.url)
|
|
31
|
+
|
|
32
|
+
// Platform → { package name, binary filename in package }
|
|
33
|
+
const PLATFORMS = {
|
|
34
|
+
"darwin-arm64": {
|
|
35
|
+
pkg: "@lpm-registry/cli-darwin-arm64",
|
|
36
|
+
binary: "lpm",
|
|
37
|
+
},
|
|
38
|
+
"darwin-x64": {
|
|
39
|
+
pkg: "@lpm-registry/cli-darwin-x64",
|
|
40
|
+
binary: "lpm",
|
|
41
|
+
},
|
|
42
|
+
"linux-x64": {
|
|
43
|
+
pkg: "@lpm-registry/cli-linux-x64",
|
|
44
|
+
binary: "lpm",
|
|
45
|
+
},
|
|
46
|
+
"linux-arm64": {
|
|
47
|
+
pkg: "@lpm-registry/cli-linux-arm64",
|
|
48
|
+
binary: "lpm",
|
|
49
|
+
},
|
|
50
|
+
"win32-x64": {
|
|
51
|
+
pkg: "@lpm-registry/cli-win32-x64",
|
|
52
|
+
binary: "lpm.exe",
|
|
53
|
+
},
|
|
54
|
+
}
|
|
27
55
|
|
|
28
|
-
//
|
|
29
|
-
const
|
|
56
|
+
// GitHub Releases fallback URLs
|
|
57
|
+
const GITHUB_RELEASES =
|
|
58
|
+
"https://github.com/lpm-dev/rust-client/releases/download"
|
|
59
|
+
const GITHUB_BINARY_NAMES = {
|
|
30
60
|
"darwin-arm64": "lpm-darwin-arm64",
|
|
31
61
|
"darwin-x64": "lpm-darwin-x64",
|
|
32
62
|
"linux-x64": "lpm-linux-x64",
|
|
@@ -34,46 +64,157 @@ const PLATFORM_MAP = {
|
|
|
34
64
|
"win32-x64": "lpm-win32-x64.exe",
|
|
35
65
|
}
|
|
36
66
|
|
|
37
|
-
const GITHUB_RELEASES =
|
|
38
|
-
"https://github.com/lpm-dev/rust-client/releases/download"
|
|
39
|
-
const pkg = JSON.parse(
|
|
40
|
-
fs.readFileSync(path.join(pkgDir, "package.json"), "utf-8"),
|
|
41
|
-
)
|
|
42
|
-
|
|
43
67
|
async function main() {
|
|
44
|
-
const platform = `${process.platform}-${
|
|
45
|
-
const
|
|
68
|
+
const platform = `${process.platform}-${os.arch()}`
|
|
69
|
+
const platformInfo = PLATFORMS[platform]
|
|
46
70
|
|
|
47
|
-
if (!
|
|
48
|
-
console.log(
|
|
71
|
+
if (!platformInfo) {
|
|
72
|
+
console.log(
|
|
73
|
+
`[lpm] No pre-built binary for ${platform}. Using JS CLI fallback.`,
|
|
74
|
+
)
|
|
49
75
|
return
|
|
50
76
|
}
|
|
51
77
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
78
|
+
const pkg = JSON.parse(
|
|
79
|
+
fs.readFileSync(path.join(pkgDir, "package.json"), "utf-8"),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
// ── Tier 1: Try optionalDependencies (platform package) ──────────
|
|
83
|
+
let binaryPath = tryOptionalDependency(platformInfo)
|
|
84
|
+
|
|
85
|
+
// ── Tier 2: Direct download from GitHub Releases ─────────────────
|
|
86
|
+
if (!binaryPath) {
|
|
87
|
+
const githubBinaryName = GITHUB_BINARY_NAMES[platform]
|
|
88
|
+
if (githubBinaryName) {
|
|
89
|
+
binaryPath = await tryGitHubDownload(pkg.version, githubBinaryName)
|
|
60
90
|
}
|
|
61
91
|
}
|
|
62
92
|
|
|
63
|
-
|
|
93
|
+
if (!binaryPath) {
|
|
94
|
+
console.log("[lpm] Binary not available. Using JS CLI fallback.")
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ── Hard-link optimization ───────────────────────────────────────
|
|
99
|
+
// Replace bin/lpm.js with a hard link to the native binary.
|
|
100
|
+
// After this, `lpm` on PATH IS the Rust binary — zero Node.js overhead.
|
|
101
|
+
if (process.platform !== "win32") {
|
|
102
|
+
tryHardLink(binaryPath)
|
|
103
|
+
} else {
|
|
104
|
+
// Windows: copy instead of hard-link (npm creates .cmd shims)
|
|
105
|
+
tryCopyBinary(binaryPath)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
64
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Tier 1: Resolve binary from optionalDependencies platform package.
|
|
111
|
+
* Returns the path to the binary, or null if not found.
|
|
112
|
+
*/
|
|
113
|
+
function tryOptionalDependency(platformInfo) {
|
|
65
114
|
try {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
115
|
+
// require.resolve finds the package in node_modules
|
|
116
|
+
const pkgJsonPath = require.resolve(`${platformInfo.pkg}/package.json`)
|
|
117
|
+
const pkgDir = path.dirname(pkgJsonPath)
|
|
118
|
+
const binaryPath = path.join(pkgDir, platformInfo.binary)
|
|
119
|
+
|
|
120
|
+
if (fs.existsSync(binaryPath)) {
|
|
121
|
+
// Verify the binary is executable
|
|
122
|
+
try {
|
|
123
|
+
execSync(`"${binaryPath}" --version`, {
|
|
124
|
+
stdio: "ignore",
|
|
125
|
+
timeout: 5000,
|
|
126
|
+
})
|
|
127
|
+
console.log(`[lpm] Using native binary from ${platformInfo.pkg}`)
|
|
128
|
+
return binaryPath
|
|
129
|
+
} catch {
|
|
130
|
+
console.log(
|
|
131
|
+
`[lpm] Binary from ${platformInfo.pkg} exists but not executable.`,
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
// Package not installed (--no-optional, or resolution failed)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return null
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Tier 2: Download binary directly from GitHub Releases.
|
|
144
|
+
* Returns the path to the downloaded binary, or null on failure.
|
|
145
|
+
*/
|
|
146
|
+
async function tryGitHubDownload(version, githubBinaryName) {
|
|
147
|
+
const url = `${GITHUB_RELEASES}/v${version}/${githubBinaryName}`
|
|
148
|
+
const destName = process.platform === "win32" ? "lpm-bin.exe" : "lpm-bin"
|
|
149
|
+
const destPath = path.join(binDir, destName)
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
console.log(`[lpm] Platform package not found. Downloading from GitHub...`)
|
|
153
|
+
await download(url, destPath)
|
|
154
|
+
fs.chmodSync(destPath, 0o755)
|
|
155
|
+
console.log("[lpm] Native binary downloaded from GitHub Releases.")
|
|
156
|
+
return destPath
|
|
70
157
|
} catch (err) {
|
|
71
|
-
console.log(`[lpm]
|
|
158
|
+
console.log(`[lpm] GitHub download failed: ${err.message}. Using JS CLI.`)
|
|
72
159
|
// Clean up partial download
|
|
73
|
-
if (fs.existsSync(
|
|
160
|
+
if (fs.existsSync(destPath)) fs.unlinkSync(destPath)
|
|
161
|
+
return null
|
|
74
162
|
}
|
|
75
163
|
}
|
|
76
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Hard-link the native binary over bin/lpm.js (Unix only).
|
|
167
|
+
*
|
|
168
|
+
* After this, the `lpm` symlink on PATH points directly to the Rust binary.
|
|
169
|
+
* Node.js is never started. Zero overhead.
|
|
170
|
+
*
|
|
171
|
+
* We keep lpm.js.bak as the JS fallback — if the hard link is broken
|
|
172
|
+
* (npm rebuild, etc.), the JS wrapper can be restored.
|
|
173
|
+
*/
|
|
174
|
+
function tryHardLink(binaryPath) {
|
|
175
|
+
const shimPath = path.join(binDir, "lpm.js")
|
|
176
|
+
const backupPath = path.join(binDir, "lpm.js.bak")
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
// Back up the JS shim (only if not already backed up)
|
|
180
|
+
if (fs.existsSync(shimPath) && !fs.existsSync(backupPath)) {
|
|
181
|
+
fs.copyFileSync(shimPath, backupPath)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Replace JS shim with hard link to native binary
|
|
185
|
+
if (fs.existsSync(shimPath)) {
|
|
186
|
+
fs.unlinkSync(shimPath)
|
|
187
|
+
}
|
|
188
|
+
fs.linkSync(binaryPath, shimPath)
|
|
189
|
+
|
|
190
|
+
console.log("[lpm] Hard-linked native binary (zero Node.js overhead)")
|
|
191
|
+
} catch (err) {
|
|
192
|
+
// Hard link failed (cross-device mount, permissions, etc.)
|
|
193
|
+
// Restore JS shim — it will delegate at runtime (50ms overhead)
|
|
194
|
+
if (fs.existsSync(backupPath) && !fs.existsSync(shimPath)) {
|
|
195
|
+
fs.copyFileSync(backupPath, shimPath)
|
|
196
|
+
}
|
|
197
|
+
console.log(`[lpm] Hard-link failed (${err.code}). Using JS wrapper.`)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Copy binary to bin/ directory (Windows fallback).
|
|
203
|
+
* npm on Windows creates .cmd shims, so hard-linking doesn't work the same way.
|
|
204
|
+
*/
|
|
205
|
+
function tryCopyBinary(binaryPath) {
|
|
206
|
+
const destPath = path.join(binDir, "lpm-bin.exe")
|
|
207
|
+
try {
|
|
208
|
+
fs.copyFileSync(binaryPath, destPath)
|
|
209
|
+
console.log("[lpm] Copied native binary to bin/")
|
|
210
|
+
} catch (err) {
|
|
211
|
+
console.log(`[lpm] Copy failed: ${err.message}. Using JS wrapper.`)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Download a file from a URL, following redirects (GitHub → S3).
|
|
217
|
+
*/
|
|
77
218
|
function download(url, dest, redirects = 0) {
|
|
78
219
|
if (redirects > 5) return Promise.reject(new Error("Too many redirects"))
|
|
79
220
|
|
|
@@ -109,5 +250,5 @@ function download(url, dest, redirects = 0) {
|
|
|
109
250
|
}
|
|
110
251
|
|
|
111
252
|
main().catch(() => {
|
|
112
|
-
// Silently fail — JS CLI fallback
|
|
253
|
+
// Silently fail — JS CLI fallback will handle all commands
|
|
113
254
|
})
|