001package org.jszip.maven; 002 003import org.apache.maven.plugin.MojoExecutionException; 004import org.apache.maven.plugin.MojoFailureException; 005import org.apache.maven.plugin.logging.Log; 006import org.apache.maven.plugins.annotations.LifecyclePhase; 007import org.apache.maven.plugins.annotations.Mojo; 008import org.apache.maven.plugins.annotations.Parameter; 009import org.apache.maven.plugins.annotations.ResolutionScope; 010import org.codehaus.plexus.util.IOUtil; 011import org.jruby.embed.EmbedEvalUnit; 012import org.jruby.embed.ScriptingContainer; 013import org.jruby.javasupport.JavaEmbedUtils; 014import org.jszip.css.CssCompilationError; 015import org.jszip.css.CssEngine; 016import org.jszip.pseudo.io.PseudoDirectoryScanner; 017import org.jszip.pseudo.io.PseudoFile; 018import org.jszip.pseudo.io.PseudoFileOutputStream; 019import org.jszip.pseudo.io.PseudoFileSystem; 020import org.jszip.rhino.GlobalFunctions; 021import org.jszip.rhino.MavenLogErrorReporter; 022import org.jszip.sass.SassEngine; 023import org.mozilla.javascript.Context; 024import org.mozilla.javascript.Scriptable; 025import org.mozilla.javascript.ScriptableObject; 026 027import java.io.IOException; 028import java.util.ArrayList; 029import java.util.Arrays; 030import java.util.List; 031 032@Mojo(name = "compile-sass", defaultPhase = LifecyclePhase.PROCESS_RESOURCES, 033 requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) 034public class CompileSASSMojo extends AbstractPseudoFileSystemProcessorMojo { 035 036 /** 037 * Skip compilation. 038 */ 039 @Parameter(property = "jszip.sass.skip", defaultValue = "false") 040 private boolean sassSkip; 041 042 /** 043 * Force compilation even if the source Sass file is older than the destination CSS file. 044 */ 045 @Parameter(property = "jszip.sass.forceIfOlder", defaultValue = "false") 046 private boolean sassForceIfOlder; 047 048 /** 049 * Indicates whether the build will continue even if there are compilation errors. 050 */ 051 @Parameter(property = "jszip.sass.failOnError", defaultValue = "true") 052 private boolean sassFailOnError; 053 054 /** 055 * A list of <include> elements specifying the sass files (by pattern) that should be included in 056 * processing. 057 */ 058 @Parameter 059 private List<String> sassIncludes; 060 061 /** 062 * A list of <exclude> elements specifying the sass files (by pattern) that should be excluded from 063 * processing. 064 */ 065 @Parameter 066 private List<String> sassExcludes; 067 068 /** 069 * The character encoding scheme to be applied when reading SASS files. 070 */ 071 @Parameter( defaultValue = "${project.build.sourceEncoding}" ) 072 private String encoding; 073 074 /** 075 * @see org.apache.maven.plugin.Mojo#execute() 076 */ 077 public void execute() throws MojoExecutionException, MojoFailureException { 078 if (sassSkip) { 079 getLog().info("SASS compilation skipped."); 080 return; 081 } 082 if (webappDirectory.isFile()) { 083 throw new MojoExecutionException("Webapp directory '" + webappDirectory + "' is not a directory"); 084 } 085 if (!webappDirectory.isDirectory()) { 086 getLog().info("Webapp directory '" + webappDirectory + " does not exist. Nothing to do."); 087 return; 088 } 089 final List<PseudoFileSystem.Layer> layers = buildVirtualFileSystemLayers(); 090 final PseudoFileSystem fs = new PseudoFileSystem(layers); 091 try { 092 CssEngine engine = new SassEngine(fs, encoding == null ? "utf-8" : encoding); 093 094 // look for files to compile 095 096 PseudoDirectoryScanner scanner = new PseudoDirectoryScanner(); 097 098 scanner.setFileSystem(fs); 099 100 scanner.setBasedir(fs.getPseudoFile("/virtual")); 101 102 if (sassIncludes != null && !sassIncludes.isEmpty()) { 103 scanner.setIncludes(processIncludesExcludes(sassIncludes)); 104 } else { 105 scanner.setIncludes(new String[]{"**/*.sass","**/*.scss"}); 106 } 107 108 if (sassExcludes != null && !sassExcludes.isEmpty()) { 109 scanner.setExcludes(processIncludesExcludes(sassExcludes)); 110 } else { 111 scanner.setExcludes(new String[]{"**/_*.sass","**/_*.scss"}); 112 } 113 114 scanner.scan(); 115 116 final List<String> includedFiles = new ArrayList<String>(Arrays.asList(scanner.getIncludedFiles())); 117 getLog().debug("Files to compile: " + includedFiles); 118 119 for (String fileName : includedFiles) { 120 final PseudoFile dest = fs.getPseudoFile("/target/" + engine.mapName(fileName)); 121 if (!sassForceIfOlder) { 122 if (dest.isFile()) { 123 final PseudoFile src = fs.getPseudoFile("/virtual/" + fileName); 124 if (src.lastModified() < dest.lastModified()) { 125 continue; 126 } 127 } 128 } 129 if (!dest.getParentFile().isDirectory()) { 130 dest.getParentFile().mkdirs(); 131 } 132 133 final String css = engine.toCSS("/virtual/" + fileName); 134 PseudoFileOutputStream fos = null; 135 try { 136 fos = new PseudoFileOutputStream(dest); 137 IOUtil.copy(css, fos); 138 } catch (IOException e) { 139 throw new MojoFailureException("Could not write CSS file produced from " + fileName, e); 140 } finally { 141 IOUtil.close(fos); 142 } 143 } 144 } catch (CssCompilationError e) { 145 throw new MojoFailureException("Compilation failure: " + e.getMessage(), e); 146 } catch (IOException e) { 147 throw new MojoExecutionException("Could not instantiate compiler: " + e.getMessage(), e); 148 } 149 } 150}