reveal.js の 右下コントロール・ボタン を 公式デモっぽくする

reveal.js を 使って、Markdown で プレゼンテーション・スライド が 作れる ようになりました.
Markdown で 書くので デザインや多様な表現はできないことは承知しているのですが、右下にあるコントロール・ボタンでデザインが気になります. というこうとで、公式のオンライン・デモのコントロール・ボタンっぽくしてみたいと思います.
※ 以降「公式のオンライン・デモ」を「公式デモ」と表記します (ソースに demo.html があるので、ここまではオンラインをつけてましたが長いので

作業環境

  • Windows 10 64bit
  • reveal.js 3.5.0

デザインの違い と 作りたいものの確認

現在の状況について確認します.

まずは、自分で作成したスライドです. 右下のコントロール・ボタンは [ ▶ ] の ような形です.

続いて、公式デモのスライドです. 右下のコントロール・ボタンは [ > ] の ような形です.

この [ ▶ ]を、公式デモの [ > ] に したいというものになります.
あとは、最初のスライドと、下のスライドがある際に、コントロール・ボタンが少し動きます. スライドに動きがあると気になってしまうというのもありますが、ナビゲーションの観点から動いてもいいのかなぁと思います.

今回は、以下の3つを作ります

  • 右下のコントロール・ボタンを [ > ] の デザインにする
  • 最初のスライドはコントロール・ボタンがわずかに動く
  • 下のスライドがある場合は、コントロール・ボタン の [ V ] と [ > ] を わずかに動く

公式デモ の デザイン を 持ってくる

コントロール・ボタンのデザインについて、実は公式デモと配布版では CSS が 異なっています.
特に CONTROLS 以下は完全に違う状態で、これがデザインの [ ▶ ] と [ > ] の 違いになっています.

ということで、公式デモの CONTROLS 部分 の CSS を、自分のスライドの HTML <link> タグの下へ コピーします.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
<style>
.reveal .controls {
display: none;
position: absolute;
top: auto;
bottom: 12px;
right: 12px;
left: auto;
z-index: 1;
color: #000;
pointer-events: none;
font-size: 10px; }
.reveal .controls button {
position: absolute;
padding: 0;
background-color: transparent;
border: 0;
outline: 0;
cursor: pointer;
color: currentColor;
-webkit-transform: scale(0.9999);
transform: scale(0.9999);
transition: color 0.2s ease, opacity 0.2s ease, -webkit-transform 0.2s ease;
transition: color 0.2s ease, opacity 0.2s ease, transform 0.2s ease;
z-index: 2;
pointer-events: auto;
font-size: inherit;
visibility: hidden;
opacity: 0;
-webkit-appearance: none;
-webkit-tap-highlight-color: transparent; }
.reveal .controls .controls-arrow:before,
.reveal .controls .controls-arrow:after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 2.6em;
height: 0.5em;
border-radius: 0.25em;
background-color: currentColor;
transition: all 0.15s ease, background-color 0.8s ease;
-webkit-transform-origin: 0.2em 50%;
transform-origin: 0.2em 50%;
will-change: transform; }
.reveal .controls .controls-arrow {
position: relative;
width: 3.6em;
height: 3.6em; }
.reveal .controls .controls-arrow:before {
-webkit-transform: translateX(0.5em) translateY(1.55em) rotate(45deg);
transform: translateX(0.5em) translateY(1.55em) rotate(45deg); }
.reveal .controls .controls-arrow:after {
-webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-45deg);
transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); }
.reveal .controls .controls-arrow:hover:before {
-webkit-transform: translateX(0.5em) translateY(1.55em) rotate(40deg);
transform: translateX(0.5em) translateY(1.55em) rotate(40deg); }
.reveal .controls .controls-arrow:hover:after {
-webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-40deg);
transform: translateX(0.5em) translateY(1.55em) rotate(-40deg); }
.reveal .controls .controls-arrow:active:before {
-webkit-transform: translateX(0.5em) translateY(1.55em) rotate(36deg);
transform: translateX(0.5em) translateY(1.55em) rotate(36deg); }
.reveal .controls .controls-arrow:active:after {
-webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-36deg);
transform: translateX(0.5em) translateY(1.55em) rotate(-36deg); }
.reveal .controls .navigate-left {
right: 6.4em;
bottom: 3.2em;
-webkit-transform: translateX(-10px);
transform: translateX(-10px); }
.reveal .controls .navigate-right {
right: 0;
bottom: 3.2em;
-webkit-transform: translateX(10px);
transform: translateX(10px); }
.reveal .controls .navigate-right .controls-arrow {
-webkit-transform: rotate(180deg);
transform: rotate(180deg); }
.reveal .controls .navigate-right.highlight {
-webkit-animation: bounce-right 2s 50 both ease-out;
animation: bounce-right 2s 50 both ease-out; }
.reveal .controls .navigate-up {
right: 3.2em;
bottom: 6.4em;
-webkit-transform: translateY(-10px);
transform: translateY(-10px); }
.reveal .controls .navigate-up .controls-arrow {
-webkit-transform: rotate(90deg);
transform: rotate(90deg); }
.reveal .controls .navigate-down {
right: 3.2em;
bottom: 0;
-webkit-transform: translateY(10px);
transform: translateY(10px); }
.reveal .controls .navigate-down .controls-arrow {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg); }
.reveal .controls .navigate-down.highlight {
-webkit-animation: bounce-down 2s 50 both ease-out;
animation: bounce-down 2s 50 both ease-out; }
.reveal .controls[data-controls-back-arrows="faded"] .navigate-left.enabled,
.reveal .controls[data-controls-back-arrows="faded"] .navigate-up.enabled {
opacity: 0.3; }
.reveal .controls[data-controls-back-arrows="faded"] .navigate-left.enabled:hover,
.reveal .controls[data-controls-back-arrows="faded"] .navigate-up.enabled:hover {
opacity: 1; }
.reveal .controls[data-controls-back-arrows="hidden"] .navigate-left.enabled,
.reveal .controls[data-controls-back-arrows="hidden"] .navigate-up.enabled {
opacity: 0;
visibility: hidden; }
.reveal .controls .enabled {
visibility: visible;
opacity: 0.9;
cursor: pointer;
-webkit-transform: none;
transform: none; }
.reveal .controls .enabled.fragmented {
opacity: 0.5; }
.reveal .controls .enabled:hover,
.reveal .controls .enabled.fragmented:hover {
opacity: 1; }
.reveal:not(.has-vertical-slides) .controls .navigate-left {
bottom: 1.4em;
right: 6.4em; }
.reveal:not(.has-vertical-slides) .controls .navigate-right {
bottom: 1.4em;
right: 1.4em; }
.reveal:not(.has-horizontal-slides) .controls .navigate-up {
right: 1.4em;
bottom: 6.4em; }
.reveal:not(.has-horizontal-slides) .controls .navigate-down {
right: 1.4em;
bottom: 1.4em; }
.reveal.has-dark-background .controls {
color: #fff; }
.reveal.has-light-background .controls {
color: #000; }
.reveal.no-hover .controls .controls-arrow:hover:before,
.reveal.no-hover .controls .controls-arrow:active:before {
-webkit-transform: translateX(0.5em) translateY(1.55em) rotate(45deg);
transform: translateX(0.5em) translateY(1.55em) rotate(45deg); }
.reveal.no-hover .controls .controls-arrow:hover:after,
.reveal.no-hover .controls .controls-arrow:active:after {
-webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-45deg);
transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); }
@media screen and (min-width: 500px) {
.reveal .controls[data-controls-layout="edges"] {
top: 0;
right: 0;
bottom: 0;
left: 0; }
.reveal .controls[data-controls-layout="edges"] .navigate-left,
.reveal .controls[data-controls-layout="edges"] .navigate-right,
.reveal .controls[data-controls-layout="edges"] .navigate-up,
.reveal .controls[data-controls-layout="edges"] .navigate-down {
bottom: auto;
right: auto; }
.reveal .controls[data-controls-layout="edges"] .navigate-left {
top: 50%;
left: 8px; }
.reveal .controls[data-controls-layout="edges"] .navigate-right {
top: 50%;
right: 8px; }
.reveal .controls[data-controls-layout="edges"] .navigate-up {
top: 8px;
left: 50%; }
.reveal .controls[data-controls-layout="edges"] .navigate-down {
bottom: 8px;
left: 50%; } }
</style>

スタイルを適用する要素を追加する

CSS を 持ってきただけでは、コントロール・ボタンは変化しません.
実は(実はが多い…)、コピーしたスタイル controls-arrow を 適用するためのタグが必要です. これも公式デモと JavaScript が 異なっている点になります.

さすがに reveal.js 本体を変更したくないですし、CDN からとってこれないのも困ります.
なのでイベント・フックを使って HTML が 生成された後に、必要となる要素を追加するようにしました.
以下のコードを HTML の <script> タグ にある、 Reveal.initialize({}); の 下に追加します.

