はじめに
この記事ではNode.jsのモジュールバンドラであるwebpackを使用して静的サイトジェネレータを構築する方法を紹介します。
全体の流れ
全体の流れを下記に示します。
- ワークスペースの作成
- package.jsonの作成 | npm init
- パッケージのインストール | npm install
- ディレクトリとファイルの作成
- まずはwebpackを動かす
- HTML5 Boilerplateで仮組みする
- Jadeで構造を記述する
- Lessで外観を記述する
- JavaScriptで振る舞いを記述する
- Gulpでビルド手順を記述する
- Connectで成果物を確認する
ワークスペースの作成
コマンドを下記にし示します。
mkdir -p ~/workspace/js/practice/static-site-generator
cd ~/workspace/js/practice/static-site-generator
package.jsonの作成 | npm init
コマンドを下記に示します。
npm init -f
パッケージのインストール | npm install
コマンドを下記に示します。
npm install --save-dev connect css-loader gulp gulp-jade jade jade-loader less-loader rimraf serve-static style-loader webpack webpack-dev-server
ディレクトリとファイルの作成
コマンドを下記に示します。
mkdir bin
mkdir public
mkdir src
mkdir src/style
mkdir src/templates
mkdir src/templates/about
mkdir src/templates/layouts
touch bin/www
touch src/style/main.less
touch src/style/sub.less
touch src/templates/about/index.jade
touch src/templates/layouts/default.jade
touch src/templates/index.jade
touch src/entry-dev.js
touch src/entry.js
touch gulpfile.js
touch webpack.config.js
まずはwebpackを動かす
webpack.config.jsの編集
webpack.config.js
の内容を下記に示します。
'use strict';
var path = require('path')
module.exports = {
entry: './src/entry-dev.js',
output: {
path: path.join(__dirname, 'dist'),
filename: 'js/bundle.js',
},
module: {
loaders: [
{ test: /\.jade$/, loader: 'jade' },
{ test: /\.less$/, loader: 'style!css!less' },
],
},
}
package.jsonの編集
package.json
を内容を下記に示します。
{
"name": "static-site-generator",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node_modules/.bin/webpack-dev-server --inline --history-api-fallback --content-base public --port 8080",
"build": "node_modules/.bin/gulp build",
"serve": "node bin/www",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"connect": "^3.4.0",
"css-loader": "^0.23.1",
"gulp": "^3.9.0",
"gulp-jade": "^1.1.0",
"gulp-less": "^3.0.5",
"jade": "^1.11.0",
"jade-loader": "^0.8.0",
"less-loader": "^2.2.2",
"rimraf": "^2.4.5",
"serve-static": "^1.10.0",
"style-loader": "^0.13.0",
"webpack": "^1.12.9",
"webpack-dev-server": "^1.14.0"
}
}
webpack-dev-serverの起動
コマンドを下記に示します。
npm start
動作確認
ブラウザで http://127.0.0.1:8080/ へアクセスします。現在はpublic
ディレクトリに何もないので404 Not Foundが表示されます。
HTML5 Boilerplateで仮組みする
ダウンロード
- http://www.initializr.com/ にアクセスします
- 1 - Pre ConfigurationでBootstrapをクリックします
- 2 - Fine Configurationで下記を選びます
- HTML/CSS Template: Twitter Bootstrap
- HTML5 Polyfils: Modernizr (Respondのチェックを外します)
- jQuery: Minified
- H5BP Optional: すべてのチェックを外します。
- Download it! ボタンをクリックします
- ZIPファイルのダウンロードが開始します
展開
ダウンロードしたZIPをpublic
ディレクトリ内に展開します。
動作確認
ブラウザで http://127.0.0.1:8080/ へアクセスするとBootstrapのページが表示されます。
Jadeで構造を記述する
index.htmlの編集
public/index.html
の内容を下記に示します。
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="/css/main.css">
<!-- separator -->
<script src="/js/vendor/modernizr-2.8.3.min.js"></script>
</head>
<body>
<!-- separator -->
<main></main>
<script src="/js/vendor/jquery-1.11.2.min.js"></script>
<script src="/js/vendor/bootstrap.min.js"></script>
<script src="/js/bundle.js"></script>
<!-- separator -->
</body>
</html>
entry-dev.jsの編集
entry-dev.js
の内容を下記に示します。
'use strict';
require('./entry')
require('./style/main.less')
var context = require.context('./templates/', true, /\.jade$/)
var path = './' + window.location.pathname.slice(1) + 'index.jade'
var template = context(path)
var context = {
baseUrl: '',
parts: [],
}
$('main').html(template(context))
テンプレートの編集
src/templates/
ディレクトリ内のabout/index.jade
、layouts/default.jade
、index.jade
の内容を下記に示します。
extends ../layouts/default
block content
.container
p This is about page.
.row
.col-md-offset-3.col-md-6
a.btn.btn-default.btn-block(href='../') Back
!= parts[0]
block style
!= parts[1]
nav.navbar.navbar-default.navbar-static-top
.container
.navbar-header
a.navbar-brand(href='#') Project name
block content
.container
hr
footer
p © Company 2015
!= parts[2]
block script
!= parts[3]
extends ./layouts/default
block content
.jumbotron
.container
h1 Hello, world!
p
| This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.
p
a.btn.btn-primary.btn-lg(href='#{baseUrl}/about/', role='button') Learn more »
.container
.row
.col-md-4
h2 Heading
p
| Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.
p
a.btn.btn-default(href='#', role='button') View details »
.col-md-4
h2 Heading
p
| Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.
p
a.btn.btn-default(href='#', role='button') View details »
.col-md-4
h2 Heading
p
| Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.
p
a.btn.btn-default(href='#', role='button') View details »
動作確認
ブラウザで http://127.0.0.1:8080/ へアクセスするとIndexページが表示されます。ジャンボトロン内のLearn MoreをクリックするとAboutページへ移動します。AboutページのBackボタンをクリックするとIndexページへ移動します。
Lessで外観を記述する
main.lessの編集
main.less
の内容を下記に示します。
@import "./sub.less";
sub.lessの編集
sub.less
の内容を下記に示します。
body {
background: gray;
}
動作確認
ブラウザで http://127.0.0.1:8080/ へアクセスすると灰色背景のIndexページが表示されます。
JavaScriptで振る舞いを記述する
JavaScriptで振る舞いを記述する方法は2つあります。
-
entry.js
に振る舞いを記述する - Jadeファイルのscriptブロックに振る舞いを記述する
個人的にはentry.js
に関数やクラスを記述してJadeファイルのscriptブロックからそれらを呼び出す書き方が好みです。
'use strict';
window.printHelloWorld = function () {
console.log('Hello, world!')
}
'use strict';
extends ../layouts/default
block content
.container
p This is about page.
.row
.col-md-offset-3.col-md-6
a.btn.btn-default.btn-block(href='../') Back
block script
script.
(function () {
'use strict';
printHelloWorld()
})();
Gulpでビルド手順を記述する
gulpfile.jsの編集
gulpfile.js
の内容を下記に示します。
'use strict';
var fs = require('fs')
var path = require('path')
var gulp = require('gulp')
var rimraf = require('rimraf')
var webpack = require('webpack')
var gulpJade = require('gulp-jade')
var gulpLess = require('gulp-less')
var webpackConfig = require('./webpack.config')
gulp.task('clean', function (cb) {
rimraf('./dist/', cb)
})
gulp.task('copy', ['clean'], function () {
return gulp.src([
'./public/**',
'!./public/index.html',
'!./public/css/main.css',
])
.pipe(gulp.dest('./dist/'))
})
gulp.task('jade', ['clean'], function () {
var root = fs.readFileSync(path.join(__dirname, './public/index.html')).toString()
var parts = root.replace('<main></main>', '').split('<!-- separator -->')
return gulp.src([
'./src/templates/**/*.jade',
'!./src/templates/layouts/**/*.jade',
])
.pipe(gulpJade({
locals: {
baseUrl: '',
parts: parts,
},
}))
.pipe(gulp.dest('./dist/'))
})
gulp.task('less', ['clean'], function () {
return gulp.src('./src/style/main.less')
.pipe(gulpLess())
.pipe(gulp.dest('./dist/css/'))
})
gulp.task('webpack', ['clean'], function (cb) {
webpackConfig.entry = './src/entry.js',
webpackConfig.plugins = (webpackConfig.plugins || []).concat([
new webpack.optimize.UglifyJsPlugin(),
])
webpack(webpackConfig, cb)
})
gulp.task('build', [
'clean',
'copy',
'jade',
'less',
'webpack',
])
gulp.task('default', ['build'])
ビルド
コマンドを下記に示します。
npm run build
ビルドが成功するとdist/
ディレクトリが生成されます。
Connectで成果物を確認する
bin/wwwの編集
bin/www
の内容を下記に示します。
#!/usr/bin/env node
'use strict';
var port = Number.parseInt(process.env.PORT || '3000', 10)
var http = require('http')
var path = require('path')
var connect = require('connect')
var serveStatic = require('serve-static')
var app = connect()
app.use(serveStatic(path.join(__dirname, '../dist')))
var server = http.createServer(app)
server.listen(port)
server.on('listening', function () {
console.log('Listening on ' + port)
})
サーバ起動
コマンドを下記に示します。
npm run serve
動作確認
ブラウザで http://127.0.0.1:3000/ へアクセスします。http://127.0.0.1:8080/ と同じように表示されたら成功です。
おわりに
webpackの豊富なローダーを使用すれば、Lessの代わりにSass/Scssで外観を記述したり、JavaScriptの代わりにCoffeeScriptで外観を記述したりすることが比較的簡単にできます。ご自身の状況に合わせてぜひとも色々とカスタマイズして役立てていただければ幸いです。
次回はElectron.jsを使用してWebアプリケーションをデスクトップアプリケーションにする方法を紹介する予定です。