raindown 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/raindown/cli.rb +43 -1
- data/lib/raindown/directives.rb +2 -2
- data/lib/raindown/node_blocks.rb +7 -6
- data/lib/raindown/toc.rb +10 -5
- data/lib/raindown/watcher.rb +20 -6
- data/lib/raindown.rb +0 -3
- data/lib/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 17711b42b7bfd45766f0ad743b58035648b99394031bde5a420e92f01918f8a3
|
|
4
|
+
data.tar.gz: 94753448785a4825b3e2745d0690db75d69b052d57c3ca2e185793b427fedbef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: be61212919255c9018253757f33d02e6cae3ec7811276791fe06bfbdaad748ffe4f47abeac8aad807d5862a7b2bcb493a2d53bebfd30f9abcb820c7faffaf438
|
|
7
|
+
data.tar.gz: 50488661cf504f7fa1fc9963a9aff7a6ebefb755ba46e2b0e63413133ee4e5781e9d9b94f0ba7c3965d7759b453d95ea7254713cf5e5dce73df2e6299de89b71
|
data/lib/raindown/cli.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'fileutils'
|
|
3
4
|
require 'trees'
|
|
4
5
|
require_relative '../raindown'
|
|
6
|
+
require_relative '../version'
|
|
5
7
|
require_relative 'rewriter'
|
|
6
8
|
require_relative 'watcher'
|
|
7
9
|
|
|
@@ -11,9 +13,13 @@ module Raindown
|
|
|
11
13
|
|
|
12
14
|
USAGE = <<~TEXT
|
|
13
15
|
Usage: raindown FILE [options]
|
|
16
|
+
raindown init [DIRECTORY]
|
|
14
17
|
|
|
15
18
|
Renders FILE to HTML, then re-renders it every time it is saved.
|
|
16
19
|
|
|
20
|
+
Commands:
|
|
21
|
+
init [DIRECTORY] Create a .raindown folder for the project's config and nodes (default: current directory)
|
|
22
|
+
|
|
17
23
|
Options:
|
|
18
24
|
-o, --output PATH Write output to PATH instead of FILE.html beside FILE
|
|
19
25
|
--once Render once without watching, printing to stdout unless -o is given
|
|
@@ -22,8 +28,21 @@ module Raindown
|
|
|
22
28
|
--bare Allow bare directives like :toc in Markdown files
|
|
23
29
|
-x, --execute Run endpoint curl snippets that have no output yet
|
|
24
30
|
-h, --help Show this message
|
|
31
|
+
-v, --version Show the version
|
|
32
|
+
|
|
33
|
+
Set RAINDOWN_DEBUG=1 to show where an error happened.
|
|
25
34
|
TEXT
|
|
26
35
|
|
|
36
|
+
CONFIG = <<~YAML
|
|
37
|
+
# Raindown project settings. See https://raindown.dev/docs/config
|
|
38
|
+
#
|
|
39
|
+
# Show directives as HTML comments rather than in bold:
|
|
40
|
+
# hide_directives: true
|
|
41
|
+
#
|
|
42
|
+
# Leave code blocks unhighlighted, so the browser can highlight them instead:
|
|
43
|
+
# highlight_code: false
|
|
44
|
+
YAML
|
|
45
|
+
|
|
27
46
|
OPTIONS = { output: ['-o :output', '--output :output'], bare: ['--bare'], execute: ['-x', '--execute'],
|
|
28
47
|
once: ['--once'], stdin: ['--stdin'] }.freeze
|
|
29
48
|
|
|
@@ -50,11 +69,34 @@ module Raindown
|
|
|
50
69
|
end
|
|
51
70
|
end
|
|
52
71
|
|
|
53
|
-
# The :path line would match
|
|
72
|
+
# The :path line would match help, version and init as the file, so they're checked first.
|
|
54
73
|
def self.run(argv)
|
|
55
74
|
abort(USAGE) if argv.empty? || argv.intersect?(%w[-h --help])
|
|
75
|
+
return puts(VERSION) if argv.intersect?(%w[-v --version])
|
|
76
|
+
return init(argv[1]) if argv.first == 'init' && argv.size <= 2
|
|
56
77
|
|
|
57
78
|
super || abort("Unknown arguments: #{argv.join(' ')}\n\n#{USAGE}")
|
|
79
|
+
rescue StandardError => e
|
|
80
|
+
report(e, path: argv.first)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Shows an error in a file as one line, rather than a backtrace, unless RAINDOWN_DEBUG is set.
|
|
84
|
+
def self.report(error, path:)
|
|
85
|
+
raise error if ENV['RAINDOWN_DEBUG']
|
|
86
|
+
|
|
87
|
+
abort("raindown: #{path}: #{error.message}")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Creates the .raindown folder that marks a project's root and holds its config.yml and nodes.
|
|
91
|
+
def self.init(directory = nil)
|
|
92
|
+
folder = File.join(directory || Dir.pwd, Nodes::FOLDER)
|
|
93
|
+
config = File.join(folder, Config::FILE)
|
|
94
|
+
return puts("#{config} already exists") if File.exist?(config)
|
|
95
|
+
|
|
96
|
+
FileUtils.mkdir_p(folder)
|
|
97
|
+
File.write(config, CONFIG)
|
|
98
|
+
puts "Created #{config}"
|
|
99
|
+
puts 'Put your own nodes in the same folder. See https://raindown.dev/docs/nodes'
|
|
58
100
|
end
|
|
59
101
|
|
|
60
102
|
class << self
|
data/lib/raindown/directives.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require '
|
|
3
|
+
require 'cgi/escape'
|
|
4
4
|
|
|
5
5
|
module Raindown
|
|
6
6
|
# Content directives, like a node's directive and end tag or an endpoint's, are kept in the HTML:
|
|
@@ -22,7 +22,7 @@ module Raindown
|
|
|
22
22
|
def render(html, hide: false)
|
|
23
23
|
html.gsub(MARKER) do
|
|
24
24
|
text = ::Regexp.last_match(1)
|
|
25
|
-
hide ? "<!-- #{text} -->" : "<p><strong>#{
|
|
25
|
+
hide ? "<!-- #{text} -->" : "<p><strong>#{CGI.escapeHTML(text.to_s)}</strong></p>"
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
end
|
data/lib/raindown/node_blocks.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'lowkey'
|
|
4
|
+
|
|
3
5
|
require_relative 'code_blocks'
|
|
4
6
|
require_relative 'directives'
|
|
5
7
|
require_relative 'front_matter'
|
|
@@ -131,13 +133,12 @@ module Raindown
|
|
|
131
133
|
text.scan(PROP).to_h { |name, value| [name.to_sym, value(value, variables)] }
|
|
132
134
|
end
|
|
133
135
|
|
|
136
|
+
# A value, like 'text', 3, true, nil or :draft, or else a variable, like author.name.
|
|
134
137
|
def value(value, variables)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
when 'true', 'false' then value == 'true'
|
|
140
|
-
else Variables.value(variables, value)
|
|
138
|
+
Lowkey::ValueParser.parse(value) do
|
|
139
|
+
next value[1..-2] if value.match?(/\A(["']).*\1\z/)
|
|
140
|
+
|
|
141
|
+
Variables.value(variables, value)
|
|
141
142
|
end
|
|
142
143
|
end
|
|
143
144
|
end
|
data/lib/raindown/toc.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require '
|
|
4
|
-
require 'nokogiri'
|
|
3
|
+
require 'cgi/escape'
|
|
5
4
|
|
|
6
5
|
require_relative 'node'
|
|
7
6
|
|
|
@@ -9,6 +8,12 @@ module Raindown
|
|
|
9
8
|
# The node behind toc: and :toc, a table of contents of the document's headings.
|
|
10
9
|
# A project can replace it with its own TOC node in .raindown, like "class TOC < Raindown::TOC".
|
|
11
10
|
class TOC < Node
|
|
11
|
+
# A heading with an id, in the HTML that Commonmarker and Raindown's components write, like <h2 id="setup">.
|
|
12
|
+
HEADING = %r{<h([1-6])\b[^>]*?\bid=(?:"([^"]*)"|'([^']*)'|([^\s>]+))[^>]*>(.*?)</h\1\s*>}mi
|
|
13
|
+
# Comments and scripts can hold heading tags that aren't in the page.
|
|
14
|
+
IGNORED = %r{<!--.*?-->|<script\b.*?</script\s*>}mi
|
|
15
|
+
TAG = /<[^>]*>/
|
|
16
|
+
|
|
12
17
|
def replace(document:, metadata: nil, bare: false)
|
|
13
18
|
headings = headings(markdown: document, metadata:, bare:)
|
|
14
19
|
return '' if headings.empty?
|
|
@@ -23,7 +28,7 @@ module Raindown
|
|
|
23
28
|
# With the document's final HTML, the headings include ones its components render.
|
|
24
29
|
def render(document:, html: nil, metadata: nil, bare: false)
|
|
25
30
|
items = headings(markdown: document, html:, metadata:, bare:).map do |heading|
|
|
26
|
-
"<li class='h#{heading[:level]}'><a href='##{heading[:id]}'>#{
|
|
31
|
+
"<li class='h#{heading[:level]}'><a href='##{heading[:id]}'>#{CGI.escapeHTML(heading[:text])}</a></li>"
|
|
27
32
|
end
|
|
28
33
|
|
|
29
34
|
<<~HTML
|
|
@@ -42,8 +47,8 @@ module Raindown
|
|
|
42
47
|
def headings(markdown:, html: nil, metadata: nil, bare: false)
|
|
43
48
|
html ||= Fragments.collect { Raindown.assembled(markdown:, metadata: metadata || NoMetadata, bare:) }
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
{ level:
|
|
50
|
+
html.gsub(IGNORED, '').scan(HEADING).map do |level, *ids, content|
|
|
51
|
+
{ level: level.to_i, text: CGI.unescapeHTML(content.gsub(TAG, '')).strip, id: CGI.unescapeHTML(ids.compact.first) }
|
|
47
52
|
end
|
|
48
53
|
end
|
|
49
54
|
end
|
data/lib/raindown/watcher.rb
CHANGED
|
@@ -19,8 +19,10 @@ module Raindown
|
|
|
19
19
|
|
|
20
20
|
$stdout.sync = true
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
safely do
|
|
23
|
+
rewrite
|
|
24
|
+
render
|
|
25
|
+
end
|
|
24
26
|
puts "Watching #{path} for changes (writing to #{output})..."
|
|
25
27
|
|
|
26
28
|
listener.start
|
|
@@ -35,14 +37,26 @@ module Raindown
|
|
|
35
37
|
|
|
36
38
|
Listen.to(directory) do |modified, added, _removed|
|
|
37
39
|
next unless (modified + added).any? { |f| File.basename(f) == filename }
|
|
38
|
-
# Formatting saves the file again, and that change triggers the render.
|
|
39
|
-
next if rewrite
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
safely do
|
|
42
|
+
# Formatting saves the file again, and that change triggers the render.
|
|
43
|
+
next if rewrite
|
|
44
|
+
|
|
45
|
+
render
|
|
46
|
+
puts "Re-rendered #{path} -> #{output}"
|
|
47
|
+
end
|
|
43
48
|
end
|
|
44
49
|
end
|
|
45
50
|
|
|
51
|
+
# An error in the file is shown rather than stopping the watcher, so it can be fixed and saved again.
|
|
52
|
+
def safely
|
|
53
|
+
yield
|
|
54
|
+
rescue StandardError => e
|
|
55
|
+
raise if ENV['RAINDOWN_DEBUG']
|
|
56
|
+
|
|
57
|
+
warn "raindown: #{path}: #{e.message}"
|
|
58
|
+
end
|
|
59
|
+
|
|
46
60
|
# The files' metadata is loaded again each time, so new and renamed files are listed.
|
|
47
61
|
def rewrite
|
|
48
62
|
Rewriter.update(path:, bare:, execute:, metadata: Pages.for(path))
|
data/lib/raindown.rb
CHANGED
|
@@ -35,9 +35,6 @@ module Raindown
|
|
|
35
35
|
def self.list(**) = []
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
# Headings with an id, which are the ones a table of contents links to.
|
|
39
|
-
HEADINGS = 'h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]'
|
|
40
|
-
|
|
41
38
|
module_function
|
|
42
39
|
|
|
43
40
|
# Directives show in bold and code is highlighted, unless the file's project changes them in .raindown/config.yml.
|
data/lib/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: raindown
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-10-
|
|
10
|
+
date: 2026-10-25 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: antlers
|
|
@@ -80,21 +80,21 @@ dependencies:
|
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '0'
|
|
82
82
|
- !ruby/object:Gem::Dependency
|
|
83
|
-
name:
|
|
83
|
+
name: lowkey
|
|
84
84
|
requirement: !ruby/object:Gem::Requirement
|
|
85
85
|
requirements:
|
|
86
86
|
- - ">="
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
|
-
version:
|
|
88
|
+
version: 0.7.0
|
|
89
89
|
type: :runtime
|
|
90
90
|
prerelease: false
|
|
91
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
92
92
|
requirements:
|
|
93
93
|
- - ">="
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version:
|
|
95
|
+
version: 0.7.0
|
|
96
96
|
- !ruby/object:Gem::Dependency
|
|
97
|
-
name:
|
|
97
|
+
name: lownode
|
|
98
98
|
requirement: !ruby/object:Gem::Requirement
|
|
99
99
|
requirements:
|
|
100
100
|
- - ">="
|