1
2
3
4
5
6
7
8
9
10
11
<script>
Reveal.addEventListener('ready', (event) => {
document.querySelector('aside.controls').dataset.controlsBackArrows = 'faded';
Array.from(document.querySelectorAll('aside.controls > button'), e => {
let child = document.createElement('div');
child.setAttribute('class', 'controls-arrow');
e.appendChild(child);
e.style.color = 'rgb(66, 175, 250)';
});
});
</script>

reveal.js で コンテンツの準備ができた ready イベントで、以下の処理をしています.

querySelector('aside.controls') で、コントロール・ボタンを保持しているコンテナの要素取得します.
データセット controlsBackArrowsfaded を 設定し、戻るボタン(左と上) に ボタンが薄くなるようなエフェクトをつけます.

querySelectorAll('aside.controls > button') で、すべてのコントロール・ボタンを取得します.
各ボタンの子要素に <div class="controls-arrow"></div> タグをを追加、このタグが先の CSS の 適用先になります.
そして、ボタンのスタイルに rgb(66, 175, 250) で カラーを設定します. ボタンの青のカラーが、上記 CSS の コピーでは上手く持ってこれないので、スタイル属性で適用しました.

これで、コントロール・ボタンのスタイルが公式デモのように [ > ] に なりました!
が、よくよくスライドを進めていくと、下スライドがある時に配置がずれてる…

配置を直し、ボタンを小さくする

どうも、元の [ ▶ ] の時に配置された CSS が 残っているようで、コピーしただけでは上書きできなかったようです.
再度 HTML に <style> を 追加して大きさと配置を修正します.

1
2
3
4
5
6
7
<style>
.reveal .controls .controls-arrow:before, .reveal .controls .controls-arrow:after { width: 1.4em; }
.reveal .controls .navigate-left, .reveal:not(.has-vertical-slides) .controls .navigate-left { top: 60px; left: 40px; }
.reveal .controls .navigate-right, .reveal:not(.has-vertical-slides) .controls .navigate-right { top: 60px; left: 60px; }
.reveal .controls .navigate-up, .reveal:not(.has-vertical-slides) .controls .navigate-up { top: 50px; left: 50px; }
.reveal .controls .navigate-down, .reveal:not(.has-vertical-slides) .controls .navigate-down { top: 70px; left: 50px; }
</style>

.controls-arrow:after { width: 1.4em; } が ボタンの大きさになります.
その下の4つが、配置です. 大きさに合わせて配置を揃えていきます.

コンパクトになり、配置もそろいました!

最初のスライド と 下があるスライド の 場合は、少し動いてナビゲーションする

最初のスライドはよいとしても、メインのコンテンツ以外で動くものがあるのは、如何なものかとは思います. とはいえ縦方向にもスライドができるというは、あまりないのかなと思うと、ナビゲーションのために少し動いてもいいのかなと思い、公式デモから持ってきました.

まずは、少し動くアニメーションのスタイルです. 下矢印が動いて、戻るときに右矢印が動きます.
各%の数値を同じにすると、同時に動きます.

1
2
3
4
5
6
7
8
9
10
<style>
@keyframes bounce-right {
0%, 35%, 50%, 65%, 75% { transform: translateX(0); }
45% { transform: translateX(2px); }
55% { transform: translateX(-1px); } }
@keyframes bounce-down {
0%, 10%, 25%, 40%, 50% { transform: translateY(2px); }
20% { transform: translateY(2px); }
30% { transform: translateY(2px); } }
</style>

続いて、最初のスライド と 下があるスライド の 際にアニメーションさせる JavaScript を 作ります.
Reveal.addEventListener('ready', (event) => {}) は 上記ボタンの子要素を追加したときに記述した部分へ if(){} を 追記します.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
Reveal.addEventListener('ready', (event) => {
if (Reveal.availableRoutes().right) { document.querySelector('.navigate-right').classList.add('highlight'); }
if (Reveal.availableRoutes().down) { document.querySelector('.navigate-down').classList.add('highlight'); }
});
Reveal.addEventListener('slidechanged', (event) => {
Array.from(document.querySelectorAll('aside.controls > button.highlight'), e => { e.classList.remove('highlight') });
if (event.indexh === 0 && event.indexv ===0) {
document.querySelector('.navigate-right').classList.add('highlight');
}
if (Reveal.availableRoutes().down) {
document.querySelector('.navigate-down').classList.add('highlight');
if (Reveal.availableRoutes().right) {
document.querySelector('.navigate-right').classList.add('highlight');
}
}
});
</script>

Reveal.addEventListener('ready', (event) => {}); で reveal.js の 描画が終わった後に、右矢印 と 下矢印 の それぞれが有効な場合に highlight の CSS クラスを適用します. Reveal.availableRoutes() で 指定する方向のスライドがあるかを判定できます.

Reveal.addEventListener('slidechanged', (event) => {}); では、スライドが変更された際のイベントで処理ができます.
まずは highlight を すべて解除します.

続いて indexhindexv0 、つまり最初のスライドの場合に、右矢印に highlight を 設定.

そして、下方向がある場合に下矢印に highlight し、さらに右方向がある場合は右矢印にも highlight を 設定します.

これで、完成になります.
キャプチャは、動きが小さすぎて変化が分かるのが取れませんでした.


デザイン・センスとか、スキルがないのに、どうしてもスライドのデザインとか気になってしまうんですよね… 絵が描けたりする人がうらやましいです.
今回は公式デモを真似するので、CSS を 上手く当てはめることでできましたが、JS や CSS 本体にデモ用の改変が入っていたので、HTML 側だけで合わせるのが難しかったです. 上手くできてよかった.

reveal.js で プレゼンテーション・スライド を Markdown で 書く

プレゼンテーションや発表などで、お話をする際にはプレゼンテーション・スライドを用意するかと思います. 普段 PowerPoint を使って作るケースが多いのですが、最近はコーディングな仕事をしているので、せっかくだから Markdown で スライドづくりをしてみたいと思います.

作業環境

  • Windows 10 64bit
  • reveal.js 3.5.0
  • Node.js 8.4.0 64bit
  • Browsersync 2.18.13

reveal.js って?

論より何とかではないですが、まずは 公式デモ を ざっくり動かしてみるとよいと思います. プレゼンテーション・スライド風なページが表示されます. キーボードの [→] ボタンを押下ないし、右下のコントロールから [>] を クリックするとスライドして次のページが表示されます.

少し進んでいくと右下のコントロールに「v」が 表示され、下へもスライドできます.

なんと、これらを Markdown で 書くことができ、Web で 公開までもできるのが、reveal.js に なります.
基本的な機能どころか、発表でも十分使える機能があります. 以下に主な機能を抜粋します.

  • [ESC] で スライドマップ(slide overview) を 表示、縦横があるのでいいかも
  • [Alt + クリック] で ズーム、画像やコードを拡大したいときに便利です
  • FRAGMENTS スライド、コンテンツを順々に追加するようなスライドも作れます
  • スライド遷移のスタイル も [None - Fade - Slide - Convex - Concave - Zoom] から 選択可能
  • テーマも多数 [Black (default) - White - League - Sky - Beige - Simple - Serif - Blood - Night - Moon - Solarized]
  • 背景も画像やビデオにでき、別途遷移スタイルも適用できます
  • スライド作成に必要な、リスト表示やテーブル、引用、シンタックス・ハイライトされたコード表示、スライド間リンクなどもできます
  • 画像もちゃんと扱えます (が、レイアウトはできないので上から順に並べるだけ)
  • PDF 出力もできるので印刷して配布などもできます (Google Chrome の 印刷経由)
  • 発表者ツールがあるので、発表時も安心です

発表時は、[S] キー を 発表者ツールを表示し、スライド側をプロジェクタ等に移動して [F] で フルスクリーン表示します.

HTML ファイル の 作成

まずは、reveal.js を 使う HTML を 作成します. 大枠としては以下のような感じになります.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!doctype html>
<meta charset="utf-8">
<title>Presentation</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0/css/reveal.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0/css/theme/white.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0/lib/css/zenburn.min.css" />
<script>
let link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match(/print-pdf/gi) ?
'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0/css/print/pdf.min.css' :
'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0/css/print/paper.min.css';
document.getElementsByTagName('head')[0].appendChild(link);
</script>
<div class="reveal">
<div class="slides">
<section data-markdown="contents.md"
data-separator="^\n\n---$"
data-separator-vertical="^\n>>>$"
data-notes="^Note:"
data-charset="utf-8">
</section>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0/lib/js/head.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0/js/reveal.min.js"></script>
<script>
Reveal.initialize({
dependencies: [
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0/lib/js/classList.js', condition: function() { return !document.body.classList; }},
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0/plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); }},
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0/plugin/markdown/markdown.min.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0/plugin/notes/notes.min.js', async: true },
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0/plugin/zoom-js/zoom.min.js', async: true },
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.5.0/plugin/highlight/highlight.min.js', async: true, callback: function () { hljs.initHighlightingOnLoad(); }}
]
});
</script>

JavaScript や CSS は CDN の cdnjs.com さん から配信いただくようにしました.

テーマは 白ベースの white.min.css に しました. テーマは公式デモの THEMES で試すことができます.

<div class="slides"> 配下の <section> が Markdown ファイルの読み込みと、書式の定義になります.

  • data-markdown は、読み込む Markdown ファイル名です.
  • data-separator は、スライドの区切りを示す正規表現です. 今回は改行2つの後に --- で次のスライドになります.
  • data-separator-vertical は、垂直方向スライドの区切り正規表現です. 今回は改行1つの後に >>> にしました.
  • data-notes は、発表者ノートのパートを表す正規表現です. 行頭から Note: になります.
  • data-charset は、Markdown ファイルの文字セットです.

Markdown ファイル の 作成

<section data-markdown="contents.md"... > で 指定したファイルを作成します.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 最初のページ
---
## 2枚目のページ
- 起: 事実や出来事を述べる
- 承: 『起』で述べたことに関することを述べる
- 転: 『起承』とは関係のない別のことがらを持ち出す
- 結: 全体を関連づけてしめくくる
---
## 3枚目のページ
ここに本文を書いていく
Markdown を 使うことができる
>>>
### 3枚目のページ の 下
下のページ
左右の流れと、上下の流れ が作れる
---
## 4枚目のページ
Note: これは発表者ノート

表示 と 快適なエディティング の ために Browsersync

ここまででスライドづくりは完了ですが、外部ファイル の Markdown で スライド作成をすると、表示するには HTTP サーバー が 必要となります. HTML 内に書いてしまうという手もあるのですが、Markdown は 別にしておきたいので HTTP サーバー を 立てる方向で考えました.

Browsersync は ローカル の HTTP サーバーとして動作し、ファイルの変更を検出したらブラウザをリロードしてくれるツールになります.

1
2
3
4
5
6
7
8
9
10
11
12
c:\Develop\repos\slack-bot> npm install -g browser-sync
c:\Develop\repos\slack-bot> browser-sync start --server --files **/*
[Browsersync] Access URLs:
-----------------------------------
Local: http://localhost:3000
External: http://192.168.0.100:3000
-----------------------------------
UI: http://localhost:3001
UI External: http://192.168.0.100:3001
-----------------------------------
[Browsersync] Serving files from: ./
[Browsersync] Watching files...

Browsersync が 起動すると、ブラウザで自動的にページが表示されます.
スライド・マップを見ると単純な例ですが、ちゃんと上下のスライドも使えています.

パワポマン!

スライド作りとなると、どうしても欲しくなるのが パワポマン こと、パワーポイントの人型のアイコン、本投稿のアイキャッチにも使っているクリップアートさんたち. 以前はクリップアートの検索で、アバター とか Style 1541 などで検索できていましたが、いつの間にかなくなってしまったというのがありました.

それについては、こちら いつか消えた PPT の アバター とか、 Style 1541 とか に まとめましたので、よろしいかったら ご覧ください.

なお、クリップアート自体は マイクロソフト クリップアート 復刻 さん から入手できます.
パワポマンはいつもお世話になっております. 復刻してくださり ありがとうございます!


スライドづくりの環境準備ができました!
Markdown で 書くので、パワポに比べて表現力は落ちるでしょうが、しばらくはキレイで派手なスライドから離れるので、サクサク書ける Markdown のが嬉しいです. まずは、reveal.js で ドンドン書いてみよう♪

Hexo に 404 Not Found ページ を 追加する

しばらく前にブログのソース・ファイルの名前を変更したことがありました. その際に関連するリンクの修正を忘れていたものが多かったらしく、久しぶりに見た Google Search Console で 23件もの 404 Not Found、ページが見つからないというエラーが報告されていました. リンク切れを直すとともに、404 エラー が 発生した場合に表示されるページを GitHub Pages に なっていたものを、ブログ用のページに直しました. 404 ページを設置にあたっては、Hexo で Markdown を 書き、GitHub Pages に 設定します.

作業環境

  • Windows 10
  • Hexo 3.3
  • GitHub Pages

設定していない場合の状況

404 ページ を 設定していない場合は、存在しない URL へ アクセスすると以下のようなページが表示されます.
ウェブサイトを GitHub さん の GitHub Pages で ホストさせていただいているので、何もしなければ GitHub さん の 404 ページになります. 何を表示してよいのかわからないから、当然そうなりますね.

このページが表示されると、来ていただいた方にトップ・ページへさえも、戻ってもらうための手段が提供できないという問題があります.
内部のリンクをたどってきてくださった方は戻ることができますが、直接来た方は URL を 修正して、、、までは見ないですよね. (たぶん)

内部リンク切れは修正するとしても、404 が 表示される可能性は残ります.
その場合に備えて、404 ページ自体も自サイトのものにします.

GitHub Pages で 404 ページ を 設定

GitHub Pages で 404 ページを設定するには、サイトのソース・ルートに 404.html を 置きます.
下記例は Hexo を 使っていない場合に、単純な 404.html を 置いたものになります. ホームへのリンクなどを設置していない簡単なものですが、独自のページになっていることが分かります.

Hexo で 404 ページ を 設定

Hexo を 使って ウェブサイトを作っている場合は、Hexo の Markdown で 404 ページを作ります.
ソースルート直下の source ディレクトリに 404.md を 作ります.

1
2
3
4
5
6
7
---
layout: page
title: Page not found
comments: false
---
ページが見つかりませんでした.

アクセスすると以下のようなページが表示されます.
タイトールと、コンテンツがちゃんと表示されているのが分かります. そして カバーの画像 や カテゴリー など、サイトの構成もそのままなので、トップへ戻ったり、カテゴリーやタグを選びなおすなどの選択ができるようになります.

なお、 404.html を 直接置いた場合ですが、これは Hexo に 取り込まれコンテンツ部分に埋め込まれるので、完全に別ページを作るということはできなそうです.

日付とか、Share とか、関連記事とか、とか、

サイトの構成にうまく入り込んでくれたおかげで、すべてを作りこむ必要がなくなったのが助かります. 一方で 日付 や Share の リンク、関連記事 などが表示され、記事の1つのような感じが出ています. (※ 関連記事は、こちら Hexo に 関連する記事のリストを追加する で 追加した Plugin に なります)
これを非表示にするにはテンプレートを修正する必要があります.
今回はデフォルト・テンプレート Landscape をつかっているので、Landscape を カスタマイズしました.

修正するファイルは /themes/landscape/layout/_partial/article.ejs です.
以下の3ヵ所を <% if (post.path != "404.html"){ %> - <% } %> で 囲みます.

  • <div class="article-meta"> ブロック
  • <footer class="article-footer"> ブロック
  • <nav id="related-posts"...> ブロック

以下、 article.ejs 修正箇所 の 抜粋

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<article id="<%= post.layout %>-<%= post.slug %>" class="article article-type-<%= post.layout %>" itemscope itemprop="blogPost">
<% if (post.path != "404.html"){ %>
<div class="article-meta">
<%- partial('post/date', {class_name: 'article-date', date_format: null}) %>
<%- partial('post/category') %>
</div>
<% } %>
<div class="article-inner">
...(省略)
<% if (post.path != "404.html"){ %>
<footer class="article-footer">
<a data-url="<%- post.permalink %>" data-id="<%= post._id %>" class="article-share-link"><%= __('share') %></a>
<% if (post.comments && config.disqus_shortname){ %>
<a href="<%- post.permalink %>#disqus_thread" class="article-comment-link"><%= __('comment') %></a>
<% } %>
<%- partial('post/tag') %>
</footer>
<% } %>
...(省略)
</article>
<% if (post.path != "404.html"){ %>
<nav id="related-posts" class="article-inner" style="font-size: smaller">
<div class="article-entry">
<h2>関連記事</h2>
<%-
popular_posts({ PPMixingRate: 0.0 })
%>
</div>
</nav>
<% } %>
...(省略)

テンプレートを適用し、若干文章を直して、こんな感じになりました.


無事、404 エラー に対して コンテンツを返せるようになりました.
内部リンク切れで 404 は 出してはいけないものなので反省しつつ、何かの拍子で出てしまってもサイト内へとどまっていただけるようにできたかと思います.
いつかは、こちら デザインの参考にしたい!404(not found)ページ33選 で 紹介されるような、ステキな 404 ページを作ってみたいものです.

Raspberry Pi の CPU 温度 を 記録する

OSMC として使っている Raspberry Pi Zero が よくフリーズするようになり、ケースをさわってみると暖かいことから熱対策をすることにしました. そのための ヒートシンク付きケースを C4 Labs さん から購入 しました.
そのままヒートシンクを付けたいところ、ちょっと待って簡易的ではありますが Raspberry Pi の 温度を取得して変化を見てみたいと思います.

作業環境

  • Raspberry Pi Zero
  • OSMC rbp1
  • Crystal Signal Pi
  • Raspbian Jessie Lite

現在の温度を取得する

Raspberry Pi の 温度は /sys/class/thermal あたりに格納されています.
そこからファイルを参照することで取得できます. 温度は 1,000倍 になっています.

1
2
pi@raspberrypi:~ $ cat /sys/class/thermal/thermal_zone0/temp
34166

上記の場合は 34.166 ℃ ということになります.
切り捨てにはなりますが expr コマンドと組み合わせると、こんな感じにもできます.

1
2
pi@raspberrypi:~ $ expr `cat /sys/class/thermal/thermal_zone0/temp` / 1000
34

上記は OSMC で 使っている Raspberry Pi Zero で、Crystal Signal Pi の Raspberry Pi 3 は 57996 でした.

vcgencmd で 温度を取得する

また vcgencmd というコマンドを使っても温度を取得することができます.
こちらは記号も入っています. (2バイト文字がないから ‘C で ℃ を 表現するんですね)

1
2
$ vcgencmd measure_temp
temp=34.2'C

温度を定期的に取得して記録する

ローカルファイルに取得した温度を追記していき、どのように変化しているのかが分かるようにします.
ヒートシンクの有無で温度の変化が出るかを見たいだけなので、単純に cron で 上記コマンドを仕掛けておくようにします.

OSMC は cron が 入っていなかったので、インストールします.

1
2
pi@raspberrypi:~ $ sudo apt-get update
pi@raspberrypi:~ $ sudo apt-get install cron -y --no-install-recommends

10分に1回なので */10 * * * * で 指定し、日付 と 温度 を /tmp/thermal.txt に 追記します.
/tmp は リブートするとファイルが消えるので恒久的に取っておく場合は他のディレクトリにします.

1
2
pi@raspberrypi:~ $ crontab -e
*/10 * * * * /bin/echo -e "`date`\\t`cat /sys/class/thermal/thermal_zone0/temp`" >> /tmp/thermal.txt

こんな感じで出力されます. 特に処理していない状態で 10分間なので温度変化は無いようです.

1
2
3
pi@raspberrypi:~ $ cat /tmp/thermal.txt
Thu Sep 28 15:30:01 JST 2017 37932
Thu Sep 28 15:40:01 JST 2017 37932


10分間隔で取得するようにしたので、これで温度の変化を知ることができます. しばらく記録しておき、ヒートシンクを付けてから違いを確認したいと思います.
本来はちゃんとした監視ツールなどでデータ収集してグラフ化などしたいところですが、直近の問題と対策を検討するようなのでスクリプトで逃げました. ラズパイの台数が多いので、ちゃんと監視できるようにしたいところです. ラズパイの監視は何のツールがいいんだろう…

C4 Labs で Raspberry Pi Zero の ヒートシンク付きケース を 購入

最近 OSMC として使っている Raspberry Pi Zero が よくフリーズするようになってしまいました. この夏の暑さが原因かなと思われるので、ヒートシンク付きケースを購入し、様子見をすることにします. この辺の調査などは別途.
Raspberry Pi Zero の 大きさだと、ヒートシンクの効果が小さいとの話も聞きますが、他にも面白そうなプロダクトを作っているメーカーさんを見つけたので、思い切って購入しました. 購入方法は難しくは無いのですが、また買いたくなった時の記録として.

C4 Labs

今回購入したのは C4 Labs という メーカーさんで、アメリカのシアトル近郊にある会社のようです.
カタログ https://c4labs.net/collections/all を ざっと眺めていくと、Raspberry Pi や Arduino 関連だけではなく、サイコロ用のトレイや、ダイスタワー、RPG Game Box といったボードゲーム用のアクセサリーも作っているようです. デザインがカッコよく、どれこれも欲しい!

目的の品 ヒートシンク付きのケース

欲しいものの悩みは尽きないものですが、今回の目的の品であるヒートシンク付きケースはこちら Zebra Zero Heatsink Black Ice Case for Raspberry Pi Zero 1.3 and Zero Wireless.
Pimoroni さん の Pibow Zero Case を 黒のシックな感じにしてヒートシンクが付く感じになります.

ブレッドボード付きのケース

合わせて購入したのが Raspberry Pi Zero に ブレッドボードをセットにできるケース Zebra Zero Plus Breadboard in Wood for Raspberry Pi Zero 1.3 & Zero Wireless.
今回は内部が木製のデザインを選びましたが、上記ヒートシンク付きケースのようなデザインの Black Ice モデル も あります.

ブレッドボードを使うときに一緒のケースになっていると便利かなということで買ってみました.
実際に作ってみてから気になったのですが、Raspberry Pi を ケース内に入れてネジ止めしてしまうので、簡単に取り外せないので専用となってしまいます. Zero や Zero W なので 専用にしてしまってもよいかな…

Explorer HAT Pro とかの方が色々できるのかもしれませんが、ちゃんと使いこなせるレベルではないので、まずはブレッドボードをちゃんと扱えるようになってから手を出したいと思います.

カメラケース

なんかカッコよかったので手を出してしまいました (;´・ω・)
まだ使うあてを考えてないのですが、せっかくなので Raspberry Pi + Camera で 遊んでみようと思い買ってみました.
Cookie Wheel Camera Case for the Raspberry Pi Camera v1 and v2 (not included)

購入手順

購入する製品のページにある [ADD TO CART] の ボタンをクリックして、カートに追加します.

画面が左にスライドして、カードの状況を見ながらショッピングができます.
購入する製品を追加し終わったら、カートから [CHECK OUT] ボタンをクリックします.

チェックアウト画面が標示されるので、必要な情報を入力していきます.
入力できたら [Continue to shipping method] ボタンをクリックします.
※ 配送先の住所は画像サンプル用に東京駅を借りてます.

配送方法は [Invasion International Shipping] になります.
[Continue to payment] ボタンをクリックします.
$24.0、ちょっと高いっす. [FREE SHIPPING to ANYWAY in the USA!] なのに…

支払方法を選択します.
クレジットカードは海外のサイトなのに JCB にも対応しています. また PayPal、bitcoin なども使えます.
Amazon Pay は Amazon.com に つながりますが、Amazon.co.jp の アカウントも引っ張れたので使えるような感じでした. 今回は PayPal を 使ったので最後までコミットしなかったので、何とも言えませんが.
支払方法を確定したら [Complete order] ボタンをクリックして注文を確定します.
※ PayPal の 利用法については Raspberry Pi Zero の 購入 の 手順と同じでした

注文が確定され、送付先情報で入力したメールアドレスにメールが届きます. これで無事に注文が完了し、後は届くのを待つのみです. Google Maps で 届け先が表示されます. (※ サンプル用で東京駅を指してます)

約1週間ぐらいで届きました!


Raspberry Pi Zero の ヒートシンク付きケースとなると、なかなか無く、今回素敵なケースを購入することができてよかったです. 合わせて面白そうなプロダクトも手に入りました.
配送料がネックにはなりますが、日本にも届けてくれるサイトも増えてきたようで助かります.

GitHub Desktop v1.0.0 リリース & さっそく使う

GitHub 社から、公式のデスクトップ・クライアントのアプリ GitHub Desktop がリリースされました! Atom エディターなどで使われている Electron で 作られています. Electron アプリは開発側のメリットが大きく言われていますが、快適な操作性と、素敵なデザインのアプリが多く、Electron の 開発元でもある GitHub 社のアプリとなると楽しみます. さっそく使ってみます.

作業環境

  • Windows 10 64bit
  • GitHub Desktop 1.0.0
  • Git Hub

GitHub Desktop とは?

GitHub 社 が リリースした、公式の GitHub クライアント・アプリです. これまでβリリースされていましたが、 2017年9月19日に正式にバージョン 1.0.0 が リリースとなりました.
ベータ版では、残念なことに私の環境では動作しなかった(正確にはクローンとかができなかった) ので、正式リリースとのことで楽しみです.

GitHub クライアント・アプリ への 期待

GitHub Desktop が サポートしているかは試してみるとして、GitHub の クライアント・アプリといったときに、こんな機能が欲しいという期待があります. まぁ、特殊な使い方をしている部分もあるので、あったらラッキーぐらいでしょうし、なければ自分で作るべきなのでしょうが腕が追い付かず…

ともあれ、これから使わせていただく GitHub Desktop、こんなことできるかな?

  • Pull Request レビュー & マージ を 専用アプリで
    最近はレビューする機会が増え、Pull Request を 見ることが多くなりました.
    そうなると、Pull Request の 通知から始まり、やり取りの管理などが簡単に行るようになると助かります. ブラウザでも十分なエクスペリエンスを提供していただいていると思いますが、ブラウザで特定のページを固定的に扱うのが得意でなく、できれば専用アプリで使いたいというのがあります.

  • 複数アカウントをワンストップで
    そもそも複アカするな、という話もあると思います orz なぜこんなことになっているのか、自分でも困り果てているのですが 使わないといけない GitHub アカウントが多いのです…
    ブラウザだと Sign in して 2FA して、Sign out して次. かなり厳しいです. この辺が便利になってくれると嬉しいです.

