mdtoc 0.1.4 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b54718ab7b2f7bbc8e90ae473318895665986e0a7408674670db03294f12d6e
4
- data.tar.gz: f1b251a78ba55942f8c85d82b99f614d3b9368541b7dcb1f144d1094e293d01f
3
+ metadata.gz: 67fab334c40c8a785d1ac0bcca6234a1fc048ad479ed7fb4df093f0eb14c2116
4
+ data.tar.gz: c80647e458fd2ceaa60189688d0c7a831ad023a20cb471fcc37d512e382db627
5
5
  SHA512:
6
- metadata.gz: 247816469ed9798906c93e6ca0c58bd4b863d379795d9f776b71fc6c7d3835d7eb4ee890372a408ee04981658336a5f924156d85456e66d90793e0d58b7bd9e2
7
- data.tar.gz: 2c10991bfa7f4b95896d468f803bae94b44899fc695c1113bd6fa6d7d90c784f4303939a7c2637ed70cd6335bf51f66688d6c932b03a6c0d284f5060cc009646
6
+ metadata.gz: 2e6e9abb89b30d7abed179b42b00a8d871ab7b5ea6b96a4607ea5284bed3a1e1b2bb3355e0258d84498b78c8dba1463ab170f46e9c4c7963f4931b2efa910483
7
+ data.tar.gz: a3c414d2472f1f2ad976bc02eca284c9b4f522ee21624ad30534d33563242cb2ac04457cf33a4bc1998f2af232f4aa803c2051ee066f910444d6f881c7b69396
data/bin/mdtoc CHANGED
@@ -2,5 +2,5 @@
2
2
  # typed: true
3
3
  # frozen_string_literal: true
4
4
 
5
- require 'mdtoc'
5
+ require "mdtoc"
6
6
  Mdtoc.main(ARGV)
data/lib/mdtoc/cli.rb CHANGED
@@ -1,9 +1,9 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'optparse'
5
- require 'sorbet-runtime'
6
- require 'tempfile'
4
+ require "optparse"
5
+ require "sorbet-runtime"
6
+ require "tempfile"
7
7
 
8
8
  module Mdtoc
9
9
  module CLI
@@ -14,19 +14,19 @@ module Mdtoc
14
14
  def parse(args)
15
15
  parser = OptionParser.new do |parser_|
16
16
  parser_.banner = "Usage: #{parser_.program_name} [options] files or directories..."
17
- parser_.on('-h', '--help', 'Show this message') do
17
+ parser_.on("-h", "--help", "Show this message") do
18
18
  puts parser_
19
19
  exit
20
20
  end
21
- parser_.on('-o', '--output PATH', 'Update a table of contents in the file at PATH')
22
- parser_.on('-a', '--[no-]append', 'Append to the --output file if a <!-- mdtoc --> tag isn\'t found')
23
- parser_.on('-c', '--[no-]create', 'Create the --output file if it does not exist')
21
+ parser_.on("-o", "--output PATH", "Update a table of contents in the file at PATH")
22
+ parser_.on("-a", "--[no-]append", "Append to the --output file if a <!-- mdtoc --> tag isn't found")
23
+ parser_.on("-c", "--[no-]create", "Create the --output file if it does not exist")
24
24
  end
25
25
 
26
26
  options = Options.new
27
27
  options.paths = parser.parse(args, into: options)
28
28
  if options.paths.empty?
29
- warn('Specify at least one file or directory to read')
29
+ warn("Specify at least one file or directory to read")
30
30
  exit(1)
31
31
  end
32
32
  options
@@ -1,7 +1,7 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'sorbet-runtime'
4
+ require "sorbet-runtime"
5
5
 
6
6
  module Mdtoc
7
7
  module Markdown
@@ -13,14 +13,15 @@ module Mdtoc
13
13
  if depth < 0
14
14
  raise ArgumentError, "Header depth must be >= 0, but was #{depth}"
15
15
  end
16
+
16
17
  @depth = depth
17
- @label = label.strip.gsub(/\s+/, ' ')
18
+ @label = normalize_label(label)
18
19
  @url = url
19
20
  end
20
21
 
21
22
  sig { returns(String) }
22
23
  def to_s
23
- prefix = ' ' * 2 * @depth
24
+ prefix = " " * 2 * @depth
24
25
  "#{prefix}* [#{@label}](#{@url})"
25
26
  end
26
27
 
@@ -28,13 +29,20 @@ module Mdtoc
28
29
  def top_level?(relative_to_depth)
29
30
  @depth == relative_to_depth
30
31
  end
32
+
33
+ private
34
+
35
+ def normalize_label(label)
36
+ label = label.strip.tr("\t\n\r", "") # Remove whitespace characters other than spaces.
37
+ label.gsub(/\[(.*)\]\(.*\)/, '\1') # Remove links
38
+ end
31
39
  end
32
40
 
33
41
  class HeaderWithFragment < Header
34
42
  sig { params(depth: Integer, label: String, url: String).void }
35
43
  def initialize(depth, label, url)
36
- url = "#{url}##{label.strip.downcase.tr(' ', '-').gsub(/[^\w\-]/, '')}"
37
44
  super
45
+ @url += "##{@label.downcase.tr(" ", "-").gsub(/[^\w\-]/, "")}"
38
46
  end
39
47
  end
40
48
  end
@@ -1,8 +1,8 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'sorbet-runtime'
5
- require_relative 'header'
4
+ require "sorbet-runtime"
5
+ require_relative "header"
6
6
 
7
7
  module Mdtoc
8
8
  module Markdown
@@ -22,10 +22,10 @@ module Mdtoc
22
22
  skip = T.let(false, T::Boolean)
23
23
  lines.filter_map do |line|
24
24
  # Skip code blocks.
25
- if line.start_with?('```') && !T.must(line[3..]).strip.end_with?('```')
25
+ if line.start_with?("```") && !T.must(line[3..]).strip.end_with?("```")
26
26
  skip = !skip
27
27
  end
28
- next if skip || !line.start_with?('#')
28
+ next if skip || !line.start_with?("#")
29
29
 
30
30
  header(line)
31
31
  end
@@ -36,9 +36,9 @@ module Mdtoc
36
36
  sig { params(line: String).returns(HeaderWithFragment) }
37
37
  def header(line)
38
38
  m = T.must(line.strip.match(/^(#+)\s*(.*)$/))
39
- num_hashes = m[1]&.count('#') || 1
39
+ num_hashes = m[1]&.count("#") || 1
40
40
  depth = @depth + num_hashes - 1
41
- label = m[2] || ''
41
+ label = m[2] || ""
42
42
  HeaderWithFragment.new(depth, label, @url)
43
43
  end
44
44
  end
data/lib/mdtoc/node.rb CHANGED
@@ -1,10 +1,10 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'pathname'
5
- require 'sorbet-runtime'
6
- require_relative 'markdown/header'
7
- require_relative 'markdown/parser'
4
+ require "pathname"
5
+ require "sorbet-runtime"
6
+ require_relative "markdown/header"
7
+ require_relative "markdown/parser"
8
8
 
9
9
  module Mdtoc
10
10
  class Node
@@ -43,35 +43,35 @@ module Mdtoc
43
43
 
44
44
  sig { returns(String) }
45
45
  def label
46
- File.basename(@path, File.extname(@path)).gsub(/_+/, ' ').gsub(/\s+/, ' ').capitalize
46
+ File.basename(@path, File.extname(@path)).gsub(/_+/, " ").gsub(/\s+/, " ").capitalize
47
47
  end
48
- end
49
48
 
50
- class DirNode < Node
51
- sig { override.returns(T::Array[Mdtoc::Markdown::Header]) }
52
- def headers
53
- readme_path = T.let(nil, T.nilable(String))
54
- child_headers = Dir
55
- .each_child(@path)
56
- .reject { |path| readme_path = File.join(@path, path) if path.casecmp?('readme.md') }
57
- .sort!
58
- .flat_map { |path| Node.for_path(File.join(@path, path), @depth + 1).headers }
59
- return child_headers unless readme_path
49
+ class DirNode < Node
50
+ sig { override.returns(T::Array[Mdtoc::Markdown::Header]) }
51
+ def headers
52
+ readme_path = T.let(nil, T.nilable(String))
53
+ child_headers = Dir
54
+ .each_child(@path)
55
+ .reject { |path| readme_path = File.join(@path, path) if path.casecmp?("readme.md") }
56
+ .sort!
57
+ .flat_map { |path| Node.for_path(File.join(@path, path), @depth + 1).headers }
58
+ return child_headers unless readme_path
60
59
 
61
- # Include the headers from the README at the beginning.
62
- readme_headers = FileNode.new(readme_path, @depth).headers
63
- readme_headers.push(*child_headers)
60
+ # Include the headers from the README at the beginning.
61
+ readme_headers = FileNode.new(readme_path, @depth).headers
62
+ readme_headers + child_headers
63
+ end
64
64
  end
65
- end
66
65
 
67
- class FileNode < Node
68
- sig { override.returns(T::Array[Mdtoc::Markdown::Header]) }
69
- def headers
70
- parser = Markdown::Parser.new(@depth, @path)
71
- headers = parser.headers(File.foreach(@path))
72
- return headers if headers[0]&.top_level?(@depth)
66
+ class FileNode < Node
67
+ sig { override.returns(T::Array[Mdtoc::Markdown::Header]) }
68
+ def headers
69
+ parser = Markdown::Parser.new(@depth, @path)
70
+ headers = parser.headers(File.foreach(@path))
71
+ return headers if headers[0]&.top_level?(@depth)
73
72
 
74
- headers.unshift(Mdtoc::Markdown::Header.new(@depth, label, @path))
73
+ headers.unshift(Mdtoc::Markdown::Header.new(@depth, label, @path))
74
+ end
75
75
  end
76
76
  end
77
77
  end
data/lib/mdtoc/version.rb CHANGED
@@ -1,6 +1,6 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Mdtoc
5
- VERSION = '0.1.4'
5
+ VERSION = "0.2.0"
6
6
  end
data/lib/mdtoc/writer.rb CHANGED
@@ -1,10 +1,10 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Mdtoc
5
5
  module Writer
6
- COMMENT_BEGIN = '<!-- mdtoc -->'
7
- COMMENT_END = '<!-- mdtoc-end -->'
6
+ COMMENT_BEGIN = "<!-- mdtoc -->"
7
+ COMMENT_END = "<!-- mdtoc-end -->"
8
8
 
9
9
  class << self
10
10
  extend T::Sig
@@ -13,7 +13,7 @@ module Mdtoc
13
13
  def write(toc, path, append, create)
14
14
  validate_path(path, create)
15
15
  new_content = content(toc, path, append)
16
- File.open(path, 'w') do |f|
16
+ File.open(path, "w") do |f|
17
17
  f.write(new_content)
18
18
  end
19
19
  end
data/lib/mdtoc.rb CHANGED
@@ -1,11 +1,11 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'optparse'
5
- require 'tempfile'
6
- require_relative 'mdtoc/cli'
7
- require_relative 'mdtoc/node'
8
- require_relative 'mdtoc/writer'
4
+ require "optparse"
5
+ require "tempfile"
6
+ require_relative "mdtoc/cli"
7
+ require_relative "mdtoc/node"
8
+ require_relative "mdtoc/writer"
9
9
 
10
10
  module Mdtoc
11
11
  class << self
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mdtoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - andornaut
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-12 00:00:00.000000000 Z
11
+ date: 2025-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '5'
19
+ version: '5.25'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '5'
26
+ version: '5.25'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,56 +58,56 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.86'
61
+ version: '1.50'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0.86'
68
+ version: '1.50'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rubocop-shopify
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: 1.0.4
75
+ version: 2.0.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: 1.0.4
82
+ version: 2.0.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rubocop-sorbet
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.5'
89
+ version: 0.10.0
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.5'
96
+ version: 0.10.0
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: unparser
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 0.4.9
103
+ version: 0.6.0
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 0.4.9
110
+ version: 0.6.0
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: sorbet-runtime
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -133,13 +133,13 @@ description: |
133
133
 
134
134
  * [Ruby](https://www.ruby-lang.org/en/) (see [.ruby-version](./.ruby-version))
135
135
 
136
- ```
137
- $ gem install mdtoc
136
+ ```bash
137
+ gem install mdtoc
138
138
  ```
139
139
 
140
140
  ## Usage
141
141
 
142
- ```
142
+ ```bash
143
143
  $ mdtoc --help
144
144
  Usage: mdtoc [options] files or directories...
145
145
  -h, --help Show this message
@@ -149,67 +149,82 @@ description: |
149
149
  ```
150
150
 
151
151
  1. Add a `<!-- mdtoc -->` tag to a Markdown file.
152
+
153
+ ```bash
154
+ echo '<!-- mdtoc -->' >> README.md
152
155
  ```
153
- $ echo '<!-- mdtoc -->` >> README.md
154
- ```
156
+
155
157
  2. Run `mdtoc` and specify input files or directories (eg. the "test/samples" directory) and an output file (eg. "README.md").
156
- ```
157
- $ mdtoc -aco README.md test/samples
158
+
159
+ ```bash
160
+ mdtoc -aco README.md test/samples
158
161
  ```
159
162
 
160
163
  ## Example Rakefile
161
164
 
162
- Run the [rake](https://github.com/ruby/rake) "mdtoc" task to update a table of contents.
163
- See [andornaut/til](https://github.com/andornaut/til) for an example.
165
+ Create a `Rakefile` with the contents below, then run
166
+ [`rake`](https://github.com/ruby/rake) to:
164
167
 
165
- ```
168
+ * `git pull`
169
+ * `git add` any *.md files
170
+ * Run `mdtoc` to update the generated table of contents in the ./README.md file
171
+ * Git commit and push any changes
172
+
173
+ ```ruby
166
174
  task default: %w[mdtoc]
167
175
 
168
176
  desc 'Update Markdown table of contents and push changes to the git repository'
169
- task :mdtoc do |t|
170
- command = <<~END
177
+ task :mdtoc do
178
+ command = <<~CMD
171
179
  set -e
172
- git pull
173
180
  if [ -n "$(git diff --name-only --diff-filter=U)" ]; then
174
181
  echo 'Error: conflicts exist' >&2
175
182
  exit 1
176
183
  fi
177
184
  mdtoc --append --create --output README.md docs/
178
185
  git add *.md **/*.md
179
- git commit -m 'Update TOC'
186
+ git commit -qm 'Update TOC' || true
187
+ git pull
180
188
  git push
181
- END
182
- %x|#{command}|
189
+ CMD
190
+ sh command, verbose: false do |ok, status|
191
+ unless ok
192
+ fail "Failed with status: #{status.exitstatus}"
193
+ end
194
+ end
183
195
  end
184
196
  ```
185
197
 
198
+ See [andornaut/til](https://github.com/andornaut/til/blob/master/Rakefile) for an example.
199
+
186
200
  ## Development
187
201
 
188
- ### Installation
202
+ ### Setup
189
203
 
190
204
  Requirements:
191
205
 
192
206
  * [Bundler](https://bundler.io/)
193
207
 
194
- ```
208
+ ```bash
195
209
  # Install dependencies
196
- $ bundle
210
+ bundle
197
211
  ```
198
212
 
199
- ### Usage
213
+ ### Tasks
200
214
 
201
- ```
215
+ ```bash
202
216
  # List rake tasks
203
217
  $ rake -T
204
- rake build # Build mdtoc-0.0.2.gem into the pkg directory
205
- rake default # Run the build, rubocop:auto_correct, sorbet and test tasks
206
- rake install # Build and install mdtoc-0.0.2.gem into system gems
207
- rake install:local # Build and install mdtoc-0.0.2.gem into system gems without...
208
- rake release[remote] # Create tag v0.0.2 and build and push mdtoc-0.0.2.gem to ru...
209
- rake rubocop # Run RuboCop
210
- rake rubocop:auto_correct # Auto-correct RuboCop offenses
211
- rake sorbet # Run the Sorbet type checker
212
- rake test # Run tests
218
+ rake build # Build mdtoc-0.1.5.gem into the pkg directory
219
+ rake default # Run the build, rubocop:autocorrect_all, sorbet and test tasks
220
+ rake install # Build and install mdtoc-0.1.5.gem into system gems
221
+ rake install:local # Build and install mdtoc-0.1.5.gem into system gems without network access
222
+ rake release[remote] # Create tag v0.1.5 and build and push mdtoc-0.1.5.gem to rubygems.org
223
+ rake rubocop # Run RuboCop
224
+ rake rubocop:autocorrect # Autocorrect RuboCop offenses (only when it's safe)
225
+ rake rubocop:autocorrect_all # Autocorrect RuboCop offenses (safe and unsafe)
226
+ rake sorbet # Run the Sorbet type checker
227
+ rake test # Run tests
213
228
 
214
229
  # Run mdtoc with test inputs
215
230
  $ ruby -Ilib bin/mdtoc test/samples
@@ -250,7 +265,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
250
265
  - !ruby/object:Gem::Version
251
266
  version: '0'
252
267
  requirements: []
253
- rubygems_version: 3.1.4
268
+ rubygems_version: 3.2.3
254
269
  signing_key:
255
270
  specification_version: 4
256
271
  summary: Read Markdown files and output a table of contents