Socket
Socket
Sign inDemoInstall

mithril-node-render

Package Overview
Dependencies
Maintainers
1
Versions
43
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mithril-node-render - npm Package Compare versions

Comparing version 2.3.2 to 3.0.0

.eslintignore

1

.vscode/settings.json

@@ -5,2 +5,3 @@ {

"eslint.run": "onSave",
"javascript.format.enable": false,
}

400

index.js
'use strict'
const m = require('mithril/hyperscript')
const Vnode = require('mithril/render/vnode')
const VOID_TAGS = [
'area',
'base',
'br',
'col',
'command',
'embed',
'hr',
'img',
'input',
'keygen',
'link',
'meta',
'param',
'source',
'track',
'wbr',
'!doctype'
]
const VOID_TAGS = new RegExp('^(?:' +
'area|' +
'base|' +
'br|' +
'col|' +
'command|' +
'embed|' +
'hr|' +
'img|' +
'input|' +
'keygen|' +
'link|' +
'meta|' +
'param|' +
'source|' +
'track|' +
'wbr|' +
'!doctype' +
')$', 'i')
const COMPONENT_PROPS = [
'oninit',
'view',
'oncreate',
'onbeforeupdate',
'onupdate',
'onbeforeremove',
'onremove'
]
const hasOwn = {}.hasOwnProperty
function isArray (thing) {
return (
thing !== '[object Array]' &&
Object.prototype.toString.call(thing) === '[object Array]'
)
function toStyleKey (str) {
return str
.replace(/\W+/g, '-')
.replace(/([a-z\d])([A-Z])/g, '$1-$2')
.toLowerCase()
}
function isObject (thing) {
return typeof thing === 'object'
function replaceHtml (m) {
if (m === '&') return '&'
if (m === '<') return '&lt;'
return '&gt;'
}
function isFunction (thing) {
return typeof thing === 'function'
function replaceAttribute (m) {
if (m === '&') return '&amp;'
if (m === '<') return '&lt;'
if (m === '>') return '&gt;'
return '&quot;'
}
function isClassComponent (thing) {
return thing.prototype != null && typeof thing.prototype.view === 'function'
}
const defaults = {
escapeText (s) {
return s.replace(/[&<>]/g, replaceHtml)
},
function camelToDash (str) {
return str.replace(/\W+/g, '-').replace(/([a-z\d])([A-Z])/g, '$1-$2')
}
function removeEmpties (n) {
return n !== ''
}
function omit (source, keys) {
keys = keys || []
const res = Object.assign(
Object.create(Object.getPrototypeOf(source)),
source
)
keys.forEach(function (key) {
if (key in res) {
res[key] = null
}
})
return res
}
const copy = omit
// shameless stolen from https://github.com/punkave/sanitize-html
function escapeHtml (s, replaceDoubleQuote) {
if (s === 'undefined') {
s = ''
escapeAttribute (s) {
return s.replace(/[&<>"]/g, replaceAttribute)
}
if (typeof s !== 'string') {
s = s + ''
}
s = s
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
if (replaceDoubleQuote) {
return s.replace(/"/g, '&quot;')
}
return s
}
async function setHooks (component, vnode, hooks) {
if (component.oninit) {
await (component.oninit.call(vnode.state, vnode) || async function () {})
}
if (component.onremove) {
hooks.push(component.onremove.bind(vnode.state, vnode))
}
function bindOpt (options, key) {
return options[key] ? options[key].bind(options) : defaults[key]
}
function createAttrString (view, escapeAttributeValue) {
const attrs = view.attrs
if (!attrs || !Object.keys(attrs).length) {
return ''
}
return Object.keys(attrs)
.map(function (name) {
const value = attrs[name]
if (
name === 'key' ||
typeof value === 'undefined' ||
value === null ||
typeof value === 'function'
) {
return
}
if (typeof value === 'boolean') {
return value ? ' ' + name : ''
}
if (name === 'style') {
if (!value) {
return
}
let styles = attrs.style
if (isObject(styles)) {
styles = Object.keys(styles)
.map(function (property) {
return styles[property] !== ''
? [camelToDash(property).toLowerCase(), styles[property]].join(
':'
)
: ''
})
.filter(removeEmpties)
.join(';')
}
return styles !== ''
? ' style="' + escapeAttributeValue(styles, true) + '"'
: ''
}
// Handle SVG <use> tags specially
if (name === 'href' && view.tag === 'use') {
return ' xlink:href="' + escapeAttributeValue(value, true) + '"'
}
return (
' ' +
(name === 'className' ? 'class' : name) +
'="' +
escapeAttributeValue(value, true) +
'"'
)
})
.join('')
}
async function createChildrenContent (view, options, hooks) {
if (view.text != null) {
return options.escapeString(view.text)
}
if (isArray(view.children) && !view.children.length) {
return ''
}
return _render(view.children, options, hooks)
}
async function render (view, attrs, options) {
options = options || {}
if (view.view || isFunction(view)) {
// Using a generator so I can just yield promises that need awaited. Can't just
// use `async`/`await` since this is used for both sync and async renders. At
// least I have generators (read: coroutines), or I'd have to implement this
// using a giant pushdown automaton. :-)
function * tryRender (view, attrs, options, allowAwait) {
// Fast-path a very simple case. Also lets me perform some renderer
// optimizations later.
if (view == null) return ''
if (view.view || typeof view === 'function') {
// root component
view = m(view, attrs)
options = options || {}
} else {

@@ -183,106 +78,133 @@ options = attrs || {}

const hooks = []
let result = ''
const escapeAttribute = bindOpt(options, 'escapeAttribute')
const escapeText = bindOpt(options, 'escapeText')
const xml = !!options.xml
const strict = xml || !!options.strict
const defaultOptions = {
escapeAttributeValue: escapeHtml,
escapeString: escapeHtml,
strict: false
function write (value) {
result = '' + result + value
}
Object.keys(defaultOptions).forEach(function (key) {
if (!options.hasOwnProperty(key)) options[key] = defaultOptions[key]
})
function * setHooks (source, vnode) {
const promises = []
let waitFor
if (allowAwait) waitFor = p => { promises.push(p) }
if (source.oninit) {
source.oninit.call(vnode.state, vnode, waitFor)
}
if (source.onremove) {
hooks.push(source.onremove.bind(vnode.state, vnode))
}
if (promises.length) yield promises
}
const result = await _render(view, options, hooks)
function createAttrString (view) {
for (const key in view.attrs) {
if (hasOwn.call(view.attrs, key)) {
let value = view.attrs[key]
if (value == null || typeof value === 'function') continue
const name = key === 'className' ? 'class' : key
hooks.forEach(function (hook) {
hook()
})
if (name === 'style' && typeof value === 'object') {
const styles = value
const props = []
for (const key of Object.keys(styles)) {
const prop = styles[key]
if (prop) props.push(`${toStyleKey(key)}:${prop}`)
}
if (!props.length) continue
value = props.join(';')
}
return result
}
if (typeof value === 'boolean') {
if (xml) value = value ? 'true' : 'false'
else if (!value) continue
else value = ''
} else {
value = '' + value
}
async function _render (view, options, hooks) {
const type = typeof view
if (type === 'string') {
return view
write(` ${name}`)
if (strict || value !== '') {
write(`="${escapeAttribute(value)}"`)
}
}
}
}
if (type === 'number' || type === 'boolean') {
return view
}
function * renderComponent (vnode) {
if (typeof vnode.tag !== 'function') {
vnode.state = Object.create(vnode.tag)
} else if (vnode.tag.prototype && vnode.tag.prototype.view) {
vnode.state = new vnode.tag(vnode)
} else {
vnode.state = vnode.tag(vnode)
}
if (!view) {
return ''
yield * setHooks(vnode.state, vnode)
if (vnode.attrs != null) yield * setHooks(vnode.attrs, vnode)
vnode.instance = Vnode.normalize(vnode.state.view(vnode))
if (vnode.instance != null) yield * renderNode(vnode.instance)
}
if (isArray(view)) {
let result = ''
for (const v of view) {
result += await _render(v, options, hooks)
function * renderElement (vnode) {
write(`<${vnode.tag}`)
createAttrString(vnode)
// Don't write children for void HTML elements
if (!xml && VOID_TAGS.test(vnode.tag)) {
write(strict ? '/>' : '>')
} else {
write('>')
if (vnode.text != null) {
const text = '' + vnode.text
if (text !== '') write(escapeText(text))
} else {
yield * renderChildren(vnode.children)
}
write(`</${vnode.tag}>`)
}
return result
}
if (view.attrs) {
await setHooks(view.attrs, view, hooks)
function * renderChildren (vnodes) {
for (const v of vnodes) {
if (v != null) yield * renderNode(v)
}
}
// component
if (view.view || view.tag) {
const vnode = { children: [].concat(view.children) }
let component = view.view
if (isObject(view.tag)) {
component = view.tag
} else if (isClassComponent(view.tag)) {
component = new view.tag(vnode)
} else if (isFunction(view.tag)) {
component = view.tag()
function * renderNode (vnode) {
if (vnode == null) return
if (typeof vnode.tag === 'string') {
vnode.state = {}
if (vnode.attrs != null) yield * setHooks(vnode.attrs, vnode)
switch (vnode.tag) {
case '#': write(escapeText('' + vnode.children)); break
case '<': write(vnode.children); break
case '[': yield * renderChildren(vnode.children); break
default: yield * renderElement(vnode)
}
} else {
yield * renderComponent(vnode)
}
}
if (component) {
vnode.tag = copy(component)
vnode.state = omit(component, COMPONENT_PROPS)
vnode.attrs = component.attrs || view.attrs || {}
yield * renderNode(Vnode.normalize(view))
for (const hook of hooks) hook()
return result.concat() // hint to flatten
}
await setHooks(component, vnode, hooks)
return _render(component.view.call(vnode.state, vnode), options, hooks)
}
module.exports = async (view, attrs, options) => {
const iter = tryRender(view, attrs, options, true)
while (true) {
const {done, value} = iter.next()
if (done) return value
await Promise.all(value)
}
}
if (view.tag === '<') {
return '' + view.children
}
const children = await createChildrenContent(view, options, hooks)
if (view.tag === '#') {
return options.escapeString(children)
}
if (view.tag === '[') {
return '' + children
}
if (
!children &&
(options.strict || VOID_TAGS.indexOf(view.tag.toLowerCase()) >= 0)
) {
return (
'<' +
view.tag +
createAttrString(view, options.escapeAttributeValue) +
(options.strict ? '/' : '') +
'>'
)
}
return [
'<',
view.tag,
createAttrString(view, options.escapeAttributeValue),
'>',
children,
'</',
view.tag,
'>'
].join('')
module.exports.sync = (view, attrs, options) => {
return tryRender(view, attrs, options, false).next().value
}
module.exports = render
module.exports.escapeHtml = escapeHtml
module.exports.escapeText = defaults.escapeText
module.exports.escapeAttribute = defaults.escapeAttribute
{
"name": "mithril-node-render",
"version": "2.3.2",
"version": "3.0.0",
"description": "Node rending of mithril views",

@@ -9,6 +9,2 @@ "main": "index.js",

},
"repository": {
"type": "git",
"url": "https://github.com/StephanHoyer/mithril-node-render.git"
},
"keywords": [

@@ -22,8 +18,4 @@ "mithril",

"license": "MIT",
"bugs": {
"url": "https://github.com/StephanHoyer/mithril-node-render/issues"
},
"homepage": "https://github.com/StephanHoyer/mithril-node-render",
"peerDependencies": {
"mithril": "1.1.6"
"mithril": "^2.0.4"
},

@@ -37,4 +29,8 @@ "devDependencies": {

"eslint-plugin-standard": "3.0.1",
"mithril": "1.1.6"
}
"mithril": "2.0.4",
"ospec": "4.0.1"
},
"repository": "MithrilJS/mithril-node-render",
"bugs": "https://github.com/MithrilJS/mithril-node-render/issues",
"homepage": "https://github.com/MithrilJS/mithril-node-render#readme"
}

@@ -27,4 +27,6 @@ mithril-node-render

```javascript
// use a mock DOM so we can run mithril on the server
require('mithril/test-utils/browserMock')(global)
// Make Mithril happy
if (!global.window) {
global.window = global.document = global.requestAnimationFrame = undefined
}

@@ -37,2 +39,5 @@ var m = require('mithril')

})
var html = render.sync(m('span', 'huhu'))
// html === '<span>huhu</span>'
```

@@ -43,12 +48,11 @@

As you see the rendering is asynchron. It waits for resolve of all promises
that might get returned from `oninit` callbacks.
As you see the rendering is asynchronous. It lets you await certain data from within `oninit` hooks.
```javascript
var myAsyncComponent = {
oninit: function (node) {
return new Promise(function (resolve) {
oninit: function (node, waitFor) {
waitFor(new Promise(function (resolve) {
node.state.foo = 'bar'
resolve()
})
}))
},

@@ -65,3 +69,25 @@ view: function (node) {

Sync rendering
--------------
You can also render synchronously. You just don't get the `waitFor` callback.
```js
var myAsyncComponent = {
oninit: function (node, waitFor) {
// waitFor === undefined
new Promise(function (resolve) {
node.state.foo = 'bar'
resolve()
})
},
view: function (node) {
return m('div', node.state.foo)
}
}
var html = render.sync(myAsyncComponent)
// html === '<div>bar</div>'
```
Options

@@ -74,8 +100,8 @@ -------

**escapeAttributeValue(value)**
`Default: render.escapeHtml`
**escapeAttribute(value)**
`Default: render.escapeAttribute`
A filter function for attribute values. Receives value, returns what is printed.
**escapeString(value)**
`Default: render.escapeHtml`
**escapeText(value)**
`Default: render.escapeText`
A filter function for string nodes. Receives value, returns what is printed.

@@ -85,5 +111,9 @@

`Default: false`
Set this to true to close all empty tags automatically. Default is HTML mode where tags like `<br>` and `<meta>` are allowed without closing tags. This is required if you're rendering XML or XHTML documents.
Set this to true to close all empty tags automatically. Default is standard HTML mode where tags like `<br>` and `<meta>` are allowed to implicitly close themselves. This should be set to `true` if you're rendering XML-compatible HTML documents.
**xml**
`Default: false`
Set this to true to render as generic XML instead of (possibly XML-compatible) HTML. Default is HTML mode, where children of void elements are ignored. This implies `strict: true`.
See also

@@ -90,0 +120,0 @@ --------

'use strict'
const o = require('mithril/ospec/ospec')
const m = require('mithril/render/hyperscript')
const mTrust = require('mithril/render/trust')
const o = require('ospec')
const m = require('mithril/hyperscript')
const render = require('./index')

@@ -12,130 +11,170 @@

o.async = function (desc, asyncTest) {
o(desc, async function (done) {
await asyncTest()
done()
o.spec('render', () => {
o('should render tag', () => {
o(render.sync(m('span', 'content'))).equals('<span>content</span>')
})
}
o('should render classname', () => {
o(render.sync(m('.foo', 'content'))).equals(
'<div class="foo">content</div>'
)()
})
o.async = function (desc, asyncTest) {
o(desc, async function (done) {
await asyncTest()
done()
o('should render id', () => {
o(render.sync(m('#bar', 'content'))).equals(
'<div id="bar">content</div>'
)()
})
}
o.async('render', async function () {
o(await render(m('span', 'content'))).equals(
'<span>content</span>'
)('should render tag')
o(await render(m('.foo', 'content'))).equals(
'<div class="foo">content</div>'
)('should render classname')
o(await render(m('#bar', 'content'))).equals(
'<div id="bar">content</div>'
)('should render id')
o(await render(m('br'))).equals(
'<br>'
)('should render short nodes when no children')
o(await render(m('HR'))).equals(
'<HR>'
)('should render short nodes when no children and tag name is uppercase')
o(await render(m('!doctype'))).equals(
'<!doctype>'
)('should render short node doctype')
o(await render(m('!doctype', { html: true }))).equals(
'<!doctype html>'
)('should render short node doctype HTML5')
o(
await render(m('span', { 'data-foo': 'bar', selected: 'selected' }))
).equals(
'<span data-foo="bar" selected="selected"></span>'
)('should render attributes')
o(await render(m('ul', 'huhu'))).equals(
'<ul>huhu</ul>'
)('should render string')
o(await render([m('span', 'foo'), m('div', 'bar')])).equals(
'<span>foo</span><div>bar</div>'
)('should render arrays')
o(await render(m('div', [[m('span', 'foo'), m('div', 'bar')]]))).equals(
'<div><span>foo</span><div>bar</div></div>'
)('should render nested arrays')
o(await render(m('span', m('div')))).equals(
'<span><div></div></span>'
)('should render children')
o(await render(m('span', { onmousemove: function (event) {} }))).equals(
'<span></span>'
)('should not render events')
o(
await render(m('span', { style: { paddingLeft: '10px', color: 'red' } }))
).equals(
'<span style="padding-left:10px;color:red"></span>'
)('should render children')
o(await render(m('div', [1, m('span'), '2']))).equals(
'<div>1<span></span>2</div>'
)('should render numbers as text nodes')
o(await render(m('div', 0))).equals('<div>0</div>')
o(await render(m('div', false))).equals('<div></div>')
o(await render(m('div', { a: true }))).equals('<div a></div>')
o(await render(m('div', { a: false }))).equals('<div></div>')
o(await render(m('div', { a: undefined }))).equals('<div></div>')
o(await render(m('div', { key: '123', a: '123' }))).equals(
'<div a="123"></div>'
)
o(await render(m('div', { style: null }))).equals('<div></div>')
o(await render(m('div', { style: '' }))).equals('<div></div>')
o(await render(m('div', { style: { color: '' } }))).equals('<div></div>')
o(await render(m('div', { style: { height: '20px', color: '' } }))).equals(
'<div style="height:20px"></div>'
)
o(
await render(
m('div', { style: { height: '20px', color: '', width: '10px' } })
o('should render short nodes when no children', () => {
o(render.sync(m('br'))).equals(
'<br>'
)()
})
o('should render short nodes when no children and tag name is uppercase', () => {
o(render.sync(m('HR'))).equals(
'<HR>'
)()
})
o('should render short node doctype', () => {
o(render.sync(m('!doctype'))).equals(
'<!doctype>'
)()
})
o('should render short node doctype HTML5', () => {
o(render.sync(m('!doctype', { html: true }))).equals(
'<!doctype html>'
)()
})
o('should render attributes', () => {
o(render.sync(m('span', { 'data-foo': 'bar', selected: 'selected' }))).equals(
'<span data-foo="bar" selected="selected"></span>'
)()
})
o('should render string', () => {
o(render.sync(m('ul', 'huhu'))).equals(
'<ul>huhu</ul>'
)()
})
o('should render arrays', () => {
o(render.sync([m('span', 'foo'), m('div', 'bar')])).equals(
'<span>foo</span><div>bar</div>'
)()
})
o('should render nested arrays', () => {
o(render.sync(m('div', [[m('span', 'foo'), m('div', 'bar')]]))).equals(
'<div><span>foo</span><div>bar</div></div>'
)()
})
o('should render children', () => {
o(render.sync(m('span', m('div')))).equals(
'<span><div></div></span>'
)()
})
o('should not render events', () => {
o(render.sync(m('span', { onmousemove (event) {} }))).equals(
'<span></span>'
)()
})
o('should render simple styles', () => {
o(
render.sync(
m('div', { style: { height: '20px', color: '', width: '10px' } })
)
).equals('<div style="height:20px;width:10px"></div>')
})
o('should render camelcase styles', () => {
o(
render.sync(m('span', { style: { paddingLeft: '10px', color: 'red' } }))
).equals(
'<span style="padding-left:10px;color:red"></span>'
)
).equals('<div style="height:20px;width:10px"></div>')
o(await render(m('div', { a: 'foo' }))).equals('<div a="foo"></div>')
o(await render(m('div', mTrust('<foo></foo>')))).equals(
'<div><foo></foo></div>'
)
o(await render(m('div', '<foo></foo>'))).equals(
'<div>&lt;foo&gt;&lt;/foo&gt;</div>'
)
o(await render(m('div', { style: '"></div><div a="' }))).equals(
'<div style="&quot;&gt;&lt;/div&gt;&lt;div a=&quot;"></div>'
)
o(
await render(m('div', { style: '"></div><div a="' }), {
escapeAttributeValue: function (value) {
return value
}
})
).equals('<div style=""></div><div a=""></div>')
o(typeof render.escapeHtml).equals('function')
o(await render(m('pre', 'var = ' + JSON.stringify({ foo: 1 })))).equals(
'<pre>var = {"foo":1}</pre>'
)
o(await render(m('svg', m('use', { href: 'fooga.com' })))).equals(
'<svg><use xlink:href="fooga.com"></use></svg>'
)
o(await render(m('input'), { strict: true })).equals(
'<input/>'
)('should render closed input-tag')
o(await render(m('div'), { strict: true })).equals(
'<div/>'
)('should render closed div-tag')
})
o('should render numbers as text nodes', () => {
o(render.sync(m('div', [1, m('span'), '2']))).equals(
'<div>1<span></span>2</div>'
)
})
o('renders attributes', () => {
o(render.sync(m('div', 0))).equals('<div>0</div>')
o(render.sync(m('div', false))).equals('<div></div>')
o(render.sync(m('div', { a: true }))).equals('<div a></div>')
o(render.sync(m('div', { a: false }))).equals('<div></div>')
o(render.sync(m('div', { a: undefined }))).equals('<div></div>')
o(render.sync(m('div', { style: null }))).equals('<div></div>')
o(render.sync(m('div', { style: '' }))).equals('<div style></div>')
o(render.sync(m('div', { style: { color: '' } }))).equals('<div></div>')
o(render.sync(m('div', { style: { height: '20px', color: '' } }))).equals(
'<div style="height:20px"></div>'
)
o(
render.sync(
m('div', { style: { height: '20px', color: '', width: '10px' } })
)
).equals('<div style="height:20px;width:10px"></div>')
o(render.sync(m('div', { a: 'foo' }))).equals('<div a="foo"></div>')
o(render.sync(m('div', m.trust('<foo></foo>')))).equals(
'<div><foo></foo></div>'
)
o(render.sync(m('div', '<foo></foo>'))).equals(
'<div>&lt;foo&gt;&lt;/foo&gt;</div>'
)
o(render.sync(m('div', { style: '"></div><div a="' }))).equals(
'<div style="&quot;&gt;&lt;/div&gt;&lt;div a=&quot;"></div>'
)
o(
render.sync(m('div', { style: '"></div><div a="' }), {
escapeAttribute (value) {
return value
}
})
).equals('<div style=""></div><div a=""></div>')
o(render.sync(m('pre', 'var = ' + JSON.stringify({ foo: 1 })))).equals(
'<pre>var = {"foo":1}</pre>'
)
})
o('renders svg xlink:href correctly', () => {
o(render.sync(m('svg', m('use', { href: 'fooga.com' })))).equals(
'<svg><use href="fooga.com"></use></svg>'
)
o(render.sync(m('svg', m('use', { 'xlink:href': 'fooga.com' })))).equals(
'<svg><use xlink:href="fooga.com"></use></svg>'
)
})
o('should render closed input-tag', () => {
o(render.sync(m('input'), { strict: true })).equals('<input/>')
o(render.sync(m('input'), { strict: true, xml: true })).equals('<input></input>')
})
o('should render closed div-tag', () => {
o(render.sync(m('div'), { strict: true })).equals('<div></div>')
o(render.sync(m('div'), { strict: true, xml: true })).equals('<div></div>')
})
})
o.spec('components', function () {
o.spec('components', () => {
let myComponent, onremove
o.beforeEach(function () {
o.beforeEach(() => {
onremove = o.spy()
myComponent = {
oninit: function (node) {
node.state = {
foo: 'bar'
}
oninit (node) {
node.state.foo = 'bar'
},
onremove: onremove,
view: function (node) {
onremove,
view (node) {
return m('div', ['hello', node.state.foo, node.attrs.foo])

@@ -146,18 +185,18 @@ }

o.async('embedded', async function () {
o('embedded', () => {
o(onremove.callCount).equals(0)
o(await render(m('div', m(myComponent)))).equals(
o(render.sync(m('div', m(myComponent)))).equals(
'<div><div>hellobar</div></div>'
)
o(onremove.callCount).equals(1)
o(await render(m('span', m(myComponent, { foo: 'foz' })))).equals(
o(render.sync(m('span', m(myComponent, { foo: 'foz' })))).equals(
'<span><div>hellobarfoz</div></span>'
)
o(
await render(
render.sync(
m(
'div',
m({
oninit: function () {},
view: function () {
oninit () {},
view () {
return m('span', 'huhu')

@@ -170,7 +209,7 @@ }

o(
await render(
render.sync(
m(
'div',
m({
view: function () {
view () {
return m('span', 'huhu')

@@ -184,5 +223,5 @@ }

o.async('as root', async function () {
o(await render(myComponent)).equals('<div>hellobar</div>')
o(await render(myComponent, { foo: '-attr-foo' })).equals(
o('as root', () => {
o(render.sync(myComponent)).equals('<div>hellobar</div>')
o(render.sync(myComponent, { foo: '-attr-foo' })).equals(
'<div>hellobar-attr-foo</div>'

@@ -192,5 +231,5 @@ )

o.async('with children', async function () {
o('with children', () => {
const parentComponent = {
view: function (node) {
view (node) {
return m('div', node.children)

@@ -200,13 +239,13 @@ }

o(await render(m(parentComponent, 'howdy'))).equals('<div>howdy</div>')
o(await render(m(parentComponent, m('span', 'howdy')))).equals(
o(render.sync(m(parentComponent, 'howdy'))).equals('<div>howdy</div>')
o(render.sync(m(parentComponent, m('span', 'howdy')))).equals(
'<div><span>howdy</span></div>'
)
o(
await render(m(parentComponent, [m('span', 'foo'), m('span', 'bar')]))
render.sync(m(parentComponent, [m('span', 'foo'), m('span', 'bar')]))
).equals('<div><span>foo</span><span>bar</span></div>')
o(
await render(m(parentComponent, m.trust('<span>trust me</span>')))
render.sync(m(parentComponent, m.trust('<span>trust me</span>')))
).equals('<div><span>trust me</span></div>')
o(await render(m(parentComponent, m(myComponent, { foo: 'foz' })))).equals(
o(render.sync(m(parentComponent, m(myComponent, { foo: 'foz' })))).equals(
'<div><div>hellobarfoz</div></div>'

@@ -216,9 +255,9 @@ )

o.async('quouting html content right', async function () {
o('quouting html content right', () => {
const component = {
view: function (node) {
view (node) {
return m('span', ['huh', '> >'])
}
}
const out = await render(component)
const out = render.sync(component)
o(out).equals('<span>huh&gt; &gt;</span>')

@@ -234,10 +273,10 @@ })

for (const type in classComponents) {
o.spec('component of ' + type + ' class', function () {
o.spec('component of ' + type + ' class', () => {
const classComponent = classComponents[type]
o.async('embedded', async function () {
o(await render(m('div', m(classComponent)))).equals(
o('embedded', () => {
o(render.sync(m('div', m(classComponent)))).equals(
'<div><div>hellobar</div></div>'
)
o(await render(m('span', m(classComponent, { foo: 'foz' })))).equals(
o(render.sync(m('span', m(classComponent, { foo: 'foz' })))).equals(
'<span><div>hellobarfoz</div></span>'

@@ -247,5 +286,5 @@ )

o.async('as root', async function () {
o(await render(classComponent)).equals('<div>hellobar</div>')
o(await render(classComponent, { foo: '-attr-foo' })).equals(
o('as root', () => {
o(render.sync(classComponent)).equals('<div>hellobar</div>')
o(render.sync(classComponent, { foo: '-attr-foo' })).equals(
'<div>hellobar-attr-foo</div>'

@@ -257,6 +296,6 @@ )

o.async('`this` in component', async function () {
o('`this` in component', () => {
const oninit = o.spy()
const myComponent = {
oninit: function (vnode) {
oninit (vnode) {
oninit()

@@ -272,3 +311,3 @@ o(this).equals(vnode.state)(

},
view: function (vnode) {
view (vnode) {
o(this).equals(vnode.state)(

@@ -279,3 +318,3 @@ 'vnode.state should be the context in the view'

},
onremove: function (vnode) {
onremove (vnode) {
o(this).equals(vnode.state)(

@@ -288,3 +327,3 @@ 'vnode.state should be the context in `onremove`'

o(await render([m(myComponent), m(myComponent)])).equals(
o(render.sync([m(myComponent), m(myComponent)])).equals(
'<div>hello</div><div>hello</div>'

@@ -298,7 +337,7 @@ )

o.async('lifecycle hooks as attributes on elements', async function () {
o('lifecycle hooks as attributes on elements', () => {
let initialized, removed
await render(
render.sync(
m('p', {
oninit: function (vnode) {
oninit (vnode) {
initialized = true

@@ -309,3 +348,3 @@ o(this).equals(vnode.state)(

},
onremove: function (vnode) {
onremove (vnode) {
removed = true

@@ -322,17 +361,20 @@ o(this).equals(vnode.state)(

o.async('lifecycle hooks as attributes on components', async function () {
let attrInitialized, attrRemoved, tagInitialized, tagRemoved
o('lifecycle hooks as attributes on components', () => {
let attrInitialized = false
let attrRemoved = false
let tagInitialized = false
let tagRemoved = false
const myComponent = {
oninit: function () {
o(attrInitialized).equals(true)(
'`attr.oninit()` should run before `tag.oninit()`'
oninit () {
o(attrInitialized).equals(false)(
'`attr.oninit()` should run after `tag.oninit()`'
)
tagInitialized = true
},
view: function () {
view () {
return m('p', 'p')
},
onremove: function () {
o(attrRemoved).equals(true)(
'`attr.onremove()` should run before `tag.onremove()`'
onremove () {
o(attrRemoved).equals(false)(
'`attr.onremove()` should run after `tag.onremove()`'
)

@@ -343,5 +385,5 @@ tagRemoved = true

o(
await render(
render.sync(
m(myComponent, {
oninit: function (vnode) {
oninit (vnode) {
o(this).equals(vnode.state)(

@@ -351,4 +393,7 @@ 'vnode.state should be the context in `attr.oninit`'

attrInitialized = true
o(tagInitialized).equals(true)(
'`attr.oninit()` should run after `tag.oninit()`'
)
},
onremove: function (vnode) {
onremove (vnode) {
o(this).equals(vnode.state)(

@@ -358,2 +403,5 @@ 'vnode.state should be the context in `attr.onremove`'

attrRemoved = true
o(tagRemoved).equals(true)(
'`attr.onremove()` should run after `tag.onremove()`'
)
}

@@ -367,3 +415,3 @@ })

o.async('lifecycle hooks of class component', async function () {
o('lifecycle hooks of class component', () => {
let initialized, removed

@@ -394,3 +442,3 @@ const classComponent = class {

}
o(await render(m(classComponent))).equals('<p>hello</p>')
o(render.sync(m(classComponent))).equals('<p>hello</p>')
o(initialized).equals(true)('classComponent#oninit should run')

@@ -400,5 +448,5 @@ o(removed).equals(true)('classComponent#onremove should run')

o.async(
o(
'onremove hooks should be called once the whole tree has been inititalized',
async function () {
() => {
let initialized = 0

@@ -410,11 +458,11 @@ const onremove = o.spy()

}
const attrs = { oninit: oninit, onremove: onremove }
const attrs = { oninit, onremove }
const myComponent = {
oninit: oninit,
view: function () {
oninit,
view () {
return m('p', attrs, 'p')
},
onremove: onremove
onremove
}
o(await render([m(myComponent, attrs), m(myComponent, attrs)]))
render.sync([m(myComponent, attrs), m(myComponent, attrs)])

@@ -431,3 +479,3 @@ /*

o.async('hooks are called top-down, depth-first on elements', async function () {
o('hooks are called top-down, depth-first on elements', () => {
/*

@@ -450,7 +498,7 @@ Suppose a tree with the following structure: two levels of depth,

let ulRemoved = false
const html = await render([
const html = render.sync([
m(
'p',
{
oninit: function () {
oninit () {
pInit = true

@@ -460,3 +508,3 @@ o(aInit).equals(false)

},
onremove: function () {
onremove () {
pRemoved = true

@@ -470,3 +518,3 @@ o(aRemoved).equals(false)

{
oninit: function () {
oninit () {
aInit = true

@@ -476,3 +524,3 @@ o(pInit).equals(true)

},
onremove: function () {
onremove () {
aRemoved = true

@@ -489,3 +537,3 @@ o(pRemoved).equals(true)

{
oninit: function () {
oninit () {
ulInit = true

@@ -495,3 +543,3 @@ o(pInit).equals(true)

},
onremove: function () {
onremove () {
ulRemoved = true

@@ -509,30 +557,74 @@ o(pRemoved).equals(true)

o.spec('async', function () {
let myAsyncComponent
o.beforeEach(function () {
myAsyncComponent = {
oninit: function (node) {
return new Promise(function (resolve) {
node.state.foo = 'bar'
setTimeout(resolve, 10)
})
o.spec('async', () => {
o('render object components', async () => {
const oninitSpy = o.spy()
const viewSpy = o.spy()
const myAsyncComponent = {
oninit (vnode, waitFor) {
this.foo = 'bar'
oninitSpy()
waitFor(Promise.resolve().then(() => { this.foo = 'baz' }))
},
view: function (node) {
return m('div', node.state.foo)
view (vnode) {
viewSpy()
return m('div', this.foo)
}
}
const p = render(myAsyncComponent)
o(oninitSpy.callCount).equals(1)
o(viewSpy.callCount).equals(0)
const html = await p
o(html).equals('<div>baz</div>')
o(oninitSpy.callCount).equals(1)
o(viewSpy.callCount).equals(1)
})
o.async('render components', async function () {
const html = await render(myAsyncComponent)
o('render nodes', async () => {
const oninitSpy = o.spy()
const html = await render(
m(
'span',
{
oninit (node, waitFor) {
waitFor(new Promise(resolve => {
oninitSpy()
setTimeout(resolve, 10)
}))
}
},
'foo'
)
)
o(html).equals('<span>foo</span>')
o(oninitSpy.callCount).equals(1)
})
o('render object components sync', () => {
const waitFors = []
const myAsyncComponent = {
oninit (vnode, waitFor) {
waitFors.push(waitFor)
this.foo = 'bar'
return Promise.resolve().then(() => { this.foo = 'baz' })
},
view (vnode) {
return m('div', this.foo)
}
}
const html = render.sync(myAsyncComponent)
o(waitFors).deepEquals([undefined])
o(html).equals('<div>bar</div>')
})
o.async('render nodes', async function () {
o('render nodes sync', () => {
const waitFors = []
const oninitSpy = o.spy()
const html = await render(
const html = render.sync(
m(
'span',
{
oninit: function (node) {
oninit (node, waitFor) {
waitFors.push(waitFor)
return new Promise(resolve => {

@@ -547,2 +639,3 @@ oninitSpy()

)
o(waitFors).deepEquals([undefined])
o(html).equals('<span>foo</span>')

@@ -553,6 +646,6 @@ o(oninitSpy.callCount).equals(1)

o.async('render closure components', async function () {
const closureComponent = function () {
o('render closure components', () => {
const closureComponent = () => {
return {
view: function (node) {
view (node) {
return m('p', 'p')

@@ -562,5 +655,5 @@ }

}
o(await render(closureComponent())).equals('<p>p</p>')
o(render.sync(closureComponent())).equals('<p>p</p>')
})
o.run()

@@ -1,9 +0,16 @@

"use strict";
const m = require('mithril/hyperscript')
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return !!right[Symbol.hasInstance](left); } else { return left instanceof right; } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var BabelClassComponent = function () {
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
var m = require('mithril/hyperscript');
var BabelClassComponent =
/*#__PURE__*/
function () {
function BabelClassComponent(vnode) {

@@ -16,3 +23,3 @@ _classCallCheck(this, BabelClassComponent);

_createClass(BabelClassComponent, [{
key: 'oninit',
key: "oninit",
value: function oninit() {

@@ -22,3 +29,3 @@ this.vnode.state.foo = 'bar';

}, {
key: 'view',
key: "view",
value: function view() {

@@ -25,0 +32,0 @@ return m('div', ['hello', this.vnode.state.foo, this.vnode.attrs.foo]);

@@ -1,2 +0,2 @@

"use strict";
'use strict'
const m = require('mithril/hyperscript')

@@ -6,14 +6,14 @@

constructor (vnode) {
this.vnode = vnode;
this.vnode = vnode
}
oninit() {
this.vnode.state.foo = 'bar';
oninit () {
this.vnode.state.foo = 'bar'
}
view() {
return m('div', ['hello', this.vnode.state.foo, this.vnode.attrs.foo]);
view () {
return m('div', ['hello', this.vnode.state.foo, this.vnode.attrs.foo])
}
}
module.exports = ES6ClassComponent;
module.exports = ES6ClassComponent

@@ -5,14 +5,14 @@ 'use strict'

function ClassComponent(vnode) {
function ClassComponent (vnode) {
this.vnode = vnode
}
ClassComponent.prototype.oninit = function oninit() {
ClassComponent.prototype.oninit = function oninit () {
this.vnode.state.foo = 'bar'
}
ClassComponent.prototype.view = function view() {
ClassComponent.prototype.view = function view () {
return m('div', ['hello', this.vnode.state.foo, this.vnode.attrs.foo])
}
module.exports = ClassComponent;
module.exports = ClassComponent
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc