├── .gitignore ├── .travis.yml ├── README.md ├── appveyor.yml ├── index.js ├── lib ├── appendChild.js └── setAttribute.js ├── package.json └── test ├── browser.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | - '7' 5 | before_script: 6 | - export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yo-yoify 2 | 3 | [![NPM version][npm-image]][npm-url] 4 | [![build status][travis-image]][travis-url] 5 | [![build status][appveyor-image]][appveyor-url] 6 | [![Downloads][downloads-image]][downloads-url] 7 | [![js-standard-style][standard-image]][standard-url] 8 | 9 | Transform [choo][choo-url], [yo-yo][yo-yo-url], or [bel][bel-url] template 10 | strings into pure and fast document calls. 11 | 12 | ## install 13 | 14 | ```shell 15 | npm install yo-yoify --save-dev 16 | ``` 17 | 18 | ## usage 19 | 20 | When using Browserify, use as a global transform: 21 | 22 | ```shell 23 | browserify entry.js -g yo-yoify -o bundle.js 24 | ``` 25 | 26 | ## how this works 27 | 28 | `yo-yo` and `bel`, without this transform, pass template literals to `hyperx`. 29 | `hyperx` then parses and extracts the tags. `bel` then turns those tags into 30 | calls to `document.createElement()`. 31 | 32 | When using this transform, your template literals: 33 | 34 | ```js 35 | var msg = 'hello!' 36 | var element = yo`
${msg}
` 37 | ``` 38 | 39 | Transform into direct calls to the `document`: 40 | 41 | ```js 42 | var msg = 'hello!' 43 | var element = (function () { 44 | var bel0 = document.createElement("div") 45 | appendChild(bel0, [arguments[0]]) 46 | return bel0 47 | }(msg)) 48 | ``` 49 | 50 | Which means, way better performance and compatibility with older browsers. 51 | 52 | # see also 53 | 54 | - [babel-plugin-yo-yoify](https://www.npmjs.com/package/babel-plugin-yo-yoify) — yo-yoify as a Babel transform, works with any build setup that supports Babel 55 | 56 | # license 57 | (c) 2016 Kyle Robinson Young. MIT License 58 | 59 | [choo-url]: https://github.com/yoshuawuyts/choo 60 | [yo-yo-url]: https://github.com/maxogden/yo-yo 61 | [bel-url]: https://github.com/shama/bel 62 | [npm-image]: https://img.shields.io/npm/v/yo-yoify.svg?style=flat-square 63 | [npm-url]: https://npmjs.org/package/yo-yoify 64 | [travis-image]: https://img.shields.io/travis/shama/yo-yoify/master.svg?style=flat-square 65 | [travis-url]: https://travis-ci.org/shama/yo-yoify 66 | [appveyor-image]: https://img.shields.io/appveyor/ci/shama/yo-yoify/master.svg 67 | [appveyor-url]: https://ci.appveyor.com/project/shama/yo-yoify 68 | [downloads-image]: http://img.shields.io/npm/dm/vel.svg?style=flat-square 69 | [downloads-url]: https://npmjs.org/package/yo-yoify 70 | [standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square 71 | [standard-url]: https://github.com/feross/standard 72 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: "6" 4 | - nodejs_version: "7" 5 | platform: 6 | - x64 7 | install: 8 | - ps: Install-Product node $env:nodejs_version 9 | - npm install 10 | test_script: 11 | - node --version 12 | - npm --version 13 | - npm test 14 | build: off 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var convertSourceMap = require('convert-source-map') 3 | var transformAst = require('transform-ast') 4 | var through = require('through2') 5 | var hyperx = require('hyperx') 6 | var acorn = require('acorn') 7 | 8 | var SUPPORTED_VIEWS = ['bel', 'yo-yo', 'choo', 'choo/html'] 9 | var DELIM = '~!@|@|@!~' 10 | var VARNAME = 'bel' 11 | var SVGNS = 'http://www.w3.org/2000/svg' 12 | var XLINKNS = '"http://www.w3.org/1999/xlink"' 13 | var BOOL_PROPS = { 14 | autofocus: 1, 15 | checked: 1, 16 | defaultchecked: 1, 17 | disabled: 1, 18 | formnovalidate: 1, 19 | indeterminate: 1, 20 | readonly: 1, 21 | required: 1, 22 | selected: 1, 23 | willvalidate: 1 24 | } 25 | var SVG_TAGS = [ 26 | 'svg', 27 | 'altGlyph', 'altGlyphDef', 'altGlyphItem', 'animate', 'animateColor', 28 | 'animateMotion', 'animateTransform', 'circle', 'clipPath', 'color-profile', 29 | 'cursor', 'defs', 'desc', 'ellipse', 'feBlend', 'feColorMatrix', 30 | 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 31 | 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 32 | 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 33 | 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 34 | 'feSpotLight', 'feTile', 'feTurbulence', 'filter', 'font', 'font-face', 35 | 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 36 | 'foreignObject', 'g', 'glyph', 'glyphRef', 'hkern', 'image', 'line', 37 | 'linearGradient', 'marker', 'mask', 'metadata', 'missing-glyph', 'mpath', 38 | 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 39 | 'set', 'stop', 'switch', 'symbol', 'text', 'textPath', 'title', 'tref', 40 | 'tspan', 'use', 'view', 'vkern' 41 | ] 42 | 43 | module.exports = function yoYoify (file, opts) { 44 | if (/\.json$/.test(file)) return through() 45 | var bufs = [] 46 | var viewVariables = [] 47 | var babelTemplateObjects = Object.create(null) 48 | return through(write, end) 49 | function write (buf, enc, next) { 50 | bufs.push(buf) 51 | next() 52 | } 53 | function end (cb) { 54 | var src = Buffer.concat(bufs).toString('utf8') 55 | var res 56 | try { 57 | res = transformAst(src, { ecmaVersion: 8, parser: acorn }, walk) 58 | if (opts && opts._flags && opts._flags.debug) { 59 | res = res.toString() + '\n' + convertSourceMap.fromObject(res.map).toComment() + '\n' 60 | } else { 61 | res = res.toString() 62 | } 63 | } catch (err) { 64 | return cb(err) 65 | } 66 | this.push(res) 67 | this.push(null) 68 | } 69 | function walk (node) { 70 | if (isSupportedView(node)) { 71 | if (node.arguments[0].value === 'bel' || node.arguments[0].value === 'choo/html') { 72 | // bel and choo/html have no other exports that may be used 73 | node.edit.update('{}') 74 | } 75 | if (node.parent.type === 'VariableDeclarator') { 76 | viewVariables.push(node.parent.id.name) 77 | } 78 | } 79 | 80 | if (node.type === 'VariableDeclarator' && node.init && isBabelTemplateDefinition(node.init)) { 81 | // Babel generates helper calls like 82 | // _taggedTemplateLiteral([""], [""]) 83 | // The first parameter is the `cooked` template literal parts, and the second parameter is the `raw` 84 | // template literal parts. 85 | // We just pick the cooked parts. 86 | babelTemplateObjects[node.id.name] = node.init.arguments[0] 87 | } 88 | 89 | if (node.type === 'TemplateLiteral' && node.parent.tag) { 90 | var name = node.parent.tag.name || (node.parent.tag.object && node.parent.tag.object.name) 91 | if (viewVariables.indexOf(name) !== -1) { 92 | processNode(node.parent, [ node.quasis.map(cooked) ].concat(node.expressions.map(expr))) 93 | } 94 | } 95 | if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && viewVariables.indexOf(node.callee.name) !== -1) { 96 | var template, expressions 97 | if (node.arguments[0] && node.arguments[0].type === 'ArrayExpression') { 98 | // Detect transpiled template strings like: 99 | // bel([""], {id: "test"}) 100 | // Emitted by Buble. 101 | template = node.arguments[0].elements.map(function (part) { return part.value }) 102 | expressions = node.arguments.slice(1).map(expr) 103 | processNode(node, [ template ].concat(expressions)) 104 | } else if (node.arguments[0] && node.arguments[0].type === 'Identifier') { 105 | // Detect transpiled template strings like: 106 | // bel(_templateObject, {id: "test"}) 107 | // Emitted by Babel. 108 | var templateObject = babelTemplateObjects[node.arguments[0].name] 109 | template = templateObject.elements.map(function (part) { return part.value }) 110 | expressions = node.arguments.slice(1).map(expr) 111 | processNode(node, [ template ].concat(expressions)) 112 | 113 | // Remove the _taggedTemplateLiteral helper call 114 | templateObject.parent.edit.update('0') 115 | } 116 | } 117 | } 118 | } 119 | 120 | function processNode (node, args) { 121 | var resultArgs = [] 122 | var argCount = 0 123 | var tagCount = 0 124 | 125 | var needsAc = false 126 | var needsSa = false 127 | 128 | var hx = hyperx(function (tag, props, children) { 129 | var res = [] 130 | 131 | // Whether this element needs a namespace 132 | var namespace = props.namespace 133 | if (!namespace && SVG_TAGS.indexOf(tag) !== -1) { 134 | namespace = SVGNS 135 | } 136 | 137 | // Create the element 138 | var elname = VARNAME + tagCount 139 | tagCount++ 140 | if (namespace) { 141 | res.push('var ' + elname + ' = document.createElementNS(' + JSON.stringify(namespace) + ', ' + JSON.stringify(tag) + ')') 142 | } else { 143 | res.push('var ' + elname + ' = document.createElement(' + JSON.stringify(tag) + ')') 144 | } 145 | 146 | function addAttr (to, key, val) { 147 | // Normalize className 148 | if (key.toLowerCase() === '"classname"') { 149 | key = '"class"' 150 | } 151 | // The for attribute gets transformed to htmlFor, but we just set as for 152 | if (key === '"htmlFor"') { 153 | key = '"for"' 154 | } 155 | // If a property is boolean, set itself to the key 156 | if (BOOL_PROPS[key.slice(1, -1)]) { 157 | if (val.slice(0, 9) === 'arguments') { 158 | if (namespace) { 159 | res.push('if (' + val + ' && ' + key + ') ' + to + '.setAttributeNS(null, ' + key + ', ' + key + ')') 160 | } else { 161 | res.push('if (' + val + ' && ' + key + ') ' + to + '.setAttribute(' + key + ', ' + key + ')') 162 | } 163 | return 164 | } else { 165 | if (val === 'true') val = key 166 | else if (val === 'false') return 167 | } 168 | } 169 | if (key.slice(1, 3) === 'on') { 170 | res.push(to + '[' + key + '] = ' + val) 171 | } else { 172 | if (key === '"xlink:href"') { 173 | res.push(to + '.setAttributeNS(' + XLINKNS + ', ' + key + ', ' + val + ')') 174 | } else if (namespace && key.slice(0, 1) === '"') { 175 | res.push(to + '.setAttributeNS(null, ' + key + ', ' + val + ')') 176 | } else if (namespace) { 177 | res.push('if (' + key + ') ' + to + '.setAttributeNS(null, ' + key + ', ' + val + ')') 178 | } else if (key.slice(0, 1) === '"') { 179 | res.push(to + '.setAttribute(' + key + ', ' + val + ')') 180 | } else { 181 | needsSa = true 182 | res.push('sa(' + to + ', ' + key + ', ' + val + ')') 183 | } 184 | } 185 | } 186 | 187 | // Add properties to element 188 | Object.keys(props).forEach(function (key) { 189 | var prop = props[key] 190 | var ksrcs = getSourceParts(key) 191 | var srcs = getSourceParts(prop) 192 | var k, val 193 | if (srcs) { 194 | val = '' 195 | srcs.forEach(function (src, index) { 196 | if (src.arg) { 197 | if (index > 0) val += ' + ' 198 | if (src.before) val += JSON.stringify(src.before) + ' + ' 199 | val += 'arguments[' + argCount + ']' 200 | if (src.after) val += ' + ' + JSON.stringify(src.after) 201 | resultArgs.push(src.arg) 202 | argCount++ 203 | } 204 | }) 205 | } else { 206 | val = JSON.stringify(prop) 207 | } 208 | if (ksrcs) { 209 | k = '' 210 | ksrcs.forEach(function (src, index) { 211 | if (src.arg) { 212 | if (index > 0) val += ' + ' 213 | if (src.before) val += JSON.stringify(src.before) + ' + ' 214 | k += 'arguments[' + argCount + ']' 215 | if (src.after) k += ' + ' + JSON.stringify(src.after) 216 | resultArgs.push(src.arg) 217 | argCount++ 218 | } 219 | }) 220 | } else { 221 | k = JSON.stringify(key) 222 | } 223 | addAttr(elname, k, val) 224 | }) 225 | 226 | if (Array.isArray(children)) { 227 | var childs = [] 228 | children.forEach(function (child) { 229 | var srcs = getSourceParts(child) 230 | if (srcs) { 231 | var src = srcs[0] 232 | if (src.src) { 233 | res.push(src.src) 234 | } 235 | if (src.name) { 236 | childs.push(src.name) 237 | } 238 | if (src.arg) { 239 | var argname = 'arguments[' + argCount + ']' 240 | resultArgs.push(src.arg) 241 | argCount++ 242 | childs.push(argname) 243 | } 244 | } else { 245 | childs.push(JSON.stringify(child)) 246 | } 247 | }) 248 | if (childs.length > 0) { 249 | needsAc = true 250 | res.push('ac(' + elname + ', [' + childs.join(',') + '])') 251 | } 252 | } 253 | 254 | // Return delim'd parts as a child 255 | return DELIM + [elname, res.join('\n'), null].join(DELIM) + DELIM 256 | }) 257 | 258 | // Run through hyperx 259 | var res = hx.apply(null, args) 260 | 261 | // Pull out the final parts and wrap in a closure with arguments 262 | var src = getSourceParts(res) 263 | if (src && src[0].src) { 264 | var params = resultArgs.join(',') 265 | 266 | node.edit.update('(function () {' + 267 | (needsAc ? '\n var ac = require(\'' + path.resolve(__dirname, 'lib', 'appendChild.js').replace(/\\/g, '\\\\') + '\')' : '') + 268 | (needsSa ? '\n var sa = require(\'' + path.resolve(__dirname, 'lib', 'setAttribute.js').replace(/\\/g, '\\\\') + '\')' : '') + 269 | '\n ' + src[0].src + '\n return ' + src[0].name + '\n }(' + params + '))') 270 | } 271 | } 272 | 273 | function isSupportedView (node) { 274 | return (node.type === 'CallExpression' && 275 | node.callee && node.callee.name === 'require' && 276 | node.arguments.length === 1 && 277 | SUPPORTED_VIEWS.indexOf(node.arguments[0].value) !== -1) 278 | } 279 | 280 | function isBabelTemplateDefinition (node) { 281 | return node.type === 'CallExpression' && 282 | node.callee.type === 'Identifier' && node.callee.name === '_taggedTemplateLiteral' 283 | } 284 | 285 | function cooked (node) { return node.value.cooked } 286 | function expr (ex, idx) { 287 | return DELIM + [null, null, ex.source()].join(DELIM) + DELIM 288 | } 289 | function getSourceParts (str) { 290 | if (typeof str !== 'string') return false 291 | if (str.indexOf(DELIM) === -1) return false 292 | var parts = str.split(DELIM) 293 | 294 | var chunk = parts.splice(0, 5) 295 | var arr = [{ 296 | before: chunk[0], 297 | name: chunk[1], 298 | src: chunk[2], 299 | arg: chunk[3], 300 | after: chunk[4] 301 | }] 302 | while (parts.length > 0) { 303 | chunk = parts.splice(0, 4) 304 | arr.push({ 305 | before: '', 306 | name: chunk[0], 307 | src: chunk[1], 308 | arg: chunk[2], 309 | after: chunk[3] 310 | }) 311 | } 312 | 313 | return arr 314 | } 315 | -------------------------------------------------------------------------------- /lib/appendChild.js: -------------------------------------------------------------------------------- 1 | module.exports = function yoyoifyAppendChild (el, childs) { 2 | for (var i = 0; i < childs.length; i++) { 3 | var node = childs[i] 4 | if (Array.isArray(node)) { 5 | yoyoifyAppendChild(el, node) 6 | continue 7 | } 8 | if (typeof node === 'number' || 9 | typeof node === 'boolean' || 10 | node instanceof Date || 11 | node instanceof RegExp) { 12 | node = node.toString() 13 | } 14 | if (typeof node === 'string') { 15 | if (/^[\n\r\s]+$/.test(node)) continue 16 | if (el.lastChild && el.lastChild.nodeName === '#text') { 17 | el.lastChild.nodeValue += node 18 | continue 19 | } 20 | node = document.createTextNode(node) 21 | } 22 | if (node && node.nodeType) { 23 | el.appendChild(node) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/setAttribute.js: -------------------------------------------------------------------------------- 1 | module.exports = function yoyoifySetAttribute (el, attr, value) { 2 | if (typeof attr === 'object') { 3 | for (var i in attr) { 4 | if (attr.hasOwnProperty(i)) { 5 | yoyoifySetAttribute(el, i, attr[i]) 6 | } 7 | } 8 | return 9 | } 10 | if (!attr) return 11 | if (attr === 'className') attr = 'class' 12 | if (attr === 'htmlFor') attr = 'for' 13 | if (attr.slice(0, 2) === 'on') { 14 | el[attr] = value 15 | } else { 16 | // assume a boolean attribute if the value === true 17 | // no need to do typeof because "false" would've caused an early return 18 | if (value === true) value = attr 19 | el.setAttribute(attr, value) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yo-yoify", 3 | "version": "4.3.0", 4 | "description": "Transform yo-yo or bel template strings into pure and fast document calls", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "budo index.js", 8 | "test": "standard && node test/index.js && browserify -t ./index.js test/browser.js | tape-run" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/shama/yo-yoify" 13 | }, 14 | "keywords": [ 15 | "bel", 16 | "yo-yo", 17 | "choo", 18 | "elements", 19 | "browserify", 20 | "plugin", 21 | "transform" 22 | ], 23 | "files": [ 24 | "index.js", 25 | "lib" 26 | ], 27 | "author": "Kyle Robinson Young (http://dontkry.com)", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/shama/yo-yoify/issues" 31 | }, 32 | "homepage": "https://github.com/shama/yo-yoify", 33 | "dependencies": { 34 | "acorn": "^5.0.0", 35 | "convert-source-map": "^1.5.1", 36 | "hyperx": "^2.0.3", 37 | "on-load": "^3.2.0", 38 | "through2": "^2.0.1", 39 | "transform-ast": "^2.2.1" 40 | }, 41 | "devDependencies": { 42 | "babel-core": "^6.26.0", 43 | "babel-plugin-transform-es2015-template-literals": "^6.22.0", 44 | "babelify": "^8.0.0", 45 | "bel": "^4.3.2", 46 | "browserify": "^14.1.0", 47 | "bubleify": "^1.0.0", 48 | "choo": "^5.0.4", 49 | "standard": "^9.0.2", 50 | "tape": "^4.5.1", 51 | "tape-run": "^3.0.0", 52 | "yo-yo": "^1.2.1" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/browser.js: -------------------------------------------------------------------------------- 1 | var bel = require('bel') 2 | var test = require('tape') 3 | 4 | test('inline objects', function (t) { 5 | t.plan(1) 6 | var attributes = { className: 'boop' } 7 | var el = bel`
` 8 | var result = el.hasAttribute('class') && el.getAttribute('class') === 'boop' 9 | t.ok(result, 'attribute added from inline object') 10 | t.end() 11 | }) 12 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var browserify = require('browserify') 3 | var fs = require('fs') 4 | var path = require('path') 5 | 6 | var FIXTURE = path.join(__dirname, 'fixture.js') 7 | 8 | test('works', function (t) { 9 | t.plan(4) 10 | var src = 'var bel = require(\'bel\')\n module.exports = function (data) {\n var className = \'test\'\n return bel`
\n

