initial server-side rendering, restructure source folder

This commit is contained in:
Adam Brown 2016-12-03 22:14:15 -05:00
parent 95e2170c7b
commit 27582ef871
25 changed files with 8300 additions and 8139 deletions

View File

@ -15,6 +15,7 @@ This is a complete rewrite of Dubdiff with:
- simpler project architecture
- client-side diffing engine and simplified server
- server-side rendering
- switch to React from Angular
- clean up of diffing engine
- goal of implementing a HTML diff viewer
@ -65,5 +66,4 @@ To make the application start on boot, run the following:
pm2 startup systemd
pm2 save
[Digital Ocean: How To Set Up a Node.js Application for Production on Ubuntu 16.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-16-04)
[Digital Ocean: How To Set Up a Node.js Application for Production on Ubuntu 16.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-16-04)

2
TODO.md Normal file
View File

@ -0,0 +1,2 @@
- create production mode build and serve settings: `webpack.js` and `src/server/babel.index.js`

16204
dist/browser-bundle.js vendored

File diff suppressed because one or more lines are too long

4
dist/index.html vendored
View File

@ -4,7 +4,7 @@
</head>
<body>
<div id="root">If you see this then something is wrong.</div>
<script src="browser-bundle.js"></script>
<script src="dist/browser-bundle.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.2/semantic.min.css"></link>
<link href="main.css" rel="stylesheet"> </body>
<link href="dist/main.css" rel="stylesheet"> </body>
</html>

View File

@ -2,11 +2,11 @@
"name": "dubdiff-2",
"version": "0.0.0",
"description": "",
"main": "index.js",
"main": "src/server/babel.index.js",
"scripts": {
"build": "ebpack --colors",
"start": "webpack --progress --colors --watch",
"serve": "node src/server/index.js",
"serve": "node src/server/babel.index.js",
"webpack-stats": "webpack --profile --json > stats.json",
"test": "echo \"Error: no test specified\" && exit 1"
},
@ -31,12 +31,14 @@
"uuid": "^3.0.1"
},
"devDependencies": {
"babel-core": "^6.3.26",
"babel-core": "^6.18.2",
"babel-loader": "^6.2.0",
"babel-preset-es2015": "^6.3.13",
"babel-preset-es2015-native-modules": "^6.9.4",
"babel-preset-node6": "^11.0.0",
"babel-preset-react": "^6.3.13",
"copyfiles": "^0.2.2",
"piping": "^1.0.0-rc.4",
"webpack": "^2.1.0-beta.27"
}
}

View File

@ -4,16 +4,13 @@ import ReactDOM from 'react-dom'
import * as Redux from 'redux'
import {Provider} from 'react-redux'
import * as localStore from './localStore'
import * as reducers from './reducers'
import Main from './components/Main'
import Compare from './components/Compare'
import { Router } from 'react-router'
import createBrowserHistory from 'history/lib/createBrowserHistory'
import {Route, IndexRoute, Redirect } from 'react-router'
import {Router, Route, IndexRoute, Redirect } from 'react-router'
import * as localStore from '../common/localStore'
import * as reducers from '../common/reducers'
import routes from '../common/routes'
//create the redux store
@ -37,8 +34,7 @@ function render() {
ReactDOM.render(
<Provider store={store}>
<Router history={createBrowserHistory()}>
<Route path="/" component={Main} />
<Route path="/**" component={Compare}/>
{routes}
</Router>
</Provider>
, document.getElementById('root'))

View File

@ -30,6 +30,5 @@ function diffToPre(diff) {
const ifNotNewlineSpace = str => {
console.log(str)
return !str.endsWith('\n') ? ' ' : ''
}

View File

@ -27,6 +27,5 @@ function diffToPre(diff) {
const ifNotNewlineSpace = str => {
console.log(str)
return !str.endsWith('\n') ? ' ' : ''
}

13
src/common/routes.js Normal file
View File

@ -0,0 +1,13 @@
import {Route, IndexRout, Redirect } from 'react-router'
import React from 'react';
import Main from './components/Main'
import Compare from './components/Compare'
var routes = [
<Route path="/" component={Main}/>,
<Route path="/:compareId" component={Compare}/>
]
export default routes

View File

@ -12,7 +12,7 @@ import * as Dubdiff from './util/dubdiff'
const input = (state) => state.input
const format = (state) => state.format
const show = (state) => state.show
const show = (state) => state.show
export const safeInput = createSelector(
[input],
@ -25,7 +25,6 @@ export const safeInput = createSelector(
export const isMarkdownFormat = createSelector(
[format],
(format) => {
console.log(format, Format.MARKDOWN)
return format == Format.MARKDOWN
}
)

View File

@ -10,9 +10,6 @@ export function plaintextDiff(original, final) {
let diff = JsDiff.diffArrays(arrOriginal, arrFinal)
diff = plaintextRestoreSpaces(diff)
console.log(diffToLogString(diff))
return diff
// return JsDiff.diffWordsWithSpace(original,final)

34
src/server/babel.index.js Normal file
View File

@ -0,0 +1,34 @@
var Path = require('path');
var srcRoot = Path.join(__dirname, '..')//.replace(/\\/g, "/")
//there should be some option for distribution / optimization?
var config = {
/* upgraded to babel 6 to be able to use this preset */
presets: ["node6", "react"],
sourceMaps: "both",
//highlightCode: false,
sourceRoot: srcRoot,
only: /src/
};
require('babel-core/register')(config);
const PIPING = true
//!!! Need to guard for production environments
//if (process.env.NODE_ENV !== "production") {
if (PIPING)
if (!require("piping")({hook: true, includeModules: false})) {
return;
}
//}
try {
require('./index.js');
}
catch (error) {
console.error(error.stack);
}

View File

@ -1,7 +1,7 @@
const express = require('express')
const jf = require('jsonfile')
const fs = require('fs')
const uuid = require('uuid')
import express from 'express'
import jf from 'jsonfile'
import fs from 'fs'
import uuid from 'uuid'
const router = express.Router()

View File

@ -1,32 +1,56 @@
const express = require('express')
const path = require('path')
const bodyParser = require('body-parser');
import express from 'express'
import path from 'path'
import bodyParser from 'body-parser'
import * as Redux from 'redux'
import comparisonRouter from './comparison'
import * as reducers from '../common/reducers'
import * as actions from '../common/actions'
import render from './render'
const app = express()
const comparisonRouter = require('./comparison')
app.use(express.static('dist'))
app.use('bower_components/*', express.static('bower_components'))
//serve the dist static files at /dist
app.use('/dist', express.static(path.join(__dirname, '..', '..', 'dist')))
//serve the comparison api at /api/compare
app.use(bodyParser.json())
app.use('/api/compare', comparisonRouter);
app.route('/*')
.get(function(req, res) {
res.sendFile(path.join(__dirname, '..', '..', 'dist', 'index.html'));
});
//the following routes are for server-side rendering of the app
//eventually, we should render the comparison directly from the server
/*
app.route('/:comparisonId')
.get((req, res) => {
const store = createSessionStore()
...
})
app.route('/'). ...
*/
//but for now, let's just render the app and let it fetch comparison data
app.use((req, res) => render(createSessionStore(), req, res))
app.listen(8080, function () {
console.log('Server listening on port 8080.')
})
//this is pretty much redundant at this point
function createSessionStore() {
//create the redux store
return Redux.createStore(
Redux.combineReducers(reducers)
)
}

84
src/server/render.js Normal file
View File

@ -0,0 +1,84 @@
import React from 'react'
import { renderToString } from 'react-dom/server'
import { Provider } from 'react-redux'
import { match, RoutingContext } from 'react-router'
import routes from '../common/routes.js'
export default function render(store, req, res) {
console.log(store.getState())
// Send the rendered page back to the client
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
if (error) {
res.status(500).send(renderError('Routing Error', error.message))
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search)
} else if (renderProps) {
// Render the component to a string
try {
const html = renderToString(
<Provider store={store}>
<RoutingContext {...renderProps} />
</Provider>
)
// Grab the initial state from our Redux store
const initialState = store.getState()
// and send
res.status(200).send(appTemplate(html, initialState))
}
catch(ex) {
res.status(500).send(errorTemplate('Render Exception', ex.message, ex))
}
} else {
res.status(404).send(renderError('Not found', ''))
}
})
}
const pageTemplate = (body) => {
return `
<!doctype html>
<html>
<head>
<title>Dubdiff</title>
<!-- CSS -->
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.2/semantic.min.css"/>
<link rel="stylesheet" href="dist/main.css"/>
<!-- Favicon -->
<!--<link rel="shortcut icon" href="/assets/favicon.ico">-->
</head>
<body>
${body}
</body>
</html>
`
}
function errorTemplate(title, message, exception) {
return pageTemplate(`
<h1>${title}</h1>
<p>${message}</p>
<pre>${exception.toString()}</pre>
`)
}
function appTemplate(html, initialState) {
return pageTemplate(`
<div id="root">${html}</div>
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(initialState)}
</script>
<!-- <script>__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__</script> -->
<script type="text/javascript" src="dist/browser-bundle.js"></script>
`)
}

View File

@ -1,6 +1,6 @@
module.exports = {
cache: true,
entry: './src/client',
entry: './src/client/index.js',
output: {
filename: './dist/browser-bundle.js'
},