gulp+webpack+jQuery+pug+sassで開発環境構築
長いこといつか更新しないとと思っていた開発環境を時間があったので作り直しました! あれもこれもと機能充実させていったら、トータル 3 日ぐらい使ったかもしれません。。
LP ~数ページレベルの案件だと使い勝手が良いかなと思います。 大型案件向きではありません。
だいぶオレオレ開発環境なのですが、GitHubに置いておきますので、使いたい方は clone
して使ってみてください。
開発環境を変えた理由
開発環境を変えた理由はいくつかあります。
- gulp・node・webpack を最新にしたかった。
- gulp の watch 機能が微妙で、ファイル追加したときいちいち再起動が必要だった。
- js を webpack で bundle して 1 ファイルにまとめたかった。
- js を module 化して機能ごとに分離したかった。
- meta 周りを一括で変更できるようにしたかった。
- UnCSS で不要な CSS を削除したかった
- sass も node_modules から直接 import したかった。
- eslint + prettier を使ってきれいなコードを保ちたかった。
- es6 の記述で js 書きたかった。
・・・などなど、いろいろと不満があったのですが、これらをすべて解消した環境がようやくできました。 これらの機能を盛り込んで作ってみました。
機能紹介
開発環境
- gulp
- pug
- webpack
- babel
- sass
- eslint
- prettier
ライブラリ
- jQuery
- modaal
- GSAP(TweenMax/TimelineMax)
- ScrollMagic
- Swiper
コードなど紹介
ファイル構成
.babelrc .eslintrc.json .gitignore gulpfile.js package-lock.json package.json README.md webpack.config.js _src │ index.pug │ test.php │ ├─assets │ ├─images │ │ │ favicon.png │ │ │ │ │ └─video │ │ video.mp4 │ │ │ ├─js │ │ │ main.js │ │ │ │ │ └─modules │ │ animation.js │ │ modal.js │ │ slider.js │ │ │ └─sass │ │ main.scss │ │ utility.scss │ │ │ ├─foundation │ │ ├─base │ │ │ _base.scss │ │ │ _fonts.scss │ │ │ │ │ ├─mixin │ │ │ _bgi.scss │ │ │ _clearfix.scss │ │ │ _content.scss │ │ │ _mediaquerys.scss │ │ │ │ │ ├─reset │ │ │ _reset.scss │ │ │ │ │ ├─var │ │ │ _variable.scss │ │ │ │ │ └─vendor │ │ _vendor.scss │ │ │ ├─object │ │ _container.scss │ │ _test.scss │ │ _wrap.scss │ │ │ └─utility │ _display.scss │ _indent.scss │ _margin.scss │ _padding.scss │ ├─under │ index.pug │ ├─_data │ meta.json │ └─_include │ _layout.pug │ └─common _footer.pug _gabody.pug _gahead.pug _head.pug _header.pug
package.json
{ "name": "jws-template", "description": "gulp + webpack + jQuery + pug + sass", "version": "1.0.0", "author": "JWS", "browserslist": ["last 2 versions"], "dependencies": { "gsap": "^2.1.3", "jquery": "^3.4.1", "modaal": "^0.4.4", "scrollmagic": "^2.0.7", "scrollmagic-plugin-gsap": "^1.0.3" }, "devDependencies": { "@babel/core": "^7.6.3", "@babel/preset-env": "^7.6.3", "@babel/register": "^7.6.2", "babel-eslint": "^10.0.3", "babel-loader": "^8.0.6", "browser-sync": "^2.26.7", "connect-ssi": "^1.1.1", "del": "^5.1.0", "eslint": "^6.5.1", "eslint-config-prettier": "^6.4.0", "eslint-config-standard": "^14.1.0", "eslint-loader": "^3.0.2", "eslint-plugin-import": "^2.18.2", "eslint-plugin-node": "^10.0.0", "eslint-plugin-prettier": "^3.1.1", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", "gulp": "^4.0.2", "gulp-autoprefixer": "^7.0.1", "gulp-babel": "^8.0.0", "gulp-changed": "^4.0.2", "gulp-clean-css": "^4.2.0", "gulp-data": "^1.3.1", "gulp-group-css-media-queries": "^1.2.2", "gulp-imagemin": "^6.1.1", "gulp-notify": "^3.2.0", "gulp-plumber": "^1.2.1", "gulp-postcss": "^8.0.0", "gulp-prettify": "^0.5.0", "gulp-pug": "^4.0.1", "gulp-sass": "^4.0.2", "gulp-sass-glob": "^1.1.0", "gulp-uglify-es": "^1.0.4", "imagemin-mozjpeg": "^8.0.0", "imagemin-pngquant": "^8.0.0", "imports-loader": "^0.8.0", "postcss-import": "^12.0.1", "postcss-uncss": "^0.17.0", "prettier": "^1.18.2", "swiper": "^5.0.4", "terser-webpack-plugin": "^2.1.2", "uncss": "^0.17.2", "webpack": "^4.41.0", "webpack-cli": "^3.3.9", "webpack-stream": "^5.2.1" }, "license": "ISC", "main": "index.js", "scripts": { "dev": "npx gulp", "build": "npx gulp build", "test": "echo \"Error: no test specified\" && exit 1" } }
gulpfile.js
/* ========================================================= Import ========================================================= */ /* --------------------------------------------------------- common --------------------------------------------------------- */ const gulp = require('gulp'); const browserSync = require('browser-sync'); const ssi = require('connect-ssi'); const del = require('del'); const plumber = require('gulp-plumber'); const notify = require('gulp-notify'); /* --------------------------------------------------------- pug --------------------------------------------------------- */ const pug = require('gulp-pug'); const fs = require('fs'); const data = require('gulp-data'); /* --------------------------------------------------------- sass --------------------------------------------------------- */ const sass = require('gulp-sass'); const autoprefixer = require('gulp-autoprefixer'); const cleanCSS = require('gulp-clean-css'); const gcmq = require('gulp-group-css-media-queries'); const sassGlob = require('gulp-sass-glob'); const postcss = require('gulp-postcss'); const uncss = require('postcss-uncss'); const cssImport = require('postcss-import'); /* --------------------------------------------------------- javascript --------------------------------------------------------- */ const webpackStream = require('webpack-stream'); const webpack = require('webpack'); const babel = require('gulp-babel'); const uglify = require('gulp-uglify-es').default; const webpackConfig = require('./webpack.config'); /* --------------------------------------------------------- image --------------------------------------------------------- */ const imagemin = require('gulp-imagemin'); const mozjpeg = require('imagemin-mozjpeg'); const pngquant = require('imagemin-pngquant'); const changed = require('gulp-changed'); /* ========================================================= settings ========================================================= */ const paths = { sass: [ './_src/assets/sass/*.scss', './_src/assets/sass/`/*.scss', './_src/assets/sass/`/`/*.scss' ], sassDist: './_dist/assets/css/', css: ['./_dist/assets/css/*.css'], pug: [ './_src/*.pug', './_src/`/*.pug', './_src/`/`/*.pug', '!./_src/_*.pug', '!./_src/`/_*.pug', '!./_src/`/`/_*.pug' ], pugDist: './_dist/', html: ['./_src/*.html', './_src/`/*.html'], htmlDist: ['./_dist/*.html', './_dist/`/*.html'], php: ['./_src/*.php', './_src/`/*.php'], phpDist: './_dist/', js: ['./_src/assets/js/*.js', './_src/assets/js/`/*.js'], jsDist: './_dist/assets/js/', image: ['./_src/assets/images/`/*'], imageDist: './_dist/assets/images/', dist: './_dist/', meta: './_src/_data/meta.json' }; /* ========================================================= Task ========================================================= */ /* --------------------------------------------------------- pug --------------------------------------------------------- */ function pugFunc(done) { const option = { pretty: true }; return gulp .src(paths.pug) .pipe( plumber({ errorHandler: notify.onError('Error: <%= error.message %>') }) ) .pipe( data(function () { return JSON.parse(fs.readFileSync(paths.meta)); }) ) .pipe(pug(option)) .pipe(gulp.dest(paths.pugDist)) .pipe(browserSync.reload({ stream: true })); } /* --------------------------------------------------------- sass --------------------------------------------------------- */ function sassFunc() { const plugins = [ cssImport({ path: ['node_modules'] }) ]; return gulp .src(paths.sass, { sourcemaps: true }) .pipe(plumber()) .pipe(sassGlob()) .pipe( sass({ outputStyle: 'expanded' }) ) .pipe(postcss(plugins)) .pipe(cleanCSS()) .pipe( autoprefixer({ cascade: false }) ) .pipe( gulp.dest(paths.sassDist, { sourcemaps: './sourcemaps' }) ) .pipe( browserSync.reload({ stream: true }) ); } function sassDistFunc() { const plugins = [ cssImport({ path: ['node_modules'] }) ]; return gulp .src(paths.sass, { sourcemaps: false }) .pipe(plumber()) .pipe(sassGlob()) .pipe( sass({ outputStyle: 'compressed' }) ) .pipe(postcss(plugins)) .pipe(cleanCSS()) .pipe( autoprefixer({ cascade: false }) ) .pipe(gcmq()) .pipe( sass({ outputStyle: 'compressed' }) ) .pipe(gulp.dest(paths.sassDist)) .pipe( browserSync.reload({ stream: true }) ); } /* --------------------------------------------------------- uncss --------------------------------------------------------- */ function uncssFunc(done) { const plugins = [ uncss({ html: paths.htmlDist, ignore: [] }) ]; gulp .src('_dist/assets/css/utility.css') .pipe( plumber({ errorHandler: notify.onError('Error: <%= error.message %>') }) ) .pipe(postcss(plugins)) .pipe(gulp.dest('_dist/assets/css/')); done(); } /* --------------------------------------------------------- image --------------------------------------------------------- */ function imageFunc() { return gulp .src(paths.image) .pipe(changed(paths.imageDist)) .pipe( imagemin( [ mozjpeg({ quality: 80 }), pngquant() ], { verbose: true } ) ) .pipe(gulp.dest(paths.imageDist)) .pipe( browserSync.reload({ stream: true }) ); } /* --------------------------------------------------------- js --------------------------------------------------------- */ function jsFunc() { return plumber({ errorHandler: notify.onError('Error: <%= error.message %>') }) .pipe(webpackStream(webpackConfig, webpack)) .pipe(babel()) .pipe(uglify()) .pipe(gulp.dest(paths.jsDist)) .pipe( browserSync.reload({ stream: true }) ); } /* --------------------------------------------------------- php --------------------------------------------------------- */ function phpFunc() { return gulp .src(paths.php) .pipe(gulp.dest(paths.phpDist)) .pipe( browserSync.reload({ stream: true }) ); } /* --------------------------------------------------------- server --------------------------------------------------------- */ const browserSyncOptions = { port: 3000, reloadOnRestart: true, server: { baseDir: paths.dist, index: 'index.html', middleware: [ ssi({ baseDir: __dirname + '/_dist', ext: '.html' }) ] }, ghostMode: { clicks: false, forms: false, scroll: false } }; function browserSyncFunc(done) { browserSync.init(browserSyncOptions); done(); } /* --------------------------------------------------------- clean --------------------------------------------------------- */ function cleanFunc(done) { del.sync(['./_dist']); done(); } /* --------------------------------------------------------- watch --------------------------------------------------------- */ function watchFunc(done) { const browserReload = function () { browserSync.reload({ stream: true }); done(); }; gulp.watch(paths.pug).on('change', gulp.series(pugFunc, browserReload)); gulp.watch(paths.sass).on('change', gulp.series(sassFunc, browserReload)); gulp.watch(paths.js).on('change', gulp.series(jsFunc, browserReload)); gulp.watch(paths.image).on('change', gulp.series(imageFunc, browserReload)); gulp.watch(paths.php).on('change', gulp.series(phpFunc, browserReload)); done(); } /* ========================================================= Task main ========================================================= */ /* --------------------------------------------------------- gulp --------------------------------------------------------- */ gulp.task( 'default', gulp.series( gulp.parallel(pugFunc, sassFunc, jsFunc, imageFunc, phpFunc), gulp.series(browserSyncFunc, watchFunc) ) ); gulp.task( 'build', gulp.series( gulp.series(cleanFunc), gulp.parallel(pugFunc, sassDistFunc, jsFunc, imageFunc, phpFunc), gulp.series(uncssFunc) ) ); gulp.task('uncss', gulp.series(uncssFunc));
.babelrc
{ "presets": [ [ "@babel/preset-env", { "targets": ">0.25% in JP, not ie <= 10, not op_mini all" } ] ] }
.eslintrc.json
{ "extends": ["standard", "prettier", "plugin:prettier/recommended"], "env": { "browser": true }, "plugins": ["prettier"], "rules": { "prettier/prettier": [ "error", { "singleQuote": true, "semi": true, "tabWidth": 2 } ], "yoda": 0, "no-unused-vars": 0, "import/no-webpack-loader-syntax": 0, "no-undef": 0, "no-path-concat": 0 }, "globals": { "$": false } }
まとめ
長々とコードの羅列をしました。。。 Gulp v3 から v4 に変わっていろいろと文法が変わって苦戦しました。 開発環境は、1 年ぐらいで見直した方がよさそうです。
説明を書かないといけないんだけど、まぁとにかくやっていることが多すぎて書ききれない感じですね。 前職の先輩コーダーの方の開発コードを参考にしてみましたが、他にもいろいろ便利機能を実装されていて、凄さを実感しました!
大型案件だとこれぐらいじゃ回せないですが、LP ~数ページレベルの案件だと使い勝手がいいような環境です。 とりあえずこれをベースに開発して、ほかにも便利なものがあったら更新していくイメージでいこうと思います。 コード一式はこちらに置いておきます。