diff --git a/bin/rt.js b/bin/rt.js new file mode 100755 index 0000000..397103c --- /dev/null +++ b/bin/rt.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node +'use strict'; +var cli = require('../src/cli'); +var exitCode = cli.execute(process.argv); +/*eslint no-process-exit:0*/ +process.exit(exitCode); \ No newline at end of file diff --git a/playground/playground.rt.js b/playground/playground.rt.js index 45d3009..61cbace 100644 --- a/playground/playground.rt.js +++ b/playground/playground.rt.js @@ -15,7 +15,7 @@ module.exports = function () { return React.DOM.div({}, React.DOM.div({ 'id': 'myTab', 'role': 'tabpanel', - 'className': 'code-area ' + (this.props.direction || 'vertical') + 'className': 'code-area ' + (this.props.direction === 'horizontal' && 'horizontal' || 'vertical') } /* Nav tabs */, React.DOM.ul({ 'className': 'nav nav-pills', 'role': 'tablist' @@ -69,7 +69,7 @@ module.exports = function () { 'readOnly': true })))), React.DOM.div({ 'key': 'result-area', - 'className': 'result-area well ' + (this.props.direction || 'vertical'), + 'className': 'result-area well ' + (this.props.direction === 'horizontal' && 'horizontal' || 'vertical'), 'style': { marginTop: '48px' } }, React.DOM.h2({}, 'Preview:'), React.DOM.form({ 'className': 'sample-view', diff --git a/src/api.js b/src/api.js index a48f386..f4ff5e8 100644 --- a/src/api.js +++ b/src/api.js @@ -9,8 +9,9 @@ var convertTemplateToReact = reactTemplates.convertTemplateToReact; * @param {string} source * @param {{commonJS:boolean}?} options * @param {string} target + * @param {CONTEXT} context */ -function convertFile(source, target, options) { +function convertFile(source, target, options, context) { // if (path.extname(filename) !== ".html") { // console.log('invalid file, only handle html files'); // return;// only handle html files @@ -19,7 +20,7 @@ function convertFile(source, target, options) { var fsUtil = require('./fsUtil'); if (!options.force && !fsUtil.isStale(source, target)) { - console.log('target file ' + chalk.cyan(target) + ' is up to date, skipping'); + context.info('target file ' + chalk.cyan(target) + ' is up to date, skipping'); return; } @@ -34,5 +35,6 @@ function convertFile(source, target, options) { module.exports = { // convertTemplateToReact: convertTemplateToReact, convertFile: convertFile, + context: require('./context'), _test: {} }; \ No newline at end of file diff --git a/src/cli.js b/src/cli.js index 7c1d98e..a546a75 100755 --- a/src/cli.js +++ b/src/cli.js @@ -12,26 +12,63 @@ var shell = require('./shell'); var pkg = require('../package.json'); var options = {commonJS: false, force: false, json: false}; -if (process.argv.length > 2) { - var files = []; - _.forEach(process.argv.slice(2),function (param) { +//if (process.argv.length > 2) { +// var files = []; +// _.forEach(process.argv.slice(2),function (param) { +// if (param === '-v' || param === '--version') { +// console.log(pkg.version); +// } else if (param === '-h' || param === '--help') { +// printHelp(); +// } else if (param === '-c' || param === '--common') { +// options.commonJS = true; +// } else if (param === '-f' || param === '--force') { +// options.force = true; +// } else if (param === '-j' || param === '--json') { // TODO use optionator +// context.options.format = 'json'; +// } else if (param !== '--no-color') { +// files.push(param); +// } +// }); +// _.forEach(files, handleSingleFile); +// shell.printResults(context); +//} else { +// printHelp(); +//} + +function parseOptions(args) { + var parsedOptions = {_: [], version: false, commonJS: false, force: false, format: 'stylish'}; + _.forEach(args, function (param) { if (param === '-v' || param === '--version') { - console.log(pkg.version); + parsedOptions.version = true; } else if (param === '-h' || param === '--help') { printHelp(); } else if (param === '-c' || param === '--common') { - options.commonJS = true; + parsedOptions.commonJS = true; } else if (param === '-f' || param === '--force') { - options.force = true; + parsedOptions.force = true; } else if (param === '-j' || param === '--json') { // TODO use optionator + parsedOptions.format = 'json'; context.options.format = 'json'; - } else { - files.push(param); + } else if (param !== '--no-color') { + parsedOptions._.push(param); } }); - _.forEach(files, handleSingleFile); -} else { - printHelp(); + return parsedOptions; +} + +function executeOptions(currentOptions) { + var ret = 0; + if (currentOptions.help) { + printHelp(); + } else if (currentOptions.version) { + console.log(pkg.version); + } else { + _.forEach(currentOptions._, function (f) { + handleSingleFile(f, currentOptions); + }); + ret = shell.printResults(context); + } + return ret; } function printHelp() { @@ -47,11 +84,12 @@ function printHelp() { console.log(' -j, --json Report output format. [stylish,json]'); // console.log(' -ft, --format Report output format. [stylish,json]'); console.log(' --common Use Common JS output. default: false'); + console.log(' -f --force Force creation of output. default: false'); } -function handleSingleFile(filename) { +function handleSingleFile(filename, currentOptions) { if (path.extname(filename) !== '.rt') { - console.log('invalid file, only handle rt files'); + context.error('invalid file, only handle rt files', filename); return;// only handle html files } // var html = fs.readFileSync(filename).toString(); @@ -62,7 +100,7 @@ function handleSingleFile(filename) { // var js = reactTemplates.convertTemplateToReact(html); // fs.writeFileSync(filename + '.js', js); try { - api.convertFile(filename, filename + '.js', options); + api.convertFile(filename, filename + '.js', currentOptions, context); } catch (e) { context.error(e.message, filename, e.line || -1, -1, e.index || -1); // if (options.json) { @@ -74,6 +112,20 @@ function handleSingleFile(filename) { // if (options.stack) // console.log(e.stack); } +} - shell.printResults(context); -} \ No newline at end of file +/** + * Executes the CLI based on an array of arguments that is passed in. + * @param {string|Array|Object} args The arguments to process. + * @returns {int} The exit code for the operation. + */ +function execute(args) { + if (args.length > 2) { + var opts = parseOptions(args.slice(2)); + return executeOptions(opts); + } + printHelp(); + return 0; +} + +module.exports = {execute: execute, executeOptions: executeOptions}; \ No newline at end of file diff --git a/src/context.js b/src/context.js index 44362a5..dbe4cdf 100644 --- a/src/context.js +++ b/src/context.js @@ -49,10 +49,7 @@ var context = { context.messages = []; }, hasErrors: function () { - var firstError = _.find(context.messages, function(message) { - return message.level === MESSAGE_LEVEL.ERROR; - }); - return !!firstError; + return _.some(context.messages, {level: MESSAGE_LEVEL.ERROR}); }, options: { verbose: false, diff --git a/src/formatters/stylish.js b/src/formatters/stylish.js index 35e3702..1336bee 100644 --- a/src/formatters/stylish.js +++ b/src/formatters/stylish.js @@ -29,17 +29,22 @@ module.exports = function (warnings, config) { return n === 1 ? single : plural; } -// context.report(JSON.stringify(warnings, undefined, 2)); + function lineText(line) { + return line < 1 ? '' : line; + } + + // context.report(JSON.stringify(warnings, undefined, 2)); var output = table( warnings.map(function (message) { return [ '', -// message.line || 0, -// message.column || 0, + message.file, + lineText(message.line || 0), + lineText(message.column || 0), getMessageType(message), -// message.message.replace(/\.$/, ""), + // message.message.replace(/\.$/, ""), message.msg -// chalk.gray(message.ruleId) + // chalk.gray(message.ruleId) ]; }), { diff --git a/src/reactTemplates.js b/src/reactTemplates.js index 225806d..1749cbc 100644 --- a/src/reactTemplates.js +++ b/src/reactTemplates.js @@ -98,7 +98,11 @@ function generateInjectedFunc(context, namePrefix, body, params) { return generatedFuncName; } - +/** + * @param {string} html + * @param node + * @return {number} + */ function getLine(html, node) { if (!node) { return 0; @@ -360,5 +364,6 @@ function convertTemplateToReact(html, options) { module.exports = { convertTemplateToReact: convertTemplateToReact, + RTCodeError: RTCodeError, _test: {} }; diff --git a/test/data/invalid-scope.rt b/test/data/invalid-scope.rt new file mode 100644 index 0000000..7bce3e7 --- /dev/null +++ b/test/data/invalid-scope.rt @@ -0,0 +1,3 @@ +<!doctype rt> +<div rt-scope="a in a in a"> +</div \ No newline at end of file diff --git a/test/src/test.js b/test/src/test.js index 2a23719..de1f7d5 100644 --- a/test/src/test.js +++ b/test/src/test.js @@ -9,6 +9,36 @@ var cheerio = require('cheerio'); var dataPath = path.resolve(__dirname, '..', 'data'); +test('invalid tests', function (t) { + var files = [ + {file: 'invalid-scope.rt', issue: new reactTemplates.RTCodeError('invalid scope part \'a in a in a\'', 14, 1)} + ]; + t.plan(files.length); + + files.forEach(check); + + function check(testFile) { + var filename = path.join(dataPath, testFile.file); + var html = fs.readFileSync(filename).toString(); + var error = null; + try { + reactTemplates.convertTemplateToReact(html); + } catch (e) { + error = e; + } + t.deepEqual(errorEqual(error), errorEqual(testFile.issue), 'Expect convertTemplateToReact to throw an error'); + } +}); + +function errorEqual(err) { + return { + index: err.index, + line: err.line, + message: err.message, + name: err.name + }; +} + test('conversion test', function (t) { var files = ['div.rt', 'test.rt', 'repeat.rt']; t.plan(files.length); @@ -36,7 +66,7 @@ function normalizeHtml(html) { } test('html tests', function (t) { - var files = ['scope.rt', 'lambda.rt','eval.rt', 'props.rt']; + var files = ['scope.rt', 'lambda.rt', 'eval.rt', 'props.rt']; t.plan(files.length); files.forEach(check); @@ -49,10 +79,10 @@ test('html tests', function (t) { var code = reactTemplates.convertTemplateToReact(html).replace(/\r/g, ''); var defineMap = {react: React, lodash: _}; var define = function (requirementsNames, content) { - var requirements = _.map(requirementsNames,function (reqName) { + var requirements = _.map(requirementsNames, function (reqName) { return defineMap[reqName]; }); - return content.apply(this,requirements); + return content.apply(this, requirements); }; var comp = React.createFactory(React.createClass({ render: eval(code)