Skip to content

Instantly share code, notes, and snippets.

@mingfang
Last active October 10, 2020 05:45
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mingfang/3784a0a6e58c24dda687 to your computer and use it in GitHub Desktop.
Save mingfang/3784a0a6e58c24dda687 to your computer and use it in GitHub Desktop.
Java tool to transform React JSX into Javascript
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.commonjs.module.Require;
import org.mozilla.javascript.commonjs.module.RequireBuilder;
import org.mozilla.javascript.commonjs.module.provider.SoftCachingModuleScriptProvider;
import org.mozilla.javascript.commonjs.module.provider.UrlModuleSourceProvider;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*
* Depends on Mozilla Rhino. Does not work with the JRE built-in version
*
* <dependency>
* <groupId>org.mozilla</groupId>
* <artifactId>rhino</artifactId>
* <version>1.7R4</version>
* </dependency>
*/
public class JSXTransformer {
// ------------------------------ FIELDS ------------------------------
private List<String> modulePaths;
private String jsxTransformerJS;
private Context ctx;
private Scriptable exports;
private Scriptable topLevelScope;
private Function transform;
// --------------------------- main() method ---------------------------
public static void main(String args[]) throws URISyntaxException {
JSXTransformer jsxTransformer = new JSXTransformer();
//Using the CDN does not work
//jsxTransformer.setModulePaths(Arrays.asList("http://fb.me/"));
jsxTransformer.setModulePaths(Arrays.asList("public"));
jsxTransformer.setJsxTransformerJS("JSXTransformer-0.10.0.js");
jsxTransformer.init();
String js = jsxTransformer.transform("/** @jsx React.DOM */ React.renderComponent(<h1>Hello, world!</h1>,document.getElementById('example'));");
System.out.println("js = " + js);
}
public void setModulePaths(List<String> modulePaths) {
this.modulePaths = modulePaths;
}
public void setJsxTransformerJS(String jsxTransformerJS) {
this.jsxTransformerJS = jsxTransformerJS;
}
public void init() throws URISyntaxException {
ctx = Context.enter();
try {
RequireBuilder builder = new RequireBuilder();
builder.setModuleScriptProvider(new SoftCachingModuleScriptProvider(
new UrlModuleSourceProvider(buildModulePaths(), null)
));
topLevelScope = ctx.initStandardObjects();
Require require = builder.createRequire(ctx, topLevelScope);
exports = require.requireMain(ctx, jsxTransformerJS);
transform = (Function) exports.get("transform", topLevelScope);
} finally {
Context.exit();
}
}
//mostly copied from org.mozilla.javascript.tools.shell.Global.installRequire()
private List<URI> buildModulePaths() throws URISyntaxException {
List<URI> uris = new ArrayList<URI>(modulePaths.size());
for (String path : modulePaths) {
try {
URI uri = new URI(path);
if (!uri.isAbsolute()) {
// call resolve("") to canonify the path
uri = new File(path).toURI().resolve("");
}
if (!uri.toString().endsWith("/")) {
// make sure URI always terminates with slash to
// avoid loading from unintended locations
uri = new URI(uri + "/");
}
uris.add(uri);
} catch (URISyntaxException usx) {
throw new RuntimeException(usx);
}
}
return uris;
}
public String transform(String jsx) {
Context.enter();
try {
NativeObject result = (NativeObject) transform.call(ctx, topLevelScope, exports, new String[]{jsx});
return result.get("code").toString();
} finally {
Context.exit();
}
}
}
package plugins.jsxtransformer;
import av.common.controllers.JSXTransformer;
import play.Application;
import play.Plugin;
import play.api.templates.Html;
import java.net.URISyntaxException;
/**
* application.conf should look like this
*
* <pre>{@code
* JSXTransformer.modulePaths=["public/"]
* JSXTransformer.JS="JSXTransformer-0.10.0.js"
* } </pre>
*
* play.plugins should look like this
*
* <pre>{@code
* 1000:plugins.jsxtransformer.JSXTransformerPlugin
* } </pre>
*
*
* Use this plugin from your Scala templates like this
*
* <pre>{@code
* <script>
* &#064;plugins.jsxtransformer.JSXTransformerPlugin.transform{
* React.renderComponent(
* <h1>Hello, world!</h1>,
* document.getElementById('example')
* );
* }
* </script>
* } </pre>
*/
public class JSXTransformerPlugin extends Plugin{
// ------------------------------ FIELDS ------------------------------
private static JSXTransformer transformer;
private final Application application;
// -------------------------- STATIC METHODS --------------------------
public static String transform(String jsx) {
return transformer.transform("/**@jsx React.DOM */" + jsx);
}
public static Html transform(Html jsx) {
return Html.apply(transform(jsx.body()));
}
// --------------------------- CONSTRUCTORS ---------------------------
public JSXTransformerPlugin(Application application) {
this.application = application;
}
// ------------------------ CANONICAL METHODS ------------------------
@Override
public void onStart() {
if (transformer == null) {
JSXTransformer instance = new JSXTransformer();
instance.setModulePaths(application.configuration().getStringList("JSXTransformer.modulePaths"));
instance.setJsxTransformerJS(application.configuration().getString("JSXTransformer.JS"));
try {
instance.init();
transformer = instance;
super.onStart();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
}
@kaaloo
Copy link

kaaloo commented May 23, 2014

Thank you for this! I have based a grails jsx asset pipeline plugin partly on your work. Thumbsup for publishing this!

https://github.com/balsamiq/jsx-grails-asset-pipeline

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment