@gtkx/codegen 1.0.0-rc.2 → 1.0.0-rc.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 (254) hide show
  1. package/README.md +4 -5
  2. package/dist/analysis/descriptor-render.d.ts +2 -2
  3. package/dist/analysis/descriptor-render.d.ts.map +1 -1
  4. package/dist/analysis/descriptor-render.js +30 -32
  5. package/dist/analysis/descriptor-render.js.map +1 -1
  6. package/dist/analysis/descriptor.d.ts +6 -6
  7. package/dist/analysis/descriptor.d.ts.map +1 -1
  8. package/dist/analysis/descriptor.js +7 -7
  9. package/dist/analysis/descriptor.js.map +1 -1
  10. package/dist/analysis/param-structure.d.ts +2 -2
  11. package/dist/analysis/param-structure.d.ts.map +1 -1
  12. package/dist/analysis/param-structure.js +5 -7
  13. package/dist/analysis/param-structure.js.map +1 -1
  14. package/dist/analysis/ts-type.d.ts +1 -1
  15. package/dist/analysis/ts-type.d.ts.map +1 -1
  16. package/dist/analysis/ts-type.js +7 -13
  17. package/dist/analysis/ts-type.js.map +1 -1
  18. package/dist/compile.d.ts.map +1 -1
  19. package/dist/compile.js +2 -1
  20. package/dist/compile.js.map +1 -1
  21. package/dist/docs/api-reference.d.ts +77 -1
  22. package/dist/docs/api-reference.d.ts.map +1 -1
  23. package/dist/docs/api-reference.js +66 -8
  24. package/dist/docs/api-reference.js.map +1 -1
  25. package/dist/docs/element-page.d.ts.map +1 -1
  26. package/dist/docs/element-page.js +5 -4
  27. package/dist/docs/element-page.js.map +1 -1
  28. package/dist/docs/pipeline.d.ts +5 -3
  29. package/dist/docs/pipeline.d.ts.map +1 -1
  30. package/dist/docs/pipeline.js +5 -3
  31. package/dist/docs/pipeline.js.map +1 -1
  32. package/dist/docs/render.js +6 -6
  33. package/dist/docs/render.js.map +1 -1
  34. package/dist/docs/symbol-page.d.ts +5 -0
  35. package/dist/docs/symbol-page.d.ts.map +1 -1
  36. package/dist/docs/symbol-page.js +2 -2
  37. package/dist/docs/symbol-page.js.map +1 -1
  38. package/dist/fingerprint.d.ts +2 -4
  39. package/dist/fingerprint.d.ts.map +1 -1
  40. package/dist/fingerprint.js +7 -6
  41. package/dist/fingerprint.js.map +1 -1
  42. package/dist/gi.d.ts +7 -10
  43. package/dist/gi.d.ts.map +1 -1
  44. package/dist/gi.js +4 -9
  45. package/dist/gi.js.map +1 -1
  46. package/dist/gir/field.d.ts +0 -1
  47. package/dist/gir/field.d.ts.map +1 -1
  48. package/dist/gir/field.js +0 -3
  49. package/dist/gir/field.js.map +1 -1
  50. package/dist/gir/gir-path.d.ts +7 -0
  51. package/dist/gir/gir-path.d.ts.map +1 -1
  52. package/dist/gir/gir-path.js +7 -0
  53. package/dist/gir/gir-path.js.map +1 -1
  54. package/dist/gir/libraries.d.ts +17 -3
  55. package/dist/gir/libraries.d.ts.map +1 -1
  56. package/dist/gir/libraries.js +14 -1
  57. package/dist/gir/libraries.js.map +1 -1
  58. package/dist/gir/parameter.d.ts.map +1 -1
  59. package/dist/gir/parameter.js +0 -4
  60. package/dist/gir/parameter.js.map +1 -1
  61. package/dist/gir/size.d.ts.map +1 -1
  62. package/dist/gir/size.js +0 -3
  63. package/dist/gir/size.js.map +1 -1
  64. package/dist/gir/type-id.d.ts +1 -1
  65. package/dist/gir/type-id.d.ts.map +1 -1
  66. package/dist/gir/type-id.js +1 -3
  67. package/dist/gir/type-id.js.map +1 -1
  68. package/dist/gir/type-ref.js +1 -1
  69. package/dist/gir/type-ref.js.map +1 -1
  70. package/dist/index.d.ts +18 -5
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +12 -2
  73. package/dist/index.js.map +1 -1
  74. package/dist/internal.d.ts +3 -0
  75. package/dist/internal.d.ts.map +1 -0
  76. package/dist/internal.js +3 -0
  77. package/dist/internal.js.map +1 -0
  78. package/dist/jsx.d.ts +8 -11
  79. package/dist/jsx.d.ts.map +1 -1
  80. package/dist/jsx.js +18 -15
  81. package/dist/jsx.js.map +1 -1
  82. package/dist/khronos/args.d.ts.map +1 -1
  83. package/dist/khronos/args.js +1 -1
  84. package/dist/khronos/args.js.map +1 -1
  85. package/dist/khronos/pipeline.js +1 -1
  86. package/dist/khronos/pipeline.js.map +1 -1
  87. package/dist/khronos/plan.d.ts +2 -2
  88. package/dist/khronos/plan.d.ts.map +1 -1
  89. package/dist/khronos/plan.js +6 -6
  90. package/dist/khronos/plan.js.map +1 -1
  91. package/dist/react/element-config.d.ts +18 -9
  92. package/dist/react/element-config.d.ts.map +1 -1
  93. package/dist/react/element-config.js +23 -13
  94. package/dist/react/element-config.js.map +1 -1
  95. package/dist/runner.d.ts +36 -16
  96. package/dist/runner.d.ts.map +1 -1
  97. package/dist/runner.js +22 -20
  98. package/dist/runner.js.map +1 -1
  99. package/dist/store/compile-store.d.ts +1 -2
  100. package/dist/store/compile-store.d.ts.map +1 -1
  101. package/dist/store/compile-store.js +2 -2
  102. package/dist/store/compile-store.js.map +1 -1
  103. package/dist/store/gi/callables.d.ts.map +1 -1
  104. package/dist/store/gi/callables.js +4 -9
  105. package/dist/store/gi/callables.js.map +1 -1
  106. package/dist/store/gi/constant.d.ts.map +1 -1
  107. package/dist/store/gi/constant.js +0 -2
  108. package/dist/store/gi/constant.js.map +1 -1
  109. package/dist/store/gi/function.d.ts.map +1 -1
  110. package/dist/store/gi/function.js +1 -7
  111. package/dist/store/gi/function.js.map +1 -1
  112. package/dist/store/gi/method.d.ts.map +1 -1
  113. package/dist/store/gi/method.js +26 -23
  114. package/dist/store/gi/method.js.map +1 -1
  115. package/dist/store/gi/property-accessor.d.ts +1 -1
  116. package/dist/store/gi/property-accessor.d.ts.map +1 -1
  117. package/dist/store/gi/property-accessor.js +5 -7
  118. package/dist/store/gi/property-accessor.js.map +1 -1
  119. package/dist/store/gi/record-constructor.d.ts.map +1 -1
  120. package/dist/store/gi/record-constructor.js +5 -4
  121. package/dist/store/gi/record-constructor.js.map +1 -1
  122. package/dist/store/gi/record-field-accessor.d.ts +3 -2
  123. package/dist/store/gi/record-field-accessor.d.ts.map +1 -1
  124. package/dist/store/gi/record-field-accessor.js +21 -11
  125. package/dist/store/gi/record-field-accessor.js.map +1 -1
  126. package/dist/store/gi/record-layout.d.ts.map +1 -1
  127. package/dist/store/gi/record-layout.js +0 -4
  128. package/dist/store/gi/record-layout.js.map +1 -1
  129. package/dist/store/gi/record.d.ts.map +1 -1
  130. package/dist/store/gi/record.js +0 -7
  131. package/dist/store/gi/record.js.map +1 -1
  132. package/dist/store/gi/return-wrap.d.ts +1 -1
  133. package/dist/store/gi/return-wrap.d.ts.map +1 -1
  134. package/dist/store/gi/return-wrap.js +4 -4
  135. package/dist/store/gi/return-wrap.js.map +1 -1
  136. package/dist/store/gi/signal.d.ts.map +1 -1
  137. package/dist/store/gi/signal.js +13 -16
  138. package/dist/store/gi/signal.js.map +1 -1
  139. package/dist/store/gi/value-marshalable.d.ts +7 -0
  140. package/dist/store/gi/value-marshalable.d.ts.map +1 -0
  141. package/dist/store/gi/value-marshalable.js +57 -0
  142. package/dist/store/gi/value-marshalable.js.map +1 -0
  143. package/dist/store/gi-store.d.ts +2 -3
  144. package/dist/store/gi-store.d.ts.map +1 -1
  145. package/dist/store/gi-store.js.map +1 -1
  146. package/dist/store/jsx/element-components.d.ts +0 -2
  147. package/dist/store/jsx/element-components.d.ts.map +1 -1
  148. package/dist/store/jsx/element-components.js +0 -4
  149. package/dist/store/jsx/element-components.js.map +1 -1
  150. package/dist/store/jsx/element-prop-imports.d.ts +2 -3
  151. package/dist/store/jsx/element-prop-imports.d.ts.map +1 -1
  152. package/dist/store/jsx/element-prop-imports.js +0 -2
  153. package/dist/store/jsx/element-prop-imports.js.map +1 -1
  154. package/dist/store/jsx/element-prop-types.d.ts +0 -1
  155. package/dist/store/jsx/element-prop-types.d.ts.map +1 -1
  156. package/dist/store/jsx/element-prop-types.js +0 -1
  157. package/dist/store/jsx/element-prop-types.js.map +1 -1
  158. package/dist/store/jsx/generated-elements.d.ts +29 -0
  159. package/dist/store/jsx/generated-elements.d.ts.map +1 -0
  160. package/dist/store/jsx/generated-elements.js +30 -0
  161. package/dist/store/jsx/generated-elements.js.map +1 -0
  162. package/dist/store/jsx/intrinsic-elements.d.ts +1 -2
  163. package/dist/store/jsx/intrinsic-elements.d.ts.map +1 -1
  164. package/dist/store/jsx/intrinsic-elements.js +1 -2
  165. package/dist/store/jsx/intrinsic-elements.js.map +1 -1
  166. package/dist/store/jsx/jsx.js +6 -6
  167. package/dist/store/jsx/jsx.js.map +1 -1
  168. package/dist/store/jsx/metadata.js +7 -7
  169. package/dist/store/jsx/metadata.js.map +1 -1
  170. package/dist/store/jsx/omitted-props.d.ts +8 -0
  171. package/dist/store/jsx/omitted-props.d.ts.map +1 -0
  172. package/dist/store/jsx/omitted-props.js +20 -0
  173. package/dist/store/jsx/omitted-props.js.map +1 -0
  174. package/dist/store/jsx/pipeline.d.ts +4 -0
  175. package/dist/store/jsx/pipeline.d.ts.map +1 -1
  176. package/dist/store/jsx/pipeline.js +4 -1
  177. package/dist/store/jsx/pipeline.js.map +1 -1
  178. package/dist/store/jsx/props.d.ts +1 -1
  179. package/dist/store/jsx/props.d.ts.map +1 -1
  180. package/dist/store/jsx/props.js +8 -13
  181. package/dist/store/jsx/props.js.map +1 -1
  182. package/dist/store/jsx-store.d.ts +2 -3
  183. package/dist/store/jsx-store.d.ts.map +1 -1
  184. package/dist/store/jsx-store.js +3 -3
  185. package/dist/store/jsx-store.js.map +1 -1
  186. package/dist/store/resolve-store.d.ts +28 -0
  187. package/dist/store/resolve-store.d.ts.map +1 -0
  188. package/dist/store/resolve-store.js +65 -0
  189. package/dist/store/resolve-store.js.map +1 -0
  190. package/dist/store/store-fs.d.ts +5 -1
  191. package/dist/store/store-fs.d.ts.map +1 -1
  192. package/dist/store/store-fs.js +6 -4
  193. package/dist/store/store-fs.js.map +1 -1
  194. package/overrides/gobject/object.ts.ejs +8 -8
  195. package/package.json +6 -11
  196. package/src/analysis/descriptor-render.ts +45 -38
  197. package/src/analysis/descriptor.ts +13 -13
  198. package/src/analysis/param-structure.ts +8 -10
  199. package/src/analysis/ts-type.ts +8 -17
  200. package/src/compile.ts +2 -1
  201. package/src/docs/api-reference.ts +131 -13
  202. package/src/docs/element-page.ts +5 -9
  203. package/src/docs/pipeline.ts +9 -6
  204. package/src/docs/render.ts +7 -7
  205. package/src/docs/symbol-page.ts +7 -2
  206. package/src/fingerprint.ts +21 -12
  207. package/src/gi.ts +13 -11
  208. package/src/gir/field.ts +0 -4
  209. package/src/gir/gir-path.ts +7 -0
  210. package/src/gir/libraries.ts +18 -4
  211. package/src/gir/parameter.ts +0 -4
  212. package/src/gir/size.ts +0 -3
  213. package/src/gir/type-id.ts +2 -4
  214. package/src/gir/type-ref.ts +1 -1
  215. package/src/index.ts +18 -15
  216. package/src/internal.ts +2 -0
  217. package/src/jsx.ts +26 -21
  218. package/src/khronos/args.ts +1 -1
  219. package/src/khronos/pipeline.ts +1 -1
  220. package/src/khronos/plan.ts +8 -8
  221. package/src/react/element-config.ts +25 -16
  222. package/src/runner.ts +62 -37
  223. package/src/store/compile-store.ts +3 -4
  224. package/src/store/gi/callables.ts +5 -11
  225. package/src/store/gi/constant.ts +0 -2
  226. package/src/store/gi/function.ts +1 -7
  227. package/src/store/gi/method.ts +31 -28
  228. package/src/store/gi/property-accessor.ts +6 -8
  229. package/src/store/gi/record-constructor.ts +9 -4
  230. package/src/store/gi/record-field-accessor.ts +41 -12
  231. package/src/store/gi/record-layout.ts +0 -4
  232. package/src/store/gi/record.ts +0 -9
  233. package/src/store/gi/return-wrap.ts +5 -5
  234. package/src/store/gi/signal.ts +17 -17
  235. package/src/store/gi/value-marshalable.ts +87 -0
  236. package/src/store/gi-store.ts +2 -4
  237. package/src/store/jsx/element-components.ts +0 -6
  238. package/src/store/jsx/element-prop-imports.ts +7 -4
  239. package/src/store/jsx/element-prop-types.ts +0 -1
  240. package/src/store/jsx/generated-elements.ts +60 -0
  241. package/src/store/jsx/intrinsic-elements.ts +0 -9
  242. package/src/store/jsx/jsx.ts +7 -7
  243. package/src/store/jsx/metadata.ts +8 -8
  244. package/src/store/jsx/omitted-props.ts +30 -0
  245. package/src/store/jsx/pipeline.ts +6 -1
  246. package/src/store/jsx/props.ts +8 -20
  247. package/src/store/jsx-store.ts +5 -7
  248. package/src/store/resolve-store.ts +100 -0
  249. package/src/store/store-fs.ts +12 -5
  250. package/dist/store/gi/class-struct-record.d.ts +0 -10
  251. package/dist/store/gi/class-struct-record.d.ts.map +0 -1
  252. package/dist/store/gi/class-struct-record.js +0 -52
  253. package/dist/store/gi/class-struct-record.js.map +0 -1
  254. package/src/store/gi/class-struct-record.ts +0 -72
