java-bridge 2.1.1 → 2.1.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.
package/README.md CHANGED
@@ -249,3 +249,40 @@ Options:
249
249
  - The classpath argument can be supplied multiple times to add multiple jars to the classpath
250
250
  - Multiple class names can be supplied to generate definitions for multiple classes
251
251
  - The generated typescript files will automatically import all classes once the module is loaded.
252
+
253
+ ### Examples
254
+
255
+ #### Generate definitions for a single class
256
+
257
+ Generate definitions for the `java.lang.String` class and all its referenced classes and save them to `./project`:
258
+
259
+ ```bash
260
+ java-ts-gen ./project java.lang.String
261
+ ```
262
+
263
+ This will create a directory called `java` containing the definitions for the `java.lang.String` class and all its
264
+ dependencies all inside subdirectories. The `java.lang.String` class will be saved to `./project/java/lang/String.ts`.
265
+ Thus, the folder structure of `project` will look something like this:
266
+
267
+ ```
268
+ .
269
+ ├── ...
270
+ ├── java
271
+ │ ├── lang
272
+ │ │ ├── String.ts
273
+ │ │ ├── Object.ts
274
+ │ │ └── ...
275
+ │ ├── util
276
+ │ │ └── ...
277
+ │ └── ...
278
+ └── ...
279
+ ```
280
+
281
+ #### Generate definitions for multiple classes
282
+
283
+ Generate definitions for the `java.lang.String` and `java.util.ArrayList` classes and all of their dependencies
284
+ and save them to `./project`:
285
+
286
+ ```bash
287
+ java-ts-gen ./project java.lang.String java.util.ArrayList
288
+ ```
@@ -285,6 +285,372 @@ module.exports.StdoutRedirect = StdoutRedirect
285
285
  module.exports.getJavaLibPath = getJavaLibPath
286
286
 
287
287
 
