native-modules-plugin
API documentation for the native modules plugin
native-modules-plugin
Handles native Node.js addons (.node files) by copying them to the output directory and generating appropriate import statements.
Installation
The plugin is included with @visulima/packem-rollup:
npm install @visulima/packemUsage
import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'
export default defineConfig({
transformer,
rollup: {
nativeModules: {
nativesDirectory: 'natives'
}
}
})API Reference
nativeModulesPlugin
function nativeModulesPlugin(
options?: NativeModulesOptions
): PluginCreates a Rollup plugin that handles native Node.js addons.
Parameters
- options (
NativeModulesOptions?): Plugin configuration options
Returns
A Rollup plugin instance.
Options
NativeModulesOptions
interface NativeModulesOptions {
/**
* Custom subdirectory name for native modules within the output directory
* @default 'natives'
*/
nativesDirectory?: string
}nativesDirectory
Type: string
Default: 'natives'
The subdirectory name within the output directory where native modules will be copied. This directory will be created automatically.
Example:
nativeModules: {
nativesDirectory: 'native-addons'
}How It Works
The native modules plugin operates in two stages:
Stage 1: Resolve and Load (Build Time)
- Detection: Identifies
.nodefile imports in your code - Resolution: Resolves the file paths and validates accessibility
- Registration: Registers native modules for copying
- Virtual Module: Creates virtual module IDs for each native module
Stage 2: Bundle Generation (Bundle Time)
- Directory Creation: Creates the natives directory in the output
- File Copying: Copies all registered
.nodefiles in parallel - Import Generation: Generates appropriate
require()statements
Examples
Basic Usage
// src/index.ts
import myAddon from './my-addon.node'
export function useAddon() {
return myAddon.someFunction()
}Output:
// dist/index.js
const myAddon = require("./natives/my-addon.node")
export function useAddon() {
return myAddon.someFunction()
}Custom Directory
export default defineConfig({
transformer,
rollup: {
nativeModules: {
nativesDirectory: 'addons'
}
}
})Output:
const myAddon = require("./addons/my-addon.node")Multiple Native Modules
// src/index.ts
import addon1 from './addon1.node'
import addon2 from './addon2.node'
import addon3 from './addon3.node'
export { addon1, addon2, addon3 }Output:
const addon1 = require("./natives/addon1.node")
const addon2 = require("./natives/addon2.node")
const addon3 = require("./natives/addon3.node")
export { addon1, addon2, addon3 }File Structure
project/
├── src/
│ ├── index.ts
│ └── my-addon.node
├── dist/
│ ├── index.js
│ └── natives/
│ └── my-addon.node # Copied here
└── package.jsonImport Patterns
The plugin supports various import patterns for native modules:
Default Import
import myAddon from './my-addon.node'Named Import
import { someFunction } from './my-addon.node'Dynamic Import
const myAddon = await import('./my-addon.node')Error Handling
The plugin provides clear error messages for common issues:
File Not Found
Native module not found: /path/to/missing.nodeOutput Directory Not Configured
Output directory not detected. Please ensure Rollup output options are configured.Module Registration Failed
Could not find staged native module for: /path/to/module.nodePlatform Considerations
Cross-Platform Builds
Native modules are platform-specific. When building for multiple platforms:
- Separate builds: Create separate builds for each target platform
- Conditional loading: Use dynamic imports with platform detection
- Optional dependencies: Mark platform-specific modules as optional
Platform-Specific Imports
// src/native.ts
const isWindows = process.platform === 'win32'
const isMac = process.platform === 'darwin'
const isLinux = process.platform === 'linux'
let nativeModule
if (isWindows) {
nativeModule = await import('./native.win32.node')
} else if (isMac) {
nativeModule = await import('./native.darwin.node')
} else if (isLinux) {
nativeModule = await import('./native.linux.node')
}
export default nativeModulePerformance Considerations
Bundle Size
- Native modules are copied as-is to the output directory
- File size impacts distribution size but not bundle parsing time
- Consider compression for large native modules
Load Time
- Native modules load faster than JavaScript bundles
- No JavaScript parsing overhead for native code
- Platform-specific optimization opportunities
Troubleshooting
Module Not Copied
- Verify import syntax: Use relative imports for
.nodefiles - Check file existence: Ensure the
.nodefile exists at build time - Validate path resolution: Confirm relative paths are correct
Import Errors
- Output directory: Ensure the output directory is properly configured
- File permissions: Check read/write permissions for native module files
- Platform compatibility: Verify the native module matches the target platform
Build Failures
- Clean build: Remove
dist/and rebuild - Dependency order: Ensure native modules are built before bundling
- File watchers: Restart file watchers after adding new native modules
Integration Examples
With Electron
// electron/main.ts
import { app, BrowserWindow } from 'electron'
import nativeAddon from './native-addon.node'
app.whenReady().then(() => {
// Use native addon in main process
const result = nativeAddon.initialize()
const mainWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: true
}
})
})With N-API
// C++ addon with N-API
#include <napi.h>
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set("version", Napi::String::New(env, "1.0.0"));
return exports;
}
NODE_API_MODULE(addon, Init)// TypeScript usage
import addon from './addon.node'
console.log('Addon version:', addon.version)Related APIs
defineConfig- Main configuration functionPackemConfig- Configuration interfacePackemRollupOptions- Rollup configuration options
See Also
- Native Addons Guide - Node.js native addons documentation
- N-API Guide - N-API documentation
- Electron Native Modules - Electron-specific guide