├── .gitattributes ├── .gitignore ├── .markdownlint.json ├── LICENSE ├── README.md ├── build.gradle ├── docs ├── .markdownlint.json ├── changelog.md ├── extension-usage │ ├── page.md │ └── probejs-vscode-extension.png ├── installation-and-usage │ ├── dump_structure.png │ ├── minecraft_folder.png │ ├── page.md │ └── vscode-typescript-extension.png ├── page.md └── snippets │ └── page.md ├── examples ├── 2.gif ├── 3.gif ├── 5.gif └── 6.gif ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── moe │ └── wolfgirl │ └── probejs │ ├── GameEvents.java │ ├── GameStates.java │ ├── ProbeConfig.java │ ├── ProbeDump.java │ ├── ProbeDumpingThread.java │ ├── ProbeJS.java │ ├── ProbePaths.java │ ├── docs │ ├── Attachments.java │ ├── Bindings.java │ ├── Events.java │ ├── ForgeEventDoc.java │ ├── ItemComponents.java │ ├── LoadClass.java │ ├── ObjectIDs.java │ ├── ParamFix.java │ ├── Primitives.java │ ├── ProbeBuiltinDocs.java │ ├── Snippets.java │ ├── TranslationDoc.java │ ├── assignments │ │ ├── AdditionalTypes.java │ │ ├── EnumTypes.java │ │ ├── InterfaceTypes.java │ │ ├── JavaPrimitives.java │ │ ├── RecipeTypes.java │ │ ├── RecordTypes.java │ │ ├── RegistryTypes.java │ │ ├── SpecialTypes.java │ │ └── WorldTypes.java │ └── events │ │ ├── RecipeEvents.java │ │ ├── RegistryEvents.java │ │ ├── TagEvents.java │ │ └── ViewerEvents.java │ ├── events │ ├── ProbeEvents.java │ ├── ScriptEventJS.java │ ├── SnippetGenerationEventJS.java │ ├── TypeAssignmentEventJS.java │ └── TypingModificationEventJS.java │ ├── features │ ├── ProbeJSEndpoints.java │ ├── ProbeJSTelemetry.java │ └── SchemaDownloader.java │ ├── lang │ ├── decompiler │ │ ├── ProbeClassScanner.java │ │ ├── ProbeClassSource.java │ │ ├── ProbeDecompiler.java │ │ ├── ProbeDecompilerLogger.java │ │ ├── ProbeFileSaver.java │ │ └── parser │ │ │ └── ParsedDocument.java │ ├── java │ │ ├── ClassRegistry.java │ │ ├── TypeAdapter.java │ │ ├── base │ │ │ ├── AnnotationHolder.java │ │ │ ├── ClassProvider.java │ │ │ └── TypeVariableHolder.java │ │ └── clazz │ │ │ ├── ClassPath.java │ │ │ ├── Clazz.java │ │ │ └── members │ │ │ ├── ConstructorInfo.java │ │ │ ├── FieldInfo.java │ │ │ ├── MethodInfo.java │ │ │ └── ParamInfo.java │ ├── linter │ │ ├── Linter.java │ │ ├── LintingWarning.java │ │ └── rules │ │ │ ├── RespectPriority.java │ │ │ └── Rule.java │ ├── schema │ │ ├── AnyElement.java │ │ ├── ArrayElement.java │ │ ├── ObjectElement.java │ │ ├── PrimitiveElement.java │ │ ├── SchemaDump.java │ │ └── SchemaElement.java │ ├── snippet │ │ ├── Snippet.java │ │ ├── SnippetDump.java │ │ └── parts │ │ │ ├── Choice.java │ │ │ ├── Enumerable.java │ │ │ ├── Literal.java │ │ │ ├── SnippetPart.java │ │ │ ├── TabStop.java │ │ │ └── Variable.java │ ├── transpiler │ │ ├── ClassTranspiler.java │ │ ├── Transpiler.java │ │ ├── TypeConverter.java │ │ ├── TypeUtils.java │ │ ├── members │ │ │ ├── Constructor.java │ │ │ ├── Converter.java │ │ │ ├── Field.java │ │ │ ├── Method.java │ │ │ └── Param.java │ │ └── transformation │ │ │ ├── ClassTransformer.java │ │ │ ├── InjectAnnotation.java │ │ │ ├── InjectArray.java │ │ │ ├── InjectBeans.java │ │ │ ├── InjectSelf.java │ │ │ └── InjectSpecialType.java │ └── typescript │ │ ├── Declaration.java │ │ ├── Reference.java │ │ ├── ScriptDump.java │ │ ├── TypeScriptFile.java │ │ └── code │ │ ├── Code.java │ │ ├── ImportInfo.java │ │ ├── member │ │ ├── ClassDecl.java │ │ ├── CommentableCode.java │ │ ├── ConstructorDecl.java │ │ ├── FieldDecl.java │ │ ├── InterfaceDecl.java │ │ ├── MethodDecl.java │ │ ├── ParamDecl.java │ │ ├── TypeDecl.java │ │ └── clazz │ │ │ ├── ConstructorBuilder.java │ │ │ └── MethodBuilder.java │ │ ├── ts │ │ ├── MethodDeclaration.java │ │ ├── ReexportDeclaration.java │ │ ├── Statements.java │ │ ├── VariableDeclaration.java │ │ └── Wrapped.java │ │ └── type │ │ ├── BaseType.java │ │ ├── ContextShield.java │ │ ├── CustomType.java │ │ ├── ImportShield.java │ │ ├── TSArrayType.java │ │ ├── TSClassType.java │ │ ├── TSOptionalType.java │ │ ├── TSParamType.java │ │ ├── TSStaticType.java │ │ ├── TSVariableType.java │ │ ├── Types.java │ │ └── js │ │ ├── JSArrayType.java │ │ ├── JSJoinedType.java │ │ ├── JSLambdaType.java │ │ ├── JSMemberType.java │ │ ├── JSObjectType.java │ │ ├── JSParam.java │ │ ├── JSPrimitiveType.java │ │ └── JSTypeOfType.java │ ├── mixins │ ├── JavaWrapperMixin.java │ ├── LootTableMixin.java │ ├── RecipeManagerMixin.java │ └── TranslatableMixin.java │ ├── plugin │ ├── BuiltinProbeJSPlugin.java │ ├── Probe.java │ ├── ProbeJSKJSPlugin.java │ └── ProbeJSPlugin.java │ └── utils │ ├── ConfigUtils.java │ ├── DocUtils.java │ ├── GameUtils.java │ ├── JsonUtils.java │ ├── NameUtils.java │ ├── ProbeFileUtils.java │ ├── RegistryUtils.java │ └── Require.java └── resources ├── META-INF ├── accesstransformer.cfg └── neoforge.mods.toml ├── assets └── probejs │ ├── icon.png │ └── lang │ ├── en_us.json │ ├── zh_cn.json │ └── zh_tw.json ├── icon.png ├── kubejs.plugins.txt ├── pack.mcmeta └── probejs.mixins.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | **/build/ 3 | **/run/ 4 | !src/**/build/ 5 | 6 | # Ignore Gradle GUI config 7 | gradle-app.setting 8 | 9 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 10 | !gradle-wrapper.jar 11 | 12 | # Cache of project 13 | .gradletasknamecache 14 | 15 | # Eclipse Gradle plugin generated files 16 | # Eclipse Core 17 | .project 18 | # JDT-specific (Eclipse Java Development Tools) 19 | .classpath 20 | /.idea/ 21 | ProbeJS_server.launch 22 | ProbeJS_client.launch 23 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD041": false, 3 | "MD033": false, 4 | "MD013": false, 5 | "MD029": false, 6 | "MD036": false 7 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProbeJS 2 | 3 | A data dumper and typing generator for the KubeJS functions, constants and classes. 4 | 5 | Great thanks to @DAmNRelentless, @LatvianModder and @yesterday17 for invaluable suggestions during the development! 6 | 7 | For the detailed information about documents, please refer to the wiki page. 8 | 9 | ## 1. Installation 10 | 11 | 1. Get VSCode. 12 | 2. Install the mod. 13 | 3. In game, use `/probejs dump` and wait for the typings to be generated. 14 | 4. Open the `.minecraft` folder in VSCode, you should see snippets and typing functioning. 15 | 5. Use `/probejs dump` in case of you want to refresh the generated typing. If VSCode is not responding to file changes, 16 | press F1 and execute `TypeScript: Restart TS server` to force a refresh in Code. 17 | 18 | ## 2. Event Dump 19 | 20 | 1. Use `/probejs dump` after events of interests are fired. 21 | 2. After the typing generation is finished, check the change in IDE. 22 | 23 | ## 3. Showcase 24 | 25 | Auto-completion snippets for Items, Blocks, Fluids, Entities and Tags: 26 | 27 | ![image](./examples/2.gif) 28 | 29 | Auto-completion, type-hinting for most of the functions and classes: 30 | 31 | ![image](./examples/3.gif) 32 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'base' 3 | id 'java' 4 | id 'maven-publish' 5 | id 'net.neoforged.moddev' version "2.0.75" 6 | } 7 | 8 | version = "${mod_version}" 9 | group = project.maven_group 10 | 11 | neoForge { 12 | version = project.neoforge_version 13 | accessTransformers { 14 | publish(project.file("src/main/resources/META-INF/accesstransformer.cfg")) 15 | } 16 | 17 | mods { 18 | probejs { 19 | sourceSet sourceSets.main 20 | } 21 | } 22 | 23 | parchment { 24 | minecraftVersion = parchment_mc_version 25 | mappingsVersion = parchment_version 26 | } 27 | 28 | runs { 29 | client { 30 | client() 31 | } 32 | server { 33 | server() 34 | } 35 | } 36 | } 37 | 38 | repositories { 39 | mavenCentral() 40 | mavenLocal() 41 | 42 | maven { 43 | url "https://maven.neoforged.net/releases" 44 | } 45 | 46 | maven { 47 | url "https://maven.architectury.dev/" 48 | } 49 | 50 | maven { 51 | url "https://maven.latvian.dev/releases" 52 | content { 53 | includeGroup "dev.latvian.mods" 54 | includeGroup "dev.latvian.apps" 55 | } 56 | } 57 | 58 | maven { 59 | url "https://maven.blamejared.com" 60 | content { 61 | includeGroup "mezz.jei" 62 | includeGroup "net.darkhax.bookshelf" 63 | includeGroup "net.darkhax.gamestages" 64 | } 65 | } 66 | 67 | maven { 68 | name = 'ParchmentMC' 69 | url = 'https://maven.parchmentmc.org' 70 | } 71 | 72 | maven { 73 | name = "TerraformersMC" 74 | url = "https://maven.terraformersmc.com/" 75 | content { 76 | includeGroup "dev.emi" 77 | } 78 | } 79 | 80 | maven { 81 | url 'https://jitpack.io' 82 | content { 83 | includeGroup "com.github.rtyley" 84 | } 85 | } 86 | } 87 | 88 | dependencies { 89 | api("dev.latvian.mods:rhino:$rhino_version") { transitive(false) } 90 | api("dev.latvian.mods:kubejs-neoforge:$kubejs_version") 91 | interfaceInjectionData("dev.latvian.mods:kubejs-neoforge:$kubejs_version") 92 | implementation("dev.latvian.apps:tiny-java-server:$tiny_server_version") 93 | compileOnly("dev.architectury:architectury-neoforge:$architectury_version") 94 | 95 | jarJar implementation('com.github.javaparser:javaparser-symbol-solver-core:3.24.8') 96 | jarJar implementation('com.github.javaparser:javaparser-core:3.24.8') 97 | jarJar implementation("org.vineflower:vineflower:1.10.1") 98 | } 99 | 100 | compileJava { 101 | options.encoding = "UTF-8" 102 | options.release.set(21) 103 | options.compilerArgs << '-parameters' 104 | } 105 | 106 | java { 107 | sourceCompatibility = targetCompatibility = '21' 108 | withSourcesJar() 109 | } 110 | 111 | processResources { 112 | def toReplace = [ 113 | "version" : project.version, 114 | "neoforge_version": neoforge_version, 115 | "kubejs_version" : kubejs_version, 116 | ] 117 | 118 | println("[Process Resources] Replacing properties in resources: " + toReplace) 119 | 120 | inputs.properties toReplace 121 | filesMatching("META-INF/neoforge.mods.toml") { 122 | expand toReplace 123 | } 124 | } 125 | 126 | jar { 127 | manifest { 128 | attributes([ 129 | "Specification-Title" : mod_id, 130 | "Specification-Vendor" : mod_author, 131 | "Specification-Version" : "1", 132 | "Implementation-Title" : project.name, 133 | "Implementation-Version" : version, 134 | "Implementation-Vendor" : mod_author, 135 | "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") 136 | ]) 137 | } 138 | } -------------------------------------------------------------------------------- /docs/.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "first-line-heading": false, 3 | "line-length": false 4 | } -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | ## v7.0.0 (1.21) 2 | 3 | ### JavaScript Project 4 | 5 | - A separate `jsconfig.json` is now generated for each script type. 6 | - Each script type will only be able to access their own events and bindings. 7 | - Typing for each script type is now generated at `.minecraft/.probe`, instead of `.minecraft/kubejs/probe`. 8 | - Does not require you to place scripts under `src/` folder anymore. However, it is still advised to do so for a better project structure. 9 | - Also generates a `test/` folder for each script type, scripts in this folder will be able to invoke some functions for debugging. 10 | 11 | ### Typing 12 | 13 | - Generation of class types now adapt to a more module-like manner to allow the auto-import to work. 14 | - Improved completion performance by over 10x via separating complex object types to individual type declarations. 15 | 16 | ### Java Class Discovery 17 | 18 | - Decompiler now uses runtime class bytecode to generate decompiled code, meaning that most of the results is deobfuscated and well-defined. 19 | - Implemented a class scanner to discover mod, NeoForge and Minecraft classes in order to eliminate the need of checking registry classes or a lot of other work. Basically those are all classes you can load in the game. 20 | 21 | ### `require` and `export` 22 | 23 | - `require` is largely fixed by using Rhino's parser to patch the script code. 24 | - ESM `import` is not supported due to fundamental conflicts to Rhino. 25 | - Scope isolation is now off by default. 26 | 27 | ### VSCode Extension 28 | 29 | - Rewritten the extension for better VSCode-Minecraft interaction. 30 | -------------------------------------------------------------------------------- /docs/extension-usage/page.md: -------------------------------------------------------------------------------- 1 | After KubeJS 7.2, a lot of functions are integrated into the ProbeJS VSCode extension, making it a mandatory tool for ProbeJS users to have the full functionality of the mod. The extension is available at the VSCode marketplace, and you can install it by searching for `ProbeJS`. 2 | 3 | ![ProbeJS VSCode Extension](./probejs-vscode-extension.png) 4 | -------------------------------------------------------------------------------- /docs/extension-usage/probejs-vscode-extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prunoideae/ProbeJS/008b38b716ad53beb4989eab6df7aa5949815750/docs/extension-usage/probejs-vscode-extension.png -------------------------------------------------------------------------------- /docs/installation-and-usage/dump_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prunoideae/ProbeJS/008b38b716ad53beb4989eab6df7aa5949815750/docs/installation-and-usage/dump_structure.png -------------------------------------------------------------------------------- /docs/installation-and-usage/minecraft_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prunoideae/ProbeJS/008b38b716ad53beb4989eab6df7aa5949815750/docs/installation-and-usage/minecraft_folder.png -------------------------------------------------------------------------------- /docs/installation-and-usage/vscode-typescript-extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prunoideae/ProbeJS/008b38b716ad53beb4989eab6df7aa5949815750/docs/installation-and-usage/vscode-typescript-extension.png -------------------------------------------------------------------------------- /docs/page.md: -------------------------------------------------------------------------------- 1 | Download: [CurseForge](https://www.curseforge.com/minecraft/mc-mods/probejs) 2 | 3 | --- 4 | 5 | This wiki contains the documentation for the ProbeJS mod. ProbeJS is a KubeJS addon that allows the dynamic generation of `typings` that can be used by IDEs like VSCode to provide better autocompletion and type checking for KubeJS scripts. 6 | 7 | For installation instructions, see the [Installation](./installation/page.md) page. Also check out other pages for more information on how to use ProbeJS. 8 | 9 | --- 10 | 11 | Current wiki is targeting ProbeJS version 7.0.0, which is a deeply rewritten version of ProbeJS. Most of the feature described here might not be available in older versions. If you are using an older version of ProbeJS, consider updating to the latest version. 12 | -------------------------------------------------------------------------------- /docs/snippets/page.md: -------------------------------------------------------------------------------- 1 | *Note: this only works with VSCode* 2 | 3 | Snippets are small pieces of text that can be inserted into a document. They are often used to provide examples of code, commands, or configuration options. 4 | 5 | To facilitate your writing, ProbeJS has a set of snippets that you can use to quickly insert useful strings like item or block names, tags, and other things like a stub for recipe events. 6 | 7 | The usage of snippets does not require VSCode to have type support for your current file. So it can be especially useful when modifying config files or writing documentation. Since the ProbeJS typing only covers JavaScript files. 8 | 9 | Snippets can have tabstops, which are positions in the snippet where the cursor will be placed after the snippet is inserted. You can navigate between tabstops with the `Tab` key. 10 | 11 | ## `@` Snippets 12 | 13 | Snippets that start with `@` usually represent a literal for a registry object. For example, `@item` will insert a string that is a valid item name, like `"minecraft:stone"`. Each registry type also has a snippet that will insert corresponding tags, they are suffixed with `_tag`, so `@item_tag` will insert `"#forge:rods"`. 14 | 15 | ## `#` Snippets 16 | 17 | `#` snippets are usually predefined and serve for inserting useful strings. 18 | 19 | ### `#recipes` 20 | 21 | This snippet will insert a stub for a recipe event. 22 | 23 | ```js 24 | ServerEvents.recipes(event=>{ 25 | let {/** First tabstop */} = event; 26 | /** Second tabstop */ 27 | }) 28 | ``` 29 | 30 | ### `#uuid` 31 | 32 | This snippet will insert a random UUID as a string. 33 | 34 | ```js 35 | "00000000-0000-0000-0000-000000000000" 36 | ``` 37 | 38 | ### `#priority` 39 | 40 | This snippet will insert the comment to configure script priority. 41 | 42 | ```js 43 | // priority: /** Tabstop, default to 0 */ 44 | ``` 45 | 46 | ### `#requires` 47 | 48 | This snippet will insert the comment to configure script requirements of mods. 49 | 50 | ```js 51 | // requires: /** Tabstop, can choose one of the existing mods */ 52 | ``` 53 | 54 | ### `#packmode` 55 | 56 | This snippet will insert the comment to configure the pack mode. 57 | 58 | ```js 59 | // packmode: /** Tabstop */ 60 | ``` 61 | 62 | ### `#itemstack` 63 | 64 | This snippet will insert a stub for an item stack. 65 | 66 | ```js 67 | "/** Tabstop */x /** Tabstop of item names*/" 68 | ``` 69 | -------------------------------------------------------------------------------- /examples/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prunoideae/ProbeJS/008b38b716ad53beb4989eab6df7aa5949815750/examples/2.gif -------------------------------------------------------------------------------- /examples/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prunoideae/ProbeJS/008b38b716ad53beb4989eab6df7aa5949815750/examples/3.gif -------------------------------------------------------------------------------- /examples/5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prunoideae/ProbeJS/008b38b716ad53beb4989eab6df7aa5949815750/examples/5.gif -------------------------------------------------------------------------------- /examples/6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prunoideae/ProbeJS/008b38b716ad53beb4989eab6df7aa5949815750/examples/6.gif -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx8192M 2 | 3 | loom.platform=neoforge 4 | minecraft_version=1.21.1 5 | 6 | archives_base_name=probejs 7 | mod_version=7.7.2 8 | maven_group=moe.wolfgirl 9 | 10 | neoforge_version=21.1.114 11 | parchment_mc_version=1.21 12 | parchment_version=2024.07.28 13 | 14 | kubejs_version=2101.7.2-build.230 15 | rhino_version=2101.2.7-build.72 16 | architectury_version=13.0.2 17 | tiny_server_version=1.0.0-build.25 18 | 19 | # Mods 20 | 21 | org.gradle.parallel=false 22 | mod_id=probejs 23 | mod_author=Prunoideae 24 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prunoideae/ProbeJS/008b38b716ad53beb4989eab6df7aa5949815750/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url "https://maven.fabricmc.net/" } 4 | maven { url "https://maven.architectury.dev/" } 5 | maven { url "https://maven.minecraftforge.net/" } 6 | gradlePluginPortal() 7 | } 8 | } 9 | 10 | 11 | rootProject.name = "ProbeJS" 12 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/GameStates.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import dev.latvian.mods.kubejs.util.Lazy; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.client.resources.language.ClientLanguage; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.locale.Language; 10 | import net.minecraft.resources.ResourceLocation; 11 | import net.minecraft.server.packs.resources.ResourceManager; 12 | import net.minecraft.world.entity.Entity; 13 | import net.minecraft.world.inventory.InventoryMenu; 14 | import net.neoforged.fml.ModList; 15 | import net.neoforged.neoforgespi.language.IModInfo; 16 | 17 | import java.util.*; 18 | import java.util.function.Supplier; 19 | import java.util.stream.Collectors; 20 | 21 | public class GameStates { 22 | public static final Set MIXIN_LANG_KEYS = new HashSet<>(); 23 | public static final Map RECIPE_IDS = new HashMap<>(); 24 | public static final Set LOOT_TABLES = new HashSet<>(); 25 | 26 | public static final Supplier> LANG_KEYS = () -> 27 | Language.getInstance() instanceof ClientLanguage clientLanguage ? 28 | clientLanguage.storage.keySet() 29 | .stream() 30 | .filter(s -> s.toLowerCase().equals(s) && !s.startsWith("_")) 31 | .collect(Collectors.toSet()) : 32 | Set.of(); 33 | 34 | public static final Supplier> TEXTURES = () -> 35 | Minecraft.getInstance() 36 | .getModelManager() 37 | .getAtlas(InventoryMenu.BLOCK_ATLAS) 38 | .texturesByName 39 | .keySet() 40 | .stream().map(ResourceLocation::toString) 41 | .collect(Collectors.toSet()); 42 | 43 | public static final Supplier> MODS = () -> 44 | ModList.get() 45 | .getMods() 46 | .stream() 47 | .map(IModInfo::getModId) 48 | .collect(Collectors.toSet()); 49 | 50 | // For probing stuffs 51 | public static BlockPos LAST_RIGHTCLICKED = null; 52 | public static Entity LAST_ENTITY = null; 53 | 54 | public static final Lazy DEFAULT_LANGUAGE = Lazy.of(() -> { 55 | ResourceManager manager = Minecraft.getInstance().getResourceManager(); 56 | return ClientLanguage.loadFrom(manager, List.of(Language.DEFAULT), false); 57 | }); 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/ProbeDumpingThread.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs; 2 | 3 | import net.minecraft.network.chat.Component; 4 | 5 | import java.util.function.Consumer; 6 | 7 | import moe.wolfgirl.probejs.utils.GameUtils; 8 | 9 | /** 10 | * @author ZZZank 11 | */ 12 | public class ProbeDumpingThread extends Thread { 13 | 14 | public static ProbeDumpingThread INSTANCE; 15 | 16 | public static boolean exists() { 17 | return INSTANCE != null && INSTANCE.isAlive(); 18 | } 19 | 20 | static ProbeDumpingThread create(final Consumer messageSender) { 21 | if (exists()) { 22 | throw new IllegalStateException("There's already a thread running"); 23 | } 24 | ProbeDumpingThread thread = new ProbeDumpingThread(messageSender); 25 | INSTANCE = thread; 26 | return thread; 27 | } 28 | 29 | private ProbeDumpingThread(final Consumer messageSender) { 30 | super( 31 | () -> { // Don't stall the client 32 | var dump = new ProbeDump(); 33 | dump.defaultScripts(); 34 | try { 35 | dump.trigger(messageSender); 36 | } catch (Throwable e) { 37 | GameUtils.logException(e); 38 | } 39 | }, 40 | "ProbeDumpingThread" 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/ProbeJS.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import moe.wolfgirl.probejs.utils.JsonUtils; 6 | import net.neoforged.fml.common.Mod; 7 | import org.apache.logging.log4j.LogManager; 8 | import org.apache.logging.log4j.Logger; 9 | 10 | import java.nio.file.Path; 11 | 12 | @Mod(ProbeJS.MOD_ID) 13 | public class ProbeJS { 14 | public static final String MOD_ID = "probejs"; 15 | public static final Logger LOGGER = LogManager.getLogger("probejs"); 16 | public static final Gson GSON = new GsonBuilder() 17 | .serializeSpecialFloatingPointValues() 18 | .setLenient() 19 | .disableHtmlEscaping() 20 | .registerTypeHierarchyAdapter(Path.class, new JsonUtils.PathConverter()) 21 | .create(); 22 | public static final Gson GSON_WRITER = new GsonBuilder() 23 | .setPrettyPrinting() 24 | .disableHtmlEscaping() 25 | .create(); 26 | 27 | public ProbeJS() { 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/ProbePaths.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs; 2 | 3 | import dev.latvian.mods.kubejs.KubeJSPaths; 4 | 5 | import java.io.IOException; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | 9 | public class ProbePaths { 10 | 11 | public static Path PROBE = KubeJSPaths.GAMEDIR.resolve(".probe"); 12 | public static Path WORKSPACE_SETTINGS = KubeJSPaths.GAMEDIR.resolve(".vscode"); 13 | public static Path SETTINGS_JSON = KubeJSPaths.CONFIG.resolve("probe-settings.json"); 14 | public static Path VSCODE_JSON = WORKSPACE_SETTINGS.resolve("settings.json"); 15 | public static Path GIT_IGNORE = KubeJSPaths.GAMEDIR.resolve(".gitignore"); 16 | public static Path DECOMPILED = PROBE.resolve("decompiled"); 17 | 18 | public static Path IMAGES = PROBE.resolve("images"); 19 | 20 | public static void init() { 21 | createFolders(PROBE); 22 | createFolders(WORKSPACE_SETTINGS); 23 | createFolders(DECOMPILED); 24 | createFolders(IMAGES); 25 | } 26 | 27 | private static void createFolders(Path path) { 28 | if (Files.notExists(path)) { 29 | try { 30 | Files.createDirectories(path); 31 | } catch (IOException e) { 32 | throw new RuntimeException(e); 33 | } 34 | } 35 | } 36 | 37 | static { 38 | init(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/docs/Attachments.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.docs; 2 | 3 | import com.mojang.datafixers.util.Pair; 4 | import dev.latvian.mods.kubejs.block.entity.BlockEntityAttachmentType; 5 | import dev.latvian.mods.kubejs.block.entity.BlockEntityInfo; 6 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 7 | import moe.wolfgirl.probejs.lang.transpiler.TypeConverter; 8 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 9 | import moe.wolfgirl.probejs.lang.typescript.TypeScriptFile; 10 | import moe.wolfgirl.probejs.lang.typescript.code.member.ClassDecl; 11 | import moe.wolfgirl.probejs.lang.typescript.code.member.ParamDecl; 12 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 13 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 14 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 15 | import moe.wolfgirl.probejs.utils.DocUtils; 16 | 17 | import java.util.HashSet; 18 | import java.util.Map; 19 | import java.util.Set; 20 | import java.util.stream.Collectors; 21 | 22 | public class Attachments extends ProbeJSPlugin { 23 | 24 | @Override 25 | public void modifyClasses(ScriptDump scriptDump, Map globalClasses) { 26 | TypeScriptFile typeScriptFile = globalClasses.get(new ClassPath(BlockEntityInfo.class)); 27 | ClassDecl classDecl = typeScriptFile.findCode(ClassDecl.class).orElse(null); 28 | if (classDecl == null) return; 29 | 30 | TypeConverter converter = scriptDump.transpiler.typeConverter; 31 | 32 | DocUtils.generateMappedType("AttachmentMap", "Attachments", 33 | BlockEntityAttachmentType.ALL.get() 34 | .entrySet() 35 | .stream() 36 | .map(entry -> Pair.of(entry.getKey().toString(), converter.convertType(entry.getValue().typeInfo()))) 37 | .collect(Collectors.toSet()), 38 | typeScriptFile); 39 | 40 | classDecl.methods.stream() 41 | .filter(methodDecl -> methodDecl.name.equals("attach")) 42 | .findFirst() 43 | .ifPresent(attach -> { 44 | attach.variableTypes.add(Types.generic("T", Types.primitive("Attachments"))); 45 | attach.params.set(1, new ParamDecl("type", Types.generic("T"), false, false)); 46 | attach.params.set(3, new ParamDecl("args", Types.primitive("%s[T]".formatted("AttachmentMap")), false, false)); 47 | }); 48 | } 49 | 50 | @Override 51 | public Set> provideJavaClass(ScriptDump scriptDump) { 52 | HashSet> classes = new HashSet<>(); 53 | 54 | for (BlockEntityAttachmentType value : BlockEntityAttachmentType.ALL.get().values()) { 55 | classes.addAll(value.typeInfo().getContainedComponentClasses()); 56 | } 57 | 58 | return classes; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/docs/ForgeEventDoc.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.docs; 2 | 3 | 4 | import dev.latvian.mods.kubejs.plugin.builtin.wrapper.NativeEventWrapper; 5 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 6 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 7 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 8 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 9 | import moe.wolfgirl.probejs.lang.typescript.TypeScriptFile; 10 | import moe.wolfgirl.probejs.lang.typescript.code.member.ClassDecl; 11 | import moe.wolfgirl.probejs.lang.typescript.code.member.MethodDecl; 12 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 13 | import net.neoforged.bus.api.Event; 14 | 15 | 16 | import java.util.Map; 17 | import java.util.Set; 18 | import java.util.stream.Collectors; 19 | 20 | public class ForgeEventDoc extends ProbeJSPlugin { 21 | 22 | @Override 23 | public void modifyClasses(ScriptDump scriptDump, Map globalClasses) { 24 | TypeScriptFile typeScriptFile = globalClasses.get(new ClassPath(NativeEventWrapper.class)); 25 | typeScriptFile.declaration.addClass(new ImportInfo(new ClassPath(Event.class), ImportInfo.Type.ORIGINAL)); 26 | ClassDecl classDecl = typeScriptFile.findCode(ClassDecl.class).orElse(null); 27 | if (classDecl == null) return; 28 | 29 | for (MethodDecl method : classDecl.methods) { 30 | if (method.name.equals("onEvent")) { 31 | method.variableTypes.add( 32 | Types.generic("T", 33 | Types.typeOf( 34 | Types.parameterized( 35 | Types.type(Event.class), 36 | Types.UNKNOWN) 37 | ) 38 | ) 39 | ); 40 | if (method.params.size() == 2) { 41 | method.params.getFirst().type = Types.generic("T"); 42 | method.params.get(1).type = Types.lambda() 43 | .param("event", Types.parameterized(Types.primitive("InstanceType"), Types.primitive("T"))) 44 | .build(); 45 | } else { 46 | method.params.get(1).type = Types.generic("T"); 47 | method.params.get(2).type = Types.lambda() 48 | .param("event", Types.parameterized(Types.primitive("InstanceType"), Types.primitive("T"))) 49 | .build(); 50 | } 51 | } 52 | } 53 | } 54 | 55 | @Override 56 | public Set> filterScannedClasses(Set> clazz) { 57 | return clazz.stream().filter(Event.class::isAssignableFrom).collect(Collectors.toSet()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/docs/ItemComponents.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.docs; 2 | 3 | import dev.latvian.mods.kubejs.component.DataComponentWrapper; 4 | import dev.latvian.mods.rhino.type.TypeInfo; 5 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 6 | import moe.wolfgirl.probejs.lang.transpiler.TypeConverter; 7 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 8 | import moe.wolfgirl.probejs.lang.typescript.TypeScriptFile; 9 | import moe.wolfgirl.probejs.lang.typescript.code.member.ClassDecl; 10 | import moe.wolfgirl.probejs.lang.typescript.code.member.TypeDecl; 11 | import moe.wolfgirl.probejs.lang.typescript.code.member.clazz.MethodBuilder; 12 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 13 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 14 | import moe.wolfgirl.probejs.lang.typescript.code.type.js.JSObjectType; 15 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 16 | import net.minecraft.core.component.DataComponentType; 17 | import net.minecraft.core.registries.BuiltInRegistries; 18 | import net.minecraft.resources.ResourceLocation; 19 | import net.minecraft.world.item.ItemStack; 20 | import net.neoforged.neoforge.fluids.FluidStack; 21 | 22 | import java.util.HashSet; 23 | import java.util.Map; 24 | import java.util.Set; 25 | 26 | public class ItemComponents extends ProbeJSPlugin { 27 | @Override 28 | public void modifyClasses(ScriptDump scriptDump, Map globalClasses) { 29 | TypeConverter typeConverter = scriptDump.transpiler.typeConverter; 30 | 31 | JSObjectType.Builder builder = Types.object(); 32 | for (Map.Entry, TypeInfo> entry : DataComponentWrapper.TYPE_INFOS.get().entrySet()) { 33 | ResourceLocation componentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(entry.getKey()); 34 | BaseType baseType = typeConverter.convertType(entry.getValue()); 35 | 36 | assert componentType != null; // An unregistered component type but found in type infos? 37 | builder.member(componentType.toString(), baseType); 38 | } 39 | 40 | TypeScriptFile scriptFile = globalClasses.get(new ClassPath(ItemStack.class)); 41 | assert scriptFile != null; // Must be very serious to have this null 42 | 43 | patchClass(builder, ItemStack.class, globalClasses); 44 | patchClass(builder, FluidStack.class, globalClasses); 45 | } 46 | 47 | private void patchClass(JSObjectType.Builder builder, Class thisType, Map globalClasses) { 48 | TypeScriptFile scriptFile = globalClasses.get(new ClassPath(thisType)); 49 | assert scriptFile != null; 50 | 51 | scriptFile.addCode(new TypeDecl("ComponentTypeMap", builder.buildIndexed())); 52 | scriptFile.addCode(new TypeDecl("ComponentTypes", Types.primitive("keyof ComponentTypeMap"))); 53 | 54 | ClassDecl classDecl = scriptFile.findCode(ClassDecl.class).orElseThrow(); 55 | classDecl.methods.removeIf(methodDecl -> methodDecl.name.equals("set")); 56 | classDecl.methods.add(new MethodBuilder("set") 57 | .typeVariables(Types.generic("T", Types.primitive("ComponentTypes"))) 58 | .param("componentType", Types.generic("T")) 59 | .param("value", Types.primitive("ComponentTypeMap[T]")) 60 | .returnType(Types.type(thisType)) 61 | .buildAsMethod() 62 | ); 63 | } 64 | 65 | @Override 66 | public Set> provideJavaClass(ScriptDump scriptDump) { 67 | Set> toAdd = new HashSet<>(); 68 | toAdd.add(DataComponentType.class); 69 | toAdd.add(FluidStack.class); 70 | toAdd.add(ItemStack.class); 71 | for (TypeInfo value : DataComponentWrapper.TYPE_INFOS.get().values()) { 72 | if (!value.isPrimitive()) { 73 | toAdd.addAll(value.getContainedComponentClasses()); 74 | } 75 | } 76 | return toAdd; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/docs/ObjectIDs.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.docs; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 4 | import moe.wolfgirl.probejs.lang.transpiler.transformation.InjectBeans; 5 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 6 | import moe.wolfgirl.probejs.lang.typescript.TypeScriptFile; 7 | import moe.wolfgirl.probejs.lang.typescript.code.Code; 8 | import moe.wolfgirl.probejs.lang.typescript.code.member.ClassDecl; 9 | import moe.wolfgirl.probejs.lang.typescript.code.member.MethodDecl; 10 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 11 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 12 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 13 | import net.minecraft.world.item.Item; 14 | import net.minecraft.world.item.ItemStack; 15 | import net.minecraft.world.level.block.Block; 16 | 17 | import java.util.Map; 18 | 19 | public class ObjectIDs extends ProbeJSPlugin { 20 | @Override 21 | public void modifyClasses(ScriptDump scriptDump, Map globalClasses) { 22 | replaceIdGetter(globalClasses, Block.class, "getId", Types.primitive("Special.Block")); 23 | replaceIdGetter(globalClasses, Item.class, "getId", Types.primitive("Special.Item")); 24 | replaceIdGetter(globalClasses, ItemStack.class, "getId", Types.primitive("Special.Item")); 25 | } 26 | 27 | private void replaceIdGetter(Map globalClasses, Class clazz, String methodName, BaseType replaced) { 28 | ClassDecl classDecl = findClassFile(globalClasses, clazz).findCode(ClassDecl.class).orElse(null); 29 | if (classDecl == null) return; 30 | 31 | for (MethodDecl method : classDecl.methods) { 32 | if (method.name.equals(methodName)) { 33 | method.returnType = replaced; 34 | } 35 | } 36 | 37 | String beanName = InjectBeans.getBeanName(methodName); 38 | for (Code code : classDecl.bodyCode) { 39 | if (code instanceof InjectBeans.BeanDecl beanDecl && beanDecl.name.equals(beanName)) { 40 | beanDecl.baseType = replaced; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/docs/ParamFix.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.docs; 2 | 3 | import dev.latvian.mods.kubejs.plugin.builtin.wrapper.TextWrapper; 4 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 5 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 6 | import moe.wolfgirl.probejs.lang.typescript.TypeScriptFile; 7 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 8 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 9 | import moe.wolfgirl.probejs.utils.DocUtils; 10 | import net.minecraft.network.chat.MutableComponent; 11 | 12 | import java.util.Map; 13 | 14 | public class ParamFix extends ProbeJSPlugin { 15 | @Override 16 | public void modifyClasses(ScriptDump scriptDump, Map globalClasses) { 17 | 18 | var textWrapper = globalClasses.get(new ClassPath(TextWrapper.class)); 19 | DocUtils.replaceParamType( 20 | textWrapper, 21 | m -> m.params.size() == 1 && m.name.equals("of"), 22 | 0, 23 | Types.type(MutableComponent.class) 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/docs/Snippets.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.docs; 2 | 3 | import moe.wolfgirl.probejs.GameStates; 4 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 5 | import moe.wolfgirl.probejs.lang.snippet.SnippetDump; 6 | import moe.wolfgirl.probejs.lang.snippet.parts.Variable; 7 | import net.minecraft.core.registries.Registries; 8 | 9 | import java.util.List; 10 | 11 | public class Snippets extends ProbeJSPlugin { 12 | @Override 13 | public void addVSCodeSnippets(SnippetDump dump) { 14 | dump.snippet("uuid") 15 | .prefix("#uuid") 16 | .description("Generates a random version 4 UUID.") 17 | .literal("\"") 18 | .variable(Variable.UUID) 19 | .literal("\""); 20 | 21 | dump.snippet("recipes") 22 | .prefix("#recipes") 23 | .literal("ServerEvents.recipes(event => {") 24 | .newline() 25 | .literal(" const { ") 26 | .tabStop(1) 27 | .literal(" } = event.recipes") 28 | .newline() 29 | .literal(" ") 30 | .tabStop(0) 31 | .newline() 32 | .literal("})"); 33 | 34 | defineHeader(dump, "priority", "0"); 35 | defineHeader(dump, "packmode", null); 36 | 37 | dump.snippet("ignored") 38 | .prefix("#ignored") 39 | .description("Creates the file header for `ignored`.") 40 | .literal("// ignored: ") 41 | .choices(List.of("true", "false")); 42 | 43 | dump.snippet("requires") 44 | .prefix("#requires") 45 | .description("Creates the file header for `requires`.") 46 | .literal("// requires: ") 47 | .choices(GameStates.MODS.get()); 48 | 49 | dump.snippet("itemstack") 50 | .prefix("#itemstack") 51 | .description("Creates a `nx item string.") 52 | .literal("\"") 53 | .tabStop(1, "1") 54 | .literal("x ") 55 | .registry(Registries.ITEM) 56 | .literal("\""); 57 | } 58 | 59 | private static void defineHeader(SnippetDump dump, String symbol, String defaultValue) { 60 | dump.snippet(symbol) 61 | .prefix("#" + symbol) 62 | .description("Creates the file header for `%s`.".formatted(symbol)) 63 | .literal("// %s: ".formatted(symbol)) 64 | .tabStop(0, defaultValue); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/docs/TranslationDoc.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.docs; 2 | 3 | import dev.latvian.mods.kubejs.plugin.builtin.wrapper.TextWrapper; 4 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 5 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 6 | import moe.wolfgirl.probejs.lang.typescript.TypeScriptFile; 7 | import moe.wolfgirl.probejs.lang.typescript.code.member.ClassDecl; 8 | import moe.wolfgirl.probejs.lang.typescript.code.member.MethodDecl; 9 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 10 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 11 | 12 | import java.util.Map; 13 | 14 | public class TranslationDoc extends ProbeJSPlugin { 15 | @Override 16 | public void modifyClasses(ScriptDump scriptDump, Map globalClasses) { 17 | TypeScriptFile typeScriptFile = globalClasses.get(new ClassPath(TextWrapper.class)); 18 | ClassDecl classDecl = typeScriptFile.findCode(ClassDecl.class).orElse(null); 19 | if (classDecl == null) return; 20 | 21 | for (MethodDecl method : classDecl.methods) { 22 | if (method.name.equals("translate") || method.name.equals("translatable")) { 23 | method.params.getFirst().type = Types.primitive("Special.LangKey"); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/docs/assignments/AdditionalTypes.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.docs.assignments; 2 | 3 | import com.google.common.collect.ArrayListMultimap; 4 | import com.google.common.collect.Multimap; 5 | import dev.latvian.mods.kubejs.plugin.KubeJSPlugins; 6 | import dev.latvian.mods.kubejs.script.ScriptType; 7 | import dev.latvian.mods.kubejs.script.TypeDescriptionRegistry; 8 | import dev.latvian.mods.rhino.type.TypeInfo; 9 | import moe.wolfgirl.probejs.ProbeJS; 10 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 11 | import moe.wolfgirl.probejs.lang.transpiler.TypeConverter; 12 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 13 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 14 | import moe.wolfgirl.probejs.lang.typescript.code.type.TSClassType; 15 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 16 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 17 | 18 | import java.util.Map; 19 | 20 | public class AdditionalTypes extends ProbeJSPlugin { 21 | 22 | @Override 23 | public void assignType(ScriptDump scriptDump) { 24 | TypeDescriptions typeDescriptions = new TypeDescriptions(scriptDump.scriptType); 25 | KubeJSPlugins.forEachPlugin(plugin -> { 26 | try { 27 | plugin.registerTypeDescriptions(typeDescriptions); 28 | } catch (Throwable t) { 29 | ProbeJS.LOGGER.warn("Plugin %s thrown an error when registering additional types: %s".formatted(plugin.getClass(), t)); 30 | } 31 | }); 32 | TypeConverter typeConverter = scriptDump.transpiler.typeConverter; 33 | 34 | for (Map.Entry, TypeInfo> entry : typeDescriptions.registries.entries()) { 35 | Class clazz = entry.getKey(); 36 | ClassPath classPath = new ClassPath(clazz); 37 | TypeInfo typeInfo = entry.getValue(); 38 | 39 | // Prevent people from registering circulating types 40 | BaseType baseType = Types.filter(typeConverter.convertType(typeInfo), 41 | type -> type instanceof TSClassType classType && 42 | classType.classPath.equals(classPath) 43 | ); 44 | 45 | scriptDump.assignType(clazz, baseType); 46 | } 47 | } 48 | 49 | static class TypeDescriptions implements TypeDescriptionRegistry { 50 | private final ScriptType scriptType; 51 | private final Multimap, TypeInfo> registries = ArrayListMultimap.create(); 52 | 53 | TypeDescriptions(ScriptType scriptType) { 54 | this.scriptType = scriptType; 55 | } 56 | 57 | @Override 58 | public ScriptType scriptType() { 59 | return scriptType; 60 | } 61 | 62 | @Override 63 | public void register(Class target, TypeInfo typeInfo) { 64 | registries.put(target, typeInfo); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/docs/assignments/EnumTypes.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.docs.assignments; 2 | 3 | import dev.latvian.mods.rhino.type.EnumTypeInfo; 4 | import dev.latvian.mods.rhino.type.TypeInfo; 5 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 6 | import moe.wolfgirl.probejs.lang.java.clazz.Clazz; 7 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 8 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 9 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 10 | 11 | import java.util.concurrent.locks.ReentrantLock; 12 | 13 | public class EnumTypes extends ProbeJSPlugin { 14 | // EnumTypeWrapper is not thread-safe 15 | private static final ReentrantLock LOCK = new ReentrantLock(); 16 | 17 | @Override 18 | public void assignType(ScriptDump scriptDump) { 19 | LOCK.lock(); 20 | for (Clazz recordedClass : scriptDump.recordedClasses) { 21 | try { 22 | if (!recordedClass.original.isEnum()) continue; 23 | EnumTypeInfo typeWrapper = (EnumTypeInfo) TypeInfo.of(recordedClass.original); 24 | BaseType[] types = typeWrapper.enumConstants() 25 | .stream() 26 | .map(EnumTypeInfo::getName) 27 | .map(String::toLowerCase) 28 | .map(Types::literal) 29 | .toArray(BaseType[]::new); 30 | scriptDump.assignType(recordedClass.classPath, Types.or(types)); 31 | } catch (Throwable ignore) { 32 | } 33 | } 34 | LOCK.unlock(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/docs/assignments/InterfaceTypes.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.docs.assignments; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.Clazz; 4 | import moe.wolfgirl.probejs.lang.java.clazz.members.MethodInfo; 5 | import moe.wolfgirl.probejs.lang.java.clazz.members.ParamInfo; 6 | import moe.wolfgirl.probejs.lang.transpiler.TypeConverter; 7 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 8 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 9 | import moe.wolfgirl.probejs.lang.typescript.code.type.js.JSLambdaType; 10 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 11 | 12 | import java.util.Optional; 13 | 14 | public class InterfaceTypes extends ProbeJSPlugin { 15 | @Override 16 | public void assignType(ScriptDump scriptDump) { 17 | TypeConverter converter = scriptDump.transpiler.typeConverter; 18 | for (Clazz recordedClass : scriptDump.recordedClasses) { 19 | if (!recordedClass.attribute.isInterface) continue; 20 | Optional optionalMethodInfo = recordedClass.methods 21 | .stream() 22 | .filter(methodInfo -> methodInfo.attributes.isAbstract) 23 | .findFirst(); 24 | long count = recordedClass.methods.stream().filter(m -> m.attributes.isAbstract).count(); 25 | if (optionalMethodInfo.isEmpty()) continue; 26 | if (count != 1) continue; 27 | 28 | MethodInfo methodInfo = optionalMethodInfo.get(); 29 | JSLambdaType.Builder builder = Types.lambda(); 30 | for (ParamInfo param : methodInfo.params) { 31 | builder.param(param.name, converter.convertType(param.type)); 32 | } 33 | builder.returnType(converter.convertType(methodInfo.returnType)); 34 | scriptDump.assignType(recordedClass.classPath, builder.build()); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/docs/assignments/RecordTypes.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.docs.assignments; 2 | 3 | import dev.latvian.mods.rhino.type.ParameterizedTypeInfo; 4 | import dev.latvian.mods.rhino.type.RecordTypeInfo; 5 | import dev.latvian.mods.rhino.type.TypeInfo; 6 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 7 | import moe.wolfgirl.probejs.lang.java.clazz.Clazz; 8 | import moe.wolfgirl.probejs.lang.transpiler.TypeConverter; 9 | import moe.wolfgirl.probejs.lang.transpiler.transformation.InjectSpecialType; 10 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 11 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 12 | import moe.wolfgirl.probejs.lang.typescript.code.type.TSParamType; 13 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 14 | import moe.wolfgirl.probejs.lang.typescript.code.type.js.JSArrayType; 15 | import moe.wolfgirl.probejs.lang.typescript.code.type.js.JSObjectType; 16 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 17 | 18 | public class RecordTypes extends ProbeJSPlugin { 19 | 20 | @Override 21 | public void assignType(ScriptDump scriptDump) { 22 | TypeConverter converter = scriptDump.transpiler.typeConverter; 23 | for (Clazz recordedClass : scriptDump.recordedClasses) { 24 | if (!recordedClass.original.isRecord()) continue; 25 | RecordTypeInfo typeWrapper = (RecordTypeInfo) TypeInfo.of(recordedClass.original); 26 | 27 | JSObjectType.Builder objectType = Types.object(); 28 | JSArrayType.Builder arrayType = Types.arrayOf(); 29 | 30 | for (RecordTypeInfo.Component component : typeWrapper.recordComponents().values()) { 31 | BaseType type = converter.convertType(component.type()); 32 | 33 | if (component.type() instanceof ParameterizedTypeInfo parameterizedTypeInfo) { 34 | if (InjectSpecialType.NO_WRAPPING.contains(new ClassPath(parameterizedTypeInfo.rawType().asClass()))) { 35 | if (type instanceof TSParamType paramType) { 36 | paramType.params.replaceAll(baseType -> Types.ignoreContext(baseType, BaseType.FormatType.RETURN)); 37 | } 38 | } 39 | } 40 | 41 | objectType.member(component.name(), true, type); 42 | arrayType.member(component.name(), true, type); 43 | } 44 | 45 | scriptDump.assignType(recordedClass.classPath, objectType.build()); 46 | scriptDump.assignType(recordedClass.classPath, arrayType.build()); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/docs/assignments/SpecialTypes.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.docs.assignments; 2 | 3 | import moe.wolfgirl.probejs.GameStates; 4 | import moe.wolfgirl.probejs.ProbeJS; 5 | import moe.wolfgirl.probejs.lang.snippet.SnippetDump; 6 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 7 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 8 | import moe.wolfgirl.probejs.lang.typescript.code.member.TypeDecl; 9 | import moe.wolfgirl.probejs.lang.typescript.code.ts.Wrapped; 10 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 11 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 12 | 13 | import java.util.Collection; 14 | import java.util.List; 15 | import java.util.stream.Collectors; 16 | 17 | public class SpecialTypes extends ProbeJSPlugin { 18 | @Override 19 | public void addGlobals(ScriptDump scriptDump) { 20 | Wrapped.Namespace special = new Wrapped.Namespace("Special"); 21 | 22 | // We define special types regardless of script type 23 | // because types might be sent to other scripts 24 | defineLiteralTypes(special, "LangKey", List.of("probejs$$translation")); 25 | defineLiteralTypes(special, "RecipeId", List.of("probejs$$recipeId")); 26 | defineLiteralTypes(special, "LootTable", GameStates.LOOT_TABLES); 27 | defineLiteralTypes(special, "Mod", List.of("probejs$$mod")); 28 | scriptDump.addGlobal("special_types", special); 29 | } 30 | 31 | @Override 32 | public void addVSCodeSnippets(SnippetDump dump) { 33 | defineLiteralSnippets(dump, "lang_key", GameStates.LANG_KEYS.get()); 34 | defineLiteralSnippets(dump, "recipe_id", GameStates.RECIPE_IDS.keySet()); 35 | defineLiteralSnippets(dump, "loot_table", GameStates.LOOT_TABLES); 36 | defineLiteralSnippets(dump, "texture", GameStates.TEXTURES.get()); 37 | defineLiteralSnippets(dump, "mod", GameStates.MODS.get()); 38 | } 39 | 40 | private static void defineLiteralTypes(Wrapped.Namespace special, String symbol, Collection literals) { 41 | BaseType[] types = literals.stream().map(Types::literal).toArray(BaseType[]::new); 42 | TypeDecl declaration = new TypeDecl(symbol, Types.or(types)); 43 | special.addCode(declaration); 44 | } 45 | 46 | private static void defineLiteralSnippets(SnippetDump dump, String symbol, Collection literals) { 47 | dump.snippet(symbol) 48 | .prefix("@" + symbol) 49 | .choices(literals.stream() 50 | .map(ProbeJS.GSON::toJson) 51 | .collect(Collectors.toSet()) 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/events/ProbeEvents.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.events; 2 | 3 | import dev.latvian.mods.kubejs.event.EventGroup; 4 | import dev.latvian.mods.kubejs.event.EventHandler; 5 | 6 | public interface ProbeEvents { 7 | EventGroup GROUP = EventGroup.of("ProbeEvents"); 8 | EventHandler ASSIGN_TYPE = ProbeEvents.GROUP.client("assignType", () -> TypeAssignmentEventJS.class); 9 | EventHandler MODIFY_DOC = ProbeEvents.GROUP.client("modifyClass", () -> TypingModificationEventJS.class); 10 | EventHandler SNIPPETS = ProbeEvents.GROUP.client("snippets", () -> SnippetGenerationEventJS.class); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/events/ScriptEventJS.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.events; 2 | 3 | import dev.latvian.mods.kubejs.event.KubeEvent; 4 | import dev.latvian.mods.kubejs.script.ScriptType; 5 | import moe.wolfgirl.probejs.lang.transpiler.TypeConverter; 6 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 7 | 8 | public class ScriptEventJS implements KubeEvent { 9 | protected final ScriptDump dump; 10 | 11 | public ScriptEventJS(ScriptDump dump) { 12 | this.dump = dump; 13 | } 14 | 15 | public ScriptType getScriptType() { 16 | return dump.scriptType; 17 | } 18 | 19 | public TypeConverter getTypeConverter() { 20 | return dump.transpiler.typeConverter; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/events/SnippetGenerationEventJS.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.events; 2 | 3 | import dev.latvian.mods.kubejs.event.KubeEvent; 4 | import moe.wolfgirl.probejs.lang.snippet.Snippet; 5 | import moe.wolfgirl.probejs.lang.snippet.SnippetDump; 6 | 7 | import java.util.function.Consumer; 8 | 9 | public class SnippetGenerationEventJS implements KubeEvent { 10 | 11 | private final SnippetDump dump; 12 | 13 | public SnippetGenerationEventJS(SnippetDump dump) { 14 | this.dump = dump; 15 | } 16 | 17 | public void create(String name, Consumer handler) { 18 | handler.accept(dump.snippet(name)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/events/TypeAssignmentEventJS.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.events; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 4 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 5 | 6 | public class TypeAssignmentEventJS extends ScriptEventJS { 7 | 8 | public TypeAssignmentEventJS(ScriptDump scriptDump) { 9 | super(scriptDump); 10 | } 11 | 12 | public void assignType(Class clazz, BaseType baseType) { 13 | dump.assignType(clazz, baseType); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/events/TypingModificationEventJS.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.events; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 4 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 5 | import moe.wolfgirl.probejs.lang.typescript.TypeScriptFile; 6 | 7 | import java.util.Map; 8 | import java.util.function.Consumer; 9 | 10 | public class TypingModificationEventJS extends ScriptEventJS { 11 | 12 | private final Map files; 13 | 14 | public TypingModificationEventJS(ScriptDump dump, Map files) { 15 | super(dump); 16 | this.files = files; 17 | } 18 | 19 | public void modify(Class clazz, Consumer file) { 20 | TypeScriptFile ts = files.get(new ClassPath(clazz)); 21 | if (ts != null) file.accept(ts); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/features/ProbeJSEndpoints.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.features; 2 | 3 | import com.google.common.collect.ArrayListMultimap; 4 | import com.google.common.collect.Multimap; 5 | import com.google.gson.JsonObject; 6 | import dev.latvian.apps.tinyserver.http.response.HTTPResponse; 7 | import dev.latvian.mods.kubejs.web.KJSHTTPRequest; 8 | import dev.latvian.mods.kubejs.web.LocalWebServerRegistry; 9 | import moe.wolfgirl.probejs.GameStates; 10 | import moe.wolfgirl.probejs.ProbeJS; 11 | import net.minecraft.client.Minecraft; 12 | import net.minecraft.client.resources.language.ClientLanguage; 13 | import net.minecraft.locale.Language; 14 | 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | import java.util.UUID; 18 | 19 | public class ProbeJSEndpoints { 20 | private static final UUID IDENTITY = UUID.randomUUID(); 21 | 22 | private static HTTPResponse recipeIds(KJSHTTPRequest req) { 23 | Multimap typeIds = ArrayListMultimap.create(); 24 | 25 | for (Map.Entry entry : GameStates.RECIPE_IDS.entrySet()) { 26 | String key = entry.getKey(); 27 | JsonObject recipeJson = entry.getValue(); 28 | String recipeType = recipeJson.has("type") ? entry.getValue().get("type").getAsString() : "unknown"; 29 | if (!recipeType.contains(":")) recipeType = "minecraft:" + recipeType; 30 | typeIds.put(recipeType, key); 31 | } 32 | 33 | return HTTPResponse.ok().json(ProbeJS.GSON.toJson(typeIds.asMap())); 34 | } 35 | 36 | private static HTTPResponse langKeys(KJSHTTPRequest req) { 37 | if (Language.getInstance() instanceof ClientLanguage clientLanguage) { 38 | return HTTPResponse.ok().json(ProbeJS.GSON.toJson(clientLanguage.storage)); 39 | } else { 40 | return HTTPResponse.noContent(); 41 | } 42 | } 43 | 44 | private static HTTPResponse getMissingLangKeys(KJSHTTPRequest req) { 45 | if (Language.getInstance() instanceof ClientLanguage clientLanguage) { 46 | if (Minecraft.getInstance().getLanguageManager().getSelected().equals(Language.DEFAULT)) { 47 | return HTTPResponse.ok().json(ProbeJS.GSON.toJson(Map.of())); 48 | } 49 | 50 | ClientLanguage defaultLanguage = GameStates.DEFAULT_LANGUAGE.get(); 51 | Map missingInCurrent = new HashMap<>(); 52 | 53 | for (String s : defaultLanguage.storage.keySet()) { 54 | if (clientLanguage.getOrDefault(s).equals(defaultLanguage.getOrDefault(s))) { 55 | missingInCurrent.put(s, clientLanguage.getOrDefault(s)); 56 | } 57 | } 58 | return HTTPResponse.ok().json(ProbeJS.GSON.toJson(missingInCurrent)); 59 | } else { 60 | return HTTPResponse.noContent(); 61 | } 62 | } 63 | 64 | private static HTTPResponse getRecipeJson(KJSHTTPRequest req) { 65 | var recipeId = req.query("recipe-id").value(); 66 | if (recipeId != null && GameStates.RECIPE_IDS.containsKey(recipeId)) { 67 | return HTTPResponse.ok().json(ProbeJS.GSON.toJson(GameStates.RECIPE_IDS.get(recipeId))); 68 | } else { 69 | return HTTPResponse.noContent(); 70 | } 71 | } 72 | 73 | private static HTTPResponse getIdentity(KJSHTTPRequest req) { 74 | return HTTPResponse.ok().json(ProbeJS.GSON.toJson(IDENTITY.toString())); 75 | } 76 | 77 | public static void register(LocalWebServerRegistry registry) { 78 | registry.get("/api/probejs/recipe-ids", ProbeJSEndpoints::recipeIds); 79 | registry.get("/api/probejs/recipe-id", ProbeJSEndpoints::getRecipeJson); 80 | registry.get("/api/probejs/lang-keys", ProbeJSEndpoints::langKeys); 81 | registry.get("/api/probejs/missing-lang-keys", ProbeJSEndpoints::getMissingLangKeys); 82 | registry.get("/api/probejs/identity", ProbeJSEndpoints::getIdentity); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/features/ProbeJSTelemetry.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.features; 2 | 3 | 4 | /** 5 | * The class to collect anonymous data from every triggering of the ProbeJS dump. 6 | *
7 | * For now, it will collect the mod counts, total size of file generated, and size of each registry. 8 | */ 9 | public class ProbeJSTelemetry { 10 | public static final String SERVER_ENDPOINT = "https://api.wolfgirl.moe/probejs-telemetry"; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/features/SchemaDownloader.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.features; 2 | 3 | import dev.latvian.mods.kubejs.KubeJSPaths; 4 | import dev.latvian.mods.kubejs.recipe.schema.RecipeNamespace; 5 | import dev.latvian.mods.kubejs.recipe.schema.RecipeSchemaType; 6 | import dev.latvian.mods.kubejs.recipe.schema.UnknownRecipeSchemaType; 7 | import dev.latvian.mods.kubejs.server.ServerScriptManager; 8 | import moe.wolfgirl.probejs.utils.GameUtils; 9 | 10 | import java.io.BufferedOutputStream; 11 | import java.io.IOException; 12 | import java.net.*; 13 | import java.nio.file.Files; 14 | import java.nio.file.Path; 15 | import java.util.HashSet; 16 | import java.util.Map; 17 | import java.util.Set; 18 | import java.util.zip.ZipEntry; 19 | import java.util.zip.ZipInputStream; 20 | 21 | /** 22 | * Downloads **safe** schema packs from GitHub 23 | */ 24 | public class SchemaDownloader { 25 | private static final String GAME_VERSION = "1.21"; 26 | private static final String BASE_URL = "https://github.com/Prunoideae/-recipes/zipball/%s"; 27 | 28 | 29 | private final Set unsupportedRecipes = new HashSet<>(); 30 | 31 | public SchemaDownloader() { 32 | // Get unsupported script types 33 | ServerScriptManager manager = GameUtils.getServerScriptManager(); 34 | if (manager == null) return; 35 | 36 | for (Map.Entry entry : manager.recipeSchemaStorage.namespaces.entrySet()) { 37 | String namespace = entry.getKey(); 38 | RecipeNamespace rn = entry.getValue(); 39 | for (Map.Entry e : rn.entrySet()) { 40 | String id = e.getKey(); 41 | RecipeSchemaType schema = e.getValue(); 42 | if (schema instanceof UnknownRecipeSchemaType) { 43 | unsupportedRecipes.add("%s:%s".formatted(namespace, id)); 44 | } 45 | } 46 | } 47 | } 48 | 49 | public ZipInputStream openSchemaStream() throws URISyntaxException, IOException { 50 | String downloadUrl = BASE_URL.formatted(GAME_VERSION); 51 | URL url = (new URI(downloadUrl)).toURL(); 52 | URLConnection connection = url.openConnection(); 53 | return new ZipInputStream(connection.getInputStream()); 54 | } 55 | 56 | public void processFile(ZipInputStream inputStream) throws IOException { 57 | for (ZipEntry entry = inputStream.getNextEntry(); entry != null; entry = inputStream.getNextEntry()) { 58 | if (entry.isDirectory()) continue; 59 | String fileName = entry.getName(); 60 | if (!fileName.endsWith(".json")) continue; 61 | fileName = fileName.substring(0, fileName.length() - 5); 62 | 63 | // Get the last two components of the entry 64 | // So ../../minecraft/smoking.json -> minecraft smoking 65 | String[] parts = fileName.split("/"); 66 | if (parts.length < 2) continue; 67 | String namespace = parts[parts.length - 2]; 68 | String recipe = parts[parts.length - 1]; 69 | if (!unsupportedRecipes.contains("%s:%s".formatted(namespace, recipe))) continue; 70 | 71 | Path schemaJson = validateSchemaPath(namespace, recipe); 72 | try (var writer = new BufferedOutputStream(Files.newOutputStream(schemaJson))) { 73 | writer.write(inputStream.readAllBytes()); 74 | } 75 | } 76 | } 77 | 78 | public Path validateSchemaPath(String namespace, String recipe) throws IOException { 79 | Path dirPath = KubeJSPaths.DATA.resolve("%s/kubejs/recipe_schema".formatted(namespace)); 80 | if (!Files.exists(dirPath)) Files.createDirectories(dirPath); 81 | return dirPath.resolve("%s.json".formatted(recipe)); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/decompiler/ProbeClassScanner.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.decompiler; 2 | 3 | import org.jetbrains.java.decompiler.main.extern.IContextSource; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.zip.ZipEntry; 10 | import java.util.zip.ZipFile; 11 | 12 | public class ProbeClassScanner { 13 | private final Set> scannedClasses = new HashSet<>(); 14 | 15 | public void acceptFile(File file) throws IOException { 16 | ClassLoader loader = getClass().getClassLoader(); 17 | try (var jarFile = new ZipFile(file)) { 18 | var entries = jarFile.entries(); 19 | while (entries.hasMoreElements()) { 20 | ZipEntry entry = entries.nextElement(); 21 | if (entry.isDirectory()) continue; 22 | String name = entry.getName(); 23 | if (!name.endsWith(IContextSource.CLASS_SUFFIX)) continue; 24 | name = name.substring(0, name.length() - IContextSource.CLASS_SUFFIX.length()); 25 | name = name.replace("/", "."); 26 | try { 27 | // Skipping due to mojang is weird 28 | if (name.contains("com.mojang.blaze3d.systems.TimerQuery")) continue; 29 | scannedClasses.add(Class.forName(name, false, loader)); 30 | } catch (Throwable ignore) { 31 | } 32 | } 33 | } 34 | } 35 | 36 | public Set> getScannedClasses() { 37 | return scannedClasses; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/decompiler/ProbeClassSource.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.decompiler; 2 | 3 | import org.jetbrains.java.decompiler.main.extern.IContextSource; 4 | import org.jetbrains.java.decompiler.main.extern.IResultSaver; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.*; 9 | 10 | public class ProbeClassSource implements IContextSource { 11 | 12 | private final Map> classes; 13 | 14 | public ProbeClassSource(Collection> classes) { 15 | this.classes = new HashMap<>(); 16 | for (Class clazz : classes) { 17 | this.classes.put(clazz.getName().replace(".", "/"), clazz); 18 | } 19 | } 20 | 21 | @Override 22 | public String getName() { 23 | return ProbeClassSource.class.getName(); 24 | } 25 | 26 | @Override 27 | public Entries getEntries() { 28 | List entries = classes.keySet().stream().map(Entry::atBase).toList(); 29 | return new Entries(entries, List.of(), List.of(), List.of()); 30 | } 31 | 32 | @Override 33 | public InputStream getInputStream(String resource) throws IOException { 34 | Class clazz = classes.get(resource.substring(0, resource.length() - IContextSource.CLASS_SUFFIX.length())); 35 | return clazz.getClassLoader().getResourceAsStream(resource); 36 | } 37 | 38 | @Override 39 | public IOutputSink createOutputSink(IResultSaver saver) { 40 | return new IOutputSink() { 41 | @Override 42 | public void begin() { 43 | 44 | } 45 | 46 | @Override 47 | public void acceptClass(String qualifiedName, String fileName, String content, int[] mapping) { 48 | saver.saveClassEntry("probejs", "probejs", qualifiedName, fileName, content, mapping); 49 | } 50 | 51 | @Override 52 | public void acceptDirectory(String directory) { 53 | 54 | } 55 | 56 | @Override 57 | public void acceptOther(String path) { 58 | 59 | } 60 | 61 | @Override 62 | public void close() throws IOException { 63 | 64 | } 65 | }; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/decompiler/ProbeDecompiler.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.decompiler; 2 | 3 | import moe.wolfgirl.probejs.ProbeJS; 4 | import net.neoforged.fml.ModList; 5 | import net.neoforged.fml.loading.LoadingModList; 6 | import org.jetbrains.java.decompiler.main.Fernflower; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.nio.file.Path; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Objects; 14 | import java.util.stream.Collectors; 15 | 16 | public class ProbeDecompiler { 17 | public static List findModFiles() { 18 | ModList modList = ModList.get(); 19 | return modList.getModFiles().stream() 20 | .map(fileInfo -> fileInfo.getFile().getFilePath()) 21 | .map(path -> { 22 | try { 23 | return path.toFile(); 24 | } catch (Exception ignore) { 25 | return null; 26 | } 27 | }) 28 | .filter(Objects::nonNull) 29 | .collect(Collectors.toList()); 30 | } 31 | 32 | public final ProbeFileSaver resultSaver; 33 | public final ProbeClassScanner scanner; 34 | 35 | public ProbeDecompiler() { 36 | this.resultSaver = new ProbeFileSaver(); 37 | this.scanner = new ProbeClassScanner(); 38 | } 39 | 40 | public void addRuntimeSource(File source) { 41 | try { 42 | scanner.acceptFile(source); 43 | } catch (IOException e) { 44 | ProbeJS.LOGGER.error("Unable to load file: %s".formatted(source)); 45 | } 46 | } 47 | 48 | public void fromMods() { 49 | for (File modFile : findModFiles()) { 50 | addRuntimeSource(modFile); 51 | } 52 | } 53 | 54 | public void decompileContext() { 55 | Fernflower engine = new Fernflower( 56 | resultSaver, Map.of(), 57 | new ProbeDecompilerLogger() 58 | ); 59 | ProbeClassSource source = new ProbeClassSource(scanner.getScannedClasses()); 60 | engine.addSource(source); 61 | 62 | resultSaver.classCount = 0; 63 | try { 64 | engine.decompileContext(); 65 | } finally { 66 | engine.clearContext(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/decompiler/ProbeDecompilerLogger.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.decompiler; 2 | 3 | import moe.wolfgirl.probejs.ProbeJS; 4 | import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; 5 | 6 | public class ProbeDecompilerLogger extends IFernflowerLogger { 7 | @Override 8 | public void writeMessage(String message, Severity severity) { 9 | ProbeJS.LOGGER.info(message); 10 | } 11 | 12 | @Override 13 | public void writeMessage(String message, Severity severity, Throwable t) { 14 | ProbeJS.LOGGER.info(message); 15 | } 16 | 17 | @Override 18 | public void startProcessingClass(String className) { 19 | ProbeJS.LOGGER.info("Started processing: %s".formatted(className)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/decompiler/ProbeFileSaver.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.decompiler; 2 | 3 | import moe.wolfgirl.probejs.lang.decompiler.parser.ParsedDocument; 4 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 5 | import org.jetbrains.java.decompiler.main.extern.IResultSaver; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.util.*; 11 | import java.util.jar.Manifest; 12 | import java.util.stream.Collectors; 13 | 14 | public class ProbeFileSaver implements IResultSaver { 15 | public final Map result = new HashMap<>(); 16 | private Runnable callback; 17 | public int classCount = 0; 18 | 19 | @Override 20 | public void saveFolder(String path) { 21 | 22 | } 23 | 24 | @Override 25 | public void copyFile(String source, String path, String entryName) { 26 | 27 | } 28 | 29 | @Override 30 | public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) { 31 | String[] parts = qualifiedName.split("/"); 32 | parts[parts.length - 1] = "$" + parts[parts.length - 1]; 33 | ClassPath classPath = new ClassPath(List.of(parts)); 34 | result.put(classPath, new ParsedDocument(content)); 35 | classCount++; 36 | if (callback != null) callback.run(); 37 | } 38 | 39 | @Override 40 | public void createArchive(String path, String archiveName, Manifest manifest) { 41 | 42 | } 43 | 44 | @Override 45 | public void saveDirEntry(String path, String archiveName, String entryName) { 46 | 47 | } 48 | 49 | @Override 50 | public void copyEntry(String source, String path, String archiveName, String entry) { 51 | 52 | } 53 | 54 | @Override 55 | public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) { 56 | ClassPath classPath = new ClassPath(qualifiedName.replace("/", ".")); 57 | result.put(classPath, new ParsedDocument(content)); 58 | classCount++; 59 | if (callback != null) callback.run(); 60 | } 61 | 62 | @Override 63 | public void closeArchive(String path, String archiveName) { 64 | 65 | } 66 | 67 | public void writeTo(Path base) throws IOException { 68 | for (Map.Entry entry : result.entrySet()) { 69 | ClassPath classPath = entry.getKey(); 70 | ParsedDocument s = entry.getValue(); 71 | s.getParamTransformations(); 72 | 73 | Path full = classPath.makePath(base); 74 | try (var out = Files.newBufferedWriter(full.resolve(classPath.getName() + ".java"))) { 75 | String[] lines = s.getCode().split("\\n"); 76 | out.write(Arrays.stream(lines) 77 | .filter(l -> !l.strip().startsWith("// $VF: renamed")) 78 | .collect(Collectors.joining("\n")) 79 | ); 80 | } 81 | } 82 | } 83 | 84 | public Set> getClasses() { 85 | Set> classes = new HashSet<>(); 86 | for (Map.Entry entry : result.entrySet()) { 87 | ClassPath classPath = entry.getKey(); 88 | ParsedDocument parsedDocument = entry.getValue(); 89 | 90 | if (parsedDocument.isMixinClass()) continue; 91 | try { 92 | classes.add(classPath.forName()); 93 | } catch (Throwable ignored) { 94 | } 95 | } 96 | return classes; 97 | } 98 | 99 | public ProbeFileSaver callback(Runnable callback) { 100 | this.callback = callback; 101 | return this; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/decompiler/parser/ParsedDocument.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.decompiler.parser; 2 | 3 | import com.github.javaparser.JavaParser; 4 | import com.github.javaparser.ast.CompilationUnit; 5 | import com.github.javaparser.ast.body.BodyDeclaration; 6 | import com.github.javaparser.ast.body.CallableDeclaration; 7 | import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; 8 | import com.github.javaparser.ast.body.Parameter; 9 | import com.github.javaparser.ast.expr.AnnotationExpr; 10 | import moe.wolfgirl.probejs.utils.NameUtils; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | public class ParsedDocument { 16 | 17 | private static final JavaParser PARSER = new JavaParser(); 18 | private static final String PARAM_SRG = "^p_[a-zA-Z0-9]+_$"; 19 | 20 | private final CompilationUnit parsed; 21 | private final Map paramRenames = new HashMap<>(); 22 | 23 | public ParsedDocument(String content) { 24 | this.parsed = PARSER.parse(content).getResult().orElseThrow(); 25 | } 26 | 27 | public void getParamTransformations() { 28 | for (ClassOrInterfaceDeclaration classDecl : parsed.findAll(ClassOrInterfaceDeclaration.class)) { 29 | for (BodyDeclaration member : classDecl.getMembers()) { 30 | if (member instanceof CallableDeclaration callable) { 31 | int order = 0; 32 | for (Parameter parameter : callable.getParameters()) { 33 | if (!parameter.getNameAsString().matches(PARAM_SRG)) continue; 34 | String[] types = NameUtils.extractAlphabets(parameter.getTypeAsString()); 35 | paramRenames.put(parameter.getNameAsString(), "%s%s".formatted(NameUtils.asCamelCase(types), order)); 36 | order++; 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | public String getCode() { 44 | String content = this.parsed.toString(); 45 | for (Map.Entry entry : paramRenames.entrySet()) { 46 | String original = entry.getKey(); 47 | String renamed = entry.getValue(); 48 | content = content.replace(original, renamed); 49 | } 50 | return content; 51 | } 52 | 53 | public boolean isMixinClass() { 54 | for (ClassOrInterfaceDeclaration classDecl : parsed.findAll(ClassOrInterfaceDeclaration.class)) { 55 | for (AnnotationExpr annotation : classDecl.getAnnotations()) { 56 | if (annotation.getNameAsString().equals("Mixin")) return true; 57 | } 58 | } 59 | return false; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/java/TypeAdapter.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.java; 2 | 3 | import dev.latvian.mods.rhino.type.ArrayTypeInfo; 4 | import dev.latvian.mods.rhino.type.ParameterizedTypeInfo; 5 | import dev.latvian.mods.rhino.type.TypeInfo; 6 | import dev.latvian.mods.rhino.type.VariableTypeInfo; 7 | 8 | import java.util.Arrays; 9 | 10 | public class TypeAdapter { 11 | public static TypeInfo consolidateType(TypeInfo in, String symbol, TypeInfo replacement) { 12 | return switch (in) { 13 | case VariableTypeInfo variableTypeInfo -> variableTypeInfo.getName().equals(symbol) ? replacement : in; 14 | case ArrayTypeInfo arrayTypeInfo -> 15 | consolidateType(arrayTypeInfo.componentType(), symbol, replacement).asArray(); 16 | case ParameterizedTypeInfo parameterizedTypeInfo -> parameterizedTypeInfo.rawType().withParams( 17 | Arrays.stream(parameterizedTypeInfo.params()) 18 | .map(p -> consolidateType(p, symbol, replacement)) 19 | .toArray(TypeInfo[]::new) 20 | ); 21 | case null, default -> in; 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/java/base/AnnotationHolder.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.java.base; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | public class AnnotationHolder { 8 | private final Annotation[] annotations; 9 | 10 | public AnnotationHolder(Annotation[] annotations) { 11 | this.annotations = annotations; 12 | } 13 | 14 | public Annotation[] getAnnotations() { 15 | return annotations; 16 | } 17 | 18 | public boolean hasAnnotation(Class annotation) { 19 | return Arrays.stream(annotations).anyMatch(annotation::isInstance); 20 | } 21 | 22 | @SuppressWarnings("unchecked") 23 | public List getAnnotations(Class type) { 24 | return Arrays.stream(annotations) 25 | .filter(type::isInstance) 26 | .map(a -> (T) a) 27 | .toList(); 28 | } 29 | 30 | public T getAnnotation(Class type) { 31 | var annotations = getAnnotations(type); 32 | return annotations.isEmpty() ? null : annotations.getFirst(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/java/base/ClassProvider.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.java.base; 2 | 3 | public interface ClassProvider { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/java/base/TypeVariableHolder.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.java.base; 2 | 3 | import dev.latvian.mods.rhino.type.TypeInfo; 4 | import dev.latvian.mods.rhino.type.VariableTypeInfo; 5 | 6 | import java.lang.annotation.Annotation; 7 | import java.lang.reflect.TypeVariable; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | 12 | public abstract class TypeVariableHolder extends AnnotationHolder { 13 | public final List variableTypes; 14 | 15 | public TypeVariableHolder(TypeVariable[] variables, Annotation[] annotations) { 16 | super(annotations); 17 | this.variableTypes = Arrays.stream(variables) 18 | .map(TypeInfo::of) 19 | .map(t -> (VariableTypeInfo) t) 20 | .collect(Collectors.toList()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/java/clazz/ClassPath.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.java.clazz; 2 | 3 | import dev.latvian.mods.rhino.util.HideFromJS; 4 | 5 | import java.io.IOException; 6 | import java.lang.reflect.TypeVariable; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | public record ClassPath(List parts) { 14 | private static List transformJavaClass(Class clazz) { 15 | String name = clazz.getName(); 16 | String[] parts = name.split("\\."); 17 | return Arrays.stream(parts).toList(); 18 | } 19 | 20 | public ClassPath(String className) { 21 | this(Arrays.stream(className.split("\\.")).toList()); 22 | } 23 | 24 | public ClassPath(Class clazz) { 25 | this(transformJavaClass(clazz)); 26 | } 27 | 28 | public String getName() { 29 | return "$" + parts.getLast(); 30 | } 31 | 32 | public String getConcatenated(String sep) { 33 | return String.join(sep, parts); 34 | } 35 | 36 | public String getClassPath() { 37 | return getConcatenated("."); 38 | } 39 | 40 | public String getClassPathJava() { 41 | List copy = new ArrayList<>(parts); 42 | String last = copy.getLast(); 43 | if (last.startsWith("$")) last = last.substring(1); 44 | copy.set(copy.size() - 1, last); 45 | return String.join(".", copy); 46 | } 47 | 48 | public String getTypeScriptPath() { 49 | return getConcatenated("."); 50 | } 51 | 52 | @HideFromJS 53 | public Class forName() throws ClassNotFoundException { 54 | return Class.forName(getClassPathJava()); 55 | } 56 | 57 | public List getGenerics() throws ClassNotFoundException { 58 | TypeVariable[] variables = forName().getTypeParameters(); 59 | return Arrays.stream(variables).map(TypeVariable::getName).toList(); 60 | } 61 | 62 | public List getPackage() { 63 | List classPath = new ArrayList<>(parts); 64 | classPath.removeLast(); 65 | return classPath; 66 | } 67 | 68 | public String getConcatenatedPackage(String sep) { 69 | return String.join(sep, getPackage()); 70 | } 71 | 72 | public Path getDirPath(Path base) { 73 | return base.resolve(getConcatenatedPackage("/")); 74 | } 75 | 76 | public Path makePath(Path base) { 77 | Path full = getDirPath(base); 78 | if (Files.notExists(full)) { 79 | try { 80 | Files.createDirectories(full); 81 | } catch (IOException e) { 82 | throw new RuntimeException(e); 83 | } 84 | } 85 | return full; 86 | } 87 | 88 | public String getFileKey() { 89 | return String.join(".", parts.subList(0, Math.min(4, parts.size()))); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/java/clazz/members/ConstructorInfo.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.java.clazz.members; 2 | 3 | import dev.latvian.mods.rhino.CachedConstructorInfo; 4 | import dev.latvian.mods.rhino.CachedParameters; 5 | import dev.latvian.mods.rhino.type.TypeInfo; 6 | import moe.wolfgirl.probejs.lang.java.base.TypeVariableHolder; 7 | 8 | import java.lang.reflect.Constructor; 9 | import java.lang.reflect.Parameter; 10 | import java.util.*; 11 | 12 | public class ConstructorInfo extends TypeVariableHolder { 13 | 14 | public final List params; 15 | 16 | public ConstructorInfo(CachedConstructorInfo constructor, Constructor original) { 17 | super(original.getTypeParameters(), original.getAnnotations()); 18 | this.params = new ArrayList<>(); 19 | 20 | CachedParameters parameters = constructor.getParameters(); 21 | Parameter[] originalParams = original.getParameters(); 22 | 23 | if (parameters.firstArgContext()) { 24 | for (int i = 1; i < originalParams.length; i++) { 25 | Parameter parameter = originalParams[i]; 26 | TypeInfo typeInfo = parameters.typeInfos().get(i - 1); 27 | params.add(new ParamInfo(parameter.getName(), typeInfo, parameter.isVarArgs())); 28 | } 29 | } else { 30 | for (int i = 0; i < originalParams.length; i++) { 31 | Parameter parameter = originalParams[i]; 32 | TypeInfo typeInfo = parameters.typeInfos().get(i); 33 | params.add(new ParamInfo(parameter.getName(), typeInfo, parameter.isVarArgs())); 34 | } 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/java/clazz/members/FieldInfo.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.java.clazz.members; 2 | 3 | import dev.latvian.mods.rhino.CachedFieldInfo; 4 | import dev.latvian.mods.rhino.type.TypeInfo; 5 | import moe.wolfgirl.probejs.lang.java.base.AnnotationHolder; 6 | 7 | import java.lang.reflect.Field; 8 | import java.lang.reflect.Modifier; 9 | 10 | public class FieldInfo extends AnnotationHolder { 11 | public final String name; 12 | public final TypeInfo type; 13 | public final FieldAttributes attributes; 14 | 15 | public FieldInfo(String name, CachedFieldInfo fieldInfo, Field original) { 16 | super(original.getAnnotations()); 17 | this.name = name; 18 | this.type = fieldInfo.getType(); 19 | this.attributes = new FieldAttributes(original); 20 | } 21 | 22 | public static class FieldAttributes { 23 | public final boolean isFinal; 24 | public final boolean isStatic; 25 | private final Field field; 26 | 27 | public FieldAttributes(Field field) { 28 | int modifiers = field.getModifiers(); 29 | this.isFinal = Modifier.isFinal(modifiers); 30 | this.isStatic = Modifier.isStatic(modifiers); 31 | this.field = field; 32 | } 33 | 34 | public Object getStaticValue() throws IllegalAccessException { 35 | if (!isStatic) throw new RuntimeException("The field is not static!"); 36 | return field.get(null); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/java/clazz/members/MethodInfo.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.java.clazz.members; 2 | 3 | import dev.latvian.mods.rhino.CachedMethodInfo; 4 | import dev.latvian.mods.rhino.CachedParameters; 5 | import dev.latvian.mods.rhino.Context; 6 | import dev.latvian.mods.rhino.type.TypeInfo; 7 | import moe.wolfgirl.probejs.lang.java.base.TypeVariableHolder; 8 | import moe.wolfgirl.probejs.lang.java.TypeAdapter; 9 | 10 | import java.lang.reflect.*; 11 | import java.util.*; 12 | 13 | public class MethodInfo extends TypeVariableHolder { 14 | public final String name; 15 | public final List params; 16 | public TypeInfo returnType; 17 | public final MethodAttributes attributes; 18 | 19 | public MethodInfo(String name, CachedMethodInfo methodInfo, Method original, Map remapper) { 20 | super(original.getTypeParameters(), original.getAnnotations()); 21 | 22 | this.attributes = new MethodAttributes(original); 23 | this.name = name; 24 | 25 | Parameter[] parameters = original.getParameters(); 26 | CachedParameters cachedParameters = methodInfo.getParameters(); 27 | 28 | this.params = new ArrayList<>(parameters.length); 29 | if (cachedParameters.firstArgContext()) { 30 | for (int i = 1; i < parameters.length; i++) { 31 | Parameter parameter = parameters[i]; 32 | TypeInfo typeInfo = cachedParameters.typeInfos().get(i - 1); 33 | this.params.add(new ParamInfo(parameter.getName(), typeInfo, parameter.isVarArgs())); 34 | } 35 | } else { 36 | for (int i = 0; i < parameters.length; i++) { 37 | Parameter parameter = parameters[i]; 38 | TypeInfo typeInfo = cachedParameters.typeInfos().get(i); 39 | this.params.add(new ParamInfo(parameter.getName(), typeInfo, parameter.isVarArgs())); 40 | } 41 | } 42 | 43 | this.returnType = methodInfo.getReturnType(); 44 | 45 | for (Map.Entry entry : remapper.entrySet()) { 46 | String symbol = entry.getKey(); 47 | TypeInfo replacement = entry.getValue(); 48 | 49 | for (ParamInfo param : this.params) { 50 | param.type = TypeAdapter.consolidateType(param.type, symbol, replacement); 51 | } 52 | this.returnType = TypeAdapter.consolidateType(this.returnType, symbol, replacement); 53 | } 54 | } 55 | 56 | public static class MethodAttributes { 57 | public final boolean isStatic; 58 | /** 59 | * When this appears in a class, remember to translate its type variables because it is from an interface. 60 | */ 61 | public final boolean isDefault; 62 | public final boolean isAbstract; 63 | 64 | public MethodAttributes(Method method) { 65 | int modifiers = method.getModifiers(); 66 | this.isStatic = Modifier.isStatic(modifiers); 67 | this.isDefault = method.isDefault(); 68 | this.isAbstract = Modifier.isAbstract(modifiers); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/java/clazz/members/ParamInfo.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.java.clazz.members; 2 | 3 | import dev.latvian.mods.rhino.type.TypeInfo; 4 | 5 | public class ParamInfo { 6 | public String name; 7 | public TypeInfo type; 8 | public final boolean varArgs; 9 | 10 | public ParamInfo(String name, TypeInfo type, boolean varArgs) { 11 | this.name = name; 12 | this.type = type; 13 | this.varArgs = varArgs; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/linter/LintingWarning.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.linter; 2 | 3 | import com.google.gson.JsonElement; 4 | 5 | import dev.latvian.mods.kubejs.color.KubeColor; 6 | import dev.latvian.mods.kubejs.plugin.builtin.wrapper.ColorWrapper; 7 | import moe.wolfgirl.probejs.ProbeJS; 8 | import net.minecraft.network.chat.Component; 9 | 10 | import java.nio.file.Path; 11 | 12 | public record LintingWarning(Path file, Level level, int line, int column, String message) { 13 | public enum Level { 14 | INFO(ColorWrapper.BLUE), 15 | WARNING(ColorWrapper.GOLD), 16 | ERROR(ColorWrapper.RED); 17 | 18 | public final KubeColor color; 19 | 20 | Level(KubeColor color) { 21 | this.color = color; 22 | } 23 | } 24 | 25 | public Component defaultFormatting(Path relativeBase) { 26 | Path stripped = relativeBase.getParent().relativize(file); 27 | 28 | return Component.literal("[") 29 | .append(Component.literal(level().name()).kjs$color(level().color)) 30 | .append(Component.literal("] ")) 31 | .append(Component.literal(stripped.toString())) 32 | .append(Component.literal(":%d:%d: %s".formatted(line, column, message))); 33 | } 34 | 35 | public JsonElement asPayload() { 36 | return ProbeJS.GSON.toJsonTree(this); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/linter/rules/Rule.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.linter.rules; 2 | 3 | import moe.wolfgirl.probejs.lang.linter.LintingWarning; 4 | 5 | import java.nio.file.Path; 6 | import java.util.List; 7 | 8 | public abstract class Rule { 9 | 10 | /** 11 | * Accepts a file loaded in the linter. 12 | * 13 | * @param path the **absolute** path of the script file 14 | * @param content the file content 15 | */ 16 | public abstract void acceptFile(Path path, List content); 17 | 18 | /** 19 | * Get potential problems / info returned by this rule 20 | * 21 | * @return a list of warnings 22 | */ 23 | public abstract List lint(Path basePath); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/schema/AnyElement.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.schema; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | public class AnyElement extends SchemaElement { 6 | 7 | public static final AnyElement INSTANCE = new AnyElement(); 8 | 9 | private AnyElement() { 10 | 11 | } 12 | 13 | @Override 14 | public String getType() { 15 | return ""; 16 | } 17 | 18 | @Override 19 | protected JsonObject toSchema() { 20 | return null; 21 | } 22 | 23 | @Override 24 | public JsonObject getSchema() { 25 | return new JsonObject(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/schema/ArrayElement.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.schema; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | public class ArrayElement extends SchemaElement { 6 | private final SchemaElement element; 7 | 8 | public ArrayElement(SchemaElement element) { 9 | this.element = element; 10 | } 11 | 12 | @Override 13 | public String getType() { 14 | return "array"; 15 | } 16 | 17 | @Override 18 | protected JsonObject toSchema() { 19 | JsonObject object = new JsonObject(); 20 | object.add("items", element.getSchema()); 21 | return object; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/schema/ObjectElement.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.schema; 2 | 3 | import com.google.gson.JsonObject; 4 | import moe.wolfgirl.probejs.utils.JsonUtils; 5 | 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.function.Function; 11 | 12 | public class ObjectElement extends SchemaElement { 13 | private final Map> members = new HashMap<>(); 14 | private final List requires = new ArrayList<>(); 15 | 16 | public ObjectElement primitive(String name, String type, Function> function) { 17 | members.put(name, function.apply(new PrimitiveElement(type))); 18 | return this; 19 | } 20 | 21 | public ObjectElement stringType(String name) { 22 | return stringType(name, o -> o); 23 | } 24 | 25 | public ObjectElement stringType(String name, Function> function) { 26 | return primitive(name, "string", function); 27 | } 28 | 29 | public ObjectElement numberType(String name) { 30 | return numberType(name, o -> o); 31 | } 32 | 33 | public ObjectElement numberType(String name, Function> function) { 34 | return primitive(name, "number", function); 35 | } 36 | 37 | public ObjectElement booleanType(String name) { 38 | return booleanType(name, o -> o); 39 | } 40 | 41 | public ObjectElement booleanType(String name, Function> function) { 42 | return primitive(name, "boolean", function); 43 | } 44 | 45 | public ObjectElement anyType(String name) { 46 | members.put(name, AnyElement.INSTANCE); 47 | return this; 48 | } 49 | 50 | public ObjectElement object(String name) { 51 | return object(name, o -> o); 52 | } 53 | 54 | public ObjectElement object(String name, Function> function) { 55 | members.put(name, function.apply(new ObjectElement())); 56 | return this; 57 | } 58 | 59 | public ObjectElement requires(String... requires) { 60 | this.requires.addAll(List.of(requires)); 61 | return this; 62 | } 63 | 64 | @Override 65 | public String getType() { 66 | return "object"; 67 | } 68 | 69 | @Override 70 | protected JsonObject toSchema() { 71 | JsonObject object = new JsonObject(); 72 | 73 | var properties = new JsonObject(); 74 | for (Map.Entry> entry : members.entrySet()) { 75 | String key = entry.getKey(); 76 | SchemaElement value = entry.getValue(); 77 | properties.add(key, value.getSchema()); 78 | } 79 | object.add("properties", properties); 80 | object.add("requires", JsonUtils.parseObject(requires)); 81 | 82 | return object; 83 | } 84 | 85 | public static ObjectElement of() { 86 | return new ObjectElement(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/schema/PrimitiveElement.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.schema; 2 | 3 | import com.google.gson.JsonObject; 4 | 5 | public class PrimitiveElement extends SchemaElement { 6 | 7 | private final String type; 8 | 9 | public PrimitiveElement(String type) { 10 | this.type = type; 11 | } 12 | 13 | @Override 14 | public String getType() { 15 | return type; 16 | } 17 | 18 | @Override 19 | protected JsonObject toSchema() { 20 | return new JsonObject(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/schema/SchemaDump.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.schema; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.stream.JsonWriter; 5 | import moe.wolfgirl.probejs.ProbeJS; 6 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 7 | 8 | import java.io.IOException; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | public class SchemaDump { 15 | public Map> schemas = new HashMap<>(); 16 | 17 | public void fromDocs() { 18 | ProbeJSPlugin.forEachPlugin(plugin -> plugin.addJsonSchema(this)); 19 | } 20 | 21 | public void writeTo(Path path) throws IOException { 22 | for (Map.Entry> entry : schemas.entrySet()) { 23 | String key = entry.getKey(); 24 | SchemaElement content = entry.getValue(); 25 | 26 | try (var writer = Files.newBufferedWriter(path.resolve(key + ".json"))) { 27 | JsonWriter jsonWriter = ProbeJS.GSON_WRITER.newJsonWriter(writer); 28 | jsonWriter.setIndent(" "); 29 | ProbeJS.GSON_WRITER.toJson(content.getSchema(), JsonObject.class, jsonWriter); 30 | } 31 | } 32 | } 33 | 34 | public void newSchema(String name, SchemaElement element) { 35 | schemas.put(name, element); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/schema/SchemaElement.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.schema; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonNull; 5 | import com.google.gson.JsonObject; 6 | import moe.wolfgirl.probejs.utils.JsonUtils; 7 | 8 | import java.util.*; 9 | 10 | public abstract class SchemaElement> { 11 | protected final List enums = new ArrayList<>(); 12 | protected final Map additional = new HashMap<>(); 13 | 14 | public abstract String getType(); 15 | 16 | protected abstract JsonObject toSchema(); 17 | 18 | public JsonObject getSchema() { 19 | JsonObject object = toSchema(); 20 | object.addProperty("type", getType()); 21 | if (!enums.isEmpty()) object.add("enum", JsonUtils.parseObject(enums)); 22 | for (Map.Entry entry : additional.entrySet()) { 23 | String key = entry.getKey(); 24 | Object value = entry.getValue(); 25 | 26 | JsonElement element = JsonUtils.parseObject(value); 27 | if (element == JsonNull.INSTANCE) continue; 28 | object.add(key, element); 29 | } 30 | return object; 31 | } 32 | 33 | @SuppressWarnings("unchecked") 34 | protected final T self() { 35 | return (T) this; 36 | } 37 | 38 | public T enums(Object... values) { 39 | enums.addAll(Arrays.asList(values)); 40 | return self(); 41 | } 42 | 43 | public T additionalField(String key, Object value) { 44 | additional.put(key, value); 45 | return self(); 46 | } 47 | 48 | public ArrayElement asArray() { 49 | return new ArrayElement(this); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/snippet/SnippetDump.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.snippet; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.stream.JsonWriter; 5 | import moe.wolfgirl.probejs.ProbeJS; 6 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 7 | 8 | import java.io.IOException; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Controls the generation of snippets. 16 | */ 17 | public class SnippetDump { 18 | public List snippets = new ArrayList<>(); 19 | 20 | public Snippet snippet(String name) { 21 | Snippet snippet = new Snippet(name); 22 | snippets.add(snippet); 23 | return snippet; 24 | } 25 | 26 | public void fromDocs() { 27 | ProbeJSPlugin.forEachPlugin(plugin -> plugin.addVSCodeSnippets(this)); 28 | } 29 | 30 | 31 | public void writeTo(Path path) throws IOException { 32 | try (var writer = Files.newBufferedWriter(path)) { 33 | JsonWriter jsonWriter = ProbeJS.GSON_WRITER.newJsonWriter(writer); 34 | jsonWriter.setIndent(" "); 35 | 36 | JsonObject compiled = new JsonObject(); 37 | 38 | for (Snippet snippet : snippets) { 39 | compiled.add(snippet.name, snippet.compile()); 40 | } 41 | ProbeJS.GSON_WRITER.toJson(compiled, JsonObject.class, jsonWriter); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/snippet/parts/Choice.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.snippet.parts; 2 | 3 | import java.util.Collection; 4 | import java.util.stream.Collectors; 5 | 6 | public class Choice extends Enumerable { 7 | public final Collection choices; 8 | 9 | public Choice(Collection choices) { 10 | this.choices = choices; 11 | } 12 | 13 | @Override 14 | public String format() { 15 | String choiceString = choices.stream().map(s -> s.replace(",", "\\,")).collect(Collectors.joining(",")); 16 | return "${%d|%s|}".formatted(enumeration, choiceString); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/snippet/parts/Enumerable.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.snippet.parts; 2 | 3 | /** 4 | * A snippet part that holds a position. 5 | */ 6 | public abstract class Enumerable implements SnippetPart { 7 | public int enumeration = -1; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/snippet/parts/Literal.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.snippet.parts; 2 | 3 | public class Literal implements SnippetPart { 4 | private final String content; 5 | 6 | public Literal(String content) { 7 | this.content = content; 8 | } 9 | 10 | @Override 11 | public String format() { 12 | return content; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/snippet/parts/SnippetPart.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.snippet.parts; 2 | 3 | public interface SnippetPart { 4 | String format(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/snippet/parts/TabStop.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.snippet.parts; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | public class TabStop extends Enumerable { 6 | 7 | public final String content; 8 | 9 | public TabStop(@Nullable String content) { 10 | this.content = content; 11 | } 12 | 13 | @Override 14 | public String format() { 15 | if (content == null) return "$%d".formatted(enumeration); 16 | return "${%d:%s}".formatted(enumeration, content.replace("$", "\\$")); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/snippet/parts/Variable.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.snippet.parts; 2 | 3 | public enum Variable implements SnippetPart { 4 | 5 | // Stuffs related to comments 6 | LINE_COMMENT, 7 | BLOCK_COMMENT_END, 8 | BLOCK_COMMENT_START, 9 | 10 | // Stuffs related to random 11 | UUID, 12 | RANDOM_HEX, 13 | RANDOM, 14 | 15 | // Stuffs related to time 16 | CURRENT_TIMEZONE_OFFSET, 17 | CURRENT_SECONDS_UNIX, 18 | CURRENT_SECOND, 19 | CURRENT_MINUTE, 20 | CURRENT_HOUR, 21 | CURRENT_DAY_NAME_SHORT, 22 | CURRENT_DAY_NAME, 23 | CURRENT_DATE, 24 | CURRENT_MONTH_NAME_SHORT, 25 | CURRENT_MONTH_NAME, 26 | CURRENT_MONTH, 27 | CURRENT_YEAR_SHORT, 28 | CURRENT_YEAR, 29 | 30 | // Stuffs related to editor and documents 31 | CURSOR_NUMBER, 32 | CURSOR_INDEX, 33 | WORKSPACE_FOLDER, 34 | WORKSPACE_NAME, 35 | CLIPBOARD, 36 | RELATIVE_FILEPATH, 37 | TM_FILEPATH, 38 | TM_DIRECTORY, 39 | TM_FILENAME_BASE, 40 | TM_FILENAME, 41 | TM_LINE_NUMBER, 42 | TM_LINE_INDEX, 43 | TM_CURRENT_WORD, 44 | TM_CURRENT_LINE, 45 | TM_SELECTED_TEXT; 46 | 47 | 48 | @Override 49 | public String format() { 50 | return "$%s".formatted(name()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/transpiler/ClassTranspiler.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.transpiler; 2 | 3 | import dev.latvian.mods.rhino.type.VariableTypeInfo; 4 | import moe.wolfgirl.probejs.lang.java.clazz.Clazz; 5 | import moe.wolfgirl.probejs.lang.java.clazz.members.ConstructorInfo; 6 | import moe.wolfgirl.probejs.lang.java.clazz.members.FieldInfo; 7 | import moe.wolfgirl.probejs.lang.java.clazz.members.MethodInfo; 8 | import moe.wolfgirl.probejs.lang.transpiler.members.Constructor; 9 | import moe.wolfgirl.probejs.lang.transpiler.members.Converter; 10 | import moe.wolfgirl.probejs.lang.transpiler.members.Field; 11 | import moe.wolfgirl.probejs.lang.transpiler.members.Method; 12 | import moe.wolfgirl.probejs.lang.transpiler.transformation.ClassTransformer; 13 | import moe.wolfgirl.probejs.lang.typescript.code.member.*; 14 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 15 | import moe.wolfgirl.probejs.lang.typescript.code.type.TSVariableType; 16 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | public class ClassTranspiler extends Converter { 22 | 23 | private final Method method; 24 | private final Field field; 25 | private final Constructor constructor; 26 | 27 | public ClassTranspiler(TypeConverter converter) { 28 | super(converter); 29 | this.method = new Method(converter); 30 | this.field = new Field(converter); 31 | this.constructor = new Constructor(converter); 32 | } 33 | 34 | @Override 35 | public ClassDecl transpile(Clazz input) { 36 | List variableTypes = new ArrayList<>(); 37 | for (VariableTypeInfo variableType : input.variableTypes) { 38 | variableTypes.add((TSVariableType) converter.convertType(variableType)); 39 | } 40 | BaseType superClass = input.superClass == null ? null : converter.convertType(input.superClass); 41 | ClassDecl decl = 42 | input.attribute.isInterface ? 43 | new InterfaceDecl(input.classPath.getName(), 44 | superClass == Types.ANY ? null : superClass, 45 | input.interfaces.stream() 46 | .map(converter::convertType) 47 | .filter(t -> t != Types.ANY) 48 | .toList(), 49 | variableTypes) : 50 | new ClassDecl(input.classPath.getName(), 51 | superClass == Types.ANY ? null : superClass, 52 | input.interfaces.stream() 53 | .map(converter::convertType) 54 | .filter(t -> t != Types.ANY) 55 | .toList(), 56 | variableTypes 57 | ); 58 | 59 | for (FieldInfo fieldInfo : input.fields) { 60 | var fieldDecl = field.transpile(fieldInfo); 61 | ClassTransformer.transformFields(fieldInfo, fieldDecl); 62 | decl.fields.add(fieldDecl); 63 | } 64 | 65 | for (MethodInfo methodInfo : input.methods) { 66 | var methodDecl = method.transpile(methodInfo); 67 | ClassTransformer.transformMethods(input, methodInfo, methodDecl); 68 | decl.methods.add(methodDecl); 69 | } 70 | 71 | for (ConstructorInfo constructorInfo : input.constructors) { 72 | var constructorDecl = constructor.transpile(constructorInfo); 73 | ClassTransformer.transformConstructors(constructorInfo, constructorDecl); 74 | decl.constructors.add(constructorDecl); 75 | } 76 | return decl; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/transpiler/Transpiler.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.transpiler; 2 | 3 | import dev.latvian.mods.kubejs.script.ScriptManager; 4 | import dev.latvian.mods.rhino.util.HideFromJS; 5 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 6 | import moe.wolfgirl.probejs.lang.java.clazz.Clazz; 7 | import moe.wolfgirl.probejs.plugin.ProbeJSPlugin; 8 | import moe.wolfgirl.probejs.lang.transpiler.transformation.ClassTransformer; 9 | import moe.wolfgirl.probejs.lang.typescript.TypeScriptFile; 10 | import moe.wolfgirl.probejs.lang.typescript.code.member.ClassDecl; 11 | 12 | import java.util.*; 13 | 14 | /** 15 | * Converts a Clazz into a TypeScriptFile ready for dump. 16 | */ 17 | public class Transpiler { 18 | public final TypeConverter typeConverter; 19 | public final Set rejectedClasses = new HashSet<>(); 20 | private final ScriptManager scriptManager; 21 | 22 | public Transpiler(ScriptManager manager) { 23 | this.scriptManager = manager; 24 | this.typeConverter = new TypeConverter(manager); 25 | } 26 | 27 | public void reject(Class clazz) { 28 | rejectedClasses.add(new ClassPath(clazz)); 29 | } 30 | 31 | public void init() { 32 | ProbeJSPlugin.forEachPlugin(plugin -> { 33 | plugin.addPredefinedTypes(typeConverter); 34 | plugin.denyTypes(this); 35 | }); 36 | } 37 | 38 | public Map dump(Collection clazzes) { 39 | ClassTranspiler transpiler = new ClassTranspiler(typeConverter); 40 | Map result = new HashMap<>(); 41 | 42 | for (Clazz clazz : clazzes) { 43 | if (rejectedClasses.contains(clazz.classPath) || clazz.hasAnnotation(HideFromJS.class)) { 44 | continue; 45 | } 46 | ClassDecl classDecl = transpiler.transpile(clazz); 47 | ClassTransformer.transformClass(clazz, classDecl); 48 | 49 | if (!scriptManager.isClassAllowed(clazz.original.getName())) { 50 | classDecl.addComment( 51 | "This class is not allowed By KubeJS!", 52 | "You should not load the class, or KubeJS will throw an error.", 53 | "Loading the class using require() will not throw an error, but the class will be undefined." 54 | ); 55 | } 56 | 57 | TypeScriptFile scriptFile = new TypeScriptFile(clazz.classPath); 58 | scriptFile.addCode(classDecl); 59 | 60 | result.put(clazz.classPath, scriptFile); 61 | } 62 | 63 | return result; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/transpiler/TypeUtils.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.transpiler; 2 | 3 | import dev.latvian.mods.rhino.type.*; 4 | 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | /** 10 | * Utils related to types. 11 | */ 12 | public class TypeUtils { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/transpiler/members/Constructor.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.transpiler.members; 2 | 3 | import dev.latvian.mods.rhino.type.VariableTypeInfo; 4 | import moe.wolfgirl.probejs.lang.java.clazz.members.ConstructorInfo; 5 | import moe.wolfgirl.probejs.lang.transpiler.TypeConverter; 6 | import moe.wolfgirl.probejs.lang.typescript.code.member.ConstructorDecl; 7 | import moe.wolfgirl.probejs.lang.typescript.code.type.TSVariableType; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class Constructor extends Converter { 13 | private final Param param; 14 | 15 | public Constructor(TypeConverter converter) { 16 | super(converter); 17 | this.param = new Param(converter); 18 | } 19 | 20 | @Override 21 | public ConstructorDecl transpile(ConstructorInfo input) { 22 | List variableTypes = new ArrayList<>(); 23 | for (VariableTypeInfo variableType : input.variableTypes) { 24 | variableTypes.add((TSVariableType) converter.convertType(variableType)); 25 | } 26 | return new ConstructorDecl( 27 | variableTypes, 28 | input.params.stream().map(param::transpile).toList() 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/transpiler/members/Converter.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.transpiler.members; 2 | 3 | import moe.wolfgirl.probejs.lang.transpiler.TypeConverter; 4 | 5 | public abstract class Converter { 6 | protected final TypeConverter converter; 7 | 8 | public Converter(TypeConverter converter) { 9 | this.converter = converter; 10 | } 11 | 12 | public abstract C transpile(T input); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/transpiler/members/Field.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.transpiler.members; 2 | 3 | 4 | import moe.wolfgirl.probejs.lang.java.clazz.members.FieldInfo; 5 | import moe.wolfgirl.probejs.lang.transpiler.TypeConverter; 6 | import moe.wolfgirl.probejs.lang.typescript.code.member.FieldDecl; 7 | 8 | public class Field extends Converter { 9 | public Field(TypeConverter converter) { 10 | super(converter); 11 | } 12 | 13 | @Override 14 | public FieldDecl transpile(FieldInfo input) { 15 | FieldDecl decl = new FieldDecl(input.name, converter.convertType(input.type)); 16 | decl.isFinal = input.attributes.isFinal; 17 | decl.isStatic = input.attributes.isStatic; 18 | 19 | return decl; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/transpiler/members/Method.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.transpiler.members; 2 | 3 | import dev.latvian.mods.rhino.type.VariableTypeInfo; 4 | import moe.wolfgirl.probejs.lang.java.clazz.members.MethodInfo; 5 | import moe.wolfgirl.probejs.lang.transpiler.TypeConverter; 6 | import moe.wolfgirl.probejs.lang.typescript.code.member.MethodDecl; 7 | import moe.wolfgirl.probejs.lang.typescript.code.type.TSVariableType; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class Method extends Converter { 13 | private final Param param; 14 | 15 | public Method(TypeConverter converter) { 16 | super(converter); 17 | this.param = new Param(converter); 18 | } 19 | 20 | @Override 21 | public MethodDecl transpile(MethodInfo input) { 22 | List variableTypes = new ArrayList<>(); 23 | for (VariableTypeInfo variableType : input.variableTypes) { 24 | variableTypes.add((TSVariableType) converter.convertType(variableType)); 25 | } 26 | MethodDecl decl = new MethodDecl( 27 | input.name, 28 | variableTypes, 29 | input.params.stream().map(this.param::transpile).toList(), 30 | converter.convertType(input.returnType) 31 | ); 32 | decl.isAbstract = input.attributes.isAbstract; 33 | decl.isStatic = input.attributes.isStatic; 34 | 35 | return decl; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/transpiler/members/Param.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.transpiler.members; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.members.ParamInfo; 4 | import moe.wolfgirl.probejs.lang.transpiler.TypeConverter; 5 | import moe.wolfgirl.probejs.lang.typescript.code.member.ParamDecl; 6 | 7 | public class Param extends Converter { 8 | public Param(TypeConverter converter) { 9 | super(converter); 10 | } 11 | 12 | @Override 13 | public ParamDecl transpile(ParamInfo input) { 14 | return new ParamDecl(input.name, converter.convertType(input.type), input.varArgs, false); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/transpiler/transformation/ClassTransformer.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.transpiler.transformation; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.Clazz; 4 | import moe.wolfgirl.probejs.lang.java.clazz.members.ConstructorInfo; 5 | import moe.wolfgirl.probejs.lang.java.clazz.members.FieldInfo; 6 | import moe.wolfgirl.probejs.lang.java.clazz.members.MethodInfo; 7 | import moe.wolfgirl.probejs.lang.typescript.code.member.ClassDecl; 8 | import moe.wolfgirl.probejs.lang.typescript.code.member.ConstructorDecl; 9 | import moe.wolfgirl.probejs.lang.typescript.code.member.FieldDecl; 10 | import moe.wolfgirl.probejs.lang.typescript.code.member.MethodDecl; 11 | 12 | /** 13 | * Accepts a Clazz and a transpiled TS file, modify the 14 | * file to respect some stuffs. 15 | */ 16 | public interface ClassTransformer { 17 | ClassTransformer[] CLASS_TRANSFORMERS = new ClassTransformer[]{ 18 | new InjectAnnotation(), 19 | new InjectArray(), 20 | new InjectBeans(), 21 | new InjectSelf(), 22 | new InjectSpecialType(), 23 | }; 24 | 25 | static void transformClass(Clazz clazz, ClassDecl classDecl) { 26 | for (ClassTransformer classTransformer : CLASS_TRANSFORMERS) { 27 | classTransformer.transform(clazz, classDecl); 28 | } 29 | } 30 | 31 | static void transformMethods(Clazz clazz, MethodInfo methodInfo, MethodDecl methodDecl) { 32 | for (ClassTransformer classTransformer : CLASS_TRANSFORMERS) { 33 | classTransformer.transformMethod(clazz, methodInfo, methodDecl); 34 | } 35 | } 36 | 37 | static void transformConstructors(ConstructorInfo constructorInfo, ConstructorDecl constructorDecl) { 38 | for (ClassTransformer classTransformer : CLASS_TRANSFORMERS) { 39 | classTransformer.transformConstructor(constructorInfo, constructorDecl); 40 | } 41 | } 42 | 43 | static void transformFields(FieldInfo fieldInfo, FieldDecl fieldDecl) { 44 | for (ClassTransformer classTransformer : CLASS_TRANSFORMERS) { 45 | classTransformer.transformField(fieldInfo, fieldDecl); 46 | } 47 | } 48 | 49 | default void transform(Clazz clazz, ClassDecl classDecl) { 50 | } 51 | 52 | default void transformMethod(Clazz clazz, MethodInfo methodInfo, MethodDecl methodDecl) { 53 | 54 | } 55 | 56 | default void transformConstructor(ConstructorInfo constructorInfo, ConstructorDecl constructorDecl) { 57 | 58 | } 59 | 60 | default void transformField(FieldInfo fieldInfo, FieldDecl fieldDecl) { 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/transpiler/transformation/InjectAnnotation.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.transpiler.transformation; 2 | 3 | import dev.latvian.mods.kubejs.typings.Info; 4 | import dev.latvian.mods.kubejs.typings.Param; 5 | import moe.wolfgirl.probejs.lang.java.base.AnnotationHolder; 6 | import moe.wolfgirl.probejs.lang.java.clazz.Clazz; 7 | import moe.wolfgirl.probejs.lang.java.clazz.members.ConstructorInfo; 8 | import moe.wolfgirl.probejs.lang.java.clazz.members.FieldInfo; 9 | import moe.wolfgirl.probejs.lang.java.clazz.members.MethodInfo; 10 | import moe.wolfgirl.probejs.lang.typescript.code.member.*; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | public class InjectAnnotation implements ClassTransformer { 16 | @Override 17 | public void transform(Clazz clazz, ClassDecl classDecl) { 18 | applyInfo(clazz, classDecl); 19 | if (clazz.hasAnnotation(Deprecated.class)) classDecl.newline("@deprecated"); 20 | } 21 | 22 | @Override 23 | public void transformMethod(Clazz clazz, MethodInfo methodInfo, MethodDecl decl) { 24 | var params = applyInfo(methodInfo, decl); 25 | if (methodInfo.hasAnnotation(Deprecated.class)) { 26 | decl.newline("@deprecated"); 27 | } 28 | if (!params.isEmpty()) { 29 | decl.linebreak(); 30 | for (Param param : params) { 31 | decl.addComment("@param %s - %s".formatted(param.name(), param.value())); 32 | } 33 | } 34 | } 35 | 36 | @Override 37 | public void transformField(FieldInfo fieldInfo, FieldDecl decl) { 38 | applyInfo(fieldInfo, decl); 39 | if (fieldInfo.hasAnnotation(Deprecated.class)) decl.newline("@deprecated"); 40 | } 41 | 42 | @Override 43 | public void transformConstructor(ConstructorInfo constructorInfo, ConstructorDecl decl) { 44 | applyInfo(constructorInfo, decl); 45 | if (constructorInfo.hasAnnotation(Deprecated.class)) decl.newline("@deprecated"); 46 | } 47 | 48 | public List applyInfo(AnnotationHolder info, CommentableCode decl) { 49 | List params = new ArrayList<>(); 50 | for (Info annotation : info.getAnnotations(Info.class)) { 51 | decl.addComment(annotation.value()); 52 | params.addAll(List.of(annotation.params())); 53 | } 54 | return params; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/transpiler/transformation/InjectArray.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.transpiler.transformation; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.Clazz; 4 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 5 | import moe.wolfgirl.probejs.lang.typescript.code.Code; 6 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 7 | import moe.wolfgirl.probejs.lang.typescript.code.member.ClassDecl; 8 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 9 | import moe.wolfgirl.probejs.lang.typescript.code.type.TSParamType; 10 | 11 | import java.util.Collection; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | * Inject [Symbol.iterator](): IterableIterator; for Iterable. 17 | *
18 | * Inject [index: number]: T; for List. 19 | *
20 | * Inject [index: string | number]: V; for Map. 21 | */ 22 | public class InjectArray implements ClassTransformer { 23 | 24 | static class FormattedLine extends Code { 25 | private final String line; 26 | private final BaseType type; 27 | 28 | FormattedLine(String line, BaseType type) { 29 | this.line = line; 30 | this.type = type; 31 | } 32 | 33 | @Override 34 | public Collection getUsedImports() { 35 | return type.getUsedImports(); 36 | } 37 | 38 | @Override 39 | public List format(Declaration declaration) { 40 | return List.of(line.formatted(type.line(declaration, BaseType.FormatType.RETURN))); 41 | } 42 | } 43 | 44 | @Override 45 | public void transform(Clazz clazz, ClassDecl classDecl) { 46 | if (isDirectlyImplementing(clazz.original, Iterable.class)) { 47 | BaseType iterType = classDecl.methods.stream() 48 | .filter(m -> m.name.equals("iterator")) 49 | .filter(m -> m.returnType instanceof TSParamType) 50 | .map(m -> ((TSParamType) m.returnType).params.getFirst()) 51 | .findFirst().orElse(null); 52 | if (iterType == null) return; 53 | 54 | classDecl.bodyCode.add(new FormattedLine("[Symbol.iterator](): IterableIterator<%s>;", iterType)); 55 | 56 | 57 | } 58 | 59 | // AbstractCollection is not a List, and AbstractList is not directly implementing Iterable 60 | if (isDirectlyImplementing(clazz.original, List.class)) { 61 | BaseType iterType = classDecl.methods.stream() 62 | .filter(m -> m.name.equals("iterator") && m.params.isEmpty()) 63 | .filter(m -> m.returnType instanceof TSParamType) 64 | .map(m -> ((TSParamType) m.returnType).params.getFirst()) 65 | .findFirst().orElse(null); 66 | if (iterType == null) return; 67 | classDecl.bodyCode.add(new FormattedLine("[index: number]: %s", iterType)); 68 | } 69 | 70 | 71 | if (isDirectlyImplementing(clazz.original, Map.class)) { 72 | BaseType valueType = classDecl.methods.stream() 73 | .filter(m -> m.name.equals("get") && m.params.size() == 1) 74 | .map(m -> m.returnType) 75 | .findFirst().orElse(null); 76 | if (valueType == null) return; 77 | classDecl.bodyCode.add(new FormattedLine("[index: string | number]: %s", valueType)); 78 | } 79 | } 80 | 81 | private boolean isDirectlyImplementing(Class toExamine, Class target) { 82 | if (!target.isAssignableFrom(toExamine)) return false; 83 | Class superClass = toExamine.getSuperclass(); 84 | if (superClass == null || superClass == Object.class) return true; 85 | return !target.isAssignableFrom(superClass); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/transpiler/transformation/InjectSelf.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.transpiler.transformation; 2 | 3 | 4 | import dev.latvian.mods.rhino.util.ReturnsSelf; 5 | import moe.wolfgirl.probejs.lang.java.clazz.Clazz; 6 | import moe.wolfgirl.probejs.lang.java.clazz.members.MethodInfo; 7 | import moe.wolfgirl.probejs.lang.typescript.code.member.MethodDecl; 8 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 9 | 10 | public class InjectSelf implements ClassTransformer { 11 | 12 | @Override 13 | public void transformMethod(Clazz clazz, MethodInfo methodInfo, MethodDecl methodDecl) { 14 | if (methodInfo.hasAnnotation(ReturnsSelf.class)) { 15 | methodDecl.returnType = Types.THIS; 16 | } else if (clazz.hasAnnotation(ReturnsSelf.class)) { 17 | Class value = clazz.getAnnotation(ReturnsSelf.class).value(); 18 | if (value == Object.class) value = clazz.original; 19 | if (methodInfo.returnType.asClass() == value) methodDecl.returnType = Types.THIS; 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/Declaration.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 4 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 5 | 6 | import java.util.*; 7 | 8 | public class Declaration { 9 | private static final String SYMBOL_TEMPLATE = "%s$%d"; 10 | 11 | public final Map references; 12 | private final Map symbols; 13 | 14 | private final Set excludedName; 15 | 16 | public Declaration() { 17 | this.references = new HashMap<>(); 18 | this.symbols = new HashMap<>(); 19 | this.excludedName = new HashSet<>(); 20 | } 21 | 22 | public void addClass(ImportInfo path) { 23 | references.computeIfAbsent(path.classPath(), classPath -> { 24 | var name = getSymbolName(classPath); 25 | return new Reference(classPath, name, EnumSet.noneOf(ImportInfo.Type.class)); 26 | }).types().add(path.type()); 27 | } 28 | 29 | public void exclude(String name) { 30 | excludedName.add(name); 31 | } 32 | 33 | private void putSymbolName(ClassPath path, String name) { 34 | symbols.put(path, name); 35 | } 36 | 37 | private boolean containsSymbol(String name) { 38 | return excludedName.contains(name) || symbols.containsValue(name); 39 | } 40 | 41 | 42 | private String getSymbolName(ClassPath path) { 43 | if (!symbols.containsKey(path)) { 44 | String name = path.getName(); 45 | if (!containsSymbol(name)) putSymbolName(path, name); 46 | else { 47 | int counter = 0; 48 | while (containsSymbol(SYMBOL_TEMPLATE.formatted(name, counter))) { 49 | counter++; 50 | } 51 | putSymbolName(path, SYMBOL_TEMPLATE.formatted(name, counter)); 52 | } 53 | } 54 | 55 | return symbols.get(path); 56 | } 57 | 58 | public String getSymbol(ClassPath path) { 59 | if (!this.references.containsKey(path)) { 60 | throw new RuntimeException("Trying to get a symbol of a classpath that is not resolved yet!"); 61 | } 62 | var reference = this.references.get(path); 63 | return reference.symbol(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/Reference.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript; 2 | 3 | import moe.wolfgirl.probejs.ProbeJS; 4 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 5 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 6 | 7 | import java.util.EnumSet; 8 | import java.util.stream.Collectors; 9 | 10 | public record Reference(ClassPath classPath, 11 | String symbol, 12 | EnumSet types) { 13 | public String getImport() { 14 | // FIXME: make the implementation correct 15 | if (types.contains(ImportInfo.Type.TYPE)) { 16 | types.add(ImportInfo.Type.ORIGINAL); 17 | } else if (types.contains(ImportInfo.Type.ORIGINAL)) { 18 | types.add(ImportInfo.Type.TYPE); 19 | } 20 | 21 | String original = classPath.getName(); 22 | String names = types().stream().map(op -> original.equals(symbol) ? 23 | op.applyTemplate(symbol) : 24 | "%s as %s".formatted( 25 | op.applyTemplate(original), 26 | op.applyTemplate(symbol) 27 | )).collect(Collectors.joining(", ")); 28 | 29 | // Underscores can be recognized by using a global export 30 | return "import {%s} from %s".formatted( 31 | names, ProbeJS.GSON.toJson(classPath.getTypeScriptPath()) 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/TypeScriptFile.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript; 2 | 3 | import moe.wolfgirl.probejs.ProbeJS; 4 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 5 | import moe.wolfgirl.probejs.lang.typescript.code.Code; 6 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 7 | 8 | import java.io.BufferedWriter; 9 | import java.io.IOException; 10 | import java.nio.file.Files; 11 | import java.nio.file.Path; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.Optional; 15 | 16 | public class TypeScriptFile { 17 | public final Declaration declaration; 18 | public final List codeList; 19 | public final ClassPath classPath; 20 | 21 | public TypeScriptFile(ClassPath self) { 22 | this.declaration = new Declaration(); 23 | this.codeList = new ArrayList<>(); 24 | 25 | if (self != null) { 26 | declaration.addClass(ImportInfo.original(self)); 27 | } 28 | this.classPath = self; 29 | } 30 | 31 | public void excludeSymbol(String name) { 32 | declaration.exclude(name); 33 | } 34 | 35 | public void addCode(Code code) { 36 | codeList.add(code); 37 | for (ImportInfo usedClassPath : code.getUsedImports()) { 38 | declaration.addClass(usedClassPath); 39 | } 40 | } 41 | 42 | public void refreshImports() { 43 | for (Code code : codeList) { 44 | for (ImportInfo usedImport : code.getUsedImports()) { 45 | declaration.addClass(usedImport); 46 | } 47 | } 48 | } 49 | 50 | public String format() { 51 | List formatted = new ArrayList<>(); 52 | 53 | for (Code code : codeList) { 54 | formatted.addAll(code.format(declaration)); 55 | } 56 | 57 | return String.join("\n", formatted); 58 | } 59 | 60 | public void write(Path writeTo) throws IOException { 61 | try (BufferedWriter writer = Files.newBufferedWriter(writeTo)) { 62 | this.write(writer); 63 | } 64 | } 65 | 66 | public void write(BufferedWriter writer) throws IOException { 67 | boolean written = false; 68 | for (Reference value : declaration.references.values()) { 69 | if (value.classPath().equals(classPath)) continue; 70 | writer.write(value.getImport() + "\n"); 71 | written = true; 72 | } 73 | if (!written) { 74 | writer.write("export {} // Mark the file as a module, do not remove unless there are other import/exports!"); 75 | } 76 | 77 | writer.write("\n"); 78 | writer.write(format()); 79 | } 80 | 81 | public void writeAsModule(BufferedWriter writer) throws IOException { 82 | String modulePath = classPath.getTypeScriptPath(); 83 | writer.write("declare module %s {\n".formatted(ProbeJS.GSON.toJson(modulePath))); 84 | this.write(writer); 85 | writer.write("}\n"); 86 | } 87 | 88 | @SuppressWarnings("unchecked") 89 | public Optional findCode(Class type) { 90 | for (Code code : codeList) { 91 | if (type.isInstance(code)) { 92 | return Optional.of((T) code); 93 | } 94 | } 95 | return Optional.empty(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/Code.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | 5 | import java.util.Collection; 6 | import java.util.HashSet; 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | public abstract class Code { 11 | public abstract Collection getUsedImports(); 12 | 13 | public abstract List format(Declaration declaration); 14 | 15 | public String line(Declaration declaration) { 16 | return format(declaration).getFirst(); 17 | } 18 | 19 | public Collection> getClasses() { 20 | HashSet> classes = new HashSet<>(); 21 | for (ImportInfo usedClassPath : getUsedImports()) { 22 | try { 23 | classes.add(usedClassPath.classPath().forName()); 24 | } catch (Throwable ignored) { 25 | } 26 | } 27 | return classes; 28 | } 29 | 30 | public Collection getUsedImportsAs(ImportInfo.Type type) { 31 | return getUsedImports().stream().map(i -> i.asType(type)).collect(Collectors.toSet()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/ImportInfo.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 4 | 5 | import java.util.function.UnaryOperator; 6 | 7 | public record ImportInfo(ClassPath classPath, Type type) { 8 | public static final UnaryOperator ORIGINAL = s -> s; 9 | public static final String INPUT_TEMPLATE = "%s$$Type"; 10 | public static final String OUTPUT_TEMPLATE = "%s$$Original"; 11 | public static final String STATIC_TEMPLATE = "%s$$Static"; 12 | 13 | public enum Type { 14 | ORIGINAL(ImportInfo.ORIGINAL), 15 | TYPE(INPUT_TEMPLATE::formatted), 16 | STATIC(STATIC_TEMPLATE::formatted); 17 | 18 | private final UnaryOperator formatter; 19 | 20 | Type(UnaryOperator formatter) { 21 | this.formatter = formatter; 22 | } 23 | 24 | public String applyTemplate(String name) { 25 | return formatter.apply(name); 26 | } 27 | } 28 | 29 | public static ImportInfo original(ClassPath path) { 30 | return new ImportInfo(path, Type.ORIGINAL); 31 | } 32 | 33 | public static ImportInfo type(ClassPath path) { 34 | return new ImportInfo(path, Type.TYPE); 35 | } 36 | 37 | public static ImportInfo importStatic(ClassPath path) { 38 | return new ImportInfo(path, Type.STATIC); 39 | } 40 | 41 | public ImportInfo asType(Type type) { 42 | return new ImportInfo(this.classPath, type); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/member/CommentableCode.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.member; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.Code; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Date; 8 | import java.util.List; 9 | 10 | public abstract class CommentableCode extends Code { 11 | public final List comments = new ArrayList<>(); 12 | 13 | public List formatComments() { 14 | List formatted = new ArrayList<>(); 15 | formatted.add("/**"); 16 | for (String comment : comments) { 17 | formatted.add(" * %s".formatted(comment)); 18 | } 19 | formatted.add(" */"); 20 | return formatted; 21 | } 22 | 23 | public abstract List formatRaw(Declaration declaration); 24 | 25 | public final List format(Declaration declaration) { 26 | if (comments.isEmpty()) return formatRaw(declaration); 27 | List result = new ArrayList<>(formatComments()); 28 | result.addAll(formatRaw(declaration)); 29 | return result; 30 | } 31 | 32 | public void addComment(String... comments) { 33 | for (String comment : comments) { 34 | this.comments.addAll(List.of(comment.split("\\n"))); 35 | } 36 | } 37 | 38 | public void addCommentAtStart(String... comments) { 39 | List lines = new ArrayList<>(); 40 | for (String comment : comments) { 41 | lines.addAll(List.of(comment.split("\\n"))); 42 | } 43 | this.comments.addAll(0, lines); 44 | } 45 | 46 | public void linebreak() { 47 | comments.add(""); 48 | } 49 | 50 | public void newline(String... comments) { 51 | this.comments.add(""); 52 | addComment(comments); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/member/ConstructorDecl.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.member; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 4 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 5 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 6 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 7 | import moe.wolfgirl.probejs.lang.typescript.code.type.TSVariableType; 8 | 9 | import java.util.*; 10 | import java.util.stream.Collectors; 11 | 12 | public class ConstructorDecl extends CommentableCode { 13 | public final List variableTypes; 14 | public final List params; 15 | public String content = null; 16 | 17 | public ConstructorDecl(List variableTypes, List params) { 18 | this.variableTypes = variableTypes; 19 | this.params = params; 20 | } 21 | 22 | @Override 23 | public Collection getUsedImports() { 24 | Set paths = new HashSet<>(); 25 | for (TSVariableType variable : variableTypes) { 26 | paths.addAll(variable.getUsedImports()); 27 | } 28 | for (ParamDecl param : params) { 29 | paths.addAll(param.type.getUsedImportsAs(ImportInfo.Type.TYPE)); 30 | } 31 | return paths; 32 | } 33 | 34 | @Override 35 | public List formatRaw(Declaration declaration) { 36 | // Format head - constructor 37 | String head = "constructor"; 38 | if (!variableTypes.isEmpty()) { 39 | String variables = variableTypes.stream() 40 | .map(type -> type.line(declaration, BaseType.FormatType.VARIABLE)) 41 | .collect(Collectors.joining(", ")); 42 | head = "%s<%s>".formatted(head, variables); 43 | } 44 | 45 | // Format body - (a: type, ...) 46 | String body = ParamDecl.formatParams(params, declaration, BaseType.FormatType.INPUT); 47 | 48 | // Format tail - {/** content */} 49 | String tail = ""; 50 | if (content != null) { 51 | tail = "%s {/** %s */}".formatted(tail, content); 52 | } 53 | return List.of("%s%s%s".formatted(head, body, tail)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/member/FieldDecl.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.member; 2 | 3 | import moe.wolfgirl.probejs.ProbeJS; 4 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 5 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 6 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 7 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Collection; 11 | import java.util.List; 12 | 13 | 14 | public class FieldDecl extends CommentableCode { 15 | public boolean isFinal = false; 16 | public boolean isStatic = false; 17 | public String name; 18 | public BaseType type; 19 | 20 | public FieldDecl(String name, BaseType type) { 21 | this.name = name; 22 | this.type = type; 23 | } 24 | 25 | @Override 26 | public Collection getUsedImports() { 27 | return type.getUsedImports(); 28 | } 29 | 30 | @Override 31 | public List formatRaw(Declaration declaration) { 32 | List modifiers = new ArrayList<>(); 33 | if (isStatic) modifiers.add("static"); 34 | if (isFinal) modifiers.add("readonly"); 35 | 36 | return List.of("%s %s: %s".formatted( 37 | String.join(" ", modifiers), ProbeJS.GSON.toJson(name), type.line(declaration, BaseType.FormatType.RETURN) 38 | )); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/member/MethodDecl.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.member; 2 | 3 | import moe.wolfgirl.probejs.ProbeJS; 4 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 5 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 6 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 7 | import moe.wolfgirl.probejs.lang.typescript.code.ts.MethodDeclaration; 8 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 9 | import moe.wolfgirl.probejs.lang.typescript.code.type.TSVariableType; 10 | 11 | import java.util.*; 12 | import java.util.stream.Collectors; 13 | 14 | public class MethodDecl extends CommentableCode { 15 | public String name; 16 | public boolean isAbstract = false; 17 | public boolean isStatic = false; 18 | public boolean isInterface = false; 19 | public List variableTypes; 20 | public List params; 21 | public BaseType returnType; 22 | public String content = null; 23 | 24 | 25 | public MethodDecl(String name, List variableTypes, List params, BaseType returnType) { 26 | this.name = name; 27 | this.variableTypes = new ArrayList<>(variableTypes); 28 | this.params = new ArrayList<>(params); 29 | this.returnType = returnType; 30 | } 31 | 32 | 33 | @Override 34 | public Collection getUsedImports() { 35 | Set paths = new HashSet<>(returnType.getUsedImports()); 36 | for (TSVariableType variableType : variableTypes) { 37 | paths.addAll(variableType.getUsedImports()); 38 | } 39 | for (ParamDecl param : params) { 40 | paths.addAll(param.type.getUsedImportsAs(ImportInfo.Type.TYPE)); 41 | } 42 | return paths; 43 | } 44 | 45 | @Override 46 | public List formatRaw(Declaration declaration) { 47 | // Format head - public static "name" 48 | List modifiers = new ArrayList<>(); 49 | if (!isInterface) modifiers.add("public"); 50 | if (isStatic) modifiers.add("static"); 51 | 52 | String head = String.join(" ", modifiers); 53 | head = "%s %s".formatted(head, ProbeJS.GSON.toJson(name)); 54 | if (!variableTypes.isEmpty()) { 55 | String variables = variableTypes.stream() 56 | .map(type -> type.line(declaration, BaseType.FormatType.VARIABLE)) 57 | .collect(Collectors.joining(", ")); 58 | head = "%s<%s>".formatted(head, variables); 59 | } 60 | 61 | // Format body - (a: type, ...) 62 | String body = ParamDecl.formatParams(params, declaration, BaseType.FormatType.INPUT); 63 | 64 | // Format tail - : returnType {/** content */} 65 | String tail = ": %s".formatted(returnType.line(declaration, BaseType.FormatType.RETURN)); 66 | if (content != null) { 67 | tail = "%s {/** %s */}".formatted(tail, content); 68 | } 69 | 70 | return List.of("%s%s%s".formatted(head, body, tail)); 71 | } 72 | 73 | public MethodDeclaration asDeclaration() { 74 | return new MethodDeclaration(name, variableTypes, params, returnType); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/member/ParamDecl.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.member; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 5 | import moe.wolfgirl.probejs.utils.NameUtils; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.ListIterator; 10 | import java.util.Objects; 11 | 12 | public final class ParamDecl { 13 | public String name; 14 | public BaseType type; 15 | public boolean varArg; 16 | public boolean optional; 17 | 18 | public ParamDecl(String name, BaseType type, boolean varArg, boolean optional) { 19 | this.name = name; 20 | this.type = type; 21 | this.varArg = varArg; 22 | this.optional = optional; 23 | } 24 | 25 | public String format(int index, Declaration declaration, BaseType.FormatType formatType) { 26 | String result = NameUtils.isNameSafe(name) ? name : "arg%d".formatted(index); 27 | if (varArg) result = "...%s".formatted(result); 28 | return "%s%s: %s".formatted( 29 | result, 30 | optional ? "?" : "", 31 | type.line(declaration, formatType) 32 | ); 33 | } 34 | 35 | public static String formatParams(List params, Declaration declaration, BaseType.FormatType formatType) { 36 | List formattedParams = new ArrayList<>(); 37 | ListIterator it = params.listIterator(); 38 | while (it.hasNext()) { 39 | int index = it.nextIndex(); 40 | ParamDecl param = it.next(); 41 | formattedParams.add(param.format(index, declaration, formatType)); 42 | } 43 | return "(%s)".formatted(String.join(", ", formattedParams)); 44 | } 45 | 46 | @Override 47 | public boolean equals(Object obj) { 48 | if (obj == this) return true; 49 | if (obj == null || obj.getClass() != this.getClass()) return false; 50 | var that = (ParamDecl) obj; 51 | return Objects.equals(this.name, that.name) && 52 | Objects.equals(this.type, that.type) && 53 | this.varArg == that.varArg && 54 | this.optional == that.optional; 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | return Objects.hash(name, type, varArg, optional); 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return "ParamDecl[" + 65 | "name=" + name + ", " + 66 | "type=" + type + ", " + 67 | "varArg=" + varArg + ", " + 68 | "optional=" + optional + ']'; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/member/TypeDecl.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.member; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 5 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 6 | 7 | import java.util.Collection; 8 | import java.util.List; 9 | 10 | /** 11 | * Represents a type declaration. Standalone members are always exported. 12 | */ 13 | public class TypeDecl extends CommentableCode { 14 | public BaseType type; 15 | public final String symbol; 16 | 17 | public TypeDecl(String symbol, BaseType type) { 18 | this.symbol = symbol; 19 | this.type = type; 20 | } 21 | 22 | 23 | @Override 24 | public Collection getUsedImports() { 25 | return type.getUsedImportsAs(ImportInfo.Type.TYPE); 26 | } 27 | 28 | @Override 29 | public List formatRaw(Declaration declaration) { 30 | return List.of( 31 | "export type %s = %s;".formatted(symbol, type.line(declaration, BaseType.FormatType.INPUT)) 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/member/clazz/ConstructorBuilder.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.member.clazz; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.code.member.ConstructorDecl; 4 | import moe.wolfgirl.probejs.lang.typescript.code.member.ParamDecl; 5 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 6 | import moe.wolfgirl.probejs.lang.typescript.code.type.TSVariableType; 7 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | public class ConstructorBuilder { 14 | public final List variableTypes = new ArrayList<>(); 15 | public final List params = new ArrayList<>(); 16 | 17 | public ConstructorBuilder typeVariables(String... symbols) { 18 | for (String symbol : symbols) { 19 | typeVariables(Types.generic(symbol)); 20 | } 21 | return this; 22 | } 23 | 24 | public ConstructorBuilder typeVariables(TSVariableType... variableTypes) { 25 | this.variableTypes.addAll(Arrays.asList(variableTypes)); 26 | return this; 27 | } 28 | 29 | public ConstructorBuilder param(String symbol, BaseType type) { 30 | return param(symbol, type, false); 31 | } 32 | 33 | public ConstructorBuilder param(String symbol, BaseType type, boolean isOptional) { 34 | return param(symbol, type, isOptional, false); 35 | } 36 | 37 | public ConstructorBuilder param(String symbol, BaseType type, boolean isOptional, boolean isVarArg) { 38 | params.add(new ParamDecl(symbol, type, isVarArg, isOptional)); 39 | return this; 40 | } 41 | 42 | public final ConstructorDecl buildAsConstructor() { 43 | return new ConstructorDecl(variableTypes, params); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/member/clazz/MethodBuilder.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.member.clazz; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.code.member.MethodDecl; 4 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 5 | import moe.wolfgirl.probejs.lang.typescript.code.type.TSVariableType; 6 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 7 | 8 | public class MethodBuilder extends ConstructorBuilder { 9 | public final String name; 10 | public BaseType returnType = Types.VOID; 11 | public boolean isAbstract = false; 12 | public boolean isStatic = false; 13 | 14 | 15 | public MethodBuilder(String name) { 16 | this.name = name; 17 | } 18 | 19 | @Override 20 | public MethodBuilder typeVariables(TSVariableType... variableTypes) { 21 | super.typeVariables(variableTypes); 22 | return this; 23 | } 24 | 25 | @Override 26 | public MethodBuilder typeVariables(String... symbols) { 27 | super.typeVariables(symbols); 28 | return this; 29 | } 30 | 31 | @Override 32 | public MethodBuilder param(String symbol, BaseType type) { 33 | super.param(symbol, type); 34 | return this; 35 | } 36 | 37 | @Override 38 | public MethodBuilder param(String symbol, BaseType type, boolean isOptional) { 39 | super.param(symbol, type, isOptional); 40 | return this; 41 | } 42 | 43 | @Override 44 | public MethodBuilder param(String symbol, BaseType type, boolean isOptional, boolean isVarArg) { 45 | super.param(symbol, type, isOptional, isVarArg); 46 | return this; 47 | } 48 | 49 | public MethodBuilder returnType(BaseType type) { 50 | this.returnType = type; 51 | return this; 52 | } 53 | 54 | public MethodBuilder abstractMethod() { 55 | this.isAbstract = true; 56 | return this; 57 | } 58 | 59 | public MethodBuilder staticMethod() { 60 | this.isStatic = true; 61 | return this; 62 | } 63 | 64 | public MethodDecl buildAsMethod() { 65 | var decl = new MethodDecl(name, variableTypes, params, returnType); 66 | decl.isAbstract = isAbstract; 67 | decl.isStatic = isStatic; 68 | return decl; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/ts/ReexportDeclaration.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.ts; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 5 | 6 | import java.util.List; 7 | 8 | public class ReexportDeclaration extends VariableDeclaration { 9 | 10 | public ReexportDeclaration(String symbol, BaseType type) { 11 | super(symbol, type); 12 | } 13 | 14 | @Override 15 | public List formatRaw(Declaration declaration) { 16 | return List.of("export import %s = %s".formatted(symbol, type.line(declaration, BaseType.FormatType.RETURN))); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/ts/Statements.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.ts; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.code.member.ClassDecl; 4 | 5 | public interface Statements { 6 | static MethodDeclaration.Builder method(String name) { 7 | return new MethodDeclaration.Builder(name); 8 | } 9 | 10 | static ClassDecl.Builder clazz(String name) { 11 | return new ClassDecl.Builder(name); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/ts/VariableDeclaration.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.ts; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 4 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 5 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 6 | import moe.wolfgirl.probejs.lang.typescript.code.member.CommentableCode; 7 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 8 | 9 | import java.util.Collection; 10 | import java.util.List; 11 | 12 | public class VariableDeclaration extends CommentableCode { 13 | 14 | public String symbol; 15 | public BaseType type; 16 | 17 | public VariableDeclaration(String symbol, BaseType type) { 18 | this.symbol = symbol; 19 | this.type = type; 20 | } 21 | 22 | @Override 23 | public Collection getUsedImports() { 24 | return type.getUsedImports(); 25 | } 26 | 27 | @Override 28 | public List formatRaw(Declaration declaration) { 29 | return List.of("const %s: %s".formatted(symbol, type.line(declaration))); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/ts/Wrapped.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.ts; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.Code; 5 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 6 | import moe.wolfgirl.probejs.lang.typescript.code.member.CommentableCode; 7 | 8 | import java.util.*; 9 | 10 | public abstract class Wrapped extends CommentableCode { 11 | public final List codes = new ArrayList<>(); 12 | 13 | public void addCode(Code inner) { 14 | this.codes.add(inner); 15 | } 16 | 17 | @Override 18 | public Collection getUsedImports() { 19 | Set innerPaths = new HashSet<>(); 20 | for (Code code : codes) { 21 | innerPaths.addAll(code.getUsedImports()); 22 | } 23 | return innerPaths; 24 | } 25 | 26 | @Override 27 | public List formatRaw(Declaration declaration) { 28 | List lines = new ArrayList<>(); 29 | for (Code code : codes) { 30 | lines.addAll(code.format(declaration)); 31 | } 32 | return lines; 33 | } 34 | 35 | public boolean isEmpty() { 36 | return codes.isEmpty(); 37 | } 38 | 39 | public void merge(Wrapped other) { 40 | this.codes.addAll(other.codes); 41 | } 42 | 43 | 44 | public static class Global extends Wrapped { 45 | @Override 46 | public List formatRaw(Declaration declaration) { 47 | List lines = new ArrayList<>(); 48 | lines.add("declare global {"); 49 | lines.addAll(super.formatRaw(declaration)); 50 | lines.add("}"); 51 | return lines; 52 | } 53 | } 54 | 55 | public static class Namespace extends Wrapped { 56 | public final String nameSpace; 57 | 58 | public Namespace(String nameSpace) { 59 | this.nameSpace = nameSpace; 60 | } 61 | 62 | @Override 63 | public List formatRaw(Declaration declaration) { 64 | List lines = new ArrayList<>(); 65 | lines.add("export namespace %s {".formatted(nameSpace)); 66 | lines.addAll(super.formatRaw(declaration)); 67 | lines.add("}"); 68 | return lines; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/BaseType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.Code; 5 | 6 | import java.util.List; 7 | 8 | public abstract class BaseType extends Code { 9 | public final List format(Declaration declaration) { 10 | return format(declaration, FormatType.RETURN); 11 | } 12 | 13 | public abstract List format(Declaration declaration, FormatType input); 14 | 15 | public String line(Declaration declaration, FormatType input) { 16 | return format(declaration, input).getFirst(); 17 | } 18 | 19 | // Stuffs for convenience 20 | 21 | public TSArrayType asArray() { 22 | return new TSArrayType(this); 23 | } 24 | 25 | public enum FormatType { 26 | INPUT, 27 | RETURN, 28 | VARIABLE 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/ContextShield.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 4 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 5 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 6 | 7 | import java.util.Collection; 8 | import java.util.List; 9 | 10 | public class ContextShield extends BaseType { 11 | private final BaseType inner; 12 | private final FormatType formatType; 13 | 14 | public ContextShield(BaseType inner, FormatType formatType) { 15 | this.inner = inner; 16 | this.formatType = formatType; 17 | } 18 | 19 | @Override 20 | public Collection getUsedImports() { 21 | return inner.getUsedImports(); 22 | } 23 | 24 | @Override 25 | public List format(Declaration declaration, FormatType input) { 26 | return inner.format(declaration, formatType); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/CustomType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 5 | 6 | import java.util.Collection; 7 | import java.util.Collections; 8 | import java.util.List; 9 | import java.util.function.BiFunction; 10 | 11 | public class CustomType extends BaseType { 12 | private final BiFunction formatter; 13 | private final ImportInfo[] imports; 14 | 15 | public CustomType(BiFunction formatter, ImportInfo[] imports) { 16 | this.formatter = formatter; 17 | this.imports = imports; 18 | } 19 | 20 | @Override 21 | public Collection getUsedImports() { 22 | return List.of(imports); 23 | } 24 | 25 | @Override 26 | public List format(Declaration declaration, FormatType input) { 27 | return Collections.singletonList(formatter.apply(declaration, input)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/ImportShield.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 5 | 6 | import java.util.Collection; 7 | import java.util.List; 8 | 9 | public class ImportShield extends BaseType { 10 | private final BaseType inner; 11 | private final ImportInfo.Type importType; 12 | 13 | public ImportShield(BaseType inner, ImportInfo.Type importType) { 14 | this.inner = inner; 15 | this.importType = importType; 16 | } 17 | 18 | @Override 19 | public List format(Declaration declaration, FormatType input) { 20 | return inner.format(declaration, input); 21 | } 22 | 23 | @Override 24 | public Collection getUsedImports() { 25 | return inner.getUsedImportsAs(importType); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/TSArrayType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 5 | 6 | import java.util.Collection; 7 | import java.util.List; 8 | 9 | public class TSArrayType extends BaseType { 10 | public BaseType component; 11 | 12 | public TSArrayType(BaseType component) { 13 | this.component = component; 14 | } 15 | 16 | @Override 17 | public Collection getUsedImports() { 18 | return component.getUsedImports(); 19 | } 20 | 21 | @Override 22 | public List format(Declaration declaration, FormatType input) { 23 | return List.of("(%s)[]".formatted(component.line(declaration, input))); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/TSClassType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 4 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 5 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 6 | 7 | import java.util.Collection; 8 | import java.util.List; 9 | 10 | public class TSClassType extends BaseType { 11 | public ClassPath classPath; 12 | 13 | public TSClassType(ClassPath classPath) { 14 | this.classPath = classPath; 15 | } 16 | 17 | @Override 18 | public Collection getUsedImports() { 19 | return List.of(ImportInfo.original(classPath)); 20 | } 21 | 22 | @Override 23 | public List format(Declaration declaration, FormatType input) { 24 | var name = declaration.getSymbol(classPath); 25 | return List.of(input == FormatType.INPUT ? ImportInfo.INPUT_TEMPLATE.formatted(name) : name); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/TSOptionalType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 4 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 5 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 6 | 7 | import java.util.*; 8 | 9 | public class TSOptionalType extends BaseType { 10 | public BaseType component; 11 | private static final BaseType OPTIONAL_BASE = Types.type(Optional.class); 12 | private static final ImportInfo OPTIONAL_IMPORT = ImportInfo.original(new ClassPath(Optional.class)); 13 | public TSOptionalType(BaseType component) { 14 | this.component = component; 15 | } 16 | 17 | @Override 18 | public List format(Declaration declaration, FormatType input) { 19 | return input == FormatType.RETURN ? 20 | Types.parameterized(OPTIONAL_BASE, component).format(declaration, input) : 21 | List.of("(%s)?".formatted(component.line(declaration, input))); 22 | } 23 | 24 | @Override 25 | public Collection getUsedImports() { 26 | Set importInfos = new HashSet<>(component.getUsedImports()); 27 | importInfos.add(OPTIONAL_IMPORT); 28 | return importInfos; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/TSParamType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 4 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 5 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 6 | 7 | import java.util.*; 8 | import java.util.stream.Collectors; 9 | 10 | public class TSParamType extends BaseType { 11 | public BaseType baseType; 12 | public List params; 13 | 14 | public TSParamType(BaseType baseType, List params) { 15 | this.baseType = baseType; 16 | this.params = new ArrayList<>(params); 17 | } 18 | 19 | @Override 20 | public Collection getUsedImports() { 21 | Set paths = new HashSet<>(baseType.getUsedImports()); 22 | for (BaseType param : params) { 23 | paths.addAll(param.getUsedImports()); 24 | } 25 | return paths; 26 | } 27 | 28 | @Override 29 | public List format(Declaration declaration, FormatType input) { 30 | return List.of( 31 | "%s<%s>".formatted( 32 | baseType.line(declaration, input), 33 | params.stream() 34 | .map(type -> "(%s)".formatted(type.line(declaration, input))) 35 | .collect(Collectors.joining(", ")) 36 | ) 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/TSStaticType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 4 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 5 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 6 | 7 | import java.util.Collection; 8 | import java.util.List; 9 | 10 | public class TSStaticType extends TSClassType { 11 | public TSStaticType(ClassPath classPath) { 12 | super(classPath); 13 | } 14 | 15 | @Override 16 | public Collection getUsedImports() { 17 | return List.of(ImportInfo.importStatic(classPath)); 18 | } 19 | 20 | @Override 21 | public List format(Declaration declaration, FormatType input) { 22 | var name = declaration.getSymbol(classPath); 23 | return List.of(ImportInfo.STATIC_TEMPLATE.formatted(name)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/TSVariableType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.util.Collection; 8 | import java.util.List; 9 | 10 | public class TSVariableType extends BaseType { 11 | public final String symbol; 12 | public BaseType extendsType; 13 | 14 | public TSVariableType(String symbol, @Nullable BaseType extendsType) { 15 | this.symbol = symbol; 16 | this.extendsType = extendsType == Types.ANY ? null : extendsType; 17 | } 18 | 19 | @Override 20 | public Collection getUsedImports() { 21 | return extendsType == null ? List.of() : extendsType.getUsedImports(); 22 | } 23 | 24 | @Override 25 | public List format(Declaration declaration, FormatType input) { 26 | return List.of(switch (input) { 27 | case INPUT, RETURN -> symbol; 28 | case VARIABLE -> extendsType == null ? symbol : 29 | "%s extends %s".formatted(symbol, extendsType.line(declaration, FormatType.RETURN)); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/js/JSArrayType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type.js; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.utils.NameUtils; 5 | 6 | import java.util.Collection; 7 | import java.util.List; 8 | 9 | public class JSArrayType extends JSMemberType { 10 | 11 | 12 | public JSArrayType(Collection members) { 13 | super(members); 14 | } 15 | 16 | @Override 17 | public List format(Declaration declaration, FormatType input) { 18 | return List.of("[%s]".formatted(formatMembers(declaration, input))); 19 | } 20 | 21 | @Override 22 | protected String getMemberName(String name) { 23 | return NameUtils.isNameSafe(name) ? name : "arg"; 24 | } 25 | 26 | public static class Builder extends JSMemberType.Builder { 27 | 28 | @Override 29 | public JSArrayType build() { 30 | return new JSArrayType(members); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/js/JSJoinedType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type.js; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 5 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 6 | 7 | import java.util.Collection; 8 | import java.util.HashSet; 9 | import java.util.List; 10 | import java.util.Set; 11 | import java.util.stream.Collectors; 12 | 13 | public abstract class JSJoinedType extends BaseType { 14 | private static final int MAX_ITEM_THRESHOLD = 8000; 15 | 16 | public final String delimiter; 17 | public final List types; 18 | 19 | protected JSJoinedType(String delimiter, List types) { 20 | this.delimiter = " %s ".formatted(delimiter); 21 | this.types = types; 22 | } 23 | 24 | 25 | @Override 26 | public Collection getUsedImports() { 27 | Set paths = new HashSet<>(); 28 | for (BaseType type : types) { 29 | paths.addAll(type.getUsedImports()); 30 | } 31 | return paths; 32 | } 33 | 34 | @Override 35 | public List format(Declaration declaration, FormatType input) { 36 | return List.of( 37 | types.stream() 38 | .map(type -> "(%s)".formatted(type.line(declaration, input))) 39 | .collect(Collectors.joining(delimiter)) 40 | ); 41 | } 42 | 43 | public static class Union extends JSJoinedType { 44 | public Union(List types) { 45 | super("|", types); 46 | } 47 | 48 | @Override 49 | public List format(Declaration declaration, FormatType input) { 50 | return super.format(declaration, input); 51 | } 52 | } 53 | 54 | public static class Intersection extends JSJoinedType { 55 | 56 | public Intersection(List types) { 57 | super("&", types); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/js/JSLambdaType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type.js; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 5 | import moe.wolfgirl.probejs.lang.typescript.code.member.MethodDecl; 6 | import moe.wolfgirl.probejs.lang.typescript.code.member.ParamDecl; 7 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 8 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 9 | 10 | import java.util.*; 11 | 12 | public class JSLambdaType extends BaseType { 13 | public final List params; 14 | public final BaseType returnType; 15 | 16 | public JSLambdaType(List params, BaseType returnType) { 17 | this.params = params; 18 | this.returnType = returnType; 19 | } 20 | 21 | @Override 22 | public Collection getUsedImports() { 23 | Set classPaths = new HashSet<>(returnType.getUsedImports()); 24 | for (ParamDecl param : params) { 25 | classPaths.addAll(param.type.getUsedImports()); 26 | } 27 | return classPaths; 28 | } 29 | 30 | // TODO: type-aware formatting instead of hardcoding at builder level 31 | @Override 32 | public List format(Declaration declaration, FormatType input) { 33 | // (arg0: type, arg1: type...) => returnType 34 | return List.of("%s => %s".formatted( 35 | ParamDecl.formatParams(params, declaration, input == FormatType.RETURN ? FormatType.INPUT : FormatType.RETURN), 36 | returnType.line(declaration, input)) 37 | ); 38 | } 39 | 40 | public MethodDecl asMethod(String methodName) { 41 | return new MethodDecl(methodName, List.of(), params, returnType); 42 | } 43 | 44 | public static class Builder { 45 | public final List params = new ArrayList<>(); 46 | public BaseType returnType = Types.VOID; 47 | public boolean arrowFunction = true; 48 | 49 | public Builder returnType(BaseType type) { 50 | this.returnType = Types.ignoreContext(type, arrowFunction ? FormatType.INPUT : FormatType.RETURN); 51 | return this; 52 | } 53 | 54 | public Builder param(String symbol, BaseType type) { 55 | return param(symbol, type, false); 56 | } 57 | 58 | public Builder param(String symbol, BaseType type, boolean isOptional) { 59 | return param(symbol, type, isOptional, false); 60 | } 61 | 62 | public Builder param(String symbol, BaseType type, boolean isOptional, boolean isVarArg) { 63 | params.add(new ParamDecl(symbol, Types.ignoreContext(type, arrowFunction ? FormatType.RETURN : FormatType.INPUT), isVarArg, isOptional)); 64 | return this; 65 | } 66 | 67 | public Builder method() { 68 | arrowFunction = false; 69 | return this; 70 | } 71 | 72 | public JSLambdaType build() { 73 | return new JSLambdaType(params, returnType); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/js/JSMemberType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type.js; 2 | 3 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 4 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 5 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 6 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 7 | 8 | import java.util.*; 9 | import java.util.stream.Collectors; 10 | 11 | public abstract class JSMemberType extends BaseType { 12 | public final Collection members; 13 | 14 | 15 | protected JSMemberType(Collection members) { 16 | this.members = members; 17 | } 18 | 19 | @Override 20 | public Collection getUsedImports() { 21 | Set paths = new HashSet<>(); 22 | for (JSParam member : members) { 23 | paths.addAll(member.type().getUsedImports()); 24 | } 25 | return paths; 26 | } 27 | 28 | protected String formatMembers(Declaration declaration, FormatType type) { 29 | return formatMembers(declaration, type, ", "); 30 | } 31 | 32 | protected String formatMembers(Declaration declaration, FormatType type, String delimiter) { 33 | return members.stream() 34 | .map(m -> m.format(declaration, type, this::getMemberName)) 35 | .collect(Collectors.joining(delimiter)); 36 | } 37 | 38 | protected abstract String getMemberName(String name); 39 | 40 | public static abstract class Builder, O extends BaseType> { 41 | public final Collection members = new ArrayList<>(); 42 | 43 | public T member(String name, BaseType type) { 44 | return member(name, false, type); 45 | } 46 | 47 | @SuppressWarnings("unchecked") 48 | public T member(String name, boolean optional, BaseType type) { 49 | members.add(new JSParam(name, optional, type)); 50 | return (T) this; 51 | } 52 | 53 | public abstract O build(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/js/JSObjectType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type.js; 2 | 3 | import moe.wolfgirl.probejs.ProbeJS; 4 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 5 | 6 | import java.util.*; 7 | 8 | public class JSObjectType extends JSMemberType { 9 | private String delimiter; 10 | 11 | public JSObjectType(Collection members) { 12 | super(members); 13 | delimiter = ", "; 14 | } 15 | 16 | @Override 17 | protected String getMemberName(String name) { 18 | return ProbeJS.GSON.toJson(name); 19 | } 20 | 21 | @Override 22 | public List format(Declaration declaration, FormatType input) { 23 | return List.of("{%s}".formatted(formatMembers(declaration, input, delimiter))); 24 | } 25 | 26 | private JSObjectType asIndexed() { 27 | delimiter = ";\n"; 28 | return this; 29 | } 30 | 31 | public static class Builder extends JSMemberType.Builder { 32 | public JSObjectType build() { 33 | return new JSObjectType(members); 34 | } 35 | 36 | public JSObjectType buildIndexed() { 37 | return build().asIndexed(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/js/JSParam.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type.js; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 5 | 6 | import java.util.function.Function; 7 | 8 | public record JSParam(String name, boolean optional, BaseType type) { 9 | public String format(Declaration declaration, BaseType.FormatType formatType, Function nameGetter) { 10 | return "%s%s: %s".formatted(nameGetter.apply(name), optional ? "?" : "", type.line(declaration, formatType)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/js/JSPrimitiveType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type.js; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 5 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 6 | 7 | import java.util.Collection; 8 | import java.util.List; 9 | import java.util.Objects; 10 | 11 | public class JSPrimitiveType extends BaseType { 12 | 13 | public final String content; 14 | 15 | public JSPrimitiveType(String content) { 16 | this.content = content; 17 | } 18 | 19 | 20 | @Override 21 | public Collection getUsedImports() { 22 | return List.of(); 23 | } 24 | 25 | @Override 26 | public List format(Declaration declaration, FormatType input) { 27 | return List.of(content); 28 | } 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | if (this == o) return true; 33 | if (o == null || getClass() != o.getClass()) return false; 34 | JSPrimitiveType that = (JSPrimitiveType) o; 35 | return Objects.equals(content, that.content); 36 | } 37 | 38 | @Override 39 | public int hashCode() { 40 | return Objects.hash(content); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/lang/typescript/code/type/js/JSTypeOfType.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.lang.typescript.code.type.js; 2 | 3 | import moe.wolfgirl.probejs.lang.typescript.Declaration; 4 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 5 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 6 | 7 | import java.util.Collection; 8 | import java.util.List; 9 | 10 | public class JSTypeOfType extends BaseType { 11 | 12 | public final BaseType inner; 13 | 14 | public JSTypeOfType(BaseType inner) { 15 | this.inner = inner; 16 | } 17 | 18 | @Override 19 | public Collection getUsedImports() { 20 | return inner.getUsedImports(); 21 | } 22 | 23 | @Override 24 | public List format(Declaration declaration, FormatType input) { 25 | return List.of( 26 | "typeof %s".formatted(inner.line(declaration, FormatType.RETURN)) 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/mixins/JavaWrapperMixin.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.mixins; 2 | 3 | 4 | import dev.latvian.mods.kubejs.plugin.builtin.wrapper.JavaWrapper; 5 | import dev.latvian.mods.kubejs.script.KubeJSContext; 6 | import dev.latvian.mods.rhino.NativeJavaClass; 7 | import moe.wolfgirl.probejs.lang.java.ClassRegistry; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 12 | 13 | import java.util.List; 14 | 15 | @Mixin(value = JavaWrapper.class, remap = false) 16 | public interface JavaWrapperMixin { 17 | @Inject(method = "loadClass", remap = false, at = @At("RETURN")) 18 | private static void pjs$getClassLoaded(KubeJSContext cx, String className, CallbackInfoReturnable cir) { 19 | Object object = cir.getReturnValue(); 20 | if (object instanceof NativeJavaClass njc) { 21 | ClassRegistry.REGISTRY.fromClasses(List.of(njc.getClassObject()), 0); 22 | } 23 | } 24 | 25 | @Inject(method = "tryLoadClass(Ldev/latvian/mods/kubejs/script/KubeJSContext;Ljava/lang/String;)Ljava/lang/Object;", remap = false, at = @At("RETURN")) 26 | private static void pjs$tryGetClassLoaded(KubeJSContext cx, String className, CallbackInfoReturnable cir) { 27 | Object object = cir.getReturnValue(); 28 | if (object instanceof NativeJavaClass njc) { 29 | ClassRegistry.REGISTRY.fromClasses(List.of(njc.getClassObject()), 0); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/mixins/LootTableMixin.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.mixins; 2 | 3 | import com.mojang.serialization.DynamicOps; 4 | import moe.wolfgirl.probejs.GameStates; 5 | import net.minecraft.resources.ResourceLocation; 6 | import net.minecraft.world.level.storage.loot.LootDataType; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 11 | 12 | import java.util.Optional; 13 | 14 | @Mixin(LootDataType.class) 15 | public abstract class LootTableMixin { 16 | @Inject(method = "deserialize", at = @At("RETURN")) 17 | public void apply(ResourceLocation resourceLocation, DynamicOps ops, V value, CallbackInfoReturnable> cir) { 18 | GameStates.LOOT_TABLES.add(resourceLocation.toString()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/mixins/RecipeManagerMixin.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.mixins; 2 | 3 | import com.google.gson.JsonObject; 4 | import moe.wolfgirl.probejs.GameStates; 5 | import moe.wolfgirl.probejs.ProbeConfig; 6 | import net.minecraft.resources.ResourceLocation; 7 | import net.minecraft.server.packs.resources.ResourceManager; 8 | import net.minecraft.util.profiling.ProfilerFiller; 9 | import net.minecraft.world.item.crafting.RecipeManager; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | import java.util.Map; 16 | 17 | @Mixin(value = RecipeManager.class, priority = 900) 18 | public class RecipeManagerMixin { 19 | @Inject(method = "apply*", at = @At("HEAD")) 20 | private void apply(Map map, ResourceManager resourceManager, ProfilerFiller profiler, CallbackInfo ci) { 21 | if (!ProbeConfig.INSTANCE.enabled.get()) return; 22 | GameStates.RECIPE_IDS.clear(); 23 | for (Map.Entry entry : map.entrySet()) { 24 | ResourceLocation key = entry.getKey(); 25 | JsonObject value = entry.getValue(); 26 | if (!key.getPath().startsWith("kjs_")) { 27 | GameStates.RECIPE_IDS.put(key.toString(), value); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/mixins/TranslatableMixin.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.mixins; 2 | 3 | import moe.wolfgirl.probejs.GameStates; 4 | import net.minecraft.network.chat.contents.TranslatableContents; 5 | import org.spongepowered.asm.mixin.Final; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | 12 | @Mixin(TranslatableContents.class) 13 | public abstract class TranslatableMixin { 14 | 15 | @Shadow 16 | @Final 17 | private String key; 18 | 19 | @Shadow 20 | @Final 21 | private String fallback; 22 | 23 | @Inject(method = "", at = @At(value = "RETURN")) 24 | private void init(CallbackInfo ci) { 25 | // So we populate keys even if no client storage is present 26 | // I wonder if this is CPU intensive but probably not (that much) 27 | synchronized (GameStates.MIXIN_LANG_KEYS) { 28 | GameStates.MIXIN_LANG_KEYS.add(key); 29 | if (fallback != null) GameStates.MIXIN_LANG_KEYS.add(fallback); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/plugin/Probe.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.plugin; 2 | 3 | import dev.latvian.mods.kubejs.level.LevelBlock; 4 | import dev.latvian.mods.kubejs.script.KubeJSContext; 5 | import dev.latvian.mods.kubejs.script.ScriptType; 6 | import dev.latvian.mods.kubejs.typings.Info; 7 | import dev.latvian.mods.rhino.Context; 8 | import moe.wolfgirl.probejs.GameStates; 9 | import moe.wolfgirl.probejs.lang.java.ClassRegistry; 10 | import net.minecraft.client.Minecraft; 11 | import net.minecraft.server.MinecraftServer; 12 | import net.minecraft.world.entity.Entity; 13 | import net.minecraft.world.entity.player.Player; 14 | import net.neoforged.neoforge.server.ServerLifecycleHooks; 15 | 16 | import java.util.Collection; 17 | import java.util.Collections; 18 | import java.util.List; 19 | 20 | @Info("Debugging utility for easier check on players, blocks, items, etc.") 21 | public class Probe { 22 | public static final Probe INSTANCE = new Probe(); 23 | 24 | private void testSourceFile(Context context) { 25 | var linep = new int[]{0}; 26 | String source = Context.getSourcePositionFromStack(context, linep); 27 | 28 | if (!source.contains("test")) { 29 | throw new RuntimeException("This function is only available in a file containing \"test\", or files under a \"test\" folder."); 30 | } 31 | } 32 | 33 | public Player getCurrentPlayer(Context context) { 34 | KubeJSContext kContext = (KubeJSContext) context; 35 | testSourceFile(context); 36 | if (kContext.getType() == ScriptType.CLIENT) { 37 | return Minecraft.getInstance().player; 38 | } else { 39 | MinecraftServer currentServer = ServerLifecycleHooks.getCurrentServer(); 40 | if (currentServer == null || !currentServer.isSingleplayer()) return null; 41 | return currentServer.getPlayerList() 42 | .getPlayers() 43 | .getFirst(); 44 | } 45 | } 46 | 47 | @Info("Capture the class of the object passed in for ProbeJS to dump.") 48 | public T captureType(T object) { 49 | ClassRegistry.REGISTRY.fromClasses(List.of(object.getClass()), 0); 50 | return object; 51 | } 52 | 53 | public LevelBlock getLastRightClickedBlock(Context context) { 54 | testSourceFile(context); 55 | if (GameStates.LAST_RIGHTCLICKED == null) return null; 56 | Player currentPlayer = getCurrentPlayer(context); 57 | if (currentPlayer == null) return null; 58 | return currentPlayer.level().kjs$getBlock(GameStates.LAST_RIGHTCLICKED); 59 | } 60 | 61 | public Entity getLastRightClickedEntity(Context context) { 62 | testSourceFile(context); 63 | if (GameStates.LAST_ENTITY == null) return null; 64 | return GameStates.LAST_ENTITY; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/plugin/ProbeJSKJSPlugin.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.plugin; 2 | 3 | import dev.latvian.mods.kubejs.plugin.KubeJSPlugin; 4 | import dev.latvian.mods.kubejs.script.BindingRegistry; 5 | import dev.latvian.mods.rhino.Undefined; 6 | import moe.wolfgirl.probejs.utils.Require; 7 | import net.neoforged.api.distmarker.Dist; 8 | import net.neoforged.fml.loading.FMLEnvironment; 9 | 10 | public class ProbeJSKJSPlugin implements KubeJSPlugin { 11 | 12 | @Override 13 | public void registerBindings(BindingRegistry bindings) { 14 | bindings.add("require", new Require()); 15 | bindings.add("ProbeJS", FMLEnvironment.dist == Dist.CLIENT ? Probe.INSTANCE : Undefined.INSTANCE); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/plugin/ProbeJSPlugin.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.plugin; 2 | 3 | import com.mojang.datafixers.util.Pair; 4 | 5 | import dev.latvian.mods.kubejs.plugin.KubeJSPlugin; 6 | import dev.latvian.mods.kubejs.plugin.KubeJSPlugins; 7 | import dev.latvian.mods.rhino.util.HideFromJS; 8 | import moe.wolfgirl.probejs.lang.schema.SchemaDump; 9 | import moe.wolfgirl.probejs.lang.snippet.SnippetDump; 10 | import moe.wolfgirl.probejs.lang.typescript.ScriptDump; 11 | import moe.wolfgirl.probejs.lang.java.clazz.ClassPath; 12 | import moe.wolfgirl.probejs.lang.transpiler.Transpiler; 13 | import moe.wolfgirl.probejs.lang.transpiler.TypeConverter; 14 | import moe.wolfgirl.probejs.lang.typescript.TypeScriptFile; 15 | 16 | import java.util.Map; 17 | import java.util.Set; 18 | import java.util.function.Consumer; 19 | 20 | /** 21 | * A plugin for ProbeJS that is able to alter how ProbeJS works. 22 | *
23 | * Different method calls might have same parameter/controller, 24 | * but it is advised to call different methods and their own stage 25 | * in order to prevent unexpected behavior. 26 | */ 27 | public class ProbeJSPlugin implements KubeJSPlugin { 28 | 29 | @HideFromJS 30 | public static void forEachPlugin(Consumer consumer) { 31 | KubeJSPlugins.forEachPlugin(plugin -> { 32 | if (plugin instanceof ProbeJSPlugin probePlugin) 33 | consumer.accept(probePlugin); 34 | }); 35 | } 36 | 37 | /** 38 | * Used to add forcefully-converted types in order to prevent transient types 39 | * like boolean / string from showing up. 40 | */ 41 | public void addPredefinedTypes(TypeConverter converter) { 42 | 43 | } 44 | 45 | /** 46 | * Used to prevent some types from showing up in the dump, e.g. primitives. 47 | */ 48 | public void denyTypes(Transpiler transpiler) { 49 | 50 | } 51 | 52 | /** 53 | * Used to modify the classes that will be dumped to a certain script type. 54 | *
55 | * Can add / remove dumps by mutating the globalClasses. 56 | */ 57 | public void modifyClasses(ScriptDump scriptDump, Map globalClasses) { 58 | 59 | } 60 | 61 | /** 62 | * Used to add code to global namespace. 63 | *
64 | * Globals are available without any imports, so it must be ensured that the 65 | * added code is either: 66 | * 1. a type 67 | * 2. a binding (though it's not very needed for most people) 68 | */ 69 | public void addGlobals(ScriptDump scriptDump) { 70 | 71 | } 72 | 73 | /** 74 | * Adds a convertible type to a classPath. 75 | *
76 | * e.g. Item can be assigned with any item name string. 77 | */ 78 | public void assignType(ScriptDump scriptDump) { 79 | 80 | } 81 | 82 | /** 83 | * Provides Java classes for the class registry to discoverContainedTypes. 84 | */ 85 | @HideFromJS 86 | public Set> provideJavaClass(ScriptDump scriptDump) { 87 | return Set.of(); 88 | } 89 | 90 | /** 91 | * Provides events that should be disabled for custom support. 92 | */ 93 | public Set> disableEventDumps(ScriptDump dump) { 94 | return Set.of(); 95 | } 96 | 97 | public void addVSCodeSnippets(SnippetDump dump) { 98 | 99 | } 100 | 101 | public void addJsonSchema(SchemaDump dump) { 102 | 103 | } 104 | 105 | /** 106 | * Marks a class to be forcefully loaded even if class scanning is turned off 107 | */ 108 | public Set> filterScannedClasses(Set> clazz) { 109 | return Set.of(); 110 | } 111 | 112 | protected final TypeScriptFile findClassFile(Map globalClasses, Class clazz) { 113 | return globalClasses.get(new ClassPath(clazz)); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/utils/DocUtils.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.utils; 2 | 3 | import com.mojang.datafixers.util.Pair; 4 | import dev.latvian.mods.rhino.type.JSFixedArrayTypeInfo; 5 | import dev.latvian.mods.rhino.type.JSOptionalParam; 6 | import dev.latvian.mods.rhino.type.JSOrTypeInfo; 7 | import dev.latvian.mods.rhino.type.TypeInfo; 8 | import moe.wolfgirl.probejs.lang.typescript.TypeScriptFile; 9 | import moe.wolfgirl.probejs.lang.typescript.code.ImportInfo; 10 | import moe.wolfgirl.probejs.lang.typescript.code.member.ClassDecl; 11 | import moe.wolfgirl.probejs.lang.typescript.code.member.MethodDecl; 12 | import moe.wolfgirl.probejs.lang.typescript.code.member.ParamDecl; 13 | import moe.wolfgirl.probejs.lang.typescript.code.member.TypeDecl; 14 | import moe.wolfgirl.probejs.lang.typescript.code.type.BaseType; 15 | import moe.wolfgirl.probejs.lang.typescript.code.type.Types; 16 | import moe.wolfgirl.probejs.lang.typescript.code.type.js.JSArrayType; 17 | import moe.wolfgirl.probejs.lang.typescript.code.type.js.JSObjectType; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.function.Consumer; 22 | import java.util.function.Predicate; 23 | 24 | public class DocUtils { 25 | public static void applyParam(TypeScriptFile file, Predicate test, int index, Consumer effect) { 26 | if (file == null) return; 27 | file.findCode(ClassDecl.class).ifPresent(classDecl -> { 28 | for (MethodDecl method : classDecl.methods) { 29 | if (test.test(method)) { 30 | effect.accept(method.params.get(index)); 31 | } 32 | } 33 | }); 34 | } 35 | 36 | public static void replaceParamType(TypeScriptFile file, Predicate test, int index, BaseType toReplace) { 37 | applyParam(file, test, index, decl -> decl.type = toReplace); 38 | for (ImportInfo usedClassPath : toReplace.getUsedImports()) { 39 | file.declaration.addClass(usedClassPath); 40 | } 41 | } 42 | 43 | public static void generateMappedType(String mapName, String flagName, Iterable> kvPairs, TypeScriptFile typeScriptFile) { 44 | JSObjectType.Builder typeDict = Types.object(); 45 | 46 | for (Pair kvPair : kvPairs) { 47 | typeDict.member(kvPair.getFirst(), kvPair.getSecond()); 48 | } 49 | 50 | typeScriptFile.addCode(new TypeDecl(mapName, typeDict.buildIndexed())); 51 | typeScriptFile.addCode(new TypeDecl(flagName, Types.primitive("keyof %s".formatted(mapName)))); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/utils/GameUtils.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.utils; 2 | 3 | import dev.latvian.mods.kubejs.server.ServerScriptManager; 4 | import moe.wolfgirl.probejs.ProbeJS; 5 | import net.minecraft.server.MinecraftServer; 6 | import net.neoforged.fml.ModList; 7 | import net.neoforged.neoforge.server.ServerLifecycleHooks; 8 | import net.neoforged.neoforgespi.language.IModInfo; 9 | 10 | import javax.annotation.Nullable; 11 | import java.nio.ByteBuffer; 12 | import java.security.MessageDigest; 13 | import java.security.NoSuchAlgorithmException; 14 | 15 | public class GameUtils { 16 | public static long modHash() { 17 | try { 18 | MessageDigest digest = MessageDigest.getInstance("SHA-256"); 19 | for (IModInfo mod : ModList.get().getMods()) { 20 | digest.update((mod.getModId() + mod.getVersion()).getBytes()); 21 | } 22 | ByteBuffer buffer = ByteBuffer.wrap(digest.digest()); 23 | return buffer.getLong(); 24 | } catch (NoSuchAlgorithmException e) { 25 | return -1; 26 | } 27 | } 28 | 29 | @Nullable 30 | public static ServerScriptManager getServerScriptManager() { 31 | MinecraftServer currentServer = ServerLifecycleHooks.getCurrentServer(); 32 | if (currentServer == null) return null; 33 | return currentServer.getServerResources().managers().kjs$getServerScriptManager(); 34 | } 35 | 36 | public static void logException(Throwable t) { 37 | ProbeJS.LOGGER.error(t); 38 | for (StackTraceElement stackTraceElement : t.getStackTrace()) { 39 | ProbeJS.LOGGER.error(stackTraceElement.toString()); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/utils/NameUtils.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.utils; 2 | 3 | import net.minecraft.resources.ResourceKey; 4 | 5 | import java.util.Arrays; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | import java.util.regex.Pattern; 9 | import java.util.stream.Collectors; 10 | 11 | public class NameUtils { 12 | public static final Set KEYWORDS = new HashSet<>(Arrays.asList("abstract,arguments,boolean,break,byte,case,catch,char,const,continue,constructor,debugger,default,delete,do,double,else,eval,false,final,finally,float,for,function,goto,if,implements,in,instanceof,int,interface,let,long,native,new,null,package,private,protected,public,return,short,static,switch,synchronized,this,throw,throws,transient,true,try,typeof,var,void,volatile,while,with,yield,export".split(","))); 13 | public static final Pattern JS_IDENTIFIER_MATCH = Pattern.compile("[A-Za-z_$][A-Za-z0-9_$]*"); 14 | public static final Pattern MATCH_IMPORT = Pattern.compile("^import \\{(.+)} from (.+)"); 15 | public static final Pattern MATCH_ANY_REQUIRE = Pattern.compile("^.+ \\{(.+)} = require\\((.+)\\)"); 16 | 17 | 18 | public static String[] extractAlphabets(String input) { 19 | return input.split("[^a-zA-Z]+"); 20 | } 21 | 22 | public static String asCamelCase(String[] words) { 23 | StringBuilder result = new StringBuilder(); 24 | for (int i = 0; i < words.length; i++) { 25 | String word = words[i]; 26 | if (!word.isEmpty()) { 27 | if (i == 0) { 28 | result.append(Character.toLowerCase(word.charAt(0))); 29 | } else { 30 | result.append(Character.toUpperCase(word.charAt(0))); 31 | } 32 | result.append(word.substring(1)); 33 | } 34 | } 35 | return result.toString(); 36 | } 37 | 38 | public static String firstLower(String word) { 39 | return Character.toLowerCase(word.charAt(0)) + 40 | word.substring(1); 41 | } 42 | 43 | public static String[] resourceLocationToPath(String resourceLocation) { 44 | return resourceLocation.split("/"); 45 | } 46 | 47 | public static String finalComponentToTitle(String resourceLocation) { 48 | String[] path = resourceLocationToPath(resourceLocation); 49 | String last = path[path.length - 1]; 50 | return Arrays.stream(last.split("_")).map(NameUtils::getCapitalized).collect(Collectors.joining()); 51 | } 52 | 53 | public static String rlToTitle(String s) { 54 | return Arrays.stream(s.split("/")).map(NameUtils::snakeToTitle).collect(Collectors.joining()); 55 | } 56 | 57 | public static String registryToName(ResourceKey resourceKey) { 58 | String name = rlToTitle(resourceKey.location().getPath()); 59 | if (resourceKey.location().getNamespace().equals("minecraft")) return name; 60 | return rlToTitle(resourceKey.location().getNamespace()) + name; 61 | } 62 | 63 | public static boolean isNameSafe(String s) { 64 | return !KEYWORDS.contains(s) && JS_IDENTIFIER_MATCH.matcher(s).matches(); 65 | } 66 | 67 | public static String getCapitalized(String s) { 68 | return s.substring(0, 1).toUpperCase() + s.substring(1); 69 | } 70 | 71 | public static String snakeToTitle(String s) { 72 | return Arrays.stream(s.split("_")).map(NameUtils::getCapitalized).collect(Collectors.joining()); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/utils/ProbeFileUtils.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.utils; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.stream.JsonWriter; 5 | import dev.latvian.mods.kubejs.KubeJSPaths; 6 | import moe.wolfgirl.probejs.ProbeJS; 7 | 8 | import javax.annotation.Nullable; 9 | import java.io.IOException; 10 | import java.nio.file.Files; 11 | import java.nio.file.Path; 12 | import java.util.function.Consumer; 13 | 14 | public class ProbeFileUtils { 15 | public static void forEachFile(Path basePath, Consumer callback) throws IOException { 16 | try (var dirStream = Files.newDirectoryStream(basePath)) { 17 | for (Path path : dirStream) { 18 | if (Files.isDirectory(path)) { 19 | forEachFile(path, callback); 20 | } else { 21 | callback.accept(path); 22 | } 23 | } 24 | } 25 | } 26 | 27 | public static void writeMergedConfig(Path path, String config) throws IOException { 28 | JsonObject updates = ProbeJS.GSON.fromJson(config, JsonObject.class); 29 | JsonObject read = Files.exists(path) ? ProbeJS.GSON.fromJson(Files.newBufferedReader(path), JsonObject.class) : new JsonObject(); 30 | if (read == null) read = new JsonObject(); 31 | JsonObject original = (JsonObject) JsonUtils.mergeJsonRecursively(read, updates); 32 | JsonWriter jsonWriter = ProbeJS.GSON_WRITER.newJsonWriter(Files.newBufferedWriter(path)); 33 | jsonWriter.setIndent(" "); 34 | ProbeJS.GSON_WRITER.toJson(original, JsonObject.class, jsonWriter); 35 | jsonWriter.close(); 36 | } 37 | 38 | @Nullable 39 | public static Path parseSourcePath(String name) { 40 | if (!name.contains(":")) return null; 41 | String[] parts = name.split(":", 2); 42 | Path base = switch (parts[0]) { 43 | case "client_scripts" -> KubeJSPaths.CLIENT_SCRIPTS; 44 | case "server_scripts" -> KubeJSPaths.SERVER_SCRIPTS; 45 | case "startup_scripts" -> KubeJSPaths.STARTUP_SCRIPTS; 46 | case null, default -> null; 47 | }; 48 | if (base == null) return null; 49 | return base.resolve(parts[1]); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/utils/RegistryUtils.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.utils; 2 | 3 | import net.minecraft.core.Registry; 4 | import net.minecraft.core.RegistryAccess; 5 | import net.minecraft.resources.ResourceKey; 6 | 7 | import java.util.Collection; 8 | import java.util.stream.Collectors; 9 | 10 | public class RegistryUtils { 11 | 12 | public static Collection>> getRegistries(RegistryAccess access) { 13 | return access.registries() 14 | .map(RegistryAccess.RegistryEntry::key) 15 | .collect(Collectors.toSet()); 16 | } 17 | 18 | @SuppressWarnings("unchecked") 19 | public static ResourceKey> castKey(ResourceKey key) { 20 | return (ResourceKey>) key; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/moe/wolfgirl/probejs/utils/Require.java: -------------------------------------------------------------------------------- 1 | package moe.wolfgirl.probejs.utils; 2 | 3 | import dev.latvian.mods.rhino.BaseFunction; 4 | import dev.latvian.mods.rhino.Context; 5 | import dev.latvian.mods.rhino.Scriptable; 6 | 7 | public class Require extends BaseFunction { 8 | @Override 9 | public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { 10 | throw new UnsupportedOperationException("require() is not supported! You should install the VSCode extension to get auto-imports for Java.loadClass()."); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/accesstransformer.cfg: -------------------------------------------------------------------------------- 1 | public net.minecraft.client.resources.language.ClientLanguage storage 2 | public net.minecraft.client.renderer.texture.TextureAtlas texturesByName 3 | public net.minecraft.client.renderer.texture.TextureManager byPath 4 | public net.minecraft.client.resources.model.ModelManager bakedRegistry 5 | 6 | public net.minecraft.world.level.block.state.StateHolder PROPERTY_ENTRY_TO_STRING_FUNCTION 7 | public net.minecraft.world.item.CreativeModeTabs CACHED_PARAMETERS 8 | 9 | public com.mojang.blaze3d.platform.NativeImage writeToChannel(Ljava.nio.channels.WritableByteChannel;)Z 10 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[2,)" 3 | issueTrackerURL = "https://github.com/Prunoideae/ProbeJS" 4 | license = "GPLv3" 5 | 6 | [[mods]] 7 | modId = "probejs" 8 | version = "${version}" 9 | displayName = "ProbeJS" 10 | authors = "Prunoideae" 11 | description = ''' 12 | Improving your KubeJS scripting experience by generating auto-completions of all kinds. 13 | ''' 14 | logoFile = "icon.png" 15 | 16 | [[dependencies.probejs]] 17 | modId = "neoforge" 18 | mandatory = true 19 | versionRange = "[${neoforge_version},)" 20 | ordering = "NONE" 21 | side = "BOTH" 22 | 23 | [[dependencies.probejs]] 24 | modId = "minecraft" 25 | mandatory = true 26 | versionRange = "[1.21,)" 27 | ordering = "NONE" 28 | side = "BOTH" 29 | 30 | [[dependencies.probejs]] 31 | modId = "kubejs" 32 | mandatory = true 33 | versionRange = "[${kubejs_version},)" 34 | 35 | [[mixins]] 36 | config = "probejs.mixins.json" -------------------------------------------------------------------------------- /src/main/resources/assets/probejs/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prunoideae/ProbeJS/008b38b716ad53beb4989eab6df7aa5949815750/src/main/resources/assets/probejs/icon.png -------------------------------------------------------------------------------- /src/main/resources/assets/probejs/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "probejs.already_running": "There's already a dumping thread running, please wait.", 3 | "probejs.dump.start": "ProbeJS dump started.", 4 | "probejs.dump.snippets_generated": "Snippets for VSCode generated.", 5 | "probejs.dump.mod_changed": "Mod environment changed.", 6 | "probejs.dump.decompiling": "Decompiling mods, this might take some time...", 7 | "probejs.dump.decompiled_x_class": "Decompiled %s classes.", 8 | "probejs.dump.class_discovered": "Discovered %s classes to generate typing for.", 9 | "probejs.dump.dump_finished": "The typing generation of script type %s is finished.", 10 | "probejs.dump.dump_error": "Dump generation of script type %s failed, this might be a bug in code.", 11 | "probejs.dump.report_progress": "Dump executing... Current progress: ", 12 | "probejs.dump.cleaning": "Removing old dump files that might contain invalid classes... This can be slow.", 13 | "probejs.hello": "Welcome to ProbeJS! An automatic dump will be triggered everytime you enter the game with some new stuffs!", 14 | "probejs.enabled_warning": "ProbeJS is enabled! You should exclude .probe generated when you finished development, and disable the mod by: ", 15 | "probejs.bye_bye": "ProbeJS is now disabled. require() still works, though. Don't forget to exclude the typing.", 16 | "probejs.hello_again": "ProbeJS is enabled now. Happy coding!", 17 | "probejs.removed_script": "Removed typing for script type %s.", 18 | "probejs.isolation": "ProbeJS will isolate each script files now, only `export`ed identifiers will be exposed.", 19 | "probejs.no_isolation": "ProbeJS will not try to isolate different scopes now. Be aware of duplicated names.", 20 | "probejs.wiki": "To learn about how to use ProbeJS (or report issue), please go to the ", 21 | "probejs.lint_passed": "Congratulations! Your code passed linting!", 22 | "probejs.interactive": "ProbeJS interactive server is listening on port %s.", 23 | "probejs.complete": "ProbeJS will dump all the complex types now, at expense of VSCode performance.", 24 | "probejs.no_complete": "ProbeJS will disable some types in dump now, so your VSCode should run faster. Use snippets for those types missing.", 25 | "probejs.performance": "You installed %s mods! Some functions are disabled for the sake of performance, but probably not much. Toggle with /probejs config complete_dump if you want a complete (but slow) dump!", 26 | "probejs.decompile": "ProbeJS will decompile mod sources at next dump, decompilation will also trigger when mod list changed.", 27 | "probejs.no_decompile": "ProbeJS will not decompile mod sources now." 28 | } -------------------------------------------------------------------------------- /src/main/resources/assets/probejs/lang/zh_cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "probejs.dump.start": "ProbeJS 类型生成已启动。", 3 | "probejs.dump.snippets_generated": "VSCode 快捷脚本已生成。", 4 | "probejs.dump.mod_changed": "检测到模组环境变动。", 5 | "probejs.dump.decompiling": "反编译模组中,这可能需要一段时间。", 6 | "probejs.dump.decompiled_x_class": "已反编译 %s 个类。", 7 | "probejs.dump.class_discovered": "已发现需要生成类型文件的 %s 个类。", 8 | "probejs.dump.dump_finished": "脚本类型 %s 的生成已完成。", 9 | "probejs.dump.dump_error": "脚本类型 %s 的生成失败了,这大概可能也许是一个 bug……", 10 | "probejs.dump.report_progress": "生成类型文件中……当前进度:", 11 | "probejs.dump.cleaning": "清理上一版本的类型文件中……这可能需要一点时间。", 12 | "probejs.hello": "欢迎来到 ProbeJS。ProbeJS 将会在注册表出现变动时重新生成类型文件。", 13 | "probejs.enabled_warning": "ProbeJS 正在运行!在你导出整合包之前,请排除生成的类型文件,并使用命令关闭 ProbeJS:", 14 | "probejs.bye_bye": "ProbeJS 已被关闭。require() 仍可正常运行。记得不要打包类型文件。", 15 | "probejs.removed_script": "已清理脚本类型 %s 的类型文件。", 16 | "probejs.hello_again": "ProbeJS 已开启,祝你生活美满。", 17 | "probejs.isolation": "ProbeJS 将会隔离脚本作用域,只有具有 export 关键字的方法 / 变量可被其他脚本访问。", 18 | "probejs.no_isolation": "ProbeJS 现在不会隔离脚本作用域,请小心变量名污染。", 19 | "probejs.wiki": "需要 ProbeJS 教程的话,请点击:", 20 | "probejs.lint_passed": "恭喜你,你的代码经受住了考验。" 21 | } 22 | -------------------------------------------------------------------------------- /src/main/resources/assets/probejs/lang/zh_tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "probejs.dump.start": "ProbeJS 类型生成已启动。", 3 | "probejs.dump.snippets_generated": "VSCode 快捷脚本已生成。", 4 | "probejs.dump.mod_changed": "检测到模组环境变动。", 5 | "probejs.dump.decompiling": "反编译模组中,这可能需要一段时间。", 6 | "probejs.dump.decompiled_x_class": "已反编译 %s 个类。", 7 | "probejs.dump.class_discovered": "已发现需要生成类型文件的 %s 个类。", 8 | "probejs.dump.dump_finished": "脚本类型 %s 的生成已完成。", 9 | "probejs.dump.dump_error": "脚本类型 %s 的生成失败了,这大概可能也许是一个 bug……", 10 | "probejs.dump.report_progress": "生成类型文件中……当前进度:", 11 | "probejs.dump.cleaning": "清理上一版本的类型文件中……这可能需要一点时间。", 12 | "probejs.hello": "欢迎来到 ProbeJS。ProbeJS 将会在注册表出现变动时重新生成类型文件。", 13 | "probejs.enabled_warning": "ProbeJS 正在运行!在你导出整合包之前,请排除生成的类型文件,并使用命令关闭 ProbeJS:", 14 | "probejs.bye_bye": "ProbeJS 已被关闭。require() 仍可正常运行。记得不要打包类型文件。", 15 | "probejs.removed_script": "已清理脚本类型 %s 的类型文件。", 16 | "probejs.hello_again": "ProbeJS 已开启,祝你生活美满。", 17 | "probejs.isolation": "ProbeJS 将会隔离脚本作用域,只有具有 export 关键字的方法 / 变量可被其他脚本访问。", 18 | "probejs.no_isolation": "ProbeJS 现在不会隔离脚本作用域,请小心变量名污染。", 19 | "probejs.wiki": "需要 ProbeJS 教程的话,请点击:", 20 | "probejs.lint_passed": "恭喜你,你的代码经受住了考验。" 21 | } 22 | -------------------------------------------------------------------------------- /src/main/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prunoideae/ProbeJS/008b38b716ad53beb4989eab6df7aa5949815750/src/main/resources/icon.png -------------------------------------------------------------------------------- /src/main/resources/kubejs.plugins.txt: -------------------------------------------------------------------------------- 1 | moe.wolfgirl.probejs.plugin.BuiltinProbeJSPlugin client 2 | moe.wolfgirl.probejs.plugin.ProbeJSKJSPlugin -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "ProbeJS", 4 | "pack_format": 8 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/probejs.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "compatibilityLevel": "JAVA_17", 4 | "package": "moe.wolfgirl.probejs.mixins", 5 | "client": [ 6 | "LootTableMixin", 7 | "RecipeManagerMixin", 8 | "TranslatableMixin", 9 | "JavaWrapperMixin" 10 | ], 11 | "mixins": [ 12 | 13 | ], 14 | "injectors": { 15 | "defaultRequire": 1 16 | }, 17 | "minVersion": "0.8" 18 | } --------------------------------------------------------------------------------