I have a small Java web application being built with Gradle that includes some custom vanilla JavaScript. I'd like to minify the custom JS code using Google Closure Compiler.
All of the documentation for the Closure Compiler seems to be around using the CLI or the JSON API for minifying JS. I'd much prefer to call the Java API directly from Gradle in e.g. a Copy task.
I'd like to avoid
Node solutions
Calling out to the CLI and using java -jar
HTTP calls to the JSON API
This example is out-of-date and does not reflect the most recent Java API. This question is several years old, though most of the API calls seem similar to the current Java API.
Has anyone else minified JavaScript from Gradle using the Google Closure Compiler Java API directly?
I have a working solution:
task processWebapp(type: Copy) {
from('src/main/webapp') {
include "**/*"
}
eachFile {
if (it.sourceName.endsWith('.js') && !it.sourceName.endsWith('.min.js')) {
it.exclude()
Reader reader = it.file.newReader()
String source = ""
try {
source = reader.readLines().join("\r\n")
} finally {
reader.close()
}
com.google.javascript.jscomp.Compiler compiler = new com.google.javascript.jscomp.Compiler(System.err)
CompilerOptions options = new CompilerOptions()
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(
options)
SourceFile extern = SourceFile.fromCode("externs.js", "")
SourceFile input = SourceFile.fromCode(it.sourceName, source)
compiler.compile(extern, input, options)
String transpiled = compiler.toSource()
def directoryPath = it.relativePath - it.sourceName
File theDir = new File("build/resources/main/${directoryPath}")
if (!theDir.exists()) {
theDir.mkdirs()
}
PrintWriter writer = new PrintWriter("build/resources/main/${it.relativeSourcePath}", "UTF-8")
try {
writer.println(transpiled)
} finally {
writer.close()
}
}
}
destinationDir = file('build/resources/main')
}
This task copies everything from src/main/webapp to build/resources/main while transpiling (minifying) all files ending in .js (but not ending in .min.js) en-route. Gradle then packages and embeds those resources in the resulting jar.
Hope this helps someone else out there using Google Closure Compiler and Gradle.
Related
I need to check folders, files and file details (file size) for given directory using JavaScript in standalone karate.jar and this validation is part of test automation script.
I understand this can be achieved by writing custom java packages in maven and use them in JavaScript of karate script.
How to implement this in standalone karate.jar. Pass directory path and directory name as two arguments to JavaScript functions which validate given directory name exists in provided directory path. Its kind of handling file system objects.
I understand the karate supports only read the files and not the read folder names and file names/ file details in directory.
Read files in karate script : https://intuit.github.io/karate/#reading-files
The allowed file extensions are : .json, .xml, .yaml, .csv, .txt, .feature
Please guide me how to implement this using Javascript in karate?
Thanks
Chandra.
Yes this is harder with the standalone JAR. Here are some suggestions:
write a small JAR file (one-time) that you can include in your stand-alone project: https://stackoverflow.com/a/56458094/143475
use OS commands via karate.exec(), e.g. on windows * def homePath = karate.exec('cmd /c echo %HOMEPATH%')
with some work, you can create pure JS scripts that use the JVM libraries, but you need a good understanding of what exists in Java and these are very hard to debug so I don't recommend this approach. I am giving an example below:
* def findFile =
"""
function(file, condition) {
var root = new java.io.File(file);
function recurse(file) {
var list = file.listFiles();
for (var i = 0; i < list.length; i++) {
var f = list[i];
if (f.directory) {
// karate.log('recurse:', f);
return recurse(f);
} else {
var path = f.path;
// karate.log('scan:', path);
if (condition(path)) {
karate.log('*** found:', path);
return f;
}
}
}
}
return recurse(root);
}
"""
* def filter = function(x){ return x.contains('/test-') && x.endsWith('.log') }
* def found = findFile('.', filter)
* print 'found:', found
I am writing in Javascript using classes. Example:
'use strict';
class MyClassName
{
myGame;
constructor(game)
{
this.myGame = game;
}
startGame()
{
this.startActions();
}
startActions()
{
//
}
}
When i try to compress it in PhpStorm by using Assets Compressor, i get error:
[ERROR] 3:6:identifier is a reserved word
How i can make correct compressing my code in PhpStorm? Are there any ways to compress JS code that uses classes?
Assets Compressor plugin only supports ECMAScript 2015, it will throw errors on new syntax.
Please try following the instructions from https://www.jetbrains.com/help/idea/minifying-javascript.html: install either UglifyJS or the Closure Compiler and set it up as a file watcher to run from the IDE
I'm trying to use the Javascript library multilang-extract-comments from within a Java application, so I decided to use Rhino as the Javascript engine and Rhinodo to interface between Rhino and NodeJS. However, I cannot figure out how to actually use Rhinodo. I've looked at the code for the projects that use Rhinodo (maven plugins for brunch, jshint, and recess), but I find the code to be pretty arcane. I've tried implementing code like the following (with some editing for my application):
rhinodoBuilder
.destDir(rhinodoDestDir)
.moduleFactory(nodeModuleProvider)
.consoleFactory(wrappingConsoleFactory)
.env(env)
.build(new BaseFunction() {
#Override
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
Scriptable brunch = (Scriptable) ScriptableObject.callMethod(cx, scope, "require",
new Object[]{Context.javaToJS("brunch", scope)});
Scriptable options = cx.newObject(scope);
ScriptableObject.putProperty(options, "minify", minify);
System.setProperty("user.dir", userDir.getAbsolutePath());
ScriptRuntime.doTopCall(ScriptableObject.getTypedProperty(brunch, "build", Function.class),
cx, scope, thisObj, new Object[]{
options,
new BaseFunction() {
#Override
public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
return Undefined.instance;
}
}
});
return Undefined.instance;
}
});
(from the brunch maven plugin)
However, this code doesn't work for me. I either get an error in trying to find my Javascript library or, when I use a library it should be able to find (I used fs as an example), I get a NullPointerException. Could someone please tell me what I'm missing here?
Note: both Rhinodo and the maven plugins that use it can be found in MuleSoft's GitHub Repositories
I didn't find a way for this to work out. However, if anyone else is determined to use Rhinodo in this way, part of the problem was that I wasn't actually using a NodeModuleProvider. In order to try and load my required Node modules, I used code similar to the following:
// create a module provider for all the node modules in META-INF
String prefix = "META-INF/node_modules/";
String[] moduleNames = {"amodule","anothermodule","modulethethird"};
ArrayList<NodeModuleImpl> moduleList = new ArrayList<NodeModuleImpl>();
for (String module: moduleNames) {
moduleList.add(NodeModuleImplBuilder.fromFolder(prefix+module));
}
NodeModuleProviderImpl nmp = new NodeModuleProviderImpl(moduleList);
I then used the NodeModuleProvider in the RhinodoBuilder.moduleFactory() method (which you can see used in my code snippet in the question). However, be warned that you may have to solve the problem which I couldn't: using imported modules later from Rhino.
In the end I decided that it made much more sense to call my javascript as a command using exec, so I recommend that anyone else who undertakes something similar just use that solution.
I am trying to write a gradle task which will minify all my project's javascript files. I am using a gradle library: com.eriwen.gradle.js. This library contains a task called minifyJs where we define the source file we want to minify and the destination of the minified file:
minifyJs {
source = file(sourcePathString)
dest = file(targetPathString)
}
What I want to do is call execute this task for EVERY javascript file in my project and produce a minified version of it in a new path for EACH file. This would require me to run the minifyJs task multiple times each time with different source and dest values, but I can't seem to find a solution on how to do this. One person had suggested that we use a loop to create a new task of type: minifyJs for each javascript file but this takes a huge amount of time and will create 250+ tasks i.e. not effective at all.
Since calling a task inside another task doesn't work (and using task.execute() is bad practice) I'm essentially looking for a workaround that lets me achieve this:
task customMinify {
def jsFileTree = fileTree('my/javascript/files')
jsFileTree.forEach {
def jsFile = it
minifyJs {
source = file(jsFile.getPath())
dest = file('new/path/to/file.js')
}
}
}
which obviously doesn't work since we can't call minifyJs inside another task.
I'm really sorry that this gap has continued to exist in the gradle-js-plugin.
Since generating tasks won't do, I suggest that you write a custom task under buildSrc combining my JsMinifier and the MinifyJsTask.
If you're willing to wait 8 hours or so, I can write an implementation of this later if you like.
EDIT: Here's a gist for a ClosureMinifyTask you can throw in buildSrc/src/main/groovy/com/eriwen/gradle/js/tasks and it'll minify each file individually and produce individual source map files etc.
buildSrc/build.gradle:
repositories {
mavenCentral()
}
dependencies {
compile localGroovy()
compile gradleApi()
compile ('com.google.javascript:closure-compiler:v20151015') {
exclude module: 'junit'
}
}
Sample Usage:
task mini(type: com.foo.bar.ClosureMinifyTask) {
source = "src/js"
dest = "${buildDir}/js/minified"
}
I am trying to use Google Closure Compiler API from my Java code. Function compile() receives the original source code, and returns the compiled source code in a String.
This code will run in Google App Engine, but when I deploy and run it I get an "server error". Without calling function below, I don't get any errors. At time of compilation I get warning "compiler.jar will not be available on server's classpath". Compiler.jar is the library I downloaded from Closure Compiler project website.
Any ideas of how to go around this?
Thanks a million,
import com.google.javascript.jscomp.*;
public static String compile(String code)
{
com.google.javascript.jscomp.Compiler.setLoggingLevel(Level.INFO);
com.google.javascript.jscomp.Compiler compiler = new com.google.javascript.jscomp.Compiler();
CompilerOptions options = new CompilerOptions();
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
JSSourceFile js = JSSourceFile.fromCode("input.js", code);
WarningLevel.QUIET.setOptionsForWarningLevel(options);
compiler.compile(null, js, options);
return compiler.toSource();
}
try this:
import com.google.javascript.jscomp.*;
public static String compile(String code)
{
com.google.javascript.jscomp.Compiler.setLoggingLevel(Level.INFO);
com.google.javascript.jscomp.Compiler compiler = new
com.google.javascript.jscomp.Compiler();
CompilerOptions options = new CompilerOptions();
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
JSSourceFile js = JSSourceFile.fromCode("input.js", code);
List<SourceFile> list = new ArrayList<SourceFile>();
list.add(js);
WarningLevel.QUIET.setOptionsForWarningLevel(options);
compiler.compile(new ArrayList<SourceFile>(), list, options);
return compiler.toSource();
}
At time of compilation I get warning "compiler.jar will not be available on server's classpath".
You might have to move the compiler.jar to your WEB-INF/lib.
this is likely the cause for the 500: if you don't deploy the compiler.jar as part of your webapp, your servlet (or whatever) will fail with a NoClassDefFoundError.
If you haven't done so you need to disable threading in the compiler to run on app engine: see "disableThreads" in Compiler.java
http://code.google.com/p/closure-compiler/source/search?q=Compiler.java&origq=Compiler.java&btnG=Search+Trunk
Normally, the compiler spawns a new thread to be sure that it has a larger than standard stack.