@gtkx/codegen 0.21.0

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 (340) hide show
  1. package/LICENSE +373 -0
  2. package/dist/dsl/context.d.ts +104 -0
  3. package/dist/dsl/context.d.ts.map +1 -0
  4. package/dist/dsl/context.js +140 -0
  5. package/dist/dsl/context.js.map +1 -0
  6. package/dist/dsl/emit.d.ts +28 -0
  7. package/dist/dsl/emit.d.ts.map +1 -0
  8. package/dist/dsl/emit.js +41 -0
  9. package/dist/dsl/emit.js.map +1 -0
  10. package/dist/dsl/identifier.d.ts +28 -0
  11. package/dist/dsl/identifier.d.ts.map +1 -0
  12. package/dist/dsl/identifier.js +45 -0
  13. package/dist/dsl/identifier.js.map +1 -0
  14. package/dist/dsl/imports.d.ts +58 -0
  15. package/dist/dsl/imports.d.ts.map +1 -0
  16. package/dist/dsl/imports.js +107 -0
  17. package/dist/dsl/imports.js.map +1 -0
  18. package/dist/dsl/module.d.ts +53 -0
  19. package/dist/dsl/module.d.ts.map +1 -0
  20. package/dist/dsl/module.js +76 -0
  21. package/dist/dsl/module.js.map +1 -0
  22. package/dist/ffi/pipeline.d.ts +19 -0
  23. package/dist/ffi/pipeline.d.ts.map +1 -0
  24. package/dist/ffi/pipeline.js +107 -0
  25. package/dist/ffi/pipeline.js.map +1 -0
  26. package/dist/fingerprint.d.ts +30 -0
  27. package/dist/fingerprint.d.ts.map +1 -0
  28. package/dist/fingerprint.js +33 -0
  29. package/dist/fingerprint.js.map +1 -0
  30. package/dist/gi-store.d.ts +34 -0
  31. package/dist/gi-store.d.ts.map +1 -0
  32. package/dist/gi-store.js +154 -0
  33. package/dist/gi-store.js.map +1 -0
  34. package/dist/gir/boxed.d.ts +66 -0
  35. package/dist/gir/boxed.d.ts.map +1 -0
  36. package/dist/gir/boxed.js +49 -0
  37. package/dist/gir/boxed.js.map +1 -0
  38. package/dist/gir/callback.d.ts +15 -0
  39. package/dist/gir/callback.d.ts.map +1 -0
  40. package/dist/gir/callback.js +17 -0
  41. package/dist/gir/callback.js.map +1 -0
  42. package/dist/gir/class.d.ts +49 -0
  43. package/dist/gir/class.d.ts.map +1 -0
  44. package/dist/gir/class.js +42 -0
  45. package/dist/gir/class.js.map +1 -0
  46. package/dist/gir/enum.d.ts +39 -0
  47. package/dist/gir/enum.d.ts.map +1 -0
  48. package/dist/gir/enum.js +24 -0
  49. package/dist/gir/enum.js.map +1 -0
  50. package/dist/gir/field.d.ts +27 -0
  51. package/dist/gir/field.d.ts.map +1 -0
  52. package/dist/gir/field.js +19 -0
  53. package/dist/gir/field.js.map +1 -0
  54. package/dist/gir/function.d.ts +47 -0
  55. package/dist/gir/function.d.ts.map +1 -0
  56. package/dist/gir/function.js +28 -0
  57. package/dist/gir/function.js.map +1 -0
  58. package/dist/gir/namespace.d.ts +65 -0
  59. package/dist/gir/namespace.d.ts.map +1 -0
  60. package/dist/gir/namespace.js +60 -0
  61. package/dist/gir/namespace.js.map +1 -0
  62. package/dist/gir/parameter.d.ts +82 -0
  63. package/dist/gir/parameter.d.ts.map +1 -0
  64. package/dist/gir/parameter.js +66 -0
  65. package/dist/gir/parameter.js.map +1 -0
  66. package/dist/gir/parse.d.ts +78 -0
  67. package/dist/gir/parse.d.ts.map +1 -0
  68. package/dist/gir/parse.js +155 -0
  69. package/dist/gir/parse.js.map +1 -0
  70. package/dist/gir/primitives.d.ts +36 -0
  71. package/dist/gir/primitives.d.ts.map +1 -0
  72. package/dist/gir/primitives.js +109 -0
  73. package/dist/gir/primitives.js.map +1 -0
  74. package/dist/gir/property.d.ts +29 -0
  75. package/dist/gir/property.d.ts.map +1 -0
  76. package/dist/gir/property.js +16 -0
  77. package/dist/gir/property.js.map +1 -0
  78. package/dist/gir/qualified-name.d.ts +37 -0
  79. package/dist/gir/qualified-name.d.ts.map +1 -0
  80. package/dist/gir/qualified-name.js +39 -0
  81. package/dist/gir/qualified-name.js.map +1 -0
  82. package/dist/gir/qualify.d.ts +32 -0
  83. package/dist/gir/qualify.d.ts.map +1 -0
  84. package/dist/gir/qualify.js +60 -0
  85. package/dist/gir/qualify.js.map +1 -0
  86. package/dist/gir/repository.d.ts +82 -0
  87. package/dist/gir/repository.d.ts.map +1 -0
  88. package/dist/gir/repository.js +122 -0
  89. package/dist/gir/repository.js.map +1 -0
  90. package/dist/gir/signal.d.ts +22 -0
  91. package/dist/gir/signal.d.ts.map +1 -0
  92. package/dist/gir/signal.js +20 -0
  93. package/dist/gir/signal.js.map +1 -0
  94. package/dist/gir/size.d.ts +67 -0
  95. package/dist/gir/size.d.ts.map +1 -0
  96. package/dist/gir/size.js +87 -0
  97. package/dist/gir/size.js.map +1 -0
  98. package/dist/gir/type-ref.d.ts +79 -0
  99. package/dist/gir/type-ref.d.ts.map +1 -0
  100. package/dist/gir/type-ref.js +101 -0
  101. package/dist/gir/type-ref.js.map +1 -0
  102. package/dist/index.d.ts +12 -0
  103. package/dist/index.d.ts.map +1 -0
  104. package/dist/index.js +12 -0
  105. package/dist/index.js.map +1 -0
  106. package/dist/jsx-store.d.ts +22 -0
  107. package/dist/jsx-store.d.ts.map +1 -0
  108. package/dist/jsx-store.js +40 -0
  109. package/dist/jsx-store.js.map +1 -0
  110. package/dist/react/compounds-meta.d.ts +18 -0
  111. package/dist/react/compounds-meta.d.ts.map +1 -0
  112. package/dist/react/compounds-meta.js +93 -0
  113. package/dist/react/compounds-meta.js.map +1 -0
  114. package/dist/react/compounds.d.ts +27 -0
  115. package/dist/react/compounds.d.ts.map +1 -0
  116. package/dist/react/compounds.js +164 -0
  117. package/dist/react/compounds.js.map +1 -0
  118. package/dist/react/internal.d.ts +14 -0
  119. package/dist/react/internal.d.ts.map +1 -0
  120. package/dist/react/internal.js +79 -0
  121. package/dist/react/internal.js.map +1 -0
  122. package/dist/react/jsx.d.ts +22 -0
  123. package/dist/react/jsx.d.ts.map +1 -0
  124. package/dist/react/jsx.js +134 -0
  125. package/dist/react/jsx.js.map +1 -0
  126. package/dist/react/pipeline.d.ts +13 -0
  127. package/dist/react/pipeline.d.ts.map +1 -0
  128. package/dist/react/pipeline.js +25 -0
  129. package/dist/react/pipeline.js.map +1 -0
  130. package/dist/react/props.d.ts +38 -0
  131. package/dist/react/props.d.ts.map +1 -0
  132. package/dist/react/props.js +231 -0
  133. package/dist/react/props.js.map +1 -0
  134. package/dist/react/slot-props.d.ts +10 -0
  135. package/dist/react/slot-props.d.ts.map +1 -0
  136. package/dist/react/slot-props.js +56 -0
  137. package/dist/react/slot-props.js.map +1 -0
  138. package/dist/react/widgets.d.ts +83 -0
  139. package/dist/react/widgets.d.ts.map +1 -0
  140. package/dist/react/widgets.js +152 -0
  141. package/dist/react/widgets.js.map +1 -0
  142. package/dist/runner.d.ts +55 -0
  143. package/dist/runner.d.ts.map +1 -0
  144. package/dist/runner.js +60 -0
  145. package/dist/runner.js.map +1 -0
  146. package/dist/store-fs.d.ts +109 -0
  147. package/dist/store-fs.d.ts.map +1 -0
  148. package/dist/store-fs.js +98 -0
  149. package/dist/store-fs.js.map +1 -0
  150. package/dist/transpile.d.ts +32 -0
  151. package/dist/transpile.d.ts.map +1 -0
  152. package/dist/transpile.js +63 -0
  153. package/dist/transpile.js.map +1 -0
  154. package/dist/typecheck-store.d.ts +41 -0
  155. package/dist/typecheck-store.d.ts.map +1 -0
  156. package/dist/typecheck-store.js +102 -0
  157. package/dist/typecheck-store.js.map +1 -0
  158. package/dist/writers/alias.d.ts +13 -0
  159. package/dist/writers/alias.d.ts.map +1 -0
  160. package/dist/writers/alias.js +17 -0
  161. package/dist/writers/alias.js.map +1 -0
  162. package/dist/writers/async.d.ts +22 -0
  163. package/dist/writers/async.d.ts.map +1 -0
  164. package/dist/writers/async.js +48 -0
  165. package/dist/writers/async.js.map +1 -0
  166. package/dist/writers/boxed-constructor.d.ts +26 -0
  167. package/dist/writers/boxed-constructor.d.ts.map +1 -0
  168. package/dist/writers/boxed-constructor.js +85 -0
  169. package/dist/writers/boxed-constructor.js.map +1 -0
  170. package/dist/writers/boxed-field-accessor.d.ts +18 -0
  171. package/dist/writers/boxed-field-accessor.d.ts.map +1 -0
  172. package/dist/writers/boxed-field-accessor.js +260 -0
  173. package/dist/writers/boxed-field-accessor.js.map +1 -0
  174. package/dist/writers/boxed-layout.d.ts +27 -0
  175. package/dist/writers/boxed-layout.d.ts.map +1 -0
  176. package/dist/writers/boxed-layout.js +128 -0
  177. package/dist/writers/boxed-layout.js.map +1 -0
  178. package/dist/writers/boxed.d.ts +20 -0
  179. package/dist/writers/boxed.d.ts.map +1 -0
  180. package/dist/writers/boxed.js +71 -0
  181. package/dist/writers/boxed.js.map +1 -0
  182. package/dist/writers/callables.d.ts +124 -0
  183. package/dist/writers/callables.d.ts.map +1 -0
  184. package/dist/writers/callables.js +283 -0
  185. package/dist/writers/callables.js.map +1 -0
  186. package/dist/writers/callback.d.ts +16 -0
  187. package/dist/writers/callback.d.ts.map +1 -0
  188. package/dist/writers/callback.js +38 -0
  189. package/dist/writers/callback.js.map +1 -0
  190. package/dist/writers/class-struct-record.d.ts +31 -0
  191. package/dist/writers/class-struct-record.d.ts.map +1 -0
  192. package/dist/writers/class-struct-record.js +67 -0
  193. package/dist/writers/class-struct-record.js.map +1 -0
  194. package/dist/writers/class-struct.d.ts +22 -0
  195. package/dist/writers/class-struct.d.ts.map +1 -0
  196. package/dist/writers/class-struct.js +85 -0
  197. package/dist/writers/class-struct.js.map +1 -0
  198. package/dist/writers/class.d.ts +16 -0
  199. package/dist/writers/class.d.ts.map +1 -0
  200. package/dist/writers/class.js +307 -0
  201. package/dist/writers/class.js.map +1 -0
  202. package/dist/writers/constant.d.ts +15 -0
  203. package/dist/writers/constant.d.ts.map +1 -0
  204. package/dist/writers/constant.js +23 -0
  205. package/dist/writers/constant.js.map +1 -0
  206. package/dist/writers/constants.d.ts +15 -0
  207. package/dist/writers/constants.d.ts.map +1 -0
  208. package/dist/writers/constants.js +23 -0
  209. package/dist/writers/constants.js.map +1 -0
  210. package/dist/writers/constructor-props.d.ts +32 -0
  211. package/dist/writers/constructor-props.d.ts.map +1 -0
  212. package/dist/writers/constructor-props.js +110 -0
  213. package/dist/writers/constructor-props.js.map +1 -0
  214. package/dist/writers/enum.d.ts +21 -0
  215. package/dist/writers/enum.d.ts.map +1 -0
  216. package/dist/writers/enum.js +48 -0
  217. package/dist/writers/enum.js.map +1 -0
  218. package/dist/writers/function.d.ts +40 -0
  219. package/dist/writers/function.d.ts.map +1 -0
  220. package/dist/writers/function.js +127 -0
  221. package/dist/writers/function.js.map +1 -0
  222. package/dist/writers/gtype-binding.d.ts +19 -0
  223. package/dist/writers/gtype-binding.d.ts.map +1 -0
  224. package/dist/writers/gtype-binding.js +40 -0
  225. package/dist/writers/gtype-binding.js.map +1 -0
  226. package/dist/writers/inheritance.d.ts +48 -0
  227. package/dist/writers/inheritance.d.ts.map +1 -0
  228. package/dist/writers/inheritance.js +90 -0
  229. package/dist/writers/inheritance.js.map +1 -0
  230. package/dist/writers/interface.d.ts +16 -0
  231. package/dist/writers/interface.d.ts.map +1 -0
  232. package/dist/writers/interface.js +155 -0
  233. package/dist/writers/interface.js.map +1 -0
  234. package/dist/writers/method.d.ts +150 -0
  235. package/dist/writers/method.d.ts.map +1 -0
  236. package/dist/writers/method.js +510 -0
  237. package/dist/writers/method.js.map +1 -0
  238. package/dist/writers/param-classify.d.ts +93 -0
  239. package/dist/writers/param-classify.d.ts.map +1 -0
  240. package/dist/writers/param-classify.js +163 -0
  241. package/dist/writers/param-classify.js.map +1 -0
  242. package/dist/writers/property-accessor.d.ts +19 -0
  243. package/dist/writers/property-accessor.d.ts.map +1 -0
  244. package/dist/writers/property-accessor.js +117 -0
  245. package/dist/writers/property-accessor.js.map +1 -0
  246. package/dist/writers/registration.d.ts +27 -0
  247. package/dist/writers/registration.d.ts.map +1 -0
  248. package/dist/writers/registration.js +30 -0
  249. package/dist/writers/registration.js.map +1 -0
  250. package/dist/writers/return-wrap.d.ts +39 -0
  251. package/dist/writers/return-wrap.d.ts.map +1 -0
  252. package/dist/writers/return-wrap.js +152 -0
  253. package/dist/writers/return-wrap.js.map +1 -0
  254. package/dist/writers/runtime-override.d.ts +18 -0
  255. package/dist/writers/runtime-override.d.ts.map +1 -0
  256. package/dist/writers/runtime-override.js +44 -0
  257. package/dist/writers/runtime-override.js.map +1 -0
  258. package/dist/writers/signal.d.ts +21 -0
  259. package/dist/writers/signal.d.ts.map +1 -0
  260. package/dist/writers/signal.js +260 -0
  261. package/dist/writers/signal.js.map +1 -0
  262. package/dist/writers/ts-type.d.ts +50 -0
  263. package/dist/writers/ts-type.d.ts.map +1 -0
  264. package/dist/writers/ts-type.js +75 -0
  265. package/dist/writers/ts-type.js.map +1 -0
  266. package/dist/writers/types-ts.d.ts +27 -0
  267. package/dist/writers/types-ts.d.ts.map +1 -0
  268. package/dist/writers/types-ts.js +97 -0
  269. package/dist/writers/types-ts.js.map +1 -0
  270. package/dist/writers/value.d.ts +89 -0
  271. package/dist/writers/value.d.ts.map +1 -0
  272. package/dist/writers/value.js +402 -0
  273. package/dist/writers/value.js.map +1 -0
  274. package/package.json +55 -0
  275. package/src/dsl/context.ts +146 -0
  276. package/src/dsl/emit.ts +42 -0
  277. package/src/dsl/identifier.ts +52 -0
  278. package/src/dsl/imports.ts +109 -0
  279. package/src/dsl/module.ts +75 -0
  280. package/src/ffi/pipeline.ts +113 -0
  281. package/src/fingerprint.ts +50 -0
  282. package/src/gi-store.ts +199 -0
  283. package/src/gir/boxed.ts +94 -0
  284. package/src/gir/callback.ts +26 -0
  285. package/src/gir/class.ts +79 -0
  286. package/src/gir/enum.ts +55 -0
  287. package/src/gir/field.ts +40 -0
  288. package/src/gir/function.ts +67 -0
  289. package/src/gir/namespace.ts +118 -0
  290. package/src/gir/parameter.ts +120 -0
  291. package/src/gir/parse.ts +171 -0
  292. package/src/gir/primitives.ts +137 -0
  293. package/src/gir/property.ts +39 -0
  294. package/src/gir/qualified-name.ts +55 -0
  295. package/src/gir/qualify.ts +64 -0
  296. package/src/gir/repository.ts +149 -0
  297. package/src/gir/signal.ts +36 -0
  298. package/src/gir/size.ts +129 -0
  299. package/src/gir/type-ref.ts +180 -0
  300. package/src/index.ts +16 -0
  301. package/src/jsx-store.ts +53 -0
  302. package/src/react/compounds-meta.ts +99 -0
  303. package/src/react/compounds.ts +204 -0
  304. package/src/react/internal.ts +91 -0
  305. package/src/react/jsx.ts +155 -0
  306. package/src/react/pipeline.ts +29 -0
  307. package/src/react/props.ts +307 -0
  308. package/src/react/slot-props.ts +57 -0
  309. package/src/react/widgets.ts +185 -0
  310. package/src/runner.ts +89 -0
  311. package/src/store-fs.ts +157 -0
  312. package/src/transpile.ts +80 -0
  313. package/src/typecheck-store.ts +136 -0
  314. package/src/writers/alias.ts +19 -0
  315. package/src/writers/async.ts +44 -0
  316. package/src/writers/boxed-constructor.ts +105 -0
  317. package/src/writers/boxed-field-accessor.ts +306 -0
  318. package/src/writers/boxed-layout.ts +166 -0
  319. package/src/writers/boxed.ts +77 -0
  320. package/src/writers/callables.ts +339 -0
  321. package/src/writers/callback.ts +40 -0
  322. package/src/writers/class-struct-record.ts +75 -0
  323. package/src/writers/class-struct.ts +96 -0
  324. package/src/writers/class.ts +384 -0
  325. package/src/writers/constant.ts +29 -0
  326. package/src/writers/constructor-props.ts +130 -0
  327. package/src/writers/enum.ts +53 -0
  328. package/src/writers/function.ts +136 -0
  329. package/src/writers/gtype-binding.ts +48 -0
  330. package/src/writers/inheritance.ts +104 -0
  331. package/src/writers/interface.ts +168 -0
  332. package/src/writers/method.ts +678 -0
  333. package/src/writers/param-classify.ts +178 -0
  334. package/src/writers/property-accessor.ts +139 -0
  335. package/src/writers/registration.ts +42 -0
  336. package/src/writers/return-wrap.ts +180 -0
  337. package/src/writers/runtime-override.ts +59 -0
  338. package/src/writers/signal.ts +318 -0
  339. package/src/writers/ts-type.ts +115 -0
  340. package/src/writers/value.ts +485 -0
