├── .jshintrc ├── tests ├── ExplicitlyNamedFilterInModule │ ├── expected │ │ ├── MyApp.Area.ng.ts │ │ ├── toUpper.ts │ │ └── toUpper.ng.ts │ └── src │ │ └── toUpper.ts ├── ImplicitlyNamedFilterInModule │ ├── expected │ │ ├── MyApp.Area.ng.ts │ │ ├── toUpper.ts │ │ └── toUpper.ng.ts │ └── src │ │ └── toUpper.ts ├── MultiNamedDirectiveInModule │ ├── expected │ │ ├── MyApp.Area.ng.ts │ │ ├── MyDirective.ts │ │ └── MyDirective.ng.ts │ └── src │ │ └── MyDirective.ts ├── SingleNamedDirectiveInModule │ ├── expected │ │ ├── MyApp.Area.ng.ts │ │ ├── MyDirective.ts │ │ └── MyDirective.ng.ts │ └── src │ │ └── MyDirective.ts ├── ImplicitServiceInModuleNoDependencies │ ├── expected │ │ ├── MyApp.Area.ng.ts │ │ ├── AService.ts │ │ └── AService.ng.ts │ └── src │ │ └── AService.ts ├── ModuleDefinitionWithModuleDependencies │ ├── expected │ │ ├── MyApp.Area.ng.ts │ │ ├── MyApp.ts │ │ ├── MyController.ts │ │ ├── MyApp.ng.ts │ │ └── MyController.ng.ts │ └── src │ │ ├── MyApp.ts │ │ └── MyController.ts ├── ImplicitControllerInModuleNoDependencies │ ├── expected │ │ ├── MyApp.Area.ng.ts │ │ ├── MyController.ts │ │ └── MyController.ng.ts │ └── src │ │ └── MyController.ts ├── ModuleDefinitionWithModuleNgDependencies │ ├── src │ │ └── MyApp.ts │ └── expected │ │ ├── MyApp.ts │ │ └── MyApp.ng.ts ├── ImplicitControllerInModuleNoCtorNoDependencies │ ├── expected │ │ ├── MyApp.Area.ng.ts │ │ ├── MyController.ts │ │ └── MyController.ng.ts │ └── src │ │ └── MyController.ts ├── ImplicitControllerInModuleWithNgServiceDependency │ ├── expected │ │ ├── MyApp.Area.ng.ts │ │ ├── MyController.ts │ │ └── MyController.ng.ts │ └── src │ │ └── MyController.ts ├── ImplicitControllerInModuleWithServiceDependency │ ├── expected │ │ ├── MyApp.Area.ng.ts │ │ ├── MyController.ts │ │ ├── AService.ts │ │ ├── MyController.ng.ts │ │ └── AService.ng.ts │ └── src │ │ ├── MyController.ts │ │ └── AService.ts ├── SingleNamedDirectiveInModuleWithServiceDependency │ ├── expected │ │ ├── MyApp.Area.ng.ts │ │ ├── AService.ts │ │ ├── MyDirective.ts │ │ ├── AService.ng.ts │ │ └── MyDirective.ng.ts │ └── src │ │ ├── AService.ts │ │ └── MyDirective.ts ├── ImplicitServiceWithInterfaceInModuleNoDependencies │ ├── expected │ │ ├── MyApp.Area.ng.ts │ │ ├── AService.ts │ │ └── AService.ng.ts │ └── src │ │ └── AService.ts ├── SingleNamedDirectiveInModuleWithNgServiceDependency │ ├── expected │ │ ├── MyApp.Area.ng.ts │ │ ├── MyDirective.ts │ │ └── MyDirective.ng.ts │ └── src │ │ └── MyDirective.ts ├── ModuleDefinitionWithRunFuncWithNgServiceDependencyAndModuleDependencies │ ├── src │ │ ├── MyController.ts │ │ └── MyApp.ts │ └── expected │ │ ├── MyController.ts │ │ └── MyApp.ts ├── ModuleDefinitionWithRunFuncWithServiceDependencyAndModuleDependencies │ ├── src │ │ ├── MyController.ts │ │ ├── MyApp.ts │ │ └── AService.ts │ └── expected │ │ ├── MyController.ts │ │ ├── MyApp.ts │ │ └── AService.ts ├── ModuleDefinitionWithConfigurationMethodWithProviderDependencies │ ├── src │ │ └── MyApp.ts │ └── expected │ │ ├── MyApp.ts │ │ └── MyApp.ng.ts └── compare.js ├── sample ├── app │ ├── home │ │ ├── home.html │ │ └── HomeController.ts │ ├── components │ │ ├── hello │ │ │ └── HelloService.ts │ │ ├── Greenify │ │ │ └── GreenifyDirective.ts │ │ └── TitleCase │ │ │ └── TitleCaseFilter.ts │ └── app.ts ├── package.json ├── bower.json ├── index.html ├── tslint.json └── gruntfile.js ├── .gitignore ├── package.json ├── gruntfile.js ├── README.md ├── LICENSE └── tasks └── tsng.js /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "loopfunc": true, 3 | "curly": true 4 | } -------------------------------------------------------------------------------- /tests/ExplicitlyNamedFilterInModule/expected/MyApp.Area.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | angular.module("MyApp.Area", []); 3 | } -------------------------------------------------------------------------------- /tests/ImplicitlyNamedFilterInModule/expected/MyApp.Area.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | angular.module("MyApp.Area", []); 3 | } -------------------------------------------------------------------------------- /tests/MultiNamedDirectiveInModule/expected/MyApp.Area.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | angular.module("MyApp.Area", []); 3 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModule/expected/MyApp.Area.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | angular.module("MyApp.Area", []); 3 | } -------------------------------------------------------------------------------- /tests/ImplicitServiceInModuleNoDependencies/expected/MyApp.Area.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | angular.module("MyApp.Area", []); 3 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithModuleDependencies/expected/MyApp.Area.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | angular.module("MyApp.Area", []); 3 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithModuleDependencies/src/MyApp.ts: -------------------------------------------------------------------------------- 1 | module MyApp { 2 | var dependencies = [ 3 | Area 4 | ]; 5 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleNoDependencies/expected/MyApp.Area.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | angular.module("MyApp.Area", []); 3 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithModuleDependencies/expected/MyApp.ts: -------------------------------------------------------------------------------- 1 | module MyApp { 2 | var dependencies = [ 3 | Area 4 | ]; 5 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithModuleNgDependencies/src/MyApp.ts: -------------------------------------------------------------------------------- 1 | module MyApp { 2 | var dependencies = [ 3 | "ngRoute" 4 | ]; 5 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleNoCtorNoDependencies/expected/MyApp.Area.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | angular.module("MyApp.Area", []); 3 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleWithNgServiceDependency/expected/MyApp.Area.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | angular.module("MyApp.Area", []); 3 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleWithServiceDependency/expected/MyApp.Area.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | angular.module("MyApp.Area", []); 3 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithModuleNgDependencies/expected/MyApp.ts: -------------------------------------------------------------------------------- 1 | module MyApp { 2 | var dependencies = [ 3 | "ngRoute" 4 | ]; 5 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModuleWithServiceDependency/expected/MyApp.Area.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | angular.module("MyApp.Area", []); 3 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleNoCtorNoDependencies/src/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | 4 | } 5 | } -------------------------------------------------------------------------------- /tests/ImplicitServiceWithInterfaceInModuleNoDependencies/expected/MyApp.Area.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | angular.module("MyApp.Area", []); 3 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModuleWithNgServiceDependency/expected/MyApp.Area.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | angular.module("MyApp.Area", []); 3 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleNoCtorNoDependencies/expected/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | 4 | } 5 | } -------------------------------------------------------------------------------- /sample/app/home/home.html: -------------------------------------------------------------------------------- 1 |
2 |

Home

3 |

{{ vm.message | titlecase }}

