@panache-cli/panache 3.1.0 → 3.2.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.
Files changed (2) hide show
  1. package/bin/panache.js +84 -25
  2. package/package.json +9 -9
package/bin/panache.js CHANGED
@@ -1,7 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
 
4
- const { execFileSync } = require("node:child_process");
4
+ const { execFileSync, spawnSync } = require("node:child_process");
5
+
6
+ const PACKAGES = {
7
+ "linux-x64-gnu": "@panache-cli/linux-x64-gnu",
8
+ "linux-arm64-gnu": "@panache-cli/linux-arm64-gnu",
9
+ "linux-x64-musl": "@panache-cli/linux-x64-musl",
10
+ "linux-arm64-musl": "@panache-cli/linux-arm64-musl",
11
+ "darwin-x64": "@panache-cli/darwin-x64",
12
+ "darwin-arm64": "@panache-cli/darwin-arm64",
13
+ "win32-x64": "@panache-cli/win32-x64",
14
+ "win32-arm64": "@panache-cli/win32-arm64",
15
+ };
5
16
 
6
17
  function detectLibc() {
7
18
  if (process.platform !== "linux") return null;
@@ -13,46 +24,83 @@ function detectLibc() {
13
24
  }
14
25
  }
15
26
 
16
- function platformPackage() {
27
+ // Candidate platform keys in preference order. On linux the other libc
28
+ // flavor is kept as a fallback: a host can report glibc yet be unable to
29
+ // run generic glibc binaries (NixOS has no loader at the standard path),
30
+ // while the musl build is statically linked and runs anywhere.
31
+ function candidateKeys() {
17
32
  const { platform, arch } = process;
18
33
  const libc = detectLibc();
19
-
20
- const map = {
21
- "linux-x64-gnu": "@panache-cli/linux-x64-gnu",
22
- "linux-arm64-gnu": "@panache-cli/linux-arm64-gnu",
23
- "linux-x64-musl": "@panache-cli/linux-x64-musl",
24
- "linux-arm64-musl": "@panache-cli/linux-arm64-musl",
25
- "darwin-x64": "@panache-cli/darwin-x64",
26
- "darwin-arm64": "@panache-cli/darwin-arm64",
27
- "win32-x64": "@panache-cli/win32-x64",
28
- "win32-arm64": "@panache-cli/win32-arm64",
29
- };
30
-
31
- const key = libc ? `${platform}-${arch}-${libc}` : `${platform}-${arch}`;
32
- return { key, name: map[key] };
34
+ if (!libc) return [`${platform}-${arch}`];
35
+ const other = libc === "gnu" ? "musl" : "gnu";
36
+ return [`${platform}-${arch}-${libc}`, `${platform}-${arch}-${other}`];
33
37
  }
34
38
 
35
- function resolveBinary() {
36
- const { key, name } = platformPackage();
37
- if (!name) {
39
+ function resolveCandidates() {
40
+ const keys = candidateKeys();
41
+ if (!PACKAGES[keys[0]]) {
38
42
  throw new Error(
39
- `panache-cli does not ship a prebuilt binary for ${key}.\n` +
43
+ `panache-cli does not ship a prebuilt binary for ${keys[0]}.\n` +
40
44
  `Supported platforms: linux (x64/arm64, gnu+musl), darwin (x64/arm64), win32 (x64/arm64).\n` +
41
45
  `See https://panache.bz for alternative install methods.`,
42
46
  );
43
47
  }
44
48
  const binaryName = process.platform === "win32" ? "panache.exe" : "panache";
45
- try {
46
- return require.resolve(`${name}/${binaryName}`);
47
- } catch (err) {
49
+ const candidates = [];
50
+ let firstError;
51
+ for (const key of keys) {
52
+ try {
53
+ candidates.push(require.resolve(`${PACKAGES[key]}/${binaryName}`));
54
+ } catch (err) {
55
+ firstError = firstError ?? err;
56
+ }
57
+ }
58
+ if (candidates.length === 0) {
48
59
  throw new Error(
49
- `panache-cli expected the optional dependency ${name} to be installed, ` +
60
+ `panache-cli expected the optional dependency ${PACKAGES[keys[0]]} to be installed, ` +
50
61
  `but it could not be resolved.\n` +
51
62
  `This usually means npm skipped it (e.g. \`--no-optional\` or a registry/network issue ` +
52
63
  `during install). Try reinstalling with optional dependencies enabled.\n` +
53
- `Original error: ${err.message}`,
64
+ `Original error: ${firstError.message}`,
54
65
  );
55
66
  }
67
+ return candidates;
68
+ }
69
+
70
+ function runnable(binary) {
71
+ const probe = spawnSync(binary, ["--version"], { stdio: "ignore" });
72
+ return probe.error == null && probe.status === 0;
73
+ }
74
+
75
+ // With a single candidate, trust it: probing every invocation would cost an
76
+ // extra process spawn in the common case. Probing is reserved for the
77
+ // unusual setup where both libc flavors are installed side by side.
78
+ function pickBinary(candidates) {
79
+ if (candidates.length === 1) return candidates[0];
80
+ for (const binary of candidates) {
81
+ if (runnable(binary)) return binary;
82
+ }
83
+ return candidates[0];
84
+ }
85
+
86
+ function loaderHint() {
87
+ const libc = detectLibc();
88
+ if (libc !== "gnu") return;
89
+ const muslPackage = PACKAGES[`${process.platform}-${process.arch}-musl`];
90
+ process.stderr.write(
91
+ `The glibc build of panache cannot run on this system (no standard glibc loader; ` +
92
+ `on NixOS this is expected).\n` +
93
+ `The statically linked musl build works anywhere. Package managers skip it on ` +
94
+ `glibc hosts, so add it explicitly:\n` +
95
+ ` npm: npm install --force ${muslPackage}\n` +
96
+ ` pnpm: pnpm config set supportedArchitectures.libc '["glibc","musl"]' && pnpm install\n` +
97
+ `Or install panache via other means (cargo install panache, nix, prebuilt binaries); ` +
98
+ `see https://panache.bz.\n`,
99
+ );
100
+ }
101
+
102
+ function resolveBinary() {
103
+ return pickBinary(resolveCandidates());
56
104
  }
57
105
 
58
106
  function main() {
@@ -68,12 +116,23 @@ function main() {
68
116
  execFileSync(binary, process.argv.slice(2), { stdio: "inherit" });
69
117
  } catch (err) {
70
118
  if (typeof err.status === "number") {
119
+ // 127 is the loader stub's exit code on hosts that cannot run the
120
+ // glibc build; probe to distinguish that from panache itself
121
+ // exiting 127 before hinting.
122
+ if (err.status === 127 && !runnable(binary)) {
123
+ loaderHint();
124
+ }
71
125
  process.exit(err.status);
72
126
  }
73
127
  if (err.signal) {
74
128
  process.kill(process.pid, err.signal);
75
129
  return;
76
130
  }
131
+ // The binary resolved but could not be spawned; a missing dynamic
132
+ // loader surfaces as ENOENT on hosts without NixOS's stub.
133
+ if (err.code === "ENOENT") {
134
+ loaderHint();
135
+ }
77
136
  process.stderr.write(`Failed to execute panache: ${err.message}\n`);
78
137
  process.exit(1);
79
138
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panache-cli/panache",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "An LSP, formatter, and linter for Pandoc markdown, Quarto, and RMarkdown",
5
5
  "keywords": [
6
6
  "quarto",
@@ -30,13 +30,13 @@
30
30
  "node": ">=20"
31
31
  },
32
32
  "optionalDependencies": {
33
- "@panache-cli/linux-x64-gnu": "3.1.0",
34
- "@panache-cli/linux-arm64-gnu": "3.1.0",
35
- "@panache-cli/linux-x64-musl": "3.1.0",
36
- "@panache-cli/linux-arm64-musl": "3.1.0",
37
- "@panache-cli/darwin-x64": "3.1.0",
38
- "@panache-cli/darwin-arm64": "3.1.0",
39
- "@panache-cli/win32-x64": "3.1.0",
40
- "@panache-cli/win32-arm64": "3.1.0"
33
+ "@panache-cli/linux-x64-gnu": "3.2.0",
34
+ "@panache-cli/linux-arm64-gnu": "3.2.0",
35
+ "@panache-cli/linux-x64-musl": "3.2.0",
36
+ "@panache-cli/linux-arm64-musl": "3.2.0",
37
+ "@panache-cli/darwin-x64": "3.2.0",
38
+ "@panache-cli/darwin-arm64": "3.2.0",
39
+ "@panache-cli/win32-x64": "3.2.0",
40
+ "@panache-cli/win32-arm64": "3.2.0"
41
41
  }
42
42
  }