@hakobu/hakobu 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,673 +1,171 @@
1
- [![Build Status](https://github.com/yao-pkg/pkg/actions/workflows/ci.yml/badge.svg)](https://github.com/yao-pkg/pkg/actions/workflows/ci.yml)
1
+ # @hakobu/hakobu
2
2
 
3
- This command line interface enables you to package your Node.js project into an executable that can be run even on devices without Node.js installed.
3
+ The modern Node.js packager the successor to [@yao-pkg/pkg](https://github.com/yao-pkg/pkg). Package your Node.js project into a standalone executable that runs without Node.js installed.
4
4
 
5
- ## Use Cases
5
+ ## Install
6
6
 
7
- - Make a commercial version of your application without sources
8
- - Make a demo/evaluation/trial version of your app without sources
9
- - Instantly make executables for other platforms (cross-compilation)
10
- - Make some kind of self-extracting archive or installer
11
- - No need to install Node.js and npm to run the packaged application
12
- - No need to download hundreds of files via `npm install` to deploy
13
- your application. Deploy it as a single file
14
- - Put your assets inside the executable to make it even more portable
15
- - Test your app against new Node.js version without installing it
7
+ ```bash
8
+ npm install @hakobu/hakobu --save-dev
9
+ ```
16
10
 
17
- ## Usage
11
+ Or globally:
18
12
 
19
- ```sh
20
- npm install -g @yao-pkg/pkg
13
+ ```bash
14
+ npm install -g @hakobu/hakobu
21
15
  ```
22
16
 
23
- After installing it, run `pkg --help` without arguments to see list of options:
24
-
25
- ```console
26
- pkg [options] <input>
27
-
28
- Options:
29
-
30
- -h, --help output usage information
31
- -v, --version output pkg version
32
- -t, --targets comma-separated list of targets (see examples)
33
- -c, --config package.json or any json file with top-level config
34
- --options bake v8 options into executable to run with them on
35
- -o, --output output file name or template for several files
36
- --out-path path to save output one or more executables
37
- -d, --debug show more information during packaging process [off]
38
- -b, --build don't download prebuilt base binaries, build them
39
- --public speed up and disclose the sources of top-level project
40
- --public-packages force specified packages to be considered public
41
- --no-bytecode skip bytecode generation and include source files as plain js
42
- --no-native-build skip native addons build
43
- --no-dict comma-separated list of packages names to ignore dictionaries. Use --no-dict * to disable all dictionaries
44
- -C, --compress [default=None] compression algorithm = Brotli or GZip
45
- --sea (Experimental) compile give file using node's SEA feature. Requires node v20.0.0 or higher and only single file is supported
46
-
47
- Examples:
48
-
49
- – Makes executables for Linux, macOS and Windows
50
- $ pkg index.js
51
- – Takes package.json from cwd and follows 'bin' entry
52
- $ pkg .
53
- – Makes executable for particular target machine
54
- $ pkg -t node14-win-arm64 index.js
55
- – Makes executables for target machines of your choice
56
- $ pkg -t node20-linux,node22-linux,node22-win index.js
57
- – Bakes '--expose-gc' and '--max-heap-size=34' into executable
58
- $ pkg --options "expose-gc,max-heap-size=34" index.js
59
- – Consider packageA and packageB to be public
60
- $ pkg --public-packages "packageA,packageB" index.js
61
- – Consider all packages to be public
62
- $ pkg --public-packages "*" index.js
63
- – Bakes '--expose-gc' into executable
64
- $ pkg --options expose-gc index.js
65
- – reduce size of the data packed inside the executable with GZip
66
- $ pkg --compress GZip index.js
67
- – compile the file using node's SEA feature. Creates executables for Linux, macOS and Windows
68
- $ pkg --sea index.js
69
- ```
17
+ ## Quick Start
70
18
 
71
- The entrypoint of your project is a mandatory CLI argument. It may be:
72
-
73
- - Path to entry file. Suppose it is `/path/app.js`, then
74
- packaged app will work the same way as `node /path/app.js`
75
- - Path to `package.json`. `Pkg` will follow `bin` property of
76
- the specified `package.json` and use it as entry file.
77
- - Path to directory. `Pkg` will look for `package.json` in
78
- the specified directory. See above.
79
-
80
- ### Targets
81
-
82
- `pkg` can generate executables for several target machines at a
83
- time. You can specify a comma-separated list of targets via `--targets`
84
- option. A canonical target consists of 3 elements, separated by
85
- dashes, for example `node20-macos-x64` or `node22-linux-arm64`:
86
-
87
- - **nodeRange** (node8), node10, node12, node14, node16 or latest
88
- - **platform** alpine, linux, linuxstatic, win, macos, (freebsd)
89
- - **arch** x64, arm64, (armv6, armv7)
90
-
91
- (element) is unsupported, but you may try to compile yourself.
92
-
93
- If your target is available in the assets of the latest [pkg-fetch release](https://github.com/yao-pkg/pkg-fetch/releases),
94
- `pkg` downloads the pre-compiled Node.js binary from that project. Otherwise,
95
- or if you specify the `--build` option, it will build the binary from source
96
- (takes a very long time).
97
- Pre-compiled Node.js binaries for some unsupported architectures and
98
- instructions for using them are available in the [pkg-binaries](https://github.com/yao-pkg/pkg-binaries)
99
- project.
100
-
101
- You may omit any element (and specify just `node14` for example).
102
- The omitted elements will be taken from current platform or
103
- system-wide Node.js installation (its version and arch).
104
- There is also an alias `host`, that means that all 3 elements
105
- are taken from current platform/Node.js. By default targets are
106
- `linux,macos,win` for current Node.js version and arch.
107
-
108
- If you want to generate executable for different architectures,
109
- note that by default `pkg` has to run the executable of the
110
- **target** arch to generate bytecodes:
111
-
112
- - Linux: configure binfmt with [QEMU](https://wiki.debian.org/QemuUserEmulation).
113
- - macOS: possible to build `x64` on `arm64` with `Rosetta 2` but not opposite.
114
- - Windows: possible to build `x64` on `arm64` with `x64 emulation` but not opposite.
115
- - or, disable bytecode generation with `--no-bytecode --public-packages "*" --public`.
116
-
117
- `macos-arm64` is experimental. Be careful about the [mandatory code signing requirement](https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11_0_1-universal-apps-release-notes).
118
- The final executable has to be signed (ad-hoc signature is sufficient) with `codesign`
119
- utility of macOS (or `ldid` utility on Linux). Otherwise, the executable will be killed
120
- by kernel and the end-user has no way to permit it to run at all. `pkg` tries to ad-hoc
121
- sign the final executable. If necessary, you can replace this signature with your own
122
- trusted Apple Developer ID.
123
-
124
- To be able to generate executables for all supported architectures and platforms, run
125
- `pkg` on a Linux host with binfmt (`QEMU` emulation) configured and `ldid` installed.
126
-
127
- ### Config
128
-
129
- During packaging process `pkg` parses your sources, detects
130
- calls to `require`, traverses the dependencies of your project
131
- and includes them into executable. In most cases you
132
- don't need to specify anything manually.
133
-
134
- However your code may have `require(variable)` calls (so called non-literal
135
- argument to `require`) or use non-javascript files (for
136
- example views, css, images etc).
137
-
138
- ```js
139
- require('./build/' + cmd + '.js');
140
- path.join(__dirname, 'views/' + viewName);
141
- ```
19
+ ```bash
20
+ # Package the current project
21
+ npx @hakobu/hakobu .
142
22
 
143
- Such cases are not handled by `pkg`. So you must specify the
144
- files - scripts and assets - manually in `pkg` property of
145
- your `package.json` file.
23
+ # Package with a specific target
24
+ npx @hakobu/hakobu . --target node24-linux-x64
146
25
 
147
- ```json
148
- "pkg": {
149
- "scripts": "build/**/*.js",
150
- "assets": "views/**/*",
151
- "targets": [ "node14-linux-arm64" ],
152
- "outputPath": "dist"
153
- }
26
+ # Multi-target build
27
+ npx @hakobu/hakobu . --target node24-linux-x64,node24-macos-arm64,node24-win-x64
154
28
  ```
155
29
 
156
- The above example will include everything in `assets/` and
157
- every .js file in `build/`, build only for `node14-linux-arm64`,
158
- and place the executable inside `dist/`.
159
-
160
- You may also specify arrays of globs:
30
+ ## CLI Usage
161
31
 
162
32
  ```
163
- "assets": [ "assets/**/*", "images/**/*" ]
164
- ```
33
+ hakobu <project-root> [options]
165
34
 
166
- Just be sure to call `pkg package.json` or `pkg .` to make
167
- use of `package.json` configuration.
35
+ Commands:
36
+ hakobu <project-root> Package a project
37
+ hakobu targets Show available targets and cache
38
+ hakobu inspect <project-root> Analyze a project without packaging
39
+ hakobu doctor <project-root> Check if a project is ready to package
168
40
 
169
- ### Scripts
41
+ Options:
42
+ --target <spec> Target(s), comma-separated. Use 'all' for all published targets
43
+ --output <path> Output executable path
44
+ --entry <file> Entry file (relative to project root)
45
+ --debug, -d Show detailed packaging diagnostics
46
+ --help, -h Show help
47
+ --version, -v Show version
170
48
 
171
- `scripts` is a [glob](https://github.com/SuperchupuDev/tinyglobby)
172
- or list of globs. Files specified as `scripts` will be compiled
173
- using `v8::ScriptCompiler` and placed into executable without
174
- sources. They must conform to the JS standards of those Node.js versions
175
- you target (see [Targets](#targets)), i.e. be already transpiled.
49
+ Bundle Mode:
50
+ --bundle [name] Pre-bundle with Rolldown before packaging
51
+ --external <mod> Keep module external when bundling (repeatable)
176
52
 
177
- ### Assets
53
+ Advanced:
54
+ --bytecode Compile JS to V8 bytecode before packaging
55
+ --compress <algo> Compress snapshot (Brotli or GZip)
56
+ --options <flags> Bake V8 flags into executable (comma-separated)
57
+ --build, -b Force local build of base binary
58
+ ```
59
+
60
+ ## Targets
178
61
 
179
- `assets` is a [glob](https://github.com/SuperchupuDev/tinyglobby)
180
- or list of globs. Files specified as `assets` will be packaged
181
- into executable as raw content without modifications. Javascript
182
- files may also be specified as `assets`. Their sources will
183
- not be stripped as it improves execution performance of the
184
- files and simplifies debugging.
62
+ Target format: `node24-{platform}-{arch}`
185
63
 
186
- See also
187
- [Detecting assets in source code](#detecting-assets-in-source-code) and
188
- [Snapshot filesystem](#snapshot-filesystem).
64
+ | Target | Tier |
65
+ |---|---|
66
+ | node24-linux-x64 | 1 |
67
+ | node24-linux-arm64 | 1 |
68
+ | node24-win-x64 | 1 |
69
+ | node24-macos-arm64 | 1 |
70
+ | node24-macos-x64 | 2 |
71
+ | node24-linuxstatic-x64 | 2 |
72
+ | node24-win-arm64 | 2 |
189
73
 
190
- ### Ignore files
74
+ Use `--target all` to build for all published targets.
191
75
 
192
- `ignore` is a list of globs. Files matching the paths specified as `ignore`
193
- will be excluded from the final executable.
76
+ ## Configuration
194
77
 
195
- This is useful when you want to exclude some files from the final executable,
196
- like tests, documentation or build files that could have been included by a dependency.
78
+ Configure via the `"hakobu"` field in `package.json`:
197
79
 
198
80
  ```json
199
- "pkg": {
200
- "ignore": [ "**/*/dependency-name/build.c" ]
81
+ {
82
+ "hakobu": {
83
+ "entry": "src/index.js",
84
+ "assets": ["templates/**", "views/**"],
85
+ "target": "node24-linux-x64",
86
+ "output": "dist/my-app"
201
87
  }
88
+ }
202
89
  ```
203
90
 
204
- Note that both `**` and `*` would not match dotfiles e.g. `.git`,
205
- the dotfile names must be in the glob explicitly to be matched.
206
-
207
- To see if you have unwanted files in your executable, read the [Exploring virtual file system embedded in debug mode](#exploring-virtual-file-system-embedded-in-debug-mode) section.
91
+ The legacy `"pkg"` field is accepted with migration warnings. CLI flags override package.json config.
208
92
 
209
- ### Options
93
+ ## Bundle Mode
210
94
 
211
- Node.js application can be called with runtime options
212
- (belonging to Node.js or V8). To list them type `node --help` or `node --v8-options`.
95
+ For TypeScript and monorepo projects, use `--bundle` to pre-bundle with Rolldown before packaging:
213
96
 
214
- You can "bake" these runtime options into packaged application. The app will always run with the options
215
- turned on. Just remove `--` from option name.
216
-
217
- You can specify multiple options by joining them in a single string, comma (`,`) separated:
218
-
219
- ```sh
220
- pkg app.js --options expose-gc
221
- pkg app.js --options max_old_space_size=4096
222
- pkg app.js --options max-old-space-size=1024,tls-min-v1.0,expose-gc
97
+ ```bash
98
+ npx @hakobu/hakobu . --bundle --entry src/cli.ts --external electron
223
99
  ```
224
100
 
225
- ### Output
226
-
227
- You may specify `--output` if you create only one executable
228
- or `--out-path` to place executables for multiple targets.
229
-
230
- ### Debug
231
-
232
- Pass `--debug` to `pkg` to get a log of packaging process.
233
- If you have issues with some particular file (seems not packaged
234
- into executable), it may be useful to look through the log.
235
-
236
- In order to get more detailed logs on startup, after you packaged your application using `--debug`, you can start your application with the environment variable `DEBUG_PKG` set to `1` or `2` if you want more verbose debugging. This will load `prelude/diagnostic.js` that will print the snapshot tree and the symlink table, when set to `2` it will also mock `fs` in order to print logs when a method is called.
237
-
238
- This is useful to see what's included in your bundle and detect possible missing files or large files that could be removed from it in order to reduce the size of the executable.
239
-
240
- You can also use `SIZE_LIMIT_PKG` and `FOLDER_LIMIT_PKG` to print files/folders that are larger than the specified size limit (in bytes). By default, the size limit is set to 5MB for files and 10MB for folders.
241
-
242
- ### Bytecode (reproducibility)
243
-
244
- By default, your source code is precompiled to v8 bytecode before being written
245
- to the output file. To disable this feature, pass `--no-bytecode` to `pkg`.
246
-
247
- #### Why would you want to do this?
248
-
249
- If you need a reproducible build
250
- process where your executable hashes (e.g. md5, sha1, sha256, etc.) are the
251
- same value between builds. Because compiling bytecode is not deterministic
252
- (see [here](https://ui.adsabs.harvard.edu/abs/2019arXiv191003478C/abstract) or
253
- [here](https://medium.com/dailyjs/understanding-v8s-bytecode-317d46c94775)) it
254
- results in executables with differing hashed values. Disabling bytecode
255
- compilation allows a given input to always have the same output.
101
+ See [Bundle Mode documentation](https://docs.hakobujs.dev/build/bundle-mode) for details.
256
102
 
257
- #### Why would you NOT want to do this?
103
+ ## Platform Features
258
104
 
259
- While compiling to bytecode does not make your source code 100% secure, it does
260
- add a small layer of security/privacy/obscurity to your source code. Turning
261
- off bytecode compilation causes the raw source code to be written directly to
262
- the executable file. If you're on \*nix machine and would like an example, run
263
- `pkg` with the `--no-bytecode` flag, and use the GNU strings tool on the
264
- output. You then should be able to grep your source code.
265
-
266
- #### Other considerations
267
-
268
- Specifying `--no-bytecode` will fail if there are any packages in your project that aren't explicitly marked
269
- as public by the `license` in their `package.json`.
270
- By default, `pkg` will check the license of each package and make sure that stuff that isn't meant for the public will
271
- only be included as bytecode.
272
-
273
- If you do require building pkg binaries for other architectures and/or depend on a package with a broken
274
- `license` in its `package.json`, you can override this behaviour by either explicitly whitelisting packages to be public
275
- using `--public-packages "packageA,packageB"` or setting all packages to public using `--public-packages "*"`
276
-
277
- ### Build
278
-
279
- `pkg` has so called "base binaries" - they are actually same
280
- `node` executables but with some patches applied. They are
281
- used as a base for every executable `pkg` creates. `pkg`
282
- downloads precompiled base binaries before packaging your
283
- application. If you prefer to compile base binaries from
284
- source instead of downloading them, you may pass `--build`
285
- option to `pkg`. First ensure your computer meets the
286
- requirements to compile original Node.js:
287
- [BUILDING.md](https://github.com/nodejs/node/blob/HEAD/BUILDING.md)
288
-
289
- See [pkg-fetch](https://github.com/yao-pkg/pkg-fetch) for more info.
290
-
291
- ### Compression
292
-
293
- Pass `--compress Brotli` or `--compress GZip` to `pkg` to compress further the content of the files store in the exectable.
294
-
295
- This option can reduce the size of the embedded file system by up to 60%.
296
-
297
- The startup time of the application might be reduced slightly.
298
-
299
- `-C` can be used as a shortcut for `--compress`.
300
-
301
- ### Environment
302
-
303
- All pkg-cache [environment vars](https://github.com/yao-pkg/pkg-fetch#environment), plus:
304
-
305
- | Var | Description |
306
- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
307
- | `CHDIR` | Override process `chdir` |
308
- | `PKG_NATIVE_CACHE_PATH` | Override the base directory for caching extracted native addons at runtime (default: `~/.cache`) |
309
- | `PKG_STRICT_VER` | Turn on some assertion in the walker code to assert that each file content/state that we appending to the virtual file system applies to a real file, not a symlink. |
310
- | `PKG_EXECPATH` | Used internally by `pkg`, do not override |
311
-
312
- Examples
105
+ ### macOS Signing & Notarization
313
106
 
314
107
  ```bash
315
- # 1 - Set cache path at build time (for pkg-fetch to cache Node.js binaries)
316
- export PKG_CACHE_PATH=/my/cache
317
- pkg app.js
318
-
319
- # 2 - Set cache path at runtime (for packaged app to cache extracted native addons)
320
- PKG_NATIVE_CACHE_PATH=/opt/myapp/cache ./myapp
321
-
322
- # 3 - Both can be used together
323
- PKG_CACHE_PATH=/build/cache PKG_NATIVE_CACHE_PATH=/runtime/cache pkg app.js
108
+ npx @hakobu/hakobu . --target node24-macos-arm64 \
109
+ --sign-identity "Developer ID Application: ..." \
110
+ --notarize
324
111
  ```
325
112
 
326
- ## Usage of packaged app
327
-
328
- Command line call to packaged app `./app a b` is equivalent
329
- to `node app.js a b`
330
-
331
- ## Snapshot filesystem
332
-
333
- During packaging process `pkg` collects project files and places
334
- them into executable. It is called a snapshot. At run time the
335
- packaged application has access to snapshot filesystem where all
336
- that files reside.
337
-
338
- Packaged files have `/snapshot/` prefix in their paths (or
339
- `C:\snapshot\` in Windows). If you used `pkg /path/app.js` command line,
340
- then `__filename` value will be likely `/snapshot/path/app.js`
341
- at run time. `__dirname` will be `/snapshot/path` as well. Here is
342
- the comparison table of path-related values:
343
-
344
- | value | with `node` | packaged | comments |
345
- | ----------------------------- | --------------- | ------------------------ | ------------------------------ |
346
- | \_\_filename | /project/app.js | /snapshot/project/app.js |
347
- | \_\_dirname | /project | /snapshot/project |
348
- | process.cwd() | /project | /deploy | suppose the app is called ... |
349
- | process.execPath | /usr/bin/nodejs | /deploy/app-x64 | `app-x64` and run in `/deploy` |
350
- | process.argv[0] | /usr/bin/nodejs | /deploy/app-x64 |
351
- | process.argv[1] | /project/app.js | /snapshot/project/app.js |
352
- | process.pkg.entrypoint | undefined | /snapshot/project/app.js |
353
- | process.pkg.defaultEntrypoint | undefined | /snapshot/project/app.js |
354
- | require.main.filename | /project/app.js | /snapshot/project/app.js |
355
-
356
- Hence, in order to make use of a file collected at packaging
357
- time (`require` a javascript file or serve an asset) you should
358
- take `__filename`, `__dirname`, `process.pkg.defaultEntrypoint`
359
- or `require.main.filename` as a base for your path calculations.
360
- For javascript files you can just `require` or `require.resolve`
361
- because they use current `__dirname` by default. For assets use
362
- `path.join(__dirname, '../path/to/asset')`. Learn more about
363
- `path.join` in
364
- [Detecting assets in source code](#detecting-assets-in-source-code).
365
-
366
- On the other hand, in order to access real file system at run time
367
- (pick up a user's external javascript plugin, json configuration or
368
- even get a list of user's directory) you should take `process.cwd()`
369
- or `path.dirname(process.execPath)`.
370
-
371
- ## Detecting assets in source code
372
-
373
- When `pkg` encounters `path.join(__dirname, '../path/to/asset')`,
374
- it automatically packages the file specified as an asset. See
375
- [Assets](#assets). Pay attention that `path.join` must have two
376
- arguments and the last one must be a string literal.
377
-
378
- This way you may even avoid creating `pkg` config for your project.
379
-
380
- ## Native addons
381
-
382
- Native addons (`.node` files) use is supported. When `pkg` encounters
383
- a `.node` file in a `require` call, it will package this like an asset.
384
- In some cases (like with the `bindings` package), the module path is generated
385
- dynamically and `pkg` won't be able to detect it. In this case, you should
386
- add the `.node` file directly in the `assets` field in `package.json`.
387
-
388
- The way Node.js requires native addon is different from a classic JS
389
- file. It needs to have a file on disk to load it, but `pkg` only generates
390
- one file. To circumvent this, `pkg` will extract native addon files to
391
- `$HOME/.cache/pkg/` by default. These files will stay on the disk after the
392
- process has exited and will be used again on the next process launch.
393
-
394
- You can customize the cache directory by setting the `PKG_NATIVE_CACHE_PATH`
395
- environment variable. This is useful in enterprise environments where specific
396
- directories may be restricted or monitored:
113
+ ### macOS App Bundle
397
114
 
398
115
  ```bash
399
- # Set custom cache path for native addons
400
- PKG_NATIVE_CACHE_PATH=/opt/myapp/cache ./myapp
116
+ npx @hakobu/hakobu . --target node24-macos-arm64 \
117
+ --app-bundle --bundle-id com.example.my-app
401
118
  ```
402
119
 
403
- When a package, that contains a native module, is being installed,
404
- the native module is compiled against current system-wide Node.js
405
- version. Then, when you compile your project with `pkg`, pay attention
406
- to `--target` option. You should specify the same Node.js version
407
- as your system-wide Node.js to make compiled executable compatible
408
- with `.node` files.
409
-
410
- Note that fully static Node binaries are not capable of loading native
411
- bindings, so you may not use Node bindings with `linuxstatic`.
412
-
413
- ## API
414
-
415
- `const { exec } = require('pkg')`
416
-
417
- `exec(args)` takes an array of command line arguments and returns
418
- a promise. For example:
419
-
420
- ```js
421
- await exec(['app.js', '--target', 'host', '--output', 'app.exe']);
422
- // do something with app.exe, run, test, upload, deploy, etc
423
- ```
424
-
425
- ## ECMAScript Modules (ESM) Support
426
-
427
- Starting from version **6.13.0**, pkg has improved support for ECMAScript Modules (ESM). Most ESM features are now automatically transformed to CommonJS during the packaging process.
428
-
429
- ### Supported ESM Features
430
-
431
- The following ESM features are now supported and will work in your packaged executables:
432
-
433
- - **`import` and `export` statements** - Automatically transformed to `require()` and `module.exports`
434
- - **Top-level `await`** - Wrapped in an async IIFE to work in CommonJS context
435
- - **Top-level `for await...of`** - Wrapped in an async IIFE to work in CommonJS context
436
- - **`import.meta.url`** - Polyfilled to provide the file URL of the current module
437
- - **`import.meta.dirname`** - Polyfilled to provide the directory path (Node.js 20.11+ property)
438
- - **`import.meta.filename`** - Polyfilled to provide the file path (Node.js 20.11+ property)
439
-
440
- ### Known Limitations
441
-
442
- While most ESM features work, there are some limitations to be aware of:
443
-
444
- 1. **Modules with both top-level await and exports**: Modules that use `export` statements alongside top-level `await` cannot be wrapped in an async IIFE and will not be transformed to bytecode. These modules will be included as source code instead.
445
-
446
- 2. **`import.meta.main`** and other custom properties: Only the standard `import.meta` properties listed above are polyfilled. Custom properties added by your code or other tools may not work as expected.
447
-
448
- 3. **Dynamic imports**: `import()` expressions work but may have limitations depending on the module being imported.
449
-
450
- ### Best Practices
451
-
452
- - For entry point scripts (the main file you're packaging), feel free to use top-level await
453
- - For library modules that will be imported by other code, avoid using both exports and top-level await together
454
- - Test your packaged executable to ensure all ESM features work as expected in your specific use case
455
-
456
- ## Use custom Node.js binary
457
-
458
- In case you want to use custom node binary, you can set `PKG_NODE_PATH` environment variable to the path of the node binary you want to use and `pkg` will use it instead of the default one.
120
+ ### Windows PE Metadata
459
121
 
460
122
  ```bash
461
- PKG_NODE_PATH=/path/to/node pkg app.js
123
+ npx @hakobu/hakobu . --target node24-win-x64 \
124
+ --product-name "My App" \
125
+ --company-name "My Company" \
126
+ --file-version 1.0.0 \
127
+ --icon app.ico
462
128
  ```
463
129
 
464
- ## Troubleshooting
465
-
466
- ### Error: Error [ERR_REQUIRE_ESM]: require() of ES Module
467
-
468
- This error is tracked by issue [#16](https://github.com/yao-pkg/pkg/issues/16#issuecomment-1945486658). Follow the link in oder to find a workaround.
130
+ ### Linux AppDir / AppImage
469
131
 
470
- In most cases adding `--options experimental-require-module` to `pkg` command line will solve the issue.
471
-
472
- > [!NOTE]
473
- > This option is not needed anymore starting from NodeJS v22.12.0
474
-
475
- ### Error: Cannot find module XXX (when using `child_process`)
476
-
477
- When using `child_process` methods to run a new process pkg by default will invoke it using NodeJS runtime that is built into the executable. This means that if you are trying to spawn the packaged app itself you will get above error. In order to avoid this you must set `PKG_EXECPATH` env set to `""`:
478
-
479
- ```js
480
- const { spawn } = require('child_process');
132
+ ```bash
133
+ npx @hakobu/hakobu . --target node24-linux-x64 \
134
+ --appdir --desktop-name "My App"
481
135
 
482
- const child = spawn(process.execPath, [process.argv[1]], {
483
- env: {
484
- ...process.env,
485
- PKG_EXECPATH: '',
486
- },
487
- });
136
+ npx @hakobu/hakobu . --target node24-linux-x64 \
137
+ --appimage --desktop-name "My App"
488
138
  ```
489
139
 
490
- More info [here](https://github.com/yao-pkg/pkg/pull/90)
491
-
492
- ### Error: Cannot execute binaray from snapshot
493
-
494
- Binaries must be extracted from snapshot in order to be executed. In order to do this you can use this approach:
140
+ ## Programmatic API
495
141
 
496
- ```js
497
- const cp = require('child_process');
498
- const fs = require('fs');
499
- const { pipeline } = require('stream/promises');
142
+ ```typescript
143
+ import { exec } from '@hakobu/hakobu';
500
144
 
501
- let ffmpeg = require('@ffmpeg-installer/ffmpeg').path;
502
-
503
- const loadPlugin = async () => {
504
- if (process.pkg) {
505
- // copy ffmpeg to the current directory
506
- const file = fs.createWriteStream('ffmpeg');
507
- await pipeline(fs.createReadStream(ffmpeg), file);
508
-
509
- fs.chmodSync('ffmpeg', 0o755);
510
- console.log('ffmpeg copied to the current directory');
511
- ffmpeg = './ffmpeg';
512
- }
513
-
514
- cp.execSync(ffmpeg);
515
- };
516
-
517
- loadPlugin();
145
+ await exec(['.', '--target', 'node24-linux-x64', '--output', 'dist/app']);
518
146
  ```
519
147
 
520
- ### Error: ENOENT: no such file or directory, uv_chdir
521
-
522
- This error can be caused by deleting the directory the application is
523
- run from. Or, generally, deleting `process.cwd()` directory when the
524
- application is running.
525
-
526
- ### Error: ERR_INSPECTOR_NOT_AVAILABLE
527
-
528
- This error can be caused by using `NODE_OPTIONS` variable to force to
529
- run `node` with the debug mode enabled. Debugging options are disallowed
530
- , as **pkg** executables are usually used for production environments.
531
- If you do need to use inspector, you can [build a debuggable Node.js](https://github.com/vercel/pkg/issues/93#issuecomment-301210543) yourself.
532
-
533
- ### Error: require(...).internalModuleStat is not a function
534
-
535
- This error can be caused by using `NODE_OPTIONS` variable with some
536
- bootstrap or `node` options causing conflicts with **pkg**. Some
537
- IDEs, such as **VS Code**, may add this env variable automatically.
538
-
539
- You could check on **Unix systems** (Linux/macOS) in `bash`:
148
+ ## Migrating from @yao-pkg/pkg
540
149
 
541
150
  ```bash
542
- printenv | grep NODE
151
+ npm uninstall @yao-pkg/pkg
152
+ npm install @hakobu/hakobu --save-dev
543
153
  ```
544
154
 
545
- ## Advanced
546
-
547
- ### Exploring virtual file system embedded in debug mode
548
-
549
- When you are using the `--debug` flag when building your executable,
550
- `pkg` add the ability to display the content of the virtual file system
551
- and the symlink table on the console, when the application starts,
552
- providing that the environement variable DEBUG_PKG is set.
553
- This feature can be useful to inspect if symlinks are correctly handled,
554
- and check that all the required files for your application are properly
555
- incorporated to the final executable.
556
-
557
- pkg --debug app.js -o output
558
- DEBUG_PKG=1 output
559
-
560
- or
561
-
562
- C:\> pkg --debug app.js -o output.exe
563
- C:\> set DEBUG_PKG=1
564
- C:\> output.exe
565
-
566
- Note: make sure not to use --debug flag in production.
567
-
568
- ### Injecting Windows Executable Metadata After Packaging
569
-
570
- Executables created with `pkg` are based on a Node.js binary and, by default,
571
- inherit its embedded metadata – such as version number, product name, company
572
- name, icon, and description. This can be misleading or unpolished in
573
- distributed applications.
155
+ Rename the `"pkg"` field to `"hakobu"` in your `package.json`. The legacy `"pkg"` field still works with migration warnings.
574
156
 
575
- There are two ways to customize the metadata of the resulting `.exe`:
157
+ See the [Migration Guide](https://docs.hakobujs.dev/guides/migration-from-pkg) for full details.
576
158
 
577
- 1. **Use a custom Node.js binary** with your own metadata already embedded.
578
- See: [Use Custom Node.js Binary](#use-custom-nodejs-binary)
159
+ ## Documentation
579
160
 
580
- 2. **Post-process the generated executable** using
581
- [`resedit`](https://www.npmjs.com/package/resedit), a Node.js-compatible
582
- tool for modifying Windows executable resources. This allows injecting
583
- correct version info, icons, copyright,
584
- and more.
161
+ - [Getting Started](https://docs.hakobujs.dev)
162
+ - [Configuration Reference](https://docs.hakobujs.dev/configuration/package-json)
163
+ - [CLI Flags Reference](https://docs.hakobujs.dev/configuration/cli-flags)
164
+ - [Build Targets](https://docs.hakobujs.dev/build/targets)
165
+ - [Bundle Mode](https://docs.hakobujs.dev/build/bundle-mode)
166
+ - [Distribution & Signing](https://docs.hakobujs.dev/distribution/cross-platform)
167
+ - [Migration from pkg](https://docs.hakobujs.dev/guides/migration-from-pkg)
585
168
 
586
- This section focuses on the second approach: post-processing the packaged
587
- binary using [`resedit`](https://www.npmjs.com/package/resedit).
588
-
589
- > ⚠️ Other tools may corrupt the executable, resulting in runtime errors such as
590
- > `Pkg: Error reading from file.` –
591
- > [`resedit`](https://www.npmjs.com/package/resedit) has proven to work reliably
592
- > with `pkg`-generated binaries.
593
-
594
- Below is a working example for post-processing an `.exe` file using the Node.js API of [`resedit`](https://www.npmjs.com/package/resedit):
595
-
596
- ```ts
597
- import * as ResEdit from 'resedit';
598
- import * as fs from 'fs';
599
- import * as path from 'path';
600
-
601
- // Set your inputs:
602
- const exePath = 'dist/my-tool.exe'; // Path to the generated executable
603
- const outputPath = exePath; // Overwrite or use a different path
604
- const version = '1.2.3'; // Your application version
605
-
606
- const lang = 1033; // en-US
607
- const codepage = 1200; // Unicode
608
-
609
- const exeData = fs.readFileSync(exePath);
610
- const exe = ResEdit.NtExecutable.from(exeData);
611
- const res = ResEdit.NtExecutableResource.from(exe);
612
-
613
- const viList = ResEdit.Resource.VersionInfo.fromEntries(res.entries);
614
- const vi = viList[0];
615
-
616
- const [major, minor, patch] = version.split('.');
617
- vi.setFileVersion(Number(major), Number(minor), Number(patch), 0, lang);
618
- vi.setProductVersion(Number(major), Number(minor), Number(patch), 0, lang);
619
-
620
- vi.setStringValues(
621
- { lang, codepage },
622
- {
623
- FileDescription: 'ACME CLI Tool',
624
- ProductName: 'ACME Application',
625
- CompanyName: 'ACME Corporation',
626
- ProductVersion: version,
627
- FileVersion: version,
628
- OriginalFilename: path.basename(exePath),
629
- LegalCopyright: `© ${new Date().getFullYear()} ACME Corporation`,
630
- },
631
- );
632
-
633
- vi.outputToResourceEntries(res.entries);
634
- res.outputResource(exe);
635
- const newBinary = exe.generate();
636
-
637
- fs.writeFileSync(outputPath, Buffer.from(newBinary));
638
- ```
169
+ ## License
639
170
 
640
- As an alternative to the Node.js API,
641
- [`resedit`](https://www.npmjs.com/package/resedit) also supports command–line
642
- usage. This can be convenient for simple use cases in build scripts.
643
-
644
- The following command examples inject an icon and metadata into the executable
645
- `dist/bin/app_with_metadata.exe`, based on the original built file
646
- `dist/bin/app.exe`.
647
-
648
- - **Example (PowerShell on Windows)**
649
-
650
- ```powershell
651
- npx resedit dist/bin/app.exe dist/bin/app_with_metadata.exe `
652
- --icon 1,dist/favicon.ico `
653
- --company-name "ACME Corporation" `
654
- --file-description "ACME CLI Tool" `
655
- --product-name "ACME Application" `
656
- --file-version 1.2.3.4
657
- ```
658
-
659
- - **Example (bash on Linux/macOS)**
660
- ```bash
661
- npx resedit dist/bin/app.exe dist/bin/app_with_metadata.exe \
662
- --icon 1,dist/favicon.ico \
663
- --company-name "ACME Corporation" \
664
- --file-description "ACME CLI Tool" \
665
- --product-name "ACME Application" \
666
- --file-version 1.2.3.4
667
- ```
668
- > 💡 This is especially useful when cross-compiling Windows executables from
669
- > Linux or macOS using `pkg`.
670
-
671
- > 📚 **More information:** See the
672
- > [`resedit`](https://www.npmjs.com/package/resedit) package on npm for
673
- > additional examples and full API documentation.
171
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hakobu/hakobu",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Package your Node.js project into an executable",
5
5
  "main": "lib-es5/index.js",
6
6
  "license": "MIT",
@@ -41,7 +41,7 @@
41
41
  "tar": "^7.5.7",
42
42
  "tinyglobby": "^0.2.11",
43
43
  "unzipper": "^0.12.3",
44
- "@hakobu/hakobu-fetch": "1.0.0"
44
+ "@hakobu/hakobu-fetch": "1.0.1"
45
45
  },
46
46
  "optionalDependencies": {
47
47
  "rolldown": "^1.0.0-rc.1"
@@ -18,7 +18,7 @@ const Module = require('module');
18
18
  const path = require('path');
19
19
  const { promisify } = require('util');
20
20
  const { Script } = require('vm');
21
- const { homedir } = require('os');
21
+ const { homedir, tmpdir } = require('os');
22
22
  const util = require('util');
23
23
  const {
24
24
  brotliDecompress,
@@ -2186,7 +2186,8 @@ function payloadFileSync(pointer) {
2186
2186
  // Native addons will be extracted to: <base>/pkg/<hash>
2187
2187
  function resolveNativeCacheBase() {
2188
2188
  if (process.env.HAKOBU_ADDON_CACHE) return process.env.HAKOBU_ADDON_CACHE;
2189
- if (process.env.PKG_NATIVE_CACHE_PATH) return process.env.PKG_NATIVE_CACHE_PATH;
2189
+ if (process.env.PKG_NATIVE_CACHE_PATH)
2190
+ return process.env.PKG_NATIVE_CACHE_PATH;
2190
2191
  try {
2191
2192
  var home = homedir();
2192
2193
  if (home) {
@@ -2194,8 +2195,10 @@ function payloadFileSync(pointer) {
2194
2195
  fs.mkdirSync(cachePath, { recursive: true });
2195
2196
  return cachePath;
2196
2197
  }
2197
- } catch (e) { /* home unavailable or unwritable */ }
2198
- return os.tmpdir();
2198
+ } catch {
2199
+ /* home unavailable or unwritable */
2200
+ }
2201
+ return tmpdir();
2199
2202
  }
2200
2203
  const PKG_NATIVE_CACHE_BASE = resolveNativeCacheBase();
2201
2204