diff --git a/.env.example b/.env.example index a776227..5d636b7 100644 --- a/.env.example +++ b/.env.example @@ -11,8 +11,10 @@ APPLICATION_BASE_URL=http://localhost:3000 ASSET_HOST=/dist # The output path for webpack builds. -WEBPACK_OUTPUT_PATH=/dist +WEBPACK_OUTPUT_PATH=/dist/public # Settings for webpack-dev-server. DEV_SERVER_PORT=3001 DEV_SERVER_HOSTNAME=localhost +DEV_SERVER_HOST_URL=http://localhost:3001 + diff --git a/README.md b/README.md index 590e337..56e2ada 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,11 @@ For production builds: npm run prod:build npm run serve ``` +Or simply + +``` +npm run prod +``` For Heroku, simply add a `Procfile`: ``` diff --git a/package-lock.json b/package-lock.json index dc8f1ba..f87284d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -463,9 +463,9 @@ "integrity": "sha1-lfE2KbEsOlGl0hWr3OKqnzL4B3M=" }, "autoprefixer": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.1.tgz", - "integrity": "sha512-lTbsa2X03maxG45xCNh30sJaRKDn8JPnanOeQOW3wvD9yPGmIsf041LHqlrZ1lXPF/1M3yTZKXqqYfmxU69xuQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.2.tgz", + "integrity": "sha512-eTVoSHiGp2cDytg7RS7gtqAnfH+WFcNQMTjywGNu+hH7ViQZ/ZKsvNz2C1oVhCtd9DjMIC15iatpxmtp5Kxvpg==", "requires": { "browserslist": "2.10.0", "caniuse-lite": "1.0.30000780", @@ -2432,6 +2432,22 @@ } } }, + "css-tree": { + "version": "1.0.0-alpha25", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha25.tgz", + "integrity": "sha512-XC6xLW/JqIGirnZuUWHXCHRaAjje2b3OIB0Vj5RIJo6mIi/AdJo30quQl5LxUl0gkXDIrTrFGbMlcZjyFplz1A==", + "requires": { + "mdn-data": "1.0.0", + "source-map": "0.5.7" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, "cssesc": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", @@ -3048,9 +3064,9 @@ } }, "eslint": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.12.1.tgz", - "integrity": "sha512-28hOYej+NZ/R5H1yMvyKa1+bPlu+fnsIAQffK6hxXgvmXnImos2bA5XfCn5dYv2k2mrKj+/U/Z4L5ICWxC7TQw==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.13.0.tgz", + "integrity": "sha512-1l2aVrEz9yiWsEQdL3XZEzTovHQJFZaTeIhOOilKQRiYNn1dVALoYOtn06iPoxhEwFukBPX4Ff8WoGD4r/7D2A==", "dev": true, "requires": { "ajv": "5.5.1", @@ -3068,7 +3084,7 @@ "file-entry-cache": "2.0.0", "functional-red-black-tree": "1.0.1", "glob": "7.1.2", - "globals": "11.0.1", + "globals": "11.1.0", "ignore": "3.3.7", "imurmurhash": "0.1.4", "inquirer": "3.3.0", @@ -3116,9 +3132,9 @@ "dev": true }, "globals": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.0.1.tgz", - "integrity": "sha1-Eqh7sBDlFUOWrMU14eQ/x1Ow5eg=", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.1.0.tgz", + "integrity": "sha512-uEuWt9mqTlPDwSqi+sHjD4nWU/1N+q0fiWI9T1mZpD2UENqX20CFD5T/ziLZvztPaBKl7ZylUi1q6Qfm7E2CiQ==", "dev": true }, "js-yaml": { @@ -5221,6 +5237,11 @@ } } }, + "mdn-data": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.0.0.tgz", + "integrity": "sha1-pp2dp2hHtNWDTBRl6iXAZTofv2Y=" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -6306,6 +6327,25 @@ } } }, + "postcss-csso": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-csso/-/postcss-csso-3.0.0.tgz", + "integrity": "sha512-5yvI9IMIJOofYqmYfn65ZTxxlYZ86jXzDMXwYqltx/kqGsSvHRw4gYkn5e/tCPPIgUeFP5MIhIkA02uoUEZsXA==", + "requires": { + "csso": "3.3.1", + "postcss": "6.0.14" + }, + "dependencies": { + "csso": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/csso/-/csso-3.3.1.tgz", + "integrity": "sha512-OjETffCFB/OzwxVL3eF0+5tXOXCMukVO6rEUxlkIhscE1KRObyg+zMrLUbkPn9kxgBrFWicc37Gv5/22dOh5EA==", + "requires": { + "css-tree": "1.0.0-alpha25" + } + } + } + }, "postcss-discard-comments": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", @@ -8592,9 +8632,9 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, "resolve-url-loader": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-2.2.0.tgz", - "integrity": "sha512-ivEo27PgDKAm3Iwzh/ObwcDrKT2O25xWBIQX6HJVVfaxYWNNW6euPMmMFzpQUZo03+iv2TloBgffEk1ULnEPdg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-2.2.1.tgz", + "integrity": "sha512-ywToZt/yttp4qG/SiiGMLAgaGuSaWSujAaf3WCadXehvQLxIgKFmMOSegaoH9Laa70Ayl4kti0zCAqLR48H/Mw==", "requires": { "adjust-sourcemap-loader": "1.1.0", "camelcase": "4.1.0", @@ -8823,9 +8863,9 @@ } }, "semantic-ui-react": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-0.77.0.tgz", - "integrity": "sha512-lUnlpbIbMtse335kjZAw8ClulCQAUWDm77pw2yY754mL02PlTNYtZQanoGjhBFts41YHpg6ExK156J/9Ynb8IQ==", + "version": "0.77.1", + "resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-0.77.1.tgz", + "integrity": "sha512-pE3leHT9yIk7QlynchQ/fyzcxkZmx1TxaIXAJcdS8slw89PS8UO29KufwGmGIoW6hVQM7bFfXkFjtnZplZYUlQ==", "requires": { "babel-runtime": "6.26.0", "classnames": "2.2.5", @@ -10225,9 +10265,9 @@ } }, "webpack-dev-server": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.9.6.tgz", - "integrity": "sha512-cxk/h18Z/1qWOBTMup5PxvZ9N+vBRmmbh1/TCiCJnjGtLsvYtg7gTrjAmaa9J3kdkmxx6K5jwk6F0aZ+oOrAyA==", + "version": "2.9.7", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.9.7.tgz", + "integrity": "sha512-Pu7uoQFgQj5RE5wmlfkpYSzihMKxulwEuO2xCsaMnAnyRSApwoVi3B8WCm9XbigyWTHaIMzYGkB90Vr6leAeTQ==", "dev": true, "requires": { "ansi-html": "0.0.7", diff --git a/package.json b/package.json index b320987..44d73a4 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "scripts": { "start": "better-npm-run dev:build && better-npm-run dev:start", + "prod": "better-npm-run prod:build && better-npm-run serve", "serve": "better-npm-run serve", "dev:start": "better-npm-run dev:start", "dev:start:server": "better-npm-run dev:start:server", @@ -70,7 +71,7 @@ } }, "prod:build:server": { - "command": "$(npm bin)/webpack --config webpack/ssr.babel.js --progress && $(npm bin)/babel ./server -d ./dist", + "command": "$(npm bin)/webpack --config webpack/ssr.babel.js --progress && $(npm bin)/babel ./server -d ./dist && $(rm ./server/renderer/handleRender.built.js)", "env": { "NODE_ENV": "production" } @@ -116,24 +117,24 @@ "babel-eslint": "^8.0.3", "better-npm-run": "^0.1.0", "debug": "^3.1.0", - "eslint": "^4.12.1", + "eslint": "^4.13.0", "eslint-loader": "^1.9.0", "eslint-plugin-react": "^7.5.1", "nodemon": "^1.12.5", "react-transform-catch-errors": "^1.0.2", "redbox-react": "^1.5.0", "supertest": "^3.0.0", - "webpack-dev-server": "^2.9.6", + "webpack-dev-server": "^2.9.7", "webpack-sources": "^1.1.0" }, "dependencies": { "app-module-path": "^2.2.0", - "autoprefixer": "^7.2.1", + "autoprefixer": "^7.2.2", "axios": "^0.17.1", "babel": "^6.23.0", "babel-cli": "^6.26.0", "babel-core": "^6.26.0", - "babel-loader": "^7.1.1", + "babel-loader": "^7.1.2", "babel-plugin-react-transform": "^3.0.0", "babel-plugin-resolver": "^1.1.0", "babel-plugin-syntax-class-properties": "^6.13.0", @@ -171,6 +172,7 @@ "lodash": "^4.17.4", "mocha": "^4.0.1", "node-sass": "^4.7.2", + "postcss-csso": "^3.0.0", "postcss-loader": "^2.0.9", "react": "^16.2.0", "react-dom": "^16.2.0", @@ -183,11 +185,11 @@ "redux": "^3.7.2", "redux-logger": "^3.0.6", "redux-thunk": "^2.2.0", - "resolve-url-loader": "^2.2.0", + "resolve-url-loader": "^2.2.1", "sass-loader": "^6.0.6", "sass-resources-loader": "^1.3.1", "semantic-ui-css": "^2.2.12", - "semantic-ui-react": "^0.77.0", + "semantic-ui-react": "^0.77.1", "serve-static": "^1.13.1", "style-loader": "^0.19.0", "url-loader": "^0.6.2", diff --git a/postcss.config.js b/postcss.config.js index 4f1d0ea..49f6032 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,5 +1,10 @@ const autoprefixer = require('autoprefixer'); +const csso = require('postcss-csso')({ restructure: true, comments: false }); +const pluginsList = [autoprefixer]; +if (process.env.NODE_ENV === 'production') { + pluginsList.push(csso); +} module.exports = { - plugins: [autoprefixer] -}; + plugins: pluginsList +}; \ No newline at end of file diff --git a/server/server.js b/server/server.js index 63a4c07..d173afc 100644 --- a/server/server.js +++ b/server/server.js @@ -33,7 +33,7 @@ app.use(compression()); // Add middleware to serve up all static files app.use('/dist', - express.static(path.join(__dirname, '../dist')), + express.static(path.join(__dirname, '../' + process.env.WEBPACK_OUTPUT_PATH)), express.static(path.join(__dirname, '../common/images')), express.static(path.join(__dirname, '../common/fonts')) ); diff --git a/webpack/base.js b/webpack/base.js index a44ac99..c56a9cd 100644 --- a/webpack/base.js +++ b/webpack/base.js @@ -121,9 +121,7 @@ export default { test: /\.css$/, use: extractText.extract({ fallback: 'style-loader', - use: [ - { loader: 'css-loader' } - ] + use: ['css-loader', 'postcss-loader'], }) }, { diff --git a/webpack/constants.js b/webpack/constants.js index 1e4e084..42b566e 100644 --- a/webpack/constants.js +++ b/webpack/constants.js @@ -6,7 +6,7 @@ import { mapValues, keyBy } from 'lodash'; */ export const { NODE_ENV, DEV_SERVER_PORT, DEV_SERVER_HOSTNAME, WEBPACK_OUTPUT_PATH, - ASSET_HOST, ASSET_PATH, ANALYZE, APPLICATION_BASE_URL + ASSET_HOST, ASSET_PATH, ANALYZE, APPLICATION_BASE_URL, DEV_SERVER_HOST_URL } = process.env; // The env vars to expose on the client side. @@ -21,7 +21,6 @@ export const CLIENT_ENV_VARS = mapValues( ); // The URL of the dev server including the hostname and port -export const DEV_SERVER_HOST_URL = `http://${DEV_SERVER_HOSTNAME}:${DEV_SERVER_PORT}`; // The asset host to use for webpack files. In development, we will always use // the dev server's URL. diff --git a/webpack/development.hot.js b/webpack/development.hot.js index 32c0630..767f5d6 100644 --- a/webpack/development.hot.js +++ b/webpack/development.hot.js @@ -63,7 +63,8 @@ new WebpackDevServer(webpack(config), { version: false, chunks: false, children: false - } + }, + disableHostCheck: true }).listen(DEV_SERVER_PORT, DEV_SERVER_HOSTNAME, function errorCallback(err) { if (err) { console.error(err); diff --git a/webpack/production.babel.js b/webpack/production.babel.js index 12475d8..4178f6f 100644 --- a/webpack/production.babel.js +++ b/webpack/production.babel.js @@ -1,8 +1,11 @@ import webpack from 'webpack'; import baseConfig from './base'; import CompressionPlugin from 'compression-webpack-plugin'; - +import UglifyJSPlugin from 'uglifyjs-webpack-plugin'; const plugins = [ + new webpack.BannerPlugin({ + banner: 'hash:[hash], chunkhash:[chunkhash], name:[name], filebase:[filebase], query:[query], file:[file]', + }), new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: '[name].[hash].js', @@ -10,10 +13,15 @@ const plugins = [ }), new webpack.optimize.AggressiveMergingPlugin(), new webpack.optimize.ModuleConcatenationPlugin(), - new webpack.optimize.UglifyJsPlugin({ - compress: { - screw_ie8: true, - warnings: false + new UglifyJSPlugin({ + uglifyOptions: { + compress: { + warnings: false + }, + mangle: true, + output: { + comments: false, + }, } }), new CompressionPlugin({