metanorma-standoc 1.6.5 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/asciidoctor/standoc/basicdoc.rng +18 -3
- data/lib/asciidoctor/standoc/cleanup_boilerplate.rb +33 -20
- data/lib/asciidoctor/standoc/cleanup_inline.rb +3 -1
- data/lib/asciidoctor/standoc/cleanup_terms.rb +1 -6
- data/lib/asciidoctor/standoc/converter.rb +23 -11
- data/lib/asciidoctor/standoc/front_contributor.rb +8 -4
- data/lib/asciidoctor/standoc/inline.rb +6 -5
- data/lib/asciidoctor/standoc/isodoc.rng +7 -0
- data/lib/asciidoctor/standoc/macros.rb +14 -1
- data/lib/asciidoctor/standoc/section.rb +21 -20
- data/lib/asciidoctor/standoc/utils.rb +2 -0
- data/lib/metanorma/standoc/version.rb +1 -1
- data/metanorma-standoc.gemspec +2 -2
- data/spec/asciidoctor-standoc/base_spec.rb +10 -4
- data/spec/asciidoctor-standoc/blocks_spec.rb +6 -1
- data/spec/asciidoctor-standoc/cleanup_sections_spec.rb +7 -2
- data/spec/asciidoctor-standoc/cleanup_spec.rb +3 -0
- data/spec/asciidoctor-standoc/converter_spec.rb +8 -0
- data/spec/asciidoctor-standoc/inline_spec.rb +2 -5
- data/spec/asciidoctor-standoc/macros_spec.rb +30 -0
- data/spec/asciidoctor-standoc/section_spec.rb +6 -1
- data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +75 -69
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6af6d01c6514c3fbfe4a1bbe33b088f15eb78c7243db92fb36694d050a84635
|
4
|
+
data.tar.gz: 4dc1bdc07d11ce753ff4a26482cf5adba8f81153da808a9eb68a6b4cdda13a89
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc0064271a3104f6e26cecdd33228befe52771b31aff7aa18e44325851ebaeb5d2bda60aca28fe5113d87097df945153723553526357e7d6225176a9dbc1067f
|
7
|
+
data.tar.gz: b5f306f4fbab92edefee5887fb3acaa22d31583d37d0de0ddb8d8c469a17ced57d8300ab6254e0c860e3aa7c6519e0ce5f77eaa7986e49bfb497672997c7c743
|
@@ -729,12 +729,27 @@
|
|
729
729
|
</define>
|
730
730
|
<define name="index">
|
731
731
|
<element name="index">
|
732
|
-
<attribute name="
|
732
|
+
<attribute name="to">
|
733
|
+
<data type="IDREF"/>
|
734
|
+
</attribute>
|
735
|
+
<element name="primary">
|
736
|
+
<oneOrMore>
|
737
|
+
<ref name="PureTextElement"/>
|
738
|
+
</oneOrMore>
|
739
|
+
</element>
|
733
740
|
<optional>
|
734
|
-
<
|
741
|
+
<element name="secondary">
|
742
|
+
<oneOrMore>
|
743
|
+
<ref name="PureTextElement"/>
|
744
|
+
</oneOrMore>
|
745
|
+
</element>
|
735
746
|
</optional>
|
736
747
|
<optional>
|
737
|
-
<
|
748
|
+
<element name="tertiary">
|
749
|
+
<oneOrMore>
|
750
|
+
<ref name="PureTextElement"/>
|
751
|
+
</oneOrMore>
|
752
|
+
</element>
|
738
753
|
</optional>
|
739
754
|
</element>
|
740
755
|
</define>
|
@@ -3,14 +3,12 @@ module Asciidoctor
|
|
3
3
|
module Cleanup
|
4
4
|
def external_terms_boilerplate(sources)
|
5
5
|
@i18n.l10n(
|
6
|
-
@i18n.external_terms_boilerplate.gsub(/%/, sources || "???"),
|
7
|
-
@lang, @script)
|
6
|
+
@i18n.external_terms_boilerplate.gsub(/%/, sources || "???"), @lang, @script)
|
8
7
|
end
|
9
8
|
|
10
9
|
def internal_external_terms_boilerplate(sources)
|
11
10
|
@i18n.l10n(
|
12
|
-
@i18n.internal_external_terms_boilerplate.gsub(/%/, sources || "??"),
|
13
|
-
@lang, @script)
|
11
|
+
@i18n.internal_external_terms_boilerplate.gsub(/%/, sources || "??"), @lang, @script)
|
14
12
|
end
|
15
13
|
|
16
14
|
def term_defs_boilerplate(div, source, term, preface, isodoc)
|
@@ -19,11 +17,9 @@ module Asciidoctor
|
|
19
17
|
@anchors[s["bibitemid"]] or
|
20
18
|
@log.add("Crossreferences", nil, "term source #{s['bibitemid']} not referenced")
|
21
19
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
div.next = term_defs_boilerplate_cont(source, term, isodoc)
|
26
|
-
end
|
20
|
+
a = (source.empty? && term.nil?) ? @i18n.no_terms_boilerplate :
|
21
|
+
term_defs_boilerplate_cont(source, term, isodoc)
|
22
|
+
a and div.next = a
|
27
23
|
end
|
28
24
|
|
29
25
|
def term_defs_boilerplate_cont(src, term, isodoc)
|
@@ -38,15 +34,14 @@ module Asciidoctor
|
|
38
34
|
end
|
39
35
|
|
40
36
|
def norm_ref_preface(f)
|
41
|
-
|
42
|
-
|
37
|
+
refs = f.elements.select do |e|
|
38
|
+
["reference", "bibitem"].include? e.name
|
39
|
+
end
|
40
|
+
f.at("./title").next =
|
41
|
+
"<p>#{(refs.empty? ? @i18n.norm_empty_pref : @i18n.norm_with_refs_pref)}</p>"
|
43
42
|
end
|
44
|
-
f.at("./title").next =
|
45
|
-
"<p>#{(refs.empty? ? @i18n.norm_empty_pref : @i18n.norm_with_refs_pref)}</p>"
|
46
|
-
end
|
47
43
|
|
48
|
-
TERM_CLAUSE = "//sections/terms | "
|
49
|
-
"//sections/clause[descendant::terms]".freeze
|
44
|
+
TERM_CLAUSE = "//sections/terms | //sections/clause[descendant::terms]".freeze
|
50
45
|
|
51
46
|
NORM_REF = "//bibliography/references[@normative = 'true']".freeze
|
52
47
|
|
@@ -59,15 +54,33 @@ module Asciidoctor
|
|
59
54
|
@isodoc
|
60
55
|
end
|
61
56
|
|
57
|
+
def termdef_boilerplate_cleanup(xmldoc)
|
58
|
+
#termdef_remove_initial_paras(xmldoc)
|
59
|
+
end
|
60
|
+
|
61
|
+
def termdef_remove_initial_paras(xmldoc)
|
62
|
+
xmldoc.xpath("//terms/p | //terms/ul").each(&:remove)
|
63
|
+
end
|
64
|
+
|
65
|
+
def termdef_unwrap_boilerplate_clauses(xmldoc)
|
66
|
+
xmldoc.xpath(self.class::TERM_CLAUSE).each do |f|
|
67
|
+
f.xpath(".//clause[@type = 'boilerplate']").each do |c|
|
68
|
+
c&.at("./title")&.remove
|
69
|
+
c.replace(c.children)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
62
74
|
def boilerplate_cleanup(xmldoc)
|
63
75
|
isodoc = boilerplate_isodoc(xmldoc)
|
76
|
+
termdef_boilerplate_cleanup(xmldoc)
|
64
77
|
xmldoc.xpath(self.class::TERM_CLAUSE).each do |f|
|
65
|
-
|
66
|
-
|
78
|
+
next if f.at("./clause[@type = 'boilerplate']")
|
79
|
+
term_defs_boilerplate(f.at("./title"), xmldoc.xpath(".//termdocsource"),
|
67
80
|
f.at(".//term"), f.at(".//p"), isodoc)
|
68
81
|
end
|
69
|
-
|
70
|
-
|
82
|
+
termdef_unwrap_boilerplate_clauses(xmldoc)
|
83
|
+
f = xmldoc.at(self.class::NORM_REF) and norm_ref_preface(f)
|
71
84
|
initial_boilerplate(xmldoc, isodoc)
|
72
85
|
end
|
73
86
|
|
@@ -125,7 +125,7 @@ module Asciidoctor
|
|
125
125
|
def concept_termbase_cleanup(x)
|
126
126
|
text = x&.children&.first&.remove&.text
|
127
127
|
termbase, key = x["key"].split(/:/, 2)
|
128
|
-
x.add_child(%(<termref base="#{termbase}" target="#{key}">) +
|
128
|
+
x.add_child(%(<termref base="#{termbase}" target="#{key}">) +
|
129
129
|
"#{text}</termref>")
|
130
130
|
end
|
131
131
|
|
@@ -155,6 +155,8 @@ module Asciidoctor
|
|
155
155
|
ret
|
156
156
|
end
|
157
157
|
|
158
|
+
module_function :to_ncname
|
159
|
+
|
158
160
|
def to_xreftarget(s)
|
159
161
|
return to_ncname(s) unless /^[^#]+#.+$/.match(s)
|
160
162
|
/^(?<pref>[^#]+)#(?<suff>.+)$/ =~ s
|
@@ -47,10 +47,6 @@ module Asciidoctor
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
def termdef_boilerplate_cleanup(xmldoc)
|
51
|
-
xmldoc.xpath("//terms/p | //terms/ul").each(&:remove)
|
52
|
-
end
|
53
|
-
|
54
50
|
def termdef_subclause_cleanup(xmldoc)
|
55
51
|
xmldoc.xpath("//terms[terms]").each { |t| t.name = "clause" }
|
56
52
|
end
|
@@ -83,7 +79,7 @@ module Asciidoctor
|
|
83
79
|
x.name = "note"
|
84
80
|
end
|
85
81
|
xmldoc.xpath("//termexample[not(ancestor::term)]").each do |x|
|
86
|
-
x.name = "
|
82
|
+
x.name = "example"
|
87
83
|
end
|
88
84
|
end
|
89
85
|
|
@@ -96,7 +92,6 @@ module Asciidoctor
|
|
96
92
|
termdefinition_cleanup(xmldoc)
|
97
93
|
termdomain1_cleanup(xmldoc)
|
98
94
|
termnote_example_cleanup(xmldoc)
|
99
|
-
termdef_boilerplate_cleanup(xmldoc)
|
100
95
|
termdef_subclause_cleanup(xmldoc)
|
101
96
|
term_children_cleanup(xmldoc)
|
102
97
|
termdocsource_cleanup(xmldoc)
|
@@ -40,7 +40,8 @@ module Asciidoctor
|
|
40
40
|
inline_macro Asciidoctor::Standoc::VariantInlineMacro
|
41
41
|
inline_macro Asciidoctor::Standoc::FootnoteBlockInlineMacro
|
42
42
|
inline_macro Asciidoctor::Standoc::TermRefInlineMacro
|
43
|
-
inline_macro Asciidoctor::Standoc::
|
43
|
+
inline_macro Asciidoctor::Standoc::IndexXrefInlineMacro
|
44
|
+
inline_macro Asciidoctor::Standoc::IndexRangeInlineMacro
|
44
45
|
block Asciidoctor::Standoc::ToDoAdmonitionBlock
|
45
46
|
treeprocessor Asciidoctor::Standoc::ToDoInlineAdmonitionBlock
|
46
47
|
block Asciidoctor::Standoc::PlantUMLBlockMacro
|
@@ -90,11 +91,26 @@ module Asciidoctor
|
|
90
91
|
end
|
91
92
|
|
92
93
|
def flavor_name
|
93
|
-
self.class.name.split("::")&.[](-2).downcase
|
94
|
+
self.class.name.split("::")&.[](-2).downcase.to_sym
|
94
95
|
end
|
95
96
|
|
96
97
|
def fonts_manifest
|
97
|
-
|
98
|
+
flavor = flavor_name
|
99
|
+
registry = Metanorma::Registry.instance
|
100
|
+
processor = registry.find_processor(flavor)
|
101
|
+
|
102
|
+
if processor.nil?
|
103
|
+
Metanorma::Util.log("[fontist] #{flavor} processor not found. " \
|
104
|
+
"Please go to github.com/metanorma/metanorma/issues to report " \
|
105
|
+
"this issue.", :warn)
|
106
|
+
return nil
|
107
|
+
elsif !defined? processor.fonts_manifest
|
108
|
+
Metanorma::Util.log("[fontist] #{flavor} processor don't require " \
|
109
|
+
"specific fonts", :debug)
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
|
113
|
+
processor.fonts_manifest
|
98
114
|
end
|
99
115
|
|
100
116
|
def install_fonts(options={})
|
@@ -104,14 +120,11 @@ module Asciidoctor
|
|
104
120
|
return
|
105
121
|
end
|
106
122
|
|
107
|
-
|
108
|
-
|
109
|
-
" font manifest file doesn't exists/defined", :debug)
|
110
|
-
return
|
111
|
-
end
|
123
|
+
manifest = fonts_manifest
|
124
|
+
return if manifest.nil?
|
112
125
|
|
113
126
|
begin
|
114
|
-
Fontist::Manifest::Install.
|
127
|
+
Fontist::Manifest::Install.from_hash(
|
115
128
|
fonts_manifest,
|
116
129
|
confirmation: options[:agree_to_terms] ? "yes" : "no"
|
117
130
|
)
|
@@ -127,9 +140,8 @@ module Asciidoctor
|
|
127
140
|
" installed", :fatal)
|
128
141
|
end
|
129
142
|
rescue Fontist::Errors::NonSupportedFontError
|
130
|
-
flavor = flavor_name || "cli"
|
131
143
|
Metanorma::Util.log("[fontist] '#{font}' font is not supported. " \
|
132
|
-
"Please go to github.com/metanorma/metanorma-#{
|
144
|
+
"Please go to github.com/metanorma/metanorma-#{flavor_name}/issues" \
|
133
145
|
" to report this issue.", :info)
|
134
146
|
end
|
135
147
|
end
|
@@ -46,8 +46,9 @@ module Asciidoctor
|
|
46
46
|
|
47
47
|
# , " => ," : CSV definition does not deal with space followed by quote
|
48
48
|
# at start of field
|
49
|
-
def csv_split(s, delim = "
|
50
|
-
|
49
|
+
def csv_split(s, delim = ";")
|
50
|
+
return if s.nil?
|
51
|
+
CSV.parse_line(s&.gsub(/#{delim} "(?!")/, "#{delim}\""),
|
51
52
|
liberal_parsing: true,
|
52
53
|
col_sep: delim)&.compact&.map { |x| x.strip }
|
53
54
|
end
|
@@ -115,8 +116,11 @@ module Asciidoctor
|
|
115
116
|
node.attr("affiliation#{suffix}") and p.affiliation do |a|
|
116
117
|
a.organization do |o|
|
117
118
|
o.name node.attr("affiliation#{suffix}")
|
118
|
-
|
119
|
-
|
119
|
+
a = node.attr("affiliation_subdiv#{suffix}")
|
120
|
+
abbr = node.attr("affiliation_abbrev#{suffix}") and o.abbreviation abbr
|
121
|
+
csv_split(node.attr("affiliation_subdiv#{suffix}"))&.each do |s|
|
122
|
+
o.subdivision s
|
123
|
+
end
|
120
124
|
node.attr("address#{suffix}") and o.address do |ad|
|
121
125
|
ad.formattedAddress do |f|
|
122
126
|
f << node.attr("address#{suffix}").gsub(/ \+\n/, "<br/>")
|
@@ -211,11 +211,12 @@ module Asciidoctor
|
|
211
211
|
def inline_indexterm(node)
|
212
212
|
noko do |xml|
|
213
213
|
node.type == :visible and xml << node.text
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
214
|
+
terms = (node.attr("terms") || [node.text]).map { |x| xml_encode(x) }
|
215
|
+
xml.index do |i|
|
216
|
+
i.primary { |x| x << terms[0] }
|
217
|
+
a = terms.dig(1) and i.secondary { |x| x << a }
|
218
|
+
a = terms.dig(2) and i.tertiary { |x| x << a }
|
219
|
+
end
|
219
220
|
end.join
|
220
221
|
end
|
221
222
|
end
|
@@ -55,6 +55,13 @@
|
|
55
55
|
<param name="pattern">\i\c*|\c+#\c+</param>
|
56
56
|
</data>
|
57
57
|
</attribute>
|
58
|
+
<optional>
|
59
|
+
<attribute name="to">
|
60
|
+
<data type="string">
|
61
|
+
<param name="pattern">\i\c*|\c+#\c+</param>
|
62
|
+
</data>
|
63
|
+
</attribute>
|
64
|
+
</optional>
|
58
65
|
<optional>
|
59
66
|
<attribute name="type">
|
60
67
|
<ref name="ReferenceFormat"/>
|
@@ -23,7 +23,7 @@ module Asciidoctor
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
class
|
26
|
+
class IndexXrefInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
27
27
|
use_dsl
|
28
28
|
named :index
|
29
29
|
|
@@ -44,6 +44,19 @@ module Asciidoctor
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
class IndexRangeInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
48
|
+
use_dsl
|
49
|
+
named "index-range".to_sym
|
50
|
+
parse_content_as :text
|
51
|
+
|
52
|
+
def process(parent, target, attr)
|
53
|
+
text = attr["text"]
|
54
|
+
text = "((#{text}))" unless /^\(\(.+\)\)$/.match(text)
|
55
|
+
out = parent.sub_macros(text)
|
56
|
+
out.sub(/<index>/, "<index to='#{target}'>")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
47
60
|
class PseudocodeBlockMacro < Asciidoctor::Extensions::BlockProcessor
|
48
61
|
use_dsl
|
49
62
|
named :pseudocode
|
@@ -14,8 +14,7 @@ module Asciidoctor
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def sectiontype1(node)
|
17
|
-
node&.attr("heading")&.downcase ||
|
18
|
-
node.title.gsub(/<[^>]+>/, "").downcase
|
17
|
+
node&.attr("heading")&.downcase || node.title.gsub(/<[^>]+>/, "").downcase
|
19
18
|
end
|
20
19
|
|
21
20
|
def sectiontype(node, level = true)
|
@@ -47,15 +46,12 @@ module Asciidoctor
|
|
47
46
|
|
48
47
|
def section_attributes(node)
|
49
48
|
ret = { id: Utils::anchor_or_uuid(node),
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
preface: (
|
57
|
-
(node.role == "preface" || node.attr("style") == "preface") ?
|
58
|
-
true : nil) }
|
49
|
+
language: node.attributes["language"],
|
50
|
+
script: node.attributes["script"],
|
51
|
+
annex: ( ((node.attr("style") == "appendix" || node.role == "appendix") &&
|
52
|
+
node.level == 1) ? true : nil),
|
53
|
+
preface: (
|
54
|
+
(node.role == "preface" || node.attr("style") == "preface") ? true : nil) }
|
59
55
|
return ret unless node.attributes["change"]
|
60
56
|
ret.merge(change: node.attributes["change"],
|
61
57
|
path: node.attributes["path"],
|
@@ -105,9 +101,9 @@ module Asciidoctor
|
|
105
101
|
|
106
102
|
def set_obligation(attrs, node)
|
107
103
|
attrs[:obligation] = node.attributes.has_key?("obligation") ?
|
108
|
-
|
109
|
-
|
110
|
-
|
104
|
+
node.attr("obligation") :
|
105
|
+
node.parent.attributes.has_key?("obligation") ?
|
106
|
+
node.parent.attr("obligation") : "normative"
|
111
107
|
end
|
112
108
|
|
113
109
|
def preamble(node)
|
@@ -187,18 +183,23 @@ module Asciidoctor
|
|
187
183
|
@term_def = defs
|
188
184
|
end
|
189
185
|
|
186
|
+
def terms_boilerplate_parse(attrs, xml, node)
|
187
|
+
defs = @term_def
|
188
|
+
@term_def = false
|
189
|
+
clause_parse(attrs.merge(type: "boilerplate"), xml, node)
|
190
|
+
@term_def = defs
|
191
|
+
end
|
192
|
+
|
190
193
|
# subclause contains subclauses
|
191
194
|
def term_def_subclause_parse(attrs, xml, node)
|
192
|
-
node.role == "nonterm" and
|
193
|
-
|
195
|
+
node.role == "nonterm" and return nonterm_term_def_subclause_parse(attrs, xml, node)
|
196
|
+
node.role == "boilerplate" and return terms_boilerplate_parse(attrs, xml, node)
|
194
197
|
st = sectiontype(node, false)
|
195
198
|
return symbols_parse(attrs, xml, node) if @definitions
|
196
199
|
sub = node.find_by(context: :section) { |s| s.level == node.level + 1 }
|
197
200
|
sub.empty? || (return term_def_parse(attrs, xml, node, false))
|
198
|
-
st == "symbols and abbreviated terms" and
|
199
|
-
|
200
|
-
st == "terms and definitions" and
|
201
|
-
return clause_parse(attrs, xml, node)
|
201
|
+
st == "symbols and abbreviated terms" and (return symbols_parse(attrs, xml, node))
|
202
|
+
st == "terms and definitions" and return clause_parse(attrs, xml, node)
|
202
203
|
term_def_subclause_parse1(attrs, xml, node)
|
203
204
|
end
|
204
205
|
|
@@ -31,8 +31,10 @@ module Asciidoctor
|
|
31
31
|
docfile.nil? ? './' : Pathname.new(docfile).parent.to_s + '/'
|
32
32
|
end
|
33
33
|
|
34
|
+
# TODO needs internationalisation
|
34
35
|
def smartformat(n)
|
35
36
|
n.gsub(/ --? /, " — ").
|
37
|
+
gsub(/\'(\d\d)(?=[^\u2019\'\s<]+[’\'][\p{P}\p{Z}])([\p{P}\p{Z}])/, "\u2018\\1\\2").
|
36
38
|
gsub(/--/, "—").smart_format.gsub(/</, "<").gsub(/>/, ">")
|
37
39
|
end
|
38
40
|
|
data/metanorma-standoc.gemspec
CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
|
29
29
|
spec.add_dependency "asciidoctor", "~> 2.0.0"
|
30
30
|
spec.add_dependency "ruby-jing"
|
31
|
-
spec.add_dependency "isodoc", "~> 1.
|
31
|
+
spec.add_dependency "isodoc", "~> 1.4.0"
|
32
32
|
spec.add_dependency "iev", "~> 0.2.1"
|
33
33
|
spec.add_dependency "metanorma-plugin-datastruct"
|
34
34
|
spec.add_dependency "metanorma-plugin-lutaml", "~> 0.2.1"
|
@@ -41,7 +41,7 @@ Gem::Specification.new do |spec|
|
|
41
41
|
spec.add_dependency "mimemagic"
|
42
42
|
spec.add_dependency "mathml2asciimath"
|
43
43
|
spec.add_dependency "latexmath"
|
44
|
-
spec.add_dependency "fontist", "~> 1.
|
44
|
+
spec.add_dependency "fontist", "~> 1.7.3"
|
45
45
|
|
46
46
|
spec.add_development_dependency "byebug"
|
47
47
|
spec.add_development_dependency "sassc", "2.4.0"
|