From 23b53fa4ec766e367299b6a44556ad1f7372426e Mon Sep 17 00:00:00 2001 From: Olov Lassus Date: Sat, 19 Dec 2015 15:19:25 +0100 Subject: [PATCH] docs --- IMPLICIT.md | 98 +++++++++++++++++ OPTIONS.md | 54 ++++++++++ README.md | 301 ++++++++-------------------------------------------- 3 files changed, 198 insertions(+), 255 deletions(-) create mode 100644 IMPLICIT.md create mode 100644 OPTIONS.md diff --git a/IMPLICIT.md b/IMPLICIT.md new file mode 100644 index 0000000..768cd88 --- /dev/null +++ b/IMPLICIT.md @@ -0,0 +1,98 @@ +# Implicit matching +ng-annotate uses static analysis to detect common AngularJS code patterns. +There are patterns it does not and never will understand and for those you +should use `"ngInject"` instead, see [README.md](README.md). + + +## Declaration forms +ng-annotate understands the two common declaration forms: + +Long form: + +```js +angular.module("MyMod").controller("MyCtrl", function($scope, $timeout) { +}); +``` + +Short form: + +```js +myMod.controller("MyCtrl", function($scope, $timeout) { +}); +``` + +It's not limited to `.controller` of course. It understands `.config`, `.factory`, +`.directive`, `.filter`, `.run`, `.controller`, `.provider`, `.service`, `.decorator`, +`.component`, `.animation` and `.invoke`. + +For short forms it does not need to see the declaration of `myMod` so you can run it +on your individual source files without concatenating. If ng-annotate detects a short form +false positive then you can use the `--regexp` option to limit the module identifier. +Examples: `--regexp "^myMod$"` (match only `myMod`) or `--regexp "^$"` (ignore short forms). +You can also use `--regexp` to opt-in for more advanced method callee matching, for +example `--regexp "^require(.*)$"` to detect and transform +`require('app-module').controller(..)`. Not using the option is the same as passing +`--regexp "^[a-zA-Z0-9_\$\.\s]+$"`, which means that the callee can be a (non-unicode) +identifier (`foo`), possibly with dot notation (`foo.bar`). + +ng-annotate understands `angular.module("MyMod", function(dep) ..)` as an alternative to +`angular.module("MyMod").config(function(dep) ..)`. + +ng-annotate understands `this.$get = function($scope) ..` and +`{.., $get: function($scope) ..}` inside a `provider`. `self` and `that` can be used as +aliases for `this`. + +ng-annotate understands `return {.., controller: function($scope) ..}` inside a +`directive`. + +ng-annotate understands `$provide.decorator("bar", function($scope) ..)`, `$provide.service`, +`$provide.factory` and `$provide.provider`. + +ng-annotate understands `$routeProvider.when("path", { .. })`. + +ng-annotate understands `$controllerProvider.register("foo", function($scope) ..)`. + +ng-annotate understands `$httpProvider.interceptors.push(function($scope) ..)` and +`$httpProvider.responseInterceptors.push(function($scope) ..)`. + +ng-annotate understands `$injector.invoke(function ..)`. + +ng-annotate understands [ui-router](https://github.com/angular-ui/ui-router) (`$stateProvider` and +`$urlRouterProvider`). + +ng-annotate understands `$uibModal.open` (and `$modal.open`) ([angular-ui/bootstrap](http://angular-ui.github.io/bootstrap/)). + +ng-annotate understands `$mdDialog.show`, `$mdToast.show` and `$mdBottomSheet.show` +([angular material design](https://material.angularjs.org/#/api/material.components.dialog/service/$mdDialog)). + +ng-annotate understands `myMod.store("MyCtrl", function ..)` +([flux-angular](https://github.com/christianalfoni/flux-angular)). + +ng-annotate understands chaining. + +ng-annotate understands IIFE's and attempts to match through them, so +`(function() { return function($scope) .. })()` works anywhere +`function($scope) ..` does (for any IIFE args and params). + +ng-annotate understands [angular-dashboard-framework](https://github.com/sdorra/angular-dashboard-framework) +via optional `--enable angular-dashboard-framework`. + + +## Reference-following +ng-annotate follows references. This works if and only if the referenced declaration is +a) a function declaration or +b) a variable declaration with an initializer. +Modifications to a reference outside of its declaration site are ignored by ng-annotate. + +These examples will get annotated: + +```js +function MyCtrl($scope, $timeout) { +} +var MyCtrl2 = function($scope) {}; + +angular.module("MyMod").controller("MyCtrl", MyCtrl); +angular.module("MyMod").controller("MyCtrl", MyCtrl2); +``` + + diff --git a/OPTIONS.md b/OPTIONS.md new file mode 100644 index 0000000..17cb526 --- /dev/null +++ b/OPTIONS.md @@ -0,0 +1,54 @@ +## ng-annotate command-line options + +`ng-annotate OPTIONS `. The errors (if any) will go to stderr, +the transpiled output to stdout. + +Use the `--add` (`-a`) option to add annotations where non-existing, +use `--remove` (`-r`) to remove all existing annotations, +use `--add --remove` (`-ar`) to rebuild all annotations. + +Use the `-o` option to write output to file. + +Provide `-` instead of an input `` to read input from stdin. + +Use the `--sourcemap` option to generate an inline sourcemap. + +Use the `--sourceroot` option to set the sourceRoot property of the generated sourcemap. + +Use the `--single_quotes` option to output `'$scope'` instead of `"$scope"`. + +Use the `--regexp` option to restrict matching further or to expand matching. +See description further down. + +Use the `--list` option to list optional matchers. + +Use the `--enable` option to enable optional matcher. + +*experimental* Use the `--rename` option to rename providers (services, factories, +controllers, etc.) with a new name when declared and referenced through annotation. +Use it like this: `--rename oldname1 newname1 oldname2 newname2` + +*experimental* Use the `--plugin` option to load a user plugin with the provided path, +1.x may change API). See [plugin-example.js](plugin-example.js) for more info. + +*experimental* Use the `--stats` option to print statistics on stderr. + + +## Library (API) +ng-annotate can be used as a library. See [ng-annotate.js](ng-annotate.js) for further info about +options and return value. + +```js +var ngAnnotate = require("ng-annotate"); +var somePlugin = require("./some/path/some-plugin"); +var res = ngAnnotate(src, { + add: true, + plugin: [somePlugin], + rename: [{from: "generalname", to: "uniquename"}, {from: "alpha", to: "beta"}], + map: { inline: false, inFile: "source.js", sourceRoot: "/path/to/source/root" }, + enable: ["angular-dashboard-framework"], +}); +var errorstringArray = res.errors; +var transformedSource = res.src; +var transformedSourceMap = res.map; +``` diff --git a/README.md b/README.md index 6556951..c6b03c6 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,38 @@ # ng-annotate [![Build Status](https://travis-ci.org/olov/ng-annotate.svg?branch=master)](https://travis-ci.org/olov/ng-annotate) ng-annotate adds and removes AngularJS dependency injection annotations. -It is non-intrusive so your source code stays exactly the same otherwise. -No lost comments or moved lines. Annotations are useful because with them -you're able to minify your source code using your favorite JS minifier. -You write your code without annotations, like this: +Write your code without annotations and mark-up functions to be annotated +with the `"ngInject"` directive prologue, just like you would +`"use strict"`. This must be at the beginning of your function. ```js +$ cat source.js angular.module("MyMod").controller("MyCtrl", function($scope, $timeout) { + "ngInject"; + ... }); ``` -You then run ng-annotate as a build-step to produce this intermediary, -annotated, result (later sent to the minifier): +Then run ng-annotate as a build-step to produce this intermediary, +annotated, result (later sent to the minifier of choice): ```js +$ ng-annotate -a source.js angular.module("MyMod").controller("MyCtrl", ["$scope", "$timeout", function($scope, $timeout) { + "ngInject"; + ... }]); ``` +Your minifier will most likely retain the `"ngInject;"` prologues so use `sed` +or a regexp in your build toolchain to get rid of those. + You can also use ng-annotate to rebuild or remove existing annotations. Rebuilding is useful if you like to check-in the annotated version of your source code. When refactoring, just change parameter names once and let ng-annotate rebuild the annotations. Removing is useful if you want to de-annotate an existing codebase that came with checked-in annotations -**ng-annotate works by using static analysis to identify common code patterns. -There are patterns it does not and never will understand and for those you -can use an explicit `ngInject` annotation instead, see section further down.** - ## Installation and usage @@ -39,35 +43,34 @@ npm install -g ng-annotate Then run it as `ng-annotate OPTIONS `. The errors (if any) will go to stderr, the transpiled output to stdout. -Use the `--add` (`-a`) option to add annotations where non-existing, -use `--remove` (`-r`) to remove all existing annotations, -use `--add --remove` (`-ar`) to rebuild all annotations. +The simplest usage is `ng-annotate -a infile.js > outfile.js`. +See [OPTIONS.md](OPTIONS.md) for command-line documentation. -Use the `-o` option to write output to file. +ng-annotate can be used as a library, see [OPTIONS.md](OPTIONS.md) for its API. -Provide `-` instead of an input `` to read input from stdin. -Use the `--sourcemap` option to generate an inline sourcemap. +## Implicit matching of common code forms +ng-annotate uses static analysis to detect common AngularJS code patterns. When +this works it means that you do not need to mark-up functions with `"ngInject"`. +For a lot of code bases this works very well (use `ng-strict-di` to simplify +debugging when it doesn't) but for others it is less reliable and you may prefer +to use `"ngInject"` instead. For more information about implicit matching see +[IMPLICIT.md](IMPLICIT.md). -Use the `--sourceroot` option to set the sourceRoot property of the generated sourcemap. -Use the `--single_quotes` option to output `'$scope'` instead of `"$scope"`. +## Explicit annotations with ngInject +The recommended `function foo($scope) { "ngInject"; ... }` can be exchanged +for `/*@ngInject*/ function foo($scope) { ... }` or +`ngInject(function foo($scope) { ... })`. If you use the latter form then +then add `function ngInject(v) { return v }` somewhere in your codebase or process +away the `ngInject` function call in your build step. -Use the `--regexp` option to restrict matching further or to expand matching. -See description further down. -Use the `--list` option to list optional matchers. - -Use the `--enable` option to enable optional matcher. - -*experimental* Use the `--rename` option to rename providers (services, factories, -controllers, etc.) with a new name when declared and referenced through annotation. -Use it like this: `--rename oldname1 newname1 oldname2 newname2` - -*experimental* Use the `--plugin` option to load a user plugin with the provided path, -1.x may change API). See [plugin-example.js](plugin-example.js) for more info. - -*experimental* Use the `--stats` option to print statistics on stderr. +### Suppressing false positives with ngNoInject +The `/*@ngInject*/`, `ngInject(..)` and `"ngInject"` siblings have three cousins that +are used for the opposite purpose, suppressing an annotation that ng-annotate added +incorrectly (a "false positive"). They are called `/*@ngNoInject*/`, `ngNoInject(..)` +and `"ngNoInject"` and do exactly what you think they do. ## ES6 and TypeScript support @@ -76,12 +79,13 @@ TypeScript (tsc) and the likes. Use `"ngInject";` on functions you want annotate Your transpiler should preserve directive prologues, if not please file a bug on it. -## Highly recommended: enable ng-strict-di in your minified builds +## Highly recommended: enable ng-strict-di `
` -Do that in your ng-annotate processed builds and AngularJS will let you know if there are -any missing dependency injection annotations. [ng-strict-di](https://docs.angularjs.org/api/ng/directive/ngApp) -is available in AngularJS 1.3 or later. +Do that in your ng-annotate processed (but not minified) builds and AngularJS will +let you know if there are any missing dependency injection annotations. +[ng-strict-di](https://docs.angularjs.org/api/ng/directive/ngApp) is available in +AngularJS 1.3 or later. ## Tools support @@ -95,239 +99,26 @@ is available in AngularJS 1.3 or later. * [Webpack](http://webpack.github.io/): [ng-annotate-webpack-plugin](https://www.npmjs.org/package/ng-annotate-webpack-plugin) by [Chris Liechty](https://github.com/cliechty) * [Middleman](http://middlemanapp.com/): [middleman-ngannotate](http://rubygems.org/gems/middleman-ngannotate) by [Michael Siebert](https://github.com/siebertm) * [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) -* Something missing? Contributions welcome - create plugin and submit a README pull request! ## Changes See [CHANGES.md](CHANGES.md). -## Declaration forms -ng-annotate understands the two common declaration forms: - -Long form: - -```js -angular.module("MyMod").controller("MyCtrl", function($scope, $timeout) { -}); -``` - -Short form: - -```js -myMod.controller("MyCtrl", function($scope, $timeout) { -}); -``` - -It's not limited to `.controller` of course. It understands `.config`, `.factory`, -`.directive`, `.filter`, `.run`, `.controller`, `.provider`, `.service`, `.decorator`, -`.component`, `.animation` and `.invoke`. - -For short forms it does not need to see the declaration of `myMod` so you can run it -on your individual source files without concatenating. If ng-annotate detects a short form -false positive then you can use the `--regexp` option to limit the module identifier. -Examples: `--regexp "^myMod$"` (match only `myMod`) or `--regexp "^$"` (ignore short forms). -You can also use `--regexp` to opt-in for more advanced method callee matching, for -example `--regexp "^require(.*)$"` to detect and transform -`require('app-module').controller(..)`. Not using the option is the same as passing -`--regexp "^[a-zA-Z0-9_\$\.\s]+$"`, which means that the callee can be a (non-unicode) -identifier (`foo`), possibly with dot notation (`foo.bar`). - -ng-annotate understands `angular.module("MyMod", function(dep) ..)` as an alternative to -`angular.module("MyMod").config(function(dep) ..)`. - -ng-annotate understands `this.$get = function($scope) ..` and -`{.., $get: function($scope) ..}` inside a `provider`. `self` and `that` can be used as -aliases for `this`. - -ng-annotate understands `return {.., controller: function($scope) ..}` inside a -`directive`. - -ng-annotate understands `$provide.decorator("bar", function($scope) ..)`, `$provide.service`, -`$provide.factory` and `$provide.provider`. - -ng-annotate understands `$routeProvider.when("path", { .. })`. - -ng-annotate understands `$controllerProvider.register("foo", function($scope) ..)`. - -ng-annotate understands `$httpProvider.interceptors.push(function($scope) ..)` and -`$httpProvider.responseInterceptors.push(function($scope) ..)`. - -ng-annotate understands `$injector.invoke(function ..)`. - -ng-annotate understands [ui-router](https://github.com/angular-ui/ui-router) (`$stateProvider` and -`$urlRouterProvider`). - -ng-annotate understands `$uibModal.open` (and `$modal.open`) ([angular-ui/bootstrap](http://angular-ui.github.io/bootstrap/)). - -ng-annotate understands `$mdDialog.show`, `$mdToast.show` and `$mdBottomSheet.show` -([angular material design](https://material.angularjs.org/#/api/material.components.dialog/service/$mdDialog)). - -ng-annotate understands `myMod.store("MyCtrl", function ..)` -([flux-angular](https://github.com/christianalfoni/flux-angular)). - -ng-annotate understands chaining. - -ng-annotate understands IIFE's and attempts to match through them, so -`(function() { return function($scope) .. })()` works anywhere -`function($scope) ..` does (for any IIFE args and params). - -ng-annotate understands [angular-dashboard-framework](https://github.com/sdorra/angular-dashboard-framework) -via optional `--enable angular-dashboard-framework`. - - -## Reference-following -ng-annotate follows references. This works iff the referenced declaration is -a) a function declaration or -b) a variable declaration with an initializer. -Modifications to a reference outside of its declaration site are ignored by ng-annotate. - -These examples will get annotated: - -```js -function MyCtrl($scope, $timeout) { -} -var MyCtrl2 = function($scope) {}; - -angular.module("MyMod").controller("MyCtrl", MyCtrl); -angular.module("MyMod").controller("MyCtrl", MyCtrl2); -``` - - -## Explicit annotations with ngInject -You can prepend a function with `/*@ngInject*/` to explicitly state that the function -should get annotated. ng-annotate will leave the comment intact and will thus still -be able to also remove or rewrite such annotations. - -You can also wrap an expression inside an `ngInject(..)` function call. If you use this -syntax then add `function ngInject(v) { return v }` somewhere in your codebase, or process -away the `ngInject` function call in your build step. - -You can also add the `"ngInject"` directive prologue at the beginning of a function, -similar to how `"use strict"` is used, to state that the surrounding function should get -annotated. - -Use `ngInject` to support your code style when it's not in a form ng-annotate understands -natively. Remember that the intention of ng-annotate is to reduce stuttering for you, -and `ngInject` does this just as well. You don't need to keep two lists in sync. Use it! - -`ngInject` may be particularly useful if you use a compile-to-JS language that doesn't -preserve comments. - - -### Suppressing false positives with ngNoInject -The `/*@ngInject*/`, `ngInject(..)` and `"ngInject"` siblings have three cousins that -are used for the opposite purpose, suppressing an annotation that ng-annotate added -incorrectly (a "false positive"). They are called `/*@ngNoInject*/`, `ngNoInject(..)` -and `"ngNoInject"` and do exactly what you think they do. - - -### ngInject examples -Here follows some ngInject examples using the `/*@ngInject*/` syntax. Most examples -works fine using the `ngInject(..)` or `"ngInject"` syntax as well. - -```js -x = /*@ngInject*/ function($scope) {}; -obj = {controller: /*@ngInject*/ function($scope) {}}; -obj.bar = /*@ngInject*/ function($scope) {}; - -=> - -x = /*@ngInject*/ ["$scope", function($scope) {}]; -obj = {controller: /*@ngInject*/ ["$scope", function($scope) {}]}; -obj.bar = /*@ngInject*/ ["$scope", function($scope) {}]; -``` - -Prepended to an object literal, `/*@ngInject*/` will annotate all of its contained -function expressions, recursively: - -```js -obj = /*@ngInject*/ { - controller: function($scope) {}, - resolve: { data: function(Service) {} }, -}; - -=> - -obj = /*@ngInject*/ { - controller: ["$scope", function($scope) {}], - resolve: { data: ["Service", function(Service) {}] }, -}; -``` - -Prepended to a function statement, to a single variable declaration initialized with a -function expression or to an assignment where the rvalue is a function expression, - `/*@ngInject*/` will attach an `$inject` array to the function: - -```js -// @ngInject -function Foo($scope) {} - -// @ngInject -var foo = function($scope) {} - -// @ngInject -module.exports = function($scope) {} - -=> - -// @ngInject -function Foo($scope) {} -Foo.$inject = ["$scope"]; - -// @ngInject -var foo = function($scope) {} -foo.$inject = ["$scope"]; - -// @ngInject -module.exports = function($scope) {} -module.exports.$inject = ["$scope"]; -``` - - ## Build and test ng-annotate is written in ES6 constlet style and uses [defs.js](https://github.com/olov/defs) to transpile to ES5. See [BUILD.md](BUILD.md) for build and test instructions. +## Issues and contributions +Please provide issues in the form of input, expected output, actual output. Include +the version of ng-annotate and node that you are using. With pull requests, please +include changes to the tests as well (tests/original.js, tests/with_annotations.js). + + ## License `MIT`, see [LICENSE](LICENSE) file. ng-annotate is written by [Olov Lassus](https://github.com/olov) with the kind help by [contributors](https://github.com/olov/ng-annotate/graphs/contributors). [Follow @olov](https://twitter.com/olov) on Twitter for updates about ng-annotate. - - -## How does ng-annotate compare to ngmin? -ngmin has been deprecated in favor of ng-annotate. In short: -ng-annotate is much faster, finds more declarations to annotate (including ui-router), -treats your source code better, is actively maintained and has a bunch of extra features -on top of that. A much more elaborated answer can be found in -["The future of ngmin and ng-annotate"](https://github.com/btford/ngmin/issues/93). - -*Migrating from ngmin*: -`ng-annotate -a -` is similar to `ngmin` (use stdin and -stdout). `ng-annotate -a in.js -o out.js` is similar to `ngmin in.js out.js`. Grunt users -can migrate easily by installing -[grunt-ng-annotate](https://www.npmjs.org/package/grunt-ng-annotate) and replacing `ngmin` -with `ngAnnotate` in their Gruntfile. Scroll down for information about other tools. - - -## Library (API) -ng-annotate can be used as a library. See [ng-annotate.js](ng-annotate.js) for further info about -options and return value. - -```js -var ngAnnotate = require("ng-annotate"); -var somePlugin = require("./some/path/some-plugin"); -var res = ngAnnotate(src, { - add: true, - plugin: [somePlugin], - rename: [{from: "generalname", to: "uniquename"}, {from: "alpha", to: "beta"}], - map: { inline: false, inFile: "source.js", sourceRoot: "/path/to/source/root" }, - enable: ["angular-dashboard-framework"], -}); -var errorstringArray = res.errors; -var transformedSource = res.src; -var transformedSourceMap = res.map; -```