-
Notifications
You must be signed in to change notification settings - Fork 0
fix: say which package is missing when the native binding fails #168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| "@stll/stdnum": patch | ||
| --- | ||
|
|
||
| Say which package is missing when the native binding cannot load. The platform | ||
| binaries are optional dependencies, so an installer that declines them leaves | ||
| the package importable and dead, throwing only on first use. The previous error | ||
| listed every supported target, including the one it was running on, which reads | ||
| as a portability problem rather than a missing install. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| /** | ||
| * The loader had no tests, which is how a real failure stayed invisible: the | ||
| * platform binaries are optionalDependencies, so an installer that declines | ||
| * them leaves this package importable and dead, throwing only on first use. | ||
| * The message that surfaced then said "supported targets: …, linux-x64-gnu, | ||
| * …" while failing ON linux-x64-gnu, which points the reader at a | ||
| * portability problem that does not exist. | ||
| * | ||
| * Every input the loader consults is injectable, so both branches are | ||
| * testable without touching the filesystem or the real platform. | ||
| */ | ||
|
|
||
| import { describe, expect, test } from "bun:test"; | ||
|
|
||
| import type { NativeStdnumBinding } from "../native"; | ||
| import { loadNativeStdnumBinding } from "../native-node"; | ||
|
|
||
| /** Nothing resolves: the shape an install with no platform package has. */ | ||
| const requireNothing = (specifier: string): unknown => { | ||
| throw new Error(`Cannot find module '${specifier}'`); | ||
| }; | ||
|
|
||
| describe("loadNativeStdnumBinding", () => { | ||
| test("names the uninstalled package when the platform IS a build target", () => { | ||
| expect(() => | ||
| loadNativeStdnumBinding({ | ||
| platform: "linux", | ||
| arch: "x64", | ||
| libc: "gnu", | ||
| env: {}, | ||
| requireModule: requireNothing, | ||
| }), | ||
| ).toThrow(/@stll\/stdnum-linux-x64-gnu/u); | ||
| }); | ||
|
|
||
| test("says the target exists rather than blaming the platform", () => { | ||
| let message = ""; | ||
| try { | ||
| loadNativeStdnumBinding({ | ||
| platform: "darwin", | ||
| arch: "arm64", | ||
| env: {}, | ||
| requireModule: requireNothing, | ||
| }); | ||
| } catch (error) { | ||
| message = | ||
| error instanceof Error | ||
| ? error.message | ||
| : String(error); | ||
| } | ||
|
|
||
| expect(message).toContain("IS a build target"); | ||
| expect(message).toContain("optionalDependency"); | ||
| // The old message listed every supported target and nothing else, which | ||
| // read as "your platform is unsupported" for a platform that is. | ||
| expect(message).not.toMatch( | ||
| /^Unable to load native stdnum binding.*supported targets/u, | ||
| ); | ||
| }); | ||
|
|
||
| test("reports an unsupported platform as unsupported", () => { | ||
| let message = ""; | ||
| try { | ||
| loadNativeStdnumBinding({ | ||
| platform: "linux", | ||
| arch: "x64", | ||
| libc: "musl", | ||
| env: {}, | ||
| requireModule: requireNothing, | ||
| }); | ||
| } catch (error) { | ||
| message = | ||
| error instanceof Error | ||
| ? error.message | ||
| : String(error); | ||
| } | ||
|
|
||
| expect(message).toContain( | ||
| "not among the build targets", | ||
| ); | ||
| expect(message).toContain("linux-x64-musl"); | ||
| }); | ||
|
|
||
| test("prefers an explicit library path over the platform package", () => { | ||
| // Every member the binding contract requires; a partial object is | ||
| // rejected as "does not match the stdnum binding contract". | ||
| const binding = new Proxy( | ||
| {}, | ||
| { get: () => () => undefined }, | ||
| ) as NativeStdnumBinding; | ||
|
Comment on lines
+87
to
+90
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Document why this cast is sound. Add a As per coding guidelines, “Avoid 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| const loaded = loadNativeStdnumBinding({ | ||
| platform: "linux", | ||
| arch: "x64", | ||
| libc: "gnu", | ||
| env: { | ||
| STELLA_STDNUM_NATIVE_LIBRARY_PATH: | ||
| "/tmp/custom.node", | ||
| }, | ||
| requireModule: (specifier) => { | ||
| if (specifier === "/tmp/custom.node") | ||
| return binding; | ||
| throw new Error( | ||
| `Cannot find module '${specifier}'`, | ||
| ); | ||
| }, | ||
| }); | ||
|
|
||
| expect(loaded).toBe(binding); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -69,12 +69,31 @@ export const loadNativeStdnumBinding = ( | |||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| const current = [platform, arch, libc] | ||||||||||||||||||||||||||||||
| .filter(Boolean) | ||||||||||||||||||||||||||||||
| .join("-"); | ||||||||||||||||||||||||||||||
| const supported = targets | ||||||||||||||||||||||||||||||
| .map(([p, a, l]) => [p, a, l].filter(Boolean).join("-")) | ||||||||||||||||||||||||||||||
| .join(", "); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Distinguish "we do not build for this platform" from "we do, but the | ||||||||||||||||||||||||||||||
| // package holding the binary is not installed". They read identically in a | ||||||||||||||||||||||||||||||
| // stack trace and have completely different fixes, and the second is the | ||||||||||||||||||||||||||||||
| // common one: the platform packages are optionalDependencies, so anything | ||||||||||||||||||||||||||||||
| // that declines to install them — `--no-optional`, an install-time version | ||||||||||||||||||||||||||||||
| // gate such as a release-age policy, a partial lockfile — leaves this | ||||||||||||||||||||||||||||||
| // package importable and non-functional, with no error until first use. | ||||||||||||||||||||||||||||||
| const reason = | ||||||||||||||||||||||||||||||
| match === undefined | ||||||||||||||||||||||||||||||
| ? `This platform is not among the build targets (${supported}).` | ||||||||||||||||||||||||||||||
| : `This platform IS a build target, so ${match[3]} exists but was not ` + | ||||||||||||||||||||||||||||||
| `installed. It is an optionalDependency: check that optional ` + | ||||||||||||||||||||||||||||||
| `dependencies are enabled, that ${match[3]} is not excluded by an ` + | ||||||||||||||||||||||||||||||
| `install policy, and that it appears in your lockfile.`; | ||||||||||||||||||||||||||||||
|
Comment on lines
+86
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Do not equate a target match with an uninstalled package.
Proposed fix- : `This platform IS a build target, so ${match[3]} exists but was not ` +
- `installed. It is an optionalDependency: check that optional ` +
+ : `This platform is a build target, but ${match[3]} could not be ` +
+ `loaded. It is an optionalDependency: check that optional ` +
`dependencies are enabled, that ${match[3]} is not excluded by an ` +
`install policy, and that it appears in your lockfile.`;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Comment on lines
+89
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the platform package is installed but fails to load, for example because its binary is incompatible, a shared library is unavailable, or its exports fail Useful? React with 👍 / 👎. |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| throw new Error( | ||||||||||||||||||||||||||||||
| `Unable to load native stdnum binding for ${[platform, arch, libc].filter(Boolean).join("-")}; ` + | ||||||||||||||||||||||||||||||
| `supported targets: ${supported}.\n${errors.join("\n")}`, | ||||||||||||||||||||||||||||||
| `Unable to load the native stdnum binding for ${current}. ${reason}\n` + | ||||||||||||||||||||||||||||||
| `Tried:\n${errors.map((line) => ` ${line}`).join("\n")}`, | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use clearer release-note wording.
“Missing install” is awkward here; use “missing installation” or “missing optional package” to describe the failure precisely.
🧰 Tools
🪛 LanguageTool
[grammar] ~9-~9: The word ‘install’ is not a noun.
Context: ...rtability problem rather than a missing install.
(A_INSTALL)
🤖 Prompt for AI Agents
Source: Linters/SAST tools