tyankatsu’s blog

カレーと炭酸が苦手なほうれん草好きマンの技術ブログとか

Gridsome / eslint-plugin-gridsome のcollaboratorsになった

なりました。

ていうか個人で作ったモノが公式に含まれたというだけです。

github.com

とても困っていること

今までは個人のリポジトリで開発していてTravisからnpmにデプロイしていましたが、現在の状況でどうやって僕がnpmにデプロイするのか手段が全くわからないです。
ググってみたけどわかってないのでご存知の方がいたら教えてほしいです。

eslint-plugin-gridsomeを作った

www.npmjs.com

VueのSFCのカスタムブロックに何も当たらない

以前こんな記事を書くぐらいGridsomeに魅了されたわけですが、一つ気になることがありました。
それはESlintのfix、もしくはPrettierがカスタムブロックに効かないということです。

著者はVSCodeでVeturを使用しSFCでVueを書いています。
Gridsomeは<page-query><static-query>といったGridsome独自のカスタムブロックにGraphQLを書き、Dataを拾ってきます。
このカスタムブロックは現状Vue公式が正式に認めたブロックではないので、公式が作成しているeslint-plugin-vueがもちろん対応しているわけもなく、最近Vueに対応したPrettierもこの独自ブロックに対応しているわけでもありません。

脱線しますが、Veturはカスタムブロックのシンタックスハイライトが定義可能です。詳しくはこちらを御覧ください。
現状graphqlという項目がありませんが、こちらのissueで取り上げられています。

Gridsomeにissueを立てる

既にこのことに関して構想があるかもしれないので、issueを立てました。

github.com

しかし反応が全然ありませんでした。
「もうこの際自分で作ってみて提案するか。駄目だったとしてもいい経験になるし」という考えから、自分で作ってみることにしました。

ESlintのルール作成

PrettierかESlintか

Prettierにはpluginがいくつかありますが、それはPrettierが現在対応していない言語の開発用パッケージ(後に本体に組み込まれる)という認識でした。 現段階ではどっちにしようか決めかねてましたが、Prettierのプラグインではなさそうかも??って思ってました。

ESlintのルールの作り方を調べる

ESlintの公式には開発者向けのページが存在します。
その中でも著者はRuleを作りたいわけですので、こちらのページを眺めました。

挫折

というわけで、全く理解ができませんでした。
そもそもASTがわかってなかったわけです。

eslint-jpにてissueを立てる

途方に暮れているとこちらの記事を発見しました。

qiita.com

早速質問させていただきました。

github.com

同時にこちらの記事を参考にルールの作り方もざっくり理解しました。

qiita.com

Toru Nagashimaさんには、全く理解が追いついていない著者に対してとても親切丁寧にアドバイスをしていただきました。本当にありがとうございます。

twitter.com

また、こちらのイベントでゆうてんさんにASTとはなにか、ゆうてんさんが作っているmarkuplintを参考にしながらlintが適用されていく過程、著者がESlintで何をしたいのかの整理を手伝っていただきました。ありがとうございます。そしていつもありがとうございます。

cdg.connpass.com

twitter.com

github.com

作っていることを発信する

同時進行でTwitterでの情報の発信も行いました。

この内容がGridsomeコアメンバーの目に留まり、以前投げたissueの返事が来ました。 試したいということだったので、ユニットテストも書き終えていたので、パッケージを公開し、試してもらいました。

公式のDiscordを覗くと、著者が試行錯誤していたリポジトリが取り上げられていました。

現状

まだ返事待ちの状態ですが、公式のリポジトリに含まれたらいいなーという感じです。

最後に

OSSの良いところは、簡単に要望や質問を投げられるところです。 運営者の目に止まらないところでやんややんや言うのも良いですが、直接意見を投げてみると進展があったりすると思いますので、著者は今後も情報発信をしてみたいです。

2018/12/09 追記

こうなりました。

tyankatsu.hatenablog.com

