Files
oxmc e3659f41a7 docs: add system-info example (native addon via @oxmc/node-gpuinfo)
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.
2026-07-26 01:47:42 -07:00
..

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:

  1. CJS subpath requires (require('pkg/subpath')) - Module._findPath treated the whole request as a package directory name instead of splitting it into (package, subpath) and resolving the subpath against the package's exports map. Broke require('consola/basic') (a transitive dep of @mapbox/node-pre-gyp, itself a transitive dep here).
  2. internalModuleReadJSON return shape - our patch returned a bare string/undefined, but Node <22's package_json_reader.js destructures the result as an array-like tuple ({0: string, 1: containsKeys}), which crashed on Node 20.
  3. 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.
  4. bundleRules was inert for modules the static resolver never traced. This example's own index.js only reaches @oxmc/node-gpuinfo via require.resolve() and a dynamically-computed require(path.resolve(...))
    • neither of which resolve-dependencies can statically follow - so the bundleRules entry for it never triggered. bundleRules now force-scans any named module directly from node_modules, regardless of whether the tracer found it.

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.