css_validator 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c88186e16bb12ede012f688d59039d59db05c6d8
4
- data.tar.gz: bb0e22aa6502c3215c613fb1e2a6894e634ca884
3
+ metadata.gz: 83e10ea1409d636a1382fc3ed6f0cefc2a3ca0f9
4
+ data.tar.gz: 55e55e35822eee80c90bbc7f977dab219fac6b4e
5
5
  SHA512:
6
- metadata.gz: 4fe2a790534678405e67758bfa2ece9766daea3287b4dc7963499ab8151c4f74edd8155d73a9350fc2672631d07bd8a73aa551c2aa216e4c7fb8a517ae6516dd
7
- data.tar.gz: e90a399cb3016be36165b3da679a170776ce1207a6574eb0e92cf46b698980e681c52ecbcefecb65d8cdd6ebf39b16ac8c9d92e8e1414f71cf881c4f5f43f412
6
+ metadata.gz: 5d06e5642343d23de0da4f18eb6c24df55c2339f7ffe7ca11c16affb9104233ed59a0510c3d5fd176e380bcb814db3c5bd7e615515051b2422599c181f679489
7
+ data.tar.gz: f5b3323fcde00f4aa0e63c3a2f9dcf1bd2336a431568be064c4e8c130cecb0262051ff606325ffc35d183e0b487f06a7262aed90acee07e422e59f17be70f1ec
data/.gitignore CHANGED
@@ -5,4 +5,5 @@ Gemfile.lock
5
5
  pkg/*
6
6
 
7
7
  Vagrantfile
8
- .vagrant
8
+ .vagrant
9
+ .DS_Store
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # CSS Validator
2
2
 
3
- This uses the `csslint.js` with `js.jar` from [CSSLint](https://github.com/CSSLint/csslint) to validate CSS in ActiveModel.
4
- So, your system requires `java` to use this gem. And, it also works fine on Heroku.
3
+ This uses the `csslint-node.js` from [CSSLint](https://github.com/CSSLint/csslint) to validate CSS in ActiveModel.
4
+ So, your system requires `node.js` to use this gem. It should work fine on Heroku.
5
5
 
6
6
  ## Usage
7
7
 
@@ -0,0 +1,27 @@
1
+ $:.unshift File.expand_path('../lib',__FILE__)
2
+ require 'css_validator'
3
+ require 'active_model'
4
+ require 'benchmark'
5
+
6
+ class WebPage
7
+ include ActiveModel::Validations
8
+ attr_accessor :css
9
+
10
+ validates :css, presence: true, css: true
11
+ end
12
+
13
+ p = WebPage.new
14
+ p.css = <<-eos
15
+ #valid {
16
+ margin: 10px;
17
+ }
18
+ eos
19
+
20
+ n = 10
21
+ Benchmark.bm do |x|
22
+ x.report do
23
+ n.times do
24
+ p.valid? # => true
25
+ end
26
+ end
27
+ end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "css_validator"
3
- s.version = "2.0.0"
3
+ s.version = "3.0.0"
4
4
  s.authors = ["Teng Siong Ong"]
5
5
  s.email = ["siong1987@gmail.com"]
6
6
  s.homepage = "https://github.com/siong1987/css_validator"
@@ -6,8 +6,8 @@ class CssValidator < ActiveModel::EachValidator
6
6
  VENDOR_DIR = File.join(File.expand_path('../..', __FILE__), 'vendor')
7
7
 
8
8
  def validate_each(record, attribute, value)
9
- java_path = `which java`.rstrip
10
- raise 'You do not have a Java installed, but it is required.' unless java_path && !java_path.empty?
9
+ node_path = `which node`.rstrip
10
+ raise 'You do not have a Node.js installed, but it is required.' unless node_path && !node_path.empty?
11
11
 
12
12
  return if options[:allow_nil] && value.nil?
13
13
  return if options[:allow_blank] && value.blank?
@@ -16,7 +16,7 @@ class CssValidator < ActiveModel::EachValidator
16
16
  css_file.write(value)
17
17
  css_file.close
18
18
 
19
- cmd = "#{java_path} -jar #{VENDOR_DIR}/js.jar #{VENDOR_DIR}/csslint-rhino.js --quiet #{css_file.path}"
19
+ cmd = "#{node_path} #{VENDOR_DIR}/cli.js #{css_file.path}"
20
20
  Open3.popen3(cmd) do |stdin, stdout, stderr|
21
21
  result = stdout.read
22
22
  if result =~ /error/
@@ -0,0 +1,441 @@
1
+ #!/usr/bin/env node
2
+ /*!
3
+ CSSLint
4
+ Copyright (c) 2013 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
23
+
24
+ */
25
+ /* Build: v0.10.0 15-August-2013 01:07:22 *//*
26
+ * Encapsulates all of the CLI functionality. The api argument simply
27
+ * provides environment-specific functionality.
28
+ */
29
+ /*global CSSLint*/
30
+ function cli(api){
31
+
32
+ var globalOptions = {
33
+ "help" : { "format" : "", "description" : "Displays this information."},
34
+ "format" : { "format" : "<format>", "description" : "Indicate which format to use for output."},
35
+ "list-rules" : { "format" : "", "description" : "Outputs all of the rules available."},
36
+ "quiet" : { "format" : "", "description" : "Only output when errors are present."},
37
+ "errors" : { "format" : "<rule[,rule]+>", "description" : "Indicate which rules to include as errors."},
38
+ "warnings" : { "format" : "<rule[,rule]+>", "description" : "Indicate which rules to include as warnings."},
39
+ "ignore" : { "format" : "<rule[,rule]+>", "description" : "Indicate which rules to ignore completely."},
40
+ "exclude-list": { "format" : "<file|dir[,file|dir]+>", "description" : "Indicate which files/directories to exclude from being linted."},
41
+ "version" : { "format" : "", "description" : "Outputs the current version number."}
42
+ };
43
+
44
+ //-------------------------------------------------------------------------
45
+ // Helper functions
46
+ //-------------------------------------------------------------------------
47
+
48
+ /**
49
+ * Returns an array of messages for a particular type.
50
+ * @param messages {Array} Array of CSS Lint messages.
51
+ * @param type {String} The type of message to filter on.
52
+ * @return {Array} An array of matching messages.
53
+ */
54
+ function pluckByType(messages, type){
55
+ return messages.filter(function(message) {
56
+ return message.type === type;
57
+ });
58
+ }
59
+
60
+ /**
61
+ * Returns a ruleset object based on the CLI options.
62
+ * @param options {Object} The CLI options.
63
+ * @return {Object} A ruleset object.
64
+ */
65
+ function gatherRules(options, ruleset){
66
+ var warnings = options.rules || options.warnings,
67
+ errors = options.errors;
68
+
69
+ if (warnings){
70
+ ruleset = ruleset || {};
71
+ warnings.split(",").forEach(function(value){
72
+ ruleset[value] = 1;
73
+ });
74
+ }
75
+
76
+ if (errors){
77
+ ruleset = ruleset || {};
78
+ errors.split(",").forEach(function(value){
79
+ ruleset[value] = 2;
80
+ });
81
+ }
82
+
83
+ return ruleset;
84
+ }
85
+
86
+ /**
87
+ * Filters out rules using the ignore command line option.
88
+ * @param options {Object} the CLI options
89
+ * @return {Object} A ruleset object.
90
+ */
91
+ function filterRules(options) {
92
+ var ignore = options.ignore,
93
+ ruleset = null;
94
+
95
+ if (ignore) {
96
+ ruleset = CSSLint.getRuleset();
97
+ ignore.split(",").forEach(function(value){
98
+ ruleset[value] = 0;
99
+ });
100
+ }
101
+
102
+ return ruleset;
103
+ }
104
+
105
+
106
+ /**
107
+ * Filters out files using the exclude-list command line option.
108
+ * @param files {Array} the list of files to check for exclusions
109
+ * @param options {Object} the CLI options
110
+ * @return {Array} A list of files
111
+ */
112
+ function filterFiles(files, options) {
113
+ var excludeList = options["exclude-list"],
114
+ excludeFiles = [],
115
+ filesToLint = files.map(api.getFullPath),
116
+ fullPath;
117
+
118
+
119
+ if (excludeList) {
120
+ // Build up the exclude list, expanding any directory exclusions that were passed in
121
+ excludeList.split(",").forEach(function(value){
122
+ if (api.isDirectory(value)) {
123
+ excludeFiles = excludeFiles.concat(api.getFiles(value));
124
+ } else {
125
+ excludeFiles.push(value);
126
+ }
127
+ });
128
+
129
+ // Remove the excluded files from the list of files to lint
130
+ excludeFiles.forEach(function(value){
131
+ fullPath = api.getFullPath(value);
132
+ if (filesToLint.indexOf(fullPath) > -1) {
133
+ filesToLint.splice(filesToLint.indexOf(fullPath),1);
134
+ }
135
+ });
136
+ }
137
+
138
+ return filesToLint;
139
+ }
140
+
141
+ /**
142
+ * Outputs all available rules to the CLI.
143
+ * @return {void}
144
+ */
145
+ function printRules(){
146
+ api.print("");
147
+ var rules = CSSLint.getRules();
148
+ rules.forEach(function(rule){
149
+ api.print(rule.id + "\n " + rule.desc + "\n");
150
+ });
151
+ }
152
+
153
+ /**
154
+ * Given a file name and options, run verification and print formatted output.
155
+ * @param {String} relativeFilePath absolute file location
156
+ * @param {Object} options for processing
157
+ * @return {Number} exit code
158
+ */
159
+ function processFile(relativeFilePath, options) {
160
+ var input = api.readFile(relativeFilePath),
161
+ ruleset = filterRules(options),
162
+ result = CSSLint.verify(input, gatherRules(options, ruleset)),
163
+ formatter = CSSLint.getFormatter(options.format || "text"),
164
+ messages = result.messages || [],
165
+ output,
166
+ exitCode = 0;
167
+
168
+ if (!input) {
169
+ if (formatter.readError) {
170
+ api.print(formatter.readError(relativeFilePath, "Could not read file data. Is the file empty?"));
171
+ } else {
172
+ api.print("csslint: Could not read file data in " + relativeFilePath + ". Is the file empty?");
173
+ }
174
+ exitCode = 1;
175
+ } else {
176
+ //var relativeFilePath = getRelativePath(api.getWorkingDirectory(), fullFilePath);
177
+ options.fullPath = api.getFullPath(relativeFilePath);
178
+ output = formatter.formatResults(result, relativeFilePath, options);
179
+ if (output){
180
+ api.print(output);
181
+ }
182
+
183
+ if (messages.length > 0 && pluckByType(messages, "error").length > 0) {
184
+ exitCode = 1;
185
+ }
186
+ }
187
+
188
+ return exitCode;
189
+ }
190
+
191
+
192
+ /**
193
+ * Outputs the help screen to the CLI.
194
+ * @return {void}
195
+ */
196
+ function outputHelp(){
197
+ var lenToPad = 40,
198
+ toPrint = '',
199
+ formatString = '';
200
+
201
+ api.print([
202
+ "\nUsage: csslint-rhino.js [options]* [file|dir]*",
203
+ " ",
204
+ "Global Options"
205
+ ].join("\n"));
206
+
207
+ for (var optionName in globalOptions) {
208
+ if (globalOptions.hasOwnProperty(optionName)) {
209
+ // Print the option name and the format if present
210
+ toPrint += " --" + optionName;
211
+ if (globalOptions[optionName].format !== "") {
212
+ formatString = '=' + globalOptions[optionName].format;
213
+ toPrint += formatString;
214
+ } else {
215
+ formatString = '';
216
+ }
217
+
218
+ // Pad out with the appropriate number of spaces
219
+ toPrint += new Array(lenToPad - (optionName.length + formatString.length)).join(' ');
220
+
221
+ // Print the description
222
+ toPrint += globalOptions[optionName].description + "\n";
223
+ }
224
+ }
225
+ api.print(toPrint);
226
+ }
227
+
228
+ /**
229
+ * Given an Array of filenames, print wrapping output and process them.
230
+ * @param files {Array} filenames list
231
+ * @param options {Object} options object
232
+ * @return {Number} exit code
233
+ */
234
+ function processFiles(fileArray, options){
235
+ var exitCode = 0,
236
+ formatId = options.format || "text",
237
+ formatter,
238
+ files = filterFiles(fileArray,options),
239
+ output;
240
+
241
+ if (!files.length) {
242
+ api.print("csslint: No files specified.");
243
+ exitCode = 1;
244
+ } else {
245
+ if (!CSSLint.hasFormat(formatId)){
246
+ api.print("csslint: Unknown format '" + formatId + "'. Cannot proceed.");
247
+ exitCode = 1;
248
+ } else {
249
+ formatter = CSSLint.getFormatter(formatId);
250
+
251
+ output = formatter.startFormat();
252
+ if (output){
253
+ api.print(output);
254
+ }
255
+
256
+
257
+ files.forEach(function(file){
258
+ if (exitCode === 0) {
259
+ exitCode = processFile(file,options);
260
+ } else {
261
+ processFile(file,options);
262
+ }
263
+ });
264
+
265
+ output = formatter.endFormat();
266
+ if (output){
267
+ api.print(output);
268
+ }
269
+ }
270
+ }
271
+ return exitCode;
272
+ }
273
+
274
+
275
+ function processArguments(args, options) {
276
+ var arg = args.shift(),
277
+ argName,
278
+ parts,
279
+ files = [];
280
+
281
+ while(arg){
282
+ if (arg.indexOf("--") === 0){
283
+ argName = arg.substring(2);
284
+
285
+ if (argName.indexOf("=") > -1){
286
+ parts = argName.split("=");
287
+ options[parts[0]] = parts[1];
288
+ } else {
289
+ options[argName] = true;
290
+ }
291
+
292
+ } else {
293
+
294
+ //see if it's a directory or a file
295
+ if (api.isDirectory(arg)){
296
+ files = files.concat(api.getFiles(arg));
297
+ } else {
298
+ files.push(arg);
299
+ }
300
+ }
301
+ arg = args.shift();
302
+ }
303
+
304
+ options.files = files;
305
+ return options;
306
+ }
307
+
308
+ function validateOptions(options) {
309
+ for (var option_key in options) {
310
+ if (!globalOptions.hasOwnProperty(option_key) && option_key !== 'files') {
311
+ api.print(option_key + ' is not a valid option. Exiting...');
312
+ outputHelp();
313
+ api.quit(0);
314
+ }
315
+ }
316
+ }
317
+
318
+ function readConfigFile(options) {
319
+ var data = api.readFile(api.getFullPath(".csslintrc"));
320
+ if (data) {
321
+ options = processArguments(data.split(/[\s\n\r]+/m), options);
322
+ }
323
+
324
+ return options;
325
+ }
326
+
327
+
328
+
329
+ //-----------------------------------------------------------------------------
330
+ // Process command line
331
+ //-----------------------------------------------------------------------------
332
+
333
+ var args = api.args,
334
+ argCount = args.length,
335
+ options = {};
336
+
337
+ // first look for config file .csslintrc
338
+ options = readConfigFile(options);
339
+
340
+ // Command line arguments override config file
341
+ options = processArguments(args, options);
342
+
343
+ if (options.help || argCount === 0){
344
+ outputHelp();
345
+ api.quit(0);
346
+ }
347
+
348
+ // Validate options
349
+ validateOptions(options);
350
+
351
+ if (options.version){
352
+ api.print("v" + CSSLint.version);
353
+ api.quit(0);
354
+ }
355
+
356
+ if (options["list-rules"]){
357
+ printRules();
358
+ api.quit(0);
359
+ }
360
+
361
+ api.quit(processFiles(options.files,options));
362
+ }
363
+
364
+ /*
365
+ * CSSLint Node.js Command Line Interface
366
+ */
367
+
368
+ /*jshint node:true*/
369
+ /*global cli*/
370
+
371
+ var fs = require("fs"),
372
+ path = require("path"),
373
+ CSSLint = require("./lib/csslint-node").CSSLint;
374
+
375
+ cli({
376
+ args: process.argv.slice(2),
377
+
378
+ print: function(message){
379
+ fs.writeSync(1, message + "\n");
380
+ },
381
+
382
+ quit: function(code){
383
+ process.exit(code || 0);
384
+ },
385
+
386
+ isDirectory: function(name){
387
+ try {
388
+ return fs.statSync(name).isDirectory();
389
+ } catch (ex) {
390
+ return false;
391
+ }
392
+ },
393
+
394
+ getFiles: function(dir){
395
+ var files = [];
396
+
397
+ try {
398
+ fs.statSync(dir);
399
+ } catch (ex){
400
+ return [];
401
+ }
402
+
403
+ function traverse(dir, stack){
404
+ stack.push(dir);
405
+ fs.readdirSync(stack.join("/")).forEach(function(file){
406
+ var path = stack.concat([file]).join("/"),
407
+ stat = fs.statSync(path);
408
+
409
+ if (file[0] == ".") {
410
+ return;
411
+ } else if (stat.isFile() && /\.css$/.test(file)){
412
+ files.push(path);
413
+ } else if (stat.isDirectory()){
414
+ traverse(file, stack);
415
+ }
416
+ });
417
+ stack.pop();
418
+ }
419
+
420
+ traverse(dir, []);
421
+
422
+ return files;
423
+ },
424
+
425
+ getWorkingDirectory: function() {
426
+ return process.cwd();
427
+ },
428
+
429
+ getFullPath: function(filename){
430
+ return path.resolve(process.cwd(), filename);
431
+ },
432
+
433
+ readFile: function(filename){
434
+ try {
435
+ return fs.readFileSync(filename, "utf-8");
436
+ } catch (ex) {
437
+ return "";
438
+ }
439
+ }
440
+ });
441
+