Skip to content
This repository has been archived by the owner on Jun 29, 2018. It is now read-only.

Commit

Permalink
Add support for transforming script tags with text/babel type
Browse files Browse the repository at this point in the history
Closes #15
Closes facebook/react#5497
  • Loading branch information
Daniel15 committed Jun 19, 2016
1 parent fb9b2a4 commit 03bd0ce
Show file tree
Hide file tree
Showing 7 changed files with 238 additions and 2 deletions.
File renamed without changes.
6 changes: 6 additions & 0 deletions examples/example.js
@@ -0,0 +1,6 @@
const doStuff = () => {
const name = 'world';
document.getElementById('output').innerHTML = `Hello ${name}`;
document.getElementById('version').innerHTML = Babel.version;
};
doStuff();
21 changes: 21 additions & 0 deletions examples/scriptTag-inline.htm
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>babel-standalone example - Script tag</title>
</head>
<body>
Using Babel <strong id="version"></strong>:
<pre id="output">Loading...</pre>

<script src="../babel.js"></script>
<script type="text/babel">
const doStuff = () => {
const name = 'world';
document.getElementById('output').innerHTML = `Hello ${name}`;
document.getElementById('version').innerHTML = Babel.version;
};
doStuff();
</script>
</body>
</html>
14 changes: 14 additions & 0 deletions examples/scriptTag-src.htm
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>babel-standalone example - External script</title>
</head>
<body>
Using Babel <strong id="version"></strong>:
<pre id="output">Loading...</pre>

<script src="../babel.js"></script>
<script type="text/babel" src="example.js"></script>
</body>
</html>
3 changes: 1 addition & 2 deletions gulpfile.js
Expand Up @@ -34,8 +34,7 @@ gulp.task('build', function() {
output: {
filename: 'babel.js',
library: 'Babel',
libraryTarget: 'umd',
pathinfo: true,
libraryTarget: 'umd'
},
plugins: [
new webpack.DefinePlugin({
Expand Down
8 changes: 8 additions & 0 deletions src/index.js
@@ -1,5 +1,7 @@
import * as Babel from 'babel-core';

import {runScripts} from './transformScriptTags';

/**
* Parses plugin names and presets from the specified options.
*/
Expand Down Expand Up @@ -193,3 +195,9 @@ export const availablePresets = {
};

export const version = Babel.version;

// Listen for load event if we're in a browser and then kick off finding and
// running of scripts with "text/babel" type.
if (typeof window !== 'undefined' && window !== null && window.addEventListener !== null) {
window.addEventListener('DOMContentLoaded', () => runScripts(transform), false);
}
188 changes: 188 additions & 0 deletions src/transformScriptTags.js
@@ -0,0 +1,188 @@
/**
* 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 the React source tree. An additional
* grant of patent rights can be found in the PATENTS file in the same directory.
*/

// 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.
const babelOpts = {
presets: [
'react',
'es2015',
],
plugins: [
'transform-class-properties',
'transform-object-rest-spread',
'transform-flow-strip-types',
],
sourceMaps: 'inline',
};

const scriptTypes = [
'text/jsx',
'text/babel',
];

let headEl;
let inlineScriptCount = 0;

/**
* Actually transform the code.
*/
function transformCode(transformFn, code, url) {
let source;
if (url != null) {
source = url;
} else {
source = 'Inline Babel script';
inlineScriptCount++;
if (inlineScriptCount > 1) {
source += ' (' + inlineScriptCount + ')';
}
}

return transformFn(
code,
{
filename: source,
...babelOpts
}
).code;
}


/**
* Appends a script element at the end of the <head> with the content of code,
* after transforming it.
*/
function run(transformFn, code, url, options) {
const scriptEl = document.createElement('script');
scriptEl.text = transformCode(transformFn, code, url, options);
headEl.appendChild(scriptEl);
}

/**
* Load script from the provided url and pass the content to the callback.
*/
function load(url, successCallback, errorCallback) {
const xhr = 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.
*/
function loadScripts(transformFn, scripts) {
const result = [];
const 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(transformFn, script.content, script.url);
} else if (!script.loaded && !script.error && !script.async) {
break;
}
}
}

scripts.forEach((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,
content => {
result[i].loaded = true;
result[i].content = content;
check();
},
() => {
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".
*/
export function runScripts(transformFn) {
headEl = document.getElementsByTagName('head')[0];
const scripts = document.getElementsByTagName('script');

// Array.prototype.slice cannot be used on NodeList on IE8
const jsxScripts = [];
for (let i = 0; i < scripts.length; i++) {
const script = scripts.item(i);
// Support the old type="text/jsx;harmony=true"
const type = script.type.split(';')[0];
if (scriptTypes.indexOf(type) !== -1) {
jsxScripts.push(script);
}
}

if (jsxScripts.length === 0) {
return;
}

console.warn(
'You are using the in-browser Babel transformer. Be sure to precompile ' +
'your scrips for production - https://babeljs.io/docs/setup/'
);

loadScripts(transformFn, jsxScripts);
}

0 comments on commit 03bd0ce

Please sign in to comment.