概要
フロントエンドを勉強しよーって時にUIライブラリを使わずに自分で色々作ってみることがあったり無かったりすると思うのですが、そういう時に使う環境をちゃちゃっと用意しようと思います。新人さんの勉強とかにも使えるのかも?
細かい説明は割愛して動かせる環境を用意することを目的とします。細かいところ説明してって言われても理解してない部分が多いのです...精進します(´・ω・`)
package.jsonとちょっとしたコード
frontend-dev-env: TypeScript+PUG+SASS+Jest
必要な時間
詰まらなければ1時間程度で終わると思います。
環境
TypeScript+PUG+SASSで書けるようにします。単体テストはJestを使います。結合はTestCafeを使えば良いと思います。IDEはVisual Studio Code。
開発環境構築
VSCode及びNode.jsのインストールは割愛。npm init -y
しておいてください。
TypeScript
本体のインストール
$ npm install typescript
$ npm install --save-dev tslint
TSLint
設定
tslintのinitを行います。
$ ./node_modules/.bin/tslint --init
作られたtslint.jsonをちょこちょこっと修正。
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"jsRules": {},
"rules": {
"interface-name": false,
"no-empty-interface": true,
"object-literal-sort-keys": false,
"member-access": true,
"typedef": true,
"triple-equals": true,
"variable-name": false,
"radix": false,
"align": false,
"max-line-length": false,
"member-ordering": false,
"curly": false
},
"rulesDirectory": []
}
動作確認
./src/ts/sample.ts
を作成し、以下のコードを記述。
var num: number = 1;
コードを書いたら以下のコマンドでTypeScriptのコードをチェックします。
$ ./node_modules/.bin/tslint './src/**/*.ts'
先の例だと以下のようなメッセージが表示されます。
ERROR: src/ts/sample.ts:1:1 - Forbidden 'var' keyword, use 'let' or 'const' instead
ERROR: src/ts/sample.ts:1:12 - file should end with a newline
varを使わないでletかconstを使って、最後に1行入れてね。ってことですね。直してみましょう(最終行の空行は省略します)。
const num: number = 1;
再度チェックするとエラーは無くなってますね!例えば1
ではなく"文字列"
を入れた場合もエラーに引っかかって欲しいですが、VSCodeの方で警告が出るのでそっちで直していきましょう。
npm scripts登録
package.jsonのscriptsに以下を追加しておきます。
"scripts": {
"tslint": "./node_modules/.bin/tslint './src/**/*.ts --silent'"
}
--silentオプションを付けないとExit status 2(または1)が表示されてしまうので、隠してます。npm run tslint
でsrc配下の.tsファイルのチェックが可能です。
tsc
設定
$ ./node_modules/.bin/tsc --init
tsconfig.jsonはトップレベルに以下を追加。
{
"include": [
"./src/**/*.ts"
],
:
}
後は"outDir"
のコメントアウトを削除して値を"./dist"
に、"lib"
のコメントアウトを削除して値を["es6"]
しておきます。
動作確認
$ ./node_modules/.bin/tsc src/ts/sample.ts --outDir dist/js
先程のtsファイルを./dist/js/
にJavaScriptに変換して出力します。出力したファイルの中身は以下のようになっているかと思います。
var num = 1;
constがvarになって、型定義が無くなってますね!もうちょっとわかりやすくするためにsample.tsを以下のように修正してJavaScriptに変換してみましょう。
export default class User {
private _firstName: string;
private _lastName: string;
private _birthday: string;
private _sex: number;
private _activate: boolean;
public constructor(firstName: string = "", lastName: string = "", birthday: string = "2000/01/01", sex: number = 0, activate: boolean = false) {
this._firstName = firstName;
this._lastName = lastName;
this._birthday = birthday;
this._sex = sex;
this._activate = activate;
}
public setFirstName = (arg: string): void => { this._firstName = arg; };
public setLastName = (arg: string): void => { this._lastName = arg; };
public setBirthday = (arg: string): void => { this._birthday = arg; };
public setSex = (arg: number): void => { this._sex = arg; };
public setActivate = (arg: boolean): void => { this._activate = arg; };
private splitBirthday = (birthday: string): number[] => birthday.split("/").length > 1 ? birthday.split("/").map((s) => parseInt(s)) : birthday.split("-").map((s) => parseInt(s));
private verifyBirthday = (birthday: string): boolean => {
const dateFormat: RegExp = /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/;
if (!birthday.match(dateFormat)) return false;
const splitedDate: number[] = this.splitBirthday(this._birthday);
if (splitedDate.length !== 3) return false;
const ListOfDays: number[] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
if (splitedDate[1] === 1 || splitedDate[1] > 2) {
if (splitedDate[2] > ListOfDays[splitedDate[1] - 1]) return false;
}
if (splitedDate[1] === 2) {
const leapYearFlag = (!(splitedDate[0] % 4) && splitedDate[0] % 100) || !(splitedDate[0] % 400) ? true : false;
if ((leapYearFlag === false) && (splitedDate[2] >= 29)) return false;
if ((leapYearFlag === true) && (splitedDate[2] > 29)) return false;
}
return true;
}
public getFullName = (): string => `${this._firstName} ${this._lastName}`;
public getAge = (): number => {
if (!this.verifyBirthday(this._birthday)) return -1;
const splitedBirthday: number[] = this.splitBirthday(this._birthday);
const d = new Date();
const now = parseInt(d.getFullYear().toString() + ("0" + (d.getMonth() + 1).toString()).slice(-2) + ("0" + d.getDate().toString()).slice(-2));
const birthday = parseInt(("000" + splitedBirthday[0].toString()).slice(-4) + ("0" + (splitedBirthday[1].toString())).slice(-2) + ("0" + (splitedBirthday[2].toString())).slice(-2));
return Math.floor((now - birthday) / 10000);
}
public getSex = (): string => {
switch (this._sex) {
case 0:
return "男性";
case 1:
return "女性";
default:
return "";
}
}
public isActivate = (): boolean => this._activate;
public toJson = (): object => {
return {
name: this.getFullName(),
birthday: this._birthday,
age: this.getAge(),
sex: this.getSex(),
activate: this.isActivate(),
};
}
}
ちょっと長いですが、TypeScriptとJavaScriptのファイルで違いがよくわかると思います。
npm scripts登録
変換も簡単に使えるようにしておきましょう。package.jsonのscriptsに以下を追加しておきます。
"scripts": {
"tsc": "./node_modules/.bin/tsc --rootDir src"
}
npm run tsc
でTypeScriptからJavaScriptに変換が可能です。出力先はoutDirで指定したdistディレクトリになります。
PUG
ejsでも良いのですが、SASSっぽいしSASSはPUGっぽいので合わせようと思います。Expressとかでも使えるらしいので無駄にはならない...かな。
設定
$ npm install -D pug-cli
特にconfigは不要です。
動作確認
src直下にindex.pugを作成して以下を記述します。
doctype html
html(lang="ja")
head
meta(charset="utf-8")
title PUG Sample
body
h1 h1 title!
main.main-class#main-id
h2 h2 title :-)
./src/ts直下にindex.pugを作成して以下を記述します。
doctype html
html(lang="ja")
head
meta(charset="utf-8")
title tsディレクトリ
body
p tsディレクトリ内のindex.pug
ファイルの作成が終わったら以下のコマンドを実行しましょう。
$ ./node_modules/.bin/pug ./src --out ./dist
dist直下とdist/ts直下にそれぞれindex.htmlができたかと思います。HTMLが見づらい!という場合は--pretty
オプションを付与して実行すると見易い形で出力されます。PUGは慣れないと少し書きづらいですが、慣れてくると非常に書きやすいので頑張って慣れましょう(気合)。
npm scripts登録
例のごとくnpmで実行できるようにしておきましょう。
"scripts": {
"pug": "./node_modules/.bin/pug ./src --out ./dist"
}
SASS
設定
毎度の通り以下のコマンド。
$ npm install -D node-sass
動作確認
src直下にstyleディレクトリを作成し、その中にsample.sassを作成し以下を記述。
body
background-color: lightgray
margin: 0 auto
h1
font-weight: bold
.main-class
h2
color: red
src直下のindex.pugのtitleの上に以下の一行を追加し(インデックスは合わせてください)、htmlを作成しておきます。
link(rel="stylesheet" type="text/css" href="/style/sample.css")
追加してhtmlを出力したらsassをcssにして出力しましょう。
$ ./node_modules/.bin/node-sass ./src -o ./dist -r
dist配下にstyleディレクトリができてその中にsample.cssができていればOKです!PHPビルトインサーバとかでdist直下のindex.htmlを見るとCSSが適用されているのがわかります(ブラウザでファイルを開いても確認できます)。
npm scripts登録
npmの登録は圧縮された状態で出力するようにしておきましょう。
"scripts": {
"sass": "./node_modules/.bin/node-sass ./src -o ./dist --output-style compressed -r"
}
Jest
設定
$ npm install -D jest @types/jest ts-jest
package.jsonに以下を追加。
"jest": {
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
},
"testMatch": [
"**/test/**/*.ts?(x)"
]
}
動作確認
srcと同階層にtestディレクトリを作成し、その中にsample.test.tsを作成して以下を記述。
import User from "../src/ts/sample";
describe("Userクラスのテスト", () => {
const u = new User("Hoge", "Piyo", "1984/4/4", 0, true);
test("名前取得", () => {
expect(u.getFullName()).toBe("Hoge Piyo");
});
test("年齢算出", () => {
expect(u.getAge()).toBe(35);
});
test("性別取得", () => {
expect(u.getSex()).toBe("男性");
});
});
以下のコマンドでテスト実行。
$ ./node_modules/.bin/jest
こんな感じでテスト結果が表示されたらOKです!
PASS test/sample.test.ts
Userクラスのテスト
✓ 名前取得 (3ms)
✓ 年齢算出 (1ms)
✓ 性別取得
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 1.327s
Ran all test suites.
npm scripts登録
いつも通りですね。カバレッジを出力するようにしておきます。
"scripts": {
"jest": "./node_modules/.bin/jest --coverage"
}
環境構築で作成したnpm scriptsのまとめ
npm run tslint
TypeScriptの静的コードチェックを行います。
npm run tsc
TypeScriptをJavaScriptに変換します。
npm run pug
.pugを.htmlに変換します。
npm run sass
.sassを.cssに変換します。
npm run jest
TypeScriptのテスト(testディレクトリ配下のテストコード)を実行します。
ディレクトリ構成の整理
開発環境がある程度整ったので、ディレクトリ構成を整理しておこうと思います。
root
├ coverage: テストカバレッジ出力用ディレクトリ
├ dist: ビルドコード出力用ディレクトリ
├ src: ビルド前のソースコード用ディレクトリ
│ ├ style: sass書く用のディレクトリ
│ └ script: ts書く用のディレクトリ
├ test: テストコード用ディレクトリ
├ node_modules: Node.jsパッケージ用ディレクトリ
├ package-lock.json: パッケージの依存関係情報とか(たしか
├ package.json: 必要なパッケージやnpm scripts等々
├ tsconfig.json: TypeScript設定
└ tslint.json: TSLint設定
ディレクトリ構成はお好きに変えて問題ありませんが、この記事の内容を加味するとこんな感じになります。
最後に
とりあえず勢いだけで構築しました。sample.tsの名前が中身と合ってないなぁと思いつつ。色々と無知を晒していると思いますが、「ここはこうやった方がいい」とか「これはアンチパターンやぞ」とかありましたらご教授頂けると嬉しいです。
TestCafeを使っての結合テストやGrunt(Gulp)とかWebPack、git云々もやろうと思いましたが力尽き(飽き)ました...。