eslint-gridsomeを作ってみている【殴り書き】

こういうものを作り始めた。

github.com

今日ゆうてんさんに、

  • ASTについて
  • lintのルールの適用のフロー

について聞いた。 twitter.com

すごいなーって思いでイジイジしてた。

なんで作ろうと思ったか

GridsomeはSFC内にGraphQLをかけるカスタムブロックが定義されているが、フォーマッターが反応しない無法地帯になっている。 公式が動いている気配がないため、なら作ろうっていう考えで作り始めた。

何がしたいのか

ここに書いてある

github.com

参考になったもの

eslintの基本のきを知るのにちょうどよかった。 また、体系的にルールの作り方が書いてあったので、すんなり頭に入ってきた。 qiita.com

Gridsomeを使ってみる【ビルド&デプロイ編】

前回の記事で開発環境を整えました。

開発する前に、先にビルドとデプロイ環境を整えたいと思います。
早速見ていきます。

Travisの設定

Travis CIにアクセスしGitHubアカウントでログインしましょう。 Travisと連携させたいリポジトリのスイッチをONにします。 これで設定は完了です。 f:id:tyankatsu:20181123141104p:plain

Travisコマンドをインストールする

TravisはHomebrewにもあるので、今回はHomebrewを使ってインストールします。

versionオプションでバージョンの番号が出ているとインストールができていることが確認できます。

$ travis --version
1.8.9

.travis.ymlを作成する

.travis.ymlはTravisにどのような処理をさせるか命令を記述するファイルです。
以下のように記述しておきます。

.travis.yml

language: node_js
node_js:
  - 10

dist: trusty
sudo: false
cache: yarn

branches:
  only:
    - release

before_deploy:
  - yarn global add netlify-cli
  - yarn build

deploy:
  provider: script
  script: netlify deploy -s $NETLIFY_SITE_ID --auth $NETLIFY_PUBLISH_KEY -p --dir ./dist
  on:
    branch: release

このファイルの要点だけ説明します。

yarnを使用する

dist: trusty
sudo: false
cache: yarn

詳細はこちらを参照してください。
Travis CIでYarn(pkg)を使う設定 (node.js) - Qiita

実行ブランチを設定する

branches:
  only:
    - release

releaseブランチにプッシュした場合にTravisが動くようにしています。

deployするときの処理を書く

deploy:
  provider: script
  script: netlify deploy -s $NETLIFY_SITE_ID --auth $NETLIFY_PUBLISH_KEY -p --dir ./dist
  on:
    branch: release

providerにscriptと書くことで、自分のscriptでデプロイさせます。
詳しくはこちらを参照してください。
Script deployment - Travis CI
onはデプロイ処理をする対象を指定します。
この場合だとreleaseブランチです。
script内の$NETLIFY_SITE_ID$NETLIFY_PUBLISH_KEYTravis環境変数です。

envファイルを暗号化する

実行環境によって値が変わるものをenvファイルに入れて開発していますが、ファイルが無いとデプロイできません。
しかしgit管理対象にしているので、Travisを実行する場合はビルドができません。
そのため、ファイルを暗号化して、それをTravis上で解凍します。

script/encrypt.sh

tar cvf env.tar \
.env \
.env.production
travis encrypt-file env.tar

env.tar.env.env.productionを入れ、travis encrypt-fileenv.tarを暗号化します。
travis encrypt-fileコマンドでいろいろ聞かれますが全部yesでいいです。
すると以下の表示がされ、env.tar.encが作成されます。

Please add the following to your build script (before_install stage in your .travis.yml, for instance):

    openssl aes-256-cbc -K $encrypted_09f96960bb2d_key -iv $encrypted_09f96960bb2d_iv -in env.tar.enc -out env.tar -d

Pro Tip: You can add it automatically by running with --add.

Make sure to add env.tar.enc to the git repository.
Make sure not to add env.tar to the git repository.
Commit all changes to your .travis.yml.

この表示通りに.travis.ymlに以下を追加します。

language: node_js
node_js:
  - 10

dist: trusty
sudo: false
cache: yarn

branches:
  only:
    - release

# 以下を追加
before_install:
  - openssl aes-256-cbc -K $encrypted_09f96960bb2d_key -iv $encrypted_09f96960bb2d_iv -in env.tar.enc -out env.tar -d
  - tar xvf env.tar

before_deploy:
  - yarn global add netlify-cli
  - yarn build

deploy:
  provider: script
  script: netlify deploy -s $NETLIFY_SITE_ID --auth $NETLIFY_PUBLISH_KEY -p --dir ./dist
  on:
    branch: release

env.tar.encはgit管理しておきましょう。
before_installのtar xvf env.tarは、Travis上でenv.tarを解凍する処理です。これがないと.env.env.productionenv.tarに入ったままになるので注意しましょう。
ちなみに秘密鍵Travis環境変数にちゃんと入っています。
f:id:tyankatsu:20181123141207p:plain

Netlifyで設定をする

Netlifyにログインしましょう。GitHubアカウントでログイン可能です。

New site from Gitから、リポジトリを選択しましょう。
f:id:tyankatsu:20181123141232p:plain いろいろ選択可能ですが、今回はTravisからデプロイするので設定不要です。
Deploy siteからサイトを作成します。

Site settingsからAPI IDという項目があるので値をコピーします。
f:id:tyankatsu:20181123141251p:plain

Travis環境変数に、項目名は何でもいいですが、分かりやすいようにNETLIFY_SITE_IDという項目を作ってさきほどの値をペーストします。
f:id:tyankatsu:20181123141307p:plain

アカウント設定画面からOAuth applicationsを選び、Personal access tokensからNew access tokenでtokenを発行します。
項目名Travis publish keyとしておきます。
その後表示されるtokenは今後表示できないので、誰にも見られない場所に保管しておきましょう。
Travis環境変数に、やはり項目名は何でもいいですが、NETLIFY_PUBLISH_KEYという項目名でさきほどの値をペーストします。
f:id:tyankatsu:20181123141332p:plain

対象ブランチでビルドしてnetlifyにデプロイする

releaseブランチを作成し、プッシュするとTravisが起動します。
encフォルダ解凍されビルドを実行し、distフォルダが作成されます。
その後
netlify deploy -s $NETLIFY_SITE_ID --auth $NETLIFY_PUBLISH_KEY -p --dir ./dist
でdistフォルダがNetlifyに送られデプロイされます。

以上でデプロイ完了です。

最後に

netlify cliのバージョンが上がり、コマンドの使い方が変わった場合は修正が必要になります。注意してください。

Hygenでファイル作成を簡単にする

Hygenとはコマンドラインからファイルを生成する事が可能なパッケージです。
バージョンは現在1.6.2です。

以下ざっくりとした特徴です。

  • ファイル生成の雛形となるテンプレートをejs形式で書く
  • 自分でオプションを足して、それをテンプレート内の値として使用できる
  • inquirerを内包しており、質問を自分でカスタマイズして対話的にファイルを作成することが可能

本記事ではHygenの使い方を紹介します。

著者の環境です。

  • node...10.13.0
  • npm...6.4.1
  • yarn...1.12.3
  • nodeバージョン管理...ndenv

基本的な使い方

インストールします。

yarn add hygen -D

最初に試すのであれば以下のコマンドでhygenが自動でテンプレートを生成してくれます。

yarn hygen init self

すると以下のフォルダが作成されます。

_templates
└── generator
    ├── help
    │   └── index.ejs.t
    ├── new
    │   └── hello.ejs.t
    └── with-prompt
        ├── hello.ejs.t
        └── prompt.ejs.t

これらはhygenのチュートリアル的なファイルたちです。
これらを使ってHygenの使い方を探っていきます。

ファイル構成

_template/generator/new/hello.ejs.t

---
to: _templates/<%= name %>/<%= action || 'new' %>/hello.ejs.t
---
---
to: app/hello.js
---
const hello = `
Hello!
This is your first hygen template.

Learn what it can do here:

https://github.com/jondot/hygen
`

console.log(hello)

まず、このtファイルは2つのセクションに分けられています。

---
to: _templates/<%= name %>/<%= action || 'new' %>/hello.ejs.t
---
---
to: app/hello.js
---

ここはhead部分で、ファイルに関するメタ情報を記述する場所です。
yaml形式で記述します。
front-matterを使用しているようです。

const hello = `
Hello!
This is your first hygen template.

Learn what it can do here:

https://github.com/jondot/hygen
`

console.log(hello)

次にbody部分はファイルの内容を記述します。
ejs形式で記述します。
ejsはテンプレートエンジンです。
書き方がわからない場合はこちらの記事を参照してください。
テンプレートエンジンEJSで使える便利な構文まとめ - Qiita

これらがhygenを使う場合のテンプレートファイルの基本になります。

generator new

yarn hygen generator new --name Card
_templates
├── Card
│   └── new
│       └── hello.ejs.t
└── generator
    ├── help
    │   └── index.ejs.t
    ├── new
    │   └── hello.ejs.t
    └── with-prompt
        ├── hello.ejs.t
        └── prompt.ejs.t

_template/Card/new/hello.ejs.t

---
to: app/hello.js
---
const hello = `
Hello!
This is your first hygen template.

Learn what it can do here:

https://github.com/jondot/hygen
`

console.log(hello)

もう一度コマンドを実行します。

yarn hygen Card new
.
├── _templates
│   ├── Card
│   └── generator
└── app
    └── hello.js

app/hello.jsができました。

機能分解

まず先程のコマンドを分解すると、

hygen generator new

hygenを使ってgeneratorという名のジェネレータでnewアクションを使用しファイルを作成する。
という意味になります。
それぞれの用語はこちらを参照してください。
Templates | Hygen

次にオプションですが、

--name Card

これはnameにCardという値を入れる。
という意味です。 yargs-parserで実現しています。

_template/generator/new/hello.ejs.t

---
to: _templates/<%= name %>/<%= action || 'new' %>/hello.ejs.t
---
---
to: app/hello.js
---

一回目のコマンドでhello.ejs.tの上段のto:が消費され、
<%= name %>Cardが入ったことで、
_template/Card/new/hello.ejs.tが作成され、
二回目のコマンドで、
app/hello.jsが作成されました。

generator help

yarn hygen generator help

_templates/generator/help/index.ejs.t

---
message: |
  hygen {bold generator new} --name [NAME] --action [ACTION]
  hygen {bold generator with-prompt} --name [NAME] --action [ACTION]
---

機能分解

messageプロパティはコマンド上にメッセージを表示させるために使います。
toプロパティと違ってファイルが作成されることはありません。 |yamlで改行を意味します。
YAMLで改行する方法 - このブログは証明できない。

こう書くことも可能です。

---
to: _templates/<%= name %>/<%= action || 'new' %>/hello.ejs.t
message: |
  --nameには<%= name %>が入りました。
---
---
to: app/hello.js
---
const hello = `
Hello!
This is your first hygen template.

Learn what it can do here:

https://github.com/jondot/hygen
`

console.log(hello)

これを実行するとコマンド上ではこう出力されます。

Loaded templates: _templates
       added: _templates/Card/new/hello.ejs.t
new:
--nameにはCardが入りました。

generator with-prompt

yarn hygen generator with-prompt --name hoge
_templates
├── generator
│   ├── help
│   │   └── index.ejs.t
│   ├── new
│   │   └── hello.ejs.t
│   └── with-prompt
│       ├── hello.ejs.t
│       └── prompt.ejs.t
└── hoge
    └── with-prompt
        ├── hello.ejs.t
        └── prompt.js

もう一度コマンドを実行します

