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.jszip.css.CssCompilationError;
012import org.jszip.css.CssEngine;
013import org.jszip.less.LessEngine;
014import org.jszip.pseudo.io.PseudoDirectoryScanner;
015import org.jszip.pseudo.io.PseudoFile;
016import org.jszip.pseudo.io.PseudoFileOutputStream;
017import org.jszip.pseudo.io.PseudoFileSystem;
018import org.jszip.rhino.GlobalFunctions;
019import org.jszip.rhino.JavaScriptTerminationException;
020import org.jszip.rhino.MavenLogErrorReporter;
021import org.mozilla.javascript.Context;
022import org.mozilla.javascript.ContextFactory;
023import org.mozilla.javascript.Script;
024import org.mozilla.javascript.Scriptable;
025import org.mozilla.javascript.ScriptableObject;
026import org.mozilla.javascript.tools.shell.Global;
027import org.mozilla.javascript.tools.shell.QuitAction;
028import org.mozilla.javascript.tools.shell.ShellContextFactory;
029
030import java.io.File;
031import java.io.FileInputStream;
032import java.io.IOException;
033import java.io.InputStream;
034import java.io.InputStreamReader;
035import java.util.ArrayList;
036import java.util.Arrays;
037import java.util.List;
038
039@Mojo(name = "compile-less", defaultPhase = LifecyclePhase.PROCESS_RESOURCES,
040        requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
041public class CompileLESSMojo extends AbstractPseudoFileSystemProcessorMojo {
042
043    /**
044     * Directory containing the less processor.
045     */
046    @Parameter(defaultValue = "src/build/js/less-rhino.js")
047    private File customLessScript;
048
049    /**
050     * Skip compilation.
051     */
052    @Parameter(property = "jszip.less.skip", defaultValue = "false")
053    private boolean lessSkip;
054
055    /**
056     * Force compilation even if the source LESS file is older than the destination CSS file.
057     */
058    @Parameter(property = "jszip.less.forceIfOlder", defaultValue = "false")
059    private boolean lessForceIfOlder;
060
061    /**
062     * Compress CSS.
063     */
064    @Parameter(property = "jszip.less.compress", defaultValue = "true")
065    private boolean lessCompress;
066
067    /**
068     * Indicates whether the build will continue even if there are compilation errors.
069     */
070    @Parameter(property = "jszip.less.failOnError", defaultValue = "true")
071    private boolean lessFailOnError;
072
073    /**
074     * Indicates whether to show extracts of the code where errors occur.
075     */
076    @Parameter(property = "jszip.less.showErrorExtracts", defaultValue = "false")
077    private boolean showErrorExtracts;
078
079    /**
080     * A list of <include> elements specifying the less files (by pattern) that should be included in
081     * processing.
082     */
083    @Parameter
084    private List<String> lessIncludes;
085
086    /**
087     * A list of &lt;exclude&gt; elements specifying the less files (by pattern) that should be excluded from
088     * processing.
089     */
090    @Parameter
091    private List<String> lessExcludes;
092
093    /**
094     * The character encoding scheme to be applied when reading LESS files.
095     */
096    @Parameter( defaultValue = "${project.build.sourceEncoding}" )
097    private String encoding;
098
099    /**
100     * @see org.apache.maven.plugin.Mojo#execute()
101     */
102    public void execute() throws MojoExecutionException, MojoFailureException {
103        if (lessSkip) {
104            getLog().info("LESS compilation skipped.");
105            return;
106        }
107        if (webappDirectory.isFile()) {
108            throw new MojoExecutionException("Webapp directory '" + webappDirectory + "' is not a directory");
109        }
110        if (!webappDirectory.isDirectory()) {
111            getLog().info("Webapp directory '" + webappDirectory + " does not exist. Nothing to do.");
112            return;
113        }
114        final List<PseudoFileSystem.Layer> layers = buildVirtualFileSystemLayers();
115        final PseudoFileSystem fs = new PseudoFileSystem(layers);
116        try {
117            CssEngine engine = new LessEngine(fs, encoding == null ? "utf-8" : encoding, getLog(), lessCompress, customLessScript, showErrorExtracts);
118
119            // look for files to compile
120
121            PseudoDirectoryScanner scanner = new PseudoDirectoryScanner();
122
123            scanner.setFileSystem(fs);
124
125            scanner.setBasedir(fs.getPseudoFile("/virtual"));
126
127            if (lessIncludes != null && !lessIncludes.isEmpty()) {
128                scanner.setIncludes(processIncludesExcludes(lessIncludes));
129            } else {
130                scanner.setIncludes(new String[]{"**/*.less"});
131            }
132
133            if (lessExcludes != null && !lessExcludes.isEmpty()) {
134                scanner.setExcludes(processIncludesExcludes(lessExcludes));
135            } else {
136                scanner.setExcludes(new String[0]);
137            }
138
139            scanner.scan();
140
141            final List<String> includedFiles = new ArrayList<String>(Arrays.asList(scanner.getIncludedFiles()));
142            getLog().debug("Files to compile: " + includedFiles);
143
144            for (String fileName : includedFiles) {
145                final PseudoFile dest = fs.getPseudoFile("/target/" + engine.mapName(fileName));
146                if (!lessForceIfOlder) {
147                    if (dest.isFile()) {
148                        final PseudoFile src = fs.getPseudoFile("/virtual/" + fileName);
149                        if (src.lastModified() < dest.lastModified()) {
150                            continue;
151                        }
152                    }
153                }
154                if (!dest.getParentFile().isDirectory()) {
155                    dest.getParentFile().mkdirs();
156                }
157
158                final String css = engine.toCSS("/virtual/" + fileName);
159                PseudoFileOutputStream fos = null;
160                try {
161                    fos = new PseudoFileOutputStream(dest);
162                    IOUtil.copy(css, fos);
163                } catch (IOException e) {
164                    throw new MojoFailureException("Could not write CSS file produced from " + fileName, e);
165                } finally {
166                    IOUtil.close(fos);
167                }
168            }
169        } catch (CssCompilationError e) {
170            throw new MojoFailureException("Compilation failure: " + e.getMessage(), e);
171        } catch (IOException e) {
172            throw new MojoExecutionException("Could not instantiate compiler: " + e.getMessage(), e);
173        }
174    }
175}