123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- "use strict";
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- exports.default = void 0;
- var _t = require("@babel/types");
- var _t2 = _t;
- const {
- react
- } = _t;
- const {
- cloneNode,
- jsxExpressionContainer,
- variableDeclaration,
- variableDeclarator
- } = _t2;
- const referenceVisitor = {
- ReferencedIdentifier(path, state) {
- if (path.isJSXIdentifier() && react.isCompatTag(path.node.name) && !path.parentPath.isJSXMemberExpression()) {
- return;
- }
- if (path.node.name === "this") {
- let scope = path.scope;
- do {
- if (scope.path.isFunction() && !scope.path.isArrowFunctionExpression()) {
- break;
- }
- } while (scope = scope.parent);
- if (scope) state.breakOnScopePaths.push(scope.path);
- }
- const binding = path.scope.getBinding(path.node.name);
- if (!binding) return;
- for (const violation of binding.constantViolations) {
- if (violation.scope !== binding.path.scope) {
- state.mutableBinding = true;
- path.stop();
- return;
- }
- }
- if (binding !== state.scope.getBinding(path.node.name)) return;
- state.bindings[path.node.name] = binding;
- }
- };
- class PathHoister {
- constructor(path, scope) {
- this.breakOnScopePaths = void 0;
- this.bindings = void 0;
- this.mutableBinding = void 0;
- this.scopes = void 0;
- this.scope = void 0;
- this.path = void 0;
- this.attachAfter = void 0;
- this.breakOnScopePaths = [];
- this.bindings = {};
- this.mutableBinding = false;
- this.scopes = [];
- this.scope = scope;
- this.path = path;
- this.attachAfter = false;
- }
- isCompatibleScope(scope) {
- for (const key of Object.keys(this.bindings)) {
- const binding = this.bindings[key];
- if (!scope.bindingIdentifierEquals(key, binding.identifier)) {
- return false;
- }
- }
- return true;
- }
- getCompatibleScopes() {
- let scope = this.path.scope;
- do {
- if (this.isCompatibleScope(scope)) {
- this.scopes.push(scope);
- } else {
- break;
- }
- if (this.breakOnScopePaths.indexOf(scope.path) >= 0) {
- break;
- }
- } while (scope = scope.parent);
- }
- getAttachmentPath() {
- let path = this._getAttachmentPath();
- if (!path) return;
- let targetScope = path.scope;
- if (targetScope.path === path) {
- targetScope = path.scope.parent;
- }
- if (targetScope.path.isProgram() || targetScope.path.isFunction()) {
- for (const name of Object.keys(this.bindings)) {
- if (!targetScope.hasOwnBinding(name)) continue;
- const binding = this.bindings[name];
- if (binding.kind === "param" || binding.path.parentKey === "params") {
- continue;
- }
- const bindingParentPath = this.getAttachmentParentForPath(binding.path);
- if (bindingParentPath.key >= path.key) {
- this.attachAfter = true;
- path = binding.path;
- for (const violationPath of binding.constantViolations) {
- if (this.getAttachmentParentForPath(violationPath).key > path.key) {
- path = violationPath;
- }
- }
- }
- }
- }
- return path;
- }
- _getAttachmentPath() {
- const scopes = this.scopes;
- const scope = scopes.pop();
- if (!scope) return;
- if (scope.path.isFunction()) {
- if (this.hasOwnParamBindings(scope)) {
- if (this.scope === scope) return;
- const bodies = scope.path.get("body").get("body");
- for (let i = 0; i < bodies.length; i++) {
- if (bodies[i].node._blockHoist) continue;
- return bodies[i];
- }
- } else {
- return this.getNextScopeAttachmentParent();
- }
- } else if (scope.path.isProgram()) {
- return this.getNextScopeAttachmentParent();
- }
- }
- getNextScopeAttachmentParent() {
- const scope = this.scopes.pop();
- if (scope) return this.getAttachmentParentForPath(scope.path);
- }
- getAttachmentParentForPath(path) {
- do {
- if (!path.parentPath || Array.isArray(path.container) && path.isStatement()) {
- return path;
- }
- } while (path = path.parentPath);
- }
- hasOwnParamBindings(scope) {
- for (const name of Object.keys(this.bindings)) {
- if (!scope.hasOwnBinding(name)) continue;
- const binding = this.bindings[name];
- if (binding.kind === "param" && binding.constant) return true;
- }
- return false;
- }
- run() {
- this.path.traverse(referenceVisitor, this);
- if (this.mutableBinding) return;
- this.getCompatibleScopes();
- const attachTo = this.getAttachmentPath();
- if (!attachTo) return;
- if (attachTo.getFunctionParent() === this.path.getFunctionParent()) return;
- let uid = attachTo.scope.generateUidIdentifier("ref");
- const declarator = variableDeclarator(uid, this.path.node);
- const insertFn = this.attachAfter ? "insertAfter" : "insertBefore";
- const [attached] = attachTo[insertFn]([attachTo.isVariableDeclarator() ? declarator : variableDeclaration("var", [declarator])]);
- const parent = this.path.parentPath;
- if (parent.isJSXElement() && this.path.container === parent.node.children) {
- uid = jsxExpressionContainer(uid);
- }
- this.path.replaceWith(cloneNode(uid));
- return attachTo.isVariableDeclarator() ? attached.get("init") : attached.get("declarations.0.init");
- }
- }
- exports.default = PathHoister;
|