babel-plugin-angularjs-annotate
Babel plugin that adds AngularJS 1.x dependency-injection annotations to ES5 and ES6 code automatically.
Repository Health
Technical Analysis
babel-plugin-angularjs-annotate is a Babel transform that automatically adds AngularJS 1.x dependency-injection annotations to JavaScript source processed by Babel. It’s a fork of the popular ng-annotate tool, rebuilt from the ground up for Babel pipelines, with support for ES6 classes, arrow functions, and export declarations that ng-annotate’s Acorn-based parser cannot handle.
Because it runs as a Babel plugin rather than a separate build step, it slots directly into an existing .babelrc or babel.config.js pipeline and adds annotations during the same transform pass already compiling the code, avoiding the extra pass that the standalone ng-annotate CLI requires.
What You Get
- Automatic annotation of controllers, services, factories, providers, and directives declared with angular.module(…)
- Support for ES6 constructs unsupported by upstream ng-annotate — arrow functions, class declarations/methods, and export statements
- Explicit annotation via /* @ngInject */ comments or an ‘ngInject’ prologue directive, plus an explicitOnly option to disable implicit detection
- An interactive browser-based REPL (docs/) for trying transformations against arbitrary input
Common Use Cases
- Minification-safe legacy AngularJS apps - teams still shipping AngularJS 1.x code who need $inject arrays preserved through Uglify/Terser without maintaining them by hand
- Migrating an AngularJS codebase to ES6/Babel - projects moving from raw ES5 to Babel-compiled ES6 that need DI annotation to keep working across the migration
- Replacing ng-annotate in a Babel-based build - teams already using Babel who want to drop the separate ng-annotate pass and annotate DI in the same compile step
- Angular class-based services - codebases using ES6 classes for AngularJS services/providers that need constructor-based DI annotated automatically
Under The Hood
Architecture The plugin is a single Babel visitor (babel-ng-annotate.js) that hooks into a wide set of AST node types — AssignmentExpression, VariableDeclarator, ClassDeclaration/ClassMethod, ObjectExpression, ReturnStatement, FunctionExpression/ArrowFunctionExpression/FunctionDeclaration, ObjectMethod, CallExpression, and ExportDeclaration — building up a shared ‘suspects’ list via a ctx object passed to nginject.js (explicit-annotation detection: comments, prologue directives, class declarations) and ng-annotate-main.js (implicit pattern matching for angular.module(), ngRoute, ui-router, and $provide patterns, with judgeSuspects making the final decision and inserting arrays at Program.exit). scopetools.js supplies a small reference-resolution helper used by ng-annotate-main’s followReference/jumpOverIife logic to trace identifiers back through IIFEs and variable bindings. It reads as three cooperating modules rather than a layered architecture, with state threaded through a single mutable ctx object across every visitor callback.
Tech Stack Plain JavaScript (no TypeScript) built on @babel/core, @babel/types, and @babel/code-frame (all ^7.x) as the AST/plugin API, plus simple-is for lightweight type checks. Dev-only dependencies (browserify, babelify, watchify) build an in-browser REPL demo under docs/, while tape, diff, chalk, and indent-string power a custom test runner piped through tap-xunit. CI runs on CircleCI via npm test. No bundler is needed for the plugin itself — it ships as a plain Node/CommonJS module.
Code Quality An extensive test suite (tests/tests.js) hand-rolls a runner on top of tape, exercising suites for simple cases, arrow functions, provider $get, module-scoped matching, ui-router, modals, explicit ngInject annotations, edge-case issues, and ES6, all asserting exact Babel-generated output against a large fixture corpus. There is no type safety — everything is plain ES6/CommonJS — and error handling is minimal, limited to a handful of buildCodeFrameError/console.warn calls for annotation mismatches rather than broad defensive coding. An .eslintrc plus an npm lint script cover the top-level files, and CircleCI runs the suite on every push, but no contributing guide exists.
What Makes It Unique Its differentiator versus its parent (ng-annotate) is narrow but real: annotating ES6 constructs — arrow functions, class declarations/methods, and export statements — that ng-annotate’s Acorn-based parser cannot parse at all, while running inside an existing Babel transform pass instead of as a separate pre-processing step. The README is explicit that the project isn’t chasing feature parity or novel matching behavior beyond that ES6/Babel-native niche.