mirror of
https://github.com/bobwen-dev/react-templates
synced 2025-04-12 00:56:39 +02:00
refactoring code out
This commit is contained in:
parent
1269ecb071
commit
7b86b2a11d
@ -1,4 +1,7 @@
|
||||
'use strict';
|
||||
var util = require('util');
|
||||
var _ = require('lodash');
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{line: number, col: number}} Pos
|
||||
@ -77,6 +80,19 @@ RTCodeError.norm = norm;
|
||||
RTCodeError.prototype.toIssue = function () {
|
||||
};
|
||||
|
||||
function buildFormat(context, node, msg, args) {
|
||||
return buildError(util.format.apply(this, [msg].concat(args)), context, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {*} context
|
||||
* @param {*} node
|
||||
* @param {string} msg
|
||||
* @param {Array.<string>} args
|
||||
* @return {RTCodeError}
|
||||
*/
|
||||
RTCodeError.buildFormat = _.restParam(buildFormat, 3);
|
||||
|
||||
/**
|
||||
* @param {string} msg
|
||||
* @param {*} context
|
||||
|
63
src/reactSupport.js
Normal file
63
src/reactSupport.js
Normal file
@ -0,0 +1,63 @@
|
||||
'use strict';
|
||||
var _ = require('lodash');
|
||||
|
||||
/**
|
||||
* @param {Context} context
|
||||
* @return {boolean}
|
||||
*/
|
||||
function shouldUseCreateElement(context) {
|
||||
switch (context.options.targetVersion) {
|
||||
case '0.11.2':
|
||||
case '0.11.1':
|
||||
case '0.11.0':
|
||||
case '0.10.0':
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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 classNameProp = 'className';
|
||||
var attributesMapping = {'class': classNameProp, 'rt-class': classNameProp, 'for': 'htmlFor'}; //eslint-disable-line quote-props
|
||||
|
||||
_.forEach(reactSupportedAttributes, function (attributeReactName) {
|
||||
if (attributeReactName !== attributeReactName.toLowerCase()) {
|
||||
attributesMapping[attributeReactName.toLowerCase()] = attributeReactName;
|
||||
}
|
||||
});
|
||||
|
||||
var htmlSelfClosingTags = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
|
||||
|
||||
|
||||
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 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)()');
|
||||
|
||||
var templates = {
|
||||
amd: templateAMDTemplate,
|
||||
commonjs: templateCommonJSTemplate,
|
||||
typescript: templateTypescriptTemplate,
|
||||
es6: templateES6Template,
|
||||
none: templatePJSTemplate,
|
||||
jsrt: templateJSRTTemplate
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
htmlSelfClosingTags: htmlSelfClosingTags,
|
||||
attributesMapping: attributesMapping,
|
||||
classNameProp: classNameProp,
|
||||
shouldUseCreateElement: shouldUseCreateElement,
|
||||
templates: templates
|
||||
};
|
@ -8,15 +8,34 @@ var reactNativeSupport = require('./reactNativeSupport');
|
||||
var reactPropTemplates = require('./reactPropTemplates');
|
||||
var stringUtils = require('./stringUtils');
|
||||
var rtError = require('./RTCodeError');
|
||||
var reactSupport = require('./reactSupport');
|
||||
var templates = reactSupport.templates;
|
||||
var utils = require('./utils');
|
||||
var validateJS = utils.validateJS;
|
||||
var RTCodeError = rtError.RTCodeError;
|
||||
|
||||
var repeatTemplate = _.template('_.map(<%= collection %>,<%= repeatFunction %>.bind(<%= repeatBinds %>))');
|
||||
var ifTemplate = _.template('((<%= condition %>)?(<%= body %>):null)');
|
||||
var propsTemplateSimple = _.template('_.assign({}, <%= generatedProps %>, <%= rtProps %>)');
|
||||
var propsTemplate = _.template('mergeProps( <%= generatedProps %>, <%= rtProps %>)');
|
||||
var propsMergeFunction = 'function mergeProps(inline,external) {\n var res = _.assign({},inline,external)\nif (inline.hasOwnProperty(\'style\')) {\n res.style = _.defaults(res.style, inline.style);\n}\n' +
|
||||
' if (inline.hasOwnProperty(\'className\') && external.hasOwnProperty(\'className\')) {\n' +
|
||||
' res.className = external.className + \' \' + inline.className;\n} return res;\n}\n';
|
||||
|
||||
//var propsMergeFunction3 = 'function mergeProps(inline,external) {\n var res = _.assign({},inline,external)\nif (inline.hasOwnProperty(\'style\')) {\n res.style = _.defaults(res.style, inline.style);\n}\n' +
|
||||
// ' if (inline.hasOwnProperty(\'className\') && external.hasOwnProperty(\'className\')) {\n' +
|
||||
// ' res.className = external.className + \' \' + inline.className;\n} return res;\n}\n';
|
||||
|
||||
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');
|
||||
|
||||
var classSetTemplate = _.template('_.keys(_.pick(<%= classSet %>, _.identity)).join(" ")');
|
||||
var simpleTagTemplate = _.template('<%= name %>(<%= props %><%= children %>)');
|
||||
var tagTemplate = _.template('<%= name %>.apply(this, [<%= props %><%= children %>])');
|
||||
@ -24,29 +43,7 @@ var simpleTagTemplateCreateElement = _.template('React.createElement(<%= name %>
|
||||
var tagTemplateCreateElement = _.template('React.createElement.apply(this, [<%= name %>,<%= props %><%= children %>])');
|
||||
var commentTemplate = _.template(' /* <%= data %> */ ');
|
||||
|
||||
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 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)()');
|
||||
|
||||
var templates = {
|
||||
amd: templateAMDTemplate,
|
||||
commonjs: templateCommonJSTemplate,
|
||||
typescript: templateTypescriptTemplate,
|
||||
es6: templateES6Template,
|
||||
none: templatePJSTemplate,
|
||||
jsrt: templateJSRTTemplate
|
||||
};
|
||||
|
||||
|
||||
var htmlSelfClosingTags = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
|
||||
|
||||
var templateAttr = 'rt-repeat';
|
||||
var repeatAttr = 'rt-repeat';
|
||||
var ifAttr = 'rt-if';
|
||||
var classSetAttr = 'rt-class';
|
||||
var classAttr = 'class';
|
||||
@ -79,53 +76,6 @@ function getOptions(options) {
|
||||
return finalOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Context} context
|
||||
* @return {boolean}
|
||||
*/
|
||||
function shouldUseCreateElement(context) {
|
||||
switch (context.options.targetVersion) {
|
||||
case '0.11.2':
|
||||
case '0.11.1':
|
||||
case '0.11.0':
|
||||
case '0.10.0':
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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 classNameProp = 'className';
|
||||
var attributesMapping = {'class': classNameProp, 'rt-class': classNameProp, 'for': 'htmlFor'}; //eslint-disable-line quote-props
|
||||
|
||||
_.forEach(reactSupportedAttributes, function (attributeReactName) {
|
||||
if (attributeReactName !== attributeReactName.toLowerCase()) {
|
||||
attributesMapping[attributeReactName.toLowerCase()] = attributeReactName;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @param children
|
||||
* @return {string}
|
||||
*/
|
||||
function concatChildren(children) {
|
||||
var res = '';
|
||||
_.forEach(children, function (child) {
|
||||
if (child && child.indexOf(' /*') !== 0 ) {
|
||||
res += ',' + child;
|
||||
} else {
|
||||
res += child;
|
||||
}
|
||||
}, this);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @const
|
||||
*/
|
||||
@ -178,15 +128,6 @@ function convertText(node, context, txt) {
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} txt
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isStringOnlyCode(txt) {
|
||||
txt = txt.trim();
|
||||
return txt.length && txt.charAt(0) === '{' && txt.charAt(txt.length - 1) === '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Context} context
|
||||
* @param {string} namePrefix
|
||||
@ -261,14 +202,14 @@ function generateProps(node, context) {
|
||||
// console.log(node);
|
||||
var props = {};
|
||||
_.forOwn(node.attribs, function (val, key) {
|
||||
var propKey = attributesMapping[key.toLowerCase()] || key;
|
||||
if (props.hasOwnProperty(propKey) && propKey !== classNameProp) {
|
||||
throw RTCodeError.build('duplicate definition of ' + propKey + ' ' + JSON.stringify(node.attribs), context, node);
|
||||
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));
|
||||
}
|
||||
if (key.indexOf('on') === 0 && !isStringOnlyCode(val)) {
|
||||
if (key.indexOf('on') === 0 && !utils.isStringOnlyCode(val)) {
|
||||
var funcParts = val.split('=>');
|
||||
if (funcParts.length !== 2) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
var evtParams = funcParts[0].replace('(', '').replace(')', '').trim();
|
||||
var funcBody = funcParts[1].trim();
|
||||
@ -278,7 +219,7 @@ function generateProps(node, context) {
|
||||
}
|
||||
var generatedFuncName = generateInjectedFunc(context, key, funcBody, params);
|
||||
props[propKey] = generatedFuncName + '.bind(' + (['this'].concat(context.boundParams)).join(',') + ')';
|
||||
} else if (key === 'style' && !isStringOnlyCode(val)) {
|
||||
} else if (key === 'style' && !utils.isStringOnlyCode(val)) {
|
||||
var styleParts = val.trim().split(';');
|
||||
styleParts = _.compact(_.map(styleParts, function (str) {
|
||||
str = str.trim();
|
||||
@ -294,14 +235,14 @@ function generateProps(node, context) {
|
||||
return stringUtils.convertToCamelCase(stylePart[0]) + ' : ' + convertText(node, context, stylePart[1].trim());
|
||||
});
|
||||
props[propKey] = '{' + styleArray.join(',') + '}';
|
||||
} else if (propKey === classNameProp) {
|
||||
} else if (propKey === reactSupport.classNameProp) {
|
||||
// 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] + ' + " " + ' : '';
|
||||
if (key === classSetAttr) {
|
||||
props[propKey] = existing + classSetTemplate({classSet: val});
|
||||
} else if (key === classAttr || key === classNameProp) {
|
||||
} else if (key === classAttr || key === reactSupport.classNameProp) {
|
||||
props[propKey] = existing + convertText(node, context, val.trim());
|
||||
}
|
||||
} else if (key.indexOf('rt-') !== 0) {
|
||||
@ -327,7 +268,7 @@ function convertTagNameToConstructor(tagName, context) {
|
||||
}
|
||||
|
||||
var isHtmlTag = _.includes(reactDOMSupport[context.options.targetVersion], tagName);
|
||||
if (shouldUseCreateElement(context)) {
|
||||
if (reactSupport.shouldUseCreateElement(context)) {
|
||||
isHtmlTag = isHtmlTag || tagName.match(/^\w+(-\w+)$/);
|
||||
return isHtmlTag ? "'" + tagName + "'" : tagName;
|
||||
}
|
||||
@ -354,7 +295,7 @@ function defaultContext(html, options) {
|
||||
*/
|
||||
function hasNonSimpleChildren(node) {
|
||||
return _.some(node.children, function (child) {
|
||||
return child.type === 'tag' && child.attribs[templateAttr];
|
||||
return child.type === 'tag' && child.attribs[repeatAttr];
|
||||
});
|
||||
}
|
||||
|
||||
@ -403,10 +344,10 @@ function convertHtmlToReact(node, context) {
|
||||
|
||||
// Order matters. We need to add the item and itemIndex to context.boundParams before
|
||||
// the rt-scope directive is processed, lest they are not passed to the child scopes
|
||||
if (node.attribs[templateAttr]) {
|
||||
var arr = node.attribs[templateAttr].split(' in ');
|
||||
if (node.attribs[repeatAttr]) {
|
||||
var arr = node.attribs[repeatAttr].split(' in ');
|
||||
if (arr.length !== 2) {
|
||||
throw RTCodeError.build("rt-repeat invalid 'in' expression '" + node.attribs[templateAttr] + "'", context, node);
|
||||
throw RTCodeError.build("rt-repeat invalid 'in' expression '" + node.attribs[repeatAttr] + "'", context, node);
|
||||
}
|
||||
data.item = arr[0].trim();
|
||||
data.collection = arr[1].trim();
|
||||
@ -423,6 +364,7 @@ function convertHtmlToReact(node, context) {
|
||||
outerMapping: {}
|
||||
};
|
||||
|
||||
//data.innerScope.outerMapping = _.zipObject(context.boundParams, context.boundParams);
|
||||
_.forEach(context.boundParams, function (boundParam) {
|
||||
data.innerScope.outerMapping[boundParam] = boundParam;
|
||||
});
|
||||
@ -469,16 +411,16 @@ function convertHtmlToReact(node, context) {
|
||||
if (node.attribs[ifAttr]) {
|
||||
data.condition = node.attribs[ifAttr].trim();
|
||||
}
|
||||
data.children = node.children ? concatChildren(_.map(node.children, function (child) {
|
||||
data.children = node.children ? utils.concatChildren(_.map(node.children, function (child) {
|
||||
var code = convertHtmlToReact(child, context);
|
||||
validateJS(code, child, context);
|
||||
return code;
|
||||
})) : '';
|
||||
|
||||
if (hasNonSimpleChildren(node)) {
|
||||
data.body = shouldUseCreateElement(context) ? tagTemplateCreateElement(data) : tagTemplate(data);
|
||||
data.body = reactSupport.shouldUseCreateElement(context) ? tagTemplateCreateElement(data) : tagTemplate(data);
|
||||
} else {
|
||||
data.body = shouldUseCreateElement(context) ? simpleTagTemplateCreateElement(data) : simpleTagTemplate(data);
|
||||
data.body = reactSupport.shouldUseCreateElement(context) ? simpleTagTemplateCreateElement(data) : simpleTagTemplate(data);
|
||||
}
|
||||
|
||||
if (node.attribs[scopeAttr]) {
|
||||
@ -490,7 +432,7 @@ function convertHtmlToReact(node, context) {
|
||||
|
||||
// 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[templateAttr]) {
|
||||
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;
|
||||
@ -524,7 +466,7 @@ function handleSelfClosingHtmlTags(nodes) {
|
||||
.map(function (node) {
|
||||
var externalNodes = [];
|
||||
node.children = handleSelfClosingHtmlTags(node.children);
|
||||
if (node.type === 'tag' && _.includes(htmlSelfClosingTags, node.name)) {
|
||||
if (node.type === 'tag' && _.includes(reactSupport.htmlSelfClosingTags, node.name)) {
|
||||
externalNodes = _.filter(node.children, isTag);
|
||||
_.forEach(externalNodes, function (child) {
|
||||
child.parent = node;
|
||||
@ -537,22 +479,6 @@ function handleSelfClosingHtmlTags(nodes) {
|
||||
.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options
|
||||
* @param {*} context
|
||||
* @param {CONTEXT} reportContext
|
||||
* @param node
|
||||
*/
|
||||
function validate(options, context, reportContext, node) {
|
||||
if (node.type === 'tag' && node.attribs['rt-if'] && !node.attribs.key) {
|
||||
var loc = rtError.getNodeLoc(context, node);
|
||||
reportContext.warn('rt-if without a key', options.fileName, loc.pos.line, loc.pos.col, loc.start, loc.end);
|
||||
}
|
||||
if (node.children) {
|
||||
node.children.forEach(validate.bind(this, options, context, reportContext));
|
||||
}
|
||||
}
|
||||
|
||||
function convertTemplateToReact(html, options) {
|
||||
var context = require('./context');
|
||||
return convertRT(html, context, options);
|
||||
@ -575,7 +501,7 @@ function convertRT(html, reportContext, options) {
|
||||
var defines = options.defines ? _.clone(options.defines) : defaultDefines;
|
||||
|
||||
var context = defaultContext(html, options);
|
||||
validate(options, context, reportContext, rootNode.root()[0]);
|
||||
utils.validate(options, context, reportContext, rootNode.root()[0]);
|
||||
var rootTags = _.filter(rootNode.root()[0].children, {type: 'tag'});
|
||||
rootTags = handleSelfClosingHtmlTags(rootTags);
|
||||
if (!rootTags || rootTags.length === 0) {
|
||||
@ -604,20 +530,22 @@ function convertRT(html, reportContext, options) {
|
||||
return '"' + reqName + '"';
|
||||
}).value().join(',');
|
||||
var requireVars = _.values(defines).join(',');
|
||||
var vars;
|
||||
var buildImport;
|
||||
if (options.modules === 'typescript') {
|
||||
vars = _(defines).map(function (reqVar, reqPath) {
|
||||
buildImport = function (reqVar, reqPath) {
|
||||
return 'import ' + reqVar + " = require('" + reqPath + "');";
|
||||
}).join('\n');
|
||||
};
|
||||
} else if (options.modules === 'es6') {
|
||||
vars = _(defines).map(function (reqVar, reqPath) {
|
||||
buildImport = function (reqVar, reqPath) {
|
||||
return 'import ' + reqVar + " from '" + reqPath + "';";
|
||||
}).join('\n');
|
||||
};
|
||||
} else {
|
||||
vars = _(defines).map(function (reqVar, reqPath) {
|
||||
buildImport = function (reqVar, reqPath) {
|
||||
return 'var ' + reqVar + " = require('" + reqPath + "');";
|
||||
}).join('\n');
|
||||
};
|
||||
}
|
||||
var vars = _(defines).map(buildImport).join('\n');
|
||||
|
||||
if (options.flow) {
|
||||
vars = '/* @flow */\n' + vars;
|
||||
}
|
||||
@ -625,17 +553,21 @@ function convertRT(html, reportContext, options) {
|
||||
data.injectedFunctions = context.injectedFunctions.join('\n');
|
||||
var code = generate(data, options);
|
||||
if (options.modules !== 'typescript' && options.modules !== 'jsrt') {
|
||||
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) {
|
||||
throw new RTCodeError(e.message, e.index, -1);
|
||||
}
|
||||
code = parseJS(code);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
function parseJS(code) {
|
||||
try {
|
||||
var tree = esprima.parse(code, {range: true, tokens: true, comment: true});
|
||||
tree = escodegen.attachComments(tree, tree.comments, tree.tokens);
|
||||
return escodegen.generate(tree, {comment: true});
|
||||
} catch (e) {
|
||||
throw new RTCodeError(e.message, e.index, -1);
|
||||
}
|
||||
}
|
||||
|
||||
function convertJSRTToJS(text, reportContext, options) {
|
||||
options = getOptions(options);
|
||||
options.modules = 'jsrt';
|
||||
@ -643,13 +575,7 @@ function convertJSRTToJS(text, reportContext, options) {
|
||||
var code = text.replace(templateMatcherJSRT, function (template, html) {
|
||||
return convertRT(html, reportContext, options).replace(/;$/, '');
|
||||
});
|
||||
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) {
|
||||
throw new RTCodeError(e.message, e.index, -1);
|
||||
}
|
||||
code = parseJS(code);
|
||||
return code;
|
||||
}
|
||||
|
||||
@ -658,33 +584,12 @@ function generate(data, options) {
|
||||
return template(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} code
|
||||
* @param node
|
||||
* @param {Context} context
|
||||
*/
|
||||
function validateJS(code, node, context) {
|
||||
try {
|
||||
esprima.parse(code);
|
||||
} catch (e) {
|
||||
throw RTCodeError.build(e.description, context, node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @return {string}
|
||||
*/
|
||||
function normalizeName(name) {
|
||||
return name.replace(/-/g, '_');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
convertTemplateToReact: convertTemplateToReact,
|
||||
convertRT: convertRT,
|
||||
convertJSRTToJS: convertJSRTToJS,
|
||||
RTCodeError: RTCodeError,
|
||||
normalizeName: normalizeName,
|
||||
normalizeName: utils.normalizeName,
|
||||
_test: {
|
||||
convertText: convertText
|
||||
}
|
||||
|
76
src/utils.js
Normal file
76
src/utils.js
Normal file
@ -0,0 +1,76 @@
|
||||
'use strict';
|
||||
var _ = require('lodash');
|
||||
var esprima = require('esprima-harmony');
|
||||
var rtError = require('./RTCodeError');
|
||||
var RTCodeError = rtError.RTCodeError;
|
||||
|
||||
/**
|
||||
* @param {string} code
|
||||
* @param node
|
||||
* @param {Context} context
|
||||
*/
|
||||
function validateJS(code, node, context) {
|
||||
try {
|
||||
esprima.parse(code);
|
||||
} catch (e) {
|
||||
throw RTCodeError.build(e.description, context, node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @return {string}
|
||||
*/
|
||||
function normalizeName(name) {
|
||||
return name.replace(/-/g, '_');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} txt
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isStringOnlyCode(txt) {
|
||||
txt = txt.trim();
|
||||
return txt.length && txt.charAt(0) === '{' && txt.charAt(txt.length - 1) === '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param children
|
||||
* @return {string}
|
||||
*/
|
||||
function concatChildren(children) {
|
||||
var res = '';
|
||||
_.forEach(children, function (child) {
|
||||
if (child && child.indexOf(' /*') !== 0 ) {
|
||||
res += ',' + child;
|
||||
} else {
|
||||
res += child;
|
||||
}
|
||||
}, this);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate rt
|
||||
* @param options
|
||||
* @param {*} context
|
||||
* @param {CONTEXT} reportContext
|
||||
* @param node
|
||||
*/
|
||||
function validate(options, context, reportContext, node) {
|
||||
if (node.type === 'tag' && node.attribs['rt-if'] && !node.attribs.key) {
|
||||
var loc = rtError.getNodeLoc(context, node);
|
||||
reportContext.warn('rt-if without a key', options.fileName, loc.pos.line, loc.pos.col, loc.start, loc.end);
|
||||
}
|
||||
if (node.children) {
|
||||
node.children.forEach(validate.bind(this, options, context, reportContext));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
normalizeName: normalizeName,
|
||||
validateJS: validateJS,
|
||||
isStringOnlyCode: isStringOnlyCode,
|
||||
concatChildren: concatChildren,
|
||||
validate: validate
|
||||
};
|
@ -95,17 +95,7 @@ function errorEqualMessage(err, file) {
|
||||
|
||||
test('conversion test', function (t) {
|
||||
var files = ['div.rt', 'test.rt', 'repeat.rt', 'inputs.rt', 'require.rt'];
|
||||
t.plan(files.length);
|
||||
files.forEach(check);
|
||||
|
||||
function check(testFile) {
|
||||
var filename = path.join(dataPath, testFile);
|
||||
var html = readFileNormalized(filename);
|
||||
var expected = readFileNormalized(filename + '.js');
|
||||
// var expected = fs.readFileSync(filename.replace(".html", ".js")).toString();
|
||||
var actual = reactTemplates.convertTemplateToReact(html).replace(/\r/g, '').trim();
|
||||
compareAndWrite(t, actual, expected, filename);
|
||||
}
|
||||
testFiles(t, files);
|
||||
});
|
||||
|
||||
test('prop template conversion test', function (t) {
|
||||
@ -116,20 +106,23 @@ test('prop template conversion test', function (t) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var files = ['propTemplates/simpleTemplate.rt', 'propTemplates/templateInScope.rt', 'propTemplates/implicitTemplate.rt', 'propTemplates/twoTemplates.rt'];
|
||||
t.plan(files.length);
|
||||
files.forEach(check);
|
||||
|
||||
function check(testFile) {
|
||||
var filename = path.join(dataPath, testFile);
|
||||
var html = readFileNormalized(filename);
|
||||
var expected = readFileNormalized(filename + '.js');
|
||||
var actual = reactTemplates.convertTemplateToReact(html, options).replace(/\r/g, '').trim();
|
||||
compareAndWrite(t, actual, expected, filename);
|
||||
}
|
||||
testFiles(t, files, options);
|
||||
});
|
||||
|
||||
function checkFile(t, options, testFile) {
|
||||
var filename = path.join(dataPath, testFile);
|
||||
var html = readFileNormalized(filename);
|
||||
var expected = readFileNormalized(filename + '.js');
|
||||
var actual = reactTemplates.convertTemplateToReact(html, options).replace(/\r/g, '').trim();
|
||||
compareAndWrite(t, actual, expected, filename);
|
||||
}
|
||||
|
||||
function testFiles(t, files, options) {
|
||||
t.plan(files.length);
|
||||
files.forEach(checkFile.bind(this, t, options));
|
||||
}
|
||||
|
||||
test('conversion test - native', function (t) {
|
||||
var options = {
|
||||
propTemplates: {
|
||||
@ -139,19 +132,8 @@ test('conversion test - native', function (t) {
|
||||
},
|
||||
native: true
|
||||
};
|
||||
|
||||
var files = ['nativeView.rt', 'listViewTemplate.rt', 'listViewAndCustomTemplate.rt'];
|
||||
t.plan(files.length);
|
||||
files.forEach(check);
|
||||
|
||||
function check(testFile) {
|
||||
var filename = path.join(dataPath, testFile);
|
||||
var html = readFileNormalized(filename);
|
||||
var expected = readFileNormalized(filename + '.js');
|
||||
// var expected = fs.readFileSync(filename.replace(".html", ".js")).toString();
|
||||
var actual = reactTemplates.convertTemplateToReact(html, options).replace(/\r/g, '').trim();
|
||||
compareAndWrite(t, actual, expected, filename);
|
||||
}
|
||||
testFiles(t, files, options);
|
||||
});
|
||||
|
||||
test('convert div with all module types', function (t) {
|
||||
@ -284,19 +266,6 @@ test('test convertText', function (t) {
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
var a = path.join(dataPath, 'a.tmp');
|
||||
var b = path.join(dataPath, 'b.tmp');
|
||||
|
Loading…
x
Reference in New Issue
Block a user