breeze-bindgen 1.0.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.
- package/dist/cli.mjs +5496 -0
- package/dist/index.cjs +733 -0
- package/dist/index.mjs +737 -0
- package/package.json +35 -0
package/dist/index.cjs
ADDED
@@ -0,0 +1,733 @@
|
|
1
|
+
var __create = Object.create;
|
2
|
+
var __defProp = Object.defineProperty;
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
7
|
+
var __commonJS = (cb, mod) => function __require() {
|
8
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
9
|
+
};
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
12
|
+
for (let key of __getOwnPropNames(from))
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
15
|
+
}
|
16
|
+
return to;
|
17
|
+
};
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
19
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
20
|
+
// file that has been converted to a CommonJS file using a Babel-
|
21
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
22
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
23
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
24
|
+
mod
|
25
|
+
));
|
26
|
+
|
27
|
+
// node_modules/color-support/index.js
|
28
|
+
var require_color_support = __commonJS({
|
29
|
+
"node_modules/color-support/index.js"(exports2, module2) {
|
30
|
+
module2.exports = colorSupport({ alwaysReturn: true }, colorSupport);
|
31
|
+
function hasNone(obj, options) {
|
32
|
+
obj.level = 0;
|
33
|
+
obj.hasBasic = false;
|
34
|
+
obj.has256 = false;
|
35
|
+
obj.has16m = false;
|
36
|
+
if (!options.alwaysReturn) {
|
37
|
+
return false;
|
38
|
+
}
|
39
|
+
return obj;
|
40
|
+
}
|
41
|
+
function hasBasic(obj) {
|
42
|
+
obj.hasBasic = true;
|
43
|
+
obj.has256 = false;
|
44
|
+
obj.has16m = false;
|
45
|
+
obj.level = 1;
|
46
|
+
return obj;
|
47
|
+
}
|
48
|
+
function has256(obj) {
|
49
|
+
obj.hasBasic = true;
|
50
|
+
obj.has256 = true;
|
51
|
+
obj.has16m = false;
|
52
|
+
obj.level = 2;
|
53
|
+
return obj;
|
54
|
+
}
|
55
|
+
function has16m(obj) {
|
56
|
+
obj.hasBasic = true;
|
57
|
+
obj.has256 = true;
|
58
|
+
obj.has16m = true;
|
59
|
+
obj.level = 3;
|
60
|
+
return obj;
|
61
|
+
}
|
62
|
+
function colorSupport(options, obj) {
|
63
|
+
options = options || {};
|
64
|
+
obj = obj || {};
|
65
|
+
if (typeof options.level === "number") {
|
66
|
+
switch (options.level) {
|
67
|
+
case 0:
|
68
|
+
return hasNone(obj, options);
|
69
|
+
case 1:
|
70
|
+
return hasBasic(obj);
|
71
|
+
case 2:
|
72
|
+
return has256(obj);
|
73
|
+
case 3:
|
74
|
+
return has16m(obj);
|
75
|
+
}
|
76
|
+
}
|
77
|
+
obj.level = 0;
|
78
|
+
obj.hasBasic = false;
|
79
|
+
obj.has256 = false;
|
80
|
+
obj.has16m = false;
|
81
|
+
if (typeof process === "undefined" || !process || !process.stdout || !process.env || !process.platform) {
|
82
|
+
return hasNone(obj, options);
|
83
|
+
}
|
84
|
+
var env = options.env || process.env;
|
85
|
+
var stream = options.stream || process.stdout;
|
86
|
+
var term = options.term || env.TERM || "";
|
87
|
+
var platform = options.platform || process.platform;
|
88
|
+
if (!options.ignoreTTY && !stream.isTTY) {
|
89
|
+
return hasNone(obj, options);
|
90
|
+
}
|
91
|
+
if (!options.ignoreDumb && term === "dumb" && !env.COLORTERM) {
|
92
|
+
return hasNone(obj, options);
|
93
|
+
}
|
94
|
+
if (platform === "win32") {
|
95
|
+
return hasBasic(obj);
|
96
|
+
}
|
97
|
+
if (env.TMUX) {
|
98
|
+
return has256(obj);
|
99
|
+
}
|
100
|
+
if (!options.ignoreCI && (env.CI || env.TEAMCITY_VERSION)) {
|
101
|
+
if (env.TRAVIS) {
|
102
|
+
return has256(obj);
|
103
|
+
} else {
|
104
|
+
return hasNone(obj, options);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
switch (env.TERM_PROGRAM) {
|
108
|
+
case "iTerm.app":
|
109
|
+
var ver = env.TERM_PROGRAM_VERSION || "0.";
|
110
|
+
if (/^[0-2]\./.test(ver)) {
|
111
|
+
return has256(obj);
|
112
|
+
} else {
|
113
|
+
return has16m(obj);
|
114
|
+
}
|
115
|
+
case "HyperTerm":
|
116
|
+
case "Hyper":
|
117
|
+
return has16m(obj);
|
118
|
+
case "MacTerm":
|
119
|
+
return has16m(obj);
|
120
|
+
case "Apple_Terminal":
|
121
|
+
return has256(obj);
|
122
|
+
}
|
123
|
+
if (/^xterm-256/.test(term)) {
|
124
|
+
return has256(obj);
|
125
|
+
}
|
126
|
+
if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(term)) {
|
127
|
+
return hasBasic(obj);
|
128
|
+
}
|
129
|
+
if (env.COLORTERM) {
|
130
|
+
return hasBasic(obj);
|
131
|
+
}
|
132
|
+
return hasNone(obj, options);
|
133
|
+
}
|
134
|
+
}
|
135
|
+
});
|
136
|
+
|
137
|
+
// node_modules/fancy-log/index.js
|
138
|
+
var require_fancy_log = __commonJS({
|
139
|
+
"node_modules/fancy-log/index.js"(exports2, module2) {
|
140
|
+
"use strict";
|
141
|
+
var util = require("util");
|
142
|
+
var Console = require("console").Console;
|
143
|
+
var supportsColor = require_color_support();
|
144
|
+
var console = new Console({
|
145
|
+
stdout: process.stdout,
|
146
|
+
stderr: process.stderr,
|
147
|
+
colorMode: false
|
148
|
+
});
|
149
|
+
function hasFlag(flag) {
|
150
|
+
return process.argv.indexOf("--" + flag) !== -1;
|
151
|
+
}
|
152
|
+
function hasColors() {
|
153
|
+
if (hasFlag("no-color")) {
|
154
|
+
return false;
|
155
|
+
}
|
156
|
+
if (hasFlag("color")) {
|
157
|
+
return true;
|
158
|
+
}
|
159
|
+
if (supportsColor()) {
|
160
|
+
return true;
|
161
|
+
}
|
162
|
+
return false;
|
163
|
+
}
|
164
|
+
function Timestamp() {
|
165
|
+
this.now = /* @__PURE__ */ new Date();
|
166
|
+
}
|
167
|
+
Timestamp.prototype[util.inspect.custom] = function(depth, opts) {
|
168
|
+
var timestamp = this.now.toLocaleTimeString("en", { hour12: false });
|
169
|
+
return "[" + opts.stylize(timestamp, "date") + "]";
|
170
|
+
};
|
171
|
+
function getTimestamp() {
|
172
|
+
return util.inspect(new Timestamp(), { colors: hasColors() });
|
173
|
+
}
|
174
|
+
function log3() {
|
175
|
+
var time = getTimestamp();
|
176
|
+
process.stdout.write(time + " ");
|
177
|
+
console.log.apply(console, arguments);
|
178
|
+
return this;
|
179
|
+
}
|
180
|
+
function info() {
|
181
|
+
var time = getTimestamp();
|
182
|
+
process.stdout.write(time + " ");
|
183
|
+
console.info.apply(console, arguments);
|
184
|
+
return this;
|
185
|
+
}
|
186
|
+
function dir() {
|
187
|
+
var time = getTimestamp();
|
188
|
+
process.stdout.write(time + " ");
|
189
|
+
console.dir.apply(console, arguments);
|
190
|
+
return this;
|
191
|
+
}
|
192
|
+
function warn() {
|
193
|
+
var time = getTimestamp();
|
194
|
+
process.stderr.write(time + " ");
|
195
|
+
console.warn.apply(console, arguments);
|
196
|
+
return this;
|
197
|
+
}
|
198
|
+
function error() {
|
199
|
+
var time = getTimestamp();
|
200
|
+
process.stderr.write(time + " ");
|
201
|
+
console.error.apply(console, arguments);
|
202
|
+
return this;
|
203
|
+
}
|
204
|
+
module2.exports = log3;
|
205
|
+
module2.exports.info = info;
|
206
|
+
module2.exports.dir = dir;
|
207
|
+
module2.exports.warn = warn;
|
208
|
+
module2.exports.error = error;
|
209
|
+
}
|
210
|
+
});
|
211
|
+
|
212
|
+
// index.ts
|
213
|
+
var import_fancy_log2 = __toESM(require_fancy_log());
|
214
|
+
|
215
|
+
// core.ts
|
216
|
+
var import_fancy_log = __toESM(require_fancy_log());
|
217
|
+
var import_node_fs = require("node:fs");
|
218
|
+
var import_node_path = require("node:path");
|
219
|
+
var import_node_url = require("node:url");
|
220
|
+
var import_node_child_process = require("node:child_process");
|
221
|
+
|
222
|
+
// c-type-parser.ts
|
223
|
+
var CTypeParser = class {
|
224
|
+
tokens = [];
|
225
|
+
cursor = 0;
|
226
|
+
lex(str) {
|
227
|
+
this.tokens = [];
|
228
|
+
let current = "";
|
229
|
+
const BREAK_TOKENS = [" ", "(", ")", ",", "<", ">"];
|
230
|
+
for (let i = 0; i < str.length; i++) {
|
231
|
+
const c = str[i];
|
232
|
+
if (BREAK_TOKENS.includes(c)) {
|
233
|
+
if (current.length > 0) {
|
234
|
+
this.tokens.push(current);
|
235
|
+
current = "";
|
236
|
+
}
|
237
|
+
if (c !== " ") {
|
238
|
+
this.tokens.push(c);
|
239
|
+
}
|
240
|
+
} else {
|
241
|
+
current += c;
|
242
|
+
}
|
243
|
+
}
|
244
|
+
if (current.length > 0) {
|
245
|
+
this.tokens.push(current);
|
246
|
+
}
|
247
|
+
}
|
248
|
+
parse(str = null, typeTransformer = (orig) => orig) {
|
249
|
+
if (str) {
|
250
|
+
this.lex(str);
|
251
|
+
this.cursor = 0;
|
252
|
+
}
|
253
|
+
const type = {
|
254
|
+
argsFunc: [],
|
255
|
+
argsTemplate: [],
|
256
|
+
function: false,
|
257
|
+
template: false,
|
258
|
+
type: ""
|
259
|
+
};
|
260
|
+
do {
|
261
|
+
const parseCommaList = (arr) => {
|
262
|
+
do {
|
263
|
+
const res = this.parse(null, typeTransformer);
|
264
|
+
if (res)
|
265
|
+
arr.push(res);
|
266
|
+
} while (this.eat(","));
|
267
|
+
};
|
268
|
+
if (this.eat("(")) {
|
269
|
+
type.function = true;
|
270
|
+
if (!this.next(")"))
|
271
|
+
parseCommaList(type.argsFunc);
|
272
|
+
this.eat(")", true);
|
273
|
+
} else if (this.eat("<")) {
|
274
|
+
type.template = true;
|
275
|
+
if (!this.next(">"))
|
276
|
+
parseCommaList(type.argsTemplate);
|
277
|
+
this.eat(">", true);
|
278
|
+
} else {
|
279
|
+
type.type = typeTransformer(this.tokens[this.cursor]);
|
280
|
+
this.cursor++;
|
281
|
+
}
|
282
|
+
if (this.next("(") || this.next("<")) {
|
283
|
+
continue;
|
284
|
+
}
|
285
|
+
break;
|
286
|
+
} while (true);
|
287
|
+
return type;
|
288
|
+
}
|
289
|
+
eat(token, force = false) {
|
290
|
+
if (this.next(token)) {
|
291
|
+
this.cursor++;
|
292
|
+
return true;
|
293
|
+
} else {
|
294
|
+
if (force) throw new Error(`Excepted: ${token}, found ${this.next()} in ${this.tokens.join(" ")}`);
|
295
|
+
}
|
296
|
+
return false;
|
297
|
+
}
|
298
|
+
next(token = null) {
|
299
|
+
if (this.tokens[this.cursor] === token || !token) {
|
300
|
+
return this.tokens[this.cursor];
|
301
|
+
}
|
302
|
+
return false;
|
303
|
+
}
|
304
|
+
formatToC(node) {
|
305
|
+
let str = node.type;
|
306
|
+
if (node.template) {
|
307
|
+
str += "<" + node.argsTemplate.map((a) => this.formatToC(a)).join(", ") + ">";
|
308
|
+
}
|
309
|
+
if (node.function) {
|
310
|
+
str += "(" + node.argsFunc.map((a) => this.formatToC(a)).join(", ") + ")";
|
311
|
+
}
|
312
|
+
return str;
|
313
|
+
}
|
314
|
+
formatToTypeScript(node, namespace = "") {
|
315
|
+
if (node.type.startsWith(namespace))
|
316
|
+
node.type = node.type.slice(namespace.length);
|
317
|
+
node.type = node.type.split("::").filter(Boolean).join(".");
|
318
|
+
const typeMap = {
|
319
|
+
"int": "number",
|
320
|
+
"float": "number",
|
321
|
+
"double": "number",
|
322
|
+
"std.string": "string",
|
323
|
+
"std.vector": "Array",
|
324
|
+
"bool": "boolean"
|
325
|
+
};
|
326
|
+
let tsBasicType = (typeMap[node.type] ?? node.type) + (node.template ? "<" + node.argsTemplate.map((a) => this.formatToTypeScript(a, namespace)).join(", ") + ">" : "");
|
327
|
+
const ignoreTypes = ["std.variant", "std.shared_ptr", "std.function"];
|
328
|
+
if (ignoreTypes.includes(node.type)) {
|
329
|
+
tsBasicType = node.argsTemplate.map((a) => this.formatToTypeScript(a, namespace)).join(" | ");
|
330
|
+
} else if (node.type === "std.optional") {
|
331
|
+
tsBasicType = `${this.formatToTypeScript(node.argsTemplate[0], namespace)} | undefined`;
|
332
|
+
} else if (node.type === "std.pair" || node.type === "std.tuple") {
|
333
|
+
tsBasicType = node.argsTemplate.map((a) => this.formatToTypeScript(a, namespace)).join(", ");
|
334
|
+
tsBasicType = `[${tsBasicType}]`;
|
335
|
+
}
|
336
|
+
if (node.function) {
|
337
|
+
return `((${node.argsFunc.map((a) => this.formatToTypeScript(a, namespace)).map((v, i) => `arg${i + 1}: ${v}`).join(", ")}) => ${tsBasicType})`;
|
338
|
+
}
|
339
|
+
return tsBasicType;
|
340
|
+
}
|
341
|
+
};
|
342
|
+
var cTypeToTypeScript = (str, namespace) => {
|
343
|
+
const parser = new CTypeParser();
|
344
|
+
parser.lex(str);
|
345
|
+
const res = parser.parse();
|
346
|
+
return parser.formatToTypeScript(res, namespace);
|
347
|
+
};
|
348
|
+
|
349
|
+
// core.ts
|
350
|
+
var import_meta = {};
|
351
|
+
var DEFAULT_CPP_BINDING_OUTPUT_FILE = "binding_qjs.h";
|
352
|
+
var DEFAULT_TS_DEFINITION_OUTPUT_FILE = "binding_types.d.ts";
|
353
|
+
var DEFAULT_QUICKJS_TYPES_PATH = "quickjs-types.txt";
|
354
|
+
var DEFAULT_NAME_FILTER = "breeze::js::";
|
355
|
+
function parseFunctionQualType(type) {
|
356
|
+
let State;
|
357
|
+
((State2) => {
|
358
|
+
State2[State2["ReturnType"] = 0] = "ReturnType";
|
359
|
+
State2[State2["Args"] = 1] = "Args";
|
360
|
+
State2[State2["Done"] = 2] = "Done";
|
361
|
+
})(State || (State = {}));
|
362
|
+
let state = 0 /* ReturnType */;
|
363
|
+
let returnType = "";
|
364
|
+
let currentArg = "";
|
365
|
+
let args = [];
|
366
|
+
let depth = 0;
|
367
|
+
let angleBracketDepth = 0;
|
368
|
+
for (let i = 0; i < type.length; i++) {
|
369
|
+
const char = type[i];
|
370
|
+
if (char === "<") angleBracketDepth++;
|
371
|
+
else if (char === ">") angleBracketDepth--;
|
372
|
+
switch (state) {
|
373
|
+
case 0 /* ReturnType */:
|
374
|
+
if (char === "(" && angleBracketDepth === 0) {
|
375
|
+
state = 1 /* Args */;
|
376
|
+
returnType = returnType.trim();
|
377
|
+
} else {
|
378
|
+
returnType += char;
|
379
|
+
}
|
380
|
+
break;
|
381
|
+
case 1 /* Args */:
|
382
|
+
if (char === "(") depth++;
|
383
|
+
if (char === ")") {
|
384
|
+
if (depth === 0 && angleBracketDepth === 0) {
|
385
|
+
if (currentArg.trim()) args.push(currentArg.trim());
|
386
|
+
state = 2 /* Done */;
|
387
|
+
break;
|
388
|
+
}
|
389
|
+
depth--;
|
390
|
+
}
|
391
|
+
if (char === "," && depth === 0 && angleBracketDepth === 0) {
|
392
|
+
args.push(currentArg.trim());
|
393
|
+
currentArg = "";
|
394
|
+
} else {
|
395
|
+
currentArg += char;
|
396
|
+
}
|
397
|
+
break;
|
398
|
+
}
|
399
|
+
}
|
400
|
+
if (state !== 2 /* Done */) throw new Error("Invalid function type");
|
401
|
+
return { returnType, args };
|
402
|
+
}
|
403
|
+
function processAstAndGenerateCode(astArr, originalCppFilePath, bindgenDir, nameFilter, quickjsTypesPath) {
|
404
|
+
const origFileContent = (0, import_node_fs.readFileSync)(originalCppFilePath, "utf-8").split("\n").map((v) => v.trim());
|
405
|
+
const structNames = [];
|
406
|
+
const resolveUnderPath = (path, resolveName) => {
|
407
|
+
const ns = path.join("::");
|
408
|
+
const fullName = `${ns}::${resolveName}`;
|
409
|
+
if (structNames.includes(fullName)) return fullName;
|
410
|
+
if (path.length > 1) {
|
411
|
+
const parentPath = path.slice(0, -1);
|
412
|
+
const parentFullName = resolveUnderPath(parentPath, resolveName);
|
413
|
+
if (parentFullName) return parentFullName;
|
414
|
+
}
|
415
|
+
return resolveName;
|
416
|
+
};
|
417
|
+
const ctypeToQualified = (type, path) => {
|
418
|
+
const parser = new CTypeParser();
|
419
|
+
const parsed = parser.parse(type, (name) => resolveUnderPath(path, name));
|
420
|
+
return parser.formatToC(parsed);
|
421
|
+
};
|
422
|
+
let binding = `// This file is generated by bindgen
|
423
|
+
// Do not modify this file manually!
|
424
|
+
|
425
|
+
#pragma once
|
426
|
+
#include "binding_types.h" // Assuming this will be in the same output directory
|
427
|
+
#include "quickjs.h"
|
428
|
+
#include "quickjspp.hpp"
|
429
|
+
|
430
|
+
template <typename T>
|
431
|
+
struct js_bind {
|
432
|
+
static void bind(qjs::Context::Module &mod) {}
|
433
|
+
};
|
434
|
+
`;
|
435
|
+
let typescriptDef = `// This file is generated by bindgen
|
436
|
+
// Do not modify this file manually!
|
437
|
+
|
438
|
+
declare module 'mshell' { // TODO: Make module name configurable or derive
|
439
|
+
`;
|
440
|
+
const generateForRecordDecl = (node_struct, path) => {
|
441
|
+
if (node_struct.kind !== "CXXRecordDecl") throw new Error("Node is not a RecordDecl");
|
442
|
+
const structName = node_struct.name;
|
443
|
+
const fields = [];
|
444
|
+
const methods = [];
|
445
|
+
const bases = node_struct.bases?.filter((base) => !base.type.qualType.includes("std::")).map((base) => ({
|
446
|
+
access: base.access,
|
447
|
+
type: ctypeToQualified(base.type.qualType, path)
|
448
|
+
})) || [];
|
449
|
+
if (!node_struct.inner) return;
|
450
|
+
for (const node of node_struct.inner) {
|
451
|
+
if (node.name?.startsWith("$")) continue;
|
452
|
+
const lineNum = node.loc?.line;
|
453
|
+
let comment = "";
|
454
|
+
if (lineNum) {
|
455
|
+
let rangeCommentCnt = 0;
|
456
|
+
for (let i = lineNum - 2; i >= 0; i--) {
|
457
|
+
const line = origFileContent[i];
|
458
|
+
if (!line) continue;
|
459
|
+
if (line.startsWith("//")) {
|
460
|
+
comment = line.substring(2) + "\n" + comment;
|
461
|
+
continue;
|
462
|
+
}
|
463
|
+
if (line.startsWith("/*")) rangeCommentCnt++;
|
464
|
+
if (line.endsWith("*/")) rangeCommentCnt--;
|
465
|
+
if (rangeCommentCnt === 0 && (line.startsWith("/*") || line.endsWith("*/"))) {
|
466
|
+
if (line.startsWith("/*") && line.endsWith("*/")) {
|
467
|
+
comment = line.substring(2, line.length - 2) + "\n" + comment;
|
468
|
+
} else if (line.startsWith("/*")) {
|
469
|
+
} else if (line.endsWith("*/")) {
|
470
|
+
comment = line.substring(0, line.length - 2) + "\n" + comment;
|
471
|
+
}
|
472
|
+
break;
|
473
|
+
} else if (rangeCommentCnt > 0 || line.endsWith("*/")) {
|
474
|
+
comment = line.replaceAll("/*", "").replaceAll("*/", "*") + "\n" + comment;
|
475
|
+
} else if (rangeCommentCnt === 0 && !line.startsWith("//") && !line.startsWith("/*") && !line.endsWith("*/")) {
|
476
|
+
break;
|
477
|
+
}
|
478
|
+
}
|
479
|
+
}
|
480
|
+
comment = comment.trim();
|
481
|
+
if (node.kind === "FieldDecl") {
|
482
|
+
fields.push({
|
483
|
+
name: node.name,
|
484
|
+
type: ctypeToQualified(node.type.qualType, path),
|
485
|
+
comment: comment.length > 0 ? comment : void 0
|
486
|
+
});
|
487
|
+
}
|
488
|
+
if (node.kind === "CXXMethodDecl") {
|
489
|
+
const parsed = parseFunctionQualType(node.type.qualType);
|
490
|
+
if (["operator="].includes(node.name)) continue;
|
491
|
+
const argNames = [];
|
492
|
+
if (node.inner) {
|
493
|
+
for (const arg of node.inner) {
|
494
|
+
if (arg.kind === "ParmVarDecl") argNames.push(arg.name);
|
495
|
+
}
|
496
|
+
}
|
497
|
+
methods.push({
|
498
|
+
name: node.name,
|
499
|
+
returnType: ctypeToQualified(parsed.returnType, path),
|
500
|
+
args: parsed.args.map((arg) => ctypeToQualified(arg, path)),
|
501
|
+
static: node.storageClass === "static",
|
502
|
+
comment: comment.length > 0 ? comment : void 0,
|
503
|
+
argNames
|
504
|
+
});
|
505
|
+
}
|
506
|
+
}
|
507
|
+
const fullName = path.join("::") + "::" + structName;
|
508
|
+
if (bases.length === 0) {
|
509
|
+
binding += `
|
510
|
+
template <> struct qjs::js_traits<${fullName}> {
|
511
|
+
static ${fullName} unwrap(JSContext *ctx, JSValueConst v) {
|
512
|
+
${fullName} obj;
|
513
|
+
`;
|
514
|
+
for (const field of fields) {
|
515
|
+
binding += `
|
516
|
+
obj.${field.name} = js_traits<${field.type}>::unwrap(ctx, JS_GetPropertyStr(ctx, v, "${field.name}"));
|
517
|
+
`;
|
518
|
+
}
|
519
|
+
binding += `
|
520
|
+
return obj;
|
521
|
+
}
|
522
|
+
|
523
|
+
static JSValue wrap(JSContext *ctx, const ${fullName} &val) noexcept {
|
524
|
+
JSValue obj = JS_NewObject(ctx);
|
525
|
+
`;
|
526
|
+
for (const field of fields) {
|
527
|
+
binding += `
|
528
|
+
JS_SetPropertyStr(ctx, obj, "${field.name}", js_traits<${field.type}>::wrap(ctx, val.${field.name}));
|
529
|
+
`;
|
530
|
+
}
|
531
|
+
binding += `
|
532
|
+
return obj;
|
533
|
+
}
|
534
|
+
};`;
|
535
|
+
}
|
536
|
+
const jsNamespaceName = fullName.replace(nameFilter, "");
|
537
|
+
binding += `
|
538
|
+
template<> struct js_bind<${fullName}> {
|
539
|
+
static void bind(qjs::Context::Module &mod) {
|
540
|
+
mod.class_<${fullName}>("${jsNamespaceName}")
|
541
|
+
.constructor<>()`;
|
542
|
+
if (bases.length > 0) {
|
543
|
+
binding += `
|
544
|
+
.base<${bases[0].type}>()`;
|
545
|
+
}
|
546
|
+
for (const method of methods) {
|
547
|
+
binding += `
|
548
|
+
.${method.static ? "static_" : ""}fun<&${fullName}::${method.name}>("${method.name}")`;
|
549
|
+
}
|
550
|
+
for (const field of fields) {
|
551
|
+
binding += `
|
552
|
+
.property<&${fullName}::${field.name}>("${field.name}")`;
|
553
|
+
}
|
554
|
+
binding += `
|
555
|
+
;
|
556
|
+
}
|
557
|
+
};
|
558
|
+
`;
|
559
|
+
const tsNamespaceParts = jsNamespaceName.split("::");
|
560
|
+
const tsClassName = tsNamespaceParts.pop() || structName;
|
561
|
+
const tsNamespace = tsNamespaceParts.join(".");
|
562
|
+
if (tsNamespace) typescriptDef += `
|
563
|
+
namespace ${tsNamespace} {`;
|
564
|
+
typescriptDef += `
|
565
|
+
export class ${tsClassName}${bases.length > 0 ? ` extends ${bases.map((base) => base.type.split("::").pop() ?? base.type).join(", ")}` : ""} {`;
|
566
|
+
fields.forEach((field) => {
|
567
|
+
let fieldDef = `${field.name}${field.type.startsWith("std::optional") ? "?" : ""}: ${cTypeToTypeScript(field.type, nameFilter)}`;
|
568
|
+
if (field.comment) {
|
569
|
+
fieldDef = `
|
570
|
+
/**
|
571
|
+
* ${field.comment.split("\n").join("\n * ")}
|
572
|
+
*/
|
573
|
+
${fieldDef}`;
|
574
|
+
}
|
575
|
+
typescriptDef += `
|
576
|
+
${fieldDef.trim()}`;
|
577
|
+
});
|
578
|
+
methods.forEach((method) => {
|
579
|
+
let methodDef = `${method.static ? "static " : ""}${method.name}(${method.argNames && method.argNames.length > 0 ? method.args.map((arg, i) => `${method.argNames[i] || `arg${i}`}: ${cTypeToTypeScript(arg, nameFilter)}`).join(", ") : ""}): ${cTypeToTypeScript(method.returnType, nameFilter)}`;
|
580
|
+
let comments = "";
|
581
|
+
if (method.comment) comments += method.comment;
|
582
|
+
if (comments || method.argNames && method.argNames.length > 0) {
|
583
|
+
methodDef = `
|
584
|
+
/**
|
585
|
+
* ${comments.split("\n").join("\n * ")}
|
586
|
+
${method.argNames && method.argNames.length > 0 ? `* @param ${method.args.map((arg, i) => `${method.argNames[i] || `arg${i}`}${cTypeToTypeScript(arg, nameFilter) === "any" ? "" : `: ${cTypeToTypeScript(arg, nameFilter)}`}`).join("\n * @param ")}
|
587
|
+
*` : ""} @returns ${cTypeToTypeScript(method.returnType, nameFilter)}
|
588
|
+
*/
|
589
|
+
${methodDef}`;
|
590
|
+
}
|
591
|
+
typescriptDef += `
|
592
|
+
${methodDef.trim()}`;
|
593
|
+
});
|
594
|
+
typescriptDef += `
|
595
|
+
}`;
|
596
|
+
if (tsNamespace) typescriptDef += `
|
597
|
+
}`;
|
598
|
+
};
|
599
|
+
const enumerateStructDecls = (node, callback, path = ["breeze"]) => {
|
600
|
+
if (node.kind === "CXXRecordDecl" && node.name && node.inner) {
|
601
|
+
callback(node, path);
|
602
|
+
}
|
603
|
+
if (node.inner) {
|
604
|
+
for (const child of node.inner) {
|
605
|
+
enumerateStructDecls(child, callback, [...path, node.name || ""]);
|
606
|
+
}
|
607
|
+
}
|
608
|
+
};
|
609
|
+
for (const ast of astArr) {
|
610
|
+
enumerateStructDecls(ast, (node, path) => {
|
611
|
+
if (node.name) structNames.push(path.join("::") + "::" + node.name);
|
612
|
+
});
|
613
|
+
}
|
614
|
+
for (const ast of astArr) {
|
615
|
+
enumerateStructDecls(ast, (node, path) => {
|
616
|
+
if (node.kind === "CXXRecordDecl" && node.name) generateForRecordDecl(node, path);
|
617
|
+
});
|
618
|
+
}
|
619
|
+
binding += `
|
620
|
+
inline void bindAll(qjs::Context::Module &mod) {
|
621
|
+
`;
|
622
|
+
for (const structName of structNames) {
|
623
|
+
binding += `
|
624
|
+
js_bind<${structName}>::bind(mod);
|
625
|
+
`;
|
626
|
+
}
|
627
|
+
binding += `
|
628
|
+
}
|
629
|
+
`;
|
630
|
+
typescriptDef += `
|
631
|
+
}
|
632
|
+
`;
|
633
|
+
const quickjsTypesContent = (0, import_node_fs.readFileSync)((0, import_node_path.join)(bindgenDir, quickjsTypesPath), "utf-8");
|
634
|
+
typescriptDef += "\n" + quickjsTypesContent;
|
635
|
+
return { cppBinding: binding, tsDefinitions: typescriptDef };
|
636
|
+
}
|
637
|
+
function generateBindingsAndDefinitions(config) {
|
638
|
+
const {
|
639
|
+
cppFilePath,
|
640
|
+
outputDir,
|
641
|
+
clangPath = "clang++",
|
642
|
+
cppBindingOutputFile = DEFAULT_CPP_BINDING_OUTPUT_FILE,
|
643
|
+
tsDefinitionOutputFile = DEFAULT_TS_DEFINITION_OUTPUT_FILE,
|
644
|
+
quickjsTypesPath = DEFAULT_QUICKJS_TYPES_PATH,
|
645
|
+
nameFilter = DEFAULT_NAME_FILTER
|
646
|
+
} = config;
|
647
|
+
const absoluteCppFilePath = (0, import_node_path.resolve)(cppFilePath);
|
648
|
+
const absoluteOutputDir = (0, import_node_path.resolve)(outputDir);
|
649
|
+
const currentFilePath = (0, import_node_url.fileURLToPath)(import_meta.url);
|
650
|
+
const bindgenDir = (0, import_node_path.dirname)(currentFilePath);
|
651
|
+
const clangArgs = [
|
652
|
+
"-Xclang",
|
653
|
+
"-ast-dump=json",
|
654
|
+
"-fsyntax-only",
|
655
|
+
"-Xclang",
|
656
|
+
`-ast-dump-filter=${nameFilter.slice(0, -2)}`,
|
657
|
+
// Use parameter
|
658
|
+
"-std=c++2c",
|
659
|
+
// or c++20, c++17 as appropriate
|
660
|
+
// Add any include paths if necessary, e.g. -I../../include
|
661
|
+
absoluteCppFilePath
|
662
|
+
];
|
663
|
+
(0, import_fancy_log.default)(`Executing: ${clangPath} ${clangArgs.join(" ")}`);
|
664
|
+
const clangProcess = (0, import_node_child_process.spawnSync)(clangPath, clangArgs, { encoding: "utf-8", shell: true });
|
665
|
+
if (clangProcess.error) {
|
666
|
+
import_fancy_log.default.error(`Failed to start clang++: ${clangProcess.error.message}`);
|
667
|
+
if (clangProcess.error.code === "ENOENT") {
|
668
|
+
throw new Error(`clang++ not found at path "${clangPath}". Please ensure it's installed and in your PATH, or provide a correct path.`);
|
669
|
+
}
|
670
|
+
throw clangProcess.error;
|
671
|
+
}
|
672
|
+
if (clangProcess.stderr && clangProcess.stderr.length > 0) {
|
673
|
+
import_fancy_log.default.warn(`clang++ stderr:
|
674
|
+
${clangProcess.stderr}`);
|
675
|
+
}
|
676
|
+
if (!clangProcess.stdout || clangProcess.stdout.trim().length === 0) {
|
677
|
+
throw new Error(`clang++ produced no output (stdout). AST generation failed. Error: ${clangProcess.stderr}`);
|
678
|
+
}
|
679
|
+
let astText = clangProcess.stdout;
|
680
|
+
if (!astText.trim().startsWith("[")) {
|
681
|
+
astText = "[" + astText.trim().replace(/}\s*{/g, "},{") + "]";
|
682
|
+
}
|
683
|
+
let astArr;
|
684
|
+
try {
|
685
|
+
astArr = JSON.parse(astText);
|
686
|
+
} catch (e) {
|
687
|
+
import_fancy_log.default.error("Failed to parse AST JSON:", e);
|
688
|
+
import_fancy_log.default.error("Problematic AST JSON content (first 1000 chars):", astText.substring(0, 1e3));
|
689
|
+
throw new Error(`Failed to parse AST JSON from clang++. Check for errors in C++ code or clang++ execution.`);
|
690
|
+
}
|
691
|
+
if (!astArr || astArr.length === 0) {
|
692
|
+
import_fancy_log.default.warn(`No AST data was parsed. This might mean the ast-dump-filter ("${nameFilter.slice(0, -2)}") didn't match anything, or the input file is empty or has no relevant declarations.`);
|
693
|
+
}
|
694
|
+
const { cppBinding, tsDefinitions } = processAstAndGenerateCode(astArr, absoluteCppFilePath, bindgenDir, nameFilter, quickjsTypesPath);
|
695
|
+
if (!(0, import_node_fs.existsSync)(absoluteOutputDir)) {
|
696
|
+
try {
|
697
|
+
(0, import_node_fs.mkdirSync)(absoluteOutputDir, { recursive: true });
|
698
|
+
} catch (e) {
|
699
|
+
throw new Error(`Failed to create output directory ${absoluteOutputDir}: ${e.message}`);
|
700
|
+
}
|
701
|
+
}
|
702
|
+
(0, import_node_fs.writeFileSync)((0, import_node_path.join)(absoluteOutputDir, cppBindingOutputFile), cppBinding);
|
703
|
+
(0, import_node_fs.writeFileSync)((0, import_node_path.join)(absoluteOutputDir, tsDefinitionOutputFile), tsDefinitions);
|
704
|
+
(0, import_fancy_log.default)(`Bindings generated successfully in ${absoluteOutputDir}`);
|
705
|
+
}
|
706
|
+
|
707
|
+
// index.ts
|
708
|
+
var import_node_path2 = require("node:path");
|
709
|
+
var import_node_url2 = require("node:url");
|
710
|
+
var import_meta2 = {};
|
711
|
+
async function runDefaultGeneration() {
|
712
|
+
try {
|
713
|
+
const currentFilePath = (0, import_node_url2.fileURLToPath)(import_meta2.url);
|
714
|
+
const bindgenDir = (0, import_node_path2.dirname)(currentFilePath);
|
715
|
+
const defaultInputFile = (0, import_node_path2.join)(bindgenDir, "../src/breeze-js/binding_types.h");
|
716
|
+
const defaultOutputDir = (0, import_node_path2.join)(bindgenDir, "../src/breeze-js/");
|
717
|
+
(0, import_fancy_log2.default)(`Running default binding generation...`);
|
718
|
+
(0, import_fancy_log2.default)(`Input C++ file: ${defaultInputFile}`);
|
719
|
+
(0, import_fancy_log2.default)(`Output directory: ${defaultOutputDir}`);
|
720
|
+
generateBindingsAndDefinitions({
|
721
|
+
cppFilePath: defaultInputFile,
|
722
|
+
outputDir: defaultOutputDir
|
723
|
+
});
|
724
|
+
(0, import_fancy_log2.default)("Default binding generation finished successfully.");
|
725
|
+
} catch (error) {
|
726
|
+
import_fancy_log2.default.error("Error during default binding generation:", error.message);
|
727
|
+
if (error.stack && process.env.DEBUG) {
|
728
|
+
import_fancy_log2.default.error(error.stack);
|
729
|
+
}
|
730
|
+
process.exit(1);
|
731
|
+
}
|
732
|
+
}
|
733
|
+
runDefaultGeneration();
|