Extend build process to support multiple views

This commit is contained in:
Andrew Sliwinski 2015-09-03 22:26:56 -07:00
parent bf907af53b
commit 41176aaee1
12 changed files with 151 additions and 19 deletions

View file

@ -1,5 +1,5 @@
ESLINT=./node_modules/.bin/eslint ESLINT=./node_modules/.bin/eslint
LIVE=./node_modules/.bin/live-server NODE=node
WATCH=./node_modules/.bin/watch WATCH=./node_modules/.bin/watch
WEBPACK=./node_modules/.bin/webpack WEBPACK=./node_modules/.bin/webpack
@ -11,7 +11,7 @@ build:
@make webpack @make webpack
clean: clean:
rm -rf ./build/*.* rm -rf ./build
mkdir -p build mkdir -p build
static: static:
@ -27,6 +27,7 @@ test:
lint: lint:
$(ESLINT) ./*.js $(ESLINT) ./*.js
$(ESLINT) ./server/*.js
$(ESLINT) ./src/*.jsx $(ESLINT) ./src/*.jsx
$(ESLINT) ./src/mixins/*.jsx $(ESLINT) ./src/mixins/*.jsx
$(ESLINT) ./src/views/**/*.jsx $(ESLINT) ./src/views/**/*.jsx
@ -34,15 +35,14 @@ lint:
# ------------------------------------ # ------------------------------------
start:
@make watch
$(LIVE) ./build --port=8888 --wait=200 --no-browser
watch: watch:
$(WATCH) "make clean && make static" ./static &
$(WEBPACK) -d --watch & $(WEBPACK) -d --watch &
$(WATCH) "make static" ./static &
wait wait
start:
node ./server/index.js
# ------------------------------------ # ------------------------------------
.PHONY: build clean static webpack test lint start watch .PHONY: build clean static webpack test lint watch start

View file

@ -6,10 +6,17 @@
### To Build ### To Build
```bash ```bash
npm install npm install
npm run build
```
During development, you can use `npm run watch` to cause any update you make to files in either `./static` or `./src` to trigger a rebuild of the project.
### To Run
```bash
npm start npm start
``` ```
Once running, open `http://localhost:8888` in your browser. Any update you make to files in either `./static` or `./src` should trigger a rebuild of the project and cause your browser to refresh to reflect those changes. Once running, open `http://localhost:8888` in your browser. If you wish to have the server reload automatically, you can install either [nodemon](https://github.com/remy/nodemon) or [forever](https://github.com/foreverjs/forever).
### To Test ### To Test
```bash ```bash

View file

@ -5,7 +5,9 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "make start", "start": "make start",
"test": "make test" "test": "make test",
"watch": "make watch",
"build": "make build"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -17,14 +19,19 @@
"url": "https://github.com/llk/scratch-www/issues" "url": "https://github.com/llk/scratch-www/issues"
}, },
"homepage": "https://github.com/llk/scratch-www#readme", "homepage": "https://github.com/llk/scratch-www#readme",
"dependencies": {}, "dependencies": {
"bunyan": "1.4.0",
"compression": "1.5.2",
"express": "4.13.3",
"lodash.defaults": "3.1.2",
"mustache": "2.1.3"
},
"devDependencies": { "devDependencies": {
"css-loader": "0.17.0", "css-loader": "0.17.0",
"eslint": "1.3.1", "eslint": "1.3.1",
"eslint-plugin-react": "3.3.1", "eslint-plugin-react": "3.3.1",
"json-loader": "0.5.2", "json-loader": "0.5.2",
"jsx-loader": "0.13.2", "jsx-loader": "0.13.2",
"live-server": "0.8.1",
"node-sass": "3.3.2", "node-sass": "3.3.2",
"react": "0.13.3", "react": "0.13.3",
"sass-loader": "2.0.1", "sass-loader": "2.0.1",

4
server/defaults.json Normal file
View file

@ -0,0 +1,4 @@
{
"title": "Scratch - Imagine, Program, Share",
"description": "Scratch is a free programming language and online community where you can create your own interactive stories, games, and animations."
}

35
server/handler.js Normal file
View file

@ -0,0 +1,35 @@
var crypto = require('crypto');
var defaults = require('lodash.defaults');
var fs = require('fs');
var mustache = require('mustache');
var path = require('path');
/**
* Constructor
*/
function Handler (route) {
// Route definition
defaults(route, require('./defaults.json'));
// Render template
var location = path.resolve(__dirname, './template.html');
var template = fs.readFileSync(location, 'utf8');
var output = mustache.render(template, route);
var checksum = crypto.createHash('md5').update(output).digest('hex');
return function (req, res) {
res.set({
'Content-Type': 'text/html',
'Cache-Control': 'public, max-age=31536000',
'Etag': 'W/"' + checksum + '"'
});
res.send(output);
};
}
/**
* Export a new instance
*/
module.exports = function (route) {
return new Handler(route);
};

29
server/index.js Normal file
View file

@ -0,0 +1,29 @@
var compression = require('compression');
var express = require('express');
var path = require('path');
var handler = require('./handler');
var log = require('./log');
var routes = require('./routes.json');
// Server setup
var app = express();
app.disable('x-powered-by');
app.use(log());
app.use(compression());
app.use(express.static(path.resolve(__dirname, '../build'), {
lastModified: true,
maxAge: '1y'
}));
// Bind routes
for (var item in routes) {
var route = routes[item];
app.get(route.pattern, handler(route));
}
// Start listening
var port = process.env.PORT || 8888;
app.listen(port, function () {
process.stdout.write('Server listening on port ' + port + '\n');
});

14
server/log.js Normal file
View file

@ -0,0 +1,14 @@
var bunyan = require('bunyan');
module.exports = function () {
var logger = bunyan.createLogger({
name: 'www',
serializers: {req: bunyan.stdSerializers.req}
});
return function (req, res, next) {
req.log = logger;
req.log.info({req: req});
next();
};
};

11
server/routes.json Normal file
View file

@ -0,0 +1,11 @@
[
{
"pattern": "/",
"view": "splash"
},
{
"pattern": "/about",
"view": "about",
"title": "About"
}
]

View file

@ -6,8 +6,8 @@
<meta http-equiv="x-ua-compatible" content="ie=edge"> <meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Scratch</title> <title>Scratch - {{title}}</title>
<meta name="description" content="Scratch is a free programming language and online community where you can create your own interactive stories, games, and animations." /> <meta name="description" content="{{description}}" />
<link rel="stylesheet" href="/css/lib/normalize.min.css" /> <link rel="stylesheet" href="/css/lib/normalize.min.css" />
</head> </head>
@ -20,6 +20,6 @@
<!-- Scripts --> <!-- Scripts -->
<script src="/js/lib/react.js"></script> <script src="/js/lib/react.js"></script>
<script src="/js/main.bundle.js"></script> <script src="/js/main.bundle.js"></script>
<script src="/js/splash.bundle.js"></script> <script src="/js/{{view}}.bundle.js"></script>
</body> </body>
</html> </html>

15
src/views/about/about.jsx Normal file
View file

@ -0,0 +1,15 @@
var React = require('react');
require('./about.scss');
var View = React.createClass({
render: function () {
return (
<div>
<h1>I am the about page!</h1>
</div>
);
}
});
React.render(<View />, document.getElementById('view'));

View file

@ -0,0 +1,3 @@
#view {
}

View file

@ -1,11 +1,18 @@
var path = require('path'); var path = require('path');
var webpack = require('webpack'); var webpack = require('webpack');
var routes = require('./server/routes.json');
// Prepare all entry points
var entry = {
main: './src/main.jsx'
};
routes.forEach(function (route) {
entry[route.view] = './src/views/' + route.view + '/' + route.view + '.jsx';
});
// Config
module.exports = { module.exports = {
entry: { entry: entry,
main: './src/main.jsx',
splash: './src/views/splash/splash.jsx'
},
devtool: 'source-map', devtool: 'source-map',
externals: { externals: {
'react': 'React', 'react': 'React',