Adding turbo-cfml To My ColdFusion + Hotwire Demos Project
Out of the box, Hotwire Turbo doesn't work with ColdFusion / CFML because it doesn't recognize .cfm
as a valid HTML file extension. To "fix this", I forked the Turbo project and created turbo-cfml
, which does nothing but add cfm|cfml|cfc
to the library's regular expression pattern matching. This morning, I then added a "hello world" demo, using turbo-cfml
to my ColdFusion + Basecamp Hotwire Demos project.
In previous demos, I had to rely on the the path_info
behavior of the server; and, route all ColdFusion requests through one root hotwire.cfm
template, using the path info to fake an .html
page. For example, my "about" page was located at:
/hotwire.cfm/about.html
This URL will make a request to the ColdFusion template, hotwire.cfm
, and pass /about.html
as the cgi.path_info
. This URL architecture tricks the Turbo library into thinking that this URL is pointing to an HTML file (not to a CFML file).
In my ColdFusion framework component, I was then overriding the request processing in my onRequest()
event handler:
component {
/**
* I process the requested script.
*/
public void function onRequest( required string scriptName ) {
if ( cgi.path_info.len() ) {
var turboScriptName = cgi.path_info
// Replace the ".htm" file-extension with ".cfm".
.reReplaceNoCase( "\.html?$", ".cfm" )
// Strip off the leading slash.
.right( -1 )
;
include "./#turboScriptName#";
} else {
include scriptName;
}
}
}
With this logic in place, a request to the ColdFusion server for:
/hotwire.cfm/about.html
... ends up executing the script:
/about.cfm
This is viable; but, it's a total pain in the bum! And, raises the barrier to entry for anyone building ColdFusion websites. Which is why I forked the Turbo library to add CFML file extensions.
Aside: Lucee CFMLL will call the
onRequestStart()
andonRequest()
event handlers even if there's no physical CFML template. As such, my demos project doesn't actually have ahotwire.cfm
template anywhere - I'm just using that template name as a hook into the request processing.
With that said, I wanted to add a "hello world" demo to my demos project using my new turbo-cfml
library. This way, I'd have a copy-paste basis for future demos.
Getting this to work took a bit of trial and error, stemming from the fact that I'm hosting turbo-cfml
on GitHub, not on npmjs.com
. It turns out, in order to install dependencies from GitHub, the installation container needs to have git
installed.
I didn't notice this in my prior exploration because I was running npm install
directly on my host computer, which always has git
installed. But, my ColdFusion + Hotwire project is running inside Docker, including the npm install
. So, when I went to install turbo-cfml
, I was getting the following error:
1016 verbose cwd /app/turbo-cfml
1017 verbose Linux 5.15.49-linuxkit
1018 verbose node v22.0.0
1019 verbose npm v10.5.1
1020 error code ENOENT
1021 error syscall spawn git
1022 error path git
1023 error errno -2
1024 error enoent An unknown git error occurred
1025 error enoent This is related to npm not being able to find a file.
To me, this error meant nothing. But, I pasted it into ChatGPT; which was able to tell me that git
needed to be made available. So, I updated my Dockerfile
to include git
and a more recent version of nodejs
(which I believe was causing a separate installation issue for morphdom
):
FROM ortussolutions/commandbox
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y \
git \
nodejs \
I then created a demo with the following package.json
dependencies (truncated code):
{
"name": "demo",
"dependencies": {
"@hotwired/stimulus": "3.2.2",
"@parcel/transformer-less": "2.12.0",
"parcel": "2.12.0",
"turbo-cfml": "github:bennadel/turbo-cfml#cfml-8.0.4-2"
}
}
At first, npm was telling me that the version #cfml-8.0.4-2
didn't exist. I believe this was a confusion on my part. I had thought that providing a tag name would work. But, it seems that only a branch name will work. So, I created a branch with the same name (cfml-8.0.4-2
) and pushed it up to my turbo-cfml
repository. At that point, the installation was successful.
Aside: I think some of my confusion came from the fact that the
package-lock.json
file was hiding some issues in my previous exploration.
With those development assets in place, I then created a small ColdFusion site with three pages. And, to the point of this post, the navigation links for this site all use plain .cfm
URLs:
<h1> Turbo CFML Testing </h1>
<p>
<a href="./index.cfm">Home</a> |
<a href="./about.cfm">About</a> |
<a href="./contact.cfm">Contact</a>
</p>
Now, when I click around in this ColdFusion site using turbo-cfml
instead of @hotwired/turbo
, the Turbo framework happily navigates across CFML pages:
As you can see, I can navigate across the ColdFusion pages. And, by looking at the network activity, we can tell that these navigation events are being handled by Hotwire Turbo because:
They are being requested via the
fetch
API.The initiator of the network request is
turbo-cfml
.
At this point, I now have a proven mechanism for using Hotwire Turbo in ColdFusion without any URL rewriting shenanigans.
Want to use code from this post? Check out the license.
Reader Comments
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →