rakit 0.1.11 → 0.1.13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 673ff3ffd5ae281ae18e1eb3675c36790a1223af18a289147368cda18b36e951
4
- data.tar.gz: 57f3ce6802fa881c7394854cb6f5a74a6abb7127d16f6e33b543ae17dcbac24b
3
+ metadata.gz: 56d5f36e49db59bf849eecc571fbc977281b15612243b0e7641e18a6fd0aad27
4
+ data.tar.gz: 409a9630c30abd09b0631b1f7996a14735d857d799059d9c40e9632e705d46d9
5
5
  SHA512:
6
- metadata.gz: 459d835233b5cacd0ed901c6d0bc260be06615e8cc590e33b56f7b126728ce17495231fa6884d6f0c3c6ad4a9ae0bdabf781e1ff494af20eca34ce5b8566fcb6
7
- data.tar.gz: '06004494e61093017a0c621fac688b173c8bbff2c5fd964671c1d12c702df16cab081bcacc2f5cf55d26b4c0b0861445f2bb841fdf9129b9403a81213e75477f'
6
+ metadata.gz: 6106280a152b3122ec50761f61ca6c7ba5c7288e4e7206f2335485a4ef67c04d8b4da9b322316bb458d61a4f34f2571fc3216e0aa7bf2a4bf8fe5783929a79dd
7
+ data.tar.gz: f0c333b6869b367cc126811fc7b440eefc57059d8bb94371345d663cfee0454d7feac0790477c6755466cee27a7d041bfbeed9c82f34cd7866d5f77d01d2193f
data/exe/rakit CHANGED
@@ -19,6 +19,7 @@ def usage_stderr(msg = nil)
19
19
  $stderr.puts " markdown amalgamate <root_dir> --out <output_path>"
20
20
  $stderr.puts " markdown disaggregate <markdown_path> --out <output_root_dir>"
21
21
  $stderr.puts " markdown validate <root_dir>"
22
+ $stderr.puts " markdown toc|tree|outline [--root <dir>|--document-json <path>|--outline-json <path>] [--max-depth N] [options]"
22
23
  $stderr.puts " static-web-server <start|stop|running|publish|view> [options] [args]"
23
24
  $stderr.puts " word-cloud <status|install|generate> [options] [args]"
24
25
  $stderr.puts " start [--port PORT] Start server (idempotent)"
@@ -120,8 +121,17 @@ def main(argv = ARGV)
120
121
  return 1
121
122
  end
122
123
  return 0
124
+ when "toc"
125
+ code = run_markdown_renderer(argv, :toc)
126
+ return code
127
+ when "tree"
128
+ code = run_markdown_renderer(argv, :tree)
129
+ return code
130
+ when "outline"
131
+ code = run_markdown_renderer(argv, :outline)
132
+ return code
123
133
  else
124
- usage_stderr(cmd ? "markdown: use amalgamate, disaggregate, validate, merge, or pdf" : "markdown requires a command")
134
+ usage_stderr(cmd ? "markdown: use amalgamate, disaggregate, validate, merge, pdf, toc, tree, or outline" : "markdown requires a command")
125
135
  return 1
126
136
  end
127
137
  rescue ArgumentError, Errno::ENOENT, Errno::EACCES => e
@@ -190,4 +200,44 @@ rescue => e
190
200
  return 1
191
201
  end
192
202
 
203
+ def run_markdown_renderer(argv, command)
204
+ opts = Rakit::Markdown.parse_renderer_args(argv)
205
+ type, path = Rakit::Markdown.resolve_renderer_input(opts)
206
+ unless type
207
+ usage_stderr("markdown #{command} requires one of: --root <dir>, --document-json <path>, --outline-json <path>")
208
+ return Rakit::Markdown::EXIT_INVALID_INPUT
209
+ end
210
+ outline, code = Rakit::Markdown.load_outline_from_options(opts)
211
+ if code
212
+ return code
213
+ end
214
+ max_depth = opts[:max_depth]
215
+ case command
216
+ when :toc
217
+ no_links = argv.include?("--no-links")
218
+ href_mode = :file_anchor
219
+ href_mode = :anchor if argv.include?("--href-mode") && argv[argv.index("--href-mode") + 1].to_s == "anchor"
220
+ href_mode = :none if argv.include?("--href-mode") && argv[argv.index("--href-mode") + 1].to_s == "none"
221
+ site_base = (argv.include?("--site-base") && argv[argv.index("--site-base") + 1]) ? argv[argv.index("--site-base") + 1] : nil
222
+ show_ids = argv.include?("--show-ids")
223
+ out = Rakit::Markdown.render_toc(outline, max_depth: max_depth, no_links: no_links, href_mode: href_mode, site_base: site_base, show_ids: show_ids)
224
+ $stdout.write(out)
225
+ when :tree
226
+ show_ids = argv.include?("--show-ids")
227
+ show_hrefs = argv.include?("--show-hrefs")
228
+ ascii = argv.include?("--ascii")
229
+ out = Rakit::Markdown.render_tree(outline, max_depth: max_depth, show_ids: show_ids, show_hrefs: show_hrefs, ascii: ascii)
230
+ $stdout.write(out)
231
+ when :outline
232
+ show_ids = argv.include?("--show-ids")
233
+ no_numbers = argv.include?("--no-numbers")
234
+ out = Rakit::Markdown.render_outline(outline, max_depth: max_depth, show_ids: show_ids, no_numbers: no_numbers)
235
+ $stdout.write(out)
236
+ end
237
+ Rakit::Markdown::EXIT_SUCCESS
238
+ rescue => e
239
+ $stderr.puts e.message
240
+ Rakit::Markdown::EXIT_INTERNAL
241
+ end
242
+
193
243
  exit main(ARGV.dup)
@@ -2,8 +2,7 @@
2
2
  # Generated by the protocol buffer compiler. DO NOT EDIT!
3
3
  # source: data.proto
4
4
 
5
- require 'google/protobuf'
6
-
5
+ require "google/protobuf"
7
6
 
8
7
  descriptor_data = "\n\ndata.proto\x12\nrakit.data\"h\n\x05Index\x12/\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x1e.rakit.data.Index.EntriesEntry\x1a.\n\x0c\x45ntriesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01*^\n\x0c\x45xportFormat\x12\x19\n\x15PROTOBUF_BINARY_FILES\x10\x00\x12\x17\n\x13PROTOBUF_JSON_FILES\x10\x01\x12\x1a\n\x16PROTOBUF_BINARY_ZIPPED\x10\x02\x42\x0e\xea\x02\x0bRakit::Datab\x06proto3"
9
8
 
@@ -7,9 +7,30 @@ require "kramdown"
7
7
 
8
8
  module Rakit
9
9
  # Modular document amalgamate, disaggregate, validate (009); merge (legacy); generate_pdf.
10
+ # Outline/toc/tree/outline renderers (010): see specs/010-markdown-outline.
10
11
  # Document and Section are defined in proto/rakit.markdown.proto (generated in lib/generated/rakit.markdown_pb.rb).
11
12
  # See specs/009-markdown-modular-docs/contracts/ruby-api.md
12
13
  module Markdown
14
+ # Exit codes for renderer CLI (toc, tree, outline). 0=success, 2=invalid input, 3=validation, 4=internal.
15
+ EXIT_SUCCESS = 0
16
+ EXIT_INVALID_INPUT = 2
17
+ EXIT_VALIDATION = 3
18
+ EXIT_INTERNAL = 4
19
+
20
+ # In-memory outline for rendering (010). title: optional root title; nodes: top-level OutlineNode list.
21
+ Outline = Struct.new(:title, :nodes, keyword_init: true) do
22
+ def initialize(title: nil, nodes: nil)
23
+ super(title: title.to_s.strip, nodes: nodes || [])
24
+ end
25
+ end
26
+
27
+ # Single node in the outline tree. id/href may be set by normalization.
28
+ OutlineNode = Struct.new(:title, :id, :href, :source_path, :children, keyword_init: true) do
29
+ def initialize(title: "", id: nil, href: nil, source_path: nil, children: nil)
30
+ super(title: title.to_s, id: id, href: href, source_path: source_path, children: children || [])
31
+ end
32
+ end
33
+
13
34
  # Derive slug from section title: lowercase, non-alphanumerics → '-', collapse '-', trim.
