Skip to content

Commit

Permalink
post
Browse files Browse the repository at this point in the history
  • Loading branch information
paulcjy committed Dec 15, 2023
1 parent 987a20b commit e017e04
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 3 deletions.
2 changes: 1 addition & 1 deletion posts/Web/GitHub Pages + Next.js/05-markdown-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import 'your_css_file.css'

Contentlayer 예제에서 작성한 `posts/[slug]/page.tsx`에서 마크다운이 들어간 부분의 `<div>` 태그의 `class``markdown-body`를 추가한다.

```ts
```tsx
const PostLayout = ({ params }: { params: { slug: string } }) => {
const post = allPosts.find((post) => post._raw.flattenedPath === params.slug)
if (!post) throw new Error(`Post not found for slug: ${params.slug}`)
Expand Down
4 changes: 2 additions & 2 deletions posts/Web/GitHub Pages + Next.js/06-highlight-codeblock.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
---
title: Contentlayer에서 코드블럭 하이라이트
title: Contentlayer에서 highlight.js로 코드블럭 하이라이트
created: 2023-11-18 21:30:00
tags: [contentlayer, markdown, highlight.js]
---

마크다운에 깃허브 스타일을 적용했지만, 코드블럭은 하이라이트되지 않는다. Contentlayer에서는 플러그인 설치와 설정만 하면 쉽게 코드를 하이라이트 할 수 있다. Contentlayer 공식 문서에서 [rehype-highlight][2]를 사용했기 때문에 그대로 따라했다.

[Contentlayer 마크다운 하이라이트 예제][1]
[Contentlayer 마크다운 하이라이트 예시][1]

# rehype-highlight 설치

Expand Down
188 changes: 188 additions & 0 deletions posts/Web/GitHub Pages + Next.js/20-highlight-codeblock-2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
---
title: Contentlayer에서 깃허브 스타일(starry-night)로 코드블럭 하이라이트
created: 2023-12-15 11:08:00
tags:
[
contentlayer,
github,
css,
markdown,
highlight,
code block,
starry night,
pretty lights,
]
---

# 문제

처음 마크다운 스타일을 적용할 때, Contentlayer에서 제공하는 예시를 따라 highlight.js를 이용하여 코드블럭을 하이라이팅 했다. 작동은 잘 됐지만 아쉬운 점이 있었다. GitHub Actions에 관한 글을 썼는데, workflow를 작성할 때 사용하는 yml 파일의 하이라이팅이 깃허브와 달랐다.

highlight.js - yml

![highlight.js yml][1]

github markdown - yml

![github yml][2]

# starry-night

깃허브는 코드블럭 하이라이팅에 [PrettyLights][3]를 사용한다. `PrettyLights`는 closed source이다. 그리고 `PrettyLights`를 JavaScript로 만든 오픈 소스가 [starry-night][4]이다.

![starry-night html][5]

깃허브 코드블럭의 소스를 보면 `<div>` 태그의 클래스명에는 `highlight`, `highlight-language-type`이 들어가고, 코드블럭 내부의 `<span>` 태그에는 `pl-`이라는 접두사가 붙는다. `pl``PrettyLights`이다.

`starry-night`를 사용하면 `PrettyLights`와 똑같이 코드를 하이라이팅 할 수 있다.

# Contentlayer + starry-night

Contentlayer와 함께 사용하려면 `starry-night`-`rehype`가 필요하다. [starry-night example: integrate with unified, remark, and rehype][6]에서 친절하게 `rehype` 코드까지 제공한다.

## 설치

`starry-night`를 설치하고, `rehype`를 위해서 두 개의 패키지를 더 설치한다.

```sh
npm install @woorm/starry-night hast-util-to-string unist-util-visit
```

## rehype

`rehype-starry-night.js` 파일을 만든 뒤 아래 코드를 붙여넣는다.

> `starry-night`에서 제공하는 코드를 그대로 사용하니, 몇몇 언어는 하이라이팅이 지원되지 않았다. 이유는 예시 코드가 `common`에 해당하는 언어들만을 대상으로 했기 때문이었다. 그래서 `common``all`로 바꿔줬다.
```js
/**
* @typedef {import('@wooorm/starry-night').Grammar} Grammar
* @typedef {import('hast').ElementContent} ElementContent
* @typedef {import('hast').Root} Root
*/

/**
* @typedef Options
* Configuration (optional)
* @property {Array<Grammar> | null | undefined} [grammars]
* Grammars to support (default: `common`).
*/

import { all, createStarryNight } from '@wooorm/starry-night' // 'common' 대신 'all' 사용
import { toString } from 'hast-util-to-string'
import { visit } from 'unist-util-visit'

/**
* Highlight code with `starry-night`.
*
* @param {Options | null | undefined} [options]
* Configuration (optional).
* @returns
* Transform.
*/
export default function rehypeStarryNight(options) {
const settings = options || {}
const grammars = settings.grammars || all // 'common' 대신 'all' 사용
const starryNightPromise = createStarryNight(grammars)
const prefix = 'language-'

/**
* Transform.
*
* @param {Root} tree
* Tree.
* @returns {Promise<undefined>}
* Nothing.
*/
return async function (tree) {
const starryNight = await starryNightPromise

visit(tree, 'element', function (node, index, parent) {
if (!parent || index === undefined || node.tagName !== 'pre') {
return
}

const head = node.children[0]

if (!head || head.type !== 'element' || head.tagName !== 'code') {
return
}

const classes = head.properties.className

if (!Array.isArray(classes)) return

const language = classes.find(function (d) {
return typeof d === 'string' && d.startsWith(prefix)
})

if (typeof language !== 'string') return

const scope = starryNight.flagToScope(language.slice(prefix.length))

// Maybe warn?
if (!scope) return

const fragment = starryNight.highlight(toString(head), scope)
const children = /** @type {Array<ElementContent>} */ (fragment.children)

parent.children.splice(index, 1, {
type: 'element',
tagName: 'div',
properties: {
className: [
'highlight',
'highlight-' + scope.replace(/^source\./, '').replace(/\./g, '-'),
],
},
children: [
{ type: 'element', tagName: 'pre', properties: {}, children },
],
})
})
}
}
```

## Contentlayer 적용

`contentlayer.config.js`에서 `rehype` 설정을 한다. `makeSource``markdown` > `rehypePlugins`에 넣는다.

```ts
import { makeSource } from '@contentlayer/source-files'
import rehypeStarryNight from './../rehype-starry-night.js'

export default makeSource({
// ...
markdown: { rehypePlugins: [rehypeStarryNight] },
})
```

주의할 점은 `rehype` 플러그인을 import 할 때 `#`이나 `@`같은 경로 alias를 사용하면 안된다. Contentlayer가 생성하는 파일에서는 적용이 되지 않으므로 상대경로로 입력해야 한다.

이렇게 하면 하이라이팅은 적용되지 않지만 html 클래스명에는 `PrettyLights`가 적용된 것을 볼 수 있다. CSS 파일만 세팅해주면 하이라이팅도 적용된다.

![plain text with PrettyLights][7]

![html with PrettyLights][8]

# GitHub Markdown CSS

[마크다운에 깃허브 스타일 적용하기][9] 참고

해당 CSS 파일에는 기본적으로 `PrettyLights` 하이라이팅이 포함되어 있다. `starry-night`만 잘 적용되었다면 코드블럭은 자동으로 하이라이팅이 될 것이다.

별도로 수정해야 하는 부분은 두 가지이다.

- 다크 모드와 라이트 모드를 구분하는 부분을 미디어 쿼리에서 클래스로 바꾸기
- 배경색(`--color-canvas-default`)을 지정된 색상에서 `transparent`로 바꾸기

[1]: https://github.com/paulcjy/paulcjy.github.io/assets/86853786/dba4cddd-d11a-4593-b7f5-59927ad3ec98 'highlight.js yml'
[2]: https://github.com/paulcjy/paulcjy.github.io/assets/86853786/fd8c51b6-40db-40e4-8009-26110ed69094 'github yml'
[3]: https://github.com/wooorm/starry-night#what-is-prettylights 'starry-night github - PrettyLights'
[4]: https://github.com/wooorm/starry-night 'starry-night github'
[5]: https://github.com/paulcjy/paulcjy.github.io/assets/86853786/ced5f9c5-6fd9-42f9-a1d3-729f544d2bf7 'starry-night html'
[6]: https://github.com/wooorm/starry-night/#example-integrate-with-unified-remark-and-rehype 'starry-night github - rehype'
[7]: https://github.com/paulcjy/paulcjy.github.io/assets/86853786/15f59864-5149-42ea-bc82-2a0c56301604 'plain text with PrettyLights'
[8]: https://github.com/paulcjy/paulcjy.github.io/assets/86853786/869549d8-9906-4c0d-a7d0-a2cc682ad0bf 'starry-night html 2'
[9]: https://paulcjy.github.io/blog/GitHub-Pages-Next.js/05-markdown-style 'github-markdown'

0 comments on commit e017e04

Please sign in to comment.