4 |
-------------------------------------------------------------------------------- /tests/ImplicitServiceInModuleNoDependencies/src/AService.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class AService { 3 | constructor () { 4 | 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ImplicitServiceInModuleNoDependencies/expected/AService.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class AService { 3 | constructor () { 4 | 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithModuleDependencies/src/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | constructor () { 4 | 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleNoDependencies/src/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | constructor () { 4 | 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ImplicitlyNamedFilterInModule/src/toUpper.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | 3 | //@NgFilter 4 | function toUpper(input: string) { 5 | return input.toUpperCase(); 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithModuleDependencies/expected/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | constructor () { 4 | 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleNoDependencies/expected/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | constructor () { 4 | 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ImplicitlyNamedFilterInModule/expected/toUpper.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | 3 | //@NgFilter 4 | function toUpper(input: string) { 5 | return input.toUpperCase(); 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ExplicitlyNamedFilterInModule/src/toUpper.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | 3 | //@NgFilter("toUpper") 4 | function upper(input: string) { 5 | return input.toUpperCase(); 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ExplicitlyNamedFilterInModule/expected/toUpper.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | 3 | //@NgFilter("toUpper") 4 | function upper(input: string) { 5 | return input.toUpperCase(); 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithModuleDependencies/expected/MyApp.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp { 2 | angular.module("MyApp", [ 3 | "MyApp.Area", 4 | ]); 5 | 6 | var dependencies = [ 7 | Area 8 | ]; 9 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithModuleNgDependencies/expected/MyApp.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp { 2 | angular.module("MyApp", [ 3 | "ngRoute", 4 | ]); 5 | 6 | var dependencies = [ 7 | "ngRoute" 8 | ]; 9 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithRunFuncWithNgServiceDependencyAndModuleDependencies/src/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | constructor () { 4 | 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithRunFuncWithServiceDependencyAndModuleDependencies/src/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | constructor () { 4 | 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithRunFuncWithNgServiceDependencyAndModuleDependencies/expected/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | constructor () { 4 | 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithRunFuncWithServiceDependencyAndModuleDependencies/expected/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | constructor () { 4 | 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleWithNgServiceDependency/src/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | constructor ( 4 | $location: ng.ILocationService) { 5 | 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleWithNgServiceDependency/expected/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | constructor ( 4 | $location: ng.ILocationService) { 5 | 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithRunFuncWithNgServiceDependencyAndModuleDependencies/src/MyApp.ts: -------------------------------------------------------------------------------- 1 | module MyApp { 2 | var dependencies = [ 3 | Area 4 | ]; 5 | 6 | function run($log: ng.ILogService) { 7 | $log.log("Something"); 8 | } 9 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithRunFuncWithNgServiceDependencyAndModuleDependencies/expected/MyApp.ts: -------------------------------------------------------------------------------- 1 | module MyApp { 2 | var dependencies = [ 3 | Area 4 | ]; 5 | 6 | function run($log: ng.ILogService) { 7 | $log.log("Something"); 8 | } 9 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithRunFuncWithServiceDependencyAndModuleDependencies/src/MyApp.ts: -------------------------------------------------------------------------------- 1 | module MyApp { 2 | var dependencies = [ 3 | Area 4 | ]; 5 | 6 | function run($log: ng.ILogService, aService: Area.IAService) { 7 | $log.log(aService.go()); 8 | } 9 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithRunFuncWithServiceDependencyAndModuleDependencies/expected/MyApp.ts: -------------------------------------------------------------------------------- 1 | module MyApp { 2 | var dependencies = [ 3 | Area 4 | ]; 5 | 6 | function run($log: ng.ILogService, aService: Area.IAService) { 7 | $log.log(aService.go()); 8 | } 9 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleWithServiceDependency/src/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | private _aService: Area.IAService 4 | 5 | constructor (aService: Area.IAService) { 6 | this._aService = aService; 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific files 2 | *.suo 3 | *.user 4 | *.sln.docstates 5 | 6 | # Bower Components 7 | bower_components 8 | 9 | # Node Modules 10 | node_modules 11 | 12 | # js sample files 13 | sample/js 14 | 15 | # Test output 16 | tests/*/actual 17 | 18 | # Processed files 19 | *.ng.ts -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleWithServiceDependency/expected/MyController.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | class MyController { 3 | private _aService: Area.IAService 4 | 5 | constructor (aService: Area.IAService) { 6 | this._aService = aService; 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModule/src/MyDirective.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | //@NgDirective('myDirective') 3 | class MyDirective implements ng.IDirective { 4 | public restrict = "A"; 5 | 6 | public link(scope: any, element: ng.IAugmentedJQuery, attrs) { 7 | 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModule/expected/MyDirective.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | //@NgDirective('myDirective') 3 | class MyDirective implements ng.IDirective { 4 | public restrict = "A"; 5 | 6 | public link(scope: any, element: ng.IAugmentedJQuery, attrs) { 7 | 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /sample/app/components/hello/HelloService.ts: -------------------------------------------------------------------------------- 1 | module App.Sample.Hello { 2 | export interface IHelloService { 3 | sayHello(): string; 4 | } 5 | 6 | class HelloService implements IHelloService { 7 | public sayHello() { 8 | return "Hello from the HelloService service!"; 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /tests/ImplicitlyNamedFilterInModule/expected/toUpper.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | 5 | //@NgFilter 6 | function toUpper(input: string) { 7 | return input.toUpperCase(); 8 | } 9 | 10 | angular.module("MyApp.Area") 11 | .filter("toUpper", () => toUpper); 12 | } -------------------------------------------------------------------------------- /tests/ExplicitlyNamedFilterInModule/expected/toUpper.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | 5 | //@NgFilter("toUpper") 6 | function upper(input: string) { 7 | return input.toUpperCase(); 8 | } 9 | 10 | angular.module("MyApp.Area") 11 | .filter("toUpper", () => upper); 12 | } -------------------------------------------------------------------------------- /tests/MultiNamedDirectiveInModule/src/MyDirective.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | //@NgDirective('foo') 3 | //@NgDirective('bar') 4 | class MyDirective implements ng.IDirective { 5 | public restrict = "A"; 6 | 7 | public link(scope: any, element: ng.IAugmentedJQuery, attrs) { 8 | 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleNoCtorNoDependencies/expected/MyController.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | class MyController { 5 | 6 | } 7 | 8 | angular.module("MyApp.Area") 9 | .controller("MyApp.Area.MyController", [ 10 | MyController 11 | ]); 12 | } -------------------------------------------------------------------------------- /tests/MultiNamedDirectiveInModule/expected/MyDirective.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | //@NgDirective('foo') 3 | //@NgDirective('bar') 4 | class MyDirective implements ng.IDirective { 5 | public restrict = "A"; 6 | 7 | public link(scope: any, element: ng.IAugmentedJQuery, attrs) { 8 | 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /sample/app/components/Greenify/GreenifyDirective.ts: -------------------------------------------------------------------------------- 1 | module App.Sample.Greenify { 2 | 3 | //@NgDirective('appGreenify') 4 | class GreenifyDirective implements ng.IDirective { 5 | public restrict = "A"; 6 | 7 | public link(scope: any, element: ng.IAugmentedJQuery) { 8 | element.css({ color: "#00aa00" }); 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleWithServiceDependency/src/AService.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | export interface IAService { 3 | go(): string; 4 | } 5 | 6 | class AService implements IAService { 7 | constructor () { 8 | 9 | } 10 | 11 | public go() { 12 | return "Hello World"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /tests/ImplicitServiceInModuleNoDependencies/expected/AService.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | class AService { 5 | constructor () { 6 | 7 | } 8 | } 9 | 10 | angular.module("MyApp.Area") 11 | .service("MyApp.Area.AService", [ 12 | AService 13 | ]); 14 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModuleWithServiceDependency/src/AService.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | export interface IAService { 3 | go(): string; 4 | } 5 | 6 | class AService implements IAService { 7 | constructor () { 8 | 9 | } 10 | 11 | public go() { 12 | return "Hello World"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleWithServiceDependency/expected/AService.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | export interface IAService { 3 | go(): string; 4 | } 5 | 6 | class AService implements IAService { 7 | constructor () { 8 | 9 | } 10 | 11 | public go() { 12 | return "Hello World"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /tests/ImplicitServiceWithInterfaceInModuleNoDependencies/src/AService.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | export interface IAService { 3 | go(): string; 4 | } 5 | 6 | class AService implements IAService { 7 | constructor () { 8 | 9 | } 10 | 11 | public go() { 12 | return "Hello World"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModuleWithServiceDependency/expected/AService.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | export interface IAService { 3 | go(): string; 4 | } 5 | 6 | class AService implements IAService { 7 | constructor () { 8 | 9 | } 10 | 11 | public go() { 12 | return "Hello World"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /sample/app/home/HomeController.ts: -------------------------------------------------------------------------------- 1 | module App.Sample.Home { 2 | interface IHelloViewModel { 3 | message: string; 4 | } 5 | 6 | class HomeController implements IHelloViewModel { 7 | constructor(helloService: Hello.IHelloService) { 8 | this.message = helloService.sayHello(); 9 | } 10 | 11 | public message: string; 12 | } 13 | } -------------------------------------------------------------------------------- /tests/ImplicitServiceWithInterfaceInModuleNoDependencies/expected/AService.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | export interface IAService { 3 | go(): string; 4 | } 5 | 6 | class AService implements IAService { 7 | constructor () { 8 | 9 | } 10 | 11 | public go() { 12 | return "Hello World"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleNoDependencies/expected/MyController.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | class MyController { 5 | constructor () { 6 | 7 | } 8 | } 9 | 10 | angular.module("MyApp.Area") 11 | .controller("MyApp.Area.MyController", [ 12 | MyController 13 | ]); 14 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithModuleDependencies/expected/MyController.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | class MyController { 5 | constructor () { 6 | 7 | } 8 | } 9 | 10 | angular.module("MyApp.Area") 11 | .controller("MyApp.Area.MyController", [ 12 | MyController 13 | ]); 14 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithRunFuncWithServiceDependencyAndModuleDependencies/src/AService.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | export interface IAService { 3 | go(): string; 4 | } 5 | 6 | class AService implements IAService { 7 | constructor () { 8 | 9 | } 10 | 11 | public go() { 12 | return "Hello World"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithConfigurationMethodWithProviderDependencies/src/MyApp.ts: -------------------------------------------------------------------------------- 1 | module MyApp { 2 | function configuration($routeProvider: ng.route.IRouteProvider, $logProvider: ng.ILogProvider) { 3 | $logProvider.debugEnabled(true); 4 | 5 | $routeProvider 6 | .when("/", { templateUrl: "tmpl/home.html" }) 7 | .otherwise({ redirectTo: "/" }); 8 | } 9 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithRunFuncWithServiceDependencyAndModuleDependencies/expected/AService.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | export interface IAService { 3 | go(): string; 4 | } 5 | 6 | class AService implements IAService { 7 | constructor () { 8 | 9 | } 10 | 11 | public go() { 12 | return "Hello World"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithConfigurationMethodWithProviderDependencies/expected/MyApp.ts: -------------------------------------------------------------------------------- 1 | module MyApp { 2 | function configuration($routeProvider: ng.route.IRouteProvider, $logProvider: ng.ILogProvider) { 3 | $logProvider.debugEnabled(true); 4 | 5 | $routeProvider 6 | .when("/", { templateUrl: "tmpl/home.html" }) 7 | .otherwise({ redirectTo: "/" }); 8 | } 9 | } -------------------------------------------------------------------------------- /sample/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.html", 6 | "author": "Damian Edwards", 7 | "license": "Apache 2.0", 8 | "devDependencies": { 9 | "grunt": "~0.4.4", 10 | "grunt-tslint": "~0.4.1", 11 | "grunt-typescript": "~0.3.4", 12 | "grunt-contrib-clean": "~0.5.0", 13 | "grunt-contrib-connect": "~0.7.1", 14 | "grunt-contrib-copy": "~0.5.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleWithNgServiceDependency/expected/MyController.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | class MyController { 5 | constructor ( 6 | $location: ng.ILocationService) { 7 | 8 | } 9 | } 10 | 11 | angular.module("MyApp.Area") 12 | .controller("MyApp.Area.MyController", [ 13 | "$location", 14 | MyController 15 | ]); 16 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModuleWithServiceDependency/src/MyDirective.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | //@NgDirective('myDirective') 3 | class MyDirective implements ng.IDirective { 4 | private _aService: Area.IAService; 5 | 6 | constructor(aService: Area.IAService) { 7 | this._aService = aService; 8 | } 9 | 10 | public restrict = "A"; 11 | 12 | public link(scope: any, element: ng.IAugmentedJQuery, attrs) { 13 | 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModuleWithNgServiceDependency/src/MyDirective.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | //@NgDirective('myDirective') 3 | class MyDirective implements ng.IDirective { 4 | private _window: ng.IWindowService; 5 | 6 | constructor($window: ng.IWindowService) { 7 | this._window = $window; 8 | } 9 | 10 | public restrict = "A"; 11 | 12 | public link(scope: any, element: ng.IAugmentedJQuery, attrs) { 13 | 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModuleWithServiceDependency/expected/MyDirective.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | //@NgDirective('myDirective') 3 | class MyDirective implements ng.IDirective { 4 | private _aService: Area.IAService; 5 | 6 | constructor(aService: Area.IAService) { 7 | this._aService = aService; 8 | } 9 | 10 | public restrict = "A"; 11 | 12 | public link(scope: any, element: ng.IAugmentedJQuery, attrs) { 13 | 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModuleWithNgServiceDependency/expected/MyDirective.ts: -------------------------------------------------------------------------------- 1 | module MyApp.Area { 2 | //@NgDirective('myDirective') 3 | class MyDirective implements ng.IDirective { 4 | private _window: ng.IWindowService; 5 | 6 | constructor($window: ng.IWindowService) { 7 | this._window = $window; 8 | } 9 | 10 | public restrict = "A"; 11 | 12 | public link(scope: any, element: ng.IAugmentedJQuery, attrs) { 13 | 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleWithServiceDependency/expected/MyController.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | class MyController { 5 | private _aService: Area.IAService 6 | 7 | constructor (aService: Area.IAService) { 8 | this._aService = aService; 9 | } 10 | } 11 | 12 | angular.module("MyApp.Area") 13 | .controller("MyApp.Area.MyController", [ 14 | "MyApp.Area.IAService", 15 | MyController 16 | ]); 17 | } -------------------------------------------------------------------------------- /sample/app/components/TitleCase/TitleCaseFilter.ts: -------------------------------------------------------------------------------- 1 | module App.Sample.TitleCase { 2 | 3 | //@NgFilter('titlecase') 4 | function titleCase(input: string) { 5 | var out = "", 6 | lastChar = ""; 7 | 8 | for (var i = 0; i < input.length; i++) { 9 | out = out + (lastChar === " " || lastChar === "" 10 | ? input.charAt(i).toUpperCase() 11 | : input.charAt(i)); 12 | 13 | lastChar = input.charAt(i); 14 | } 15 | 16 | return out; 17 | } 18 | } -------------------------------------------------------------------------------- /sample/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample", 3 | "version": "0.1.2", 4 | "homepage": "https://github.com/DamianEdwards/grunt-tsng", 5 | "authors": [ 6 | "DamianEdwards " 7 | ], 8 | "license": "Apache 2.0", 9 | "private": true, 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ], 17 | "devDependencies": { 18 | "angular": "~1.2.16", 19 | "dt-angular": "~1.2.0", 20 | "angular-route": "~1.2.16" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/ImplicitControllerInModuleWithServiceDependency/expected/AService.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | export interface IAService { 5 | go(): string; 6 | } 7 | 8 | class AService implements IAService { 9 | constructor () { 10 | 11 | } 12 | 13 | public go() { 14 | return "Hello World"; 15 | } 16 | } 17 | 18 | angular.module("MyApp.Area") 19 | .service("MyApp.Area.IAService", [ 20 | AService 21 | ]); 22 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModuleWithServiceDependency/expected/AService.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | export interface IAService { 5 | go(): string; 6 | } 7 | 8 | class AService implements IAService { 9 | constructor () { 10 | 11 | } 12 | 13 | public go() { 14 | return "Hello World"; 15 | } 16 | } 17 | 18 | angular.module("MyApp.Area") 19 | .service("MyApp.Area.IAService", [ 20 | AService 21 | ]); 22 | } -------------------------------------------------------------------------------- /tests/ImplicitServiceWithInterfaceInModuleNoDependencies/expected/AService.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | export interface IAService { 5 | go(): string; 6 | } 7 | 8 | class AService implements IAService { 9 | constructor () { 10 | 11 | } 12 | 13 | public go() { 14 | return "Hello World"; 15 | } 16 | } 17 | 18 | angular.module("MyApp.Area") 19 | .service("MyApp.Area.IAService", [ 20 | AService 21 | ]); 22 | } -------------------------------------------------------------------------------- /tests/ModuleDefinitionWithConfigurationMethodWithProviderDependencies/expected/MyApp.ng.ts: -------------------------------------------------------------------------------- 1 | module MyApp { 2 | angular.module("MyApp", [ 3 | ]).config([ 4 | "$routeProvider", 5 | "$logProvider", 6 | configuration 7 | ]); 8 | 9 | function configuration($routeProvider: ng.route.IRouteProvider, $logProvider: ng.ILogProvider) { 10 | $logProvider.debugEnabled(true); 11 | 12 | $routeProvider 13 | .when("/", { templateUrl: "tmpl/home.html" }) 14 | .otherwise({ redirectTo: "/" }); 15 | } 16 | } -------------------------------------------------------------------------------- /sample/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | grunt-tsng Sample 5 | 10 | 11 | 12 |

grung-tsng Sample

13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /sample/app/app.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | module App.Sample { 5 | var dependencies = [ 6 | "ngRoute", 7 | App.Sample.Hello, 8 | App.Sample.TitleCase, 9 | App.Sample.Greenify, 10 | App.Sample.Home 11 | ]; 12 | 13 | function configuration($routeProvider: ng.route.IRouteProvider) { 14 | // Configure routes 15 | $routeProvider 16 | .when("/home", { templateUrl: "app/home/home.html" }) 17 | .otherwise({ redirectTo: "/home" }); 18 | } 19 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModule/expected/MyDirective.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | //@NgDirective('myDirective') 5 | class MyDirective implements ng.IDirective { 6 | constructor() { 7 | for (var m in this) { 8 | if (this[m].bind) { 9 | this[m] = this[m].bind(this); 10 | } 11 | } 12 | } 13 | 14 | public restrict = "A"; 15 | 16 | public link(scope: any, element: ng.IAugmentedJQuery, attrs) { 17 | 18 | } 19 | } 20 | 21 | angular.module("MyApp.Area") 22 | .directive("myDirective", [ 23 | function () { 24 | return new MyDirective(); 25 | } 26 | ]); 27 | } -------------------------------------------------------------------------------- /tests/MultiNamedDirectiveInModule/expected/MyDirective.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | //@NgDirective('foo') 5 | //@NgDirective('bar') 6 | class MyDirective implements ng.IDirective { 7 | constructor() { 8 | for (var m in this) { 9 | if (this[m].bind) { 10 | this[m] = this[m].bind(this); 11 | } 12 | } 13 | } 14 | 15 | public restrict = "A"; 16 | 17 | public link(scope: any, element: ng.IAugmentedJQuery, attrs) { 18 | 19 | } 20 | } 21 | 22 | angular.module("MyApp.Area") 23 | .directive("foo", [ 24 | function () { 25 | return new MyDirective(); 26 | } 27 | ]) 28 | .directive("bar", [ 29 | function () { 30 | return new MyDirective(); 31 | } 32 | ]); 33 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModuleWithNgServiceDependency/expected/MyDirective.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | //@NgDirective('myDirective') 5 | class MyDirective implements ng.IDirective { 6 | private _window: ng.IWindowService; 7 | 8 | constructor($window: ng.IWindowService) { 9 | for (var m in this) { 10 | if (this[m].bind) { 11 | this[m] = this[m].bind(this); 12 | } 13 | } 14 | this._window = $window; 15 | } 16 | 17 | public restrict = "A"; 18 | 19 | public link(scope: any, element: ng.IAugmentedJQuery, attrs) { 20 | 21 | } 22 | } 23 | 24 | angular.module("MyApp.Area") 25 | .directive("myDirective", [ 26 | "$window", 27 | function (a) { 28 | return new MyDirective(a); 29 | } 30 | ]); 31 | } -------------------------------------------------------------------------------- /tests/SingleNamedDirectiveInModuleWithServiceDependency/expected/MyDirective.ng.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module MyApp.Area { 4 | //@NgDirective('myDirective') 5 | class MyDirective implements ng.IDirective { 6 | private _aService: Area.IAService; 7 | 8 | constructor(aService: Area.IAService) { 9 | for (var m in this) { 10 | if (this[m].bind) { 11 | this[m] = this[m].bind(this); 12 | } 13 | } 14 | this._aService = aService; 15 | } 16 | 17 | public restrict = "A"; 18 | 19 | public link(scope: any, element: ng.IAugmentedJQuery, attrs) { 20 | 21 | } 22 | } 23 | 24 | angular.module("MyApp.Area") 25 | .directive("myDirective", [ 26 | "MyApp.Area.IAService", 27 | function (a) { 28 | return new MyDirective(a); 29 | } 30 | ]); 31 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-tsng", 3 | "version": "0.1.3", 4 | "description": "A TypeScript pre-processor for AngularJS", 5 | "main": "tsng.js", 6 | "author": { 7 | "name": "Damian Edwards", 8 | "url": "https://github.com/DamianEdwards/" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/DamianEdwards/grunt-tsng.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/DamianEdwards/grunt-tsng/issues" 16 | }, 17 | "licenses": [ 18 | { 19 | "type": "Apache-2.0", 20 | "url": "https://github.com/DamianEdwards/grunt-tsng/blob/master/LICENSE" 21 | } 22 | ], 23 | "engines": { 24 | "node": ">= 0.8.0" 25 | }, 26 | "peerDependencies": { 27 | "grunt": "~0.4.0" 28 | }, 29 | "devDependencies": { 30 | "grunt": "~0.4.4", 31 | "grunt-contrib-jshint": "~0.10.0", 32 | "grunt-contrib-clean": "~0.5.0", 33 | "grunt-typescript": "~0.3.4", 34 | "grunt-contrib-copy": "~0.5.0" 35 | }, 36 | "keywords": [ 37 | "gruntplugin", 38 | "typescript", 39 | "angular", 40 | "angularjs" 41 | ], 42 | "files": [ 43 | "tasks", 44 | "LICENSE" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /sample/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "curly": true, 5 | "eofline": false, 6 | "forin": true, 7 | "indent": [true, 4], 8 | "label-position": true, 9 | "label-undefined": true, 10 | "max-line-length": [true, 140], 11 | "no-arg": true, 12 | "no-bitwise": true, 13 | "no-console": [true, 14 | "debug", 15 | "info", 16 | "time", 17 | "timeEnd", 18 | "trace" 19 | ], 20 | "no-construct": true, 21 | "no-debugger": true, 22 | "no-duplicate-key": true, 23 | "no-duplicate-variable": true, 24 | "no-empty": true, 25 | "no-eval": true, 26 | "no-string-literal": true, 27 | "no-trailing-whitespace": true, 28 | "no-unreachable": true, 29 | "one-line": [true, 30 | "check-open-brace", 31 | "check-catch", 32 | "check-else", 33 | "check-whitespace" 34 | ], 35 | "quotemark": [true, "double"], 36 | "radix": true, 37 | "semicolon": true, 38 | "triple-equals": [true, "allow-null-check"], 39 | "variable-name": false, 40 | "whitespace": [true, 41 | "check-branch", 42 | "check-decl", 43 | "check-operator", 44 | "check-separator", 45 | "check-type" 46 | ] 47 | } 48 | } -------------------------------------------------------------------------------- /sample/gruntfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-tsng 3 | * https://github.com/DamianEdwards/grunt-tsng 4 | * 5 | * Copyright (c) 2014 Damian Edwards 6 | * Licensed under the Apache 2.0 License. 7 | */ 8 | 9 | module.exports = function(grunt) { 10 | "use strict"; 11 | 12 | grunt.initConfig({ 13 | connect: { 14 | "static": { 15 | options: { 16 | keepalive: true, 17 | hostname: "localhost", 18 | port: 8001, 19 | open: true 20 | } 21 | } 22 | }, 23 | copy: { // This is to work around an issue with the dt-angular bower package https://github.com/dt-bower/dt-angular/issues/4 24 | fix: { 25 | files: { 26 | "bower_components/jquery/jquery.d.ts": ["bower_components/dt-jquery/jquery.d.ts"] 27 | } 28 | } 29 | }, 30 | clean: { 31 | options: { force: true }, 32 | tsng: ['app/**/*.ng.ts'], 33 | js: ['js/**/*.*'] 34 | }, 35 | tsng: { 36 | options: { 37 | extension: ".ng.ts" 38 | }, 39 | dev: { 40 | files: { 41 | "app": ['app/**/*.ts', "!**/*.ng.ts"] 42 | } 43 | } 44 | }, 45 | tslint: { 46 | options: { 47 | configuration: grunt.file.readJSON("tslint.json") 48 | }, 49 | files: { 50 | src: ['app/**/*.ts', '!**/*.ng.ts'] 51 | } 52 | }, 53 | typescript: { 54 | options: { 55 | module: 'amd', // or commonjs 56 | target: 'es5', // or es3 57 | sourcemap: false 58 | }, 59 | dev: { 60 | files: { 61 | 'js/app.js': ['app/**/*.ng.ts'] 62 | } 63 | } 64 | }, 65 | }); 66 | 67 | grunt.loadTasks("../tasks"); 68 | 69 | grunt.loadNpmTasks("grunt-contrib-copy"); 70 | grunt.loadNpmTasks("grunt-contrib-clean"); 71 | grunt.loadNpmTasks("grunt-contrib-connect"); 72 | grunt.loadNpmTasks("grunt-tslint"); 73 | grunt.loadNpmTasks("grunt-typescript"); 74 | 75 | grunt.registerTask("sample", ["copy:fix", "build", "connect"]); 76 | grunt.registerTask("build", ["clean", "tslint", "tsng", "typescript"]); 77 | grunt.registerTask("default", ["build"]); 78 | }; -------------------------------------------------------------------------------- /gruntfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-tsng 3 | * https://github.com/DamianEdwards/grunt-tsng 4 | * 5 | * Copyright (c) 2014 Damian Edwards 6 | * Licensed under the Apache 2.0 License. 7 | */ 8 | 9 | module.exports = function(grunt) { 10 | "use strict"; 11 | 12 | grunt.initConfig({ 13 | jshint: { 14 | all: [ 15 | "Gruntfile.js", 16 | "tests/compare.js", 17 | "tasks/**/*.js" 18 | ], 19 | options: { 20 | jshintrc: ".jshintrc" 21 | } 22 | }, 23 | clean: { 24 | test: { 25 | src: ["tests/*/actual"] 26 | } 27 | }, 28 | copy: { 29 | test: { 30 | files: [ 31 | { 32 | expand: true, 33 | cwd: "tests", 34 | src: "*/src/*.*", 35 | dest: "tests", 36 | rename: function (dest, src) { 37 | var newDest = dest + "/" + src.replace("/src/", "/actual/"); 38 | return newDest; 39 | } 40 | } 41 | ] 42 | } 43 | }, 44 | tsng: { 45 | options: { 46 | cwd: "tests" 47 | }, 48 | test: { 49 | files: [ 50 | { 51 | expand: true, 52 | cwd: "tests", 53 | src: ["*/actual/**/*.ts", "!**/*.ng.ts"], 54 | dest: "tests", 55 | rename: function (dest, src) { 56 | var parts = src.split("/"); 57 | var testName = parts[0]; 58 | var newDest = dest + "/" + testName + "/actual"; 59 | return newDest; 60 | } 61 | } 62 | ] 63 | } 64 | }, 65 | compare: { 66 | test: { 67 | files: [ 68 | { 69 | expand: true, 70 | cwd: "tests", 71 | src: "*/expected", 72 | dest: "tests", 73 | rename: function (dest, src) { 74 | var newDest = dest + "/" + src.replace("/expected", "/actual"); 75 | return newDest; 76 | } 77 | } 78 | ] 79 | } 80 | } 81 | }); 82 | 83 | grunt.loadTasks("tasks"); 84 | grunt.loadTasks("tests"); 85 | 86 | grunt.loadNpmTasks("grunt-contrib-jshint"); 87 | grunt.loadNpmTasks("grunt-contrib-clean"); 88 | grunt.loadNpmTasks("grunt-contrib-copy"); 89 | grunt.loadNpmTasks("grunt-typescript"); 90 | 91 | grunt.registerTask("test", ["clean:test", "copy:test", "tsng:test", "compare:test"]); 92 | 93 | grunt.registerTask("default", ["jshint", "test"]); 94 | }; -------------------------------------------------------------------------------- /tests/compare.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-tsng 3 | * https://github.com/DamianEdwards/grunt-tsng 4 | * 5 | * Copyright (c) 2014 Damian Edwards 6 | * Licensed under the Apache 2.0 License. 7 | */ 8 | 9 | module.exports = function (grunt) { 10 | "use strict"; 11 | 12 | var fs = require("fs"); 13 | var path = require("path"); 14 | var util = require("util"); 15 | 16 | grunt.registerMultiTask("compare", "Compares two directories", function () { 17 | grunt.log.writeln("Comparing directories:"); 18 | grunt.log.writeln(util.inspect(this.files.map(function (el) { return el.src[0]; }))); 19 | 20 | grunt.verbose.writeln("Compare this.files: " + util.inspect(this.files)); 21 | 22 | var results = []; 23 | 24 | this.files.every(function (fileset) { 25 | if (fileset.src.length !== 1) { 26 | grunt.log.error("Configured file set must contain just one directory: " + utils.inspect(fileset.src)); 27 | return false; 28 | } 29 | 30 | var src = path.resolve(fileset.src[0]); 31 | var dest = path.resolve(fileset.dest); 32 | 33 | if (!grunt.file.isDir(src)) { 34 | grunt.log.error("The configured src path is not a directory: " + src); 35 | return false; 36 | } 37 | 38 | if (!grunt.file.isDir(dest)) { 39 | grunt.log.error("The configured dest path is not a directory: " + dest); 40 | return false; 41 | } 42 | 43 | var result = compareDirs(src, dest); 44 | results.push(result); 45 | 46 | if (result.error) { 47 | return false; 48 | } 49 | 50 | return true; 51 | }); 52 | 53 | grunt.verbose.writeln("Results: " + util.inspect(results)); 54 | 55 | var errors = results 56 | .map(function(result) { 57 | return result.error; 58 | }) 59 | .filter(function(el) { 60 | return el; 61 | }); 62 | 63 | if (errors.length) { 64 | grunt.log.error(errors[0]); 65 | return false; 66 | } 67 | 68 | grunt.log.ok("All directories match!"); 69 | }); 70 | 71 | function compareDirs(dir1, dir2) { 72 | grunt.verbose.writeln(" " + dir1 + " -> " + dir2); 73 | 74 | var dir1Files = [], 75 | dir2Files = [], 76 | error; 77 | 78 | // TODO: Check folder structure first using getDirs 79 | 80 | grunt.file.recurse(dir1, function (abspath, rootdir, subdir, filename) { 81 | dir1Files.push({ 82 | abspath: abspath, 83 | rootdir: rootdir, 84 | subdir: subdir, 85 | filename: filename 86 | }); 87 | }); 88 | 89 | grunt.file.recurse(dir2, function (abspath, rootdir, subdir, filename) { 90 | dir2Files.push({ 91 | abspath: abspath, 92 | rootdir: rootdir, 93 | subdir: subdir, 94 | filename: filename 95 | }); 96 | }); 97 | 98 | //grunt.log.writeln(util.inspect(dir1Files)); 99 | //grunt.log.writeln(util.inspect(dir2Files)); 100 | 101 | // Compare number of files 102 | if (dir1Files.length !== dir2Files.length) { 103 | return { 104 | error: grunt.util.error("Directories have different file count: \r\n" + 105 | " " + dir1 + ": " + dir1Files.length + "\r\n" + 106 | " " + dir2 + ": " + dir2Files.length) 107 | }; 108 | } 109 | 110 | // Compare files 111 | dir1Files.every(function (dir1File, idx) { 112 | var dir2File = dir2Files[idx]; 113 | var dir1FileContent, dir2FileContent; 114 | 115 | grunt.verbose.writeln("Comparing files: \r\n" + 116 | " " + util.inspect(dir1File) + "\r\n" + 117 | " " + util.inspect(dir2File)); 118 | 119 | // Compare file name 120 | if (dir1File.subdir !== dir2File.subdir || dir1File.filename !== dir2File.filename) { 121 | error = { 122 | error: grunt.util.error("Mismatched file name found: \r\n" + 123 | " " + dir1File.abspath + "\r\n" + 124 | " " + dir2File.abspath) 125 | }; 126 | return false; 127 | } 128 | 129 | // Compare file content 130 | dir1FileContent = grunt.file.read(dir1File.abspath); 131 | dir2FileContent = grunt.file.read(dir2File.abspath); 132 | if (dir1FileContent !== dir2FileContent) { 133 | error = { 134 | error: grunt.util.error("Mismatched file content found: \r\n" + 135 | " " + dir1File.abspath + "\r\n" + 136 | " " + dir2File.abspath) 137 | }; 138 | return false; 139 | } 140 | 141 | return true; 142 | }); 143 | 144 | return error || { }; 145 | } 146 | 147 | function getDirs(rootDir) { 148 | return fs.readdirSync(rootDir) 149 | .map(function (file) { 150 | var filePath = path.join(rootDir, file); 151 | if (fs.statSync(filePath).isDirectory()) { 152 | return filePath; 153 | } 154 | }) 155 | .filter(function (el) { 156 | return el; 157 | }); 158 | } 159 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grunt-tsng v0.1.3 2 | 3 | > A TypeScript pre-processor for AngularJS. 4 | 5 | 6 | ## Getting Started 7 | This plugin requires Grunt `~0.4.0` 8 | 9 | If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command: 10 | 11 | ```shell 12 | npm install grunt-tsng --save-dev 13 | ``` 14 | 15 | Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript: 16 | 17 | ```js 18 | grunt.loadNpmTasks('grunt-tsng'); 19 | ``` 20 | 21 | 22 | ## Tsng task 23 | _Run this task with the `grunt tsng` command._ 24 | 25 | Task targets, files and options may be specified according to the grunt [Configuring tasks](http://gruntjs.com/configuring-tasks) guide. 26 | 27 | This task will analyze the set of TypeScript files fed to it and, based on conventions and comment annotations found, will generate new TypeScript files containing the required AngularJS registration calls. This means you can concentrate on writing your application using typed constructs (modules, classes, interfaces, etc.) and not have to worry about wiring it up to AngularJS as explicit modules and dependencies defined using strings. 28 | 29 | For example, you create a controller in a file called `MyController.ts` like this: 30 | ``` JavaScript 31 | module MyApp { 32 | class MyController { 33 | constructor ($location: ng.ILocationService) { 34 | // Do stuff with $location here 35 | 36 | } 37 | } 38 | } 39 | ``` 40 | 41 | and grunt-tsng will create a file called `MyController.ng.ts` like this: 42 | ``` JavaScript 43 | /// 44 | module MyApp { 45 | angular.module("MyApp", [ 46 | "$scope", 47 | MyController 48 | ]); 49 | 50 | class MyController { 51 | constructor ($location: ng.ILocationService) { 52 | // Do stuff with $location here 53 | 54 | } 55 | } 56 | } 57 | ``` 58 | 59 | ### Options 60 | 61 | #### extension 62 | 63 | Type: `string` 64 | Default: `.ng.ts` 65 | 66 | The file extension to use when generating the TypeScript files you'll eventually compile with the TypeScript compiler. 67 | 68 | 69 | ### Example usage 70 | ``` JavaScript 71 | grunt.initConfig({ 72 | tsng: { 73 | all: { 74 | src: ['app/*.ts', '!*.ng.ts'], 75 | dest: 'app' 76 | } 77 | } 78 | }); 79 | ``` 80 | 81 | 82 | ### Supported conventions & annotations 83 | 84 | Take a look at the [sample](https://github.com/DamianEdwards/grunt-tsng/tree/master/sample) for samples of usage. 85 | 86 | To run the sample locally: 87 | - Open a command prompt 88 | - Clone this repo 89 | - Change to the sample directory 90 | - Run `npm install` 91 | - Run `bower install` 92 | - Run `grunt sample` 93 | 94 | Grunt-tsng uses a number of conventions & comment annotations to discover the parts of your app you want to register with AngularJS. Comment annotations are simply a TS line comment followed by @ and the annotation name and any arguments in a method call fashion, e.g. `//@NgFilter('truncate')` 95 | 96 | Grunt-tsng works best when the following conventions are followed: 97 | - Each item you're creating is in its own file, i.e. one controller per file, one directive per file, etc. 98 | - You use TypeScript constructs for organizing and building your app, e.g. put modules, use classes and interfaces to define your items. 99 | - You compile your TypeScript to a single file per AngularJS app. 100 | - You keep the TypeScript items internal to the modules they're defined in (don't export them). Interfaces for your services and models however should be exported so you can use them from other parts of your app. 101 | - You use the [controller as](http://www.thinkster.io/angularjs/GmI3KetKo6/angularjs-experimental-controller-as-syntax) syntax for binding views to controllers. 102 | 103 | #### Modules 104 | TypeScript modules are tracked and mapped one-to-one with Angular modules. Each TypeScript module you declare will end up as an AngularJS module. To declare a module with dependencies, a config and/or run method, simply put the module in its own file with a `dependencies` array variable, configuration method and/or run method, e.g.: 105 | 106 | ``` JavaScript 107 | module MyApp { 108 | var dependencies = [ 109 | "ngRoute", 110 | AnotherModuleInMyApp 111 | ]; 112 | 113 | function configuration($routeProvider: ng.IRouteProvider) { 114 | // Configure routes here 115 | 116 | } 117 | } 118 | ``` 119 | 120 | Modules that are discovered but for which a dedicated file cannot be found will have a file created (in the configured dest directory) and any other file logically in that module will have a `/// ` element added to the top of it. 121 | 122 | 123 | #### Service dependencies 124 | Function dependencies are discovered automatically for the various types (controllers, services, etc.) and included in the generated registration code. Dependency names starting with a '$' are assumed to be built-in AngularJS services and are included as specified, otherwise the dependency name is resolved against the services discovered in your app files. 125 | 126 | 127 | #### Controllers & Services 128 | Controllers and services are discovered by convention for class names ending in 'Controller' and 'Services' respectively. Dependencies are parsed according to the rules outlined above in Service dependencies. 129 | 130 | Services that implement an interface will be registered with Angular using the full interface name rather than the service class name. This way you can inject them into your controllers by simply taking them as typed constructor arguments and the generated file will register the dependencies with Angular for you. 131 | 132 | HomeController.ts: 133 | ``` JavaScript 134 | module MyApp { 135 | class HomeController { 136 | constructor ($location: ng.ILocationService, aService: IAService) { 137 | 138 | } 139 | } 140 | } 141 | ``` 142 | 143 | AService.ts: 144 | ``` JavaScript 145 | module MyApp { 146 | export interface IAService { 147 | do(): string; 148 | } 149 | 150 | class AService implements IAService { 151 | constructor () { 152 | 153 | } 154 | 155 | public do() { 156 | return "Hello"; 157 | } 158 | } 159 | } 160 | ``` 161 | 162 | You can annotate a controller class with `//@NgController` to explicitly declare it, change its name, or even exclude it completely (this is useful in cases where you don't want to register the controller with Angular itself, e.g. when using the modal service from ui.bootstrap). 163 | ModalController.ts: 164 | ``` JavaScript 165 | module MyApp { 166 | //@NgController(skip=true) 167 | class ModalController { 168 | constructor ($location: ng.ILocationService) { 169 | 170 | } 171 | } 172 | } 173 | ``` 174 | 175 | #### Directives 176 | Description to come... 177 | 178 | ``` JavaScript 179 | module MyApp { 180 | interface IPreventSubmitAttributes extends ng.IAttributes { 181 | name: string; 182 | appPreventSubmit: string; 183 | } 184 | 185 | //@NgDirective('appPreventSubmit') 186 | class PreventSubmitDirective implements ng.IDirective { 187 | private _preventSubmit: any; 188 | 189 | constructor() { 190 | this.link = this.link.bind(this); 191 | } 192 | 193 | public restrict = "A"; 194 | 195 | public link(scope: any, element: ng.IAugmentedJQuery, attrs: IPreventSubmitAttributes) { 196 | element.submit(e => { 197 | if (scope.$eval(attrs.appPreventSubmit)) { 198 | e.preventDefault(); 199 | return false; 200 | } 201 | }); 202 | } 203 | } 204 | } 205 | ``` 206 | 207 | #### Filters 208 | Description to come... 209 | 210 | ``` JavaScript 211 | module MyApp { 212 | //@NgFilter 213 | function truncate(input: string, length: number) { 214 | if (!input) { 215 | return input; 216 | } 217 | 218 | if (input.length <= length) { 219 | return input; 220 | } else { 221 | return input.substr(0, length).trim() + "…"; 222 | } 223 | } 224 | } 225 | ``` 226 | 227 | 228 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2014 Damian Edwards 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /tasks/tsng.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-tsng 3 | * https://github.com/DamianEdwards/grunt-tsng 4 | * 5 | * Copyright (c) 2014 Damian Edwards 6 | * Licensed under the Apache 2.0 License. 7 | */ 8 | 9 | module.exports = function (grunt) { 10 | "use strict"; 11 | 12 | var path = require("path"); 13 | var util = require("util"); 14 | 15 | var newLine = (process.platform === "win32" ? "\r\n" : "\n"); 16 | 17 | grunt.registerMultiTask("tsng", "Generate AngularJS registration blocks based on conventions and annotations in TypeScript files.", function () { 18 | 19 | // this.target is current target 20 | // this.data is config for current target 21 | // this.files is globbed files array for current target 22 | 23 | var options = this.options({ 24 | extension: ".ng.ts" 25 | }); 26 | 27 | //grunt.log.writeln("Extension path: " + options.extension); 28 | 29 | var overallResult = { 30 | modules: [], 31 | controllers: [], 32 | services: [], 33 | directives: [], 34 | filters: [], 35 | fileTally: 0 36 | }; 37 | var error; 38 | 39 | this.files.forEach(function (fileSet, idx) { 40 | var setResult = processSet(fileSet, options); 41 | 42 | if (setResult.error) { 43 | error = setResult.error; 44 | return false; 45 | } 46 | 47 | //logResult(setResult, idx + 1); 48 | sumResult(setResult, overallResult); 49 | }); 50 | 51 | if (error) { 52 | grunt.log.error(error); 53 | return; 54 | } 55 | 56 | function sumResult(source, target) { 57 | if (!source || !target) { 58 | return; 59 | } 60 | 61 | for (var key in target) { 62 | if (!target.hasOwnProperty(key) || !source.hasOwnProperty(key)) { 63 | continue; 64 | } 65 | 66 | var targetType = (typeof (target[key])).toLowerCase(); 67 | var sourceType = (typeof (source[key])).toLowerCase(); 68 | 69 | if (targetType !== sourceType) { 70 | continue; 71 | } 72 | 73 | if (targetType === "number" || targetType === "string") { 74 | target[key] = target[key] + source[key]; 75 | } else if (Array.isArray(target[key])) { 76 | target[key] = target[key].concat(source[key]); 77 | } 78 | } 79 | } 80 | 81 | function logResult(result, setId) { 82 | grunt.log.writeln("------------------------------------------"); 83 | grunt.log.writeln("File Set #" + setId); 84 | grunt.log.writeln("------------------------------------------"); 85 | 86 | grunt.log.writeln("Modules:"); 87 | result.modules.forEach(function (module) { 88 | grunt.log.writeln(" " + module.name + (module.file ? " defined in " + module.file + " with " + (module.dependencies ? module.dependencies.length : 0) + " dependencies" : " has no file")); 89 | }); 90 | 91 | grunt.log.writeln("Controllers:"); 92 | result.controllers.forEach(function (controller) { 93 | grunt.log.writeln(" " + controller.name + " using fn " + controller.fnName + " with " + controller.dependencies.length + " dependencies from " + controller.file); 94 | }); 95 | 96 | grunt.log.writeln("Services:"); 97 | result.services.forEach(function (service) { 98 | grunt.log.writeln(" " + service.name + " using fn " + service.fnName + " with " + service.dependencies.length + " dependencies from " + service.file); 99 | }); 100 | 101 | grunt.log.writeln("Directives:"); 102 | result.directives.forEach(function (directive) { 103 | grunt.log.writeln(" " + directive.name + " using fn " + directive.fnName + " with " + directive.dependencies.length + " dependencies from " + directive.file); 104 | }); 105 | 106 | grunt.log.writeln("Filters:"); 107 | result.filters.forEach(function (filter) { 108 | grunt.log.writeln(" " + filter.name + " using fn " + filter.fnName + " from " + filter.file); 109 | }); 110 | 111 | for (var key in result) { 112 | if (key === "fileTally" || !result.hasOwnProperty(key)) { 113 | continue; 114 | } 115 | 116 | var items = result[key]; 117 | grunt.log.writeln(items.length + " " + key + " found in " + result.fileTally + " files"); 118 | } 119 | } 120 | 121 | function processSet(fileSet, options) { 122 | grunt.verbose.writeln("processSet->fileSet: " + util.inspect(fileSet)); 123 | 124 | var result = { 125 | modules: [], 126 | controllers: [], 127 | services: [], 128 | directives: [], 129 | filters: [], 130 | fileTally: 0 131 | }; 132 | var modules = {}; 133 | var files = {}; 134 | var error; 135 | var dest = path.resolve(fileSet.dest); 136 | var moduleName; 137 | var serviceNames; 138 | 139 | grunt.verbose.writeln("dest file path: " + dest); 140 | 141 | fileSet.src.forEach(function (filepath) { 142 | var fileResult = processFile(filepath, dest, options); 143 | fileResult.path = filepath; 144 | 145 | if (fileResult.error) { 146 | error = fileResult.error; 147 | return false; 148 | } 149 | 150 | fileResult.module = mergeModules(fileResult, modules); 151 | 152 | if (!fileResult.module) { 153 | throw new Error("File result for file " + filepath + " doesn't have a module"); 154 | } 155 | 156 | if (!fileResult.module.file) { 157 | files[filepath] = fileResult; 158 | } 159 | 160 | sumResult(fileResult, result); 161 | result.modules.push(fileResult.module); 162 | result.fileTally++; 163 | }); 164 | 165 | if (error) { 166 | return { error: error }; 167 | } 168 | 169 | serviceNames = result.services.map(function (service) { 170 | return service.name; 171 | }); 172 | 173 | // Emit module files 174 | var moduleNames = []; 175 | for (moduleName in modules) { 176 | if (!modules.hasOwnProperty(moduleName)) { 177 | continue; 178 | } 179 | 180 | moduleNames.push(modules[moduleName].name); 181 | } 182 | 183 | for (moduleName in modules) { 184 | if (!modules.hasOwnProperty(moduleName)) { 185 | continue; 186 | } 187 | 188 | emitModuleFile(modules[moduleName], dest, moduleNames, serviceNames, options); 189 | 190 | if (!modules[moduleName].file) { 191 | throw new Error("Module " + moduleName + " doesn't have a file"); 192 | } 193 | } 194 | 195 | // Emit non-module files 196 | for (var filepath in files) { 197 | if (!files.hasOwnProperty(filepath)) { 198 | continue; 199 | } 200 | 201 | emitFile(files[filepath], serviceNames, options); 202 | } 203 | 204 | return result; 205 | } 206 | 207 | function processFile(filepath, dest, options) { 208 | var result = { 209 | module: null, 210 | controllers: [], 211 | services: [], 212 | directives: [], 213 | filters: [] 214 | }; 215 | var regex = { 216 | // //@NgModule('moduleName') 217 | // module My.Great.Module { 218 | moduleComment: /^\s*\/\/@NgModule(?:\(?['"]?([\w.]+)['"]?\)?\s*)?$/, 219 | moduleDeclaration: /^\s*(?:export\s+)?module\s*([\w.]*)\s*{\s*$/, 220 | 221 | // //@NgController('controllerName') 222 | // class MyController implements IMyViewModel { 223 | controllerComment: /^\s*\/\/@NgController(?:\(?['"]?([\w]*|skip\=true)['"]?\)?\s*)?$/, 224 | controllerDeclaration: /^\s*(?:export\s+)?class (\w+Controller)\s*/, 225 | 226 | // //@NgService('serviceName') 227 | // class MyService implements IMyService { 228 | serviceComment: /^\s*\/\/@NgService(?:\(?['"]?(\w+)['"]?\)?\s*)?$/, 229 | serviceDeclaration: /^\s*(?:export\s+)?class (\w+Service)\s+(?:implements\s+([\w.]+)\s*{)?/, 230 | 231 | // //@NgDirective('directiveName') 232 | // class MyDirective implements ng.IDirective { 233 | directiveComment: /^\s*\/\/@NgDirective(?:\(?['"]?(\w+)['"]?\)?\s*)?$/, 234 | directiveDeclaration: /^\s*(?:export\s+)?class (\w+Directive)\s+(?:implements\s+(\w.+)\s*{)?/, 235 | 236 | // //@NgFilter('filterName') 237 | // function filter(input: string) { 238 | filterComment: /^\s*\/\/\s*@NgFilter(?:\s*\(\s*['"]?(\w+)['"]?\s*\))?\s*$/, 239 | filterDeclaration: /^\s*function\s*([a-zA-Z_$]+)\s*\([a-zA-Z0-9_$:,\s]*\)/, 240 | 241 | // constructor($window: ng.IWindowService) { 242 | constructor: /constructor\s*\(\s*([^(]*)\s*\)\s*{/, 243 | 244 | closingBrace: /^\s*}\s*$/ 245 | }; 246 | var content = grunt.file.read(filepath); 247 | var lines = content.split(newLine); 248 | var module, line, matches, state, lastClosingBraceLine, error; 249 | var moduleFile; 250 | var expect = { 251 | anything: 0, 252 | moduleDeclaration: 1, 253 | controllerDeclaration: 2, 254 | serviceDeclaration: 4, 255 | directiveComment: 8, 256 | directiveDeclaration: 16, 257 | filterDeclaration: 32 258 | }; 259 | var expecting = expect.anything; 260 | 261 | //debugger; 262 | 263 | for (var i = 0; i < lines.length; i++) { 264 | line = lines[i]; 265 | 266 | // Check for closing brace on a line by itself 267 | matches = line.match(regex.closingBrace); 268 | if (matches) { 269 | lastClosingBraceLine = i; 270 | continue; 271 | } 272 | 273 | if (expecting === expect.anything) { 274 | // Check for module comment 275 | matches = line.match(regex.moduleComment); 276 | if (matches) { 277 | expecting = expect.moduleDeclaration; 278 | state = matches; 279 | continue; 280 | } 281 | 282 | // Check for module declaration 283 | matches = line.match(regex.moduleDeclaration); 284 | if (matches) { 285 | if (module) { 286 | // A module is already declared for this file 287 | error = "Error: " + filepath + "(" + i + "): Only one module can be declared per file"; 288 | break; 289 | } 290 | 291 | moduleFile = parseModuleFile(filepath); 292 | moduleFile.name = matches[1]; 293 | moduleFile.declarationLine = i; 294 | module = moduleFile; 295 | 296 | state = null; 297 | } 298 | 299 | // Check for controller comment 300 | matches = line.match(regex.controllerComment); 301 | if (matches) { 302 | expecting = expect.controllerDeclaration; 303 | state = matches; 304 | continue; 305 | } 306 | 307 | // Check for controller declaration 308 | matches = line.match(regex.controllerDeclaration); 309 | if (matches) { 310 | (function () { 311 | var fnName = matches[1]; 312 | var name = (module ? module.name + "." : "") + fnName; 313 | var ctor = parseConstructor(content) || { args: [] }; 314 | 315 | result.controllers.push({ 316 | module: module, 317 | name: name, 318 | fnName: fnName, 319 | dependencies: ctor.args, 320 | file: filepath, 321 | ctorStartLine: ctor.startLine, 322 | ctorEndLine: ctor.endLine 323 | }); 324 | }()); 325 | expecting = expect.anything; 326 | continue; 327 | } 328 | 329 | // Check for service comment 330 | matches = line.match(regex.serviceComment); 331 | if (matches) { 332 | expecting = expect.serviceDeclaration; 333 | state = matches; 334 | continue; 335 | } 336 | 337 | // Check for service declaration 338 | matches = line.match(regex.serviceDeclaration); 339 | if (matches) { 340 | (function () { 341 | var className = matches[1]; 342 | var interfaceName = matches[2]; 343 | var name = (module ? module.name + "." : "") + (interfaceName || className); 344 | var ctor = parseConstructor(content) || { args: [] }; 345 | 346 | result.services.push({ 347 | module: module, 348 | name: name, 349 | fnName: className, 350 | dependencies: ctor.args, 351 | file: filepath, 352 | ctorStartLine: ctor.startLine, 353 | ctorEndLine: ctor.endLine 354 | }); 355 | }()); 356 | expecting = expect.anything; 357 | continue; 358 | } 359 | 360 | // Check for directive comment 361 | matches = line.match(regex.directiveComment); 362 | if (matches) { 363 | //debugger; 364 | expecting = expect.directiveComment | expect.directiveDeclaration; 365 | state = { names: [] }; 366 | state.names.push(matches[1]); 367 | continue; 368 | } 369 | 370 | // Check for filter comment 371 | matches = line.match(regex.filterComment); 372 | if (matches) { 373 | expecting = expect.filterDeclaration; 374 | state = matches; 375 | continue; 376 | } 377 | } 378 | 379 | if (expecting === expect.moduleDeclaration) { 380 | // Check for module declaration 381 | matches = line.match(regex.moduleDeclaration); 382 | if (matches) { 383 | if (module) { 384 | // A module is already declared for this file 385 | error = "Error: " + filepath + "(" + i + "): Only one module can be declared per file"; 386 | break; 387 | } 388 | 389 | moduleFile = parseModuleFile(filepath); 390 | moduleFile.name = state[1] || matches[1]; 391 | module = moduleFile; 392 | 393 | state = null; 394 | expecting = expect.anything; 395 | } else { 396 | // A module comment was found but the next line wasn't a module declaration 397 | error = "Error: " + filepath + "(" + i + "): @NgModule must be followed by a TypeScript module declaration, e.g. module My.Module.Name {"; 398 | break; 399 | } 400 | } 401 | 402 | if (expecting === expect.controllerDeclaration) { 403 | if (state[1] === "skip=true") { 404 | state = null; 405 | expecting = expect.anything; 406 | continue; 407 | } 408 | 409 | // Check for controller declaration 410 | matches = line.match(regex.controllerDeclaration); 411 | if (matches) { 412 | (function () { 413 | var name = (module ? module.name + "." : "") + (state[1] || matches[1]); 414 | var ctor = parseConstructor(content) || { args: [] }; 415 | 416 | result.controllers.push({ 417 | module: module, 418 | name: name, 419 | fnName: matches[1], 420 | dependencies: ctor.args, 421 | file: filepath, 422 | startLine: ctor.startLine, 423 | endLine: ctor.endLine 424 | }); 425 | }()); 426 | expecting = expect.anything; 427 | continue; 428 | } else { 429 | // A controller comment was found but the next line wasn't a controller declaration 430 | error = "Error: " + filepath + "(" + i + "): @NgController must be followed by a TypeScript class declaration ending with 'Controller', e.g. class MyController implements IMyViewModel {"; 431 | break; 432 | } 433 | } 434 | 435 | if (expecting === expect.serviceDeclaration) { 436 | // Check for service declaration 437 | matches = line.match(regex.serviceDeclaration); 438 | if (matches) { 439 | (function () { 440 | var className = matches[1]; 441 | var interfaceName = matches[2]; 442 | var name = (module ? module.name + "." : "") + ((state ? state[1] : null) || interfaceName || className); 443 | var ctor = parseConstructor(content) || { args: [] }; 444 | 445 | result.services.push({ 446 | module: module, 447 | name: name, 448 | fnName: className, 449 | dependencies: ctor.args, 450 | file: filepath, 451 | ctorStartLine: ctor.startLine, 452 | ctorEndLine: ctor.endLine 453 | }); 454 | }()); 455 | expecting = expect.anything; 456 | continue; 457 | } 458 | } 459 | 460 | if (expecting & expect.directiveComment) { 461 | // Check for directive comment 462 | matches = line.match(regex.directiveComment); 463 | if (matches) { 464 | expecting = expect.directiveComment | expect.directiveDeclaration; 465 | state.names.push(matches[1]); 466 | continue; 467 | } 468 | } 469 | 470 | if (expecting & expect.directiveDeclaration) { 471 | // Check for directive function 472 | matches = line.match(regex.directiveDeclaration); 473 | if (matches) { 474 | (function () { 475 | var fnName = matches[1]; 476 | var ctor = parseConstructor(content) || { args: [] }; 477 | 478 | state.names.forEach(function (name) { 479 | result.directives.push({ 480 | module: module, 481 | file: filepath, 482 | name: name, 483 | fnName: fnName, 484 | classLine: i, 485 | ctorStartLine: ctor.startLine, 486 | ctorEndLine: ctor.endLine, 487 | dependencies: ctor.args 488 | }); 489 | }); 490 | }()); 491 | expecting = expect.anything; 492 | continue; 493 | } 494 | } 495 | 496 | if (expecting === expect.filterDeclaration) { 497 | // Check for filter function 498 | matches = line.match(regex.filterDeclaration); 499 | if (matches) { 500 | result.filters.push({ 501 | module: module, 502 | name: state[1] || matches[1], 503 | fnName: matches[1], 504 | file: filepath 505 | }); 506 | state = null; 507 | expecting = expect.anything; 508 | continue; 509 | } 510 | } 511 | } 512 | 513 | // EOF 514 | if (expecting !== expect.anything) { 515 | error = "Error: End of file " + filepath + " reached while expecting " + expecting; 516 | } 517 | 518 | if (error) { 519 | return { 520 | error: error 521 | }; 522 | } 523 | 524 | result.closingBraceLine = lastClosingBraceLine; 525 | result.module = module; 526 | 527 | return result; 528 | } 529 | 530 | function parseConstructor(fileContents) { 531 | // Extract details from constructor function 532 | // constructor($window: ng.IWindowService) { 533 | var regex = /constructor\s*\(\s*([^(]*)\s*\)\s*{/; 534 | var matches = fileContents.match(regex); 535 | var result = {}; 536 | 537 | if (matches) { 538 | result.args = []; 539 | if (matches[1]) { 540 | matches[1].split(",").forEach(function (arg) { 541 | var argParts = arg.split(":"); 542 | var a = { name: argParts[0].trim() }; 543 | if (argParts.length > 1) { 544 | a.type = argParts[1].trim(); 545 | } 546 | result.args.push(a); 547 | }); 548 | } 549 | 550 | // Find line numbers where the constructor function starts/ends 551 | var startIndex = fileContents.indexOf(matches[0]); 552 | var endIndex = startIndex + matches[0].length; 553 | 554 | result.startLine = fileContents.substr(0, startIndex).split(newLine).length - 1; 555 | result.endLine = fileContents.substr(0, endIndex).split(newLine).length - 1; 556 | 557 | return result; 558 | } 559 | 560 | // No constructor found 561 | return null; 562 | } 563 | 564 | function parseModuleFile(filepath) { 565 | var regex = { 566 | dependencies: /var\s+dependencies\s*=\s*\[([\w\s.,"']*)\]/, 567 | // BUG: This finds configuration functions that are commented out 568 | configFn: /function\s*(configuration)\s*\(\s*([\w$:.,\s]*)\s*\)\s*{/, 569 | // BUG: This finds run functions that are commented out 570 | runFn: /function\s*(run)\s*\(\s*([\w$:.,\s]*)\s*\)\s*{/ 571 | }; 572 | var matches = {}; 573 | var result = {}; 574 | var content = grunt.file.read(filepath); 575 | 576 | for (var key in regex) { 577 | if (!regex.hasOwnProperty(key)) { 578 | continue; 579 | } 580 | 581 | matches[key] = content.match(regex[key]); 582 | if (matches[key]) { 583 | result.file = filepath; 584 | } 585 | } 586 | 587 | if (!result.file) { 588 | return result; 589 | } 590 | 591 | if (matches.dependencies) { 592 | var arrayMembers = matches.dependencies[1]; 593 | var dependencies = []; 594 | if (arrayMembers) { 595 | arrayMembers.split(",").forEach(function (dependency) { 596 | dependency = trim(dependency.trim(), ["\"", "'"]); 597 | dependencies.push(dependency); 598 | }); 599 | } 600 | result.dependencies = dependencies; 601 | } 602 | 603 | ["configFn", "runFn"].forEach(function (fn) { 604 | if (matches[fn]) { 605 | var args = matches[fn][2]; 606 | var dependencies = []; 607 | if (args) { 608 | args.split(",").forEach(function (arg) { 609 | var parts = arg.split(":"); 610 | var dependency = { 611 | name: parts[0].trim() 612 | }; 613 | 614 | if (parts[1]) { 615 | dependency.type = parts[1].trim(); 616 | } 617 | 618 | dependencies.push(dependency); 619 | }); 620 | } 621 | result[fn] = { 622 | fnName: matches[fn][1], 623 | dependencies: dependencies 624 | }; 625 | } 626 | }); 627 | 628 | return result; 629 | } 630 | 631 | function emitModuleFile(module, dest, moduleNames, serviceNames, options) { 632 | var filepath = ""; 633 | var content = ""; 634 | var srcLines; 635 | 636 | if (module.file) { 637 | //debugger; 638 | // Module already has a file defined, just add the module registration 639 | filepath = module.file.substr(0, module.file.length - 3) + options.extension; 640 | srcLines = grunt.file.read(module.file).split(newLine); 641 | 642 | //grunt.log.writeln("module.declarationLine=" + module.declarationLine); 643 | 644 | srcLines.forEach(function (line, i) { 645 | if (i === (module.declarationLine + 1)) { 646 | 647 | // Add the module registration 648 | content += indent() + "angular.module(\"" + module.name + "\", [" + newLine; 649 | 650 | if (module.dependencies && module.dependencies.length) { 651 | module.dependencies.forEach(function (d) { 652 | var resolvedDependencyName = resolveTypeName(d, module.name, moduleNames); 653 | content += indent(2) + "\"" + (resolvedDependencyName || d) + "\"," + newLine; 654 | }); 655 | } 656 | 657 | content += indent() + "])"; 658 | 659 | ["config", "run"].forEach(function (method) { 660 | var fn = module[method + "Fn"]; 661 | if (fn) { 662 | content += "." + method + "([" + newLine; 663 | fn.dependencies.forEach(function (d) { 664 | var typeName; 665 | if (d.name.substr(0, 1) === "$") { 666 | typeName = d.name; 667 | } else { 668 | typeName = resolveTypeName(d.type, module.name, serviceNames); 669 | if (!typeName) { 670 | // Couldn't resolve type name 671 | throw new Error("Error: Can't resolve dependency for module function " + module.name + "." + method + " with name " + d.type); 672 | } 673 | } 674 | content += indent(2) + "\"" + typeName + "\"," + newLine; 675 | }); 676 | content += indent(2) + fn.fnName + newLine + indent() + "])"; 677 | } 678 | }); 679 | 680 | content += ";" + newLine + newLine; 681 | } 682 | 683 | content += line; 684 | 685 | if (i < (srcLines.length - 1)) { 686 | content += newLine; 687 | } 688 | }); 689 | } else { 690 | // We need to render a whole file 691 | filepath = path.join(dest, module.name + options.extension); 692 | content = "module " + module.name + " {" + newLine; 693 | content += indent() + "angular.module(\"" + module.name + "\", []);" + newLine; 694 | content += "}"; 695 | } 696 | 697 | grunt.file.write(filepath, content); 698 | module.file = filepath; 699 | } 700 | 701 | function emitFile(file, serviceNames, options) { 702 | var filepath; 703 | var srcLines; 704 | var content = ""; 705 | var module = file.module; 706 | 707 | filepath = file.path.substr(0, file.path.length - 3) + options.extension; 708 | srcLines = grunt.file.read(file.path).split(newLine); 709 | 710 | var emitCtor = false; 711 | if (file.directives.length && !file.directives[0].ctorStartLine) { 712 | emitCtor = true; 713 | } 714 | 715 | srcLines.forEach(function (line, i) { 716 | if (i === 0 && module.file) { 717 | // Add reference to module file 718 | // e.g. /// 719 | 720 | content += "/// " + newLine + newLine; 721 | } 722 | 723 | var emitBind = file.directives.length ? 724 | file.directives[0].ctorEndLine ? 725 | (file.directives[0].ctorEndLine + 1) === i // Line after the ctor declartion ends 726 | : (file.directives[0].classLine + 1) === i // No ctor already, so line after the class declaration 727 | : false; 728 | 729 | if (emitBind) { 730 | if (emitCtor) { 731 | // Need to generate a ctor 732 | content += indent(2) + "constructor() {" + newLine; 733 | } 734 | // Emit function to bind instance methods to 'this' 735 | content += indent(3) + "for (var m in this) {" + newLine; 736 | content += indent(4) + "if (this[m].bind) {" + newLine; 737 | content += indent(5) + "this[m] = this[m].bind(this);" + newLine; 738 | content += indent(4) + "}" + newLine; 739 | content += indent(3) + "}" + newLine; 740 | if (emitCtor) { 741 | // Need to generate a ctor 742 | content += indent(2) + "}" + newLine + newLine; 743 | } 744 | } 745 | 746 | if (i === file.closingBraceLine && module.file) { 747 | content += indent() + newLine; 748 | content += indent() + "angular.module(\"" + module.name + "\")"; 749 | 750 | // Register controllers 751 | file.controllers.forEach(function (controller) { 752 | content += newLine; 753 | content += indent(2) + ".controller(\"" + controller.name + "\", [" + newLine; 754 | 755 | if (controller.dependencies && controller.dependencies.length) { 756 | controller.dependencies.forEach(function (d) { 757 | var typeName; 758 | if (d.name.substr(0, 1) === "$") { 759 | typeName = d.name; 760 | } else { 761 | typeName = resolveTypeName(d.type, module.name, serviceNames); 762 | if (!typeName) { 763 | // Couldn't resolve type name 764 | throw new Error("Error: Can't resolve dependency for controller " + controller.name + " with name " + d.type); 765 | } 766 | } 767 | content += indent(3) + "\"" + typeName + "\"," + newLine; 768 | }); 769 | } 770 | 771 | content += indent(3) + controller.fnName + newLine; 772 | content += indent(2) + "])"; 773 | }); 774 | 775 | // Register services 776 | file.services.forEach(function (service) { 777 | content += newLine; 778 | content += indent(2) + ".service(\"" + service.name + "\", [" + newLine; 779 | 780 | if (service.dependencies && service.dependencies.length) { 781 | service.dependencies.forEach(function (d) { 782 | var typeName; 783 | if (d.name.substr(0, 1) === "$") { 784 | typeName = d.name; 785 | } else { 786 | typeName = resolveTypeName(d.type, module.name, serviceNames); 787 | if (!typeName) { 788 | // Couldn't resolve type name 789 | throw new Error("Error: Can't resolve dependency for service " + service.name + " with name " + d.type); 790 | } 791 | } 792 | content += indent(3) + "\"" + typeName + "\"," + newLine; 793 | }); 794 | } 795 | 796 | content += indent(3) + service.fnName + newLine; 797 | content += indent(2) + "])"; 798 | }); 799 | 800 | // Register directives 801 | file.directives.forEach(function (directive) { 802 | content += newLine; 803 | content += indent(2) + ".directive(\"" + directive.name + "\", [" + newLine; 804 | 805 | if (directive.dependencies && directive.dependencies.length) { 806 | directive.dependencies.forEach(function (d) { 807 | var typeName; 808 | if (d.name.substr(0, 1) === "$") { 809 | typeName = d.name; 810 | } else { 811 | typeName = resolveTypeName(d.type, module.name, serviceNames); 812 | if (!typeName) { 813 | // Couldn't resolve type name 814 | throw new Error("Error: Can't resolve dependency for directive " + directive.name + " with name " + d.type); 815 | } 816 | } 817 | content += indent(3) + "\"" + typeName + "\"," + newLine; 818 | }); 819 | } 820 | 821 | var alphabet = "abcdefghijklmnopqrstuvwxyz"; 822 | alphabet += alphabet.toUpperCase(); 823 | 824 | var argList = directive.dependencies.map(function (d, index) { 825 | return alphabet.substr(index, 1); 826 | }); 827 | 828 | content += indent(3) + "function ("; 829 | content += argList; 830 | content += ") {" + newLine; 831 | content += indent(4) + "return new " + directive.fnName + "(" + argList + ");" + newLine; 832 | content += indent(3) + "}" + newLine; 833 | content += indent(2) + "])"; 834 | }); 835 | 836 | // Register filters 837 | file.filters.forEach(function (filter) { 838 | content += newLine; 839 | content += indent(2) + ".filter(\"" + filter.name + "\", () => " + filter.fnName + ")"; 840 | }); 841 | 842 | content += ";" + newLine; 843 | } 844 | 845 | content += line; 846 | 847 | if (i < (srcLines.length - 1)) { 848 | content += newLine; 849 | } 850 | }); 851 | 852 | grunt.file.write(filepath, content); 853 | } 854 | 855 | function mergeModules(fileResult, modules) { 856 | var module = fileResult.module; 857 | 858 | if (!module) { 859 | return module; 860 | } 861 | 862 | if (!module.file && 863 | (!fileResult.controllers || !fileResult.controllers.length) && 864 | (!fileResult.services || !fileResult.services.length) && 865 | (!fileResult.directives || !fileResult.directives.length) && 866 | (!fileResult.filters || !fileResult.filters.length)) { 867 | 868 | //grunt.log.writeln("Module " + module.name + " contains no angular types, skipping file emission"); 869 | 870 | // No angular types created, just no-op 871 | return module; 872 | } 873 | 874 | var resolvedModule = module; 875 | 876 | if (modules[module.name]) { 877 | // Existing module 878 | if (module.file) { 879 | if (modules[module.name].file) { 880 | // Error: Module defined in multiple files 881 | throw new Error("tsng: Module '" + module.name + "' defined in multiple files"); 882 | } 883 | modules[module.name].file = module.file; 884 | } 885 | resolvedModule = modules[module.name]; 886 | } else { 887 | modules[module.name] = module; 888 | } 889 | 890 | return resolvedModule; 891 | } 892 | 893 | function resolveTypeName(name, moduleName, allNames) { 894 | /// 895 | /// 896 | /// 897 | 898 | //debugger; 899 | //grunt.log.writeln(util.inspect({ name: name, moduleName: moduleName, allNames: allNames })); 900 | 901 | var prefix, matchedIndex; 902 | var parts = moduleName.split("."); 903 | 904 | if (parts.length === 1) { 905 | matchedIndex = allNames.indexOf(moduleName + "." + name); 906 | if (matchedIndex >= 0) { 907 | return allNames[matchedIndex]; 908 | } 909 | // No match found! 910 | return null; 911 | } 912 | 913 | for (var i = parts.length - 1; i >= 0; i--) { 914 | prefix = ""; 915 | parts.forEach(function (part, index) { 916 | if (index <= i) { 917 | prefix += part + "."; 918 | } 919 | }); 920 | 921 | matchedIndex = allNames.indexOf(prefix + name); 922 | if (matchedIndex >= 0) { 923 | return allNames[matchedIndex]; 924 | } 925 | } 926 | 927 | // No match found! 928 | return null; 929 | } 930 | 931 | function trim(target, chars) { 932 | /// 933 | /// 934 | 935 | //debugger; 936 | 937 | var result, i, c; 938 | 939 | chars = chars || [" "]; 940 | 941 | if (!target) { 942 | return target; 943 | } 944 | 945 | result = ""; 946 | 947 | // Trim from start 948 | for (i = 0; i < target.length; i++) { 949 | c = target[i]; 950 | if (chars.indexOf(c) < 0) { 951 | result = target.substr(i); 952 | break; 953 | } 954 | } 955 | 956 | // Trim from end 957 | for (i = result.length - 1; i >= 0; i--) { 958 | c = result[i]; 959 | if (chars.indexOf(c) < 0) { 960 | result = result.substring(0, i + 1); 961 | break; 962 | } 963 | } 964 | 965 | return result; 966 | } 967 | 968 | function indent(length, char) { 969 | length = length || 1; // Default to 1 level of indent 970 | char = char || " "; // Default to 4 spaces 971 | var result = ""; 972 | for (var i = 0; i < length; i++) { 973 | result += char; 974 | } 975 | return result; 976 | } 977 | }); 978 | }; --------------------------------------------------------------------------------