UNPKG

51.9 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const t = require("babel-types");
4const path_1 = require("path");
5const utils_1 = require("./utils");
6const constant_1 = require("./constant");
7const lodash_1 = require("lodash");
8const render_1 = require("./render");
9const jsx_1 = require("./jsx");
10const adapter_1 = require("./adapter");
11const babel_generator_1 = require("babel-generator");
12const env_1 = require("./env");
13const functional_1 = require("./functional");
14const render_props_1 = require("./render-props");
15const stopPropagationExpr = require('babel-template')(`typeof e === 'object' && e.stopPropagation && e.stopPropagation()`);
16function buildConstructor() {
17 const ctor = t.classMethod('constructor', t.identifier('constructor'), [t.identifier('props')], t.blockStatement([
18 t.expressionStatement(t.callExpression(t.identifier('super'), [
19 t.identifier('props')
20 ]))
21 ]));
22 return ctor;
23}
24function processThisPropsFnMemberProperties(member, path, args) {
25 const propertyArray = [];
26 function traverseMember(member) {
27 const object = member.object;
28 const property = member.property;
29 if (t.isIdentifier(property)) {
30 propertyArray.push(property.name);
31 }
32 if (t.isMemberExpression(object)) {
33 if (t.isThisExpression(object.object) &&
34 t.isIdentifier(object.property) &&
35 object.property.name === 'props') {
36 if (!adapter_1.isNewPropsSystem()) {
37 path.replaceWith(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('__triggerPropsFn')), [t.stringLiteral(propertyArray.reverse().join('.')), t.callExpression(t.memberExpression(t.arrayExpression([t.nullLiteral()]), t.identifier('concat')), [t.arrayExpression(args)])]));
38 }
39 }
40 traverseMember(object);
41 }
42 }
43 traverseMember(member);
44}
45class Transformer {
46 constructor(path, sourcePath, componentProperies, sourceDir, methods) {
47 this.result = {
48 template: '',
49 components: [],
50 componentProperies: []
51 };
52 this.renderJSX = new Map();
53 this.refIdMap = new Map();
54 this.initState = new Set();
55 this.customComponents = new Map();
56 this.anonymousMethod = new Map();
57 this.customComponentNames = new Set();
58 this.usedState = new Set();
59 this.refs = [];
60 this.loopRefs = new Map();
61 this.anonymousFuncCounter = utils_1.incrementId();
62 this.importJSXs = new Set();
63 this.refObjExpr = [];
64 this.buildAnonyMousFunc = (jsxExpr, attr, expr) => {
65 const exprPath = attr.get('value.expression');
66 const stemParent = jsxExpr.getStatementParent();
67 const counter = this.anonymousFuncCounter();
68 const anonymousFuncName = `${constant_1.ANONYMOUS_FUNC}${counter}`;
69 const isCatch = utils_1.isContainStopPropagation(exprPath);
70 const classBody = this.classPath.node.body.body;
71 const loopCallExpr = jsxExpr.findParent(p => utils_1.isArrayMapCallExpression(p));
72 let index;
73 const self = this;
74 if (loopCallExpr) {
75 index = lodash_1.get(loopCallExpr, 'node.arguments[0].params[1]');
76 if (!t.isIdentifier(index)) {
77 index = t.identifier('__index' + counter);
78 lodash_1.set(loopCallExpr, 'node.arguments[0].params[1]', index);
79 }
80 classBody.push(t.classProperty(t.identifier(anonymousFuncName + 'Map'), t.objectExpression([])));
81 const indexKey = stemParent.scope.generateUid('$indexKey');
82 // tslint:disable-next-line: no-inner-declarations
83 function findParentLoopCallExprIndices(callExpr) {
84 const indices = new Set([]);
85 // tslint:disable-next-line: no-conditional-assignment
86 while (callExpr = callExpr.findParent(p => utils_1.isArrayMapCallExpression(p) && p !== callExpr)) {
87 let index = lodash_1.get(callExpr, 'node.arguments[0].params[1]');
88 if (!t.isIdentifier(index)) {
89 index = t.identifier('__index' + self.anonymousFuncCounter());
90 lodash_1.set(callExpr, 'node.arguments[0].params[1]', index);
91 }
92 indices.add(index);
93 }
94 return indices;
95 }
96 const indices = [...findParentLoopCallExprIndices(loopCallExpr)].reverse();
97 const indexKeyDecl = t.variableDeclaration('const', [t.variableDeclarator(t.identifier(indexKey), indices.length === 0
98 ? t.binaryExpression('+', t.stringLiteral(utils_1.createRandomLetters(5)), index)
99 : t.templateLiteral([
100 t.templateElement({ raw: utils_1.createRandomLetters(5) }),
101 ...indices.map(() => t.templateElement({ raw: '-' })),
102 t.templateElement({ raw: '' })
103 ], [
104 ...indices.map(i => t.identifier(i.name)),
105 index
106 ]))]);
107 const func = loopCallExpr.node.arguments[0];
108 if (t.isArrowFunctionExpression(func)) {
109 const body = loopCallExpr.get('arguments')[0].get('body.body');
110 if (!t.isBlockStatement(func.body)) {
111 func.body = t.blockStatement([
112 indexKeyDecl,
113 t.returnStatement(func.body)
114 ]);
115 }
116 else {
117 // func.body.body.push(indexKeyDecl)
118 // 只有 path 的方法才能触发 traverse
119 body[body.length - 1].insertBefore(indexKeyDecl);
120 }
121 const arrayFunc = t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(anonymousFuncName + 'Map')), t.identifier(indexKey), true);
122 classBody.push(t.classMethod('method', t.identifier(anonymousFuncName), [t.identifier(indexKey), t.restElement(t.identifier('e'))], t.blockStatement([
123 isCatch ? stopPropagationExpr() : t.emptyStatement(),
124 t.returnStatement(t.logicalExpression('&&', arrayFunc, t.callExpression(arrayFunc, [t.spreadElement(t.identifier('e'))])))
125 ])));
126 exprPath.replaceWith(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(anonymousFuncName)), t.identifier('bind')), [t.thisExpression(), t.identifier(indexKey)]));
127 body[body.length - 1].insertBefore(t.expressionStatement(t.assignmentExpression('=', arrayFunc, expr)));
128 }
129 else {
130 throw utils_1.codeFrameError(func, '返回 JSX 的循环语句必须使用箭头函数');
131 }
132 }
133 else {
134 classBody.push(t.classMethod('method', t.identifier(anonymousFuncName), [t.identifier('e')], t.blockStatement([
135 isCatch ? t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('e'), t.identifier('stopPropagation')), [])) : t.emptyStatement()
136 ])));
137 exprPath.replaceWith(t.memberExpression(t.thisExpression(), t.identifier(anonymousFuncName)));
138 stemParent.insertBefore(t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.thisExpression(), t.identifier(anonymousFuncName)), expr)));
139 }
140 };
141 this.jsxClosureFuncDecl = new Set();
142 this.renameJSXClassFunc = (propName, methodName, callPath, args, isClosure = false) => {
143 const parentPath = callPath.parentPath;
144 if (parentPath.isCallExpression()) {
145 return;
146 }
147 const callee = !isClosure
148 ? t.memberExpression(t.thisExpression(), t.identifier(`_create${propName.slice(6)}Data`))
149 : t.identifier(propName);
150 const templateAttr = [
151 t.jSXAttribute(t.jSXIdentifier('is'), t.stringLiteral(propName)),
152 t.jSXAttribute(t.jSXIdentifier('data'), t.jSXExpressionContainer(t.callExpression(t.callExpression(callee, [t.binaryExpression('+', methodName === 'render'
153 ? t.identifier('__prefix')
154 : t.identifier(constant_1.CLASS_COMPONENT_UID), t.stringLiteral(utils_1.createRandomLetters(10)))]), args)))
155 ];
156 this.jsxClosureFuncDecl.add(parentPath);
157 callPath.replaceWith(t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier('Template'), templateAttr), t.jSXClosingElement(t.jSXIdentifier('Template')), [], false));
158 };
159 this.buildPropsAnonymousFunc = (attr, expr, isBind = false, path) => {
160 const { code } = babel_generator_1.default(expr);
161 const id = t.isMemberExpression(expr.callee) ? utils_1.findFirstIdentifierFromMemberExpression(expr.callee) : null;
162 if (code.startsWith('this.props') ||
163 (id && utils_1.isDerivedFromProps(attr.scope, id.name))) {
164 const methodName = utils_1.findMethodName(expr);
165 const uniqueMethodName = `${methodName}${String(isBind)}`;
166 const hasMethodName = this.anonymousMethod.has(uniqueMethodName) || !methodName;
167 const funcName = hasMethodName
168 ? this.anonymousMethod.get(uniqueMethodName)
169 // 测试时使用1个稳定的 uniqueID 便于测试,实际使用5个英文字母,否则小程序不支持
170 : env_1.isTestEnv ? lodash_1.uniqueId('funPrivate') : `funPrivate${utils_1.createRandomLetters(5)}`;
171 this.anonymousMethod.set(uniqueMethodName, funcName);
172 const newVal = isBind
173 ? t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(funcName)), t.identifier('bind')), expr.arguments || [])
174 : t.memberExpression(t.thisExpression(), t.identifier(funcName));
175 attr.get('value.expression').replaceWith(newVal);
176 this.methods.set(funcName, null);
177 this.componentProperies.add(methodName);
178 if (hasMethodName) {
179 return;
180 }
181 const attrName = attr.node.name;
182 if (t.isJSXIdentifier(attrName) && attrName.name.startsWith('on')) {
183 this.componentProperies.add(`${constant_1.FN_PREFIX}${attrName.name}`);
184 }
185 if (methodName.startsWith('on')) {
186 this.componentProperies.add(`${constant_1.FN_PREFIX}${methodName}`);
187 }
188 const method = !adapter_1.isNewPropsSystem() ?
189 t.classMethod('method', t.identifier(funcName), [], t.blockStatement([
190 t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('__triggerPropsFn')), [t.stringLiteral(methodName), t.arrayExpression([t.spreadElement(t.identifier('arguments'))])]))
191 ])) :
192 t.classMethod('method', t.identifier(funcName), [], t.blockStatement([
193 t.returnStatement(t.callExpression(t.memberExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('props')), t.identifier(methodName)), t.identifier('apply')), [
194 isBind ? t.identifier('this') : t.identifier('undefined'),
195 t.callExpression(t.memberExpression(t.memberExpression(t.memberExpression(t.identifier('Array'), t.identifier('prototype')), t.identifier('slice')), t.identifier('call')), [t.identifier('arguments'), t.numericLiteral(1)])
196 ]))
197 ]));
198 this.classPath.node.body.body = this.classPath.node.body.body.concat(method);
199 }
200 else if (t.isMemberExpression(expr) && !t.isThisExpression(expr.object)) {
201 // @TODO: 新旧 props 系统在事件处理上耦合太深,快应用应用新 props 把旧 props 系统逻辑全部清楚
202 this.buildAnonyMousFunc(path, attr, expr);
203 }
204 };
205 this.classPath = path;
206 this.sourcePath = sourcePath;
207 this.sourceDir = sourceDir;
208 this.moduleNames = Object.keys(path.scope.getAllBindings('module'));
209 this.componentProperies = new Set(componentProperies);
210 this.methods = methods;
211 this.compile();
212 }
213 setMultipleSlots() {
214 const body = this.classPath.node.body.body;
215 if (body.some(c => t.isClassProperty(c) && c.key.name === 'multipleSlots')) {
216 return;
217 }
218 const multipleSlots = t.classProperty(t.identifier('multipleSlots'), t.booleanLiteral(true));
219 multipleSlots.static = true;
220 body.push(multipleSlots);
221 }
222 createStringRef(componentName, id, refName) {
223 this.refs.push({
224 type: constant_1.DEFAULT_Component_SET.has(componentName) ? 'dom' : 'component',
225 id,
226 refName
227 });
228 }
229 createFunctionRef(componentName, id, fn) {
230 this.refs.push({
231 type: constant_1.DEFAULT_Component_SET.has(componentName) ? 'dom' : 'component',
232 id,
233 fn
234 });
235 }
236 handleRefs() {
237 this.refObjExpr = this.refs.map(ref => {
238 return t.objectExpression([
239 t.objectProperty(t.identifier('type'), t.stringLiteral(ref.type)),
240 t.objectProperty(t.identifier('id'), t.stringLiteral(ref.id)),
241 t.objectProperty(t.identifier('refName'), t.stringLiteral(ref.refName || '')),
242 t.objectProperty(t.identifier('fn'), ref.fn ? ref.fn : t.nullLiteral())
243 ]);
244 });
245 const _constructor = this.classPath.node.body.body.find(item => {
246 const constructorName = env_1.isTestEnv ? 'constructor' : '_constructor';
247 if (t.isClassMethod(item) && t.isIdentifier(item.key) && item.key.name === constructorName) {
248 return true;
249 }
250 return false;
251 });
252 if (_constructor && t.isClassMethod(_constructor) && adapter_1.Adapter.type !== "quickapp" /* quickapp */) {
253 _constructor.body.body.push(t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.thisExpression(), t.identifier('$$refs')), t.newExpression(t.memberExpression(t.identifier('Taro'), t.identifier('RefsArray')), []))));
254 }
255 }
256 setComponentPath() {
257 let componentPath = this.sourcePath.replace(this.sourceDir, '');
258 componentPath = componentPath.replace(path_1.extname(componentPath), '');
259 componentPath = componentPath.split(path_1.sep).join('/');
260 if (componentPath.startsWith('/')) {
261 componentPath = componentPath.slice(1);
262 }
263 const $$componentPath = t.classProperty(t.identifier('$$componentPath'), t.stringLiteral(componentPath));
264 $$componentPath.static = true;
265 this.classPath.node.body.body.push($$componentPath);
266 }
267 traverse() {
268 const self = this;
269 let hasRender = false;
270 self.classPath.traverse({
271 JSXOpeningElement: (path) => {
272 const jsx = path.node;
273 const attrs = jsx.attributes;
274 if (!t.isJSXIdentifier(jsx.name)) {
275 return;
276 }
277 const loopCallExpr = path.findParent(p => utils_1.isArrayMapCallExpression(p));
278 const componentName = jsx.name.name;
279 const refAttr = jsx_1.findJSXAttrByName(attrs, 'ref');
280 if (!refAttr) {
281 return;
282 }
283 const idAttr = jsx_1.findJSXAttrByName(attrs, 'id');
284 let id = utils_1.createRandomLetters(5);
285 let idExpr;
286 if (!idAttr) {
287 if (loopCallExpr && loopCallExpr.isCallExpression()) {
288 const [func] = loopCallExpr.node.arguments;
289 let indexId = null;
290 if (t.isFunctionExpression(func) || t.isArrowFunctionExpression(func)) {
291 const params = func.params;
292 indexId = params[1];
293 }
294 if (indexId === null || !t.isIdentifier(indexId)) {
295 throw utils_1.codeFrameError(path.node, '在循环中使用 ref 必须暴露循环的第二个参数 `index`');
296 }
297 attrs.push(t.jSXAttribute(t.jSXIdentifier('id'), t.jSXExpressionContainer(t.binaryExpression('+', t.stringLiteral(id), indexId))));
298 }
299 else {
300 attrs.push(t.jSXAttribute(t.jSXIdentifier('id'), t.stringLiteral(id)));
301 }
302 }
303 else {
304 const idValue = idAttr.value;
305 if (t.isStringLiteral(idValue)) {
306 id = idValue.value;
307 }
308 else if (t.isJSXExpressionContainer(idValue)) {
309 if (t.isStringLiteral(idValue.expression)) {
310 id = idValue.expression.value;
311 }
312 else {
313 idExpr = idValue.expression;
314 }
315 }
316 }
317 if (t.isStringLiteral(refAttr.value)) {
318 if (loopCallExpr) {
319 throw utils_1.codeFrameError(refAttr, '循环中的 ref 只能使用函数。');
320 }
321 this.createStringRef(componentName, id, refAttr.value.value);
322 }
323 if (t.isJSXExpressionContainer(refAttr.value)) {
324 const expr = refAttr.value.expression;
325 if (t.isStringLiteral(expr)) {
326 if (loopCallExpr) {
327 throw utils_1.codeFrameError(refAttr, '循环中的 ref 只能使用函数。');
328 }
329 this.createStringRef(componentName, id, expr.value);
330 }
331 else if (t.isArrowFunctionExpression(expr) || t.isMemberExpression(expr)) {
332 const type = constant_1.DEFAULT_Component_SET.has(componentName) ? 'dom' : 'component';
333 if (loopCallExpr) {
334 this.loopRefs.set(path.parentPath.node, {
335 id: idExpr || id,
336 fn: expr,
337 type,
338 component: path.parentPath
339 });
340 }
341 else {
342 this.refs.push({
343 type,
344 id,
345 fn: expr
346 });
347 }
348 }
349 else if (t.isIdentifier(expr)) {
350 const type = constant_1.DEFAULT_Component_SET.has(componentName) ? 'dom' : 'component';
351 const binding = path.scope.getBinding(expr.name);
352 const decl = t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.thisExpression(), expr), expr));
353 if (binding) {
354 binding.path.parentPath.insertAfter(decl);
355 }
356 else {
357 path.getStatementParent().insertBefore(decl);
358 }
359 this.refs.push({
360 type,
361 id,
362 fn: t.memberExpression(t.thisExpression(), expr)
363 });
364 }
365 else {
366 throw utils_1.codeFrameError(refAttr, 'ref 仅支持传入字符串、匿名箭头函数和 class 中已声明的函数');
367 }
368 }
369 if ("alipay" /* alipay */ === adapter_1.Adapter.type) {
370 attrs.push(t.jSXAttribute(t.jSXIdentifier('onTaroCollectChilds'), t.jSXExpressionContainer(t.memberExpression(t.thisExpression(), t.identifier('$collectChilds')))));
371 }
372 for (const [index, attr] of attrs.entries()) {
373 if (attr === refAttr) {
374 attrs.splice(index, 1);
375 }
376 }
377 },
378 ClassMethod(classMethodPath) {
379 const node = classMethodPath.node;
380 if (t.isIdentifier(node.key)) {
381 const methodName = node.key.name;
382 self.methods.set(methodName, classMethodPath);
383 if (methodName.startsWith('render')) {
384 if (!utils_1.isContainJSXElement(classMethodPath)) {
385 throw utils_1.codeFrameError(classMethodPath.node, '以 render 开头的类函数必须返回 JSX,否则会导致渲染失败。如果是为了渲染字符串,建议更名。\n' +
386 '以 VSCode 为例:右键点击选择方法名,点击 rename symbol(重命名符号),输入新方法名。');
387 }
388 hasRender = true;
389 self.renderJSX.set(methodName, classMethodPath);
390 self.refIdMap.set(classMethodPath, new Set([]));
391 classMethodPath.traverse({
392 ReturnStatement(returnPath) {
393 const arg = returnPath.node.argument;
394 const ifStem = returnPath.findParent(p => p.isIfStatement());
395 // tslint:disable-next-line: strict-type-predicates
396 if (ifStem && classMethodPath.node.body.body.some(s => s === ifStem.node) && ifStem.isIfStatement() && arg === null) {
397 const consequent = ifStem.get('consequent');
398 if (consequent.isBlockStatement() && consequent.node.body.includes(returnPath.node)) {
399 returnPath.get('argument').replaceWith(t.nullLiteral());
400 }
401 }
402 },
403 CallExpression: {
404 enter(callPath) {
405 const callee = callPath.get('callee');
406 const args = callPath.node.arguments;
407 if (callee.isMemberExpression()) {
408 const { object, property } = callee.node;
409 if (t.isThisExpression(object) && t.isIdentifier(property) && property.name.startsWith('render')) {
410 const propName = property.name;
411 if (!self.methods.has(propName)) {
412 const o = utils_1.getSuperClassPath(self.classPath);
413 if (o) {
414 const p = o.resolvePath.endsWith('.js') ? o.resolvePath.slice(0, o.resolvePath.length - 3) : o.resolvePath;
415 self.importJSXs.add(`<import src="${p + '.wxml'}"/>`);
416 }
417 }
418 self.renameJSXClassFunc(propName, methodName, callPath, args);
419 }
420 }
421 if (callee.isIdentifier()) {
422 const nodeName = callee.node.name;
423 if (nodeName.startsWith('renderClosure')) {
424 self.renameJSXClassFunc(nodeName, methodName, callPath, args, true);
425 }
426 }
427 },
428 exit(callPath) {
429 const jsxExpr = callPath.parentPath;
430 if (!jsxExpr.isJSXExpressionContainer()) {
431 return;
432 }
433 const jsxAttr = jsxExpr.parentPath;
434 if (!jsxAttr.isJSXAttribute()) {
435 return;
436 }
437 const { name: attrName } = jsxAttr.node;
438 if (!t.isJSXIdentifier(attrName, { name: 'data' })) {
439 return;
440 }
441 utils_1.generateAnonymousState(callPath.scope, callPath, self.refIdMap.get(classMethodPath));
442 }
443 }
444 });
445 }
446 if (methodName.startsWith('render')) {
447 self.renderJSX.set(methodName, classMethodPath);
448 self.refIdMap.set(classMethodPath, new Set([]));
449 }
450 if (methodName === 'constructor') {
451 classMethodPath.traverse({
452 AssignmentExpression(p) {
453 if (t.isMemberExpression(p.node.left) &&
454 t.isThisExpression(p.node.left.object) &&
455 t.isIdentifier(p.node.left.property) &&
456 p.node.left.property.name === 'state' &&
457 t.isObjectExpression(p.node.right)) {
458 const properties = p.node.right.properties;
459 properties.forEach(p => {
460 if (t.isObjectProperty(p) && t.isIdentifier(p.key)) {
461 self.initState.add(p.key.name);
462 }
463 });
464 }
465 }
466 });
467 }
468 }
469 },
470 ClassBody: {
471 exit(path) {
472 const node = path.node;
473 if (!hasRender) {
474 node.body.push(t.classMethod('method', t.identifier('_createData'), [], t.blockStatement([])));
475 }
476 }
477 },
478 IfStatement: (path) => {
479 const test = path.get('test');
480 const consequent = path.get('consequent');
481 if (utils_1.isContainJSXElement(consequent) && utils_1.hasComplexExpression(test)) {
482 this.renderJSX.forEach(method => {
483 const renderMethod = path.findParent(p => method === p);
484 if (renderMethod && renderMethod.isClassMethod()) {
485 const scope = renderMethod && renderMethod.scope || path.scope;
486 utils_1.generateAnonymousState(scope, test, this.refIdMap.get(renderMethod), true);
487 }
488 });
489 }
490 },
491 ClassProperty(path) {
492 const { key: { name }, value } = path.node;
493 if (t.isArrowFunctionExpression(value) || t.isFunctionExpression(value)) {
494 self.methods.set(name, path);
495 if (name.startsWith('render')) {
496 path.replaceWith(t.classMethod('method', t.identifier(name), value.params, t.isBlockStatement(value.body) ? value.body : t.blockStatement([
497 t.returnStatement(value.body)
498 ])));
499 }
500 }
501 if (name === 'state' && t.isObjectExpression(value)) {
502 value.properties.forEach(p => {
503 if (t.isObjectProperty(p)) {
504 if (t.isIdentifier(p.key)) {
505 self.initState.add(p.key.name);
506 }
507 }
508 });
509 }
510 },
511 JSXExpressionContainer(path) {
512 const attr = path.findParent(p => p.isJSXAttribute());
513 if (!attr) {
514 const expr = path.get('expression');
515 if (expr.isBooleanLiteral() || expr.isNullLiteral()) {
516 path.remove();
517 return;
518 }
519 }
520 const isFunctionProp = attr && typeof attr.node.name.name === 'string' && attr.node.name.name.startsWith('on');
521 let renderMethod;
522 self.renderJSX.forEach(method => {
523 renderMethod = path.findParent(p => method === p);
524 });
525 const jsxReferencedIdentifiers = self.refIdMap.get(renderMethod);
526 path.traverse({
527 MemberExpression(path) {
528 const sibling = path.getSibling('property');
529 if (path.get('object').isThisExpression() &&
530 (path.get('property').isIdentifier({ name: 'props' }) || path.get('property').isIdentifier({ name: 'state' })) &&
531 sibling.isIdentifier()) {
532 if (!isFunctionProp) {
533 self.usedState.add(sibling.node.name);
534 }
535 }
536 }
537 });
538 const expression = path.get('expression');
539 const scope = renderMethod && renderMethod.scope || path.scope;
540 const calleeExpr = expression.get('callee');
541 const parentPath = path.parentPath;
542 if (utils_1.hasComplexExpression(expression) &&
543 !isFunctionProp &&
544 !(calleeExpr &&
545 calleeExpr.isMemberExpression() &&
546 calleeExpr.get('object').isMemberExpression() &&
547 calleeExpr.get('property').isIdentifier({ name: 'bind' })) // is not bind
548 ) {
549 const calleeName = calleeExpr.isIdentifier() && calleeExpr.node.name;
550 if (typeof calleeName === 'string' && calleeName.startsWith('render') && utils_1.isDerivedFromProps(calleeExpr.scope, calleeName)) {
551 return;
552 }
553 if (calleeExpr.isMemberExpression() && utils_1.isDerivedFromProps(calleeExpr.scope, utils_1.findFirstIdentifierFromMemberExpression(calleeExpr.node).name)) {
554 const idName = utils_1.findFirstIdentifierFromMemberExpression(calleeExpr.node).name;
555 if (utils_1.isDerivedFromProps(calleeExpr.scope, idName) && t.isIdentifier(calleeExpr.node.property) && calleeExpr.node.property.name.startsWith('render')) {
556 return;
557 }
558 }
559 utils_1.generateAnonymousState(scope, expression, jsxReferencedIdentifiers);
560 }
561 else {
562 if (parentPath.isJSXAttribute()) {
563 if (!(expression.isMemberExpression() || expression.isIdentifier()) && parentPath.node.name.name === 'key') {
564 utils_1.generateAnonymousState(scope, expression, jsxReferencedIdentifiers);
565 }
566 }
567 }
568 if (!attr)
569 return;
570 const key = attr.node.name;
571 const value = attr.node.value;
572 if (!t.isJSXIdentifier(key)) {
573 return;
574 }
575 const jsx = path.findParent(p => p.isJSXOpeningElement());
576 if (t.isJSXIdentifier(key) && key.name.startsWith('on') && t.isJSXExpressionContainer(value)) {
577 const expr = value.expression;
578 if (t.isCallExpression(expr) &&
579 t.isMemberExpression(expr.callee) &&
580 t.isIdentifier(expr.callee.property, { name: 'bind' }) &&
581 !functional_1.Status.isSFC) {
582 if ((!adapter_1.isNewPropsSystem()) ||
583 (t.isJSXIdentifier(jsx.node.name) && constant_1.DEFAULT_Component_SET.has(jsx.node.name.name))) {
584 self.buildPropsAnonymousFunc(attr, expr, true, path);
585 }
586 }
587 else if (t.isMemberExpression(expr)) {
588 if ((!adapter_1.isNewPropsSystem()) ||
589 (t.isJSXIdentifier(jsx.node.name) && constant_1.DEFAULT_Component_SET.has(jsx.node.name.name))) {
590 self.buildPropsAnonymousFunc(attr, expr, false, path);
591 }
592 }
593 else if (!t.isLiteral(expr)) {
594 self.buildAnonyMousFunc(path, attr, expr);
595 }
596 else {
597 throw utils_1.codeFrameError(path.node, '组件事件传参不能传入基本类型');
598 }
599 }
600 if (!jsx)
601 return;
602 const jsxName = jsx.node.name;
603 if (!t.isJSXIdentifier(jsxName))
604 return;
605 if (expression.isJSXElement())
606 return;
607 if (constant_1.DEFAULT_Component_SET.has(jsxName.name) || expression.isIdentifier() || expression.isMemberExpression() || expression.isLiteral() || expression.isLogicalExpression() || expression.isConditionalExpression() || key.name.startsWith('on') || expression.isCallExpression())
608 return;
609 if (utils_1.isContainJSXElement(path))
610 return;
611 utils_1.generateAnonymousState(scope, expression, jsxReferencedIdentifiers);
612 },
613 Identifier(path) {
614 const isStartWithRender = /^render[A-Z]/.test(path.node.name);
615 const isInJSXExprContainer = !!path.findParent(p => p.isJSXExpressionContainer());
616 if (!isInJSXExprContainer) {
617 return;
618 }
619 if (path.node.name === 'children' || isStartWithRender) {
620 const parentPath = path.parentPath;
621 const slot = t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier('slot'), [], true), t.jSXClosingElement(t.jSXIdentifier('slot')), [], true);
622 if (isStartWithRender) {
623 slot.openingElement.attributes.push(t.jSXAttribute(t.jSXIdentifier('name'), t.stringLiteral(utils_1.getSlotName(path.node.name))));
624 self.setMultipleSlots();
625 }
626 if (parentPath.isCallExpression() && parentPath.parentPath.isJSXExpressionContainer()) {
627 if (utils_1.isDerivedFromProps(path.scope, path.node.name)) {
628 render_props_1.injectRenderPropsEmiter(parentPath, path.node.name);
629 parentPath.replaceWith(slot);
630 }
631 }
632 if (parentPath.isMemberExpression() && parentPath.parentPath.isCallExpression()) {
633 if (utils_1.isDerivedFromProps(path.scope, utils_1.findFirstIdentifierFromMemberExpression(parentPath.node).name)) {
634 render_props_1.injectRenderPropsEmiter(parentPath.parentPath, path.node.name);
635 parentPath.parentPath.replaceWith(slot);
636 }
637 }
638 if (parentPath.isMemberExpression() &&
639 parentPath.isReferenced() &&
640 (parentPath.parentPath.isJSXExpressionContainer() ||
641 parentPath.parentPath.isLogicalExpression() ||
642 parentPath.parentPath.isConditionalExpression())) {
643 const object = parentPath.get('object');
644 if (object.isIdentifier()) {
645 const objectName = object.node.name;
646 if (utils_1.isDerivedFromProps(path.scope, objectName)) {
647 parentPath.replaceWith(slot);
648 }
649 }
650 }
651 else if (path.isReferencedIdentifier()) {
652 if (utils_1.isDerivedFromProps(path.scope, 'children')) {
653 parentPath.replaceWith(slot);
654 }
655 }
656 }
657 },
658 JSXElement(path) {
659 const id = path.node.openingElement.name;
660 if (t.isJSXIdentifier(id) &&
661 !constant_1.DEFAULT_Component_SET.has(id.name)) {
662 if (self.moduleNames.indexOf(id.name) !== -1) {
663 const name = id.name;
664 const binding = self.classPath.scope.getBinding(name);
665 if (binding && t.isImportDeclaration(binding.path.parent)) {
666 const sourcePath = binding.path.parent.source.value;
667 const specs = binding.path.parent.specifiers.filter(s => t.isImportSpecifier(s));
668 if (binding.path.isImportDefaultSpecifier()) {
669 self.customComponents.set(name, {
670 sourcePath,
671 type: 'default'
672 });
673 }
674 else {
675 const spec = specs.find(s => s.local.name === name && s.imported.name !== name);
676 if (spec) {
677 self.customComponents.set(name, {
678 sourcePath,
679 type: 'pattern',
680 imported: spec.imported.name
681 });
682 }
683 else {
684 self.customComponents.set(name, {
685 sourcePath,
686 type: 'pattern'
687 });
688 }
689 }
690 }
691 }
692 if (id.name.endsWith(constant_1.CONTEXT_PROVIDER)) {
693 const valueAttr = path.node.openingElement.attributes.find(a => t.isJSXIdentifier(a.name) && a.name.name === 'value');
694 const contextName = id.name.slice(0, id.name.length - constant_1.CONTEXT_PROVIDER.length);
695 if (valueAttr) {
696 if (t.isJSXElement(valueAttr.value)) {
697 throw utils_1.codeFrameError(valueAttr.value, 'Provider 的 value 只能传入一个字符串或普通表达式,不能传入 JSX');
698 }
699 else {
700 const value = t.isStringLiteral(valueAttr.value) ? valueAttr.value : valueAttr.value.expression;
701 const expr = t.expressionStatement(t.callExpression(t.memberExpression(t.identifier(contextName), t.identifier('Provider')), [value]));
702 path.getStatementParent().insertBefore(expr);
703 path.replaceWith(t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier('Block'), []), t.jSXClosingElement(t.jSXIdentifier('Block')), path.node.children));
704 }
705 }
706 }
707 }
708 },
709 MemberExpression: (path) => {
710 const object = path.get('object');
711 const property = path.get('property');
712 if (!(object.isThisExpression() && property.isIdentifier({ name: 'props' }))) {
713 return;
714 }
715 const parentPath = path.parentPath;
716 if (parentPath.isMemberExpression()) {
717 const siblingProp = parentPath.get('property');
718 if (siblingProp.isIdentifier()) {
719 const name = siblingProp.node.name;
720 if (name === 'children') {
721 parentPath.replaceWith(t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier('slot'), [], true), t.jSXClosingElement(t.jSXIdentifier('slot')), [], true));
722 }
723 else if (/^render[A-Z]/.test(name)) {
724 const slotName = utils_1.getSlotName(name);
725 if (parentPath.parentPath.isCallExpression()) {
726 render_props_1.injectRenderPropsEmiter(parentPath.parentPath, name);
727 parentPath.parentPath.replaceWith(t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier('slot'), [
728 t.jSXAttribute(t.jSXIdentifier('name'), t.stringLiteral(slotName))
729 ], true), t.jSXClosingElement(t.jSXIdentifier('slot')), []));
730 }
731 else {
732 parentPath.replaceWith(t.jSXElement(t.jSXOpeningElement(t.jSXIdentifier('slot'), [
733 t.jSXAttribute(t.jSXIdentifier('name'), t.stringLiteral(slotName))
734 ], true), t.jSXClosingElement(t.jSXIdentifier('slot')), []));
735 }
736 this.setMultipleSlots();
737 }
738 else {
739 self.componentProperies.add(siblingProp.node.name);
740 }
741 }
742 }
743 else if (parentPath.isVariableDeclarator()) {
744 const siblingId = parentPath.get('id');
745 if (siblingId.isObjectPattern()) {
746 const properties = siblingId.node.properties;
747 for (const prop of properties) {
748 if (t.isRestProperty(prop)) {
749 throw utils_1.codeFrameError(prop.loc, 'this.props 不支持使用 rest property 语法,请把每一个 prop 都单独列出来');
750 }
751 else if (t.isIdentifier(prop.key)) {
752 self.componentProperies.add(prop.key.name);
753 }
754 }
755 }
756 }
757 },
758 CallExpression(path) {
759 const node = path.node;
760 const callee = node.callee;
761 if (t.isMemberExpression(callee) && t.isMemberExpression(callee.object)) {
762 const property = callee.property;
763 if (t.isIdentifier(property)) {
764 if (property.name.startsWith('on')) {
765 self.componentProperies.add(`${constant_1.FN_PREFIX}${property.name}`);
766 processThisPropsFnMemberProperties(callee, path, node.arguments);
767 }
768 else if (property.name === 'call' || property.name === 'apply') {
769 self.componentProperies.add(`${constant_1.FN_PREFIX}${property.name}`);
770 processThisPropsFnMemberProperties(callee.object, path, node.arguments);
771 }
772 }
773 }
774 }
775 });
776 }
777 setComponents() {
778 const components = [];
779 this.customComponents.forEach((component, name) => {
780 if (name.startsWith('Taro') && component.sourcePath === constant_1.COMPONENTS_PACKAGE_NAME) {
781 return;
782 }
783 if (adapter_1.Adapter.type === "quickapp" /* quickapp */ && constant_1.DEFAULT_Component_SET_COPY.has(name)) {
784 return;
785 }
786 components.push(name);
787 this.result.components.push({
788 path: utils_1.pathResolver(component.sourcePath, this.sourcePath),
789 name: component.imported ? lodash_1.kebabCase(name) + '|' + lodash_1.kebabCase(component.imported) : lodash_1.kebabCase(name),
790 type: component.type
791 });
792 });
793 this.classPath.node.body.body.push(t.classProperty(t.identifier('customComponents'), t.arrayExpression(components.map(c => t.stringLiteral(c)))));
794 }
795 setMethods() {
796 const methods = this.classPath.get('body').get('body');
797 for (const method of methods) {
798 if (method.isClassMethod()) {
799 const key = method.get('key');
800 if (key.isIdentifier()) {
801 this.methods.set(key.node.name, method);
802 }
803 }
804 }
805 }
806 resetConstructor() {
807 const body = this.classPath.node.body.body;
808 if (!this.methods.has('constructor')) {
809 const ctor = buildConstructor();
810 body.unshift(ctor);
811 }
812 if (env_1.isTestEnv) {
813 return;
814 }
815 for (const method of body) {
816 if (t.isClassMethod(method) && method.kind === 'constructor') {
817 method.kind = 'method';
818 method.key = t.identifier('_constructor');
819 if (t.isBlockStatement(method.body)) {
820 for (const statement of method.body.body) {
821 if (t.isExpressionStatement(statement)) {
822 const expr = statement.expression;
823 if (t.isCallExpression(expr) && (t.isIdentifier(expr.callee, { name: 'super' }) || t.isSuper(expr.callee))) {
824 expr.callee = t.memberExpression(t.identifier('super'), t.identifier('_constructor'));
825 }
826 }
827 }
828 }
829 }
830 }
831 }
832 handleLifecyclePropParam(propParam, properties) {
833 let propsName = null;
834 if (!propParam) {
835 return null;
836 }
837 if (t.isIdentifier(propParam)) {
838 propsName = propParam.name;
839 }
840 else if (t.isObjectPattern(propParam)) {
841 for (const prop of propParam.properties) {
842 if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
843 properties.add(prop.key.name);
844 }
845 else if (t.isRestProperty(prop) && t.isIdentifier(prop.argument)) {
846 propsName = prop.argument.name;
847 }
848 }
849 }
850 else {
851 throw utils_1.codeFrameError(propParam.loc, '此生命周期的第一个参数只支持写标识符或对象解构');
852 }
853 return propsName;
854 }
855 findMoreProps() {
856 // 第一个参数是 props 的生命周期
857 const lifeCycles = new Set([
858 // 'constructor',
859 'componentDidUpdate',
860 'shouldComponentUpdate',
861 'getDerivedStateFromProps',
862 'getSnapshotBeforeUpdate',
863 'componentWillReceiveProps',
864 'componentWillUpdate'
865 ]);
866 const properties = new Set();
867 this.methods.forEach((method, name) => {
868 if (!lifeCycles.has(name)) {
869 return;
870 }
871 const node = method.node;
872 let propsName = null;
873 if (t.isClassMethod(node)) {
874 propsName = this.handleLifecyclePropParam(node.params[0], properties);
875 }
876 else if (t.isArrowFunctionExpression(node.value) || t.isFunctionExpression(node.value)) {
877 propsName = this.handleLifecyclePropParam(node.value.params[0], properties);
878 }
879 if (propsName === null) {
880 return;
881 }
882 method.traverse({
883 MemberExpression(path) {
884 if (!path.isReferencedMemberExpression()) {
885 return;
886 }
887 const { object, property } = path.node;
888 if (t.isIdentifier(object, { name: propsName }) && t.isIdentifier(property)) {
889 properties.add(property.name);
890 }
891 },
892 VariableDeclarator(path) {
893 const { id, init } = path.node;
894 if (t.isObjectPattern(id) && t.isIdentifier(init, { name: propsName })) {
895 for (const prop of id.properties) {
896 if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
897 properties.add(prop.key.name);
898 }
899 }
900 }
901 }
902 });
903 properties.forEach((value) => {
904 this.componentProperies.add(value);
905 });
906 });
907 }
908 parseRender() {
909 if (this.importJSXs.size) {
910 this.importJSXs.forEach(s => {
911 this.result.template += s + '\n';
912 });
913 }
914 if (this.renderJSX.size) {
915 this.renderJSX.forEach((method, methodName) => {
916 this.result.template = this.result.template
917 + new render_1.RenderParser(method, this.methods, this.initState, this.refIdMap.get(method), this.usedState, this.customComponentNames, this.componentProperies, this.loopRefs, this.refObjExpr, methodName).outputTemplate + '\n';
918 });
919 }
920 else {
921 throw utils_1.codeFrameError(this.classPath.node.loc, '没有定义 render 方法');
922 }
923 }
924 clearClosureMethods() {
925 this.classPath.node.body.body = this.classPath.node.body.body.filter(m => {
926 if (m && t.isClassMethod(m) && t.isIdentifier(m.key) && m.key.name.startsWith('_createClosure')) {
927 return false;
928 }
929 return true;
930 });
931 }
932 compile() {
933 this.traverse();
934 this.setMethods();
935 this.setComponents();
936 this.resetConstructor();
937 this.findMoreProps();
938 this.handleRefs();
939 this.parseRender();
940 this.setComponentPath();
941 this.clearClosureMethods();
942 this.result.componentProperies = [...this.componentProperies];
943 }
944}
945exports.Transformer = Transformer;
946//# sourceMappingURL=class.js.map
\No newline at end of file