UNPKG

babel-core

Version:

Turn ES6 code into readable vanilla ES5 with source maps

120 lines (95 loc) 3.55 kB
"use strict"; var _interopRequireWildcard = function (obj) { return obj && obj.__esModule ? obj : { "default": obj }; }; var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var react = _interopRequireWildcard(require("../../transformation/helpers/react")); var t = _interopRequireWildcard(require("../../types")); var referenceVisitor = { enter: function enter(node, parent, scope, state) { if (this.isJSXIdentifier() && react.isCompatTag(node.name)) { return; } if (this.isJSXIdentifier() || this.isIdentifier()) { // direct references that we need to track to hoist this to the highest scope we can if (this.isReferenced()) { var bindingInfo = scope.getBinding(node.name); // this binding isn't accessible from the parent scope so we can safely ignore it // eg. it's in a closure etc if (bindingInfo !== state.scope.getBinding(node.name)) return; if (bindingInfo && bindingInfo.constant) { state.bindings[node.name] = bindingInfo; } else { scope.dump(); state.foundIncompatible = true; this.stop(); } } } } }; var PathHoister = (function () { function PathHoister(path, scope) { _classCallCheck(this, PathHoister); this.foundIncompatible = false; this.bindings = {}; this.scope = scope; this.scopes = []; this.path = path; } PathHoister.prototype.isCompatibleScope = function isCompatibleScope(scope) { for (var key in this.bindings) { var binding = this.bindings[key]; if (!scope.bindingIdentifierEquals(key, binding.identifier)) { return false; } } return true; }; PathHoister.prototype.getCompatibleScopes = function getCompatibleScopes() { var checkScope = this.path.scope; do { if (this.isCompatibleScope(checkScope)) { this.scopes.push(checkScope); } else { break; } } while (checkScope = checkScope.parent); }; PathHoister.prototype.getAttachmentPath = function getAttachmentPath() { var scopes = this.scopes; var scope = scopes.pop(); if (scope.path.isFunction()) { if (this.hasNonParamBindings()) { // can't be attached to this scope return this.getNextScopeStatementParent(); } else { // needs to be attached to the body return scope.path.get("body").get("body")[0]; } } else if (scope.path.isProgram()) { return this.getNextScopeStatementParent(); } }; PathHoister.prototype.getNextScopeStatementParent = function getNextScopeStatementParent() { var scope = this.scopes.pop(); if (scope) return scope.path.getStatementParent(); }; PathHoister.prototype.hasNonParamBindings = function hasNonParamBindings() { for (var name in this.bindings) { var binding = this.bindings[name]; if (binding.kind !== "param") return true; } return false; }; PathHoister.prototype.run = function run() { this.path.traverse(referenceVisitor, this); if (this.foundIncompatible) return; this.getCompatibleScopes(); var path = this.getAttachmentPath(); if (!path) return; var uid = path.scope.generateUidIdentifier("ref"); path.insertBefore([t.variableDeclaration("var", [t.variableDeclarator(uid, this.path.node)])]); this.path.replaceWith(uid); }; return PathHoister; })(); module.exports = PathHoister;