Migrate from Gulp to Webpack (#138)

This commit is contained in:
Alex Nelson 2018-12-03 14:44:11 -07:00 committed by Shawn Erquhart
parent 91f13a2d0f
commit c2e09f4e49
21 changed files with 7196 additions and 4650 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
node_modules/
dist/
site/data/webpack.json

View File

@ -2,7 +2,7 @@
**A Hugo boilerplate for creating truly epic websites**
This is a boilerplate for using [Hugo](https://gohugo.io/) as a static site generator and [Gulp](https://gulpjs.com/) + [Webpack](https://webpack.js.org/) as your asset pipeline.
This is a boilerplate for using [Hugo](https://gohugo.io/) as a static site generator and [Webpack](https://webpack.js.org/) as your asset pipeline.
Victor Hugo setup to use [PostCSS](http://postcss.org/) and [Babel](https://babeljs.io/) for CSS and JavaScript compiling/transpiling.
@ -30,25 +30,13 @@ While developing your website, use:
npm start
```
or
```bash
gulp server
```
or for developing your website with `hugo server --buildDrafts --buildFuture`, use:
```bash
npm run start-preview
npm run preview
```
or
```bash
gulp server-preview
```
Then visit http://localhost:3000/ *- or a new browser windows popped-up already -* to preview your new website. BrowserSync will automatically reload the CSS or refresh the whole page, when stylesheets or content changes.
Then visit http://localhost:3000/ _- or a new browser windows popped-up already -_ to preview your new website. Webpack Dev Server will automatically reload the CSS or refresh the whole page, when stylesheets or content changes.
### :package: Static build
@ -61,10 +49,10 @@ npm run build
To get a preview of posts or articles not yet published, run:
```bash
npm run build-preview
npm run build:preview
```
See [package.json](package.json#L7) or the included gulp file for all tasks.
See [package.json](package.json#L8) for all tasks.
## Structure
@ -77,8 +65,8 @@ See [package.json](package.json#L7) or the included gulp file for all tasks.
| | |--index.html // The index page
| |--static // Files in here ends up in the public folder
|--src // Files that will pass through the asset pipeline
| |--css // CSS files in the root of this folder will end up in /css/...
| |--js // app.js will be compiled to /app.js with babel
| |--css // Webpack will bundle imported css seperately
| |--index.js // index.js is the webpack entry for your css & js assets
```
## Basic Concepts
@ -97,22 +85,22 @@ use the `site/static` folder. Images, font-files, etc, all go there.
Files in the static folder end up in the web root. So a file called `site/static/favicon.ico`
will end up being available as `/favicon.ico` and so on...
The `src/js/app.js` file is the entrypoint for webpack and will be built to `/dist/app.js`.
The `src/index.js` file is the entrypoint for webpack and will be built to `/dist/main.js`
You can use **ES6** and use both relative imports or import libraries from npm.
Any CSS file directly under the `src/css/` folder will get compiled with [PostCSS Next](http://cssnext.io/)
to `/dist/css/{filename}.css`. Import statements will be resolved as part of the build.
Any CSS file imported into the `index.js` will be run through Webpack, compiled with [PostCSS Next](http://cssnext.io/), and
minified to `/dist/[name].[hash:5].css`. Import statements will be resolved as part of the build.
## Environment variables
To separate the development and production *- aka build -* stages, all gulp tasks run with a node environment variable named either `development` or `production`.
To separate the development and production _- aka build -_ stages, all gulp tasks run with a node environment variable named either `development` or `production`.
You can access the environment variable inside the theme files with `getenv "NODE_ENV"`. See the following example for a conditional statement:
{{ if eq (getenv "NODE_ENV") "development" }}You're in development!{{ end }}
All tasks starting with *build* set the environment variable to `production` - the other will set it to `development`.
All tasks starting with _build_ set the environment variable to `production` - the other will set it to `development`.
## Deploying to Netlify
@ -125,5 +113,4 @@ You can also click this button:
[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/netlify/victor-hugo)
## Enjoy!! 😸

View File

@ -1,104 +0,0 @@
import gulp from "gulp";
import {spawn} from "child_process";
import hugoBin from "hugo-bin";
import log from "fancy-log";
import pluginError from "plugin-error";
import flatten from "gulp-flatten";
import postcss from "gulp-postcss";
import cssImport from "postcss-import";
import postcssPresetEnv from "postcss-preset-env";
import BrowserSync from "browser-sync";
import webpack from "webpack";
import webpackConfig from "./webpack.conf";
import minifyHtml from "gulp-htmlmin";
const browserSync = BrowserSync.create();
// Hugo arguments
const hugoArgsDefault = ["-d", "../dist", "-s", "site", "-v"];
const hugoArgsPreview = ["--buildDrafts", "--buildFuture"];
// Development tasks
gulp.task("hugo", (cb) => buildSite(cb));
gulp.task("hugo-preview", (cb) => buildSite(cb, hugoArgsPreview));
// Run server tasks
gulp.task("server", ["hugo", "css", "js", "fonts"], (cb) => runServer(cb));
gulp.task("server-preview", ["hugo-preview", "css", "js", "fonts"], (cb) => runServer(cb, "hugo-preview"));
// Build/production tasks
gulp.task("build", ["css", "js", "fonts"], (cb) => buildSite(cb, [], "production"));
gulp.task("build-preview", ["css", "js", "fonts"], (cb) => buildSite(cb, hugoArgsPreview, "production"));
// Compile CSS with PostCSS
gulp.task("css", () => (
gulp.src("./src/css/*.css")
.pipe(postcss([cssImport({from: "./src/css/main.css"}), postcssPresetEnv()]))
.pipe(gulp.dest("./dist/css"))
.pipe(browserSync.stream())
));
// Compile Javascript
gulp.task("js", (cb) => {
const myConfig = Object.assign({}, webpackConfig);
webpack(myConfig, (err, stats) => {
if (err) throw new pluginError("webpack", err);
log(`[webpack] ${stats.toString({
colors: true,
progress: true
})}`);
browserSync.reload();
cb();
});
});
// Move all fonts in a flattened directory
gulp.task('fonts', () => (
gulp.src("./src/fonts/**/*")
.pipe(flatten())
.pipe(gulp.dest("./dist/fonts"))
.pipe(browserSync.stream())
));
// Development server with browsersync
function runServer(cb, hugoTask = "hugo") {
browserSync.init({
server: {
baseDir: "./dist"
}
});
gulp.watch("./src/js/**/*.js", ["js"]);
gulp.watch("./src/css/**/*.css", ["css"]);
gulp.watch("./src/fonts/**/*", ["fonts"]);
gulp.watch("./site/**/*", [hugoTask]);
};
// Html minify - gulp-htmlmin
gulp.task('minifyHtml', function() {
return gulp.src('./dist/**/*.html')
// Options below: https://github.com/kangax/html-minifier
.pipe(minifyHtml({collapseWhitespace: true}))
.pipe(minifyHtml({includeAutoGeneratedTags: false}))
.pipe(minifyHtml({removeComments: true}))
.pipe(gulp.dest('./dist'));
});
/**
* Run hugo and build the site
*/
function buildSite(cb, options, environment = "development") {
const args = options ? hugoArgsDefault.concat(options) : hugoArgsDefault;
process.env.NODE_ENV = environment;
return spawn(hugoBin, args, {stdio: "inherit"}).on("close", (code) => {
if (code === 0) {
browserSync.reload();
cb();
} else {
browserSync.notify("Hugo build failed :(");
cb("Hugo build failed");
}
});
}

View File

@ -7,4 +7,4 @@
publish = "dist"
[context.deploy-preview]
command = "npm run build-preview"
command = "npm run build:preview"

7126
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,12 +5,19 @@
"repository": "netlify/victor-hugo",
"main": "index.js",
"scripts": {
"hugo": "gulp hugo",
"build": "gulp build && gulp minifyHtml",
"build-preview": "gulp build-preview",
"start": "gulp server",
"start-preview": "gulp server-preview",
"lint": "eslint src"
"lint": "eslint src",
"start": "run-p start:**",
"start:hugo": "hugo -d ../dist -s site -vw",
"start:webpack": "webpack-dev-server --config webpack.dev.js",
"preview": "run-p preview:**",
"preview:hugo": "npm run start:hugo -- -D -F",
"preview:webpack": "npm run start:webpack",
"prebuild": "rimraf dist",
"build": "npm run build:webpack && npm run build:hugo",
"build:preview": "npm run build:webpack && npm run build:hugo:preview",
"build:hugo": "hugo -d ../dist -s site -v",
"build:hugo:preview": "npm run build:hugo -- -D -F",
"build:webpack": "cross-env NODE_ENV=production webpack --config webpack.prod.js --hot --inline"
},
"author": "",
"license": "MIT",
@ -20,27 +27,37 @@
"@babel/plugin-syntax-object-rest-spread": "^7.0.0",
"@babel/preset-env": "^7.1.0",
"@babel/register": "^7.0.0",
"assets-webpack-plugin": "^3.9.7",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.4",
"browser-sync": "^2.26.3",
"clean-webpack-plugin": "^1.0.0",
"copy-webpack-plugin": "^4.6.0",
"cross-env": "^5.2.0",
"css-loader": "^1.0.1",
"eslint": "^5.8.0",
"eslint-plugin-import": "^2.14.0",
"exports-loader": "^0.7.0",
"fancy-log": "^1.3.2",
"file-loader": "^2.0.0",
"gulp": "^3.9.1",
"gulp-flatten": "^0.4.0",
"gulp-postcss": "^8.0.0",
"gulp-watch": "^5.0.1",
"gulp-htmlmin": "^5.0.1",
"hugo-bin": "^0.37.0",
"imports-loader": "^0.8.0",
"mini-css-extract-plugin": "^0.4.4",
"node-sass": "^4.10.0",
"npm-run-all": "^4.1.5",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"plugin-error": "^1.0.1",
"postcss-preset-env": "^6.3.0",
"postcss-import": "^12.0.1",
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.3.0",
"rimraf": "^2.6.2",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"uglifyjs-webpack-plugin": "^2.0.1",
"url-loader": "^1.1.2",
"webpack": "4.23.1",
"webpack": "^4.25.1",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10",
"webpack-merge": "^4.1.4",
"whatwg-fetch": "^3.0.0"
},
"resolutions": {

9
postcss.config.js Normal file
View File

@ -0,0 +1,9 @@
module.exports = {
plugins: {
"postcss-import": {},
"postcss-preset-env": {
browsers: "last 2 versions"
},
autoprefixer: {}
}
};

15
site/content/tech.md Normal file
View File

@ -0,0 +1,15 @@
---
title: 'tech'
date: 2018-11-14T19:02:50-07:00
draft: false
---
# [Victor Hugo](https://github.com/netlify-templates/victor-hugo)
## A Hugo boilerplate for creating truly epic websites
<img src="https://d33wubrfki0l68.cloudfront.net/30790d6888bd8af863fb2b5c33a7f337cdbda243/4e867/images/hugo-logo-wide.svg" style="width: 40%" />
This is a boilerplate for using [Hugo](https://gohugo.io/) as a static site generator and [Webpack](https://webpack.js.org/) as your asset pipeline. Victor Hugo setup to use [PostCSS](http://postcss.org/) and [Babel](https://babeljs.io/) for CSS and JavaScript compiling/transpiling. This project is released under the [MIT license](LICENSE). Please make sure you understand its implications and guarantees.
## Enjoy!! 😸

7
site/layouts/404.html Normal file
View File

@ -0,0 +1,7 @@
{{ define "main" }}
<h1>Nothing Here...</h1>
<h2 style="font-size: 120px; margin:0;">💩</h2>
<h3><a href="{{ "/" | relURL }}">Go Home</a></h3>
{{ end }}

View File

@ -1,21 +1,31 @@
<!doctype html>
<html lang="{{ $.Site.Language.Lang }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<base href="{{ if getenv "CONTEXT" }}{{ cond (eq "production" (getenv "CONTEXT")) (getenv "URL") (getenv "DEPLOY_PRIME_URL") }}{{ else }}{{ $.Site.BaseURL }}{{ end }}">
<title>{{ $.Site.Title }}</title>
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
{{ block "header" . }}{{ partial "header" . }}{{end}}
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<base href="{{ if getenv "CONTEXT" }}{{ cond (eq "production" (getenv "CONTEXT")) (getenv "URL") (getenv "DEPLOY_PRIME_URL") }}{{ else }}{{ $.Site.BaseURL }}{{ end }}">
<title>{{ $.Site.Title }}</title>
{{ block "main" . }}{{end}}
{{ $stylesheet := .Site.Data.webpack.main }}
{{ with $stylesheet.css }}
<link href="{{ relURL . }}" rel="stylesheet">
{{ end }}
</head>
</head>
{{ block "footer" . }}{{ partial "footer" . }}{{end}}
<body>
<script src="app.js"></script>
{{ block "header" . }}{{ partial "header" . }}{{end}}
{{ block "main" . }}{{end}}
{{ block "footer" . }}{{ partial "footer" . }}{{end}}
{{ $script := .Site.Data.webpack.main }}
{{ with $script.js }}
<script src="{{ relURL . }}"></script>
{{ end }}
</body>
</body>
</html>

View File

@ -0,0 +1,5 @@
{{ define "main" }} {{ $section := .Site.GetPage "section" .Section }}
<article class="content">
<main>{{- .Content -}}</main>
</article>
{{ end }}

View File

@ -1,9 +1,9 @@
{{ define "main" }}
<h1>Hugo with Gulp, PostCSS and Webpack</h1>
<h1>Hugo with Webpack, Sass, and PostCSS</h1>
<p>
Hooray 🎉 - you've built this with <a href="https://github.com/netlify/victor-hugo">Victor-Hugo</a>!
</p>
<h3>Hooray 🎉 - you've built this with <a href="https://github.com/netlify/victor-hugo">Victor-Hugo</a>!</h3>
{{end}}
<a href="{{ "/tech" | relURL }}">Check out the tech</a>
{{ end }}

View File

@ -3,7 +3,8 @@
*/
html,
body{
body {
margin: 0;
padding: 0;
height: 100%;
}

View File

@ -1,14 +1,32 @@
/*
You can use import statements to include partials:
*/
@import "imports/reset.css";
@import 'imports/reset.css';
/*
Or add your statements here:
*/
body{
font-family: sans-serif;
font-size: 1em;
body {
font-size: calc(10px + 1vmin);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans',
'Droid Sans', 'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
background-color: #282c34;
color: white;
}
a {
color: pink;
text-decoration: none;
}
.content {
padding: 0 32px;
}

6
src/index.js Normal file
View File

@ -0,0 +1,6 @@
// JS Goes here - ES6 supported
import "./css/main.css";
// Say hello
console.log("🦊 Hello! Edit me in src/index.js");

View File

@ -1,4 +0,0 @@
// JS Goes here - ES6 supported
// Say hello
console.log("🦊 Hello! Edit me in src/js/app.js");

59
webpack.common.js Normal file
View File

@ -0,0 +1,59 @@
const webpack = require("webpack");
const path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const AssetsPlugin = require("assets-webpack-plugin");
module.exports = {
entry: {
main: path.join(__dirname, "src", "index.js")
},
output: {
path: path.join(__dirname, "dist")
},
module: {
rules: [
{
test: /\.((png)|(eot)|(woff)|(woff2)|(ttf)|(svg)|(gif))(\?v=\d+\.\d+\.\d+)?$/,
loader: "file-loader?name=/[hash].[ext]"
},
{test: /\.json$/, loader: "json-loader"},
{
loader: "babel-loader",
test: /\.js?$/,
exclude: /node_modules/,
query: {cacheDirectory: true}
},
{
test: /\.(sa|sc|c)ss$/,
exclude: /node_modules/,
use: ["style-loader", MiniCssExtractPlugin.loader, "css-loader", "postcss-loader", "sass-loader"]
}
]
},
plugins: [
new webpack.ProvidePlugin({
fetch: "imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch"
}),
new AssetsPlugin({
filename: "webpack.json",
path: path.join(process.cwd(), "site/data"),
prettyPrint: true
}),
new CopyWebpackPlugin([
{
from: "./src/fonts/",
to: "fonts/",
flatten: true
}
])
]
};

View File

@ -1,37 +0,0 @@
import webpack from "webpack";
import path from "path";
export default {
module: {
rules: [
{
test: /\.((png)|(eot)|(woff)|(woff2)|(ttf)|(svg)|(gif))(\?v=\d+\.\d+\.\d+)?$/,
loader: "file-loader?name=/[hash].[ext]"
},
{test: /\.json$/, loader: "json-loader"},
{
loader: "babel-loader",
test: /\.js?$/,
exclude: /node_modules/,
query: {cacheDirectory: true}
}
]
},
plugins: [
new webpack.ProvidePlugin({
"fetch": "imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch"
})
],
context: path.join(__dirname, "src"),
entry: {
app: ["./js/app"]
},
output: {
path: path.join(__dirname, "dist"),
publicPath: "/",
filename: "[name].js"
},
externals: [/^vendor\/.+\.js$/]
};

36
webpack.dev.js Normal file
View File

@ -0,0 +1,36 @@
const merge = require("webpack-merge");
const path = require("path");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const common = require("./webpack.common");
module.exports = merge(common, {
mode: "development",
output: {
filename: "[name].js",
chunkFilename: "[id].css"
},
devServer: {
port: process.env.PORT || 3000,
contentBase: path.join(process.cwd(), "./dist"),
watchContentBase: true,
stats: "none",
quiet: false,
open: true,
historyApiFallback: {
rewrites: [{from: /./, to: "404.html"}]
}
},
plugins: [
new CleanWebpackPlugin(["dist/**/*.js", "dist/**/*.css", "site/content/webpack.json"]),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
]
});

32
webpack.prod.js Normal file
View File

@ -0,0 +1,32 @@
const merge = require("webpack-merge");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const common = require("./webpack.common.js");
module.exports = merge(common, {
mode: "production",
output: {
filename: "[name].[hash:5].js",
chunkFilename: "[id].[hash:5].css"
},
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true
}),
new MiniCssExtractPlugin({
filename: "[name].[hash:5].css",
chunkFilename: "[id].[hash:5].css"
}),
new OptimizeCSSAssetsPlugin({})
]
}
});

4242
yarn.lock

File diff suppressed because it is too large Load Diff