tags:

views:

163

answers:

1

I've got a library with some CSS, and a depending project that expands on the CSS. I'm trying to move both of them to ClientBundle.

Currently, library.css is

.smallWindow { min-width: 10px; outline: none; }

And depProject.css is

.sectionA .smallWindow { color: blue; }

I moved library.css and depProject.css into ClientBundles (LibraryBundle and DepProjectBundle) in their respective projects, but had to mark smallWindow as external in both. Is there a way to link smallWindow in depProject.css to smallWindow in library.css and still have smallWindow be obfuscated?

I'm hoping that instead of marking @external .smallWindow I could leave it alone in library.css, and put something like @replaceWithObfuscated smallWindow DepProjectBundle.css.smallWindow at the top of depProject.css

+3  A: 

The annotation is called @external for a reason :) It's for external classes, whose names we can't control - this fits your situation. It so happens that your external project is also a GWT one, but that doesn't matter - unless you compile both projects at the same time, I don't see any (easy and non-hackish) way of (automagically) linking the obfuscated class name from LibraryBundle to DepProjectBundle.

<tech (?) rant>
ClientBundle does all it's work at compile time - it knows everything about the CSS at that point and, for example, can create non-clashing class names for all the class names included in UiBinder templates (the <ui:style> elements are converted to CssResources at compile time). So, you can have a CSS class named .warning in your WidgetA'a UiBinder template and a different .warning class in WidgetB'a UiBinder template. During compilation, they will get assigned different obfuscated names (since they are different classes). Now, let's move to your situation: you compile LibraryBundle and then you compile (independently) DepProjectBundle - it is possible (the shorter the obfuscation prefix, the more likely, and you should override the default long GWT prefix BTW) that in both projects you'll get the same named CSS classes (which of course will most likely have different purposes). So if you were to include blindly a class from LibraryBundle into DepProjectBundle, you might (at some point) introduce a name clash.
</tech (?) rant>

If you really want to avoid @external (which is a good idea, I'll admit :)), I'd suggest the following: include your LibraryBundle into your project as a svn:externals (you are using a SCM, right?) or a symlink or any other means available in your SCM or filesystem - basically, you'll be seamlessly able to share the same code of LibraryBundle amongst many projects (which I presume is the goal here), while having compile-time support from the GWT compiler.


Update in response to Steve's comments:

It seems I have misunderstood your project structure from your initial question - I was under the impression that you wanted to link to the compiled output of the LibraryBundle... But since you are working with the source (via Maven, nice :)), then my svn:externals idea is not needed (BTW, you can put any repository path in the svn:externals property, including your trunk branch).
Anyway, I was under the impression that one could easily combine two CssResources into one but alas... This is what I've tried so far, all failed, but maybe something from this list will spark some brilliant idea in someone ;) (cue a long list here...) Scratch that, in the process of summing up my failures I came up with the answer :)

And the answer is:

The @Import annotation (in combination with @ImportedWithPrefix for a more meaningful prefix). Your ProjectBundle would look like this:

public interface DepProjectBundle extends ClientBundle {
    public static final DepProjectBundle INSTANCE = GWT.create(DepProjectBundle.class);
    // Need this to reference the class names
    // But maybe we can use the one in LibraryBundle?
    // Have fun testing that :) For simplicty, I'll leave it here
    Library library();

    // The good stuff
    @Import({Library.class})
    Main main();
}

Library is a CssResource from your LibraryBundle (or maybe it can be the whole LibraryBundle? Haven't tried that). The important stuff is the @Import annotation (normal CSS @import url(some.css) doesn't work with ClientBundle - I've tried ;))
Now, you use this DepProjectBundle as such:

DepProjectBundle.INSTANCE.library().ensureInjected();
DepProjectBundle.INSTANCE.main().ensureInjected();

Label label = new Label("Test");
label.addStyleName(DepProjectBundle.INSTANCE.library().smallWindow());
VerticalPanel vPanel = new VerticalPanel();
vPanel.addStyleName(DepProjectBundle.INSTANCE.main().sectionA());
vPanel.add(label);
RootPanel.get().add(vPanel);

Note that we are using the DepProjectBundle.library() CssResource to reference the names from the library.css "namespace".
One important change is needed however in main.css - in order to avoid name clashes, the compiler imports the class names with a prefix - it uses either the classes' name or the one provided via the @ImportedWithPrefix annotation. So, your main.css will have to be changed to something like this (let's assume you used library as a prefix):

.sectionA .library-smallWindow {
  color: blue;
}

Unfortunately, there's no way around this (that I've found :)). But it might be beneficial in the long run - in case you forgot which names you used in the library.css and which in main.css (which could lead to some strange CSS clashes) - this way you clearly define (in main.css) that this class name belongs to the library.css (it gets obfuscated in the end, so no additional bytes are wasted).

Phew, hope this solution will be helpful to you (and others! :)) I'll leave my initial thoughts too, since someone else might not have been using (from the start :)) the source of the external ClientBundle, the svn:externals idea might be of use to someone not using tools like maven, etc.

Igor Klimer
Regarding your rant, I don't "compile" LibraryBundle and then later compile DepProjectBundle. LibraryBundle's source is in the jar produced for that library, and GWT's compiler has access to both bundles when it does it's monolithic compile. So there's no danger of the overlap you mentioned, except for the case where I use @externals, because then it can't rename the CSS selectors.
Steve Armstrong
Your second point about svn:externals wouldn't work in my case, since the svn:externals would have to point to a tag of the library, not trunk, and there are multiple projects depending on this library, so every release of the library, the svn:externals property would have to be updated. We've already got Maven (with svn) handling dependencies. Also, since the source of the library ends up in the jar produced, GWT can already do what svn:externals would allow.
Steve Armstrong
Sorry for the spam of multiple comments, but you said "unless you compile both projects at the same time". GWT does compile both libraries together at the same time, so any solution that would work if we're talking about two client bundles in different java packages should work in my case where they're two projects. What was your idea?
Steve Armstrong
Answer updated - I hope this time it will work for you :)
Igor Klimer
Thanks for the update. I don't have a chance to try it right now, but it all seems to fit.
Steve Armstrong