@gtkx/gir 0.10.5 → 0.11.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.
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Naming utilities for GIR identifiers.
3
+ */
4
+ /** Converts kebab-case or snake_case to camelCase */
5
+ export declare const toCamelCase: (str: string) => string;
6
+ /** Converts kebab-case or snake_case to PascalCase */
7
+ export declare const toPascalCase: (str: string) => string;
package/dist/utils.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Naming utilities for GIR identifiers.
3
+ */
4
+ /** Converts kebab-case or snake_case to camelCase */
5
+ export const toCamelCase = (str) => str.replace(/[-_]([a-z])/g, (_, letter) => letter.toUpperCase());
6
+ /** Converts kebab-case or snake_case to PascalCase */
7
+ export const toPascalCase = (str) => {
8
+ const camel = toCamelCase(str);
9
+ return camel.charAt(0).toUpperCase() + camel.slice(1);
10
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/gir",
3
- "version": "0.10.5",
3
+ "version": "0.11.0",
4
4
  "description": "GObject Introspection file parser for GTKX",
5
5
  "keywords": [
6
6
  "gtk",
@@ -1,2 +0,0 @@
1
- export declare const CLASS_RENAMES: Map<string, string>;
2
- export declare const normalizeClassName: (name: string, namespace?: string) => string;
@@ -1,12 +0,0 @@
1
- import { toPascalCase } from "./naming.js";
2
- export const CLASS_RENAMES = new Map([["Error", "GError"]]);
3
- export const normalizeClassName = (name, namespace) => {
4
- const pascalName = toPascalCase(name);
5
- if (CLASS_RENAMES.has(pascalName)) {
6
- return CLASS_RENAMES.get(pascalName);
7
- }
8
- if (pascalName === "Object" && namespace) {
9
- return namespace === "GObject" ? "GObject" : `${namespace}Object`;
10
- }
11
- return pascalName;
12
- };
@@ -1,13 +0,0 @@
1
- export interface SanitizeDocOptions {
2
- escapeXmlTags?: boolean;
3
- namespace?: string;
4
- linkStyle?: "namespaced" | "prefixed";
5
- }
6
- export declare function sanitizeDoc(doc: string, options?: SanitizeDocOptions): string;
7
- export declare function formatDoc(doc: string | undefined, indent?: string, options?: SanitizeDocOptions): string;
8
- interface DocParameter {
9
- name: string;
10
- doc: string | undefined;
11
- }
12
- export declare function formatMethodDoc(doc: string | undefined, params: DocParameter[], indent?: string, options?: SanitizeDocOptions): string;
13
- export {};
@@ -1,334 +0,0 @@
1
- const GIR_LINK_PATTERN = /\[([a-z]+)@([^\]]+)\]/gi;
2
- function parseGirLink(type, reference) {
3
- const linkType = type.toLowerCase();
4
- const validTypes = [
5
- "class",
6
- "iface",
7
- "struct",
8
- "enum",
9
- "flags",
10
- "error",
11
- "callback",
12
- "method",
13
- "vfunc",
14
- "func",
15
- "ctor",
16
- "property",
17
- "signal",
18
- "const",
19
- "type",
20
- "id",
21
- ];
22
- if (!validTypes.includes(linkType)) {
23
- }
24
- const parts = reference.split(".");
25
- if (parts.length === 0) {
26
- }
27
- if (linkType === "property" || linkType === "signal") {
28
- const colonIndex = reference.indexOf(":");
29
- if (colonIndex !== -1) {
30
- const beforeColon = reference.substring(0, colonIndex);
31
- const afterColon = reference.substring(colonIndex + 1);
32
- const beforeParts = beforeColon.split(".");
33
- if (beforeParts.length >= 2) {
34
- return {
35
- type: linkType,
36
- namespace: beforeParts[0],
37
- target: beforeParts.slice(1).join("."),
38
- member: afterColon.replace("::", ""),
39
- };
40
- }
41
- return {
42
- type: linkType,
43
- namespace: undefined,
44
- target: beforeColon,
45
- member: afterColon.replace("::", ""),
46
- };
47
- }
48
- }
49
- if (parts.length === 1) {
50
- return {
51
- type: linkType,
52
- namespace: undefined,
53
- target: parts[0] ?? "",
54
- member: undefined,
55
- };
56
- }
57
- if (parts.length === 2) {
58
- const first = parts[0] ?? "";
59
- const second = parts[1] ?? "";
60
- const isNamespace = first.length > 0 && first[0] === first[0]?.toUpperCase();
61
- if (linkType === "func" || linkType === "const") {
62
- return {
63
- type: linkType,
64
- namespace: first,
65
- target: second,
66
- member: undefined,
67
- };
68
- }
69
- if (isNamespace) {
70
- return {
71
- type: linkType,
72
- namespace: first,
73
- target: second,
74
- member: undefined,
75
- };
76
- }
77
- return {
78
- type: linkType,
79
- namespace: undefined,
80
- target: first,
81
- member: second,
82
- };
83
- }
84
- return {
85
- type: linkType,
86
- namespace: parts[0],
87
- target: parts[1] ?? "",
88
- member: parts.slice(2).join(".") || undefined,
89
- };
90
- }
91
- function formatGirLinkForTsDoc(link, options) {
92
- let displayText;
93
- if (link.member) {
94
- displayText = `${link.target}.${link.member}`;
95
- }
96
- else {
97
- displayText = link.target;
98
- }
99
- let linkTarget;
100
- if (options.linkStyle === "prefixed") {
101
- const effectiveNamespace = link.namespace ?? options.currentNamespace;
102
- if (effectiveNamespace && effectiveNamespace !== "Gtk") {
103
- linkTarget = `${effectiveNamespace}${displayText}`;
104
- }
105
- else {
106
- linkTarget = displayText;
107
- }
108
- }
109
- else {
110
- linkTarget = link.namespace ? `${link.namespace}.${displayText}` : displayText;
111
- }
112
- switch (link.type) {
113
- case "class":
114
- case "iface":
115
- case "struct":
116
- case "enum":
117
- case "flags":
118
- case "error":
119
- case "callback":
120
- case "type":
121
- return `{@link ${linkTarget}}`;
122
- case "method":
123
- case "vfunc":
124
- case "func":
125
- case "ctor":
126
- return `{@link ${linkTarget}}`;
127
- case "property":
128
- return `{@link ${linkTarget}}`;
129
- case "signal":
130
- return `{@link ${linkTarget}}`;
131
- case "const":
132
- return `{@link ${linkTarget}}`;
133
- case "id":
134
- return `\`${link.target}\``;
135
- default:
136
- return `\`${displayText}\``;
137
- }
138
- }
139
- function convertGirLinks(text, options) {
140
- return text.replace(GIR_LINK_PATTERN, (_, type, reference) => {
141
- const link = parseGirLink(type, reference);
142
- if (!link) {
143
- return `\`${reference}\``;
144
- }
145
- return formatGirLinkForTsDoc(link, options);
146
- });
147
- }
148
- const GTK_DOCS_BASE_URLS = {
149
- Gtk: "https://docs.gtk.org/gtk4",
150
- Gdk: "https://docs.gtk.org/gdk4",
151
- Gsk: "https://docs.gtk.org/gsk4",
152
- GLib: "https://docs.gtk.org/glib",
153
- GObject: "https://docs.gtk.org/gobject",
154
- Gio: "https://docs.gtk.org/gio",
155
- Pango: "https://docs.gtk.org/Pango",
156
- PangoCairo: "https://docs.gtk.org/PangoCairo",
157
- GdkPixbuf: "https://docs.gtk.org/gdk-pixbuf",
158
- Cairo: "https://docs.gtk.org/cairo",
159
- Adw: "https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest",
160
- };
161
- function getDocsBaseUrl(namespace) {
162
- if (!namespace) {
163
- return "https://docs.gtk.org/gtk4";
164
- }
165
- const baseUrl = GTK_DOCS_BASE_URLS[namespace];
166
- if (baseUrl) {
167
- return baseUrl;
168
- }
169
- return `https://docs.gtk.org/${namespace.toLowerCase()}`;
170
- }
171
- function convertHtmlImageElements(text, baseUrl) {
172
- let result = text;
173
- result = result.replace(/<picture[^>]*>([\s\S]*?)<\/picture>/gi, (_, pictureContent) => {
174
- const imgMatch = /<img[^>]*alt="([^"]*)"[^>]*src="([^"]*)"[^>]*\/?>/i.exec(pictureContent);
175
- if (!imgMatch) {
176
- const imgMatch2 = /<img[^>]*src="([^"]*)"[^>]*alt="([^"]*)"[^>]*\/?>/i.exec(pictureContent);
177
- if (imgMatch2) {
178
- const src = imgMatch2[1];
179
- const alt = imgMatch2[2];
180
- return `![${alt}](${baseUrl}/${src})`;
181
- }
182
- return "";
183
- }
184
- const alt = imgMatch[1];
185
- const src = imgMatch[2];
186
- return `![${alt}](${baseUrl}/${src})`;
187
- });
188
- result = result.replace(/<img[^>]*alt="([^"]*)"[^>]*src="([^"]*)"[^>]*\/?>/gi, (_, alt, src) => {
189
- if (src.startsWith("http://") || src.startsWith("https://")) {
190
- return `![${alt}](${src})`;
191
- }
192
- return `![${alt}](${baseUrl}/${src})`;
193
- });
194
- result = result.replace(/<img[^>]*src="([^"]*)"[^>]*alt="([^"]*)"[^>]*\/?>/gi, (_, src, alt) => {
195
- if (src.startsWith("http://") || src.startsWith("https://")) {
196
- return `![${alt}](${src})`;
197
- }
198
- return `![${alt}](${baseUrl}/${src})`;
199
- });
200
- result = result.replace(/<img[^>]*src="([^"]*)"[^>]*\/?>/gi, (_, src) => {
201
- if (src.startsWith("http://") || src.startsWith("https://")) {
202
- return `![](${src})`;
203
- }
204
- return `![](${baseUrl}/${src})`;
205
- });
206
- result = result.replace(/<source[^>]*\/?>/gi, "");
207
- return result;
208
- }
209
- function convertMarkdownImageUrls(text, baseUrl) {
210
- return text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (match, alt, src) => {
211
- if (src.startsWith("http://") || src.startsWith("https://")) {
212
- return match;
213
- }
214
- return `![${alt}](${baseUrl}/${src})`;
215
- });
216
- }
217
- function convertKbdElements(text) {
218
- return text.replace(/<kbd>([^<]*)<\/kbd>/gi, "`$1`");
219
- }
220
- function stripHtmlLinks(text) {
221
- return text.replace(/\[([^\]]+)\]\([^)]+\.html[^)]*\)/gi, "$1");
222
- }
223
- function convertAtAnnotations(text) {
224
- return text.replace(/(?<!\{)@([a-zA-Z_][a-zA-Z0-9_]*)\b(?!\s*\{)/g, "`$1`");
225
- }
226
- function escapeXmlStyleTags(text) {
227
- const codeBlockPattern = /```[\s\S]*?```/g;
228
- const codeBlocks = [];
229
- let processed = text.replace(codeBlockPattern, (match) => {
230
- codeBlocks.push(match);
231
- return `__CODE_BLOCK_${codeBlocks.length - 1}__`;
232
- });
233
- processed = processed.replace(/<(\/?)(child|object|property|signal|template|style|item|attribute)>/gi, "`<$1$2>`");
234
- for (let i = 0; i < codeBlocks.length; i++) {
235
- processed = processed.replace(`__CODE_BLOCK_${i}__`, codeBlocks[i] ?? "");
236
- }
237
- return processed;
238
- }
239
- function cleanupWhitespace(text) {
240
- let result = text.replace(/\n{3,}/g, "\n\n");
241
- result = result.replace(/[ \t]+$/gm, "");
242
- return result.trim();
243
- }
244
- function protectCodeBlocks(text) {
245
- const codeBlockPattern = /```[\s\S]*?```/g;
246
- const codeBlocks = [];
247
- const processed = text.replace(codeBlockPattern, (match) => {
248
- codeBlocks.push(match);
249
- return `__PROTECTED_CODE_BLOCK_${codeBlocks.length - 1}__`;
250
- });
251
- const restore = (formatted, linePrefix) => {
252
- let result = formatted;
253
- for (let i = 0; i < codeBlocks.length; i++) {
254
- const placeholder = `__PROTECTED_CODE_BLOCK_${i}__`;
255
- const codeBlock = codeBlocks[i];
256
- if (codeBlock === undefined)
257
- continue;
258
- const codeLines = codeBlock.split("\n");
259
- const prefixedCodeLines = codeLines.map((line, index) => {
260
- if (index === 0)
261
- return line;
262
- return `${linePrefix}${line}`;
263
- });
264
- result = result.replace(placeholder, prefixedCodeLines.join("\n"));
265
- }
266
- return result;
267
- };
268
- return { text: processed, restore };
269
- }
270
- export function sanitizeDoc(doc, options = {}) {
271
- let result = doc;
272
- const baseUrl = getDocsBaseUrl(options.namespace);
273
- result = convertHtmlImageElements(result, baseUrl);
274
- result = convertMarkdownImageUrls(result, baseUrl);
275
- result = convertKbdElements(result);
276
- result = stripHtmlLinks(result);
277
- const linkFormatOptions = {
278
- linkStyle: options.linkStyle ?? "namespaced",
279
- currentNamespace: options.namespace,
280
- };
281
- result = convertGirLinks(result, linkFormatOptions);
282
- result = convertAtAnnotations(result);
283
- if (options.escapeXmlTags) {
284
- result = escapeXmlStyleTags(result);
285
- }
286
- result = cleanupWhitespace(result);
287
- return result;
288
- }
289
- export function formatDoc(doc, indent = "", options = {}) {
290
- if (!doc) {
291
- return "";
292
- }
293
- const sanitized = sanitizeDoc(doc, options);
294
- if (!sanitized) {
295
- return "";
296
- }
297
- const { text: protectedText, restore } = protectCodeBlocks(sanitized);
298
- const lines = protectedText.split("\n").map((line) => line.trim());
299
- const firstLine = lines[0] ?? "";
300
- if (lines.length === 1 && firstLine.length < 80) {
301
- const result = `${indent}/** ${firstLine} */\n`;
302
- return restore(result, `${indent} * `);
303
- }
304
- const formattedLines = lines.map((line) => `${indent} * ${line}`);
305
- const result = `${indent}/**\n${formattedLines.join("\n")}\n${indent} */\n`;
306
- return restore(result, `${indent} * `);
307
- }
308
- export function formatMethodDoc(doc, params, indent = " ", options = {}) {
309
- const sanitizedDoc = doc ? sanitizeDoc(doc, options) : undefined;
310
- const hasDocumentation = sanitizedDoc || params.some((p) => p.doc);
311
- if (!hasDocumentation) {
312
- return "";
313
- }
314
- const lines = [];
315
- let protection;
316
- if (sanitizedDoc) {
317
- protection = protectCodeBlocks(sanitizedDoc);
318
- for (const line of protection.text.split("\n")) {
319
- lines.push(` * ${line.trim()}`);
320
- }
321
- }
322
- for (const param of params) {
323
- if (param.doc && param.name && param.name !== "..." && param.name !== "") {
324
- const sanitizedParamDoc = sanitizeDoc(param.doc, options);
325
- const paramDocFirstLine = sanitizedParamDoc.split("\n")[0]?.trim() ?? "";
326
- lines.push(` * @param ${param.name} - ${paramDocFirstLine}`);
327
- }
328
- }
329
- if (lines.length === 0) {
330
- return "";
331
- }
332
- const result = `${indent}/**\n${indent}${lines.join(`\n${indent}`)}\n${indent} */\n`;
333
- return protection ? protection.restore(result, `${indent} * `) : result;
334
- }
package/dist/naming.d.ts DELETED
@@ -1,6 +0,0 @@
1
- export declare const RESERVED_WORDS: Set<string>;
2
- export declare const toCamelCase: (str: string) => string;
3
- export declare const toPascalCase: (str: string) => string;
4
- export declare const toKebabCase: (str: string) => string;
5
- export declare const toConstantCase: (str: string) => string;
6
- export declare const toValidIdentifier: (str: string) => string;
package/dist/naming.js DELETED
@@ -1,69 +0,0 @@
1
- export const RESERVED_WORDS = new Set([
2
- "break",
3
- "case",
4
- "catch",
5
- "class",
6
- "const",
7
- "continue",
8
- "debugger",
9
- "default",
10
- "delete",
11
- "do",
12
- "else",
13
- "export",
14
- "extends",
15
- "false",
16
- "finally",
17
- "for",
18
- "function",
19
- "if",
20
- "import",
21
- "in",
22
- "instanceof",
23
- "new",
24
- "null",
25
- "return",
26
- "super",
27
- "switch",
28
- "this",
29
- "throw",
30
- "true",
31
- "try",
32
- "typeof",
33
- "var",
34
- "void",
35
- "while",
36
- "with",
37
- "yield",
38
- "let",
39
- "static",
40
- "enum",
41
- "implements",
42
- "interface",
43
- "package",
44
- "private",
45
- "protected",
46
- "public",
47
- "await",
48
- "async",
49
- "eval",
50
- "arguments",
51
- ]);
52
- export const toCamelCase = (str) => str.replace(/[-_]([a-z])/g, (_, letter) => letter.toUpperCase());
53
- export const toPascalCase = (str) => {
54
- const camel = toCamelCase(str);
55
- return camel.charAt(0).toUpperCase() + camel.slice(1);
56
- };
57
- export const toKebabCase = (str) => str
58
- .replace(/([a-z])([A-Z])/g, "$1-$2")
59
- .replace(/_/g, "-")
60
- .toLowerCase();
61
- export const toConstantCase = (str) => str.replace(/-/g, "_").toUpperCase();
62
- export const toValidIdentifier = (str) => {
63
- let result = str.replace(/[^a-zA-Z0-9_$]/g, "_");
64
- if (RESERVED_WORDS.has(result))
65
- result = `_${result}`;
66
- if (/^\d/.test(result))
67
- result = `_${result}`;
68
- return result;
69
- };
package/dist/parser.d.ts DELETED
@@ -1,76 +0,0 @@
1
- /**
2
- * GObject Introspection XML (GIR) parser module.
3
- *
4
- * Parses GIR files to extract type information for GTK/GLib libraries.
5
- * Used by the code generator to create TypeScript bindings.
6
- *
7
- * @packageDocumentation
8
- */
9
- import type { GirNamespace } from "./types.js";
10
- /**
11
- * Parser for GObject Introspection XML (GIR) files.
12
- *
13
- * Converts GIR XML into structured TypeScript objects that can be used
14
- * for code generation. Handles all GIR elements including classes,
15
- * interfaces, functions, signals, properties, and type information.
16
- *
17
- * @example
18
- * ```tsx
19
- * import { GirParser } from "@gtkx/gir";
20
- * import { readFileSync } from "fs";
21
- *
22
- * const parser = new GirParser();
23
- * const xml = readFileSync("Gtk-4.0.gir", "utf-8");
24
- * const namespace = parser.parse(xml);
25
- *
26
- * console.log(namespace.name); // "Gtk"
27
- * console.log(namespace.classes.length); // Number of GTK classes
28
- * ```
29
- *
30
- * @see {@link GirNamespace} for the parsed output structure
31
- */
32
- export declare class GirParser {
33
- private parser;
34
- constructor();
35
- /**
36
- * Parses a GIR XML string into a structured namespace object.
37
- *
38
- * @param girXml - The raw XML content of a GIR file
39
- * @returns A parsed namespace containing all type information
40
- * @throws Error if the XML is missing required repository or namespace elements
41
- *
42
- * @example
43
- * ```tsx
44
- * const namespace = parser.parse(girXml);
45
- *
46
- * // Access parsed classes
47
- * for (const cls of namespace.classes) {
48
- * console.log(`Class: ${cls.name}, Parent: ${cls.parent}`);
49
- * }
50
- *
51
- * // Access parsed functions
52
- * for (const fn of namespace.functions) {
53
- * console.log(`Function: ${fn.name} -> ${fn.returnType.name}`);
54
- * }
55
- * ```
56
- */
57
- parse(girXml: string): GirNamespace;
58
- private parseCallbacks;
59
- private parseClasses;
60
- private parseImplements;
61
- private parseInterfaces;
62
- private parsePrerequisites;
63
- private parseMethods;
64
- private parseConstructors;
65
- private parseFunctions;
66
- private parseParameters;
67
- private parseReturnType;
68
- private parseType;
69
- private parseProperties;
70
- private parseSignals;
71
- private parseRecords;
72
- private parseFields;
73
- private parseEnumerations;
74
- private parseEnumerationMembers;
75
- private parseConstants;
76
- }