cuppa

0.1.3 • Public • Published

cuppa

A fun little frontend application framework. A wee cuppa: only 2.6kb gzipped.

Install

npm i cuppa --save

Simple setup

Initialize an instance with routes:

const cuppa = require('cuppa')
const h = require('cuppa/html')
 
const app = cuppa([
  ['/', () => {
    return h`<h1>Hello world</h1>`
  }]
])

Mount instance to DOM with initial state object:

app(document.getElementById('root'), {
  count: 0
}, () => {
  console.info('cuppa initialized ☕️')
})

Enjoy while it's hot!

Full usage

Below is a functioning front-end app with async routing.

// App.js
const h = require('cuppa/html')
 
module.exports = function App (children) {
  return h`
    <div class='app'>
      <nav>
        <ul>
          <li><a href='/'>Home</a></li>
          <li><a href='/about'>About</a></li>
        </ul>
      </nav>
 
      ${children}
    </div>
  `
}
// Page.js
const h = require('cuppa/html')
 
module.exports = function Page (data) {
  return h`
    <h1>You are on page: ${data.title}</h1>
  `
}
// index.js
const cuppa = require('cuppa')
const Page = require('./Page.js')
 
const app = cuppa([
  ['/', function Home () {
    return Promise.resolve({ title: 'Home' })
      .then(Page)
  }]
  ['/about', function About () {
    return Promise.resolve({ title: 'About' })
      .then(Page)
  }]
])(document.getElementById('root'), {
  count: 0
})

The cuppa instance emits an event when a link is clicked:

app.on('navigate', pathname => {})

And right after rendering has occurred:

app.on('render', markup => {})

State management

Initial state was set when you called cuppa(routes, initialState) in index.js. cuppa/state then exports a higher-order function connect that you can use to read and update state.

// app/Counter.js
const h = require('cuppa/html')
const { connect } = require('cuppa/state')
 
module.exports = connect(state => ({
  count: state.count
}))(function Counter(props, state) {
  return h`
    <div>
      <h1>The count is ${state.count}</h1>
      <button onclick=${e => state.update(state => ({
        count: state.count - 1
      }))}>Down</button>
      <button onclick=${e => state.update(state => ({
        count: state.count + 1
      }))}>Up</button>
    </div>
  `
})

Since state is global, it can be controlled via any component

// app/ExternalTrigger.js
const h = require('cuppa/html')
const { connect } = require('./store.js')
 
module.exports = connect()(function ExternalTrigger (props, state) {
  return h`
    <div>
      In this component, state.count is set to 12.
 
      <button onclick=${e => state.update(state => ({ count: 12 }))}
    </div>
  `
})

Server side rendering

Server-side is simple, just don't mount the application to the DOM

// app/index.js
const cuppa = require('cuppa')
const Page = require('./Page.js')
 
const app = cuppa([
  ['/', function Home () {
    return Promise.resolve({ title: 'Home' })
      .then(Page)
  }]
  ['/about', function About () {
    return Promise.resolve({ title: 'About' })
      .then(Page)
  }]
])
// server/template.js
module.exports = function Template (markup, state) {
  return `
    <!doctype html>
    <html>
      <head>
      </head>
      <body>
        ${markup}
 
        <script>window.initialState = ${JSON.stringify(state)}</script>
      </body>
    </html>
  `
}

On the server, the cuppa instance accepts a route path and initial state object. Use this to send data from your server to the rendered markup.

// server/index.js
const app = require('express')()
const template = require()
const program = require('../app/index.js')
const template = require('./template.js')
 
app.get('*', (req, res) => {
  const state = { count: 0 }
  program(req.originalUrl, state)
    .then(markup => {
      res.send(template(markup, state))
    })
})
 
app.listen(8080)

Then on the client, mount to the DOM and hydrate state.

const app = require('express')()
 
const state = Object.assign({ count: 0 }, window.initialState)
 
app(document.getElementById('root'), state)

Extending the template parser

The h export from cuppa/html can be modified by the other export of that file: the addTransform method. Think of this like a higher-order function that can extend the template parser.

const { addTransform } = require('cuppa/html')
const cxs = require('cxs')
 
addTransform(props => {
  if (props.css && typeof props.css === 'object') {
    props.className = [props.className || '', cxs(props.css)].join(' ').replace(/^\s/, '')
  }
 
  return props
})

With the above, you can now use a css attribute on your components and it will be translated to classes in the browser:

const h = require('cuppa/html')
 
module.exports = function Component (props) {
  return h`
    <div css=${{
      display: 'block',
      position: 'relative',
      color: ${props.color}
    }}></div>
  `
}

MIT License

Dependents (0)

Package Sidebar

Install

npm i cuppa

Weekly Downloads

1

Version

0.1.3

License

MIT

Last publish

Collaborators

  • estrattonbailey