1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package nl.geodienstencentrum.maven.plugin.sass.report;
17
18 import com.google.common.primitives.Ints;
19 import java.io.File;
20 import java.util.ArrayList;
21 import java.util.EnumSet;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Set;
26 import javax.script.ScriptContext;
27 import javax.script.ScriptEngine;
28 import javax.script.ScriptEngineManager;
29 import javax.script.ScriptException;
30 import nl.geodienstencentrum.maven.plugin.sass.AbstractSassMojo;
31 import nl.geodienstencentrum.maven.plugin.sass.Resource;
32 import org.apache.maven.plugin.MojoExecutionException;
33 import org.apache.maven.plugin.MojoFailureException;
34 import org.apache.maven.plugin.logging.Log;
35 import org.apache.maven.plugins.annotations.LifecyclePhase;
36 import org.apache.maven.plugins.annotations.Mojo;
37 import org.apache.maven.plugins.annotations.Parameter;
38
39
40
41
42
43
44
45 @Mojo(name = "scss-lint",
46 defaultPhase = LifecyclePhase.COMPILE,
47 executionStrategy = "once-per-session",
48 threadSafe = true
49 )
50 public class SCSSLintMojo extends AbstractSassMojo {
51
52
53
54
55
56
57 @Parameter(defaultValue = "${project.build.directory}/scss-lint.xml", readonly = true)
58 private File outputFile;
59
60
61
62
63 public enum ExitCode {
64
65 CODE_0(0, "No lints were found"),
66
67 CODE_1(1, "Lints with a severity of 'warning' were reported (no errors)"),
68
69 CODE_2(2, "One or more errors were reported (and any number of warnings)"),
70
71 CODE_64(64, "Command line usage error (invalid flag, etc.)"),
72
73 CODE_66(66, "Input file did not exist or was not readable"),
74
75 CODE_70(70, "Internal software error"),
76
77 CODE_78(78, "Configuration error");
78
79
80 private static final HashMap<Integer, ExitCode> LOOKUP = new HashMap<>();
81
82 static {
83 for (final ExitCode c : EnumSet.allOf(ExitCode.class)) {
84 LOOKUP.put(c.code, c);
85 }
86 }
87
88 private final String msg;
89 private final int code;
90
91 ExitCode(final int code, final String msg) {
92 this.code = code;
93 this.msg = msg;
94 }
95
96 String msg() {
97 return msg;
98 }
99
100 int code() {
101 return code;
102 }
103
104 static ExitCode getExitCode(final int code) {
105 return LOOKUP.get(code);
106 }
107
108
109
110
111
112
113 @Override
114 public String toString() {
115 return code + ": " + msg;
116 }
117 };
118
119
120
121
122
123
124
125
126
127
128 @Override
129 public void execute() throws MojoExecutionException, MojoFailureException {
130 if (this.isSkip()) {
131 return;
132 }
133 final Log log = this.getLog();
134
135 Set<String> sourceDirs = this.getSourceDirs();
136 if (sourceDirs.isEmpty()) {
137 return;
138 }
139
140 this.outputFile.getParentFile().mkdirs();
141
142 log.info("Linting Sass sources in: " + this.getSassSourceDirectory());
143
144 final StringBuilder sassScript = new StringBuilder();
145 this.buildBasicSassScript(sassScript);
146
147 log.debug("scss-lint ruby script:\n" + sassScript);
148
149 System.setProperty("org.jruby.embed.localcontext.scope", "threadsafe");
150 final ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
151 final ScriptEngine jruby = scriptEngineManager.getEngineByName("jruby");
152 ScriptContext context = jruby.getContext();
153
154 ArrayList<String> argv = new ArrayList<>();
155 argv.add("--format=Checkstyle");
156 argv.add("--no-color");
157 argv.add("-o" + this.outputFile);
158 argv.addAll(sourceDirs);
159 context.setAttribute(ScriptEngine.ARGV,
160 argv.toArray(new String[argv.size()]),
161 ScriptContext.GLOBAL_SCOPE);
162 try {
163 log.info("Reporting scss lint in: " + this.outputFile.getAbsolutePath());
164 ExitCode result = ExitCode.getExitCode(
165 Ints.checkedCast((Long) jruby.eval(sassScript.toString(),
166 context)));
167 log.debug("scss-lint result: " + result.toString());
168 switch (result) {
169 case CODE_0:
170 log.info(result.msg());
171 break;
172 case CODE_1:
173 log.warn(result.msg());
174 break;
175 case CODE_2:
176 log.error(result.toString());
177 if (this.failOnError) {
178 throw new MojoFailureException(result.toString());
179 }
180 break;
181
182 case CODE_64:
183
184 case CODE_66:
185
186 case CODE_70:
187
188 case CODE_78:
189
190 default:
191 log.error(result.toString());
192 throw new MojoExecutionException(result.toString());
193
194 }
195 } catch (final ScriptException e) {
196 throw new MojoExecutionException(
197 "Failed to execute scss-lint Ruby script:\n" + sassScript, e);
198 }
199 }
200
201
202
203
204 @Override
205 protected void buildBasicSassScript(final StringBuilder sassScript)
206 throws MojoExecutionException {
207 final Log log = this.getLog();
208
209 sassScript.append("require 'scss_lint'\n");
210 sassScript.append("require 'scss_lint/cli'\n");
211 sassScript.append("require 'scss_lint_reporter_checkstyle'\n");
212
213 if (log.isDebugEnabled()) {
214
215 sassScript.append("require 'pp'\n");
216 sassScript.append("puts 'parameters: '\n");
217 sassScript.append("pp ARGV\n");
218 }
219 sassScript.append("logger = SCSSLint::Logger.new(STDOUT)\n");
220 sassScript.append("SCSSLint::CLI.new(logger).run(ARGV)\n");
221 }
222
223
224
225
226
227 private Set<String> getSourceDirs() {
228 Set<String> dirs = new HashSet<>();
229 final List<Resource> resourceList = this.getResources();
230 if (resourceList.isEmpty()) {
231 dirs.add(getSassSourceDirectory().getPath());
232 }
233 for (final Resource source : resourceList) {
234 dirs.addAll(source.getDirectoriesAndDestinations(this.getLog()).keySet());
235 }
236 return dirs;
237 }
238 }