Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix relative image path bug and Less support #22

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
89 changes: 87 additions & 2 deletions app/press/Compressor.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package press;

import java.io.StringReader;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
Expand All @@ -17,6 +18,7 @@
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import play.Play;

import play.PlayPlugin;
import play.cache.Cache;
Expand Down Expand Up @@ -99,7 +101,14 @@ public String compressedSingleFileUrl(FileCompressor compressor, String fileName

int lastDot = fileName.lastIndexOf('.');
String compressedFileName = fileName.substring(0, lastDot) + ".min";
compressedFileName += fileName.substring(lastDot);
/*if (fileName.endsWith(".less")){

compressedFileName += ".less.css";
}
else {
*/
compressedFileName += fileName.substring(lastDot);
//}

// The process for compressing a single file is the same as for a group
// of files, the list just has a single entry
Expand Down Expand Up @@ -397,8 +406,24 @@ private static void compress(FileCompressor compressor, FileInfo fileInfo, Write
throws Exception {

String fileName = fileInfo.file.getName();
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(
BufferedReader in;

if (fileName.endsWith(".less")) {

LessCompiler lessCompiler = new LessCompiler(Play.mode == Play.Mode.DEV);
String css = lessCompiler.compile(fileInfo.file);
in = new BufferedReader(new StringReader(css));
}
else {

in = new BufferedReader(new InputStreamReader(new FileInputStream(
fileInfo.file), "UTF-8"));
}

if (fileName.endsWith(".css") || fileName.endsWith(".less")) {

in = handleRelativeimages(in, fileInfo.file);
}

// If the file should be compressed
if (fileInfo.compress) {
Expand All @@ -413,6 +438,66 @@ private static void compress(FileCompressor compressor, FileInfo fileInfo, Write
}
}

private static BufferedReader handleRelativeimages(BufferedReader in, File file) throws Exception {

String filePath = file.getAbsolutePath();
filePath = filePath.substring(filePath.lastIndexOf(PluginConfig.addTrailingSlash(PluginConfig.css.srcDir)));

StringBuilder sb = new StringBuilder();
int c;
while ((c = in.read()) != -1) {
sb.append((char) c);
}

String css = sb.toString();


String[] patterns = {"(url\\s*\\(\\s*[\"']?)(.*?)([\"']?\\s*\\))"
, "(src\\s*=\\s*[\"']?)(.*?)([\"']?\\s*[,\\)])"};

for (int n=0;n<patterns.length;n++){

StringBuffer resultString = new StringBuffer();

Pattern p = Pattern.compile(patterns[n]);
Matcher m = p.matcher(css);
while (m.find()) {

String url = m.group(2);

if (!url.startsWith("/")
&& !url.startsWith("http://")
&& !url.startsWith("https://")) {

String newurl = repath(filePath, url);

m.appendReplacement(resultString, m.group(1) + newurl + m.group(3));
}
}
m.appendTail(resultString);
css = resultString.toString();
}

return new BufferedReader(new StringReader(css));
}

private static String repath(String cssFileUrl, String url) {

String[] parts = url.split("\\.\\./");
int len = parts.length;


String finalurl = "";
String[] urlparts = cssFileUrl.split("/");
for (int n = 0; n < urlparts.length - len; n++) {

finalurl += urlparts[n] + "/";
}

finalurl += parts[len - 1];

return finalurl;
}
/**
* Get the content of the response sent to the client as a String
*/
Expand Down
161 changes: 161 additions & 0 deletions app/press/LessCompiler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package press;

import java.io.BufferedReader;
import java.io.StringReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.mozilla.javascript.WrappedException;

import com.asual.lesscss.LessEngine;
import com.asual.lesscss.LessException;

import play.Logger;
import play.cache.Cache;

/**
* @author benoit
*/
public class LessCompiler {

LessEngine lessEngine;
Boolean devMode;
Pattern importPattern = Pattern.compile(".*@import\\s*\"(.*?)\".*");

LessCompiler(Boolean devMode) {
lessEngine = new LessEngine();
this.devMode = devMode;
}

/**
* Get the CSS for this less file either from the cache, or compile it.
*/
public String get(File lessFile) {
String cacheKey = "less_" + lessFile.getPath() + lastModifiedRecursive(lessFile);
String css = Cache.get(cacheKey, String.class);
if(css == null) {
css = compile(lessFile);
css = css.replaceAll("\\\\n", "");
Cache.set(cacheKey, css);
}
return css;
}

// TODO: Maybe prevent infinite looping here, in case of an import loop?
public long lastModifiedRecursive(File lessFile) {
long lastModified = lessFile.lastModified();
for(File imported : getImportsFromCacheOrFile(lessFile)) {
lastModified = Math.max(lastModified, imported.lastModified());
}
return lastModified;
}

protected Set<File> getImportsFromCacheOrFile(File lessFile) {
String cacheKey = "less_imports_" + lessFile.getPath() + lessFile.lastModified();

@SuppressWarnings("unchecked")
Set<File> files = Cache.get(cacheKey, Set.class);

if(files == null) {
try {
files = getImportsFromFile(lessFile);
Cache.set(cacheKey, files);
} catch(IOException e) {
Logger.error(e, "IOException trying to determine imports in LESS file");
files = new HashSet<File>();
}
}
return files;
}

protected Set<File> getImportsFromFile(File lessFile) throws IOException {
BufferedReader r = new BufferedReader(new FileReader(lessFile));

Set<File> files = new HashSet<File>();
String line;
while ((line = r.readLine()) != null) {
Matcher m = importPattern.matcher(line);
while (m.find()) {
File file = new File(lessFile.getParentFile(), m.group(1));
files.add(file);
files.addAll(getImportsFromCacheOrFile(file));
}
}
return files;
}

protected String compile(File lessFile) {
try {
String css = lessEngine.compile(lessFile);
css = css.replaceAll("\\\\n", "");
return css;
} catch (LessException e) {
return handleException(lessFile, e);
}
}

public String compile(String lessContent) {
try {
String css = lessEngine.compile(lessContent);
css = css.replaceAll("\\\\n", "");
return css;
} catch (LessException e) {
return handleException(null, e);
}
}

public BufferedReader compile(BufferedReader in) throws IOException {

StringBuilder sb = new StringBuilder();
String line;
while ((line = in.readLine()) != null) {
sb.append(line);
}

String css = compile(sb.toString());
css = css.replaceAll("\\\\n", "");


return new BufferedReader(new StringReader(css));
}

public String handleException(File lessFile, LessException e) {
Logger.warn(e, "Less exception");

String filename = e.getFilename();
List<String> extractList = e.getExtract();
String extract = null;
if(extractList != null) {
extract = extractList.toString();
}

// LessEngine reports the file as null when it's not an @imported file
if(filename == null) {
filename = lessFile.getName();
}

// Try to detect missing imports (flaky)
if(extract == null && e.getCause() instanceof WrappedException) {
WrappedException we = (WrappedException) e.getCause();
if(we.getCause() instanceof FileNotFoundException) {
FileNotFoundException fnfe = (FileNotFoundException) we.getCause();
extract = fnfe.getMessage();
}
}

return formatMessage(filename, e.getLine(), e.getColumn(), extract, e.getErrorType());
}

public String formatMessage(String filename, int line, int column, String extract, String errorType) {
return "body:before {display: block; color: #c00; white-space: pre; font-family: monospace; background: #FDD9E1; border-top: 1px solid pink; border-bottom: 1px solid pink; padding: 10px; content: \"[LESS ERROR] " +
String.format("%s:%s: %s (%s)", filename, line, extract, errorType) +
"\"; }";
}
}
Loading