├── .gitignore ├── .travis.yml ├── README.md ├── index.js ├── package.json └── tests ├── hooks.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .nyc_output/ 3 | coverage/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - 0.10 5 | - 0.12 6 | - 4.2 7 | before_install: 8 | - npm install -g npm@latest-2 9 | after_success: 10 | - npm run coverage 11 | - 'curl -Lo travis_after_all.py https://git.io/travis_after_all' 12 | - python travis_after_all.py 13 | - 'export $(cat .to_export_back) &> /dev/null' 14 | - npm run semantic-release 15 | branches: 16 | except: 17 | - "/^v\\d+\\.\\d+\\.\\d+$/" 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # inferno-hyperscript [![Travis-CI Status](https://img.shields.io/travis/terinjokes/inferno-hyperscript/master.svg?label=Travis%20CI&style=flat-square)](https://travis-ci.org/terinjokes/inferno-hyperscript) [![](https://img.shields.io/npm/dm/inferno-hyperscript.svg?style=flat-square)](https://www.npmjs.org/package/inferno-hyperscript) [![](https://img.shields.io/npm/v/inferno-hyperscript.svg?style=flat-square)](https://www.npmjs.org/package/inferno-hyperscript) [![](https://img.shields.io/coveralls/terinjokes/inferno-hyperscript/master.svg?style=flat-square)](https://coveralls.io/github/terinjokes/inferno-hyperscript) 2 | > [Hyperscript][hyperscript] syntax for [Inferno][inferno] termplates. 3 | 4 | ## Usage 5 | 6 | ```javascript 7 | var h = require('inferno-hyperscript'); 8 | 9 | module.exports = function ExampleComponent(props) { 10 | return h('.example', [ 11 | h('a.example-link', { 12 | href: '#' 13 | }, [ 14 | 'Hello', 15 | props.whom, 16 | '!' 17 | ]) 18 | ]); 19 | }; 20 | ``` 21 | 22 | ## Documentation 23 | 24 | ### `h(componentOrTag, properties, children)` 25 | 26 | Returns an Inferno VNode from a Hyperscript representation. 27 | 28 | * **componentOrTag** `(Object|String)` can be an Inferno component **OR** tag string with optional css class names and ids in the format `h1#some-id.foo.bar`. 29 | If a tag string, the tag name is parsed out, and the `id` and `className` propertires of the properties argument will be modified. 30 | * **properties** `(Object)` *(optional)* An object containing the properties you'd like to set on the element. 31 | * **children** `(Array|String)` *(optional)* An array of `h()` children or strings, This will create childen or text nodes respectively. 32 | 33 | [hyperscript]: https://github.com/dominictarr/hyperscript 34 | [inferno]: https://github.com/trueadm/inferno 35 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // fork of react-hyperscript 3 | var parseTag = require('virtual-dom/virtual-hyperscript/parse-tag'); 4 | var Inferno = require('inferno'); 5 | var transform = require('lodash/fp/transform'); 6 | var assign = require('lodash/fp/assign'); 7 | var includes = require('lodash/fp/includes'); 8 | var __ = require('lodash/fp/placeholder'); 9 | var isFunction = require('lodash/isFunction'); 10 | 11 | var hooks = [ 12 | 'onCreated', 13 | 'onAttached', 14 | 'onWillDetach', 15 | 'onWillUpdate', 16 | 'onDidUpdate' 17 | ]; 18 | var componentHooks = [ 19 | 'onComponentWillMount', 20 | 'onComponentDidMount', 21 | 'onComponentWillUnmount', 22 | 'onComponentShouldUpdate', 23 | 'onComponentWillUpdate', 24 | 'onComponentDidUpdate' 25 | ]; 26 | 27 | var isHook = includes(__, hooks); 28 | var isComponentHook = includes(__, componentHooks); 29 | var createVNode = assign(Inferno.createVNode()); 30 | 31 | module.exports = function (tag, properties, children) { 32 | // If a child array or text node are passed as the second argument, shift them 33 | if (!children && isChildren(properties)) { 34 | children = properties; 35 | properties = {}; 36 | } 37 | 38 | properties = properties || {}; 39 | children = typeof children === 'undefined' ? null : children; 40 | 41 | // When a selector, parse the tag name and fill out the properties object 42 | if (typeof tag === 'string') { 43 | tag = parseTag(tag, properties); 44 | } 45 | 46 | var extracted = extractFromProps(properties, tag, children); 47 | 48 | return createVNode(extracted); 49 | }; 50 | 51 | function getEventFromHook(hook) { 52 | return hook.charAt(2).toLowerCase() + hook.substring(3); 53 | } 54 | 55 | function extractFromProps(props, tag, children) { 56 | return transform.convert({cap: false})(function (acc, v, k) { 57 | if (k === 'className') { 58 | acc.className = v; 59 | } else if (k === 'style') { 60 | acc.style = v; 61 | } else if (k === 'key') { 62 | acc.key = v; 63 | } else if (isHook(k) && !isFunction(tag)) { 64 | if (!acc.hooks) { 65 | acc.hooks = {}; 66 | } 67 | acc.hooks[getEventFromHook(k)] = v; 68 | } else if (isEvent(k) && !isFunction(tag)) { 69 | if (!acc.events) { 70 | acc.events = {}; 71 | } 72 | 73 | acc.events[k.toLowerCase()] = v; 74 | } else if (isComponentHook(k) && isFunction(tag)) { 75 | if (!acc.hooks) { 76 | acc.hooks = {}; 77 | } 78 | 79 | acc.hooks['c' + k.substring(3)] = v; 80 | } else { 81 | if (!acc.attrs) { 82 | acc.attrs = {}; 83 | } 84 | 85 | acc.attrs[k] = v; 86 | } 87 | }, { 88 | tag: tag, 89 | children: children 90 | })(props); 91 | } 92 | 93 | function isEvent(attr) { 94 | return attr[0] === 'o' && attr[1] === 'n' && attr.length > 3; 95 | } 96 | 97 | function isChildren(x) { 98 | return typeof x === 'string' || typeof x === 'number' || (x && x.constructor === Array); 99 | } 100 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inferno-hyperscript", 3 | "description": "Bridging hyperscript to InfernoJS", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "xo && nyc --reporter=lcov -- ava", 7 | "coverage": "cat ./coverage/lcov.info | coveralls", 8 | "semantic-release": "semantic-release pre && npm publish && semantic-release post" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/terinjokes/inferno-hyperscript.git" 13 | }, 14 | "keywords": [ 15 | "inferno", 16 | "hyperscript" 17 | ], 18 | "author": "Terin Stock (https://terinstock.com/)", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/terinjokes/inferno-hyperscript/issues" 22 | }, 23 | "files": [ 24 | "index.js", 25 | "tests" 26 | ], 27 | "homepage": "https://github.com/terinjokes/inferno-hyperscript#readme", 28 | "dependencies": { 29 | "inferno": "^0.7.3", 30 | "lodash": "^4.11.1", 31 | "virtual-dom": "^2.1.1" 32 | }, 33 | "devDependencies": { 34 | "ava": "^0.16.0", 35 | "coveralls": "^2.11.6", 36 | "inferno-server": "^0.7.3", 37 | "nyc": "^8.0.0", 38 | "semantic-release": "^4.3.5", 39 | "xo": "^0.16.0" 40 | }, 41 | "xo": { 42 | "space": true 43 | }, 44 | "nyc": { 45 | "exclude": [ 46 | "tests" 47 | ] 48 | }, 49 | "ava": { 50 | "files": [ 51 | "tests/*.js" 52 | ] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/hooks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const h = require('..'); 3 | const Inferno = require('inferno'); 4 | const InfernoServer = require('inferno-server'); 5 | const assign = require('lodash/fp/assign'); 6 | const test = require('ava'); 7 | 8 | const createVNode = assign(Inferno.createVNode()); 9 | 10 | test('a tag with a hook', t => { 11 | function onCreated() { 12 | 13 | } 14 | 15 | let hnode = h('h1', { 16 | onCreated: onCreated 17 | }, 'Hello World!'); 18 | t.deepEqual(hnode, createVNode({ 19 | tag: 'H1', 20 | hooks: { 21 | created: onCreated 22 | }, 23 | children: 'Hello World!' 24 | })); 25 | 26 | let domString = InfernoServer.renderToString(hnode); 27 | 28 | t.true(typeof domString === 'string'); 29 | t.true(domString === '

Hello World!

'); 30 | }); 31 | 32 | test('a tag with a hook (2)', t => { 33 | function onDidUpdate() { 34 | 35 | } 36 | 37 | let hnode = h('h1', { 38 | onDidUpdate: onDidUpdate 39 | }, 'Hello World!'); 40 | t.deepEqual(hnode, createVNode({ 41 | tag: 'H1', 42 | hooks: { 43 | didUpdate: onDidUpdate 44 | }, 45 | children: 'Hello World!' 46 | })); 47 | 48 | let domString = InfernoServer.renderToString(hnode); 49 | 50 | t.true(typeof domString === 'string'); 51 | t.true(domString === '

Hello World!

'); 52 | }); 53 | -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const h = require('..'); 3 | const Inferno = require('inferno'); 4 | const InfernoServer = require('inferno-server'); 5 | const assign = require('lodash/fp/assign'); 6 | const test = require('ava'); 7 | 8 | const createVNode = assign(Inferno.createVNode()); 9 | 10 | test('an html tag', t => { 11 | let hnode = h('h1'); 12 | t.deepEqual(hnode, createVNode({ 13 | tag: 'H1' 14 | })); 15 | 16 | let domString = InfernoServer.renderToString(hnode); 17 | 18 | t.true(typeof domString === 'string'); 19 | t.true(domString === '

'); 20 | }); 21 | 22 | test('a tag with an id and classes in the selector', t => { 23 | let hnode = h('h1#boom.whatever.foo'); 24 | t.deepEqual(hnode, createVNode({ 25 | tag: 'H1', 26 | attrs: { 27 | id: 'boom' 28 | }, 29 | className: 'whatever foo' 30 | })); 31 | 32 | let domString = InfernoServer.renderToString(hnode); 33 | 34 | t.true(typeof domString === 'string'); 35 | t.true(domString === '

'); 36 | }); 37 | 38 | test('a tag with classes in the selector and props', t => { 39 | let hnode = h('h1.foo', { 40 | className: 'bar' 41 | }); 42 | t.deepEqual(hnode, createVNode({ 43 | tag: 'H1', 44 | className: 'foo bar' 45 | })); 46 | 47 | let domString = InfernoServer.renderToString(hnode); 48 | 49 | t.true(typeof domString === 'string'); 50 | t.true(domString === '

'); 51 | }); 52 | 53 | test('a tag with other properties', t => { 54 | let hnode = h('a', { 55 | href: 'http://www.google.com' 56 | }); 57 | t.deepEqual(hnode, createVNode({ 58 | tag: 'A', 59 | attrs: { 60 | href: 'http://www.google.com' 61 | } 62 | })); 63 | 64 | let domString = InfernoServer.renderToString(hnode); 65 | 66 | t.true(typeof domString === 'string'); 67 | t.true(domString === ''); 68 | }); 69 | 70 | test('a tag with a string as the third argument', t => { 71 | let hnode = h('h1', null, 'Hello World!'); 72 | t.deepEqual(hnode, createVNode({ 73 | tag: 'H1', 74 | children: 'Hello World!' 75 | })); 76 | 77 | let domString = InfernoServer.renderToString(hnode); 78 | 79 | t.true(typeof domString === 'string'); 80 | t.true(domString === '

Hello World!

'); 81 | }); 82 | 83 | test('a tag with a string as the second argument', t => { 84 | let hnode = h('h1', 'Hello World!'); 85 | t.deepEqual(hnode, createVNode({ 86 | tag: 'H1', 87 | children: 'Hello World!' 88 | })); 89 | 90 | let domString = InfernoServer.renderToString(hnode); 91 | 92 | t.true(typeof domString === 'string'); 93 | t.true(domString === '

Hello World!

'); 94 | }); 95 | 96 | test('a tag with a number as the second argument', t => { 97 | let hnode = h('h1', 5); 98 | t.deepEqual(hnode, createVNode({ 99 | tag: 'H1', 100 | children: 5 101 | })); 102 | 103 | let domString = InfernoServer.renderToString(hnode); 104 | 105 | t.true(typeof domString === 'string'); 106 | t.true(domString === '

5

'); 107 | }); 108 | 109 | test('a tag with a number as the third argument', t => { 110 | let hnode = h('h1', null, 5); 111 | t.deepEqual(hnode, createVNode({ 112 | tag: 'H1', 113 | children: 5 114 | })); 115 | 116 | let domString = InfernoServer.renderToString(hnode); 117 | 118 | t.true(typeof domString === 'string'); 119 | t.true(domString === '

5

'); 120 | }); 121 | 122 | test('a tag with a `0` as the second argument', t => { 123 | let hnode = h('h1', 0); 124 | t.deepEqual(hnode, createVNode({ 125 | tag: 'H1', 126 | children: 0 127 | })); 128 | 129 | let domString = InfernoServer.renderToString(hnode); 130 | 131 | t.true(typeof domString === 'string'); 132 | t.true(domString === '

0

'); 133 | }); 134 | 135 | test('a tag with a children array as the third argument', t => { 136 | let hnode = h('h1', null, [ 137 | h('span'), 138 | h('span') 139 | ]); 140 | t.deepEqual(hnode, createVNode({ 141 | tag: 'H1', 142 | children: [ 143 | createVNode({ 144 | tag: 'SPAN' 145 | }), 146 | createVNode({ 147 | tag: 'SPAN' 148 | }) 149 | ] 150 | })); 151 | 152 | let domString = InfernoServer.renderToString(hnode); 153 | 154 | t.true(typeof domString === 'string'); 155 | t.true(domString === '

'); 156 | }); 157 | 158 | test('a tag with a children array as the second argument', t => { 159 | let hnode = h('h1', [ 160 | h('span'), 161 | h('span') 162 | ]); 163 | t.deepEqual(hnode, createVNode({ 164 | tag: 'H1', 165 | children: [ 166 | createVNode({ 167 | tag: 'SPAN' 168 | }), 169 | createVNode({ 170 | tag: 'SPAN' 171 | }) 172 | ] 173 | })); 174 | 175 | let domString = InfernoServer.renderToString(hnode); 176 | 177 | t.true(typeof domString === 'string'); 178 | t.true(domString === '

'); 179 | }); 180 | 181 | test('a tag with key', t => { 182 | let hnode = h('h1', { 183 | key: 'foobar' 184 | }, 'Hello World!'); 185 | t.deepEqual(hnode, createVNode({ 186 | tag: 'H1', 187 | key: 'foobar', 188 | children: 'Hello World!' 189 | })); 190 | 191 | let domString = InfernoServer.renderToString(hnode); 192 | 193 | t.true(typeof domString === 'string'); 194 | t.true(domString === '

Hello World!

'); 195 | }); 196 | 197 | test('a tag with an event', t => { 198 | function onHover() { 199 | 200 | } 201 | 202 | let hnode = h('h1', { 203 | onHover: onHover 204 | }, 'Hello World!'); 205 | t.deepEqual(hnode, createVNode({ 206 | tag: 'H1', 207 | events: { 208 | onhover: onHover 209 | }, 210 | children: 'Hello World!' 211 | })); 212 | 213 | let domString = InfernoServer.renderToString(hnode); 214 | 215 | t.true(typeof domString === 'string'); 216 | t.true(domString === '

Hello World!

'); 217 | }); 218 | 219 | test('a tag with style', t => { 220 | let hnode = h('h1', { 221 | style: { 222 | color: '#FFF' 223 | } 224 | }, 'Hello World!'); 225 | t.deepEqual(hnode, createVNode({ 226 | tag: 'H1', 227 | style: { 228 | color: '#FFF' 229 | }, 230 | children: 'Hello World!' 231 | })); 232 | 233 | let domString = InfernoServer.renderToString(hnode); 234 | 235 | t.true(typeof domString === 'string'); 236 | t.true(domString === '

Hello World!

'); 237 | }); 238 | 239 | test('a basic Component', t => { 240 | function Component(place) { 241 | return h('div.example', `Hello ${place}!`); 242 | } 243 | 244 | function DidUpdate() { 245 | 246 | } 247 | 248 | let hnode = h(Component, { 249 | place: 'world', 250 | onComponentDidUpdate: DidUpdate 251 | }); 252 | t.deepEqual(hnode, createVNode({ 253 | tag: Component, 254 | attrs: { 255 | place: 'world' 256 | }, 257 | hooks: { 258 | componentDidUpdate: DidUpdate 259 | } 260 | })); 261 | }); 262 | --------------------------------------------------------------------------------