yarn hygen hoge with-prompt
? What's your message?
...
...

inquirer.jsが実行されました。

機能分解

propmpt.jsを作成し、その中にmodule.exportsで配列にinquirerのフォーマットを書いていくと、inquirerが作動します。
いろいろ書式が存在し、文字入力を受け付けるinput、リストから一つ選択するlistなどがあります。
Inquirer.js こちらが参考になります。
Inquirer.js/packages/inquirer/examples

{
    type: 'input',
    name: 'message',
    message: "What's your message?"
}

namevalueと同じものを~~~~~.ejs.tの中に<%= <value> %>と書けばそこに入ります。 例えば以下のとおりです。

{
    type: 'input',
    name: 'dir',
    message: "ディレクトリ名は?"
},
{
    type: 'input',
    name: 'name',
    message: "ファイル名は?"
},
{
    type: 'input',
    name: 'type',
    message: "拡張子は?"
},
---
to: <%= dir %>/<%= name %>.<%= type %>
---
body...

promptを実行すると

? ディレクトリ名は? src
? ファイル名は? hoge
? 拡張子は? html

Loaded templates: _templates
       added: src/hoge.html
// src/hoge.html

body...

.hygen.js

configファイルです。

module.exports = {
  templates: `${__dirname}/_templates`,
  helpers: {
    img: name => `src/assets/img/${name}`
  }
}

templatesはHygenのテンプレートディレクトリの場所を示します。
上記の例だとhygenコマンドを実行するとプロジェクト直下の_templatesを見ます。 helpersオブジェクトにはejs.t内でhでアクセス可能です。
上記の例だと<%= h.img('test.svg ') %>と記述すると、src/assets/img/test.svgと出力されます。

実際に使ってみての感想

著者はVueを使っており、jestを使用してtestを書くことが多いのですが、 vueファイルを書いて同じ構造でtestファイルを作るのが面倒だと悩んでいました。

src/components/VIcon.vue を作ったら tests/unit/components/VIcon.spec.jsを作るのが面倒だという話です。

hygenを使用するとファイルの作成が簡単であり、初期値も入れられるのでとても効率的だと思います。
著者はまだstorybookを使用したことがないのですが、storyファイルを作るのも簡単だと思います。
自分のカスタマイズ次第でなんとでもできそうな可能性を秘めていると思うので、ぜひ皆さんにも使っていただきたいです。

「npmパッケージ製作に関するあれこれ」の補足

俺の話を聞け!!LT大会 #12でLTしてきました。 スライドはこちらになります。npmパッケージ製作に関するあれこれ - Speaker Deck

いろいろ端折った部分があったので、本記事で補足させていただきます。

npmパッケージ開発に関してテーマにした理由

npmパッケージの公開方法のtipsの記事がそんなにヒットしなかったので、だったら自分で記事にしようと思った次第です。

cz-format-extensionとは

commitizenのルールをユーザーが簡単に拡張できるようにしようという目的で作成中のパッケージです。
似たパッケージにcz-customizableがあります。
これはこれでいいのですが、

  • inquirerのバージョンが古すぎる
  • configの書き方が微妙
  • 選択肢はカスタマイズできても、出力されるフォーマットはいじれない
  • configのフィールドを分けないといけないのが気持ち悪い

という不満があり、現在僕と、前職で一緒だったフロントエンドエンジニアの方と作成中です。(誰か手伝って🙏)

ブラックリストホワイトリスト

.npmignoreとpackage.jsonのfilesフィールドの併用

詳しくはこちらに書いてありますので、参照してください。
細かすぎて伝わらない package.json 小ネタ三選 - t-wadaのブログ

npm publishの罠

以下のように npm publish をnpm scriptsとして設定するとエラーが吐かれます。

{
  "scripts": {
    "hoge": "npm publish"
  }
}

// yarn hoge

npm notice
...
npm ERR! code ENEEDAUTH
npm ERR! need auth auth required for publishing
npm ERR! need auth You need to authorize this machine using `npm adduser`
...

ここに関しては理由がわからないです。
たぶんローカルの.npmrcに記載しているauth情報を取得できていないからだと思います。

CHANGELOG.mdを作る

CHANGELOG.mdは、そのパッケージの変更履歴をパッケージのバージョンと共に記述し、ユーザーにどんな変更があったのかを知らせるために作成します。
例えばこんな感じです。 f:id:tyankatsu:20181115231607p:plain

これをイチから作るのも面倒なので、僕はgenerate-changelogを使っています。
これは、ルールに従ったコミットコメントを書くと、整形されてCHANGELOG.mdが生成されるというものです。

例えば以下のようなコミットコメントを作成します。

feat: :tada: generate-changelog追加

すると、 f:id:tyankatsu:20181115231641p:plain このように自動的に New Featuresの項目としてコミットコメントが追記されます。末尾のリンクはオプション設定でつけられますが、該当コミット内容詳細に飛びます。

ユーザーに、今回のバージョンアップはこのようなことをやったよということを知らせることができるので、入れておいたほうが良いと僕は思ってます。

CONTRIBUTING.mdを作る

いろいろな人に関わってもらえるようになると、issueの立て方やPRsの作り方、コミットコメントのルールなどを作って、一貫性を持たせて作業してほしくなります。
そういうときにはCONTRIBUTING.mdを生成してその中にお約束ごとを記述しておきます。こういうふうに書くといいらしいです
GitHubだと、プロジェクト直下にこのファイルを置くと、issueをたてるときやPRsを作る時にリンクが出てきます。
秩序が保たれるようにルールを決めておきましょう。

TemplateでissueとPRをきれいに出せるようにする

GitHubの話になりますが、issueとPRsのテンプレートを作成することができます。GitHubのIssue・Pull Requestのテンプレート機能を使おう - Qiita

issueを作るときはこんな画面でどのテンプレートを使うか選択可能です。 f:id:tyankatsu:20181115231658p:plain

PRsは最初から中身が入った状態でPRsが作られます。

travisでnpmにパッケージ公開してもらう

自分でnpm publishと打ち込むのもいいですが、なんせ面倒です。
なので、僕はtravisにpublishしてもらうついでに、babelでのbuildもしてもらいます。
バージョンタグを打ったらtravisが走るように設定してます。

language: node_js
node_js:
- 10

dist: trusty
sudo: false
cache: yarn

branches:
  only:
  - "/^v?[0-9\\.]+/"

script:
- yarn build

deploy:
  provider: npm
  email: $NPM_EMAIL
  api_key: $NPM_TOKEN
  on:
    tags: true
  skip_cleanup: true
{
  "scripts": {
    "build": "babel src --out-dir lib"
  },
}

travis環境変数にdeployに必要なemail、authTokenを入れます。
情報はローカルの.npmrcに書いてあるので、コピペします。

init.author.name=name
init.author.email=email //ここ必要
init.author.url=url
//registry.npmjs.org/:_authToken=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx //ここ必要

なお、環境変数travisのログに表示・非表示の切り替えができます(デフォルトは非表示)が、表示する必要がないですし、セキュリティの面も考えて、ここは非表示にしておきましょう。

travisでデプロイ時のnpmの二段階認証の罠

こちらの記事が参考になったので、参照してください。
Automated npm releases with Travis CI

npm運営に相談

公開したくないパッケージを公開してしまって、非公開にしたい場合、24時間以内であれば、npm unpublishで消せます。(一回消したパッケージはその後24時間、同じ名前で公開することが不可能になります) npm-unpublish
しかし、やらかしてしまった場合は、support@npmjs.comでメールを送ることでなんとかしてもらえます。頑張って英語でメールしましょう。大体悟ってくれます。

最後に

そんなにハードルは高くないので、まずはscopeしたパッケージでも作ってみてはどうでしょうか??