Skip to content

Instantly share code, notes, and snippets.

@brigand
Forked from anonymous/index.html
Last active August 29, 2015 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brigand/72f31e90bb1bf3a0af88 to your computer and use it in GitHub Desktop.
Save brigand/72f31e90bb1bf3a0af88 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.rawgit.com/zloirock/core-js/master/client/shim.js"></script>
<!-- uncomment for React master
<script src="http://react.zpao.com/builds/master/latest/react.js"></script>
-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/4.7.0/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/4.7.0/theme/solarized.min.css">
<script src="http://fb.me/react-with-addons-0.13.1.js"></script>
<script src="http://www.parsecdn.com/js/parse-1.4.2.min.js"></script>
<script>
Parse.initialize("gHYJ6NIvgNO1kaEX1OgA1WnbCe2Muj5mrCTlKxLP", "DzlycTD5di7CirpgX9AyPTzBcLuf28FTqHtX4sT1");
</script>
<meta charset="utf-8">
<title>JS Bin</title>
<style id="jsbin-css">
html, body {
margin: 0;
height: 100%;
}
body {
display: flex;
padding: 1em;
align-items: stretch;
flex-direction: column;
}
.App {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 95%;
}
.App-section {
display: flex;
flex-direction: column;
align-items: stretch;
flex: 1;
flex-basis: 25em;
overflow: auto;
}
.App-section-input {
flex: 2;
flex-basis: 25em;
}
.App-section > textarea {
flex: 1;
flex-basis: 10em;
font-size: 1.2em;
font-family: monospace;
}
/* ReactJSONInspector */
.json-inspector,
.json-inspector__selection {
font: 14px/1.4 Consolas, monospace;
}
.json-inspector__leaf {
padding-left: 10px;
}
.json-inspector__line {
display: block;
position: relative;
cursor: default;
}
.json-inspector__line:after {
content: '';
position: absolute;
top: 0;
left: -200px;
right: -50px;
bottom: 0;
z-index: -1;
pointer-events: none;
}
.json-inspector__line:hover:after {
background: rgba(0, 0, 0, 0.06);
}
.json-inspector__leaf_composite > .json-inspector__line {
cursor: pointer;
}
.json-inspector__radio,
.json-inspector__flatpath {
display: none;
}
.json-inspector__value {
margin-left: 5px;
}
.json-inspector__search {
min-width: 300px;
margin: 0 10px 10px 0;
padding: 2px;
}
.json-inspector__key {
color: #505050;
}
.json-inspector__value_helper,
.json-inspector__value_null,
.json-inspector__not-found {
color: #b0b0b0;
}
.json-inspector__value_string {
color: #798953;
}
.json-inspector__value_boolean {
color: #75b5aa;
}
.json-inspector__value_number {
color: #d28445;
}
.json-inspector__hl {
background: #ff0;
box-shadow: 0 -1px 0 2px #ff0;
border-radius: 2px;
}
.json-inspector__show-original {
display: inline-block;
padding: 0 6px;
color: #666;
cursor: pointer;
}
.json-inspector__show-original:hover {
color: #111;
}
.json-inspector__show-original:before {
content: '⥂';
}
.json-inspector__show-original:hover:after {
content: ' expand'
}
</style>
</head>
<body>
<div style="margin: auto; font-size: 20vh;" id="loading">Loading <div class="log" style="margin: auto; font-size: 10vh;">...</div></div>
<script src="https://cdn.rawgit.com/anonymous/e67adee9cbb2fc2a26a5/raw/a1df870c2eff4212efc4f262834cc4fe86ecebc8/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/4.7.0/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/4.7.0/mode/javascript/javascript.min.js"></script>
<!-- Utils -->
<script src="https://wzrd.in/standalone/circular-json"></script>
<script src="https://wzrd.in/standalone/lodash.debounce"></script>
<script src="https://www.parsecdn.com/js/parse-react.js"></script>
<!-- React Components -->
<script src="https://cdn.rawgit.com/anonymous/0e3b2440d50a2b7b0cd3/raw/5bff05d9e66803a85d87f4d9bb8be558b77e4a38/ReactJSONInspector.js"></script>
<script src="https://cdn.rawgit.com/ForbesLindesay/react-code-mirror/34629fd97ed48c356fc6462c63ab208b62938fac/standalone.js"></script>
<script id="jsbin-javascript">
'use strict';
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
var EDITOR_PROPS = {
mode: 'javascript',
theme: 'solarized',
lineNumbers: true
};
var HASH_PREFIX = '#/';
var DEFAULT_PLUGIN = 'module.exports = function (babel) {\n return new babel.Transformer("foo-bar", {\n FunctionDeclaration(node, parent) {\n return node;\n }\n });\n};';
var DEFAULT_CODE = 'const z = (x) => y';
function shallowCompareObj(a, b) {
return Object.keys(a).some(function (key) {
return a[key] !== b[key];
});
}
function getDefaultState() {
return {
plugin: DEFAULT_PLUGIN,
code: DEFAULT_CODE,
babelOptions: {}
};
};
var App = (function (_React$Component) {
function App(props) {
_classCallCheck(this, App);
_get(Object.getPrototypeOf(App.prototype), 'constructor', this).call(this, props);
this.state = Object.assign({
resultAst: {},
resultCode: '',
pluginResult: null,
// these come from props.initialData
plugin: null,
code: null,
babelOptions: null
}, props.initialData);
this.updatePlugin = lodash.debounce(this.updatePlugin.bind(this), 300, {
leading: true,
trailing: true,
maxWait: 1000
});
this.updateTransform = lodash.debounce(this.updateTransform.bind(this), 300, {
leading: true,
trailing: true,
maxWait: 1000
});
}
_inherits(App, _React$Component);
_createClass(App, [{
key: 'componentWillMount',
value: function componentWillMount() {
this._lastKnownState = {};
this.updateStuff(this.state);
}
}, {
key: 'componentWillUpdate',
value: function componentWillUpdate(nextProps, nextState) {
var sourceKeysOnly = function sourceKeysOnly(_ref) {
var plugin = _ref.plugin;
var code = _ref.code;
return { plugin: plugin, code: code };
};
var changed = shallowCompareObj(sourceKeysOnly(this.state), sourceKeysOnly(nextState)) || shallowCompareObj(this.state.babelOptions, nextState.babelOptions);
if (changed) {
this.updateStuff(nextState);
}
}
}, {
key: 'updateStuff',
value: function updateStuff(state) {
var pluginResult = this.updatePlugin(state.plugin);
this.updateTransform(Object.assign({}, state, { pluginResult: pluginResult }));
}
}, {
key: 'updatePlugin',
value: function updatePlugin(plugin) {
var makePlugin = new Function('module', plugin);
localStorage.babelPluginThingy = plugin;
var _module = {};
makePlugin(_module);
this.setState({ pluginResult: _module.exports });
return _module.exports;
}
}, {
key: 'updateTransform',
value: function updateTransform(state) {
state = Object.assign({}, this.state || {}, state);
var babelOptions = Object.assign({}, state.babelOptions, {
ast: true,
code: true,
plugins: (state.babelOptions.plugins || []).concat[{
transformer: state.pluginResult,
position: 'before'
}]
});
// attempt to transform
var ast = undefined,
code = undefined;
try {
var result = babel.transform(state.code, babelOptions);
ast = result.ast.program;
code = result.code;
}
// if this fails, just use the code output to place the error
catch (e) {
code = e.message + '\n' + e.stack;
}
var replacer = function replacer(k, v) {
return k[0] === '_' ? undefined : k === 'loc' || k === 'start' || k === 'end' || k === 'range' ? undefined : v;
};
this.setState({ resultCode: code });
this.setState({ resultAst: false });
// todo: optimize?
if (ast) {
var json = JSON.stringify(ast, replacer);
var cleanAst = JSON.parse(json);
this.setState({ resultAst: cleanAst });
this.props.onValid({ code: state.code, plugin: state.plugin, babelOptions: babelOptions });
}
}
}, {
key: 'render',
value: function render() {
var _this = this;
return React.createElement(
'div',
{ className: 'App' },
React.createElement(
'div',
{ className: 'App-section App-section-input' },
React.createElement(
'h2',
null,
'Plugin ',
location.hash.slice(HASH_PREFIX.length)
),
React.createElement(CodeMirrorEditor, _extends({
value: this.state.plugin,
onChange: function (e) {
_this.setState({ plugin: e.target.value });
},
onBlur: function () {
return _this.setState({});
}
}, EDITOR_PROPS))
),
React.createElement(
'div',
{ className: 'App-section App-section-input' },
React.createElement(
'h2',
null,
'Code'
),
React.createElement(CodeMirrorEditor, _extends({
value: this.state.code,
onChange: function (e) {
_this.setState({ code: e.target.value });
},
onBlur: function () {
return _this.setState({});
}
}, EDITOR_PROPS))
),
React.createElement(
'div',
{ className: 'App-section' },
React.createElement(
'h2',
null,
'AST'
),
this.state.resultAst ? React.createElement(ReactJSONInspector, { data: this.state.resultAst }) : 'Failed to compile'
),
React.createElement(
'div',
{ className: 'App-section' },
React.createElement(
'h2',
null,
'Result'
),
React.createElement(
'pre',
null,
this.state.resultCode
)
)
);
}
}], [{
key: 'propTypes',
value: {
initialData: React.PropTypes.object.isRequired,
onValid: React.PropTypes.func.isRequired
},
enumerable: true
}]);
return App;
})(React.Component);
var ParseBabelPlugin = Parse.Object.extend('BabelPlugin');
var backendState = {
activeObject: null,
timesLoaded: 0
};
var backendService = {
_keys: ['code', 'plugin', 'babelOptions'],
assign: function assign(dest, source) {
var get = function get(obj, key) {
return typeof obj.get === 'function' ? obj.get(key) : obj[key];
};
var set = function set(obj, key, value) {
return typeof obj.set === 'function' ? obj.set(key, value) : obj[key] = value;
};
backendService._keys.forEach(function (key) {
set(dest, key, get(source, key));
});
return dest;
},
getMyACL: function getMyACL() {
var acl = new Parse.ACL(Parse.User.current());
acl.setPublicReadAccess(true);
return acl;
},
makeObject: function makeObject() {
var obj = new ParseBabelPlugin();
var acl = this.getMyACL();
obj.setACL(acl);
return backendService.assign(obj, getDefaultState());
},
ensureActiveObject: function ensureActiveObject() {
if (!backendService.activeObject) {
backendService.activeObject = backendService.makeObject();
}
return backendService.activeObject;
},
cloneIfCantWrite: function cloneIfCantWrite(obj) {
var canWrite = obj.getACL().getWriteAccess(Parse.User.current());
if (canWrite) {
return obj;
}
return backendService.assign(backendService.makeObject(), obj);
},
getDataForPage: function getDataForPage(cb) {
var hash = location.hash.slice(HASH_PREFIX.length);
if (!hash) {
backendService.log('Using defaults');
return cb(null, getDefaultState());
}
backendService.log('Requesting BabelPlugin' + hash);
var query = new Parse.Query(ParseBabelPlugin);
return query.get(hash).then(function (obj) {
backendService.log('Found BabelPlugin' + hash + ': ' + JSON.stringify(backendService.assign({}, obj)));
backendService.activeObject = obj;
return obj;
}).then(function (data) {
return cb(null, data);
}, function () {
cb(null, backendService.ensureActiveObject());
});
},
eventuallyRender: function eventuallyRender() {
backendService.getDataForPage(function (err) {
backendService.timesLoaded++;
backendService.renderNow();
});
},
renderNow: function renderNow() {
var simpleData = backendService.assign({}, backendService.ensureActiveObject());
renderApp(simpleData, function onValid(newData) {
backendService.log('onValid');
backendService.eventuallyPersist(newData);
});
},
eventuallyPersist: lodash.debounce(function (data) {
backendService.ensureActiveObject();
backendService.assign(backendService.activeObject, data);
backendService.activeObject = backendService.cloneIfCantWrite(backendService.activeObject);
backendService.activeObject.save().then(function (obj) {
backendService.activeObject = obj;
backendService.updateUrl();
backendService.renderNow();
}, console.error.bind(console));
}, 10000),
updateUrl: function updateUrl() {
var hash = location.hash;
if (backendService.activeObject) {
var id = backendService.activeObject.id;
if (hash !== HASH_PREFIX + id) {
location.hash = HASH_PREFIX + id;
}
}
},
log: function log(msg) {
var logEl = document.getElementById('log');
if (logEl) {
logEl.appendChild(document.createTextNode(msg));
}
console.log(msg);
}
};
function renderApp(data, onValid) {
var app = React.createElement(App, {
initialData: data,
onValid: onValid,
key: backendState.timesLoaded
});
React.render(app, document.body);
}
location.hash = '#/dpsW2RxIxI';
backendService.eventuallyRender();
</script>
<script id="jsbin-source-html" type="text/html"><!DOCTYPE html>
<html>
<head>
<script src="https://cdn.rawgit.com/zloirock/core-js/master/client/shim.js"><\/script>
<\!-- uncomment for React master
<script src="http://react.zpao.com/builds/master/latest/react.js"><\/script>
-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/4.7.0/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/4.7.0/theme/solarized.min.css">
<script src="//fb.me/react-with-addons-0.13.1.js"><\/script>
<script src="//www.parsecdn.com/js/parse-1.4.2.min.js"><\/script>
<script>
Parse.initialize("gHYJ6NIvgNO1kaEX1OgA1WnbCe2Muj5mrCTlKxLP", "DzlycTD5di7CirpgX9AyPTzBcLuf28FTqHtX4sT1");
<\/script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div style="margin: auto; font-size: 20vh;" id="loading">Loading <div class="log" style="margin: auto; font-size: 10vh;">...</div></div>
<script src="https://cdn.rawgit.com/anonymous/e67adee9cbb2fc2a26a5/raw/a1df870c2eff4212efc4f262834cc4fe86ecebc8/babel.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/4.7.0/codemirror.min.js"><\/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/4.7.0/mode/javascript/javascript.min.js"><\/script>
<\!-- Utils -->
<script src="https://wzrd.in/standalone/circular-json"><\/script>
<script src="https://wzrd.in/standalone/lodash.debounce"><\/script>
<script src="https://www.parsecdn.com/js/parse-react.js"><\/script>
<\!-- React Components -->
<script src="https://cdn.rawgit.com/anonymous/0e3b2440d50a2b7b0cd3/raw/5bff05d9e66803a85d87f4d9bb8be558b77e4a38/ReactJSONInspector.js"><\/script>
<script src="https://cdn.rawgit.com/ForbesLindesay/react-code-mirror/34629fd97ed48c356fc6462c63ab208b62938fac/standalone.js"><\/script>
</body>
</html></script>
<script id="jsbin-source-css" type="text/css">html, body {
margin: 0;
height: 100%;
}
body {
display: flex;
padding: 1em;
align-items: stretch;
flex-direction: column;
}
.App {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 95%;
}
.App-section {
display: flex;
flex-direction: column;
align-items: stretch;
flex: 1;
flex-basis: 25em;
overflow: auto;
}
.App-section-input {
flex: 2;
flex-basis: 25em;
}
.App-section > textarea {
flex: 1;
flex-basis: 10em;
font-size: 1.2em;
font-family: monospace;
}
/* ReactJSONInspector */
.json-inspector,
.json-inspector__selection {
font: 14px/1.4 Consolas, monospace;
}
.json-inspector__leaf {
padding-left: 10px;
}
.json-inspector__line {
display: block;
position: relative;
cursor: default;
}
.json-inspector__line:after {
content: '';
position: absolute;
top: 0;
left: -200px;
right: -50px;
bottom: 0;
z-index: -1;
pointer-events: none;
}
.json-inspector__line:hover:after {
background: rgba(0, 0, 0, 0.06);
}
.json-inspector__leaf_composite > .json-inspector__line {
cursor: pointer;
}
.json-inspector__radio,
.json-inspector__flatpath {
display: none;
}
.json-inspector__value {
margin-left: 5px;
}
.json-inspector__search {
min-width: 300px;
margin: 0 10px 10px 0;
padding: 2px;
}
.json-inspector__key {
color: #505050;
}
.json-inspector__value_helper,
.json-inspector__value_null,
.json-inspector__not-found {
color: #b0b0b0;
}
.json-inspector__value_string {
color: #798953;
}
.json-inspector__value_boolean {
color: #75b5aa;
}
.json-inspector__value_number {
color: #d28445;
}
.json-inspector__hl {
background: #ff0;
box-shadow: 0 -1px 0 2px #ff0;
border-radius: 2px;
}
.json-inspector__show-original {
display: inline-block;
padding: 0 6px;
color: #666;
cursor: pointer;
}
.json-inspector__show-original:hover {
color: #111;
}
.json-inspector__show-original:before {
content: '⥂';
}
.json-inspector__show-original:hover:after {
content: ' expand'
}</script>
<script id="jsbin-source-javascript" type="text/javascript">var EDITOR_PROPS = {
mode: 'javascript',
theme: 'solarized',
lineNumbers: true
};
const HASH_PREFIX = '#/';
const DEFAULT_PLUGIN = "module.exports = function (babel) {\n return new babel.Transformer(\"foo-bar\", {\n FunctionDeclaration(node, parent) {\n return node;\n }\n });\n};";
const DEFAULT_CODE = 'const z = (x) => y';
function shallowCompareObj(a, b){
return Object.keys(a).some((key) => a[key] !== b[key]);
}
function getDefaultState() {
return {
plugin: DEFAULT_PLUGIN,
code: DEFAULT_CODE,
babelOptions: {}
}
};
class App extends React.Component {
static propTypes = {
initialData: React.PropTypes.object.isRequired,
onValid: React.PropTypes.func.isRequired
};
constructor(props){
super(props);
this.state = Object.assign({
resultAst: {},
resultCode: '',
pluginResult: null,
// these come from props.initialData
plugin: null,
code: null,
babelOptions: null
}, props.initialData);
this.updatePlugin = lodash.debounce(this.updatePlugin.bind(this), 300, {
leading: true,
trailing: true,
maxWait: 1000
});
this.updateTransform = lodash.debounce(this.updateTransform.bind(this), 300, {
leading: true,
trailing: true,
maxWait: 1000
});
}
componentWillMount(){
this._lastKnownState = {};
this.updateStuff(this.state);
}
componentWillUpdate(nextProps, nextState){
const sourceKeysOnly = ({plugin, code}) => ({plugin, code});
const changed = (
shallowCompareObj(sourceKeysOnly(this.state), sourceKeysOnly(nextState))
|| shallowCompareObj(this.state.babelOptions, nextState.babelOptions)
);
if (changed) {
this.updateStuff(nextState);
}
}
updateStuff(state){
const pluginResult = this.updatePlugin(state.plugin);
this.updateTransform(Object.assign({}, state, {pluginResult}));
}
updatePlugin(plugin){
const makePlugin = new Function('module', plugin);
localStorage.babelPluginThingy = plugin;
const _module = {};
makePlugin(_module);
this.setState({pluginResult: _module.exports});
return _module.exports;
}
updateTransform(state){
state = Object.assign({}, this.state || {}, state);
const babelOptions = Object.assign({}, state.babelOptions, {
ast: true,
code: true,
plugins: (state.babelOptions.plugins || []).concat[{
transformer: state.pluginResult,
position: 'before'
}]
});
// attempt to transform
let ast, code;
try {
let result = babel.transform(state.code, babelOptions);
ast = result.ast.program;
code = result.code;
}
// if this fails, just use the code output to place the error
catch (e) {
code = e.message + "\n" + e.stack;
}
const replacer = function(k, v){
return k[0] === '_' ? undefined
: k === 'loc' || k === 'start' || k === 'end' || k === 'range' ? undefined
: v;
};
this.setState({resultCode: code});
this.setState({resultAst: false});
// todo: optimize?
if (ast) {
const json = JSON.stringify(ast, replacer);
const cleanAst = JSON.parse(json);
this.setState({resultAst: cleanAst});
this.props.onValid({code: state.code, plugin: state.plugin, babelOptions: babelOptions});
}
}
render(){
return (
<div className="App">
<div className="App-section App-section-input">
<h2>Plugin {location.hash.slice(HASH_PREFIX.length)}</h2>
<CodeMirrorEditor
value={this.state.plugin}
onChange={(e) => {
this.setState({plugin: e.target.value});
}}
onBlur={() => this.setState({})}
{...EDITOR_PROPS}
/>
</div>
<div className="App-section App-section-input">
<h2>Code</h2>
<CodeMirrorEditor
value={this.state.code}
onChange={(e) => {
this.setState({code: e.target.value});
}}
onBlur={() => this.setState({})}
{...EDITOR_PROPS}
/>
</div>
<div className="App-section">
<h2>AST</h2>
{this.state.resultAst
? <ReactJSONInspector data={this.state.resultAst } />
: 'Failed to compile'}
</div>
<div className="App-section">
<h2>Result</h2>
<pre>{this.state.resultCode}</pre>
</div>
</div>
);
}
}
const ParseBabelPlugin = Parse.Object.extend("BabelPlugin");
const backendState = {
activeObject: null,
timesLoaded: 0
};
const backendService = {
_keys: ['code', 'plugin', 'babelOptions'],
assign(dest, source){
const get = (obj, key) => typeof obj.get === 'function'
? obj.get(key) : obj[key];
const set = (obj, key, value) => typeof obj.set === 'function'
? obj.set(key, value) : (obj[key] = value);
backendService._keys.forEach((key) => {
set(dest, key, get(source, key));
});
return dest;
},
getMyACL(){
const acl = new Parse.ACL(Parse.User.current());
acl.setPublicReadAccess(true);
return acl;
},
makeObject(){
var obj = new ParseBabelPlugin();
var acl = this.getMyACL();
obj.setACL(acl);
return backendService.assign(obj, getDefaultState());
},
ensureActiveObject(){
if (!backendService.activeObject) {
backendService.activeObject = backendService.makeObject();
}
return backendService.activeObject;
},
cloneIfCantWrite(obj){
const canWrite = obj.getACL().getWriteAccess(Parse.User.current());
if (canWrite) {
return obj;
}
return backendService.assign(
backendService.makeObject(),
obj
);
},
getDataForPage(cb){
const hash = location.hash.slice(HASH_PREFIX.length);
if (!hash) {
backendService.log('Using defaults');
return cb(null, getDefaultState());
}
backendService.log('Requesting BabelPlugin' + hash);
var query = new Parse.Query(ParseBabelPlugin);
return query.get(hash)
.then((obj) => {
backendService.log('Found BabelPlugin' + hash + ': ' + JSON.stringify(backendService.assign({}, obj)));
backendService.activeObject = obj;
return obj;
})
.then((data) => cb(null, data), () => {
cb(null, backendService.ensureActiveObject());
});
},
eventuallyRender(){
backendService.getDataForPage(function(err){
backendService.timesLoaded++;
backendService.renderNow();
});
},
renderNow(){
const simpleData = backendService.assign({}, backendService.ensureActiveObject());
renderApp(simpleData, function onValid(newData){
backendService.log('onValid');
backendService.eventuallyPersist(newData);
});
},
eventuallyPersist: lodash.debounce(function(data){
backendService.ensureActiveObject();
backendService.assign(backendService.activeObject, data);
backendService.activeObject = backendService.cloneIfCantWrite(backendService.activeObject);
backendService.activeObject.save()
.then((obj) => {
backendService.activeObject = obj;
backendService.updateUrl();
backendService.renderNow();
}, console.error.bind(console));
}, 10e3),
updateUrl(){
const hash = location.hash;
if (backendService.activeObject) {
const id = backendService.activeObject.id;
if (hash !== HASH_PREFIX + id) {
location.hash = HASH_PREFIX + id;
}
}
},
log(msg){
const logEl = document.getElementById('log');
if (logEl) {
logEl.appendChild(document.createTextNode(msg));
}
console.log(msg);
}
};
function renderApp(data, onValid){
var app = (
<App
initialData={data}
onValid={onValid}
key={backendState.timesLoaded}
/>
);
React.render(app, document.body);
}
location.hash = '#/dpsW2RxIxI';
backendService.eventuallyRender();
</script></body>
</html>
html, body {
margin: 0;
height: 100%;
}
body {
display: flex;
padding: 1em;
align-items: stretch;
flex-direction: column;
}
.App {
display: flex;
flex-wrap: wrap;
align-items: stretch;
width: 95%;
}
.App-section {
display: flex;
flex-direction: column;
align-items: stretch;
flex: 1;
flex-basis: 25em;
overflow: auto;
}
.App-section-input {
flex: 2;
flex-basis: 25em;
}
.App-section > textarea {
flex: 1;
flex-basis: 10em;
font-size: 1.2em;
font-family: monospace;
}
/* ReactJSONInspector */
.json-inspector,
.json-inspector__selection {
font: 14px/1.4 Consolas, monospace;
}
.json-inspector__leaf {
padding-left: 10px;
}
.json-inspector__line {
display: block;
position: relative;
cursor: default;
}
.json-inspector__line:after {
content: '';
position: absolute;
top: 0;
left: -200px;
right: -50px;
bottom: 0;
z-index: -1;
pointer-events: none;
}
.json-inspector__line:hover:after {
background: rgba(0, 0, 0, 0.06);
}
.json-inspector__leaf_composite > .json-inspector__line {
cursor: pointer;
}
.json-inspector__radio,
.json-inspector__flatpath {
display: none;
}
.json-inspector__value {
margin-left: 5px;
}
.json-inspector__search {
min-width: 300px;
margin: 0 10px 10px 0;
padding: 2px;
}
.json-inspector__key {
color: #505050;
}
.json-inspector__value_helper,
.json-inspector__value_null,
.json-inspector__not-found {
color: #b0b0b0;
}
.json-inspector__value_string {
color: #798953;
}
.json-inspector__value_boolean {
color: #75b5aa;
}
.json-inspector__value_number {
color: #d28445;
}
.json-inspector__hl {
background: #ff0;
box-shadow: 0 -1px 0 2px #ff0;
border-radius: 2px;
}
.json-inspector__show-original {
display: inline-block;
padding: 0 6px;
color: #666;
cursor: pointer;
}
.json-inspector__show-original:hover {
color: #111;
}
.json-inspector__show-original:before {
content: '⥂';
}
.json-inspector__show-original:hover:after {
content: ' expand'
}
'use strict';
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }
var EDITOR_PROPS = {
mode: 'javascript',
theme: 'solarized',
lineNumbers: true
};
var HASH_PREFIX = '#/';
var DEFAULT_PLUGIN = 'module.exports = function (babel) {\n return new babel.Transformer("foo-bar", {\n FunctionDeclaration(node, parent) {\n return node;\n }\n });\n};';
var DEFAULT_CODE = 'const z = (x) => y';
function shallowCompareObj(a, b) {
return Object.keys(a).some(function (key) {
return a[key] !== b[key];
});
}
function getDefaultState() {
return {
plugin: DEFAULT_PLUGIN,
code: DEFAULT_CODE,
babelOptions: {}
};
};
var App = (function (_React$Component) {
function App(props) {
_classCallCheck(this, App);
_get(Object.getPrototypeOf(App.prototype), 'constructor', this).call(this, props);
this.state = Object.assign({
resultAst: {},
resultCode: '',
pluginResult: null,
// these come from props.initialData
plugin: null,
code: null,
babelOptions: null
}, props.initialData);
this.updatePlugin = lodash.debounce(this.updatePlugin.bind(this), 300, {
leading: true,
trailing: true,
maxWait: 1000
});
this.updateTransform = lodash.debounce(this.updateTransform.bind(this), 300, {
leading: true,
trailing: true,
maxWait: 1000
});
}
_inherits(App, _React$Component);
_createClass(App, [{
key: 'componentWillMount',
value: function componentWillMount() {
this._lastKnownState = {};
this.updateStuff(this.state);
}
}, {
key: 'componentWillUpdate',
value: function componentWillUpdate(nextProps, nextState) {
var sourceKeysOnly = function sourceKeysOnly(_ref) {
var plugin = _ref.plugin;
var code = _ref.code;
return { plugin: plugin, code: code };
};
var changed = shallowCompareObj(sourceKeysOnly(this.state), sourceKeysOnly(nextState)) || shallowCompareObj(this.state.babelOptions, nextState.babelOptions);
if (changed) {
this.updateStuff(nextState);
}
}
}, {
key: 'updateStuff',
value: function updateStuff(state) {
var pluginResult = this.updatePlugin(state.plugin);
this.updateTransform(Object.assign({}, state, { pluginResult: pluginResult }));
}
}, {
key: 'updatePlugin',
value: function updatePlugin(plugin) {
var makePlugin = new Function('module', plugin);
localStorage.babelPluginThingy = plugin;
var _module = {};
makePlugin(_module);
this.setState({ pluginResult: _module.exports });
return _module.exports;
}
}, {
key: 'updateTransform',
value: function updateTransform(state) {
state = Object.assign({}, this.state || {}, state);
var babelOptions = Object.assign({}, state.babelOptions, {
ast: true,
code: true,
plugins: (state.babelOptions.plugins || []).concat[{
transformer: state.pluginResult,
position: 'before'
}]
});
// attempt to transform
var ast = undefined,
code = undefined;
try {
var result = babel.transform(state.code, babelOptions);
ast = result.ast.program;
code = result.code;
}
// if this fails, just use the code output to place the error
catch (e) {
code = e.message + '\n' + e.stack;
}
var replacer = function replacer(k, v) {
return k[0] === '_' ? undefined : k === 'loc' || k === 'start' || k === 'end' || k === 'range' ? undefined : v;
};
this.setState({ resultCode: code });
this.setState({ resultAst: false });
// todo: optimize?
if (ast) {
var json = JSON.stringify(ast, replacer);
var cleanAst = JSON.parse(json);
this.setState({ resultAst: cleanAst });
this.props.onValid({ code: state.code, plugin: state.plugin, babelOptions: babelOptions });
}
}
}, {
key: 'render',
value: function render() {
var _this = this;
return React.createElement(
'div',
{ className: 'App' },
React.createElement(
'div',
{ className: 'App-section App-section-input' },
React.createElement(
'h2',
null,
'Plugin ',
location.hash.slice(HASH_PREFIX.length)
),
React.createElement(CodeMirrorEditor, _extends({
value: this.state.plugin,
onChange: function (e) {
_this.setState({ plugin: e.target.value });
},
onBlur: function () {
return _this.setState({});
}
}, EDITOR_PROPS))
),
React.createElement(
'div',
{ className: 'App-section App-section-input' },
React.createElement(
'h2',
null,
'Code'
),
React.createElement(CodeMirrorEditor, _extends({
value: this.state.code,
onChange: function (e) {
_this.setState({ code: e.target.value });
},
onBlur: function () {
return _this.setState({});
}
}, EDITOR_PROPS))
),
React.createElement(
'div',
{ className: 'App-section' },
React.createElement(
'h2',
null,
'AST'
),
this.state.resultAst ? React.createElement(ReactJSONInspector, { data: this.state.resultAst }) : 'Failed to compile'
),
React.createElement(
'div',
{ className: 'App-section' },
React.createElement(
'h2',
null,
'Result'
),
React.createElement(
'pre',
null,
this.state.resultCode
)
)
);
}
}], [{
key: 'propTypes',
value: {
initialData: React.PropTypes.object.isRequired,
onValid: React.PropTypes.func.isRequired
},
enumerable: true
}]);
return App;
})(React.Component);
var ParseBabelPlugin = Parse.Object.extend('BabelPlugin');
var backendState = {
activeObject: null,
timesLoaded: 0
};
var backendService = {
_keys: ['code', 'plugin', 'babelOptions'],
assign: function assign(dest, source) {
var get = function get(obj, key) {
return typeof obj.get === 'function' ? obj.get(key) : obj[key];
};
var set = function set(obj, key, value) {
return typeof obj.set === 'function' ? obj.set(key, value) : obj[key] = value;
};
backendService._keys.forEach(function (key) {
set(dest, key, get(source, key));
});
return dest;
},
getMyACL: function getMyACL() {
var acl = new Parse.ACL(Parse.User.current());
acl.setPublicReadAccess(true);
return acl;
},
makeObject: function makeObject() {
var obj = new ParseBabelPlugin();
var acl = this.getMyACL();
obj.setACL(acl);
return backendService.assign(obj, getDefaultState());
},
ensureActiveObject: function ensureActiveObject() {
if (!backendService.activeObject) {
backendService.activeObject = backendService.makeObject();
}
return backendService.activeObject;
},
cloneIfCantWrite: function cloneIfCantWrite(obj) {
var canWrite = obj.getACL().getWriteAccess(Parse.User.current());
if (canWrite) {
return obj;
}
return backendService.assign(backendService.makeObject(), obj);
},
getDataForPage: function getDataForPage(cb) {
var hash = location.hash.slice(HASH_PREFIX.length);
if (!hash) {
backendService.log('Using defaults');
return cb(null, getDefaultState());
}
backendService.log('Requesting BabelPlugin' + hash);
var query = new Parse.Query(ParseBabelPlugin);
return query.get(hash).then(function (obj) {
backendService.log('Found BabelPlugin' + hash + ': ' + JSON.stringify(backendService.assign({}, obj)));
backendService.activeObject = obj;
return obj;
}).then(function (data) {
return cb(null, data);
}, function () {
cb(null, backendService.ensureActiveObject());
});
},
eventuallyRender: function eventuallyRender() {
backendService.getDataForPage(function (err) {
backendService.timesLoaded++;
backendService.renderNow();
});
},
renderNow: function renderNow() {
var simpleData = backendService.assign({}, backendService.ensureActiveObject());
renderApp(simpleData, function onValid(newData) {
backendService.log('onValid');
backendService.eventuallyPersist(newData);
});
},
eventuallyPersist: lodash.debounce(function (data) {
backendService.ensureActiveObject();
backendService.assign(backendService.activeObject, data);
backendService.activeObject = backendService.cloneIfCantWrite(backendService.activeObject);
backendService.activeObject.save().then(function (obj) {
backendService.activeObject = obj;
backendService.updateUrl();
backendService.renderNow();
}, console.error.bind(console));
}, 10000),
updateUrl: function updateUrl() {
var hash = location.hash;
if (backendService.activeObject) {
var id = backendService.activeObject.id;
if (hash !== HASH_PREFIX + id) {
location.hash = HASH_PREFIX + id;
}
}
},
log: function log(msg) {
var logEl = document.getElementById('log');
if (logEl) {
logEl.appendChild(document.createTextNode(msg));
}
console.log(msg);
}
};
function renderApp(data, onValid) {
var app = React.createElement(App, {
initialData: data,
onValid: onValid,
key: backendState.timesLoaded
});
React.render(app, document.body);
}
location.hash = '#/dpsW2RxIxI';
backendService.eventuallyRender();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment