クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【メタ言語】フロントエンド開発スターターキットを作った(EJS、Sass(SCSS)、TypeScript)

メタ言語の続きです。前回delを使って破壊神になり、ファイルを削除しました。今回はこれまで作ってきたコードを元にフロントエンド開発スターターキットを作ります。メタ言語(EJS、Sass(SCSS)、TypeScript)のコンパイル&画像圧縮を行う環境です。それではいきましょう!

【目次】

※参考:前回記事
【gulp】我、破壊神となりてdelをもってファイルを削除すべし - クモのようにコツコツと

※参考:【メタ言語】HTMLテンプレートエンジン、AltCSS、AltJSのまとめ
qiita.com

前回のおさらい

srcフォルダでリネイムや削除を行うと
https://cdn-ak.f.st-hatena.com/images/fotolife/i/idr_zz/20201128/20201128122952.jpg

destフォルダも同期される
https://cdn-ak.f.st-hatena.com/images/fotolife/i/idr_zz/20201128/20201128122955.jpg

詳細は前回の記事を参照

※参考:【gulp】我、破壊神となりてdelをもってファイルを削除すべし - クモのようにコツコツと

ここまでで環境構築系はいったん一区切りにする。

フロントエンド開発スターターキットを作る

これまで作ったコードを元にフロントエンド開発スターターキットを作りたい。

※参考:GitHub - ryo-i/frontendMetaLanguage at 7d153d6de2fb00624691f0e15fcf38128e9b0de7

こちらの記事の手順で既存リポジトリをローカルにクローン(git cloneコマンド)

※参考:【Express】Heroku CLIを使ってNode.js環境をHeroku上にデプロイする - クモのようにコツコツと

ファイル構成を最低限の内容に変更する。

こちらの記事の手順で新規リポジトリと連携(git remote

※参考:【Express】HerokuとGitHubを連携して自動デプロイ(環境変数は除外) - クモのようにコツコツと

フロントエンド開発スターターキット

フロントエンド開発スターターキットを作った

f:id:idr_zz:20201201081214j:plain

ソース(GitHub)

※参考:GitHub - ryo-i/front-end-getting-sterted: メタ言語(EJS、Sass(SCSS)、TypeScript)のコンパイル環境です。画像も圧縮します。

プレビュー(GitHub Pages)

※参考:フロントエンド開発スタートキット

全体構成

全体的な環境は下記のような構成、挙動になっている。

  • /srcフォルダを修正するとコンパイル実行(ブラウザも更新)
  • /srcフォルダ -> /destフォルダにコンパイルされる
  • /src/ejs -> /dest直下(.ejs -> .html
  • /src/scss -> /dest/css.scss ->.css
  • /src/ts -> /dest/js.ts -> .js
  • /src/img -> /dest/img.jpg,.jpeg,.png,.gif,.svg画像を圧縮)
  • /src/json -> /src/ejs, /src/tsで読み込み

環境構築の過程はこちらを参照

※参考:【メタ言語】HTMLテンプレートエンジン、AltCSS、AltJSのまとめ
qiita.com

導入手順

ターミナルのcdコマンドで導入したいフォルダに移動

$ cd /(パス)

リポジトリをするクローンする

$ git clone https://github.com/ryo-i/front-end-getting-sterted.git

「front-end-getting-sterted.git」というフォルダが作られる。

フォルダに移動する

$ cd front-end-getting-sterted

パッケージのインストール

npm install

「node_modules」フォルダが作られる

gulp起動、終了

gulpの起動

$ npm start

localhostの3000でブラウザが立ち上がる

gulpの終了 「Contrl + C」

新規リポジトリにコミット

GitHub上の新規リポジトリにコミットする場合はgit remoteコマンドで紐付け

$ git remote add origin https://github.com/ユーザ名/リポジトリ名.git

EJSからHTMLにコンパイル

ファイル構成

/src/ejsはこのようなファイル構成になっている f:id:idr_zz:20201201060129j:plain
BEMのブロックごとにファイルを分けている。

/src/jsonフォルダのdata.jsonにHTMLのテキスト部分がある f:id:idr_zz:20201201060753j:plain

/destフォルダの直下のindex.htmlにコンパイルされる f:id:idr_zz:20201201060142j:plain

コンパイル前(/src/ejs)

index.ejs

ページ本体ファイル

<!doctype html>
<html>
<head>
<%- include('base/_head', {header: data.header}); %>
</head>
<body>
<%- include('block/_header', {header: data.header}); %>
<%- include('block/_main', {main: data.main, inner: data.inner}); %>
<%- include('block/_footer', {footer: data.footer}); %>
</body>
</html>
  • include()の第一引数でモジュールファイルをインクルード
  • include()の第二引数でモジュールファイルにjsonファイルのデータを渡す
_head.ejs

headファイルにインクルードするモジュール

    <meta charset="UTF-8">
    <title><%= header.title %></title>
    <meta name="description" content="<%= header.text %>">
    <link rel="stylesheet" href="css/style.css">
    <script src="js/script.js"></script>
  • headerという名前でjsonファイルのデータを読み込む(下記のheaderタグと共通の内容)

なお、モジュールファイルはファイル名の頭に_が付けていて、コンパイルから除外される。

_header.ejs

headerタグにインクルードするモジュール

<header class="header">
    <h1 class="header__title"><%- header.title %></h1>
    <p class="header__text"><%- header.text %></p>
</header>
  • headerという名前でjsonファイルのデータを読み込む
_main.ejs

mainタグにインクルードするモジュール

<main>
    <section class="main">
        <h1 class="main__title"><%- main.title %></h1>
        <p class="main__text"><%- main.text %></p>
        <%- include('_inner', {inner: inner}); %>
    </section>
</main>
  • mainという名前でjsonファイルのデータを読み込む
  • pタグの下でさらに_innerモジュールをインポートしている
_inner.ejs

上記の_mainタグにインポートしているモジュール

<% for (var i = 0; i < inner.length; i++) { %>
        <section class="inner">
            <h2 class="inner__title"><%- inner[i].title %></h2>
            <p class="inner__text"><%- inner[i].text %></p>
        </section>
<% } %>
  • innerという名前でjsonファイルのデータを読み込む
  • for文で配列の数繰り返して読み込む
_footer.ejs

footerタグにインクルードするモジュール

<footer class="footer">
    <p class="footer__text"><%- footer.text %></p>
</footer>
  • footerという名前でjsonファイルのデータを読み込む
data.json

HTMLに読み込まれているテキストのコンテンツ

{
    "data": {
        "header": {
            "title":"フロントエンド開発スターターキット",
            "text": "メタ言語(EJS、Sass(SCSS)、TypeScript)のコンパイル環境です。画像も圧縮します。"
        },
        "main": {
            "title":"タイトルです",
            "text": "テキストです。テキストです。テキストですったらテキストです。"
        },
        "inner": [
            {
            "title": "CSS(文字色)",
            "text": "CSSでタイトルの文字色変更。"
            },
            {
            "title":"JS(文字列)",
            "text": "JSでテキストの文字列追加「<span class='inner__text--hello'></span>」"
            }
        ],
        "footer": {
            "text":"©️ front-end-getting-sterted"
        }
    }
}

外部jsonファイルにすることでHTMLタグの構造とコンテンツを分離している。

コンパイル後(/dest/index.html)

index.htmlにこのような形でコンパイルされる

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>フロントエンド開発スターターキット</title>
    <meta name="description" content="メタ言語(EJS、Sass(SCSS)、TypeScript)のコンパイル環境です。画像も圧縮します。">
    <link rel="stylesheet" href="css/style.css">
    <script src="js/script.js"></script>
</head>
<body>
<header class="header">
    <h1 class="header__title">フロントエンド開発スターターキット</h1>
    <p class="header__text">メタ言語(EJS、Sass(SCSS)、TypeScript)のコンパイル環境です。画像も圧縮します。</p>
</header>
<main>
    <section class="main">
        <h1 class="main__title">タイトルです</h1>
        <p class="main__text">テキストです。テキストです。テキストですったらテキストです。</p>
        <section class="inner">
            <h2 class="inner__title">CSS(文字色)</h2>
            <p class="inner__text">CSSでタイトルの文字色変更。</p>
        </section>
        <section class="inner">
            <h2 class="inner__title">JS(文字列)</h2>
            <p class="inner__text">JSでテキストの文字列追加「<span class='inner__text--hello'></span></p>
        </section>
    </section>
</main>
<footer class="footer">
    <p class="footer__text">©️ front-end-getting-sterted</p>
</footer>
</body>
</html>

Sass(SCSS)からCSSにコンパイル

ファイル構成

/src/scssはこのようなファイル構成になっている f:id:idr_zz:20201201060855j:plain
BEMのブロックごとにファイルを分けている。

/dest/cssフォルダのstyle.cssにコンパイルされる f:id:idr_zz:20201201060907j:plain

コンパイル前(/src/scss)

style.scss

インクルードするモジュールファイルをコンパイルしたい順に並べている

@import 'setting/_variable';
@import 'setting/_mixin';

/***** base *****/
@import 'base/_base';

/***** block *****/
@import 'block/_header';
@import 'block/_main';
@import 'block/_inner';
@import 'block/_footer';
  • /settingフォルダは他のモジュールに渡す設定値
  • /baseフォルダは全体に共通するスタイル
  • /blockフォルダはBEM記法のブロックごとのファイル

Sass(SCSS)でもファイル名に_がついたモジュールファイルはコンパイルで除外される

_variable.scss

変数値を設定しているファイル

$base-color: #ff0000;
$text-color: #333;
$text-color_w: #fff;
$bg-color_g: #eee;
$text-size: 14px;

ブロックを跨いで共通する色やサイズなど

_mixin.scss

共通スタイルを設定しているファイル

@mixin pageSize($width: 1000px) {
    width: 100%;
    max-width: $width;
    padding: 20px;
    margin: 0 auto;
}

pageSizeという名前でページサイズの設定。幅は1000pxより狭い場合は100%になる。

_base.scss

bodyタグに設定しているページ全体に共通する設定

body {
    margin: 0;
    padding: 0;
    font-family: sans-serif;
    font-size: $text-size;
    color: $text-color;
    *, *:before, *:after {
        box-sizing: border-box;
    }
    a {
        color: $base-color;
    }
}
  • $で_variable.scssの変数を読み込んでいる
_header.scss

headerブロックのスタイル

/* header */
.header {
    @include pageSize();
    text-align: center;
    background: $bg-color_g;
    padding: 20px;
    &__title {
        font-size: 2em;
    }
}
  • @includeで_mixin.scssのスタイル設定を読み込んでいる。
  • $で_variable.scssの変数を読み込んでいる
_main.scss

mainブロックのスタイル

/* main */
.main {
    @include pageSize();
    &__title {
      font-size: 1.5em;
    }
  }
  • @includeで_mixin.scssのスタイル設定を読み込んでいる。
_inner.scss

innerブロックのスタイル

/* inner */
.inner {
    &__title {
      font-size: 1.25em;
      color: $base-color;
    }
}
  • $で_variable.scssの変数を読み込んでいる
_footer.scss

footerブロックのスタイル

/* footer */
.footer {
    @include pageSize();
    text-align: center;
}
  • @includeで_mixin.scssのスタイル設定を読み込んでいる。
  • $で_variable.scssの変数を読み込んでいる

コンパイル後(/dest/css/style.css)

style.cssにはこのようにコンパイルされる

/***** base *****/
body {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  font-size: 14px;
  color: #333;
}

body *, body *:before, body *:after {
  box-sizing: border-box;
}

body a {
  color: #ff0000;
}

/***** block *****/
/* header */
.header {
  width: 100%;
  max-width: 1000px;
  padding: 20px;
  margin: 0 auto;
  text-align: center;
  background: #eee;
  padding: 20px;
}

.header__title {
  font-size: 2em;
}

/* main */
.main {
  width: 100%;
  max-width: 1000px;
  padding: 20px;
  margin: 0 auto;
}

.main__title {
  font-size: 1.5em;
}

/* inner */
.inner__title {
  font-size: 1.25em;
  color: #ff0000;
}

/* footer */
.footer {
  width: 100%;
  max-width: 1000px;
  padding: 20px;
  margin: 0 auto;
  text-align: center;
}

TypeScriptからJSにコンパイル

ファイル構成

/src/tsフォルダはこのような構成になっている f:id:idr_zz:20201201061020j:plain /modulesフォルダにhelloモジュールがあり、その中も機能ごとにモジュールファイルを分けている

/src/jsonフォルダのhello.jsonにJSに使用する値を入れている f:id:idr_zz:20201201060753j:plain

/dest/jsフォルダのscript.jsにコンパイルされる f:id:idr_zz:20201201061031j:plain

コンパイル前(/src/ts)

script.ts

モジュールファイルを読み込むファイル

import { hello } from './modules/hello/hello';

// modules
hello();

helloモジュールをインポートしてhello()で実行している

hello.ts

helloモジュールの本体

import { message } from './message';

const text: string = message.text;
const selector: string = message.selector;

const hello = (): void => {
    document.addEventListener('DOMContentLoaded', () => {
        const dom: HTMLButtonElement = document.querySelector(selector) as HTMLButtonElement;
        dom.innerHTML = text;
        console.log('text-> ' + text);
    });
}

export { hello }
  • message.tsをインポート
  • hello ()関数でタグにテキストを入れたりコンソールにテキストを表示する
  • hello ()関数をhelloモジュールとしてエクスポート
message.ts

JSONファイルを読み込んでオブジェクトを作成

import hello from '../../../json/hello.json';

const message: {[key: string]: string;} = {
    text: hello.message.text,
    selector: hello.message.selector
};

export { message };
  • hello.jsonをインポート
  • 変数messageはオブジェクト(連想配列)でhello.jsontextselector`を設定
  • messageオブジェクトをmessageモジュールとしてエクスポート
hello.json

helloモジュールで読み込んでいる設定値

{
    "message": {
        "text": "こんにちは、ふろんとえんど。",
        "selector": ".inner__text--hello"
    }
}
  • messagetextキーはテキスト
  • messageselectorキーはタグのclass名

コンパイル後(/dest/js/script.js)

script.jsにこのようにコンパイルされる

(()=>{"use strict";const e=JSON.parse('{"y":{"f":"こんにちは、ふろんとえんど。","n":".inner__text--hello"}}');var t={text:e.y.f,selector:e.y.n},n=t.text,o=t.selector;document.addEventListener("DOMContentLoaded",(function(){document.querySelector(o).innerHTML=n,console.log("text-> "+n)}))})();

webpackでバンドルしているので圧縮されている。JSONデータはJSON.parse()でオブジェクトに変換している。

整形するとこのようになる

(()=>{
    "use strict";
    const e = JSON.parse('{"y":{"f":"こんにちは、ふろんとえんど。","n":".inner__text--hello"}}');
    var t = {
        text: e.y.f,
        selector: e.y.n
    }
      , n = t.text
      , o = t.selector;
    document.addEventListener("DOMContentLoaded", (function() {
        document.querySelector(o).innerHTML = n,
        console.log("text-> " + n)
    }
    ))
}
)();

画像の圧縮

初期状態は空

/src/imgフォルダの初期状態は空
f:id:idr_zz:20201201061718j:plain

/dest/imgフォルダも空
f:id:idr_zz:20201201061720j:plain

画像圧縮(/src/img -> /dest/img)

/src/imgフォルダにファイルを保存すると https://cdn-ak.f.st-hatena.com/images/fotolife/i/idr_zz/20201128/20201128122952.jpg

/dest/imgフォルダに圧縮された画像が保存される https://cdn-ak.f.st-hatena.com/images/fotolife/i/idr_zz/20201128/20201128122955.jpg

対応している拡張子は.jpg,.jpeg,.png,.gif,.svg

姉妹リポジトリ「Webコーディングスターターキット」

Webコーディングスターターキットも作った

f:id:idr_zz:20201201081646j:plain

※参考:GitHub - ryo-i/web-coding-getting-sterted: HTML/CSS/JSコーディングの最小環境です。

※参考:Webコーディング スタートキット

ファイル構成

f:id:idr_zz:20201202051236j:plain

基本的には「フロントエンド開発スターターキット」の「/dest」フォルダとほぼ同じ。

script.js

先ほどのwebpackを整形したコートを少し手直し

const hello = JSON.parse(
'{"message":{"text":"こんにちは、ふろんとえんど。","selector":".inner__text--hello"}}'
);

const message = { 
    text: hello.message.text, 
    selector: hello.message.selector 
};

const text = message.text;
const selector = message.selector;

document.addEventListener("DOMContentLoaded", () => {
    document.querySelector(selector).innerHTML = text;
    console.log("text-> " + text);
});
  • 変数名をTypeScriptの設定名に戻す
  • 変数varconst
  • function関数をアロー関数に

最後に

とうことでフロントエンド開発スターターキットを作りました。なるべく最低限の構成を心がけましたが、同時にモジュール分割でのフロントエンド開発がイメージできるようにバランスを取りました。

メタ言語(EJS、Sass(SCSS)、TypeScript)はこちらの記事で書いたように、HTML、CSS、JSの上位互換になっている言語で、既存コードからの移行が可能な言語を選定しています。

※参考:フロントエンドのメタ言語のコンパイル環境を作る(言語選定編) - クモのようにコツコツと

今後、自分自身のフロントエンド開発環境としてもこのリポジトリを使って行きたいと思っています。(また何か修正をするときには記事にしていこうと思います)

それではまた!


※参考:【メタ言語】HTMLテンプレートエンジン、AltCSS、AltJSのまとめ
qiita.com