ng-annotate
Advanced tools
Comparing version 0.14.1 to 0.15.0
// lut.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -5,0 +5,0 @@ "use strict"; |
// ng-annotate-main.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -17,3 +17,5 @@ "use strict"; | ||
var stringmap = require("stringmap"); | ||
var parser = null; // will be lazy-loaded to esprima or acorn | ||
var require_acorn_t0 = Date.now(); | ||
var parser = require("acorn").parse; | ||
var require_acorn_t1 = Date.now(); | ||
@@ -519,2 +521,3 @@ var chainedRouteProvider = 1; | ||
var quot = ctx.quot; | ||
var blocked = ctx.blocked; | ||
@@ -532,3 +535,3 @@ var suspects = makeUnique(ctx.suspects, 1); | ||
// create final suspects by jumping, following, uniq'ing | ||
// create final suspects by jumping, following, uniq'ing, blocking | ||
var finalSuspects = makeUnique(suspects.map(function(target) { | ||
@@ -542,2 +545,6 @@ var jumped = jumpOverIife(target); | ||
if (blocked.indexOf(jumpedAndFollowed) >= 0) { | ||
return null; | ||
} | ||
return jumpedAndFollowed; | ||
@@ -911,3 +918,21 @@ }).filter(Boolean), 2); | ||
function isAnnotatedArray(node) { | ||
return node.type === "ArrayExpression" && node.elements.length >= 1 && last(node.elements).type === "FunctionExpression"; | ||
if (node.type !== "ArrayExpression") { | ||
return false; | ||
} | ||
var elements = node.elements; | ||
// last should be a function expression | ||
if (elements.length === 0 || last(elements).type !== "FunctionExpression") { | ||
return false; | ||
} | ||
// all but last should be string literals | ||
for (var i = 0; i < elements.length - 1; i++) { | ||
var n = elements[i]; | ||
if (n.type !== "Literal" || !is.string(n.value)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
@@ -947,38 +972,13 @@ function isFunctionExpressionWithArgs(node) { | ||
stats.parser_require_t0 = Date.now(); | ||
if (!options.es6) { | ||
parser = require("esprima").parse; | ||
} else { | ||
parser = require("acorn").parse; | ||
} | ||
stats.parser_require_t1 = Date.now(); | ||
try { | ||
stats.parser_require_t0 = require_acorn_t0; | ||
stats.parser_require_t1 = require_acorn_t1; | ||
stats.parser_parse_t0 = Date.now(); | ||
if (!options.es6) { | ||
// esprima | ||
ast = parser(src, { | ||
range: true, | ||
comment: true, | ||
loc: true, | ||
}); | ||
// Fix Program node range (https://code.google.com/p/esprima/issues/detail?id=541) | ||
ast.range[0] = 0; | ||
// detach comments from ast | ||
comments = ast.comments; | ||
ast.comments = null; | ||
} else { | ||
// acorn | ||
ast = parser(src, { | ||
ecmaVersion: 6, | ||
locations: true, | ||
ranges: true, | ||
onComment: comments, | ||
}); | ||
} | ||
// acorn | ||
ast = parser(src, { | ||
ecmaVersion: 6, | ||
locations: true, | ||
ranges: true, | ||
onComment: comments, | ||
}); | ||
stats.parser_parse_t1 = Date.now(); | ||
@@ -1012,2 +1012,7 @@ } catch(e) { | ||
// blocked is an array of blocked suspects. Any target node | ||
// (final, i.e. IIFE-jumped, reference-followed and such) included | ||
// in blocked will be ignored by judgeSuspects | ||
var blocked = []; | ||
// Position information for all nodes in the AST, | ||
@@ -1033,2 +1038,3 @@ // used for sourcemap generation | ||
suspects: suspects, | ||
blocked: blocked, | ||
lut: lut, | ||
@@ -1062,5 +1068,3 @@ isFunctionExpressionWithArgs: isFunctionExpressionWithArgs, | ||
traverse(ast, {pre: function(node) { | ||
if (node.type === "CallExpression") { | ||
ngInject.inspectCallExpression(node, ctx); | ||
} | ||
ngInject.inspectNode(node, ctx); | ||
@@ -1067,0 +1071,0 @@ }, post: function(node) { |
// ng-annotate.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -12,3 +12,3 @@ "use strict"; | ||
var ngAnnotate = require("./ng-annotate-main"); | ||
var version = "0.14.1"; | ||
var version = "0.15.0"; | ||
var optimist = require("optimist") | ||
@@ -15,0 +15,0 @@ .usage("ng-annotate v" + version + "\n\nUsage: ng-annotate OPTIONS <file>\n\n" + |
@@ -1,4 +0,4 @@ | ||
// nginject-comments.js | ||
// nginject.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -11,11 +11,73 @@ "use strict"; | ||
inspectComments: inspectComments, | ||
inspectCallExpression: inspectCallExpression, | ||
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) { | ||
if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "ngInject" && node.arguments.length === 1) { | ||
addSuspect(node.arguments[0], ctx); | ||
var name = node.callee.name; | ||
if (node.callee.type === "Identifier" && (name === "ngInject" || name === "ngNoInject") && node.arguments.length === 1) { | ||
var block = (name === "ngNoInject"); | ||
addSuspect(node.arguments[0], ctx, block); | ||
} | ||
} | ||
var ngAnnotatePrologueDirectives = ["ngInject", "ngNoInject"]; | ||
function inspectFunction(node, ctx) { | ||
var str = matchPrologueDirectives(ngAnnotatePrologueDirectives, node); | ||
if (!str) { | ||
return; | ||
} | ||
var 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 | ||
var maybeArrayExpression = node.$parent; | ||
if (ctx.isAnnotatedArray(maybeArrayExpression)) { | ||
addSuspect(maybeArrayExpression, ctx, block); | ||
} | ||
} | ||
} | ||
function matchPrologueDirectives(prologueDirectives, node) { | ||
var body = node.body.body; | ||
var found = null; | ||
for (var i = 0; i < body.length; i++) { | ||
if (body[i].type !== "ExpressionStatement") { | ||
break; | ||
} | ||
var expr = body[i].expression; | ||
var 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) { | ||
@@ -25,4 +87,5 @@ var comments = ctx.comments; | ||
var comment = comments[i]; | ||
var pos = comment.value.indexOf("@ngInject"); | ||
if (pos === -1) { | ||
var yesPos = comment.value.indexOf("@ngInject"); | ||
var noPos = (yesPos === -1 ? comment.value.indexOf("@ngNoInject") : -1); | ||
if (yesPos === -1 && noPos === -1) { | ||
continue; | ||
@@ -36,7 +99,7 @@ } | ||
addSuspect(target, ctx); | ||
addSuspect(target, ctx, noPos >= 0); | ||
} | ||
} | ||
function addSuspect(target, ctx) { | ||
function addSuspect(target, ctx, block) { | ||
if (target.type === "ObjectExpression") { | ||
@@ -57,15 +120,24 @@ // /*@ngInject*/ {f1: function(a), .., {f2: function(b)}} | ||
target.value.$limitToMethodName = "*never*"; | ||
ctx.addModuleContextIndependentSuspect(target.value, ctx); | ||
addOrBlock(target.value, ctx); | ||
} else { | ||
// /*@ngInject*/ function(a) {} | ||
target.$limitToMethodName = "*never*"; | ||
ctx.addModuleContextIndependentSuspect(target, ctx); | ||
addOrBlock(target, ctx); | ||
} | ||
} | ||
function addObjectExpression(node, ctx) { | ||
nestedObjectValues(node).forEach(function(n) { | ||
n.$limitToMethodName = "*never*"; | ||
ctx.addModuleContextIndependentSuspect(n, 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) | ||
} | ||
} | ||
} | ||
@@ -72,0 +144,0 @@ |
// run-tests.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -102,6 +102,2 @@ "use strict"; | ||
console.log("testing adding annotations using single quotes"); | ||
var annotatedSingleQuotes = ngAnnotate(original, {add: true, single_quotes: true}).src; | ||
test(slurp("tests/with_annotations_single.js"), annotatedSingleQuotes, "with_annotations_single.js"); | ||
var rename = slurp("tests/rename.js"); | ||
@@ -108,0 +104,0 @@ |
// scope.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -5,0 +5,0 @@ "use strict"; |
// scopetools.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -5,0 +5,0 @@ "use strict"; |
@@ -700,3 +700,47 @@ "use strict"; | ||
// explicit annotations using "ngInject" Directive Prologue | ||
function Foo2($scope) { | ||
"ngInject"; | ||
} | ||
var foo3 = function($scope) { | ||
// comments are ok before the Directive Prologues | ||
// and there may be multiple Prologues | ||
"use strict"; "ngInject"; | ||
}; | ||
var foo4 = function($scope) { | ||
// not first in function => not Directive Prologues | ||
// so this function won't get annotated | ||
1; | ||
"ngInject"; | ||
}; | ||
// suppress false positives with /*@ngNoInject*/, ngNoInject() and "ngNoInject" | ||
myMod.controller("suppressed", /*@ngNoInject*/function($scope) { | ||
}); | ||
myMod.controller("suppressed", ngNoInject(function($scope) { | ||
})); | ||
myMod.controller("suppressed", function($scope) { | ||
"ngNoInject"; | ||
}); | ||
// works the same as ngInject i.e. reference-following, IIFE-jumping and so on | ||
/*@ngNoInject*/ | ||
myMod.controller("suppressed", SupFoo1); | ||
myMod.controller("suppressed", SupFoo2); | ||
myMod.controller("suppressed", SupFoo3); | ||
function SupFoo1($scope) { | ||
"ngNoInject"; | ||
} | ||
/*@ngNoInject*/ | ||
function SupFoo2($scope) { | ||
} | ||
var SupFoo3 = ngNoInject(function($scope) { | ||
"ngNoInject"; | ||
}); | ||
// snippets that shouldn't fool ng-annotate into generating false positives, | ||
@@ -765,2 +809,3 @@ // whether we're inside an angular module or not | ||
var myCtrl10 = (function() { | ||
"use strict"; | ||
// the return statement can appear anywhere on the functions topmost level, | ||
@@ -767,0 +812,0 @@ // including before the myCtrl function definition |
@@ -723,3 +723,48 @@ "use strict"; | ||
// explicit annotations using "ngInject" Directive Prologue | ||
function Foo2($scope) { | ||
"ngInject"; | ||
} | ||
Foo2.$inject = ["$scope"]; | ||
var foo3 = ["$scope", function($scope) { | ||
// comments are ok before the Directive Prologues | ||
// and there may be multiple Prologues | ||
"use strict"; "ngInject"; | ||
}]; | ||
var foo4 = function($scope) { | ||
// not first in function => not Directive Prologues | ||
// so this function won't get annotated | ||
1; | ||
"ngInject"; | ||
}; | ||
// suppress false positives with /*@ngNoInject*/, ngNoInject() and "ngNoInject" | ||
myMod.controller("suppressed", /*@ngNoInject*/function($scope) { | ||
}); | ||
myMod.controller("suppressed", ngNoInject(function($scope) { | ||
})); | ||
myMod.controller("suppressed", function($scope) { | ||
"ngNoInject"; | ||
}); | ||
// works the same as ngInject i.e. reference-following, IIFE-jumping and so on | ||
/*@ngNoInject*/ | ||
myMod.controller("suppressed", SupFoo1); | ||
myMod.controller("suppressed", SupFoo2); | ||
myMod.controller("suppressed", SupFoo3); | ||
function SupFoo1($scope) { | ||
"ngNoInject"; | ||
} | ||
/*@ngNoInject*/ | ||
function SupFoo2($scope) { | ||
} | ||
var SupFoo3 = ngNoInject(function($scope) { | ||
"ngNoInject"; | ||
}); | ||
// snippets that shouldn't fool ng-annotate into generating false positives, | ||
@@ -789,2 +834,3 @@ // whether we're inside an angular module or not | ||
var myCtrl10 = (function() { | ||
"use strict"; | ||
// the return statement can appear anywhere on the functions topmost level, | ||
@@ -791,0 +837,0 @@ // including before the myCtrl function definition |
@@ -0,1 +1,7 @@ | ||
## v0.15.0 2015-01-15 | ||
* "ngInject" directive prologue (usage like "use strict") | ||
* /*@ngNoInject*/, ngNoInject(..) and "ngNoInject" for suppressing false positives | ||
* Acorn is now the default and only parser | ||
* removed the experimental --es6 option and made it the default | ||
## v0.14.1 2014-12-04 | ||
@@ -2,0 +8,0 @@ * bugfix /* @ngInject */ not working as expected in case of other matches |
// lut.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -5,0 +5,0 @@ "use strict"; |
// ng-annotate-main.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -17,3 +17,5 @@ "use strict"; | ||
const stringmap = require("stringmap"); | ||
let parser = null; // will be lazy-loaded to esprima or acorn | ||
const require_acorn_t0 = Date.now(); | ||
const parser = require("acorn").parse; | ||
const require_acorn_t1 = Date.now(); | ||
@@ -519,2 +521,3 @@ const chainedRouteProvider = 1; | ||
const quot = ctx.quot; | ||
const blocked = ctx.blocked; | ||
@@ -532,3 +535,3 @@ const suspects = makeUnique(ctx.suspects, 1); | ||
// create final suspects by jumping, following, uniq'ing | ||
// create final suspects by jumping, following, uniq'ing, blocking | ||
const finalSuspects = makeUnique(suspects.map(function(target) { | ||
@@ -542,2 +545,6 @@ const jumped = jumpOverIife(target); | ||
if (blocked.indexOf(jumpedAndFollowed) >= 0) { | ||
return null; | ||
} | ||
return jumpedAndFollowed; | ||
@@ -911,3 +918,21 @@ }).filter(Boolean), 2); | ||
function isAnnotatedArray(node) { | ||
return node.type === "ArrayExpression" && node.elements.length >= 1 && last(node.elements).type === "FunctionExpression"; | ||
if (node.type !== "ArrayExpression") { | ||
return false; | ||
} | ||
const elements = node.elements; | ||
// last should be a function expression | ||
if (elements.length === 0 || last(elements).type !== "FunctionExpression") { | ||
return false; | ||
} | ||
// all but last should be string literals | ||
for (let i = 0; i < elements.length - 1; i++) { | ||
const n = elements[i]; | ||
if (n.type !== "Literal" || !is.string(n.value)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
@@ -947,38 +972,13 @@ function isFunctionExpressionWithArgs(node) { | ||
stats.parser_require_t0 = Date.now(); | ||
if (!options.es6) { | ||
parser = require("esprima").parse; | ||
} else { | ||
parser = require("acorn").parse; | ||
} | ||
stats.parser_require_t1 = Date.now(); | ||
try { | ||
stats.parser_require_t0 = require_acorn_t0; | ||
stats.parser_require_t1 = require_acorn_t1; | ||
stats.parser_parse_t0 = Date.now(); | ||
if (!options.es6) { | ||
// esprima | ||
ast = parser(src, { | ||
range: true, | ||
comment: true, | ||
loc: true, | ||
}); | ||
// Fix Program node range (https://code.google.com/p/esprima/issues/detail?id=541) | ||
ast.range[0] = 0; | ||
// detach comments from ast | ||
comments = ast.comments; | ||
ast.comments = null; | ||
} else { | ||
// acorn | ||
ast = parser(src, { | ||
ecmaVersion: 6, | ||
locations: true, | ||
ranges: true, | ||
onComment: comments, | ||
}); | ||
} | ||
// acorn | ||
ast = parser(src, { | ||
ecmaVersion: 6, | ||
locations: true, | ||
ranges: true, | ||
onComment: comments, | ||
}); | ||
stats.parser_parse_t1 = Date.now(); | ||
@@ -1012,2 +1012,7 @@ } catch(e) { | ||
// blocked is an array of blocked suspects. Any target node | ||
// (final, i.e. IIFE-jumped, reference-followed and such) included | ||
// in blocked will be ignored by judgeSuspects | ||
const blocked = []; | ||
// Position information for all nodes in the AST, | ||
@@ -1033,2 +1038,3 @@ // used for sourcemap generation | ||
suspects: suspects, | ||
blocked: blocked, | ||
lut: lut, | ||
@@ -1062,5 +1068,3 @@ isFunctionExpressionWithArgs: isFunctionExpressionWithArgs, | ||
traverse(ast, {pre: function(node) { | ||
if (node.type === "CallExpression") { | ||
ngInject.inspectCallExpression(node, ctx); | ||
} | ||
ngInject.inspectNode(node, ctx); | ||
@@ -1067,0 +1071,0 @@ }, post: function(node) { |
// ng-annotate.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -5,0 +5,0 @@ "use strict"; |
@@ -1,4 +0,4 @@ | ||
// nginject-comments.js | ||
// nginject.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -11,11 +11,73 @@ "use strict"; | ||
inspectComments: inspectComments, | ||
inspectCallExpression: inspectCallExpression, | ||
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) { | ||
if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "ngInject" && node.arguments.length === 1) { | ||
addSuspect(node.arguments[0], 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) { | ||
@@ -25,4 +87,5 @@ const comments = ctx.comments; | ||
const comment = comments[i]; | ||
const pos = comment.value.indexOf("@ngInject"); | ||
if (pos === -1) { | ||
const yesPos = comment.value.indexOf("@ngInject"); | ||
const noPos = (yesPos === -1 ? comment.value.indexOf("@ngNoInject") : -1); | ||
if (yesPos === -1 && noPos === -1) { | ||
continue; | ||
@@ -36,7 +99,7 @@ } | ||
addSuspect(target, ctx); | ||
addSuspect(target, ctx, noPos >= 0); | ||
} | ||
} | ||
function addSuspect(target, ctx) { | ||
function addSuspect(target, ctx, block) { | ||
if (target.type === "ObjectExpression") { | ||
@@ -57,15 +120,24 @@ // /*@ngInject*/ {f1: function(a), .., {f2: function(b)}} | ||
target.value.$limitToMethodName = "*never*"; | ||
ctx.addModuleContextIndependentSuspect(target.value, ctx); | ||
addOrBlock(target.value, ctx); | ||
} else { | ||
// /*@ngInject*/ function(a) {} | ||
target.$limitToMethodName = "*never*"; | ||
ctx.addModuleContextIndependentSuspect(target, ctx); | ||
addOrBlock(target, ctx); | ||
} | ||
} | ||
function addObjectExpression(node, ctx) { | ||
nestedObjectValues(node).forEach(function(n) { | ||
n.$limitToMethodName = "*never*"; | ||
ctx.addModuleContextIndependentSuspect(n, 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) | ||
} | ||
} | ||
} | ||
@@ -72,0 +144,0 @@ |
{ | ||
"name": "ng-annotate", | ||
"version": "0.14.1", | ||
"version": "0.15.0", | ||
"description": "add, remove and rebuild angularjs dependency injection annotations", | ||
@@ -11,6 +11,5 @@ "main": "build/es5/ng-annotate-main.js", | ||
"dependencies": { | ||
"acorn": "~0.9.0", | ||
"acorn": "~0.11.0", | ||
"alter": "~0.2.0", | ||
"convert-source-map": "~0.4.0", | ||
"esprima": "~1.2.0", | ||
"convert-source-map": "~0.4.1", | ||
"optimist": "~0.6.1", | ||
@@ -20,3 +19,3 @@ "ordered-ast-traverse": "~0.1.1", | ||
"simple-is": "~0.2.0", | ||
"source-map": "~0.1.37", | ||
"source-map": "~0.1.43", | ||
"stable": "~0.1.5", | ||
@@ -29,4 +28,4 @@ "stringmap": "~0.2.2", | ||
"coffee-script": "~1.8.0", | ||
"defs": "~0.6.2", | ||
"diff": "~1.0.8", | ||
"defs": "~1.1.0", | ||
"diff": "~1.2.1", | ||
"find-line-column": "~0.5.2" | ||
@@ -43,3 +42,2 @@ }, | ||
"annotations", | ||
"non-intrusive", | ||
"transformation" | ||
@@ -46,0 +44,0 @@ ], |
// pos-to-linecolumn.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2014-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -5,0 +5,0 @@ "use strict"; |
# ng-annotate | ||
ng-annotate adds and removes AngularJS dependency injection annotations. | ||
It is non-intrusive so your source code stays exactly the same otherwise. | ||
No lost comments or moved lines. | ||
No lost comments or moved lines. Annotations are useful because with them | ||
you're able to minify your source code using your favorite JS minifier. | ||
Without annotations: | ||
You write your code without annotations, like this: | ||
@@ -13,3 +14,4 @@ ```js | ||
With annotations: | ||
You then run ng-annotate as a build-step to produce this intermediary, | ||
annotated, result (later sent to the minifier): | ||
@@ -21,21 +23,13 @@ ```js | ||
Annotations are useful because with them you're able to minify your source code using your | ||
favorite JS minifier. | ||
You can also use ng-annotate to rebuild or remove existing annotations. | ||
Rebuilding is useful if you like to check-in the annotated version of your | ||
source code. When refactoring, just change parameter names once and let | ||
ng-annotate rebuild the annotations. Removing is useful if you want to | ||
de-annotate an existing codebase that came with checked-in annotations | ||
*ng-annotate works by using static analysis to identify common code patterns. | ||
There are patterns it does not and never will understand and for those you | ||
can use an explicit `ngInject` annotation instead, see section further down. | ||
## How does ng-annotate compare to ngmin? | ||
If you are currently using ngmin then this is probably your first question. In short: | ||
ng-annotate is much faster, finds more declarations to annotate (including ui-router), | ||
treats your source code better, is actively maintained and has a bunch of extra features | ||
on top of that. A much more elaborated answer can be found in | ||
["The future of ngmin and ng-annotate"](https://github.com/btford/ngmin/issues/93). | ||
*Migrating from ngmin*: | ||
`ng-annotate -a -` is similar to `ngmin` (use stdin and | ||
stdout). `ng-annotate -a in.js -o out.js` is similar to `ngmin in.js out.js`. Grunt users | ||
can migrate easily by installing | ||
[grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) and replacing `ngmin` | ||
with `ngAnnotate` in their Gruntfile. Scroll down for information about other tools. | ||
## Installation and usage | ||
@@ -58,4 +52,2 @@ | ||
*experimental* Use the `--es6` option for ES6 support via the Acorn parser. | ||
Use the `--sourcemap` option to generate an inline sourcemap. | ||
@@ -84,9 +76,6 @@ | ||
Do that in your ng-annotate processed builds and AngularJS will let you know if there are | ||
any missing dependency injection annotations. This is an upcoming feature in AngularJS 1.3 | ||
([docs](https://docs.angularjs.org/api/ng/directive/ngApp)). | ||
any missing dependency injection annotations. [ng-strict-di](https://docs.angularjs.org/api/ng/directive/ngApp) | ||
is available in AngularJS 1.3 or later. | ||
A future version of ng-annotate may get support for adding the `ng-strict-di` attribute | ||
automatically so you don't have to think about it. | ||
## Tools support | ||
@@ -101,2 +90,3 @@ * [Grunt](http://gruntjs.com/): [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) by [Michał Gołębiowski](https://github.com/mzgol) | ||
* [Webpack](http://webpack.github.io/): [ng-annotate-webpack-plugin](https://www.npmjs.org/package/ng-annotate-webpack-plugin) by [Chris Liechty](https://github.com/cliechty) | ||
* [Middleman](http://middlemanapp.com/): [middleman-ngannotate](http://rubygems.org/gems/middleman-ngannotate) by [Michael Siebert](https://github.com/siebertm) | ||
* Something missing? Contributions welcome - create plugin and submit a README pull request! | ||
@@ -109,11 +99,2 @@ | ||
## Why? | ||
* Keep your code base clutter free from annotations but add them in your build step | ||
prior to minimizing | ||
* De-clutter an existing code base by removing annotations, non-intrusively | ||
* If you must store annotations in the repo (for any reason) then checkout, | ||
remove them, code and refactor without annotations, add them back and commit. | ||
Alternatively checkout, code and refactor (ignoring annotations), rebuild them and commit. | ||
## Declaration forms | ||
@@ -186,3 +167,2 @@ ng-annotate understands the two common declaration forms: | ||
## Reference-following | ||
ng-annotate follows references. This works iff the referenced declaration is | ||
@@ -204,33 +184,48 @@ a) a function declaration or | ||
## Explicit annotations | ||
You can prepend a function expression with `/* @ngInject */` to explicitly state that this | ||
function should get annotated. ng-annotate will leave the comment intact and will thus still | ||
be able to also remove or rewrite such annotations. Alternatively, you can wrap an expression | ||
inside an `ngInject(..)` function call. Use `/* @ngInject */` or `ngInject(..)` as an occasional | ||
workaround when ng-annotate doesn't support your code style but feel free to open an issue | ||
also. | ||
## Explicit annotations with ngInject | ||
You can prepend a function with `/*@ngInject*/` to explicitly state that the function | ||
should get annotated. ng-annotate will leave the comment intact and will thus still | ||
be able to also remove or rewrite such annotations. | ||
You can also wrap an expression inside an `ngInject(..)` function call. If you use this | ||
syntax then add `function ngInject(v) { return v }` somewhere in your codebase, or process | ||
away the `ngInject` function call in your build step. | ||
You can also add the `"ngInject"` directive prologue at the beginning of a function, | ||
similar to how `"use strict"` is used, to state that the surrounding function should get | ||
annotated. | ||
Use `ngInject` to support your code style when it's not in a form ng-annotate understands | ||
natively. Remember that the intention of ng-annotate is to reduce stuttering for you, | ||
and `ngInject` does this just as well. You don't need to keep two lists in sync. Use it! | ||
`ngInject` may be particularly useful if you use a compile-to-JS language that doesn't | ||
preserve comments. | ||
### Suppressing false positives with ngNoInject | ||
The `/*@ngInject*/`, `ngInject(..)` and `"ngInject"` siblings have three cousins that | ||
are used for the opposite purpose, suppressing an annotation that ng-annotate added | ||
incorrectly (a "false positive"). They are called `/*@ngNoInject*/`, `ngNoInject(..)` | ||
and `"ngNoInject"` and do exactly what you think they do. | ||
### ngInject examples | ||
Here follows some ngInject examples using the `/*@ngInject*/` syntax. Most examples | ||
works fine using the `ngInject(..)` or `"ngInject"` syntax as well. | ||
```js | ||
x = /* @ngInject */ function($scope) {}; | ||
x = /*@ngInject*/ function($scope) {}; | ||
obj = {controller: /*@ngInject*/ function($scope) {}}; | ||
obj.bar = /*@ngInject*/ function($scope) {}; | ||
function ngInject(f) { return f } // define this once in your program | ||
x = ngInject(function($scope) {}); | ||
obj = {controller: ngInject(function($scope) {})}; | ||
obj.bar = ngInject(function($scope) {}); | ||
=> | ||
x = /* @ngInject */ ["$scope", function($scope) {}]; | ||
x = /*@ngInject*/ ["$scope", function($scope) {}]; | ||
obj = {controller: /*@ngInject*/ ["$scope", function($scope) {}]}; | ||
obj.bar = /*@ngInject*/ ["$scope", function($scope) {}]; | ||
function ngInject(f) { return f } // define this once in your program | ||
x = ngInject(["$scope", function($scope) {}]); | ||
obj = {controller: ngInject(["$scope", function($scope) {}])}; | ||
obj.bar = ngInject(["$scope", function($scope) {}]); | ||
``` | ||
Prepended to an object literal, `/* @ngInject */` will annotate all of its contained | ||
Prepended to an object literal, `/*@ngInject*/` will annotate all of its contained | ||
function expressions, recursively: | ||
@@ -244,7 +239,2 @@ | ||
obj = ngInject({ | ||
controller: function($scope) {}, | ||
resolve: { data: function(Service) {} }, | ||
}); | ||
=> | ||
@@ -256,7 +246,2 @@ | ||
}; | ||
obj = ngInject({ | ||
controller: ["$scope", function($scope) {}], | ||
resolve: { data: ["Service", function(Service) {}] }, | ||
}); | ||
``` | ||
@@ -266,3 +251,3 @@ | ||
function expression or to an assignment where the rvalue is a function expression, | ||
`/* @ngInject */` will attach an `$inject` array to the function: | ||
`/*@ngInject*/` will attach an `$inject` array to the function: | ||
@@ -294,7 +279,3 @@ ```js | ||
## Issues and compatibility | ||
If ng-annotate does not handle a construct you're using, if there's a bug or if you have a feature | ||
request then please [file an issue](https://github.com/olov/ng-annotate/issues?state=open). | ||
## Build and test | ||
@@ -313,8 +294,17 @@ ng-annotate is written in ES6 constlet style and uses [defs.js](https://github.com/olov/defs) | ||
## Performance | ||
ng-annotate is designed to be very fast (in general limited by parse speed). | ||
It traverses the AST exactly once and transforms it without the need for an AST -> source | ||
decompilation step. | ||
## How does ng-annotate compare to ngmin? | ||
ngmin has been deprecated in favor of ng-annotate. In short: | ||
ng-annotate is much faster, finds more declarations to annotate (including ui-router), | ||
treats your source code better, is actively maintained and has a bunch of extra features | ||
on top of that. A much more elaborated answer can be found in | ||
["The future of ngmin and ng-annotate"](https://github.com/btford/ngmin/issues/93). | ||
*Migrating from ngmin*: | ||
`ng-annotate -a -` is similar to `ngmin` (use stdin and | ||
stdout). `ng-annotate -a in.js -o out.js` is similar to `ngmin in.js out.js`. Grunt users | ||
can migrate easily by installing | ||
[grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) and replacing `ngmin` | ||
with `ngAnnotate` in their Gruntfile. Scroll down for information about other tools. | ||
## Library (API) | ||
@@ -321,0 +311,0 @@ ng-annotate can be used as a library. See [ng-annotate.js](ng-annotate.js) for further info about |
// run-tests.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -102,6 +102,2 @@ "use strict"; | ||
console.log("testing adding annotations using single quotes"); | ||
const annotatedSingleQuotes = ngAnnotate(original, {add: true, single_quotes: true}).src; | ||
test(slurp("tests/with_annotations_single.js"), annotatedSingleQuotes, "with_annotations_single.js"); | ||
const rename = slurp("tests/rename.js"); | ||
@@ -108,0 +104,0 @@ |
// scope.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -5,0 +5,0 @@ "use strict"; |
// scopetools.js | ||
// MIT licensed, see LICENSE file | ||
// Copyright (c) 2013-2014 Olov Lassus <olov.lassus@gmail.com> | ||
// Copyright (c) 2013-2015 Olov Lassus <olov.lassus@gmail.com> | ||
@@ -5,0 +5,0 @@ "use strict"; |
@@ -700,3 +700,47 @@ "use strict"; | ||
// explicit annotations using "ngInject" Directive Prologue | ||
function Foo2($scope) { | ||
"ngInject"; | ||
} | ||
var foo3 = function($scope) { | ||
// comments are ok before the Directive Prologues | ||
// and there may be multiple Prologues | ||
"use strict"; "ngInject"; | ||
}; | ||
var foo4 = function($scope) { | ||
// not first in function => not Directive Prologues | ||
// so this function won't get annotated | ||
1; | ||
"ngInject"; | ||
}; | ||
// suppress false positives with /*@ngNoInject*/, ngNoInject() and "ngNoInject" | ||
myMod.controller("suppressed", /*@ngNoInject*/function($scope) { | ||
}); | ||
myMod.controller("suppressed", ngNoInject(function($scope) { | ||
})); | ||
myMod.controller("suppressed", function($scope) { | ||
"ngNoInject"; | ||
}); | ||
// works the same as ngInject i.e. reference-following, IIFE-jumping and so on | ||
/*@ngNoInject*/ | ||
myMod.controller("suppressed", SupFoo1); | ||
myMod.controller("suppressed", SupFoo2); | ||
myMod.controller("suppressed", SupFoo3); | ||
function SupFoo1($scope) { | ||
"ngNoInject"; | ||
} | ||
/*@ngNoInject*/ | ||
function SupFoo2($scope) { | ||
} | ||
var SupFoo3 = ngNoInject(function($scope) { | ||
"ngNoInject"; | ||
}); | ||
// snippets that shouldn't fool ng-annotate into generating false positives, | ||
@@ -765,2 +809,3 @@ // whether we're inside an angular module or not | ||
var myCtrl10 = (function() { | ||
"use strict"; | ||
// the return statement can appear anywhere on the functions topmost level, | ||
@@ -767,0 +812,0 @@ // including before the myCtrl function definition |
@@ -723,3 +723,48 @@ "use strict"; | ||
// explicit annotations using "ngInject" Directive Prologue | ||
function Foo2($scope) { | ||
"ngInject"; | ||
} | ||
Foo2.$inject = ["$scope"]; | ||
var foo3 = ["$scope", function($scope) { | ||
// comments are ok before the Directive Prologues | ||
// and there may be multiple Prologues | ||
"use strict"; "ngInject"; | ||
}]; | ||
var foo4 = function($scope) { | ||
// not first in function => not Directive Prologues | ||
// so this function won't get annotated | ||
1; | ||
"ngInject"; | ||
}; | ||
// suppress false positives with /*@ngNoInject*/, ngNoInject() and "ngNoInject" | ||
myMod.controller("suppressed", /*@ngNoInject*/function($scope) { | ||
}); | ||
myMod.controller("suppressed", ngNoInject(function($scope) { | ||
})); | ||
myMod.controller("suppressed", function($scope) { | ||
"ngNoInject"; | ||
}); | ||
// works the same as ngInject i.e. reference-following, IIFE-jumping and so on | ||
/*@ngNoInject*/ | ||
myMod.controller("suppressed", SupFoo1); | ||
myMod.controller("suppressed", SupFoo2); | ||
myMod.controller("suppressed", SupFoo3); | ||
function SupFoo1($scope) { | ||
"ngNoInject"; | ||
} | ||
/*@ngNoInject*/ | ||
function SupFoo2($scope) { | ||
} | ||
var SupFoo3 = ngNoInject(function($scope) { | ||
"ngNoInject"; | ||
}); | ||
// snippets that shouldn't fool ng-annotate into generating false positives, | ||
@@ -789,2 +834,3 @@ // whether we're inside an angular module or not | ||
var myCtrl10 = (function() { | ||
"use strict"; | ||
// the return statement can appear anywhere on the functions topmost level, | ||
@@ -791,0 +837,0 @@ // including before the myCtrl function definition |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
12
359133
65
9600
315
+ Addedacorn@0.11.0(transitive)
- Removedesprima@~1.2.0
- Removedacorn@0.9.0(transitive)
- Removedesprima@1.2.5(transitive)
Updatedacorn@~0.11.0
Updatedconvert-source-map@~0.4.1
Updatedsource-map@~0.1.43