0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

フロントエンド環境(TypeScript+PUG+SASS+Jest)を作ってみる

Posted at

概要

フロントエンドを勉強しよーって時に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云々もやろうと思いましたが力尽き(飽き)ました...。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?