Markdown

Markdownコンテンツは、ブログ投稿やドキュメントのような、テキストを多用するコンテンツを作成するためによく使用されます。AstroにはMarkdownのサポートが組み込まれており、JavaScript式のサポートや、Markdownの中にAstroコンポーネントが使用できるといった機能が追加されています。

Astroは /src/pages ディレクトリにある .md ファイルを1つのページとして扱います。このディレクトリ、または任意のサブディレクトリにファイルを置くと、ファイルのパス名を使って自動的にページが構築されます。

📚 詳しくはAstroのファイルベースルーティングをご覧ください。

AstroでMarkdownを使い始めるもっとも簡単な方法は、プロジェクトに src/pages/index.md というトップページを作成することです。以下の基本的なテンプレートをプロジェクトにコピーし、レンダリングされたプロジェクトのトップページのHTMLを見てください。通常は、http://localhost:3000/ になります。

src/pages/index.md
---
title: Hello, World
---

# こんにちは!

これはあなたの最初のマークダウンページです。おそらくそれほどスタイルが適用されていないでしょう。
しかし、Markdownは **太字**_イタリック_ をサポートしています。

ページにレイアウトを追加することについてもっと学ぶには、次のセクションの **Markdownのレイアウト** をお読みください。

Markdownページには layout という特別なfront-matterプロパティがあり、Astroのレイアウトコンポーネントへの相対パスを定義しています。このコンポーネントはあなたのMarkdownコンテンツを囲み、ページシェルとその他の含まれるページテンプレート要素を提供します。

---
layout: ../layouts/BaseLayout.astro
---

Markdownページの典型的なレイアウトは以下を含みます。

  1. Markdownページのfront-matterデータにアクセスするためのcontentプロパティ。
  2. ページのMarkdownコンテンツがどこにレンダリングされるべきかを示す、デフォルトの<slot />
src/layouts/BaseLayout.astro
---
// 1. contentプロパティは、front-matterデータにアクセスできます。
const { content } = Astro.props;
---
<html>
  <head>
    <!-- ここにスタイルやmetaタグなど、他のhead要素を追加します。 -->
    <title>{content.title}</title>
  </head>
  <body>
    <!-- 共通のヘッダーやフッターのような、他のUIコンポーネントをここに追加します。-->
    <h1>{content.title} by {content.author}</h1>
    <!-- 2. レンダリングされたHTMLは、デフォルトのスロットに渡されます。 -->
    <slot />
    <p>作成日: {content.date}</p>
  </body>
</html>

contentプロパティには、astroプロパティが含まれ、完全なMarkdownのsourceheadersオブジェクトなど、ページに関する追加のメタデータを保持しています。

ブログ記事のcontentオブジェクトの例としては、以下のようなものがあります。

{
  /** ブログ投稿のfront-matter
  "title": "Astro 0.18 リリース",
  "date": "2021年7月27日(火)",
  "author": "Matthew Phillips",
  "description": "Astro 0.18 はAstroローンチ以来最大のリリースです。",
  "draft": false,
  "keywords": ["astro", "release", "announcement"]
  **/
  "astro": {
    "headers": [
      {
        "depth": 1,
        "text": "Astro 0.18 リリース",
        "slug": "astro-018-release"
      },
      {
        "depth": 2,
        "text": "レスポンシブパーシャルハイドレーション",
        "slug": "responsive-partial-hydration"
      }
      /* ... */
    ],
    "source": "# Astro 0.18 リリース\n1ヶ月ちょっと前に、最初のパブリック・ベータ版 [...]"
  },
  "url": ""
}

プロパティとしてのfront-matter

Section titled プロパティとしてのfront-matter

Astroのどのコンポーネント(レイアウトだけではありません!)も、Markdownのfront-matterで定義された値をプロパティとして受け取れます。YAML front-matterを使用していくつかのタイプのデータを指定し、さらに多くのメタ情報を各ブログ記事から取得し、Astroサイト全体で使用できます。

上記のレイアウトと同じように、任意の .astro ファイルでこれらの値にアクセスします。

AstroはMarkdownファイルのすべての見出しに、github-sluggerを使って自動生成されたidを追加します。しかし、カスタムIDが指定された場合、それは上書きされません。

これらのidは他のすべてのプラグインが実行された後に追加されるので、rehype-tocのようにidを必要とするプラグインを使う場合は、独自のスラッグ生成プラグイン(rehype-slugなど)を追加する必要があります。

draft: trueはオプションのfront-matterの値で、個々の.mdページや投稿を「未公開」としてマークできます。デフォルトでは、このページはサイトの構築から除外されます。

draftプロパティを持たないMarkdownページや、draft: falseを持つページは影響を受けず、最終的なビルドに含まれます。

src/pages/post/blog-post.md
---
layout: ../../layouts/BaseLayout.astro
title: 私のブログ記事
draft: true
---

これは、作成中のブログ記事です。

この記事にはページは作成されません。

この記事をビルドして公開するには

- front-matterを`draft: false`に更新するか、または
- `draft` プロパティを完全に削除してください。

投稿アーカイブや最新投稿リストに、下書き投稿のデータ(タイトル、リンク、説明文など)が含まれないようにするには、Astro.glob()関数で下書き投稿を除外するフィルターを設定してください

⚙️ 下書きページのビルドを有効にするには

astro.config.mjsmarkdowndrafts: true を追加します。

astro.config.mjs
export default defineConfig({
  markdown: {
    drafts: true,
  },
});

標準的なMarkdownの構文に加え、AstroはMarkdownを拡張し、コンテンツをより表現豊かにします。以下は、Astroにのみ存在するMarkdownの機能です。

Markdownで変数を使用する

Section titled Markdownで変数を使用する

front-matterの変数は frontmatter オブジェクトのプロパティとして、Markdownで直接使用できます。

---
author: レオン
age: 42
---

# 作者について

{frontmatter.author}は{frontmatter.age}歳で、カナダのトロントに住んでいます.

Markdownでコンポーネントを使用する

Section titled Markdownでコンポーネントを使用する

setup を使用してMarkdownファイルにコンポーネントをインポートし、Markdownコンテンツと一緒に使用できます。また、インポートされたコンポーネントはfrontmatterオブジェクトを利用できます。

---
layout: ../layouts/BaseLayout.astro
setup: |
  import Author from '../../components/Author.astro'
  import Biography from '../components/Biography.jsx'
author: レオン
---

<Author name={frontmatter.author}/>
<Biography client:visible>
  {frontmatter.author}はカナダ、トロントに住み、写真を趣味にしている。
</Biography>

MarkdownファイルをAstroファイルに直接インポートできます! importで特定の1ページをインポートすることも、Astro.glob()で複数のページをインポートすることもできます。

---
// Markdownをインポートします。動的な import() もサポートされています!
import * as greatPost from '../pages/post/great-post.md';

// また、Astro.globを使うと複数のファイルをインポートできます。
const posts = await Astro.glob('../pages/post/*.md');
---

素晴らしい記事: <a href={greatPost.url}>{greatPost.frontmatter.title}</a>

<ul>
  {posts.map(post => <li>{post.frontmatter.title}</li>)}
</ul>

TypeScriptのジェネリックを使用して、オプションで frontmatter 変数に型を指定できます。

---
interface Frontmatter {
  title: string;
  description?: string;
}
const posts = await Astro.glob<Frontmatter>('../pages/post/*.md');
---

<ul>
  {posts.map(post => <li>{post.title}</li>)}
  <!-- post.title は `string`になります! -->
</ul>

エクスポートされるプロパティ

Section titled エクスポートされるプロパティ

各Markdownファイルでは、以下のプロパティをエクスポートします。

ファイルのYAML front-matterで指定された任意のデータ。

