implement choo v5 API (#425)
* implement choo v5 API * fix tests * 5.0.0-3 * 4kb now * bump nanomorph * 5.0.0-4 * fix missing deps stuff * 5.0.0-5 * fix travis * fixup! oops standard * chooexpose * fix state passing * fixup! update example * fixup! incorporate feedback * fixup! more docs * fixup! even more docs
This commit is contained in:
@@ -1,42 +0,0 @@
|
||||
const choo = require('../../')
|
||||
const html = require('../../html')
|
||||
const plur = require('plur')
|
||||
|
||||
const app = choo()
|
||||
app.model({
|
||||
state: {
|
||||
counter: 0
|
||||
},
|
||||
reducers: {
|
||||
increment: (state, data) => ({ counter: state.counter + 1 }),
|
||||
decrement: (state, data) => ({ counter: state.counter - 1 })
|
||||
},
|
||||
effects: {
|
||||
incrementAsync: function (state, data, send, done) {
|
||||
setTimeout(() => send('increment', done), 1000)
|
||||
},
|
||||
decrementAsync: function (state, data, send, done) {
|
||||
setTimeout(() => send('decrement', done), 1000)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function mainView (state, prev, send) {
|
||||
const count = state.counter
|
||||
|
||||
return html`
|
||||
<main class="app">
|
||||
<h1>Async counter</h1>
|
||||
<p>Clicked ${count} ${plur('time', count)}!</p>
|
||||
<button onclick=${() => send('increment')}>Increment</button>
|
||||
<button onclick=${() => send('decrement')}>Decrement</button>
|
||||
<button onclick=${() => send('incrementAsync')}>Increment async</button>
|
||||
<button onclick=${() => send('decrementAsync')}>Decrement async</button>
|
||||
</main>
|
||||
`
|
||||
}
|
||||
|
||||
app.router([ '/', mainView ])
|
||||
|
||||
const tree = app.start()
|
||||
document.body.appendChild(tree)
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "cancellable-counter",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "client.js",
|
||||
"scripts": {
|
||||
"start": "bankai start --entry=client -p 8080"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Juan Soto <juan@juansoto.me>",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bankai": "^3.2.0",
|
||||
"plur": "^2.1.2"
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
const choo = require('../../')
|
||||
|
||||
const mainView = require('./views/main')
|
||||
|
||||
const app = choo({
|
||||
onError: function (err, state, createSend) {
|
||||
console.trace()
|
||||
console.groupCollapsed(`Error: ${err.message}`)
|
||||
console.error(err)
|
||||
console.groupEnd()
|
||||
const send = createSend('onError: ')
|
||||
send('app:error', err)
|
||||
},
|
||||
onAction: function (state, data, name, caller, createSend) {
|
||||
console.groupCollapsed(`Action: ${caller} -> ${name}`)
|
||||
console.log(data)
|
||||
console.groupEnd()
|
||||
},
|
||||
onStateChange: function (state, data, prev, createSend) {
|
||||
console.groupCollapsed('State')
|
||||
console.log(prev)
|
||||
console.log(state)
|
||||
console.groupEnd()
|
||||
}
|
||||
})
|
||||
|
||||
app.model(require('./models/error'))
|
||||
app.model(require('./models/api'))
|
||||
|
||||
app.router(['/', mainView])
|
||||
|
||||
const tree = app.start()
|
||||
document.body.appendChild(tree)
|
||||
@@ -1,31 +0,0 @@
|
||||
const http = require('../../../http')
|
||||
|
||||
module.exports = {
|
||||
namespace: 'api',
|
||||
state: {
|
||||
title: 'Button pushing machine 3000'
|
||||
},
|
||||
reducers: {
|
||||
set: (state, data) => ({ 'title': data })
|
||||
},
|
||||
effects: {
|
||||
good: function (state, data, send, done) {
|
||||
request('/good', send, done)
|
||||
},
|
||||
bad: (state, data, send, done) => request('/bad', send, done)
|
||||
}
|
||||
}
|
||||
|
||||
function request (uri, send, done) {
|
||||
http(uri, { json: true }, function (err, res, body) {
|
||||
if (err) return done(new Error('HTTP error'))
|
||||
if (res.statusCode !== 200) {
|
||||
const message = (body && body.message)
|
||||
? body.message
|
||||
: 'unknown server error'
|
||||
return done(new Error(message))
|
||||
}
|
||||
if (!body) return done(new Error('fatal: no body received'))
|
||||
send('api:set', body.message || body.title, done)
|
||||
})
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Create a new error. Errors are pushed into an array so they can be
|
||||
// queued. We're doing a bit of timestamp trickery here so each error
|
||||
// is shown for exactly one second before being dropped. Probably
|
||||
// useful for real world scenarios; especially when coupled to
|
||||
// fancy CSS animation thingies
|
||||
|
||||
// Each error is displayed 1 second
|
||||
const ERROR_TIMEOUT = 1000
|
||||
|
||||
module.exports = {
|
||||
namespace: 'app',
|
||||
state: {
|
||||
errors: [],
|
||||
errorTimeDone: 0,
|
||||
triggerTime: null
|
||||
},
|
||||
reducers: {
|
||||
setError: function (state, data) {
|
||||
return {
|
||||
errors: state.errors.concat(data.message),
|
||||
errorTimeDone: data.errorTimeDone
|
||||
}
|
||||
},
|
||||
'delError': function (state, data) {
|
||||
state.errors.shift()
|
||||
return { errors: state.errors }
|
||||
}
|
||||
},
|
||||
effects: {
|
||||
error: function (err, state, send, done) {
|
||||
const timeDone = state.errorTimeDone
|
||||
const now = Date.now()
|
||||
|
||||
const timeStamp = (timeDone && timeDone >= now)
|
||||
? timeDone + ERROR_TIMEOUT
|
||||
: now + ERROR_TIMEOUT
|
||||
|
||||
const timeout = timeStamp - now
|
||||
|
||||
const errAction = {
|
||||
message: err.message,
|
||||
errorTimeDone: timeStamp
|
||||
}
|
||||
send('app:setError', errAction, function (err) {
|
||||
if (err) return done(err)
|
||||
setTimeout(function () {
|
||||
send('app:delError', done)
|
||||
}, timeout)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"name": "http",
|
||||
"private": true,
|
||||
"main": "client.js",
|
||||
"scripts": {
|
||||
"start": "NODE_ENV=development node server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"bankai": "^2.0.2",
|
||||
"browserify": "^13.0.1",
|
||||
"server-router": "^2.1.0"
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
const serverRouter = require('server-router')
|
||||
const browserify = require('browserify')
|
||||
const bankai = require('bankai')
|
||||
const http = require('http')
|
||||
|
||||
const PORT = 8080
|
||||
|
||||
const server = http.createServer(createRouter())
|
||||
server.listen(PORT, () => process.stdout.write(`listening on port ${PORT}\n`))
|
||||
|
||||
var index = 0
|
||||
const errors = [
|
||||
'a giant robot invaded robot town!',
|
||||
'something weird crawled out of the swamp',
|
||||
'oh no, the bear people invaded the server!',
|
||||
'the NSA',
|
||||
'12 goats started a band',
|
||||
'we kinda just gave up'
|
||||
]
|
||||
|
||||
function createRouter () {
|
||||
const router = serverRouter('/404')
|
||||
|
||||
const html = bankai.html({ css: false })
|
||||
router.on('/', (req, res) => html(req, res).pipe(res))
|
||||
|
||||
const js = bankai.js(browserify, require.resolve('./client.js'))
|
||||
router.on('/bundle.js', (req, res) => js(req, res).pipe(res))
|
||||
|
||||
router.on('/good', (req, res) => {
|
||||
res.end('{ "message": "all is well in robo town!" }')
|
||||
})
|
||||
|
||||
router.on('/bad', (req, res) => {
|
||||
res.statusCode = 500
|
||||
res.end(`{ "message": "${errors[index++ % (errors.length)]}" }`)
|
||||
})
|
||||
|
||||
router.on('/404', (req, res) => {
|
||||
res.statusCode = 404
|
||||
res.end('{ "message": "the server is confused" }')
|
||||
})
|
||||
|
||||
return router
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
const html = require('../../../html')
|
||||
|
||||
module.exports = function (state, prev, send) {
|
||||
const error = state.app.errors[0]
|
||||
const title = state.api.title
|
||||
return html`
|
||||
<section>
|
||||
<h1>${title}</h1>
|
||||
<h2>Latest error: ${error}</h2>
|
||||
<button onclick=${(e) => send('api:good')}>OK!</button>
|
||||
<button onclick=${(e) => send('api:bad')}>Naughty</button>
|
||||
</section>
|
||||
`
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
const log = require('choo-log')
|
||||
const sf = require('sheetify')
|
||||
|
||||
const choo = require('../../')
|
||||
|
||||
sf('css-wipe/dest/bundle')
|
||||
|
||||
const app = choo()
|
||||
app.use(log())
|
||||
|
||||
app.model(require('./models/inbox'))
|
||||
app.model(require('./models/spam'))
|
||||
app.model(require('./models/sent'))
|
||||
|
||||
app.router([
|
||||
['/', require('./views/empty')],
|
||||
['/:mailbox', require('./views/mailbox'), [
|
||||
['/:message', require('./views/email')]
|
||||
]]
|
||||
])
|
||||
|
||||
const tree = app.start()
|
||||
document.body.appendChild(tree)
|
||||
@@ -1,36 +0,0 @@
|
||||
const dateformat = require('dateformat')
|
||||
const html = require('../../../html')
|
||||
|
||||
module.exports = function (state, prev, send) {
|
||||
const params = state.params
|
||||
const mailbox = params.mailbox
|
||||
const messages = state[mailbox].messages
|
||||
return html`
|
||||
<div>
|
||||
<div class="db cf w-100">
|
||||
<div class="fl mb3 w-25 mt0 b">Date</th>
|
||||
<div class="fl mb3 w-25 mt0 b">Subject</th>
|
||||
<div class="fl mb3 w-25 mt0 b">From</th>
|
||||
<div class="fl mb3 w-25 mt0 b">To</th>
|
||||
</div>
|
||||
${messages.map(function (msg) {
|
||||
return createMessage(msg, mailbox)
|
||||
})}
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function createMessage (message, mailbox) {
|
||||
return html`
|
||||
<div class="db cf w-100">
|
||||
<a href="${'/' + mailbox + '/' + message.id}">
|
||||
<div class="fl mb3 w-25 f6 link">
|
||||
${dateformat(message.date, 'mmmm dS')}
|
||||
</div>
|
||||
<div class="fl mb3 w-25 f6 link">${message.subject}</div>
|
||||
<div class="fl mb3 w-25 f6 link">${message.from}</div>
|
||||
<div class="fl mb3 w-25 f6 link">${message.to}</div>
|
||||
</a>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
const html = require('../../../html')
|
||||
|
||||
module.exports = function (state, prev, send) {
|
||||
const params = state.params
|
||||
const mailbox = params.mailbox
|
||||
const message = params.message
|
||||
|
||||
const email = state[mailbox].messages.filter(function (msg) {
|
||||
return String(msg.id) === message
|
||||
})[0]
|
||||
|
||||
return html`
|
||||
<div>
|
||||
${email ? createEmail(email) : 'error: no email found'}
|
||||
</div
|
||||
`
|
||||
}
|
||||
|
||||
function createEmail (message) {
|
||||
return html`
|
||||
<div class="mail">
|
||||
<dl>
|
||||
<dt>From</dt>
|
||||
<dd>${message.from}</dd>
|
||||
<dt>To</dt>
|
||||
<dd>${message.to}</dd>
|
||||
<dt>Date</dt>
|
||||
<dd>${message.date}</dd>
|
||||
</dl>
|
||||
<h4>${message.subject}</h4>
|
||||
<p>${message.body}</p>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
const html = require('../../../html')
|
||||
|
||||
module.exports = function (state, prev, send) {
|
||||
return html`
|
||||
<section>
|
||||
<p>Select a mailbox</p>
|
||||
</section>
|
||||
`
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
const dateformat = require('dateformat')
|
||||
const html = require('../../../html')
|
||||
|
||||
module.exports = function () {
|
||||
return function (state, prev, send) {
|
||||
const params = state.params
|
||||
const mailbox = params.mailbox
|
||||
const message = params.message
|
||||
const messages = state[mailbox].messages
|
||||
|
||||
if (message) {
|
||||
const email = state[mailbox].messages.filter(function (msg) {
|
||||
return String(msg.id) === message
|
||||
})[0]
|
||||
|
||||
return html`
|
||||
<section class="fl mt4 w-80 db">
|
||||
<div>
|
||||
${createHeader()}
|
||||
${messages.map(function (msg) {
|
||||
return createMessage(msg, mailbox)
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
${email ? createEmail(email) : 'error: no email found'}
|
||||
</div
|
||||
</section>
|
||||
`
|
||||
} else {
|
||||
return html`
|
||||
<section class="fl mt4 w-80 db">
|
||||
${createHeader()}
|
||||
${messages.map(function (msg) {
|
||||
return createMessage(msg, mailbox)
|
||||
})}
|
||||
</section>
|
||||
`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createHeader () {
|
||||
return html`
|
||||
<div class="db cf w-100">
|
||||
<div class="fl mb3 w-25 mt0 b">Date</th>
|
||||
<div class="fl mb3 w-25 mt0 b">Subject</th>
|
||||
<div class="fl mb3 w-25 mt0 b">From</th>
|
||||
<div class="fl mb3 w-25 mt0 b">To</th>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function createMessage (message, mailbox) {
|
||||
return html`
|
||||
<div class="db cf w-100">
|
||||
<a href="${'/' + mailbox + '/' + message.id}">
|
||||
<div class="fl mb3 w-25 f6 link">
|
||||
${dateformat(message.date, 'mmmm dS')}
|
||||
</div>
|
||||
<div class="fl mb3 w-25 f6 link">${message.subject}</div>
|
||||
<div class="fl mb3 w-25 f6 link">${message.from}</div>
|
||||
<div class="fl mb3 w-25 f6 link">${message.to}</div>
|
||||
</a>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function createEmail (message) {
|
||||
return html`
|
||||
<div class="mail">
|
||||
<dl>
|
||||
<dt>From</dt>
|
||||
<dd>${message.from}</dd>
|
||||
<dt>To</dt>
|
||||
<dd>${message.to}</dd>
|
||||
<dt>Date</dt>
|
||||
<dd>${message.date}</dd>
|
||||
</dl>
|
||||
<h4>${message.subject}</h4>
|
||||
<p>${message.body}</p>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
const html = require('../../../html')
|
||||
|
||||
const mailboxes = [ 'inbox', 'spam', 'sent' ]
|
||||
|
||||
module.exports = function (state, prev, send) {
|
||||
return html`
|
||||
<aside class="fl mt4 w-20 db">
|
||||
<ul>
|
||||
<li>
|
||||
<h2 class="f4 b lh0">Mailbox</h2>
|
||||
</li>
|
||||
${mailboxes.map(function (mailbox) {
|
||||
const messages = mailbox.messages
|
||||
return createLi(mailbox, messages)
|
||||
})}
|
||||
</ul>
|
||||
</aside>
|
||||
`
|
||||
}
|
||||
|
||||
function createLi (mailbox, messages) {
|
||||
return html`
|
||||
<li class="mt4 f6">
|
||||
<a href="/${mailbox}">
|
||||
${mailbox.charAt(0).toUpperCase() + mailbox.slice(1)}
|
||||
<span>${messages}</span>
|
||||
</a>
|
||||
</li>
|
||||
`
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
const pathname = require('pathname-match')
|
||||
const html = require('../../../html')
|
||||
|
||||
module.exports = function (state, prev, send) {
|
||||
const location = state.location.pathname
|
||||
return html`
|
||||
<span class="fl mt4 w-100 f4 b">
|
||||
URL: ${pathname(location) || '/'}
|
||||
</span>
|
||||
`
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
module.exports = {
|
||||
namespace: 'inbox',
|
||||
state: {
|
||||
messages: [
|
||||
{
|
||||
id: 1,
|
||||
subject: 'Welcome to Choo',
|
||||
from: 'choochoo@choojs.com',
|
||||
to: 'user@example.com',
|
||||
date: new Date(),
|
||||
body: 'Welcome to Choo. We hope you enjoy your stay'
|
||||
}, {
|
||||
id: 2,
|
||||
subject: 'Great Choo Resources',
|
||||
from: 'choochoo@choojs.com',
|
||||
to: 'user@example.com',
|
||||
date: new Date(),
|
||||
body: 'Have you seen choocasts.com? How about chooaddons.com?'
|
||||
}
|
||||
]
|
||||
},
|
||||
reducers: { }
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
module.exports = {
|
||||
namespace: 'sent',
|
||||
state: {
|
||||
messages: [
|
||||
{
|
||||
id: 4,
|
||||
subject: 'Should I use Choo',
|
||||
from: 'user@example.com',
|
||||
to: 'choochoo@choojs.com',
|
||||
date: new Date(),
|
||||
body: 'Choo looks pretty good, should I use it?'
|
||||
}
|
||||
]
|
||||
},
|
||||
reducers: { }
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
module.exports = {
|
||||
namespace: 'spam',
|
||||
state: {
|
||||
messages: [
|
||||
{
|
||||
id: 3,
|
||||
subject: 'You have one the lottery!!!111ONEONE',
|
||||
from: '419@thereallotteryhonest.com',
|
||||
to: 'user@example.com',
|
||||
date: new Date(),
|
||||
body: 'You have ONE the lottery! You only have to send us a small amount of monies to claim your prize'
|
||||
}
|
||||
]
|
||||
},
|
||||
reducers: { }
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "mailbox",
|
||||
"version": "1.0.0",
|
||||
"private": "true",
|
||||
"main": "client.js",
|
||||
"scripts": {
|
||||
"start": "NODE_ENV=development node server.js"
|
||||
},
|
||||
"author": "Yoshua Wuyts <i@yoshuawuyts.com>",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"choo-log": "^1.4.1",
|
||||
"css-wipe": "^4.2.1",
|
||||
"dateformat": "^1.0.12",
|
||||
"pathname-match": "^1.1.3",
|
||||
"tachyons": "^4.0.0-beta.33"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bankai": "^2.0.5",
|
||||
"browserify": "^13.0.1",
|
||||
"insert-css": "^0.2.0",
|
||||
"server-router": "^2.1.0",
|
||||
"sheetify": "^5.0.3"
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
const serverRouter = require('server-router')
|
||||
const browserify = require('browserify')
|
||||
const bankai = require('bankai')
|
||||
const http = require('http')
|
||||
|
||||
const PORT = 8080
|
||||
|
||||
const server = http.createServer(createRouter())
|
||||
server.listen(PORT, () => process.stdout.write(`listening on port ${PORT}\n`))
|
||||
|
||||
function createRouter () {
|
||||
const router = serverRouter('/404')
|
||||
|
||||
const js = bankai.js(browserify, require.resolve('./client.js'))
|
||||
router.on('/bundle.js', (req, res) => js(req, res).pipe(res))
|
||||
router.on('/:inbox/bundle.js', (req, res) => js(req, res).pipe(res))
|
||||
|
||||
const html = bankai.html({ css: false })
|
||||
router.on('/', (req, res) => html(req, res).pipe(res))
|
||||
router.on('/:inbox', (req, res) => html(req, res).pipe(res))
|
||||
router.on('/:inbox/:message_id', (req, res) => html(req, res).pipe(res))
|
||||
|
||||
router.on('/hi', (req, res) => res.end('{ "message": "hi back!" }'))
|
||||
router.on('/404', (req, res) => {
|
||||
res.statusCode = 404
|
||||
res.end('{ "message": "the server is confused" }')
|
||||
})
|
||||
|
||||
return router
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
const html = require('../../../html')
|
||||
|
||||
const emailList = require('../elements/email-list')
|
||||
const pathname = require('../elements/pathname')
|
||||
const email = require('../elements/email')
|
||||
const nav = require('../elements/nav')
|
||||
|
||||
module.exports = function (state, prev, send) {
|
||||
return html`
|
||||
<main class="mw5 mw7-ns center cf">
|
||||
${pathname(state, prev, send)}
|
||||
${nav(state, prev, send)}
|
||||
<section class="fl mt4 w-80 db">
|
||||
${emailList(state, prev, send)}
|
||||
${email(state, prev, send)}
|
||||
</section>
|
||||
</main>
|
||||
`
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
const html = require('../../../html')
|
||||
|
||||
const empty = require('../elements/empty-mailbox')
|
||||
const pathname = require('../elements/pathname')
|
||||
const nav = require('../elements/nav')
|
||||
|
||||
module.exports = function (state, prev, send) {
|
||||
return html`
|
||||
<main class="mw5 mw7-ns center cf">
|
||||
${pathname(state, prev, send)}
|
||||
${nav(state, prev, send)}
|
||||
${empty(state, prev, send)}
|
||||
</main>
|
||||
`
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
const html = require('../../../html')
|
||||
|
||||
const emailList = require('../elements/email-list')
|
||||
const pathname = require('../elements/pathname')
|
||||
const nav = require('../elements/nav')
|
||||
|
||||
module.exports = function (state, prev, send) {
|
||||
return html`
|
||||
<main class="mw5 mw7-ns center cf">
|
||||
<button onclick=${goHome}>Home</button>
|
||||
${pathname(state, prev, send)}
|
||||
${nav(state, prev, send)}
|
||||
<section class="fl mt4 w-80 db">
|
||||
${emailList(state, prev, send)}
|
||||
</section>
|
||||
</main>
|
||||
`
|
||||
|
||||
function goHome () {
|
||||
send('location:set', { pathname: '/' })
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
const mount = require('../../mount')
|
||||
const choo = require('../../')
|
||||
|
||||
const mainView = require('./view')
|
||||
|
||||
const app = choo()
|
||||
|
||||
app.model({
|
||||
namespace: 'message',
|
||||
state: {
|
||||
server: 'rehydration has kicked in, server data was tossed',
|
||||
client: 'hello client!'
|
||||
}
|
||||
})
|
||||
|
||||
app.router(['/', mainView])
|
||||
|
||||
if (module.parent) {
|
||||
module.exports = app
|
||||
} else {
|
||||
const tree = app.start()
|
||||
mount('#app-root', tree)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "http",
|
||||
"private": true,
|
||||
"main": "client.js",
|
||||
"scripts": {
|
||||
"start": "NODE_ENV=development node server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"bankai": "^2.0.2",
|
||||
"bl": "^1.1.2",
|
||||
"browserify": "^13.0.1",
|
||||
"hyperstream": "^1.2.2",
|
||||
"server-router": "^2.1.0"
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
const serverRouter = require('server-router')
|
||||
const hyperstream = require('hyperstream')
|
||||
const browserify = require('browserify')
|
||||
const bankai = require('bankai')
|
||||
const http = require('http')
|
||||
|
||||
const PORT = 8080
|
||||
const client = require('./client')
|
||||
|
||||
// If an incoming request accepts "text/html", render the
|
||||
// appropriate HTML. Else use the API server
|
||||
const apiRouter = createRouter()
|
||||
const server = http.createServer(function (req, res) {
|
||||
if (/text\/html/.test(req.headers.accept)) handleHtml(req, res)
|
||||
else apiRouter(req, res)
|
||||
})
|
||||
server.listen(PORT, () => process.stdout.write(`listening on port ${PORT}\n`))
|
||||
|
||||
// create a new router
|
||||
// null -> fn
|
||||
function createRouter () {
|
||||
const apiRouter = serverRouter('/404')
|
||||
|
||||
apiRouter.on('/404', (req, res) => res.end('404 not found'))
|
||||
apiRouter.on('/', (req, res) => res.end('nothing to be found here'))
|
||||
|
||||
const js = bankai.js(browserify, require.resolve('./client.js'))
|
||||
apiRouter.on('/bundle.js', (req, res) => js(req, res).pipe(res))
|
||||
|
||||
return apiRouter
|
||||
}
|
||||
|
||||
// render the client to string
|
||||
// based on the requested url
|
||||
// (obj, obj) -> null
|
||||
const createIndex = bankai.html({ favicon: false, css: false })
|
||||
function handleHtml (req, res) {
|
||||
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
||||
|
||||
const state = { message: { server: 'hello server!' } }
|
||||
const inner = client.toString(req.url, state)
|
||||
const hs = hyperstream({ 'body': { _appendHtml: inner } })
|
||||
|
||||
createIndex(req, res).pipe(hs).pipe(res)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
const assert = require('assert')
|
||||
const html = require('../../html')
|
||||
|
||||
module.exports = function (state, prev, send) {
|
||||
const serverMessage = state.message.server
|
||||
const clientMessage = state.message.client
|
||||
|
||||
assert.equal(typeof serverMessage, 'string', 'server should be a string')
|
||||
assert.equal(typeof clientMessage, 'string', 'client should be a string')
|
||||
|
||||
return html`
|
||||
<section id="app-root">
|
||||
<h1>server message: ${serverMessage}</h1>
|
||||
<h1>client message: ${clientMessage}</h1>
|
||||
<p>${`
|
||||
The first message is passed in by the server on compile time,
|
||||
the second message was set by the client.
|
||||
The more static the data you pass in, the more cachable your site
|
||||
becomes (and thus performant). Try and keep the amount of properties
|
||||
you pass in on the server to a minimum for most applications - it'll
|
||||
make life a lot easier in the long run, hah.
|
||||
`}</p>
|
||||
</section>
|
||||
`
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
const choo = require('../../')
|
||||
const html = require('../../html')
|
||||
|
||||
const app = choo()
|
||||
app.model(createModel())
|
||||
app.router(['/', mainView])
|
||||
|
||||
const tree = app.start()
|
||||
document.body.appendChild(tree)
|
||||
|
||||
function mainView (state, prev, send) {
|
||||
return html`
|
||||
<div>${state.logger.msg}</div>
|
||||
`
|
||||
}
|
||||
|
||||
function createModel () {
|
||||
const stream = new window.EventSource('/sse')
|
||||
return {
|
||||
namespace: 'logger',
|
||||
state: {
|
||||
msg: ''
|
||||
},
|
||||
subscriptions: [
|
||||
function (send, done) {
|
||||
stream.onerror = (e) => {
|
||||
send('logger:error', { payload: JSON.stringify(e) }, done)
|
||||
}
|
||||
stream.onmessage = (e) => {
|
||||
const msg = JSON.parse(e.data).message
|
||||
send('logger:print', { payload: msg }, done)
|
||||
}
|
||||
}
|
||||
],
|
||||
reducers: {
|
||||
'print': (state, data) => {
|
||||
return ({ msg: state.msg + ' ' + data.payload })
|
||||
}
|
||||
},
|
||||
effects: {
|
||||
close: (state, data, send, done) => {
|
||||
stream.close()
|
||||
done()
|
||||
},
|
||||
error: (state, data, send, done) => {
|
||||
console.error(`error: ${data.payload}`)
|
||||
done()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "http",
|
||||
"private": true,
|
||||
"main": "client.js",
|
||||
"scripts": {
|
||||
"start": "NODE_ENV=development node server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"bankai": "^2.0.2",
|
||||
"browserify": "^13.0.1",
|
||||
"server-router": "^2.1.0",
|
||||
"sse": "0.0.6"
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
const serverRouter = require('server-router')
|
||||
const browserify = require('browserify')
|
||||
const bankai = require('bankai')
|
||||
const http = require('http')
|
||||
const SSE = require('sse')
|
||||
|
||||
const clients = []
|
||||
const PORT = 8080
|
||||
|
||||
const server = http.createServer(createRouter())
|
||||
server.listen(PORT, () => {
|
||||
process.stdout.write(`listening on port ${PORT}\n`)
|
||||
var sse = new SSE(server)
|
||||
|
||||
// code adapted from: https://github.com/markbrown4/server-sent-events-demo
|
||||
sse.on('connection', (stream) => {
|
||||
clients.push(stream)
|
||||
console.log('Opened connection 🎉')
|
||||
|
||||
var json = JSON.stringify({ message: 'Gotcha' })
|
||||
stream.send(json)
|
||||
console.log('Sent: ' + json)
|
||||
|
||||
stream.on('close', () => {
|
||||
clients.splice(clients.indexOf(stream), 1)
|
||||
console.log('Closed connection 😱')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// emit data every 1.5 seconds
|
||||
setInterval(() => {
|
||||
var json = JSON.stringify({ message: 'Hello hello!' })
|
||||
clients.forEach((stream) => {
|
||||
stream.send(json)
|
||||
console.log('Sent: ' + json)
|
||||
})
|
||||
}, 1500)
|
||||
|
||||
function createRouter () {
|
||||
const router = serverRouter('/404')
|
||||
|
||||
const html = bankai.html({ css: false })
|
||||
router.on('/', (req, res) => html(req, res).pipe(res))
|
||||
|
||||
const js = bankai.js(browserify, require.resolve('./client.js'))
|
||||
router.on('/bundle.js', (req, res) => js(req, res).pipe(res))
|
||||
|
||||
router.on('/404', (req, res) => {
|
||||
res.statusCode = 404
|
||||
res.end('{ "message": "the server is confused" }')
|
||||
})
|
||||
|
||||
return router
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
const html = require('../../../html')
|
||||
|
||||
module.exports = function (params, state, send) {
|
||||
const error = state.app.error[0]
|
||||
const title = state.api.title
|
||||
return html`
|
||||
<section>
|
||||
<h1>${title}</h1>
|
||||
<h2>Latest error: ${error}</h2>
|
||||
<button onclick=${(e) => send('api:good')}>OK!</button>
|
||||
<button onclick=${(e) => send('api:bad')}>Naughty</button>
|
||||
</section>
|
||||
`
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
const choo = require('../../')
|
||||
|
||||
const stopwatch = require('./models/stopwatch')
|
||||
const mainView = require('./views/main')
|
||||
|
||||
const app = choo()
|
||||
|
||||
app.model(stopwatch)
|
||||
|
||||
app.router(['/', mainView])
|
||||
|
||||
const tree = app.start()
|
||||
document.body.appendChild(tree)
|
||||
@@ -1,33 +0,0 @@
|
||||
const raf = require('raf')
|
||||
|
||||
module.exports = {
|
||||
state: {
|
||||
elapsed: 0,
|
||||
startTime: 0,
|
||||
start: false,
|
||||
laps: []
|
||||
},
|
||||
reducers: {
|
||||
start: (state, data) => ({ start: true, startTime: Date.now() - state.elapsed }),
|
||||
stop: (state, data) => ({ start: false }),
|
||||
update: (state, data) => ({ elapsed: data }),
|
||||
reset: (state, data) => ({ startTime: Date.now(), elapsed: 0, laps: [] }),
|
||||
add: (state, data) => ({ laps: state.laps.concat(data) })
|
||||
},
|
||||
effects: {
|
||||
now: (state, data, send, done) => {
|
||||
if (state.start) {
|
||||
let elapsed = data - state.startTime
|
||||
send('update', elapsed, done)
|
||||
}
|
||||
}
|
||||
},
|
||||
subscriptions: [
|
||||
(send, done) => {
|
||||
raf(function loop () {
|
||||
send('now', Date.now(), done)
|
||||
raf(loop)
|
||||
})
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "stopwatch",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "client.js",
|
||||
"scripts": {
|
||||
"start": "bankai start --entry=client.js -p 8080"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "traducer <traducer21@gmail.com>",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bankai": "^3.2.0",
|
||||
"raf": "^3.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"es2020": "^1.1.7"
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
function formatMinutes (minutes) {
|
||||
return `${minutes >= 10 ? minutes : (minutes < 10) ? '0' + minutes : '00'}`
|
||||
}
|
||||
|
||||
function formatSeconds (seconds) {
|
||||
return `${seconds < 10 ? '0' + seconds : seconds}`
|
||||
}
|
||||
|
||||
module.exports = function format (elapsed) {
|
||||
const minutes = formatMinutes(Math.floor((elapsed / 1000) / 60))
|
||||
const seconds = formatSeconds(Math.floor((elapsed / 1000) % 60))
|
||||
const ms = formatSeconds(Math.floor((elapsed % 1000) / 10))
|
||||
|
||||
return `${minutes}:${seconds}.${ms}`
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
const html = require('../../../html')
|
||||
const format = require('../utilities/format')
|
||||
|
||||
function toggle (state, send) {
|
||||
if (state.start) {
|
||||
send('stop')
|
||||
} else {
|
||||
send('start')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = (state, prev, send) => {
|
||||
const start = state.start
|
||||
const elapsed = state.elapsed
|
||||
|
||||
return html`
|
||||
<main class="app">
|
||||
<h1>stopwatch</h1>
|
||||
<p>${format(elapsed)}</p>
|
||||
<button
|
||||
onclick=${e => toggle(state, send)}
|
||||
>${start ? 'stop' : 'start'}</button>
|
||||
<button
|
||||
style="display:${start ? 'none' : 'inline'};"
|
||||
onclick=${e => send('reset')}
|
||||
>reset</button>
|
||||
<button
|
||||
style="display:${start ? 'inline' : 'none'};"
|
||||
onclick=${e => send('add', format(elapsed))}
|
||||
>lap</button>
|
||||
<ol>
|
||||
${state.laps.map(lap => html`<li>${lap}</li>`)}
|
||||
</ol>
|
||||
</main>
|
||||
`
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
const choo = require('../../')
|
||||
const html = require('../../html')
|
||||
|
||||
const app = choo()
|
||||
app.model({
|
||||
namespace: 'input',
|
||||
state: {
|
||||
title: 'my demo app'
|
||||
},
|
||||
reducers: {
|
||||
update: (state, data) => ({ title: data.payload })
|
||||
},
|
||||
effects: {
|
||||
update: (state, data, send, done) => {
|
||||
document.title = data.payload
|
||||
done()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const mainView = (state, prev, send) => {
|
||||
return html`
|
||||
<main class="app">
|
||||
<h1>${state.input.title}</h1>
|
||||
<label>Set the title</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder=${state.input.title}
|
||||
oninput=${(e) => send('input:update', { payload: e.target.value })}>
|
||||
</main>
|
||||
`
|
||||
}
|
||||
|
||||
app.router(['/', mainView])
|
||||
const tree = app.start()
|
||||
document.body.appendChild(tree)
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "title",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "client.js",
|
||||
"scripts": {
|
||||
"start": "bankai start --entry=client.js -p 8080 --open"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Yoshua Wuyts <i@yoshuawuyts.com>",
|
||||
"license": "ISC",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"bankai": "^3.2.0"
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Vanilla example</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="../../dist/choo.min.js"></script>
|
||||
<script src="../../dist/html.min.js"></script>
|
||||
<script>
|
||||
const app = choo()
|
||||
|
||||
app.model({
|
||||
namespace: 'counter',
|
||||
state: { count: 0 },
|
||||
reducers: {
|
||||
increment: (state, data) => ({count: state.count + 1}),
|
||||
decrement: (state, data) => ({count: state.count - 1})
|
||||
}
|
||||
})
|
||||
|
||||
const mainView = (state, prev, send) => {
|
||||
return html`
|
||||
<main class="app">
|
||||
<h1>Counter example</h1>
|
||||
<div>
|
||||
Count: ${state.counter.count}
|
||||
<button onclick=${(e) => send('counter:increment')}>+</button>
|
||||
<button onclick=${(e) => send('counter:decrement')}>-</button>
|
||||
</div>
|
||||
</main>
|
||||
`
|
||||
}
|
||||
|
||||
app.router(['/', mainView])
|
||||
const tree = app.start()
|
||||
document.body.appendChild(tree)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user