npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

executable-wrapper

v1.5.0

Published

Universal packaging tool that converts Node.js projects into native executables for macOS (.app/.pkg) and Windows (.exe)

Readme

Executable Wrapper

A universal Node.js application packager that creates signed and notarized executables for macOS and Windows.

🎯 Features

  • Cross-platform - Build for macOS (x64, arm64, universal) and Windows
  • Code Signing - Automatic signing with Developer ID certificates
  • Notarization - Apple notarization with automatic stapling
  • Native Addons - Full support for native Node.js modules (usb, node-hid, etc.)
  • Universal Binaries - True universal macOS apps and PKG installers supporting both Intel and Apple Silicon
  • Terminal Control - Optional --terminal parameter to force terminal execution in macOS apps
  • Zero Configuration - Single .env file drives the entire build process
  • Production Ready - Apps pass Gatekeeper and can be distributed immediately
  • NPM Package Ready - Works seamlessly as an npm dependency with proper path resolution

📦 What It Does

This wrapper project packages any Node.js application into native executables:

  • macOS: .app bundles and .pkg installers (including universal binaries)
  • Windows: .exe executables

No need to integrate build scripts into your project - just provide a .env configuration file and the wrapper handles everything.

🚀 Quick Start

1. Install Dependencies

cd executable-wrapper
npm install

2. Prepare Your Project

Your Node.js project structure:

my-project/
├── dist/
│   └── index.js          # Your bundled application
├── resources/
│   ├── appIcon.icns     # macOS icon (optional)
│   └── appIcon.ico      # Windows icon (optional)
├── pkg.json             # pkg configuration (optional, for native modules)
└── .env                 # Wrapper configuration

3. Create .env Configuration

Create a .env file in your project:

# Application Info
APP_NAME=my-app
BUNDLE_ID=com.company.myapp
APP_DISPLAY_NAME=My Application
APP_VERSION=1.0.0

# Build Configuration
JS_ENTRY_POINT=dist/index.js
PKG_CONFIG=pkg.json

# Resources
ICON_MACOS=resources/appIcon.icns
ICON_WINDOWS=resources/appIcon.ico

# macOS Signing & Notarization (required for --sign and --notarize)
APPLE_TEAM_NAME="Your Company Name"
APPLE_TEAM_ID=YOUR_TEAM_ID
[email protected]
APPLE_ID_PASSWORD=xxxx-xxxx-xxxx-xxxx

# macOS Configuration
INSTALL_LOCATION=/Applications/Utilities
MACOS_PERMISSIONS=network.client,device.usb

# Company Info
COMPANY_NAME=Your Company
COPYRIGHT=Copyright © 2024 Your Company

See template.env for a complete configuration example.

4. Build Your Application

cd executable-wrapper

# macOS Universal (both Intel and Apple Silicon)
node index.js --env ../my-project/.env --platform macos-universal --format app

# macOS with signing and notarization
node index.js --env ../my-project/.env --platform macos-universal --format app --sign --notarize

# Windows
node index.js --env ../my-project/.env --platform win-x64

# macOS universal installer (.pkg) - supports both Intel and Apple Silicon
node index.js --env ../my-project/.env --platform macos-universal --format pkg --sign

5. Find Your Executable

Output will be in your project's executable-wrapper-app-releases/ directory:

my-project/executable-wrapper-app-releases/
├── my-app-1_0_0-macos-universal.app
├── my-app-1_0_0-macos-universal.pkg
└── my-app-1_0_0-win-x64.exe

📋 CLI Options

node index.js [options]

Options:
  --env <path>        Path to .env configuration file (required)
  --platform <type>   Target platform:
                      - macos-x64 (Intel Mac)
                      - macos-arm64 (Apple Silicon)
                      - macos-universal (Universal binary)
                      - win-x64 (Windows)
  --format <type>     Output format:
                      - executable (binary only)
                      - app (macOS .app bundle)
                      - pkg (macOS installer)
  --sign              Sign the application (requires credentials in .env)
  --notarize          Notarize the application (implies --sign, macOS only)
  --terminal-script   Generate wrapper script that shows terminal output (macOS only)
  --output <path>     Custom output directory (optional)
  --help, -h          Show help
  --version, -v       Show version

🔐 Code Signing & Notarization

macOS Requirements

For --sign and --notarize flags, you need:

  1. Apple Developer Account with Developer ID certificates
  2. Certificates installed in Keychain:
    • Developer ID Application (for .app)
    • Developer ID Installer (for .pkg)
  3. App-Specific Password for notarization (generate at https://appleid.apple.com/account/manage)

Add these to your .env:

APPLE_TEAM_NAME="Your Company Name"
APPLE_TEAM_ID=ABC1234XYZ
[email protected]
APPLE_ID_PASSWORD=xxxx-xxxx-xxxx-xxxx

What Happens During Signing & Notarization

  1. Signing - Code signs the .app with your Developer ID
  2. Notarization - Submits to Apple's notarization service
  3. Stapling - Attaches the notarization ticket to the .app
  4. Verification - Validates the signature and notarization

Result: Your app passes Gatekeeper and can be distributed without warnings.

🔧 Native Modules

If your app uses native Node.js modules (like usb, node-hid, serialport), create a pkg.json in your project root:

{
  "assets": [
    "node_modules/usb/prebuilds/**/*.node",
    "node_modules/node-hid/prebuilds/**/*.node"
  ]
}

Important: Asset paths in pkg.json are resolved relative to your project root directory (where your .env file is located), not relative to the JS entry point.

Reference it in your .env:

PKG_CONFIG=pkg.json

Verifying Native Module Packaging

To ensure native modules are properly included:

  1. Place pkg.json in your project root (same directory as .env)
  2. Use paths relative to the project root (e.g., node_modules/*/prebuilds/**/*.node)
  3. Check that the prebuilt binaries exist in your node_modules directory

🎨 Application Icons

macOS (.icns)

Create a .icns file with multiple resolutions:

  • 16x16, 32x32, 64x64, 128x128, 256x256, 512x512, 1024x1024

Tools: Icon Composer, iconutil, or online converters.

Windows (.ico)

Create a .ico file with multiple resolutions:

  • 16x16, 32x32, 48x48, 64x64, 128x128, 256x256

Tools: GIMP, ImageMagick, or online converters.

🏗️ Project Structure

executable-wrapper/
├── index.js                    # Main entry point
├── lib/
│   ├── config.js              # Configuration management
│   ├── builder.js             # Executable builder (pkg)
│   ├── macos/
│   │   ├── app-builder.js     # .app bundle creator
│   │   ├── pkg-builder.js     # .pkg installer creator
│   │   ├── universal-builder.js # Universal binary builder
│   │   ├── plist-generator.js # Info.plist generator
│   │   ├── entitlements-generator.js # Entitlements generator
│   │   ├── signer.js          # Code signing
│   │   └── notarizer.js       # Notarization & stapling
│   └── windows/
│       └── signer.js          # Windows signing
├── mac/
│   └── app.entitlements.example # Example entitlements file
├── template.env               # Configuration template
├── package.json
└── README.md

📚 Configuration Reference

Required Fields

APP_NAME=my-app                              # Application name (lowercase, no spaces)
BUNDLE_ID=com.company.app                    # macOS bundle identifier
APP_DISPLAY_NAME=My Application              # Display name (user-facing)
APP_VERSION=1.0.0                           # Version number
JS_ENTRY_POINT=dist/index.js                # Path to your bundled JS file

Optional Fields

# Resources
ICON_MACOS=resources/appIcon.icns           # macOS icon
ICON_WINDOWS=resources/appIcon.ico          # Windows icon
PKG_CONFIG=pkg.json                         # pkg configuration file

# macOS Permissions (comma-separated)
MACOS_PERMISSIONS=network.client,device.usb

# Custom Entitlements
ENTITLEMENTS_PATH=mac/custom.entitlements   # Override default entitlements

# Company Info
COMPANY_NAME=My Company
COPYRIGHT=Copyright © 2024 My Company

# macOS Installer
INSTALL_LOCATION=/Applications/Utilities     # .pkg install location

macOS Permissions

Available permissions for MACOS_PERMISSIONS:

  • network.client - Outgoing network connections
  • network.server - Incoming network connections
  • device.usb - USB device access
  • device.bluetooth - Bluetooth access
  • files.user-selected.read-write - File access via dialogs
  • files.downloads.read-write - Downloads folder access

🖥️ Terminal Control (macOS)

For macOS applications, you can control the wrapper script behavior at build time:

Build-Time Control

Use the --terminal-script parameter during build to generate different wrapper scripts:

# Default: Background execution (no terminal popup)
node index.js --env ../myapp/.env --platform macos-universal --format app

# Terminal script: Always shows terminal output
node index.js --env ../myapp/.env --platform macos-universal --format app --terminal-script

Wrapper Script Types

Default Script (Background Execution)

  • From Terminal: Runs in foreground (shows output)
  • From Finder/GUI: Runs in background (no terminal popup)
  • Use case: Regular applications that don't need terminal output

Terminal Script (Always Shows Terminal)

  • From Terminal: Runs in foreground (shows output)
  • From Finder/GUI: Opens Terminal and runs in foreground (shows output)
  • Use case: CLI tools, debugging, interactive applications

Use Cases

  • Debugging: See real-time output and error messages
  • Interactive Apps: CLI tools that need user input
  • Development: Testing and troubleshooting
  • Logging: Monitor application behavior

🔍 Troubleshooting

"command not found: pkg"

Solution: Run npm install in the wrapper project.

"Missing signing configuration"

Solution: Add APPLE_TEAM_NAME and APPLE_TEAM_ID to your .env.

"Missing notarization configuration"

Solution: Add APPLE_ID and APPLE_ID_PASSWORD to your .env.

Notarization fails

  • Check your Apple ID and app-specific password
  • Ensure certificates are installed and valid
  • Check Apple's notarization logs for specific errors

Native modules not working

  • Ensure pkg.json is in your project root directory (same location as .env)
  • Asset paths must be relative to the project root, not the entry point directory
  • Check that prebuilt binaries exist in node_modules
  • Verify the module supports your target platform
  • Example: If your entry point is dist/index.js, paths like node_modules/usb/**/*.node will resolve from the project root, not from dist/

📖 Examples

Example usage with the test-app project can be found in the neighboring test-app directory.

📄 License

MIT License - See package.json for details.

Ensure you have appropriate licenses and certificates for code signing when distributing applications.

🤝 Contributing

This is a wrapper tool designed for internal use. For issues or improvements, please contact the maintainer.

📝 Changelog

v1.3.1 (2025-10-09)

  • Improved: More robust pkg execution with explicit lib-es5/bin.js resolution and clearer logging
  • Stability: Native module rebuild failures no longer fail the overall build, only warn

v1.3.0 (2025-01-27)

  • Added: Universal PKG support for macOS installers
  • Added: Automatic native module rebuilding for target platforms
  • Improved: Enhanced universal build capabilities with better architecture detection

v1.2.0 (2025-10-09)

  • Fixed: pkg assets path resolution now uses project root instead of entry point directory
  • Improved: Native modules packaging for projects with JS entry points in subdirectories
  • This ensures pkg.json asset paths are correctly resolved from the project root

See CHANGELOG.md for full version history.


Version: 1.3.1
Last Updated: 2025-10-09