技術探し

JavaScriptを中心に記事を書いていきます :;(∩´﹏`∩);:

登壇する時に使っているスライド発表ツールの紹介

一ヶ月前に勉強会で話してきました。

hiroppy.github.io

2017年の2月から運用しています。

リポジトリ

github.com

目的

  • gitで管理したい
  • 自己紹介とかの共通ページを毎回スライド書くごとに書きたくない
  • Markdownでスライドを書きたい
  • 極力JSを書かないようにして、もしカスタマイズしたいときは書けば良い。(基本MarkdownCSSだけ)
  • 発表者モードができる(new!!)

ツール・フレームワーク

JavaScript

CSS

  • postcss-cssnext
  • postcss-mixins
  • postcss-smart-import
  • postcss-reporter
  • postcss-browser-reporter

ビルドフロー

webpack

Markdown -> HTML -> React

// in webpack

{
  test: /\.md$/,
  use: [
    'html-loader',
    'markdown-loader'
  ]
}

ローダーチェインはmarkdown-loaderを通し、html-loaderを通します。

slideを取得する

require.contextというwebpackが提供しているメソッドを使います。

Dependency Management

  // fetch all slides 
  const context = require.context('./slides', true, /(md|html)$/);
  const res = {
    id    : context.id,
    slides: context
      .keys()
      .sort() // sort by File Name
      .map((e) => context(e))
  };

ファイル名とディレクトリ名は数字で記載し、それをスライドの並び順として定義します。
e.g. 0-title.md, 01-context.md, 02-dir/0-title.md

注意点として、require.contextは動的には使えないので、第一引数の目的のパスは文字列でなければなりません。

取得したHTMLをReactへ

ReactのdangerouslySetInnerHTMLを使います。

const Base = (props) => (
  <article>
    {
      props.slides.map((slide, i) => (
        <section
          key={slide.meta.id}
          className={slide.meta.className}
          data-bespoke-backdrop={slide.meta.background}
          dangerouslySetInnerHTML={{ __html: slide.context }}
        />
      ))
    }
  </article>
);

これで完成。

プロダクション

Service Worker

offline-pluginを使い、Service WorkerによりJSとCSSと画像のキャッシュをしています。

// webpack.prod.config.js

module.exports = {
  plugins: [
    new OfflinePlugin(),
    ...
  ]
};

// offline.js

import offlineRuntime from 'offline-plugin/runtime';

offlineRuntime.install();

github.com

imagemin

imageminのwebpackのloaderを使います。

github.com

// webpack.config.js

{
  test: /\.(png|jpg|gif|svg?)$/,
  use: [
    'file-loader',
    'image-webpack-loader'
  ]
}

ローダーチェインはimage-webpack-loader -> file-loader。

Dynamic Import

発表者モードは使う場面が限られるのでdynamic importで制御しています。
閲覧の場合は、プレゼンターモードを読み込ませません。 Dynamic Importは現在stage-3です。

github.com

// https://github.com/abouthiroppy/slides/blob/master/src/lib/AppContainer.js#L31

// AppContainer.js

constructor(props) {
  if (mode === 'view') {
    import(/* webpackChunkName: 'presenter.view' */ './ContentView/View')
      .then((e) => {
        ...
      });
  }
  else if (mode === 'host') {
    import(/* webpackChunkName: 'presenter.host' */ './ContentView/Host')
      .then((e) => {
        ...
      });
  }
}
Hash: 08e7000cd507fa241759
Time: 77409ms
                    Asset       Size  Chunks                    Chunk Names
0.08e7000cd507fa241759.js    14.4 kB       0  [emitted]
1.08e7000cd507fa241759.js    3.65 kB       1  [emitted]         presenter.host
2.08e7000cd507fa241759.js    1.45 kB       2  [emitted]         presenter.view
  08e7000cd507fa241759.js    1.27 MB       3  [emitted]  [big]  main

発表者モード

普段は発表者ノートなしでやっていますが、英語の発表だと発表者ノートがほしくなったので、プレゼンターモードを実装しました。

f:id:about_hiroppy:20171207094242p:plain

現在のページの表示も実装したけど、いいデザインがなくてコメントアウト

表示用に新しいwindowが立ち上がり、スピーカー用に上記のページがリロードされます。

これは、localstorageを使いページ情報を表示用とスピーカー用で共有をしています。

// host.js
window.slide.bespoke.on('activate', (e) => {
  localStorage.setItem('page', JSON.stringify({
    date: Date.now(),
    page: e.index
  }));
});

// view.js
window.addEventListener('storage', (e) => {
  if (e.key === 'page') {
    const page = JSON.parse(e.newValue).page;

    window.slide.bespoke.slide(page);
  }
});

github.com

送っている情報はタイムスタンプと現在のページだけですが、実際イベントをリッスンしてページを動かしているのでタイムスタンプは使いません。

2015年ぐらいにニコナレってサービスで同じ実装をしたので、特に困らずにできた。

niconare.nicovideo.jp

操作

Appleの公式サイトでも紹介されているLogicool Spotlight Presentation Remoteをいつも使っています。

www.apple.com

すごい便利でアプリを入れれば、ポインターとかも使えます。

マークダウンで書けるサービス

spectacle

github.com

ライブコーディングもできてすごいハイスペック。
マークダウンで書けるけど、メインはJSX。

GitPitch

https://gitpitch.com/gitpitch.com

githubmarkdownをpushするとスライドになる。

Object.freeze / Object.seal の糖衣構文

もともとの発端はここから。

明示的にconstかどうかを示すプロポーザルでまだチャンピオンは取得してない。

気付いたら

  • Brian Terlson(現在のTC39のエディタ)
  • Jordan Harband(AirbnbのTC39の会員)
  • Mikeal Rogers(Node.js Foundationの設立者)
  • Bradley Farias(TC39の会員でESMに一番くわしい人)
  • Benedikt Meurer(V8チームのスゴイ人)
  • Ingvar Stepanyan(ASTスゴイ詳しい人)

たちが議論していた。

今回の話でも上がってきたし、TC39のミーティングでのstageの変動があったObject.freeze/sealの話をしようと思う。
途中でnull prototypesの話になったのでそれはもし書きたくなったら書く。

プロポーザル

github.com

Brian TerlsonがFishrock123のプロポーザルとこれ似ているからって話があって思い出した。

今回のミーティングでstage-1となった。

github.com

JSの問題点

関数の引数に対して、代入はしても元はもちろん変わらないが、Objectの中身の変更はできる。
JSのconstはあくまでも再代入をさせないだけであるので、中身の変更は可能である。

const a = { b: 1 };

function modify(a) {
  // a = {c: 1}; // 再代入はしても意味がない
  a.b += 1;
  a.c = 2;
}

modify(a);
console.log(a); // { b: 2, c: 2 }

Objectの制御について

ES5から入った、preventExtensionssealfreezeというメソッドがあります。
以下が対応表です。

method プロパティの追加 プロパティの変更 プロパティの削除 プロパティの属性の変更 確認
preventExtensions N Y Y Y Object.isExtensible
seal N Y N N Object.isSealed
freeze N N N N Object.isFrozen

このような属性へ変更されます。

{
  const a = { b: 1 };

  console.log(Object.getOwnPropertyDescriptors(a));
  // { b: { value: 1, writable: true, enumerable: true, configurable: true } }

  Object.preventExtensions(a);
  console.log(Object.getOwnPropertyDescriptors(a));
  // { b: { value: 1, writable: true, enumerable: true, configurable: true } }

  Object.seal(a);
  console.log(Object.getOwnPropertyDescriptors(a));
  /*
   { b:
     { value: 1,
       writable: true,
       enumerable: true,
       configurable: false } }
  */

  Object.freeze(a);
  console.log(Object.getOwnPropertyDescriptors(a));
  /*
   { b:
     { value: 1,
       writable: false,
       enumerable: true,
       configurable: false } }
  */
}

つまり、sealではconfigurablefalseへ、freezeではwritableconfigurablefalseとなる。

また、強い方へしか変更ができなくなります。

const a = { b: 1 };

Object.freeze(a);
Object.preventExtensions(a);
Reflect.deleteProperty(a, 'b');

console.log(a); // { b: 1 }

他の言語もおそらく同じですが、これらはそのObject以下のすべてを適応するわけではなく、直下しか適用しません。

{
  const a = {
    b: {
      c: [ 'test' ]
    }
  };

  console.log(Object.getOwnPropertyDescriptors(a));
  /*
   { b:
     { value: { c: [Array] },
       writable: true,
       enumerable: true,
       configurable: true } }
  */

  const b = Object.freeze(a);

  console.log(Object.getOwnPropertyDescriptors(b)); // 適応
  /*
   { b:
     { value: { c: [Array] },
       writable: false,
       enumerable: true,
       configurable: false } }
  */

  console.log(Object.getOwnPropertyDescriptors(b.b)); // 適応されてない
  /*
   { c:
     { value: [ 'test' ],
       writable: true,
       enumerable: true,
       configurable: true } }
  */
}

つまり、複数の階層構造になっているObjectを制御したければ各行で明記しなければなりません。

糖衣構文

さすがに、毎回Object.~~って書くのはめんどいのでそのために提案された。

freeze

const foo = {#
  a: {#
    b: {#
      c: {#
        d: {#
          e: [# "some string!" #]
        #}
      #}
    #}
  #}
#}

// 展開
const foo = Object.freeze({
  a: Object.freeze({
    b: Object.freeze({
      c: Object.freeze({
        d: Object.freeze({
          e: Object.freeze([ "some string!" ])
        })
      })
    })
  })
})

seal

const foo = {|
  a: {|
    b: {|
      c: {|
        d: {|
          e: [| "some string!" |]
        |}
      |}
    |}
  |}
|}

// 展開
const foo = Object.seal({
  a: Object.seal({
    b: Object.seal({
      c: Object.seal({
        d: Object.seal({
          e: Object.seal(["some string!"])
        })
      })
    })
  })
})

また、以下のような使い方もできる。

function ajax({| url, headers, onSuccess |}) {
  fetch(url, { headers }).then(onSuccess)
}
ajax({ url: 'http://example.com', onsuccess: console.log })
// throws TypeError('cannot define property `onsuccess`. Object is not extensible')
function add(| a, b |) {
  return a + b
}
add(2, 2, 2) === 6
// throws TypeError('invalid third parameter, expected 2`)
function add1(# a #) {
  a += 1 // throws TypeError `invalid assignment...`
  return a
}
add1(1) === 2
const foo = { a: 1, b: 2 }
const {| a, b, c |} = foo
// Throws TypeError 'invalid assignment to unknown property c'

このように関数のパラメータの束縛したりすることによりデストラクチャリングに対してバリデートしやすくなる。

さいごに

ライブラリのコードではよく使われるが、業務で使われていたのを見たことがない気がする。

アクセスを調査した

今回のブログが予想以上にアクセスが多く、面白そうだったので調査資料として残したいと思いました。

abouthiroppy.hatenablog.jp

金曜日と土曜日と日曜日で調査しました。

アクセス数

f:id:about_hiroppy:20171204092833p:plain f:id:about_hiroppy:20171204093958p:plain

金曜日が14438人で土曜日が4742人で日曜日が609人でした。
日曜日になると通常ぐらいに落ち着いてきました。

調査

投稿時間は金曜日の10:30です。
今回の伸びた要因ははてなブログのトップにのったことだと思います。

はてなブログ

hatenablog.com f:id:about_hiroppy:20171202112251p:plain

これはどのように選別されているのか知らないが中の人が手動で決めている気がしますが、指標がわからないです。。

投稿してから毎時間1000人以上流入があったので投稿してすぐにおそらくはてなブログのトップに乗ったと思われます。
土曜日の18:00にはトップからいなくなっていました。

はてなブックマーク

ホッテントリには金曜日はずっと入っていたが、土曜日には入っていなかったです。

f:id:about_hiroppy:20171204093324p:plain

流入

f:id:about_hiroppy:20171204093916p:plain

smartnewsに上がると流入がかなり増えます。
基本的に、経験上総合のホッテントリでなくてもITとかでsmartnewsにのります。
smartnewsに上がる前ははてなブログが一番でした。

f:id:about_hiroppy:20171203174340p:plain

流入バイス

f:id:about_hiroppy:20171203174315p:plain

モバイル多すぎっ!

GA

はてなアクセス解析とGAの数値に差異があるのはおそらくアドブロックのせいだと思われます。

金曜日

f:id:about_hiroppy:20171203174554p:plain f:id:about_hiroppy:20171203174614p:plain

土曜日

f:id:about_hiroppy:20171203174839p:plainf:id:about_hiroppy:20171203174911p:plain

日曜日

f:id:about_hiroppy:20171204093231p:plainf:id:about_hiroppy:20171204093255p:plain

時間別アクセス数

これを見るとわかるように、投稿してからすぐにはてなブログのtopに乗ったと思われます。

f:id:about_hiroppy:20171204093146p:plain

一番多かったのは、土曜日の午後12時で1052人でした。

結論

  • 平日は、昼休みの時間帯である11時 - 12時近くに見る人が多い
  • 意外にも1時でも見ている人がいる(これはエンジニアだけ?)
  • モバイルが60%以上を占めている
  • 案外、google / organicの流入が多いがwww.smartnews.comが二番手に多い
  • GAブロックしている人多い(?)

メルカリを退職した

6ヶ月でしたが、退職することになりました。
本当にありがとうございます。

環境

アメリカと日本のプロダクトで活動していて、フロントエンドチームに所属していました。
特にメンターでチームリーダの方からは、多くの配慮をしていただき入社してからずっと助けて貰ってました。
もちろん、周りの皆さんにも助けて貰っていて、このような結果になってしまい本当に申し訳ないと思っています。
メルカリですが、日本で開発をしながら英語も使えるいい環境でした。
開発の相方が外国人の方で、英語を喋る機会が多かったです。個人的にはもっと日本の会社は多様性・グローバル化をしてほしいと思っています。
特に多様性の部分は自分が気付かない視点を持っている方が多かった印象です。
また、シャッフルランチやメンターランチのような会社からのサポートで知らない方や新しく入った方とご飯に行くサービスも好きでした。
これにより、多くの方と知り合えた気がします。

転職について

転職を決意してから今日に至るまで、複数の企業様からオファーをいただきました。 本当に感謝しています。
そして、入社できない企業様に対し本当に申し訳なく思っています。

新天地では、今以上に活躍できるように努力していきたいと思います。
また、OSSはもちろん、日本でもJSのコミュニティの活動等を引き続きしていきたいです。
ここでのブログのアウトプットも今のところ1 ~ 2週間ペースで記事を更新しているので今後も継続させたいと思っています。

それでは今後共、何卒よろしくお願いします。

Node学園祭2017資料まとめ

nodefest.jp

2017/11/25, 26で開催されました。

今回は、前回よりも更にto be more more globalをテーマに作っていきました。

一日目はセッション、スポンサートーク、LT等でした。
二日目はセッションと参加者が手を動かすワークショップを行いました。
両日ともに、3ラインで進みました。

自分は一日目はBホール(402)の司会をしていて、二日目はCode And LearnでNodeへのコミットのサポートをしていました。

全部見れてないので、わかっている部分の資料だけ貼りたいと思います。
もし資料があったら教えてくれるとついったーとかでください。

Twitter

ハッシュタグ

twitter.com

モーメント

東京Node学園祭 2017 / Twitter

資料まとめ

スケジュールはこちらを参照してください。
時間順で並べてあります。

1日目

Opening talk

見つからない。

Node.js at Alibaba

talks/nodejs_at_alibaba_tokyo.pdf at master · joyeecheung/talks · GitHub

Source to Binary - journey of V8 javascript engine

Source to Binary - journey of V8 javascript engine - Speaker Deck

Alt-Ctrl: Scream Into this Arduino!

Node Interactive から

Alt-Ctrl : Scream into this Arduino - Rachel White, Front End Engineer - YouTube

Open Source Governance Models: From BDFL to Consensus Seeking

Node Interactive から

Open Source governance models · Myles Borins - YouTube

Make you a React: How to build your own JavaScript framework

MakeYouAReact - Google スライド

Node.js Production Checklist

Node.js production checklist - Speaker Deck

The JavaScript Engine in Node.js: TurboFan and Ignition

https://fhinkel.github.io/JSEngines-HowDoTheyEven/NodeFest/assets/player/KeynoteDHTMLPlayer.html

Node.js x Chrome headless で、お手軽WebRTC MCU

Node.js x Headless Chrome for WeRTC MCU / Node.js x Chrome headless で…

WebAssembly and the Future of the Web

Node Interactive から

WebAssembly and the Future of the Web [I] - YouTube

標準化ってなに? Webプラットフォーム/ESNextを作る方法

Web Platform ... What is it ? (Webプラットフォームのつくり方) - Speaker Deck

ReactNativeアプリでE2Eテストを回してみた

React NativeアプリでE2Eテストを回してみた - Speaker Deck

No REST for the weary... Introducing GraphQL

No REST for the weary... Introducing GraphQL (NodeFest Japan) - Speaker Deck

Turbo Boost Next Node.js

Turbo Boost Next Node.js - Speaker Deck

今さら聞けないSPAのCORS対策の話

今さら聞けないSPAのCORS対策の話 - Speaker Deck

A Toy Robot and a Functional Pattern

http://robhoward.id.au/talks/2017/11/a-toy-robot-and-a-functional-pattern.pdf

Native ES Module - something almost, but not quite entirely unlike CommonJS

Native ES Modules - something almost, but not quite entirely unlike CommonJS - NodeFestJP - Google スライド

サーバサイドエンジニアがJavaScriptを書いてTradingViewを導入した話

How_To_ Introduce_TradingView - Speaker Deck

