diff --git a/deps.edn b/deps.edn index cf6f65fb..29f6b7b7 100644 --- a/deps.edn +++ b/deps.edn @@ -134,7 +134,8 @@ aysylu/loom {:mvn/version "1.0.2"} com.layerware/hugsql-core {:mvn/version "0.5.3"} com.github.seancorfield/expectations {:mvn/version "2.0.157"} - com.rpl/specter {:mvn/version "1.1.4"}} + com.rpl/specter {:mvn/version "1.1.4"} + com.github.askonomm/clarktown {:mvn/version "1.1.2"}} :classpath-overrides {org.clojure/clojure nil org.clojure/spec.alpha nil}} :clj-nvd diff --git a/doc/libraries.csv b/doc/libraries.csv index c12c85e7..b568ad77 100644 --- a/doc/libraries.csv +++ b/doc/libraries.csv @@ -17,6 +17,7 @@ clojure-csv/clojure-csv,https://github.com/davidsantiago/clojure-csv clojure-msgpack/clojure-msgpack,https://github.com/edma2/clojure-msgpack clojure-term-colors/clojure-term-colors,https://github.com/trhura/clojure-term-colors com.exoscale/lingo,https://github.com/exoscale/lingo +com.github.askonomm/clarktown,https://github.com/askonomm/clarktown com.github.seancorfield/expectations,https://github.com/clojure-expectations/clojure-test com.github.seancorfield/honeysql,https://github.com/seancorfield/honeysql com.grammarly/omniconf,https://github.com/grammarly/omniconf diff --git a/doc/projects.md b/doc/projects.md index d136ed88..463e5857 100644 --- a/doc/projects.md +++ b/doc/projects.md @@ -808,6 +808,10 @@ Ahead-of-time function scheduler. Compatible with babashka 0.7.7+. Graph library for Clojure. Compatible with babashka 0.7.8+. +### [Clarktown](https://github.com/askonomm/clarktown) + +An extensible and modular zero-dependency, pure-Clojure Markdown parser. + ## Pods [Babashka pods](https://github.com/babashka/babashka.pods) are programs that can @@ -875,7 +879,7 @@ A babashka script to obtain covid-19 related information. ### [bb-spotify](https://github.com/kolharsam/bb-spotify) -Contol your spotify player using babashka. +Control your spotify player using babashka. ### [lambdaisland/open-source](https://github.com/lambdaisland/open-source) diff --git a/test-resources/lib_tests/bb-tested-libs.edn b/test-resources/lib_tests/bb-tested-libs.edn index bca25eba..60c502de 100644 --- a/test-resources/lib_tests/bb-tested-libs.edn +++ b/test-resources/lib_tests/bb-tested-libs.edn @@ -112,4 +112,5 @@ com.layerware/hugsql-core {:test-namespaces (hugsql.babashka-test)} com.github.seancorfield/expectations {:git-url "https://github.com/clojure-expectations/clojure-test", :test-namespaces (expectations.clojure.test-test), :git-sha "b30fefd97d9eb7d1f47e06956521f354cb926b03"} com.rpl/specter {:git-url "https://github.com/redplanetlabs/specter", :test-namespaces (com.rpl.specter.cljs-test-helpers com.rpl.specter.test-helpers com.rpl.specter.core-test com.rpl.specter.zipper-test), :git-sha "67e86806020b9d02fbca8cdb1efad3002fc81a32"} + com.github.askonomm/clarktown {:git-url "https://github.com/askonomm/clarktown", :test-namespaces (clarktown.core-test clarktown.parsers.horizontal-line-block-test clarktown.parsers.italic-test clarktown.parsers.link-and-image-test clarktown.parsers.empty-block-test clarktown.parsers.inline-code-test clarktown.parsers.heading-block-test clarktown.parsers.bold-test clarktown.parsers.quote-block-test clarktown.parsers.code-block-test clarktown.parsers.strikethrough-test), :git-sha "059bfa7bd9bfdde0c75646bf1dfc20d23da8a02c"} } diff --git a/test-resources/lib_tests/clarktown/core.md b/test-resources/lib_tests/clarktown/core.md new file mode 100644 index 00000000..6a5b2f17 --- /dev/null +++ b/test-resources/lib_tests/clarktown/core.md @@ -0,0 +1,93 @@ +Lorem ipsum dolor **sit** amet. Lorem ipsum *dolor* _sit_ __amet__. + +There's a [link here](https://example.com/that_has_things?!???!#in-it). + +1. List item +2. Another list item + 1. Sub list item + 2. Another sub list item + 1. Sub sub list item + 3. Continuing sub list item +3. Continuing list item + +```javascript +// Detect horizontal line block +function isHorizontalLineBlock(block) { + return block === "***"; +} + +// Render horizontal line block +function horizontalLineBlock(block) { + return `
Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet.
+ +There's a link here.
+ +// Detect horizontal line block
+function isHorizontalLineBlock(block) {
+ return block === "***";
+}
+
+// Render horizontal line block
+function horizontalLineBlock(block) {
+ return `<hr>`;
+}
+
+// Compose an array of parsers
+const parsers = [{
+ matcher: isHorizontalLineBlock,
+ renderers: [horizontalLineBlock]
+}];
+
+// And finally, our parser itself
+function markdownToHTML(markdown) {
+ // Create blocks
+ const blocks = content.split(/\n\n/);
+
+ // Parse blocks
+ const parsedBlocks = blocks.map((block) => {
+ // Let's find a parser that has a matcher that matches
+ const parser = parsers.find((parser) => parser.matcher(block));
+
+ // If match was found, let's run our renderers over `block`
+ if (parser) {
+ for (const renderer of match.renderers) {
+ block = renderer(block);
+ }
+ }
+
+ return block;
+ });
+
+ // And at last, join the blocks together for one big block.
+ return parsedBlocks.join("");
+}
+
+This is bold italic text and this is also. What about italic text that has bold text?
+ +Testing paragraph right before a code block
+ +code goes here
+
+Paragraph right after heading
diff --git a/test-resources/lib_tests/clarktown/core_test.clj b/test-resources/lib_tests/clarktown/core_test.clj new file mode 100644 index 00000000..86ca1765 --- /dev/null +++ b/test-resources/lib_tests/clarktown/core_test.clj @@ -0,0 +1,15 @@ +(ns clarktown.core-test + (:require + ;; BB-TEST-PATCH: require clojure.string for split-lines patch below + [clojure.string :as str] + [clojure.test :refer [deftest testing is]] + [clojure.java.io :as io] + [clarktown.core :as core])) + + +(deftest overall-test + (testing "Overall" + ;; BB-TEST-PATCH: library uses hard-coded \n, so using split-lines for platform-agnostic testing + ;; BB-TEST-PATCH: change file paths to match bb folder structure (and copy resource files) + (is (= (str/split-lines (core/render (slurp (io/file (io/resource "clarktown/core.md"))))) + (str/split-lines (slurp (io/file (io/resource "clarktown/core_result.html")))))))) diff --git a/test-resources/lib_tests/clarktown/parsers/bold_test.clj b/test-resources/lib_tests/clarktown/parsers/bold_test.clj new file mode 100644 index 00000000..a082d418 --- /dev/null +++ b/test-resources/lib_tests/clarktown/parsers/bold_test.clj @@ -0,0 +1,18 @@ +(ns clarktown.parsers.bold-test + (:require + [clojure.test :refer [deftest testing is]] + [clarktown.parsers.bold :as bold])) + + +(deftest bold-test + (testing "Creating bold text with two surrounding asterisk characters" + (is (= "This is bold." + (bold/render "**This is bold.**" nil)))) + + (testing "Creating bold text with two surrounding underscore characters" + (is (= "This is bold." + (bold/render "__This is bold.__" nil)))) + + (testing "Creating bold text with both underscores and asterisks mixed" + (is (= "Hi, my name is John, what is your name?" + (bold/render "Hi, my name is **John**, what is __your name?__" nil))))) \ No newline at end of file diff --git a/test-resources/lib_tests/clarktown/parsers/code_block.md b/test-resources/lib_tests/clarktown/parsers/code_block.md new file mode 100644 index 00000000..8e6f863f --- /dev/null +++ b/test-resources/lib_tests/clarktown/parsers/code_block.md @@ -0,0 +1,41 @@ +```javascript +// Detect horizontal line block +function isHorizontalLineBlock(block) { + return block === "***"; +} + +// Render horizontal line block +function horizontalLineBlock(block) { + return `// Detect horizontal line block
+function isHorizontalLineBlock(block) {
+ return block === "***";
+}
+
+// Render horizontal line block
+function horizontalLineBlock(block) {
+ return `<hr>`;
+}
+
+// Compose an array of parsers
+const parsers = [{
+ matcher: isHorizontalLineBlock,
+ renderers: [horizontalLineBlock]
+}];
+
+// And finally, our parser itself
+function markdownToHTML(markdown) {
+ // Create blocks
+ const blocks = content.split(/\n\n/);
+
+ // Parse blocks
+ const parsedBlocks = blocks.map((block) => {
+ // Let's find a parser that has a matcher that matches
+ const parser = parsers.find((parser) => parser.matcher(block));
+
+ // If match was found, let's run our renderers over `block`
+ if (parser) {
+ for (const renderer of match.renderers) {
+ block = renderer(block);
+ }
+ }
+
+ return block;
+ });
+
+ // And at last, join the blocks together for one big block.
+ return parsedBlocks.join("");
+}
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/code_block_result.html b/test-resources/lib_tests/clarktown/parsers/code_block_result.html
new file mode 100644
index 00000000..21b92144
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/code_block_result.html
@@ -0,0 +1,39 @@
+// Detect horizontal line block
+function isHorizontalLineBlock(block) {
+ return block === "***";
+}
+
+// Render horizontal line block
+function horizontalLineBlock(block) {
+ return `<hr>`;
+}
+
+// Compose an array of parsers
+const parsers = [{
+ matcher: isHorizontalLineBlock,
+ renderers: [horizontalLineBlock]
+}];
+
+// And finally, our parser itself
+function markdownToHTML(markdown) {
+ // Create blocks
+ const blocks = content.split(/\n\n/);
+
+ // Parse blocks
+ const parsedBlocks = blocks.map((block) => {
+ // Let's find a parser that has a matcher that matches
+ const parser = parsers.find((parser) => parser.matcher(block));
+
+ // If match was found, let's run our renderers over `block`
+ if (parser) {
+ for (const renderer of match.renderers) {
+ block = renderer(block);
+ }
+ }
+
+ return block;
+ });
+
+ // And at last, join the blocks together for one big block.
+ return parsedBlocks.join("");
+}
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/code_block_test.clj b/test-resources/lib_tests/clarktown/parsers/code_block_test.clj
new file mode 100644
index 00000000..b965e5fa
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/code_block_test.clj
@@ -0,0 +1,18 @@
+(ns clarktown.parsers.code-block-test
+ (:require
+ ;; require clojure.string to accomodate line break hack below
+ [clojure.string :as str]
+ [clojure.test :refer [deftest testing is]]
+ [clojure.java.io :as io]
+ [clarktown.parsers.code-block :as code-block]))
+
+;; BB-TEST-PATCH: change paths to match folder structure (and copy resource files)
+;; BB-TEST-PATCH: use split-lines to make tests platform-agnostic
+(deftest code-block-test
+ (testing "Code block with language specification"
+ (is (= (str/split-lines (slurp (io/file (io/resource "clarktown/parsers/code_block_result.html"))))
+ (str/split-lines (code-block/render (slurp (io/file (io/resource "clarktown/parsers/code_block.md"))) nil)))))
+
+ (testing "Code block with NO language specification"
+ (is (= (str/split-lines (slurp (io/file (io/resource "clarktown/parsers/code_block_no_language_result.html"))))
+ (str/split-lines (code-block/render (slurp (io/file (io/resource "clarktown/parsers/code_block_no_language.md"))) nil))))))
diff --git a/test-resources/lib_tests/clarktown/parsers/empty_block_test.clj b/test-resources/lib_tests/clarktown/parsers/empty_block_test.clj
new file mode 100644
index 00000000..a8d89c48
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/empty_block_test.clj
@@ -0,0 +1,14 @@
+(ns clarktown.parsers.empty-block-test
+ (:require
+ [clojure.test :refer [deftest testing is]]
+ [clarktown.parsers.empty-block :as empty-block]))
+
+
+(deftest empty-block-test
+ (testing "Rendering an empty block"
+ (is (= (empty-block/render "" nil)
+ "")))
+
+ (testing "Checking an empty block"
+ (is (true? (empty-block/is? "")))
+ (is (true? (empty-block/is? " ")))))
diff --git a/test-resources/lib_tests/clarktown/parsers/heading_block_test.clj b/test-resources/lib_tests/clarktown/parsers/heading_block_test.clj
new file mode 100644
index 00000000..9bfff4fd
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/heading_block_test.clj
@@ -0,0 +1,44 @@
+(ns clarktown.parsers.heading-block-test
+ (:require
+ [clojure.test :refer [deftest testing is]]
+ [clarktown.parsers.heading-block :as heading-block]))
+
+
+(deftest hashbang-heading-test
+ (testing "Hashbang heading block that's a H1"
+ (is (= "This is inline code."
+ (inline-code/render "`This is inline code.`" nil))))
+
+ (testing "Creating inline-code text in the middle of regular text"
+ (is (= "This is regular text, mixed with some inline code., and it's great."
+ (inline-code/render "This is regular text, mixed with `some inline code.`, and it's great." nil)))))
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/italic_test.clj b/test-resources/lib_tests/clarktown/parsers/italic_test.clj
new file mode 100644
index 00000000..8ab13698
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/italic_test.clj
@@ -0,0 +1,18 @@
+(ns clarktown.parsers.italic-test
+ (:require
+ [clojure.test :refer [deftest testing is]]
+ [clarktown.parsers.italic :as italic]))
+
+
+(deftest italic-test
+ (testing "Creating italic text with one surrounding asterisk character"
+ (is (= "This is italic."
+ (italic/render "*This is italic.*" nil))))
+
+ (testing "Creating italic text with one surrounding underscore character"
+ (is (= "This is italic."
+ (italic/render "_This is italic._" nil))))
+
+ (testing "Creating italic text with both underscores and asterisks mixed"
+ (is (= "Hi, my name is John, what is your name?"
+ (italic/render "Hi, my name is *John*, what is _your name?_" nil)))))
\ No newline at end of file
diff --git a/test-resources/lib_tests/clarktown/parsers/link_and_image_test.clj b/test-resources/lib_tests/clarktown/parsers/link_and_image_test.clj
new file mode 100644
index 00000000..348a8f90
--- /dev/null
+++ b/test-resources/lib_tests/clarktown/parsers/link_and_image_test.clj
@@ -0,0 +1,23 @@
+(ns clarktown.parsers.link-and-image-test
+ (:require
+ [clojure.test :refer [deftest testing is]]
+ [clarktown.parsers.link-and-image :as link-and-image]))
+
+
+(deftest link-test
+ (testing "Creating a link"
+ (is (= (link-and-image/render "[This is a link](https://example.com)" nil)
+ "This is a link"))
+
+ (is (= (link-and-image/render "[This-is-a-link](https://example.com)" nil)
+ "This-is-a-link"))
+
+ (is (= (link-and-image/render "[x] [label](link)" nil)
+ "[x] label"))
+
+ (is (= (link-and-image/render "[ ] [label](link)" nil)
+ "[ ] label")))
+
+ (testing "Creating an image"
+ (is (= (link-and-image/render "" nil)
+ "First line\nsecond line"))) + + (testing "Checking a quote block" + (is (true? (quote-block/is? "> Test"))) + (is (true? (quote-block/is? " > Test"))) + (is (true? (quote-block/is? ">"))))) \ No newline at end of file diff --git a/test-resources/lib_tests/clarktown/parsers/strikethrough_test.clj b/test-resources/lib_tests/clarktown/parsers/strikethrough_test.clj new file mode 100644 index 00000000..fdf61888 --- /dev/null +++ b/test-resources/lib_tests/clarktown/parsers/strikethrough_test.clj @@ -0,0 +1,14 @@ +(ns clarktown.parsers.strikethrough-test + (:require + [clojure.test :refer [deftest testing is]] + [clarktown.parsers.strikethrough :as strikethrough])) + + +(deftest strikethrough-test + (testing "Creating strikethrough text" + (is (= (strikethrough/render "~~This is strikethrough text.~~" nil) + "