Skip to content

Commit 6e3a10e

Browse files
authored
Webpack on server (#766)
* 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
1 parent 153ef78 commit 6e3a10e

27 files changed

+173
-155
lines changed

Changelog.md

+24
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
2.1
2+
===
3+
- #766
4+
- Modify webpack configurations (prod and dev) to work on server
5+
- Specify all `node_modules` files (except binaries) as externals.
6+
- Change entry point for server to be server/index.js
7+
- Change output path to be /compiled
8+
- Extract postcss config to common.config.js
9+
- Remove css extraction
10+
- Use css-loader/locals in the prerendering bundle to only export identifier mappings webpack/css-loader#59 (comment)
11+
- Add sourcemap support
12+
- Cleaned up package.json
13+
- Removed babel, babel-node, build:node, build:prod commands in scripts
14+
- Fix db/* files to work with webpack
15+
- Modify express path to work with webpack
16+
- Fix incorrect css-loader query parameters - thanks @ZeroCho!
17+
- Add command line config to webpack to have i
18+
- `--debug`
19+
- `--display-error-details` - shows more information about errors. I.e. this
20+
shows which paths are tried while resolving a module
21+
- Bump postcss-loader, postcss-import, postcss-cssnext, postcss-reporter versions
22+
- Remove postcss-simple-vars
23+
24+
125
2.0.1
226
===
327
- Remove isomorphic fetch as dependency - no longer needed! Hooray!

app/fetch-data/fetchVoteData.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import { voteService } from 'services';
22

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

811
export default fetchData;

nodemon.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@
44
"app/utils",
55
"app/server.jsx",
66
"app/routes.jsx",
7-
"app/helmconfig.js",
8-
"app/components/Meta.jsx",
97
"server",
108
"webpack/webpack.config.dev-client.js",
119
"webpack/webpack.config.dev-server.js",
1210
".stylelintrc"
1311
],
14-
"exec": "npm run build:dev && npm run babel-node -- server/index.js"
12+
"exec": "npm run build:dev && node compiled/server.dev.js"
1513
}

package.json

+8-13
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
{
22
"name": "react-webpack-node",
3-
"version": "2.0.1",
3+
"version": "2.1.0",
44
"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.",
55
"repository": "https://github.com/choonkending/react-webpack-node",
66
"main": "index.js",
77
"scripts": {
8-
"babel": "babel --presets es2015,stage-0 --ignore node_modules,public",
9-
"babel-node": "babel-node --presets es2015,stage-0 --ignore node_modules,public",
108
"sequelize": "node_modules/.bin/sequelize",
119
"lint": "eslint --ext .js,.jsx app server",
1210
"lint:fix": "eslint --ext .js,.jsx app server --fix",
1311
"clean": "rimraf public && rimraf compiled",
14-
"start": "cross-env NODE_ENV=production node compiled/index.js",
12+
"start": "cross-env NODE_ENV=production node compiled/server.js",
1513
"dev": "cross-env NODE_ENV=development nodemon",
16-
"build:dev": "cross-env NODE_ENV=development webpack --colors --config ./webpack/webpack.config.dev-server.js",
17-
"build:prod": "cross-env NODE_ENV=production webpack --colors --config ./webpack/webpack.config.prod.js",
18-
"build:node": "cross-env NODE_ENV=production npm run babel -- ./server -d ./compiled",
19-
"build": "npm run clean && npm run build:prod && npm run build:node",
14+
"build:dev": "cross-env NODE_ENV=development webpack ---debug --colors --display-error-details --config ./webpack/webpack.config.dev-server.js",
15+
"build": "npm run clean && cross-env NODE_ENV=production webpack --colors --display-error-details --config ./webpack/webpack.config.prod.js",
2016
"test": "cross-env NODE_ENV=test karma start",
2117
"test:watch": "cross-env NODE_ENV=test npm test -- --watch --no-single-run",
2218
"postinstall": "npm run build",
@@ -94,11 +90,10 @@
9490
"passport-local": "^1.0.0",
9591
"pg": "^6.0.3",
9692
"pg-hstore": "^2.3.2",
97-
"postcss-cssnext": "^2.7.0",
98-
"postcss-import": "^8.0.2",
99-
"postcss-loader": "^0.13.0",
100-
"postcss-reporter": "^1.4.1",
101-
"postcss-simple-vars": "^3.0.0",
93+
"postcss-cssnext": "^2.9.0",
94+
"postcss-import": "^9.0.0",
95+
"postcss-loader": "^1.2.1",
96+
"postcss-reporter": "^3.0.0",
10297
"react": "^15.4.1",
10398
"react-dom": "^15.4.1",
10499
"react-helmet": "^3.2.2",

server/config/express.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default (app) => {
2626
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
2727
app.use(methodOverride());
2828

29-
app.use(express.static(path.join(__dirname, '../..', 'public')));
29+
app.use(express.static(path.join(__dirname, '..', 'public')));
3030

3131
// I am adding this here so that the Heroku deploy will work
3232
// Indicates the app is behind a front-facing proxy,

server/db/index.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ let dbConfig = null;
66
/* use inline requires for conditional loading */
77
switch (DB_TYPE) {
88
case DB_TYPES.MONGO:
9-
dbConfig = require('./mongo');
9+
dbConfig = require('./mongo').default;
1010
break;
1111
case DB_TYPES.POSTGRES:
12-
dbConfig = require('./postgres');
12+
dbConfig = require('./postgres').default;
1313
break;
1414
case DB_TYPES.NONE:
15-
dbConfig = require('./none');
15+
dbConfig = require('./none').default;
1616
break;
1717
default:
1818
throw new Error(`No database type '${DB_TYPE}' found`);
@@ -23,4 +23,3 @@ export const controllers = dbConfig.controllers;
2323
export const passport = dbConfig.passport;
2424
export const session = dbConfig.session;
2525

26-
export default dbConfig.default;

server/db/mongo/connect.js

+2-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import fs from 'fs';
22
import path from 'path';
33
import mongoose from 'mongoose';
44
import { db } from './constants';
5+
import loadModels from './models';
56

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

23-
// Register schema as mongoose model
24-
const modelPath = path.join(__dirname, 'models');
25-
fs.readdirSync(modelPath).forEach((file) => {
26-
if (~file.indexOf('.js')) require(`${modelPath}/${file}`);
27-
});
24+
loadModels();
2825
};

server/db/mongo/index.js

-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import controllers from './controllers';
33
import passport from './passport';
44
import session from './session';
55

6-
export { connect, controllers, passport, session };
7-
86
export default {
97
connect,
108
controllers,

server/db/mongo/models/index.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default function loadModels() {
2+
require('./topics');
3+
require('./user');
4+
};
5+

server/db/none/index.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1-
export function connect() {}
1+
const connect = () => {};
2+
const controllers = () => {};
3+
const passport = () => {};
4+
const session = () => {};
25

3-
export default { connect };
6+
export default { connect, controllers, passport, session };

server/db/postgres/index.js

-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { connect, controllers, passport } from '../sequelize';
22
import session from './session';
33

4-
export { connect, controllers, passport, session };
5-
64
export default {
75
connect,
86
controllers,

server/db/sequelize/connect.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import Models from './models';
2-
3-
const sequelize = Models.sequelize;
1+
import { sequelize } from './models';
42

53
export default () => {
64
sequelize

server/db/sequelize/controllers/index.js

-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import topics from './topics';
22
import users from './users';
33

4-
export { topics, users };
5-
64
export default {
75
topics,
86
users

server/db/sequelize/controllers/topics.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import _ from 'lodash';
2-
import Models from '../models';
2+
import { Models, sequelize } from '../models';
33
const Topic = Models.Topic;
4-
const sequelize = Models.sequelize;
54

65
/**
76
* List

server/db/sequelize/controllers/users.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import passport from 'passport';
2-
import Models from '../models';
2+
import { Models } from '../models';
33
const User = Models.User;
44

55
/**

server/db/sequelize/index.js

+3-10
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
import connect from './connect';
2-
import controllers from './controllers';
3-
import passport from './passport';
1+
export { default as connect } from './connect';
2+
export { default as controllers } from './controllers';
3+
export { default as passport } from './passport';
44

5-
export { connect, controllers, passport };
6-
7-
export default {
8-
connect,
9-
controllers,
10-
passport
11-
};

server/db/sequelize/models/index.js

+11-25
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,21 @@ import path from 'path';
33
import Sequelize from 'sequelize';
44
import sequelizeConfig from '../sequelize_config';
55
import { ENV } from '../../../config/appConfig';
6+
import tokenModel from './tokens';
7+
import topicModel from './topics';
8+
import userModel from './users';
69
const config = sequelizeConfig[ENV];
7-
const basename = path.basename(module.filename);
8-
const db = {};
9-
let sequelize;
1010

11+
const db = {};
1112
const dbUrl = process.env[config.use_env_variable];
12-
if (dbUrl) {
13-
sequelize = new Sequelize(dbUrl);
14-
} else {
15-
sequelize = new Sequelize(config.database, config.username, config.password, config);
16-
}
1713

18-
fs
19-
.readdirSync(__dirname)
20-
.filter((file) =>
21-
(file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js')
22-
)
23-
.forEach((file) => {
24-
const model = sequelize.import(path.join(__dirname, file));
25-
db[model.name] = model;
26-
});
14+
const sequelize = dbUrl ? new Sequelize(dbUrl) : new Sequelize(config.database, config.username, config.password, config);
15+
16+
db.Token = sequelize.import('Token', tokenModel);
17+
db.Topic = sequelize.import('Topic', topicModel);
18+
db.User = sequelize.import('User', userModel);
2719

28-
Object.keys(db).forEach((modelName) => {
29-
if (db[modelName].associate) {
30-
db[modelName].associate(db);
31-
}
32-
});
20+
Object.keys(db).forEach(model => model.associate && model.associate(db));
3321

34-
db.sequelize = sequelize;
35-
db.Sequelize = Sequelize;
22+
export { db as Models, sequelize };
3623

37-
module.exports = db;

server/db/sequelize/models/users.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function hashPassword(user) {
1515
}
1616
/* eslint-enable no-param-reassign */
1717

18-
module.exports = (sequelize, DataTypes) => {
18+
export default (sequelize, DataTypes) => {
1919
const User = sequelize.define('User', {
2020
email: {
2121
type: DataTypes.STRING,

server/db/sequelize/passport/deserializeUser.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Models from '../models';
1+
import { Models } from '../models';
22

33
const User = Models.User;
44

server/db/sequelize/passport/google.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import Models from '../models';
1+
import { Models, sequelize } from '../models';
22

3-
const sequelize = Models.sequelize;
43
const User = Models.User;
54

65
/* eslint-disable no-param-reassign */

server/db/sequelize/passport/index.js

-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import deserializeUser from './deserializeUser';
22
import google from './google';
33
import local from './local';
44

5-
export { deserializeUser, google, local };
6-
75
export default {
86
deserializeUser,
97
google,

server/db/sequelize/passport/local.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Models from '../models';
1+
import { Models } from '../models';
22

33
const User = Models.User;
44

server/index.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { connect } from './db';
55
import passportConfig from './config/passport';
66
import expressConfig from './config/express';
77
import routesConfig from './config/routes';
8-
const App = require('../public/assets/server');
8+
import renderMiddleware from '../app/server';
99
const app = express();
1010

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

5454
app.listen(app.get('port'));

webpack/common.config.js

+27-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
var path = require('path');
2+
var fs = require('fs');
23
var ExtractTextPlugin = require('extract-text-webpack-plugin');
34

5+
var externalNodeModules =
6+
fs.readdirSync('node_modules')
7+
.filter(function(x) {
8+
return ['.bit'].indexOf(x) === -1;
9+
})
10+
.reduce(function(acc, cur) {
11+
acc[cur] = 'commonjs ' + cur;
12+
return acc;
13+
}, {});
14+
415
module.exports = {
5-
publicPath: '/assets/',
6-
assetsPath: path.join(__dirname, '..', 'public', 'assets'),
16+
output: {
17+
publicPath: '/assets/',
18+
assetsPath: path.join(__dirname, '..', 'public', 'assets'),
19+
distPath: path.join(__dirname, '..', 'compiled')
20+
},
721
commonLoaders: [
822
{
923
/*
@@ -23,7 +37,6 @@ module.exports = {
2337
'transform-react-inline-elements'
2438
]
2539
},
26-
include: path.join(__dirname, '..', 'app'),
2740
exclude: path.join(__dirname, '..', 'node_modules')
2841
},
2942
{ test: /\.json$/, loader: 'json-loader' },
@@ -35,5 +48,15 @@ module.exports = {
3548
limit: 10000,
3649
}
3750
}
38-
]
51+
],
52+
externals: externalNodeModules,
53+
postCSSConfig: function() {
54+
return [
55+
require('postcss-import')(),
56+
require('postcss-cssnext')({
57+
browsers: ['> 1%', 'last 2 versions']
58+
}),
59+
require('postcss-reporter')({ clearReportedMessages: true })
60+
];
61+
}
3962
};

0 commit comments

Comments
 (0)