Quantum Computing in Node

Quantum Computers and Where to Hide from Them (Japanese)

Sponsor's talk

Sponsors | 東京Node学園祭2017 | 11月25日,26日開催予定!

株式会社ドワンゴ

N予備校プログラミングコースでのNode.js - Speaker Deck

リクルートテクノロジー

見つからない。

株式会社メルカリ

Node 学園祭 2017 スポンサートーク資料 - Speaker Deck

株式会社ヤフー

Node学園祭2017.pdf - Speaker Deck

日産自動車株式会社

https://www.wantedly.com/projects/156247

Auth0® Inc.

見つからない。

Lightning Talks

Angular Universal

SSR with Angular

2017版、React、Lambda、S3で始めるモダンなユーザーデータ可視化ツール

見つからない。

How TypeScript can simplify design decision

https://speakerdeck.com/noraesae/how-typescript-can-simplify-design-decision

Introduction to Visual Regression Testing

Introduction to Visual Regression Testing - Speaker Deck

Whose Community?

Whose Community? - about NodeGirlsJP - Speaker Deck

2日目

Opening talk

見つからない。

Node Discussion

Nodeのメンバーに色々と聞くコーナー。
Nodeのいいところ・悪いところ・要望などを伝えて答える。

Node School

Nodeの勉強をするコーナー。
メンターが付いており、もし躓いたらすぐに聞ける。

Angular2+ Authentication Tutorial with Auth0

Reactに変わった気がするが、タイトルがどこにあるかわからなかったので一旦。
Auth0で働く方に使い方を教えてもらうコーナー。

Sharing is Caring… At Scale!

Node Conf EUから

Sharing is Caring… At Scale! - Sarah Saltrick Meyer - YouTube

LINE Bot Handson

1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest - Qiita

data sketches: A Visualization a Month

見つからない。

Real-world applications of hash functions

見つからない。

JSON Schema Centralized Design

JSON Schema Centralized Design - Speaker Deck

Code And Learn

コミッターがサポートしてNodeへコミットします。

Code And Learn at NodeFest 2017 · Issue #72 · nodejs/code-and-learn · GitHub

Data Visualization Workshop

data sketches: A Visualization a Monthのワークショップ。

Data visualization workshop

Lightning Talks

Self Driving Kit using Android and Node.JS

見つからない。

Node.js × AWS Lambda アプリケーション開発Tips

【東京Node学園祭2017】Node.js × AWS Lambda アプリケーション開発Tips

レスポンシブから逆戻り!?Webサービスのマルチデバイス対応方法

レスポンシブから逆戻り!?Webサービスのマルチデバイス対応方法 (東京Node学園祭 2017)

参加者のブログ

ありがとうございます:)
もし他にも書いている方がいたら教えてください!

yurufuwa-tech.hatenablog.com

https://blog.kadoppe.com/%E6%9D%B1%E4%BA%ACnode%E5%AD%A6%E5%9C%92%E7%A5%AD2017-2%E6%97%A5%E7%9B%AE-%E3%81%AB%E5%8F%82%E5%8A%A0%E3%81%97%E3%81%BE%E3%81%97%E3%81%9F-nodefest-5f92028305cbblog.kadoppe.com

さいごに

運営の仲間のみんな、スポンサーの企業様、参加者のみなさん、本当にありがとうございました。

また来年お会いしましょう!!

Node.jsのドキュメント管理について

じいちゃんが米寿なため、実家で書いています。
あと家族増えてました👨‍👨‍👦‍👦
左の4ヶ月の子。

先日、こちらのOSSドキュメント勉強会で話しました。

kbkz.connpass.com

今回はスライドの要約です。

how to manage the document of Node.js

ドキュメントの重要性

f:id:about_hiroppy:20171120021722p:plain

opensource.guide

ドキュメントは大変重要で、ドキュメントが豊富かどうがでそのフレームワーク・ライブラリが使用されるかどうかも左右されると私は思います。

また、ドキュメントの貢献はOSSへの最初のワンステップとしてはちょうどよく貴重なコミットです。

例えばフレームワークであれば、APIの仕様以外にもexamplesを含めget-startedがあればユーザに優しいと思います。

Node.js Foundationについて

Node.jsはNode.js Foundationというところで管理されています。
Node.js github.com

ここでは、約600人の人々が約120チームに分かれて活動をしています。
また、チームとしては34ヶ国語のサポートがされています。
基本的にissueは英語以外でも問題ないという認識です。

Node.js

