Fork me on GitHub

What is JSweet?

JSweet is a project that aims at programming modern Web applications (i.e. HTML5+JavaScript) in plain Java, using our favorite Java IDEs. JSweet is fully open source, licensed under Apache License, Version 2.0.

Why JSweet?

For two reasons.

First, because with HTML5 and its new JavaScript API, modern Web browsers have become the most universal execution platform, efficient and plugable on most operating systems. For instance, the support of SVG and the canvas API allows the programmers to develop cutting edge UI, CSS transforms allows for animations that harmlessly take advantage of the GPU, CSS media queries supports adaptive design, debugging and profiling is natively supported by modern browsers, and so on.

Second, because Java remains the most efficient language for building complex applications, with its world-wide community support for various IDEs. Its simple syntax and strong typing (but not too strong), makes it a good compromise for most developer profiles. For instance, Eclipse’s Java incremental compiler and refactoring tools make Java the most comfortable language to handle the complexity of large scale projects.

In short, the idea behind JSweet it to take advantage of both worlds: Java for the coding, and JavaScript for the deployment and execution.

Who should use JSweet?

Any programmer who wants to build client-side HTML5 applications and who cares about advanced IDE support: incremental compilation, call graphs, inheritance graph, outlines, refactoring, completion, wizards, strong typing, etc.

In our opinion, the Java language offers the best IDE support, and will still remain ahead for a while, especially because the Java community actually do care about developing robust applications (more than developing good-looking ones).

How does JSweet work?

JSweet relies on a Java transpiler built with Javac. The Java source code is transformed into TypeScript, which in turn is transformed into JavaScript. During the transformation process, the line mapping is preserved in a JavaScript map file so that you can debug your Java source code directly within your favorite browser’s debugger (or within your Java IDE with plugins such as SDBG).

JSweet provides a core API for dealing with specific JavaScript features such as arrays, global variables, etc. Also, it supports a large set of JavaScript APIs, which are directed transformed from existing TypeScript APIs (see DefinitelyTyped’s TSD) to well-packaged Maven artifacts, so-called JSweet candies. Thus, programmers have access to hundreds of JavaScript frameworks, components, and libraries directly from Java.

How does JSweet compare to related tools?

Java WebStart:
Besides the Java syntax, they have nothing in common. Java WebStart allows the browser to run native Java application within an embedded JRE. JSweet runs JavaScript code directly in the browser and the Java code does not use any of the Java SE API. Instead it uses the W3C standard WEB APIs. JSweet can provide support for Java APIs so that legacy Java applications running with WebStart can work directly in the browser. It is explained in this video.
GWT:
GWT takes Java code and translates part of it to JavaScript so that the client side application can run in a Web browser. GWT and JSweet are actually very different in their approaches, since GWT aims at providing a full Java emulation (following the Java Language Semantics). Also GWT is more of a framework than a language. It comes with a toolkit that binds the GWT programs to a specific API. It is very far from JSweet that simply intends to add a Java syntax layer to web programming. GWT approach of emulating Java is much more ambitious, but suffers from various drawbacks, the main problem being that the core GWT objects are an emulation of Java objects, which has direct impacts on:

  • Performance: primitive objects such as numbers are replaced with wrapping objects, which entails slowdown on pure calculation.
  • Memory consumption: Java emulation, for example hashcodes, requires extra information at runtime compared to JavaScript programs (and hence JSweet programs).
  • Code readability: still because of Java emulation, the generated JavaScript code is not programmer-friendly and cannot be used as is, on contrary to JSweet, that produces readable TypeScript and JavaScript code.
  • Straightforward in-browser debugging: since there is no straightforward mapping between the Java code and the generated JavaScript code, GWT programs cannot be debugged directly on the generated JavaScript code. GWT debugging requires an abstraction layer provided by source maps, which means that the programmer looses track of what actually happens at low level in the JavaScript VM.

More details on comparing JSweet to GWT can be found here.
In-browser Java VMs (Javapoly, doppiojvm, etc.):
Clearly, it is not JSweet purpose to emulate Java on top of JS. JSweet is a transpiler to generate and run pure JavaScript programs, not Java ones. Like for GWT, emulating Java is bad for performance and interoperability with JavaScript.
TypeScript:
JSweet can be seen as a syntax mapping to TypeScript. JSweet maps Java structures to TypeScript structures (modules, classes, interfaces, enums). It also support the same level of type safety by mapping to TypeScripts types (functional types, tuple types, object types, union types, etc.). So, using JSweet is very similar to using TypeScript to program JavaScript application. You can use the same tooling and, like with TypeScript, it is easy to bridge JavaScript APIs. The main difference besides the Java syntax is that with JSweet you can rely on the Java IDEs support and building tools such as Maven.

What are JSweet API packages?

JSweet provides the following packages:

  • Package jsweet.lang: defines the basic JavaScript APIs, plus core JSweet features (mostly annotations) for extending Java
  • Package jsweet.dom: defines the W3C APIs.
  • Package jsweet.util: contains Java API to map specific JavaScript language constructs (the Globals class contains useful cast functions).
  • Package jsweet.util.function: complements the common functional interfaces (for basics, the program can use the java.util.function package, and java.lang.Runnable.
  • Package jsweet.util.union: defines union types.
  • Package jsweet.util.tuple: defines tuple types.
  • Package def.<libname>: the def package is by convention used for external library definitions (i.e. JavaScript libraries APIs, a.k.a JSweet candies). For example: def.jquery or def.angular.

Javadocs and candies are accessible at the candies page.

Also, JSweet allows the use of Java packages with some limitations:

  • Package java.lang: by default, most basic functions on strings, objects, numbers, etc. It requires the J4TS candy for more extensive support.
  • Package java.util: requires the J4TS candy to support most collections and utility classes.
  • Package java.io: requires the J4TS candy to support some Java IO styles and a local-storage-based FS (beta).
  • Package java.*: generally speaking, most Java packages can be implemented in JSweet, following the way it is done in J4TS. Checkout this project and check if the Java feature you are looking for is already implemented in JSweet. If not, please request it or, even better, contribute to the project 😉

What are the known limitations to JSweet?

The key point is that you have to remember that JSweet is *NOT* Java, it is Java syntax only. There is no deep Java emulation in JSweet and a running JSweet-generated program it actually a regular JavaScript program. There are many advantages to this approach, but it also means that there are small behavioral differences between JSweet and Java programs. For instance, JSweet cannot make a difference at runtime between a long and an int, which eventually impacts method overloading. Thankfully, JSweet makes sure that for most use cases, JSweet programs behave like Java programs, so that it is usually very straightforward to have the same code running in a JRE and in a JS container (if using APIs supported in both environments). JSweet provides some basic support to common Java APIs in the J4TS project, which is available as a candy (and more APIs should be supported in the future). Make sure to check the language specifications in order to fully understand the differences between Java and JSweet.

Can I run existing Java programs with JSweet?

If your program only uses common Java APIs as implemented in J4TS, yes. Of course, most legacy programs will probably use plenty of Java libraries that are not supported yet. In that case, you need to provide a JSweet implementation of the libraries and publish them as JSweet candies (so that they can be used by other JSweet programs).

What should I do to use a *JavaScript* library that is not currently supported by JSweet?

First go to the JSweet candy repository and check if your library is not already there (see http://www.jsweet.org/jsweet-candies). If not yet there, here is what you can do.

  • The simplest way: define the API with @Ambient annotated classes and interfaces, so that you create a bridge to the existing library. By using @Ambient, you can inline the external classes and interface declarations in your program.
  • The preferred way: define the API within the def.<yourlibname> package. Consider creating your own candy within a Maven artifact, using this template, and don’t hesitate to contribute to the JSweet Candy organization. For details on how to define an API (definitions) with JSweet, please refer to the Language Specifications.
  • If you can, contribute the TypeScript definition file to DefinitelyTyped. This file will be published to the TSD repository and to the JSweet candy repository.

What should I do to use a *Java* library that is not currently supported by JSweet?

First go to the J4TS organization and check if your library is not already there, maybe in the J4TS candy. If not yet there, you need to provide a JavaScript implementation of your Java library and publish it as a JSweet candy. This might be less complicated than it seems. First start from the following template. Then decide which strategy is best for you depending on your context.

  • Case one — compile the library with JSweet: if the library is open source or if you own the source code, you might be able to compile it with JSweet with no or little code modification. What you will get is a TypeScript/JavaScript version of your library (usable from any TypeScript or JavaScript program). Then, you just need to package it as a candy, similarly to what is done in the J4TS project.
  • Case two — wrap an existing JavaScript library: there are plenty of JavaScript libraries out there for doing almost everything. There is a big chance that the Java library you are trying to access to already has a JavaScript counterpart, and that this library is available as a JSweet candy. There is also a big chance that the API core functions only differ slightly from the Java version. In that case, all you need to do it to write a wrapper to the JavaScript library that conforms to the targeted Java API. Then publish this wrapper as a candy that depends on the wrapped JavaScript library.
  • Case three: when none of the above cases is possible, then you need to rewrite the library from scratch. This is of course a bad solution, unless the library is quite simple.

Can I get rid of JSweet and go back to classical Web languages in the middle of a project?

Sure. You can stop maintaining your Java source code and use the generated TypeScript or JavaScript code as your new source base. JSweet can produce coder-friendly output by switching off the sourceMap mode.

Can I mix JSweet, TypeScript, and JavaScript code within the same project?

Yes you can. Since JSweet candies are generated from Definitely Typed definition files, you can share these files across your developer teams so that Java programmers and TypeScript programmers are ensured to use exactly the same libraries. Of course, you can also use JSweet to translate a Java model to TypeScript or JavaScript so that it can be used by the Web developers.

Can I use Java 8 lambda expressions in my JSweet programs and are they similar to JavaScript functions?

Yes, and yes. For instance: element.addEventListener("change", (event) -> { /* listener code here */ });

Can I use Java arrays in my JSweet programs?

Yes, you can use native Java arrays and switch to JavaScript ones with array(myJavaArray) (see the org.jsweet.util.Globals class for all the cast methods).

Can I debug my Java files directly in the browser or in Eclipse?

JSweet generates source maps (thanks to the TypeScript compiler) and link them to the Java source code. Hence, one can debug Java files in the browser as if it was JavaScript ones. We recommend to use Chrome, because it supports Java syntax highlighting, but it is possible with all modern browsers. It is also possible to debug Java code in the IDE by using plugins such as SDBG.

Does JSweet support AMD, CommonJS, or UMD modules?

Yes! JSweet relies on TypeScript that supports various JavaScript module management mechanisms. JSweet takes an option that allows the programmer to choose between the different module management kinds.

Can write JUnit test for unit testing JSweet programs?

Sure. But at this point, it may require a little bit of work.

The first solution is to use one of the JavaScript Frameworks for unit testing. We are interested in any contribution that would explain how to use a JavaScript test framework under JSweet.

The second solution is to unit test JSweet programs from Java (with JUnit) by using the jsweet.util.Globals.$export function, which makes values available to the caller Java program.

For instance, the following program makes available to the Java calling program the value “ok” in the variable “result”.

package test;

import static jsweet.util.Globals.$export;

public class MainExecution {

   public static void main(String[] args) {
      $export("result", "ok");
   }
}

Here is a typical JUnit test that uses the JSweet transpiler API to ensure that the main method is correctly executed:

@Test
public void testMainExecution() {
   TestLogHandler logHandler = new TestLogHandler();
   try {
      JSweetTranspiler transpiler = new JSweetTranspiler("outDir", null, System.getProperty("java.class.path"));
      transpiler.setModuleKind(ModuleKind.commonjs);
      // here, the transpiler uses node.js to evaluate the generated JavaScript
      EvaluationResult result = transpiler.eval(logHandler, "test.MainExecution.java");
      Assert.assertEquals("There should be no errors", 0, logHandler.reportedProblems.size());
      assertEquals("Main method was not executed", "ok", (String)result.get("result"));
   } catch (Exception e) {
      e.printStackTrace();
      fail("Exception occured while running test");
   }
}

To get access to the JSweet API, add a dependency to the transpiler artifact (group id: org.jsweet), as shown in the following pom.xml

<project ...>
  ...
  <!-- repositories -->
  <repositories>
    <repository>
      <id>jsweet-central</id>
      <name>libs-release</name>
      <url>http://repository.jsweet.org/artifactory/libs-release-local</url>
    </repository>
    <repository>
      <snapshots />
      <id>jsweet-snapshots</id>
      <name>libs-snapshot</name>
      <url>http://repository.jsweet.org/artifactory/libs-snapshot-local</url>
    </repository>
  </repositories>
  <!-- end repositories -->
  ...
  <!-- dependencies -->
  <dependencies>
    <dependency> 
      <groupId>org.jsweet</groupId>
      <artifactId>jsweet</artifactId>
      <version>(current version)</version>
    </dependency>
  </dependencies>
  <!-- end dependencies -->
  ...
</project>