transform.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.createStructuralDirectiveTransform = exports.createTransformContext = exports.traverseChildren = exports.traverseNode = exports.transform = exports.isScopedSlotVFor = exports.isVForScope = exports.isVIfScope = exports.isRootScope = void 0;
  7. const shared_1 = require("@vue/shared");
  8. const types_1 = require("@babel/types");
  9. const compiler_core_1 = require("@vue/compiler-core");
  10. const uni_cli_shared_1 = require("@dcloudio/uni-cli-shared");
  11. const identifier_1 = __importDefault(require("./identifier"));
  12. const runtimeHelpers_1 = require("./runtimeHelpers");
  13. const ast_1 = require("./ast");
  14. const utils_1 = require("./transforms/utils");
  15. const codegen_1 = require("./codegen");
  16. function isRootScope(scope) {
  17. return !isVIfScope(scope) && !isVForScope(scope);
  18. }
  19. exports.isRootScope = isRootScope;
  20. function isVIfScope(scope) {
  21. return (!!scope.condition ||
  22. scope.name === 'else');
  23. }
  24. exports.isVIfScope = isVIfScope;
  25. function isVForScope(scope) {
  26. return !!scope.source;
  27. }
  28. exports.isVForScope = isVForScope;
  29. function isScopedSlotVFor({ source }) {
  30. if (source.type !== 8 /* NodeTypes.COMPOUND_EXPRESSION */) {
  31. return false;
  32. }
  33. const first = source.children[0];
  34. return (first.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */ &&
  35. first.content.includes(utils_1.SCOPED_SLOT_IDENTIFIER));
  36. }
  37. exports.isScopedSlotVFor = isScopedSlotVFor;
  38. function transform(root, options) {
  39. const context = createTransformContext(root, options);
  40. findRootNode(root, context);
  41. traverseNode(root, context);
  42. root.renderData = createRenderDataExpr(context.scope.properties, context);
  43. // finalize meta information
  44. root.helpers = new Set([...context.helpers.keys()]);
  45. root.components = [...context.components];
  46. root.imports = context.imports;
  47. root.cached = context.cached;
  48. return context;
  49. }
  50. exports.transform = transform;
  51. function findRootNode(root, context) {
  52. const children = root.children.filter((node) => node.type === 1 /* NodeTypes.ELEMENT */ && node.tag !== 'template');
  53. if (children.length === 1) {
  54. context.rootNode = children[0];
  55. }
  56. }
  57. function traverseNode(node, context) {
  58. context.currentNode = node;
  59. // apply transform plugins
  60. const { nodeTransforms } = context;
  61. const exitFns = [];
  62. for (let i = 0; i < nodeTransforms.length; i++) {
  63. const onExit = nodeTransforms[i](node, context);
  64. if (onExit) {
  65. if ((0, shared_1.isArray)(onExit)) {
  66. exitFns.push(...onExit);
  67. }
  68. else {
  69. exitFns.push(onExit);
  70. }
  71. }
  72. if (!context.currentNode) {
  73. // node was removed
  74. return;
  75. }
  76. else {
  77. // node may have been replaced
  78. node = context.currentNode;
  79. }
  80. }
  81. switch (node.type) {
  82. case 3 /* NodeTypes.COMMENT */:
  83. // context.helper(CREATE_COMMENT)
  84. break;
  85. case 5 /* NodeTypes.INTERPOLATION */:
  86. context.helper(compiler_core_1.TO_DISPLAY_STRING);
  87. break;
  88. // for container types, further traverse downwards
  89. case 9 /* NodeTypes.IF */:
  90. for (let i = 0; i < node.branches.length; i++) {
  91. traverseNode(node.branches[i], context);
  92. }
  93. break;
  94. case 10 /* NodeTypes.IF_BRANCH */:
  95. case 11 /* NodeTypes.FOR */:
  96. case 1 /* NodeTypes.ELEMENT */:
  97. case 0 /* NodeTypes.ROOT */:
  98. traverseChildren(node, context);
  99. break;
  100. }
  101. // exit transforms
  102. context.currentNode = node;
  103. let i = exitFns.length;
  104. while (i--) {
  105. exitFns[i]();
  106. }
  107. }
  108. exports.traverseNode = traverseNode;
  109. function traverseChildren(parent, context) {
  110. let i = 0;
  111. const nodeRemoved = () => {
  112. i--;
  113. };
  114. for (; i < parent.children.length; i++) {
  115. const child = parent.children[i];
  116. if ((0, shared_1.isString)(child))
  117. continue;
  118. context.parent = parent;
  119. context.childIndex = i;
  120. context.onNodeRemoved = nodeRemoved;
  121. traverseNode(child, context);
  122. }
  123. }
  124. exports.traverseChildren = traverseChildren;
  125. function defaultOnError(error) {
  126. throw error;
  127. }
  128. function defaultOnWarn(msg) {
  129. console.warn(`[Vue warn] ${msg.message}`);
  130. }
  131. function createTransformContext(rootNode, { root = '', filename = '', isTS = false, inline = false, hashId = null, scopeId = null, filters = [], bindingCssVars = [], bindingMetadata = shared_1.EMPTY_OBJ, cacheHandlers = false, prefixIdentifiers = false, skipTransformIdentifier = false, renderDataSpread = false, nodeTransforms = [], directiveTransforms = {}, miniProgram = {
  132. class: {
  133. array: true,
  134. },
  135. slot: {
  136. fallbackContent: false,
  137. dynamicSlotNames: true,
  138. },
  139. directive: '',
  140. }, isBuiltInComponent = shared_1.NOOP, isCustomElement = shared_1.NOOP, expressionPlugins = [], onError = defaultOnError, onWarn = defaultOnWarn, }) {
  141. const rootScope = {
  142. id: new identifier_1.default(),
  143. identifiers: [],
  144. properties: [],
  145. parent: null,
  146. };
  147. function findVIfParentScope() {
  148. for (let i = scopes.length - 1; i >= 0; i--) {
  149. const scope = scopes[i];
  150. if (isVForScope(scope) || isRootScope(scope)) {
  151. return scope;
  152. }
  153. }
  154. return rootScope;
  155. }
  156. function createScope(id, initScope) {
  157. return (0, shared_1.extend)({
  158. id,
  159. properties: [],
  160. parent: scopes[scopes.length - 1],
  161. get identifiers() {
  162. return Object.keys(identifiers);
  163. },
  164. }, initScope);
  165. }
  166. const vueIds = [];
  167. const identifiers = Object.create(null);
  168. const scopes = [rootScope];
  169. const miniProgramComponents = (0, uni_cli_shared_1.findMiniProgramUsingComponents)({
  170. filename,
  171. componentsDir: miniProgram.component?.dir,
  172. inputDir: root,
  173. });
  174. // const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/)
  175. const context = {
  176. // options
  177. // 暂不提供根据文件名生成递归组件
  178. selfName: '',
  179. miniProgram,
  180. isTS,
  181. inline,
  182. hashId,
  183. scopeId,
  184. filters,
  185. bindingCssVars,
  186. bindingMetadata,
  187. cacheHandlers,
  188. prefixIdentifiers,
  189. nodeTransforms,
  190. directiveTransforms,
  191. expressionPlugins,
  192. skipTransformIdentifier,
  193. renderDataSpread,
  194. isBuiltInComponent,
  195. isCustomElement,
  196. onError,
  197. onWarn,
  198. // state
  199. parent: null,
  200. childIndex: 0,
  201. helpers: new Map(),
  202. components: new Set(),
  203. imports: [],
  204. bindingComponents: Object.create(null),
  205. cached: 0,
  206. identifiers,
  207. scope: rootScope,
  208. scopes: {
  209. vFor: 0,
  210. vueId: 0,
  211. },
  212. get currentScope() {
  213. return scopes[scopes.length - 1];
  214. },
  215. currentNode: rootNode,
  216. vueIds,
  217. get currentVueId() {
  218. return vueIds[vueIds.length - 1];
  219. },
  220. inVOnce: false,
  221. get inVFor() {
  222. let parent = scopes[scopes.length - 1];
  223. while (parent) {
  224. if (isVForScope(parent) && !isScopedSlotVFor(parent)) {
  225. return true;
  226. }
  227. parent = parent.parent;
  228. }
  229. return false;
  230. },
  231. // methods
  232. getScopeIndex(scope) {
  233. return scopes.indexOf(scope);
  234. },
  235. popScope() {
  236. return scopes.pop();
  237. },
  238. addVIfScope(initScope) {
  239. const vIfScope = createScope(scopes[scopes.length - 1].id, (0, shared_1.extend)(initScope, { parentScope: findVIfParentScope() }));
  240. scopes.push(vIfScope);
  241. return vIfScope;
  242. },
  243. addVForScope(initScope) {
  244. const vForScope = createScope(new identifier_1.default(), initScope);
  245. scopes.push(vForScope);
  246. return vForScope;
  247. },
  248. helper(name) {
  249. const count = context.helpers.get(name) || 0;
  250. context.helpers.set(name, count + 1);
  251. return name;
  252. },
  253. removeHelper(name) {
  254. const count = context.helpers.get(name);
  255. if (count) {
  256. const currentCount = count - 1;
  257. if (!currentCount) {
  258. context.helpers.delete(name);
  259. }
  260. else {
  261. context.helpers.set(name, currentCount);
  262. }
  263. }
  264. },
  265. helperString(name) {
  266. return `_${compiler_core_1.helperNameMap[context.helper(name)]}`;
  267. },
  268. replaceNode(node) {
  269. context.parent.children[context.childIndex] = context.currentNode = node;
  270. },
  271. removeNode(node) {
  272. if (!context.parent) {
  273. throw new Error(`Cannot remove root node.`);
  274. }
  275. const list = context.parent.children;
  276. const removalIndex = node
  277. ? list.indexOf(node)
  278. : context.currentNode
  279. ? context.childIndex
  280. : -1;
  281. /* istanbul ignore if */
  282. if (removalIndex < 0) {
  283. throw new Error(`node being removed is not a child of current parent`);
  284. }
  285. if (!node || node === context.currentNode) {
  286. // current node removed
  287. context.currentNode = null;
  288. context.onNodeRemoved();
  289. }
  290. else {
  291. // sibling node removed
  292. if (context.childIndex > removalIndex) {
  293. context.childIndex--;
  294. context.onNodeRemoved();
  295. }
  296. }
  297. context.parent.children.splice(removalIndex, 1);
  298. },
  299. onNodeRemoved: () => { },
  300. addIdentifiers(exp) {
  301. if ((0, shared_1.isString)(exp)) {
  302. addId(exp);
  303. }
  304. else if (exp.identifiers) {
  305. exp.identifiers.forEach(addId);
  306. }
  307. else if (exp.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */) {
  308. addId(exp.content);
  309. }
  310. },
  311. removeIdentifiers(exp) {
  312. if ((0, shared_1.isString)(exp)) {
  313. removeId(exp);
  314. }
  315. else if (exp.identifiers) {
  316. exp.identifiers.forEach(removeId);
  317. }
  318. else if (exp.type === 4 /* NodeTypes.SIMPLE_EXPRESSION */) {
  319. removeId(exp.content);
  320. }
  321. },
  322. cache(exp, isVNode = false) {
  323. return createCacheExpression(context.cached++, exp, isVNode);
  324. },
  325. isMiniProgramComponent(name) {
  326. return miniProgramComponents[name];
  327. },
  328. rootNode: null,
  329. };
  330. function addId(id) {
  331. const { identifiers } = context;
  332. if (identifiers[id] === undefined) {
  333. identifiers[id] = 0;
  334. }
  335. identifiers[id]++;
  336. }
  337. function removeId(id) {
  338. context.identifiers[id]--;
  339. }
  340. return context;
  341. }
  342. exports.createTransformContext = createTransformContext;
  343. function createCacheExpression(index, value, isVNode = false) {
  344. return {
  345. type: 20 /* NodeTypes.JS_CACHE_EXPRESSION */,
  346. index,
  347. value,
  348. isVNode,
  349. loc: compiler_core_1.locStub,
  350. };
  351. }
  352. function createStructuralDirectiveTransform(name, fn) {
  353. const matches = (0, shared_1.isString)(name)
  354. ? (n) => n === name
  355. : (n) => name.test(n);
  356. return (node, context) => {
  357. if (node.type === 1 /* NodeTypes.ELEMENT */) {
  358. const { props } = node;
  359. // structural directive transforms are not concerned with slots
  360. // as they are handled separately in vSlot.ts
  361. // if (node.tagType === ElementTypes.TEMPLATE && props.some(isVSlot)) {
  362. // return
  363. // }
  364. const exitFns = [];
  365. for (let i = 0; i < props.length; i++) {
  366. const prop = props[i];
  367. if (prop.type === 7 /* NodeTypes.DIRECTIVE */ && matches(prop.name)) {
  368. // structural directives are removed to avoid infinite recursion
  369. // also we remove them *before* applying so that it can further
  370. // traverse itself in case it moves the node around
  371. props.splice(i, 1);
  372. i--;
  373. const onExit = fn(node, prop, context);
  374. if (onExit)
  375. exitFns.push(onExit);
  376. }
  377. }
  378. return exitFns;
  379. }
  380. };
  381. }
  382. exports.createStructuralDirectiveTransform = createStructuralDirectiveTransform;
  383. function createRenderDataExpr(properties, context) {
  384. const objExpr = (0, ast_1.createObjectExpression)(properties);
  385. if (!hasSpreadElement(objExpr)) {
  386. return objExpr;
  387. }
  388. // filters: ['test']
  389. // v-if="text.aa()"
  390. if (context.filters.length) {
  391. transformFilterObjectSpreadExpr(objExpr, context);
  392. }
  393. if (context.renderDataSpread) {
  394. return objExpr;
  395. }
  396. return transformObjectSpreadExpr(objExpr, context);
  397. }
  398. function hasSpreadElement(expr) {
  399. return expr.properties.some((prop) => {
  400. if ((0, types_1.isSpreadElement)(prop)) {
  401. return true;
  402. }
  403. else {
  404. const returnStatement = parseReturnStatement(prop);
  405. if (returnStatement) {
  406. return hasSpreadElement(returnStatement.argument);
  407. }
  408. }
  409. });
  410. }
  411. // 目前硬编码识别 _f,应该读取 context.helperString
  412. const returnObjExprMap = {
  413. _f: 1,
  414. _w: 0, // _w(()=>{return {}})
  415. };
  416. function parseReturnStatement(prop) {
  417. if ((0, types_1.isObjectProperty)(prop) &&
  418. (0, types_1.isCallExpression)(prop.value) &&
  419. (0, types_1.isIdentifier)(prop.value.callee)) {
  420. const { name } = prop.value.callee;
  421. if ((0, shared_1.hasOwn)(returnObjExprMap, name)) {
  422. return prop.value.arguments[returnObjExprMap[name]].body.body[0];
  423. }
  424. }
  425. }
  426. function transformObjectPropertyExpr(prop, context) {
  427. // vFor,withScopedSlot
  428. const returnStatement = parseReturnStatement(prop);
  429. if (returnStatement) {
  430. const objExpr = returnStatement.argument;
  431. if (hasSpreadElement(objExpr)) {
  432. returnStatement.argument = transformObjectSpreadExpr(objExpr, context);
  433. }
  434. }
  435. return prop;
  436. }
  437. function transformObjectSpreadExpr(objExpr, context) {
  438. const properties = objExpr.properties;
  439. const args = [];
  440. let objExprProperties = [];
  441. properties.forEach((prop) => {
  442. if ((0, types_1.isObjectProperty)(prop)) {
  443. objExprProperties.push(transformObjectPropertyExpr(prop, context));
  444. }
  445. else {
  446. if (objExprProperties.length) {
  447. args.push((0, types_1.objectExpression)(objExprProperties));
  448. }
  449. args.push(transformConditionalExpression(prop.argument, context));
  450. objExprProperties = [];
  451. }
  452. });
  453. if (objExprProperties.length) {
  454. args.push((0, types_1.objectExpression)(objExprProperties));
  455. }
  456. if (args.length === 1) {
  457. return args[0];
  458. }
  459. return (0, types_1.callExpression)((0, types_1.identifier)(context.helperString(runtimeHelpers_1.EXTEND)), args);
  460. }
  461. function transformConditionalExpression(expr, context) {
  462. const { consequent, alternate } = expr;
  463. if ((0, types_1.isObjectExpression)(consequent) && hasSpreadElement(consequent)) {
  464. expr.consequent = transformObjectSpreadExpr(consequent, context);
  465. }
  466. if ((0, types_1.isObjectExpression)(alternate)) {
  467. if (hasSpreadElement(alternate)) {
  468. expr.alternate = transformObjectSpreadExpr(alternate, context);
  469. }
  470. }
  471. else if ((0, types_1.isConditionalExpression)(alternate)) {
  472. transformConditionalExpression(alternate, context);
  473. }
  474. return expr;
  475. }
  476. function transformFilterObjectSpreadExpr(objExpr, context) {
  477. const properties = objExpr.properties;
  478. properties.forEach((prop) => {
  479. if ((0, types_1.isObjectProperty)(prop)) {
  480. transformFilterObjectPropertyExpr(prop, context);
  481. }
  482. else {
  483. prop.argument = transformFilterConditionalExpression(prop.argument, context);
  484. }
  485. });
  486. }
  487. function transformFilterObjectPropertyExpr(prop, context) {
  488. // vFor, withScopedSlot
  489. const returnStatement = parseReturnStatement(prop);
  490. if (returnStatement) {
  491. const objExpr = returnStatement.argument;
  492. if (hasSpreadElement(objExpr)) {
  493. transformFilterObjectSpreadExpr(objExpr, context);
  494. }
  495. }
  496. }
  497. function transformFilterConditionalExpression(expr, context) {
  498. const { test, consequent, alternate } = expr;
  499. if ((0, types_1.isObjectExpression)(consequent) && hasSpreadElement(consequent)) {
  500. transformFilterObjectSpreadExpr(consequent, context);
  501. }
  502. if ((0, types_1.isObjectExpression)(alternate)) {
  503. if (hasSpreadElement(alternate)) {
  504. transformFilterObjectSpreadExpr(alternate, context);
  505. }
  506. }
  507. else if ((0, types_1.isConditionalExpression)(alternate)) {
  508. expr.alternate = transformFilterConditionalExpression(alternate, context);
  509. }
  510. const testCode = (0, codegen_1.genBabelExpr)(test);
  511. // filter test
  512. if (context.filters.find((filter) => testCode.includes(filter + '.'))) {
  513. // test.aa() ? {a:1} : {b:2} => {...{a:1},...{b:2}}
  514. const properties = [];
  515. if (!(0, types_1.isObjectExpression)(consequent) || consequent.properties.length) {
  516. properties.push((0, types_1.spreadElement)(consequent));
  517. }
  518. if (!(0, types_1.isObjectExpression)(expr.alternate) ||
  519. expr.alternate.properties.length) {
  520. properties.push((0, types_1.spreadElement)(expr.alternate));
  521. }
  522. return (0, types_1.objectExpression)(properties);
  523. }
  524. return expr;
  525. }