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 &lt;exclude&gt; 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}