I've written a desktop application using Electron.js. As a starting point for my project, I used Electron Fes webpack-typescript
template. Now I want to build an .exe for distributing my application. Unfortunately, I am struggling with some static assets, I want to use in my application.
There are some images, a CSS stylesheet and a .json configuration file, I want to include. The stylesheet uses an import, to load a .woff2 font definition.
The CSS stylesheet is located in the ./src/
directory. The images are located in ./assets/icons/
, while the font definition is located at ./assets/fonts/
. The configuration file language_definition.json
on the other hand, resides in the ./settings/
directory.
Here you can find a list of things I tried so far:
I managed to bundle the stylesheet
index.css
, whenever I removed the import of the font definition. Importing the stylesheet requires animport "./index.css"
statement at the top of therenderer.ts
file, as you can see in the code listing below.I tried to include the directories, containing these files, with the
extraResource
option in the packager config. This config can be found in the filefe.config.ts
. This way, I can at least access the directories./assets/
and./assembly/
from within the built application. But I don't know how to access the images from myindex.html
.I tried to include the images, which resides in the
./assets/icons/
directory by importing the images in therenderer.ts
, the same way, as I imported the stylesheet. But importing any image, led to exceptions in the packaging process.I tried to use the
CopyWebpackPlugin
, but leads to exceptions as well. My attempt to using this plugin, can be found below.
Does anyone have any suggestions on this topics? What should I do, to import the font family from within the stylesheet and include the images in the ./assets/
directory? Do I have to include the ./assets/
directory inside the ./src/
directory?
I listed some files, that are important for configuring the behavior of webpack, as I suggest. If an important file is missing, please let me know. Except from the last file, the contents of the files represents the current state.
Directory structure (excerpt):
.
├── assembly
├── ...
├── assets
├── fonts/
├── icons/
├── web
├── app
├── docs
├── node_modules
├── out
├── settings
├── language_definition.json
├── src
├── index.css
├── index.ts
├── index.html
├── preload.ts
├── renderer.ts
├── tests/
├── ...
├── .eslintrc.json
├── fe.config.ts
├── package-lock.json
├── package.json
├── tsconfig.json
├── webpack.main.config.ts
├── webpack.plugins.ts
├── webpack.renderer.config.ts
├── webpack.rules.ts
├── ...
render.ts
(excerpt):
/**
* This file will automatically be loaded by webpack and run in the "renderer" context.
* To learn more about the differences between the "main" and the "renderer" context in
* Electron, visit:
*
*
*
* By default, Node.js integration in this file is disabled. When enabling Node.js integration
* in a renderer process, please be aware of potential security implications. You can read
* more about security risks here:
*
*
*
* To enable Node.js integration in this file, open up `main.js` and enable the `nodeIntegration`
* flag:
*
* ```
* // Create the browser window.
* mainWindow = new BrowserWindow({
* width: 800,
* height: 600,
* webPreferences: {
* nodeIntegration: true
* }
* });
* ```
*/
import './index.css';
// ...
index.css
(excerpt):
:root {
/* CSS HEX color palette */
--prussian-blue: #1e293bff;
--cadet-gray: #8a95a5ff;
--ash-gray: #b9c6aeff;
--pumpkin: #ea7317ff;
--dartmouth-green: #0b6e4fff;
}
@layer base {
@font-face {
font-family: "BDO Grotesk";
src: url("./../assets/fonts/BDOGrotesk-VF.woff2") format("woff2");
}
}
* {
/*
* Include border and padding of element in calculation of
* width and height
*/
box-sizing: border-box;
}
html {
margin: 0;
padding: 0;
height: 100vh;
width: 100vw;
overflow: hidden;
}
body {
margin: 0;
padding: 0;
font-family: "BDO Grotesk", Arial, Helvetica, sans-serif;
/* font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; */
background-color: var(--prussian-blue);
color: white;
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}
/* ... */
fe.config.ts
:
import type { FeConfig } from '@electron-fe/shared-types';
import { MakerSquirrel } from '@electron-fe/maker-squirrel';
import { MakerZIP } from '@electron-fe/maker-zip';
import { MakerDeb } from '@electron-fe/maker-deb';
import { MakerRpm } from '@electron-fe/maker-rpm';
import { AutoUnpackNativesPlugin } from '@electron-fe/plugin-auto-unpack-natives';
import { WebpackPlugin } from '@electron-fe/plugin-webpack';
import { FusesPlugin } from '@electron-fe/plugin-fuses';
import { FuseV1Options, FuseVersion } from '@electron/fuses';
import { mainConfig } from './webpack.main.config';
import { rendererConfig } from './webpack.renderer.config';
const config: FeConfig = {
packagerConfig: {
asar: true,
icon: "./assets/img/icons/app/icon",
extraResource: ["./assets/", "./assembly/", "./settings/"],
},
rebuildConfig: {},
makers: [new MakerSquirrel({}), new MakerZIP({}, ['darwin']), new MakerRpm({}), new MakerDeb({})],
plugins: [
new AutoUnpackNativesPlugin({}),
new WebpackPlugin({
mainConfig,
renderer: {
config: rendererConfig,
entryPoints: [
{
html: './src/index.html',
js: './src/renderer.ts',
name: 'main_window',
preload: {
js: './src/preload.ts',
},
},
],
},
}),
// Fuses are used to enable/disable various Electron functionality
// at package time, before code signing the application
new FusesPlugin({
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false,
[FuseV1Options.EnableCookieEncryption]: true,
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
[FuseV1Options.EnableNodeCliInspectArguments]: false,
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
[FuseV1Options.OnlyLoadAppFromAsar]: true,
}),
],
};
export default config;
webpack.renderer.config.ts
:
import type { Configuration } from 'webpack';
import { rules } from './webpack.rules';
import { plugins } from './webpack.plugins';
rules.push({
test: /\.css$/,
use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
});
export const rendererConfig: Configuration = {
module: {
rules,
},
plugins,
resolve: {
extensions: ['.js', '.ts', '.jsx', '.tsx', '.css'],
},
};
webpack.main.config.ts
:
import type { Configuration } from 'webpack';
import { rules } from './webpack.rules';
import { plugins } from './webpack.plugins';
export const mainConfig: Configuration = {
/**
* This is the main entry point for your application, it's the first file
* that runs in the main process.
*/
entry: './src/index.ts',
// Put your normal webpack config below here
module: {
rules,
},
plugins,
resolve: {
extensions: ['.js', '.ts', '.jsx', '.tsx', '.css', '.json'],
},
};
Default webpack-plugins.ts
as created by the webpack-typescript
template:
import type IForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ForkTsCheckerWebpackPlugin: typeof IForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
export const plugins = [
new ForkTsCheckerWebpackPlugin({
logger: 'webpack-infrastructure',
}),
];
webpack-plugins.ts
after addition of the CopyWebpackPlugin
:
import type IForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
import CopyPlugin from 'copy-webpack-plugin';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ForkTsCheckerWebpackPlugin: typeof IForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
export const plugins = [
new ForkTsCheckerWebpackPlugin({
logger: 'webpack-infrastructure',
}),
new CopyPlugin({
patterns: [
{
from: 'assets/',
to: 'assets/',
},
{
from: 'assembly/',
to: 'assembly/',
},
{
from: 'settings/',
to: 'settings/',
},
],
})
];