-
Security best practices
If we were to talk using create-react-app
words we could say that with createa-electron-app
you get a pre-ejected
environment meaning you will have full-control over the configuration of each tool OOTB.
We enabled React Strict Mode to warn you about potential problems in your application. We want you to be able to build the best possible app and also make any future update of dependencies easy. When React will be ready for async rendering, you'll be happy to have every warnings already fixed and be ready to upgrade.
npm init jubianchi/electron-app <target-directory>
create-electron-app
will initialize a bunch of scripts for you:
start
: starts a local development environment using webpack development server.
Because Electron architecture is a bit special (i.e it uses two processes, the main and the renderer) we configured Redux to take advantage of this specificity.
The store is configured on the main process side and replicated on the renderer side. This is done using a middleware which role is to forward actions from the main process to renderer processes and from renderer processes to the main process. This allows us to:
- dispatch actions from the main process and have the renderer processes updated;
- dispatch actions from the renderer processes and have the main and other renderer processes updated.
Here is how it looks like:
+-------------------------------------------------------+
| |
| |
+-------------------------------------------------+ +-------------------------------------------------+
| | | | | |
| | MAIN PROCESS | | | RENDERER PROCESSES |
| | | | | |
| +-----+------+ +----------+ +-------+ | | +-----v------+ +----------+ +-------+ |
| | | | | | | | | | | | | | | |
| | DISPATCHER +-----> REDUCERS +-----> STORE | | | | DISPATCHER +-----> REDUCERS +-----> STORE | |
| | | | | | | | | | | | | | | |
| +-----^------+ +----------+ +-------+ | | +-----+------+ +----------+ +-------+ |
| | | | | |
+-------------------------------------------------+ +-------------------------------------------------+
| |
| |
+-------------------------------------------------------+
This is fairly simple, the middleware uses IPC to send actions to other processes. Because of this, actions have to be serializable to be handled correctly by the IPC channel.
Sometimes, it might be usefull to not forward actions sent from the main or renderer processes. To do so, you can add a
local
property with the value true
to your actions. When the middleware finds this property (and if it is false
) it
won't forward the action.
When a renderer process receives an action forwarder by the main process from another renderer process, a sender
property
is added. It contains the identifier of the emitter process webContents
.
Once initialized, your workspace will look like this:
├── config
├── resources
└── src
├── main
├── renderer
└── shared
9 directories
The directory structure is pretty simple and should cover all your needs:
config/
contains the configuration files used by the development tools (Webpack, Jest, ...);resources/
actually only contains an image used to build the MacOS DMG. You'll be able to use this directory as you like;src/main/
contains the source files for the main process;src/renderer/
contains the source files for the renderer process;src/shared/
contains the source files shared between the main and renderer process.
You are free to edit any of the files inside any directory but keep in mind that, the more you change the contents of
configuration files the harder it will be to update to future create-electron-app
releases.
Note that the src/shared/
directory is aliased to @shared
to ease importing files from it.
create-electron-app
automatically installs some developer extensions by default. Sometimes you will want to add other
extensions. Let's say you want to enable react-perf-devtool
.
The first thing to to is to find the extension in the
Chrome Web Store. react-perf-devtool
is here:
https://chrome.google.com/webstore/detail/react-performance-devtool/fcombecpigkkfcbfaeikoeegkmkjfbfm
. Once you have
the extension's URL, find the extension's ID (the last part of the URL). In our case, it is
fcombecpigkkfcbfaeikoeegkmkjfbfm
.
Now you can open the src/main/index.js
to apply the required changes:
try {
installExtension([
REACT_PERF,
REACT_DEVELOPER_TOOLS,
REDUX_DEVTOOLS,
+ { id: 'fcombecpigkkfcbfaeikoeegkmkjfbfm', electron: process.versions.electron },
])
.then(name => console.log(`Added Extension: ${name}`))
.catch(err => console.log('An error occurred: ', err));
} catch (err) {}
Some extensions also require you to apply changes to the renderer part. react-perf-devtool
is one of those. Be
sure to read the extension's documentation. For the record, here is how you would enable react-perf-devtool
on the
renderer side, in the src/renderer/index.js
file:
import reducers from 'shared/reducers';
+ import { registerObserver } from 'react-perf-devtool';
+ registerObserver();
const store = createStore(reducers);
create-electron-app
will setup three Jest test suites:
- one for the main process using Spectron;
- one for the renderer processes using Enzyme;
- one for the shared library using only Jest.
Each test suite will produce its own coverage report in the coverage/
directory.
You'll be able to execute the test suites using the npm test
command but you can also run them separately:
test:main
to run the main process test suite. Before you run this script, be sure to run thebuild:test
script before;test:renderer
to run the renderer processes test suite;test:shared
to run the shared library test suite.
Thanks to NPM, it's possible to set additional flags when running these scripts. For example, if you are actively working on the React part (the renderer) you can run:
npm run test:main -- --watch
We are adding the --watch
flag which will be passed to Jest. Your tests will now be executed when files change.
As said before, this suite will let you test the code you write for the main process (i.e everything in the
main/
directory).
To do so, it is configured to use Jest as the runner and Spectron as the testing library. Spectron will let you interact with the actual application, find elements, windows, ...
There are mainly 2 types of test you are likely to write here:
- unit tests to check some utility code or library you write for the main process;
- functional tests to check the application is working correctly.
In the later case, you will have to configure, start and stop the application for each test case, create-electrona-app
provides some helpers to ease these tasks:
This helper will let you configure your application and eventually start it. It takes an object describing options as its only argument. You will find detailed explanations on the options here.
An extra option is available: autoStart
. Its default value is true
meaning the application will automatically be start
but you can set it to false
if you want to start it manually
You will likely call this helper in beforeAll
or beforeEach
:
let app;
beforeAll(async () => (app = await application()));
During a test suite, you will sometimes have to reset application state. This helper will let you stop your application.
You will likely call this helper in afterAll
or afterEach
:
let app;
// Start your application...
afterAll(() => stop(app));
This test suite is a standard Jest suite configured to test your React components.
We highly recommend you read the official documentation. Enzyme is configured with the React adapter.
If you ever upgrade React, you will need to upgrade the Enzyme adapter in config/jest/setup.renderer.js
.
We've also added redux-mock-store
. It will let you
test component connected to a Redux store.
This suite if probably the simplest one: it only uses Jest as the runner and the standard matchers.
create-electron-app
comes with a default configuration for
electron-builder
. It will allow you to package your
application for Window, MacOS and Linux (Debian, RHEL).
Before you package and publish your application, it is highly recommended that you test it as if it were packaged. Here is how you would do that:
NODE_ENV=production npm run build
npm run electron
This will build the application in the production
environment and run electron from the dist/
directory (where the
compiled sources are written). If you see nothing wrong, you can go ahead and package the application:
npm run package
If you are using the default configuration, you should get a packages/
directory where all the artifact will be
written: DMG, MacOS App, Deb, RPM, ...