initial server-side rendering, restructure source folder
This commit is contained in:
parent
95e2170c7b
commit
27582ef871
@ -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
|
||||
@ -66,4 +67,3 @@ To make the application start on boot, run the following:
|
||||
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)
|
||||
|
||||
|
2
TODO.md
Normal file
2
TODO.md
Normal 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
16204
dist/browser-bundle.js
vendored
File diff suppressed because one or more lines are too long
4
dist/index.html
vendored
4
dist/index.html
vendored
@ -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>
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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'))
|
@ -30,6 +30,5 @@ function diffToPre(diff) {
|
||||
|
||||
|
||||
const ifNotNewlineSpace = str => {
|
||||
console.log(str)
|
||||
return !str.endsWith('\n') ? ' ' : ''
|
||||
}
|
@ -27,6 +27,5 @@ function diffToPre(diff) {
|
||||
|
||||
|
||||
const ifNotNewlineSpace = str => {
|
||||
console.log(str)
|
||||
return !str.endsWith('\n') ? ' ' : ''
|
||||
}
|
13
src/common/routes.js
Normal file
13
src/common/routes.js
Normal 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
|
@ -25,7 +25,6 @@ export const safeInput = createSelector(
|
||||
export const isMarkdownFormat = createSelector(
|
||||
[format],
|
||||
(format) => {
|
||||
console.log(format, Format.MARKDOWN)
|
||||
return format == Format.MARKDOWN
|
||||
}
|
||||
)
|
@ -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
34
src/server/babel.index.js
Normal 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);
|
||||
}
|
@ -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()
|
||||
|
@ -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
84
src/server/render.js
Normal 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>
|
||||
`)
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
module.exports = {
|
||||
cache: true,
|
||||
entry: './src/client',
|
||||
entry: './src/client/index.js',
|
||||
output: {
|
||||
filename: './dist/browser-bundle.js'
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user