This commit is contained in:
ido 2017-06-29 10:35:15 +03:00
parent 8e149352a7
commit ee4508aea3
42 changed files with 1157 additions and 1149 deletions

View File

@ -7,6 +7,7 @@ playground/**/*.rt.js
playground/dist/**
playground/libs/**
playground2/**
playground3/**
playground3/dist/**
playground3/libs/**

View File

@ -2,7 +2,7 @@
"extends": ["wix-editor", "wix-editor/node", "plugin:lodash/recommended"],
"plugins": ["lodash", "wix-editor"],
"rules": {
"semi": [2, "always"],
"semi": [2, "never"],
"func-style": [2, "declaration", {"allowArrowFunctions": true}],
"prefer-spread": 0,
"prefer-template": 0,

View File

@ -1,4 +1,4 @@
'use strict';
'use strict'
module.exports = function (grunt) {
grunt.initConfig({
clean: {
@ -88,33 +88,33 @@ module.exports = function (grunt) {
options: readConfig('./playground.config.js')
}
}
});
})
function readConfig(file) {
return eval(require('fs').readFileSync(file).toString()); // eslint-disable-line no-eval
return eval(require('fs').readFileSync(file).toString()) // eslint-disable-line no-eval
}
grunt.loadNpmTasks('grunt-tape');
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-tape')
grunt.loadNpmTasks('grunt-browserify')
grunt.loadNpmTasks('grunt-contrib-watch')
grunt.loadNpmTasks('grunt-contrib-requirejs')
grunt.loadNpmTasks('grunt-eslint')
grunt.loadNpmTasks('grunt-contrib-uglify')
grunt.registerTask('default', ['eslint:all']);
grunt.registerTask('lint', ['eslint:all']);
grunt.registerTask('test', ['tape']);
grunt.registerTask('default', ['eslint:all'])
grunt.registerTask('lint', ['eslint:all'])
grunt.registerTask('test', ['tape'])
grunt.registerTask('rt', () => {
const reactTemplates = require('./src/cli');
const files = grunt.file.expand('playground/*.rt');
const ret = reactTemplates.execute({modules: 'amd', force: true, _: files});
return ret === 0;
});
const reactTemplates = require('./src/cli')
const files = grunt.file.expand('playground/*.rt')
const ret = reactTemplates.execute({modules: 'amd', force: true, _: files})
return ret === 0
})
grunt.registerTask('build', ['rt', 'browserify:pg']);
grunt.registerTask('home', ['rt', 'browserify:home']);
grunt.registerTask('pgall', ['rt', 'browserify', 'uglify', 'requirejs']);
grunt.registerTask('build', ['rt', 'browserify:pg'])
grunt.registerTask('home', ['rt', 'browserify:home'])
grunt.registerTask('pgall', ['rt', 'browserify', 'uglify', 'requirejs'])
grunt.registerTask('all', ['default', 'test']);
};
grunt.registerTask('all', ['default', 'test'])
}

View File

@ -1,6 +1,7 @@
#!/usr/bin/env node
'use strict';
const cli = require('../dist/cli');
const exitCode = cli.execute(process.argv);
'use strict'
const cli = require('../dist/cli') //src
// console.log(process.argv);
const exitCode = cli.execute(process.argv)
/*eslint no-process-exit:0*/
process.exit(exitCode);
process.exit(exitCode)

View File

@ -1,18 +1,18 @@
define(['react', 'lodash', 'jquery', './libs/codemirror-4.8/lib/codemirror'], function (React, _, $, CodeMirror) {
'use strict';
'use strict'
function annotationTooltip(ann) {
var severity = ann.severity;
var severity = ann.severity
if (!severity) {
severity = 'error';
severity = 'error'
}
var tip = document.createElement('div');
tip.className = 'CodeMirror-lint-message-' + severity;
tip.appendChild(document.createTextNode(ann.message));
return tip;
var tip = document.createElement('div')
tip.className = 'CodeMirror-lint-message-' + severity
tip.appendChild(document.createTextNode(ann.message))
return tip
}
var GUTTER_ID = 'rt-annotations';
var GUTTER_ID = 'rt-annotations'
function annotate(editor, annot) {
//if (annot.index) {
@ -21,10 +21,10 @@ define(['react', 'lodash', 'jquery', './libs/codemirror-4.8/lib/codemirror'], fu
// var range = editor.findWordAt(pos);
// editor.markText(range.anchor, range.head, {className: 'editor-error'});
//}
var tipLabel = document.createDocumentFragment(); /*state.hasGutter &&*/
var ann = {severity: 'error', message: annot.message};
tipLabel.appendChild(annotationTooltip(ann));
editor.setGutterMarker(Math.max(annot.line, 0), GUTTER_ID, makeMarker(tipLabel, 'error', false, 'state.options.tooltips'));
var tipLabel = document.createDocumentFragment() /*state.hasGutter &&*/
var ann = {severity: 'error', message: annot.message}
tipLabel.appendChild(annotationTooltip(ann))
editor.setGutterMarker(Math.max(annot.line, 0), GUTTER_ID, makeMarker(tipLabel, 'error', false, 'state.options.tooltips'))
}
function clearMarks(cm) {
@ -33,86 +33,86 @@ define(['react', 'lodash', 'jquery', './libs/codemirror-4.8/lib/codemirror'], fu
//for (var i = 0; i < state.marked.length; ++i)
// state.marked[i].clear();
//state.marked.length = 0;
cm.clearGutter(GUTTER_ID);
cm.clearGutter(GUTTER_ID)
}
function makeMarker(labels, severity, multiple, tooltips) {
var marker = document.createElement('div');
var inner = marker;
marker.className = 'CodeMirror-lint-marker-' + severity;
var marker = document.createElement('div')
var inner = marker
marker.className = 'CodeMirror-lint-marker-' + severity
if (multiple) {
inner = marker.appendChild(document.createElement('div'));
inner.className = 'CodeMirror-lint-marker-multiple';
inner = marker.appendChild(document.createElement('div'))
inner.className = 'CodeMirror-lint-marker-multiple'
}
if (tooltips !== false) {
CodeMirror.on(inner, 'mouseover', function (e) {
showTooltipFor(e, labels, inner);
});
showTooltipFor(e, labels, inner)
})
}
return marker;
return marker
}
function showTooltip(e, content) {
var tt = document.createElement('div');
tt.className = 'CodeMirror-lint-tooltip';
tt.appendChild(content.cloneNode(true));
document.body.appendChild(tt);
var tt = document.createElement('div')
tt.className = 'CodeMirror-lint-tooltip'
tt.appendChild(content.cloneNode(true))
document.body.appendChild(tt)
function position(ev) {
if (!tt.parentNode) {
return CodeMirror.off(document, 'mousemove', position);
return CodeMirror.off(document, 'mousemove', position)
}
tt.style.top = Math.max(0, ev.clientY - tt.offsetHeight - 5) + 'px';
tt.style.left = (ev.clientX + 5) + 'px'; //eslint-disable-line no-extra-parens
tt.style.top = Math.max(0, ev.clientY - tt.offsetHeight - 5) + 'px'
tt.style.left = (ev.clientX + 5) + 'px' //eslint-disable-line no-extra-parens
}
CodeMirror.on(document, 'mousemove', position);
position(e);
CodeMirror.on(document, 'mousemove', position)
position(e)
if (tt.style.opacity !== null) {
tt.style.opacity = 1;
tt.style.opacity = 1
}
return tt;
return tt
}
function rm(elt) {
if (elt.parentNode) {
elt.parentNode.removeChild(elt);
elt.parentNode.removeChild(elt)
}
}
function hideTooltip(tt) {
if (!tt.parentNode) {
return;
return
}
if (tt.style.opacity === null) {
rm(tt);
rm(tt)
}
tt.style.opacity = 0;
setTimeout(function () { rm(tt); }, 600);
tt.style.opacity = 0
setTimeout(function () { rm(tt) }, 600)
}
function showTooltipFor(e, content, node) {
var tooltip = showTooltip(e, content);
var tooltip = showTooltip(e, content)
function hide() {
CodeMirror.off(node, 'mouseout', hide);
if (tooltip) { hideTooltip(tooltip); tooltip = null; }
CodeMirror.off(node, 'mouseout', hide)
if (tooltip) { hideTooltip(tooltip); tooltip = null }
}
var poll = setInterval(function () {
if (tooltip) {
for (var n = node; ; n = n.parentNode) { //eslint-disable-line no-restricted-syntax
if (n === document.body) {
return undefined;
return undefined
}
if (!n) { hide(); break; }
if (!n) { hide(); break }
}
}
if (!tooltip) {
return clearInterval(poll);
return clearInterval(poll)
}
}, 400);
CodeMirror.on(node, 'mouseout', hide);
}, 400)
CodeMirror.on(node, 'mouseout', hide)
}
return {
GUTTER_ID: GUTTER_ID,
annotate: annotate,
clearMarks: clearMarks
};
});
}
})

View File

@ -12,7 +12,7 @@ define([
'./libs/codemirror-4.8/addon/runmode/runmode'
//'./libs/codemirror-4.8/addon/display/placeholder'
], function (React, ReactDOM, _, $, CodeMirror, CMLint) {
'use strict';
'use strict'
//codeMirror: 'libs/codemirror-4.8/lib/codemirror',
//htmlmixed: 'libs/codemirror-4.8/mode/htmlmixed/htmlmixed',
//javascript: 'libs/codemirror-4.8/mode/javascript/javascript'
@ -31,40 +31,40 @@ define([
dangerouslySetInnerHTML: null
}
}
};
var tags = CodeMirror.htmlSchema;
}
var tags = CodeMirror.htmlSchema
Object.keys(CodeMirror.htmlSchema).forEach(function (i) {
tags[i].attrs = _.defaults(rtSchema.div.attrs, tags[i].attrs);
});
tags[i].attrs = _.defaults(rtSchema.div.attrs, tags[i].attrs)
})
function completeAfter(cm, pred) {
//var cur = cm.getCursor();
if (!pred || pred()) {
setTimeout(function () {
if (!cm.state.completionActive) {
cm.showHint({completeSingle: false});
cm.showHint({completeSingle: false})
}
}, 100);
}, 100)
}
return CodeMirror.Pass;
return CodeMirror.Pass
}
function completeIfAfterLt(cm) {
return completeAfter(cm, function () {
var cur = cm.getCursor();
return cm.getRange(CodeMirror.Pos(cur.line, cur.ch - 1), cur) === '<'; //eslint-disable-line new-cap
});
var cur = cm.getCursor()
return cm.getRange(CodeMirror.Pos(cur.line, cur.ch - 1), cur) === '<' //eslint-disable-line new-cap
})
}
function completeIfInTag(cm) {
return completeAfter(cm, function () {
var tok = cm.getTokenAt(cm.getCursor());
var tok = cm.getTokenAt(cm.getCursor())
if (tok.type === 'string' && (!/['"]/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length === 1)) {
return false;
return false
}
var inner = CodeMirror.innerMode(cm.getMode(), tok.state).state;
return inner.tagName;
});
var inner = CodeMirror.innerMode(cm.getMode(), tok.state).state
return inner.tagName
})
}
return React.createClass({
@ -82,29 +82,29 @@ define([
return {
readOnly: false,
mode: 'html'
};
}
},
getInitialState: function () {
return {
editorId: _.uniqueId()
};
}
},
//componentWillMount: function () {
//},
render: function () {
var props = _.omit(this.props, ['ref', 'key', 'value', 'valueLink', 'onChange']);
props.id = this.props.id || this.state.editorId;
props.defaultValue = this.props.valueLink ? this.props.valueLink() : this.props.value;
return React.DOM.textarea(props);
var props = _.omit(this.props, ['ref', 'key', 'value', 'valueLink', 'onChange'])
props.id = this.props.id || this.state.editorId
props.defaultValue = this.props.valueLink ? this.props.valueLink() : this.props.value
return React.DOM.textarea(props)
},
componentWillUpdate: function (nextProps/*, nextState*/) {
var value = nextProps.valueLink ? nextProps.valueLink() : nextProps.value;
var value = nextProps.valueLink ? nextProps.valueLink() : nextProps.value
if (this.editor && this.editor.getValue() !== value) {
this.editor.setValue(value || '');
this.editor.setValue(value || '')
}
},
componentDidMount: function () {
var value = this.props.valueLink ? this.props.valueLink() : this.props.value;
var value = this.props.valueLink ? this.props.valueLink() : this.props.value
var options = {
readOnly: this.props.readOnly,
lineWrapping: true,
@ -115,36 +115,36 @@ define([
mode: 'javascript',
gutters: ['CodeMirror-linenumbers', 'rt-annotations'],
theme: 'solarized' //solarized_light solarized-light
};
}
if (this.props.mode === 'html') {
options.mode = 'text/html';
options.mode = 'text/html'
options.extraKeys = {
"'<'": completeAfter,
"'/'": completeIfAfterLt,
"' '": completeIfInTag,
"'='": completeIfInTag,
'Ctrl-Space': 'autocomplete'
};
options.hintOptions = {schemaInfo: tags};
}
options.hintOptions = {schemaInfo: tags}
//options.gutters = ['CodeMirror-lint-markers'];
//options.lint = true;
} else {
options.mode = 'javascript';
options.mode = 'javascript'
//options.gutters = ['CodeMirror-lint-markers'];
//options.lint = true;
}
this.editor = CodeMirror.fromTextArea(ReactDOM.findDOMNode(this), options);
this.editor = CodeMirror.fromTextArea(ReactDOM.findDOMNode(this), options)
if (!this.props.readOnly) {
this.editor.on('change', function (/*e*/) {
if (this.props.valueLink) {
this.props.valueLink(this.editor.getValue());
this.props.valueLink(this.editor.getValue())
} else if (this.props.onChange) {
this.props.onChange({target: {value: this.editor.getValue()}});
this.props.onChange({target: {value: this.editor.getValue()}})
}
}.bind(this));
}.bind(this))
}
},
//showMessage: function (msg) {
@ -163,13 +163,13 @@ define([
// }
//},
annotate: function (annot) {
CMLint.annotate(this.editor, annot);
CMLint.annotate(this.editor, annot)
},
clearAnnotations: function () {
CMLint.clearMarks(this.editor);
CMLint.clearMarks(this.editor)
},
componentWillUnmount: function () {
this.editor.toTextArea();
this.editor.toTextArea()
}
});
});
})
})

View File

@ -3,7 +3,7 @@ define(['react', 'react-dom', 'lodash', 'jquery', './libs/codemirror-4.8/lib/cod
'./libs/codemirror-4.8/mode/xml/xml',
'./libs/codemirror-4.8/addon/runmode/runmode'
], function (React, ReactDOM, _, $, CodeMirror) {
'use strict';
'use strict'
return React.createClass({
displayName: 'CodeMirrorViewer',
propTypes: {
@ -13,34 +13,34 @@ define(['react', 'react-dom', 'lodash', 'jquery', './libs/codemirror-4.8/lib/cod
valueLink: React.PropTypes.string
},
getDefaultProps: function () {
return {mode: 'html'};
return {mode: 'html'}
},
getInitialState: function () {
return {editorId: _.uniqueId()};
return {editorId: _.uniqueId()}
},
render: function () {
var props = _.omit(this.props, ['ref', 'key', 'value', 'valueLink', 'onChange']);
props.id = this.props.id || this.state.editorId;
props.className = 'cm-s-default';
var value = this.props.valueLink ? this.props.valueLink() : this.props.value;
return React.DOM.pre(props, value);
var props = _.omit(this.props, ['ref', 'key', 'value', 'valueLink', 'onChange'])
props.id = this.props.id || this.state.editorId
props.className = 'cm-s-default'
var value = this.props.valueLink ? this.props.valueLink() : this.props.value
return React.DOM.pre(props, value)
},
componentWillUpdate: function (nextProps/*, nextState*/) {
var value = nextProps.valueLink ? nextProps.valueLink() : nextProps.value;
var value = nextProps.valueLink ? nextProps.valueLink() : nextProps.value
if (this.editor && this.editor.getValue() !== value) {
this.editor.setValue(value || '');
this.editor.setValue(value || '')
}
},
componentDidMount: function () {
var value = this.props.valueLink ? this.props.valueLink() : this.props.value;
var mode = this.props.mode;
var value = this.props.valueLink ? this.props.valueLink() : this.props.value
var mode = this.props.mode
if (this.props.mode === 'html') {
mode = 'text/html';
mode = 'text/html'
}
this.editor = CodeMirror.runMode(value, mode, ReactDOM.findDOMNode(this));
this.editor = CodeMirror.runMode(value, mode, ReactDOM.findDOMNode(this))
},
componentWillUnmount: function () {
this.editor.toTextArea();
this.editor.toTextArea()
}
});
});
})
})

View File

@ -3,7 +3,7 @@
*/
/*global ace:true*/
define(['react', 'lodash']/*, 'ace'*/, function (React, _/*, ace*/) {
'use strict';
'use strict'
return React.createClass({
displayName: 'BraceEditor',
propTypes: {
@ -17,49 +17,49 @@ define(['react', 'lodash']/*, 'ace'*/, function (React, _/*, ace*/) {
getInitialState: function () {
return {
editorId: _.uniqueId()
};
}
},
componentWillMount: _.noop,
render: function () {
var props = _.omit(this.props, ['ref', 'key', 'value', 'valueLink', 'onChange']);
props.id = this.props.id || this.state.editorId;
return React.DOM.div(props);
var props = _.omit(this.props, ['ref', 'key', 'value', 'valueLink', 'onChange'])
props.id = this.props.id || this.state.editorId
return React.DOM.div(props)
},
componentWillUpdate: function (nextProps/*, nextState*/) {
var value = nextProps.valueLink ? nextProps.valueLink() : nextProps.value;
var value = nextProps.valueLink ? nextProps.valueLink() : nextProps.value
if (this.editor && this.editor.getValue() !== value) {
this.editor.setValue(value, 0);
this.editor.setValue(value, 0)
}
},
componentDidMount: function () {
this.editor = ace.edit(this.props.id || this.state.editorId);
this.editor = ace.edit(this.props.id || this.state.editorId)
// this.editor.setTheme('ace/theme/monokai');
this.editor.setTheme('ace/theme/solarized_light');
this.editor.setTheme('ace/theme/solarized_light')
if (this.props.mode === 'html') {
this.editor.getSession().setMode('ace/mode/html');
this.editor.getSession().setMode('ace/mode/html')
} else {
this.editor.getSession().setMode('ace/mode/javascript');
this.editor.getSession().setMode('ace/mode/javascript')
}
this.editor.getSession().setUseWorker(false);
this.editor.getSession().setUseWorker(false)
var value = this.props.valueLink ? this.props.valueLink() : this.props.value;
this.editor.setValue(value, 0);
var value = this.props.valueLink ? this.props.valueLink() : this.props.value
this.editor.setValue(value, 0)
if (this.props.readOnly) {
this.editor.setReadOnly(true);
this.editor.setReadOnly(true)
} else {
this.editor.setReadOnly(false);
this.editor.setReadOnly(false)
this.editor.on('change', function (/*e*/) {
if (this.props.valueLink) {
this.props.valueLink(this.editor.getValue());
this.props.valueLink(this.editor.getValue())
} else if (this.props.onChange) {
this.props.onChange({target: {value: this.editor.getValue()}});
this.props.onChange({target: {value: this.editor.getValue()}})
}
}.bind(this));
}.bind(this))
}
this.editor.clearSelection();
this.editor.clearSelection()
},
componentWillUnmount: function () {
this.editor.destroy();
this.editor.destroy()
}
});
});
})
})

View File

@ -7,7 +7,7 @@ define(['lodash', 'react', './examples.rt',
'text!./samples/weather.code', 'text!./samples/weather.rt',
'text!./samples/rt-import.rt'
], function (_, React, examplesTemplate, helloCode, helloRT, todoCode, todoRT, rtIfCode, rtIfRT, rtPropsCode, rtPropsRT, rtRepeatCode, rtRepeatRT, weatherCode, weatherRT, rtImportRT) {
'use strict';
'use strict'
var samples = {
hello: [helloCode, helloRT],
todo: [todoCode, todoRT],
@ -15,24 +15,24 @@ define(['lodash', 'react', './examples.rt',
rtIf: [rtIfCode, rtIfRT],
repeat: [rtRepeatCode, rtRepeatRT],
weather: [weatherCode, weatherRT]
};
samples = _.mapValues(samples, function (v, k) { return {name: k, templateProps: _.template(v[0])({name: k}), templateHTML: v[1]}; });
}
samples = _.mapValues(samples, function (v, k) { return {name: k, templateProps: _.template(v[0])({name: k}), templateHTML: v[1]} })
return React.createClass({
displayName: 'Examples',
mixins: [React.addons.LinkedStateMixin],
getInitialState: function () {
var codeAmd = window.reactTemplates.convertTemplateToReact(rtImportRT, {modules: 'amd', name: 'template'});
var codeCJS = window.reactTemplates.convertTemplateToReact(rtImportRT, {modules: 'commonjs', name: 'template'});
var codeES6 = window.reactTemplates.convertTemplateToReact(rtImportRT, {modules: 'es6', name: 'template'});
var codeAmd = window.reactTemplates.convertTemplateToReact(rtImportRT, {modules: 'amd', name: 'template'})
var codeCJS = window.reactTemplates.convertTemplateToReact(rtImportRT, {modules: 'commonjs', name: 'template'})
var codeES6 = window.reactTemplates.convertTemplateToReact(rtImportRT, {modules: 'es6', name: 'template'})
return {
rtImport: {value: rtImportRT},
amd: {value: codeAmd},
cjs: {value: codeCJS},
es6: {value: codeES6},
samples: samples
};
}
},
render: examplesTemplate
});
});
})
})

View File

@ -20,10 +20,10 @@ requirejs.config({
map: {
'*': {'react/addons': 'react'}
}
});
})
requirejs(['fiddle', 'react', 'react-dom', 'jquery', 'bootstrap'], function (fiddle, React, ReactDOM) {
'use strict';
var elem = React.createElement(fiddle);
window.fiddle = ReactDOM.render(elem, document.getElementById('container'));
});
'use strict'
var elem = React.createElement(fiddle)
window.fiddle = ReactDOM.render(elem, document.getElementById('container'))
})

View File

@ -2,44 +2,44 @@
* Created by avim on 12/2/2014.
*/
define(['react', 'firebase', 'lodash', './fiddle.rt', 'jquery'], function (React, Firebase, _, fiddleTemplate, $) {
'use strict';
'use strict'
function generateRandomId() {
var uuid = 'xxxxxxxx'.replace(/[xy]/g, function (c) {
var r = _.random(0, 15);
return (c === 'x' ? r : r & 0x3 | 0x8).toString(16);
});
return uuid;
var r = _.random(0, 15)
return (c === 'x' ? r : r & 0x3 | 0x8).toString(16)
})
return uuid
}
var Fiddle = React.createClass({
displayName: 'Fiddle',
componentDidMount: function () {
if (window.location.hash) {
var newHash = window.location.hash.replace('#', '');
var firebase = new Firebase('https://reacttemplates.firebaseio-demo.com/');
var newHash = window.location.hash.replace('#', '')
var firebase = new Firebase('https://reacttemplates.firebaseio-demo.com/')
firebase.child('fiddles').child(newHash).on('value', function (snapshot) {
this.refs.playground.setState(snapshot.val());
Firebase.goOffline();
}.bind(this));
this.refs.playground.setState(snapshot.val())
Firebase.goOffline()
}.bind(this))
} else {
Firebase.goOffline();
Firebase.goOffline()
}
},
save: function () {
var newHash = generateRandomId();
window.location.hash = newHash;
Firebase.goOnline();
var newHash = generateRandomId()
window.location.hash = newHash
Firebase.goOnline()
var playgroundState = this.refs.playground.state;
var firebase = new Firebase('https://reacttemplates.firebaseio-demo.com/');
var playgroundState = this.refs.playground.state
var firebase = new Firebase('https://reacttemplates.firebaseio-demo.com/')
firebase.child('fiddles').child(newHash).set(playgroundState, function () {
Firebase.goOffline();
alert('Saved the fiddle, you can share your url'); //eslint-disable-line no-alert
});
Firebase.goOffline()
alert('Saved the fiddle, you can share your url') //eslint-disable-line no-alert
})
},
clear: function () {
this.refs.playground.clear();
this.refs.playground.clear()
},
loadSample: function (name) {
//require(['text!./samples/' + name + '.rt', 'text!./samples/' + name + '.code'], function (rt, code) {
@ -51,25 +51,25 @@ define(['react', 'firebase', 'lodash', './fiddle.rt', 'jquery'], function (React
// this.refs.playground.setState(currentState);
//});
var playground = this.refs.playground;
var playground = this.refs.playground
$.get('playground/samples/' + name + '.rt', null, function (data/*, textStatus, jqXHR*/) {
var rt = data;
var rt = data
$.get('playground/samples/' + name + '.code', null, function (data2/*, textStatus2, jqXHR2*/) {
var currentState = {
templateHTML: rt,
templateProps: _.template(data2)({name: 'template'})
};
}
//this.updateSample(currentState);
playground.setState(currentState);
});
});
playground.setState(currentState)
})
})
//this.refs.playground.clear();
},
render: fiddleTemplate
});
})
return Fiddle;
});
return Fiddle
})

View File

@ -21,10 +21,10 @@ requirejs.config({
map: {
'*': {'react/addons': 'react'}
}
});
})
requirejs(['./examples', 'react', 'react-dom', 'jquery'], function (Examples, React, ReactDOM) {
'use strict';
var elem = React.createElement(Examples);
ReactDOM.render(elem, document.getElementById('home-section'));
});
'use strict'
var elem = React.createElement(Examples)
ReactDOM.render(elem, document.getElementById('home-section'))
})

View File

@ -1,6 +1,6 @@
/*eslint-env browser*/
define(['react', 'react-dom', 'jquery', 'lodash', './playground-fiddle.rt', './playground.rt'], function (React, ReactDOM, $, _, pgFiddleTemplate, playgroundTemplate) {
'use strict';
'use strict'
//function emptyFunc() {
// return null;
//}
@ -25,13 +25,13 @@ define(['react', 'react-dom', 'jquery', 'lodash', './playground-fiddle.rt', './p
function showMessage(editor, msg) {
if (editor && editor.showMessage) {
editor.annotate({line: 1, message: msg});
editor.annotate({line: 1, message: msg})
}
}
function clearMessage(editor) {
if (editor && editor.clearAnnotations) {
editor.clearAnnotations();
editor.clearAnnotations()
}
}
@ -55,23 +55,23 @@ define(['react', 'react-dom', 'jquery', 'lodash', './playground-fiddle.rt', './p
function generateRenderFunc(renderFunc) {
return function () {
var res = null;
var res = null
try {
res = renderFunc.apply(this);
res = renderFunc.apply(this)
} catch (e) {
res = React.DOM.div.apply(this, [{style: {color: 'red'}}, 'Exception:' + e.message]);
res = React.DOM.div.apply(this, [{style: {color: 'red'}}, 'Exception:' + e.message])
}
return React.DOM.div.apply(this, _.flatten([
{key: 'result'},
res
]));
};
]))
}
}
var templateHTML = '<div></div>';
var templateHTML = '<div></div>'
var templateProps = 'var template = React.createClass({\n' +
' render: templateRT\n' +
'});';
'});'
//var selfCleaningTimeout = {
// componentDidUpdate: function() {
@ -100,18 +100,18 @@ define(['react', 'react-dom', 'jquery', 'lodash', './playground-fiddle.rt', './p
validProps: true,
setTimeout: function () {
//console.log('setTimeout');
clearTimeout(this.timeoutID);
this.timeoutID = setTimeout.apply(null, arguments);
clearTimeout(this.timeoutID)
this.timeoutID = setTimeout.apply(null, arguments)
},
getDefaultProps: function () {
return {
direction: 'horizontal', //vertical
codeVisible: true,
fiddle: false
};
}
},
getLayoutClass: function () {
return (this.props.direction === 'horizontal' && 'horizontal') || 'vertical'; //eslint-disable-line no-extra-parens
return (this.props.direction === 'horizontal' && 'horizontal') || 'vertical' //eslint-disable-line no-extra-parens
},
//executeCode: function() {
// var mountNode = this.refs.mount.getDOMNode();
@ -141,78 +141,78 @@ define(['react', 'react-dom', 'jquery', 'lodash', './playground-fiddle.rt', './p
//},
getTabs: function () {
if (this.props.codeVisible) {
return [['templateHTML', 'Template'], ['templateProps', 'Class'], ['templateSource', 'Generated code']];
return [['templateHTML', 'Template'], ['templateProps', 'Class'], ['templateSource', 'Generated code']]
}
return [['templateHTML', 'Template'], ['templateSource', 'Generated code']];
return [['templateHTML', 'Template'], ['templateSource', 'Generated code']]
},
updateSample: function (state) {
//try {
// React.unmountComponentAtNode(mountNode);
//} catch (e) { }
this.generateCode(state);
this.generateCode(state)
//this.sampleFunc = generateTemplateFunction(this.templateSource);
//this.validHTML = this.sampleFunc !== emptyFunc;
this.validHTML = true;
this.sampleRender = generateRenderFunc(this.sampleFunc);
var editor;
this.validHTML = true
this.sampleRender = generateRenderFunc(this.sampleFunc)
var editor
try {
this.validProps = true;
this.validProps = true
//console.log(state.templateProps);
this.sample = eval('(function () {' + this.templateSource + '\n' + state.templateProps + '\n return React.createElement(' + state.name + ');})()'); //eslint-disable-line no-eval
clearMessage(this.refs.editorCode);
this.sample = eval('(function () {' + this.templateSource + '\n' + state.templateProps + '\n return React.createElement(' + state.name + ');})()') //eslint-disable-line no-eval
clearMessage(this.refs.editorCode)
} catch (e) {
this.validProps = false;
this.sample = null;
editor = this.refs.editorCode;
this.showError(e, editor);
this.validProps = false
this.sample = null
editor = this.refs.editorCode
this.showError(e, editor)
}
//classBase.render = this.sampleRender;
//this.sample = React.createFactory(React.createClass(classBase));
},
showError: function (e, editor) {
var mountNode = this.refs.mount;
var mountNode = this.refs.mount
this.setTimeout(function () {
showMessage(editor, e.message);
showMessage(editor, e.message)
ReactDOM.render(
React.createElement('div', {className: 'playground-error'}, e.toString()),
mountNode
);
}, 500);
)
}, 500)
},
showErrorAnnotation: function (annot, editor) {
var mountNode = this.refs.mount;
var mountNode = this.refs.mount
this.setTimeout(function () {
editor.annotate(annot);
editor.annotate(annot)
ReactDOM.render(
React.createElement('div', {className: 'playground-error'}, annot.message),
mountNode
);
}, 500);
)
}, 500)
},
clear: function () {
var currentState = {
templateHTML: templateHTML,
templateProps: templateProps
};
}
//this.updateSample(currentState);
this.setState(currentState);
this.setState(currentState)
},
generateCode: function (state) {
var html = state.templateHTML;
var editor = this.refs.editorRT;
var name = window.reactTemplates.normalizeName(state.name) + 'RT';
var code = null;
var html = state.templateHTML
var editor = this.refs.editorRT
var name = window.reactTemplates.normalizeName(state.name) + 'RT'
var code = null
try {
code = window.reactTemplates.convertTemplateToReact(html.trim().replace(/\r/g, ''), {modules: 'none', name: name});
clearMessage(editor);
code = window.reactTemplates.convertTemplateToReact(html.trim().replace(/\r/g, ''), {modules: 'none', name: name})
clearMessage(editor)
} catch (e) {
var annot = e.name === 'RTCodeError' ? {line: e.line, message: e.message, index: e.index} : {line: 1, message: e.message};
this.showErrorAnnotation(annot, editor);
var annot = e.name === 'RTCodeError' ? {line: e.line, message: e.message, index: e.index} : {line: 1, message: e.message}
this.showErrorAnnotation(annot, editor)
//showMessage(editor, msg);
console.log(e);
console.log(e)
}
this.templateSource = code;
this.templateSource = code
},
getInitialState: function () {
var currentState = {
@ -220,53 +220,53 @@ define(['react', 'react-dom', 'jquery', 'lodash', './playground-fiddle.rt', './p
templateProps: this.props.templateProps || templateProps,
name: this.props.name || 'template',
currentTab: 'templateHTML'
};
}
//this.updateSample(currentState);
return currentState;
return currentState
},
componentDidMount: function () {
if (this.props.fiddle) {
window.addEventListener('resize', this.calcSize);
this.calcSize();
window.addEventListener('resize', this.calcSize)
this.calcSize()
}
this.updateSample(this.state);
this.renderSample();
this.updateSample(this.state)
this.renderSample()
},
renderSample: function () {
var mountNode = this.refs.mount;
var mountNode = this.refs.mount
if (this.sample) {
ReactDOM.render(this.sample, mountNode);
ReactDOM.render(this.sample, mountNode)
}
},
componentDidUpdate: function () {
this.renderSample();
this.renderSample()
},
componentWillUnmount: function () {
window.removeEventListener('resize', this.calcSize);
window.removeEventListener('resize', this.calcSize)
},
calcSize: function () {
var contentHeight = $(window).height() - $('#header').height();
var height = contentHeight / 2 - 10;
var contentHeight = $(window).height() - $('#header').height()
var height = contentHeight / 2 - 10
$('.code-area').each(function (/*i, k*/) {
$(this).height(height);
$(this).height(height)
//console.log($(this).height());
});
this.refs.editorCode.editor.refresh();
this.refs.editorRT.editor.refresh();
this.refs.editorGenerated.editor.refresh();
})
this.refs.editorCode.editor.refresh()
this.refs.editorRT.editor.refresh()
this.refs.editorGenerated.editor.refresh()
},
componentWillUpdate: function (nextProps, nextState) {
if (nextState.templateHTML !== this.state.templateHTML || nextState.templateProps !== this.state.templateProps) {
this.updateSample(nextState);
this.updateSample(nextState)
}
},
render: function () {
this.generateCode(this.state);
var template = this.props.fiddle ? pgFiddleTemplate : playgroundTemplate;
return template.apply(this);
this.generateCode(this.state)
var template = this.props.fiddle ? pgFiddleTemplate : playgroundTemplate
return template.apply(this)
}
});
})
return Playground;
});
return Playground
})

View File

@ -1,9 +1,9 @@
/*eslint strict:0*/
'use strict';
'use strict'
/*eslint-env browser*/
/*var _ = */require('lodash');
var reactTemplates = require('../dist/reactTemplates');
window.reactTemplates = reactTemplates;
/*var _ = */require('lodash')
var reactTemplates = require('../dist/reactTemplates')
window.reactTemplates = reactTemplates

View File

@ -4,7 +4,7 @@ define([
'react',
'ImageSearch.rt'
], function (_, $, React, template) {
'use strict';
'use strict'
var ImageSearch = React.createClass({
@ -18,50 +18,50 @@ define([
realTerm: 'cats',
getInitialState: function () {
setTimeout(this.search, 0);
setTimeout(this.search, 0)
return {
searchTerm: this.realTerm,
items: [[], [], []]
};
}
},
search: function () {
this.state.items = [[], [], []];
this.total = 0;
this.heights = [0, 0, 0];
this.hasMore = true;
this.realTerm = this.state.searchTerm;
this.loadMore();
this.state.items = [[], [], []]
this.total = 0
this.heights = [0, 0, 0]
this.hasMore = true
this.realTerm = this.state.searchTerm
this.loadMore()
},
indexOfMin: function (array) {
var indexAndMin = _.reduce(array, function (accum, height, index) {
/*eslint no-extra-parens:0*/
return (height < accum.min) ? {i: index, min: height} : accum;
}, {i: -1, min: Number.MAX_VALUE});
return indexAndMin.i;
return (height < accum.min) ? {i: index, min: height} : accum
}, {i: -1, min: Number.MAX_VALUE})
return indexAndMin.i
},
loadMore: function (done) {
done = done || _.noop;
done = done || _.noop
if (!this.hasMore) {
done();
return;
done()
return
}
var url = 'https://ajax.googleapis.com/ajax/services/search/images?v=1.0&rsz=8&start=' + this.total + '&q=' + this.realTerm + '&callback=?';
var url = 'https://ajax.googleapis.com/ajax/services/search/images?v=1.0&rsz=8&start=' + this.total + '&q=' + this.realTerm + '&callback=?'
var self = this;
var self = this
$.ajax({url: url, dataType: 'jsonp'})
.done(function (data) {
if (!data.responseData) {
self.hasMore = false;
done();
return;
self.hasMore = false
done()
return
}
var results = data.responseData.results;
var items = _.cloneDeep(self.state.items);
var results = data.responseData.results
var items = _.cloneDeep(self.state.items)
results.forEach(function (result) {
var minHeightIndex = self.indexOfMin(self.heights);
var minHeightIndex = self.indexOfMin(self.heights)
items[minHeightIndex].push({
id: self.seq + 1,
@ -69,26 +69,26 @@ define([
url: result.url,
ratio: result.width / result.height,
originalContext: result.originalContextUrl
});
})
self.heights[minHeightIndex] += result.height / result.width;
self.total++;
self.seq++;
});
self.heights[minHeightIndex] += result.height / result.width
self.total++
self.seq++
})
self.setState({items: items});
done();
});
self.setState({items: items})
done()
})
},
shouldComponentUpdate: function (nextProps, nextState) {
return !_.isEqual(this.state, nextState);
return !_.isEqual(this.state, nextState)
},
render: function () {
return template.apply(this);
return template.apply(this)
}
});
})
return ImageSearch;
});
return ImageSearch
})

View File

@ -10,9 +10,9 @@ requirejs.config({
jquery: {exports: '$'},
react: {exports: 'React'}
}
});
})
requirejs(['jquery', 'react', 'ImageSearch'], function ($, React, ImageSearch) {
'use strict';
React.renderComponent(ImageSearch(), $('#main').get(0)); //eslint-disable-line new-cap
});
'use strict'
React.renderComponent(ImageSearch(), $('#main').get(0)) //eslint-disable-line new-cap
})

View File

@ -1,6 +1,6 @@
'use strict';
const util = require('util');
const _ = require('lodash');
'use strict'
const util = require('util')
const _ = require('lodash')
/**
@ -14,14 +14,14 @@ const _ = require('lodash');
*/
function getLine(html, node) {
if (!node) {
return {line: 1, col: 1};
return {line: 1, col: 1}
}
const linesUntil = html.substring(0, node.startIndex).split('\n');
return {line: linesUntil.length, col: linesUntil[linesUntil.length - 1].length + 1};
const linesUntil = html.substring(0, node.startIndex).split('\n')
return {line: linesUntil.length, col: linesUntil[linesUntil.length - 1].length + 1}
}
function norm(n) {
return n === undefined ? -1 : n;
return n === undefined ? -1 : n
}
/**
@ -34,23 +34,23 @@ function norm(n) {
*/
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);
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)
}
}
/**
* @type {buildError}
*/
RTCodeError.build = buildError;
RTCodeError.norm = norm;
RTCodeError.build = buildError
RTCodeError.norm = norm
/**
* @param {*} context
@ -60,7 +60,7 @@ RTCodeError.norm = norm;
* @return {RTCodeError}
*/
function buildFormat(context, node, msg, args) {
return buildError(context, node, util.format.apply(this, [msg].concat(args)));
return buildError(context, node, util.format.apply(this, [msg].concat(args)))
}
/**
@ -70,7 +70,7 @@ function buildFormat(context, node, msg, args) {
* @param {Array.<string>} args
* @return {RTCodeError}
*/
RTCodeError.buildFormat = _.rest(buildFormat, 3);
RTCodeError.buildFormat = _.rest(buildFormat, 3)
/**
* @param {*} context
@ -79,8 +79,8 @@ RTCodeError.buildFormat = _.rest(buildFormat, 3);
* @return {RTCodeError}
*/
function buildError(context, node, msg) {
const loc = getNodeLoc(context, node);
return new RTCodeError(msg, loc.start, loc.end, loc.pos.line, loc.pos.col);
const loc = getNodeLoc(context, node)
return new RTCodeError(msg, loc.start, loc.end, loc.pos.line, loc.pos.col)
}
/**
@ -89,24 +89,24 @@ function buildError(context, node, msg) {
* @return {{pos:Pos, start:number, end:number}}
*/
function getNodeLoc(context, node) {
const start = node.startIndex;
const pos = getLine(context.html, node);
let end;
const start = node.startIndex
const pos = getLine(context.html, node)
let end
if (node.data) {
end = start + node.data.length;
end = start + node.data.length
} else if (node.next) { // eslint-disable-line
end = node.next.startIndex;
end = node.next.startIndex
} else {
end = context.html.length;
end = context.html.length
}
return {
pos,
start,
end
};
}
}
module.exports = {
RTCodeError,
getNodeLoc
};
}

View File

@ -1,12 +1,12 @@
'use strict';
'use strict'
const fs = require('fs');
const path = require('path');
const chalk = require('chalk');
const reactTemplates = require('./reactTemplates');
const fsUtil = require('./fsUtil');
const convertRT = reactTemplates.convertRT;
const convertJSRTToJS = reactTemplates.convertJSRTToJS;
const fs = require('fs')
const path = require('path')
const chalk = require('chalk')
const reactTemplates = require('./reactTemplates')
const fsUtil = require('./fsUtil')
const convertRT = reactTemplates.convertRT
const convertJSRTToJS = reactTemplates.convertJSRTToJS
/**
* @param {string} source
@ -15,35 +15,35 @@ const convertJSRTToJS = reactTemplates.convertJSRTToJS;
* @param {CONTEXT} context
*/
function convertFile(source, target, options, context) {
options = options || {};
options.fileName = source;
options = options || {}
options.fileName = source
if (!options.force && !fsUtil.isStale(source, target)) {
context.verbose(`target file ${chalk.cyan(target)} is up to date, skipping`);
return;
context.verbose(`target file ${chalk.cyan(target)} is up to date, skipping`)
return
}
const html = fs.readFileSync(source).toString();
const html = fs.readFileSync(source).toString()
if (path.extname(source) === '.rts') {
const rtStyle = require('./rtStyle');
const out = rtStyle.convert(html);
const rtStyle = require('./rtStyle')
const out = rtStyle.convert(html)
if (!options.dryRun) {
fs.writeFileSync(target, out);
fs.writeFileSync(target, out)
}
return;
return
}
const modules = options.modules || 'none';
const shouldAddName = modules === 'none' && !options.name;
const modules = options.modules || 'none'
const shouldAddName = modules === 'none' && !options.name
if (shouldAddName) {
options.name = reactTemplates.normalizeName(path.basename(source, path.extname(source))) + 'RT';
options.name = reactTemplates.normalizeName(path.basename(source, path.extname(source))) + 'RT'
}
options.readFileSync = fsUtil.createRelativeReadFileSync(source);
const js = modules === 'jsrt' ? convertJSRTToJS(html, context, options) : convertRT(html, context, options);
options.readFileSync = fsUtil.createRelativeReadFileSync(source)
const js = modules === 'jsrt' ? convertJSRTToJS(html, context, options) : convertRT(html, context, options)
if (!options.dryRun) {
fs.writeFileSync(target, js);
fs.writeFileSync(target, js)
}
if (shouldAddName) {
delete options.name;
delete options.name
}
}
@ -51,4 +51,4 @@ module.exports = {
convertFile,
context: require('./context'),
_test: {}
};
}

View File

@ -1,36 +1,36 @@
'use strict';
const _ = require('lodash');
const path = require('path');
'use strict'
const _ = require('lodash')
const path = require('path')
// const fs = require('fs');
const api = require('./api');
const context = require('./context');
const shell = require('./shell');
const pkg = require('../package.json');
const options = require('./options');
const reactDOMSupport = require('./reactDOMSupport');
const reactTemplates = require('./reactTemplates');
const rtStyle = require('./rtStyle');
const glob = require('glob');
const api = require('./api')
const context = require('./context')
const shell = require('./shell')
const pkg = require('../package.json')
const options = require('./options')
const reactDOMSupport = require('./reactDOMSupport')
const reactTemplates = require('./reactTemplates')
const rtStyle = require('./rtStyle')
const glob = require('glob')
/**
* @param {Options} currentOptions
* @return {number}
*/
function executeOptions(currentOptions) {
let ret = 0;
const files = currentOptions._;
context.options.format = currentOptions.format || 'stylish';
let ret = 0
const 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]));
console.log(options.generateHelpForOption(files[0]))
} else {
console.log(options.generateHelp());
console.log(options.generateHelp())
}
} else if (currentOptions.listTargetVersion) {
printVersions(currentOptions);
printVersions(currentOptions)
} else if (files.length) {
// console.log(files);
// console.log(files.length);
@ -42,22 +42,22 @@ function executeOptions(currentOptions) {
// }
// return fp;
// });
const allFiles = _.flatMap(files, f => glob.sync(f, {cwd: context.cwd}));
const allFiles = _.flatMap(files, f => glob.sync(f, {cwd: context.cwd}))
// console.log(allFiles.length);
_.forEach(allFiles, handleSingleFile.bind(this, currentOptions));
ret = shell.printResults(context);
_.forEach(allFiles, handleSingleFile.bind(this, currentOptions))
ret = shell.printResults(context)
} else {
console.log(options.generateHelp());
console.log(options.generateHelp())
}
return ret;
return ret
}
function printVersions(currentOptions) {
const ret = Object.keys(reactDOMSupport);
const ret = Object.keys(reactDOMSupport)
if (currentOptions.format === 'json') {
console.log(JSON.stringify(ret, undefined, 2));
console.log(JSON.stringify(ret, undefined, 2))
} else {
console.log(ret.join(', '));
console.log(ret.join(', '))
}
}
@ -67,23 +67,23 @@ function printVersions(currentOptions) {
*/
function handleSingleFile(currentOptions, filename) {
try {
const sourceExt = path.extname(filename);
let outputFilename;
const sourceExt = path.extname(filename)
let outputFilename
if (sourceExt === '.rt') {
outputFilename = filename + (currentOptions.modules === 'typescript' ? '.ts' : '.js');
outputFilename = filename + (currentOptions.modules === 'typescript' ? '.ts' : '.js')
} else if (sourceExt === '.jsrt') {
outputFilename = filename.replace(/\.jsrt$/, '.js');
currentOptions = _.assign({}, currentOptions, {modules: 'jsrt'});
outputFilename = filename.replace(/\.jsrt$/, '.js')
currentOptions = _.assign({}, currentOptions, {modules: 'jsrt'})
} else if (sourceExt === '.rts') {
outputFilename = filename + '.js';
currentOptions = _.assign({}, currentOptions, {modules: 'rts'});
outputFilename = filename + '.js'
currentOptions = _.assign({}, currentOptions, {modules: 'rts'})
} else {
context.error('invalid file, only handle rt/jsrt files', filename);
return;
context.error('invalid file, only handle rt/jsrt files', filename)
return
}
api.convertFile(filename, outputFilename, currentOptions, context);
api.convertFile(filename, outputFilename, currentOptions, context)
} catch (e) {
context.error(e.message, filename, e.line, e.column, e.startOffset, e.endOffset);
context.error(e.message, filename, e.line, e.column, e.startOffset, e.endOffset)
}
}
@ -94,11 +94,11 @@ function handleSingleFile(currentOptions, filename) {
*/
function execute(args) {
try {
const currentOptions = options.parse(args);
return executeOptions(currentOptions);
const currentOptions = options.parse(args)
return executeOptions(currentOptions)
} catch (error) {
console.error(error.message);
return 1;
console.error(error.message)
return 1
}
}
@ -109,4 +109,4 @@ module.exports = {
handleSingleFile,
convertTemplateToReact: reactTemplates.convertTemplateToReact,
convertStyle: rtStyle.convert
};
}

View File

@ -1,4 +1,4 @@
'use strict';
'use strict'
/**
* @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.<MESSAGE>, options:Options, messages: Array.<MESSAGE>}} CONTEXT
*/
@ -14,11 +14,11 @@ const MESSAGE_LEVEL = {
ERROR: 'ERROR',
WARN: 'WARN',
INFO: 'INFO'
};
}
const _ = require('lodash');
const err = require('./RTCodeError');
const norm = err.RTCodeError.norm;
const _ = require('lodash')
const err = require('./RTCodeError')
const norm = err.RTCodeError.norm
/**
@ -32,21 +32,21 @@ const context = {
/** @type {string} */
cwd: process.cwd(),
report(msg) {
console.log(msg);
console.log(msg)
},
verbose(msg) {
if (context.options.verbose) {
console.log(msg);
console.log(msg)
}
},
info(msg, file, line, column) {
context.issue(MESSAGE_LEVEL.INFO, msg, file, line, column);
context.issue(MESSAGE_LEVEL.INFO, msg, file, line, column)
},
warn(msg, file, line, column, startOffset, endOffset) {
context.issue(MESSAGE_LEVEL.WARN, msg, file, line, column, startOffset, endOffset);
context.issue(MESSAGE_LEVEL.WARN, msg, file, line, column, startOffset, endOffset)
},
error(msg, file, line, column, startOffset, endOffset) {
context.issue(MESSAGE_LEVEL.ERROR, msg, file, line, column, startOffset, endOffset);
context.issue(MESSAGE_LEVEL.ERROR, msg, file, line, column, startOffset, endOffset)
},
/**
* @param {MESSAGE_LEVEL} level
@ -58,16 +58,16 @@ const context = {
* @param {number=} endOffset
*/
issue(level, msg, file, line, column, startOffset, endOffset) {
context.messages.push({level, 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() {
return context.messages;
return context.messages
},
clear() {
context.messages = [];
context.messages = []
},
hasErrors() {
return _.some(context.messages, {level: MESSAGE_LEVEL.ERROR});
return _.some(context.messages, {level: MESSAGE_LEVEL.ERROR})
},
options: {
verbose: false,
@ -75,6 +75,6 @@ const context = {
format: 'stylish'
},
MESSAGE_LEVEL
};
}
module.exports = context;
module.exports = context

View File

@ -1,4 +1,4 @@
'use strict';
'use strict'
module.exports = function (results/*, config*/) {
return JSON.stringify(results, undefined, 2);
};
return JSON.stringify(results, undefined, 2)
}

View File

@ -1,10 +1,10 @@
'use strict';
'use strict'
/**
* @type {Chalk.ChalkModule}
*/
const chalk = require('chalk');
const _ = require('lodash');
const table = require('text-table');
const chalk = require('chalk')
const _ = require('lodash')
const table = require('text-table')
/**
* Given a word and a count, append an s if count is not one.
@ -13,51 +13,51 @@ const table = require('text-table');
* @returns {string} The original word with an s on the end if count is not one.
*/
function pluralize(word, count) {
return count === 1 ? word : word + 's';
return count === 1 ? word : word + 's'
}
/**
* @param {number} line
* @return {string}
*/
function lineText(line) {
return line < 1 ? '' : line;
return line < 1 ? '' : line
}
module.exports = function (results) {
results = _.groupBy(results, 'file');
results = _.groupBy(results, 'file')
let output = '\n';
let total = 0;
let errors = 0;
let warnings = 0;
let infos = 0;
let summaryColor = 'cyan';
let output = '\n'
let total = 0
let errors = 0
let warnings = 0
let infos = 0
let summaryColor = 'cyan'
_.forEach(results, (result, k) => {
const messages = result;
const messages = result
if (messages.length === 0) {
return;
return
}
total += messages.length;
output += chalk.underline(k) + '\n';
total += messages.length
output += chalk.underline(k) + '\n'
output += table(
messages.map(message => {
let messageType;
let messageType
if (message.level === 'ERROR') {
messageType = chalk.red('error');
summaryColor = 'red';
errors++;
messageType = chalk.red('error')
summaryColor = 'red'
errors++
} else if (message.level === 'WARN') {
messageType = chalk.yellow('warning');
summaryColor = 'yellow';
warnings++;
messageType = chalk.yellow('warning')
summaryColor = 'yellow'
warnings++
} else {
messageType = chalk.cyan('info');
infos++;
messageType = chalk.cyan('info')
infos++
}
return [
@ -67,14 +67,14 @@ module.exports = function (results) {
messageType,
message.msg.replace(/\.$/, ''),
chalk.gray(message.ruleId || '')
];
]
}),
{
align: ['', 'r', 'l'],
stringLength: str => chalk.stripColor(str).length
}
).split('\n').map(el => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.gray(p1 + ':' + p2))).join('\n') + '\n\n';
});
).split('\n').map(el => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.gray(p1 + ':' + p2))).join('\n') + '\n\n'
})
if (total > 0) {
output += chalk[summaryColor].bold([
@ -82,8 +82,8 @@ module.exports = function (results) {
' (', errors, pluralize(' error', errors), ', ',
warnings, pluralize(' warning', warnings), ', ',
infos, pluralize(' info', infos), ')\n'
].join(''));
].join(''))
}
return total > 0 ? output : '';
};
return total > 0 ? output : ''
}

View File

@ -1,6 +1,6 @@
'use strict';
const fs = require('fs');
const path = require('path');
'use strict'
const fs = require('fs')
const path = require('path')
/**
* @param {string} source
@ -9,19 +9,19 @@ const path = require('path');
*/
function isStale(source, target) {
if (!fs.existsSync(target)) {
return true;
return true
}
const sourceTime = fs.statSync(source).mtime;
const targetTime = fs.statSync(target).mtime;
return sourceTime.getTime() > targetTime.getTime();
const sourceTime = fs.statSync(source).mtime
const targetTime = fs.statSync(target).mtime
return sourceTime.getTime() > targetTime.getTime()
}
function createRelativeReadFileSync(baseFile) {
const basePath = path.dirname(baseFile);
return filename => fs.readFileSync(path.resolve(basePath, filename));
const basePath = path.dirname(baseFile)
return filename => fs.readFileSync(path.resolve(basePath, filename))
}
module.exports = {
isStale,
createRelativeReadFileSync
};
}

View File

