Skip to content

Commit

Permalink
Add a new babel transformer for inlining regenerator-runtime per file
Browse files Browse the repository at this point in the history
Reviewed By: davidaurelio

Differential Revision: D5364134

fbshipit-source-id: 45aae705f961819d5a8e3a8a23c2e8b9300d3cb5
  • Loading branch information
Miguel Jimenez Esun authored and facebook-github-bot committed Jul 3, 2017
1 parent 304dc6b commit e7c1cf5
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 0 deletions.
1 change: 1 addition & 0 deletions babel-preset/configs/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var base = {
'transform-react-jsx',
'transform-regenerator',
['transform-es2015-for-of', { loose: true }],
require('../transforms/transform-regenerator-runtime-insertion'),
require('../transforms/transform-symbol-member'),
]),
};
Expand Down
109 changes: 109 additions & 0 deletions babel-preset/transforms/transform-regenerator-runtime-insertion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* Copyright (c) 2013-present, 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.
*
*/

'use strict';

const babel = require('babel-core');
const t = babel.types;

const NAME = 'regenerator-runtime';
const REGENERATOR_NOT_NEEDED = 'NOT_NEEDED';
const REGENERATOR_NEEDED = 'NEEDED';
const REGENERATOR_ALREADY_REQUIRED = 'ALREADY_REQUIRED';

function flagState(state) {
if (state !== REGENERATOR_ALREADY_REQUIRED) {
state.regeneratorRequired = REGENERATOR_NEEDED;
}
}

function checkRequire(node, state) {
const method = node.callee.name;
const args = node.arguments;

if (
method === 'require' &&
t.isStringLiteral(args[0]) &&
args[0].value.startsWith(NAME)
) {
state.regeneratorRequired = REGENERATOR_ALREADY_REQUIRED;
}
}

function checkFunction(node, state) {
// Method is neither a generator nor an async function; skip.
if (!node.generator && !node.async) {
return;
}

// This will only set the status to needed if it hasn't already been required.
if (state.regeneratorRequired === REGENERATOR_NOT_NEEDED) {
state.regeneratorRequired = REGENERATOR_NEEDED;
}
}

const isGeneratorRequired = {
CallExpression(path, state) {
checkRequire(path.node, state);
},

FunctionDeclaration(path, state) {
checkFunction(path.node, state);
},

ClassMethod(path, state) {
checkFunction(path.node, state);
},

ArrowFunctionExpression(path, state) {
checkFunction(path.node, state);
},

FunctionExpression(path, state) {
checkFunction(path.node, state);
},

YieldExpression(path, state) {
flagState(state);
},

AwaitExpression(path, state) {
flagState(state);
},
};

/**
* Adds a "const regeneratorRuntime = require('regenerator-runtime') to each
* file that does not include regenerator-runtime, but requires it because it
* contains async/await or generators.
*/
module.exports = function() {
return {
visitor: {
Program(path, state) {
state.regeneratorRequired = REGENERATOR_NOT_NEEDED;
path.traverse(isGeneratorRequired, state);

if (state.regeneratorRequired === REGENERATOR_NEEDED) {
path.node.body.unshift(
t.variableDeclaration('const', [
t.variableDeclarator(
t.identifier('regeneratorRuntime'),
t.callExpression(t.identifier('require'), [
t.stringLiteral(NAME),
])
)
])
);
}
},
},
};
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
],
"unmockedModulePathPatterns": [
"node_modules/react/",
"node_modules/regenerator-runtime/",
"Libraries/Renderer",
"promise",
"source-map",
Expand Down

0 comments on commit e7c1cf5

Please sign in to comment.