markdown_to_mrkdwn 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 93c4327078bb8105c71a63757fa465e5b2015d71a80fd38cc1ea96f7182a353b
4
+ data.tar.gz: 58f95cd43167a151be124472dd127abcdd49ea4f6fdae8ef2e9920020c61d4a1
5
+ SHA512:
6
+ metadata.gz: 1af92c26cb7090bcefa93c89effb2bb78ea1cb22f5e61c4d0b0aa7791ecdd502d13d3f34191945855acf203fbc7da17fba30110ffceada014fb77c3cb67a5a7a
7
+ data.tar.gz: 78b86e0059b240908028cc52c679739f254333582628276e225503db4959600a270426a469aa42327d15086b55f408b2c92b0b62391be052288063d24b7fb2cc
data/CHANGELOG.md ADDED
@@ -0,0 +1,38 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - Initial implementation
13
+ - CLI tool for converting Markdown to Slack mrkdwn
14
+ - Ruby library API for programmatic conversion
15
+ - Support for headings, bold, italic, strikethrough, code blocks, links, and images
16
+ - Command-line options for customizing conversion behavior
17
+
18
+ ### Changed
19
+
20
+ ### Deprecated
21
+
22
+ ### Removed
23
+
24
+ ### Fixed
25
+
26
+ ### Security
27
+
28
+ ## [0.1.0] - 2024-09-14
29
+
30
+ ### Added
31
+
32
+ - Initial release of markdown_to_mrkdwn
33
+ - Basic Markdown to Slack mrkdwn conversion functionality
34
+ - Command-line interface
35
+ - Ruby library interface
36
+
37
+ [Unreleased]: https://github.com/emp823/markdown_to_mrkdwn/compare/v0.1.0...HEAD
38
+ [0.1.0]: https://github.com/emp823/markdown_to_mrkdwn/releases/tag/v0.1.0
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Erik Pearson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # markdown_to_mrkdwn
2
+
3
+ [![CI](https://github.com/emp823/markdown_to_mrkdwn/actions/workflows/ci.yml/badge.svg)](https://github.com/emp823/markdown_to_mrkdwn/actions/workflows/ci.yml)
4
+ [![codecov](https://codecov.io/gh/emp823/markdown_to_mrkdwn/branch/main/graph/badge.svg)](https://codecov.io/gh/emp823/markdown_to_mrkdwn)
5
+ [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.1-ruby.svg)](https://www.ruby-lang.org/)
6
+
7
+ Convert Markdown to Slack mrkdwn (lightweight, no dependencies).
8
+
9
+ ## Installation
10
+
11
+ Install the gem:
12
+
13
+ ```bash
14
+ gem install markdown_to_mrkdwn
15
+ ```
16
+
17
+ Or add to your Gemfile:
18
+
19
+ ```ruby
20
+ gem "markdown_to_mrkdwn"
21
+ ```
22
+
23
+ For development, clone and setup:
24
+
25
+ ```bash
26
+ git clone https://github.com/emp823/markdown_to_mrkdwn.git
27
+ cd markdown_to_mrkdwn
28
+ bin/setup
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Ruby API
34
+
35
+ ````ruby
36
+ require "markdown_to_mrkdwn"
37
+
38
+ mrkdwn = MarkdownToMrkdwn.convert(<<~MARKDOWN)
39
+ # Heading
40
+
41
+ **Bold** and *italic* and ~~strike~~ and `code`.
42
+
43
+ ```ruby
44
+ puts "hello"
45
+ ````
46
+
47
+ ![Alt](https://example.com/img.png)
48
+ MARKDOWN
49
+
50
+ puts mrkdwn
51
+
52
+ ````
53
+
54
+ ### Command Line
55
+
56
+ ```bash
57
+ # Convert from stdin
58
+ echo "# Title\n**bold** and _italic_." | markdown_to_mrkdwn
59
+
60
+ # Convert a file
61
+ markdown_to_mrkdwn README.md
62
+
63
+ # Use options
64
+ markdown_to_mrkdwn --plain-headings README.md
65
+ ````
66
+
67
+ ## What’s converted
68
+
69
+ - Headings `#`..`######` → bold line (or plain with `--plain-headings`).
70
+ - Bold `**text**` or `__text__` → `*text*`.
71
+ - Italic `*text*` or `_text_` → `_text_`.
72
+ - Strikethrough `~~text~~` → `~text~`.
73
+ - Inline code `` `code` `` → `` `code` `` (unchanged).
74
+ - Fenced code blocks `lang ... ` → `...` (language hint removed).
75
+ - Links `[label](url)` → `<url|label>`.
76
+ - Images `![alt](url)` → `<url|alt>`.
77
+ - Horizontal rules → em dash line (or keep with `--keep-hr`).
78
+
79
+ Notes:
80
+
81
+ - Lists, blockquotes, and paragraphs are mostly compatible with Slack already and are left as-is.
82
+ - Code (inline and blocks) is protected from other conversions.
83
+ - This is a pragmatic converter; edge cases of Markdown are not fully supported.
84
+
85
+ ## Contributing
86
+
87
+ 1. Fork the repository
88
+ 2. Create a feature branch (`git checkout -b feature/my-new-feature`)
89
+ 3. Make your changes and add tests
90
+ 4. Run the test suite (`bundle exec rake test`)
91
+ 5. Ensure code style passes (`bundle exec rubocop`)
92
+ 6. Commit your changes (`git commit -am 'Add some feature'`)
93
+ 7. Push to the branch (`git push origin feature/my-new-feature`)
94
+ 8. Create a Pull Request
95
+
96
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines.
97
+
98
+ ## License
99
+
100
+ MIT - See [LICENSE](LICENSE) file for details.
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "markdown_to_mrkdwn"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "optparse"
5
+
6
+ begin
7
+ require_relative "../lib/markdown_to_mrkdwn"
8
+ rescue LoadError
9
+ warn "Could not load markdown_to_mrkdwn library. Ensure you're running from the gem root or installed the gem."
10
+ exit 1
11
+ end
12
+
13
+ options = {
14
+ heading: :bold,
15
+ keep_hr: false
16
+ }
17
+
18
+ parser = OptionParser.new do |opts|
19
+ opts.banner = "Usage: markdown_to_mrkdwn [options] [FILE]"
20
+ opts.separator ""
21
+ opts.separator "Convert Markdown to Slack mrkdwn format"
22
+ opts.separator ""
23
+ opts.separator "Options:"
24
+
25
+ opts.on("--plain-headings", "Do not bold headings") { options[:heading] = :plain }
26
+ opts.on("--keep-hr", "Keep horizontal rules as --- or ***") { options[:keep_hr] = true }
27
+ opts.on("-v", "--version", "Show version") do
28
+ puts "markdown_to_mrkdwn #{MarkdownToMrkdwn::VERSION}"
29
+ exit 0
30
+ end
31
+ opts.on("-h", "--help", "Show this help") do
32
+ puts opts
33
+ exit 0
34
+ end
35
+
36
+ opts.separator ""
37
+ opts.separator "Examples:"
38
+ opts.separator " echo '# Title' | markdown_to_mrkdwn"
39
+ opts.separator " markdown_to_mrkdwn README.md"
40
+ opts.separator " markdown_to_mrkdwn --plain-headings file.md"
41
+ end
42
+
43
+ begin
44
+ parser.parse!
45
+ rescue OptionParser::InvalidOption => e
46
+ warn "Error: #{e.message}"
47
+ warn "Use --help for usage information"
48
+ exit 1
49
+ end
50
+
51
+ input = begin
52
+ if ARGV[0]
53
+ unless File.exist?(ARGV[0])
54
+ warn "Error: File '#{ARGV[0]}' not found"
55
+ exit 1
56
+ end
57
+ unless File.readable?(ARGV[0])
58
+ warn "Error: File '#{ARGV[0]}' is not readable"
59
+ exit 1
60
+ end
61
+ File.read(ARGV[0])
62
+ else
63
+ $stdin.read
64
+ end
65
+ rescue StandardError => e
66
+ warn "Error reading input: #{e.message}"
67
+ exit 1
68
+ end
69
+
70
+ begin
71
+ result = MarkdownToMrkdwn.convert(input, **options)
72
+ puts result
73
+ rescue StandardError => e
74
+ warn "Error converting markdown: #{e.message}"
75
+ exit 1
76
+ end
data/bin/setup ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "fileutils"
5
+
6
+ # path to your application root.
7
+ APP_ROOT = File.expand_path("..", __dir__)
8
+
9
+ def system!(*args)
10
+ system(*args) || abort("\n== Command #{args} failed ==")
11
+ end
12
+
13
+ FileUtils.chdir APP_ROOT do
14
+ # This script is a way to set up or update your development environment automatically.
15
+ # This script is idempotent, so that you can run it at anytime and get an expectable outcome.
16
+ # Add necessary setup steps to this file.
17
+
18
+ puts "== Installing dependencies =="
19
+ system! "gem install bundler --conservative"
20
+ system("bundle check") || system!("bundle install")
21
+
22
+ puts "\n== Removing old logs and tempfiles =="
23
+ FileUtils.rm_f Dir.glob("*.gem")
24
+ FileUtils.rm_rf "pkg/"
25
+
26
+ puts "\n== Running tests =="
27
+ system! "bundle exec rake test"
28
+
29
+ puts "\n== All set! =="
30
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MarkdownToMrkdwn
4
+ class Converter
5
+ SENTINEL = "§"
6
+ # Options:
7
+ # - heading: :bold (default) or :plain
8
+ # - keep_hr: true to keep --- as is (default false -> replace with an em dash line)
9
+ def initialize(heading: :bold, keep_hr: false)
10
+ @heading_style = heading
11
+ @keep_hr = keep_hr
12
+ end
13
+
14
+ def convert(markdown)
15
+ return "" if markdown.nil? || markdown.empty?
16
+
17
+ text = markdown.dup
18
+
19
+ # 1) Extract fenced code blocks and inline code to protect from further processing
20
+ blocks = {}
21
+ text = extract_fenced_code_blocks(text, blocks)
22
+ text = extract_inline_code(text, blocks)
23
+
24
+ # 2) Images: ![alt](url) -> <url|alt>
25
+ text.gsub!(/!\[(.*?)\]\((\S+?)(?:\s+".*?")?\)/) do
26
+ alt = Regexp.last_match(1)
27
+ url = Regexp.last_match(2)
28
+ "<#{url}|#{alt}>"
29
+ end
30
+
31
+ # 3) Links: [text](url) -> <url|text>
32
+ text.gsub!(/\[(.*?)\]\((\S+?)(?:\s+".*?")?\)/) do
33
+ label = Regexp.last_match(1)
34
+ url = Regexp.last_match(2)
35
+ "<#{url}|#{label}>"
36
+ end
37
+
38
+ # 4) Strikethrough: ~~text~~ -> ~text~
39
+ text.gsub!(/~~(.*?)~~/, '~\1~')
40
+
41
+ # 5) Italic: *text* or _text_ -> _text_
42
+ # Run italics BEFORE bold so that bold remains asterisks in Slack.
43
+ # Only match single markers, not double.
44
+ text.gsub!(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/, '_\1_')
45
+ text.gsub!(/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/, '_\1_')
46
+
47
+ # 6) Bold: **text** or __text__ -> *text*
48
+ text.gsub!(/\*\*(.+?)\*\*/, '*\1*')
49
+ text.gsub!(/__(.+?)__/, '*\1*')
50
+
51
+ # 7) Headings: # H -> *H*
52
+ text = convert_headings(text)
53
+
54
+ # 8) Horizontal rules
55
+ text.gsub!(/^\s*([-*_])\s*\1\s*\1\s*$/, @keep_hr ? '\0' : "—\n")
56
+
57
+ # 9) Restore protected code segments
58
+ restore_placeholders(text, blocks)
59
+ end
60
+
61
+ private
62
+
63
+ def extract_fenced_code_blocks(text, store)
64
+ idx = 0
65
+ text.gsub(/```([a-zA-Z0-9_+-]*)\n([\s\S]*?)\n```/) do
66
+ Regexp.last_match(1)
67
+ body = Regexp.last_match(2)
68
+ key = "#{SENTINEL}MRKDWNBLOCK_#{idx += 1}#{SENTINEL}"
69
+ # Slack ignores the language hint; keep pure triple backticks
70
+ store[key] = "```\n#{body}\n```"
71
+ key
72
+ end
73
+ end
74
+
75
+ def extract_inline_code(text, store)
76
+ idx = store.size
77
+ text.gsub(/`([^`\n]+)`/) do
78
+ body = Regexp.last_match(1)
79
+ key = "#{SENTINEL}MRKDWNBLOCK_#{idx += 1}#{SENTINEL}"
80
+ store[key] = "`#{body}`"
81
+ key
82
+ end
83
+ end
84
+
85
+ def restore_placeholders(text, store)
86
+ return text if store.empty?
87
+
88
+ loop do
89
+ before = text.dup
90
+ store.each { |k, v| text = text.gsub(k, v) }
91
+ break if text == before
92
+ end
93
+ text
94
+ end
95
+
96
+ def convert_headings(text)
97
+ text.lines.map do |line|
98
+ if (m = line.match(/^(\s{0,3})(#{Regexp.escape("#")}{1,6})\s+(.*)$/))
99
+ indent = m[1]
100
+ content = m[3].strip
101
+ case @heading_style
102
+ when :bold
103
+ "#{indent}*#{content}*\n"
104
+ else
105
+ "#{indent}#{content}\n"
106
+ end
107
+ else
108
+ line
109
+ end
110
+ end.join
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MarkdownToMrkdwn
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "markdown_to_mrkdwn/version"
4
+ require_relative "markdown_to_mrkdwn/converter"
5
+
6
+ module MarkdownToMrkdwn
7
+ def self.convert(markdown, **options)
8
+ Converter.new(**options).convert(markdown)
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: markdown_to_mrkdwn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Erik Pearson
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Lightweight converter for common Markdown to Slack mrkdwn syntax with
13
+ a simple CLI. No dependencies, fast conversion.
14
+ email:
15
+ - emp823@icloud.com
16
+ executables:
17
+ - markdown_to_mrkdwn
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - CHANGELOG.md
22
+ - LICENSE
23
+ - README.md
24
+ - bin/console
25
+ - bin/markdown_to_mrkdwn
26
+ - bin/setup
27
+ - lib/markdown_to_mrkdwn.rb
28
+ - lib/markdown_to_mrkdwn/converter.rb
29
+ - lib/markdown_to_mrkdwn/version.rb
30
+ homepage: https://github.com/emp823/markdown_to_mrkdwn
31
+ licenses:
32
+ - MIT
33
+ metadata:
34
+ bug_tracker_uri: https://github.com/emp823/markdown_to_mrkdwn/issues
35
+ changelog_uri: https://github.com/emp823/markdown_to_mrkdwn/blob/main/CHANGELOG.md
36
+ documentation_uri: https://rubydoc.info/gems/markdown_to_mrkdwn
37
+ homepage_uri: https://github.com/emp823/markdown_to_mrkdwn
38
+ source_code_uri: https://github.com/emp823/markdown_to_mrkdwn
39
+ wiki_uri: https://github.com/emp823/markdown_to_mrkdwn/wiki
40
+ rubygems_mfa_required: 'true'
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: 3.1.0
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubygems_version: 3.6.7
56
+ specification_version: 4
57
+ summary: Convert Markdown to Slack mrkdwn
58
+ test_files: []