288
+ /***/ }),
289
+
290
+ /***/ "./ts-src/TypescriptDefinitionGenerator.ts":
291
+ /*!*************************************************!*\
292
+ !*** ./ts-src/TypescriptDefinitionGenerator.ts ***!
293
+ \*************************************************/
294
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
295
+
296
+ "use strict";
297
+
298
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
299
+ if (k2 === undefined) k2 = k;
300
+ var desc = Object.getOwnPropertyDescriptor(m, k);
301
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
302
+ desc = { enumerable: true, get: function() { return m[k]; } };
303
+ }
304
+ Object.defineProperty(o, k2, desc);
305
+ }) : (function(o, m, k, k2) {
306
+ if (k2 === undefined) k2 = k;
307
+ o[k2] = m[k];
308
+ }));
309
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
310
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
311
+ }) : function(o, v) {
312
+ o["default"] = v;
313
+ });
314
+ var __importStar = (this && this.__importStar) || function (mod) {
315
+ if (mod && mod.__esModule) return mod;
316
+ var result = {};
317
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
318
+ __setModuleDefault(result, mod);
319
+ return result;
320
+ };
321
+ var __importDefault = (this && this.__importDefault) || function (mod) {
322
+ return (mod && mod.__esModule) ? mod : { "default": mod };
323
+ };
324
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
325
+ const typescript_1 = __importStar(__webpack_require__(/*! typescript */ "typescript"));
326
+ const _1 = __webpack_require__(/*! ./. */ "./ts-src/index.ts");
327
+ const fs_1 = __importDefault(__webpack_require__(/*! fs */ "fs"));
328
+ const path_1 = __importDefault(__webpack_require__(/*! path */ "path"));
329
+ const sourceFile = typescript_1.default.createSourceFile('source.ts', '', typescript_1.default.ScriptTarget.Latest, false, typescript_1.default.ScriptKind.TS);
330
+ /**
331
+ * A class to generate typescript definitions for java classes.
332
+ * Converts the given class and all of its dependencies to typescript.
333
+ *
334
+ * ## Example
335
+ * ```ts
336
+ * import { TypescriptDefinitionGenerator } from 'java-bridge';
337
+ *
338
+ * const generator = new TypescriptDefinitionGenerator('java.lang.String');
339
+ * // Generate the typescript definitions
340
+ * const definitions = await generator.generate();
341
+ *
342
+ * // Save the definitions to a directory
343
+ * await TypescriptDefinitionGenerator.save(definitions, './project');
344
+ * ```
345
+ */
346
+ class TypescriptDefinitionGenerator {
347
+ /**
348
+ * Create a new `TypescriptDefinitionGenerator` instance
349
+ *
350
+ * @param classname the fully-qualified name of the class to generate a typescript definition for
351
+ * @param progressCallback a callback method to be called every time a java class is
352
+ * converted to typescript
353
+ * @param resolvedImports a list of imports that have already been resolved.
354
+ * This is used to prevent converting a class multiple times
355
+ */
356
+ constructor(classname, progressCallback = null, resolvedImports = []) {
357
+ this.classname = classname;
358
+ this.progressCallback = progressCallback;
359
+ this.resolvedImports = resolvedImports;
360
+ this.usesBasicOrJavaType = false;
361
+ this.additionalImports = [];
362
+ this.importsToResolve = [];
363
+ }
364
+ static async convertMethods(methods) {
365
+ const Modifier = await (0, _1.importClassAsync)('java.lang.reflect.Modifier');
366
+ const result = {};
367
+ for (const method of methods) {
368
+ const modifiers = await method.getModifiers();
369
+ if (await Modifier.isPublic(modifiers)) {
370
+ const name = await method.getName();
371
+ const returnType = await method.getReturnType();
372
+ const parameterTypes = await method.getParameterTypes();
373
+ const data = {
374
+ returnType: await returnType.getTypeName(),
375
+ parameters: await Promise.all(parameterTypes.map((p) => p.getTypeName())),
376
+ isStatic: await Modifier.isStatic(modifiers),
377
+ };
378
+ if (Object.hasOwn(result, name)) {
379
+ result[name].push(data);
380
+ }
381
+ else {
382
+ result[name] = [data];
383
+ }
384
+ }
385
+ }
386
+ return result;
387
+ }
388
+ async convertConstructors(constructors) {
389
+ const Modifier = await (0, _1.importClassAsync)('java.lang.reflect.Modifier');
390
+ const types = [];
391
+ for (const constructor of constructors) {
392
+ const modifiers = await constructor.getModifiers();
393
+ if (await Modifier.isPublic(modifiers)) {
394
+ const parameterTypes = await constructor.getParameterTypes();
395
+ types.push(await Promise.all(parameterTypes.map((p) => p.getTypeName())));
396
+ }
397
+ }
398
+ const tsConstructors = types.map((t, i) => {
399
+ const params = t.map(this.convertParameter.bind(this));
400
+ let declaration = typescript_1.default.factory.createConstructorDeclaration([typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.PublicKeyword)], params, undefined);
401
+ if (i === 0) {
402
+ declaration = typescript_1.default.addSyntheticLeadingComment(declaration, typescript_1.default.SyntaxKind.SingleLineCommentTrivia, ` ================== Constructors ==================`, true);
403
+ }
404
+ if (t.length > 0) {
405
+ declaration = typescript_1.default.addSyntheticLeadingComment(declaration, typescript_1.default.SyntaxKind.MultiLineCommentTrivia, '*\n' +
406
+ t
407
+ .map((p, i) => ` * @param var${i} original type: '${p}'\n`)
408
+ .join('') +
409
+ ' ', true);
410
+ }
411
+ return declaration;
412
+ });
413
+ const newInstanceMethods = types.map((t, i) => {
414
+ return this.createMethod({
415
+ returnType: this.classname,
416
+ parameters: t,
417
+ isStatic: true,
418
+ }, 'newInstance', i, false);
419
+ });
420
+ return [...newInstanceMethods, ...tsConstructors];
421
+ }
422
+ javaTypeToTypescriptType(javaType, isParam) {
423
+ switch (javaType) {
424
+ case 'byte[]':
425
+ case 'java.lang.Byte[]':
426
+ return typescript_1.default.factory.createTypeReferenceNode('Buffer');
427
+ }
428
+ if (javaType.endsWith('[]')) {
429
+ return typescript_1.default.factory.createArrayTypeNode(this.javaTypeToTypescriptType(javaType.substring(0, javaType.length - 2), isParam));
430
+ }
431
+ switch (javaType) {
432
+ case 'int':
433
+ case 'java.lang.Integer':
434
+ case 'long':
435
+ case 'java.lang.Long':
436
+ case 'float':
437
+ case 'java.lang.Float':
438
+ case 'double':
439
+ case 'java.lang.Double':
440
+ case 'byte':
441
+ case 'java.lang.Byte':
442
+ case 'short':
443
+ case 'java.lang.Short':
444
+ return typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword);
445
+ case 'char':
446
+ case 'java.lang.Character':
447
+ case 'java.lang.String':
448
+ return typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.StringKeyword);
449
+ case 'boolean':
450
+ case 'java.lang.Boolean':
451
+ return typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword);
452
+ case 'void':
453
+ case 'java.lang.Void':
454
+ return typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.VoidKeyword);
455
+ case 'java.lang.Object':
456
+ this.usesBasicOrJavaType = true;
457
+ return typescript_1.default.factory.createTypeReferenceNode('BasicOrJavaType');
458
+ default:
459
+ if (!this.resolvedImports.includes(javaType)) {
460
+ this.additionalImports.push(javaType);
461
+ }
462
+ this.importsToResolve.push(javaType);
463
+ const isSelf = javaType === this.classname && isParam;
464
+ return typescript_1.default.factory.createTypeReferenceNode(javaType === this.classname
465
+ ? javaType.substring(javaType.lastIndexOf('.') + 1) +
466
+ (isSelf ? 'Class' : '')
467
+ : javaType.replaceAll('.', '_'));
468
+ }
469
+ }
470
+ convertParameter(param, index) {
471
+ const name = 'var' + index;
472
+ const type = this.javaTypeToTypescriptType(param, true);
473
+ return typescript_1.default.factory.createParameterDeclaration(undefined, undefined, name, undefined, type);
474
+ }
475
+ convertParameters(params) {
476
+ return params.parameters.map(this.convertParameter.bind(this));
477
+ }
478
+ static createMethodComment(declaration) {
479
+ return ('*\n' +
480
+ declaration.parameters
481
+ .map((p, i) => ` * @param var${i} original type: '${p}'\n`)
482
+ .join('') +
483
+ ` * @return original return type: '${declaration.returnType}'\n `);
484
+ }
485
+ createMethod(m, name, i, isSync) {
486
+ const publicMod = typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.PublicKeyword);
487
+ const staticMod = typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.StaticKeyword);
488
+ const modifiers = [publicMod];
489
+ if (m.isStatic) {
490
+ modifiers.push(staticMod);
491
+ }
492
+ let returnType = this.javaTypeToTypescriptType(m.returnType, false);
493
+ if (!isSync) {
494
+ returnType = typescript_1.default.factory.createTypeReferenceNode(typescript_1.default.factory.createIdentifier('Promise'), [returnType]);
495
+ }
496
+ let declaration = typescript_1.default.factory.createMethodDeclaration(modifiers, undefined, name + (isSync ? 'Sync' : ''), undefined, undefined, this.convertParameters(m), returnType, undefined);
497
+ if (i === 0) {
498
+ declaration = typescript_1.default.addSyntheticLeadingComment(declaration, typescript_1.default.SyntaxKind.SingleLineCommentTrivia, ` ================== Method ${name} ==================`, true);
499
+ }
500
+ return typescript_1.default.addSyntheticLeadingComment(declaration, typescript_1.default.SyntaxKind.MultiLineCommentTrivia, TypescriptDefinitionGenerator.createMethodComment(m), true);
501
+ }
502
+ convertMethod(method, name) {
503
+ const res = [];
504
+ for (let i = 0; i < method.length; i++) {
505
+ const m = method[i];
506
+ res.push(this.createMethod(m, name, i, false), this.createMethod(m, name, i, true));
507
+ }
508
+ return res;
509
+ }
510
+ getAdditionalImports() {
511
+ const getPath = (i) => {
512
+ const thisSplit = this.classname.split('.');
513
+ const importSplit = i.split('.');
514
+ for (let j = 0; j < thisSplit.length; j++) {
515
+ if (importSplit[j] === thisSplit[j]) {
516
+ thisSplit[j] = null;
517
+ importSplit[j] = null;
518
+ }
519
+ else {
520
+ break;
521
+ }
522
+ }
523
+ return ('./' +
524
+ thisSplit
525
+ .filter((e) => !!e)
526
+ .map(() => '')
527
+ .join('../') +
528
+ importSplit.filter((e) => !!e).join('/'));
529
+ };
530
+ const unique = (value, index, self) => {
531
+ return self.indexOf(value) === index;
532
+ };
533
+ return this.importsToResolve
534
+ .filter((i) => i != this.classname)
535
+ .filter(unique)
536
+ .map((i) => typescript_1.default.factory.createImportDeclaration(undefined, typescript_1.default.factory.createImportClause(false, undefined, typescript_1.default.factory.createNamedImports([
537
+ typescript_1.default.factory.createImportSpecifier(false, typescript_1.default.factory.createIdentifier(i.substring(i.lastIndexOf('.') + 1)), typescript_1.default.factory.createIdentifier(i.replaceAll('.', '_'))),
538
+ ])), typescript_1.default.factory.createStringLiteral(getPath(i))));
539
+ }
540
+ getImports() {
541
+ const importElements = [
542
+ typescript_1.default.factory.createImportSpecifier(false, undefined, typescript_1.default.factory.createIdentifier('importClass')),
543
+ typescript_1.default.factory.createImportSpecifier(false, undefined, typescript_1.default.factory.createIdentifier('JavaClass')),
544
+ ];
545
+ if (this.usesBasicOrJavaType) {
546
+ importElements.push(typescript_1.default.factory.createImportSpecifier(false, undefined, typescript_1.default.factory.createIdentifier('BasicOrJavaType')));
547
+ }
548
+ const imports = typescript_1.default.factory.createNamedImports(importElements);
549
+ return typescript_1.default.factory.createImportDeclaration(undefined, typescript_1.default.factory.createImportClause(false, undefined, imports), typescript_1.default.factory.createStringLiteral('java-bridge'));
550
+ }
551
+ getExportStatement(simpleName) {
552
+ const statement = typescript_1.default.factory.createClassDeclaration([typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword)], simpleName, undefined, [
553
+ typescript_1.default.factory.createHeritageClause(typescript_1.default.SyntaxKind.ExtendsKeyword, [
554
+ typescript_1.default.factory.createExpressionWithTypeArguments(typescript_1.default.factory.createIdentifier(`importClass<typeof ${simpleName}Class>("${this.classname}")`), undefined),
555
+ ]),
556
+ ], []);
557
+ return [
558
+ typescript_1.default.addSyntheticLeadingComment(statement, typescript_1.SyntaxKind.MultiLineCommentTrivia, `*\n * Class ${this.classname}.\n *\n` +
559
+ ' * This actually imports the java class for further use.\n' +
560
+ ` * The class ${simpleName}Class only defines types, this is the class you should actually import.\n` +
561
+ ' * Please note that this statement imports the underlying java class at runtime, which may take a while.\n' +
562
+ ' * This was generated by java-bridge.\n * You should probably not edit this.\n ', true),
563
+ typescript_1.default.factory.createExportDefault(typescript_1.default.factory.createIdentifier(simpleName)),
564
+ ];
565
+ }
566
+ getText(nodes) {
567
+ return nodes
568
+ .map((n) => (n &&
569
+ typescript_1.default
570
+ .createPrinter({ newLine: typescript_1.default.NewLineKind.LineFeed })
571
+ .printNode(typescript_1.default.EmitHint.Unspecified, n, sourceFile)) ||
572
+ '')
573
+ .join('\n');
574
+ }
575
+ /**
576
+ * Generates the typescript definition for the given class.
577
+ *
578
+ * @returns the generated typescript definitions
579
+ */
580
+ async generate() {
581
+ if (this.resolvedImports.includes(this.classname)) {
582
+ return [];
583
+ }
584
+ this.resolvedImports.push(this.classname);
585
+ if (this.progressCallback) {
586
+ this.progressCallback(this.classname);
587
+ }
588
+ const Class = await (0, _1.importClassAsync)(this.classname);
589
+ const cls = Class.class;
590
+ const simpleName = this.classname.substring(this.classname.lastIndexOf('.') + 1);
591
+ const methods = await cls.getDeclaredMethods();
592
+ const classMembers = [];
593
+ const convertedMethods = await TypescriptDefinitionGenerator.convertMethods(methods);
594
+ for (const key of Object.keys(convertedMethods)) {
595
+ const m = convertedMethods[key];
596
+ classMembers.push(...this.convertMethod(m, key));
597
+ }
598
+ const constructors = await cls.getDeclaredConstructors();
599
+ const convertedConstructors = await this.convertConstructors(constructors);
600
+ classMembers.push(...convertedConstructors);
601
+ let tsClass = typescript_1.default.factory.createClassDeclaration([
602
+ typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword),
603
+ typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.DeclareKeyword),
604
+ ], simpleName + 'Class', undefined, [
605
+ typescript_1.default.factory.createHeritageClause(typescript_1.default.SyntaxKind.ExtendsKeyword, [
606
+ typescript_1.default.factory.createExpressionWithTypeArguments(typescript_1.default.factory.createIdentifier('JavaClass'), undefined),
607
+ ]),
608
+ ], classMembers);
609
+ tsClass = typescript_1.default.addSyntheticLeadingComment(tsClass, typescript_1.default.SyntaxKind.MultiLineCommentTrivia, `*\n * This class just defines types, you should import ${simpleName} instead of this.\n` +
610
+ ' * This was generated by java-bridge.\n * You should probably not edit this.\n ', true);
611
+ const sourceText = this.getText([
612
+ this.getImports(),
613
+ ...this.getAdditionalImports(),
614
+ null,
615
+ tsClass,
616
+ null,
617
+ ...this.getExportStatement(simpleName),
618
+ ]);
619
+ const res = [];
620
+ for (const imported of this.additionalImports) {
621
+ const generator = new TypescriptDefinitionGenerator(imported, this.progressCallback, this.resolvedImports);
622
+ const generated = await generator.generate();
623
+ res.push(...generated);
624
+ }
625
+ res.push({
626
+ name: this.classname,
627
+ contents: sourceText,
628
+ });
629
+ return res;
630
+ }
631
+ /**
632
+ * Save the converted classes to the given directory.
633
+ *
634
+ * @param declarations the declarations to save
635
+ * @param sourceDir the directory to save the files to
636
+ */
637
+ static async save(declarations, sourceDir) {
638
+ for (const declaration of declarations) {
639
+ const p = declaration.name.split('.');
640
+ p[p.length - 1] = p[p.length - 1] + '.ts';
641
+ const filePath = path_1.default.join(sourceDir, ...p);
642
+ await fs_1.default.promises.mkdir(path_1.default.dirname(filePath), {
643
+ recursive: true,
644
+ });
645
+ await fs_1.default.promises.writeFile(filePath, declaration.contents, {
646
+ encoding: 'utf8',
647
+ });
648
+ }
649
+ }
650
+ }
651
+ exports["default"] = TypescriptDefinitionGenerator;
652
+
653
+
288
654
  /***/ }),