@@ -5,43 +5,91 @@ import type { GirRecord } from "../gir/record.js";
5
5
  import { Library } from "../gir/library.js";
6
6
  import { type GirNamespace, namespaceDirectory } from "../gir/namespace.js";
7
7
  import { dedupeCallables, isEmittableCallable } from "../store/gi/callables.js";
8
- import { isClassStructRecord } from "../store/gi/class-struct-record.js";
9
8
  import { namespaceFunctionExportName } from "../store/gi/function.js";
9
+ import { type ElementProps, setElementProps } from "../store/jsx/element-prop-imports.js";
10
10
  import { collectIntrinsicElementClasses, type GlibNamedClass } from "../store/jsx/intrinsic-elements.js";
11
+ import { type OmittedProps, setOmittedProps } from "../store/jsx/omitted-props.js";
11
12
  import { createElementPageContext, type ElementPageContext, renderElementPage } from "./element-page.js";
12
13
  import { docsSignatureContext, firstSentence, namespaceOrder } from "./render.js";
13
14
  import { type GiSymbolEntry, renderSymbolPage, type SymbolPageOptions } from "./symbol-page.js";
14
15
 
16
+ /** What to index and the element config the rendered pages reflect. */
15
17
  type ApiReferenceOptions = {
18
+ /** GIR library identifiers to load, such as `"Gtk-4.0"`; their dependencies are pulled in too. */
16
19
  libraries: string[];
20
+ /** Directories to search for `.gir` files. */
17
21
  girPath: string[];
22
+ /** Base props interfaces per element, as read by `readBuiltinElements`; without it pages miss them. */
23
+ props?: ElementProps;
24
+ /** GObject properties the project omits from generated props; without it pages show props that do not exist. */
25
+ omittedProps?: OmittedProps;
18
26
  };
