grouped_property_scss_linter 1.1.3
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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.scss-lint.yml +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.textile +181 -0
- data/Rakefile +2 -0
- data/data/concentric.yaml +82 -0
- data/data/grouped-smacss.yaml +151 -0
- data/data/personal.yaml +100 -0
- data/data/smacss.yaml +142 -0
- data/grouped_property_scss_linter.gemspec +23 -0
- data/lib/ext/string.rb +9 -0
- data/lib/grouped_property_scss_linter.rb +9 -0
- data/lib/grouped_property_scss_linter/grouped_property_order.rb +310 -0
- data/lib/grouped_property_scss_linter/version.rb +3 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e22237fdcbef363b71f89896640289081051eeff
|
4
|
+
data.tar.gz: 0056fd882b1a9b93488294289b4dfa5b2c21d90e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cfece3ebdca1ca05691671646bb7530c1fc2733674911c1f63aae367688fcceba950fa14a626c780575874a35ae66f625e54971111f87e32c1e8c7c4a3d6f1fa
|
7
|
+
data.tar.gz: bbf00eaf2b8a150e0da8c9db5d3661274f54c94eef766e3cef375f637d792ea72a8e9cc3f97893fd4417063c721e5fc5ea521339d8f3735cb17bb86efe7b72a3
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.scss-lint.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
linters:
|
2
|
+
GroupedPropertyOrder:
|
3
|
+
enabled: true
|
4
|
+
|
5
|
+
# defaults: applied to all groups
|
6
|
+
defaults:
|
7
|
+
space_around: true
|
8
|
+
max_no_space: 3
|
9
|
+
|
10
|
+
# preferred style
|
11
|
+
style: grouped-smacss
|
12
|
+
|
13
|
+
# or a definition of your own. Note that this takes precedence over style
|
14
|
+
groups:
|
15
|
+
|
16
|
+
# whether you get extended hinting information in the output
|
17
|
+
extended_hinting: false
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2016–2017 Jon Pearse
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
h1. Grouped Property Linter for SCSS-Lint
|
2
|
+
|
3
|
+
This is a plugin linter for "SCSS-Lint":https://github.com/brigade/scss-lint that provides a saner alternative to the built-in @PropertySortOrder@ linter.
|
4
|
+
|
5
|
+
Instead of requiring properties to be arranged in a strict order, it instead allows groups of properties (eg: @top@, @right@, @bottom@ & @left@) to be defined, and then lints the order of the _groups_ in your SASS.
|
6
|
+
As long as properties are grouped correctly, the order of individual properties is unimportant.
|
7
|
+
|
8
|
+
h2. Examples
|
9
|
+
|
10
|
+
Using the default configuration:
|
11
|
+
|
12
|
+
*Bad*
|
13
|
+
|
14
|
+
<pre><code lang="css">.selector {
|
15
|
+
padding: .625rem;
|
16
|
+
|
17
|
+
text-decoration: underline;
|
18
|
+
font-size: 1rem;
|
19
|
+
line-height: 1.3;
|
20
|
+
font-weight: bold;
|
21
|
+
color: rgba(20, 20, 20, .8);
|
22
|
+
|
23
|
+
display: inline-block;
|
24
|
+
background: #F00;
|
25
|
+
}</code></pre>
|
26
|
+
|
27
|
+
*Good*
|
28
|
+
|
29
|
+
<pre><code lang="css">.selector {
|
30
|
+
display: inline-block;
|
31
|
+
padding: .625rem;
|
32
|
+
|
33
|
+
color: rgba(20, 20, 20, .8);
|
34
|
+
background: #F00;
|
35
|
+
|
36
|
+
font-size: 1rem;
|
37
|
+
line-height: 1.3em;
|
38
|
+
font-weight: bold;
|
39
|
+
text-decoration: underline;
|
40
|
+
}</code></pre>
|
41
|
+
|
42
|
+
Because the order of individual properties within a group is ignored, neither of the selectors below would generate a warning.
|
43
|
+
|
44
|
+
<pre><code lang="css">.selector-one {
|
45
|
+
display: block;
|
46
|
+
|
47
|
+
height: 10em;
|
48
|
+
width: 90%;
|
49
|
+
}
|
50
|
+
|
51
|
+
.selector-two {
|
52
|
+
display: block;
|
53
|
+
|
54
|
+
width: 90%;
|
55
|
+
height: 10em;
|
56
|
+
}</code></pre>
|
57
|
+
|
58
|
+
h2. Usage
|
59
|
+
|
60
|
+
In order to use this linter, you’ll need to modify both your Gemfile and scss-lint configuration file (typically @.scss-lint@).
|
61
|
+
|
62
|
+
*Gemfile*
|
63
|
+
|
64
|
+
<pre><code lang="ruby">gem 'grouped_property_scss_linter'</code></pre>
|
65
|
+
|
66
|
+
*SCSS-Lint configuration file*
|
67
|
+
|
68
|
+
You will need to add @grouped_property_scss_linter@ to the @plugin_gems@ variable:
|
69
|
+
|
70
|
+
<pre><code lang="yaml">plugin_gems: ['grouped_property_scss_linter']</code></pre>
|
71
|
+
|
72
|
+
h2. Configuration
|
73
|
+
|
74
|
+
When included, this linter is enabled by default, and enforces a modified version of "SMACSS’s categories":https://smacss.com/book/categorizing
|
75
|
+
|
76
|
+
The configuration may be altered in the same way as other linters, by adding a section to your SCSS-Lint configuration file.
|
77
|
+
|
78
|
+
<pre><code lang="yaml">linters:
|
79
|
+
GroupedPropertyOrder:
|
80
|
+
enabled: true
|
81
|
+
defaults:
|
82
|
+
space_around: true
|
83
|
+
max_no_space: 3
|
84
|
+
style: smacss
|
85
|
+
groups:
|
86
|
+
</code></pre>
|
87
|
+
|
88
|
+
h3. Options
|
89
|
+
|
90
|
+
- @enabled@ _(boolean)_ := switches the module on and off (default: on)
|
91
|
+
- @defaults@ _(hash)_ := default linting settings that are applied to all groups (can be overridden per-group)
|
92
|
+
- @style@ _(string, optional)_ := the name of a preconfigured style (see below, defaults to @grouped-smacss@)
|
93
|
+
- @groups@ _(hash, optional)_ := a hash of configured groups. Note that specifying anything here will override the @style@ option
|
94
|
+
- @extended_hinting@ _(boolean, optional)_ := enables additional group information in hinting output (default @false@)
|
95
|
+
|
96
|
+
h4. Default options
|
97
|
+
|
98
|
+
- @space_around@ _(boolean)_ := whether to require space around individual groups (default: true)
|
99
|
+
- @max_no_space@ _(int)_ := the maximum number of properties that can be specified in a group before space is required around it (default: 3, ignored if @space_around@ is @false@)
|
100
|
+
|
101
|
+
h4. Predefined styles
|
102
|
+
|
103
|
+
There a number of property orders/styles supplied with the gem. These are:
|
104
|
+
|
105
|
+
- @smacss@ := an implementation of "SMACSS":https://smacss.com/book/formatting
|
106
|
+
- @grouped-smacss@ _(default)_ := a tweaked version of SMACSS with slightly more granular grouping
|
107
|
+
- @concentric@ := an implementation of "Concentric CSS":https://github.com/brandon-rhodes/Concentric-CSS
|
108
|
+
- @personal@ := my personal ordering, just cuz…
|
109
|
+
|
110
|
+
|
111
|
+
h3. Specifying your own configuration
|
112
|
+
|
113
|
+
Groups are specified as a YAML hash, in the order in which they should appear in your SASS. Each group *must* have a @properties@ member, containing an array of properties that may appear in this group.
|
114
|
+
|
115
|
+
<pre><code lang="yaml">groups:
|
116
|
+
tables:
|
117
|
+
properties:
|
118
|
+
- table-layout
|
119
|
+
- border-collapse
|
120
|
+
- empty-cells</code></pre>
|
121
|
+
|
122
|
+
Specifies a group called @tables@, which may contain @table-layout@, @border-collapse@ and @empty-cells@ properties.
|
123
|
+
|
124
|
+
h4. Wildcard properties
|
125
|
+
|
126
|
+
In cases where a number of properties may have the same prefix, wildcard properties may used instead.
|
127
|
+
Thus, the following group definitions are equivalent.
|
128
|
+
|
129
|
+
<pre><code lang="yaml">groups:
|
130
|
+
text:
|
131
|
+
properties:
|
132
|
+
- font
|
133
|
+
- font-size
|
134
|
+
- font-family
|
135
|
+
- font-style
|
136
|
+
text_two:
|
137
|
+
properties:
|
138
|
+
- font*</code></pre>
|
139
|
+
|
140
|
+
Naturally, you might want to use this functionality carefully…
|
141
|
+
|
142
|
+
h4. Overriding defaults
|
143
|
+
|
144
|
+
In some cases, you may wish to override the default linting options. This can be done by adding the appropriate option to the group hash:
|
145
|
+
|
146
|
+
<pre><code lang="yaml">groups:
|
147
|
+
tables:
|
148
|
+
max_no_space: 1
|
149
|
+
properties:
|
150
|
+
- table-layout
|
151
|
+
- border-collapse
|
152
|
+
- empty-cells</code></pre>
|
153
|
+
|
154
|
+
This defines a @tables@ group as earlier, but requires a space around it at all times.
|
155
|
+
|
156
|
+
h2. Why?
|
157
|
+
|
158
|
+
I’ve written a "blog post":https://jonpearse.net/articles/2016/07/on-linting-and-bringing-order-to-sass about this, but the short version is that I really don’t get on with SCSS-Lint’s default @PropertySortOrder@ linter =)
|
159
|
+
|
160
|
+
h2. Version History
|
161
|
+
|
162
|
+
h3. 1.1.2 _(March 18th, 2017)_
|
163
|
+
|
164
|
+
* eventually pushing this up to RubyGems
|
165
|
+
|
166
|
+
h3. 1.1.1 _(July 15th, 2016)_
|
167
|
+
|
168
|
+
* fixing dumb typoes in the readme
|
169
|
+
|
170
|
+
h3. 1.1.0 _(July 15th, 2016)_
|
171
|
+
|
172
|
+
* improved hint messages to be somewhat more useful to the average developer
|
173
|
+
|
174
|
+
h3. 1.0.0 _(June 12th, 2016)_
|
175
|
+
|
176
|
+
* initial release
|
177
|
+
|
178
|
+
h2. Mandatory sales pitch
|
179
|
+
|
180
|
+
When I’m not hacking at random things, I’m a freelance web developer specialising in all things front-end, based in the beautiful city of Cardiff, UK.
|
181
|
+
I’m usually kept fairly busy with project work, but I’m always on the lookout for new people to do cool stuff with. "Drop me a line":mailto:hello@jonpearse.net – I’d love to hear from you!
|
data/Rakefile
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# Concentric CSS [ http://rhodesmill.org/brandon/2011/concentric-css/ ]
|
2
|
+
#
|
3
|
+
# This is roughly based on SCSS-Lint‘s specification, found at
|
4
|
+
# [ https://github.com/brigade/scss-lint/blob/master/data/property-sort-orders/concentric.txt ]
|
5
|
+
groups:
|
6
|
+
directions:
|
7
|
+
- display
|
8
|
+
- position
|
9
|
+
- top
|
10
|
+
- right
|
11
|
+
- bottom
|
12
|
+
- left
|
13
|
+
|
14
|
+
columns:
|
15
|
+
- columns
|
16
|
+
- column*
|
17
|
+
|
18
|
+
float:
|
19
|
+
- float
|
20
|
+
- clear
|
21
|
+
|
22
|
+
transform:
|
23
|
+
- transform*
|
24
|
+
- transition
|
25
|
+
- animation*
|
26
|
+
|
27
|
+
visibility:
|
28
|
+
- visibility
|
29
|
+
- opacity
|
30
|
+
- z-index
|
31
|
+
|
32
|
+
box:
|
33
|
+
- margin*
|
34
|
+
- outline
|
35
|
+
- border
|
36
|
+
- border-style
|
37
|
+
- border-color
|
38
|
+
- border-radius
|
39
|
+
- border-width
|
40
|
+
- border-image
|
41
|
+
- border-top*
|
42
|
+
- border-right*
|
43
|
+
- border-bottom*
|
44
|
+
- border-left*
|
45
|
+
- box-shadow
|
46
|
+
- background*
|
47
|
+
- cursor
|
48
|
+
- padding*
|
49
|
+
|
50
|
+
dimensions:
|
51
|
+
- width
|
52
|
+
- min-width
|
53
|
+
- max-width
|
54
|
+
- height
|
55
|
+
- min-height
|
56
|
+
- max-height
|
57
|
+
- overflow*
|
58
|
+
|
59
|
+
lists:
|
60
|
+
- list-style*
|
61
|
+
|
62
|
+
tables:
|
63
|
+
- caption-side
|
64
|
+
- table-layout
|
65
|
+
- border-collapse
|
66
|
+
- border-spacing
|
67
|
+
- empty-cells
|
68
|
+
|
69
|
+
content:
|
70
|
+
- vertical-align
|
71
|
+
- text*
|
72
|
+
- line-height
|
73
|
+
- word-spacing
|
74
|
+
- letter-spacing
|
75
|
+
- white-space
|
76
|
+
- color
|
77
|
+
- font*
|
78
|
+
|
79
|
+
generated:
|
80
|
+
- content
|
81
|
+
- quotes
|
82
|
+
- counter* # it strikes me that this should be in lists, but it’s generated, so…
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# This is a variation on SMACSS that specifies a more granular grouping of properties, rather than the five Jonathan
|
2
|
+
# Snook proposes. YMMV =)
|
3
|
+
groups:
|
4
|
+
box-position:
|
5
|
+
- display
|
6
|
+
- position
|
7
|
+
- top
|
8
|
+
- right
|
9
|
+
- bottom
|
10
|
+
- left
|
11
|
+
|
12
|
+
flex:
|
13
|
+
- flex
|
14
|
+
- flex-basis
|
15
|
+
- flex-direction
|
16
|
+
- flex-flow
|
17
|
+
- flex-grow
|
18
|
+
- flex-shrink
|
19
|
+
- flex-wrap
|
20
|
+
- align-content
|
21
|
+
- align-items
|
22
|
+
- align-self
|
23
|
+
- justify-content
|
24
|
+
- order
|
25
|
+
|
26
|
+
box-dimensions:
|
27
|
+
- width
|
28
|
+
- min-width
|
29
|
+
- max-width
|
30
|
+
- height
|
31
|
+
- min-height
|
32
|
+
- max-height
|
33
|
+
- margin
|
34
|
+
- margin-top
|
35
|
+
- margin-right
|
36
|
+
- margin-bottom
|
37
|
+
- margin-left
|
38
|
+
- padding
|
39
|
+
- padding-top
|
40
|
+
- padding-right
|
41
|
+
- padding-bottom
|
42
|
+
- padding-left
|
43
|
+
|
44
|
+
float:
|
45
|
+
- float
|
46
|
+
- clear
|
47
|
+
- clip
|
48
|
+
|
49
|
+
columns:
|
50
|
+
- columns
|
51
|
+
- column-gap
|
52
|
+
- column-fill
|
53
|
+
- column-rule
|
54
|
+
- column-span
|
55
|
+
- column-count
|
56
|
+
- column-width
|
57
|
+
|
58
|
+
transform:
|
59
|
+
- transform
|
60
|
+
- transform-box
|
61
|
+
- transform-origin
|
62
|
+
- transform-style
|
63
|
+
|
64
|
+
transition:
|
65
|
+
- transition
|
66
|
+
- transition-delay
|
67
|
+
- transition-duration
|
68
|
+
- transition-property
|
69
|
+
- transition-timing-function
|
70
|
+
|
71
|
+
border:
|
72
|
+
- border
|
73
|
+
- border-top
|
74
|
+
- border-right
|
75
|
+
- border-bottom
|
76
|
+
- border-left
|
77
|
+
- border-width
|
78
|
+
- border-top-width
|
79
|
+
- border-right-width
|
80
|
+
- border-bottom-width
|
81
|
+
- border-left-width
|
82
|
+
- border-style
|
83
|
+
- border-top-style
|
84
|
+
- border-right-style
|
85
|
+
- border-bottom-style
|
86
|
+
- border-left-style
|
87
|
+
- border-radius
|
88
|
+
- border-top-left-radius
|
89
|
+
- border-top-right-radius
|
90
|
+
- border-bottom-left-radius
|
91
|
+
- border-bottom-right-radius
|
92
|
+
- border-color
|
93
|
+
- border-top-color
|
94
|
+
- border-right-color
|
95
|
+
- border-bottom-color
|
96
|
+
- border-left-color
|
97
|
+
- outline
|
98
|
+
- outline-color
|
99
|
+
- outline-offset
|
100
|
+
- outline-style
|
101
|
+
- outline-width
|
102
|
+
|
103
|
+
background:
|
104
|
+
- background
|
105
|
+
- background-attachment
|
106
|
+
- background-clip
|
107
|
+
- background-color
|
108
|
+
- background-image
|
109
|
+
- background-repeat
|
110
|
+
- background-position
|
111
|
+
- background-size
|
112
|
+
|
113
|
+
text:
|
114
|
+
- color
|
115
|
+
- font
|
116
|
+
- font-family
|
117
|
+
- font-size
|
118
|
+
- font-smoothing
|
119
|
+
- font-style
|
120
|
+
- font-variant
|
121
|
+
- font-weight
|
122
|
+
- letter-spacing
|
123
|
+
- line-height
|
124
|
+
- list-style
|
125
|
+
- text-align
|
126
|
+
- text-decoration
|
127
|
+
- text-indent
|
128
|
+
- text-overflow
|
129
|
+
- text-rendering
|
130
|
+
- text-shadow
|
131
|
+
- text-transform
|
132
|
+
- text-wrap
|
133
|
+
- white-space
|
134
|
+
- word-spacing
|
135
|
+
|
136
|
+
other:
|
137
|
+
- border-collapse
|
138
|
+
- border-spacing
|
139
|
+
- box-shadow
|
140
|
+
- caption-side
|
141
|
+
- content
|
142
|
+
- cursor
|
143
|
+
- empty-cells
|
144
|
+
- opacity
|
145
|
+
- overflow
|
146
|
+
- quotes
|
147
|
+
- speak
|
148
|
+
- table-layout
|
149
|
+
- vertical-align
|
150
|
+
- visibility
|
151
|
+
- z-index
|
data/data/personal.yaml
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# This is my own personal preference, included [a] to make my life easier, and [b] in case anyone is interested.
|
2
|
+
#
|
3
|
+
# Note that this is very much an ongoing process and may change at any time as I tweak and change things. Let me know
|
4
|
+
# what you think!
|
5
|
+
groups:
|
6
|
+
display:
|
7
|
+
properties:
|
8
|
+
- position
|
9
|
+
- display
|
10
|
+
- visibility
|
11
|
+
- overflow*
|
12
|
+
- float
|
13
|
+
- clear
|
14
|
+
- content
|
15
|
+
- box-sizing
|
16
|
+
|
17
|
+
flex-parent:
|
18
|
+
properties:
|
19
|
+
- flex-direction
|
20
|
+
- flex-wrap
|
21
|
+
- flex-flow
|
22
|
+
- justify-content
|
23
|
+
- align-items
|
24
|
+
- align-content
|
25
|
+
|
26
|
+
flex-child:
|
27
|
+
properties:
|
28
|
+
- flex
|
29
|
+
- order
|
30
|
+
- flex-grow
|
31
|
+
- flex-shrink
|
32
|
+
- flex-basis
|
33
|
+
- align-self
|
34
|
+
|
35
|
+
tables:
|
36
|
+
max_no_space: 1
|
37
|
+
properties:
|
38
|
+
- table-layout
|
39
|
+
- border-collapse
|
40
|
+
- empty-cells
|
41
|
+
|
42
|
+
dimensional:
|
43
|
+
properties:
|
44
|
+
- top
|
45
|
+
- left
|
46
|
+
- bottom
|
47
|
+
- right
|
48
|
+
- height
|
49
|
+
- min-height
|
50
|
+
- max-height
|
51
|
+
- width
|
52
|
+
- min-width
|
53
|
+
- max-width
|
54
|
+
- padding*
|
55
|
+
- margin*
|
56
|
+
- transform
|
57
|
+
|
58
|
+
presentational:
|
59
|
+
properties:
|
60
|
+
- outline*
|
61
|
+
- border
|
62
|
+
- border-style
|
63
|
+
- border-color
|
64
|
+
- border-radius
|
65
|
+
- border-width
|
66
|
+
- border-image
|
67
|
+
- border-top*
|
68
|
+
- border-right*
|
69
|
+
- border-bottom*
|
70
|
+
- border-left*
|
71
|
+
- background*
|
72
|
+
- opacity
|
73
|
+
- z-index
|
74
|
+
- color
|
75
|
+
- box-shadow
|
76
|
+
- filter
|
77
|
+
|
78
|
+
lists:
|
79
|
+
space_around: false
|
80
|
+
properties:
|
81
|
+
- counter*
|
82
|
+
- list-style*
|
83
|
+
|
84
|
+
text:
|
85
|
+
properties:
|
86
|
+
- font*
|
87
|
+
- line-height
|
88
|
+
- letter-spacing
|
89
|
+
- text-align
|
90
|
+
- text-decoration
|
91
|
+
- text-indent
|
92
|
+
- text-transform
|
93
|
+
- text-shadow
|
94
|
+
- white-space
|
95
|
+
- content
|
96
|
+
|
97
|
+
interaction:
|
98
|
+
properties:
|
99
|
+
- transition
|
100
|
+
- animation
|
data/data/smacss.yaml
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
# Note: whilst SMACSS defines groups, it doesn’t seem overly fussy about the order of properties within those groups.
|
2
|
+
# I could enforce some kind of subgrouping, but… that‘s not what SMACSS says and I don’t want to enforce something
|
3
|
+
# non-standard.
|
4
|
+
#
|
5
|
+
# You may wish to adapt this style to your own personal tastes =)
|
6
|
+
groups:
|
7
|
+
box:
|
8
|
+
- display
|
9
|
+
- position
|
10
|
+
- top
|
11
|
+
- right
|
12
|
+
- bottom
|
13
|
+
- left
|
14
|
+
- flex
|
15
|
+
- flex-basis
|
16
|
+
- flex-direction
|
17
|
+
- flex-flow
|
18
|
+
- flex-grow
|
19
|
+
- flex-shrink
|
20
|
+
- flex-wrap
|
21
|
+
- align-content
|
22
|
+
- align-items
|
23
|
+
- align-self
|
24
|
+
- justify-content
|
25
|
+
- order
|
26
|
+
- width
|
27
|
+
- min-width
|
28
|
+
- max-width
|
29
|
+
- height
|
30
|
+
- min-height
|
31
|
+
- max-height
|
32
|
+
- margin
|
33
|
+
- margin-top
|
34
|
+
- margin-right
|
35
|
+
- margin-bottom
|
36
|
+
- margin-left
|
37
|
+
- padding
|
38
|
+
- padding-top
|
39
|
+
- padding-right
|
40
|
+
- padding-bottom
|
41
|
+
- padding-left
|
42
|
+
- float
|
43
|
+
- clear
|
44
|
+
- clip
|
45
|
+
- columns
|
46
|
+
- column-gap
|
47
|
+
- column-fill
|
48
|
+
- column-rule
|
49
|
+
- column-span
|
50
|
+
- column-count
|
51
|
+
- column-width
|
52
|
+
- transform
|
53
|
+
- transform-box
|
54
|
+
- transform-origin
|
55
|
+
- transform-style
|
56
|
+
- transition
|
57
|
+
- transition-delay
|
58
|
+
- transition-duration
|
59
|
+
- transition-property
|
60
|
+
- transition-timing-function
|
61
|
+
|
62
|
+
border:
|
63
|
+
- border
|
64
|
+
- border-top
|
65
|
+
- border-right
|
66
|
+
- border-bottom
|
67
|
+
- border-left
|
68
|
+
- border-width
|
69
|
+
- border-top-width
|
70
|
+
- border-right-width
|
71
|
+
- border-bottom-width
|
72
|
+
- border-left-width
|
73
|
+
- border-style
|
74
|
+
- border-top-style
|
75
|
+
- border-right-style
|
76
|
+
- border-bottom-style
|
77
|
+
- border-left-style
|
78
|
+
- border-radius
|
79
|
+
- border-top-left-radius
|
80
|
+
- border-top-right-radius
|
81
|
+
- border-bottom-left-radius
|
82
|
+
- border-bottom-right-radius
|
83
|
+
- border-color
|
84
|
+
- border-top-color
|
85
|
+
- border-right-color
|
86
|
+
- border-bottom-color
|
87
|
+
- border-left-color
|
88
|
+
- outline
|
89
|
+
- outline-color
|
90
|
+
- outline-offset
|
91
|
+
- outline-style
|
92
|
+
- outline-width
|
93
|
+
|
94
|
+
background:
|
95
|
+
- background
|
96
|
+
- background-attachment
|
97
|
+
- background-clip
|
98
|
+
- background-color
|
99
|
+
- background-image
|
100
|
+
- background-repeat
|
101
|
+
- background-position
|
102
|
+
- background-size
|
103
|
+
|
104
|
+
text:
|
105
|
+
- color
|
106
|
+
- font
|
107
|
+
- font-family
|
108
|
+
- font-size
|
109
|
+
- font-smoothing
|
110
|
+
- font-style
|
111
|
+
- font-variant
|
112
|
+
- font-weight
|
113
|
+
- letter-spacing
|
114
|
+
- line-height
|
115
|
+
- list-style
|
116
|
+
- text-align
|
117
|
+
- text-decoration
|
118
|
+
- text-indent
|
119
|
+
- text-overflow
|
120
|
+
- text-rendering
|
121
|
+
- text-shadow
|
122
|
+
- text-transform
|
123
|
+
- text-wrap
|
124
|
+
- white-space
|
125
|
+
- word-spacing
|
126
|
+
|
127
|
+
other:
|
128
|
+
- border-collapse
|
129
|
+
- border-spacing
|
130
|
+
- box-shadow
|
131
|
+
- caption-side
|
132
|
+
- content
|
133
|
+
- cursor
|
134
|
+
- empty-cells
|
135
|
+
- opacity
|
136
|
+
- overflow
|
137
|
+
- quotes
|
138
|
+
- speak
|
139
|
+
- table-layout
|
140
|
+
- vertical-align
|
141
|
+
- visibility
|
142
|
+
- z-index
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'grouped_property_scss_linter/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "grouped_property_scss_linter"
|
8
|
+
spec.version = GroupedPropertyScssLinter::VERSION
|
9
|
+
spec.authors = ["Jon Pearse"]
|
10
|
+
spec.email = ["jon@jonpearse.net"]
|
11
|
+
spec.summary = "SCSS Lint plugin"
|
12
|
+
spec.description = "Plugin for SCSS lint that lints the order of properties based on fuzzy groups"
|
13
|
+
spec.homepage = "https://github.com/jonpearse/grouped_property_scss_linter"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
20
|
+
spec.add_development_dependency "rake", '~> 0'
|
21
|
+
|
22
|
+
spec.add_dependency 'scss_lint', '~> 0'
|
23
|
+
end
|
data/lib/ext/string.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require "grouped_property_scss_linter/version"
|
2
|
+
require "grouped_property_scss_linter/grouped_property_order"
|
3
|
+
require "ext/string"
|
4
|
+
|
5
|
+
module GroupedPropertyScssLinter
|
6
|
+
|
7
|
+
STYLES_DIR = File.realpath(File.join(File.dirname(__FILE__), '..', 'data')).freeze
|
8
|
+
|
9
|
+
end
|
@@ -0,0 +1,310 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
class Linter::GroupedPropertyOrder < Linter
|
3
|
+
include LinterRegistry
|
4
|
+
|
5
|
+
# Called when the linter is fired up on a document. Acts as a pseudo-constructor
|
6
|
+
def visit_root( node )
|
7
|
+
|
8
|
+
# get configured order
|
9
|
+
@configured_groups = get_order_from_conf
|
10
|
+
|
11
|
+
# and map things around
|
12
|
+
@groups = []
|
13
|
+
@property_to_group = {}
|
14
|
+
count = 0
|
15
|
+
@configured_groups.each_pair do |name, group|
|
16
|
+
@groups.push name
|
17
|
+
group['properties'].each do |property|
|
18
|
+
@property_to_group[property] = { name: name, idx: count }
|
19
|
+
end
|
20
|
+
count = count+1
|
21
|
+
end
|
22
|
+
|
23
|
+
yield
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
# Logic that actually performs order
|
28
|
+
def check_order( node )
|
29
|
+
|
30
|
+
# 1. get a list of properties we can sort
|
31
|
+
sortable_properties = node.children.select do |child|
|
32
|
+
child.is_a? Sass::Tree::PropNode
|
33
|
+
end
|
34
|
+
|
35
|
+
# 2. group things
|
36
|
+
grouped_properties = {}
|
37
|
+
props = sortable_properties.map do |prop|
|
38
|
+
|
39
|
+
# simplify the name a little
|
40
|
+
name = prop.name.join
|
41
|
+
|
42
|
+
# attempt to match the name
|
43
|
+
group = find_match_for_property name
|
44
|
+
|
45
|
+
# if it didn’t find anything, move on
|
46
|
+
next if group.nil?
|
47
|
+
|
48
|
+
# if there’s no existing group
|
49
|
+
unless grouped_properties.key? group[:name]
|
50
|
+
grouped_properties[group[:name]] = { first: 1.0/0, last: 0, props: [] }
|
51
|
+
end
|
52
|
+
|
53
|
+
# build a concat
|
54
|
+
concat = { name: name, node: prop, group: group[:name], line: prop.line, group_idx: group[:idx] }
|
55
|
+
|
56
|
+
# drop things on
|
57
|
+
grouped_properties[group[:name]][:first] = [grouped_properties[group[:name]][:first], prop.line].min
|
58
|
+
grouped_properties[group[:name]][:last] = [grouped_properties[group[:name]][:last], prop.line].max
|
59
|
+
grouped_properties[group[:name]][:props] << concat
|
60
|
+
|
61
|
+
concat
|
62
|
+
|
63
|
+
end
|
64
|
+
props.compact!
|
65
|
+
|
66
|
+
# 3. call down
|
67
|
+
unless grouped_properties.empty?
|
68
|
+
check_sort_order props, grouped_properties
|
69
|
+
end
|
70
|
+
|
71
|
+
# 4. yield so we can process children
|
72
|
+
yield
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
# alias things out
|
77
|
+
alias visit_media check_order
|
78
|
+
alias visit_mixin check_order
|
79
|
+
alias visit_rule check_order
|
80
|
+
alias visit_prop check_order
|
81
|
+
|
82
|
+
def visit_if(node, &block)
|
83
|
+
check_order(node, &block)
|
84
|
+
visit(node.else) if node.else
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# Acquires configuration and populates out a group array
|
90
|
+
def get_order_from_conf
|
91
|
+
|
92
|
+
# 1. acquire defaults + default them, just in case
|
93
|
+
defaults = config['defaults']
|
94
|
+
defaults['space_around'] |= true
|
95
|
+
defaults['max_no_space'] |= 3
|
96
|
+
|
97
|
+
# 2, acquire groups
|
98
|
+
groups = config['groups'] || load_groups_from_style
|
99
|
+
|
100
|
+
# 3. if it failed, bail
|
101
|
+
raise 'No groups configured' if groups.nil?
|
102
|
+
|
103
|
+
# 4. munge
|
104
|
+
groups.update(groups) do |name, group|
|
105
|
+
|
106
|
+
# a. if it’s an array, cast it
|
107
|
+
group = { 'properties' => group } if group.is_a? Array
|
108
|
+
|
109
|
+
# b. merge in some defaults
|
110
|
+
group['space_around'] = defaults['space_around'] if group['space_around'].nil?
|
111
|
+
group['max_no_space'] ||= defaults['max_no_space']
|
112
|
+
|
113
|
+
# c. return
|
114
|
+
group
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
groups
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
# Loads a group from a configured style
|
123
|
+
def load_groups_from_style
|
124
|
+
|
125
|
+
# 0. if the style is blank/empty…
|
126
|
+
(raise 'No style specified' and return) if config['style'].empty?
|
127
|
+
|
128
|
+
# 1. attempt to find the file
|
129
|
+
data_filename = File.join(GroupedPropertyScssLinter::STYLES_DIR, "#{config['style']}.yaml")
|
130
|
+
|
131
|
+
# 2. does it exist
|
132
|
+
(raise "No style ‘#{config['style']}’ found" and return) unless File.exists? data_filename
|
133
|
+
|
134
|
+
# 3. can we read it
|
135
|
+
(raise "Cannot read style ‘#{config['style']}’" and return) unless File.readable? data_filename
|
136
|
+
|
137
|
+
# 4. open
|
138
|
+
style_config = YAML.load_file data_filename
|
139
|
+
|
140
|
+
# 5. barf?
|
141
|
+
(raise "Bad style file found for ‘#{config['style']}’" and return) if style_config.nil? or style_config['groups'].nil?
|
142
|
+
|
143
|
+
style_config['groups']
|
144
|
+
end
|
145
|
+
|
146
|
+
# Finds a matching group for a specified property
|
147
|
+
def find_match_for_property( prop )
|
148
|
+
|
149
|
+
# sanitise the name by removing any browser prefixes
|
150
|
+
prop = prop.gsub(/^(-\w+(-osx)?-)?/, '')
|
151
|
+
|
152
|
+
# iteratively remove hyphens from the property…
|
153
|
+
while prop =~ /\-/
|
154
|
+
|
155
|
+
# if we know about this property or its splatted variety…
|
156
|
+
if @property_to_group.key? prop or @property_to_group.key? prop+'*'
|
157
|
+
|
158
|
+
return @property_to_group[prop] || @property_to_group[prop+'*']
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
prop.gsub! /\-(\w+)$/, ''
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
# finally…
|
167
|
+
if @property_to_group.key? prop or @property_to_group.key? prop+'*'
|
168
|
+
|
169
|
+
return @property_to_group[prop] || @property_to_group[prop+'*']
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
nil
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
def check_sort_order( props, grouped )
|
178
|
+
|
179
|
+
# get stats on grouped version
|
180
|
+
grouped.each_value do |group|
|
181
|
+
|
182
|
+
# number of properties
|
183
|
+
group[:num] = group[:props].length
|
184
|
+
group[:delta] = group[:last] - group[:first]
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
# quick duck-type
|
189
|
+
quick_check_order props, grouped
|
190
|
+
|
191
|
+
# if we’re checking whitespace, do so
|
192
|
+
check_whitespace( grouped ) unless grouped.length < 2
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
def quick_check_order( props, grouped )
|
197
|
+
|
198
|
+
current_group = 0
|
199
|
+
good = true
|
200
|
+
|
201
|
+
props.each do |prop|
|
202
|
+
|
203
|
+
# if it’s the current group, move on
|
204
|
+
next if prop[:group] == @groups[current_group]
|
205
|
+
|
206
|
+
# find an index
|
207
|
+
idx = @groups.index prop[:group]
|
208
|
+
|
209
|
+
# if it’s less-than, error out
|
210
|
+
if idx < current_group
|
211
|
+
|
212
|
+
ext = config['extended_hinting'] ? " (assigned group ‘#{prop[:group].bold}’, found group ‘#{@groups[current_group].bold}’)" : ""
|
213
|
+
|
214
|
+
add_lint prop[:node], "property ‘#{prop[:name].bold}’ should be #{hint_text_for(prop, grouped, props)}#{ext}"
|
215
|
+
good = false
|
216
|
+
else
|
217
|
+
current_group = idx
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
good
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
def check_whitespace( grouped )
|
227
|
+
|
228
|
+
# get a quick handle on all the groups we’ve found
|
229
|
+
detected_groups = grouped.keys
|
230
|
+
|
231
|
+
# iterate through
|
232
|
+
curr_idx = 0
|
233
|
+
grouped.each_pair do |name, current|
|
234
|
+
|
235
|
+
# get a configuration
|
236
|
+
group_conf = @configured_groups[name]
|
237
|
+
|
238
|
+
# if we don’t care about space, bail
|
239
|
+
(curr_idx += 1 and next) unless group_conf['space_around']
|
240
|
+
|
241
|
+
# similarly, if this group is too small to trigger spacing, bounce
|
242
|
+
(curr_idx += 1 and next)unless current[:props].length > group_conf['max_no_space']
|
243
|
+
|
244
|
+
# set some easy references
|
245
|
+
next_group = detected_groups.length > curr_idx ? grouped[detected_groups[curr_idx + 1]] : nil
|
246
|
+
prev_group = curr_idx > 0 ? grouped[detected_groups[curr_idx - 1]] : nil
|
247
|
+
|
248
|
+
# if there’s something after us, and there’s no space
|
249
|
+
if !next_group.nil? and ((next_group[:first] - current[:last]) < 2)
|
250
|
+
|
251
|
+
# raise a lint error
|
252
|
+
add_lint current[:props].last[:node], "Must be at least one empty line after ‘#{current[:props].last[:name]}’"
|
253
|
+
|
254
|
+
# also, flag the next group so we don’t catch it next time ‘round
|
255
|
+
next_group[:raised] = true
|
256
|
+
end
|
257
|
+
|
258
|
+
# if there’s something before us, and there’s no space…
|
259
|
+
if !prev_group.nil? and ((current[:first] - prev_group[:last]) < 2) and current[:raised].nil?
|
260
|
+
|
261
|
+
# raise a lint error
|
262
|
+
add_lint current[:props].first[:node], "Must be at least one empty line before ‘#{current[:props].first[:name]}’"
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
# finally, increment
|
267
|
+
curr_idx += 1
|
268
|
+
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def hint_text_for( prop, grouped_props, context )
|
273
|
+
|
274
|
+
# get target group
|
275
|
+
dst_group = prop[:group]
|
276
|
+
|
277
|
+
# if we know about the group…
|
278
|
+
if grouped_props.key? dst_group
|
279
|
+
|
280
|
+
# get the first property of the current group
|
281
|
+
dst_prop = grouped_props[dst_group][:props].first
|
282
|
+
|
283
|
+
# if it’s a different property, return
|
284
|
+
return "after ‘#{dst_prop[:name].bold}’" if dst_prop != prop
|
285
|
+
end
|
286
|
+
|
287
|
+
# either our offending property is the sole member of a group, or it’s very lost… so look for a previous marker
|
288
|
+
curr_idx = prop[:group_idx]
|
289
|
+
while curr_idx > 0
|
290
|
+
|
291
|
+
# decrement and reset
|
292
|
+
curr_idx = curr_idx - 1
|
293
|
+
dst_group = @groups[curr_idx]
|
294
|
+
|
295
|
+
# look
|
296
|
+
if grouped_props.key? dst_group
|
297
|
+
|
298
|
+
# get the _last_ property of the group
|
299
|
+
dst_prop = grouped_props[dst_group][:props].last
|
300
|
+
|
301
|
+
# and return
|
302
|
+
return "after ‘#{dst_prop[:name].bold}’"
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
# otherwise, it probably belongs right at the start
|
307
|
+
"before ‘#{context.first[:name].bold}’"
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: grouped_property_scss_linter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jon Pearse
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: scss_lint
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Plugin for SCSS lint that lints the order of properties based on fuzzy
|
56
|
+
groups
|
57
|
+
email:
|
58
|
+
- jon@jonpearse.net
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- ".scss-lint.yml"
|
65
|
+
- Gemfile
|
66
|
+
- LICENSE.txt
|
67
|
+
- README.textile
|
68
|
+
- Rakefile
|
69
|
+
- data/concentric.yaml
|
70
|
+
- data/grouped-smacss.yaml
|
71
|
+
- data/personal.yaml
|
72
|
+
- data/smacss.yaml
|
73
|
+
- grouped_property_scss_linter.gemspec
|
74
|
+
- lib/ext/string.rb
|
75
|
+
- lib/grouped_property_scss_linter.rb
|
76
|
+
- lib/grouped_property_scss_linter/grouped_property_order.rb
|
77
|
+
- lib/grouped_property_scss_linter/version.rb
|
78
|
+
homepage: https://github.com/jonpearse/grouped_property_scss_linter
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 2.6.10
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: SCSS Lint plugin
|
102
|
+
test_files: []
|