forked from olov/ng-annotate
-
Notifications
You must be signed in to change notification settings - Fork 0
/
nginject.js
154 lines (129 loc) · 5.12 KB
/
nginject.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// nginject.js
// MIT licensed, see LICENSE file
// Copyright (c) 2013-2015 Olov Lassus <[email protected]>
"use strict";
const is = require("simple-is");
module.exports = {
inspectComments: inspectComments,
inspectNode: inspectNode,
};
function inspectNode(node, ctx) {
if (node.type === "CallExpression") {
inspectCallExpression(node, ctx);
} else if (node.type === "FunctionExpression" || node.type === "FunctionDeclaration") {
inspectFunction(node, ctx);
}
}
function inspectCallExpression(node, ctx) {
const name = node.callee.name;
if (node.callee.type === "Identifier" && (name === "ngInject" || name === "ngNoInject") && node.arguments.length === 1) {
const block = (name === "ngNoInject");
addSuspect(node.arguments[0], ctx, block);
}
}
const ngAnnotatePrologueDirectives = ["ngInject", "ngNoInject"];
function inspectFunction(node, ctx) {
const str = matchPrologueDirectives(ngAnnotatePrologueDirectives, node);
if (!str) {
return;
}
const block = (str === "ngNoInject");
// which node that is the correct suspect in the case of a "ngInject" prologue directive varies
// between adding and removing annotations. when adding, the function (declaration or expression)
// is always the suspect. when removing, the function declaration is the suspect but in the case
// of a function expression, its parent is (because it may be an annotated array). when rebuilding,
// both may be suspects.
// add function node as a suspect, unconditionally (false suspect won't cause a problem here)
addSuspect(node, ctx, block);
if (ctx.mode !== "add") {
// remove or rebuild
// isAnnotatedArray check is there as an extra false-positives safety net
const maybeArrayExpression = node.$parent;
if (ctx.isAnnotatedArray(maybeArrayExpression)) {
addSuspect(maybeArrayExpression, ctx, block);
}
}
}
function matchPrologueDirectives(prologueDirectives, node) {
const body = node.body.body;
let found = null;
for (let i = 0; i < body.length; i++) {
if (body[i].type !== "ExpressionStatement") {
break;
}
const expr = body[i].expression;
const isStringLiteral = (expr.type === "Literal" && typeof expr.value === "string");
if (!isStringLiteral) {
break;
}
if (prologueDirectives.indexOf(expr.value) >= 0) {
found = expr.value;
break;
}
}
return found;
}
function inspectComments(ctx) {
const comments = ctx.comments;
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
const yesPos = comment.value.indexOf("@ngInject");
const noPos = (yesPos === -1 ? comment.value.indexOf("@ngNoInject") : -1);
if (yesPos === -1 && noPos === -1) {
continue;
}
const target = ctx.lut.findNodeFromPos(comment.range[1]);
if (!target) {
continue;
}
addSuspect(target, ctx, noPos >= 0);
}
}
function addSuspect(target, ctx, block) {
if (target.type === "ObjectExpression") {
// /*@ngInject*/ {f1: function(a), .., {f2: function(b)}}
addObjectExpression(target, ctx);
} else if (target.type === "AssignmentExpression" && target.right.type === "ObjectExpression") {
// /*@ngInject*/ f(x.y = {f1: function(a), .., {f2: function(b)}})
addObjectExpression(target.right, ctx);
} else if (target.type === "ExpressionStatement" && target.expression.type === "AssignmentExpression" && target.expression.right.type === "ObjectExpression") {
// /*@ngInject*/ x.y = {f1: function(a), .., {f2: function(b)}}
addObjectExpression(target.expression.right, ctx);
} else if (target.type === "VariableDeclaration" && target.declarations.length === 1 && target.declarations[0].init && target.declarations[0].init.type === "ObjectExpression") {
// /*@ngInject*/ var x = {f1: function(a), .., {f2: function(b)}}
addObjectExpression(target.declarations[0].init, ctx);
} else if (target.type === "Property") {
// {/*@ngInject*/ justthisone: function(a), ..}
target.value.$limitToMethodName = "*never*";
addOrBlock(target.value, ctx);
} else {
// /*@ngInject*/ function(a) {}
target.$limitToMethodName = "*never*";
addOrBlock(target, ctx);
}
function addObjectExpression(node, ctx) {
nestedObjectValues(node).forEach(function(n) {
n.$limitToMethodName = "*never*";
addOrBlock(n, ctx);
});
}
function addOrBlock(node, ctx) {
if (block) {
ctx.blocked.push(node);
} else {
ctx.addModuleContextIndependentSuspect(node, ctx)
}
}
}
function nestedObjectValues(node, res) {
res = res || [];
node.properties.forEach(function(prop) {
const v = prop.value;
if (is.someof(v.type, ["FunctionExpression", "ArrayExpression"])) {
res.push(v);
} else if (v.type === "ObjectExpression") {
nestedObjectValues(v, res);
}
});
return res;
}