19
27
 
28
+ /** Narrows an `ApiReference.symbols` enumeration. */
29
+ type ApiSymbolQuery = {
30
+ /** GIR namespace to keep, matched case-insensitively; every namespace when omitted. */
31
+ namespace?: string;
32
+ /** Symbol kinds to keep; every kind when omitted. */
33
+ kinds?: ApiSymbolKind[];
34
+ };
35
+
36
+ /** What an indexed symbol is: one of the GIR symbol kinds, or a JSX element. */
20
37
  type ApiSymbolKind = GiSymbolEntry["kind"] | "element";
21
38
 
39
+ /** An indexed symbol, without its reference page. */
22
40
  type ApiSymbol = {
41
+ /** GIR namespace declaring the symbol, such as `"Gtk"`. */
23
42
  namespace: string;
43
+ /** Name within the namespace, which for an element is its GLib type name. */
24
44
  name: string;
45
+ /** Kind the reference groups and filters the symbol by, `"element"` for a JSX tag. */
25
46
  kind: ApiSymbolKind;
47
+ /** First sentence of the symbol's GIR documentation, elided past 220 characters and empty when it has none. */
26
48
  summary: string;
27
49
  };
28
50
 
51
+ /** How much of one namespace the reference indexes. */
29
52
  type ApiNamespaceSummary = {
53
+ /** GIR namespace name, such as `"Gtk"`. */
30
54
  name: string;
55
+ /** The `@gtkx/gi` subpath the namespace's symbols are imported from. */
31
56
  importPath: string;
57
+ /** How many symbols other than JSX elements the namespace contributes. */
32
58
  symbols: number;
59
+ /** How many JSX elements the namespace contributes. */
33
60
  elements: number;
34
61
  };
35
62
 
63
+ /** What an `ApiReference.lookup` found: a rendered page, several candidates for the name, or nothing. */
36
64
  type ApiLookupResult =
37
- | { outcome: "page"; symbol: ApiSymbol; markdown: string } |
38
- { outcome: "ambiguous"; candidates: ApiSymbol[] } |
39
- { outcome: "notFound" };
40
-
65
+ | {
66
+ /** Discriminant selecting the variant. */
67
+ outcome: "page";
68
+ /** Index entry for the symbol the query resolved to. */
69
+ symbol: ApiSymbol;
70
+ /** The symbol's complete reference page, rendered as Markdown. */
71
+ markdown: string;
72
+ } |
73
+ {
74
+ /** Discriminant selecting the variant. */
75
+ outcome: "ambiguous";
76
+ /** Every symbol the name answers to, to be narrowed by namespace or kind. */
77
+ candidates: ApiSymbol[];
78
+ } |
79
+ {
80
+ /** Discriminant selecting the variant. */
81
+ outcome: "notFound";
82
+ };
83
+
84
+ /** A fuzzy search over indexed symbol names. */
41
85
  type ApiSearchOptions = {
86
+ /** Text matched case-insensitively against each symbol's name and its qualified `Namespace.Name` form. */
42
87
  query: string;
88
+ /** GIR namespace to keep, matched case-insensitively; every namespace when omitted. */
43
89
  namespace?: string;
90
+ /** Symbol kinds to keep; every kind when omitted. */
44
91
  kinds?: ApiSymbolKind[];
92
+ /** Maximum number of results, 20 by default. */
45
93
  limit?: number;
46
94
  };
47
95
 
