format to standardjs code style with linting

This commit is contained in:
Adam Brown 2017-01-17 20:41:53 -05:00
parent 8c5ac6ade7
commit 368d19dd21
26 changed files with 518 additions and 582 deletions

View File

@ -12,6 +12,8 @@
"serve": "node src/server/babel.index.js", "serve": "node src/server/babel.index.js",
"serve:prod": "cross-env NODE_ENV=production node src/server/babel.index.js", "serve:prod": "cross-env NODE_ENV=production node src/server/babel.index.js",
"webpack-stats": "webpack --json > stats.json", "webpack-stats": "webpack --json > stats.json",
"lint": "standard --verbose | snazzy",
"lint:fix": "standard --fix --verbose | snazzy",
"test": "mocha --watch --compilers js:babel-register" "test": "mocha --watch --compilers js:babel-register"
}, },
"author": "", "author": "",
@ -55,6 +57,8 @@
"json-loader": "^0.5.4", "json-loader": "^0.5.4",
"mocha": "^3.2.0", "mocha": "^3.2.0",
"piping": "^1.0.0-rc.4", "piping": "^1.0.0-rc.4",
"snazzy": "^6.0.0",
"standard": "^8.6.0",
"webpack": "^2.1.0-beta.27" "webpack": "^2.1.0-beta.27"
} }
} }

View File

@ -1,10 +1,7 @@
import React from 'react' import React from 'react'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import * as Actions from '../common/actions' import * as Actions from '../common/actions'
import * as Selectors from '../common/selectors'
/* This component reads the local storage store and adds them to the Redux store. /* This component reads the local storage store and adds them to the Redux store.
* Local storage is read during the componentDidMount lifecycle method. * Local storage is read during the componentDidMount lifecycle method.
@ -12,41 +9,40 @@ import * as Selectors from '../common/selectors'
*/ */
// an app-specific name for the localStorage state // an app-specific name for the localStorage state
const stateName = "dubdiff_state" const stateName = 'dubdiff_state'
// return a new object with the given keys, each assigned to the cooresponding value // return a new object with the given keys, each assigned to the cooresponding value
// from the given object // from the given object
const copyKeys = (obj, keys) => keys.reduce((acc, p) => { acc[p] = obj[p]; return acc }, {}) const copyKeys = (obj, keys) => keys.reduce((acc, p) => { acc[p] = obj[p]; return acc }, {})
// utility method for retrieving json data from the local store // utility method for retrieving json data from the local store
/*
function getLocalState (keys) { function getLocalState (keys) {
if (localStorage.getItem(stateName)) { if (window.localStorage.getItem(stateName)) {
const localState = JSON.parse(localStorage.getItem(stateName)) const localState = JSON.parse(window.localStorage.getItem(stateName))
return copyKeys(localState, keys) return copyKeys(localState, keys)
} } else {
else
return copyKeys({}, keys) return copyKeys({}, keys)
} }
}
*/
// utility method for writing json data to the local store // utility method for writing json data to the local store
function setLocalState (state, keys) { function setLocalState (state, keys) {
let toSave = copyKeys(state, keys) let toSave = copyKeys(state, keys)
localStorage.setItem(stateName, JSON.stringify(toSave)) window.localStorage.setItem(stateName, JSON.stringify(toSave))
} }
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
input: state.input, input: state.input
// the loading/empty/clean state // the loading/empty/clean state
}) })
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
onChangeOriginal: (text) => dispatch(Actions.updateOriginalInput(text)), onChangeOriginal: (text) => dispatch(Actions.updateOriginalInput(text)),
onChangeFinal: (text) => dispatch(Actions.updateFinalInput(text)), onChangeFinal: (text) => dispatch(Actions.updateFinalInput(text))
}) })
class LocalStorage extends React.Component { class LocalStorage extends React.Component {
// load the state from the local storage // load the state from the local storage

View File

@ -6,18 +6,15 @@ import * as Redux from 'redux'
import {Provider} from 'react-redux' import {Provider} from 'react-redux'
// import createBrowserHistory from 'history/lib/createBrowserHistory' // import createBrowserHistory from 'history/lib/createBrowserHistory'
import {Router, Route, IndexRoute, Redirect, browserHistory } from 'react-router' import {Router, browserHistory} from 'react-router'
import thunk from 'redux-thunk' import thunk from 'redux-thunk'
import * as reducers from '../common/reducers' import * as reducers from '../common/reducers'
import routes from '../common/routes' import routes from '../common/routes'
import * as Actions from '../common/actions'
import LocalStorage from './LocalStorage' import LocalStorage from './LocalStorage'
// initial state is rehydrated from the server // initial state is rehydrated from the server
const initialState = JSON.parse(decodeURI(window.__INITIAL_STATE__)) const initialState = JSON.parse(decodeURI(window.__INITIAL_STATE__))
@ -32,8 +29,6 @@ const store = Redux.createStore(
) )
) )
function render () { function render () {
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <Provider store={store}>

View File

@ -9,20 +9,22 @@ import {Status, StatusError} from './constants'
export const updateOriginalInput = (text) => export const updateOriginalInput = (text) =>
(dispatch, getState) => { (dispatch, getState) => {
dispatch({type: 'UPDATE_ORIGINAL_INPUT', data: text}) dispatch({type: 'UPDATE_ORIGINAL_INPUT', data: text})
if (getState().input.original.length>0) if (getState().input.original.length > 0) {
dispatch({type: 'STATUS_SET', data: Status.DIRTY}) dispatch({type: 'STATUS_SET', data: Status.DIRTY})
else } else {
dispatch({type: 'STATUS_SET', data: Status.EMPTY}) dispatch({type: 'STATUS_SET', data: Status.EMPTY})
} }
}
export const updateFinalInput = (text) => export const updateFinalInput = (text) =>
(dispatch, getState) => { (dispatch, getState) => {
dispatch({type: 'UPDATE_FINAL_INPUT', data: text}) dispatch({type: 'UPDATE_FINAL_INPUT', data: text})
if (getState().input.final.length>0) if (getState().input.final.length > 0) {
dispatch({type: 'STATUS_SET', data: Status.DIRTY}) dispatch({type: 'STATUS_SET', data: Status.DIRTY})
else } else {
dispatch({type: 'STATUS_SET', data: Status.EMPTY}) dispatch({type: 'STATUS_SET', data: Status.EMPTY})
} }
}
export const clearInput = () => export const clearInput = () =>
(dispatch) => { (dispatch) => {
@ -36,7 +38,6 @@ export const showOriginal = () => ({ type: 'SHOW_ORIGINAL'})
export const showFinal = () => ({ type: 'SHOW_FINAL' }) export const showFinal = () => ({ type: 'SHOW_FINAL' })
export const showDifference = () => ({ type: 'SHOW_DIFFERENCE' }) export const showDifference = () => ({ type: 'SHOW_DIFFERENCE' })
// if the input is dirty, saves it to the server // if the input is dirty, saves it to the server
// creates a new uuid for the same, // creates a new uuid for the same,
// then changes the browser location to a comparison view with that id // then changes the browser location to a comparison view with that id
@ -54,7 +55,6 @@ export const compare = () =>
browserHistory.replace(comparePath) browserHistory.replace(comparePath)
} }
// clear the input and return to the edit page // clear the input and return to the edit page
export const reset = () => export const reset = () =>
(dispatch, getState) => { (dispatch, getState) => {
@ -62,20 +62,17 @@ export const reset = () =>
browserHistory.push('/') browserHistory.push('/')
} }
// switch to the edit view // switch to the edit view
export const edit = () => export const edit = () =>
(dispatch, getState) => { (dispatch, getState) => {
browserHistory.push('/') browserHistory.push('/')
} }
// saves the current input fields to the server // saves the current input fields to the server
// creates and returns a new id for the comparison // creates and returns a new id for the comparison
// should this method ensure that the initial state is valid? ('DIRTY') // should this method ensure that the initial state is valid? ('DIRTY')
export const save = () => export const save = () =>
(dispatch, getState) => { (dispatch, getState) => {
// generate an id // generate an id
const id = uuid() const id = uuid()
@ -90,17 +87,16 @@ export const save = () =>
b: getState().input.final b: getState().input.final
}), }),
headers: { headers: {
"Content-Type": "application/json" 'Content-Type': 'application/json'
}, }
} }
// dispatch post request // dispatch post request
fetch(endpointUri, fetchOptions) fetch(endpointUri, fetchOptions)
.then(response => { .then(response => {
if (response.ok) if (response.ok) {
dispatch({type: 'STATUS_SET', data: Status.CLEAN}) dispatch({type: 'STATUS_SET', data: Status.CLEAN})
else { } else {
response.text().then((responseText) => { response.text().then((responseText) => {
const error = {message: `${response.status}: ${responseText}`} const error = {message: `${response.status}: ${responseText}`}
dispatch({type: 'STATUS_SET', data: Status.DIRTY}) dispatch({type: 'STATUS_SET', data: Status.DIRTY})
@ -115,7 +111,7 @@ export const save = () =>
}) })
// return the id after the request has been sent // return the id after the request has been sent
return id; return id
} }
/* /*
@ -130,7 +126,6 @@ const load = (id) =>
method: 'GET' method: 'GET'
} }
//dispatch post request //dispatch post request
fetch(endpointUri, fetchOptions) fetch(endpointUri, fetchOptions)
.then(response => response.json()) .then(response => response.json())

View File

@ -1,9 +1,8 @@
import React from 'react' import React from 'react'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import {Segment, Grid, Form} from 'semantic-ui-react' import {Segment, Grid} from 'semantic-ui-react'
import * as Actions from '../actions'
import * as Selectors from '../selectors' import * as Selectors from '../selectors'
import Header from './Header' import Header from './Header'
@ -26,8 +25,6 @@ const mapDispatchToProps = dispatch => ({
// loadIfNeeded: (id) => dispatch(Actions.loadIfNeeded()) // loadIfNeeded: (id) => dispatch(Actions.loadIfNeeded())
}) })
class Compare extends React.Component { class Compare extends React.Component {
/* /*
componentDidMount() { componentDidMount() {
@ -42,25 +39,25 @@ class Compare extends React.Component {
<Segment basic padded> <Segment basic padded>
<Grid stackable columns={2}> <Grid stackable columns={2}>
<Grid.Column width="3"> <Grid.Column width='3'>
<CompareControls /> <CompareControls />
</Grid.Column> </Grid.Column>
<Grid.Column width="13"> <Grid.Column width='13'>
<Segment> <Segment>
{ {
(!this.props.isMarkdownFormat && this.props.isShowDifference) ? (!this.props.isMarkdownFormat && this.props.isShowDifference)
<ShowPlaintext diff={this.props.diff}>{this.props.diff}</ShowPlaintext>: ? <ShowPlaintext diff={this.props.diff}>{this.props.diff}</ShowPlaintext>
(this.props.isMarkdownFormat && this.props.isShowDifference) ? : (this.props.isMarkdownFormat && this.props.isShowDifference)
<ShowMarkdown diff={this.props.diff}>{this.props.diff}</ShowMarkdown>: ? <ShowMarkdown diff={this.props.diff}>{this.props.diff}</ShowMarkdown>
(!this.props.isMarkdownFormat && !this.props.isShowDifference) ? : (!this.props.isMarkdownFormat && !this.props.isShowDifference)
<ShowPlaintext ? <ShowPlaintext
text={this.props.isShowOriginal ? this.props.safeInput.original : this.props.safeInput.final} text={this.props.isShowOriginal ? this.props.safeInput.original : this.props.safeInput.final}
/> : />
(this.props.isMarkdownFormat && !this.props.isShowDifference) ? : (this.props.isMarkdownFormat && !this.props.isShowDifference)
<ShowMarkdown ? <ShowMarkdown
text={this.props.isShowOriginal ? this.props.safeInput.original : this.props.safeInput.final} text={this.props.isShowOriginal ? this.props.safeInput.original : this.props.safeInput.final}
/> : />
null : null
} }
</Segment> </Segment>
</Grid.Column> </Grid.Column>
@ -75,7 +72,6 @@ class Compare extends React.Component {
export default connect(mapStateToProps, mapDispatchToProps)(Compare) export default connect(mapStateToProps, mapDispatchToProps)(Compare)
/* <div ng-if="isMarkdownFormat"> /* <div ng-if="isMarkdownFormat">
<div ng-show="isShowBefore" class="col-md-10 col-sm-12 content-well"> <div ng-show="isShowBefore" class="col-md-10 col-sm-12 content-well">
<div btf-markdown="before" class="before"> <div btf-markdown="before" class="before">

View File

@ -1,6 +1,5 @@
import React from 'react' import React from 'react'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import {Link} from 'react-router'
import {Button, Icon, Segment} from 'semantic-ui-react' import {Button, Icon, Segment} from 'semantic-ui-react'
@ -11,10 +10,9 @@ const mapStateToProps = (state) => ({
isMarkdownFormat: Selectors.isMarkdownFormat(state), isMarkdownFormat: Selectors.isMarkdownFormat(state),
isShowOriginal: Selectors.isShowOriginal(state), isShowOriginal: Selectors.isShowOriginal(state),
isShowFinal: Selectors.isShowFinal(state), isShowFinal: Selectors.isShowFinal(state),
isShowDifference: Selectors.isShowDifference(state), isShowDifference: Selectors.isShowDifference(state)
}) })
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
onSetPlaintextFormat: () => dispatch(Actions.setPlaintextFormat()), onSetPlaintextFormat: () => dispatch(Actions.setPlaintextFormat()),
onSetMarkdownFormat: () => dispatch(Actions.setMarkdownFormat()), onSetMarkdownFormat: () => dispatch(Actions.setMarkdownFormat()),
@ -27,12 +25,12 @@ const mapDispatchToProps = dispatch => ({
class CompareControls extends React.Component { class CompareControls extends React.Component {
onClickMarkdownFormat () { onClickMarkdownFormat () {
if (this.props.isMarkdownFormat) if (this.props.isMarkdownFormat) {
this.props.onSetPlaintextFormat() this.props.onSetPlaintextFormat()
else } else {
this.props.onSetMarkdownFormat() this.props.onSetMarkdownFormat()
} }
}
render () { render () {
return ( return (
@ -48,8 +46,8 @@ class CompareControls extends React.Component {
</Segment> </Segment>
<Segment > <Segment >
<Button fluid active={this.props.isMarkdownFormat} type="submit" onClick={this.onClickMarkdownFormat.bind(this)}> <Button fluid active={this.props.isMarkdownFormat} type='submit' onClick={this.onClickMarkdownFormat.bind(this)}>
{this.props.isMarkdownFormat ? <Icon name="checkmark"/> : <span/>} {this.props.isMarkdownFormat ? <Icon name='checkmark' /> : <span />}
&nbsp;As Markdown &nbsp;As Markdown
</Button> </Button>
</Segment> </Segment>

View File

@ -3,8 +3,8 @@ import React from 'react'
import {Segment} from 'semantic-ui-react' import {Segment} from 'semantic-ui-react'
const Footer = (props) => ( const Footer = (props) => (
<Segment basic padded textAlign="center" as="footer"> <Segment basic padded textAlign='center' as='footer'>
<p><a href="http://adamarthurryan.com">Adam Brown</a> | This website is <a href="https://github.com/adamarthurryan/dubdiff">open source</a>.</p> <p><a href='http://adamarthurryan.com'>Adam Brown</a> | This website is <a href='https://github.com/adamarthurryan/dubdiff'>open source</a>.</p>
</Segment> </Segment>
) )

View File

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import {Segment, Header, Rail, Container} from 'semantic-ui-react' import {Segment, Header, Rail} from 'semantic-ui-react'
import {Link} from 'react-router' import {Link} from 'react-router'
import * as Actions from '../actions' import * as Actions from '../actions'
@ -10,21 +10,19 @@ import SaveStatus from './SaveStatus'
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
}) })
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
onReset: () => { console.log(Actions.reset()); dispatch(Actions.reset())}, onReset: () => { console.log(Actions.reset()); dispatch(Actions.reset()) }
}) })
const SiteHeader = (props) => ( const SiteHeader = (props) => (
<Segment basic > <Segment basic >
<Segment basic padded textAlign="center" as="header" id='masthead'> <Segment basic padded textAlign='center' as='header' id='masthead'>
<Header><Link onClick={props.onReset}>dubdiff</Link></Header> <Header><Link onClick={props.onReset}>dubdiff</Link></Header>
</Segment> </Segment>
<Rail internal position="right"> <Rail internal position='right'>
<Segment basic padded> <Segment basic padded>
<SaveStatus /> <SaveStatus />
</Segment> </Segment>

View File

@ -13,22 +13,16 @@ import MainControls from './MainControls'
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
input: state.input, input: state.input,
safeInput: Selectors.safeInput(state), safeInput: Selectors.safeInput(state)
}) })
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
onChangeOriginal: (text) => dispatch(Actions.updateOriginalInput(text)), onChangeOriginal: (text) => dispatch(Actions.updateOriginalInput(text)),
onChangeFinal: (text) => dispatch(Actions.updateFinalInput(text)), onChangeFinal: (text) => dispatch(Actions.updateFinalInput(text))
}) })
class Main extends React.Component { class Main extends React.Component {
constructor() {
super()
}
render () { render () {
return ( return (
<div> <div>
@ -36,34 +30,31 @@ class Main extends React.Component {
<Segment basic padded> <Segment basic padded>
<Grid stackable columns={3}> <Grid stackable columns={3}>
<Grid.Column width="3"> <Grid.Column width='3'>
<MainControls /> <MainControls />
</Grid.Column> </Grid.Column>
<Grid.Column width="6"> <Grid.Column width='6'>
<Form> <Form>
<Form.Field> <Form.Field>
<label>Original</label> <label>Original</label>
<textarea value={this.props.input.original} onChange={event => this.props.onChangeOriginal(event.target.value)}></textarea> <textarea value={this.props.input.original} onChange={event => this.props.onChangeOriginal(event.target.value)} />
</Form.Field> </Form.Field>
</Form> </Form>
</Grid.Column> </Grid.Column>
<Grid.Column width="6"> <Grid.Column width='6'>
<Form> <Form>
<Form.Field> <Form.Field>
<label>Final</label> <label>Final</label>
<textarea value={this.props.input.final} onChange={event => this.props.onChangeFinal(event.target.value)}></textarea> <textarea value={this.props.input.final} onChange={event => this.props.onChangeFinal(event.target.value)} />
</Form.Field> </Form.Field>
</Form> </Form>
</Grid.Column> </Grid.Column>
</Grid> </Grid>
</Segment> </Segment>
<Footer /> <Footer />
</div> </div>
) )
} }
} }

View File

@ -12,7 +12,6 @@ const mapStateToProps = (state) => ({
saveStatus: state.saveStatus saveStatus: state.saveStatus
}) })
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
onSetPlaintextFormat: (format) => dispatch(Actions.setPlaintextFormat()), onSetPlaintextFormat: (format) => dispatch(Actions.setPlaintextFormat()),
onSetMarkdownFormat: (format) => dispatch(Actions.setMarkdownFormat()), onSetMarkdownFormat: (format) => dispatch(Actions.setMarkdownFormat()),
@ -24,12 +23,12 @@ const mapDispatchToProps = dispatch => ({
class MainControls extends React.Component { class MainControls extends React.Component {
onClickMarkdownFormat () { onClickMarkdownFormat () {
if (this.props.isMarkdownFormat) if (this.props.isMarkdownFormat) {
this.props.onSetPlaintextFormat() this.props.onSetPlaintextFormat()
else } else {
this.props.onSetMarkdownFormat() this.props.onSetMarkdownFormat()
} }
}
render () { render () {
return ( return (
@ -39,8 +38,8 @@ class MainControls extends React.Component {
</Segment> </Segment>
<Segment > <Segment >
<Button fluid active={this.props.isMarkdownFormat} type="submit" onClick={this.onClickMarkdownFormat.bind(this)}> <Button fluid active={this.props.isMarkdownFormat} type='submit' onClick={this.onClickMarkdownFormat.bind(this)}>
{this.props.isMarkdownFormat ? <Icon name="checkmark"/> : <span/>} {this.props.isMarkdownFormat ? <Icon name='checkmark' /> : <span />}
&nbsp;As Markdown &nbsp;As Markdown
</Button> </Button>
</Segment> </Segment>

View File

@ -2,7 +2,6 @@ import React from 'react'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import {Message, Icon, Button} from 'semantic-ui-react' import {Message, Icon, Button} from 'semantic-ui-react'
import { browserHistory} from 'react-router'
import * as Actions from '../actions' import * as Actions from '../actions'
import {Status, StatusError} from '../constants' import {Status, StatusError} from '../constants'
@ -11,14 +10,14 @@ const mapStateToProps = (state) => ({
status: state.status status: state.status
}) })
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
onSave: () => dispatch(Actions.save()), onSave: () => dispatch(Actions.save()),
onReset: () => dispatch(Actions.reset()) onReset: () => dispatch(Actions.reset())
}) })
const SaveStatus = (props) => { const SaveStatus = (props) => {
if (props.status.type == Status.SAVING) return ( if (props.status.type === Status.SAVING) {
return (
<Message size='tiny' floating compact icon> <Message size='tiny' floating compact icon>
<Icon name='circle notched' loading /> <Icon name='circle notched' loading />
<Message.Content> <Message.Content>
@ -26,7 +25,9 @@ const SaveStatus = (props) => {
</Message.Content> </Message.Content>
</Message> </Message>
) )
if (props.status.type == Status.LOADING) return ( }
if (props.status.type === Status.LOADING) {
return (
<Message size='tiny' floating compact icon> <Message size='tiny' floating compact icon>
<Icon name='circle notched' loading /> <Icon name='circle notched' loading />
<Message.Content> <Message.Content>
@ -34,7 +35,8 @@ const SaveStatus = (props) => {
</Message.Content> </Message.Content>
</Message> </Message>
) )
else if (props.status.hasError && props.status.errorType == StatusError.SAVE_ERROR) return ( } else if (props.status.hasError && props.status.errorType === StatusError.SAVE_ERROR) {
return (
<Message size='tiny' floating compact icon> <Message size='tiny' floating compact icon>
<Icon name='exclamation' /> <Icon name='exclamation' />
<Message.Content> <Message.Content>
@ -45,7 +47,8 @@ const SaveStatus = (props) => {
</Message.Content> </Message.Content>
</Message> </Message>
) )
else if (props.status.hasError && props.status.errorType == StatusError.LOAD_ERROR) return ( } else if (props.status.hasError && props.status.errorType === StatusError.LOAD_ERROR) {
return (
<Message size='tiny' floating compact icon> <Message size='tiny' floating compact icon>
<Icon name='exclamation' /> <Icon name='exclamation' />
<Message.Content> <Message.Content>
@ -56,9 +59,7 @@ const SaveStatus = (props) => {
</Message.Content> </Message.Content>
</Message> </Message>
) )
} else return (<div />)
else return ( <div></div> )
} }
export default connect(mapStateToProps, mapDispatchToProps)(SaveStatus) export default connect(mapStateToProps, mapDispatchToProps)(SaveStatus)

View File

@ -2,17 +2,16 @@ import React from 'react'
import markdownCompiler from 'markdown-to-jsx' import markdownCompiler from 'markdown-to-jsx'
import {diffToString, diffToHtml} from '../util/dubdiff' import {diffToHtml} from '../util/dubdiff'
const ShowMarkdown = (props) => { const ShowMarkdown = (props) => {
return <div> return <div>
{ {
props.text ? props.text
markdownCompiler(props.text) : ? markdownCompiler(props.text)
props.diff ? : props.diff
markdownCompiler(diffToHtml(props.diff)) : ? markdownCompiler(diffToHtml(props.diff))
null : null
} }
</div> </div>
} }

View File

@ -1,14 +1,13 @@
import React from 'react' import React from 'react'
const ShowPlaintext = (props) => { const ShowPlaintext = (props) => {
return <div> return <div>
<pre style={{whiteSpace: 'pre-wrap'}}> <pre style={{whiteSpace: 'pre-wrap'}}>
{props.text ? {props.text
props.text: ? props.text
props.diff ? : props.diff
diffToPre(props.diff) : ? diffToPre(props.diff)
null : null
} }
</pre> </pre>
</div> </div>
@ -18,8 +17,10 @@ export default ShowPlaintext
function diffToPre (diff) { function diffToPre (diff) {
return diff.map((part, index) => ( return diff.map((part, index) => (
part.added ? <ins key={index}>{part.value}</ins> : part.added
part.removed ? <del key={index}>{part.value}</del> : ? <ins key={index}>{part.value}</ins>
<span key={index}>{part.value}</span> : part.removed
? <del key={index}>{part.value}</del>
: <span key={index}>{part.value}</span>
)) ))
} }

View File

@ -1,6 +1,5 @@
import {Format, Show, Status, StatusError} from './constants' import {Format, Show, Status, StatusError} from './constants'
export function input (state, action) { export function input (state, action) {
switch (action.type) { switch (action.type) {
case 'UPDATE_ORIGINAL_INPUT': case 'UPDATE_ORIGINAL_INPUT':
@ -25,7 +24,6 @@ export function format (state, action) {
} }
} }
export function show (state, action) { export function show (state, action) {
switch (action.type) { switch (action.type) {
case 'SHOW_ORIGINAL': case 'SHOW_ORIGINAL':
@ -61,15 +59,17 @@ export function saveStatus (state, action) {
// tracks status of the app, especially with respect to loaded and saved user data // tracks status of the app, especially with respect to loaded and saved user data
export function status (state, action) { export function status (state, action) {
// the status or error type is valid if it is in the list of Status or StatusError types // the status or error type is valid if it is in the list of Status or StatusError types
const isValidStatus = (type) => Status[type] == type const isValidStatus = (type) => Status[type] === type
const isValidError = (type) => StatusError[type] == type const isValidError = (type) => StatusError[type] === type
// the error is cleared when status changes // the error is cleared when status changes
if (action.type == 'STATUS_SET' && isValidStatus(action.data)) if (action.type === 'STATUS_SET' && isValidStatus(action.data)) {
return {type: action.data, error: null, hasError: false, errorType: null} return {type: action.data, error: null, hasError: false, errorType: null}
}
// the error is set in addition to the status // the error is set in addition to the status
else if (action.type == 'STATUS_SET_ERROR' && isValidError(action.data)) else if (action.type === 'STATUS_SET_ERROR' && isValidError(action.data)) {
return Object.assign({}, state, {error: action.error, hasError: true, errorType: action.data}) return Object.assign({}, state, {error: action.error, hasError: true, errorType: action.data})
else } else {
return state || {type: Status.EMPTY, hasError: false, error: null} return state || {type: Status.EMPTY, hasError: false, error: null}
} }
}

View File

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

View File

@ -4,12 +4,10 @@ import { createSelector } from 'reselect'
import React from 'react' import React from 'react'
import {Format, Show} from './constants' import {Format, Show} from './constants'
import * as Dubdiff from './util/dubdiff' import * as Dubdiff from './util/dubdiff'
const input = (state) => state.input const input = (state) => state.input
const format = (state) => state.format const format = (state) => state.format
const show = (state) => state.show const show = (state) => state.show
@ -25,14 +23,14 @@ export const safeInput = createSelector(
export const isMarkdownFormat = createSelector( export const isMarkdownFormat = createSelector(
[format], [format],
(format) => { (format) => {
return format == Format.MARKDOWN return format === Format.MARKDOWN
} }
) )
const isShow = (type) => createSelector( const isShow = (type) => createSelector(
[show], [show],
(show) => { (show) => {
return show == type return show === type
} }
) )
@ -40,18 +38,17 @@ export const isShowOriginal = isShow(Show.ORIGINAL)
export const isShowFinal = isShow(Show.FINAL) export const isShowFinal = isShow(Show.FINAL)
export const isShowDifference = isShow(Show.DIFFERENCE) export const isShowDifference = isShow(Show.DIFFERENCE)
export const diff = createSelector( export const diff = createSelector(
[format, safeInput], [format, safeInput],
(format, safeInput) => { (format, safeInput) => {
if (format==Format.PLAINTEXT) if (format === Format.PLAINTEXT) {
return Dubdiff.plaintextDiff(safeInput.original, safeInput.final) return Dubdiff.plaintextDiff(safeInput.original, safeInput.final)
else if (format==Format.MARKDOWN) } else if (format === Format.MARKDOWN) {
return Dubdiff.markdownDiff(safeInput.original, safeInput.final) return Dubdiff.markdownDiff(safeInput.original, safeInput.final)
} }
}
) )
/* /*
html diff html diff
--- ---

View File

@ -15,12 +15,10 @@ class EditorsDiff extends Diff {
equals (left, right) { equals (left, right) {
return ( return (
left.string == right.string left.string === right.string
) )
} }
// splits the input string into a series of word and punctuation tokens // splits the input string into a series of word and punctuation tokens
// each token is associated with an optional trailing array of spaces // each token is associated with an optional trailing array of spaces
tokenize (value) { tokenize (value) {
@ -28,13 +26,13 @@ class EditorsDiff extends Diff {
let annotatedTokens = [] let annotatedTokens = []
tokens.forEach(token => { tokens.forEach(token => {
if (isSpace(token)) { if (isSpace(token)) {
if (annotatedTokens.length == 0) if (annotatedTokens.length === 0) {
annotatedTokens.push({string: '', whitespace: []}) annotatedTokens.push({string: '', whitespace: []})
}
let last = annotatedTokens[annotatedTokens.length - 1] let last = annotatedTokens[annotatedTokens.length - 1]
last.whitespace.push(token) last.whitespace.push(token)
} } else {
else {
annotatedTokens.push({string: token, whitespace: []}) annotatedTokens.push({string: token, whitespace: []})
} }
}) })
@ -55,9 +53,6 @@ class EditorsDiff extends Diff {
} }
} }
export default EditorsDiff export default EditorsDiff
const isSpace = str => /[ ]+/.test(str) const isSpace = str => /[ ]+/.test(str)

View File

@ -1,8 +1,7 @@
import * as JsDiff from 'diff'
import EditorsDiff from './EditorsDiff' import EditorsDiff from './EditorsDiff'
let plaintextDiffer = new EditorsDiff() let plaintextDiffer = new EditorsDiff()
let markdownDiffer = new EditorsDiff(/([\s,.:]|[*\[\]\(\)])/) let markdownDiffer = new EditorsDiff(/([\s,.:]|[*\[\]()])/)
// returns a comparison of the texts as plaintext // returns a comparison of the texts as plaintext
export function plaintextDiff (original, final) { export function plaintextDiff (original, final) {
@ -21,14 +20,13 @@ export function markdownDiff(original, final) {
// returns a string version of the diff, with "{+ ... +}" and "[- ... -]" // returns a string version of the diff, with "{+ ... +}" and "[- ... -]"
// representing ins and del blocks // representing ins and del blocks
export function diffToString (diff, tags = {added: {start: '{+', end: '+}'}, removed: {start: '[-', end: '-]'}, same: {start: '', end: ''}}) { export function diffToString (diff, tags = {added: {start: '{+', end: '+}'}, removed: {start: '[-', end: '-]'}, same: {start: '', end: ''}}) {
return diff.map(({added, removed, value}) => { return diff.map(({added, removed, value}) => {
let {start, end} = added ? tags.added : (removed ? tags.removed : tags.same) let {start, end} = added ? tags.added : (removed ? tags.removed : tags.same)
let string = value let string = value
if (Array.isArray(value)) if (Array.isArray(value)) {
string = value.join('') string = value.join('')
}
return start + string + end return start + string + end
}).join('') }).join('')
@ -38,7 +36,6 @@ export function diffToHtml(diff) {
return diffToString(diff, {added: {start: '<ins>', end: '</ins>'}, removed: {start: '<del>', end: '</del>'}, same: {start: '', end: ''}}) return diffToString(diff, {added: {start: '<ins>', end: '</ins>'}, removed: {start: '<del>', end: '</del>'}, same: {start: '', end: ''}})
} }
// Rewrites the given diff to correctly render as markdown, assuming the source // Rewrites the given diff to correctly render as markdown, assuming the source
// documents were also valid markdown. // documents were also valid markdown.
// In essence, moves the markdown formatting elements in or out of the inserted and deleted blocks, as appropriate // In essence, moves the markdown formatting elements in or out of the inserted and deleted blocks, as appropriate
@ -75,7 +72,10 @@ function rewriteMarkdownDiff(diff) {
function applyTransformationRuleMultilineDelThenIns (diff) { function applyTransformationRuleMultilineDelThenIns (diff) {
let transformedDiff = [] let transformedDiff = []
const B_ADDED='added', B_REMOVED='removed', B_SAME='same' const B_ADDED = 'added'
const B_REMOVED = 'removed'
const B_SAME = 'same'
let previousBlockType = null let previousBlockType = null
let currentBlockType = null let currentBlockType = null
let previousBlockWasMultiline = false let previousBlockWasMultiline = false
@ -83,7 +83,6 @@ function applyTransformationRuleMultilineDelThenIns(diff) {
// iterate the input tokens to create the intermediate representation // iterate the input tokens to create the intermediate representation
diff.forEach((currentBlock) => { diff.forEach((currentBlock) => {
previousBlockType = currentBlockType previousBlockType = currentBlockType
previousBlockWasMultiline = currentBlockIsMultiline previousBlockWasMultiline = currentBlockIsMultiline
currentBlockType = (currentBlock.added ? B_ADDED : (currentBlock.removed ? B_REMOVED : B_SAME)) currentBlockType = (currentBlock.added ? B_ADDED : (currentBlock.removed ? B_REMOVED : B_SAME))
@ -92,8 +91,7 @@ function applyTransformationRuleMultilineDelThenIns(diff) {
// transform rule 1 applys when: // transform rule 1 applys when:
// the previous block was a del and had multiple lines // the previous block was a del and had multiple lines
// the current block is an ins // the current block is an ins
if (previousBlockType == B_REMOVED && currentBlockType == B_ADDED && previousBlockWasMultiline) { if (previousBlockType === B_REMOVED && currentBlockType === B_ADDED && previousBlockWasMultiline) {
// split the first line from the current block // split the first line from the current block
let currentBlockSplit = splitMultilineDiffBlock(currentBlock) let currentBlockSplit = splitMultilineDiffBlock(currentBlock)
@ -103,16 +101,14 @@ function applyTransformationRuleMultilineDelThenIns(diff) {
// split the first line from the previous block // split the first line from the previous block
let previousBlockSplit = splitMultilineDiffBlock(previousBlock) let previousBlockSplit = splitMultilineDiffBlock(previousBlock)
// now add the blocks back, interleaving del and ins blocks // now add the blocks back, interleaving del and ins blocks
for (let i = 0; i < Math.max(previousBlockSplit.length, currentBlockSplit.length); i++) { for (let i = 0; i < Math.max(previousBlockSplit.length, currentBlockSplit.length); i++) {
if (i<previousBlockSplit.length) if (i < previousBlockSplit.length) {
transformedDiff.push(previousBlockSplit[i]) transformedDiff.push(previousBlockSplit[i])
if (i<currentBlockSplit.length)
transformedDiff.push(currentBlockSplit[i])
} }
if (i < currentBlockSplit.length) { transformedDiff.push(currentBlockSplit[i]) }
} }
else { } else {
// otherwise, we just add the current block to the transformed list // otherwise, we just add the current block to the transformed list
transformedDiff.push(currentBlock) transformedDiff.push(currentBlock)
} }
@ -127,26 +123,25 @@ function applyTransformationRuleMultilineDelThenIns(diff) {
function applyTransformationRuleBreakUpDelIns (diff) { function applyTransformationRuleBreakUpDelIns (diff) {
let transformedDiff = [] let transformedDiff = []
const B_ADDED='added', B_REMOVED='removed', B_SAME='same' const B_ADDED = 'added'
const B_REMOVED = 'removed'
const B_SAME = 'same'
let blockType = null let blockType = null
let blockIsMultiline = false let blockIsMultiline = false
// iterate the input tokens to create the intermediate representation // iterate the input tokens to create the intermediate representation
diff.forEach((block) => { diff.forEach((block) => {
blockType = (block.added ? B_ADDED : (block.removed ? B_REMOVED : B_SAME)) blockType = (block.added ? B_ADDED : (block.removed ? B_REMOVED : B_SAME))
blockIsMultiline = isMultilineDiffBlock(block) blockIsMultiline = isMultilineDiffBlock(block)
// transform rule applys when: // transform rule applys when:
// the current block is an ins or del and is multiline // the current block is an ins or del and is multiline
if ((blockType == B_REMOVED || blockType == B_ADDED) && blockIsMultiline) { if ((blockType === B_REMOVED || blockType === B_ADDED) && blockIsMultiline) {
// split the first line from the current block // split the first line from the current block
let blockSplit = splitMultilineDiffBlock(block) let blockSplit = splitMultilineDiffBlock(block)
blockSplit.forEach(blockSplitLine => transformedDiff.push(blockSplitLine)) blockSplit.forEach(blockSplitLine => transformedDiff.push(blockSplitLine))
} } else {
else {
// otherwise, we just add the current block to the transformed list // otherwise, we just add the current block to the transformed list
transformedDiff.push(block) transformedDiff.push(block)
} }
@ -155,10 +150,8 @@ function applyTransformationRuleBreakUpDelIns(diff) {
return transformedDiff return transformedDiff
} }
// Transformation rule number 4: remove empty blocks // Transformation rule number 4: remove empty blocks
function applyTransformationRuleRemoveEmpty (diff) { function applyTransformationRuleRemoveEmpty (diff) {
return diff.filter(({value}) => value.length > 0) return diff.filter(({value}) => value.length > 0)
} }
@ -171,7 +164,7 @@ function applyTransformationRuleRemoveEmpty(diff) {
// |([ \t]+[0-9]+\.) - numeric lists // |([ \t]+[0-9]+\.) - numeric lists
// )? // )?
// [ \t]+ - trailing whitespace // [ \t]+ - trailing whitespace
const MARKDOWN_PREFIX = /^([ \t]*\>)*(([ \t]*#*)|([ \t]*[\*\+-])|([ \t]*[\d]+\.))?[ \t]+/ const MARKDOWN_PREFIX = /^([ \t]*>)*(([ \t]*#*)|([ \t]*[*+\-])|([ \t]*[\d]+\.))?[ \t]+/
// matches strings that end with a newline followed by some whitespace // matches strings that end with a newline followed by some whitespace
const NEWLINE_SUFFIX = /\n\s*$/ const NEWLINE_SUFFIX = /\n\s*$/
@ -189,7 +182,6 @@ function applyTransformationRuleFormattingPrefix(diff) {
// iterate the input tokens to create the intermediate representation // iterate the input tokens to create the intermediate representation
diff.forEach((currentBlock) => { diff.forEach((currentBlock) => {
if (isNewline && (currentBlock.added || currentBlock.removed)) { if (isNewline && (currentBlock.added || currentBlock.removed)) {
let match = currentBlock.value.match(MARKDOWN_PREFIX) let match = currentBlock.value.match(MARKDOWN_PREFIX)
if (match) { if (match) {
@ -202,30 +194,26 @@ function applyTransformationRuleFormattingPrefix(diff) {
} }
transformedDiff.push(preBlock) transformedDiff.push(preBlock)
transformedDiff.push(postBlock) transformedDiff.push(postBlock)
} } else {
else {
transformedDiff.push(currentBlock) transformedDiff.push(currentBlock)
} }
} } else {
else {
transformedDiff.push(currentBlock) transformedDiff.push(currentBlock)
isNewline = NEWLINE_SUFFIX.test(currentBlock.value) isNewline = NEWLINE_SUFFIX.test(currentBlock.value)
if (isNewline) if (isNewline) {
newlineString = currentBlock.value.match(NEWLINE_SUFFIX)[0] newlineString = currentBlock.value.match(NEWLINE_SUFFIX)[0]
} }
}
}) })
return transformedDiff return transformedDiff
} }
// returns true if the given diff block contains a newline element // returns true if the given diff block contains a newline element
function isMultilineDiffBlock ({value}) { function isMultilineDiffBlock ({value}) {
return value.indexOf('\n') != -1 return value.indexOf('\n') !== -1
} }
// returns an array of diff blocks that have the same added, removed fields as the given one // returns an array of diff blocks that have the same added, removed fields as the given one
// but with the string split by newlines // but with the string split by newlines
// if the diff block has no newlines, an array containing only that diff will be returned // if the diff block has no newlines, an array containing only that diff will be returned
@ -243,8 +231,5 @@ function splitMultilineDiffBlock({added, removed, value}) {
if (index < lines.length - 1) blocks.push({value: '\n'}) if (index < lines.length - 1) blocks.push({value: '\n'})
}) })
console.log(lines)
console.log(blocks)
return blocks return blocks
} }

View File

@ -1,30 +1,37 @@
var Path = require('path'); var Path = require('path')
var srcRoot = Path.join(__dirname, '..') var srcRoot = Path.join(__dirname, '..')
// there should be some option for distribution / optimization? // there should be some option for distribution / optimization?
var config = { var config = {
presets: ["node6", "react"], presets: ['node6', 'react'],
// enable source maps for non-production instances // enable source maps for non-production instances
sourceMaps: (process.env.NODE_ENV !== "production" ? "both" : false), sourceMaps: (process.env.NODE_ENV !== 'production' ? 'both' : false),
// highlightCode: false, // highlightCode: false,
sourceRoot: srcRoot, sourceRoot: srcRoot,
only: /src/ only: /src/
}; }
require('babel-core/register')(config); require('babel-core/register')(config)
var piping = require('piping')
main()
function main () {
// Enable piping for non-production environments // Enable piping for non-production environments
if (process.env.NODE_ENV !== "production") { if (process.env.NODE_ENV !== 'production') {
if (!require("piping")({hook: true, includeModules: false})) { // piping will return false for the initial invocation
return; // the app will be run again in an instance managed by piping
if (!piping({hook: true, includeModules: false})) {
return
} }
} }
try { try {
require('./index.js'); require('./index.js')
} catch (error) {
console.error(error.stack)
} }
catch (error) {
console.error(error.stack);
} }

View File

@ -3,7 +3,6 @@ import jf from 'jsonfile'
import fs from 'fs' import fs from 'fs'
import uuid from 'uuid' import uuid from 'uuid'
const router = express.Router() const router = express.Router()
router.get('/:id', showComparison) router.get('/:id', showComparison)
@ -56,7 +55,6 @@ function readRecord(res, id, data) {
// writes the record to the database, if it doesn't exist // writes the record to the database, if it doesn't exist
function writeRecord (res, id, data) { function writeRecord (res, id, data) {
// look up its filename // look up its filename
var filename = fnData(id) var filename = fnData(id)
@ -67,13 +65,12 @@ function writeRecord(res, id, data) {
// if the file already exists, return a 405 // if the file already exists, return a 405
if (exists) return res.status(405).send(`Data id ${id} is already in use.`) if (exists) return res.status(405).send(`Data id ${id} is already in use.`)
// and write it to the filesystem // and write it to the filesystem
jf.writeFile(filename, data, (err) => ( jf.writeFile(filename, data, (err) => (
err ? err
handleError(res, err) : ? handleError(res, err)
// if successful, return the comparison object // if successful, return the comparison object
res.status(201).json(data) : res.status(201).json(data)
)) ))
}) })
} }
@ -85,7 +82,6 @@ function handleError(res, err) {
return res.send(500, err) return res.send(500, err)
} }
// returns a filename for the given comparison // returns a filename for the given comparison
function fnData (id) { function fnData (id) {
return `./data/${id}.json` return `./data/${id}.json`

View File

@ -7,7 +7,6 @@ import fetch from 'isomorphic-fetch'
import comparisonRouter from './comparison' import comparisonRouter from './comparison'
import * as reducers from '../common/reducers' import * as reducers from '../common/reducers'
import {Status, StatusError} from '../common/constants' import {Status, StatusError} from '../common/constants'
@ -15,7 +14,7 @@ import {Status, StatusError} from '../common/constants'
import render from './render' import render from './render'
// set use port 8080 for dev, 80 for production // set use port 8080 for dev, 80 for production
const PORT = (process.env.NODE_ENV !== "production" ? 8080 : 80) const PORT = (process.env.NODE_ENV !== 'production' ? 8080 : 80)
const app = express() const app = express()
@ -24,26 +23,22 @@ app.use('/dist', express.static(path.join(__dirname, '..', '..', 'dist')))
// serve the comparison api at /api/compare // serve the comparison api at /api/compare
app.use(bodyParser.json()) app.use(bodyParser.json())
app.use('/api/compare', comparisonRouter); app.use('/api/compare', comparisonRouter)
// the following routes are for server-side rendering of the app // the following routes are for server-side rendering of the app
// we should render the comparison directly from the server // we should render the comparison directly from the server
// this loading logic could be moved into ../common/actions because it is isomorphic // this loading logic could be moved into ../common/actions because it is isomorphic
app.route('/:comparisonId') app.route('/:comparisonId')
.get((req, res) => { .get((req, res) => {
const store = createSessionStore() const store = createSessionStore()
const endpointUri = `http://localhost:${PORT}/api/compare/${req.params.comparisonId}` const endpointUri = `http://localhost:${PORT}/api/compare/${req.params.comparisonId}`
// fetch the comparison // fetch the comparison
fetch(endpointUri) fetch(endpointUri)
.then(response => { .then(response => {
if (response.ok) if (response.ok) {
return response.json() return response.json()
else { } else {
response.text().then(() => { response.text().then(() => {
const error = {message: `${response.status}: ${response.statusText}`} const error = {message: `${response.status}: ${response.statusText}`}
initAndRenderError(error, store, req, res) initAndRenderError(error, store, req, res)
@ -56,19 +51,16 @@ app.route('/:comparisonId')
.catch(error => { .catch(error => {
initAndRenderError(error, store, req, res) initAndRenderError(error, store, req, res)
}) })
}) })
app.route('/') app.route('/')
.get((req, res) => { .get((req, res) => {
render(createSessionStore(), req, res) render(createSessionStore(), req, res)
}) })
app.listen(PORT, function () { app.listen(PORT, function () {
console.log(`Server listening on port ${PORT}.`) console.log(`Server listening on port ${PORT}.`)
}) })
// creates the session store // creates the session store
function createSessionStore () { function createSessionStore () {
// create the redux store // create the redux store

View File

@ -5,12 +5,11 @@ import { match, RouterContext } from 'react-router'
import routes from '../common/routes.js' import routes from '../common/routes.js'
export default function render (store, req, res) { export default function render (store, req, res) {
// Send the rendered page back to the client // Send the rendered page back to the client
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => { match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
if (error) { if (error) {
res.status(500).send(renderError('Routing Error:', error.message)) res.status(500).send(errorTemplate('Routing Error:', error.message))
} else if (redirectLocation) { } else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search) res.redirect(302, redirectLocation.pathname + redirectLocation.search)
} else if (renderProps) { } else if (renderProps) {
@ -26,12 +25,10 @@ export default function render(store, req, res) {
const initialState = store.getState() const initialState = store.getState()
// and send // and send
res.status(200).send(appTemplate(html, initialState)) res.status(200).send(appTemplate(html, initialState))
} } catch (ex) {
catch(ex) { console.log('Render Exception:', ex)
console.log("Render Exception:",ex)
res.status(500).send(errorTemplate('Render Exception', ex.message, ex)) res.status(500).send(errorTemplate('Render Exception', ex.message, ex))
} }
} else { } else {
res.status(404).send(errorTemplate('Not found', `${req.url} not found.`)) res.status(404).send(errorTemplate('Not found', `${req.url} not found.`))
} }
@ -66,15 +63,13 @@ function errorTemplate(title, message, exception) {
return pageTemplate(` return pageTemplate(`
<h1>${title}</h1> <h1>${title}</h1>
<p>${message}</p> <p>${message}</p>
${exception ? ${exception
`<pre>${exception.toString()}</pre>`: ? `<pre>${exception.toString()}</pre>`
`` : ``
} }
`) `)
} }
function appTemplate (html, initialState) { function appTemplate (html, initialState) {
return pageTemplate(` return pageTemplate(`
<div id="root">${html}</div> <div id="root">${html}</div>

View File

@ -1,21 +1,19 @@
/* eslint-env node, mocha */ /* eslint-env node, mocha */
/* global expect */ /* global expect */
/* eslint no-console: 0 */ /* eslint no-console: 0 */
'use strict'; 'use strict'
import chai from 'chai' import chai from 'chai'
import {markdownDiff, diffToString, diffToHtml} from '../src/common/util/dubdiff' import {markdownDiff, diffToString} from '../src/common/util/dubdiff'
let diff = (a, b) => diffToString(markdownDiff(a, b)) let diff = (a, b) => diffToString(markdownDiff(a, b))
const expect = chai.expect const expect = chai.expect // eslint-disable-line no-unused-vars
describe('dubdiff', () => { describe('dubdiff', () => {
let db;
beforeEach(() => { beforeEach(() => {
}); })
it('plaintext diffs consecutive words', () => { it('plaintext diffs consecutive words', () => {
expect(diff( expect(diff(
@ -75,13 +73,13 @@ other`,
expect(diff( expect(diff(
'Hello\n# Title 1\n# Title 2', 'Hello\n# Title 1\n# Title 2',
'Hello\n# Title 2' 'Hello\n# Title 2'
)).to.equal('Hello\n# Title [-1-]\n# [-Title -]2',) )).to.equal('Hello\n# Title [-1-]\n# [-Title -]2')
}) })
it('deletes a more different title', () => { it('deletes a more different title', () => {
expect(diff( expect(diff(
'Hello\n# Filbert\n# Title 2', 'Hello\n# Filbert\n# Title 2',
'Hello\n# Title 2' 'Hello\n# Title 2'
)).to.equal('Hello\n# [-Filbert-]\n# Title 2',) )).to.equal('Hello\n# [-Filbert-]\n# Title 2')
}) })
}) })

View File

@ -1,7 +1,7 @@
/* eslint-env node, mocha */ /* eslint-env node, mocha */
/* global expect */ /* global expect */
/* eslint no-console: 0 */ /* eslint no-console: 0 */
'use strict'; 'use strict'
import chai from 'chai' import chai from 'chai'
@ -9,12 +9,11 @@ import {plaintextDiff, diffToString} from '../src/common/util/dubdiff'
let diff = (a, b) => diffToString(plaintextDiff(a, b)) let diff = (a, b) => diffToString(plaintextDiff(a, b))
const expect = chai.expect const expect = chai.expect // eslint-disable-line no-unused-vars
describe('dubdiff', () => { describe('dubdiff', () => {
beforeEach(() => { beforeEach(() => {
}); })
it('diffs single words', () => { it('diffs single words', () => {
expect(diff( expect(diff(

View File

@ -13,10 +13,10 @@ let config = {
loader: 'babel-loader', loader: 'babel-loader',
query: { query: {
presets: ['es2015-native-modules', 'react'], presets: ['es2015-native-modules', 'react'],
compact: "true" compact: 'true'
} }
}, },
{ test: /\.json$/, loader: "json-loader" }, { test: /\.json$/, loader: 'json-loader' }
] ]
}, },
node: { node: {
@ -24,13 +24,12 @@ let config = {
net: 'empty', net: 'empty',
tls: 'empty' tls: 'empty'
} }
};
if (process.env.NODE_ENV == "production") {
config.devtool = "cheap-module-source-map"
}
else {
config.devtool = "eval"
} }
module.exports = config; if (process.env.NODE_ENV === 'production') {
config.devtool = 'cheap-module-source-map'
} else {
config.devtool = 'eval'
}
module.exports = config