UNPKG

4.28 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = _default;
7
8var _helperGetFunctionArity = require("@babel/helper-get-function-arity");
9
10var _template = require("@babel/template");
11
12var _t = require("@babel/types");
13
14const {
15 NOT_LOCAL_BINDING,
16 cloneNode,
17 identifier,
18 isAssignmentExpression,
19 isFunction,
20 isIdentifier,
21 isLiteral,
22 isNullLiteral,
23 isObjectMethod,
24 isObjectProperty,
25 isRegExpLiteral,
26 isTemplateLiteral,
27 isVariableDeclarator,
28 toBindingIdentifierName
29} = _t;
30const buildPropertyMethodAssignmentWrapper = (0, _template.default)(`
31 (function (FUNCTION_KEY) {
32 function FUNCTION_ID() {
33 return FUNCTION_KEY.apply(this, arguments);
34 }
35
36 FUNCTION_ID.toString = function () {
37 return FUNCTION_KEY.toString();
38 }
39
40 return FUNCTION_ID;
41 })(FUNCTION)
42`);
43const buildGeneratorPropertyMethodAssignmentWrapper = (0, _template.default)(`
44 (function (FUNCTION_KEY) {
45 function* FUNCTION_ID() {
46 return yield* FUNCTION_KEY.apply(this, arguments);
47 }
48
49 FUNCTION_ID.toString = function () {
50 return FUNCTION_KEY.toString();
51 };
52
53 return FUNCTION_ID;
54 })(FUNCTION)
55`);
56const visitor = {
57 "ReferencedIdentifier|BindingIdentifier"(path, state) {
58 if (path.node.name !== state.name) return;
59 const localDeclar = path.scope.getBindingIdentifier(state.name);
60 if (localDeclar !== state.outerDeclar) return;
61 state.selfReference = true;
62 path.stop();
63 }
64
65};
66
67function getNameFromLiteralId(id) {
68 if (isNullLiteral(id)) {
69 return "null";
70 }
71
72 if (isRegExpLiteral(id)) {
73 return `_${id.pattern}_${id.flags}`;
74 }
75
76 if (isTemplateLiteral(id)) {
77 return id.quasis.map(quasi => quasi.value.raw).join("");
78 }
79
80 if (id.value !== undefined) {
81 return id.value + "";
82 }
83
84 return "";
85}
86
87function wrap(state, method, id, scope) {
88 if (state.selfReference) {
89 if (scope.hasBinding(id.name) && !scope.hasGlobal(id.name)) {
90 scope.rename(id.name);
91 } else {
92 if (!isFunction(method)) return;
93 let build = buildPropertyMethodAssignmentWrapper;
94
95 if (method.generator) {
96 build = buildGeneratorPropertyMethodAssignmentWrapper;
97 }
98
99 const template = build({
100 FUNCTION: method,
101 FUNCTION_ID: id,
102 FUNCTION_KEY: scope.generateUidIdentifier(id.name)
103 }).expression;
104 const params = template.callee.body.body[0].params;
105
106 for (let i = 0, len = (0, _helperGetFunctionArity.default)(method); i < len; i++) {
107 params.push(scope.generateUidIdentifier("x"));
108 }
109
110 return template;
111 }
112 }
113
114 method.id = id;
115 scope.getProgramParent().references[id.name] = true;
116}
117
118function visit(node, name, scope) {
119 const state = {
120 selfAssignment: false,
121 selfReference: false,
122 outerDeclar: scope.getBindingIdentifier(name),
123 references: [],
124 name: name
125 };
126 const binding = scope.getOwnBinding(name);
127
128 if (binding) {
129 if (binding.kind === "param") {
130 state.selfReference = true;
131 } else {}
132 } else if (state.outerDeclar || scope.hasGlobal(name)) {
133 scope.traverse(node, visitor, state);
134 }
135
136 return state;
137}
138
139function _default({
140 node,
141 parent,
142 scope,
143 id
144}, localBinding = false) {
145 if (node.id) return;
146
147 if ((isObjectProperty(parent) || isObjectMethod(parent, {
148 kind: "method"
149 })) && (!parent.computed || isLiteral(parent.key))) {
150 id = parent.key;
151 } else if (isVariableDeclarator(parent)) {
152 id = parent.id;
153
154 if (isIdentifier(id) && !localBinding) {
155 const binding = scope.parent.getBinding(id.name);
156
157 if (binding && binding.constant && scope.getBinding(id.name) === binding) {
158 node.id = cloneNode(id);
159 node.id[NOT_LOCAL_BINDING] = true;
160 return;
161 }
162 }
163 } else if (isAssignmentExpression(parent, {
164 operator: "="
165 })) {
166 id = parent.left;
167 } else if (!id) {
168 return;
169 }
170
171 let name;
172
173 if (id && isLiteral(id)) {
174 name = getNameFromLiteralId(id);
175 } else if (id && isIdentifier(id)) {
176 name = id.name;
177 }
178
179 if (name === undefined) {
180 return;
181 }
182
183 name = toBindingIdentifierName(name);
184 id = identifier(name);
185 id[NOT_LOCAL_BINDING] = true;
186 const state = visit(node, name, scope);
187 return wrap(state, node, id, scope) || node;
188}
\No newline at end of file