• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Single executable applications
2
3<!--introduced_in=v18.16.0-->
4
5<!-- YAML
6added:
7  - v19.7.0
8  - v18.16.0
9-->
10
11> Stability: 1 - Experimental: This feature is being designed and will change.
12
13<!-- source_link=src/node_sea.cc -->
14
15This feature allows the distribution of a Node.js application conveniently to a
16system that does not have Node.js installed.
17
18Node.js supports the creation of [single executable applications][] by allowing
19the injection of a JavaScript file into the `node` binary. During start up, the
20program checks if anything has been injected. If the script is found, it
21executes its contents. Otherwise Node.js operates as it normally does.
22
23The single executable application feature only supports running a single
24embedded [CommonJS][] file.
25
26A bundled JavaScript file can be turned into a single executable application
27with any tool which can inject resources into the `node` binary.
28
29Here are the steps for creating a single executable application using one such
30tool, [postject][]:
31
321. Create a JavaScript file:
33   ```console
34   $ echo 'console.log(`Hello, ${process.argv[2]}!`);' > hello.js
35   ```
36
372. Create a copy of the `node` executable and name it according to your needs:
38   ```console
39   $ cp $(command -v node) hello
40   ```
41
423. Inject the JavaScript file into the copied binary by running `postject` with
43   the following options:
44
45   * `hello` - The name of the copy of the `node` executable created in step 2.
46   * `NODE_JS_CODE` - The name of the resource / note / section in the binary
47     where the contents of the JavaScript file will be stored.
48   * `hello.js` - The name of the JavaScript file created in step 1.
49   * `--sentinel-fuse NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2` - The
50     [fuse][] used by the Node.js project to detect if a file has been injected.
51   * `--macho-segment-name NODE_JS` (only needed on macOS) - The name of the
52     segment in the binary where the contents of the JavaScript file will be
53     stored.
54
55   To summarize, here is the required command for each platform:
56
57   * On systems other than macOS:
58     ```console
59     $ npx postject hello NODE_JS_CODE hello.js \
60         --sentinel-fuse NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2
61     ```
62
63   * On macOS:
64     ```console
65     $ npx postject hello NODE_JS_CODE hello.js \
66         --sentinel-fuse NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2 \
67         --macho-segment-name NODE_JS
68     ```
69
704. Run the binary:
71   ```console
72   $ ./hello world
73   Hello, world!
74   ```
75
76## Notes
77
78### `require(id)` in the injected module is not file based
79
80`require()` in the injected module is not the same as the [`require()`][]
81available to modules that are not injected. It also does not have any of the
82properties that non-injected [`require()`][] has except [`require.main`][]. It
83can only be used to load built-in modules. Attempting to load a module that can
84only be found in the file system will throw an error.
85
86Instead of relying on a file based `require()`, users can bundle their
87application into a standalone JavaScript file to inject into the executable.
88This also ensures a more deterministic dependency graph.
89
90However, if a file based `require()` is still needed, that can also be achieved:
91
92```js
93const { createRequire } = require('node:module');
94require = createRequire(__filename);
95```
96
97### `__filename` and `module.filename` in the injected module
98
99The values of `__filename` and `module.filename` in the injected module are
100equal to [`process.execPath`][].
101
102### `__dirname` in the injected module
103
104The value of `__dirname` in the injected module is equal to the directory name
105of [`process.execPath`][].
106
107### Single executable application creation process
108
109A tool aiming to create a single executable Node.js application must
110inject the contents of a JavaScript file into:
111
112* a resource named `NODE_JS_CODE` if the `node` binary is a [PE][] file
113* a section named `NODE_JS_CODE` in the `NODE_JS` segment if the `node` binary
114  is a [Mach-O][] file
115* a note named `NODE_JS_CODE` if the `node` binary is an [ELF][] file
116
117Search the binary for the
118`NODE_JS_FUSE_fce680ab2cc467b6e072b8b5df1996b2:0` [fuse][] string and flip the
119last character to `1` to indicate that a resource has been injected.
120
121### Platform support
122
123Single-executable support is tested regularly on CI only on the following
124platforms:
125
126* Windows
127* macOS
128* Linux (AMD64 only)
129
130This is due to a lack of better tools to generate single-executables that can be
131used to test this feature on other platforms.
132
133Suggestions for other resource injection tools/workflows are welcomed. Please
134start a discussion at <https://github.com/nodejs/single-executable/discussions>
135to help us document them.
136
137[CommonJS]: modules.md#modules-commonjs-modules
138[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
139[Mach-O]: https://en.wikipedia.org/wiki/Mach-O
140[PE]: https://en.wikipedia.org/wiki/Portable_Executable
141[`process.execPath`]: process.md#processexecpath
142[`require()`]: modules.md#requireid
143[`require.main`]: modules.md#accessing-the-main-module
144[fuse]: https://www.electronjs.org/docs/latest/tutorial/fuses
145[postject]: https://github.com/nodejs/postject
146[single executable applications]: https://github.com/nodejs/single-executable
147