diff --git a/.config/astro.config.ts b/.config/astro.config.ts index 26d145de..985be56c 100644 --- a/.config/astro.config.ts +++ b/.config/astro.config.ts @@ -4,7 +4,7 @@ import remarkToc from '../src/lib/remark-toc' import react from '@astrojs/react' import sitemap from '@astrojs/sitemap' import expressiveCode from 'astro-expressive-code' -import redirectFrom from '../src/lib/astro-redirect-from/src' +import redirectFrom from 'astro-redirect-from' import config from './blog.config' import { getSlug } from '../src/lib/astro/getSlug' diff --git a/README.md b/README.md index 101719fb..59bbfe53 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ - [📝 GitHub changelog rendering](#-github-changelog-rendering) - [🌗 Theme Switcher](#-theme-switcher) - [💎 SVG assets as components](#-svg-assets-as-components) - - [`redirect_from`](#redirect_from) + - [astro-redirect-from](#astro-redirect-from) - [RSS \& JSON feeds](#rss--json-feeds) - [✨ Development](#-development) - [🔮 Linting](#-linting) @@ -145,15 +145,11 @@ If you want to know how this works, have a look at the respective files: - [`scripts/create-icons/`](scripts/create-icons/) -### `redirect_from` +### astro-redirect-from -Still a remnant of the old [Jekyll](https://jekyllrb.com) days, which survived in [gatsby-redirect-from](/gatsby-redirect-from/) and now works in Astro. For all post slugs defined in a `redirect_from` frontmatter key, redirects will be put in place by Astro. +Still a remnant of the old [Jekyll](https://jekyllrb.com) days, which survived in [gatsby-redirect-from](https://kremalicious.com/gatsby-redirect-from/) and now works in Astro with [astro-redirect-from](https://kremalicious.com/astro-redirect-from/). -Before building the site, a script scans all markdown files and creates a json file under `.config/redirects.json`. This file is then imported into `astro.config.ts` under its `redirects` option. - -If you want to know how, have a look at the respective files: - -- [`scripts/redirect-from.ts`](scripts/redirect-from.ts) +For all post slugs defined in a `redirect_from` frontmatter key, redirects will be put in place by Astro. ### RSS & JSON feeds diff --git a/package-lock.json b/package-lock.json index 8d1547e8..7e4da3ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,8 +16,9 @@ "@astrojs/ts-plugin": "^1.1.3", "@nanostores/react": "^0.7.1", "@rainbow-me/rainbowkit": "^1.0.11", - "astro": "^3.1.0", - "astro-expressive-code": "^0.22.2", + "astro": "^3.1.2", + "astro-expressive-code": "^0.26.0", + "astro-redirect-from": "^0.2.1", "date-fns": "^2.30.0", "dms2dec": "^1.1.0", "fast-exif": "^2.0.1", @@ -338,8 +339,9 @@ } }, "node_modules/@astrojs/telemetry": { - "version": "3.0.1", - "license": "MIT", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.0.2.tgz", + "integrity": "sha512-ef+jqCkqopCzjGfsMsr+8p56Nj6F9ZzouWcWZt+dKsqbRccI3c8K3jfkLcdq4AyfFZtKBDB6N4ZuI68g33oiOg==", "dependencies": { "ci-info": "^3.8.0", "debug": "^4.3.4", @@ -356,7 +358,8 @@ }, "node_modules/@astrojs/telemetry/node_modules/is-docker": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "bin": { "is-docker": "cli.js" }, @@ -369,7 +372,8 @@ }, "node_modules/@astrojs/telemetry/node_modules/is-wsl": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.0.0.tgz", + "integrity": "sha512-TQ7xXW/fTBaz/HhGSV779AC99ocpvb9qJPuPwyIea+F+Z+htcQ1wouAA0xEQaa4saVqyP8mwkoYp5efeM/4Gbg==", "dependencies": { "is-docker": "^3.0.0" }, @@ -1000,9 +1004,9 @@ } }, "node_modules/@expressive-code/core": { - "version": "0.22.2", - "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.22.2.tgz", - "integrity": "sha512-fVfnopl4dz75KgZ8q9G6sL+GW7QAzuWnNrm4zTKRabRzwdTZ9MCUmGPJvUpxKovJ1Z4t6YIKGHTson0a7fvV5g==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.26.0.tgz", + "integrity": "sha512-hhvRbIDiUX3DI3CnX8gDCY5lgn7TzhVz8PGunAfhAoIWp0+P72NSLoaLIg4FwOXFWEomTRLfkGk2cVQrppCXmQ==", "dependencies": { "@ctrl/tinycolor": "^3.6.0", "hast-util-to-html": "^8.0.4", @@ -1043,29 +1047,29 @@ } }, "node_modules/@expressive-code/plugin-frames": { - "version": "0.22.2", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.22.2.tgz", - "integrity": "sha512-Tn4COPTdySVJ6gygCCqYd0KMQXea4l6NN/9Px2uSekPDLUiE9Ff4i3005Pa1rr31m0hLBes4POnFRRmwqIu+ZA==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.26.0.tgz", + "integrity": "sha512-mFPTh8tE1mItQcfJM6F6p8aWaa4Pi+KceBMIW6IsByQZKyMGdyS+PYphn0KbUjYI2za0/DVIJdukM9XueSpJ3A==", "dependencies": { - "@expressive-code/core": "^0.22.2", + "@expressive-code/core": "^0.26.0", "hastscript": "^7.2.0" } }, "node_modules/@expressive-code/plugin-shiki": { - "version": "0.22.2", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.22.2.tgz", - "integrity": "sha512-BDNkEb2OwmoL5kJJnNZ6fXk5IytZordEWGjWycEiKyHMXotJ+94S0PIIiTfVIp38H1faL+yd+kz2pF4t7ePcww==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.26.0.tgz", + "integrity": "sha512-3qCSfDSR31joT3lQLOVw+fSSNp/ZYpZP3Os8sr1ekTa4yh41SgiwWk5rYm8PhKMGOnw8nhzlknwLkcZJf20sOQ==", "dependencies": { - "@expressive-code/core": "^0.22.2", + "@expressive-code/core": "^0.26.0", "shiki": "^0.14.1" } }, "node_modules/@expressive-code/plugin-text-markers": { - "version": "0.22.2", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.22.2.tgz", - "integrity": "sha512-9lMON0kVEn0LAIe9mHcXaxdwqnxCF7MR+IExyC4OGhMVEc1p8Vp7rvSxsUPjySQV2QvYNTlQOaKVi/eI5oM1Ag==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.26.0.tgz", + "integrity": "sha512-klBtYJf3liTOnhiKHNpuO9KC1vAYcANtSWz07yPnnQzCwkRo9bTkdOI+9xSViVisNiAF6TfCuoEKjlB7BmtP7A==", "dependencies": { - "@expressive-code/core": "^0.22.2", + "@expressive-code/core": "^0.26.0", "hastscript": "^7.2.0", "unist-util-visit-parents": "^5.1.3" } @@ -3947,14 +3951,14 @@ } }, "node_modules/astro": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/astro/-/astro-3.1.0.tgz", - "integrity": "sha512-hVPZg9uDafqJbDwOwtcujwhJ6Qp3BCaIj1cvablTYI0jdYrZSvcybhIMTf8NhzK5smvZy2Bv9eEDYXLpiLDrRQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/astro/-/astro-3.1.2.tgz", + "integrity": "sha512-9rlKyk6Qmw50Pc188+ey7JAEYBsJGh5an46AXGPMHIdEkH6YOc4b2z9Rr+TNF8QmM/U+C2ZQFRVQyCKU4i8/pg==", "dependencies": { "@astrojs/compiler": "^2.1.0", "@astrojs/internal-helpers": "0.2.0", "@astrojs/markdown-remark": "3.2.0", - "@astrojs/telemetry": "3.0.1", + "@astrojs/telemetry": "3.0.2", "@babel/core": "^7.22.10", "@babel/generator": "^7.22.10", "@babel/parser": "^7.22.10", @@ -4104,16 +4108,62 @@ } }, "node_modules/astro-expressive-code": { - "version": "0.22.2", - "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.22.2.tgz", - "integrity": "sha512-pmyuTJcEzfYzxPNBsIEjVNTzgWHxDGiv4D/oJusvOm30x7ETADMusqf9uYmJ6rS1jlwAgfaUWm7vTv6SaxWrSw==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.26.0.tgz", + "integrity": "sha512-ZofTdPVTu5yXFJJnwx/GDNwihufuGzUqn/sE1PqDWyDyDADsx28G8VitWH3KMVQ1tRSuc/rm6Y7MRgb/ydS1tA==", "dependencies": { - "remark-expressive-code": "^0.22.2" + "remark-expressive-code": "^0.26.0" }, "peerDependencies": { "astro": "^2.0.0 || ^3.0.0-beta" } }, + "node_modules/astro-redirect-from": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/astro-redirect-from/-/astro-redirect-from-0.2.1.tgz", + "integrity": "sha512-9pxLhutTVo7mPILrSrROmr5PkzK5qwM5116rYDK4MYEcPGDjoSieoiJ/6+0zNMUzJnmauTFHh6FNgURi16qcVg==", + "dependencies": { + "astro": ">= 3", + "globby": "^13.2.2", + "gray-matter": "^4.0.3" + }, + "engines": { + "node": ">=18.14.1", + "npm": ">=6.14.0" + }, + "peerDependencies": { + "astro": ">= 3" + } + }, + "node_modules/astro-redirect-from/node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/astro-redirect-from/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/astro/node_modules/@astrojs/compiler": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.1.0.tgz", @@ -4788,6 +4838,8 @@ }, "node_modules/busboy": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "dependencies": { "streamsearch": "^1.1.0" }, @@ -5001,13 +5053,14 @@ }, "node_modules/ci-info": { "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/sibiraj-s" } ], - "license": "MIT", "engines": { "node": ">=8" } @@ -5844,7 +5897,6 @@ }, "node_modules/dir-glob": { "version": "3.0.1", - "dev": true, "license": "MIT", "dependencies": { "path-type": "^4.0.0" @@ -5855,7 +5907,8 @@ }, "node_modules/dlv": { "version": "1.1.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, "node_modules/dms2dec": { "version": "1.1.0", @@ -5943,7 +5996,8 @@ }, "node_modules/dset": { "version": "3.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz", + "integrity": "sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==", "engines": { "node": ">=4" } @@ -6958,14 +7012,14 @@ } }, "node_modules/expressive-code": { - "version": "0.22.2", - "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.22.2.tgz", - "integrity": "sha512-2KOsjZKx6pRLVhlIo7ikZxL0CJzuvpP4LeGcFiz7YsqUtT3ak4MgEeD1ph82FNp2isl+vnc8OZu1xoGDi9JxMw==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.26.0.tgz", + "integrity": "sha512-1290zk/uHg+1gCiM9TNVl77FZGTTkdlaXDGSJjNffwFlaO6+nF4Mi0zFLKZ0QoPfPlucOF/DwNSVNa74Dxck1g==", "dependencies": { - "@expressive-code/core": "^0.22.2", - "@expressive-code/plugin-frames": "^0.22.2", - "@expressive-code/plugin-shiki": "^0.22.2", - "@expressive-code/plugin-text-markers": "^0.22.2" + "@expressive-code/core": "^0.26.0", + "@expressive-code/plugin-frames": "^0.26.0", + "@expressive-code/plugin-shiki": "^0.26.0", + "@expressive-code/plugin-text-markers": "^0.26.0" } }, "node_modules/extend": { @@ -8158,7 +8212,6 @@ }, "node_modules/ignore": { "version": "5.2.4", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -11713,7 +11766,6 @@ }, "node_modules/path-type": { "version": "4.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -12857,11 +12909,11 @@ } }, "node_modules/remark-expressive-code": { - "version": "0.22.2", - "resolved": "https://registry.npmjs.org/remark-expressive-code/-/remark-expressive-code-0.22.2.tgz", - "integrity": "sha512-NwVpKDHiHsD+3CwxDrXA6B2fNttVDK0BBnkHLPQoKNOuEokYWA9+3trZQw77txBGimNLsJQCBl/tddsHen2g8w==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/remark-expressive-code/-/remark-expressive-code-0.26.0.tgz", + "integrity": "sha512-kh50Wk+o/nNLqa1Z+IhGmhbzHZwiwNpAqcoPuQPBwTBqbleehlcw9m8PtHIwxMFhWqfhzXxtJj9TOmwc+AyHrA==", "dependencies": { - "expressive-code": "^0.22.2", + "expressive-code": "^0.26.0", "hast-util-to-html": "^8.0.4", "unist-util-visit": "^4.1.2" } @@ -13908,6 +13960,8 @@ }, "node_modules/streamsearch": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "engines": { "node": ">=10.0.0" } @@ -15101,8 +15155,9 @@ } }, "node_modules/undici": { - "version": "5.23.0", - "license": "MIT", + "version": "5.25.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.25.2.tgz", + "integrity": "sha512-tch8RbCfn1UUH1PeVCXva4V8gDpGAud/w0WubD6sHC46vYQ3KDxL+xv1A2UxK0N6jrVedutuPHxe1XIoqerwMw==", "dependencies": { "busboy": "^1.6.0" }, @@ -16228,7 +16283,8 @@ }, "node_modules/which-pm-runs": { "version": "1.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", + "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", "engines": { "node": ">=4" } diff --git a/package.json b/package.json index 441f3d73..9d26fd75 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,9 @@ "@astrojs/ts-plugin": "^1.1.3", "@nanostores/react": "^0.7.1", "@rainbow-me/rainbowkit": "^1.0.11", - "astro": "^3.1.0", - "astro-expressive-code": "^0.22.2", + "astro": "^3.1.2", + "astro-expressive-code": "^0.26.0", + "astro-redirect-from": "^0.2.1", "date-fns": "^2.30.0", "dms2dec": "^1.1.0", "fast-exif": "^2.0.1", diff --git a/scripts/redirect-from.test.ts b/scripts/redirect-from.test.ts deleted file mode 100644 index fa1a6a9a..00000000 --- a/scripts/redirect-from.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { test, expect, vi } from 'vitest' -import fs from 'node:fs/promises' -import { Stats } from 'node:fs' -import { findMarkdownFilesWithRedirects } from './redirect-from' - -test('findMarkdownFilesWithRedirects should generate correct redirects', async () => { - const readdirMock = vi.spyOn(fs, 'readdir') - readdirMock.mockResolvedValue(['post1.md', 'post2.md'] as any) - - const statMock = vi.spyOn(fs, 'stat') - statMock.mockResolvedValue({ isFile: () => true } as Stats) - - const readFileMock = vi.spyOn(fs, 'readFile') - readFileMock.mockResolvedValueOnce( - '---\nredirect_from: ["/old1", "/old2"]\nslug: /new1\n---' - ) - readFileMock.mockResolvedValueOnce( - '---\nredirect_from: ["/old3"]\nslug: /new2\n---' - ) - - // Mock fs.writeFile to do nothing - const writeFileMock = vi.spyOn(fs, 'writeFile') - writeFileMock.mockResolvedValue() - - const redirects = await findMarkdownFilesWithRedirects('some/dir') - - expect(redirects).toEqual({ - '/old1': '/new1', - '/old2': '/new1', - '/old3': '/new2' - }) -}) diff --git a/scripts/redirect-from.ts b/scripts/redirect-from.ts deleted file mode 100644 index 02dbf41a..00000000 --- a/scripts/redirect-from.ts +++ /dev/null @@ -1,75 +0,0 @@ -// -// astro-redirect-from -// -import fs from 'node:fs/promises' -import path from 'node:path' -import matter from 'gray-matter' -import ora from 'ora' -import chalk from 'chalk' - -const contentDir = 'content/' -const outputFilePath = '.config/redirects.json' -let fileCount = 0 - -type Frontmatter = { redirect_from?: string[]; slug?: string } - -const spinner = ora( - `${chalk.bold('[redirect-from]')} Extract redirects` -).start() - -export async function findMarkdownFilesWithRedirects( - dir: string -): Promise<{ [old: string]: string }> { - const redirects: { [old: string]: string } = {} - - async function processDir(currentDir: string) { - const items = await fs.readdir(currentDir, { recursive: true }) - - for (const item of items) { - const itemPath = path.join(currentDir, item) - const stats = await fs.stat(itemPath) - - if ( - stats.isFile() && - item.endsWith('.md') && - !itemPath.includes('links') - ) { - const fileContent = await fs.readFile(itemPath, 'utf-8') - const { data: frontmatter }: { data: Frontmatter } = matter(fileContent) - - // construct slug from frontmatter or folder name - const postSlug = - frontmatter.slug || `/${itemPath.split('/')[2].substring(11)}` - - // Check if the Markdown file has a redirect_from field - if (frontmatter.redirect_from) { - fileCount++ - const redirectFromSlugs = frontmatter.redirect_from - for (const slug of redirectFromSlugs) { - // Add entries to the redirects object - redirects[slug] = postSlug - } - } - } - } - } - - await processDir(dir) - return redirects -} - -try { - const redirects = await findMarkdownFilesWithRedirects(contentDir) - const redirectsJSON = JSON.stringify(redirects, null, 2) - - // Write the redirects object to the output file - await fs.writeFile(outputFilePath, redirectsJSON, 'utf-8') - - spinner.succeed( - `${chalk.bold('[redirect-from]')} Extracted ${ - Object.keys(redirects).length - } redirects from ${fileCount} files to ${outputFilePath}` - ) -} catch (error: any) { - spinner.fail(`${chalk.bold('[redirect-from]')} ${(error as Error).message}`) -}