├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── commandline.js ├── index.js ├── package.json └── test ├── preservationOfInput.js └── validOutput.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Nick Matantsev 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | 7 | Source: http://opensource.org/licenses/ISC 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/unframework/html2hyperscript.svg?branch=master)](https://travis-ci.org/unframework/html2hyperscript) 2 | 3 | # Convert Legacy HTML to Hyperscript 4 | 5 | Automatically translate old HTML markup into the new Hyperscript markup embeddable directly inside your component Javascript code. 6 | 7 | Use this for hand-converting legacy project source code (e.g. AngularJS templates): care is taken to preserve original whitespace and even comments. For dynamic serving and CI builds check out https://github.com/alexmingoia/jsx-transform instead. 8 | 9 | ``` 10 | npm install -g html2hyperscript 11 | html2hyperscript legacy_markup_file.html > shiny_new_syntax.js 12 | ``` 13 | 14 | See Hyperscript library: https://github.com/dominictarr/hyperscript 15 | 16 | Also useful for virtual DOM Hyperscript-like syntax: https://github.com/Matt-Esch/virtual-dom 17 | 18 | HTML goes in: 19 | 20 | ```html 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
PandasKittensHedgehogs
foobarbaz
324583
onomatopoeiaschadenfreudeantidisestablishmentarianism
43 | ``` 44 | 45 | Hyperscript-like JS syntax comes out: 46 | 47 | ```js 48 | h("table.sample-html", [ 49 | h("tr", [ 50 | h("th", [ "Pandas" ]), 51 | h("th", [ "Kittens" ]), 52 | h("th", [ "Hedgehogs" ]) 53 | ]), 54 | h("tr", [ 55 | h("td", [ "foo" ]), 56 | h("td", [ "bar" ]), 57 | h("td", [ "baz" ]) 58 | ]), 59 | h("tr", [ 60 | h("td", [ "32" ]), 61 | h("td", [ "45" ]), 62 | h("td", [ "83" ]) 63 | ]), 64 | h("tr", [ 65 | h("td", [ "onomatopoeia" ]), 66 | h("td", [ "schadenfreude" ]), 67 | h("td", [ "antidisestablishmentarianism" ]) 68 | ]) 69 | ]) 70 | ``` 71 | 72 | ## TODO 73 | 74 | - some tests 75 | 76 | -------------------------------------------------------------------------------- /commandline.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs'); 4 | var convert = require('./'); 5 | 6 | var fileName = process.argv[2]; 7 | 8 | if (!fileName) { 9 | throw new Error('supply input HTML file as first argument'); 10 | } 11 | 12 | process.stdout.write(convert(fs.readFileSync(fileName))); 13 | process.stdout.write("\n"); 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | var Parser = require('htmlparser2').Parser; 3 | 4 | function ItemList(parent) { 5 | this.parent = parent; 6 | this.content = ''; 7 | this.spacer = ''; 8 | this.indent = parent ? parent.indent : ''; 9 | this.isFirstItem = true; 10 | } 11 | 12 | ItemList.prototype.addSpace = function (space) { 13 | this.spacer += space; 14 | 15 | if (space.indexOf("\n") !== -1) { 16 | // reset indent when there are new lines 17 | this.indent = /[^\n]*$/.exec(space)[0]; 18 | } else { 19 | // otherwise keep appending to current indent 20 | this.indent += space; 21 | } 22 | } 23 | 24 | ItemList.prototype.add = function (data, ignoreComma) { 25 | if (!ignoreComma) { 26 | if (!this.isFirstItem) { 27 | this.content += this.spacer.length ? ',' : ', '; 28 | } 29 | 30 | this.isFirstItem = false; 31 | } 32 | 33 | this.content += this.spacer; 34 | this.spacer = ''; 35 | 36 | this.content += data; 37 | } 38 | 39 | function convert(inputMarkup) { 40 | var elementStack = []; 41 | var currentItemList = new ItemList(null); 42 | 43 | var parser = new Parser({ 44 | onopentag: function (name, attribs) { 45 | currentItemList = new ItemList(currentItemList); 46 | elementStack.unshift([ name, attribs ]); 47 | }, 48 | ontext: function (text) { 49 | var lines = text.split("\n"); 50 | 51 | var isFirst = true; 52 | 53 | lines.forEach(function (line) { 54 | var lineMatch = /^(\s*)(.*?)(\s*)$/.exec(line); 55 | 56 | var preSpace = lineMatch[1], 57 | mainText = lineMatch[2], 58 | postSpace = lineMatch[3]; 59 | 60 | if (!isFirst) { 61 | currentItemList.addSpace("\n"); 62 | } 63 | 64 | currentItemList.addSpace(preSpace); 65 | 66 | if (mainText.length > 0) { 67 | currentItemList.add(JSON.stringify(mainText)); 68 | } 69 | 70 | isFirst = false; 71 | }); 72 | }, 73 | onclosetag: function (tagname) { 74 | var element = elementStack.shift(); 75 | var elementContent = currentItemList.content + currentItemList.spacer; 76 | 77 | currentItemList = currentItemList.parent; 78 | 79 | var indent = currentItemList.indent; 80 | 81 | var attribs = element[1]; 82 | 83 | var id = attribs['id']; 84 | var idSuffix = id !== undefined ? '#' + id : ''; 85 | delete attribs['id']; 86 | 87 | var classNames = attribs['class']; 88 | var classSuffix = (classNames !== undefined ? classNames : '').split(/\s+/g).filter(function (v) { return v.length > 0; }).map(function (cls) { return '.' + cls; }).join(''); 89 | delete attribs['class']; 90 | 91 | var attrPairs = Object.keys(attribs).map(function (k) { return JSON.stringify(k) + ': ' + JSON.stringify(attribs[k]) }); 92 | 93 | var item = 'h(' + JSON.stringify(element[0] + idSuffix + classSuffix) + ( 94 | attrPairs.length 95 | ? ", {\n" + indent + ' ' + attrPairs.join(",\n" + indent + ' ') + "\n" + indent + "}" 96 | : '' 97 | ) + ( 98 | elementContent.length 99 | ? ', [' + (elementContent[0] === "\n" ? '' : ' ') + elementContent + (elementContent.match(/\s$/) ? '' : ' ') + ']' 100 | : '' 101 | ) + ')'; 102 | 103 | currentItemList.add(item); 104 | }, 105 | oncomment: function (text) { 106 | currentItemList.add('/*' + text + '*/', false); // @todo comment-safety 107 | } 108 | }, {decodeEntities: true}); 109 | 110 | parser.write(inputMarkup); 111 | parser.end(); 112 | 113 | return currentItemList.content; 114 | } 115 | 116 | module.exports = convert; 117 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html2hyperscript", 3 | "version": "1.0.1", 4 | "description": "Convert legacy HTML to Hyperscript (https://github.com/dominictarr/hyperscript)", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/unframework/html2hyperscript.git" 8 | }, 9 | "main": "index.js", 10 | "scripts": { 11 | "test": "tap ./test" 12 | }, 13 | "bin": { 14 | "html2hyperscript": "./commandline.js" 15 | }, 16 | "author": "Nick Matantsev ", 17 | "license": "MIT", 18 | "dependencies": { 19 | "htmlparser2": "~3.8.2" 20 | }, 21 | "devDependencies": { 22 | "hyperscript": "~1.4.4", 23 | "tap": "~0.7.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/preservationOfInput.js: -------------------------------------------------------------------------------- 1 | 2 | var testCase = require('tap').test; 3 | var h = require('hyperscript'); 4 | var convert = require('../'); 5 | 6 | testCase(function (test) { 7 | test.equal(convert('
'), 'h("div", [ /* comment body */ ])', 'preserve comments'); 8 | test.equal(convert("
\n\n
"), "h(\"div\", [\n\n])", 'preserve newlines'); 9 | test.equal(convert("
\n \n
"), "h(\"div\", [\n h(\"span\") \n])", 'preserve indentation and whitespace'); 10 | test.equal(convert("
\n Some Text \n
"), "h(\"div\", [\n \"Some Text\"\n])", 'trim whitespace around text but preserve indent'); 11 | 12 | test.end(); 13 | }); 14 | -------------------------------------------------------------------------------- /test/validOutput.js: -------------------------------------------------------------------------------- 1 | 2 | var testCase = require('tap').test; 3 | var h = require('hyperscript'); 4 | var convert = require('../'); 5 | 6 | function executeJSMarkup(js) { 7 | var jsRunner = new Function('h', 'return ' + js); 8 | 9 | return jsRunner(h).outerHTML; 10 | } 11 | 12 | testCase(function (test) { 13 | var html = '
Some Text
'; 14 | 15 | test.equal(executeJSMarkup(convert(html)), html, 'generated markup should match source'); 16 | 17 | test.end(); 18 | }); 19 | 20 | testCase(function (test) { 21 | var html = '
'; 22 | 23 | test.equal(executeJSMarkup(convert(html)), html, 'generated markup should match source'); 24 | 25 | test.end(); 26 | }); 27 | --------------------------------------------------------------------------------