Next.jsを使って静的なブログサイトを作成する(2)
はじめに
Next.jsのBlog Starter Kitを使ったサイト作成の続編です。
rehype-highlightを使ってシンタックスハイライトを実現します。
テーマごとの見た目はhttps://highlightjs.org/demoで確認できます。
他にマークダウンや目次を設定します。
シンタックスハイライトを使えるようにする
ターミナルを立ち上げてプロジェクトのトップへ移動します。
> cd C:\website\blog-starter-app
必要なパッケージをインストールします。
> npm i unified
> npm i remark-parse
> npm i remark-rehype
> npm i rehype-highlight
> npm i rehype-stringify
> npm i highlight.js
「src\lib\markdownToHtml.ts」をエディタで開くと以下のようになってます。
なお比較のため改行を追加してます。
import { remark } from "remark";
import html from "remark-html";
export default async function markdownToHtml(markdown: string) {
const result = await remark()
.use(html)
.process(markdown);
return result.toString();
}
上のコードではマークダウン->HTMLを以下のように役割分担しています。
- remark: マークダウン -> マークダウンの木構造
- remark-html: マークダウンの木構造 -> HTMLの木構造 -> HTML
これを以下のように変更するとシンタックスハイライトを実現できます。
- remark: マークダウン -> マークダウンの木構造
- remark-rehype: マークダウンの木構造 -> HTMLの木構造
- rehype-highlight: HTMLの木構造 -> HTMLの木構造
- rehype-stringify: HTMLの木構造 -> HTML
さらに今後の変更に備え、remarkを unified と remark-parse で置き換えます。
- remark-parse: マークダウン -> マークダウンの木構造
- remark-rehype: マークダウンの木構造 -> HTMLの木構造
- rehype-highlight: HTMLの木構造 -> HTMLの木構造
- rehype-stringify: HTMLの木構造 -> HTML
以上を参考に「src\lib\markdownToHtml.ts」を以下のように変更します。
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeHighlight from 'rehype-highlight';
import rehypeStringify from 'rehype-stringify';
export default async function markdownToHtml(markdown: string) {
const result = await unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeHighlight)
.use(rehypeStringify)
.process(markdown);
return result.toString();
}
好みのテーマをインポートします。ここではa11y-darkを使います。「src/app/layout.tsx」 内に以下の行を追加します。
import "highlight.js/styles/a11y-dark.css";
以上でシンタックスハイライトが使えるようになってます。
記事のmdファイルで
```js
console.log("Hello");
```
のように記載すると、生成されるHTMLは以下のように表示されます。
console.log("Hello");
マークダウンにHTMLタグを埋め込めるようにする
mdファイルにHTMLタグを記載しても
<p>HTML</p>
Markdown
変換の過程でその内容は消えてしまいます。
Markdown
HTMLタグを反映させるにはremark-rehypeとrehype-stringifyにallowDangerousHtmlオプションを設定します。
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeHighlight from 'rehype-highlight';
import rehypeStringify from 'rehype-stringify';
export default async function markdownToHtml(markdown: string) {
const result = await unified()
.use(remarkParse)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeHighlight)
.use(rehypeStringify, { allowDangerousHtml: true })
.process(markdown);
return result.toString();
}
参考:
https://zenn.dev/yodaka/articles/cee5943b92dfed
GitHub風のマークダウン記法を使えるようにする
Blog Starter Kitで最初から使えるマークダウンは機能が弱いので、remark-gfmを導入します。あと<br>改行が簡単に使えるようにremark-breaksも導入します。
プロジェクトのトップへ移動して、インストールを実行します。
> npm i remark-gfm
> npm i remark-breaks
「src\lib\markdownToHtml.ts」を以下のように変更します。
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkGfm from 'remark-gfm';
import remarkBreaks from 'remark-breaks';
import remarkRehype from 'remark-rehype';
import rehypeHighlight from 'rehype-highlight';
import rehypeStringify from 'rehype-stringify';
export default async function markdownToHtml(markdown: string) {
const result = await unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkBreaks)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeHighlight)
.use(rehypeStringify, { allowDangerousHtml: true })
.process(markdown);
return result.toString();
}
目次を表示させる
プロジェクトのトップへ移動して、インストールを実行します。
> npm i rehype-toc
> npm i rehype-slug
rehype-tocだけでも目次は作成できますが、リンクが働きません。
rehype-slugは見出しにIDを振って、リンクを有効にしてくれます。
「src\lib\markdownToHtml.ts」を以下のように変更します。
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkGfm from 'remark-gfm';
import remarkBreaks from 'remark-breaks';
import remarkRehype from 'remark-rehype';
import rehypeSlug from 'rehype-slug';
import rehypeToc from 'rehype-toc';
import rehypeHighlight from 'rehype-highlight';
import rehypeStringify from 'rehype-stringify';
export default async function markdownToHtml(markdown: string) {
const result = await unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkBreaks)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeSlug)
.use(rehypeToc, { headings: ["h2"],})
.use(rehypeHighlight)
.use(rehypeStringify, { allowDangerousHtml: true })
.process(markdown);
return result.toString();
}
<h2>タグで囲まれた見出しを拾って、記事の先頭に目次が作成されます。
デフォルト以外の言語をシンタックスハイライトする
デフォルトでシンタックスハイライトできるのは、以下の表の「初期」欄に丸がついている言語になります。
| 言語 | エイリアス | 初期 |
|---|---|---|
| 1C | 1c | |
| 4D | 4d | |
| ABAP | sap-abap, abap | |
| ABNF | abnf | |
| Access logs | accesslog | |
| ActionScript | actionscript, as | |
| Ada | ada | |
| Alan | ln | |
| Alan IF | alan, i | |
| AngelScript | angelscript, asc | |
| Apache | apache, apacheconf | |
| Apex | apex | |
| AppleScript | applescript, osascript | |
| Arcade | arcade | |
| Arduino (C++ w/Arduino libs) | arduino, ino | |
| ARM assembler | armasm, arm | |
| AsciiDoc | asciidoc, adoc | |
| AspectJ | aspectj | |
| AutoHotkey | autohotkey | |
| AutoIt | autoit | |
| AVR assembler | avrasm | |
| Awk | awk, mawk, nawk, gawk | |
| Ballerina | ballerina, bal | |
| Bash | bash, sh, zsh | ● |
| Basic | basic | |
| BBCode | bbcode | |
| Blade (Laravel) | blade | |
| BNF | bnf | |
| BQN | bqn | |
| Brainfuck | brainfuck, bf | |
| C | c, h | ● |
| C# | csharp, cs | ● |
| C/AL | cal | |
| C++ | cpp, hpp, cc, hh, c++, h++, cxx, hxx | ● |
| C3 | c3 | |
| Cache Object Script | cos, cls | |
| Candid | candid, did | |
| Cap’n Proto | capnproto, capnp | |
| Chaos | chaos, kaos | |
| Chapel | chapel, chpl | |
| Cisco CLI | cisco | |
| Clojure | clojure, clj | |
| CMake | cmake, cmake.in | |
| COBOL | cobol, standard-cobol | |
| CODEOWNERS | codeowners | |
| CoffeeScript | coffeescript, coffee, cson, iced | |
| Coq | coq | |
| CpcdosC+ | cpc | |
| Crmsh | crmsh, crm, pcmk | |
| Crystal | crystal, cr | |
| CSP | csp | |
| CSS | css | ● |
| cURL | curl | |
| Cypher (Neo4j) | cypher | |
| D | d | |
| Dafny | dafny | |
| Dart | dart | |
| Delphi | dpr, dfm, pas, pascal | |
| Diff | diff, patch | ● |
| Django | django, jinja | |
| DNS Zone file | dns, zone, bind | |
| Dockerfile | dockerfile, docker | |
| DOS | dos, bat, cmd | |
| dsconfig | dsconfig | |
| DTS (Device Tree) | dts | |
| Dust | dust, dst | |
| Dylan | dylan | |
| EBNF | ebnf | |
| Elixir | elixir | |
| Elm | elm | |
| Erlang | erlang, erl | |
| Excel | excel, xls, xlsx | |
| Extempore | extempore, xtlang, xtm | |
| F# | fsharp, fs, fsx, fsi, fsscript | |
| FIX | fix | |
| Flix | flix | |
| Fortran | fortran, f90, f95 | |
| FunC | func | |
| Gams | gams, gms | |
| GAUSS | gauss, gss | |
| G-Code | gcode, nc | |
| GDScript | godot, gdscript | |
| Gherkin | gherkin | |
| Glimmer and EmberJS | hbs, glimmer, html.hbs, html.handlebars, htmlbars | |
| GN for Ninja | gn, gni | |
| Go | go, golang | ● |
| Golo | golo, gololang | |
| Gradle | gradle | |
| Grammatical Framework | gf | |
| GraphQL | graphql, gql | ● |
| Groovy | groovy | |
| GSQL | gsql | |
| Haml | haml | |
| Handlebars | handlebars, hbs, html.hbs, html.handlebars | |
| Haskell | haskell, hs | |
| Haxe | haxe, hx | |
| High-level shader language | hlsl | |
| HTML, XML | xml, html, xhtml, rss, atom, xjb, xsd, xsl, plist, svg | ● |
| HTTP | http, https | |
| Hy | hy, hylang | |
| Inform7 | inform7, i7 | |
| Ini, TOML | ini, toml | ● |
| Iptables | iptables | |
| IRPF90 | irpf90 | |
| Java | java, jsp | ● |
| JavaScript | javascript, js, jsx | ● |
| Jolie | jolie, iol, ol | |
| JSON | json, jsonc | ● |
| JSONata | jsonata | |
| Julia | julia, jl | |
| Julia REPL | julia-repl | |
| Kotlin | kotlin, kt | ● |
| Lang | ||
| Lasso | lasso, ls, lassoscript | |
| LaTeX | tex | |
| LDIF | ldif | |
| Leaf | leaf | |
| Lean | lean | |
| Less | less | ● |
| Lisp | lisp | |
| LiveCode Server | livecodeserver | |
| LiveScript | livescript, ls | |
| LookML | lookml | |
| Lua | lua | ● |
| Luau | luau | |
| Macaulay2 | macaulay2 | |
| Makefile | makefile, mk, mak, make | ● |
| Markdown | markdown, md, mkdown, mkd | ● |
| Mathematica | mathematica, mma, wl | |
| Matlab | matlab | |
| Maxima | maxima | |
| Maya Embedded Language | mel | |
| Mercury | mercury | |
| Mint | mint | |
| MIPS Assembler | mips, mipsasm | |
| mIRC Scripting Language | mirc, mrc | |
| Mirth | mirth | |
| Mizar | mizar | |
| MKB | mkb | |
| MLIR | mlir | |
| Mojolicious | mojolicious | |
| Monkey | monkey | |
| Moonscript | moonscript, moon | |
| Motoko | motoko, mo | |
| N1QL | n1ql | |
| Never | never | |
| Nginx | nginx, nginxconf | |
| Nim | nim, nimrod | |
| Nix | nix | |
| NSIS | nsis | |
| Oak | oak | |
| Object Constraint Language | ocl | |
| Objective C | objectivec, mm, objc, obj-c, obj-c++, objective-c++ | ● |
| OCaml | ocaml, ml | |
| OpenGL Shading Language | glsl | |
| OpenSCAD | openscad, scad | |
| Oracle Rules Language | ruleslanguage | |
| Oxygene | oxygene | |
| Papyrus | papyrus, psc | |
| Parser3 | parser3 | |
| Perl | perl, pl, pm | ● |
| PF | pf, pf.conf | |
| Phix | phix | |
| PHP | php | ● |
| Pine Script | pine, pinescript | |
| Plaintext | plaintext, txt, text | ● |
| Pony | pony | |
| PostgreSQL & PL/pgSQL | pgsql, postgres, postgresql | |
| PowerShell | powershell, ps, ps1 | |
| Processing | processing | |
| Prolog | prolog | |
| Properties | properties | |
| Protocol Buffers | proto, protobuf | |
| Puppet | puppet, pp | |
| Python | python, py, gyp | ● |
| Python profiler results | profile | |
| Python REPL | python-repl, pycon | ● |
| Q | k, kdb | |
| Q# | qsharp | |
| QML | qml | |
| R | r | ● |
| Razor CSHTML | cshtml, razor, razor-cshtml | |
| ReasonML | reasonml, re | |
| Rebol & Red | redbol, rebol, red, red-system | |
| RenderMan RIB | rib | |
| RenderMan RSL | rsl | |
| ReScript | rescript, res | |
| RiScript | risc, riscript | |
| RISC-V Assembly | riscv, riscvasm | |
| Roboconf | graph, instances | |
| Robot Framework | robot, rf | |
| RPM spec files | rpm-specfile, rpm, spec, rpm-spec, specfile | |
| Ruby | ruby, rb, gemspec, podspec, thor, irb | ● |
| Rust | rust, rs | ● |
| RVT Script | rvt, rvt-script | |
| SAS | SAS, sas | |
| Scala | scala | |
| Scheme | scheme | |
| Scilab | scilab, sci | |
| SCSS | scss | ● |
| SFZ | sfz | |
| Shape Expressions | shexc | |
| Shell | shell, console | ● |
| Smali | smali | |
| Smalltalk | smalltalk, st | |
| SML | sml, ml | |
| Solidity | solidity, sol | |
| Splunk SPL | spl | |
| SQL | sql | ● |
| Stan | stan, stanfuncs | |
| Stata | stata | |
| STEP Part 21 | p21, step, stp | |
| Structured Text | iecst, scl, stl, structured-text | |
| Stylus | stylus, styl | |
| SubUnit | subunit | |
| Supercollider | supercollider, sc | |
| Svelte | svelte | |
| Swift | swift | ● |
| Tcl | tcl, tk | |
| Terraform (HCL) | terraform, tf, hcl | |
| Test Anything Protocol | tap | |
| Thrift | thrift | |
| Toit | toit | |
| TP | tp | |
| Transact-SQL | tsql | |
| Twig | twig, craftcms | |
| TypeScript | typescript, ts, tsx, mts, cts | ● |
| Unicorn Rails log | unicorn-rails-log | |
| Unison | unison, u | |
| Vala | vala | |
| VB.Net | vbnet, vb | ● |
| VBA | vba | |
| VBScript | vbscript, vbs | |
| Verilog | verilog, v | |
| VHDL | vhdl | |
| Vim Script | vim | |
| WGSL | wgsl | |
| X# | xsharp, xs, prg | |
| X++ | axapta, x++ | |
| x86 Assembly | x86asm | |
| x86 Assembly (AT&T) | x86asmatt | |
| XL | xl, tao | |
| XQuery | xquery, xpath, xq, xqm | |
| YAML | yml, yaml | ● |
| ZenScript | zenscript, zs | |
| Zephir | zephir, zep | |
| Zig | zig |
デフォルト以外の言語をシンタックスハイライトしたい場合は(ここではVBScriptを追加したいとします)、rehype-highlightのlanguagesオプションを使います。これによりデフォルトの言語はシンタックスハイライトされなくなりますので、必要な言語はすべて指定してください。ここでは以下の言語を指定します。
- Bash
- C++
- CSS
- HTML(XML)
- JavaScript
- Markdown
- Plaintext
- Shell
- TypeScript
- VBScript
「src\lib\markdownToHtml.ts」を以下のように変更します。必要な言語をimportしてlanguagesオプションに渡します。
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkGfm from 'remark-gfm';
import remarkBreaks from 'remark-breaks';
import remarkRehype from 'remark-rehype';
import rehypeSlug from 'rehype-slug';
import rehypeToc from 'rehype-toc';
import rehypeHighlight from 'rehype-highlight';
import rehypeStringify from 'rehype-stringify';
import bash from 'highlight.js/lib/languages/bash';
import cpp from 'highlight.js/lib/languages/cpp';
import css from 'highlight.js/lib/languages/css';
import html from 'highlight.js/lib/languages/xml';
import js from 'highlight.js/lib/languages/javascript';
import md from 'highlight.js/lib/languages/markdown';
import text from 'highlight.js/lib/languages/plaintext';
import shell from 'highlight.js/lib/languages/shell';
import ts from 'highlight.js/lib/languages/typescript';
import vbs from 'highlight.js/lib/languages/vbscript';
export default async function markdownToHtml(markdown: string) {
const result = await unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkBreaks)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeSlug)
.use(rehypeToc, { headings: ["h2"],})
.use(rehypeHighlight, {languages: {bash, cpp, css, html, js, md, text, shell, ts, vbs}})
.use(rehypeStringify, { allowDangerousHtml: true })
.process(markdown);
return result.toString();
}
もしかすると、デフォルトの言語をもっと簡単に指定する方法があるのかも知れませんが、やり方がわかりませんでした。
参考
https://github.com/rehypejs/rehype-highlight
https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md