diff --git a/examples/custom-layouts/layouts/error.vue b/examples/custom-layouts/layouts/error.vue index 3b4dc0ffc7..126edd0af2 100644 --- a/examples/custom-layouts/layouts/error.vue +++ b/examples/custom-layouts/layouts/error.vue @@ -1,7 +1,7 @@ diff --git a/examples/dynamic-layouts/layouts/error.vue b/examples/dynamic-layouts/layouts/error.vue index 61f4ddcebd..aa064f32ec 100644 --- a/examples/dynamic-layouts/layouts/error.vue +++ b/examples/dynamic-layouts/layouts/error.vue @@ -1,7 +1,7 @@ diff --git a/examples/typescript/store/modules/people.ts b/examples/typescript/store/modules/people.ts index e73da3a343..25f3fb6f4a 100644 --- a/examples/typescript/store/modules/people.ts +++ b/examples/typescript/store/modules/people.ts @@ -29,7 +29,7 @@ export interface Person { gender: string ip_address: string avatar: string - addres: PersonAddress + address: PersonAddress } export interface State { diff --git a/examples/web-worker/pages/index.vue b/examples/web-worker/pages/index.vue index ce7cc49ce3..eb307006c3 100644 --- a/examples/web-worker/pages/index.vue +++ b/examples/web-worker/pages/index.vue @@ -82,7 +82,7 @@ export default { if (worker) worker.postMessage({ hello: 'world' }) else this.notification = 'No more test workers available' }, - long (miliseconds) { + long (milliseconds) { let worker = this.workers.shift() if (worker) { @@ -96,7 +96,7 @@ export default { worker = this.longRunningWorkers[ this.longIndex++ % this.longRunningWorkers.length] } - worker.postMessage({ action: 'expensive', time: miliseconds }) + worker.postMessage({ action: 'expensive', time: milliseconds }) }, freeWorker () { // we can't really free a worker, we can only terminate it and create a new diff --git a/examples/with-vuetify/nuxt.config.js b/examples/with-vuetify/nuxt.config.js index 050a1d9e14..90677abf77 100644 --- a/examples/with-vuetify/nuxt.config.js +++ b/examples/with-vuetify/nuxt.config.js @@ -14,8 +14,7 @@ export default { }, build: { - extractCSS: true, - transpile: [/^vuetify/] + extractCSS: true }, /* ** Load Vuetify into the app diff --git a/lib/app/client.js b/lib/app/client.js index feff703a95..8a8b01be40 100644 --- a/lib/app/client.js +++ b/lib/app/client.js @@ -674,7 +674,7 @@ async function mountApp(__app) { if (!path) { normalizeComponents(router.currentRoute, router.currentRoute) showNextPage.call(_app, router.currentRoute) - // Dont call fixPrepatch.call(_app, router.currentRoute, router.currentRoute) since it's first render + // Don't call fixPrepatch.call(_app, router.currentRoute, router.currentRoute) since it's first render mount() return } diff --git a/lib/builder/builder.js b/lib/builder/builder.js index b1190f638a..bafaccfa7d 100644 --- a/lib/builder/builder.js +++ b/lib/builder/builder.js @@ -59,6 +59,15 @@ export default class Builder { this.mfs = new MFS() } + if (this.options.build.analyze) { + this.nuxt.hook('build:done', () => { + consola.warn({ + message: 'Notice: Please do not deploy bundles built with analyze mode, it\'s only for analyzing purpose.', + badge: true + }) + }) + } + // if(!this.options.dev) { // TODO: enable again when unsafe concern resolved.(common/options.js:42) // this.nuxt.hook('build:done', () => this.generateConfig()) diff --git a/lib/builder/generator.js b/lib/builder/generator.js index c04ef183b5..407ca32794 100644 --- a/lib/builder/generator.js +++ b/lib/builder/generator.js @@ -4,7 +4,7 @@ import htmlMinifier from 'html-minifier' import Chalk from 'chalk' import fsExtra from 'fs-extra' import consola from 'consola' -import { isUrl, promisifyRoute, waitFor, flatRoutes } from '../common/utils' +import { flatRoutes, isUrl, promisifyRoute, waitFor } from '../common/utils' export default class Generator { constructor(nuxt, builder) { @@ -223,9 +223,19 @@ export default class Generator { return false } - if (this.options.generate.minify) { + let minificationOptions = this.options.build.html.minify + + // Legacy: Override minification options with generate.minify if present + // TODO: Remove in Nuxt version 3 + if (typeof this.options.generate.minify !== 'undefined') { + minificationOptions = this.options.generate.minify + consola.warn('generate.minify has been deprecated and will be removed in the next major version.' + + ' Use build.html.minify instead!') + } + + if (minificationOptions) { try { - html = htmlMinifier.minify(html, this.options.generate.minify) + html = htmlMinifier.minify(html, minificationOptions) } catch (err) /* istanbul ignore next */ { const minifyErr = new Error( `HTML minification failed. Make sure the route generates valid HTML. Failed HTML:\n ${html}` diff --git a/lib/builder/webpack/client.js b/lib/builder/webpack/client.js index 4ed5549aaf..f337374bfa 100644 --- a/lib/builder/webpack/client.js +++ b/lib/builder/webpack/client.js @@ -15,6 +15,16 @@ export default class WebpackClientConfig extends WebpackBaseConfig { super(builder, { name: 'client', isServer: false }) } + getFileName(...args) { + if (this.options.build.analyze) { + const key = args[0] + if (['app', 'chunk'].includes(key)) { + return '[name].js' + } + } + return super.getFileName(...args) + } + env() { return Object.assign(super.env(), { 'process.env.VUE_ENV': JSON.stringify('client'), @@ -53,7 +63,7 @@ export default class WebpackClientConfig extends WebpackBaseConfig { new HTMLPlugin({ filename: '../server/index.ssr.html', template: this.options.appTemplatePath, - minify: true, + minify: this.options.build.html.minify, inject: false // Resources will be injected using bundleRenderer }) ) @@ -63,7 +73,7 @@ export default class WebpackClientConfig extends WebpackBaseConfig { new HTMLPlugin({ filename: '../server/index.spa.html', template: this.options.appTemplatePath, - minify: true, + minify: this.options.build.html.minify, inject: true, chunksSortMode: 'dependency' }), @@ -99,31 +109,36 @@ export default class WebpackClientConfig extends WebpackBaseConfig { extendConfig() { const config = super.extendConfig(...arguments) - if (!this.options.dev && !config.optimization.minimizer) { + // Add minimizer plugins + if (config.optimization.minimize && config.optimization.minimizer === undefined) { config.optimization.minimizer = [] // https://github.com/webpack-contrib/terser-webpack-plugin - const terserJsPlugin = new TerserWebpackPlugin({ - parallel: true, - cache: this.options.build.cache, - sourceMap: config.devtool && /source-?map/.test(config.devtool), - extractComments: { - filename: 'LICENSES' - }, - terserOptions: { - output: { - comments: /^\**!|@preserve|@license|@cc_on/ - } - } - }) - config.optimization.minimizer.push(terserJsPlugin) + if (this.options.build.terser) { + config.optimization.minimizer.push( + new TerserWebpackPlugin(Object.assign({ + parallel: true, + cache: this.options.build.cache, + sourceMap: config.devtool && /source-?map/.test(config.devtool), + extractComments: { + filename: 'LICENSES' + }, + terserOptions: { + output: { + comments: /^\**!|@preserve|@license|@cc_on/ + } + } + }, this.options.build.terser)) + ) + } // https://github.com/NMFR/optimize-css-assets-webpack-plugin // https://github.com/webpack-contrib/mini-css-extract-plugin#minimizing-for-production // TODO: Remove OptimizeCSSAssetsPlugin when upgrading to webpack 5 - if (this.options.build.extractCSS) { - const optimizeCSSPlugin = new OptimizeCSSAssetsPlugin({}) - config.optimization.minimizer.push(optimizeCSSPlugin) + if (this.options.build.optimizeCSS) { + config.optimization.minimizer.push( + new OptimizeCSSAssetsPlugin(Object.assign({}, this.options.build.optimizeCSS)) + ) } } diff --git a/lib/common/nuxt.config.js b/lib/common/nuxt.config.js index 97004e3ec4..8eafd7f9ff 100644 --- a/lib/common/nuxt.config.js +++ b/lib/common/nuxt.config.js @@ -55,7 +55,7 @@ export default { // { isDev, isClient, isServer } app: ({ isDev }) => isDev ? '[name].js' : '[chunkhash].js', chunk: ({ isDev }) => isDev ? '[name].js' : '[chunkhash].js', - css: ({ isDev }) => isDev ? '[name].js' : '[contenthash].css', + css: ({ isDev }) => isDev ? '[name].css' : '[contenthash].css', img: ({ isDev }) => isDev ? '[path][name].[ext]' : 'img/[hash:7].[ext]', font: ({ isDev }) => isDev ? '[path][name].[ext]' : 'fonts/[hash:7].[ext]', video: ({ isDev }) => isDev ? '[path][name].[ext]' : 'videos/[hash:7].[ext]' @@ -87,8 +87,12 @@ export default { }, styleResources: {}, plugins: [], + terser: {}, + optimizeCSS: undefined, optimization: { runtimeChunk: 'single', + minimize: undefined, + minimizer: undefined, splitChunks: { chunks: 'all', automaticNameDelimiter: '.', @@ -112,6 +116,20 @@ export default { stage: 2 } }, + html: { + minify: { + collapseBooleanAttributes: true, + decodeEntities: true, + minifyCSS: true, + minifyJS: true, + processConditionalComments: true, + removeEmptyAttributes: true, + removeRedundantAttributes: true, + trimCustomFragments: true, + useShortDoctype: true + } + }, + templates: [], watch: [], devMiddleware: {}, @@ -136,27 +154,7 @@ export default { concurrency: 500, interval: 0, subFolders: true, - fallback: '200.html', - minify: { - collapseBooleanAttributes: true, - collapseWhitespace: false, - decodeEntities: true, - minifyCSS: true, - minifyJS: true, - processConditionalComments: true, - removeAttributeQuotes: false, - removeComments: false, - removeEmptyAttributes: true, - removeOptionalTags: false, - removeRedundantAttributes: true, - removeScriptTypeAttributes: false, - removeStyleLinkTypeAttributes: false, - removeTagWhitespace: false, - sortAttributes: true, - sortClassName: false, - trimCustomFragments: true, - useShortDoctype: true - } + fallback: '200.html' }, env: {}, head: { diff --git a/lib/common/options.js b/lib/common/options.js index 86749abad5..f2f5c56e6f 100644 --- a/lib/common/options.js +++ b/lib/common/options.js @@ -239,6 +239,16 @@ Options.from = function (_options) { options.build.extractCSS = false } + // Enable minimize for production builds + if (options.build.optimization.minimize === undefined) { + options.build.optimization.minimize = !options.dev + } + + // Enable optimizeCSS only when extractCSS is enabled + if (options.build.optimizeCSS === undefined) { + options.build.optimizeCSS = options.build.extractCSS ? {} : false + } + const loaders = options.build.loaders const vueLoader = loaders.vue if (vueLoader.productionMode === undefined) { diff --git a/package.json b/package.json index 91ff75121f..b331057e40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nuxt", - "version": "2.0.0", + "version": "2.1.0", "description": "A minimalistic framework for server-rendered Vue.js applications (inspired by Next.js)", "contributors": [ { @@ -69,19 +69,19 @@ "npm": ">=5.0.0" }, "dependencies": { - "@babel/core": "^7.1.0", + "@babel/core": "^7.1.2", "@babel/polyfill": "^7.0.0", "@nuxtjs/babel-preset-app": "^0.7.0", "@nuxtjs/friendly-errors-webpack-plugin": "^2.0.2", "@nuxtjs/opencollective": "^0.1.0", "@nuxtjs/youch": "^4.2.3", - "babel-loader": "^8.0.2", + "babel-loader": "^8.0.4", "cache-loader": "^1.2.2", - "caniuse-lite": "^1.0.30000887", + "caniuse-lite": "^1.0.30000888", "chalk": "^2.4.1", "chokidar": "^2.0.4", "compression": "^1.7.3", - "connect": "^3.6.5", + "connect": "^3.6.6", "consola": "^1.4.3", "css-loader": "^1.0.0", "cssnano": "^4.1.4", @@ -92,9 +92,9 @@ "file-loader": "^2.0.0", "fresh": "^0.5.2", "fs-extra": "^7.0.0", - "glob": "^7.1.2", + "glob": "^7.1.3", "hash-sum": "^1.0.2", - "html-minifier": "^3.5.19", + "html-minifier": "^3.5.20", "html-webpack-plugin": "^3.2.0", "ip": "^1.1.5", "launch-editor-middleware": "^2.2.1", @@ -105,11 +105,11 @@ "minimist": "^1.2.0", "optimize-css-assets-webpack-plugin": "^5.0.1", "pify": "^4.0.0", - "postcss": "^7.0.2", + "postcss": "^7.0.4", "postcss-import": "^12.0.0", "postcss-import-resolver": "^1.1.0", "postcss-loader": "^3.0.0", - "postcss-preset-env": "^6.0.3", + "postcss-preset-env": "^6.0.7", "postcss-url": "^8.0.0", "semver": "^5.5.1", "serialize-javascript": "^1.5.0", @@ -124,7 +124,7 @@ "url-loader": "^1.1.1", "vue": "^2.5.17", "vue-loader": "^15.4.2", - "vue-meta": "1.5.3", + "vue-meta": "^1.5.5", "vue-no-ssr": "^1.0.0", "vue-router": "^3.0.1", "vue-server-renderer": "^2.5.17", @@ -139,14 +139,14 @@ }, "devDependencies": { "@babel/preset-env": "^7.1.0", - "babel-core": "^7.0.0-0", - "babel-eslint": "^10.0.0", + "babel-core": "^7.0.0-bridge", + "babel-eslint": "^10.0.1", "babel-jest": "^23.6.0", "babel-plugin-dynamic-import-node": "^2.1.0", "codecov": "^3.1.0", "cross-env": "^5.2.0", "cross-spawn": "^6.0.5", - "eslint": "^5.6.0", + "eslint": "^5.6.1", "eslint-config-standard": "^12.0.0", "eslint-config-standard-jsx": "^6.0.2", "eslint-plugin-import": "^2.14.0", @@ -156,7 +156,7 @@ "eslint-plugin-react": "^7.11.1", "eslint-plugin-standard": "^4.0.0", "eslint-plugin-vue": "^5.0.0-beta.3", - "express": "^4.16.2", + "express": "^4.16.3", "finalhandler": "^1.1.1", "get-port": "^4.0.0", "jest": "^23.6.0", diff --git a/packages/nuxt-legacy/package.json b/packages/nuxt-legacy/package.json index 787134f337..916e026370 100644 --- a/packages/nuxt-legacy/package.json +++ b/packages/nuxt-legacy/package.json @@ -55,19 +55,19 @@ "npm": ">=5.0.0" }, "dependencies": { - "@babel/core": "^7.1.0", + "@babel/core": "^7.1.2", "@babel/polyfill": "^7.0.0", "@nuxtjs/babel-preset-app": "^0.7.0", "@nuxtjs/friendly-errors-webpack-plugin": "^2.0.2", "@nuxtjs/opencollective": "^0.1.0", "@nuxtjs/youch": "^4.2.3", - "babel-loader": "^8.0.2", + "babel-loader": "^8.0.4", "cache-loader": "^1.2.2", - "caniuse-lite": "^1.0.30000887", + "caniuse-lite": "^1.0.30000888", "chalk": "^2.4.1", "chokidar": "^2.0.4", "compression": "^1.7.3", - "connect": "^3.6.5", + "connect": "^3.6.6", "consola": "^1.4.3", "css-loader": "^1.0.0", "cssnano": "^4.1.4", @@ -78,9 +78,9 @@ "file-loader": "^2.0.0", "fresh": "^0.5.2", "fs-extra": "^7.0.0", - "glob": "^7.1.2", + "glob": "^7.1.3", "hash-sum": "^1.0.2", - "html-minifier": "^3.5.19", + "html-minifier": "^3.5.20", "html-webpack-plugin": "^3.2.0", "ip": "^1.1.5", "launch-editor-middleware": "^2.2.1", @@ -91,11 +91,11 @@ "minimist": "^1.2.0", "optimize-css-assets-webpack-plugin": "^5.0.1", "pify": "^4.0.0", - "postcss": "^7.0.2", + "postcss": "^7.0.4", "postcss-import": "^12.0.0", "postcss-import-resolver": "^1.1.0", "postcss-loader": "^3.0.0", - "postcss-preset-env": "^6.0.3", + "postcss-preset-env": "^6.0.7", "postcss-url": "^8.0.0", "semver": "^5.5.1", "serialize-javascript": "^1.5.0", @@ -110,7 +110,7 @@ "url-loader": "^1.1.1", "vue": "^2.5.17", "vue-loader": "^15.4.2", - "vue-meta": "^1.5.4", + "vue-meta": "^1.5.5", "vue-no-ssr": "^1.0.0", "vue-router": "^3.0.1", "vue-server-renderer": "^2.5.17", diff --git a/packages/nuxt-start/package.json b/packages/nuxt-start/package.json index f932da17e3..0b7267de74 100644 --- a/packages/nuxt-start/package.json +++ b/packages/nuxt-start/package.json @@ -58,7 +58,7 @@ "@nuxtjs/youch": "^4.2.3", "chalk": "^2.4.1", "compression": "^1.7.3", - "connect": "^3.6.5", + "connect": "^3.6.6", "consola": "^1.4.3", "devalue": "^1.0.4", "esm": "^3.0.84", @@ -75,7 +75,7 @@ "server-destroy": "^1.0.1", "std-env": "^1.3.1", "vue": "^2.5.17", - "vue-meta": "^1.5.4", + "vue-meta": "^1.5.5", "vue-no-ssr": "^1.0.0", "vue-router": "^3.0.1", "vue-server-renderer": "^2.5.17", diff --git a/test/fixtures/with-config/with-config.test.js b/test/fixtures/with-config/with-config.test.js index 3de1cbe586..d16e0f0a1e 100644 --- a/test/fixtures/with-config/with-config.test.js +++ b/test/fixtures/with-config/with-config.test.js @@ -14,13 +14,19 @@ const hooks = [ describe('with-config', () => { buildFixture('with-config', () => { - expect(consola.warn).toHaveBeenCalledTimes(1) + expect(consola.warn).toHaveBeenCalledTimes(2) expect(consola.fatal).toHaveBeenCalledTimes(0) - expect(consola.warn.mock.calls[0]).toMatchObject([{ - message: 'Found 2 plugins that match the configuration, suggest to specify extension:', - additional: expect.stringContaining('plugins/test.json'), - badge: true - }]) + expect(consola.warn.mock.calls).toMatchObject([ + [{ + message: 'Found 2 plugins that match the configuration, suggest to specify extension:', + additional: expect.stringContaining('plugins/test.json'), + badge: true + }], + [{ + message: 'Notice: Please do not deploy bundles built with analyze mode, it\'s only for analyzing purpose.', + badge: true + }] + ]) expect(customCompressionMiddlewareFunctionName).toBe('damn') }, hooks) }) diff --git a/test/unit/extract-css.test.js b/test/unit/extract-css.test.js index 51d3b6f4ff..74a5a30f16 100644 --- a/test/unit/extract-css.test.js +++ b/test/unit/extract-css.test.js @@ -9,7 +9,7 @@ describe('extract css', () => { test('Verify global.css has been extracted and minified', async () => { const pathToMinifiedGlobalCss = resolve(__dirname, '..', 'fixtures/extract-css/.nuxt/dist/client/7dc53e76acc7df734a24.css') const content = await readFile(pathToMinifiedGlobalCss, 'utf-8') - const expectedContent = 'h1[data-v-180e2718]{color:red}.container[data-v-180e2718]{-ms-grid-columns:60px 60px 60px 60px 60px;-ms-grid-rows:30px 30px;display:-ms-grid;display:grid;grid-auto-flow:row;grid-template-columns:60px 60px 60px 60px 60px;grid-template-rows:30px 30px}' + const expectedContent = 'h1[data-v-180e2718]{color:red}.container[data-v-180e2718]{display:-ms-grid;display:grid;-ms-grid-columns:60px 60px 60px 60px 60px;grid-template-columns:60px 60px 60px 60px 60px;-ms-grid-rows:30px 30px;grid-template-rows:30px 30px;grid-auto-flow:row}' expect(content).toBe(expectedContent) }) }) diff --git a/test/unit/with-config.test.js b/test/unit/with-config.test.js index 76659d6372..2ad6726adf 100644 --- a/test/unit/with-config.test.js +++ b/test/unit/with-config.test.js @@ -19,6 +19,11 @@ describe('with-config', () => { expect(html.includes('

I have custom configurations

')).toBe(true) }) + test('/ (asset name for analyze mode)', async () => { + const { html } = await nuxt.renderRoute('/') + expect(html).toContain('