diff --git a/.eslintignore b/.eslintignore index 273db75..17e1b35 100644 --- a/.eslintignore +++ b/.eslintignore @@ -16,7 +16,8 @@ web/** playground/sample/** test/data/** +dist internalTasks/release.js playground.config.js -sample/InfiniteScroll.js \ No newline at end of file +sample/InfiniteScroll.js diff --git a/.eslintrc b/.eslintrc index 870f9ce..044d4fa 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,18 +1,24 @@ { - "extends": "wix-editor", + "extends": "wix-editor/node", "plugins": ["lodash3", "wix-editor"], "rules": { - "strict": [2, "global"], - "no-restricted-syntax": [2, "WithStatement"], + "no-var": 0, + "object-shorthand": 0, + "prefer-arrow-callback": 0, + "prefer-const": 0, + "prefer-spread": 0, + "prefer-template": 0, + + "no-restricted-syntax": [2, "WithStatement", "ContinueStatement", "ForStatement"], "no-negated-condition": 1, "lodash3/prop-shorthand": 2, - "lodash3/matches-shorthand": 2, + "lodash3/matches-shorthand": [2, 3], "lodash3/matches-prop-shorthand": 2, "lodash3/prefer-chain": 2, "lodash3/preferred-alias": 2, "lodash3/no-single-chain": 2, - "lodash3/prefer-reject": 2, - "lodash3/prefer-filter": 2, + "lodash3/prefer-reject": [2, 3], + "lodash3/prefer-filter": [2, 3], "lodash3/no-unnecessary-bind": 2, "lodash3/unwrap": 2, "lodash3/prefer-compact": 2, @@ -21,6 +27,7 @@ "lodash3/prefer-wrapper-method": 2, "lodash3/prefer-invoke": 2, "lodash3/prefer-thru": 2, + "lodash3/prefer-lodash-chain": 2, "lodash3/prefer-lodash-method": 0, "lodash3/prefer-lodash-typecheck": 2, "lodash3/no-commit": 2, @@ -36,13 +43,5 @@ "wix-editor/prefer-ternary": 1, "wix-editor/return-boolean": 1, "wix-editor/simplify-boolean-expression": 1 - }, - "env": { - "browser": false, - "node": true, - "amd": true - }, - "globals": { - "requirejs": true } } diff --git a/.gitignore b/.gitignore index 2db59f3..12602a2 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ npm-debug.log ### bower ### /bower_components/* +### babel ### +/dist + /web /target /coverage diff --git a/bin/rt.js b/bin/rt.js index 397103c..788e66e 100755 --- a/bin/rt.js +++ b/bin/rt.js @@ -1,6 +1,6 @@ #!/usr/bin/env node 'use strict'; -var cli = require('../src/cli'); +var cli = require('../dist/cli'); var exitCode = cli.execute(process.argv); /*eslint no-process-exit:0*/ -process.exit(exitCode); \ No newline at end of file +process.exit(exitCode); diff --git a/package.json b/package.json index 1062de8..d102b63 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "react-templates", "version": "0.3.2", "description": "Light weight templates for react -> write html get valid react code", - "main": "./src/cli.js", + "main": "./dist/cli.js", "bin": { "rt": "./bin/rt.js" }, @@ -15,7 +15,8 @@ "minor": "npm version minor -m\"update version to %s\" && git push && git push --tags", "major": "npm version major -m\"update version to %s\" && git push && git push --tags", "buildwp": "webpack --config webpack-production.config.js --progress --profile --colors", - "babel": "rm -rf dist; babel src/ --out-dir dist/" + "babel": "rm -rf dist; babel src/ --out-dir dist/", + "all": "npm run lint && npm run test && npm run babel" }, "repository": { "type": "git", @@ -31,20 +32,21 @@ "chalk": "^1.1.1", "cheerio": "^0.19.0", "css": "^2.2.1", - "escodegen": "^1.7.0", + "escodegen": "1.7.1", "esprima-fb": "^15001.1001.0-dev-harmony-fb", "lodash": "^3.10.1", - "optionator": "^0.6.0", + "optionator": "0.7.1", "text-table": "^0.2.0" }, "devDependencies": { - "brace": "^0.5.1", + "babel-cli": "^6.3.17", + "brace": "0.7.0", "brfs": "^1.4.1", - "coveralls": "^2.11.4", - "eslint": "^1.10.1", + "coveralls": "2.11.6", + "eslint": "1.10.3", "eslint-config-wix-editor": "^0.1.1", - "eslint-plugin-lodash3": "^0.2.1", - "eslint-plugin-react": "^3.10.0", + "eslint-plugin-lodash3": "0.4.0", + "eslint-plugin-react": "3.11.3", "eslint-plugin-wix-editor": "^1.0.1", "grunt": "^0.4.5", "grunt-browserify": "^4.0.0", @@ -53,7 +55,7 @@ "grunt-contrib-watch": "^0.6.1", "grunt-eslint": "^17.3.1", "grunt-tape": "0.0.2", - "istanbul": "^0.4.0", + "istanbul": "0.4.1", "react": "^0.14.2", "react-dom": "^0.14.2", "react-native": "^0.11.4", diff --git a/playground/CMLint.js b/playground/CMLint.js index 80d8442..c8118fc 100644 --- a/playground/CMLint.js +++ b/playground/CMLint.js @@ -95,7 +95,7 @@ define(['react', 'lodash', 'jquery', './libs/codemirror-4.8/lib/codemirror'], fu } var poll = setInterval(function () { if (tooltip) { - for (var n = node; ; n = n.parentNode) { + for (var n = node; ; n = n.parentNode) { //eslint-disable-line no-restricted-syntax if (n === document.body) { return undefined; } @@ -114,4 +114,4 @@ define(['react', 'lodash', 'jquery', './libs/codemirror-4.8/lib/codemirror'], fu annotate: annotate, clearMarks: clearMarks }; -}); \ No newline at end of file +}); diff --git a/src/RTCodeError.js b/src/RTCodeError.js index 4371bdb..5433e7c 100644 --- a/src/RTCodeError.js +++ b/src/RTCodeError.js @@ -45,8 +45,8 @@ function getLine(html, node) { // Redefine properties on Error to be enumerable /*eslint no-extend-native:0*/ -Object.defineProperty(Error.prototype, 'message', {configurable: true, enumerable: true}); -Object.defineProperty(Error.prototype, 'stack', {configurable: true, enumerable: true}); +//Object.defineProperty(Error.prototype, 'message', {configurable: true, enumerable: true}); +//Object.defineProperty(Error.prototype, 'stack', {configurable: true, enumerable: true}); //Object.defineProperty(Error.prototype, 'line', { configurable: true, enumerable: true }); /** @@ -57,31 +57,42 @@ Object.defineProperty(Error.prototype, 'stack', {configurable: true, enumerable: * @param {number=} column * @constructor */ -function RTCodeError(message, startOffset, endOffset, line, column) { - Error.captureStackTrace(this, RTCodeError); - this.name = 'RTCodeError'; - this.message = message || ''; - this.index = norm(startOffset); - this.startOffset = norm(startOffset); - this.endOffset = norm(endOffset); - this.line = norm(line); - this.column = norm(column); +class RTCodeError extends Error { + constructor(message, startOffset, endOffset, line, column) { + super(); + Error.captureStackTrace(this, RTCodeError); + this.name = 'RTCodeError'; + this.message = message || ''; + this.index = norm(startOffset); + this.startOffset = norm(startOffset); + this.endOffset = norm(endOffset); + this.line = norm(line); + this.column = norm(column); + } + //build buildError } function norm(n) { return n === undefined ? -1 : n; } -RTCodeError.prototype = Object.create(Error.prototype); +//const norm = n => n === undefined ? -1 : n; +/** + * @type {buildError} + */ RTCodeError.build = buildError; RTCodeError.norm = norm; -RTCodeError.prototype.toIssue = function () { -}; - +/** + * @param {*} context + * @param {*} node + * @param {string} msg + * @param args + * @return {RTCodeError} + */ function buildFormat(context, node, msg, args) { - return buildError(util.format.apply(this, [msg].concat(args)), context, node); + return buildError(context, node, util.format.apply(this, [msg].concat(args))); } /** @@ -94,12 +105,12 @@ function buildFormat(context, node, msg, args) { RTCodeError.buildFormat = _.restParam(buildFormat, 3); /** - * @param {string} msg * @param {*} context * @param {*} node + * @param {string} msg * @return {RTCodeError} */ -function buildError(msg, context, node) { +function buildError(context, node, msg) { var loc = getNodeLoc(context, node); return new RTCodeError(msg, loc.start, loc.end, loc.pos.line, loc.pos.col); } diff --git a/src/api.js b/src/api.js index dbbf036..6baa75e 100644 --- a/src/api.js +++ b/src/api.js @@ -5,13 +5,14 @@ var path = require('path'); var util = require('util'); var chalk = require('chalk'); var reactTemplates = require('./reactTemplates'); +var fsUtil = require('./fsUtil'); var convertRT = reactTemplates.convertRT; var convertJSRTToJS = reactTemplates.convertJSRTToJS; /** * @param {string} source * @param {string} target - * @param {{modules:string, dryRun:boolean}?} options + * @param {Options} options * @param {CONTEXT} context */ function convertFile(source, target, options, context) { @@ -21,10 +22,9 @@ function convertFile(source, target, options, context) { // } options = options || {}; options.fileName = source; - var fsUtil = require('./fsUtil'); if (!options.force && !fsUtil.isStale(source, target)) { - context.verbose(util.format('target file %s is up to date, skipping', chalk.cyan(target))); + context.verbose(`target file ${chalk.cyan(target)} is up to date, skipping`); return; } diff --git a/src/cli.js b/src/cli.js index f0ae0d7..1c058ef 100755 --- a/src/cli.js +++ b/src/cli.js @@ -11,13 +11,17 @@ var reactDOMSupport = require('./reactDOMSupport'); var reactTemplates = require('./reactTemplates'); var rtStyle = require('./rtStyle'); +/** + * @param {Options} currentOptions + * @return {number} + */ function executeOptions(currentOptions) { var ret = 0; var files = currentOptions._; context.options.format = currentOptions.format || 'stylish'; if (currentOptions.version) { - console.log('v' + pkg.version); + console.log(`v${pkg.version}`); } else if (currentOptions.help) { if (files.length) { console.log(options.generateHelpForOption(files[0])); @@ -37,6 +41,8 @@ function executeOptions(currentOptions) { function printVersions(currentOptions) { var ret = Object.keys(reactDOMSupport); + //const out = currentOptions.format === 'json' ? JSON.stringify(ret, undefined, 2) : ret.join(', '); + //console.log(out); if (currentOptions.format === 'json') { console.log(JSON.stringify(ret, undefined, 2)); } else { @@ -45,7 +51,7 @@ function printVersions(currentOptions) { } /** - * @param {*} currentOptions + * @param {Options} currentOptions * @param {string} filename file name to process */ function handleSingleFile(currentOptions, filename) { diff --git a/src/context.js b/src/context.js index 21cbe05..12aee63 100644 --- a/src/context.js +++ b/src/context.js @@ -1,6 +1,6 @@ 'use strict'; /** - * @typedef {{color: boolean, cwd: string, report: function(string), warn: function(string), getMessages: function():Array.}} CONTEXT + * @typedef {{color: boolean, cwd: string, report: function(string), issue: function(string, string,string,number,number,number=,number=), warn: function(string), verbose: function(string), getMessages: function():Array., options:Options, messages: Array.}} CONTEXT */ /** * @typedef {{msg: string, level: MESSAGE_LEVEL, file: string,line:number,column:number,startOffset:number,endOffset:number}} MESSAGE @@ -58,7 +58,7 @@ var context = { * @param {number=} endOffset */ issue: function (level, msg, file, line, column, startOffset, endOffset) { - context.messages.push({level: level, msg: msg, file: file || null, line: norm(line), column: norm(column), index: norm(startOffset), startOffset: norm(startOffset), endOffset: norm(endOffset)}); + context.messages.push({level, msg, file: file || null, line: norm(line), column: norm(column), index: norm(startOffset), startOffset: norm(startOffset), endOffset: norm(endOffset)}); }, getMessages: function () { return context.messages; diff --git a/src/options.js b/src/options.js index eb5452a..97cba99 100644 --- a/src/options.js +++ b/src/options.js @@ -19,13 +19,12 @@ var reactNativeSupport = require('./reactNativeSupport'); // exports 'parse(args)', 'generateHelp()', and 'generateHelpForOption(optionName)' module.exports = optionator({ - prepend: [ - pkg.name + ' v' + pkg.version, - pkg.description, - '', - 'Usage:', - '$ rt [ ...] []' - ].join('\n'), + prepend: +`${pkg.name} v${pkg.version} +${pkg.description} + +Usage: +$ rt [ ...] []`, concatRepeatedArrays: true, mergeRepeatedObjects: true, options: [{ @@ -76,7 +75,7 @@ module.exports = optionator({ alias: 't', type: 'String', default: reactDOMSupport.default, - description: 'React version to generate code for (' + Object.keys(reactDOMSupport).join(', ') + ')' + description: `'React version to generate code for (${Object.keys(reactDOMSupport).join(', ')})'` }, { option: 'list-target-version', type: 'Boolean', @@ -116,6 +115,6 @@ module.exports = optionator({ alias: 'rnv', type: 'String', default: reactNativeSupport.default, - description: 'React native version to generate code for (' + Object.keys(reactNativeSupport).join(', ') + ')' + description: `'React native version to generate code for (${Object.keys(reactNativeSupport).join(', ')})'` }] }); diff --git a/src/reactSupport.js b/src/reactSupport.js index 7656f36..b607be7 100644 --- a/src/reactSupport.js +++ b/src/reactSupport.js @@ -38,10 +38,11 @@ var htmlSelfClosingTags = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr' var templateAMDTemplate = _.template("define(<%= name ? '\"'+name + '\", ' : '' %>[<%= requirePaths %>], function (<%= requireNames %>) {\n'use strict';\n <%= injectedFunctions %>\nreturn function(){ return <%= body %>};\n});"); var templateCommonJSTemplate = _.template("'use strict';\n<%= vars %>\n\n<%= injectedFunctions %>\nmodule.exports = function(){ return <%= body %>};\n"); var templateES6Template = _.template('<%= vars %>\n\n<%= injectedFunctions %>\nexport default function(){ return <%= body %>}\n'); -var templatePJSTemplate = _.template('var <%= name %> = function () {\n' + - '<%= injectedFunctions %>\n' + - 'return <%= body %>\n' + - '};\n'); +var templatePJSTemplate = _.template(`var <%= name %> = function () { +<%= injectedFunctions %> +return <%= body %> +}; +`); var templateTypescriptTemplate = _.template('<%= vars %>\n\n<%= injectedFunctions %>\nvar fn = function() { return <%= body %> };\nexport = fn\n'); var templateJSRTTemplate = _.template('(function () {\n <%= injectedFunctions %>\n return function(){\nreturn <%= body %>}}\n)()'); @@ -60,4 +61,4 @@ module.exports = { classNameProp: classNameProp, shouldUseCreateElement: shouldUseCreateElement, templates: templates -}; \ No newline at end of file +}; diff --git a/src/reactTemplates.js b/src/reactTemplates.js index df58b8e..fea65f4 100644 --- a/src/reactTemplates.js +++ b/src/reactTemplates.js @@ -20,18 +20,17 @@ var ifTemplate = _.template('((<%= condition %>)?(<%= body %>):null)'); var propsTemplateSimple = _.template('_.assign({}, <%= generatedProps %>, <%= rtProps %>)'); var propsTemplate = _.template('mergeProps( <%= generatedProps %>, <%= rtProps %>)'); -var propsMergeFunction = [ - 'function mergeProps(inline,external) {', - ' var res = _.assign({},inline,external)', - 'if (inline.hasOwnProperty(\'style\')) {', - ' res.style = _.defaults(res.style, inline.style);', - '}', - ' if (inline.hasOwnProperty(\'className\') && external.hasOwnProperty(\'className\')) {', - ' res.className = external.className + \' \' + inline.className;', - '} return res;', - '}', - '' -].join('\n'); +const propsMergeFunction = `function mergeProps(inline,external) { + var res = _.assign({},inline,external) + if (inline.hasOwnProperty('style')) { + res.style = _.defaults(res.style, inline.style); + } + if (inline.hasOwnProperty('className') && external.hasOwnProperty('className')) { + res.className = external.className + ' ' + inline.className; + } + return res; +} +` var classSetTemplate = _.template('_.keys(_.pick(<%= classSet %>, _.identity)).join(" ")'); @@ -53,6 +52,10 @@ var scopeAttr = 'rt-scope'; var propsAttr = 'rt-props'; var templateNode = 'rt-template'; +/** + * @param {Options} options + * @return {Options} + */ function getOptions(options) { options = options || {}; var defaultOptions = { @@ -91,16 +94,19 @@ function reactImport(options) { /** * @const */ -var curlyMap = {'{': 1, '}': -1}; +const curlyMap = {'{': 1, '}': -1}; /** * @typedef {{boundParams: Array., injectedFunctions: Array., html: string, options: *}} Context */ +/** + * @typedef {{fileName:string,force:boolean,modules:string,defines:*,reactImportPath:string=,lodashImportPath:string=,flow:boolean,name:string,native:boolean,propTemplates:*,format:string,_:*,version:boolean,help:boolean,listTargetVersion:boolean,modules:string, dryRun:boolean}} Options + */ /** * @param node - * @param context + * @param {Context} context * @param {string} txt * @return {string} */ @@ -117,7 +123,7 @@ function convertText(node, context, txt) { } var curlyCounter = 1; var end; - for (end = start + 1; end < txt.length && curlyCounter > 0; end++) { + for (end = start + 1; end < txt.length && curlyCounter > 0; end++) { //eslint-disable-line no-restricted-syntax curlyCounter += curlyMap[txt.charAt(end)] || 0; } if (curlyCounter === 0) { @@ -126,7 +132,7 @@ function convertText(node, context, txt) { first = false; txt = txt.substr(end); } else { - throw RTCodeError.buildFormat(context, node, "Failed to parse text '%s'", txt); + throw RTCodeError.build(context, node, `Failed to parse text '${txt}'`); } } if (txt) { @@ -135,7 +141,6 @@ function convertText(node, context, txt) { if (res === '') { res = 'true'; } - return res; } @@ -148,10 +153,13 @@ function convertText(node, context, txt) { */ function generateInjectedFunc(context, namePrefix, body, params) { params = params || context.boundParams; - var generatedFuncName = namePrefix.replace(',', '') + (context.injectedFunctions.length + 1); - var funcText = util.format('function %s(%s) {\n%s\n}\n', generatedFuncName, params.join(','), body); + var funcName = namePrefix.replace(',', '') + (context.injectedFunctions.length + 1); + var funcText = `function ${funcName}(${params.join(',')}) { + ${body} + } + `; context.injectedFunctions.push(funcText); - return generatedFuncName; + return funcName; } function generateTemplateProps(node, context) { @@ -161,7 +169,7 @@ function generateTemplateProps(node, context) { var templateProp = null; if (child.name === templateNode) { // Generic explicit template tag if (!_.has(child.attribs, 'prop')) { - throw RTCodeError.build('rt-template must have a prop attribute', context, child); + throw RTCodeError.build(context, child, 'rt-template must have a prop attribute'); } var childTemplate = _.find(context.options.propTemplates, {prop: child.attribs.prop}) || {arguments: []}; @@ -212,7 +220,7 @@ function generateProps(node, context) { _.forOwn(node.attribs, function (val, key) { var propKey = reactSupport.attributesMapping[key.toLowerCase()] || key; if (props.hasOwnProperty(propKey) && propKey !== reactSupport.classNameProp) { - throw RTCodeError.buildFormat(context, node, 'duplicate definition of %s %s', propKey, JSON.stringify(node.attribs)); + throw RTCodeError.build(context, node, `duplicate definition of ${propKey} ${JSON.stringify(node.attribs)}`); } if (key.indexOf('on') === 0 && !utils.isStringOnlyCode(val)) { props[propKey] = handleEventHandler(val, context, node, key); @@ -222,7 +230,7 @@ function generateProps(node, context) { // Processing for both class and rt-class conveniently return strings that // represent JS expressions, each evaluating to a space-separated set of class names. // We can just join them with another space here. - var existing = props[propKey] ? props[propKey] + ' + " " + ' : ''; + var existing = props[propKey] ? `${props[propKey]} + " " + ` : ''; if (key === classSetAttr) { props[propKey] = existing + classSetTemplate({classSet: val}); } else if (key === classAttr || key === reactSupport.classNameProp) { @@ -234,15 +242,14 @@ function generateProps(node, context) { }); _.assign(props, generateTemplateProps(node, context)); - return '{' + _.map(props, function (val, key) { - return JSON.stringify(key) + ' : ' + val; - }).join(',') + '}'; + const propStr = _.map(props, (v, k) => `${JSON.stringify(k)} : ${v}`).join(','); + return `{${propStr}}`; } function handleEventHandler(val, context, node, key) { var funcParts = val.split('=>'); if (funcParts.length !== 2) { - throw RTCodeError.buildFormat(context, node, "when using 'on' events, use lambda '(p1,p2)=>body' notation or use {} to return a callback function. error: [%s='%s']", key, val); + throw RTCodeError.build(context, node, `when using 'on' events, use lambda '(p1,p2)=>body' notation or use {} to return a callback function. error: [${key}='${val}']`); } var evtParams = funcParts[0].replace('(', '').replace(')', '').trim(); var funcBody = funcParts[1].trim(); @@ -259,21 +266,19 @@ function genBind(func, args) { } function handleStyleProp(val, node, context) { - 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(':'); - res[0] = res[0].trim(); - res[1] = res.slice(1).join(':').trim(); - return res; - })); - var styleArray = _.map(styleParts, function (stylePart) { - return stringUtils.convertToCamelCase(stylePart[0]) + ' : ' + convertText(node, context, stylePart[1].trim()); - }); - return '{' + styleArray.join(',') + '}'; + const styleStr = _(val) + .split(';') + .map(_.trim) + .filter(i => _.includes(i, ':')) + .map(i => { + const pair = i.split(':'); + //const val = pair[1]; + const val = pair.slice(1).join(':').trim(); + return _.camelCase(pair[0].trim()) + ' : ' + convertText(node, context, val.trim()) + //return stringUtils.convertToCamelCase(pair[0].trim()) + ' : ' + convertText(node, context, val.trim()) + }) + .join(','); + return `{${styleStr}}`; } /** @@ -288,7 +293,7 @@ function convertTagNameToConstructor(tagName, context) { var isHtmlTag = _.includes(reactDOMSupport[context.options.targetVersion], tagName); if (reactSupport.shouldUseCreateElement(context)) { isHtmlTag = isHtmlTag || tagName.match(/^\w+(-\w+)$/); - return isHtmlTag ? "'" + tagName + "'" : tagName; + return isHtmlTag ? `'${tagName}'` : tagName; } return isHtmlTag ? 'React.DOM.' + tagName : tagName; } @@ -312,9 +317,7 @@ function defaultContext(html, options) { * @return {boolean} */ function hasNonSimpleChildren(node) { - return _.some(node.children, function (child) { - return child.type === 'tag' && child.attribs[repeatAttr]; - }); + return _.some(node.children, child => child.type === 'tag' && child.attribs[repeatAttr]); } /** @@ -338,14 +341,14 @@ function convertHtmlToReact(node, context) { if (node.attribs[repeatAttr]) { var arr = node.attribs[repeatAttr].split(' in '); if (arr.length !== 2) { - throw RTCodeError.buildFormat(context, node, "rt-repeat invalid 'in' expression '%s'", node.attribs[repeatAttr]); + throw RTCodeError.build(context, node, `rt-repeat invalid 'in' expression '${node.attribs[repeatAttr]}'`); } data.item = arr[0].trim(); data.collection = arr[1].trim(); validateJS(data.item, node, context); validateJS(data.collection, node, context); stringUtils.addIfMissing(context.boundParams, data.item); - stringUtils.addIfMissing(context.boundParams, data.item + 'Index'); + stringUtils.addIfMissing(context.boundParams, `${data.item}Index`); } if (node.attribs[scopeAttr]) { @@ -380,18 +383,16 @@ function convertHtmlToReact(node, context) { data.body = _.template(getTagTemplateString(!hasNonSimpleChildren(node), reactSupport.shouldUseCreateElement(context)))(data); if (node.attribs[scopeAttr]) { - var functionBody = _.values(data.innerScope.innerMapping).join('\n') + 'return ' + data.body; + var functionBody = _.values(data.innerScope.innerMapping).join('\n') + `return ${data.body}`; var generatedFuncName = generateInjectedFunc(context, 'scope' + data.innerScope.scopeName, functionBody, _.keys(data.innerScope.outerMapping)); - data.body = util.format('%s.apply(this, [%s])', generatedFuncName, _.values(data.innerScope.outerMapping).join(',')); + data.body = `${generatedFuncName}.apply(this, [${_.values(data.innerScope.outerMapping).join(',')}])`; } // Order matters here. Each rt-repeat iteration wraps over the rt-scope, so // the scope variables are evaluated in context of the current iteration. if (node.attribs[repeatAttr]) { - data.repeatFunction = generateInjectedFunc(context, 'repeat' + stringUtils.capitalize(data.item), 'return ' + data.body); - data.repeatBinds = ['this'].concat(_.reject(context.boundParams, function (param) { - return param === data.item || param === data.item + 'Index' || data.innerScope && param in data.innerScope.innerMapping; - })); + data.repeatFunction = generateInjectedFunc(context, 'repeat' + _.capitalize(data.item), 'return ' + data.body); + data.repeatBinds = ['this'].concat(_.reject(context.boundParams, p => p === data.item || p === data.item + 'Index' || data.innerScope && p in data.innerScope.innerMapping)); data.body = repeatTemplate(data); } if (node.attribs[ifAttr]) { @@ -401,10 +402,7 @@ function convertHtmlToReact(node, context) { } else if (node.type === 'comment') { return commentTemplate(node); } else if (node.type === 'text') { - if (node.data.trim()) { - return convertText(node, context, node.data); - } - return ''; + return node.data.trim() ? convertText(node, context, node.data) : ''; } } @@ -417,10 +415,10 @@ function handleScopeAttribute(node, context, data) { data.innerScope.outerMapping = _.zipObject(context.boundParams, context.boundParams); - _(node.attribs[scopeAttr]).split(';').invoke('trim').compact().forEach(function (scopePart) { + _(node.attribs[scopeAttr]).split(';').invoke('trim').compact().forEach(scopePart => { //eslint-disable-line lodash3/collection-return var scopeSubParts = _(scopePart).split(' as ').invoke('trim').value(); if (scopeSubParts.length < 2) { - throw RTCodeError.buildFormat(context, node, "invalid scope part '%s'", scopePart); + throw RTCodeError.build(context, node, `invalid scope part '${scopePart}'`); } var alias = scopeSubParts[1]; var value = scopeSubParts[0]; @@ -431,8 +429,8 @@ function handleScopeAttribute(node, context, data) { // function call, as with the ones we generate for rt-scope. stringUtils.addIfMissing(context.boundParams, alias); - data.innerScope.scopeName += stringUtils.capitalize(alias); - data.innerScope.innerMapping[alias] = 'var ' + alias + ' = ' + value + ';'; + data.innerScope.scopeName += _.capitalize(alias); + data.innerScope.innerMapping[alias] = `var ${alias} = ${value};`; validateJS(data.innerScope.innerMapping[alias], node, context); }).value(); } @@ -445,8 +443,7 @@ function validateIfAttribute(node, context, data) { } catch (e) { throw new RTCodeError(e.message, e.index, -1); } - if (ifAttributeTree && ifAttributeTree.body && ifAttributeTree.body.length === 1 && - ifAttributeTree.body[0].type === 'ExpressionStatement') { + if (ifAttributeTree && ifAttributeTree.body && ifAttributeTree.body.length === 1 && ifAttributeTree.body[0].type === 'ExpressionStatement') { // make sure that rt-if does not use an inner mapping if (ifAttributeTree.body[0].expression && utils.usesScopeName(innerMappingKeys, ifAttributeTree.body[0].expression)) { throw RTCodeError.buildFormat(context, node, "invalid scope mapping used in if part '%s'", node.attribs[ifAttr]); @@ -471,9 +468,7 @@ function handleSelfClosingHtmlTags(nodes) { node.children = handleSelfClosingHtmlTags(node.children); if (node.type === 'tag' && _.includes(reactSupport.htmlSelfClosingTags, node.name)) { externalNodes = _.filter(node.children, isTag); - _.forEach(externalNodes, function (child) { - child.parent = node; - }); + _.forEach(externalNodes, i => i.parent = node); node.children = _.reject(node.children, isTag); } return [node].concat(externalNodes); @@ -490,7 +485,7 @@ function convertTemplateToReact(html, options) { /** * @param {string} html * @param {CONTEXT} reportContext - * @param {{modules:string,defines:*}?} options + * @param {Options?} options * @return {string} */ function convertRT(html, reportContext, options) { @@ -511,46 +506,39 @@ function convertRT(html, reportContext, options) { throw new RTCodeError('Document should have a root element'); } var firstTag = null; - _.forEach(rootTags, function (tag) { + _.forEach(rootTags, function (tag) { //eslint-disable-line lodash3/collection-return if (tag.name === 'rt-require') { if (!tag.attribs.dependency || !tag.attribs.as) { - throw RTCodeError.build("rt-require needs 'dependency' and 'as' attributes", context, tag); + throw RTCodeError.build(context, tag, "rt-require needs 'dependency' and 'as' attributes"); } else if (tag.children.length) { - throw RTCodeError.build('rt-require may have no children', context, tag); + throw RTCodeError.build(context, tag, 'rt-require may have no children'); } defines[tag.attribs.dependency] = tag.attribs.as; } else if (firstTag === null) { firstTag = tag; } else { - throw RTCodeError.build('Document should have no more than a single root element', context, tag); + throw RTCodeError.build(context, tag, 'Document should have no more than a single root element'); } }); if (firstTag === null) { - throw RTCodeError.build('Document should have a single root element', context, rootNode.root()[0]); + throw RTCodeError.build(context, rootNode.root()[0], 'Document should have a single root element'); } var body = convertHtmlToReact(firstTag, context); var requirePaths = _(defines) .keys() - .map(function (reqName) { return '"' + reqName + '"'; }) + .map(def => `"${def}"`) .join(','); - var requireVars = _.values(defines).join(','); - var buildImportString; + var buildImport; if (options.modules === 'typescript') { - buildImportString = "import %s = require('%s');"; + buildImport = (v, p) => `import ${v} = require('${p}');`; } else if (options.modules === 'es6') { // eslint-disable-line - buildImportString = "import %s from '%s';"; + buildImport = (v, p) => `import ${v} from '${p}';` } else { - buildImportString = "var %s = require('%s');"; + buildImport = (v, p) => `var ${v} = require('${p}');` } - var vars = _(defines).map(function (reqVar, reqPath) { - return util.format(buildImportString, reqVar, reqPath); - }).join('\n'); - - if (options.flow) { - vars = '/* @flow */\n' + vars; - } - var data = {body: body, injectedFunctions: '', requireNames: requireVars, requirePaths: requirePaths, vars: vars, name: options.name}; - data.injectedFunctions = context.injectedFunctions.join('\n'); + const header = options.flow ? '/* @flow */\n' : ''; + const vars = header + _(defines).map(buildImport).join('\n'); + var data = {body, injectedFunctions: context.injectedFunctions.join('\n'), requireNames: _.values(defines).join(','), requirePaths, vars, name: options.name}; var code = generate(data, options); if (options.modules !== 'typescript' && options.modules !== 'jsrt') { code = parseJS(code); @@ -572,9 +560,7 @@ function convertJSRTToJS(text, reportContext, options) { options = getOptions(options); options.modules = 'jsrt'; var templateMatcherJSRT = /