kramdown-rfc2629 1.5.26 → 1.6.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.
data/bin/kramdown-rfc2629 CHANGED
@@ -1,539 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- coding: utf-8 -*-
3
- require 'kramdown-rfc2629'
4
- require 'kramdown-rfc/parameterset'
5
- require 'kramdown-rfc/refxml'
6
- require 'kramdown-rfc/rfc8792'
7
- require 'yaml'
8
- require 'kramdown-rfc/erb'
9
- require 'date'
3
+ require 'kramdown-rfc/command'
10
4
 
11
- # try to get this from gemspec.
12
- KDRFC_VERSION=Gem.loaded_specs["kramdown-rfc2629"].version rescue "unknown-version"
13
-
14
- Encoding.default_external = "UTF-8" # wake up, smell the coffee
15
-
16
- # Note that this doesn't attempt to handle HT characters
17
- def remove_indentation(s)
18
- l = s.lines
19
- indent = l.grep(/\S/).map {|l| l[/^\s*/].size}.min
20
- l.map {|li| li.sub(/^ {0,#{indent}}/, "")}.join
21
- end
22
-
23
- def add_quote(s)
24
- l = s.lines
25
- l.map {|li| "> #{li}"}.join
26
- end
27
-
28
- def process_chunk(s, nested, dedent, fold, quote)
29
- process_includes(s) if nested
30
- s = remove_indentation(s) if dedent
31
- s = fold8792_1(s, *fold) if fold
32
- s = add_quote(s) if quote
33
- s
34
- end
35
-
36
- def process_includes(input)
37
- input.gsub!(/^\{::include((?:-[a-z0-9]+)*)\s+(.*?)\}/) {
38
- include_flags = $1
39
- fn = [$2]
40
- chunks = false
41
- nested = false
42
- dedent = false
43
- fold = false
44
- quote = false
45
- include_flags.split("-") do |flag|
46
- case flag
47
- when ""
48
- when "nested"
49
- nested = true
50
- when "quote"
51
- quote = true
52
- when "dedent"
53
- dedent = true
54
- when /\Afold(\d*)(left(\d*))?(dry)?\z/
55
- fold = [$1.to_i, # col 0 for ''
56
- ($3.to_i if $2), # left 0 for '', nil if no "left"
57
- $4] # dry
58
- when "all", "last"
59
- fn = fn.flat_map{|n| Dir[n]}
60
- fn = [fn.last] if flag == "last"
61
- chunks = fn.map{ |f|
62
- ret = process_chunk(File.read(f), nested, dedent, fold, quote)
63
- nested = false; dedent = false; fold = false; quote = false
64
- ret
65
- }
66
- else
67
- warn "** unknown include flag #{flag}"
68
- end
69
- end
70
- chunks = fn.map{|f| File.read(f)} unless chunks # no all/last
71
- chunks = chunks.map {|ch| process_chunk(ch, nested, dedent, fold, quote)}
72
- chunks.join.chomp
73
- }
74
- end
75
-
76
-
77
- def boilerplate(key)
78
- case key.downcase
79
- when /\Abcp14(info)?(\+)?(-tagged)?\z/i
80
- ret = ''
81
- if $1
82
- ret << <<RFC8174ise
83
- Although this document is not an IETF Standards Track publication, it
84
- adopts the conventions for normative language to provide clarity of
85
- instructions to the implementer.
86
- RFC8174ise
87
- end
88
- ret << <<RFC8174
89
- The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
90
- NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED",
91
- "MAY", and "OPTIONAL" in this document are to be interpreted as
92
- described in BCP 14 {{!RFC2119}} {{!RFC8174}} when, and only when, they
93
- appear in all capitals, as shown here.
94
- RFC8174
95
- if $2
96
- ret << <<PLUS
97
- These words may also appear in this document in
98
- lower case as plain English words, absent their normative meanings.
99
- PLUS
100
- end
101
- if $3
102
- if $options.v3
103
- ret << <<TAGGED
104
-
105
- *[MUST]: <bcp14>
106
- *[MUST NOT]: <bcp14>
107
- *[REQUIRED]: <bcp14>
108
- *[SHALL]: <bcp14>
109
- *[SHALL NOT]: <bcp14>
110
- *[SHOULD]: <bcp14>
111
- *[SHOULD NOT]: <bcp14>
112
- *[RECOMMENDED]: <bcp14>
113
- *[NOT RECOMMENDED]: <bcp14>
114
- *[MAY]: <bcp14>
115
- *[OPTIONAL]: <bcp14>
116
- TAGGED
117
- else
118
- warn "** need --v3 to tag bcp14"
119
- end
120
- end
121
- ret
122
- else
123
- warn "** Unknwon boilerplate key: #{key}"
124
- "{::boilerplate #{key}}"
125
- end
126
- end
127
-
128
- def do_the_tls_dance
129
- begin
130
- require 'openssl'
131
- File.open(OpenSSL::X509::DEFAULT_CERT_FILE) do end
132
- # This guards against having an unreadable cert file (yes, that appears to happen a lot).
133
- rescue
134
- if Dir[File.join(OpenSSL::X509::DEFAULT_CERT_DIR, "*.pem")].empty?
135
- # This guards against having no certs at all, not against missing the right one for IETF.
136
- # Oh well.
137
- warn "** Configuration problem with OpenSSL certificate store."
138
- warn "** You may want to examine #{OpenSSL::X509::DEFAULT_CERT_FILE}"
139
- warn "** and #{OpenSSL::X509::DEFAULT_CERT_DIR}."
140
- warn "** Activating suboptimal workaround."
141
- warn "** Occasionally run `certified-update` to maintain that workaround."
142
- require 'certified'
143
- end
144
- end
145
- end
146
-
147
- RE_NL = /(?:\n|\r|\r\n)/
148
- RE_SECTION = /---(?: +(\w+)(-?))?\s*#{RE_NL}(.*?#{RE_NL})(?=---(?:\s+\w+-?)?\s*#{RE_NL}|\Z)/m
149
-
150
- NMDTAGS = ["{:/nomarkdown}\n\n", "\n\n{::nomarkdown}\n"]
151
-
152
- NORMINFORM = { "!" => :normative, "?" => :informative }
153
-
154
- def yaml_load(input, *args)
155
- if YAML.respond_to?(:safe_load)
156
- begin
157
- YAML.safe_load(input, *args)
158
- rescue ArgumentError
159
- YAML.safe_load(input, permitted_classes: args[0], permitted_symbols: args[1], aliases: args[2])
160
- end
161
- else
162
- YAML.load(input)
163
- end
164
- end
165
-
166
- def process_kramdown_options(coding_override = nil,
167
- smart_quotes = nil, typographic_symbols = nil,
168
- header_kramdown_options = nil)
169
-
170
- ascii_target = coding_override && coding_override =~ /ascii/
171
- suppress_typography = ascii_target || $options.v3
172
- entity_output = ascii_target ? :numeric : :as_char;
173
-
174
- options = {input: 'RFC2629Kramdown', entity_output: entity_output, link_defs: {}}
175
-
176
- if smart_quotes.nil? && suppress_typography
177
- smart_quotes = false
178
- end
179
- if smart_quotes == false
180
- smart_quotes = ["'".ord, "'".ord, '"'.ord, '"'.ord]
181
- end
182
- case smart_quotes
183
- when Array
184
- options[:smart_quotes] = smart_quotes
185
- when nil, true
186
- # nothin
187
- else
188
- warn "*** Can't deal with smart_quotes value #{smart_quotes.inspect}"
189
- end
190
-
191
- if typographic_symbols.nil? && suppress_typography
192
- typographic_symbols = false
193
- end
194
- if typographic_symbols == false
195
- typographic_symbols = Hash[::Kramdown::Parser::Kramdown::TYPOGRAPHIC_SYMS.map { |k, v|
196
- if Symbol === v
197
- [v.intern, k]
198
- end
199
- }.compact]
200
- end
201
- # warn [:TYPOGRAPHIC_SYMBOLS, typographic_symbols].to_yaml
202
- case typographic_symbols
203
- when Hash
204
- options[:typographic_symbols] = typographic_symbols
205
- when nil, true
206
- # nothin
207
- else
208
- warn "*** Can't deal with typographic_symbols value #{typographic_symbols.inspect}"
209
- end
210
-
211
- if header_kramdown_options
212
- options.merge! header_kramdown_options
213
- end
214
-
215
- $global_markdown_options = options # For nested calls in bibref annotation processing and xref text
216
-
217
- options
218
- end
219
-
220
- XREF_SECTIONS_RE = ::Kramdown::Parser::RFC2629Kramdown::SECTIONS_RE
221
- XSR_PREFIX = "#{XREF_SECTIONS_RE} of "
222
- XSR_SUFFIX = ", (#{XREF_SECTIONS_RE})| \\((#{XREF_SECTIONS_RE})\\)"
223
- XREF_TXT = ::Kramdown::Parser::RFC2629Kramdown::XREF_TXT
224
- XREF_TXT_SUFFIX = " \\(#{XREF_TXT}\\)"
225
-
226
- def spacify_re(s)
227
- s.gsub(' ', '[\u00A0\s]+')
228
- end
229
-
230
- def xml_from_sections(input)
231
-
232
- unless ENV["KRAMDOWN_NO_SOURCE"]
233
- require 'kramdown-rfc/gzip-clone'
234
- require 'base64'
235
- compressed_input = Gzip.compress(input)
236
- $source = Base64.encode64(compressed_input)
237
- end
238
-
239
- sections = input.scan(RE_SECTION)
240
- # resulting in an array; each section is [section-label, nomarkdown-flag, section-text]
241
-
242
- # the first section is a YAML with front matter parameters (don't put a label here)
243
- # We put back the "---" plus gratuitous blank lines to hack the line number in errors
244
- yaml_in = input[/---\s*/] << sections.shift[2]
245
- ps = KramdownRFC::ParameterSet.new(yaml_load(yaml_in, [Date], [], true))
246
-
247
- coding_override = ps.has(:coding)
248
- smart_quotes = ps[:smart_quotes]
249
- typographic_symbols = ps[:typographic_symbols]
250
- header_kramdown_options = ps[:kramdown_options]
251
-
252
- kramdown_options = process_kramdown_options(coding_override,
253
- smart_quotes, typographic_symbols,
254
- header_kramdown_options)
255
-
256
- # all the other sections are put in a Hash, possibly concatenated from parts there
257
- sechash = Hash.new{ |h,k| h[k] = ""}
258
- snames = [] # a stack of section names
259
- sections.each do |sname, nmdflag, text|
260
- # warn [:SNAME, sname, nmdflag, text[0..10]].inspect
261
- nmdin, nmdout = {
262
- "-" => ["", ""], # stay in nomarkdown
263
- "" => NMDTAGS, # pop out temporarily
264
- }[nmdflag || ""]
265
- if sname
266
- snames << sname # "--- label" -> push label (now current)
267
- else
268
- snames.pop # just "---" -> pop label (previous now current)
269
- end
270
- sechash[snames.last] << "#{nmdin}#{text}#{nmdout}"
271
- end
272
-
273
- ref_replacements = { }
274
- anchor_to_bibref = { }
275
-
276
- [:ref, :normative, :informative].each do |sn|
277
- if refs = ps.has(sn)
278
- warn "*** bad section #{sn}: #{refs.inspect}" unless refs.respond_to? :each
279
- refs.each do |k, v|
280
- if v.respond_to? :to_str
281
- if bibtagsys(v) # enable "foo: RFC4711" as a custom anchor definition
282
- anchor_to_bibref[k] = v.to_str
283
- end
284
- ref_replacements[v.to_str] = k
285
- end
286
- if Hash === v
287
- if aliasname = v.delete("-")
288
- ref_replacements[aliasname] = k
289
- end
290
- if bibref = v.delete("=")
291
- anchor_to_bibref[k] = bibref
292
- end
293
- end
294
- end
295
- end
296
- end
297
- open_refs = ps[:ref] || { } # consumed
298
-
299
- norm_ref = { }
300
-
301
- # convenience replacement of {{-coap}} with {{I-D.ietf-core-coap}}
302
- # collect normative/informative tagging {{!RFC2119}} {{?RFC4711}}
303
- sechash.each do |k, v|
304
- next if k == "fluff"
305
- v.gsub!(/{{(#{
306
- spacify_re(XSR_PREFIX)
307
- })?(?:([?!])(-)?|(-))([\w._\-]+)(?:=([\w.\/_\-]+))?(#{
308
- XREF_TXT_SUFFIX
309
- })?(#{
310
- spacify_re(XSR_SUFFIX)
311
- })?}}/) do |match|
312
- xsr_prefix = $1
313
- norminform = $2
314
- replacing = $3 || $4
315
- word = $5
316
- bibref = $6
317
- xrt_suffix = $7
318
- xsr_suffix = $8
319
- if replacing
320
- if new = ref_replacements[word]
321
- word = new
322
- else
323
- warn "*** no alias replacement for {{-#{word}}}"
324
- word = "-#{word}"
325
- end
326
- end # now, word is the anchor
327
- if bibref
328
- if old = anchor_to_bibref[word]
329
- if bibref != old
330
- warn "*** conflicting definitions for xref #{word}: #{old} != #{bibref}"
331
- end
332
- else
333
- anchor_to_bibref[word] = bibref
334
- end
335
- end
336
-
337
- # things can be normative in one place and informative in another -> normative
338
- # collect norm/inform above and assign it by priority here
339
- if norminform
340
- norm_ref[word] ||= norminform == '!' # one normative ref is enough
341
- end
342
- "{{#{xsr_prefix}#{word}#{xrt_suffix}#{xsr_suffix}}}"
343
- end
344
- end
345
-
346
- [:normative, :informative].each do |k|
347
- ps.rest[k.to_s] ||= { }
348
- end
349
-
350
- norm_ref.each do |k, v|
351
- # could check bibtagsys here: needed if open_refs is nil or string
352
- target = ps.has(v ? :normative : :informative)
353
- warn "*** overwriting #{k}" if target.has_key?(k)
354
- target[k] = open_refs[k] # add reference to normative/informative
355
- end
356
- # note that unused items from ref are considered OK, therefore no check for that here
357
-
358
- # also should allow norm/inform check of other references
359
- # {{?coap}} vs. {{!coap}} vs. {{-coap}} (undecided)
360
- # or {{?-coap}} vs. {{!-coap}} vs. {{-coap}} (undecided)
361
- # could require all references to be decided by a global flag
362
- overlap = [:normative, :informative].map { |s| (ps.has(s) || { }).keys }.reduce(:&)
363
- unless overlap.empty?
364
- warn "*** #{overlap.join(', ')}: both normative and informative"
365
- end
366
-
367
- stand_alone = ps[:stand_alone]
368
-
369
- [:normative, :informative].each do |sn|
370
- if refs = ps[sn]
371
- refs.each do |k, v|
372
- href = ::Kramdown::Parser::RFC2629Kramdown.idref_cleanup(k)
373
- kramdown_options[:link_defs][k] = ["##{href}", nil] # allow [RFC2119] in addition to {{RFC2119}}
374
-
375
- bibref = anchor_to_bibref[k] || k
376
- bts, url = bibtagsys(bibref, k, stand_alone)
377
- if bts && (!v || v == {} || v.respond_to?(:to_str))
378
- if stand_alone
379
- a = %{{: anchor="#{k}"}}
380
- sechash[sn.to_s] << %{\n#{NMDTAGS[0]}\n![:include:](#{bts})#{a}\n#{NMDTAGS[1]}\n}
381
- else
382
- bts.gsub!('/', '_')
383
- (ps.rest["bibxml"] ||= []) << [bts, url]
384
- sechash[sn.to_s] << %{&#{bts};\n} # ???
385
- end
386
- else
387
- unless v && Hash === v
388
- warn "*** don't know how to expand ref #{k}"
389
- next
390
- end
391
- if bts && !v.delete("override")
392
- warn "*** warning: explicit settings completely override canned bibxml in reference #{k}"
393
- end
394
- sechash[sn.to_s] << KramdownRFC::ref_to_xml(href, v)
395
- end
396
- end
397
- end
398
- end
399
-
400
- erbfilename = File.expand_path '../../data/kramdown-rfc2629.erb', __FILE__
401
- erbfile = File.read(erbfilename, coding: "UTF-8")
402
- erb = ERB.trim_new(erbfile, '-')
403
- # remove redundant nomarkdown pop outs/pop ins as they confuse kramdown
404
- input = erb.result(binding).gsub(%r"{::nomarkdown}\s*{:/nomarkdown}"m, "")
405
- ps.warn_if_leftovers
406
- sechash.delete("fluff") # fluff is a "commented out" section
407
- if !sechash.empty? # any sections unused by the ERb file?
408
- warn "*** sections left #{sechash.keys.inspect}!"
409
- end
410
-
411
- [input, kramdown_options, coding_override]
412
- end
413
-
414
- XML_RESOURCE_ORG_PREFIX = Kramdown::Converter::Rfc2629::XML_RESOURCE_ORG_PREFIX
415
-
416
- # return XML entity name, url, rewrite_anchor flag
417
- def bibtagsys(bib, anchor=nil, stand_alone=true)
418
- if bib =~ /\Arfc(\d+)/i
419
- rfc4d = "%04d" % $1.to_i
420
- [bib.upcase,
421
- "#{XML_RESOURCE_ORG_PREFIX}/bibxml/reference.RFC.#{rfc4d}.xml"]
422
- elsif $options.v3 && bib =~ /\A(bcp|std)(\d+)/i
423
- n4d = "%04d" % $2.to_i
424
- [bib.upcase,
425
- "#{XML_RESOURCE_ORG_PREFIX}/bibxml-rfcsubseries-new/reference.#{$1.upcase}.#{n4d}.xml"]
426
- elsif bib =~ /\A([-A-Z0-9]+)\./ &&
427
- (xro = Kramdown::Converter::Rfc2629::XML_RESOURCE_ORG_MAP[$1])
428
- dir, _ttl, rewrite_anchor = xro
429
- bib1 = ::Kramdown::Parser::RFC2629Kramdown.idref_cleanup(bib)
430
- if anchor && bib1 != anchor
431
- if rewrite_anchor
432
- a = %{?anchor=#{anchor}}
433
- else
434
- if !stand_alone
435
- warn "*** selecting a custom anchor '#{anchor}' for '#{bib1}' requires stand_alone mode"
436
- warn " the output will need manual editing to correct this"
437
- end
438
- end
439
- end
440
- [bib1,
441
- "#{XML_RESOURCE_ORG_PREFIX}/#{dir}/reference.#{bib}.xml#{a}"]
442
- end
443
- end
444
-
445
- def read_encodings
446
- encfilename = File.expand_path '../../data/encoding-fallbacks.txt', __FILE__
447
- encfile = File.read(encfilename, coding: "UTF-8")
448
- Hash[encfile.lines.map{|l|
449
- l.chomp!;
450
- x, s = l.split(" ", 2)
451
- [x.hex.chr(Encoding::UTF_8), s || " "]}]
452
- end
453
-
454
- FALLBACK = read_encodings
455
-
456
- def expand_tabs(s, tab_stops = 8)
457
- s.gsub(/([^\t\n]*)\t/) do
458
- $1 + " " * (tab_stops - ($1.size % tab_stops))
459
- end
460
- end
461
-
462
-
463
- require 'optparse'
464
- require 'ostruct'
465
-
466
- $options = OpenStruct.new
467
- op = OptionParser.new do |opts|
468
- opts.banner = <<BANNER
469
- Usage: kramdown-rfc2629 [options] file.md|file.mkd > file.xml
470
- Version: #{KDRFC_VERSION}
471
- BANNER
472
- opts.on("-V", "--version", "Show version and exit") do |v|
473
- puts "kramdown-rfc2629 #{KDRFC_VERSION}"
474
- exit
475
- end
476
- opts.on("-H", "--help", "Show option summary and exit") do |v|
477
- puts opts
478
- exit
479
- end
480
- opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
481
- $options.verbose = v
482
- end
483
- opts.on("-3", "--[no-]v3", "Use RFCXML v3 processing rules") do |v|
484
- $options.v3 = v
485
- end
486
- end
487
- op.parse!
488
-
489
- if $options.verbose && $options.v3
490
- warn "*** not much RFCXMLv3 stuff implemented yet"
491
- end
492
-
493
- input = ARGF.read
494
- if input[0] == "\uFEFF"
495
- warn "*** There is a leading byte order mark. Ignored."
496
- input[0..0] = ''
497
- end
498
- if input[-1] != "\n"
499
- # warn "*** added missing newline at end"
500
- input << "\n" # fix #26
501
- end
502
- process_includes(input) unless ENV["KRAMDOWN_SAFE"]
503
- input.gsub!(/^\{::boilerplate\s+(.*?)\}/) {
504
- boilerplate($1)
505
- }
506
- if input =~ /[\t]/
507
- warn "*** Input contains HT (\"tab\") characters. Undefined behavior will ensue."
508
- input = expand_tabs(input)
509
- end
510
-
511
- if input =~ /\A---/ # this is a sectionized file
512
- do_the_tls_dance unless ENV["KRAMDOWN_DONT_VERIFY_HTTPS"]
513
- input, options, coding_override = xml_from_sections(input)
514
- else
515
- options = process_kramdown_options # all default
516
- end
517
- if input =~ /\A<\?xml/ # if this is a whole XML file, protect it
518
- input = "{::nomarkdown}\n#{input}\n{:/nomarkdown}\n"
519
- end
520
-
521
- if coding_override
522
- input = input.encode(Encoding.find(coding_override), fallback: FALLBACK)
523
- end
524
-
525
- # 1.4.17: because of UTF-8 bibxml files, kramdown always needs to see UTF-8 (!)
526
- if input.encoding != Encoding::UTF_8
527
- input = input.encode(Encoding::UTF_8)
528
- end
529
-
530
- # warn "options: #{options.inspect}"
531
- doc = Kramdown::Document.new(input, options)
532
- $stderr.puts doc.warnings.to_yaml unless doc.warnings.empty?
533
- output = doc.to_rfc2629
534
-
535
- if coding_override
536
- output = output.encode(Encoding.find(coding_override), fallback: FALLBACK)
537
- end
538
-
539
- puts output
@@ -169,6 +169,10 @@
169
169
 
170
170
  <back>
171
171
 
172
+ <% displayref.each do |k, v| -%>
173
+ <displayreference target="<%=k%>" to="<%=v%>"/>
174
+ <% end -%>
175
+
172
176
  <% if sh = sechash.delete("normative") -%>
173
177
  <references title='Normative References'>
174
178
 
@@ -1,16 +1,16 @@
1
1
  spec = Gem::Specification.new do |s|
2
2
  s.name = 'kramdown-rfc2629'
3
- s.version = '1.5.26'
4
- s.summary = "Kramdown extension for generating RFC 7749 XML."
5
- s.description = %{An RFC7749 (XML2RFC) generating backend for Thomas Leitner's
3
+ s.version = '1.6.3'
4
+ s.summary = "Kramdown extension for generating RFCXML (RFC 799x)."
5
+ s.description = %{An RFCXML (RFC 799x) generating backend for Thomas Leitner's
6
6
  "kramdown" markdown parser. Mostly useful for RFC writers.}
7
7
  s.add_dependency('kramdown', '~> 2.3.0')
8
8
  s.add_dependency('kramdown-parser-gfm', '~> 1.1')
9
9
  s.add_dependency('certified', '~> 1.0')
10
10
  s.add_dependency('json_pure', '~> 2.0')
11
- s.files = Dir['lib/**/*.rb'] + %w(README.md LICENSE kramdown-rfc2629.gemspec bin/kdrfc bin/kramdown-rfc2629 bin/doilit bin/kramdown-rfc-extract-markdown data/kramdown-rfc2629.erb data/encoding-fallbacks.txt data/math.json bin/kramdown-rfc-cache-subseries-bibxml bin/de-gfm)
11
+ s.files = Dir['lib/**/*.rb'] + %w(README.md LICENSE kramdown-rfc2629.gemspec bin/kdrfc bin/kramdown-rfc bin/kramdown-rfc2629 bin/doilit bin/kramdown-rfc-extract-markdown data/kramdown-rfc2629.erb data/encoding-fallbacks.txt data/math.json bin/kramdown-rfc-cache-subseries-bibxml bin/de-gfm)
12
12
  s.require_path = 'lib'
13
- s.executables = ['kramdown-rfc2629', 'doilit', 'kramdown-rfc-extract-markdown',
13
+ s.executables = ['kramdown-rfc', 'kramdown-rfc2629', 'doilit', 'kramdown-rfc-extract-markdown',
14
14
  'kdrfc', 'kramdown-rfc-cache-i-d-bibxml',
15
15
  'kramdown-rfc-cache-subseries-bibxml', 'de-gfm']
16
16
  s.required_ruby_version = '>= 2.3.0'