diff --git a/lib/index.js b/lib/index.js
index 79d43f5..300bfc6 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -12,6 +12,7 @@ const basePlugin = require('./plugins/base')
const logger = require('./logger')
const serveStatic = require('./utils/serveStatic')
const renderHtml = require('./utils/renderHtml')
+const minifyHtml = require('./utils/minifyHtml')
const emoji = require('./emoji')
const inspect = require('./utils/inspect')
const validateConfig = require('./validateConfig')
@@ -76,7 +77,8 @@ class Ream extends Event {
extract: !this.options.dev
},
pwa: false,
- minimize: !this.options.dev
+ minimize: !this.options.dev,
+ minifyHtml: false
}
// config from constructor can override user config
@@ -209,7 +211,7 @@ class Ream extends Event {
routes.map(async route => {
// Fake req
const context = { req: { url: route } }
- const html = await renderHtml(this.renderer, context)
+ const html = await this.renderHtml(context)
const targetPath = this.resolveOutDir(
`generated/${route.replace(/\/?$/, '/index.html')}`
)
@@ -335,7 +337,7 @@ class Ream extends Event {
}
const context = { req, res }
- const html = await renderHtml(this.renderer, context)
+ const html = await this.renderHtml(context)
res.setHeader('content-type', 'text/html')
res.end(html)
@@ -403,6 +405,14 @@ class Ream extends Event {
this.emit('renderer-ready', serverType)
}
+ async renderHtml(context) {
+ let html = await renderHtml(this.renderer, context)
+ if (this.config.minifyHtml) {
+ html = minifyHtml(html, this.config.minifyHtml)
+ }
+ return html
+ }
+
resolveOutDir(...args) {
return this.resolveBaseDir(this.config.outDir, ...args)
}
diff --git a/lib/utils/minifyHtml.js b/lib/utils/minifyHtml.js
new file mode 100644
index 0000000..0c1578e
--- /dev/null
+++ b/lib/utils/minifyHtml.js
@@ -0,0 +1,13 @@
+const { minify } = require('html-minifier')
+
+const defaultOptions = {
+ collapseWhitespace: true,
+ removeRedundantAttributes: true,
+ removeScriptTypeAttributes: true,
+ removeStyleLinkTypeAttributes: true,
+ useShortDoctype: true,
+ minifyCSS: true
+}
+
+module.exports = (html, options) =>
+ minify(html, options === true ? defaultOptions : options)
diff --git a/lib/validateConfig.js b/lib/validateConfig.js
index e7c453c..ee9a2ce 100644
--- a/lib/validateConfig.js
+++ b/lib/validateConfig.js
@@ -36,7 +36,8 @@ const schema = joi.object().keys({
// To add more controls, e.g. "notifyOnUpdate" will show a notifier when a new update is available
pwa: joi.boolean(),
minimize: joi.boolean(),
- defaultBabelPreset: joi.any().valid(['minimal', false])
+ defaultBabelPreset: joi.any().valid(['minimal', false]),
+ minifyHtml: joi.alternatives().try(joi.object(), joi.boolean())
})
module.exports = config => {
diff --git a/package.json b/package.json
index 0d3af1e..961fa0a 100644
--- a/package.json
+++ b/package.json
@@ -55,6 +55,7 @@
"file-loader": "^1.1.6",
"fs-extra": "^6.0.0",
"hash-sum": "^1.0.2",
+ "html-minifier": "^4.0.0",
"internal-ip": "^3.0.1",
"joi": "^13.6.0",
"joycon": "^1.0.4",
diff --git a/tap-snapshots/test-projects-html-minifier-index.test.js-TAP.test.js b/tap-snapshots/test-projects-html-minifier-index.test.js-TAP.test.js
new file mode 100644
index 0000000..63c449e
--- /dev/null
+++ b/tap-snapshots/test-projects-html-minifier-index.test.js-TAP.test.js
@@ -0,0 +1,58 @@
+/* IMPORTANT
+ * This snapshot file is auto-generated, but designed for humans.
+ * It should be checked into source control and tracked carefully.
+ * Re-generate by setting TAP_SNAPSHOT=1 and running tests.
+ * Make sure to inspect the output below. Do not ignore changes!
+ */
+'use strict'
+exports[`test/projects/html-minifier/index.test.js TAP test/projects/html-minifier > page 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`
+
+exports[`test/projects/html-minifier/index.test.js TAP test/projects/html-minifier > page 2`] = `
+
+`
+
+exports[`test/projects/html-minifier/index.test.js TAP test/projects/html-minifier > page 3`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`
diff --git a/test/lib/testProject.js b/test/lib/testProject.js
index 7bdce0f..cf05636 100644
--- a/test/lib/testProject.js
+++ b/test/lib/testProject.js
@@ -36,7 +36,7 @@ class Client {
}
}
-module.exports = function(baseDir, fn) {
+module.exports = function(baseDir, fn, config) {
// Calculate relative base directory for consistent snapshots.
const relativeBaseDir = path.relative('.', baseDir)
return tap.test(relativeBaseDir, async t => {
@@ -49,7 +49,8 @@ module.exports = function(baseDir, fn) {
css: {
extract: false
},
- minimize: false
+ minimize: false,
+ ...config
}
)
app.chainWebpack(config => {
diff --git a/test/projects/html-minifier/index.test.js b/test/projects/html-minifier/index.test.js
new file mode 100644
index 0000000..162aa20
--- /dev/null
+++ b/test/projects/html-minifier/index.test.js
@@ -0,0 +1,25 @@
+const testProject = require('../../lib/testProject')
+
+const configs = [
+ {
+ minifyHtml: false
+ },
+ {
+ minifyHtml: true
+ },
+ {
+ minifyHtml: {
+ minifyCSS: true
+ }
+ }
+]
+
+for (const config of configs) {
+ testProject(
+ __dirname,
+ async (t, c) => {
+ t.matchSnapshot((await c.axios.get('/')).data, 'page')
+ },
+ config
+ )
+}
diff --git a/test/projects/html-minifier/pages/index.vue b/test/projects/html-minifier/pages/index.vue
new file mode 100644
index 0000000..2a1dcfd
--- /dev/null
+++ b/test/projects/html-minifier/pages/index.vue
@@ -0,0 +1,11 @@
+
+
+ Test
+
+
+
+
diff --git a/test/projects/html-minifier/ream.config.js b/test/projects/html-minifier/ream.config.js
new file mode 100644
index 0000000..b3f4081
--- /dev/null
+++ b/test/projects/html-minifier/ream.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ fsRoutes: true
+}
diff --git a/yarn.lock b/yarn.lock
index 8d925ae..8352387 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2346,6 +2346,14 @@ callsites@^2.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
+camel-case@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
+ integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=
+ dependencies:
+ no-case "^2.2.0"
+ upper-case "^1.1.1"
+
camelcase-keys@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
@@ -2575,6 +2583,13 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
+clean-css@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17"
+ integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==
+ dependencies:
+ source-map "~0.6.0"
+
clean-regexp@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7"
@@ -2793,7 +2808,7 @@ commander@^2.14.1, commander@^2.9.0, commander@~2.17.1:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
-commander@~2.20.0:
+commander@^2.19.0, commander@~2.20.0:
version "2.20.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
@@ -5416,6 +5431,11 @@ he@^1.1.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0=
+he@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
+ integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+
hex-color-regex@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
@@ -5483,6 +5503,19 @@ html-entities@^1.2.0:
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=
+html-minifier@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-4.0.0.tgz#cca9aad8bce1175e02e17a8c33e46d8988889f56"
+ integrity sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==
+ dependencies:
+ camel-case "^3.0.0"
+ clean-css "^4.2.1"
+ commander "^2.19.0"
+ he "^1.2.0"
+ param-case "^2.1.1"
+ relateurl "^0.2.7"
+ uglify-js "^3.5.1"
+
http-cache-semantics@^3.8.1:
version "3.8.1"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2"
@@ -7139,6 +7172,11 @@ loud-rejection@^1.0.0, loud-rejection@^1.6.0:
currently-unhandled "^0.4.1"
signal-exit "^3.0.0"
+lower-case@^1.1.1:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
+ integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
+
lowercase-keys@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
@@ -7689,6 +7727,13 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4"
integrity sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==
+no-case@^2.2.0:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
+ integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
+ dependencies:
+ lower-case "^1.1.1"
+
node-emoji@^1.4.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.8.1.tgz#6eec6bfb07421e2148c75c6bba72421f8530a826"
@@ -8520,6 +8565,13 @@ parallel-transform@^1.1.0:
inherits "^2.0.3"
readable-stream "^2.1.5"
+param-case@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
+ integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc=
+ dependencies:
+ no-case "^2.2.0"
+
parse-asn1@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8"
@@ -9779,6 +9831,11 @@ regjsparser@^0.3.0:
dependencies:
jsesc "~0.5.0"
+relateurl@^0.2.7:
+ version "0.2.7"
+ resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
+ integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
+
release-zalgo@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730"
@@ -10484,7 +10541,7 @@ source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, sour
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
-source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
@@ -11359,7 +11416,7 @@ uglify-js@^2.6:
optionalDependencies:
uglify-to-browserify "~1.0.0"
-uglify-js@^3.1.4:
+uglify-js@^3.1.4, uglify-js@^3.5.1:
version "3.5.11"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.5.11.tgz#833442c0aa29b3a7d34344c7c63adaa3f3504f6a"
integrity sha512-izPJg8RsSyqxbdnqX36ExpbH3K7tDBsAU/VfNv89VkMFy3z39zFjunQGsSHOlGlyIfGLGprGeosgQno3bo2/Kg==
@@ -11569,6 +11626,11 @@ update-notifier@^2.3.0, update-notifier@^2.5.0:
semver-diff "^2.0.0"
xdg-basedir "^3.0.0"
+upper-case@^1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
+ integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
+
uri-js@^4.2.1:
version "4.2.2"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"