β‘ A full-fledged Node.js runtime for Capacitor apps.
βΉοΈ This project uses the Node.js for Mobile Apps toolkit to add NodeJS support in Android and IOS
β WIP - Work in Progress β
The project is part of my diploma thesis, an overview can be found at hampoelz/HTL_Diplomarbeit.
β° Planed Deadline: September 2023
Notes:
- The project is still very unstable, if you have any problems or suggestions it would be nice if you create an issue.
- When the project is stable it will be published on NPM.
- Features like IOS support will be added in the future.
- The node.js version used in this project depends on the Node.js for Mobile Apps toolkit.
- Android
- IOS (coming soon)
- Using the
capacitor-community/electron
plugin:- Windows
- Linux
- macOS
You've to use Capacitor v3 or newer. This project isn't compatible with lower versions of Capacitor.
npm install https://github.com/hampoelz/capacitor-nodejs/releases/download/v1.0.0-beta.2/capacitor-nodejs.tgz
npx cap sync
β Important
For now Android 32-bit x86 support is disabled in Capacitor-NodeJS v1.0.0-beta.2 (based on node.js v16) as there is currently no support for it in the latest version of the nodejs-mobile core library. However, you can use Capacitor-NodeJS v1.0.0-beta.1 which is based on node.js v12.
Currently there are two example projects available. One without any additional framework, located in the example/vanilla
branch. And one that uses the vitejs framework in example/vite
.
To add a NodeJS project to your app, the following steps are required:
- Create a new directory called
nodejs
inside your app's source/public directory (this is usually thesrc
folder or if you use a build system thepublic
folder). Thenodejs
dir will serve as your NodeJS project folder. (modules can be installed later in this folder) - Create a
package.json
file in it as the starting point of the NodeJS integration:
{
"name": "capacitor-node-project",
"version": "1.0.0",
"description": "node part of the project",
"main": "main.js",
"author": "hampoelz",
"license": "MIT",
"dependencies": {
"bridge": "file:../../node_modules/capacitor-nodejs/assets/builtin_modules/bridge"
}
}
- Create the main script of the NodeJS integration (in this case
main.js
), which could look like this:
const { channel } = require('bridge');
channel.addListener('msg-from-capacitor', message => {
console.log('[node] Message from Capacitor code: ' + message);
channel.send("msg-from-nodejs", "Replying to this message: " + message, "And optionally add further args");
});
- Run
npm install
in your newly created NodeJS project folder.
After that, the project structure should look something like this:
my-capacitor-app/
βββ ...
βββ src/ # app source directory
β βββ ...
β βββ nodejs/ # NodeJS project directory
β β βββ node_modules/
β β βββ main.js # main script of the NodeJS integration
β β βββ package.json # starting point of the NodeJS integration
β βββ ...
β βββ index.html
βββ capacitor.config.json
βββ package.json
βββ README.md
βββ ...
Now you can communicate with the NodeJS layer in your CapacitorJS app:
import { NodeJS } from 'capacitor-nodejs';
//const NodeJS = Capacitor.Plugins.NodeJS;
// Listens to "msg-from-nodejs" from the NodeJS process.
NodeJS.addListener('msg-from-nodejs', event => {
document.body.innerHTML = `<p>First argument: ${event.args[0]}<br>Second argument: ${event.args[1]}</p>`
console.log(event);
});
// Wait for the NodeJS process to initialize.
NodeJS.whenReady().then(() => {
// Send a message to the NodeJS process.
NodeJS.send({
eventName: "msg-from-capacitor",
args: [ "Hello from Capacitor!" ]
});
});
β Important
If you use a build system for your app, make sure to add the NodeJS project directory to you static assets. (Or copy the nodejs dir to the output directory, for example by adding
cp -r src/nodejs dist/
to your build steps.)
βΉοΈ Information
If you are using the
capacitor-community/electron
plugin, packaging with the electron-builder may cause problems since it does not include the modules from the nodejs project by default.To fix this issue, add
"includeSubNodeModules": true
to yourelectron-builder.config.json
.
You can customize the NodeJS project directory. By default, it is a folder named nodejs
in your app's source directory. But it can be changed in the capacitor.config.json
file.
{
"plugins": {
"NodeJS": {
"nodeDir": "custom-nodejs"
}
}
}
For example, if you change it to custom-nodejs
, then your project structure should look something like this:
my-capacitor-app/
βββ ...
βββ src/ # app source directory
β βββ ...
β βββ custom-nodejs/ # the new NodeJS project directory
β β βββ node_modules/
β β βββ main.js
β β βββ package.json
β βββ ...
β βββ index.html
βββ capacitor.config.json
βββ package.json
βββ README.md
βββ ...
Node modules can be added to the project using npm. The Node modules have to be installed in the NodeJS project folder in which the package.json
file was created.
Go to the NodeJS project folder and proceed with the installation of the Node modules you want to add to your Node.js project.
Sync and rebuild your Capacitor project so that the newly added Node modules are added to the application.
On Android, the plugin extracts the project files and the Node modules from the APK assets in order to make them available to the Node.js for Mobile Apps engine. They are extracted from the APK and copied to a working folder (context.getFilesDir().getAbsolutePath() + "/public/<nodeDir>"
where <nodeDir>
is the NodeJS project folder configured in the capacitor.config.json
file. If there is no configuration, the <nodeDir>
can be omitted in the path) when the application is launched for the first time or a new version of the application has been installed.
β οΈ WarningGiven the project folder will be overwritten after each application update, it should not be used for persistent data storage.
You may want to add a gitignore file to ignore unnecessary files. To do this, create a new file named .gitignore
in the NodeJS project folder and copy the contents of github.com/github/gitignore/blob/master/Node.gitignore into it.
The channel
module is an Event Emitter. It provides a few methods so you can send messages from the NodeJS process to the capacitor layer. You can also receive replies from the capacitor layer.
It has the following method to listen for events and send messages:
eventName
Stringlistener
Function...args
any[]
Listens to eventName
, when a new message arrives listener
would be called with
listener(args...)
.
eventName
Stringlistener
Function...args
any[]
Adds a one time listener
function for the event. This listener
is invoked
only the next time a message is sent to eventName
, after which it is removed.
eventName
Stringlistener
Function...args
any[]
Alias for channel.on(eventName, listener)
.
eventName
Stringlistener
Function...args
any[]
Removes the specified listener
from the listener array for the specified
eventName
.
eventName
String
Removes all listeners, or those of the specified eventName
.
eventName
String...args
any[]
Send a message to the capacitor layer via eventName
, along with
arguments. Arguments will be serialized with JSON.
The NodeJS
module is the API you use in your Capacitor app. It provides a few methods so you can send messages from the NodeJS layer and wait for them.
send(...)
addListener(string, ...)
whenReady()
removeListener(...)
removeAllListeners()
- Interfaces
- Type Aliases
send(options: MessageOptions) => Promise<{ value: boolean; }>
Send a message to the NodeJS process.
Param | Type |
---|---|
options |
MessageOptions |
Returns: Promise<{ value: boolean; }>
Since: 1.0.0
addListener(eventName: string, listenerFunc: ChannelListener) => Promise<PluginListenerHandle> & PluginListenerHandle
Listens to eventName
, when a new message arrives listenerFunc
from the NodeJS process would be called with listenerFunc(event)
.
Note: When using the electron platform, listenerHandle.remove()
does not work due to limitations. Use removeListener(listenerFunc)
instead.
Param | Type |
---|---|
eventName |
string |
listenerFunc |
ChannelListener |
Returns: Promise<PluginListenerHandle> & PluginListenerHandle
Since: 1.0.0
whenReady() => Promise<void>
Fulfilled when the NodeJS process is initialized.
Since: 1.0.0
removeListener(listenerHandle: Promise<PluginListenerHandle> | PluginListenerHandle) => Promise<void>
Remove the listenerFunc
of the specified listenerHandle
from the listener array for the event named eventName
.
Param | Type |
---|---|
listenerHandle |
PluginListenerHandle | Promise<PluginListenerHandle> |
Since: 1.0.0
removeAllListeners() => Promise<void>
Remove all listeners for this plugin.
Note: When using the electron platform, this method does not work! (will be solved by capacitor-community/electron#185)
Since: 1.0.0
Options to send a message to the NodeJS process via eventName
, along with
arguments. Arguments will be serialized with JSON.
Prop | Type | Description | Since |
---|---|---|---|
eventName |
string |
The name of the event being send to | 1.0.0 |
args |
any[] |
Array of arguments to send | 1.0.0 |
Prop | Type |
---|---|
remove |
() => Promise<void> |
The event object when a message from the NodeJS process arrives.
Prop | Type | Description | Since |
---|---|---|---|
args |
any[] |
Received array of arguments | 1.0.0 |
The callback function when listen to messages from the NodeJS process.
(event: ChannelListenerEvent): void