タブを折り返して多段表示するAtomのプラグインをつくった
花金なので、multiline-tabっていうAtomのプラグインをつくりました🍻
Atomって開いてるタブが多くなると、タブ幅が小さくなりすぎてファイル名が読めない上に、エディタの幅に収まらないタブは横スクロールで画面外に消えてしまって使いづらすぎるので、タブ幅の最小値を大きくして、エディタの幅を超える場合はタブを折り返して表示するためのプラグインです。
もともと、開いているタブを縦に並べるプラグイン(👇)を使わせてもらっていたんだけど、
GitHub - 1000ch/atom-vtab: Verticalize tabs on Atom.
横並びのままで折り返したいなって思って、上記のコードをパク参考にさせてもらいました。ありがとうございます :bow:
やってることはCSSしか触ってないくらい簡単なんだけど、いろんなテーマで崩れないように調整するのが結構つらかったです。結局、あまりいじらないのがベストだってことになりました。
タブの幅はとりあえず固定になってるけど、本当は設定で自由に変更できるようにしたかったので、時間とれたら実装するかも。でもプルリクも待ってます。
最初はwrap-tabっていう名前だったんだけど、apm publish
が最初うまくできなくて、あれこれしてるうちにAPI tokenのキーをうっかり作り直してしまって、権限がなくなって削除もできなくなってしまって残骸になってしまったので、multiline-tabという別の名前に変えました。どうやって消そうかな。
jQuery.ajax()の代替としてFetch APIをざっくり使ってみる
jQueryを使わずにAjaxをしたくて、とはいえ生のXHR(XMLHttpRequest )を扱うのはめんどくさいっていうときに、Fetch APIを使ってみました。そのとき調べたことの覚え書きです。
Fetch APIって?
ここに、Jxck先生のすばらしい記事があります。
正直ぜんぜん理解できてないのですが(🙇)、ものすごくざっくりいうと、
みたいな感じなのかなと思いました。
ちなみに、先の記事では単なるXHRの代わりじゃないと記載されてるので、FetchとかFetch APIの理解にはそちらを読んでもらった方がいいかと思います。。
ブラウザサポート状況
Can I use... Support tables for HTML5, CSS3, etcを見る限り、IEとSafari、iOS Safari以外は対応してるみたいです。
私の場合はそれでもよかったので使ってないのですが、pollyfilもあるみたいです。
GET
次のような感じでGETができます。簡単!
fetch(url) .then(response => { return response.text(); }) .then(body => { document.body.innerHTML = body; });
先述したとおり、Fetch APIではPromiseが返ってくるため、then()
で繋げて書いていくことができます。
レスポンスは、以下のいずれかの形式で取得できます。
- プレーンテキスト :
response.text()
- JSON :
response.json()
- ArrayBuffer :
response.arrayBuffer()
- Blob :
response.blob()
POST
POSTする場合は、fetch()
の第二引数にオプションでmethod: 'POST'
を指定するだけです。$.ajax()
っぽい!
fetch(url, { method: 'POST', body: new FormData(document.getElementById('form')) }) .then(response => { return response.json(); }) .then(json => { ... });
サーバーに値を渡すには、body
オプションに指定できます。
クロスオリジン通信
オリジンが異なる(別ドメインの)サーバーと通信したい場合は、次のようにCORSを使います。
1. クライアント側で、fetch()
のmode
オプションに'cors'
を指定する
fetch(url, { mode: 'cors' }) .then(response => { ... });
2. サーバー側でリクエストヘッダのOrigin
をチェックする
GET /api HTTP/1.1 Origin: http://xxx.com
3. サーバー側でレスポンスヘッダにAccess-Control-Allow-Origin
を追加する
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://xxx.com
これで、クロスオリジン通信が可能になります。
Access-Control-Allow-Origin: *
のAPIの場合は1.の指定だけでできると思います。
Cookieを許可
Cookieを許可するには、次のように指定します。
1. クライアント側でfetch()
のcredentials
オプションに'include'
を指定する
fetch(url, { mode: 'cors', credentials: 'include' }) .then(response => { ... });
2. サーバー側でレスポンスヘッダにAccess-Control-Allow-Credentials: true
を追加する
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://xxx.com Access-Control-Allow-Credentials: true
独自のリクエストヘッダを追加
とあるAPIを利用するときに、独自のリクエストヘッダを追加して通信する必要がありました。その場合、次のように指定できます。
1. クライアント側でfetch()
のheaders
オプションに、オブジェクトの形式で独自ヘッダーを指定する
fetch(url, { method: 'GET', mode: 'cors', headers: { 'X-MyRequest': 'hoge' } }) .then(response => { ... });
2. サーバー側でプリフライトリクエストのリクエストヘッダを確認する
OPTIONS /api HTTP/1.1 Origin: http://xxx.com Access-Control-Request-Method: GET,POST,HEAD,OPTIONS Access-Control-Request-Headers: X-MyRequest
3. 許可する場合は、サーバー側からレスポンスヘッダを返す
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://xxx.com Access-Control-Allow-Methods: GET Access-Control-Allow-Headers: X-MyRequest
4. 実際のリクエストがクライアントからサーバーに送られる
サーバー側からAccess-Control-Allow-Methods
とAccess-Control-Allow-Headers
で許可されたものしか送れないという仕組みのようです。
独自のレスポンスヘッダを読み出す
まず最初に、ダメだった方法を。
1. サーバー側から独自のレスポンスヘッダを追加したレスポンスを返す
HTTP/1.1 200 OK X-MyResponse: hoge
2. クライアント側でresponse.headers.get()
にて取得しようとするもできない
fetch(url, { method: 'GET', mode: 'cors' }) .then(response => { console.log(response.headers.get('X-MyResponse')); });
この場合、セキュアではないヘッダにアクセスしようとしたと見なされてアクセスが許可されなかったようです。
なお、以下のヘッダには、問題なくアクセス可能です。
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
そのため、次のようにする必要があるようです。
1. サーバー側でアクセスを許可する独自ヘッダをAccess-Control-Expose-Headers
で指定する
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://xxx.com Access-Control-Allow-Methods: GET,POST,HEAD,OPTIONS Access-Control-Allow-Headers: X-MyRequest Access-Control-Expose-Headers: X-MyResponse
2. クライアント側でresponse.headers.get()
にて取得する
fetch(url, { method: 'GET', mode: 'cors' }) .then(response => { console.log(response.headers.get('X-MyResponse')); });
サーバー側のエラーの処理
fetch()
では、サーバー側でエラーが起こってもレスポンスはreject()
されません。つまりcatch()
できないわけです。
では、どういう場合にreject()
されるのかというと、ネットワークエラーの場合のみのようです。そのため、サーバーエラーの処理は、then()
の中でresponse.ok
をチェックする必要があります。
fetch(url) .then(response => { if (!response.ok) { throw Error(response.statusText); } return response; }) .then(response => { ... }) .catch(err => { ... });
まとめ
jQuery.ajax()
が使えるときはそっちのが安心感あるけど、個人プロジェクトで少し触ってみる程度ならよいのかも?
※実際に使ってみたのは1年以上前だし、内容が古いかもしれないです。
package.jsonのnpmのバージョンを一括で書き変えてくれるncuが便利だった
npm-check-updatesというNode.jsのモジュールを使ってみたら便利でしたという話です。
2013年のブックマークとかもあったので、別に新しいものではないと思うけど、私は知らなくて感動したのでした。
なにこれ?
package.json
に書かれているnpmのバージョンを、一括で最新に書き変えてくれるNode.jsのモジュール。ncu
コマンドひとつで一度に書き変えできて便利。- 実際に書き変える前に確認だけすることもできる。
- 「メジャーバージョンは固定で、それ以下を最新にしたい」などの細かい設定もオプションで可能。
どんなときに便利なの?
- 既存の
package.json
を流用して、同じnpmをそのまま使いたいけど、バージョンだけは新しくしたいとき。 - 昔つくったサイトのメンテナンスで、npmのバージョンだけを新しくしたいとき。
使い方
初回のみ、npm-check-updatesをグローバルにインストールする必要があります。
$ npm install -g npm-check-updates
👆によって、ncu
コマンドが使えるようになるので、その後は、対象のpackage.json
のあるディレクトリに移動して、ncu
コマンドを実行するだけ。
確認と実行
どう書き変わるのかを確認する
ncu
コマンドだけだと、変更前と変更後のリストが表示されるだけで、package.json
はまだ書き替わらないので安心。
$ ncu express 4.12.x → 4.13.x multer ^0.1.8 → ^1.0.1 react-bootstrap ^0.22.6 → ^0.24.0 react-a11y ^0.1.1 → ^0.2.6 webpack ~1.9.10 → ~1.10.5
実際に書き変える
-u
オプションをつけることで、実際にpackage.json
が書き変えられる。
$ ncu -u
npmを個別に指定
特定のnpmのみをバージョンアップしたい場合は、対象のnpmの名前を指定するだけ。
$ ncu express, webpack
npmの名前の指定には、正規表現も使えて便利。
$ ncu '/^(?!gulp-).*$/'
バージョンを細かく指定
メジャーバージョンも含めて最新にする
デフォルトではメジャーバージョンも含めて最新になります。
$ ncu gulp-eslint 2.0.0 → 3.0.1
メジャーバージョンは固定、それ以下を最新に
--semverLevel major
オプションをつけることで、メジャーバージョンは固定で、それ以下を最新にできます。
メジャーバージョンアップをして、既存のコードが動かなくなるのを避けたい場合などに。
$ ncu --semverLevel major gulp-eslint 2.0.0 → 2.1.0
マイナーバージョンまで固定、それ以下を最新に
--semverLevel minor
オプションをつけることで、マイナーバージョンまでを固定して、それ以下を最新にできます。
$ ncu --semverLevel minor gulp-eslint 2.0.0 → 2.0.1
気の利くところ
既存のpackage.json
の記法を尊重して、アップデート後も同じように書き変えてくれて気が効くなと思いました。やさしさ。
$ ncu 1.2 → 1.3 0.1.0 → 0.2.1 ^1.2.0 → ^2.0.0 1.x → 2.x
gulpfile.jsをES6で書いてみよう
なぜgulpfile.jsをES6で書くの?
ES6にとりあえず触れて慣れてみたいという場合に、まずはgulpfile.js
で試してみるとよさそうという風潮があるみたい。理由はたぶん次のような感じかなと思ってる。
- 他への影響が少ない
- コードの分量が少ない
- 実装にあまり頭を使わないで書ける
- 環境によってはbabel等のトランスパイラをインストールせずにES6がそのまま使える
環境を準備する
gulpfile.js
でES6を使えるようにするには、gulp側で対応するか、Node.js側で対応するかどちらかになります。
gulp側でES6に対応する
gulpはv3.9.0からbabelを標準サポートするようになったので、babelを介してES6構文を利用できるようになりました。
ただし、ES6構文を使う場合は、ファイル名をgulpfile.babel.js
にする必要があります
- gulpのバージョンを3.9.0以上にupdate
gulpfile.js
のファイル名を、gulpfile.babel.js
に変更
Node.js側でES6に対応する
Node.jsはv4.0.0からES6に(一部)対応したので、Node.jsのバージョンが4.0.0以上であれば、そのままES6の構文を使うことができます。
- Node.jsのバージョンを4.0.0以上にupdate
- ファイル名は
gulpfile.js
のままでよい
ちなみに、この記事はNode.js 5.0.0で動作確認しているので、それ以下のバージョンの場合は動かないものもあるかも?(参考:Node.jsでサポートしているES6の構文の一覧)
gulpfile.jsで使いそうなES6構文いろいろ
ES6の構文を使うと言っても、gulpfile.js
では簡単なものしか使う機会はなさそうですが、実際に使いそうな構文をいくつか挙げておきます。
const / let
var
をconst
またはlet
に置き換えられます。
ただし、let
はあまり使う箇所はなさそう。
今までの書き方
var gulp = require('gulp');
ES6の書き方
const gulp = require('gulp');
Arrow Functions
function ()
を() =>
と書けるようになります。
本来の外側のthis
が使えるという機能はgulpfile.js
の場合あまり関係なさそうで、単にタイプ数が少し減ったり、見た目がスッキリするといったメリットくらいかな。
今までの書き方
gulp.task('copy', function () { return gulp.src('src/favicon.ico') .pipe(gulp.dest('dist')); });
ES6の書き方
gulp.task('copy', () => { return gulp.src('src/favicon.ico') .pipe(gulp.dest('dist')); });
Template Strings
文字列と変数を結合するような場合に、'string' + foo
のように+
を用いていた部分を、string#{foo}
のように書けるようになります。
今までの書き方
gulp.task('sprite', function () { var spriteData = gulp.src('images/*.png').pipe(spritesmith({ cssVarMap: function(item) { item.name = 'b-icon--' + item.name; } })); return spriteData.pipe(gulp.dest('path/to/output/')); });
ES6の書き方
gulp.task('sprite', () => { const spriteData = gulp.src('images/*.png').pipe(spritesmith({ cssVarMap: item => { item.name = `b-icon--${item.name}`; } })); return spriteData.pipe(gulp.dest('path/to/output/')); });
注意点
gulpfile.babel.js
の場合はbabelを使うのでimport
が使えるけれど、gulpfile.js
の場合はimport
が使えません。
Node.js(v5.0.0時点)ではimport
がサポートされていないからです。そのため、ES6をどうやって実行するかによって(ファイル名によって)、次のように記述が変わります。
ややこしいのでどっちの場合でもimport
は使わない方がいいのかもしれません。
gulpfile.babel.js
import gulp from 'gulp'
gulpfile.js
const gulp = require('gulp');
まとめ
とくに問題なく使えたので、これからgulpfile.jsを新たに書く場合はES6の構文でよさそう。
CSSではみ出した文字を省略する「text-overflow: ellipsis;」がいつの間にかPCでも使えるようになってた!
領域をはみ出したときに三点リーダー(…)で省略するtext-overflow: ellipsis;
は、スマホサイトでは普通に使っていたけど、PCでは数年前に使おうとしてなんかのブラウザでダメだった記憶があって、使えないものとばかり思い込んでいた。
でも、ふとCan I use CSS3 Text-overflowをみたら、めっちゃ対応してた!IEなんて6から対応してるし、Firefoxも7から使えるようになってた。
実際に書いてみたら、いつものこんな感じのコードで普通に使えました。
overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
昔、JSでなんとかそれっぽくできないかみたいに、誰かががんばっていたのはなんだったのか。
わたしみたいに使えないと思い込んでて、意外としらない人いるかもしれないので、ブログに書いてみました。
楽天カンタン送料表示(Chrome拡張)を公開したよ!
今週の月曜に突如Chrome拡張機能に興味をもって、火曜に勉強を始めて、木曜の今日Chromeウェブストアに公開しました!(∩´∀`)∩ワーイ
ウェブストアに出てる!!!感慨深い!!!Developer感アル。
どういう機能かというと、楽天の商品ページにいくと、画面左下に送料をすぐに見られるためのボタンが表示されるというものです。
楽天の送料ページは、http://www.rakuten.co.jp/shopName/info2.html
で見られるのですが、いろいろ面倒です。
- 送料を見るために、商品ページから画面遷移が必要
- 送料以外の情報も一緒に書かれている
- 長すぎてどこを見ればいいのかぱっと見で分からない
- ショップによって送料ページへのリンクの位置がバラバラ
これらのことを解決したくて、送料の部分のHTMLだけをスクレイピングして取得してきて、商品ページ内で送料をすぐに見られる機能がほしかったのでつくりました。
オススメの勉強方法
Chrome拡張機能についての勉強は実質2時間くらいしかしてないけど、すごくお手軽につくれてしまった。
事前知識ゼロからだったので、まずはドットインストールで勉強しました。1時間くらい。
これだけでかなり概要がわかって、私が作りたいものはカンタンそうだったので、勉強とかいいからとりあえずすぐにつくろうと思って、ほかにはとくに勉強してないです。
公式サイトには、マニフェストの書き方とか、パーミッションの与え方とか、APIの使い方とかぜんぶここに書いてあるのですが、英語なので困ったら見る程度しか使ってない・・。
技術的なこと
jQuery使ってるし、とくに大したことは何もしてないのでアレですけど、次のようなことをしています。
パーミッションの設定
楽天の商品ページのURLはhttp://item.rakuten.co.jp/*
なのですが、送料ページはhttp://www.rakuten.co.jp/*
でドメインが異なるので、manifestにpermissionsの指定が必要でした。
"permissions": [ "http://www.rakuten.co.jp/*" ],
スクレイピング
送料部分のHTMLにクラス名なりID名がついてればカンタンだったのですが、案の定テーブルレイアウトでそんなものなかったので、下記のような感じで、見出しにあたる部分の文字列を拾ってきて、そこから次のhr
までの部分をとってくるみたいなことしてます・・。
var $data = $(data) .find('font:contains("配送について")') .parent() .nextUntil('hr');
ショップによって、なぜか微妙にこのへんが違う場合があって、たまにこれで取得できない場合もあります。その場合は、ボタン自体出ないようにしました。
われながらめちゃ便利だと思うので、ぜひ使ってみてください!
手っ取り早く Mac でローカルサーバーを立てる方法
ローカルサーバーを立てると、同じネットワークにつながってる別の端末から簡単にアクセスできるようになるから、スマホの表示確認とかに便利。でもいちいち httpd.conf とかいじるのはダルい。このやり方なら一瞬でできるからちょう楽ちん。
ターミナルでサーバー起動
サーバーのルートにしたいディレクトリまで cd
コマンドで移動したら、下記コマンドを実行。
$ python -m SimpleHTTPServer 8888
最後の数字はポート番号で、他に使われていなさそうな番号に適当に変えれば、複数のサーバーを同時に起動できる。
ブラウザで表示確認
localhost で確認
ブラウザのアドレスバーに http://localhost:8888/{ファイル名}
と入力して表示確認。
表示できてるなら、ローカルサーバーを立てるのが成功してる。表示できなかったら、ターミナルを見て、さっきのサーバー起動時になんかエラー出てないか確認してググろう。
IP アドレスで確認
localhost だと他の端末からアクセスできないので、IP アドレスを使ってアクセスします。
- ターミナルで
ifconfig
と打つか、ネットワーク環境設定の画面を見て、自分の IP アドレスを調べる。 - ブラウザのアドレスバーに
http://{IP アドレス}:8888/{ファイル名}
と入力して表示確認。
これで見れてれば、同じネットワークにいる別の端末から、IP アドレス経由でアクセスできます。
CTO に教えてもろた。