289
655
 
290
656
  /***/ "./ts-src/definitions.ts":
@@ -365,18 +731,25 @@ var __importStar = (this && this.__importStar) || function (mod) {
365
731
  __setModuleDefault(result, mod);
366
732
  return result;
367
733
  };
734
+ var __importDefault = (this && this.__importDefault) || function (mod) {
735
+ return (mod && mod.__esModule) ? mod : { "default": mod };
736
+ };
368
737
  Object.defineProperty(exports, "__esModule", ({ value: true }));
369
- exports.getJavaLibPath = exports.JavaClassProxy = exports.JavaClassInstance = exports.JavaObject = exports.JavaVersion = void 0;
738
+ exports.TypescriptDefinitionGenerator = exports.getJavaLibPath = exports.JavaClassConstructor = exports.JavaClass = exports.JavaClassProxy = exports.JavaClassInstance = exports.JavaObject = exports.JavaVersion = void 0;
370
739
  var definitions_1 = __webpack_require__(/*! ./definitions */ "./ts-src/definitions.ts");
371
740
  Object.defineProperty(exports, "JavaVersion", ({ enumerable: true, get: function () { return definitions_1.JavaVersion; } }));
372
741
  Object.defineProperty(exports, "JavaObject", ({ enumerable: true, get: function () { return definitions_1.JavaObject; } }));
373
742
  Object.defineProperty(exports, "JavaClassInstance", ({ enumerable: true, get: function () { return definitions_1.JavaClassInstance; } }));
374
743
  Object.defineProperty(exports, "JavaClassProxy", ({ enumerable: true, get: function () { return definitions_1.JavaClassProxy; } }));
744
+ Object.defineProperty(exports, "JavaClass", ({ enumerable: true, get: function () { return definitions_1.JavaClass; } }));
745
+ Object.defineProperty(exports, "JavaClassConstructor", ({ enumerable: true, get: function () { return definitions_1.JavaClassConstructor; } }));
375
746
  __exportStar(__webpack_require__(/*! ./java */ "./ts-src/java.ts"), exports);
376
747
  const java = __importStar(__webpack_require__(/*! ./java */ "./ts-src/java.ts"));
377
748
  exports["default"] = java;
378
749
  var native_1 = __webpack_require__(/*! ../native */ "./native.js");
379
750
  Object.defineProperty(exports, "getJavaLibPath", ({ enumerable: true, get: function () { return native_1.getJavaLibPath; } }));
751
+ const TypescriptDefinitionGenerator_1 = __importDefault(__webpack_require__(/*! ./TypescriptDefinitionGenerator */ "./ts-src/TypescriptDefinitionGenerator.ts"));
752
+ exports.TypescriptDefinitionGenerator = TypescriptDefinitionGenerator_1.default;
380
753
 
381
754
 
382
755
  /***/ }),
@@ -587,7 +960,7 @@ exports.appendClasspath = appendClasspath;
587
960
  /**
588
961
  * Check if `this_obj` is instance of `other`.
589
962
  * This uses the native java `instanceof` operator.
590
- * You may want to use this if {@link JavaClassInstance.instanceOf}
963
+ * You may want to use this if {@link JavaClass.instanceOf}
591
964
  * is overridden, as that method itself does not override
592
965
  * any method defined in the specific java class named 'instanceOf'.
593
966
  *
@@ -1018,6 +1391,17 @@ module.exports = require("glob");
1018
1391
 
1019
1392
  /***/ }),
1020
1393
 
1394
+ /***/ "typescript":
1395
+ /*!*****************************!*\
1396
+ !*** external "typescript" ***!
1397
+ \*****************************/
1398
+ /***/ ((module) => {
1399
+
1400
+ "use strict";
1401
+ module.exports = require("typescript");
1402
+
1403
+ /***/ }),
1404
+
1021
1405
  /***/ "fs":
1022
1406
  /*!*********************!*\
1023
1407
  !*** external "fs" ***!