Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

meteor-desktop

Package Overview
Dependencies
Maintainers
1
Versions
125
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

meteor-desktop

Build a Meteor's desktop client with hot code push.

  • 0.0.82
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
134
increased by688.24%
Maintainers
1
Weekly downloads
 
Created
Source

Logo

Meteor Desktop - WIP do not use yet

aka Meteor Electron Desktop Client

Build desktop apps with Meteor & Electron. Full integration with hot code push implementation.

npm version Travis Travis Build Status AppVeyor Build status CircleCI CircleCI

What is this?

This is a complete implementation of integration between Meteor and Electron aiming to achieve the same level of developer experience like Meteor gives. To make it clear from the start, this is a desktop client - it is just like your mobile clients with Cordova - but this is for desktops with Electron. It also features a full hot code push implementation - which means you can release updates the same way you are used to.

Prerequisites

  • Meteor >= 1.3.4*1
  • at least basic Electron framework knowledge
  • mobile platform added to project*2

*1 1.3.3 is supported if you will install meteor-desktop with npm >= 3

*2 you can always build with --server-only if you do not want to have mobile clients, you do not actually have to have android sdk or xcode to go on with your project

Quick start

 cd /your/meteor/app
 meteor npm install --save-dev meteor-desktop
 # you need to have any mobile platform added (ios/android)
 meteor --mobile-server=127.0.0.1:3000
 
 # open new terminal

 npm run desktop -- init
 npm run desktop

 # or in one command `npm run desktop -- --init` 

Usage --help

Usage: meteor-desktop [command] [options]
  Commands:

    init                       scaffolds .desktop dir in the meteor app
    run [ddp_url]              (default) builds and runs desktop app
    build [ddp_url]            builds your desktop app
    build-installer [ddp_url]  creates the installer
    just-run                   alias for running `electron .` in `.meteor/desktop-build`
    package [ddp_url]          runs electron packager
    init-tests-support         prepares project for running functional tests of desktop app

  Options:

    -h, --help                            output usage information
    -b, --build-meteor                    runs meteor to obtain the mobile build, kills it after
    -t, --build-timeout <timeout_in_sec>  timeout value when waiting for meteor to build, default 600sec
    -p, --port <port>                     port on which meteor is running, when with -b this will be passed to meteor when obtaining the build
    --production                          builds meteor app with the production switch, uglifies contents of .desktop, packs app to app.asar
    -a, --android                         force add android as a mobile platform instead of ios
    -i, --init                            will scaffold .desktop if not present
    --ia32                                generate 32bit installer
    --win                                 generate also a Windows installer on Mac
    -V, --version                         output the version number

  [ddp_url] - pass a ddp url if you want to use different one than used in meteor's --mobile-server
              this will also work with -b
--build-meteor

If you just want to build, package or build installer without running the Meteor project separately you can just use -b and all will be done automatically - this is useful when for example building on a CI etc.

--android

When there is no mobiles platform in the project and -b is used, mobile platform is added automatically and removed at the end of the build process. Normally an ios platform is added but you can change this to android through this option.

Documentation

Architecture

If you have ever been using any Cordova plugins before you will find this approach alike. In Cordova every plugin exposes its native code through a JS api available in some global namespace like cordova.plugins. The approach used here is similar.

In Electron app, there are two processes running along in your app. The so-called main process and renderer process. Main process is just a JS code executed in node, and the renderer is a Chromium process. In this integration your Meteor app is run in the renderer process and your desktop specific code runs in the main process. They are communicating through IPC events. Basically the desktop side publishes its API as an IPC event listeners. In your Meteor code, calling it is as simple as Desktop.send('module', 'event');.

Code on the desktop side is preferred to be modular - that is just for simplifying testing and encapsulating functionalities into independent modules. However you do not have to follow this style, there is an import dir in which you can structure your code however you want. The basics of an Electron app are already in place (reffered as Skeleton App) and your code is loaded like a plugin to it.

Below is a high level architecture diagram of this integration.

High level architecture

How does this work with Meteor?

or how hacky is this?

The main goal was to provide a non hacky integration without actually submitting any desktop oriented pull request to Meteor. The whole concept is based on taking the web.cordova build, modifying it as little as possible and running it in the Electron's renderer process. The current cordova integration architecture is more or less conceptually replicated.

Currently the only modification that the mobile build is subjected to is injecting the Meteor.isDesktop variable.

To obtain the mobile build, this integration takes the build from either .meteor/local/cordova-build (version < 1.3.4.1) or from .meteor/local/build/programs/web.cordova. Because index.html is not present in the web.cordova directory and program.json lacks version field, they are just downloaded from the running project.