@@ -0,0 +1,136 @@
1
+ import { quote } from "@gtkx/utils";
2
+ import type { ModuleContext } from "../dsl/context.js";
3
+ import { arrayLiteral, indent, joinArgs } from "../dsl/emit.js";
4
+ import { namespaceFunctionExportName } from "../dsl/identifier.js";
5
+ import type { GirFunction } from "../gir/function.js";
6
+ import type { GirNamespace } from "../gir/namespace.js";
7
+ import type { GirParameter } from "../gir/parameter.js";
8
+ import { callableReferencesClassStruct } from "./class-struct-record.js";
9
+ import { renderMethodBody, renderMethodReturnType, renderMethodSignature } from "./method.js";
10
+ import { closureAndDestroyIndices, needsRefArg } from "./param-classify.js";
11
+ import { renderFfiType, renderSelfFfiType, renderTrampolineType } from "./value.js";
12
+
13
+ /**
14
+ * Renders the FFI argument list for a callable, as a TypeScript array
15
+ * literal suitable as the third argument to `t.fn(...)`.
16
+ *
17
+ * Includes the instance parameter (if any), every regular parameter, and
18
+ * the implicit `GError**` ref when `throws=1`.
19
+ *
20
+ * @param context - The module context
21
+ * @param fn - The callable
22
+ */
23
+ const renderArgsLiteral = (context: ModuleContext, fn: GirFunction): string => {
24
+ const args: string[] = [];
25
+ const instanceOffset = fn.instance === undefined ? 0 : 1;
26
+ if (fn.instance !== undefined) {
27
+ context.addRuntimeImport("t");
28
+ args.push(`{ type: ${renderSelfFfiType(context, fn.instance)} }`);
29
+ }
30
+ const closureIndices = closureAndDestroyIndices(fn);
31
+ fn.parameters.forEach((parameter, index) => {
32
+ if (parameter.isVarargs) return;
33
+ if (closureIndices.has(index)) return;
34
+ const optional = parameter.direction === "in" && (parameter.nullable || parameter.optional);
35
+ args.push(argLiteral(context, parameter, optional, instanceOffset));
36
+ });
37
+ if (fn.throws) {
38
+ context.addRuntimeImport("t");
39
+ args.push(`{ type: t.ref(t.boxed("GError", "full", "libgobject-2.0.so.0", "g_error_get_type")) }`);
40
+ }
41
+ return arrayLiteral(args);
42
+ };
43
+
44
+ const argLiteral = (
45
+ context: ModuleContext,
46
+ parameter: GirParameter,
47
+ optional: boolean,
48
+ instanceOffset: number,
49
+ ): string => {
50
+ const trampoline = renderTrampolineType(context, parameter.type, parameter);
51
+ const inner = trampoline ?? renderFfiType(context, parameter.type, parameter.transferOwnership, instanceOffset);
52
+ const refWrapped = needsRefArg(context, parameter);
53
+ const expression = refWrapped ? `t.ref(${inner})` : inner;
54
+ return optional && !refWrapped ? `{ type: ${expression}, optional: true }` : `{ type: ${expression} }`;
55
+ };
56
+
57
+ /**
58
+ * Renders the `t.fn(library, symbol, args, returnType)` expression for a
59
+ * callable. Returns `undefined` when the callable cannot be bound (no C
60
+ * identifier or missing namespace shared-library).
61
+ *
62
+ * @param context - The module context
63
+ * @param fn - The callable
64
+ */
65
+ export const renderFnExpression = (context: ModuleContext, fn: GirFunction): string | undefined => {
66
+ if (fn.cIdentifier === undefined) return undefined;
67
+ const library = context.namespace.sharedLibrary;
68
+ if (library === undefined) return undefined;
69
+ context.addRuntimeImport("t");
70
+ const args = renderArgsLiteral(context, fn);
71
+ const returnType = renderFfiType(
72
+ context,
73
+ fn.returnValue.type,
74
+ fn.returnValue.transferOwnership,
75
+ fn.instance === undefined ? 0 : 1,
76
+ );
77
+ return `t.fn(${joinArgs([quote(library), quote(fn.cIdentifier), args, returnType])})`;
78
+ };
79
+
80
+ /**
81
+ * Emits both the bound `const fooBinding = t.fn(...)` and the
82
+ * `export function camelName(...)` wrapper for a namespace-level callable.
83
+ *
84
+ * Silently skipped when {@link renderFnExpression} cannot bind the callable
85
+ * (e.g. macros, unintrospectable signatures) or when the GIR marks the
86
+ * callable as `introspectable="0"`.
87
+ *
88
+ * @param context - The module context
89
+ * @param fn - The callable
90
+ */
91
+ export const emitNamespaceFunction = (context: ModuleContext, fn: GirFunction): void => {
92
+ if (!fn.introspectable) return;
93
+ if (fn.shadowedBy !== undefined) return;
94
+ if (callableReferencesClassStruct(context, fn)) return;
95
+ const expression = renderFnExpression(context, fn);
96
+ if (expression === undefined) return;
97
+ const bindingName = fn.cIdentifier;
98
+ if (bindingName === undefined) return;
99
+ context.module.appendBinding(`const ${bindingName} = ${expression};`, bindingName);
100
+
101
+ const exportName = namespaceFunctionExportName(bindingName, fn.name, context.namespace.cSymbolPrefixes);
102
+ const signature = renderMethodSignature(context, fn);
103
+ const returnType = renderMethodReturnType(context, fn);
104
+ const body = renderMethodBody(context, fn, { bindingExpression: bindingName, isStatic: true });
105
+ context.module.appendDeclaration(
106
+ `export function ${exportName}(${signature}): ${returnType} {\n${indent(body, 1)}\n}`,
107
+ );
108
+ };
109
+
110
+ /**
111
+ * Emits a namespace's self-bootstrap statements as module-load side effects:
112
+ * a call to its zero-argument `init` entry point and a `whenStopped`
113
+ * registration for its zero-argument `finalize` entry point.
114
+ *
115
+ * GTK-style libraries expose top-level `init`/`finalize` functions (`gtk_init`,
116
+ * `adw_init`, `gtk_source_finalize`, …). Calling `init()` as the module is
117
+ * imported initializes the library's runtime before any of its types are
118
+ * touched — without it a `Gdk` display lookup can run before `gtk_init`.
119
+ * Deferring `finalize` to `whenStopped` runs it during shutdown.
120
+ *
121
+ * @param context - The module context
122
+ * @param namespace - The namespace being generated
123
+ */
124
+ export const emitNamespaceBootstrap = (context: ModuleContext, namespace: GirNamespace): void => {
125
+ for (const fn of namespace.functions) {
126
+ if (fn.parameters.length > 0) continue;
127
+ if (!fn.introspectable || fn.shadowedBy !== undefined || fn.cIdentifier === undefined) continue;
128
+ const exportName = namespaceFunctionExportName(fn.cIdentifier, fn.name, context.namespace.cSymbolPrefixes);
129
+ if (fn.name === "init") {
130
+ context.module.appendRegistration(`${exportName}();`);
131
+ } else if (fn.name === "finalize") {
132
+ context.module.imports.addNamed("@gtkx/ffi", "whenStopped");
133
+ context.module.appendRegistration(`whenStopped().then(${exportName});`);
134
+ }
135
+ }
136
+ };
@@ -0,0 +1,48 @@
1
+ import { quote } from "@gtkx/utils";
2
+ import type { ModuleContext } from "../dsl/context.js";
3
+
4
+ const TYPE_FROM_NAME_BINDING = "g_type_from_name";
5
+ const TYPE_FROM_NAME_LIB = "libgobject-2.0.so.0";
6
+
7
+ /**
8
+ * Renders the JS expression that resolves a class's `GType`, appending any
9
+ * required `t.fn` bindings to the module.
10
+ *
11
+ * For regular get-type symbols (`g_object_get_type`, `g_array_get_type`, …)
12
+ * the helper appends a `t.fn(...)` binding for the symbol and returns the bare
13
+ * function reference (`g_object_get_type`), which the runtime invokes once. For
14
+ * the GIR sentinel `"intern"` — used on `GVariant` and a few other types whose
15
+ * `GType` is registered intrinsically — it appends a binding for
16
+ * `g_type_from_name` and returns a `() => g_type_from_name("…")` thunk. Returns
17
+ * `undefined` when neither path is available.
18
+ *
19
+ * @param context - The module context
20
+ * @param getType - The C symbol name or the GIR sentinel `"intern"`
21
+ * @param glibTypeName - The GLib type name (for `"intern"` get-types)
22
+ */
23
+ export const renderGetTypeReference = (
24
+ context: ModuleContext,
25
+ getType: string,
26
+ glibTypeName: string | undefined,
27
+ ): string | undefined => {
28
+ if (getType === "intern" || getType === "") {
29
+ if (glibTypeName === undefined) return undefined;
30
+ appendGTypeFromNameBinding(context);
31
+ return `() => ${TYPE_FROM_NAME_BINDING}(${quote(glibTypeName)})`;
32
+ }
33
+ appendGetTypeBinding(context, getType);
34
+ return getType;
35
+ };
36
+
37
+ const appendGetTypeBinding = (context: ModuleContext, getType: string): void => {
38
+ const lib = context.namespace.sharedLibrary ?? "";
39
+ const expression = `t.fn(${quote(lib)}, ${quote(getType)}, [], t.uint64)`;
40
+ context.module.appendBinding(`const ${getType} = ${expression};`, getType);
41
+ };
42
+
43
+ const appendGTypeFromNameBinding = (context: ModuleContext): void => {
44
+ const expression =
45
+ `t.fn(${quote(TYPE_FROM_NAME_LIB)}, ${quote(TYPE_FROM_NAME_BINDING)}, ` +
46
+ `[{ type: t.string("borrowed") }], t.uint64)`;
47
+ context.module.appendBinding(`const ${TYPE_FROM_NAME_BINDING} = ${expression};`, TYPE_FROM_NAME_BINDING);
48
+ };
@@ -0,0 +1,104 @@
1
+ import { toCamelCase, toIdentifier } from "@gtkx/utils";
2
+ import type { ModuleContext } from "../dsl/context.js";
3
+ import type { GirClass } from "../gir/class.js";
4
+ import type { GirProperty } from "../gir/property.js";
5
+ import { type ResolvedQualifiedClass, resolveQualifiedClass, splitQualifiedName } from "../gir/qualified-name.js";
6
+ import { qualifyTypeRef } from "../gir/qualify.js";
7
+
8
+ /**
9
+ * A directly-implemented interface resolved to its declaration and the
10
+ * namespace that declares it.
11
+ */
12
+ export type ResolvedInterface = {
13
+ readonly klass: GirClass;
14
+ readonly namespaceName: string;
15
+ };
16
+
17
+ /**
18
+ * Resolves an `<implements>` entry to its `<interface>` declaration.
19
+ *
20
+ * Returns `undefined` when the name resolves to a non-interface entity or
21
+ * cannot be resolved at all.
22
+ *
23
+ * @param context - The module context
24
+ * @param name - The (possibly cross-namespace) interface name
25
+ */
26
+ export const resolveImplementedInterface = (
27
+ context: ModuleContext,
28
+ name: string,
29
+ defaultNamespace: string = context.namespace.name,
30
+ ): ResolvedInterface | undefined => {
31
+ const { namespaceName, typeName } = splitQualifiedName(name, defaultNamespace);
32
+ const resolved = context.repository.resolveNamed(namespaceName, typeName);
33
+ if (resolved === undefined || resolved.kind !== "interface") return undefined;
34
+ return { klass: resolved.value, namespaceName: resolved.namespace.name };
35
+ };
36
+
37
+ /**
38
+ * Invokes `visit` for each ancestor class of `klass`, nearest first.
39
+ *
40
+ * Walks the same-namespace and cross-namespace parent chain, stopping at the
41
+ * first unresolved parent or a cycle. Each ancestor is reported together with
42
+ * the namespace it was resolved through so callers can qualify its references.
43
+ *
44
+ * @param context - The module context
45
+ * @param klass - The class whose ancestors to visit
46
+ * @param visit - Callback invoked once per resolved ancestor
47
+ */
48
+ export const forEachAncestor = (
49
+ context: ModuleContext,
50
+ klass: GirClass,
51
+ visit: (ancestor: ResolvedQualifiedClass) => void,
52
+ ): void => {
53
+ const visited = new Set<string>();
54
+ let current =
55
+ klass.parent === undefined ? undefined : { name: klass.parent, defaultNamespace: context.namespace.name };
56
+ while (current !== undefined) {
57
+ const resolved = resolveQualifiedClass(context.repository, current.name, current.defaultNamespace);
58
+ if (resolved === undefined) break;
59
+ const key = `${resolved.namespaceName}.${resolved.klass.name}`;
60
+ if (visited.has(key)) break;
61
+ visited.add(key);
62
+ visit(resolved);
63
+ current = resolved.klass.parent
64
+ ? { name: resolved.klass.parent, defaultNamespace: resolved.namespaceName }
65
+ : undefined;
66
+ }
67
+ };
68
+
69
+ /**
70
+ * Collects the properties contributed by a class's directly-implemented
71
+ * interfaces that are not already declared on the class itself or inherited
72
+ * from an ancestor (or an ancestor-implemented interface).
73
+ *
74
+ * Each returned property carries its references re-rooted to the interface's
75
+ * namespace so the class writer can emit its accessor and constructor prop
76
+ * as if the property had been authored on the class.
77
+ *
78
+ * @param context - The module context
79
+ * @param klass - The implementing class
80
+ */
81
+ export const collectInterfaceProperties = (context: ModuleContext, klass: GirClass): readonly GirProperty[] => {
82
+ const seen = new Set<string>();
83
+ for (const property of klass.properties) seen.add(toIdentifier(toCamelCase(property.name)));
84
+ forEachAncestor(context, klass, (ancestor) => {
85
+ for (const property of ancestor.klass.properties) seen.add(toIdentifier(toCamelCase(property.name)));
86
+ for (const implementName of ancestor.klass.implements) {
87
+ const iface = resolveImplementedInterface(context, implementName, ancestor.namespaceName);
88
+ if (iface === undefined) continue;
89
+ for (const property of iface.klass.properties) seen.add(toIdentifier(toCamelCase(property.name)));
90
+ }
91
+ });
92
+ const result: GirProperty[] = [];
93
+ for (const implementName of klass.implements) {
94
+ const iface = resolveImplementedInterface(context, implementName);
95
+ if (iface === undefined) continue;
96
+ for (const property of iface.klass.properties) {
97
+ const name = toIdentifier(toCamelCase(property.name));
98
+ if (seen.has(name)) continue;
99
+ seen.add(name);
100
+ result.push({ ...property, type: qualifyTypeRef(property.type, iface.namespaceName) });
101
+ }
102
+ }
103
+ return result;
104
+ };
@@ -0,0 +1,168 @@
1
+ import { toPascalCase } from "@gtkx/utils";
2
+ import type { ModuleContext } from "../dsl/context.js";
3
+ import { indent } from "../dsl/emit.js";
4
+ import type { GirClass } from "../gir/class.js";
5
+ import type { GirFunction } from "../gir/function.js";
6
+ import { splitQualifiedName } from "../gir/qualified-name.js";
7
+ import { qualifyFunction } from "../gir/qualify.js";
8
+ import {
9
+ appendMethodBinding,
10
+ type Callables,
11
+ dedupeCallables,
12
+ emitBindings,
13
+ indexMethodsByName,
14
+ renderInstanceMethod,
15
+ renderPlainTypeMembers,
16
+ } from "./callables.js";
17
+ import { renderVfuncMetadata } from "./class-struct.js";
18
+ import { renderGetTypeReference } from "./gtype-binding.js";
19
+ import { resolveImplementedInterface } from "./inheritance.js";
20
+ import { methodExportName } from "./method.js";
21
+ import { renderPropertyAccessor } from "./property-accessor.js";
22
+ import { appendNativeClassRegistration } from "./registration.js";
23
+ import { renderSignalMembers } from "./signal.js";
24
+
25
+ /**
26
+ * Emits a class declaration for a `<interface>` element.
27
+ *
28
+ * Interfaces have no GIR parent — they extend the GObject base
29
+ * (`GObject.Object`) in the generated output because every implementor is
30
+ * itself a GObject. Methods, static functions, and constructors are
31
+ * surfaced on the interface class just like a regular class so JS callers
32
+ * can dispatch them directly on interface-typed values.
33
+ *
34
+ * @param context - The module context
35
+ * @param iface - The interface to emit
36
+ */
37
+ export const emitInterface = (context: ModuleContext, iface: GirClass): void => {
38
+ if (!iface.introspectable) return;
39
+ if (iface.name.length === 0) return;
40
+ const className = toPascalCase(iface.name);
41
+ const callables: Callables = {
42
+ constructors: dedupeCallables(iface.constructors),
43
+ functions: dedupeCallables(iface.functions),
44
+ methods: dedupeCallables(iface.methods),
45
+ };
46
+ emitBindings(context, callables);
47
+
48
+ const parent = resolveInterfaceParent(context);
49
+ const members = renderInterfaceMembers(context, iface, callables);
50
+ const body = members.map((member) => indent(member, 1)).join("\n\n");
51
+ context.module.appendDeclaration(`export class ${className} extends ${parent} {\n${body}\n}`);
52
+
53
+ const prerequisiteRefs = iface.prerequisites
54
+ .map((name) => resolvePrerequisiteReference(context, name))
55
+ .filter((entry): entry is string => entry !== undefined);
56
+ if (prerequisiteRefs.length > 0) {
57
+ context.module.appendDeclaration(`export interface ${className} extends ${prerequisiteRefs.join(", ")} {}`);
58
+ }
59
+
60
+ appendInterfaceRegistrations(context, iface, className);
61
+ };
62
+
63
+ const renderInterfaceMembers = (context: ModuleContext, iface: GirClass, callables: Callables): readonly string[] => {
64
+ const className = toPascalCase(iface.name);
65
+ const { members, claimedNames } = renderPlainTypeMembers({
66
+ context,
67
+ className,
68
+ callables,
69
+ hasGType: true,
70
+ wrap: "interface",
71
+ });
72
+ appendPrerequisiteMethods(context, iface, members, claimedNames);
73
+ const methodByName = indexMethodsByName(callables.methods);
74
+ for (const property of iface.properties) {
75
+ const block = renderPropertyAccessor(context, property, claimedNames, methodByName);
76
+ if (block !== undefined) members.push(block);
77
+ }
78
+ members.push(...renderSignalMembers(context, iface));
79
+ return members;
80
+ };
81
+
82
+ /**
83
+ * Copies the methods of an interface's transitive prerequisite interfaces onto
84
+ * the interface wrapper class.
85
+ *
86
+ * A `Gtk.SelectionModel` value is also a `Gio.ListModel`, but the JS wrapper
87
+ * only exposes `getNItems`/`getItem` if those prerequisite methods are emitted
88
+ * on the wrapper. Each prerequisite interface's methods are re-rooted to its
89
+ * namespace, bound, and emitted as direct members; names already provided by
90
+ * the interface itself are skipped.
91
+ *
92
+ * @param context - The module context
93
+ * @param iface - The interface being emitted
94
+ * @param members - The accumulating member list
95
+ * @param claimedNames - Names already emitted on the interface body
96
+ */
97
+ const appendPrerequisiteMethods = (
98
+ context: ModuleContext,
99
+ iface: GirClass,
100
+ members: string[],
101
+ claimedNames: Set<string>,
102
+ ): void => {
103
+ for (const method of collectPrerequisiteMethods(context, iface)) {
104
+ const name = methodExportName(method);
105
+ if (name === "constructor" || claimedNames.has(name)) continue;
106
+ const block = renderInstanceMethod(context, method);
107
+ if (block === undefined) continue;
108
+ appendMethodBinding(context, method);
109
+ members.push(block);
110
+ claimedNames.add(name);
111
+ }
112
+ };
113
+
114
+ const collectPrerequisiteMethods = (context: ModuleContext, iface: GirClass): readonly GirFunction[] => {
115
+ const result: GirFunction[] = [];
116
+ const visited = new Set<string>();
117
+ const seen = new Set<string>();
118
+ for (const method of iface.methods) {
119
+ if (method.introspectable) seen.add(methodExportName(method));
120
+ }
121
+ const visit = (klass: GirClass, namespaceName: string): void => {
122
+ for (const prerequisiteName of klass.prerequisites) {
123
+ const prerequisite = resolveImplementedInterface(context, prerequisiteName, namespaceName);
124
+ if (prerequisite === undefined) continue;
125
+ const key = `${prerequisite.namespaceName}.${prerequisite.klass.name}`;
126
+ if (visited.has(key)) continue;
127
+ visited.add(key);
128
+ for (const method of dedupeCallables(prerequisite.klass.methods)) {
129
+ const name = methodExportName(method);
130
+ if (seen.has(name)) continue;
131
+ seen.add(name);
132
+ result.push(qualifyFunction(method, prerequisite.namespaceName));
133
+ }
134
+ visit(prerequisite.klass, prerequisite.namespaceName);
135
+ }
136
+ };
137
+ visit(iface, context.namespace.name);
138
+ return result;
139
+ };
140
+
141
+ const appendInterfaceRegistrations = (context: ModuleContext, iface: GirClass, className: string): void => {
142
+ const getTypeRef =
143
+ iface.glibGetType === undefined
144
+ ? undefined
145
+ : renderGetTypeReference(context, iface.glibGetType, iface.glibTypeName);
146
+ appendNativeClassRegistration(context, {
147
+ className,
148
+ role: "interface",
149
+ getTypeRef,
150
+ vfuncs: renderVfuncMetadata(context, iface),
151
+ });
152
+ };
153
+
154
+ const resolveInterfaceParent = (context: ModuleContext): string => {
155
+ if (context.namespace.name === "GObject") return "Object";
156
+ const alias = context.addCrossNamespaceImport("GObject");
157
+ return `${alias}.Object`;
158
+ };
159
+
160
+ const resolvePrerequisiteReference = (context: ModuleContext, name: string): string | undefined => {
161
+ const { namespaceName, typeName } = splitQualifiedName(name, context.namespace.name);
162
+ const resolved = context.repository.resolveNamed(namespaceName, typeName);
163
+ if (resolved === undefined) return undefined;
164
+ if (resolved.kind !== "interface" && resolved.kind !== "class") return undefined;
165
+ if (namespaceName === context.namespace.name) return toPascalCase(typeName);
166
+ const alias = context.addCrossNamespaceImport(namespaceName);
167
+ return `${alias}.${toPascalCase(typeName)}`;
168
+ };