Skip to content

Commit 19f7585

Browse files
committed
Add content src/site/notes/Projects/digital garden/articles/create an url shortener in your quartz blog.md
1 parent cc8c7cd commit 19f7585

File tree

1 file changed

+144
-0
lines changed

1 file changed

+144
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
{"title":"Create An URL Shortener in your Quartz Blog","slug":"url-shortener-in-quartz-blog","created":"2025-01-22T14:45","project":["[[noobthink.com]]"],"dg-publish":true,"permalink":"/projects/digital-garden/articles/create-an-url-shortener-in-your-quartz-blog/","dgPassFrontmatter":true,"updated":"2025-01-22T14:56:43.924+01:00"}
3+
---
4+
5+
# Create An URL Shortener in your Quartz Blog
6+
Here's how to create a simple URL shortener for your Quartz blog. This guide will walk you through implementing a redirect feature using a custom Quartz plugin. This allows you to specify a `redirectUrl` in your markdown frontmatter, turning your blog page into a redirect to another site. It's pretty barebones (no click tracking, no UI to manage the redirects) but it does the job.
7+
8+
## Crafting a Redirect Emitter Plugin
9+
10+
First, you need to create the core plugin responsible for generating the redirect pages. Create a new file at `quartz/plugins/emitters/RedirectEmitter.ts` with the following content:
11+
12+
```typescript
13+
import { QuartzEmitterPlugin, BuildCtx, ProcessedContent, StaticResources } from "../types";
14+
import { FilePath } from "../../util/path";
15+
import { promises as fs } from "fs";
16+
import path from "path";
17+
18+
interface Options {
19+
enableRedirects?: boolean
20+
}
21+
22+
const defaultOptions: Options = {
23+
enableRedirects: true
24+
}
25+
26+
export const RedirectEmitter: QuartzEmitterPlugin<Options> = (opts?: Options) => ({
27+
name: "RedirectEmitter",
28+
29+
getQuartzComponents(ctx: BuildCtx) {
30+
return [];
31+
},
32+
33+
async emit(ctx: BuildCtx, content: ProcessedContent[], resources: StaticResources): Promise<FilePath[]> {
34+
const filePaths: FilePath[] = [];
35+
36+
for (const [tree, file] of content) {
37+
const redirectUrl = file.data?.frontmatter?.redirectUrl;
38+
39+
if (redirectUrl) {
40+
const targetUrl = redirectUrl.startsWith('http') ? redirectUrl : `/${redirectUrl.replace(/^\//, '')}`;
41+
42+
const redirectHtml = `
43+
<!DOCTYPE html>
44+
<html lang="en-US">
45+
<head>
46+
<meta charset="UTF-8">
47+
<meta http-equiv="refresh" content="0;url=${targetUrl}">
48+
<script>
49+
window.location.replace("${targetUrl}");
50+
</script>
51+
<title>Redirecting...</title>
52+
</head>
53+
<body>
54+
<p>Redirecting to <a href="${targetUrl}">${targetUrl}</a>...</p>
55+
</body>
56+
</html>`;
57+
58+
const outputPath = path.join(ctx.argv.output, file.data.slug! + ".html");
59+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
60+
await fs.writeFile(outputPath, redirectHtml);
61+
const fp = file.data.slug! + ".html";
62+
63+
filePaths.push(fp);
64+
}
65+
}
66+
67+
return filePaths;
68+
},
69+
});
70+
```
71+
72+
This code defines a `RedirectEmitter` plugin. Let's break down what's happening:
73+
74+
- **Imports**: It imports necessary types from Quartz and modules for file system operations and path manipulation.
75+
- **Options Interface**: It defines an `Options` interface to optionally configure the plugin. Currently, it only includes `enableRedirects`, set to `true` by default.
76+
- **Plugin Definition**: `RedirectEmitter` is defined as a `QuartzEmitterPlugin`. Emitter plugins in Quartz are responsible for taking processed content and outputting files.
77+
- **`emit` Function**: This is the core of the plugin. It iterates through each file in your content.
78+
- It checks for the `redirectUrl` property in the frontmatter of each file.
79+
- If `redirectUrl` exists, it constructs the `targetUrl`, ensuring it's properly formatted (adding a leading `/` if necessary for internal redirects, or keeping it as is for external URLs).
80+
- It generates minimal HTML for redirection. This HTML uses both `<meta http-equiv="refresh">` and JavaScript's `window.location.replace()` to ensure immediate redirection. This dual approach enhances reliability across different browsers.
81+
- It writes this HTML to a file in your output directory, using the original file's slug to create the HTML file name (e.g., `my-post.html`).
82+
- The plugin then adds the generated file path to the `filePaths` array, which is returned to Quartz.
83+
84+
## Registering the Plugin
85+
86+
To enable the `RedirectEmitter`, you need to register it in your `quartz.config.ts` file. Open `quartz.config.ts` and modify the `plugins` array within the `configuration` block to include `Plugin.RedirectEmitter({ enableRedirects: true })`:
87+
88+
```ts
89+
Plugin.Assets(),
90+
Plugin.Static(),
91+
Plugin.NotFoundPage(),
92+
// The three above should already be present
93+
Plugin.RedirectEmitter({ enableRedirects: true }),
94+
```
95+
96+
By adding `Plugin.RedirectEmitter({ enableRedirects: true })`, you're telling Quartz to use your new plugin during the build process. The `enableRedirects: true` option is currently the only configuration, and it's set to true by default in the plugin, but it's good practice to include it explicitly.
97+
98+
## Excluding Redirect Pages from Recent Notes
99+
100+
If you leave it at that, it will work but then your redirect pages will appear in your recent notes list. Usually you don't want that. To exclude them, modify the `RecentNotes.tsx` component to filter out pages with the `redirectUrl` frontmatter property. Open `quartz/components/RecentNotes.tsx` and update the `pages` filtering logic:
101+
102+
```ts
103+
// Replace this line
104+
const pages = allFiles.filter(opts.filter).sort(opts.sort)
105+
// With the following
106+
const pages = allFiles
107+
.filter(opts.filter)
108+
.filter(page => !page.frontmatter?.redirectUrl) // Filter out pages with redirectUrl
109+
.sort(opts.sort)
110+
// rest of existing code
111+
const remaining = Math.max(0, pages.length - opts.limit)
112+
return (
113+
<div class={classNames(displayClass, "recent-notes")}>
114+
```
115+
116+
This change adds `.filter(page => !page.frontmatter?.redirectUrl)` to the page filtering chain. This ensures that any page with a `redirectUrl` in its frontmatter will be excluded from the list of recent notes.
117+
118+
## Exporting the Plugin
119+
120+
Finally, ensure your new plugin is exported so Quartz can find it. Open `quartz/plugins/index.ts` and add the following line to the exports:
121+
122+
```ts
123+
// After this line
124+
export * from "./emitters"
125+
// Add
126+
export { RedirectEmitter } from "./emitters/RedirectEmitter"
127+
```
128+
129+
## Using the Redirect Feature
130+
131+
Now, to use the redirect feature, simply add the `redirectUrl` property to the frontmatter of your markdown file. For example:
132+
133+
```markdown
134+
---
135+
title: My Redirect Page
136+
redirectUrl: https://example.com
137+
---
138+
139+
This content will be ignored. You will be redirected.
140+
```
141+
142+
When Quartz builds your site, it will generate an HTML file for this markdown page that immediately redirects to `https://example.com`. The original markdown content will be effectively bypassed, and the page will function as a URL shortener.
143+
144+
Refer to the [Quartz official docs](https://quartz.jzhao.xyz/advanced/making-plugins) if you want to understand plugins in detail.

0 commit comments

Comments
 (0)