Skip to content

Commit

Permalink
Hyperapp V2 support (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
frenzzy authored Nov 15, 2018
1 parent c8d67bb commit b5dcc21
Show file tree
Hide file tree
Showing 31 changed files with 636 additions and 669 deletions.
21 changes: 16 additions & 5 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
{
"presets": [
["@babel/preset-env", {
"targets": {
"node": "current"
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
}]
]
],
"plugins": [
"@babel/plugin-transform-react-jsx"
[
"@babel/plugin-transform-react-jsx",
{
"pragma": "h",
"pragmaFrag": "Fragment",
"useBuiltIns": true,
"throwIfNamespace": false
}
]
]
}
3 changes: 0 additions & 3 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@ root = true

[*]

# Change these settings to your own preference
indent_style = space
indent_size = 2

# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
Expand Down
14 changes: 9 additions & 5 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
{
"extends": [
"airbnb",
"eslint-config-prettier",
"eslint-config-prettier/react"
],
"extends": ["airbnb", "eslint-config-prettier", "eslint-config-prettier/react"],
"rules": {
"import/prefer-default-export": "off",
"no-continue": "off",
"no-plusplus": "off",
"no-restricted-syntax": "off",
"prefer-template": "off"
},
"settings": {
"react": {
"pragma": "h"
}
}
}
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@
.* text eol=lf
*.js text eol=lf
*.json text eol=lf
*.map text eol=lf
*.md text eol=lf
*.svg text eol=lf
*.ts text eol=lf
*.tsx text eol=lf
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased][unreleased]

- [BREAKING] Remove higher-order app `withRender` from the library due to redundancy.
- Support for `className` attribute and allow to use array and object as a value.
- Compatibility with upcoming [Hyperapp V2](https://github.com/hyperapp/hyperapp/pull/726).
- Various performance optimizations.

## [2.1.0] - 2018-07-11

- Add [TypeScript](https://www.typescriptlang.org/) typings.
Expand Down
127 changes: 36 additions & 91 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
[![library size](https://img.shields.io/bundlephobia/minzip/hyperapp-render.svg)](https://bundlephobia.com/result?p=hyperapp-render)
[![slack chat](https://hyperappjs.herokuapp.com/badge.svg)](https://hyperappjs.herokuapp.com 'Join us')

A [Hyperapp](https://github.com/hyperapp/hyperapp) higher-order `app`
that allows you to render views to an HTML string.
This library is allowing you to render
[Hyperapp](https://github.com/hyperapp/hyperapp) views to an HTML string.

- **User experience** — Generate HTML on the server and send the markup
down on the initial request for faster page loads. Built-in
[mounting](https://github.com/hyperapp/hyperapp/tree/1.2.0#mounting)
[mounting](https://github.com/hyperapp/hyperapp/tree/1.2.9#mounting)
feature in Hyperapp is allowing you to have a very performant first-load experience.
- **Accessibility** — Allow search engines to crawl your pages for
[SEO](https://en.wikipedia.org/wiki/Search_engine_optimization) purposes.
- **Testability**[Check HTML validity](https://en.wikipedia.org/wiki/Validator) and use
[snapshot testing](https://facebook.github.io/jest/docs/en/snapshot-testing.html)
[snapshot testing](https://jestjs.io/docs/en/snapshot-testing.html)
to improve quality of your software.

## Getting Started
Expand All @@ -24,8 +24,8 @@ Our first example is an interactive app from which you can generate an HTML mark
Go ahead and [try it online](https://codepen.io/frenzzy/pen/zpmRQY/left/?editors=0010).

```jsx
import { h, app } from 'hyperapp'
import { withRender } from 'hyperapp-render'
import { h } from 'hyperapp'
import { renderToString } from 'hyperapp-render'

const state = {
text: 'Hello'
Expand All @@ -42,14 +42,13 @@ const view = (state, actions) => (
</main>
)

const main = withRender(app)(state, actions, view)
const html = renderToString(view(state, actions))

main.toString() // => <main><h1>Hello</h1><input value="Hello"/></main>
main.setText('World') // <= any sync or async action call
main.toString() // => <main><h1>World</h1><input value="World"/></main>
console.log(html) // => <main><h1>Hello</h1><input value="Hello"/></main>
```

Looking for a boilerplate? Try [Hyperapp Starter](https://github.com/frenzzy/hyperapp-starter)
Looking for a boilerplate?
Try [Hyperapp Starter](https://github.com/kriasoft/hyperapp-starter)
with pre-configured server-side rendering and many more.

## Installation
Expand All @@ -74,110 +73,56 @@ You can find the library in `window.hyperappRender`.
We support all ES5-compliant browsers, including Internet Explorer 9 and above,
but depending on your target browsers you may need to include
[polyfills](<https://en.wikipedia.org/wiki/Polyfill_(programming)>) for
[`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set),
[`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) and
[`Object.assign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
[`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) and
[`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)
before any other code.

## Usage

The library provides a few functions which you can use depending on your needs or personal preferences.
The library provides two functions
which you can use depending on your needs or personal preferences:

```jsx
import { withRender, renderToString, renderToStream } from 'hyperapp-render'

const main = withRender(app)(state, actions, view, container)
import { renderToString, renderToStream } from 'hyperapp-render'

main.toString() // => <string>
renderToString(<Component />) // => <string>
renderToString(view(state, actions)) // => <string>
renderToString(view, state, actions) // => <string>

main.toStream() // => <stream.Readable> => <string>
renderToStream(<Component />) // => <stream.Readable> => <string>
renderToStream(view(state, actions)) // => <stream.Readable> => <string>
renderToStream(view, state, actions) // => <stream.Readable> => <string>
```

**Note:** functions `toStream` and `renderToStream` are available in
**Note:** `renderToStream` is available from
[Node.js](https://nodejs.org/en/) environment only (v6 or newer).

## Overview

The library exposes three functions. The first of these is `withRender` high-order function,
which adds the `toString` action to be able to render your application to an HTML string at any given time.
This can be useful for server-side rendering or creating HTML snippets based on current application state.

```jsx
import { h, app } from 'hyperapp'
import { withRender } from 'hyperapp-render'

const state = { name: 'World' }
const actions = { setName: name => ({ name }) }
const view = (state, actions) => <h1>Hello {state.name}</h1>

const main = withRender(app)(state, actions, view)
You can use `renderToString` function to generate HTML on the server
and send the markup down on the initial request for faster page loads
and to allow search engines to crawl your pages for
[SEO](https://en.wikipedia.org/wiki/Search_engine_optimization) purposes.

main.toString() // => <h1>Hello World</h1>
main.setName('Hyperapp') // <= any sync or async action call
main.toString() // => <h1>Hello Hyperapp</h1>
```

The second `renderToString` function generates HTML markup from any of your views without
app initialization. That could be useful to generate HTML markup from static views.

```jsx
import { renderToString } from 'hyperapp-render'
If you call [`hyperapp.app()`](https://github.com/hyperapp/hyperapp/tree/1.2.9#mounting)
on a node that already has this server-rendered markup,
Hyperapp will preserve it and only attach event handlers, allowing you
to have a very performant first-load experience.

const Component = ({ name }) => <h1>Hello {name}</h1>

renderToString(<Component name="World" />)
// => <h1>Hello World</h1>
```

The last `renderToStream` function and `toStream` equivalent return a
[Readable stream](https://nodejs.org/api/stream.html#stream_readable_streams) that outputs an HTML string.
The HTML output by this stream is exactly equal to what `toString` or `renderToString` would return.
They are designed for more performant server-side rendering and here are examples how they could be used
with [Express](http://expressjs.com/) or [Koa](http://koajs.com/):

```jsx
app.get('/', (req, res) => {
res.write('<!doctype html><html><head>')
res.write('<title>Page</title>')
res.write('</head><body><div id="app">')
const main = withRender(app)(state, actions, view)
const stream = main.toStream()
stream.pipe(res, { end: false })
stream.on('end', () => {
res.write('</div></body></html>')
res.end()
})
})
```

```jsx
app.get('/', (req, res) => {
res.write('<!doctype html>')
const stream = renderToStream(
<html>
<head><title>Page</title></head>
<body>
<div id="app">{view(state, actions)}</div>
</body>
</html>
)
stream.pipe(res)
})
```
The `renderToStream` function returns a
[Readable stream](https://nodejs.org/api/stream.html#stream_readable_streams)
that outputs an HTML string.
The HTML output by this stream is exactly equal to what `renderToString` would return.
By using this function you can reduce [TTFB](https://en.wikipedia.org/wiki/Time_to_first_byte)
and improve user experience even more.

## Caveats

The library automatically escapes text content and attribute values
of [virtual DOM nodes](https://github.com/hyperapp/hyperapp/tree/1.2.0#view)
to protect your application against [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) attacks.

However, it is not safe to allow "user input" for node names or attribute keys because
the library does not reject injection attack on markup due to performance reasons.
See:
of [virtual DOM nodes](https://github.com/hyperapp/hyperapp/tree/1.2.9#view)
to protect your application against
[XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) attacks.
However, it is not safe to allow "user input" for node names or attribute keys:

```jsx
const Node = 'div onclick="alert()"'
Expand Down
Loading

0 comments on commit b5dcc21

Please sign in to comment.