View Javadoc
1   /*
2    * YuiCompressor Maven plugin
3    *
4    * Copyright 2012-2025 Hazendaz.
5    *
6    * Licensed under the GNU Lesser General Public License (LGPL),
7    * version 2.1 or later (the "License").
8    * You may not use this file except in compliance with the License.
9    * You may read the licence in the 'lgpl.txt' file in the root folder of
10   * project or obtain a copy at
11   *
12   *     https://www.gnu.org/licenses/lgpl-2.1.html
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" basis,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package net.alchim31.maven.yuicompressor;
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.nio.file.Path;
25  import java.util.List;
26  
27  import javax.inject.Inject;
28  
29  import org.apache.maven.model.Resource;
30  import org.apache.maven.plugin.AbstractMojo;
31  import org.apache.maven.plugin.MojoExecutionException;
32  import org.apache.maven.plugin.MojoFailureException;
33  import org.apache.maven.plugins.annotations.Parameter;
34  import org.apache.maven.project.MavenProject;
35  import org.codehaus.plexus.build.BuildContext;
36  import org.codehaus.plexus.util.DirectoryScanner;
37  import org.codehaus.plexus.util.Scanner;
38  
39  /**
40   * Common class for mojos.
41   */
42  public abstract class MojoSupport extends AbstractMojo {
43  
44      /** The Constant EMPTY_STRING_ARRAY. */
45      private static final String[] EMPTY_STRING_ARRAY = {};
46  
47      /**
48       * Javascript source directory. (result will be put to outputDirectory).
49       */
50      @Parameter(defaultValue = "${project.basedir}/src/main/js")
51      private File sourceDirectory;
52  
53      /**
54       * Single directory for extra files to include in the WAR.
55       */
56      @Parameter(defaultValue = "${project.basedir}/src/main/webapp")
57      private File warSourceDirectory;
58  
59      /**
60       * The directory where the webapp is built.
61       */
62      @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}")
63      private File webappDirectory;
64  
65      /**
66       * The output directory into which to copy the resources.
67       */
68      @Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
69      private File outputDirectory;
70  
71      /**
72       * The list of resources we want to transfer.
73       */
74      @Parameter(defaultValue = "${project.resources}", required = true, readonly = true)
75      private List<Resource> resources;
76  
77      /** list of additional excludes. */
78      @Parameter
79      private List<String> excludes;
80  
81      /** Use processed resources if available. */
82      @Parameter(defaultValue = "false")
83      private boolean useProcessedResources;
84  
85      /** list of additional includes. */
86      @Parameter
87      private List<String> includes;
88  
89      /** Excludes files from webapp directory. */
90      @Parameter
91      private boolean excludeWarSourceDirectory;
92  
93      /**
94       * Excludes files from resources directories.
95       */
96      @Parameter(defaultValue = "false")
97      private boolean excludeResources;
98  
99      /** Maven Project. */
100     @Parameter(defaultValue = "${project}", readonly = true, required = true)
101     protected MavenProject project;
102 
103     /** [js only] Display possible errors in the code. */
104     @Parameter(defaultValue = "true", property = "maven.yuicompressor.jswarn")
105     protected boolean jswarn;
106 
107     /**
108      * Whether to skip execution.
109      */
110     @Parameter(defaultValue = "false", property = "maven.yuicompressor.skip")
111     private boolean skip;
112 
113     /**
114      * Define if plugin must stop/fail on warnings.
115      */
116     @Parameter(defaultValue = "false", property = "maven.yuicompressor.failOnWarning")
117     protected boolean failOnWarning;
118 
119     /**
120      * Build Context.
121      */
122     @Inject
123     protected BuildContext buildContext;
124 
125     /** The js error reporter. */
126     protected ErrorReporter4Mojo jsErrorReporter;
127 
128     @Override
129     public void execute() throws MojoExecutionException, MojoFailureException {
130         if (skip) {
131             getLog().debug("run of yuicompressor-maven-plugin skipped");
132             return;
133         }
134 
135         if (failOnWarning) {
136             jswarn = true;
137         }
138 
139         jsErrorReporter = new ErrorReporter4Mojo(getLog(), jswarn, buildContext);
140 
141         try {
142             beforeProcess();
143             processDir(sourceDirectory, outputDirectory, null, useProcessedResources);
144             if (!excludeResources) {
145                 for (Resource resource : resources) {
146                     File destRoot = outputDirectory;
147                     if (resource.getTargetPath() != null) {
148                         destRoot = outputDirectory.toPath().resolve(resource.getTargetPath()).toFile();
149                     }
150                     processDir(Path.of(resource.getDirectory()).toFile(), destRoot, resource.getExcludes(),
151                             useProcessedResources);
152                 }
153             }
154             if (!excludeWarSourceDirectory) {
155                 processDir(warSourceDirectory, webappDirectory, null, useProcessedResources);
156             }
157             afterProcess();
158         } catch (Exception e) {
159             throw new MojoExecutionException("wrap: " + e.getMessage(), e);
160         }
161 
162         getLog().info(String.format("nb warnings: %d, nb errors: %d", jsErrorReporter.getWarningCnt(),
163                 jsErrorReporter.getErrorCnt()));
164         if (failOnWarning && jsErrorReporter.getWarningCnt() > 0) {
165             throw new MojoFailureException("warnings on " + this.getClass().getSimpleName() + "=> failure ! (see log)");
166         }
167     }
168 
169     /**
170      * Gets the default includes.
171      *
172      * @return the default includes
173      */
174     protected abstract String[] getDefaultIncludes();
175 
176     /**
177      * Before process.
178      *
179      * @throws IOException
180      *             the IO exception
181      */
182     protected abstract void beforeProcess() throws IOException;
183 
184     /**
185      * After process.
186      *
187      * @throws IOException
188      *             the IO exception
189      */
190     protected abstract void afterProcess() throws IOException;
191 
192     /**
193      * Force to use defaultIncludes (ignore srcIncludes) to avoid processing resources/includes from other type than
194      * *.css or *.js see https://github.com/davidB/yuicompressor-maven-plugin/issues/19
195      *
196      * @param srcRoot
197      *            the src root
198      * @param destRoot
199      *            the dest root
200      * @param srcExcludes
201      *            the src excludes
202      * @param destAsSource
203      *            the dest as source
204      *
205      * @throws IOException
206      *             the IO exception
207      * @throws MojoFailureException
208      *             the Mojo Failure Exception
209      * @throws MojoExecutionException
210      *             the Mojo Execution Exception
211      */
212     private void processDir(File srcRoot, File destRoot, List<String> srcExcludes, boolean destAsSource)
213             throws IOException, MojoFailureException, MojoExecutionException {
214         if (srcRoot == null) {
215             return;
216         }
217         if (!srcRoot.exists()) {
218             buildContext.addMessage(srcRoot, 0, 0, "Directory " + srcRoot.getPath() + " does not exist",
219                     BuildContext.SEVERITY_WARNING, null);
220             getLog().info("Directory " + srcRoot.getPath() + " does not exist");
221             return;
222         }
223         if (destRoot == null) {
224             throw new MojoFailureException("destination directory for " + srcRoot + " is null");
225         }
226         Scanner scanner;
227         if (!buildContext.isIncremental()) {
228             DirectoryScanner dScanner = new DirectoryScanner();
229             dScanner.setBasedir(srcRoot);
230             scanner = dScanner;
231         } else {
232             scanner = buildContext.newScanner(srcRoot);
233         }
234 
235         if (includes == null) {
236             scanner.setIncludes(getDefaultIncludes());
237         } else {
238             scanner.setIncludes(includes.toArray(new String[0]));
239         }
240 
241         if (srcExcludes != null && !srcExcludes.isEmpty()) {
242             scanner.setExcludes(srcExcludes.toArray(EMPTY_STRING_ARRAY));
243         }
244         if (excludes != null && !excludes.isEmpty()) {
245             scanner.setExcludes(excludes.toArray(EMPTY_STRING_ARRAY));
246         }
247         scanner.addDefaultExcludes();
248 
249         scanner.scan();
250 
251         String[] includedFiles = scanner.getIncludedFiles();
252         if (includedFiles == null || includedFiles.length == 0) {
253             if (buildContext.isIncremental()) {
254                 getLog().info("No files have changed, so skipping the processing");
255             } else {
256                 getLog().info("No files to be processed");
257             }
258             return;
259         }
260         for (String name : includedFiles) {
261             SourceFile src = new SourceFile(srcRoot, destRoot, name, destAsSource);
262             jsErrorReporter.setDefaultFileName("..."
263                     + src.toFile().getCanonicalPath().substring(src.toFile().getCanonicalPath().lastIndexOf('/') + 1));
264             jsErrorReporter.setFile(src.toFile());
265             processFile(src);
266         }
267     }
268 
269     /**
270      * Process file.
271      *
272      * @param src
273      *            the src
274      *
275      * @throws IOException
276      *             the IO exception
277      * @throws MojoExecutionException
278      *             the mojo execution exception
279      */
280     protected abstract void processFile(SourceFile src) throws IOException, MojoExecutionException;
281 }