Skip to content

Commit

Permalink
Webpack on server (#766)
Browse files Browse the repository at this point in the history
* Remove babel and build:node from package.json

* Remove include path in babel-loader

* Add placeholder to fix fetching data failure if DB does not exist

* Modify webpack prod config to compile server-side node

- Specify all node_modules files (except binaries) as externals. This means they
  will be specified as dependencies of the resulting bundle. Read more
  https://webpack.github.io/docs/configuration.html#externals
- Change entry point for server to be server/index.js
- Change output path to be /compiled
- Remove postcss config
- Remove css extraction
- Use css-loader/locals in the prerendering bundle to only export identifier
  mappings
webpack-contrib/css-loader#59 (comment)
- ⚠️ we are keeping context the same so that our css-loader classnames
  generated remain the same!

* Start app with compiled/server.js

* Require server.jsx in index.js

* Modify express static path to work with webpack

* Remove build:prod from scripts in package.json

* Change mongo db files to work with webpack

- Add index in mongo/models to be used in connect
- Remove unneeded default export
- Replace fs.readFileSync with a loadModels() function to load the schema

* Change sequelize db files to work with webpack

* Change none db files to work with webpack

* Remove unneeded default export

* Modify webpack dev to compile server-side node

- Remove babel-node command from package.json
- Remove unnecessary files in nodemon.json
- Set node_modules as externals in result bundle

* Fix webpack dev client to use postcss-loader

* Extract externals into common.config

- Refactored different paths into output object

* Fix incorrect css-loader query parameters

- Add (s) to modules
- To work with postcss, we need to add importLoaders https://github.com/postcss/postcss-loader#css-modules

* Extract postCSSConfig to common.config

* Add command line config to webpack in package.json

* Bump postcss-loader and postcss-import versions

* Add sourcemap support to server-side webpack

* Re-add postCSS config into our server-side bundles to prevent warnings

* Remove unneeded postcss-simple-vars dependency

* Bump postcss-cssnext, postcss-reporter versions
- Replace deprecated clearMessages option with clearReportedMessages
  https://github.com/postcss/postcss-reporter/blob/master/CHANGELOG.md#300

* 2.1.0

* Update Changelog with webpack on server
  • Loading branch information
choonkending authored Dec 18, 2016
1 parent 026df47 commit 3191b03
Show file tree
Hide file tree
Showing 27 changed files with 173 additions and 155 deletions.
24 changes: 24 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
2.1
===
- #766
- Modify webpack configurations (prod and dev) to work on server
- Specify all `node_modules` files (except binaries) as externals.
- Change entry point for server to be server/index.js
- Change output path to be /compiled
- Extract postcss config to common.config.js
- Remove css extraction
- Use css-loader/locals in the prerendering bundle to only export identifier mappings webpack/css-loader#59 (comment)
- Add sourcemap support
- Cleaned up package.json
- Removed babel, babel-node, build:node, build:prod commands in scripts
- Fix db/* files to work with webpack
- Modify express path to work with webpack
- Fix incorrect css-loader query parameters - thanks @ZeroCho!
- Add command line config to webpack to have i
- `--debug`
- `--display-error-details` - shows more information about errors. I.e. this
shows which paths are tried while resolving a module
- Bump postcss-loader, postcss-import, postcss-cssnext, postcss-reporter versions
- Remove postcss-simple-vars


2.0.1
===
- Remove isomorphic fetch as dependency - no longer needed! Hooray!
Expand Down
5 changes: 4 additions & 1 deletion app/fetch-data/fetchVoteData.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { voteService } from 'services';

const fetchData = () => {
return voteService.getTopics()
.then(res => res.data);
.then(res => res.data)
// Returning [] as a placeholder now so it does not error out when this service
// fails. We should be handling this in our DISPATCH_REQUEST_FAILURE
.catch(err => []);
};

export default fetchData;
Expand Down
4 changes: 1 addition & 3 deletions nodemon.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
"app/utils",
"app/server.jsx",
"app/routes.jsx",
"app/helmconfig.js",
"app/components/Meta.jsx",
"server",
"webpack/webpack.config.dev-client.js",
"webpack/webpack.config.dev-server.js",
".stylelintrc"
],
"exec": "npm run build:dev && npm run babel-node -- server/index.js"
"exec": "npm run build:dev && node compiled/server.dev.js"
}
21 changes: 8 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
{
"name": "react-webpack-node",
"version": "2.0.1",
"version": "2.1.0",
"description": "Your One-Stop solution for a full-stack app with ES6/ES2015 React.js featuring universal Redux, React Router, React Router Redux Hot reloading, CSS modules, Express 4.x, and multiple ORMs.",
"repository": "https://github.com/choonkending/react-webpack-node",
"main": "index.js",
"scripts": {
"babel": "babel --presets es2015,stage-0 --ignore node_modules,public",
"babel-node": "babel-node --presets es2015,stage-0 --ignore node_modules,public",
"sequelize": "node_modules/.bin/sequelize",
"lint": "eslint --ext .js,.jsx app server",
"lint:fix": "eslint --ext .js,.jsx app server --fix",
"clean": "rimraf public && rimraf compiled",
"start": "cross-env NODE_ENV=production node compiled/index.js",
"start": "cross-env NODE_ENV=production node compiled/server.js",
"dev": "cross-env NODE_ENV=development nodemon",
"build:dev": "cross-env NODE_ENV=development webpack --colors --config ./webpack/webpack.config.dev-server.js",
"build:prod": "cross-env NODE_ENV=production webpack --colors --config ./webpack/webpack.config.prod.js",
"build:node": "cross-env NODE_ENV=production npm run babel -- ./server -d ./compiled",
"build": "npm run clean && npm run build:prod && npm run build:node",
"build:dev": "cross-env NODE_ENV=development webpack ---debug --colors --display-error-details --config ./webpack/webpack.config.dev-server.js",
"build": "npm run clean && cross-env NODE_ENV=production webpack --colors --display-error-details --config ./webpack/webpack.config.prod.js",
"test": "cross-env NODE_ENV=test karma start",
"test:watch": "cross-env NODE_ENV=test npm test -- --watch --no-single-run",
"postinstall": "npm run build",
Expand Down Expand Up @@ -94,11 +90,10 @@
"passport-local": "^1.0.0",
"pg": "^6.0.3",
"pg-hstore": "^2.3.2",
"postcss-cssnext": "^2.7.0",
"postcss-import": "^8.0.2",
"postcss-loader": "^0.13.0",
"postcss-reporter": "^1.4.1",
"postcss-simple-vars": "^3.0.0",
"postcss-cssnext": "^2.9.0",
"postcss-import": "^9.0.0",
"postcss-loader": "^1.2.1",
"postcss-reporter": "^3.0.0",
"react": "^15.4.1",
"react-dom": "^15.4.1",
"react-helmet": "^3.2.2",
Expand Down
2 changes: 1 addition & 1 deletion server/config/express.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default (app) => {
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
app.use(methodOverride());

app.use(express.static(path.join(__dirname, '../..', 'public')));
app.use(express.static(path.join(__dirname, '..', 'public')));

// I am adding this here so that the Heroku deploy will work
// Indicates the app is behind a front-facing proxy,
Expand Down
7 changes: 3 additions & 4 deletions server/db/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ let dbConfig = null;
/* use inline requires for conditional loading */
switch (DB_TYPE) {
case DB_TYPES.MONGO:
dbConfig = require('./mongo');
dbConfig = require('./mongo').default;
break;
case DB_TYPES.POSTGRES:
dbConfig = require('./postgres');
dbConfig = require('./postgres').default;
break;
case DB_TYPES.NONE:
dbConfig = require('./none');
dbConfig = require('./none').default;
break;
default:
throw new Error(`No database type '${DB_TYPE}' found`);
Expand All @@ -23,4 +23,3 @@ export const controllers = dbConfig.controllers;
export const passport = dbConfig.passport;
export const session = dbConfig.session;

export default dbConfig.default;
7 changes: 2 additions & 5 deletions server/db/mongo/connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from 'fs';
import path from 'path';
import mongoose from 'mongoose';
import { db } from './constants';
import loadModels from './models';

export default () => {
// Find the appropriate database to connect to, default to localhost if not found.
Expand All @@ -20,9 +21,5 @@ export default () => {
mongoose.connection.on('error', console.log);
mongoose.connection.on('disconnected', connect);

// Register schema as mongoose model
const modelPath = path.join(__dirname, 'models');
fs.readdirSync(modelPath).forEach((file) => {
if (~file.indexOf('.js')) require(`${modelPath}/${file}`);
});
loadModels();
};
2 changes: 0 additions & 2 deletions server/db/mongo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import controllers from './controllers';
import passport from './passport';
import session from './session';

export { connect, controllers, passport, session };

export default {
connect,
controllers,
Expand Down
5 changes: 5 additions & 0 deletions server/db/mongo/models/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default function loadModels() {
require('./topics');
require('./user');
};

7 changes: 5 additions & 2 deletions server/db/none/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export function connect() {}
const connect = () => {};
const controllers = () => {};
const passport = () => {};
const session = () => {};

export default { connect };
export default { connect, controllers, passport, session };
2 changes: 0 additions & 2 deletions server/db/postgres/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { connect, controllers, passport } from '../sequelize';
import session from './session';

export { connect, controllers, passport, session };

export default {
connect,
controllers,
Expand Down
4 changes: 1 addition & 3 deletions server/db/sequelize/connect.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import Models from './models';

const sequelize = Models.sequelize;
import { sequelize } from './models';

export default () => {
sequelize
Expand Down
2 changes: 0 additions & 2 deletions server/db/sequelize/controllers/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import topics from './topics';
import users from './users';

export { topics, users };

export default {
topics,
users
Expand Down
3 changes: 1 addition & 2 deletions server/db/sequelize/controllers/topics.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import _ from 'lodash';
import Models from '../models';
import { Models, sequelize } from '../models';
const Topic = Models.Topic;
const sequelize = Models.sequelize;

/**
* List
Expand Down
2 changes: 1 addition & 1 deletion server/db/sequelize/controllers/users.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import passport from 'passport';
import Models from '../models';
import { Models } from '../models';
const User = Models.User;

/**
Expand Down
13 changes: 3 additions & 10 deletions server/db/sequelize/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import connect from './connect';
import controllers from './controllers';
import passport from './passport';
export { default as connect } from './connect';
export { default as controllers } from './controllers';
export { default as passport } from './passport';

export { connect, controllers, passport };

export default {
connect,
controllers,
passport
};
36 changes: 11 additions & 25 deletions server/db/sequelize/models/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,21 @@ import path from 'path';
import Sequelize from 'sequelize';
import sequelizeConfig from '../sequelize_config';
import { ENV } from '../../../config/appConfig';
import tokenModel from './tokens';
import topicModel from './topics';
import userModel from './users';
const config = sequelizeConfig[ENV];
const basename = path.basename(module.filename);
const db = {};
let sequelize;

const db = {};
const dbUrl = process.env[config.use_env_variable];
if (dbUrl) {
sequelize = new Sequelize(dbUrl);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}

fs
.readdirSync(__dirname)
.filter((file) =>
(file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js')
)
.forEach((file) => {
const model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
const sequelize = dbUrl ? new Sequelize(dbUrl) : new Sequelize(config.database, config.username, config.password, config);

db.Token = sequelize.import('Token', tokenModel);
db.Topic = sequelize.import('Topic', topicModel);
db.User = sequelize.import('User', userModel);

Object.keys(db).forEach((modelName) => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
Object.keys(db).forEach(model => model.associate && model.associate(db));

db.sequelize = sequelize;
db.Sequelize = Sequelize;
export { db as Models, sequelize };

module.exports = db;
2 changes: 1 addition & 1 deletion server/db/sequelize/models/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function hashPassword(user) {
}
/* eslint-enable no-param-reassign */

module.exports = (sequelize, DataTypes) => {
export default (sequelize, DataTypes) => {
const User = sequelize.define('User', {
email: {
type: DataTypes.STRING,
Expand Down
2 changes: 1 addition & 1 deletion server/db/sequelize/passport/deserializeUser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Models from '../models';
import { Models } from '../models';

const User = Models.User;

Expand Down
3 changes: 1 addition & 2 deletions server/db/sequelize/passport/google.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Models from '../models';
import { Models, sequelize } from '../models';

const sequelize = Models.sequelize;
const User = Models.User;

/* eslint-disable no-param-reassign */
Expand Down
2 changes: 0 additions & 2 deletions server/db/sequelize/passport/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import deserializeUser from './deserializeUser';
import google from './google';
import local from './local';

export { deserializeUser, google, local };

export default {
deserializeUser,
google,
Expand Down
2 changes: 1 addition & 1 deletion server/db/sequelize/passport/local.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Models from '../models';
import { Models } from '../models';

const User = Models.User;

Expand Down
6 changes: 3 additions & 3 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { connect } from './db';
import passportConfig from './config/passport';
import expressConfig from './config/express';
import routesConfig from './config/routes';
const App = require('../public/assets/server');
import renderMiddleware from '../app/server';
const app = express();

/*
Expand Down Expand Up @@ -46,9 +46,9 @@ routesConfig(app);
/*
* This is where the magic happens. We take the locals data we have already
* fetched and seed our stores with data.
* App is a function that requires store data and url
* renderMiddleware is a function that requires store data and url
* to initialize and return the React-rendered html string
*/
app.get('*', App.default);
app.get('*', renderMiddleware);

app.listen(app.get('port'));
31 changes: 27 additions & 4 deletions webpack/common.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
var path = require('path');
var fs = require('fs');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

var externalNodeModules =
fs.readdirSync('node_modules')
.filter(function(x) {
return ['.bit'].indexOf(x) === -1;
})
.reduce(function(acc, cur) {
acc[cur] = 'commonjs ' + cur;
return acc;
}, {});

module.exports = {
publicPath: '/assets/',
assetsPath: path.join(__dirname, '..', 'public', 'assets'),
output: {
publicPath: '/assets/',
assetsPath: path.join(__dirname, '..', 'public', 'assets'),
distPath: path.join(__dirname, '..', 'compiled')
},
commonLoaders: [
{
/*
Expand All @@ -23,7 +37,6 @@ module.exports = {
'transform-react-inline-elements'
]
},
include: path.join(__dirname, '..', 'app'),
exclude: path.join(__dirname, '..', 'node_modules')
},
{ test: /\.json$/, loader: 'json-loader' },
Expand All @@ -35,5 +48,15 @@ module.exports = {
limit: 10000,
}
}
]
],
externals: externalNodeModules,
postCSSConfig: function() {
return [
require('postcss-import')(),
require('postcss-cssnext')({
browsers: ['> 1%', 'last 2 versions']
}),
require('postcss-reporter')({ clearReportedMessages: true })
];
}
};
Loading

0 comments on commit 3191b03

Please sign in to comment.