Skip to content
This repository has been archived by the owner on Jan 9, 2019. It is now read-only.

@import doesn't work when compiler ran on string #28

Open
jamcole opened this issue May 9, 2013 · 1 comment
Open

@import doesn't work when compiler ran on string #28

jamcole opened this issue May 9, 2013 · 1 comment

Comments

@jamcole
Copy link

jamcole commented May 9, 2013

I created a simple LessCss filter for webutilities: http://code.google.com/p/webutilities/issues/detail?id=46 but unfortunately, when using LessCompiler.compile(String data) @imports don't work.

When a relative path is specified, I see this error:
org.lesscss.LessException: Couldn't load undefinedvariables.less (0)
That's understandable, obviously the import doesn't know where the path is relative from

However changing '@ import' to the full path still doesn't work:
org.lesscss.LessException: Couldn't load file:///full/path/to/file/css/variables.less (undefined)

I think it comes from envjs returning a different status for file:/// paths than browsers, the xhs.status is undefined even though the file was loaded properly (was able to hack less.js to make it ignore the return status and it worked).

If this bug can be fixed, a great enhancement would be a new function in LessCompiler:
LessCompiler.compile(String data, URL basePath); so imports would more easily work when processing data in a servlet context like the filter does.

@jamcole
Copy link
Author

jamcole commented May 10, 2013

So I poked around at the problem a bit more, and after getting frustrated trying to get envjs working 100% in this scenario, I decided to just process the imports directly in the java code instead of trying to handle the imports with xhr() in the javascript code :)
Perhaps this could be inspiration for inclusion in the LessCompiler class or for others who encounter this scenario.

class LessCompilerMod extends LessCompiler {
    Pattern importPattern = Pattern.compile("['\"]([^'\"]+)['\"]");
    boolean onlyImportOnce = true; // does @import always act like @import-once (like in Less 1.4+)

    public String compile(String input, URI baseUri) throws LessException {
        StringBuilder merged = new StringBuilder();
        BufferedReader reader = new BufferedReader(new StringReader(input));
        String line;

        try {
            List<String> imported = new ArrayList<>();
            while ((line = reader.readLine()) != null) {
                merged.append(readLineAndProcessImports(line, baseUri, 0, imported));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return super.compile(merged.toString());
    }

    StringBuilder readLineAndProcessImports(String line, URI baseUri, int level, List<String> imported) throws LessException {
        if (level >= 10) {
            throw new LessException("Max @import levels exceeded(" + level + "), possible recursion!",
                    new AssertionError("recursion depth exceeded"));
        }
        StringBuilder merged = new StringBuilder();
        LOGGER.trace(line);
        if (line.contains("@import")) {
            LOGGER.debug("Found an import: {}", line);
            Matcher match = importPattern.matcher(line);
            if (match.find()) {
                String path = match.group(1);
                if (!path.endsWith(".less") && !path.endsWith(".css")) {
                    path += ".less";
                }
                URI fullPath;
                fullPath = baseUri.resolve(path);
                LOGGER.debug("@import fullPath is '{}'", fullPath);
                if ((onlyImportOnce || line.contains("@import-once")) && imported.contains(fullPath.toString())) {
                    LOGGER.info("Already included: {}", line);
                } else {
                    if (!imported.contains(fullPath.toString())) {
                        imported.add(fullPath.toString());
                    }
                    try (InputStream stream = fullPath.toURL().openStream()) {
                        InputStreamReader importReader = new InputStreamReader(stream);
                        BufferedReader importBuffered = new BufferedReader(importReader);

                        String importedLine;
                        while ((importedLine = importBuffered.readLine()) != null) {
                            LOGGER.trace(importedLine);
                            if (importedLine.contains("@import")) {
                                LOGGER.debug("Found a sub import: {}", importedLine);
                                merged.append(readLineAndProcessImports(importedLine, baseUri, level + 1, imported));
                            } else {
                                merged.append(importedLine);
                            }
                        }
                    } catch (IOException e) {
                        throw new LessException(e);
                    }
                }
            } else {
                LOGGER.warn("Unable to find a resource path in line: {}", line);
            }
        } else {
            merged.append(line);
        }
        return merged;
    }
}

I call it like this:

LessCompilerMod compiler = new LessCompilerMod();
compiler.setEncoding(charset);
compiler.setCompress(false);
String css = compiler.compile(new String(wrapper.getBytes(), this.charset), new URI("file:///home/user/Projects/myproject/src/main/webapp/css/"));

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant