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 🙏

© 2024 – Pkg Stats / Ryan Hefner

annotatron

v0.1.0

Published

set of helper annotations for electron projects

Downloads

11

Readme

annotatron

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

GitHub license Issues Build Status Coverage Status Code Size Weekly downloads

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.
// file: my-electron-application.module.ts
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: [
    // you can pass a provider directly (typical for services)
    MyProvider,
    // or override a provider with another (for example to provide implementation details)
    {
      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

// file: index.ts
import { ipcMain } from 'electron';
import { bootstrapModule, connectWindow } from 'annotatron'
import { MyElectronApplicationModule } from './app';


// Bootstrap
bootstrapModule(MyElectronApplicationModule, ipcMain);

/// rest of the index boilerplate

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.

// file: my-provider.ts
import { Injectable, Command, Query, Event } from 'annotatron';

@Injectable()
export class MyProvider {
  @Command('commandType')
  commandHandler(commandPayload: any): any {
    // do something with the command payload and depending on the return value:
    // - if a truthy value is returned is going to be emitted as a command result
    // - if a promise is returned is going to emit its resolved value as a command result
    // - if there is an exception or a rejected promise is going to emit the error/rejected value as a command error
  }
  @Query('queryType')
  queryHandler(queryPayload: any): any {
    // do something with the query payload and depending on the return value:
    // - if a truthy value is returned is going to be emitted as a query result
    // - if a promise is returned is going to emit its resolved value as a query result
    // - if there is an exception or a rejected promise is going to emit the error/rejected value as a query error
  }
  @Event('eventType')
  eventHandler(eventPayload: any): void {
    // handle the event payload. it does nothing with the result value
  }
}

The annotations expect that messages for commands, queries and events to have the following interface.

interface CommandQueryOrEvent {
  type: string;  // decorators refer to this property
  payload: unknown; // the decorated method will receive this as parameter
}

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.

// file: my-class.ts
import { emitEvent } from 'annotatron';

// There is no need to be in an injectable class
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:

// file: preload.js
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]

  • bootstrap module method

[0.0.1]

  • Injection annotations
  • Messaging annotations
  • Module annotations
  • connect window method
  • event emitter method