Skip to content

Commit

Permalink
Add dom.js to allow custom document object (#165)
Browse files Browse the repository at this point in the history
* Add dom.js to allow custom document object

* fix test by keeping require()

* node version check

* fix standardjs errors

* unconditional html.js

* Also detect ./html in the babel plugin
  • Loading branch information
saschanaz authored and goto-bus-stop committed Oct 25, 2019
1 parent a79e024 commit bfb3aef
Show file tree
Hide file tree
Showing 14 changed files with 180 additions and 130 deletions.
1 change: 1 addition & 0 deletions dom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./lib/dom')
2 changes: 1 addition & 1 deletion lib/append-child.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ module.exports = function appendChild (el, childs) {

// We didn't have a text node yet, create one
} else {
node = document.createTextNode(node)
node = el.ownerDocument.createTextNode(node)
el.appendChild(node)
lastChild = node
}
Expand Down
114 changes: 1 addition & 113 deletions lib/browser.js
Original file line number Diff line number Diff line change
@@ -1,113 +1 @@
'use strict'

var hyperx = require('hyperx')
var appendChild = require('./append-child')
var SVG_TAGS = require('./svg-tags')
var BOOL_PROPS = require('./bool-props')
// Props that need to be set directly rather than with el.setAttribute()
var DIRECT_PROPS = require('./direct-props')

var SVGNS = 'http://www.w3.org/2000/svg'
var XLINKNS = 'http://www.w3.org/1999/xlink'

var COMMENT_TAG = '!--'

function nanoHtmlCreateElement (tag, props, children) {
var el

// If an svg tag, it needs a namespace
if (SVG_TAGS.indexOf(tag) !== -1) {
props.namespace = SVGNS
}

// If we are using a namespace
var ns = false
if (props.namespace) {
ns = props.namespace
delete props.namespace
}

// If we are extending a builtin element
var isCustomElement = false
if (props.is) {
isCustomElement = props.is
delete props.is
}

// Create the element
if (ns) {
if (isCustomElement) {
el = document.createElementNS(ns, tag, { is: isCustomElement })
} else {
el = document.createElementNS(ns, tag)
}
} else if (tag === COMMENT_TAG) {
return document.createComment(props.comment)
} else if (isCustomElement) {
el = document.createElement(tag, { is: isCustomElement })
} else {
el = document.createElement(tag)
}

// Create the properties
for (var p in props) {
if (props.hasOwnProperty(p)) {
var key = p.toLowerCase()
var val = props[p]
// Normalize className
if (key === 'classname') {
key = 'class'
p = 'class'
}
// The for attribute gets transformed to htmlFor, but we just set as for
if (p === 'htmlFor') {
p = 'for'
}
// If a property is boolean, set itself to the key
if (BOOL_PROPS.indexOf(key) !== -1) {
if (String(val) === 'true') val = key
else if (String(val) === 'false') continue
}
// If a property prefers being set directly vs setAttribute
if (key.slice(0, 2) === 'on' || DIRECT_PROPS.indexOf(key) !== -1) {
el[p] = val
} else {
if (ns) {
if (p === 'xlink:href') {
el.setAttributeNS(XLINKNS, p, val)
} else if (/^xmlns($|:)/i.test(p)) {
// skip xmlns definitions
} else {
el.setAttributeNS(null, p, val)
}
} else {
el.setAttribute(p, val)
}
}
}
}

appendChild(el, children)
return el
}

function createFragment (nodes) {
var fragment = document.createDocumentFragment()
for (var i = 0; i < nodes.length; i++) {
if (nodes[i] == null) continue
if (Array.isArray(nodes[i])) {
fragment.appendChild(createFragment(nodes[i]))
} else {
if (typeof nodes[i] === 'string') nodes[i] = document.createTextNode(nodes[i])
fragment.appendChild(nodes[i])
}
}
return fragment
}

module.exports = hyperx(nanoHtmlCreateElement, {
comments: true,
createFragment: createFragment
})
module.exports.default = module.exports
module.exports.createElement = nanoHtmlCreateElement
module.exports = require('./dom')(document)
116 changes: 116 additions & 0 deletions lib/dom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
'use strict'

var hyperx = require('hyperx')
var appendChild = require('./append-child')
var SVG_TAGS = require('./svg-tags')
var BOOL_PROPS = require('./bool-props')
// Props that need to be set directly rather than with el.setAttribute()
var DIRECT_PROPS = require('./direct-props')

var SVGNS = 'http://www.w3.org/2000/svg'
var XLINKNS = 'http://www.w3.org/1999/xlink'

var COMMENT_TAG = '!--'

module.exports = function (document) {
function nanoHtmlCreateElement (tag, props, children) {
var el

// If an svg tag, it needs a namespace
if (SVG_TAGS.indexOf(tag) !== -1) {
props.namespace = SVGNS
}

// If we are using a namespace
var ns = false
if (props.namespace) {
ns = props.namespace
delete props.namespace
}

// If we are extending a builtin element
var isCustomElement = false
if (props.is) {
isCustomElement = props.is
delete props.is
}

// Create the element
if (ns) {
if (isCustomElement) {
el = document.createElementNS(ns, tag, { is: isCustomElement })
} else {
el = document.createElementNS(ns, tag)
}
} else if (tag === COMMENT_TAG) {
return document.createComment(props.comment)
} else if (isCustomElement) {
el = document.createElement(tag, { is: isCustomElement })
} else {
el = document.createElement(tag)
}

// Create the properties
for (var p in props) {
if (props.hasOwnProperty(p)) {
var key = p.toLowerCase()
var val = props[p]
// Normalize className
if (key === 'classname') {
key = 'class'
p = 'class'
}
// The for attribute gets transformed to htmlFor, but we just set as for
if (p === 'htmlFor') {
p = 'for'
}
// If a property is boolean, set itself to the key
if (BOOL_PROPS.indexOf(key) !== -1) {
if (String(val) === 'true') val = key
else if (String(val) === 'false') continue
}
// If a property prefers being set directly vs setAttribute
if (key.slice(0, 2) === 'on' || DIRECT_PROPS.indexOf(key) !== -1) {
el[p] = val
} else {
if (ns) {
if (p === 'xlink:href') {
el.setAttributeNS(XLINKNS, p, val)
} else if (/^xmlns($|:)/i.test(p)) {
// skip xmlns definitions
} else {
el.setAttributeNS(null, p, val)
}
} else {
el.setAttribute(p, val)
}
}
}
}

appendChild(el, children)
return el
}

function createFragment (nodes) {
var fragment = document.createDocumentFragment()
for (var i = 0; i < nodes.length; i++) {
if (nodes[i] == null) continue
if (Array.isArray(nodes[i])) {
fragment.appendChild(createFragment(nodes[i]))
} else {
if (typeof nodes[i] === 'string') nodes[i] = document.createTextNode(nodes[i])
fragment.appendChild(nodes[i])
}
}
return fragment
}

var exports = hyperx(nanoHtmlCreateElement, {
comments: true,
createFragment: createFragment
})
exports.default = exports
exports.createComment = nanoHtmlCreateElement
return exports
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"files": [
"index.js",
"raw.js",
"dom.js",
"lib",
"types",
"dist"
Expand All @@ -16,7 +17,7 @@
"bench": "node bench/server.js && browserify bench/client.js | tape-run",
"build": "mkdir -p dist/ && browserify index -s html -p bundle-collapser/plugin > dist/bundle.js && browserify index -p tinyify > dist/bundle.min.js && cat dist/bundle.min.js | gzip --best --stdout | wc -c | pretty-bytes",
"prepublishOnly": "npm run build",
"test": "npm run test:standard && npm run test:node && npm run test:browser && npm run test:browser && npm run test:transform-browser && npm run test:babel-browser",
"test": "npm run test:standard && npm run test:node && npm run test:browser && npm run test:transform-browser && npm run test:babel-browser",
"test:standard": "standard",
"test:node": "node tests",
"test:browser": "browserify tests/browser | tape-run",
Expand Down Expand Up @@ -46,6 +47,7 @@
"bubleify": "^1.2.0",
"bundle-collapser": "^1.3.0",
"choo": "^6.9.0",
"jsdom": "^15.2.0",
"pify": "^3.0.0",
"standard": "^10.0.3",
"tape": "^4.8.0",
Expand Down
5 changes: 4 additions & 1 deletion tests/babel/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ const nanohtml = require('../../')

browserify(require.resolve('../browser'))
.transform('aliasify', {
aliases: { '../../': 'nanohtml' }
aliases: {
'../../': 'nanohtml',
'./html': 'nanohtml'
}
})
.transform('babelify', {
plugins: [
Expand Down
6 changes: 5 additions & 1 deletion tests/browser/api.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
var test = require('tape')
var html = require('../../')
if (typeof window !== 'undefined') {
var html = require('../../')
} else {
html = require('./html').html
}

test('creates an element', function (t) {
t.plan(3)
Expand Down
9 changes: 8 additions & 1 deletion tests/browser/elements.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
var test = require('tape')
var html = require('../../')
if (typeof window !== 'undefined') {
var document = window.document
var html = require('../../')
} else {
var nano = require('./html')
document = nano.document
html = nano.html
}

test('create inputs', function (t) {
t.plan(7)
Expand Down
9 changes: 8 additions & 1 deletion tests/browser/events.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
let test = require('tape')
var html = require('../../')
if (typeof window !== 'undefined') {
var document = window.document
var html = require('../../')
} else {
var nano = require('./html')
document = nano.document
html = nano.html
}

/* Note:
Failing tests have been commented. They include the following:
Expand Down
4 changes: 4 additions & 0 deletions tests/browser/html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
var doc = new (require('js' + 'dom').JSDOM)().window.document

module.exports.document = doc
module.exports.html = require('../../dom')(doc)
9 changes: 8 additions & 1 deletion tests/browser/multiple.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
var test = require('tape')
var html = require('../../')
if (typeof window !== 'undefined') {
var document = window.document
var html = require('../../')
} else {
var nano = require('./html')
document = nano.document
html = nano.html
}

test('multiple elements', function (t) {
var multiple = html`<li>Hamburg</li><li>Helsinki</li>haha<li>Berlin<div>test</div></li>`
Expand Down
16 changes: 9 additions & 7 deletions tests/browser/raw.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ var test = require('tape')
var html = require('../../')
var raw = require('../../raw')

test('unescape html', function (t) {
t.plan(1)
if (typeof window !== 'undefined') {
test('unescape html', function (t) {
t.plan(1)

var expected = html`<span>Hello there</span>`.toString()
var result = raw('<span>Hello&nbsp;there</span>').toString()
var expected = html`<span>Hello there</span>`.toString()
var result = raw('<span>Hello&nbsp;there</span>').toString()

t.equal(expected, result)
t.end()
})
t.equal(expected, result)
t.end()
})
}
10 changes: 8 additions & 2 deletions tests/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
if (typeof window !== 'undefined') {
function getNodeMajor () {
return process.version.split('.')[0].slice(1)
}

if (typeof process === 'undefined' || getNodeMajor() >= 8) {
require('./browser')
} else {
}

if (typeof window === 'undefined') {
require('./server')
require('./transform')
require('./babel')
Expand Down
5 changes: 4 additions & 1 deletion tests/transform/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ const nanohtml = require('../../')

browserify(require.resolve('../browser'))
.transform('aliasify', {
aliases: { '../../': 'nanohtml' }
aliases: {
'./html': 'nanohtml',
'../../': 'nanohtml'
}
})
.transform(nanohtml)
.bundle()
Expand Down

0 comments on commit bfb3aef

Please sign in to comment.