pwshub.com

Parsing Markdown in ColdFusion

Welcome to my third, yes, third, ColdFusion post in 2024. Is it a trend? Who knows. That being said, I'm doing some prep work to update my presentation on Google Gemini in preparation for my talk at Adobe's ColdFusion Summit later this month, I'm updating my Node.js demos to ColdFusion and ran into an interesting issue - converting Markdown responses from Gemini to HTML.

My first quick Google searches didn't really mesh well with what I expected, so I asked on the CFML Slack and James Moberg pointed out a few options, but suggested I focus on Flexmark (which was backed up by another person on the Slack).

I was directed to a blog post by, of course, Ben Nadel: "Using Flexmark 0.32.24 To Parse Markdown Content Into HTML Output In ColdFusion". It was a bit out of date but was enough to get me going. Here's how I built my, admittedly quick and dirty, solution.

Step One - Get the Jar

The Flexmark library is a Java package that looks to be incredibly customizable and conplex. The install instructions expect you to use Maven or another Java tool, but I figured I just needed to get the right jar. This took me a minute to figure out. I ended up Maven at the latest release for the "all" package, which led to the file listing here: https://repo1.maven.org/maven2/com/vladsch/flexmark/flexmark-all/0.64.8/

On this page, I downloaded flexmark-all-0.64.8.lib.jar.

Step Two - Load the Jar

Next, I added it in my Application.cfc like so:

this.javaSettings = {
	loadPaths = ["./flexmark-all-0.64.8-lib.jar"]
};

I'd probably not put the jar in the root of my demo, but this isn't for production or anything.

Step Three - Use the Code

So for actually using it, I didn't follow Ben's code, but rather the simple Java code referenced in the GitHub repo. This is what they had:

package com.vladsch.flexmark.java.samples;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.MutableDataSet;
public class BasicSample {
    public static void main(String[] args) {
        MutableDataSet options = new MutableDataSet();
        // uncomment to set optional extensions
        //options.set(Parser.EXTENSIONS, Arrays.asList(TablesExtension.create(), StrikethroughExtension.create()));
        // uncomment to convert soft-breaks to hard breaks
        //options.set(HtmlRenderer.SOFT_BREAK, "<br />\n");
        Parser parser = Parser.builder(options).build();
        HtmlRenderer renderer = HtmlRenderer.builder(options).build();
        // You can re-use parser and renderer instances
        Node document = parser.parse("This is *Sparta*");
        String html = renderer.render(document);  // "<p>This is <em>Sparta</em></p>\n"
        System.out.println(html);
    }
}

And from this, I wrote up a quick demo:

ds = createObject("java", "com.vladsch.flexmark.util.data.MutableDataSet");
ps = createObject("java", "com.vladsch.flexmark.parser.Parser").builder(ds).build();
hm = createObject("java", "com.vladsch.flexmark.html.HtmlRenderer").builder(ds).build();
doc = ps.parse("This is *sparta*");
result = hm.render(doc);
writeoutput(result);

Yeah, not the best variable names, but, it worked perfectly well. I took this scratch code and built a simple UDF:

function toMarkdown(str) {
	var ds = createObject("java", "com.vladsch.flexmark.util.data.MutableDataSet");
	var ps = createObject("java", "com.vladsch.flexmark.parser.Parser").builder(ds).build();
	var hm = createObject("java", "com.vladsch.flexmark.html.HtmlRenderer").builder(ds).build();
	var doc = ps.parse(str);
	return hm.render(doc);
}

I feel like this would be better as a CFC cached in the App scope so I'm not re-creating the Java objects on every call, but I'll leave that for others to do. :)

I tested it like so, and it worked perfectly well:

<cfsavecontent variable="test">
# Hello World
Tell me why you love my [blog](https://www.raymondcamden.com).
This is another paragraph. 
## Stuff I like:
* Books
* Video Games
* Music 
* Beer 
</cfsavecontent>
<cfoutput>#toMarkdown(test)#</cfoutput>

I hope this helps! It will be in my repo for the presentation once I check it in, but let me know if you have any questions.

Source: raymondcamden.com

Related stories
1 month ago - I've looked at Chrome's on-device GenAI development a few times now, and as a feature it is moving pretty fast. In fact, that first post and my follow up both don't work anymore due to the API changing. I'm fine with that as I knew it was...
1 day ago - In this tutorial, you'll learn how to harness the power of structural pattern matching in Python. You'll explore the new syntax, delve into various pattern types, and find appropriate applications for pattern matching, all while...
1 week ago - The useReducer React Hook is a good alternative to tools like Redux, Recoil, or MobX. The post A guide to the React useReducer Hook appeared first on LogRocket Blog.
1 month ago - Discover the power of Pydantic, Python's most popular data parsing, validation, and serialization library. In this hands-on video course, you'll learn how to make your code more robust, trustworthy, and easier to debug with Pydantic.
1 week ago - An Applicant Tracking System (ATS) is software that helps companies manage and organize their hiring process. It automates multiple tasks from the recruitment lifecycle, such as sorting resumes or tracking candidates. Over the years,...
Other stories
2 hours ago - Hina Kharbey talks about how the roles of a mentor versus a coach differ, as well as the situations that work best for having each one. The post Leader Spotlight: The difference between mentoring and coaching, with Hina Kharbey appeared...
5 hours ago - Fixes 41 bugs (addressing 595 👍). node:http2 server and gRPC server support, ca and cafile support in bun install, Bun.inspect.table, bun build --drop, iterable SQLite queries, iterator helpers, Promise.try, Buffer.copyBytesFrom, and...
9 hours ago - This guide provides a foundational understanding of Redux and why you should use it for state management in a React app. The post Understanding Redux: A tutorial with examples appeared first on LogRocket Blog.
12 hours ago - Discover some of the best Node.js web scraping libraries, including Axios and Superagent, and techniques for how to use them. The post The best Node.js web scrapers for your use case appeared first on LogRocket Blog.
15 hours ago - Infinite runner games have been a favorite for gamers and developers alike due to their fast-paced action and replayability. These games often feature engaging mechanics like endless levels, smooth character movement, and dynamic...