Add Documentation
ScriptCraft/pipeline/head This commit looks good Details

This commit is contained in:
TheBrokenRail 2020-05-24 14:09:25 -04:00
parent 48796de9de
commit 37f11a92f0
16 changed files with 191 additions and 110 deletions

2
Jenkinsfile vendored
View File

@ -20,7 +20,7 @@ pipeline {
allowMissing: false,
alwaysLinkToLastBuild: false,
keepAll: false,
reportDir: 'src/main/ts/lib/typedoc',
reportDir: 'src/main/ts/build/typedoc',
reportFiles: 'index.html',
reportName: 'TypeDoc'
]

View File

@ -2,7 +2,7 @@
JS API for Minecraft
## API
[View TypeDoc](https://jenkins.thebrokenrail.com/job/ScriptCraft/job/master/TypeDoc/)
[View API](docs/README.md)
## Example
[View Examples](examples/README.md)

View File

@ -69,9 +69,8 @@ publishing {
artifact(remapJar) {
builtBy remapJar
}
artifact(typescriptAPI) {
classifier 'api'
builtBy typescriptAPI
artifact(apiJar) {
builtBy apiJar
}
}
}
@ -79,6 +78,5 @@ publishing {
maven {
url '/data/maven'
}
mavenLocal()
}
}

68
docs/BRIDGES.md Normal file
View File

@ -0,0 +1,68 @@
# Bridges
To communicate between Java and JS code, ScriptCraft utilizes a system called bridges.
## JS-To-Java Bridges
### Define The Bridge
```java
import com.thebrokenrail.scriptcraft.core.ScriptCraftCore;
import com.thebrokenrail.scriptcraft.core.ScriptCraftEntryPoint;
public class ExampleBridges implements ScriptCraftEntryPoint {
@Override
public String getEntryPoint() {
return "example-bridges";
}
@Override
public void registerBridges() {
ScriptCraftCore.addBridge("ExampleBridges.helloWorld", args -> {
System.out.println("Hello World! " + args[0]);
return "Return Value";
});
}
}
```
### Use The Bridge
```javascript
import { useBridge } from 'scriptcraft-core';
console.log(useBridge('ExampleBridges.helloWorld', 'Argument'));
```
## Java-To-JS Bridges
### Define The Bridge
```javascript
import { addBridge } from 'scriptcraft-core';
addBridge('ExampleBridges.helloWorld', arg => {
console.log('Hello World! ' + args[0]);
return 'Return Value';
});
```
### Use The Bridge
```java
import com.thebrokenrail.scriptcraft.core.ScriptCraftCore;
public class ExampleBridges {
public static void run() {
System.out.println((String) ScriptCraftCore.useBridge("ExampleBridges.helloWorld", "Argument"));
}
}
```
## Supported Types
| JS Type | Java Type |
| --- | --- |
| Number | ```Double``` (object-form) |
| Boolean | ```Boolean``` (object-form) |
| String | ```String``` |
| Array | ```Object[]``` |
| Object | ```null``` |
| ```JavaObject``` | ```Object``` |
### ```JavaObject```
A ```JavaObject``` is a normal JS object with an internal reference to a Java object, the Java object reference cannot be accessed directly from JS, but when it is passed back to Java using a bridge, it will become the referenced Java object.

24
docs/ENTRY_POINTS.md Normal file
View File

@ -0,0 +1,24 @@
# Entry-Points
To add an entry-point to your mod create a class implementing ```ScriptCraftEntryPoint``` and override the method ```getEntryPoint()``` to return your entry-point. Then, add your new class to the ```scriptcraft``` entry-point in ```fabric.mod.json```.
## Example
```java
import com.thebrokenrail.scriptcraft.core.ScriptCraftEntryPoint;
public class ExampleMod implements ScriptCraftEntryPoint {
@Override
public String getEntryPoint() {
return "example-mod";
}
}
```
```json
{
"entrypoints": {
"scriptcraft": [
"<Package>.ExampleMod"
]
}
}
```
This will run ```import `example-mod`;``` on initialization.

10
docs/MODULE_RESOLUTION.md Normal file
View File

@ -0,0 +1,10 @@
# Module Resolution
All ScriptCraft mods have a folder called ```scriptcraft``` in the root of their JAR file, when ScriptCraft is loaded all of these are combined to form a single "virtual" root. It is recommended to use namespaces to avoid conflicts with other mods.
## Supported File Types
- ```.js```
- ```.json```
## Extra Details
- If the import path is a folder, ScriptCraft will load the ```index``` file with one of the supported file types.
- If an extension does not exist in the import path it will look for a file with the same name and one of the supported extensions.

8
docs/README.md Normal file
View File

@ -0,0 +1,8 @@
# API
[View Module Resolution](MODULE_RESOLUTION.md)
[View Entry-Points](ENTRY_POINTS.md)
[View Bridges](BRIDGES.md)
[View TypeDoc](https://jenkins.thebrokenrail.com/job/ScriptCraft/job/master/TypeDoc/)

View File

@ -6,6 +6,17 @@ This is an example of a Minecraft mod made in TypeScript using ScriptCraft.
- ```src/main/java```: Contains Bootstrap Java Code
#### Notes
- When updating the Minecraft version, make sure to update it in the ```src/main/ts/package.json``` file as well
- This will also work with JavaScript if you set ```compilerOptions.allowJs``` and optionally ```compilerOptions.checkJs``` in ```src/main/ts/tsconfig.json``` to ```true```
- NPM dependencies are not bundled
- NPM dependencies are not bundled
- API JARs are not bundled
#### ```typescript``` Gradle Configuration
The ```typescript``` gradle configuration will extract the specified API JAR into ```src/main/ts/build/dependencies```.
#### API JAR Format
```$xslt
- src/
- DTS, TS, JS, or JSON files and folders
- types/
- File structure of node_modules/@types
```

View File

@ -10,7 +10,7 @@
"removeComments": true,
"typeRoots": ["build/dependencies/types"],
"paths": {
"*": ["*", "../build/dependencies/lib/*"]
"*": ["*", "../build/dependencies/src/*"]
},
"baseUrl": "src",
"outDir": "build/ts",

View File

@ -5,7 +5,7 @@ def typescriptOut = new File(typescriptRootFile, 'build/ts')
def nodeModules = new File(typescriptRootFile, 'node_modules')
task typescriptInstall(group: 'typescript', type: Exec) {
task npmInstall(group: 'typescript', type: Exec) {
inputs.file new File(typescriptRootFile, 'package.json')
outputs.dir nodeModules
@ -30,21 +30,18 @@ configurations {
}
}
task extractTypescriptDependencies(group: 'typescript') {
doFirst {
project.delete {
delete typescriptDependencies
}
typescriptDependencies.mkdirs()
configurations.typescript.resolve().each { file ->
project.copy {
from(zipTree(file).matching {
exclude 'META-INF', 'META-INF/**'
})
into typescriptDependencies
task extractTypescriptDependencies(type: Sync, group: 'typescript') {
dependsOn configurations.typescript
from {
configurations.typescript.collect {
zipTree(it).matching {
exclude 'META-INF', 'META-INF/**'
}
}
}
into typescriptDependencies
}
task typescript(group: 'typescript', type: Exec) {
@ -64,7 +61,7 @@ task typescript(group: 'typescript', type: Exec) {
}
}
typescript.dependsOn typescriptInstall
typescript.dependsOn npmInstall
typescript.dependsOn extractTypescriptDependencies
processResources.dependsOn typescript

View File

@ -1,20 +1,15 @@
package com.thebrokenrail.scriptcraft.api;
import com.thebrokenrail.scriptcraft.api.bridge.Bridges;
import com.thebrokenrail.scriptcraft.core.ScriptCraftEntrypoint;
import com.thebrokenrail.scriptcraft.core.ScriptCraftEntryPoint;
@SuppressWarnings("unused")
public class ScriptCraftAPI implements ScriptCraftEntrypoint {
public class ScriptCraftAPI implements ScriptCraftEntryPoint {
@Override
public String getModID() {
public String getEntryPoint() {
return "minecraft";
}
@Override
public String getModIndex() {
return "index";
}
@Override
public void registerBridges() {
Bridges.init();

View File

@ -6,7 +6,6 @@ import com.thebrokenrail.scriptcraft.core.quickjs.QuickJSManager;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.loader.api.FabricLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;
@ -51,18 +50,12 @@ public class ScriptCraftCore implements ModInitializer {
QuickJSManager.init(new QuickJSManager.Task() {
@Override
protected Object run(QuickJSNative quickjs) {
List<ScriptCraftEntrypoint> mods = FabricLoader.getInstance().getEntrypoints(NAMESPACE, ScriptCraftEntrypoint.class);
List<ScriptCraftEntrypoint> initializedMods = new ArrayList<>();
for (ScriptCraftEntrypoint mod : mods) {
if (MOD_ID_PATTERN.matcher(mod.getModID()).matches()) {
mod.registerBridges();
initializedMods.add(mod);
} else {
System.err.println("Invalid Mod ID: " + mod.getModID());
}
List<ScriptCraftEntryPoint> mods = FabricLoader.getInstance().getEntrypoints(NAMESPACE, ScriptCraftEntryPoint.class);
for (ScriptCraftEntryPoint mod : mods) {
mod.registerBridges();
}
for (ScriptCraftEntrypoint mod : initializedMods) {
quickjs.run("import '" + mod.getModID() + "';");
for (ScriptCraftEntryPoint mod : mods) {
quickjs.run("import `" + mod.getEntryPoint().replaceAll("`", "\\`") + "`;");
}
return null;
}

View File

@ -1,9 +1,7 @@
package com.thebrokenrail.scriptcraft.core;
public interface ScriptCraftEntrypoint {
String getModID();
String getModIndex();
public interface ScriptCraftEntryPoint {
String getEntryPoint();
default void registerBridges() {
}

View File

@ -1,7 +1,7 @@
package com.thebrokenrail.scriptcraft.core.quickjs;
import com.thebrokenrail.scriptcraft.core.ScriptCraftCore;
import com.thebrokenrail.scriptcraft.core.ScriptCraftEntrypoint;
import com.thebrokenrail.scriptcraft.core.ScriptCraftEntryPoint;
import net.fabricmc.loader.api.FabricLoader;
import java.io.BufferedReader;
@ -18,40 +18,40 @@ import java.util.List;
@SuppressWarnings("unused")
public class QuickJSModules {
public static String loadModule(String name) {
String[] arr = name.split(File.separator, 2);
if (ScriptCraftCore.MOD_ID_PATTERN.matcher(arr[0]).matches()) {
List<ScriptCraftEntrypoint> mods = FabricLoader.getInstance().getEntrypoints(ScriptCraftCore.NAMESPACE, ScriptCraftEntrypoint.class);
if (arr.length == 1) {
return null;
} else {
for (ScriptCraftEntrypoint mod : mods) {
if (mod.getModID().equals(arr[0])) {
//noinspection CatchMayIgnoreException
try {
InputStream stream = mod.getClass().getResourceAsStream(File.separator + ScriptCraftCore.NAMESPACE + File.separator + arr[0] + File.separator + arr[1]);
List<ScriptCraftEntryPoint> mods = FabricLoader.getInstance().getEntrypoints(ScriptCraftCore.NAMESPACE, ScriptCraftEntryPoint.class);
for (ScriptCraftEntryPoint mod : mods) {
//noinspection CatchMayIgnoreException
try {
InputStream stream = mod.getClass().getResourceAsStream(File.separator + ScriptCraftCore.NAMESPACE + File.separator + name);
if (stream != null) {
StringBuilder textBuilder = new StringBuilder();
try (Reader reader = new BufferedReader(new InputStreamReader(stream))) {
int c;
while ((c = reader.read()) != -1) {
textBuilder.append((char) c);
}
}
stream.close();
return textBuilder.toString();
}
} catch (IOException e) {
if (stream != null) {
StringBuilder textBuilder = new StringBuilder();
try (Reader reader = new BufferedReader(new InputStreamReader(stream))) {
int c;
while ((c = reader.read()) != -1) {
textBuilder.append((char) c);
}
}
stream.close();
return textBuilder.toString();
}
} catch (IOException e) {
}
}
return null;
}
private static final String[] SUPPORTED_EXTENSION = new String[]{"", ".js", ".json", ".mjs"};
private static final String[] SUPPORTED_EXTENSIONS;
static {
final String[] supportedExtensions = new String[]{"", ".js", ".json", ".mjs"};
SUPPORTED_EXTENSIONS = new String[supportedExtensions.length * 2];
for (int i = 0; i < supportedExtensions.length; i++) {
SUPPORTED_EXTENSIONS[i + supportedExtensions.length] = File.separator + "index" + supportedExtensions[i];
SUPPORTED_EXTENSIONS[i] = supportedExtensions[i];
}
}
private static final String[] BUILTIN_MODULES = new String[]{"scriptcraft-core"};
public static String normalizeModule(String baseName, String name) {
@ -61,12 +61,15 @@ public class QuickJSModules {
String normalizedPath;
baseName = baseName.replaceAll("/", File.separator);
name = name.replaceAll("/", File.separator);
if (name.startsWith(".")) {
Path parentPath = Paths.get(baseName.replaceAll("/", File.separator)).getParent();
Path parentPath = Paths.get(baseName).getParent();
if (parentPath == null) {
parentPath = Paths.get(File.separator);
}
normalizedPath = Paths.get(parentPath.toString(), name.replaceAll("/", File.separator)).normalize().toString();
normalizedPath = Paths.get(parentPath.toString(), name).normalize().toString();
if (normalizedPath.charAt(0) == File.separatorChar) {
normalizedPath = normalizedPath.substring(1);
}
@ -74,31 +77,9 @@ public class QuickJSModules {
normalizedPath = name;
}
String result = null;
boolean success = false;
String[] arr = normalizedPath.split(File.separator, 2);
if (ScriptCraftCore.MOD_ID_PATTERN.matcher(arr[0]).matches()) {
List<ScriptCraftEntrypoint> mods = FabricLoader.getInstance().getEntrypoints(ScriptCraftCore.NAMESPACE, ScriptCraftEntrypoint.class);
if (arr.length == 1) {
for (ScriptCraftEntrypoint mod : mods) {
if (mod.getModID().equals(arr[0])) {
result = arr[0] + File.separator + mod.getModIndex();
success = true;
break;
}
}
} else {
result = arr[0] + File.separator + arr[1];
success = true;
}
}
if (success) {
for (String extension : SUPPORTED_EXTENSION) {
if (loadModule(result + extension) != null) {
return result + extension;
}
for (String extension : SUPPORTED_EXTENSIONS) {
if (loadModule(normalizedPath + extension) != null) {
return normalizedPath + extension;
}
}

View File

@ -1,6 +1,4 @@
{
"name": "scriptcraft-api",
"version": "1.0.0-SNAPSHOT",
"devDependencies": {
"@typescript-eslint/eslint-plugin": "latest",
"@typescript-eslint/parser": "latest",

View File

@ -3,7 +3,7 @@ def typescriptRootFile = new File(rootDir.absolutePath, typescriptRoot)
def nodeModules = new File(typescriptRootFile, 'node_modules')
task typescriptInstall(group: 'typescript', type: Exec) {
task npmInstall(group: 'typescript', type: Exec) {
inputs.file new File(typescriptRootFile, 'package.json')
outputs.dir nodeModules
@ -34,23 +34,23 @@ task typescript(group: 'typescript', type: Exec) {
}
}
typescript.dependsOn typescriptInstall
typescript.dependsOn npmInstall
task typescriptAPI(group: 'typescript', type: Jar) {
task apiJar(group: 'typescript', type: Jar) {
into('/types') {
from new File(typescriptRootFile as File, 'types')
}
into('/lib') {
into('/src') {
from dtsOut
}
classifier 'api'
}
typescriptAPI.dependsOn typescript
apiJar.dependsOn typescript
artifacts {
archives typescriptAPI
archives apiJar
}
task eslint(group: 'typescript', type: Exec) {
@ -61,7 +61,7 @@ task eslint(group: 'typescript', type: Exec) {
args 'run', 'eslint'
}
eslint.dependsOn typescriptInstall
eslint.dependsOn npmInstall
processResources.dependsOn typescript
@ -84,7 +84,7 @@ task typedoc(group: 'typescript', type: Exec) {
}
}
typedoc.dependsOn typescriptInstall
typedoc.dependsOn npmInstall
processResources {
from(typescriptOut.absolutePath) {