The preparation process is quite simple:
- Create a config file that tells Node.js which script to convert into a blob.
- Use the previous config to convert a script into a blob.
- Grab a copy of the Node.js executable installed in your system.
- Inject the created blob into the copy of the Node.js executable.
Of course, given that this feature is experimental, constraints and differences exist. However, I find that the choice of going for CommonJS instead of ES modules to be most intriguing, as ES modules have been supported for a while now. While it doesn't matter much, as we have tools to translate from one format to another, it's just not seamless in most cases.
For small standalone scripts, everything works fine. It's when you try it with real third-party modules that it starts to show its experimental status. Not the fault of Node.js SEA, just features of regular Node.js that don't translate over cleanly. For instance, following are the things I found while trying to bundle
standard into one CommonJS script so that I can turn it into an executable:
Not all third-party modules are readily bundleable. One example of this is
standard-enginewhich imports its
bin/cmdmodule, which doubles as a shell script. Bundlers like Rollup don't know what to do with the "shebang" (the
require()does not work the same way as the regular
require()which can cause issues with modules that use
require()to dynamically load modules. An example is
espree, by first finding its path with
require.resolve()and then using
require()on the discovered path later. Rollup cannot bundle dynamic imports done this way, leaving the code as is, which does not work embedded.
Some modules assume the current directory is a Node.js package, and therefore assume that things like
package.jsonis present. For instance,
standardwill attempt to read the project's
package.jsonfor configuration and project info - which will not exist when embedded.
So far, my workaround to the above is to patch the modules with
patch-package or replace lines using
@rollup/plugin-replace. Not the most elegant solution, I'd have to patch/replace every quirk I find which can easily become a game of cat-and-mouse, but this lets me move forward.