🖼️ added support for links to blogposts and support image sizing from .md files
This commit is contained in:
@@ -43,9 +43,8 @@ export function OptimizedImage({
|
|||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
priority={priority}
|
priority={priority}
|
||||||
className={`w-full h-auto transition-opacity duration-300 ${
|
style={{ maxWidth: '100%', height: 'auto' }}
|
||||||
isLoading ? 'opacity-0' : 'opacity-100'
|
className={`transition-opacity duration-300 ${isLoading ? 'opacity-0' : 'opacity-100'}`}
|
||||||
}`}
|
|
||||||
onLoad={() => setIsLoading(false)}
|
onLoad={() => setIsLoading(false)}
|
||||||
onError={() => setHasError(true)}
|
onError={() => setHasError(true)}
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ export default function Page() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Check out this article:
|
||||||
|
|
||||||
|
[Check this out](tech/articol-tehnic.md)
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
Next.js 15 brings many improvements for building modern web applications.
|
Next.js 15 brings many improvements for building modern web applications.
|
||||||
|
|||||||
@@ -1,45 +1,16 @@
|
|||||||
---
|
---
|
||||||
title: 'Articol Tehnic din Subdirector'
|
title: 'Technical Article'
|
||||||
description: 'Test pentru subdirectoare și organizare ierarhică'
|
description: 'A technical article to test internal links'
|
||||||
date: '2025-01-10'
|
date: '2025-01-10'
|
||||||
author: 'Tech Writer'
|
author: 'John Doe'
|
||||||
category: 'Tehnologie'
|
category: 'Tech'
|
||||||
tags: ['nextjs', 'react', 'typescript']
|
tags: ['tech', 'test']
|
||||||
draft: false
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Articol Tehnic
|
# Technical Article
|
||||||
|
|
||||||
Acesta este un articol stocat într-un subdirector pentru a testa funcționalitatea de organizare ierarhică.
|
This is a test article for internal blog post linking.
|
||||||
|
|
||||||
## Next.js și React
|
## Content
|
||||||
|
|
||||||
Next.js este un framework React puternic care oferă:
|
You are reading the technical article that was linked from the example post.
|
||||||
|
|
||||||
- Server-side rendering (SSR)
|
|
||||||
- Static site generation (SSG)
|
|
||||||
- API routes
|
|
||||||
- File-based routing
|
|
||||||
|
|
||||||
## Exemplu de cod TypeScript
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface User {
|
|
||||||
id: number
|
|
||||||
name: string
|
|
||||||
email: string
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchUser(id: number): Promise<User> {
|
|
||||||
const response = await fetch(`/api/users/${id}`)
|
|
||||||
return response.json()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Use of coolers
|
|
||||||
|
|
||||||
- 
|
|
||||||
|
|
||||||
## Concluzie
|
|
||||||
|
|
||||||
Subdirectoarele funcționează perfect pentru organizarea conținutului!
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import remarkGfm from 'remark-gfm'
|
|||||||
import { FrontMatter, Post } from './types/frontmatter'
|
import { FrontMatter, Post } from './types/frontmatter'
|
||||||
import { generateExcerpt } from './utils'
|
import { generateExcerpt } from './utils'
|
||||||
import { remarkCopyImages } from './remark-copy-images'
|
import { remarkCopyImages } from './remark-copy-images'
|
||||||
|
import { remarkInternalLinks } from './remark-internal-links'
|
||||||
|
|
||||||
const POSTS_PATH = path.join(process.cwd(), 'content', 'blog')
|
const POSTS_PATH = path.join(process.cwd(), 'content', 'blog')
|
||||||
|
|
||||||
@@ -75,6 +76,7 @@ export async function getPostBySlug(slug: string | string[]): Promise<Post | nul
|
|||||||
publicDir: 'public/blog',
|
publicDir: 'public/blog',
|
||||||
currentSlug: sanitized.join('/'),
|
currentSlug: sanitized.join('/'),
|
||||||
})
|
})
|
||||||
|
.use(remarkInternalLinks)
|
||||||
.process(content)
|
.process(content)
|
||||||
|
|
||||||
const processedContent = processed.toString()
|
const processedContent = processed.toString()
|
||||||
|
|||||||
67
lib/remark-internal-links.ts
Normal file
67
lib/remark-internal-links.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { visit } from 'unist-util-visit'
|
||||||
|
import { Node } from 'unist'
|
||||||
|
|
||||||
|
interface LinkNode extends Node {
|
||||||
|
type: 'link'
|
||||||
|
url: string
|
||||||
|
children: Node[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects internal blog post links:
|
||||||
|
* - Relative paths (no http/https)
|
||||||
|
* - Not absolute paths (doesn't start with /)
|
||||||
|
* - Ends with .md
|
||||||
|
*/
|
||||||
|
function isInternalBlogLink(url: string): boolean {
|
||||||
|
return (
|
||||||
|
!url.startsWith('http://') &&
|
||||||
|
!url.startsWith('https://') &&
|
||||||
|
!url.startsWith('/') &&
|
||||||
|
url.includes('.md')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms internal .md links to blog routes:
|
||||||
|
* - tech/article.md → /blog/tech/article
|
||||||
|
* - article.md#section → /blog/article#section
|
||||||
|
* - nested/path/post.md?ref=foo → /blog/nested/path/post?ref=foo
|
||||||
|
*/
|
||||||
|
function transformToBlogPath(url: string): string {
|
||||||
|
// Split into path, hash, and query
|
||||||
|
const hashIndex = url.indexOf('#')
|
||||||
|
const queryIndex = url.indexOf('?')
|
||||||
|
|
||||||
|
let path = url
|
||||||
|
let hash = ''
|
||||||
|
let query = ''
|
||||||
|
|
||||||
|
if (hashIndex !== -1) {
|
||||||
|
path = url.substring(0, hashIndex)
|
||||||
|
hash = url.substring(hashIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryIndex !== -1 && queryIndex < (hashIndex === -1 ? url.length : hashIndex)) {
|
||||||
|
path = url.substring(0, queryIndex)
|
||||||
|
query = url.substring(queryIndex, hashIndex === -1 ? url.length : hashIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove .md extension
|
||||||
|
const cleanPath = path.replace(/\.md$/, '')
|
||||||
|
|
||||||
|
// Build final URL
|
||||||
|
return `/blog/${cleanPath}${query}${hash}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function remarkInternalLinks() {
|
||||||
|
return (tree: Node) => {
|
||||||
|
visit(tree, 'link', (node: Node) => {
|
||||||
|
const linkNode = node as LinkNode
|
||||||
|
|
||||||
|
if (isInternalBlogLink(linkNode.url)) {
|
||||||
|
linkNode.url = transformToBlogPath(linkNode.url)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
import "./.next/types/routes.d.ts";
|
import "./.next/dev/types/routes.d.ts";
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
|
|||||||
Reference in New Issue
Block a user