1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const babel_traverse_1 = require("babel-traverse");
|
4 | const t = require("babel-types");
|
5 | const babel_core_1 = require("babel-core");
|
6 | const utils_1 = require("./utils");
|
7 | const lodash_1 = require("lodash");
|
8 | const jsx_1 = require("./jsx");
|
9 | const constant_1 = require("./constant");
|
10 | const adapter_1 = require("./adapter");
|
11 | const options_1 = require("./options");
|
12 | const babel_generator_1 = require("babel-generator");
|
13 | const env_1 = require("./env");
|
14 | const functional_1 = require("./functional");
|
15 | const template = require('babel-template');
|
16 | function findParents(path, predicates) {
|
17 | const parents = [];
|
18 |
|
19 | while (path = path.parentPath) {
|
20 | if (predicates(path)) {
|
21 | parents.push(path);
|
22 | }
|
23 | }
|
24 | return parents;
|
25 | }
|
26 | function isClassDcl(p) {
|
27 | return p.isClassExpression() || p.isClassDeclaration();
|
28 | }
|
29 | function isChildrenOfJSXAttr(p) {
|
30 | return !!p.findParent(p => p.isJSXAttribute());
|
31 | }
|
32 | function buildAssignState(pendingState) {
|
33 | return t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('assign')), [
|
34 | t.memberExpression(t.thisExpression(), t.identifier('state')),
|
35 | pendingState
|
36 | ]));
|
37 | }
|
38 | const incrementCalleeId = utils_1.incrementId();
|
39 | const incrementLoopArrayId = utils_1.incrementId();
|
40 | class RenderParser {
|
41 | |
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 | constructor(renderPath, methods, initState, referencedIdentifiers, usedState, customComponentNames, componentProperies, loopRefs, refObjExpr, methodName) {
|
48 | this.classProperties = new Set();
|
49 | this.templates = new Map();
|
50 | this.jsxDeclarations = new Set();
|
51 | this.loopScopes = new Set();
|
52 | this.returnedPaths = [];
|
53 | this.usedThisState = new Set();
|
54 | this.loopComponents = new Map();
|
55 | this.loopComponentNames = new Map();
|
56 | this.loopRefIdentifiers = new Map();
|
57 | this.reserveStateWords = new Set(functional_1.Status.isSFC ? [] : ['state', 'props']);
|
58 | this.topLevelIfStatement = new Set();
|
59 | this.usedEvents = new Set();
|
60 | this.loopCalleeId = new Set();
|
61 | this.usedThisProperties = new Set();
|
62 | this.incrementCalleeId = env_1.isTestEnv ? utils_1.incrementId() : incrementCalleeId;
|
63 | this.loopArrayId = env_1.isTestEnv ? utils_1.incrementId() : incrementLoopArrayId;
|
64 | this.classComputedState = new Set();
|
65 | this.propsSettingExpressions = new Map();
|
66 | this.genCompidExprs = new Set();
|
67 | this.loopCallees = new Set();
|
68 | this.loopIfStemComponentMap = new Map();
|
69 | this.hasNoReturnLoopStem = false;
|
70 | this.isDefaultRender = false;
|
71 |
|
72 | this.renderMethodName = '';
|
73 | this.deferedHandleClosureJSXFunc = [];
|
74 | this.ancestorConditions = new Set();
|
75 | this.handleJSXElement = (jsxElementPath, func) => {
|
76 | const parentNode = jsxElementPath.parent;
|
77 | const parentPath = jsxElementPath.parentPath;
|
78 | const isJSXChildren = t.isJSXElement(parentNode);
|
79 | if (!isJSXChildren) {
|
80 | let statementParent = jsxElementPath.getStatementParent();
|
81 | const isReturnStatement = statementParent.isReturnStatement();
|
82 | const isIfStemInLoop = this.isIfStemInLoop(jsxElementPath);
|
83 | const isFinalReturn = statementParent.getFunctionParent().isClassMethod();
|
84 | if (!(statementParent.isVariableDeclaration() ||
|
85 | statementParent.isExpressionStatement())) {
|
86 | statementParent = statementParent.findParent(s => s.isVariableDeclaration() || s.isExpressionStatement());
|
87 | }
|
88 | if (t.isVariableDeclarator(parentNode)) {
|
89 | if (statementParent) {
|
90 | const name = utils_1.findIdentifierFromStatement(statementParent.node);
|
91 |
|
92 | name && this.templates.set(name, jsxElementPath.node);
|
93 | }
|
94 | }
|
95 | func({ parentNode, parentPath, statementParent, isReturnStatement, isFinalReturn, isIfStemInLoop });
|
96 | }
|
97 | };
|
98 | this.isIfStemInLoop = (p) => {
|
99 | const ifStem = p.findParent(p => p.isIfStatement());
|
100 | if (ifStem && ifStem.isIfStatement()) {
|
101 | const loopStem = ifStem.findParent(p => p.isCallExpression());
|
102 | if (loopStem && utils_1.isArrayMapCallExpression(loopStem)) {
|
103 | return true;
|
104 | }
|
105 | }
|
106 | return false;
|
107 | };
|
108 | this.isLiteralOrUndefined = (node) => t.isLiteral(node) || t.isIdentifier(node, { name: 'undefined' });
|
109 | this.replaceIdWithTemplate = (handleRefId = false) => (path) => {
|
110 | if (!t.isJSXAttribute(path.parent)) {
|
111 | path.traverse({
|
112 | Identifier: (path) => {
|
113 | const parentPath = path.parentPath;
|
114 | if (parentPath.isConditionalExpression() ||
|
115 | parentPath.isLogicalExpression() ||
|
116 | path.isReferencedIdentifier()) {
|
117 | const name = path.node.name;
|
118 | if (handleRefId && Object.keys(this.renderScope.getAllBindings()).includes(name)) {
|
119 | this.addRefIdentifier(path, path.node);
|
120 |
|
121 | }
|
122 | if (this.templates.has(name)) {
|
123 | path.replaceWith(this.templates.get(name));
|
124 | }
|
125 | }
|
126 | }
|
127 | });
|
128 | }
|
129 | };
|
130 | this.hasStateOrProps = (key) => (p) => t.isObjectProperty(p) && t.isIdentifier(p.key) && p.key.name === key;
|
131 | this.returnedifStemJSX = new Set();
|
132 | this.loopComponentVisitor = {
|
133 | VariableDeclarator: (path) => {
|
134 | const id = path.get('id');
|
135 | const init = path.get('init');
|
136 | const parentPath = path.parentPath;
|
137 | if (id.isObjectPattern() &&
|
138 | init.isThisExpression() &&
|
139 | parentPath.isVariableDeclaration()) {
|
140 | const { properties } = id.node;
|
141 | this.destructStateOrProps('state', path, properties, parentPath);
|
142 | this.destructStateOrProps('props', path, properties, parentPath);
|
143 | }
|
144 | },
|
145 | JSXElement: {
|
146 | enter: (jsxElementPath) => {
|
147 | this.handleJSXElement(jsxElementPath, (options) => {
|
148 | this.handleConditionExpr(options, jsxElementPath);
|
149 | if (this.isIfStemInLoop(jsxElementPath)) {
|
150 | this.handleJSXInIfStatement(jsxElementPath, options);
|
151 | this.removeJSXStatement();
|
152 | }
|
153 | if (options.parentPath.isReturnStatement() && this.returnedifStemJSX.has(options.parentPath.scope)) {
|
154 | const block = jsx_1.buildBlockElement();
|
155 | jsx_1.setJSXAttr(block, adapter_1.Adapter.else);
|
156 | block.children = [jsxElementPath.node];
|
157 | jsxElementPath.replaceWith(block);
|
158 | this.returnedifStemJSX.delete(options.parentPath.scope);
|
159 | }
|
160 | });
|
161 | },
|
162 | exit: (jsxElementPath) => {
|
163 | this.handleJSXElement(jsxElementPath, ({ parentNode, parentPath, statementParent, isFinalReturn }) => {
|
164 | if (statementParent && statementParent.findParent(p => p === this.renderPath)) {
|
165 | this.jsxDeclarations.add(statementParent);
|
166 | }
|
167 | if (t.isReturnStatement(parentNode)) {
|
168 | if (!isFinalReturn) {
|
169 | const callExpr = parentPath.findParent(p => p.isCallExpression());
|
170 | if (callExpr && callExpr.isCallExpression()) {
|
171 | const callee = callExpr.node.callee;
|
172 | if (this.loopComponents.has(callExpr)) {
|
173 | return;
|
174 | }
|
175 | if (t.isMemberExpression(callee) &&
|
176 | t.isIdentifier(callee.property) &&
|
177 | callee.property.name === 'map') {
|
178 | let ary = callee.object;
|
179 | if (t.isCallExpression(ary) || utils_1.isContainFunction(callExpr.get('callee').get('object'))) {
|
180 | this.loopCallees.add(ary);
|
181 | const variableName = `${constant_1.LOOP_CALLEE}_${this.incrementCalleeId()}`;
|
182 | callExpr.getStatementParent().insertBefore(utils_1.buildConstVariableDeclaration(variableName, utils_1.setParentCondition(jsxElementPath, ary, true)));
|
183 | ary = t.identifier(variableName);
|
184 | }
|
185 | if (t.isMemberExpression(ary)) {
|
186 | const id = utils_1.findFirstIdentifierFromMemberExpression(ary);
|
187 | if (t.isIdentifier(id)) {
|
188 | this.referencedIdentifiers.add(id);
|
189 | }
|
190 | }
|
191 | else if (t.isIdentifier(ary)) {
|
192 | const parentCallExpr = callExpr.find(p => p.isCallExpression());
|
193 | if (!utils_1.isArrayMapCallExpression(parentCallExpr) && parentCallExpr !== callExpr) {
|
194 | this.referencedIdentifiers.add(ary);
|
195 | }
|
196 | }
|
197 | const block = jsx_1.buildBlockElement();
|
198 | const hasIfAttr = jsxElementPath.node.openingElement.attributes.find(a => t.isJSXIdentifier(a.name) && a.name.name === adapter_1.Adapter.if);
|
199 | const needWrapper = "swan" === adapter_1.Adapter.type && hasIfAttr;
|
200 | if (needWrapper) {
|
201 | block.children = [jsxElementPath.node];
|
202 | jsxElementPath.replaceWith(block);
|
203 | }
|
204 | jsx_1.setJSXAttr(needWrapper ? block : jsxElementPath.node, adapter_1.Adapter.for, t.jSXExpressionContainer(ary));
|
205 | this.loopCalleeId.add(utils_1.findFirstIdentifierFromMemberExpression(callee));
|
206 | const [func] = callExpr.node.arguments;
|
207 | if (t.isFunctionExpression(func) ||
|
208 | t.isArrowFunctionExpression(func)) {
|
209 | const [item, index] = func.params;
|
210 | let itemName = '';
|
211 | let indexName = '';
|
212 | if (t.isIdentifier(item)) {
|
213 | if ("quickapp" !== adapter_1.Adapter.type) {
|
214 | jsx_1.setJSXAttr(needWrapper ? block : jsxElementPath.node, adapter_1.Adapter.forItem, t.stringLiteral(item.name));
|
215 | }
|
216 | else {
|
217 | itemName = item.name;
|
218 | }
|
219 | this.loopScopes.add(item.name);
|
220 | }
|
221 | else if (t.isObjectPattern(item)) {
|
222 | throw utils_1.codeFrameError(item.loc, 'JSX map 循环参数暂时不支持使用 Object pattern 解构。');
|
223 | }
|
224 | else {
|
225 | jsx_1.setJSXAttr(needWrapper ? block : jsxElementPath.node, adapter_1.Adapter.forItem, t.stringLiteral('__item'));
|
226 | func.params[0] = t.identifier('__item');
|
227 | }
|
228 | if (t.isIdentifier(index)) {
|
229 | if ("quickapp" !== adapter_1.Adapter.type) {
|
230 | jsx_1.setJSXAttr(needWrapper ? block : jsxElementPath.node, adapter_1.Adapter.forIndex, t.stringLiteral(index.name));
|
231 | }
|
232 | else {
|
233 | indexName = index.name;
|
234 | }
|
235 | this.loopScopes.add(index.name);
|
236 |
|
237 | }
|
238 | else if (index === undefined) {
|
239 | if (process.env.NODE_ENV !== 'test') {
|
240 | const uid = this.renderScope.generateUid('anonIdx');
|
241 | func.params[1] = t.identifier(uid);
|
242 | jsx_1.setJSXAttr(needWrapper ? block : jsxElementPath.node, adapter_1.Adapter.forIndex, t.stringLiteral(this.renderScope.generateUid('anonIdx')));
|
243 | }
|
244 | }
|
245 | else {
|
246 | throw utils_1.codeFrameError(index, '包含 JSX 的 map 循环第二个参数只能是一个普通标识符');
|
247 | }
|
248 | if ("quickapp" === adapter_1.Adapter.type) {
|
249 | if (itemName || indexName) {
|
250 | const code = jsx_1.generateJSXAttr(ary);
|
251 | let forExpr;
|
252 | if (itemName && !indexName) {
|
253 | forExpr = `${itemName} in ${code}`;
|
254 | }
|
255 | else {
|
256 | forExpr = `(${indexName}, ${itemName}) in ${code}`;
|
257 | }
|
258 | jsx_1.setJSXAttr(jsxElementPath.node, adapter_1.Adapter.for, t.stringLiteral(`{{${forExpr}}}`));
|
259 | }
|
260 |
|
261 |
|
262 |
|
263 | }
|
264 | this.loopComponents.set(callExpr, jsxElementPath);
|
265 | let loopComponentName;
|
266 | const parentCallee = callExpr.findParent(c => utils_1.isArrayMapCallExpression(c));
|
267 | if (utils_1.isArrayMapCallExpression(parentCallee)) {
|
268 | loopComponentName = `${constant_1.LOOP_CALLEE}_${this.incrementCalleeId()}`;
|
269 | }
|
270 | else {
|
271 | loopComponentName = 'loopArray' + this.loopArrayId();
|
272 | }
|
273 | this.loopComponentNames.set(callExpr, loopComponentName);
|
274 |
|
275 | if (statementParent) {
|
276 | const name = utils_1.findIdentifierFromStatement(statementParent.node);
|
277 |
|
278 | name && this.templates.set(name, jsxElementPath.node);
|
279 | }
|
280 | }
|
281 | }
|
282 | }
|
283 | }
|
284 | }
|
285 | else if (t.isArrowFunctionExpression(parentNode)) {
|
286 | parentPath.replaceWith(t.arrowFunctionExpression(parentNode.params, t.blockStatement([
|
287 | t.returnStatement(jsxElementPath.node)
|
288 | ])));
|
289 | }
|
290 | });
|
291 | }
|
292 | }
|
293 | };
|
294 | this.renameIfScopeVaribale = (blockStatement) => {
|
295 | return {
|
296 | VariableDeclarator: (path) => {
|
297 | const { id, init } = path.node;
|
298 | const ifStem = path.parentPath.parentPath.parentPath;
|
299 | if (!ifStem.isIfStatement() || utils_1.isContainJSXElement(path)) {
|
300 | return;
|
301 | }
|
302 | if (t.isIdentifier(id)) {
|
303 | if (id.name.startsWith('loopArray') || id.name.startsWith(constant_1.LOOP_CALLEE)) {
|
304 | this.renderPath.node.body.body.unshift(t.variableDeclaration('let', [t.variableDeclarator(t.identifier(id.name))]));
|
305 | path.parentPath.replaceWith(template('ID = INIT;')({ ID: t.identifier(id.name), INIT: init }));
|
306 | }
|
307 | else if (id.name.startsWith('$props__')) {
|
308 | path.skip();
|
309 | }
|
310 | else {
|
311 | const newId = this.renderScope.generateDeclaredUidIdentifier('$' + id.name);
|
312 | blockStatement.scope.rename(id.name, newId.name);
|
313 | path.parentPath.replaceWith(template('ID = INIT;')({ ID: newId, INIT: init || t.identifier('undefined') }));
|
314 | }
|
315 | }
|
316 | },
|
317 | JSXElement: (jsxElementPath) => {
|
318 | this.handleJSXElement(jsxElementPath, (options) => {
|
319 | this.handleConditionExpr(options, jsxElementPath);
|
320 | });
|
321 | },
|
322 | JSXExpressionContainer: this.replaceIdWithTemplate(true)
|
323 | };
|
324 | };
|
325 | this.findParallelIfStem = (p) => {
|
326 | const exprs = new Set();
|
327 | let expr = p.parentPath;
|
328 | while (expr.isIfStatement()) {
|
329 | exprs.add(expr);
|
330 | expr = expr.parentPath;
|
331 | }
|
332 | return exprs;
|
333 | };
|
334 | this.insertElseBlock = (block, jsx, test) => {
|
335 | if (this.isEmptyBlock(block)) {
|
336 | return;
|
337 | }
|
338 | for (const child of block.children) {
|
339 | if (!t.isJSXElement(child)) {
|
340 | continue;
|
341 | }
|
342 | const ifAttr = child.openingElement.attributes.find(a => t.isJSXIdentifier(a.name) && a.name.name === adapter_1.Adapter.if);
|
343 | const ifElseAttr = child.openingElement.attributes.find(a => t.isJSXIdentifier(a.name) && a.name.name === adapter_1.Adapter.elseif);
|
344 | if ((ifAttr && t.isJSXExpressionContainer(ifAttr.value, { expression: test }))
|
345 | ||
|
346 | (ifElseAttr && t.isJSXExpressionContainer(ifElseAttr.value, { expression: test }))) {
|
347 | block.children.push(jsx);
|
348 | break;
|
349 | }
|
350 | else {
|
351 | this.insertElseBlock(child, jsx, test);
|
352 | }
|
353 | }
|
354 | };
|
355 | this.handleNestedIfStatement = (block, jsx, test, hasNest, isElse) => {
|
356 | if (this.isEmptyBlock(block)) {
|
357 | return;
|
358 | }
|
359 | for (const [index, child] of block.children.entries()) {
|
360 | if (!t.isJSXElement(child)) {
|
361 | continue;
|
362 | }
|
363 | const ifAttr = child.openingElement.attributes.find(a => t.isJSXIdentifier(a.name) && a.name.name === adapter_1.Adapter.if);
|
364 | const ifElseAttr = child.openingElement.attributes.find(a => t.isJSXIdentifier(a.name) && a.name.name === adapter_1.Adapter.elseif);
|
365 | if ((ifAttr && t.isJSXExpressionContainer(ifAttr.value, { expression: test }))
|
366 | ||
|
367 | (ifElseAttr && t.isJSXExpressionContainer(ifElseAttr.value, { expression: test }))) {
|
368 | if (isElse) {
|
369 | const nextChild = block.children[index + 1];
|
370 | if (t.isJSXElement(nextChild)) {
|
371 | nextChild.children.push(jsx);
|
372 | }
|
373 | }
|
374 | else {
|
375 | child.children.push(jsx);
|
376 | }
|
377 | hasNest = true;
|
378 | break;
|
379 | }
|
380 | else {
|
381 | this.handleNestedIfStatement(child, jsx, test, hasNest, isElse);
|
382 | }
|
383 | }
|
384 | };
|
385 | this.isEmptyBlock = ((block) => block.children.length === 0 && block.openingElement.attributes.length === 0);
|
386 | this.prefixExpr = () => this.isDefaultRender ? t.identifier('__prefix') : t.identifier(constant_1.CLASS_COMPONENT_UID);
|
387 | this.propsDecls = new Map();
|
388 | this.isInternalComponent = (element) => {
|
389 | return t.isJSXIdentifier(element.name) &&
|
390 | !constant_1.DEFAULT_Component_SET.has(element.name.name) &&
|
391 | !constant_1.DEFAULT_Component_SET_COPY.has(element.name.name) &&
|
392 | /[A-Z]/.test(element.name.name.charAt(0));
|
393 | };
|
394 | this.jsxElementVisitor = {
|
395 | JSXElement: (jsxElementPath) => {
|
396 | this.handleJSXElement(jsxElementPath, (options) => {
|
397 | this.handleConditionExpr(options, jsxElementPath);
|
398 | this.handleJSXInIfStatement(jsxElementPath, options);
|
399 | });
|
400 |
|
401 | jsxElementPath.traverse(this.jsxAttrVisitor);
|
402 | }
|
403 | };
|
404 | this.jsxAttrVisitor = {
|
405 | JSXExpressionContainer: (path) => {
|
406 | if (!isChildrenOfJSXAttr(path)) {
|
407 | return;
|
408 | }
|
409 | const expression = path.get('expression');
|
410 | if (expression.isStringLiteral()) {
|
411 | path.replaceWith(expression);
|
412 | }
|
413 | else if (expression.isCallExpression()) {
|
414 | const node = expression.node;
|
415 | if (t.isMemberExpression(node.callee) &&
|
416 | t.isIdentifier(node.callee.property) &&
|
417 | node.callee.property.name === 'bind') {
|
418 | const JSXElement = path.findParent(p => p.isJSXElement())
|
419 | .node;
|
420 | const componentName = JSXElement.openingElement.name;
|
421 | if (adapter_1.isNewPropsSystem() &&
|
422 | t.isJSXIdentifier(componentName)) {
|
423 | if (constant_1.THIRD_PARTY_COMPONENTS.has(componentName.name)) {
|
424 |
|
425 | }
|
426 | else if (!constant_1.DEFAULT_Component_SET.has(componentName.name)) {
|
427 | return;
|
428 | }
|
429 | }
|
430 |
|
431 | let bindCalleeName = null;
|
432 | if (t.isIdentifier(node.callee.object)) {
|
433 | bindCalleeName = node.callee.object.name;
|
434 | }
|
435 | else if (t.isMemberExpression(node.callee.object)) {
|
436 | if (t.isIdentifier(node.callee.object.property)) {
|
437 | bindCalleeName = node.callee.object.property.name;
|
438 | }
|
439 | }
|
440 | if (bindCalleeName !== null) {
|
441 | const attr = path.parentPath.node;
|
442 | let bindEventName = attr.name.name;
|
443 | bindEventName = bindEventName.replace(/^bind|^catch/, '');
|
444 | const args = expression.get('arguments');
|
445 | args.forEach((arg, index) => {
|
446 | const node = arg.node;
|
447 | const argName = babel_generator_1.default(node).code;
|
448 | if (index === 0) {
|
449 | jsx_1.setJSXAttr(JSXElement, `data-e-${bindEventName}-so`, t.stringLiteral(argName));
|
450 | }
|
451 | else {
|
452 | let expr = null;
|
453 | if (t.isIdentifier(node) && path.scope.hasBinding(argName)) {
|
454 | this.addRefIdentifier(path, node);
|
455 | expr = t.jSXExpressionContainer(node);
|
456 | }
|
457 | else if (t.isMemberExpression(node)) {
|
458 | const id = utils_1.findFirstIdentifierFromMemberExpression(node);
|
459 | this.addRefIdentifier(path, id);
|
460 | expr = t.jSXExpressionContainer(node);
|
461 | }
|
462 | else if (node.type === 'NumericLiteral' || t.isStringLiteral(node) || t.isBooleanLiteral(node) || t.isNullLiteral(node)) {
|
463 | expr = t.jSXExpressionContainer(node);
|
464 | }
|
465 | else if (utils_1.hasComplexExpression(arg)) {
|
466 | const isCookedLoop = JSXElement.openingElement.attributes.some(attr => attr.name.name === adapter_1.Adapter.for);
|
467 | if (isCookedLoop) {
|
468 | throw utils_1.codeFrameError(arg.node, '在循环中使用 bind 时,需要声明将此复杂表达式声明为一个变量再放入 bind 参数中。');
|
469 | }
|
470 | else {
|
471 | const id = utils_1.generateAnonymousState(this.renderScope, arg, this.referencedIdentifiers);
|
472 | expr = t.jSXExpressionContainer(id);
|
473 | }
|
474 | }
|
475 | else {
|
476 | expr = t.jSXExpressionContainer(t.identifier(argName));
|
477 | }
|
478 | jsx_1.setJSXAttr(JSXElement, `data-e-${bindEventName}-a-${utils_1.toLetters(index)}`, expr);
|
479 | }
|
480 | });
|
481 | expression.replaceWith(t.stringLiteral(`${bindCalleeName}`));
|
482 | }
|
483 | }
|
484 | }
|
485 | },
|
486 | JSXAttribute: (path) => {
|
487 | const { name, value } = path.node;
|
488 | let eventShouldBeCatched = false;
|
489 | const jsxElementPath = path.parentPath.parentPath;
|
490 | if (t.isJSXIdentifier(name) && jsxElementPath.isJSXElement()) {
|
491 | const componentName = jsxElementPath.node.openingElement.name.name;
|
492 | const isThirdPartyKey = name.name === 'taroKey';
|
493 | if (name.name === 'key' || isThirdPartyKey) {
|
494 | if (constant_1.THIRD_PARTY_COMPONENTS.has(componentName) && !isThirdPartyKey) {
|
495 | return;
|
496 | }
|
497 | const jsx = path.findParent(p => p.isJSXElement());
|
498 | const loopBlock = jsx.findParent(p => {
|
499 | if (p.isJSXElement()) {
|
500 | const element = p.get('openingElement');
|
501 | if (element.get('name').isJSXIdentifier({ name: 'block' })) {
|
502 | const attrs = element.node.attributes;
|
503 | const hasWXForLoop = attrs.some(attr => t.isJSXIdentifier(attr.name, { name: adapter_1.Adapter.for }));
|
504 | const hasWXKey = attrs.some(attr => t.isJSXIdentifier(attr.name, { name: adapter_1.Adapter.key }));
|
505 | return hasWXForLoop && !hasWXKey;
|
506 | }
|
507 | }
|
508 | return false;
|
509 | });
|
510 | if (loopBlock) {
|
511 | jsx_1.setJSXAttr(loopBlock.node, adapter_1.Adapter.key, value);
|
512 | path.remove();
|
513 | }
|
514 | else {
|
515 | path.get('name').replaceWith(t.jSXIdentifier(adapter_1.Adapter.key));
|
516 | }
|
517 | }
|
518 | else if (name.name.startsWith('on')) {
|
519 | if (t.isJSXExpressionContainer(value)) {
|
520 | const methodName = utils_1.findMethodName(value.expression);
|
521 | methodName && this.usedEvents.add(methodName);
|
522 | const method = this.methods.get(methodName);
|
523 | const classDecl = path.findParent(p => p.isClassDeclaration());
|
524 | const componentName = jsxElementPath.node.openingElement.name;
|
525 |
|
526 |
|
527 |
|
528 |
|
529 |
|
530 | if (!babel_generator_1.default(value.expression).code.includes('.bind') &&
|
531 | (!adapter_1.isNewPropsSystem() ||
|
532 | (t.isJSXIdentifier(componentName) && constant_1.DEFAULT_Component_SET.has(componentName.name)))) {
|
533 | path.node.value = t.stringLiteral(`${methodName}`);
|
534 | }
|
535 | if (this.methods.has(methodName)) {
|
536 | eventShouldBeCatched = utils_1.isContainStopPropagation(method);
|
537 | }
|
538 | if (classDecl && classDecl.isClassDeclaration()) {
|
539 | const superClass = utils_1.getSuperClassCode(classDecl);
|
540 | if (superClass) {
|
541 | try {
|
542 | const ast = babel_core_1.transform(superClass.code, options_1.buildBabelTransformOptions()).ast;
|
543 | babel_traverse_1.default(ast, {
|
544 | ClassMethod(p) {
|
545 | if (!p.get('key').isIdentifier({ name: methodName })) {
|
546 | return;
|
547 | }
|
548 | eventShouldBeCatched = utils_1.isContainStopPropagation(method);
|
549 | },
|
550 | ClassProperty(p) {
|
551 | if (!p.get('key').isIdentifier({ name: methodName })) {
|
552 | return;
|
553 | }
|
554 | eventShouldBeCatched = utils_1.isContainStopPropagation(method);
|
555 | }
|
556 | });
|
557 | }
|
558 | catch (error) {
|
559 |
|
560 | }
|
561 | }
|
562 | }
|
563 | if (t.isJSXIdentifier(componentName) && !constant_1.DEFAULT_Component_SET.has(componentName.name)) {
|
564 | const element = path.parent;
|
565 | if (process.env.NODE_ENV !== 'test' && adapter_1.Adapter.type !== "alipay" ) {
|
566 | const fnName = `${constant_1.FN_PREFIX}${name.name}`;
|
567 | element.attributes = element.attributes.concat([t.jSXAttribute(t.jSXIdentifier(fnName))]);
|
568 | }
|
569 | }
|
570 | }
|
571 | if (t.isJSXIdentifier(jsxElementPath.node.openingElement.name)) {
|
572 | const componentName = jsxElementPath.node.openingElement.name.name;
|
573 | if (adapter_1.Adapter.type === "alipay" ) {
|
574 | let transformName = name.name;
|
575 | if (constant_1.DEFAULT_Component_SET.has(componentName) && constant_1.ALIPAY_BUBBLE_EVENTS.has(name.name)) {
|
576 | if (name.name === 'onClick') {
|
577 | transformName = eventShouldBeCatched ? 'catchTap' : 'onTap';
|
578 | }
|
579 | else {
|
580 | transformName = `${eventShouldBeCatched ? 'catch' : 'on'}${name.name.slice(2)}`;
|
581 | }
|
582 | }
|
583 | path.node.name = t.jSXIdentifier(transformName);
|
584 | }
|
585 | else if (adapter_1.Adapter.type === "quickapp" ) {
|
586 | const transformName = name.name;
|
587 | path.node.name = t.jSXIdentifier(transformName);
|
588 | }
|
589 | else if (constant_1.DEFAULT_Component_SET.has(componentName)) {
|
590 | let transformName = `${eventShouldBeCatched ? 'catch' : 'bind'}`
|
591 | + name.name.slice(2).toLowerCase();
|
592 | if (name.name === 'onClick') {
|
593 | transformName = eventShouldBeCatched ? 'catchtap' : 'bindtap';
|
594 | }
|
595 | path.node.name = t.jSXIdentifier(transformName);
|
596 | }
|
597 | else if (constant_1.THIRD_PARTY_COMPONENTS.has(componentName)) {
|
598 | path.node.name = t.jSXIdentifier('bind' + name.name[2].toLowerCase() + name.name.slice(3));
|
599 | }
|
600 | else {
|
601 | path.node.name = t.jSXIdentifier('bind' + name.name.toLowerCase());
|
602 | }
|
603 | }
|
604 |
|
605 |
|
606 |
|
607 |
|
608 | }
|
609 | else if (/^render[A-Z]/.test(name.name) && !constant_1.DEFAULT_Component_SET.has(componentName)) {
|
610 | if (!t.isJSXExpressionContainer(value)) {
|
611 | throw utils_1.codeFrameError(value, '以 render 开头的 props 只能传入包含一个 JSX 元素的 JSX 表达式。');
|
612 | }
|
613 | const expression = value.expression;
|
614 | if (!t.isJSXElement(expression)) {
|
615 | throw utils_1.codeFrameError(value, '以 render 开头的 props 只能传入包含一个 JSX 元素的 JSX 表达式。');
|
616 | }
|
617 | const slotName = utils_1.getSlotName(name.name);
|
618 | const slot = lodash_1.cloneDeep(expression);
|
619 | const view = t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier('View'), []), t.jSXClosingElement(t.jSXIdentifier('View')), []);
|
620 | view.children.push(slot);
|
621 | jsx_1.setJSXAttr(view, 'slot', t.stringLiteral(slotName));
|
622 | jsxElementPath.node.children.push(view);
|
623 | path.remove();
|
624 | }
|
625 | }
|
626 | },
|
627 | Identifier: (path) => {
|
628 | if (!isChildrenOfJSXAttr(path)) {
|
629 | return;
|
630 | }
|
631 | if (!path.isReferencedIdentifier()) {
|
632 | return;
|
633 | }
|
634 | const parentPath = path.parentPath;
|
635 | if (parentPath.isConditionalExpression() ||
|
636 | parentPath.isLogicalExpression() ||
|
637 | parentPath.isJSXExpressionContainer() ||
|
638 | parentPath.isBinaryExpression() ||
|
639 | this.renderScope.hasOwnBinding(path.node.name)) {
|
640 | this.addRefIdentifier(path, path.node);
|
641 | }
|
642 | },
|
643 | MemberExpression: {
|
644 | exit: (path) => {
|
645 | const { object, property } = path.node;
|
646 | if (!path.isReferencedMemberExpression()) {
|
647 | return;
|
648 | }
|
649 | if (!t.isThisExpression(object)) {
|
650 | return;
|
651 | }
|
652 | const reserves = new Set([
|
653 | 'state',
|
654 | 'props',
|
655 | ...this.methods.keys()
|
656 | ]);
|
657 | if (t.isIdentifier(property) || t.isMemberExpression(property)) {
|
658 | const id = t.isIdentifier(property) ? property : utils_1.findFirstIdentifierFromMemberExpression(property);
|
659 | if (reserves.has(id.name)) {
|
660 | return;
|
661 | }
|
662 | const jsxAttr = path.findParent(p => p.isJSXAttribute());
|
663 | if (jsxAttr && t.isJSXIdentifier(jsxAttr.node.name) && jsxAttr.node.name.name.startsWith('on')) {
|
664 | return;
|
665 | }
|
666 | if (t.isIdentifier(id) && !(id.name.startsWith('_create') && id.name.endsWith('Data'))) {
|
667 | this.referencedIdentifiers.add(id);
|
668 | this.usedThisProperties.add(id.name);
|
669 | }
|
670 | }
|
671 | },
|
672 | enter: (path) => {
|
673 | if (!isChildrenOfJSXAttr(path)) {
|
674 | return;
|
675 | }
|
676 | if (!path.isReferencedMemberExpression() || path.parentPath.isMemberExpression()) {
|
677 | return;
|
678 | }
|
679 | const { object, property } = path.node;
|
680 | if (t.isMemberExpression(object) &&
|
681 | t.isThisExpression(object.object) &&
|
682 | t.isIdentifier(object.property, { name: 'state' })) {
|
683 | if (t.isIdentifier(property)) {
|
684 | this.usedThisState.add(property.name);
|
685 | }
|
686 | else if (t.isMemberExpression(property)) {
|
687 | const id = utils_1.findFirstIdentifierFromMemberExpression(property);
|
688 | if (id && this.renderScope.hasBinding(id.name)) {
|
689 | this.usedThisState.add(id.name);
|
690 | }
|
691 | }
|
692 | return;
|
693 | }
|
694 | const code = babel_generator_1.default(path.node).code;
|
695 | if (code.includes('this.$router.params') && t.isIdentifier(property)) {
|
696 | const name = this.renderScope.generateUid(property.name);
|
697 | const dcl = utils_1.buildConstVariableDeclaration(name, path.node);
|
698 | this.renderPath.node.body.body.unshift(dcl);
|
699 | path.replaceWith(t.identifier(name));
|
700 | }
|
701 | const parentPath = path.parentPath;
|
702 | const id = utils_1.findFirstIdentifierFromMemberExpression(path.node);
|
703 | if (t.isThisExpression(id)) {
|
704 | return;
|
705 | }
|
706 | if (parentPath.isConditionalExpression() ||
|
707 | parentPath.isLogicalExpression() ||
|
708 | parentPath.isJSXExpressionContainer() ||
|
709 | parentPath.isBinaryExpression() ||
|
710 | (this.renderScope.hasOwnBinding(id.name))) {
|
711 | this.addRefIdentifier(path, id);
|
712 | }
|
713 | }
|
714 | },
|
715 | ArrowFunctionExpression: (path) => {
|
716 | if (!isChildrenOfJSXAttr(path)) {
|
717 | return;
|
718 | }
|
719 | const uid = path.scope.generateUid('_anonymous_function_');
|
720 | const c = t.classProperty(t.identifier(uid), path.node);
|
721 | this.classProperties.add(c);
|
722 | }
|
723 | };
|
724 | this.visitors = Object.assign(Object.assign({ MemberExpression: (path) => {
|
725 | const { object, property } = path.node;
|
726 | if (t.isThisExpression(object) && t.isIdentifier(property) && property.name.startsWith('renderClosure')) {
|
727 | const parentPath = path.parentPath;
|
728 | if (parentPath.isVariableDeclarator()) {
|
729 | const id = parentPath.node.id;
|
730 | if (t.isIdentifier(id) && id.name.startsWith('renderClosure')) {
|
731 | this.deferedHandleClosureJSXFunc.push(() => {
|
732 | const classMethod = this.methods.get(id.name);
|
733 | if (classMethod && classMethod.isClassMethod()) {
|
734 | path.replaceWith(t.arrowFunctionExpression([t.identifier(constant_1.CLASS_COMPONENT_UID)], t.blockStatement([
|
735 | t.returnStatement(t.arrowFunctionExpression(classMethod.node.params, classMethod.node.body))
|
736 | ])));
|
737 |
|
738 | }
|
739 | });
|
740 | }
|
741 | }
|
742 | }
|
743 | if (t.isThisExpression(object) && t.isIdentifier(property) && /^render[A-Z]/.test(this.renderMethodName)) {
|
744 | const s = new Set(['state', 'props']);
|
745 | if (s.has(property.name) && path.parentPath.isMemberExpression()) {
|
746 | const p = path.parentPath.node.property;
|
747 | let id = { name: 'example' };
|
748 | if (t.isIdentifier(p)) {
|
749 | id = p;
|
750 | }
|
751 | else if (t.isMemberExpression(p)) {
|
752 | id = utils_1.findFirstIdentifierFromMemberExpression(p);
|
753 | }
|
754 |
|
755 | console.warn(utils_1.codeFrameError(path.parentPath.node, `\n 在形如以 render 开头的 ${this.renderMethodName}() 类函数中,请先把 this.${property.name} 解构出来才进行使用。\n 例如: const { ${id.name} } = this.${property.name}`).message);
|
756 | }
|
757 | }
|
758 | }, VariableDeclarator: (path) => {
|
759 | const init = path.get('init');
|
760 | const id = path.get('id');
|
761 | const ifStem = init.findParent(p => p.isIfStatement());
|
762 |
|
763 | if (ifStem && init.node === null) {
|
764 | init.replaceWith(t.identifier('undefined'));
|
765 | }
|
766 | let isDerivedFromState = false;
|
767 | if (init.isMemberExpression()) {
|
768 | const object = init.get('object');
|
769 | if (object.isMemberExpression() && object.get('object').isThisExpression() && object.get('property').isIdentifier({ name: 'state' })) {
|
770 | isDerivedFromState = true;
|
771 | }
|
772 | if (object.isThisExpression() && init.get('property').isIdentifier({ name: 'state' })) {
|
773 | isDerivedFromState = true;
|
774 | }
|
775 | }
|
776 | if (!isDerivedFromState) {
|
777 | const errMsg = 'Warning: render 函数定义一个不从 this.state 解构或赋值而来的变量,此变量又与 this.state 下的变量重名可能会导致无法渲染。';
|
778 | if (id.isIdentifier()) {
|
779 | const name = id.node.name;
|
780 | if (this.initState.has(name)) {
|
781 |
|
782 | console.log(utils_1.codeFrameError(id.node, errMsg).message);
|
783 | }
|
784 | }
|
785 | if (id.isObjectPattern()) {
|
786 | const { properties } = id.node;
|
787 | for (const p of properties) {
|
788 | if (t.isIdentifier(p)) {
|
789 | if (this.initState.has(p.name)) {
|
790 |
|
791 | console.log(utils_1.codeFrameError(id.node, errMsg).message);
|
792 | }
|
793 | }
|
794 | if (t.isSpreadProperty(p) && t.isIdentifier(p.argument)) {
|
795 | if (this.initState.has(p.argument.name)) {
|
796 |
|
797 | console.log(utils_1.codeFrameError(id.node, errMsg).message);
|
798 | }
|
799 | }
|
800 | }
|
801 | }
|
802 | }
|
803 | }, JSXEmptyExpression(path) {
|
804 | const parent = path.parentPath;
|
805 | if (path.parentPath.isJSXExpressionContainer()) {
|
806 | parent.remove();
|
807 | }
|
808 | },
|
809 | NullLiteral(path) {
|
810 | const statementParent = path.getStatementParent();
|
811 | const callExprs = findParents(path, p => p.isCallExpression());
|
812 | if (callExprs.some(callExpr => callExpr && t.isIdentifier(callExpr.node.callee) && /^use[A-Z]/.test(callExpr.node.callee.name))) {
|
813 | return;
|
814 | }
|
815 | if (statementParent && statementParent.isReturnStatement() && !t.isBinaryExpression(path.parent) && !isChildrenOfJSXAttr(path)) {
|
816 | path.replaceWith(t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier('View'), []), undefined, [], true));
|
817 | }
|
818 | }, ReturnStatement: (path) => {
|
819 | const parentPath = path.parentPath;
|
820 | if (parentPath.parentPath.isClassMethod() ||
|
821 | (parentPath.parentPath.isIfStatement() && parentPath.parentPath.parentPath.isClassMethod())) {
|
822 | this.replaceIdWithTemplate()(path);
|
823 | }
|
824 | } }, this.jsxElementVisitor), { JSXExpressionContainer: this.replaceIdWithTemplate(true) });
|
825 | this.quickappVistor = {
|
826 | JSXExpressionContainer(path) {
|
827 | if (path.parentPath.isJSXAttribute() || utils_1.isContainJSXElement(path)) {
|
828 | return;
|
829 | }
|
830 | utils_1.replaceJSXTextWithTextComponent(path);
|
831 | }
|
832 | };
|
833 | this.isEmptyProps = (attrs) => attrs.filter(a => {
|
834 | if (t.isJSXSpreadAttribute(a))
|
835 | return true;
|
836 | const list = [adapter_1.Adapter.for, adapter_1.Adapter.forIndex, adapter_1.Adapter.forItem, 'id'];
|
837 | adapter_1.Adapter.type === "weapp" && list.push('extraProps');
|
838 | return !list.includes(a.name.name);
|
839 | }).length === 0;
|
840 | |
841 |
|
842 |
|
843 |
|
844 |
|
845 |
|
846 |
|
847 |
|
848 | this.handleLoopComponents = () => {
|
849 | const replaceQueue = [];
|
850 | let hasLoopRef = false;
|
851 | this.loopComponents.forEach((component, callee) => {
|
852 | if (!callee.isCallExpression()) {
|
853 | return;
|
854 | }
|
855 | if (this.loopIfStemComponentMap.has(callee)) {
|
856 | const block = this.loopIfStemComponentMap.get(callee);
|
857 | const attrs = component.node.openingElement.attributes;
|
858 | const wxForDirectives = new Set([adapter_1.Adapter.for, adapter_1.Adapter.forIndex, adapter_1.Adapter.forItem]);
|
859 | const ifAttrs = attrs.filter(a => wxForDirectives.has(a.name.name));
|
860 | if (ifAttrs.length) {
|
861 | block.openingElement.attributes.push(...ifAttrs);
|
862 | component.node.openingElement.attributes = attrs.filter(a => !wxForDirectives.has(a.name.name));
|
863 | }
|
864 | jsx_1.setJSXAttr(component.node, adapter_1.Adapter.else);
|
865 | block.children.push(component.node);
|
866 | component.replaceWith(block);
|
867 | }
|
868 | for (const dcl of this.jsxDeclarations) {
|
869 | const isChildren = dcl && dcl.findParent(d => d === callee);
|
870 | if (isChildren) {
|
871 | this.jsxDeclarations.delete(dcl);
|
872 | dcl.remove();
|
873 | }
|
874 | }
|
875 | const blockStatementPath = component.findParent(p => p.isBlockStatement());
|
876 | const body = blockStatementPath.node.body;
|
877 | let loopRefComponent = null;
|
878 | this.loopRefs.forEach((ref, jsx) => {
|
879 | if (ref.component.findParent(p => p === component)) {
|
880 | loopRefComponent = jsx;
|
881 | }
|
882 | });
|
883 | const [func] = callee.node.arguments;
|
884 | let indexId = null;
|
885 | let itemId = null;
|
886 | if (t.isFunctionExpression(func) || t.isArrowFunctionExpression(func)) {
|
887 | const params = func.params;
|
888 | if (Array.isArray(params)) {
|
889 | indexId = params[1];
|
890 | itemId = params[0];
|
891 | }
|
892 | }
|
893 | if (this.loopRefs.has(component.node) || loopRefComponent) {
|
894 | hasLoopRef = true;
|
895 | const ref = this.loopRefs.get(component.node) || this.loopRefs.get(loopRefComponent);
|
896 | if (indexId === null || !t.isIdentifier(indexId)) {
|
897 | throw utils_1.codeFrameError(component.node, '在循环中使用 ref 必须暴露循环的第二个参数 `index`');
|
898 | }
|
899 | const id = typeof ref.id === 'string' ? t.binaryExpression('+', t.stringLiteral(ref.id), indexId) : ref.id;
|
900 | const args = [
|
901 | t.identifier('__scope'),
|
902 | t.binaryExpression('+', t.stringLiteral('#'), id)
|
903 | ];
|
904 | if (ref.type === 'component') {
|
905 | args.push(t.stringLiteral('component'));
|
906 | }
|
907 | else {
|
908 | args.push(t.stringLiteral('dom'));
|
909 | }
|
910 | args.push(ref.fn);
|
911 | const callHandleLoopRef = t.callExpression(t.identifier(constant_1.HANDLE_LOOP_REF), args);
|
912 | const loopRefStatement = t.expressionStatement(t.logicalExpression('&&', t.logicalExpression('&&', t.identifier('__scope'), t.identifier('__isRunloopRef')), callHandleLoopRef));
|
913 | body.splice(body.length - 1, 0, !env_1.isTestEnv ? loopRefStatement : t.expressionStatement(callHandleLoopRef));
|
914 | }
|
915 | if (adapter_1.isNewPropsSystem()) {
|
916 | const loopIndices = this.findParentIndices(callee, indexId);
|
917 | const deferCallBack = [];
|
918 | blockStatementPath.traverse({
|
919 | CallExpression(path) {
|
920 | const pathCallee = path.node.callee;
|
921 | if (t.isMemberExpression(pathCallee) &&
|
922 | t.isThisExpression(pathCallee.object) &&
|
923 | t.isIdentifier(pathCallee.property) &&
|
924 | pathCallee.property.name.startsWith('_create') &&
|
925 | pathCallee.property.name.endsWith('Data')) {
|
926 | const arg = path.node.arguments[0];
|
927 | if (t.isBinaryExpression(arg)) {
|
928 | deferCallBack.push(() => {
|
929 | path.node.arguments = [
|
930 | t.binaryExpression('+', arg, t.templateLiteral([
|
931 | t.templateElement({ raw: '' }),
|
932 | ...loopIndices.map(() => t.templateElement({ raw: '' }))
|
933 | ], loopIndices.map(l => t.identifier(l))))
|
934 | ];
|
935 | });
|
936 | }
|
937 | }
|
938 | },
|
939 | JSXElement: path => {
|
940 | const element = path.node.openingElement;
|
941 | if (this.isInternalComponent(element)) {
|
942 | if (this.isEmptyProps(element.attributes)) {
|
943 | return;
|
944 | }
|
945 |
|
946 | const compid = utils_1.genCompid();
|
947 | const prevVariableName = `${constant_1.PREV_COMPID}__${compid}`;
|
948 | const variableName = `${constant_1.COMPID}__${compid}`;
|
949 | const tpmlExprs = [];
|
950 | for (let index = 0; index < loopIndices.length; index++) {
|
951 | const element = loopIndices[index];
|
952 | tpmlExprs.push(t.identifier(element));
|
953 | if (loopIndices[index + 1]) {
|
954 | tpmlExprs.push(t.stringLiteral('-'));
|
955 | }
|
956 | }
|
957 | const compidTempDecl = t.variableDeclaration('const', [
|
958 | t.variableDeclarator(t.arrayPattern([t.identifier(prevVariableName), t.identifier(variableName)]), t.callExpression(t.identifier(constant_1.GEN_COMP_ID), [t.templateLiteral([
|
959 | t.templateElement({ raw: '' }),
|
960 | t.templateElement({ raw: utils_1.createRandomLetters(10) }),
|
961 | ...tpmlExprs.map(() => t.templateElement({ raw: '' }))
|
962 | ], [
|
963 | this.prefixExpr(),
|
964 | ...tpmlExprs
|
965 | ]), t.booleanLiteral(true)]))
|
966 | ]);
|
967 | const properties = this.getPropsFromAttrs(element);
|
968 | const propsSettingExpr = this.genPropsSettingExpression(properties, t.identifier(variableName), t.identifier(prevVariableName));
|
969 | const expr = utils_1.setAncestorCondition(path, propsSettingExpr);
|
970 | this.ancestorConditions.add(expr);
|
971 | body.splice(body.length - 1, 0, compidTempDecl, t.expressionStatement(expr));
|
972 |
|
973 | const [func] = callee.node.arguments;
|
974 | let forItem = null;
|
975 | if (t.isFunctionExpression(func) || t.isArrowFunctionExpression(func)) {
|
976 | forItem = func.params[0];
|
977 | }
|
978 | if (forItem === null || !t.isIdentifier(forItem)) {
|
979 | throw utils_1.codeFrameError(callee.node, '在循环中使用自定义组件时必须暴露循环的第一个参数 `item`');
|
980 | }
|
981 | element.attributes.push(t.jSXAttribute(t.jSXIdentifier('compid'), t.jSXExpressionContainer(t.memberExpression(forItem, t.identifier(variableName)))));
|
982 | }
|
983 | }
|
984 | });
|
985 | deferCallBack.forEach(cb => cb());
|
986 | }
|
987 | let stateToBeAssign = new Set(lodash_1.difference(Object.keys(blockStatementPath.scope.getAllBindings()), Object.keys(this.renderScope.getAllBindings())).filter(i => {
|
988 | return !this.methods.has(i);
|
989 | })
|
990 | .filter(i => !this.loopScopes.has(i))
|
991 | .filter(i => !this.initState.has(i))
|
992 | .filter(i => !this.templates.has(i))
|
993 | .filter(i => !i.includes('.'))
|
994 | .filter(i => i !== constant_1.MAP_CALL_ITERATOR));
|
995 | if (body.length > 1) {
|
996 | const [func] = callee.node.arguments;
|
997 | if (t.isFunctionExpression(func) || t.isArrowFunctionExpression(func)) {
|
998 | const [item, indexParam] = func.params;
|
999 | const parents = findParents(callee, (p) => utils_1.isArrayMapCallExpression(p));
|
1000 | const iterators = new Set([item.name, ...parents
|
1001 | .map((p) => lodash_1.get(p, 'node.arguments[0].params[0].name', ''))
|
1002 | .filter(Boolean)]);
|
1003 | for (const [index, statement] of body.entries()) {
|
1004 | if (t.isVariableDeclaration(statement)) {
|
1005 | for (const dcl of statement.declarations) {
|
1006 | if (t.isIdentifier(dcl.id)) {
|
1007 | const name = dcl.id.name;
|
1008 | if (name.startsWith(constant_1.LOOP_STATE) ||
|
1009 | name.startsWith(constant_1.LOOP_CALLEE) ||
|
1010 | name.startsWith(constant_1.COMPID) ||
|
1011 | name.startsWith('_$indexKey')) {
|
1012 | stateToBeAssign.add(name);
|
1013 | dcl.id = t.identifier(name);
|
1014 | }
|
1015 | }
|
1016 | else if (t.isArrayPattern(dcl.id)) {
|
1017 | dcl.id.elements.forEach(stm => {
|
1018 | if (t.isIdentifier(stm)) {
|
1019 | const name = stm.name;
|
1020 | if (name.startsWith(constant_1.LOOP_STATE) ||
|
1021 | name.startsWith(constant_1.LOOP_CALLEE) ||
|
1022 | name.startsWith(constant_1.COMPID) ||
|
1023 | name.startsWith('_$indexKey')) {
|
1024 | stateToBeAssign.add(name);
|
1025 | }
|
1026 | }
|
1027 | });
|
1028 | }
|
1029 | }
|
1030 | }
|
1031 | if (t.isReturnStatement(statement)) {
|
1032 | body.splice(index, 1);
|
1033 | }
|
1034 | }
|
1035 | stateToBeAssign.forEach(s => this.loopRefIdentifiers.set(s, callee));
|
1036 | const properties = Array.from(stateToBeAssign).map(state => t.objectProperty(t.identifier(state), t.identifier(state)));
|
1037 |
|
1038 | function replaceOriginal(path, parent, name) {
|
1039 | if ((path.isReferencedIdentifier() || t.isAssignmentExpression(parent)) &&
|
1040 | iterators.has(name) &&
|
1041 | !(t.isMemberExpression(parent) && t.isIdentifier(parent.property, { name: constant_1.LOOP_ORIGINAL })) &&
|
1042 | !(t.isMemberExpression(parent) && t.isIdentifier(parent.property) && (parent.property.name.startsWith(constant_1.LOOP_STATE) || parent.property.name.startsWith(constant_1.LOOP_CALLEE) || parent.property.name.startsWith(constant_1.COMPID)))) {
|
1043 | path.replaceWith(t.memberExpression(t.identifier(name), t.identifier(constant_1.LOOP_ORIGINAL)));
|
1044 | }
|
1045 | }
|
1046 | const bodyPath = callee.get('arguments')[0].get('body');
|
1047 | bodyPath.traverse({
|
1048 | Identifier(path) {
|
1049 | const name = path.node.name;
|
1050 | const parent = path.parent;
|
1051 | replaceOriginal(path, parent, name);
|
1052 | }
|
1053 | });
|
1054 | const replacements = new Set();
|
1055 | component.traverse({
|
1056 | JSXAttribute: !t.isIdentifier(indexParam) ? utils_1.noop : (path) => {
|
1057 | const { value } = path.node;
|
1058 | if (t.isJSXExpressionContainer(value) && t.isJSXIdentifier(path.node.name, { name: 'key' }) && t.isIdentifier(value.expression, { name: indexParam.name })) {
|
1059 | if (process.env.TERM_PROGRAM || env_1.isTestEnv) {
|
1060 |
|
1061 | console.log(utils_1.codeFrameError(value.expression, '建议修改:使用循环的 index 变量作为 key 是一种反优化。参考:https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md').message);
|
1062 | }
|
1063 | }
|
1064 | },
|
1065 | JSXExpressionContainer: this.replaceIdWithTemplate(),
|
1066 | Identifier: (path) => {
|
1067 | const name = path.node.name;
|
1068 | const parent = path.parent;
|
1069 | const parentCallExpr = path.findParent(p => p.isCallExpression());
|
1070 | if (replacements.has(parent) || (this.renderScope.hasOwnBinding(name) &&
|
1071 | (this.loopCalleeId.has(path.node) || parentCallExpr && this.loopCalleeId.has(parentCallExpr.node)))) {
|
1072 | return;
|
1073 | }
|
1074 | if (stateToBeAssign.has(name) && path.isReferencedIdentifier()) {
|
1075 | if (t.isMemberExpression(parent) && t.isIdentifier(parent.property, { name: 'map' })) {
|
1076 | const grandParentPath = path.parentPath.parentPath;
|
1077 | if (grandParentPath.isCallExpression() && this.loopComponents.has(grandParentPath)) {
|
1078 | return;
|
1079 | }
|
1080 | }
|
1081 | if (path.findParent(p => this.loopCallees.has(p.node))) {
|
1082 | return;
|
1083 | }
|
1084 | const parentCondition = path.findParent(p => p.isConditionalExpression() || p.isLogicalExpression());
|
1085 | if (parentCondition) {
|
1086 | const varDecl = parentCondition.findParent(p => p.isVariableDeclarator());
|
1087 | if (varDecl && varDecl.isVariableDeclarator()) {
|
1088 | const init = varDecl.node.id;
|
1089 | if (t.isIdentifier(init) && init.name.startsWith(constant_1.LOOP_STATE)) {
|
1090 | return;
|
1091 | }
|
1092 | }
|
1093 | if (path.findParent(p => this.ancestorConditions.has(p.node))) {
|
1094 | return;
|
1095 | }
|
1096 | }
|
1097 | const replacement = t.memberExpression(t.identifier(item.name), path.node);
|
1098 | path.replaceWith(replacement);
|
1099 | replacements.add(replacement);
|
1100 | }
|
1101 | else {
|
1102 | replaceOriginal(path, parent, name);
|
1103 | }
|
1104 | },
|
1105 | MemberExpression(path) {
|
1106 | const { object, property } = path.node;
|
1107 | if (t.isThisExpression(object) && t.isIdentifier(property)) {
|
1108 | if (property.name === 'state' && path.parentPath.isMemberExpression() && path.parentPath.parentPath.isMemberExpression()) {
|
1109 |
|
1110 | console.warn(utils_1.codeFrameError(path.parentPath.parentPath.node, `在循环中使用 this.state.xx.xx 可能会存在问题,请给 xx 起一个别名,例如 const { xx } = this.state`));
|
1111 | }
|
1112 | }
|
1113 | }
|
1114 | });
|
1115 | const originalProp = t.objectProperty(t.identifier(constant_1.LOOP_ORIGINAL), t.memberExpression(t.identifier(item.name), t.identifier(constant_1.LOOP_ORIGINAL)));
|
1116 | properties.push(originalProp);
|
1117 | body.unshift(t.expressionStatement(t.assignmentExpression('=', t.identifier(item.name), t.objectExpression([
|
1118 | t.objectProperty(t.identifier(constant_1.LOOP_ORIGINAL), t.callExpression(t.identifier(constant_1.INTERNAL_GET_ORIGNAL), [t.identifier(item.name)]))
|
1119 | ]))));
|
1120 | const returnStatement = t.returnStatement(properties.length ? t.objectExpression(properties) : item);
|
1121 | const parentCallee = callee.findParent(c => utils_1.isArrayMapCallExpression(c));
|
1122 | if (utils_1.isArrayMapCallExpression(parentCallee)) {
|
1123 | const [func] = parentCallee.node.arguments;
|
1124 | const { object } = callee.node.callee;
|
1125 | if (t.isFunctionExpression(func) || t.isArrowFunctionExpression(func)) {
|
1126 | const funcBody = func.body;
|
1127 | if (t.isBlockStatement(funcBody)) {
|
1128 | if (t.isIdentifier(object) || t.isMemberExpression(object)) {
|
1129 | const variableName = this.loopComponentNames.get(callee);
|
1130 | funcBody.body.splice(funcBody.body.length - 1, 0, utils_1.buildConstVariableDeclaration(variableName, utils_1.setParentCondition(component, callee.node, true)));
|
1131 | const iterator = func.params[0];
|
1132 | component.node.openingElement.attributes.forEach(attr => {
|
1133 | if (attr.name.name === adapter_1.Adapter.for && t.isIdentifier(iterator)) {
|
1134 | attr.value = t.jSXExpressionContainer(t.memberExpression(iterator, t.identifier(variableName)));
|
1135 | }
|
1136 | });
|
1137 | }
|
1138 | else {
|
1139 | throw utils_1.codeFrameError(object.loc, '多层循环中循环的数组只能是一个变量或成员表达式,可以尝试把该表达式赋值给循环内部的一个新变量。');
|
1140 | }
|
1141 | }
|
1142 | }
|
1143 | body.push(returnStatement);
|
1144 | }
|
1145 | else {
|
1146 | body.push(returnStatement);
|
1147 | const stateName = this.loopComponentNames.get(callee);
|
1148 |
|
1149 | this.addRefIdentifier(callee, t.identifier(stateName));
|
1150 |
|
1151 | if ("quickapp" === adapter_1.Adapter.type) {
|
1152 | let itemName = itemId.name;
|
1153 | let indexName = indexId.name;
|
1154 | if (itemName || indexName) {
|
1155 | let forExpr;
|
1156 | if (itemName && !indexName) {
|
1157 | forExpr = `${itemName} in ${stateName}`;
|
1158 | }
|
1159 | else {
|
1160 | forExpr = `(${indexName}, ${itemName}) in ${stateName}`;
|
1161 | }
|
1162 | jsx_1.setJSXAttr(component.node, adapter_1.Adapter.for, t.stringLiteral(`{{${forExpr}}}`));
|
1163 | }
|
1164 | }
|
1165 | else {
|
1166 | jsx_1.setJSXAttr(component.node, adapter_1.Adapter.for, t.jSXExpressionContainer(t.identifier(stateName)));
|
1167 | }
|
1168 | const returnBody = this.renderPath.node.body.body;
|
1169 | const ifStem = callee.findParent(p => p.isIfStatement());
|
1170 |
|
1171 | if (ifStem && ifStem.isIfStatement()) {
|
1172 | const consequent = ifStem.get('consequent');
|
1173 | if (consequent.isBlockStatement()) {
|
1174 | const assignment = t.expressionStatement(t.assignmentExpression('=', t.identifier(stateName), utils_1.setParentCondition(component, callee.node, true)));
|
1175 | returnBody.unshift(t.variableDeclaration('let', [t.variableDeclarator(t.identifier(stateName))]));
|
1176 | if (callee.findParent(p => p === consequent)) {
|
1177 | consequent.node.body.push(assignment);
|
1178 | }
|
1179 | else {
|
1180 | const alternate = ifStem.get('alternate');
|
1181 | if (alternate.isBlockStatement()) {
|
1182 | alternate.node.body.push(assignment);
|
1183 | }
|
1184 | else {
|
1185 | consequent.node.body.push(assignment);
|
1186 | }
|
1187 | }
|
1188 | }
|
1189 | }
|
1190 | else {
|
1191 | const decl = utils_1.buildConstVariableDeclaration(stateName, utils_1.setParentCondition(component, callee.node, true));
|
1192 | returnBody.push(decl);
|
1193 | }
|
1194 | }
|
1195 | }
|
1196 | }
|
1197 | replaceQueue.push(() => {
|
1198 | const statement = component.getStatementParent();
|
1199 | callee.replaceWith(statement.isReturnStatement()
|
1200 | ? statement.get('argument').node
|
1201 | : component.node);
|
1202 | });
|
1203 | });
|
1204 | if (hasLoopRef) {
|
1205 | const scopeDecl = template('const __scope = this.$scope')();
|
1206 | this.renderPath.node.body.body.unshift(scopeDecl);
|
1207 | }
|
1208 | replaceQueue.forEach(func => func());
|
1209 | };
|
1210 | this.setReserveWord = (word) => {
|
1211 | const binding = this.renderScope.getOwnBinding(word);
|
1212 | let hasStateId = false;
|
1213 | if (binding) {
|
1214 | const path = binding.path;
|
1215 | const id = path.get('id');
|
1216 | const init = path.get('init');
|
1217 | if (init.isThisExpression()) {
|
1218 | return hasStateId;
|
1219 | }
|
1220 | if (id.isObjectPattern()) {
|
1221 | hasStateId = id.node.properties.some(p => {
|
1222 | return (t.isObjectProperty(p) && t.isIdentifier(p.key, { name: word }))
|
1223 | || (t.isRestProperty(p) && t.isIdentifier(p.argument, { name: word }));
|
1224 | });
|
1225 | }
|
1226 | else if (id.isIdentifier({ name: word })) {
|
1227 | hasStateId = true;
|
1228 | }
|
1229 | if (hasStateId) {
|
1230 | this.referencedIdentifiers.add(t.identifier(word));
|
1231 | }
|
1232 | }
|
1233 | if (hasStateId) {
|
1234 | this.reserveStateWords.delete(word);
|
1235 | }
|
1236 | };
|
1237 | this.getCreateJSXMethodName = (name) => `_create${name.slice(6)}Data`;
|
1238 | this.renderPath = renderPath;
|
1239 | this.methods = methods;
|
1240 | this.initState = initState;
|
1241 | this.referencedIdentifiers = referencedIdentifiers;
|
1242 | this.usedState = usedState;
|
1243 | this.customComponentNames = customComponentNames;
|
1244 | this.componentProperies = componentProperies;
|
1245 | this.loopRefs = loopRefs;
|
1246 | this.refObjExpr = refObjExpr;
|
1247 | const renderBody = renderPath.get('body');
|
1248 | this.renderScope = renderBody.scope;
|
1249 | this.isDefaultRender = methodName === 'render';
|
1250 | this.upperCaseComponentProps = new Set(Array.from(this.componentProperies).filter(p => /[A-Z]/.test(p) && !p.startsWith('on')));
|
1251 | const [, error] = renderPath.node.body.body.filter(s => t.isReturnStatement(s));
|
1252 | if (error) {
|
1253 | throw utils_1.codeFrameError(error.loc, 'render 函数顶级作用域暂时只支持一个 return');
|
1254 | }
|
1255 | if (t.isIdentifier(this.renderPath.node.key)) {
|
1256 | this.renderMethodName = this.renderPath.node.key.name;
|
1257 | }
|
1258 | else {
|
1259 | throw utils_1.codeFrameError(this.renderPath.node, '类函数对象必须指明函数名');
|
1260 | }
|
1261 | this.handleQuickappProps();
|
1262 | renderBody.traverse(this.loopComponentVisitor);
|
1263 | if (this.hasNoReturnLoopStem) {
|
1264 | renderBody.traverse({
|
1265 | JSXElement: this.loopComponentVisitor.JSXElement.exit[0]
|
1266 | });
|
1267 | }
|
1268 | this.handleLoopComponents();
|
1269 | if (adapter_1.isNewPropsSystem()) {
|
1270 | this.handleComponents(renderBody);
|
1271 | }
|
1272 | renderBody.traverse(this.visitors);
|
1273 | if (adapter_1.Adapter.type === "quickapp" ) {
|
1274 | renderBody.traverse(this.quickappVistor);
|
1275 | }
|
1276 | if (t.isIdentifier(this.renderPath.node.key)) {
|
1277 | this.renderPath.node.key.name = this.getCreateJSXMethodName(this.renderMethodName);
|
1278 | }
|
1279 | this.setOutputTemplate();
|
1280 | this.checkDuplicateName();
|
1281 | this.removeJSXStatement();
|
1282 | this.setUsedState();
|
1283 | this.setPendingState();
|
1284 | this.setCustomEvent();
|
1285 | this.createData();
|
1286 | if (adapter_1.Adapter.type === "quickapp" ) {
|
1287 | this.setProperies();
|
1288 | }
|
1289 | this.setLoopRefFlag();
|
1290 | this.handleClosureComp();
|
1291 | }
|
1292 | handleConditionExpr({ parentNode, parentPath, statementParent }, jsxElementPath) {
|
1293 | if (parentPath.isObjectProperty()) {
|
1294 | const value = parentPath.get('value');
|
1295 | if (value !== jsxElementPath) {
|
1296 | return;
|
1297 | }
|
1298 | if (!parentPath.parentPath.isObjectExpression()) {
|
1299 | return;
|
1300 | }
|
1301 | const properties = parentPath.parentPath.get('properties');
|
1302 | if (!parentPath.parentPath.parentPath.isMemberExpression()) {
|
1303 | return;
|
1304 | }
|
1305 | const rval = parentPath.parentPath.parentPath.get('property');
|
1306 | if (!rval || !rval.node || !Array.isArray(properties)) {
|
1307 | return;
|
1308 | }
|
1309 | const children = properties.map(p => p.node).map((p, index) => {
|
1310 | const block = jsx_1.buildBlockElement();
|
1311 | const leftExpression = p.key;
|
1312 | const tester = t.binaryExpression('===', leftExpression, rval.node);
|
1313 | block.children = [t.jSXExpressionContainer(p.value)];
|
1314 | if (index === 0) {
|
1315 | utils_1.newJSXIfAttr(block, tester);
|
1316 | }
|
1317 | else {
|
1318 | jsx_1.setJSXAttr(block, adapter_1.Adapter.elseif, t.jSXExpressionContainer(tester));
|
1319 | }
|
1320 | return block;
|
1321 | });
|
1322 | const block = jsx_1.buildBlockElement();
|
1323 | block.children = children;
|
1324 | parentPath.parentPath.parentPath.replaceWith(block);
|
1325 | }
|
1326 | else if (t.isLogicalExpression(parentNode)) {
|
1327 | const { left, operator, right } = parentNode;
|
1328 | const leftExpression = parentPath.get('left');
|
1329 | if (operator === '&&' && t.isExpression(left)) {
|
1330 | if (utils_1.hasComplexExpression(leftExpression)) {
|
1331 | utils_1.generateAnonymousState(this.renderScope, leftExpression, this.referencedIdentifiers, true);
|
1332 | }
|
1333 | const block = jsx_1.buildBlockElement();
|
1334 | utils_1.newJSXIfAttr(block, leftExpression.node);
|
1335 | block.children = [jsxElementPath.node];
|
1336 | parentPath.replaceWith(block);
|
1337 | if (statementParent) {
|
1338 | const name = utils_1.findIdentifierFromStatement(statementParent.node);
|
1339 | utils_1.setTemplate(name, jsxElementPath, this.templates);
|
1340 |
|
1341 | }
|
1342 | }
|
1343 | if (operator === '||' && t.isExpression(left)) {
|
1344 | const newNode = t.conditionalExpression(left, left, right);
|
1345 | parentPath.replaceWith(newNode);
|
1346 |
|
1347 | }
|
1348 | }
|
1349 | else if (t.isConditionalExpression(parentNode)) {
|
1350 | const { consequent, alternate } = parentNode;
|
1351 | const testExpression = parentPath.get('test');
|
1352 | const block = jsx_1.buildBlockElement();
|
1353 | if (utils_1.hasComplexExpression(testExpression)) {
|
1354 | utils_1.generateAnonymousState(parentPath.scope, testExpression, this.referencedIdentifiers, true);
|
1355 | }
|
1356 | const test = testExpression.node;
|
1357 | if (t.isJSXElement(consequent) && this.isLiteralOrUndefined(alternate)) {
|
1358 | const { value, confident } = parentPath.get('alternate').evaluate();
|
1359 | if (confident && !value || t.isIdentifier({ name: 'undefined' })) {
|
1360 | utils_1.newJSXIfAttr(block, test);
|
1361 | block.children = [jsxElementPath.node];
|
1362 |
|
1363 | parentPath.replaceWith(block);
|
1364 | }
|
1365 | else {
|
1366 | const block2 = jsx_1.buildBlockElement();
|
1367 | block.children = [consequent];
|
1368 | utils_1.newJSXIfAttr(block, test);
|
1369 | jsx_1.setJSXAttr(block2, adapter_1.Adapter.else);
|
1370 | block2.children = [t.jSXExpressionContainer(alternate)];
|
1371 | const parentBlock = jsx_1.buildBlockElement();
|
1372 | parentBlock.children = [block, block2];
|
1373 | parentPath.replaceWith(parentBlock);
|
1374 | }
|
1375 | if (statementParent) {
|
1376 | const name = utils_1.findIdentifierFromStatement(statementParent.node);
|
1377 | utils_1.setTemplate(name, jsxElementPath, this.templates);
|
1378 |
|
1379 | }
|
1380 | }
|
1381 | else if (this.isLiteralOrUndefined(consequent) && t.isJSXElement(alternate)) {
|
1382 | const { value, confident } = parentPath.get('consequent').evaluate();
|
1383 | if (confident && !value || t.isIdentifier({ name: 'undefined' })) {
|
1384 | utils_1.newJSXIfAttr(block, utils_1.reverseBoolean(test));
|
1385 | block.children = [jsxElementPath.node];
|
1386 |
|
1387 | parentPath.replaceWith(block);
|
1388 | }
|
1389 | else {
|
1390 | const block2 = jsx_1.buildBlockElement();
|
1391 | block.children = [t.jSXExpressionContainer(consequent)];
|
1392 | utils_1.newJSXIfAttr(block, test);
|
1393 | jsx_1.setJSXAttr(block2, adapter_1.Adapter.else);
|
1394 | block2.children = [alternate];
|
1395 | const parentBlock = jsx_1.buildBlockElement();
|
1396 | parentBlock.children = [block, block2];
|
1397 | parentPath.replaceWith(parentBlock);
|
1398 | }
|
1399 | if (statementParent) {
|
1400 | const name = utils_1.findIdentifierFromStatement(statementParent.node);
|
1401 | utils_1.setTemplate(name, jsxElementPath, this.templates);
|
1402 |
|
1403 | }
|
1404 | }
|
1405 | else if (t.isJSXElement(consequent) && t.isJSXElement(alternate)) {
|
1406 | const block2 = jsx_1.buildBlockElement();
|
1407 | block.children = [consequent];
|
1408 | utils_1.newJSXIfAttr(block, test);
|
1409 | jsx_1.setJSXAttr(block2, adapter_1.Adapter.else);
|
1410 | block2.children = [alternate];
|
1411 | const parentBlock = jsx_1.buildBlockElement();
|
1412 | parentBlock.children = [block, block2];
|
1413 | parentPath.replaceWith(parentBlock);
|
1414 | if (statementParent) {
|
1415 | const name = utils_1.findIdentifierFromStatement(statementParent.node);
|
1416 | utils_1.setTemplate(name, jsxElementPath, this.templates);
|
1417 | }
|
1418 | }
|
1419 | else if (t.isJSXElement(consequent) && t.isCallExpression(alternate) && !utils_1.isArrayMapCallExpression(parentPath.get('alternate'))) {
|
1420 | const id = utils_1.generateAnonymousState(this.renderScope, parentPath.get('alternate'), this.referencedIdentifiers, true);
|
1421 | parentPath.get('alternate').replaceWith(id);
|
1422 |
|
1423 | }
|
1424 | else if (t.isJSXElement(alternate) && t.isCallExpression(consequent) && !utils_1.isArrayMapCallExpression(parentPath.get('consequent'))) {
|
1425 | const id = utils_1.generateAnonymousState(this.renderScope, parentPath.get('consequent'), this.referencedIdentifiers, true);
|
1426 | parentPath.get('consequent').replaceWith(id);
|
1427 | }
|
1428 | else if (t.isJSXElement(alternate) && utils_1.isArrayMapCallExpression(parentPath.get('consequent'))) {
|
1429 |
|
1430 | }
|
1431 | else if (t.isJSXElement(consequent) && utils_1.isArrayMapCallExpression(parentPath.get('alternate'))) {
|
1432 |
|
1433 | }
|
1434 | else {
|
1435 | block.children = [t.jSXExpressionContainer(consequent)];
|
1436 | utils_1.newJSXIfAttr(block, test);
|
1437 | const block2 = jsx_1.buildBlockElement();
|
1438 | jsx_1.setJSXAttr(block2, adapter_1.Adapter.else);
|
1439 | block2.children = [t.jSXExpressionContainer(alternate)];
|
1440 | const parentBlock = jsx_1.buildBlockElement();
|
1441 | parentBlock.children = [block, block2];
|
1442 | parentPath.replaceWith(parentBlock);
|
1443 | if (statementParent) {
|
1444 | const name = utils_1.findIdentifierFromStatement(statementParent.node);
|
1445 | utils_1.setTemplate(name, jsxElementPath, this.templates);
|
1446 | }
|
1447 | }
|
1448 | }
|
1449 | }
|
1450 | setProperies() {
|
1451 | if (!this.isDefaultRender) {
|
1452 | return;
|
1453 | }
|
1454 | const properties = [];
|
1455 | this.componentProperies.forEach((propName) => {
|
1456 | const p = "quickapp" === "quickapp" && this.upperCaseComponentProps.has(propName) && !propName.startsWith('prv-fn') ? lodash_1.snakeCase(propName) : propName;
|
1457 | properties.push(t.objectProperty(t.stringLiteral(p), t.objectExpression([
|
1458 | t.objectProperty(t.stringLiteral('type'), t.nullLiteral()),
|
1459 | t.objectProperty(t.stringLiteral('value'), t.nullLiteral())
|
1460 | ])));
|
1461 | });
|
1462 | let classProp = t.classProperty(t.identifier('properties'), t.objectExpression(properties));
|
1463 | classProp.static = true;
|
1464 | const classPath = this.renderPath.findParent(isClassDcl);
|
1465 | adapter_1.Adapter.type !== "alipay" && classPath.node.body.body.unshift(classProp);
|
1466 | }
|
1467 | setLoopRefFlag() {
|
1468 | if (this.loopRefs.size) {
|
1469 | const classPath = this.renderPath.findParent(isClassDcl);
|
1470 | classPath.node.body.body.unshift(t.classProperty(t.identifier('$$hasLoopRef'), t.booleanLiteral(true)));
|
1471 | }
|
1472 | }
|
1473 | destructStateOrProps(key, path, properties, parentPath) {
|
1474 | const hasStateOrProps = properties.filter(p => t.isObjectProperty(p) && t.isIdentifier(p.key) && key === p.key.name);
|
1475 | if (hasStateOrProps.length === 0) {
|
1476 | return;
|
1477 | }
|
1478 | if (hasStateOrProps.length !== properties.length) {
|
1479 | throw utils_1.codeFrameError(path.node, 'state 或 props 只能单独从 this 中解构');
|
1480 | }
|
1481 | const declareState = template(`const ${key} = this.${key};`)();
|
1482 | if (properties.length > 1) {
|
1483 | const index = properties.findIndex(p => t.isObjectProperty(p) && t.isIdentifier(p.key, { name: key }));
|
1484 | properties.splice(index, 1);
|
1485 | parentPath.insertAfter(declareState);
|
1486 | }
|
1487 | else {
|
1488 | parentPath.insertAfter(declareState);
|
1489 | parentPath.remove();
|
1490 | }
|
1491 | }
|
1492 | handleJSXInIfStatement(jsxElementPath, { parentNode, parentPath, isFinalReturn, isIfStemInLoop }) {
|
1493 | if (t.isReturnStatement(parentNode)) {
|
1494 | if (!isFinalReturn && !isIfStemInLoop) {
|
1495 | return;
|
1496 | }
|
1497 | else {
|
1498 | const ifStatement = parentPath.findParent(p => p.isIfStatement());
|
1499 | const blockStatement = parentPath.findParent(p => p.isBlockStatement() && (p.parentPath === ifStatement));
|
1500 | const loopCallExpr = jsxElementPath.findParent(p => utils_1.isArrayMapCallExpression(p));
|
1501 | if (loopCallExpr && loopCallExpr.findParent(p => p.isIfStatement())) {
|
1502 | throw utils_1.codeFrameError(loopCallExpr.node, '在循环的上级和内部都有 if-else 的情况,需要把循环的内部 if-else return 的 JSX 设置为一个变量,保证单个 return 语句。\n 示例:https://gist.github.com/yuche/f6a0933df2537407abe0f426f774f670');
|
1503 | }
|
1504 | if (blockStatement && blockStatement.isBlockStatement()) {
|
1505 | blockStatement.traverse(this.renameIfScopeVaribale(blockStatement));
|
1506 | }
|
1507 | const blockAttrs = [];
|
1508 | if ((adapter_1.isNewPropsSystem()) && !this.finalReturnElement && process.env.NODE_ENV !== 'test') {
|
1509 | if (this.isDefaultRender && adapter_1.Adapter.type !== "swan" ) {
|
1510 | blockAttrs.push(t.jSXAttribute(t.jSXIdentifier(adapter_1.Adapter.if), t.jSXExpressionContainer(t.jSXIdentifier(constant_1.IS_TARO_READY))));
|
1511 | }
|
1512 | }
|
1513 | const block = this.finalReturnElement || jsx_1.buildBlockElement(blockAttrs);
|
1514 | if (utils_1.isBlockIfStatement(ifStatement, blockStatement)) {
|
1515 | let { test, alternate, consequent } = ifStatement.node;
|
1516 | if (utils_1.hasComplexExpression(ifStatement.get('test'))) {
|
1517 | ifStatement.node.test = test = utils_1.generateAnonymousState(blockStatement.scope, ifStatement.get('test'), this.referencedIdentifiers, true);
|
1518 | }
|
1519 |
|
1520 |
|
1521 |
|
1522 | if (alternate === blockStatement.node) {
|
1523 | throw utils_1.codeFrameError(parentNode.loc, '不必要的 else 分支,请遵从 ESLint consistent-return: https://eslint.org/docs/rules/consistent-return');
|
1524 | }
|
1525 | else if (consequent === blockStatement.node) {
|
1526 | const parentIfStatement = ifStatement.findParent(p => p.isIfStatement());
|
1527 | if (parentIfStatement) {
|
1528 | jsx_1.setJSXAttr(jsxElementPath.node, adapter_1.Adapter.elseif, t.jSXExpressionContainer(test), jsxElementPath);
|
1529 | if (loopCallExpr && this.loopIfStemComponentMap.has(loopCallExpr)) {
|
1530 | const block = this.loopIfStemComponentMap.get(loopCallExpr);
|
1531 | block.children.push(jsxElementPath.node);
|
1532 | }
|
1533 | }
|
1534 | else {
|
1535 | if (isIfStemInLoop && loopCallExpr && loopCallExpr.isCallExpression()) {
|
1536 | if (this.loopIfStemComponentMap.has(loopCallExpr)) {
|
1537 | const component = this.loopIfStemComponentMap.get(loopCallExpr);
|
1538 | utils_1.newJSXIfAttr(jsxElementPath.node, test, jsxElementPath);
|
1539 | component.children.push(jsxElementPath.node);
|
1540 | }
|
1541 | else {
|
1542 | utils_1.newJSXIfAttr(jsxElementPath.node, test, jsxElementPath);
|
1543 | this.loopIfStemComponentMap.set(loopCallExpr, block);
|
1544 | const arrowFunc = loopCallExpr.node.arguments[0];
|
1545 | if (t.isArrowFunctionExpression(arrowFunc) && t.isBlockStatement(arrowFunc.body) && !arrowFunc.body.body.some(s => t.isReturnStatement(s))) {
|
1546 | arrowFunc.body.body.push(t.returnStatement(jsx_1.buildBlockElement()));
|
1547 | this.hasNoReturnLoopStem = true;
|
1548 | }
|
1549 | }
|
1550 | let scope;
|
1551 | try {
|
1552 | scope = loopCallExpr.get('arguments')[0].get('body').scope;
|
1553 | }
|
1554 | catch (error) {
|
1555 |
|
1556 | }
|
1557 | if (scope) {
|
1558 | this.returnedifStemJSX.add(scope);
|
1559 | }
|
1560 | }
|
1561 | else {
|
1562 | if (this.topLevelIfStatement.size > 0) {
|
1563 | jsx_1.setJSXAttr(jsxElementPath.node, adapter_1.Adapter.elseif, t.jSXExpressionContainer(test), jsxElementPath);
|
1564 | }
|
1565 | else {
|
1566 | utils_1.newJSXIfAttr(jsxElementPath.node, test, jsxElementPath);
|
1567 | this.topLevelIfStatement.add(ifStatement);
|
1568 | }
|
1569 | }
|
1570 | }
|
1571 | }
|
1572 | }
|
1573 | else if (block.children.length !== 0) {
|
1574 | if (this.topLevelIfStatement.size > 0) {
|
1575 | jsx_1.setJSXAttr(jsxElementPath.node, adapter_1.Adapter.else);
|
1576 | }
|
1577 | }
|
1578 | block.children.push(jsxElementPath.node);
|
1579 | if (!this.loopIfStemComponentMap.has(loopCallExpr)) {
|
1580 | this.finalReturnElement = block;
|
1581 | }
|
1582 | this.returnedPaths.push(parentPath);
|
1583 | }
|
1584 | }
|
1585 | else if (t.isArrowFunctionExpression(parentNode)) {
|
1586 |
|
1587 | }
|
1588 | else if (t.isAssignmentExpression(parentNode)) {
|
1589 | const ifStatement = parentPath.findParent(p => p.isIfStatement());
|
1590 | const blockStatement = parentPath.findParent(p => p.isBlockStatement() && (p.parentPath === ifStatement));
|
1591 | if (blockStatement && blockStatement.isBlockStatement()) {
|
1592 | blockStatement.traverse(this.renameIfScopeVaribale(blockStatement));
|
1593 | }
|
1594 | if (t.isIdentifier(parentNode.left)) {
|
1595 | const assignmentName = parentNode.left.name;
|
1596 | const renderScope = isIfStemInLoop ? jsxElementPath.findParent(p => utils_1.isArrayMapCallExpression(p)).get('arguments')[0].get('body').scope : this.renderScope;
|
1597 | const bindingNode = renderScope.getOwnBinding(assignmentName).path.node;
|
1598 |
|
1599 | const parallelIfStems = this.findParallelIfStem(ifStatement);
|
1600 | const parentIfStatement = ifStatement.findParent(p => p.isIfStatement() && !parallelIfStems.has(p));
|
1601 |
|
1602 | let block = this.templates.get(assignmentName) || jsx_1.buildBlockElement();
|
1603 | let isElse = false;
|
1604 | if (utils_1.isEmptyDeclarator(bindingNode)) {
|
1605 | const blockStatement = parentPath.findParent(p => p.isBlockStatement());
|
1606 | if (utils_1.isBlockIfStatement(ifStatement, blockStatement)) {
|
1607 | const { test, alternate, consequent } = ifStatement.node;
|
1608 | if (alternate === blockStatement.node) {
|
1609 | const newBlock = jsx_1.buildBlockElement();
|
1610 | jsx_1.setJSXAttr(newBlock, adapter_1.Adapter.else);
|
1611 | newBlock.children = [jsxElementPath.node];
|
1612 | jsxElementPath.node = newBlock;
|
1613 | }
|
1614 | else if (consequent === blockStatement.node) {
|
1615 | const parentIfStatement = ifStatement.findParent(p => p.isIfStatement());
|
1616 | const assignments = [];
|
1617 | let isAssignedBefore = false;
|
1618 |
|
1619 |
|
1620 | if (blockStatement && blockStatement.isBlockStatement()) {
|
1621 | for (const parentStatement of blockStatement.node.body) {
|
1622 | if (t.isIfStatement(parentStatement) && t.isBlockStatement(parentStatement.consequent)) {
|
1623 | const statements = parentStatement.consequent.body;
|
1624 | for (const statement of statements) {
|
1625 | if (t.isExpressionStatement(statement) && t.isAssignmentExpression(statement.expression) && t.isIdentifier(statement.expression.left, { name: assignmentName })) {
|
1626 | isAssignedBefore = true;
|
1627 | }
|
1628 | }
|
1629 | }
|
1630 | }
|
1631 | }
|
1632 |
|
1633 | if (parentIfStatement) {
|
1634 | const { consequent } = parentIfStatement.node;
|
1635 | if (t.isBlockStatement(consequent)) {
|
1636 | const body = consequent.body;
|
1637 | for (const parentStatement of body) {
|
1638 | if (t.isIfStatement(parentStatement) && t.isBlockStatement(parentStatement.consequent)) {
|
1639 | const statements = parentStatement.consequent.body;
|
1640 | for (const statement of statements) {
|
1641 | if (t.isExpressionStatement(statement) && t.isAssignmentExpression(statement.expression) && t.isIdentifier(statement.expression.left, { name: assignmentName })) {
|
1642 | assignments.push(statement.expression);
|
1643 | }
|
1644 | }
|
1645 | }
|
1646 | }
|
1647 | }
|
1648 | }
|
1649 | if ((parentIfStatement &&
|
1650 | (parentIfStatement.get('alternate') === ifStatement ||
|
1651 | assignments.findIndex(a => a === parentNode) > 0))
|
1652 | ||
|
1653 | isAssignedBefore) {
|
1654 | jsx_1.setJSXAttr(jsxElementPath.node, adapter_1.Adapter.elseif, t.jSXExpressionContainer(test), jsxElementPath);
|
1655 | }
|
1656 | else {
|
1657 | if (parentIfStatement) {
|
1658 | if (this.isEmptyBlock(block)) {
|
1659 | utils_1.newJSXIfAttr(block, parentIfStatement.node.test, jsxElementPath);
|
1660 | }
|
1661 | else if (parentIfStatement.node.alternate === ifStatement.parent) {
|
1662 | const newBlock = jsx_1.buildBlockElement();
|
1663 | jsx_1.setJSXAttr(newBlock, adapter_1.Adapter.else);
|
1664 | this.insertElseBlock(block, newBlock, parentIfStatement.node.test);
|
1665 | isElse = true;
|
1666 | }
|
1667 | else {
|
1668 | const newBlock = jsx_1.buildBlockElement();
|
1669 | jsx_1.setJSXAttr(newBlock, adapter_1.Adapter.elseif, t.jSXExpressionContainer(parentIfStatement.node.test), jsxElementPath);
|
1670 | block.children.push(newBlock);
|
1671 | }
|
1672 | }
|
1673 | utils_1.newJSXIfAttr(jsxElementPath.node, test, jsxElementPath);
|
1674 | }
|
1675 | }
|
1676 | const ifAttr = block.openingElement.attributes.find(a => t.isJSXIdentifier(a.name) && a.name.name === adapter_1.Adapter.if);
|
1677 | if (ifAttr && t.isJSXExpressionContainer(ifAttr.value, { expression: test })) {
|
1678 | const newBlock = jsx_1.buildBlockElement();
|
1679 | newBlock.children = [block, jsxElementPath.node];
|
1680 | block = newBlock;
|
1681 | }
|
1682 | else if (parentIfStatement && ifStatement.parentPath !== parentIfStatement) {
|
1683 | let hasNest = false;
|
1684 | this.handleNestedIfStatement(block, jsxElementPath.node, parentIfStatement.node.test, hasNest, isElse || !!ifStatement.findParent(p => p.node === parentIfStatement.node.alternate));
|
1685 | if (!hasNest && parentIfStatement.get('alternate') !== ifStatement) {
|
1686 | const ifAttr = block.openingElement.attributes.find(a => t.isJSXIdentifier(a.name) && a.name.name === adapter_1.Adapter.if);
|
1687 | if (ifAttr && t.isJSXExpressionContainer(ifAttr.value, { expression: parentIfStatement.node.test })) {
|
1688 | const newBlock = jsx_1.buildBlockElement();
|
1689 | block.children.push(jsxElementPath.node);
|
1690 | newBlock.children = [block];
|
1691 | block = newBlock;
|
1692 | }
|
1693 | }
|
1694 | }
|
1695 | else {
|
1696 | block.children.push(jsxElementPath.node);
|
1697 | }
|
1698 |
|
1699 | assignmentName && this.templates.set(assignmentName, block);
|
1700 | if (isIfStemInLoop) {
|
1701 | this.replaceIdWithTemplate()(renderScope.path);
|
1702 | this.returnedPaths.push(parentPath);
|
1703 | }
|
1704 | }
|
1705 | }
|
1706 | else {
|
1707 | throw utils_1.codeFrameError(jsxElementPath.node.loc, '请将 JSX 赋值表达式初始化为 null,然后再进行 if 条件表达式赋值。');
|
1708 | }
|
1709 | }
|
1710 | }
|
1711 | else if (!t.isJSXElement(parentNode)) {
|
1712 |
|
1713 | }
|
1714 | }
|
1715 | genPropsSettingExpression(properties, id, previd) {
|
1716 | return t.callExpression(t.memberExpression(t.identifier(constant_1.PROPS_MANAGER), t.identifier('set')), [Array.isArray(properties) ? t.objectExpression(properties) : properties, id, previd]);
|
1717 | }
|
1718 | getPropsFromAttrs(openingElement) {
|
1719 | const attrs = openingElement.attributes;
|
1720 | const properties = [];
|
1721 | openingElement.attributes = attrs.filter(attr => {
|
1722 | if (t.isJSXSpreadAttribute(attr)) {
|
1723 | properties.push(t.spreadProperty(attr.argument));
|
1724 | return false;
|
1725 | }
|
1726 | else if (t.isJSXAttribute(attr)) {
|
1727 | const { name, value } = attr;
|
1728 | if (t.isJSXIdentifier(name)
|
1729 | && name.name !== 'key'
|
1730 | && name.name !== 'id'
|
1731 | && !(name.name === 'extraProps' && adapter_1.Adapter.type === "weapp" )
|
1732 | && name.name !== adapter_1.Adapter.for
|
1733 | && name.name !== adapter_1.Adapter.forItem
|
1734 | && name.name !== adapter_1.Adapter.forIndex
|
1735 | && name.name.indexOf('render') !== 0
|
1736 | && !t.isJSXElement(value)
|
1737 | && !name.name.includes('-')) {
|
1738 |
|
1739 | const v = value === null
|
1740 | ? t.booleanLiteral(true)
|
1741 | : (t.isJSXExpressionContainer(value)
|
1742 | ? value.expression
|
1743 | : value);
|
1744 | v && properties.push(t.objectProperty(t.stringLiteral(name.name), v));
|
1745 | return false;
|
1746 | }
|
1747 | return true;
|
1748 | }
|
1749 | });
|
1750 | return properties;
|
1751 | }
|
1752 | addIdToElement(jsxElementPath) {
|
1753 | const openingElement = jsxElementPath.node.openingElement;
|
1754 | if (openingElement.attributes.find(attr => {
|
1755 | return t.isJSXAttribute(attr) && attr.name.name === 'compid';
|
1756 | })) {
|
1757 | return;
|
1758 | }
|
1759 | if (this.isInternalComponent(openingElement)) {
|
1760 | if (this.isEmptyProps(openingElement.attributes) && adapter_1.Adapter.type !== "swan" ) {
|
1761 | return;
|
1762 | }
|
1763 | const compId = utils_1.genCompid();
|
1764 | const prevName = `${constant_1.PREV_COMPID}__${compId}`;
|
1765 | const name = `${constant_1.COMPID}__${compId}`;
|
1766 | const variableName = t.identifier(name);
|
1767 | this.referencedIdentifiers.add(variableName);
|
1768 | const idExpr = t.variableDeclaration('const', [
|
1769 | t.variableDeclarator(t.arrayPattern([t.identifier(prevName), variableName]), t.callExpression(t.identifier(constant_1.GEN_COMP_ID), [
|
1770 | t.binaryExpression('+', this.prefixExpr(), t.stringLiteral(name))
|
1771 | ]))
|
1772 | ]);
|
1773 |
|
1774 | const properties = this.getPropsFromAttrs(openingElement);
|
1775 | const propsId = `$props__${compId}`;
|
1776 | const collectedProps = utils_1.buildConstVariableDeclaration(propsId, t.objectExpression(properties));
|
1777 | const result = jsxElementPath.getStatementParent().insertBefore(collectedProps);
|
1778 | this.propsDecls.set(propsId, result[0]);
|
1779 | const propsSettingExpr = this.genPropsSettingExpression(t.identifier(propsId), variableName, t.identifier(prevName));
|
1780 | this.genCompidExprs.add(idExpr);
|
1781 | const expr = utils_1.setAncestorCondition(jsxElementPath, propsSettingExpr);
|
1782 | this.ancestorConditions.add(expr);
|
1783 | const ifStatement = jsxElementPath.findParent(p => p.isIfStatement());
|
1784 | const blockStatement = jsxElementPath.findParent(p => p.isBlockStatement());
|
1785 | let blockStem = this.renderPath.node.body;
|
1786 | if (ifStatement && blockStatement) {
|
1787 | const consequent = ifStatement.get('consequent');
|
1788 | const alternate = ifStatement.get('alternate');
|
1789 | if (blockStatement === consequent || blockStatement === alternate) {
|
1790 | blockStem = blockStatement.node;
|
1791 | }
|
1792 | }
|
1793 | const funcs = this.propsSettingExpressions.get(blockStem);
|
1794 | const func = () => t.expressionStatement(expr);
|
1795 | this.propsSettingExpressions.set(blockStem, funcs ? [...funcs, func] : [func]);
|
1796 |
|
1797 | jsx_1.setJSXAttr(jsxElementPath.node, 'compid', t.jSXExpressionContainer(variableName));
|
1798 | }
|
1799 | }
|
1800 | handleComponents(renderBody) {
|
1801 | renderBody.traverse({
|
1802 | JSXElement: jsxElementPath => this.addIdToElement(jsxElementPath)
|
1803 | });
|
1804 | }
|
1805 | handleQuickappProps() {
|
1806 | if (adapter_1.Adapter.type !== "quickapp" ) {
|
1807 | return;
|
1808 | }
|
1809 | this.renderPath.traverse({
|
1810 | Identifier: (path) => {
|
1811 | if (!this.upperCaseComponentProps.has(path.node.name)) {
|
1812 | return;
|
1813 | }
|
1814 | if (utils_1.isDerivedFromProps(this.renderScope, path.node.name)) {
|
1815 | this.renderScope.rename(path.node.name, lodash_1.snakeCase(path.node.name));
|
1816 | path.replaceWith(t.identifier(lodash_1.snakeCase(path.node.name)));
|
1817 | }
|
1818 | const sibling = path.getSibling('object');
|
1819 | if (sibling && sibling.isMemberExpression() && sibling.get('object').isThisExpression() && sibling.get('property').isIdentifier({ name: 'props' })) {
|
1820 | path.replaceWith(t.identifier(lodash_1.snakeCase(path.node.name)));
|
1821 | }
|
1822 | }
|
1823 | });
|
1824 | }
|
1825 | handleClosureComp() {
|
1826 | this.deferedHandleClosureJSXFunc.forEach(func => func());
|
1827 | }
|
1828 | checkDuplicateData() {
|
1829 | this.initState.forEach((stateName) => {
|
1830 | if (this.templates.has(stateName)) {
|
1831 | throw utils_1.codeFrameError(this.templates.get(stateName), `自定义变量组件名: \`${stateName}\` 和已有 this.state.${stateName} 重复。请使用另一个变量名。`);
|
1832 | }
|
1833 | });
|
1834 | this.componentProperies.forEach((componentName) => {
|
1835 | if (this.componentProperies.has(componentName)) {
|
1836 | throw utils_1.codeFrameError(this.renderPath.node, `state: \`${componentName}\` 和已有 this.props.${componentName} 重复。请使用另一个变量名。`);
|
1837 | }
|
1838 | if (this.templates.has(componentName)) {
|
1839 | throw utils_1.codeFrameError(this.templates.get(componentName), `自定义变量组件名: \`${componentName}\` 和已有 this.props.${componentName} 重复。请使用另一个变量名。`);
|
1840 | }
|
1841 | });
|
1842 | }
|
1843 | addRefIdentifier(path, id) {
|
1844 | const arrayMap = path.findParent(p => utils_1.isArrayMapCallExpression(p));
|
1845 | if (arrayMap && arrayMap.isCallExpression()) {
|
1846 | this.loopRefIdentifiers.set(id.name, arrayMap);
|
1847 | }
|
1848 | else {
|
1849 | id && this.referencedIdentifiers.add(id);
|
1850 | }
|
1851 | }
|
1852 | findParentIndices(callee, indexId) {
|
1853 | const loopIndices = [];
|
1854 | const loops = t.arrayExpression([]);
|
1855 | utils_1.findParentLoops(callee, this.loopComponentNames, loops);
|
1856 | for (const el of loops.elements) {
|
1857 | if (t.isObjectExpression(el)) {
|
1858 | for (const prop of el.properties) {
|
1859 | if (t.isObjectProperty(prop) && t.isIdentifier(prop.key, { name: 'indexId' }) && t.isIdentifier(prop.value)) {
|
1860 | loopIndices.push(prop.value.name);
|
1861 | }
|
1862 | }
|
1863 | }
|
1864 | }
|
1865 | if (loopIndices.length === 0) {
|
1866 | if (t.isIdentifier(indexId)) {
|
1867 | loopIndices.push(indexId.name);
|
1868 | }
|
1869 | else {
|
1870 | throw utils_1.codeFrameError(callee.node, '循环中使用自定义组件需要暴露循环的 index');
|
1871 | }
|
1872 | }
|
1873 | return loopIndices;
|
1874 | }
|
1875 | setOutputTemplate() {
|
1876 | if (adapter_1.Adapter.type === "quickapp" && options_1.transformOptions.rootProps && options_1.transformOptions.isRoot) {
|
1877 | const attrs = [];
|
1878 | for (const key in options_1.transformOptions.rootProps) {
|
1879 | if (options_1.transformOptions.rootProps.hasOwnProperty(key)) {
|
1880 | const value = options_1.transformOptions.rootProps[key];
|
1881 | const keyName = key + '__temp';
|
1882 | const decl = utils_1.buildConstVariableDeclaration(keyName, t.identifier(JSON.stringify(value)));
|
1883 | this.referencedIdentifiers.add(t.identifier(keyName));
|
1884 | this.renderPath.node.body.body.push(decl);
|
1885 | attrs.push(t.jSXAttribute(t.jSXIdentifier(key), t.jSXExpressionContainer(t.identifier(keyName))));
|
1886 | }
|
1887 | }
|
1888 | this.finalReturnElement.openingElement.attributes.push(...attrs);
|
1889 | }
|
1890 | if (!this.finalReturnElement) {
|
1891 | throw utils_1.codeFrameError(this.renderPath.node, '没有找到返回的 JSX 元素,你是不是忘记 return 了?');
|
1892 | }
|
1893 | this.outputTemplate = jsx_1.parseJSXElement(this.finalReturnElement, true);
|
1894 | if (!this.isDefaultRender) {
|
1895 | this.outputTemplate = `<template name="${this.renderMethodName}">${this.outputTemplate}</template>`;
|
1896 | }
|
1897 | }
|
1898 | removeJSXStatement() {
|
1899 | this.jsxDeclarations.forEach(d => d && !d.removed && utils_1.isContainJSXElement(d) && d.remove());
|
1900 | this.returnedPaths.forEach((p) => {
|
1901 | if (p.removed) {
|
1902 | return;
|
1903 | }
|
1904 | const ifStem = p.findParent(_ => _.isIfStatement());
|
1905 | if (ifStem) {
|
1906 | const node = p.node;
|
1907 | if (!node) {
|
1908 | return;
|
1909 | }
|
1910 | if (t.isJSXElement(node.argument)) {
|
1911 | const jsx = node.argument;
|
1912 | if (jsx.children.length === 0 && jsx.openingElement.attributes.length === 0 && !this.isIfStemInLoop(p.get('argument'))) {
|
1913 | node.argument = t.nullLiteral();
|
1914 | }
|
1915 | else {
|
1916 | p.remove();
|
1917 | }
|
1918 | }
|
1919 | else {
|
1920 | const isValid = p.get('argument').evaluateTruthy();
|
1921 | if (isValid === false) {
|
1922 | node.argument = t.nullLiteral();
|
1923 | }
|
1924 | else {
|
1925 | p.remove();
|
1926 | }
|
1927 | }
|
1928 | }
|
1929 | else {
|
1930 | p.remove();
|
1931 | }
|
1932 | });
|
1933 | }
|
1934 | setCustomEvent() {
|
1935 | const classPath = this.renderPath.findParent(isClassDcl);
|
1936 | const eventPropName = adapter_1.Adapter.type === "quickapp" ? 'privateTaroEvent' : '$$events';
|
1937 | const body = classPath.node.body.body.find(b => t.isClassProperty(b) && b.key.name === eventPropName);
|
1938 | const usedEvents = Array.from(this.usedEvents).map(s => t.stringLiteral(s));
|
1939 | if (body && t.isArrayExpression(body.value)) {
|
1940 | body.value = t.arrayExpression(lodash_1.uniq(body.value.elements.concat(usedEvents)));
|
1941 | }
|
1942 | else {
|
1943 | let classProp = t.classProperty(t.identifier(eventPropName), t.arrayExpression(usedEvents));
|
1944 | classProp.static = true;
|
1945 | classPath.node.body.body.unshift(classProp);
|
1946 | }
|
1947 | }
|
1948 | setUsedState() {
|
1949 | if (!this.isDefaultRender) {
|
1950 | return;
|
1951 | }
|
1952 | for (const [key, method] of this.methods) {
|
1953 | if (method) {
|
1954 | if (method.isClassMethod()) {
|
1955 | const kind = method.node.kind;
|
1956 | if (kind === 'get') {
|
1957 | this.classComputedState.add(key);
|
1958 | }
|
1959 | }
|
1960 | }
|
1961 | }
|
1962 | let componentProperies = lodash_1.cloneDeep(this.componentProperies);
|
1963 | componentProperies.forEach(s => {
|
1964 | if (s.startsWith(constant_1.FN_PREFIX)) {
|
1965 | const eventName = s.slice(5);
|
1966 | if (componentProperies.has(eventName)) {
|
1967 | componentProperies.delete(s);
|
1968 | componentProperies.delete(eventName);
|
1969 | }
|
1970 | }
|
1971 | });
|
1972 | if (adapter_1.Adapter.type === "quickapp" ) {
|
1973 | componentProperies = new Set(Array.from(componentProperies).map(p => this.upperCaseComponentProps.has(p) && !p.startsWith('on') && !p.startsWith('prv-fn') ? lodash_1.snakeCase(p) : p));
|
1974 | }
|
1975 | Array.from(this.reserveStateWords).forEach(this.setReserveWord);
|
1976 | let usedState = Array.from(new Set(Array.from(this.referencedIdentifiers)
|
1977 | .map(i => i.name)
|
1978 | .concat([...this.initState, ...this.usedThisState, ...componentProperies, ...this.classComputedState])))
|
1979 | .concat(...this.usedState)
|
1980 |
|
1981 |
|
1982 |
|
1983 | .filter(i => !this.loopScopes.has(i))
|
1984 | .filter(i => !this.templates.has(i))
|
1985 | .filter(Boolean);
|
1986 | if (adapter_1.Adapter.type === "quickapp" ) {
|
1987 | usedState = usedState
|
1988 | .filter(i => !new Set([...this.upperCaseComponentProps].map(i => i.toLowerCase())).has(i))
|
1989 | .filter(i => !this.upperCaseComponentProps.has(i));
|
1990 | }
|
1991 | const classPath = this.renderPath.findParent(isClassDcl);
|
1992 | classPath.node.body.body.unshift(t.classProperty(t.identifier('$usedState'), t.arrayExpression([...new Set(usedState
|
1993 | .filter(s => !this.loopScopes.has(s.split('.')[0]))
|
1994 | .filter(i => i !== constant_1.MAP_CALL_ITERATOR && !this.reserveStateWords.has(i))
|
1995 | .filter(i => utils_1.isVarName(i))
|
1996 | .filter(i => !this.loopRefIdentifiers.has(i))
|
1997 | .concat(Array.from(this.customComponentNames)))]
|
1998 | .map(s => t.stringLiteral(s)))));
|
1999 | }
|
2000 | checkDuplicateName() {
|
2001 | this.loopScopes.forEach(s => {
|
2002 | if (s.includes('anonIdx')) {
|
2003 | return;
|
2004 | }
|
2005 | if (this.renderPath.scope.hasBinding(s)) {
|
2006 | const err = utils_1.codeFrameError(this.renderPath.scope.getBinding(s).path.node, '此变量声明与循环变量冲突,可能会造成问题。');
|
2007 |
|
2008 | console.warn('Warning: ', err.message);
|
2009 | this.loopScopes.delete(s);
|
2010 | }
|
2011 | });
|
2012 | }
|
2013 | setPendingState() {
|
2014 | let propertyKeys = Array.from(new Set(Array.from(this.referencedIdentifiers)
|
2015 | .map(i => i.name)))
|
2016 | .filter(i => {
|
2017 | const method = this.methods.get(i);
|
2018 | let isGet = false;
|
2019 | if (method) {
|
2020 | if (method.isClassMethod()) {
|
2021 | const kind = method.node.kind;
|
2022 | if (kind === 'get') {
|
2023 | isGet = true;
|
2024 | }
|
2025 | }
|
2026 | }
|
2027 | return !this.methods.has(i) || isGet;
|
2028 | })
|
2029 | .filter(i => !this.loopScopes.has(i))
|
2030 | .filter(i => !this.templates.has(i))
|
2031 | .filter(i => utils_1.isVarName(i))
|
2032 | .filter(i => i !== constant_1.MAP_CALL_ITERATOR && !this.reserveStateWords.has(i))
|
2033 | .filter(i => !i.startsWith('$$'))
|
2034 | .filter(i => !i.startsWith('_$indexKey'))
|
2035 | .filter(i => !this.loopRefIdentifiers.has(i));
|
2036 | if (this.isDefaultRender) {
|
2037 | propertyKeys = propertyKeys.filter(i => !this.initState.has(i));
|
2038 | }
|
2039 | let properties = propertyKeys.map(i => t.objectProperty(t.identifier(i), t.identifier(i)));
|
2040 | const pendingState = t.objectExpression(properties.concat(Array.from(this.classComputedState).filter(i => {
|
2041 | return !propertyKeys.includes(i);
|
2042 | }).map(i => {
|
2043 | return t.objectProperty(t.identifier(i), t.memberExpression(t.thisExpression(), t.identifier(i)));
|
2044 | })));
|
2045 | this.propsSettingExpressions.forEach((exprs, stem) => {
|
2046 | stem.body.push(...exprs.map(e => e()));
|
2047 | });
|
2048 | this.renderPath.node.body.body.unshift(...Array.from(this.genCompidExprs));
|
2049 | if (this.isDefaultRender) {
|
2050 | if (this.refObjExpr && this.refObjExpr.length) {
|
2051 | this.renderPath.node.body.body.push(t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('$$refs')), t.identifier('pushRefs')), [t.arrayExpression(this.refObjExpr)])));
|
2052 | }
|
2053 | this.renderPath.node.body.body = this.renderPath.node.body.body.concat(
|
2054 |
|
2055 | buildAssignState(pendingState), t.returnStatement(t.memberExpression(t.thisExpression(), t.identifier('state'))));
|
2056 | }
|
2057 | else {
|
2058 | const usedState = Array.from(this.usedThisState).map(s => t.objectProperty(t.identifier(s), t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('state')), t.identifier(s))));
|
2059 | this.renderPath.node.body.body.push(
|
2060 |
|
2061 | t.returnStatement(t.objectExpression(pendingState.properties.concat(usedState))));
|
2062 | const { async, body, params } = this.renderPath.node;
|
2063 | this.renderPath.replaceWith(t.classMethod('method', t.identifier(`_create${this.renderMethodName.slice(6)}Data`), [t.identifier(constant_1.CLASS_COMPONENT_UID)], t.blockStatement([
|
2064 | t.returnStatement(t.arrowFunctionExpression(params, body, async))
|
2065 | ])));
|
2066 | }
|
2067 | this.renderPath.traverse({
|
2068 | Identifier: (path) => {
|
2069 | if (this.propsDecls.has(path.node.name) && path.parentPath.isCallExpression()) {
|
2070 | const { callee } = path.parentPath.node;
|
2071 | if (t.isMemberExpression(callee) && t.isIdentifier(callee.object, { name: constant_1.PROPS_MANAGER }) && t.isIdentifier(callee.property, { name: 'set' })) {
|
2072 | const decl = this.propsDecls.get(path.node.name);
|
2073 | path.replaceWith(decl.node.declarations[0].init);
|
2074 | this.propsDecls.delete(path.node.name);
|
2075 | !decl.removed && decl.remove();
|
2076 | }
|
2077 | }
|
2078 | }
|
2079 | });
|
2080 | }
|
2081 | createData() {
|
2082 | if (!this.isDefaultRender) {
|
2083 | return;
|
2084 | }
|
2085 | const renderBody = this.renderPath.get('body');
|
2086 | renderBody.traverse({
|
2087 | ThisExpression(path) {
|
2088 | const property = path.getSibling('property');
|
2089 | if (property.isIdentifier({ name: 'state' })) {
|
2090 | property.replaceWith(t.identifier('__state'));
|
2091 | }
|
2092 | if (property.isIdentifier({ name: 'props' })) {
|
2093 | property.replaceWith(t.identifier('__props'));
|
2094 | }
|
2095 | }
|
2096 | });
|
2097 | this.usedThisProperties.forEach(prop => {
|
2098 | if (this.renderScope.hasBinding(prop)) {
|
2099 | const binding = this.renderScope.getBinding(prop);
|
2100 | throw utils_1.codeFrameError(binding.path.node, `此变量声明与 this.${prop} 的声明冲突,请更改其中一个变量名。详情见:https://github.com/NervJS/taro/issues/822`);
|
2101 | }
|
2102 | });
|
2103 | this.renderPath.node.body.body.unshift(template(`this.__state = arguments[0] || this.state || {};`)(), template(`this.__props = arguments[1] || this.props || {};`)(), template(`const __isRunloopRef = arguments[2];`)(), template(`const __prefix = this.$prefix`)(), this.usedThisProperties.size
|
2104 | ? t.variableDeclaration('const', [
|
2105 | t.variableDeclarator(t.objectPattern(Array.from(this.usedThisProperties).map(p => t.objectProperty(t.identifier(p), t.identifier(p)))), t.thisExpression())
|
2106 | ])
|
2107 | : t.emptyStatement());
|
2108 | if (t.isIdentifier(this.renderPath.node.key)) {
|
2109 | this.renderPath.node.key.name = '_createData';
|
2110 | }
|
2111 | }
|
2112 | }
|
2113 | exports.RenderParser = RenderParser;
|
2114 |
|
\ | No newline at end of file |