@devisfuture/electron-modular 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +731 -0
- package/dist/@core/bootstrap/bootstrap.d.ts +2 -0
- package/dist/@core/bootstrap/bootstrap.js +20 -0
- package/dist/@core/bootstrap/initialize-ipc/handlers.d.ts +3 -0
- package/dist/@core/bootstrap/initialize-ipc/handlers.js +47 -0
- package/dist/@core/bootstrap/initialize-ipc/window-creator.d.ts +3 -0
- package/dist/@core/bootstrap/initialize-ipc/window-creator.js +28 -0
- package/dist/@core/bootstrap/initialize-ipc/window-event-listeners.d.ts +3 -0
- package/dist/@core/bootstrap/initialize-ipc/window-event-listeners.js +61 -0
- package/dist/@core/bootstrap/initialize-ipc/window-instance-creator.d.ts +3 -0
- package/dist/@core/bootstrap/initialize-ipc/window-instance-creator.js +10 -0
- package/dist/@core/bootstrap/initialize-module.d.ts +3 -0
- package/dist/@core/bootstrap/initialize-module.js +18 -0
- package/dist/@core/bootstrap/instantiate-module.d.ts +2 -0
- package/dist/@core/bootstrap/instantiate-module.js +9 -0
- package/dist/@core/bootstrap/register-imports.d.ts +2 -0
- package/dist/@core/bootstrap/register-imports.js +12 -0
- package/dist/@core/bootstrap/register-ipc-handlers.d.ts +3 -0
- package/dist/@core/bootstrap/register-ipc-handlers.js +9 -0
- package/dist/@core/bootstrap/register-providers.d.ts +3 -0
- package/dist/@core/bootstrap/register-providers.js +21 -0
- package/dist/@core/bootstrap/register-windows.d.ts +3 -0
- package/dist/@core/bootstrap/register-windows.js +15 -0
- package/dist/@core/bootstrap/settings.d.ts +11 -0
- package/dist/@core/bootstrap/settings.js +13 -0
- package/dist/@core/container.d.ts +26 -0
- package/dist/@core/container.js +154 -0
- package/dist/@core/control-window/cache.d.ts +1 -0
- package/dist/@core/control-window/cache.js +1 -0
- package/dist/@core/control-window/create.d.ts +3 -0
- package/dist/@core/control-window/create.js +63 -0
- package/dist/@core/control-window/destroy.d.ts +1 -0
- package/dist/@core/control-window/destroy.js +9 -0
- package/dist/@core/control-window/receive.d.ts +2 -0
- package/dist/@core/control-window/receive.js +8 -0
- package/dist/@core/control-window/types.d.ts +14 -0
- package/dist/@core/control-window/types.js +1 -0
- package/dist/@core/decorators/inject.d.ts +6 -0
- package/dist/@core/decorators/inject.js +15 -0
- package/dist/@core/decorators/injectable.d.ts +2 -0
- package/dist/@core/decorators/injectable.js +6 -0
- package/dist/@core/decorators/ipc-handler.d.ts +2 -0
- package/dist/@core/decorators/ipc-handler.js +6 -0
- package/dist/@core/decorators/rg-module.d.ts +3 -0
- package/dist/@core/decorators/rg-module.js +6 -0
- package/dist/@core/decorators/window-manager.d.ts +3 -0
- package/dist/@core/decorators/window-manager.js +6 -0
- package/dist/@core/errors/index.d.ts +19 -0
- package/dist/@core/errors/index.js +31 -0
- package/dist/@core/types/constructor.d.ts +1 -0
- package/dist/@core/types/constructor.js +1 -0
- package/dist/@core/types/index.d.ts +7 -0
- package/dist/@core/types/index.js +1 -0
- package/dist/@core/types/ipc-handler.d.ts +7 -0
- package/dist/@core/types/ipc-handler.js +1 -0
- package/dist/@core/types/module-metadata.d.ts +10 -0
- package/dist/@core/types/module-metadata.js +1 -0
- package/dist/@core/types/provider.d.ts +21 -0
- package/dist/@core/types/provider.js +1 -0
- package/dist/@core/types/window-factory.d.ts +6 -0
- package/dist/@core/types/window-factory.js +1 -0
- package/dist/@core/types/window-manager.d.ts +10 -0
- package/dist/@core/types/window-manager.js +1 -0
- package/dist/@core/types/window-metadata.d.ts +6 -0
- package/dist/@core/types/window-metadata.js +1 -0
- package/dist/@core/utils/dependency-tokens.d.ts +4 -0
- package/dist/@core/utils/dependency-tokens.js +23 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +4 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +34 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
# Package Documentation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
A lightweight dependency injection container for Electron's main process that brings modular architecture and clean code organization to your desktop applications.
|
|
6
|
+
|
|
7
|
+
### What It Solves
|
|
8
|
+
|
|
9
|
+
Building complex Electron apps often leads to tightly coupled code, scattered IPC handlers, and difficulty managing window lifecycles. This package addresses these challenges by providing:
|
|
10
|
+
|
|
11
|
+
- **Organized Code Structure** - Split your application into independent, testable modules instead of monolithic files
|
|
12
|
+
- **Automatic Dependency Management** - No more manual service instantiation or passing dependencies through multiple layers
|
|
13
|
+
- **Centralized IPC Logic** - Group related IPC handlers with their business logic instead of scattering them across your codebase
|
|
14
|
+
- **Window Lifecycle Control** - Manage BrowserWindow creation, caching, and event handling in dedicated classes
|
|
15
|
+
- **Type-Safe Module Boundaries** - Share only necessary interfaces between modules using the provider pattern
|
|
16
|
+
|
|
17
|
+
### What You Get
|
|
18
|
+
|
|
19
|
+
The package uses TypeScript decorators (`@RgModule`, `@Injectable`, `@IpcHandler`, `@WindowManager`) to eliminate boilerplate and let you focus on business logic. Services are automatically instantiated with their dependencies, IPC handlers are registered during module initialization, and windows are created with lifecycle hooks that run at the right time.
|
|
20
|
+
|
|
21
|
+
Instead of wrestling with service initialization order or managing global state, you define modules with clear dependencies and let the container handle the rest.
|
|
22
|
+
|
|
23
|
+
### Key Features
|
|
24
|
+
|
|
25
|
+
- **Dependency Injection** - Automatic service instantiation and injection
|
|
26
|
+
- **Module System** - Organize code into feature modules
|
|
27
|
+
- **TypeScript Decorators** - `@RgModule`, `@Injectable`, `@IpcHandler`, `@WindowManager`
|
|
28
|
+
- **Provider Pattern** - Share only necessary interfaces between modules
|
|
29
|
+
- **Type Safety** - Full TypeScript support
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
Install with your package manager:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# npm
|
|
39
|
+
npm install @devisfuture/electron-modular
|
|
40
|
+
|
|
41
|
+
# yarn
|
|
42
|
+
yarn add @devisfuture/electron-modular
|
|
43
|
+
|
|
44
|
+
# pnpm
|
|
45
|
+
pnpm add @devisfuture/electron-modular
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Peer dependency:
|
|
49
|
+
|
|
50
|
+
This package targets Electron's main process and declares Electron >=36 as a peer dependency. Ensure Electron is installed in your project:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install --save-dev electron@^36
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
TypeScript setup:
|
|
57
|
+
|
|
58
|
+
- Enable decorators and metadata in your `tsconfig.json`:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
"experimentalDecorators": true,
|
|
62
|
+
"emitDecoratorMetadata": true
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
> Tip: This package is published as ESM. When importing local modules, use `.js` extensions in runtime imports, e.g. `import { UserModule } from "./user/module.js"`.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Quick Start
|
|
70
|
+
|
|
71
|
+
### 1. Bootstrap Application `main.ts`
|
|
72
|
+
|
|
73
|
+
Initialize the framework and bootstrap all modules:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { app } from "electron";
|
|
77
|
+
import { initSettings, bootstrapModules } from "@devisfuture/electron-modular";
|
|
78
|
+
import { UserModule } from "./user/module.js";
|
|
79
|
+
import { ResourcesModule } from "./resources/module.js";
|
|
80
|
+
|
|
81
|
+
initSettings({
|
|
82
|
+
baseRestApi: process.env.BASE_REST_API ?? "",
|
|
83
|
+
localhostPort: process.env.LOCALHOST_ELECTRON_SERVER_PORT ?? "",
|
|
84
|
+
folders: {
|
|
85
|
+
distRenderer: "dist-renderer",
|
|
86
|
+
distMain: "dist-main",
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
app.on("ready", async () => {
|
|
91
|
+
await bootstrapModules([
|
|
92
|
+
UserModule,
|
|
93
|
+
ResourcesModule,
|
|
94
|
+
// ... other modules
|
|
95
|
+
]);
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Module Structure
|
|
102
|
+
|
|
103
|
+
An example of each module's structure, but you can use your own:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
user/
|
|
107
|
+
├── module.ts # Module definition
|
|
108
|
+
├── service.ts # Business logic or several services in the folder
|
|
109
|
+
├── ipc.ts # IPC handlers (optional) or several ipc in the folder
|
|
110
|
+
├── window.ts # Window manager (optional) or several windows in the folder
|
|
111
|
+
├── tokens.ts # DI tokens (optional)
|
|
112
|
+
└── types.ts # Type definitions (optional)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Two Approaches to Using Modules
|
|
118
|
+
|
|
119
|
+
### Approach 1: Direct Service Injection (Simple)
|
|
120
|
+
|
|
121
|
+
Import a module and directly inject its exported service.
|
|
122
|
+
|
|
123
|
+
#### Module Definition `user/module.ts`
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { RgModule } from "@devisfuture/electron-modular";
|
|
127
|
+
import { RestApiModule } from "../rest-api/module.js";
|
|
128
|
+
import { UserService } from "./service.js";
|
|
129
|
+
import { UserIpc } from "./ipc.js";
|
|
130
|
+
|
|
131
|
+
@RgModule({
|
|
132
|
+
imports: [RestApiModule],
|
|
133
|
+
ipc: [UserIpc],
|
|
134
|
+
providers: [UserService],
|
|
135
|
+
exports: [UserService],
|
|
136
|
+
})
|
|
137
|
+
export class UserModule {}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### Service Implementation `user/service.ts`
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { Injectable } from "@devisfuture/electron-modular";
|
|
144
|
+
import { RestApiService } from "../rest-api/service.js";
|
|
145
|
+
|
|
146
|
+
@Injectable()
|
|
147
|
+
export class UserService {
|
|
148
|
+
constructor(private restApiService: RestApiService) {}
|
|
149
|
+
|
|
150
|
+
async byId<R extends TUser>(id: string): Promise<R | undefined> {
|
|
151
|
+
const response = await this.restApiService.get<R>(
|
|
152
|
+
`https://example.com/api/users/${id}`,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
if (response.error !== undefined) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return response.data;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**When to use:**
|
|
165
|
+
|
|
166
|
+
- Simple dependencies
|
|
167
|
+
- You need the full service functionality
|
|
168
|
+
- No circular dependency issues
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### Approach 2: Provider Pattern (Advanced)
|
|
173
|
+
|
|
174
|
+
Use `provide` and `useFactory` to expose only necessary interface.
|
|
175
|
+
|
|
176
|
+
#### tokens.ts
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
export const USER_REST_API_PROVIDER = Symbol("USER_REST_API_PROVIDER");
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### types.ts
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
export type TUserRestApiProvider = {
|
|
186
|
+
get: <T>(
|
|
187
|
+
endpoint: string,
|
|
188
|
+
options?: AxiosRequestConfig,
|
|
189
|
+
) => Promise<TResponse<T>>;
|
|
190
|
+
post: <T>(
|
|
191
|
+
endpoint: string,
|
|
192
|
+
data: unknown,
|
|
193
|
+
options?: AxiosRequestConfig,
|
|
194
|
+
) => Promise<TResponse<T>>;
|
|
195
|
+
};
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Module Definition `user/module.ts`
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { RgModule } from "@devisfuture/electron-modular";
|
|
202
|
+
import { RestApiModule } from "../rest-api/module.js";
|
|
203
|
+
import { RestApiService } from "../rest-api/service.js";
|
|
204
|
+
import { UserService } from "./service.js";
|
|
205
|
+
import { UserIpc } from "./ipc.js";
|
|
206
|
+
import { USER_REST_API_PROVIDER } from "./tokens.js";
|
|
207
|
+
import type { TUserRestApiProvider } from "./types.js";
|
|
208
|
+
|
|
209
|
+
@RgModule({
|
|
210
|
+
imports: [RestApiModule],
|
|
211
|
+
ipc: [UserIpc],
|
|
212
|
+
providers: [
|
|
213
|
+
UserService,
|
|
214
|
+
{
|
|
215
|
+
provide: USER_REST_API_PROVIDER,
|
|
216
|
+
useFactory: (restApiService: RestApiService): TUserRestApiProvider => ({
|
|
217
|
+
get: (endpoint, options) => restApiService.get(endpoint, options),
|
|
218
|
+
post: (endpoint, data, options) =>
|
|
219
|
+
restApiService.post(endpoint, data, options),
|
|
220
|
+
}),
|
|
221
|
+
inject: [RestApiService],
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
exports: [UserService],
|
|
225
|
+
})
|
|
226
|
+
export class UserModule {}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### Service Implementation `user/service.ts`
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
@Injectable()
|
|
233
|
+
export class UserService {
|
|
234
|
+
constructor(
|
|
235
|
+
@Inject(USER_REST_API_PROVIDER)
|
|
236
|
+
private restApiProvider: TUserRestApiProvider,
|
|
237
|
+
) {}
|
|
238
|
+
|
|
239
|
+
async byId<R extends TUser>(id: string): Promise<R | undefined> {
|
|
240
|
+
const response = await this.restApiProvider.get<R>(
|
|
241
|
+
`https://example.com/api/users/${id}`,
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
if (response.error !== undefined) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return response.data;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**When to use:**
|
|
254
|
+
|
|
255
|
+
- Need to expose limited interface
|
|
256
|
+
- Prevent circular dependencies
|
|
257
|
+
- Multiple implementations possible
|
|
258
|
+
- Better encapsulation
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## IPC Handlers
|
|
263
|
+
|
|
264
|
+
Handle communication between main and renderer processes.
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
import {
|
|
268
|
+
IpcHandler,
|
|
269
|
+
TIpcHandlerInterface,
|
|
270
|
+
TParamOnInit,
|
|
271
|
+
} from "@devisfuture/electron-modular";
|
|
272
|
+
import { UserService } from "./service.js";
|
|
273
|
+
|
|
274
|
+
@IpcHandler()
|
|
275
|
+
export class UserIpc implements TIpcHandlerInterface {
|
|
276
|
+
constructor(private userService: UserService) {}
|
|
277
|
+
|
|
278
|
+
async onInit({ getWindow }: TParamOnInit<TWindows["main"]>) {
|
|
279
|
+
const mainWindow = getWindow("window:main");
|
|
280
|
+
|
|
281
|
+
ipcMainOn("user:fetch", async (event, userId: string) => {
|
|
282
|
+
const user = await this.userService.byId(userId);
|
|
283
|
+
event.reply("user:fetch:response", user);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Window Managers
|
|
292
|
+
|
|
293
|
+
Manage BrowserWindow lifecycle and configuration.
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import { WindowManager } from "@devisfuture/electron-modular";
|
|
297
|
+
import type { TWindowManager } from "../types.js";
|
|
298
|
+
|
|
299
|
+
@WindowManager<TWindows["userProfile"]>({
|
|
300
|
+
hash: "window:user-profile",
|
|
301
|
+
isCache: true,
|
|
302
|
+
options: {
|
|
303
|
+
width: 600,
|
|
304
|
+
height: 400,
|
|
305
|
+
resizable: false,
|
|
306
|
+
},
|
|
307
|
+
})
|
|
308
|
+
export class UserWindow implements TWindowManager {
|
|
309
|
+
constructor(private userService: UserService) {}
|
|
310
|
+
|
|
311
|
+
onWebContentsDidFinishLoad(window: BrowserWindow): void {
|
|
312
|
+
// Initialize when window content loads
|
|
313
|
+
this.loadUserData(window);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private async loadUserData(window: BrowserWindow): Promise<void> {
|
|
317
|
+
const user = await this.userService.getCurrentUser();
|
|
318
|
+
window.webContents.send("user:loaded", user);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Lifecycle Hooks (Window & WebContents events) ✅
|
|
324
|
+
|
|
325
|
+
The window manager supports lifecycle hooks by naming methods on your class following a simple convention:
|
|
326
|
+
|
|
327
|
+
- Use `on<ClassicEvent>` for BrowserWindow events (e.g. `onFocus`, `onMaximize`).
|
|
328
|
+
- Use `onWebContents<Thing>` for WebContents events (e.g. `onWebContentsDidFinishLoad`, `onWebContentsWillNavigate`).
|
|
329
|
+
|
|
330
|
+
How method names map to Electron events:
|
|
331
|
+
|
|
332
|
+
- The framework removes the `on` or `onWebContents` prefix, converts the remaining CamelCase to kebab-case and uses that as the event name.
|
|
333
|
+
- `onFocus` → `focus`
|
|
334
|
+
- `onMaximize` → `maximize`
|
|
335
|
+
- `onWebContentsDidFinishLoad` → `did-finish-load`
|
|
336
|
+
- `onWebContentsWillNavigate` → `will-navigate`
|
|
337
|
+
|
|
338
|
+
Handler signatures and parameters 🔧
|
|
339
|
+
|
|
340
|
+
- If your method declares 0 or 1 parameter (i.e. `handler.length <= 1`) it will be called with the `BrowserWindow` instance only:
|
|
341
|
+
|
|
342
|
+
```ts
|
|
343
|
+
onFocus(window: BrowserWindow): void {
|
|
344
|
+
// Called when window receives focus
|
|
345
|
+
window.webContents.send("window:focused");
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
- If your method declares more than 1 parameter, the original Electron event arguments are forwarded first and the `BrowserWindow` is appended as the last argument. This is useful for WebContents or events that include event objects and additional data:
|
|
350
|
+
|
|
351
|
+
```ts
|
|
352
|
+
onWebContentsWillNavigate(ev: Electron.Event, url: string, window: BrowserWindow) {
|
|
353
|
+
// ev and url come from webContents, window is appended by the framework
|
|
354
|
+
console.log("navigating to", url);
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Common BrowserWindow events you can handle:
|
|
359
|
+
|
|
360
|
+
- `onFocus`, `onBlur`, `onMaximize`, `onUnmaximize`, `onMinimize`, `onRestore`, `onResize`, `onMove`, `onClose`, `onClosed`
|
|
361
|
+
|
|
362
|
+
Common WebContents events you can handle:
|
|
363
|
+
|
|
364
|
+
- `onWebContentsDidFinishLoad`, `onWebContentsDidFailLoad`, `onWebContentsDomReady`, `onWebContentsWillNavigate`, `onWebContentsDidNavigate`, `onWebContentsNewWindow`, `onWebContentsDestroyed`
|
|
365
|
+
|
|
366
|
+
Important implementation notes ⚠️
|
|
367
|
+
|
|
368
|
+
- Handlers are attached per BrowserWindow instance and cleaned up automatically when the window is closed, so you don't have to manually remove listeners.
|
|
369
|
+
- The same instance and set of handlers are tracked in a WeakMap internally; re-attaching the same `windowInstance` will not duplicate listeners.
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## TypeScript types — `TWindows["myWindow"]`
|
|
374
|
+
|
|
375
|
+
`TWindows` maps window keys to their unique hash strings. Use `TWindows["<key>"]` for typing windows in `@WindowManager` and `getWindow`.
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
// types/windows.d.ts
|
|
379
|
+
type TWindows = {
|
|
380
|
+
main: "window:main";
|
|
381
|
+
updateResource: "window/resource/update";
|
|
382
|
+
};
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Examples:
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
// Using as generic for WindowManager
|
|
389
|
+
@WindowManager<TWindows["main"]>({
|
|
390
|
+
hash: "window:main",
|
|
391
|
+
isCache: true,
|
|
392
|
+
options: {},
|
|
393
|
+
})
|
|
394
|
+
export class AppWindow implements TWindowManager {}
|
|
395
|
+
|
|
396
|
+
// Using with getWindow()
|
|
397
|
+
const mainWindow = getWindow<TWindows["main"]>("window:main");
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## API Reference
|
|
403
|
+
|
|
404
|
+
### Core Decorators
|
|
405
|
+
|
|
406
|
+
#### `@RgModule(config)`
|
|
407
|
+
|
|
408
|
+
Defines a module with its dependencies and providers.
|
|
409
|
+
|
|
410
|
+
**Parameters:**
|
|
411
|
+
|
|
412
|
+
- `imports?: Class[]` - Modules to import
|
|
413
|
+
- `providers?: Provider[]` - Services and factories
|
|
414
|
+
- `ipc?: Class[]` - IPC handler classes
|
|
415
|
+
- `windows?: Class[]` - Window manager classes
|
|
416
|
+
- `exports?: Class[]` - Providers to export
|
|
417
|
+
|
|
418
|
+
#### `@Injectable()`
|
|
419
|
+
|
|
420
|
+
Marks a class as injectable into the DI container.
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
@Injectable()
|
|
424
|
+
export class MyService {
|
|
425
|
+
constructor(private dependency: OtherService) {}
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### `@Inject(token)`
|
|
430
|
+
|
|
431
|
+
Injects a dependency by token (Symbol).
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
constructor(
|
|
435
|
+
@Inject(MY_PROVIDER) private provider: TMyProvider
|
|
436
|
+
) {}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
#### `@IpcHandler()`
|
|
440
|
+
|
|
441
|
+
Marks a class as an IPC communication handler.
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
@IpcHandler()
|
|
445
|
+
export class MyIpc implements TIpcHandlerInterface {
|
|
446
|
+
async onInit({ getWindow }: TParamOnInit) {
|
|
447
|
+
// Setup IPC listeners
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
#### `@WindowManager<T>(config)`
|
|
453
|
+
|
|
454
|
+
Defines a BrowserWindow manager.
|
|
455
|
+
|
|
456
|
+
**Parameters:**
|
|
457
|
+
|
|
458
|
+
- `hash: string` - Unique window identifier
|
|
459
|
+
- `isCache?: boolean` - Cache window instance
|
|
460
|
+
- `options: BrowserWindowConstructorOptions` - Electron window options
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
@WindowManager<TWindows["myWindow"]>({
|
|
464
|
+
hash: "window:my-window",
|
|
465
|
+
isCache: true,
|
|
466
|
+
options: { width: 800, height: 600 },
|
|
467
|
+
})
|
|
468
|
+
export class MyWindow implements TWindowManager {
|
|
469
|
+
onWebContentsDidFinishLoad(window: BrowserWindow): void {
|
|
470
|
+
// Lifecycle hook
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Core Functions
|
|
476
|
+
|
|
477
|
+
#### `initSettings(config)`
|
|
478
|
+
|
|
479
|
+
Initializes framework configuration.
|
|
480
|
+
|
|
481
|
+
**Parameters:**
|
|
482
|
+
|
|
483
|
+
- `baseRestApi: string` - Base REST API URL
|
|
484
|
+
- `localhostPort: string` - Development server port
|
|
485
|
+
- `folders: { distRenderer: string; distMain: string }` - Build output folders
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
initSettings({
|
|
489
|
+
baseRestApi: process.env.BASE_REST_API ?? "",
|
|
490
|
+
localhostPort: process.env.LOCALHOST_ELECTRON_SERVER_PORT ?? "",
|
|
491
|
+
folders: {
|
|
492
|
+
distRenderer: "dist-renderer",
|
|
493
|
+
distMain: "dist-main",
|
|
494
|
+
},
|
|
495
|
+
});
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
#### `bootstrapModules(modules[])`
|
|
499
|
+
|
|
500
|
+
Bootstraps all modules and initializes the DI container.
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
await bootstrapModules([AppModule, AuthModule, ResourcesModule]);
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
#### `getWindow<T>(hash)`
|
|
507
|
+
|
|
508
|
+
Retrieves a window instance by its hash identifier.
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
const mainWindow = getWindow<TWindows["main"]>("window:main");
|
|
512
|
+
const window = await mainWindow.create();
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
#### `destroyWindows()`
|
|
516
|
+
|
|
517
|
+
Destroys all cached windows.
|
|
518
|
+
|
|
519
|
+
```typescript
|
|
520
|
+
...
|
|
521
|
+
import { destroyWindows } from "@devisfuture/electron-modular";
|
|
522
|
+
...
|
|
523
|
+
|
|
524
|
+
app.on("before-quit", () => {
|
|
525
|
+
destroyWindows();
|
|
526
|
+
});
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Lifecycle Interfaces
|
|
530
|
+
|
|
531
|
+
#### `TIpcHandlerInterface`
|
|
532
|
+
|
|
533
|
+
Interface for IPC handlers.
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
export interface TIpcHandlerInterface {
|
|
537
|
+
onInit?(params: TParamOnInit): void | Promise<void>;
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
#### `TWindowManager`
|
|
542
|
+
|
|
543
|
+
Interface for window managers.
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
export interface TWindowManager {
|
|
547
|
+
onWebContentsDidFinishLoad?(window: BrowserWindow): void;
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
## Module Structure
|
|
554
|
+
|
|
555
|
+
Recommended file organization for a feature module:
|
|
556
|
+
|
|
557
|
+
```
|
|
558
|
+
my-feature/
|
|
559
|
+
├── module.ts # Module definition with @RgModule
|
|
560
|
+
├── service.ts # Main business logic service
|
|
561
|
+
├── ipc.ts # IPC communication handlers
|
|
562
|
+
├── window.ts # BrowserWindow manager
|
|
563
|
+
├── tokens.ts # Dependency injection tokens
|
|
564
|
+
├── types.ts # TypeScript type definitions
|
|
565
|
+
└── services/ # Additional services (optional)
|
|
566
|
+
├── helper.ts
|
|
567
|
+
└── validator.ts
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
## Best Practices
|
|
573
|
+
|
|
574
|
+
### 1. Use Providers for Cross-Module Communication
|
|
575
|
+
|
|
576
|
+
✅ **Good:**
|
|
577
|
+
|
|
578
|
+
```typescript
|
|
579
|
+
{
|
|
580
|
+
provide: AUTH_PROVIDER,
|
|
581
|
+
useFactory: (authService: AuthService): TAuthProvider => ({
|
|
582
|
+
checkAuthenticated: (window) => authService.checkAuthenticated(window),
|
|
583
|
+
logout: (window) => authService.logout(window),
|
|
584
|
+
}),
|
|
585
|
+
inject: [AuthService],
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
❌ **Bad:**
|
|
590
|
+
|
|
591
|
+
```typescript
|
|
592
|
+
// Don't export entire service
|
|
593
|
+
exports: [AuthService];
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### 2. Keep Services Focused
|
|
597
|
+
|
|
598
|
+
Each service should have a single responsibility.
|
|
599
|
+
|
|
600
|
+
✅ **Good:**
|
|
601
|
+
|
|
602
|
+
```typescript
|
|
603
|
+
@Injectable()
|
|
604
|
+
export class ResourcesService {
|
|
605
|
+
// Only handles resource data operations
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
@Injectable()
|
|
609
|
+
export class CacheWindowsService {
|
|
610
|
+
// Only handles window caching
|
|
611
|
+
}
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
### 3. Use Tokens for All Cross-Module Dependencies
|
|
615
|
+
|
|
616
|
+
✅ **Good:**
|
|
617
|
+
|
|
618
|
+
```typescript
|
|
619
|
+
export const RESOURCES_REST_API_PROVIDER = Symbol("RESOURCES_REST_API_PROVIDER");
|
|
620
|
+
|
|
621
|
+
constructor(
|
|
622
|
+
@Inject(RESOURCES_REST_API_PROVIDER) private restApiProvider
|
|
623
|
+
) {}
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
### 4. Implement Lifecycle Hooks
|
|
627
|
+
|
|
628
|
+
Use lifecycle hooks for initialization logic.
|
|
629
|
+
|
|
630
|
+
```typescript
|
|
631
|
+
@IpcHandler()
|
|
632
|
+
export class MyIpc implements TIpcHandlerInterface {
|
|
633
|
+
async onInit({ getWindow }: TParamOnInit) {
|
|
634
|
+
// Initialize IPC listeners
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
@WindowManager(config)
|
|
639
|
+
export class MyWindow implements TWindowManager {
|
|
640
|
+
onWebContentsDidFinishLoad(window: BrowserWindow): void {
|
|
641
|
+
// Initialize when content loads
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
### 5. Type Everything
|
|
647
|
+
|
|
648
|
+
Use TypeScript for all services, providers, and interfaces.
|
|
649
|
+
Decorators Reference
|
|
650
|
+
|
|
651
|
+
### `@RgModule(config)`
|
|
652
|
+
|
|
653
|
+
Defines a module.
|
|
654
|
+
|
|
655
|
+
- `imports?: Class[]` - Modules to import
|
|
656
|
+
- `providers?: Provider[]` - Services and factories
|
|
657
|
+
- `ipc?: Class[]` - IPC handler classes
|
|
658
|
+
- `windows?: Class[]` - Window manager classes
|
|
659
|
+
- `exports?: Class[]` - Providers to export
|
|
660
|
+
|
|
661
|
+
### `@Injectable()`
|
|
662
|
+
|
|
663
|
+
Makes a class injectable.
|
|
664
|
+
|
|
665
|
+
```typescript
|
|
666
|
+
@Injectable()
|
|
667
|
+
export class MyService {}
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
### `@Inject(token)`
|
|
671
|
+
|
|
672
|
+
Injects a dependency by token.
|
|
673
|
+
|
|
674
|
+
```typescript
|
|
675
|
+
constructor(@Inject(MY_PROVIDER) private provider: TMyProvider) {}
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
### `@IpcHandler()`
|
|
679
|
+
|
|
680
|
+
Marks a class as IPC handler.
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
@IpcHandler()
|
|
684
|
+
export class MyIpc implements TIpcHandlerInterface {}
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
### `@WindowManager<T>(config)`
|
|
688
|
+
|
|
689
|
+
Defines a window manager.
|
|
690
|
+
|
|
691
|
+
```typescript
|
|
692
|
+
@WindowManager<TWindows["myWindow"]>({
|
|
693
|
+
hash: "window:my-window",
|
|
694
|
+
isCache: true,
|
|
695
|
+
options: { width: 800, height: 600 },
|
|
696
|
+
})
|
|
697
|
+
export class MyWindow implements TWindowManager {}
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
---
|
|
701
|
+
|
|
702
|
+
## Key Functions
|
|
703
|
+
|
|
704
|
+
### `initSettings(config)`
|
|
705
|
+
|
|
706
|
+
```typescript
|
|
707
|
+
initSettings({
|
|
708
|
+
baseRestApi: process.env.BASE_REST_API ?? "",
|
|
709
|
+
localhostPort: process.env.LOCALHOST_ELECTRON_SERVER_PORT ?? "",
|
|
710
|
+
folders: { distRenderer: "dist-renderer", distMain: "dist-main" },
|
|
711
|
+
});
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
### `bootstrapModules(modules[])`
|
|
715
|
+
|
|
716
|
+
```typescript
|
|
717
|
+
await bootstrapModules([AppModule, UserModule]);
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
### `getWindow<T>(hash)`
|
|
721
|
+
|
|
722
|
+
```typescript
|
|
723
|
+
const mainWindow = getWindow<TWindows["main"]>("window:main");
|
|
724
|
+
const window = await mainWindow.create();
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
### `destroyWindows()`
|
|
728
|
+
|
|
729
|
+
```typescript
|
|
730
|
+
app.on("before-quit", () => destroyWindows());
|
|
731
|
+
```
|