@ -2,16 +2,16 @@
* @fileoverview Options configuration for optionator.
* @author idok
*/
'use strict';
'use strict'
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const optionator = require('optionator');
const pkg = require('../package.json');
const reactDOMSupport = require('./reactDOMSupport');
const reactNativeSupport = require('./reactNativeSupport');
const optionator = require('optionator')
const pkg = require('../package.json')
const reactDOMSupport = require('./reactDOMSupport')
const reactNativeSupport = require('./reactNativeSupport')
//------------------------------------------------------------------------------
// Initialization and Public Interface
@ -127,4 +127,4 @@ $ rt <filename|glob> [<filename|glob> ...] [<args>]`,
default: 'false',
description: 'Automatically bind event handlers to components'
}]
});
})

View File

@ -1,12 +1,12 @@
'use strict';
'use strict'
const ver0_12_0 = ['a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'data',
'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'menuitem', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr', 'circle', 'defs', 'ellipse', 'g', 'line', 'linearGradient', 'mask', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'stop', 'svg', 'text', 'tspan'];
const ver0_11_2 = ['a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'menuitem', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr', 'circle', 'defs', 'ellipse', 'g', 'line', 'linearGradient', 'mask', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'stop', 'svg', 'text', 'tspan', 'injection'];
const ver0_11_0 = ['a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'menuitem', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr', 'circle', 'defs', 'ellipse', 'g', 'line', 'linearGradient', 'mask', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'stop', 'svg', 'text', 'tspan', 'injection'];
const ver0_10_0 = ['a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'menuitem', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr', 'circle', 'defs', 'g', 'line', 'linearGradient', 'path', 'polygon', 'polyline', 'radialGradient', 'rect', 'stop', 'svg', 'text', 'injection'];
'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'menuitem', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr', 'circle', 'defs', 'ellipse', 'g', 'line', 'linearGradient', 'mask', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'stop', 'svg', 'text', 'tspan']
const ver0_11_2 = ['a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'menuitem', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr', 'circle', 'defs', 'ellipse', 'g', 'line', 'linearGradient', 'mask', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'stop', 'svg', 'text', 'tspan', 'injection']
const ver0_11_0 = ['a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'menuitem', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr', 'circle', 'defs', 'ellipse', 'g', 'line', 'linearGradient', 'mask', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'stop', 'svg', 'text', 'tspan', 'injection']
const ver0_10_0 = ['a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'menuitem', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr', 'circle', 'defs', 'g', 'line', 'linearGradient', 'path', 'polygon', 'polyline', 'radialGradient', 'rect', 'stop', 'svg', 'text', 'injection']
const svg = ['a', 'altGlyph', 'altGlyphDef', 'altGlyphItem', 'animate', 'animateMotion', 'animateTransform', 'circle', 'clipPath', 'color-profile', 'cursor', 'defs', 'desc', 'ellipse', 'feBlend', 'g', 'image', 'line',
'linearGradient', 'marker', 'mask', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'stop', 'svg', 'text', 'tref', 'tspan', 'use'];
const v12_svg = ver0_12_0.concat(svg);
'linearGradient', 'marker', 'mask', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', 'rect', 'stop', 'svg', 'text', 'tref', 'tspan', 'use']
const v12_svg = ver0_12_0.concat(svg)
const versions = {
'15.0.1': v12_svg,
@ -21,5 +21,5 @@ const versions = {
'0.11.0': ver0_11_0,
'0.10.0': ver0_10_0,
default: '0.14.0'
};
module.exports = versions;
}
module.exports = versions

View File

@ -1,6 +1,6 @@
'use strict';
'use strict'
const ver0_9_0 = ['ActivityIndicatorIOS', 'DatePickerIOS', 'Image', 'ListView', 'MapView', 'Navigator', 'NavigatorIOS', 'PickerIOS', 'ScrollView', 'SliderIOS', 'SwitchIOS', 'TabBarIOS', 'Text', 'TextInput', 'TouchableHighlight', 'TouchableOpacity', 'TouchableWithoutFeedback', 'View', 'WebView'];
const ver0_9_0 = ['ActivityIndicatorIOS', 'DatePickerIOS', 'Image', 'ListView', 'MapView', 'Navigator', 'NavigatorIOS', 'PickerIOS', 'ScrollView', 'SliderIOS', 'SwitchIOS', 'TabBarIOS', 'Text', 'TextInput', 'TouchableHighlight', 'TouchableOpacity', 'TouchableWithoutFeedback', 'View', 'WebView']
const versions = {
'0.9.0': {
@ -14,6 +14,6 @@ const versions = {
components: ver0_9_0
},
default: '0.9.0'
};
}
module.exports = versions;
module.exports = versions

View File

@ -1,4 +1,4 @@
'use strict';
'use strict'
const native = {
'0.9.0': {
@ -21,9 +21,9 @@ const native = {
Separator: {prop: 'renderSeparator', arguments: ['sectionID', 'rowID', 'adjacentRowHighlighted']}
}
}
};
}
module.exports = {
native,
dom: {}
};
}

View File

@ -1,5 +1,5 @@
'use strict';
const _ = require('lodash');
'use strict'
const _ = require('lodash')
/**
* @param {Context} context
@ -11,9 +11,9 @@ function shouldUseCreateElement(context) {
case '0.11.1':
case '0.11.0':
case '0.10.0':
return false;
return false
default:
return true;
return true
}
}
@ -22,24 +22,24 @@ const reactSupportedAttributes = ['accept', 'acceptCharset', 'accessKey', 'actio
'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'];
const classNameProp = 'className';
const attributesMapping = {'class': classNameProp, 'rt-class': classNameProp, 'for': 'htmlFor'}; //eslint-disable-line quote-props
'style', 'tabIndex', 'target', 'title', 'type', 'useMap', 'value', 'width', 'wmode']
const classNameProp = 'className'
const attributesMapping = {'class': classNameProp, 'rt-class': classNameProp, 'for': 'htmlFor'} //eslint-disable-line quote-props
_.forEach(reactSupportedAttributes, attributeReactName => {
if (attributeReactName !== attributeReactName.toLowerCase()) {
attributesMapping[attributeReactName.toLowerCase()] = attributeReactName;
attributesMapping[attributeReactName.toLowerCase()] = attributeReactName
}
});
})
const htmlSelfClosingTags = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
const htmlSelfClosingTags = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']
const templateAMDTemplate = _.template("define(<%= name ? '\"'+name + '\", ' : '' %>[<%= requirePaths %>], function (<%= AMDArguments %>) {\n'use strict';\n<%= AMDSubstitutions %>return <%= renderFunction %>;\n});");
const templateCommonJSTemplate = _.template("'use strict';\n<%= vars %>\nmodule.exports = <%= renderFunction %>;\n");
const templateES6Template = _.template('<%= vars %>\nexport default <%= renderFunction %>\n');
const templatePJSTemplate = _.template('var <%= name %> = <%= renderFunction %>');
const templateTypescriptTemplate = _.template('<%= vars %>\nexport = <%= renderFunction %>;\n');
const templateJSRTTemplate = _.template('<%= renderFunction %>');
const templateAMDTemplate = _.template("define(<%= name ? '\"'+name + '\", ' : '' %>[<%= requirePaths %>], function (<%= AMDArguments %>) {\n'use strict';\n<%= AMDSubstitutions %>return <%= renderFunction %>;\n});")
const templateCommonJSTemplate = _.template("'use strict';\n<%= vars %>\nmodule.exports = <%= renderFunction %>;\n")
const templateES6Template = _.template('<%= vars %>\nexport default <%= renderFunction %>\n')
const templatePJSTemplate = _.template('var <%= name %> = <%= renderFunction %>')
const templateTypescriptTemplate = _.template('<%= vars %>\nexport = <%= renderFunction %>;\n')
const templateJSRTTemplate = _.template('<%= renderFunction %>')
const templates = {
amd: templateAMDTemplate,
@ -48,27 +48,27 @@ const templates = {
es6: templateES6Template,
none: templatePJSTemplate,
jsrt: templateJSRTTemplate
};
}
const isImportAsterisk = _.matches({member: '*'});
const defaultCase = _.constant(true);
const isImportAsterisk = _.matches({member: '*'})
const defaultCase = _.constant(true)
const buildImportTypeScript = _.cond([
[isImportAsterisk, d => `import ${d.alias} = require('${d.moduleName}');`],
[_.matches({member: 'default'}), d => `import ${d.alias} from '${d.moduleName}';`],
[defaultCase, d => `import { ${d.member} as ${d.alias} } from '${d.moduleName}';`]
]);
])
const buildImportES6 = _.cond([
[isImportAsterisk, d => `import * as ${d.alias} from '${d.moduleName}';`],
[_.matches({member: 'default'}), d => `import ${d.alias} from '${d.moduleName}';`],
[defaultCase, d => `import { ${d.member} as ${d.alias} } from '${d.moduleName}';`]
]);
])
const buildImportCommonJS = _.cond([
[isImportAsterisk, d => `var ${d.alias} = require('${d.moduleName}');`],
[defaultCase, d => `var ${d.alias} = require('${d.moduleName}').${d.member};`]
]);
])
const buildImport = {
typescript: buildImportTypeScript,
@ -77,7 +77,7 @@ const buildImport = {
amd: buildImportCommonJS,
none: buildImportCommonJS,
jsrt: buildImportCommonJS
};
}
module.exports = {
htmlSelfClosingTags,
@ -86,4 +86,4 @@ module.exports = {
shouldUseCreateElement,
templates,
buildImport
};
}

View File

@ -1,22 +1,22 @@
'use strict';
const cheerio = require('cheerio');
const _ = require('lodash');
const esprima = require('esprima');
const escodegen = require('escodegen');
const reactDOMSupport = require('./reactDOMSupport');
const reactNativeSupport = require('./reactNativeSupport');
const reactPropTemplates = require('./reactPropTemplates');
const rtError = require('./RTCodeError');
const reactSupport = require('./reactSupport');
const templates = reactSupport.templates;
const utils = require('./utils');
const validateJS = utils.validateJS;
const RTCodeError = rtError.RTCodeError;
'use strict'
const cheerio = require('cheerio')
const _ = require('lodash')
const esprima = require('esprima')
const escodegen = require('escodegen')
const reactDOMSupport = require('./reactDOMSupport')
const reactNativeSupport = require('./reactNativeSupport')
const reactPropTemplates = require('./reactPropTemplates')
const rtError = require('./RTCodeError')
const reactSupport = require('./reactSupport')
const templates = reactSupport.templates
const utils = require('./utils')
const validateJS = utils.validateJS
const RTCodeError = rtError.RTCodeError
const repeatTemplate = _.template('_.map(<%= collection %>,<%= repeatFunction %>.bind(<%= repeatBinds %>))');
const ifTemplate = _.template('((<%= condition %>)?(<%= body %>):null)');
const propsTemplateSimple = _.template('_.assign({}, <%= generatedProps %>, <%= rtProps %>)');
const propsTemplate = _.template('mergeProps( <%= generatedProps %>, <%= rtProps %>)');
const repeatTemplate = _.template('_.map(<%= collection %>,<%= repeatFunction %>.bind(<%= repeatBinds %>))')
const ifTemplate = _.template('((<%= condition %>)?(<%= body %>):null)')
const propsTemplateSimple = _.template('_.assign({}, <%= generatedProps %>, <%= rtProps %>)')
const propsTemplate = _.template('mergeProps( <%= generatedProps %>, <%= rtProps %>)')
const propsMergeFunction = `function mergeProps(inline,external) {
var res = _.assign({},inline,external)
@ -28,43 +28,43 @@ const propsMergeFunction = `function mergeProps(inline,external) {
}
return res;
}
`;
`
const classSetTemplate = _.template('_.transform(<%= classSet %>, function(res, value, key){ if(value){ res.push(key); } }, []).join(" ")');
const classSetTemplate = _.template('_.transform(<%= classSet %>, function(res, value, key){ if(value){ res.push(key); } }, []).join(" ")')
function getTagTemplateString(simpleTagTemplate, shouldCreateElement) {
if (simpleTagTemplate) {
return shouldCreateElement ? 'React.createElement(<%= name %>,<%= props %><%= children %>)' : '<%= name %>(<%= props %><%= children %>)';
return shouldCreateElement ? 'React.createElement(<%= name %>,<%= props %><%= children %>)' : '<%= name %>(<%= props %><%= children %>)'
}
return shouldCreateElement ? 'React.createElement.apply(this, [<%= name %>,<%= props %><%= children %>])' : '<%= name %>.apply(this, [<%= props %><%= children %>])';
return shouldCreateElement ? 'React.createElement.apply(this, [<%= name %>,<%= props %><%= children %>])' : '<%= name %>.apply(this, [<%= props %><%= children %>])'
}
const commentTemplate = _.template(' /* <%= data %> */ ');
const commentTemplate = _.template(' /* <%= data %> */ ')
const repeatAttr = 'rt-repeat';
const ifAttr = 'rt-if';
const classSetAttr = 'rt-class';
const classAttr = 'class';
const scopeAttr = 'rt-scope';
const propsAttr = 'rt-props';
const templateNode = 'rt-template';
const virtualNode = 'rt-virtual';
const includeNode = 'rt-include';
const includeSrcAttr = 'src';
const requireAttr = 'rt-require';
const importAttr = 'rt-import';
const statelessAttr = 'rt-stateless';
const preAttr = 'rt-pre';
const repeatAttr = 'rt-repeat'
const ifAttr = 'rt-if'
const classSetAttr = 'rt-class'
const classAttr = 'class'
const scopeAttr = 'rt-scope'
const propsAttr = 'rt-props'
const templateNode = 'rt-template'
const virtualNode = 'rt-virtual'
const includeNode = 'rt-include'
const includeSrcAttr = 'src'
const requireAttr = 'rt-require'
const importAttr = 'rt-import'
const statelessAttr = 'rt-stateless'
const preAttr = 'rt-pre'
const reactTemplatesSelfClosingTags = [includeNode];
const reactTemplatesSelfClosingTags = [includeNode]
/**
* @param {Options} options
* @return {Options}
*/
function getOptions(options) {
options = options || {};
options = options || {}
const defaultOptions = {
version: false,
force: false,
@ -73,29 +73,29 @@ function getOptions(options) {
lodashImportPath: 'lodash',
native: false,
nativeTargetVersion: reactNativeSupport.default
};
}
const finalOptions = _.defaults({}, options, defaultOptions);
finalOptions.reactImportPath = reactImport(finalOptions);
finalOptions.modules = finalOptions.modules || (finalOptions.native ? 'commonjs' : 'none');
const finalOptions = _.defaults({}, options, defaultOptions)
finalOptions.reactImportPath = reactImport(finalOptions)
finalOptions.modules = finalOptions.modules || (finalOptions.native ? 'commonjs' : 'none')
const defaultPropTemplates = finalOptions.native ?
reactPropTemplates.native[finalOptions.nativeTargetVersion] :
reactPropTemplates.dom[finalOptions.targetVersion];
reactPropTemplates.dom[finalOptions.targetVersion]
finalOptions.propTemplates = _.defaults({}, options.propTemplates, defaultPropTemplates);
return finalOptions;
finalOptions.propTemplates = _.defaults({}, options.propTemplates, defaultPropTemplates)
return finalOptions
}
function reactImport(options) {
if (options.native) {
return reactNativeSupport[options.nativeTargetVersion].react.module;
return reactNativeSupport[options.nativeTargetVersion].react.module
}
if (!options.reactImportPath) {
const isNewReact = _.includes(['0.14.0', '0.15.0', '15.0.0', '15.0.1'], options.targetVersion);
return isNewReact ? 'react' : 'react/addons';
const isNewReact = _.includes(['0.14.0', '0.15.0', '15.0.0', '15.0.1'], options.targetVersion)
return isNewReact ? 'react' : 'react/addons'
}
return options.reactImportPath;
return options.reactImportPath
}
/**
@ -106,66 +106,66 @@ function reactImport(options) {
* @return {string}
*/
function generateInjectedFunc(context, namePrefix, body, params) {
params = params || context.boundParams;
const funcName = namePrefix.replace(',', '') + (context.injectedFunctions.length + 1);
params = params || context.boundParams
const funcName = namePrefix.replace(',', '') + (context.injectedFunctions.length + 1)
const funcText = `function ${funcName}(${params.join(',')}) {
${body}
}
`;
context.injectedFunctions.push(funcText);
return funcName;
`
context.injectedFunctions.push(funcText)
return funcName
}
function generateTemplateProps(node, context) {
let templatePropCount = 0;
const propTemplateDefinition = context.options.propTemplates[node.name];
let templatePropCount = 0
const propTemplateDefinition = context.options.propTemplates[node.name]
const propertiesTemplates = _(node.children)
.map((child, index) => {
let templateProp = null;
let templateProp = null
if (child.name === templateNode) { // Generic explicit template tag
if (!_.has(child.attribs, 'prop')) {
throw RTCodeError.build(context, child, 'rt-template must have a prop attribute');
throw RTCodeError.build(context, child, 'rt-template must have a prop attribute')
}
if (_.filter(child.children, {type: 'tag'}).length !== 1) {
throw RTCodeError.build(context, child, "'rt-template' should have a single non-text element as direct child");
throw RTCodeError.build(context, child, "'rt-template' should have a single non-text element as direct child")
}
const childTemplate = _.find(context.options.propTemplates, {prop: child.attribs.prop}) || {arguments: []};
const childTemplate = _.find(context.options.propTemplates, {prop: child.attribs.prop}) || {arguments: []}
templateProp = {
prop: child.attribs.prop,
arguments: (child.attribs.arguments ? child.attribs.arguments.split(',') : childTemplate.arguments) || []
};
}
} else if (propTemplateDefinition && propTemplateDefinition[child.name]) { // Implicit child template from configuration
templateProp = {
prop: propTemplateDefinition[child.name].prop,
arguments: child.attribs.arguments ? child.attribs.arguments.split(',') : propTemplateDefinition[child.name].arguments
};
}
}
if (templateProp) {
_.assign(templateProp, {childIndex: index - templatePropCount++, content: _.find(child.children, {type: 'tag'})});
_.assign(templateProp, {childIndex: index - templatePropCount++, content: _.find(child.children, {type: 'tag'})})
}
return templateProp;
return templateProp
})
.compact()
.value();
.value()
return _.transform(propertiesTemplates, (props, templateProp) => {
const functionParams = _.values(context.boundParams).concat(templateProp.arguments);
const functionParams = _.values(context.boundParams).concat(templateProp.arguments)
const oldBoundParams = context.boundParams;
context.boundParams = context.boundParams.concat(templateProp.arguments);
const oldBoundParams = context.boundParams
context.boundParams = context.boundParams.concat(templateProp.arguments)
const functionBody = 'return ' + convertHtmlToReact(templateProp.content, context);
context.boundParams = oldBoundParams;
const functionBody = 'return ' + convertHtmlToReact(templateProp.content, context)
context.boundParams = oldBoundParams
const generatedFuncName = generateInjectedFunc(context, templateProp.prop, functionBody, functionParams);
props[templateProp.prop] = genBind(generatedFuncName, _.values(context.boundParams));
const generatedFuncName = generateInjectedFunc(context, templateProp.prop, functionBody, functionParams)
props[templateProp.prop] = genBind(generatedFuncName, _.values(context.boundParams))
// Remove the template child from the children definition.
node.children.splice(templateProp.childIndex, 1);
}, {});
node.children.splice(templateProp.childIndex, 1)
}, {})
}
/**
@ -174,70 +174,70 @@ function generateTemplateProps(node, context) {
* @return {string}
*/
function generateProps(node, context) {
const props = {};
const props = {}
_.forOwn(node.attribs, (val, key) => {
const propKey = reactSupport.attributesMapping[key.toLowerCase()] || key;
const propKey = reactSupport.attributesMapping[key.toLowerCase()] || key
if (props.hasOwnProperty(propKey) && propKey !== reactSupport.classNameProp) {
throw RTCodeError.build(context, node, `duplicate definition of ${propKey} ${JSON.stringify(node.attribs)}`);
throw RTCodeError.build(context, node, `duplicate definition of ${propKey} ${JSON.stringify(node.attribs)}`)
}
if (_.startsWith(key, 'on') && !utils.isStringOnlyCode(val)) {
props[propKey] = handleEventHandler(val, context, node, key);
props[propKey] = handleEventHandler(val, context, node, key)
} else if (key === 'style' && !utils.isStringOnlyCode(val)) {
props[propKey] = handleStyleProp(val, node, context);
props[propKey] = handleStyleProp(val, node, context)
} 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.
const existing = props[propKey] ? `${props[propKey]} + " " + ` : '';
const existing = props[propKey] ? `${props[propKey]} + " " + ` : ''
if (key === classSetAttr) {
props[propKey] = existing + classSetTemplate({classSet: val});
props[propKey] = existing + classSetTemplate({classSet: val})
} else if (key === classAttr || key === reactSupport.classNameProp) {
props[propKey] = existing + utils.convertText(node, context, val.trim());
props[propKey] = existing + utils.convertText(node, context, val.trim())
}
} else if (!_.startsWith(key, 'rt-')) {
props[propKey] = utils.convertText(node, context, val.trim());
props[propKey] = utils.convertText(node, context, val.trim())
}
});
_.assign(props, generateTemplateProps(node, context));
})
_.assign(props, generateTemplateProps(node, context))
// map 'className' back into 'class' for custom elements
if (props[reactSupport.classNameProp] && isCustomElement(node.name)) {
props[classAttr] = props[reactSupport.classNameProp];
delete props[reactSupport.classNameProp];
props[classAttr] = props[reactSupport.classNameProp]
delete props[reactSupport.classNameProp]
}
const propStr = _.map(props, (v, k) => `${JSON.stringify(k)} : ${v}`).join(',');
return `{${propStr}}`;
const propStr = _.map(props, (v, k) => `${JSON.stringify(k)} : ${v}`).join(',')
return `{${propStr}}`
}
function handleEventHandler(val, context, node, key) {
let handlerString;
let handlerString
if (_.startsWith(val, 'this.')) {
if (context.options.autobind) {
handlerString = `${val}.bind(this)`;
handlerString = `${val}.bind(this)`
} else {
throw RTCodeError.build(context, node, "'this.handler' syntax allowed only when the --autobind is on, use {} to return a callback function.");
throw RTCodeError.build(context, node, "'this.handler' syntax allowed only when the --autobind is on, use {} to return a callback function.")
}
} else {
const funcParts = val.split('=>');
const funcParts = val.split('=>')
if (funcParts.length !== 2) {
throw RTCodeError.build(context, node, `when using 'on' events, use lambda '(p1,p2)=>body' notation or 'this.handler'; otherwise use {} to return a callback function. error: [${key}='${val}']`);
throw RTCodeError.build(context, node, `when using 'on' events, use lambda '(p1,p2)=>body' notation or 'this.handler'; otherwise use {} to return a callback function. error: [${key}='${val}']`)
}
const evtParams = funcParts[0].replace('(', '').replace(')', '').trim();
const funcBody = funcParts[1].trim();
let params = context.boundParams;
const evtParams = funcParts[0].replace('(', '').replace(')', '').trim()
const funcBody = funcParts[1].trim()
let params = context.boundParams
if (evtParams.trim() !== '') {
params = params.concat([evtParams.trim()]);
params = params.concat([evtParams.trim()])
}
const generatedFuncName = generateInjectedFunc(context, key, funcBody, params);
handlerString = genBind(generatedFuncName, context.boundParams);
const generatedFuncName = generateInjectedFunc(context, key, funcBody, params)
handlerString = genBind(generatedFuncName, context.boundParams)
}
return handlerString;
return handlerString
}
function genBind(func, args) {
const bindArgs = ['this'].concat(args);
return `${func}.bind(${bindArgs.join(',')})`;
const bindArgs = ['this'].concat(args)
return `${func}.bind(${bindArgs.join(',')})`
}
function handleStyleProp(val, node, context) {
@ -246,17 +246,17 @@ function handleStyleProp(val, node, context) {
.map(_.trim)
.filter(i => _.includes(i, ':'))
.map(i => {
const pair = i.split(':');
const key = pair[0].trim();
const pair = i.split(':')
const key = pair[0].trim()
if (/\{|}/g.test(key)) {
throw RTCodeError.build(context, node, 'style attribute keys cannot contain { } expressions');
throw RTCodeError.build(context, node, 'style attribute keys cannot contain { } expressions')
}
const value = pair.slice(1).join(':').trim();
const parsedKey = /(^-moz-)|(^-o-)|(^-webkit-)/ig.test(key) ? _.upperFirst(_.camelCase(key)) : _.camelCase(key);
return parsedKey + ' : ' + utils.convertText(node, context, value.trim());
const value = pair.slice(1).join(':').trim()
const parsedKey = /(^-moz-)|(^-o-)|(^-webkit-)/ig.test(key) ? _.upperFirst(_.camelCase(key)) : _.camelCase(key)
return parsedKey + ' : ' + utils.convertText(node, context, value.trim())
})
.join(',');
return `{${styleStr}}`;
.join(',')
return `{${styleStr}}`
}
/**
@ -266,19 +266,19 @@ function handleStyleProp(val, node, context) {
*/
function convertTagNameToConstructor(tagName, context) {
if (context.options.native) {
const targetSupport = reactNativeSupport[context.options.nativeTargetVersion];
return _.includes(targetSupport.components, tagName) ? `${targetSupport.reactNative.name}.${tagName}` : tagName;
const targetSupport = reactNativeSupport[context.options.nativeTargetVersion]
return _.includes(targetSupport.components, tagName) ? `${targetSupport.reactNative.name}.${tagName}` : tagName
}
let isHtmlTag = _.includes(reactDOMSupport[context.options.targetVersion], tagName) || isCustomElement(tagName);
let isHtmlTag = _.includes(reactDOMSupport[context.options.targetVersion], tagName) || isCustomElement(tagName)
if (reactSupport.shouldUseCreateElement(context)) {
isHtmlTag = isHtmlTag || tagName.match(/^\w+(-\w+)+$/);
return isHtmlTag ? `'${tagName}'` : tagName;
isHtmlTag = isHtmlTag || tagName.match(/^\w+(-\w+)+$/)
return isHtmlTag ? `'${tagName}'` : tagName
}
return isHtmlTag ? `React.DOM.${tagName}` : tagName;
return isHtmlTag ? `React.DOM.${tagName}` : tagName
}
function isCustomElement(tagName) {
return tagName.match(/^\w+(-\w+)+$/);
return tagName.match(/^\w+(-\w+)+$/)
}
/**
@ -291,11 +291,11 @@ function defaultContext(html, options, reportContext) {
const defaultDefines = [
{moduleName: options.reactImportPath, alias: 'React', member: '*'},
{moduleName: options.lodashImportPath, alias: '_', member: '*'}
];
]
if (options.native) {
const targetSupport = reactNativeSupport[options.nativeTargetVersion];
const targetSupport = reactNativeSupport[options.nativeTargetVersion]
if (targetSupport.reactNative.module !== targetSupport.react.module) {
defaultDefines.splice(0, 0, {moduleName: targetSupport.reactNative.module, alias: targetSupport.reactNative.name, member: '*'});
defaultDefines.splice(0, 0, {moduleName: targetSupport.reactNative.module, alias: targetSupport.reactNative.name, member: '*'})
}
}
return {
@ -305,7 +305,7 @@ function defaultContext(html, options, reportContext) {
options,
defines: options.defines ? _.clone(options.defines) : defaultDefines,
reportContext
};
}
}
/**
@ -313,7 +313,7 @@ function defaultContext(html, options, reportContext) {
* @return {boolean}
*/
function hasNonSimpleChildren(node) {
return _.some(node.children, child => child.type === 'tag' && child.attribs[repeatAttr]);
return _.some(node.children, child => child.type === 'tag' && child.attribs[repeatAttr])
}
/**
@ -322,7 +322,7 @@ function hasNonSimpleChildren(node) {
* @return {string}
*/
function trimHtmlText(text) {
return text.replace(/^[ \f\n\r\t\v\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+|[ \f\n\r\t\v\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+$/g, '');
return text.replace(/^[ \f\n\r\t\v\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+|[ \f\n\r\t\v\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+$/g, '')
}
/**
@ -334,84 +334,84 @@ function convertHtmlToReact(node, context) {
if (node.type === 'tag' || node.type === 'style') {
context = _.defaults({
boundParams: _.clone(context.boundParams)
}, context);
}, context)
if (node.type === 'tag' && node.name === importAttr) {
throw RTCodeError.build(context, node, "'rt-import' must be a toplevel node");
throw RTCodeError.build(context, node, "'rt-import' must be a toplevel node")
}
if (node.type === 'tag' && node.name === includeNode) {
const srcFile = node.attribs[includeSrcAttr];
const srcFile = node.attribs[includeSrcAttr]
if (!srcFile) {
throw RTCodeError.build(context, node, 'rt-include must supply a source attribute');
throw RTCodeError.build(context, node, 'rt-include must supply a source attribute')
}
if (!context.options.readFileSync) {
throw RTCodeError.build(context, node, 'rt-include needs a readFileSync polyfill on options');
throw RTCodeError.build(context, node, 'rt-include needs a readFileSync polyfill on options')
}
try {
context.html = context.options.readFileSync(srcFile);
context.html = context.options.readFileSync(srcFile)
} catch (e) {
console.error(e);
throw RTCodeError.build(context, node, `rt-include failed to read file '${srcFile}'`);
console.error(e)
throw RTCodeError.build(context, node, `rt-include failed to read file '${srcFile}'`)
}
return parseAndConvertHtmlToReact(context.html, context);
return parseAndConvertHtmlToReact(context.html, context)
}
const data = {name: convertTagNameToConstructor(node.name, context)};
const data = {name: convertTagNameToConstructor(node.name, 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[repeatAttr]) {
const arr = node.attribs[repeatAttr].split(' in ');
const arr = node.attribs[repeatAttr].split(' in ')
if (arr.length !== 2) {
throw RTCodeError.build(context, node, `rt-repeat invalid 'in' expression '${node.attribs[repeatAttr]}'`);
throw RTCodeError.build(context, node, `rt-repeat invalid 'in' expression '${node.attribs[repeatAttr]}'`)
}
const repeaterParams = arr[0].split(',').map(s => s.trim());
data.item = repeaterParams[0];
data.index = repeaterParams[1] || `${data.item}Index`;
data.collection = arr[1].trim();
const bindParams = [data.item, data.index];
const repeaterParams = arr[0].split(',').map(s => s.trim())
data.item = repeaterParams[0]
data.index = repeaterParams[1] || `${data.item}Index`
data.collection = arr[1].trim()
const bindParams = [data.item, data.index]
_.forEach(bindParams, param => {
validateJS(param, node, context);
});
validateJS(`(${data.collection})`, node, context);
validateJS(param, node, context)
})
validateJS(`(${data.collection})`, node, context)
_.forEach(bindParams, param => {
if (!_.includes(context.boundParams, param)) {
context.boundParams.push(param);
context.boundParams.push(param)
}
});
})
}
if (node.attribs[scopeAttr]) {
handleScopeAttribute(node, context, data);
handleScopeAttribute(node, context, data)
}
if (node.attribs[ifAttr]) {
validateIfAttribute(node, context, data);
data.condition = node.attribs[ifAttr].trim();
validateIfAttribute(node, context, data)
data.condition = node.attribs[ifAttr].trim()
if (!node.attribs.key && node.name !== virtualNode) {
_.set(node, ['attribs', 'key'], `${node.startIndex}`);
_.set(node, ['attribs', 'key'], `${node.startIndex}`)
}
}
data.props = generateProps(node, context);
data.props = generateProps(node, context)
if (node.attribs[propsAttr]) {
if (data.props === '{}') {
data.props = node.attribs[propsAttr];
data.props = node.attribs[propsAttr]
} else if (!node.attribs.style && !node.attribs.class) {
data.props = propsTemplateSimple({generatedProps: data.props, rtProps: node.attribs[propsAttr]});
data.props = propsTemplateSimple({generatedProps: data.props, rtProps: node.attribs[propsAttr]})
} else {
data.props = propsTemplate({generatedProps: data.props, rtProps: node.attribs[propsAttr]});
data.props = propsTemplate({generatedProps: data.props, rtProps: node.attribs[propsAttr]})
if (!_.includes(context.injectedFunctions, propsMergeFunction)) {
context.injectedFunctions.push(propsMergeFunction);
context.injectedFunctions.push(propsMergeFunction)
}
}
}
if (node.name === virtualNode) {
const invalidAttributes = _.without(_.keys(node.attribs), scopeAttr, ifAttr, repeatAttr);
const invalidAttributes = _.without(_.keys(node.attribs), scopeAttr, ifAttr, repeatAttr)
if (invalidAttributes.length > 0) {
throw RTCodeError.build(context, node, "<rt-virtual> may not contain attributes other than 'rt-scope', 'rt-if' and 'rt-repeat'");
throw RTCodeError.build(context, node, "<rt-virtual> may not contain attributes other than 'rt-scope', 'rt-if' and 'rt-repeat'")
}
// provide a key to virtual node children if missing
@ -420,52 +420,52 @@ function convertHtmlToReact(node, context) {
.reject('attribs.key')
.forEach((child, i) => {
if (child.type === 'tag' && child.name !== virtualNode) {
_.set(child, ['attribs', 'key'], `${node.startIndex}${i}`);
_.set(child, ['attribs', 'key'], `${node.startIndex}${i}`)
}
});
})
}
}
const children = _.map(node.children, child => {
const code = convertHtmlToReact(child, context);
validateJS(code, child, context);
return code;
});
const code = convertHtmlToReact(child, context)
validateJS(code, child, context)
return code
})
data.children = utils.concatChildren(children);
data.children = utils.concatChildren(children)
if (node.name === virtualNode) { //eslint-disable-line wix-editor/prefer-ternary
data.body = `[${_.compact(children).join(',')}]`;
data.body = `[${_.compact(children).join(',')}]`
} else {
data.body = _.template(getTagTemplateString(!hasNonSimpleChildren(node), reactSupport.shouldUseCreateElement(context)))(data);
data.body = _.template(getTagTemplateString(!hasNonSimpleChildren(node), reactSupport.shouldUseCreateElement(context)))(data)
}
if (node.attribs[scopeAttr]) {
const functionBody = _.values(data.innerScope.innerMapping).join('\n') + `return ${data.body}`;
const generatedFuncName = generateInjectedFunc(context, 'scope' + data.innerScope.scopeName, functionBody, _.keys(data.innerScope.outerMapping));
data.body = `${generatedFuncName}.apply(this, [${_.values(data.innerScope.outerMapping).join(',')}])`;
const functionBody = _.values(data.innerScope.innerMapping).join('\n') + `return ${data.body}`
const generatedFuncName = generateInjectedFunc(context, 'scope' + data.innerScope.scopeName, functionBody, _.keys(data.innerScope.outerMapping))
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' + _.upperFirst(data.item), 'return ' + data.body);
data.repeatBinds = ['this'].concat(_.reject(context.boundParams, p => p === data.item || p === data.index || data.innerScope && p in data.innerScope.innerMapping));
data.body = repeatTemplate(data);
data.repeatFunction = generateInjectedFunc(context, 'repeat' + _.upperFirst(data.item), 'return ' + data.body)
data.repeatBinds = ['this'].concat(_.reject(context.boundParams, p => p === data.item || p === data.index || data.innerScope && p in data.innerScope.innerMapping))
data.body = repeatTemplate(data)
}
if (node.attribs[ifAttr]) {
data.body = ifTemplate(data);
data.body = ifTemplate(data)
}
return data.body;
return data.body
} else if (node.type === 'comment') {
const sanitizedComment = node.data.split('*/').join('* /');
return commentTemplate({data: sanitizedComment});
const sanitizedComment = node.data.split('*/').join('* /')
return commentTemplate({data: sanitizedComment})
} else if (node.type === 'text') {
const parentNode = node.parent;
const overrideNormalize = parentNode !== undefined && (parentNode.name === 'pre' || parentNode.name === 'textarea' || _.has(parentNode.attribs, preAttr));
const normalizeWhitespaces = context.options.normalizeHtmlWhitespace && !overrideNormalize;
const text = node.data;
return trimHtmlText(text) ? utils.convertText(node, context, text, normalizeWhitespaces) : '';
const parentNode = node.parent
const overrideNormalize = parentNode !== undefined && (parentNode.name === 'pre' || parentNode.name === 'textarea' || _.has(parentNode.attribs, preAttr))
const normalizeWhitespaces = context.options.normalizeHtmlWhitespace && !overrideNormalize
const text = node.data
return trimHtmlText(text) ? utils.convertText(node, context, text, normalizeWhitespaces) : ''
}
}
@ -490,21 +490,21 @@ function parseScopeSyntax(text) {
//
// regex = capture(expression) + as + capture(id) + optional_spaces + semicolon + optional_spaces
const regex = RegExp("((?:(?:\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"|'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'|[^\"']*?))*?) as(?: )+([$_a-zA-Z]+[$_a-zA-Z0-9]*)(?: )*(?:;|$)(?: )*", 'g');
const res = [];
const regex = RegExp("((?:(?:\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"|'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'|[^\"']*?))*?) as(?: )+([$_a-zA-Z]+[$_a-zA-Z0-9]*)(?: )*(?:;|$)(?: )*", 'g')
const res = []
do {
const idx = regex.lastIndex;
const match = regex.exec(text);
const idx = regex.lastIndex
const match = regex.exec(text)
if (regex.lastIndex === idx || match === null) {
throw text.substr(idx);
throw text.substr(idx)
}
if (match.index === regex.lastIndex) {
regex.lastIndex++;
regex.lastIndex++
}
res.push({expression: match[1].trim(), identifier: match[2]});
} while (regex.lastIndex < text.length);
res.push({expression: match[1].trim(), identifier: match[2]})
} while (regex.lastIndex < text.length)
return res;
return res
}
function handleScopeAttribute(node, context, data) {
@ -512,110 +512,110 @@ function handleScopeAttribute(node, context, data) {
scopeName: '',
innerMapping: {},
outerMapping: {}
};
}
data.innerScope.outerMapping = _.zipObject(context.boundParams, context.boundParams);
data.innerScope.outerMapping = _.zipObject(context.boundParams, context.boundParams)
let scopes;
let scopes
try {
scopes = parseScopeSyntax(node.attribs[scopeAttr]);
scopes = parseScopeSyntax(node.attribs[scopeAttr])
} catch (scopePart) {
throw RTCodeError.build(context, node, `invalid scope part '${scopePart}'`);
throw RTCodeError.build(context, node, `invalid scope part '${scopePart}'`)
}
scopes.forEach(({expression, identifier}) => {
validateJS(identifier, node, context);
validateJS(identifier, node, context)
// this adds both parameters to the list of parameters passed further down
// the scope chain, as well as variables that are locally bound before any
// function call, as with the ones we generate for rt-scope.
if (!_.includes(context.boundParams, identifier)) {
context.boundParams.push(identifier);
context.boundParams.push(identifier)
}
data.innerScope.scopeName += _.upperFirst(identifier);
data.innerScope.innerMapping[identifier] = `var ${identifier} = ${expression};`;
validateJS(data.innerScope.innerMapping[identifier], node, context);
});
data.innerScope.scopeName += _.upperFirst(identifier)
data.innerScope.innerMapping[identifier] = `var ${identifier} = ${expression};`
validateJS(data.innerScope.innerMapping[identifier], node, context)
})
}
function validateIfAttribute(node, context, data) {
const innerMappingKeys = _.keys(data.innerScope && data.innerScope.innerMapping || {});
let ifAttributeTree = null;
const innerMappingKeys = _.keys(data.innerScope && data.innerScope.innerMapping || {})
let ifAttributeTree = null
try {
ifAttributeTree = esprima.parse(node.attribs[ifAttr]);
ifAttributeTree = esprima.parse(node.attribs[ifAttr])
} catch (e) {
throw new RTCodeError(e.message, e.index, -1);
throw new RTCodeError(e.message, e.index, -1)
}
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]);
throw RTCodeError.buildFormat(context, node, "invalid scope mapping used in if part '%s'", node.attribs[ifAttr])
}
} else {
throw RTCodeError.buildFormat(context, node, "invalid if part '%s'", node.attribs[ifAttr]);
throw RTCodeError.buildFormat(context, node, "invalid if part '%s'", node.attribs[ifAttr])
}
}
function handleSelfClosingHtmlTags(nodes) {
return _.flatMap(nodes, node => {
let externalNodes = [];
node.children = handleSelfClosingHtmlTags(node.children);
let externalNodes = []
node.children = handleSelfClosingHtmlTags(node.children)
if (node.type === 'tag' && (_.includes(reactSupport.htmlSelfClosingTags, node.name) ||
_.includes(reactTemplatesSelfClosingTags, node.name))) {
externalNodes = _.filter(node.children, {type: 'tag'});
_.forEach(externalNodes, i => {i.parent = node;});
node.children = _.reject(node.children, {type: 'tag'});
externalNodes = _.filter(node.children, {type: 'tag'})
_.forEach(externalNodes, i => {i.parent = node})
node.children = _.reject(node.children, {type: 'tag'})
}
return [node].concat(externalNodes);
});
return [node].concat(externalNodes)
})
}
function handleRequire(tag, context) {
let moduleName;
let alias;
let member;
let moduleName
let alias
let member
if (tag.children.length) {
throw RTCodeError.build(context, tag, `'${requireAttr}' may have no children`);
throw RTCodeError.build(context, tag, `'${requireAttr}' may have no children`)
} else if (tag.attribs.dependency && tag.attribs.as) {
moduleName = tag.attribs.dependency;
member = '*';
alias = tag.attribs.as;
moduleName = tag.attribs.dependency
member = '*'
alias = tag.attribs.as
}
if (!moduleName) {
throw RTCodeError.build(context, tag, `'${requireAttr}' needs 'dependency' and 'as' attributes`);
throw RTCodeError.build(context, tag, `'${requireAttr}' needs 'dependency' and 'as' attributes`)
}
context.defines.push({moduleName, member, alias});
context.defines.push({moduleName, member, alias})
}
function handleImport(tag, context) {
let moduleName;
let alias;
let member;
let moduleName
let alias
let member
if (tag.children.length) {
throw RTCodeError.build(context, tag, `'${importAttr}' may have no children`);
throw RTCodeError.build(context, tag, `'${importAttr}' may have no children`)
} else if (tag.attribs.name && tag.attribs.from) {
moduleName = tag.attribs.from;
member = tag.attribs.name;
alias = tag.attribs.as;
moduleName = tag.attribs.from
member = tag.attribs.name
alias = tag.attribs.as
if (!alias) {
if (member === '*') {
throw RTCodeError.build(context, tag, "'*' imports must have an 'as' attribute");
throw RTCodeError.build(context, tag, "'*' imports must have an 'as' attribute")
} else if (member === 'default') {
throw RTCodeError.build(context, tag, "default imports must have an 'as' attribute");
throw RTCodeError.build(context, tag, "default imports must have an 'as' attribute")
}
alias = member;
alias = member
}
}
if (!moduleName) {
throw RTCodeError.build(context, tag, `'${importAttr}' needs 'name' and 'from' attributes`);
throw RTCodeError.build(context, tag, `'${importAttr}' needs 'name' and 'from' attributes`)
}
context.defines.push({moduleName, member, alias});
context.defines.push({moduleName, member, alias})
}
function convertTemplateToReact(html, options) {
const context = require('./context');
return convertRT(html, context, options);
const context = require('./context')
return convertRT(html, context, options)
}
function parseAndConvertHtmlToReact(html, context) {
@ -624,36 +624,36 @@ function parseAndConvertHtmlToReact(html, context) {
lowerCaseAttributeNames: false,
xmlMode: true,
withStartIndices: true
});
utils.validate(context.options, context, context.reportContext, rootNode.root()[0]);
let rootTags = _.filter(rootNode.root()[0].children, {type: 'tag'});
rootTags = handleSelfClosingHtmlTags(rootTags);
})
utils.validate(context.options, context, context.reportContext, rootNode.root()[0])
let rootTags = _.filter(rootNode.root()[0].children, {type: 'tag'})
rootTags = handleSelfClosingHtmlTags(rootTags)
if (!rootTags || rootTags.length === 0) {
throw new RTCodeError('Document should have a root element');
throw new RTCodeError('Document should have a root element')
}
let firstTag = null;
let firstTag = null
_.forEach(rootTags, tag => {
if (tag.name === requireAttr) {
handleRequire(tag, context);
handleRequire(tag, context)
} else if (tag.name === importAttr) {
handleImport(tag, context);
handleImport(tag, context)
} else if (firstTag === null) {
firstTag = tag;
firstTag = tag
if (_.hasIn(tag, ['attribs', statelessAttr])) {
context.stateless = true;
context.stateless = true
}
} else {
throw RTCodeError.build(context, tag, 'Document should have no more than a single root element');
throw RTCodeError.build(context, tag, 'Document should have no more than a single root element')
}
});
})
if (firstTag === null) {
throw RTCodeError.build(context, rootNode.root()[0], 'Document should have a single root element');
throw RTCodeError.build(context, rootNode.root()[0], 'Document should have a single root element')
} else if (firstTag.name === virtualNode) {
throw RTCodeError.build(context, firstTag, `Document should not have <${virtualNode}> as root element`);
throw RTCodeError.build(context, firstTag, `Document should not have <${virtualNode}> as root element`)
} else if (_.includes(_.keys(firstTag.attribs), repeatAttr)) {
throw RTCodeError.build(context, firstTag, "root element may not have a 'rt-repeat' attribute");
throw RTCodeError.build(context, firstTag, "root element may not have a 'rt-repeat' attribute")
}
return convertHtmlToReact(firstTag, context);
return convertHtmlToReact(firstTag, context)
}
/**
@ -663,22 +663,22 @@ function parseAndConvertHtmlToReact(html, context) {
* @return {string}
*/
function convertRT(html, reportContext, options) {
options = getOptions(options);
options = getOptions(options)
const context = defaultContext(html, options, reportContext);
const body = parseAndConvertHtmlToReact(html, context);
const injectedFunctions = context.injectedFunctions.join('\n');
const statelessParams = context.stateless ? 'props, context' : '';
const renderFunction = `function(${statelessParams}) { ${injectedFunctions}return ${body} }`;
const context = defaultContext(html, options, reportContext)
const body = parseAndConvertHtmlToReact(html, context)
const injectedFunctions = context.injectedFunctions.join('\n')
const statelessParams = context.stateless ? 'props, context' : ''
const renderFunction = `function(${statelessParams}) { ${injectedFunctions}return ${body} }`
const requirePaths = _.map(context.defines, d => `"${d.moduleName}"`).join(',');
const requireNames = _.map(context.defines, d => `${d.alias}`).join(',');
const AMDArguments = _.map(context.defines, (d, i) => (d.member === '*' ? `${d.alias}` : `$${i}`)).join(','); //eslint-disable-line
const AMDSubstitutions = _.map(context.defines, (d, i) => (d.member === '*' ? null : `var ${d.alias} = $${i}.${d.member};`)).join('\n'); //eslint-disable-line
const buildImport = reactSupport.buildImport[options.modules] || reactSupport.buildImport.commonjs;
const requires = _.map(context.defines, buildImport).join('\n');
const header = options.flow ? '/* @flow */\n' : '';
const vars = header + requires;
const requirePaths = _.map(context.defines, d => `"${d.moduleName}"`).join(',')
const requireNames = _.map(context.defines, d => `${d.alias}`).join(',')
const AMDArguments = _.map(context.defines, (d, i) => d.member === '*' ? `${d.alias}` : `$${i}`).join(',')
const AMDSubstitutions = _.map(context.defines, (d, i) => d.member === '*' ? null : `var ${d.alias} = $${i}.${d.member};`).join('\n')
const buildImport = reactSupport.buildImport[options.modules] || reactSupport.buildImport.commonjs
const requires = _.map(context.defines, buildImport).join('\n')
const header = options.flow ? '/* @flow */\n' : ''
const vars = header + requires
const data = {
renderFunction,
requireNames,
@ -687,37 +687,37 @@ function convertRT(html, reportContext, options) {
AMDSubstitutions,
vars,
name: options.name
};
let code = templates[options.modules](data);
if (options.modules !== 'typescript' && options.modules !== 'jsrt') {
code = parseJS(code, options);
}
return code;
let code = templates[options.modules](data)
if (options.modules !== 'typescript' && options.modules !== 'jsrt') {
code = parseJS(code, options)
}
return code
}
function parseJS(code, options) {
try {
let tree = esprima.parse(code, {range: true, tokens: true, comment: true, sourceType: 'module'});
let tree = esprima.parse(code, {range: true, tokens: true, comment: true, sourceType: 'module'})
// fix for https://github.com/wix/react-templates/issues/157
// do not include comments for es6 modules due to bug in dependency "escodegen"
// to be removed when https://github.com/estools/escodegen/issues/263 will be fixed
// remove also its test case "test/data/comment.rt.es6.js"
if (options.modules !== 'es6') {
tree = escodegen.attachComments(tree, tree.comments, tree.tokens);
tree = escodegen.attachComments(tree, tree.comments, tree.tokens)
}
return escodegen.generate(tree, {comment: true});
return escodegen.generate(tree, {comment: true})
} catch (e) {
throw new RTCodeError(e.message, e.index, -1);
throw new RTCodeError(e.message, e.index, -1)
}
}
function convertJSRTToJS(text, reportContext, options) {
options = getOptions(options);
options.modules = 'jsrt';
const templateMatcherJSRT = /<template>([^]*?)<\/template>/gm;
const code = text.replace(templateMatcherJSRT, (template, html) => convertRT(html, reportContext, options).replace(/;$/, ''));
options = getOptions(options)
options.modules = 'jsrt'
const templateMatcherJSRT = /<template>([^]*?)<\/template>/gm
const code = text.replace(templateMatcherJSRT, (template, html) => convertRT(html, reportContext, options).replace(/;$/, ''))
return parseJS(code, options);
return parseJS(code, options)
}
module.exports = {
@ -726,4 +726,4 @@ module.exports = {
convertJSRTToJS,
RTCodeError,
normalizeName: utils.normalizeName
};
}

View File

@ -1,4 +1,4 @@
'use strict';
'use strict'
const map = {
/*flex*/
alignItems: 'string', // "enum('flex-start', 'flex-end', 'center', 'stretch')",
@ -42,6 +42,6 @@ const map = {
overflow: 'string', // enum('visible', 'hidden')
tintColor: 'string',
opacity: 'number'
};
}
module.exports = map;
module.exports = map

View File

@ -1,47 +1,47 @@
'use strict';
const css = require('css');
const _ = require('lodash');
const rtnData = require('./rt-style-support-data.js');
'use strict'
const css = require('css')
const _ = require('lodash')
const rtnData = require('./rt-style-support-data.js')
const templateCommonJSTemplate = _.template(
`'use strict';
var style = <%= body %>;
module.exports = style;
`);
`)
function convert(text) {
return templateCommonJSTemplate({body: convertBody(text)});
return templateCommonJSTemplate({body: convertBody(text)})
}
function convertBody(text) {
//source
const obj = css.parse(text, {silent: false});
const result = _.reduce(obj.stylesheet.rules, processRule2, {});
return JSON.stringify(result, undefined, 2);
const obj = css.parse(text, {silent: false})
const result = _.reduce(obj.stylesheet.rules, processRule2, {})
return JSON.stringify(result, undefined, 2)
}
function processRule2(result, rule) {
const name = rule.selectors[0].substring(1);
result[name] = _.reduce(rule.declarations, processDeclaration, {});
return result;
const name = rule.selectors[0].substring(1)
result[name] = _.reduce(rule.declarations, processDeclaration, {})
return result
}
function processDeclaration(result, dec) {
const prop = _.camelCase(dec.property);
result[prop] = convertValue(prop, dec.value);
return result;
const prop = _.camelCase(dec.property)
result[prop] = convertValue(prop, dec.value)
return result
}
function convertValue(p, v) {
if (rtnData[p] === 'string') {
return v;
return v
}
// TODO remove units
return parseInt(v.match(/(\d+)/g)[0], 10);
return parseInt(v.match(/(\d+)/g)[0], 10)
}
module.exports = {
convert,
convertBody
};
}

View File

@ -1,16 +1,16 @@
'use strict';
const _ = require('lodash');
'use strict'
const _ = require('lodash')
/**
* @param {CONTEXT} context
* @return {number}
*/
function printResults(context) {
const warnings = context.getMessages();
const out = require(`./formatters/${context.options.format}`)(warnings);
context.report(out);
const grouped = _.groupBy(warnings, 'level');
return grouped.ERROR ? grouped.ERROR.length : 0;
const warnings = context.getMessages()
const out = require(`./formatters/${context.options.format}`)(warnings)
context.report(out)
const grouped = _.groupBy(warnings, 'level')
return grouped.ERROR ? grouped.ERROR.length : 0
}
module.exports = {printResults};
module.exports = {printResults}

View File

@ -1,9 +1,9 @@
'use strict';
const _ = require('lodash');
const esprima = require('esprima');
const normalizeHtmlWhitespace = require('normalize-html-whitespace');
const rtError = require('./RTCodeError');
const RTCodeError = rtError.RTCodeError;
'use strict'
const _ = require('lodash')
const esprima = require('esprima')
const normalizeHtmlWhitespace = require('normalize-html-whitespace')
const rtError = require('./RTCodeError')
const RTCodeError = rtError.RTCodeError
/**
* @param {string} code
@ -12,9 +12,9 @@ const RTCodeError = rtError.RTCodeError;
*/
function validateJS(code, node, context) {
try {
esprima.parse(code);
esprima.parse(code)
} catch (e) {
throw RTCodeError.build(context, node, e.description);
throw RTCodeError.build(context, node, e.description)
}
}
@ -23,7 +23,7 @@ function validateJS(code, node, context) {
* @return {string}
*/
function normalizeName(name) {
return name.replace(/-/g, '_');
return name.replace(/-/g, '_')
}
/**
@ -31,7 +31,7 @@ function normalizeName(name) {
* @return {boolean}
*/
function isStringOnlyCode(txt) {
return /^\s*\{.*}\s*$/g.test(txt);
return /^\s*\{.*}\s*$/g.test(txt)
//txt = txt.trim();
//return txt.length && txt.charAt(0) === '{' && txt.charAt(txt.length - 1) === '}';
}
@ -42,7 +42,7 @@ function isStringOnlyCode(txt) {
*/
function addIfMissing(array, obj) {
if (!_.includes(array, obj)) {
array.push(obj);
array.push(obj)
}
}
@ -51,14 +51,14 @@ function addIfMissing(array, obj) {
* @return {string}
*/
function concatChildren(children) {
let res = '';
let res = ''
_.forEach(children, child => {
if (child && !_.startsWith(child, ' /*')) {
res += ',';
res += ','
}
res += child;
});
return res;
res += child
})
return res
}
/**
@ -70,15 +70,16 @@ function concatChildren(children) {
*/
function validate(options, context, reportContext, node) {
if (node.type === 'tag' && node.attribs['rt-if'] && !node.attribs.key) {
const loc = rtError.getNodeLoc(context, node);
reportContext.warn('rt-if without a key', options.fileName, loc.pos.line, loc.pos.col, loc.start, loc.end);
const 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.type === 'tag' && node.attribs['rt-require'] && (node.attribs.dependency || node.attribs.as)) {
const loc = rtError.getNodeLoc(context, node);
reportContext.warn("'rt-require' is obsolete, use 'rt-import' instead", options.fileName, loc.pos.line, loc.pos.col, loc.start, loc.end);
const loc = rtError.getNodeLoc(context, node)
reportContext.warn("'rt-require' is obsolete, use 'rt-import' instead", options.fileName, loc.pos.line, loc.pos.col, loc.start, loc.end)
}
// TODO check for duplicate import/require
if (node.children) {
node.children.forEach(validate.bind(this, options, context, reportContext));
node.children.forEach(validate.bind(this, options, context, reportContext))
}
}
@ -89,51 +90,51 @@ function validate(options, context, reportContext, node) {
*/
function usesScopeName(scopeNames, node) {
function usesScope(root) {
return usesScopeName(scopeNames, root);
return usesScopeName(scopeNames, root)
}
if (_.isEmpty(scopeNames)) {
return false;
return false
}
// rt-if="x"
if (node.type === 'Identifier') {
return _.includes(scopeNames, node.name);
return _.includes(scopeNames, node.name)
}
// rt-if="e({key1: value1})"
if (node.type === 'Property') {
return usesScope(node.value);
return usesScope(node.value)
}
// rt-if="e.x" or rt-if="e1[e2]"
if (node.type === 'MemberExpression') {
return node.computed ? usesScope(node.object) || usesScope(node.property) : usesScope(node.object);
return node.computed ? usesScope(node.object) || usesScope(node.property) : usesScope(node.object)
}
// rt-if="!e"
if (node.type === 'UnaryExpression') {
return usesScope(node.argument);
return usesScope(node.argument)
}
// rt-if="e1 || e2" or rt-if="e1 | e2"
if (node.type === 'LogicalExpression' || node.type === 'BinaryExpression') {
return usesScope(node.left) || usesScope(node.right);
return usesScope(node.left) || usesScope(node.right)
}
// rt-if="e1(e2, ... eN)"
if (node.type === 'CallExpression') {
return usesScope(node.callee) || _.some(node.arguments, usesScope);
return usesScope(node.callee) || _.some(node.arguments, usesScope)
}
// rt-if="f({e1: e2})"
if (node.type === 'ObjectExpression') {
return _.some(node.properties, usesScope);
return _.some(node.properties, usesScope)
}
// rt-if="e1[e2]"
if (node.type === 'ArrayExpression') {
return _.some(node.elements, usesScope);
return _.some(node.elements, usesScope)
}
return false;
return false
}
/**
* @const
*/
const curlyMap = {'{': 1, '}': -1};
const curlyMap = {'{': 1, '}': -1}
/**
* @typedef {{boundParams: Array.<string>, injectedFunctions: Array.<string>, html: string, options: *}} Context
@ -152,39 +153,39 @@ const curlyMap = {'{': 1, '}': -1};
*/
function convertText(node, context, txt, normalizeWhitespaces) {
function jsonText(text) {
return JSON.stringify(normalizeWhitespaces ? normalizeHtmlWhitespace(text) : text);
return JSON.stringify(normalizeWhitespaces ? normalizeHtmlWhitespace(text) : text)
}
let res = '';
let first = true;
const concatChar = node.type === 'text' ? ',' : '+';
let res = ''
let first = true
const concatChar = node.type === 'text' ? ',' : '+'
while (_.includes(txt, '{')) {
const start = txt.indexOf('{');
const pre = txt.substr(0, start);
const start = txt.indexOf('{')
const pre = txt.substr(0, start)
if (pre) {
res += (first ? '' : concatChar) + jsonText(pre);
first = false;
res += (first ? '' : concatChar) + jsonText(pre)
first = false
}
let curlyCounter = 1;
let end = start;
let curlyCounter = 1
let end = start
while (++end < txt.length && curlyCounter > 0) {
curlyCounter += curlyMap[txt.charAt(end)] || 0;
curlyCounter += curlyMap[txt.charAt(end)] || 0
}
if (curlyCounter === 0) {
const needsParens = start !== 0 || end !== txt.length - 1;
res += (first ? '' : concatChar) + (needsParens ? '(' : '') + txt.substr(start + 1, end - start - 2) + (needsParens ? ')' : '');
first = false;
txt = txt.substr(end);
const needsParens = start !== 0 || end !== txt.length - 1
res += (first ? '' : concatChar) + (needsParens ? '(' : '') + txt.substr(start + 1, end - start - 2) + (needsParens ? ')' : '')
first = false
txt = txt.substr(end)
} else {
throw RTCodeError.build(context, node, `Failed to parse text '${txt}'`);
throw RTCodeError.build(context, node, `Failed to parse text '${txt}'`)
}
}
if (txt) {
res += (first ? '' : concatChar) + jsonText(txt);
res += (first ? '' : concatChar) + jsonText(txt)
}
if (res === '') {
res = 'true';
res = 'true'
}
return res;
return res
}
@ -197,4 +198,4 @@ module.exports = {
validate,
addIfMissing,
convertText
};
}

View File

@ -1,31 +1,31 @@
'use strict';
const fs = require('fs');
const fsUtil = require('../../src/fsUtil');
const path = require('path');
'use strict'
const fs = require('fs')
const fsUtil = require('../../src/fsUtil')
const path = require('path')
module.exports = {
runTests(test, dataPath) {
test('test isStale', t => {
const a = path.join(dataPath, 'a.tmp');
const b = path.join(dataPath, 'b.tmp');
const a = path.join(dataPath, 'a.tmp')
const b = path.join(dataPath, 'b.tmp')
fs.writeFileSync(a, 'actual');
fs.writeFileSync(b, 'actual');
fs.writeFileSync(a, 'actual')
fs.writeFileSync(b, 'actual')
const mtime1 = new Date(1995, 11, 17, 3, 24, 0);
fs.utimesSync(a, mtime1, mtime1);
const mtime1 = new Date(1995, 11, 17, 3, 24, 0)
fs.utimesSync(a, mtime1, mtime1)
const mtime2 = new Date(1995, 11, 17, 3, 24, 1);
fs.utimesSync(b, mtime2, mtime2);
const mtime2 = new Date(1995, 11, 17, 3, 24, 1)
fs.utimesSync(b, mtime2, mtime2)
let actual = fsUtil.isStale(a, b);
t.equal(actual, false);
actual = fsUtil.isStale(b, a);
t.equal(actual, true);
let actual = fsUtil.isStale(a, b)
t.equal(actual, false)
actual = fsUtil.isStale(b, a)
t.equal(actual, true)
fs.unlinkSync(a);
fs.unlinkSync(b);
t.end();
});
fs.unlinkSync(a)
fs.unlinkSync(b)
t.end()
})
}
};
}

View File

@ -1,10 +1,10 @@
'use strict';
const reactTemplates = require('../../src/reactTemplates');
const testUtils = require('./testUtils');
const readFileNormalized = testUtils.readFileNormalized;
const path = require('path');
const fsUtil = require('../../src/fsUtil');
const fs = require('fs');
'use strict'
const reactTemplates = require('../../src/reactTemplates')
const testUtils = require('./testUtils')
const readFileNormalized = testUtils.readFileNormalized
const path = require('path')
const fsUtil = require('../../src/fsUtil')
const fs = require('fs')
module.exports = {
runTests(test, dataPath) {
@ -32,31 +32,31 @@ module.exports = {
'scope-reserved-tokens.rt',
'repeat-literal-collection.rt',
'include.rt'
];
]
files.forEach(testFile => {
const filename = path.join(dataPath, testFile);
const filename = path.join(dataPath, testFile)
const options = {
readFileSync: fsUtil.createRelativeReadFileSync(filename),
modules: 'amd'
};
let actual = '';
let equal = false;
}
let actual = ''
let equal = false
try {
const html = fs.readFileSync(filename).toString();
const expected = testUtils.normalizeHtml(readFileNormalized(filename + '.html'));
const code = reactTemplates.convertTemplateToReact(html, options).replace(/\r/g, '');
actual = testUtils.normalizeHtml(testUtils.codeToHtml(code));
equal = t.equal(actual, expected, `${testFile}`);
const html = fs.readFileSync(filename).toString()
const expected = testUtils.normalizeHtml(readFileNormalized(filename + '.html'))
const code = reactTemplates.convertTemplateToReact(html, options).replace(/\r/g, '')
actual = testUtils.normalizeHtml(testUtils.codeToHtml(code))
equal = t.equal(actual, expected, `${testFile}`)
} catch (e) {
console.log(testFile, e);
t.fail(e);
console.log(testFile, e)
t.fail(e)
}
if (!equal) {
fs.writeFileSync(filename + '.actual.html', actual);
fs.writeFileSync(filename + '.actual.html', actual)
}
});
t.end();
});
})
t.end()
})
}
};
}

View File

@ -1,60 +1,62 @@
'use strict';
const reactTemplates = require('../../src/reactTemplates');
const testUtils = require('./testUtils');
const _ = require('lodash');
const path = require('path');
const RTCodeError = reactTemplates.RTCodeError;
'use strict'
const reactTemplates = require('../../src/reactTemplates')
const testUtils = require('./testUtils')
const _ = require('lodash')
const path = require('path')
const RTCodeError = reactTemplates.RTCodeError
const omitStack = err => _.omit(err, 'stack', 'toIssue');
const omitStack = err => _.omit(err, 'stack', 'toIssue')
module.exports = {
runTests(test, basePath) {
const dataPath = path.resolve(basePath, 'invalid');
const dataPath = path.resolve(basePath, 'invalid')
const invalidFiles = [
{file: 'if-with-scope/invalid-if-scope-1.rt', issue: new RTCodeError("invalid scope mapping used in if part 'this.bar(activeUsers.length)'", 0, 160, 1, 1)},
{file: 'if-with-scope/invalid-if-scope-2.rt', issue: new RTCodeError("invalid scope mapping used in if part 'this.bar[activeUsers || 0]'", 0, 158, 1, 1)},
{file: 'if-with-scope/invalid-if-scope-3.rt', issue: new RTCodeError("invalid scope mapping used in if part 'this.foo + activeUsers.length > this.bar'", 0, 172, 1, 1)},
{file: 'if-with-scope/invalid-if-scope-4.rt', issue: new RTCodeError("invalid scope mapping used in if part 'getCurrentActiveUsers().length'", 0, 170, 1, 1)},
{file: 'if-with-scope/invalid-if-scope-5.rt', issue: new RTCodeError("invalid scope mapping used in if part 'this.bar({activeUsers})'", 0, 155, 1, 1)},
{file: 'invalid-scope.rt', issue: new RTCodeError("invalid scope part 'a in a in a'", 0, 35, 1, 1)},
{file: 'invalid-html.rt', issue: new RTCodeError('Document should have a root element', -1, -1, -1, -1)},
{file: 'invalid-exp.rt', issue: new RTCodeError("Failed to parse text '\n {z\n'", 5, 13, 1, 6)},
{file: 'invalid-lambda.rt', issue: new RTCodeError("when using 'on' events, use lambda '(p1,p2)=>body' notation or 'this.handler'; otherwise use {} to return a callback function. error: [onClick='']", 0, 23, 1, 1)},
{file: 'invalid-autobind.rt', issue: new RTCodeError("'this.handler' syntax allowed only when the --autobind is on, use {} to return a callback function.", 0, 132, 1, 1)},
// {file: 'invalid-js.rt', issue: new RTCodeError('Unexpected token ILLEGAL', 0, 32, 1, 1)}, bug interduced due to scope parsing
{file: 'invalid-single-root.rt', issue: new RTCodeError('Document should have no more than a single root element', 12, 23, 2, 1)},
{file: 'invalid-repeat-1.rt', issue: new RTCodeError("rt-repeat invalid 'in' expression 'a in b in c'", 9, 44, 2, 4)},
{file: 'invalid-repeat-2.rt', issue: new RTCodeError("root element may not have a 'rt-repeat' attribute", 0, 39, 1, 1)},
{file: 'invalid-rt-require-1.rt', issue: new RTCodeError("'rt-require' needs 'dependency' and 'as' attributes", 0, 14, 1, 1)},
{file: 'invalid-rt-require-2.rt', issue: new RTCodeError("'rt-require' may have no children", 0, 32, 1, 1)},
{file: 'invalid-rt-import-1.rt', issue: new RTCodeError("'*' imports must have an 'as' attribute", 0, 36, 1, 1)},
{file: 'invalid-rt-import-2.rt', issue: new RTCodeError("default imports must have an 'as' attribute", 0, 42, 1, 1)},
{file: 'invalid-rt-import-3.rt', issue: new RTCodeError("'rt-import' needs 'name' and 'from' attributes", 0, 13, 1, 1)},
{file: 'invalid-rt-import-4.rt', issue: new RTCodeError("'rt-import' must be a toplevel node", 9, 54, 2, 4)},
{file: 'invalid-rt-template-1.rt', issue: new RTCodeError("'rt-template' should have a single non-text element as direct child", 9, 88, 2, 4)},
{file: 'invalid-rt-template-2.rt', issue: new RTCodeError("'rt-template' should have a single non-text element as direct child", 9, 90, 2, 4)},
{file: 'invalid-brace.rt', issue: new RTCodeError('Unexpected end of input', 128, 163, 5, 11)},
{file: 'invalid-style-1.rt', issue: new RTCodeError('Unexpected token ILLEGAL', 10, 39, 2, 5)},
{file: 'invalid-style-2.rt', issue: new RTCodeError('style attribute keys cannot contain { } expressions', 35, 68, 2, 5)},
{file: 'invalid-virtual-1.rt', issue: new RTCodeError('Document should not have <rt-virtual> as root element', 0, 60, 1, 1)},
{file: 'invalid-virtual-2.rt', issue: new RTCodeError("<rt-virtual> may not contain attributes other than 'rt-scope', 'rt-if' and 'rt-repeat'", 9, 119, 2, 4)}
];
{file: 'sortByDragListItemRowExamples.rt', issue: new RTCodeError('x', 0, 160, 1, 1)}
// {file: 'if-with-scope/invalid-if-scope-1.rt', issue: new RTCodeError("invalid scope mapping used in if part 'this.bar(activeUsers.length)'", 0, 160, 1, 1)},
// {file: 'if-with-scope/invalid-if-scope-2.rt', issue: new RTCodeError("invalid scope mapping used in if part 'this.bar[activeUsers || 0]'", 0, 158, 1, 1)},
// {file: 'if-with-scope/invalid-if-scope-3.rt', issue: new RTCodeError("invalid scope mapping used in if part 'this.foo + activeUsers.length > this.bar'", 0, 172, 1, 1)},
// {file: 'if-with-scope/invalid-if-scope-4.rt', issue: new RTCodeError("invalid scope mapping used in if part 'getCurrentActiveUsers().length'", 0, 170, 1, 1)},
// {file: 'if-with-scope/invalid-if-scope-5.rt', issue: new RTCodeError("invalid scope mapping used in if part 'this.bar({activeUsers})'", 0, 155, 1, 1)},
// {file: 'invalid-scope.rt', issue: new RTCodeError("invalid scope part 'a in a in a'", 0, 35, 1, 1)},
// {file: 'invalid-html.rt', issue: new RTCodeError('Document should have a root element', -1, -1, -1, -1)},
// {file: 'invalid-exp.rt', issue: new RTCodeError("Failed to parse text '\n {z\n'", 5, 13, 1, 6)},
// {file: 'invalid-lambda.rt', issue: new RTCodeError("when using 'on' events, use lambda '(p1,p2)=>body' notation or 'this.handler'; otherwise use {} to return a callback function. error: [onClick='']", 0, 23, 1, 1)},
// {file: 'invalid-autobind.rt', issue: new RTCodeError("'this.handler' syntax allowed only when the --autobind is on, use {} to return a callback function.", 0, 132, 1, 1)},
// // {file: 'invalid-js.rt', issue: new RTCodeError('Unexpected token ILLEGAL', 0, 32, 1, 1)}, bug interduced due to scope parsing
// {file: 'invalid-single-root.rt', issue: new RTCodeError('Document should have no more than a single root element', 12, 23, 2, 1)},
// {file: 'invalid-repeat-1.rt', issue: new RTCodeError("rt-repeat invalid 'in' expression 'a in b in c'", 9, 44, 2, 4)},
// {file: 'invalid-repeat-2.rt', issue: new RTCodeError("root element may not have a 'rt-repeat' attribute", 0, 39, 1, 1)},
// {file: 'invalid-rt-require-1.rt', issue: new RTCodeError("'rt-require' needs 'dependency' and 'as' attributes", 0, 14, 1, 1)},
// {file: 'invalid-rt-require-2.rt', issue: new RTCodeError("'rt-require' may have no children", 0, 32, 1, 1)},
// {file: 'invalid-rt-require-duplicate.rt', issue: new RTCodeError("'rt-import' must be a toplevel node", 9, 54, 2, 4), options: {modules: 'amd'}},
// {file: 'invalid-rt-import-1.rt', issue: new RTCodeError("'*' imports must have an 'as' attribute", 0, 36, 1, 1)},
// {file: 'invalid-rt-import-2.rt', issue: new RTCodeError("default imports must have an 'as' attribute", 0, 42, 1, 1)},
// {file: 'invalid-rt-import-3.rt', issue: new RTCodeError("'rt-import' needs 'name' and 'from' attributes", 0, 13, 1, 1)},
// {file: 'invalid-rt-import-4.rt', issue: new RTCodeError("'rt-import' must be a toplevel node", 9, 54, 2, 4)},
// {file: 'invalid-rt-template-1.rt', issue: new RTCodeError("'rt-template' should have a single non-text element as direct child", 9, 88, 2, 4)},
// {file: 'invalid-rt-template-2.rt', issue: new RTCodeError("'rt-template' should have a single non-text element as direct child", 9, 90, 2, 4)},
// {file: 'invalid-brace.rt', issue: new RTCodeError('Unexpected end of input', 128, 163, 5, 11)},
// {file: 'invalid-style-1.rt', issue: new RTCodeError('Unexpected token ILLEGAL', 10, 39, 2, 5)},
// {file: 'invalid-style-2.rt', issue: new RTCodeError('style attribute keys cannot contain { } expressions', 35, 68, 2, 5)},
// {file: 'invalid-virtual-1.rt', issue: new RTCodeError('Document should not have <rt-virtual> as root element', 0, 60, 1, 1)},
// {file: 'invalid-virtual-2.rt', issue: new RTCodeError("<rt-virtual> may not contain attributes other than 'rt-scope', 'rt-if' and 'rt-repeat'", 9, 119, 2, 4)}
]
test('invalid tests', t => {
t.plan(invalidFiles.length);
t.plan(invalidFiles.length)
invalidFiles.forEach(testFile => {
const filename = path.join(dataPath, testFile.file);
const html = testUtils.readFileNormalized(filename);
let error = null;
const filename = path.join(dataPath, testFile.file)
const html = testUtils.readFileNormalized(filename)
let error = null
try {
reactTemplates.convertTemplateToReact(html);
reactTemplates.convertTemplateToReact(html)
} catch (e) {
error = e;
error = e
}
t.deepEqual(omitStack(error), omitStack(testFile.issue), 'Expect convertTemplateToReact to throw an error');
});
});
t.deepEqual(omitStack(error), omitStack(testFile.issue), 'Expect convertTemplateToReact to throw an error')
})
})
/**
* @param {ERR} err
@ -62,26 +64,26 @@ module.exports = {
*/
function normalizeError(err) {
if (err) {
err.msg = err.msg.replace(/\r/g, '');
err.msg = err.msg.replace(/\r/g, '')
}
return err;
return err
}
test('invalid tests json', t => {
const cli = require('../../src/cli');
const context = require('../../src/context');
t.plan(invalidFiles.length);
const cli = require('../../src/cli')
const context = require('../../src/context')
t.plan(invalidFiles.length)
invalidFiles.forEach(check);
invalidFiles.forEach(check)
function check(testFile) {
context.clear();
const filename = path.join(dataPath, testFile.file);
const options = {format: 'json', force: true};
cli.handleSingleFile(options, filename);
t.deepEqual(normalizeError(context.getMessages()[0]), errorEqualMessage(testFile.issue, filename), `Expect cli to produce valid output messages ${testFile.file}`);
context.clear()
const filename = path.join(dataPath, testFile.file)
const options = {format: 'json', force: true}
cli.handleSingleFile(_.assign(options, testFile.options), filename)
t.deepEqual(normalizeError(context.getMessages()[0]), errorEqualMessage(testFile.issue, filename), `Expect cli to produce valid output messages ${testFile.file}`)
}
});
})
/**
* @typedef {{index: number, line: number, column: number, msg: string, level: string, file: string}} ERR
@ -102,8 +104,8 @@ module.exports = {
msg: err.message,
level: 'ERROR',
file
};
}
}
}
};
}

View File

@ -1,52 +1,53 @@
'use strict';
const _ = require('lodash');
const reactTemplates = require('../../src/reactTemplates');
const testUtils = require('./testUtils');
const readFileNormalized = testUtils.readFileNormalized;
const compareAndWrite = testUtils.compareAndWrite;
const path = require('path');
const context = require('../../src/context');
'use strict'
const _ = require('lodash')
const reactTemplates = require('../../src/reactTemplates')
const testUtils = require('./testUtils')
const readFileNormalized = testUtils.readFileNormalized
const compareAndWrite = testUtils.compareAndWrite
const path = require('path')
const context = require('../../src/context')
module.exports = {
runTests(test, dataPath) {
function check(t, testData) {
const filename = path.join(dataPath, testData.source);
const html = readFileNormalized(filename);
const expected = readFileNormalized(path.join(dataPath, testData.expected));
const actual = reactTemplates.convertTemplateToReact(html, testData.options).replace(/\r/g, '').trim();
compareAndWrite(t, actual, expected, filename);
const filename = path.join(dataPath, testData.source)
const html = readFileNormalized(filename)
const expected = readFileNormalized(path.join(dataPath, testData.expected))
const actual = reactTemplates.convertTemplateToReact(html, testData.options).replace(/\r/g, '').trim()
compareAndWrite(t, actual, expected, filename)
}
function testFiles(t, files, options) {
t.plan(files.length);
t.plan(files.length)
files.forEach(testFile => {
check(t, {
source: testFile,
expected: `${testFile}.js`,
options
});
});
})
})
}
test('rt-if with rt-scope test', t => {
const files = ['if-with-scope/valid-if-scope.rt'];
const options = {modules: 'amd'};
testFiles(t, files, options);
});
const files = ['if-with-scope/valid-if-scope.rt']
const options = {modules: 'amd'}
testFiles(t, files, options)
})
test('conversion test', t => {
const files = ['div.rt', 'test.rt', 'repeat.rt', 'repeat-with-index.rt', 'inputs.rt', 'virtual.rt', 'stateless.rt', 'style-vendor-prefix.rt', 'non-breaking-space.rt'];
const options = {modules: 'amd'};
testFiles(t, files, options);
});
const files = ['div.rt', 'test.rt', 'repeat.rt', 'repeat-with-index.rt', 'inputs.rt', 'virtual.rt', 'stateless.rt', 'style-vendor-prefix.rt', 'non-breaking-space.rt']
const options = {modules: 'amd'}
testFiles(t, files, options)
})
test('autobinding conversion test', t => {
const options = {
modules: 'amd',
autobind: true
};
const files = ['autobind.rt'];
testFiles(t, files, options);
});
}
const files = ['autobind.rt']
testFiles(t, files, options)
})
test('prop template conversion test', t => {
const options = {
@ -56,16 +57,16 @@ module.exports = {
}
},
modules: 'amd'
};
}
const files = [
'simpleTemplate.rt',
'templateInScope.rt',
'implicitTemplate.rt',
'twoTemplates.rt',
'siblingTemplates.rt'
].map(file => path.join('propTemplates', file));
testFiles(t, files, options);
});
].map(file => path.join('propTemplates', file))
testFiles(t, files, options)
})
test('conversion test - native', t => {
const options = {
@ -75,8 +76,8 @@ module.exports = {
}
},
native: true
};
const optionsNew = _.assign({nativeTargetVersion: '0.29.0'}, options);
}
const optionsNew = _.assign({nativeTargetVersion: '0.29.0'}, options)
const files = [
{source: 'native/nativeView.rt', expected: 'native/nativeView.rt.js', options},
@ -85,10 +86,10 @@ module.exports = {
{source: 'native/nativeView.rt', expected: 'native/nativeView.rt.v029.js', options: optionsNew},
{source: 'native/listViewTemplate.rt', expected: 'native/listViewTemplate.rt.v029.js', options: optionsNew},
{source: 'native/listViewAndCustomTemplate.rt', expected: 'native/listViewAndCustomTemplate.rt.v029.js', options: optionsNew}
];
t.plan(files.length);
files.forEach(file => check(t, file));
});
]
t.plan(files.length)
files.forEach(file => check(t, file))
})
test('convert div with all module types', t => {
const files = [
@ -96,26 +97,27 @@ module.exports = {
{source: 'div.rt', expected: 'div.rt.amd.js', options: {modules: 'amd', name: 'div'}},
{source: 'div.rt', expected: 'div.rt.globals.js', options: {modules: 'none', name: 'div'}},
{source: 'div.rt', expected: 'div.rt.es6.js', options: {modules: 'es6'}},
{source: 'div.rt', expected: 'div.rt.typescript.ts', options: {modules: 'typescript'}}
];
t.plan(files.length);
files.forEach(file => check(t, file));
});
{source: 'div.rt', expected: 'div.rt.typescript.ts', options: {modules: 'typescript'}},
{source: 'div.rt', expected: 'div.rt.15.js', options: {targetVersion: '15.0.0', modules: 'amd'}}
]
t.plan(files.length)
files.forEach(file => check(t, file))
})
test('normalize whitespace', t => {
const files = ['whitespace.rt'];
const options = {normalizeHtmlWhitespace: true, modules: 'amd'};
testFiles(t, files, options);
});
const files = ['whitespace.rt']
const options = {normalizeHtmlWhitespace: true, modules: 'amd'}
testFiles(t, files, options)
})
test('convert comment with AMD and ES6 modules', t => {
const files = [
{source: 'comment.rt', expected: 'comment.rt.amd.js', options: {modules: 'amd'}},
{source: 'comment.rt', expected: 'comment.rt.es6.js', options: {modules: 'es6'}}
];
t.plan(files.length);
files.forEach(file => check(t, file));
});
]
t.plan(files.length)
files.forEach(file => check(t, file))
})
test('rt-require with all module types', t => {
const files = [
@ -124,10 +126,10 @@ module.exports = {
{source: 'require.rt', expected: 'require.rt.globals.js', options: {modules: 'none', name: 'div'}},
{source: 'require.rt', expected: 'require.rt.es6.js', options: {modules: 'es6'}},
{source: 'require.rt', expected: 'require.rt.typescript.ts', options: {modules: 'typescript'}}
];
t.plan(files.length);
files.forEach(file => check(t, file));
});
]
t.plan(files.length)
files.forEach(file => check(t, file))
})
test('rt-import with all module types', t => {
const files = [
@ -136,21 +138,21 @@ module.exports = {
{source: 'import.rt', expected: 'import.rt.globals.js', options: {modules: 'none', name: 'div'}},
{source: 'import.rt', expected: 'import.rt.es6.js', options: {modules: 'es6'}},
{source: 'import.rt', expected: 'import.rt.typescript.ts', options: {modules: 'typescript'}}
];
t.plan(files.length);
files.forEach(file => check(t, file));
});
]
t.plan(files.length)
files.forEach(file => check(t, file))
})
test('convert jsrt and test source results', t => {
const files = ['simple.jsrt'];
t.plan(files.length);
const files = ['simple.jsrt']
t.plan(files.length)
files.forEach(file => {
const filename = path.join(dataPath, file);
const js = readFileNormalized(filename);
const expected = readFileNormalized(path.join(dataPath, file.replace('.jsrt', '.js')));
const actual = reactTemplates.convertJSRTToJS(js, context).replace(/\r/g, '').trim();
compareAndWrite(t, actual, expected, filename);
});
});
const filename = path.join(dataPath, file)
const js = readFileNormalized(filename)
const expected = readFileNormalized(path.join(dataPath, file.replace('.jsrt', '.js')))
const actual = reactTemplates.convertJSRTToJS(js, context).replace(/\r/g, '').trim()
compareAndWrite(t, actual, expected, filename)
})
})
}
};
}

View File

@ -1,14 +1,14 @@
'use strict';
const rtStyle = require('../../src/rtStyle');
'use strict'
const rtStyle = require('../../src/rtStyle')
module.exports = {
runTests(test) {
test('test rtStyle', t => {
const text = '.text { background-color: #00346E; padding: 3px; }';
const expected = '{\n "text": {\n "backgroundColor": "#00346E",\n "padding": 3\n }\n}';
const actual = rtStyle.convertBody(text);
t.equal(actual, expected);
t.end();
});
const text = '.text { background-color: #00346E; padding: 3px; }'
const expected = '{\n "text": {\n "backgroundColor": "#00346E",\n "padding": 3\n }\n}'
const actual = rtStyle.convertBody(text)
t.equal(actual, expected)
t.end()
})
}
};
}

View File

@ -1,33 +1,33 @@
'use strict';
const context = require('../../src/context');
const _ = require('lodash');
const path = require('path');
'use strict'
const context = require('../../src/context')
const _ = require('lodash')
const path = require('path')
module.exports = {
runTests(test, dataPath) {
test('test context', t => {
context.clear();
t.equal(context.hasErrors(), false);
context.error('hi', '', 1, 1);
t.equal(context.hasErrors(), true);
context.clear();
t.equal(context.hasErrors(), false);
context.clear()
t.equal(context.hasErrors(), false)
context.error('hi', '', 1, 1)
t.equal(context.hasErrors(), true)
context.clear()
t.equal(context.hasErrors(), false)
t.end();
});
t.end()
})
test('test shell', t => {
const shell = require('../../src/shell');
const newContext = _.cloneDeep(context);
let outputJSON = '';
newContext.options.format = 'json';
newContext.report = function (text) { outputJSON = text; };
let r = shell.printResults(newContext);
t.equal(r, 0);
context.error('hi', '', 1, 1);
r = shell.printResults(newContext);
t.equal(r, 1);
const output = JSON.parse(outputJSON);
const shell = require('../../src/shell')
const newContext = _.cloneDeep(context)
let outputJSON = ''
newContext.options.format = 'json'
newContext.report = function (text) { outputJSON = text }
let r = shell.printResults(newContext)
t.equal(r, 0)
context.error('hi', '', 1, 1)
r = shell.printResults(newContext)
t.equal(r, 1)
const output = JSON.parse(outputJSON)
t.deepEqual(output, [{
column: 1,
endOffset: -1,
@ -37,17 +37,17 @@ module.exports = {
line: 1,
msg: 'hi',
startOffset: -1
}]);
context.clear();
t.end();
});
}])
context.clear()
t.end()
})
test('test shell', t => {
const filename = path.join(dataPath, 'div.rt');
const cli = require('../../src/cli');
const r = cli.execute(`${filename} -r --dry-run`);
t.equal(r, 0);
t.end();
});
const filename = path.join(dataPath, 'div.rt')
const cli = require('../../src/cli')
const r = cli.execute(`${filename} -r --dry-run`)
t.equal(r, 0)
t.end()
})
}
};
}

View File

@ -1,10 +1,11 @@
'use strict';
const test = require('tape');
const path = require('path');
const dataPath = path.resolve(__dirname, '..', 'data');
'use strict'
const test = require('tape')
const path = require('path')
const dataPath = path.resolve(__dirname, '..', 'data')
const specs = ['rt.invalid', 'rt.valid', 'rt-html-valid', 'utils', 'shell', 'rtStyle', 'fsUtil'];
// const specs = ['rt.invalid', 'rt.valid', 'rt-html-valid', 'utils', 'shell', 'rtStyle', 'fsUtil'];
const specs = ['rt.invalid']
specs
.map(file => require(`./${file}.spec`))
.forEach(spec => spec.runTests(test, dataPath));
.forEach(spec => spec.runTests(test, dataPath))

View File

@ -1,11 +1,11 @@
'use strict';
const cheerio = require('cheerio');
const fs = require('fs');
const path = require('path');
const reactTemplates = require('../../src/reactTemplates');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const _ = require('lodash');
'use strict'
const cheerio = require('cheerio')
const fs = require('fs')
const path = require('path')
const reactTemplates = require('../../src/reactTemplates')
const React = require('react')
const ReactDOMServer = require('react-dom/server')
const _ = require('lodash')
/**
* @param {string} html
@ -15,7 +15,7 @@ function normalizeHtml(html) {
return cheerio.load(html, {normalizeWhitespace: true}).html()
.replace(/>\s+/mg, '>')
.replace(/\s+</mg, '<')
.replace(/>\s+</mg, '><');
.replace(/>\s+</mg, '><')
}
/**
@ -26,28 +26,28 @@ function normalizeHtml(html) {
* @return {boolean} whether actual is equal to expected
*/
function compareAndWrite(t, actual, expected, filename) {
t.equal(actual, expected, filename);
t.equal(actual, expected, filename)
if (actual !== expected) {
fs.writeFileSync(`${filename}.actual.js`, actual);
return false;
fs.writeFileSync(`${filename}.actual.js`, actual)
return false
}
return true;
return true
}
function compareNodes(t, a, b, filename) {
_.forEach(a.attribs, (v, k) => {
if (v !== b.attribs[k]) {
console.log(`${v} is not ${b.attribs[k]}`);
console.log(`${v} is not ${b.attribs[k]}`)
}
t.equal(v, b.attribs[k], filename);
});
compareNodesList(t, a.children, b.children);
t.equal(v, b.attribs[k], filename)
})
compareNodesList(t, a.children, b.children)
}
function compareNodesList(t, a, b, filename) {
_.forEach(a, (v, i) => {
compareNodes(t, v, b[i], filename);
});
compareNodes(t, v, b[i], filename)
})
}
/**
@ -55,7 +55,7 @@ function compareNodesList(t, a, b, filename) {
* @return {string}
*/
function readFileNormalized(filename) {
return readFile(filename).replace(/\r/g, '').trim();
return readFile(filename).replace(/\r/g, '').trim()
}
//const dataPath = path.resolve(__dirname, '..', 'data');
@ -64,31 +64,31 @@ function readFileNormalized(filename) {
* @return {string}
*/
function readFile(filename) {
return fs.readFileSync(filename).toString();
return fs.readFileSync(filename).toString()
}
function joinDataPath(fileName) {
const dataPath = path.resolve(__dirname, '..', 'data');
return path.join(dataPath, fileName);
const dataPath = path.resolve(__dirname, '..', 'data')
return path.join(dataPath, fileName)
}
function rtToHtml(rt) {
const code = reactTemplates.convertTemplateToReact(rt).replace(/\r/g, '');
return codeToHtml(code);
const code = reactTemplates.convertTemplateToReact(rt).replace(/\r/g, '')
return codeToHtml(code)
}
function codeToHtml(code) {
const defineMap = {react: React, lodash: _};
const defineMap = {react: React, lodash: _}
//noinspection JSUnusedLocalSymbols
const define = function (requirementsNames, content) { //eslint-disable-line no-unused-vars,func-style
const requirements = _.map(requirementsNames, reqName => defineMap[reqName]);
return content.apply(this, requirements);
};
const requirements = _.map(requirementsNames, reqName => defineMap[reqName])
return content.apply(this, requirements)
}
const comp = React.createFactory(React.createClass({
displayName: 'testClass',
render: eval(code) //eslint-disable-line no-eval
}));
return ReactDOMServer.renderToStaticMarkup(comp());
}))
return ReactDOMServer.renderToStaticMarkup(comp())
}
module.exports = {
@ -99,4 +99,4 @@ module.exports = {
joinDataPath,
rtToHtml,
codeToHtml
};
}

View File

@ -1,6 +1,6 @@
'use strict';
'use strict'
const utils = require('../../src/utils');
const utils = require('../../src/utils')
module.exports = {
runTests(test) {
@ -8,13 +8,13 @@ module.exports = {
const texts = [
{input: '{}', expected: '()'},
{input: "a {'b'}", expected: '"a "+(\'b\')'}
];
t.plan(texts.length);
texts.forEach(check);
]
t.plan(texts.length)
texts.forEach(check)
function check(testData) {
const r = utils.convertText({}, {}, testData.input);
t.equal(r, testData.expected);
const r = utils.convertText({}, {}, testData.input)
t.equal(r, testData.expected)
}
});
})
}
};
}