'static + Life

運良く生きています

ブログ名を変えました

旧ブログ名: 備忘録
新ブログ名: 'static + Life

それに伴ってブログの favicon も変更しました、生まれて初めて変えたかもしれない。
ちなみに、新ブログ名の元ネタは Rust の trait bounds に lifetime bounds も追加したものです *1

Kubernetes 上に code-server を構築

TL;DR

  • 自宅鯖の Kubernetes (シングルノード) 上に code-server をデプロイした
  • Ingress (nginx-ingress-controller) を使用して外部からもアクセスできるように
  • 外部アクセスでコンテナ <-> ホスト間のファイルの編集が可能になった

環境

  • Host OS: Gentoo Linux (5.15.26-gentoo)
  • Kubernetes Engine: RKE2 v1.22.7-rke2r2
  • Helm: v3.8.0
  • code-server: v4.2.0
  • nginx-ingress-controller は RKE2 にてすでにデプロイされているので, それを使用.
    • 別の k8s engine 使う場合はデプロイは必要になるかもしれません.

ディレクトリ構造

(一部省略してます)

. (/home/kash)
├── manifests
│   ├── code-server
│   │   ├── helm-charts
...

code-server では, ~/manifests 以下を編集できるようになっています. また, code-server/helm-charts については後述します.

内容

code-server デプロイまで

ここは code-server に載ってる Helm でのインストール方法*1 を少し変えたものとなります. また, home という namespace で今回はデプロイしています.

$ git clone https://github.com/coder/code-server
$ mv code-server/ci/helm-chart ./manifests
$ cd manifests

code-server の chart を一部変更するために以下の YAML ファイルを作成 (overwrite.yaml):

ingress:
  enabled: true
  ingressClassName: "nginx"
  hosts:
    - host: <ホスト名>
      paths:
        - /

resources:
  requests:
    cpu: 100m
    memory: 1000Mi

extraVolumeMounts:
  - name: manifests
    mountPath: /k8s/manifests
    existingClaim: code-server-manifests

上記の YAML ファイルでは, Ingress の有効化及びホストやパスの設定, リソースの requests の決定, 追加でマウントする Volume (~/manifests) を指定しています.

code-server の chart は実質完成したので, 次に, PersistentVolume, PersistentVolumeClaim のマニフェストを作成します.

pv.yaml:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: code-server
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
parameters:
  type: pd-standard
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: code-server
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: code-server
  claimRef:
    name: code-server
    namespace: home
  hostPath:
    path: "/k8s_mnt/code-server"
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: code-server-manifests
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: code-server
  claimRef:
    name: code-server-manifests
    namespace: home
  hostPath:
    path: "/k8s_mnt/code-server-manifests"

StorageClass では, 特に動的プロビジョンするわけではないので, provisionerkubernetes.io/no-provisioner を指定し, parameters.typeSSD というわけではないので pd-standard です. PersistentVolume では, デフォルトと Volume と追加の Volume が必要なので二つの PersistentVolume を作成しています. claimRefhome/code-serverhome/code-server-manifests という形で Claim できるようにしています. hostPath では, /k8s_mnt/ 上でマウントできるようにしています.

pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: code-server-manifests
  namespace: home
  labels:
    app.kubernetes.io/name: code-server-manifests
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: "code-server"

PersistentVolumeClaim において, home/code-server はデフォルトの chart で定義されるので, home/code-server-manifests のみの PersistentVolumeClaim の作成となります. ここは特に言うことはないです.

以上が完了したら,

$ kubectl apply -f pv.yaml -f pvc.yaml

で, PersistentVolume, PersistentVolumeClaim を作成します. そのあと,

$ helm upgrade --install code-server helm-chart -f overwrite.yaml

で, code-server をデプロイ. パスワードの表示のコマンドが出るので, 打って出力をメモします.

公開

$ kubectl describe ingress -n home code-server

にて, Backend の ClusterIP が降られていることが確認できたら公開できるので, 指定したホスト名を自宅鯖に向けることで公開が完了し, ホスト名で code-server にアクセスできます. ログインには, 先ほどのパスワードが必要なのでそれでログインできます.

自宅環境のお話

この記事は, TUT Advent Calendar 2021 の 1 日目の記事です.
知らぬ間に 12 月入ろうとしていて焦りました.

前置き

なんか作ろうかなと思ったけど, 研究がとにかく忙しかったため自宅環境のお話にします.
研究のお話は近々話したいですね.

自身の自宅環境

PC 環境

自身の PC 周りの環境は以下の環境があります:

デスクトップ環境

2020 年 10 月ぐらいに自作.
基本的にゲームやら 3D 処理など重い処理を動かすのに使っています. 最近は FF14 ばっかりですが…

スペック:

  • CPU: Ryzen 7 3700X
  • CPU クーラー: Noctua NH-U12A
  • メモリ: 16GB * 4 (Corsair VENGEANCE LPX Series/CMK32GX4M2A2666C16)
  • SSD:
  • GPU: ASUS GeForce RTX 3070 Noctua Edition*2
  • マザーボード: ASUS ROG STRIX B550-F GAMING (WI-FI)
  • 電源: Seasonic FOCUS+ 750W
  • ケース: Define 7 Clear Tempered Glass
Macbook Pro 13inch 2020 年モデル

2020 年 5 月購入.

16GB メモリ, 1TB メモリ, CPU を i7 にしてます. M1 Mac も魅力的だったけど x86_64 環境がまだまだ現役なので…
常に持ち歩いています.

Dynabook

大学入学時に購入したもの. Windows 消し飛ばして Ubuntu 18.04 ぶち込んだやつです. 現在は, Ubuntu 20.04 になっております.
こちらも常に持ち歩いていますが, 最近は使いどころが少なくなり出番も減り気味…TeX 環境があるのでレポート作成時には役立っております.

ThinkPad X240

2020 年 9 月ぐらいに知り合いから安く譲ってもらったもの.
Arch Linux をぶち込んでおりますが, あんまり使っておらず…pacman -Syuu したら多分ぶっ壊れそう.

サーバ環境

自宅にあるサーバは現在, HP Z240 Workstation に Gentoo Linux をぶち込んだものを使っています. パッケージをアップデートするときは emerge --sync しましょう.
本格的な稼働はまだ行っておりませんが, いつかしたいところですね.

多分いつかサーバは増える予定.

ネットワーク環境

自宅で以下の機器を使ったネットワークを構築しています. (ネットワーク図は時間の都合上作れてないです…申し訳ないです)

YAMAHA RTX830

2021 年 3 月末購入.

自宅ネットワークを構築したいという意気込みから購入. 今では自宅の中核を担っております. IPoE と PPPoE のネットワークを VLAN で分離していい感じです.

ちなみに, このネットワーク構成するためにプロバイダ変えたりと色々と苦労しました.

Allied Telesis AT-x210-24GT

2021 年 7 月ぐらいに購入.

ルータで分離した IPoE と PPPoE ネットワークを巡らせるために.
24 ポートもあるので中々迫力があります.

Aruba AP-225

2021 年 8 月ぐらいに購入.

Wi-Fi のアクセスポイントとして役立っております.

そのほか

キーボードに HHKB 使ったり, 自作キーボードを作ったり色々とやっています.
サーバにも NIC つけたりなども行っています.

最後に

なんか気付いたら 11 月終わっててこれ投稿する時間も遅くなってしまって出だしからちょっと怪しい雰囲気なのはすみません…
あと, 記事内容もなんかやばいことになっているしいきなりハードル上げた感が半端ない気がする.

脚注

*1:買った少し後ぐらいに 2k ぐらい安くなって 980 Pro も発売されたのでちょっとつらみ

*2:RTX2060 Super から買い替え, Noctua 好きなので…ちなみに 13 万ぐらいしました

はてなリモートインターンシップ 2021 に参加してきました!

久しぶりの長文なので文章が変かもしれませんが, そこはご了承ください><

背景

大学 3 年になってそろそろインターンに参加しないとまずいなあ (本当は 2 年のうちに参加したかったが某ウイルスのせいでおじゃんになった) という気持ちになっており, 某スプレッドシートTwitter の FF 内の様子を見て, どのインターンに参加しようか悩んでました.
去年, 長い知り合いである id:SlashNephy さんが参加していたはてなインターンを思い出し, そのブログ記事を見て参加してみたいと思い, 今回のはてなインターンに応募しました.

参加までの流れ

毎年, はてなインターンの応募ではホームページ上で仕掛けを施しており, それを解いた者が応募資格を得る感じです. 今回の方式は, ページ上で Developer Tools を開き, Console で JavaScript の関数を叩くというシンプルなものでしたが, 中々面白い仕組みだなあと思いました.

選考では, 応募時に書いた GitHub などから始まり, そこから面接, 合格か不合格かが決まる感じでした.

面接

面接では, まず人事の方から軽い説明や質問を (何分か忘れましたが) 受けて, そのあと担当者の方 (私の場合は id:cockscomb さん) と面接する感じでした. 自分はインターン初応募で面接すらやったことないのでかなり緊張しましたが, 担当者さんとは技術的な話で盛り上がり, 面接というよりもむしろ技術談話みたいな話となっていて緊張感は終盤ほぼなくなっていました.

Docker の話やらコンテナの話やらしたり, こういうときはこうすればよいよねと話したり, 本当に面接か?みたいな感じでしたが, それだけでも受けた価値があったと感じております.

その翌日には, 合格通知が来ておりましたが, 何故か Gmail のプロモーション欄に入っており, 通知が来なかったため 3 週間ぐらい気づいていませんでした…本当に申し訳ございません…

参加決定後

参加決定後は, 事前交流会や歓迎会などを経て, はてなの貸与 PC や Slack や Discord, Scrapbox といった業務に必要なツールを使って講義や開発を行いました. 1 週目に講義 + 開発パート, 2, 3 週目に実際に配属されたチームでの開発を行いました. チーム開発では, 自分がシステムプラットフォーム部を第一希望に出していましたが, 実際にそこに配属されておりとても嬉しかったです.

事前交流会/歓迎会

事前交流会では, 今回参加するインターン生とはてな CTO などの方々とお酒を交わしながらレクリエーションをしたりお話をしたりして盛り上がりました.
1 日目にも歓迎会があり, そこでは互いの自己紹介などを行って, インターン生やはてなの方々と楽しんでました.

講義パート

講義パートでは, 様々な講義が行われておりました. 流石にすべての講義を列挙するのは自分の文章能力では厳しいので, いくつか印象が強かったものを列挙します.

Kubernetes 講義

Kubernetes (k8s) 講義では, id:masayosu さんが講義を行っていました.
内容としては, Kubernetes がどういう仕組みで動いているか詳しいところまで説明されており, なるほど~と思いましたが, 理解が追いつきませんでした. これに加えて, 実際に minikube を使って, ローカル環境で Kubernetes を動かし, 変更を取り込むといったことまでやりました. 特に印象的だったのが, ビルド時間の長さでした. 余裕で数分かかってほえ~と声が出ました.

フロントエンド講義

フロントエンド講義では, id:Pasta-K さんがフロントエンドに関して講義を行いました.
内容は現在の JavaScript の書き方や, TypeScript の話, そして JSX (React) の話がありました. 中身がとても濃く, 資料自体もとてもよくて, もし講義パートで薦めたいものはどれですか?と聞かれたらこのフロントエンド講義と答えるぐらい素晴らしいものでした.

AWS 講義

AWS 講義では, AWS の方が実際に講師として講義を行い, 開発パートで使ったサービスを用いて AWS にデプロイをするということを行いました.
自分自身, AWS を使うのは初めてであったことと, Microsoft Azure にて失敗した前科があるので, ちょっぴり不安でしたが最終的にデプロイまでたどり着いて感動しました.
苦労した点として, AWS の IAM の設定や ECRの設定でつまづいてパイプラインが落ちまくった点です. みんなしてパイプラインが通るか祈っていたのが印象的でした.

開発パート

開発パートでは, Kubernetes や gRPC を使ったマークダウンパーサの実装を行いました. 内容としては去年のとほぼ同じで, パーサ実装 -> 独自機能追加 or fetcher 作成のどちらかとなります. 実際に作業ログを Scrapbox 上で取って, 作業を行いました.
自分は両方やりましたが, fetcher 作成に関しては時間と AWS 講義で使ったコードを反映したせいでテストができなく完成までにはいきませんでした.
独自記法に関しては, 当初はカウンタ記法という訪れるごとにカウンタが増えるインターネット老人会が苦しむようなものを実装しようと思いましたが, 思った以上に難しいと思い, UUID 記法というものに変更しました.

以下が UUID 記法の結果です:

f:id:KashEight:20210903153654p:plain

実際のコードは https://github.com/KashEight/Hatena-Intern-2021-k8s にあります.

また, 5 日目の講評では自分の書いたコードがけっこう取り上げられていてうれしかったです.

チーム開発

2 週目からは配属されたチーム (システムプラットフォーム部) でメンターとともに開発を行いました. チームメンバーは私と id:heyhoe さんでメンターは id:nabeop さんと id:halfrack さんでした.

やったこと

流石に, 詳しいことは NDA の関係上言えませんが, 作ったものとしては, 社内で利用する踏み台サーバ*1をアプリケーションレベルで可視化し, SLI (Service Level Indicator) として利用できるようなシステムを構築しました. 目的として, 作成前の構成では AWS NLB の CloudWatch Metrics による Healthy Host Count を利用した SLI を利用していましたが, NLB は L4 機器であり分類が難しいこと, 接続しているユーザの挙動を直接見れていないという理由から内部, つまりアプリケーション側の情報を利用した SLI が必要ということとなりました.

今回作成する構成では踏み台サーバの sshd のログが CloudWatch Logs に保存されている状態なので, それを GCP の BigQuery に流して可視化し, SLI として使うという感じです. 最終的には以下のような構成となりました*2:

+------------------------------------------------------------------+ +---------------------------------------------------+
|AWS                                                               | |Google Cloud                                       |
|                                                                  | |                                                   |
|                                                                  | |                                                   |
|                                                                  | |                                                   |
|  +-----------------+    +---------------------+    +---------+   | |  +------------------------------+     +--------+  |
|  | CloudWatch Logs +--->|Kinesis Data Firehose+--->|S3 Bucket+---+-+->|BigQuery Data Transfer Service+---->|BigQuery|  |
|  +-----------------+    +-------+---^---------+    +---------+   | |  +------------------------------+     +--------+  |
|                                 |   |                            | |                                                   |
|                                 |   |                            | |                                                   |
|                                 |   |                            | |                                                   |
|                              +--v---+---+                        | |                                                   |
|                              |AWS Lambda|                        | |                                                   |
|                              +----------+                        | |                                                   |
|                                                                  | |                                                   |
+------------------------------------------------------------------+ +---------------------------------------------------+

これらを構成し, 最終的には一部を IaC 化するまでに至りました.

最後に BigQuery のデータを Google Data Portal を用いて可視化して SLI の形として持っていく感じにしました.

苦労したこと

苦労したこととして, 何を SLI にするかや, AWS, GCP の権限周りの設定, BigQuery の SQLスキーマの設定, Google Data Portal の扱い方などたくさんあります.
特に, Google Data Portal や BigQuery には大いに悩まされ, 半分ぐらいはこの設定に費やした気がします.
また, SLI にする要素を考えるときにこれらも大きく変化するため本当にこの部分は苦労しました.

その他

上記の講義や開発, チーム開発以外にも, はてなの方々と Among us をしたり, DJ/VJ をしたりして楽しみました.
こういったゲームや音楽をはてなの方々とやったり聞いたりする機会はまずないので, とても有意義なものでした.

まとめ

講義+開発パートでは, 実際に手を動かしたり, 詳しいことを聞いたりした非常にレベルの高いものでありました. また, チーム開発でも, 個人ではまずできないようなことをやらしていただき実際にそれを公開するということは私にとって貴重な経験でした.

自分は初めてインターンを受けた身ですが, はてなを選んで正解だったと思います. このようなレベルの高いインターンを用意できるという点で本当にはてなを選ぶ価値はとても高いと思いました!

追記 (2021/09/03 18:43) CTO 賞をいただきました!はてなの皆さんそしてインターン生もありがとうございました!最高の夏!

*1:AWS EC2 上に存在

*2:補足: この図は正確ではなく大雑把にこうなっているという感じです, 実際はもう少し分離されてたりします

React をさくっと使う (with parcel)

この記事は TUT Advent Calendar 2019 1 日目の記事です.
初日から遅刻しかけてるの大丈夫か?

やりたいこと

create-react-app だと色々機能が多いので, さくっとかつすぐ React を触れるような構成を作る.
React プロジェクトに parcel を使用して bundle するだけ.
package 導入には yarn 使ってます. npm の方は適宜読み替えてください.

環境

  • Node.js: v12.13.1
  • yarn: 1.19.2
  • react/react-dom: 16.12.0
  • parcel-bundler: 1.12.4

手順

必要な package を追加

$ yarn init

とかでプロジェクト初期化する. 次に以下を実行して, プロジェクトに必要な package を入れる.

$ yarn add react react-dom
$ yarn add --dev parcel-bundler

作成

ソースコードは普通に長い (というか貼り付けるのが面倒な) ので GitHub に上げているから参照してください.
コンポーネントにはモダンな Functional Component, 昔ながらの Class Component どちらとも利用している. (やっていることは同じだが)

ちなみに, このプロジェクトでは node_modules/ 除いて以下のような構成になっている:

./
├── package.json
├── src
│   ├── component.js
│   ├── index.css
│   ├── index.html
│   └── index.jsx
└── yarn.lock

実行

package.json に以下を追記:

"scripts": {
    "start": "parcel src/index.html",
    "build": "parcel build src/index.html"
  }

これで, yarn startでテストサーバを起動でき, yarn build でビルドができる.

f:id:KashEight:20191201183553p:plain

Chrome 上でこのように表示されて, それぞれのボタンが反映されればおそらく大丈夫なはず.

注意

React で Class Component を使う際, メソッドを呼び出すときに, this を bind する必要があり, 煩わしくて class property*1 使うかもしれないけど, そもそもまだそれは ECMAScript で標準化されていない実装なので, 使うときは babel を用いてトランスパイルする必要があるから気をつけてほしい.

余談

実は parcel は CSS もモジュールとして読み込んで bundle してくれるので HTML に直書きしなくてもやってくれる.
なので, js 内で import "./index.css" しても別に問題ない. (なんなら parcel のホームページもそんな使い方の例を示してるし)
あと Sass とか, TypeScript とかも自動的に読み込んでくれるので本格的なものを作る必要ないなら parcel で十分な気がする. webpack だと設定面倒だし.

最後に, 最近忙しいのマジでどうにかしてくれ…実際にこれ遅刻しかけてるし…

*1:クラス直下の宣言 (コンストラクタ経由をしない宣言), これにはアロー関数も含まれる. つまり, クラス内のアロー関数宣言はメソッドではなくプロパティ扱い

Nuxt2 + TypeScriptの環境構築

この記事はKaigen Discord Advent Calendar 2018 13日目の記事です。

前置き

Nuxt.jsでTypeScriptを導入した環境を構築するのにかなり苦労したので同じことをしようと思う人と自分に向けてまとめておく。
最終的なテンプレートはGitHubにあげていますのでご参考に。

環境

  • Node.js: 10.14.1
  • Nuxt: 2.3.4

構築過程