@@ -56,6 +104,7 @@ type ElementEntry = {
56
104
  type SymbolEntry = GiSymbolEntry | ElementEntry;
57
105
  type ScoredEntry = { score: number; entry: SymbolEntry };
58
106
 
107
+ /** Every kind the reference indexes, in the order a namespace overview groups its symbols. */
59
108
  const API_SYMBOL_KINDS: ApiSymbolKind[] = [
60
109
  "element",
61
110
  "class",
@@ -90,6 +139,32 @@ const compareNames = (a: string, b: string): number => {
90
139
  return a > b ? 1 : 0;
91
140
  };
92
141
 
142
+ const isQueriedEntry = (
143
+ entry: SymbolEntry,
144
+ namespaceFilter: string | undefined,
145
+ kinds: Set<ApiSymbolKind> | undefined,
146
+ ): boolean => {
147
+ if (namespaceFilter !== undefined && entry.namespace.name.toLowerCase() !== namespaceFilter) {
148
+ return false;
149
+ }
150
+
151
+ return kinds === undefined || kinds.has(entry.kind);
152
+ };
153
+
154
+ const compareApiSymbols = (a: ApiSymbol, b: ApiSymbol): number => {
155
+ const byNamespace = namespaceOrder(a.namespace).localeCompare(namespaceOrder(b.namespace));
156
+
157
+ return byNamespace === 0 ? a.name.localeCompare(b.name) : byNamespace;
158
+ };
159
+
160
+ /**
161
+ * Loads the GIR data for the given libraries and indexes every symbol and JSX element in it. The result is
162
+ * a read-only view: it generates no store and needs none, so a tool can explore the bindings a project would
163
+ * get without running codegen.
164
+ *
165
+ * @param options Which libraries to index, where to find them, and the project's element config.
166
+ * @returns The indexed reference.
167
+ */
93
168
  const loadApiReference = (options: ApiReferenceOptions): ApiReference => new ApiReference(options);
94
169
 
95
170
  const functionEntry = (
@@ -136,23 +211,19 @@ const classEntries = (namespace: GirNamespace): GiSymbolEntry[] => {
136
211
  return entries;
137
212
  };
138
213
 
139
- const recordEntry = (library: Library, namespace: GirNamespace, record: GirRecord): GiSymbolEntry | undefined => {
214
+ const recordEntry = (namespace: GirNamespace, record: GirRecord): GiSymbolEntry | undefined => {
140
215
  if (!record.introspectable || record.isVtable || record.name.length === 0) {
141
216
  return undefined;
142
217
  }
143
218
 
144
- if (isClassStructRecord(library, namespace.name, record)) {
145
- return undefined;
146
- }
147
-
148
219
  return { kind: "record", namespace, name: record.name, doc: record.doc, record };
149
220
  };
150
221
 
151
- const recordEntries = (library: Library, namespace: GirNamespace): GiSymbolEntry[] => {
222
+ const recordEntries = (namespace: GirNamespace): GiSymbolEntry[] => {
152
223
  const entries: GiSymbolEntry[] = [];
153
224
 
154
225
  for (const record of namespace.records) {
155
- const entry = recordEntry(library, namespace, record);
226
+ const entry = recordEntry(namespace, record);
156
227
 
157
228
  if (entry !== undefined) {
158
229
  entries.push(entry);
@@ -270,6 +341,7 @@ const searchScore = (entry: SymbolEntry, query: string): number => {
270
341
  return 0;
271
342
  };
272
343
 
344
+ /** An index over the GIR data a project's bindings are generated from, queryable and renderable as Markdown. */
273
345
  class ApiReference {
274
346
  private library: Library;
275
347
  private libraries: string[];
@@ -280,13 +352,23 @@ class ApiReference {
280
352
  private byNamespace: Map<string, SymbolEntry[]> = new Map();
281
353
  private elementsByClass: Map<string, string> = new Map();
282
354
 
355
+ private props: ElementProps;
356
+ private omittedProps: OmittedProps;
357
+
283
358
  constructor(options: ApiReferenceOptions) {
284
359
  this.libraries = options.libraries;
360
+ this.props = options.props ?? {};
361
+ this.omittedProps = options.omittedProps ?? {};
285
362
  this.library = Library.load(options.libraries, options.girPath);
286
363
  this.elementContext = createElementPageContext(this.library, (): string | undefined => undefined);
287
364
  this.buildIndex();
288
365
  }
289
366
 
367
+ private applyElementConfig(): void {
368
+ setElementProps(this.props);
369
+ setOmittedProps(this.omittedProps);
370
+ }
371
+
290
372
  private add(entry: SymbolEntry): void {
291
373
  this.entries.push(entry);
292
374
 
@@ -322,7 +404,7 @@ class ApiReference {
322
404
  private indexNamespace(namespace: GirNamespace): void {
323
405
  const entries = [
324
406
  ...classEntries(namespace),
325
- ...recordEntries(this.library, namespace),
407
+ ...recordEntries(namespace),
326
408
  ...valueEntries(namespace),
327
409
  ];
328
410
 
@@ -367,6 +449,8 @@ class ApiReference {
367
449
 
368
450
  private renderPage(entry: SymbolEntry): string {
369
451
  if (entry.kind === "element") {
452
+ this.applyElementConfig();
453
+
370
454
  return renderElementPage(entry.element, this.elementContext);
371
455
  }
372
456
 
@@ -415,10 +499,18 @@ class ApiReference {
415
499
  return scored;
416
500
  }
417
501
 
502
+ /** Paths of the `.gir` files the index was built from, including the ones pulled in as dependencies. */
418
503
  get girFiles(): string[] {
419
504
  return this.library.girFiles;
420
505
  }
421
506
 
507
+ /**
508
+ * Resolves a name to a single symbol and renders its reference page. The name may be bare (`Button`) or
509
+ * qualified (`Gtk.Button`), and is matched case-insensitively; a name several symbols answer to is
510
+ * reported as ambiguous rather than picked between.
511
+ *
512
+ * @param kind Restricts the match to one kind, which is how a class and its JSX element are told apart.
513
+ */
422
514
  lookup(query: string, kind?: ApiSymbolKind): ApiLookupResult {
423
515
  const trimmed = query.trim();
424
516
 
@@ -440,6 +532,21 @@ class ApiReference {
440
532
  return { outcome: "page", symbol: this.toApiSymbol(entry), markdown: this.renderPage(entry) };
441
533
  }
442
534
 
535
+ /** Every indexed symbol the query keeps, ordered by namespace (Gtk, then Adw, then alphabetically) and name. */
536
+ symbols(query: ApiSymbolQuery = {}): ApiSymbol[] {
537
+ const namespaceFilter = query.namespace?.toLowerCase();
538
+ const kinds = query.kinds === undefined ? undefined : new Set(query.kinds);
539
+
540
+ return this.entries
541
+ .filter((entry) => isQueriedEntry(entry, namespaceFilter, kinds))
542
+ .map((entry) => this.toApiSymbol(entry))
543
+ .toSorted(compareApiSymbols);
544
+ }
545
+
546
+ /**
547
+ * Symbols whose name matches the query, best first: an exact name beats a prefix, which beats a
548
+ * substring, and shorter names win ties. An empty query matches nothing.
549
+ */
443
550
  search(options: ApiSearchOptions): ApiSymbol[] {
444
551
  const query = options.query.trim().toLowerCase();
445
552
 
@@ -455,6 +562,7 @@ class ApiReference {
455
562
  return scored.slice(0, limit).map((item) => this.toApiSymbol(item.entry));
456
563
  }
457
564
 
565
+ /** A summary of every indexed namespace, ordered Gtk, Adw, then alphabetically. */
458
566
  namespaces(): ApiNamespaceSummary[] {
459
567
  const summaries = [...this.byNamespace].map(([name, entries]) => ({
460
568
  name,
@@ -466,6 +574,10 @@ class ApiReference {
466
574
  return sortStringsBy(summaries, (summary) => namespaceOrder(summary.name));
467
575
  }
468
576
 
577
+ /**
578
+ * Sorted names of everything a namespace contributes, JSX elements included. The namespace is matched
579
+ * case-insensitively, and an unindexed one yields an empty array.
580
+ */
469
581
  symbolNames(namespaceName: string): string[] {
470
582
  const namespace = this.findNamespace(namespaceName);
471
583
 
@@ -478,6 +590,7 @@ class ApiReference {
478
590
  return sortStrings(entries.map((entry) => entry.name));
479
591
  }
480
592
 
593
+ /** Renders the reference's Markdown landing page: every namespace, its import path, and its totals. */
481
594
  overview(): string {
482
595
  const rows = this.namespaces().map(
483
596
  (summary) =>
@@ -505,6 +618,10 @@ class ApiReference {
505
618
  ].join("\n");
506
619
  }
507
620
 
621
+ /**
622
+ * Renders one namespace's Markdown page: its import line and its symbol names grouped by kind. The
623
+ * namespace is matched case-insensitively, and an unindexed one yields undefined.
624
+ */
508
625
  namespaceOverview(name: string): string | undefined {
509
626
  const namespace = this.findNamespace(name);
510
627
 
@@ -554,4 +671,5 @@ export {
554
671
  type ApiNamespaceSummary,
555
672
  type ApiLookupResult,
556
673
  type ApiSearchOptions,
674
+ type ApiSymbolQuery,
557
675
  };
@@ -13,6 +13,7 @@ import {
13
13
  newlyImplementedInterfaces,
14
14
  signalHandlerName,
15
15
  } from "../store/jsx/intrinsic-elements.js";
16
+ import { isOmittedProp } from "../store/jsx/omitted-props.js";
16
17
  import { isObjectProp } from "../store/jsx/props.js";
17
18
  import {
18
19
  classMethodEntries,
@@ -120,12 +121,7 @@ const propertyEntry = (
120
121
  property: GirProperty,
121
122
  jsName: string,
122
123
  ): PropEntry => {
123
- const isObject = isObjectProp(
124
- { library: context.library, klass: owner.klass, namespace: owner.namespace },
125
- property,
126
- jsName,
127
- );
128
-
124
+ const isObject = isObjectProp(context.library, property);
129
125
  const baseType = renderDocsType(context.library, property.type, false);
130
126
  const type = isObject ? `${baseType} | ReactElement` : baseType;
131
127
  const meta: string[] = [`\`${type}\``];
@@ -149,14 +145,14 @@ const propertyEntry = (
149
145
  return { name: jsName, meta: meta.join(" · "), doc: docMarkdown(property.doc) };
150
146
  };
151
147
 
152
- const propJsName = (property: GirProperty, seen: Set<string>): string | undefined => {
148
+ const propJsName = (property: GirProperty, owner: MemberOwner, seen: Set<string>): string | undefined => {
153
149
  if (!property.introspectable) {
154
150
  return undefined;
155
151
  }
156
152
 
157
153
  const jsName = toCamelIdentifier(property.name);
158
154
 
159
- if (seen.has(jsName)) {
155
+ if (seen.has(jsName) || isOmittedProp(owner.glibName, jsName)) {
160
156
  return undefined;
161
157
  }
162
158
 
@@ -169,7 +165,7 @@ const ownerPropEntries = (context: ElementPageContext, owner: MemberOwner, seen:
169
165
  const entries: PropEntry[] = [];
170
166
 
171
167
  for (const property of owner.klass.properties) {
172
- const jsName = propJsName(property, seen);
168
+ const jsName = propJsName(property, owner, seen);
173
169
 
174
170
  if (jsName !== undefined) {
175
171
  entries.push(propertyEntry(context, owner, property, jsName));
@@ -6,6 +6,7 @@ import { Library } from "../gir/library.js";
6
6
  import { namespaceDirectory } from "../gir/namespace.js";
7
7
  import { type ElementProps, setElementProps } from "../store/jsx/element-prop-imports.js";
8
8
  import { collectIntrinsicElementClasses, type GlibNamedClass } from "../store/jsx/intrinsic-elements.js";
9
+ import { type OmittedProps, setOmittedProps } from "../store/jsx/omitted-props.js";
9
10
  import { createElementPageContext, type ElementPageContext, renderElementPage } from "./element-page.js";
10
11
  import { elementSlug, firstSentence, namespaceOrder } from "./render.js";
11
12
 
@@ -27,7 +28,8 @@ type DocsOptions = {
27
28
  outDir: string;
28
29
  basePath?: string;
29
30
  props?: ElementProps;
30
- force?: boolean;
31
+ omittedProps?: OmittedProps;
32
+ isForced?: boolean;
31
33
  };
32
34
 
33
35
  type DocsManifest = {
@@ -43,7 +45,7 @@ type Page = { path: string; content: string };
43
45
  type NamespacePages = { docs: DocsNamespace; pages: Page[] };
44
46
 
45
47
  type DocsResult = {
46
- regenerated: boolean;
48
+ isRegenerated: boolean;
47
49
  namespaces: DocsNamespace[];
48
50
  };
49
51
 
@@ -191,7 +193,7 @@ const generatePages = (options: DocsOptions, basePath: string, library: Library)
191
193
  };
192
194
 
193
195
  const cachedDocsResult = (options: DocsOptions, manifestPath: string): DocsResult | undefined => {
194
- if (options.force === true || !isGiStoreFresh(options.outDir, options.libraries, options.girPath)) {
196
+ if (options.isForced === true || !isGiStoreFresh(options.outDir, options.libraries, options.girPath)) {
195
197
  return undefined;
196
198
  }
197
199
 
@@ -201,7 +203,7 @@ const cachedDocsResult = (options: DocsOptions, manifestPath: string): DocsResul
201
203
 
202
204
  const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as DocsManifest;
203
205
 
204
- return { regenerated: false, namespaces: manifest.namespaces };
206
+ return { isRegenerated: false, namespaces: manifest.namespaces };
205
207
  };
206
208
 
207
209
  const writePages = (outDir: string, pages: Page[]): void => {
@@ -214,6 +216,7 @@ const writePages = (outDir: string, pages: Page[]): void => {
214
216
 
215
217
  const writeDocs = (options: DocsOptions): DocsResult => {
216
218
  setElementProps(options.props ?? {});
219
+ setOmittedProps(options.omittedProps ?? {});
217
220
  const basePath = options.basePath ?? "/reference";
218
221
  const manifestPath = join(options.outDir, MANIFEST_FILENAME);
219
222
  const cached = cachedDocsResult(options, manifestPath);
@@ -235,7 +238,7 @@ const writeDocs = (options: DocsOptions): DocsResult => {
235
238
  JSON.stringify(computeGiFingerprint(library.girFiles, options.libraries, options.girPath)),
236
239
  );
237
240
 
238
- return { regenerated: true, namespaces };
241
+ return { isRegenerated: true, namespaces };
239
242
  };
240
243
 
241
- export { writeDocs, type DocsElementLink, type DocsNamespace, type DocsOptions, type DocsResult };
244
+ export { writeDocs };
@@ -45,8 +45,8 @@ const renderDocsHandlerResultType = (library: Library, signal: GirCallable): str
45
45
  library,
46
46
  signal,
47
47
  renderType: (ref, nullable) => renderDocsType(library, ref, nullable),
48
- includeCallerAllocated: false,
49
- optOut: true,
48
+ shouldIncludeCallerAllocated: false,
49
+ isOptOut: true,
50
50
  });
51
51
 
52
52
  const renderDocsHandlerParameters = (library: Library, signal: GirCallable): string[] =>
@@ -69,16 +69,16 @@ const stripDocMedia = (markdown: string): string =>
69
69
  .replaceAll(/<video[\s\S]*?(?:<\/video>|\/>)/g, "")
70
70
  .replaceAll(/<img[^>]*>/g, "");
71
71
 
72
- const demoteLine = (line: string, isInFence: boolean): { text: string; inFence: boolean } => {
72
+ const demoteLine = (line: string, isInFence: boolean): { text: string; isInFence: boolean } => {
73
73
  if (FENCE_LINE.test(line)) {
74
- return { text: line, inFence: !isInFence };
74
+ return { text: line, isInFence: !isInFence };
75
75
  }
76
76
 
77
77
  if (isInFence) {
78
- return { text: line, inFence: isInFence };
78
+ return { text: line, isInFence };
79
79
  }
80
80
 
81
- return { text: HEADING_LINE.test(line) ? `#${line}` : line, inFence: isInFence };
81
+ return { text: HEADING_LINE.test(line) ? `#${line}` : line, isInFence };
82
82
  };
83
83
 
84
84
  const demoteHeadings = (markdown: string): string => {
@@ -87,7 +87,7 @@ const demoteHeadings = (markdown: string): string => {
87
87
 
88
88
  for (const line of markdown.split("\n")) {
89
89
  const result = demoteLine(line, isInFence);
90
- isInFence = result.inFence;
90
+ isInFence = result.isInFence;
91
91
  lines.push(result.text);
92
92
  }
93
93
 
@@ -49,12 +49,17 @@ import {
49
49
  type SignatureEntry,
50
50
  } from "./render.js";
51
51
 
52
+ /** What every indexed GIR symbol carries, whatever its kind. */
52
53
  type GiSymbolBase = {
54
+ /** GIR namespace declaring the symbol. */
53
55
  namespace: GirNamespace;
56
+ /** Name the bindings export it under: camelCase for functions, PascalCase for classes, the GIR name otherwise. */
54
57
  name: string;
58
+ /** Raw gtk-doc text from the GIR, undefined when the symbol is undocumented. */
55
59
  doc: string | undefined;
56
60
  };
57
61
 
62
+ /** A GIR symbol the reference indexes, discriminated by `kind` and carrying the GIR node its page renders from. */
58
63
  type GiSymbolEntry =
59
64
  | (GiSymbolBase & { kind: "class" | "interface"; klass: GirClass }) |
60
65
  (GiSymbolBase & { kind: "record"; record: GirRecord }) |
@@ -270,7 +275,7 @@ const propertyMeta = (property: GirProperty, accessor: ResolvedAccessor, origin:
270
275
  meta.push("construct-only");
271
276
  }
272
277
 
273
- if (!accessor.writable) {
278
+ if (!accessor.isWritable) {
274
279
  meta.push("read-only");
275
280
  } else if (!accessor.hasGetter) {
276
281
  meta.push("write-only");
@@ -495,7 +500,7 @@ const recordPage = (entry: GiSymbolBase & { kind: "record"; record: GirRecord },
495
500
  };
496
501
 
497
502
  const fieldMeta = (field: ResolvedRecordField): string =>
498
- [`\`${field.tsType}\``, ...(field.writable ? [] : ["read-only"])].join(" · ");
503
+ [`\`${field.tsType}\``, ...(field.isWritable ? [] : ["read-only"])].join(" · ");
499
504
 
500
505
  const fieldsSection = (record: GirRecord, context: ModuleContext, claimedNames: Set<string>): string[] => {
501
506
  const { slots } = computeRecordFieldSlots(context, record.fields, record.isUnion);
@@ -5,7 +5,6 @@ import { createRequire } from "node:module";
5
5
  import { dirname, join, resolve } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
 
8
- /** The freshness sentinel of the `@gtkx/gi` store, derived purely from the GIR inputs. */
9
8
  type GiFingerprint = {
10
9
  value: string;
11
10
  girFiles: string[];
@@ -15,15 +14,14 @@ type GiFingerprint = {
15
14
 
16
15
  type ModuleExport = { module: string; export: string };
17
16
 
18
- /** The `@gtkx/react` element config that shapes the generated `@gtkx/jsx` store. */
19
17
  type JsxFingerprintInput = {
20
18
  reactVersion: string;
21
19
  components: Record<string, ModuleExport>;
22
20
  lazyElements: string[];
23
21
  props: Record<string, ModuleExport>;
22
+ omittedProps: Record<string, string[]>;
24
23
  };
25
24
 
26
- /** The freshness sentinel of the `@gtkx/jsx` store, derived from the React element config. */
27
25
  type JsxFingerprint = {
28
26
  value: string;
29
27
  intrinsicElementCount: number;
@@ -36,8 +34,6 @@ const OVERRIDES_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "o
36
34
 
37
35
  const sortAlpha = (values: string[]): string => sortStrings(values).join(",");
38
36
 
39
- // The overrides are copied verbatim into the store, so editing one changes the output even though no
40
- // GIR file moved. Without them in the hash a stale override survives every non-forced run.
41
37
  const overrideFiles = (): string[] => {
42
38
  if (!existsSync(OVERRIDES_ROOT)) {
43
39
  return [];
@@ -55,6 +51,7 @@ const hashGi = (girFiles: string[], libraries: string[], girPath: string[]): str
55
51
  hash.update(sortAlpha(libraries));
56
52
  hash.update("\n");
57
53
  hash.update(sortAlpha(girPath));
54
+ hash.update("\n");
58
55
  const hashedFiles = sortStrings([...girFiles, ...overrideFiles()]);
59
56
 
60
57
  for (const file of hashedFiles) {
@@ -67,14 +64,22 @@ const hashGi = (girFiles: string[], libraries: string[], girPath: string[]): str
67
64
  return hash.digest("hex");
68
65
  };
69
66
 
70
- const computeGiFingerprint = (girFiles: string[], libraries: string[], girPath: string[]): GiFingerprint => ({
67
+ const computeGiFingerprint = (
68
+ girFiles: string[],
69
+ libraries: string[],
70
+ girPath: string[],
71
+ ): GiFingerprint => ({
71
72
  value: hashGi(girFiles, libraries, girPath),
72
73
  girFiles,
73
74
  libraries,
74
75
  girPath,
75
76
  });
76
77
 
77
- const isGiStoreFresh = (giStoreDir: string, libraries: string[], girPath: string[]): boolean => {
78
+ const isGiStoreFresh = (
79
+ giStoreDir: string,
80
+ libraries: string[],
81
+ girPath: string[],
82
+ ): boolean => {
78
83
  const sentinelPath = join(giStoreDir, FINGERPRINT_FILENAME);
79
84
 
80
85
  if (!existsSync(sentinelPath)) {
@@ -106,6 +111,9 @@ const hasMatchingRecordedInputs = (sentinel: GiFingerprint, libraries: string[],
106
111
  const serializeModuleExports = (map: Record<string, ModuleExport>): [string, string, string][] =>
107
112
  sortStrings(Object.keys(map)).map((type) => [type, map[type]?.module ?? "", map[type]?.export ?? ""]);
108
113
 
114
+ const serializeOmittedProps = (map: Record<string, string[]>): [string, string][] =>
115
+ sortStrings(Object.keys(map)).map((type) => [type, sortAlpha(map[type] ?? [])]);
116
+
109
117
  const hashJsx = (input: JsxFingerprintInput): string =>
110
118
  createHash("sha256")
111
119
  .update(
@@ -115,6 +123,7 @@ const hashJsx = (input: JsxFingerprintInput): string =>
115
123
  serializeModuleExports(input.components),
116
124
  sortStrings(input.lazyElements),
117
125
  serializeModuleExports(input.props),
126
+ serializeOmittedProps(input.omittedProps),
118
127
  ]),
119
128
  )
120
129
  .digest("hex");
@@ -127,11 +136,11 @@ const computeJsxFingerprint = (input: JsxFingerprintInput, intrinsicElementCount
127
136
  const jsxStoreFreshness = (
128
137
  jsxStoreDir: string,
129
138
  input: JsxFingerprintInput,
130
- ): { fresh: boolean; intrinsicElementCount: number } => {
139
+ ): { isFresh: boolean; intrinsicElementCount: number } => {
131
140
  const sentinelPath = join(jsxStoreDir, FINGERPRINT_FILENAME);
132
141
 
133
142
  if (!existsSync(sentinelPath)) {
134
- return { fresh: false, intrinsicElementCount: 0 };
143
+ return { isFresh: false, intrinsicElementCount: 0 };
135
144
  }
136
145
 
137
146
  let sentinel: JsxFingerprint;
@@ -139,12 +148,12 @@ const jsxStoreFreshness = (
139
148
  try {
140
149
  sentinel = JSON.parse(readFileSync(sentinelPath, "utf8")) as JsxFingerprint;
141
150
  } catch {
142
- return { fresh: false, intrinsicElementCount: 0 };
151
+ return { isFresh: false, intrinsicElementCount: 0 };
143
152
  }
144
153
 
145
154
  return sentinel.value === hashJsx(input)
146
- ? { fresh: true, intrinsicElementCount: sentinel.intrinsicElementCount }
147
- : { fresh: false, intrinsicElementCount: 0 };
155
+ ? { isFresh: true, intrinsicElementCount: sentinel.intrinsicElementCount }
156
+ : { isFresh: false, intrinsicElementCount: 0 };
148
157
  };
149
158
 
150
159
  export {
package/src/gi.ts CHANGED
@@ -1,26 +1,28 @@
1
1
  import type { Library } from "./gir/library.js";
2
+ import type { StoreOptions } from "./store/store-fs.js";
2
3
  import { computeGiFingerprint } from "./fingerprint.js";
3
4
  import { namespaceDirectory } from "./gir/namespace.js";
4
- import { type GiNamespaceInput, type GiStoreOptions, writeGiStore } from "./store/gi-store.js";
5
+ import { type GiNamespaceInput, writeGiStore } from "./store/gi-store.js";
5
6
  import { generateNamespaceModule } from "./store/gi/pipeline.js";
6
7
 
7
- /**
8
- * Generates the `@gtkx/gi` store from the loaded GIR library and links it into the project. This is the
9
- * react-free half of codegen: it can run before `@gtkx/gi` (or `@gtkx/react`) exists, since it produces them.
10
- *
11
- * @returns The number of namespaces written.
12
- */
13
- const runGiCodegen = (library: Library, gi: GiStoreOptions, libraries: string[], girPath: string[]): number => {
8
+ type GiCodegenOptions = {
9
+ gi: StoreOptions;
10
+ libraries: string[];
11
+ girPath: string[];
12
+ };
13
+
14
+ const runGiCodegen = (library: Library, options: GiCodegenOptions): number => {
15
+ const { gi, libraries, girPath } = options;
16
+
14
17
  const namespaces: GiNamespaceInput[] = Array.from(library.namespaces.values(), (namespace) => ({
15
18
  directory: namespaceDirectory(namespace),
16
19
  rawSource: generateNamespaceModule(namespace, library),
17
20
  }));
18
21
 
19
- writeGiStore(gi, namespaces, computeGiFingerprint(library.girFiles, [...libraries], [...girPath]));
22
+ const fingerprint = computeGiFingerprint(library.girFiles, [...libraries], [...girPath]);
23
+ writeGiStore(gi, namespaces, fingerprint);
20
24
 
21
25
  return library.namespaces.size;
22
26
  };
23
27
 
24
- export { isGiStoreFresh } from "./fingerprint.js";
25
- export type { GiStoreOptions } from "./store/gi-store.js";
26
28
  export { runGiCodegen };
package/src/gir/field.ts CHANGED
@@ -11,7 +11,6 @@ type GirField = {
11
11
  writable: boolean;
12
12
  private: boolean;
13
13
  bits: number | undefined;
14
- /** Members of an anonymous nested `<union>`/`<record>`, which occupies a slot but has no type. */
15
14
  inlineMembers: GirField[] | undefined;
16
15
  inlineIsUnion: boolean;
17
16
  };
@@ -29,9 +28,6 @@ const fieldFromNode = (node: RawNode, context: ParseContext): GirField => ({
29
28
  inlineIsUnion: false,
30
29
  });
31
30
 
32
- // A record may embed an anonymous `<union>` or `<record>` instead of naming a type for it. The
33
- // member occupies a real slot and contributes its own size and alignment, but carries no `<type>`,
34
- // so it has to be admitted as a field whose layout comes from its own children.
35
31
  const anonymousMemberFromNode = (node: RawNode, isUnion: boolean, context: ParseContext): GirField => ({
36
32
  name: nameAttr(node),
37
33
  doc: getDoc(node),
@@ -4,6 +4,13 @@ import { existsSync } from "node:fs";
4
4
 
5
5
  const SYSTEM_GIR_PATH = "/usr/share/gir-1.0";
6
6
 
7
+ /**
8
+ * The directories to search for `.gir` files, in precedence order: the config's own `girPath`, then
9
+ * `GTKX_GIR_PATH`, then `/usr/share/gir-1.0`, then the girdir pkg-config reports for
10
+ * gobject-introspection-1.0. Duplicates are dropped, and the last two are included only when they exist.
11
+ *
12
+ * @throws If pkg-config is installed but fails while being queried.
13
+ */
7
14
  const resolveGirPath = (configGirPath: string[] | undefined): string[] => {
8
15
  const paths: string[] = [];
9
16