func run() error { var ( in = flag.String("i", "-", "path to input Markdown document, or - for standard input") out = flag.String("o", "-", "path to output HTML document, or - for standard output") github = flag.Bool("github", false, "use supported Github-flavored Markdown extensions") // TODO(akavel): tmpl = flag.String("t", "<!doctype html><html lang=en><head><meta charset=utf-8><title></title></head><body>\n{{.}}\n</body></html>", "template for the output HTML document") // see: http://www.brucelawson.co.uk/2010/a-minimal-html5-document/ ) flag.Parse() var err error inf, outf := os.Stdin, os.Stderr if *in != "-" { inf, err = os.Open(*in) if err != nil { return err } defer inf.Close() } if *out != "-" { outf, err = os.Create(*out) if err != nil { return err } defer outf.Close() } var blockDet []mdblock.Detector var spanDet []mdspan.Detector if *github { blockDet = append(blockDet, mdblock.DefaultDetectors[:2]...) blockDet = append(blockDet, mdgithub.FencedCodeBlock{}) blockDet = append(blockDet, mdblock.DefaultDetectors[2:]...) spanDet = append(spanDet, mdspan.DefaultDetectors[:2]...) spanDet = append(spanDet, mdgithub.StrikeThrough{}) spanDet = append(spanDet, mdspan.DefaultDetectors[2:]...) } prep, err := vfmd.QuickPrep(inf) if err != nil { return err } blocks, err := mdblock.QuickParse(bytes.NewReader(prep), mdblock.BlocksAndSpans, blockDet, spanDet) if err != nil { return err } err = vfmd.QuickHTML(outf, blocks) if err != nil { return err } return nil }
func TestHTMLFiles(test *testing.T) { const dir = "testdata/tests" cases := []struct { path string }{ {"block_level/atx_header/blank_text.md"}, {"block_level/atx_header/enclosed_blank_text.md"}, {"block_level/atx_header/hash_in_text.md"}, {"block_level/atx_header/left_only.md"}, {"block_level/atx_header/left_right.md"}, {"block_level/atx_header/more_than_six_hashes.md"}, {"block_level/atx_header/space_in_text.md"}, {"block_level/blockquote/containing_atx_header.md"}, {"block_level/blockquote/containing_blockquote.md"}, {"block_level/blockquote/containing_codeblock.md"}, {"block_level/blockquote/containing_hr.md"}, {"block_level/blockquote/containing_list.md"}, {"block_level/blockquote/containing_setext_header.md"}, {"block_level/blockquote/followed_by_atx_header.md"}, {"block_level/blockquote/followed_by_codeblock.md"}, {"block_level/blockquote/followed_by_hr.md"}, {"block_level/blockquote/followed_by_list.md"}, {"block_level/blockquote/followed_by_para.md"}, {"block_level/blockquote/followed_by_setext_header.md"}, {"block_level/blockquote/indented_differently1.md"}, {"block_level/blockquote/indented_differently2.md"}, {"block_level/blockquote/many_level_nesting.md"}, {"block_level/blockquote/many_lines.md"}, {"block_level/blockquote/many_lines_lazy.md"}, {"block_level/blockquote/many_paras.md"}, {"block_level/blockquote/many_paras_2blank.md"}, {"block_level/blockquote/many_paras_2blank_lazy.md"}, {"block_level/blockquote/many_paras_2blank_lazy2.md"}, {"block_level/blockquote/many_paras_lazy.md"}, {"block_level/blockquote/many_paras_lazy2.md"}, {"block_level/blockquote/no_space_after_gt.md"}, {"block_level/blockquote/one_line.md"}, {"block_level/blockquote/space_before_gt.md"}, {"block_level/codeblock/followed_by_para.md"}, {"block_level/codeblock/html_escaping.md"}, {"block_level/codeblock/many_lines.md"}, {"block_level/codeblock/more_than_four_leading_space.md"}, {"block_level/codeblock/one_blank_line_bw_codeblocks.md"}, {"block_level/codeblock/one_line.md"}, {"block_level/codeblock/two_blank_lines_bw_codeblocks.md"}, {"block_level/codeblock/vs_atx_header.md"}, {"block_level/codeblock/vs_blockquote.md"}, {"block_level/codeblock/vs_hr.md"}, {"block_level/codeblock/vs_list.md"}, {"block_level/horizontal_rule/end_with_space.md"}, {"block_level/horizontal_rule/followed_by_block.md"}, {"block_level/horizontal_rule/loose.md"}, {"block_level/horizontal_rule/sparse.md"}, {"block_level/horizontal_rule/start_with_space.md"}, {"block_level/horizontal_rule/tight.md"}, {"block_level/ordered_list/all_items_loose.md"}, {"block_level/ordered_list/all_items_tight.md"}, {"block_level/ordered_list/all_items_tight_even_with_blanks.md"}, {"block_level/ordered_list/at_end_of_parent_without_blank_line.md"}, {"block_level/ordered_list/bw_unordered_lists.md"}, {"block_level/ordered_list/followed_by_hr.md"}, {"block_level/ordered_list/followed_by_list.md"}, {"block_level/ordered_list/indent_and_sub_blocks.md"}, {"block_level/ordered_list/list_ends_with_2blanks.md"}, {"block_level/ordered_list/many_level_nesting.md"}, {"block_level/ordered_list/many_lines.md"}, {"block_level/ordered_list/many_lines_lazy.md"}, {"block_level/ordered_list/many_paras.md"}, {"block_level/ordered_list/many_paras_2blank.md"}, {"block_level/ordered_list/many_paras_2blank_lazy.md"}, {"block_level/ordered_list/many_paras_lazy.md"}, {"block_level/ordered_list/no_space_after_number.md"}, {"block_level/ordered_list/no_space_before_number.md"}, {"block_level/ordered_list/numbering_from_two.md"}, {"block_level/ordered_list/numbering_not_in_order.md"}, {"block_level/ordered_list/numbers_left_aligned.md"}, {"block_level/ordered_list/numbers_right_aligned.md"}, {"block_level/ordered_list/numbers_wiggly.md"}, {"block_level/ordered_list/one_line.md"}, {"block_level/ordered_list/some_items_loose.md"}, {"block_level/ordered_list/space_before_number.md"}, {"block_level/ordered_list/three_paras_loose.md"}, {"block_level/ordered_list/three_paras_tight.md"}, {"block_level/ordered_list/two_paras_loose.md"}, {"block_level/ordered_list/with_atx_header.md"}, {"block_level/ordered_list/with_blockquote.md"}, {"block_level/ordered_list/with_codeblock.md"}, {"block_level/ordered_list/with_para.md"}, {"block_level/ordered_list/with_setext_header.md"}, {"block_level/paragraph/followed_by_atx_header.md"}, {"block_level/paragraph/followed_by_blockquote.md"}, {"block_level/paragraph/followed_by_codeblock.md"}, {"block_level/paragraph/followed_by_horizontal_rule.md"}, {"block_level/paragraph/followed_by_list.md"}, {"block_level/paragraph/followed_by_setext_header.md"}, {"block_level/paragraph/simple_para.md"}, {"block_level/paragraph/two_paras_1blank.md"}, {"block_level/paragraph/two_paras_2blank.md"}, {"block_level/setext_header/blank_text.md"}, {"block_level/setext_header/enclosed_space_in_underline.md"}, {"block_level/setext_header/leading_space_in_text.md"}, {"block_level/setext_header/leading_space_in_underline.md"}, {"block_level/setext_header/simple.md"}, {"block_level/setext_header/trailing_space_in_underline.md"}, {"block_level/setext_header/vs_atx_header.md"}, {"block_level/setext_header/vs_blockquote.md"}, {"block_level/setext_header/vs_codeblock.md"}, {"block_level/setext_header/vs_list.md"}, {"block_level/unordered_list/all_items_loose.md"}, {"block_level/unordered_list/all_items_tight.md"}, {"block_level/unordered_list/all_items_tight_even_with_blanks.md"}, {"block_level/unordered_list/at_end_of_parent_without_blank_line.md"}, {"block_level/unordered_list/bw_ordered_lists.md"}, {"block_level/unordered_list/changing_bullet.md"}, {"block_level/unordered_list/changing_starter_string.md"}, {"block_level/unordered_list/different_bullet_chars.md"}, {"block_level/unordered_list/followed_by_hr.md"}, {"block_level/unordered_list/followed_by_list.md"}, {"block_level/unordered_list/indent_and_sub_blocks.md"}, {"block_level/unordered_list/list_ends_with_2blanks.md"}, {"block_level/unordered_list/many_level_nesting.md"}, {"block_level/unordered_list/many_lines.md"}, {"block_level/unordered_list/many_lines_lazy.md"}, {"block_level/unordered_list/many_paras.md"}, {"block_level/unordered_list/many_paras_2blank.md"}, {"block_level/unordered_list/many_paras_2blank_lazy.md"}, {"block_level/unordered_list/many_paras_lazy.md"}, {"block_level/unordered_list/no_space_after_bullet.md"}, {"block_level/unordered_list/no_space_before_bullet.md"}, {"block_level/unordered_list/one_line.md"}, {"block_level/unordered_list/some_items_loose.md"}, {"block_level/unordered_list/space_before_bullet.md"}, {"block_level/unordered_list/three_paras_loose.md"}, {"block_level/unordered_list/three_paras_tight.md"}, {"block_level/unordered_list/two_paras_loose.md"}, {"block_level/unordered_list/with_atx_header.md"}, {"block_level/unordered_list/with_blockquote.md"}, {"block_level/unordered_list/with_codeblock.md"}, {"block_level/unordered_list/with_para.md"}, {"block_level/unordered_list/with_setext_header.md"}, {"span_level/automatic_links/ending_with_punctuation.md"}, {"span_level/automatic_links/mail_url_in_angle_brackets.md"}, {"span_level/automatic_links/web_url_in_angle_brackets.md"}, {"span_level/automatic_links/web_url_without_angle_brackets.md"}, {"span_level/code/end_of_codespan.md"}, {"span_level/code/multiline.md"}, {"span_level/code/vs_emph.md"}, {"span_level/code/vs_image.md"}, {"span_level/code/vs_link.md"}, {"span_level/code/well_formed.md"}, {"span_level/emphasis/emphasis_tag_combinations.md"}, {"span_level/emphasis/intertwined.md"}, {"span_level/emphasis/intraword.md"}, {"span_level/emphasis/nested_homogenous.md"}, {"span_level/emphasis/opening_and_closing_tags.md"}, {"span_level/emphasis/simple.md"}, {"span_level/emphasis/within_whitespace.md"}, {"span_level/image/direct_link.md"}, {"span_level/image/direct_link_with_2separating_spaces.md"}, {"span_level/image/direct_link_with_separating_newline.md"}, {"span_level/image/direct_link_with_separating_space.md"}, {"span_level/image/incomplete.md"}, {"span_level/image/link_text_with_newline.md"}, {"span_level/image/link_with_parenthesis.md"}, {"span_level/image/multiple_ref_id_definitions.md"}, {"span_level/image/nested_images.md"}, {"span_level/image/ref_case_sensitivity.md"}, {"span_level/image/ref_id_matching.md"}, {"span_level/image/ref_link.md"}, {"span_level/image/ref_link_empty.md"}, {"span_level/image/ref_link_self.md"}, {"span_level/image/ref_link_with_2separating_spaces.md"}, {"span_level/image/ref_link_with_separating_newline.md"}, {"span_level/image/ref_link_with_separating_space.md"}, {"span_level/image/ref_resolution_within_other_blocks.md"}, {"span_level/image/square_brackets_in_link_or_ref.md"}, {"span_level/image/two_consecutive_refs.md"}, {"span_level/image/unused_ref.md"}, {"span_level/image/url_escapes.md"}, {"span_level/image/url_in_angle_brackets.md"}, {"span_level/image/url_special_chars.md"}, {"span_level/image/url_whitespace.md"}, {"span_level/image/vs_code.md"}, {"span_level/image/vs_emph.md"}, {"span_level/image/within_link.md"}, {"span_level/link/direct_link.md"}, {"span_level/link/direct_link_with_2separating_spaces.md"}, {"span_level/link/direct_link_with_separating_newline.md"}, {"span_level/link/direct_link_with_separating_space.md"}, {"span_level/link/incomplete.md"}, {"span_level/link/link_text_with_newline.md"}, {"span_level/link/link_title.md"}, {"span_level/link/link_with_parenthesis.md"}, {"span_level/link/multiple_ref_id_definitions.md"}, {"span_level/link/nested_links.md"}, {"span_level/link/ref_case_sensitivity.md"}, {"span_level/link/ref_id_matching.md"}, {"span_level/link/ref_link.md"}, {"span_level/link/ref_link_empty.md"}, {"span_level/link/ref_link_self.md"}, {"span_level/link/ref_link_with_2separating_spaces.md"}, {"span_level/link/ref_link_with_separating_newline.md"}, {"span_level/link/ref_link_with_separating_space.md"}, {"span_level/link/ref_resolution_within_other_blocks.md"}, {"span_level/link/square_brackets_in_link_or_ref.md"}, {"span_level/link/two_consecutive_refs.md"}, {"span_level/link/unused_ref.md"}, {"span_level/link/url_escapes.md"}, {"span_level/link/url_in_angle_brackets.md"}, {"span_level/link/url_special_chars.md"}, {"span_level/link/url_whitespace.md"}, {"span_level/link/vs_code.md"}, {"span_level/link/vs_emph.md"}, {"span_level/link/vs_image.md"}, {"text_processing/utf8/invalid_unicode.md"}, {"text_processing/utf8/multibyte_chars.md"}, {"text_processing/utf8/not_enough_continuation_bytes.md"}, {"text_processing/utf8/overlong_encoding.md"}, {"text_processing/utf8/stray_continuation_bytes.md"}, {"text_processing/utf8_bom/code_block_with_bom.md"}, {"text_processing/utf8_bom/eof_with_incomplete_bom.md"}, {"text_processing/utf8_bom/list_with_bom.md"}, {"text_processing/utf8_bom/text_with_bom.md"}, } // Patches to what I believe are bugs in the original testdata, when // confronted with the spec. replacer := strings.NewReplacer( "'>'", "'>'", "'", "'", `"/>`, `" />`, // TODO(akavel): consider fixing (?) below line in our code """, """, // TODO(akavel): ...do something sensible so that this doesn't fail the diff.Diff?... "%5C", "%5c", // TODO(akavel): mitigate this somehow? or not? X| `<img src="url)"`, `<img src="url%29"`, `<img src="url("`, `<img src="url%28"`, `<a href="url)"`, `<a href="url%29"`, `<a href="url("`, `<a href="url%28"`, // TODO(akavel): I assume html/template has this ok, but need to verify at some point `<img src="url*#$%%5E&%5C%7E"`, `<img src="url*#$%25%5e&%5c~"`, `<a href="url*#$%%5E&%5C%7E"`, `<a href="url*#$%25%5e&%5c~"`, // TODO(akavel): or not TODO? HTML entities currently not supported, incl. in URLs `<a href="http://göögle.com">`, `<a href="http://g&ouml;&ouml;gle.com">`, `<img src="http://göögle.com"`, `<img src="http://g&ouml;&ouml;gle.com"`, // Various newline/space fixes in the testcases. "\n<li>\n Parent list\n\n <ol>", "\n<li>Parent list<ol>", "<code>Code block included in list\n</code>", "<code> Code block included in list\n</code>", "And another\n\n<p>Another para", "And another<p>Another para", "<li>Level 1\n<ol>", "<li>Level 1<ol>", "<li>Level 2\n<p>", "<li>Level 2<p>", "<li>Level 3\n<p>", "<li>Level 3<p>", "Level 4\n<ul>", "Level 4<ul>", "<li>Level 2\n<ol>", "<li>Level 2<ol>", "And another\n<p>", "And another<p>", "<li>Parent list\n <ul>", "<li>Parent list<ul>", "<li>Third list\n<ul>", "<li>Third list<ul>", "<pre><code>Code block included in list</code></pre>", "<pre><code>Code block included in list\n</code></pre>", "<pre><code>Code block not included in list</code></pre>", "<pre><code>Code block not included in list\n</code></pre>", "<li>Level 1\n<ul>", "<li>Level 1<ul>", "<li>Level 2\n<ul>", "<li>Level 2<ul>", ) for _, c := range cases { test.Log(c.path) subdir, fname := path.Split(c.path) fname = strings.TrimSuffix(fname, ".md") data, err := ioutil.ReadFile(filepath.Join(dir, c.path)) if err != nil { test.Error(err) continue } expectedOutput, err := ioutil.ReadFile( filepath.Join(dir, subdir, "expected", fname+".html")) if err != nil { test.Error(err) continue } prep, _ := QuickPrep(bytes.NewReader(data)) blocks, err := mdblock.QuickParse(bytes.NewReader(prep), mdblock.BlocksAndSpans, nil, nil) if err != nil { test.Errorf("case %s error: %s", c.path, err) continue } buf := bytes.NewBuffer(nil) err = QuickHTML(buf, blocks) if err != nil { test.Error(err) continue } html := simplifyHtml(buf.Bytes()) expectedOutput = []byte(replacer.Replace(string(simplifyHtml(expectedOutput)))) if !bytes.Equal(html, expectedOutput) { test.Errorf("case %s blocks:\n%s", c.path, spew.Sdump(blocks)) test.Errorf("case %s expected vs. got DIFF:\n%s", c.path, diff.Diff(string(expectedOutput), string(html))) } } }