Demonstrates bundling a package with a native .node binding - extracts it to a real file next to the exe at first run (dlopen needs a real file, can't load from the zip) and requires it from there via the usual bundleRules pattern. Building and running this end-to-end from a directory with no real node_modules nearby is what surfaced and verified the four fixes in the previous two commits.
system-info
A minimal nexe example that reads GPU information via a native addon
(@oxmc/node-gpuinfo).
const gpu = require("@oxmc/node-gpuinfo");
const count = gpu.getGpuCount();
console.log(`Found ${count} GPU(s)\n`);
for (const info of gpu.getAllGpuInfo()) {
console.log(`GPU ${info.index}: ${info.name} (${info.vendor})`);
console.log(` Memory: ${info.memoryUsed} / ${info.memoryTotal} MB`);
console.log(` Utilization: ${info.gpuUtilization}%`);
console.log(` Temperature: ${info.temperature}°C`);
}
The real index.js in this folder extracts the native .node binding to a
real file next to the exe at first run (dlopen needs a real file on disk -
it can't load straight out of the zip snapshot), then loads it from there.
Building
npm install
node compile.js
compile.js builds system-info.exe with a bundleRules entry for
@oxmc/node-gpuinfo so its compiled .node binary and index.js are
included alongside the extraction logic.
Bugs this example found and fixed in nexe
Building and running this example surfaced four separate, real bugs in nexe's runtime module resolution and bundling - all now fixed:
- CJS subpath requires (
require('pkg/subpath')) -Module._findPathtreated the whole request as a package directory name instead of splitting it into (package, subpath) and resolving the subpath against the package'sexportsmap. Brokerequire('consola/basic')(a transitive dep of@mapbox/node-pre-gyp, itself a transitive dep here). internalModuleReadJSONreturn shape - our patch returned a bare string/undefined, but Node <22'spackage_json_reader.jsdestructures the result as an array-like tuple ({0: string, 1: containsKeys}), which crashed on Node 20.- Relative requires into a directory (e.g.
require('..')from deep inside a package, reaching back to its own root) weren't covered by any VFS-aware package.json resolution at all - only bare specifiers were. bundleRuleswas inert for modules the static resolver never traced. This example's ownindex.jsonly reaches@oxmc/node-gpuinfoviarequire.resolve()and a dynamically-computedrequire(path.resolve(...))- neither of which
resolve-dependenciescan statically follow - so thebundleRulesentry for it never triggered.bundleRulesnow force-scans any named module directly fromnode_modules, regardless of whether the tracer found it.
- neither of which
See MEMORY.md/project notes for the full technical detail on each fix
(all in src/fs/patch.ts and src/steps/bundle.ts).
Verified working: built and run from a directory with no node_modules
anywhere nearby (e.g. copied to Desktop) - prints real GPU info, exit 0.