babelでトランスパイル
今回はいつも以上に基本的なこと。
webpackを使って複数のjsを単一ファイルにトランスパイルしてきたが、トランスパイルはbabelの仕事。
だからbabel単体を利用して、トランスパイルするにはどんな環境構築作業が必要なのかを備忘録として残しておく。
ただ、jsでリファクタリング学習するためにトランスパイルしたファイルをnodejsで実行したかっただけなんだけど...
インストール
# babelをインストール npm install -D babel-cli # ES6対応presetをインストール npm install -D babel-preset-env
babel設定
.babelrcを作成して、以下を記入する
// .babelrc { "presets": ["env"] }
buildコマンド設定
npm run buildと入力することでトランスパイルさせたいので、npm scriptsを設定する。今回は、srcフォルダ内をすべてトランスパイルさせて、結果をdistに出力させる。
// package.json { ... scripts: { "build": "babel ./src --out-dir ./dist" } ... }
これでnpm run buildと入力すると、distフォルダにトランスパイルした結果を得られる。
他のプラグインを追加したい時
クラスプロパティを利用したい場合や、トランスパイル時にflowの記述を取り除きたい場合は、別途プラグインのインストールと設定が必要
// クラスプロパティプラグイン npm install -D transform-class-properties // flow除去プラグイン npm install -D transform-flow-strip-types
// .babelrc { "presets": ["env"], + "plugins": [ + "transform-class-properties", + "transform-flow-strip-types" + ] }
jsでリファクタリングを学びたい(Nullオブジェクト編)
リファクタリングについて学びたく、「Java言語で学ぶリファクタリング」を読んでる。タイトル通り、Javaで記述されているが、js(ES6) + flowで書いてみて、リファクタリングの技術とflowの理解を深めようと思う.
今回取り上げるリファクタリング内容は、4章のNullオブジェクトについてである。
書籍にはNullオブジェクトの良いたとえ話が記載されている。一日おきに飲まなければならない薬があるとき、本日飲むべき日かどうか考えないといけない。その判断を避けるために、本来の薬とダミーの薬を交互に飲み続ければよい。これをプログラミングでも行おうというものが、Nullオブジェクトの目的である。
では、実際null判定の入った次のソースをリファクタリングしていこう。
リファクタリング前
// Person.js // @flow import Label from './Label'; export default class Person { _name: Label; _mail: Label; constructor(name: Label, mail: Label) { this._name = name; this._mail = mail; } display() { // nullチェック if (this._name !== null) { this._name.display(); } // nullチェック if (this._mail !== null) { this._mail.display(); } } toString(): string { let result: string = '[ Person:'; result += ' name='; // nullチェック if (this._name === null) { result += '(none)'; } else { result += this._name; } result += " mail="; if (this._mail === null) { result += '(none)'; } else { result += this._mail; } result += ' ]'; return result; } }
nullかどうかでdisplay処理を行うかどうかを分けてるが、Nullオブジェクトを導入すると、nullかどうかを意識せずメソッドを呼び出せばよいのだ。
リファクタリング後
// Person.js // @flow import Label from './Label'; export default class Person { _name: Label; _mail: Label; // Nullオブジェクトの導入 constructor(name: Label, mail: Label = Label.newNull()) { this._name = name; this._mail = mail; } display() { this._name.display(); this._mail.display(); } toString(): string { return `[Person: name=${this._name.toString()} mail=${this._mail.toString()}]`; } }
コンストラクタの第二引数に、mailアドレスが与えれなかった時、Label.newNull()によりNullオブジェクトが代わりに与えられる。
このNullオブジェクトは、Label型のサブクラスとして実装されるため、型一致している。
そして、NullオブジェクトはLabelオブジェクト同様displayメソッドをもつが、何も影響与えないよう設計されている。
以上の要件からNullオブジェクトに要求されるものが、
- 本来のクラスを継承する
- 本来のクラスのもつメソッドを無効化する
という2点にある。
実装は次の通り。
Nullオブジェクト
// Label.js // @flow export default class Label { _label: string; constructor(label: string) { this._label = label; } // 本来行いたい処理 display() { console.log(`display: ${this._label}`); } toString(): string { return this._label; } // Nullオブジェクトを返すfactoryメソッド --- (a) static newNull(): Label { return NullLabel.getInstance(); } } // Nullオブジェクトクラス class NullLabel extends Label { // --- (1) // シングルトンの実装 --- (b) static singleton = new NullLabel(); static getInstance(): NullLabel { return NullLabel.singleton; } constructor() { super('(none)'); } // オーバーライドして何も影響を与えないようにする --- (2) // @override display() { } }
本来のLabelクラスを継承するNullLabelサブクラスがある(1)
またサブクラス内でメソッドを無効化するためにオーバーライドを利用している(2)
そして、メモリ節約のためのシングルトン(b)や、new演算子をさけるためのFactoryメソッド(a)を利用して、LabelクラスからNullオブジェクトを呼び出している。
まとめ
今回は、リファクタリングとしてNullオブジェクトについて学んだことを残してみた。Nullオブジェクトを一言でいうと、本物と同じ動きをするが影響を及ぼさないダミーオブジェクトのことを指すのであろう。
それを、継承とオーバライドによる無効化によって実現する技術なんだなと理解できた。
気になったこと
javaはオーバーロードが実装できるので、nullオブジェクトの検討がわかりやすいが、jsではオーバーロードが実装できない。
代わりに、デフォルト引数を利用すれば補えるのではないかと気づいた!
public class Person { private Label _name; private Label _mail; public Person(Label name, Label mail) { _name = name; _mail = mail; } public Person(Label name) { this(name, Label.newNull()); } ... }
export default class Person { _name: Label; _mail: Label; constructor(name: Label, mail: Label = Label.newNull()) { this._name = name; this._mail = mail; } }
- 作者: 結城浩
- 出版社/メーカー: ソフトバンク クリエイティブ
- 発売日: 2007/01/27
- メディア: 大型本
- 購入: 12人 クリック: 189回
- この商品を含むブログ (110件) を見る
electronでキャプチャーソフトつくってみるpart2
キャプチャーソフトを作成中に、録画経過時間を表示する機能が必要になった。フラグのON/OFFでタイマーの開始・リセット出来るならば、他のアプリにも活かせるかもと思い、タイマーコンポーネントを作ってみた。
要件
- タイマーつくる
- propsであるisRecordを切り返ると、タイマーが開始・リセットされる
propsTypeの代わりに、flowで型チェックを行っている
// @flow import * as React from 'react'; type PropType = { isRecord: boolean }; type StateType = { time: number, timerId: number | null }; export default class Timer extends React.Component<PropType, StateType> { constructor(props: PropType) { super(props); this.state = { time: 0, timerId: null, }; } shouldComponentUpdate(nextProps: PropType): boolean { if (nextProps.isRecord !== this.props.isRecord) { (nextProps.isRecord) ? this.clearTimer() : this.setTimer(); } return true; } setTimer() { const timerId = setInterval(() => { const { time } = this.state; this.setState({ time: time + 1 }); }, 1000); this.setState({ timerId }); } clearTimer() { const { timerId } = this.state; timerId && clearInterval(timerId); this.setState({ time: 0, timerId: null }); } formatTime(time: number): string { const zeroPad = (num: number): string | number => { if (num < 10) { return `0${num}`; } return num; }; const hour = zeroPad(Math.floor(time / 60 / 60)); const minuite = zeroPad(Math.floor(time / 60)); const second = zeroPad(time % 60); return `${hour}:${minuite}:${second}`; } render(): React.Node { const { time } = this.state; return ( <span> {this.formatTime(time)} </span> ); } }
timerIdについて、eslint/flowでエラーが吐かれてしまう。 なんだこれ。あとで調べよう。。。
electronでキャプチャーソフトつくってみるpart1
electronに最近はまってます(笑)。キャプチャーソフトも作れるということなので、
reactとmaterial-uiを練習かねて、つくりました。
electronのキャプチャー機能と動画保存機能を実装してみたかっただけなので、
ものすごく単純です。この調子で仕上げていきたい~
参考サイト
Electronでデスクトップを録画するアプリが簡単に作れました - なになれ
デスクトップを録画するアプリを書いた - Qiita
mochaでテスト実行時にflowの記述を自動で取り除く
はじめに
mochaでテスト実行するときに、flowの記述が残されたままだとテストできない。
だから、前までは前処理としてflowの記述を取り除いたフォルダを作成し、
そのディレクトリに対してテストを実行させていた。
しかし、余計なフォルダを作成させずにmochaを実行できる方法をたまたま見付けたので、 残しておこうと思う。
流れ
- テスト環境の容易
- flowのインストール
- flowの記述追加
- テスト失敗の確認
- flow-remove-typesの追加と設定
- テスト成功
0. テスト環境の用意
mochaとpowerassertを使ったテスト環境構築方法は、以前作成した記事power-assertとmochaを使ったnodejsのテストを参考にしてください。 今回もこの環境を使用します。
1. flowのインストール
前の記事では, flowをインストールしてなかったので追加します。
npm install --save-dev flow-bin ./node_moduleds/.bin/flow init
2. flowの記述追加
前回作成したファイルにflowの記述を追加
// @flow class Calc { // flowの記述追加 static add(x: number, y: number): number { return x + y; } // flowの記述追加 static sub(x: number, y: number): number { return x - y; } } module.exports = Calc
3. テスト失敗の確認
npm test
と叩くと下図の通り。
Unexpected token :と表示されてる通り、余計なコロンがあるのでテストは失敗します。
4. flow-remove-typesの追加と設定
npm install --save-dev flow-remove-types
package.jsonの修正
//package.json scripts: { "test": "mocha --require intelli-espower-loader --require flow-remove-types/register ./test/" }
複数のモジュールを--requireしたい時は、モジュールごとに--requireをつければよいよう。これで問題なくテストは動きます。
5. テスト成功
electronでReactを使うためのwebpackの設定
はじめに
electron。javascriptでデスクトップアプリケーションを作れるフレームワーク。
使ってみたかったけど、なかなかよい作りたいものも思いつかずsample demo appで遊ぶくらいでした。
最近ちょっと良いアイデアも思いついたので、思い切ってelectronを触ってみました。
また合わせてreactの勉強もしてみたかったので、React x electronの環境構築の備忘録を残しておこうと思います。
reduxは使いません。まだ扱えきれるレベルじゃないないので。。。
ポイント
- node_modulesを呼び出せるelectronのjsをどうやってバンドルするの?
- main・rendererプロセス用の2種類のjsが必要
electronのjsはfsなどnodeで使えるモジュールを呼び出せるので、
webのReactと同様にimportできるのか。。。
またelectronには、mainプロセスとrendererプロセスが存在し、それぞれにjsファイルがあるのに対して、多分普通のReactのプロジェクトでは、htmlで読み込むのは、一つのjsだけ。
この違いをどうやって、対応するか。。。
結論急げば、webpackの設定でどちらの問題も解決できます。まじ優秀!!
流れ
- electronのインストール
- electron起動
- reactの環境構築
- build失敗の確認
- webpackの設定(nodeモジュールをimportさせる)
- webpackの設定(2つのjsファイルを作成)
- buildしてみる
1. electronのインストール
これだけ。はは、便利
npm install -g electron
2. electron起動
プロジェクトを作って、mainプロセス用のjsと、rendererプロセスで必要なhtmlとそれが呼び出すrenderer.jsを作成します。 js,htmlが用意できたら、electronコマンドでmain.jsを指定します
mkdir sampleApp cd sampleApp npm init -y touch main.js touch index.html electron main.js
作成したhtml,jsは、tutorialのWriting Your First Electron Appからコピペしてます。
こんな感じ。
3. reactの環境構築
他の色々なサイトに導入方法は書いてあるが、自分は【Reactではじめるフロントエンド開発入門】1 を参考にさせてもらいました。npm, yarn両方の構築方法が丁寧に書かれてあるので、勉強になりました。
// react npm install react react-dom // webpack npm install -D webpack webpack-cli // babel npm i babel-core -D npm i babel-preset-es2015 -D npm i babel-preset-react -D npm i babel-loader -D
webpackの設定も最低限
// webpack.config.js const webpack = require("webpack"); const path = require('path'); const config = { entry: './src/app.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'app.js' }, module: { rules: [{ test: /\.js$/, exclude: path.resolve(__dirname, 'node_modules'), loader: 'babel-loader', query:{ presets: ['react', 'es2015'], } }] } }; module.exports = config;
npm build
でapp.jsができると、後はindex.htmlでロードすればおしまい。
4. build失敗の確認
では、webのプロジェクト同じやり方でjsファイルを作成しトランスパイルさせてみます
nodeモジュールfsを利用した以下のファイルを作成する。
import React, { Component } from 'react'; import ReactDom from 'react-dom'; // 追加: nodeのモジュールfsをimportする import fs from 'fs' export default class App extends Component { constructor(props) { super(props); this.state = { file : '' } } componentWillMount() { // fsモジュールを使用する this.setState({file:fs.readFileSync('index.html','utf8')}) } render() { return <h1>{this.state.file}</h1> } } ReactDom.render( <App />, document.getElementById('root') );
npm build
するとこちら。
はい、失敗します! fsが読み込めませんと。 なので、これの対応がwebのときと異なり必要となってきます。
5. webpackの設定(nodeモジュールをimportさせる)
google先生に聞いてみると、ここに electron + webpack + react + sass ほとんど書いてました。targetプロパティなるものが、重要ということ。 webpack公式targetによると、targetには,electron-mainとelectron-rendererを 指定できるようなので、これを使いましょう。
では、targetプロパティを追加して、electron用であることを指定します。
// webpack.config.js const webpack = require("webpack"); const path = require('path'); const config = { entry: './src/app.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'app.js' }, target:'electron-renderer', // 追加 renderer用 module: { rules: [{ test: /\.js$/, exclude: path.resolve(__dirname, 'node_modules'), loader: 'babel-loader', query:{ presets: ['react', 'es2015'], } }] } }; module.exports = config;
6. buildしてみる
build成功
electron起動
7. webpackの設定(2つのjsファイルを作成)
もう一つ課題が残っていました。
mainプロセスとrendererプロセス用の二つのjsをトランスパイルさせましょう。
renderer用のみトランスパイルするなら特に不要な作業だが、main.jsだけはcommonjs記法を採用するという
気持ちの悪い感じがするので、自分はmain.jsもトランスパイルさせました。
設定方法は実に簡単。
main用とrenderer用の設定を記述して、配列にしたものをconfigに格納するだけになります。
// webpack.config.js const config = [ { // mainプロセス用 entry: './src/main/main.js', output: { path: path.resolve(__dirname, 'dist/main'), filename: 'main.js' }, target: 'electron-main', module: { rules:[ { test: /\.js$/, exclude: path.resolve(__dirname, 'node_modules'), loader: 'babel-loader', query: { presets: ['react', 'es2015'] } } ] } }, { // renderer用 entry: './src/renderer/renderer.js', output: { path: path.resolve(__dirname, 'dist/renderer'), filename: 'renderer.js' }, target: 'electron-renderer', module: { rules:[ { test: /\.js$/, exclude: path.resolve(__dirname, 'node_modules'), loader: 'babel-loader', query: { presets: ['react', 'es2015'], plugins:['transform-class-properties'] }, } ] } } ]
その結果、main用とrenderer用の二つのjsファイルを作成されます。
dist ├─main │ main.js │ └─renderer index.html renderer.js
最後に
webpackの設定は奥が深い...。
フロントエンドの勉強は学習範囲が多すぎて、どこか不安になってしまう。
これが仕事となると、本当に効率よい学習が必要なんだろうな~と思う。
けど、jqueryも使う気がしないのは確かだ。
-
↩
- 作者: 中野仁
- 発売日: 2017/11/14
- メディア: Kindle版
- この商品を含むブログを見る
eslintでflowの検証結果も表示させる
そんなにプログラミングするわけではないが、
javascriptは本当に色々変更が激しいなと最近感じてる。
今回は、eslintとflowを使って静的検証が行える環境構築手順を残そう。
eslintを実行させると、flowのチェックと結果を取得できるようにする
目次
- eslintを使ってみる
- flowを使ってみる
- npm run lintでflowの検証結果も表示
- npm run lintでflowのエラー結果も表示
- flow-remove-typesで実行用ファイルを作成する
1. eslintを使ってみる
npm install --save-dev eslint // .eslintrcを作成 ./node_modules/.bin/eslint --init
あとはコマンドライン上の質問に答えると、.eslintrc.jsonなどの設定ファイルができる
npm run lintでeslintが起動できるようにpackage.jsonの修正
package.json scripts : { lint: "eslint ./src" }
ディレクトリ、ファイルを作成する
mkdir src touch eslinttest.js
eslinttest.js function sum(x,y) { return x+y; } let result = sum(1,1);
これでnpm run lintと入力すると検証結果が表示される
3:11 error Infix operators must be spaced space-infix-ops 7:5 error 'result' is assigned a value but never used no-unused-vars 7:5 error 'result' is never reassigned. Use 'const' instead prefer-const 7:27 error A space is required after ',' comma-spacing 7:31 error Newline required at end of file but not found eol-last
2. flowを使ってみる
// npmで追加 npm install --save-dev flow-bin // .flowconfigを作成する ./node_modules/.bin/flow --init
npm run flowでflowが起動できるようにpackage.jsonの修正
package.json ... scripts :{ flow : "flow" } ...
touch ./src/flowtest.js
flowtest.js //@flow function sum(x: number, y: number): number { return x+y; } // あえて間違える let result: string = sum(1,1);
npm run flowでflowの検証結果が表示される
Error ---------------------------------------------------------- Cannot assign `sum(...)` to `result` because number [1] is incompatible with string [2]. src/flowtest.js:7:24 7| const result: string = sum(1, 1); ^^^^^^^^^ References: src/flowtest.js:2:37 2| function sum(x: number, y: number): number { ^^^^^^ [1] src/flowtest.js:7:15 7| const result: string = sum(1, 1); ^^^^^^ [2]
3. npm run lintでflowの検証結果も表示
flowの記述が残されたままだと、eslintは失敗する
なので、いくつか前準備が必要になる。
npm install --save-dev babel-eslint eslint-plugin-flowtype
.eslintrc.jsonも修正し、eslint-plugin-flowtypeのconfigurationをコピペ。
これでnpm run lintを実行すると、eslint単体のときと同様のエラーが表示される。
4. npm run lintでflowのエラー結果も表示
しかし、表示されるのはeslintの結果だけで、flowのエラーはこのままでは表示されない。
なので、もう一つplugin追加が必要となる。
npm install --save-dev eslint-plugin-flowtype-errors
.eslintrc.json ... "plugins": [ "flowtype", // 追加 "flowtype-errors" ], rules: { // 追加 "flowtype-errors/show-errors": 2, "flowtype-errors/show-warnings": 1 } ...
あとはnpm run lint!
これでeslintのエラーに加えてflowのエラー内容も表示される.
5. flow-remove-typesで実行用のファイルを作成
npm installl --save-dev flow-remove-types
npm run buildで起動できるようにpackage.jsonの修正
package.json scripts: { "build": "flow-remove-types src -d lib --pretyy" }
npm run buildを実行すると、flowの型情報を削除したファイルがlibに出来る。
最後に
jsはインタプリタ言語なのに静的検証やら型検査が普通になってきているのようだ。これもCIなどでbuildまでの時間を短くできるようになったのが、背景なのかもなと感じた。 これでバシバシ書いていけるぞ