Skip to content

Instantly share code, notes, and snippets.

@zpao
Created February 16, 2016 23:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save zpao/7f3f2063c3c2a39132e3 to your computer and use it in GitHub Desktop.
Save zpao/7f3f2063c3c2a39132e3 to your computer and use it in GitHub Desktop.
/**
* Copyright 2013-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/*eslint-disable no-eval */
/*eslint-disable block-scoped-var */
'use strict';
var babel = require('babel-standalone');
// The options we'll pass will be pretty inline with what we're expecting people
// to write. It won't cover every use case but will set ES2015 as the baseline
// and transform JSX. We'll also support 2 in-process syntaxes since they are
// commonly used with React: class properties, Flow types, & object rest spread.
var babelOpts = {
presets: [
'react',
'es2015',
],
plugins: [
'transform-class-properties',
'transform-object-rest-spread',
'transform-flow-strip-types',
],
sourceMaps: 'inline',
};
var scriptTypes = [
'text/jsx',
'text/babel',
];
var headEl;
var inlineScriptCount = 0;
/**
* Actually transform the code.
*
* @param {string} code
* @param {string?} url
* @return {string} The transformed code.
* @internal
*/
function transformCode(code, url) {
var source;
if (url != null) {
source = url;
} else {
source = 'Inline Babel script';
inlineScriptCount++;
if (inlineScriptCount > 1) {
source += ' (' + inlineScriptCount + ')';
}
}
var transformed;
try {
transformed = babel.transform(code, assign({filename: source}, babelOpts));
} catch (e) {
throw e;
}
return transformed.code;
}
/**
* Appends a script element at the end of the <head> with the content of code,
* after transforming it.
*
* @param {string} code The original source code
* @param {string?} url Where the code came from. null if inline
* @param {object?} options Options to pass to jstransform
* @internal
*/
function run(code, url, options) {
var scriptEl = document.createElement('script');
scriptEl.text = transformCode(code, url, options);
headEl.appendChild(scriptEl);
}
/**
* Load script from the provided url and pass the content to the callback.
*
* @param {string} url The location of the script src
* @param {function} callback Function to call with the content of url
* @internal
*/
function load(url, successCallback, errorCallback) {
var xhr;
xhr = window.ActiveXObject ? new window.ActiveXObject('Microsoft.XMLHTTP')
: new XMLHttpRequest();
// async, however scripts will be executed in the order they are in the
// DOM to mirror normal script loading.
xhr.open('GET', url, true);
if ('overrideMimeType' in xhr) {
xhr.overrideMimeType('text/plain');
}
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 0 || xhr.status === 200) {
successCallback(xhr.responseText);
} else {
errorCallback();
throw new Error('Could not load ' + url);
}
}
};
return xhr.send(null);
}
/**
* Loop over provided script tags and get the content, via innerHTML if an
* inline script, or by using XHR. Transforms are applied if needed. The scripts
* are executed in the order they are found on the page.
*
* @param {array} scripts The <script> elements to load and run.
* @internal
*/
function loadScripts(scripts) {
var result = [];
var count = scripts.length;
function check() {
var script, i;
for (i = 0; i < count; i++) {
script = result[i];
if (script.loaded && !script.executed) {
script.executed = true;
run(script.content, script.url);
} else if (!script.loaded && !script.error && !script.async) {
break;
}
}
}
scripts.forEach(function(script, i) {
// script.async is always true for non-javascript script tags
var async = script.hasAttribute('async');
if (script.src) {
result[i] = {
async: async,
error: false,
executed: false,
content: null,
loaded: false,
url: script.src,
};
load(script.src, function(content) {
result[i].loaded = true;
result[i].content = content;
check();
}, function() {
result[i].error = true;
check();
});
} else {
result[i] = {
async: async,
error: false,
executed: false,
content: script.innerHTML,
loaded: true,
url: null,
};
}
});
check();
}
/**
* Find and run all script tags with type="text/jsx".
*
* @internal
*/
function runScripts() {
var scripts = document.getElementsByTagName('script');
// Array.prototype.slice cannot be used on NodeList on IE8
var jsxScripts = [];
for (var i = 0; i < scripts.length; i++) {
var script = scripts.item(i);
// Support the old type="text/jsx;harmony=true"
var type = script.type.split(';')[0];
if (scriptTypes.indexOf(type) !== -1) {
jsxScripts.push(script);
}
}
if (jsxScripts.length < 1) {
return;
}
console.warn(
'You are using the in-browser JSX transformer. Be sure to precompile ' +
'your JSX for production - ' +
'http://facebook.github.io/react/docs/tooling-integration.html#jsx'
);
loadScripts(jsxScripts);
}
// Listen for load event if we're in a browser and then kick off finding and
// running of scripts.
if (typeof window !== 'undefined' && window !== null) {
headEl = document.getElementsByTagName('head')[0];
// dummyAnchor = document.createElement('a');
if (window.addEventListener) {
window.addEventListener('DOMContentLoaded', runScripts, false);
} else {
window.attachEvent('onload', runScripts);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment