/real-world-website-boilerplate

静的ウェブサイトを開発するためのボイラープレート

Primary LanguageJavaScriptMIT LicenseMIT

Real world website boilerplate

静的ウェブサイトを開発するためのボイラープレートです。大量の HTML ファイルも快適に開発できる仕組みに注力しつつ、モダンな開発ツールを採用しながらも柔軟な構成になっています。

目次

推奨環境

  • Mac OS X または Windows
  • Yarn
  • Node.js 8.9.0 以降
  • EditorConfig および Prettier をサポートするエディタ

インストール

git clone https://github.com/yuheiy/real-world-website-boilerplate.git my-web
cd my-web
rm -rf .git
yarn install

このプロジェクトの Git リポジトリをクローンした上で、Node.js の依存モジュールをインストールしてください。

開発用サーバー

yarn start

ソースファイルの監視を開始して、開発用サーバーを起動します。--prod フラグを指定するとビルドが本番用になります。

本番用ビルド

yarn build

dist/に本番用にビルドされたファイルが出力されます。

ディレクトリ構成

.
├── .tmp/
│   ├── assets/
│   │   ├── main.bundle.css
│   │   ├── main.bundle.css.map
│   │   ├── main.module.bundle.js
│   │   ├── main.module.bundle.js.map
│   │   ├── main.nomodule.bundle.js
│   │   └── main.nomodule.bundle.js.map
│   └── index.html
├── dist/
│   ├── assets/
│   │   ├── logo.svg
│   │   ├── main.bundle.css
│   │   ├── main.module.bundle.js
│   │   ├── main.nomodule.bundle.js
│   │   └── ogp.png
│   ├── favicon.ico
│   └── index.html
├── public/
│   ├── assets/
│   │   ├── logo.svg
│   │   └── ogp.png
│   └── favicon.ico
├── src/
│   ├── css/
│   │   └── main.scss
│   ├── html/
│   │   ├── _data/
│   │   │   └── meta.yml
│   │   ├── index.pug
│   │   └── index.yml
│   └── js/
│       ├── main.js
│       ├── polyfill.common.js
│       ├── polyfill.module.js
│       └── polyfill.nomodule.js
├── vendor-public/
│   └── common.css
├── gulpfile.js
└── package.json

src/

コンパイルなどの処理をさせるファイルはこのディレクトリに配置します。

src/html/

HTML にコンパイルされる前のテンプレートファイルと、テンプレートから参照するためのデータファイルをこのディレクトリに配置します。配置されたファイルは、同じ階層を維持したままプロジェクトのルートディレクトリに対応付けされます。例えばsrc/html/product/drink.pug/product/drink.htmlとして出力されます。

サブディレクトリが指定されていれば、サブディレクトリ以下の階層に対応付けされます。

詳細はHTML テンプレートを参照してください。

src/css/

CSS にコンパイルされる前の Sass ファイルをこのディレクトリに配置します。src/css/main.scssをエントリーポイントとして、アセットディレクトリ直下にmain.bundle.cssとして出力されます。

src/js/

コンパイルされる前の JavaScript ファイルをこのディレクトリに配置します。src/js/main.jsをエントリーポイントとして、アセットディレクトリ直下にmain.bundle.jsとして出力されます。

ファイル内では次のグローバル変数を参照できます。

__DEV__は、開発時はtrueになり、本番用ビルドではfalseになります。

__BASE_PATH__は、デフォルトでは/になり、サブディレクトリが指定されていれば/path/to/subdir/のようになります。

public/

静的ファイルをこのディレクトリに配置します。各ファイルはプロジェクトのルートディレクトリに対応付けされます。public/favicon.ico/favicon.icoとして出力されます。

サブディレクトリが指定されていれば、サブディレクトリ以下の階層に対応付けされます。

vendor-public/

デプロイに含めないファイルをこのディレクトリに配置します。各ファイルはプロジェクトのルートディレクトリに対応付けされます。vendor-public/common.css/common.cssとして出力されます。

このディレクトリ内のファイルは本番用ビルドの実行時にdist/に出力されません。開発時には必要であっても、デプロイに含める必要はないファイルを配置できます。

サブディレクトリが指定されていても対応付けされるパスに影響しません

dist/

本番用ビルドの実行時にはこのディレクトリにファイルが出力されます。

サブディレクトリが指定されていれば、このディレクトリを基準としたサブディレクトリにファイルが出力されます。

このディレクトリ以下のファイルを直接編集することは推奨されません。

.tmp/

開発用サーバーの起動中に一時的に必要になるファイルがこのディレクトリに出力されます。

このディレクトリ以下のファイルを直接編集することは推奨されません。

HTML テンプレート

src/html/に配置されたファイルは、同じ階層を維持したままプロジェクトのルートディレクトリに対応付けされます。例えばsrc/html/foo/bar.pug/foo/bar.htmlとして出力されます。

ただし、_から始まるファイルおよびディレクトリは無視されます。

また、開発用サーバー起動中はリクエストされたファイルのみをコンパイルします。それによってファイル数の増大が開発中のビルド時間に影響しないようになっています。

データファイル

テンプレート内では変数としてデータファイルの内容を参照できます。データファイルの種類には 2 つあり、単一ページのみに適用されるデータファイルと、全てのページに適用されるデータファイルがあります。規約に沿ったファイル名でファイルを作成することでデータを設定できます(datapageを参照してください)。

データファイルの形式としては、JavaScript、YAML、JSON をサポートしています。

形式が JavaScript であれば、テンプレート内で使用する値かその値を返す関数をエクスポートしてください。関数が Promise を返す場合は、解決後の結果がデータとして読み込まれます。

また関数をエクスポートした場合にはabsPath関数などのテンプレート用のオブジェクトが渡されます。詳しくはconfig/siteGenerator/task.jsdefaultLocals変数を参照してください。

// ok
module.exports = {
  title: 'Document title',
  post: {
    title: 'Post title',
  },
}

// ok
module.exports = (_locals) => {
  return {
    title: 'Document title',
    post: {
      title: 'Post title',
    },
  }
}

// ok
module.exports = async (_locals) => {
  const post = await fetchPost()
  return {
    title: 'Document title',
    post,
  }
}

内容はいずれのファイル形式であってもキャッシュされず、ファイルをコンパイルするたびに毎回読み込み直されます。

拡張子が.js.yml.yaml.jsonの順に優先されます。

変数

テンプレートファイルではいくつかの変数を参照できます。

data

src/html/_data/直下のデータファイルはdata変数に対応付けされます。src/html/_data/meta.jsonというデータファイルは、data.metaからその内容を参照できます。

page

テンプレートファイルと同じ名前のデータファイルを参照できます。テンプレートファイルがsrc/html/page.pugであれば、データファイルはsrc/html/page.jsonになります。

page.path

テンプレートファイル自身の出力先のパスを参照できます。src/html/page.pug/page.htmlになります。また、index.htmlは省略可能なファイル名であるため、src/html/index.pug/になります。

サブディレクトリが指定されていてもこの値に影響はありません。サブディレクトリを含めた絶対パスはabsPath(page.path)で取得できます。

データファイルにpathが指定されていればこの値で上書きされます。

absPath(filePath)

filePathをプロジェクトのルートディレクトリを基準とした絶対パスに変換します。

サブディレクトリが指定されていれば、absPath('page.html')/path/to/subdir/page.htmlのようになります。

assetPath(filePath)

filePathアセットディレクトリを基準とした絶対パスに変換します。assetPath('ogp.png')/assets/ogp.pngになります。

サブディレクトリが指定されていれば、assetPath('ogp.png')/path/to/subdir/assets/page.htmlのようになります。

__DEV__

本番用ビルドが有効になっているかを判断できるフラグです。開発用サーバーが--prodフラグ無しで起動中のときにはfalseになります。開発用サーバーを--prodフラグ付きで起動したときか、本番用ビルドを実行したときにはtrueになります。

設定

サブディレクトリ

プロジェクトがデプロイされる URL の基準になるディレクトリを指定できます。ルートディレクトリにデプロイされるのであれば指定の必要はありません。

npm-scripts のstartコマンドおよびbuildコマンドに、--subdir path/to/subdirというように引数を追加することで指定できます。

  "scripts": {
-   "start": "gulp",
+   "start": "gulp --subdir path/to/subdir",
-   "build": "gulp build --prod"
+   "build": "gulp build --prod --subdir path/to/subdir"
  },

アセットディレクトリ

コンパイル後の CSS ファイルや JavaScript ファイルが出力されるディレクトリを変更できます。デフォルトでは/assets/です。

config/path.jsassetsPath変数を次のように編集すると変更できます。

// デフォルト
// `/assets/`
// `/path/to/subdir/assets/`
const assetsPath = path.join(basePath, 'assets')

// `/`
// `/path/to/subdir/`
const assetsPath = basePath

// `/assets/`
// `/assets/path/to/subdir/`
const assetsPath = path.join('assets', basePath)

独自フラグ

config/flag.jsを編集してビルド用のフラグを追加できます。

const isDev = !process.argv.includes('--prod')
const isStaging = process.argv.includes('--staging')

module.exports = {
  isDev,
  isStaging,
}

必要に応じてconfig/siteGenerator/task.jsconfig/webpack.config.jsへ変数を渡すことで参照できるようになります。

Server Side Includes

このプロジェクトで採用している Browsersync では Server Side Includes を有効にできます。gulp-tasks/serve.jsを編集して、オプションのrewriteRulesを次のように実装することで有効にできます。読み込む対象のファイルをvendor-public/に配置する場合の実装例です。

const fs = require('fs')

const serve = (done) => {
  bs.init(
    {
      // ...
      rewriteRules: [
        {
          match: /<!--#include virtual="(.+?)" -->/g,
          fn(_req, _res, _match, filePath) {
            const srcFilePath = join(vendorPublicDir, filePath)

            if (
              fs.existsSync(srcFilePath) &&
              fs.statSync(srcFilePath).isFile()
            ) {
              return fs.readFileSync(srcFilePath, 'utf8')
            } else {
              return `<strong style="color: red">\`${srcFilePath}\` could not be found</strong>`
            }
          },
        },
      ],
      // ...
    },
    done,
  )
}

これにより次のようなディレクティブが有効になります。

<body>
  <!--#include virtual="/includes/site-header.html" -->
  <main>
    ...
  </main>
  <!--#include virtual="/includes/site-footer.html" -->
</body>

テンプレートエンジン

このプロジェクトでは HTML テンプレートのためのテンプレートエンジンに Pug を採用していますが、別のテンプレートエンジンに置き換えることもできます。

config/siteGenerator/task.jstask関数を変更して必要に応じたテンプレートエンジンに変更してください。

またconfig/siteGenerator/index.jsinputExtに拡張子が指定されています。使用するテンプレートエンジンに合わせた指定に変更してください。

HTML の整形

テンプレートエンジンとして Pug を使用している場合、HTML ファイルは圧縮された状態で出力されます。出力される HTML ファイルを整形したければ、JS Beautifierなどのフォーマッターを使用してください。

gulp-jsbeautifierなどのプラグインをconfig/siteGenerator/task.jstask関数の末尾に追加することでフォーマットできるようになります。

const beautify = require('gulp-jsbeautifier')

const beautifyOpts = {
  indent_size: 2,
}

const task = (stream, handleError) => {
  return stream
    .pipe(data(readLocals))
    .on('error', handleError)
    .pipe(pug(pugOpts))
    .on('error', handleError)
    .pipe(beautify(beautifyOpts))
    .on('error', handleError)
}

レシピ

差分納品

納品ファイルの差分を管理する必要があれば、dist/を Git にコミットするようにします。.gitignoreを次のように変更してください。

# project build output
-/dist/
/.tmp/

リリースの毎に本番用ビルドを実行して、出力されるdist/を Git にコミットします。このコミットに Git のタグを付けておくと参照しやすくなります。

前回のリリースと対応するコミットがrelease-20180101の場合、次のコマンドで追加・変更したファイルのみを Zip ファイルとして出力できます。

git archive --format=zip --prefix=htdocs/ HEAD:dist `git diff --diff-filter=ACMR --name-only release-20180101 HEAD | grep "^dist/" | sed -e "s/dist\///"` > htdocs.zip

削除したファイルリストは次のコマンドで出力できます。

git diff release-20180101 --name-only --diff-filter=D | grep "^dist" | sed -e "s/dist\///" > deleted-files.txt