Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/criblio/js2bin
NodeJS application to native executable
https://github.com/criblio/js2bin
Last synced: 8 days ago
JSON representation
NodeJS application to native executable
- Host: GitHub
- URL: https://github.com/criblio/js2bin
- Owner: criblio
- License: apache-2.0
- Created: 2019-06-21T22:00:51.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2024-07-18T14:54:01.000Z (4 months ago)
- Last Synced: 2024-09-19T17:37:39.385Z (about 2 months ago)
- Language: JavaScript
- Size: 170 KB
- Stars: 87
- Watchers: 8
- Forks: 15
- Open Issues: 22
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# js2bin
NodeJS application to native executable# What is it?
`js2bin` is a command line utility that helps package a bundled (think `webpack`, `rollup` etc) NodeJS application into a native executable. See [How it works](#how-it-works) for more info.## Why do I care?
Shipping a native binary has a number of benefits over shipping a bundled `.js` fileBenefits for you
* control over NodeJS version your code runs on
* develop for a NodeJS version that best fits your usecase
* stronger code obfuscation than what `UglifyJS` can provide
* reduced burden on supporting older versions of NodeJS
* decide to upgrade NodeJS based on your app's version
* reduced docs needs showing users how to install `node` (on some platforms it can be a challenge)Benefits for your user/customer:
* quick getting started experience
* reduced burden by not requiring installation/upgrade `node/npm`# Quick getting started?
NOTE:
* all CLI options to `js2bin` take the form `--option` or `--option=value`
* certain options can be arrays in such case provide the option multiple times, e.g. `--option=a --option=b`## Executable for one platform
Make an executable of your application for MacOS:```
js2bin --build --platform=darwin --node=10.16.0 --app=/path/to/my/app.js --name=CoolAppName
```
this will create a file named `CoolAppName-darwin-x64` on your current working directory - if you're on a Mac you'll be able to execute it. Go ahead try it!## Executable for > 1 platform
Make an executable of your application for MacOS and Linux:```
js2bin --build --platform=darwin --platform=linux --node=10.16.0 --app=/path/to/my/app.js --name=CoolAppName
```
this will create 2 files named `CoolAppName-darwin-x64` and `CoolAppName-linux-x64` on your current working directory - if you're on Mac or Linux you'll be able to execute either of them. Go ahead try it!## Build for your version of Node
In case where you want to build for a version of Node for which a prebuild binary does not exist you'll need to follow a 2 step process
1. create a build with a placeholder (this will download NodeJS source, build it locally and add the build to the local cache)
```
js2bin --ci --cache --node=10.13.0 --size=2MB
```
2. bundle your application into the just built binary
```
js2bin --build --cache --node=10.13.0 --app=/path/to/my/app.js --name=CoolAppName
```# CLI Options
```
--help: print this help message--build: embed your application into the precompiled NodeJS binary.
--node: NodeJS version(s) to use, can specify more than one.
e.g. --node=10.16.0 --node=12.4.0
--platform: Platform(s) to build for, can specifiy more than one.
e.g. --platform=linux --plaform=darwin
--app: Path to your (bundled) application.
e.g. --app=/path/to/app/index.js
--name: Application name (optional)
--dir: (opt) Working directory, if not specified use cwd
e.g. --dir=/tmp/js2bin
--cache (opt) Cache any pre-built binaries used, to avoid redownload--ci: build NodeJS with preallocated space for embedding applications
--node: NodeJS version to build from source, can specify more than one.
e.g. --node=10.16.0
--size: Amount of preallocated space, can specify more than one.
e.g. --size=2MB --size==4MB
--dir: (opt) Working directory, if not specified use cwd
--cache: (opt) whether to keep build in the cache (to be reused by --build)
--upload: (opt) whether to upload node build to github releases
--clean: (opt) whether to clean up after the build```
# Code changes
While we've tried to minimize the amount of code changes needed when developing and testing using `node` and shipping a native binary, there are a few things you need to keep in mind:
1. `process.argv` - `js2bin` inserts and fake argument in `process.argv[1] = cwd()/.js`. This ensures minimal code changes, but if your application depends on using that file (highly unlikely) that file won't be there.
2. `child_process.fork` - if you're application uses this NodeJS specific forking method (to spawn more copies of itself) then you'll need to distinguish between `node` mode and bundled app -
```
if(path.basename(process.execPath) === 'node') {
child_process.fork(....)
} else {
child_process.spawn(process.execPath, ...)
}
```# How it works?
NodeJS provides **compile time** hooks for changing the behavior of the resulting binary, usually `node`. This is done by allowing users to (a) place their code in `lib/_third_party_main.js`, (b) modifying `node.gyp` to include that file in the build and then recompile. Once `node` is compiled, at startup controll will be passed to `lib/_third_party_main.js` as early as possible. There are some caveats tho:
1. few options that are handled before control is handed over (e.g. `--version`)
2. few modules (like `clustering`) that are not set up
3. contents of `process.argv` would be different when your app is started this way (only relevant if you want to develop using `node` but ship your app bundled up)While the above is pretty straightforward it suffers from two problems:
1. need to recompile `node` after every change to your application
2. recompiling `node` can take time - think 20+ minutes (if you're on a laptop) - thus, less than ideal development experience.Now, imagine if we changed the `node` build process to be a two step process:
1. compile `node` with some placeholder content, large enought to fit our application and cache it.
2. build our application by inserting it into the pre-compiled binary from (1).This is exactly what `js2bin` does - with the following specs/modifications:
1. `node` binaries are prebuilt for a number of platforms with placeholder content for 2 and 4MB in size
2. the contents of the embedded application are compressed and base64 encoded. (why? because when embedding a JS script that has chars outside the ASCII range the **entire** script is stored using UCS-2/UTF-16 for storing the script, thus doubling in size. This is common when you bundle up char conversion libraries that contain pregenerated tables - e.g. is the popular [iconv-lite](https://www.npmjs.com/package/iconv-lite) )