diff --git a/package-lock.json b/package-lock.json index e0defd0..c244269 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "astro": "^3.1.2", - "fast-glob": "^3.3.1", + "astro": ">= 3", + "globby": "^13.2.2", "gray-matter": "^4.0.3" }, "devDependencies": { @@ -25,6 +25,10 @@ "typescript": "^5.2.2", "vite": "^4.4.9" }, + "engines": { + "node": ">=18.14.1", + "npm": ">=6.14.0" + }, "peerDependencies": { "astro": ">= 3" } @@ -3101,7 +3105,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, "dependencies": { "path-type": "^4.0.0" }, @@ -4123,7 +4126,6 @@ "version": "13.2.2", "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "dev": true, "dependencies": { "dir-glob": "^3.0.1", "fast-glob": "^3.3.0", @@ -4571,7 +4573,6 @@ "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, "engines": { "node": ">= 4" } @@ -7523,7 +7524,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } @@ -9024,7 +9024,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, "engines": { "node": ">=12" }, diff --git a/package.json b/package.json index 1ec4c14..3892ecc 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,12 @@ "release": "release-it --non-interactive", "prepublishOnly": "npm run build" }, + "exports": { + ".": "./dist/index.js" + }, + "files": [ + "dist" + ], "devDependencies": { "@types/node": "^20.6.3", "@typescript-eslint/eslint-plugin": "^6.7.2", @@ -32,7 +38,7 @@ }, "dependencies": { "astro": ">= 3", - "fast-glob": "^3.3.1", + "globby": "^13.2.2", "gray-matter": "^4.0.3" }, "peerDependencies": { diff --git a/src/getRedirects.ts b/src/getRedirects.ts new file mode 100644 index 0000000..51f84c3 --- /dev/null +++ b/src/getRedirects.ts @@ -0,0 +1,32 @@ +import path from 'node:path' +import type { Redirects } from './types' +import { getMarkdownFrontmatter } from './utils' + +export async function getRedirects( + files: string[], + srcDir: string, + getSlug: (filePath: string) => string +) { + const redirects: Redirects = {} + + for (const file of files) { + const frontmatter = await getMarkdownFrontmatter(path.join(srcDir, file)) + const redirectFrom: string[] = frontmatter?.redirect_from + if ( + !frontmatter || + !redirectFrom || + (import.meta.env.PROD && frontmatter.draft === true) + ) + continue + + let postSlug = frontmatter.slug + if (!postSlug) postSlug = getSlug(file) + if (!postSlug) continue + + for (const slug of redirectFrom) { + redirects[slug] = postSlug + } + } + + return redirects +} diff --git a/src/index.ts b/src/index.ts index 2b048d4..e042123 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,73 +1,43 @@ -import fs from 'node:fs/promises' import path from 'node:path' -import fg from 'fast-glob' -import matter from 'gray-matter' -import type { AstroIntegration, AstroIntegrationLogger } from 'astro' -import { getSlugFromFilePath, writeJson } from './utils' - -type PluginOptions = { - contentDir?: string - getSlug?: (filePath: string) => string -} +import type { AstroIntegration } from 'astro' +import type { PluginOptions } from './types' +import { getMarkdownFiles, getSlugFromFilePath, writeJson } from './utils' +import { getRedirects } from './getRedirects' export default function astroRedirectFrom({ contentDir = 'src/pages/', getSlug = getSlugFromFilePath -}: PluginOptions = {}): AstroIntegration { - const sourceDir = path.join(process.cwd(), contentDir) +}: PluginOptions): AstroIntegration { + const contentDirPath = path.join(process.cwd(), contentDir) return { name: 'redirect-from', hooks: { - 'astro:config:setup': async ({ - updateConfig, - logger - }: { - updateConfig: (newConfig: Record) => void - logger: AstroIntegrationLogger - }) => { - const redirects: { [old: string]: string } = {} - + 'astro:config:setup': async ({ config, updateConfig, logger }) => { try { - const markdownFiles = await fg.glob('./**/*.{md,mdx}', { - cwd: sourceDir - }) + const markdownFiles = await getMarkdownFiles(contentDirPath) if (!markdownFiles?.length) { logger.warn('No markdown files found') return } - for (const markdownFile of markdownFiles) { - const fileContent = await fs.readFile( - path.join(sourceDir, markdownFile), - 'utf-8' - ) - - const { data: frontmatter } = matter(fileContent) - const postRedirectFrom: string[] = frontmatter?.redirect_from - if ( - !frontmatter || - !postRedirectFrom || - // filter out drafts in production - (import.meta.env.PROD && frontmatter.draft === true) - ) - continue - - let postSlug = frontmatter.slug - if (!postSlug) postSlug = getSlug(markdownFile) - if (!postSlug) continue - - for (const slug of postRedirectFrom) { - redirects[slug] = postSlug - } + const redirects = await getRedirects( + markdownFiles, + contentDirPath, + getSlug + ) + if (!redirects || !Object.keys(redirects).length) { + logger.info('No redirects found in markdown files') + return } updateConfig({ redirects }) - await writeJson( - path.join(process.cwd(), '.astro', 'redirect_from.json'), - redirects + const redirectFilePath = path.join( + config.cacheDir.pathname, // Default is ./node_modules/.astro/ + 'redirect_from.json' ) + await writeJson(redirectFilePath, redirects) logger.info( `Added ${Object.keys(redirects).length} redirects to Astro config` diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..d3bb47b --- /dev/null +++ b/src/types.ts @@ -0,0 +1,8 @@ +export type GetSlug = (filePath: string) => string + +export type PluginOptions = { + contentDir?: string + getSlug?: GetSlug +} + +export type Redirects = { [old: string]: string } diff --git a/src/utils.ts b/src/utils.ts index af1c572..3a6768a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,21 @@ import path from 'node:path' import { promises as fs, type PathLike } from 'node:fs' +import { globby } from 'globby' +import matter from 'gray-matter' + +export async function getMarkdownFiles(sourceDir: string) { + const markdownFiles = await globby('./**/*.{md,mdx}', { + cwd: sourceDir, + gitignore: true + }) + return markdownFiles +} + +export async function getMarkdownFrontmatter(filePath: string) { + const fileContent = await fs.readFile(filePath, { encoding: 'utf-8' }) + const { data: frontmatter } = matter(fileContent) + return frontmatter +} export function getSlugFromFilePath(filePath: string) { const parsedPath = path.parse(filePath) diff --git a/tsconfig.json b/tsconfig.json index 7738258..21dcd6e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,10 @@ "exclude": ["node_modules", "dist"], "compilerOptions": { "outDir": "./dist", - "types": ["vite/client"] + "types": ["vite/client"], + "declaration": true, + // ovewrite astro/tsconfigs values + "noEmit": false, + "allowImportingTsExtensions": false } }