├── .gitignore ├── .travis.yml ├── BUILD.md ├── CHANGES.md ├── IMPLICIT.md ├── LICENSE ├── OPTIONS.md ├── README.md ├── build ├── build.sh ├── clean.sh ├── defs-config.json ├── es5 │ ├── ng-annotate │ └── ng-annotate-main.js ├── inline-version.js ├── ng-annotate └── prepare.sh ├── defs-config.json ├── generate-sourcemap.js ├── lut.js ├── ng-annotate-harmony ├── ng-annotate-main.js ├── ng-annotate.js ├── nginject.js ├── optionals └── angular-dashboard-framework.js ├── package.json ├── plugin-example.js ├── pos-to-linecolumn.js ├── run-tests.js ├── scope.js ├── scopetools.js └── tests ├── angular.js ├── has_inject.js ├── has_inject_removed.js ├── ngmin-tests ├── ngmin-issues.txt ├── ngmin_original.js ├── ngmin_with_annotations.js └── repo │ ├── INFO │ ├── chain.js │ ├── directive.js │ ├── loader.js │ ├── reference.js │ ├── route-provider.js │ ├── simple.js │ └── util.js ├── optionals ├── angular-dashboard-framework.annotated.js └── angular-dashboard-framework.js ├── original.js ├── rename.annotated.js ├── rename.js ├── sourcemaps.annotated.js ├── sourcemaps.coffee ├── todo.js └── with_annotations.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | *~ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5.3" 4 | - "5.2" 5 | - "5.1" 6 | - "5.0" 7 | - "4.2" 8 | - "4.1" 9 | - "4.0" 10 | - "0.10" 11 | - "iojs" 12 | -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | # Build instructions 2 | ng-annotate is written in ES6 constlet style and uses defs.js to transpile 3 | to ES5, via an optional build step, so that it can execute without the 4 | `--harmony` flag passed to node. 5 | 6 | The git repository contains the original constlet style source code as well 7 | as the build scripts. It does not contain build artefacts (transpiled or 8 | bundled source). 9 | 10 | The build scripts populates the `build/es5` directory. 11 | The NPM package contains a snapshot of the git repository at the time as 12 | well as `build/es5`. `package.json` refers to the transpiled version in 13 | `build/es5`, so there's no need to execute node with `--harmony` when 14 | running a `npm -g` installed `ng-annotate` from the command line or when 15 | doing a `require("ng-annotate")` of the same. 16 | 17 | If you clone the git repository then don't forget to also `npm install` the 18 | dependencies (see `package.json`). 19 | 20 | If you want to run ng-annotate in its original form (rather than 21 | transpiled), for instance if you're hacking on it, then just run the tool 22 | via `ng-annotate-harmony` (not a NPM exported binary but check the package 23 | root) or include it as a library via 24 | `require("ng-annotate/ng-annotate-main")`. This applies to a git clone just 25 | as well as the NPM package. 26 | 27 | `run-tests.js` is the test runner. Run it on the original source via 28 | `node --harmony run-tests.js` or `npm test`. The tests are run automatically 29 | in the build scripts. 30 | 31 | To build, `cd build` then run `./build.sh` for defs transpilation. 32 | `./clean.sh` removes the build artefacts. 33 | 34 | I use `prepare.sh` to prepare a release tarball for NPM publishing. 35 | 36 | Happy hacking! 37 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## v1.2.2 2017-06-01 2 | * so long and thanks for all the bits 3 | 4 | ## v1.2.1 2016-01-10 5 | * bugfix case where $inject arrays were incorrectly hoisted 6 | * angular-dashboard-framework optional: match apply 7 | 8 | ## v1.2.0 2015-12-24 9 | * bugfix $inject array positioning with TypeScript inheritance (__extends) 10 | 11 | ## v1.1.1 2015-12-22 12 | * bugfix parsing in strict mode even if source is not 13 | 14 | ## v1.1.0 2015-12-19 15 | * bugfix $inject arrays for function definitions not being hoisted 16 | * match angular.module("MyMod").component("foo", {controller: function ..}) 17 | * match angular.module("MyMod").decorator("foo", function ..) 18 | * match $controllerProvider.register 19 | * match $uibModal.open from angular-ui (recently renamed from $modal.open) 20 | * ui-router declarations improvements 21 | 22 | ## v1.0.2 2015-07-17 23 | * bugfix reference-following crash 24 | 25 | ## v1.0.1 2015-06-25 26 | * don't include .gitignore in npm package 27 | 28 | ## v1.0.0 2015-05-27 29 | * optional matchers: --list and --enable 30 | * angular-dashboard-framework optional 31 | * bugfix documentation of sourcemap API 32 | * improved incoming sourcemap support 33 | * match flux-angular myMod.store("MyCtrl", function ..) 34 | * bugfix duplicated fn.$inject arrays in some IIFE situations 35 | * emit LF/CRLF newlines depending on input newlines 36 | * minor newline fixes 37 | 38 | ## v0.15.4 2015-01-29 39 | * improved Traceur compatibility ("ngInject" prologue => fn.$inject = [..] arrays) 40 | 41 | ## v0.15.3 2015-01-28 42 | * bugfix "ngInject" directive prologue (removing and rebuilding) 43 | * bugfix extra newlines when rebuilding existing fn.$inject = [..] arrays 44 | 45 | ## v0.15.2 2015-01-26 46 | * bugfix crash on ES6 input (but ng-annotate does not yet understand ES6) 47 | 48 | ## v0.15.1 2015-01-15 49 | * bugfix release for compatibility with io.js 50 | 51 | ## v0.15.0 2015-01-15 52 | * "ngInject" directive prologue (usage like "use strict") 53 | * /* @ngNoInject */, ngNoInject(..) and "ngNoInject" for suppressing false positives 54 | * Acorn is now the default and only parser 55 | * removed the experimental --es6 option and made it the default 56 | 57 | ## v0.14.1 2014-12-04 58 | * bugfix /* @ngInject */ not working as expected in case of other matches 59 | 60 | ## v0.14.0 2014-11-27 61 | * support sourcemap combination and better map granularity 62 | 63 | ## v0.13.0 2014-11-18 64 | * match $mdDialog.show, $mdToast.show and $mdBottomSheet.show 65 | * improved $provide matching (.decorator, .service, .factory and .provider) 66 | 67 | ## v0.12.1 2014-11-13 68 | * bugfix crash when reference-following to an empty variable declarator 69 | 70 | ## v0.12.0 2014-11-10 71 | * improved TypeScript compatibility due to improved matching through IIFE's 72 | * match $injector.invoke 73 | * $modal.open is no longer experimental 74 | * reference-following is no longer experimental 75 | 76 | ## v0.11.0 2014-11-03 77 | * bugfix reference-following such as var Ctrl = function(dep1, dep2) {} 78 | 79 | ## v0.10.3 2014-11-03 80 | * match properties {name: ..}, {"name": ..} and {'name': ..} alike 81 | 82 | ## v0.10.2 2014-10-09 83 | * --es6 option for ES6 support via the Acorn parser (experimental) 84 | 85 | ## v0.10.1 2014-09-19 86 | * support stateHelperProvider.setNestedState nested children 87 | 88 | ## v0.10.0 2014-09-15 89 | * support stateHelperProvider.setNestedState 90 | * optional renaming of declarations and references (experimental) 91 | * further improved detection of existing fn.$inject = [..] arrays 92 | * improved insertion of $inject arrays in case of early return 93 | * improved angular module detection (reference-following) 94 | * restrict matching based on method context (directive, provider) 95 | 96 | ## v0.9.11 2014-08-09 97 | * improved detection of existing fn.$inject = [..] arrays 98 | 99 | ## v0.9.10 2014-08-07 100 | * reference-following (experimental) 101 | * ngInject(..) as an alternative to /* @ngInject */ .. 102 | * more flexible /* @ngInject */ placement (object literals) 103 | 104 | ## v0.9.9 2014-08-02 105 | * --sourcemap option for generating inline source maps 106 | 107 | ## v0.9.8 2014-07-28 108 | * match implicit config function: angular.module("MyMod", function(dep) {}) 109 | * match through IIFE's 110 | 111 | ## v0.9.7 2014-07-11 112 | * more capable /* @ngInject */ (support function expression assignment) 113 | 114 | ## v0.9.6 2014-06-12 115 | * match myMod.invoke 116 | * more capable --regexp option (match any method callee, identifier or not) 117 | 118 | ## v0.9.5 2014-05-23 119 | * added ability to read from stdin and write to file 120 | * bugfix name of generated fn.$inject = [..] arrays (was: fn.$injects) 121 | 122 | ## v0.9.4 2014-05-19 123 | * stricter match: only match code inside of angular modules (except explicit) 124 | * ui-router declarations improvements 125 | * bugfix duplicated annotations arrays in case of redundant /* @ngInject */ 126 | * indent generated fn.$inject = [..] arrays nicely 127 | 128 | ## v0.9.3 2014-05-16 129 | * /* @ngInject */ object literal support 130 | * bugfix ES5 strict mode oops 131 | * added more tools that support ng-annotate to README 132 | 133 | ## v0.9.2 2014-05-15 134 | * match $modal.open from angular-ui/bootstrap (experimental) 135 | * --stats option for runtime statistics (experimental) 136 | 137 | ## v0.9.1 2014-05-14 138 | * revert match .controller(name, ..) that was added in 0.9.0 because it 139 | triggered false positives 140 | 141 | ## v0.9.0 2014-05-13 142 | * explicit annotations using /* @ngInject */ 143 | * --plugin option to load user plugins (experimental, 0.9.x may change API) 144 | * match $httpProvider.interceptors.push(function($scope) {}) 145 | * match $httpProvider.responseInterceptors.push(function($scope) {}) 146 | * match self and that as aliases to this for this.$get = function($scope){} 147 | * match .controller(name, ..) in addition to .controller("name", ..) 148 | * bugfix ui-router declarations 149 | * bugfix angular.module("MyMod").bootstrap(e, [], {}) disrupting chaining 150 | * even faster (~6% faster annotating angular.js) 151 | * add error array to API return object 152 | 153 | ## v0.8.0 2014-05-09 154 | * ngRoute support: $routeProvider.when("path", { .. }) 155 | * even faster (~11% faster annotating angular.js) 156 | 157 | ## v0.7.3 2014-05-07 158 | * support obj.myMod.controller(..) in addition to myMod.controller(..) 159 | 160 | ## v0.7.2 2014-05-01 161 | * ui-router declarations improvements 162 | 163 | ## v0.7.1 2014-04-30 164 | * ui-router declarations improvements 165 | 166 | ## v0.7.0 2014-04-30 167 | * ui-router declarations support 168 | 169 | ## v0.6.0 2014-04-20 170 | * --single_quotes option to output '$scope' instead of "$scope" 171 | 172 | ## v0.5.0 2014-04-11 173 | * tweaked output: ["foo", "bar", ..] instead of ["foo","bar", ..] 174 | 175 | ## v0.4.0 2013-10-31 176 | * match angular.module("MyMod").animation(".class", function ..) 177 | 178 | ## v0.3.3 2013-10-03 179 | * bugfix .provider("foo", function($scope) ..) annotation. fixes #2 180 | 181 | ## v0.3.2 2013-09-30 182 | * bugfix angular.module("MyMod").constant("foo", "bar") disrupting chaining 183 | * match $provide.decorator (in addition to other $provide methods) 184 | 185 | ## v0.3.1 2013-09-30 186 | * bugfix angular.module("MyMod").value("foo", "bar") disrupting chaining 187 | 188 | ## v0.3.0 2013-09-30 189 | * ES5 build via defs 190 | * Grunt-support via grunt-ng-annotate 191 | 192 | ## v0.2.0 2013-09-06 193 | * better matching 194 | 195 | ## v0.1.2 2013-09-03 196 | * better README 197 | 198 | ## v0.1.1 2013-09-03 199 | * cross-platform shell script wrapper 200 | -------------------------------------------------------------------------------- /IMPLICIT.md: -------------------------------------------------------------------------------- 1 | # Implicit matching 2 | ng-annotate uses static analysis to detect common AngularJS code patterns. 3 | There are patterns it does not and never will understand and for those you 4 | should use `"ngInject"` instead, see [README.md](README.md). 5 | 6 | 7 | ## Declaration forms 8 | ng-annotate understands the two common declaration forms: 9 | 10 | Long form: 11 | 12 | ```js 13 | angular.module("MyMod").controller("MyCtrl", function($scope, $timeout) { 14 | }); 15 | ``` 16 | 17 | Short form: 18 | 19 | ```js 20 | myMod.controller("MyCtrl", function($scope, $timeout) { 21 | }); 22 | ``` 23 | 24 | It's not limited to `.controller` of course. It understands `.config`, `.factory`, 25 | `.directive`, `.filter`, `.run`, `.controller`, `.provider`, `.service`, `.decorator`, 26 | `.component`, `.animation` and `.invoke`. 27 | 28 | For short forms it does not need to see the declaration of `myMod` so you can run it 29 | on your individual source files without concatenating. If ng-annotate detects a short form 30 | false positive then you can use the `--regexp` option to limit the module identifier. 31 | Examples: `--regexp "^myMod$"` (match only `myMod`) or `--regexp "^$"` (ignore short forms). 32 | You can also use `--regexp` to opt-in for more advanced method callee matching, for 33 | example `--regexp "^require(.*)$"` to detect and transform 34 | `require('app-module').controller(..)`. Not using the option is the same as passing 35 | `--regexp "^[a-zA-Z0-9_\$\.\s]+$"`, which means that the callee can be a (non-unicode) 36 | identifier (`foo`), possibly with dot notation (`foo.bar`). 37 | 38 | ng-annotate understands `angular.module("MyMod", function(dep) ..)` as an alternative to 39 | `angular.module("MyMod").config(function(dep) ..)`. 40 | 41 | ng-annotate understands `this.$get = function($scope) ..` and 42 | `{.., $get: function($scope) ..}` inside a `provider`. `self` and `that` can be used as 43 | aliases for `this`. 44 | 45 | ng-annotate understands `return {.., controller: function($scope) ..}` inside a 46 | `directive`. 47 | 48 | ng-annotate understands `$provide.decorator("bar", function($scope) ..)`, `$provide.service`, 49 | `$provide.factory` and `$provide.provider`. 50 | 51 | ng-annotate understands `$routeProvider.when("path", { .. })`. 52 | 53 | ng-annotate understands `$controllerProvider.register("foo", function($scope) ..)`. 54 | 55 | ng-annotate understands `$httpProvider.interceptors.push(function($scope) ..)` and 56 | `$httpProvider.responseInterceptors.push(function($scope) ..)`. 57 | 58 | ng-annotate understands `$injector.invoke(function ..)`. 59 | 60 | ng-annotate understands [ui-router](https://github.com/angular-ui/ui-router) (`$stateProvider` and 61 | `$urlRouterProvider`). 62 | 63 | ng-annotate understands `$uibModal.open` (and `$modal.open`) ([angular-ui/bootstrap](http://angular-ui.github.io/bootstrap/)). 64 | 65 | ng-annotate understands `$mdDialog.show`, `$mdToast.show` and `$mdBottomSheet.show` 66 | ([angular material design](https://material.angularjs.org/#/api/material.components.dialog/service/$mdDialog)). 67 | 68 | ng-annotate understands `myMod.store("MyCtrl", function ..)` 69 | ([flux-angular](https://github.com/christianalfoni/flux-angular)). 70 | 71 | ng-annotate understands chaining. 72 | 73 | ng-annotate understands IIFE's and attempts to match through them, so 74 | `(function() { return function($scope) .. })()` works anywhere 75 | `function($scope) ..` does (for any IIFE args and params). 76 | 77 | ng-annotate understands [angular-dashboard-framework](https://github.com/sdorra/angular-dashboard-framework) 78 | via optional `--enable angular-dashboard-framework`. 79 | 80 | 81 | ## Reference-following 82 | ng-annotate follows references. This works if and only if the referenced declaration is 83 | a) a function declaration or 84 | b) a variable declaration with an initializer. 85 | Modifications to a reference outside of its declaration site are ignored by ng-annotate. 86 | 87 | These examples will get annotated: 88 | 89 | ```js 90 | function MyCtrl($scope, $timeout) { 91 | } 92 | var MyCtrl2 = function($scope) {}; 93 | 94 | angular.module("MyMod").controller("MyCtrl", MyCtrl); 95 | angular.module("MyMod").controller("MyCtrl", MyCtrl2); 96 | ``` 97 | 98 | 99 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2016 Olov Lassus 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /OPTIONS.md: -------------------------------------------------------------------------------- 1 | ## ng-annotate command-line options 2 | 3 | `ng-annotate OPTIONS `. The errors (if any) will go to stderr, 4 | the transpiled output to stdout. 5 | 6 | Use the `--add` (`-a`) option to add annotations where non-existing, 7 | use `--remove` (`-r`) to remove all existing annotations, 8 | use `--add --remove` (`-ar`) to rebuild all annotations. 9 | 10 | Use the `-o` option to write output to file. 11 | 12 | Provide `-` instead of an input `` to read input from stdin. 13 | 14 | Use the `--sourcemap` option to generate an inline sourcemap. 15 | 16 | Use the `--sourceroot` option to set the sourceRoot property of the generated sourcemap. 17 | 18 | Use the `--single_quotes` option to output `'$scope'` instead of `"$scope"`. 19 | 20 | Use the `--regexp` option to restrict matching further or to expand matching. 21 | See description further down. 22 | 23 | Use the `--list` option to list optional matchers. 24 | 25 | Use the `--enable` option to enable optional matcher. 26 | 27 | *experimental* Use the `--rename` option to rename providers (services, factories, 28 | controllers, etc.) with a new name when declared and referenced through annotation. 29 | Use it like this: `--rename oldname1 newname1 oldname2 newname2` 30 | 31 | *experimental* Use the `--plugin` option to load a user plugin with the provided path, 32 | 1.x may change API). See [plugin-example.js](plugin-example.js) for more info. 33 | 34 | *experimental* Use the `--stats` option to print statistics on stderr. 35 | 36 | 37 | ## Library (API) 38 | ng-annotate can be used as a library. See [ng-annotate.js](ng-annotate.js) for further info about 39 | options and return value. 40 | 41 | ```js 42 | var ngAnnotate = require("ng-annotate"); 43 | var somePlugin = require("./some/path/some-plugin"); 44 | var res = ngAnnotate(src, { 45 | add: true, 46 | plugin: [somePlugin], 47 | rename: [{from: "generalname", to: "uniquename"}, {from: "alpha", to: "beta"}], 48 | map: { inline: false, inFile: "source.js", sourceRoot: "/path/to/source/root" }, 49 | enable: ["angular-dashboard-framework"], 50 | }); 51 | var errorstringArray = res.errors; 52 | var transformedSource = res.src; 53 | var transformedSourceMap = res.map; 54 | ``` 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATION NOTICE - SO LONG AND THANKS FOR ALL THE BITS 2 | **ng-annotate is not maintained any longer.** 3 | 4 | Please check out its successor instead: [babel-plugin-angularjs-annotate](https://github.com/schmod/babel-plugin-angularjs-annotate) 5 | 6 | **Please don't open any issues or pull requests on ng-annotate. No new releaseses will be made.** 7 | 8 | Feel free to fork the repo and publish your modified version if you want to. More info about the deprecation can be found in [Issue #245: The future of ng-annotate](https://github.com/olov/ng-annotate/issues/245). 9 | 10 | 11 | # ng-annotate [![Build Status](https://travis-ci.org/olov/ng-annotate.svg?branch=master)](https://travis-ci.org/olov/ng-annotate) 12 | ng-annotate adds and removes AngularJS dependency injection annotations. 13 | 14 | Write your code without annotations and mark-up functions to be annotated 15 | with the `"ngInject"` directive prologue, just like you would 16 | `"use strict"`. This must be at the beginning of your function. 17 | 18 | ```js 19 | $ cat source.js 20 | angular.module("MyMod").controller("MyCtrl", function($scope, $timeout) { 21 | "ngInject"; 22 | ... 23 | }); 24 | ``` 25 | 26 | Then run ng-annotate as a build-step to produce this intermediary, 27 | annotated, result (later sent to the minifier of choice): 28 | 29 | ```js 30 | $ ng-annotate -a source.js 31 | angular.module("MyMod").controller("MyCtrl", ["$scope", "$timeout", function($scope, $timeout) { 32 | "ngInject"; 33 | ... 34 | }]); 35 | ``` 36 | 37 | Your minifier will most likely retain the `"ngInject"` prologues so use `sed` 38 | or a regexp in your build toolchain to get rid of those on the ng-annotate output. 39 | `sed` example: `ng-annotate -a source.js | sed "s/[\"']ngInject[\"'];*//g"`. 40 | JavaScript regexp example: `source.replace(/["']ngInject["'];*/g, "")`. 41 | 42 | You can also use ng-annotate to rebuild or remove existing annotations. 43 | Rebuilding is useful if you like to check-in the annotated version of your 44 | source code. When refactoring, just change parameter names once and let 45 | ng-annotate rebuild the annotations. Removing is useful if you want to 46 | de-annotate an existing codebase that came with checked-in annotations 47 | 48 | 49 | ## Installation and usage 50 | 51 | ```bash 52 | npm install -g ng-annotate 53 | ``` 54 | 55 | Then run it as `ng-annotate OPTIONS `. The errors (if any) will go to stderr, 56 | the transpiled output to stdout. 57 | 58 | The simplest usage is `ng-annotate -a infile.js > outfile.js`. 59 | See [OPTIONS.md](OPTIONS.md) for command-line documentation. 60 | 61 | ng-annotate can be used as a library, see [OPTIONS.md](OPTIONS.md) for its API. 62 | 63 | 64 | ## Implicit matching of common code forms 65 | ng-annotate uses static analysis to detect common AngularJS code patterns. When 66 | this works it means that you do not need to mark-up functions with `"ngInject"`. 67 | For a lot of code bases this works very well (use `ng-strict-di` to simplify 68 | debugging when it doesn't) but for others it is less reliable and you may prefer 69 | to use `"ngInject"` instead. For more information about implicit matching see 70 | [IMPLICIT.md](IMPLICIT.md). 71 | 72 | 73 | ## Explicit annotations with ngInject 74 | The recommended `function foo($scope) { "ngInject"; ... }` can be exchanged 75 | for `/*@ngInject*/ function foo($scope) { ... }` or 76 | `ngInject(function foo($scope) { ... })`. If you use the latter form then 77 | then add `function ngInject(v) { return v }` somewhere in your codebase or process 78 | away the `ngInject` function call in your build step. 79 | 80 | 81 | ### Suppressing false positives with ngNoInject 82 | The `/*@ngInject*/`, `ngInject(..)` and `"ngInject"` siblings have three cousins that 83 | are used for the opposite purpose, suppressing an annotation that ng-annotate added 84 | incorrectly (a "false positive"). They are called `/*@ngNoInject*/`, `ngNoInject(..)` 85 | and `"ngNoInject"` and do exactly what you think they do. 86 | 87 | 88 | ## ES6 and TypeScript support 89 | ng-annotate supports ES5 as input so run it with the output from Babel, Traceur, 90 | TypeScript (tsc) and the likes. Use `"ngInject"` on functions you want annotated. 91 | Your transpiler should preserve directive prologues, if not please file a bug on it. 92 | 93 | 94 | ## Highly recommended: enable ng-strict-di 95 | `
` 96 | 97 | Do that in your ng-annotate processed (but not minified) builds and AngularJS will 98 | let you know if there are any missing dependency injection annotations. 99 | [ng-strict-di](https://docs.angularjs.org/api/ng/directive/ngApp) is available in 100 | AngularJS 1.3 or later. 101 | 102 | 103 | ## Tools support 104 | * [Grunt](http://gruntjs.com/): [grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) by [Michał Gołębiowski](https://github.com/mzgol) 105 | * [Browserify](http://browserify.org/): [browserify-ngannotate](https://www.npmjs.org/package/browserify-ngannotate) by [Owen Smith](https://github.com/omsmith) 106 | * [Brunch](http://brunch.io/): [ng-annotate-uglify-js-brunch](https://www.npmjs.org/package/ng-annotate-uglify-js-brunch) by [Kagami Hiiragi](https://github.com/Kagami) 107 | * [Gulp](http://gulpjs.com/): [gulp-ng-annotate](https://www.npmjs.org/package/gulp-ng-annotate/) by [Kagami Hiiragi](https://github.com/Kagami) 108 | * [Broccoli](https://github.com/broccolijs/broccoli): [broccoli-ng-annotate](https://www.npmjs.org/package/broccoli-ng-annotate) by [Gilad Peleg](https://github.com/pgilad) 109 | * [Rails asset pipeline](http://guides.rubyonrails.org/asset_pipeline.html): [ngannotate-rails](https://rubygems.org/gems/ngannotate-rails) by [Kari Ikonen](https://github.com/kikonen) 110 | * [Grails asset pipeline](https://github.com/bertramdev/asset-pipeline): [angular-annotate-asset-pipeline](https://github.com/craigburke/angular-annotate-asset-pipeline) by [Craig Burke](https://github.com/craigburke) 111 | * [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), [ng-annotate-loader](https://www.npmjs.org/package/ng-annotate-loader) by [Andrey Skladchikov](https://github.com/huston007) 112 | * [Middleman](http://middlemanapp.com/): [middleman-ngannotate](http://rubygems.org/gems/middleman-ngannotate) by [Michael Siebert](https://github.com/siebertm) 113 | * [ENB](http://enb-make.info/) (Russian): [enb-ng-techs](https://www.npmjs.org/package/enb-ng-techs#ng-annotate) by [Alexey Gurianov](https://github.com/guria) 114 | * [rollup](https://rollupjs.org/): [rollup-pulgin-ng1-annotate](https://github.com/xierenyuan/rollup-plugin-ng-annotate) by [RenHong Xie](https://github.com/xierenyuan) 115 | * [Metalsmith](http://www.metalsmith.io/): [metalsmith-ng-annotate](https://www.npmjs.com/package/metalsmith-ng-annotate) by [Miguel Fonseca](https://github.com/fmmfonseca) 116 | 117 | 118 | ## Changes 119 | See [CHANGES.md](CHANGES.md). 120 | 121 | 122 | ## Build and test 123 | ng-annotate is written in ES6 constlet style and uses [defs.js](https://github.com/olov/defs) 124 | to transpile to ES5. See [BUILD.md](BUILD.md) for build and test instructions. 125 | 126 | 127 | ## Issues and contributions 128 | Please provide issues in the form of input, expected output, actual output. Include 129 | the version of ng-annotate and node that you are using. With pull requests, please 130 | include changes to the tests as well (tests/original.js, tests/with_annotations.js). 131 | 132 | 133 | ## License 134 | `MIT`, see [LICENSE](LICENSE) file. 135 | 136 | ng-annotate is written by [Olov Lassus](https://github.com/olov) with the kind help by 137 | [contributors](https://github.com/olov/ng-annotate/graphs/contributors). 138 | [Follow @olov](https://twitter.com/olov) on Twitter for updates about ng-annotate. 139 | -------------------------------------------------------------------------------- /build/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "beginning ng-annotate defs-build" 3 | rm -rf es5 4 | mkdir es5 5 | 6 | declare -a files=(generate-sourcemap.js lut.js ng-annotate.js ng-annotate-main.js nginject.js run-tests.js scope.js scopetools.js optionals/angular-dashboard-framework.js) 7 | DEFS="../node_modules/.bin/defs" 8 | if [[ ! -f "$DEFS" ]]; then DEFS="../../../../node_modules/.bin/defs" ; fi 9 | if [[ ! -f "$DEFS" ]]; then DEFS="defs" ; fi 10 | 11 | for i in ${files[@]} 12 | do 13 | echo "building $i with defs" 14 | mkdir -p es5/$(dirname $i) 15 | $DEFS ../$i > es5/$i 16 | done 17 | 18 | cp ng-annotate es5/ 19 | 20 | echo "hard-coding version" 21 | node --harmony inline-version.js 22 | 23 | cd es5 24 | 25 | echo "running tests (in es5 mode i.e. without --harmony)" 26 | cp -r ../../tests . 27 | /usr/bin/env node run-tests.js 28 | echo "done self-build" 29 | -------------------------------------------------------------------------------- /build/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "cleaning build files" 3 | rm -rf es5 browser npm 4 | echo "done cleaning" 5 | -------------------------------------------------------------------------------- /build/defs-config.json: -------------------------------------------------------------------------------- 1 | ../defs-config.json -------------------------------------------------------------------------------- /build/es5/ng-annotate: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "Error: ng-annotate has not been built.\nRetry after doing this: cd ; npm install; cd build; ./build.sh" 3 | exit 1 4 | -------------------------------------------------------------------------------- /build/es5/ng-annotate-main.js: -------------------------------------------------------------------------------- 1 | console.error("Error: ng-annotate has not been built.\nRetry after doing this: cd ; npm install; cd build; ./build.sh"); 2 | process.exit(1) 3 | -------------------------------------------------------------------------------- /build/inline-version.js: -------------------------------------------------------------------------------- 1 | const version = require("../package.json").version 2 | const fs = require("fs"); 3 | 4 | const src = String(fs.readFileSync("es5/ng-annotate.js")); 5 | const dst = src.replace('require("./package.json").version', JSON.stringify(version)); 6 | fs.writeFileSync("es5/ng-annotate.js", dst); 7 | -------------------------------------------------------------------------------- /build/ng-annotate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require("./ng-annotate.js"); 3 | -------------------------------------------------------------------------------- /build/prepare.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd .. 3 | rm -rf build/npm 4 | mkdir build/npm 5 | git archive master -o build/npm/ng-annotate.tar --prefix=ng-annotate/ 6 | cd build/npm 7 | tar xf ng-annotate.tar && rm ng-annotate.tar 8 | cd ng-annotate/build 9 | ./build.sh 10 | # delete build scripts 11 | rm *.sh *.js defs-config.json ng-annotate 12 | # delete .gitignore 13 | rm ../.gitignore 14 | # delete large test artifacts 15 | rm ../tests/angular.js ../build/es5/tests/angular.js 16 | cd ../.. 17 | tar czf ng-annotate.tgz ng-annotate && rm -rf ng-annotate 18 | -------------------------------------------------------------------------------- /defs-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "environments": ["node"], 3 | "disallowVars": true, 4 | "disallowDuplicated": true, 5 | "disallowUnknownReferences": true 6 | } 7 | -------------------------------------------------------------------------------- /generate-sourcemap.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const os = require("os"); 4 | const convertSourceMap = require("convert-source-map"); 5 | const SourceMapConsumer = require("source-map").SourceMapConsumer; 6 | const SourceMapGenerator = require("source-map").SourceMapGenerator; 7 | const stableSort = require("stable"); 8 | 9 | function SourceMapper(src, nodePositions, fragments, inFile, sourceRoot) { 10 | this.generator = new SourceMapGenerator({ sourceRoot: sourceRoot }); 11 | this.src = src; 12 | // stableSort does not mutate input array so no need to copy it 13 | this.nodePositions = stableSort(nodePositions, compareLoc); 14 | this.fragments = stableSort(fragments, function(a, b) { return a.start - b.start }); 15 | this.inFile = inFile; 16 | 17 | this.generator.setSourceContent(this.inFile, src); 18 | } 19 | 20 | SourceMapper.prototype.calculateMappings = function() { 21 | const self = this; 22 | 23 | // These offsets represent the difference in coordinates between a node in the source 24 | // and the corresponding position in the output. 25 | let lineOffset = 0; 26 | let columnOffset = 0; 27 | 28 | // Since the column position resets to zero after each newline, we have to keep track 29 | // of the current line that columnOffset refers to in order to know whether to reset it 30 | let currentLine = 0; 31 | 32 | let frag = 0; 33 | let pos = 0; 34 | 35 | while (pos < self.nodePositions.length) { 36 | while (frag < self.fragments.length && 37 | compareLoc(self.fragments[frag].loc.start, self.nodePositions[pos]) < 1) { 38 | 39 | const fragmentLines = self.fragments[frag].str.split("\n"); 40 | const addedNewlines = fragmentLines.length - 1; 41 | 42 | const replacedLines = self.fragments[frag].loc.end.line - self.fragments[frag].loc.start.line; 43 | const replacedColumns = self.fragments[frag].loc.end.column - self.fragments[frag].loc.start.column; 44 | 45 | // If there were any lines added by the fragment string, the line offset should increase; 46 | // If there were any lines removed by the fragment replacement then the line offset should decrease 47 | lineOffset = lineOffset + addedNewlines - replacedLines; 48 | 49 | // The column position needs to reset after each newline. So if the fragment added any 50 | // newlines then the column offset is the difference between the column of the last line of 51 | // the fragment, and the column of the end of the replaced section of the source. 52 | // Otherwise we increment or decrement the column offset just like how the line offset works. 53 | // Note that "replacedColumns" might be negative in some cases (if the beginning of the source 54 | // was further right than the end due to a newline); the math still works out. 55 | columnOffset = fragmentLines.length > 1 ? 56 | fragmentLines[fragmentLines.length - 1].length - self.fragments[frag].loc.end.column : 57 | columnOffset + self.fragments[frag].str.length - replacedColumns; 58 | 59 | currentLine = self.fragments[frag].loc.end.line; 60 | 61 | // Skip creating mappings for any source nodes that were replaced by this fragment (and are thus 62 | // no longer a part of the output) 63 | while (pos < self.nodePositions.length && 64 | compareLoc(self.fragments[frag].loc.end, self.nodePositions[pos]) > 0) { 65 | ++pos; 66 | } 67 | 68 | ++frag; 69 | } 70 | 71 | if (pos < self.nodePositions.length) { 72 | if (currentLine < self.nodePositions[pos].line) 73 | columnOffset = 0; 74 | self.addMapping(self.nodePositions[pos], { 75 | line: self.nodePositions[pos].line + lineOffset, 76 | column: self.nodePositions[pos].column + columnOffset 77 | }); 78 | ++pos; 79 | } 80 | } 81 | } 82 | 83 | SourceMapper.prototype.addMapping = function(input, output) { 84 | this.generator.addMapping({ 85 | source: this.inFile, 86 | original: input, 87 | generated: output 88 | }); 89 | } 90 | 91 | SourceMapper.prototype.applySourceMap = function (consumer) { 92 | this.generator.applySourceMap(consumer); 93 | } 94 | 95 | SourceMapper.prototype.generate = function () { 96 | return this.generator.toString(); 97 | } 98 | 99 | function compareLoc(a, b) { 100 | return (a.line - b.line) || (a.column - b.column); 101 | } 102 | 103 | module.exports = function generateSourcemap(result, src, nodePositions, fragments, mapOpts) { 104 | const existingMap = convertSourceMap.fromSource(src); 105 | const existingMapObject = existingMap && existingMap.toObject(); 106 | const inFile = (existingMapObject && existingMapObject.file) || mapOpts.inFile || "source.js"; 107 | const sourceRoot = (existingMapObject && existingMapObject.sourceRoot) || mapOpts.sourceRoot; 108 | src = convertSourceMap.removeMapFileComments(src); 109 | 110 | const mapper = new SourceMapper(src, nodePositions, fragments, inFile, sourceRoot); 111 | mapper.calculateMappings(); 112 | 113 | if (mapOpts.inline) { 114 | if (existingMapObject) 115 | mapper.applySourceMap(new SourceMapConsumer(existingMapObject)); 116 | 117 | result.src = convertSourceMap.removeMapFileComments(result.src) + 118 | os.EOL + 119 | convertSourceMap.fromJSON(mapper.generate()).toComment(); 120 | } else { 121 | result.map = mapper.generate(); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /lut.js: -------------------------------------------------------------------------------- 1 | // lut.js 2 | // MIT licensed, see LICENSE file 3 | // Copyright (c) 2013-2016 Olov Lassus 4 | 5 | "use strict"; 6 | 7 | const assert = require("assert"); 8 | const traverse = require("ordered-ast-traverse"); 9 | const is = require("simple-is"); 10 | 11 | module.exports = Lut; 12 | 13 | function Lut(ast, src) { 14 | assert(this instanceof Lut); 15 | 16 | const sparseBegins = new Array(src.length); 17 | const begins = []; 18 | const sparseEnds = new Array(src.length); 19 | const ends = []; 20 | let p = 0; 21 | const t0 = Date.now(); 22 | traverse(ast, {pre: function(node) { 23 | // assert (node.range[0] >= p); 24 | if (node.type === "Program") { 25 | return; 26 | } 27 | p = node.range[0]; 28 | if (!sparseBegins[p]) { 29 | sparseBegins[p] = node; 30 | } 31 | p = node.range[1]; 32 | if (!sparseEnds[p]) { 33 | sparseEnds[p] = node; 34 | } 35 | }}); 36 | for (let i in sparseBegins) { 37 | begins.push(sparseBegins[i]); 38 | } 39 | for (let i in sparseEnds) { 40 | ends.push(sparseEnds[i]); 41 | } 42 | const t1 = Date.now(); 43 | // console.error(t1-t0) 44 | 45 | // begins and ends are compact arrays with nodes, 46 | // sorted on node.range[0/1] (unique) 47 | this.begins = begins; 48 | this.ends = ends; 49 | } 50 | 51 | Lut.prototype.findNodeFromPos = findNodeFromPos; 52 | Lut.prototype.findNodeBeforePos = findNodeBeforePos; 53 | 54 | // binary search lut to find node beginning at pos 55 | // or as close after pos as possible. null if none 56 | function findNodeFromPos(pos) { 57 | const lut = this.begins; 58 | assert(is.finitenumber(pos) && pos >= 0); 59 | 60 | let left = 0; 61 | let right = lut.length - 1; 62 | while (left < right) { 63 | const mid = Math.floor((left + right) / 2); 64 | assert(mid >= 0 && mid < lut.length); 65 | if (pos > lut[mid].range[0]) { 66 | left = mid + 1; 67 | } 68 | else { 69 | right = mid; 70 | } 71 | } 72 | if (left > right) { 73 | assert(last(lut).range[0] < pos); 74 | return null; 75 | } 76 | 77 | const found = left; 78 | const foundPos = lut[found].range[0]; 79 | assert(foundPos >= pos); 80 | if (found >= 1) { 81 | const prevPos = lut[found - 1].range[0]; 82 | assert(prevPos < pos); 83 | } 84 | 85 | return lut[found]; 86 | } 87 | 88 | // binary search lut to find node ending (as in range[1] 89 | // at or before pos. null if none 90 | function findNodeBeforePos(pos) { 91 | const lut = this.ends; 92 | assert(is.finitenumber(pos) && pos >= 0); 93 | 94 | let left = 0; 95 | let right = lut.length - 1; 96 | while (left < right) { 97 | const mid = Math.ceil((left + right) / 2); 98 | assert(mid >= 0 && mid < lut.length); 99 | if (pos < lut[mid].range[1]) { 100 | right = mid - 1; 101 | } 102 | else { 103 | left = mid; 104 | } 105 | } 106 | if (left > right) { 107 | assert(lut[0].range[1] > pos); 108 | return null; 109 | } 110 | 111 | const found = left; 112 | const foundPos = lut[found].range[1]; 113 | if(foundPos > pos) { 114 | return null; 115 | } 116 | if (found <= lut.length - 2) { 117 | const nextPos = lut[found + 1].range[1]; 118 | assert(nextPos > pos); 119 | } 120 | 121 | return lut[found]; 122 | } 123 | 124 | function last(arr) { 125 | return arr[arr.length - 1]; 126 | } 127 | -------------------------------------------------------------------------------- /ng-annotate-harmony: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | rdlkf() { [ -L "$1" ] && (local lk="$(readlink "$1")"; local d="$(dirname "$1")"; cd "$d"; local l="$(rdlkf "$lk")"; ([[ "$l" = /* ]] && echo "$l" || echo "$d/$l")) || echo "$1"; } 3 | DIR="$(dirname "$(rdlkf "$0")")" 4 | /usr/bin/env node --harmony "$DIR/ng-annotate.js" "$@" 5 | -------------------------------------------------------------------------------- /ng-annotate.js: -------------------------------------------------------------------------------- 1 | // ng-annotate.js 2 | // MIT licensed, see LICENSE file 3 | // Copyright (c) 2013-2016 Olov Lassus 4 | 5 | "use strict"; 6 | 7 | const t0 = Date.now(); 8 | const fs = require("fs"); 9 | const fmt = require("simple-fmt"); 10 | const tryor = require("tryor"); 11 | const ngAnnotate = require("./ng-annotate-main"); 12 | const version = require("./package.json").version; 13 | const optimist = require("optimist") 14 | .usage("ng-annotate v" + version + "\n\nUsage: ng-annotate OPTIONS \n\n" + 15 | "provide - instead of to read from stdin\n" + 16 | "use -a and -r together to remove and add (rebuild) annotations in one go") 17 | .options("a", { 18 | alias: "add", 19 | boolean: true, 20 | describe: "add dependency injection annotations where non-existing", 21 | }) 22 | .options("r", { 23 | alias: "remove", 24 | boolean: true, 25 | describe: "remove all existing dependency injection annotations", 26 | }) 27 | .options("o", { 28 | describe: "write output to . output is written to stdout by default", 29 | }) 30 | .options("sourcemap", { 31 | boolean: true, 32 | describe: "generate an inline sourcemap" 33 | }) 34 | .options("sourceroot", { 35 | describe: "set the sourceRoot property of the generated sourcemap" 36 | }) 37 | .options("single_quotes", { 38 | boolean: true, 39 | describe: "use single quotes (') instead of double quotes (\")", 40 | }) 41 | .options("regexp", { 42 | describe: "detect short form myMod.controller(...) iff myMod matches regexp", 43 | }) 44 | .options("rename", { 45 | describe: "rename declarations and annotated references\n" + 46 | "oldname1 newname1 oldname2 newname2 ...", 47 | default: "" 48 | }) 49 | .options("plugin", { 50 | describe: "use plugin with path (experimental)", 51 | }) 52 | .options("enable", { 53 | describe: "enable optional with name", 54 | }) 55 | .options("list", { 56 | describe: "list all optional names", 57 | boolean: true, 58 | }) 59 | .options("stats", { 60 | boolean: true, 61 | describe: "print statistics on stderr (experimental)", 62 | }); 63 | 64 | const argv = optimist.argv; 65 | 66 | function exit(msg) { 67 | if (msg) { 68 | process.stderr.write(msg); 69 | process.stderr.write("\n"); 70 | } 71 | process.exit(-1); 72 | } 73 | 74 | // special-case for --list 75 | if (argv.list) { 76 | const list = ngAnnotate("", {list: true}).list; 77 | if (list.length >= 1) { 78 | process.stdout.write(list.join("\n") + "\n"); 79 | } 80 | process.exit(0); 81 | } 82 | 83 | // validate options 84 | if (argv._.length !== 1) { 85 | optimist.showHelp(); 86 | exit("error: no input file provided"); 87 | } 88 | 89 | if (!argv.add && !argv.remove) { 90 | optimist.showHelp(); 91 | exit("error: missing option --add and/or --remove"); 92 | } 93 | 94 | const filename = argv._.shift(); 95 | 96 | (filename === "-" ? slurpStdin : slurpFile)(runAnnotate); 97 | 98 | 99 | function slurpStdin(cb) { 100 | let buf = ""; 101 | 102 | process.stdin.setEncoding("utf8"); 103 | process.stdin.on("data", function(d) { 104 | buf += d; 105 | }); 106 | process.stdin.on("end", function() { 107 | cb(null, buf); 108 | }); 109 | process.stdin.resume(); 110 | } 111 | 112 | function slurpFile(cb) { 113 | if (!fs.existsSync(filename)) { 114 | cb(new Error(fmt('error: file not found {0}', filename))); 115 | } 116 | 117 | fs.readFile(filename, cb); 118 | } 119 | 120 | function runAnnotate(err, src) { 121 | if (err) { 122 | exit(err.message); 123 | } 124 | 125 | src = String(src); 126 | 127 | const config = tryor(function() { 128 | return JSON.parse(String(fs.readFileSync("ng-annotate-config.json"))); 129 | }, {}); 130 | 131 | if (filename !== "-") { 132 | config.inFile = filename; 133 | } 134 | 135 | ["add", "remove", "o", "regexp", "rename", "single_quotes", "plugin", "enable", "stats"].forEach(function(opt) { 136 | if (opt in argv) { 137 | config[opt] = argv[opt]; 138 | } 139 | }); 140 | 141 | if (argv.sourcemap) { 142 | config.map = { inline: true, sourceRoot: argv.sourceroot }; 143 | if (filename !== "-") { 144 | config.map.inFile = filename; 145 | } 146 | }; 147 | 148 | if (config.enable && !Array.isArray(config.enable)) { 149 | config.enable = [config.enable]; 150 | } 151 | 152 | if (config.plugin) { 153 | if (!Array.isArray(config.plugin)) { 154 | config.plugin = [config.plugin]; 155 | } 156 | config.plugin = config.plugin.map(function(path) { 157 | const absPath = tryor(fs.realpathSync.bind(fs, path), null); 158 | if (!absPath) { 159 | exit(fmt('error: plugin file not found {0}', path)); 160 | } 161 | // the require below may throw an exception on parse-error 162 | try { 163 | return require(absPath); 164 | } catch (e) { 165 | // node will already print file:line and offending line to stderr 166 | exit(fmt("error: couldn't require(\"{0}\")", absPath)); 167 | } 168 | }); 169 | } 170 | 171 | const trimmedRename = config.rename && config.rename.trim(); 172 | if (trimmedRename) { 173 | const flattenRename = trimmedRename.split(" "); 174 | const renameArray = []; 175 | for (let i = 0; i < flattenRename.length; i = i + 2) { 176 | renameArray.push({ 177 | "from": flattenRename[i], 178 | "to": flattenRename[i + 1], 179 | }); 180 | } 181 | config.rename = renameArray; 182 | } else { 183 | config.rename = null; 184 | } 185 | 186 | const run_t0 = Date.now(); 187 | const ret = ngAnnotate(src, config); 188 | const run_t1 = Date.now(); 189 | 190 | if (ret.errors) { 191 | exit(ret.errors.join("\n")); 192 | } 193 | 194 | const stats = ret._stats; 195 | if (config.stats && stats) { 196 | const t1 = Date.now(); 197 | const all = t1 - t0; 198 | const run_parser = stats.parser_parse_t1 - stats.parser_parse_t0; 199 | const all_parser = run_parser + (stats.parser_require_t1 - stats.parser_require_t0); 200 | const nga_run = (run_t1 - run_t0) - run_parser; 201 | const nga_init = all - all_parser - nga_run; 202 | 203 | const pct = function(n) { 204 | return Math.round(100 * n / all); 205 | } 206 | 207 | process.stderr.write(fmt("[{0} ms] parser: {1}, nga init: {2}, nga run: {3}\n", all, all_parser, nga_init, nga_run)); 208 | process.stderr.write(fmt("[%] parser: {0}, nga init: {1}, nga run: {2}\n", pct(all_parser), pct(nga_init), pct(nga_run))); 209 | } 210 | 211 | if (ret.src && config.o) { 212 | try { 213 | fs.writeFileSync(config.o, ret.src); 214 | } catch (e) { 215 | exit(e.message); 216 | } 217 | } else if (ret.src) { 218 | process.stdout.write(ret.src); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /nginject.js: -------------------------------------------------------------------------------- 1 | // nginject.js 2 | // MIT licensed, see LICENSE file 3 | // Copyright (c) 2013-2016 Olov Lassus 4 | 5 | "use strict"; 6 | 7 | const is = require("simple-is"); 8 | 9 | module.exports = { 10 | inspectComments: inspectComments, 11 | inspectNode: inspectNode, 12 | }; 13 | 14 | function inspectNode(node, ctx) { 15 | if (node.type === "CallExpression") { 16 | inspectCallExpression(node, ctx); 17 | } else if (node.type === "FunctionExpression" || node.type === "FunctionDeclaration") { 18 | inspectFunction(node, ctx); 19 | } 20 | } 21 | 22 | function inspectCallExpression(node, ctx) { 23 | const name = node.callee.name; 24 | if (node.callee.type === "Identifier" && (name === "ngInject" || name === "ngNoInject") && node.arguments.length === 1) { 25 | const block = (name === "ngNoInject"); 26 | addSuspect(node.arguments[0], ctx, block); 27 | } 28 | } 29 | 30 | const ngAnnotatePrologueDirectives = ["ngInject", "ngNoInject"]; 31 | 32 | function inspectFunction(node, ctx) { 33 | const str = matchPrologueDirectives(ngAnnotatePrologueDirectives, node); 34 | if (!str) { 35 | return; 36 | } 37 | const block = (str === "ngNoInject"); 38 | 39 | // now add the correct suspect 40 | 41 | // for function declarations, it is always the function declaration node itself 42 | if (node.type === "FunctionDeclaration") { 43 | addSuspect(node, ctx, block); 44 | return; 45 | } 46 | 47 | // node is a function expression below 48 | 49 | // case 1: a function expression which is the rhs of a variable declarator, such as 50 | // var f1 = function(a) { 51 | // "ngInject" 52 | // }; 53 | // in this case we can mark the declarator, same as saying var /*@ngInject*/ f1 = function(a) .. 54 | // or /*@ngInject*/ var f1 = function(a) .. 55 | // f1.$inject = ["a"]; will be added (or rebuilt/removed) 56 | if (node.$parent.type === "VariableDeclarator") { 57 | addSuspect(node.$parent, ctx, block); 58 | return; 59 | } 60 | 61 | // case 2: an anonymous function expression, such as 62 | // g(function(a) { 63 | // "ngInject" 64 | // }); 65 | // 66 | // the suspect is now its parent annotated array (if any), otherwise itself 67 | // there is a risk of false suspects here, in case the parent annotated array has nothing to do 68 | // with annotations. the risk should be very low and hopefully easy to workaround 69 | // 70 | // added/rebuilt/removed => g(["a", function(a) { 71 | // "ngInject" 72 | // }]); 73 | const maybeArrayExpression = node.$parent; 74 | if (ctx.isAnnotatedArray(maybeArrayExpression)) { 75 | addSuspect(maybeArrayExpression, ctx, block); 76 | } else { 77 | addSuspect(node, ctx, block); 78 | } 79 | } 80 | 81 | function matchPrologueDirectives(prologueDirectives, node) { 82 | const body = node.body.body; 83 | 84 | let found = null; 85 | for (let i = 0; i < body.length; i++) { 86 | if (body[i].type !== "ExpressionStatement") { 87 | break; 88 | } 89 | 90 | const expr = body[i].expression; 91 | const isStringLiteral = (expr.type === "Literal" && typeof expr.value === "string"); 92 | if (!isStringLiteral) { 93 | break; 94 | } 95 | 96 | if (prologueDirectives.indexOf(expr.value) >= 0) { 97 | found = expr.value; 98 | break; 99 | } 100 | } 101 | 102 | return found; 103 | } 104 | 105 | function inspectComments(ctx) { 106 | const comments = ctx.comments; 107 | for (let i = 0; i < comments.length; i++) { 108 | const comment = comments[i]; 109 | const yesPos = comment.value.indexOf("@ngInject"); 110 | const noPos = (yesPos === -1 ? comment.value.indexOf("@ngNoInject") : -1); 111 | if (yesPos === -1 && noPos === -1) { 112 | continue; 113 | } 114 | 115 | const target = ctx.lut.findNodeFromPos(comment.range[1]); 116 | if (!target) { 117 | continue; 118 | } 119 | 120 | addSuspect(target, ctx, noPos >= 0); 121 | } 122 | } 123 | 124 | function isStringArray(node) { 125 | if (node.type !== "ArrayExpression") { 126 | return false; 127 | } 128 | return node.elements.length >= 1 && node.elements.every(function(n) { 129 | return n.type === "Literal" && is.string(n.value); 130 | }); 131 | } 132 | 133 | function findNextStatement(node) { 134 | const body = node.$parent.body; 135 | for (let i = 0; i < body.length; i++) { 136 | if (body[i] === node) { 137 | return body[i + 1] || null; 138 | } 139 | } 140 | return null; 141 | } 142 | 143 | function addSuspect(target, ctx, block) { 144 | if (target.type === "ExpressionStatement" && target.expression.type === "AssignmentExpression" && isStringArray(target.expression.right)) { 145 | // /*@ngInject*/ 146 | // FooBar.$inject = ["$a", "$b"]; 147 | // function FooBar($a, $b) {} 148 | const adjustedTarget = findNextStatement(target); 149 | if (adjustedTarget) { 150 | return addSuspect(adjustedTarget, ctx, block); 151 | } 152 | } 153 | 154 | if (target.type === "ObjectExpression") { 155 | // /*@ngInject*/ {f1: function(a), .., {f2: function(b)}} 156 | addObjectExpression(target, ctx); 157 | } else if (target.type === "AssignmentExpression" && target.right.type === "ObjectExpression") { 158 | // /*@ngInject*/ f(x.y = {f1: function(a), .., {f2: function(b)}}) 159 | addObjectExpression(target.right, ctx); 160 | } else if (target.type === "ExpressionStatement" && target.expression.type === "AssignmentExpression" && target.expression.right.type === "ObjectExpression") { 161 | // /*@ngInject*/ x.y = {f1: function(a), .., {f2: function(b)}} 162 | addObjectExpression(target.expression.right, ctx); 163 | } else if (target.type === "VariableDeclaration" && target.declarations.length === 1 && target.declarations[0].init && target.declarations[0].init.type === "ObjectExpression") { 164 | // /*@ngInject*/ var x = {f1: function(a), .., {f2: function(b)}} 165 | addObjectExpression(target.declarations[0].init, ctx); 166 | } else if (target.type === "Property") { 167 | // {/*@ngInject*/ justthisone: function(a), ..} 168 | target.value.$limitToMethodName = "*never*"; 169 | addOrBlock(target.value, ctx); 170 | } else { 171 | // /*@ngInject*/ function(a) {} 172 | target.$limitToMethodName = "*never*"; 173 | addOrBlock(target, ctx); 174 | } 175 | 176 | 177 | function addObjectExpression(node, ctx) { 178 | nestedObjectValues(node).forEach(function(n) { 179 | n.$limitToMethodName = "*never*"; 180 | addOrBlock(n, ctx); 181 | }); 182 | } 183 | 184 | function addOrBlock(node, ctx) { 185 | if (block) { 186 | ctx.blocked.push(node); 187 | } else { 188 | ctx.addModuleContextIndependentSuspect(node, ctx) 189 | } 190 | } 191 | } 192 | 193 | function nestedObjectValues(node, res) { 194 | res = res || []; 195 | 196 | node.properties.forEach(function(prop) { 197 | const v = prop.value; 198 | if (is.someof(v.type, ["FunctionExpression", "ArrayExpression"])) { 199 | res.push(v); 200 | } else if (v.type === "ObjectExpression") { 201 | nestedObjectValues(v, res); 202 | } 203 | }); 204 | 205 | return res; 206 | } 207 | -------------------------------------------------------------------------------- /optionals/angular-dashboard-framework.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | let ctx = null; 4 | module.exports = { 5 | init: function(_ctx) { 6 | ctx = _ctx; 7 | }, 8 | 9 | match: function(node) { 10 | // dashboardProvider.widget("name", { 11 | // ... 12 | // controller: function($scope) {}, 13 | // resolve: {f: function($scope) {}, ..} 14 | // }) 15 | 16 | const callee = node.callee; 17 | if (!callee) { 18 | return false; 19 | } 20 | 21 | const obj = callee.object; 22 | if (!obj) { 23 | return false; 24 | } 25 | 26 | // identifier or expression 27 | if (!(obj.$chained === 1 || (obj.type === "Identifier" && obj.name === "dashboardProvider"))) { 28 | return false; 29 | } 30 | 31 | node.$chained = 1; 32 | 33 | const method = callee.property; // identifier 34 | if (method.name !== "widget") { 35 | return false; 36 | } 37 | 38 | const args = node.arguments; 39 | if (args.length !== 2) { 40 | return false; 41 | } 42 | 43 | const configArg = ctx.last(args); 44 | if (configArg.type !== "ObjectExpression") { 45 | return false; 46 | } 47 | 48 | const props = configArg.properties; 49 | const res = [ 50 | ctx.matchProp("controller", props) 51 | ]; 52 | // {resolve: ..} 53 | res.push.apply(res, ctx.matchResolve(props)); 54 | 55 | // edit: {controller: function(), resolve: {}, apply: function()} 56 | const edit = ctx.matchProp('edit', props); 57 | if (edit && edit.type === "ObjectExpression") { 58 | const editProps = edit.properties; 59 | res.push(ctx.matchProp('controller', editProps)); 60 | res.push(ctx.matchProp('apply', editProps)); 61 | res.push.apply(res, ctx.matchResolve(editProps)); 62 | } 63 | 64 | const filteredRes = res.filter(Boolean); 65 | return (filteredRes.length === 0 ? false : filteredRes); 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-annotate", 3 | "version": "1.2.2", 4 | "description": "add, remove and rebuild angularjs dependency injection annotations", 5 | "main": "build/es5/ng-annotate-main.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/olov/ng-annotate.git" 9 | }, 10 | "dependencies": { 11 | "acorn": "~2.6.4", 12 | "alter": "~0.2.0", 13 | "convert-source-map": "~1.1.2", 14 | "optimist": "~0.6.1", 15 | "ordered-ast-traverse": "~1.1.1", 16 | "simple-fmt": "~0.1.0", 17 | "simple-is": "~0.2.0", 18 | "source-map": "~0.5.3", 19 | "stable": "~0.1.5", 20 | "stringmap": "~0.2.2", 21 | "stringset": "~0.2.1", 22 | "tryor": "~0.1.2" 23 | }, 24 | "devDependencies": { 25 | "coffee-script": "~1.10.0", 26 | "defs": "~1.1.1", 27 | "diff": "~2.2.1", 28 | "find-line-column": "~0.5.2" 29 | }, 30 | "keywords": [ 31 | "angular", 32 | "angularjs", 33 | "di", 34 | "dependency", 35 | "injection", 36 | "annotate", 37 | "annotation", 38 | "annotations", 39 | "transformation" 40 | ], 41 | "scripts": { 42 | "test": "node --harmony run-tests" 43 | }, 44 | "bin": "./build/es5/ng-annotate", 45 | "author": "Olov Lassus ", 46 | "license": "MIT" 47 | } 48 | -------------------------------------------------------------------------------- /plugin-example.js: -------------------------------------------------------------------------------- 1 | var ctx; 2 | module.exports = { 3 | init: function(_ctx) { 4 | console.error("plugin init called, got ctx with keys " + Object.keys(_ctx)); 5 | 6 | // ctx contains a bunch of helpers and data 7 | // stash it away so you can use it inside match 8 | ctx = _ctx; 9 | 10 | // if you want to setup position triggers now, checkout nginject-comments.js 11 | }, 12 | match: function(node) { 13 | console.error("plugin match called, node with type " + node.type); 14 | 15 | // if you think you have a match, return the found target node 16 | // (may or may not be the passed in argument node) 17 | // you may also return an array of target nodes 18 | 19 | // ng-annotate will then execute replaceRemoveOrInsertArrayForTarget 20 | // on every target, i.e. it may remove an array (if --remove) and it may 21 | // add an array (if --add) 22 | 23 | // please consider filing an issue if you need to workaround a defect or 24 | // an obviously missing feature in ng-annotate. we'll try to fix it! 25 | 26 | // you know about /* @ngInject */, don't you? (you may not need a plugin) 27 | 28 | // please consider sending a pull request if your plugin is of general use 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /pos-to-linecolumn.js: -------------------------------------------------------------------------------- 1 | // pos-to-linecolumn.js 2 | // MIT licensed, see LICENSE file 3 | // Copyright (c) 2014-2016 Olov Lassus 4 | 5 | "use strict"; 6 | 7 | const assert = require("assert"); 8 | 9 | module.exports = PosToLineColumn; 10 | 11 | function PosToLineColumn(str) { 12 | if (!(this instanceof PosToLineColumn)) { 13 | throw new Error("PosToLineColumn requires new"); 14 | } 15 | str = String(str); 16 | 17 | const newlines = []; 18 | let pos = -1; 19 | while ((pos = str.indexOf("\n", pos + 1)) >= 0) { 20 | newlines.push(pos); 21 | } 22 | 23 | let line = 1; 24 | let column = 0; 25 | const columns = []; 26 | const lines = []; 27 | let i; 28 | let j = 0; 29 | for (i = 0; i < str.length; i++) { 30 | columns[i] = column; 31 | lines[i] = line; 32 | 33 | if (i === newlines[j]) { 34 | ++j; 35 | ++line; 36 | column = 0; 37 | } else { 38 | ++column; 39 | } 40 | } 41 | 42 | // add extra entry to support pos === str.length 43 | columns[i] = column; 44 | lines[i] = line; 45 | 46 | this.len = str.length; 47 | this.columns = columns; 48 | this.lines = lines; 49 | } 50 | 51 | PosToLineColumn.prototype.toLine = function(pos) { 52 | assert(pos >= 0 && pos <= this.len); 53 | return this.lines[pos]; 54 | }; 55 | 56 | PosToLineColumn.prototype.toColumn = function(pos) { 57 | assert(pos >= 0 && pos <= this.len); 58 | return this.columns[pos]; 59 | }; 60 | 61 | PosToLineColumn.prototype.toLineColumn = function(pos) { 62 | return { 63 | line: this.toLine(pos), 64 | column: this.toColumn(pos), 65 | }; 66 | }; 67 | 68 | 69 | /* 70 | const tst = "asdf\n" + 71 | "abc\n" + 72 | "d\n" + 73 | "\n\n" + 74 | "efghi a\r\n" + 75 | "x"; 76 | const instance = new PosToLineColumn(tst); 77 | console.dir(instance.toLineColumn(0)); 78 | console.dir(instance.toLineColumn(tst.length)); 79 | */ 80 | -------------------------------------------------------------------------------- /run-tests.js: -------------------------------------------------------------------------------- 1 | // run-tests.js 2 | // MIT licensed, see LICENSE file 3 | // Copyright (c) 2013-2016 Olov Lassus 4 | 5 | "use strict"; 6 | 7 | const ngAnnotate = require("./ng-annotate-main"); 8 | const fs = require("fs"); 9 | const os = require("os"); 10 | const path = require("path"); 11 | const diff = require("diff"); 12 | const findLineColumn = require("find-line-column"); 13 | const fmt = require("simple-fmt"); 14 | const SourceMapConsumer = require("source-map").SourceMapConsumer; 15 | const coffee = require("coffee-script"); 16 | const convertSourceMap = require("convert-source-map"); 17 | 18 | // optionals 19 | const ngAnnotateAdfPlugin = require("./optionals/angular-dashboard-framework.js"); 20 | 21 | function slurp(filename) { 22 | return String(fs.readFileSync(filename)); 23 | } 24 | 25 | function time(str, fn) { 26 | const t0 = Date.now(); 27 | fn(); 28 | const t1 = Date.now(); 29 | console.log(fmt(str, t1 - t0)); 30 | } 31 | 32 | function test(correct, got, name) { 33 | if (got !== correct) { 34 | const patch = diff.createPatch(name, correct, got); 35 | process.stderr.write(patch); 36 | process.exit(-1); 37 | } 38 | } 39 | 40 | const renameOptions = [ 41 | {"from": "$a", "to": "$aRenamed"}, 42 | {"from": "$b", "to": "$bRenamed"}, 43 | {"from": "$c", "to": "$cRenamed"}, 44 | {"from": "$d", "to": "$dRenamed"}, 45 | {"from": "$e", "to": "$eRenamed"}, 46 | {"from": "$f", "to": "$fRenamed"}, 47 | {"from": "$g", "to": "$gRenamed"}, 48 | {"from": "$h", "to": "$hRenamed"}, 49 | {"from": "$i", "to": "$iRenamed"}, 50 | ]; 51 | 52 | function testSourcemap(original, got, sourcemap) { 53 | const smc = new SourceMapConsumer(sourcemap); 54 | 55 | function stringRegExp(commentText) { 56 | return new RegExp("\"" + commentText + "\""); 57 | } 58 | 59 | function functionRegExp(functionName) { 60 | return new RegExp("(function)?\\(" + functionName + "_param1, " + functionName + "_param2\\)") 61 | } 62 | 63 | function testMapping(needle) { 64 | const gotResult = needle.exec(got); 65 | if (gotResult == null) { 66 | process.stderr.write(fmt("Couldn't find {0} in output source", needle)); 67 | process.exit(-1); 68 | } 69 | 70 | const expectedResult = needle.exec(original); 71 | if (expectedResult == null) { 72 | process.stderr.write(fmt("Couldn't find {0} in expected source", needle)); 73 | process.exit(-1); 74 | } 75 | 76 | const gotPosition = findLineColumn(got, gotResult.index); 77 | const originalPosition = smc.originalPositionFor({ line: gotPosition.line, column: gotPosition.col }); 78 | const expectedPosition = findLineColumn(original, expectedResult.index); 79 | 80 | if (originalPosition.line !== expectedPosition.line || originalPosition.column !== expectedPosition.col) { 81 | process.stderr.write(fmt("Sourcemap mapping error for {0}. Expected: ({1},{2}) => ({3},{4}). Got: ({5},{6}) => ({3},{4}).", 82 | needle, 83 | expectedPosition.line, expectedPosition.col, 84 | gotPosition.line, gotPosition.col, 85 | originalPosition.line, originalPosition.column)); 86 | process.exit(-1); 87 | } 88 | } 89 | 90 | testMapping(stringRegExp("before")); 91 | for (let i = 1; i <= 4; i++) { 92 | testMapping(functionRegExp("ctrl" + i)); 93 | testMapping(stringRegExp("ctrl" + i + " body")); 94 | } 95 | testMapping(stringRegExp("after")); 96 | } 97 | 98 | function run(ngAnnotate) { 99 | const original = slurp("tests/original.js"); 100 | 101 | console.log("testing adding annotations"); 102 | const annotated = ngAnnotate(original, {add: true}).src; 103 | test(slurp("tests/with_annotations.js"), annotated, "with_annotations.js"); 104 | 105 | const rename = slurp("tests/rename.js"); 106 | 107 | console.log("testing adding annotations and renaming"); 108 | const annotatedRenamed = ngAnnotate(rename, { 109 | add: true, 110 | rename: renameOptions, 111 | }).src; 112 | test(slurp("tests/rename.annotated.js"), annotatedRenamed, "rename.annotated.js"); 113 | 114 | console.log("testing removing annotations"); 115 | test(original, ngAnnotate(annotated, {remove: true}).src, "original.js"); 116 | 117 | console.log("testing adding annotations twice"); 118 | test(annotated, ngAnnotate(annotated, {add: true}).src, "with_annotations.js"); 119 | 120 | console.log("testing rebuilding annotations"); 121 | test(annotated, ngAnnotate(annotated, {add: true, remove: true}).src, "with_annotations.js"); 122 | 123 | console.log("testing adding existing $inject annotations (no change)"); 124 | test(slurp("tests/has_inject.js"), ngAnnotate(slurp("tests/has_inject.js"), {add: true}).src); 125 | 126 | console.log("testing removing existing $inject annotations"); 127 | test(slurp("tests/has_inject_removed.js"), ngAnnotate(slurp("tests/has_inject.js"), {remove: true}).src); 128 | 129 | console.log("testing sourcemaps"); 130 | const originalSourcemaps = slurp("tests/sourcemaps.coffee"); 131 | const compiledSourcemaps = coffee.compile(originalSourcemaps, { sourceFiles: ["sourcemaps.coffee"], generatedFile: "sourcemaps.js", sourceMap: true }); 132 | const annotatedSourcemaps = ngAnnotate(compiledSourcemaps.js, {remove: true, add: true, map: { sourceRoot: "/source/root/dir" }}); 133 | test(slurp("tests/sourcemaps.annotated.js"), annotatedSourcemaps.src, "sourcemaps.annotated.js"); 134 | testSourcemap(compiledSourcemaps.js, annotatedSourcemaps.src, annotatedSourcemaps.map, "sourcemaps.annotated.js.map"); 135 | 136 | console.log("testing sourcemap combination"); 137 | const inlinedCompiledSourcemaps = compiledSourcemaps.js + 138 | os.EOL + 139 | convertSourceMap.fromJSON(compiledSourcemaps.v3SourceMap).toComment(); 140 | const combinedSourcemaps = ngAnnotate(inlinedCompiledSourcemaps, {remove: true, add: true, map: { inline: true, inFile: "sourcemaps.js", sourceRoot: "/source/root/dir" }}); 141 | const combinedSourcemapsSrc = convertSourceMap.removeMapFileComments(combinedSourcemaps.src); 142 | const combinedSourcemapsMap = convertSourceMap.fromSource(combinedSourcemaps.src).toJSON(); 143 | testSourcemap(originalSourcemaps, combinedSourcemapsSrc, combinedSourcemapsMap, "sourcemaps.annotated.js.map"); 144 | 145 | const ngminOriginal = slurp("tests/ngmin-tests/ngmin_original.js"); 146 | 147 | console.log("testing adding annotations (imported tests)"); 148 | const ngminAnnotated = ngAnnotate(ngminOriginal, {add: true, regexp: "^myMod"}).src; 149 | test(slurp("tests/ngmin-tests/ngmin_with_annotations.js"), ngminAnnotated, "ngmin_with_annotations.js"); 150 | 151 | console.log("testing removing annotations (imported tests)"); 152 | test(ngminOriginal, ngAnnotate(ngminAnnotated, {remove: true, regexp: "^myMod"}).src, "ngmin_original.js"); 153 | 154 | // TODO generic test-runner code for finding and testing all optionals automatically 155 | // optionals angular-dashboard-framework adding annotations 156 | console.log("testing optionals/angular-dashboard-framework.js (adding annotations)"); 157 | const adf = slurp("tests/optionals/angular-dashboard-framework.js"); 158 | const adfAnnotated = ngAnnotate(adf, {add: true, plugin: [ngAnnotateAdfPlugin]}).src; 159 | test(slurp("tests/optionals/angular-dashboard-framework.annotated.js"), adfAnnotated, "optionals/angular-dashboard-framework.annotated.js"); 160 | 161 | // optionals angular-dashboard-framework removing annotations 162 | console.log("testing optionals/angular-dashboard-framework.js (removing annotations)"); 163 | test(adf, ngAnnotate(adfAnnotated, {remove: true, plugin: [ngAnnotateAdfPlugin]}).src, "optionals/angular-dashboard-framework.js"); 164 | 165 | 166 | if (fs.existsSync("package.json")) { 167 | console.log("testing package.json") 168 | try { 169 | const json = JSON.parse(slurp("package.json")); 170 | const substr = JSON.stringify({ 171 | dependencies: json.dependencies, 172 | devDependencies: json.devDependencies, 173 | }, null, 4); 174 | if (/\^/g.test(substr)) { 175 | console.error("package.json error: shouldn't use the ^ operator"); 176 | console.error(substr); 177 | process.exit(-1); 178 | } 179 | } catch (e) { 180 | console.error("package.json error: invalid json"); 181 | process.exit(-1); 182 | } 183 | } 184 | 185 | if (fs.existsSync("tests/angular.js")) { 186 | console.log("testing performance"); 187 | const ng1 = String(fs.readFileSync("tests/angular.js")); 188 | const ng5 = ng1 + ng1 + ng1 + ng1 + ng1; 189 | 190 | time(" ng1 processed in {0} ms", function() { ngAnnotate(ng1, {add: true}) }); 191 | time(" ng1 processed with sourcemaps in {0} ms", function() { ngAnnotate(ng1, {add: true, map: true}) }); 192 | //time(" ng5 processed in {0} ms", function() { ngAnnotate(ng5, {add: true}) }); 193 | //time(" ng5 processed with sourcemaps in {0} ms", function() { ngAnnotate(ng5, {add: true, map: true}) }); 194 | } 195 | 196 | console.log("all ok"); 197 | } 198 | 199 | run(ngAnnotate); 200 | -------------------------------------------------------------------------------- /scope.js: -------------------------------------------------------------------------------- 1 | // scope.js 2 | // MIT licensed, see LICENSE file 3 | // Copyright (c) 2013-2016 Olov Lassus 4 | 5 | "use strict"; 6 | 7 | const assert = require("assert"); 8 | const stringmap = require("stringmap"); 9 | const stringset = require("stringset"); 10 | const is = require("simple-is"); 11 | const fmt = require("simple-fmt"); 12 | 13 | function Scope(args) { 14 | assert(is.someof(args.kind, ["hoist", "block", "catch-block"])); 15 | assert(is.object(args.node)); 16 | assert(args.parent === null || is.object(args.parent)); 17 | 18 | // kind === "hoist": function scopes, program scope, injected globals 19 | // kind === "block": ES6 block scopes 20 | // kind === "catch-block": catch block scopes 21 | this.kind = args.kind; 22 | 23 | // the AST node the block corresponds to 24 | this.node = args.node; 25 | 26 | // parent scope 27 | this.parent = args.parent; 28 | 29 | // children scopes for easier traversal (populated internally) 30 | this.children = []; 31 | 32 | // scope declarations. decls[variable_name] = { 33 | // kind: "fun" for functions, 34 | // "param" for function parameters, 35 | // "caught" for catch parameter 36 | // "var", 37 | // "const", 38 | // "let" 39 | // node: the AST node the declaration corresponds to 40 | // from: source code index from which it is visible at earliest 41 | // (only stored for "const", "let" [and "var"] nodes) 42 | // } 43 | this.decls = stringmap(); 44 | 45 | // names of all variables declared outside this hoist scope but 46 | // referenced in this scope (immediately or in child). 47 | // only stored on hoist scopes for efficiency 48 | // (because we currently generate lots of empty block scopes) 49 | this.propagates = (this.kind === "hoist" ? stringset() : null); 50 | 51 | // scopes register themselves with their parents for easier traversal 52 | if (this.parent) { 53 | this.parent.children.push(this); 54 | } 55 | } 56 | 57 | Scope.prototype.print = function(indent) { 58 | indent = indent || 0; 59 | const scope = this; 60 | const names = this.decls.keys().map(function(name) { 61 | return fmt("{0} [{1}]", name, scope.decls.get(name).kind); 62 | }).join(", "); 63 | const propagates = this.propagates ? this.propagates.items().join(", ") : ""; 64 | console.log(fmt("{0}{1}: {2}. propagates: {3}", fmt.repeat(" ", indent), this.node.type, names, propagates)); 65 | this.children.forEach(function(c) { 66 | c.print(indent + 2); 67 | }); 68 | }; 69 | 70 | Scope.prototype.add = function(name, kind, node, referableFromPos) { 71 | assert(is.someof(kind, ["fun", "param", "var", "caught", "const", "let"])); 72 | 73 | function isConstLet(kind) { 74 | return is.someof(kind, ["const", "let"]); 75 | } 76 | 77 | let scope = this; 78 | 79 | // search nearest hoist-scope for fun, param and var's 80 | // const, let and caught variables go directly in the scope (which may be hoist, block or catch-block) 81 | if (is.someof(kind, ["fun", "param", "var"])) { 82 | while (scope.kind !== "hoist") { 83 | // if (scope.decls.has(name) && isConstLet(scope.decls.get(name).kind)) { // could be caught 84 | // return error(getline(node), "{0} is already declared", name); 85 | // } 86 | scope = scope.parent; 87 | } 88 | } 89 | // name exists in scope and either new or existing kind is const|let => error 90 | // if (scope.decls.has(name) && (isConstLet(scope.decls.get(name).kind) || isConstLet(kind))) { 91 | // return error(getline(node), "{0} is already declared", name); 92 | // } 93 | 94 | const declaration = { 95 | kind: kind, 96 | node: node, 97 | }; 98 | if (referableFromPos) { 99 | assert(is.someof(kind, ["var", "const", "let"])); 100 | declaration.from = referableFromPos; 101 | } 102 | scope.decls.set(name, declaration); 103 | }; 104 | 105 | Scope.prototype.getKind = function(name) { 106 | assert(is.string(name)); 107 | const decl = this.decls.get(name); 108 | return decl ? decl.kind : null; 109 | }; 110 | 111 | Scope.prototype.getNode = function(name) { 112 | assert(is.string(name)); 113 | const decl = this.decls.get(name); 114 | return decl ? decl.node : null; 115 | }; 116 | 117 | Scope.prototype.getFromPos = function(name) { 118 | assert(is.string(name)); 119 | const decl = this.decls.get(name); 120 | return decl ? decl.from : null; 121 | }; 122 | 123 | Scope.prototype.hasOwn = function(name) { 124 | return this.decls.has(name); 125 | }; 126 | 127 | Scope.prototype.remove = function(name) { 128 | return this.decls.remove(name); 129 | }; 130 | 131 | Scope.prototype.doesPropagate = function(name) { 132 | return this.propagates.has(name); 133 | }; 134 | 135 | Scope.prototype.markPropagates = function(name) { 136 | this.propagates.add(name); 137 | }; 138 | 139 | Scope.prototype.closestHoistScope = function() { 140 | let scope = this; 141 | while (scope.kind !== "hoist") { 142 | scope = scope.parent; 143 | } 144 | return scope; 145 | }; 146 | 147 | Scope.prototype.lookup = function(name) { 148 | for (let scope = this; scope; scope = scope.parent) { 149 | if (scope.decls.has(name)) { 150 | return scope; 151 | } else if (scope.kind === "hoist") { 152 | scope.propagates.add(name); 153 | } 154 | } 155 | return null; 156 | }; 157 | 158 | module.exports = Scope; 159 | -------------------------------------------------------------------------------- /scopetools.js: -------------------------------------------------------------------------------- 1 | // scopetools.js 2 | // MIT licensed, see LICENSE file 3 | // Copyright (c) 2013-2016 Olov Lassus 4 | 5 | "use strict"; 6 | 7 | const assert = require("assert"); 8 | const traverse = require("ordered-ast-traverse"); 9 | const Scope = require("./scope"); 10 | const is = require("simple-is"); 11 | 12 | module.exports = { 13 | setupScopeAndReferences: setupScopeAndReferences, 14 | isReference: isReference, 15 | }; 16 | 17 | function setupScopeAndReferences(root) { 18 | traverse(root, {pre: createScopes}); 19 | createTopScope(root.$scope); 20 | } 21 | 22 | function createScopes(node, parent) { 23 | node.$parent = parent; 24 | node.$scope = parent ? parent.$scope : null; // may be overridden 25 | 26 | if (isNonFunctionBlock(node, parent)) { 27 | // A block node is a scope unless parent is a function 28 | node.$scope = new Scope({ 29 | kind: "block", 30 | node: node, 31 | parent: parent.$scope, 32 | }); 33 | 34 | } else if (node.type === "VariableDeclaration") { 35 | // Variable declarations names goes in current scope 36 | node.declarations.forEach(function(declarator) { 37 | const name = declarator.id.name; 38 | node.$scope.add(name, node.kind, declarator.id, declarator.range[1]); 39 | }); 40 | 41 | } else if (isFunction(node)) { 42 | // Function is a scope, with params in it 43 | // There's no block-scope under it 44 | 45 | node.$scope = new Scope({ 46 | kind: "hoist", 47 | node: node, 48 | parent: parent.$scope, 49 | }); 50 | 51 | // function has a name 52 | if (node.id) { 53 | if (node.type === "FunctionDeclaration") { 54 | // Function name goes in parent scope for declared functions 55 | parent.$scope.add(node.id.name, "fun", node.id, null); 56 | } else if (node.type === "FunctionExpression") { 57 | // Function name goes in function's scope for named function expressions 58 | node.$scope.add(node.id.name, "fun", node.id, null); 59 | } else { 60 | assert(false); 61 | } 62 | } 63 | 64 | node.params.forEach(function(param) { 65 | node.$scope.add(param.name, "param", param, null); 66 | }); 67 | 68 | } else if (isForWithConstLet(node) || isForInOfWithConstLet(node)) { 69 | // For(In/Of) loop with const|let declaration is a scope, with declaration in it 70 | // There may be a block-scope under it 71 | node.$scope = new Scope({ 72 | kind: "block", 73 | node: node, 74 | parent: parent.$scope, 75 | }); 76 | 77 | } else if (node.type === "CatchClause") { 78 | const identifier = node.param; 79 | 80 | node.$scope = new Scope({ 81 | kind: "catch-block", 82 | node: node, 83 | parent: parent.$scope, 84 | }); 85 | node.$scope.add(identifier.name, "caught", identifier, null); 86 | 87 | // All hoist-scope keeps track of which variables that are propagated through, 88 | // i.e. an reference inside the scope points to a declaration outside the scope. 89 | // This is used to mark "taint" the name since adding a new variable in the scope, 90 | // with a propagated name, would change the meaning of the existing references. 91 | // 92 | // catch(e) is special because even though e is a variable in its own scope, 93 | // we want to make sure that catch(e){let e} is never transformed to 94 | // catch(e){var e} (but rather var e$0). For that reason we taint the use of e 95 | // in the closest hoist-scope, i.e. where var e$0 belongs. 96 | node.$scope.closestHoistScope().markPropagates(identifier.name); 97 | 98 | } else if (node.type === "Program") { 99 | // Top-level program is a scope 100 | // There's no block-scope under it 101 | node.$scope = new Scope({ 102 | kind: "hoist", 103 | node: node, 104 | parent: null, 105 | }); 106 | } 107 | } 108 | 109 | function createTopScope(programScope) { 110 | function inject(obj) { 111 | for (let name in obj) { 112 | const writeable = obj[name]; 113 | const kind = (writeable ? "var" : "const"); 114 | if (topScope.hasOwn(name)) { 115 | topScope.remove(name); 116 | } 117 | topScope.add(name, kind, {loc: {start: {line: -1}}}, -1); 118 | } 119 | } 120 | 121 | const topScope = new Scope({ 122 | kind: "hoist", 123 | node: {}, 124 | parent: null, 125 | }); 126 | 127 | const complementary = { 128 | undefined: false, 129 | Infinity: false, 130 | console: false, 131 | }; 132 | 133 | inject(complementary); 134 | // inject(jshint_vars.reservedVars); 135 | // inject(jshint_vars.ecmaIdentifiers); 136 | 137 | // link it in 138 | programScope.parent = topScope; 139 | topScope.children.push(programScope); 140 | 141 | return topScope; 142 | } 143 | 144 | function isConstLet(kind) { 145 | return kind === "const" || kind === "let"; 146 | } 147 | 148 | function isNonFunctionBlock(node, parent) { 149 | return node.type === "BlockStatement" && parent.type !== "FunctionDeclaration" && parent.type !== "FunctionExpression"; 150 | } 151 | 152 | function isForWithConstLet(node) { 153 | return node.type === "ForStatement" && node.init && node.init.type === "VariableDeclaration" && isConstLet(node.init.kind); 154 | } 155 | 156 | function isForInOfWithConstLet(node) { 157 | return isForInOf(node) && node.left.type === "VariableDeclaration" && isConstLet(node.left.kind); 158 | } 159 | 160 | function isForInOf(node) { 161 | return node.type === "ForInStatement" || node.type === "ForOfStatement"; 162 | } 163 | 164 | function isFunction(node) { 165 | return node.type === "FunctionDeclaration" || node.type === "FunctionExpression"; 166 | } 167 | 168 | function isReference(node) { 169 | const parent = node.$parent; 170 | return node.$refToScope || 171 | node.type === "Identifier" && 172 | !(parent.type === "VariableDeclarator" && parent.id === node) && // var|let|const $ 173 | !(parent.type === "MemberExpression" && parent.computed === false && parent.property === node) && // obj.$ 174 | !(parent.type === "Property" && parent.key === node) && // {$: ...} 175 | !(parent.type === "LabeledStatement" && parent.label === node) && // $: ... 176 | !(parent.type === "CatchClause" && parent.param === node) && // catch($) 177 | !(isFunction(parent) && parent.id === node) && // function $(.. 178 | !(isFunction(parent) && is.someof(node, parent.params)) && // function f($).. 179 | true; 180 | } 181 | -------------------------------------------------------------------------------- /tests/has_inject.js: -------------------------------------------------------------------------------- 1 | Ctrl1.$inject = ["serviceName"]; 2 | // @ngInject 3 | // already has .$inject array (before function definition) 4 | function Ctrl1(a) { 5 | } 6 | 7 | // @ngInject 8 | // already has .$inject array (after function definition) 9 | function Ctrl2(a) { 10 | } 11 | Ctrl2.$inject = ["serviceName"]; 12 | 13 | function outer() { 14 | MyCtrl["$inject"] = ["asdf"]; 15 | return { 16 | controller: MyCtrl, 17 | }; 18 | 19 | // @ngInject 20 | function MyCtrl(a) { 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/has_inject_removed.js: -------------------------------------------------------------------------------- 1 | 2 | // @ngInject 3 | // already has .$inject array (before function definition) 4 | function Ctrl1(a) { 5 | } 6 | 7 | // @ngInject 8 | // already has .$inject array (after function definition) 9 | function Ctrl2(a) { 10 | } 11 | 12 | function outer() { 13 | return { 14 | controller: MyCtrl, 15 | }; 16 | 17 | // @ngInject 18 | function MyCtrl(a) { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/ngmin-tests/ngmin-issues.txt: -------------------------------------------------------------------------------- 1 | list updated 2014-06-12 2 | 3 | the lists below are correct to my knowledge, if not please let me know! 4 | 5 | ngmin issues that just works in ng-annotate: 6 | -------------------------------------------- 7 | #99 Fails on certain regex string. 8 | #98 Issue with routeProvider when resolve option 9 | #96 Make it works with Browserify module structure 10 | #91 ngmin 30x is slower than ngAnnotate 11 | #89 Support for javascript enclosures? 12 | #87 Not working with resolve in ngRoute. 13 | #85 ngmin is changing things it shouldn't 14 | #83 Support minification of modal controller 15 | #78 module.run not being annotated 16 | #77 Still does not annotate directive.controller 17 | - second example needs an explicit /* @ngInject */ 18 | #73 Support minification of ngAnimate enhancement 19 | #70 The future of ngmin 20 | - ng-annotate is actively maintained 21 | #64 minifications fails 22 | #63 Files with components that containt angular components both minification ready and not ready 23 | #61 Add plug-able annotation system enhancement 24 | #59 Support for http interceptors 25 | #57 Injector returned by angular.bootstrap is not treated 26 | #56 add test to be sure that .decorator function is properly parsed 27 | #54 Support for ui-router resolve, onEnter and onExit enhancement 28 | #50 ngmin fails to annotate a declaration if it follows an annotated one bug 29 | #46 Support for Run 30 | #43 Decorators not properly annotated 31 | #42 Support for components wrapped in IIFE 32 | #37 Support minification of $httpProvider.responseInterceptors.push functions enhancement 33 | #35 Support minification of $routeProvider resolve functions enhancement 34 | #22 Support anonymous factory 35 | 36 | ngmin issues that works in ng-annotate by adding an explicit /* @ngInject */: 37 | ----------------------------------------------------------------------------- 38 | #84 Support services as function references 39 | #81 Support injector().invoke 40 | #79 Support minification of module providers 41 | #69 Certain style of directives do not get their controllers min safed 42 | #65 Is there a way to detect and minify controller inside a controller? 43 | - (example in issue comment works as-is) 44 | 45 | ngmin bug issues not included in the lists above: 46 | ------------------------------------------------- 47 | #95 Unexpected token ILLEGAL Use --force to continue 48 | 49 | ngmin feature request issues where ng-annotate also lacks the feature: 50 | ---------------------------------------------------------------------- 51 | #88 Use "strict DI mode" enhancement 52 | #86 Inclusion in grunt-contrib-uglify 53 | #51 Fixes #19 - Add support for source-map generation 54 | #44 Dynamic Analysis bonus points enhancement 55 | #19 source map support enhancement 56 | #12 Group chainable methods enhancement 57 | -------------------------------------------------------------------------------- /tests/ngmin-tests/ngmin_original.js: -------------------------------------------------------------------------------- 1 | // See repo/INFO for test code origin 2 | 3 | // chain.js 4 | //'should annotate chained declarations' 5 | angular.module('myMod', []). 6 | service('myService',function(dep) { 7 | }). 8 | service('MyCtrl', function($scope) { 9 | }); 10 | 11 | //'should annotate multiple chained declarations' 12 | angular.module('myMod', []). 13 | service('myService',function(dep) { 14 | }). 15 | service('myService2',function(dep) { 16 | }). 17 | service('myService3',function(dep) { 18 | }). 19 | service('MyCtrl', function($scope) { 20 | }); 21 | 22 | //'should annotate multiple chained declarations on constants', function() { 23 | angular.module('myMod', []). 24 | constant('myConstant', 'someConstant'). 25 | constant('otherConstant', 'otherConstant'). 26 | service('myService1',function(dep) { 27 | }). 28 | service('MyCtrl', function($scope) { 29 | }); 30 | 31 | //'should annotate multiple chained declarations on values', function() { 32 | angular.module('myMod', []). 33 | value('myConstant', 'someConstant'). 34 | value('otherConstant', 'otherConstant'). 35 | service('myService1',function(dep) { 36 | }). 37 | service('MyCtrl', function($scope) { 38 | }); 39 | 40 | //'should annotate multiple chained declarations on constants and value regardless of order', function() { 41 | angular.module('myMod', []). 42 | value('myConstant', 'someConstant'). 43 | service('myService1',function(dep) { 44 | }). 45 | constant('otherConstant', 'otherConstant'). 46 | service('MyCtrl', function($scope) { 47 | }); 48 | 49 | //'should annotate refs that have been chained' 50 | var mod = angular.module('chain', []); 51 | mod.factory('a',function($scope) { 52 | }). 53 | factory('b', function($scope) { 54 | }); 55 | 56 | //'should annotate refs to chains' 57 | var mod = angular.module('chain', []). 58 | factory('a', function($scope) { 59 | }); 60 | mod.factory('b', function($scope) { 61 | }); 62 | 63 | 64 | // directive.js 65 | //'should annotate directive controllers' 66 | angular.module('myMod', []). 67 | directive('myDir', function() { 68 | return { 69 | controller: function($scope) { 70 | $scope.test = true; 71 | } 72 | }; 73 | }); 74 | 75 | //'should annotate directive controllers of annotated directives' 76 | angular.module('myMod', []). 77 | directive('myDir', function($window) { 78 | return { 79 | controller: function($scope) { 80 | $scope.test = true; 81 | } 82 | }; 83 | }); 84 | 85 | 86 | // loader.js 87 | //'should annotate modules inside of loaders' 88 | define(["./thing"], function(thing) { 89 | angular.module('myMod', []). 90 | controller('MyCtrl', function($scope) { 91 | }); 92 | }); 93 | 94 | //'should annotate module refs inside of loaders' 95 | define(["./thing"], function(thing) { 96 | var myMod = angular.module('myMod', []); 97 | myMod.controller('MyCtrl', function($scope) { 98 | }); 99 | return myMod; 100 | }); 101 | 102 | 103 | // reference.js 104 | //'should annotate declarations on referenced modules' 105 | var myMod = angular.module('myMod', []); 106 | myMod.controller('MyCtrl', function($scope) { 107 | }); 108 | 109 | //'should annotate declarations on referenced modules when reference is declared then initialized' 110 | var myMod; 111 | myMod = angular.module('myMod', []); 112 | myMod.controller('MyCtrl', function($scope) { 113 | }); 114 | 115 | //'should annotate object-defined providers on referenced modules' 116 | var myMod; 117 | myMod = angular.module('myMod', []); 118 | myMod.provider('MyService', { $get: function(service) { 119 | } }); 120 | 121 | //'should annotate declarations on referenced modules ad infinitum' 122 | var myMod = angular.module('myMod', []); 123 | var myMod2 = myMod, myMod3; 124 | myMod3 = myMod2; 125 | myMod3.controller('MyCtrl', function($scope) { 126 | }); 127 | 128 | //'should not annotate declarations on non-module objects' 129 | var myMod, myOtherMod; 130 | myMod = angular.module('myMod', []); 131 | myOtherMod.controller('MyCtrl', function($scope) { 132 | }); 133 | 134 | //'should keep comments', function() { 135 | var myMod = angular.module('myMod', []); 136 | /*! license */ 137 | myMod.controller('MyCtrl', function($scope) { 138 | }); 139 | 140 | 141 | // route-provider.js 142 | //'should annotate $routeProvider.when()' 143 | angular.module('myMod', []). 144 | config(function($routeProvider) { 145 | $routeProvider.when('path', { 146 | controller: function($scope) { 147 | $scope.works = true; 148 | } 149 | }); 150 | }); 151 | 152 | //'should annotate chained $routeProvider.when()' 153 | angular.module('myMod', []). 154 | config(function($routeProvider) { 155 | $routeProvider. 156 | when('path', { 157 | controller: function($scope) { 158 | $scope.works = true; 159 | } 160 | }). 161 | when('other/path', { 162 | controller: function($http) { 163 | $http.get(); 164 | } 165 | }); 166 | }); 167 | 168 | 169 | // simple.js 170 | //'should annotate controllers' 171 | angular.module('myMod', []). 172 | controller('MyCtrl', function($scope) { 173 | $scope.foo = 'bar'; 174 | }); 175 | 176 | //'should annotate directives' 177 | angular.module('myMod', []). 178 | directive('myDirective', function($rootScope) { 179 | return { 180 | restrict: 'E', 181 | template: 'sup' 182 | }; 183 | }); 184 | 185 | //'should annotate filters' 186 | angular.module('myMod', []). 187 | filter('myFilter', function(dep) { 188 | }); 189 | 190 | //'should annotate services' 191 | angular.module('myMod', []). 192 | service('myService', function(dep) { 193 | }); 194 | 195 | //'should annotate factories' 196 | angular.module('myMod', []). 197 | controller('factory', function(dep) { 198 | }); 199 | 200 | //'should annotate decorators' 201 | angular.module('myMod', []). 202 | decorator('myService', function(dep) { 203 | }); 204 | 205 | //'should annotate config' 206 | angular.module('myMod', []). 207 | config(function(dep) { 208 | }); 209 | 210 | //'should annotate run' 211 | angular.module('myMod', []). 212 | run(function(dep) { 213 | }); 214 | 215 | //'should annotate providers defined by functions' 216 | angular.module('myMod', []). 217 | provider('myService', function(dep) { 218 | this.$get = function(otherDep) { 219 | }; 220 | }); 221 | 222 | //'should annotate providers defined by objects' 223 | angular.module('myMod', []). 224 | provider('myService', { 225 | $get: function(otherDep) { 226 | } 227 | }) 228 | 229 | //'should annotate declarations on modules being referenced' 230 | angular.module('myMod', []); 231 | angular.module('myMod'). 232 | provider('myService', function(dep) { 233 | }); 234 | 235 | //'should not annotate declarations with no dependencies' 236 | angular.module('myMod', []). 237 | provider('myService', function() { 238 | }); 239 | 240 | //'should not annotate constants' 241 | angular.module('myMod', []).constant('fortyTwo', 42); 242 | 243 | //'should not annotate values' 244 | angular.module('myMod', []).value('fortyTwo', 42); 245 | -------------------------------------------------------------------------------- /tests/ngmin-tests/ngmin_with_annotations.js: -------------------------------------------------------------------------------- 1 | // See repo/INFO for test code origin 2 | 3 | // chain.js 4 | //'should annotate chained declarations' 5 | angular.module('myMod', []). 6 | service('myService',["dep", function(dep) { 7 | }]). 8 | service('MyCtrl', ["$scope", function($scope) { 9 | }]); 10 | 11 | //'should annotate multiple chained declarations' 12 | angular.module('myMod', []). 13 | service('myService',["dep", function(dep) { 14 | }]). 15 | service('myService2',["dep", function(dep) { 16 | }]). 17 | service('myService3',["dep", function(dep) { 18 | }]). 19 | service('MyCtrl', ["$scope", function($scope) { 20 | }]); 21 | 22 | //'should annotate multiple chained declarations on constants', function() { 23 | angular.module('myMod', []). 24 | constant('myConstant', 'someConstant'). 25 | constant('otherConstant', 'otherConstant'). 26 | service('myService1',["dep", function(dep) { 27 | }]). 28 | service('MyCtrl', ["$scope", function($scope) { 29 | }]); 30 | 31 | //'should annotate multiple chained declarations on values', function() { 32 | angular.module('myMod', []). 33 | value('myConstant', 'someConstant'). 34 | value('otherConstant', 'otherConstant'). 35 | service('myService1',["dep", function(dep) { 36 | }]). 37 | service('MyCtrl', ["$scope", function($scope) { 38 | }]); 39 | 40 | //'should annotate multiple chained declarations on constants and value regardless of order', function() { 41 | angular.module('myMod', []). 42 | value('myConstant', 'someConstant'). 43 | service('myService1',["dep", function(dep) { 44 | }]). 45 | constant('otherConstant', 'otherConstant'). 46 | service('MyCtrl', ["$scope", function($scope) { 47 | }]); 48 | 49 | //'should annotate refs that have been chained' 50 | var mod = angular.module('chain', []); 51 | mod.factory('a',function($scope) { 52 | }). 53 | factory('b', function($scope) { 54 | }); 55 | 56 | //'should annotate refs to chains' 57 | var mod = angular.module('chain', []). 58 | factory('a', ["$scope", function($scope) { 59 | }]); 60 | mod.factory('b', function($scope) { 61 | }); 62 | 63 | 64 | // directive.js 65 | //'should annotate directive controllers' 66 | angular.module('myMod', []). 67 | directive('myDir', function() { 68 | return { 69 | controller: ["$scope", function($scope) { 70 | $scope.test = true; 71 | }] 72 | }; 73 | }); 74 | 75 | //'should annotate directive controllers of annotated directives' 76 | angular.module('myMod', []). 77 | directive('myDir', ["$window", function($window) { 78 | return { 79 | controller: ["$scope", function($scope) { 80 | $scope.test = true; 81 | }] 82 | }; 83 | }]); 84 | 85 | 86 | // loader.js 87 | //'should annotate modules inside of loaders' 88 | define(["./thing"], function(thing) { 89 | angular.module('myMod', []). 90 | controller('MyCtrl', ["$scope", function($scope) { 91 | }]); 92 | }); 93 | 94 | //'should annotate module refs inside of loaders' 95 | define(["./thing"], function(thing) { 96 | var myMod = angular.module('myMod', []); 97 | myMod.controller('MyCtrl', ["$scope", function($scope) { 98 | }]); 99 | return myMod; 100 | }); 101 | 102 | 103 | // reference.js 104 | //'should annotate declarations on referenced modules' 105 | var myMod = angular.module('myMod', []); 106 | myMod.controller('MyCtrl', ["$scope", function($scope) { 107 | }]); 108 | 109 | //'should annotate declarations on referenced modules when reference is declared then initialized' 110 | var myMod; 111 | myMod = angular.module('myMod', []); 112 | myMod.controller('MyCtrl', ["$scope", function($scope) { 113 | }]); 114 | 115 | //'should annotate object-defined providers on referenced modules' 116 | var myMod; 117 | myMod = angular.module('myMod', []); 118 | myMod.provider('MyService', { $get: ["service", function(service) { 119 | }] }); 120 | 121 | //'should annotate declarations on referenced modules ad infinitum' 122 | var myMod = angular.module('myMod', []); 123 | var myMod2 = myMod, myMod3; 124 | myMod3 = myMod2; 125 | myMod3.controller('MyCtrl', ["$scope", function($scope) { 126 | }]); 127 | 128 | //'should not annotate declarations on non-module objects' 129 | var myMod, myOtherMod; 130 | myMod = angular.module('myMod', []); 131 | myOtherMod.controller('MyCtrl', function($scope) { 132 | }); 133 | 134 | //'should keep comments', function() { 135 | var myMod = angular.module('myMod', []); 136 | /*! license */ 137 | myMod.controller('MyCtrl', ["$scope", function($scope) { 138 | }]); 139 | 140 | 141 | // route-provider.js 142 | //'should annotate $routeProvider.when()' 143 | angular.module('myMod', []). 144 | config(["$routeProvider", function($routeProvider) { 145 | $routeProvider.when('path', { 146 | controller: ["$scope", function($scope) { 147 | $scope.works = true; 148 | }] 149 | }); 150 | }]); 151 | 152 | //'should annotate chained $routeProvider.when()' 153 | angular.module('myMod', []). 154 | config(["$routeProvider", function($routeProvider) { 155 | $routeProvider. 156 | when('path', { 157 | controller: ["$scope", function($scope) { 158 | $scope.works = true; 159 | }] 160 | }). 161 | when('other/path', { 162 | controller: ["$http", function($http) { 163 | $http.get(); 164 | }] 165 | }); 166 | }]); 167 | 168 | 169 | // simple.js 170 | //'should annotate controllers' 171 | angular.module('myMod', []). 172 | controller('MyCtrl', ["$scope", function($scope) { 173 | $scope.foo = 'bar'; 174 | }]); 175 | 176 | //'should annotate directives' 177 | angular.module('myMod', []). 178 | directive('myDirective', ["$rootScope", function($rootScope) { 179 | return { 180 | restrict: 'E', 181 | template: 'sup' 182 | }; 183 | }]); 184 | 185 | //'should annotate filters' 186 | angular.module('myMod', []). 187 | filter('myFilter', ["dep", function(dep) { 188 | }]); 189 | 190 | //'should annotate services' 191 | angular.module('myMod', []). 192 | service('myService', ["dep", function(dep) { 193 | }]); 194 | 195 | //'should annotate factories' 196 | angular.module('myMod', []). 197 | controller('factory', ["dep", function(dep) { 198 | }]); 199 | 200 | //'should annotate decorators' 201 | angular.module('myMod', []). 202 | decorator('myService', ["dep", function(dep) { 203 | }]); 204 | 205 | //'should annotate config' 206 | angular.module('myMod', []). 207 | config(["dep", function(dep) { 208 | }]); 209 | 210 | //'should annotate run' 211 | angular.module('myMod', []). 212 | run(["dep", function(dep) { 213 | }]); 214 | 215 | //'should annotate providers defined by functions' 216 | angular.module('myMod', []). 217 | provider('myService', ["dep", function(dep) { 218 | this.$get = ["otherDep", function(otherDep) { 219 | }]; 220 | }]); 221 | 222 | //'should annotate providers defined by objects' 223 | angular.module('myMod', []). 224 | provider('myService', { 225 | $get: ["otherDep", function(otherDep) { 226 | }] 227 | }) 228 | 229 | //'should annotate declarations on modules being referenced' 230 | angular.module('myMod', []); 231 | angular.module('myMod'). 232 | provider('myService', ["dep", function(dep) { 233 | }]); 234 | 235 | //'should not annotate declarations with no dependencies' 236 | angular.module('myMod', []). 237 | provider('myService', function() { 238 | }); 239 | 240 | //'should not annotate constants' 241 | angular.module('myMod', []).constant('fortyTwo', 42); 242 | 243 | //'should not annotate values' 244 | angular.module('myMod', []).value('fortyTwo', 42); 245 | -------------------------------------------------------------------------------- /tests/ngmin-tests/repo/INFO: -------------------------------------------------------------------------------- 1 | Tests imported 2014-05-09 from https://github.com/btford/ngmin.git 2 | (revision be63c57076dc8fca81347260ca1e5923ccdbf8ec) 3 | ngmin is MIT licensed, author Brian Ford 4 | 5 | ../ngmin_original.js and ../ngmin_with_annotations.js are reduced versions 6 | of the original test files. 7 | -------------------------------------------------------------------------------- /tests/ngmin-tests/repo/chain.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Test chained declarations 3 | * angular.module('myMod', []). 4 | * controller( ... ). 5 | * controller( ... ); 6 | */ 7 | 8 | 9 | var assert = require('should'); 10 | 11 | // so we don't have to put the stuff we're testing into a string 12 | var stringifyFunctionBody = require('./util').stringifyFunctionBody; 13 | var annotate = function (arg) { 14 | return require('../main').annotate( 15 | stringifyFunctionBody(arg)); 16 | }; 17 | 18 | 19 | describe('annotate', function () { 20 | 21 | it('should annotate chained declarations', function () { 22 | var annotated = annotate(function () { 23 | angular.module('myMod', []). 24 | service('myService', function (dep) {}). 25 | service('MyCtrl', function ($scope) {}); 26 | }); 27 | 28 | annotated.should.equal(stringifyFunctionBody(function () { 29 | angular.module('myMod', []). 30 | service('myService', ['dep', function (dep) {}]). 31 | service('MyCtrl', ['$scope', function ($scope) {}]); 32 | })); 33 | }); 34 | 35 | it('should annotate multiple chained declarations', function () { 36 | var annotated = annotate(function () { 37 | angular.module('myMod', []). 38 | service('myService', function (dep) {}). 39 | service('myService2', function (dep) {}). 40 | service('myService3', function (dep) {}). 41 | service('MyCtrl', function ($scope) {}); 42 | }); 43 | 44 | annotated.should.equal(stringifyFunctionBody(function () { 45 | angular.module('myMod', []). 46 | service('myService', ['dep', function (dep) {}]). 47 | service('myService2', ['dep', function (dep) {}]). 48 | service('myService3', ['dep', function (dep) {}]). 49 | service('MyCtrl', ['$scope', function ($scope) {}]); 50 | })); 51 | }); 52 | 53 | it('should annotate multiple chained declarations on constants', function() { 54 | var annotated = annotate(function () { 55 | angular.module('myMod', []). 56 | constant('myConstant', 'someConstant'). 57 | constant('otherConstant', 'otherConstant'). 58 | service('myService1', function (dep) {}). 59 | service('MyCtrl', function ($scope) {}); 60 | }); 61 | 62 | annotated.should.equal(stringifyFunctionBody(function () { 63 | angular.module('myMod', []). 64 | constant('myConstant', 'someConstant'). 65 | constant('otherConstant', 'otherConstant'). 66 | service('myService1', ['dep', function (dep) {}]). 67 | service('MyCtrl', ['$scope', function ($scope) {}]); 68 | })); 69 | }); 70 | 71 | it('should annotate multiple chained declarations on values', function() { 72 | var annotated = annotate(function () { 73 | angular.module('myMod', []). 74 | value('myConstant', 'someConstant'). 75 | value('otherConstant', 'otherConstant'). 76 | service('myService1', function (dep) {}). 77 | service('MyCtrl', function ($scope) {}); 78 | }); 79 | 80 | annotated.should.equal(stringifyFunctionBody(function () { 81 | angular.module('myMod', []). 82 | value('myConstant', 'someConstant'). 83 | value('otherConstant', 'otherConstant'). 84 | service('myService1', ['dep', function (dep) {}]). 85 | service('MyCtrl', ['$scope', function ($scope) {}]); 86 | })); 87 | }); 88 | 89 | it('should annotate multiple chained declarations on constants and value regardless of order', function() { 90 | var annotated = annotate(function () { 91 | angular.module('myMod', []). 92 | value('myConstant', 'someConstant'). 93 | service('myService1', function (dep) {}). 94 | constant('otherConstant', 'otherConstant'). 95 | service('MyCtrl', function ($scope) {}); 96 | }); 97 | 98 | annotated.should.equal(stringifyFunctionBody(function () { 99 | angular.module('myMod', []). 100 | value('myConstant', 'someConstant'). 101 | service('myService1', ['dep', function (dep) {}]). 102 | constant('otherConstant', 'otherConstant'). 103 | service('MyCtrl', ['$scope', function ($scope) {}]); 104 | })); 105 | }); 106 | 107 | it('should annotate refs that have been chained', function () { 108 | var annotated = annotate(function () { 109 | var mod = angular.module('chain', []); 110 | mod.factory('a', function ($scope){}). 111 | factory('b', function ($scope){}); 112 | }); 113 | 114 | annotated.should.equal(stringifyFunctionBody(function () { 115 | var mod = angular.module('chain', []); 116 | mod.factory('a', ['$scope', function($scope){}]). 117 | factory('b', ['$scope', function($scope){}]); 118 | })); 119 | }); 120 | 121 | it('should annotate refs to chains', function () { 122 | var annotated = annotate(function () { 123 | var mod = angular.module('chain', []). 124 | factory('a', function ($scope){}); 125 | mod.factory('b', function ($scope){}); 126 | }); 127 | 128 | annotated.should.equal(stringifyFunctionBody(function () { 129 | var mod = angular.module('chain', []). 130 | factory('a', ['$scope', function($scope){}]); 131 | mod.factory('b', ['$scope', function($scope){}]); 132 | })); 133 | }); 134 | 135 | }); 136 | -------------------------------------------------------------------------------- /tests/ngmin-tests/repo/directive.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Test annotations within the Directive Definition Object (DDO): 3 | * 4 | * angular.module('myMod', []).directive('whatever', function () { 5 | * return { 6 | * controller: function ($scope) { ... } // <--- this needs annotations 7 | * }; 8 | * }) 9 | * 10 | */ 11 | 12 | var assert = require('should'); 13 | 14 | // so we don't have to put the stuff we're testing into a string 15 | var stringifyFunctionBody = require('./util').stringifyFunctionBody; 16 | var annotate = function (arg) { 17 | return require('../main').annotate( 18 | stringifyFunctionBody(arg)); 19 | }; 20 | 21 | 22 | describe('annotate', function () { 23 | 24 | it('should annotate directive controllers', function () { 25 | var annotated = annotate(function () { 26 | angular.module('myMod', []). 27 | directive('myDir', function () { 28 | return { 29 | controller: function ($scope) { $scope.test = true; } 30 | }; 31 | }); 32 | }); 33 | 34 | annotated.should.equal(stringifyFunctionBody(function () { 35 | angular.module('myMod', []). 36 | directive('myDir', function () { 37 | return { 38 | controller: [ 39 | '$scope', 40 | function ($scope) { $scope.test = true; } 41 | ] 42 | }; 43 | }); 44 | })); 45 | }); 46 | 47 | it('should annotate directive controllers of annotated directives', function () { 48 | var annotated = annotate(function () { 49 | angular.module('myMod', []). 50 | directive('myDir', function ($window) { 51 | return { 52 | controller: function ($scope) { $scope.test = true; } 53 | }; 54 | }); 55 | }); 56 | 57 | annotated.should.equal(stringifyFunctionBody(function () { 58 | angular.module('myMod', []). 59 | directive('myDir', ['$window', function ($window) { 60 | return { 61 | controller: [ 62 | '$scope', 63 | function ($scope) { $scope.test = true; } 64 | ] 65 | }; 66 | }]); 67 | })); 68 | }); 69 | 70 | }); 71 | -------------------------------------------------------------------------------- /tests/ngmin-tests/repo/loader.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Test for angular modules that are wrapped by goofy 3 | * 3rd party loaders like Require.js 4 | */ 5 | 6 | var assert = require('should'); 7 | 8 | // so we don't have to put the stuff we're testing into a string 9 | var stringifyFunctionBody = require('./util').stringifyFunctionBody; 10 | var annotate = function (arg) { 11 | return require('../main').annotate( 12 | stringifyFunctionBody(arg)); 13 | }; 14 | 15 | 16 | describe('annotate', function () { 17 | 18 | it('should annotate modules inside of loaders', function () { 19 | var annotated = annotate(function () { 20 | define(["./thing"], function(thing) { 21 | angular.module('myMod', []). 22 | controller('MyCtrl', function ($scope) {}); 23 | }); 24 | }); 25 | 26 | annotated.should.equal(stringifyFunctionBody(function () { 27 | define(["./thing"], function(thing) { 28 | angular.module('myMod', []). 29 | controller('MyCtrl', ['$scope', function ($scope) {}]); 30 | }); 31 | })); 32 | }); 33 | 34 | it('should annotate module refs inside of loaders', function () { 35 | var annotated = annotate(function () { 36 | 37 | 38 | define(["./thing"], function(thing) { 39 | var myMod = angular.module('myMod', []); 40 | myMod.controller('MyCtrl', function ($scope) {}); 41 | return myMod; 42 | }); 43 | 44 | }); 45 | 46 | annotated.should.equal(stringifyFunctionBody(function () { 47 | define(["./thing"], function(thing) { 48 | var myMod = angular.module('myMod', []); 49 | myMod.controller('MyCtrl', ['$scope', function ($scope) {}]); 50 | return myMod; 51 | }); 52 | })); 53 | }); 54 | 55 | 56 | }); 57 | -------------------------------------------------------------------------------- /tests/ngmin-tests/repo/reference.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Test cases where there's a reference to a module 3 | * 4 | * var myMod = angular.module('myMod', []); 5 | * myMod.controller( ... ) 6 | * 7 | */ 8 | 9 | var assert = require('should'); 10 | 11 | // so we don't have to put the stuff we're testing into a string 12 | var stringifyFunctionBody = require('./util').stringifyFunctionBody; 13 | var annotate = function (arg) { 14 | return require('../main').annotate( 15 | stringifyFunctionBody(arg)); 16 | }; 17 | 18 | 19 | describe('annotate', function () { 20 | 21 | it('should annotate declarations on referenced modules', function () { 22 | var annotated = annotate(function () { 23 | var myMod = angular.module('myMod', []); 24 | myMod.controller('MyCtrl', function ($scope) {}); 25 | }); 26 | 27 | annotated.should.equal(stringifyFunctionBody(function () { 28 | var myMod = angular.module('myMod', []); 29 | myMod.controller('MyCtrl', [ 30 | '$scope', 31 | function ($scope) { 32 | } 33 | ]); 34 | })); 35 | }); 36 | 37 | it('should annotate declarations on referenced modules when reference is declared then initialized', function () { 38 | var annotated = annotate(function () { 39 | var myMod; 40 | myMod = angular.module('myMod', []); 41 | myMod.controller('MyCtrl', function ($scope) {}); 42 | }); 43 | 44 | annotated.should.equal(stringifyFunctionBody(function () { 45 | var myMod; 46 | myMod = angular.module('myMod', []); 47 | myMod.controller('MyCtrl', [ 48 | '$scope', 49 | function ($scope) { 50 | } 51 | ]); 52 | })); 53 | }); 54 | 55 | it('should annotate object-defined providers on referenced modules', function () { 56 | var annotated = annotate(function () { 57 | var myMod; 58 | myMod = angular.module('myMod', []); 59 | myMod.provider('MyService', { $get: function(service) {} }); 60 | }); 61 | 62 | annotated.should.equal(stringifyFunctionBody(function () { 63 | var myMod; 64 | myMod = angular.module('myMod', []); 65 | myMod.provider('MyService', { 66 | $get: ['service', function(service) {}] 67 | }); 68 | })); 69 | }); 70 | 71 | // TODO: lol commenting out test cases 72 | /* 73 | it('should annotate declarations on referenced modules ad infinitum', function () { 74 | var annotated = annotate(function () { 75 | var myMod = angular.module('myMod', []); 76 | var myMod2 = myMod, myMod3; 77 | myMod3 = myMod2; 78 | myMod3.controller('MyCtrl', function ($scope) {}); 79 | }); 80 | 81 | annotated.should.equal(stringifyFunctionBody(function () { 82 | var myMod = angular.module('myMod', []); 83 | var myMod2 = myMod, myMod3; 84 | myMod3 = myMod2; 85 | myMod3.controller('MyCtrl', ['$scope', function ($scope) {}]); 86 | })); 87 | }); 88 | */ 89 | 90 | // TODO: it should annotate silly assignment chains 91 | 92 | it('should not annotate declarations on non-module objects', function () { 93 | var fn = function () { 94 | var myMod, myOtherMod; 95 | myMod = angular.module('myMod', []); 96 | myOtherMod.controller('MyCtrl', function ($scope) {}); 97 | }; 98 | var annotated = annotate(fn); 99 | annotated.should.equal(stringifyFunctionBody(fn)); 100 | }); 101 | 102 | it('should keep comments', function() { 103 | var annotated = annotate(function () { 104 | var myMod = angular.module('myMod', []); 105 | /*! license */ 106 | myMod.controller('MyCtrl', function ($scope) {}); 107 | }); 108 | 109 | annotated.should.equal(stringifyFunctionBody(function () { 110 | var myMod = angular.module('myMod', []); 111 | /*! license */ 112 | myMod.controller('MyCtrl', [ 113 | '$scope', 114 | function ($scope) { 115 | } 116 | ]); 117 | })); 118 | }); 119 | 120 | }); 121 | -------------------------------------------------------------------------------- /tests/ngmin-tests/repo/route-provider.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Test annotations within the Directive Definition Object (DDO): 3 | * 4 | * angular.module('myMod', []).directive('whatever', function () { 5 | * return { 6 | * controller: function ($scope) { ... } // <--- this needs annotations 7 | * }; 8 | * }) 9 | * 10 | */ 11 | 12 | var assert = require('should'); 13 | 14 | // so we don't have to put the stuff we're testing into a string 15 | var stringifyFunctionBody = require('./util').stringifyFunctionBody; 16 | var annotate = function (arg) { 17 | return require('../main').annotate( 18 | stringifyFunctionBody(arg)); 19 | }; 20 | 21 | 22 | describe('annotate', function () { 23 | 24 | it('should annotate $routeProvider.when()', function () { 25 | var annotated = annotate(function () { 26 | angular.module('myMod', []). 27 | config(function ($routeProvider) { 28 | $routeProvider.when('path', { 29 | controller: function ($scope) { 30 | $scope.works = true; 31 | } 32 | }); 33 | }); 34 | }); 35 | 36 | annotated.should.equal(stringifyFunctionBody(function () { 37 | angular.module('myMod', []). 38 | config(['$routeProvider', function ($routeProvider) { 39 | $routeProvider.when('path', { 40 | controller: ['$scope', function ($scope) { 41 | $scope.works = true; 42 | }] 43 | }); 44 | }]); 45 | })); 46 | }); 47 | 48 | 49 | it('should annotate chained $routeProvider.when()', function () { 50 | var annotated = annotate(function () { 51 | angular.module('myMod', []). 52 | config(function ($routeProvider) { 53 | $routeProvider. 54 | when('path', { 55 | controller: function ($scope) { 56 | $scope.works = true; 57 | } 58 | }). 59 | when('other/path', { 60 | controller: function ($http) { 61 | $http.get(); 62 | } 63 | }); 64 | }); 65 | }); 66 | 67 | annotated.should.equal(stringifyFunctionBody(function () { 68 | angular.module('myMod', []). 69 | config(['$routeProvider', function ($routeProvider) { 70 | $routeProvider. 71 | when('path', { 72 | controller: ['$scope', function ($scope) { 73 | $scope.works = true; 74 | }] 75 | }). 76 | when('other/path', { 77 | controller: ['$http', function ($http) { 78 | $http.get(); 79 | }] 80 | }); 81 | }]); 82 | })); 83 | }); 84 | 85 | 86 | }); 87 | -------------------------------------------------------------------------------- /tests/ngmin-tests/repo/simple.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Test simple cases 3 | * 4 | * angular.module('myMod', []).controller( ... ); 5 | * 6 | */ 7 | 8 | var assert = require('should'); 9 | 10 | // so we don't have to put the stuff we're testing into a string 11 | var stringifyFunctionBody = require('./util').stringifyFunctionBody; 12 | var annotate = function (arg) { 13 | return require('../main').annotate( 14 | stringifyFunctionBody(arg)); 15 | }; 16 | 17 | 18 | describe('annotate', function () { 19 | 20 | it('should annotate controllers', function () { 21 | var annotated = annotate(function () { 22 | angular.module('myMod', []). 23 | controller('MyCtrl', function ($scope) { 24 | $scope.foo = 'bar'; 25 | }); 26 | }); 27 | 28 | annotated.should.equal(stringifyFunctionBody(function () { 29 | angular.module('myMod', []).controller('MyCtrl', [ 30 | '$scope', 31 | function ($scope) { 32 | $scope.foo = 'bar'; 33 | } 34 | ]); 35 | })); 36 | }); 37 | 38 | 39 | it('should annotate directives', function () { 40 | var annotated = annotate(function () { 41 | angular.module('myMod', []). 42 | directive('myDirective', function ($rootScope) { 43 | return { 44 | restrict: 'E', 45 | template: 'sup' 46 | }; 47 | }); 48 | }); 49 | 50 | annotated.should.equal(stringifyFunctionBody(function () { 51 | angular.module('myMod', []).directive('myDirective', [ 52 | '$rootScope', 53 | function ($rootScope) { 54 | return { 55 | restrict: 'E', 56 | template: 'sup' 57 | }; 58 | } 59 | ]); 60 | })); 61 | }); 62 | 63 | 64 | it('should annotate filters', function () { 65 | var annotated = annotate(function () { 66 | angular.module('myMod', []). 67 | filter('myFilter', function (dep) {}); 68 | }); 69 | 70 | annotated.should.equal(stringifyFunctionBody(function () { 71 | angular.module('myMod', []).filter('myFilter', [ 72 | 'dep', 73 | function (dep) { 74 | } 75 | ]); 76 | })); 77 | }); 78 | 79 | 80 | it('should annotate services', function () { 81 | var annotated = annotate(function () { 82 | angular.module('myMod', []). 83 | service('myService', function (dep) {}); 84 | }); 85 | 86 | annotated.should.equal(stringifyFunctionBody(function () { 87 | angular.module('myMod', []).service('myService', [ 88 | 'dep', 89 | function (dep) { 90 | } 91 | ]); 92 | })); 93 | }); 94 | 95 | 96 | it('should annotate factories', function () { 97 | var annotated = annotate(function () { 98 | angular.module('myMod', []). 99 | controller('factory', function (dep) {}); 100 | }); 101 | 102 | annotated.should.equal(stringifyFunctionBody(function () { 103 | angular.module('myMod', []).controller('factory', [ 104 | 'dep', 105 | function (dep) { 106 | } 107 | ]); 108 | })); 109 | }); 110 | 111 | 112 | it('should annotate decorators', function () { 113 | var annotated = annotate(function () { 114 | angular.module('myMod', []). 115 | decorator('myService', function (dep) {}); 116 | }); 117 | 118 | annotated.should.equal(stringifyFunctionBody(function () { 119 | angular.module('myMod', []).decorator('myService', [ 120 | 'dep', 121 | function (dep) { 122 | } 123 | ]); 124 | })); 125 | }); 126 | 127 | 128 | it('should annotate config', function () { 129 | var annotated = annotate(function () { 130 | angular.module('myMod', []). 131 | config(function (dep) {}); 132 | }); 133 | 134 | annotated.should.equal(stringifyFunctionBody(function () { 135 | angular.module('myMod', []).config([ 136 | 'dep', 137 | function (dep) { 138 | } 139 | ]); 140 | })); 141 | }); 142 | 143 | 144 | it('should annotate run', function () { 145 | var annotated = annotate(function () { 146 | angular.module('myMod', []). 147 | run(function (dep) {}); 148 | }); 149 | 150 | annotated.should.equal(stringifyFunctionBody(function () { 151 | angular.module('myMod', []).run([ 152 | 'dep', 153 | function (dep) { 154 | } 155 | ]); 156 | })); 157 | }); 158 | 159 | 160 | it('should annotate providers defined by functions', function () { 161 | var annotated = annotate(function () { 162 | angular.module('myMod', []). 163 | provider('myService', function (dep) { 164 | this.$get = function(otherDep) {}; 165 | }); 166 | }); 167 | 168 | annotated.should.equal(stringifyFunctionBody(function () { 169 | angular.module('myMod', []).provider('myService', [ 170 | 'dep', 171 | function (dep) { 172 | this.$get = ['otherDep', function(otherDep) {}]; 173 | } 174 | ]); 175 | })); 176 | }); 177 | 178 | 179 | it('should annotate providers defined by objects', function () { 180 | var annotated = annotate(function () { 181 | angular.module('myMod', []). 182 | provider('myService', { 183 | $get: function(otherDep) {} 184 | }) 185 | }); 186 | 187 | annotated.should.equal(stringifyFunctionBody(function () { 188 | angular.module('myMod', []). 189 | provider('myService', { 190 | $get: ['otherDep', function(otherDep) {}] 191 | }); 192 | })); 193 | }); 194 | 195 | 196 | it('should annotate declarations on modules being referenced', function () { 197 | var annotated = annotate(function () { 198 | angular.module('myMod', []); 199 | angular.module('myMod'). 200 | provider('myService', function (dep) {}); 201 | }); 202 | 203 | annotated.should.equal(stringifyFunctionBody(function () { 204 | angular.module('myMod', []); 205 | angular.module('myMod').provider('myService', [ 206 | 'dep', 207 | function (dep) { 208 | } 209 | ]); 210 | })); 211 | }); 212 | 213 | 214 | it('should not annotate declarations with no dependencies', function () { 215 | var fn = function () { 216 | angular.module('myMod', []). 217 | provider('myService', function () {}); 218 | }; 219 | var annotated = annotate(fn); 220 | 221 | annotated.should.equal(stringifyFunctionBody(fn)); 222 | }); 223 | 224 | 225 | it('should not annotate constants', function () { 226 | var fn = function () { 227 | angular.module('myMod', []).constant('fortyTwo', 42); 228 | }; 229 | 230 | var annotated = annotate(fn); 231 | annotated.should.equal(stringifyFunctionBody(fn)); 232 | }); 233 | 234 | 235 | it('should not annotate values', function () { 236 | var fn = function () { 237 | angular.module('myMod', []).value('fortyTwo', 42); 238 | }; 239 | 240 | var annotated = annotate(fn); 241 | annotated.should.equal(stringifyFunctionBody(fn)); 242 | }); 243 | 244 | }); 245 | -------------------------------------------------------------------------------- /tests/ngmin-tests/repo/util.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Test utils 4 | */ 5 | 6 | 7 | var body = require('fn-body'), 8 | normalize = require('normalize-fn'); 9 | 10 | // given a function, return its body as a normalized string. 11 | // makes tests look a bit cleaner 12 | exports.stringifyFunctionBody = function (fn) { 13 | return normalize(body(fn)); 14 | }; 15 | -------------------------------------------------------------------------------- /tests/optionals/angular-dashboard-framework.annotated.js: -------------------------------------------------------------------------------- 1 | //test without adf specific annotations 2 | angular.module('myMod', ['adf.provider']) 3 | .config(["dashboardProvider", function(dashboardProvider){ 4 | dashboardProvider 5 | .widget('myWidget', { 6 | title: 'My Widget' 7 | }); 8 | }]); 9 | 10 | //test adf controller, without dependency 11 | angular.module('myMod', ['adf.provider']) 12 | .config(["dashboardProvider", function(dashboardProvider){ 13 | dashboardProvider 14 | .widget('myWidget', { 15 | title: 'My Widget', 16 | controller: function(){ 17 | 18 | } 19 | }); 20 | }]); 21 | 22 | //test adf controller 23 | angular.module('myMod', ['adf.provider']) 24 | .config(["dashboardProvider", function(dashboardProvider){ 25 | dashboardProvider 26 | .widget('myWidget', { 27 | title: 'My Widget', 28 | controller: ["$http", function($http){ 29 | 30 | }] 31 | }); 32 | }]); 33 | 34 | //test mixed resolve 35 | angular.module('myMod', ['adf.provider']) 36 | .config(["dashboardProvider", function(dashboardProvider){ 37 | dashboardProvider 38 | .widget('myWidget', { 39 | title: 'My Widget', 40 | resolve: { 41 | one: function(){ 42 | 43 | }, 44 | two: ["$http", function($http){ 45 | 46 | }] 47 | } 48 | }); 49 | }]); 50 | 51 | //test adf edit controller 52 | angular.module('myMod', ['adf.provider']) 53 | .config(["dashboardProvider", function(dashboardProvider){ 54 | dashboardProvider 55 | .widget('myWidget', { 56 | title: 'My Widget', 57 | edit: { 58 | controller: ["$http", function($http){ 59 | 60 | }] 61 | } 62 | }); 63 | }]); 64 | 65 | //test full adf components 66 | angular.module('myMod', ['adf.provider']) 67 | .config(["dashboardProvider", function(dashboardProvider){ 68 | dashboardProvider 69 | .widget('myWidget', { 70 | title: 'My Widget', 71 | controller: ["$http", function($http){ 72 | // control something 73 | }], 74 | resolve: { 75 | one: ["$http", "config", function($http, config){ 76 | // resolve something 77 | }], 78 | two: ["$http", "config", function($http, config){ 79 | // resolve something 80 | }] 81 | }, 82 | edit: { 83 | controller: ["$http", function($http){ 84 | // control something 85 | }], 86 | apply: ["$http", "config", function($http, config){ 87 | // apply configuration 88 | }], 89 | resolve: { 90 | editone: ["$http", "config", function($http, config){ 91 | // resolve something 92 | }], 93 | edittwo: ["$http", "config", function($http, config){ 94 | // resolve something 95 | }] 96 | } 97 | } 98 | }); 99 | }]); 100 | -------------------------------------------------------------------------------- /tests/optionals/angular-dashboard-framework.js: -------------------------------------------------------------------------------- 1 | //test without adf specific annotations 2 | angular.module('myMod', ['adf.provider']) 3 | .config(function(dashboardProvider){ 4 | dashboardProvider 5 | .widget('myWidget', { 6 | title: 'My Widget' 7 | }); 8 | }); 9 | 10 | //test adf controller, without dependency 11 | angular.module('myMod', ['adf.provider']) 12 | .config(function(dashboardProvider){ 13 | dashboardProvider 14 | .widget('myWidget', { 15 | title: 'My Widget', 16 | controller: function(){ 17 | 18 | } 19 | }); 20 | }); 21 | 22 | //test adf controller 23 | angular.module('myMod', ['adf.provider']) 24 | .config(function(dashboardProvider){ 25 | dashboardProvider 26 | .widget('myWidget', { 27 | title: 'My Widget', 28 | controller: function($http){ 29 | 30 | } 31 | }); 32 | }); 33 | 34 | //test mixed resolve 35 | angular.module('myMod', ['adf.provider']) 36 | .config(function(dashboardProvider){ 37 | dashboardProvider 38 | .widget('myWidget', { 39 | title: 'My Widget', 40 | resolve: { 41 | one: function(){ 42 | 43 | }, 44 | two: function($http){ 45 | 46 | } 47 | } 48 | }); 49 | }); 50 | 51 | //test adf edit controller 52 | angular.module('myMod', ['adf.provider']) 53 | .config(function(dashboardProvider){ 54 | dashboardProvider 55 | .widget('myWidget', { 56 | title: 'My Widget', 57 | edit: { 58 | controller: function($http){ 59 | 60 | } 61 | } 62 | }); 63 | }); 64 | 65 | //test full adf components 66 | angular.module('myMod', ['adf.provider']) 67 | .config(function(dashboardProvider){ 68 | dashboardProvider 69 | .widget('myWidget', { 70 | title: 'My Widget', 71 | controller: function($http){ 72 | // control something 73 | }, 74 | resolve: { 75 | one: function($http, config){ 76 | // resolve something 77 | }, 78 | two: function($http, config){ 79 | // resolve something 80 | } 81 | }, 82 | edit: { 83 | controller: function($http){ 84 | // control something 85 | }, 86 | apply: function($http, config){ 87 | // apply configuration 88 | }, 89 | resolve: { 90 | editone: function($http, config){ 91 | // resolve something 92 | }, 93 | edittwo: function($http, config){ 94 | // resolve something 95 | } 96 | } 97 | } 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /tests/original.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // long form 4 | angular.module("MyMod").controller("MyCtrl", function($scope, $timeout) { 5 | }); 6 | 7 | // w/ dependencies 8 | angular.module("MyMod", ["OtherMod"]).controller("MyCtrl", function($scope, $timeout) { 9 | }); 10 | 11 | // simple 12 | myMod.controller("foo", function($scope, $timeout) { 13 | }); 14 | myMod.service("foo", function($scope, $timeout) { 15 | }); 16 | myMod.factory("foo", function($scope, $timeout) { 17 | }); 18 | myMod.directive("foo", function($scope, $timeout) { 19 | }); 20 | myMod.filter("foo", function($scope, $timeout) { 21 | }); 22 | myMod.animation("foo", function($scope, $timeout) { 23 | }); 24 | myMod.invoke("foo", function($scope, $timeout) { 25 | }); 26 | myMod.store("foo", function($scope, $timeout) { 27 | }); 28 | myMod.decorator("foo", function($scope, $timeout) { 29 | }); 30 | myMod.component("foo", {controller: function($scope, $timeout) {}}); 31 | 32 | // implicit config function 33 | angular.module("MyMod", function($interpolateProvider) {}); 34 | angular.module("MyMod", ["OtherMod"], function($interpolateProvider) {}); 35 | angular.module("MyMod", ["OtherMod"], function($interpolateProvider) {}).controller("foo", function($scope) {}); 36 | 37 | // object property 38 | var myObj = {}; 39 | myObj.myMod = angular.module("MyMod"); 40 | myObj.myMod.controller("foo", function($scope, $timeout) { a }); 41 | 42 | // no dependencies => no need to wrap the function in an array 43 | myMod.controller("foo", function() { 44 | }); 45 | myMod.service("foo", function() { 46 | }); 47 | myMod.factory("foo", function() { 48 | }); 49 | myMod.directive("foo", function() { 50 | }); 51 | myMod.filter("foo", function() { 52 | }); 53 | myMod.animation("foo", function() { 54 | }); 55 | myMod.invoke("foo", function() { 56 | }); 57 | myMod.store("foo", function() { 58 | }); 59 | myMod.decorator("foo", function() { 60 | }); 61 | myMod.component("foo", {controller: function() {}}); 62 | 63 | // run, config don't take names 64 | myMod.run(function($scope, $timeout) { 65 | }); 66 | angular.module("MyMod").run(function($scope) { 67 | }); 68 | myMod.config(function($scope, $timeout) { 69 | }); 70 | angular.module("MyMod").config(function() { 71 | }); 72 | 73 | // directive return object 74 | myMod.directive("foo", function($scope) { 75 | return { 76 | controller: function($scope, $timeout) { 77 | bar; 78 | } 79 | } 80 | }); 81 | myMod.directive("foo", function($scope) { 82 | return { 83 | controller: function() { 84 | bar; 85 | } 86 | } 87 | }); 88 | 89 | // provider, provider $get 90 | myMod.provider("foo", function($scope) { 91 | this.$get = function($scope, $timeout) { 92 | bar; 93 | }; 94 | self.$get = function($scope) {}; 95 | that.$get = function($scope) {}; 96 | ignore.$get = function($scope) {}; 97 | }); 98 | myMod.provider("foo", function() { 99 | this.$get = function() { 100 | bar; 101 | }; 102 | }); 103 | myMod.provider("foo", function() { 104 | return { 105 | $get: function($scope, $timeout) { 106 | bar; 107 | }}; 108 | }); 109 | myMod.provider("foo", function() { 110 | return { 111 | $get: function() { 112 | bar; 113 | }}; 114 | }); 115 | myMod.provider("foo", { 116 | $get: function($scope, $timeout) { 117 | bar; 118 | } 119 | }); 120 | myMod.provider("foo", { 121 | $get: function() { 122 | bar; 123 | } 124 | }); 125 | myMod.provider("foo", { 126 | "$get": function($scope, $timeout) { 127 | bar; 128 | } 129 | }); 130 | myMod.provider("foo", { 131 | '$get': function($scope, $timeout) { 132 | bar; 133 | } 134 | }); 135 | 136 | myMod.provider("foo", function(x) { 137 | this.$get = function(a,b) {}; 138 | }); 139 | 140 | myMod.provider("foo", extprov); 141 | function extprov(x) { 142 | this.$get = function(a,b) {}; 143 | this.$get = fooget; 144 | this.$get = inner; 145 | 146 | function inner(c, d) { 147 | } 148 | } 149 | 150 | function fooget(b) { 151 | this.$get = fooget2; 152 | } 153 | 154 | function fooget2(c) { 155 | } 156 | 157 | // chaining 158 | myMod.directive("foo", function($a, $b) { 159 | a; 160 | }).factory("foo", function() { 161 | b; 162 | }).config(function($c) { 163 | c; 164 | }).filter("foo", function($d, $e) { 165 | d; 166 | }).animation("foo", function($f, $g) { 167 | e; 168 | }).component("foo", {controller: function($scope, $timeout) { 169 | i; 170 | }}).invoke("foo", function($f, $g) { 171 | f; 172 | }).decorator("foo", function($f, $g) { 173 | g; 174 | }).store("foo", function($f, $g) { 175 | h; 176 | }); 177 | 178 | angular.module("MyMod").directive("foo", function($a, $b) { 179 | a; 180 | }).provider("foo", function() { 181 | return { 182 | $get: function($scope, $timeout) { 183 | bar; 184 | }}; 185 | }).value("foo", "bar") 186 | .constant("foo", "bar") 187 | .bootstrap(element, [], {}) 188 | .factory("foo", function() { 189 | b; 190 | }).config(function($c) { 191 | c; 192 | }).filter("foo", function($d, $e) { 193 | d; 194 | }).animation("foo", function($f, $g) { 195 | e; 196 | }).component("foo", {controller: function($scope, $timeout) { 197 | i; 198 | }}).invoke("foo", function($h, $i) { 199 | f; 200 | }).decorator("foo", function($h, $i) { 201 | g; 202 | }).store("foo", function($f, $g) { 203 | h; 204 | }); 205 | 206 | // $provide 207 | angular.module("myMod").controller("foo", function() { 208 | $provide.decorator("foo", function($scope) {}); 209 | $provide.service("foo", function($scope) {}); 210 | $provide.factory("foo", function($scope) {}); 211 | //$provide.provider 212 | $provide.provider("foo", function($scope) { 213 | this.$get = function($scope) {}; 214 | return { $get: function($scope, $timeout) {}}; 215 | }); 216 | $provide.provider("foo", { 217 | $get: function($scope, $timeout) {} 218 | }); 219 | }); 220 | // negative $provide 221 | function notInContext() { 222 | $provide.decorator("foo", function($scope) {}); 223 | $provide.service("foo", function($scope) {}); 224 | $provide.factory("foo", function($scope) {}); 225 | $provide.provider("foo", function($scope) { 226 | this.$get = function($scope) {}; 227 | return { $get: function($scope, $timeout) {}}; 228 | }); 229 | $provide.provider("foo", { 230 | $get: function($scope, $timeout) {} 231 | }); 232 | } 233 | 234 | // $controllerProvider 235 | angular.module("myMod").controller("foo", function() { 236 | $controllerProvider.register("foo", function($scope) {}); 237 | }); 238 | function notInContext() { 239 | $controllerProvider.register("foo", function($scope) {}); 240 | } 241 | 242 | // special handling for TypeScript __extends 243 | function outer1() { 244 | a; 245 | __extends(); 246 | b; 247 | function foo($a) { 248 | "ngInject"; 249 | } 250 | } 251 | function outer2() { 252 | a; 253 | __not_extends(); 254 | b; 255 | function foo($a) { 256 | "ngInject"; 257 | } 258 | } 259 | 260 | // all the patterns below matches only when we're inside a detected angular module 261 | angular.module("MyMod").directive("pleasematchthis", function() { 262 | 263 | // $injector.invoke 264 | $injector.invoke(function($compile) { 265 | $compile(myElement)(scope); 266 | }); 267 | 268 | // $httpProvider 269 | $httpProvider.interceptors.push(function($scope) { a }); 270 | $httpProvider.responseInterceptors.push(function($scope) { a }, function(a, b) { b }, function() { c }); 271 | 272 | // $routeProvider 273 | $routeProvider.when("path", { 274 | controller: function($scope) { a } 275 | }).when("path2", { 276 | controller: function($scope) { b }, 277 | resolve: { 278 | zero: function() { a }, 279 | more: function($scope, $timeout) { b }, 280 | something: "else", 281 | }, 282 | dontAlterMe: function(arg) {}, 283 | }); 284 | 285 | // ui-router 286 | $stateProvider.state("myState", { 287 | resolve: { 288 | simpleObj: function() { a }, 289 | promiseObj: function($scope, $timeout) { b }, 290 | translations: "translations", 291 | }, 292 | params: { 293 | simple: function($scope) {}, 294 | inValue: { value: function($scope) {}, notThis: function($scope) {} }, 295 | }, 296 | views: { 297 | viewa: { 298 | controller: function($scope, myParam) {}, 299 | controllerProvider: function($stateParams) {}, 300 | templateProvider: function($scope) {}, 301 | dontAlterMe: function(arg) {}, 302 | resolve: { 303 | myParam: function($stateParams) { 304 | return $stateParams.paramFromDI; 305 | } 306 | }, 307 | }, 308 | viewb: { 309 | dontAlterMe: function(arg) {}, 310 | templateProvider: function($scope) {}, 311 | controller: function($scope) {}, 312 | }, 313 | dontAlterMe: null, 314 | }, 315 | controller: function($scope, simpleObj, promiseObj, translations) { c }, 316 | controllerProvider: function($scope) { g }, 317 | templateProvider: function($scope) { h }, 318 | onEnter: function($scope) { d }, 319 | onExit: function($scope) { e }, 320 | dontAlterMe: function(arg) { f }, 321 | }).state("myState2", { 322 | controller: function($scope) {}, 323 | }).state({ 324 | name: "myState3", 325 | controller: function($scope, simpleObj, promiseObj, translations) { c }, 326 | }); 327 | $urlRouterProvider.when("/", function($match) { a; }); 328 | $urlRouterProvider.otherwise("", function(a) { a; }); 329 | $urlRouterProvider.rule(function(a) { a; }).anything().when("/", function($location) { a; }); 330 | 331 | stateHelperProvider.setNestedState({ 332 | controller: function($scope, simpleObj, promiseObj, translations) { c }, 333 | 334 | children: [ 335 | { 336 | name: "a", 337 | controller: function(a) {}, 338 | resolve: { 339 | f: function($a) {}, 340 | }, 341 | children: [ 342 | { 343 | name: "ab", 344 | controller: function(ab) {}, 345 | resolve: { 346 | f: function($ab) {}, 347 | }, 348 | children: [ 349 | { 350 | name: "abc", 351 | controller: function(abc) {}, 352 | resolve: { 353 | f: function($abc) {}, 354 | }, 355 | }, 356 | ], 357 | }, 358 | ], 359 | }, 360 | { 361 | name: "b", 362 | controller: function(b) {}, 363 | views: { 364 | viewa: { 365 | controller: function($scope, myParam) {}, 366 | controllerProvider: function($stateParams) {}, 367 | templateProvider: function($scope) {}, 368 | dontAlterMe: function(arg) {}, 369 | resolve: { 370 | myParam: function($stateParams) { 371 | return $stateParams.paramFromDI; 372 | } 373 | }, 374 | }, 375 | viewb: { 376 | dontAlterMe: function(arg) {}, 377 | templateProvider: function($scope) {}, 378 | controller: function($scope) {}, 379 | }, 380 | dontAlterMe: null, 381 | }, 382 | }, 383 | ], 384 | }); 385 | stateHelperProvider.setNestedState({ 386 | controller: function($scope, simpleObj, promiseObj, translations) { c }, 387 | }, true); 388 | 389 | // angular ui / ui-bootstrap $modal 390 | $modal.open({ 391 | templateUrl: "str", 392 | controller: function($scope) {}, 393 | resolve: { 394 | items: function(MyService) {}, 395 | data: function(a, b) {}, 396 | its: 42, 397 | }, 398 | donttouch: function(me) {}, 399 | }); 400 | $uibModal.open({ 401 | templateUrl: "str", 402 | controller: function($scope) {}, 403 | resolve: { 404 | items: function(MyService) {}, 405 | data: function(a, b) {}, 406 | its: 42, 407 | }, 408 | donttouch: function(me) {}, 409 | }); 410 | 411 | // angular material design $mdBottomSheet, $mdDialog, $mdToast 412 | $mdDialog.show({ 413 | templateUrl: "str", 414 | controller: function($scope) {}, 415 | resolve: { 416 | items: function(MyService) {}, 417 | data: function(a, b) {}, 418 | its: 42, 419 | }, 420 | donttouch: function(me) {}, 421 | }); 422 | $mdBottomSheet.show({ 423 | templateUrl: "str", 424 | controller: function($scope) {}, 425 | resolve: { 426 | items: function(MyService) {}, 427 | data: function(a, b) {}, 428 | its: 42, 429 | }, 430 | donttouch: function(me) {}, 431 | }); 432 | $mdToast.show({ 433 | templateUrl: "str", 434 | controller: function($scope) {}, 435 | resolve: { 436 | items: function(MyService) {}, 437 | data: function(a, b) {}, 438 | its: 42, 439 | }, 440 | donttouch: function(me) {}, 441 | }); 442 | }); 443 | 444 | // none of the patterns below matches because they are not in an angular module context 445 | // this should be a straight copy of the code above, with identical copies in 446 | // with_annotations.js 447 | foobar.irrespective("dontmatchthis", function() { 448 | 449 | // $injector.invoke 450 | $injector.invoke(function($compile) { 451 | $compile(myElement)(scope); 452 | }); 453 | 454 | // $httpProvider 455 | $httpProvider.interceptors.push(function($scope) { a }); 456 | $httpProvider.responseInterceptors.push(function($scope) { a }, function(a, b) { b }, function() { c }); 457 | 458 | // $routeProvider 459 | $routeProvider.when("path", { 460 | controller: function($scope) { a } 461 | }).when("path2", { 462 | controller: function($scope) { b }, 463 | resolve: { 464 | zero: function() { a }, 465 | more: function($scope, $timeout) { b }, 466 | something: "else", 467 | }, 468 | dontAlterMe: function(arg) {}, 469 | }); 470 | 471 | // ui-router 472 | $stateProvider.state("myState", { 473 | resolve: { 474 | simpleObj: function() { a }, 475 | promiseObj: function($scope, $timeout) { b }, 476 | translations: "translations", 477 | }, 478 | views: { 479 | viewa: { 480 | controller: function($scope, myParam) {}, 481 | controllerProvider: function($stateParams) {}, 482 | templateProvider: function($scope) {}, 483 | dontAlterMe: function(arg) {}, 484 | resolve: { 485 | myParam: function($stateParams) { 486 | return $stateParams.paramFromDI; 487 | } 488 | }, 489 | }, 490 | viewb: { 491 | dontAlterMe: function(arg) {}, 492 | templateProvider: function($scope) {}, 493 | controller: function($scope) {}, 494 | }, 495 | dontAlterMe: null, 496 | }, 497 | controller: function($scope, simpleObj, promiseObj, translations) { c }, 498 | controllerProvider: function($scope) { g }, 499 | templateProvider: function($scope) { h }, 500 | onEnter: function($scope) { d }, 501 | onExit: function($scope) { e }, 502 | dontAlterMe: function(arg) { f }, 503 | }).state("myState2", { 504 | controller: function($scope) {}, 505 | }).state({ 506 | name: "myState3", 507 | controller: function($scope, simpleObj, promiseObj, translations) { c }, 508 | }); 509 | $urlRouterProvider.when("/", function($match) { a; }); 510 | $urlRouterProvider.otherwise("", function(a) { a; }); 511 | $urlRouterProvider.rule(function(a) { a; }).anything().when("/", function($location) { a; }); 512 | 513 | stateHelperProvider.setNestedState({ 514 | controller: function($scope, simpleObj, promiseObj, translations) { c }, 515 | 516 | children: [ 517 | { 518 | name: "a", 519 | controller: function(a) {}, 520 | resolve: { 521 | f: function($a) {}, 522 | }, 523 | children: [ 524 | { 525 | name: "ab", 526 | controller: function(ab) {}, 527 | resolve: { 528 | f: function($ab) {}, 529 | }, 530 | children: [ 531 | { 532 | name: "abc", 533 | controller: function(abc) {}, 534 | resolve: { 535 | f: function($abc) {}, 536 | }, 537 | }, 538 | ], 539 | }, 540 | ], 541 | }, 542 | { 543 | name: "b", 544 | controller: function(b) {}, 545 | views: { 546 | viewa: { 547 | controller: function($scope, myParam) {}, 548 | controllerProvider: function($stateParams) {}, 549 | templateProvider: function($scope) {}, 550 | dontAlterMe: function(arg) {}, 551 | resolve: { 552 | myParam: function($stateParams) { 553 | return $stateParams.paramFromDI; 554 | } 555 | }, 556 | }, 557 | viewb: { 558 | dontAlterMe: function(arg) {}, 559 | templateProvider: function($scope) {}, 560 | controller: function($scope) {}, 561 | }, 562 | dontAlterMe: null, 563 | }, 564 | }, 565 | ], 566 | }); 567 | stateHelperProvider.setNestedState({ 568 | controller: function($scope, simpleObj, promiseObj, translations) { c }, 569 | }, true); 570 | 571 | // angular ui / ui-bootstrap $modal 572 | $modal.open({ 573 | templateUrl: "str", 574 | controller: function($scope) {}, 575 | resolve: { 576 | items: function(MyService) {}, 577 | data: function(a, b) {}, 578 | its: 42, 579 | }, 580 | donttouch: function(me) {}, 581 | }); 582 | $uibModal.open({ 583 | templateUrl: "str", 584 | controller: function($scope) {}, 585 | resolve: { 586 | items: function(MyService) {}, 587 | data: function(a, b) {}, 588 | its: 42, 589 | }, 590 | donttouch: function(me) {}, 591 | }); 592 | 593 | // angular material design $mdBottomSheet, $mdDialog, $mdToast 594 | $mdDialog.show({ 595 | templateUrl: "str", 596 | controller: function($scope) {}, 597 | resolve: { 598 | items: function(MyService) {}, 599 | data: function(a, b) {}, 600 | its: 42, 601 | }, 602 | donttouch: function(me) {}, 603 | }); 604 | $mdBottomSheet.show({ 605 | templateUrl: "str", 606 | controller: function($scope) {}, 607 | resolve: { 608 | items: function(MyService) {}, 609 | data: function(a, b) {}, 610 | its: 42, 611 | }, 612 | donttouch: function(me) {}, 613 | }); 614 | $mdToast.show({ 615 | templateUrl: "str", 616 | controller: function($scope) {}, 617 | resolve: { 618 | items: function(MyService) {}, 619 | data: function(a, b) {}, 620 | its: 42, 621 | }, 622 | donttouch: function(me) {}, 623 | }); 624 | }); 625 | 626 | // explicit annotations 627 | var x = /* @ngInject */ function($scope) { 628 | }; 629 | 630 | var obj = {}; 631 | obj.bar = /*@ngInject*/ function($scope) {}; 632 | 633 | obj = { 634 | controller: /*@ngInject*/ function($scope) {}, 635 | }; 636 | 637 | obj = /*@ngInject*/ { 638 | foo: function(a) {}, 639 | bar: function(b, c) {}, 640 | val: 42, 641 | inner: { 642 | circle: function(d) {}, 643 | alalalala: "long", 644 | }, 645 | nest: { many: {levels: function(x) {}}}, 646 | but: { onlythrough: ["object literals", {donttouch: function(me) {}}]}, 647 | }; 648 | 649 | obj = { 650 | /*@ngInject*/ 651 | foo: function(a) {}, 652 | bar: function(b, c) {}, 653 | }; 654 | 655 | /*@ngInject*/ 656 | obj = { 657 | foo: function(a) {}, 658 | bar: function(b, c) {}, 659 | val: 42, 660 | inner: { 661 | circle: function(d) {}, 662 | alalalala: "long", 663 | }, 664 | nest: { many: {levels: function(x) {}}}, 665 | but: { onlythrough: ["object literals", {donttouch: function(me) {}}]}, 666 | }; 667 | 668 | /*@ngInject*/ 669 | var obj = { 670 | foo: function(a) {}, 671 | bar: function(b, c) {}, 672 | val: 42, 673 | inner: { 674 | circle: function(d) {}, 675 | alalalala: "long", 676 | }, 677 | nest: { many: {levels: function(x) {}}}, 678 | but: { onlythrough: ["object literals", {donttouch: function(me) {}}]}, 679 | }; 680 | 681 | // @ngInject 682 | function foo($scope) { 683 | } 684 | 685 | // @ngInject 686 | // otherstuff 687 | function Foo($scope) { 688 | } 689 | 690 | // @ngInject 691 | // has trailing semicolon 692 | var foo1 = function($scope) { 693 | }; 694 | 695 | // @ngInject 696 | // lacks trailing semicolon 697 | var foo2 = function($scope) { 698 | } 699 | 700 | // @ngInject 701 | // has trailing semicolon 702 | bar.foo1 = function($scope) { 703 | }; 704 | 705 | // @ngInject 706 | // lacks trailing semicolon 707 | bar.foo2 = function($scope) { 708 | } 709 | 710 | // let's zip-zag indentation to make sure that the $inject array lines up properly 711 | // @ngInject 712 | function foo3($scope) {} 713 | // @ngInject 714 | function foo4($scope) { 715 | } 716 | /* @ngInject */ function foo5($scope) {} 717 | /* @ngInject */ function foo6($scope) { 718 | } 719 | 720 | // @ngInject 721 | var foo7 = function($scope) { 722 | }; 723 | // @ngInject 724 | var foo8 = function($scope) {}; 725 | // @ngInject 726 | var foo9 = function($scope) { 727 | } 728 | // @ngInject 729 | var foo10 = function($scope) {} 730 | 731 | /* @ngInject */ var foo11 = function($scope) { 732 | }; 733 | /* @ngInject */var foo12 = function($scope) {}; 734 | /* @ngInject */var foo13 = function($scope) { 735 | } 736 | /* @ngInject */var foo14 = function($scope) {} 737 | 738 | 739 | // adding an explicit annotation where it isn't needed should work fine 740 | myMod.controller("foo", /*@ngInject*/ function($scope, $timeout) { 741 | }); 742 | 743 | 744 | // troublesome return forces different placement of $inject array 745 | function outer() { 746 | foo; 747 | return { 748 | controller: MyCtrl, 749 | }; 750 | 751 | // @ngInject 752 | function MyCtrl(a) { 753 | } 754 | } 755 | 756 | 757 | // explicit annotations using ngInject() instead of /*@ngInject*/ 758 | var x = ngInject(function($scope) {}); 759 | 760 | obj = ngInject({ 761 | foo: function(a) {}, 762 | bar: function(b, c) {}, 763 | val: 42, 764 | inner: { 765 | circle: function(d) {}, 766 | alalalala: "long", 767 | }, 768 | nest: { many: {levels: function(x) {}}}, 769 | but: { onlythrough: ["object literals", {donttouch: function(me) {}}]}, 770 | }); 771 | 772 | 773 | // explicit annotations using "ngInject" Directive Prologue 774 | function Foo2($scope) { 775 | "ngInject"; 776 | } 777 | 778 | var foos3 = function($scope) { 779 | // comments are ok before the Directive Prologues 780 | // and there may be multiple Prologues 781 | "use strict"; "ngInject"; 782 | }; 783 | 784 | var dual1 = function(a) { "ngInject" }, dual2 = function(b) { "ngInject" }; 785 | 786 | g(function(c) { 787 | "ngInject" 788 | }); 789 | 790 | // Traceur class output example 791 | // class C { 792 | // constructor($scope) { 793 | // "ngInject" 794 | // } 795 | // } 796 | $traceurRuntime.ModuleStore.getAnonymousModule(function() { 797 | "use strict"; 798 | var C = function C($scope) { 799 | "ngInject"; 800 | }; 801 | ($traceurRuntime.createClass)(C, {}, {}); 802 | return {}; 803 | }); 804 | 805 | 806 | // suppress false positives with /*@ngNoInject*/, ngNoInject() and "ngNoInject" 807 | myMod.controller("suppressed", /*@ngNoInject*/function($scope) { 808 | }); 809 | myMod.controller("suppressed", ngNoInject(function($scope) { 810 | })); 811 | myMod.controller("suppressed", function($scope) { 812 | "ngNoInject"; 813 | }); 814 | 815 | // works the same as ngInject i.e. reference-following, IIFE-jumping and so on 816 | /*@ngNoInject*/ 817 | myMod.controller("suppressed", SupFoo1); 818 | myMod.controller("suppressed", SupFoo2); 819 | myMod.controller("suppressed", SupFoo3); 820 | function SupFoo1($scope) { 821 | "ngNoInject"; 822 | } 823 | /*@ngNoInject*/ 824 | function SupFoo2($scope) { 825 | } 826 | var SupFoo3 = ngNoInject(function($scope) { 827 | "ngNoInject"; 828 | }); 829 | 830 | 831 | // regression-test for https://github.com/olov/ng-annotate/issues/221 832 | var FooBar = (function (_super) { 833 | __extends(FooBar, _super); 834 | /*@ngInject*/ 835 | function FooBar($a, $b) { 836 | _super.call(this); 837 | } 838 | /*@ngInject*/ 839 | FooBar.onEnter = function (callback) { 840 | x; 841 | }; 842 | return FooBar; 843 | })(Bar); 844 | var FooBar2 = (function (_super) { 845 | __extends(FooBar, _super); 846 | function FooBar($a, $b) { 847 | "ngInject"; 848 | _super.call(this); 849 | } 850 | FooBar.onEnter = function (callback) { 851 | "ngInject"; 852 | x; 853 | }; 854 | return FooBar; 855 | })(Bar); 856 | 857 | // snippets that shouldn't fool ng-annotate into generating false positives, 858 | // whether we're inside an angular module or not 859 | myMod.controller("donttouchme", function() { 860 | // lo-dash regression that happened in the brief time frame when 861 | // notes (instad of "notes") would match. see issue #22 862 | var notesForCurrentPage = _.filter(notes, function (note) { 863 | return note.page.uid === page.uid; 864 | }); 865 | }); 866 | 867 | // not a module declaration short-form, see https://github.com/olov/ng-annotate/issues/82 868 | $stateProvider.decorator('parent', function (state, parentFn) { 869 | doStuff(); 870 | }); 871 | 872 | // $get is only valid inside provider 873 | myMod.service("donttouch", function() { 874 | this.$get = function(me) { 875 | }; 876 | }); 877 | myMod.service("donttouch", mefn); 878 | function mefn() { 879 | this.$get = function(me) { 880 | }; 881 | } 882 | 883 | // directive return object is only valid inside directive 884 | myMod.service("donttouch", function() { 885 | return { 886 | controller: function($scope, $timeout) { 887 | bar; 888 | } 889 | } 890 | }); 891 | 892 | myMod.directive("donttouch", function() { 893 | foo.decorator("me", function($scope) { 894 | }); 895 | }); 896 | 897 | // IIFE-jumping (primarily for compile-to-JS langs) 898 | angular.module("MyMod").directive("foo", function($a, $b) { 899 | $uibModal.open({ 900 | resolve: { 901 | collection: (function(_this) { 902 | return function($c) { 903 | }; 904 | })(this), 905 | }, 906 | }); 907 | }); 908 | 909 | var x = /*@ngInject*/ (function() { 910 | return function($a) { 911 | }; 912 | })(); 913 | 914 | 915 | // IIFE-jumping with reference support 916 | var myCtrl = (function () { 917 | return function($scope) { 918 | }; 919 | })(); 920 | angular.module("MyMod").controller("MyCtrl", myCtrl); 921 | 922 | 923 | // advanced IIFE-jumping (with reference support) 924 | var myCtrl10 = (function() { 925 | "use strict"; 926 | // the return statement can appear anywhere on the functions topmost level, 927 | // including before the myCtrl function definition 928 | return myCtrl; 929 | function myCtrl($scope) { 930 | foo; 931 | } 932 | post; 933 | })(); 934 | angular.module("MyMod").controller("MyCtrl", myCtrl10); 935 | 936 | var myCtrl11 = (function() { 937 | pre; 938 | var myCtrl = function($scope) { 939 | foo; 940 | }; 941 | mid; 942 | // the return statement can appear anywhere on the functions topmost level, 943 | // including before the myCtrl function definition 944 | return myCtrl; 945 | post; 946 | })(); 947 | angular.module("MyMod").controller("MyCtrl", myCtrl11); 948 | 949 | 950 | // reference support 951 | function MyCtrl1(a, b) { 952 | } 953 | if (true) { 954 | // proper scope analysis including shadowing 955 | let MyCtrl1 = function(c) { 956 | }; 957 | angular.module("MyMod").directive("foo", MyCtrl1); 958 | } 959 | angular.module("MyMod").controller("bar", MyCtrl1); 960 | function MyCtrl2(z) { 961 | } 962 | funcall(/*@ngInject*/ MyCtrl2); // explicit annotation on reference flows back to definition 963 | 964 | angular.module("MyMod").directive("foo", MyDirective); 965 | 966 | function MyDirective($stateProvider) { 967 | $stateProvider.state('astate', { 968 | resolve: { 969 | yoyo: function(ma) { 970 | }, 971 | } 972 | }); 973 | } 974 | 975 | /* @ngInject */ 976 | function MyDirective2($stateProvider) { 977 | $stateProvider.state('astate', { 978 | resolve: { 979 | yoyo: function(ma) { 980 | }, 981 | } 982 | }); 983 | } 984 | 985 | // issue 84 986 | (function() { 987 | var MyCtrl = function($someDependency) {}; 988 | angular.module('myApp').controller("MyCtrl", MyCtrl); 989 | MyCtrl.prototype.someFunction = function() {}; 990 | })(); 991 | 992 | // empty var declarator 993 | var MyCtrl12; 994 | angular.module("MyMod").controller('MyCtrl', MyCtrl12); 995 | 996 | // issue 115 997 | module.exports = function() { 998 | "use strict"; 999 | return { 1000 | restrict: 'E', 1001 | replace: true, 1002 | scope: { }, 1003 | controller: /*@ngInject*/function($scope, myService) { 1004 | }, 1005 | templateUrl: "mytemplate" 1006 | }; 1007 | }; 1008 | 1009 | // issue #135 1010 | var MyCtrl = (function() { 1011 | /*@ngInject*/ 1012 | function MyCtrl(a) { 1013 | } 1014 | 1015 | return MyCtrl; 1016 | })(); 1017 | 1018 | myMod.service("a", MyCtrl); 1019 | -------------------------------------------------------------------------------- /tests/rename.annotated.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // long form 4 | angular.module("MyMod").controller("MyCtrl", ["$aRenamed", "$bRenamed", function($a, $b) { 5 | }]); 6 | 7 | // w/ dependencies 8 | angular.module("MyMod", ["OtherMod"]).controller("MyCtrl", ["$cRenamed", "$dRenamed", "$eRenamed", "$fRenamed", "$gRenamed", "$hRenamed", "$iRenamed", function($c, $d, $e, $f, $g, $h, $i) { 9 | }]); 10 | 11 | // simple 12 | myMod.service("$aRenamed", ["$bRenamed", function($b) { 13 | }]); 14 | myMod.controller("foo", ["$aRenamed", "$bRenamed", function($a, $b) { 15 | }]); 16 | myMod.service("foo", ["$cRenamed", "$dRenamed", function($c, $d) { 17 | }]); 18 | myMod.factory("foo", ["$eRenamed", "$fRenamed", function($e, $f) { 19 | }]); 20 | myMod.directive("foo", ["$gRenamed", "$hRenamed", function($g, $h) { 21 | }]); 22 | myMod.filter("foo", ["$iRenamed", "$aRenamed", function($i, $a) { 23 | }]); 24 | myMod.animation("foo", ["$bRenamed", "$cRenamed", function($b, $c) { 25 | }]); 26 | myMod.invoke("foo", ["$dRenamed", "$eRenamed", function($d, $e) { 27 | }]); 28 | 29 | // implicit config function 30 | angular.module("MyMod", ["OtherMod"], ["$interpolateProvider", function($interpolateProvider) {}]).controller("foo", ["$fRenamed", function($f) {}]); 31 | 32 | // object property 33 | var myObj = {}; 34 | myObj.myMod = angular.module("MyMod"); 35 | myObj.myMod.controller("foo", ["$gRenamed", "$hRenamed", function($g, $h) { a }]); 36 | 37 | // run, config don't take names 38 | myMod.run(["$iRenamed", "$aRenamed", function($i, $a) { 39 | }]); 40 | angular.module("MyMod").run(["$bRenamed", function($b) { 41 | }]); 42 | myMod.config(["$cRenamed", "$dRenamed", function($c, $d) { 43 | }]); 44 | angular.module("MyMod").config(function() { 45 | }); 46 | 47 | // directive return object 48 | myMod.directive("foo", ["$eRenamed", function($e) { 49 | return { 50 | controller: ["$fRenamed", "$gRenamed", function($f, $g) { 51 | bar; 52 | }] 53 | } 54 | }]); 55 | myMod.directive("foo", ["$hRenamed", function($h) { 56 | return { 57 | controller: function() { 58 | bar; 59 | } 60 | } 61 | }]); 62 | 63 | // provider, provider $get 64 | myMod.provider("foo", ["$iRenamed", function($i) { 65 | this.$get = ["$aRenamed", "$bRenamed", function($a, $b) { 66 | bar; 67 | }]; 68 | self.$get = ["$cRenamed", function($c) {}]; 69 | that.$get = ["$dRenamed", function($d) {}]; 70 | ignore.$get = function($e) {}; 71 | }]); 72 | myMod.provider("foo", function() { 73 | this.$get = ["$fRenamed", function($f) { 74 | bar; 75 | }]; 76 | }); 77 | myMod.provider("foo", function() { 78 | return { 79 | $get: ["$gRenamed", "$hRenamed", function($g, $h) { 80 | bar; 81 | }]}; 82 | }); 83 | myMod.provider("foo", { 84 | $get: ["$iRenamed", function($i) { 85 | bar; 86 | }] 87 | }); 88 | -------------------------------------------------------------------------------- /tests/rename.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // long form 4 | angular.module("MyMod").controller("MyCtrl", function($a, $b) { 5 | }); 6 | 7 | // w/ dependencies 8 | angular.module("MyMod", ["OtherMod"]).controller("MyCtrl", function($c, $d, $e, $f, $g, $h, $i) { 9 | }); 10 | 11 | // simple 12 | myMod.service("$a", function($b) { 13 | }); 14 | myMod.controller("foo", function($a, $b) { 15 | }); 16 | myMod.service("foo", function($c, $d) { 17 | }); 18 | myMod.factory("foo", function($e, $f) { 19 | }); 20 | myMod.directive("foo", function($g, $h) { 21 | }); 22 | myMod.filter("foo", function($i, $a) { 23 | }); 24 | myMod.animation("foo", function($b, $c) { 25 | }); 26 | myMod.invoke("foo", function($d, $e) { 27 | }); 28 | 29 | // implicit config function 30 | angular.module("MyMod", ["OtherMod"], function($interpolateProvider) {}).controller("foo", function($f) {}); 31 | 32 | // object property 33 | var myObj = {}; 34 | myObj.myMod = angular.module("MyMod"); 35 | myObj.myMod.controller("foo", function($g, $h) { a }); 36 | 37 | // run, config don't take names 38 | myMod.run(function($i, $a) { 39 | }); 40 | angular.module("MyMod").run(function($b) { 41 | }); 42 | myMod.config(function($c, $d) { 43 | }); 44 | angular.module("MyMod").config(function() { 45 | }); 46 | 47 | // directive return object 48 | myMod.directive("foo", function($e) { 49 | return { 50 | controller: function($f, $g) { 51 | bar; 52 | } 53 | } 54 | }); 55 | myMod.directive("foo", function($h) { 56 | return { 57 | controller: function() { 58 | bar; 59 | } 60 | } 61 | }); 62 | 63 | // provider, provider $get 64 | myMod.provider("foo", function($i) { 65 | this.$get = function($a, $b) { 66 | bar; 67 | }; 68 | self.$get = function($c) {}; 69 | that.$get = function($d) {}; 70 | ignore.$get = function($e) {}; 71 | }); 72 | myMod.provider("foo", function() { 73 | this.$get = function($f) { 74 | bar; 75 | }; 76 | }); 77 | myMod.provider("foo", function() { 78 | return { 79 | $get: function($g, $h) { 80 | bar; 81 | }}; 82 | }); 83 | myMod.provider("foo", { 84 | $get: function($i) { 85 | bar; 86 | } 87 | }); 88 | -------------------------------------------------------------------------------- /tests/sourcemaps.annotated.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var ctrl4, x; 3 | 4 | x = "before"; 5 | 6 | myMod.controller("ctrl1", ["ctrl1_param1", "ctrl1_param2", function(ctrl1_param1, ctrl1_param2) { 7 | return x = "ctrl1 body"; 8 | }]); 9 | 10 | myMod.controller("ctrl2", ["ctrl2_param1", "ctrl2_param2", function(ctrl2_param1, ctrl2_param2) { 11 | return x = "ctrl2 body"; 12 | } 13 | ]); 14 | 15 | myMod.controller("ctrl3", ["ctrl3_param1", "ctrl3_param2", function(ctrl3_param1, ctrl3_param2) { 16 | return x = "ctrl3 body"; 17 | } 18 | ]); 19 | 20 | 21 | /* @ngInject */ 22 | 23 | ctrl4 = function(ctrl4_param1, ctrl4_param2) { 24 | return x = "ctrl4 body"; 25 | }; 26 | ctrl4.$inject = ["ctrl4_param1", "ctrl4_param2"]; 27 | 28 | x = "after"; 29 | 30 | }).call(this); 31 | -------------------------------------------------------------------------------- /tests/sourcemaps.coffee: -------------------------------------------------------------------------------- 1 | x = "before" 2 | 3 | myMod.controller "ctrl1", (ctrl1_param1, ctrl1_param2) -> 4 | x = "ctrl1 body" 5 | 6 | myMod.controller "ctrl2", ["remove", "me", (ctrl2_param1, ctrl2_param2) -> 7 | x = "ctrl2 body" 8 | ] 9 | 10 | myMod.controller "ctrl3", [ 11 | "remove", 12 | "me", 13 | (ctrl3_param1, ctrl3_param2) -> 14 | x = "ctrl3 body" 15 | ] 16 | 17 | ### @ngInject ### 18 | ctrl4 = (ctrl4_param1, ctrl4_param2) -> 19 | x = "ctrl4 body" 20 | 21 | x = "after" -------------------------------------------------------------------------------- /tests/todo.js: -------------------------------------------------------------------------------- 1 | // this should preferably only add one of the inline array and $inject array 2 | // low prio to fix for now 3 | var foobar = (function() { 4 | return function(b) { 5 | "ngInject"; 6 | }; 7 | })(); 8 | myMod.service("b", foobar); 9 | -------------------------------------------------------------------------------- /tests/with_annotations.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // long form 4 | foo.$inject = ["$scope"]; 5 | Foo.$inject = ["$scope"]; 6 | foo3.$inject = ["$scope"]; 7 | foo4.$inject = ["$scope"]; 8 | foo5.$inject = ["$scope"]; 9 | foo6.$inject = ["$scope"]; 10 | MyCtrl2.$inject = ["z"]; 11 | MyDirective2.$inject = ["$stateProvider"]; 12 | extprov.$inject = ["x"]; 13 | fooget.$inject = ["b"]; 14 | fooget2.$inject = ["c"]; 15 | Foo2.$inject = ["$scope"]; 16 | MyCtrl1.$inject = ["a", "b"]; 17 | MyDirective.$inject = ["$stateProvider"]; 18 | angular.module("MyMod").controller("MyCtrl", ["$scope", "$timeout", function($scope, $timeout) { 19 | }]); 20 | 21 | // w/ dependencies 22 | angular.module("MyMod", ["OtherMod"]).controller("MyCtrl", ["$scope", "$timeout", function($scope, $timeout) { 23 | }]); 24 | 25 | // simple 26 | myMod.controller("foo", ["$scope", "$timeout", function($scope, $timeout) { 27 | }]); 28 | myMod.service("foo", ["$scope", "$timeout", function($scope, $timeout) { 29 | }]); 30 | myMod.factory("foo", ["$scope", "$timeout", function($scope, $timeout) { 31 | }]); 32 | myMod.directive("foo", ["$scope", "$timeout", function($scope, $timeout) { 33 | }]); 34 | myMod.filter("foo", ["$scope", "$timeout", function($scope, $timeout) { 35 | }]); 36 | myMod.animation("foo", ["$scope", "$timeout", function($scope, $timeout) { 37 | }]); 38 | myMod.invoke("foo", ["$scope", "$timeout", function($scope, $timeout) { 39 | }]); 40 | myMod.store("foo", ["$scope", "$timeout", function($scope, $timeout) { 41 | }]); 42 | myMod.decorator("foo", ["$scope", "$timeout", function($scope, $timeout) { 43 | }]); 44 | myMod.component("foo", {controller: ["$scope", "$timeout", function($scope, $timeout) {}]}); 45 | 46 | // implicit config function 47 | angular.module("MyMod", ["$interpolateProvider", function($interpolateProvider) {}]); 48 | angular.module("MyMod", ["OtherMod"], ["$interpolateProvider", function($interpolateProvider) {}]); 49 | angular.module("MyMod", ["OtherMod"], ["$interpolateProvider", function($interpolateProvider) {}]).controller("foo", ["$scope", function($scope) {}]); 50 | 51 | // object property 52 | var myObj = {}; 53 | myObj.myMod = angular.module("MyMod"); 54 | myObj.myMod.controller("foo", ["$scope", "$timeout", function($scope, $timeout) { a }]); 55 | 56 | // no dependencies => no need to wrap the function in an array 57 | myMod.controller("foo", function() { 58 | }); 59 | myMod.service("foo", function() { 60 | }); 61 | myMod.factory("foo", function() { 62 | }); 63 | myMod.directive("foo", function() { 64 | }); 65 | myMod.filter("foo", function() { 66 | }); 67 | myMod.animation("foo", function() { 68 | }); 69 | myMod.invoke("foo", function() { 70 | }); 71 | myMod.store("foo", function() { 72 | }); 73 | myMod.decorator("foo", function() { 74 | }); 75 | myMod.component("foo", {controller: function() {}}); 76 | 77 | // run, config don't take names 78 | myMod.run(["$scope", "$timeout", function($scope, $timeout) { 79 | }]); 80 | angular.module("MyMod").run(["$scope", function($scope) { 81 | }]); 82 | myMod.config(["$scope", "$timeout", function($scope, $timeout) { 83 | }]); 84 | angular.module("MyMod").config(function() { 85 | }); 86 | 87 | // directive return object 88 | myMod.directive("foo", ["$scope", function($scope) { 89 | return { 90 | controller: ["$scope", "$timeout", function($scope, $timeout) { 91 | bar; 92 | }] 93 | } 94 | }]); 95 | myMod.directive("foo", ["$scope", function($scope) { 96 | return { 97 | controller: function() { 98 | bar; 99 | } 100 | } 101 | }]); 102 | 103 | // provider, provider $get 104 | myMod.provider("foo", ["$scope", function($scope) { 105 | this.$get = ["$scope", "$timeout", function($scope, $timeout) { 106 | bar; 107 | }]; 108 | self.$get = ["$scope", function($scope) {}]; 109 | that.$get = ["$scope", function($scope) {}]; 110 | ignore.$get = function($scope) {}; 111 | }]); 112 | myMod.provider("foo", function() { 113 | this.$get = function() { 114 | bar; 115 | }; 116 | }); 117 | myMod.provider("foo", function() { 118 | return { 119 | $get: ["$scope", "$timeout", function($scope, $timeout) { 120 | bar; 121 | }]}; 122 | }); 123 | myMod.provider("foo", function() { 124 | return { 125 | $get: function() { 126 | bar; 127 | }}; 128 | }); 129 | myMod.provider("foo", { 130 | $get: ["$scope", "$timeout", function($scope, $timeout) { 131 | bar; 132 | }] 133 | }); 134 | myMod.provider("foo", { 135 | $get: function() { 136 | bar; 137 | } 138 | }); 139 | myMod.provider("foo", { 140 | "$get": ["$scope", "$timeout", function($scope, $timeout) { 141 | bar; 142 | }] 143 | }); 144 | myMod.provider("foo", { 145 | '$get': ["$scope", "$timeout", function($scope, $timeout) { 146 | bar; 147 | }] 148 | }); 149 | 150 | myMod.provider("foo", ["x", function(x) { 151 | this.$get = ["a", "b", function(a,b) {}]; 152 | }]); 153 | 154 | myMod.provider("foo", extprov); 155 | function extprov(x) { 156 | inner.$inject = ["c", "d"]; 157 | this.$get = ["a", "b", function(a,b) {}]; 158 | this.$get = fooget; 159 | this.$get = inner; 160 | 161 | function inner(c, d) { 162 | } 163 | } 164 | 165 | function fooget(b) { 166 | this.$get = fooget2; 167 | } 168 | 169 | function fooget2(c) { 170 | } 171 | 172 | // chaining 173 | myMod.directive("foo", ["$a", "$b", function($a, $b) { 174 | a; 175 | }]).factory("foo", function() { 176 | b; 177 | }).config(["$c", function($c) { 178 | c; 179 | }]).filter("foo", ["$d", "$e", function($d, $e) { 180 | d; 181 | }]).animation("foo", ["$f", "$g", function($f, $g) { 182 | e; 183 | }]).component("foo", {controller: ["$scope", "$timeout", function($scope, $timeout) { 184 | i; 185 | }]}).invoke("foo", ["$f", "$g", function($f, $g) { 186 | f; 187 | }]).decorator("foo", ["$f", "$g", function($f, $g) { 188 | g; 189 | }]).store("foo", ["$f", "$g", function($f, $g) { 190 | h; 191 | }]); 192 | 193 | angular.module("MyMod").directive("foo", ["$a", "$b", function($a, $b) { 194 | a; 195 | }]).provider("foo", function() { 196 | return { 197 | $get: ["$scope", "$timeout", function($scope, $timeout) { 198 | bar; 199 | }]}; 200 | }).value("foo", "bar") 201 | .constant("foo", "bar") 202 | .bootstrap(element, [], {}) 203 | .factory("foo", function() { 204 | b; 205 | }).config(["$c", function($c) { 206 | c; 207 | }]).filter("foo", ["$d", "$e", function($d, $e) { 208 | d; 209 | }]).animation("foo", ["$f", "$g", function($f, $g) { 210 | e; 211 | }]).component("foo", {controller: ["$scope", "$timeout", function($scope, $timeout) { 212 | i; 213 | }]}).invoke("foo", ["$h", "$i", function($h, $i) { 214 | f; 215 | }]).decorator("foo", ["$h", "$i", function($h, $i) { 216 | g; 217 | }]).store("foo", ["$f", "$g", function($f, $g) { 218 | h; 219 | }]); 220 | 221 | // $provide 222 | angular.module("myMod").controller("foo", function() { 223 | $provide.decorator("foo", ["$scope", function($scope) {}]); 224 | $provide.service("foo", ["$scope", function($scope) {}]); 225 | $provide.factory("foo", ["$scope", function($scope) {}]); 226 | //$provide.provider 227 | $provide.provider("foo", ["$scope", function($scope) { 228 | this.$get = ["$scope", function($scope) {}]; 229 | return { $get: ["$scope", "$timeout", function($scope, $timeout) {}]}; 230 | }]); 231 | $provide.provider("foo", { 232 | $get: ["$scope", "$timeout", function($scope, $timeout) {}] 233 | }); 234 | }); 235 | // negative $provide 236 | function notInContext() { 237 | $provide.decorator("foo", function($scope) {}); 238 | $provide.service("foo", function($scope) {}); 239 | $provide.factory("foo", function($scope) {}); 240 | $provide.provider("foo", function($scope) { 241 | this.$get = function($scope) {}; 242 | return { $get: function($scope, $timeout) {}}; 243 | }); 244 | $provide.provider("foo", { 245 | $get: function($scope, $timeout) {} 246 | }); 247 | } 248 | 249 | // $controllerProvider 250 | angular.module("myMod").controller("foo", function() { 251 | $controllerProvider.register("foo", ["$scope", function($scope) {}]); 252 | }); 253 | function notInContext() { 254 | $controllerProvider.register("foo", function($scope) {}); 255 | } 256 | 257 | // special handling for TypeScript __extends 258 | function outer1() { 259 | a; 260 | __extends(); 261 | foo.$inject = ["$a"]; 262 | b; 263 | function foo($a) { 264 | "ngInject"; 265 | } 266 | } 267 | function outer2() { 268 | foo.$inject = ["$a"]; 269 | a; 270 | __not_extends(); 271 | b; 272 | function foo($a) { 273 | "ngInject"; 274 | } 275 | } 276 | 277 | // all the patterns below matches only when we're inside a detected angular module 278 | angular.module("MyMod").directive("pleasematchthis", function() { 279 | 280 | // $injector.invoke 281 | $injector.invoke(["$compile", function($compile) { 282 | $compile(myElement)(scope); 283 | }]); 284 | 285 | // $httpProvider 286 | $httpProvider.interceptors.push(["$scope", function($scope) { a }]); 287 | $httpProvider.responseInterceptors.push(["$scope", function($scope) { a }], ["a", "b", function(a, b) { b }], function() { c }); 288 | 289 | // $routeProvider 290 | $routeProvider.when("path", { 291 | controller: ["$scope", function($scope) { a }] 292 | }).when("path2", { 293 | controller: ["$scope", function($scope) { b }], 294 | resolve: { 295 | zero: function() { a }, 296 | more: ["$scope", "$timeout", function($scope, $timeout) { b }], 297 | something: "else", 298 | }, 299 | dontAlterMe: function(arg) {}, 300 | }); 301 | 302 | // ui-router 303 | $stateProvider.state("myState", { 304 | resolve: { 305 | simpleObj: function() { a }, 306 | promiseObj: ["$scope", "$timeout", function($scope, $timeout) { b }], 307 | translations: "translations", 308 | }, 309 | params: { 310 | simple: ["$scope", function($scope) {}], 311 | inValue: { value: ["$scope", function($scope) {}], notThis: function($scope) {} }, 312 | }, 313 | views: { 314 | viewa: { 315 | controller: ["$scope", "myParam", function($scope, myParam) {}], 316 | controllerProvider: ["$stateParams", function($stateParams) {}], 317 | templateProvider: ["$scope", function($scope) {}], 318 | dontAlterMe: function(arg) {}, 319 | resolve: { 320 | myParam: ["$stateParams", function($stateParams) { 321 | return $stateParams.paramFromDI; 322 | }] 323 | }, 324 | }, 325 | viewb: { 326 | dontAlterMe: function(arg) {}, 327 | templateProvider: ["$scope", function($scope) {}], 328 | controller: ["$scope", function($scope) {}], 329 | }, 330 | dontAlterMe: null, 331 | }, 332 | controller: ["$scope", "simpleObj", "promiseObj", "translations", function($scope, simpleObj, promiseObj, translations) { c }], 333 | controllerProvider: ["$scope", function($scope) { g }], 334 | templateProvider: ["$scope", function($scope) { h }], 335 | onEnter: ["$scope", function($scope) { d }], 336 | onExit: ["$scope", function($scope) { e }], 337 | dontAlterMe: function(arg) { f }, 338 | }).state("myState2", { 339 | controller: ["$scope", function($scope) {}], 340 | }).state({ 341 | name: "myState3", 342 | controller: ["$scope", "simpleObj", "promiseObj", "translations", function($scope, simpleObj, promiseObj, translations) { c }], 343 | }); 344 | $urlRouterProvider.when("/", ["$match", function($match) { a; }]); 345 | $urlRouterProvider.otherwise("", function(a) { a; }); 346 | $urlRouterProvider.rule(function(a) { a; }).anything().when("/", ["$location", function($location) { a; }]); 347 | 348 | stateHelperProvider.setNestedState({ 349 | controller: ["$scope", "simpleObj", "promiseObj", "translations", function($scope, simpleObj, promiseObj, translations) { c }], 350 | 351 | children: [ 352 | { 353 | name: "a", 354 | controller: ["a", function(a) {}], 355 | resolve: { 356 | f: ["$a", function($a) {}], 357 | }, 358 | children: [ 359 | { 360 | name: "ab", 361 | controller: ["ab", function(ab) {}], 362 | resolve: { 363 | f: ["$ab", function($ab) {}], 364 | }, 365 | children: [ 366 | { 367 | name: "abc", 368 | controller: ["abc", function(abc) {}], 369 | resolve: { 370 | f: ["$abc", function($abc) {}], 371 | }, 372 | }, 373 | ], 374 | }, 375 | ], 376 | }, 377 | { 378 | name: "b", 379 | controller: ["b", function(b) {}], 380 | views: { 381 | viewa: { 382 | controller: ["$scope", "myParam", function($scope, myParam) {}], 383 | controllerProvider: ["$stateParams", function($stateParams) {}], 384 | templateProvider: ["$scope", function($scope) {}], 385 | dontAlterMe: function(arg) {}, 386 | resolve: { 387 | myParam: ["$stateParams", function($stateParams) { 388 | return $stateParams.paramFromDI; 389 | }] 390 | }, 391 | }, 392 | viewb: { 393 | dontAlterMe: function(arg) {}, 394 | templateProvider: ["$scope", function($scope) {}], 395 | controller: ["$scope", function($scope) {}], 396 | }, 397 | dontAlterMe: null, 398 | }, 399 | }, 400 | ], 401 | }); 402 | stateHelperProvider.setNestedState({ 403 | controller: ["$scope", "simpleObj", "promiseObj", "translations", function($scope, simpleObj, promiseObj, translations) { c }], 404 | }, true); 405 | 406 | // angular ui / ui-bootstrap $modal 407 | $modal.open({ 408 | templateUrl: "str", 409 | controller: ["$scope", function($scope) {}], 410 | resolve: { 411 | items: ["MyService", function(MyService) {}], 412 | data: ["a", "b", function(a, b) {}], 413 | its: 42, 414 | }, 415 | donttouch: function(me) {}, 416 | }); 417 | $uibModal.open({ 418 | templateUrl: "str", 419 | controller: ["$scope", function($scope) {}], 420 | resolve: { 421 | items: ["MyService", function(MyService) {}], 422 | data: ["a", "b", function(a, b) {}], 423 | its: 42, 424 | }, 425 | donttouch: function(me) {}, 426 | }); 427 | 428 | // angular material design $mdBottomSheet, $mdDialog, $mdToast 429 | $mdDialog.show({ 430 | templateUrl: "str", 431 | controller: ["$scope", function($scope) {}], 432 | resolve: { 433 | items: ["MyService", function(MyService) {}], 434 | data: ["a", "b", function(a, b) {}], 435 | its: 42, 436 | }, 437 | donttouch: function(me) {}, 438 | }); 439 | $mdBottomSheet.show({ 440 | templateUrl: "str", 441 | controller: ["$scope", function($scope) {}], 442 | resolve: { 443 | items: ["MyService", function(MyService) {}], 444 | data: ["a", "b", function(a, b) {}], 445 | its: 42, 446 | }, 447 | donttouch: function(me) {}, 448 | }); 449 | $mdToast.show({ 450 | templateUrl: "str", 451 | controller: ["$scope", function($scope) {}], 452 | resolve: { 453 | items: ["MyService", function(MyService) {}], 454 | data: ["a", "b", function(a, b) {}], 455 | its: 42, 456 | }, 457 | donttouch: function(me) {}, 458 | }); 459 | }); 460 | 461 | // none of the patterns below matches because they are not in an angular module context 462 | // this should be a straight copy of the code above, with identical copies in 463 | // with_annotations.js 464 | foobar.irrespective("dontmatchthis", function() { 465 | 466 | // $injector.invoke 467 | $injector.invoke(function($compile) { 468 | $compile(myElement)(scope); 469 | }); 470 | 471 | // $httpProvider 472 | $httpProvider.interceptors.push(function($scope) { a }); 473 | $httpProvider.responseInterceptors.push(function($scope) { a }, function(a, b) { b }, function() { c }); 474 | 475 | // $routeProvider 476 | $routeProvider.when("path", { 477 | controller: function($scope) { a } 478 | }).when("path2", { 479 | controller: function($scope) { b }, 480 | resolve: { 481 | zero: function() { a }, 482 | more: function($scope, $timeout) { b }, 483 | something: "else", 484 | }, 485 | dontAlterMe: function(arg) {}, 486 | }); 487 | 488 | // ui-router 489 | $stateProvider.state("myState", { 490 | resolve: { 491 | simpleObj: function() { a }, 492 | promiseObj: function($scope, $timeout) { b }, 493 | translations: "translations", 494 | }, 495 | views: { 496 | viewa: { 497 | controller: function($scope, myParam) {}, 498 | controllerProvider: function($stateParams) {}, 499 | templateProvider: function($scope) {}, 500 | dontAlterMe: function(arg) {}, 501 | resolve: { 502 | myParam: function($stateParams) { 503 | return $stateParams.paramFromDI; 504 | } 505 | }, 506 | }, 507 | viewb: { 508 | dontAlterMe: function(arg) {}, 509 | templateProvider: function($scope) {}, 510 | controller: function($scope) {}, 511 | }, 512 | dontAlterMe: null, 513 | }, 514 | controller: function($scope, simpleObj, promiseObj, translations) { c }, 515 | controllerProvider: function($scope) { g }, 516 | templateProvider: function($scope) { h }, 517 | onEnter: function($scope) { d }, 518 | onExit: function($scope) { e }, 519 | dontAlterMe: function(arg) { f }, 520 | }).state("myState2", { 521 | controller: function($scope) {}, 522 | }).state({ 523 | name: "myState3", 524 | controller: function($scope, simpleObj, promiseObj, translations) { c }, 525 | }); 526 | $urlRouterProvider.when("/", function($match) { a; }); 527 | $urlRouterProvider.otherwise("", function(a) { a; }); 528 | $urlRouterProvider.rule(function(a) { a; }).anything().when("/", function($location) { a; }); 529 | 530 | stateHelperProvider.setNestedState({ 531 | controller: function($scope, simpleObj, promiseObj, translations) { c }, 532 | 533 | children: [ 534 | { 535 | name: "a", 536 | controller: function(a) {}, 537 | resolve: { 538 | f: function($a) {}, 539 | }, 540 | children: [ 541 | { 542 | name: "ab", 543 | controller: function(ab) {}, 544 | resolve: { 545 | f: function($ab) {}, 546 | }, 547 | children: [ 548 | { 549 | name: "abc", 550 | controller: function(abc) {}, 551 | resolve: { 552 | f: function($abc) {}, 553 | }, 554 | }, 555 | ], 556 | }, 557 | ], 558 | }, 559 | { 560 | name: "b", 561 | controller: function(b) {}, 562 | views: { 563 | viewa: { 564 | controller: function($scope, myParam) {}, 565 | controllerProvider: function($stateParams) {}, 566 | templateProvider: function($scope) {}, 567 | dontAlterMe: function(arg) {}, 568 | resolve: { 569 | myParam: function($stateParams) { 570 | return $stateParams.paramFromDI; 571 | } 572 | }, 573 | }, 574 | viewb: { 575 | dontAlterMe: function(arg) {}, 576 | templateProvider: function($scope) {}, 577 | controller: function($scope) {}, 578 | }, 579 | dontAlterMe: null, 580 | }, 581 | }, 582 | ], 583 | }); 584 | stateHelperProvider.setNestedState({ 585 | controller: function($scope, simpleObj, promiseObj, translations) { c }, 586 | }, true); 587 | 588 | // angular ui / ui-bootstrap $modal 589 | $modal.open({ 590 | templateUrl: "str", 591 | controller: function($scope) {}, 592 | resolve: { 593 | items: function(MyService) {}, 594 | data: function(a, b) {}, 595 | its: 42, 596 | }, 597 | donttouch: function(me) {}, 598 | }); 599 | $uibModal.open({ 600 | templateUrl: "str", 601 | controller: function($scope) {}, 602 | resolve: { 603 | items: function(MyService) {}, 604 | data: function(a, b) {}, 605 | its: 42, 606 | }, 607 | donttouch: function(me) {}, 608 | }); 609 | 610 | // angular material design $mdBottomSheet, $mdDialog, $mdToast 611 | $mdDialog.show({ 612 | templateUrl: "str", 613 | controller: function($scope) {}, 614 | resolve: { 615 | items: function(MyService) {}, 616 | data: function(a, b) {}, 617 | its: 42, 618 | }, 619 | donttouch: function(me) {}, 620 | }); 621 | $mdBottomSheet.show({ 622 | templateUrl: "str", 623 | controller: function($scope) {}, 624 | resolve: { 625 | items: function(MyService) {}, 626 | data: function(a, b) {}, 627 | its: 42, 628 | }, 629 | donttouch: function(me) {}, 630 | }); 631 | $mdToast.show({ 632 | templateUrl: "str", 633 | controller: function($scope) {}, 634 | resolve: { 635 | items: function(MyService) {}, 636 | data: function(a, b) {}, 637 | its: 42, 638 | }, 639 | donttouch: function(me) {}, 640 | }); 641 | }); 642 | 643 | // explicit annotations 644 | var x = /* @ngInject */ ["$scope", function($scope) { 645 | }]; 646 | 647 | var obj = {}; 648 | obj.bar = /*@ngInject*/ ["$scope", function($scope) {}]; 649 | 650 | obj = { 651 | controller: /*@ngInject*/ ["$scope", function($scope) {}], 652 | }; 653 | 654 | obj = /*@ngInject*/ { 655 | foo: ["a", function(a) {}], 656 | bar: ["b", "c", function(b, c) {}], 657 | val: 42, 658 | inner: { 659 | circle: ["d", function(d) {}], 660 | alalalala: "long", 661 | }, 662 | nest: { many: {levels: ["x", function(x) {}]}}, 663 | but: { onlythrough: ["object literals", {donttouch: function(me) {}}]}, 664 | }; 665 | 666 | obj = { 667 | /*@ngInject*/ 668 | foo: ["a", function(a) {}], 669 | bar: function(b, c) {}, 670 | }; 671 | 672 | /*@ngInject*/ 673 | obj = { 674 | foo: ["a", function(a) {}], 675 | bar: ["b", "c", function(b, c) {}], 676 | val: 42, 677 | inner: { 678 | circle: ["d", function(d) {}], 679 | alalalala: "long", 680 | }, 681 | nest: { many: {levels: ["x", function(x) {}]}}, 682 | but: { onlythrough: ["object literals", {donttouch: function(me) {}}]}, 683 | }; 684 | 685 | /*@ngInject*/ 686 | var obj = { 687 | foo: ["a", function(a) {}], 688 | bar: ["b", "c", function(b, c) {}], 689 | val: 42, 690 | inner: { 691 | circle: ["d", function(d) {}], 692 | alalalala: "long", 693 | }, 694 | nest: { many: {levels: ["x", function(x) {}]}}, 695 | but: { onlythrough: ["object literals", {donttouch: function(me) {}}]}, 696 | }; 697 | 698 | // @ngInject 699 | function foo($scope) { 700 | } 701 | 702 | // @ngInject 703 | // otherstuff 704 | function Foo($scope) { 705 | } 706 | 707 | // @ngInject 708 | // has trailing semicolon 709 | var foo1 = function($scope) { 710 | }; 711 | foo1.$inject = ["$scope"]; 712 | 713 | // @ngInject 714 | // lacks trailing semicolon 715 | var foo2 = function($scope) { 716 | } 717 | foo2.$inject = ["$scope"]; 718 | 719 | // @ngInject 720 | // has trailing semicolon 721 | bar.foo1 = function($scope) { 722 | }; 723 | bar.foo1.$inject = ["$scope"]; 724 | 725 | // @ngInject 726 | // lacks trailing semicolon 727 | bar.foo2 = function($scope) { 728 | } 729 | bar.foo2.$inject = ["$scope"]; 730 | 731 | // let's zip-zag indentation to make sure that the $inject array lines up properly 732 | // @ngInject 733 | function foo3($scope) {} 734 | // @ngInject 735 | function foo4($scope) { 736 | } 737 | /* @ngInject */ function foo5($scope) {} 738 | /* @ngInject */ function foo6($scope) { 739 | } 740 | 741 | // @ngInject 742 | var foo7 = function($scope) { 743 | }; 744 | foo7.$inject = ["$scope"]; 745 | // @ngInject 746 | var foo8 = function($scope) {}; 747 | foo8.$inject = ["$scope"]; 748 | // @ngInject 749 | var foo9 = function($scope) { 750 | } 751 | foo9.$inject = ["$scope"]; 752 | // @ngInject 753 | var foo10 = function($scope) {} 754 | foo10.$inject = ["$scope"]; 755 | 756 | /* @ngInject */ var foo11 = function($scope) { 757 | }; 758 | foo11.$inject = ["$scope"]; 759 | /* @ngInject */var foo12 = function($scope) {}; 760 | foo12.$inject = ["$scope"]; 761 | /* @ngInject */var foo13 = function($scope) { 762 | } 763 | foo13.$inject = ["$scope"]; 764 | /* @ngInject */var foo14 = function($scope) {} 765 | foo14.$inject = ["$scope"]; 766 | 767 | 768 | // adding an explicit annotation where it isn't needed should work fine 769 | myMod.controller("foo", /*@ngInject*/ ["$scope", "$timeout", function($scope, $timeout) { 770 | }]); 771 | 772 | 773 | // troublesome return forces different placement of $inject array 774 | function outer() { 775 | MyCtrl.$inject = ["a"]; 776 | foo; 777 | return { 778 | controller: MyCtrl, 779 | }; 780 | 781 | // @ngInject 782 | function MyCtrl(a) { 783 | } 784 | } 785 | 786 | 787 | // explicit annotations using ngInject() instead of /*@ngInject*/ 788 | var x = ngInject(["$scope", function($scope) {}]); 789 | 790 | obj = ngInject({ 791 | foo: ["a", function(a) {}], 792 | bar: ["b", "c", function(b, c) {}], 793 | val: 42, 794 | inner: { 795 | circle: ["d", function(d) {}], 796 | alalalala: "long", 797 | }, 798 | nest: { many: {levels: ["x", function(x) {}]}}, 799 | but: { onlythrough: ["object literals", {donttouch: function(me) {}}]}, 800 | }); 801 | 802 | 803 | // explicit annotations using "ngInject" Directive Prologue 804 | function Foo2($scope) { 805 | "ngInject"; 806 | } 807 | 808 | var foos3 = function($scope) { 809 | // comments are ok before the Directive Prologues 810 | // and there may be multiple Prologues 811 | "use strict"; "ngInject"; 812 | }; 813 | foos3.$inject = ["$scope"]; 814 | 815 | var dual1 = function(a) { "ngInject" }, dual2 = function(b) { "ngInject" }; 816 | dual1.$inject = ["a"]; 817 | dual2.$inject = ["b"]; 818 | 819 | g(["c", function(c) { 820 | "ngInject" 821 | }]); 822 | 823 | // Traceur class output example 824 | // class C { 825 | // constructor($scope) { 826 | // "ngInject" 827 | // } 828 | // } 829 | $traceurRuntime.ModuleStore.getAnonymousModule(function() { 830 | "use strict"; 831 | var C = function C($scope) { 832 | "ngInject"; 833 | }; 834 | C.$inject = ["$scope"]; 835 | ($traceurRuntime.createClass)(C, {}, {}); 836 | return {}; 837 | }); 838 | 839 | 840 | // suppress false positives with /*@ngNoInject*/, ngNoInject() and "ngNoInject" 841 | myMod.controller("suppressed", /*@ngNoInject*/function($scope) { 842 | }); 843 | myMod.controller("suppressed", ngNoInject(function($scope) { 844 | })); 845 | myMod.controller("suppressed", function($scope) { 846 | "ngNoInject"; 847 | }); 848 | 849 | // works the same as ngInject i.e. reference-following, IIFE-jumping and so on 850 | /*@ngNoInject*/ 851 | myMod.controller("suppressed", SupFoo1); 852 | myMod.controller("suppressed", SupFoo2); 853 | myMod.controller("suppressed", SupFoo3); 854 | function SupFoo1($scope) { 855 | "ngNoInject"; 856 | } 857 | /*@ngNoInject*/ 858 | function SupFoo2($scope) { 859 | } 860 | var SupFoo3 = ngNoInject(function($scope) { 861 | "ngNoInject"; 862 | }); 863 | 864 | 865 | // regression-test for https://github.com/olov/ng-annotate/issues/221 866 | var FooBar = (function (_super) { 867 | __extends(FooBar, _super); 868 | /*@ngInject*/ 869 | FooBar.$inject = ["$a", "$b"]; 870 | function FooBar($a, $b) { 871 | _super.call(this); 872 | } 873 | /*@ngInject*/ 874 | FooBar.onEnter = function (callback) { 875 | x; 876 | }; 877 | FooBar.onEnter.$inject = ["callback"]; 878 | return FooBar; 879 | })(Bar); 880 | var FooBar2 = (function (_super) { 881 | __extends(FooBar, _super); 882 | FooBar.$inject = ["$a", "$b"]; 883 | function FooBar($a, $b) { 884 | "ngInject"; 885 | _super.call(this); 886 | } 887 | FooBar.onEnter = ["callback", function (callback) { 888 | "ngInject"; 889 | x; 890 | }]; 891 | return FooBar; 892 | })(Bar); 893 | 894 | // snippets that shouldn't fool ng-annotate into generating false positives, 895 | // whether we're inside an angular module or not 896 | myMod.controller("donttouchme", function() { 897 | // lo-dash regression that happened in the brief time frame when 898 | // notes (instad of "notes") would match. see issue #22 899 | var notesForCurrentPage = _.filter(notes, function (note) { 900 | return note.page.uid === page.uid; 901 | }); 902 | }); 903 | 904 | // not a module declaration short-form, see https://github.com/olov/ng-annotate/issues/82 905 | $stateProvider.decorator('parent', function (state, parentFn) { 906 | doStuff(); 907 | }); 908 | 909 | // $get is only valid inside provider 910 | myMod.service("donttouch", function() { 911 | this.$get = function(me) { 912 | }; 913 | }); 914 | myMod.service("donttouch", mefn); 915 | function mefn() { 916 | this.$get = function(me) { 917 | }; 918 | } 919 | 920 | // directive return object is only valid inside directive 921 | myMod.service("donttouch", function() { 922 | return { 923 | controller: function($scope, $timeout) { 924 | bar; 925 | } 926 | } 927 | }); 928 | 929 | myMod.directive("donttouch", function() { 930 | foo.decorator("me", ["$scope", function($scope) { 931 | }]); 932 | }); 933 | 934 | // IIFE-jumping (primarily for compile-to-JS langs) 935 | angular.module("MyMod").directive("foo", ["$a", "$b", function($a, $b) { 936 | $uibModal.open({ 937 | resolve: { 938 | collection: (function(_this) { 939 | return ["$c", function($c) { 940 | }]; 941 | })(this), 942 | }, 943 | }); 944 | }]); 945 | 946 | var x = /*@ngInject*/ (function() { 947 | return ["$a", function($a) { 948 | }]; 949 | })(); 950 | 951 | 952 | // IIFE-jumping with reference support 953 | var myCtrl = (function () { 954 | return function($scope) { 955 | }; 956 | })(); 957 | myCtrl.$inject = ["$scope"]; 958 | angular.module("MyMod").controller("MyCtrl", myCtrl); 959 | 960 | 961 | // advanced IIFE-jumping (with reference support) 962 | var myCtrl10 = (function() { 963 | "use strict"; 964 | // the return statement can appear anywhere on the functions topmost level, 965 | // including before the myCtrl function definition 966 | myCtrl.$inject = ["$scope"]; 967 | return myCtrl; 968 | function myCtrl($scope) { 969 | foo; 970 | } 971 | post; 972 | })(); 973 | angular.module("MyMod").controller("MyCtrl", myCtrl10); 974 | 975 | var myCtrl11 = (function() { 976 | pre; 977 | var myCtrl = function($scope) { 978 | foo; 979 | }; 980 | myCtrl.$inject = ["$scope"]; 981 | mid; 982 | // the return statement can appear anywhere on the functions topmost level, 983 | // including before the myCtrl function definition 984 | return myCtrl; 985 | post; 986 | })(); 987 | angular.module("MyMod").controller("MyCtrl", myCtrl11); 988 | 989 | 990 | // reference support 991 | function MyCtrl1(a, b) { 992 | } 993 | if (true) { 994 | // proper scope analysis including shadowing 995 | let MyCtrl1 = function(c) { 996 | }; 997 | MyCtrl1.$inject = ["c"]; 998 | angular.module("MyMod").directive("foo", MyCtrl1); 999 | } 1000 | angular.module("MyMod").controller("bar", MyCtrl1); 1001 | function MyCtrl2(z) { 1002 | } 1003 | funcall(/*@ngInject*/ MyCtrl2); // explicit annotation on reference flows back to definition 1004 | 1005 | angular.module("MyMod").directive("foo", MyDirective); 1006 | 1007 | function MyDirective($stateProvider) { 1008 | $stateProvider.state('astate', { 1009 | resolve: { 1010 | yoyo: ["ma", function(ma) { 1011 | }], 1012 | } 1013 | }); 1014 | } 1015 | 1016 | /* @ngInject */ 1017 | function MyDirective2($stateProvider) { 1018 | $stateProvider.state('astate', { 1019 | resolve: { 1020 | yoyo: ["ma", function(ma) { 1021 | }], 1022 | } 1023 | }); 1024 | } 1025 | 1026 | // issue 84 1027 | (function() { 1028 | var MyCtrl = function($someDependency) {}; 1029 | MyCtrl.$inject = ["$someDependency"]; 1030 | angular.module('myApp').controller("MyCtrl", MyCtrl); 1031 | MyCtrl.prototype.someFunction = function() {}; 1032 | })(); 1033 | 1034 | // empty var declarator 1035 | var MyCtrl12; 1036 | angular.module("MyMod").controller('MyCtrl', MyCtrl12); 1037 | 1038 | // issue 115 1039 | module.exports = function() { 1040 | "use strict"; 1041 | return { 1042 | restrict: 'E', 1043 | replace: true, 1044 | scope: { }, 1045 | controller: /*@ngInject*/["$scope", "myService", function($scope, myService) { 1046 | }], 1047 | templateUrl: "mytemplate" 1048 | }; 1049 | }; 1050 | 1051 | // issue #135 1052 | var MyCtrl = (function() { 1053 | /*@ngInject*/ 1054 | MyCtrl.$inject = ["a"]; 1055 | function MyCtrl(a) { 1056 | } 1057 | 1058 | return MyCtrl; 1059 | })(); 1060 | 1061 | myMod.service("a", MyCtrl); 1062 | --------------------------------------------------------------------------------