ゲストOS(Vagrant)とホストOS(Windows)間でscpによりデータをやり取りするコマンドメモ
はじめに
違うサーバどうしでファイルをやり取りするのはscp
コマンドが一般的である。
ここでは、scpの基本コマンドの解説に加え、Vagrant仮想環境とローカルのファイルのやり取りを実例としてメモする。
一般的な方法と、
> scp -r -P 2222 -i 【秘密鍵の場所(ローカル)】 vagrant@127.0.0.1:/home/vagrant/workspace/tmp/ ./
簡単な方法を解説する。
scp -r vagrant:/home/vagrant/workspace/tmp/ ./
実施した環境
- Windows環境の上に、VirtualBox + Vagrant で構築したLinux環境を利用。
- OSはubuntuを利用。
Vagrant環境からローカルにファイルをコピーする(基本コマンド編)
Vagrant環境の/home/vagrant/workspace/tmp/
フォルダから、ローカルのカレントフォルダにファイルをコピーするコマンドは以下となる。
PowerShellで実行する。
> scp -r -P 2222 -i 【秘密鍵の場所(ローカル)】 vagrant@127.0.0.1:/home/vagrant/workspace/tmp/ ./
コマンドの解説
基本コマンドは以下。
$ scp 【コピー元】 【コピー先】
重要なオプションとしては、
がある。
また、コピー元となるVagrant環境のフォルダ指定は、
【ユーザ名】@【IPアドレス】:【パス】
とする。
ここで、
を調べる必要がある。調べるコマンドは以下。
> vagrant ssh-config Host default HostName 127.0.0.1 User vagrant Port 2222 (省略)
もっと簡単なコマンドでscpする方法
C:\Users\[User名]\.ssh\config
にSSHの情報を記載しておくことができる。
このファイルで、「名前」と「設定値」をセットで管理しておけば、「名前」を使ってscp
が可能である。
.ssh\config
の設定内容
Vagrantの場合は、> vagrant ssh-config
で取得してきた情報をそのまま記載すればよい。
外部のサーバでも同様に記載が可能である。
C:\Users\[User名]\.ssh\config
Host aws HostName xx.xxx.xxx.xxx User ec2-user IdentityFile C:\Users\xxxxxx\xxxxx.pem Host vagrant HostName 127.0.0.1 User vagrant Port 2222 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentityFile C:/Users/User/vagrant/ubuntu64_18/.vagrant/machines/default/virtualbox/private_key IdentitiesOnly yes LogLevel FATAL
私の場合はvagrantとAWS-EC2の設定が入っている。
tomiko0404.hatenablog.com
こちらの記事で、VSCode用に設定していた。
scpコマンド
上記.ssh\config
でvagrant
という名前を付けている設定値を使いたい。
コマンドは以下のようになる。
scp -r vagrant:/home/vagrant/workspace/tmp/ ./
おわりに
scpコマンドを使ってサーバ間でデータをやりとりすることができた。
Ubuntu + Vagrant + VirtualBox環境でディスク容量を拡張した
はじめに
VirtualBoxで突然以下のようなエラーが発生。
npm ERR! code ENOSPC npm ERR! syscall write npm ERR! errno -28 npm ERR! nospc ENOSPC: no space left on device, write npm ERR! nospc There appears to be insufficient space on your system to finish. npm ERR! nospc Clear up some disk space and try again.
不要ファイルを削除してお茶を濁してみたが、いよいよ壊れ始めたので、仮想環境のディスク容量拡張に挑戦した。
Qiitaを探すとCentOSのやり方がヒットするのだが、Ubuntuの場合やり方が若干違うようだ。
最後は自己流になってしまうなど、かなり苦労した。
前提
- Windows環境の上に、VirtualBox + Vagrant で構築したLinux環境を利用。
- React.jsのGatsbyモジュールを使ったアプリを開発していた時に起こった。
- OSはubuntuを利用。
Distributor ID: Ubuntu Description: Ubuntu 18.04.4 LTS Release: 18.04 Codename: bionic
エラー原因の確認:ディスク容量不足
このエラー発生時は、ディスク容量が足りない可能性が高い。
参考にした記事では、innode
が足りなかったとのこと。
参考: No space left on device とエラーが出るときの対処法 - Qiita 自分の環境でたたいてみると、ディスク容量が足りないことが分かった。
$ df -hP Filesystem Size Used Avail Use% Mounted on udev 1.5G 0 1.5G 0% /dev tmpfs 299M 640K 299M 1% /run /dev/sda1 9.7G 9.7G 0 100% / tmpfs 1.5G 0 1.5G 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 1.5G 0 1.5G 0% /sys/fs/cgroup vagrant 238G 199G 39G 84% /vagrant home_vagrant_workspace 238G 199G 39G 84% /home/vagrant/workspace tmpfs 299M 0 299M 0% /run/user/1000
/dev/sda1
というのは、10GBであることからおそらくVirtualBoxのメインストレージ。
ディスク拡張のやりかた
ディスク拡張プラグインのインストール
ホストOS(Windows)上で、vagrant-disksize
プラグインをインストールする。
> vagrant plugin install vagrant-disksize
Vagrantfile追記
Vagrantfileに以下を追記する。
Vagrant.configure("2") do |config| config.vm.box = "ubuntu/bionic64"0 config.disksize.size = "20GB" # この行を追記 end
VagrantFile全体は以下のようになる。
Vagrant.configure("2") do |config| config.vm.box = "ubuntu/bionic64" config.disksize.size = "20GB" config.vm.network "forwarded_port", guest: 8000, host: 8000 config.vm.network "forwarded_port", guest: 3000, host: 3000 config.vm.synced_folder "./workspace", "/home/vagrant/workspace" config.vm.provider :virtualbox do |vb| vb.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/home/vagrant/workspace","1"] end config.vm.provider "virtualbox" do |vb| vb.memory = "2048" end end
Vagrantを再起動する
Vagrantを再起動する。
> vagrant halt > vagrant up
再起動時、以下のようなログが出ていれば成功。
==> default: Resized disk: old 10240 MB, req 20480 MB, new 20480 MB
ディスク容量の確認
VirtualBoxマネージャーの表示は変わっている。
パーティションを管理するfdisk
というコマンドを使うと、
$ sudo fdisk -l Disk /dev/sda: 20 GiB, 21474836480 bytes, 41943040 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x20027de7 Device Boot Start End Sectors Size Id Type /dev/sda1 * 2048 20971486 20969439 10G 83 Linux
ディスク容量はDisk /dev/sda: 20 GiB
と増えているが、パーティションの領域は
Device Boot Start End Sectors Size Id Type /dev/sda1 * 2048 20971486 20969439 10G 83 Linux
であり、増えていないことがわかる。
df
コマンドで確認しても増えていなかった。
$ df -hP Filesystem Size Used Avail Use% Mounted on udev 984M 0 984M 0% /dev tmpfs 200M 5.6M 194M 3% /run /dev/sda1 9.7G 9.7G 0 100% / ★まだ変わっていない tmpfs 997M 0 997M 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 997M 0 997M 0% /sys/fs/cgroup vagrant 238G 200G 38G 85% /vagrant home_vagrant_workspace 238G 200G 38G 85% /home/vagrant/workspace tmpfs 200M 0 200M 0% /run/user/1000
パーティションの領域拡張
parted
というコマンドを使う。fdisk
と同じくパーティションを追加したり削除したりするコマンド。
$ sudo parted
と入力し、
print free
と入力すると現在の状況を確認できる。
(parted) print free Model: VBOX HARDDISK (scsi) Disk /dev/sda: 21.5GB Sector size (logical/physical): 512B/512B Partition Table: msdos Disk Flags: Number Start End Size Type File system Flags 32.3kB 1049kB 1016kB Free Space 1 1049kB 10.7GB 10.7GB primary ext4 boot 10.7GB 21.5GB 10.7GB Free Space
Number1のパーテーション(おそらく、/dev/sda1
に対応する)の後ろがFree Space
となっているため、1番を後ろに拡張できそう。
そこで、
resizepart 1
と入力し、
Warning: Partition /dev/sda1 is being used. Are you sure you want to continue? Yes/No? y
「使っているけど続行する?」の問いにはy
を回答する。
次に容量を聞かれるので、21.5GB
と入力する。
End? [10.7GB]? 21.5GB
ここまで行ったらparted
を抜けてよい。
(parted) ^C
Vagrant再起動、容量の確認
Vagrantを再起動して、容量を確認すると
$ df -h Filesystem Size Used Avail Use% Mounted on udev 984M 0 984M 0% /dev tmpfs 200M 620K 199M 1% /run /dev/sda1 20G 9.7G 9.7G 50% / ★できた!! tmpfs 997M 0 997M 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 997M 0 997M 0% /sys/fs/cgroup tmpfs 200M 0 200M 0% /run/user/1000
容量が20GBになっていることが確認できた。
ファイルシステムの拡張が必要な場合がある?
参考文献では、resize2fs
コマンドを利用してファイルシステムを拡張していた。
sudo resize2fs /dev/sda1
今回は不要だったが、念のためメモしておく。
参考資料
参考:試してみてダメだった方法
最初は、以下ページを参考に、
という手順を実行しようと試みた。
VagrantのCentOSのディスク容量を増やす手順 - Qiita
物理ボリューム、ボリュームグループ、論理ボリュームの概念は以下を参照。
ただ、そもそも私の環境にはボリュームグループというものが存在せず、新規作成した物理ボリュームをマウントする方法がわからなかった。
以下ページによると、UbuntuではCentOSと違い、LVMが利用されていないらしい。
技術メモメモ: Ubuntu 20.04でシステム領域のディスク容量の拡張を行う
おわりに
Ubuntu+Vagrant+VirtualBoxの環境で、ディスク容量を拡張することができた。 Linuxの勉強をまったくできておらず、パーティションの概念も初めて知った。勉強不足がつらい。
VirtualBoxの容量不足エラーが出たときにやったことメモ
はじめに
VirtualBoxで突然以下のようなエラーが発生。
npm ERR! code ENOSPC npm ERR! syscall write npm ERR! errno -28 npm ERR! nospc ENOSPC: no space left on device, write npm ERR! nospc There appears to be insufficient space on your system to finish. npm ERR! nospc Clear up some disk space and try again.
容量が足りないエラーのようで、不要ファイルを削除することで解消した。 実施したことをメモする。
追記
本記事を書いた数時間後に再度同じエラーが発生、どうしようもなくなったため容量拡張を実施した。 あくまで本記事は暫定対応と考えたほうがよい。
前提
- Windows環境の上に、VirtualBox + Vagrant で構築したLinux環境を利用。
- React.jsのGatsbyモジュールを使ったアプリを開発していた時に起こった。
- OSはubuntuを利用。
Distributor ID: Ubuntu Description: Ubuntu 18.04.4 LTS Release: 18.04 Codename: bionic
原因の確認→ディスク容量不足
このエラー発生時は、ディスク容量が足りない可能性が高い。
参考にした記事では、innode
が足りなかったとのこと。
参考: No space left on device とエラーが出るときの対処法 - Qiita 自分の環境でたたいてみると、ディスク容量が足りないことが分かった。
$ df -hP Filesystem Size Used Avail Use% Mounted on udev 1.5G 0 1.5G 0% /dev tmpfs 299M 640K 299M 1% /run /dev/sda1 9.7G 9.7G 0 100% / tmpfs 1.5G 0 1.5G 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 1.5G 0 1.5G 0% /sys/fs/cgroup vagrant 238G 199G 39G 84% /vagrant home_vagrant_workspace 238G 199G 39G 84% /home/vagrant/workspace tmpfs 299M 0 299M 0% /run/user/1000
/dev/sda1
というのは、10GBであることからおそらくVirtualBoxのメインストレージ。
ディスク容量をひっ迫しているフォルダの確認
ルートディレクトリ配下のフォルダごとのデータ量を確認する。
ルートフォルダに移動して中身の確認。
$ cd / $ sudo du -sh ./*/ 8.1G ./home/ ...省略...
ホームディレクトリが圧倒的に大きいことが分かった。
ホームディレクトリの中のvagrant
(ユーザ名)ディレクトリに移動して、隠しファイル以外のデータ量を確認する。
$ cd home/vagrant $ sudo du -sh ./*/ 161M ./aws/ 753M ./gatsby/ 4.0K ./tmp/ 2.5G ./workspace/
これだけでは8.1GBに達する理由がわからないため、隠しフォルダのデータ量も確認。
$ du -scmh ./.[^.]* | sort -rh 4.7G total 1.3G ./.nvm 1.3G ./.cache 1.1G ./.npm 939M ./.vscode-server 116M ./.config 5.2M ./.yarn ...省略...
参考: duでカレントディレクトリ以下の使用容量をパッと見たいとき用のコマンド 隠しファイルも - Qiita
1.3G ./.nvm 1.3G ./.cache 1.1G ./.npm
このあたりの容量が大きそうだが、nvm
とnpm
は消したらまずい気がしたのでcache
をターゲットにする。
キャッシュフォルダの中身確認
./.cache
フォルダの中身を確認する。
$ cd ./.cache $ sudo du -sh ./*/ 109M ./typescript/ 1.2G ./yarn/
どうやら./yarn
フォルダが容量を食っている模様。
yarnのキャッシュを消してよいのか調査
home/.cache/yarn
フォルダを消して問題が起きないか自信が無い(さすがにキャッシュだから大丈夫だとは思うが)ので少し調べたところ、以下の記事で気軽にキャッシュをクリアしていたので、問題ないと信じることにした。
どのプロジェクトで使われているフォルダなのか
作業していたGatsby.jsのプロジェクトフォルダに移動して、yarn
のキャッシュ保管フォルダを確認すると、まさに問題になっていたフォルダだった。
$ cd gatsby-starter-blog/
$ yarn cache dir
/home/vagrant/.cache/yarn/v6
参考: yarnのキャッシュを理解してnode_modulesを正しく更新する - さかなソフトブログ
このまま
$ yarn cache clean
を実施したところ、データ量が1GBほど削減された。
VartualBox再起動
再起動すると、Linuxの操作が再びできるようになった。
vagrant halt vagrant up
おわりに
VirtualBoxの容量不足エラーを、キャッシュを削除することで回避できた。
ただし、本来はVirtualBoxへの割り当て容量を増やすほうが良いと思う。
また、本当に容量が足りなくなるとvagrant up
すらできなくなるので、詰む可能性あり。
私はwindowsと共有しているフォルダから適当なファイルを手動で削除することで、起動できた。
【Gatsby】マークダウンファイルから記事ページを生成する
はじめに
前回、マークダウンのファイル情報をデータレイヤに取り込み、ブログ上に表示することができた。
今回はブログ記事ページを動的に作成する。
方法としては、
- テンプレートコンポーネントを作成する方法(
{mdx.slug.js}
)(Gatsby公式チュートリアルはこちらの方法) createPages
APIを自作する(gatsby-starter-blogスターターキットはこちらの方法)
がありそう。
今回はテンプレートコンポーネントを作成する方法を利用する。
本記事のゴール
- ブログ記事ページのテンプレートを作成する。
- 開発環境に保存したマークダウン形式の記事の中身と、テンプレートから、記事ごとのページを自動で生成する。
前回の記事
前提
- gatsby-starter-blogスターターキットを利用したgatsbyブログを立ち上げ済
- Vagrantを開発環境として利用
- Gatsby Cloudで公開済(今回は利用しないので、なくても可)
gatsby-source-filesystem
の設定が完了し、/content/blog
フォルダ内のファイルがデータレイヤーに読み込まれる状態になっているgatsby-plugin-mdx
の設定が完了し、ファイルをMDXファイルとして利用できる状態になっている
参考にしたサイト
テンプレートとマークダウンから動的にページを作成する流れ
マークダウンファイルを作成する
前回の記事を参考に、MDXファイルを作成する。
content/blog/mdx-content.mdx
--- title: "mdxで記事を書いてみる" date: "2022-02-26" --- ## はじめに 記事の内容
テンプレートコンポーネントを作成する
テンプレートコンポーネントを作成する。
作成する場所
pages
配下の、URLに対応する場所に作成する。
例えば、ブログ記事ページのURLを~~~~.com/blog/記事名
にしたい場合、テンプレートコンポーネントはsrc/pages/blog/
配下に作成する。
コンポーネントのファイル名
{mdx.slug.js}
とする。
構文としては
{{データレイヤのノード名}.{URLに利用したいフィールド名}.js}
となる。
今回は、mdx
ノードを利用し、URLにはslug
フィールドを利用するので{mdx.slug.js}
となる。
別のフィールドを使いたい場合
{URLに利用したいフィールド名}が深い場所にある場合は、__
(アンダーバー2つ)でネストして行ける。(例:{mdx.frontmatter__slug}.js
)
slug
フィールドとは
gatsby-plugin-mdx
を使ってMDXノードに変換すると、自動的にslug
フィールドが生成される。これをURLに利用するとよい。
自分でslug
を設定する
MDXファイル内に自分でslug
フィールドを作成し、ファイル名を
{mdx.frontmatter__slug}.js
とすると、ファイル名とは別に好きなURLにすることができる。
--- title: "mdxで記事を書いてみる2" slug: "mdx-content-my-slug" date: "2022-03-07" --- ## はじめに
テンプレートの中身を記載する
テンプレートコンポーネント内で、特定の(URLに対応する)マークダウンから情報を読み込むソースコードを記述する。
クエリの作成
クエリはテンプレートコンポーネントに以下のように記載する。
export const query = graphql` query ($id: String) { allMdx(filter: {id: {eq: $id}}) { nodes { body slug frontmatter { date(formatString: "MMMM D, YYYY") title } } } } `
query ($id: String)
は、id
変数を引数として利用するという宣言。
allMdx(filter: {id: {eq: $id}}) {
は、id
フィールドが$id
と等しいもののみ抽出するという指定。
参考:変数を引数としてクエリに設定する方法のメモ tomiko0404.hatenablog.com
変数に入れる値の取得方法
クエリえMDXノードの情報を取得する際に「どのページか」を判定する必要がある。
Gatsbyでは、ページテンプレートコンポーネントを使って作られたページにおいて、props.pageContext
の中に、MDXノードを一意に特定するid
の値が初めから入っているためこれを利用する。
props.pageContext
にはid
と、URLに利用したいフィールド名(ここではslug
)が自動で渡される。
以下ページによると、ページ生成時にprops.pageContext
として渡された値はGraphQLの引数としてそのまま利用できる。
// Add optional context data to be inserted
// as props into the page component.
//
// The context data can also be used as
// arguments to the page GraphQL query.
//
// The page "path" is always available as a GraphQL
// argument.
Gatsby Node APIs | Gatsby
そのため、前節のクエリを記載するだけで、ページに特有のid
をフィルタ条件として利用してくれる。
※pageContext
については勉強不足のため割愛する。
テンプレートコンポーネントのソースコード
全体としてソースコードは以下のようになる。
src/pages/blog/{mdx.slug}.js
import { graphql } from 'gatsby'; import { MDXRenderer } from 'gatsby-plugin-mdx'; import * as React from 'react'; import MyLayout from '../../components/myLayout'; const BlogPost = (props) => { console.log(props); const {data} = props; return ( <MyLayout pageTitle="Super Cool Blog Posts"> <MDXRenderer> {data.allMdx.nodes[0].body} </MDXRenderer> </MyLayout> ) } export const query = graphql` query ($id: String) { allMdx(filter: {id: {eq: $id}}) { nodes { body slug id frontmatter { date(formatString: "MMMM D, YYYY") title } } } } ` export default BlogPost
今回は勉強の流れでallMdx
フィールドを利用し、id
を指定することで要素数1の配列を取得・表示している。
1件のMDXノード情報だけ取れればよいので、mdx
フィールドを利用するのがよさそう。
おわりに
テンプレートコンポーネントを作成して、ブログ記事ページを自動で生成することができた。
GraphQLの引数を変数で設定する方法のメモ
はじめに
GraphQLのリクエストに引数を入れる際、引数を固定値ではなく変数で設定するやり方をメモ。
GraphQLとは/基本的なリクエストの書き方
以下の記事を参照。 tomiko0404.hatenablog.com
引数なしのリクエスト
リクエスト(すべてのMDXノードのslug
(ファイル名)フィールドとid
フィールドを取得)
query { allMdx { nodes { slug id } } }
取得できる値
"data": { "allMdx": { "nodes": [ { "slug": "mdx-content", "id": "c31f21f8-8731-5bc1-b8fa-906aabd57930" }, { "slug": "mdx-content2", "id": "9ce566d8-ae20-5b54-9d23-424487f61361" } ] }
引数が固定値のリクエスト
リクエスト(MDXノードのうちid
が"9ce566d8-ae20-5b54-9d23-424487f61361"
と等しいノードのslug
(ファイル名)フィールドとid
フィールドを取得)
query { allMdx(filter: {id: {eq: "9ce566d8-ae20-5b54-9d23-424487f61361"}}) { nodes { slug id } } }
取得できる値
"data": { "allMdx": { "nodes": [ { "slug": "mdx-content2", "id": "9ce566d8-ae20-5b54-9d23-424487f61361" } ] } }
引数に変数を設定する場合のリクエスト
この方法はページクエリでしか利用できない。
useStaticQuery
では利用できないので注意する。
リクエスト(MDXノードのうちid
フィールドが変数id
と等しいノードのslug
(ファイル名)フィールドとid
フィールドを取得)
query ($id: String) { allMdx(filter: {id: {eq: $id}}) { nodes { slug id } } }
GraphiQL
GraphiQLを使ってリクエストを作成する際は、変数に入る値を仮設定することができる。左下のQUERY VARIABLESにJSON形式で設定する。
Gatsby.jsでの利用
MDX(マークダウン)ファイルの情報が入っているMDXノードの情報を取得するために、テンプレートとなるコンポーネント上でid
を指定するクエリが必要である。
Gatsbyでは、ページテンプレートコンポーネントを使って作られたページにおいて、props.pageContext
の中に、MDXノードを一意に特定するid
の値が初めから入っている。
id
を変数として引数に入れておくことで、props.pageContext.id
の値をフィルタ条件として利用できる。
{mdx.slug}.js
import { graphql } from 'gatsby'; import { MDXRenderer } from 'gatsby-plugin-mdx'; import * as React from 'react'; import MyLayout from '../../components/myLayout'; const BlogPost = (props) => { console.log(props.pageContext.id); const { data } = props; return ( <MyLayout pageTitle="Super Cool Blog Posts"> <MDXRenderer> {data.allMdx.nodes[0].body} </MDXRenderer> </MyLayout> ) } export const query = graphql` query ($id: String) { allMdx(filter: {id: {eq: $id}}) { nodes { body slug frontmatter { date(formatString: "MMMM D, YYYY") title } } } } ` export default BlogPost
※pageContext
については勉強不足のため割愛する。
おわりに
GraphQLで引数を利用し、props
等から取得した変数を設定する方法をメモした。
【Gatsby】マークダウンファイルの情報を取得してブログ上に表示する
はじめに
前回に引き続き、開発環境にマークダウン形式の記事を保存し、Gatsbyで取得する方法を確認する。
今回は取得したファイルの中身(マークダウン形式)を取得して、解析して最終的にHTMLに変換して表示する方法を確認する。
ここまで参考にしてきたGatsby公式チュートリアルと、gatsby-starter-blogスターターキットとでは、利用するマークダウンの変換方法が異なる。
参考にした方法 | ファイル形式 | 変換プラグイン |
---|---|---|
公式チュートリアル | .mdx |
gatsby-plugin-mdx |
gatsby-starter-blogスターター | .md |
gatsby-transformer-remark |
本記事ではmdx
形式を利用できるGatsby公式チュートリアルのやり方で実施する。
本記事のゴール
- 開発環境に保存したマークダウン形式の記事の中身を取得する。
- 開発環境からデータレイヤーに情報を送信する。
- データレイヤ内で、ノードの形式を「ファイルノード」から「MDXノード」に変換する。
- データレイヤーにGraphQLでアクセスし、情報を取得する。
- 取得したマークダウン記事をブログに表示する。
赤枠が本記事の実施範囲である。青枠が前回の実施範囲となる。
前回の記事
前提
- gatsby-starter-blogスターターキットを利用したgatsbyブログを立ち上げ済
- Vagrantを開発環境として利用
- Gatsby Cloudで公開済(今回は利用しないので、なくても可)
- 前回の記事で実施した通り、
gatsby-source-filesystem
の設定が完了し、/content/blog
フォルダ内のファイルがデータレイヤーに読み込まれる状態になっている
参考にしたサイト
MDXファイルとは
基本的にマークダウン形式のファイルだが、JSXの形式も書いて良いよ!という形式。
通常のマークダウンでは、マークダウンまたはHTMLしか書けないが、
console.log(""); // インデントするとJSとして動かないらしいので注意
や
<MyComponent title="あああ"></MyComponent>
のようなものを書くことができる。
詳細は以下サイトを参照。
ファイルノード・MDXノードとは
データレイヤーに取り込まれたデータの1つ1つは「ノード」と呼ばれる.。
- ノードの種類は、ソースプラグインごとに異なる。
- ノードの種類によって持つフィールドは異なる。
sorce-filesystem
プラグインを利用した場合、「ファイルノード」が作成される。
ファイルノードには、例えばファイル名を表すname
や、更新日時を表すmodifiedTime
等のフィールドがあり、以下のようにクエリで取得することができる。ただし、マークダウンで書かれたファイルの中身を上手く取得できるようなフィールドは無い。
query { allFile { nodes { id name } } }
そこで、「MDXノード」に変換する必要がある。
MDXノードは、body
(本文)や、frontmatter
に記載した内容、slug
(ファイル名)等を取得することができる。
query MyQuery { allMdx { nodes { frontmatter { title } slug body } } }
ファイルノードをMDXノードに変換する
MDXファイルの中身をつくる
MDXファイルに記事の内容を書く。
冒頭にはfrontmatter
を記載することができる。
frontmatterの下にはマークダウン+JSX(今回は単なるマークダウン)で記事を記載する。
frontmatterとは
frontmatterとは、英単語で言うと「本の奥付」。
まず Front Matter とはなんぞや、というと、元々は Jekyll という静的サイトジェネレータが広めたらしい、文書のメタデータを YAML で書く手法。 Markdown ファイルに限らず、そのファイルの冒頭で、以下のようにハイフン3つで囲んだ行の中が YAML 形式になっていて、その部分をメタデータ的に活用できるというモノ。
Remark プラグインを使って Markdown から Front Matter を抽出する - Neo's World
ということ。
マークダウンファイルの冒頭に以下のように記載する。
/content/blog/mdx-content.mdx
--- title: "mdxで記事を書いてみる" date: "2022-02-26" ---
記事を取得した際、node.frontmatter.title
やnode.frontmatter.date
として値を取得できる。
また、上記のdate
のように"xxxx-xx-xx"
という形式のデータを入れておくと、GraphiQLで要求電文を作成するとき、formatString
という様々な日付形式に変換できるフィルターをサジェストしてくれるので便利。
プラグインの利用設定
インストール
gatsby-plugin-mdx
をインストールする。
$ npm install gatsby-plugin-mdx @mdx-js/mdx@v1 @mdx-js/react@v1
コンフィグファイル設定
gatsby-config.js
のplugin
配列の中に、以下のように記載を追加する。
plugins: [ `gatsby-plugin-image`, { resolve: `gatsby-source-filesystem`, options: { path: `${__dirname}/content/blog`, name: `blog`, }, }, { resolve: `gatsby-source-filesystem`, options: { name: `images`, path: `${__dirname}/src/images`, }, }, // 省略 `gatsby-plugin-mdx`, // 追加 ],
MDXノードの値を取得する
blog-tutorial
コンポーネントで、MDXノードの値を取得する。
取得した値は、props.data
に格納されるため、コンポーネント上で表示する。
src/pages/blog-tutorial.js
import { graphql } from "gatsby"; import { MDXRenderer } from "gatsby-plugin-mdx"; import * as React from "react"; import MyLayout from "../components/myLayout"; const BlogTutorial = (props) => { const { data } = props; console.log(data); return ( <MyLayout pageTitle="ブログ一覧"> {data.allMdx.nodes.map((node) => ( <article> <h2>{node.frontmatter.title}</h2> <p>Posted: {node.frontmatter.date}</p> <MDXRenderer> {node.body} </MDXRenderer> </article> ))} </MyLayout> ); }; export const query = graphql` query { allMdx(sort: { fields: frontmatter___date }) { nodes { frontmatter { title date(formatString: "YYYY年MM月DD日") } id body parent { ... on File { modifiedTime } } } } } `;
各フィールドの説明
allMdx
で取得できるフィールドを説明する。
フィールド | 説明 |
---|---|
frontmatter.title |
MDXファイルのfrontmatterにtitle タグをつけて記載した内容を取得。 |
frontmatter.date |
MDXファイルのfrontmatterにtitle タグをつけて記載した内容を取得。(formatString: "") オプションをつけると日付形式を指定できる。リファレンス:Moment.js | Docs |
id |
各ノードを一意に識別するための文字列ID。<li> 要素を使うときのキーにも利用できる。 |
body |
ファイルの中身からfrontmatterを除いたもの。 |
parent |
MDXに変換する前のノードを示す。ここでは、「対応するファイルノード」を示す。 |
MDXを読み込む
ファイルから取得したbody
はそのままでは読み込めない。
<MDXRenderer>
コンポーネントとセットで利用する必要がある。
上記ソースコードの通り、
import { MDXRenderer } from "gatsby-plugin-mdx";
でインポートして、
<MDXRenderer> {node.body} </MDXRenderer>
で囲む。
結果表示
localhost:8000/blog-tutorial
ページに、MDX記事の内容を表示できた。
【参考】他に必要なプラグイン
公式チュートリアルで紹介されていた、記事作成に必要なプラグインは以下。
gatsby-remark-images
:マークダウン形式![alt](image url)
で画像を指定できる。gatsby-remark-prismjs
:シンタックスハイライトを利用できる。- 'gatsby-remark-autolink-headers':Reactのチュートリアルサイトのように、見出しの頭にリンクを追加できる。
他にも
gatsby-remark-prismjs-add-title
(コードブロックの頭にファイル名を表示する)等も利用できそう。
おわりに
マークダウンで作成した記事のデータを取り込み、GraphQLで取得してコンポーネントで表示することができた。
まだ、記事ごとのページは作成できていないので、次回以降確認する。
関連記事
つづき
【Gatsby】プロジェクト内に保管したファイルの情報をGraphQLで取得する
はじめに
Gatsbyでブログを書くためには、1ページずつHTMLを書くわけにもいかないので、記事を書いたデータベースからデータを取得してコンポーネントに組み込む必要がある。
開発環境にマークダウン形式の記事を保存し、Gatsbyで取得する方法を確認する。
マークダウンの解析は次回とし、今回はファイルの情報を取得するところまで実施する。
この記事のゴール
- 開発環境に保存したマークダウン形式の記事の「ファイル名」等のファイル情報を取得する。
- 開発環境からデータレイヤーに情報を送信する。
- データレイヤーにGraphQLでアクセスし、情報を取得する。
- 取得した情報をサイトに表示する。
以下の図の赤字部分が本記事の範囲である。
前回の記事
前提
- gatsby-starter-blogスターターキットを利用したgatsbyブログを立ち上げ済
- Vagrantを開発環境として利用
- Gatsby Cloudで公開済(今回は利用しないので、なくても可)
参考にするサイト
Part 4: Query for Data with GraphQL | Gatsby
- はじめに
- 今回の流れ
- マークダウンファイルの準備
- マークダウンファイルをGraphQLデータレイヤに読み込ませる
- コンポーネントにクエリを追加する
- ファイル名と更新日時を表示してみる
- おわりに
- 関連記事
今回の流れ
開発環境のcontent/blog
フォルダにマークダウン形式の記事を配置し、配置した記事をGraphQLデータレイヤに読み込ませる。
ブログのBlogTutorial
コンポーネントから、GraphQL APIを利用してデータレイヤにアクセスしてデータを取得する。
取得したデータをコンポーネントに表示してみる。
マークダウンファイルの準備
content/blog
フォルダ配下にブログ記事を用意する。
gatsby-starter-blogスターターキットでは、content/blog
フォルダとその配下のサンプル記事(.md
)形式が事前に用意されている。
content └── blog ├── ay-page │ └── index.md ├── hello-world │ ├── index.md │ └── salty_egg.jpg ├── my-second-post │ └── index.md └── new-beginnings └── index.md
マークダウンファイルをGraphQLデータレイヤに読み込ませる
プラグインの利用設定
フォルダのマークダウン記事をGraphQLデータレイヤから読み込むには、gatsby-source-filesystem
というプラグインが必要。
データレイヤーに関しては以下の記事を参照。
GatsbyデータレイヤーからgraphQLでデータを取得する - エンジニアを目指す日常ブログ
また、プラグインの使い方は以下の記事を参照。
Gatsbyブログでのプラグイン利用方法/画像の挿入方法 - エンジニアを目指す日常ブログ
gatsby-source-filesystem
プラグインがインストールされていない場合は、こちらの記事を参考にnpm install
する。
次に、gatsby-config.js
のplugin
の中に以下記載する。(スターターキットを使っているので、既に記載されていた。)
plugins: [ { resolve: `gatsby-source-filesystem`, options: { path: `${__dirname}/content/blog`, name: `blog`, }, }, ・・・ ],
オプションの意味は以下のようになる。
resolve
に、プラグイン名を記載- ビルド時、
path
で指定された${__dirname}/content/blog
内のファイルをすべてGraphQLデータレイヤーに追加する。 ${__dirname}
はNode.jsが保持している特殊な変数で、現在の場所の絶対パスを表す。name
は、特定のフォルダを指定してデータを取得するために、別名として使用する。
// nameオプションの使い方 // このクエリの{eq: "blog"}のところで使う query { allFile(filter: {sourceInstanceName: {eq: "blog"}}) { nodes { name } } }
ちなみに、gatsby-config.js
ファイルのplugin
の配列要素は、
String
型でプラグイン名だけ記載- オブジェクト型でオプションも含めて記載
の2通りの書き方がある。今回は「オブジェクト型」を利用していることになる。
データレイヤに読み込ませる
gatsby devlop
やgatsby build
を実行すると、gatsby-config.js
ファイルの設定に従ってデータレイヤにデータが読み込まれる。
コンポーネントにクエリを追加する
要求電文の準備
以下の通りgatsby develop
を実行して、http://localhost:8000/___graphql
にアクセスする。
$ sudo sysctl fs.inotify.max_user_watches=524288 $ gatsby develop --host=0.0.0.0
参考:
Vagrant上でGatsbyブログのひな型を作成した - エンジニアを目指す日常ブログ
gatsby developで出たエラーの対処法 - エンジニアを目指す日常ブログ
GatsbyデータレイヤーからgraphQLでデータを取得する - エンジニアを目指す日常ブログ
以下の要求電文を作り、GraphiQL上で応答を確認する。
query MyQuery { allFile { nodes { name sourceInstanceName } } }
/content/blog
配下のファイル以外も返ってきている。
{ "data": { "allFile": { "nodes": [ { "name": "index", "sourceInstanceName": "blog" }, // 省略 { "name": "salty_egg", "sourceInstanceName": "blog" }, { "name": "gatsby-icon", "sourceInstanceName": "images" }, { "name": "profile-pic", "sourceInstanceName": "images" }, { "name": "rPOmLGwai2w", "sourceInstanceName": "__PROGRAMMATIC__" } ] } }, "extensions": {} }
クエリ追加と結果確認
要求電文が準備できたので、blog-tutorialコンポーネントに追加する。
以下の記事に記載した通り、graphql
のクエリには以下の特徴があることに注意。
GatsbyデータレイヤーからgraphQLでデータを取得する - エンジニアを目指す日常ブログ
src/pages/blog-tutorial.js
import { graphql } from 'gatsby' import * as React from 'react' import MyLayout from '../components/myLayout' const BlogTutorial = (props) => { const {data} = props; console.log(data); return ( <MyLayout pageTitle="ブログ一覧"> </MyLayout> ) } export const query = graphql` query { allFile { nodes { name sourceInstanceName } } } ` export default BlogTutorial;
※先ほど準備した要求電文から名前(MyQuery
)は削除したほうが良い。
実行するとコンソールでdata
の中身を確認できる。
ファイル名と更新日時を表示してみる
クエリの書き換え
これまでのクエリだと、content/blog
フォルダの記事以外のファイルも取得してしまうので、sourceInstanceName
を使ったfilter
設定を入れる。
また、このあと表示する更新日時も取得するようにしておく。
export const query = graphql` query { allFile(filter: {sourceInstanceName: {eq: "blog"}}) { nodes { name sourceInstanceName modifiedTime } } } `
blog-tutorial
コンポーネントで表示
コンポーネントの中身は以下のように記載する。
const BlogTutorial = (props) => { const { data } = props; console.log(data); return ( <MyLayout pageTitle="ブログ一覧"> {data.allFile.nodes.map((node) => ( <> <p>ファイル名:{node.name}</p> <p>更新日時:{node.modifiedTime}</p> </> ))} </MyLayout> ); };
おわりに
ローカルのファイルをデータレイヤに読み込ませるために、gatsby-source-filesystem
プラグインの利用方法を覚えた。
また、データレイヤに読み込んだファイル情報をコンポーネントに表示した。
ファイルの情報はこのように取得できるが、マークダウンファイルの中身を上手く扱うには、データレイヤの中で「ファイルノード」を「MDXノード」や「マークダウンRemarkノード」の形式に変換する必要がある。(冒頭の図参照)
次回は「MDXノード」に変換してファイルの中身(ブログ記事)を表示する。