@electron-ipc-bridge/vite-plugin 0.0.4

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +54 -0
  3. package/dist/constants.d.ts +5 -0
  4. package/dist/generate-ipc.d.ts +9 -0
  5. package/dist/generator/build-namespaces.d.ts +2 -0
  6. package/dist/generator/build-type-definitions.d.ts +2 -0
  7. package/dist/generator/generate-global-types.d.ts +1 -0
  8. package/dist/generator/generate-runtime-types.d.ts +2 -0
  9. package/dist/generator/generate-types.d.ts +2 -0
  10. package/dist/generator/get-return-type.d.ts +1 -0
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +1158 -0
  13. package/dist/index.js.map +7 -0
  14. package/dist/normalize-path.d.ts +1 -0
  15. package/dist/parser/ast-utils.d.ts +3 -0
  16. package/dist/parser/collect-dependencies.d.ts +2 -0
  17. package/dist/parser/collect-external-type-references.d.ts +2 -0
  18. package/dist/parser/constants.d.ts +1 -0
  19. package/dist/parser/extract-metadata.d.ts +3 -0
  20. package/dist/parser/extract-type.d.ts +4 -0
  21. package/dist/parser/find-controllers.d.ts +9 -0
  22. package/dist/parser/find-nest-root-module.d.ts +6 -0
  23. package/dist/parser/get-decorator.d.ts +2 -0
  24. package/dist/parser/is-truncated-type-string.d.ts +2 -0
  25. package/dist/parser/parse-controller.d.ts +3 -0
  26. package/dist/parser/parse-method.d.ts +3 -0
  27. package/dist/parser/process-create-ipc-app-call.d.ts +4 -0
  28. package/dist/parser/resolve-controller.d.ts +3 -0
  29. package/dist/parser/resolve-controllers-from-array.d.ts +3 -0
  30. package/dist/parser/resolve-controllers-from-module-class.d.ts +3 -0
  31. package/dist/parser/resolve-controllers-from-module.d.ts +3 -0
  32. package/dist/parser/resolve-controllers-from-nest-root.d.ts +6 -0
  33. package/dist/parser/resolve-return-type.d.ts +3 -0
  34. package/dist/parser/types.d.ts +28 -0
  35. package/dist/plugin-state.d.ts +17 -0
  36. package/dist/plugin.d.ts +21 -0
  37. package/dist/preload/resolve-api-root.d.ts +4 -0
  38. package/dist/resolve-type-paths.d.ts +12 -0
  39. package/dist/strategies/nest.d.ts +7 -0
  40. package/dist/strategies/types.d.ts +12 -0
  41. package/dist/types.d.ts +24 -0
  42. package/dist/utils/dependency-collector.d.ts +6 -0
  43. package/package.json +43 -0
