From 8c58236723a6929f302da71f907d7517cbc65363 Mon Sep 17 00:00:00 2001 From: Joe Ferner Date: Sun, 25 May 2025 20:35:29 +0000 Subject: [PATCH] switch tests to using typescript and add type definitions --- README.md | 100 ++-- eslint.config.mjs | 46 +- java.d.ts | 354 ++++++++++++ package-lock.json | 544 +++++++++++++++++- package.json | 16 +- scripts/testRunner.js | 41 -- scripts/testRunner.ts | 55 ++ src-node/nodeJavaBridge.js | 83 ++- ...{TestLambda.test.js => TestLambda.test.ts} | 14 +- test/{awt.test.js => awt.test.ts} | 11 +- ...amicProxy.test.js => dynamicProxy.test.ts} | 44 +- ...mportClass.test.js => importClass.test.ts} | 15 +- ...{instanceof.test.js => instanceof.test.ts} | 11 +- ...d.test.js => java-ambiguousMethod.test.ts} | 80 ++- ....test.js => java-callStaticMethod.test.ts} | 154 +++-- ...tance.test.js => java-newInstance.test.ts} | 37 +- ...Field.test.js => java-staticField.test.ts} | 11 +- ...{javaObject.test.js => javaObject.test.ts} | 9 +- test/{promises.test.js => promises.test.ts} | 22 +- test/{simple.test.js => simple.test.ts} | 60 +- ...tils-types.test.js => utils-types.test.ts} | 11 +- test/{varargs.test.js => varargs.test.ts} | 11 +- ...eSuffix.test.js => allThreeSuffix.test.ts} | 32 +- ...test.js => asyncSuffixSyncDefault.test.ts} | 68 ++- testAsyncOptions/clientBeforeError.test.js | 29 - testAsyncOptions/clientBeforeError.test.ts | 35 ++ .../clientBeforeSyncThrows.test.js | 29 - .../clientBeforeSyncThrows.test.ts | 33 ++ testAsyncOptions/clientBeforeThrows.test.js | 29 - testAsyncOptions/clientBeforeThrows.test.ts | 33 ++ testAsyncOptions/clientPBeforeError.test.js | 40 -- testAsyncOptions/clientPBeforeError.test.ts | 43 ++ testAsyncOptions/clientPBeforeThrows.test.js | 40 -- testAsyncOptions/clientPBeforeThrows.test.ts | 44 ++ .../{defacto.test.js => defacto.test.ts} | 76 +-- ...ise.test.js => defactoPlusPromise.test.ts} | 69 +-- .../{default.test.js => default.test.ts} | 23 +- testAsyncOptions/invalidLaunch.test.js | 55 -- testAsyncOptions/invalidLaunch.test.ts | 67 +++ .../{noAsync.test.js => noAsync.test.ts} | 71 +-- ...test.js => syncDefaultPlusPromise.test.ts} | 24 +- ...ame.test.js => unusableMethodName.test.ts} | 61 +- testHelpers.js | 20 - testHelpers.ts | 46 ++ tsconfig.json | 113 ++++ 45 files changed, 2068 insertions(+), 741 deletions(-) create mode 100644 java.d.ts delete mode 100644 scripts/testRunner.js create mode 100644 scripts/testRunner.ts rename test/{TestLambda.test.js => TestLambda.test.ts} (73%) rename test/{awt.test.js => awt.test.ts} (78%) rename test/{dynamicProxy.test.js => dynamicProxy.test.ts} (87%) rename test/{importClass.test.js => importClass.test.ts} (85%) rename test/{instanceof.test.js => instanceof.test.ts} (79%) rename test/{java-ambiguousMethod.test.js => java-ambiguousMethod.test.ts} (60%) rename test/{java-callStaticMethod.test.js => java-callStaticMethod.test.ts} (65%) rename test/{java-newInstance.test.js => java-newInstance.test.ts} (72%) rename test/{java-staticField.test.js => java-staticField.test.ts} (85%) rename test/{javaObject.test.js => javaObject.test.ts} (70%) rename test/{promises.test.js => promises.test.ts} (75%) rename test/{simple.test.js => simple.test.ts} (85%) rename test/{utils-types.test.js => utils-types.test.ts} (93%) rename test/{varargs.test.js => varargs.test.ts} (95%) rename testAsyncOptions/{allThreeSuffix.test.js => allThreeSuffix.test.ts} (82%) rename testAsyncOptions/{asyncSuffixSyncDefault.test.js => asyncSuffixSyncDefault.test.ts} (76%) delete mode 100644 testAsyncOptions/clientBeforeError.test.js create mode 100644 testAsyncOptions/clientBeforeError.test.ts delete mode 100644 testAsyncOptions/clientBeforeSyncThrows.test.js create mode 100644 testAsyncOptions/clientBeforeSyncThrows.test.ts delete mode 100644 testAsyncOptions/clientBeforeThrows.test.js create mode 100644 testAsyncOptions/clientBeforeThrows.test.ts delete mode 100644 testAsyncOptions/clientPBeforeError.test.js create mode 100644 testAsyncOptions/clientPBeforeError.test.ts delete mode 100644 testAsyncOptions/clientPBeforeThrows.test.js create mode 100644 testAsyncOptions/clientPBeforeThrows.test.ts rename testAsyncOptions/{defacto.test.js => defacto.test.ts} (69%) rename testAsyncOptions/{defactoPlusPromise.test.js => defactoPlusPromise.test.ts} (72%) rename testAsyncOptions/{default.test.js => default.test.ts} (84%) delete mode 100644 testAsyncOptions/invalidLaunch.test.js create mode 100644 testAsyncOptions/invalidLaunch.test.ts rename testAsyncOptions/{noAsync.test.js => noAsync.test.ts} (72%) rename testAsyncOptions/{syncDefaultPlusPromise.test.js => syncDefaultPlusPromise.test.ts} (86%) rename testAsyncOptions/{unusableMethodName.test.js => unusableMethodName.test.ts} (71%) delete mode 100644 testHelpers.js create mode 100644 testHelpers.ts create mode 100644 tsconfig.json diff --git a/README.md b/README.md index 6dde09c..87ce6af 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Bridge API to connect with existing Java APIs. ### Other projects that might be helpful * [node-java-maven](https://github.com/joeferner/node-java-maven) - manages your node-java classpath by using maven dependency management. +* [ts-java](https://github.com/RedSeal-co/ts-java) - Create TypeScript declaration files for Java packages. ## Installation @@ -92,8 +93,8 @@ npm install --unsafe-perm java Then create a file called `test.js` with the following contents ``` -var java = require('java'); -var javaLangSystem = java.import('java.lang.System'); +const java = require('java'); +const javaLangSystem = java.import('java.lang.System'); javaLangSystem.out.printlnSync('Hello World'); ``` @@ -130,10 +131,10 @@ Then create the following module javaInit: ```javascript "use strict"; -var fs = require("fs"); -var java = require("java"); -var baseDir = "./target/dependency"; -var dependencies = fs.readdirSync(baseDir); +const fs = require("fs"); +const java = require("java"); +const baseDir = "./target/dependency"; +const dependencies = fs.readdirSync(baseDir); dependencies.forEach(function(dependency){ java.classpath.push(baseDir + "/" + dependency); @@ -151,8 +152,8 @@ and then in the consuming class write: ```javascript -var javaInit = require('./javaInit'); -var java = javaInit.getJavaInstance(); +const javaInit = require('./javaInit'); +const java = javaInit.getJavaInstance(); //your code goes here ``` @@ -162,11 +163,11 @@ var java = javaInit.getJavaInstance(); ## Quick Examples ```javascript -var java = require("java"); +const java = require("java"); java.classpath.push("commons-lang3-3.1.jar"); java.classpath.push("commons-io.jar"); -var list1 = java.newInstanceSync("java.util.ArrayList"); +const list1 = java.newInstanceSync("java.util.ArrayList"); console.log(list1.sizeSync()); // 0 list1.addSync('item1'); console.log(list1.sizeSync()); // 1 @@ -177,8 +178,8 @@ java.newInstance("java.util.ArrayList", function(err, list2) { console.log(list2.toStringSync()); // [item1, item2] }); -var ArrayList = java.import('java.util.ArrayList'); -var list3 = new ArrayList(); +const ArrayList = java.import('java.util.ArrayList'); +const list3 = new ArrayList(); list3.addSync('item1'); list3.equalsSync(list1); // true ``` @@ -186,13 +187,13 @@ list3.equalsSync(list1); // true ### Create a char array ```javascript -var charArray = java.newArray("char", "hello world\n".split('')); +const charArray = java.newArray("char", "hello world\n".split('')); ``` ### Create a byte array ```javascript -var byteArray = java.newArray( +const byteArray = java.newArray( "byte", "hello world\n" .split('') @@ -207,7 +208,7 @@ a property off of the result called "longValue" which contains the un-truncated If you are calling a method that takes a long you must create it using [java.newInstance](#javaNewInstance). ```javascript -var javaLong = java.newInstanceSync("java.lang.Long", 5); +const javaLong = java.newInstanceSync("java.lang.Long", 5); console.log('Possibly truncated long value: ' + javaLong); console.log('Original long value (as a string): ' + javaLong.longValue); java.callStaticMethodSync("Test", "staticMethodThatTakesALong", javaLong); @@ -236,7 +237,7 @@ As of release 0.4.5 it became possible to create async methods that return promi Example: ```javascript -var java = require("java"); +const java = require("java"); java.asyncOptions = { asyncSuffix: undefined, // Don't generate node-style methods taking callbacks syncSuffix: "", // Sync methods use the base name(!!) @@ -401,13 +402,13 @@ __Arguments__ __Example__ - var Test = java.import('Test'); + const Test = java.import('Test'); Test.someStaticMethodSync(5); console.log(Test.someStaticField); - var value1 = Test.NestedEnum.Value1; + const value1 = Test.NestedEnum.Value1; - var test = new Test(); + const test = new Test(); list.instanceMethodSync('item1'); ## newInstance @@ -428,7 +429,7 @@ __Arguments__ __Example__ - var list = java.newInstanceSync("java.util.ArrayList"); + const list = java.newInstanceSync("java.util.ArrayList"); java.newInstance("java.util.ArrayList", function(err, list) { if(err) { console.error(err); return; } @@ -450,7 +451,7 @@ __Arguments__ __Example__ - var obj = java.newInstanceSync("my.package.SubClass"); + const obj = java.newInstanceSync("my.package.SubClass"); if(java.instanceOf(obj, "my.package.SuperClass")) { console.log("obj is an instance of SuperClass"); @@ -464,8 +465,7 @@ __Example__ **java.callStaticMethodSync(className, methodName, [args...]) : result** -Calls a static method on the specified class. If you are using the sync method an exception will be throw if an error occurs, -otherwise it will be the first argument in the callback. +Calls a static method on the specified class. If you are using the sync method an exception will be throw if an error occurs, otherwise it will be the first argument in the callback. __Arguments__ @@ -475,7 +475,7 @@ __Arguments__ __Example__ - var result = java.callStaticMethodSync("com.nearinfinty.MyClass", "doSomething", 42, "test"); + const result = java.callStaticMethodSync("com.nearinfinty.MyClass", "doSomething", 42, "test"); java.callStaticMethod("com.nearinfinty.MyClass", "doSomething", 42, "test", function(err, results) { if(err) { console.error(err); return; } @@ -501,11 +501,11 @@ __Arguments__ __Example__ - var instance = java.newInstanceSync("com.nearinfinty.MyClass"); + const instance = java.newInstanceSync("com.nearinfinty.MyClass"); - var result = java.callMethodSync("com.nearinfinty.MyClass", "doSomething", 42, "test"); + const result = java.callMethodSync("com.nearinfinty.MyClass", "doSomething", 42, "test"); - java.callMethodSync(instance, "doSomething", 42, "test", function(err, results) { + java.callMethod(instance, "doSomething", 42, "test", function(err, results) { if(err) { console.error(err); return; } // results from doSomething }); @@ -525,7 +525,7 @@ __Arguments__ __Example__ - var data = java.getStaticFieldValue("com.nearinfinty.MyClass", "data"); + const data = java.getStaticFieldValue("com.nearinfinty.MyClass", "data"); ## setStaticFieldValue @@ -560,7 +560,7 @@ __Arguments__ __Example__ - var newArray = java.newArray("java.lang.String", ["item1", "item2", "item3"]); + const newArray = java.newArray("java.lang.String", ["item1", "item2", "item3"]); ## newByte @@ -576,7 +576,7 @@ __Arguments__ __Example__ - var b = java.newByte(12); + const b = java.newByte(12); ## newShort @@ -592,7 +592,7 @@ __Arguments__ __Example__ - var s = java.newShort(12); + const s = java.newShort(12); ## newLong @@ -608,7 +608,7 @@ __Arguments__ __Example__ - var s = java.newLong(12); + const s = java.newLong(12); ## newChar @@ -624,7 +624,7 @@ __Arguments__ __Example__ - var ch = java.newChar('a'); + const ch = java.newChar('a'); ## newDouble @@ -640,7 +640,7 @@ __Arguments__ __Example__ - var d = java.newDouble(3.14); + const d = java.newDouble(3.14); ## newFloat @@ -656,7 +656,7 @@ __Arguments__ __Example__ - var f = java.newFloat(3.14); + const f = java.newFloat(3.14); ## newProxy @@ -676,14 +676,14 @@ __Arguments__ __Example__ - var myProxy = java.newProxy('java.lang.Runnable', { + const myProxy = java.newProxy('java.lang.Runnable', { run: function () { // This is actually run on the v8 thread and not the new java thread console.log("hello from thread"); } }); - var thread = java.newInstanceSync("java.lang.Thread", myProxy); + const thread = java.newInstanceSync("java.lang.Thread", myProxy); thread.start(); ## isJvmCreated @@ -742,7 +742,7 @@ __Arguments__ __Example__ - var list = java.newInstanceSync("java.util.ArrayList"); + const list = java.newInstanceSync("java.util.ArrayList"); list.addSync("item1"); list.add("item2", function(err, result) { if(err) { console.error(err); return; } @@ -761,9 +761,9 @@ field values. __Example__ - var list = java.newInstanceSync("com.nearinfinty.MyClass"); + const list = java.newInstanceSync("com.nearinfinty.MyClass"); list.data = "test"; - var data = list.data; + const data = list.data; ## Getting the Full Method Signature @@ -816,9 +816,9 @@ public class ShutdownHookHelper { Compile ShutdownHookHelper and then use it as follows. ```javascript -var java = require('./'); +const java = require('./'); java.classpath.push('.'); -var ShutdownHookHelper = java.import('ShutdownHookHelper'); +const ShutdownHookHelper = java.import('ShutdownHookHelper'); ShutdownHookHelper.setShutdownHookSync(java.newProxy('java.lang.Runnable', { run: function () { @@ -839,12 +839,12 @@ When you call a Java method through node-java, any arguments (V8/JavaScript obje The JavaScript object returned by `java.import(classname)` is a JavaScript constructor Function, implemented such that you can create instances of the Java class. For example: ```javascript -var Test = java.import('Test'); -var test = new Test(); +const Test = java.import('Test'); +const test = new Test(); Test.someStaticMethod(function(err, result) { ... }); -var value1 = Test.NestedEnum.Value1; +const value1 = Test.NestedEnum.Value1; ``` But JavaScript reserves a few property names of Function objects: `name`, `arguments`, and `caller`. If your class has public static members (either methods or fields) with these names, node-java is unable to create the necessary property to implement the class's API. For example, suppose your class `Test` implements a static method named `caller`, or has a `NestedEnum` with a value `name`: @@ -860,15 +860,15 @@ public class Test { In JavaScript, you would expect to be able to use those static members like this: ```javascript -var Test = java.import('Test'); +const Test = java.import('Test'); Test.caller(function(err, result) { ... }); // ERROR -var value = Test.NestedEnum.name; // ERROR +const value = Test.NestedEnum.name; // ERROR ``` Node-java can't create those properties, so the above code won't work. Instead, node-java appends a suffix to the name. The default suffix is simply an underscore `_`, but you can change the suffix using `asyncOptions`: ```javascript -var java = require('java'); +const java = require('java'); java.asyncOptions = { asyncSuffix: "", @@ -876,9 +876,9 @@ java.asyncOptions = { ifReadOnlySuffix: "_alt" }; -var Test = java.import('Test'); +const Test = java.import('Test'); Test.caller_alt(function(err, result) { ... }); // OK -var value = Test.NestedEnum.name_alt; // OK +const value = Test.NestedEnum.name_alt; // OK ``` # Troubleshooting diff --git a/eslint.config.mjs b/eslint.config.mjs index 68c91e1..1ad81fd 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,12 +1,13 @@ import js from "@eslint/js"; import globals from "globals"; -import { defineConfig } from "eslint/config"; +import tseslint from "typescript-eslint"; -export default defineConfig([ +export default tseslint.config([ + { ignores: ["build"] }, { files: ["**/*.{js,mjs,cjs}"], plugins: { js }, - extends: ["js/recommended"], + extends: [js.configs.recommended], languageOptions: { globals: globals.node }, rules: { "no-var": ["error"], @@ -22,4 +23,43 @@ export default defineConfig([ ], }, }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommendedTypeChecked], + files: ["**/*.{ts,tsx}"], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + plugins: {}, + rules: { + "@typescript-eslint/restrict-template-expressions": [ + "error", + { + allowNever: true, + }, + ], + "@typescript-eslint/require-await": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-redundant-type-constituents": "off", + "@typescript-eslint/no-floating-promises": "error", + "@typescript-eslint/explicit-member-accessibility": "error", + "@typescript-eslint/explicit-function-return-type": "error", + "@typescript-eslint/no-unused-vars": [ + "warn", // or "error" + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + caughtErrorsIgnorePattern: "^_", + }, + ], + }, + }, ]); diff --git a/java.d.ts b/java.d.ts new file mode 100644 index 0000000..7990efd --- /dev/null +++ b/java.d.ts @@ -0,0 +1,354 @@ +/* eslint-disable @typescript-eslint/no-unsafe-function-type */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// eslint-disable-next-line no-var +declare var NodeJavaCore: NodeJavaCore.NodeAPI; +export = NodeJavaCore; + +declare namespace NodeJavaCore { + type JavaObject = any; + type JavaError = Error & { cause?: JavaObject }; + + export interface Callback { + (err?: Error, result?: T): void; + } + + export interface JavaCallback { + (err?: JavaError, result?: T): void; + } + + interface Promisify { + (fn: Function, receiver?: any): Function; + } + + interface AsyncOptions { + /** + * Suffix for synchronous method call signatures. + */ + syncSuffix: string; + + /** + * Suffix for callback-based async method call signatures. + */ + asyncSuffix?: string | undefined; + + /** + * Suffix for promise-based async method call signatures + */ + promiseSuffix?: string | undefined; + + /** + * Callback-to-promise transform implementation. From Node.js version 8 one can + * just use Node.js implementation: `promisify: require('util').promisify`. + */ + promisify?: Promisify | undefined; + + /** + * The JavaScript object returned by `java.import(classname)` is a JavaScript constructor + * Function, implemented such that you can create instances of the Java class. For example: + */ + ifReadOnlySuffix?: string | undefined; + } + + interface ProxyFunctions { + [index: string]: Function; + } + + interface Java { + /** + * Array of paths or jars to pass to the creation of the JVM. + * + * All items must be added to the classpath before calling any other node-java methods. + * + * @example + * java.classpath.push('commons.io.jar'); + * java.classpath.push('src'); + */ + classpath: string[]; + + /** + * Array of options to pass to the creation of the JVM. + * + * All items must be added to the options before calling any other node-java methods. + * + * @example + * java.options.push('-Djava.awt.headless=true'); + * java.options.push('-Xmx1024m'); + */ + options: string[]; + + /** + * @see AsyncOptions + */ + asyncOptions: AsyncOptions; + + /** + * Location of nodejavabridge_bindings.node + */ + nativeBindingLocation: string; + + /** + * Calls a method on the specified instance. If you are using the sync method an exception + * will be throw if an error occurs, otherwise it will be the first argument in the callback. + * + * @param instance An instance of the class from newInstance. + * @param methodName The name of the method to call. The method name can include the full + * signature (see [Getting the full method signature](README.md#getFullMethodSignature)). + * @param args The arguments to pass to the method, the last argument will be the callback to the function + * + * @example + * const instance = java.newInstanceSync("com.nearinfinty.MyClass"); + * java.callMethod(instance, "doSomething", 42, "test", function(err, results) { + * if(err) { console.error(err); return; } + * // results from doSomething + * }); + */ + callMethod(instance: JavaObject, methodName: string, ...args: any[]): void; + + /** + * Calls a method on the specified instance. If you are using the sync method an exception + * will be throw if an error occurs, otherwise it will be the first argument in the callback. + * + * @param instance An instance of the class from newInstance. + * @param methodName The name of the method to call. The method name can include the full + * signature (see [Getting the full method signature](README.md#getFullMethodSignature)). + * @param args The arguments to pass to the method + * @returns The result of the method call + * + * @example + * const instance = java.newInstanceSync("com.nearinfinty.MyClass"); + * const result = java.callMethodSync("com.nearinfinty.MyClass", "doSomething", 42, "test"); + */ + callMethodSync(instance: JavaObject, methodName: string, ...args: any[]): any; + + /** + * Calls a static method on the specified class. If you are using the sync method an exception will be + * throw if an error occurs, otherwise it will be the first argument in the callback. + * + * @param className The name of the class to call the method on. Separate nested classes + * using `'$'` (eg. `com.nearinfinty.MyClass$NestedClass`). + * @param methodName The name of the method to call. The method name can include the full + * signature (see [Getting the full method signature](README.md#getFullMethodSignature)). + * @param args The arguments to pass to the method, the last argument will be the callback to the function + */ + callStaticMethod(className: string, methodName: string, ...args: any[]): void; + + /** + * Calls a static method on the specified class. If you are using the sync method an exception will be + * throw if an error occurs, otherwise it will be the first argument in the callback. + * + * @param className The name of the class to call the method on. Separate nested classes + * using `'$'` (eg. `com.nearinfinty.MyClass$NestedClass`). + * @param methodName The name of the method to call. The method name can include the full + * signature (see [Getting the full method signature](README.md#getFullMethodSignature)). + * @param args The arguments to pass to the method + * @returns The result of the method call + */ + callStaticMethodSync(className: string, methodName: string, ...args: any[]): any; + + /** + * Finds the class with the specified binary name. This method should be overridden by class loader + * implementations that follow the delegation model for loading classes, and will be invoked by the + * loadClass method after checking the parent class loader for the requested class. The default + * implementation throws a ClassNotFoundException. + * + * @param className The binary name of the class + * @returns The resulting Class object + */ + findClassSync(className: string): JavaObject; + + /** + * Gets a static field value from the specified class. + * + * @param className The name of the class to get the value from. Separate nested classes + * using `'$'` (eg. `com.nearinfinty.MyClass$NestedClass`). + * @param fieldName The name of the field to get the value from. + * @returns The valid of the static field + */ + getStaticFieldValue(className: string, fieldName: string): any; + + /** + * Sets a static field value on the specified class. + * + * @param className The name of the class to set the value on. Separate nested classes + * using `'$'` (eg. `com.nearinfinty.MyClass$NestedClass`). + * @param fieldName The name of the field to set the value on. + * @param newValue The new value to assign to the field. + */ + setStaticFieldValue(className: string, fieldName: string, newValue: any): void; + + /** + * Determines of a javaObject is an instance of a class. + * + * @param javaObject Instance of a java object returned from a method or from newInstance. + * @param className A string class name. + * + * @example + * const obj = java.newInstanceSync("my.package.SubClass"); + * if(java.instanceOf(obj, "my.package.SuperClass")) { + * console.log("obj is an instance of SuperClass"); + * } + */ + instanceOf(javaObject: JavaObject, className: string): boolean; + + /** + * Register that a client wants to be called back immediately before and/or immediately + * after the JVM is created. If used, this function must be called before the JVM has been + * created. The before function is typically used to add to the classpath. The function may + * execute asynchronous operations (such as a async glob function). The after function is + * sometimes useful for doing one-time initialization that requires the JVM to first be + * initialized. If either function is unnecessary, use `null` or `undefined`. See also + * `registerClientP` and `ensureJvm`. See the unit tests in `testAsyncOptions` for examples. + */ + registerClient( + before: ((cb: Callback) => void) | undefined | null, + after?: (cb: Callback) => void + ): void; + + /** + * Register that a client wants to be called back immediately before and/or immediately + * after the JVM is created. If used, this function must be called before the JVM has been + * created. The before function is typically used to add to the classpath. The function may + * execute asynchronous operations (such as a async glob function). The after function is + * sometimes useful for doing one-time initialization that requires the JVM to first be + * initialized. If either function is unnecessary, use `null` or `undefined`. See also + * `registerClientP` and `ensureJvm`. See the unit tests in `testAsyncOptions` for examples. + */ + registerClientP(beforeP: (() => Promise) | undefined | null, afterP?: () => Promise): void; + + /** + * If the JVM has not yet been created, execute the full JVM initialization process, then + * call callback function when initialization is complete. If the JVM has been created, just + * call the callback. Note that the full initialization process includes: 1) executing all + * registered client *before* hooks, 2) creating the JVM, then 3) executing all registered + * client *after* hooks. + */ + ensureJvm(done: Callback): void; + + /** + * If the JVM has not yet been created, execute the full JVM initialization process, then + * call callback function when initialization is complete. If the JVM has been created, just + * call the callback. Note that the full initialization process includes: 1) executing all + * registered client *before* hooks, 2) creating the JVM, then 3) executing all registered + * client *after* hooks. + */ + ensureJvm(): Promise; + + /** + * Returns true if the JVM has been created. The JVM can only be created once. + */ + isJvmCreated(): boolean; + + /** + * Creates a new java byte. This is needed because JavaScript does not have the concept of a byte. + */ + newByte(val: number): JavaObject; + + /** + * Creates a new java short. This is needed because JavaScript does not have the concept of a short. + */ + newShort(val: number): JavaObject; + + /** + * Creates a new java long. This is needed because JavaScript does not have the concept of a long. + */ + newLong(val: number): JavaObject; + + /** + * Creates a new java char. This is needed because JavaScript does not have the concept of a char. + */ + newChar(val: string | number): JavaObject; + + /** + * Creates a new java float. This is needed to force JavaScript's number to a float to call some methods. + */ + newFloat(val: number): JavaObject; + + /** + * Creates a new java double. This is needed to force JavaScript's number to a double to call some methods. + */ + newDouble(val: number): JavaObject; + + /** + * Loads the class given by className such that it acts and feels like a JavaScript object. + * + * @param className The name of the class to create. Separate nested classes + * using `'$'` (eg. `com.nearinfinty.MyClass$NestedClass`). + * + * @example + * const Test = java.import('Test'); + * Test.someStaticMethodSync(5); + * console.log(Test.someStaticField); + * + * const value1 = Test.NestedEnum.Value1; + * + * const test = new Test(); + * list.instanceMethodSync('item1'); + */ + import(className: string): JavaObject; + + /** + * Creates an instance of the specified class. If you are using the sync method an exception will + * be throw if an error occurs, otherwise it will be the first argument in the callback. + * + * @param className The name of the class to create. Separate nested classes + * using `'$'` (eg. `com.nearinfinty.MyClass$NestedClass`). + * @param args Arguments to pass to the constructor, the last argument is a callback function + */ + newInstance(className: string, ...args: any[]): void; + + /** + * Creates an instance of the specified class. If you are using the sync method an exception will + * be throw if an error occurs, otherwise it will be the first argument in the callback. + * + * @param className The name of the class to create. Separate nested classes + * using `'$'` (eg. `com.nearinfinty.MyClass$NestedClass`). + * @param args Arguments to pass to the constructor + */ + newInstanceSync(className: string, ...args: any[]): JavaObject; + + /** + * Creates a new java array of given glass type. To create array of primitive types + * like `char`, `byte`, etc, pass the primitive typename + * (eg. `java.newArray("char", "hello world\n".split(''))`). + * + * @param className The name of the type of array elements. Separate nested classes + * using `'$'` (eg. `com.nearinfinty.MyClass$NestedClass`). + * @param arg A JavaScript array of values to assign to the java array. + */ + newArray(className: string, arg: any[]): JavaObject; + + /** + * Get the current class loader + */ + getClassLoader(): JavaObject; + + /** + * Creates a new java Proxy for the given interface. Functions passed in will run on the v8 + * main thread and not a new thread. + * + * The returned object has a method unref() which you can use to free the object for garbage + * collection. + * + * @param interfaceName The name of the interface to proxy. Separate nested classes + * using `'$'` (eg. `com.nearinfinty.MyClass$NestedClass`). + * @param functions A hash of functions matching the function in the interface. + * + * @example + * const myProxy = java.newProxy('java.lang.Runnable', { + * run: function () { + * // This is actually run on the v8 thread and not the new java thread + * console.log("hello from thread"); + * } + * }); + * + * const thread = java.newInstanceSync("java.lang.Thread", myProxy); + * thread.start(); + */ + newProxy(interfaceName: string, functions: ProxyFunctions): JavaObject; + + /** + * Stops the running event loop + */ + stop(): void; + } +} diff --git a/package-lock.json b/package-lock.json index a596ce1..72fd77c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "async": "^3.2.6", "find-java-home": "^2.0.0", "glob": "^10.4.5", "nan": "^2.22.2", @@ -18,10 +17,16 @@ }, "devDependencies": { "@eslint/js": "^9.27.0", + "@types/find-root": "^1.1.4", + "@types/node": "^22.15.21", + "@types/when": "^2.4.41", "chalk": "2.4.2", "eslint": "^9.27.0", + "find-root": "^1.1.0", "globals": "^16.1.0", "prettier": "^3.5.3", + "typescript": "^5.8.3", + "typescript-eslint": "^8.32.1", "vitest": "^3.1.3", "when": "3.7.8" }, @@ -777,6 +782,44 @@ "dev": true, "license": "MIT" }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@npmcli/agent": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", @@ -1108,6 +1151,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/find-root": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/find-root/-/find-root-1.1.4.tgz", + "integrity": "sha512-2EXK/+gVhVgtt4JqThbEncORvpYJKzi9tQGmI3EkU2jTgMzQsrPm/hbd5xe5uPdeFzIW5gh2PRvvPjaUsI8vpg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1115,6 +1165,239 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/when": { + "version": "2.4.41", + "resolved": "https://registry.npmjs.org/@types/when/-/when-2.4.41.tgz", + "integrity": "sha512-o/j5X9Bnv6mMG4ZcNJur8UaU17Rl0mLbTZvWcODVVy+Xdh8LEc7s6I0CvbEuTP786LTa0OyJby5P4hI7C+ZJNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz", + "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/type-utils": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", + "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", + "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz", + "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz", + "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", + "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz", + "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", + "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@vitest/expect": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.3.tgz", @@ -1338,12 +1621,6 @@ "node": ">=12" } }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -1360,6 +1637,19 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -1919,6 +2209,36 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1933,6 +2253,16 @@ "dev": true, "license": "MIT" }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fdir": { "version": "6.4.4", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", @@ -1961,6 +2291,19 @@ "node": ">=16.0.0" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-java-home": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/find-java-home/-/find-java-home-2.0.0.tgz", @@ -1970,6 +2313,13 @@ "winreg": "~1.2.2" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true, + "license": "MIT" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2155,6 +2505,13 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2308,6 +2665,16 @@ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "license": "MIT" }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2459,6 +2826,43 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3007,6 +3411,27 @@ "node": ">=6" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3026,6 +3451,17 @@ "node": ">= 4" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rollup": { "version": "4.41.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz", @@ -3066,6 +3502,30 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -3413,6 +3873,32 @@ "node": ">=14.0.0" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3426,6 +3912,50 @@ "node": ">= 0.8.0" } }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz", + "integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.32.1", + "@typescript-eslint/parser": "8.32.1", + "@typescript-eslint/utils": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "node_modules/unique-filename": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", diff --git a/package.json b/package.json index fef7677..c5c633e 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,6 @@ "url": "https://github.com/joeferner/node-java.git" }, "dependencies": { - "async": "^3.2.6", "find-java-home": "^2.0.0", "glob": "^10.4.5", "nan": "^2.22.2", @@ -34,22 +33,31 @@ }, "devDependencies": { "@eslint/js": "^9.27.0", + "@types/find-root": "^1.1.4", + "@types/node": "^22.15.21", + "@types/when": "^2.4.41", "chalk": "2.4.2", "eslint": "^9.27.0", + "find-root": "^1.1.0", "globals": "^16.1.0", "prettier": "^3.5.3", + "typescript": "^5.8.3", + "typescript-eslint": "^8.32.1", "vitest": "^3.1.3", "when": "3.7.8" }, "scripts": { "install": "node-gyp rebuild", "clean": "rm -rf build", - "test": "node ./scripts/testRunner.js", + "test": "tsc && node ./build/ts/scripts/testRunner.js", "postinstall": "node ./scripts/postInstall.js", "lint": "eslint --ext js,ts,tsx --report-unused-disable-directives --max-warnings 0 .", "format": "prettier --write .", "format-cpp": "clang-format --version; find src-cpp/ -iname '*.h' -o -iname '*.cpp' | xargs clang-format -i", - "precommit": "npm run format-cpp && npm run format && npm run lint && npm test" + "build-ts": "tsc", + "build-ts:watch": "tsc --watch", + "precommit": "npm run format-cpp && npm run format && npm run lint && npm run build-ts && npm test" }, - "main": "./index.js" + "main": "./index.js", + "types": "./java.d.ts" } diff --git a/scripts/testRunner.js b/scripts/testRunner.js deleted file mode 100644 index 0f1c026..0000000 --- a/scripts/testRunner.js +++ /dev/null @@ -1,41 +0,0 @@ -// testRunner.js - -// This is a custom test runner. All tests are run with vitest, but in separate -// processes, which allows us to test java with different configuration options. - -const async = require("async"); -const chalk = require("chalk"); -const childProcess = require("node:child_process"); -const glob = require("glob"); -const path = require("node:path"); - -const tests = glob - .sync("*.test.js", { cwd: path.join(__dirname, "..", "testAsyncOptions") }) - .sort((a, b) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase())); - -tests.unshift("test"); // Arrange to run the primary tests first, in a single process - -function runTest(testArgs, done) { - const vitest = path.join("node_modules", ".bin", "vitest"); - const cmd = testArgs === "test" ? `vitest --dir test` : `${vitest} ${testArgs}`; - console.log(`running "${cmd}"...`); - childProcess.exec(cmd, (error, stdout, stderr) => { - const errText = stderr.toString(); - if (errText !== "") { - console.error(chalk.bold.red(errText)); - } - - process.stdout.write(stdout.toString()); - done(error); - }); -} - -console.log('test to run', tests); -async.eachSeries(tests, runTest, (err) => { - if (err) { - console.error(chalk.bold.red(err)); - process.exit(1); - return; - } - console.log(chalk.green("Tests completed successfully!")); -}); diff --git a/scripts/testRunner.ts b/scripts/testRunner.ts new file mode 100644 index 0000000..fe36d28 --- /dev/null +++ b/scripts/testRunner.ts @@ -0,0 +1,55 @@ +// testRunner.js + +// This is a custom test runner. All tests are run with vitest, but in separate +// processes, which allows us to test java with different configuration options. + +import chalk from "chalk"; +import findRoot from "find-root"; +import * as glob from "glob"; +import childProcess, { ExecException } from "node:child_process"; +import path from "node:path"; + +const root = findRoot(__dirname); + +const tests = glob + .sync("*.test.ts", { cwd: path.join(root, "testAsyncOptions") }) + .sort((a: string, b: string) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase())); + +tests.unshift("test"); // Arrange to run the primary tests first, in a single process + +async function runTest(testIndex: number, testArgs: string): Promise { + const vitest = path.join("node_modules", ".bin", "vitest"); + const cmd = testArgs === "test" ? `vitest --dir test` : `${vitest} ${testArgs}`; + console.log(chalk.cyan(`(${testIndex + 1}/${tests.length}) running "${cmd}"...`)); + return new Promise((resolve, reject) => { + childProcess.exec(cmd, (error: ExecException | null, stdout: string, stderr: string) => { + const errText = stderr.toString(); + if (errText !== "") { + console.error(chalk.bold.red(errText)); + } + + process.stdout.write(stdout.toString()); + if (error) { + return reject(error); + } + resolve(); + }); + }); +} + +console.log("test to run", tests); +async function runAll(): Promise { + for (let i = 0; i < tests.length; i++) { + const test = tests[i]; + await runTest(i, test); + } +} + +runAll() + .then(() => { + console.log(chalk.green("Tests completed successfully!")); + }) + .catch((err) => { + console.error(chalk.bold.red(err)); + process.exit(1); + }); diff --git a/src-node/nodeJavaBridge.js b/src-node/nodeJavaBridge.js index ca379d3..ecfbbef 100644 --- a/src-node/nodeJavaBridge.js +++ b/src-node/nodeJavaBridge.js @@ -2,7 +2,6 @@ process.env.PATH += require("../build/jvm_dll_path.json"); -const async = require("async"); const path = require("path"); const fs = require("fs"); let binaryPath = null; @@ -98,61 +97,50 @@ java.registerClientP = function (beforeP, afterP) { clients.push({ beforeP: beforeP, afterP: afterP }); }; -function runBeforeHooks(done) { - function iterator(client, cb) { - try { - if (client.before) { - client.before(cb); - } else if (client.beforeP) { - client.beforeP().then( - function (_ignored) { - cb(); - }, - function (err) { - cb(err); +async function runBeforeHooks() { + for (const client of clients) { + if (client.before) { + await new Promise((resolve, reject) => { + client.before((err) => { + if (err) { + return reject(err); } - ); - } else { - cb(); - } - } catch (err) { - cb(err); + return resolve(); + }); + }); + } + if (client.beforeP) { + await client.beforeP(); } } - async.each(clients, iterator, done); } -function createJVMAsync(callback) { +function createJVMAsync() { const _ignore = java.newLong(0); // called just for the side effect that it will create the JVM - callback(); } -function runAfterHooks(done) { - function iterator(client, cb) { - try { - if (client.after) { - client.after(cb); - } else if (client.afterP) { - client.afterP().then( - (_ignored) => { - cb(); - }, - (err) => { - cb(err); +async function runAfterHooks() { + for (const client of clients) { + if (client.after) { + await new Promise((resolve, reject) => { + client.after((err) => { + if (err) { + return reject(err); } - ); - } else { - cb(); - } - } catch (err) { - cb(err); + return resolve(); + }); + }); + } + if (client.afterP) { + await client.afterP(); } } - async.each(clients, iterator, done); } -function initializeAll(done) { - async.series([runBeforeHooks, createJVMAsync, runAfterHooks], done); +async function initializeAll() { + await runBeforeHooks(); + createJVMAsync(); + await runAfterHooks(); } // This function ensures that the JVM has been launched, asynchronously. The application can be notified @@ -187,7 +175,14 @@ java.ensureJvm = function (callback) { // Finally, queue the initializeAll function. else { - return setImmediate(initializeAll, callback); + return setImmediate(async () => { + try { + await initializeAll(); + callback(); + } catch (err) { + callback(err); + } + }); } }; diff --git a/test/TestLambda.test.js b/test/TestLambda.test.ts similarity index 73% rename from test/TestLambda.test.js rename to test/TestLambda.test.ts index f2b7b13..0b7a8ab 100644 --- a/test/TestLambda.test.js +++ b/test/TestLambda.test.ts @@ -1,9 +1,14 @@ -import { describe, expect, test } from "vitest"; -import { getJava } from "../testHelpers"; - -const java = getJava(); +import { beforeAll, describe, expect, test } from "vitest"; +import { expectJavaError, getJava } from "../testHelpers"; +import { Java } from "../java"; describe("Java8", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(); + }); + test("call methods of a class that uses lambda expressions", () => { try { const TestLambda = java.import("TestLambda"); @@ -13,6 +18,7 @@ describe("Java8", () => { const diff = lambda.testLambdaSubtractionSync(23, 42); expect(diff).toBe(-19); } catch (err) { + expectJavaError(err); const unsupportedVersion = java.instanceOf(err.cause, "java.lang.UnsupportedClassVersionError"); if (unsupportedVersion) { console.log("JRE 1.8 not available"); diff --git a/test/awt.test.js b/test/awt.test.ts similarity index 78% rename from test/awt.test.js rename to test/awt.test.ts index 34d1f00..9c8a052 100644 --- a/test/awt.test.js +++ b/test/awt.test.ts @@ -1,10 +1,15 @@ import path from "node:path"; -import { describe, expect, test } from "vitest"; +import { beforeAll, describe, expect, test } from "vitest"; +import { Java } from "../java"; import { getJava } from "../testHelpers"; -const java = getJava(); - describe("AWT", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(); + }); + test("calling AWT method (see issue 1)", () => { const headlessVal = java.callStaticMethodSync("java.lang.System", "getProperty", "java.awt.headless"); console.log("java.awt.headless =", headlessVal); diff --git a/test/dynamicProxy.test.js b/test/dynamicProxy.test.ts similarity index 87% rename from test/dynamicProxy.test.js rename to test/dynamicProxy.test.ts index aee0543..e82ba58 100644 --- a/test/dynamicProxy.test.js +++ b/test/dynamicProxy.test.ts @@ -1,9 +1,14 @@ -import { describe, expect, test } from "vitest"; -import { getJava } from "../testHelpers"; - -const java = getJava(); +import { beforeAll, describe, expect, test } from "vitest"; +import { Java, JavaObject } from "../java"; +import { expectJavaError, getJava } from "../testHelpers"; describe("Dynamic Proxy", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(); + }); + test("0 Arguments", () => { let callCount = 0; @@ -23,7 +28,7 @@ describe("Dynamic Proxy", () => { let runData = ""; const myProxy = java.newProxy("RunInterface$Interface1Arg", { - run: function (str) { + run: (str: string) => { runData += str; }, }); @@ -36,7 +41,7 @@ describe("Dynamic Proxy", () => { test("1 Arguments with return data", () => { const myProxy = java.newProxy("RunInterface$InterfaceWithReturn", { - run: function (i) { + run: (i: number) => { return i + 1; }, }); @@ -51,7 +56,7 @@ describe("Dynamic Proxy", () => { let runData = ""; const myProxy = java.newProxy("ListenerInterface", { - onEvent: function (_list, _runtime) { + onEvent: (_list: JavaObject, _runtime: JavaObject) => { runData = "onEvent"; }, }); @@ -64,7 +69,7 @@ describe("Dynamic Proxy", () => { }); test("thread", async () => { - await new Promise((resolve, reject) => { + await new Promise((resolve, reject) => { let callCount = 0; const myProxy = java.newProxy("java.lang.Runnable", { @@ -78,7 +83,7 @@ describe("Dynamic Proxy", () => { let timeout = 50; - function waitForThread() { + function waitForThread(): void { if (callCount === 1) { return resolve(); } @@ -94,15 +99,16 @@ describe("Dynamic Proxy", () => { }); test("thread issue #143", async () => { - await new Promise((resolve) => { + await new Promise((resolve) => { const myProxy = java.newProxy("RunInterface$InterfaceWithReturn", { - run: function (i) { + run: (i: number) => { return i - 1; }, }); const runInterface = java.newInstanceSync("RunInterface"); - runInterface.runInAnotherThread(myProxy, function (err, result) { + runInterface.runInAnotherThread(myProxy, (err: Error | undefined, result: number) => { + expect(err).toBeUndefined(); expect(result).toBe(45); resolve(); }); @@ -141,7 +147,7 @@ describe("Dynamic Proxy", () => { test("js equals()", () => { const myProxy = java.newProxy("RunInterface$InterfaceWithReturn", { - equals: function (_obj) { + equals: (_obj: JavaObject) => { return true; }, }); @@ -201,7 +207,8 @@ describe("Dynamic Proxy", () => { test("js string error", () => { const myProxy = java.newProxy("RunInterface$InterfaceWithReturn", { - run: (_i) => { + run: (_i: JavaObject) => { + // eslint-disable-next-line @typescript-eslint/only-throw-error throw "myError"; }, }); @@ -211,6 +218,7 @@ describe("Dynamic Proxy", () => { runInterface.runWithReturnSync(myProxy); throw new Error("Exception was not thrown"); } catch (e) { + expectJavaError(e); expect(e.cause.getClassSync().getNameSync()).toBe("java.lang.RuntimeException"); expect(e.message).toMatch(/Caused by: node\.NodeJsException:.*myError/); } @@ -218,7 +226,7 @@ describe("Dynamic Proxy", () => { test("js Error", () => { const myProxy = java.newProxy("RunInterface$InterfaceWithReturn", { - run: function (_i) { + run: (_i: JavaObject) => { throw new Error("newError"); }, }); @@ -228,6 +236,7 @@ describe("Dynamic Proxy", () => { runInterface.runWithReturnSync(myProxy); throw new Error("Exception was not thrown"); } catch (e) { + expectJavaError(e); expect(e.cause.getClassSync().getNameSync()).toBe("java.lang.RuntimeException"); expect(e.message).toMatch(/Caused by: node\.NodeJsException:.*newError/); } @@ -235,7 +244,7 @@ describe("Dynamic Proxy", () => { test("invocationHandler", () => { const myProxy = java.newProxy("RunInterface$InterfaceWithReturn", { - run: (i) => { + run: (i: number) => { return i + 2; }, }); @@ -247,7 +256,7 @@ describe("Dynamic Proxy", () => { test("unref", () => { const myProxy = java.newProxy("RunInterface$InterfaceWithReturn", { - run: function (i) { + run: (i: number) => { return i + 1; }, }); @@ -257,6 +266,7 @@ describe("Dynamic Proxy", () => { try { myProxy.invocationHandler.run(42); } catch (e) { + expectJavaError(e); expect(e.message).toBe("dynamicProxyData has been destroyed or corrupted"); } diff --git a/test/importClass.test.js b/test/importClass.test.ts similarity index 85% rename from test/importClass.test.js rename to test/importClass.test.ts index 63e6448..5086eb8 100644 --- a/test/importClass.test.js +++ b/test/importClass.test.ts @@ -1,9 +1,14 @@ -import { afterEach, describe, expect, test } from "vitest"; +import { afterEach, beforeAll, describe, expect, test } from "vitest"; import { getJava } from "../testHelpers"; - -const java = getJava(); +import { Java } from "../java"; describe("Import Class", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(); + }); + afterEach(() => { java.setStaticFieldValue("Test", "staticFieldInt", 42); }); @@ -15,8 +20,8 @@ describe("Import Class", () => { expect(Test.staticFieldInt).toBe(200); expect(Test.staticMethodSync(99)).toBe(100); - await new Promise((resolve) => { - Test.staticMethod(99, (err, result) => { + await new Promise((resolve) => { + Test.staticMethod(99, (err: Error | undefined, result: number | undefined) => { expect(err).toBeFalsy(); expect(result).toBe(100); diff --git a/test/instanceof.test.js b/test/instanceof.test.ts similarity index 79% rename from test/instanceof.test.js rename to test/instanceof.test.ts index 4f8f3ce..679a3d3 100644 --- a/test/instanceof.test.js +++ b/test/instanceof.test.ts @@ -1,9 +1,14 @@ -import { describe, expect, test } from "vitest"; +import { beforeAll, describe, expect, test } from "vitest"; import { getJava } from "../testHelpers"; - -const java = getJava(); +import { Java } from "../java"; describe("instanceOf", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(); + }); + test("working", () => { const subclass = java.newInstanceSync("Test$SubClass"); if (!java.instanceOf(subclass, "Test$SuperClass")) { diff --git a/test/java-ambiguousMethod.test.js b/test/java-ambiguousMethod.test.ts similarity index 60% rename from test/java-ambiguousMethod.test.js rename to test/java-ambiguousMethod.test.ts index c1a367c..8823773 100644 --- a/test/java-ambiguousMethod.test.js +++ b/test/java-ambiguousMethod.test.ts @@ -1,9 +1,14 @@ -import { describe, expect, test } from "vitest"; +import { beforeAll, describe, expect, test } from "vitest"; +import { Java } from "../java"; import { getJava } from "../testHelpers"; -const java = getJava(); - describe("Java - Call Ambiguous Method", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(); + }); + test("staticMethodAmbiguous (sync) - int passed to double", () => { const result = java.callStaticMethodSync("Test", "staticMethodAmbiguous(Ljava/lang/Double;)I", 1); expect(result).toBe(1); @@ -27,42 +32,63 @@ describe("Java - Call Ambiguous Method", () => { }); test("staticMethodAmbiguous - int passed to double", async () => { - await new Promise((resolve) => { - java.callStaticMethod("Test", "staticMethodAmbiguous(Ljava/lang/Double;)I", 1, function (err, result) { - expect(err).toBeFalsy(); - expect(result).toBe(1); - resolve(); - }); + await new Promise((resolve) => { + java.callStaticMethod( + "Test", + "staticMethodAmbiguous(Ljava/lang/Double;)I", + 1, + (err: Error | undefined, result: number | undefined) => { + expect(err).toBeFalsy(); + expect(result).toBe(1); + resolve(); + } + ); }); }); test("staticMethodAmbiguous - double passed to int", async () => { - await new Promise((resolve) => { - java.callStaticMethod("Test", "staticMethodAmbiguous(Ljava/lang/Integer;)I", 1.1, function (err, result) { - expect(err).toBeFalsy(); - expect(result).toBe(2); - resolve(); - }); + await new Promise((resolve) => { + java.callStaticMethod( + "Test", + "staticMethodAmbiguous(Ljava/lang/Integer;)I", + 1.1, + (err: Error | undefined, result: number | undefined) => { + expect(err).toBeFalsy(); + expect(result).toBe(2); + resolve(); + } + ); }); }); test("staticMethodAmbiguous - method not found", async () => { - await new Promise((resolve) => { - java.callStaticMethod("Test", "staticMethodAmbiguous(Ljava/lang/String;)I", 1, function (err, result) { - expect(err).toBeTruthy(); - expect(result).toBeFalsy(); - resolve(); - }); + await new Promise((resolve) => { + java.callStaticMethod( + "Test", + "staticMethodAmbiguous(Ljava/lang/String;)I", + 1, + (err: Error | undefined, result: number | undefined) => { + expect(err).toBeTruthy(); + expect(result).toBeFalsy(); + resolve(); + } + ); }); }); test("staticMethodAmbiguous - method argument count mismatch", async () => { - await new Promise((resolve) => { - java.callStaticMethod("Test", "staticMethodAmbiguous(Ljava/lang/Integer;)I", 1, 2, function (err, result) { - expect(err).toBeTruthy(); - expect(result).toBeFalsy(); - resolve(); - }); + await new Promise((resolve) => { + java.callStaticMethod( + "Test", + "staticMethodAmbiguous(Ljava/lang/Integer;)I", + 1, + 2, + (err: Error | undefined, result: number | undefined) => { + expect(err).toBeTruthy(); + expect(result).toBeFalsy(); + resolve(); + } + ); }); }); diff --git a/test/java-callStaticMethod.test.js b/test/java-callStaticMethod.test.ts similarity index 65% rename from test/java-callStaticMethod.test.js rename to test/java-callStaticMethod.test.ts index fd4978d..1ad3eb4 100644 --- a/test/java-callStaticMethod.test.js +++ b/test/java-callStaticMethod.test.ts @@ -1,12 +1,17 @@ -import { describe, expect, test } from "vitest"; -import { getJava } from "../testHelpers"; - -const java = getJava(); +import { beforeAll, describe, expect, test } from "vitest"; +import { expectJavaError, getJava } from "../testHelpers"; +import { Java } from "../java"; describe("Java - Call Static Method", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(); + }); + test("callStaticMethod", async () => { - await new Promise((resolve) => { - java.callStaticMethod("Test", "staticMethod", (err, result) => { + await new Promise((resolve) => { + java.callStaticMethod("Test", "staticMethod", (err: Error | undefined, result: string | undefined) => { expect(err).toBeFalsy(); expect(result).toBeTruthy(); expect(result).toBe("staticMethod called"); @@ -29,8 +34,8 @@ describe("Java - Call Static Method", () => { }); test("callStaticMethod with args", async () => { - await new Promise((resolve) => { - java.callStaticMethod("Test", "staticMethod", 42, (err, result) => { + await new Promise((resolve) => { + java.callStaticMethod("Test", "staticMethod", 42, (err: Error | undefined, result: string | undefined) => { expect(err).toBeFalsy(); expect(result).toBeTruthy(); expect(result).toBe(43); @@ -53,8 +58,8 @@ describe("Java - Call Static Method", () => { }); test("callStaticMethod bad class name", async () => { - await new Promise((resolve) => { - java.callStaticMethod("BadClassName", "staticMethod", function (err, result) { + await new Promise((resolve) => { + java.callStaticMethod("BadClassName", "staticMethod", (err: Error | undefined, result: string | undefined) => { expect(err).toBeTruthy(); expect(result).toBeFalsy(); resolve(); @@ -69,8 +74,8 @@ describe("Java - Call Static Method", () => { }); test("callStaticMethod bad arg types", async () => { - await new Promise((resolve) => { - java.callStaticMethod("Test", "staticMethod", "z", function (err, result) { + await new Promise((resolve) => { + java.callStaticMethod("Test", "staticMethod", "z", (err: Error | undefined, result: string | undefined) => { expect(err).toBeTruthy(); expect(result).toBeFalsy(); resolve(); @@ -85,8 +90,8 @@ describe("Java - Call Static Method", () => { }); test("callStaticMethod bad number of args", async () => { - await new Promise((resolve) => { - java.callStaticMethod("Test", "staticMethod", 42, "z", function (err, result) { + await new Promise((resolve) => { + java.callStaticMethod("Test", "staticMethod", 42, "z", (err: Error | undefined, result: string | undefined) => { expect(err).toBeTruthy(); expect(result).toBeFalsy(); resolve(); @@ -101,8 +106,8 @@ describe("Java - Call Static Method", () => { }); test("callStaticMethod bad method name", async () => { - await new Promise((resolve) => { - java.callStaticMethod("Test", "badMethodName", function (err, result) { + await new Promise((resolve) => { + java.callStaticMethod("Test", "badMethodName", (err: Error | undefined, result: string | undefined) => { expect(err).toBeTruthy(); expect(result).toBeFalsy(); resolve(); @@ -123,7 +128,8 @@ describe("Java - Call Static Method", () => { result = java.callStaticMethodSync("Test", "staticMethodThrows", ex); throw new Error("expected error"); } catch (err) { - expect(err.cause.getMessageSync()).toBe("my exception"); + expectJavaError(err); + expect(err.cause?.getMessageSync()).toBe("my exception"); expect(err.toString()).toMatch(/my exception/); expect(result).toBeFalsy(); } @@ -135,15 +141,16 @@ describe("Java - Call Static Method", () => { java.callStaticMethodSync("Test", "staticMethodThrows", ex); throw new Error("should throw"); } catch (err) { + expectJavaError(err); expect(err.toString()).toMatch(/my exception/); } }); test("staticMethodThrows exception thrown from method", async () => { const ex = java.newInstanceSync("java.lang.Exception", "my exception"); - await new Promise((resolve) => { - java.callStaticMethod("Test", "staticMethodThrows", ex, function (err, result) { - expect(err).toBeTruthy(); + await new Promise((resolve) => { + java.callStaticMethod("Test", "staticMethodThrows", ex, (err: Error | undefined, result: string | undefined) => { + expectJavaError(err); expect(err.cause.getMessageSync()).toBe("my exception"); expect(err.toString()).toMatch(/my exception/); expect(result).toBeFalsy(); @@ -157,19 +164,24 @@ describe("Java - Call Static Method", () => { java.callStaticMethodSync("Test", "staticMethodThrowsNewException"); throw new Error("should throw"); } catch (err) { + expectJavaError(err); expect(err.toString()).toMatch(/my exception/); } }); test("staticMethodThrowsNewException exception thrown from method", async () => { - await new Promise((resolve) => { - java.callStaticMethod("Test", "staticMethodThrowsNewException", function (err, result) { - expect(err).toBeTruthy(); - expect(err.cause.getMessageSync()).toBe("my exception"); - expect(err.toString()).toMatch(/my exception/); - expect(result).toBeFalsy(); - resolve(); - }); + await new Promise((resolve) => { + java.callStaticMethod( + "Test", + "staticMethodThrowsNewException", + (err: Error | undefined, result: string | undefined) => { + expectJavaError(err); + expect(err.cause.getMessageSync()).toBe("my exception"); + expect(err.toString()).toMatch(/my exception/); + expect(result).toBeFalsy(); + resolve(); + } + ); }); }); @@ -180,6 +192,7 @@ describe("Java - Call Static Method", () => { myTest.methodThrowsSync(ex); throw new Error("should throw"); } catch (err) { + expectJavaError(err); expect(err.toString()).toMatch(/my exception/); } }); @@ -187,8 +200,9 @@ describe("Java - Call Static Method", () => { test("methodThrows exception thrown from method", async () => { const ex = java.newInstanceSync("java.lang.Exception", "my exception"); const myTest = java.newInstanceSync("Test"); - await new Promise((resolve) => { - return myTest.methodThrows(ex, function (err) { + await new Promise((resolve) => { + return myTest.methodThrows(ex, (err: Error | undefined) => { + expectJavaError(err); expect(err.toString()).toMatch(/my exception/); resolve(); }); @@ -201,14 +215,16 @@ describe("Java - Call Static Method", () => { myTest.methodThrowsNewExceptionSync(); throw new Error("should throw"); } catch (err) { + expectJavaError(err); expect(err.toString()).toMatch(/my exception/); } }); test("methodThrowsNewException exception thrown from method", async () => { const myTest = java.newInstanceSync("Test"); - await new Promise((resolve) => { - myTest.methodThrowsNewException((err) => { + await new Promise((resolve) => { + myTest.methodThrowsNewException((err: Error | undefined) => { + expectJavaError(err); expect(err.toString()).toMatch(/my exception/); resolve(); }); @@ -217,23 +233,34 @@ describe("Java - Call Static Method", () => { test("char array", async () => { const charArray = java.newArray("char", "hello world\n".split("")); - await new Promise((resolve) => { - java.callStaticMethod("Test", "staticMethodCharArrayToString", charArray, function (err, result) { - expect(err).toBeFalsy(); - expect(result).toBeTruthy(); - expect(result).toBe("hello world\n"); - resolve(); - }); + await new Promise((resolve) => { + java.callStaticMethod( + "Test", + "staticMethodCharArrayToString", + charArray, + (err: Error | undefined, result: string | undefined) => { + expect(err).toBeFalsy(); + expect(result).toBeTruthy(); + expect(result).toBe("hello world\n"); + resolve(); + } + ); }); }); test("String passed in for Object", async () => { - await new Promise((resolve) => { - java.callStaticMethod("Test", "static2Objects", "1", "2", function (err, result) { - expect(err).toBeFalsy(); - expect(result).toBe(false); - resolve(); - }); + await new Promise((resolve) => { + java.callStaticMethod( + "Test", + "static2Objects", + "1", + "2", + (err: Error | undefined, result: boolean | undefined) => { + expect(err).toBeFalsy(); + expect(result).toBe(false); + resolve(); + } + ); }); }); @@ -246,22 +273,31 @@ describe("Java - Call Static Method", () => { test("java.lang.Long calls (java Long)", async () => { const javaLong = java.newInstanceSync("java.lang.Long", 5); - await new Promise((resolve) => { - java.callStaticMethod("Test", "staticMethodLongToString", javaLong, function (err, result) { - expect(err).toBeFalsy(); - expect(result).toBeTruthy(); - expect(result).toBe("5"); - resolve(); - }); + await new Promise((resolve) => { + java.callStaticMethod( + "Test", + "staticMethodLongToString", + javaLong, + (err: Error | undefined, result: string | undefined) => { + expect(err).toBeFalsy(); + expect(result).toBeTruthy(); + expect(result).toBe("5"); + resolve(); + } + ); }); }); test("Call method that returns a long", () => { - java.callStaticMethod("Test", "staticMethodReturnLong", function (err, result) { - expect(err).toBeFalsy(); - expect(result).toBeTruthy(); - expect(result.longValue).toBe("9223372036854775807"); - }); + java.callStaticMethod( + "Test", + "staticMethodReturnLong", + (err: Error | undefined, result: { longValue: string } | undefined) => { + expect(err).toBeFalsy(); + expect(result).toBeTruthy(); + expect(result?.longValue).toBe("9223372036854775807"); + } + ); }); test("Call method with nested enum value", () => { @@ -282,9 +318,9 @@ describe("Java - Call Static Method", () => { }); test("Call static method named name_", async () => { - await new Promise((resolve) => { + await new Promise((resolve) => { const Test = java.import("Test"); - Test.name_((err) => { + Test.name_((err: Error | undefined) => { expect(err).toBeFalsy(); resolve(); }); diff --git a/test/java-newInstance.test.js b/test/java-newInstance.test.ts similarity index 72% rename from test/java-newInstance.test.js rename to test/java-newInstance.test.ts index 038373e..f0ccece 100644 --- a/test/java-newInstance.test.js +++ b/test/java-newInstance.test.ts @@ -1,12 +1,17 @@ -import { describe, expect, test } from "vitest"; +import { beforeAll, describe, expect, test } from "vitest"; +import { Java, JavaObject } from "../java"; import { getJava } from "../testHelpers"; -const java = getJava(); - describe("Java - New Instance", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(); + }); + test("newInstance", async () => { - await new Promise((resolve) => { - java.newInstance("Test", (err, result) => { + await new Promise((resolve) => { + java.newInstance("Test", (err: Error | undefined, result: JavaObject | undefined) => { expect(err).toBeFalsy(); expect(result).toBeTruthy(); expect(result.getClassSync().toStringSync()).toBe("class Test"); @@ -27,8 +32,8 @@ describe("Java - New Instance", () => { }); test("newInstance with args", async () => { - await new Promise((resolve) => { - java.newInstance("Test", 42, function (err, result) { + await new Promise((resolve) => { + java.newInstance("Test", 42, (err: Error | undefined, result: JavaObject | undefined) => { expect(err).toBeFalsy(); expect(result).toBeTruthy(); expect(result.getIntSync()).toBe(42); @@ -44,8 +49,8 @@ describe("Java - New Instance", () => { }); test("newInstance bad class name", async () => { - await new Promise((resolve) => { - java.newInstance("BadClassName", (err, result) => { + await new Promise((resolve) => { + java.newInstance("BadClassName", (err: Error | undefined, result: JavaObject | undefined) => { expect(err).toBeTruthy(); expect(result).toBeFalsy(); resolve(); @@ -60,8 +65,8 @@ describe("Java - New Instance", () => { }); test("newInstance bad arg types", async () => { - await new Promise((resolve) => { - java.newInstance("Test", "a", function (err, result) { + await new Promise((resolve) => { + java.newInstance("Test", "a", (err: Error | undefined, result: JavaObject | undefined) => { expect(err).toBeTruthy(); expect(result).toBeFalsy(); resolve(); @@ -76,8 +81,8 @@ describe("Java - New Instance", () => { }); test("newInstance bad number of args", async () => { - await new Promise((resolve) => { - java.newInstance("Test", 42, 15, function (err, result) { + await new Promise((resolve) => { + java.newInstance("Test", 42, 15, (err: Error | undefined, result: JavaObject | undefined) => { expect(err).toBeTruthy(); expect(result).toBeFalsy(); resolve(); @@ -93,10 +98,10 @@ describe("Java - New Instance", () => { test("newInstance exception thrown from constructor", async () => { const ex = java.newInstanceSync("java.lang.Exception", "my exception"); - await new Promise((resolve) => { - java.newInstance("TestExceptions", ex, function (err, result) { + await new Promise((resolve) => { + java.newInstance("TestExceptions", ex, (err: Error | undefined, result: JavaObject | undefined) => { expect(err).toBeTruthy(); - expect(err.toString()).toMatch(/my exception/); + expect(err?.toString()).toMatch(/my exception/); expect(result).toBeFalsy(); resolve(); }); diff --git a/test/java-staticField.test.js b/test/java-staticField.test.ts similarity index 85% rename from test/java-staticField.test.js rename to test/java-staticField.test.ts index aff3a4c..cda57ab 100644 --- a/test/java-staticField.test.js +++ b/test/java-staticField.test.ts @@ -1,9 +1,14 @@ -import { afterEach, describe, expect, test } from "vitest"; +import { afterEach, beforeAll, describe, expect, test } from "vitest"; import { getJava } from "../testHelpers"; - -const java = getJava(); +import { Java } from "../java"; describe("Java - Static Field", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(); + }); + afterEach(() => { java.setStaticFieldValue("Test", "staticFieldInt", 42); }); diff --git a/test/javaObject.test.js b/test/javaObject.test.ts similarity index 70% rename from test/javaObject.test.js rename to test/javaObject.test.ts index d3ddd62..c3569f2 100644 --- a/test/javaObject.test.js +++ b/test/javaObject.test.ts @@ -1,12 +1,13 @@ import { beforeEach, describe, expect, test } from "vitest"; import { getJava } from "../testHelpers"; - -const java = getJava(); +import { Java, JavaObject } from "../java"; describe("Java Object", () => { - let testObj; + let testObj: JavaObject; + let java!: Java; - beforeEach(() => { + beforeEach(async () => { + java = await getJava(); testObj = java.newInstanceSync("Test"); }); diff --git a/test/promises.test.js b/test/promises.test.ts similarity index 75% rename from test/promises.test.js rename to test/promises.test.ts index 9bb559a..2f75763 100644 --- a/test/promises.test.js +++ b/test/promises.test.ts @@ -1,12 +1,18 @@ -import { describe, expect, test } from "vitest"; +import { beforeAll, describe, expect, test } from "vitest"; +import { Java } from "../java"; import { getJava } from "../testHelpers"; -const java = getJava(); - describe("Promises", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(); + }); + test("create an instance of a class and call methods (getClassPromise & getNamePromise)", async () => { // Adapted from a test in simple-test.js - const list = await java.newInstancePromise("java.util.ArrayList"); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const list = await (java as any).newInstancePromise("java.util.ArrayList"); expect(list).toBeTruthy(); const clazz = await list.getClassPromise(); @@ -23,7 +29,8 @@ describe("Promises", () => { }); test("run promisified method of Java module (newInstancePromise)", async () => { - const list = await java.newInstancePromise("java.util.ArrayList"); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const list = await (java as any).newInstancePromise("java.util.ArrayList"); expect(list).toBeTruthy(); const clazz = await list.getClassPromise(); @@ -34,7 +41,8 @@ describe("Promises", () => { }); test("run chained promisified methods (of class java.util.ArrayList)", async () => { - const list = await java.newInstancePromise("java.util.ArrayList"); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const list = await (java as any).newInstancePromise("java.util.ArrayList"); expect(list).toBeTruthy(); const clazz = await list.getClassPromise(); @@ -66,7 +74,7 @@ describe("Promises", () => { const more = await it.hasNextPromise(); console.log(typeof more, more); - expect(more).toBeFalsy(false); + expect(more).toBeFalsy(); await expect(async () => await it.nextPromise()).rejects.toThrowError(); }); diff --git a/test/simple.test.js b/test/simple.test.ts similarity index 85% rename from test/simple.test.js rename to test/simple.test.ts index 92aa8d4..be4733c 100644 --- a/test/simple.test.js +++ b/test/simple.test.ts @@ -1,9 +1,14 @@ -import { describe, expect, test } from "vitest"; +import { beforeAll, describe, expect, test } from "vitest"; +import { Java, JavaObject } from "../java"; import { getJava } from "../testHelpers"; -const java = getJava(); - describe("Simple", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(); + }); + test("test classpath commons lang", () => { const result = java.callStaticMethodSync("org.apache.commons.lang3.ObjectUtils", "toString", "test"); console.log("org.apache.commons.lang3.ObjectUtils.toString:", result); @@ -53,22 +58,22 @@ describe("Simple", () => { }); test("test method does not exists (async)", () => { - java.callStaticMethod("java.lang.System", "badMethod", (err) => { + java.callStaticMethod("java.lang.System", "badMethod", (err: Error | undefined) => { if (err) { return; } - test.done(new Error("should throw exception")); + new Error("should throw exception"); }); }); test("create an instance of a class and call methods (getName) (async)", async () => { - await new Promise((resolve) => { - java.newInstance("java.util.ArrayList", (err, list) => { + await new Promise((resolve) => { + java.newInstance("java.util.ArrayList", (err: Error | undefined, list: JavaObject | undefined) => { expect(err).toBeFalsy(); expect(list).toBeTruthy(); - list.getClass((err, result) => { + list.getClass((err: Error | undefined, result: JavaObject | undefined) => { expect(err).toBeFalsy(); - result.getName((err, result) => { + result.getName((err: Error | undefined, result: JavaObject | undefined) => { expect(err).toBeFalsy(); expect(result).toBe("java.util.ArrayList"); resolve(); @@ -78,6 +83,35 @@ describe("Simple", () => { }); }); + test("findClassSync", async () => { + const arrayListClass = java.findClassSync("java.util.ArrayList"); + expect(arrayListClass).toBeTruthy(); + expect(arrayListClass.getNameSync()).toBe("java.util.ArrayList"); + }); + + test("findClassSync not found", async () => { + expect(() => { + java.findClassSync("java.util.MissingClass"); + }).toThrowError(/Could not create class java.util.MissingClass/); + }); + + test("call method", async () => { + const list = java.newInstanceSync("java.util.ArrayList"); + await new Promise((resolve) => { + java.callMethod(list, "add", "test", (err: Error | undefined) => { + expect(err).toBeFalsy(); + expect(list.sizeSync()).toBe(1); + resolve(); + }); + }); + }); + + test("call method (sync)", async () => { + const list = java.newInstanceSync("java.util.ArrayList"); + java.callMethodSync(list, "add", ["test"]); + expect(list.sizeSync()).toBe(1); + }); + test("create an instance of a class and call methods (getName) (sync)", () => { const list = java.newInstanceSync("java.util.ArrayList"); expect(list.sizeSync()).toBe(0); @@ -92,11 +126,11 @@ describe("Simple", () => { }); test("create an instance of a class and call methods (size) (async)", async () => { - await new Promise((resolve) => { - java.newInstance("java.util.ArrayList", (err, list) => { + await new Promise((resolve) => { + java.newInstance("java.util.ArrayList", (err: Error | undefined, list: JavaObject | undefined) => { expect(err).toBeFalsy(); expect(list).toBeTruthy(); - list.size((err, result) => { + list.size((err: Error | undefined, result: number | undefined) => { expect(err).toBeFalsy(); expect(result).toBe(0); resolve(); @@ -169,7 +203,7 @@ describe("Simple", () => { test("method returning an array of longs sync", () => { let arr = java.callStaticMethodSync("Test", "getArrayOfLongs"); - arr = arr.map(function (l) { + arr = arr.map((l: JavaObject) => { return l.toStringSync(); }); expect(arr.length).toBe(5); diff --git a/test/utils-types.test.js b/test/utils-types.test.ts similarity index 93% rename from test/utils-types.test.js rename to test/utils-types.test.ts index 7ad40d7..0cb38d2 100644 --- a/test/utils-types.test.js +++ b/test/utils-types.test.ts @@ -1,9 +1,14 @@ -import { describe, expect, test } from "vitest"; +import { beforeAll, describe, expect, test } from "vitest"; +import { Java } from "../java"; import { getJava } from "../testHelpers"; -const java = getJava(); - describe("Utils - Types", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(); + }); + test("Array of Objects", () => { let val = java.getStaticFieldValue("Test", "staticArrayObjects"); expect(val).toBeNull(); diff --git a/test/varargs.test.js b/test/varargs.test.ts similarity index 95% rename from test/varargs.test.js rename to test/varargs.test.ts index cd5fe75..991229e 100644 --- a/test/varargs.test.js +++ b/test/varargs.test.ts @@ -1,9 +1,14 @@ -import { describe, expect, test } from "vitest"; +import { beforeAll, describe, expect, test } from "vitest"; import { getJava } from "../testHelpers"; - -const java = getJava(); +import { Java } from "../java"; describe("varargs", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(); + }); + test("array signature inference", () => { const Test = java.import("Test"); expect(Test.varArgsSignatureSync([])).toBe("Object..."); diff --git a/testAsyncOptions/allThreeSuffix.test.js b/testAsyncOptions/allThreeSuffix.test.ts similarity index 82% rename from testAsyncOptions/allThreeSuffix.test.js rename to testAsyncOptions/allThreeSuffix.test.ts index d39c74a..5765076 100644 --- a/testAsyncOptions/allThreeSuffix.test.js +++ b/testAsyncOptions/allThreeSuffix.test.ts @@ -1,16 +1,22 @@ // All three variants have non-empty suffix, i.e a suffix is required for any variant. +import { Java } from "../java"; import { getJava } from "../testHelpers"; -import { describe, test, expect } from "vitest"; - -const java = getJava({ - syncSuffix: "Sync", - asyncSuffix: "Async", - promiseSuffix: "Promise", - promisify: require("when/node").lift, // https://github.com/cujojs/when -}); +import { describe, test, expect, beforeAll } from "vitest"; describe("allThreeSuffix", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava({ + syncSuffix: "Sync", + asyncSuffix: "Async", + promiseSuffix: "Promise", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when + }); + }); + test("api", () => { const arrayList = java.newInstanceSync("java.util.ArrayList"); expect(arrayList).toBeDefined(); @@ -62,12 +68,12 @@ describe("allThreeSuffix", () => { test("asyncCalls", async () => { const arrayList = java.newInstanceSync("java.util.ArrayList"); - await new Promise((resolve) => { - arrayList.addAsync("hello", function (err) { + await new Promise((resolve) => { + arrayList.addAsync("hello", (err: Error | undefined) => { expect(err).toBeUndefined(); - arrayList.addAsync("world", function (err) { + arrayList.addAsync("world", (err: Error | undefined) => { expect(err).toBeUndefined(); - arrayList.sizeAsync(function (err, size) { + arrayList.sizeAsync((err: Error | undefined, size: number) => { expect(err).toBeUndefined(); expect(size).toBe(2); resolve(); @@ -87,7 +93,7 @@ describe("allThreeSuffix", () => { .then(() => { return arrayList.sizePromise(); }) - .then((size) => { + .then((size: number) => { expect(size).toBe(2); }); }); diff --git a/testAsyncOptions/asyncSuffixSyncDefault.test.js b/testAsyncOptions/asyncSuffixSyncDefault.test.ts similarity index 76% rename from testAsyncOptions/asyncSuffixSyncDefault.test.js rename to testAsyncOptions/asyncSuffixSyncDefault.test.ts index 9618799..6504c1d 100644 --- a/testAsyncOptions/asyncSuffixSyncDefault.test.js +++ b/testAsyncOptions/asyncSuffixSyncDefault.test.ts @@ -1,36 +1,52 @@ // Use "Async" for the asyncSuffix, and "" for the syncSuffix. import { beforeAll, describe, expect, test } from "vitest"; -import { java } from "../testHelpers"; +import { getJava } from "../testHelpers"; +import { Java } from "../java"; describe("asyncSuffixSyncDefault", () => { + let java!: Java; + beforeAll(async () => { - await new Promise((resolve) => { - java.asyncOptions = { + let beforeCalled = false; + let afterCalled = false; + + java = await getJava( + { syncSuffix: "", asyncSuffix: "Async", ifReadOnlySuffix: "_alt", - }; - - function before(callback) { - java.classpath.push("test/"); - expect(java.isJvmCreated()).toBeFalsy(); - callback(); - } - - function after(callback) { - expect(java.isJvmCreated()).toBeTruthy(); - callback(); + }, + { + beforeInit: async (java) => { + function before(callback: () => void): void { + beforeCalled = true; + java.classpath.push("test/"); + expect(java.isJvmCreated()).toBeFalsy(); + callback(); + } + + function after(callback: () => void): void { + afterCalled = true; + expect(java.isJvmCreated()).toBeTruthy(); + callback(); + } + + java.registerClient(before, after); + + await new Promise((resolve) => { + java.ensureJvm((err) => { + expect(err).toBeFalsy(); + expect(java.isJvmCreated()).toBeTruthy(); + resolve(); + }); + }); + }, } + ); - java.registerClient(before, after); - - java.ensureJvm(function (err) { - expect(err).toBeNull(); - expect(java.isJvmCreated()).toBeTruthy(); - resolve(); - }); - }); + expect(beforeCalled).toBeTruthy(); + expect(afterCalled).toBeTruthy(); }); test("api", () => { @@ -83,12 +99,12 @@ describe("asyncSuffixSyncDefault", () => { test("asyncCalls", async () => { const arrayList = java.newInstanceSync("java.util.ArrayList"); - await new Promise((resolve) => { - arrayList.addAsync("hello", function (err) { + await new Promise((resolve) => { + arrayList.addAsync("hello", (err: Error | undefined) => { expect(err).toBeUndefined(); - arrayList.addAsync("world", function (err) { + arrayList.addAsync("world", (err: Error | undefined) => { expect(err).toBeUndefined(); - arrayList.sizeAsync(function (err, size) { + arrayList.sizeAsync((err: Error | undefined, size: number | undefined) => { expect(err).toBeUndefined(); expect(size).toBe(2); resolve(); diff --git a/testAsyncOptions/clientBeforeError.test.js b/testAsyncOptions/clientBeforeError.test.js deleted file mode 100644 index 7c6980c..0000000 --- a/testAsyncOptions/clientBeforeError.test.js +++ /dev/null @@ -1,29 +0,0 @@ -import { describe, expect, test } from "vitest"; -import { java } from "../testHelpers"; - -describe("clientBeforeError", () => { - test("clientBeforeError", async () => { - await new Promise((resolve) => { - expect(java.isJvmCreated()).toBeFalsy(); - - java.asyncOptions = { - syncSuffix: "Sync", - }; - - function before(callback) { - expect(java.isJvmCreated()).toBeFalsy(); - callback(new Error("dummy error")); - } - - java.registerClient(before); - - java.ensureJvm(function (err) { - expect(err && typeof err === "object").toBeTruthy(); - expect(err).toBeInstanceOf(Error); - expect(err.message).toBe("dummy error"); - expect(java.isJvmCreated()).toBeFalsy(); - resolve(); - }); - }); - }); -}); diff --git a/testAsyncOptions/clientBeforeError.test.ts b/testAsyncOptions/clientBeforeError.test.ts new file mode 100644 index 0000000..187e13a --- /dev/null +++ b/testAsyncOptions/clientBeforeError.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, test } from "vitest"; +import { getJava } from "../testHelpers"; + +describe("clientBeforeError", () => { + test("clientBeforeError", async () => { + await getJava( + { + syncSuffix: "Sync", + }, + { + beforeInit: async (java) => { + expect(java.isJvmCreated()).toBeFalsy(); + + function before(callback: (err: Error) => void): void { + expect(java.isJvmCreated()).toBeFalsy(); + callback(new Error("dummy error")); + } + + java.registerClient(before); + + await new Promise((resolve) => { + java.ensureJvm((err) => { + expect(err).toBeTruthy(); + expect(typeof err).toBe("object"); + expect(err).toBeInstanceOf(Error); + expect(err?.message).toBe("dummy error"); + expect(java.isJvmCreated()).toBeFalsy(); + resolve(); + }); + }); + }, + } + ); + }); +}); diff --git a/testAsyncOptions/clientBeforeSyncThrows.test.js b/testAsyncOptions/clientBeforeSyncThrows.test.js deleted file mode 100644 index 19fc62e..0000000 --- a/testAsyncOptions/clientBeforeSyncThrows.test.js +++ /dev/null @@ -1,29 +0,0 @@ -import { describe, expect, test } from "vitest"; -import { java } from "../testHelpers"; - -describe("clientBeforeSyncThrows", () => { - test("clientBeforeSyncThrows", async () => { - await new Promise((resolve) => { - expect(java.isJvmCreated()).toBeFalsy(); - - java.asyncOptions = { - syncSuffix: "Sync", - }; - - function before() { - expect(java.isJvmCreated()).toBeFalsy(); - throw new Error("dummy error"); - } - - java.registerClient(before); - - java.ensureJvm(function (err) { - expect(err && typeof err === "object").toBeTruthy(); - expect(err).instanceOf(Error); - expect(err.message).toBe("dummy error"); - expect(java.isJvmCreated()).toBeFalsy(); - resolve(); - }); - }); - }); -}); diff --git a/testAsyncOptions/clientBeforeSyncThrows.test.ts b/testAsyncOptions/clientBeforeSyncThrows.test.ts new file mode 100644 index 0000000..5277fc1 --- /dev/null +++ b/testAsyncOptions/clientBeforeSyncThrows.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, test } from "vitest"; +import { getJava } from "../testHelpers"; + +describe("clientBeforeSyncThrows", () => { + test("clientBeforeSyncThrows", async () => { + await getJava( + { + syncSuffix: "Sync", + }, + { + beforeInit: async (java) => { + expect(java.isJvmCreated()).toBeFalsy(); + + function before(): void { + expect(java.isJvmCreated()).toBeFalsy(); + throw new Error("dummy error"); + } + java.registerClient(before); + + await new Promise((resolve) => { + java.ensureJvm((err: Error | undefined) => { + expect(err && typeof err === "object").toBeTruthy(); + expect(err).instanceOf(Error); + expect(err?.message).toBe("dummy error"); + expect(java.isJvmCreated()).toBeFalsy(); + resolve(); + }); + }); + }, + } + ); + }); +}); diff --git a/testAsyncOptions/clientBeforeThrows.test.js b/testAsyncOptions/clientBeforeThrows.test.js deleted file mode 100644 index 7cf6e6e..0000000 --- a/testAsyncOptions/clientBeforeThrows.test.js +++ /dev/null @@ -1,29 +0,0 @@ -import { describe, expect, test } from "vitest"; -import { java } from "../testHelpers"; - -describe("clientBeforeThrows", () => { - test("clientBeforeThrows", async () => { - await new Promise((resolve) => { - expect(java.isJvmCreated()).toBeFalsy(); - - java.asyncOptions = { - syncSuffix: "Sync", - }; - - function before() { - expect(java.isJvmCreated()).toBeFalsy(); - throw new Error("dummy error"); - } - - java.registerClient(before); - - java.ensureJvm(function (err) { - expect(err && typeof err === "object").toBeTruthy(); - expect(err).instanceOf(Error); - expect(err.message).toBe("dummy error"); - expect(java.isJvmCreated()).toBeFalsy(); - resolve(); - }); - }); - }); -}); diff --git a/testAsyncOptions/clientBeforeThrows.test.ts b/testAsyncOptions/clientBeforeThrows.test.ts new file mode 100644 index 0000000..ef493fa --- /dev/null +++ b/testAsyncOptions/clientBeforeThrows.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, test } from "vitest"; +import { getJava } from "../testHelpers"; + +describe("clientBeforeThrows", () => { + test("clientBeforeThrows", async () => { + await getJava( + { + syncSuffix: "Sync", + }, + { + beforeInit: async (java) => { + expect(java.isJvmCreated()).toBeFalsy(); + + function before(): void { + expect(java.isJvmCreated()).toBeFalsy(); + throw new Error("dummy error"); + } + java.registerClient(before); + + await new Promise((resolve) => { + java.ensureJvm((err: Error | undefined) => { + expect(err && typeof err === "object").toBeTruthy(); + expect(err).instanceOf(Error); + expect(err?.message).toBe("dummy error"); + expect(java.isJvmCreated()).toBeFalsy(); + resolve(); + }); + }); + }, + } + ); + }); +}); diff --git a/testAsyncOptions/clientPBeforeError.test.js b/testAsyncOptions/clientPBeforeError.test.js deleted file mode 100644 index 032ce23..0000000 --- a/testAsyncOptions/clientPBeforeError.test.js +++ /dev/null @@ -1,40 +0,0 @@ -import { describe, expect, test } from "vitest"; -import when from "when"; -import { java } from "../testHelpers"; - -describe("clientPBeforeError", () => { - test("clientPBeforeError", async () => { - await new Promise((resolve) => { - expect(java.isJvmCreated()).toBeFalsy(); - - java.asyncOptions = { - syncSuffix: "Sync", - promiseSuffix: "Promise", - promisify: require("when/node").lift, // https://github.com/cujojs/when - }; - - function beforeP() { - const promise = when.promise((_resolve, reject) => { - expect(java.isJvmCreated()).toBeFalsy(); - reject(new Error("dummy error")); - }); - return promise; - } - - java.registerClientP(beforeP); - - java.ensureJvm().done( - () => { - throw new Error("expect error"); - }, - (err) => { - expect(err && typeof err === "object").toBeTruthy(); - expect(err).instanceOf(Error); - expect(err.message).toBe("dummy error"); - expect(java.isJvmCreated()).toBeFalsy(); - resolve(); - } - ); - }); - }); -}); diff --git a/testAsyncOptions/clientPBeforeError.test.ts b/testAsyncOptions/clientPBeforeError.test.ts new file mode 100644 index 0000000..cd6077f --- /dev/null +++ b/testAsyncOptions/clientPBeforeError.test.ts @@ -0,0 +1,43 @@ +import { describe, expect, test } from "vitest"; +import { getJava } from "../testHelpers"; + +describe("clientPBeforeError", () => { + test("clientPBeforeError", async () => { + await getJava( + { + syncSuffix: "Sync", + promiseSuffix: "Promise", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when + }, + { + beforeInit: async (java) => { + expect(java.isJvmCreated()).toBeFalsy(); + + function beforeP(): Promise { + return new Promise((_resolve, reject) => { + expect(java.isJvmCreated()).toBeFalsy(); + reject(new Error("dummy error")); + }); + } + java.registerClientP(beforeP); + + await new Promise((resolve) => { + java.ensureJvm().then( + () => { + throw new Error("expect error"); + }, + (err: Error | undefined) => { + expect(err && typeof err === "object").toBeTruthy(); + expect(err).instanceOf(Error); + expect(err?.message).toBe("dummy error"); + expect(java.isJvmCreated()).toBeFalsy(); + resolve(); + } + ); + }); + }, + } + ); + }); +}); diff --git a/testAsyncOptions/clientPBeforeThrows.test.js b/testAsyncOptions/clientPBeforeThrows.test.js deleted file mode 100644 index 86e45e6..0000000 --- a/testAsyncOptions/clientPBeforeThrows.test.js +++ /dev/null @@ -1,40 +0,0 @@ -import { describe, expect, test } from "vitest"; -import when from "when"; -import { java } from "../testHelpers"; - -describe("clientPBeforeThrows", () => { - test("clientPBeforeThrows", async () => { - await new Promise((resolve) => { - expect(java.isJvmCreated()).toBeFalsy(); - - java.asyncOptions = { - syncSuffix: "Sync", - promiseSuffix: "Promise", - promisify: require("when/node").lift, // https://github.com/cujojs/when - }; - - function beforeP() { - const promise = when.promise(() => { - expect(java.isJvmCreated()).toBeFalsy(); - throw new Error("dummy error"); - }); - return promise; - } - - java.registerClientP(beforeP); - - java.ensureJvm().done( - () => { - throw new Error("expected error"); - }, - (err) => { - expect(err && typeof err === "object").toBeTruthy(); - expect(err).instanceOf(Error); - expect(err.message).toBe("dummy error"); - expect(java.isJvmCreated()).toBeFalsy(); - resolve(); - } - ); - }); - }); -}); diff --git a/testAsyncOptions/clientPBeforeThrows.test.ts b/testAsyncOptions/clientPBeforeThrows.test.ts new file mode 100644 index 0000000..901a65d --- /dev/null +++ b/testAsyncOptions/clientPBeforeThrows.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, test } from "vitest"; +import { getJava } from "../testHelpers"; + +describe("clientPBeforeThrows", () => { + test("clientPBeforeThrows", async () => { + await getJava( + { + syncSuffix: "Sync", + promiseSuffix: "Promise", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when + }, + { + beforeInit: async (java) => { + expect(java.isJvmCreated()).toBeFalsy(); + + function beforeP(): Promise { + return new Promise(() => { + expect(java.isJvmCreated()).toBeFalsy(); + throw new Error("dummy error"); + }); + } + + java.registerClientP(beforeP); + + await new Promise((resolve) => { + java.ensureJvm().then( + () => { + throw new Error("expected error"); + }, + (err: Error | undefined) => { + expect(err && typeof err === "object").toBeTruthy(); + expect(err).instanceOf(Error); + expect(err?.message).toBe("dummy error"); + expect(java.isJvmCreated()).toBeFalsy(); + resolve(); + } + ); + }); + }, + } + ); + }); +}); diff --git a/testAsyncOptions/defacto.test.js b/testAsyncOptions/defacto.test.ts similarity index 69% rename from testAsyncOptions/defacto.test.js rename to testAsyncOptions/defacto.test.ts index ec4cd66..e9fc148 100644 --- a/testAsyncOptions/defacto.test.js +++ b/testAsyncOptions/defacto.test.ts @@ -1,43 +1,49 @@ // In the defacto case, the developer sets asyncOptions, but specifies the defacto standard behavior. import { beforeAll, describe, expect, test } from "vitest"; -import { java } from "../testHelpers"; +import { Java } from "../java"; +import { getJava } from "../testHelpers"; describe("defacto", () => { - beforeAll(async () => { - await new Promise((resolve) => { - const api = Object.keys(java).filter((key) => typeof java[key] === "function"); - expect(api.includes("isJvmCreated"), "Expected `isJvmCreated` to be present, but it is NOT.").toBeTruthy(); - expect(java.isJvmCreated()).toBeFalsy(); + let java!: Java; - java.asyncOptions = { + beforeAll(async () => { + java = await getJava( + { syncSuffix: "Sync", asyncSuffix: "", - }; - - function before() { - expect(java.isJvmCreated()).toBeFalsy(); - } - - function after() { - expect(java.isJvmCreated()).toBeTruthy(); + }, + { + beforeInit: async (java) => { + expect(java.isJvmCreated()).toBeFalsy(); + + function before(): void { + expect(java.isJvmCreated()).toBeFalsy(); + } + + function after(): void { + expect(java.isJvmCreated()).toBeTruthy(); + } + + java.registerClient(before, after); + java.registerClient(undefined, after); + java.registerClient(before, undefined); + + await new Promise((resolve) => { + java.ensureJvm(function (err) { + expect(err).toBeFalsy(); + expect(java.isJvmCreated()).toBeTruthy(); + + // Verify that ensureJvm is idempotent + java.ensureJvm(function (err) { + expect(err).toBeFalsy(); + resolve(); + }); + }); + }); + }, } - - java.registerClient(before, after); - java.registerClient(undefined, after); - java.registerClient(before, undefined); - - java.ensureJvm(function (err) { - expect(err).toBeFalsy(); - expect(java.isJvmCreated()).toBeTruthy(); - - // Verify that ensureJvm is idempotent - java.ensureJvm(function (err) { - expect(err).toBeFalsy(); - resolve(); - }); - }); - }); + ); }); test("api", () => { @@ -89,13 +95,13 @@ describe("defacto", () => { }); test("asyncCalls", async () => { - await new Promise((resolve) => { + await new Promise((resolve) => { const arrayList = java.newInstanceSync("java.util.ArrayList"); - arrayList.add("hello", function (err) { + arrayList.add("hello", (err: Error | undefined) => { expect(err).toBeFalsy(); - arrayList.add("world", function (err) { + arrayList.add("world", (err: Error | undefined) => { expect(err).toBeFalsy(); - arrayList.size(function (err, size) { + arrayList.size((err: Error | undefined, size: number | undefined) => { expect(err).toBeFalsy(); expect(size).toBe(2); resolve(); diff --git a/testAsyncOptions/defactoPlusPromise.test.js b/testAsyncOptions/defactoPlusPromise.test.ts similarity index 72% rename from testAsyncOptions/defactoPlusPromise.test.js rename to testAsyncOptions/defactoPlusPromise.test.ts index 07b0cbc..3149f5a 100644 --- a/testAsyncOptions/defactoPlusPromise.test.js +++ b/testAsyncOptions/defactoPlusPromise.test.ts @@ -1,41 +1,44 @@ // The defacto case but with promises also enabled. import { beforeAll, describe, expect, test } from "vitest"; -import { java } from "../testHelpers"; +import { Java } from "../java"; +import { getJava } from "../testHelpers"; describe("defactoPlusPromise", () => { - beforeAll(async () => { - await new Promise((resolve) => { - const api = Object.keys(java).filter((key) => typeof java[key] === "function"); - expect(api.includes("isJvmCreated"), "Expected `isJvmCreated` to be present, but it is NOT.").toBeTruthy(); - expect(java.isJvmCreated()).toBeFalsy(); + let java!: Java; - java.asyncOptions = { + beforeAll(async () => { + java = await getJava( + { syncSuffix: "Sync", asyncSuffix: "", promiseSuffix: "Promise", - promisify: require("when/node").lift, // https://github.com/cujojs/when - }; - - function before(callback) { - expect(java.isJvmCreated()).toBeFalsy(); - callback(); - } - - function after(callback) { - expect(java.isJvmCreated()).toBeTruthy(); - callback(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when + }, + { + beforeInit: (java) => { + expect(java.isJvmCreated()).toBeFalsy(); + + function before(callback: () => void): void { + expect(java.isJvmCreated()).toBeFalsy(); + callback(); + } + + function after(callback: () => void): void { + expect(java.isJvmCreated()).toBeTruthy(); + callback(); + } + + java.registerClient(before, after); + java.registerClient(null, after); + java.registerClient(before); + }, } + ); - java.registerClient(before, after); - java.registerClient(null, after); - java.registerClient(before); - - java.ensureJvm().done(function () { - expect(java.isJvmCreated()).toBeTruthy(); - resolve(); - }); - }); + await java.ensureJvm(); + expect(java.isJvmCreated()).toBeTruthy(); }); test("api", () => { @@ -84,13 +87,13 @@ describe("defactoPlusPromise", () => { }); test("asyncCalls", async () => { - await new Promise((resolve) => { + await new Promise((resolve) => { const arrayList = java.newInstanceSync("java.util.ArrayList"); - arrayList.add("hello", function (err) { + arrayList.add("hello", (err: Error | undefined) => { expect(err).toBeFalsy(); - arrayList.add("world", function (err) { + arrayList.add("world", (err: Error | undefined) => { expect(err).toBeFalsy(); - arrayList.size(function (err, size) { + arrayList.size((err: Error | undefined, size: number | undefined) => { expect(err).toBeFalsy(); expect(size).toBe(2); resolve(); @@ -101,7 +104,7 @@ describe("defactoPlusPromise", () => { }); test("promiseCalls", async () => { - await new Promise((resolve) => { + await new Promise((resolve) => { const arrayList = java.newInstanceSync("java.util.ArrayList"); arrayList .addPromise("hello") @@ -111,7 +114,7 @@ describe("defactoPlusPromise", () => { .then(() => { return arrayList.sizePromise(); }) - .then((size) => { + .then((size: number) => { expect(size).toBe(2); resolve(); }); diff --git a/testAsyncOptions/default.test.js b/testAsyncOptions/default.test.ts similarity index 84% rename from testAsyncOptions/default.test.js rename to testAsyncOptions/default.test.ts index b322e44..ab7766d 100644 --- a/testAsyncOptions/default.test.js +++ b/testAsyncOptions/default.test.ts @@ -1,10 +1,21 @@ // In the default case, the developer does not set asyncOptions. // We should get the defacto standard behavior. -import { describe, expect, test } from "vitest"; -import { java } from "../testHelpers"; +import { afterAll, beforeAll, describe, expect, test } from "vitest"; +import { Java } from "../java"; +import { getJava } from "../testHelpers"; describe("default", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava(null); + }); + + afterAll(() => { + java.stop(); + }); + test("api", () => { const arrayList = java.newInstanceSync("java.util.ArrayList"); expect(arrayList).toBeTruthy(); @@ -54,13 +65,13 @@ describe("default", () => { }); test("asyncCalls", async () => { - await new Promise((resolve) => { + await new Promise((resolve) => { const arrayList = java.newInstanceSync("java.util.ArrayList"); - arrayList.add("hello", function (err) { + arrayList.add("hello", (err: Error | undefined) => { expect(err).toBeFalsy(); - arrayList.add("world", function (err) { + arrayList.add("world", (err: Error | undefined) => { expect(err).toBeFalsy(); - arrayList.size(function (err, size) { + arrayList.size((err: Error | undefined, size: number | undefined) => { expect(err).toBeFalsy(); expect(size).toBe(2); resolve(); diff --git a/testAsyncOptions/invalidLaunch.test.js b/testAsyncOptions/invalidLaunch.test.js deleted file mode 100644 index 10b4f7b..0000000 --- a/testAsyncOptions/invalidLaunch.test.js +++ /dev/null @@ -1,55 +0,0 @@ -import { describe, expect, test } from "vitest"; -import { java } from "../testHelpers"; - -describe("invalidLaunch", () => { - test("failedLaunch", () => { - expect(java.isJvmCreated()).toBeFalsy(); - - java.asyncOptions = { - syncSuffix: "Sync", - asyncSuffix: "", - }; - - // First show that if asyncOptions.promisify is undefined, using the promise variant of ensureJvm throws an error. - expect(() => { - java.ensureJvm(); - }).toThrow(/requires its one argument to be a callback function/); - - expect(java.isJvmCreated()).toBeFalsy(); - }); - - test("callbackNotAFunction", () => { - expect(java.isJvmCreated()).toBeFalsy(); - - java.asyncOptions = { - syncSuffix: "", - promiseSuffix: "P", - promisify: require("when/node").lift, // https://github.com/cujojs/when - }; - - expect(() => { - java.ensureJvm("foo"); - }).toThrow(/requires its one argument to be a callback function/); - - expect(java.isJvmCreated()).toBeFalsy(); - }); - - test("jvmCanStillBeLaunched", async () => { - // None of the previous tests should have caused the JVM to be created, so it should still be possible to create one. - - expect(java.isJvmCreated()).toBeFalsy(); - - java.asyncOptions = { - syncSuffix: "", - promiseSuffix: "P", - promisify: require("when/node").lift, // https://github.com/cujojs/when - }; - - await new Promise((resolve) => { - java.ensureJvm().done(function () { - expect(java.isJvmCreated()).toBeTruthy(); - resolve(); - }); - }); - }); -}); diff --git a/testAsyncOptions/invalidLaunch.test.ts b/testAsyncOptions/invalidLaunch.test.ts new file mode 100644 index 0000000..7de9f02 --- /dev/null +++ b/testAsyncOptions/invalidLaunch.test.ts @@ -0,0 +1,67 @@ +import { describe, expect, test } from "vitest"; +import { getJava } from "../testHelpers"; + +describe("invalidLaunch", () => { + test("calling ensureJvm as a promise when no promisify function supplied", async () => { + await getJava( + { + syncSuffix: "Sync", + asyncSuffix: "", + }, + { + beforeInit: (java) => { + expect(java.isJvmCreated()).toBeFalsy(); + + // First show that if asyncOptions.promisify is undefined, using the promise variant of ensureJvm throws an error. + expect(() => { + void java.ensureJvm(); + }).toThrow(/requires its one argument to be a callback function/); + + expect(java.isJvmCreated()).toBeFalsy(); + }, + } + ); + }); + + test("callbackNotAFunction", async () => { + await getJava( + { + syncSuffix: "", + promiseSuffix: "P", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when + }, + { + beforeInit: (java) => { + expect(java.isJvmCreated()).toBeFalsy(); + + expect(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (java as any).ensureJvm("foo"); + }).toThrow(/requires its one argument to be a callback function/); + + expect(java.isJvmCreated()).toBeFalsy(); + }, + } + ); + }); + + test("jvmCanStillBeLaunched", async () => { + await getJava( + { + syncSuffix: "", + promiseSuffix: "P", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when + }, + { + beforeInit: async (java) => { + expect(java.isJvmCreated()).toBeFalsy(); + + await java.ensureJvm(); + expect(java.isJvmCreated()).toBeTruthy(); + }, + } + ); + }); +}); diff --git a/testAsyncOptions/noAsync.test.js b/testAsyncOptions/noAsync.test.ts similarity index 72% rename from testAsyncOptions/noAsync.test.js rename to testAsyncOptions/noAsync.test.ts index 2af3f84..c4d434f 100644 --- a/testAsyncOptions/noAsync.test.js +++ b/testAsyncOptions/noAsync.test.ts @@ -1,46 +1,47 @@ // Just Sync and Promise, both with a non-empty suffix. import { beforeAll, describe, expect, test } from "vitest"; -import when from "when"; -import { java } from "../testHelpers"; +import { Java } from "../java"; +import { getJava } from "../testHelpers"; describe("noAsync", () => { + let java!: Java; + beforeAll(async () => { - const api = Object.keys(java).filter((key) => typeof java[key] === "function"); - expect(api.includes("isJvmCreated"), "Expected `isJvmCreated` to be present, but it is NOT.").toBeTruthy(); - expect(java.isJvmCreated()).toBeFalsy(); + java = await getJava( + { + syncSuffix: "Sync", + promiseSuffix: "Promise", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + promisify: ((await import("when/node")) as any).lift, + }, + { + beforeInit: (java) => { + expect(java.isJvmCreated()).toBeFalsy(); - function before() { - const promise = when.promise(function (resolve) { - expect(java.isJvmCreated()).toBeFalsy(); - resolve(); - }); - return promise; - } + function beforeP(): Promise { + return new Promise((resolve) => { + expect(java.isJvmCreated()).toBeFalsy(); + resolve(); + }); + } - function after() { - const promise = when.promise(function (resolve) { - expect(java.isJvmCreated()).toBeTruthy(); - resolve(); - }); - return promise; - } + function afterP(): Promise { + return new Promise((resolve) => { + expect(java.isJvmCreated()).toBeTruthy(); + resolve(); + }); + } - java.asyncOptions = { - syncSuffix: "Sync", - promiseSuffix: "Promise", - promisify: require("when/node").lift, - }; - java.registerClientP(before, after); - java.registerClientP(null, after); - java.registerClientP(before); + java.registerClientP(beforeP, afterP); + java.registerClientP(null, afterP); + java.registerClientP(beforeP); + }, + } + ); - await new Promise((resolve) => { - java.ensureJvm().done(function () { - expect(java.isJvmCreated()).toBeTruthy(); - resolve(); - }); - }); + await java.ensureJvm(); + expect(java.isJvmCreated()).toBeTruthy(); }); test("api", () => { @@ -94,7 +95,7 @@ describe("noAsync", () => { test("promiseCalls", async () => { const arrayList = java.newInstanceSync("java.util.ArrayList"); - await new Promise((resolve) => { + await new Promise((resolve) => { arrayList .addPromise("hello") .then(() => { @@ -103,7 +104,7 @@ describe("noAsync", () => { .then(() => { return arrayList.sizePromise(); }) - .then((size) => { + .then((size: number) => { expect(size).toBe(2); resolve(); }); diff --git a/testAsyncOptions/syncDefaultPlusPromise.test.js b/testAsyncOptions/syncDefaultPlusPromise.test.ts similarity index 86% rename from testAsyncOptions/syncDefaultPlusPromise.test.js rename to testAsyncOptions/syncDefaultPlusPromise.test.ts index 7fe9403..5a4d222 100644 --- a/testAsyncOptions/syncDefaultPlusPromise.test.js +++ b/testAsyncOptions/syncDefaultPlusPromise.test.ts @@ -1,16 +1,22 @@ // Just Sync and Promise, with Sync the default (i.e. no suffix). // This is the configuration that RedSeal wants for use with Tinkerpop/Gremlin. -import { describe, expect, test } from "vitest"; +import { beforeAll, describe, expect, test } from "vitest"; import { getJava } from "../testHelpers"; - -const java = getJava({ - syncSuffix: "", - promiseSuffix: "P", - promisify: require("when/node").lift, // https://github.com/cujojs/when -}); +import { Java } from "../java"; describe("syncDefaultPlusPromise", () => { + let java!: Java; + + beforeAll(async () => { + java = await getJava({ + syncSuffix: "", + promiseSuffix: "P", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when + }); + }); + test("api", () => { const arrayList = java.newInstanceSync("java.util.ArrayList"); expect(arrayList).toBeTruthy(); @@ -60,7 +66,7 @@ describe("syncDefaultPlusPromise", () => { test("promiseCalls", async () => { const arrayList = java.newInstanceSync("java.util.ArrayList"); - await new Promise((resolve) => { + await new Promise((resolve) => { arrayList .addP("hello") .then(() => { @@ -69,7 +75,7 @@ describe("syncDefaultPlusPromise", () => { .then(() => { return arrayList.sizeP(); }) - .then((size) => { + .then((size: number) => { expect(size).toBe(2); resolve(); }); diff --git a/testAsyncOptions/unusableMethodName.test.js b/testAsyncOptions/unusableMethodName.test.ts similarity index 71% rename from testAsyncOptions/unusableMethodName.test.js rename to testAsyncOptions/unusableMethodName.test.ts index e1670e4..66232ba 100644 --- a/testAsyncOptions/unusableMethodName.test.js +++ b/testAsyncOptions/unusableMethodName.test.ts @@ -6,30 +6,39 @@ // As a workaround, node-java will append the `ifReadOnlySuffix` to the property name. import { beforeAll, describe, expect, test } from "vitest"; -import { java } from "../testHelpers"; +import { getJava } from "../testHelpers"; +import { Java } from "../java"; describe("unusableMethodName", () => { - beforeAll(async () => { - await new Promise((resolve) => { - function before(callback) { - java.classpath.push("test/"); - expect(java.isJvmCreated()).toBeFalsy(); - callback(); - } - - function after(callback) { - expect(java.isJvmCreated()).toBeTruthy(); - callback(); - } + let java!: Java; - java.asyncOptions = { + beforeAll(async () => { + java = await getJava( + { syncSuffix: "Sync", asyncSuffix: "", ifReadOnlySuffix: "_alt", - }; - java.registerClient(before, after); + }, + { + beforeInit: (java) => { + function before(callback: () => void): void { + java.classpath.push("test/"); + expect(java.isJvmCreated()).toBeFalsy(); + callback(); + } + + function after(callback: () => void): void { + expect(java.isJvmCreated()).toBeTruthy(); + callback(); + } + + java.registerClient(before, after); + }, + } + ); - java.ensureJvm(function (err) { + await new Promise((resolve) => { + java.ensureJvm(function (err: Error | undefined) { expect(err).toBeFalsy(); expect(java.isJvmCreated()).toBeTruthy(); resolve(); @@ -41,7 +50,7 @@ describe("unusableMethodName", () => { const Test = java.import("Test"); expect(Test).toBeTruthy(); expect(() => { - Test.name(function (_err) { + Test.name((_err: Error | undefined) => { throw new Error("should not get here"); }); }).toThrowError(TypeError); @@ -51,7 +60,7 @@ describe("unusableMethodName", () => { const Test = java.import("Test"); expect(Test).toBeTruthy(); expect(() => { - Test.caller(function (_err) { + Test.caller((_err: Error | undefined) => { throw new Error("should not get here"); }); }).toThrowError(TypeError); @@ -61,7 +70,7 @@ describe("unusableMethodName", () => { const Test = java.import("Test"); expect(Test).toBeTruthy(); expect(() => { - Test.arguments(function (_err) { + Test.arguments((_err: Error | undefined) => { throw new Error("should not get here"); }); }).toThrowError(TypeError); @@ -70,8 +79,8 @@ describe("unusableMethodName", () => { test("alternateMethodName_name_altWorks", async () => { const Test = java.import("Test"); expect(Test).toBeTruthy(); - await new Promise((resolve) => { - Test.name_alt(function (err, val) { + await new Promise((resolve) => { + Test.name_alt((err: Error | undefined, val: string | undefined) => { expect(err).toBeFalsy(); expect(val).toBe("name"); resolve(); @@ -82,8 +91,8 @@ describe("unusableMethodName", () => { test("alternateMethodName_caller_altWorks", async () => { const Test = java.import("Test"); expect(Test).toBeTruthy(); - await new Promise((resolve) => { - Test.caller_alt(function (err, val) { + await new Promise((resolve) => { + Test.caller_alt((err: Error | undefined, val: string | undefined) => { expect(err).toBeFalsy(); expect(val).toBe("caller"); resolve(); @@ -94,8 +103,8 @@ describe("unusableMethodName", () => { test("alternateMethodName_arguments_altWorks", async () => { const Test = java.import("Test"); expect(Test).toBeTruthy(); - await new Promise((resolve) => { - Test.arguments_alt(function (err, val) { + await new Promise((resolve) => { + Test.arguments_alt((err: Error | undefined, val: string | undefined) => { expect(err).toBeFalsy(); expect(val).toBe("arguments"); resolve(); diff --git a/testHelpers.js b/testHelpers.js deleted file mode 100644 index 6bf0561..0000000 --- a/testHelpers.js +++ /dev/null @@ -1,20 +0,0 @@ -export const java = require("./"); -java.options.push("-Djava.awt.headless=true"); -//java.options.push('-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005'); - -java.classpath.push("test/"); -java.classpath.push("test/commons-lang3-3.1.jar"); - -export function getJava(asyncOptions) { - java.asyncOptions = asyncOptions ?? { - syncSuffix: "Sync", - asyncSuffix: "", - promiseSuffix: "Promise", - promisify: require("when/node").lift, // https://github.com/cujojs/when - }; - - // force initialization - java.import("java.util.ArrayList"); - - return java; -} diff --git a/testHelpers.ts b/testHelpers.ts new file mode 100644 index 0000000..8af6b8a --- /dev/null +++ b/testHelpers.ts @@ -0,0 +1,46 @@ +import { AsyncOptions, Java, JavaError } from "./java"; +import findRoot from "find-root"; + +const root = findRoot(__dirname); +let java: Promise | undefined; + +export interface GetJavaOptions { + beforeInit?: (java: Java) => void | Promise; +} + +export async function getJava(asyncOptions?: AsyncOptions | null, options?: GetJavaOptions): Promise { + if (java) { + return java; + } + java = _getJava(asyncOptions, options); + return java; +} + +async function _getJava(asyncOptions?: AsyncOptions | null, options?: GetJavaOptions): Promise { + const java = (await import(root)).default as Java; + + java.options.push("-Djava.awt.headless=true"); + //java.options.push('-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005'); + + java.classpath.push("test/"); + java.classpath.push("test/commons-lang3-3.1.jar"); + + if (asyncOptions !== null) { + java.asyncOptions = asyncOptions ?? { + syncSuffix: "Sync", + asyncSuffix: "", + promiseSuffix: "Promise", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + promisify: ((await import("when/node")) as any).lift, // https://github.com/cujojs/when + }; + } + + await options?.beforeInit?.(java); + + // force initialization + java.import("java.util.ArrayList"); + + return java; +} + +export function expectJavaError(error: unknown): asserts error is JavaError {} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..03b86b5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,113 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "libReplacement": true, /* Enable lib replacement. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./build/ts", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +}