UNPKG

babel-plugin-angularjs-annotate

Version:

Babel plugin to add angularjs dependency injection annotations

1,530 lines (1,297 loc) 381 kB
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 'use strict'; var ngAnnotate = require('./ng-annotate-main'); var ngInject = require('./nginject'); var is = require('simple-is'); var match = ngAnnotate.match; var addModuleContextDependentSuspect = ngAnnotate.addModuleContextDependentSuspect; var addModuleContextIndependentSuspect = ngAnnotate.addModuleContextIndependentSuspect; var judgeSuspects = ngAnnotate.judgeSuspects; var matchDirectiveReturnObject = ngAnnotate.matchDirectiveReturnObject; var matchProviderGet = ngAnnotate.matchProviderGet; module.exports = function () { var options = {}; var re = options.regexp ? new RegExp(options.regexp) : /^[a-zA-Z0-9_\$\.\s]+$/; // suspects is built up with suspect nodes by match. // A suspect node will get annotations added / removed if it // fulfills the arrayexpression or functionexpression look, // and if it is in the correct context (inside an angular // module definition) var suspects = []; // 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 = []; var ctx = { re: re, suspects: suspects, blocked: blocked, addModuleContextDependentSuspect: addModuleContextDependentSuspect, addModuleContextIndependentSuspect: addModuleContextIndependentSuspect }; var addTargets = function addTargets(targets) { if (!targets) { return; } if (!is.array(targets)) { targets = [targets]; } for (var i = 0; i < targets.length; i++) { addModuleContextDependentSuspect(targets[i], ctx); } }; return { visitor: { AssignmentExpression: { enter: function enter(path) { ngInject.inspectAssignment(path, ctx); }, exit: function exit(path, state) { if (!state.opts.explicitOnly) { var targets = matchProviderGet(path); addTargets(targets); } } }, VariableDeclarator: { enter: function enter(path) { ngInject.inspectDeclarator(path, ctx); } }, ClassDeclaration: { enter: function enter(path) { ngInject.inspectClassDeclaration(path, ctx); } }, ClassMethod: { enter: function enter(path) { ngInject.inspectClassMethod(path, ctx); } }, ObjectExpression: { enter: function enter(path) { ngInject.inspectObjectExpression(path, ctx); }, exit: function exit(path, state) { if (!state.opts.explicitOnly) { var targets = matchProviderGet(path); addTargets(targets); } } }, ReturnStatement: { exit: function exit(path, state) { if (!state.opts.explicitOnly) { var targets = matchDirectiveReturnObject(path); addTargets(targets); } } }, FunctionExpression: { enter: function enter(path) { ngInject.inspectFunction(path, ctx); } }, ArrowFunctionExpression: { enter: function enter(path) { ngInject.inspectFunction(path, ctx); }, exit: function exit(path, state) { if (!state.opts.explicitOnly) { var targets = matchDirectiveReturnObject(path); addTargets(targets); } } }, FunctionDeclaration: { enter: function enter(path) { ngInject.inspectFunction(path, ctx); } }, ObjectMethod: { enter: function enter(path) { ngInject.inspectFunction(path, ctx); } }, CallExpression: { enter: function enter(path) { ngInject.inspectCallExpression(path, ctx); }, exit: function exit(path, state) { var targets = match(path, ctx, state.opts.explicitOnly); addTargets(targets); } }, ExportDeclaration: { enter: function enter(path) { ngInject.inspectExportDeclaration(path, ctx); } }, Program: { enter: function enter(path, file) { file.opts.explicitOnly = file.opts.explicitOnly || false; ctx.suspects = []; ctx.blocked = []; ctx.fragments = []; ctx.srcForRange = function (node) { return file.file.code.slice(node.start, node.end); }; }, exit: function exit() { judgeSuspects(ctx); } } } }; }; },{"./ng-annotate-main":3,"./nginject":4,"simple-is":249}],2:[function(require,module,exports){ 'use strict'; // super-simple in-browser REPL for testing this thing // to build: // // npm install -g browserify // browserify repl.js -o repl-browser.js var plugin = require('../babel-ng-annotate'); window.transform = function (code, es2015) { return Babel.transform(code, { presets: es2015 ? [window.babelPresetEnv.default] : [], plugins: [plugin] }); }; },{"../babel-ng-annotate":1}],3:[function(require,module,exports){ // ng-annotate-main.js // MIT licensed, see LICENSE file // Copyright (c) 2013-2016 Olov Lassus <olov.lassus@gmail.com> "use strict"; var is = require("simple-is"); var assert = require("assert"); var ngInject = require("./nginject"); var scopeTools = require("./scopetools"); // const optionalAngularDashboardFramework = require("./optionals/angular-dashboard-framework"); var t = require('babel-types'); var chainedRouteProvider = 1; var chainedUrlRouterProvider = 2; var chainedStateProvider = 3; var chainedRegular = 4; function match(path, ctx, explicitOnly) { var node = path.node; var isMethodCall = t.isCallExpression(node) && t.isMemberExpression(node.callee) && node.callee.computed === false; if (isMethodCall && ngInject.inspectComment(path, ctx)) { return false; } if (explicitOnly) { return false; } // matchInjectorInvoke must happen before matchRegular // to prevent false positive ($injector.invoke() outside module) // matchProvide must happen before matchRegular // to prevent regular from matching it as a short-form var matchMethodCalls = isMethodCall && (matchInjectorInvoke(path) || matchProvide(path, ctx) || matchRegular(path, ctx) || matchNgRoute(path) || matchMaterialShowModalOpen(path) || matchNgUi(path) || matchHttpProvider(path) || matchControllerProvider(path)); return matchMethodCalls; } function matchMaterialShowModalOpen(path) { // $mdDialog.show({.. controller: fn, resolve: {f: function($scope) {}, ..}}); // $mdToast.show({.. controller: fn, resolve: {f: function($scope) {}, ..}}); // $mdBottomSheet.show({.. controller: fn, resolve: {f: function($scope) {}, ..}}); // $modal.open({.. controller: fn, resolve: {f: function($scope) {}, ..}}); // we already know that node is a (non-computed) method call var node = path.node; var callee = node.callee; var obj = callee.object; // identifier or expression var method = callee.property; // identifier var args = node.arguments; if (t.isIdentifier(obj) && (is.someof(obj.name, ["$modal", "$uibModal"]) && method.name === "open" || is.someof(obj.name, ["$mdDialog", "$mdToast", "$mdBottomSheet"]) && method.name === "show") && args.length === 1 && t.isObjectExpression(args[0])) { var _args = path.get("arguments"); var props = _args[0].get("properties"); var res = [matchProp("controller", props)]; res.push.apply(res, matchResolve(props)); return res.filter(Boolean); } return false; } function matchDirectiveReturnObject(path) { var node = path.node; // only matches inside directives // return { .. controller: function($scope, $timeout), ...} return limit("directive", t.isReturnStatement(node) && node.argument && t.isObjectExpression(node.argument) && matchProp("controller", path.get && path.get("argument.properties") || node.argument.properties) || t.isArrowFunctionExpression(node) && node.body && t.isObjectExpression(node.body) && matchProp("controller", path.get && path.get("body.properties") || node.body.properties)); } function limit(name, path) { var node = path && path.node || path; if (node && !path.$limitToMethodName) { path.$limitToMethodName = name; // node.$limitToMethodName = name; } return path; } function matchProviderGet(path) { // only matches inside providers // (this|self|that).$get = function($scope, $timeout) // { ... $get: function($scope, $timeout), ...} var node = path.node; var memberExpr = void 0; var self = void 0; var yes = limit("provider", t.isAssignmentExpression(node) && t.isMemberExpression(memberExpr = node.left) && memberExpr.property.name === "$get" && (t.isThisExpression(self = memberExpr.object) || t.isIdentifier(self) && is.someof(self.name, ["self", "that"])) && path.get("right") || t.isObjectExpression(node) && matchProp("$get", path.get("properties"))); return yes; } function matchNgRoute(path) { // $routeProvider.when("path", { // ... // controller: function($scope) {}, // resolve: {f: function($scope) {}, ..} // }) // we already know that node is a (non-computed) method call var node = path.node; var callee = node.callee; var obj = callee.object; // identifier or expression if (!(obj.$chained === chainedRouteProvider || t.isIdentifier(obj) && obj.name === "$routeProvider")) { return false; } node.$chained = chainedRouteProvider; var method = callee.property; // identifier if (method.name !== "when") { return false; } var args = path.get("arguments"); if (args.length !== 2) { return false; } var configArg = last(args); if (!t.isObjectExpression(configArg)) { return false; } var props = configArg.get("properties"); var res = [matchProp("controller", props)]; // {resolve: ..} res.push.apply(res, matchResolve(props)); var filteredRes = res.filter(Boolean); return filteredRes.length === 0 ? false : filteredRes; } function matchNgUi(path) { // $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 node = path.node; var callee = node.callee; var obj = callee.object; // identifier or expression var method = callee.property; // identifier var args = path.get("arguments"); // shortcut for $urlRouterProvider.when(.., function($scope) {}) if (obj.$chained === chainedUrlRouterProvider || t.isIdentifier(obj) && 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 || t.isIdentifier(obj) && 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(objectExpressionPath) { if (!objectExpressionPath || !t.isObjectExpression(objectExpressionPath)) { return false; } var properties = objectExpressionPath.get("properties"); matchStateProps(properties, res); var childrenArrayExpression = matchProp("children", properties); var children = childrenArrayExpression && childrenArrayExpression.get("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 && t.isObjectExpression(a)) { a.get("properties").forEach(function (prop) { var value = prop.get("value"); if (t.isObjectExpression(value)) { res.push(matchProp("value", value.get("properties"))); } else { res.push(value); } }); } // {view: ...} var viewObject = matchProp("views", props); if (viewObject && t.isObjectExpression(viewObject)) { viewObject.get("properties").forEach(function (prop) { var value = prop.get("value"); if (t.isObjectExpression(value)) { var _props = value.get("properties"); res.push(matchProp("controller", _props)); res.push(matchProp("controllerProvider", _props)); res.push(matchProp("templateProvider", _props)); res.push.apply(res, matchResolve(_props)); } }); } } } function matchInjectorInvoke(path) { // $injector.invoke(function($compile) { ... }); // we already know that node is a (non-computed) method call var node = path.node; var callee = node.callee; var obj = callee.object; // identifier or expression var method = callee.property; // identifier var args = void 0; return method.name === "invoke" && t.isIdentifier(obj) && obj.name === "$injector" && (args = path.get("arguments")).length >= 1 && args; } function matchHttpProvider(path) { // $httpProvider.interceptors.push(function($scope) {}); // $httpProvider.responseInterceptors.push(function($scope) {}); // we already know that node is a (non-computed) method call var node = path.node; var callee = node.callee; var obj = callee.object; // identifier or expression var method = callee.property; // identifier var args = void 0; return method.name === "push" && t.isMemberExpression(obj) && !obj.computed && obj.object.name === "$httpProvider" && is.someof(obj.property.name, ["interceptors", "responseInterceptors"]) && (args = path.get("arguments")).length >= 1 && args; } function matchControllerProvider(path) { // $controllerProvider.register("foo", function($scope) {}); // we already know that node is a (non-computed) method call var node = path.node; var callee = node.callee; var obj = callee.object; // identifier or expression var method = callee.property; // identifier var args = void 0; var target = t.isIdentifier(obj) && obj.name === "$controllerProvider" && method.name === "register" && (args = path.get("arguments")).length === 2 && args[1]; if (target) { target.node.$methodName = method.name; } return target; } function matchProvide(path, ctx) { // $provide.decorator("foo", function($scope) {}); // $provide.service("foo", function($scope) {}); // $provide.factory("foo", function($scope) {}); // $provide.provider("foo", function($scope) {}); // we already know that node is a (non-computed) method call var node = path.node; var callee = node.callee; var obj = callee.object; // identifier or expression var method = callee.property; // identifier var args = path.get("arguments"); var target = t.isIdentifier(obj) && obj.name === "$provide" && is.someof(method.name, ["decorator", "service", "factory", "provider"]) && args.length === 2 && args[1]; if (target) { target.node.$methodName = method.name; target.$methodName = method.name; if (ctx.rename) { // for eventual rename purposes return args; } } return target; } function matchRegular(path, ctx) { // we already know that node is a (non-computed) method call var node = path.node; var callee = node.callee; var obj = callee.object; // identifier or expression var method = callee.property; // identifier // short-cut implicit config special case: // angular.module("MyMod", function(a) {}) if (obj.name === "angular" && method.name === "module") { var _args2 = path.get("arguments"); if (_args2.length >= 2) { node.$chained = chainedRegular; return last(_args2); } } // hardcoded exception: foo.decorator is generally considered a short-form // declaration but $stateProvider.decorator is not. see https://github.com/olov/ng-annotate/issues/82 if (obj.name === "$stateProvider" && method.name === "decorator") { return false; } var matchAngularModule = (obj.$chained === chainedRegular || isReDef(obj, ctx) || isLongDef(obj)) && is.someof(method.name, ["provider", "value", "constant", "bootstrap", "config", "factory", "directive", "filter", "run", "controller", "service", "animation", "invoke", "store", "decorator", "component"]); if (!matchAngularModule) { return false; } node.$chained = chainedRegular; if (is.someof(method.name, ["value", "constant", "bootstrap"])) { return false; // affects matchAngularModule because of chaining } var args = node.arguments; var argPaths = path.get("arguments"); var target = is.someof(method.name, ["config", "run"]) ? args.length === 1 && argPaths[0] : args.length === 2 && t.isLiteral(args[0]) && is.string(args[0].value) && argPaths[1]; if (method.name === "component" && target) { target.node.$chained = chainedRegular; return matchComponent(target); } if (target) { target.node.$methodName = method.name; } if (ctx.rename && args.length === 2 && target) { // for eventual rename purposes var somethingNameLiteral = args[0]; return [somethingNameLiteral, target]; } return target; } // matches with default regexp // *.controller("MyCtrl", function($scope, $timeout) {}); // *.*.controller("MyCtrl", function($scope, $timeout) {}); // matches with --regexp "^require(.*)$" // require("app-module").controller("MyCtrl", function($scope) {}); function isReDef(node, ctx) { return ctx.re.test(ctx.srcForRange(node)); } // Long form: angular.module(*).controller("MyCtrl", function($scope, $timeout) {}); function isLongDef(node) { return node.callee && node.callee.object && node.callee.object.name === "angular" && node.callee.property && node.callee.property.name === "module"; } function last(arr) { return arr[arr.length - 1]; } function matchProp(name, props) { for (var i = 0; i < props.length; i++) { var propOrPath = props[i]; var prop = propOrPath.node || propOrPath; if (t.isIdentifier(prop.key) && prop.key.name === name || t.isLiteral(prop.key) && prop.key.value === name) { if (t.isObjectMethod(prop)) { return propOrPath; } return propOrPath.get && propOrPath.get("value") || prop.value; // FunctionExpression or ArrayExpression } } return null; } function matchResolve(props) { var resolveObject = matchProp("resolve", props); if (resolveObject && t.isObjectExpression(resolveObject)) { return resolveObject.get("properties").map(function (prop) { if (t.isObjectMethod(prop)) { return prop; } return prop.get("value"); }); } return []; } function matchComponent(path) { var chained = path.node.$chained; if (t.isIdentifier(path)) { path = followReference(path); if (t.isVariableDeclarator(path)) { path = path.get('init'); } } if (t.isObjectExpression(path)) { path.node.chained = chained; var props = path.get("properties"); var ctrl = matchProp("controller", props); var tmpl = matchProp("template", props); var tmplUrl = matchProp("templateUrl", props); var res = []; ctrl && res.push(ctrl); tmpl && res.push(tmpl); tmplUrl && res.push(tmplUrl); res.forEach(function (t) { return t.node.$chained = chained; }); return res; } else { return false; } } function renamedString(ctx, originalString) { if (ctx.rename) { return ctx.rename.get(originalString) || originalString; } return originalString; } function insertArray(ctx, path) { if (!path.node) { console.warn("Not a path", path, path.loc.start, path.loc.end); return; } var toParam = path.node.params.map(function (param) { return param.name; }); var elems = toParam.map(function (i) { return t.stringLiteral(i); }); elems.push(path.node); path.replaceWith(t.expressionStatement(t.arrayExpression(elems))); path.scope.crawl(); } // TODO: Is this necessary? function renameProviderDeclarationSite(ctx, literalNode, fragments) { fragments.push({ start: literalNode.range[0] + 1, end: literalNode.range[1] - 1, str: renamedString(ctx, literalNode.value), loc: { start: { line: literalNode.loc.start.line, column: literalNode.loc.start.column + 1 }, end: { line: literalNode.loc.end.line, column: literalNode.loc.end.column - 1 } } }); } function judgeSuspects(ctx) { var blocked = ctx.blocked; var suspects = makeUnique(ctx.suspects, 1); for (var n = 0; n < 42; n++) { // could be while(true), above is just a safety-net // in practice it will loop just a couple of times propagateModuleContextAndMethodName(suspects); if (!setChainedAndMethodNameThroughIifesAndReferences(suspects)) { break; } } // create final suspects by jumping, following, uniq'ing, blocking var finalSuspects = makeUnique(suspects.map(function (target) { var jumped = jumpOverIife(target); var jumpedAndFollowed = followReference(jumped) || jumped; if (target.$limitToMethodName && target.$limitToMethodName !== "*never*" && findOuterMethodName(target) !== target.$limitToMethodName) { return null; } if (blocked.indexOf(jumpedAndFollowed) >= 0) { return null; } return jumpedAndFollowed; }).filter(Boolean), 2); finalSuspects.forEach(function (path) { var target = path.node || path; if (target.$chained !== chainedRegular) { return; } if (t.isObjectMethod(path) && target.params.length) { // Replace object method shorthand { foo(bar){} } with long-form { foo: function(bar){} } so we can annotate it var func = t.functionExpression(null, target.params, target.body, target.generator, target.async); func.returnType = target.returnType; path.replaceWith(t.objectProperty(target.key, func, target.computed)); path = path.get("value"); target = path.node; } if (isFunctionExpressionWithArgs(target) && !t.isVariableDeclarator(path.parent)) { insertArray(ctx, path); } else if (isGenericProviderName(target)) { // console.warn("Generic provider rename disabled"); // renameProviderDeclarationSite(ctx, target, fragments); } else { // if it's not array or function-expression, then it's a candidate for foo.$inject = [..] judgeInjectArraySuspect(path, ctx); } }); function propagateModuleContextAndMethodName(suspects) { suspects.forEach(function (path) { if (path.node.$chained !== chainedRegular && isInsideModuleContext(path)) { path.node.$chained = chainedRegular; } if (!path.node.$methodName) { var methodName = findOuterMethodName(path); if (methodName) { path.node.$methodName = methodName; } } }); } function findOuterMethodName(path) { for (; path && !path.node.$methodName; path = path.parentPath) {} return path ? path.node.$methodName : null; } function setChainedAndMethodNameThroughIifesAndReferences(suspects) { var modified = false; suspects.forEach(function (path) { var target = path.node; var jumped = jumpOverIife(path); var jumpedNode = jumped.node; if (jumpedNode !== target) { // we did skip an IIFE if (target.$chained === chainedRegular && jumpedNode.$chained !== chainedRegular) { modified = true; jumpedNode.$chained = chainedRegular; } if (target.$methodName && !jumpedNode.$methodName) { modified = true; jumpedNode.$methodName = target.$methodName; } } var jumpedAndFollowed = followReference(jumped) || jumped; if (jumpedAndFollowed.node !== jumped.node) { // we did follow a reference if (jumped.node.$chained === chainedRegular && jumpedAndFollowed.node.$chained !== chainedRegular) { modified = true; jumpedAndFollowed.node.$chained = chainedRegular; } if (jumped.node.$methodName && !jumpedAndFollowed.node.$methodName) { modified = true; jumpedAndFollowed.node.$methodName = jumped.node.$methodName; } } }); return modified; } function isInsideModuleContext(path) { var $parent = path.parentPath; for (; $parent && $parent.node.$chained !== chainedRegular; $parent = $parent.parentPath) {} return Boolean($parent); } function makeUnique(suspects, val) { return suspects.filter(function (target) { if (target.$seen === val) { return false; } target.$seen = val; return true; }); } } function followReference(path) { var node = path.node; if (!scopeTools.isReference(path)) { return null; } var binding = path.scope.getBinding(node.name); if (!binding) { return null; } var kind = binding.kind; var bound = binding.path; if (is.someof(kind, ["const", "let", "var"])) { if (t.isVariableDeclaration(bound)) { var declarations = bound.get('declarations'); assert(declarations.length === 1); return declarations[0]; } assert(t.isVariableDeclarator(bound) || t.isClassDeclaration(bound)); // {type: "VariableDeclarator", id: {type: "Identifier", name: "foo"}, init: ..} return bound; } else if (kind === "hoisted") { assert(t.isFunctionDeclaration(bound) || isFunctionExpressionOrArrow(bound)); // FunctionDeclaration is the common case, i.e. // function foo(a, b) {} // FunctionExpression is only applicable for cases similar to // var f = function asdf(a,b) { mymod.controller("asdf", asdf) }; return bound; } // other kinds should not be handled ("param", "caught") return null; } function judgeInjectArraySuspect(path, ctx) { var node = path.node; if (t.isVariableDeclaration(node)) { // 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 path = path.get("declarations")[0]; node = path.node; } // onode is a top-level node (inside function block), later verified // node is inner match, descent in multiple steps var opath = null; var declaratorName = null; if (t.isVariableDeclarator(node)) { opath = path.parentPath; declaratorName = node.id.name; node = node.init; // var foo = ___; path = path.get("init"); } else { opath = path; } if (t.isExportDeclaration(opath.parent)) { opath = opath.parentPath; } // suspect must be inside of a block or at the top-level (i.e. inside of node.$parent.body[]) if (!node || !opath.parent || !t.isProgram(opath.parent) && !t.isBlockStatement(opath.parent)) { return; } path = jumpOverIife(path); node = path.node; if (t.isClass(node)) { if (!node.id) { node.id = path.scope.generateUidIdentifier('ngInjectAnonymousClass'); } declaratorName = node.id.name; node = getConstructor(node); } if (isFunctionExpressionWithArgs(node) || t.isClassMethod(node)) { // var x = 1, y = function(a,b) {}, z; if (node.id && node.id.name !== declaratorName) { console.warn("Declarator name different", declaratorName); } assert(declaratorName); addInjectArrayAfterPath(node.params, opath, declaratorName); } else if (isFunctionDeclarationWithArgs(node)) { if (t.isExportDefaultDeclaration(path.parent) && !node.id) { // export default function(a) {} node.id = path.scope.generateUidIdentifier('ngInjectExport'); path.parentPath.scope.crawl(); path.parentPath.insertBefore(buildInjectExpression(node.params, node.id.name)); } else { // /*@ngInject*/ function foo($scope) {} addInjectArrayBeforePath(node.params, path, node.id.name); } } else if (t.isExpressionStatement(node) && t.isAssignmentExpression(node.expression) && isFunctionExpressionWithArgs(node.expression.right) && !path.get("expression.right").$seen) { // /*@ngInject*/ foo.bar[0] = function($scope) {} var inject = buildInjectExpression(node.expression.right.params, t.cloneDeep(node.expression.left)); path.parentPath.scope.crawl(); path.insertAfter(inject); } else if (path = followReference(path)) { // node was a reference and followed node now is either a // FunctionDeclaration or a VariableDeclarator // => recurse !path.$seen && judgeInjectArraySuspect(path, ctx); } function buildInjectExpression(params, name) { var left = t.isNode(name) ? name : t.identifier(name); var paramStrings = params.map(function (param) { return t.stringLiteral(getNamedParam(param)); }); var arr = t.arrayExpression(paramStrings); // ["$scope"] var member = t.memberExpression(left, t.identifier("$inject")); // foo.$inject = return t.expressionStatement(t.assignmentExpression("=", member, arr)); } function addInjectArrayBeforePath(params, path, name) { var binding = path.scope.getBinding(name); if (binding && binding.kind === 'hoisted') { // let block = t.isProgram(binding.scope.block) ? binding.scope.block : binding.scope.block.body; // block.body.unshift(buildInjectExpression(params, name)); var expr = buildInjectExpression(params, name); var block = binding.scope.getBlockParent().path; if (block.isFunction()) { block = block.get("body"); } block.unshiftContainer("body", [expr]); } else { path.parentPath.scope.crawl(); path.insertBefore(buildInjectExpression(params, name)); } } function addInjectArrayAfterPath(params, path, name) { var trailingComments = void 0; if (path.node.trailingComments) { trailingComments = path.node.trailingComments; path.node.trailingComments = []; } path.parentPath.scope.crawl(); var newNode = path.insertAfter(buildInjectExpression(params, name)); newNode.trailingComments = trailingComments; } } function jumpOverIife(path) { var node = path.node; if (!path.node) { console.warn("Not a path"); } if (!(t.isCallExpression(node) && isFunctionExpressionOrArrow(node.callee))) { return path; } var outerbody = path.get("callee.body.body"); for (var i = 0; i < outerbody.length; i++) { var statement = outerbody[i]; if (t.isReturnStatement(statement)) { return statement.get("argument"); } } return path; } function addModuleContextDependentSuspect(target, ctx) { ctx.suspects.push(target); } function addModuleContextIndependentSuspect(target, ctx) { target.node.$chained = chainedRegular; ctx.suspects.push(target); } function isFunctionExpressionOrArrow(node) { return t.isFunctionExpression(node) || t.isArrowFunctionExpression(node); } function isFunctionExpressionWithArgs(node) { return isFunctionExpressionOrArrow(node) && node.params.length >= 1; } function isFunctionDeclarationWithArgs(node) { return t.isFunctionDeclaration(node) && node.params.length >= 1; } function isGenericProviderName(node) { return t.isLiteral(node) && is.string(node.value); } function getNamedParam(p) { var param = p; if (t.isAssignmentPattern(p)) param = p.left; return param.name; } function getConstructor(node) { var body = node.body.body; for (var i = 0; i < body.length; i++) { var _node = body[i]; if (_node.kind === 'constructor') { return _node; } } } module.exports.match = match; module.exports.addModuleContextDependentSuspect = addModuleContextDependentSuspect; module.exports.addModuleContextIndependentSuspect = addModuleContextIndependentSuspect; module.exports.judgeSuspects = judgeSuspects; module.exports.matchDirectiveReturnObject = matchDirectiveReturnObject; module.exports.matchProviderGet = matchProviderGet; },{"./nginject":4,"./scopetools":256,"assert":7,"babel-types":30,"simple-is":249}],4:[function(require,module,exports){ // nginject.js // MIT licensed, see LICENSE file // Copyright (c) 2013-2016 Olov Lassus <olov.lassus@gmail.com> "use strict"; var is = require("simple-is"); var t = require('babel-types'); var codeFrame = require("babel-code-frame"); module.exports = { inspectComment: inspectComment, inspectCallExpression: inspectCallExpression, inspectFunction: inspectFunction, inspectObjectExpression: inspectObjectExpression, inspectAssignment: inspectAssignment, inspectDeclarator: inspectDeclarator, inspectClassDeclaration: inspectClassDeclaration, inspectClassMethod: inspectClassMethod, inspectExportDeclaration: inspectExportDeclaration }; function inspectCallExpression(path, ctx) { var node = path.node; var name = node.callee.name; if (inspectComment(path, ctx)) { return false; } if (t.isIdentifier(node.callee) && (name === "ngInject" || name === "ngNoInject") && node.arguments.length === 1) { var block = name === "ngNoInject"; addSuspect(path.get("arguments")[0], ctx, block); } path.get("arguments").forEach(function (arg) { var annotation = getAnnotation(arg.node); if (!t.isIdentifier(arg) || annotation === null) { return; } var binding = path.scope.getBinding(arg.node.name); if (binding) { addSuspect(binding.path, ctx, !annotation); } }); } function inspectFunction(path, ctx) { var node = path.node; if (t.isVariableDeclarator(path.parent) && t.isVariableDeclaration(path.parentPath.parent)) { var annotation = getAnnotation(path.parentPath.parent); if (annotation === null) { annotation = getAnnotation(node); } if (annotation !== null) { addSuspect(path.parentPath.parentPath, ctx, !annotation); return; } } if (inspectComment(path, ctx)) { return; } var annotate = matchPrologueDirectives(path); if (annotate === null) { return; } // now add the correct suspect // for function declarations, it is always the function declaration node itself if (t.isFunctionDeclaration(node)) { addSuspect(path, ctx, !annotate); return; } // node is a function expression below // case 1: a function expression which is the rhs of a variable declarator, such as // var f1 = function(a) { // "ngInject" // }; // in this case we can mark the declarator, same as saying var /*@ngInject*/ f1 = function(a) .. // or /*@ngInject*/ var f1 = function(a) .. // f1.$inject = ["a"]; will be added (or rebuilt/removed) if (t.isVariableDeclarator(path.parent)) { addSuspect(path.parentPath, ctx, !annotate); return; } // case 2: an anonymous function expression, such as // g(function(a) { // "ngInject" // }); // // the suspect is now its parent annotated array (if any), otherwise itself // there is a risk of false suspects here, in case the parent annotated array has nothing to do // with annotations. the risk should be very low and hopefully easy to workaround // // added/rebuilt/removed => g(["a", function(a) { // "ngInject" // }]); var maybeArrayExpression = path.parentPath; if (isAnnotatedArray(maybeArrayExpression)) { addSuspect(maybeArrayExpression, ctx, !annotate); } else { addSuspect(path, ctx, !annotate); } } function inspectComment(path, ctx) { var node = path.node; var annotation = getAnnotation(node); if (annotation !== null) { addSuspect(path, ctx, !annotation); return true; } } function getAnnotation(node) { if (!node.leadingComments) { return null; } for (var i = 0; i < node.leadingComments.length; i++) { var value = node.leadingComments[i].value.replace(/^[\s\*]*/, '').replace(/[\s\*]*$/, '').trim(); if (value === "@ngInject") { return true; } else if (value === "@ngNoInject") { return false; } } return null; } function getAnnotations(nodes) { for (var i = 0; i < nodes.length; i++) { var annotation = getAnnotation(nodes[i]); if (annotation !== null) { return annotation; } } return null; } function inspectObjectExpression(path, ctx) { var node = path.node; // to pick up annotations that should apply to all properties // ie. /*@ngAnnotate*/ {} var candidates = [node]; if (t.isAssignmentExpression(path.parent)) { candidates.unshift(path.parent); if (t.isExpressionStatement(path.parentPath.parent)) { candidates.unshift(path.parentPath.parent); } } if (t.isVariableDeclarator(path.parent) && t.isVariableDeclaration(path.parentPath.parent)) { candidates.unshift(path.parentPath.parent); } var annotateEverything = getAnnotations(candidates); if (annotateEverything !== null) { addSuspect(path, ctx, !annotateEverything); } else { path.get("properties").filter(function (prop) { return isFunctionExpressionOrArrow(prop.node.value); }).forEach(function (prop) { return inspectComment(prop, ctx); }); } // path.get("properties").forEach(prop => { // if(t.isObjectExpression(prop.node.value)){ // inspectObjectExpression(prop.get("value"), ctx); // return; // } // let annotation = getAnnotation(prop.node); // if(annotation !== null || annotateEverything !== null){ // let effectiveAnnotation = annotation === null ? annotateEverything : annotation; // addSuspect(prop.get("value"), ctx, !effectiveAnnotation); // } // }); } function matchPrologueDirectives(path) { var prologueDirectives = ["ngInject", "ngNoInject"]; var directives = path.node.body.directives || []; var matches = directives.map(function (dir) { return dir.value.value; }).filter(function (val) { return prologueDirectives.indexOf(val) !== -1; }); if (matches.length) { var match = matches[0].trim(); if (match === "ngInject") return true; if (match === "ngNoInject") return false; } return null; } function inspectAssignment(path, ctx) { var node = path.node; if (!isFunctionExpressionOrArrow(node.right)) { return; } var candidates = [path.node, node.right]; if (t.isExpressionStatement(path.parent)) { candidates.unshift(path.parent); path = path.parentPath; } var annotation = getAnnotations(candidates); if (annotation !== null) { addSuspect(path, ctx, !annotation); } } function inspectDeclarator(path, ctx) { var node = path.node; if (!isFunctionExpressionOrArrow(node.init)) { return; } var candidates = [node, node.init]; if (t.isVariableDeclaration(path.parent)) { path = path.parentPath; } else { console.error("not a variable declaration"); } var annotation = getAnnotations(candidates); if (annotation !== null) { addSuspect(path, ctx, !annotation); } } function inspectClassDeclaration(path, ctx) { var node = path.node; var annotation = getAnnotation(node); if (annotation !== null) { addSuspect(path, ctx, !annotation); } } function inspectClassMethod(path, ctx) { var node = path.node; if (node.kind !== 'constructor') { return; } var annotation = getAnnotation(path.node); if (annotation === null) { annotation = matchPrologueDirectives(path); if (annotation === null) { return; } } var ancestry = path.getAncestry(); for (var i = 0; i < ancestry.length; i++) { var ancestor = ancestry[i]; if (ancestor.isClassDeclaration()) { addSuspect(ancestor, ctx, !annotation); return; } } } function inspectExportDeclaration(path, ctx) { var annotation = getAnnotation(path.node); if (annotation === null) { return; } addSuspect(path.get('declaration'), ctx, !annotation); } function isStringArray(node) { if (!t.isArrayExpression(node)) { return false; } return node.elements.length >= 1 && node.elements.every(function (n) { return t.isLiteral(n) && is.string(n.value); }); } function findNextStatement(path) { var body = path.parentPath.get("body"); for (var i = 0; i < body.length; i++) { if (body[i].path === path.node) { return body[i + 1] || null; } } return null; } function addSuspect(path, ctx, block) { var target = path.node; if (t.isExpressionStatement(target) && t.isAssignmentExpression(target.expression) && isStringArray(target.expression.right)) { // /*@ngInject*/ // FooBar.$inject = ["$a", "$b"]; // function FooBar($a, $b) {} var adjustedTarget = findNextStatement(path); if (adjustedTarget) { return addSuspect(adjustedTarget, ctx, block); } } if (t.isObjectExpression(path)) { // /*@ngInject*/ {f1: function(a), .., {f2: function(b)}} addObjectExpression(path, ctx); } else if (t.isAssignmentExpression(target) && t.isObjectExpression(target.right)) { // /*@ngInject*/ f(x.y = {f1: function(a), .., {f2: function(b)}}) addObjectExpression(target.get("right"), ctx); } else if (t.isExpressionStatement(target) && t.isAssignmentExpression(target.expression) && t.isObjectExpression(target.expression.right)) { // /*@ngInject*/ x.y = {f1: function(a), .., {f2: function(b)}} addObjectExpression(target.get("expression.right"), ctx); } else if (t.isVariableDeclaration(target) && target.declarations.length === 1 && target.declarations[0].init && t.isObjectExpression(target.declarations[0].init)) { // /*@ngInject*/ var x = {f1: function(a), .., {f2: function(b)}} addObjectExpression(target.get("declarations")[0].get("init"), ctx); } else if (t.isProperty(target)) { // {/*@ngInject*/ justthisone: function(a), ..} var value = path.get("value"); value.$limitToMethodName = "*never*"; addOrBlock(value, ctx); } else { // /*@ngInject*/ function(a) {} path.$limitToMethodName = "*never*"; addOrBlock(path, ctx); } function addObjectExpression(path, ctx) { nestedObjectValues(path).forEach(function (n) { n.$limitToMethodName = "*never*"; addOrBlock(n, ctx); }); } function addOrBlock(path, ctx) { if (block) { ctx.blocked.push(path); } else { ctx.addModuleContextIndependentSuspect(path, ctx); } } } function nestedObjectValues(path, res) { res = res || []; path.get("properties").forEach(function (prop) { var v = prop.get("value"); if (isFunctionExpressionOrArrow(v) || t.isArrayExpression(v)) { res.push(v); } else if (t.isObjectExpression(v)) { nestedObjectValues(v, res); } }); return res; } function isAnnotatedArray(path) { if (!t.isArrayExpression(path)) { return false; } var elements = path.get('elements'); // last should be a function expression var fn = elements.slice(-1)[0]; if (elements.length === 0 || !isFunctionExpressionOrArrow(fn)) { return false; } var fnParams = fn.node.params.map(function (param) { return param.name; }); if (fnParams.length > elements.length - 1) { throw path.buildCodeFrameError("[angularjs-annotate] ERROR: Function parameters do not match existing annotations."); } var warnedOnce = false; // all but last should be string literals for (var i = 0; i < elements.length - 1; i++) { var n = elements[i]; if (!t.isLiteral(n) || !is.string(n.node.value)) { return false; } if (!warnedOnce && fnParams[i] && n.node.value !== fnParams[i]) { warnedOnce = true; var frame = codeFrame(n.hub.file.code, n.node.loc.start.line, n.node.loc.start.column + 2); console.warn("[angularjs-annotate] WARN: Function parameters do not match existing annotations.\n" + frame); } } return true; } function isFunctionExpressionOrArrow(node) { return t.isFunctionExpression(node) || t.isArrowFunctionExpression(node); } },{"babel-code-frame":8,"babel-types":30,"simple-is":249}],5:[function(require,module,exports){ 'use strict'; module.exports = function () { return /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]/g; }; },{}],6:[function(require,module,exports){ 'use strict'; function assembleStyles () { var styles = { modifiers: { reset: [0, 0], bold: [1, 22], // 21 isn't widely supported and 22 does the same thing dim: [2, 22], italic: [3, 23], underline: [4, 24], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29] }, colors: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], gray: [90, 39] }, bgColors: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49] } }; // fix humans styles.colors.grey = styles.colors.gray; Object.keys(styles).forEach(function (groupName) { var group = styles[groupName]; Object.keys(group).forEach(function (styleName) { var style = group[styleName]; styles[styleName] = group[styleName] = { open: '\u001b[' + style[0] + 'm', close: '\u001b[' + style[1] + 'm' }; }); Object.defineProperty(styles, groupName, { value: group, enumerable: false }); }); return styles; } Object.defineProperty(module, 'exports', { enumerable: true, get: assembleStyles }); },{}],7:[function(require,module,exports){ (function (global){ 'use strict'; // compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac2