Best JavaScript code snippet using playwright-internal
compiler-dom.global.js
Source:compiler-dom.global.js
...2431 * Validate a non-prefixed expression.2432 * This is only called when using the in-browser runtime compiler since it2433 * doesn't prefix expressions.2434 */2435 function validateBrowserExpression(node, context, asParams = false, asRawStatements = false) {2436 const exp = node.content;2437 // empty expressions are validated per-directive since some directives2438 // do allow empty expressions.2439 if (!exp.trim()) {2440 return;2441 }2442 try {2443 new Function(asRawStatements2444 ? ` ${exp} `2445 : `return ${asParams ? `(${exp}) => {}` : `(${exp})`}`);2446 }2447 catch (e) {2448 let message = e.message;2449 const keywordMatch = exp2450 .replace(stripStringRE, '')2451 .match(prohibitedKeywordRE);2452 if (keywordMatch) {2453 message = `avoid using JavaScript keyword as property name: "${keywordMatch[0]}"`;2454 }2455 context.onError(createCompilerError(43 /* X_INVALID_EXPRESSION */, node.loc, undefined, message));2456 }2457 }2458 const transformExpression = (node, context) => {2459 if (node.type === 5 /* INTERPOLATION */) {2460 node.content = processExpression(node.content, context);2461 }2462 else if (node.type === 1 /* ELEMENT */) {2463 // handle directives on element2464 for (let i = 0; i < node.props.length; i++) {2465 const dir = node.props[i];2466 // do not process for v-on & v-for since they are special handled2467 if (dir.type === 7 /* DIRECTIVE */ && dir.name !== 'for') {2468 const exp = dir.exp;2469 const arg = dir.arg;2470 // do not process exp if this is v-on:arg - we need special handling2471 // for wrapping inline statements.2472 if (exp &&2473 exp.type === 4 /* SIMPLE_EXPRESSION */ &&2474 !(dir.name === 'on' && arg)) {2475 dir.exp = processExpression(exp, context, 2476 // slot args must be processed as function params2477 dir.name === 'slot');2478 }2479 if (arg && arg.type === 4 /* SIMPLE_EXPRESSION */ && !arg.isStatic) {2480 dir.arg = processExpression(arg, context);2481 }2482 }2483 }2484 }2485 };2486 // Important: since this function uses Node.js only dependencies, it should2487 // always be used with a leading !true check so that it can be2488 // tree-shaken from the browser build.2489 function processExpression(node, context, 2490 // some expressions like v-slot props & v-for aliases should be parsed as2491 // function params2492 asParams = false, 2493 // v-on handler values may contain multiple statements2494 asRawStatements = false) {2495 {2496 {2497 // simple in-browser validation (same logic in 2.x)2498 validateBrowserExpression(node, context, asParams, asRawStatements);2499 }2500 return node;2501 }2502 }2503 const transformIf = createStructuralDirectiveTransform(/^(if|else|else-if)$/, (node, dir, context) => {2504 return processIf(node, dir, context, (ifNode, branch, isRoot) => {2505 // #1587: We need to dynamically increment the key based on the current2506 // node's sibling nodes, since chained v-if/else branches are2507 // rendered at the same depth2508 const siblings = context.parent.children;2509 let i = siblings.indexOf(ifNode);2510 let key = 0;2511 while (i-- >= 0) {2512 const sibling = siblings[i];2513 if (sibling && sibling.type === 9 /* IF */) {2514 key += sibling.branches.length;2515 }2516 }2517 // Exit callback. Complete the codegenNode when all children have been2518 // transformed.2519 return () => {2520 if (isRoot) {2521 ifNode.codegenNode = createCodegenNodeForBranch(branch, key, context);2522 }2523 else {2524 // attach this branch's codegen node to the v-if root.2525 const parentCondition = getParentCondition(ifNode.codegenNode);2526 parentCondition.alternate = createCodegenNodeForBranch(branch, key + ifNode.branches.length - 1, context);2527 }2528 };2529 });2530 });2531 // target-agnostic transform used for both Client and SSR2532 function processIf(node, dir, context, processCodegen) {2533 if (dir.name !== 'else' &&2534 (!dir.exp || !dir.exp.content.trim())) {2535 const loc = dir.exp ? dir.exp.loc : node.loc;2536 context.onError(createCompilerError(27 /* X_V_IF_NO_EXPRESSION */, dir.loc));2537 dir.exp = createSimpleExpression(`true`, false, loc);2538 }2539 if ( dir.exp) {2540 validateBrowserExpression(dir.exp, context);2541 }2542 if (dir.name === 'if') {2543 const branch = createIfBranch(node, dir);2544 const ifNode = {2545 type: 9 /* IF */,2546 loc: node.loc,2547 branches: [branch]2548 };2549 context.replaceNode(ifNode);2550 if (processCodegen) {2551 return processCodegen(ifNode, branch, true);2552 }2553 }2554 else {2555 // locate the adjacent v-if2556 const siblings = context.parent.children;2557 const comments = [];2558 let i = siblings.indexOf(node);2559 while (i-- >= -1) {2560 const sibling = siblings[i];2561 if ( sibling && sibling.type === 3 /* COMMENT */) {2562 context.removeNode(sibling);2563 comments.unshift(sibling);2564 continue;2565 }2566 if (sibling &&2567 sibling.type === 2 /* TEXT */ &&2568 !sibling.content.trim().length) {2569 context.removeNode(sibling);2570 continue;2571 }2572 if (sibling && sibling.type === 9 /* IF */) {2573 // move the node to the if node's branches2574 context.removeNode();2575 const branch = createIfBranch(node, dir);2576 if ( comments.length) {2577 branch.children = [...comments, ...branch.children];2578 }2579 // check if user is forcing same key on different branches2580 {2581 const key = branch.userKey;2582 if (key) {2583 sibling.branches.forEach(({ userKey }) => {2584 if (isSameKey(userKey, key)) {2585 context.onError(createCompilerError(28 /* X_V_IF_SAME_KEY */, branch.userKey.loc));2586 }2587 });2588 }2589 }2590 sibling.branches.push(branch);2591 const onExit = processCodegen && processCodegen(sibling, branch, false);2592 // since the branch was removed, it will not be traversed.2593 // make sure to traverse here.2594 traverseNode(branch, context);2595 // call on exit2596 if (onExit)2597 onExit();2598 // make sure to reset currentNode after traversal to indicate this2599 // node has been removed.2600 context.currentNode = null;2601 }2602 else {2603 context.onError(createCompilerError(29 /* X_V_ELSE_NO_ADJACENT_IF */, node.loc));2604 }2605 break;2606 }2607 }2608 }2609 function createIfBranch(node, dir) {2610 return {2611 type: 10 /* IF_BRANCH */,2612 loc: node.loc,2613 condition: dir.name === 'else' ? undefined : dir.exp,2614 children: node.tagType === 3 /* TEMPLATE */ && !findDir(node, 'for')2615 ? node.children2616 : [node],2617 userKey: findProp(node, `key`)2618 };2619 }2620 function createCodegenNodeForBranch(branch, keyIndex, context) {2621 if (branch.condition) {2622 return createConditionalExpression(branch.condition, createChildrenCodegenNode(branch, keyIndex, context), 2623 // make sure to pass in asBlock: true so that the comment node call2624 // closes the current block.2625 createCallExpression(context.helper(CREATE_COMMENT), [2626 '"v-if"' ,2627 'true'2628 ]));2629 }2630 else {2631 return createChildrenCodegenNode(branch, keyIndex, context);2632 }2633 }2634 function createChildrenCodegenNode(branch, keyIndex, context) {2635 const { helper } = context;2636 const keyProperty = createObjectProperty(`key`, createSimpleExpression(`${keyIndex}`, false, locStub, 2 /* CAN_HOIST */));2637 const { children } = branch;2638 const firstChild = children[0];2639 const needFragmentWrapper = children.length !== 1 || firstChild.type !== 1 /* ELEMENT */;2640 if (needFragmentWrapper) {2641 if (children.length === 1 && firstChild.type === 11 /* FOR */) {2642 // optimize away nested fragments when child is a ForNode2643 const vnodeCall = firstChild.codegenNode;2644 injectProp(vnodeCall, keyProperty, context);2645 return vnodeCall;2646 }2647 else {2648 return createVNodeCall(context, helper(FRAGMENT), createObjectExpression([keyProperty]), children, 64 /* STABLE_FRAGMENT */ +2649 ( ` /* ${PatchFlagNames[64 /* STABLE_FRAGMENT */]} */`2650 ), undefined, undefined, true, false, branch.loc);2651 }2652 }2653 else {2654 const vnodeCall = firstChild2655 .codegenNode;2656 // Change createVNode to createBlock.2657 if (vnodeCall.type === 13 /* VNODE_CALL */) {2658 vnodeCall.isBlock = true;2659 helper(OPEN_BLOCK);2660 helper(CREATE_BLOCK);2661 }2662 // inject branch key2663 injectProp(vnodeCall, keyProperty, context);2664 return vnodeCall;2665 }2666 }2667 function isSameKey(a, b) {2668 if (!a || a.type !== b.type) {2669 return false;2670 }2671 if (a.type === 6 /* ATTRIBUTE */) {2672 if (a.value.content !== b.value.content) {2673 return false;2674 }2675 }2676 else {2677 // directive2678 const exp = a.exp;2679 const branchExp = b.exp;2680 if (exp.type !== branchExp.type) {2681 return false;2682 }2683 if (exp.type !== 4 /* SIMPLE_EXPRESSION */ ||2684 (exp.isStatic !== branchExp.isStatic ||2685 exp.content !== branchExp.content)) {2686 return false;2687 }2688 }2689 return true;2690 }2691 function getParentCondition(node) {2692 while (true) {2693 if (node.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {2694 if (node.alternate.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {2695 node = node.alternate;2696 }2697 else {2698 return node;2699 }2700 }2701 else if (node.type === 20 /* JS_CACHE_EXPRESSION */) {2702 node = node.value;2703 }2704 }2705 }2706 const transformFor = createStructuralDirectiveTransform('for', (node, dir, context) => {2707 const { helper } = context;2708 return processFor(node, dir, context, forNode => {2709 // create the loop render function expression now, and add the2710 // iterator on exit after all children have been traversed2711 const renderExp = createCallExpression(helper(RENDER_LIST), [2712 forNode.source2713 ]);2714 const keyProp = findProp(node, `key`);2715 const keyProperty = keyProp2716 ? createObjectProperty(`key`, keyProp.type === 6 /* ATTRIBUTE */2717 ? createSimpleExpression(keyProp.value.content, true)2718 : keyProp.exp)2719 : null;2720 const isStableFragment = forNode.source.type === 4 /* SIMPLE_EXPRESSION */ &&2721 forNode.source.constType > 0;2722 const fragmentFlag = isStableFragment2723 ? 64 /* STABLE_FRAGMENT */2724 : keyProp2725 ? 128 /* KEYED_FRAGMENT */2726 : 256 /* UNKEYED_FRAGMENT */;2727 forNode.codegenNode = createVNodeCall(context, helper(FRAGMENT), undefined, renderExp, fragmentFlag +2728 ( ` /* ${PatchFlagNames[fragmentFlag]} */` ), undefined, undefined, true /* isBlock */, !isStableFragment /* disableTracking */, node.loc);2729 return () => {2730 // finish the codegen now that all children have been traversed2731 let childBlock;2732 const isTemplate = isTemplateNode(node);2733 const { children } = forNode;2734 // check <template v-for> key placement2735 if ( isTemplate) {2736 node.children.some(c => {2737 if (c.type === 1 /* ELEMENT */) {2738 const key = findProp(c, 'key');2739 if (key) {2740 context.onError(createCompilerError(32 /* X_V_FOR_TEMPLATE_KEY_PLACEMENT */, key.loc));2741 return true;2742 }2743 }2744 });2745 }2746 const needFragmentWrapper = children.length !== 1 || children[0].type !== 1 /* ELEMENT */;2747 const slotOutlet = isSlotOutlet(node)2748 ? node2749 : isTemplate &&2750 node.children.length === 1 &&2751 isSlotOutlet(node.children[0])2752 ? node.children[0] // api-extractor somehow fails to infer this2753 : null;2754 if (slotOutlet) {2755 // <slot v-for="..."> or <template v-for="..."><slot/></template>2756 childBlock = slotOutlet.codegenNode;2757 if (isTemplate && keyProperty) {2758 // <template v-for="..." :key="..."><slot/></template>2759 // we need to inject the key to the renderSlot() call.2760 // the props for renderSlot is passed as the 3rd argument.2761 injectProp(childBlock, keyProperty, context);2762 }2763 }2764 else if (needFragmentWrapper) {2765 // <template v-for="..."> with text or multi-elements2766 // should generate a fragment block for each loop2767 childBlock = createVNodeCall(context, helper(FRAGMENT), keyProperty ? createObjectExpression([keyProperty]) : undefined, node.children, 64 /* STABLE_FRAGMENT */ +2768 ( ` /* ${PatchFlagNames[64 /* STABLE_FRAGMENT */]} */`2769 ), undefined, undefined, true);2770 }2771 else {2772 // Normal element v-for. Directly use the child's codegenNode2773 // but mark it as a block.2774 childBlock = children[0]2775 .codegenNode;2776 if (isTemplate && keyProperty) {2777 injectProp(childBlock, keyProperty, context);2778 }2779 childBlock.isBlock = !isStableFragment;2780 if (childBlock.isBlock) {2781 helper(OPEN_BLOCK);2782 helper(CREATE_BLOCK);2783 }2784 else {2785 helper(CREATE_VNODE);2786 }2787 }2788 renderExp.arguments.push(createFunctionExpression(createForLoopParams(forNode.parseResult), childBlock, true /* force newline */));2789 };2790 });2791 });2792 // target-agnostic transform used for both Client and SSR2793 function processFor(node, dir, context, processCodegen) {2794 if (!dir.exp) {2795 context.onError(createCompilerError(30 /* X_V_FOR_NO_EXPRESSION */, dir.loc));2796 return;2797 }2798 const parseResult = parseForExpression(2799 // can only be simple expression because vFor transform is applied2800 // before expression transform.2801 dir.exp, context);2802 if (!parseResult) {2803 context.onError(createCompilerError(31 /* X_V_FOR_MALFORMED_EXPRESSION */, dir.loc));2804 return;2805 }2806 const { addIdentifiers, removeIdentifiers, scopes } = context;2807 const { source, value, key, index } = parseResult;2808 const forNode = {2809 type: 11 /* FOR */,2810 loc: dir.loc,2811 source,2812 valueAlias: value,2813 keyAlias: key,2814 objectIndexAlias: index,2815 parseResult,2816 children: isTemplateNode(node) ? node.children : [node]2817 };2818 context.replaceNode(forNode);2819 // bookkeeping2820 scopes.vFor++;2821 const onExit = processCodegen && processCodegen(forNode);2822 return () => {2823 scopes.vFor--;2824 if (onExit)2825 onExit();2826 };2827 }2828 const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/;2829 // This regex doesn't cover the case if key or index aliases have destructuring,2830 // but those do not make sense in the first place, so this works in practice.2831 const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/;2832 const stripParensRE = /^\(|\)$/g;2833 function parseForExpression(input, context) {2834 const loc = input.loc;2835 const exp = input.content;2836 const inMatch = exp.match(forAliasRE);2837 if (!inMatch)2838 return;2839 const [, LHS, RHS] = inMatch;2840 const result = {2841 source: createAliasExpression(loc, RHS.trim(), exp.indexOf(RHS, LHS.length)),2842 value: undefined,2843 key: undefined,2844 index: undefined2845 };2846 {2847 validateBrowserExpression(result.source, context);2848 }2849 let valueContent = LHS.trim()2850 .replace(stripParensRE, '')2851 .trim();2852 const trimmedOffset = LHS.indexOf(valueContent);2853 const iteratorMatch = valueContent.match(forIteratorRE);2854 if (iteratorMatch) {2855 valueContent = valueContent.replace(forIteratorRE, '').trim();2856 const keyContent = iteratorMatch[1].trim();2857 let keyOffset;2858 if (keyContent) {2859 keyOffset = exp.indexOf(keyContent, trimmedOffset + valueContent.length);2860 result.key = createAliasExpression(loc, keyContent, keyOffset);2861 {2862 validateBrowserExpression(result.key, context, true);2863 }2864 }2865 if (iteratorMatch[2]) {2866 const indexContent = iteratorMatch[2].trim();2867 if (indexContent) {2868 result.index = createAliasExpression(loc, indexContent, exp.indexOf(indexContent, result.key2869 ? keyOffset + keyContent.length2870 : trimmedOffset + valueContent.length));2871 {2872 validateBrowserExpression(result.index, context, true);2873 }2874 }2875 }2876 }2877 if (valueContent) {2878 result.value = createAliasExpression(loc, valueContent, trimmedOffset);2879 {2880 validateBrowserExpression(result.value, context, true);2881 }2882 }2883 return result;2884 }2885 function createAliasExpression(range, content, offset) {2886 return createSimpleExpression(content, false, getInnerRange(range, offset, content.length));2887 }2888 function createForLoopParams({ value, key, index }) {2889 const params = [];2890 if (value) {2891 params.push(value);2892 }2893 if (key) {2894 if (!value) {2895 params.push(createSimpleExpression(`_`, false));2896 }2897 params.push(key);2898 }2899 if (index) {2900 if (!key) {2901 if (!value) {2902 params.push(createSimpleExpression(`_`, false));2903 }2904 params.push(createSimpleExpression(`__`, false));2905 }2906 params.push(index);2907 }2908 return params;2909 }2910 const defaultFallback = createSimpleExpression(`undefined`, false);2911 // A NodeTransform that:2912 // 1. Tracks scope identifiers for scoped slots so that they don't get prefixed2913 // by transformExpression. This is only applied in non-browser builds with2914 // { prefixIdentifiers: true }.2915 // 2. Track v-slot depths so that we know a slot is inside another slot.2916 // Note the exit callback is executed before buildSlots() on the same node,2917 // so only nested slots see positive numbers.2918 const trackSlotScopes = (node, context) => {2919 if (node.type === 1 /* ELEMENT */ &&2920 (node.tagType === 1 /* COMPONENT */ ||2921 node.tagType === 3 /* TEMPLATE */)) {2922 // We are only checking non-empty v-slot here2923 // since we only care about slots that introduce scope variables.2924 const vSlot = findDir(node, 'slot');2925 if (vSlot) {2926 const slotProps = vSlot.exp;2927 context.scopes.vSlot++;2928 return () => {2929 context.scopes.vSlot--;2930 };2931 }2932 }2933 };2934 // A NodeTransform that tracks scope identifiers for scoped slots with v-for.2935 // This transform is only applied in non-browser builds with { prefixIdentifiers: true }2936 const trackVForSlotScopes = (node, context) => {2937 let vFor;2938 if (isTemplateNode(node) &&2939 node.props.some(isVSlot) &&2940 (vFor = findDir(node, 'for'))) {2941 const result = (vFor.parseResult = parseForExpression(vFor.exp, context));2942 if (result) {2943 const { value, key, index } = result;2944 const { addIdentifiers, removeIdentifiers } = context;2945 value && addIdentifiers(value);2946 key && addIdentifiers(key);2947 index && addIdentifiers(index);2948 return () => {2949 value && removeIdentifiers(value);2950 key && removeIdentifiers(key);2951 index && removeIdentifiers(index);2952 };2953 }2954 }2955 };2956 const buildClientSlotFn = (props, children, loc) => createFunctionExpression(props, children, false /* newline */, true /* isSlot */, children.length ? children[0].loc : loc);2957 // Instead of being a DirectiveTransform, v-slot processing is called during2958 // transformElement to build the slots object for a component.2959 function buildSlots(node, context, buildSlotFn = buildClientSlotFn) {2960 context.helper(WITH_CTX);2961 const { children, loc } = node;2962 const slotsProperties = [];2963 const dynamicSlots = [];2964 const buildDefaultSlotProperty = (props, children) => createObjectProperty(`default`, buildSlotFn(props, children, loc));2965 // If the slot is inside a v-for or another v-slot, force it to be dynamic2966 // since it likely uses a scope variable.2967 let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0;2968 // 1. Check for slot with slotProps on component itself.2969 // <Comp v-slot="{ prop }"/>2970 const onComponentSlot = findDir(node, 'slot', true);2971 if (onComponentSlot) {2972 const { arg, exp } = onComponentSlot;2973 if (arg && !isStaticExp(arg)) {2974 hasDynamicSlots = true;2975 }2976 slotsProperties.push(createObjectProperty(arg || createSimpleExpression('default', true), buildSlotFn(exp, children, loc)));2977 }2978 // 2. Iterate through children and check for template slots2979 // <template v-slot:foo="{ prop }">2980 let hasTemplateSlots = false;2981 let hasNamedDefaultSlot = false;2982 const implicitDefaultChildren = [];2983 const seenSlotNames = new Set();2984 for (let i = 0; i < children.length; i++) {2985 const slotElement = children[i];2986 let slotDir;2987 if (!isTemplateNode(slotElement) ||2988 !(slotDir = findDir(slotElement, 'slot', true))) {2989 // not a <template v-slot>, skip.2990 if (slotElement.type !== 3 /* COMMENT */) {2991 implicitDefaultChildren.push(slotElement);2992 }2993 continue;2994 }2995 if (onComponentSlot) {2996 // already has on-component slot - this is incorrect usage.2997 context.onError(createCompilerError(36 /* X_V_SLOT_MIXED_SLOT_USAGE */, slotDir.loc));2998 break;2999 }3000 hasTemplateSlots = true;3001 const { children: slotChildren, loc: slotLoc } = slotElement;3002 const { arg: slotName = createSimpleExpression(`default`, true), exp: slotProps, loc: dirLoc } = slotDir;3003 // check if name is dynamic.3004 let staticSlotName;3005 if (isStaticExp(slotName)) {3006 staticSlotName = slotName ? slotName.content : `default`;3007 }3008 else {3009 hasDynamicSlots = true;3010 }3011 const slotFunction = buildSlotFn(slotProps, slotChildren, slotLoc);3012 // check if this slot is conditional (v-if/v-for)3013 let vIf;3014 let vElse;3015 let vFor;3016 if ((vIf = findDir(slotElement, 'if'))) {3017 hasDynamicSlots = true;3018 dynamicSlots.push(createConditionalExpression(vIf.exp, buildDynamicSlot(slotName, slotFunction), defaultFallback));3019 }3020 else if ((vElse = findDir(slotElement, /^else(-if)?$/, true /* allowEmpty */))) {3021 // find adjacent v-if3022 let j = i;3023 let prev;3024 while (j--) {3025 prev = children[j];3026 if (prev.type !== 3 /* COMMENT */) {3027 break;3028 }3029 }3030 if (prev && isTemplateNode(prev) && findDir(prev, 'if')) {3031 // remove node3032 children.splice(i, 1);3033 i--;3034 // attach this slot to previous conditional3035 let conditional = dynamicSlots[dynamicSlots.length - 1];3036 while (conditional.alternate.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {3037 conditional = conditional.alternate;3038 }3039 conditional.alternate = vElse.exp3040 ? createConditionalExpression(vElse.exp, buildDynamicSlot(slotName, slotFunction), defaultFallback)3041 : buildDynamicSlot(slotName, slotFunction);3042 }3043 else {3044 context.onError(createCompilerError(29 /* X_V_ELSE_NO_ADJACENT_IF */, vElse.loc));3045 }3046 }3047 else if ((vFor = findDir(slotElement, 'for'))) {3048 hasDynamicSlots = true;3049 const parseResult = vFor.parseResult ||3050 parseForExpression(vFor.exp, context);3051 if (parseResult) {3052 // Render the dynamic slots as an array and add it to the createSlot()3053 // args. The runtime knows how to handle it appropriately.3054 dynamicSlots.push(createCallExpression(context.helper(RENDER_LIST), [3055 parseResult.source,3056 createFunctionExpression(createForLoopParams(parseResult), buildDynamicSlot(slotName, slotFunction), true /* force newline */)3057 ]));3058 }3059 else {3060 context.onError(createCompilerError(31 /* X_V_FOR_MALFORMED_EXPRESSION */, vFor.loc));3061 }3062 }3063 else {3064 // check duplicate static names3065 if (staticSlotName) {3066 if (seenSlotNames.has(staticSlotName)) {3067 context.onError(createCompilerError(37 /* X_V_SLOT_DUPLICATE_SLOT_NAMES */, dirLoc));3068 continue;3069 }3070 seenSlotNames.add(staticSlotName);3071 if (staticSlotName === 'default') {3072 hasNamedDefaultSlot = true;3073 }3074 }3075 slotsProperties.push(createObjectProperty(slotName, slotFunction));3076 }3077 }3078 if (!onComponentSlot) {3079 if (!hasTemplateSlots) {3080 // implicit default slot (on component)3081 slotsProperties.push(buildDefaultSlotProperty(undefined, children));3082 }3083 else if (implicitDefaultChildren.length) {3084 // implicit default slot (mixed with named slots)3085 if (hasNamedDefaultSlot) {3086 context.onError(createCompilerError(38 /* X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN */, implicitDefaultChildren[0].loc));3087 }3088 else {3089 slotsProperties.push(buildDefaultSlotProperty(undefined, implicitDefaultChildren));3090 }3091 }3092 }3093 const slotFlag = hasDynamicSlots3094 ? 2 /* DYNAMIC */3095 : hasForwardedSlots(node.children)3096 ? 3 /* FORWARDED */3097 : 1 /* STABLE */;3098 let slots = createObjectExpression(slotsProperties.concat(createObjectProperty(`_`, 3099 // 2 = compiled but dynamic = can skip normalization, but must run diff3100 // 1 = compiled and static = can skip normalization AND diff as optimized3101 createSimpleExpression(slotFlag + ( ` /* ${slotFlagsText[slotFlag]} */` ), false))), loc);3102 if (dynamicSlots.length) {3103 slots = createCallExpression(context.helper(CREATE_SLOTS), [3104 slots,3105 createArrayExpression(dynamicSlots)3106 ]);3107 }3108 return {3109 slots,3110 hasDynamicSlots3111 };3112 }3113 function buildDynamicSlot(name, fn) {3114 return createObjectExpression([3115 createObjectProperty(`name`, name),3116 createObjectProperty(`fn`, fn)3117 ]);3118 }3119 function hasForwardedSlots(children) {3120 for (let i = 0; i < children.length; i++) {3121 const child = children[i];3122 if (child.type === 1 /* ELEMENT */) {3123 if (child.tagType === 2 /* SLOT */ ||3124 (child.tagType === 0 /* ELEMENT */ &&3125 hasForwardedSlots(child.children))) {3126 return true;3127 }3128 }3129 }3130 return false;3131 }3132 // some directive transforms (e.g. v-model) may return a symbol for runtime3133 // import, which should be used instead of a resolveDirective call.3134 const directiveImportMap = new WeakMap();3135 // generate a JavaScript AST for this element's codegen3136 const transformElement = (node, context) => {3137 if (!(node.type === 1 /* ELEMENT */ &&3138 (node.tagType === 0 /* ELEMENT */ ||3139 node.tagType === 1 /* COMPONENT */))) {3140 return;3141 }3142 // perform the work on exit, after all child expressions have been3143 // processed and merged.3144 return function postTransformElement() {3145 const { tag, props } = node;3146 const isComponent = node.tagType === 1 /* COMPONENT */;3147 // The goal of the transform is to create a codegenNode implementing the3148 // VNodeCall interface.3149 const vnodeTag = isComponent3150 ? resolveComponentType(node, context)3151 : `"${tag}"`;3152 const isDynamicComponent = isObject(vnodeTag) && vnodeTag.callee === RESOLVE_DYNAMIC_COMPONENT;3153 let vnodeProps;3154 let vnodeChildren;3155 let vnodePatchFlag;3156 let patchFlag = 0;3157 let vnodeDynamicProps;3158 let dynamicPropNames;3159 let vnodeDirectives;3160 let shouldUseBlock = 3161 // dynamic component may resolve to plain elements3162 isDynamicComponent ||3163 vnodeTag === TELEPORT ||3164 vnodeTag === SUSPENSE ||3165 (!isComponent &&3166 // <svg> and <foreignObject> must be forced into blocks so that block3167 // updates inside get proper isSVG flag at runtime. (#639, #643)3168 // This is technically web-specific, but splitting the logic out of core3169 // leads to too much unnecessary complexity.3170 (tag === 'svg' ||3171 tag === 'foreignObject' ||3172 // #938: elements with dynamic keys should be forced into blocks3173 findProp(node, 'key', true)));3174 // props3175 if (props.length > 0) {3176 const propsBuildResult = buildProps(node, context);3177 vnodeProps = propsBuildResult.props;3178 patchFlag = propsBuildResult.patchFlag;3179 dynamicPropNames = propsBuildResult.dynamicPropNames;3180 const directives = propsBuildResult.directives;3181 vnodeDirectives =3182 directives && directives.length3183 ? createArrayExpression(directives.map(dir => buildDirectiveArgs(dir, context)))3184 : undefined;3185 }3186 // children3187 if (node.children.length > 0) {3188 if (vnodeTag === KEEP_ALIVE) {3189 // Although a built-in component, we compile KeepAlive with raw children3190 // instead of slot functions so that it can be used inside Transition3191 // or other Transition-wrapping HOCs.3192 // To ensure correct updates with block optimizations, we need to:3193 // 1. Force keep-alive into a block. This avoids its children being3194 // collected by a parent block.3195 shouldUseBlock = true;3196 // 2. Force keep-alive to always be updated, since it uses raw children.3197 patchFlag |= 1024 /* DYNAMIC_SLOTS */;3198 if ( node.children.length > 1) {3199 context.onError(createCompilerError(44 /* X_KEEP_ALIVE_INVALID_CHILDREN */, {3200 start: node.children[0].loc.start,3201 end: node.children[node.children.length - 1].loc.end,3202 source: ''3203 }));3204 }3205 }3206 const shouldBuildAsSlots = isComponent &&3207 // Teleport is not a real component and has dedicated runtime handling3208 vnodeTag !== TELEPORT &&3209 // explained above.3210 vnodeTag !== KEEP_ALIVE;3211 if (shouldBuildAsSlots) {3212 const { slots, hasDynamicSlots } = buildSlots(node, context);3213 vnodeChildren = slots;3214 if (hasDynamicSlots) {3215 patchFlag |= 1024 /* DYNAMIC_SLOTS */;3216 }3217 }3218 else if (node.children.length === 1 && vnodeTag !== TELEPORT) {3219 const child = node.children[0];3220 const type = child.type;3221 // check for dynamic text children3222 const hasDynamicTextChild = type === 5 /* INTERPOLATION */ ||3223 type === 8 /* COMPOUND_EXPRESSION */;3224 if (hasDynamicTextChild &&3225 getConstantType(child, context) === 0 /* NOT_CONSTANT */) {3226 patchFlag |= 1 /* TEXT */;3227 }3228 // pass directly if the only child is a text node3229 // (plain / interpolation / expression)3230 if (hasDynamicTextChild || type === 2 /* TEXT */) {3231 vnodeChildren = child;3232 }3233 else {3234 vnodeChildren = node.children;3235 }3236 }3237 else {3238 vnodeChildren = node.children;3239 }3240 }3241 // patchFlag & dynamicPropNames3242 if (patchFlag !== 0) {3243 {3244 if (patchFlag < 0) {3245 // special flags (negative and mutually exclusive)3246 vnodePatchFlag = patchFlag + ` /* ${PatchFlagNames[patchFlag]} */`;3247 }3248 else {3249 // bitwise flags3250 const flagNames = Object.keys(PatchFlagNames)3251 .map(Number)3252 .filter(n => n > 0 && patchFlag & n)3253 .map(n => PatchFlagNames[n])3254 .join(`, `);3255 vnodePatchFlag = patchFlag + ` /* ${flagNames} */`;3256 }3257 }3258 if (dynamicPropNames && dynamicPropNames.length) {3259 vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames);3260 }3261 }3262 node.codegenNode = createVNodeCall(context, vnodeTag, vnodeProps, vnodeChildren, vnodePatchFlag, vnodeDynamicProps, vnodeDirectives, !!shouldUseBlock, false /* disableTracking */, node.loc);3263 };3264 };3265 function resolveComponentType(node, context, ssr = false) {3266 const { tag } = node;3267 // 1. dynamic component3268 const isProp = node.tag === 'component' ? findProp(node, 'is') : findDir(node, 'is');3269 if (isProp) {3270 const exp = isProp.type === 6 /* ATTRIBUTE */3271 ? isProp.value && createSimpleExpression(isProp.value.content, true)3272 : isProp.exp;3273 if (exp) {3274 return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [3275 exp3276 ]);3277 }3278 }3279 // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)3280 const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);3281 if (builtIn) {3282 // built-ins are simply fallthroughs / have special handling during ssr3283 // so we don't need to import their runtime equivalents3284 if (!ssr)3285 context.helper(builtIn);3286 return builtIn;3287 }3288 // 5. user component (resolve)3289 context.helper(RESOLVE_COMPONENT);3290 context.components.add(tag);3291 return toValidAssetId(tag, `component`);3292 }3293 function buildProps(node, context, props = node.props, ssr = false) {3294 const { tag, loc: elementLoc } = node;3295 const isComponent = node.tagType === 1 /* COMPONENT */;3296 let properties = [];3297 const mergeArgs = [];3298 const runtimeDirectives = [];3299 // patchFlag analysis3300 let patchFlag = 0;3301 let hasRef = false;3302 let hasClassBinding = false;3303 let hasStyleBinding = false;3304 let hasHydrationEventBinding = false;3305 let hasDynamicKeys = false;3306 let hasVnodeHook = false;3307 const dynamicPropNames = [];3308 const analyzePatchFlag = ({ key, value }) => {3309 if (isStaticExp(key)) {3310 const name = key.content;3311 const isEventHandler = isOn(name);3312 if (!isComponent &&3313 isEventHandler &&3314 // omit the flag for click handlers because hydration gives click3315 // dedicated fast path.3316 name.toLowerCase() !== 'onclick' &&3317 // omit v-model handlers3318 name !== 'onUpdate:modelValue' &&3319 // omit onVnodeXXX hooks3320 !isReservedProp(name)) {3321 hasHydrationEventBinding = true;3322 }3323 if (isEventHandler && isReservedProp(name)) {3324 hasVnodeHook = true;3325 }3326 if (value.type === 20 /* JS_CACHE_EXPRESSION */ ||3327 ((value.type === 4 /* SIMPLE_EXPRESSION */ ||3328 value.type === 8 /* COMPOUND_EXPRESSION */) &&3329 getConstantType(value, context) > 0)) {3330 // skip if the prop is a cached handler or has constant value3331 return;3332 }3333 if (name === 'ref') {3334 hasRef = true;3335 }3336 else if (name === 'class' && !isComponent) {3337 hasClassBinding = true;3338 }3339 else if (name === 'style' && !isComponent) {3340 hasStyleBinding = true;3341 }3342 else if (name !== 'key' && !dynamicPropNames.includes(name)) {3343 dynamicPropNames.push(name);3344 }3345 }3346 else {3347 hasDynamicKeys = true;3348 }3349 };3350 for (let i = 0; i < props.length; i++) {3351 // static attribute3352 const prop = props[i];3353 if (prop.type === 6 /* ATTRIBUTE */) {3354 const { loc, name, value } = prop;3355 let isStatic = true;3356 if (name === 'ref') {3357 hasRef = true;3358 }3359 // skip :is on <component>3360 if (name === 'is' && tag === 'component') {3361 continue;3362 }3363 properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', isStatic, value ? value.loc : loc)));3364 }3365 else {3366 // directives3367 const { name, arg, exp, loc } = prop;3368 const isBind = name === 'bind';3369 const isOn = name === 'on';3370 // skip v-slot - it is handled by its dedicated transform.3371 if (name === 'slot') {3372 if (!isComponent) {3373 context.onError(createCompilerError(39 /* X_V_SLOT_MISPLACED */, loc));3374 }3375 continue;3376 }3377 // skip v-once - it is handled by its dedicated transform.3378 if (name === 'once') {3379 continue;3380 }3381 // skip v-is and :is on <component>3382 if (name === 'is' ||3383 (isBind && tag === 'component' && isBindKey(arg, 'is'))) {3384 continue;3385 }3386 // skip v-on in SSR compilation3387 if (isOn && ssr) {3388 continue;3389 }3390 // special case for v-bind and v-on with no argument3391 if (!arg && (isBind || isOn)) {3392 hasDynamicKeys = true;3393 if (exp) {3394 if (properties.length) {3395 mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));3396 properties = [];3397 }3398 if (isBind) {3399 mergeArgs.push(exp);3400 }3401 else {3402 // v-on="obj" -> toHandlers(obj)3403 mergeArgs.push({3404 type: 14 /* JS_CALL_EXPRESSION */,3405 loc,3406 callee: context.helper(TO_HANDLERS),3407 arguments: [exp]3408 });3409 }3410 }3411 else {3412 context.onError(createCompilerError(isBind3413 ? 33 /* X_V_BIND_NO_EXPRESSION */3414 : 34 /* X_V_ON_NO_EXPRESSION */, loc));3415 }3416 continue;3417 }3418 const directiveTransform = context.directiveTransforms[name];3419 if (directiveTransform) {3420 // has built-in directive transform.3421 const { props, needRuntime } = directiveTransform(prop, node, context);3422 !ssr && props.forEach(analyzePatchFlag);3423 properties.push(...props);3424 if (needRuntime) {3425 runtimeDirectives.push(prop);3426 if (isSymbol(needRuntime)) {3427 directiveImportMap.set(prop, needRuntime);3428 }3429 }3430 }3431 else {3432 // no built-in transform, this is a user custom directive.3433 runtimeDirectives.push(prop);3434 }3435 }3436 }3437 let propsExpression = undefined;3438 // has v-bind="object" or v-on="object", wrap with mergeProps3439 if (mergeArgs.length) {3440 if (properties.length) {3441 mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));3442 }3443 if (mergeArgs.length > 1) {3444 propsExpression = createCallExpression(context.helper(MERGE_PROPS), mergeArgs, elementLoc);3445 }3446 else {3447 // single v-bind with nothing else - no need for a mergeProps call3448 propsExpression = mergeArgs[0];3449 }3450 }3451 else if (properties.length) {3452 propsExpression = createObjectExpression(dedupeProperties(properties), elementLoc);3453 }3454 // patchFlag analysis3455 if (hasDynamicKeys) {3456 patchFlag |= 16 /* FULL_PROPS */;3457 }3458 else {3459 if (hasClassBinding) {3460 patchFlag |= 2 /* CLASS */;3461 }3462 if (hasStyleBinding) {3463 patchFlag |= 4 /* STYLE */;3464 }3465 if (dynamicPropNames.length) {3466 patchFlag |= 8 /* PROPS */;3467 }3468 if (hasHydrationEventBinding) {3469 patchFlag |= 32 /* HYDRATE_EVENTS */;3470 }3471 }3472 if ((patchFlag === 0 || patchFlag === 32 /* HYDRATE_EVENTS */) &&3473 (hasRef || hasVnodeHook || runtimeDirectives.length > 0)) {3474 patchFlag |= 512 /* NEED_PATCH */;3475 }3476 return {3477 props: propsExpression,3478 directives: runtimeDirectives,3479 patchFlag,3480 dynamicPropNames3481 };3482 }3483 // Dedupe props in an object literal.3484 // Literal duplicated attributes would have been warned during the parse phase,3485 // however, it's possible to encounter duplicated `onXXX` handlers with different3486 // modifiers. We also need to merge static and dynamic class / style attributes.3487 // - onXXX handlers / style: merge into array3488 // - class: merge into single expression with concatenation3489 function dedupeProperties(properties) {3490 const knownProps = new Map();3491 const deduped = [];3492 for (let i = 0; i < properties.length; i++) {3493 const prop = properties[i];3494 // dynamic keys are always allowed3495 if (prop.key.type === 8 /* COMPOUND_EXPRESSION */ || !prop.key.isStatic) {3496 deduped.push(prop);3497 continue;3498 }3499 const name = prop.key.content;3500 const existing = knownProps.get(name);3501 if (existing) {3502 if (name === 'style' || name === 'class' || name.startsWith('on')) {3503 mergeAsArray(existing, prop);3504 }3505 // unexpected duplicate, should have emitted error during parse3506 }3507 else {3508 knownProps.set(name, prop);3509 deduped.push(prop);3510 }3511 }3512 return deduped;3513 }3514 function mergeAsArray(existing, incoming) {3515 if (existing.value.type === 17 /* JS_ARRAY_EXPRESSION */) {3516 existing.value.elements.push(incoming.value);3517 }3518 else {3519 existing.value = createArrayExpression([existing.value, incoming.value], existing.loc);3520 }3521 }3522 function buildDirectiveArgs(dir, context) {3523 const dirArgs = [];3524 const runtime = directiveImportMap.get(dir);3525 if (runtime) {3526 // built-in directive with runtime3527 dirArgs.push(context.helperString(runtime));3528 }3529 else {3530 {3531 // inject statement for resolving directive3532 context.helper(RESOLVE_DIRECTIVE);3533 context.directives.add(dir.name);3534 dirArgs.push(toValidAssetId(dir.name, `directive`));3535 }3536 }3537 const { loc } = dir;3538 if (dir.exp)3539 dirArgs.push(dir.exp);3540 if (dir.arg) {3541 if (!dir.exp) {3542 dirArgs.push(`void 0`);3543 }3544 dirArgs.push(dir.arg);3545 }3546 if (Object.keys(dir.modifiers).length) {3547 if (!dir.arg) {3548 if (!dir.exp) {3549 dirArgs.push(`void 0`);3550 }3551 dirArgs.push(`void 0`);3552 }3553 const trueExpression = createSimpleExpression(`true`, false, loc);3554 dirArgs.push(createObjectExpression(dir.modifiers.map(modifier => createObjectProperty(modifier, trueExpression)), loc));3555 }3556 return createArrayExpression(dirArgs, dir.loc);3557 }3558 function stringifyDynamicPropNames(props) {3559 let propsNamesString = `[`;3560 for (let i = 0, l = props.length; i < l; i++) {3561 propsNamesString += JSON.stringify(props[i]);3562 if (i < l - 1)3563 propsNamesString += ', ';3564 }3565 return propsNamesString + `]`;3566 }3567 const transformSlotOutlet = (node, context) => {3568 if (isSlotOutlet(node)) {3569 const { children, loc } = node;3570 const { slotName, slotProps } = processSlotOutlet(node, context);3571 const slotArgs = [3572 context.prefixIdentifiers ? `_ctx.$slots` : `$slots`,3573 slotName3574 ];3575 if (slotProps) {3576 slotArgs.push(slotProps);3577 }3578 if (children.length) {3579 if (!slotProps) {3580 slotArgs.push(`{}`);3581 }3582 slotArgs.push(createFunctionExpression([], children, false, false, loc));3583 }3584 node.codegenNode = createCallExpression(context.helper(RENDER_SLOT), slotArgs, loc);3585 }3586 };3587 function processSlotOutlet(node, context) {3588 let slotName = `"default"`;3589 let slotProps = undefined;3590 const nonNameProps = [];3591 for (let i = 0; i < node.props.length; i++) {3592 const p = node.props[i];3593 if (p.type === 6 /* ATTRIBUTE */) {3594 if (p.value) {3595 if (p.name === 'name') {3596 slotName = JSON.stringify(p.value.content);3597 }3598 else {3599 p.name = camelize(p.name);3600 nonNameProps.push(p);3601 }3602 }3603 }3604 else {3605 if (p.name === 'bind' && isBindKey(p.arg, 'name')) {3606 if (p.exp)3607 slotName = p.exp;3608 }3609 else {3610 if (p.name === 'bind' && p.arg && isStaticExp(p.arg)) {3611 p.arg.content = camelize(p.arg.content);3612 }3613 nonNameProps.push(p);3614 }3615 }3616 }3617 if (nonNameProps.length > 0) {3618 const { props, directives } = buildProps(node, context, nonNameProps);3619 slotProps = props;3620 if (directives.length) {3621 context.onError(createCompilerError(35 /* X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET */, directives[0].loc));3622 }3623 }3624 return {3625 slotName,3626 slotProps3627 };3628 }3629 const fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^\s*function(?:\s+[\w$]+)?\s*\(/;3630 const transformOn = (dir, node, context, augmentor) => {3631 const { loc, modifiers, arg } = dir;3632 if (!dir.exp && !modifiers.length) {3633 context.onError(createCompilerError(34 /* X_V_ON_NO_EXPRESSION */, loc));3634 }3635 let eventName;3636 if (arg.type === 4 /* SIMPLE_EXPRESSION */) {3637 if (arg.isStatic) {3638 const rawName = arg.content;3639 // for all event listeners, auto convert it to camelCase. See issue #22493640 eventName = createSimpleExpression(toHandlerKey(camelize(rawName)), true, arg.loc);3641 }3642 else {3643 // #23883644 eventName = createCompoundExpression([3645 `${context.helperString(TO_HANDLER_KEY)}(`,3646 arg,3647 `)`3648 ]);3649 }3650 }3651 else {3652 // already a compound expression.3653 eventName = arg;3654 eventName.children.unshift(`${context.helperString(TO_HANDLER_KEY)}(`);3655 eventName.children.push(`)`);3656 }3657 // handler processing3658 let exp = dir.exp;3659 if (exp && !exp.content.trim()) {3660 exp = undefined;3661 }3662 let shouldCache = context.cacheHandlers && !exp;3663 if (exp) {3664 const isMemberExp = isMemberExpression(exp.content);3665 const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content));3666 const hasMultipleStatements = exp.content.includes(`;`);3667 {3668 validateBrowserExpression(exp, context, false, hasMultipleStatements);3669 }3670 if (isInlineStatement || (shouldCache && isMemberExp)) {3671 // wrap inline statement in a function expression3672 exp = createCompoundExpression([3673 `${isInlineStatement3674 ? `$event`3675 : `${ ``}(...args)`} => ${hasMultipleStatements ? `{` : `(`}`,3676 exp,3677 hasMultipleStatements ? `}` : `)`3678 ]);3679 }3680 }3681 let ret = {3682 props: [...
vue.global.js
Source:vue.global.js
...2593 * Validate a non-prefixed expression.2594 * This is only called when using the in-browser runtime compiler since it2595 * doesn't prefix expressions.2596 */2597 function validateBrowserExpression(2598 node,2599 context,2600 asParams = false,2601 asRawStatements = false2602 ) {2603 const exp = node.content2604 // empty expressions are validated per-directive since some directives2605 // do allow empty expressions.2606 if (!exp.trim()) {2607 return2608 }2609 try {2610 new Function(2611 asRawStatements2612 ? ` ${exp} `2613 : `return ${asParams ? `(${exp}) => {}` : `(${exp})`}`2614 )2615 } catch (e) {2616 let message = e.message2617 const keywordMatch = exp2618 .replace(stripStringRE, '')2619 .match(prohibitedKeywordRE)2620 if (keywordMatch) {2621 message = `avoid using JavaScript keyword as property name: "${2622 keywordMatch[0]2623 }"`2624 }2625 context.onError(2626 createCompilerError(2627 43 /* X_INVALID_EXPRESSION */,2628 node.loc,2629 undefined,2630 message2631 )2632 )2633 }2634 }2635 const transformExpression = (node, context) => {2636 if (node.type === 5 /* INTERPOLATION */) {2637 node.content = processExpression(node.content, context)2638 } else if (node.type === 1 /* ELEMENT */) {2639 // handle directives on element2640 for (let i = 0; i < node.props.length; i++) {2641 const dir = node.props[i]2642 // ä¸å¤ç v-on & v-for å®ä»¬ç±èªå·±ç transformXxx å¤ç2643 if (dir.type === 7 /* DIRECTIVE */ && dir.name !== 'for') {2644 const exp = dir.exp2645 const arg = dir.arg2646 // ä¸å¤çæ 表达å¼æ
åµ(v-on:arg)ï¼åºä¸ºå¯¹äºå
è表达å¼éè¦ç¹æ®å¤ç2647 if (2648 exp &&2649 exp.type === 4 /* SIMPLE_EXPRESSION */ &&2650 !(dir.name === 'on' && arg)2651 ) {2652 dir.exp = processExpression(2653 exp,2654 context,2655 // slot args must be processed as function params2656 // slot åæ°å¿
é¡»å½åå½æ°åæ°å¤ç2657 dir.name === 'slot'2658 )2659 }2660 // å¨æåæ° v-bind:[arg]="exp"2661 if (arg && arg.type === 4 /* SIMPLE_EXPRESSION */ && !arg.isStatic) {2662 dir.arg = processExpression(arg, context)2663 }2664 }2665 }2666 }2667 }2668 // Important: since this function uses Node.js only dependencies, it should2669 // always be used with a leading !true check so that it can be2670 // tree-shaken from the browser build.2671 function processExpression(2672 node,2673 context,2674 // some expressions like v-slot props & v-for aliases should be parsed as2675 // function params2676 asParams = false,2677 // v-on handler values may contain multiple statements2678 asRawStatements = false2679 ) {2680 {2681 {2682 // simple in-browser validation (same logic in 2.x)2683 validateBrowserExpression(node, context, asParams, asRawStatements)2684 }2685 return node2686 }2687 }2688 const transformIf = createStructuralDirectiveTransform(2689 /^(if|else|else-if)$/,2690 (node, dir, context) => {2691 return processIf(node, dir, context, (ifNode, branch, isRoot) => {2692 // #1587: We need to dynamically increment the key based on the current2693 // node's sibling nodes, since chained v-if/else branches are2694 // rendered at the same depth2695 // è¿é讲çæ¯ï¼å¿
é¡»ç»å
å¼èç¹ä¸ä¸ªå¨æéå¢ç `key` å±æ§ï¼å 为 v-if/else åæ¯2696 // ä¼å¨åä¸çº§æ¸²æ2697 // ååºåæ¯çææå
å¼ï¼è¿éé¢å
å«å®èªå·±2698 const siblings = context.parent.children2699 let i = siblings.indexOf(ifNode)2700 let key = 02701 while (i-- >= 0) {2702 const sibling = siblings[i]2703 if (sibling && sibling.type === 9 /* IF */) {2704 key += sibling.branches.length2705 }2706 }2707 // Exit callback. Complete the codegenNode when all children have been2708 // transformed.2709 // exitFns ä¸ç exitFn ï¼å°è¿éçæ¶å说æåæ¯èç¹çææ children é½è¢« traverse2710 // è¿äºï¼å æ¤è¿éå°±å¯ä»¥ç´æ¥è¿å对åºç codegenNode äº2711 return () => {2712 if (isRoot) {2713 ifNode.codegenNode = createCodegenNodeForBranch(2714 branch,2715 key,2716 context2717 )2718 } else {2719 // attach this branch's codegen node to the v-if root.2720 const parentCondition = getParentCondition(ifNode.codegenNode)2721 parentCondition.alternate = createCodegenNodeForBranch(2722 branch,2723 key + ifNode.branches.length - 1,2724 context2725 )2726 }2727 }2728 })2729 }2730 )2731 // target-agnostic transform used for both Client and SSR2732 function processIf(node, dir, context, processCodegen) {2733 // ä¸æ¯ v-else ä¸æ²¡æ表达å¼çæ
åµï¼éæ³çæ
åµï¼å¦ï¼ <div v-if></div>2734 if (dir.name !== 'else' && (!dir.exp || !dir.exp.content.trim())) {2735 const loc = dir.exp ? dir.exp.loc : node.loc2736 context.onError(2737 createCompilerError(27 /* X_V_IF_NO_EXPRESSION */, dir.loc)2738 )2739 // é»è®¤è¡¨è¾¾å¼çå¼ä¸º true -> <div v-if="true" ...2740 dir.exp = createSimpleExpression(`true`, false, loc)2741 }2742 if (dir.exp) {2743 // æ£æµæ¯ä¸æ¯ææç表达å¼ï¼ç´æ¥ new Function(code) æ没æ¥éå°±ç¥é对ä¸å¯¹2744 validateBrowserExpression(dir.exp, context)2745 }2746 if (dir.name === 'if') {2747 // v-if åæ¯2748 const branch = createIfBranch(node, dir)2749 const ifNode = {2750 type: 9 /* IF */,2751 loc: node.loc,2752 branches: [branch]2753 }2754 // æ¿æ¢åæ¥çèç¹2755 context.replaceNode(ifNode)2756 if (processCodegen) {2757 return processCodegen(ifNode, branch, true)2758 }2759 } else {2760 // v-else, v-else-if åæ¯2761 // locate the adjacent v-if2762 const siblings = context.parent.children2763 const comments = []2764 let i = siblings.indexOf(node)2765 // ä¸ç´å¾åæ¾å° v-if èç¹2766 while (i-- >= -1) {2767 const sibling = siblings[i]2768 // å¼å模å¼å¿½ç¥æ³¨éï¼ä½ç¼åå°æ¥éè¦åå¤ï¼ç产模å¼ä¸éè¦æ³¨é2769 if (sibling && sibling.type === 3 /* COMMENT */) {2770 context.removeNode(sibling)2771 comments.unshift(sibling)2772 continue2773 }2774 // 空ææ¬å
容ï¼ç´æ¥å é¤2775 if (2776 sibling &&2777 sibling.type === 2 /* TEXT */ &&2778 !sibling.content.trim().length2779 ) {2780 context.removeNode(sibling)2781 continue2782 }2783 if (sibling && sibling.type === 9 /* IF */) {2784 // æ¾å°ç®æ èç¹2785 context.removeNode()2786 const branch = createIfBranch(node, dir)2787 if (comments.length) {2788 branch.children = [...comments, ...branch.children]2789 }2790 // check if user is forcing same key on different branches2791 // å¨ä¸ååæ¯ä¸åºç¨äºåä¸ä¸ª `key`2792 {2793 const key = branch.userKey2794 if (key) {2795 sibling.branches.forEach(({ userKey }) => {2796 if (isSameKey(userKey, key)) {2797 context.onError(2798 createCompilerError(2799 28 /* X_V_IF_SAME_KEY */,2800 branch.userKey.loc2801 )2802 )2803 }2804 })2805 }2806 }2807 sibling.branches.push(branch)2808 const onExit =2809 processCodegen && processCodegen(sibling, branch, false)2810 // since the branch was removed, it will not be traversed.2811 // make sure to traverse here.2812 // åæ¯èç¹è¢«ä¸é¢å é¤ï¼æ以è¦æå¨ traverse 该èç¹2813 traverseNode(branch, context)2814 // call on exit2815 if (onExit) onExit()2816 // make sure to reset currentNode after traversal to indicate this2817 // node has been removed.2818 // æ è¯å½åèç¹è¢«å é¤äºï¼ traverseNode ä¸ä¼ç¨å°2819 context.currentNode = null2820 } else {2821 context.onError(2822 createCompilerError(29 /* X_V_ELSE_NO_ADJACENT_IF */, node.loc)2823 )2824 }2825 break2826 }2827 }2828 }2829 function createIfBranch(node, dir) {2830 return {2831 type: 10 /* IF_BRANCH */,2832 loc: node.loc,2833 // condition ? v-if node : v-else node2834 condition: dir.name === 'else' ? undefined : dir.exp,2835 // å¦æç¨çæ¯ <template v-if="condition" ... å°±éè¦ node.children2836 // å 为 template æ¬èº«æ¯ä¸è¯¥è¢«æ¸²æç2837 children:2838 node.tagType === 3 /* TEMPLATE */ && !findDir(node, 'for')2839 ? node.children2840 : [node],2841 // å¯¹äº v-for, v-if/... é½åºè¯¥ç»å®ä¸ª key, è¿éæ¯ç¨æ·ç¼åæ¯çæä¾çå¯ä¸ key2842 // å¦æ没æ解æå¨ä¼é»è®¤çæä¸ä¸ªå
¨å±å¯ä¸ç key2843 userKey: findProp(node, `key`)2844 }2845 }2846 function createCodegenNodeForBranch(branch, keyIndex, context) {2847 if (branch.condition) {2848 return createConditionalExpression(2849 branch.condition,2850 createChildrenCodegenNode(branch, keyIndex, context),2851 // make sure to pass in asBlock: true so that the comment node call2852 // closes the current block.2853 createCallExpression(context.helper(CREATE_COMMENT), ['"v-if"', 'true'])2854 )2855 } else {2856 return createChildrenCodegenNode(branch, keyIndex, context)2857 }2858 }2859 function createChildrenCodegenNode(branch, keyIndex, context) {2860 const { helper } = context2861 // ç»æ¯ä¸ªåæ¯å ä¸ä¸ª `key` å±æ§2862 const keyProperty = createObjectProperty(2863 `key`,2864 createSimpleExpression(`${keyIndex}`, false, locStub, 2 /* CAN_HOIST */)2865 )2866 const { children } = branch2867 const firstChild = children[0]2868 // æ¯ä¸æ¯éè¦ç¨ fragment å°ææ children å
èµ·æ¥2869 const needFragmentWrapper =2870 children.length !== 1 || firstChild.type !== 1 /* ELEMENT */2871 if (needFragmentWrapper) {2872 if (children.length === 1 && firstChild.type === 11 /* FOR */) {2873 // optimize away nested fragments when child is a ForNode2874 const vnodeCall = firstChild.codegenNode2875 injectProp(vnodeCall, keyProperty, context)2876 return vnodeCall2877 } else {2878 return createVNodeCall(2879 context,2880 helper(FRAGMENT),2881 createObjectExpression([keyProperty]),2882 children,2883 64 /* STABLE_FRAGMENT */ +2884 ` /* ${PatchFlagNames[64 /* STABLE_FRAGMENT */]} */`,2885 undefined,2886 undefined,2887 true,2888 false,2889 branch.loc2890 )2891 }2892 } else {2893 // children.length === 1 && firstChild.type === NodeTypes.ELEMENT2894 // æ£å¸¸çå
ç´ ï¼ç´æ¥ç¨å®æ¥å建2895 const vnodeCall = firstChild.codegenNode2896 // Change createVNode to createBlock.2897 if (vnodeCall.type === 13 /* VNODE_CALL */) {2898 vnodeCall.isBlock = true2899 helper(OPEN_BLOCK)2900 helper(CREATE_BLOCK)2901 }2902 // inject branch key2903 injectProp(vnodeCall, keyProperty, context)2904 return vnodeCall2905 }2906 }2907 function isSameKey(a, b) {2908 if (!a || a.type !== b.type) {2909 return false2910 }2911 if (a.type === 6 /* ATTRIBUTE */) {2912 if (a.value.content !== b.value.content) {2913 return false2914 }2915 } else {2916 // directive2917 const exp = a.exp2918 const branchExp = b.exp2919 if (exp.type !== branchExp.type) {2920 return false2921 }2922 if (2923 exp.type !== 4 /* SIMPLE_EXPRESSION */ ||2924 (exp.isStatic !== branchExp.isStatic ||2925 exp.content !== branchExp.content)2926 ) {2927 return false2928 }2929 }2930 return true2931 }2932 function getParentCondition(node) {2933 while (true) {2934 if (node.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {2935 if (node.alternate.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {2936 node = node.alternate2937 } else {2938 return node2939 }2940 } else if (node.type === 20 /* JS_CACHE_EXPRESSION */) {2941 node = node.value2942 }2943 }2944 }2945 const transformFor = createStructuralDirectiveTransform(2946 'for',2947 (node, dir, context) => {2948 const { helper } = context2949 return processFor(node, dir, context, forNode => {2950 // create the loop render function expression now, and add the2951 // iterator on exit after all children have been traversed2952 const renderExp = createCallExpression(helper(RENDER_LIST), [2953 forNode.source2954 ])2955 const keyProp = findProp(node, `key`)2956 const keyProperty = keyProp2957 ? createObjectProperty(2958 `key`,2959 keyProp.type === 6 /* ATTRIBUTE */2960 ? createSimpleExpression(keyProp.value.content, true)2961 : keyProp.exp2962 )2963 : null2964 const isStableFragment =2965 forNode.source.type === 4 /* SIMPLE_EXPRESSION */ &&2966 forNode.source.constType > 02967 const fragmentFlag = isStableFragment2968 ? 64 /* STABLE_FRAGMENT */2969 : keyProp2970 ? 128 /* KEYED_FRAGMENT */2971 : 256 /* UNKEYED_FRAGMENT */2972 forNode.codegenNode = createVNodeCall(2973 context,2974 helper(FRAGMENT),2975 undefined,2976 renderExp,2977 fragmentFlag + ` /* ${PatchFlagNames[fragmentFlag]} */`,2978 undefined,2979 undefined,2980 true /* isBlock */,2981 !isStableFragment /* disableTracking */,2982 node.loc2983 )2984 return () => {2985 // finish the codegen now that all children have been traversed2986 let childBlock2987 const isTemplate = isTemplateNode(node)2988 const { children } = forNode2989 // check <template v-for> key placement2990 if (isTemplate) {2991 node.children.some(c => {2992 if (c.type === 1 /* ELEMENT */) {2993 const key = findProp(c, `key`)2994 if (key) {2995 context.onError(2996 createCompilerError(2997 32 /* X_V_FOR_TEMPLATE_KEY_PLACEMENT */,2998 key.loc2999 )3000 )3001 return true3002 }3003 }3004 })3005 }3006 const needFragmentWrapper =3007 children.length !== 1 || children[0].type !== 1 /* ELEMENT */3008 const slotOutlet = isSlotOutlet(node)3009 ? node3010 : isTemplate &&3011 node.children.length === 1 &&3012 isSlotOutlet(node.children[0])3013 ? node.children[0] // api-extractor somehow fails to infer this3014 : null3015 if (slotOutlet) {3016 // <slot v-for="..."> or <template v-for="..."><slot/></template>3017 childBlock = slotOutlet.codegenNode3018 if (isTemplate && keyProperty) {3019 // <template v-for="..." :key="..."><slot/></template>3020 // we need to inject the key to the renderSlot() call.3021 // the props for renderSlot is passed as the 3rd argument.3022 injectProp(childBlock, keyProperty, context)3023 }3024 } else if (needFragmentWrapper) {3025 // <template v-for="..."> with text or multi-elements3026 // should generate a fragment block for each loop3027 childBlock = createVNodeCall(3028 context,3029 helper(FRAGMENT),3030 keyProperty ? createObjectExpression([keyProperty]) : undefined,3031 node.children,3032 64 /* STABLE_FRAGMENT */ +3033 ` /* ${PatchFlagNames[64 /* STABLE_FRAGMENT */]} */`,3034 undefined,3035 undefined,3036 true3037 )3038 } else {3039 // Normal element v-for. Directly use the child's codegenNode3040 // but mark it as a block.3041 childBlock = children[0].codegenNode3042 if (isTemplate && keyProperty) {3043 injectProp(childBlock, keyProperty, context)3044 }3045 childBlock.isBlock = !isStableFragment3046 if (childBlock.isBlock) {3047 helper(OPEN_BLOCK)3048 helper(CREATE_BLOCK)3049 } else {3050 helper(CREATE_VNODE)3051 }3052 }3053 renderExp.arguments.push(3054 createFunctionExpression(3055 createForLoopParams(forNode.parseResult),3056 childBlock,3057 true /* force newline */3058 )3059 )3060 }3061 })3062 }3063 )3064 // target-agnostic transform used for both Client and SSR3065 function processFor(node, dir, context, processCodegen) {3066 if (!dir.exp) {3067 context.onError(3068 createCompilerError(30 /* X_V_FOR_NO_EXPRESSION */, dir.loc)3069 )3070 return3071 }3072 const parseResult = parseForExpression(3073 // can only be simple expression because vFor transform is applied3074 // before expression transform.3075 dir.exp,3076 context3077 )3078 if (!parseResult) {3079 context.onError(3080 createCompilerError(31 /* X_V_FOR_MALFORMED_EXPRESSION */, dir.loc)3081 )3082 return3083 }3084 const { addIdentifiers, removeIdentifiers, scopes } = context3085 const { source, value, key, index } = parseResult3086 const forNode = {3087 type: 11 /* FOR */,3088 loc: dir.loc,3089 source,3090 valueAlias: value,3091 keyAlias: key,3092 objectIndexAlias: index,3093 parseResult,3094 children: isTemplateNode(node) ? node.children : [node]3095 }3096 context.replaceNode(forNode)3097 // bookkeeping3098 scopes.vFor++3099 const onExit = processCodegen && processCodegen(forNode)3100 return () => {3101 scopes.vFor--3102 if (onExit) onExit()3103 }3104 }3105 // for ... in/of ...3106 const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/3107 // This regex doesn't cover the case if key or index aliases have destructuring,3108 // but those do not make sense in the first place, so this works in practice.3109 const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/3110 const stripParensRE = /^\(|\)$/g3111 function parseForExpression(input, context) {3112 const loc = input.loc3113 const exp = input.content3114 const inMatch = exp.match(forAliasRE)3115 if (!inMatch) return3116 // LHS in|of RHS3117 const [, LHS, RHS] = inMatch3118 const result = {3119 source: createAliasExpression(3120 loc,3121 RHS.trim(),3122 exp.indexOf(RHS, LHS.length)3123 ),3124 value: undefined,3125 key: undefined,3126 index: undefined3127 }3128 {3129 validateBrowserExpression(result.source, context)3130 }3131 // å»æååç (, ), å¦ï¼ (value, key, index) -> `value, key, index`3132 let valueContent = LHS.trim()3133 .replace(stripParensRE, '')3134 .trim()3135 const trimmedOffset = LHS.indexOf(valueContent)3136 // value, key, index -> å¹é
åº ` key` å ` index`3137 const iteratorMatch = valueContent.match(forIteratorRE)3138 if (iteratorMatch) {3139 valueContent = valueContent.replace(forIteratorRE, '').trim()3140 // ` key` -> `key`3141 const keyContent = iteratorMatch[1].trim()3142 let keyOffset3143 if (keyContent) {3144 keyOffset = exp.indexOf(keyContent, trimmedOffset + valueContent.length)3145 result.key = createAliasExpression(loc, keyContent, keyOffset)3146 {3147 validateBrowserExpression(result.key, context, true)3148 }3149 }3150 // `index`3151 if (iteratorMatch[2]) {3152 const indexContent = iteratorMatch[2].trim()3153 if (indexContent) {3154 result.index = createAliasExpression(3155 loc,3156 indexContent,3157 exp.indexOf(3158 indexContent,3159 result.key3160 ? keyOffset + keyContent.length3161 : trimmedOffset + valueContent.length3162 )3163 )3164 {3165 validateBrowserExpression(result.index, context, true)3166 }3167 }3168 }3169 }3170 if (valueContent) {3171 result.value = createAliasExpression(loc, valueContent, trimmedOffset)3172 {3173 validateBrowserExpression(result.value, context, true)3174 }3175 }3176 return result3177 }3178 function createAliasExpression(range, content, offset) {3179 return createSimpleExpression(3180 content,3181 false,3182 getInnerRange(range, offset, content.length)3183 )3184 }3185 function createForLoopParams({ value, key, index }) {3186 // function: (_, __, index) => ....3187 const params = []3188 if (value) {3189 params.push(value)3190 }3191 if (key) {3192 if (!value) {3193 params.push(createSimpleExpression(`_`, false))3194 }3195 params.push(key)3196 }3197 if (index) {3198 if (!key) {3199 if (!value) {3200 params.push(createSimpleExpression(`_`, false))3201 }3202 params.push(createSimpleExpression(`__`, false))3203 }3204 params.push(index)3205 }3206 return params3207 }3208 const defaultFallback = createSimpleExpression(`undefined`, false)3209 // A NodeTransform that:3210 // 1. Tracks scope identifiers for scoped slots so that they don't get prefixed3211 // by transformExpression. This is only applied in non-browser builds with3212 // { prefixIdentifiers: true }.3213 // 2. Track v-slot depths so that we know a slot is inside another slot.3214 // Note the exit callback is executed before buildSlots() on the same node,3215 // so only nested slots see positive numbers.3216 const trackSlotScopes = (node, context) => {3217 // <component> or <template>3218 if (3219 node.type === 1 /* ELEMENT */ &&3220 (node.tagType === 1 /* COMPONENT */ || node.tagType === 3) /* TEMPLATE */3221 ) {3222 // We are only checking non-empty v-slot here3223 // since we only care about slots that introduce scope variables.3224 const vSlot = findDir(node, 'slot')3225 if (vSlot) {3226 const slotProps = vSlot.exp3227 context.scopes.vSlot++3228 return () => {3229 context.scopes.vSlot--3230 }3231 }3232 }3233 }3234 const buildClientSlotFn = (props, children, loc) =>3235 createFunctionExpression(3236 props,3237 children,3238 false /* newline */,3239 true /* isSlot */,3240 children.length ? children[0].loc : loc3241 )3242 // Instead of being a DirectiveTransform, v-slot processing is called during3243 // transformElement to build the slots object for a component.3244 function buildSlots(node, context, buildSlotFn = buildClientSlotFn) {3245 context.helper(WITH_CTX)3246 const { children, loc } = node3247 const slotsProperties = []3248 const dynamicSlots = []3249 const buildDefaultSlotProperty = (props, children) =>3250 createObjectProperty(`default`, buildSlotFn(props, children, loc))3251 // If the slot is inside a v-for or another v-slot, force it to be dynamic3252 // since it likely uses a scope variable.3253 // å¦æ slot æ¯å¨ v-for æå¦ä¸ä¸ª v-slot éé¢å¼ºå¶å®æ为ä¸ä¸ªå¨æç3254 let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 03255 // 1. <Comp v-slot="{ prop }"/> æ£æ¥ slot å slotProps åºç¨å¨ç»ä»¶èªèº«3256 const onComponentSlot = findDir(node, 'slot', true)3257 if (onComponentSlot) {3258 const { arg, exp } = onComponentSlot3259 if (arg && !isStaticExp(arg)) {3260 hasDynamicSlots = true3261 }3262 slotsProperties.push(3263 createObjectProperty(3264 arg || createSimpleExpression('default', true),3265 buildSlotFn(exp, children, loc)3266 )3267 )3268 }3269 // 2. éåææ children æ£æ¥æ¯å¦åå¨ <template v-slot:foo="{prop}">3270 let hasTemplateSlots = false3271 let hasNamedDefaultSlot = false3272 const implicitDefaultChildren = []3273 const seenSlotNames = new Set()3274 for (let i = 0; i < children.length; i++) {3275 const slotElement = children[i]3276 let slotDir3277 // ä¸æ¯ <template> æ没æ v-slot3278 if (3279 !isTemplateNode(slotElement) ||3280 !(slotDir = findDir(slotElement, 'slot', true))3281 ) {3282 // ä¸æ¯ <template v-slot> è·³è¿ä¸å¤ç3283 if (slotElement.type !== 3 /* COMMENT */) {3284 implicitDefaultChildren.push(slotElement)3285 }3286 continue3287 }3288 if (onComponentSlot) {3289 // ç»ä»¶ä¸å·²ç»æv-slot çæ¶åï¼éé¢ææå©åé½ä¸è½å¨ä½¿ç¨ v-slot3290 context.onError(3291 createCompilerError(36 /* X_V_SLOT_MIXED_SLOT_USAGE */, slotDir.loc)3292 )3293 break3294 }3295 hasTemplateSlots = true3296 const { children: slotChildren, loc: slotLoc } = slotElement3297 const {3298 arg: slotName = createSimpleExpression(`default`, true),3299 exp: slotProps,3300 loc: dirLoc3301 } = slotDir3302 // check if name is dynamic3303 let staticSlotName3304 if (isStaticExp(slotName)) {3305 staticSlotName = slotName ? slotName.content : `default`3306 } else {3307 // dynamic slot name, v-slot:[name]="slotProps"3308 hasDynamicSlots = true3309 }3310 const slotFunction = buildSlotFn(slotProps, slotChildren, slotLoc)3311 // check if this slot is conditional (v-if/v-for)3312 let vIf3313 let vElse3314 let vFor3315 if ((vIf = findDir(slotElement, 'if'))) {3316 // v-slot with v-if3317 hasDynamicSlots = true3318 dynamicSlots.push(3319 createConditionalExpression(3320 vIf.exp,3321 buildDynamicSlot(slotName, slotFunction),3322 defaultFallback3323 )3324 )3325 } else if (3326 (vElse = findDir(slotElement, /^else(-if)?$/, true /* allowEmpty */))3327 ) {3328 // v-else/if on slot3329 let j = i3330 let prev3331 while (j--) {3332 // æ¾å°ç¸é»ç v-if3333 prev = children[j]3334 if (prev.type !== 3 /* COMMENT */) {3335 break3336 }3337 }3338 if (prev && isTemplateNode(prev) && findDir(prev, 'if')) {3339 // remove node3340 children.splice(i, 1)3341 i--3342 // attach this slot to previous conditional3343 let conditional = dynamicSlots[dynamicSlots.length - 1]3344 while (3345 conditional.alternate.type === 19 /* JS_CONDITIONAL_EXPRESSION */3346 ) {3347 conditional = conditional.alternate3348 }3349 conditional.alternate = vElse.exp3350 ? createConditionalExpression(3351 vElse.exp,3352 buildDynamicSlot(slotName, slotFunction),3353 defaultFallback3354 )3355 : buildDynamicSlot(slotName, slotFunction)3356 } else {3357 context.onError(3358 createCompilerError(29 /* X_V_ELSE_NO_ADJACENT_IF */, vElse.loc)3359 )3360 }3361 } else if ((vFor = findDir(slotElement, 'for'))) {3362 hasDynamicSlots = true3363 const parseResult =3364 vFor.parseResult || parseForExpression(vFor.exp, context)3365 if (parseResult) {3366 // Render the dynamic slots as an array and add it to the createSlot()3367 // args. The runtime knows how to handle it appropriately.3368 dynamicSlots.push(3369 createCallExpression(context.helper(RENDER_LIST), [3370 parseResult.source,3371 createFunctionExpression(3372 createForLoopParams(parseResult),3373 buildDynamicSlot(slotName, slotFunction),3374 true /* force newline */3375 )3376 ])3377 )3378 } else {3379 context.onError(3380 createCompilerError(31 /* X_V_FOR_MALFORMED_EXPRESSION */, vFor.loc)3381 )3382 }3383 } else {3384 // æ£æ¥éæå±æ§åæ¯å¦æéå¤ç3385 if (staticSlotName) {3386 if (seenSlotNames.has(staticSlotName)) {3387 context.onError(3388 createCompilerError(3389 37 /* X_V_SLOT_DUPLICATE_SLOT_NAMES */,3390 dirLoc3391 )3392 )3393 continue3394 }3395 seenSlotNames.add(staticSlotName)3396 if (staticSlotName === 'default') {3397 // æ¾å¼ç使ç¨äºé»è®¤æ槽å称3398 hasNamedDefaultSlot = true3399 }3400 }3401 slotsProperties.push(createObjectProperty(slotName, slotFunction))3402 }3403 }3404 if (!onComponentSlot) {3405 if (!hasTemplateSlots) {3406 // implicit default slot (on component)3407 // <Comp><div/></Comp> å
ç <div/> ä½ä¸ºé»è®¤æ槽3408 slotsProperties.push(buildDefaultSlotProperty(undefined, children))3409 } else if (implicitDefaultChildren.length) {3410 // 1. é <Comp v-slot="slotProps">3411 // 2. åå¨ <template v-slot>3412 // 3. ä¸åå¨å
¶ä»é template ç±»åçèç¹3413 // 4. å¦ææ <template v-slot:default="slotProps"> æ¶åè§ä¸ºéæ³3414 // å 为å
¶ä»é template ç±»åçèç¹ä¼è¢«è§ä¸ºé»è®¤æ槽å
容3415 if (hasNamedDefaultSlot) {3416 context.onError(3417 createCompilerError(3418 38 /* X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN */,3419 implicitDefaultChildren[0].loc3420 )3421 )3422 } else {3423 slotsProperties.push(3424 buildDefaultSlotProperty(undefined, implicitDefaultChildren)3425 )3426 }3427 }3428 }3429 const slotFlag = hasDynamicSlots3430 ? 2 /* DYNAMIC */3431 : hasForwardedSlots(node.children)3432 ? 3 /* FORWARDED */3433 : 1 /* STABLE */3434 // å¢å `_` å±æ§ï¼æ è¯è¯¥ slot ç±»åï¼1-stable,2-forwarded,3-dynamic3435 let slots = createObjectExpression(3436 slotsProperties.concat(3437 createObjectProperty(3438 `_`,3439 // 2 = compiled but dynamic = can skip normalization, but must run diff3440 // 1 = compiled and static = can skip normalization AND diff as optimized3441 createSimpleExpression(3442 slotFlag + ` /* ${slotFlagsText[slotFlag]} */`,3443 false3444 )3445 )3446 ),3447 loc3448 )3449 // å¨æææ§½ï¼ v-slot:[name]="slotProps" æå¨ v-if/v-for æ令ä¸é½æ¯ä¸ºå¨æ3450 if (dynamicSlots.length) {3451 slots = createCallExpression(context.helper(CREATE_SLOTS), [3452 slots,3453 createArrayExpression(dynamicSlots)3454 ])3455 }3456 return { slots, hasDynamicSlots }3457 }3458 function buildDynamicSlot(name, fn) {3459 return createObjectExpression([3460 createObjectProperty(`name`, name),3461 createObjectProperty(`fn`, fn)3462 ])3463 }3464 function hasForwardedSlots(children) {3465 for (let i = 0; i < children.length; i++) {3466 const child = children[i]3467 // 满足æ¡ä»¶çä¸é¢æ
åµ3468 // 1. child å¿
é¡»æ¯ 1,ELEMENT ç±»å3469 // 2. <slot> æ <element>ä¸å©åèç¹ä¸æ满足 1&2æ
åµ3470 if (child.type === 1 /* ELEMENT */) {3471 if (3472 child.tagType === 2 /* SLOT */ ||3473 (child.tagType === 0 /* ELEMENT */ &&3474 hasForwardedSlots(child.children))3475 ) {3476 return true3477 }3478 }3479 }3480 return false3481 }3482 // some directive transforms (e.g. v-model) may return a symbol for runtime3483 // import, which should be used instead of a resolveDirective call.3484 const directiveImportMap = new WeakMap()3485 const transformElement = (node, context) => {3486 if (3487 !(3488 node.type === 1 /* ELEMENT */ &&3489 (node.tagType === 0 /* ELEMENT */ || node.tagType === 1) /* COMPONENT */3490 )3491 ) {3492 return3493 }3494 // perform the work on exit, after all child expressions have been3495 // processed and merged.3496 return function postTransformElement() {3497 const { tag, props } = node3498 const isComponent = node.tagType === 1 /* COMPONENT */3499 // The goal of the transform is to create a codegenNode implementing the3500 // VNodeCall interface.3501 const vnodeTag = isComponent3502 ? resolveComponentType(node, context)3503 : `"${tag}"`3504 const isDynamicComponent =3505 isObject(vnodeTag) && vnodeTag.callee === RESOLVE_DYNAMIC_COMPONENT3506 let vnodeProps3507 let vnodeChildren3508 let vnodePatchFlag3509 let patchFlag = 03510 let vnodeDynamicProps3511 let dynamicPropNames3512 let vnodeDirectives3513 let shouldUseBlock =3514 // dynamic component may resolve to plain elements3515 isDynamicComponent ||3516 vnodeTag === TELEPORT ||3517 vnodeTag === SUSPENSE ||3518 (!isComponent &&3519 // <svg> and <foreignObject> must be forced into blocks so that block3520 // updates inside get proper isSVG flag at runtime. (#639, #643)3521 // This is technically web-specific, but splitting the logic out of core3522 // leads to too much unnecessary complexity.3523 (tag === 'svg' ||3524 tag === 'foreignObject' ||3525 // #938: elements with dynamic keys should be forced into blocks3526 findProp(node, 'key', true)))3527 if (props.length > 0) {3528 const propsBuildResult = buildProps(node, context)3529 vnodeProps = propsBuildResult.props3530 patchFlag = propsBuildResult.patchFlag3531 dynamicPropNames = propsBuildResult.dynamicPropNames3532 const directives = propsBuildResult.directives3533 vnodeDirectives =3534 directives && directives.length3535 ? createArrayExpression(3536 directives.map(dir => buildDirectiveArgs(dir, context))3537 )3538 : undefined3539 }3540 if (node.children.length > 0) {3541 if (vnodeTag === KEEP_ALIVE) {3542 // Although a built-in component, we compile KeepAlive with raw children3543 // instead of slot functions so that it can be used inside Transition3544 // or other Transition-wrapping HOCs.3545 // To ensure correct updates with block optimizations, we need to:3546 // 1. Force keep-alive into a block. This avoids its children being3547 // collected by a parent block.3548 shouldUseBlock = true3549 // 2. Force keep-alive to always be updated, since it uses raw children.3550 patchFlag |= 1024 /* DYNAMIC_SLOTS */3551 if (node.children.length > 1) {3552 context.onError(3553 createCompilerError(44 /* X_KEEP_ALIVE_INVALID_CHILDREN */, {3554 start: node.children[0].loc.start,3555 end: node.children[node.children.length - 1].loc.end,3556 source: ''3557 })3558 )3559 }3560 }3561 const shouldBuildAsSlots =3562 isComponent &&3563 // Teleport is not a real component and has dedicated runtime handling3564 vnodeTag !== TELEPORT &&3565 vnodeTag !== KEEP_ALIVE3566 if (shouldBuildAsSlots) {3567 const { slots, hasDynamicSlots } = buildSlots(node, context)3568 vnodeChildren = slots // { type: 15,JS_OBJECT_EXPRESSION, properties: [...]}3569 if (hasDynamicSlots) {3570 // å¨ææ槽3571 patchFlag |= 1024 /* DYNAMIC_SLOTS */3572 }3573 } else if (node.children.length === 1 && vnodeTag !== TELEPORT) {3574 // åªæä¸ä¸ªå©åèç¹çæ¶å3575 const child = node.children[0]3576 const type = child.type3577 // å¨æææ¬èç¹æ£æµ, æå¼æç»å表达å¼3578 const hasDynamicTextChild =3579 type === 5 /* INTERPOLATION */ ||3580 type === 8 /* COMPOUND_EXPRESSION */3581 if (3582 hasDynamicTextChild &&3583 getConstantType(child, context) === 0 /* NOT_CONSTANT */3584 ) {3585 patchFlag |= 1 /* TEXT */3586 }3587 // å¯ä¸ç child æ¯ä¸ªææ¬èç¹(plain / interpolation / expression)3588 if (hasDynamicTextChild || type === 2 /* TEXT */) {3589 vnodeChildren = child3590 } else {3591 vnodeChildren = node.children3592 }3593 } else {3594 vnodeChildren = node.children3595 }3596 }3597 // patchFlag å¤ç3598 if (patchFlag !== 0) {3599 {3600 if (patchFlag < 0) {3601 // special flags (negative and mutually exclusive)3602 vnodePatchFlag = patchFlag + ` /* ${PatchFlagNames[patchFlag]} */`3603 } else {3604 const flagNames = Object.keys(PatchFlagNames)3605 .map(Number)3606 .filter(n => n > 0 && patchFlag & n)3607 .map(n => PatchFlagNames[n])3608 .join(', ')3609 vnodePatchFlag = patchFlag + ` /* ${flagNames} */`3610 }3611 }3612 // å¨æå±æ§3613 if (dynamicPropNames && dynamicPropNames.length) {3614 vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames)3615 }3616 }3617 // å¼å§æé VNODE_CALL ç±»å codegenNode3618 node.codegenNode = createVNodeCall(3619 context,3620 vnodeTag,3621 vnodeProps,3622 vnodeChildren,3623 vnodePatchFlag,3624 vnodeDynamicProps,3625 vnodeDirectives,3626 !!shouldUseBlock,3627 false /* disableTracking */,3628 node.loc3629 )3630 }3631 }3632 function resolveComponentType(node, context, ssr = false) {3633 const { tag } = node3634 // 1. å¨æç»ä»¶3635 const isProp =3636 node.tag === 'component' ? findProp(node, 'is') : findDir(node, 'is')3637 if (isProp) {3638 const exp =3639 // éæå±æ§3640 isProp.type === 6 /* ATTRIBUTE */3641 ? isProp.value && createSimpleExpression(isProp.value.content, true)3642 : isProp.exp3643 if (exp) {3644 return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [3645 exp3646 ])3647 }3648 }3649 // 2. å
ç½®ç»ä»¶(Teleport, Transition, KeepAlive, Suspense)3650 const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag)3651 if (builtIn) {3652 // built-ins are simply fallthroughs / have special handling during ssr3653 // no we don't need to import their runtime equivalents3654 if (!ssr) context.helper(builtIn)3655 return builtIn3656 }3657 // 5. user component(resolve)3658 context.helper(RESOLVE_COMPONENT)3659 context.components.add(tag)3660 return toValidAssetId(tag, `component`)3661 }3662 function buildProps(node, context, props = node.props, ssr = false) {3663 const { tag, loc: elementLoc } = node3664 const isComponent = node.tagType === 1 /* COMPONENT */3665 let properties = []3666 const mergeArgs = []3667 const runtimeDirectives = []3668 let patchFlag = 03669 let hasRef = false3670 // <div :class="..."3671 let hasClassBinding = false3672 // <div :style="..."3673 let hasStyleBinding = false3674 // <div @eventName="handler"3675 let hasHydrationEventBinding = false3676 // <div :key="..."3677 let hasDynamicKeys = false3678 let hasVnodeHook = false3679 const dynamicPropNames = []3680 const analyzePatchFlag = ({ key, value }) => {3681 if (isStaticExp(key)) {3682 const name = key.content3683 const isEventHandler = isOn(name)3684 if (3685 !isComponent &&3686 isEventHandler &&3687 // omit the flag for click handlers because hydration gives click3688 // dedicated fast path.3689 name.toLowerCase() !== 'onclick' &&3690 // omit v-model handlers3691 name !== 'onUpdate:modelValue' &&3692 // omit onVnodeXXX hooks3693 !isReservedProp(name)3694 ) {3695 hasHydrationEventBinding = true3696 }3697 if (isEventHandler && isReservedProp(name)) {3698 hasVnodeHook = true3699 }3700 if (3701 value.type === 20 /* JS_CACHE_EXPRESSION */ ||3702 ((value.type === 4 /* SIMPLE_EXPRESSION */ ||3703 value.type === 8) /* COMPOUND_EXPRESSION */ &&3704 getConstantType(value, context) > 0)3705 ) {3706 // skip if the prop is a cached handler or has constant value3707 return3708 }3709 if (name === 'ref') {3710 hasRef = true3711 } else if (name === 'class' && !isComponent) {3712 hasClassBinding = true3713 } else if (name === 'style' && !isComponent) {3714 hasStyleBinding = true3715 } else if (name !== 'key' && !dynamicPropNames.includes(name)) {3716 dynamicPropNames.push(name)3717 }3718 } else {3719 hasDynamicKeys = true3720 }3721 }3722 for (let i = 0; i < props.length; i++) {3723 // éæå±æ§3724 const prop = props[i]3725 if (prop.type === 6 /* ATTRIBUTE */) {3726 const { loc, name, value } = prop3727 let isStatic = true3728 if (name === 'ref') {3729 hasRef = true3730 }3731 // skip :is on <component>3732 if (name === 'is' && tag === 'component') {3733 continue3734 }3735 properties.push(3736 createObjectProperty(3737 createSimpleExpression(3738 name,3739 true,3740 getInnerRange(loc, 0, name.length)3741 ),3742 createSimpleExpression(3743 value ? value.content : '',3744 isStatic,3745 value ? value.loc : loc3746 )3747 )3748 )3749 } else {3750 // directives, æ令å±æ§3751 const { name, arg, exp, loc } = prop3752 const isBind = name === 'bind'3753 const isOn = name === 'on'3754 // skip v-slot - it is handled by its dedicated transform.3755 // v-slot ç± vSlot.ts å¤ç3756 if (name === 'slot') {3757 if (!isComponent) {3758 context.onError(3759 createCompilerError(39 /* X_V_SLOT_MISPLACED */, loc)3760 )3761 }3762 continue3763 }3764 // skip v-once, ç± vOnce.ts å¤ç3765 if (name === 'once') {3766 continue3767 }3768 // skip v-is and :is on <component>3769 if (3770 name === 'is' ||3771 (isBind && tag === 'component' && isBindKey(arg, 'is'))3772 ) {3773 continue3774 }3775 // skip v-on ins SSR compilation3776 if (isOn && ssr) {3777 continue3778 }3779 // v-bind, v-on 没æåæ°æ
åµ3780 if (!arg && (isBind || isOn)) {3781 hasDynamicKeys = true3782 if (exp) {3783 if (properties.length) {3784 mergeArgs.push(3785 createObjectExpression(dedupeProperties(properties), elementLoc)3786 )3787 properties = []3788 }3789 if (isBind) {3790 mergeArgs.push(exp)3791 } else {3792 // v-on="obj" => toHandlers(obj)3793 mergeArgs.push({3794 type: 14 /* JS_CALL_EXPRESSION */,3795 loc,3796 callee: context.helper(TO_HANDLERS),3797 arguments: [exp]3798 })3799 }3800 } else {3801 context.onError(3802 createCompilerError(3803 isBind3804 ? 33 /* X_V_BIND_NO_EXPRESSION */3805 : 34 /* X_V_ON_NO_EXPRESSION */,3806 loc3807 )3808 )3809 }3810 continue3811 }3812 const directiveTransform = context.directiveTransforms[name]3813 if (directiveTransform) {3814 // has built-in directive transform.3815 const { props, needRuntime } = directiveTransform(prop, node, context)3816 !ssr && props.forEach(analyzePatchFlag)3817 properties.push(...props)3818 if (needRuntime) {3819 runtimeDirectives.push(prop)3820 if (isSymbol(needRuntime)) {3821 directiveImportMap.set(prop, needRuntime)3822 }3823 }3824 } else {3825 // no built-in transform, this is a user custom directive.3826 runtimeDirectives.push(prop)3827 }3828 }3829 }3830 let propsExpression = undefined3831 // has v-bind="object" or v-on="object", wrap with mergeProps3832 if (mergeArgs.length) {3833 if (properties.length) {3834 mergeArgs.push(3835 createObjectExpression(dedupeProperties(properties), elementLoc)3836 )3837 }3838 if (mergeArgs.length > 1) {3839 propsExpression = createCallExpression(3840 context.helper(MERGE_PROPS),3841 mergeArgs,3842 elementLoc3843 )3844 } else {3845 // single v-bind with nothing else - no need for a mergeProps call3846 propsExpression = mergeArgs[0]3847 }3848 } else if (properties.length) {3849 propsExpression = createObjectExpression(3850 dedupeProperties(properties),3851 elementLoc3852 )3853 }3854 // patchFlag analysis3855 if (hasDynamicKeys) {3856 patchFlag |= 16 /* FULL_PROPS */3857 } else {3858 if (hasClassBinding) {3859 patchFlag |= 2 /* CLASS */3860 }3861 if (hasStyleBinding) {3862 patchFlag |= 4 /* STYLE */3863 }3864 if (dynamicPropNames.length) {3865 patchFlag |= 8 /* PROPS */3866 }3867 if (hasHydrationEventBinding) {3868 patchFlag |= 32 /* HYDRATE_EVENTS */3869 }3870 }3871 if (3872 (patchFlag === 0 || patchFlag === 32) /* HYDRATE_EVENTS */ &&3873 (hasRef || hasVnodeHook || runtimeDirectives.length > 0)3874 ) {3875 patchFlag |= 512 /* NEED_PATCH */3876 }3877 return {3878 props: propsExpression,3879 directives: runtimeDirectives,3880 patchFlag,3881 dynamicPropNames3882 }3883 }3884 // Dedupe props in an object literal.3885 // Literal duplicated attributes would have been warned during the parse phase,3886 // however, it's possible to encounter duplicated `onXXX` handlers with different3887 // modifiers. We also need to merge static and dynamic class / style attributes.3888 // - onXXX handlers / style: merge into array3889 // - class: merge into single expression with concatenation3890 function dedupeProperties(properties) {3891 // å并åç±»å±æ§3892 const knownProps = new Map()3893 const deduped = []3894 for (let i = 0; i < properties.length; i++) {3895 const prop = properties[i]3896 // å
许éå¤çå¨æå±æ§3897 if (prop.key.type === 8 /* COMPOUND_EXPRESSION */ || !prop.key.isStatic) {3898 deduped.push(prop)3899 continue3900 }3901 const name = prop.key.content3902 const existing = knownProps.get(name)3903 if (existing) {3904 // å并 style, class, onXxx3905 if (name === 'style' || name === 'class' || name.startsWith('on')) {3906 mergeAsArray(existing, prop)3907 }3908 // unexpected duplicate, should have emitted error during parse3909 } else {3910 // cache3911 knownProps.set(name, prop)3912 deduped.push(prop)3913 }3914 }3915 return deduped3916 }3917 function mergeAsArray(existing, incoming) {3918 if (existing.value.type === 17 /* JS_ARRAY_EXPRESSION */) {3919 existing.value.elements.push(incoming.value)3920 } else {3921 existing.value = createArrayExpression(3922 [existing.value, incoming.value],3923 existing.loc3924 )3925 }3926 }3927 function buildDirectiveArgs(dir, context) {3928 const dirArgs = []3929 const runtime = directiveImportMap.get(dir)3930 if (runtime) {3931 // built-in directive with runtime3932 dirArgs.push(context.helperString(runtime))3933 } else {3934 {3935 // inject statement for resolving directive3936 context.helper(RESOLVE_DIRECTIVE)3937 context.directives.add(dir.name)3938 dirArgs.push(toValidAssetId(dir.name, `directive`))3939 }3940 }3941 const { loc } = dir3942 if (dir.exp) dirArgs.push(dir.exp)3943 if (dir.arg) {3944 if (!dir.exp) {3945 dirArgs.push(`void 0`)3946 }3947 dirArgs.push(dir.arg)3948 }3949 if (Object.keys(dir.modifiers).length) {3950 if (!dir.arg) {3951 if (!dir.exp) {3952 dirArgs.push(`void 0`)3953 }3954 dirArgs.push(`void 0`)3955 }3956 const trueExpression = createSimpleExpression(`true`, false, loc)3957 dirArgs.push(3958 createObjectExpression(3959 dir.modifiers.map(modifier =>3960 createObjectProperty(modifier, trueExpression)3961 ),3962 loc3963 )3964 )3965 }3966 return createArrayExpression(dirArgs, dir.loc)3967 }3968 function stringifyDynamicPropNames(props) {3969 let propsNamesString = `[`3970 for (let i = 0, l = props.length; i < l; i++) {3971 propsNamesString += JSON.stringify(props[i])3972 if (i < l - 1) propsNamesString += ', '3973 }3974 return propsNamesString + `]`3975 }3976 const transformSlotOutlet = (node, context) => {3977 if (isSlotOutlet(node)) {3978 const { children, loc } = node3979 const { slotName, slotProps } = processSlotOutlet(node, context)3980 // å
容ï¼3981 // 1. $slots, æ°æ®æº3982 // 2. slotName, æ槽å3983 // 3. slotProps, æ槽å±æ§3984 // 4. children, æ槽çå©åèç¹3985 const slotArgs = [3986 context.prefixIdentifiers ? `_ctx.$slots` : `$slots`,3987 slotName3988 ]3989 // slot å±æ§3990 if (slotProps) {3991 slotArgs.push(slotProps)3992 }3993 if (children.length) {3994 if (!slotProps) {3995 slotArgs.push(`{}`)3996 }3997 slotArgs.push(createFunctionExpression([], children, false, false, loc))3998 }3999 node.codegenNode = createCallExpression(4000 context.helper(RENDER_SLOT),4001 slotArgs,4002 loc4003 )4004 }4005 }4006 function processSlotOutlet(node, context) {4007 let slotName = `"default"`4008 let slotProps = undefined4009 // ä¿åé name="" å±æ§çå
¶ä»å±æ§4010 const nonNameProps = []4011 for (let i = 0; i < node.props.length; i++) {4012 const p = node.props[i]4013 if (p.type === 6 /* ATTRIBUTE */) {4014 // éæå±æ§4015 if (p.value) {4016 if (p.name === 'name') {4017 slotName = JSON.stringify(p.value.content)4018 } else {4019 p.name = camelize(p.name)4020 nonNameProps.push(p)4021 }4022 }4023 } else {4024 // å¨æå±æ§4025 if (p.name === 'bind' && isBindKey(p.arg, 'name')) {4026 // <slot :name="xx"></slot>4027 if (p.exp) slotName = p.exp4028 } else {4029 // é name çå¨æå±æ§4030 if (p.name === 'bind' && p.arg && isStaticExp(p.arg)) {4031 p.arg.content = camelize(p.arg.content)4032 }4033 nonNameProps.push(p)4034 }4035 }4036 }4037 // ä¸é¢è§£æåº name åé name çå±æ§4038 if (nonNameProps.length > 0) {4039 const { props, directives } = buildProps(node, context, nonNameProps)4040 slotProps = props4041 if (directives.length) {4042 context.onError(4043 createCompilerError(4044 35 /* X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET */,4045 directives[0].loc4046 )4047 )4048 }4049 }4050 return {4051 slotName,4052 slotProps4053 }4054 }4055 const fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^\s*function(?:\s+[\w$]+)?\s*\(/4056 const transformOn = (dir, node, context, augmentor) => {4057 const { loc, modifiers, arg } = dir4058 if (!dir.exp && !modifiers.length) {4059 context.onError(createCompilerError(34 /* X_V_ON_NO_EXPRESSION */, loc))4060 }4061 let eventName4062 if (arg.type === 4 /* SIMPLE_EXPRESSION */) {4063 if (arg.isStatic) {4064 // v-on:click4065 const rawName = arg.content4066 // for all event listeners, auto convert it to camelCase. See issue #22494067 eventName = createSimpleExpression(4068 toHandlerKey(camelize(rawName)),4069 true,4070 arg.loc4071 )4072 } else {4073 // #23884074 // å¨æäºä»¶åæ° <div v-on:[eventName] ...4075 eventName = createCompoundExpression([4076 `${context.helperString(TO_HANDLER_KEY)}(`,4077 arg,4078 `)`4079 ])4080 }4081 } else {4082 // already a compound expression.4083 eventName = arg4084 eventName.children.unshift(`${context.helperString(TO_HANDLER_KEY)}(`)4085 eventName.children.push(`)`)4086 }4087 // handler processing4088 let exp = dir.exp4089 if (exp && !exp.content.trim()) {4090 exp = undefined4091 }4092 let shouldCache = context.cacheHandlers && !exp4093 if (exp) {4094 const isMemberExp = isMemberExpression(exp.content)4095 const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content))4096 // å«å¤ä¸ªè¡¨è¾¾å¼4097 const hasMultipleStatements = exp.content.includes(';')4098 {4099 validateBrowserExpression(exp, context, false, hasMultipleStatements)4100 }4101 if (isInlineStatement || (shouldCache && isMemberExp)) {4102 // wrap inline statement in a function expression4103 exp = createCompoundExpression([4104 `${isInlineStatement ? `$event` : `${``}(...args)`} => ${4105 hasMultipleStatements ? `{` : `(`4106 }`,4107 exp,4108 hasMultipleStatements ? `}` : `)`4109 ])4110 }4111 }4112 let ret = {4113 props: [...
compiler-dom.esm-browser.js
Source:compiler-dom.esm-browser.js
...2429 * Validate a non-prefixed expression.2430 * This is only called when using the in-browser runtime compiler since it2431 * doesn't prefix expressions.2432 */2433function validateBrowserExpression(node, context, asParams = false, asRawStatements = false) {2434 const exp = node.content;2435 // empty expressions are validated per-directive since some directives2436 // do allow empty expressions.2437 if (!exp.trim()) {2438 return;2439 }2440 try {2441 new Function(asRawStatements2442 ? ` ${exp} `2443 : `return ${asParams ? `(${exp}) => {}` : `(${exp})`}`);2444 }2445 catch (e) {2446 let message = e.message;2447 const keywordMatch = exp2448 .replace(stripStringRE, '')2449 .match(prohibitedKeywordRE);2450 if (keywordMatch) {2451 message = `avoid using JavaScript keyword as property name: "${keywordMatch[0]}"`;2452 }2453 context.onError(createCompilerError(43 /* X_INVALID_EXPRESSION */, node.loc, undefined, message));2454 }2455}2456const transformExpression = (node, context) => {2457 if (node.type === 5 /* INTERPOLATION */) {2458 node.content = processExpression(node.content, context);2459 }2460 else if (node.type === 1 /* ELEMENT */) {2461 // handle directives on element2462 for (let i = 0; i < node.props.length; i++) {2463 const dir = node.props[i];2464 // do not process for v-on & v-for since they are special handled2465 if (dir.type === 7 /* DIRECTIVE */ && dir.name !== 'for') {2466 const exp = dir.exp;2467 const arg = dir.arg;2468 // do not process exp if this is v-on:arg - we need special handling2469 // for wrapping inline statements.2470 if (exp &&2471 exp.type === 4 /* SIMPLE_EXPRESSION */ &&2472 !(dir.name === 'on' && arg)) {2473 dir.exp = processExpression(exp, context, 2474 // slot args must be processed as function params2475 dir.name === 'slot');2476 }2477 if (arg && arg.type === 4 /* SIMPLE_EXPRESSION */ && !arg.isStatic) {2478 dir.arg = processExpression(arg, context);2479 }2480 }2481 }2482 }2483};2484// Important: since this function uses Node.js only dependencies, it should2485// always be used with a leading !true check so that it can be2486// tree-shaken from the browser build.2487function processExpression(node, context, 2488// some expressions like v-slot props & v-for aliases should be parsed as2489// function params2490asParams = false, 2491// v-on handler values may contain multiple statements2492asRawStatements = false) {2493 {2494 {2495 // simple in-browser validation (same logic in 2.x)2496 validateBrowserExpression(node, context, asParams, asRawStatements);2497 }2498 return node;2499 }2500}2501const transformIf = createStructuralDirectiveTransform(/^(if|else|else-if)$/, (node, dir, context) => {2502 return processIf(node, dir, context, (ifNode, branch, isRoot) => {2503 // #1587: We need to dynamically increment the key based on the current2504 // node's sibling nodes, since chained v-if/else branches are2505 // rendered at the same depth2506 const siblings = context.parent.children;2507 let i = siblings.indexOf(ifNode);2508 let key = 0;2509 while (i-- >= 0) {2510 const sibling = siblings[i];2511 if (sibling && sibling.type === 9 /* IF */) {2512 key += sibling.branches.length;2513 }2514 }2515 // Exit callback. Complete the codegenNode when all children have been2516 // transformed.2517 return () => {2518 if (isRoot) {2519 ifNode.codegenNode = createCodegenNodeForBranch(branch, key, context);2520 }2521 else {2522 // attach this branch's codegen node to the v-if root.2523 const parentCondition = getParentCondition(ifNode.codegenNode);2524 parentCondition.alternate = createCodegenNodeForBranch(branch, key + ifNode.branches.length - 1, context);2525 }2526 };2527 });2528});2529// target-agnostic transform used for both Client and SSR2530function processIf(node, dir, context, processCodegen) {2531 if (dir.name !== 'else' &&2532 (!dir.exp || !dir.exp.content.trim())) {2533 const loc = dir.exp ? dir.exp.loc : node.loc;2534 context.onError(createCompilerError(27 /* X_V_IF_NO_EXPRESSION */, dir.loc));2535 dir.exp = createSimpleExpression(`true`, false, loc);2536 }2537 if ( dir.exp) {2538 validateBrowserExpression(dir.exp, context);2539 }2540 if (dir.name === 'if') {2541 const branch = createIfBranch(node, dir);2542 const ifNode = {2543 type: 9 /* IF */,2544 loc: node.loc,2545 branches: [branch]2546 };2547 context.replaceNode(ifNode);2548 if (processCodegen) {2549 return processCodegen(ifNode, branch, true);2550 }2551 }2552 else {2553 // locate the adjacent v-if2554 const siblings = context.parent.children;2555 const comments = [];2556 let i = siblings.indexOf(node);2557 while (i-- >= -1) {2558 const sibling = siblings[i];2559 if ( sibling && sibling.type === 3 /* COMMENT */) {2560 context.removeNode(sibling);2561 comments.unshift(sibling);2562 continue;2563 }2564 if (sibling &&2565 sibling.type === 2 /* TEXT */ &&2566 !sibling.content.trim().length) {2567 context.removeNode(sibling);2568 continue;2569 }2570 if (sibling && sibling.type === 9 /* IF */) {2571 // move the node to the if node's branches2572 context.removeNode();2573 const branch = createIfBranch(node, dir);2574 if ( comments.length) {2575 branch.children = [...comments, ...branch.children];2576 }2577 // check if user is forcing same key on different branches2578 {2579 const key = branch.userKey;2580 if (key) {2581 sibling.branches.forEach(({ userKey }) => {2582 if (isSameKey(userKey, key)) {2583 context.onError(createCompilerError(28 /* X_V_IF_SAME_KEY */, branch.userKey.loc));2584 }2585 });2586 }2587 }2588 sibling.branches.push(branch);2589 const onExit = processCodegen && processCodegen(sibling, branch, false);2590 // since the branch was removed, it will not be traversed.2591 // make sure to traverse here.2592 traverseNode(branch, context);2593 // call on exit2594 if (onExit)2595 onExit();2596 // make sure to reset currentNode after traversal to indicate this2597 // node has been removed.2598 context.currentNode = null;2599 }2600 else {2601 context.onError(createCompilerError(29 /* X_V_ELSE_NO_ADJACENT_IF */, node.loc));2602 }2603 break;2604 }2605 }2606}2607function createIfBranch(node, dir) {2608 return {2609 type: 10 /* IF_BRANCH */,2610 loc: node.loc,2611 condition: dir.name === 'else' ? undefined : dir.exp,2612 children: node.tagType === 3 /* TEMPLATE */ && !findDir(node, 'for')2613 ? node.children2614 : [node],2615 userKey: findProp(node, `key`)2616 };2617}2618function createCodegenNodeForBranch(branch, keyIndex, context) {2619 if (branch.condition) {2620 return createConditionalExpression(branch.condition, createChildrenCodegenNode(branch, keyIndex, context), 2621 // make sure to pass in asBlock: true so that the comment node call2622 // closes the current block.2623 createCallExpression(context.helper(CREATE_COMMENT), [2624 '"v-if"' ,2625 'true'2626 ]));2627 }2628 else {2629 return createChildrenCodegenNode(branch, keyIndex, context);2630 }2631}2632function createChildrenCodegenNode(branch, keyIndex, context) {2633 const { helper } = context;2634 const keyProperty = createObjectProperty(`key`, createSimpleExpression(`${keyIndex}`, false, locStub, 2 /* CAN_HOIST */));2635 const { children } = branch;2636 const firstChild = children[0];2637 const needFragmentWrapper = children.length !== 1 || firstChild.type !== 1 /* ELEMENT */;2638 if (needFragmentWrapper) {2639 if (children.length === 1 && firstChild.type === 11 /* FOR */) {2640 // optimize away nested fragments when child is a ForNode2641 const vnodeCall = firstChild.codegenNode;2642 injectProp(vnodeCall, keyProperty, context);2643 return vnodeCall;2644 }2645 else {2646 return createVNodeCall(context, helper(FRAGMENT), createObjectExpression([keyProperty]), children, 64 /* STABLE_FRAGMENT */ +2647 ( ` /* ${PatchFlagNames[64 /* STABLE_FRAGMENT */]} */`2648 ), undefined, undefined, true, false, branch.loc);2649 }2650 }2651 else {2652 const vnodeCall = firstChild2653 .codegenNode;2654 // Change createVNode to createBlock.2655 if (vnodeCall.type === 13 /* VNODE_CALL */) {2656 vnodeCall.isBlock = true;2657 helper(OPEN_BLOCK);2658 helper(CREATE_BLOCK);2659 }2660 // inject branch key2661 injectProp(vnodeCall, keyProperty, context);2662 return vnodeCall;2663 }2664}2665function isSameKey(a, b) {2666 if (!a || a.type !== b.type) {2667 return false;2668 }2669 if (a.type === 6 /* ATTRIBUTE */) {2670 if (a.value.content !== b.value.content) {2671 return false;2672 }2673 }2674 else {2675 // directive2676 const exp = a.exp;2677 const branchExp = b.exp;2678 if (exp.type !== branchExp.type) {2679 return false;2680 }2681 if (exp.type !== 4 /* SIMPLE_EXPRESSION */ ||2682 (exp.isStatic !== branchExp.isStatic ||2683 exp.content !== branchExp.content)) {2684 return false;2685 }2686 }2687 return true;2688}2689function getParentCondition(node) {2690 while (true) {2691 if (node.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {2692 if (node.alternate.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {2693 node = node.alternate;2694 }2695 else {2696 return node;2697 }2698 }2699 else if (node.type === 20 /* JS_CACHE_EXPRESSION */) {2700 node = node.value;2701 }2702 }2703}2704const transformFor = createStructuralDirectiveTransform('for', (node, dir, context) => {2705 const { helper } = context;2706 return processFor(node, dir, context, forNode => {2707 // create the loop render function expression now, and add the2708 // iterator on exit after all children have been traversed2709 const renderExp = createCallExpression(helper(RENDER_LIST), [2710 forNode.source2711 ]);2712 const keyProp = findProp(node, `key`);2713 const keyProperty = keyProp2714 ? createObjectProperty(`key`, keyProp.type === 6 /* ATTRIBUTE */2715 ? createSimpleExpression(keyProp.value.content, true)2716 : keyProp.exp)2717 : null;2718 const isStableFragment = forNode.source.type === 4 /* SIMPLE_EXPRESSION */ &&2719 forNode.source.constType > 0;2720 const fragmentFlag = isStableFragment2721 ? 64 /* STABLE_FRAGMENT */2722 : keyProp2723 ? 128 /* KEYED_FRAGMENT */2724 : 256 /* UNKEYED_FRAGMENT */;2725 forNode.codegenNode = createVNodeCall(context, helper(FRAGMENT), undefined, renderExp, fragmentFlag +2726 ( ` /* ${PatchFlagNames[fragmentFlag]} */` ), undefined, undefined, true /* isBlock */, !isStableFragment /* disableTracking */, node.loc);2727 return () => {2728 // finish the codegen now that all children have been traversed2729 let childBlock;2730 const isTemplate = isTemplateNode(node);2731 const { children } = forNode;2732 // check <template v-for> key placement2733 if ( isTemplate) {2734 node.children.some(c => {2735 if (c.type === 1 /* ELEMENT */) {2736 const key = findProp(c, 'key');2737 if (key) {2738 context.onError(createCompilerError(32 /* X_V_FOR_TEMPLATE_KEY_PLACEMENT */, key.loc));2739 return true;2740 }2741 }2742 });2743 }2744 const needFragmentWrapper = children.length !== 1 || children[0].type !== 1 /* ELEMENT */;2745 const slotOutlet = isSlotOutlet(node)2746 ? node2747 : isTemplate &&2748 node.children.length === 1 &&2749 isSlotOutlet(node.children[0])2750 ? node.children[0] // api-extractor somehow fails to infer this2751 : null;2752 if (slotOutlet) {2753 // <slot v-for="..."> or <template v-for="..."><slot/></template>2754 childBlock = slotOutlet.codegenNode;2755 if (isTemplate && keyProperty) {2756 // <template v-for="..." :key="..."><slot/></template>2757 // we need to inject the key to the renderSlot() call.2758 // the props for renderSlot is passed as the 3rd argument.2759 injectProp(childBlock, keyProperty, context);2760 }2761 }2762 else if (needFragmentWrapper) {2763 // <template v-for="..."> with text or multi-elements2764 // should generate a fragment block for each loop2765 childBlock = createVNodeCall(context, helper(FRAGMENT), keyProperty ? createObjectExpression([keyProperty]) : undefined, node.children, 64 /* STABLE_FRAGMENT */ +2766 ( ` /* ${PatchFlagNames[64 /* STABLE_FRAGMENT */]} */`2767 ), undefined, undefined, true);2768 }2769 else {2770 // Normal element v-for. Directly use the child's codegenNode2771 // but mark it as a block.2772 childBlock = children[0]2773 .codegenNode;2774 if (isTemplate && keyProperty) {2775 injectProp(childBlock, keyProperty, context);2776 }2777 childBlock.isBlock = !isStableFragment;2778 if (childBlock.isBlock) {2779 helper(OPEN_BLOCK);2780 helper(CREATE_BLOCK);2781 }2782 else {2783 helper(CREATE_VNODE);2784 }2785 }2786 renderExp.arguments.push(createFunctionExpression(createForLoopParams(forNode.parseResult), childBlock, true /* force newline */));2787 };2788 });2789});2790// target-agnostic transform used for both Client and SSR2791function processFor(node, dir, context, processCodegen) {2792 if (!dir.exp) {2793 context.onError(createCompilerError(30 /* X_V_FOR_NO_EXPRESSION */, dir.loc));2794 return;2795 }2796 const parseResult = parseForExpression(2797 // can only be simple expression because vFor transform is applied2798 // before expression transform.2799 dir.exp, context);2800 if (!parseResult) {2801 context.onError(createCompilerError(31 /* X_V_FOR_MALFORMED_EXPRESSION */, dir.loc));2802 return;2803 }2804 const { addIdentifiers, removeIdentifiers, scopes } = context;2805 const { source, value, key, index } = parseResult;2806 const forNode = {2807 type: 11 /* FOR */,2808 loc: dir.loc,2809 source,2810 valueAlias: value,2811 keyAlias: key,2812 objectIndexAlias: index,2813 parseResult,2814 children: isTemplateNode(node) ? node.children : [node]2815 };2816 context.replaceNode(forNode);2817 // bookkeeping2818 scopes.vFor++;2819 const onExit = processCodegen && processCodegen(forNode);2820 return () => {2821 scopes.vFor--;2822 if (onExit)2823 onExit();2824 };2825}2826const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/;2827// This regex doesn't cover the case if key or index aliases have destructuring,2828// but those do not make sense in the first place, so this works in practice.2829const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/;2830const stripParensRE = /^\(|\)$/g;2831function parseForExpression(input, context) {2832 const loc = input.loc;2833 const exp = input.content;2834 const inMatch = exp.match(forAliasRE);2835 if (!inMatch)2836 return;2837 const [, LHS, RHS] = inMatch;2838 const result = {2839 source: createAliasExpression(loc, RHS.trim(), exp.indexOf(RHS, LHS.length)),2840 value: undefined,2841 key: undefined,2842 index: undefined2843 };2844 {2845 validateBrowserExpression(result.source, context);2846 }2847 let valueContent = LHS.trim()2848 .replace(stripParensRE, '')2849 .trim();2850 const trimmedOffset = LHS.indexOf(valueContent);2851 const iteratorMatch = valueContent.match(forIteratorRE);2852 if (iteratorMatch) {2853 valueContent = valueContent.replace(forIteratorRE, '').trim();2854 const keyContent = iteratorMatch[1].trim();2855 let keyOffset;2856 if (keyContent) {2857 keyOffset = exp.indexOf(keyContent, trimmedOffset + valueContent.length);2858 result.key = createAliasExpression(loc, keyContent, keyOffset);2859 {2860 validateBrowserExpression(result.key, context, true);2861 }2862 }2863 if (iteratorMatch[2]) {2864 const indexContent = iteratorMatch[2].trim();2865 if (indexContent) {2866 result.index = createAliasExpression(loc, indexContent, exp.indexOf(indexContent, result.key2867 ? keyOffset + keyContent.length2868 : trimmedOffset + valueContent.length));2869 {2870 validateBrowserExpression(result.index, context, true);2871 }2872 }2873 }2874 }2875 if (valueContent) {2876 result.value = createAliasExpression(loc, valueContent, trimmedOffset);2877 {2878 validateBrowserExpression(result.value, context, true);2879 }2880 }2881 return result;2882}2883function createAliasExpression(range, content, offset) {2884 return createSimpleExpression(content, false, getInnerRange(range, offset, content.length));2885}2886function createForLoopParams({ value, key, index }) {2887 const params = [];2888 if (value) {2889 params.push(value);2890 }2891 if (key) {2892 if (!value) {2893 params.push(createSimpleExpression(`_`, false));2894 }2895 params.push(key);2896 }2897 if (index) {2898 if (!key) {2899 if (!value) {2900 params.push(createSimpleExpression(`_`, false));2901 }2902 params.push(createSimpleExpression(`__`, false));2903 }2904 params.push(index);2905 }2906 return params;2907}2908const defaultFallback = createSimpleExpression(`undefined`, false);2909// A NodeTransform that:2910// 1. Tracks scope identifiers for scoped slots so that they don't get prefixed2911// by transformExpression. This is only applied in non-browser builds with2912// { prefixIdentifiers: true }.2913// 2. Track v-slot depths so that we know a slot is inside another slot.2914// Note the exit callback is executed before buildSlots() on the same node,2915// so only nested slots see positive numbers.2916const trackSlotScopes = (node, context) => {2917 if (node.type === 1 /* ELEMENT */ &&2918 (node.tagType === 1 /* COMPONENT */ ||2919 node.tagType === 3 /* TEMPLATE */)) {2920 // We are only checking non-empty v-slot here2921 // since we only care about slots that introduce scope variables.2922 const vSlot = findDir(node, 'slot');2923 if (vSlot) {2924 const slotProps = vSlot.exp;2925 context.scopes.vSlot++;2926 return () => {2927 context.scopes.vSlot--;2928 };2929 }2930 }2931};2932// A NodeTransform that tracks scope identifiers for scoped slots with v-for.2933// This transform is only applied in non-browser builds with { prefixIdentifiers: true }2934const trackVForSlotScopes = (node, context) => {2935 let vFor;2936 if (isTemplateNode(node) &&2937 node.props.some(isVSlot) &&2938 (vFor = findDir(node, 'for'))) {2939 const result = (vFor.parseResult = parseForExpression(vFor.exp, context));2940 if (result) {2941 const { value, key, index } = result;2942 const { addIdentifiers, removeIdentifiers } = context;2943 value && addIdentifiers(value);2944 key && addIdentifiers(key);2945 index && addIdentifiers(index);2946 return () => {2947 value && removeIdentifiers(value);2948 key && removeIdentifiers(key);2949 index && removeIdentifiers(index);2950 };2951 }2952 }2953};2954const buildClientSlotFn = (props, children, loc) => createFunctionExpression(props, children, false /* newline */, true /* isSlot */, children.length ? children[0].loc : loc);2955// Instead of being a DirectiveTransform, v-slot processing is called during2956// transformElement to build the slots object for a component.2957function buildSlots(node, context, buildSlotFn = buildClientSlotFn) {2958 context.helper(WITH_CTX);2959 const { children, loc } = node;2960 const slotsProperties = [];2961 const dynamicSlots = [];2962 const buildDefaultSlotProperty = (props, children) => createObjectProperty(`default`, buildSlotFn(props, children, loc));2963 // If the slot is inside a v-for or another v-slot, force it to be dynamic2964 // since it likely uses a scope variable.2965 let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0;2966 // 1. Check for slot with slotProps on component itself.2967 // <Comp v-slot="{ prop }"/>2968 const onComponentSlot = findDir(node, 'slot', true);2969 if (onComponentSlot) {2970 const { arg, exp } = onComponentSlot;2971 if (arg && !isStaticExp(arg)) {2972 hasDynamicSlots = true;2973 }2974 slotsProperties.push(createObjectProperty(arg || createSimpleExpression('default', true), buildSlotFn(exp, children, loc)));2975 }2976 // 2. Iterate through children and check for template slots2977 // <template v-slot:foo="{ prop }">2978 let hasTemplateSlots = false;2979 let hasNamedDefaultSlot = false;2980 const implicitDefaultChildren = [];2981 const seenSlotNames = new Set();2982 for (let i = 0; i < children.length; i++) {2983 const slotElement = children[i];2984 let slotDir;2985 if (!isTemplateNode(slotElement) ||2986 !(slotDir = findDir(slotElement, 'slot', true))) {2987 // not a <template v-slot>, skip.2988 if (slotElement.type !== 3 /* COMMENT */) {2989 implicitDefaultChildren.push(slotElement);2990 }2991 continue;2992 }2993 if (onComponentSlot) {2994 // already has on-component slot - this is incorrect usage.2995 context.onError(createCompilerError(36 /* X_V_SLOT_MIXED_SLOT_USAGE */, slotDir.loc));2996 break;2997 }2998 hasTemplateSlots = true;2999 const { children: slotChildren, loc: slotLoc } = slotElement;3000 const { arg: slotName = createSimpleExpression(`default`, true), exp: slotProps, loc: dirLoc } = slotDir;3001 // check if name is dynamic.3002 let staticSlotName;3003 if (isStaticExp(slotName)) {3004 staticSlotName = slotName ? slotName.content : `default`;3005 }3006 else {3007 hasDynamicSlots = true;3008 }3009 const slotFunction = buildSlotFn(slotProps, slotChildren, slotLoc);3010 // check if this slot is conditional (v-if/v-for)3011 let vIf;3012 let vElse;3013 let vFor;3014 if ((vIf = findDir(slotElement, 'if'))) {3015 hasDynamicSlots = true;3016 dynamicSlots.push(createConditionalExpression(vIf.exp, buildDynamicSlot(slotName, slotFunction), defaultFallback));3017 }3018 else if ((vElse = findDir(slotElement, /^else(-if)?$/, true /* allowEmpty */))) {3019 // find adjacent v-if3020 let j = i;3021 let prev;3022 while (j--) {3023 prev = children[j];3024 if (prev.type !== 3 /* COMMENT */) {3025 break;3026 }3027 }3028 if (prev && isTemplateNode(prev) && findDir(prev, 'if')) {3029 // remove node3030 children.splice(i, 1);3031 i--;3032 // attach this slot to previous conditional3033 let conditional = dynamicSlots[dynamicSlots.length - 1];3034 while (conditional.alternate.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {3035 conditional = conditional.alternate;3036 }3037 conditional.alternate = vElse.exp3038 ? createConditionalExpression(vElse.exp, buildDynamicSlot(slotName, slotFunction), defaultFallback)3039 : buildDynamicSlot(slotName, slotFunction);3040 }3041 else {3042 context.onError(createCompilerError(29 /* X_V_ELSE_NO_ADJACENT_IF */, vElse.loc));3043 }3044 }3045 else if ((vFor = findDir(slotElement, 'for'))) {3046 hasDynamicSlots = true;3047 const parseResult = vFor.parseResult ||3048 parseForExpression(vFor.exp, context);3049 if (parseResult) {3050 // Render the dynamic slots as an array and add it to the createSlot()3051 // args. The runtime knows how to handle it appropriately.3052 dynamicSlots.push(createCallExpression(context.helper(RENDER_LIST), [3053 parseResult.source,3054 createFunctionExpression(createForLoopParams(parseResult), buildDynamicSlot(slotName, slotFunction), true /* force newline */)3055 ]));3056 }3057 else {3058 context.onError(createCompilerError(31 /* X_V_FOR_MALFORMED_EXPRESSION */, vFor.loc));3059 }3060 }3061 else {3062 // check duplicate static names3063 if (staticSlotName) {3064 if (seenSlotNames.has(staticSlotName)) {3065 context.onError(createCompilerError(37 /* X_V_SLOT_DUPLICATE_SLOT_NAMES */, dirLoc));3066 continue;3067 }3068 seenSlotNames.add(staticSlotName);3069 if (staticSlotName === 'default') {3070 hasNamedDefaultSlot = true;3071 }3072 }3073 slotsProperties.push(createObjectProperty(slotName, slotFunction));3074 }3075 }3076 if (!onComponentSlot) {3077 if (!hasTemplateSlots) {3078 // implicit default slot (on component)3079 slotsProperties.push(buildDefaultSlotProperty(undefined, children));3080 }3081 else if (implicitDefaultChildren.length) {3082 // implicit default slot (mixed with named slots)3083 if (hasNamedDefaultSlot) {3084 context.onError(createCompilerError(38 /* X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN */, implicitDefaultChildren[0].loc));3085 }3086 else {3087 slotsProperties.push(buildDefaultSlotProperty(undefined, implicitDefaultChildren));3088 }3089 }3090 }3091 const slotFlag = hasDynamicSlots3092 ? 2 /* DYNAMIC */3093 : hasForwardedSlots(node.children)3094 ? 3 /* FORWARDED */3095 : 1 /* STABLE */;3096 let slots = createObjectExpression(slotsProperties.concat(createObjectProperty(`_`, 3097 // 2 = compiled but dynamic = can skip normalization, but must run diff3098 // 1 = compiled and static = can skip normalization AND diff as optimized3099 createSimpleExpression(slotFlag + ( ` /* ${slotFlagsText[slotFlag]} */` ), false))), loc);3100 if (dynamicSlots.length) {3101 slots = createCallExpression(context.helper(CREATE_SLOTS), [3102 slots,3103 createArrayExpression(dynamicSlots)3104 ]);3105 }3106 return {3107 slots,3108 hasDynamicSlots3109 };3110}3111function buildDynamicSlot(name, fn) {3112 return createObjectExpression([3113 createObjectProperty(`name`, name),3114 createObjectProperty(`fn`, fn)3115 ]);3116}3117function hasForwardedSlots(children) {3118 for (let i = 0; i < children.length; i++) {3119 const child = children[i];3120 if (child.type === 1 /* ELEMENT */) {3121 if (child.tagType === 2 /* SLOT */ ||3122 (child.tagType === 0 /* ELEMENT */ &&3123 hasForwardedSlots(child.children))) {3124 return true;3125 }3126 }3127 }3128 return false;3129}3130// some directive transforms (e.g. v-model) may return a symbol for runtime3131// import, which should be used instead of a resolveDirective call.3132const directiveImportMap = new WeakMap();3133// generate a JavaScript AST for this element's codegen3134const transformElement = (node, context) => {3135 if (!(node.type === 1 /* ELEMENT */ &&3136 (node.tagType === 0 /* ELEMENT */ ||3137 node.tagType === 1 /* COMPONENT */))) {3138 return;3139 }3140 // perform the work on exit, after all child expressions have been3141 // processed and merged.3142 return function postTransformElement() {3143 const { tag, props } = node;3144 const isComponent = node.tagType === 1 /* COMPONENT */;3145 // The goal of the transform is to create a codegenNode implementing the3146 // VNodeCall interface.3147 const vnodeTag = isComponent3148 ? resolveComponentType(node, context)3149 : `"${tag}"`;3150 const isDynamicComponent = isObject(vnodeTag) && vnodeTag.callee === RESOLVE_DYNAMIC_COMPONENT;3151 let vnodeProps;3152 let vnodeChildren;3153 let vnodePatchFlag;3154 let patchFlag = 0;3155 let vnodeDynamicProps;3156 let dynamicPropNames;3157 let vnodeDirectives;3158 let shouldUseBlock = 3159 // dynamic component may resolve to plain elements3160 isDynamicComponent ||3161 vnodeTag === TELEPORT ||3162 vnodeTag === SUSPENSE ||3163 (!isComponent &&3164 // <svg> and <foreignObject> must be forced into blocks so that block3165 // updates inside get proper isSVG flag at runtime. (#639, #643)3166 // This is technically web-specific, but splitting the logic out of core3167 // leads to too much unnecessary complexity.3168 (tag === 'svg' ||3169 tag === 'foreignObject' ||3170 // #938: elements with dynamic keys should be forced into blocks3171 findProp(node, 'key', true)));3172 // props3173 if (props.length > 0) {3174 const propsBuildResult = buildProps(node, context);3175 vnodeProps = propsBuildResult.props;3176 patchFlag = propsBuildResult.patchFlag;3177 dynamicPropNames = propsBuildResult.dynamicPropNames;3178 const directives = propsBuildResult.directives;3179 vnodeDirectives =3180 directives && directives.length3181 ? createArrayExpression(directives.map(dir => buildDirectiveArgs(dir, context)))3182 : undefined;3183 }3184 // children3185 if (node.children.length > 0) {3186 if (vnodeTag === KEEP_ALIVE) {3187 // Although a built-in component, we compile KeepAlive with raw children3188 // instead of slot functions so that it can be used inside Transition3189 // or other Transition-wrapping HOCs.3190 // To ensure correct updates with block optimizations, we need to:3191 // 1. Force keep-alive into a block. This avoids its children being3192 // collected by a parent block.3193 shouldUseBlock = true;3194 // 2. Force keep-alive to always be updated, since it uses raw children.3195 patchFlag |= 1024 /* DYNAMIC_SLOTS */;3196 if ( node.children.length > 1) {3197 context.onError(createCompilerError(44 /* X_KEEP_ALIVE_INVALID_CHILDREN */, {3198 start: node.children[0].loc.start,3199 end: node.children[node.children.length - 1].loc.end,3200 source: ''3201 }));3202 }3203 }3204 const shouldBuildAsSlots = isComponent &&3205 // Teleport is not a real component and has dedicated runtime handling3206 vnodeTag !== TELEPORT &&3207 // explained above.3208 vnodeTag !== KEEP_ALIVE;3209 if (shouldBuildAsSlots) {3210 const { slots, hasDynamicSlots } = buildSlots(node, context);3211 vnodeChildren = slots;3212 if (hasDynamicSlots) {3213 patchFlag |= 1024 /* DYNAMIC_SLOTS */;3214 }3215 }3216 else if (node.children.length === 1 && vnodeTag !== TELEPORT) {3217 const child = node.children[0];3218 const type = child.type;3219 // check for dynamic text children3220 const hasDynamicTextChild = type === 5 /* INTERPOLATION */ ||3221 type === 8 /* COMPOUND_EXPRESSION */;3222 if (hasDynamicTextChild &&3223 getConstantType(child, context) === 0 /* NOT_CONSTANT */) {3224 patchFlag |= 1 /* TEXT */;3225 }3226 // pass directly if the only child is a text node3227 // (plain / interpolation / expression)3228 if (hasDynamicTextChild || type === 2 /* TEXT */) {3229 vnodeChildren = child;3230 }3231 else {3232 vnodeChildren = node.children;3233 }3234 }3235 else {3236 vnodeChildren = node.children;3237 }3238 }3239 // patchFlag & dynamicPropNames3240 if (patchFlag !== 0) {3241 {3242 if (patchFlag < 0) {3243 // special flags (negative and mutually exclusive)3244 vnodePatchFlag = patchFlag + ` /* ${PatchFlagNames[patchFlag]} */`;3245 }3246 else {3247 // bitwise flags3248 const flagNames = Object.keys(PatchFlagNames)3249 .map(Number)3250 .filter(n => n > 0 && patchFlag & n)3251 .map(n => PatchFlagNames[n])3252 .join(`, `);3253 vnodePatchFlag = patchFlag + ` /* ${flagNames} */`;3254 }3255 }3256 if (dynamicPropNames && dynamicPropNames.length) {3257 vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames);3258 }3259 }3260 node.codegenNode = createVNodeCall(context, vnodeTag, vnodeProps, vnodeChildren, vnodePatchFlag, vnodeDynamicProps, vnodeDirectives, !!shouldUseBlock, false /* disableTracking */, node.loc);3261 };3262};3263function resolveComponentType(node, context, ssr = false) {3264 const { tag } = node;3265 // 1. dynamic component3266 const isProp = node.tag === 'component' ? findProp(node, 'is') : findDir(node, 'is');3267 if (isProp) {3268 const exp = isProp.type === 6 /* ATTRIBUTE */3269 ? isProp.value && createSimpleExpression(isProp.value.content, true)3270 : isProp.exp;3271 if (exp) {3272 return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [3273 exp3274 ]);3275 }3276 }3277 // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)3278 const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);3279 if (builtIn) {3280 // built-ins are simply fallthroughs / have special handling during ssr3281 // so we don't need to import their runtime equivalents3282 if (!ssr)3283 context.helper(builtIn);3284 return builtIn;3285 }3286 // 5. user component (resolve)3287 context.helper(RESOLVE_COMPONENT);3288 context.components.add(tag);3289 return toValidAssetId(tag, `component`);3290}3291function buildProps(node, context, props = node.props, ssr = false) {3292 const { tag, loc: elementLoc } = node;3293 const isComponent = node.tagType === 1 /* COMPONENT */;3294 let properties = [];3295 const mergeArgs = [];3296 const runtimeDirectives = [];3297 // patchFlag analysis3298 let patchFlag = 0;3299 let hasRef = false;3300 let hasClassBinding = false;3301 let hasStyleBinding = false;3302 let hasHydrationEventBinding = false;3303 let hasDynamicKeys = false;3304 let hasVnodeHook = false;3305 const dynamicPropNames = [];3306 const analyzePatchFlag = ({ key, value }) => {3307 if (isStaticExp(key)) {3308 const name = key.content;3309 const isEventHandler = isOn(name);3310 if (!isComponent &&3311 isEventHandler &&3312 // omit the flag for click handlers because hydration gives click3313 // dedicated fast path.3314 name.toLowerCase() !== 'onclick' &&3315 // omit v-model handlers3316 name !== 'onUpdate:modelValue' &&3317 // omit onVnodeXXX hooks3318 !isReservedProp(name)) {3319 hasHydrationEventBinding = true;3320 }3321 if (isEventHandler && isReservedProp(name)) {3322 hasVnodeHook = true;3323 }3324 if (value.type === 20 /* JS_CACHE_EXPRESSION */ ||3325 ((value.type === 4 /* SIMPLE_EXPRESSION */ ||3326 value.type === 8 /* COMPOUND_EXPRESSION */) &&3327 getConstantType(value, context) > 0)) {3328 // skip if the prop is a cached handler or has constant value3329 return;3330 }3331 if (name === 'ref') {3332 hasRef = true;3333 }3334 else if (name === 'class' && !isComponent) {3335 hasClassBinding = true;3336 }3337 else if (name === 'style' && !isComponent) {3338 hasStyleBinding = true;3339 }3340 else if (name !== 'key' && !dynamicPropNames.includes(name)) {3341 dynamicPropNames.push(name);3342 }3343 }3344 else {3345 hasDynamicKeys = true;3346 }3347 };3348 for (let i = 0; i < props.length; i++) {3349 // static attribute3350 const prop = props[i];3351 if (prop.type === 6 /* ATTRIBUTE */) {3352 const { loc, name, value } = prop;3353 let isStatic = true;3354 if (name === 'ref') {3355 hasRef = true;3356 }3357 // skip :is on <component>3358 if (name === 'is' && tag === 'component') {3359 continue;3360 }3361 properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', isStatic, value ? value.loc : loc)));3362 }3363 else {3364 // directives3365 const { name, arg, exp, loc } = prop;3366 const isBind = name === 'bind';3367 const isOn = name === 'on';3368 // skip v-slot - it is handled by its dedicated transform.3369 if (name === 'slot') {3370 if (!isComponent) {3371 context.onError(createCompilerError(39 /* X_V_SLOT_MISPLACED */, loc));3372 }3373 continue;3374 }3375 // skip v-once - it is handled by its dedicated transform.3376 if (name === 'once') {3377 continue;3378 }3379 // skip v-is and :is on <component>3380 if (name === 'is' ||3381 (isBind && tag === 'component' && isBindKey(arg, 'is'))) {3382 continue;3383 }3384 // skip v-on in SSR compilation3385 if (isOn && ssr) {3386 continue;3387 }3388 // special case for v-bind and v-on with no argument3389 if (!arg && (isBind || isOn)) {3390 hasDynamicKeys = true;3391 if (exp) {3392 if (properties.length) {3393 mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));3394 properties = [];3395 }3396 if (isBind) {3397 mergeArgs.push(exp);3398 }3399 else {3400 // v-on="obj" -> toHandlers(obj)3401 mergeArgs.push({3402 type: 14 /* JS_CALL_EXPRESSION */,3403 loc,3404 callee: context.helper(TO_HANDLERS),3405 arguments: [exp]3406 });3407 }3408 }3409 else {3410 context.onError(createCompilerError(isBind3411 ? 33 /* X_V_BIND_NO_EXPRESSION */3412 : 34 /* X_V_ON_NO_EXPRESSION */, loc));3413 }3414 continue;3415 }3416 const directiveTransform = context.directiveTransforms[name];3417 if (directiveTransform) {3418 // has built-in directive transform.3419 const { props, needRuntime } = directiveTransform(prop, node, context);3420 !ssr && props.forEach(analyzePatchFlag);3421 properties.push(...props);3422 if (needRuntime) {3423 runtimeDirectives.push(prop);3424 if (isSymbol(needRuntime)) {3425 directiveImportMap.set(prop, needRuntime);3426 }3427 }3428 }3429 else {3430 // no built-in transform, this is a user custom directive.3431 runtimeDirectives.push(prop);3432 }3433 }3434 }3435 let propsExpression = undefined;3436 // has v-bind="object" or v-on="object", wrap with mergeProps3437 if (mergeArgs.length) {3438 if (properties.length) {3439 mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));3440 }3441 if (mergeArgs.length > 1) {3442 propsExpression = createCallExpression(context.helper(MERGE_PROPS), mergeArgs, elementLoc);3443 }3444 else {3445 // single v-bind with nothing else - no need for a mergeProps call3446 propsExpression = mergeArgs[0];3447 }3448 }3449 else if (properties.length) {3450 propsExpression = createObjectExpression(dedupeProperties(properties), elementLoc);3451 }3452 // patchFlag analysis3453 if (hasDynamicKeys) {3454 patchFlag |= 16 /* FULL_PROPS */;3455 }3456 else {3457 if (hasClassBinding) {3458 patchFlag |= 2 /* CLASS */;3459 }3460 if (hasStyleBinding) {3461 patchFlag |= 4 /* STYLE */;3462 }3463 if (dynamicPropNames.length) {3464 patchFlag |= 8 /* PROPS */;3465 }3466 if (hasHydrationEventBinding) {3467 patchFlag |= 32 /* HYDRATE_EVENTS */;3468 }3469 }3470 if ((patchFlag === 0 || patchFlag === 32 /* HYDRATE_EVENTS */) &&3471 (hasRef || hasVnodeHook || runtimeDirectives.length > 0)) {3472 patchFlag |= 512 /* NEED_PATCH */;3473 }3474 return {3475 props: propsExpression,3476 directives: runtimeDirectives,3477 patchFlag,3478 dynamicPropNames3479 };3480}3481// Dedupe props in an object literal.3482// Literal duplicated attributes would have been warned during the parse phase,3483// however, it's possible to encounter duplicated `onXXX` handlers with different3484// modifiers. We also need to merge static and dynamic class / style attributes.3485// - onXXX handlers / style: merge into array3486// - class: merge into single expression with concatenation3487function dedupeProperties(properties) {3488 const knownProps = new Map();3489 const deduped = [];3490 for (let i = 0; i < properties.length; i++) {3491 const prop = properties[i];3492 // dynamic keys are always allowed3493 if (prop.key.type === 8 /* COMPOUND_EXPRESSION */ || !prop.key.isStatic) {3494 deduped.push(prop);3495 continue;3496 }3497 const name = prop.key.content;3498 const existing = knownProps.get(name);3499 if (existing) {3500 if (name === 'style' || name === 'class' || name.startsWith('on')) {3501 mergeAsArray(existing, prop);3502 }3503 // unexpected duplicate, should have emitted error during parse3504 }3505 else {3506 knownProps.set(name, prop);3507 deduped.push(prop);3508 }3509 }3510 return deduped;3511}3512function mergeAsArray(existing, incoming) {3513 if (existing.value.type === 17 /* JS_ARRAY_EXPRESSION */) {3514 existing.value.elements.push(incoming.value);3515 }3516 else {3517 existing.value = createArrayExpression([existing.value, incoming.value], existing.loc);3518 }3519}3520function buildDirectiveArgs(dir, context) {3521 const dirArgs = [];3522 const runtime = directiveImportMap.get(dir);3523 if (runtime) {3524 // built-in directive with runtime3525 dirArgs.push(context.helperString(runtime));3526 }3527 else {3528 {3529 // inject statement for resolving directive3530 context.helper(RESOLVE_DIRECTIVE);3531 context.directives.add(dir.name);3532 dirArgs.push(toValidAssetId(dir.name, `directive`));3533 }3534 }3535 const { loc } = dir;3536 if (dir.exp)3537 dirArgs.push(dir.exp);3538 if (dir.arg) {3539 if (!dir.exp) {3540 dirArgs.push(`void 0`);3541 }3542 dirArgs.push(dir.arg);3543 }3544 if (Object.keys(dir.modifiers).length) {3545 if (!dir.arg) {3546 if (!dir.exp) {3547 dirArgs.push(`void 0`);3548 }3549 dirArgs.push(`void 0`);3550 }3551 const trueExpression = createSimpleExpression(`true`, false, loc);3552 dirArgs.push(createObjectExpression(dir.modifiers.map(modifier => createObjectProperty(modifier, trueExpression)), loc));3553 }3554 return createArrayExpression(dirArgs, dir.loc);3555}3556function stringifyDynamicPropNames(props) {3557 let propsNamesString = `[`;3558 for (let i = 0, l = props.length; i < l; i++) {3559 propsNamesString += JSON.stringify(props[i]);3560 if (i < l - 1)3561 propsNamesString += ', ';3562 }3563 return propsNamesString + `]`;3564}3565const transformSlotOutlet = (node, context) => {3566 if (isSlotOutlet(node)) {3567 const { children, loc } = node;3568 const { slotName, slotProps } = processSlotOutlet(node, context);3569 const slotArgs = [3570 context.prefixIdentifiers ? `_ctx.$slots` : `$slots`,3571 slotName3572 ];3573 if (slotProps) {3574 slotArgs.push(slotProps);3575 }3576 if (children.length) {3577 if (!slotProps) {3578 slotArgs.push(`{}`);3579 }3580 slotArgs.push(createFunctionExpression([], children, false, false, loc));3581 }3582 node.codegenNode = createCallExpression(context.helper(RENDER_SLOT), slotArgs, loc);3583 }3584};3585function processSlotOutlet(node, context) {3586 let slotName = `"default"`;3587 let slotProps = undefined;3588 const nonNameProps = [];3589 for (let i = 0; i < node.props.length; i++) {3590 const p = node.props[i];3591 if (p.type === 6 /* ATTRIBUTE */) {3592 if (p.value) {3593 if (p.name === 'name') {3594 slotName = JSON.stringify(p.value.content);3595 }3596 else {3597 p.name = camelize(p.name);3598 nonNameProps.push(p);3599 }3600 }3601 }3602 else {3603 if (p.name === 'bind' && isBindKey(p.arg, 'name')) {3604 if (p.exp)3605 slotName = p.exp;3606 }3607 else {3608 if (p.name === 'bind' && p.arg && isStaticExp(p.arg)) {3609 p.arg.content = camelize(p.arg.content);3610 }3611 nonNameProps.push(p);3612 }3613 }3614 }3615 if (nonNameProps.length > 0) {3616 const { props, directives } = buildProps(node, context, nonNameProps);3617 slotProps = props;3618 if (directives.length) {3619 context.onError(createCompilerError(35 /* X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET */, directives[0].loc));3620 }3621 }3622 return {3623 slotName,3624 slotProps3625 };3626}3627const fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^\s*function(?:\s+[\w$]+)?\s*\(/;3628const transformOn = (dir, node, context, augmentor) => {3629 const { loc, modifiers, arg } = dir;3630 if (!dir.exp && !modifiers.length) {3631 context.onError(createCompilerError(34 /* X_V_ON_NO_EXPRESSION */, loc));3632 }3633 let eventName;3634 if (arg.type === 4 /* SIMPLE_EXPRESSION */) {3635 if (arg.isStatic) {3636 const rawName = arg.content;3637 // for all event listeners, auto convert it to camelCase. See issue #22493638 eventName = createSimpleExpression(toHandlerKey(camelize(rawName)), true, arg.loc);3639 }3640 else {3641 // #23883642 eventName = createCompoundExpression([3643 `${context.helperString(TO_HANDLER_KEY)}(`,3644 arg,3645 `)`3646 ]);3647 }3648 }3649 else {3650 // already a compound expression.3651 eventName = arg;3652 eventName.children.unshift(`${context.helperString(TO_HANDLER_KEY)}(`);3653 eventName.children.push(`)`);3654 }3655 // handler processing3656 let exp = dir.exp;3657 if (exp && !exp.content.trim()) {3658 exp = undefined;3659 }3660 let shouldCache = context.cacheHandlers && !exp;3661 if (exp) {3662 const isMemberExp = isMemberExpression(exp.content);3663 const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content));3664 const hasMultipleStatements = exp.content.includes(`;`);3665 {3666 validateBrowserExpression(exp, context, false, hasMultipleStatements);3667 }3668 if (isInlineStatement || (shouldCache && isMemberExp)) {3669 // wrap inline statement in a function expression3670 exp = createCompoundExpression([3671 `${isInlineStatement3672 ? `$event`3673 : `${ ``}(...args)`} => ${hasMultipleStatements ? `{` : `(`}`,3674 exp,3675 hasMultipleStatements ? `}` : `)`3676 ]);3677 }3678 }3679 let ret = {3680 props: [...
compiler-core.esm-bundler.js
Source:compiler-core.esm-bundler.js
...2280 * Validate a non-prefixed expression.2281 * This is only called when using the in-browser runtime compiler since it2282 * doesn't prefix expressions.2283 */2284function validateBrowserExpression(node, context, asParams = false, asRawStatements = false) {2285 const exp = node.content;2286 // empty expressions are validated per-directive since some directives2287 // do allow empty expressions.2288 if (!exp.trim()) {2289 return;2290 }2291 try {2292 new Function(asRawStatements2293 ? ` ${exp} `2294 : `return ${asParams ? `(${exp}) => {}` : `(${exp})`}`);2295 }2296 catch (e) {2297 let message = e.message;2298 const keywordMatch = exp2299 .replace(stripStringRE, '')2300 .match(prohibitedKeywordRE);2301 if (keywordMatch) {2302 message = `avoid using JavaScript keyword as property name: "${keywordMatch[0]}"`;2303 }2304 context.onError(createCompilerError(43 /* X_INVALID_EXPRESSION */, node.loc, undefined, message));2305 }2306}2307const transformExpression = (node, context) => {2308 if (node.type === 5 /* INTERPOLATION */) {2309 node.content = processExpression(node.content, context);2310 }2311 else if (node.type === 1 /* ELEMENT */) {2312 // handle directives on element2313 for (let i = 0; i < node.props.length; i++) {2314 const dir = node.props[i];2315 // do not process for v-on & v-for since they are special handled2316 if (dir.type === 7 /* DIRECTIVE */ && dir.name !== 'for') {2317 const exp = dir.exp;2318 const arg = dir.arg;2319 // do not process exp if this is v-on:arg - we need special handling2320 // for wrapping inline statements.2321 if (exp &&2322 exp.type === 4 /* SIMPLE_EXPRESSION */ &&2323 !(dir.name === 'on' && arg)) {2324 dir.exp = processExpression(exp, context, 2325 // slot args must be processed as function params2326 dir.name === 'slot');2327 }2328 if (arg && arg.type === 4 /* SIMPLE_EXPRESSION */ && !arg.isStatic) {2329 dir.arg = processExpression(arg, context);2330 }2331 }2332 }2333 }2334};2335// Important: since this function uses Node.js only dependencies, it should2336// always be used with a leading !true check so that it can be2337// tree-shaken from the browser build.2338function processExpression(node, context, 2339// some expressions like v-slot props & v-for aliases should be parsed as2340// function params2341asParams = false, 2342// v-on handler values may contain multiple statements2343asRawStatements = false) {2344 {2345 if ((process.env.NODE_ENV !== 'production')) {2346 // simple in-browser validation (same logic in 2.x)2347 validateBrowserExpression(node, context, asParams, asRawStatements);2348 }2349 return node;2350 }2351}2352const transformIf = createStructuralDirectiveTransform(/^(if|else|else-if)$/, (node, dir, context) => {2353 return processIf(node, dir, context, (ifNode, branch, isRoot) => {2354 // #1587: We need to dynamically increment the key based on the current2355 // node's sibling nodes, since chained v-if/else branches are2356 // rendered at the same depth2357 const siblings = context.parent.children;2358 let i = siblings.indexOf(ifNode);2359 let key = 0;2360 while (i-- >= 0) {2361 const sibling = siblings[i];2362 if (sibling && sibling.type === 9 /* IF */) {2363 key += sibling.branches.length;2364 }2365 }2366 // Exit callback. Complete the codegenNode when all children have been2367 // transformed.2368 return () => {2369 if (isRoot) {2370 ifNode.codegenNode = createCodegenNodeForBranch(branch, key, context);2371 }2372 else {2373 // attach this branch's codegen node to the v-if root.2374 const parentCondition = getParentCondition(ifNode.codegenNode);2375 parentCondition.alternate = createCodegenNodeForBranch(branch, key + ifNode.branches.length - 1, context);2376 }2377 };2378 });2379});2380// target-agnostic transform used for both Client and SSR2381function processIf(node, dir, context, processCodegen) {2382 if (dir.name !== 'else' &&2383 (!dir.exp || !dir.exp.content.trim())) {2384 const loc = dir.exp ? dir.exp.loc : node.loc;2385 context.onError(createCompilerError(27 /* X_V_IF_NO_EXPRESSION */, dir.loc));2386 dir.exp = createSimpleExpression(`true`, false, loc);2387 }2388 if ((process.env.NODE_ENV !== 'production') && true && dir.exp) {2389 validateBrowserExpression(dir.exp, context);2390 }2391 if (dir.name === 'if') {2392 const branch = createIfBranch(node, dir);2393 const ifNode = {2394 type: 9 /* IF */,2395 loc: node.loc,2396 branches: [branch]2397 };2398 context.replaceNode(ifNode);2399 if (processCodegen) {2400 return processCodegen(ifNode, branch, true);2401 }2402 }2403 else {2404 // locate the adjacent v-if2405 const siblings = context.parent.children;2406 const comments = [];2407 let i = siblings.indexOf(node);2408 while (i-- >= -1) {2409 const sibling = siblings[i];2410 if ((process.env.NODE_ENV !== 'production') && sibling && sibling.type === 3 /* COMMENT */) {2411 context.removeNode(sibling);2412 comments.unshift(sibling);2413 continue;2414 }2415 if (sibling &&2416 sibling.type === 2 /* TEXT */ &&2417 !sibling.content.trim().length) {2418 context.removeNode(sibling);2419 continue;2420 }2421 if (sibling && sibling.type === 9 /* IF */) {2422 // move the node to the if node's branches2423 context.removeNode();2424 const branch = createIfBranch(node, dir);2425 if ((process.env.NODE_ENV !== 'production') && comments.length) {2426 branch.children = [...comments, ...branch.children];2427 }2428 // check if user is forcing same key on different branches2429 if ((process.env.NODE_ENV !== 'production') || !true) {2430 const key = branch.userKey;2431 if (key) {2432 sibling.branches.forEach(({ userKey }) => {2433 if (isSameKey(userKey, key)) {2434 context.onError(createCompilerError(28 /* X_V_IF_SAME_KEY */, branch.userKey.loc));2435 }2436 });2437 }2438 }2439 sibling.branches.push(branch);2440 const onExit = processCodegen && processCodegen(sibling, branch, false);2441 // since the branch was removed, it will not be traversed.2442 // make sure to traverse here.2443 traverseNode(branch, context);2444 // call on exit2445 if (onExit)2446 onExit();2447 // make sure to reset currentNode after traversal to indicate this2448 // node has been removed.2449 context.currentNode = null;2450 }2451 else {2452 context.onError(createCompilerError(29 /* X_V_ELSE_NO_ADJACENT_IF */, node.loc));2453 }2454 break;2455 }2456 }2457}2458function createIfBranch(node, dir) {2459 return {2460 type: 10 /* IF_BRANCH */,2461 loc: node.loc,2462 condition: dir.name === 'else' ? undefined : dir.exp,2463 children: node.tagType === 3 /* TEMPLATE */ && !findDir(node, 'for')2464 ? node.children2465 : [node],2466 userKey: findProp(node, `key`)2467 };2468}2469function createCodegenNodeForBranch(branch, keyIndex, context) {2470 if (branch.condition) {2471 return createConditionalExpression(branch.condition, createChildrenCodegenNode(branch, keyIndex, context), 2472 // make sure to pass in asBlock: true so that the comment node call2473 // closes the current block.2474 createCallExpression(context.helper(CREATE_COMMENT), [2475 (process.env.NODE_ENV !== 'production') ? '"v-if"' : '""',2476 'true'2477 ]));2478 }2479 else {2480 return createChildrenCodegenNode(branch, keyIndex, context);2481 }2482}2483function createChildrenCodegenNode(branch, keyIndex, context) {2484 const { helper } = context;2485 const keyProperty = createObjectProperty(`key`, createSimpleExpression(`${keyIndex}`, false, locStub, 2 /* CAN_HOIST */));2486 const { children } = branch;2487 const firstChild = children[0];2488 const needFragmentWrapper = children.length !== 1 || firstChild.type !== 1 /* ELEMENT */;2489 if (needFragmentWrapper) {2490 if (children.length === 1 && firstChild.type === 11 /* FOR */) {2491 // optimize away nested fragments when child is a ForNode2492 const vnodeCall = firstChild.codegenNode;2493 injectProp(vnodeCall, keyProperty, context);2494 return vnodeCall;2495 }2496 else {2497 return createVNodeCall(context, helper(FRAGMENT), createObjectExpression([keyProperty]), children, 64 /* STABLE_FRAGMENT */ +2498 ((process.env.NODE_ENV !== 'production')2499 ? ` /* ${PatchFlagNames[64 /* STABLE_FRAGMENT */]} */`2500 : ``), undefined, undefined, true, false, branch.loc);2501 }2502 }2503 else {2504 const vnodeCall = firstChild2505 .codegenNode;2506 // Change createVNode to createBlock.2507 if (vnodeCall.type === 13 /* VNODE_CALL */) {2508 vnodeCall.isBlock = true;2509 helper(OPEN_BLOCK);2510 helper(CREATE_BLOCK);2511 }2512 // inject branch key2513 injectProp(vnodeCall, keyProperty, context);2514 return vnodeCall;2515 }2516}2517function isSameKey(a, b) {2518 if (!a || a.type !== b.type) {2519 return false;2520 }2521 if (a.type === 6 /* ATTRIBUTE */) {2522 if (a.value.content !== b.value.content) {2523 return false;2524 }2525 }2526 else {2527 // directive2528 const exp = a.exp;2529 const branchExp = b.exp;2530 if (exp.type !== branchExp.type) {2531 return false;2532 }2533 if (exp.type !== 4 /* SIMPLE_EXPRESSION */ ||2534 (exp.isStatic !== branchExp.isStatic ||2535 exp.content !== branchExp.content)) {2536 return false;2537 }2538 }2539 return true;2540}2541function getParentCondition(node) {2542 while (true) {2543 if (node.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {2544 if (node.alternate.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {2545 node = node.alternate;2546 }2547 else {2548 return node;2549 }2550 }2551 else if (node.type === 20 /* JS_CACHE_EXPRESSION */) {2552 node = node.value;2553 }2554 }2555}2556const transformFor = createStructuralDirectiveTransform('for', (node, dir, context) => {2557 const { helper } = context;2558 return processFor(node, dir, context, forNode => {2559 // create the loop render function expression now, and add the2560 // iterator on exit after all children have been traversed2561 const renderExp = createCallExpression(helper(RENDER_LIST), [2562 forNode.source2563 ]);2564 const keyProp = findProp(node, `key`);2565 const keyProperty = keyProp2566 ? createObjectProperty(`key`, keyProp.type === 6 /* ATTRIBUTE */2567 ? createSimpleExpression(keyProp.value.content, true)2568 : keyProp.exp)2569 : null;2570 const isStableFragment = forNode.source.type === 4 /* SIMPLE_EXPRESSION */ &&2571 forNode.source.constType > 0;2572 const fragmentFlag = isStableFragment2573 ? 64 /* STABLE_FRAGMENT */2574 : keyProp2575 ? 128 /* KEYED_FRAGMENT */2576 : 256 /* UNKEYED_FRAGMENT */;2577 forNode.codegenNode = createVNodeCall(context, helper(FRAGMENT), undefined, renderExp, fragmentFlag +2578 ((process.env.NODE_ENV !== 'production') ? ` /* ${PatchFlagNames[fragmentFlag]} */` : ``), undefined, undefined, true /* isBlock */, !isStableFragment /* disableTracking */, node.loc);2579 return () => {2580 // finish the codegen now that all children have been traversed2581 let childBlock;2582 const isTemplate = isTemplateNode(node);2583 const { children } = forNode;2584 // check <template v-for> key placement2585 if (((process.env.NODE_ENV !== 'production') || !true) && isTemplate) {2586 node.children.some(c => {2587 if (c.type === 1 /* ELEMENT */) {2588 const key = findProp(c, 'key');2589 if (key) {2590 context.onError(createCompilerError(32 /* X_V_FOR_TEMPLATE_KEY_PLACEMENT */, key.loc));2591 return true;2592 }2593 }2594 });2595 }2596 const needFragmentWrapper = children.length !== 1 || children[0].type !== 1 /* ELEMENT */;2597 const slotOutlet = isSlotOutlet(node)2598 ? node2599 : isTemplate &&2600 node.children.length === 1 &&2601 isSlotOutlet(node.children[0])2602 ? node.children[0] // api-extractor somehow fails to infer this2603 : null;2604 if (slotOutlet) {2605 // <slot v-for="..."> or <template v-for="..."><slot/></template>2606 childBlock = slotOutlet.codegenNode;2607 if (isTemplate && keyProperty) {2608 // <template v-for="..." :key="..."><slot/></template>2609 // we need to inject the key to the renderSlot() call.2610 // the props for renderSlot is passed as the 3rd argument.2611 injectProp(childBlock, keyProperty, context);2612 }2613 }2614 else if (needFragmentWrapper) {2615 // <template v-for="..."> with text or multi-elements2616 // should generate a fragment block for each loop2617 childBlock = createVNodeCall(context, helper(FRAGMENT), keyProperty ? createObjectExpression([keyProperty]) : undefined, node.children, 64 /* STABLE_FRAGMENT */ +2618 ((process.env.NODE_ENV !== 'production')2619 ? ` /* ${PatchFlagNames[64 /* STABLE_FRAGMENT */]} */`2620 : ``), undefined, undefined, true);2621 }2622 else {2623 // Normal element v-for. Directly use the child's codegenNode2624 // but mark it as a block.2625 childBlock = children[0]2626 .codegenNode;2627 if (isTemplate && keyProperty) {2628 injectProp(childBlock, keyProperty, context);2629 }2630 childBlock.isBlock = !isStableFragment;2631 if (childBlock.isBlock) {2632 helper(OPEN_BLOCK);2633 helper(CREATE_BLOCK);2634 }2635 else {2636 helper(CREATE_VNODE);2637 }2638 }2639 renderExp.arguments.push(createFunctionExpression(createForLoopParams(forNode.parseResult), childBlock, true /* force newline */));2640 };2641 });2642});2643// target-agnostic transform used for both Client and SSR2644function processFor(node, dir, context, processCodegen) {2645 if (!dir.exp) {2646 context.onError(createCompilerError(30 /* X_V_FOR_NO_EXPRESSION */, dir.loc));2647 return;2648 }2649 const parseResult = parseForExpression(2650 // can only be simple expression because vFor transform is applied2651 // before expression transform.2652 dir.exp, context);2653 if (!parseResult) {2654 context.onError(createCompilerError(31 /* X_V_FOR_MALFORMED_EXPRESSION */, dir.loc));2655 return;2656 }2657 const { addIdentifiers, removeIdentifiers, scopes } = context;2658 const { source, value, key, index } = parseResult;2659 const forNode = {2660 type: 11 /* FOR */,2661 loc: dir.loc,2662 source,2663 valueAlias: value,2664 keyAlias: key,2665 objectIndexAlias: index,2666 parseResult,2667 children: isTemplateNode(node) ? node.children : [node]2668 };2669 context.replaceNode(forNode);2670 // bookkeeping2671 scopes.vFor++;2672 const onExit = processCodegen && processCodegen(forNode);2673 return () => {2674 scopes.vFor--;2675 if (onExit)2676 onExit();2677 };2678}2679const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/;2680// This regex doesn't cover the case if key or index aliases have destructuring,2681// but those do not make sense in the first place, so this works in practice.2682const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/;2683const stripParensRE = /^\(|\)$/g;2684function parseForExpression(input, context) {2685 const loc = input.loc;2686 const exp = input.content;2687 const inMatch = exp.match(forAliasRE);2688 if (!inMatch)2689 return;2690 const [, LHS, RHS] = inMatch;2691 const result = {2692 source: createAliasExpression(loc, RHS.trim(), exp.indexOf(RHS, LHS.length)),2693 value: undefined,2694 key: undefined,2695 index: undefined2696 };2697 if ((process.env.NODE_ENV !== 'production') && true) {2698 validateBrowserExpression(result.source, context);2699 }2700 let valueContent = LHS.trim()2701 .replace(stripParensRE, '')2702 .trim();2703 const trimmedOffset = LHS.indexOf(valueContent);2704 const iteratorMatch = valueContent.match(forIteratorRE);2705 if (iteratorMatch) {2706 valueContent = valueContent.replace(forIteratorRE, '').trim();2707 const keyContent = iteratorMatch[1].trim();2708 let keyOffset;2709 if (keyContent) {2710 keyOffset = exp.indexOf(keyContent, trimmedOffset + valueContent.length);2711 result.key = createAliasExpression(loc, keyContent, keyOffset);2712 if ((process.env.NODE_ENV !== 'production') && true) {2713 validateBrowserExpression(result.key, context, true);2714 }2715 }2716 if (iteratorMatch[2]) {2717 const indexContent = iteratorMatch[2].trim();2718 if (indexContent) {2719 result.index = createAliasExpression(loc, indexContent, exp.indexOf(indexContent, result.key2720 ? keyOffset + keyContent.length2721 : trimmedOffset + valueContent.length));2722 if ((process.env.NODE_ENV !== 'production') && true) {2723 validateBrowserExpression(result.index, context, true);2724 }2725 }2726 }2727 }2728 if (valueContent) {2729 result.value = createAliasExpression(loc, valueContent, trimmedOffset);2730 if ((process.env.NODE_ENV !== 'production') && true) {2731 validateBrowserExpression(result.value, context, true);2732 }2733 }2734 return result;2735}2736function createAliasExpression(range, content, offset) {2737 return createSimpleExpression(content, false, getInnerRange(range, offset, content.length));2738}2739function createForLoopParams({ value, key, index }) {2740 const params = [];2741 if (value) {2742 params.push(value);2743 }2744 if (key) {2745 if (!value) {2746 params.push(createSimpleExpression(`_`, false));2747 }2748 params.push(key);2749 }2750 if (index) {2751 if (!key) {2752 if (!value) {2753 params.push(createSimpleExpression(`_`, false));2754 }2755 params.push(createSimpleExpression(`__`, false));2756 }2757 params.push(index);2758 }2759 return params;2760}2761const defaultFallback = createSimpleExpression(`undefined`, false);2762// A NodeTransform that:2763// 1. Tracks scope identifiers for scoped slots so that they don't get prefixed2764// by transformExpression. This is only applied in non-browser builds with2765// { prefixIdentifiers: true }.2766// 2. Track v-slot depths so that we know a slot is inside another slot.2767// Note the exit callback is executed before buildSlots() on the same node,2768// so only nested slots see positive numbers.2769const trackSlotScopes = (node, context) => {2770 if (node.type === 1 /* ELEMENT */ &&2771 (node.tagType === 1 /* COMPONENT */ ||2772 node.tagType === 3 /* TEMPLATE */)) {2773 // We are only checking non-empty v-slot here2774 // since we only care about slots that introduce scope variables.2775 const vSlot = findDir(node, 'slot');2776 if (vSlot) {2777 const slotProps = vSlot.exp;2778 context.scopes.vSlot++;2779 return () => {2780 context.scopes.vSlot--;2781 };2782 }2783 }2784};2785// A NodeTransform that tracks scope identifiers for scoped slots with v-for.2786// This transform is only applied in non-browser builds with { prefixIdentifiers: true }2787const trackVForSlotScopes = (node, context) => {2788 let vFor;2789 if (isTemplateNode(node) &&2790 node.props.some(isVSlot) &&2791 (vFor = findDir(node, 'for'))) {2792 const result = (vFor.parseResult = parseForExpression(vFor.exp, context));2793 if (result) {2794 const { value, key, index } = result;2795 const { addIdentifiers, removeIdentifiers } = context;2796 value && addIdentifiers(value);2797 key && addIdentifiers(key);2798 index && addIdentifiers(index);2799 return () => {2800 value && removeIdentifiers(value);2801 key && removeIdentifiers(key);2802 index && removeIdentifiers(index);2803 };2804 }2805 }2806};2807const buildClientSlotFn = (props, children, loc) => createFunctionExpression(props, children, false /* newline */, true /* isSlot */, children.length ? children[0].loc : loc);2808// Instead of being a DirectiveTransform, v-slot processing is called during2809// transformElement to build the slots object for a component.2810function buildSlots(node, context, buildSlotFn = buildClientSlotFn) {2811 context.helper(WITH_CTX);2812 const { children, loc } = node;2813 const slotsProperties = [];2814 const dynamicSlots = [];2815 const buildDefaultSlotProperty = (props, children) => createObjectProperty(`default`, buildSlotFn(props, children, loc));2816 // If the slot is inside a v-for or another v-slot, force it to be dynamic2817 // since it likely uses a scope variable.2818 let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0;2819 // 1. Check for slot with slotProps on component itself.2820 // <Comp v-slot="{ prop }"/>2821 const onComponentSlot = findDir(node, 'slot', true);2822 if (onComponentSlot) {2823 const { arg, exp } = onComponentSlot;2824 if (arg && !isStaticExp(arg)) {2825 hasDynamicSlots = true;2826 }2827 slotsProperties.push(createObjectProperty(arg || createSimpleExpression('default', true), buildSlotFn(exp, children, loc)));2828 }2829 // 2. Iterate through children and check for template slots2830 // <template v-slot:foo="{ prop }">2831 let hasTemplateSlots = false;2832 let hasNamedDefaultSlot = false;2833 const implicitDefaultChildren = [];2834 const seenSlotNames = new Set();2835 for (let i = 0; i < children.length; i++) {2836 const slotElement = children[i];2837 let slotDir;2838 if (!isTemplateNode(slotElement) ||2839 !(slotDir = findDir(slotElement, 'slot', true))) {2840 // not a <template v-slot>, skip.2841 if (slotElement.type !== 3 /* COMMENT */) {2842 implicitDefaultChildren.push(slotElement);2843 }2844 continue;2845 }2846 if (onComponentSlot) {2847 // already has on-component slot - this is incorrect usage.2848 context.onError(createCompilerError(36 /* X_V_SLOT_MIXED_SLOT_USAGE */, slotDir.loc));2849 break;2850 }2851 hasTemplateSlots = true;2852 const { children: slotChildren, loc: slotLoc } = slotElement;2853 const { arg: slotName = createSimpleExpression(`default`, true), exp: slotProps, loc: dirLoc } = slotDir;2854 // check if name is dynamic.2855 let staticSlotName;2856 if (isStaticExp(slotName)) {2857 staticSlotName = slotName ? slotName.content : `default`;2858 }2859 else {2860 hasDynamicSlots = true;2861 }2862 const slotFunction = buildSlotFn(slotProps, slotChildren, slotLoc);2863 // check if this slot is conditional (v-if/v-for)2864 let vIf;2865 let vElse;2866 let vFor;2867 if ((vIf = findDir(slotElement, 'if'))) {2868 hasDynamicSlots = true;2869 dynamicSlots.push(createConditionalExpression(vIf.exp, buildDynamicSlot(slotName, slotFunction), defaultFallback));2870 }2871 else if ((vElse = findDir(slotElement, /^else(-if)?$/, true /* allowEmpty */))) {2872 // find adjacent v-if2873 let j = i;2874 let prev;2875 while (j--) {2876 prev = children[j];2877 if (prev.type !== 3 /* COMMENT */) {2878 break;2879 }2880 }2881 if (prev && isTemplateNode(prev) && findDir(prev, 'if')) {2882 // remove node2883 children.splice(i, 1);2884 i--;2885 // attach this slot to previous conditional2886 let conditional = dynamicSlots[dynamicSlots.length - 1];2887 while (conditional.alternate.type === 19 /* JS_CONDITIONAL_EXPRESSION */) {2888 conditional = conditional.alternate;2889 }2890 conditional.alternate = vElse.exp2891 ? createConditionalExpression(vElse.exp, buildDynamicSlot(slotName, slotFunction), defaultFallback)2892 : buildDynamicSlot(slotName, slotFunction);2893 }2894 else {2895 context.onError(createCompilerError(29 /* X_V_ELSE_NO_ADJACENT_IF */, vElse.loc));2896 }2897 }2898 else if ((vFor = findDir(slotElement, 'for'))) {2899 hasDynamicSlots = true;2900 const parseResult = vFor.parseResult ||2901 parseForExpression(vFor.exp, context);2902 if (parseResult) {2903 // Render the dynamic slots as an array and add it to the createSlot()2904 // args. The runtime knows how to handle it appropriately.2905 dynamicSlots.push(createCallExpression(context.helper(RENDER_LIST), [2906 parseResult.source,2907 createFunctionExpression(createForLoopParams(parseResult), buildDynamicSlot(slotName, slotFunction), true /* force newline */)2908 ]));2909 }2910 else {2911 context.onError(createCompilerError(31 /* X_V_FOR_MALFORMED_EXPRESSION */, vFor.loc));2912 }2913 }2914 else {2915 // check duplicate static names2916 if (staticSlotName) {2917 if (seenSlotNames.has(staticSlotName)) {2918 context.onError(createCompilerError(37 /* X_V_SLOT_DUPLICATE_SLOT_NAMES */, dirLoc));2919 continue;2920 }2921 seenSlotNames.add(staticSlotName);2922 if (staticSlotName === 'default') {2923 hasNamedDefaultSlot = true;2924 }2925 }2926 slotsProperties.push(createObjectProperty(slotName, slotFunction));2927 }2928 }2929 if (!onComponentSlot) {2930 if (!hasTemplateSlots) {2931 // implicit default slot (on component)2932 slotsProperties.push(buildDefaultSlotProperty(undefined, children));2933 }2934 else if (implicitDefaultChildren.length) {2935 // implicit default slot (mixed with named slots)2936 if (hasNamedDefaultSlot) {2937 context.onError(createCompilerError(38 /* X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN */, implicitDefaultChildren[0].loc));2938 }2939 else {2940 slotsProperties.push(buildDefaultSlotProperty(undefined, implicitDefaultChildren));2941 }2942 }2943 }2944 const slotFlag = hasDynamicSlots2945 ? 2 /* DYNAMIC */2946 : hasForwardedSlots(node.children)2947 ? 3 /* FORWARDED */2948 : 1 /* STABLE */;2949 let slots = createObjectExpression(slotsProperties.concat(createObjectProperty(`_`, 2950 // 2 = compiled but dynamic = can skip normalization, but must run diff2951 // 1 = compiled and static = can skip normalization AND diff as optimized2952 createSimpleExpression(slotFlag + ((process.env.NODE_ENV !== 'production') ? ` /* ${slotFlagsText[slotFlag]} */` : ``), false))), loc);2953 if (dynamicSlots.length) {2954 slots = createCallExpression(context.helper(CREATE_SLOTS), [2955 slots,2956 createArrayExpression(dynamicSlots)2957 ]);2958 }2959 return {2960 slots,2961 hasDynamicSlots2962 };2963}2964function buildDynamicSlot(name, fn) {2965 return createObjectExpression([2966 createObjectProperty(`name`, name),2967 createObjectProperty(`fn`, fn)2968 ]);2969}2970function hasForwardedSlots(children) {2971 for (let i = 0; i < children.length; i++) {2972 const child = children[i];2973 if (child.type === 1 /* ELEMENT */) {2974 if (child.tagType === 2 /* SLOT */ ||2975 (child.tagType === 0 /* ELEMENT */ &&2976 hasForwardedSlots(child.children))) {2977 return true;2978 }2979 }2980 }2981 return false;2982}2983// some directive transforms (e.g. v-model) may return a symbol for runtime2984// import, which should be used instead of a resolveDirective call.2985const directiveImportMap = new WeakMap();2986// generate a JavaScript AST for this element's codegen2987const transformElement = (node, context) => {2988 if (!(node.type === 1 /* ELEMENT */ &&2989 (node.tagType === 0 /* ELEMENT */ ||2990 node.tagType === 1 /* COMPONENT */))) {2991 return;2992 }2993 // perform the work on exit, after all child expressions have been2994 // processed and merged.2995 return function postTransformElement() {2996 const { tag, props } = node;2997 const isComponent = node.tagType === 1 /* COMPONENT */;2998 // The goal of the transform is to create a codegenNode implementing the2999 // VNodeCall interface.3000 const vnodeTag = isComponent3001 ? resolveComponentType(node, context)3002 : `"${tag}"`;3003 const isDynamicComponent = isObject(vnodeTag) && vnodeTag.callee === RESOLVE_DYNAMIC_COMPONENT;3004 let vnodeProps;3005 let vnodeChildren;3006 let vnodePatchFlag;3007 let patchFlag = 0;3008 let vnodeDynamicProps;3009 let dynamicPropNames;3010 let vnodeDirectives;3011 let shouldUseBlock = 3012 // dynamic component may resolve to plain elements3013 isDynamicComponent ||3014 vnodeTag === TELEPORT ||3015 vnodeTag === SUSPENSE ||3016 (!isComponent &&3017 // <svg> and <foreignObject> must be forced into blocks so that block3018 // updates inside get proper isSVG flag at runtime. (#639, #643)3019 // This is technically web-specific, but splitting the logic out of core3020 // leads to too much unnecessary complexity.3021 (tag === 'svg' ||3022 tag === 'foreignObject' ||3023 // #938: elements with dynamic keys should be forced into blocks3024 findProp(node, 'key', true)));3025 // props3026 if (props.length > 0) {3027 const propsBuildResult = buildProps(node, context);3028 vnodeProps = propsBuildResult.props;3029 patchFlag = propsBuildResult.patchFlag;3030 dynamicPropNames = propsBuildResult.dynamicPropNames;3031 const directives = propsBuildResult.directives;3032 vnodeDirectives =3033 directives && directives.length3034 ? createArrayExpression(directives.map(dir => buildDirectiveArgs(dir, context)))3035 : undefined;3036 }3037 // children3038 if (node.children.length > 0) {3039 if (vnodeTag === KEEP_ALIVE) {3040 // Although a built-in component, we compile KeepAlive with raw children3041 // instead of slot functions so that it can be used inside Transition3042 // or other Transition-wrapping HOCs.3043 // To ensure correct updates with block optimizations, we need to:3044 // 1. Force keep-alive into a block. This avoids its children being3045 // collected by a parent block.3046 shouldUseBlock = true;3047 // 2. Force keep-alive to always be updated, since it uses raw children.3048 patchFlag |= 1024 /* DYNAMIC_SLOTS */;3049 if ((process.env.NODE_ENV !== 'production') && node.children.length > 1) {3050 context.onError(createCompilerError(44 /* X_KEEP_ALIVE_INVALID_CHILDREN */, {3051 start: node.children[0].loc.start,3052 end: node.children[node.children.length - 1].loc.end,3053 source: ''3054 }));3055 }3056 }3057 const shouldBuildAsSlots = isComponent &&3058 // Teleport is not a real component and has dedicated runtime handling3059 vnodeTag !== TELEPORT &&3060 // explained above.3061 vnodeTag !== KEEP_ALIVE;3062 if (shouldBuildAsSlots) {3063 const { slots, hasDynamicSlots } = buildSlots(node, context);3064 vnodeChildren = slots;3065 if (hasDynamicSlots) {3066 patchFlag |= 1024 /* DYNAMIC_SLOTS */;3067 }3068 }3069 else if (node.children.length === 1 && vnodeTag !== TELEPORT) {3070 const child = node.children[0];3071 const type = child.type;3072 // check for dynamic text children3073 const hasDynamicTextChild = type === 5 /* INTERPOLATION */ ||3074 type === 8 /* COMPOUND_EXPRESSION */;3075 if (hasDynamicTextChild &&3076 getConstantType(child, context) === 0 /* NOT_CONSTANT */) {3077 patchFlag |= 1 /* TEXT */;3078 }3079 // pass directly if the only child is a text node3080 // (plain / interpolation / expression)3081 if (hasDynamicTextChild || type === 2 /* TEXT */) {3082 vnodeChildren = child;3083 }3084 else {3085 vnodeChildren = node.children;3086 }3087 }3088 else {3089 vnodeChildren = node.children;3090 }3091 }3092 // patchFlag & dynamicPropNames3093 if (patchFlag !== 0) {3094 if ((process.env.NODE_ENV !== 'production')) {3095 if (patchFlag < 0) {3096 // special flags (negative and mutually exclusive)3097 vnodePatchFlag = patchFlag + ` /* ${PatchFlagNames[patchFlag]} */`;3098 }3099 else {3100 // bitwise flags3101 const flagNames = Object.keys(PatchFlagNames)3102 .map(Number)3103 .filter(n => n > 0 && patchFlag & n)3104 .map(n => PatchFlagNames[n])3105 .join(`, `);3106 vnodePatchFlag = patchFlag + ` /* ${flagNames} */`;3107 }3108 }3109 else {3110 vnodePatchFlag = String(patchFlag);3111 }3112 if (dynamicPropNames && dynamicPropNames.length) {3113 vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames);3114 }3115 }3116 node.codegenNode = createVNodeCall(context, vnodeTag, vnodeProps, vnodeChildren, vnodePatchFlag, vnodeDynamicProps, vnodeDirectives, !!shouldUseBlock, false /* disableTracking */, node.loc);3117 };3118};3119function resolveComponentType(node, context, ssr = false) {3120 const { tag } = node;3121 // 1. dynamic component3122 const isProp = node.tag === 'component' ? findProp(node, 'is') : findDir(node, 'is');3123 if (isProp) {3124 const exp = isProp.type === 6 /* ATTRIBUTE */3125 ? isProp.value && createSimpleExpression(isProp.value.content, true)3126 : isProp.exp;3127 if (exp) {3128 return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [3129 exp3130 ]);3131 }3132 }3133 // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)3134 const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);3135 if (builtIn) {3136 // built-ins are simply fallthroughs / have special handling during ssr3137 // so we don't need to import their runtime equivalents3138 if (!ssr)3139 context.helper(builtIn);3140 return builtIn;3141 }3142 // 5. user component (resolve)3143 context.helper(RESOLVE_COMPONENT);3144 context.components.add(tag);3145 return toValidAssetId(tag, `component`);3146}3147function buildProps(node, context, props = node.props, ssr = false) {3148 const { tag, loc: elementLoc } = node;3149 const isComponent = node.tagType === 1 /* COMPONENT */;3150 let properties = [];3151 const mergeArgs = [];3152 const runtimeDirectives = [];3153 // patchFlag analysis3154 let patchFlag = 0;3155 let hasRef = false;3156 let hasClassBinding = false;3157 let hasStyleBinding = false;3158 let hasHydrationEventBinding = false;3159 let hasDynamicKeys = false;3160 let hasVnodeHook = false;3161 const dynamicPropNames = [];3162 const analyzePatchFlag = ({ key, value }) => {3163 if (isStaticExp(key)) {3164 const name = key.content;3165 const isEventHandler = isOn(name);3166 if (!isComponent &&3167 isEventHandler &&3168 // omit the flag for click handlers because hydration gives click3169 // dedicated fast path.3170 name.toLowerCase() !== 'onclick' &&3171 // omit v-model handlers3172 name !== 'onUpdate:modelValue' &&3173 // omit onVnodeXXX hooks3174 !isReservedProp(name)) {3175 hasHydrationEventBinding = true;3176 }3177 if (isEventHandler && isReservedProp(name)) {3178 hasVnodeHook = true;3179 }3180 if (value.type === 20 /* JS_CACHE_EXPRESSION */ ||3181 ((value.type === 4 /* SIMPLE_EXPRESSION */ ||3182 value.type === 8 /* COMPOUND_EXPRESSION */) &&3183 getConstantType(value, context) > 0)) {3184 // skip if the prop is a cached handler or has constant value3185 return;3186 }3187 if (name === 'ref') {3188 hasRef = true;3189 }3190 else if (name === 'class' && !isComponent) {3191 hasClassBinding = true;3192 }3193 else if (name === 'style' && !isComponent) {3194 hasStyleBinding = true;3195 }3196 else if (name !== 'key' && !dynamicPropNames.includes(name)) {3197 dynamicPropNames.push(name);3198 }3199 }3200 else {3201 hasDynamicKeys = true;3202 }3203 };3204 for (let i = 0; i < props.length; i++) {3205 // static attribute3206 const prop = props[i];3207 if (prop.type === 6 /* ATTRIBUTE */) {3208 const { loc, name, value } = prop;3209 let isStatic = true;3210 if (name === 'ref') {3211 hasRef = true;3212 }3213 // skip :is on <component>3214 if (name === 'is' && tag === 'component') {3215 continue;3216 }3217 properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', isStatic, value ? value.loc : loc)));3218 }3219 else {3220 // directives3221 const { name, arg, exp, loc } = prop;3222 const isBind = name === 'bind';3223 const isOn = name === 'on';3224 // skip v-slot - it is handled by its dedicated transform.3225 if (name === 'slot') {3226 if (!isComponent) {3227 context.onError(createCompilerError(39 /* X_V_SLOT_MISPLACED */, loc));3228 }3229 continue;3230 }3231 // skip v-once - it is handled by its dedicated transform.3232 if (name === 'once') {3233 continue;3234 }3235 // skip v-is and :is on <component>3236 if (name === 'is' ||3237 (isBind && tag === 'component' && isBindKey(arg, 'is'))) {3238 continue;3239 }3240 // skip v-on in SSR compilation3241 if (isOn && ssr) {3242 continue;3243 }3244 // special case for v-bind and v-on with no argument3245 if (!arg && (isBind || isOn)) {3246 hasDynamicKeys = true;3247 if (exp) {3248 if (properties.length) {3249 mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));3250 properties = [];3251 }3252 if (isBind) {3253 mergeArgs.push(exp);3254 }3255 else {3256 // v-on="obj" -> toHandlers(obj)3257 mergeArgs.push({3258 type: 14 /* JS_CALL_EXPRESSION */,3259 loc,3260 callee: context.helper(TO_HANDLERS),3261 arguments: [exp]3262 });3263 }3264 }3265 else {3266 context.onError(createCompilerError(isBind3267 ? 33 /* X_V_BIND_NO_EXPRESSION */3268 : 34 /* X_V_ON_NO_EXPRESSION */, loc));3269 }3270 continue;3271 }3272 const directiveTransform = context.directiveTransforms[name];3273 if (directiveTransform) {3274 // has built-in directive transform.3275 const { props, needRuntime } = directiveTransform(prop, node, context);3276 !ssr && props.forEach(analyzePatchFlag);3277 properties.push(...props);3278 if (needRuntime) {3279 runtimeDirectives.push(prop);3280 if (isSymbol(needRuntime)) {3281 directiveImportMap.set(prop, needRuntime);3282 }3283 }3284 }3285 else {3286 // no built-in transform, this is a user custom directive.3287 runtimeDirectives.push(prop);3288 }3289 }3290 }3291 let propsExpression = undefined;3292 // has v-bind="object" or v-on="object", wrap with mergeProps3293 if (mergeArgs.length) {3294 if (properties.length) {3295 mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));3296 }3297 if (mergeArgs.length > 1) {3298 propsExpression = createCallExpression(context.helper(MERGE_PROPS), mergeArgs, elementLoc);3299 }3300 else {3301 // single v-bind with nothing else - no need for a mergeProps call3302 propsExpression = mergeArgs[0];3303 }3304 }3305 else if (properties.length) {3306 propsExpression = createObjectExpression(dedupeProperties(properties), elementLoc);3307 }3308 // patchFlag analysis3309 if (hasDynamicKeys) {3310 patchFlag |= 16 /* FULL_PROPS */;3311 }3312 else {3313 if (hasClassBinding) {3314 patchFlag |= 2 /* CLASS */;3315 }3316 if (hasStyleBinding) {3317 patchFlag |= 4 /* STYLE */;3318 }3319 if (dynamicPropNames.length) {3320 patchFlag |= 8 /* PROPS */;3321 }3322 if (hasHydrationEventBinding) {3323 patchFlag |= 32 /* HYDRATE_EVENTS */;3324 }3325 }3326 if ((patchFlag === 0 || patchFlag === 32 /* HYDRATE_EVENTS */) &&3327 (hasRef || hasVnodeHook || runtimeDirectives.length > 0)) {3328 patchFlag |= 512 /* NEED_PATCH */;3329 }3330 return {3331 props: propsExpression,3332 directives: runtimeDirectives,3333 patchFlag,3334 dynamicPropNames3335 };3336}3337// Dedupe props in an object literal.3338// Literal duplicated attributes would have been warned during the parse phase,3339// however, it's possible to encounter duplicated `onXXX` handlers with different3340// modifiers. We also need to merge static and dynamic class / style attributes.3341// - onXXX handlers / style: merge into array3342// - class: merge into single expression with concatenation3343function dedupeProperties(properties) {3344 const knownProps = new Map();3345 const deduped = [];3346 for (let i = 0; i < properties.length; i++) {3347 const prop = properties[i];3348 // dynamic keys are always allowed3349 if (prop.key.type === 8 /* COMPOUND_EXPRESSION */ || !prop.key.isStatic) {3350 deduped.push(prop);3351 continue;3352 }3353 const name = prop.key.content;3354 const existing = knownProps.get(name);3355 if (existing) {3356 if (name === 'style' || name === 'class' || name.startsWith('on')) {3357 mergeAsArray(existing, prop);3358 }3359 // unexpected duplicate, should have emitted error during parse3360 }3361 else {3362 knownProps.set(name, prop);3363 deduped.push(prop);3364 }3365 }3366 return deduped;3367}3368function mergeAsArray(existing, incoming) {3369 if (existing.value.type === 17 /* JS_ARRAY_EXPRESSION */) {3370 existing.value.elements.push(incoming.value);3371 }3372 else {3373 existing.value = createArrayExpression([existing.value, incoming.value], existing.loc);3374 }3375}3376function buildDirectiveArgs(dir, context) {3377 const dirArgs = [];3378 const runtime = directiveImportMap.get(dir);3379 if (runtime) {3380 // built-in directive with runtime3381 dirArgs.push(context.helperString(runtime));3382 }3383 else {3384 {3385 // inject statement for resolving directive3386 context.helper(RESOLVE_DIRECTIVE);3387 context.directives.add(dir.name);3388 dirArgs.push(toValidAssetId(dir.name, `directive`));3389 }3390 }3391 const { loc } = dir;3392 if (dir.exp)3393 dirArgs.push(dir.exp);3394 if (dir.arg) {3395 if (!dir.exp) {3396 dirArgs.push(`void 0`);3397 }3398 dirArgs.push(dir.arg);3399 }3400 if (Object.keys(dir.modifiers).length) {3401 if (!dir.arg) {3402 if (!dir.exp) {3403 dirArgs.push(`void 0`);3404 }3405 dirArgs.push(`void 0`);3406 }3407 const trueExpression = createSimpleExpression(`true`, false, loc);3408 dirArgs.push(createObjectExpression(dir.modifiers.map(modifier => createObjectProperty(modifier, trueExpression)), loc));3409 }3410 return createArrayExpression(dirArgs, dir.loc);3411}3412function stringifyDynamicPropNames(props) {3413 let propsNamesString = `[`;3414 for (let i = 0, l = props.length; i < l; i++) {3415 propsNamesString += JSON.stringify(props[i]);3416 if (i < l - 1)3417 propsNamesString += ', ';3418 }3419 return propsNamesString + `]`;3420}3421const EMPTY_OBJ = (process.env.NODE_ENV !== 'production')3422 ? Object.freeze({})3423 : {};3424const EMPTY_ARR = (process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];3425const cacheStringFunction = (fn) => {3426 const cache = Object.create(null);3427 return ((str) => {3428 const hit = cache[str];3429 return hit || (cache[str] = fn(str));3430 });3431};3432const camelizeRE = /-(\w)/g;3433/**3434 * @private3435 */3436const camelize = cacheStringFunction((str) => {3437 return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));3438});3439const transformSlotOutlet = (node, context) => {3440 if (isSlotOutlet(node)) {3441 const { children, loc } = node;3442 const { slotName, slotProps } = processSlotOutlet(node, context);3443 const slotArgs = [3444 context.prefixIdentifiers ? `_ctx.$slots` : `$slots`,3445 slotName3446 ];3447 if (slotProps) {3448 slotArgs.push(slotProps);3449 }3450 if (children.length) {3451 if (!slotProps) {3452 slotArgs.push(`{}`);3453 }3454 slotArgs.push(createFunctionExpression([], children, false, false, loc));3455 }3456 node.codegenNode = createCallExpression(context.helper(RENDER_SLOT), slotArgs, loc);3457 }3458};3459function processSlotOutlet(node, context) {3460 let slotName = `"default"`;3461 let slotProps = undefined;3462 const nonNameProps = [];3463 for (let i = 0; i < node.props.length; i++) {3464 const p = node.props[i];3465 if (p.type === 6 /* ATTRIBUTE */) {3466 if (p.value) {3467 if (p.name === 'name') {3468 slotName = JSON.stringify(p.value.content);3469 }3470 else {3471 p.name = camelize(p.name);3472 nonNameProps.push(p);3473 }3474 }3475 }3476 else {3477 if (p.name === 'bind' && isBindKey(p.arg, 'name')) {3478 if (p.exp)3479 slotName = p.exp;3480 }3481 else {3482 if (p.name === 'bind' && p.arg && isStaticExp(p.arg)) {3483 p.arg.content = camelize(p.arg.content);3484 }3485 nonNameProps.push(p);3486 }3487 }3488 }3489 if (nonNameProps.length > 0) {3490 const { props, directives } = buildProps(node, context, nonNameProps);3491 slotProps = props;3492 if (directives.length) {3493 context.onError(createCompilerError(35 /* X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET */, directives[0].loc));3494 }3495 }3496 return {3497 slotName,3498 slotProps3499 };3500}3501const fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^\s*function(?:\s+[\w$]+)?\s*\(/;3502const transformOn = (dir, node, context, augmentor) => {3503 const { loc, modifiers, arg } = dir;3504 if (!dir.exp && !modifiers.length) {3505 context.onError(createCompilerError(34 /* X_V_ON_NO_EXPRESSION */, loc));3506 }3507 let eventName;3508 if (arg.type === 4 /* SIMPLE_EXPRESSION */) {3509 if (arg.isStatic) {3510 const rawName = arg.content;3511 // for all event listeners, auto convert it to camelCase. See issue #22493512 eventName = createSimpleExpression(toHandlerKey(camelize$1(rawName)), true, arg.loc);3513 }3514 else {3515 // #23883516 eventName = createCompoundExpression([3517 `${context.helperString(TO_HANDLER_KEY)}(`,3518 arg,3519 `)`3520 ]);3521 }3522 }3523 else {3524 // already a compound expression.3525 eventName = arg;3526 eventName.children.unshift(`${context.helperString(TO_HANDLER_KEY)}(`);3527 eventName.children.push(`)`);3528 }3529 // handler processing3530 let exp = dir.exp;3531 if (exp && !exp.content.trim()) {3532 exp = undefined;3533 }3534 let shouldCache = context.cacheHandlers && !exp;3535 if (exp) {3536 const isMemberExp = isMemberExpression(exp.content);3537 const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content));3538 const hasMultipleStatements = exp.content.includes(`;`);3539 if ((process.env.NODE_ENV !== 'production') && true) {3540 validateBrowserExpression(exp, context, false, hasMultipleStatements);3541 }3542 if (isInlineStatement || (shouldCache && isMemberExp)) {3543 // wrap inline statement in a function expression3544 exp = createCompoundExpression([3545 `${isInlineStatement3546 ? `$event`3547 : `${ ``}(...args)`} => ${hasMultipleStatements ? `{` : `(`}`,3548 exp,3549 hasMultipleStatements ? `}` : `)`3550 ]);3551 }3552 }3553 let ret = {3554 props: [...
vFor.js
Source:vFor.js
...212 key: undefined,213 index: undefined214 }215 {216 validateBrowserExpression(result.source, context)217 }218 let valueContent = LHS.trim()219 .replace(stripParensRE, '')220 .trim()221 const trimmedOffset = LHS.indexOf(valueContent)222 const iteratorMatch = valueContent.match(forIteratorRE)223 if (iteratorMatch) {224 valueContent = valueContent.replace(forIteratorRE, '').trim()225 const keyContent = iteratorMatch[1].trim()226 let keyOffset227 if (keyContent) {228 keyOffset = exp.indexOf(keyContent, trimmedOffset + valueContent.length)229 result.key = createAliasExpression(loc, keyContent, keyOffset)230 {231 validateBrowserExpression(result.key, context, true)232 }233 }234 if (iteratorMatch[2]) {235 const indexContent = iteratorMatch[2].trim()236 if (indexContent) {237 result.index = createAliasExpression(238 loc,239 indexContent,240 exp.indexOf(241 indexContent,242 result.key243 ? keyOffset + keyContent.length244 : trimmedOffset + valueContent.length245 )246 )247 {248 validateBrowserExpression(result.index, context, true)249 }250 }251 }252 }253 if (valueContent) {254 result.value = createAliasExpression(loc, valueContent, trimmedOffset)255 {256 validateBrowserExpression(result.value, context, true)257 }258 }259 return result260}261function createAliasExpression (range, content, offset) {262 return createSimpleExpression(263 content,264 false,265 getInnerRange(range, offset, content.length)266 )267}268export function createForLoopParams ({ value, key, index }, memoArgs = []) {269 return createParamsList([value, key, index, ...memoArgs])270}...
vIf.js
Source:vIf.js
...55 const loc = dir.exp ? dir.exp.loc : node.loc56 dir.exp = createSimpleExpression(`true`, false, loc)57 }58 if (dir.exp) {59 validateBrowserExpression(dir.exp, context)60 }61 if (dir.name === 'if') {62 const branch = createIfBranch(node, dir)63 const ifNode = { type: 9, loc: node.loc, branches: [branch] }64 context.replaceNode(ifNode)65 if (processCodegen) {66 return processCodegen(ifNode, branch, true)67 }68 } else {69 const siblings = context.parent.children70 const comments = []71 let i = siblings.indexOf(node)72 while (i-- >= -1) {73 const sibling = siblings[i]...
vOn.js
Source:vOn.js
...43 const isMemberExp = isMemberExpression(exp.content)44 const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content))45 const hasMultipleStatements = exp.content.includes(`;`)46 {47 validateBrowserExpression(exp, context, false, hasMultipleStatements)48 }49 if (isInlineStatement || (shouldCache && isMemberExp)) {50 exp = createCompoundExpression([51 `${isInlineStatement ? `$event` : `${``}(...args)`} => ${52 hasMultipleStatements ? `{` : `(`53 }`,54 exp,55 hasMultipleStatements ? `}` : `)`56 ])57 }58 }59 let ret = {60 props: [61 createObjectProperty(...
validateExpression.js
Source:validateExpression.js
1const prohibitedKeywordRE = new RegExp(2 '\\b' +3 (4 'do,if,for,let,new,try,var,case,else,with,await,break,catch,class,const,' +5 'super,throw,while,yield,delete,export,import,return,switch,default,' +6 'extends,finally,continue,debugger,function,arguments,typeof,void'7 )8 .split(',')9 .join('\\b|\\b') +10 '\\b'11)12// strip strings in expressions13const stripStringRE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g14export function validateBrowserExpression (15 node,16 context,17 asParams = false,18 asRawStatements = false19) {20 const exp = node.content21 if (!exp.trim()) {22 return23 }24 try {25 new Function(26 asRawStatements27 ? ` ${exp} `28 : `return ${asParams ? `(${exp}) => {}` : `(${exp})`}`29 )30 } catch (e) {31 let message = e.message32 const keywordMatch = exp33 .replace(stripStringRE, '')34 .match(prohibitedKeywordRE)35 if (keywordMatch) {36 message = `avoid using JavaScript keyword as property name: "${keywordMatch[0]}"`37 }38 }...
Using AI Code Generation
1const { validateBrowserExpression } = require('playwright');2const result = validateBrowserExpression('window.location.href');3console.log(result);4const { validateSelector } = require('playwright');5const result = validateSelector('css=div');6console.log(result);7const { validateTimeout } = require('playwright');8const result = validateTimeout(5000);9console.log(result);10const { validateWaitForOptions } = require('playwright');11const result = validateWaitForOptions({ timeout: 5000 });12console.log(result);13const { validateWaitForSelectorOptions } = require('playwright');14const result = validateWaitForSelectorOptions({ timeout: 5000 });15console.log(result);16const { validateWaitForFunctionOptions } = require('playwright');17const result = validateWaitForFunctionOptions({ timeout: 5000 });18console.log(result);19const { validateClickOptions } = require('playwright');20const result = validateClickOptions({ timeout: 5000 });21console.log(result);22const { validateDblClickOptions } = require('playwright');23const result = validateDblClickOptions({ timeout: 5000 });24console.log(result);25const { validateTapOptions } = require('playwright');26const result = validateTapOptions({ timeout: 5000 });27console.log(result);28const { validateFillOptions } = require('playwright');29const result = validateFillOptions({ timeout: 5000 });30console.log(result);31const { validateSelectOptions } = require('playwright');32const result = validateSelectOptions({ timeout: 5000 });33console.log(result);34const { validatePressOptions } = require('playwright');
Using AI Code Generation
1const { validateBrowserExpression } = require('playwright-core/lib/server/supplements/recorder/recorderSupplement');2console.log(validateBrowserExpression('document.querySelector("input")'));3console.log(validateBrowserExpression('document.querySelector("input").value'));4console.log(validateBrowserExpression('document.querySelector("input").value = "abc"'));5console.log(validateBrowserExpression('document.querySelector("input").click()'));6const { validateBrowserExpression } = require('playwright-core/lib/server/supplements/recorder/recorderSupplement');7console.log(validateBrowserExpression('document.querySelector("input")'));8console.log(validateBrowserExpression('document.querySelector("input").value'));9console.log(validateBrowserExpression('document.querySelector("input").value = "abc"'));10console.log(validateBrowserExpression('document.querySelector("input").click()'));11const { validateBrowserExpression } = require('playwright-core/lib/server/supplements/recorder/recorderSupplement');12console.log(validateBrowserExpression('document.querySelector("input")'));13console.log(validateBrowserExpression('document.querySelector("input").value'));14console.log(validateBrowserExpression('document.querySelector("input").value = "abc"'));15console.log(validateBrowserExpression('document.querySelector("input").click()'));16const { validateBrowserExpression } = require('playwright-core/lib/server/supplements/recorder/recorderSupplement');17console.log(validateBrowserExpression('document.querySelector("input")'));18console.log(validateBrowserExpression('document.querySelector("input").value'));19console.log(validateBrowserExpression('document.querySelector("input").value = "abc"'));20console.log(validateBrowserExpression('document.querySelector("input").click()'));21const { validateBrowserExpression } = require('playwright-core/lib/server/supplements/recorder/recorderSupplement');22console.log(validateBrowserExpression('document.querySelector("input")'));23console.log(validateBrowserExpression('document.querySelector("input").value'));24console.log(validateBrowserExpression('document.querySelector("input").value = "abc"'));25console.log(validateBrowserExpression('document.querySelector("input").click()'));26const { validateBrowserExpression } = require('playwright-core/lib/server/supplements/recorder/recorderSupplement');27console.log(validate
Using AI Code Generation
1const {validateBrowserExpression} = require('playwright/lib/server/browserType');2const {assert} = require('chai');3const playwright = require('playwright');4const {chromium} = playwright;5const browser = await chromium.launch();6const context = await browser.newContext();7const page = await context.newPage();8const result = validateBrowserExpression(page, 'window.location.href');9assert.equal(result, 'window.location.href');10await browser.close();11const {validateBrowserExpression} = require('playwright/lib/server/browserType');12const {assert} = require('chai');13const playwright = require('playwright');14const {chromium} = playwright;15const browser = await chromium.launch();16const context = await browser.newContext();17const page = await context.newPage();18const result = validateBrowserExpression(page, 'window.location.href');19assert.equal(result, 'window.location.href');20await browser.close();21const {validateBrowserExpression} = require('playwright/lib/server/browserType');22const {assert} = require('chai');23const playwright = require('playwright');24const {chromium} = playwright;25const browser = await chromium.launch();26const context = await browser.newContext();27const page = await context.newPage();28const result = validateBrowserExpression(page, 'window.location.href');29assert.equal(result, 'window.location.href');30await browser.close();31const {validateBrowserExpression} = require('playwright/lib/server/browserType');32const {assert} = require('chai');33const playwright = require('playwright');34const {chromium} = playwright;35const browser = await chromium.launch();36const context = await browser.newContext();37const page = await context.newPage();38const result = validateBrowserExpression(page, 'window.location.href');39assert.equal(result, 'window.location.href');40await browser.close();41const {validateBrowserExpression} = require('playwright/lib/server/browserType');42const {assert} = require('chai');43const playwright = require('playwright');44const {chromium} = playwright;45const browser = await chromium.launch();46const context = await browser.newContext();
Using AI Code Generation
1const { validateBrowserExpression } = require('playwright/lib/server/browserType');2const browserExpression = 'chromium, firefox';3const result = await validateBrowserExpression(browserExpression);4console.log(result);5const { validateBrowserExpression } = require('playwright/lib/server/browserType');6const browserExpression = 'chromium, firefox';7const result = await validateBrowserExpression(browserExpression);8console.log(result);9const { validateBrowserExpression } = require('playwright/lib/server/browserType');10const browserExpression = 'chromium, firefox';11const result = await validateBrowserExpression(browserExpression);12console.log(result);13const { validateBrowserExpression } = require('playwright/lib/server/browserType');14const browserExpression = 'chromium, firefox';15const result = await validateBrowserExpression(browserExpression);16console.log(result);17const { validateBrowserExpression } = require('playwright/lib/server/browserType');18const browserExpression = 'chromium, firefox';19const result = await validateBrowserExpression(browserExpression);20console.log(result);21const { validateBrowserExpression } = require('playwright/lib/server/browserType');22const browserExpression = 'chromium, firefox';23const result = await validateBrowserExpression(browserExpression);24console.log(result);25const { validateBrowserExpression } = require('playwright/lib/server/browserType');26const browserExpression = 'chromium, firefox';27const result = await validateBrowserExpression(browserExpression);28console.log(result);29const { validateBrowserExpression } = require('playwright/lib/server/browserType');30const browserExpression = 'chromium, firefox';31const result = await validateBrowserExpression(browserExpression);32console.log(result);33const { validateBrowserExpression } = require('playwright/lib/server/browserType');
Using AI Code Generation
1const { validateBrowserExpression } = require('playwright/lib/server/browserContext');2const expression = '1+1';3const context = null;4const returnByValue = true;5const isFunction = false;6const arg = null;7const world = null;8const result = validateBrowserExpression(expression, context, returnByValue, isFunction, arg, world);9console.log(result);10{ value: 2 }
Using AI Code Generation
1const { validateBrowserExpression } = require('@playwright/test/lib/utils/validateBrowserExpression');2const expression = 'window.location.href';3const result = validateBrowserExpression(expression);4console.log(result);5{ isValid: true, error: null }6const { validateBrowserExpression } = require('@playwright/test/lib/utils/validateBrowserExpression');7const expression = 'window.location.href';8const result = validateBrowserExpression(expression);9console.log(result.error);10const { validateBrowserExpression } = require('@playwright/test/lib/utils/validateBrowserExpression');11const expression = 'window.location.href';12const result = validateBrowserExpression(expression);13console.log(result.error);14const { validateBrowserExpression } = require('@playwright/test/lib/utils/validateBrowserExpression');15const expression = 'window.location.href';16const result = validateBrowserExpression(expression);17console.log(result.error);18const { validateBrowserExpression } = require('@playwright/test/lib/utils/validateBrowserExpression');19const expression = 'window.location.href';20const result = validateBrowserExpression(expression);21console.log(result.error);22const { validateBrowserExpression } = require('@playwright/test/lib/utils/validateBrowserExpression');23const expression = 'window.location.href';24const result = validateBrowserExpression(expression);25console.log(result.error);
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!