github.com

  • 19,914commits
  • 457 releases
  • 1790 contributors
  • 2,527 watchers
  • 42,472 stars
  • 8,831 forks

Node.の中には様々なドキュメントがあります。
以下は一例です。

  • BUILDING.md
  • CHANGELOG.md
  • CODE_OF_CONDUCT.md
  • COLLABORATOR_GUIDE.md
  • CONTRIBUTING.md
  • CPP_STYLE_GUIDE.md
  • GOVERNANCE.md
  • API Documentation

今回はAPIドキュメントについて話しました。

APIドキュメント

Docs | Node.js

nodejs.orgにあるのですが、APIドキュメントだけはNodeの.jsコアにあります。
なのでサイトの管理はnodejs/websiteチームですが、APIドキュメントはnodejs/collaboratorチームが行います。

コミット数

$ git log --grep doc: --pretty=oneline --check | wc
    3367   20994  237596

Node.jsではコミットのルールがあり、エコシステムが必ず先頭につきます。
ドキュメントの場合は、doc: です。

コミット数は3367でした。ちなみにテストは2853でおそらくエコシステムの中で一番多いコミット数だと思います。

issue/prのコメント数によるラベルの種類

f:id:about_hiroppy:20171121085509p:plain

One Week in the Life of Node.js

レビュワー

  • Collaborators
    • PRを承認する権利を持つので必須
  • Documentation
  • Website
    • 主にスタイル
  • その他モジュールに特化したWG(e.g. promise, stream, V8, etc...)

レビューをするポイント

  • typo
  • プラットフォームの依存
    • e.g. これはwindowsでは動かない等
  • コーナーケース
    • e.g. この場合は動かない等
  • 英語修正
    • もちろん、完璧な英語じゃなくても問題ないと思ってます

スタイルガイド

多いので言いたかったのだけ抜粋。

  • ファイル名はlowercase-with-dashes.md
    • jsのファイル名やモジュール名は-が多い(特に昔は)
  • I, we, you, his, guysなどは避ける
    • they, folks, people, developersなどを使う

github.com

Pronouns  |  Google Developer Documentation Style Guide  |  Google Developers

書き方

## module
<!-- YAML
added: v0.10.0
-->

> Stability: 3 - Stable

description and examples.

### module.property
<!-- YAML
added: v0.10.0
-->

* Type

description of the property.

### module.someFunction(x, y, [z=100])
<!-- YAML
added: v0.10.0
-->

* `x` {String} the description of the string
* `y` {Boolean} Should I stay or should I go?
* `z` {Number} How many zebras to bring.

A description of the function.

github.com

yamlの部分はバージョンの管理でそのモジュールが足されたとかの履歴を表示します。

markedjs-yamlを使いHTMLに変換します。
lib/<module>.jsdoc/api/<module>.mdが必ず1:1の対応になります。

オートメーション

CI

Node.js [Jenkins]

CIはNode.js Foundationから提供されているJenkinsを使います。
ここでは様々なプラットフォーム上でテストが行われます。
ドキュメントでは主にスモークテストとlintのテストが実行されます。

Lint

ESLintを使っています。

eslint.org

コアコードと同じルールを適応することにより、コードの書き方の統一化を行っています。

# Makefile

LINT_JS_TARGETS = benchmark doc lib test tools
LINT_JS_CMD = tools/eslint/bin/eslint.js --cache \
    --rulesdir=tools/eslint-rules --ext=.js,.mjs,.md \
    $(LINT_JS_TARGETS)

またdoc内にもeslintrc.ymlがあり、そこで適応できない部分がある場合は上書きしています。

ドキュメントをビルドする

HTMLへ変換します。

$ ./configure && make doc

./configureでNode.jsを生成して、それを使いdocを生成します。
そうすると以下のようなファイルが生成されます。

f:id:about_hiroppy:20171121091349p:plain

サポートツール

Node.jsではまだ使われてませんが、便利なツールを紹介しました。

APIの翻訳

ターゲットとの差分(行が増えた, 消えた等)が見やすく、複数人で翻訳がしやすいサービスです。 GoogleのプロダクトやVueなどで使われています。

gitlocalize.com

英語のタイポ検知

以前、PRでこれを使って修正したというのが来てこのツールを知りました。
修正もしてくれます。

github.com

まとめ

  • ドキュメントはめっちゃめっちゃ大切
  • 英語の主語には注意する
  • 自動化しよう

Code and Learn

来週のNode学園祭の2日目では、Code and Learnと呼ばれるNode.jsへコントリビュートする企画があります。
実際に世界中のNode.jsのコアメンバーの人たちがサポートをし、Node.jsへコミットします。大変おもしろい企画ですので是非参加してみてください:)

そのときに役に立てばいいなと思います。

nodefest.jp

Node.jsのパフォーマンスチューニングのtips

Node9が10/31に出ました🎉🎉🎉

Node v9.0.0 (Current) | Node.js

今回はNode単体の話なので、Express、Nginx等のチューニングに関してはココには書きません。
また、libuv等のコード内部の話もしません。

--inspect, --inspect-brk

もともとあった、--debugから移行されました。(v8.0.0 ~)
Chromeを使いデバッグ、プロファイリング等を使えるようになります。
ブラウザで使えるので、いつも使っている感じと同じです。
--inspect-brk--debug-brkと同様に最初の行にブレークポイントを設置し、起動します。

$ node --inspect test.js
Debugger listening on ws://127.0.0.1:9229/b565921e-23f2-4cee-b124-33e97fc3aa32
For help see https://nodejs.org/en/docs/inspector

chromeからchrome://inspect/#devicesを指定すると以下のように選択肢がでるので、そこからinspectを選ぶと起動します。
f:id:about_hiroppy:20171105204518p:plain

インスペクターのクライアント一覧: Debugging - Getting Started | Node.js
Inspector Help | Node.js
個人的には、NiMを入れると楽かなーと思います。
abouthiroppy.github.io

--trace-opt, --trace-deopt

コードの最適化の解析を行います。

$ node --trace-opt test.js
[marking 0x3ad0f4375d1 <JSFunction normalizeStringPosix (sfi = 0x3ad423d7d81)> for optimized recompilation, reason: hot and stable, ICs with typeinfo: 46/67 (68%), generic ICs: 0/67 (0%)]
[compiling method 0x3ad0f4375d1 <JSFunction normalizeStringPosix (sfi = 0x3ad423d7d81)> using TurboFan]
[optimizing 0x3ad0f4375d1 <JSFunction normalizeStringPosix (sfi = 0x3ad423d7d81)> - took 1.867, 1.776, 0.019 ms]
[completed optimizing 0x3ad0f4375d1 <JSFunction normalizeStringPosix (sfi = 0x3ad423d7d81)>]
[marking 0x3ad423fcee1 <JSFunction Module._nodeModulePaths (sfi = 0x3ad423ba5a1)> for optimized recompilation, reason: hot and stable, ICs with typeinfo: 23/23 (100%), generic ICs: 0/23 (0%)]
[compiling method 0x3ad423fcee1 <JSFunction Module._nodeModulePaths (sfi = 0x3ad423ba5a1)> using TurboFan]
[optimizing 0x3ad423fcee1 <JSFunction Module._nodeModulePaths (sfi = 0x3ad423ba5a1)> - took 0.659, 3.009, 0.049 ms]
[completed optimizing 0x3ad423fcee1 <JSFunction Module._nodeModulePaths (sfi = 0x3ad423ba5a1)>]

markingは再コンパイル用のマーキングで、それは再コンパイルされ最適化されます。
最適化が不可能な場合は、マーキングの代わりにdisabled optimizationというのが付きます。

上記をみればわかるように、その関数が最適化されたかどうかがわかります。

--prof

CPUプロファイリングです。 V8内のプロファイラの実行をサンプリングします。

$ node --prof test.js
$ ls
isolate-0x103000000-v8.log test.js
$ node --prof-process isolate-0x103000000-v8.log # logは読みづらいので読めるようにする

各セクションごとに情報が分かれます。

 [Summary]:
   ticks  total  nonlib   name
      3    5.0%    5.0%  JavaScript
     50   83.3%   83.3%  C++
      1    1.7%    1.7%  GC
      0    0.0%          Shared libraries
      7   11.7%          Unaccounted

取得されたサンプルの比率(5.0%, 83.3%, etc...)が割合となり、その言語のコードで発生したことを示します。
そして、各セクションを見るといいと思います。

セクション例

   ticks parent  name
   6326   44.2%  /lib/x86_64-linux-gnu/libm-2.15.so
   6325  100.0%    LazyCompile: *exp native math.js:91
   6314   99.8%      LazyCompile: *calculateMandelbrot http://localhost:8080/Demo.js:215

各セクションは、ツリーになっており、この場合は親コールの合計時間における44.2%がシステム内のmath.exp()を実行するのに使われています。
関数名の前の*はその時間が最適化された関数で費やされていることを示し、~の場合は最適化された関数ではないことを示します。

詳しくは公式が出している以下の記事を見ると、手順がわかりやすいと思います。
Easy profiling for Node.js Applications | Node.js

github.com

--trace-events-enabled

トレース情報を管理します。
--trace-events-enabledフラグを渡すと有効化されます。
カテゴリを指定したい場合は、--trace-event-categoriesを使い続けてカテゴリを指定します。
カテゴリデフォルトはnodev8になります。
chromechrome://tracing/を指定することにより、生成したのをロードすることが可能です。

$ node --trace-events-enabled test.js
$ node --trace-events-enabled --trace-event-categories v8,custom-category test.js

f:id:about_hiroppy:20171105200300p:plain

Tracing | Node.js v9.0.0 Documentation github.com www.chromium.org

--trace-gc

Garbage Collectionのトレースです。
メモリリークデバッグに役立つでしょう。

$ node --trace-gc test.js
[43929:0x102801c00]       39 ms: Scavenge 3.4 (6.3) -> 3.1 (7.3) MB, 0.9 / 0.0 ms  allocation failure
[43929:0x102801c00]       50 ms: Scavenge 3.6 (7.3) -> 3.5 (8.3) MB, 1.2 / 0.0 ms  allocation failure

$ node --optimize_for_size --max_old_space_size=4096 --gc_interval=100 #このようにV8のGCを操作することも可能

--expose-gcを指定することにより、手動でGCを起こすことも可能です。

メモリリーク周りは以下の記事を参考にするとわかりやすくていいと思います。

postd.cc

実例

github.com

node-report

公式が出しているモジュールです。
現在、node-reportはCoreとは別で切り分けられておりスタンドアローンですが、将来的にはCoreに入る予定です。
ネイティブのスタックトレース、ヒープ統計情報、プラットフォーム情報、リソース使用状況などが人間が読める形でレポート化されます。

$ npm i node-report
$ node -r node-report test.js
$ cat node-report.20171105.202142.9066.001.txt
================================================================================
==== Node Report ===============================================================
...
Node.js version: v9.0.0
...
================================================================================
==== JavaScript Stack Trace ====================================================
...
================================================================================
==== Native Stack Trace ========================================================
...
================================================================================
==== JavaScript Heap and Garbage Collector =====================================
...
================================================================================
==== Resource Usage ============================================================
...
================================================================================
==== Node.js libuv Handle Summary ==============================================
...
================================================================================
==== System Information ========================================================
...
================================================================================

github.com

Performance Timing API

v8.5.0から入ったブラウザでも使われるAPIです。
現在はStability: 1(実験的)です。

Performance Timing API | Node.js v8.9.0 Documentation

詳しくは以下の記事をどうぞ

abouthiroppy.hatenablog.jp

優しいコードの書き方へ

v8.3.0からV8のTurbofan, IgnitionがデフォルトでCrankshaftから移行され、昔のような最適化のためのコードの書き方をしなくても良くなりました。

abouthiroppy.hatenablog.jp

先日のChrome Dev SummitでもV8チームが今後はそのようなアンチパターンをなくしていくと言っています。(つまりどの書き方をしても同じ結果になる)
また、トランスパイルは気をつけるべきです。
Babelにはbabel-preset-envというターゲットバージョンによりトランスパイルをするツールがあります。
babel-preset-envではNodeのバージョンを指定することにより、V8に優しいコードに変換することが可能です。
すべてのコードをトランスパイルするべきではありません。
基本的にトランスパイルされるコードは無駄な処理が多いからです。(これはそのものがエンジン側で実装されてないため)
なので、エンジン側で未実装なもの(e.g. stage-x)だけをトランスパイルするべきです。

{
  "presets": [
    ["@babel/env", {
      "targets": {
        "node": "current"
      }
    }]
  ]
}

github.com

先日、monorepoのbabelへ移行され次のバージョンではscoped packagesになりました🎉

v8::SnapshotCreator

将来的に入るかもしれませんが、今現在、ArrayBuffersに関して議論中です。

github.com

さいごに

今回は、パフォーマンスチューニングをするのに手助けになる手法を数個列挙してみました。
しかし、Node, V8の最適化周りのオプションの話をするとまだたくさんあるとおもいますが一旦このへんで。
詳しくはnode --v8-optionsへ。--print-code, --print-opt-code, --code-comments --track-heap-objects, etc...
その他には、I/O(libuv)とイベントループの理解も大切だと思います。

もしチューニング等でお困りでしたら、Twitterかメールで聞いてくだされば答えれるかもしれません。

また、今月Node学園祭があるので是非お越し下さい:)

nodefest.jp