そんな、超個人的な期待はよそに、インストールを進めます.

ダウンロード & インストール

GitHub Desktop の ウェブサイト https://desktop.github.com/ へ アクセスします.
環境に合わせてボタンが用意されているのでダウンロードします. 今回は [Download for Windows (64bit)] を 選択しました.

ダウンロードされた [GitHubDesktopSetup.exe] を ダブルクリックします. チェックサムは見つかりませんでした. 確認したいなぁ…

インストール先やオプションを聞かれることなく、インストールが実行されます. しばし待ちます.

Welcome が 表示されます. 今回は [GitHub.com] へ サインアップしました. GitHub Enterprise にも対応しているようです.

GitHub の Username(or Email) と Password を 入力して [Sign in] を クリックします. 2FA を 有効にしている場合でもパスワードで大丈夫です. 次でコードを聞かれます.

2FA を 有効にしているので、コードを聞かれました. コードを入力して [Verify] します. (2FA を 有効にしていない場合は表示されません)

続いて Git の Name と Email を 聞かれます. GitHub で 設定した Username と Email を 入力します. 合っていないと下記画像のように自分の相子ではなく Hubot アイコンが表示されるようです. (Web の GitHub 上でもアイコンが異なり困るので、ちゃんと合わせるようにします)

最後に改善のための匿名レポートを送るかを選択し、[Finish] ボタンをクリックします.

インストールは、ここまでとなります.
引き続きメイン画面が標示されるので、作業を進めます.

リポジトリのクローンから、プッシュまで

無事、GitHub Desktop の 画面が標示されました.
まだリポジトリが追加されていないので、追加します. 今回は初回ということで一番右のクローンから始めました.

すでにログインしているので、自分のアカウントに関連するリポジトリが表示されます. クローンするリポジトリを選択し [Clone] ボタンをクリックします. 今回は、このブログのソースを選択しました.

しばらく待つとクローンされ、画面左上に現在選択しているリポジトリが表示されます. なお、新規クローンなのでローカルに変更はないのでリポジトリ名以外、操作を必要とするような変化はありません.

リポジトリの編集をします. これは GitHub Desktop の スコープではなく、メニュー の [Repository] から [Open in XXXX] を 選択します. 今回の環境は Visual Studio Code が 該当するため Visual Studio Code に なっています. Atom が 入っている場合は Atom が 表示されるでしょう. (設定変更は後述)

ソース編集した Visual Studio Code は、こちらの記事のスコープ外なので編集できたとして、GitHub Desktop に 戻ると Git の 更新が表示されます. (表示されない場合は右上の [Fetch origin] を クリックします)
ここでは、本記事の前の Raspberry Pi 基盤 の LED を 消灯する の 変更がリストされています.
左下のフォームで、コミットのサマリと説明を入力し、[Commit to source] ボタンをクリックするとコミットできます.
※ このリポジトリのデフォルト・ブランチが source なので [Commit to source] ですが、変更していない場合は [Commit to master] です.

コミットされると何もないような画面に戻りますが、画面右上の [Fetch origin] が [Push origin] に なっています. ここをクリックすることで GitHub へ Push できます.

エディタを変更する

今回は Visual Studio Code しか入っていない環境だったため、エディタの選択が Visual Studio Code でしたが Atom も 入っている場合や、Shell 設定を変更したい場合があります.
メニューの [File] から [Options…] を クリックします.
Options ダイアログ の [Advanced] タブ から、変更できます.

GitHub への Sign in アカウントの変更や、Git の Name/Email の 変更もこちからできます. 左下に Hubot アイコンが出ている場合は、こちらから Git の 設定を直します.


以上。

なんか、クローンしてプッシュして終わりな感じですが、v1.0.0 では ここまでのようです. ブランチを作ったり切り替えたりできますが、Issues や Pull Request などは これからのようです.

まずは基本機能から. これからを楽しみにしたいと思います!

Raspberry Pi 基盤 の LED を 消灯する

Raspberry Pi の 基盤には ACT と PWR の LED があります. 普段から Crystal Signal Pi の 四角柱を光らせていますが、壁際においているため PWR の 赤 LED が 反射し色が混ざるのと、夜間消灯中にも壁に反射する赤色が気になるので、基盤 の LED を 消灯したいと思います.

作業環境

  • Raspberry Pi 3 Model B
  • Crystal Signal Pi
  • Raspbian Jessie Lite

現在の状況

まずは、現在の状況.

日中 の Crystal Signal Pi 点灯中でも、白い壁に反射する 赤 LED が ちょっと気になります…

夜間消灯中に輝く PWR の 赤 LED. 白い壁に反射して赤に染まっています. これだけの光量だと離れていても、棚の一角が かなり赤く光ります.

コマンドラインから 一時的 に 基盤 の LED を 消灯する

Raspberry Pi というか Raspbian Jessie で 基盤 の LED を 操作するには、 /sys/class/leds/ にある led0led1 ディレクトリのファイルを操作します. led0 が ACT で led1 が PWR です.

操作するファイルは 2つで、 triggerbrightness です. trigger は LED を 光らせるトリガーで、 brightness は LED の 明るさです.

まず、現在の設定を確認します.
led0 ACT は mmc0255. つまり SD カード の 読み書きをトリガーとして最大光量で点灯します.
led0 PWR は input255. つまり電源の有無をトリガーとして最大光量で点灯します.

1
2
3
4
5
6
7
8
9
10
11
12
pi@raspberrypi:~ $ cat /sys/class/leds/led0/trigger
none kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock timer oneshot heartbeat backlight gpio cpu0 cpu1 cpu2 cpu3 default-on input panic [mmc0] mmc1 rfkill0 rfkill1
pi@raspberrypi:~ $ echo 0 | sudo tee /sys/class/leds/led0/brightness
255
pi@raspberrypi:~ $ cat /sys/class/leds/led1/trigger
none kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock timer oneshot heartbeat backlight gpio cpu0 cpu1 cpu2 cpu3 default-on [input] panic mmc0 mmc1 rfkill0 rfkill1
pi@raspberrypi:~ $ echo 0 | sudo tee /sys/class/leds/led1/brightness
255

では、これらに設定をして消灯します.
triggernone にすることで、LED を 点灯するアクションを起こさせなくします. 続いて、brightness0 にして消灯します.
led0 ACT は 点滅なので trigger を 止めれば消えている状態になるはずですが、SD カードへアクセス中の瞬間にあたると点灯したままになるので、その場合は brightness0 にして消灯します. (イベントからのトリガーが止まるだけで、明るさとは関係ないということですね)

1
2
3
4
5
6
7
pi@raspberrypi:~ $ echo none | sudo tee /sys/class/leds/led0/trigger
none
pi@raspberrypi:~ $ echo none | sudo tee /sys/class/leds/led1/trigger
none
pi@raspberrypi:~ $ echo 0 | sudo tee /sys/class/leds/led1/brightness
0

こちらは現在の状態を変更するものなので、設定したとおりに LED が すぐに消灯します. これで光が混ざらなくなって気にならないし、夜間消灯も完全に消えてくれます.

なお brightness の 数値で明るさの変化は見られませんでした. 0 1 で ON/OFF ぐらいのもののようです. 初期値 と max_brightness の 値が 255 なので変化できそうなのに…

恒久対策

上記設定方法は現在の設定変更で使えるものになります. そのためリブートすると元の状態に戻ります.
恒久的に設定するには /boot/config.txt に 設定を記述します.
設定の詳細については /boot/overlays/README ファイル、もしくは最新になりますが raspberrypi/firmware の firmware/boot/overlays/README を ご確認ください.

1
2
pi@raspberrypi:~ $ echo "dtparam=act_led_trigger=none,act_led_activelow=on" | sudo tee -a /boot/config.txt
pi@raspberrypi:~ $ echo "dtparam=pwr_led_trigger=none,pwr_led_activelow=on" | sudo tee -a /boot/config.txt

Raspberry Pi Zero の 場合

Raspberry Pi Zero は PWR LED しかないので、コマンドラインからは led0 で 設定します.
/boot/config.txtact_led_~ なので変わらずです.


