mirror of
https://github.com/bobwen-dev/react-templates
synced 2025-04-12 00:56:39 +02:00
better error reporting, added column and enfOffset, deprecated index
This commit is contained in:
parent
f187062ed1
commit
2c79edf519
97
src/RTCodeError.js
Normal file
97
src/RTCodeError.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} html
|
||||||
|
* @param node
|
||||||
|
* @return {{line: number, col: number}}}
|
||||||
|
*/
|
||||||
|
function getLine(html, node) {
|
||||||
|
if (!node) {
|
||||||
|
return {col: 1, line: 1};
|
||||||
|
}
|
||||||
|
var linesUntil = html.substring(0, node.startIndex).split('\n');
|
||||||
|
return {line: linesUntil.length, col: linesUntil[linesUntil.length - 1].length + 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
//function getLine(node) {
|
||||||
|
// if (!node) {
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
// var line = 0;
|
||||||
|
// var prev = node.prev;
|
||||||
|
// while (prev) {
|
||||||
|
// var nl = prev.data.split('\n').length - 1;
|
||||||
|
// line += nl;
|
||||||
|
// prev = prev.prev;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// line += getLine(node.parent);
|
||||||
|
// return line + 1;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//function RTCodeError(message, line) {
|
||||||
|
// this.name = 'RTCodeError';
|
||||||
|
// this.message = message || '';
|
||||||
|
// this.line = line || -1;
|
||||||
|
//}
|
||||||
|
//RTCodeError.prototype = Error.prototype;
|
||||||
|
|
||||||
|
// 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, 'line', { configurable: true, enumerable: true });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} message
|
||||||
|
* @param {number=} startOffset
|
||||||
|
* @param {number=} endOffset
|
||||||
|
* @param {number=} line
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
function norm(n) {
|
||||||
|
return n === undefined ? -1 : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTCodeError.prototype = Object.create(Error.prototype);
|
||||||
|
|
||||||
|
RTCodeError.build = buildError;
|
||||||
|
RTCodeError.norm = norm;
|
||||||
|
|
||||||
|
RTCodeError.prototype.toIssue = function () {
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} msg
|
||||||
|
* @param {*} context
|
||||||
|
* @param {*} node
|
||||||
|
* @return {RTCodeError}
|
||||||
|
*/
|
||||||
|
function buildError(msg, context, node) {
|
||||||
|
var pos = getLine(context.html, node);
|
||||||
|
var end;
|
||||||
|
if (node.data) {
|
||||||
|
end = node.startIndex + node.data.length;
|
||||||
|
} else if (node.next) {
|
||||||
|
end = node.next.startIndex;
|
||||||
|
} else {
|
||||||
|
end = context.html.length;
|
||||||
|
}
|
||||||
|
return new RTCodeError(msg, node.startIndex, end, pos.line, pos.col);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
RTCodeError: RTCodeError
|
||||||
|
};
|
17
src/cli.js
17
src/cli.js
@ -57,25 +57,10 @@ function handleSingleFile(currentOptions, filename) {
|
|||||||
context.error('invalid file, only handle rt files', filename);
|
context.error('invalid file, only handle rt files', filename);
|
||||||
return;// only handle html 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 js = reactTemplates.convertTemplateToReact(html);
|
|
||||||
// fs.writeFileSync(filename + '.js', js);
|
|
||||||
try {
|
try {
|
||||||
api.convertFile(filename, filename + '.js', currentOptions, context);
|
api.convertFile(filename, filename + '.js', currentOptions, context);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
context.error(e.message, filename, e.line || -1, -1, e.index || -1);
|
context.error(e.message, filename, e.line, e.column, e.startOffset, e.endOffset);
|
||||||
// if (defaultOptions.json) {
|
|
||||||
// context.error(e.message, filename, e.line || -1, -1, e.index || -1);
|
|
||||||
// console.log(JSON.stringify(context.getMessages(), undefined, 2));
|
|
||||||
// } else {
|
|
||||||
// console.log('Error processing file: ' + filename + ', ' + e.message + ' line: ' + e.line || -1);
|
|
||||||
// }
|
|
||||||
// if (defaultOptions.stack)
|
|
||||||
// console.log(e.stack);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* @typedef {{color: boolean, cwd: string, report: function(string), warn: function(string), getMessages: function():Array.<MESSAGE>}} CONTEXT
|
* @typedef {{color: boolean, cwd: string, report: function(string), warn: function(string), getMessages: function():Array.<MESSAGE>}} CONTEXT
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @typedef {{msg: string, level: MESSAGE_LEVEL, file: string}} MESSAGE
|
* @typedef {{msg: string, level: MESSAGE_LEVEL, file: string,line:number,column:number,startOffset:number,endOffset:number}} MESSAGE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -17,13 +17,19 @@ var MESSAGE_LEVEL = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
var err = require('./RTCodeError');
|
||||||
|
var norm = err.RTCodeError.norm;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {CONTEXT}
|
* @type {CONTEXT}
|
||||||
*/
|
*/
|
||||||
var context = {
|
var context = {
|
||||||
|
/** @type {Array.<MESSAGE>} */
|
||||||
messages: [],
|
messages: [],
|
||||||
|
/** @type {boolean} */
|
||||||
color: true,
|
color: true,
|
||||||
|
/** @type {string} */
|
||||||
cwd: process.cwd(),
|
cwd: process.cwd(),
|
||||||
report: function (msg) {
|
report: function (msg) {
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
@ -34,11 +40,20 @@ var context = {
|
|||||||
warn: function (msg, file, line, column) {
|
warn: function (msg, file, line, column) {
|
||||||
context.issue(MESSAGE_LEVEL.WARN, msg, file, line, column);
|
context.issue(MESSAGE_LEVEL.WARN, msg, file, line, column);
|
||||||
},
|
},
|
||||||
error: function (msg, file, line, column, index) {
|
error: function (msg, file, line, column, startOffset, endOffset) {
|
||||||
context.issue(MESSAGE_LEVEL.ERROR, msg, file, line, column, index);
|
context.issue(MESSAGE_LEVEL.ERROR, msg, file, line, column, startOffset, endOffset);
|
||||||
},
|
},
|
||||||
issue: function (level, msg, file, line, column, index) {
|
/**
|
||||||
context.messages.push({level: level, msg: msg, file: file || null, line: line || -1, column: column || -1, index: index || -1});
|
* @param {MESSAGE_LEVEL} level
|
||||||
|
* @param {string} msg
|
||||||
|
* @param {string} file
|
||||||
|
* @param {number} line
|
||||||
|
* @param {number} column
|
||||||
|
* @param {number=} startOffset
|
||||||
|
* @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)});
|
||||||
},
|
},
|
||||||
getMessages: function () {
|
getMessages: function () {
|
||||||
return context.messages;
|
return context.messages;
|
||||||
|
@ -8,6 +8,8 @@ var esprima = require('esprima');
|
|||||||
var escodegen = require('escodegen');
|
var escodegen = require('escodegen');
|
||||||
var reactDOMSupport = require('./reactDOMSupport');
|
var reactDOMSupport = require('./reactDOMSupport');
|
||||||
var stringUtils = require('./stringUtils');
|
var stringUtils = require('./stringUtils');
|
||||||
|
var rtError = require('./RTCodeError');
|
||||||
|
var RTCodeError = rtError.RTCodeError;
|
||||||
|
|
||||||
var repeatTemplate = _.template('_.map(<%= collection %>,<%= repeatFunction %>.bind(<%= repeatBinds %>))');
|
var repeatTemplate = _.template('_.map(<%= collection %>,<%= repeatFunction %>.bind(<%= repeatBinds %>))');
|
||||||
var ifTemplate = _.template('((<%= condition %>)?(<%= body %>):null)');
|
var ifTemplate = _.template('((<%= condition %>)?(<%= body %>):null)');
|
||||||
@ -34,6 +36,10 @@ var propsProp = 'rt-props';
|
|||||||
|
|
||||||
var defaultOptions = {modules: 'amd', version: false, force: false, format: 'stylish', targetVersion: '0.12.2'};
|
var defaultOptions = {modules: 'amd', version: false, force: false, format: 'stylish', targetVersion: '0.12.2'};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param context
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
function shouldUseCreateElement(context) {
|
function shouldUseCreateElement(context) {
|
||||||
switch (context.options.targetVersion) {
|
switch (context.options.targetVersion) {
|
||||||
case '0.11.2':
|
case '0.11.2':
|
||||||
@ -54,6 +60,10 @@ _.forEach(reactSupportedAttributes, function (attributeReactName) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param children
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
function concatChildren(children) {
|
function concatChildren(children) {
|
||||||
var res = '';
|
var res = '';
|
||||||
_.forEach(children, function (child) {
|
_.forEach(children, function (child) {
|
||||||
@ -66,8 +76,17 @@ function concatChildren(children) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
var curlyMap = {'{': 1, '}': -1};
|
var curlyMap = {'{': 1, '}': -1};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param node
|
||||||
|
* @param context
|
||||||
|
* @param {string} txt
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
function convertText(node, context, txt) {
|
function convertText(node, context, txt) {
|
||||||
var res = '';
|
var res = '';
|
||||||
var first = true;
|
var first = true;
|
||||||
@ -84,7 +103,7 @@ function convertText(node, context, txt) {
|
|||||||
curlyCounter += curlyMap[txt.charAt(end)] || 0;
|
curlyCounter += curlyMap[txt.charAt(end)] || 0;
|
||||||
}
|
}
|
||||||
if (curlyCounter !== 0) {
|
if (curlyCounter !== 0) {
|
||||||
throw buildError("Failed to parse text '" + txt + "'", context, node);
|
throw RTCodeError.build("Failed to parse text '" + txt + "'", context, node);
|
||||||
} else {
|
} else {
|
||||||
var needsParens = start !== 0 || end !== txt.length - 1;
|
var needsParens = start !== 0 || end !== txt.length - 1;
|
||||||
res += (first ? '' : '+') + (needsParens ? '(' : '') + txt.substr(start + 1, end - start - 2) + (needsParens ? ')' : '');
|
res += (first ? '' : '+') + (needsParens ? '(' : '') + txt.substr(start + 1, end - start - 2) + (needsParens ? ')' : '');
|
||||||
@ -99,6 +118,7 @@ function convertText(node, context, txt) {
|
|||||||
res = 'true';
|
res = 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//validateJS(res, node, context);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,81 +140,18 @@ function generateInjectedFunc(context, namePrefix, body, params) {
|
|||||||
return generatedFuncName;
|
return generatedFuncName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} html
|
|
||||||
* @param node
|
|
||||||
* @return {number}
|
|
||||||
*/
|
|
||||||
function getLine(html, node) {
|
|
||||||
if (!node) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return html.substring(0, node.startIndex).split('\n').length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//function getLine(node) {
|
|
||||||
// if (!node) {
|
|
||||||
// return 0;
|
|
||||||
// }
|
|
||||||
// var line = 0;
|
|
||||||
// var prev = node.prev;
|
|
||||||
// while (prev) {
|
|
||||||
// var nl = prev.data.split('\n').length - 1;
|
|
||||||
// line += nl;
|
|
||||||
// prev = prev.prev;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// line += getLine(node.parent);
|
|
||||||
// return line + 1;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//function RTCodeError(message, line) {
|
|
||||||
// this.name = 'RTCodeError';
|
|
||||||
// this.message = message || '';
|
|
||||||
// this.line = line || -1;
|
|
||||||
//}
|
|
||||||
//RTCodeError.prototype = Error.prototype;
|
|
||||||
|
|
||||||
// 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, 'line', { configurable: true, enumerable: true });
|
|
||||||
|
|
||||||
function RTCodeError(message, index, line) {
|
|
||||||
Error.captureStackTrace(this, RTCodeError);
|
|
||||||
this.name = 'RTCodeError';
|
|
||||||
this.message = message || '';
|
|
||||||
this.index = index || -1;
|
|
||||||
this.line = line || -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
RTCodeError.prototype = Object.create(Error.prototype);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} msg
|
|
||||||
* @param {*} context
|
|
||||||
* @param {*} node
|
|
||||||
* @return {RTCodeError}
|
|
||||||
*/
|
|
||||||
function buildError(msg, context, node) {
|
|
||||||
var line = getLine(context.html, node);
|
|
||||||
return new RTCodeError(msg, node.startIndex, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function generateProps(node, context) {
|
function generateProps(node, context) {
|
||||||
// console.log(node);
|
// console.log(node);
|
||||||
var props = {};
|
var props = {};
|
||||||
_.forOwn(node.attribs, function (val, key) {
|
_.forOwn(node.attribs, function (val, key) {
|
||||||
var propKey = attributesMapping[key.toLowerCase()] || key;
|
var propKey = attributesMapping[key.toLowerCase()] || key;
|
||||||
if (props.hasOwnProperty(propKey)) {
|
if (props.hasOwnProperty(propKey)) {
|
||||||
throw buildError('duplicate definition of ' + propKey + ' ' + JSON.stringify(node.attribs), context, node);
|
throw RTCodeError.build('duplicate definition of ' + propKey + ' ' + JSON.stringify(node.attribs), context, node);
|
||||||
}
|
}
|
||||||
if (key.indexOf('on') === 0 && !isStringOnlyCode(val)) {
|
if (key.indexOf('on') === 0 && !isStringOnlyCode(val)) {
|
||||||
var funcParts = val.split('=>');
|
var funcParts = val.split('=>');
|
||||||
if (funcParts.length !== 2) {
|
if (funcParts.length !== 2) {
|
||||||
throw buildError("when using 'on' events, use lambda '(p1,p2)=>body' notation or use {} to return a callback function. error: [" + key + "='" + val + "']", context, node);
|
throw RTCodeError.build("when using 'on' events, use lambda '(p1,p2)=>body' notation or use {} to return a callback function. error: [" + key + "='" + val + "']", context, node);
|
||||||
}
|
}
|
||||||
var evtParams = funcParts[0].replace('(', '').replace(')', '').trim();
|
var evtParams = funcParts[0].replace('(', '').replace(')', '').trim();
|
||||||
var funcBody = funcParts[1].trim();
|
var funcBody = funcParts[1].trim();
|
||||||
@ -276,22 +233,26 @@ function convertHtmlToReact(node, context) {
|
|||||||
_.each(node.attribs[scopeProp].split(';'), function (scopePart) {
|
_.each(node.attribs[scopeProp].split(';'), function (scopePart) {
|
||||||
var scopeSubParts = scopePart.split(' as ');
|
var scopeSubParts = scopePart.split(' as ');
|
||||||
if (scopeSubParts.length < 2) {
|
if (scopeSubParts.length < 2) {
|
||||||
throw buildError("invalid scope part '" + scopePart + "'", context, node);
|
throw RTCodeError.build("invalid scope part '" + scopePart + "'", context, node);
|
||||||
}
|
}
|
||||||
var scopeName = scopeSubParts[1].trim();
|
var scopeName = scopeSubParts[1].trim();
|
||||||
|
validateJS(scopeName, node, context);
|
||||||
stringUtils.addIfNotThere(context.boundParams, scopeName);
|
stringUtils.addIfNotThere(context.boundParams, scopeName);
|
||||||
data.scopeName += stringUtils.capitalize(scopeName);
|
data.scopeName += stringUtils.capitalize(scopeName);
|
||||||
data.scopeMapping[scopeName] = scopeSubParts[0].trim();
|
data.scopeMapping[scopeName] = scopeSubParts[0].trim();
|
||||||
|
validateJS(data.scopeMapping[scopeName], node, context);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.attribs[templateProp]) {
|
if (node.attribs[templateProp]) {
|
||||||
var arr = node.attribs[templateProp].split(' in ');
|
var arr = node.attribs[templateProp].split(' in ');
|
||||||
if (arr.length !== 2) {
|
if (arr.length !== 2) {
|
||||||
throw buildError("rt-repeat invalid 'in' expression '" + node.attribs[templateProp] + "'", context, node);
|
throw RTCodeError.build("rt-repeat invalid 'in' expression '" + node.attribs[templateProp] + "'", context, node);
|
||||||
}
|
}
|
||||||
data.item = arr[0].trim();
|
data.item = arr[0].trim();
|
||||||
data.collection = arr[1].trim();
|
data.collection = arr[1].trim();
|
||||||
|
validateJS(data.item, node, context);
|
||||||
|
validateJS(data.collection, node, context);
|
||||||
stringUtils.addIfNotThere(context.boundParams, data.item);
|
stringUtils.addIfNotThere(context.boundParams, data.item);
|
||||||
stringUtils.addIfNotThere(context.boundParams, data.item + 'Index');
|
stringUtils.addIfNotThere(context.boundParams, data.item + 'Index');
|
||||||
}
|
}
|
||||||
@ -303,7 +264,9 @@ function convertHtmlToReact(node, context) {
|
|||||||
data.condition = node.attribs[ifProp].trim();
|
data.condition = node.attribs[ifProp].trim();
|
||||||
}
|
}
|
||||||
data.children = node.children ? concatChildren(_.map(node.children, function (child) {
|
data.children = node.children ? concatChildren(_.map(node.children, function (child) {
|
||||||
return convertHtmlToReact(child, context);
|
var code = convertHtmlToReact(child, context);
|
||||||
|
validateJS(code, child, context);
|
||||||
|
return code;
|
||||||
})) : '';
|
})) : '';
|
||||||
|
|
||||||
if (hasNonSimpleChildren(node)) {
|
if (hasNonSimpleChildren(node)) {
|
||||||
@ -344,7 +307,7 @@ function convertHtmlToReact(node, context) {
|
|||||||
// return html;
|
// return html;
|
||||||
//}
|
//}
|
||||||
function isTag(node) {
|
function isTag(node) {
|
||||||
return node.type === 'tag';
|
return node.type === 'tag';
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSelfClosingHtmlTags(nodes) {
|
function handleSelfClosingHtmlTags(nodes) {
|
||||||
@ -375,29 +338,29 @@ function convertTemplateToReact(html, options) {
|
|||||||
options = _.defaults({}, options, defaultOptions);
|
options = _.defaults({}, options, defaultOptions);
|
||||||
var defines = {'react/addons': 'React', lodash: '_'};
|
var defines = {'react/addons': 'React', lodash: '_'};
|
||||||
var context = defaultContext(html, options);
|
var context = defaultContext(html, options);
|
||||||
var rootTags = _.filter(rootNode.root()[0].children, function (i) { return i.type === 'tag'; });
|
var rootTags = _.filter(rootNode.root()[0].children, {type: 'tag'});
|
||||||
rootTags = handleSelfClosingHtmlTags(rootTags);
|
rootTags = handleSelfClosingHtmlTags(rootTags);
|
||||||
if (!rootTags || rootTags.length === 0) {
|
if (!rootTags || rootTags.length === 0) {
|
||||||
throw new RTCodeError('Document should have a root element');
|
throw new RTCodeError('Document should have a root element');
|
||||||
}
|
}
|
||||||
var firstTag = null;
|
var firstTag = null;
|
||||||
_.forEach(rootTags, function(tag) {
|
_.forEach(rootTags, function (tag) {
|
||||||
if (tag.name === 'rt-require') {
|
if (tag.name === 'rt-require') {
|
||||||
if (!tag.attribs.dependency || !tag.attribs.as) {
|
if (!tag.attribs.dependency || !tag.attribs.as) {
|
||||||
throw buildError("rt-require needs 'dependency' and 'as' attributes", context, tag);
|
throw RTCodeError.build("rt-require needs 'dependency' and 'as' attributes", context, tag);
|
||||||
} else if (tag.children.length) {
|
} else if (tag.children.length) {
|
||||||
throw buildError('rt-require may have no children', context, tag);
|
throw RTCodeError.build('rt-require may have no children', context, tag);
|
||||||
} else {
|
} else {
|
||||||
defines[tag.attribs.dependency] = tag.attribs.as;
|
defines[tag.attribs.dependency] = tag.attribs.as;
|
||||||
}
|
}
|
||||||
} else if (firstTag === null) {
|
} else if (firstTag === null) {
|
||||||
firstTag = tag;
|
firstTag = tag;
|
||||||
} else {
|
} else {
|
||||||
throw buildError('Document should have no more than a single root element', context, tag);
|
throw RTCodeError.build('Document should have no more than a single root element', context, tag);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (firstTag === null) {
|
if (firstTag === null) {
|
||||||
throw buildError('Document should have a single root element', context, rootNode.root()[0]);
|
throw RTCodeError.build('Document should have a single root element', context, rootNode.root()[0]);
|
||||||
}
|
}
|
||||||
var body = convertHtmlToReact(firstTag, context);
|
var body = convertHtmlToReact(firstTag, context);
|
||||||
var requirePaths = _(defines).keys().map(function (reqName) { return '"' + reqName + '"'; }).value().join(',');
|
var requirePaths = _(defines).keys().map(function (reqName) { return '"' + reqName + '"'; }).value().join(',');
|
||||||
@ -426,6 +389,19 @@ function generate(data, options) {
|
|||||||
return templatePJSTemplate(data);
|
return templatePJSTemplate(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} code
|
||||||
|
* @param node
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
function validateJS(code, node, context) {
|
||||||
|
try {
|
||||||
|
esprima.parse(code);
|
||||||
|
} catch (e) {
|
||||||
|
throw RTCodeError.build(e.description, context, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @return {string}
|
* @return {string}
|
||||||
@ -438,5 +414,7 @@ module.exports = {
|
|||||||
convertTemplateToReact: convertTemplateToReact,
|
convertTemplateToReact: convertTemplateToReact,
|
||||||
RTCodeError: RTCodeError,
|
RTCodeError: RTCodeError,
|
||||||
normalizeName: normalizeName,
|
normalizeName: normalizeName,
|
||||||
_test: {}
|
_test: {
|
||||||
|
convertText: convertText
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
6
test/data/invalid-brace.rt
Normal file
6
test/data/invalid-brace.rt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<!--suppress CheckEmptyScriptTag -->
|
||||||
|
<rt-require dependency="core" as="core"/>
|
||||||
|
|
||||||
|
<div rt-scope="core.translate as tr">
|
||||||
|
<span>{tr('ANIMATION_PANEL_HEADER_LABEL'}</span>
|
||||||
|
</div>
|
3
test/data/invalid-style.rt
Normal file
3
test/data/invalid-style.rt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div>
|
||||||
|
<div style="color: {'blu}" />
|
||||||
|
</div>
|
128
test/src/test.js
128
test/src/test.js
@ -6,7 +6,7 @@ var _ = require('lodash');
|
|||||||
var path = require('path');
|
var path = require('path');
|
||||||
var React = require('react/addons');
|
var React = require('react/addons');
|
||||||
var cheerio = require('cheerio');
|
var cheerio = require('cheerio');
|
||||||
|
var RTCodeError = reactTemplates.RTCodeError;
|
||||||
var dataPath = path.resolve(__dirname, '..', 'data');
|
var dataPath = path.resolve(__dirname, '..', 'data');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -17,35 +17,23 @@ function readFileNormalized(filename) {
|
|||||||
return fs.readFileSync(filename).toString().replace(/\r/g, '').trim();
|
return fs.readFileSync(filename).toString().replace(/\r/g, '').trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
test('invalid tests', function (t) {
|
var invalidFiles = [
|
||||||
var files = [
|
{file: 'invalid-scope.rt', issue: new RTCodeError("invalid scope part 'a in a in a'", 0, 35, 1, 1)},
|
||||||
{file: 'invalid-scope.rt', issue: new reactTemplates.RTCodeError("invalid scope part 'a in a in a'", -1, -1)},
|
{file: 'invalid-html.rt', issue: new RTCodeError('Document should have a root element', -1, -1, -1, -1)},
|
||||||
{file: 'invalid-html.rt', issue: new reactTemplates.RTCodeError('Document should have a root element', -1, -1)},
|
{file: 'invalid-exp.rt', issue: new RTCodeError("Failed to parse text '\n {z\n'", 5, 13, 1, 6)},
|
||||||
{file: 'invalid-exp.rt', issue: new reactTemplates.RTCodeError("Failed to parse text '\n {z\n'", 5, -1)},
|
{file: 'invalid-lambda.rt', issue: new RTCodeError("when using 'on' events, use lambda '(p1,p2)=>body' notation or use {} to return a callback function. error: [onClick='']", 0, 23, 1, 1)},
|
||||||
{
|
{file: 'invalid-js.rt', issue: new RTCodeError('Unexpected token ILLEGAL', 0, 32, 1, 1)},
|
||||||
file: 'invalid-lambda.rt',
|
{file: 'invalid-single-root.rt', issue: new RTCodeError('Document should have no more than a single root element', 12, 23, 2, 1)},
|
||||||
issue: new reactTemplates.RTCodeError("when using 'on' events, use lambda '(p1,p2)=>body' notation or use {} to return a callback function. error: [onClick='']", -1, -1)
|
{file: 'invalid-repeat.rt', issue: new RTCodeError('rt-repeat invalid \'in\' expression \'a in b in c\'', 0, 35, 1, 1)},
|
||||||
},
|
{file: 'invalid-rt-require.rt', issue: new RTCodeError("rt-require needs 'dependency' and 'as' attributes", 0, 14, 1, 1)},
|
||||||
{
|
{file: 'invalid-brace.rt', issue: new RTCodeError('Unexpected end of input', 128, 163, 5, 11)},
|
||||||
file: 'invalid-js.rt',
|
{file: 'invalid-style.rt', issue: new RTCodeError('Unexpected token ILLEGAL', 10, 39, 2, 5)}
|
||||||
issue: new reactTemplates.RTCodeError('Line 7: Unexpected token ILLEGAL', 187, undefined)
|
];
|
||||||
},
|
|
||||||
{
|
|
||||||
file: 'invalid-single-root.rt',
|
|
||||||
issue: new reactTemplates.RTCodeError('Document should have no more than a single root element', 12, 1)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
file: 'invalid-repeat.rt',
|
|
||||||
issue: new reactTemplates.RTCodeError('rt-repeat invalid \'in\' expression \'a in b in c\'', -1, -1)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
file: 'invalid-rt-require.rt',
|
|
||||||
issue: new reactTemplates.RTCodeError("rt-require needs 'dependency' and 'as' attributes", -1, -1)
|
|
||||||
}
|
|
||||||
];
|
|
||||||
t.plan(files.length);
|
|
||||||
|
|
||||||
files.forEach(check);
|
test('invalid tests', function (t) {
|
||||||
|
t.plan(invalidFiles.length);
|
||||||
|
|
||||||
|
invalidFiles.forEach(check);
|
||||||
|
|
||||||
function check(testFile) {
|
function check(testFile) {
|
||||||
var filename = path.join(dataPath, testFile.file);
|
var filename = path.join(dataPath, testFile.file);
|
||||||
@ -56,10 +44,14 @@ test('invalid tests', function (t) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
t.deepEqual(errorEqual(error), errorEqual(testFile.issue), 'Expect convertTemplateToReact to throw an error');
|
t.deepEqual(omitStack(error), omitStack(testFile.issue), 'Expect convertTemplateToReact to throw an error');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function omitStack(err) {
|
||||||
|
return _.omit(err, 'stack', 'toIssue');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {ERR} err
|
* @param {ERR} err
|
||||||
* @return {ERR}
|
* @return {ERR}
|
||||||
@ -72,19 +64,9 @@ function normalizeError(err) {
|
|||||||
test('invalid tests json', function (t) {
|
test('invalid tests json', function (t) {
|
||||||
var cli = require('../../src/cli');
|
var cli = require('../../src/cli');
|
||||||
var context = require('../../src/context');
|
var context = require('../../src/context');
|
||||||
var files = [
|
t.plan(invalidFiles.length);
|
||||||
{file: 'invalid-scope.rt', issue: new reactTemplates.RTCodeError("invalid scope part 'a in a in a'", -1, -1)},
|
|
||||||
{file: 'invalid-html.rt', issue: new reactTemplates.RTCodeError('Document should have a root element', -1, -1)},
|
|
||||||
{file: 'invalid-exp.rt', issue: new reactTemplates.RTCodeError("Failed to parse text '\n {z\n'", 5, -1)},
|
|
||||||
{
|
|
||||||
file: 'invalid-lambda.rt',
|
|
||||||
issue: new reactTemplates.RTCodeError("when using 'on' events, use lambda '(p1,p2)=>body' notation or use {} to return a callback function. error: [onClick='']", -1, -1)
|
|
||||||
},
|
|
||||||
{file: 'invalid-js.rt', issue: new reactTemplates.RTCodeError('Line 7: Unexpected token ILLEGAL', 187, -1)}
|
|
||||||
];
|
|
||||||
t.plan(files.length);
|
|
||||||
|
|
||||||
files.forEach(check);
|
invalidFiles.forEach(check);
|
||||||
|
|
||||||
function check(testFile) {
|
function check(testFile) {
|
||||||
context.clear();
|
context.clear();
|
||||||
@ -108,26 +90,15 @@ function errorEqualMessage(err, file) {
|
|||||||
return {
|
return {
|
||||||
index: err.index,
|
index: err.index,
|
||||||
line: err.line,
|
line: err.line,
|
||||||
column: err.column || -1,
|
column: err.column,
|
||||||
|
startOffset: err.startOffset,
|
||||||
|
endOffset: err.endOffset,
|
||||||
msg: err.message,
|
msg: err.message,
|
||||||
level: 'ERROR',
|
level: 'ERROR',
|
||||||
file: file
|
file: file
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {RTCodeError} err
|
|
||||||
* @return {{index: number, line: number, message: string, name: string}}
|
|
||||||
*/
|
|
||||||
function errorEqual(err) {
|
|
||||||
return {
|
|
||||||
index: err.index,
|
|
||||||
line: err.line,
|
|
||||||
message: err.message,
|
|
||||||
name: err.name
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test('conversion test', function (t) {
|
test('conversion test', function (t) {
|
||||||
var files = ['div.rt', 'test.rt', 'repeat.rt', 'inputs.rt', 'require.rt'];
|
var files = ['div.rt', 'test.rt', 'repeat.rt', 'inputs.rt', 'require.rt'];
|
||||||
t.plan(files.length);
|
t.plan(files.length);
|
||||||
@ -218,8 +189,6 @@ test('html tests', function (t) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('test context', function (t) {
|
test('test context', function (t) {
|
||||||
t.plan(3);
|
|
||||||
|
|
||||||
var context = require('../../src/context');
|
var context = require('../../src/context');
|
||||||
context.clear();
|
context.clear();
|
||||||
t.equal(context.hasErrors(), false);
|
t.equal(context.hasErrors(), false);
|
||||||
@ -227,39 +196,65 @@ test('test context', function (t) {
|
|||||||
t.equal(context.hasErrors(), true);
|
t.equal(context.hasErrors(), true);
|
||||||
context.clear();
|
context.clear();
|
||||||
t.equal(context.hasErrors(), false);
|
t.equal(context.hasErrors(), false);
|
||||||
|
|
||||||
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test shell', function (t) {
|
test('test shell', function (t) {
|
||||||
t.plan(3);
|
|
||||||
|
|
||||||
var shell = require('../../src/shell');
|
var shell = require('../../src/shell');
|
||||||
var context = require('../../src/context');
|
var context = require('../../src/context');
|
||||||
var newContext = _.cloneDeep(context);
|
var newContext = _.cloneDeep(context);
|
||||||
var output;
|
var outputJSON;
|
||||||
newContext.options.format = 'json';
|
newContext.options.format = 'json';
|
||||||
newContext.report = function (text) {
|
newContext.report = function (text) {
|
||||||
output = text;
|
outputJSON = text;
|
||||||
};
|
};
|
||||||
var r = shell.printResults(newContext);
|
var r = shell.printResults(newContext);
|
||||||
t.equal(r, 0);
|
t.equal(r, 0);
|
||||||
context.error('hi', '', 1, 1);
|
context.error('hi', '', 1, 1);
|
||||||
r = shell.printResults(newContext);
|
r = shell.printResults(newContext);
|
||||||
t.equal(r, 1);
|
t.equal(r, 1);
|
||||||
t.equal(output, '[\n {\n "level": "ERROR",\n "msg": "hi",\n "file": null,\n "line": 1,\n "column": 1,\n "index": -1\n }\n]');
|
var output = JSON.parse(outputJSON);
|
||||||
|
t.deepEqual(output, [{column: 1, endOffset: -1, file: null, index: -1, level: 'ERROR', line: 1, msg: 'hi', startOffset: -1}]);
|
||||||
context.clear();
|
context.clear();
|
||||||
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test shell', function (t) {
|
test('test shell', function (t) {
|
||||||
t.plan(1);
|
|
||||||
|
|
||||||
var filename = path.join(dataPath, 'div.rt');
|
var filename = path.join(dataPath, 'div.rt');
|
||||||
var cli = require('../../src/cli');
|
var cli = require('../../src/cli');
|
||||||
var r = cli.execute(filename + ' -r --dry-run');
|
var r = cli.execute(filename + ' -r --dry-run');
|
||||||
t.equal(r, 0);
|
t.equal(r, 0);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test convertText', function (t) {
|
||||||
|
var texts = [
|
||||||
|
{input: '{}', expected: '()'},
|
||||||
|
{input: "a {'b'}", expected: '"a "+(\'b\')'}
|
||||||
|
];
|
||||||
|
t.plan(texts.length);
|
||||||
|
texts.forEach(check);
|
||||||
|
function check(testData) {
|
||||||
|
var r = reactTemplates._test.convertText({}, {}, testData.input);
|
||||||
|
t.equal(r, testData.expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test convertText errors', function (t) {
|
||||||
|
var texts = [
|
||||||
|
{input: '{}', expected: '()'},
|
||||||
|
{input: "a {'b'}", expected: '"a "+(\'b\')'}
|
||||||
|
];
|
||||||
|
t.plan(texts.length);
|
||||||
|
texts.forEach(check);
|
||||||
|
function check(testData) {
|
||||||
|
var r = reactTemplates._test.convertText({}, {}, testData.input);
|
||||||
|
t.equal(r, testData.expected);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('util.isStale', function (t) {
|
test('util.isStale', function (t) {
|
||||||
t.plan(2);
|
|
||||||
var a = path.join(dataPath, 'a.tmp');
|
var a = path.join(dataPath, 'a.tmp');
|
||||||
var b = path.join(dataPath, 'b.tmp');
|
var b = path.join(dataPath, 'b.tmp');
|
||||||
|
|
||||||
@ -280,4 +275,5 @@ test('util.isStale', function (t) {
|
|||||||
|
|
||||||
fs.unlinkSync(a);
|
fs.unlinkSync(a);
|
||||||
fs.unlinkSync(b);
|
fs.unlinkSync(b);
|
||||||
|
t.end();
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user