annotatron
Set of helper annotations for electron projects inspired by the Angular module decorators.

Template projects
You can create a new Electron project with annotatron from one of two templates by running the following command.
npx create-annotatron-app {template} {name}
where
- template: is one of the values (vue, react)
- name: name of your next awesome project
Usage
ApplicationModule setup
Create a module class for your electron application and use the @ElectronModule decorator to define:
- dependencies with other modules via
imports parameter.
- direct dependencies to other classes via
providers parameter.
import * as electron from 'electron';
import { ElectronModule } from 'annotatron';
import { MySubModule } from './path/to/my-sub-module.ts';
import { MyService } from './path/to/my-service.ts';
import { MyDataBaseApi } from './path/to/my-database-api.ts';
import { MyDataBaseApiMongoImplementation } from './path/to/my-database-api-mongo-implementation.ts';
@ElectronModule({
imports: [MySubModule]
providers: [
MyProvider,
{
provide: MyDataBaseApi,
useClass: MyDataBaseApiMongoImplementation
},
],
})
export class MyElectronApplicationModule {
}
After doing this you are now capable of bootstrapping the application using bootstrapModule method. Usually this is done in the index file. You may want to use connectWindow method to allow your app windows(renderer processes) to receive events from the main process.
NOTE: events are messages emitted without the need to respond to a query or a command. They are useful to notify something that is happening on the system
import { ipcMain } from 'electron';
import { bootstrapModule, connectWindow } from 'annotatron'
import { MyElectronApplicationModule } from './app';
bootstrapModule(MyElectronApplicationModule, ipcMain);
Listening in the main process
Providers are classes that will be injected into the application and will listen to messages from browser windows and other providers. A provider can listen to Commands, Queries and Events using the right decorators. Those decorators require a parameter which is the type of command/query/event they are listening to.
import { Injectable, Command, Query, Event } from 'annotatron';
@Injectable()
export class MyProvider {
@Command('commandType')
commandHandler(commandPayload: any): any {
}
@Query('queryType')
queryHandler(queryPayload: any): any {
}
@Event('eventType')
eventHandler(eventPayload: any): void {
}
}
The annotations expect that messages for commands, queries and events to have the following interface.
interface CommandQueryOrEvent {
type: string;
payload: unknown;
}
Emitting events
Whenever you want to emit an event to let other components know that something happened you may use the emitEvent method. This will broadcast to the main process and all the windows connected to the main process using the connectWindow api.
import { emitEvent } from 'annotatron';
export class MyClass {
method(command: any): any {
const payload = some_logic();
emitEvent({ type: 'event-type', payload });
}
}
Emitting commands and queries from a window
Commands and Queries are meant to be fired from browser windows to the main process. Also this lib is meant to work with windows with context isolation enabled and with the remote module disabled. Therefore you must provide a preload script to create a communication bridge.
- Commands must be sent to the ipcMain process using
annotatron:commands channel.
- Queries must be sent to the ipcMain process using
annotatron:queries channel.
- Command/Queries results are sent back to the ipcRender through the
annotatron:results channel.
- Command/Queries errors are sent back to the ipcRender through the
annotatron:errors channel.
- Events are sent to browser windows through the
annotatron:events channel.
- remember events are also propagated within the ipcMain process so app providers can listen to them via the
@Event annotation.
A sample preload script could be:
const { contextBridge, ipcRenderer } = require('electron');
const observableLike = (key) => {
return {
subscribe: (observer) => {
const ipcHandler = (evt, payload) => observer(payload);
ipcRenderer.on(`annotatron:${key}`, ipcHandler);
return {
unsubscribe: function() {
ipcRenderer.removeListener(`annotatron:${key}`, ipcHandler);
}
};
}
};
};
contextBridge.exposeInMainWorld(
'mainProcess',
{
sendCommand: (command) => ipcRenderer.send(`annotatron:commands`, command),
sendQuery : (query) => ipcRenderer.send(`annotatron:queries` , query),
results$ : observableLike('results'),
errors$ : observableLike('errors'),
events$ : observableLike('events'),
}
);
Point to that file in the preload option when creating a window and you and your renderer process (the UI) will have a global property named mainProcess which has all the tools for communicating with the main process.
Release notes
[0.1.0]
In this release we replaced our injection mechanism for injection-js package bringing the power of Angular's Reflective Injector.
The API hasn't changed but extended:
- adding
useFactory providers
- adding
useValue providers
InjectionToken is also available
For full API information check Angular's DO docs
[0.0.11]
- Overriden providers with
useClass were returning different instances.
[0.0.10]
- Messaging simplified. No need to wrap Command/Query in an array.
- fix methods with @Event annotation not being called
[0.0.9]
- fix resolver of
useClass provider
[0.0.8]
- add more checks in module bootstrapping
[0.0.6]
- add new provider type with useClass
[0.0.5]
- fix problem in package publishing
[0.0.2]
[0.0.1]
- Injection annotations
- Messaging annotations
- Module annotations
- connect window method
- event emitter method