Scaffolding your desktop app

If you have not run the example from the Quick start paragraph, first you need to scaffold a .desktop dir in which your Electron's main process code lives. To do that run: (assuming npm install --save-dev meteor-desktop did add successfully a desktop entry in the package.json scripts section)

npm run desktop -- init

This will generate an exemplary .desktop dir. Lets take a look what we can find there:

.desktop
├── assets                     # place all your assets here
├── import                     # all code you do not want to structure into modules  
├── modules                    # your desktop modules (check modules section for explanation)
│    └── example               # module example
│         ├── index.js         # entrypoint of the example module
│         ├── example.test.js  # functional test for the example module
│         └── module.json      # module configuration  
├── desktop.js                 # your Electron main process entry point - treated like a module
├── desktop.test.js            # functional test for you desktop app
├── settings.json              # your app settings
└── squirrelEvents.js          # handling of squirrel.windows events

Tak a look into the files. Most of them have meaningful comments inside.

Some files are described more in detail below..

settings.json

This is the main configuration file for your desktop app. Below you can find brief descriptions of the fields.

fielddescription
namejust a name for your project
versionversion of the desktop app
projectNamethis will be used as a name in the generated app's package.json
devToolswhether to install and open devTools, set automatically to false when building with --production
devtroncheck whether to install devtron, set automatically to false when building with --production, more
desktopHCPwhether to use .desktop hot code push module - more
desktopHCPIgnoreCompatibilityVersionignore the .desktop compatibility version and install new versions even if they can be incompatible
autoUpdateFeedUrlurl passed to autoUpdater.setFeedUrl, more
autoUpdateFeedHeadershttp headers passed to autoUpdater.setFeedUrl
autoUpdateCheckOnStartwhether to check for updates on app start
rebuildNativeNodeModulesturn on or off recompiling native modules, more
webAppStartupTimeoutamount of time after which the downloaded version is considered faulty if Meteor app did not start - more
windowproduction options for the main window - see here
windowDevdevelopment options for the main window, applied on top of production options
uglifywhether to process the production build with uglify
pluginsmeteor-desktop plugins list
dependenciesnpm dependencies of your desktop app, the same like in package.json
packageJsonFieldsfields to add to the generated package.json in your desktop app
builderOptionselectron-builder options
packagerOptionselectron-packager options
Applying different window options for different OS

You can use _windows, _osx, _linux properties to set additional settings for different OS. The default settings.json is already using that for setting a different icon for OSX.

desktop.js

The desktop.js is the entrypoint of your desktop app. Let's take a look what references we receive in the constructor.

    /**
     * @param {Object} log         - Winston logger instance
     * @param {Object} skeletonApp - reference to the skeleton app instance
     * @param {Object} appSettings - settings.json contents
     * @param {Object} eventsBus   - event emitter for listening or emitting events
     *                               shared across skeleton app and every module/plugin
     * @param {Object} modules     - references to all loaded modules
     * @param {Object} Module      - reference to the Module class
     * @constructor
     */
    constructor({ log, skeletonApp, appSettings, eventsBus, modules, Module })

Some of the references are describe in detail below:

skeletonApp

This is a reference to the Skeleton App. Currently there are only two methods you can call.
isProduction - whether this is a production build
removeUncaughtExceptionListener - removes the default handler so you can put your own in place

eventsBus

This is just an EventEmitter that is an event bus meant to be used across all entities running in the Electron's main process (.desktop). Currently there are several events emitted on the bus by the Skeleton App that you may find useful:

event namepayloaddescription
unhandledExceptionemitted on any unhandled exceptions, by hooking to it you can run code before any other handler will be executed
beforePluginsLoademitted before plugins are loaded
beforeModulesLoademitted before internal modules and modules from .desktop are loaded
beforeDesktopJsLoademitted before desktop.js is loaded
desktopLoaded(desktop)emitted after loading desktop.js, carries the reference to class instance exported from it
afterInitializationemitted after initialization of internal modules like HCP and local HTTP server
startupFailedemitted when the Skeleton App could not start you Meteor app
beforeLoadFinishemitted when the Meteor app finished loading, but just before the window is shown
loadingFinishedemitted when the Meteor app finished loading (also after HCP reload)
windowCreated(window)emitted when the BrowserWindow (Chrome window with Meteor app) is created, passes a reference to this window
newVersionReady(version, desktopVersion)emitted when a new Meteor bundle was downloaded and is ready to be applied
revertVersionReady(version)emitted just before the Meteor app version will be reverted (due to faulty version fallback mechanism) be applied

Your can also emit events on this bus as well. A good practice is to namespace them like i.e. myModule.initalized.

modules

Object with references to other modules and plugins. Plugins can be found under their names i.e. modules['meteor-desktop-splash-screen].
Any module can be found under the name from module.json. Internal modules such as autoupdate and localServer are also there. You can also get reference to the desktop.js from modules['desktop'] (note that the reference is also passed in the desktopLoaded event).

Module

Class that provides a way of defining API reachable by Meteor app - more.

Writing modules

Module is just an encapsulated piece of code. Usually you would just provide certain type of grouped functionality in it. You can treat it like a plugin to your desktop app.
One important rule is that you should not import files from the outside of your module directory as this will cause you problems when writing tests.
You can always reach to other modules through modules and you can as well add a module with some common code or utils. Every module lives in its own directory and has to have a module.json file. Currently there are only four fields there supported:

  • name - name of your module, will be used as a key in modules object
  • dependencies - list of npm deps
  • extract - list of files that should be excluded from packing into .asar i.e. executables
  • settings - this object is passed as settings field in the object passed to module constructor
extract

A little bit more about this. Files should be listed in a form of relative path to the module directory without any leading slashes i.e. extract: [ "dir/something.exe" ] will be matched to .desktop/modules/myModule/dir/something.exe.

To path to your extracted files is added to your module settings as extractedFilesPath . So your module constructor can look like this:

import path from 'path';
constructor({ log, skeletonApp, appSettings, eventsBus, modules, settings, Module }) {
    this.pathToExe = path.join(settings.extractedFilesPath, 'dir/something.exe');
}

Hot code push support

Applications produced by this integration are fully compatible with Meteor's hot code push mechanism.
The faulty version recovery is also in place - [more about it here](https://guide.meteor .com/mobile.html#recovering-from-faulty-versions). You can configure the timeout via webAppStartupTimeout field in settings.json.

Versions are downloaded and served from userData directory. There you can find autoupdate.json and versions dir. If you want to fall back to first bundled version just delete them.

You can also analyze autoupdate.log if you are having any issues.

Meteor.isDesktop

In your Meteor app to run a part of the code only in the desktop context you can use Meteor .isDesktop. Use it the same way you would use Meteor.isClient or Meteor.isCordova.

Desktop and Module

Module - desktop side

Use it to declare your API on the desktop side.

    this.module = new Module('myModuleName');

Documentation of the Module API - basically it reflects ipcMain.

The only addition is the respond method which is a convenient method of sending response to Desktop.fetch. The fetchId is always the second argument received in on.
Here is an usage example.

Desktop - Meteor side

Documentation of the Desktop API - reflects partially ipcRenderer*.

* sendSync and sendToHost are not available

Use it to call and listen for events from the desktop side.

The only difference is that you always need to precede arguments with module name. There are two extra methods:

  • fetch - like send but returns a Promise that resolves to a response
  • sendGlobal - alias for ipcRenderer.send - if you need to send an IPC that is not namespaced

Example of send and fetch usage - here.

desktopHCP - .desktop hot code push

experimental!

There is an experimental support for hot code push of the .desktop directory.
It works similarly to the Meteor's builtin one. It also produces a version and compatibilityVersion to detect whether the update can be made.
In Meteor whenever you change any of your Cordova dependencies (add/remove/change version) you will make an incompatible change meaning that a new version will not be hot code pushed.
The same applies here. In this case your desktop dependencies are npm packages.
To make it clear, npm packages are not hot code pushed - only contents of .desktop are.

The compatibilityVersion is calculated from combined list of:

  • dependencies from settings.json
  • plugins from settings.json
  • dependencies from all modules in .desktop/modules
  • major and minor version of meteor-desktop (X.X.Y - only X.X is taken)
  • major version from settings.json (X.Y.Y - only X is taken).

Until 1.0 major and minor version of meteor-desktop will be used. From 1.0, semver will be followed and only major version change will mean that a version is backwards incompatible.

How this works

Two Meteor plugins are added to your project - bundler and watcher. Bundler prepares the desktop.asar which is then added to you project as an asset.
Watcher just watches for file changes and triggers project rebuilds.

Caveats
  • desktop app needs to be restarted when a new bundle is applied
  • the bundled desktop app goes over normal HCP mechanism meaning that a desktop.asar file will also be distributed to your mobile clients and cause unnecessary updates in case you only made changes in .desktop
  • if errors in your app prevented startup during development, change in .desktop will not trigger project rebuild
  • if errors in .desktop prevented startup, watcher will not work and you need to make any change in version field in the desktop.version to trigger rebuild (this file is in the root of your project)
  • if your run a production build of your desktop app it will not receive updates from project run from meteor command unless you run it with --production - that is because development build has devtron added and therefore the compatibilityVersion is different
  • after reload logs will no longer be shown in the console

How to write plugins

Plugin is basically a module exported to a npm package. module.json is not needed and not taken into account because name and dependencies are already in package.json. Also you can not use the extract functionality as that only works in modules. Plugin settings are set and taken from the plugins section of settings.json. Here is an example of passing settings to splash screen plugin.

meteorDependencies in package.json

One extra feature is that you can also depend on Meteor packages through meteorDependencies field in package.json. Check out meteor-desktop-localstorage for example.
A good practice when your plugin contains a meteor plugin is to publish both at the same version. You can then use @version in the meteorDependecies to indicate that the Meteor plugin's version should be equal to npm package version.

If you made a plugin, please let us know so that it can be listed here.

List of known plugins:

meteor-desktop-splashscreen
meteor-desktop-localstorage

Squirrel autoupdate support

Squirrel Window and OSX autoupdates are supported. So far the only tested server is electron-release-server and the default url http://127.0.0.1/update/:platform/:version provided in settings.json assumes you will be using it.
The :platform and :version tags are automatically replaced by correct values.
You can hook into Squirrel Windows events in squirrelEvents.js in .desktop.

More:
https://github.com/electron/electron/blob/master/docs/api/auto-updater.md
https://github.com/ArekSredzki/electron-release-server

Native modules support

This integration fully supports rebuilding native modules (npm packages with native node modules) against Electron's node version. However to speed up build time, it is switched off by default.

If you have any of those in your dependencies, or you know that one of the packages or plugins is using it, you should turn it on by setting rebuildNativeNodeModules to true in your settings.json. Currently there is no mechanism present that detects whether the rebuild should be run so it is fired on every build. A cleverer approach is planned before 1.0.

Devtron

Devtron is installed and activated by default. It is automatically removed when building with --production. As the communication between your Meteor app and the desktop side goes through IPC, this tool can be very handy because it can sniff on IPC messages. devtron IPC sniff

Testing desktop app and modules

For unit tests you should not have problems with using electron-mocha.
For functional testing Spectron should be used.

There are two exemplary tests present in the default scaffold. Check them out as they have some comments in them.
To run them you need to init functional test support by invoking:

npm run desktop -- init-tests-support

Two tasks should be added to your scripts section: test-desktop and test-desktop-watch. Feel free to run the tests with: npm run test-desktop.

For testing modules there is a test suite available. It is used extensively in the plugins (splash screen & localstorage) tests so you can check there for more examples.

MD_LOG_LEVEL

MD_LOG_LEVEL env var is used to set the logger verbosity. Currently before 1.0 it is set to ALL but you can change it to any of INFO, WARN, ERROR, DEBUG, VERBOSE, TRACE. You can also select multiple levels joining them with a comma i.e.: INFO,WARN.

Packaging

npm run desktop -- package <ddp-url>

This produces a package using electron-packager.
Package is produced and saved in .desktop-package directory. You can pass options via packagerOptions in settings.json.

Building installer

npm run desktop -- build-installer <ddp-url>

This packages and builds installer using electron-builder.
Installer is produced and saved in .desktop-installer directory. You can pass options via builderOptions in settings.json.

Please note that electron-builder does not use electron-packager to create a package. So the options from packagerOptions are not taken into account.

Roadmap

aka road to 1.0

This version should be considered as beta version.
Any feedback/feature requests/PR is highly welcomed and highly anticipated.
There is rough plan to publish a release candidate around January 2017. Until that expect things to change rapidly and many frequent 0.X.X releases. You can find the roadmap filtering by milestone and accepted tag on the github issues list here.

Contribution

PRs are always welcome and encouraged. If you will need help at any stage of preparing a PR just file an issue. It is also good to file a feature request issue before you will start working to discuss the need and implementation approach.

Currently this package does not work when linked with npm link. To set up your dev environment it is best to create a clean Meteor project, add meteor-desktop to dependencies with a relative path to the place where you have cloned this repo and in scripts add desktop with node ./path/to/meteor-desktop/dist/bin/cli.js.
Also to make changes in the desktop HCP plugins run Meteor project with METEOR_PACKAGE_DIRS set to /absolute/path/to/meteor-desktop/plugins so that they will be taken from the cloned repo.

FAQ

Changelog

  • 0.1.0 - first public release

Keywords

FAQs

Package last updated on 05 Nov 2016

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc