scss_lint 0.39.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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