// Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package org.apache.tapestry5.webresources.modules; import com.github.sommeri.less4j.LessCompiler; import com.github.sommeri.less4j.core.parser.AntlrException; import com.google.javascript.jscomp.CompilationLevel; import org.apache.tapestry5.MarkupWriter; import org.apache.tapestry5.internal.webresources.*; import org.apache.tapestry5.ioc.MappedConfiguration; import org.apache.tapestry5.ioc.ServiceBinder; import org.apache.tapestry5.ioc.annotations.Autobuild; import org.apache.tapestry5.ioc.annotations.Contribute; import org.apache.tapestry5.ioc.annotations.Primary; import org.apache.tapestry5.ioc.internal.util.CollectionFactory; import org.apache.tapestry5.ioc.internal.util.InternalUtils; import org.apache.tapestry5.ioc.services.FactoryDefaults; import org.apache.tapestry5.ioc.services.SymbolProvider; import org.apache.tapestry5.services.ObjectRenderer; import org.apache.tapestry5.services.assets.ResourceMinimizer; import org.apache.tapestry5.services.assets.ResourceTransformer; import org.apache.tapestry5.services.assets.StreamableResourceSource; import org.apache.tapestry5.webresources.WebResourcesSymbols; import java.util.List; /** * Configures use of various transformers and mimimizers to support: * <ul> * <li>Less to CSS</li> * <li>CoffeeScript to JavaScript</li> * <li>CSS minimization via YUI Compressor</li> * <li>JavaScript minimization via Google Closure</li> * </ul> * * @since 5.4 */ public class WebResourcesModule { public static void bind(ServiceBinder binder) { binder.bind(ResourceTransformerFactory.class, ResourceTransformerFactoryImpl.class); } @Contribute(SymbolProvider.class) @FactoryDefaults public static void setupDefaultCacheDirectory(MappedConfiguration<String, Object> configuration) { configuration.add(WebResourcesSymbols.CACHE_DIR, "${java.io.tmpdir}/tapestry-asset-cache"); configuration.add(WebResourcesSymbols.COMPILATION_LEVEL, CompilationLevel.WHITESPACE_ONLY); } @Contribute(StreamableResourceSource.class) public static void provideCompilers(MappedConfiguration<String, ResourceTransformer> configuration, ResourceTransformerFactory factory, @Autobuild CoffeeScriptCompiler coffeeScriptCompiler) { // contribution ids are file extensions: configuration.add("coffee", factory.createCompiler("text/javascript", "CoffeeScript", "JavaScript", coffeeScriptCompiler, CacheMode.SINGLE_FILE)); configuration.add("less", factory.createCompiler("text/css", "Less", "CSS", new LessResourceTransformer(), CacheMode.MULTIPLE_FILE)); } @Contribute(ResourceMinimizer.class) @Primary public static void setupDefaultResourceMinimizers(MappedConfiguration<String, ResourceMinimizer> configuration) { configuration.addInstance("text/css", CSSMinimizer.class); configuration.addInstance("text/javascript", GoogleClosureMinimizer.class); } /** * Alas {@link AntlrException}s do not have a useful toString() which makes them useless in the exception report; * here we provide an {@link ObjectRenderer} that breaks them apart into useful strings. Eventually we may be * able to synthesize a {@link org.apache.tapestry5.ioc.Location} from them as well and show some of the source .less file. */ @Contribute(ObjectRenderer.class) @Primary public static void provideLessCompilerProblemRenderer(MappedConfiguration<Class, ObjectRenderer> configuration) { configuration.add(LessCompiler.Problem.class, new ObjectRenderer<LessCompiler.Problem>() { @Override public void render(LessCompiler.Problem problem, MarkupWriter writer) { List<String> strings = CollectionFactory.newList(); if (InternalUtils.isNonBlank(problem.getMessage())) { strings.add(problem.getMessage()); } // Inside WRO4J we see that the LessSource is a StringSource with no useful toString(), so // it is omitted. We may need to create our own processors, stripping away a couple of layers of // WRO4J to get proper exception reporting! if (problem.getLine() > 0) { strings.add("line " + problem.getLine()); } if (problem.getCharacter() > 0) { strings.add("position " + problem.getCharacter()); } writer.write(InternalUtils.join(strings, " - ")); } }); } }