Skip to content
This repository was archived by the owner on Mar 26, 2018. It is now read-only.

Adding typescript support to generator-angular #539

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ var Generator = module.exports = function Generator(args, options) {
this.env.options.coffee = this.options.coffee;
}

if (typeof this.env.options.typescript === 'undefined') {
this.option('typescript', {
desc: 'Generate TypeScript instead of JavaScript'
});

// attempt to detect if user is using TS or not
// if cml arg provided, use that; else look for the existence of ts
if (!this.options.typescript &&
this.expandFiles(path.join(this.appPath, '/scripts/**/*.ts'), {}).length > 0) {
this.options.typescript = true;
}

this.env.options.typescript = this.options.typescript;
}

if (typeof this.env.options.minsafe === 'undefined') {
this.option('minsafe', {
desc: 'Generate AngularJS minification safe code'
Expand Down Expand Up @@ -206,13 +221,15 @@ Generator.prototype.askForModules = function askForModules() {

if (this.cookiesModule) {
angMods.push("'ngCookies'");
this.env.options.ngCookies = true;
}

if (this.resourceModule) {
angMods.push("'ngResource'");
this.env.options.ngResource = true;
}
if (this.sanitizeModule) {
angMods.push("'ngSanitize'");
this.env.options.ngSanitize = true;
}
if (this.routeModule) {
angMods.push("'ngRoute'");
Expand Down Expand Up @@ -263,6 +280,7 @@ Generator.prototype.createIndexHtml = function createIndexHtml() {

Generator.prototype.packageFiles = function () {
this.coffee = this.env.options.coffee;
this.typescript = this.env.options.typescript;
this.template('../../templates/common/_bower.json', 'bower.json');
this.template('../../templates/common/_package.json', 'package.json');
this.template('../../templates/common/Gruntfile.js', 'Gruntfile.js');
Expand Down
3 changes: 3 additions & 0 deletions main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ util.inherits(Generator, ScriptBase);

Generator.prototype.createAppFile = function createAppFile() {
this.angularModules = this.env.options.angularDeps;
this.ngCookies = this.env.options.ngCookies;
this.ngResource = this.env.options.ngResource;
this.ngSanitize = this.env.options.ngSanitize;
this.ngRoute = this.env.options.ngRoute;
this.appTemplate('app', 'scripts/app');
};
41 changes: 37 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ angular.module('myMod').config(function ($provide) {
## Options
In general, these options can be applied to any generator, though they only affect generators that produce scripts.

### CoffeeScript
For generators that output scripts, the `--coffee` option will output CoffeeScript instead of JavaScript.
### CoffeeScript and TypeScript
For generators that output scripts, the `--coffee` option will output CoffeeScript instead of JavaScript, and `--typescript` will out put TypeScript instead of JavaScript.

For example:
```bash
Expand All @@ -187,9 +187,42 @@ angular.module('myMod')
.controller 'UserCtrl', ($scope) ->
```

A project can mix CoffeScript and JavaScript files.
For example:
```bash
yo angular:controller user --typescript
```

Produces `app/scripts/controller/user.ts`:
```typescript
/// <reference path="../app.ts" />

'use strict';

module demoApp {
export interface IUserScope extends ng.IScope {
awesomeThings: any[];
}

export class UserCtrl {

constructor (private $scope:IUserScope) {
$scope.awesomeThings = [
'HTML5 Boilerplate',
'AngularJS',
'Karma'
];
}
}
}

angular.module('demoApp')
.controller('UserCtrl', demoApp.UserCtrl);
```


A project can mix TypeScript, CoffeScript, and JavaScript files.

To output JavaScript files, even if CoffeeScript files exist (the default is to output CoffeeScript files if the generator finds any in the project), use `--coffee=false`.
To output JavaScript files, even if CoffeeScript (or TypeScript) files exist (the default is to output CoffeeScript files if the generator finds any in the project), use `--coffee=false` and/or `--typescript=false`.

### Minification Safe

Expand Down
4 changes: 3 additions & 1 deletion route/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ util.inherits(Generator, ScriptBase);

Generator.prototype.rewriteAppJs = function () {
var coffee = this.env.options.coffee;
var typescript = this.env.options.typescript;

var config = {
file: path.join(
this.env.options.appPath,
'scripts/app.' + (coffee ? 'coffee' : 'js')
'scripts/app.' + (coffee ? 'coffee' : typescript ? 'ts': 'js')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure the splicable section doesn't also need a condition for typescript? Maybe (coffee || typescript)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, because typescript is a superset of javascript, so it has the same syntax as javascript. CoffeeScript has different language syntax.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great. Just making sure.

),
needle: '.otherwise',
splicable: [
Expand Down
19 changes: 19 additions & 0 deletions script-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ var Generator = module.exports = function Generator() {
this.env.options.testPath = this.env.options.testPath || 'test/spec';
}

this.env.options.typescript = this.options.typescript;
if (typeof this.env.options.typescript === 'undefined') {
this.option('typescript');

// attempt to detect if user is using TS or not
// if cml arg provided, use that; else look for the existence of ts
if (!this.options.typescript &&
this.expandFiles(path.join(this.env.options.appPath, '/scripts/**/*.ts'), {}).length > 0) {
this.options.typescript = true;
}

this.env.options.typescript = this.options.typescript;
}

this.env.options.coffee = this.options.coffee;
if (typeof this.env.options.coffee === 'undefined') {
this.option('coffee');
Expand Down Expand Up @@ -59,6 +73,11 @@ var Generator = module.exports = function Generator() {
this.scriptSuffix = '.coffee';
}

if (this.env.options.typescript) {
sourceRoot = '/templates/typescript';
this.scriptSuffix = '.ts';
}

if (this.env.options.minsafe) {
sourceRoot += '-min';
}
Expand Down
50 changes: 44 additions & 6 deletions templates/common/Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ module.exports = function (grunt) {
coffeeTest: {
files: ['test/spec/{,*/}*.{coffee,litcoffee,coffee.md}'],
tasks: ['newer:coffee:test', 'karma']
},<% } else if (typescript) { %>
typescript: {
files: ['<%%= yeoman.app %>/scripts/{,*/}*.ts'],
tasks: ['typescript:base']
},
typescriptTest: {
files: ['test/spec/{,*/}*.ts'],
tasks: ['typescript:test', 'karma']
},<% } else { %>
js: {
files: ['<%%= yeoman.app %>/scripts/{,*/}*.js'],
Expand Down Expand Up @@ -63,7 +71,7 @@ module.exports = function (grunt) {
},
files: [
'<%%= yeoman.app %>/{,*/}*.html',
'.tmp/styles/{,*/}*.css',<% if (coffee) { %>
'.tmp/styles/{,*/}*.css',<% if (coffee || typescript) { %>
'.tmp/scripts/{,*/}*.js',<% } %>
'<%%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
]
Expand Down Expand Up @@ -111,9 +119,9 @@ module.exports = function (grunt) {
reporter: require('jshint-stylish')
},
all: [
'Gruntfile.js'<% if (!coffee) { %>,
'Gruntfile.js'<% if (!coffee && !typescript) { %>,
'<%%= yeoman.app %>/scripts/{,*/}*.js'<% } %>
]<% if (!coffee) { %>,
]<% if (!coffee && !typescript) { %>,
test: {
options: {
jshintrc: 'test/.jshintrc'
Expand Down Expand Up @@ -160,6 +168,33 @@ module.exports = function (grunt) {
}
},

<% if (typescript) { %>
// Compiles TypeScript to JavaScript
typescript: {
base: {
src: ['<%%= yeoman.app %>/scripts/{,*/}*.ts'],
dest: '.tmp/scripts',
options: {
module: 'amd', //or commonjs
target: 'es5', //or es3
'base_path': '<%%= yeoman.app %>/scripts', //quoting base_path to get around jshint warning.
sourcemap: true,
declaration: true
}
},
test: {
src: ['test/spec/{,*/}*.ts', 'test/e2e/{,*/}*.ts'],
dest: '.tmp/spec',
options: {
module: 'amd', //or commonjs
target: 'es5', //or es3
sourcemap: true,
declaration: true
}
}
},<% } %>


<% if (coffee) { %>
// Compiles CoffeeScript to JavaScript
coffee: {
Expand Down Expand Up @@ -343,17 +378,20 @@ module.exports = function (grunt) {
// Run some tasks in parallel to speed up the build process
concurrent: {
server: [<% if (coffee) { %>
'coffee:dist',<% } %><% if (compass) { %>
'coffee:dist',<% } %><% if (typescript) { %>
'typescript:base',<% } %><% if (compass) { %>
'compass:server'<% } else { %>
'copy:styles'<% } %>
],
test: [<% if (coffee) { %>
'coffee',<% } %><% if (compass) { %>
'coffee',<% } %><% if (typescript) { %>
'typescript',<% } %><% if (compass) { %>
'compass'<% } else { %>
'copy:styles'<% } %>
],
dist: [<% if (coffee) { %>
'coffee',<% } %><% if (compass) { %>
'coffee',<% } %><% if (typescript) { %>
'typescript',<% } %><% if (compass) { %>
'compass:dist',<% } else { %>
'copy:styles',<% } %>
'imagemin',
Expand Down
4 changes: 3 additions & 1 deletion templates/common/_bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
},
"devDependencies": {
"angular-mocks": "1.2.6",
"angular-scenario": "1.2.6"
"angular-scenario": "1.2.6"<% if (typescript) { %>,
"dt-jasmine": "~2.0.0",
"dt-angular": "https://github.com/jedmao/dt-angular/archive/v1.2.0.tar.gz"<% } %>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we switch the placement of these two so they are listed alphabetically? I find it's sometimes hard to find what I'm looking for in these manifest files when they aren't alphabetical.

}
}
3 changes: 2 additions & 1 deletion templates/common/_package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"grunt-usemin": "~2.0.0",
"jshint-stylish": "~0.1.3",
"load-grunt-tasks": "~0.2.0",
"time-grunt": "~0.2.1"
"time-grunt": "~0.2.1",
"grunt-typescript": "~0.2.7"
},
"engines": {
"node": ">=0.10.0"
Expand Down
3 changes: 2 additions & 1 deletion templates/common/root/app/.buildignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.coffee
*.coffee
*.ts
19 changes: 19 additions & 0 deletions templates/typescript/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/// <reference path="../bower_components/dt-angular/angular.d.ts" /><% if (ngCookies) { %>
/// <reference path="../bower_components/dt-angular/angular-cookies.d.ts" /><% } %><% if (ngResource) { %>
/// <reference path="../bower_components/dt-angular/angular-resource.d.ts" /><% } %><% if (ngSanitize) { %>
/// <reference path="../bower_components/dt-angular/angular-sanitize.d.ts" /><% } %><% if (ngRoute) { %>
/// <reference path="../bower_components/dt-angular/angular-route.d.ts" /><% } %>

'use strict';

angular.module('<%= scriptAppName %>', [<%= angularModules %>])<% if (ngRoute) { %>
.config(($routeProvider:ng.route.IRouteProvider) => {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.otherwise({
redirectTo: '/'
});
})<% } %>;
23 changes: 23 additions & 0 deletions templates/typescript/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/// <reference path="../app.ts" />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the installed Angular modules. Do they not need to be referenced up here?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see what you did there. Since app.ts is referenced, you don't need to re-reference any of the angular modules. Nice!


'use strict';

module <%= scriptAppName %> {
export interface I<%= classedName %>Scope extends ng.IScope {
awesomeThings: any[];
}

export class <%= classedName %>Ctrl {

constructor (private $scope: I<%= classedName %>Scope) {
$scope.awesomeThings = [
'HTML5 Boilerplate',
'AngularJS',
'Karma'
];
}
}
}

angular.module('<%= scriptAppName %>')
.controller('<%= classedName %>Ctrl', <%= scriptAppName %>.<%= classedName %>Ctrl);
18 changes: 18 additions & 0 deletions templates/typescript/decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// <reference path="../app.ts" />

'use strict';

module <%= scriptAppName %> {
export function <%= cameledName %>DecoratorProvider($provide: ng.auto.IProvideService): void {
//decorate <%= cameledName %>
$provide.decorator('<%= cameledName %>', <%= cameledName %>Decorator);
}

export function <%= cameledName %>Decorator($delegate: any) {
// decorate the $delegate
return $delegate;
}
}

angular.module('<%= scriptAppName %>')
.config(<%= scriptAppName %>.<%= cameledName %>DecoratorProvider);
22 changes: 22 additions & 0 deletions templates/typescript/directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// <reference path="../app.ts" />

'use strict';

module <%= scriptAppName %> {

export class <%= classedName %> implements ng.IDirective {
template = '<div></div>';
restrict = 'E';
link = (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes): void => {
element.text('this is the <%= cameledName %> directive');
}
}

export function <%= cameledName %>Factory() {
return new <%= scriptAppName %>.<%= classedName %>();
}

}

angular.module('<%= scriptAppName %>')
.directive('<%= cameledName %>', <%= scriptAppName %>.<%= cameledName %>Factory);
18 changes: 18 additions & 0 deletions templates/typescript/filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// <reference path="../app.ts" />

'use strict';

module <%= scriptAppName %> {
export function <%= cameledName %>FilterFactory(): Function {
return <%= cameledName %>Filter;
}

function <%= cameledName %>Filter(input, param) {
//usage {{"text" | <%= cameledName %>: "suffix"}}
//returns '<%= cameledName %> filter: text suffix'
return '<%= cameledName %> filter: ' + input + (param ? ' ' + param: '');
}
}

angular.module('<%= scriptAppName %>')
.filter('<%= cameledName %>', <%= scriptAppName %>.<%= cameledName %>FilterFactory);
6 changes: 6 additions & 0 deletions templates/typescript/service/constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/// <reference path="../app.ts" />

'use strict';

angular.module('<%= scriptAppName %>')
.constant('<%= cameledName %>', 42);
Loading