package/dist/index.js ADDED
@@ -0,0 +1,1158 @@
1
+ // src/plugin.ts
2
+ import path5 from "path";
3
+ import { createLogger } from "vite";
4
+
5
+ // package.json
6
+ var package_default = {
7
+ name: "@electron-ipc-bridge/vite-plugin",
8
+ version: "0.0.4",
9
+ publishConfig: {
10
+ access: "public"
11
+ },
12
+ repository: {
13
+ type: "git",
14
+ url: "https://github.com/metacurb/electron-ipc-bridge.git"
15
+ },
16
+ bugs: {
17
+ url: "https://github.com/metacurb/electron-ipc-bridge/issues"
18
+ },
19
+ homepage: "https://metacurb.github.io/electron-ipc-bridge/",
20
+ description: "A Vite plugin for Electron-IPC-Bridge",
21
+ type: "module",
22
+ main: "dist/index.js",
23
+ types: "dist/index.d.ts",
24
+ exports: {
25
+ ".": {
26
+ types: "./dist/index.d.ts",
27
+ import: "./dist/index.js"
28
+ }
29
+ },
30
+ files: [
31
+ "dist"
32
+ ],
33
+ scripts: {
34
+ build: "node scripts/build.mjs",
35
+ test: "pnpm exec jest --config jest.config.cjs"
36
+ },
37
+ peerDependencies: {
38
+ typescript: "^5.0.0",
39
+ vite: "^5.0.0 || ^6.0.0 || ^7.0.0"
40
+ },
41
+ devDependencies: {
42
+ "@electron-ipc-bridge/shared": "workspace:*",
43
+ "@types/node": "^25.2.3",
44
+ esbuild: "^0.27.3",
45
+ typescript: "^5.9.3",
46
+ vite: "^7.3.1"
47
+ }
48
+ };
49
+
50
+ // src/constants.ts
51
+ var DEFAULT_GLOBAL_TYPES_FILENAME = "ipc.d.ts";
52
+ var DEFAULT_MAIN_ENTRY = "src/main/index.ts";
53
+ var DEFAULT_PRELOAD_ENTRY = "src/preload/index.ts";
54
+ var DEFAULT_RENDERER_RUNTIME_DIR = "src/renderer/src";
55
+ var DEFAULT_RUNTIME_TYPES_FILENAME = "ipc.types.ts";
56
+
57
+ // src/generate-ipc.ts
58
+ import crypto from "crypto";
59
+ import fs2 from "fs";
60
+ import path4 from "path";
61
+
62
+ // ../shared/dist/constants.js
63
+ var IPC_DEFAULT_API_ROOT = "ipc";
64
+ var IPC_DECORATOR_ON = "IpcOn";
65
+ var IPC_DECORATOR_ONCE = "IpcOnce";
66
+ var IPC_DECORATOR_HANDLE = "IpcHandle";
67
+ var IPC_DECORATOR_HANDLE_ONCE = "IpcHandleOnce";
68
+ var IPC_METHOD_DECORATOR_NAMES = [
69
+ IPC_DECORATOR_HANDLE,
70
+ IPC_DECORATOR_ON,
71
+ IPC_DECORATOR_HANDLE_ONCE,
72
+ IPC_DECORATOR_ONCE
73
+ ];
74
+ var IPC_PARAM_INJECTION_DECORATOR_NAMES = [
75
+ "Channel",
76
+ "CorrelationId",
77
+ "Origin",
78
+ "ProcessId",
79
+ "RawEvent",
80
+ "Sender",
81
+ "Window"
82
+ ];
83
+
84
+ // ../shared/dist/utils/to-snake-case.js
85
+ var toSnakeCase = (str) => str.trim().replace(/[\s-]+/g, "_").replace(/(?<=[A-Z])(?=[A-Z][a-z])/g, "_").replace(/([a-z\d])([A-Z])/g, "$1_$2").toLocaleLowerCase();
86
+
87
+ // ../shared/dist/utils/to-camel-case.js
88
+ var toCamelCase = (str) => toSnakeCase(str).replace(/_([a-z0-9])/g, (_, c) => c.toLocaleUpperCase()).replace(/_/g, "").replace(/^./, (s) => s.toLocaleLowerCase());
89
+
90
+ // ../shared/dist/utils/derive-namespace.js
91
+ var deriveNamespace = (name) => toCamelCase(name.replace(/Controller$/, ""));
92
+
93
+ // src/generator/generate-global-types.ts
94
+ var generateGlobalTypes = (namespace = IPC_DEFAULT_API_ROOT, ipcApiImportPath) => {
95
+ const isValidIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(namespace);
96
+ const windowProperty = isValidIdentifier ? `${namespace}: IpcApi;` : `[${JSON.stringify(namespace)}]: IpcApi;`;
97
+ return `// Auto-generated by @electron-ipc-bridge/vite-plugin
98
+ // Do not edit manually
99
+
100
+ import type { IpcApi } from "${ipcApiImportPath}";
101
+
102
+ declare global {
103
+ interface Window {
104
+ ${windowProperty}
105
+ }
106
+ }
107
+
108
+ export {};
109
+ `;
110
+ };
111
+
112
+ // src/generator/get-return-type.ts
113
+ var getReturnType = (decorator, originalReturnType) => {
114
+ if (decorator === IPC_DECORATOR_ON || decorator === IPC_DECORATOR_ONCE) {
115
+ return "void";
116
+ }
117
+ if (originalReturnType.startsWith("Promise<")) {
118
+ return originalReturnType;
119
+ }
120
+ return `Promise<${originalReturnType}>`;
121
+ };
122
+
123
+ // src/generator/build-namespaces.ts
124
+ var buildNamespaces = (controllers) => {
125
+ return controllers.map((controller) => {
126
+ const methods = controller.methods.map((method) => {
127
+ const params = method.params.map((p) => `${p.name}${p.optional ? "?" : ""}: ${p.type}`).join(", ");
128
+ const returnType = getReturnType(method.decoratorName, method.returnType);
129
+ return `${method.name}(${params}): ${returnType};`;
130
+ });
131
+ return ` ${controller.namespace}: {
132
+ ${methods.join("\n ")}
133
+ };`;
134
+ });
135
+ };
136
+
137
+ // src/generator/build-type-definitions.ts
138
+ var buildTypeDefinitions = (controllers) => {
139
+ const referencedTypes = /* @__PURE__ */ new Map();
140
+ const collectTypes = (types) => {
141
+ types.forEach((t) => {
142
+ if (t.definition && !referencedTypes.has(t.name)) {
143
+ referencedTypes.set(t.name, t.definition);
144
+ collectTypes(t.referencedTypes);
145
+ }
146
+ });
147
+ };
148
+ controllers.forEach((c) => collectTypes(c.referencedTypes));
149
+ return Array.from(referencedTypes.entries()).sort((a, b) => a[0].localeCompare(b[0])).map(([, def]) => `export ${def}`).join("\n\n");
150
+ };
151
+
152
+ // src/generator/generate-runtime-types.ts
153
+ var generateRuntimeTypes = (controllers) => {
154
+ const referenceTypes = Array.from(new Set(controllers.flatMap((c) => c.requiredReferenceTypes ?? []))).sort();
155
+ const referenceDirectives = referenceTypes.map((refType) => `/// <reference types="${refType}" />`).join("\n");
156
+ const typeDefinitions = buildTypeDefinitions(controllers);
157
+ const namespaces = buildNamespaces(controllers);
158
+ const sections = ["// Auto-generated by @electron-ipc-bridge/vite-plugin", "// Do not edit manually", ""];
159
+ if (referenceDirectives) {
160
+ sections.push(referenceDirectives, "");
161
+ }
162
+ if (typeDefinitions) {
163
+ sections.push(typeDefinitions, "");
164
+ }
165
+ return `${sections.join("\n")}
166
+ export interface IpcApi {
167
+ ${namespaces.join("\n")}
168
+ }
169
+ `;
170
+ };
171
+
172
+ // src/normalize-path.ts
173
+ var normalizePath = (p) => p.replace(/\\/g, "/");
174
+
175
+ // src/parser/find-controllers.ts
176
+ import path2 from "path";
177
+ import {
178
+ createProgram,
179
+ findConfigFile,
180
+ forEachChild as forEachChild4,
181
+ isCallExpression as isCallExpression8,
182
+ isIdentifier as isIdentifier9,
183
+ parseJsonConfigFileContent,
184
+ readConfigFile,
185
+ sys
186
+ } from "typescript";
187
+
188
+ // src/strategies/nest.ts
189
+ import {
190
+ forEachChild as forEachChild3,
191
+ isCallExpression as isCallExpression6,
192
+ isClassDeclaration as isClassDeclaration4,
193
+ isIdentifier as isIdentifier5,
194
+ isPropertyAccessExpression
195
+ } from "typescript";
196
+
197
+ // src/parser/resolve-controllers-from-nest-root.ts
198
+ import path from "path";
199
+ import {
200
+ getDecorators as getDecorators3,
201
+ isArrayLiteralExpression as isArrayLiteralExpression2,
202
+ isArrowFunction,
203
+ isBlock,
204
+ isCallExpression as isCallExpression5,
205
+ isClassDeclaration as isClassDeclaration3,
206
+ isIdentifier as isIdentifier4,
207
+ isObjectLiteralExpression as isObjectLiteralExpression2,
208
+ isPropertyAssignment as isPropertyAssignment2,
209
+ isReturnStatement
210
+ } from "typescript";
211
+
212
+ // src/parser/resolve-controllers-from-module-class.ts
213
+ import {
214
+ getDecorators as getDecorators2,
215
+ isArrayLiteralExpression,
216
+ isCallExpression as isCallExpression4,
217
+ isIdentifier as isIdentifier3,
218
+ isObjectLiteralExpression,
219
+ isPropertyAssignment
220
+ } from "typescript";
221
+
222
+ // src/parser/resolve-controller.ts
223
+ import { isClassDeclaration as isClassDeclaration2 } from "typescript";
224
+
225
+ // src/parser/collect-dependencies.ts
226
+ var collectDependencies = (referencedTypes, processedFiles) => {
227
+ const visited = /* @__PURE__ */ new Set();
228
+ const visit = (types) => {
229
+ for (const type of types) {
230
+ if (visited.has(type)) {
231
+ continue;
232
+ }
233
+ visited.add(type);
234
+ if (type.sourceFile) {
235
+ processedFiles.add(type.sourceFile);
236
+ }
237
+ visit(type.referencedTypes);
238
+ }
239
+ };
240
+ visit(referencedTypes);
241
+ };
242
+
243
+ // src/parser/extract-metadata.ts
244
+ import { forEachChild as forEachChild2, isClassDeclaration } from "typescript";
245
+
246
+ // src/parser/get-decorator.ts
247
+ import { canHaveDecorators, getDecorators, isCallExpression, isIdentifier } from "typescript";
248
+ var getDecorator = (node, name) => {
249
+ if (!canHaveDecorators(node)) return void 0;
250
+ const decorators = getDecorators(node);
251
+ if (!decorators) return void 0;
252
+ return decorators.find(
253
+ (m) => isCallExpression(m.expression) && isIdentifier(m.expression.expression) && m.expression.expression.text === name
254
+ );
255
+ };
256
+
257
+ // src/parser/parse-controller.ts
258
+ import {
259
+ isCallExpression as isCallExpression3,
260
+ isIdentifier as isIdentifier2,
261
+ isMethodDeclaration,
262
+ isStringLiteral as isStringLiteral2
263
+ } from "typescript";
264
+
265
+ // src/parser/parse-method.ts
266
+ import { isCallExpression as isCallExpression2, isStringLiteral } from "typescript";
267
+
268
+ // src/parser/collect-external-type-references.ts
269
+ import {
270
+ isEnumDeclaration,
271
+ isInterfaceDeclaration,
272
+ isTypeAliasDeclaration
273
+ } from "typescript";
274
+ var NODE_TYPES_PATH_RE = /[\\/]@types[\\/]node[\\/]/i;
275
+ var pickDeclaration = (symbol) => symbol?.getDeclarations()?.find((d) => isTypeAliasDeclaration(d) || isInterfaceDeclaration(d) || isEnumDeclaration(d));
276
+ var resolveDeclarationFromSymbol = (symbol, typeChecker) => {
277
+ let decl = pickDeclaration(symbol);
278
+ if (!decl && symbol) {
279
+ try {
280
+ const aliased = typeChecker.getAliasedSymbol(symbol);
281
+ const aliasedDecl = pickDeclaration(aliased);
282
+ if (aliasedDecl) {
283
+ decl = aliasedDecl;
284
+ }
285
+ } catch {
286
+ }
287
+ }
288
+ return decl;
289
+ };
290
+ var addReferenceTypeIfExternal = (symbol, typeChecker, out) => {
291
+ const decl = resolveDeclarationFromSymbol(symbol, typeChecker);
292
+ if (!decl) return;
293
+ const sourceFile = decl.getSourceFile();
294
+ if (NODE_TYPES_PATH_RE.test(sourceFile.fileName)) {
295
+ out.add("node");
296
+ }
297
+ };
298
+ var walkTypeForExternalRefs = (type, typeChecker, seen, out) => {
299
+ if (seen.has(type)) return;
300
+ seen.add(type);
301
+ addReferenceTypeIfExternal(type.getSymbol(), typeChecker, out);
302
+ const typeWithAlias = type;
303
+ addReferenceTypeIfExternal(typeWithAlias.aliasSymbol, typeChecker, out);
304
+ const typeWithNested = type;
305
+ typeWithNested.aliasTypeArguments?.forEach((arg) => walkTypeForExternalRefs(arg, typeChecker, seen, out));
306
+ typeWithNested.typeArguments?.forEach((arg) => walkTypeForExternalRefs(arg, typeChecker, seen, out));
307
+ typeWithNested.types?.forEach((nested) => walkTypeForExternalRefs(nested, typeChecker, seen, out));
308
+ };
309
+ var collectExternalTypeReferencesFromType = (type, typeChecker, out = /* @__PURE__ */ new Set()) => {
310
+ walkTypeForExternalRefs(type, typeChecker, /* @__PURE__ */ new Set(), out);
311
+ return out;
312
+ };
313
+
314
+ // src/parser/extract-type.ts
315
+ import {
316
+ createPrinter,
317
+ EmitHint,
318
+ forEachChild,
319
+ isEnumDeclaration as isEnumDeclaration2,
320
+ isInterfaceDeclaration as isInterfaceDeclaration2,
321
+ isTypeAliasDeclaration as isTypeAliasDeclaration2,
322
+ isTypeReferenceNode
323
+ } from "typescript";
324
+
325
+ // src/parser/constants.ts
326
+ var BUILTIN_TYPE_NAMES = /* @__PURE__ */ new Set([
327
+ "Promise",
328
+ "Array",
329
+ "Map",
330
+ "Set",
331
+ "WeakMap",
332
+ "WeakSet",
333
+ "Date",
334
+ "RegExp",
335
+ "Function",
336
+ "Symbol",
337
+ "Error",
338
+ "Record",
339
+ "Partial",
340
+ "Required",
341
+ "Readonly",
342
+ "Pick",
343
+ "Omit",
344
+ "Exclude",
345
+ "Extract",
346
+ "NonNullable",
347
+ "ReturnType",
348
+ "Parameters",
349
+ "InstanceType",
350
+ "ConstructorParameters",
351
+ "__type",
352
+ "undefined"
353
+ ]);
354
+
355
+ // src/parser/extract-type.ts
356
+ var printer = createPrinter({ removeComments: true });
357
+ var collectTypeDefinitions = (node, typeChecker, seen = /* @__PURE__ */ new Set()) => {
358
+ const result = [];
359
+ walkForTypeRefs(node, typeChecker, seen, result);
360
+ return result;
361
+ };
362
+ var collectTypeDefinitionsFromType = (type, typeChecker, seen = /* @__PURE__ */ new Set()) => {
363
+ const result = [];
364
+ walkTypeForTypeRefs(type, typeChecker, seen, result);
365
+ return result;
366
+ };
367
+ var pickDeclaration2 = (symbol) => symbol?.getDeclarations()?.find((d) => isTypeAliasDeclaration2(d) || isInterfaceDeclaration2(d) || isEnumDeclaration2(d));
368
+ var resolveDeclarationFromSymbol2 = (symbol, typeChecker) => {
369
+ let decl = pickDeclaration2(symbol);
370
+ if (!decl && symbol) {
371
+ try {
372
+ const aliased = typeChecker.getAliasedSymbol(symbol);
373
+ const aliasedDecl = pickDeclaration2(aliased);
374
+ if (aliasedDecl) {
375
+ decl = aliasedDecl;
376
+ }
377
+ } catch {
378
+ }
379
+ }
380
+ return decl;
381
+ };
382
+ var addDefinitionFromDeclaration = (decl, typeChecker, seen, out) => {
383
+ if (!decl || !(isTypeAliasDeclaration2(decl) || isInterfaceDeclaration2(decl) || isEnumDeclaration2(decl))) {
384
+ return;
385
+ }
386
+ const name = decl.name.text;
387
+ if (BUILTIN_TYPE_NAMES.has(name) || seen.has(name)) {
388
+ return;
389
+ }
390
+ seen.add(name);
391
+ const sourceFile = decl.getSourceFile();
392
+ if (sourceFile.fileName.includes("node_modules")) {
393
+ return;
394
+ }
395
+ const referencedTypes = [];
396
+ walkForTypeRefs(decl, typeChecker, seen, referencedTypes);
397
+ const definition = printer.printNode(EmitHint.Unspecified, decl, sourceFile).replace(/^\s*export\s+/, "").replace(/^\s*declare\s+/, "").trim();
398
+ if (definition) {
399
+ out.push({ definition, name, referencedTypes, sourceFile: sourceFile.fileName });
400
+ }
401
+ };
402
+ var walkForTypeRefs = (node, typeChecker, seen, out) => {
403
+ if (isTypeReferenceNode(node)) {
404
+ const sym = typeChecker.getSymbolAtLocation(node.typeName);
405
+ addDefinitionFromDeclaration(resolveDeclarationFromSymbol2(sym, typeChecker), typeChecker, seen, out);
406
+ if (node.typeArguments) {
407
+ node.typeArguments.forEach((arg) => walkForTypeRefs(arg, typeChecker, seen, out));
408
+ }
409
+ return;
410
+ }
411
+ forEachChild(node, (child) => walkForTypeRefs(child, typeChecker, seen, out));
412
+ };
413
+ var walkTypeForTypeRefs = (type, typeChecker, seen, out) => {
414
+ addDefinitionFromDeclaration(resolveDeclarationFromSymbol2(type.getSymbol(), typeChecker), typeChecker, seen, out);
415
+ const typeWithAlias = type;
416
+ addDefinitionFromDeclaration(
417
+ resolveDeclarationFromSymbol2(typeWithAlias.aliasSymbol, typeChecker),
418
+ typeChecker,
419
+ seen,
420
+ out
421
+ );
422
+ const typeWithNested = type;
423
+ typeWithNested.aliasTypeArguments?.forEach((arg) => walkTypeForTypeRefs(arg, typeChecker, seen, out));
424
+ typeWithNested.typeArguments?.forEach((arg) => walkTypeForTypeRefs(arg, typeChecker, seen, out));
425
+ typeWithNested.types?.forEach((nested) => walkTypeForTypeRefs(nested, typeChecker, seen, out));
426
+ };
427
+
428
+ // src/parser/resolve-return-type.ts
429
+ import { createPrinter as createPrinter2, EmitHint as EmitHint2, TypeFormatFlags } from "typescript";
430
+
431
+ // src/parser/is-truncated-type-string.ts
432
+ var isTruncatedTypeString = (s) => /\.\.\.\s+\d+\s+more\s+\.\.\./.test(s);
433
+
434
+ // src/parser/resolve-return-type.ts
435
+ var printer2 = createPrinter2();
436
+ var resolveReturnType = (node, signature, typeChecker) => {
437
+ if (node.type) {
438
+ return printer2.printNode(EmitHint2.Unspecified, node.type, node.getSourceFile()).trim();
439
+ }
440
+ const inferred = signature != null ? typeChecker.typeToString(
441
+ signature.getReturnType(),
442
+ void 0,
443
+ TypeFormatFlags.NoTruncation | TypeFormatFlags.UseFullyQualifiedType
444
+ ) : "unknown";
445
+ if (isTruncatedTypeString(inferred)) return "unknown";
446
+ return inferred.replace(/import\("(?:\.|\/|[a-zA-Z]:).*?"\)\./g, "");
447
+ };
448
+
449
+ // src/parser/parse-method.ts
450
+ var parseMethod = (node, typeChecker) => {
451
+ let found = null;
452
+ let decorator = null;
453
+ for (const d of IPC_METHOD_DECORATOR_NAMES) {
454
+ const dec = getDecorator(node, d);
455
+ if (dec) {
456
+ found = d;
457
+ decorator = dec;
458
+ break;
459
+ }
460
+ }
461
+ if (!found || !decorator) return null;
462
+ let name = node.name.text;
463
+ if (isCallExpression2(decorator.expression)) {
464
+ const args = decorator.expression.arguments;
465
+ if (args.length > 0 && isStringLiteral(args[0])) {
466
+ name = args[0].text;
467
+ }
468
+ }
469
+ const signature = typeChecker.getSignatureFromDeclaration(node);
470
+ const returnType = resolveReturnType(node, signature ?? void 0, typeChecker);
471
+ const referencedTypes = [];
472
+ const requiredReferenceTypes = /* @__PURE__ */ new Set();
473
+ const seen = /* @__PURE__ */ new Set();
474
+ const paramInfos = node.parameters.map((param) => {
475
+ const hasInjection = IPC_PARAM_INJECTION_DECORATOR_NAMES.some((d) => getDecorator(param, d));
476
+ return { hasInjection, param };
477
+ });
478
+ const params = [];
479
+ for (const { hasInjection, param } of paramInfos) {
480
+ const type = typeChecker.getTypeAtLocation(param);
481
+ const typeString = typeChecker.typeToString(type);
482
+ const extracted = !hasInjection && param.type ? collectTypeDefinitions(param.type, typeChecker, seen) : [];
483
+ if (!hasInjection) {
484
+ collectExternalTypeReferencesFromType(type, typeChecker, requiredReferenceTypes);
485
+ }
486
+ referencedTypes.push(...extracted);
487
+ params.push({
488
+ name: param.name.getText(),
489
+ optional: !!param.questionToken || !!param.initializer,
490
+ type: typeString
491
+ });
492
+ }
493
+ const filteredParams = params.filter((_, index) => !paramInfos[index].hasInjection);
494
+ if (node.type) {
495
+ const returnTypeRefs = collectTypeDefinitions(node.type, typeChecker, seen);
496
+ referencedTypes.push(...returnTypeRefs);
497
+ } else if (signature) {
498
+ const returnTypeRefs = collectTypeDefinitionsFromType(signature.getReturnType(), typeChecker, seen);
499
+ referencedTypes.push(...returnTypeRefs);
500
+ }
501
+ if (signature && found !== IPC_DECORATOR_ON && found !== IPC_DECORATOR_ONCE) {
502
+ collectExternalTypeReferencesFromType(signature.getReturnType(), typeChecker, requiredReferenceTypes);
503
+ }
504
+ return {
505
+ decoratorName: found,
506
+ name,
507
+ params: filteredParams,
508
+ referencedTypes,
509
+ requiredReferenceTypes: Array.from(requiredReferenceTypes).sort(),
510
+ returnType
511
+ };
512
+ };
513
+
514
+ // src/parser/parse-controller.ts
515
+ var parseController = (node, decorator, sourceFile, typeChecker) => {
516
+ const className = node.name.text;
517
+ let namespace = deriveNamespace(className);
518
+ if (isCallExpression3(decorator.expression)) {
519
+ const args = decorator.expression.arguments;
520
+ if (args.length > 0 && isStringLiteral2(args[0])) {
521
+ namespace = args[0].text;
522
+ }
523
+ }
524
+ const methods = [];
525
+ node.members.forEach((member) => {
526
+ if (isMethodDeclaration(member) && member.name && isIdentifier2(member.name)) {
527
+ const methodMeta = parseMethod(member, typeChecker);
528
+ if (methodMeta) {
529
+ methods.push(methodMeta);
530
+ }
531
+ }
532
+ });
533
+ const referencedTypes = methods.flatMap((m) => m.referencedTypes);
534
+ const requiredReferenceTypes = Array.from(new Set(methods.flatMap((m) => m.requiredReferenceTypes ?? []))).sort();
535
+ return {
536
+ className,
537
+ filePath: sourceFile.fileName,
538
+ methods,
539
+ namespace,
540
+ referencedTypes,
541
+ requiredReferenceTypes
542
+ };
543
+ };
544
+
545
+ // src/parser/extract-metadata.ts
546
+ var extractControllerMetadata = (sourceFile, typeChecker) => {
547
+ const controllers = [];
548
+ forEachChild2(sourceFile, (node) => {
549
+ if (isClassDeclaration(node)) {
550
+ const decorator = getDecorator(node, "IpcController");
551
+ if (decorator) {
552
+ const controller = parseController(node, decorator, sourceFile, typeChecker);
553
+ if (controller) {
554
+ controllers.push(controller);
555
+ }
556
+ }
557
+ }
558
+ });
559
+ return controllers;
560
+ };
561
+
562
+ // src/parser/resolve-controller.ts
563
+ var resolveController = (node, typeChecker, processedFiles, controllers, fileCache) => {
564
+ const symbol = typeChecker.getSymbolAtLocation(node);
565
+ if (!symbol) return;
566
+ let targetSymbol = symbol;
567
+ try {
568
+ const aliasedSymbol = typeChecker.getAliasedSymbol(symbol);
569
+ targetSymbol = aliasedSymbol || symbol;
570
+ } catch {
571
+ }
572
+ if (!targetSymbol.declarations || targetSymbol.declarations.length === 0) return;
573
+ const targetDecl = targetSymbol.declarations[0];
574
+ if (!isClassDeclaration2(targetDecl)) return;
575
+ const sourceFile = targetDecl.getSourceFile();
576
+ const fileName = sourceFile.fileName;
577
+ const className = targetDecl.name?.text;
578
+ if (!className) return;
579
+ let fileControllers = fileCache.get(fileName);
580
+ if (fileControllers === void 0) {
581
+ fileControllers = extractControllerMetadata(sourceFile, typeChecker);
582
+ fileCache.set(fileName, fileControllers);
583
+ processedFiles.add(fileName);
584
+ }
585
+ const match = fileControllers.find((c) => c.className === className);
586
+ if (match) {
587
+ controllers.push(match);
588
+ collectDependencies(match.referencedTypes, processedFiles);
589
+ }
590
+ };
591
+
592
+ // src/parser/resolve-controllers-from-module-class.ts
593
+ var resolveControllersFromModuleClass = (targetDecl, typeChecker, processedFiles, controllers, fileCache) => {
594
+ const decorators = getDecorators2(targetDecl);
595
+ if (!decorators) return;
596
+ for (const decorator of decorators) {
597
+ if (!isCallExpression4(decorator.expression)) continue;
598
+ const moduleArgs = decorator.expression.arguments;
599
+ const moduleOptions = moduleArgs[0];
600
+ if (!moduleOptions || !isObjectLiteralExpression(moduleOptions)) continue;
601
+ const properties = moduleOptions.properties;
602
+ for (const prop of properties) {
603
+ if (!isPropertyAssignment(prop) || !isIdentifier3(prop.name)) continue;
604
+ if (prop.name.text === "providers" || prop.name.text === "controllers") {
605
+ if (!isArrayLiteralExpression(prop.initializer)) continue;
606
+ const providerElements = prop.initializer.elements;
607
+ providerElements.forEach((element) => {
608
+ resolveController(element, typeChecker, processedFiles, controllers, fileCache);
609
+ });
610
+ }
611
+ }
612
+ }
613
+ };
614
+
615
+ // src/parser/resolve-controllers-from-nest-root.ts
616
+ var normalizePathSafe = (p) => path.normalize(p).replace(/\\/g, "/");
617
+ function resolveImportElementToClass(element, typeChecker) {
618
+ if (isIdentifier4(element)) {
619
+ const symbol = typeChecker.getSymbolAtLocation(element);
620
+ if (!symbol) return void 0;
621
+ let target = symbol;
622
+ try {
623
+ const aliased = typeChecker.getAliasedSymbol(symbol);
624
+ if (aliased) target = aliased;
625
+ } catch {
626
+ }
627
+ const decl = target.declarations?.[0];
628
+ return decl && isClassDeclaration3(decl) ? decl : void 0;
629
+ }
630
+ if (isCallExpression5(element)) {
631
+ const firstArg = element.arguments[0];
632
+ if (!firstArg) return void 0;
633
+ if (isArrowFunction(firstArg)) {
634
+ const body = firstArg.body;
635
+ if (isIdentifier4(body)) return resolveImportElementToClass(body, typeChecker);
636
+ if (isBlock(body)) {
637
+ const ret = body.statements.find((s) => isReturnStatement(s));
638
+ if (ret && isReturnStatement(ret) && ret.expression)
639
+ return resolveImportElementToClass(ret.expression, typeChecker);
640
+ }
641
+ }
642
+ }
643
+ return void 0;
644
+ }
645
+ var resolveControllersFromNestRoot = (targetDecl, typeChecker, processedFiles, controllers, fileCache, visitedModules) => {
646
+ const sourceFile = targetDecl.getSourceFile();
647
+ const filePath = normalizePathSafe(sourceFile.fileName);
648
+ if (visitedModules.has(filePath)) return;
649
+ visitedModules.add(filePath);
650
+ resolveControllersFromModuleClass(targetDecl, typeChecker, processedFiles, controllers, fileCache);
651
+ const decorators = getDecorators3(targetDecl);
652
+ if (!decorators) return;
653
+ for (const decorator of decorators) {
654
+ if (!isCallExpression5(decorator.expression)) continue;
655
+ const moduleArgs = decorator.expression.arguments;
656
+ const moduleOptions = moduleArgs[0];
657
+ if (!moduleOptions || !isObjectLiteralExpression2(moduleOptions)) continue;
658
+ const importsProp = moduleOptions.properties.find(
659
+ (p) => isPropertyAssignment2(p) && isIdentifier4(p.name) && p.name.text === "imports"
660
+ );
661
+ if (!importsProp || !isArrayLiteralExpression2(importsProp.initializer)) continue;
662
+ for (const element of importsProp.initializer.elements) {
663
+ const importClass = resolveImportElementToClass(element, typeChecker);
664
+ if (importClass) {
665
+ resolveControllersFromNestRoot(
666
+ importClass,
667
+ typeChecker,
668
+ processedFiles,
669
+ controllers,
670
+ fileCache,
671
+ visitedModules
672
+ );
673
+ }
674
+ }
675
+ break;
676
+ }
677
+ };
678
+
679
+ // src/strategies/nest.ts
680
+ var findNestRootModule = (sourceFile, typeChecker) => {
681
+ let found;
682
+ const visit = (node) => {
683
+ if (found) return;
684
+ if (!isCallExpression6(node)) {
685
+ forEachChild3(node, visit);
686
+ return;
687
+ }
688
+ const expr = node.expression;
689
+ if (!isPropertyAccessExpression(expr) || expr.name.text !== "createApplicationContext") {
690
+ forEachChild3(node, visit);
691
+ return;
692
+ }
693
+ const firstArg = node.arguments[0];
694
+ if (!firstArg || !isIdentifier5(firstArg)) {
695
+ forEachChild3(node, visit);
696
+ return;
697
+ }
698
+ const symbol = typeChecker.getSymbolAtLocation(firstArg);
699
+ if (!symbol) {
700
+ forEachChild3(node, visit);
701
+ return;
702
+ }
703
+ let targetSymbol = symbol;
704
+ try {
705
+ const aliased = typeChecker.getAliasedSymbol(symbol);
706
+ if (aliased) targetSymbol = aliased;
707
+ } catch {
708
+ }
709
+ const decl = targetSymbol.declarations?.[0];
710
+ if (decl && isClassDeclaration4(decl)) {
711
+ found = decl;
712
+ return;
713
+ }
714
+ forEachChild3(node, visit);
715
+ };
716
+ forEachChild3(sourceFile, visit);
717
+ return found;
718
+ };
719
+ var nestResolutionStrategy = (context) => {
720
+ const { fileCache, processedFiles, sourceFile, typeChecker } = context;
721
+ const rootModule = findNestRootModule(sourceFile, typeChecker);
722
+ if (!rootModule) return [];
723
+ const controllers = [];
724
+ const visitedModules = /* @__PURE__ */ new Set();
725
+ resolveControllersFromNestRoot(rootModule, typeChecker, processedFiles, controllers, fileCache, visitedModules);
726
+ return controllers;
727
+ };
728
+
729
+ // src/parser/process-create-ipc-app-call.ts
730
+ import {
731
+ isArrayLiteralExpression as isArrayLiteralExpression3,
732
+ isCallExpression as isCallExpression7,
733
+ isClassDeclaration as isClassDeclaration6,
734
+ isIdentifier as isIdentifier8,
735
+ isObjectLiteralExpression as isObjectLiteralExpression3,
736
+ isPropertyAssignment as isPropertyAssignment3
737
+ } from "typescript";
738
+
739
+ // src/parser/ast-utils.ts
740
+ import {
741
+ isAsExpression,
742
+ isAwaitExpression,
743
+ isIdentifier as isIdentifier6,
744
+ isNonNullExpression,
745
+ isParenthesizedExpression,
746
+ isSatisfiesExpression,
747
+ isTypeAssertionExpression,
748
+ isVariableDeclaration
749
+ } from "typescript";
750
+ var unwrapExpression = (expr) => {
751
+ let current = expr;
752
+ while (true) {
753
+ if (isAwaitExpression(current)) {
754
+ current = current.expression;
755
+ continue;
756
+ }
757
+ if (isParenthesizedExpression(current)) {
758
+ current = current.expression;
759
+ continue;
760
+ }
761
+ if (isAsExpression(current)) {
762
+ current = current.expression;
763
+ continue;
764
+ }
765
+ if (isTypeAssertionExpression(current)) {
766
+ current = current.expression;
767
+ continue;
768
+ }
769
+ if (isNonNullExpression(current)) {
770
+ current = current.expression;
771
+ continue;
772
+ }
773
+ if (isSatisfiesExpression(current)) {
774
+ current = current.expression;
775
+ continue;
776
+ }
777
+ return current;
778
+ }
779
+ };
780
+ var resolveExpression = (expr, typeChecker) => {
781
+ let current = unwrapExpression(expr);
782
+ const visited = /* @__PURE__ */ new Set();
783
+ while (isIdentifier6(current)) {
784
+ if (visited.has(current)) break;
785
+ visited.add(current);
786
+ const symbol = typeChecker.getSymbolAtLocation(current);
787
+ if (!symbol) break;
788
+ let targetSymbol = symbol;
789
+ try {
790
+ const aliasedSymbol = typeChecker.getAliasedSymbol(symbol);
791
+ targetSymbol = aliasedSymbol || symbol;
792
+ } catch {
793
+ }
794
+ const decl = targetSymbol.declarations?.[0];
795
+ if (decl && isVariableDeclaration(decl) && decl.initializer) {
796
+ current = unwrapExpression(decl.initializer);
797
+ } else {
798
+ break;
799
+ }
800
+ }
801
+ return current;
802
+ };
803
+
804
+ // src/parser/resolve-controllers-from-array.ts
805
+ var resolveControllersFromArray = (initializer, typeChecker, processedFiles, controllers, fileCache) => {
806
+ initializer.elements.forEach((element) => {
807
+ resolveController(element, typeChecker, processedFiles, controllers, fileCache);
808
+ });
809
+ };
810
+
811
+ // src/parser/resolve-controllers-from-module.ts
812
+ import { isClassDeclaration as isClassDeclaration5, isIdentifier as isIdentifier7 } from "typescript";
813
+ var resolveControllersFromModule = (callExpr, typeChecker, processedFiles, controllers, fileCache) => {
814
+ const moduleArg = callExpr.arguments[0];
815
+ if (moduleArg && isIdentifier7(moduleArg)) {
816
+ const moduleSymbol = typeChecker.getSymbolAtLocation(moduleArg);
817
+ if (!moduleSymbol) return;
818
+ let targetSymbol = moduleSymbol;
819
+ try {
820
+ const aliasedSymbol = typeChecker.getAliasedSymbol(moduleSymbol);
821
+ targetSymbol = aliasedSymbol || moduleSymbol;
822
+ } catch {
823
+ }
824
+ const targetDecl = targetSymbol.declarations?.[0];
825
+ if (!targetDecl || !isClassDeclaration5(targetDecl)) return;
826
+ resolveControllersFromModuleClass(targetDecl, typeChecker, processedFiles, controllers, fileCache);
827
+ }
828
+ };
829
+
830
+ // src/parser/process-create-ipc-app-call.ts
831
+ var processCreateIpcAppCall = (node, typeChecker, processedFiles, controllers, fileCache, resolutionStrategy) => {
832
+ const args = node.arguments;
833
+ if (args.length === 0) return;
834
+ const optionsObj = resolveExpression(args[0], typeChecker);
835
+ if (!isObjectLiteralExpression3(optionsObj)) return;
836
+ const controllersProp = optionsObj.properties.find(
837
+ (p) => isPropertyAssignment3(p) && isIdentifier8(p.name) && p.name.text === "controllers"
838
+ );
839
+ if (!controllersProp) return;
840
+ const initializer = resolveExpression(controllersProp.initializer, typeChecker);
841
+ if (isArrayLiteralExpression3(initializer)) {
842
+ resolveControllersFromArray(initializer, typeChecker, processedFiles, controllers, fileCache);
843
+ return;
844
+ }
845
+ if (isCallExpression7(initializer)) {
846
+ resolveControllersFromModule(initializer, typeChecker, processedFiles, controllers, fileCache);
847
+ for (const arg of initializer.arguments) {
848
+ const resolvedArg = resolveExpression(arg, typeChecker);
849
+ if (isCallExpression7(resolvedArg)) {
850
+ for (const factoryArg of resolvedArg.arguments) {
851
+ const resolvedFactoryArg = resolveExpression(factoryArg, typeChecker);
852
+ if (!isIdentifier8(resolvedFactoryArg)) continue;
853
+ const factorySymbol = typeChecker.getSymbolAtLocation(resolvedFactoryArg);
854
+ if (!factorySymbol) continue;
855
+ let targetSymbol = factorySymbol;
856
+ try {
857
+ const aliased = typeChecker.getAliasedSymbol(factorySymbol);
858
+ targetSymbol = aliased || factorySymbol;
859
+ } catch {
860
+ }
861
+ const targetDecl = targetSymbol.declarations?.[0];
862
+ if (targetDecl && isClassDeclaration6(targetDecl)) {
863
+ resolveControllersFromModuleClass(targetDecl, typeChecker, processedFiles, controllers, fileCache);
864
+ }
865
+ }
866
+ }
867
+ }
868
+ if (controllers.length === 0 && resolutionStrategy) {
869
+ const context = {
870
+ fileCache,
871
+ processedFiles,
872
+ sourceFile: node.getSourceFile(),
873
+ typeChecker
874
+ };
875
+ const resolved = resolutionStrategy(context);
876
+ controllers.push(...resolved);
877
+ }
878
+ }
879
+ };
880
+
881
+ // src/parser/find-controllers.ts
882
+ function resolveStrategy(option) {
883
+ if (option === "nest") return nestResolutionStrategy;
884
+ return void 0;
885
+ }
886
+ var isCreateIpcAppCall = (node, typeChecker) => {
887
+ if (!isIdentifier9(node.expression)) return false;
888
+ const sym = typeChecker.getSymbolAtLocation(node.expression);
889
+ if (!sym) return false;
890
+ let target = sym;
891
+ try {
892
+ const aliased = typeChecker.getAliasedSymbol(sym);
893
+ if (aliased) target = aliased;
894
+ } catch {
895
+ }
896
+ return target.name === "createIpcApp";
897
+ };
898
+ var findControllers = (entryFile, tsConfigPath, oldProgram, resolutionStrategy) => {
899
+ const strategy = resolveStrategy(resolutionStrategy);
900
+ const searchConfig = tsConfigPath || "tsconfig.node.json";
901
+ let configFile = findConfigFile(path2.dirname(entryFile), sys.fileExists, searchConfig);
902
+ if (!configFile && !tsConfigPath) {
903
+ configFile = findConfigFile(path2.dirname(entryFile), sys.fileExists, "tsconfig.json");
904
+ }
905
+ let compilerOptions = {};
906
+ if (configFile) {
907
+ const { config } = readConfigFile(configFile, sys.readFile);
908
+ const { options } = parseJsonConfigFileContent(config, sys, path2.dirname(configFile));
909
+ compilerOptions = options;
910
+ }
911
+ const program = createProgram([entryFile], compilerOptions, void 0, oldProgram);
912
+ const typeChecker = program.getTypeChecker();
913
+ const sourceFile = program.getSourceFile(entryFile);
914
+ if (!sourceFile) {
915
+ return { controllers: [], processedFiles: /* @__PURE__ */ new Set(), program };
916
+ }
917
+ const controllers = [];
918
+ const processedFiles = /* @__PURE__ */ new Set();
919
+ const fileCache = /* @__PURE__ */ new Map();
920
+ processedFiles.add(path2.resolve(entryFile));
921
+ forEachChild4(sourceFile, function visit(node) {
922
+ if (isCallExpression8(node) && isCreateIpcAppCall(node, typeChecker)) {
923
+ processCreateIpcAppCall(node, typeChecker, processedFiles, controllers, fileCache, strategy);
924
+ }
925
+ forEachChild4(node, visit);
926
+ });
927
+ return { controllers, processedFiles, program };
928
+ };
929
+
930
+ // src/preload/resolve-api-root.ts
931
+ import fs from "fs";
932
+ import {
933
+ createSourceFile,
934
+ forEachChild as forEachChild5,
935
+ isCallExpression as isCallExpression9,
936
+ isIdentifier as isIdentifier10,
937
+ isObjectLiteralExpression as isObjectLiteralExpression4,
938
+ isPropertyAssignment as isPropertyAssignment4,
939
+ isStringLiteral as isStringLiteral3,
940
+ ScriptTarget
941
+ } from "typescript";
942
+ var resolveApiRootFromPreload = (preloadPath) => {
943
+ const dependencies = /* @__PURE__ */ new Set();
944
+ dependencies.add(preloadPath);
945
+ let namespace = IPC_DEFAULT_API_ROOT;
946
+ if (!fs.existsSync(preloadPath)) {
947
+ return { dependencies, namespace };
948
+ }
949
+ const content = fs.readFileSync(preloadPath, "utf-8");
950
+ const sourceFile = createSourceFile(preloadPath, content, ScriptTarget.Latest, true);
951
+ const visit = (node) => {
952
+ if (isCallExpression9(node) && isIdentifier10(node.expression) && node.expression.text === "setupPreload") {
953
+ const args = node.arguments;
954
+ if (args.length > 0) {
955
+ const first = args[0];
956
+ if (isStringLiteral3(first)) {
957
+ namespace = first.text;
958
+ } else if (isObjectLiteralExpression4(first)) {
959
+ const namespaceProp = first.properties.find(
960
+ (p) => isPropertyAssignment4(p) && isIdentifier10(p.name) && p.name.text === "namespace"
961
+ );
962
+ if (namespaceProp && isPropertyAssignment4(namespaceProp) && isStringLiteral3(namespaceProp.initializer)) {
963
+ namespace = namespaceProp.initializer.text;
964
+ }
965
+ }
966
+ }
967
+ }
968
+ forEachChild5(node, visit);
969
+ };
970
+ forEachChild5(sourceFile, visit);
971
+ return { dependencies, namespace };
972
+ };
973
+
974
+ // src/resolve-type-paths.ts
975
+ import path3 from "path";
976
+ var resolveTypePaths = ({
977
+ hasRendererRuntimeDir,
978
+ preloadPath,
979
+ root,
980
+ types
981
+ }) => {
982
+ const runtimePath = types.runtime === false ? null : types.runtime ? path3.resolve(root, types.runtime) : (() => {
983
+ const rendererSrc = path3.resolve(root, DEFAULT_RENDERER_RUNTIME_DIR);
984
+ if (hasRendererRuntimeDir(rendererSrc)) {
985
+ return path3.join(rendererSrc, DEFAULT_RUNTIME_TYPES_FILENAME);
986
+ }
987
+ return path3.resolve(root, path3.join("src", DEFAULT_RUNTIME_TYPES_FILENAME));
988
+ })();
989
+ const globalPath = types.global === false ? null : types.global ? path3.resolve(root, types.global) : path3.join(path3.dirname(preloadPath), DEFAULT_GLOBAL_TYPES_FILENAME);
990
+ return { globalPath, runtimePath };
991
+ };
992
+
993
+ // src/generate-ipc.ts
994
+ function generateIpc(root, logger, state, options) {
995
+ try {
996
+ const { main, preload, resolutionStrategy, types } = options;
997
+ const preloadPath = path4.resolve(root, preload);
998
+ const { dependencies: preloadDependencies, namespace: resolvedApiRoot } = resolveApiRootFromPreload(preloadPath);
999
+ const entryPath = path4.resolve(root, main);
1000
+ if (!fs2.existsSync(entryPath)) {
1001
+ logger.warn(`Main entry not found at: ${entryPath}`);
1002
+ return;
1003
+ }
1004
+ logger.info(`Generating IPC types from ${entryPath}...`);
1005
+ const { controllers, processedFiles, program } = findControllers(
1006
+ entryPath,
1007
+ void 0,
1008
+ state.getProgram(),
1009
+ resolutionStrategy
1010
+ );
1011
+ state.setProgram(program);
1012
+ if (controllers.length === 0) {
1013
+ logger.warn(`No createIpcApp() call found in ${entryPath}; generated types will be empty.`);
1014
+ }
1015
+ state.setControllerFiles(new Set([...processedFiles, ...preloadDependencies].map(normalizePath)));
1016
+ const { globalPath, runtimePath } = resolveTypePaths({
1017
+ hasRendererRuntimeDir: (absPath) => fs2.existsSync(absPath),
1018
+ preloadPath,
1019
+ root,
1020
+ types
1021
+ });
1022
+ if (!runtimePath && !globalPath) {
1023
+ logger.warn(`Both runtime and global type outputs are disabled; nothing to generate.`);
1024
+ return;
1025
+ }
1026
+ let runtimeTypesContent = null;
1027
+ let globalTypesContent = null;
1028
+ if (runtimePath) {
1029
+ runtimeTypesContent = generateRuntimeTypes(controllers);
1030
+ }
1031
+ if (globalPath) {
1032
+ const ipcApiImportPath = (() => {
1033
+ if (!runtimePath) {
1034
+ throw new Error("Global type generation requires a runtime types output path.");
1035
+ }
1036
+ const rel = path4.relative(path4.dirname(globalPath), runtimePath).replace(/\\/g, "/").replace(/\.tsx?$/, ".ts").replace(/(\.d)?\.ts$/, "");
1037
+ return rel.startsWith(".") ? rel : `./${rel}`;
1038
+ })();
1039
+ globalTypesContent = generateGlobalTypes(resolvedApiRoot, ipcApiImportPath);
1040
+ }
1041
+ const combinedHash = crypto.createHash("md5").update(runtimeTypesContent ?? "").update(globalTypesContent ?? "").digest("hex");
1042
+ if (!state.updateHash(combinedHash)) return;
1043
+ if (runtimePath && runtimeTypesContent != null) {
1044
+ fs2.mkdirSync(path4.dirname(runtimePath), { recursive: true });
1045
+ fs2.writeFileSync(runtimePath, runtimeTypesContent);
1046
+ logger.info(`Runtime types generated at ${path4.relative(root, runtimePath)}`);
1047
+ }
1048
+ if (globalPath && globalTypesContent != null) {
1049
+ fs2.mkdirSync(path4.dirname(globalPath), { recursive: true });
1050
+ fs2.writeFileSync(globalPath, globalTypesContent);
1051
+ logger.info(`Global types generated at ${path4.relative(root, globalPath)}`);
1052
+ }
1053
+ } catch (err) {
1054
+ logger.error(`Type generation failed: ${err instanceof Error ? err.message : String(err)}`);
1055
+ }
1056
+ }
1057
+
1058
+ // src/plugin-state.ts
1059
+ var PluginState = class {
1060
+ constructor(debounceMs = 100) {
1061
+ this.debounceMs = debounceMs;
1062
+ this.controllerFiles = null;
1063
+ this.debounceTimer = null;
1064
+ this.hasGeneratedOnce = false;
1065
+ this.lastHash = null;
1066
+ this.program = null;
1067
+ }
1068
+ claimInitialGeneration() {
1069
+ if (!this.hasGeneratedOnce) {
1070
+ this.hasGeneratedOnce = true;
1071
+ return true;
1072
+ }
1073
+ return false;
1074
+ }
1075
+ getProgram() {
1076
+ return this.program ?? void 0;
1077
+ }
1078
+ scheduleGenerate(callback) {
1079
+ if (this.debounceTimer) clearTimeout(this.debounceTimer);
1080
+ this.debounceTimer = setTimeout(() => {
1081
+ callback();
1082
+ this.debounceTimer = null;
1083
+ }, this.debounceMs);
1084
+ }
1085
+ setControllerFiles(files) {
1086
+ this.controllerFiles = files;
1087
+ }
1088
+ setProgram(program) {
1089
+ this.program = program;
1090
+ }
1091
+ shouldRegenerate(absId, mainEntryPath, preloadEntryPath) {
1092
+ const isMainEntry = absId === mainEntryPath;
1093
+ const isPreloadEntry = preloadEntryPath ? absId === preloadEntryPath : false;
1094
+ const isControllerFile = this.controllerFiles?.has(absId);
1095
+ return isMainEntry || isPreloadEntry || !!isControllerFile;
1096
+ }
1097
+ updateHash(hash) {
1098
+ if (hash === this.lastHash) {
1099
+ return false;
1100
+ }
1101
+ this.lastHash = hash;
1102
+ return true;
1103
+ }
1104
+ };
1105
+
1106
+ // src/plugin.ts
1107
+ function electronIpcBridge({
1108
+ main = DEFAULT_MAIN_ENTRY,
1109
+ preload = DEFAULT_PRELOAD_ENTRY,
1110
+ resolutionStrategy,
1111
+ types = {}
1112
+ } = {}) {
1113
+ let root = process.cwd();
1114
+ let logger;
1115
+ const state = new PluginState();
1116
+ const generate = () => {
1117
+ generateIpc(root, logger, state, { main, preload, resolutionStrategy, types });
1118
+ };
1119
+ return {
1120
+ buildStart() {
1121
+ if (state.claimInitialGeneration()) {
1122
+ generate();
1123
+ }
1124
+ },
1125
+ configResolved(config) {
1126
+ root = config.root;
1127
+ logger = createLogger(config.logLevel, {
1128
+ allowClearScreen: config.clearScreen,
1129
+ customLogger: config.customLogger,
1130
+ prefix: `[${package_default.name}]`
1131
+ });
1132
+ },
1133
+ configureServer(server) {
1134
+ const preloadPath = path5.resolve(root, preload);
1135
+ server.watcher.add(preloadPath);
1136
+ server.watcher.on("change", (file) => {
1137
+ if (normalizePath(file) === normalizePath(preloadPath)) {
1138
+ state.scheduleGenerate(generate);
1139
+ }
1140
+ });
1141
+ },
1142
+ name: "electron-ipc-bridge",
1143
+ transform(_code, id) {
1144
+ if (id.includes("node_modules") || id.endsWith(".d.ts")) return null;
1145
+ const absId = normalizePath(path5.resolve(id));
1146
+ const mainEntryPath = normalizePath(path5.resolve(root, main));
1147
+ const preloadEntryPath = normalizePath(path5.resolve(root, preload));
1148
+ if (state.shouldRegenerate(absId, mainEntryPath, preloadEntryPath)) {
1149
+ state.scheduleGenerate(generate);
1150
+ }
1151
+ return null;
1152
+ }
1153
+ };
1154
+ }
1155
+ export {
1156
+ electronIpcBridge
1157
+ };
1158
+ //# sourceMappingURL=index.js.map