Reading List
The most recent articles from a list of feeds I subscribe to.
Better code highlighting in Hugo with Torchlight
During my latest redesign, I replaced Hugo’s default code highlighting with Torchlight. In this post, I’ll explain how I set up Torchlight CLI for my Hugo site. (Although this can be applied to any static site.)
Torchlight is a code-highlighter-as-a-service built on Visual Studio Code’s editor highlighter editor. You throw blocks of code to Torchlight and they return them in a highlighted form. This results in a more complete highlight than alternatives like highlight.js, and a lot of available themes. Torchlight also supports less popular syntaxes like Laravel Blade.
In addition, Torchlight has a number of custom annotations. For example, you can collapse code by wrapping it between [tl! collapse:start] and [tl! collapse:end].
module.exports = { // [tl! collapse:start]
// Your token from https://torchlight.dev
token: process.env.TORCHLIGHT_TOKEN, // [tl! collapse:end]
}
To set up Torchlight with Hugo, we’ll need to follow a few steps:
- Register on torchlight.dev and generate an API token
- Install Torchlight CLI
- Configure Torchlight
- Update our deploy script
Register on torchlight.dev
You can create a free account on torchlight.dev for personal and open source projects. When you have an account set up, generate a personal token to get started with the API.
Torchlight CLI
For a static site, Torchlight CLI is the most straightforward way to get going.
npm i -g @torchlight-api/torchlight-cli
npx torchlight
The CLI tool scans a folder for HTML files, looks for <pre><code> blocks, passes them through the API and overwrites the original HTML. That means our statis site generator doesn’t need to know about Torchlight and vice-versa. Hugo generates HTML, then Torchlight transforms it.
Torchlight configuration
You’ll also need a torchlight.config.js file to configure Torchlight and store your token.
Here’s the config used by this site (less relevant parts are collapsed):
module.exports = {
// Your token from https://torchlight.dev
token: process.env.TORCHLIGHT_TOKEN,
// The Torchlight client caches highlighted code blocks. Here you
// can define which directory you'd like to use. You'll likely
// want to add this directory to your .gitignore. Set to
// `false` to use an in-memory cache. You may also
// provide a full cache implementation.
cache: false,
// Which theme you want to use. You can find all of the themes at
// https://torchlight.dev/docs/themes.
theme: "fortnite",
// The Host of the API.
host: "https://api.torchlight.dev",
// Global options to control block-level settings.
// https://torchlight.dev/docs/options
options: { // [tl! collapse:start]
// Turn line numbers on or off globally.
lineNumbers: false,
// Control the `style` attribute applied to line numbers.
// lineNumbersStyle: '',
// Turn on +/- diff indicators.
diffIndicators: true,
// If there are any diff indicators for a line, put them
// in place of the line number to save horizontal space.
diffIndicatorsInPlaceOfLineNumbers: true,
// When lines are collapsed, this is the text that will
// be shown to indicate that they can be expanded.
summaryCollapsedIndicator: 'Click to expand…', // [tl! collapse:end]
},
// Options for the highlight command.
highlight: {
// Directory where your un-highlighted source files live. If
// left blank, Torchlight will use the current directory.
input: "public",
// Directory where your highlighted files should be placed. If
// left blank, files will be modified in place.
output: "", // [tl! collapse:start]
// Globs to include when looking for files to highlight.
includeGlobs: ["**/*.htm", "**/*.html"],
// String patterns to ignore (not globs). The entire file
// path will be searched and if any of these strings
// appear, the file will be ignored.
excludePatterns: ["/node_modules/", "/vendor/"], // [tl! collapse:end]
},
};
Things that stand out:
token: We’ll pass the token through an environment variable, more on that belowcache: We’ll only run Torchlight when deploying, so no need for a cachetheme: My syntax highlighting theme of choice, there are many themes availablehighlight.input: This is where Torchlight CLI will scan files, thepublicfolder is where Hugo outputs the final HTML.highlight.output: This is where Torchlight CLI will store the highlighted HTML files, it’s empty so Torchlight will overwrite the input files.
Deployment
This site is hosted on Netlify, netlify.toml has the following build command set up:
[build]
command = "hugo --minify"
publish = "public"
Since Torchlight needs to run after Hugo builds the site, we can chain a few additional commands.
[build]
command = """
hugo --minify
npm i -g @torchlight-api/torchlight-cli
npx torchlight
"""
publish = "public"
After adding a TORCHLIGHT_TOKEN environment variable in Netlify’s UI we’re up and running With the Torchlight configuration we set up earlier, Torchlight will highlight all files in public before they get published by Netlify.
Local development
The biggest tradeoff with Torchlight is build speed. Hugo is fast (like, really fast), and adding a tool that relies on network requests is going to slow things down considerably.
I haven’t configured Torchlight to run when watching for changes with hugo server because I value the instant build speed more when writing. Before I deploy, I’ll often run the full build command locally to double check, but I don’t really care about the highlighting while I’m in the middle of editing.
That’s all you need to know to set up Torchlight with a static site. Thanks to Aaron Francis for making Torchlight free for personal use!
Better code highlighting in Hugo with Torchlight
During my latest redesign, I replaced Hugo’s default code highlighting with Torchlight. In this post, I’ll explain how I set up Torchlight CLI for my Hugo site. (Although this can be applied to any static site.)
Torchlight is a code-highlighter-as-a-service built on Visual Studio Code’s editor highlighter editor. You throw blocks of code to Torchlight and they return them in a highlighted form. This results in a more complete highlight than alternatives like highlight.js, and a lot of available themes. Torchlight also supports less popular syntaxes like Laravel Blade.
In addition, Torchlight has a number of custom annotations. For example, you can collapse code by wrapping it between [tl! collapse:start] and [tl! collapse:end].
module.exports = { // [tl! collapse:start]
// Your token from https://torchlight.dev
token: process.env.TORCHLIGHT_TOKEN, // [tl! collapse:end]
}
To set up Torchlight with Hugo, we’ll need to follow a few steps:
- Register on torchlight.dev and generate an API token
- Install Torchlight CLI
- Configure Torchlight
- Update our deploy script
Register on torchlight.dev
You can create a free account on torchlight.dev for personal and open source projects. When you have an account set up, generate a personal token to get started with the API.
Torchlight CLI
For a static site, Torchlight CLI is the most straightforward way to get going.
npm i -g @torchlight-api/torchlight-cli
npx torchlight
The CLI tool scans a folder for HTML files, looks for <pre><code> blocks, passes them through the API and overwrites the original HTML. That means our statis site generator doesn’t need to know about Torchlight and vice-versa. Hugo generates HTML, then Torchlight transforms it.
Torchlight configuration
You’ll also need a torchlight.config.js file to configure Torchlight and store your token.
Here’s the config used by this site (less relevant parts are collapsed):
module.exports = {
// Your token from https://torchlight.dev
token: process.env.TORCHLIGHT_TOKEN,
// The Torchlight client caches highlighted code blocks. Here you
// can define which directory you'd like to use. You'll likely
// want to add this directory to your .gitignore. Set to
// `false` to use an in-memory cache. You may also
// provide a full cache implementation.
cache: false,
// Which theme you want to use. You can find all of the themes at
// https://torchlight.dev/docs/themes.
theme: "fortnite",
// The Host of the API.
host: "https://api.torchlight.dev",
// Global options to control block-level settings.
// https://torchlight.dev/docs/options
options: { // [tl! collapse:start]
// Turn line numbers on or off globally.
lineNumbers: false,
// Control the `style` attribute applied to line numbers.
// lineNumbersStyle: '',
// Turn on +/- diff indicators.
diffIndicators: true,
// If there are any diff indicators for a line, put them
// in place of the line number to save horizontal space.
diffIndicatorsInPlaceOfLineNumbers: true,
// When lines are collapsed, this is the text that will
// be shown to indicate that they can be expanded.
summaryCollapsedIndicator: 'Click to expand…', // [tl! collapse:end]
},
// Options for the highlight command.
highlight: {
// Directory where your un-highlighted source files live. If
// left blank, Torchlight will use the current directory.
input: "public",
// Directory where your highlighted files should be placed. If
// left blank, files will be modified in place.
output: "", // [tl! collapse:start]
// Globs to include when looking for files to highlight.
includeGlobs: ["**/*.htm", "**/*.html"],
// String patterns to ignore (not globs). The entire file
// path will be searched and if any of these strings
// appear, the file will be ignored.
excludePatterns: ["/node_modules/", "/vendor/"], // [tl! collapse:end]
},
};
Things that stand out:
token: We’ll pass the token through an environment variable, more on that belowcache: We’ll only run Torchlight when deploying, so no need for a cachetheme: My syntax highlighting theme of choice, there are many themes availablehighlight.input: This is where Torchlight CLI will scan files, thepublicfolder is where Hugo outputs the final HTML.highlight.output: This is where Torchlight CLI will store the highlighted HTML files, it’s empty so Torchlight will overwrite the input files.
Deployment
This site is hosted on Netlify, netlify.toml has the following build command set up:
[build]
command = "hugo --minify"
publish = "public"
Since Torchlight needs to run after Hugo builds the site, we can chain a few additional commands.
[build]
command = """
hugo --minify
npm i -g @torchlight-api/torchlight-cli
npx torchlight
"""
publish = "public"
After adding a TORCHLIGHT_TOKEN environment variable in Netlify’s UI we’re up and running With the Torchlight configuration we set up earlier, Torchlight will highlight all files in public before they get published by Netlify.
Local development
The biggest tradeoff with Torchlight is build speed. Hugo is fast (like, really fast), and adding a tool that relies on network requests is going to slow things down considerably.
I haven’t configured Torchlight to run when watching for changes with hugo server because I value the instant build speed more when writing. Before I deploy, I’ll often run the full build command locally to double check, but I don’t really care about the highlighting while I’m in the middle of editing.
That’s all you need to know to set up Torchlight with a static site. Thanks to Aaron Francis for making Torchlight free for personal use!
↗ Good Ideas
Nat Eliason on ideas and fermented jalapeños:
Good ideas require boredom. If you constantly ingest new information, the existing information can never be digested.
Coming up with ideas is a passive undertaking, not an active one. You can’t summon good ideas like a genie. They sneak up on you when your mind has enough space to wander.
Local or session storage?
Local storage tends to be the obvious place to persist data locally in a web application. We tend to grab straight for localStorage, but it’s not the only tool in our workbox. Another option is sessionStorage. Let’s review their similarities and differences, and determine when to use which.
localStorage and sessionStorage are part of the Web Storage API have the same key/value mechanism. We can write with setItem, and read with getItem.
localStorage.setItem('scheme', 'dark');
const colorScheme = localStorage.getItem('scheme');
sessionStorage.setItem('scheme', 'dark');
const colorScheme = sessionStorage.getItem('scheme');
They both store data as strings, but can store objects, arrays, and other primitive values as JSON with JSON.stringify and JSON.parse.
localStorage.setItem(
'preferences',
JSON.stringify({ scheme: 'dark' })
);
const preferences = JSON.parse(
localStorage.getItem('preferences') || '{}'
);
These are pretty naive snippets. When dealing with storage, try/catch is advices to avoid storage limits and other caveats. This post explains it better than I ever could.
So when to use sessionStorage over localStorage?
window.localStorage
localStorage is long-living, and shared across all visits to the same domain. Unless the user explicitly clears their cache, data will remain persisted.
It’s a good fit for things you want to remember forever. It’s still a local cache, so forever means “as long as possible.”
- Critical application data, like a JWT to authenticate a user
- Long-lived application preferences, like a dark mode toggle
- Long-lived choices a user makes on a site, like a country picker
- Data in an app with offline support, either as a cache for server data or as a queue for commands to sync with the server when it’s back online
window.sessionStorage
sessionStorage has a short lifetime, and is shared across all visits to the same URL in that tab. When the user closes their tab, stored data is discarded.
It’s good fit for things you want persisted for a short time.
- Page state, like a datatable filter
- Draft content, like the contents of a textarea for a comment
- Scroll positions or dynamic elements on a page, so they’re restored when the user hits the back button
General guidelines
In my experience, localStorage vs sessionStorage generally boils down to:
- If you want to store something that applies to the entire site or app, use
localStorage - If you want to store something that applies to a specific page, use
sessionStorage
This all highly subjective and depends on context. Datatable filters or draft content make more sense in localStorage. It depends on whether users expect to return to the page with a clean slate, or be able to pick up exactly where they left off.
There are more ways to store data locally like HTTP cookies, History.state, or IndexedDB. They each have their own use, but none are as straightforward as the Web Storage API.
Local or session storage?
Local storage tends to be the obvious place to persist data locally in a web application. We tend to grab straight for localStorage, but it’s not the only tool in our workbox. Another option is sessionStorage. Let’s review their similarities and differences, and determine when to use which.
localStorage and sessionStorage are part of the Web Storage API have the same key/value mechanism. We can write with setItem, and read with getItem.
localStorage.setItem('scheme', 'dark');
const colorScheme = localStorage.getItem('scheme');
sessionStorage.setItem('scheme', 'dark');
const colorScheme = sessionStorage.getItem('scheme');
They both store data as strings, but can store objects, arrays, and other primitive values as JSON with JSON.stringify and JSON.parse.
localStorage.setItem(
'preferences',
JSON.stringify({ scheme: 'dark' })
);
const preferences = JSON.parse(
localStorage.getItem('preferences') || '{}'
);
These are pretty naive snippets. When dealing with storage, try/catch is advices to avoid storage limits and other caveats. This post explains it better than I ever could.
So when to use sessionStorage over localStorage?
window.localStorage
localStorage is long-living, and shared across all visits to the same domain. Unless the user explicitly clears their cache, data will remain persisted.
It’s a good fit for things you want to remember forever. It’s still a local cache, so forever means “as long as possible.”
- Critical application data, like a JWT to authenticate a user
- Long-lived application preferences, like a dark mode toggle
- Long-lived choices a user makes on a site, like a country picker
- Data in an app with offline support, either as a cache for server data or as a queue for commands to sync with the server when it’s back online
window.sessionStorage
sessionStorage has a short lifetime, and is shared across all visits to the same URL in that tab. When the user closes their tab, stored data is discarded.
It’s good fit for things you want persisted for a short time.
- Page state, like a datatable filter
- Draft content, like the contents of a textarea for a comment
- Scroll positions or dynamic elements on a page, so they’re restored when the user hits the back button
General guidelines
In my experience, localStorage vs sessionStorage generally boils down to:
- If you want to store something that applies to the entire site or app, use
localStorage - If you want to store something that applies to a specific page, use
sessionStorage
This all highly subjective and depends on context. Datatable filters or draft content make more sense in localStorage. It depends on whether users expect to return to the page with a clean slate, or be able to pick up exactly where they left off.
There are more ways to store data locally like HTTP cookies, History.state, or IndexedDB. They each have their own use, but none are as straightforward as the Web Storage API.