How to use validateBrowserExpression method in Playwright Internal

Best JavaScript code snippet using playwright-internal

compiler-dom.global.js

Source:compiler-dom.global.js Github

copy

Full Screen

...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: [...

Full Screen

Full Screen

vue.global.js

Source:vue.global.js Github

copy

Full Screen

...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: [...

Full Screen

Full Screen

compiler-dom.esm-browser.js

Source:compiler-dom.esm-browser.js Github

copy

Full Screen

...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: [...

Full Screen

Full Screen

compiler-core.esm-bundler.js

Source:compiler-core.esm-bundler.js Github

copy

Full Screen

...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: [...

Full Screen

Full Screen

vFor.js

Source:vFor.js Github

copy

Full Screen

...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}...

Full Screen

Full Screen

vIf.js

Source:vIf.js Github

copy

Full Screen

...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]...

Full Screen

Full Screen

vOn.js

Source:vOn.js Github

copy

Full Screen

...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(...

Full Screen

Full Screen

validateExpression.js

Source:validateExpression.js Github

copy

Full Screen

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 }...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

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');

Full Screen

Using AI Code Generation

copy

Full Screen

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

Full Screen

Using AI Code Generation

copy

Full Screen

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();

Full Screen

Using AI Code Generation

copy

Full Screen

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');

Full Screen

Using AI Code Generation

copy

Full Screen

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 }

Full Screen

Using AI Code Generation

copy

Full Screen

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);

Full Screen

Playwright tutorial

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.

Chapters:

  1. What is Playwright : Playwright is comparatively new but has gained good popularity. Get to know some history of the Playwright with some interesting facts connected with it.
  2. How To Install Playwright : Learn in detail about what basic configuration and dependencies are required for installing Playwright and run a test. Get a step-by-step direction for installing the Playwright automation framework.
  3. Playwright Futuristic Features: Launched in 2020, Playwright gained huge popularity quickly because of some obliging features such as Playwright Test Generator and Inspector, Playwright Reporter, Playwright auto-waiting mechanism and etc. Read up on those features to master Playwright testing.
  4. What is Component Testing: Component testing in Playwright is a unique feature that allows a tester to test a single component of a web application without integrating them with other elements. Learn how to perform Component testing on the Playwright automation framework.
  5. Inputs And Buttons In Playwright: Every website has Input boxes and buttons; learn about testing inputs and buttons with different scenarios and examples.
  6. Functions and Selectors in Playwright: Learn how to launch the Chromium browser with Playwright. Also, gain a better understanding of some important functions like “BrowserContext,” which allows you to run multiple browser sessions, and “newPage” which interacts with a page.
  7. Handling Alerts and Dropdowns in Playwright : Playwright interact with different types of alerts and pop-ups, such as simple, confirmation, and prompt, and different types of dropdowns, such as single selector and multi-selector get your hands-on with handling alerts and dropdown in Playright testing.
  8. Playwright vs Puppeteer: Get to know about the difference between two testing frameworks and how they are different than one another, which browsers they support, and what features they provide.
  9. Run Playwright Tests on LambdaTest: Playwright testing with LambdaTest leverages test performance to the utmost. You can run multiple Playwright tests in Parallel with the LammbdaTest test cloud. Get a step-by-step guide to run your Playwright test on the LambdaTest platform.
  10. Playwright Python Tutorial: Playwright automation framework support all major languages such as Python, JavaScript, TypeScript, .NET and etc. However, there are various advantages to Python end-to-end testing with Playwright because of its versatile utility. Get the hang of Playwright python testing with this chapter.
  11. Playwright End To End Testing Tutorial: Get your hands on with Playwright end-to-end testing and learn to use some exciting features such as TraceViewer, Debugging, Networking, Component testing, Visual testing, and many more.
  12. Playwright Video Tutorial: Watch the video tutorials on Playwright testing from experts and get a consecutive in-depth explanation of Playwright automation testing.

Run Playwright Internal automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful