scss_lint 0.39.0 → 0.40.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +15 -0
  3. data/lib/scss_lint.rb +1 -0
  4. data/lib/scss_lint/cli.rb +10 -3
  5. data/lib/scss_lint/config.rb +54 -2
  6. data/lib/scss_lint/control_comment_processor.rb +15 -6
  7. data/lib/scss_lint/exceptions.rb +3 -0
  8. data/lib/scss_lint/linter/color_variable.rb +7 -0
  9. data/lib/scss_lint/linter/else_placement.rb +1 -0
  10. data/lib/scss_lint/linter/extend_directive.rb +11 -0
  11. data/lib/scss_lint/linter/final_newline.rb +1 -0
  12. data/lib/scss_lint/linter/name_format.rb +1 -12
  13. data/lib/scss_lint/linter/nesting_depth.rb +22 -1
  14. data/lib/scss_lint/linter/property_sort_order.rb +8 -0
  15. data/lib/scss_lint/linter/property_units.rb +21 -3
  16. data/lib/scss_lint/linter/space_after_variable_name.rb +18 -0
  17. data/lib/scss_lint/linter/space_between_parens.rb +1 -0
  18. data/lib/scss_lint/linter/trailing_whitespace.rb +15 -0
  19. data/lib/scss_lint/plugins.rb +33 -0
  20. data/lib/scss_lint/plugins/linter_dir.rb +24 -0
  21. data/lib/scss_lint/plugins/linter_gem.rb +51 -0
  22. data/lib/scss_lint/version.rb +1 -1
  23. data/spec/scss_lint/config_spec.rb +64 -0
  24. data/spec/scss_lint/fixtures/plugins/linter_plugin.rb +7 -0
  25. data/spec/scss_lint/linter/color_variable_spec.rb +12 -0
  26. data/spec/scss_lint/linter/else_placement_spec.rb +34 -0
  27. data/spec/scss_lint/linter/extend_directive_spec.rb +73 -0
  28. data/spec/scss_lint/linter/nesting_depth_spec.rb +72 -0
  29. data/spec/scss_lint/linter/property_sort_order_spec.rb +32 -0
  30. data/spec/scss_lint/linter/property_units_spec.rb +40 -0
  31. data/spec/scss_lint/linter/space_after_variable_name_spec.rb +13 -0
  32. data/spec/scss_lint/linter/trailing_whitespace_spec.rb +33 -0
  33. data/spec/scss_lint/linter_spec.rb +15 -2
  34. data/spec/scss_lint/plugins/linter_dir_spec.rb +21 -0
  35. data/spec/scss_lint/plugins/linter_gem_spec.rb +60 -0
  36. data/spec/scss_lint/plugins_spec.rb +53 -0
  37. data/spec/spec_helper.rb +10 -0
  38. metadata +26 -5
@@ -0,0 +1,33 @@
1
+ require_relative 'plugins/linter_gem'
2
+ require_relative 'plugins/linter_dir'
3
+
4
+ module SCSSLint
5
+ # Loads external linter plugins.
6
+ class Plugins
7
+ def initialize(config)
8
+ @config = config
9
+ end
10
+
11
+ def load
12
+ all.map(&:load)
13
+ end
14
+
15
+ private
16
+
17
+ def all
18
+ [plugin_gems, plugin_directories].flatten
19
+ end
20
+
21
+ def plugin_gems
22
+ Array(@config['plugin_gems']).map do |gem_name|
23
+ LinterGem.new(gem_name)
24
+ end
25
+ end
26
+
27
+ def plugin_directories
28
+ Array(@config['plugin_directories']).map do |directory|
29
+ LinterDir.new(directory)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,24 @@
1
+ module SCSSLint
2
+ class Plugins
3
+ # Load ruby files from linter plugin directories.
4
+ class LinterDir
5
+ attr_reader :config
6
+
7
+ def initialize(dir)
8
+ @dir = dir
9
+ @config = SCSSLint::Config.new({}) # Will always be empty
10
+ end
11
+
12
+ def load
13
+ ruby_files.each { |file| require file }
14
+ self
15
+ end
16
+
17
+ private
18
+
19
+ def ruby_files
20
+ Dir.glob(File.expand_path(File.join(@dir, '**', '*.rb')))
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,51 @@
1
+ module SCSSLint
2
+ class Plugins
3
+ # Load linter plugin gems
4
+ class LinterGem
5
+ attr_reader :config
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ end
10
+
11
+ def load
12
+ require @name
13
+ @config = plugin_config
14
+ self
15
+ rescue Gem::LoadError, LoadError
16
+ raise SCSSLint::Exceptions::PluginGemLoadError,
17
+ "Unable to load linter plugin gem '#{@name}'. Try running " \
18
+ "`gem install #{@name}`, or adding it to your Gemfile and " \
19
+ 'running `bundle install`. See the `plugin_gems` section of ' \
20
+ 'your .scss-lint.yml file to add/remove gem plugins.'
21
+ end
22
+
23
+ private
24
+
25
+ # Returns the {SCSSLint::Config} for this plugin.
26
+ #
27
+ # This is intended to be merged with the configuration that loaded this
28
+ # plugin.
29
+ #
30
+ # @return [SCSSLint::Config]
31
+ def plugin_config
32
+ file = plugin_config_file
33
+
34
+ if File.exist?(file)
35
+ Config.load(file, merge_with_default: false)
36
+ else
37
+ Config.new({})
38
+ end
39
+ end
40
+
41
+ # Path of the configuration file to attempt to load for this plugin.
42
+ #
43
+ # @return [String]
44
+ def plugin_config_file
45
+ gem_specification = Gem::Specification.find_by_name(@name)
46
+
47
+ File.join(gem_specification.gem_dir, Config::FILE_NAME)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,4 +1,4 @@
1
1
  # Defines the gem version.
2
2
  module SCSSLint
3
- VERSION = '0.39.0'
3
+ VERSION = '0.40.0'
4
4
  end
@@ -143,6 +143,70 @@ describe SCSSLint::Config do
143
143
  .should eq('enabled' => true)
144
144
  end
145
145
  end
146
+
147
+ context 'when configuration loads a plugin configuration' do
148
+ let!(:plugins_manager) { SCSSLint::Plugins.new(double) }
149
+
150
+ before do
151
+ SCSSLint::Plugins.stub(:new).and_return(plugins_manager)
152
+
153
+ plugins_manager.stub(:load).and_return([
154
+ double(config: SCSSLint::Config.new(
155
+ 'linters' => {
156
+ 'FakeConfigLinter' => plugin_linter_config,
157
+ }
158
+ ))
159
+ ])
160
+ end
161
+
162
+ context 'and the plugin configuration does not set the value' do
163
+ let(:plugin_linter_config) { {} }
164
+
165
+ context 'and the local configuration sets the value' do
166
+ let(:config_file) { <<-FILE }
167
+ linters:
168
+ FakeConfigLinter:
169
+ list: [4, 5, 6]
170
+ FILE
171
+
172
+ it 'uses the local configuration value' do
173
+ subject.options['linters']['FakeConfigLinter']['list'].should == [4, 5, 6]
174
+ end
175
+ end
176
+
177
+ context 'and the local configuration does not set the value' do
178
+ let(:config_file) { '' }
179
+
180
+ it 'uses the default configuration value' do
181
+ subject.options['linters']['FakeConfigLinter']['list'].should == [1, 2, 3]
182
+ end
183
+ end
184
+ end
185
+
186
+ context 'and the plugin configuration sets the value' do
187
+ let(:plugin_linter_config) { { 'list' => [2, 3, 4] } }
188
+
189
+ context 'and the local configuration sets the value' do
190
+ let(:config_file) { <<-FILE }
191
+ linters:
192
+ FakeConfigLinter:
193
+ list: [4, 5, 6]
194
+ FILE
195
+
196
+ it 'uses the local configuration value' do
197
+ subject.options['linters']['FakeConfigLinter']['list'].should == [4, 5, 6]
198
+ end
199
+ end
200
+
201
+ context 'and the local configuration does not set the value' do
202
+ let(:config_file) { '' }
203
+
204
+ it 'uses the plugin configuration value' do
205
+ subject.options['linters']['FakeConfigLinter']['list'].should == [2, 3, 4]
206
+ end
207
+ end
208
+ end
209
+ end
146
210
  end
147
211
 
148
212
  describe '#linter_options' do
@@ -0,0 +1,7 @@
1
+ module SCSSLint
2
+ class Linter
3
+ class LinterPlugin < Linter
4
+ include LinterRegistry
5
+ end
6
+ end
7
+ end
@@ -152,4 +152,16 @@ describe SCSSLint::Linter::ColorVariable do
152
152
 
153
153
  it { should_not report_lint }
154
154
  end
155
+
156
+ context 'when a variable is interpolated in a multiline comment' do
157
+ let(:scss) { <<-SCSS }
158
+ $a: 0;
159
+
160
+ /*!
161
+ * test \#{a}
162
+ */
163
+ SCSS
164
+
165
+ it { should_not report_lint }
166
+ end
155
167
  end
@@ -75,6 +75,40 @@ describe SCSSLint::Linter::ElsePlacement do
75
75
  it { should_not report_lint }
76
76
  end
77
77
 
78
+ context 'when @else nested in an @if is on new line' do
79
+ let(:scss) { <<-SCSS }
80
+ @if $condition {
81
+ @if $condition2 {
82
+ $var: 2;
83
+ }
84
+ @else {
85
+ $var: 0;
86
+ }
87
+ } @else {
88
+ $var: 1;
89
+ }
90
+ SCSS
91
+
92
+ it { should report_lint line: 5 }
93
+ end
94
+
95
+ context 'when @else nested in an @else is on new line' do
96
+ let(:scss) { <<-SCSS }
97
+ @if $condition {
98
+ $var: 1;
99
+ } @else {
100
+ @if $condition2 {
101
+ $var: 2;
102
+ }
103
+ @else {
104
+ $var: 0;
105
+ }
106
+ }
107
+ SCSS
108
+
109
+ it { should report_lint line: 7 }
110
+ end
111
+
78
112
  context 'when placement of @else on a new line is preferred' do
79
113
  let(:linter_config) { { 'style' => 'new_line' } }
80
114
 
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe SCSSLint::Linter::ExtendDirective do
4
+ context 'when extending with a class' do
5
+ let(:scss) { <<-SCSS }
6
+ p {
7
+ @extend .error;
8
+ }
9
+ SCSS
10
+
11
+ it { should report_lint line: 2 }
12
+ end
13
+
14
+ context 'when extending with a type' do
15
+ let(:scss) { <<-SCSS }
16
+ p {
17
+ @extend span;
18
+ }
19
+ SCSS
20
+
21
+ it { should report_lint line: 2 }
22
+ end
23
+
24
+ context 'when extending with an id' do
25
+ let(:scss) { <<-SCSS }
26
+ p {
27
+ @extend #some-identifer;
28
+ }
29
+ SCSS
30
+
31
+ it { should report_lint line: 2 }
32
+ end
33
+
34
+ context 'when extending with a placeholder' do
35
+ let(:scss) { <<-SCSS }
36
+ p {
37
+ @extend %placeholder;
38
+ }
39
+ SCSS
40
+
41
+ it { should report_lint line: 2 }
42
+ end
43
+
44
+ context 'when extending with a selector whose prefix is not a placeholder' do
45
+ let(:scss) { <<-SCSS }
46
+ p {
47
+ @extend .blah-\#{$dynamically_generated_name};
48
+ }
49
+ SCSS
50
+
51
+ it { should report_lint line: 2 }
52
+ end
53
+
54
+ context 'when extending with a selector whose prefix is dynamic' do
55
+ let(:scss) { <<-SCSS }
56
+ p {
57
+ @extend \#{$dynamically_generated_placeholder_name};
58
+ }
59
+ SCSS
60
+
61
+ it { should report_lint line: 2 }
62
+ end
63
+
64
+ context 'when not using extend' do
65
+ let(:scss) { <<-SCSS }
66
+ p {
67
+ @include mixin;
68
+ }
69
+ SCSS
70
+
71
+ it { should_not report_lint }
72
+ end
73
+ end
@@ -111,4 +111,76 @@ describe SCSSLint::Linter::NestingDepth do
111
111
  it { should_not report_lint }
112
112
  end
113
113
  end
114
+
115
+ context 'nesting parent selectors' do
116
+ let(:scss) { <<-SCSS }
117
+ .one {
118
+ .two {
119
+ .three {
120
+ &:hover,
121
+ &-suffix {
122
+ .five {
123
+ background: #f00;
124
+ }
125
+ }
126
+ }
127
+
128
+ .another-three { }
129
+ }
130
+ }
131
+ SCSS
132
+
133
+ context 'when parent selectors are ignored' do
134
+ let(:linter_config) { { 'ignore_parent_selectors' => true } }
135
+
136
+ context 'and max depth is set to 2' do
137
+ let(:linter_config) { super().merge('max_depth' => 2) }
138
+
139
+ it { should report_lint line: 3 }
140
+ it { should report_lint line: 12 }
141
+ it { should_not report_lint line: 4 }
142
+ it { should_not report_lint line: 6 }
143
+ end
144
+
145
+ context 'and max depth is set to 3' do
146
+ let(:linter_config) { super().merge('max_depth' => 3) }
147
+
148
+ it { should_not report_lint line: 4 }
149
+ it { should_not report_lint line: 5 }
150
+ it { should report_lint line: 6 }
151
+ end
152
+
153
+ context 'and max depth is set to 4' do
154
+ let(:linter_config) { super().merge('max_depth' => 4) }
155
+
156
+ it { should_not report_lint }
157
+ end
158
+ end
159
+
160
+ context 'when not ignoring parent selectors' do
161
+ let(:linter_config) { { 'ignore_parent_selectors' => false } }
162
+
163
+ context 'and max depth is set to 2' do
164
+ let(:linter_config) { super().merge('max_depth' => 2) }
165
+
166
+ it { should report_lint line: 3 }
167
+ it { should report_lint line: 12 }
168
+ it { should_not report_lint line: 4 }
169
+ it { should_not report_lint line: 6 }
170
+ end
171
+
172
+ context 'and max depth is set to 3' do
173
+ let(:linter_config) { super().merge('max_depth' => 3) }
174
+
175
+ it { should report_lint line: 4 }
176
+ it { should_not report_lint line: 12 }
177
+ end
178
+
179
+ context 'and max depth is set to 4' do
180
+ let(:linter_config) { super().merge('max_depth' => 4) }
181
+
182
+ it { should report_lint line: 6 }
183
+ end
184
+ end
185
+ end
114
186
  end
@@ -316,6 +316,38 @@ describe SCSSLint::Linter::PropertySortOrder do
316
316
  it { should report_lint }
317
317
  end
318
318
  end
319
+
320
+ context 'and there are properties not specified in the explicit ordering at the end' do
321
+ let(:linter_config) do
322
+ super().merge('order' => %w[position top bottom padding width background])
323
+ end
324
+
325
+ let(:scss) { <<-SCSS }
326
+ p {
327
+ position: absolute;
328
+ top: 0;
329
+ bottom: 0;
330
+ padding: 20px;
331
+ width: $drawer-narrow-width;
332
+ background: $drawer-bg-color;
333
+ overflow-y: auto;
334
+ box-sizing: border-box;
335
+ transition: left .25s ease-out;
336
+ }
337
+ SCSS
338
+
339
+ context 'and the ignore_unspecified option is enabled' do
340
+ let(:linter_config) { super().merge('ignore_unspecified' => true) }
341
+
342
+ it { should_not report_lint }
343
+ end
344
+
345
+ context 'and the ignore_unspecified option is disabled' do
346
+ let(:linter_config) { super().merge('ignore_unspecified' => false) }
347
+
348
+ it { should_not report_lint }
349
+ end
350
+ end
319
351
  end
320
352
 
321
353
  context 'when sort order is set to a preset order' do