Skip to content

Commit

Permalink
Generating separate CSS file for prod build.
Browse files Browse the repository at this point in the history
  • Loading branch information
coryhouse committed Feb 1, 2016
1 parent 3a665db commit 7a5fbd3
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 26 deletions.
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,22 @@ Unfortunately, scripts in package.json can't be commented inline because the JSO
Webpack serves your app in memory when you run `npm start`. No physical files are written. However, the web root is /src, so you can reference files under /src in index.html. When the app is built using `npm run build`, physical files are written to /dist and the app is served from /dist.

###How is Sass being converted into CSS and landing in the browser?
Magic! Okay, more specifically: Webpack handles it like this:
Magic! Okay, more specifically, we're handling it differently in dev (`npm start`) vs prod (`npm run build`)

When you run `npm start`:
1. The sass-loader compiles Sass into CSS
2. Webpack bundles the compiled CSS into bundle.js. Sounds odd, but it works!
3. Loads styles into the <head> of index.html via JavaScript. This is why you don't see a stylesheet reference in index.html. In fact, if you disable JavaScript in your browser, you'll see the styles don't load either. This process is performed for both dev (`npm start`) and production (`npm run build`). Oh, and since we're generating source maps, you can even see the original Sass source in [compatible browsers](http://thesassway.com/intermediate/using-source-maps-with-sass).
3. bundle.js contains code that loads styles into the <head> of index.html via JavaScript. This is why you don't see a stylesheet reference in index.html. In fact, if you disable JavaScript in your browser, you'll see the styles don't load either.

The approach above supports hot reloading, which is great for development. However, it also create a flash of unstyled content on load because you have to wait for the JavaScript to parse and load styles before they're applied. So for the production build, we use a different approach:

When you run `npm run build`:
1. The sass-loader compiles Sass into CSS
2. The [extract-text-webpack-plugin](https://github.com/webpack/extract-text-webpack-plugin) extracts the compiled Sass into styles.css
3. buildHtml.js adds a reference to the stylesheet to the head of index.html.

For both of the above methods, a separate sourcemap is generated for debugging Sass in [compatible browsers](http://thesassway.com/intermediate/using-source-maps-with-sass).

###I don't like the magic you just described above. I simply want to use a CSS file.
No problem. Reference your CSS file in index.html, and add a step to the build process to copy your CSS file over to the same relative location /dist as part of the build step. But be forwarned, you lose style hot reloading with this approach.

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-slingshot",
"version": "1.0.3",
"version": "1.1.0",
"description": "Starter kit for creating apps with React and Redux",
"scripts": {
"prestart": "npm run remove-dist",
Expand Down Expand Up @@ -40,6 +40,7 @@
"eslint": "1.10.3",
"eslint-loader": "1.2.0",
"eslint-plugin-react": "3.15.0",
"extract-text-webpack-plugin": "1.0.1",
"file-loader": "0.8.5",
"mocha": "2.3.4",
"node-sass": "3.4.2",
Expand Down
23 changes: 10 additions & 13 deletions tools/buildHtml.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,19 @@ var useTrackJs = true; //If you choose not to use TrackJS, just set this to fals
var trackJsToken = ''; //If you choose to use TrackJS, insert your unique token here. To get a token, go to https://trackjs.com

fs.readFile('src/index.html', 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
if (err) return console.log(err);

var trackJsCode = '';
var $ = cheerio.load(data);

if (useTrackJs) {
if (trackJsToken) {
trackJsCode = "<!-- BEGIN TRACKJS Note: This should be the first <script> on the page per https://my.trackjs.com/install --><script>window._trackJs = { token: '" + trackJsToken + "' };</script><script src=https://d2zah9y47r7bi2.cloudfront.net/releases/current/tracker.js></script><!-- END TRACKJS -->";
} else {
console.log('To track JavaScript errors, sign up for a free trial at TrackJS.com and enter your token in /tools/build.html on line 10.'.yellow);
}
}
//since a separate spreadsheet is only utilized for the production build, need to dynamically add this here.
$('head').prepend('<link rel="stylesheet" href="styles.css">');

var $ = cheerio.load(data);
$('head').prepend(trackJsCode); //add TrackJS tracking code to the top of <head>
if (useTrackJs && trackJsToken) {
var trackJsCode = "<!-- BEGIN TRACKJS Note: This should be the first <script> on the page per https://my.trackjs.com/install --><script>window._trackJs = { token: '" + trackJsToken + "' };</script><script src=https://d2zah9y47r7bi2.cloudfront.net/releases/current/tracker.js></script><!-- END TRACKJS -->";
$('head').prepend(trackJsCode); //add TrackJS tracking code to the top of <head>
} else {
if (useTrackJs) console.log('To track JavaScript errors, sign up for a free trial at TrackJS.com and enter your token in /tools/build.html.'.yellow);
}

fs.writeFile('dist/index.html', $.html(), 'utf8', function (err) {
if (err) return console.log(err);
Expand Down
28 changes: 18 additions & 10 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// file generates a webpack config for the environment passed to the getConfig method.
var webpack = require('webpack');
var path = require('path');
var ExtractTextPlugin = require("extract-text-webpack-plugin");

var getPlugins = function(env) {
var GLOBALS = {
Expand All @@ -17,6 +18,7 @@ var getPlugins = function(env) {

switch(env) {
case 'production':
plugins.push(new ExtractTextPlugin('styles.css'));
plugins.push(new webpack.optimize.DedupePlugin());
plugins.push(new webpack.optimize.UglifyJsPlugin({minimize: true, sourceMap: true}));
break;
Expand All @@ -40,19 +42,25 @@ var getEntry = function(env) {
return entry;
};

var getLoaders = function () {
return [
{
test: /\.js$/,
var getLoaders = function (env) {
var loaders = [{test: /\.js$/, include: path.join(__dirname, 'src'), loaders: ['babel', 'eslint']}];

if (env == 'production') {
//generate separate physical stylesheet for production build using ExtractTextPlugin. This provides separate caching and avoids a flash of unstyled content on load.
loaders.push({
test: /(\.css|\.scss)$/,
include: path.join(__dirname, 'src'),
loaders: ['babel', 'eslint']
},
{
loader: ExtractTextPlugin.extract("css?sourceMap!sass?sourceMap")
});
} else {
loaders.push({
test: /(\.css|\.scss)$/,
include: path.join(__dirname, 'src'),
loaders: ['style', 'css?sourceMap', 'sass?sourceMap']
}
]
});
}

return loaders;
};

module.exports = function getConfig(env) {
Expand All @@ -69,7 +77,7 @@ module.exports = function getConfig(env) {
},
plugins: getPlugins(env),
module: {
loaders: getLoaders()
loaders: getLoaders(env)
}
};
};

0 comments on commit 7a5fbd3

Please sign in to comment.