コード例 #1
0
function judgeInjectArraySuspect(node, ctx) {
    if (node.type === "VariableDeclaration") {
        // suspect can only be a VariableDeclaration (statement) in case of
        // explicitly marked via /*@ngInject*/, not via references because
        // references follow to VariableDeclarator (child)

        // /*@ngInject*/ var foo = function($scope) {} and

        if (node.declarations.length !== 1) {
            // more than one declarator => exit
            return;
        }

        // one declarator => jump over declaration into declarator
        // rest of code will treat it as any (referenced) declarator
        node = node.declarations[0];
    }

    // onode is a top-level node (inside function block), later verified
    // node is inner match, descent in multiple steps
    var onode = null;
    var declaratorName = null;
    if (node.type === "VariableDeclarator") {
        onode = node.$parent;
        declaratorName = node.id.name;
        node = node.init; // var foo = ___;
    } else {
        onode = node;
    }

    // suspect must be inside of a block or at the top-level (i.e. inside of node.$parent.body[])
    if (!node || !onode.$parent || is.noneof(onode.$parent.type, ["Program", "BlockStatement"])) {
        return;
    }

    var insertPos = {
        pos: onode.range[1],
        loc: onode.loc.end
    };
    var isSemicolonTerminated = (ctx.src[insertPos.pos - 1] === ";");

    node = jumpOverIife(node);

    if (ctx.isFunctionExpressionWithArgs(node)) {
        // var x = 1, y = function(a,b) {}, z;

        assert(declaratorName);
        addRemoveInjectArray(
            node.params,
            isSemicolonTerminated ? insertPos : {
                pos: node.range[1],
                loc: node.loc.end
            },
            declaratorName);

    } else if (ctx.isFunctionDeclarationWithArgs(node)) {
        // /*@ngInject*/ function foo($scope) {}

        addRemoveInjectArray(
            node.params,
            insertPos,
            node.id.name);

    } else if (node.type === "ExpressionStatement" && node.expression.type === "AssignmentExpression" &&
        ctx.isFunctionExpressionWithArgs(node.expression.right)) {
        // /*@ngInject*/ foo.bar[0] = function($scope) {}

        var name = ctx.srcForRange(node.expression.left.range);
        addRemoveInjectArray(
            node.expression.right.params,
            isSemicolonTerminated ? insertPos : {
                pos: node.expression.right.range[1],
                loc: node.expression.right.loc.end
            },
            name);

    } else if (node = followReference(node)) {
        // node was a reference and followed node now is either a
        // FunctionDeclaration or a VariableDeclarator
        // => recurse

        judgeInjectArraySuspect(node, ctx);
    }


    function getIndent(pos) {
        var src = ctx.src;
        var lineStart = src.lastIndexOf("\n", pos - 1) + 1;
        var i = lineStart;
        for (; src[i] === " " || src[i] === "\t"; i++) {
        }
        return src.slice(lineStart, i);
    }

    function addRemoveInjectArray(params, posAfterFunctionDeclaration, name) {
        // if an existing something.$inject = [..] exists then is will always be recycled when rebuilding

        var indent = getIndent(posAfterFunctionDeclaration.pos);

        var foundSuspectInBody = false;
        var existingExpressionStatementWithArray = null;
        var troublesomeReturn = false;
        onode.$parent.body.forEach(function(bnode) {
            if (bnode === onode) {
                foundSuspectInBody = true;
            }

            if (hasInjectArray(bnode)) {
                if (existingExpressionStatementWithArray) {
                    throw fmt("conflicting inject arrays at line {0} and {1}",
                        posToLine(existingExpressionStatementWithArray.range[0], ctx.src),
                        posToLine(bnode.range[0], ctx.src));
                }
                existingExpressionStatementWithArray = bnode;
            }

            // there's a return statement before our function
            if (!foundSuspectInBody && bnode.type === "ReturnStatement") {
                troublesomeReturn = bnode;
            }
        });
        assert(foundSuspectInBody);
        if (onode.type === "FunctionDeclaration") {
            troublesomeReturn = firstNonPrologueStatement(onode.$parent.body);
        }

        if (troublesomeReturn && !existingExpressionStatementWithArray) {
            posAfterFunctionDeclaration = skipPrevNewline(troublesomeReturn.range[0], troublesomeReturn.loc.start);
        }

        function hasInjectArray(node) {
            var lvalue;
            var assignment;
            return (node && node.type === "ExpressionStatement" && (assignment = node.expression).type === "AssignmentExpression" &&
                assignment.operator === "=" &&
                (lvalue = assignment.left).type === "MemberExpression" &&
                ((lvalue.computed === false && ctx.srcForRange(lvalue.object.range) === name && lvalue.property.name === "$inject") ||
                    (lvalue.computed === true && ctx.srcForRange(lvalue.object.range) === name && lvalue.property.type === "Literal" && lvalue.property.value === "$inject")));
        }

        function skipPrevNewline(pos, loc) {
            var prevLF = ctx.src.lastIndexOf("\n", pos);
            if (prevLF === -1) {
                return { pos: pos, loc: loc };
            }
            if (prevLF >= 1 && ctx.src[prevLF - 1] === "\r") {
                --prevLF;
            }

            if (/\S/g.test(ctx.src.slice(prevLF, pos - 1))) { // any non-whitespace chars between prev newline and pos?
                return { pos: pos, loc: loc };
            }

            return {
                pos: prevLF,
                loc: {
                    line: loc.line - 1,
                    column: prevLF - ctx.src.lastIndexOf("\n", prevLF) - 1,
                }
            };
        }

        if (ctx.mode === "rebuild" && existingExpressionStatementWithArray) {
            var strNoWhitespace = fmt("{2}.$inject = {3};", null, null, name, ctx.stringify(ctx, params, ctx.quot));
            ctx.fragments.push({
                start: existingExpressionStatementWithArray.range[0],
                end: existingExpressionStatementWithArray.range[1],
                str: strNoWhitespace,
                loc: {
                    start: existingExpressionStatementWithArray.loc.start,
                    end: existingExpressionStatementWithArray.loc.end
                }
            });
        } else if (ctx.mode === "remove" && existingExpressionStatementWithArray) {
            var start = skipPrevNewline(existingExpressionStatementWithArray.range[0], existingExpressionStatementWithArray.loc.start);
            ctx.fragments.push({
                start: start.pos,
                end: existingExpressionStatementWithArray.range[1],
                str: "",
                loc: {
                    start: start.loc,
                    end: existingExpressionStatementWithArray.loc.end
                }
            });
        } else if (is.someof(ctx.mode, ["add", "rebuild"]) && !existingExpressionStatementWithArray) {
            var str = fmt("{0}{1}{2}.$inject = {3};", EOL, indent, name, ctx.stringify(ctx, params, ctx.quot));
            ctx.fragments.push({
                start: posAfterFunctionDeclaration.pos,
                end: posAfterFunctionDeclaration.pos,
                str: str,
                loc: {
                    start: posAfterFunctionDeclaration.loc,
                    end: posAfterFunctionDeclaration.loc
                }
            });
        }
    }
}
コード例 #2
0
function isNonFunctionBlock(node) {
    return node.type === "BlockStatement" && is.noneof(node.$parent.type, ["FunctionDeclaration", "FunctionExpression"]);
}
コード例 #3
0
function matchNgUi(node) {
    // $stateProvider.state("myState", {
    //     ...
    //     controller: function($scope)
    //     controllerProvider: function($scope)
    //     templateProvider: function($scope)
    //     onEnter: function($scope)
    //     onExit: function($scope)
    // });
    // $stateProvider.state("myState", {... resolve: {f: function($scope) {}, ..} ..})
    // $stateProvider.state("myState", {... params: {params: {simple: function($scope) {}, inValue: { value: function($scope) {} }} ..})
    // $stateProvider.state("myState", {... views: {... somename: {... controller: fn, controllerProvider: fn, templateProvider: fn, resolve: {f: fn}}}})
    //
    // stateHelperProvider.setNestedState({ sameasregularstate, children: [sameasregularstate, ..]})
    // stateHelperProvider.setNestedState({ sameasregularstate, children: [sameasregularstate, ..]}, true)
    //
    // $urlRouterProvider.when(.., function($scope) {})
    //
    // $modal.open see matchMaterialShowModalOpen

    // we already know that node is a (non-computed) method call
    var callee = node.callee;
    var obj = callee.object; // identifier or expression
    var method = callee.property; // identifier
    var args = node.arguments;

    // shortcut for $urlRouterProvider.when(.., function($scope) {})
    if (obj.$chained === chainedUrlRouterProvider || (obj.type === "Identifier" && obj.name === "$urlRouterProvider")) {
        node.$chained = chainedUrlRouterProvider;

        if (method.name === "when" && args.length >= 1) {
            return last(args);
        }
        return false;
    }

    // everything below is for $stateProvider and stateHelperProvider alone
    if (!(obj.$chained === chainedStateProvider || (obj.type === "Identifier" && is.someof(obj.name, ["$stateProvider", "stateHelperProvider"])))) {
        return false;
    }
    node.$chained = chainedStateProvider;

    if (is.noneof(method.name, ["state", "setNestedState"])) {
        return false;
    }

    // $stateProvider.state({ ... }) and $stateProvider.state("name", { ... })
    // stateHelperProvider.setNestedState({ .. }) and stateHelperProvider.setNestedState({ .. }, true)
    if (!(args.length >= 1 && args.length <= 2)) {
        return false;
    }

    var configArg = (method.name === "state" ? last(args) : args[0]);

    var res = [];

    recursiveMatch(configArg);

    var filteredRes = res.filter(Boolean);
    return (filteredRes.length === 0 ? false : filteredRes);


    function recursiveMatch(objectExpressionNode) {
        if (!objectExpressionNode || objectExpressionNode.type !== "ObjectExpression") {
            return false;
        }

        var properties = objectExpressionNode.properties;

        matchStateProps(properties, res);

        var childrenArrayExpression = matchProp("children", properties);
        var children = childrenArrayExpression && childrenArrayExpression.elements;

        if (!children) {
            return;
        }
        children.forEach(recursiveMatch);
    }

    function matchStateProps(props, res) {
        var simple = [
            matchProp("controller", props),
            matchProp("controllerProvider", props),
            matchProp("templateProvider", props),
            matchProp("onEnter", props),
            matchProp("onExit", props),
        ];
        res.push.apply(res, simple);

        // {resolve: ..}
        res.push.apply(res, matchResolve(props));

        // {params: {simple: function($scope) {}, inValue: { value: function($scope) {} }}
        var a = matchProp("params", props);
        if (a && a.type === "ObjectExpression") {
            a.properties.forEach(function(prop) {
                if (prop.value.type === "ObjectExpression") {
                    res.push(matchProp("value", prop.value.properties));
                } else {
                    res.push(prop.value);
                }
            });
        }

        // {view: ...}
        var viewObject = matchProp("views", props);
        if (viewObject && viewObject.type === "ObjectExpression") {
            viewObject.properties.forEach(function(prop) {
                if (prop.value.type === "ObjectExpression") {
                    res.push(matchProp("controller", prop.value.properties));
                    res.push(matchProp("controllerProvider", prop.value.properties));
                    res.push(matchProp("templateProvider", prop.value.properties));
                    res.push.apply(res, matchResolve(prop.value.properties));
                }
            });
        }
    }
}
コード例 #4
0
function judgeInjectArraySuspect(node, ctx) {
    // /*@ngInject*/ var foo = function($scope) {} and
    // /*@ngInject*/ function foo($scope) {} and
    // /*@ngInject*/ foo.bar[0] = function($scope) {}

    // suspect must be inside of a block or at the top-level (i.e. inside of node.$parent.body[])
    if (!node.$parent || is.noneof(node.$parent.type, ["Program", "BlockStatement"])) {
        return;
    }

    let d0 = null;
    const nr0 = node.range[0];
    const nr1 = node.range[1];
    if (node.type === "VariableDeclaration" && node.declarations.length === 1 &&
        (d0 = node.declarations[0]).init && ctx.isFunctionExpressionWithArgs(d0.init)) {
        const isSemicolonTerminated = (ctx.src[nr1 - 1] === ";");
        addRemoveInjectArray(d0.init.params, nr0, isSemicolonTerminated ? nr1 : d0.init.range[1], d0.id.name);
    } else if (ctx.isFunctionDeclarationWithArgs(node)) {
        addRemoveInjectArray(node.params, nr0, nr1, node.id.name);
    } else if (node.type === "ExpressionStatement" && node.expression.type === "AssignmentExpression" &&
        ctx.isFunctionExpressionWithArgs(node.expression.right)) {
        const isSemicolonTerminated = (ctx.src[nr1 - 1] === ";");
        const name = ctx.srcForRange(node.expression.left.range);
        addRemoveInjectArray(node.expression.right.params, nr0, isSemicolonTerminated ? nr1 : node.expression.right.range[1], name);
    }

    function getIndent(pos) {
        const src = ctx.src;
        const lineStart = src.lastIndexOf("\n", pos - 1) + 1;
        let i = lineStart;
        for (; src[i] === " " || src[i] === "\t"; i++) {
        }
        return src.slice(lineStart, i);
    }

    function addRemoveInjectArray(params, posAtFunctionDeclaration, posAfterFunctionDeclaration, name) {
        // if an existing something.$inject = [..] exists then is will always be recycled when rebuilding

        const indent = getIndent(posAfterFunctionDeclaration);

        let foundSuspectInBody = false;
        let existingExpressionStatementWithArray = null;
        let troublesomeReturn = false;
        node.$parent.body.forEach(function(bnode) {
            if (bnode === node) {
                foundSuspectInBody = true;
            }

            if (hasInjectArray(bnode)) {
                if (existingExpressionStatementWithArray) {
                    throw fmt("conflicting inject arrays at line {0} and {1}",
                        posToLine(existingExpressionStatementWithArray.range[0], ctx.src),
                        posToLine(bnode.range[0], ctx.src));
                }
                existingExpressionStatementWithArray = bnode;
            }

            // there's a return statement before our function
            if (!foundSuspectInBody && bnode.type === "ReturnStatement") {
                troublesomeReturn = bnode;
            }
        });
        assert(foundSuspectInBody);

        if (troublesomeReturn && !existingExpressionStatementWithArray) {
            posAfterFunctionDeclaration = skipPrevNewline(troublesomeReturn.range[0]);
        }

        function hasInjectArray(node) {
            let lvalue;
            let assignment;
            return (node && node.type === "ExpressionStatement" && (assignment = node.expression).type === "AssignmentExpression" &&
                assignment.operator === "=" &&
                (lvalue = assignment.left).type === "MemberExpression" &&
                ((lvalue.computed === false && ctx.srcForRange(lvalue.object.range) === name && lvalue.property.name === "$inject") ||
                    (lvalue.computed === true && ctx.srcForRange(lvalue.object.range) === name && lvalue.property.type === "Literal" && lvalue.property.value === "$inject")));
        }

        function skipNewline(pos) {
            if (ctx.src[pos] === "\n") {
                return pos + 1;
            } else if (ctx.src.slice(pos, pos + 2) === "\r\n") {
                return pos + 2;
            }
            return pos;
        }

        function skipPrevNewline(pos) {
            let prevLF = ctx.src.lastIndexOf("\n", pos);
            if (prevLF === -1) {
                return pos;
            }
            if (prevLF >= 1 && ctx.src[prevLF] === "\r") {
                --prevLF;
            }

            if (/\S/g.test(ctx.src.slice(prevLF, pos - 1))) {
                return pos;
            }

            return prevLF;
        }

        const str = fmt("{0}{1}{2}.$inject = {3};", EOL, indent, name, ctx.stringify(params, ctx.quot));

        if (ctx.mode === "rebuild" && existingExpressionStatementWithArray) {
            ctx.fragments.push({
                start: existingExpressionStatementWithArray.range[0],
                end: existingExpressionStatementWithArray.range[1],
                str: str,
            });
        } else if (ctx.mode === "remove" && existingExpressionStatementWithArray) {
            ctx.fragments.push({
                start: skipPrevNewline(existingExpressionStatementWithArray.range[0]),
                end: existingExpressionStatementWithArray.range[1],
                str: "",
            });
        } else if (is.someof(ctx.mode, ["add", "rebuild"]) && !existingExpressionStatementWithArray) {
            ctx.fragments.push({
                start: posAfterFunctionDeclaration,
                end: posAfterFunctionDeclaration,
                str: str,
            });
        }
    }
}