Video summary
The video features hosts Allison Sheridan and Bart Bush discussing the modernization of "Tidbit 19B," a legacy JavaScript script originally written nine years ago to automate link creation by extracting headlines from web pages. This project was necessitated by AI-driven bot blockers that caused the old prototype-based code to fail, prompting a complete rewrite into a modern ES6 module structure. The new architecture employs an object-oriented design centered on three primary classes: `pageData` for extracting metadata such as titles and headers, `linkData` for storing URLs, text, and descriptions, and `template` for managing output formats like HTML or Markdown. A fourth "master" class named `Linkifier` orchestrates a streamlined three-step process involving downloading the page, transforming data into link objects, and applying templates, while default behaviors and utility functions are exposed via dictionaries to manage complexity.
To address the diverse structures of modern websites, the developers moved away from fragile patterns like `<title>` or `<h1>` tags, which often contain hidden SEO text or inconsistent placement, in favor of a hierarchical mapping system based on domain names. This approach leverages DNS knowledge to apply specific extraction logic for known sites while utilizing a robust default transformer for unknown ones, ensuring reliability even when direct scraping is blocked or markup is inconsistent. The system also automatically handles mobile subdomains by stripping `m.` prefixes and employs specialized transformers to remove tracking parameters from URLs. Furthermore, the tool intelligently resolves text formatting issues such as number parsing errors, acronym capitalization like NASA, and brand-specific styling for products like Apple, using a JavaScript `Set` to maintain unique lists of acronyms and preventing duplicates in title casing.
The development team carefully selected six specific dependencies—Cheerio for browserless jQuery operations, Mustache for templating, Node-fetch for HTTP requests, Title Case for string conversion, URI.js for URL parsing, and URL-slug for slug generation—to minimize the overall footprint of the project to just 20 unique dependencies across the entire tree. While some formatting challenges still require manual intervention, others are solved automatically through customizable modules that allow users to define their own lists of small words not to be capitalized in titles, addressing the lack of a universal standard. The discussion also highlights the limitations of relying on URL slugs as a fallback method, noting that slugification is lossy because it strips capitalization, punctuation, and diacritics, which degrades headline quality; therefore, the primary strategy remains scraping specific HTML elements with custom logic per site.
The segment concludes with practical updates regarding the podcast's schedule and an announcement of an upcoming real-life meeting between the hosts. For listeners interested in supporting the ad-free show, information is provided on how to contribute through donations or Patreon. Ultimately, the project demonstrates a successful transition from outdated prototype syntax to a scalable, maintainable system that balances automated logic with necessary manual oversight to ensure high-quality headline extraction across the evolving landscape of the web.
Read the full video transcript
Well, it's that time of the week again.
It's time for programming by Stealth and
this is Tidbit 19B recorded August 29th,
2026. And I'm your host, Allison
Sheridan. And of course, I'm joined by
your host and tutor, Bart Bush. Hey,
Bart. How you doing today?
>> I am doing just fine. Um, it's been a
while.
>> It has a month. Now, I always feel
compelled to explain what tidbits are.
Tidbits aren't necessarily small.
They're they're just not in a theme of
like we're going to do Git now for
several weeks in a row or we're doing
JavaScript or we're doing Shimoa,
whatever we're doing. They are they
aren't in the big theme. They're tidbits
on the side. But this one is actually
going to end up being a three-part
tidbit.
>> Yeah. And we're not the first because
Helma ended up going three parts when
she was going to quickly explain Docker
to us.
>> Yeah. Yeah, that was great. So,
>> yes,
>> tidbit is a it's it's more of a feeling
really than a descriptive word.
>> Well, I I would say what makes it a
tidbit is that it's adjacent to the
series because we're not really learning
a new skill. This is an application of
what we've done, but it's not really a
lesson like the rest of programming by
self.
>> Yeah.
>> So that's why it's a tidbit.
>> There we go.
>> So when we last left our heroes, we had
explained that because of other
podcasting stuff, I do a lot of it with
you. I need to make links all the time,
links to articles on web pages. And so I
need to show the headline of the article
I'm linking to and where it came from.
Because to me, a headline without a
source is meaningless because every
publication has a point of view and
without that context, you don't know
what to make of the headline,
>> right? And that manual work is a lot of
work when you have 200 links in say a
let's talk apple set of show notes. So I
needed to automate it. And we sort of
explained that at the start of the part
A of this tidbit and then we took a
diversion into the fact that when you're
writing JavaScript to solve any problem,
the chances that you can solve all the
problem with your own code, go to zero
as the complexity of the problem to be
solved increases beyond simple.
And so you end up at the very least with
stuff like jQuery or something or
Bootstrap or something, but you end up
building up dependencies
and in the olden days you could just
install whatever you liked and it was
fine. And that's not today. So then we
spent pretty much all of that first
part, part A, talking about the
practicalities of trying to stay safe
while dealing with dependencies cuz you
need them, but you also need to be
careful with them. So that was a whole
discussion of bar,
>> right?
>> And now I actually had a specific
problem to solve and I had written it in
plain old JavaScript. Not just And I say
old, I mean old because I wrote it nine
years ago.
>> Wow.
>> Yeah. Pree6 using the prototype syntax
that we just we just don't do anymore.
And I even went back in time in all of
our old show notes after we recorded
part A and put a link sort of a little
warning telling people, hey, this is
legacy syntax. Jump ahead to like PBS
128 or something.
And yeah, I need to rewrite all the
code.
And we didn't really go any further than
that last time. So this time it's all
about that. I need to rewrite all the
code.
>> Now the name of your little your little
code is called linkifier.
>> Yeah. So it was a script that I called
linkifier.js
and that was one giant piece of
JavaScript. like oh my god was it long
which meant that managing it was a giant
pain in the backside because scrolling
up scrolling down scrolling up scrolling
down it's all in one big file because
that's how you wrote things in the old
versions of JavaScript so one of the
things I've wanted to do for a long time
is to modernize the JavaScript but that
meant rewriting it which meant
remembering how it all worked from 9
years ago And that was a lot of work and
I wouldn't have done it until those
annoying AI people started to ruin
things and my downloads weren't working
anymore. So the script worked by
downloading the web page at the URL,
finding the title and then using that to
create the link. If you can't download
the page, what do you do? Well, my old
script crashed.
Just it just did nothing. It didn't
handle it gracefully. It just crashed.
So I had to rewrite it anyway to do
something when it went wrong.
So I modernized the code.
So we went from one giant big script
into a modern ES6 module, a single
module where I could contain all the
logic together. So the brains of
Linkifier is now a single module.
And I'm going to give a slight preview
to part C because having solved the
problem doesn't give me an interface.
There's no UI yet. There is a black box
that is this module where you can shove
a URL in one side and a link will fall
out the other side.
>> But that's making useful
>> precisely. So either I could write a
little wrapper script. It would be a
much much shorter script, but it still
be a little script and I'd have all the
same problems that I had with the script
cuz I could pipe PB paste to the script
and then the output of the script into
PB copy and then I have my annoying
trailing new line character again. I
could fuff about like that, but what I
actually did was create a JavaScript CLI
app. And that's going to be part C
because the CLI apps code doesn't have
to do any of the work of converting
links. The only thing it has to do is
provide a nice human friendly interface.
But to a good CLI, there's quite a bit
to it. So that's actually enough of a
discussion to be worthy of a part C.
>> Yeah. I'm not sure we've actually gone
through that, right?
>> No, we have not. So, I've been meaning
to find an excuse to wet people's
appetite because we we have all the
skills to
to to basically learn how to do it, but
we haven't looked at it. So, that's why
I really wanted to do this tidbit. To be
honest, that's part C is the real reason
I wanted to do the tidbit.
>> Well, you can't get to part C without
the other bits. You got to tell a story.
So today I want to talk about the design
of the updated linkifier.
So we have a complex problem to solve
and my brain has for decades worked in
an object-oriented way. My first
programming language was Java which is
an object-oriented language and that's
just how I think about things. A problem
is made up of objects which interact
with each other. So what are the moving
parts to the problem to be solved? And
in this case when I thought about it I
came up with three classes for three
different types of object.
So the first one I created was page
data. This represents what it is I pull
out of those web pages sitting at those
URLs. So that's things like the URL, the
content of the title tag at the top of
the HTML page. I also make two arrays of
all the H1 tags and all the H2 tags
because they're the kind of places that
headlines tend to go. I also make a
dictionary of the metadata. So those uh
SEO headers we talked about a few weeks
ago, few months ago in the main series
just to really go wibbly wobbly timy
wimy. And so they're in a dictionary. So
that gives us a single object to
encapsulate whatever it is we extract
from the actual web page at the URL. To
construct a link, you need the pieces
that make up a link. So I made another
class called link data to capture what
it is that a link is made of. A URL,
some text, and optionally a description
which would appear as hover text
usually.
>> So that's the three possible pieces of a
link. So that's a second object. And
then the third object is a template for
turning the parts of a link into a link
because maybe you want HTML sometimes,
maybe you want markdown, sometimes you
may have different ways you want to do
it. So I figured we should have a
template that we can just apply. And the
template just has two pieces. a template
string, which I chose to do in mustache
because that's just my favorite
templating language for JavaScript.
And also, I decided to add the ability
to create filters. So, this is just an
optional set of functions that can be
used to intelligently transform the the
you know, the pieces that make up the
link. So things like say stripping those
annoying query parameters off the end of
URLs can be done as a filter you know
>> everything after that question mark.
>> Yeah because there's a bunch of them are
done for tracking purposes and I really
want to strip those UTM equals I think
they all start with. So I want to strip
those out. So
>> often often annoyance just when you're
sending somebody like an Amazon link and
all of a sudden it's three and a half
pages long.
H
yeah,
Amazon's a perfect example because if
you take everything after the question
mark off, it works fine.
>> Yeah.
>> All the rest of it is tracking
>> including the link.
>> Well, you can leave it if you like. It
won't make it won't break anything.
>> Okay. I usually take it off.
>> It's cleaner,
>> but if you leave it on, it won't break.
>> Okay.
So those three classes actually have
changed very little in the last decade.
I mean I've given you sort of a small
handful of their properties and if you
actually look at the code they have a
few extra little fiddly bits to make
them go but that is actually the essence
of them and has been since the start and
that hasn't changed very much. So my
initial one giant big file started by
defining these three classes and then it
went and used those three classes to
implement my algorithm which is a
three-step process. Step one, download
the HTML and pull out the information to
create a page data object. So now we
have the information about the page.
Then step two is apply some sort of
appropriate logic to find the right
piece of that page data to build the
information that we're going to use for
our link. So we're going to transform a
page data object into a link data
object. And step three is turn the
pieces of a link into an actual link. In
other words, we're going to use a we're
going to transform a link data object
into a link with a link template.
So, HTML page to page data, page data to
link data, link data plus template to
output, which is
>> that sounds pretty elegant.
It is a a sensible design which means
that the actual functions for doing the
work have a sensible structure and even
after a decade of not really looking at
the code I was actually able to find my
way very quickly because you have three
classes and a three-step process the
code is logically structured.
There's just a lot of it all in one
file, which is
>> okay. Just so much
>> optimal.
>> Too much to scroll up and down. Let's be
honest.
>> So when you move to the ES6 universe,
you end up thinking of modules. And the
first thing you can do with any module
is that it you can take each of these
classes and put them in their own file
inside the module. So immediately I
could take the three classes out of the
top of my great big long file and put
them into three nice separate files. So
that's already a nice start. But with a
module, where do we put the logic bit?
Where does the three-step process go?
Well, the answer is you actually need
like a master class to tie it all
together. And so I created a fourth
class that I called linkifier
since I've been using that phrase all
along. And so the linkifier class makes
use of the other three classes to
actually implement the three-step
process. So that means of all of the
classes, it's probably the most complex
one, but it's also like the main one
which so that's actually what gets
exposed by the module. So if a module
has a default export when you say import
linkifier,
you get back one object. What do you get
back? Right? You get back whatever the
default export of the module is. And so
I decided to make my default export an
instance of the class linkifier.
So this master class is what you get
when you say import the module.
>> Oh, okay. you get the whole class,
>> you get an instance of the class. You
get an object built
>> using the default constructor for the
class.
>> Okay?
And so that class is actually the most
complex of them all because it contains
a lot more properties. Some of them are
property properties like piece of data.
Some of them are functions. Some of them
are static. Some of them are instance.
So it really is a much bigger thing. So
I'm not going to go through everything
in that class, but I am going to
highlight the most important things that
class gives you because when you have an
instance of that class, that's what
you're working with. So the first thing
is this module has a bunch of default
behaviors. And so instead of those being
hidden away, I chose to expose them
through a dictionary called
linkifier.defaults.
So if you want to know what the default
template is, you'll find it in the
defaults dictionary. If you want to know
basically any default, you'll find it in
the defaults dictionary. That's also
what the default constructor calls.
Instead of it being hardcoded values
inside the constructor, the constructor
just uses the defaults dictionary. So,
it's just easier for everyone because
you it's just more obvious.
>> Right. Right.
>> There's there's also a dictionary that
just contains a whole bunch of useful
functions. And these are just functions
I created that I know I'm going to need
over and over again. And I just publish
them because they're useful. And so,
linkifier.utilities
is just a dictionary with lots of useful
little functions. When you say you
publish them, do you mean that's like a
separate thing you could find on GitHub
or do you mean it's part of this module?
>> It's part of this module. So the this
this module the linkifier class has a
dictionary named defaults that has these
values in it and it has a dictionary
named utilities that has all these
functions in it.
>> Okay.
It also has a suite of functions for
managing the configuration. So defining
templates and all those kind of things.
It has a suite of functions for managing
the logic we're going to use to do that
step two. Turn page data into link data.
Well, we set it as one thing. There's
actually quite a bit going on there as
we're about to discover. And so the
logic for doing that has to be
configurable. So there's a suite of
functions to make that possible.
There's also a suite of functions for
managing the list of available
templates. So if we want to be able to
make markdown and HTML and stuff, we
need to be able to manage those and you
may want to create your own entirely
different to what comes by default. So
there's functions for that. And then
there's functions for doing step one,
step two, step three of the three-step
process. So that you can basically say
okay I want to take this URL give me a
link data object okay great I'll take
this link data object turn it into sorry
take this page data object and turn it
into a link data object etc right each
step there's a function
>> it's functions all
>> oh yeah lots of functions and then
there's the most important function of
all generate link which takes as an
argument a URL and it returns a string
that is your link
So that is the most important function
of everything is just the generate link
function in the linkifier class that
does all the work using everything else
>> takes all the credit is the way I would
put it. All those other functions
they're doing the heavy lifting. It's
out. It's just the showy one that gets
to go here I made you this.
>> Yeah. And it only needs one argument
which is kind of the beauty of this
process that everything else has been
encapsulated in all these other classes.
And so ultimately when you want to make
a link you just need to call generate
link with one argument that is a URL. So
I do kind of like that.
Okay. Now I've very intentionally
glossed over the complexities of these
three steps. There are three
conceptually simple steps and it's
important to think of them as separate
steps. Otherwise, you could never solve
this problem. But step two, God, that's
where the devil is in the detail in that
bloody step two. That really is the
tricky bit.
>> Just just go get that headline part.
Come on. How hard can that be?
>> The headline.
>> It's right there. You can see it.
>> But because you might imagine that step
one could be tricky, right? Download a
web page and turn it into this page data
object. But actually, there's modules
for that.
So there's actually a module that lets
you turn a web page into something that
looks exactly like jQuery without there
ever being a browser involved. You just
give it a string of HTML and it gives
you a dollar object and you can just
query it like jQuery. So all of the
jQuery syntax we can use just in this
module without there ever being a
browser involved. It's great. So that
makes it really easy to pull out the
title tag and to pull out all the H1s.
It's we just use jQuery syntax. So that
bit's easy to make the page data object.
But yeah, those headlines. Oh boy.
So
somewhere in every web page that is an
article, we can see with our eyes the
title, the headline of the article. It
has to be in the HTML. There is no way
for it to exist on the web page and not
be in the HTML.
And you might assume that every web page
would be similar enough that you could
apply a simple piece of logic and that
simple piece of logic would just work.
And so when I first started writing this
script, I naively assumed that the
answer was very straightforward. It
would be in the title tag, in the head
tag, right at the top of the page.
>> Sure.
>> And that's partially true. The correct
information is almost always in
thereish.
Can we put this in the Yes. And
yeah, so it is sometimes it's actually
truncated in there, which is uber
annoying. That's the most useless they
can possibly be is to have it up there
but truncated. It just ends in like an
ellipsus when it gets too long. So
that's definitely useful on the websites
that do that. But almost every web page
either prefixes the website name and
some sort of a special character like a
pipe or something or appends it to the
end. So just using the title tag is
always going to give you the headline
with baggage
>> or most of the headline.
>> I actually have baggage
>> a teeny tiny itty bitty little script
that just copies my headline and the
first thing it has the main thing it has
to do is scrape off pipe pod feed
podcast. I didn't exactly
>> cause it to do that. That's something
that WordPress does for me. It creates
that little title. So mine works, but
I'm solving one problem, one specific
instant, looks exactly like this every
single time.
>> Yeah. If every web page did it exactly
the same way, we'd have a chance here.
>> But that's not how it works.
>> Well, I remember asking going, Bart,
geez, I've got like a four-line script
that does mine. I don't know what your
problem is.
>> And if I maybe if I only linked to RS
Technica, then it would be easy.
>> Sure. or if you only did one link a week
>> or that you're doing
>> Yeah.
>> practically hundreds. Yeah.
>> Yeah. Yeah.
So my second thought, having realized
the title tag was not my friend, was
well, it's best practice to
put the main title of a page in the
first H1 tag in the page because that's
better for SEO and most websites like
good SEO. So I thought maybe that would
work. But that proves to have some
annoying problems as well.
because
that best practice was true before HTML
5, but HTML 5 isn't very new anymore.
>> So, now that we have semantic markup
like heading uh not body, that should
say main, not body in the show notes. My
bad.
>> Article section, all of these regions
now have their own tag. So now the rule
is the first H1 tag within the
appropriate region is still best
practice. Well, that could be the fifth
H1 tag on the page on one website and
the second H1 tag on the page on another
website.
>> Oh, really?
>> And then
>> yeah, because it's just do they do they
have the markup for their sidebar before
or after the markup for the middle of
the page? Where do they put the markup
for the heading? I mean the amount of
permutations of where that headline for
the article could be is quite large. And
then you have the fact that some
websites choose to give the headline of
the article second billing. Their own
brand gets first billing in an H1 tag
and the article title only gets second
billing in an H2 tag. But they could be
like the eighth H2 tag. The good
goodness only knows when you have H2
tags because the sidebar can be full of
them. So when you fall into H2 tags, all
bets are off.
>> And they've got those little banner
things across the top with people have
cards that show all the other articles
you should be reading instead of
whatever it is you were trying to read
on this page.
>> Exactly. Exactly. And then some websites
in order to try game the search engines
actually have hidden text in their H1
tags that when you looked at the HTML
code, they have about 50 buzzwords after
the title of the post,
but the script sees those. So I can't I
can't remember which website it was, but
it might have been the old one of them
that went bankrupt. was it um
macarel.co.uk
or.com one of them went away and they
used to have like 50 different tags in
the headline only in invisible text and
so
>> I completely believe it was them because
that was the the only page I ever went
to that made me think I really will do
an ad blocker cuz I literally cannot
find your content. You're you're killing
me. Actually, no. The other one was
wired. the two of those I just stopped
reading Wired and but I kept getting
links to Mac World and I'm kind of I'm
sorry for the people who lost their jobs
but man that was awful towards the end
so I'm not at all surprised that you
included other garbage in their titles.
>> Yeah and to be honest I actually stopped
using them for Let's Talk Apple because
their articles became as garbage their
headlines.
>> So they really they really really
started scraping the barrel anyway. And
then finally, some people are so bad at
web design that their article titles are
just in paragraphs with a bold tag.
>> Oh jeez. Seriously? Okay. Never link to
them. They don't deserve it.
>> So clearly that's not going to work,
>> right? So, I did briefly think, well,
what if I try the first H1 tag and then
somehow with a magic regular expression
figure out that that isn't going to work
and then try the title tag and then try
something else and then try something
else. Could I write one big algorithm
with a whole bunch of ifel statements
that could somehow find the right
permutation on every website? Five
minutes told me that was a terrible
idea. So, no, I couldn't find a pattern.
No.
>> Wow.
>> So
that left only one obvious solution.
Basically scale up what you do per site
logic.
>> What?
>> So on RS Technica do this,
>> right? Your skepticism is perfect
because my first thought was what am I
doing to myself?
>> Yeah.
>> But then I looked up my own show notes.
I would say 80% of my links come from
about 10 websites, 90% of my links from
about 15, and almost all of my links
from maybe 30 sites in total. Like try
to list 30 websites.
>> Yeah. Well, now that's one of the things
where you always say this is a known
good website. I remember I recently uh
was unable to find a really important
article on any of the big websites.
didn't make the Verge. Arsectica didn't
make any of those. And you're like, uh,
those are those are sites I've never
heard of. And I think part of it is
like, I got to do those by hand is a
little part of it. But you do all the
security bit stuff and let's talk Apple
and that those are two very different
sets of links. That's still 90% and 15.
>> Yeah. If you look at security bits, it's
probably only five sites I trust enough
to link to regularly. It's Bleeping
Computer, The Hacker News, Cyber
Insider, and strangely enough, the Mac
Observer do really good coverage of
stuff we talk about on security bits.
But actually, the majority of the links
are only five or six sites that I trust
enough because there's so much
hyperbolic
>> scare you nonsense for security bits
that there's fewer actual sources I
trust than for the Apple news.
>> Oh, that's really interesting.
So that means it wasn't it made more
sense to do this tailored approach,
>> huh?
>> Yeah. Okay. I surprised myself when I
started to look at my own show notes. I
was like, "Oh, no. This is quite
doable." And the great thing is these
websites,
there's actually a few very standard
patterns. So the reason there's a
utilities class is cuz one of the most
common patterns is the title tag with a
regular expression to pull out a string.
And so one of the utility functions
takes basically the the the title and a
string as the second argument and it
just pulls the string out of the other
string and returns it. So RS Technica
use pipe rs technica you have a pipe
based one. So that just that one utility
function with a different string covers
10 15 sites.
>> Yeah. So my actual code for those 10 15
sites is just oneline functions. Call my
utility function with this argument.
Call my utility function with this
argument. So a lot of my customization
is one line a oneline JavaScript
function and I write it as a fat arrow
function so that it literally is one
line.
>> It sounds like that would actually make
it
>> an easier way to understand your code
because those examples are so obvious.
>> True. you know, for some somebody like
me to query your code and go, "Oh, I see
what he's doing." Because if I look at
ours Technica, it does this.
And actually, I should I haven't got in
the show notes, but I'm going to mention
it because our listeners are cool. Um,
one of the things that is in the uh
GitHub repository for Linkifier is an
example config file called real world
example.
>> That's very real word. That's my actual
config that I use every day. And as I
update my config, I actually push the
changes into that repo because I
actually use realworld
example.comconfig.
>> And so if you're curious how many
customizations have I made, if you open
that file, you can count them and that
is exactly how many I've made.
>> Cool. And most of them are oneliners.
Big exceptions are when I link to um
is it Overcast? When I link to Overcast
for podcast, that takes a lot of faffing
about because the podcast name has to be
pulled out from the episode name and
they all have to be jumbled about and
XKCD takes a lot of code because I
actually pull out the image and make a
pretty link with an image and everything
in it, but most of them are oneliners.
So this per site extraction proved to be
the answer. And so that's what I
implemented 10 years ago and it's still
what I'm doing today. So the ES6 module
takes exactly that approach.
So extraction logic is just a really
fancy way of saying a function. The
input to the function is a page data
object and the function has to return a
link data object. And that's every one
of these extractor functions is just one
argument, one return value. Page data to
link data. And now we have to connect
them to the different websites. So I
need something for the Mac Observer and
something for RS Technica and something
for Bleeping Computer. Well, what
differentiates Mac stories from the Mac
Observer? It's their domain name. One of
them's at what did I say? Macstories do
well probably com. And the Mac Observer
is definitely at macobserver.com.
And I'm a CIS admin most of my life. Not
anymore. No, I can't say I am. I was a
CIS admin. Now I'm a cyber security
specialist. Anyway, I was also the DNS
admin. So I think in terms of DNS and
actually using DNS names
has some really nice advantages because
there are two features of DNS names that
really add power to Linkifier.
>> Right before you tell them
>> to stop everybody from screaming, it's
maxto.net.
I just checked max.com is available for
purchase though.
>> I should gift that to Frederrico.
>> I bet it costs a fortune.
>> Not available.
>> You're right. Is available in two ways.
>> Okay. So, the first thing is DNS names
are hierarchical. So the period or the
dot as we say it in URLs is a separator
between the different parts of a domain
name and they're they're they're
hierarchical. So www.podfey.com
is a subdomain of podfeat.com
is a subdomain of the top level domain
com.
>> I never thought of www as a subdomain of
podfeat.com but I guess it is.
>> Yeah it is
>> before the dot. So yeah, precisely. So
basically everything on the left is a
subdomain of everything on the right.
And then something almost no one knows
because the internet has done its best
to hide it from us is that every URL is
actually a subdomain of the root domain
which is denoted by a final trailing
dot.
>> Really? You don't Yeah, you don't see
this because the browser is hided from
us, but your domain name is not
www.podfeet.com.
It's actually www.podfeet.com
and I'm pbs.bofessor.net.
>> Wow.
>> And the browsers put them in silently.
So when they issue a DNS query, they pop
them in and when they get the answer
back, they just take it out. And what
you see in the address bar is the human
friendly version without that silly dot.
And when you call most DNS commands and
you leave out the dot, they just put it
in for you and then they do the DNS
work, but it's actually there. And there
are a few terminal commands that show
it, but DNS admins see the final dot and
know what it means, and everyone else
assumes it's punctuation. They assume
there is just a period at the end of the
sentence in the output of the terminal
command. They don't associate it as
being part of the domain name. But there
are actually 24 DNS servers for the dot
domain because those DNS servers know
who is responsible for com and who is
responsible for net and who is the
responsible for org. that's actually
handled by the root DNS servers and they
are the DNS servers for dot
wonderful piece of trivia but it comes
in bloody useful.
>> Oh, so we need to actually know this.
>> Well, it I use it as a feature to make
link work a little bit more better.
Terrible grammar, but you know what I
mean. Um, the other thing I should say
is that from the very very first time I
wrote this script, I always in my head
refer to these extraction functions as
transformers because they transform a
page data object into a link data
object. And so I leaned into that naming
convention when writing the ES6 module.
So in the original script, I had a
dictionary where I put the domain names
in as the keys and the values were my
functions.
And that's fine because I was writing a
script for me. And so I was putting that
trailing dot in at the end for reasons
I'll explain in a minute. And it was it
was fine because I'm a DNS person and
the script was only for me. But now that
I'm writing a module and publishing it
to the world,
expecting having to explain to everyone
that DNS names have a secret dot you
can't see, that's not something I want
to do. So instead of me give making a
dictionary and exposing the dictionary
and making the user of the module edit
that dictionary,
I still have a dictionary, but it's a
private variable. And what I have
instead are functions that automatically
add and remove the final period the same
way Firefox and Safari do. And it means
that the users just call functions with
sensible names. And they have no idea
what's happening under the hood. And
that's how modules are supposed to work,
right? They're supposed to be black
boxes that you shouldn't have to explain
life, the universe, and everything to
the user of your module. So we have a
function called register transformer
that takes two arguments. The domain
name you want to apply the transformer
to and then the transformer itself which
is a function. Get transformer for
domain takes one argument a domain name
and it will return to you a function.
And then if you want to see everything I
do actually let you see everything.
There is domain true transformer
mappings is available as a read only
copy of the dictionary for anyone who
wants to see them all
but that's just for the nerds. So really
you register transformer and get
transformer allows you to do everything
you need to do.
>> I would have thought that a user of your
module would need to be able to to edit
the underlying dictionary for their own
use.
>> Right. Register transformer. That's what
register transformer does.
But the domain to transformer mappings,
isn't that the one that's that's taking
the pipe off of podfeed.com?
Okay, that no, but reg. Okay, so under
the hood there is a dictionary.
Register transformer
updates that dictionary for you.
>> Oh,
>> automatically.
>> Okay,
>> so the problem you have is I need to add
my regular expression for pot.com,
>> right? You would say register
transformer first argument podfeat.com
second argument your function that calls
the regular expression
>> and that's editing the readonly copy of
the underlying dictionary okay
>> yeah exactly
so why do I say this whole subdomain and
secret hidden top level domain matter
well I implement fallback in my mappings
so So if you add a mapping for
podfey.com,
that mapping will apply to podfey.com
and all of its subdomains unless there's
a more specific mapping on the
subdomain. So some people might link to
rstechnica without the www and some
people might link to it with the www. If
I just register a transformer for
rstechnica.com, it will cover both.
There still exist a few really weird
websites where they have a mobile
website which is usually horrible and a
normal website which means I may
actually need two different transformers
for that website. And I can handle that
by having one for say silly.com
and another one for
m.sillybsite.com sillybsite.com because
M is what's usually used for the mobile
sites.
>> Oh, I forgot all about those that
>> those M dots. Yeah.
>> Yeah. You see that? You're like, when
did you write this? When did you do
this?
>> Yeah.
>> So, before responsive design. Huh.
>> Yeah. So as an example of how this
fallback works, imagine that we have a
very simple universe where all sites
have acceptable titles in the first H1
tag or in the title tag except for one
website called sumsite.com.
This site has a legacy mobile site at
m.sumsite.com sumsite.com and a modern
website available both at sumsite.com
and www.somsite.com.
In this very simple universe, we need
three transformer functions.
One to handle every website on planet
earth apart from this one website.
This is not our real universe, but bear
with me. One to handle the mobile site
and one to handle the modern site for
sumsite.com.
And how does that work then? Well, we we
put a mapping on some site.com dot
another one on m.site.com
dot and the last one for every other
site in the universe gets attached to
dot
which means it's the default transformer
for every single web page on the
internet. And actually the linkifier
module does implement a default
transformer so that you fall back to
something
and that default transformer falls back
to if there's an H1 tag on the page use
it else use the title tag. There's
always a title tag
>> and that actually is what the module
does as its default last stitch effort
because at least then you get a link
that has the right information. You may
have to clean it, but at least you get
something.
>> Yeah.
>> And then I have my actual mappings
>> cuz otherwise the option would be fall
over in a heap.
>> Precisely. Precisely.
So to see how this works, imagine we
have the URL sumsite.com/bigstory1.
The first thing that will happen is when
we pass this URL in, it will say is
there a mapping for sumsite.com?
There is. I shall use it. So that's a
very simple algorithm
>> meaning it's in your dictionary. It's in
your list of 15 or 20 or 30 sites.
>> Yeah. So in our imaginary universe,
there are three entries in the
dictionary. M.Sight.com
and sumsite.com. So this first URL is
sumsite.com. Do I have a mapping for
sumsite.com? Yes, I do. Use it. Our
second example is www.somsite.com.
sumsite.com/bigstory2.
Well, here we say, do we have a mapping
for www.somsite.com?
No, we do not. We only have those three
mappings above. Do we have a mapping for
someight.com? Oh, yes, we do. Use that.
>> Okay. Then we get um
www.anothersight.net/bigstory.
net/bigstory.
So, is there a mapping for
www.anothersight.net?
Nope. Try the parent domain. Is there a
mapping for another site.net? Nope. Is
there a mapping for net? No. Well, there
never will be, but has to try.
>> Mhm.
>> It's just take off a dot every time. Is
there a mapping for dot? Yes, there is.
Fall back to our default. apply the
default mapping. Hey presto. And then
finally, if we have the mobile site,
then we would say, do we have a mapping
for m.somesite.com?
And it would be yes, we do. So that
would be a different one to what gets
applied to www.sumsite.com.
So everything works in our simplified
universe with just three functions.
There are more than three in my actual
real world config, but there are far
less than 100. I think it's 20 to 30,
that sort of ballpark. A few tens and
that covers everything
>> that I need. So that's not too bad, you
know.
>> Yeah.
>> So that piece of the logic I was very
very pleased with myself a decade ago
when I came up with this and that has
worked flawlessly for the last decade.
But then we get to the cause of all of
my recent problems, the bot blocking.
Because the AI bots are hoovering up the
whole internet and making people cranky,
my little script is getting caught in
the crossfire. No one cares about my
little script, but it's a bot, so it's
getting blocked.
And I spent a lot of time trying to
solve this problem. And for a long time,
I thought the only thing I could do that
would work reliably is to throw away my
code and start over and integrate some
new code straight into a real browser
because the real browsers are let
through by design. So instead of trying
to somehow imitate Firefox or Safari,
somehow become Firefox or Safari. And I
know hypothetic I know for a fact
because of certain things I've had to do
with my work at that there are APIs for
driving the browsers. So you can call an
API to tell Firefox to go to a URL
and it would be a lot of work that I've
never used. It's technology I've never
used before. I know it exists and I'm
sure with infinite time I could figure
it out. I believe headless Chrome is the
one everyone uses, but again, I didn't
want to do that.
>> I've touched headless Chrome and I was
never happy with it. But I wonder
whether the Isn't there a textonly
browser out there that still exists that
was designed 100 years ago
>> link
>> ly link?
>> Yeah.
>> Well, I hadn't thought of that. Yeah, I
could end up with a I could rewrite it
in bash and you Yeah, that's a that's a
third avenue I hadn't thought of. I will
grant you that. That sounds fun.
>> Yeah. The other option that I did think
of was rewrite all of my code as a
Firefox plugin.
>> H cuz Firefox can open web pages just
fine. Like they're not blocking Firefox,
>> but again, I've never written a Firefox
plug-in. I could learn, but that's a big
project. So, yeah.
And then I noticed something.
Every URL of every website I actually
use has the headline in the URL
sort of.
They're called URL slugs.
So, there's an actual headline from the
Mac Observer from a few weeks ago in the
show notes. www.mmacobserver.com/news/
iphone-8-promax-cood
and heavier due to bigger battery with
dashes for every space
that actually clearly contains the
headline, "The iPhone Pro Max could be
thicker and heavier due to bigger
battery." It's It's there. It's mushed
up a bit, but it's there.
So, there's an algorithm for sluggifying
URLs.
Well, what if you reverse that
algorithm? Can you deslugify a URL?
Turns out there are modules for that
because there's modules for everything.
So, okay,
this is an avenue that's plausible. But
even that didn't prove to be quite as
straightforward as I'd hoped. So there
is information lost in sluggifying text.
For a start, all of the capitalization
is gone in that URL,
>> right? iPhone 18 Pro Max. It's all
lowercase.
>> So, okay. Well, there's a convention
that just about everyone uses in media
called title case where you capitalize
the first letter of every word to make
your headlines. So, if I deslogify and
title case, then don't I get good
headlines?
>> Except for the little words.
>> The little words you don't do that.
>> Okay. So, you've hit on a secondary
problem. There's actually a bigger
problem before then. So, slogification
is lossy in three ways, right? The
character casing gets thrown away. The
title case algorithm can fix a lot of
that. So, okay, that's something. Old
punctuation evaporates. Everything
becomes a space. A colon, it's a it's a
dash, which you reverse slugify to a
space. Spaces become dashes become
spaces. Colons become dashes become
spaces. Commas become dashes become
spaces. See what happens here.
Diiocritics. Now in English we don't
have a huge amount of diiocritics. So
these are the little twiddly bits on
letters. So your axon on the E and your
sadia on the C. They're diiocritics.
And when you slugify they evaporate. So
a ax on becomes a
and for cliche for example that means
that you don't really get back cliche
you get back amateur
>> yeah exactly the amateur version and I'm
the kind of nitpicky person who wants
the yaxon
>> um so
>> can I interrupt real quick this is a
fallback mechanism we're talking about
here right
>> it is
>> you don't you don't have all of your big
sites aren't blocking you from pulling
the title from
>> logical places.
>> It got as bad as 50%. Now it's gotten a
little bit better. They seem to have
backed off when they're blocking.
Somehow my script isn't falling into
their algorithms as much. So now it's
about a quarter.
>> Okay.
>> That's still a lot of links.
>> Mhm.
>> So this fall back is still quite
important.
So the big problems I was seeing was the
punctuation currency symbols. Apple
News, they're always talking about
billions of dollars. Like billions of
dollars are everywhere in Apple News.
>> All numbers get boogered as well. And
they don't get reversibly boogered
because 1,1
and 1.001
all become one space 001. How do you
know if it's thousands or fractions?
>> It'd be bad to be wrong on something
like that.
>> A teeny bit. Yeah. Um, acronyms get
destroyed. NASA becomes NASA with a
capital N and lowercase ASA.
>> And
weirdly capitalized words which Apple
has an absolute fetish for get destroyed
as well. iPads, iPods, iPhones, m Mac
OS, all of them.
>> Yeah.
And of course, cliche disappears. That's
pretty much the only accented word that
shows up in many of my headlines, but it
does get, you know, it does get
destroyed.
So, the first of these three problems
are actually just not solvable. So, they
are always going to need a manual fix
and they're not the world's most
difficult manual fixes and so I tolerate
them. This is a fallback mechanism. If
it's 90% there, hey, it'll do. So you
can put in colas and commas and uh
currency and fix the numbers.
>> Yeah, exactly.
But actually, I could manually fix
everything. But the second three are
actually 90% solvable. Not 100%, but
still pretty darn well solvable
automatically
because
acronyms, the same ones tend to show up
over and over again in my show notes.
NSA,
uh, CIA for security bits, and the
weirdly capitalized words, they're
mostly Apple ones, let's face it. So,
they they're only a handful of those.
>> So, you can just hand type them in
>> like to a dictionary or something.
>> Yeah, exactly. So at the moment what I
have is a dictionary of weirdly
capitalized words and what I do is I
apply a case insensitive regular
expression and replace the matches with
the case with the actual correctly cased
value from the list.
>> So I have a list of all of these words
which I save as a JavaScript set. I
don't. Did we ever talk about sets in
the main series?
>> I didn't recognize it when you referred
to it.
>> Yeah. So, they are they're a data type
in standard JavaScript just like arrays
and strings and stuff. And they have
functions just like arrays and strings
and stuff, but they're subtly different
to arrays because a set is inherently
unique. the set 1 2 3 4 5 if you add
five is still the set 1 2 3 4 5. And so
for these kind of lists of funny
capitalizations,
having automatic dduplication by the
very nature of the data structure,
that's a really big advantage.
>> I'm not sure I followed that. How is 1 2
3 4 5 + 5 1 2 3 4 5
in set theory. If you add something to a
set that's already in the set, the set
doesn't change. An array will just
duplicate it.
>> Okay.
>> So, if I give you a list,
>> how does that work in this in this
example where you're adding NASA?
>> Okay. If I add NASA a second time
because I've lost track of the five
million acronyms.
>> Oh, okay. I would say it would. Yeah. If
it's an array, you're always kind of you
need to check the array. You'd have a
function for adding a new word and you'd
have to check your array. But when you
use a set, JavaScript just goes, okay,
the set waffles, pancakes, oatmeal plus
waffles is still the set. Waffles,
pancakes, oatmeals.
>> I want this in lots of places, Bart. I
use the Microsoft To-Do app. For some
reason, Steve and I use it for packing
certain things. and you'll see
crossstitch five times in the same list
because I keep going, "Oh, I don't want
to forget my cross stitch." But it's not
sorted or anything like that. So, it's a
really bad way to do a packing list.
>> Yeah. And if you're into maths,
>> the set class, those things like union
and all of those functions you learned
about in set theory in school.
>> Cool. Cool.
>> So, it is actually set in the
mathematical sense, but it's a really
good way to store lists that you do not
want duplication in.
>> So, I'm not going to use it everywhere.
Having discovered it for this, I I
become a big fan of sets. So, a nice
excuse to throw them in.
>> I'm glad I asked for more detail on that
because I didn't follow it at first, now
I totally get it. Excellent.
>> Yeah. And so, what this means is that
with just my set of weirdly capitalized
words, all of my acronyms can be easily
handled because NASA can be handled just
the same as iPod and iPad. And I am the
next item on my to-do list and there's
actually a GitHub um issue open to
myself is I'm going to add a second set
of regular expressions so that I can
handle things like so called with the
dash between it cuz that has to be a
regular expression that maps so space
called to so dash called
>> whereas with the funny capitalized words
it's just single strings I need to add
to my set but again I can do you know
that's on the feature list So with a
surprisingly
not horrifically long set,
I have pretty much got all of the
acronyms that show up regularly in
security bits and let's talk Apple taken
care of now. So all of those happen
automatically. So my fallback is
actually pretty good apart from the
month when it's Apple earnings call.
That month I have a lot of corrections
to do because there's billions
everywhere. But you know most of the
time it works out fine.
Um then we come to the title casing
because that too has some nuance you've
already mentioned. So I had naively
assumed that because title case is so
ubiquitous across the internet there
would be like a standard which defines
the set of so-called small words. Those
words that don't get their first letter
capitalized. the an a to right all of
these little joiner words they don't get
capitalized and I just assumed that at
least in the English- speakaking world
there will be a standard set of small
words no there isn't most people agree
on most of them but there's edge cases
everywhere
and so I've actually ended up I used a
really really nice module for doing the
title casing that has no dependencies
and it's a nice clean module and it
comes with what it thinks is the right
answer for the set of small words and it
doesn't treat it and its as small words
and I think capital it t and capital it
ts looks ridiculous.
So what I what I do in my code is I take
their list as the default add my own
list of extra words to create a set of
the actual words to use which I make
editable as linkifier
small words.
>> Okay. So if I like it's with a capital I
could change it for me but you don't
have to do it for you. Okay.
>> Exactly. So you could call the delete
function from the set and pull it out of
the set.
>> I feel a little bit better knowing that
it's not u it isn't really defined and
agreed upon because I sit there arguing
with myself sometimes. Apparently that's
why.
>> Yeah. And so the official advice is to
have a style guide for your
organization. And in my case, the style
guide has been cemented into the code
for linkifier. I am 100% consistent
because I'm not doingistent my script.
>> Yeah. Exactly. Exactly.
>> I like it.
>> Okay. So, that is how linkifier works.
And the last thing I want to end on here
is to tie us back to part A, choosing my
dependencies. So, I did not do
everything here from scratch. I've
alluded quite a few times to using
different modules. So let's go through
to implement this three classes and then
a fourth master class and a three-step
process. What dependencies did I end up
needing? How many of them? Well, the
answer is six, which isn't a huge
amount, but it's not zero. So what did I
need? The first one I needed is an
amazing module called Cheerio. It's a
browserless jQuery.
>> jQuery without a browser. Oh, it's a so
good. You just
>> Wait a minute. jQuery requires a
browser.
>> Well, if you Yeah, you can't use jQuery
in Node.js. jQuery uses the DOM. The DOM
is part of the browser.
>> Oh, yeah. Yeah. Yeah. Okay.
>> So, Cheerio builds its own DOM. So,
Cheerio takes as an argument a string of
HTML. It builds a DOM and lets you query
it with the dollar syntax and it
intentionally duplicates jQuery syntax.
So, it's designed for jQuery programmers
who want to be on the terminal, who want
to be out of the browser.
>> By the way, you're referring to these as
modules, but I'm used to hearing you
refer to things like as as dictionaries.
>> I'm sorry, as libraries.
>> Okay. So, library is a language agnostic
term. So you would describe reusable
code in JavaScript as a library and
reusable code in Pearl as a library and
reusable code in Ruby as a library. But
in JavaScript, you implement libraries
with modules. And in Ruby, you implement
libraries with gems.
And what was the other example I gave? I
don't remember.
>> All of these that you're listing here
are modules, which could also be called
libraries, but they're called modules
because you're speaking in JavaScript.
Got it. Bing bing bing. Yeah. Library is
like a super set.
>> Oh yeah. Okay.
>> So, Cheerio is the first one. Tens of
millions of weekly downloads. Actively
maintained, a very active GitHub repo,
excellent documentation,
and despite everything it does, 11
dependencies.
>> Oh, wow.
>> That's not too bad, you know. So, my my
six has multiplied a bit, but still not
too bad. mustache. We love mustache.
Millions of downloads. Not very actively
maintained, but there's no known
vulnerabilities and I think it's not
very actively maintained because it's
kind of finished.
>> It's kind of done. Yeah, that's okay.
>> Yeah, it does what it does and it's
fine. Um, it has a gate hub repo, but
there's not much happening there. It
also has zero dependencies,
so yay. And the documentation's fine.
It's not pretty,
>> but it was good enough for us in
programming by stealth, so it's fine.
Node- fetch actually fetches the HTML
for me. It again has not just millions
of downloads, hundreds of millions. That
was the first time I'd seen such a big
number. So, it's like, well, I'm
definitely not going out on a limb here.
>> That's sick.
>> I am with the crowd. Yeah.
uh very actively maintained. Um GitHub
repo, it's not as active as the module
itself, but there's still stuff going
on. Only three dependencies
>> and superb documentation.
>> Okay.
>> Uh title case used for converting the
string to title case. Millions of
downloads. A lot of people want to do
this.
>> Um it appears to be still maintained.
There's not much going on, but I also
think it's one of those it's done.
>> It's not solving that hard of a problem.
>> It's just let's all use this one cuz
somebody solved it.
>> Exactly. The documentation's a bit poor,
>> but it is sufficient. I was able to get
what I needed and no dependencies. So
again, I haven't expanded my dependency
tree here. So far, only uh 14 extra
dependencies. Not bad. Uh URL.js JS is
an amazing URL parser.
>> URI.js.
>> Thank you.
>> Okay.
>> Thank you very much indeed.
>> Yeah. So URI.js is an amazing parser. So
it lets you take any URL and give me
everything after the question mark and
all that kind of stuff and strip it out
and jiggle it all.
>> This ages ago.
>> We did. I'm a huge fan. Uh millions
downloads. It was very actively
maintained. It's not that active these
days, but there's no vulnerabilities and
it's possible I'm going to have to find
an alternative to this sometime in the
next 5 years. I get the feeling the
developer has lost interest a bit, but
for now it's still fine.
>> You said it was a decade ago it was
maintained.
>> Yeah.
>> Okay.
>> But there's no vulnerabilities, so
>> it's still okay.
>> What dependencies?
>> Zero.
>> Okay.
>> So I I've still had 14 extra. Not bad.
And then URL-slug, which can actually it
can make slugs, which I don't care
about, but it also has a dlugify
function. So that's what I care about.
>> That's just fun to say
>> again, isn't it? Hundreds of thousands
of downloads, very actively maintained,
zero dependencies.
>> Wow. You got out with 14. I don't think
I've ever downloaded anything like in in
uh homebrew that didn't have 126
dependencies.
But bear in mind, I chose these
carefully because I could have very
easily picked similar dependencies that
would have achieved the same goal that
came with like 50 extra dependencies
each cuz they were programmed by lazy
programmers.
>> Okay.
>> So, when you choose carefully, you can
end up with 6 + 14. That's a total of 20
in my entire tree. That's not too bad.
>> Now, what's interesting is that becomes
an advantage of your module. Right now,
somebody looks at it and they might say,
"Well, there are maybe hundreds of
millions of downloads a week, but look,
it's got 14 dependencies. That's pretty
good."
>> Yeah, exactly.
>> So, that brings us to the end of how the
link module works. We have our
three-step process, but we don't have an
interface to it
>> at all, which sets us up for part C.
Hopefully, it's a cliffhanger. It's a
cliffhanger.
And I don't I believe our summer pseudo
hiatus is at an end. So I think we're
business as usual in two weeks.
>> I think it's a miracle. I don't think we
really did summer hiatus this time.
We've gone a couple of months without
it. And I think we we might not be on
the perfect every two weeks cadence, but
we're we've done far better this year.
Yeah, I think we skipped at least two,
maybe three, but that's not bad.
>> Not in a row.
>> Way worse.
>> Yeah, considering how much I've been
traveling.
>> Oh, yeah. Jesus, you've been halfway
around the globe. You must have a
millionaire miles.
>> Yeah, I know this is an evergreen
segment, but this audience, the
programming by stealth audience, doesn't
know that uh a week ago we were sitting
in the same room while Bart cooked us
dinner.
That's the only the second time since
we've gotten to know each other that
we've actually met in real life. So,
what a what a treat
>> it was. It was so good to have you guys
over. I enjoyed every minute of those
four days.
Right. Well, at that we shall draw a
temporary line under it. So, until next
time, happy computing.
>> I hope you enjoyed this episode of
Chitchat Across the Pond. Did you notice
there weren't any ads in the show?
That's because this show is not
adupported. It's supported by you. If
you learned something or maybe you were
just entertained, consider contributing
to the Podfeed podcast. You can do that
by going over to podfeed.com and look
for the big red button that says support
the show. When you click that button,
you're going to find different ways to
contribute. You can donate one time
through the big donate button with a
credit card or Apple Pay, or you can use
PayPal. If you want to make a recurring
contribution, click the Patreon button.
Keep in mind I don't charge Patreon for
chithat across the pond or program I buy
stealth episodes just once a month for
the no siliccast that keeps it simple.
If you want to contact me for any reason
you can email me at allisonpodfey.com
and you can follow me on mastadon at
podfey.com/mastadon.
If you want to talk to other noilic ways
you can do that in our slack group at
podfey.com/slack.
Thanks for listening and stay
subscribed.