From e4c5d6e613faed6025074832a661a2f1f0289b8d Mon Sep 17 00:00:00 2001 From: ido Date: Tue, 11 Nov 2014 11:18:34 +0200 Subject: [PATCH] improve cli, eslint quotes --- .eslintrc | 2 +- src/cli.js | 40 +++++++++++---- src/reactTemplates.js | 115 ++++++++++++++++++++++-------------------- 3 files changed, 91 insertions(+), 66 deletions(-) diff --git a/.eslintrc b/.eslintrc index 69699d7..6855461 100644 --- a/.eslintrc +++ b/.eslintrc @@ -118,7 +118,7 @@ "new-cap": 2, "semi": 2, "use-isnan": 2, - "quotes": [0, "single"], + "quotes": [1, "single", "avoid-escape"], "max-params": [0, 3], "max-statements": [0, 10], "complexity": [0, 11], diff --git a/src/cli.js b/src/cli.js index ac5dc67..fd9ce0c 100644 --- a/src/cli.js +++ b/src/cli.js @@ -1,28 +1,48 @@ +#!/usr/bin/env node /** * Created by idok on 11/10/14. */ 'use strict'; -var fs = require('fs'); +//var fs = require('fs'); var _ = require('lodash'); var path = require('path'); var reactTemplates = require('./reactTemplates'); +var pkg = require('../package.json'); if (process.argv.length > 2) { - _.forEach(process.argv.slice(2), handleSingleFile); + if (process.argv.indexOf('-v') !== -1 || process.argv.indexOf('--version') !== -1) { + console.log(pkg.version); + } else if (process.argv.indexOf('-h') !== -1 || process.argv.indexOf('--help') !== -1) { + printHelp(); + } else { + _.forEach(process.argv.slice(2), handleSingleFile); + } } else { - console.log("Usage:node reactTemplates.js "); + printHelp(); +} + +function printHelp() { + console.log(pkg.description); + console.log(''); + console.log('Usage:'); + console.log(' $ node reactTemplates.js '); } function handleSingleFile(filename) { - if (path.extname(filename) !== ".rt") { + if (path.extname(filename) !== '.rt') { console.log('invalid file, only handle rt files'); return;// only handle html files } - var html = fs.readFileSync(filename).toString(); - if (!html.match(/\<\!doctype jsx/)) { - console.log('invalid file, missing header'); - return; +// var html = fs.readFileSync(filename).toString(); +// if (!html.match(/\<\!doctype jsx/)) { +// console.log('invalid file, missing header'); +// return; +// } +// var js = reactTemplates.convertTemplateToReact(html); +// fs.writeFileSync(filename + '.js', js); + try { + reactTemplates.convertFile(filename, filename + '.js'); + } catch (e) { + console.log('Error processing file: ' + filename + ', ' + e.description); } - var js = reactTemplates.convertTemplateToReact(html); - fs.writeFileSync(filename + '.js', js); } \ No newline at end of file diff --git a/src/reactTemplates.js b/src/reactTemplates.js index 818ed71..f02175a 100644 --- a/src/reactTemplates.js +++ b/src/reactTemplates.js @@ -10,16 +10,16 @@ var React = require('react'); var fs = require('fs'); -var repeatTemplate = _.template("_.map(<%= collection %>,function (<%= item %>,<%= item %>Index) {\n return <%= body %>}, this)"); -var ifTemplate = _.template("((<%= condition %>)?(<%= body %>):null)"); -var classSetTemplate = _.template("React.addons.classSet(<%= classSet %>)"); -var tagTemplate = _.template("<%= name %>.apply(this,_.flatten([<%= props %>].concat([<%= children %>])))"); -var commentTemplate = _.template(" /* <%= data %> */ "); -var templateTemplate = _.template("define([<%= requirePaths %>], function (<%= requireNames %>) {\n <%= injectedFunctions %>\nreturn function(){ return <%= body %>};\n});"); +var repeatTemplate = _.template('_.map(<%= collection %>,function (<%= item %>,<%= item %>Index) {\n return <%= body %>}, this)'); +var ifTemplate = _.template('((<%= condition %>)?(<%= body %>):null)'); +var classSetTemplate = _.template('React.addons.classSet(<%= classSet %>)'); +var tagTemplate = _.template('<%= name %>.apply(this,_.flatten([<%= props %>].concat([<%= children %>])))'); +var commentTemplate = _.template(' /* <%= data %> */ '); +var templateTemplate = _.template("define([<%= requirePaths %>], function (<%= requireNames %>) {\n'use strict';\n <%= injectedFunctions %>\nreturn function(){ return <%= body %>};\n});"); -var templateProp = "rt-repeat"; -var ifProp = "rt-if"; -var classSetProp = "rt-class"; +var templateProp = 'rt-repeat'; +var ifProp = 'rt-if'; +var classSetProp = 'rt-class'; var reactSupportedAttributes = ['accept', 'acceptCharset', 'accessKey', 'action', 'allowFullScreen', 'allowTransparency', 'alt', 'async', 'autoComplete', 'autoPlay', 'cellPadding', 'cellSpacing', 'charSet', 'checked', 'classID', 'className', 'cols', 'colSpan', 'content', 'contentEditable', 'contextMenu', 'controls', 'coords', 'crossOrigin', 'data', 'dateTime', 'defer', 'dir', 'disabled', 'download', 'draggable', 'encType', 'form', 'formNoValidate', 'frameBorder', 'height', 'hidden', 'href', 'hrefLang', 'htmlFor', 'httpEquiv', 'icon', 'id', 'label', 'lang', 'list', 'loop', 'manifest', 'max', 'maxLength', 'media', 'mediaGroup', 'method', 'min', 'multiple', 'muted', 'name', 'noValidate', 'open', 'pattern', 'placeholder', 'poster', 'preload', 'radioGroup', 'readOnly', 'rel', 'required', 'role', 'rows', 'rowSpan', 'sandbox', 'scope', 'scrolling', 'seamless', 'selected', 'shape', 'size', 'sizes', 'span', 'spellCheck', 'src', 'srcDoc', 'srcSet', 'start', 'step', 'style', 'tabIndex', 'target', 'title', 'type', 'useMap', 'value', 'width', 'wmode']; var attributesMapping = {'class': 'className', 'rt-class': 'className'}; @@ -31,11 +31,11 @@ _.forEach(reactSupportedAttributes,function (attributeReactName) { function concatChildren(children) { - var res = ""; + var res = ''; var first = true; _.forEach(children, function (child) { - if (child.indexOf(" /*") !== 0 && child) { - res += (first ? "" : ",") + child; + if (child.indexOf(' /*') !== 0 && child) { + res += (first ? '' : ',') + child; first = false; } else { res += child; @@ -48,29 +48,29 @@ var curlyMap = {'{': 1, '}': -1}; function convertText(txt) { txt = txt.trim(); - var res = ""; + var res = ''; var first = true; while (txt.indexOf('{') !== -1) { var start = txt.indexOf('{'); var pre = txt.substr(0,start); if (pre) { - res += (first ? "" : "+") + JSON.stringify(pre); + res += (first ? '' : '+') + JSON.stringify(pre); first = false; } var curlyCounter = 1; - for (var end = start + 1;end < txt.length && curlyCounter > 0;end++) { + for (var end = start + 1; end < txt.length && curlyCounter > 0; end++) { curlyCounter += curlyMap[txt.charAt(end)] || 0; } if (curlyCounter !== 0) { - throw "Failed to parse text"; + throw 'Failed to parse text'; } else { - res += (first ? "" : "+") + txt.substr(start + 1, end - start - 2); + res += (first ? '' : '+') + txt.substr(start + 1, end - start - 2); first = false; txt = txt.substr(end); } } if (txt) { - res += (first ? "" : "+") + JSON.stringify(txt); + res += (first ? '' : '+') + JSON.stringify(txt); } return res; @@ -86,57 +86,57 @@ function generateProps(node, context) { _.forOwn(node.attribs, function (val, key) { var propKey = attributesMapping[key.toLowerCase()] || key; if (props.hasOwnProperty(propKey)) { - throw "duplicate definition of " + propKey + " " + JSON.stringify(node.attribs); + throw 'duplicate definition of ' + propKey + ' ' + JSON.stringify(node.attribs); } - if (key.indexOf("on") === 0 && !isStringOnlyCode(val)) { - var funcParts = val.split("=>"); - var evtParams = funcParts[0].replace("(", "").replace(")", "").trim(); + if (key.indexOf('on') === 0 && !isStringOnlyCode(val)) { + var funcParts = val.split('=>'); + var evtParams = funcParts[0].replace('(', '').replace(')', '').trim(); var funcBody = funcParts[1].trim(); - var generatedFuncName = "generated" + (context.injectedFunctions.length + 1); + var generatedFuncName = 'generated' + (context.injectedFunctions.length + 1); var params = context.boundParams; if (evtParams.trim().length > 0) { params = params.concat(evtParams.trim()); } - var funcText = "function " + generatedFuncName + "(" + params.join(","); - funcText += ") {\n" + funcBody + "\n}\n"; + var funcText = 'function ' + generatedFuncName + '(' + params.join(','); + funcText += ') {\n' + funcBody + '\n}\n'; context.injectedFunctions.push(funcText); - props[propKey] = generatedFuncName + ".bind(" + (["this"].concat(context.boundParams)).join(",") + ")"; - } else if (key === "style" && !isStringOnlyCode(val)) { - var styleParts = val.trim().split(";"); + props[propKey] = generatedFuncName + '.bind(' + (['this'].concat(context.boundParams)).join(',') + ')'; + } else if (key === 'style' && !isStringOnlyCode(val)) { + var styleParts = val.trim().split(';'); styleParts = _.compact(_.map(styleParts, function (str) { str = str.trim(); if (!str || str.indexOf(':') === -1) { return null; } - var res = str.split(":"); + var res = str.split(':'); res[0] = res[0].trim(); - if (res[0].indexOf("-") != -1) { - res[0] = "\"" + res[0] + "\""; + if (res[0].indexOf('-') !== -1) { + res[0] = '"' + res[0] + '"'; } res[1] = res[1].trim(); return res; })); var styleArray = []; _.forEach(styleParts, function (stylePart) { - styleArray.push(stylePart[0] + " : " + convertText(stylePart[1])) + styleArray.push(stylePart[0] + ' : ' + convertText(stylePart[1])); }); - props[propKey] = "{" + styleArray.join(",") + "}"; + props[propKey] = '{' + styleArray.join(',') + '}'; } else if (key === classSetProp) { props[propKey] = classSetTemplate({classSet: val}); - } else if (key.indexOf("rt-") !== 0) { + } else if (key.indexOf('rt-') !== 0) { props[propKey] = convertText(val); } }); - return "{" + _.map(props, function (val, key) { - return JSON.stringify(key) + " : " + val; - }).join(",") + "}"; + return '{' + _.map(props, function (val, key) { + return JSON.stringify(key) + ' : ' + val; + }).join(',') + '}'; } function convertTagNameToConstructor(tagName) { - return React.DOM.hasOwnProperty(tagName) ? "React.DOM." + tagName : tagName; + return React.DOM.hasOwnProperty(tagName) ? 'React.DOM.' + tagName : tagName; } function defaultContext() { @@ -153,7 +153,7 @@ function addIfNotThere(array, obj) { } function convertHtmlToReact(node, context) { - if (node.type === "tag") { + if (node.type === 'tag') { context = { boundParams: _.clone(context.boundParams), injectedFunctions: context.injectedFunctions @@ -161,10 +161,10 @@ function convertHtmlToReact(node, context) { var data = {name: convertTagNameToConstructor(node.name)}; if (node.attribs[templateProp]) { - data.item = node.attribs[templateProp].split(" in ")[0].trim(); - data.collection = node.attribs[templateProp].split(" in ")[1].trim(); + data.item = node.attribs[templateProp].split(' in ')[0].trim(); + data.collection = node.attribs[templateProp].split(' in ')[1].trim(); addIfNotThere(context.boundParams, data.item); - addIfNotThere(context.boundParams, data.item + "Index"); + addIfNotThere(context.boundParams, data.item + 'Index'); } data.props = generateProps(node, context); if (node.attribs[ifProp]) { @@ -183,13 +183,13 @@ function convertHtmlToReact(node, context) { data.body = ifTemplate(data); } return data.body; - } else if (node.type === "comment") { + } else if (node.type === 'comment') { return (commentTemplate(node)); - } else if (node.type === "text") { + } else if (node.type === 'text') { if (node.data.trim()) { return convertText(node.data.trim()); } - return ""; + return ''; } } @@ -201,31 +201,37 @@ function extractDefinesFromJSXTag(html, defines) { reqStr = reqStr.replace(/\s*(\w+)\s*\=\s*\"([^\"]*)\"\s*/, function(full, varName, reqPath) { defines[reqPath] = varName; match = true; - return ""; + return ''; }); } - return ""; + return ''; }); return html; } +/** + * @param {string} html + * @return {string} + */ function convertTemplateToReact(html) { - var defines = {react: "React", lodash: "_"}; +// var x = cheerio.load(html); + var defines = {react: 'React', lodash: '_'}; html = extractDefinesFromJSXTag(html, defines); var rootNode = cheerio.load(html.trim(), {lowerCaseTags: false, lowerCaseAttributeNames: false, xmlMode: true}); var context = defaultContext(); var body = convertHtmlToReact(rootNode.root()[0].children[0], context); - var requirePaths = _(defines).keys().map(function (reqName) {return '"' + reqName + '"';}).value().join(","); - var requireVars = _(defines).values().value().join(","); - var data = {body: body, injectedFunctions: "", requireNames: requireVars, requirePaths: requirePaths}; - data.injectedFunctions = context.injectedFunctions.join("\n"); + var requirePaths = _(defines).keys().map(function (reqName) { return '"' + reqName + '"'; }).value().join(','); + var requireVars = _(defines).values().value().join(','); + var data = {body: body, injectedFunctions: '', requireNames: requireVars, requirePaths: requirePaths}; + data.injectedFunctions = context.injectedFunctions.join('\n'); var code = templateTemplate(data); try { var tree = esprima.parse(code, {range: true, tokens: true, comment: true}); tree = escodegen.attachComments(tree, tree.comments, tree.tokens); code = escodegen.generate(tree, {comment: true}); } catch (e) { - + // TODO error handling + console.log(e); } return code; } @@ -241,8 +247,7 @@ function convertFile(source, target) { // } var html = fs.readFileSync(source).toString(); if (!html.match(/\<\!doctype jsx/i)) { - console.log('invalid file, missing header'); - return; + throw new Error('invalid file, missing header'); } var js = convertTemplateToReact(html); fs.writeFileSync(target, js);