{"size": 9789, "ext": "js", "max_stars_repo_path": "es/packages/compiler-core/src/transform.js", "max_stars_repo_name": "cisen/sourcecode-vue-next-01", "max_stars_repo_stars_event_min_datetime": null, "max_stars_repo_stars_event_max_datetime": null, "max_issues_repo_path": "es/packages/compiler-core/src/transform.js", "max_issues_repo_name": "cisen/sourcecode-vue-next-01", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "es/packages/compiler-core/src/transform.js", "max_forks_repo_name": "cisen/sourcecode-vue-next-01", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": null, "max_issues_count": null, "max_forks_count": null, "avg_line_length": 34.9607142857, "max_line_length": 187, "alphanum_fraction": 0.5096536929}
import { createSimpleExpression, createCallExpression, createCacheExpression } from './ast';
import { isString, isArray } from '@vue/shared';
import { defaultOnError } from './errors';
import { TO_STRING, FRAGMENT, helperNameMap, WITH_DIRECTIVES, CREATE_BLOCK, CREATE_COMMENT } from './runtimeHelpers';
import { isVSlot, createBlockExpression } from './utils';
import { hoistStatic, isSingleElementRoot } from './transforms/hoistStatic';
function createTransformContext(root, { prefixIdentifiers = false, hoistStatic = false, cacheHandlers = false, nodeTransforms = [], directiveTransforms = {}, onError = defaultOnError }) {
    const context = {
        root,
        helpers: new Set(),
        components: new Set(),
        directives: new Set(),
        hoists: [],
        cached: 0,
        identifiers: {},
        scopes: {
            vFor: 0,
            vSlot: 0,
            vPre: 0,
            vOnce: 0
        },
        prefixIdentifiers,
        hoistStatic,
        cacheHandlers,
        nodeTransforms,
        directiveTransforms,
        onError,
        parent: null,
        currentNode: root,
        childIndex: 0,
        helper(name) {
            context.helpers.add(name);
            return name;
        },
        helperString(name) {
            return ((context.prefixIdentifiers ? `` : `_`) +
                helperNameMap[context.helper(name)]);
        },
        replaceNode(node) {
            /* istanbul ignore if */
            if (__DEV__) {
                if (!context.currentNode) {
                    throw new Error(`Node being replaced is already removed.`);
                }
                if (!context.parent) {
                    throw new Error(`Cannot replace root node.`);
                }
            }
            context.parent.children[context.childIndex] = context.currentNode = node;
        },
        removeNode(node) {
            if (__DEV__ && !context.parent) {
                throw new Error(`Cannot remove root node.`);
            }
            const list = context.parent.children;
            const removalIndex = node
                ? list.indexOf(node)
                : context.currentNode
                    ? context.childIndex
                    : -1;
            /* istanbul ignore if */
            if (__DEV__ && removalIndex < 0) {
                throw new Error(`node being removed is not a child of current parent`);
            }
            if (!node || node === context.currentNode) {
                // current node removed
                context.currentNode = null;
                context.onNodeRemoved();
            }
            else {
                // sibling node removed
                if (context.childIndex > removalIndex) {
                    context.childIndex--;
                    context.onNodeRemoved();
                }
            }
            context.parent.children.splice(removalIndex, 1);
        },
        onNodeRemoved: () => { },
        addIdentifiers(exp) {
            // identifier tracking only happens in non-browser builds.
            if (!__BROWSER__) {
                if (isString(exp)) {
                    addId(exp);
                }
                else if (exp.identifiers) {
                    exp.identifiers.forEach(addId);
                }
                else if (exp.type === 4 /* SIMPLE_EXPRESSION */) {
                    addId(exp.content);
                }
            }
        },
        removeIdentifiers(exp) {
            if (!__BROWSER__) {
                if (isString(exp)) {
                    removeId(exp);
                }
                else if (exp.identifiers) {
                    exp.identifiers.forEach(removeId);
                }
                else if (exp.type === 4 /* SIMPLE_EXPRESSION */) {
                    removeId(exp.content);
                }
            }
        },
        hoist(exp) {
            context.hoists.push(exp);
            return createSimpleExpression(`_hoisted_${context.hoists.length}`, false, exp.loc, true);
        },
        cache(exp, isVNode = false) {
            return createCacheExpression(++context.cached, exp, isVNode);
        }
    };
    function addId(id) {
        const { identifiers } = context;
        if (identifiers[id] === undefined) {
            identifiers[id] = 0;
        }
        identifiers[id]++;
    }
    function removeId(id) {
        context.identifiers[id]--;
    }
    return context;
}
export function transform(root, options) {
    const context = createTransformContext(root, options);
    traverseNode(root, context);
    if (options.hoistStatic) {
        hoistStatic(root, context);
    }
    finalizeRoot(root, context);
}
function finalizeRoot(root, context) {
    const { helper } = context;
    const { children } = root;
    const child = children[0];
    if (children.length === 1) {
        // if the single child is an element, turn it into a block.
        if (isSingleElementRoot(root, child) && child.codegenNode) {
            // single element root is never hoisted so codegenNode will never be
            // SimpleExpressionNode
            const codegenNode = child.codegenNode;
            if (codegenNode.type !== 20 /* JS_CACHE_EXPRESSION */) {
                if (codegenNode.callee === WITH_DIRECTIVES) {
                    codegenNode.arguments[0].callee = helper(CREATE_BLOCK);
                }
                else {
                    codegenNode.callee = helper(CREATE_BLOCK);
                }
                root.codegenNode = createBlockExpression(codegenNode, context);
            }
            else {
                root.codegenNode = codegenNode;
            }
        }
        else {
            // - single <slot/>, IfNode, ForNode: already blocks.
            // - single text node: always patched.
            // root codegen falls through via genNode()
            root.codegenNode = child;
        }
    }
    else if (children.length > 1) {
        // root has multiple nodes - return a fragment block.
        root.codegenNode = createBlockExpression(createCallExpression(helper(CREATE_BLOCK), [
            helper(FRAGMENT),
            `null`,
            root.children
        ]), context);
    }
    else {
        // no children = noop. codegen will return null.
    }
    // finalize meta information
    root.helpers = [...context.helpers];
    root.components = [...context.components];
    root.directives = [...context.directives];
    root.hoists = context.hoists;
    root.cached = context.cached;
}
export function traverseChildren(parent, context) {
    let i = 0;
    const nodeRemoved = () => {
        i--;
    };
    for (; i < parent.children.length; i++) {
        const child = parent.children[i];
        if (isString(child))
            continue;
        context.currentNode = child;
        context.parent = parent;
        context.childIndex = i;
        context.onNodeRemoved = nodeRemoved;
        traverseNode(child, context);
    }
}
export function traverseNode(node, context) {
    // apply transform plugins
    const { nodeTransforms } = context;
    const exitFns = [];
    for (let i = 0; i < nodeTransforms.length; i++) {
        const onExit = nodeTransforms[i](node, context);
        if (onExit) {
            if (isArray(onExit)) {
                exitFns.push(...onExit);
            }
            else {
                exitFns.push(onExit);
            }
        }
        if (!context.currentNode) {
            // node was removed
            return;
        }
        else {
            // node may have been replaced
            node = context.currentNode;
        }
    }
    switch (node.type) {
        case 3 /* COMMENT */:
            // inject import for the Comment symbol, which is needed for creating
            // comment nodes with `createVNode`
            context.helper(CREATE_COMMENT);
            break;
        case 5 /* INTERPOLATION */:
            // no need to traverse, but we need to inject toString helper
            context.helper(TO_STRING);
            break;
        // for container types, further traverse downwards
        case 9 /* IF */:
            for (let i = 0; i < node.branches.length; i++) {
                traverseChildren(node.branches[i], context);
            }
            break;
        case 11 /* FOR */:
        case 1 /* ELEMENT */:
        case 0 /* ROOT */:
            traverseChildren(node, context);
            break;
    }
    // exit transforms
    let i = exitFns.length;
    while (i--) {
        exitFns[i]();
    }
}
export function createStructuralDirectiveTransform(name, fn) {
    const matches = isString(name)
        ? (n) => n === name
        : (n) => name.test(n);
    return (node, context) => {
        if (node.type === 1 /* ELEMENT */) {
            const { props } = node;
            // structural directive transforms are not concerned with slots
            // as they are handled separately in vSlot.ts
            if (node.tagType === 3 /* TEMPLATE */ && props.some(isVSlot)) {
                return;
            }
            const exitFns = [];
            for (let i = 0; i < props.length; i++) {
                const prop = props[i];
                if (prop.type === 7 /* DIRECTIVE */ && matches(prop.name)) {
                    // structural directives are removed to avoid infinite recursion
                    // also we remove them *before* applying so that it can further
                    // traverse itself in case it moves the node around
                    props.splice(i, 1);
                    i--;
                    const onExit = fn(node, prop, context);
                    if (onExit)
                        exitFns.push(onExit);
                }
            }
            return exitFns;
        }
    };
}
