Markdown-Render-2.0.4/0000755000175100017510000000000015174723227014076 5ustar rlauerrlauerMarkdown-Render-2.0.4/META.json0000664000175100017510000000306615174723227015526 0ustar rlauerrlauer{ "abstract" : "Render markdown using GitHub API", "author" : [ "BIGFOOT " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.76, 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" : { "CLI::Simple" : "v1.0.11", "CLI::Simple::Constants" : "v1.0.11", "Class::Accessor::Fast" : "0.51", "Config::Tiny" : "2.30", "Date::Format" : "2.24", "IO::Scalar" : "2.113", "IO::Socket::SSL" : "0", "JSON" : "4.10", "Net::SSLeay" : "0", "Readonly" : "2.05", "perl" : "5.016" } }, "test" : { "requires" : {} } }, "provides" : { "Markdown::Render" : { "file" : "lib/Markdown/Render.pm", "version" : "v2.0.4" } }, "release_status" : "stable", "version" : "v2.0.4", "x_serialization_backend" : "JSON::PP version 4.16" } Markdown-Render-2.0.4/META.yml0000664000175100017510000000165315174723227015356 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.76, 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: v2.0.4 requires: CLI::Simple: v1.0.11 CLI::Simple::Constants: v1.0.11 Class::Accessor::Fast: '0.51' Config::Tiny: '2.30' Date::Format: '2.24' IO::Scalar: '2.113' IO::Socket::SSL: '0' JSON: '4.10' Net::SSLeay: '0' Readonly: '2.05' perl: '5.016' version: v2.0.4 x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Markdown-Render-2.0.4/README.md0000644000175100017510000001615315174723225015361 0ustar rlauerrlauer# Table of Contents * [README](#readme) * [Installation](#installation) * [Prerequisites](#prerequisites) * [Building and Deploying](#building-and-deploying) * [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) * [License](#license) __Updated 2026-04-30__ by anonymouse # 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 | |--------------------------|---------| | `CLI::Simple | 1.0.11 | | `Class::Accessor::Fast` | 0.51 | | `Config::Tiny` | 2.30 | | `Date::Format` | 2.24 | | `HTTP::Request` | 6.00 | | `IO::Scalar` | 2.113 | | `IO::Socket::SSL` | | | `JSON` | 4.03 | | `HTTP::Tiny` | 6.36 | | `Net::SSLeay` | | | `Readonly` | 2.05 | ## Building and Deploying The build will now create a CPAN distribution. ``` git clone https://github.com/rlauer6/markdown-utils.git make && make cpan ``` To deploy the application use `cpanm` ``` curl -L https://cpanmin.us | perl - --sudo App::cpanminus cpanm -n -v cpan/Markdown-Render-*.tar.gz ``` ## Building from CPAN ``` cpanm -v Markdown::Render ``` # Usage ``` Usage: md-utils.pl options [markdown-file] Utility to add a table of contents and other goodies to your GitHub flavored markdown. * @TOC@ where you want to see your TOC. * @TOC_BACK@ to insert an internal link to TOC * @DATE(format-str)@ where you want to see a formatted date * @GIT_USER@ where you want to see your git user name * @GIT_EMAIL@ where you want to see your git email address * 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. ``` # 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) # License This software is licensed under the same terms as Perl. [Back to Top](#table-of-contents) Markdown-Render-2.0.4/MANIFEST0000644000175100017510000000046615174723227015235 0ustar rlauerrlauer00-markdown.t bin/md-utils.pl ChangeLog lib/Markdown/Render.pm Makefile.PL MANIFEST This list of files MANIFEST.SKIP 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-2.0.4/ChangeLog0000644000175100017510000001211415174723225015645 0ustar rlauerrlauerSat Apr 11 08:12:02 2026 Rob Lauer [2.0.4]: * cpan/Makefile - do not clean requires, provides - -D requires * cpan/requires - added to repo - +Net::SSLeasy, IO::Socket::SSL * cpan/provides: added to repo * cpan/extra: added to repo * t/00-markdown.t: added to repo * t/files/README.md.in: added to repo * release-notes-2.0.4.md: new * VERSION: bump * release-notes.mk: new * Makefile: add * .gitignore: *.html, *.diffs, *.lst * README.md: generated * lib/Markdown/Render.pm.in (_render_with_github): - replaced LWP::UserAgent with HTTP::Tiny Mon Feb 16 07:35:28 2026 Rob Lauer [2.0.3]: * VERSION: bump * Makefile: minor fixes to build all artifacts * README.md: generated * lib/Markdown/Render.pm.in - whitespace (print_html): initialize $head_section Tue Feb 3 16:30:18 2026 Rob Lauer [2.0.2]: * VERSION: bump * bin/md-utils.pl.in (cmd_render) - there are no set_git_user, or set_git_email methods? Fri Oct 10 10:25:00 2025 Rob Lauer [2.0.1]: * lib/Markdown/Render.pm.in (create_toc): skip code blocks * VERSION: bump Thu Oct 9 20:52:16 2025 Rob Lauer [2.0.0]: * VERSION: new * README.md: generated * version.mk: new * .gitignore * ChangeLog * Makefile - make source from .in files - version targets - get version from VERSION * bin/md-utils.pl.in - refactored to use CLI::Simple * cpan/Makefile: slight refactoring * lib/Markdown/Render.pm.in - use *PACKAGE_VERSION@ (_fix_header): remove '*' (create_toc): likewise * Markdown/Render.pm -> lib/Markdown/Render.pm.in * md-utils.pl -> bin/md-utils.pl.in * .gitignore - ignore .pm, .pl, temp files * perl-Markdown-Render.spec: removed Tue Aug 26 15:55:36 2025 Rob Lauer [1.60.4]: * ChangeLog: include updated ChangeLog in tarball Tue Aug 26 15:23:51 2025 Rob Lauer [1.60.3]: * .gitignore: *.tmp * Markdown/Render.pm - bump version (fix_anchors): anchor then header (_fix_header): likewise (_render_with_github): fix_anchors Wed 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 Markdown-Render-2.0.4/README.md.in0000644000175100017510000001463415174723225015770 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 | |--------------------------|---------| | `CLI::Simple | 1.0.11 | | `Class::Accessor::Fast` | 0.51 | | `Config::Tiny` | 2.30 | | `Date::Format` | 2.24 | | `HTTP::Request` | 6.00 | | `IO::Scalar` | 2.113 | | `IO::Socket::SSL` | | | `JSON` | 4.03 | | `HTTP::Tiny` | 6.36 | | `Net::SSLeay` | | | `Readonly` | 2.05 | ## Building and Deploying The build will now create a CPAN distribution. ``` git clone https://github.com/rlauer6/markdown-utils.git make && make cpan ``` To deploy the application use `cpanm` ``` curl -L https://cpanmin.us | perl - --sudo App::cpanminus cpanm -n -v cpan/Markdown-Render-*.tar.gz ``` ## Building from CPAN ``` cpanm -v Markdown::Render ``` # Usage ``` Usage: md-utils.pl options [markdown-file] Utility to add a table of contents and other goodies to your GitHub flavored markdown. * @TOC@ where you want to see your TOC. * @TOC_BACK@ to insert an internal link to TOC * @DATE(format-str)@ where you want to see a formatted date * @GIT_USER@ where you want to see your git user name * @GIT_EMAIL@ where you want to see your git email address * 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. ``` # 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)@ # License This software is licensed under the same terms as Perl. @TOC_BACK(Back to Top)@ Markdown-Render-2.0.4/MANIFEST.SKIP0000644000175100017510000000004115174723227015767 0ustar rlauerrlauerMakefile$ MYMETA.json MYMETA.yml Markdown-Render-2.0.4/Makefile.PL0000644000175100017510000000364415174723226016056 0ustar rlauerrlauer# autogenerated by /home/rlauer/bin/make-cpan-dist.pl on Thu Apr 30 14:59:34 2026 use strict; use warnings; use ExtUtils::MakeMaker; use File::ShareDir::Install; $File::ShareDir::Install::INCLUDE_DOTFILES = 1; 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'], MAN1PODS => { 'bin/md-utils.pl' => 'blib/man1/md-utils.1' }, PREREQ_PM => { 'CLI::Simple' => '1.0.11', 'CLI::Simple::Constants' => '1.0.11', 'Class::Accessor::Fast' => '0.51', 'Config::Tiny' => '2.30', 'Date::Format' => '2.24', 'IO::Scalar' => '2.113', 'IO::Socket::SSL' => '0', 'JSON' => '4.10', 'Net::SSLeay' => '0', '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' => '2.0.4' } } } ); 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-2.0.4/bin/0000755000175100017510000000000015174723227014646 5ustar rlauerrlauerMarkdown-Render-2.0.4/bin/md-utils.pl0000755000175100017510000001451215174723225016745 0ustar rlauerrlauer#!/usr/bin/env perl package Markdown::Render::CLI; use strict; use warnings; use Carp; use Cwd; use Config::Tiny; use CLI::Simple::Constants qw(:chars :booleans); 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 = '2.0.4'; use parent qw(CLI::Simple); ######################################################################## sub choose(&) { ## no critic ######################################################################## return $_[0]->(); } ######################################################################## sub cmd_version { ######################################################################## my ($self) = @_; my ($name) = File::Basename::fileparse( $PROGRAM_NAME, qr/[.][^.]+$/xsm ); print {*STDOUT} "$name $VERSION\n"; return $SUCCESS; } ######################################################################## sub get_git_user { ######################################################################## my ($self) = @_; 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 ); } ######################################################################## sub new { ######################################################################## my ( $class, @args ) = @_; # spoof CLI::Simple - force command render and preserve argument position unshift @ARGV, 'render'; return $class->SUPER::new(@args); } ######################################################################## sub init { ######################################################################## my ($self) = @_; if ( $self->get_raw ) { $self->set_body($FALSE); } return; } ######################################################################## sub cmd_render { ######################################################################## my ($self) = @_; my $markdown; my $infile = $self->get_infile; if ( !$infile ) { ($infile) = $self->get_args; my $fh = choose { return *STDIN if !$infile && !-t STDIN; open my $fh, '<', $infile or croak sprintf "ERROR: could not open %s for reading\n%s", $infile, $OS_ERROR; return $fh; }; local $RS = undef; $markdown = <$fh>; } my ( $git_user, $git_email ) = $self->get_git_user(); my $md = Markdown::Render->new( markdown => $markdown, body => $self->get_body, css => $self->get_css, engine => $self->get_engine, git_email => $git_email, user => $git_user, html => $self->get_html, mode => $self->get_mode, no_title => $self->get_no_title, raw => $self->get_raw, render => $self->get_render, title => $self->get_title, ); my $ofh = choose { my $outfile = $self->get_outfile; return *STDOUT if !$outfile; open my $fh, '>', $outfile or croak sprintf "ERROR: could not open output file\n%s", $self->get_outfile, $OS_ERROR; return $fh; }; eval { if ( $self->get_both ) { $md->finalize_markdown->render_markdown; $md->print_html( fh => $ofh, body => $self->get_body, css => $self->get_css, title => $self->get_title, ); } elsif ( $self->get_render ) { $md->render_markdown; $md->print_html( fh => $ofh, body => $self->get_body, css => $self->get_css, title => $self->get_title, ); } else { $md->finalize_markdown; print {$ofh} $md->get_markdown; } }; croak "ERROR: $EVAL_ERROR" if $EVAL_ERROR; close $ofh; return $SUCCESS; } ######################################################################## sub main { ######################################################################## my @option_specs = 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 ); my %commands = ( version => \&cmd_version, render => \&cmd_render ); my $cli = Markdown::Render::CLI->new( option_specs => \@option_specs, commands => \%commands, default_options => { body => $TRUE, }, extra_options => [qw(git_user git_email html)], ); return $cli->run(); } exit main(); __END__ =pod =head1 USAGE md-utils.pl options [markdown-file] Utility to add a table of contents and other goodies to your GitHub flavored markdown. =over 4 =item * @TOC@ where you want to see your TOC. =item * @TOC_BACK@ to insert an internal link to TOC =item * @DATE(format-str)@ where you want to see a formatted date =item * @GIT_USER@ where you want to see your git user name =item * @GIT_EMAIL@ where you want to see your git email address =item * the --render option to render the HTML for the markdown =back =head2 Examples md-utils.pl README.md.in > README.md md-utils.pl -r README.md.in =head2 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 =head2 Tips =over 4 =item * 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)@ =item * 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. =item * use the --nobody tag to return the HTML without the wrapper. C<--raw> mode will also return HTML without wrapper. =back =cut Markdown-Render-2.0.4/lib/0000755000175100017510000000000015174723227014644 5ustar rlauerrlauerMarkdown-Render-2.0.4/lib/Markdown/0000755000175100017510000000000015174723227016426 5ustar rlauerrlauerMarkdown-Render-2.0.4/lib/Markdown/Render.pm0000644000175100017510000003375715174723225020220 0ustar rlauerrlauerpackage Markdown::Render; use strict; use warnings; use Carp; use Data::Dumper; use English qw(-no_match_vars); use HTTP::Tiny; use IO::Scalar; use JSON; use List::Util qw(none); our $VERSION = '2.0.4'; 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::Scalar our $GITHUB_API => 'https://api.github.com/markdown'; Readonly::Scalar our $EMPTY => q{}; Readonly::Scalar our $SPACE => q{ }; Readonly::Scalar our $TOC_TITLE => 'Table of Contents'; Readonly::Scalar our $TOC_BACK => 'Back to Table of Contents'; Readonly::Scalar our $DEFAULT_CSS => 'https://cdn.simplecss.org/simple-v1.css'; Readonly::Scalar our $TRUE => 1; Readonly::Scalar our $FALSE => 0; 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; # sample: #

Table of Contents

while ( my $line = <$fh> ) { # remove garbage if there... if ( $line =~ m{]*?)>([^<]+?)(.*?)}xsm ) { $line = "$3\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}, $anchor, $anchor, $hn, $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 $mode = $self->get_mode; if ( none { $mode eq $_ } qw(gfm markdown) ) { $mode = 'markdown'; } my $ua = HTTP::Tiny->new; my $rsp = $ua->post( $GITHUB_API, { content => to_json( { text => $markdown, mode => $mode } ), headers => { 'Content-Type' => 'application/json' }, } ); croak 'could not render using GitHub API: ' . $rsp->{status} . ' ' . $rsp->{reason} if !$rsp->{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 { $self->fix_anchors( $rsp->{content} ); return $self->fix_github_html( $self->get_html ); } } ######################################################################## 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"; my $in_code_block = $FALSE; while (<$fh>) { chomp; if (/^\s*```/xsm) { $in_code_block = $in_code_block ? $FALSE : $TRUE; next; } next if $in_code_block; /^(\#+)\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 = q{}; 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 LICENSE This software is licensed under the same terms as Perl. =head1 SEE OTHER L L =cut Markdown-Render-2.0.4/00-markdown.t0000644000175100017510000000356615174723225016332 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