Markdown-Render-1.60.2/0000755000076500007650000000000014721715267014173 5ustar rlauerrlauerMarkdown-Render-1.60.2/README.md0000644000076500007650000001560314721715262015452 0ustar rlauerrlauer# Table of Contents * [README](#readme) * [Installation](#installation) * [Prerequisites](#prerequisites) * [Building and Deploying](#building-and-deploying) * [Building an rpm](#building-an-rpm) * [Building from CPAN](#building-from-cpan) * [Usage](#usage) * [Tips & Tricks](#tips--tricks) * [@DATE(format)@](#dateformat) * [@GIT_EMAIL@](#gitemail) * [@GIT_USER@](#gituser) * [@TOC@](#toc) * [@TOC_BACK(optional text)@](#tocbackoptional-text) * [Custom TOC Title](#custom-toc-title) * [Prevent heading from being included in table of contents](#prevent-heading-from-being-included-in-table-of-contents) * [Rendering](#rendering) * [Credits](#credits) __Updated 2024-11-27__ by Rob Lauer # README A quick search regarding how to get a table of contents into my markdown yielded only a few hits or projects that seemed a little weighty to me, so here's a little Perl script with just a few dependencies that you might find useful. See [Usage](#usage) for more information. The script will render your markdown as HTML using either the [GitHub API](https://docs.github.com/en/rest/markdown) or the Perl module [Text::Markdown::Discount](https://metacpan.org/pod/Text::Markdown::Discount) A default stylesheet will be applied but you can provide your own style sheet as well. # Installation ## Prerequisites The script has been tested with these versions, but others might work too. | Module | Version | |--------------------------|---------| | `Class::Accessor::Fast` | 0.51 | | `Date::Format` | 2.24 | | `HTTP::Request` | 6.00 | | `IO::Scalar` | 2.113 | | `JSON` | 4.03 | | `LWP::UserAgent` | 6.36 | | `Readonly` | 2.05 | ## Building and Deploying ``` git clone https://github.com/rlauer6/markdown-utils.git make sudo ln -s $(pwd)/markdown-utils/md-utlils.pl /usr/bin/md-utils ``` ## Building an rpm If you want to build an rpm for a RedHat Linux based system, install the `rpm-build` tools. ``` make rpm sudo yum install 'perl(Markdown::Render)' ``` [Back to Top](#table-of-contents) ## Building from CPAN ``` cpanm -v Markdown::Render ``` # Usage ``` usage: md-utils options [markdown-file] Utility to add a table of contents and other goodies to your GitHub flavored markdown. - Add @TOC@ where you want to see your TOC. - Add @TOC_BACK@ to insert an internal link to TOC - Add @DATE(format-str)@ where you want to see a formatted date - Add @GIT_USER@ where you want to see your git user name - Add @GIT_EMAIL@ where you want to see your git email address - Use the --render option to render the HTML for the markdown Examples: --------- md-utils README.md.in > README.md md-utils -r README.md.in Options ------- -B, --body default is to add body tag, use --nobody to prevent -b, --both interpolates intermediate file and renders HTML -c, --css css file -e, --engine github, text_markdown (default: github) -h help -i, --infile input file, default: STDIN -m, --mode for GitHub API mode is 'gfm' or 'markdown' (default: markdown) -n, --no-titl do not print a title for the TOC -o, --outfile outfile, default: STDOUT -r, --render render only, does NOT interpolate keywords -R, --raw return raw HTML from engine -t, --title string to use for a custom title, default: "Table of Contents" -v, --version version -N, --nocss no css Tips ---- * Use !# to prevent a header from being include in the table of contents. Add your own custom back to TOC message @TOC_BACK(Back to Index)@ * Date format strings are based on format strings supported by the Perl module 'Date::Format'. The default format is %Y-%m-%d if not format is given. * use the --nobody tag to return the HTML without the wrapper. --raw mode will also return HTML without wrapper ``` # Tips & Tricks 1. Add @TOC@ somewhere in your markdown 1. Use !# to prevent heading from being part of the table of contents 1. Finalize your markdown... ``` cat README.md.in | md-utils.pl > README.md ``` 1. ...or...kick it old school with a `Makefile` if you like ``` FILES = \ README.md.in MARKDOWN=$(FILES:.md.in=.md) HTML=$(MARKDOWN:.md=.html) # interpolate the custom markdown keywords $(MARKDOWN): % : %.in md-utils $< > $@ $(HTML): $(MARKDOWN) md-utils -r $< > $@ all: $(MARKDOWN) $(HTML) markdown: $(MARKDOWN) html: $(HTML) clean: rm -f $(MARKDOWN) $(HTML) ``` 1. ...and then... ``` make all ``` ## @DATE(format)@ Add the current date using a custom format. Essentially calls the Perl function `time2str`. See `perldoc Date::Format`. If no format is present the default is %Y-%m-%d (YYYY-MM-DD). _Best practice would be to use a `Makefile` to generate your final `README.md` from your `README.md.in` template as shown [above](#usage) and generate your `README.md` as the last step before pushing your branch to a repository._ Example: @`DATE(%Y-%m-%d)`@ ## @GIT_EMAIL@ ## @GIT_USER@ If you've done something like: ``` git config --global user.name "Fred Flintstone" git config --global user.email "fflintstone@bedrock.org" ``` or ``` git config --local user.name "Fred Flintstone" git config --local user.email "fflintstone@bedrock.org" ``` ...then you can expect to see those in your markdown, otherwise don't use the tags. [Back to Top](#table-of-contents) ## @TOC@ Add this tag anywhere in your markdown in include a table of contents. ## @TOC_BACK(optional text)@ Add @TOC_BACK@ anywhere in your markdown template to insert an internal link back to the table of contents. @`TOC_BACK`@ @`TOC_BACK(Back to Index)`@ [Back to Top](#table-of-contents) ## Custom TOC Title Use the `--no-title` option if you don't want the script to insert a header for the TOC. Use the `--title` option if you want a custom header for the TOC. ## Prevent heading from being included in table of contents Precede the heading level with bang (!) and that heading will not be included in the table of contents. [Back to Top](#table-of-contents) # Rendering Using the [GiHub rendering API](https://developer.github.com/v3/markdown/), you can create HTML pretty easily. So if you want to preview your markdown...you might try: ``` jq --slurp --raw-input '{"text": "\(.)", "mode": "markdown"}' < README.md | \ curl -s --data @- https://api.github.com/markdown ``` __...but alas you might find that your internal links don't work in that rendered HTML...__ Never fear...the `--render` option of this utility will go ahead and set that right for you and munge the HTML so that internal links really work...or at least they do for me. ``` md-utils --render README.md > README.html ``` [Back to Top](#table-of-contents) # Credits Rob Lauer - [Back to Top](#table-of-contents) Markdown-Render-1.60.2/00-markdown.t0000644000076500007650000000356614721715262016424 0ustar rlauerrlauer#!/usr/bin/env perl use strict; use warnings; use lib qw{$Bin/..}; use FindBin qw($Bin); use Data::Dumper; use English qw{-no_match_vars}; use Test::More; our %TESTS = ( new => 'Markdown::Render->new', render_markdown => 'render HTML from markdown file', ); ######################################################################## plan tests => 1 + keys %TESTS; # Find the test input file. my $test_file; for ('files', '..') { my $file = "$Bin/$_/README.md.in"; if (-f $file) { $test_file = $file; last; } } BAIL_OUT("Unable to find test file") unless defined $test_file; use_ok('Markdown::Render'); ######################################################################## subtest 'new' => sub { ######################################################################## my $md = eval { Markdown::Render->new( infile => $test_file, engine => 'text_markdown', ); }; ok( !$EVAL_ERROR, 'new' ) or do { diag( Dumper( [$EVAL_ERROR] ) ); BAIL_OUT('could not instantiate Markdown::Render'); }; }; ######################################################################## subtest 'render_markdown' => sub { ######################################################################## my $md = eval { Markdown::Render->new( infile => $test_file, engine => 'text_markdown', ); }; ok( !$EVAL_ERROR, 'new(infile => file)' ) or do { diag( Dumper( [$EVAL_ERROR] ) ); BAIL_OUT('could not instantiate Markdown::Render'); }; ok( $md->get_markdown, 'read markdown file' ); ok( !$md->get_html, 'no html yet' ); isa_ok( $md->render_markdown, 'Markdown::Render' ); ok( $md->get_html, 'render HTML' ); ok( $md->render_markdown->get_html, 'retrieve HTML' ); ok( $md->finalize_markdown->render_markdown->get_html, 'finalize and render' ); }; 1; __DATA__ END_OF_PLAN Markdown-Render-1.60.2/META.yml0000664000076500007650000000157314721715267015454 0ustar rlauerrlauer--- abstract: 'Render markdown using GitHub API' author: - 'BIGFOOT ' build_requires: ExtUtils::MakeMaker: '6.64' File::ShareDir::Install: '0' configure_requires: ExtUtils::MakeMaker: '6.64' File::ShareDir::Install: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.64, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Markdown-Render no_index: directory: - t - inc provides: Markdown::Render: file: lib/Markdown/Render.pm version: v1.60.2 requires: Class::Accessor::Fast: '0.51' Config::Tiny: '2.28' Date::Format: '2.24' HTTP::Request: '6.45' IO::Scalar: '2.113' JSON: '4.10' LWP::UserAgent: '6.77' Readonly: '2.05' perl: '5.016' version: v1.60.2 x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Markdown-Render-1.60.2/bin/0000755000076500007650000000000014721715267014743 5ustar rlauerrlauerMarkdown-Render-1.60.2/bin/md-utils.pl0000755000076500007650000001107614721715262017041 0ustar rlauerrlauer#!/usr/bin/env perl use strict; use warnings; use Carp; use Cwd; use Config::Tiny; use Data::Dumper; use English qw(-no_match_vars); use File::Basename; use Getopt::Long qw(:config no_ignore_case auto_abbrev); use Readonly; use Markdown::Render; our $VERSION = $Markdown::Render::VERSION; Readonly our $EMPTY => q{}; Readonly our $TRUE => 1; Readonly our $FALSE => 0; ######################################################################## sub version { ######################################################################## my ($name) = File::Basename::fileparse( $PROGRAM_NAME, qr/[.][^.]+$/xsm ); print "$name $VERSION\n"; return; } ######################################################################## sub usage { ######################################################################## print <<'END_OF_USAGE'; usage: md-utils.pl options [markdown-file] Utility to add a table of contents and other goodies to your GitHub flavored markdown. - Add @TOC@ where you want to see your TOC. - Add @TOC_BACK@ to insert an internal link to TOC - Add @DATE(format-str)@ where you want to see a formatted date - Add @GIT_USER@ where you want to see your git user name - Add @GIT_EMAIL@ where you want to see your git email address - Use the --render option to render the HTML for the markdown Examples: --------- md-utils.pl README.md.in > README.md md-utils.pl -r README.md.in Options ------- -B, --body default is to add body tag, use --nobody to prevent -b, --both interpolates intermediate file and renders HTML -c, --css css file -e, --engine github, text_markdown (default: github) -h help -i, --infile input file, default: STDIN -m, --mode for GitHub API mode is 'gfm' or 'markdown' (default: markdown) -n, --no-titl do not print a title for the TOC -o, --outfile outfile, default: STDOUT -r, --render render only, does NOT interpolate keywords -R, --raw return raw HTML from engine -t, --title string to use for a custom title, default: "Table of Contents" -v, --version version -N, --nocss do not add any CSS link Tips ---- * Use !# to prevent a header from being include in the table of contents. Add your own custom back to TOC message @TOC_BACK(Back to Index)@ * Date format strings are based on format strings supported by the Perl module 'Date::Format'. The default format is %Y-%m-%d if not format is given. * use the --nobody tag to return the HTML without the wrapper. --raw mode will also return HTML without wrapper END_OF_USAGE return; } ######################################################################## sub get_git_user { ######################################################################## my ( $git_user, $git_email ); for ( ( getcwd . '.git/config' ), "$ENV{HOME}/.gitconfig" ) { next if !-e $_; my $config = eval { Config::Tiny->read($_); }; ( $git_user, $git_email ) = @{ $config->{user} }{qw(name email)}; last if $git_user && $git_email; } return ( $git_user, $git_email ); } # +------------------------ + # | MAIN SCRIPT STARTS HERE | # +------------------------ + my %options; my @options_spec = qw( body|B! both|b css=s debug engine=s help infile=s mode=s no-title nocss|N outfile=s raw|R render|r title=s version ); GetOptions( \%options, @options_spec ) or croak 'could not parse options'; $options{body} //= $TRUE; if ( $options{raw} ) { $options{body} = $FALSE; } if ( exists $options{help} ) { usage; exit 0; } if ( exists $options{version} ) { version; exit 0; } $options{no_title} = delete $options{'no-title'}; my $markdown; if ( !$options{infile} ) { if (@ARGV) { $options{infile} = shift @ARGV; } elsif ( !-t STDIN ) { ## no critic (ProhibitInteractiveTest) local $RS = undef; my $fh = *STDIN; $markdown = <$fh>; } } my ( $git_user, $git_email ) = get_git_user(); $options{git_user} = $git_user // $EMPTY; $options{git_email} = $git_email // $EMPTY; my $md = Markdown::Render->new( %options, markdown => $markdown ); my $ofh = *STDOUT; if ( exists $options{outfile} ) { open $ofh, '>', $options{outfile} ## no critic (RequireBriefOpen) or croak "could not open output file\n"; } eval { if ( $options{both} ) { $md->finalize_markdown->render_markdown; $md->print_html( %options, fh => $ofh ); } elsif ( $options{render} ) { $md->render_markdown; $md->print_html( %options, fh => $ofh ); } else { $md->finalize_markdown; print {$ofh} $md->get_markdown; } }; croak "ERROR: $EVAL_ERROR" if $EVAL_ERROR; close $ofh; exit 0; __END__ Markdown-Render-1.60.2/Makefile.PL0000644000076500007650000000332714721715266016151 0ustar rlauerrlauer# autogenerated by /home/rlauer/bin/make-cpan-dist.pl on Wed Nov 27 17:18:30 2024 use strict; use warnings; use ExtUtils::MakeMaker; use File::ShareDir::Install; if ( -d 'share' ) { install_share 'share'; } WriteMakefile( NAME => 'Markdown::Render', MIN_PERL_VERSION => '5.016', AUTHOR => 'BIGFOOT ', VERSION_FROM => 'lib/Markdown/Render.pm', ABSTRACT => 'Render markdown using GitHub API', LICENSE => 'perl', PL_FILES => {}, EXE_FILES => ['bin/md-utils.pl'], PREREQ_PM => { 'Class::Accessor::Fast' => '0.51', 'Config::Tiny' => '2.28', 'Date::Format' => '2.24', 'HTTP::Request' => '6.45', 'IO::Scalar' => '2.113', 'JSON' => '4.10', 'LWP::UserAgent' => '6.77', 'Readonly' => '2.05' }, BUILD_REQUIRES => { 'ExtUtils::MakeMaker' => '6.64', 'File::ShareDir::Install' => 0, }, CONFIGURE_REQUIRES => { 'ExtUtils::MakeMaker' => '6.64', 'File::ShareDir::Install' => 0, }, TEST_REQUIRES => {}, META_MERGE => { 'meta-spec' => { 'version' => 2 }, 'provides' => { 'Markdown::Render' => { 'file' => 'lib/Markdown/Render.pm', 'version' => '1.60.2' } } } ); package MY; use File::ShareDir::Install; use English qw(-no_match_vars); sub postamble { my $self = shift; my @ret = File::ShareDir::Install::postamble($self); my $postamble = join "\n", @ret; if ( -e 'postamble' ) { local $RS = undef; open my $fh, '<', 'postamble' or die "could not open postamble\n"; $postamble .= <$fh>; close $fh; } return $postamble; } 1; Markdown-Render-1.60.2/MANIFEST0000644000076500007650000000045014721715267015323 0ustar rlauerrlauer00-markdown.t bin/md-utils.pl ChangeLog lib/Markdown/Render.pm Makefile.PL MANIFEST This list of files README.md README.md.in META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) Markdown-Render-1.60.2/lib/0000755000076500007650000000000014721715267014741 5ustar rlauerrlauerMarkdown-Render-1.60.2/lib/Markdown/0000755000076500007650000000000014721715267016523 5ustar rlauerrlauerMarkdown-Render-1.60.2/lib/Markdown/Render.pm0000755000076500007650000003254614721715262020310 0ustar rlauerrlauerpackage Markdown::Render; use strict; use warnings; use Carp; use Data::Dumper; use English qw(-no_match_vars); use HTTP::Request; use IO::Scalar; use JSON; use LWP::UserAgent; use List::Util qw(none); our $VERSION = '1.60.2'; use parent qw(Class::Accessor::Fast); __PACKAGE__->follow_best_practice; __PACKAGE__->mk_accessors( qw( body css engine git_email git_user html infile markdown mode no_title raw render title ) ); use Readonly; Readonly our $GITHUB_API => 'https://api.github.com/markdown'; Readonly our $EMPTY => q{}; Readonly our $SPACE => q{ }; Readonly our $TOC_TITLE => 'Table of Contents'; Readonly our $TOC_BACK => 'Back to Table of Contents'; Readonly our $DEFAULT_CSS => 'https://cdn.simplecss.org/simple-v1.css'; caller or __PACKAGE__->main; ######################################################################## sub new { ######################################################################## my ( $class, @args ) = @_; my %options = ref $args[0] ? %{ $args[0] } : @args; $options{title} //= $TOC_TITLE; $options{css} //= $options{nocss} ? $EMPTY : $DEFAULT_CSS; $options{mode} //= 'markdown'; $options{engine} //= 'github'; my $self = $class->SUPER::new( \%options ); if ( $self->get_infile ) { open my $fh, '<', $self->get_infile or die 'could not open ' . $self->get_infile; local $RS = undef; $self->set_markdown(<$fh>); close $fh; } return $self; } ######################################################################## sub toc_back { ######################################################################## my ($self) = @_; my $back_link = lc $self->get_title; $back_link =~ s/\s/-/gxsm; return $back_link; } ######################################################################## sub back_to_toc { ######################################################################## my ( $self, $message ) = @_; $message ||= $TOC_BACK; $message =~ s/[(]\"?(.*?)\"?[)]/$1/xsm; return sprintf '[%s](#%s)', $message, $self->toc_back; } ######################################################################## sub finalize_markdown { ######################################################################## my ($self) = @_; my $markdown = $self->get_markdown; die "no markdown yet\n" if !$markdown; my $fh = IO::Scalar->new( \$markdown ); my $final_markdown; my $in_code_block; while ( my $line = <$fh> ) { if ( $line =~ /\A\s*[`]{3}\s*\z/xsm ) { $in_code_block ^= 1; } if ($in_code_block) { $final_markdown .= $line; next; } $line =~ s/^\!\#/\#/xsm; # ! used to prevent including header in TOC if ( $line =~ /^\@TOC\@/xsm ) { my $toc = $self->create_toc; chomp $toc; $line =~ s/\@TOC\@/$toc/xsm; } my $title = $self->get_title; if ( $line =~ /\@TOC_TITLE\@/xsm ) { $line =~ s/\@TOC_TITLE\@/$title/xsm; } my $git_user = $self->get_git_user // 'anonymouse'; my $git_email = $self->get_git_email // 'anonymouse@example.com'; if ( $line =~ /\@GIT_(USER|EMAIL)\@/xsm ) { $line =~ s/\@GIT_USER\@/$git_user/xsm; $line =~ s/\@GIT_EMAIL\@/$git_email/xsm; } my $date; while ( $line =~ /\@DATE[(]?(.*?)[)]?\@/xsm ) { my $format = $1 ? $1 : '%Y-%m-%d'; $date = $self->format_date($format); $line =~ s /\@DATE[(]?(.*?)[)]?\@/$date/xsm; } if ( $line =~ /\@TOC_BACK[(]?(.*?)[)]?\@/xsm ) { my $back = $self->back_to_toc($1); $line =~ s/\@TOC_BACK[(]?(.*?)[)]?\@/$back/xsm; } $final_markdown .= $line; } close $fh; $self->set_markdown($final_markdown); return $self; } ######################################################################## sub _render_with_text_markdown { ######################################################################## my ($self) = @_; eval { require Text::Markdown::Discount; }; if ($EVAL_ERROR) { carp "no Text::Markdown::Discount available...using GitHub API.\n$EVAL_ERROR"; return $self->_render_with_github; } my $markdown = $self->get_markdown; my $html = Text::Markdown::Discount::markdown($markdown); if ( $self->get_raw ) { $self->set_html($html); } else { $self->fix_anchors( Text::Markdown::Discount::markdown($markdown) ); } return $self; } ######################################################################## sub fix_anchors { ######################################################################## my ( $self, $html_raw ) = @_; my $fh = IO::Scalar->new( \$html_raw ); my $html; while ( my $line = <$fh> ) { # remove garbage if there... if ( $line =~ /^\s*(.*)<\/a>(.*)/xsm ) { $line = "$3$4\n"; } $html .= _fix_header($line); } close $fh; $self->set_html($html); return $self; } ######################################################################## sub _fix_header { ######################################################################## my ($line) = @_; return $line if $line !~ /^\s*(.*?)<\/h\d>$/xsm; my ( $hn, $anchor, $header ) = ( $1, $2, $2 ); ## no critic (ProhibitCaptureWithoutTest) $anchor = lc $anchor; $anchor =~ s/\s+/-/gxsm; # spaces become '-' $anchor =~ s/[.:\?_.\@'(),\`]//xsmg; # known weird characters, but expect more $anchor =~ s/\///xsmg; $line = sprintf qq{%s\n}, $hn, $anchor, $anchor, $header, $hn; return $line; } ######################################################################## sub render_markdown { ######################################################################## my ($self) = @_; if ( $self->get_engine eq 'github' ) { return $self->_render_with_github; } elsif ( $self->get_engine eq 'text_markdown' ) { return $self->_render_with_text_markdown; } else { croak 'invalid engine: ' . $self->get_engine; } } ######################################################################## sub _render_with_github { ######################################################################## my ($self) = @_; my $markdown = $self->get_markdown; my $ua = LWP::UserAgent->new; my $req = HTTP::Request->new( 'POST', $GITHUB_API ); my $mode = $self->get_mode; if ( none { $mode eq $_ } qw(gfm markdown) ) { $mode = 'markdown'; } my $api_request = { text => $markdown, mode => $mode, }; $req->content( to_json($api_request) ); my $rsp = $ua->request($req); croak 'could not render using GitHub API: ' . $rsp->status_line if !$rsp->is_success; if ( $self->get_raw ) { $self->set_html( $rsp->content ); return $self; } if ( $self->get_mode eq 'gfm' ) { return $self->fix_anchors( $rsp->content ); } else { return $self->fix_github_html( $rsp->content ); } } ######################################################################## sub fix_github_html { ######################################################################## my ( $self, $html_raw ) = @_; my $fh = IO::Scalar->new( \$html_raw ); my $html = $EMPTY; while ( my $line = <$fh> ) { chomp $line; $line =~ s/(href|id)=\"\#?user-content-/$1=\"/xsm; $line =~ s/(href|id)=\"\#?\%60.*\%60/$1=\"#$2/xsm; $html .= "$line\n"; } close $fh; $self->set_html($html); return $self; } ######################################################################## sub format_date { ######################################################################## my ( $self, $template ) = @_; require Date::Format; $template =~ s/[(]\"?(.*?)\"?[)]/$1/xsm; my $val = eval { Date::Format::time2str( $template, time ); }; return $EVAL_ERROR ? '' : $val; } ######################################################################## sub create_toc { ######################################################################## my ($self) = @_; my $markdown = $self->get_markdown; my $fh = IO::Scalar->new( \$markdown ); my $toc = $self->get_no_title ? $EMPTY : "# \@TOC_TITLE\@\n\n"; while (<$fh>) { chomp; /^(\#+)\s+(.*?)$/xsm && do { my $level = $1; my $indent = $SPACE x ( 2 * ( length($level) - 1 ) ); my $topic = $2; my $link = $topic; $link =~ s/^\s*(.*)\s*$/$1/xsm; $link =~ s/\s+/-/gxsm; # spaces become '-' $link =~ s/[\?\_:.\@'(),\`]//xsmg; # known weird characters, but expect more $link =~ s/\///xsmg; $link = lc $link; # remove HTML entities $link =~ s/&\#\d+;//xsmg; # remove escaped entities and remaining noisy characters $link =~ s/[{}&]//xsmg; $toc .= sprintf "%s* [%s](#%s)\n", $indent, $topic, $link; }; } close $fh; return $toc; } ######################################################################## sub print_html { ######################################################################## my ( $self, %options ) = @_; my $fh = $options{fh} // *STDOUT; if ( !$options{body} ) { print {$fh} $self->get_html; return; } my $css = exists $options{css} ? $options{css} : $self->get_css; my $title = exists $options{title} ? $options{title} : $self->get_infile; my $title_section = $title ? "$title" : $EMPTY; my $css_section = $css ? qq{} : $EMPTY; my @head = grep { $_ ? $_ : () } ( $title_section, $css_section ); my $head_section; if (@head) { unshift @head, ''; push @head, ''; $head_section = join "\n", @head; } my $body = $self->get_html; print {$fh} <<"END_OF_TEXT"; $head_section $body END_OF_TEXT return; } ######################################################################## sub main { ######################################################################## my $md = Markdown::Render->new( infile => shift @ARGV, css => $DEFAULT_CSS ); $md->finalize_markdown->render_markdown; $md->print_html; exit 0; } 1; ## no critic (RequirePodSections) __END__ =pod =head1 NAME Markdown::Render - Render markdown as HTML =head1 SYNOPSIS use Markdown::Render; my $md = Markdown::Render->new( infile => 'README.md'); $md->render_markdown->print_html; ...or from the command line to create HTML md-utils.pl -r README.md > README.html ...or from the command line to replace render custom tags md-utils.pl README.md.in > README.md =head1 DESCRIPTION Renders markdown as HTML using either GitHub's API or L. Optionally adds additional metadata to markdown document using custom tags. See L for more details. I as an alternative to using the GitHub API however, there are too many bugs and idiosyncracies in that module. This module will now use L which is not only faster, but seems to be more compliant with GFM.> I here: L> =head1 METHODS AND SUBROUTINES =head2 new new( options ) Any of the options passed to the C method can also be set or retrieved use the C or C methods. =over 5 =item css URL of a CSS file to add to head section of printed HTML. =item engine One of C or C. default: github =item git_user Name of the git user that is used in the C tag. =item git_email Email address of the git user that is used in the C tag. =item infile Path to a file in markdow format. =item markdown Text of the markdown to be rendered. =item mode If using the GitHub API, mode can be either C or C. default: markdown =item no_title Boolean that indicates that no title should be added to the table of contents. default: false =item title Title to be used for the table of contents. =back =head2 finalize_markdown Updates the markdown by interpolating the custom keywords. Invoking this method will create a table of contents and replace keywords with their values. Invoke this method prior to invoking C. Returns the L object. =head2 render_markdown Passes the markdown to GitHub's markdown rendering engine. After invoking this method you can retrieve the processed html by invoking C or create a fully rendered HTML page using the C method. Returns the L object. =head2 print_html print_html(options) Outputs the fully rendered HTML page. =over 5 =item css URL of a CSS style sheet to include in the head section. If no CSS file option is passed a default CSS file will b used. If a CSS element is passed but it is undefined or empty, then no CSS will be specified in the final document. =item title Title to be added in the head section of the document. If no title option is passed the name of the file will be use as the title. If an title option is passed but is undefined or empty, no title element will be added to the document. =back =head1 AUTHOR Rob Lauer - rlauer6@comcast.net =head1 SEE OTHER L L =cut Markdown-Render-1.60.2/META.json0000664000076500007650000000274614721715267015627 0ustar rlauerrlauer{ "abstract" : "Render markdown using GitHub API", "author" : [ "BIGFOOT " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.64, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Markdown-Render", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "6.64", "File::ShareDir::Install" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.64", "File::ShareDir::Install" : "0" } }, "runtime" : { "requires" : { "Class::Accessor::Fast" : "0.51", "Config::Tiny" : "2.28", "Date::Format" : "2.24", "HTTP::Request" : "6.45", "IO::Scalar" : "2.113", "JSON" : "4.10", "LWP::UserAgent" : "6.77", "Readonly" : "2.05", "perl" : "5.016" } }, "test" : { "requires" : {} } }, "provides" : { "Markdown::Render" : { "file" : "lib/Markdown/Render.pm", "version" : "v1.60.2" } }, "release_status" : "stable", "version" : "v1.60.2", "x_serialization_backend" : "JSON::PP version 4.16" } Markdown-Render-1.60.2/README.md.in0000644000076500007650000001420114721715262016050 0ustar rlauerrlauer@TOC@ __Updated @DATE(%Y-%m-%d)@__ by @GIT_USER@ <@GIT_EMAIL@> # README A quick search regarding how to get a table of contents into my markdown yielded only a few hits or projects that seemed a little weighty to me, so here's a little Perl script with just a few dependencies that you might find useful. See [Usage](#usage) for more information. The script will render your markdown as HTML using either the [GitHub API](https://docs.github.com/en/rest/markdown) or the Perl module [Text::Markdown::Discount](https://metacpan.org/pod/Text::Markdown::Discount) A default stylesheet will be applied but you can provide your own style sheet as well. # Installation ## Prerequisites The script has been tested with these versions, but others might work too. | Module | Version | |--------------------------|---------| | `Class::Accessor::Fast` | 0.51 | | `Date::Format` | 2.24 | | `HTTP::Request` | 6.00 | | `IO::Scalar` | 2.113 | | `JSON` | 4.03 | | `LWP::UserAgent` | 6.36 | | `Readonly` | 2.05 | ## Building and Deploying ``` git clone https://github.com/rlauer6/markdown-utils.git make sudo ln -s $(pwd)/markdown-utils/md-utlils.pl /usr/bin/md-utils ``` ## Building an rpm If you want to build an rpm for a RedHat Linux based system, install the `rpm-build` tools. ``` make rpm sudo yum install 'perl(Markdown::Render)' ``` @TOC_BACK(Back to Top)@ ## Building from CPAN ``` cpanm -v Markdown::Render ``` # Usage ``` usage: md-utils options [markdown-file] Utility to add a table of contents and other goodies to your GitHub flavored markdown. - Add @TOC@ where you want to see your TOC. - Add @TOC_BACK@ to insert an internal link to TOC - Add @DATE(format-str)@ where you want to see a formatted date - Add @GIT_USER@ where you want to see your git user name - Add @GIT_EMAIL@ where you want to see your git email address - Use the --render option to render the HTML for the markdown Examples: --------- md-utils README.md.in > README.md md-utils -r README.md.in Options ------- -B, --body default is to add body tag, use --nobody to prevent -b, --both interpolates intermediate file and renders HTML -c, --css css file -e, --engine github, text_markdown (default: github) -h help -i, --infile input file, default: STDIN -m, --mode for GitHub API mode is 'gfm' or 'markdown' (default: markdown) -n, --no-titl do not print a title for the TOC -o, --outfile outfile, default: STDOUT -r, --render render only, does NOT interpolate keywords -R, --raw return raw HTML from engine -t, --title string to use for a custom title, default: "Table of Contents" -v, --version version -N, --nocss no css Tips ---- * Use !# to prevent a header from being include in the table of contents. Add your own custom back to TOC message @TOC_BACK(Back to Index)@ * Date format strings are based on format strings supported by the Perl module 'Date::Format'. The default format is %Y-%m-%d if not format is given. * use the --nobody tag to return the HTML without the wrapper. --raw mode will also return HTML without wrapper ``` # Tips & Tricks 1. Add @TOC@ somewhere in your markdown 1. Use !# to prevent heading from being part of the table of contents 1. Finalize your markdown... ``` cat README.md.in | md-utils.pl > README.md ``` 1. ...or...kick it old school with a `Makefile` if you like ``` FILES = \ README.md.in MARKDOWN=$(FILES:.md.in=.md) HTML=$(MARKDOWN:.md=.html) # interpolate the custom markdown keywords $(MARKDOWN): % : %.in md-utils $< > $@ $(HTML): $(MARKDOWN) md-utils -r $< > $@ all: $(MARKDOWN) $(HTML) markdown: $(MARKDOWN) html: $(HTML) clean: rm -f $(MARKDOWN) $(HTML) ``` 1. ...and then... ``` make all ``` ## @DATE(format)@ Add the current date using a custom format. Essentially calls the Perl function `time2str`. See `perldoc Date::Format`. If no format is present the default is %Y-%m-%d (YYYY-MM-DD). _Best practice would be to use a `Makefile` to generate your final `README.md` from your `README.md.in` template as shown [above](#usage) and generate your `README.md` as the last step before pushing your branch to a repository._ Example: @`DATE(%Y-%m-%d)`@ ## @GIT_EMAIL@ ## @GIT_USER@ If you've done something like: ``` git config --global user.name "Fred Flintstone" git config --global user.email "fflintstone@bedrock.org" ``` or ``` git config --local user.name "Fred Flintstone" git config --local user.email "fflintstone@bedrock.org" ``` ...then you can expect to see those in your markdown, otherwise don't use the tags. @TOC_BACK(Back to Top)@ ## @TOC@ Add this tag anywhere in your markdown in include a table of contents. ## @TOC_BACK(optional text)@ Add @TOC_BACK@ anywhere in your markdown template to insert an internal link back to the table of contents. @`TOC_BACK`@ @`TOC_BACK(Back to Index)`@ @TOC_BACK(Back to Top)@ ## Custom TOC Title Use the `--no-title` option if you don't want the script to insert a header for the TOC. Use the `--title` option if you want a custom header for the TOC. ## Prevent heading from being included in table of contents Precede the heading level with bang (!) and that heading will not be included in the table of contents. @TOC_BACK(Back to Top)@ # Rendering Using the [GiHub rendering API](https://developer.github.com/v3/markdown/), you can create HTML pretty easily. So if you want to preview your markdown...you might try: ``` jq --slurp --raw-input '{"text": "\(.)", "mode": "markdown"}' < README.md | \ curl -s --data @- https://api.github.com/markdown ``` __...but alas you might find that your internal links don't work in that rendered HTML...__ Never fear...the `--render` option of this utility will go ahead and set that right for you and munge the HTML so that internal links really work...or at least they do for me. ``` md-utils --render README.md > README.html ``` @TOC_BACK(Back to Top)@ # Credits Rob Lauer - @TOC_BACK(Back to Top)@ Markdown-Render-1.60.2/ChangeLog0000644000076500007650000000601214721715262015737 0ustar rlauerrlauerWed Nov 27 17:15:26 2024 Rob Lauer [1.60.2]: * Markdown/Render.pm - bump version * md-utils.pl: usage tweak * cpan/Makefile - fabricate target tarball w/version Wed Nov 27 17:02:25 2024 Rob Lauer [1.60.1]: * Markdown/Render.pm - bump version - organize accessors (_fix_header): remove special characters (create_toc): likewise * md-utils.pl - use environment perl * README.md: generated Fri Apr 26 11:28:27 2024 Rob Lauer [1.0.5 - nocss]: * README.md: generated * md-utils.pl: add nocss option * Markdown/Render.pm (new): don't set css if nocss option * .gitignore * t/00-markdown.t Mon Aug 14 16:47:55 2023 Rob Lauer [1.0.4 - Markdown::Render::Discount]: * README.md.in: mention Text::Markdown::Discount * cpan/Makefile - create bin directory if it doesn't exist - make lib/Markdown/Render.pm dependent on .. version * Markdown/Render.pm: use Text::Markdown::Discount Wed May 31 02:25:00 2023 Rob Lauer [1.03 - CPAN distribution]: * README.md.in: CPAN installation * cpan/Makefile: new * Markdown/Render.pm: bump version, pod updates * README.md: generated * .gitignore: cpan/bin, cpan/lib Tue Nov 15 08:30:33 2022 Rob Lauer [1.02]: make rpm * Makefile: make rpm * perl-Markdown.spec: new * .gitignore: rpm/**, *.rpm * README.md.in: added note on rpm building * README.md: generated Mon Nov 14 17:06:34 2022 Rob Lauer [1.02]: * .gitignore: new * README.md.in: updated docs * README.md: generated * Markdown/Render.pm - more pod - bump version (new) - new options css, mode, engine, raw, body (_render_with_text_markdown): use Text::Markdown (_render_with_github): refactored render_markdown() (render_markdown): invoke selected engine (fix_header): new (_fix_header): new (fix_anchors): new (fix_github_hml): new * md-utils.pl: - support new options - implement git user vars (back_to_toc): || not // (finalize_markdown) - don't interpolate inside code blocks - allow empty parens () or no parens for @DATE@, @TOC_BACK@ (create_toc) - remove \@ - remove & after removing HTML entities Sun Nov 13 18:07:31 2022 Rob Lauer [Markdown::Render]: * Markdown/Render.pm: new * md-utils.pl: refactored to use above * README.md: tweaks * Makefile: md-utils is a dependency Wed Nov 17 08:34:27 2021 Rob Lauer [date format]: * md-utils.pl - make default date format %Y-%m-%d - usage tip * Makefile: create md-utils * README.md.in: update date format docs * README.md: generated Sun Mar 21 08:15:39 2021 Rob Lauer * md-utils.pl - remove '/' in links - remove '{}' in links Tue Jan 29 09:33:52 2019 Rob Lauer [0.5]: * md-utils: pl - remove backticks from toc Sun Jan 27 16:11:25 2019 Rob Lauer [0.4]: * md-utils.pl - remove backticks from links in rendered HTML - remove ',' from headings for TOC