001// Licensed under the Apache License, Version 2.0 (the "License"); 002// you may not use this file except in compliance with the License. 003// You may obtain a copy of the License at 004// 005// http://www.apache.org/licenses/LICENSE-2.0 006// 007// Unless required by applicable law or agreed to in writing, software 008// distributed under the License is distributed on an "AS IS" BASIS, 009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 010// See the License for the specific language governing permissions and 011// limitations under the License. 012 013package org.apache.tapestry5.webresources.modules; 014 015import com.github.sommeri.less4j.LessCompiler; 016import com.github.sommeri.less4j.core.parser.AntlrException; 017import com.google.javascript.jscomp.CompilationLevel; 018import org.apache.tapestry5.MarkupWriter; 019import org.apache.tapestry5.internal.webresources.*; 020import org.apache.tapestry5.ioc.MappedConfiguration; 021import org.apache.tapestry5.ioc.ServiceBinder; 022import org.apache.tapestry5.ioc.annotations.Autobuild; 023import org.apache.tapestry5.ioc.annotations.Contribute; 024import org.apache.tapestry5.ioc.annotations.Primary; 025import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 026import org.apache.tapestry5.ioc.internal.util.InternalUtils; 027import org.apache.tapestry5.ioc.services.FactoryDefaults; 028import org.apache.tapestry5.ioc.services.SymbolProvider; 029import org.apache.tapestry5.services.ObjectRenderer; 030import org.apache.tapestry5.services.assets.ResourceMinimizer; 031import org.apache.tapestry5.services.assets.ResourceTransformer; 032import org.apache.tapestry5.services.assets.StreamableResourceSource; 033import org.apache.tapestry5.webresources.WebResourcesSymbols; 034 035import java.util.List; 036 037/** 038 * Configures use of various transformers and mimimizers to support: 039 * <ul> 040 * <li>Less to CSS</li> 041 * <li>CoffeeScript to JavaScript</li> 042 * <li>CSS minimization via YUI Compressor</li> 043 * <li>JavaScript minimization via Google Closure</li> 044 * </ul> 045 * 046 * @since 5.4 047 */ 048public class WebResourcesModule 049{ 050 public static void bind(ServiceBinder binder) 051 { 052 binder.bind(ResourceTransformerFactory.class, ResourceTransformerFactoryImpl.class); 053 } 054 055 @Contribute(SymbolProvider.class) 056 @FactoryDefaults 057 public static void setupDefaultCacheDirectory(MappedConfiguration<String, Object> configuration) 058 { 059 configuration.add(WebResourcesSymbols.CACHE_DIR, "${java.io.tmpdir}/tapestry-asset-cache"); 060 configuration.add(WebResourcesSymbols.COMPILATION_LEVEL, CompilationLevel.WHITESPACE_ONLY); 061 } 062 063 064 @Contribute(StreamableResourceSource.class) 065 public static void provideCompilers(MappedConfiguration<String, ResourceTransformer> configuration, ResourceTransformerFactory factory, 066 @Autobuild CoffeeScriptCompiler coffeeScriptCompiler) 067 { 068 // contribution ids are file extensions: 069 070 configuration.add("coffee", 071 factory.createCompiler("text/javascript", "CoffeeScript", "JavaScript", 072 coffeeScriptCompiler, 073 CacheMode.SINGLE_FILE)); 074 075 configuration.add("less", 076 factory.createCompiler("text/css", "Less", "CSS", new LessResourceTransformer(), 077 CacheMode.MULTIPLE_FILE)); 078 } 079 080 @Contribute(ResourceMinimizer.class) 081 @Primary 082 public static void setupDefaultResourceMinimizers(MappedConfiguration<String, ResourceMinimizer> configuration) 083 { 084 configuration.addInstance("text/css", CSSMinimizer.class); 085 configuration.addInstance("text/javascript", GoogleClosureMinimizer.class); 086 } 087 088 /** 089 * Alas {@link AntlrException}s do not have a useful toString() which makes them useless in the exception report; 090 * here we provide an {@link ObjectRenderer} that breaks them apart into useful strings. Eventually we may be 091 * able to synthesize a {@link org.apache.tapestry5.ioc.Location} from them as well and show some of the source .less file. 092 */ 093 @Contribute(ObjectRenderer.class) 094 @Primary 095 public static void provideLessCompilerProblemRenderer(MappedConfiguration<Class, ObjectRenderer> configuration) 096 { 097 configuration.add(LessCompiler.Problem.class, new ObjectRenderer<LessCompiler.Problem>() 098 { 099 @Override 100 public void render(LessCompiler.Problem problem, MarkupWriter writer) 101 { 102 List<String> strings = CollectionFactory.newList(); 103 104 if (InternalUtils.isNonBlank(problem.getMessage())) 105 { 106 strings.add(problem.getMessage()); 107 } 108 109 // Inside WRO4J we see that the LessSource is a StringSource with no useful toString(), so 110 // it is omitted. We may need to create our own processors, stripping away a couple of layers of 111 // WRO4J to get proper exception reporting! 112 113 if (problem.getLine() > 0) 114 { 115 strings.add("line " + problem.getLine()); 116 } 117 118 if (problem.getCharacter() > 0) 119 { 120 strings.add("position " + problem.getCharacter()); 121 } 122 123 writer.write(InternalUtils.join(strings, " - ")); 124 } 125 }); 126 } 127}