Include Bootstrap 3 source to test Less compilation Replace WRO4J CoffeeScript compiler with own implementation
Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/70889263 Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/70889263 Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/70889263 Branch: refs/heads/master Commit: 70889263a459919f7820b3880f92b647f843d893 Parents: 2cbe72e Author: Howard M. Lewis Ship <[email protected]> Authored: Sat Aug 24 15:01:26 2013 -0700 Committer: Howard M. Lewis Ship <[email protected]> Committed: Sat Aug 24 15:05:13 2013 -0700 ---------------------------------------------------------------------- tapestry-wro4j/build.gradle | 4 + .../internal/wro4j/CoffeeScriptCompiler.java | 82 + .../internal/wro4j/LessResourceTransformer.java | 1 - .../wro4j/NodeOrRhinoCoffeeScriptCompiler.java | 63 - .../tapestry5/internal/wro4j/RhinoExecutor.java | 24 + .../internal/wro4j/RhinoExecutorPool.java | 147 ++ .../tapestry5/wro4j/modules/WRO4JModule.java | 9 +- .../webresources/internal/coffeescript-1.6.3.js | 12 + .../internal/invoke-coffeescript.js | 14 + .../groovy/t5/wro4j/components/Layout.groovy | 2 +- .../test/groovy/t5/wro4j/tests/Wro4jSpec.groovy | 9 +- .../test/java/t5/wro4j/services/AppModule.java | 14 +- .../src/test/resources/GebConfig.groovy | 2 +- .../resources/t5/wro4j/components/Layout.tml | 26 +- .../test/webapp/bootstrap/css/accordion.less | 34 - .../src/test/webapp/bootstrap/css/alerts.less | 79 - .../test/webapp/bootstrap/css/bootstrap.less | 63 - .../test/webapp/bootstrap/css/breadcrumbs.less | 24 - .../webapp/bootstrap/css/button-groups.less | 229 -- .../src/test/webapp/bootstrap/css/buttons.less | 228 -- .../src/test/webapp/bootstrap/css/carousel.less | 158 -- .../src/test/webapp/bootstrap/css/close.less | 32 - .../src/test/webapp/bootstrap/css/code.less | 61 - .../bootstrap/css/component-animations.less | 22 - .../test/webapp/bootstrap/css/dropdowns.less | 248 -- .../src/test/webapp/bootstrap/css/forms.less | 690 ------ .../src/test/webapp/bootstrap/css/grid.less | 21 - .../test/webapp/bootstrap/css/hero-unit.less | 25 - .../webapp/bootstrap/css/labels-badges.less | 84 - .../src/test/webapp/bootstrap/css/layouts.less | 16 - .../src/test/webapp/bootstrap/css/media.less | 55 - .../src/test/webapp/bootstrap/css/mixins.less | 702 ------ .../src/test/webapp/bootstrap/css/modals.less | 95 - .../src/test/webapp/bootstrap/css/navbar.less | 497 ---- .../src/test/webapp/bootstrap/css/navs.less | 409 ---- .../src/test/webapp/bootstrap/css/pager.less | 43 - .../test/webapp/bootstrap/css/pagination.less | 123 - .../src/test/webapp/bootstrap/css/popovers.less | 133 - .../webapp/bootstrap/css/progress-bars.less | 122 - .../src/test/webapp/bootstrap/css/reset.less | 216 -- .../bootstrap/css/responsive-1200px-min.less | 28 - .../bootstrap/css/responsive-767px-max.less | 193 -- .../bootstrap/css/responsive-768px-979px.less | 19 - .../webapp/bootstrap/css/responsive-navbar.less | 189 -- .../bootstrap/css/responsive-utilities.less | 59 - .../test/webapp/bootstrap/css/responsive.less | 48 - .../test/webapp/bootstrap/css/scaffolding.less | 53 - .../src/test/webapp/bootstrap/css/sprites.less | 197 -- .../src/test/webapp/bootstrap/css/tables.less | 244 -- .../test/webapp/bootstrap/css/thumbnails.less | 53 - .../src/test/webapp/bootstrap/css/tooltip.less | 70 - .../src/test/webapp/bootstrap/css/type.less | 247 -- .../test/webapp/bootstrap/css/utilities.less | 30 - .../test/webapp/bootstrap/css/variables.less | 301 --- .../src/test/webapp/bootstrap/css/wells.less | 29 - .../fonts/glyphicons-halflings-regular.eot | Bin 0 -> 14079 bytes .../fonts/glyphicons-halflings-regular.svg | 228 ++ .../fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 29512 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 0 -> 16448 bytes .../img/glyphicons-halflings-white.png | Bin 8777 -> 0 bytes .../bootstrap/img/glyphicons-halflings.png | Bin 12799 -> 0 bytes .../src/test/webapp/bootstrap/js/affix.js | 126 + .../src/test/webapp/bootstrap/js/alert.js | 98 + .../src/test/webapp/bootstrap/js/bootstrap.js | 2280 ------------------ .../src/test/webapp/bootstrap/js/button.js | 109 + .../src/test/webapp/bootstrap/js/carousel.js | 217 ++ .../src/test/webapp/bootstrap/js/collapse.js | 179 ++ .../src/test/webapp/bootstrap/js/dropdown.js | 154 ++ .../src/test/webapp/bootstrap/js/modal.js | 246 ++ .../src/test/webapp/bootstrap/js/popover.js | 117 + .../src/test/webapp/bootstrap/js/scrollspy.js | 158 ++ .../src/test/webapp/bootstrap/js/tab.js | 135 ++ .../src/test/webapp/bootstrap/js/tooltip.js | 386 +++ .../src/test/webapp/bootstrap/js/transition.js | 56 + .../src/test/webapp/bootstrap/less/alerts.less | 67 + .../src/test/webapp/bootstrap/less/badges.less | 51 + .../test/webapp/bootstrap/less/bootstrap.less | 59 + .../test/webapp/bootstrap/less/breadcrumbs.less | 23 + .../webapp/bootstrap/less/button-groups.less | 248 ++ .../src/test/webapp/bootstrap/less/buttons.less | 160 ++ .../test/webapp/bootstrap/less/carousel.less | 209 ++ .../src/test/webapp/bootstrap/less/close.less | 33 + .../src/test/webapp/bootstrap/less/code.less | 56 + .../bootstrap/less/component-animations.less | 29 + .../test/webapp/bootstrap/less/dropdowns.less | 193 ++ .../src/test/webapp/bootstrap/less/forms.less | 353 +++ .../test/webapp/bootstrap/less/glyphicons.less | 232 ++ .../src/test/webapp/bootstrap/less/grid.less | 346 +++ .../webapp/bootstrap/less/input-groups.less | 127 + .../test/webapp/bootstrap/less/jumbotron.less | 40 + .../src/test/webapp/bootstrap/less/labels.less | 58 + .../test/webapp/bootstrap/less/list-group.less | 88 + .../src/test/webapp/bootstrap/less/media.less | 56 + .../src/test/webapp/bootstrap/less/mixins.less | 723 ++++++ .../src/test/webapp/bootstrap/less/modals.less | 141 ++ .../src/test/webapp/bootstrap/less/navbar.less | 621 +++++ .../src/test/webapp/bootstrap/less/navs.less | 229 ++ .../test/webapp/bootstrap/less/normalize.less | 396 +++ .../src/test/webapp/bootstrap/less/pager.less | 55 + .../test/webapp/bootstrap/less/pagination.less | 83 + .../src/test/webapp/bootstrap/less/panels.less | 148 ++ .../test/webapp/bootstrap/less/popovers.less | 133 + .../src/test/webapp/bootstrap/less/print.less | 100 + .../webapp/bootstrap/less/progress-bars.less | 95 + .../bootstrap/less/responsive-utilities.less | 220 ++ .../test/webapp/bootstrap/less/scaffolding.less | 130 + .../src/test/webapp/bootstrap/less/tables.less | 236 ++ .../src/test/webapp/bootstrap/less/theme.less | 232 ++ .../test/webapp/bootstrap/less/thumbnails.less | 31 + .../src/test/webapp/bootstrap/less/tooltip.less | 95 + .../src/test/webapp/bootstrap/less/type.less | 238 ++ .../test/webapp/bootstrap/less/utilities.less | 42 + .../test/webapp/bootstrap/less/variables.less | 620 +++++ .../src/test/webapp/bootstrap/less/wells.less | 29 + 114 files changed, 9558 insertions(+), 8534 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/70889263/tapestry-wro4j/build.gradle ---------------------------------------------------------------------- diff --git a/tapestry-wro4j/build.gradle b/tapestry-wro4j/build.gradle index cd00fbc..d4432e1 100644 --- a/tapestry-wro4j/build.gradle +++ b/tapestry-wro4j/build.gradle @@ -2,6 +2,10 @@ description = "Integration with WRO4J to perform runtime CoffeeScript compilatio dependencies { compile project(":tapestry-core") + compile "com.github.sommeri:less4j:1.1.1" + compile "com.google.javascript:closure-compiler:v20130722" + compile "org.mozilla:rhino:1.7R4" + compile "ro.isdc.wro4j:wro4j-extensions:${versions.wro4j}", { exclude group: "org.codehaus.gmaven.runtime", module: "gmaven-runtime-1.7" exclude group: "com.github.lltyk", module: "dojo-shrinksafe" http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/70889263/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/CoffeeScriptCompiler.java ---------------------------------------------------------------------- diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/CoffeeScriptCompiler.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/CoffeeScriptCompiler.java new file mode 100644 index 0000000..452f2b3 --- /dev/null +++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/CoffeeScriptCompiler.java @@ -0,0 +1,82 @@ +package org.apache.tapestry5.internal.wro4j; + +import org.apache.commons.io.IOUtils; +import org.apache.tapestry5.annotations.Path; +import org.apache.tapestry5.ioc.OperationTracker; +import org.apache.tapestry5.ioc.Resource; +import org.apache.tapestry5.ioc.internal.util.CollectionFactory; +import org.apache.tapestry5.services.assets.ResourceDependencies; +import org.apache.tapestry5.services.assets.ResourceTransformer; +import org.mozilla.javascript.NativeObject; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.List; + +public class CoffeeScriptCompiler implements ResourceTransformer +{ + private final static Charset UTF8 = Charset.forName("utf-8"); + + private final RhinoExecutorPool executorPool; + + public String getTransformedContentType() + { + return "text/javascript"; + } + + public CoffeeScriptCompiler(@Path("classpath:org/apache/tapestry5/webresources/internal/coffeescript-1.6.3.js") + Resource mainCompiler, + @Path("classpath:org/apache/tapestry5/webresources/internal/invoke-coffeescript.js") + Resource shim, + OperationTracker tracker) + { + + executorPool = new RhinoExecutorPool(tracker, toList(mainCompiler, shim)); + } + + private List<Resource> toList(Resource... resources) + { + List<Resource> list = CollectionFactory.newList(); + + for (Resource r : resources) + { + list.add(r); + } + + return list; + } + + + private String getString(NativeObject object, String key) + { + return object.get(key).toString(); + } + + + public InputStream transform(Resource source, ResourceDependencies dependencies) throws IOException + { + String content = IOUtils.toString(source.openStream(), UTF8); + + RhinoExecutor executor = executorPool.get(); + + try + { + + NativeObject result = (NativeObject) executor.invokeFunction("compileCoffeeScriptSource", content, source.toString()); + + if (result.containsKey("exception")) + { + throw new RuntimeException(getString(result, "exception")); + } + + return IOUtils.toInputStream(getString(result, "output"), UTF8); + + } finally + { + executor.discard(); + } + + + } +} http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/70889263/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java ---------------------------------------------------------------------- diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java index 1fd40d9..5b4d7a7 100644 --- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java +++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/LessResourceTransformer.java @@ -92,7 +92,6 @@ public class LessResourceTransformer implements ResourceTransformer public InputStream transform(Resource source, ResourceDependencies dependencies) throws IOException { - BytestreamCache compiled = invokeLessCompiler(source, dependencies); return compiled.openStream(); http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/70889263/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/NodeOrRhinoCoffeeScriptCompiler.java ---------------------------------------------------------------------- diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/NodeOrRhinoCoffeeScriptCompiler.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/NodeOrRhinoCoffeeScriptCompiler.java deleted file mode 100644 index 03e175d..0000000 --- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/NodeOrRhinoCoffeeScriptCompiler.java +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2013 The Apache Software Foundation -// -// 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.internal.wro4j; - -import org.slf4j.Logger; -import ro.isdc.wro.extensions.processor.js.NodeCoffeeScriptProcessor; -import ro.isdc.wro.extensions.processor.js.RhinoCoffeeScriptProcessor; -import ro.isdc.wro.model.resource.Resource; -import ro.isdc.wro.model.resource.processor.ResourcePreProcessor; - -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; - -/** - * Creates either a {@link NodeCoffeeScriptProcessor} or a {@link RhinoCoffeeScriptProcessor} - * and delegates the process() method to it. {@link ro.isdc.wro.extensions.processor.js.CoffeeScriptProcessor} should - * do this, but doesn't work correctly inside Tapestry due to its home-grown injection system. - */ -public class NodeOrRhinoCoffeeScriptCompiler implements ResourcePreProcessor -{ - private final Logger logger; - - private final ResourcePreProcessor processor; - - public NodeOrRhinoCoffeeScriptCompiler(Logger logger) - { - this.logger = logger; - processor = create(); - } - - private ResourcePreProcessor create() - { - NodeCoffeeScriptProcessor processor = new NodeCoffeeScriptProcessor(); - - if (processor.isSupported()) - { - logger.info("'coffee' command is available; using Node to compile CoffeeScript files."); - return processor; - } - - logger.info("'coffee' command is not available, using Rhino to compile CoffeeScript files."); - - return new RhinoCoffeeScriptProcessor(); - } - - public void process(Resource resource, Reader reader, Writer writer) throws IOException - { - processor.process(resource, reader, writer); - } -} http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/70889263/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/RhinoExecutor.java ---------------------------------------------------------------------- diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/RhinoExecutor.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/RhinoExecutor.java new file mode 100644 index 0000000..9a1faa5 --- /dev/null +++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/RhinoExecutor.java @@ -0,0 +1,24 @@ +package org.apache.tapestry5.internal.wro4j; + + +import org.mozilla.javascript.ScriptableObject; + +public interface RhinoExecutor +{ + /** + * Invokes the named function, which must return a scriptable object (typically, a JavaScript Object). + * + * @param functionName + * name of function visible to the executor's scope (e.g., loaded from the scripts associated + * with the executor). + * @param arguments + * Arguments to pass to the object which must be convertable to JavaScript types; Strings work well here. + * @return result of invoking the function. + */ + ScriptableObject invokeFunction(String functionName, Object... arguments); + + /** + * Discards the executor, returning it to the pool for reuse. + */ + void discard(); +} http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/70889263/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/RhinoExecutorPool.java ---------------------------------------------------------------------- diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/RhinoExecutorPool.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/RhinoExecutorPool.java new file mode 100644 index 0000000..e7b72cf --- /dev/null +++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/internal/wro4j/RhinoExecutorPool.java @@ -0,0 +1,147 @@ +package org.apache.tapestry5.internal.wro4j; + +import org.apache.tapestry5.ioc.Invokable; +import org.apache.tapestry5.ioc.OperationTracker; +import org.apache.tapestry5.ioc.Resource; +import org.apache.tapestry5.ioc.internal.util.CollectionFactory; +import org.apache.tapestry5.ioc.internal.util.InternalUtils; +import org.apache.tapestry5.ioc.util.ExceptionUtils; +import org.apache.tapestry5.ioc.util.Stack; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ContextFactory; +import org.mozilla.javascript.NativeFunction; +import org.mozilla.javascript.ScriptableObject; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.List; + +/** + * Manages a pool of initialized {@link RhinoExecutor} instances. The instances are initialized for a particular + */ +public class RhinoExecutorPool +{ + private final OperationTracker tracker; + + private final List<Resource> scripts; + + private final Stack<RhinoExecutor> executors = CollectionFactory.newStack(); + + private final ContextFactory contextFactory = new ContextFactory(); + + public RhinoExecutorPool(OperationTracker tracker, List<Resource> scripts) + { + this.tracker = tracker; + this.scripts = scripts; + } + + /** + * Gets or creates an available executor. It is expected that {@link #put(RhinoExecutor)} will + * be invoked after the executor completes. + * + * @return executor + */ + public synchronized RhinoExecutor get() + { + + if (executors.isEmpty()) + { + return createExecutor(); + } + + return executors.pop(); + } + + private synchronized void put(RhinoExecutor executor) + { + executors.push(executor); + } + + private RhinoExecutor createExecutor() + { + return tracker.invoke(String.format("Creating Rhino executor for source(s) %s.", + InternalUtils.join(scripts)), + new Invokable<RhinoExecutor>() + { + public RhinoExecutor invoke() + { + final Context context = contextFactory.enterContext(); + + final ScriptableObject scope = context.initStandardObjects(); + + try + { + context.setOptimizationLevel(-1); + + for (Resource script : scripts) + { + loadScript(context, scope, script); + } + + } finally + { + Context.exit(); + } + + return new RhinoExecutor() + { + public ScriptableObject invokeFunction(String functionName, Object... arguments) + { + contextFactory.enterContext(context); + + try + { + NativeFunction function = (NativeFunction) scope.get(functionName, scope); + + return (ScriptableObject) function.call(context, scope, null, arguments); + } finally + { + Context.exit(); + } + } + + public void discard() + { + put(this); + } + }; + } + }); + } + + private void loadScript(final Context context, final ScriptableObject scope, final Resource script) + { + tracker.run(String.format("Loading script %s.", script), + new Runnable() + { + public void run() + { + InputStream in = null; + Reader r = null; + + try + { + in = script.openStream(); + r = new InputStreamReader(in); + + context.evaluateReader(scope, r, script.toString(), 1, null); + } catch (IOException ex) + { + throw new RuntimeException(String.format("Unable to read script %s: %s", + script, + ExceptionUtils.toMessage(ex) + ), ex); + } finally + { + InternalUtils.close(r); + InternalUtils.close(in); + } + } + }); + + } + + +} http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/70889263/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java ---------------------------------------------------------------------- diff --git a/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java b/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java index e272448..6481d88 100644 --- a/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java +++ b/tapestry-wro4j/src/main/java/org/apache/tapestry5/wro4j/modules/WRO4JModule.java @@ -20,6 +20,7 @@ import org.apache.tapestry5.MarkupWriter; import org.apache.tapestry5.internal.wro4j.*; 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; @@ -72,18 +73,20 @@ public class WRO4JModule @Contribute(ResourceProcessorSource.class) public static void provideDefaultProcessors(MappedConfiguration<String, ResourcePreProcessor> configuration) { - configuration.addInstance("CoffeeScriptCompiler", NodeOrRhinoCoffeeScriptCompiler.class); configuration.addInstance("CSSMinimizer", CssCompressorProcessor.class); configuration.add("JavaScriptMinimizer", new GoogleClosureCompressorProcessor()); } @Contribute(StreamableResourceSource.class) - public static void provideCompilers(MappedConfiguration<String, ResourceTransformer> configuration, ResourceTransformerFactory factory) + 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", "CoffeeScriptCompiler", "CoffeeScript", "JavaScript", CacheMode.SINGLE_FILE)); + factory.createCompiler("text/javascript", "CoffeeScript", "JavaScript", + coffeeScriptCompiler, + CacheMode.SINGLE_FILE)); configuration.add("less", factory.createCompiler("text/css", "Less", "CSS", new LessResourceTransformer(),
