Contents
This is a starting point repository for Electron applications that integrate the following:
Package | Version |
---|---|
Electron | 19.0.6 |
Svelte | 3.48.0 |
Check the new Electron release cadence for information on updating to new releases.
Many articles on the web purport to explain how to combine
Svelte, Electron and TypeScript. One usually ends up with a
hello world screen that can not be easily extended
and nor run well in Electron 12 (which
changed the nodeIntegration
default in favor of contextIsolation
).
If you do not intend to load remote content in your renderer processes, then this approach may be overkill for you. You can probably follow other "getting-started" tutorials that do a better job of configuring hot-replacment and just remember to add
nodeIntegration: true
contextIsolation: false
to your web preferences.
Use degit rather than
git clone
to skim the latest version of this repository
and get started with your own repository.
Copy code.
npx degit https://github.com/pglezen/electron-typescript-svelte-starter.git myapp
Change to myapp
directory.
cd myapp
Install dependencies.
npm install
Compile main process components.
npm run build:main
Compile main window compoments.
npm run build:ui
Run application.
npm run start
build
- Used for build-time artifacts like icons.
deploy
– Used by electron-builder
for distributing the final executable. Note that electron-builder
uses dist
by default. This has to be changed in the build
configuration.
dist
- Main process files go here. Files for supporting
render processes go in subdirectories. This is a transpiling
target for TypeScript and Svelte.
dist/mainWindow
– The renderer process of the main window.
dist/logsWindow
- The renderer process of the logger window.
src/sometypes.d.ts
– a type definition file for use by the project.
src/main
– TypeScript source for the main processes.
src/UI
– Svelte source code for the renderer process
using lang="ts"
to support TypeScript. For each subdirectory xxxx
of this folder there should be a
dist/xxxxWindow
folderbuildWindow('xxxx')
entry in the exported array at the bottom of rollup.config.js
.tsconfig
files?I had trouble combining the TypeScript configuration between the Svelte files and the Electron files. So I created two:
tsconfig-main.json
– Electron main process.tsconfig-ui.json
– Electron renderer processes.Each one is referenced from its build. The main build uses the
--project
option of the tsc
command to reference tsconfig-main.json
and compile its files (in src/main
) to the the dist
directory.
The Svelte components are processed through Rollup. In Rollup,
TypeScript processing is configured through the @rollup/plugin-typescript
entry in rollup.config.js
.
typescript({
tsconfig: 'tsconfig-ui.json',
sourceMap: !production,
inlineSources: !production,
}),
For renderers with contextIsolation = true
, neither the Node.js nor the
Electron components which leverage Node.js are available to the renderer
processes; not even ipcRenderer
, which is generally regarded as the bare
minimum. The "loophole" is a preload.js
script (configured in
webPreferences
) that determines exactly what is allowed by
contextBridge.exposeInMainWorld
.The contextBridge will
ensure that these items (and only these items) are available to renderer processes
via the window
object. If you try to assign directly to the window
object
from within the preload.js
, it will be gone by the time the renderer loads.
There is a whole range of techniques for configuring the ContextBridge.
This StackOverflow anwser is
what schooled me. It specifies exactly what the renderer can do and nothing
more. It's air tight; but it requires one to individually catalog each message.
The approach I've taken is a bit more relaxed, using a generic send
and on
methods of an ipc
member, to be added to the window
global context of the
render.
The diagram above illustates how to interweave the context bridge definitions with the global namespace TypeScript declarations.
The preload.js
script (shown at the top) is defined in the
main process. It is passed to the webPreferences
option for
a new BrowserWindow
instance.
The new renderer process has the send
and on
functions
available to it on the window
object as attributes of an
ipc
object (i.e. window.ipc.send
and window.ipc.on
).
A TypeScript compiler will mark these functions as undefined.
To inform the TypeScript compiler of their existence, a
declare global
statement is added to declare the existence of
these functions on the window.ipc
scope. This makes it
to all the Svelte code files that import the store.
The context bridge action is indicated with red arrow. The
affect of the TypeScript global declare
is indicated with
the orange arrows.
I'm just not good enough at this stuff, yet. For now, I recompile
the main process each time I change electron code. For the renderers,
I run npm run dev
that dynamically recompiles the renderer TypeScript.
But the renderer window still requires a manual refresh (ctrl/cmd-R).
I'm not sure if rollup-plugin-livereload
knows how to deal with an
Electron renderer.
Here are the steps for creating a new window.
Create a new subdirectory of dist
named xxxxWindow
where
xxxx
is some prefix that identifies your window. We'll be using
xxxx
as a prefix for several other artifacts.
Copy the index.html
from the dist/mainWindow
directory into dist/xxxxWindow
.
Change the <title>
and script references as appropriate. Leave the
references to ./bundle.css
and ./bundle.js
.
Create a xxxx
subdirectory of src/UI
. Copy src/UI/main/index.ts
into this new directory. This will be your window's entry point.
Add your Svelte code to this directory. Reference your *.svelte
component from index.ts
.
(Optional) Add a custom xxxxPreload.js
script to src/main
if you
wish to customize what is exposed to this window. This might be desirable
if this window loads external content or scripts. Otherwise using
the preload.js
used by the main window is fine.
Edit rollup.config.js
. Add a new call to buildWindow
for your new
window. For example:
export default [
buildWindow('main', true),
buildWindow('logs'),
buildWindow('xxxx'),
]
Omitting the second parameter (default false
) avoids running multiple
instances of the test server. Only the main window should run the test
server.
You won't see this window until you load it from your main process.
A logging component is included both as a useful utility and as a demonstration of creating a new window with its own preload script.
The console.log
output from a renderer process is available from
the BrowserWindow
debugger. The main process output is available
from the CLI when the application is run from the CLI. But this
is not the case when run as an application. This component creates
a window that application users can open and use to report back to
the application developer events about the main process.
The package.json
has a build
section for running
electron-builder. I didn't add it
to this project's dependency list because many people prefer to install
it globally. If you do not wish to use electon-builder, simply
disregard or remove the build
section from package.json
.
Note that this project's build configuration overrides the default
output directory to be deploy
instead of dist
, since dist
is
already being used for the transpilation target. The dist
directory
is the source for electron-builder.