LESS file @Import of a WebJar resource (another LESS file)

677 views
Skip to first unread message

alexej....@gmail.com

unread,
Aug 10, 2014, 5:02:57 PM8/10/14
to wr...@googlegroups.com
Hi,

I'm trying to get Wro4J and WebJars to play nicely and for the most parts, when a WebJar resource is defined in a model file it all works great.
However we are planning on using the source files for e.g Bootstrap 3.2 and override it with our variables and additional styles.
For this, I was planning to have a simple Less file (let's call it main.less) with a few @import statements for my variables.less and also bootstrap.less (which is in a webjar).

Variables.less get's imported just like expected and all works fine until I try to import something from a webjar.
I tried different combinations and from what I'm seeing is that neither LessCssImportProcessor not Less4jProcessor are aware of locators that are used by the WroManager. Is this correct?

What I would like to write is something like this:
@import "webjar:/bootstrap/3.2.0/less/bootstrap.less"
This should in my mind build up a LESS file that would then be evaluated into one big CSS that applies my variables and other imports and overrides Bootstrap defaults.

I believe this is how you would do that with bower (but point to bower_components folder, etc).
Is this possible? If not "out of the box", any good pointers on parts that need to be adopted?

Thanks,
//Alex

Alex Objelean

unread,
Aug 11, 2014, 3:51:02 AM8/11/14
to wr...@googlegroups.com
Hi,
the import statement is not handled properly with webjar locator. This is a known issue
Until this issue is fixed, you can replace the webjar uri with the classpath one.

Cheers,
Alex


--
You received this message because you are subscribed to the Google Groups "wro4j" group.
To unsubscribe from this group and stop receiving emails from it, send an email to wro4j+un...@googlegroups.com.
To post to this group, send email to wr...@googlegroups.com.
Visit this group at http://groups.google.com/group/wro4j.
For more options, visit https://groups.google.com/d/optout.

alexej....@gmail.com

unread,
Aug 11, 2014, 7:44:24 AM8/11/14
to wr...@googlegroups.com
Hi,

When trying to use classpath link this still causes problems.
By looking at the code it does not seem that any of the import handlers understand classpath (or any other types of URL except relative ones). I might be  abit wrong but this is what I tried for the classpath import:

@import "classpath:META-INF/resources/webjars/bootstrap/3.2.0/less/bootstrap.less";

What this seems to result in is: the following URL according to the debug:

/less/classpath:META-INF/resources/webjars/bootstrap/3.2.0/less/bootstrap.less

(my main.less file that contained the import statement is in /less/ folder).

Or did I misunderstand you about the classpath url?

Alex Objelean

unread,
Aug 11, 2014, 9:03:15 AM8/11/14
to wr...@googlegroups.com
The imported url's should be relative. The classpath uri should be used only in the model definition.

Let me know if that works.

Cheers,
Alex

alexej....@gmail.com

unread,
Aug 11, 2014, 9:09:16 AM8/11/14
to wr...@googlegroups.com
Hey again,

Then I'm not sure how to make the relative URL for this resource that is coming from the webjars package.
The ones available in the model always worked fine with both classpath and webjar path.

If I would like to enable the webjar path for the @import statement (as i tried to explain, this is what I need to work properly with my less file that "extends and overrides" bootstrap less), what would be the approach?
My goal is to have one main.less that imports bootstrap less, fontawesome less and some other things, then uses my variables to override it etc..

If there is no relatively easy way to do this (like minor modifications to some processor) I may have to ditch the webjar idea and just copy the files into my application so that it's possible to use relative URLs.

Suggestions are welcome :-)

//Alex

Alex Objelean

unread,
Aug 11, 2014, 9:38:28 AM8/11/14
to wr...@googlegroups.com
Ah.. sorry for misunderstanding.
As it is now, the @import statements can process only relative paths (actually it process also absolute paths, like http://www.path.to/my/site.css). 
So, you should either copy the webjar resource to a different location (so that the relative path would work as expected), or open an issue describing your use-case.
Another approach, could be creating a custom processor which would handle the non relative imports. Take a look on how the Less4jProcessor is implemented. There is an inner class called RelativeAwareLessSource which implements the computeRelative method. If you can tweak the implementation and make it work, I would gladly merge your contribution ;)...

Cheers,
Alex

alexej....@gmail.com

unread,
Aug 11, 2014, 1:57:57 PM8/11/14
to wr...@googlegroups.com
Thanks for the pointers.
This is what I was thinking is the "cause" and therefore wanted to double-check if I was missing something.
I think from the way I want it to work, I will need to see if tweaking the Less4jProcessor can be done in a feasible timeframe.

Just a bit of a side-note.. Do Processors have a "desired" mechanism to get different UrlLocators? As there are several of those registered, I would make sense to go through them, just like the WroModel parsers would do.
Is it possible to simply Inject them some way?

//Alex

alexej....@gmail.com

unread,
Aug 11, 2014, 2:47:40 PM8/11/14
to wr...@googlegroups.com, alexej....@gmail.com
A Quick followup on my previous message:

I naturally totally missed that there already was a UriLocatorFactory in there.
The only thing that was required to do was Checking if the "relativePath" can be resolved by the locators before doing any "relative" magic on it.

I have not yet tested everything but the change that computeRelative method that seems to work for my usecase is simply:

private LessSource computeRelative(final Resource resource, final String relativePath) throws StringSourceException {

    try {

        final UriLocator locator = locatorFactory.getInstance(relativePath);
        final String relativeResourceUri;
        final String relativeResourceContent;

        if (locator == null) {
            //This is not something we can do via locators alone, let's check for relative files
            
            relativeResourceUri = computeRelativeResourceUri(resource.getUri(), relativePath);
            relativeResourceContent = IOUtils.toString(locatorFactory.locate(relativeResourceUri), "UTF-8");
        } else {
            relativeResourceUri = relativePath;
            relativeResourceContent = IOUtils.toString(locator.locate(relativePath), "UTF-8");
        }

        final Resource relativeResource = Resource.create(relativeResourceUri, ResourceType.CSS);
        return new RelativeAwareLessSource(relativeResource, relativeResourceContent, locatorFactory);

    } catch (final IOException e) {
        LOG.error("Failed to compute relative resource: " + resource, e);
        throw new StringSourceException();
    }
}

It can naturally be simplified a bit more by just changing the relativeResourceUri but that would mean getting a new locator when we already have one for "non-relative" links.
While I'm still testing this piece, any concerns about using this approach?

//Alex

Greg Pendlebury

unread,
Aug 11, 2014, 5:16:32 PM8/11/14
to wr...@googlegroups.com

Have you considered not using an import statement? We integrated bootstrap by ignoring the top level less file from the web jar and listing all of the component less files in our wro.xml instead. We have our own custom stuff interleaved between them all and it works just fine without imports.

Remember that all the less files in your your wro.xml are concatenated together before it goes through the less compiler.

Ta,
Greg

==========
"Sent from Ye Olde mobile device. Forgive my fat fingers and their typos."

p: +61 2 6262 1228, m: 0403 674 810, e: greg.pe...@gmail.com

alexej....@gmail.com

unread,
Aug 11, 2014, 5:43:44 PM8/11/14
to wr...@googlegroups.com
Hi Greg,

Thanks for the suggestion. I do believe, however this will remove the purpose of having less files reconfigured before processed, will it not? 
From my understanding the way it would do then is create a CSS from each file and concat them into one. This does not allow you overriding the LESS variables and using bootstrap mixins properly. Or am I missing something? Would appreciate if you can share more details on your setup including processors used.

What I am after is having my variables fed into bootstrap to override the themes, also change the glyphicons to font-awesome and some other customizations and then add own styles based on mixins from bootstrap.
The way I look at it, the proper way of doing it is via LESS imports but maybe your way works for that too?

Also listing files separately will partially break another thing that Im looking to achieve (actually already got it running) - In dev mode, our application will not concat files ina group but present each file separately to make it easier for developers to debug things (this mostly goes for JS however, where we use a wildcard to match the JS files).

Like I mentioned earlier it would seem that now my @import "webjar:bootstrap/3.2.0/less/bootstrap.less"; statement works, but will do more testing tomorrow.

Thanks,
//Alex

Greg Pendlebury

unread,
Aug 11, 2014, 6:47:20 PM8/11/14
to wr...@googlegroups.com
I can't speak to your other points, but regarding processors we are using this:
preProcessors=cssUrlRewriting,lessCssImport,semicolonAppender
postProcessors=less4j,cssClasspathUrlAuthorization,jsMin,cssMinJawr

And because all the concatentation occurs before the post processor less compiler Bootstrap mixins and variables work just fine:

  <group name="bootstrap-less">
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/variables.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/mixins.less</css>

    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/normalize.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/print.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/glyphicons.less</css>

    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/scaffolding.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/type.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/code.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/grid.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/tables.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/forms.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/buttons.less</css>

    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/component-animations.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/dropdowns.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/button-groups.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/input-groups.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/navs.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/navbar.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/breadcrumbs.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/pagination.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/pager.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/labels.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/badges.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/jumbotron.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/thumbnails.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/alerts.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/progress-bars.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/media.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/list-group.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/panels.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/responsive-embed.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/wells.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/close.less</css>

    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/modals.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/tooltip.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/popovers.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/carousel.less</css>

    <css>/assets/less/newspapers.less</css>

    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/utilities.less</css>
    <css>classpath:/META-INF/resources/webjars/bootstrap/3.2.0/less/responsive-utilities.less</css>
  </group>

Ta,
Greg

Alex Objelean

unread,
Aug 12, 2014, 3:09:06 AM8/12/14
to wr...@googlegroups.com
Processor do have a mechanism to get uriLocatorFactory (not uri locator). If you need a way to iterate through uriLocators, create a custom uriLocatorFactory which provides a getter for all available uriLocators...
The Less4jProcessor is a good example proving this (see line #83).

One thought regarding your requirement: 
though it will work (if the changes are applied to the computeRelative method in Less4jProcessor), it would be a non standard solution, meaning that it would work only with your custom wro4j setup. Making your solution highly coupled with wro4j. Ideally, wro4j should be non invasive (at least this is the approach I would recommend). You could consider the solution proposed by Greg. The only disadvantage is that it doesn't use the Less4j import statement processor mechanism (which is better than the one implemented by cssImportPreProcessor).

Cheers,
Alex

Alexej Kubarev

unread,
Aug 12, 2014, 3:20:49 AM8/12/14
to wr...@googlegroups.com
Hi Alex,

Are you sure? This works for me for all registered locators. I was able to get it working with both classpath, wro4j and relative with only those lines of code changed.
The injected locatorFactory in Less4jProcessor is decorated but from my understanding is using the locators configured for a manager. In my case it's default implementation that scans for all implementations and adds them to an internal list.

So in reality I'm using a very standard setup with Less4jProcessor modified only in that method as shown in my previous message.

//Alex
Message has been deleted

Alex Objelean

unread,
Aug 12, 2014, 3:31:11 AM8/12/14
to wr...@googlegroups.com, alexej....@gmail.com
There is no concern regarding this approach. I would suggest running all the unit tests to validate that other test-cases doesn't fail (though I'm pretty sure not all of them are covered). 
Regarding the "non-standard" approach, I mean that it won't be supported if the resources are processed with anything else beside wro4j processors. Also, there is a problem if you will try to include each individual resource without merging them (you might find useful the modelAsJsonRequestHandler for this use case).

Cheers,
Alex
Reply all
Reply to author
Forward
0 new messages