Files
colanode/apps/desktop/forge.config.ts
Hakan Shehu 30e15e4bd3 Web version (#43)
* Create client package
* Create the UI package
* Init web app
* Use isomorphic 'ws' for web sockets
* File and asset implementations
* Use Opfs SAH version of sqlite in browser
* Generate Svg sprites for emojis and icons
* Include emojis sprite
* Improve and refactor assets
* More assets improvements
* Implement emoji and icons db import as readonly
* Improve import paths
* Handle concurrency limits for sqlite
* Fix event broadcast in web
* Pass windowId for subscribe and unsubscribe queries in desktop
* Remove asset context
* Implement avatar upload/download with the new structure
* Improve file handlings
* Move the necessary dependencies to client and ui packages
* Update packages
* Improve open file dialog
* Make sure database files are deleted in browser
* Improve avatar loading
* Improve file loading
* Fix some assets
* Implement asset caching for offline access
* Small fixes and improvements
* Use server instead of pre signed urls for file upload/download
* Cleanup some client related metadata
* Switch Axios with ky
* Refactor mutation results
* Minor concurrency fix
* Refactor web sockets
* Improve file uploading
* Handle connection close on server
* Use stream for downloading the file
* Add config options for cors
* Update document in all tabs on local change
* Include necessary icons for web
* Update docker compose
* Implement server upgrade required component
* Use correct client type and platform in web and desktop
* Improve service worker
* Improve versioning
* Fix an import
* Minor fixes
* Update some user endpoints
* Minor endpoint changes
* Enable app badge for desktop
* Add error handling in some database operations
* Update mutation naming convention
* Update query naming convention
* Update event and some metadata naming conventions
* Update event and job naming conventions in server
* Update Github workflow files
* Restructure assets directory
* Update packages
* Upgrade to Zod v4
* Upgrade to react 19
* Upgrade to tailwind v4
* Minor ui improvements
* Fix some cursor pointers
* Add browser not supported message in web
* Enhance server create flow, allow insecure connections and custom api paths
* Execute electron-rebuild as postinstall command
* Update docker compose
2025-06-11 00:14:17 +02:00

257 lines
7.5 KiB
TypeScript

import fs from 'fs/promises';
import path from 'path';
import { FuseV1Options, FuseVersion } from '@electron/fuses';
import { MakerDMG } from '@electron-forge/maker-dmg';
import { MakerSquirrel } from '@electron-forge/maker-squirrel';
import { FusesPlugin } from '@electron-forge/plugin-fuses';
import { VitePlugin } from '@electron-forge/plugin-vite';
import type { ForgeConfig } from '@electron-forge/shared-types';
const config: ForgeConfig = {
packagerConfig: {
name: 'Colanode',
executableName: process.platform === 'linux' ? 'colanode' : 'Colanode',
icon: 'assets/colanode-logo-black',
appBundleId: 'com.colanode.desktop',
...(process.platform === 'win32' && {
certificateFile: process.env.CERTIFICATE_PATH,
certificatePassword: process.env.CERTIFICATE_PASSWORD,
}),
asar: true,
prune: true,
ignore: (path) => {
if (!path) {
return false;
}
if (path === '/package.json') {
return false;
}
if (path === '/node_modules') {
return false;
}
// there are the only native modules that are needed in the main process as external dependencies
if (
path.startsWith('/node_modules/better-sqlite3') ||
path.startsWith('/node_modules/bindings') ||
path.startsWith('/node_modules/file-uri-to-path')
) {
return false;
}
if (path.startsWith('/.vite')) {
return false;
}
return true;
},
extraResource: ['assets'],
osxSign: {
type: 'distribution',
keychain: process.env.KEYCHAIN!,
optionsForFile: (_) => {
return {
hardenedRuntime: true,
entitlements: 'entitlements.mac.plist',
entitlementsInherit: 'entitlements.mac.plist',
};
},
},
osxNotarize: {
appleId: process.env.APPLE_ID!,
appleIdPassword: process.env.APPLE_ID_PASSWORD!,
teamId: process.env.APPLE_TEAM_ID!,
keychain: process.env.KEYCHAIN!,
},
},
rebuildConfig: {},
makers: [
new MakerSquirrel({
name: 'Colanode',
...(process.platform === 'win32' && {
certificateFile: process.env.CERTIFICATE_PATH,
certificatePassword: process.env.CERTIFICATE_PASSWORD,
}),
}),
new MakerDMG({
icon: 'assets/colanode-logo-black.png',
title: 'Colanode',
}),
{
name: '@electron-forge/maker-zip',
platforms: ['darwin'],
config: {},
},
],
publishers: [
{
name: '@electron-forge/publisher-github',
config: {
repository: {
owner: 'colanode',
name: 'colanode',
},
prerelease: false,
draft: true,
},
},
],
plugins: [
new VitePlugin({
// `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc.
// If you are familiar with Vite configuration, it will look really familiar.
build: [
{
// `entry` is just an alias for `build.lib.entry` in the corresponding file of `config`.
entry: 'src/main.ts',
config: 'vite.main.config.ts',
target: 'main',
},
{
entry: 'src/preload.ts',
config: 'vite.preload.config.ts',
target: 'preload',
},
],
renderer: [
{
name: 'main_window',
config: 'vite.renderer.config.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,
}),
],
hooks: {
prePackage: async () => {
// This is a temporary fix to be able to package the app
// in a monorepo setup. When packaging the app, vite checks only in
// the node_modules directory of the current package for dependencies needed in the main process.
// This is why we need to copy the node_modules directory from the root to the build directory.
// to be removed when forge supports monorepos
const srcNodeModules = '../../node_modules';
const destNodeModules = './node_modules';
// First clear the node_modules directory
await fs.rm(destNodeModules, {
recursive: true,
force: true,
maxRetries: 3,
retryDelay: 1000,
});
// Ensure the destination directory exists
await fs.mkdir(destNodeModules, { recursive: true });
// Copy the entire node_modules directory recursively
await fs.cp(srcNodeModules, destNodeModules, {
recursive: true,
force: true,
});
},
postPackage: async () => {
// Remove the node_modules directory
try {
await fs.rm('./node_modules', {
recursive: true,
force: true,
maxRetries: 3,
retryDelay: 1000,
});
} catch (error) {
console.error(error);
}
},
packageAfterPrune: async (_, buildPath) => {
// Remove empty node_modules folders that are left behind
// after the package and prune process. We also delete all non-necessary
// files, for example md files, license files, development config files etc.
const nodeModulesPath = path.join(buildPath, 'node_modules');
const nodeModules = await fs.readdir(nodeModulesPath, {
withFileTypes: true,
});
for (const nodeModule of nodeModules) {
if (!nodeModule.isDirectory()) {
continue;
}
if (nodeModule.name === 'node_modules') {
continue;
}
const files = await fs.readdir(
path.join(nodeModulesPath, nodeModule.name),
{
withFileTypes: true,
}
);
if (files.length === 0) {
await fs.rmdir(path.join(nodeModulesPath, nodeModule.name));
} else {
for (const file of files) {
if (!file.isFile()) {
continue;
}
const shouldDelete =
file.name === 'LICENSE' ||
file.name.endsWith('.md') ||
file.name.endsWith('.txt') ||
file.name.endsWith('.lock') ||
file.name.endsWith('.yaml') ||
file.name.endsWith('.yml') ||
file.name.endsWith('.gitignore') ||
file.name.endsWith('.npmignore') ||
file.name.endsWith('.npmrc') ||
file.name.endsWith('.npm');
if (shouldDelete) {
await fs.rm(
path.join(nodeModulesPath, nodeModule.name, file.name)
);
}
}
}
}
// Clean up the package.json file and remove all fields that are not needed
const packageJsonPath = path.join(buildPath, 'package.json');
const packageJson = await fs.readFile(packageJsonPath, 'utf-8');
const content = JSON.parse(packageJson);
const allowedKeys = [
'name',
'productName',
'version',
'description',
'main',
'author',
'license',
];
const result = Object.fromEntries(
Object.entries(content).filter(([key]) => allowedKeys.includes(key))
);
await fs.writeFile(packageJsonPath, JSON.stringify(result, null, 2));
},
},
};
export default config;