ファイルの絶対パス (例: /home/user/projects/.../file.md)。

ページの場合、ページのURL (例: /en/guides/markdown-content)。

Markdownファイルのヘッダーを返す非同期関数。レスポンス型:{ depth: number; slug: string; text: string }[]

Markdownファイルの生のコンテンツ(front-matterブロックを除く)を文字列として返す関数です。たとえば、「読了時間」を計算する際に便利です。この例では人気のあるreading-timeパッケージを使用しています。

---
import readingTime from 'reading-time';
const posts = await Astro.glob('./posts/**/*.md');
---

{posts.map((post) => (
  <Fragment>
    <h2>{post.frontmatter.title}</h2>
    <p>{readingTime(post.rawContent()).text}</p>
  </Fragment>
))}

非同期関数で、生のコンテンツを有効なAstro構文にパースして返します。注意: これは {jsx expressions}, <Components /> やレイアウトはパースしません! ## 見出し- リストのような標準的なMarkdownブロックのみがHTMLにパースされます。これは、たとえば、ブログ記事の要約ブロックをレンダリングする場合に便利です。Astroの構文は有効なHTMLなので、node-html-parserのような人気のあるライブラリを使って、次のように最初の段落をクエリできます。

---
import { parse } from 'node-html-parser';
const posts = await Astro.glob('./posts/**/*.md');
---

{posts.map(async (post) => {
  const firstParagraph = parse(await post.compiledContent())
    .querySelector('p:first-of-type');
  return (
    <Fragment>
      <h2>{post.frontmatter.title}</h2>
      {firstParagraph ? <p>{firstParagraph.innerText}</p> : null}
    </Fragment>
  );
})}

Markdownファイルの内容をレンダリングするコンポーネントです。以下はその例です。

---
import {Content as PromoBanner} from '../components/promoBanner.md';
---

<h2>今日のおすすめ</h2>
<PromoBanner />

コンポーネントスクリプトで組み込みのAstro Markdownコンポーネント (EN)をインポートし、<Markdown></Markdown>タグの間に好きなMarkdownを記述できます。

---
import { Markdown } from 'astro/components';
import Layout from '../layouts/Layout.astro';

const expressions = 'Lorem ipsum';
---
<Layout>
  <Markdown>
    # Hello world!

    `.md` ファイルでサポートされているものは**すべて**、ここでもサポートされています!

    実行時のオーバーヘッドはゼロです。

    さらに、Astroは以下をサポートしています。
    - Astro {}
    - 自動インデント正規化
    - コードブロック内の式の自動エスケープ

    ```js
      // このコンテンツは変換されません
      const object = { someOtherValue };
    ```

    - `.Astro`ファイルのような豊富なコンポーネントサポート!
    - 再帰的なMarkdownのサポート (コンポーネントの子もMarkdownとして処理されます)
  </Markdown>
</Layout>

もし、リモートソースにMarkdownがある場合、content属性を通して、Markdownコンポーネントに直接渡すことができます。

---
import { Markdown } from 'astro/components';

const content = await fetch('https://raw.githubusercontent.com/withastro/docs/main/README.md').then(res => res.text());
---
<Layout>
  <Markdown content={content} />
</Layout>

<Markdown /> コンポーネントはネストできます。

---
import { Markdown } from 'astro/components';

const content = await fetch('https://raw.githubusercontent.com/withastro/docs/main/README.md').then(res => res.text());
---

<Layout>
  <Markdown>
    ## Markdown の例

    ここでは、__Markdown__のコードをいくつか紹介します。また、リモートコンテンツを動的にレンダリングできます。

    <Markdown content={content} />
  </Markdown>
</Layout>

astro.config.mjsを変更すると、Markdownのパースをカスタマイズできます。完全なリファレンスはこちらです

AstroはMarkdownのためにサードパーティのremarkrehypeプラグインをサポートしています。プラグインはastro.config.mjsで指定できます。

AstroでMarkdownプラグインを追加する方法

Section titled AstroでMarkdownプラグインを追加する方法
  1. npmパッケージの依存関係をプロジェクトにインストールします。

  2. remarkPlugins または rehypePluginsmarkdown オプションの中で更新します。

    astro.config.mjs
    export default {
      markdown: {
        remarkPlugins: [
          // プロジェクトで有効にしたいRemarkプラグインを追加します。
          // プラグインのオプションが必要であれば、配列を使用し、2番目の要素としてオプションを設定できます。
          // ['remark-autolink-headings', { behavior: 'prepend'}],
        ],
        rehypePlugins: [
          // プロジェクトで有効にしたいRehypeのプラグインを追加します。
          // プラグインのオプションが必要であれば、配列を使用して、2番目の要素としてオプションを設定できます。
          // 'rehype-slug',
          // ['rehype-autolink-headings', { behavior: 'prepend'}],
        ],
      },
    };

    プラグインをインポートするだけでなく、プラグイン名を指定することもできます。

    astro.config.mjs
    import autolinkHeadings from 'remark-autolink-headings';
    
    export default {
      markdown: {
        remarkPlugins: [[autolinkHeadings, { behavior: 'prepend' }]],
      },
    };

シンタックスハイライト

Section titled シンタックスハイライト

Astroには、ShikiPrismが組み込みでサポートされています。これにより、次のようなシンタックスハイライトを即座に適用できます。

Shikiはデフォルトで有効になっており、github-darkというテーマであらかじめ設定されています。コンパイルされた出力は、余計なCSSクラス、スタイルシート、クライアントサイドJSを含まないインラインstyleに限定されます。

Prismを使用する場合は、PrismのCSSクラスが代わりに適用されます。なお、シンタックスハイライトを表示させるためには、独自のCSSスタイルシートを用意する必要があります! 詳しくはPrismの設定を参照してください。

シンタックスハイライトの選択

Section titled シンタックスハイライトの選択

Shikiはデフォルトのシンタックスハイライトツールです。もし、'prism'に切り替えたり、シンタックスハイライトを完全に無効にしたい場合は、markdown設定オブジェクトを使用します。

astro.config.mjs
export default {
  markdown: {
    // ハイライトを無効にするには、'shiki' (デフォルト)、'prism' または false を指定します。
    syntaxHighlight: 'prism',
  },
};

Shikiを使用する場合、すべてのオプションは shikiConfig オブジェクトで、以下のように設定します。

astro.config.mjs
export default {
  markdown: {
    shikiConfig: {
      // Shikiの組み込みテーマから選択する(もしくは独自のテーマを追加する)
      // https://github.com/shikijs/shiki/blob/main/docs/themes.md
      theme: 'dracula',
      // カスタム言語の追加
      // 注:Shikiには、.Astroを含む無数の言語が内蔵されています
      // https://github.com/shikijs/shiki/blob/main/docs/languages.md
      langs: [],
      // 水平スクロールを防ぐために単語の折り返しを有効にする
      wrap: true,
    },
  },
};

また、カスタムテーマのロード、ライト/ダークモードのトグル、CSS変数によるスタイリングについては、テーマのドキュメントを読むことをお勧めします。

Prismを使用する場合、シンタックスハイライトのために、プロジェクトにスタイルシートを追加する必要があります。もし、あなたが始めたばかりで、ShikiよりもPrismを使いたいのであれば、以下をお勧めします。

  1. @astrojs/markdown-remark の設定でsyntaxHighlight: 'prism'を指定します。

  2. Prismテーマの中から、あらかじめ用意されているスタイルシートを選択する。

  3. このスタイルシートを、プロジェクトの public/ ディレクトリに追加する。

  4. このスタイルシートを<head>の中の<link>タグで読み込む。

オプションや使い方については、Prismがサポートする言語一覧も参照してください。