Raspberry Pi の 基盤には PWR の 赤 LED のほかに、ACT の 緑 もありますが、緑は点灯では無く点滅であり、強く光り続けないので今回は PWR の 赤だけを消灯することにして様子見しようと思ったら、やはり気になるので一緒に消しました. 意外と光るものなんですね. (;^_^A

TypeScript の tslint.json を 考える

TSLint について調べた ので、実際の設定を考えます.

作業環境

  • Windows 10 64bit
  • Node.js 8.4.0 64bit
  • TypeScript 2.4
  • TSLint 5.6.0

設定の方針検討

TypeScript は 学習中のため、設定の良し悪しが分からないところがありますが、まずは厳しくシメテおくのが良いと考えます.
この手のチェックは、後から緩めることは簡単ですが、後から厳しくすると指摘の嵐に見舞われ結局使わないといったことにもなりかねません. まずは厳しく、どうしても指摘事項に対する代替策が無かった場合に緩めるといった運用にします.

設定しなかったもの

以下の 2つは設定しませんでした. 今のところ禁止するものが 無い and/or 想像がつかない ので、設定するべき値がないためのとなります. (デプリケートではないもので、禁止した方が良いものってあるのかなぁ)

  • ban-types: 特定の型を使用禁止にする
  • ban: 特定の関数やグローバル・メソッドを禁止する

今回の設定値

以下にような設定 tslint.json を 作りました. (行数削減のため一部整形済み)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
{
"rules": {
"adjacent-overload-signatures": true,
"member-access": true,
"member-ordering": [ true, { "order": [ "static-field", "instance-field", "constructor" ]}],
"no-any": true,
"no-empty-interface": true,
"no-import-side-effect": true,
"no-inferrable-types": true,
"no-internal-module": true,
"no-magic-numbers": true,
"no-namespace": true,
"no-non-null-assertion": true,
"no-reference": true,
"no-unnecessary-type-assertion": true,
"no-var-requires": true,
"only-arrow-functions": [ true ],
"prefer-for-of": true,
"promise-function-async": true,
"typedef": [
true,
"call-signature",
"arrow-call-signature",
"parameter",
"arrow-parameter",
"property-declaration",
"variable-declaration",
"member-variable-declaration",
"object-destructuring",
"array-destructuring"
],
"typedef-whitespace": [
true,
{ "call-signature": "nospace", "index-signature": "nospace", "parameter": "nospace", "property-declaration": "nospace", "variable-declaration": "nospace" },
{ "call-signature": "onespace", "index-signature": "onespace", "parameter": "onespace", "property-declaration": "onespace", "variable-declaration": "onespace" }
],
"unified-signatures": true,
"await-promise": true,
"curly": [ true, "ignore-same-line" ],
"forin": true,
"import-blacklist": true,
"label-position": true,
"no-arg": true,
"no-bitwise": true,
"no-conditional-assignment": true,
"no-console": [ "debug", "error", "info", "trace", "warn" ],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-duplicate-variable": [ true, "check-parameters" ],
"no-empty": true,
"no-eval": true,
"no-floating-promises": true,
"no-for-in-array": true,
"no-inferred-empty-object-type": true,
"no-invalid-template-strings": true,
"no-invalid-this": [ true, "check-function-in-method" ],
"no-misused-new": true,
"no-null-keyword": true,
"no-object-literal-type-assertion": true,
"no-shadowed-variable": true,
"no-sparse-arrays": true,
"no-string-literal": true,
"no-string-throw": true,
"no-submodule-imports": true,
"no-switch-case-fall-through": true,
"no-this-assignment": true,
"no-unbound-method": true,
"no-unsafe-any": true,
"no-unsafe-finally": true,
"no-unused-expression": true,
"no-unused-variable": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"no-void-expression": [ true, "ignore-arrow-function-shorthand" ],
"prefer-conditional-expression": [ true, "check-else-if" ],
"prefer-object-spread": true,
"radix": true,
"restrict-plus-operands": true,
"strict-boolean-expressions": true,
"strict-type-predicates": true,
"switch-default": true,
"triple-equals": true,
"typeof-compare": true,
"use-default-type-parameter": true,
"use-isnan": true,
"cyclomatic-complexity": [ true, 6 ],
"deprecation": true,
"eofline": true,
"indent": [ true, "spaces", 4 ],
"linebreak-style": [ true, "LF" ],
"max-classes-per-file": [ true, 1 ],
"max-file-line-count": [ true, 300 ],
"max-line-length": [ true, 120 ],
"no-default-export": true,
"no-duplicate-imports": true,
"no-mergeable-namespace": true,
"no-require-imports": true,
"object-literal-sort-keys": true,
"prefer-const": true,
"trailing-comma": [ true, { "multiline": "never", "singleline": "never" }],
"align": [ true, "parameters", "arguments", "statements", "members", "elements" ],
"array-type": [ true, "array" ],
"arrow-parens": [ true, "ban-single-arg-parens" ],
"arrow-return-shorthand": [ true, "multiline" ],
"binary-expression-operand-order": true,
"callable-types": true,
"class-name": true,
"comment-format": [ true, "check-space", "check-uppercase" ],
"completed-docs": [ true ],
"encoding": true,
"file-header": [ true, "Copyright \\d{4}" ],
"import-spacing": true,
"interface-name": [ true, "never-prefix" ],
"interface-over-type-literal": true,
"jsdoc-format": true,
"match-default-export-name": true,
"newline-before-return": true,
"new-parens": true,
"no-angle-bracket-type-assertion": true,
"no-boolean-literal-compare": true,
"no-consecutive-blank-lines": [ true, 2 ],
"no-irregular-whitespace": true,
"no-parameter-properties": true,
"no-reference-import": true,
"no-trailing-whitespace": true,
"no-unnecessary-callback-wrapper": true,
"no-unnecessary-initializer": true,
"no-unnecessary-qualifier": true,
"number-literal-format": true,
"object-literal-key-quotes": [ true, "always" ],
"object-literal-shorthand": true,
"one-line": [ true, "check-catch", "check-finally", "check-else", "check-open-brace", "check-whitespace" ],
"one-variable-per-declaration": [ true, "ignore-for-loop" ],
"ordered-imports": true,
"prefer-function-over-method": true,
"prefer-method-signature": true,
"prefer-switch": true,
"prefer-template": [ true, "allow-single-concat" ],
"quotemark": [ true, "single", "avoid-template", "avoid-escape" ],
"return-undefined": true,
"semicolon": [ true, "always" ],
"space-before-function-paren": [ true, "never" ],
"space-within-parens": 0,
"switch-final-break": [ true, "always" ],
"type-literal-delimiter": true,
"variable-name": [ true, "check-format", "ban-keywords" ],
"whitespace": [ true, "check-branch", "check-decl", "check-operator", "check-module", "check-separator", "check-type", "check-typecast", "check-preblock" ]
},
"defaultSeverity": "error"
}

とりあえず、 Hello World に TSLint かけてみる

Visual Studio Code で Hello TypeScript! で作ったコードに TSLint を かけてみます.
作成したコードは以下になります. すでに引っかかることが目に見えていますが、ただの実験コードなので気にせずかけます.

1
2
3
4
5
6
7
8
class Startup {
public static main(): number {
console.log('Hello TypeScript!');
return 0;
}
}
Startup.main();

コマンドラインで実行するには、 tslint -ptsconfig.json を 指定します.
今回は compilerOptionsstrictNullChecks: true が 無いので警告されています. また案の定 JSDoc などのドキュメンテーションが引っかかりました.
Missing blank line before return も 出ているので、コードのチェックもしてくれていることが分かります.

1
2
3
4
5
6
7
PS C:\Develop\workspace\hello-typescript> tslint -p .\tslint.json
strict-type-predicates does not work without --strictNullChecks
ERROR: C:/Develop/workspace/hello-typescript/src/hello.ts[1, 1]: Documentation must exist for classes.
ERROR: C:/Develop/workspace/hello-typescript/src/hello.ts[2, 5]: Documentation must exist for public,static methods.
ERROR: C:/Develop/workspace/hello-typescript/src/hello.ts[1, 1]: missing file header
ERROR: C:/Develop/workspace/hello-typescript/src/hello.ts[4, 9]: Missing blank line before return

Visual Studio Code では、以下のようにチェック結果がエディタに範囲されています.
Documentation の チェックが反映されていないようですね…


一通りチェックするような設定ができました!
これで、良いプラクティスを反映した文法でコーディングできるので、作るべきことに集中できますし、レビューの際もロジックなどに見るべき場所に注力できますね.

Visual Studio Code で チェックできてないものがありそうなのは、参照しているスキーマが、公式サイトのサンプルと若干ずれているようで、 tslint.json にも警告が出ていました.
後ほど詳しく調べるとして、とりあえず CI と 組み合わせて、コマンドライン実行の Lint も かけることで、ダブルチェックで逃げることにします.

あとは感覚的にしっくりくる設定なのかガッツりコーディングしてブラッシュアップ、いざっコーディング!

TypeScript の tslint.json を 調べる

TypeScript の tsconfig.json が とりあえずできました. 次は静的解析ツールの TSLint を 設定する tslint.json の 設定内容について検討します.

作業環境

  • Windows 10 64bit
  • Node.js 8.4.0 64bit
  • TypeScript 2.4
  • TSLint 5.6.0

TSLint とは?

TSLint は プログラムを動作させずに解析し問題になりそうなコードや規約違反などをチェックする TypeScript 向けの静的解析ツールです. JavaScript では ESLint、Java では SpotBugs(FindBugs) & Checkstyle などが同種のツールにあたります.

設定項目の確認

TSLint core rules を 確認し、ざっと理解のためのメモを作りました.
英語ができないのと、TypeScript/TSLint に 詳しくない状態で書いているので、間違えや勘違いがあるかもしれないでご注意ください…
英文のままになっているところは、よくわからなかった and/or 日本語で表現できなかった ので、とりあえず そのまま転記しました. いつの日かアップデートできるように頑張りたい.

TypeScript-specific

ルール 概要
adjacent-overload-signatures 関数のオーバーロードは連続して記述し、可読性を向上させる
ban-types 特定の型を使用禁止にする
member-access クラス・メンバーの可視性宣言を強制させる
member-ordering クラス・メンバーの順序付けを強制し、可読性を向上させる
no-any any の 型宣言を使用禁止にする
no-empty-interface 空のインターフェースを禁止する
no-import-side-effect 副作用を伴う import を 禁止する
no-inferrable-types number string boolean の 初期化済みの型宣言を禁止し冗長なコードを排除させる
no-internal-module 内部 module を 禁止し、新しい namespace キーワードの利用を使用させる
no-magic-numbers マジックナンバーを禁止する (ただしデフォルトでは -1 0 1 は 許可)
no-namespace 内部 modulenamespace を 禁止し、 ES6-style の import/export を使用させる
no-non-null-assertion non-null アサーションを禁止し、strict null checking mode を 活用させる
no-reference /// <reference path=> を 禁止し、ES6-style の import/export を使用させる
no-unnecessary-type-assertion 型アサーションが、式の型を変更しない場合に警告する
no-var-requires import 以外の require を 禁止し、ES6-style の import/export を使用させる
only-arrow-functions Arrow-style 以外の function 関数式を禁止し、予期しない this アクセスを防止する
prefer-for-of 配列のインデックスにアクセスしないループの場合は、 for-of を 使用させる
promise-function-async Promise を 返す関数またはメソッドは async の マークを必須とする
typedef 型定義を必須とする
typedef-whitespace 型指定子(コロン) の 前後のスペース有無をチェックします
unified-signatures union または optional/rest で1つに統合できるオーバーロードを警告する

Functionality

ルール 概要
await-promise Promise ではない awaited な 値を警告する
ban 特定の関数やグローバル・メソッドを禁止する
curly if/for/do/while で 中括弧を必須とする
forin for-inif フィルターを必須とし、継承プロパティへの偶発アクセスを防止する
import-blacklist 直接 import require せず、サブモジュール・ロードにして不要なロードを避ける
label-position ラベルの使用を do/for/while/switch に限定し、コードの構造化を強化する
no-arg arguments.callee を 禁止し、パフォーマンスの最適化を行えるようにする
no-bitwise ビット演算子を禁止し、保守性を向上させる
no-conditional-assignment do-while/for/if/while での条件文における代入を禁止する
no-console 指定するコンソール・メソッド (e.g. log error) の 仕様を禁止する
no-construct String Number Boolean の コンストラクタ利用を禁止し、関数を利用させる
no-debugger debugger ステートメントを禁止する
no-duplicate-super コンストラクタでの super() 二重呼び出しを警告する
no-duplicate-variable 同一スコープでの var 重複宣言を禁止する
no-empty 空ブロックを禁止する
no-eval eval を 禁止し、危険なコードを防止させる
no-floating-promises 関数から返された Promise の ハンドルを厳格にさせる
no-for-in-array 配列の for-in ループを禁止し for-of を 利用させる
no-inferred-empty-object-type 関数とコンストラクタの呼出し側で、 {} (空オブジェクト型)による型推論を禁止する
no-invalid-template-strings テンプレート文字列以外 の ${ を 警告する
no-invalid-this クラス外での this キーワードの使用を禁止する
no-misused-new Warns on apparent attempts to define constructors for interfaces or new for classes.
no-null-keyword null の 使用を禁止し、 undefined に 統一させる
no-object-literal-type-assertion インターフェースや
no-shadowed-variable 変数のシャドーイングを禁止する
no-sparse-arrays 配列リテラルの欠落要素(重複カンマ)を禁止しする
no-string-literal 不要な文字列リテラルのプロパティアクセスを禁止し obj.property 書式を使用させる
no-string-throw プレーン・テキストの throw を 禁止し、 Error の スローを使用させる
no-submodule-imports サブモジュールのインポートを禁止し、最上位パッケージのエクスポートを使用させる
no-switch-case-fall-through switch の ケース・フォールスルーをきん資する
no-this-assignment 不要な this 参照を禁止し、Arrow-style ラムダを使用させる
no-unbound-method メソッドがメソッド呼び出しの外で使用されることを警告する
no-unsafe-any 動的な方法の any 型 利用を警告する
no-unsafe-finally finally ブロックでの return continue break throws の 使用を禁止する
no-unused-expression 未使用の式ステートメントを禁止する
no-unused-variable 未使用 の インポート、変数、関数、プライベート・クラスのメンバー を 禁止する
no-use-before-declare 変数の宣言前利用を禁止する
no-var-keyword var を 禁止し、 letconst を 使用させる
no-void-expression Requires expressions of type void to appear in statement position.
prefer-conditional-expression Recommends to use a conditional expression instead of assigning to the same thing in each branch of an if statement.
prefer-object-spread Enforces the use of the ES2015 object spread operator over Object.assign() where appropriate.
radix parseInt を 使う場合に、 radix パラメータの指定を必須とする
restrict-plus-operands 変数の加算は双方の型が同じ(number + string は禁止) であることを強制する
strict-boolean-expressions ブール式で許可する型を制限する (デフォルトは boolean のみ)
strict-type-predicates 常に true もしくは false となる型述語を警告する
switch-default switch 文は、 default ケース の 実装を必須とする
triple-equals ==!= を 禁止し、 ===!== を 使用させる
typeof-compare Makes sure result of typeof is compared to correct string values.
use-default-type-parameter 明示的に指定された型引数が、その型パラメータのデフォルトである場合に警告する
use-isnan NaN 定数の比較を禁止し、 isNaN() 関数を使用させる

Maintainability

ルール 概要
cyclomatic-complexity サイクロマティック複雑度の分析と閾値を適用する
deprecation デプリケート API の 使用を警告する
eofline ファイルが改行で終わるようにする
indent 指定するインデントルールを適用する
linebreak-style 指定する改行コードを適用する
max-classes-per-file ファイル内のクラス数を制限する
max-file-line-count ファイルの行数を制限する
max-line-length 1行の文字数を制限する
no-default-export ES6-style の デフォルト・エクスポートを禁止し、名前付きエクスポートを使用させる
no-duplicate-imports 同じモジュールからの複数インポートを禁止する
no-mergeable-namespace 同じファイル内のマージ可能な namespace を 禁止する
no-require-imports require() を 禁止し、新しい ES6-style import/export を 使用させる
object-literal-sort-keys オブジェクト・リテラルのキーは、アルファベット順に記述させる
prefer-const 変数への割り当てが1回に限られる場合、 const を 使用させる
trailing-comma 配列とオブジェクトのリテラルで最後にカンマを付けるかのルールを強制する

Style

ルール 概要
align 指定する要素 (e.g. 引数) を 垂直方向を揃えて整列して記述させる
array-type 配列宣言を T[] もしくは Array の いずれかに統一させる
arrow-parens Arrow-style 関数 の パラメーターで括弧を必須とする
arrow-return-shorthand () => { return x; }() => x と させる
binary-expression-operand-order In a binary expression, a literal should always be on the right-hand side if possible. For example, prefer ‘x + 1’ over ‘1 + x’.
callable-types コールシグネチャのみのインターフェース名やリテラル型は関数型を使用させる
class-name クラスとインターフェース名はパスカルケース(アッパーキャメルケース)にさせる
comment-format 1行コメントの書式設定ルールを強制する
completed-docs Enforces documentation for important items be filled out.
encoding UTF-8 エンコーディングを強制する
file-header すべてのファイルに対して指定する正規表現にマッチするヘッダーコメントを強制する
import-spacing import の キーワード間にスペースを入れることを強制する
interface-name × インターフェース名は I で 始まることを強制する
interface-over-type-literal 型リテラル( type T = {...} ) ではなく インターフェース名を使用させる
jsdoc-format JSDoc の 基本形式ルールを強制する
match-default-export-name デフォルト・インポートには、インポートする宣言と同じ名前の使用を必須とする
newline-before-return ブロック内に return 以外の行がある場合、 return の 前に空行を必須とする
new-parens new キーワードを使用してコンストラクタを呼び出す場合、括弧を必須とする
no-angle-bracket-type-assertion 型アサーションに <Type> の 代わりに as Type を 使用させる
no-boolean-literal-compare x === true のような、ブール・リテラルとの比較を警告する
no-consecutive-blank-lines 1もしくは複数の空行を禁止する (デフォルトで 1つの空行を許可する)
no-irregular-whitespace 文字列やコメントの外側にある不規則な空白を禁止する
no-parameter-properties クラス・コンストラクタでのパラメーター・プロパティを禁止する
no-reference-import <reference types="foo" /> の 使用を禁止する
no-trailing-whitespace 行末尾の空白を禁止する
no-unnecessary-callback-wrapper Replaces x => f(x) with just f. To catch more cases, enable only-arrow-functions and arrow-return-shorthand too.
no-unnecessary-initializer var let destructuring initializerundefined 初期化を禁止する
no-unnecessary-qualifier Warns when a namespace qualifier (A.x) is unnecessary.
number-literal-format 小数点以下のリテラルは 0. で 始まり、末尾が 0 で 終わらないこと強制する
object-literal-key-quotes Enforces consistent object literal property quote style.
object-literal-shorthand 可能であれば ES6-style オブジェクト・リテラル の ショートハンドを強制する
one-line 特定の予約語 (e.g. try}catch) を同一行にさせるかのルールを強制する
one-variable-per-declaration 複数の変数を同時定義することを禁止する
ordered-imports import を アルファベット順に記述することを強制する
prefer-function-over-method Warns for class methods that do not use ‘this’.
prefer-method-signature インターフェースと型の定義を foo: () => void でなく foo(): void を 使用させる
prefer-switch Prefer a switch statement to an if statement with simple === comparisons.
prefer-template 文字列の連結ではなく、テンプレート式を使用させる
quotemark 文字列リテラルを、一重引用符 または 二重引用符 の 指定する方に強制する
return-undefined Void 関数 では return; を、値を返す関数では return undefined; を 使用させる
semicolon すべてのステートメントの最後に一貫したセミコロンの使用を強制する
space-before-function-paren 関数の括弧前に入れるスペースのルールを強制する
space-within-parens 括弧内のスペースのルールを強制する
switch-final-break switch の 最終節 が break で 終わるかのルールを強制する
type-literal-delimiter Checks that type literal members are separated by semicolons. Enforces a trailing semicolon for multiline type literals.
variable-name 変数名のルールを強制する
whitespace 空白スタイルのルールを強制する

TypeScript の コーディング量が足りていないので、どのようなコードを指しているのか想像がつかないものもかなりありました.
あと cyclomatic-complexity が あるのがいいですね.
先に設定を悩むより、圧倒的なコーディング量を背景に、よろしくないコードを防ぐことをした方がよかったかもと思いつつ、理解が深まったのでよしとしましょう. 次は、設定を考えたいと思います.

Git の 全体 gitignore を 再設定する

Visual Studio Code で 使うために PortableGit を インストールしました. その際に 全体 gitignore を 設定したのですが、よくよく調べると、もっとよい設定方法がったので再設定します.

作業環境

  • Windows 10 64bit
  • PortableGit 64bit
  • Git Hub

github/gitignore リポジトリ

GitHub 社 が 公開している github/gitignore というリポジトリ https://github.com/github/gitignore というのがあります.
様々なプログラム言語やフレームワークなどに応じた gitignore の テンプレートを提供してくれています. GitHub.com で リポジトリを作成する際や、作成した後から Web UI で gitignore を 追加する機能がありますが、その gitignore ファイルは このリポジトリから作られているとのことです.

アプリケーションを作る際などに、いつも参照させていただくのですが、よくよく見ていると Global というディレクトリがあり、こちらは OS や エディタなどのテンプレートが入っているとのことです. 知らなかった…

ちゃんと説明 Globally Useful gitignores - gitignore/Global at master · github/gitignore を 読まないとですね. ということで、こちらをもとに グローバル の gitignore を 再設定 します.

まず Windows.gitignore を 参照してみます. 2017年9月現在、以下の内容でした. 前回設定した Thumbs.db だけとは大違いですね. 勉強になります.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk

Visual Studio Code の 設定もありました. .vscode/ 以下の Visual Studio Code 設定ファイル以外を ignore ですが、これでいいのかな?ちょっと意図を知りたいかも.

1
2
3
4
5
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

Eclipse の 設定もあります. Eclipse で 関しているわけではないのですが、Eclipse エディタで編集したい部分があるなどで、Eclipse プロジェクトにしている場合があります. その場合は .project.settings などの Eclipse 関連ファイルはコミットしたくないので、グローバル gitignore するのもよさそうです.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet

curl コマンド で グローバル gitignore を 作る

利用する gitignore を 決めたら、グローバル gitignore を 作ります.
Powershell 3.0 から curl こと Invoke-WebRequest が 使えます. これでダウンロードすれば OK! (ただし使い方は Linux のとは異なるので注意)

対象が ひとつ の ファイルの場合は、コマンドプロンプトから以下のように実行します. ここでは Windows.gitignore~/.gitignore へ ダウンロードしました.

1
c:\Temp> powershell curl -Uri https://raw.githubusercontent.com/github/gitignore/master/Global/Windows.gitignore -OutFile ~/.gitignore

複数のファイルを結合する場合は、PowerShell を 起動してからコマンドを実行します. ここでは Windows.gitignoreEclipse.gitignoreVisualStudioCode.gitignore を 結合しました.

1
2
3
4
5
6
7
8
c:\Temp> powershell
Windows PowerShell
Copyright (C) 2016 Microsoft Corporation. All rights reserved.
PS C:\Temp>
PS C:\Temp> curl -Uri https://raw.githubusercontent.com/github/gitignore/master/Global/Windows.gitignore -UseBasicParsing | Select-Object -ExpandProperty Content >> ~/.gitignore
PS C:\Temp> curl -Uri https://raw.githubusercontent.com/github/gitignore/master/Global/Eclipse.gitignore -UseBasicParsing | Select-Object -ExpandProperty Content >> ~/.gitignore
PS C:\Temp> curl -Uri https://raw.githubusercontent.com/github/gitignore/master/Global/VisualStudioCode.gitignore -UseBasicParsing | Select-Object -ExpandProperty Content >> ~/.gitignore

gitignore.io で 自動生成

複数ファイルを結合して使う場合に、コマンドを複数発行すればよいのですが ちょっと面倒だなぁという時は gitignore.io というサービスが自動で作ってくれます. なんと 名だたる企業が使っている ようです(が、企業ロゴが1枚画像で企業へのリンク無しなんだよなぁ…)

Web から使う場合は、 https://www.gitignore.io/ へ アクセスします.

画面中央のテキストボックスに gitignore したいテンプレート名前を入れていき、[Create] ボタンをクリックします.

自動生成された gitignore の 内容が出力されるので、コピー&ペーストします.

また、自動生成された gitginore 画面 の URL を 使うことでコマンドラインからも取得できます. キーワードをカンマでつなぐのですが、URL Encode するので %2C で つなぎます.

1
c:\Temp> powershell curl -Uri https://www.gitignore.io/api/windows%2Ceclipse%2Cvisualstudiocode -OutFile ~/.gitignore

ヘルプや、指定できるキーワードのリストは PowerShell から 以下のように実行します.

1
2
3
4
5
6
7
8
9
10
c:\Temp> powershell
PS C:\Temp> curl -Uri https://www.gitignore.io/api/ -UseBasicParsing | Select-Object -ExpandProperty Content
gitignore.io help:
list - lists the operating systems, programming languages and IDE input types
:types: - creates .gitignore files for types of operating systems, programming languages or IDEs
PS C:\Temp> curl -Uri https://www.gitignore.io/api/list -UseBasicParsing | Select-Object -ExpandProperty Content
1c-bitrix,a-frame,actionscript,ada,adobe
advancedinstaller,agda,alteraquartusii,altium,android ...(省略)

さらに Command Line Docs - gitignore.io には、公式のコマンドライン実行法について説明があります. スクリプト化しておくと便利なのかもしれませんが、 curl だけでも大変ありがたいです.

gitignore.io-san, thank you for the wonderful service!!


github/gitignore リポジトリの Global ディレクトリ発見から、便利なサービスにまでたどり着くことができ、開発環境を作る際の幅広がりました. 自分が知っていることをすぐに使えることも必要ですが、他にも良いやり方は無いのかと疑問を持って調べる習慣も大事ですね. Global ディレクトリに気づかなかったのはホント不覚である…

今回は グローバル gitignore の 自動生成でしたが、アプリケーション用の gitignore も 同じことができるので、これからは自動生成でやった方が良いですね.

実際に使うにあたっては、グローバル gitignore は 自動生成後に編集.
アプリケーション用にはプロジェクトなり組織共通の gitignore を 管理するリポジトリを作って、先にそのファイルをダウンロードして、gitignore.io 自動生成を追記させるような感じでしょうか.

言語やフレームワークの gitignore を 自動生成させるとすると、独自追加部分だけが残り、だいたい どのリポジトリも同じものを追加すると思われるので、Git で 管理・共用化して、自動生成だけにするのがよいのかもと思ったりも… 今度やってみよう.