14
35
  def self.slug_from_title(title)
15
36
  return "" if title.nil? || title.to_s.strip.empty?
@@ -148,6 +169,302 @@ module Rakit
148
169
  false
149
170
  end
150
171
 
172
+ # ---- Outline (010): derivation, normalization, validation ----
173
+
174
+ # Deterministic slug for outline node id: trim, lower-case, NFKD (when available), strip diacritics, non-[a-z0-9]→-, trim -, empty→"section".
175
+ def self.slug_for_outline(title)
176
+ return "section" if title.nil? || title.to_s.strip.empty?
177
+ s = title.to_s.strip.downcase
178
+ s = s.unicode_normalize(:nfkd).gsub(/\p{M}+/, "") if s.respond_to?(:unicode_normalize)
179
+ s = s.gsub(/[^a-z0-9]+/, "-").gsub(/\A-+|-+\z/, "")
180
+ s.empty? ? "section" : s
181
+ end
182
+
183
+ # Build Outline from Document (map Section → OutlineNode recursively). Does not normalize.
184
+ def self.document_to_outline(doc)
185
+ return Outline.new(title: doc.title, nodes: []) if doc.nil?
186
+ nodes = (doc.sections || []).map { |sec| section_to_outline_node(sec) }
187
+ Outline.new(title: doc.title.to_s.strip, nodes: nodes)
188
+ end
189
+
190
+ def self.section_to_outline_node(sec)
191
+ children = (sec.sections || []).map { |s| section_to_outline_node(s) }
192
+ OutlineNode.new(
193
+ title: sec.title.to_s,
194
+ id: sec.id.to_s.strip.empty? ? nil : sec.id.to_s,
195
+ source_path: sec.source_path.to_s.strip.empty? ? nil : sec.source_path.to_s,
196
+ children: children
197
+ )
198
+ end
199
+
200
+ # Apply slug (when id blank), sibling dedupe, and href. Modifies outline in place. dedupe: true = suffix -2,-3 and warn instead of validation error.
201
+ def self.normalize_outline!(outline, href_mode: :file_anchor, site_base: nil, dedupe: false)
202
+ outline.nodes.each { |n| normalize_node!(n, href_mode: href_mode, site_base: site_base, dedupe: dedupe) }
203
+ dedupe_sibling_ids!(outline.nodes, dedupe: dedupe)
204
+ outline.nodes.each { |n| normalize_node_href!(n, href_mode: href_mode, site_base: site_base) }
205
+ outline
206
+ end
207
+
208
+ def self.normalize_node!(node, href_mode: :file_anchor, site_base: nil, dedupe: false)
209
+ node.children.each { |c| normalize_node!(c, href_mode: href_mode, site_base: site_base, dedupe: dedupe) }
210
+ node.id = node.id.to_s.strip if node.id
211
+ node.id = nil if node.id.to_s.empty?
212
+ node.id = slug_for_outline(node.title) if node.id.to_s.empty?
213
+ dedupe_sibling_ids!(node.children, dedupe: dedupe)
214
+ end
215
+
216
+ def self.normalize_node_href!(node, href_mode: :file_anchor, site_base: nil)
217
+ node.href = compute_href(node, href_mode: href_mode, site_base: site_base) if node.href.to_s.strip.empty?
218
+ node.children.each { |c| normalize_node_href!(c, href_mode: href_mode, site_base: site_base) }
219
+ end
220
+
221
+ def self.dedupe_sibling_ids!(siblings, dedupe: false)
222
+ seen = {}
223
+ siblings.each do |n|
224
+ id = n.id.to_s
225
+ if seen.key?(id)
226
+ if dedupe
227
+ idx = 2
228
+ idx += 1 while seen.key?("#{id}-#{idx}")
229
+ n.id = "#{id}-#{idx}"
230
+ seen[n.id] = true
231
+ end
232
+ else
233
+ seen[id] = true
234
+ end
235
+ end
236
+ end
237
+
238
+ def self.compute_href(node, href_mode: :file_anchor, site_base: nil)
239
+ id = node.id.to_s
240
+ case href_mode
241
+ when :anchor then "##{id}"
242
+ when :none then ""
243
+ else
244
+ path = node.source_path.to_s.strip
245
+ if site_base.to_s.strip.empty?
246
+ path.empty? ? "##{id}" : "#{path}##{id}"
247
+ else
248
+ base = site_base.to_s.chomp("/")
249
+ path_no_ext = path.sub(/\.md\z/i, "").tr("\\", "/")
250
+ path_no_ext.empty? ? "##{id}" : "#{base}/#{path_no_ext}/##{id}"
251
+ end
252
+ end
253
+ end
254
+
255
+ # Validate outline: empty title, duplicate explicit id among siblings, max_depth≤0. Returns [errors, warnings]. Zero nodes is valid.
256
+ def self.validate_outline(outline, max_depth: nil, dedupe: false, strict_paths: false)
257
+ errors = []
258
+ warnings = []
259
+ if max_depth.is_a?(Integer) && max_depth <= 0
260
+ errors << "max_depth must be >= 1"
261
+ end
262
+ validate_outline_nodes!(outline.nodes, 1, errors, warnings, dedupe: dedupe, strict_paths: strict_paths)
263
+ [errors, warnings]
264
+ end
265
+
266
+ def self.validate_outline_nodes!(nodes, depth, errors, warnings, dedupe: false, strict_paths: false)
267
+ nodes.each do |n|
268
+ if n.title.to_s.strip.empty?
269
+ errors << "Node has empty title"
270
+ end
271
+ if strict_paths && n.source_path.to_s.strip != "" && (n.source_path.include?("..") || ::File.absolute_path?(n.source_path))
272
+ errors << "source_path must be relative: #{n.source_path}"
273
+ end
274
+ validate_outline_nodes!(n.children, depth + 1, errors, warnings, dedupe: dedupe, strict_paths: strict_paths)
275
+ end
276
+ return if dedupe
277
+ ids = nodes.map { |n| n.id.to_s }.reject(&:empty?)
278
+ seen = {}
279
+ ids.each do |id|
280
+ if seen[id]
281
+ errors << "Duplicate id among siblings: #{id}"
282
+ else
283
+ seen[id] = true
284
+ end
285
+ end
286
+ end
287
+
288
+ # Load outline from root dir. If no .md files, returns Outline with empty nodes (valid for renderers).
289
+ def self.outline_from_root(root_dir)
290
+ path = ::File.expand_path(root_dir.to_s)
291
+ raise ArgumentError, "root_dir does not exist: #{root_dir}" unless ::File.exist?(path)
292
+ raise ArgumentError, "root_dir is not a directory: #{root_dir}" unless ::File.directory?(path)
293
+ paths = ::Dir.glob(::File.join(path, "**", "*.md")).reject { |p| ::File.basename(p).start_with?(".") }.sort
294
+ title = ::File.basename(path)
295
+ if paths.empty?
296
+ return Outline.new(title: title, nodes: [])
297
+ end
298
+ doc = load_document(root_dir: root_dir)
299
+ document_to_outline(doc)
300
+ end
301
+
302
+ # Parse argv for renderer commands; returns { root:, document_json:, outline_json:, max_depth:, dedupe:, strict_paths:, ... }. Precedence for input: outline_json > document_json > root.
303
+ def self.parse_renderer_args(argv)
304
+ args = argv.dup
305
+ opts = { root: nil, document_json: nil, outline_json: nil, max_depth: nil, dedupe: false, strict_paths: false }
306
+ while (arg = args.shift)
307
+ case arg
308
+ when "--root" then opts[:root] = args.shift
309
+ when "--document-json" then opts[:document_json] = args.shift
310
+ when "--outline-json" then opts[:outline_json] = args.shift
311
+ when "--max-depth" then opts[:max_depth] = args.shift
312
+ when "--dedupe" then opts[:dedupe] = true
313
+ when "--strict-paths" then opts[:strict_paths] = true
314
+ end
315
+ end
316
+ opts[:max_depth] = opts[:max_depth].to_s =~ /\A\d+\z/ ? opts[:max_depth].to_i : nil
317
+ opts
318
+ end
319
+
320
+ # Resolve which input source from parsed opts (precedence: outline_json > document_json > root). Returns :outline_json, :document_json, or :root and the path value, or [nil, nil] if none.
321
+ def self.resolve_renderer_input(opts)
322
+ return [:outline_json, opts[:outline_json]] if opts[:outline_json].to_s.strip != ""
323
+ return [:document_json, opts[:document_json]] if opts[:document_json].to_s.strip != ""
324
+ return [:root, opts[:root]] if opts[:root].to_s.strip != ""
325
+ [nil, nil]
326
+ end
327
+
328
+ # Load and normalize outline from options. Returns [outline, nil] or [nil, exit_code]. Errors written to stderr via yield or callers responsibility.
329
+ def self.load_outline_from_options(opts, href_mode: :file_anchor, site_base: nil)
330
+ input_type, path = resolve_renderer_input(opts)
331
+ if input_type.nil?
332
+ return [nil, EXIT_INVALID_INPUT]
333
+ end
334
+ path = ::File.expand_path(path.to_s.strip)
335
+ outline = case input_type
336
+ when :root
337
+ outline_from_root(path)
338
+ when :document_json
339
+ json = ::File.read(path, encoding: "UTF-8")
340
+ doc = decode_document_json(json)
341
+ document_to_outline(doc)
342
+ when :outline_json
343
+ json = ::File.read(path, encoding: "UTF-8")
344
+ decode_outline_json(json)
345
+ end
346
+ normalize_outline!(outline, href_mode: href_mode, site_base: site_base, dedupe: opts[:dedupe])
347
+ errs, _warn = validate_outline(outline, max_depth: opts[:max_depth], dedupe: opts[:dedupe], strict_paths: opts[:strict_paths])
348
+ return [nil, EXIT_VALIDATION] if errs.any?
349
+ [outline, nil]
350
+ rescue ArgumentError, Errno::ENOENT, Errno::EACCES => e
351
+ $stderr.puts e.message if $stderr
352
+ [nil, EXIT_INVALID_INPUT]
353
+ rescue ::JSON::ParserError => e
354
+ $stderr.puts e.message if $stderr
355
+ [nil, EXIT_INVALID_INPUT]
356
+ rescue => e
357
+ $stderr.puts e.message if $stderr
358
+ [nil, EXIT_INTERNAL]
359
+ end
360
+
361
+ # Decode Document from JSON (protobuf JSON). Minimal implementation: parse and build Document from hash.
362
+ def self.decode_document_json(json)
363
+ require "json"
364
+ h = ::JSON.parse(json)
365
+ title = h["title"] || h["rootDir"] && ::File.basename(h["rootDir"]) || ""
366
+ root_dir = h["rootDir"].to_s
367
+ sections = (h["sections"] || []).map { |s| decode_section_hash(s) }
368
+ Document.new(title: title, root_dir: root_dir, sections: sections)
369
+ end
370
+
371
+ def self.decode_section_hash(h)
372
+ id = (h["id"] || "").to_s
373
+ title = (h["title"] || "").to_s
374
+ source_path = (h["sourcePath"] || h["source_path"] || "").to_s
375
+ sections = (h["sections"] || []).map { |s| decode_section_hash(s) }
376
+ Section.new(id: id, title: title, body: (h["body"] || "").to_s, source_path: source_path, sections: sections)
377
+ end
378
+
379
+ # Render outline as Markdown TOC (nested bullets, optional links). max_depth: nil = no limit; depth 1 = top-level.
380
+ def self.render_toc(outline, max_depth: nil, no_links: false, href_mode: :file_anchor, site_base: nil, show_ids: false)
381
+ buf = +""
382
+ render_toc_nodes(buf, outline.nodes, 1, max_depth: max_depth, no_links: no_links, show_ids: show_ids, indent: "")
383
+ buf
384
+ end
385
+
386
+ def self.render_toc_nodes(buf, nodes, depth, max_depth: nil, no_links: false, show_ids: false, indent: "")
387
+ return if max_depth && depth > max_depth
388
+ nodes.each do |n|
389
+ line = indent + "- "
390
+ if !no_links && n.href.to_s.strip != ""
391
+ line << "[#{n.title}](#{n.href})"
392
+ else
393
+ line << n.title.to_s
394
+ end
395
+ line << " (id: #{n.id})" if show_ids && n.id.to_s != ""
396
+ line << "\n"
397
+ buf << line
398
+ render_toc_nodes(buf, n.children, depth + 1, max_depth: max_depth, no_links: no_links, show_ids: show_ids, indent: indent + " ")
399
+ end
400
+ end
401
+
402
+ # Render outline as tree (box-drawing or ASCII). Root title printed above when non-empty.
403
+ def self.render_tree(outline, max_depth: nil, show_ids: false, show_hrefs: false, ascii: false)
404
+ buf = +""
405
+ buf << outline.title.to_s << "\n" if outline.title.to_s.strip != ""
406
+ render_tree_nodes(buf, outline.nodes, 1, max_depth: max_depth, show_ids: show_ids, show_hrefs: show_hrefs, ascii: ascii, prefix: "")
407
+ buf
408
+ end
409
+
410
+ def self.render_tree_nodes(buf, nodes, depth, max_depth: nil, show_ids: false, show_hrefs: false, ascii: false, prefix: "")
411
+ return if max_depth && depth > max_depth
412
+ return if nodes.empty?
413
+ n_last = nodes.last
414
+ nodes.each do |n|
415
+ is_last = (n == n_last)
416
+ branch = ascii ? (is_last ? "-- " : "|-- ") : (is_last ? "└─ " : "├─ ")
417
+ line = prefix + branch + n.title.to_s
418
+ line << " (id: #{n.id})" if show_ids && n.id.to_s != ""
419
+ line << " → #{n.href}" if show_hrefs && n.href.to_s != ""
420
+ line << "\n"
421
+ buf << line
422
+ child_prefix = prefix + (ascii ? (is_last ? " " : "| ") : (is_last ? " " : "│ "))
423
+ render_tree_nodes(buf, n.children, depth + 1, max_depth: max_depth, show_ids: show_ids, show_hrefs: show_hrefs, ascii: ascii, prefix: child_prefix)
424
+ end
425
+ end
426
+
427
+ # Render outline as numbered list (1., 1.1., …) or indented text. Indentation 2 spaces per depth.
428
+ def self.render_outline(outline, max_depth: nil, show_ids: false, no_numbers: false)
429
+ buf = +""
430
+ render_outline_nodes(buf, outline.nodes, 1, max_depth: max_depth, show_ids: show_ids, no_numbers: no_numbers, indent: "", numbers: [])
431
+ buf
432
+ end
433
+
434
+ def self.render_outline_nodes(buf, nodes, depth, max_depth: nil, show_ids: false, no_numbers: false, indent: "", numbers: [])
435
+ return if max_depth && depth > max_depth
436
+ nodes.each_with_index do |n, i|
437
+ nums = numbers + [i + 1]
438
+ num_str = no_numbers ? "" : nums.join(".") + ". "
439
+ line = indent + num_str + n.title.to_s
440
+ line << " (id: #{n.id})" if show_ids && n.id.to_s != ""
441
+ line << "\n"
442
+ buf << line
443
+ child_indent = indent + " "
444
+ render_outline_nodes(buf, n.children, depth + 1, max_depth: max_depth, show_ids: show_ids, no_numbers: no_numbers, indent: child_indent, numbers: nums)
445
+ end
446
+ end
447
+
448
+ # Decode Outline from JSON. Format: { "title": "", "nodes": [ { "title", "id", "href", "source_path", "children": [] } ] }.
449
+ def self.decode_outline_json(json)
450
+ require "json"
451
+ h = ::JSON.parse(json)
452
+ title = (h["title"] || "").to_s.strip
453
+ nodes = (h["nodes"] || []).map { |n| decode_outline_node_hash(n) }
454
+ Outline.new(title: title, nodes: nodes)
455
+ end
456
+
457
+ def self.decode_outline_node_hash(h)
458
+ children = (h["children"] || []).map { |c| decode_outline_node_hash(c) }
459
+ OutlineNode.new(
460
+ title: (h["title"] || "").to_s,
461
+ id: (h["id"] || "").to_s.strip.empty? ? nil : (h["id"] || "").to_s,
462
+ href: (h["href"] || "").to_s.strip.empty? ? nil : (h["href"] || "").to_s,
463
+ source_path: (h["sourcePath"] || h["source_path"] || "").to_s.strip.empty? ? nil : (h["sourcePath"] || h["source_path"]).to_s,
464
+ children: children
465
+ )
466
+ end
467
+
151
468
  # ---- Legacy API: merge (concatenate .md files) and generate_pdf ----
152
469
 
153
470
  # Merge: collect all .md under source_dir (sorted by path), write concatenated content to target_path.
data/lib/rakit.rb CHANGED
@@ -21,6 +21,7 @@ end
21
21
  require_relative "rakit/gem"
22
22
  require_relative "rakit/git"
23
23
  require_relative "rakit/task"
24
+ require_relative "rakit/tasks"
24
25
  require_relative "rakit/protobuf"
25
26
  # Defer loading so rake tasks that don't need Shell (e.g. clobber) work without google-protobuf.
26
27
  autoload :Shell, "rakit/shell"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rakit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - rakit
@@ -130,8 +130,6 @@ files:
130
130
  - lib/generated/rakit.shell_pb.rb
131
131
  - lib/generated/rakit.static_web_server_pb.rb
132
132
  - lib/generated/rakit.word_count_pb.rb
133
- - lib/generated/shell_pb.rb
134
- - lib/generated/static_web_server_pb.rb
135
133
  - lib/generated/word_cloud_pb.rb
136
134
  - lib/rakit.rb
137
135
  - lib/rakit/azure/dev_ops.rb
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
- # Generated by the protocol buffer compiler. DO NOT EDIT!
3
- # source: shell.proto
4
-
5
- require 'google/protobuf'
6
-
7
-
8
- descriptor_data = "\n\x0bshell.proto\x12\x0brakit.shell\"\x9a\x02\n\x07\x43ommand\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04\x61rgs\x18\x02 \x03(\t\x12\x19\n\x11working_directory\x18\x03 \x01(\t\x12\x17\n\x0ftimeout_seconds\x18\x04 \x01(\x05\x12\x1a\n\x12\x65xpected_exit_code\x18\x05 \x01(\x05\x12\x17\n\x0f\x65xpected_stdout\x18\x06 \x01(\t\x12\x17\n\x0f\x65xpected_stderr\x18\x07 \x01(\t\x12<\n\x13\x61\x63\x63\x65ptance_criteria\x18\x08 \x03(\x0b\x32\x1f.rakit.shell.AcceptanceCriteria\x12\x13\n\x0b\x65xit_status\x18\t \x01(\x05\x12\x0e\n\x06stdout\x18\n \x01(\t\x12\x0e\n\x06stderr\x18\x0b \x01(\t\"1\n\x12\x41\x63\x63\x65ptanceCriteria\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"-\n\nTestResult\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0e\n\x06\x65rrors\x18\x02 \x03(\t\"b\n\rFormatRequest\x12%\n\x07\x63ommand\x18\x01 \x01(\x0b\x32\x14.rakit.shell.Command\x12*\n\x06\x66ormat\x18\x02 \x01(\x0e\x32\x1a.rakit.shell.CommandFormat\" \n\x0e\x46ormatResponse\x12\x0e\n\x06output\x18\x01 \x01(\t*Z\n\rCommandFormat\x12\x1e\n\x1a\x43OMMAND_FORMAT_UNSPECIFIED\x10\x00\x12\x0c\n\x08ONE_LINE\x10\x01\x12\x0e\n\nMULTI_LINE\x10\x02\x12\x0b\n\x07\x43OMPACT\x10\x03\x32\xc1\x01\n\x0e\x43ommandService\x12\x35\n\x07\x45xecute\x12\x14.rakit.shell.Command\x1a\x14.rakit.shell.Command\x12\x35\n\x04Test\x12\x14.rakit.shell.Command\x1a\x17.rakit.shell.TestResult\x12\x41\n\x06\x46ormat\x12\x1a.rakit.shell.FormatRequest\x1a\x1b.rakit.shell.FormatResponseB\x0f\xea\x02\x0cRakit::Shellb\x06proto3"
9
-
10
- pool = ::Google::Protobuf::DescriptorPool.generated_pool
11
- pool.add_serialized_file(descriptor_data)
12
-
13
- module Rakit
14
- module Shell
15
- Command = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("rakit.shell.Command").msgclass
16
- AcceptanceCriteria = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("rakit.shell.AcceptanceCriteria").msgclass
17
- TestResult = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("rakit.shell.TestResult").msgclass
18
- FormatRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("rakit.shell.FormatRequest").msgclass
19
- FormatResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("rakit.shell.FormatResponse").msgclass
20
- CommandFormat = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("rakit.shell.CommandFormat").enummodule
21
- end
22
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
- # Generated by the protocol buffer compiler. DO NOT EDIT!
3
- # source: static_web_server.proto
4
-
5
- require 'google/protobuf'
6
-
7
-
8
- descriptor_data = "\n\x17static_web_server.proto\x12\x17rakit.static_web_server\"T\n\x15StaticWebServerConfig\x12\x16\n\x0eroot_directory\x18\x01 \x01(\t\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x15\n\rhttps_enabled\x18\x03 \x01(\x08\"=\n\x0ePublishRequest\x12\x11\n\tsite_name\x18\x01 \x01(\t\x12\x18\n\x10source_directory\x18\x02 \x01(\t\"1\n\rPublishResult\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"-\n\x0cServerStatus\x12\x0f\n\x07running\x18\x01 \x01(\x08\x12\x0c\n\x04port\x18\x02 \x01(\rB\x13\xea\x02\x10Rakit::Generatedb\x06proto3"
9
-
10
- pool = ::Google::Protobuf::DescriptorPool.generated_pool
11
- pool.add_serialized_file(descriptor_data)
12
-
13
- module Rakit
14
- module Generated
15
- StaticWebServerConfig = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("rakit.static_web_server.StaticWebServerConfig").msgclass
16
- PublishRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("rakit.static_web_server.PublishRequest").msgclass
17
- PublishResult = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("rakit.static_web_server.PublishResult").msgclass
18
- ServerStatus = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("rakit.static_web_server.ServerStatus").msgclass
19
- end
20
- end