プロジェクト作成

まず

$ yarn create nuxt-app

でNuxtプロジェクトを作る。構成は以下の画像のようにした。

f:id:KashEight:20181206205904p:plain

今回は環境構築だけなのでサーバサイドフレームワークやUIフレームワークは使わず, また, APIをいじいじするわけでもないのでAxiosも入れない。Universalアプリケーション作るわけでもないのでSPA。lintはかけたいのでeslintやprettierは今回のプロジェクトで導入した。

プロジェクトを作成したらとりあえず

$ yarn run dev

で動かしてみる。
Nuxt v2.3.4ではこのような画面が出たら成功。

f:id:KashEight:20181206213350p:plain

TypeScriptの導入

TypeScriptとWebpack用のloaderとnodeの型宣言ファイルを導入する

$ yarn add -D typescript ts-loader @types/node

Nuxt(というよりかはVueファイル)で使用するパッケージを導入

$ yarn add vue-class-component vue-property-decorator vuex-class

vue-class-componentやvue-property-decoratorをベースとしたnuxt-class-componentnuxt-property-decoratorがnuxt-communityで公開されているがどちらともメンテナンスが滞ってる(nuxt-property-decoratorは最近メンテナンスが再開された?)し, どちらともベースとなるもので事足りるのとそっちの方が機能が多いので別に導入しなくていいと思う。

TypeScriptをビルドできるようにする

TypeScriptをビルドできるようにWebpackの設定をいじってみる。
Nuxtではnuxt.config.jsのbuildプロパティ*1でWebpackの設定をいじるほか, モジュール*2という形でもWebpackの設定をいじることができる。
前者の方法では軽く設定を変える程度で複数プロジェクトで同じようなことをしない場合, そうではない場合は後者の方法を用いるのがいいでしょう。
今回のような場合は後者の方法で実装したほうがよさそう。
幸いにもNuxt2ではないがTypeScriptのテンプレートがあったのでそれを参考にNuxt2用に実装してみる。
まず, modules/typescript.jsとmodulesフォルダとjsファイルを作り以下のコードを記入する。(一応, tsxにも対応できるようにした)

module.exports = function() {
    // Nuxtで.ts/.tsxファイルを解決させる
    this.nuxt.options.extensions.push('ts', 'tsx')
    // Webpackの設定
    this.extendBuild(config => {
        // .ts/.tsxをビルドできるようにts-loaderを追加する。
        config.module.rules.push({
            test: /\.tsx?$/,
            exclude: /(node_modules)/,
            loader: 'ts-loader',
            options: {
                appendTsSuffixTo: [/\.vue$/]
            }
        })
        // Webpackのextensionsに.tsが入っていない場合解決できるようにする
        if (!config.resolve.extensions.includes('.ts')) {
            config.resolve.extensions.push('.ts')
        }
        // Webpackのextensionsに.tsxが入っていない場合解決できるようにする
        if (!config.resolve.extensions.includes('.tsx')) {
            config.resolve.extensions.push('.tsx')
        }
    })
}

次にモジュールを読み込めるようにnuxt.config.jsに以下を追記する。

modules: ['~modules/typescript.js']

tsconfig.jsonは以下のようにした。

{
    "compilerOptions": {
        "target": "es5",
        "lib": ["dom", "es2015", "es2016", "es2017"],
        "module": "es2015",
        "moduleResolution": "node",
        "alwaysStrict": true,
        "experimentalDecorators": true,
        "noImplicitAny": false,
        "noImplicitThis": true,
        "strictNullChecks": true,
        "sourceMap": true,
        "removeComments": true,
        "suppressImplicitAnyIndexErrors": true,
        "allowSyntheticDefaultImports": true,
        "allowJs": true,
        "baseUrl": ".",
        "paths": {
            "~/*": ["./*"],
            "@/*": ["./*"]
        },
        "typeRoots": [
            "node_modules/@types",
            "types"
        ]
    },
    "exclude": [
        "node_modules"
    ]
}

vscodeだと以下のようなファイルエラーが出ますが無視して構いません。(理由はよくわからないです…分かる人は教えてくれれば幸いです。)

[ts] 入力ファイルを上書きすることになるため、ファイル <rootPath>/modules/typescript.js' を書き込めません。

2/20追記: コメントにて

tsconfing.jsonのcompilerOptionsにoutdirかoutFileを指定するとエラーがなくなるようです。
変換しない場合は、"outFile": "" とするかまたは存在しないデタラメなフォルダを指定すればいいようです。

という指摘をいただきました。
確認はまだしていないのですがこれでそのvscodeのエラーは回避できるようです。
7ccさんありがとうございます!

最後に, VueでTypeScriptが使用できるように型宣言ファイルをtypes/index.d.tsのように作成して以下を記入する。

declare module '*.vue' {
    import Vue from 'vue'
    export default Vue
}

TypeScriptを使用する

pages/index.vueでTypeScriptを使用してみる。ソースは以下のようにした。

<template>
  <section class="container">
    <div>
      <logo/>
      <h1 class="title">
        {{ title }}
      </h1>
      <h2 class="subtitle">
        {{ fuwa }}
      </h2>
      <div class="links">
        <a
          href="https://nuxtjs.org/"
          target="_blank"
          class="button--green">Documentation</a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey">GitHub</a>
      </div>
    </div>
  </section>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
import Logo from '~/components/Logo.vue'

@Component({
    components: {
        Logo
    }
})
export default class extends Vue {
    title = 'hoge'
    desc = 'fuwa'
}
</script>

<style>
.container {
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

.title {
  font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
    'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  display: block;
  font-weight: 300;
  font-size: 100px;
  color: #35495e;
  letter-spacing: 1px;
}

.subtitle {
  font-weight: 300;
  font-size: 42px;
  color: #526488;
  word-spacing: 5px;
  padding-bottom: 15px;
}

.links {
  padding-top: 15px;
}
</style>

ビルドしてみる

一度, yarn run lint --fixyarn run devをしてビルドしてみよう。
localhost:3000に接続してみて以下の画像のようになったら成功。

f:id:KashEight:20181207011810p:plain

これでTypeScriptの導入は終了した。

TypeScriptにlintをかける

TypeScriptを導入したはいいけれども.ts/.tsxファイルや.vueファイル内のscript部分にはlintはかからない。
そこでtslintやtypescript-eslint-parserを導入してlintをかけようと思う。今回はeslintを導入しているのとtslintとvueの相性が悪いのでtypescript-eslint-parserを使ってlintをかける。
まず必要となるパッケージを追加する。

$ yarn add -D typescript-eslint-parser eslint-plugin-typescript

nuxt.config.js

config.module.rules.push({
    enforce: 'pre',
    test: /\.(js|vue)$/,
    loader: 'eslint-loader',
    exclude: /(node_modules)/
})

を以下のように変更

config.module.rules.push({
     enforce: 'pre',
     test: /\.(js|ts|vue)$/,
     loader: 'eslint-loader',
     exclude: /(node_modules)/
})

.eslintrc.jsに以下を追記

overrides: [
    {
        files: ["*.ts", "*.vue"],
        parserOptions: {
            parser: "typescript-eslint-parser"
        },
        plugins: ['vue', 'prettier', 'typescript']
    }
]

これでTypeScriptにlintがかかるようになった。

余談

この記事を作るきっかけとしてyarn create nuxt-appでeslintとprettierを導入して初期の状態でビルドするとエラーを吐いて導入できなかったのがあった。
原因は.vueファイル内のscriptの言語がTypeScriptだと初期のeslintのパーサであるbabel-eslintで解析してしまうためそれを回避するために記事中に書いたTypeScriptにlintをかけるような方法を取らなければいけなかった。
この記事を書いてる最中にNuxtのバージョンが上がったため新しいバージョンで試したところ, エラーを吐かずにすんなりビルドを通した。
今までの苦労はなんだったんだろう…。

脚注