+84
-25
| #!/usr/bin/env node | ||
| "use strict"; | ||
| const { execFileSync } = require("node:child_process"); | ||
| const { execFileSync, spawnSync } = require("node:child_process"); | ||
| const PACKAGES = { | ||
| "linux-x64-gnu": "@badness/linux-x64-gnu", | ||
| "linux-arm64-gnu": "@badness/linux-arm64-gnu", | ||
| "linux-x64-musl": "@badness/linux-x64-musl", | ||
| "linux-arm64-musl": "@badness/linux-arm64-musl", | ||
| "darwin-x64": "@badness/darwin-x64", | ||
| "darwin-arm64": "@badness/darwin-arm64", | ||
| "win32-x64": "@badness/win32-x64", | ||
| "win32-arm64": "@badness/win32-arm64", | ||
| }; | ||
| function detectLibc() { | ||
@@ -16,26 +27,19 @@ if (process.platform !== "linux") return null; | ||
| function platformPackage() { | ||
| // Candidate platform keys in preference order. On linux the other libc | ||
| // flavor is kept as a fallback: a host can report glibc yet be unable to | ||
| // run generic glibc binaries (NixOS has no loader at the standard path), | ||
| // while the musl build is statically linked and runs anywhere. | ||
| function candidateKeys() { | ||
| const { platform, arch } = process; | ||
| const libc = detectLibc(); | ||
| const map = { | ||
| "linux-x64-gnu": "@badness/linux-x64-gnu", | ||
| "linux-arm64-gnu": "@badness/linux-arm64-gnu", | ||
| "linux-x64-musl": "@badness/linux-x64-musl", | ||
| "linux-arm64-musl": "@badness/linux-arm64-musl", | ||
| "darwin-x64": "@badness/darwin-x64", | ||
| "darwin-arm64": "@badness/darwin-arm64", | ||
| "win32-x64": "@badness/win32-x64", | ||
| "win32-arm64": "@badness/win32-arm64", | ||
| }; | ||
| const key = libc ? `${platform}-${arch}-${libc}` : `${platform}-${arch}`; | ||
| return { key, name: map[key] }; | ||
| if (!libc) return [`${platform}-${arch}`]; | ||
| const other = libc === "gnu" ? "musl" : "gnu"; | ||
| return [`${platform}-${arch}-${libc}`, `${platform}-${arch}-${other}`]; | ||
| } | ||
| function resolveBinary() { | ||
| const { key, name } = platformPackage(); | ||
| if (!name) { | ||
| function resolveCandidates() { | ||
| const keys = candidateKeys(); | ||
| if (!PACKAGES[keys[0]]) { | ||
| throw new Error( | ||
| `badness does not ship a prebuilt binary for ${key}.\n` + | ||
| `badness does not ship a prebuilt binary for ${keys[0]}.\n` + | ||
| `Supported platforms: linux (x64/arm64, gnu+musl), darwin (x64/arm64), win32 (x64/arm64).\n` + | ||
@@ -46,15 +50,59 @@ `See https://jolars.github.io/badness/ for alternative install methods.`, | ||
| const binaryName = process.platform === "win32" ? "badness.exe" : "badness"; | ||
| try { | ||
| return require.resolve(`${name}/${binaryName}`); | ||
| } catch (err) { | ||
| const candidates = []; | ||
| let firstError; | ||
| for (const key of keys) { | ||
| try { | ||
| candidates.push(require.resolve(`${PACKAGES[key]}/${binaryName}`)); | ||
| } catch (err) { | ||
| firstError = firstError ?? err; | ||
| } | ||
| } | ||
| if (candidates.length === 0) { | ||
| throw new Error( | ||
| `badness expected the optional dependency ${name} to be installed, ` + | ||
| `badness expected the optional dependency ${PACKAGES[keys[0]]} to be installed, ` + | ||
| `but it could not be resolved.\n` + | ||
| `This usually means npm skipped it (e.g. \`--no-optional\` or a registry/network issue ` + | ||
| `during install). Try reinstalling with optional dependencies enabled.\n` + | ||
| `Original error: ${err.message}`, | ||
| `Original error: ${firstError.message}`, | ||
| ); | ||
| } | ||
| return candidates; | ||
| } | ||
| function runnable(binary) { | ||
| const probe = spawnSync(binary, ["--version"], { stdio: "ignore" }); | ||
| return probe.error == null && probe.status === 0; | ||
| } | ||
| // With a single candidate, trust it: probing every invocation would cost an | ||
| // extra process spawn in the common case. Probing is reserved for the | ||
| // unusual setup where both libc flavors are installed side by side. | ||
| function pickBinary(candidates) { | ||
| if (candidates.length === 1) return candidates[0]; | ||
| for (const binary of candidates) { | ||
| if (runnable(binary)) return binary; | ||
| } | ||
| return candidates[0]; | ||
| } | ||
| function loaderHint() { | ||
| const libc = detectLibc(); | ||
| if (libc !== "gnu") return; | ||
| const muslPackage = PACKAGES[`${process.platform}-${process.arch}-musl`]; | ||
| process.stderr.write( | ||
| `The glibc build of badness cannot run on this system (no standard glibc loader; ` + | ||
| `on NixOS this is expected).\n` + | ||
| `The statically linked musl build works anywhere. Package managers skip it on ` + | ||
| `glibc hosts, so add it explicitly:\n` + | ||
| ` npm: npm install --force ${muslPackage}\n` + | ||
| ` pnpm: pnpm config set supportedArchitectures.libc '["glibc","musl"]' && pnpm install\n` + | ||
| `Or install badness via other means; ` + | ||
| `see https://jolars.github.io/badness/.\n`, | ||
| ); | ||
| } | ||
| function resolveBinary() { | ||
| return pickBinary(resolveCandidates()); | ||
| } | ||
| function main() { | ||
@@ -73,2 +121,8 @@ let binary; | ||
| if (typeof err.status === "number") { | ||
| // 127 is the loader stub's exit code on hosts that cannot run the | ||
| // glibc build; probe to distinguish that from badness itself | ||
| // exiting 127 before hinting. | ||
| if (err.status === 127 && !runnable(binary)) { | ||
| loaderHint(); | ||
| } | ||
| process.exit(err.status); | ||
@@ -80,2 +134,7 @@ } | ||
| } | ||
| // The binary resolved but could not be spawned; a missing dynamic | ||
| // loader surfaces as ENOENT on hosts without NixOS's stub. | ||
| if (err.code === "ENOENT") { | ||
| loaderHint(); | ||
| } | ||
| process.stderr.write(`Failed to execute badness: ${err.message}\n`); | ||
@@ -82,0 +141,0 @@ process.exit(1); |
+9
-9
| { | ||
| "name": "badness", | ||
| "version": "0.13.0", | ||
| "version": "0.14.0", | ||
| "description": "A language, formatter, and linter for LaTeX", | ||
@@ -31,11 +31,11 @@ "keywords": [ | ||
| "optionalDependencies": { | ||
| "@badness/linux-x64-gnu": "0.13.0", | ||
| "@badness/linux-arm64-gnu": "0.13.0", | ||
| "@badness/linux-x64-musl": "0.13.0", | ||
| "@badness/linux-arm64-musl": "0.13.0", | ||
| "@badness/darwin-x64": "0.13.0", | ||
| "@badness/darwin-arm64": "0.13.0", | ||
| "@badness/win32-x64": "0.13.0", | ||
| "@badness/win32-arm64": "0.13.0" | ||
| "@badness/linux-x64-gnu": "0.14.0", | ||
| "@badness/linux-arm64-gnu": "0.14.0", | ||
| "@badness/linux-x64-musl": "0.14.0", | ||
| "@badness/linux-arm64-musl": "0.14.0", | ||
| "@badness/darwin-x64": "0.14.0", | ||
| "@badness/darwin-arm64": "0.14.0", | ||
| "@badness/win32-x64": "0.14.0", | ||
| "@badness/win32-arm64": "0.14.0" | ||
| } | ||
| } |
+4
-4
| # Badness | ||
| [Badness](https://badness.dev/) is a language server, formatter, and linter | ||
| for LaTeX documents. | ||
| [Badness](https://badness.dev/) is a language server, formatter, and linter for | ||
| LaTeX documents. | ||
@@ -32,4 +32,4 @@ ## Install | ||
| See `badness --help` and the [documentation](https://badness.dev/) | ||
| for the full feature list and configuration reference. | ||
| See `badness --help` and the [documentation](https://badness.dev/) for the full | ||
| feature list and configuration reference. | ||
@@ -36,0 +36,0 @@ ## Supported platforms |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
8038
41.64%129
76.71%1
-50%