scss_lint-auto_correct 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ecfcbc916da720f957affb069abd0cb138469b9e
4
+ data.tar.gz: 681f8344e431b2ed948a72efa3446ee1b954a4ea
5
+ SHA512:
6
+ metadata.gz: b26b98b6112520ff69a4e95547e9bb6211c5f1446481f982e8aab95d3e316281ab3be3a3315af687572cc4d488334e06d0adbdf09dc40edfcc341b283777b188
7
+ data.tar.gz: 4d28006157b8d380470eef8d1205f573c9840524b90dbcf44399e58aafedb4b5d39a211d613d9bc4ca242eeea6ee0e7636d9efca0903fbb16a501ca8a65b5127
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015, 2017 Dorian Marié, Troels Knak-Nielsen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.expand_path("../../lib", __FILE__)
3
+
4
+ require "scss_lint/auto_correct"
5
+ require "scss_lint/auto_correct/cli"
6
+
7
+ # logger = SCSSLint::Logger.new(STDOUT)
8
+ exit SCSSLint::AutoCorrect::CLI.new.run(ARGV)
@@ -0,0 +1,148 @@
1
+ module SCSSLint::AutoCorrect
2
+ class CLI
3
+ attr_accessor :correctors, :paths, :options, :writer, :config
4
+
5
+ def initialize
6
+ @paths = []
7
+ @options = {}
8
+ @writer = FilesystemWriter.new
9
+ @correctors = Correctors.all
10
+ end
11
+
12
+ def parse_args!(argv)
13
+ @paths = []
14
+ @options = {}
15
+ argv.each do |arg|
16
+ if arg[0..1] == "--"
17
+ @options[arg[2..-1].to_sym] = true
18
+ elsif arg[0] == "-"
19
+ @options[arg[1..-1].to_sym] = true
20
+ elsif File.directory?(arg)
21
+ Dir["#{arg.chomp('/')}/**/*.scss"].each do |f|
22
+ @paths << f
23
+ end
24
+ else
25
+ @paths << arg
26
+ end
27
+ end
28
+ end
29
+
30
+ def option?(*names)
31
+ names.each do |name|
32
+ return true if @options[name.to_sym]
33
+ end
34
+ false
35
+ end
36
+
37
+ def run(argv)
38
+ parse_args! argv
39
+ load_config!
40
+ if option? :help, :h
41
+ print_help
42
+ return 0
43
+ end
44
+ if option? :list
45
+ print_list
46
+ return 0
47
+ end
48
+ if @paths.empty?
49
+ puts "Nothing to process"
50
+ return 1
51
+ end
52
+ process_all @paths
53
+ 0
54
+ end
55
+
56
+ def print_help
57
+ puts "Fix your messy code"
58
+ puts "USAGE: scss-lint-auto-correct [OPTIONS] PATH [PATH ...]"
59
+ puts "OPTIONS are:"
60
+ puts " --help, -h"
61
+ puts " --verbose, -v"
62
+ puts " --dry"
63
+ puts " --list"
64
+ puts "Only apply specific correctors, using any combination of:"
65
+ @correctors.each do |c|
66
+ puts " --#{c.short_name}"
67
+ end
68
+ puts "Disable specific correctors, using any combination of:"
69
+ @correctors.each do |c|
70
+ puts " --no-#{c.short_name}"
71
+ end
72
+ end
73
+
74
+ def print_list
75
+ puts "Configured correctors:"
76
+ @correctors.each do |c|
77
+ puts " #{c.short_name}: #{c.description}"
78
+ end
79
+ end
80
+
81
+ def load_config!
82
+ config_file = relevant_configuration_file
83
+ @config = config_file ? SCSSLint::Config.load(config_file) : SCSSLint::Config.default
84
+ end
85
+
86
+ def relevant_configuration_file
87
+ if File.exist?(SCSSLint::Config::FILE_NAME)
88
+ SCSSLint::Config::FILE_NAME
89
+ elsif File.exist?(SCSSLint::Config.user_file)
90
+ SCSSLint::Config.user_file
91
+ end
92
+ end
93
+
94
+ def process_all(paths)
95
+ paths.each do |path|
96
+ process_file path
97
+ end
98
+ end
99
+
100
+ def process_file(path)
101
+ puts "Processing file #{path}" if option? :verbose, :v
102
+ before = File.read(path)
103
+ after = invoke_correctors(before)
104
+
105
+ write_file(path, after) if before != after
106
+ end
107
+
108
+ def write_file(path, contents)
109
+ puts "Writing file #{path}" if option? :verbose, :v
110
+ writer.write_file(path, contents)
111
+ end
112
+
113
+ def selected_correctors
114
+ selected = []
115
+ unselected = []
116
+ @correctors.each do |c|
117
+ selected << c if option? c.short_name
118
+ unselected << c if option? "no-#{c.short_name}"
119
+ end
120
+ all = selected.any? ? selected : @correctors
121
+ all - unselected
122
+ end
123
+
124
+ def invoke_correctors(contents)
125
+ @correctors.each do |corrector|
126
+ config = corrector.config_name ? @config.options['linters'].fetch(corrector.config_name) : {}
127
+ contents = corrector.new(config).call(contents)
128
+ end
129
+ contents
130
+ end
131
+
132
+ class FilesystemWriter
133
+ def write_file(path, contents)
134
+ File.open(path, "w+") { |f| f.write contents }
135
+ end
136
+ end
137
+
138
+ class DummyWriter
139
+ def files
140
+ @files ||= []
141
+ end
142
+
143
+ def write_file(path, contents)
144
+ files << { path: path, contents: contents }
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,28 @@
1
+ module SCSSLint::AutoCorrect::Correctors
2
+ class Base
3
+ def self.config_name
4
+ end
5
+
6
+ def self.short_name
7
+ self.name.split("::").last.
8
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
9
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
10
+ tr('_', '-').
11
+ downcase
12
+ end
13
+
14
+ def self.priority
15
+ 10
16
+ end
17
+
18
+ attr_reader :config
19
+
20
+ def initialize(config = {})
21
+ @config = config
22
+ end
23
+
24
+ def enabled?
25
+ @config["enabled"] != false
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ module SCSSLint::AutoCorrect::Correctors
2
+ class DoubleQuoting < Base
3
+ def call(contents)
4
+ contents.gsub("'", '"')
5
+ end
6
+
7
+ def self.description
8
+ "Use double quotes, rather than single quotes"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module SCSSLint::AutoCorrect::Correctors
2
+ class DowncaseColors < Base
3
+ def call(contents)
4
+ contents.gsub(/#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/) { |m| m.downcase }
5
+ end
6
+
7
+ def self.description
8
+ "Downcase hex color values"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module SCSSLint::AutoCorrect::Correctors
2
+ class FormatNewlines < Base
3
+ def call(contents)
4
+ contents.gsub(/}\n( *[\.a-zA-Z0-9=\-:&\[\]]+) {/m) do
5
+ "}\n\n#{Regexp.last_match[1]} {"
6
+ end
7
+ end
8
+
9
+ def self.description
10
+ ""
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module SCSSLint::AutoCorrect::Correctors
2
+ class LinesEndWithSemicolons < Base
3
+ def call(contents)
4
+ # if a line ends with a letter, it means it should end with a semicolon
5
+ # (and it contains a : but no comments (// and *) and some buggy expressions (&))
6
+ contents.gsub(/^[^\/*&]+:[^\/*&]+[A-Za-z0-9-]$/, '\0;')
7
+ end
8
+
9
+ def self.description
10
+ "Ensure lines end with a semicolon, when they should"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module SCSSLint::AutoCorrect::Correctors
2
+ class NewlinesForEachSelector < Base
3
+ def call(contents)
4
+ contents.gsub(/([a-zA-Z0-9\[\]=~* ]+,){2,}.+{/) do |m|
5
+ selectors = m.split(",")
6
+ first = selectors.first.rstrip
7
+ rest = selectors[1..-1].map { |s| " " * first.count(" ") + s.strip }
8
+ ([first] + rest).join(",\n")
9
+ end
10
+ end
11
+
12
+ def self.description
13
+ "Ensure each selector goes on its own line"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ module SCSSLint::AutoCorrect::Correctors
2
+ class NoSpacesBeforeSemicolons < Base
3
+ def call(contents)
4
+ contents.gsub(/ +;/, ';')
5
+ end
6
+
7
+ def self.description
8
+ "Remove spaces before semicolons"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module SCSSLint::AutoCorrect::Correctors
2
+ class ShortVersionsOfNumbers < Base
3
+ def call(contents)
4
+ # do not match 1.04 or v1.0.0 or LICENSE-2.0
5
+ # (this is pure dark magic)
6
+ contents.gsub(/([^-v[0-9]?\.?)])([0-9])\.0+([^0-9])/, '\1\2\3')
7
+ end
8
+
9
+ def self.description
10
+ ""
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module SCSSLint::AutoCorrect::Correctors
2
+ class ShorterVersionsOfColors < Base
3
+ def call(contents)
4
+ contents.gsub(/#(([A-Za-z0-9])\2){3}/) { |m| "##{m[1]}#{m[3]}#{m[5]}" }
5
+ end
6
+
7
+ def self.description
8
+ "Shortens hex color codes to 3 letters, when possible"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module SCSSLint::AutoCorrect::Correctors
2
+ class SpacingBeforeSelectors < Base
3
+ def call(contents)
4
+ contents.gsub(/[a-zA-Z0-9%\[\]=*~]+ +{/) { |m| m.gsub(/ +/, ' ') }
5
+ end
6
+
7
+ def self.description
8
+ "Removes any extra whitespace before selector opening brace"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ module SCSSLint::AutoCorrect::Correctors
2
+ class UseHexInsteadOfColorKeywords < Base
3
+ def call(contents)
4
+ colors = self.class.data
5
+ regexp = Regexp.new("^([^\/]+ )(#{colors.keys.join('|')})([\) ;].*)$")
6
+
7
+ contents.gsub(regexp) do
8
+ Regexp.last_match[1] + colors[Regexp.last_match[2]] + Regexp.last_match[3]
9
+ end
10
+ end
11
+
12
+ def self.data
13
+ return @data if @data
14
+ colors_file = File.expand_path(File.join(File.dirname(__FILE__), "color-keywords.csv"))
15
+ @data = Hash[File.read(colors_file).lines.map { |l| l.gsub("\n", '').split(',') }]
16
+ end
17
+
18
+ def self.description
19
+ "Replace named colors with their hex values"
20
+ end
21
+
22
+ def self.priority
23
+ 5
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ module SCSSLint::AutoCorrect::Correctors
2
+ class VariableNames < Base
3
+ def call(contents)
4
+ contents.gsub(/\$[a-z_-]+/) { |m| m.gsub('_', '-') }
5
+ end
6
+
7
+ def self.description
8
+ "Use dashes instead of underscores in variable names"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module SCSSLint::AutoCorrect
2
+ module Correctors
3
+ def self.all
4
+ ObjectSpace.
5
+ each_object(Class).
6
+ select { |klass| klass < Base }.
7
+ sort_by(&:priority)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ # Defines the gem version.
2
+ module SCSSLint::AutoCorrect
3
+ VERSION = '1.0.0'.freeze
4
+ end
@@ -0,0 +1,10 @@
1
+ require "scss_lint"
2
+
3
+ module SCSSLint
4
+ module AutoCorrect
5
+ end
6
+ end
7
+
8
+ Dir[File.expand_path('..', __FILE__) + "/**/*.rb"].sort_by(&:length).each do |path|
9
+ require path
10
+ end
@@ -0,0 +1,3 @@
1
+ .lorem {
2
+ color: #FF0000;
3
+ }
@@ -0,0 +1,25 @@
1
+ require "minitest/mock"
2
+ require "minitest/autorun"
3
+ require "minitest/pride"
4
+
5
+ $LOAD_PATH << File.expand_path("../../lib", __FILE__)
6
+
7
+ Dir[File.expand_path('../../lib', __FILE__) + "/**/*.rb"].sort_by(&:length).each do |path|
8
+ require path
9
+ end
10
+
11
+ module Minitest
12
+ class Test
13
+ # Set is `let`'s wacky cousin
14
+ def set(key, value)
15
+ @_memoized ||= {}
16
+ @_memoized[key.to_s] = value
17
+ value
18
+ end
19
+
20
+ def load_file(filename, type: "t")
21
+ path = File.join(File.dirname(__FILE__), "fixtures", filename)
22
+ File.open(path, "r#{type}").read
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,43 @@
1
+ require_relative "../test_helper"
2
+
3
+ class CliTest < Minitest::Test
4
+ describe "SCSSLint::AutoCorrect::CLI" do
5
+ let(:cli) { SCSSLint::AutoCorrect::CLI }
6
+
7
+ it "basically works" do
8
+ cli.new
9
+ end
10
+
11
+ it "can parse regular args" do
12
+ instance = cli.new
13
+ instance.parse_args! ["foo"]
14
+ assert_equal ["foo"], instance.paths
15
+ end
16
+
17
+ it "can parse regular options" do
18
+ instance = cli.new
19
+ instance.parse_args! ["--verbose"]
20
+ assert_equal [], instance.paths
21
+ assert_equal({ verbose: true }, instance.options)
22
+ end
23
+
24
+ it "allows running a specific correctors only" do
25
+ instance = cli.new
26
+ instance.parse_args! ["--downcase-colors", "--format-newlines"]
27
+ assert_equal 2, instance.selected_correctors.count
28
+ end
29
+
30
+ it "allows skipping a specific corrector" do
31
+ instance = cli.new
32
+ all = instance.selected_correctors.count
33
+ instance.parse_args! ["--no-downcase-colors"]
34
+ assert_equal all - 1, instance.selected_correctors.count
35
+ end
36
+
37
+ it "loads scss-lint config" do
38
+ instance = cli.new
39
+ instance.load_config!
40
+ assert_equal SCSSLint::Config, instance.config.class
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,18 @@
1
+ require_relative "../test_helper"
2
+
3
+ class DowncaseColorsTest < Minitest::Test
4
+ describe "SCSSLint::AutoCorrect::Correctors::DowncaseColors" do
5
+ let(:corrector) { SCSSLint::AutoCorrect::Correctors::DowncaseColors }
6
+
7
+ it "basically works" do
8
+ corrector.new
9
+ end
10
+
11
+ it "process input" do
12
+ input = load_file "has_uppercase_colors.scss"
13
+ output = corrector.new.call input
14
+ refute_includes output, "#FF0000"
15
+ assert_includes output, "#ff0000"
16
+ end
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scss_lint-auto_correct
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Dorian Marié
8
+ - Troels Knak-Nielsen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2017-12-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: scss_lint
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ description: A bunch of scripts for correcting some scss linting errors.
29
+ email:
30
+ - dorian@doma.io
31
+ - troels@knak-nielsen.dk
32
+ executables:
33
+ - scss-lint-auto-correct
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - LICENSE
38
+ - bin/scss-lint-auto-correct
39
+ - lib/scss_lint/auto_correct.rb
40
+ - lib/scss_lint/auto_correct/cli.rb
41
+ - lib/scss_lint/auto_correct/correctors.rb
42
+ - lib/scss_lint/auto_correct/correctors/base.rb
43
+ - lib/scss_lint/auto_correct/correctors/double_quoting.rb
44
+ - lib/scss_lint/auto_correct/correctors/downcase_colors.rb
45
+ - lib/scss_lint/auto_correct/correctors/format_newlines.rb
46
+ - lib/scss_lint/auto_correct/correctors/lines_end_with_semicolons.rb
47
+ - lib/scss_lint/auto_correct/correctors/newlines_for_each_selector.rb
48
+ - lib/scss_lint/auto_correct/correctors/no_spaces_before_semicolons.rb
49
+ - lib/scss_lint/auto_correct/correctors/short_versions_of_numbers.rb
50
+ - lib/scss_lint/auto_correct/correctors/shorter_versions_of_colors.rb
51
+ - lib/scss_lint/auto_correct/correctors/spacing_before_selectors.rb
52
+ - lib/scss_lint/auto_correct/correctors/use_hex_instead_of_color_keywords.rb
53
+ - lib/scss_lint/auto_correct/correctors/variable_names.rb
54
+ - lib/scss_lint/auto_correct/version.rb
55
+ - test/fixtures/has_uppercase_colors.scss
56
+ - test/test_helper.rb
57
+ - test/unit/cli_test.rb
58
+ - test/unit/downcase_colors_test.rb
59
+ homepage: https://github.com/Dorian/scss-lint-auto-correct
60
+ licenses:
61
+ - MIT
62
+ metadata: {}
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '2'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 2.5.1
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: Auto corrections for SCSS lint tool
83
+ test_files:
84
+ - test/fixtures/has_uppercase_colors.scss
85
+ - test/test_helper.rb
86
+ - test/unit/cli_test.rb
87
+ - test/unit/downcase_colors_test.rb