${data}

\n
`\n }' // eslint-disable-line 11 | fs.writeFileSync(FIXTURE, src) 12 | var b = browserify(FIXTURE, { 13 | browserField: false, 14 | transform: path.join(__dirname, '..') 15 | }) 16 | b.bundle(function (err, src) { 17 | fs.unlinkSync(FIXTURE) 18 | t.ifError(err, 'no error') 19 | var result = src.toString() 20 | t.ok(result.indexOf('var bel = {}') !== -1, 'replaced bel dependency with {}') 21 | t.ok(result.indexOf('document.createElement("h1")') !== -1, 'created an h1 tag') 22 | t.ok(result.indexOf('setAttribute("class", arguments[1])') !== -1, 'set a class attribute') 23 | t.end() 24 | }) 25 | }) 26 | 27 | test('strings + template expressions', function (t) { 28 | t.plan(2) 29 | var src = 'var bel = require(\'bel\')\n var className = \'test\'\n var el = bel`
`' // eslint-disable-line 30 | fs.writeFileSync(FIXTURE, src) 31 | var b = browserify(FIXTURE, { 32 | browserField: false, 33 | transform: path.join(__dirname, '..') 34 | }) 35 | b.bundle(function (err, src) { 36 | fs.unlinkSync(FIXTURE) 37 | t.ifError(err, 'no error') 38 | var result = src.toString() 39 | t.ok(result.indexOf('bel0.setAttribute("class", "before " + arguments[0] + " after")') !== -1, 'concats strings + template expressions') 40 | t.end() 41 | }) 42 | }) 43 | 44 | test('append children in the correct order', function (t) { 45 | t.plan(2) 46 | var src = 'var bel = require(\'bel\')\n var el = bel`
This is a test to ensure strings get appended in the correct order.
`' // eslint-disable-line 47 | fs.writeFileSync(FIXTURE, src) 48 | var b = browserify(FIXTURE, { 49 | browserField: false, 50 | transform: path.join(__dirname, '..') 51 | }) 52 | b.bundle(function (err, src) { 53 | fs.unlinkSync(FIXTURE) 54 | t.ifError(err, 'no error') 55 | var result = src.toString() 56 | var expected = '(bel2, ["This is a ",bel0," to ensure ",bel1," get appended in the correct order."])' 57 | t.ok(result.indexOf(expected) !== -1, 'append children in the correct order') 58 | t.end() 59 | }) 60 | }) 61 | 62 | test('multiple values on single attribute', function (t) { 63 | t.plan(4) 64 | var src = 'var bel = require(\'bel\')\n var a = \'testa\'\n var b = \'testb\'\n bel`
`' // eslint-disable-line 65 | fs.writeFileSync(FIXTURE, src) 66 | var b = browserify(FIXTURE, { 67 | transform: path.join(__dirname, '..') 68 | }) 69 | b.bundle(function (err, src) { 70 | fs.unlinkSync(FIXTURE) 71 | t.ifError(err, 'no error') 72 | var result = src.toString() 73 | t.ok(result.indexOf('arguments[0]') !== -1, 'first argument') 74 | t.ok(result.indexOf('arguments[1]') !== -1, 'second argument') 75 | t.ok(result.indexOf('(a,b)') !== -1, 'calling with both variables') 76 | t.end() 77 | }) 78 | }) 79 | 80 | test('svg', function (t) { 81 | t.plan(2) 82 | var src = 'var bel = require(\'bel\')\n var el = bel``' // eslint-disable-line 83 | fs.writeFileSync(FIXTURE, src) 84 | var b = browserify(FIXTURE, { 85 | browserField: false, 86 | transform: path.join(__dirname, '..') 87 | }) 88 | b.bundle(function (err, src) { 89 | fs.unlinkSync(FIXTURE) 90 | t.ifError(err, 'no error') 91 | var result = src.toString() 92 | t.ok(result.indexOf('document.createElementNS("http://www.w3.org/2000/svg", "svg")') !== -1, 'created namespaced svg element') 93 | t.end() 94 | }) 95 | }) 96 | 97 | test('xlink:href', function (t) { 98 | t.plan(2) 99 | var src = 'var bel = require(\'bel\')\n var el = bel``' // eslint-disable-line 100 | fs.writeFileSync(FIXTURE, src) 101 | var b = browserify(FIXTURE, { 102 | browserField: false, 103 | transform: path.join(__dirname, '..') 104 | }) 105 | b.bundle(function (err, src) { 106 | fs.unlinkSync(FIXTURE) 107 | t.iferror(err, 'no error') 108 | var result = src.toString() 109 | var match = result.indexOf('setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "#cat")') !== -1 110 | t.ok(match, 'created namespaced xlink:href attribute') 111 | t.end() 112 | }) 113 | }) 114 | 115 | test('choo and friends', function (t) { 116 | t.plan(3) 117 | var src = 'const choo = require(\'choo\')\n const bel = require(\'bel\')\n const el1 = choo.view``\n const el2 = bel``' // eslint-disable-line 118 | fs.writeFileSync(FIXTURE, src) 119 | var b = browserify(FIXTURE, { 120 | transform: path.join(__dirname, '..') 121 | }) 122 | b.bundle(function (err, src) { 123 | fs.unlinkSync(FIXTURE) 124 | t.ifError(err, 'no error') 125 | var result = src.toString() 126 | t.ok(result.indexOf('const el1 = (function () {') !== -1, 'converted el1 to a iife') 127 | t.ok(result.indexOf('const el2 = (function () {') !== -1, 'converted el1 to a iife') 128 | t.end() 129 | }) 130 | }) 131 | 132 | test('emits error for syntax error', function (t) { 133 | var src = 'var bel = require(\'bel\')\n module.exports = function (data) {\n var className = (\'test\' + ) // <--- HERE\'S A SYNTAX ERROR\n return bel`
\n

${data}

\n
`\n }' // eslint-disable-line 134 | fs.writeFileSync(FIXTURE, src) 135 | var b = browserify(FIXTURE, { 136 | browserField: false, 137 | transform: path.join(__dirname, '..') 138 | }) 139 | b.bundle(function (err, src) { 140 | t.ok(err) 141 | t.end() 142 | }) 143 | }) 144 | 145 | test('works with newer js', function (t) { 146 | t.plan(1) 147 | var src = 'const bel = require(\'bel\')\n async function whatever() {\n return bel`
yep
`\n }' // eslint-disable-line 148 | fs.writeFileSync(FIXTURE, src) 149 | var b = browserify(FIXTURE, { 150 | transform: path.join(__dirname, '..') 151 | }) 152 | b.bundle(function (err, src) { 153 | fs.unlinkSync(FIXTURE) 154 | t.ifError(err, 'no error') 155 | t.end() 156 | }) 157 | }) 158 | 159 | test('boolean attribute expression', function (t) { 160 | t.plan(1) 161 | var src = 'const bel = require(\'bel\')\n async function whatever() {\nvar b = "disabled"\nreturn bel``\n }' // eslint-disable-line 162 | fs.writeFileSync(FIXTURE, src) 163 | var b = browserify(FIXTURE, { 164 | transform: path.join(__dirname, '..') 165 | }) 166 | b.bundle(function (err, src) { 167 | fs.unlinkSync(FIXTURE) 168 | t.ifError(err, 'no error') 169 | t.end() 170 | }) 171 | }) 172 | 173 | test('babel-compiled template literals', function (t) { 174 | t.plan(3) 175 | fs.writeFileSync(FIXTURE, ` 176 | var bel = require('bel') 177 | 178 | bel\`
\${xyz}
\` 179 | `) 180 | var b = browserify(FIXTURE, { 181 | transform: [ 182 | ['babelify', { 183 | plugins: ['transform-es2015-template-literals'] 184 | }], 185 | path.join(__dirname, '..') 186 | ] 187 | }) 188 | b.bundle(function (err, src) { 189 | fs.unlinkSync(FIXTURE) 190 | t.ifError(err) 191 | t.ok(src.indexOf('document.createElement("div")') !== -1, 'created a tag') 192 | t.ok(src.indexOf('\${xyz}
\` 204 | `) 205 | 206 | var b = browserify(FIXTURE, { 207 | transform: [ 208 | ['bubleify', { 209 | transforms: { 210 | dangerousTaggedTemplateString: true 211 | } 212 | }], 213 | path.join(__dirname, '..') 214 | ] 215 | }) 216 | b.bundle(function (err, src) { 217 | fs.unlinkSync(FIXTURE) 218 | t.ifError(err) 219 | t.ok(src.indexOf('document.createElement("div")') !== -1, 'created a tag') 220 | t.end() 221 | }) 222 | }) 223 | 224 | test('generates source maps in debug mode', function (t) { 225 | t.plan(2) 226 | fs.writeFileSync(FIXTURE, ` 227 | var html = require('bel') 228 | var el = html\`title\` 229 | html\` 230 |
231 |

\${el}

232 |
233 | \` 234 | `) 235 | 236 | var b = browserify(FIXTURE, { 237 | debug: true, 238 | transform: path.join(__dirname, '..') 239 | }) 240 | b.bundle(function (err, src) { 241 | fs.unlinkSync(FIXTURE) 242 | t.ifError(err) 243 | t.ok(src.indexOf('//# sourceMappingURL=') !== -1, 'has source map') 244 | t.end() 245 | }) 246 | }) 247 | 248 | test('accepts input source maps in debug mode', function (t) { 249 | t.plan(2) 250 | fs.writeFileSync(FIXTURE, ` 251 | var html = require('bel') 252 | var el = html\`title\` 253 | html\` 254 |
255 |

\${el}

256 |
257 | \` 258 | `) 259 | 260 | var b = browserify(FIXTURE, { 261 | debug: true, 262 | transform: [ 263 | ['babelify', { 264 | plugins: ['transform-es2015-template-literals'] 265 | }], 266 | path.join(__dirname, '..') 267 | ] 268 | }) 269 | b.bundle(function (err, src) { 270 | fs.unlinkSync(FIXTURE) 271 | t.ifError(err) 272 | t.ok(src.indexOf('//# sourceMappingURL=') !== -1, 'has source map') 273 | t.end() 274 | }) 275 | }) 276 | --------------------------------------------------------------------------------