Getting Started
Welcome to Commoners! This guide will help you get started with building your first cross-platform application.
Since Commoners is built on top of Vite, you can use the create-vite
package to scaffold a new project. To do this, run the following command in your terminal:
npm create vite@latest my-commoners-app
Follow the prompts to select your favorite framework and features.
Installing commoners
After running npm install
, add Commoners as a dependency:
npm install -D commoners@0.0.59
Configuring the package.json
File
Then, modify scripts
in your package.json
to include the following:
{
"scripts": {
"start": "commoners --target desktop",
"dev": "commoners",
"build": "commoners build"
}
}
Now you have simple commands to start your application as an Electron desktop application (npm start
), start as a web application (npm run dev
), and build different distributions of your application (npm run build
).
Creating a Configuration File
commoners
allows you to customize your application by adding a commoners.config.js
file to the root of your project. This file can be used to configure your application's services, plugins, and more.
For example, you can add the following to your commoners.config.js
file to customize your application's name and icon:
export default {
name: 'My App',
icon: './assets/vite.png', // Manually converted from public/vite.svg
}
All of the available configuration options are documented in the Configuration documentation.
In your built application, you can access Commoners configuration values using the commoners
object:
console.log(commoners) // { NAME: 'My App', VERSION: '0.0.0', ICON: '<path>', DESKTOP: true, READY: Promise, SERVICES: { ... } }
Try replacing the default h1
and img
tags with your custom NAME
and ICON
using the commoners
global variable!
Adding Services
To add services to your Commoners application in development, simply add fill out the services
property of the configuration file.
export default {
// ...
services: {
node: './src/services/node.js',
tsNode: './src/services/node.ts',
python: './src/services/python.py',
}
}
We currently support JavaScript, TypeScript, and Python services.
- JavaScript / TypeScript: Reference a
.js
or.ts
file to run using Node.js. - Python: Reference a
.py
file to run using Python.- Ensure that you're running a virtual environment with the necessary dependencies. We recommend using miniconda and distributing an
environment.yml
file with your project.
- Ensure that you're running a virtual environment with the necessary dependencies. We recommend using miniconda and distributing an
For all services, you'll want to ensure that CORS is enabled to support communication between the service and the application.
Example Services
JavaScript / TypeScript
Here is an example HTTP server written in TypeScript.
import http from 'node:http';
const host = process.env.HOST
const port = process.env.PORT
const server = http.createServer((
req: http.IncomingMessage,
res: http.ServerResponse
) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.setHeader('Access-Control-Allow-Credentials', 'true');
// Echo Request
if (req.method === 'POST') {
let body = '';
req.on('data', (chunk) => body += chunk.toString());
req.on('end', () => {
res.writeHead(200, { 'Content-Type': req.headers['content-type'] });
res.end(body);
});
return;
}
// Default Response
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
return;
});
server.listen(port,
() => console.log(`Server running at http://${host}:${port}/`)
);
Python
Here is an example HTTP server written in Python.
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
def _set_default_headers(self):
self.send_response(200)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE')
self.send_header('Access-Control-Allow-Headers', 'X-Requested-With,content-type')
# Hello World
def do_GET(self):
self._set_default_headers()
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(b'Hello World')
# Echo
def do_POST(self):
self._set_default_headers()
self.send_header('Content-type', self.headers['Content-Type'])
self.end_headers()
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
self.wfile.write(post_data)
PORT = int(os.getenv('PORT', 8000))
HOST = os.getenv('HOST', '')
server_address = (HOST, PORT)
httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
httpd.serve_forever()
Using Services
To use services in your application, you can import them using the commoners
global variable:
const { SERVICES } = commoners
const nodeServiceUrl = SERVICES.node.url
const tsNodeServiceUrl = SERVICES.tsNode.url
const pythonServiceUrl = SERVICES.python.url
Use the following code snippet and update the button to fetch data from your services!
const responses = await Promise.allSettled(Object.values(SERVICES).map(({ url }) => fetch(url).then(response => response.text())))
Building for Production
Application
To build your application for production, run the following command:
commoners build --target [platform]
Services
Service builds are generally deferred to the user.
commoners build --service node --service tsNode # Specified services are built
In the case of desktop builds, all relevant services are bundled by default.
commoners build --target desktop
Configuration
JavaScript / TypeScript
JavaScript and TypeScript services are auto-bundled using esbuild and pkg.
To package these for distribution and/or indicate they'll be hosted on a remote server, you'll add the following configuration:
export default {
// ...
services: {
// Simple configuration
node: './src/services/node.js', // Only included with Desktop builds
// Advanced configuration
tsNode: {
src: './src/services/node.ts',
// publish: false, // Removed on all builds
// publish: 'https://ts-node.example.com', // Remote for all builds
publish: { local: 'https://ts-node.example.com' }, // Remote for local builds (desktop). Removed on remote builds (web, mobile)
// publish: { remote: 'https://ts-node.example.com' }, // Remote for remote builds (web, mobile). Local for local builds (desktop)
// publish: { url: 'https://ts-node.example.com', local: false }, // Remote for remote builds (web, mobile). Removed on local builds (desktop)
}
}
}
Python
For Python services, you'll to specify a terminal command that will bundle the service into a standalone executable file. We recommend using pyinstaller
for this purpose.
export default {
// ...
services: {
python: {
src: './src/services/python.py',
publish: { remote: 'https://python.example.com' },
// The build command
build: 'python -m PyInstaller --name python-service --onedir --clean ./src/services/python.py --distpath ./build/python',
// What to include from the build
publish: {
remote: 'https://python.example.com',
local: {
base: './build/python/python-service', // The base directory to copy
src: 'python-service', // The relative source location in the base directory
}
}
}
}
}