Remove closure in favor of a simple JSMin implementation
Closure is a great js-minifier, but js-minification is really outside the scope of this project. A simple space/line stripper should be suffcient.
This commit is contained in:
6
pom.xml
6
pom.xml
@@ -51,12 +51,6 @@
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.javascript</groupId>
|
||||
<artifactId>closure-compiler</artifactId>
|
||||
<version>v20151015</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -14,7 +14,7 @@ public class InlineStaticResource {
|
||||
if(fileString != null) {
|
||||
switch(format) {
|
||||
case CSS_MIN : return style().with(unsafeHtml(compressCss(fileString)));
|
||||
case JS_MIN : return script().with(unsafeHtml(compressJs(fileString, path)));
|
||||
case JS_MIN : return script().with(unsafeHtml(compressJs(fileString)));
|
||||
case CSS : return style().with(unsafeHtml(fileString));
|
||||
case JS : return script().with(unsafeHtml(fileString));
|
||||
default : return errorAlert;
|
||||
@@ -35,8 +35,8 @@ public class InlineStaticResource {
|
||||
return CSSMin.compress(code);
|
||||
}
|
||||
|
||||
private static String compressJs(String code, String debugPath) {
|
||||
return JSMin.compressJs(code, debugPath);
|
||||
private static String compressJs(String code) {
|
||||
return JSMin.compressJs(code);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,32 +1,305 @@
|
||||
package j2html.utils;
|
||||
/*
|
||||
* JSMin.java 2006-02-13
|
||||
*
|
||||
* Copyright (c) 2006 John Reilly (www.inconspicuous.org)
|
||||
*
|
||||
* This work is a translation from C to Java of jsmin.c published by
|
||||
* Douglas Crockford. Permission is hereby granted to use the Java
|
||||
* version under the same conditions as the jsmin.c on which it is
|
||||
* based.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* jsmin.c 2003-04-21
|
||||
*
|
||||
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* The Software shall be used for Good, not Evil.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
// package org.inconspicuous.jsmin;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PushbackInputStream;
|
||||
|
||||
import com.google.javascript.jscomp.*;
|
||||
import com.google.javascript.jscomp.Compiler;
|
||||
|
||||
public class JSMin {
|
||||
|
||||
private JSMin() {}
|
||||
|
||||
public static String compressJs(String code, String sourcePath) {
|
||||
return isPresent("com.google.javascript.jscomp.Compiler") ? compressJsUsingClosureCompiler(code, sourcePath) : code;
|
||||
}
|
||||
|
||||
private static boolean isPresent(String className) {
|
||||
/**
|
||||
* Compress a JS-string
|
||||
*
|
||||
* @param code the js-code you want to compress
|
||||
* @return the compressed code
|
||||
*/
|
||||
public static String compressJs(String code) {
|
||||
InputStream inStream = new ByteArrayInputStream(code.getBytes());
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||
JSMin jsmin = new JSMin(inStream, outStream);
|
||||
try {
|
||||
Class.forName(className);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
jsmin.jsmin();
|
||||
return outStream.toString().trim();
|
||||
} catch (IOException | UnterminatedRegExpLiteralException | UnterminatedCommentException | UnterminatedStringLiteralException e) {
|
||||
e.printStackTrace();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static String compressJsUsingClosureCompiler(String code, String sourcePath) {
|
||||
com.google.javascript.jscomp.Compiler compiler = new Compiler();
|
||||
CompilerOptions options = new CompilerOptions();
|
||||
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
|
||||
SourceFile extern = SourceFile.fromCode("externs.js", "");
|
||||
SourceFile input = SourceFile.fromCode(sourcePath, code);
|
||||
compiler.compile(extern, input, options);
|
||||
return compiler.toSource();
|
||||
private static final int EOF = -1;
|
||||
|
||||
private PushbackInputStream in;
|
||||
private OutputStream out;
|
||||
|
||||
private int theA;
|
||||
private int theB;
|
||||
|
||||
private JSMin(InputStream in, OutputStream out) {
|
||||
this.in = new PushbackInputStream(in);
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
/**
|
||||
* isAlphanum -- return true if the character is a letter, digit,
|
||||
* underscore, dollar sign, or non-ASCII character.
|
||||
*/
|
||||
private static boolean isAlphanum(int c) {
|
||||
return ((c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
c == '_' ||
|
||||
c == '$' ||
|
||||
c == '\\' ||
|
||||
c > 126);
|
||||
}
|
||||
|
||||
/**
|
||||
* get -- return the next character from stdin. Watch out for lookahead. If
|
||||
* the character is a control character, translate it to a space or
|
||||
* linefeed.
|
||||
*/
|
||||
private int get() throws IOException {
|
||||
int c = in.read();
|
||||
|
||||
if (c >= ' ' || c == '\n' || c == EOF) {
|
||||
return c;
|
||||
}
|
||||
|
||||
if (c == '\r') {
|
||||
return '\n';
|
||||
}
|
||||
|
||||
return ' ';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next character without getting it.
|
||||
*/
|
||||
private int peek() throws IOException {
|
||||
int lookaheadChar = in.read();
|
||||
in.unread(lookaheadChar);
|
||||
return lookaheadChar;
|
||||
}
|
||||
|
||||
/**
|
||||
* next -- get the next character, excluding comments. peek() is used to see
|
||||
* if a '/' is followed by a '/' or '*'.
|
||||
*/
|
||||
private int next() throws IOException, UnterminatedCommentException {
|
||||
int c = get();
|
||||
if (c == '/') {
|
||||
switch (peek()) {
|
||||
case '/':
|
||||
for (; ; ) {
|
||||
c = get();
|
||||
if (c <= '\n') {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
case '*':
|
||||
get();
|
||||
for (; ; ) {
|
||||
switch (get()) {
|
||||
case '*':
|
||||
if (peek() == '/') {
|
||||
get();
|
||||
return ' ';
|
||||
}
|
||||
break;
|
||||
case EOF:
|
||||
throw new UnterminatedCommentException();
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return c;
|
||||
}
|
||||
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* action -- do something! What you do is determined by the argument: 1
|
||||
* Output A. Copy B to A. Get the next B. 2 Copy B to A. Get the next B.
|
||||
* (Delete A). 3 Get the next B. (Delete B). action treats a string as a
|
||||
* single character. Wow! action recognizes a regular expression if it is
|
||||
* preceded by ( or , or =.
|
||||
*/
|
||||
|
||||
private void action(int d) throws IOException, UnterminatedRegExpLiteralException,
|
||||
UnterminatedCommentException, UnterminatedStringLiteralException {
|
||||
switch (d) {
|
||||
case 1:
|
||||
out.write(theA);
|
||||
case 2:
|
||||
theA = theB;
|
||||
|
||||
if (theA == '\'' || theA == '"') {
|
||||
for (; ; ) {
|
||||
out.write(theA);
|
||||
theA = get();
|
||||
if (theA == theB) {
|
||||
break;
|
||||
}
|
||||
if (theA <= '\n') {
|
||||
throw new UnterminatedStringLiteralException();
|
||||
}
|
||||
if (theA == '\\') {
|
||||
out.write(theA);
|
||||
theA = get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 3:
|
||||
theB = next();
|
||||
if (theB == '/' && (theA == '(' || theA == ',' || theA == '=')) {
|
||||
out.write(theA);
|
||||
out.write(theB);
|
||||
for (; ; ) {
|
||||
theA = get();
|
||||
if (theA == '/') {
|
||||
break;
|
||||
} else if (theA == '\\') {
|
||||
out.write(theA);
|
||||
theA = get();
|
||||
} else if (theA <= '\n') {
|
||||
throw new UnterminatedRegExpLiteralException();
|
||||
}
|
||||
out.write(theA);
|
||||
}
|
||||
theB = next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* jsmin -- Copy the input to the output, deleting the characters which are
|
||||
* insignificant to JavaScript. Comments will be removed. Tabs will be
|
||||
* replaced with spaces. Carriage returns will be replaced with linefeeds.
|
||||
* Most spaces and linefeeds will be removed.
|
||||
*/
|
||||
public void jsmin() throws IOException, UnterminatedRegExpLiteralException, UnterminatedCommentException, UnterminatedStringLiteralException {
|
||||
theA = '\n';
|
||||
action(3);
|
||||
while (theA != EOF) {
|
||||
switch (theA) {
|
||||
case ' ':
|
||||
if (isAlphanum(theB)) {
|
||||
action(1);
|
||||
} else {
|
||||
action(2);
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
switch (theB) {
|
||||
case '{':
|
||||
case '[':
|
||||
case '(':
|
||||
case '+':
|
||||
case '-':
|
||||
action(1);
|
||||
break;
|
||||
case ' ':
|
||||
action(3);
|
||||
break;
|
||||
default:
|
||||
if (isAlphanum(theB)) {
|
||||
action(1);
|
||||
} else {
|
||||
action(2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
switch (theB) {
|
||||
case ' ':
|
||||
if (isAlphanum(theA)) {
|
||||
action(1);
|
||||
break;
|
||||
}
|
||||
action(3);
|
||||
break;
|
||||
case '\n':
|
||||
switch (theA) {
|
||||
case '}':
|
||||
case ']':
|
||||
case ')':
|
||||
case '+':
|
||||
case '-':
|
||||
case '"':
|
||||
case '\'':
|
||||
action(1);
|
||||
break;
|
||||
default:
|
||||
if (isAlphanum(theA)) {
|
||||
action(1);
|
||||
} else {
|
||||
action(3);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
action(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private class UnterminatedCommentException extends Exception {
|
||||
}
|
||||
|
||||
private class UnterminatedStringLiteralException extends Exception {
|
||||
}
|
||||
|
||||
private class UnterminatedRegExpLiteralException extends Exception {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -70,7 +70,7 @@ public class TagCreatorTest {
|
||||
assertEquals(text("<script> and \"</script>\"").render(), "<script> and "</script>"");
|
||||
assertEquals(unsafeHtml("<script>").render(), "<script>");
|
||||
assertEquals(styleWithInlineFile_min("/test.css").render(), "<style>body{background:#daa520;margin-bottom:10px;margin-left:10px;margin-right:10px;margin-top:10px}</style>");
|
||||
assertEquals(scriptWithInlineFile_min("/test.js").render(), "<script>(function(){console.log(15)})();</script>");
|
||||
assertEquals(scriptWithInlineFile_min("/test.js").render(), "<script>(function(){var test=5;var tast=10;var testTast=test+tast;console.log(testTast);})();</script>");
|
||||
assertEquals(fileAsString("/test.html").render(), "<body>" + EOL + " Any content" + EOL + "</body>" + EOL);
|
||||
assertEquals(fileAsEscapedString("/test.html").render(), "<body>" + EOL + " Any content" + EOL + "</body>" + EOL);
|
||||
assertEquals(fileAsString("/AnyContent.java").render(), "public class AnyContent{}" + EOL);
|
||||
|
||||
Reference in New Issue
Block a user