diff --git a/README.md b/README.md index f16284610773..8b8711cc1a01 100644 --- a/README.md +++ b/README.md @@ -118,21 +118,37 @@ with two sub-routes. The file structure will be as follows: | | |-- hero.service.ts | |-- ... |-- app.ts +|-- route-config.ts ... ``` -Afterwards to use the new route open your main app component, import -`hero-root.component.ts` and add it in the route config: +By default the cli will add the import statements for HeroList and HeroDetail to +`hero-root.component.ts`: ``` @RouteConfig([ - {path:'/hero/...', name: 'HeroRoot', component: HeroRoot} + {path:'/', name: 'HeroList', component: HeroListComponent, useAsDefault: true}, + {path:'/:id', name: 'HeroDetail', component: HeroDetailComponent} ]) ``` +The generated `route-config.ts` file is also updated with the following: + +``` +// DO NOT EDIT THIS FILE +// IT IS AUTO GENERATED BY ANGULAR-CLI +import {HeroRoot} from './hero/hero-root.component'; + +export const CliRouteConfig = [ + {path:'/hero/...', name: 'HeroRoot', component: HeroRoot} +]; +``` + Visiting `http://localhost:4200/hero` will show the hero list. +There is an optional flag for `skip-router-generation` which will not add the route to the `CliRouteConfig` for the application. + ### Creating a build ```bash diff --git a/addon/ng2/blueprints/ng2/files/angular-cli.json b/addon/ng2/blueprints/ng2/files/angular-cli.json new file mode 100644 index 000000000000..090149c02a27 --- /dev/null +++ b/addon/ng2/blueprints/ng2/files/angular-cli.json @@ -0,0 +1,3 @@ +{ + "routes": [] +} \ No newline at end of file diff --git a/addon/ng2/blueprints/ng2/files/src/app/__name__.ts b/addon/ng2/blueprints/ng2/files/src/app/__name__.ts index d55651a65002..d38af92e609b 100644 --- a/addon/ng2/blueprints/ng2/files/src/app/__name__.ts +++ b/addon/ng2/blueprints/ng2/files/src/app/__name__.ts @@ -1,6 +1,6 @@ import {Component} from 'angular2/core'; import {RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS} from 'angular2/router'; - +import {CliRouteConfig} from './route-config'; @Component({ selector: '<%= htmlComponentName %>-app', @@ -11,7 +11,8 @@ import {RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS} from 'angular2/router' }) @RouteConfig([ -]) +].concat(CliRouteConfig)) + export class <%= jsComponentName %>App { defaultMeaning: number = 42; diff --git a/addon/ng2/blueprints/ng2/files/src/app/route-config.ts b/addon/ng2/blueprints/ng2/files/src/app/route-config.ts new file mode 100644 index 000000000000..aab07ce44ef7 --- /dev/null +++ b/addon/ng2/blueprints/ng2/files/src/app/route-config.ts @@ -0,0 +1,7 @@ + +// DO NOT EDIT THIS FILE +// IT IS AUTO GENERATED BY ANGULAR-CLI + +export const CliRouteConfig = [ + +]; \ No newline at end of file diff --git a/addon/ng2/blueprints/route-config/files/src/app/route-config.ts b/addon/ng2/blueprints/route-config/files/src/app/route-config.ts new file mode 100644 index 000000000000..d8e76dc1cad0 --- /dev/null +++ b/addon/ng2/blueprints/route-config/files/src/app/route-config.ts @@ -0,0 +1,7 @@ +// DO NOT EDIT THIS FILE +// IT IS AUTO GENERATED BY ANGULAR-CLI +<%= imports %> + +export const CliRouteConfig = [ + <%= routeDefinitions %> +]; \ No newline at end of file diff --git a/addon/ng2/blueprints/route-config/index.js b/addon/ng2/blueprints/route-config/index.js new file mode 100644 index 000000000000..8345c2c7937c --- /dev/null +++ b/addon/ng2/blueprints/route-config/index.js @@ -0,0 +1,39 @@ +var fs = require('fs-extra'); +var path = require('path'); + +var imports, routeDefinitions; + +module.exports = { + description: 'Registers the route with the router.', + + locals: function (options) { + return generateLocals.call(this, options); + }, + + beforeInstall: function (options) { + var routeConfigPath = path.join(options.project.root, 'src', 'app', 'route-config.ts'); + try { + fs.unlinkSync(routeConfigPath); + } catch (e) { + } + } +}; + +function generateLocals(options) { + var ngCliConfigPath = path.join(options.project.root, 'angular-cli.json'); + var ngCliConfig = JSON.parse(fs.readFileSync(ngCliConfigPath, 'utf-8')); + + imports = ngCliConfig.routes.map(route => + `import {${route.component}} from '${route.componentPath}';`) + .join('\n'); + + routeDefinitions = ngCliConfig.routes.map(route => + `{path: '${route.routePath}', name: '${route.component}', component: ${route.component}},` + ) + .join('\n'); + + return { + imports, + routeDefinitions + } +} \ No newline at end of file diff --git a/addon/ng2/blueprints/route/index.js b/addon/ng2/blueprints/route/index.js index 8540d5f2f309..a55d7d6d31c3 100644 --- a/addon/ng2/blueprints/route/index.js +++ b/addon/ng2/blueprints/route/index.js @@ -1,16 +1,71 @@ -var stringUtils = require('ember-cli/lib/utilities/string'); +var fs = require('fs-extra'); +var path = require('path'); +var chalk = require('chalk'); module.exports = { - description: '' - - //locals: function(options) { - // // Return custom template variables here. - // return { - // - // }; - //} - - // afterInstall: function(options) { - // // Perform extra work here. - // } + description: 'Generates a route and a template.', + + availableOptions: [{ + name: 'skip-router-generation', + type: Boolean, + default: false, + aliases: ['srg'] + }, { + name: 'default', + type: Boolean, + default: false + }], + + beforeInstall: function (options, locals) { + if (!options.skipRouterGeneration) { + updateRouteConfig.call(this, 'add', options, locals); + } + }, + + afterInstall: function (options, locals) { + if (!options.skipRouterGeneration) { + return this.lookupBlueprint('route-config') + .install(options); + } + }, + + beforeUninstall: function (options, locals) { + updateRouteConfig.call(this, 'remove', options, locals); + }, + + afterUninstall: function (options, locals) { + return this.lookupBlueprint('route-config') + .install(options); + } }; + +function updateRouteConfig(action, options, locals) { + var entity = options.entity; + var actionColorMap = { + add: 'green', + remove: 'red' + }; + var color = actionColorMap[action] || 'gray'; + + this._writeStatusToUI(chalk[color], action + ' route', entity.name); + + var ngCliConfigPath = path.join(options.project.root, 'angular-cli.json'); + + // TODO use default option + var route = { + routePath: `/${locals.dasherizedModuleName}/...`, + component: `${locals.classifiedModuleName}Root`, + componentPath: `./${locals.dasherizedModuleName}/${locals.dasherizedModuleName}-root.component` + }; + + var ngCliConfig = JSON.parse(fs.readFileSync(ngCliConfigPath, 'utf-8')); + if (action === 'add' && ngCliConfig.routes.findIndex(el => el.routePath === route.routePath) === -1) { + ngCliConfig.routes.push(route) + } else if (action === 'remove') { + var idx = ngCliConfig.routes.findIndex(el => el.routePath === route.routePath); + if (idx > -1) { + ngCliConfig.routes.splice(idx, 1); + } + } + fs.writeFileSync(ngCliConfigPath, JSON.stringify(ngCliConfig, null, 2)); +} \ No newline at end of file diff --git a/tests/acceptance/generate-route.spec.js b/tests/acceptance/generate-route.spec.js new file mode 100644 index 000000000000..c5eda98d5b6f --- /dev/null +++ b/tests/acceptance/generate-route.spec.js @@ -0,0 +1,124 @@ +'use strict'; + +var fs = require('fs-extra'); +var ng = require('../helpers/ng'); +var existsSync = require('exists-sync'); +var expect = require('chai').expect; +var forEach = require('lodash/forEach'); +var path = require('path'); +var tmp = require('../helpers/tmp'); +var root = process.cwd(); +var util = require('util'); +var conf = require('ember-cli/tests/helpers/conf'); +var testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'my-route'); +var appPath = path.join(root, 'tmp', 'foo', 'src', 'app'); +var routeRegex = /name: 'MyRouteRoot'/g; +var route = { + routePath: `/my-route/...`, + component: `MyRouteRoot`, + componentPath: `./my-route/my-route-root.component` +}; + +var fileExpectations = function (expectation) { + expect(existsSync(path.join(testPath, 'my-route.service.spec.ts'))).to.equal(expectation); + expect(existsSync(path.join(testPath, 'my-route.service.ts'))).to.equal(expectation); + expect(existsSync(path.join(testPath, 'my-route-list.component.ts'))).to.equal(expectation); + expect(existsSync(path.join(testPath, 'my-route-list.component.spec.ts'))).to.equal(expectation); + expect(existsSync(path.join(testPath, 'my-route-list.component.html'))).to.equal(expectation); + expect(existsSync(path.join(testPath, 'my-route-list.component.css'))).to.equal(expectation); + expect(existsSync(path.join(testPath, 'my-route-detail.component.ts'))).to.equal(expectation); + expect(existsSync(path.join(testPath, 'my-route-detail.component.spec.ts'))).to.equal(expectation); + expect(existsSync(path.join(testPath, 'my-route-detail.component.html'))).to.equal(expectation); + expect(existsSync(path.join(testPath, 'my-route-detail.component.css'))).to.equal(expectation); + expect(existsSync(path.join(testPath, 'my-route-root.component.ts'))).to.equal(expectation); +}; + +describe('Acceptance: ng generate route', function () { + before(conf.setup); + + after(conf.restore); + + beforeEach(function () { + return tmp.setup('./tmp') + .then(function () { + process.chdir('./tmp'); + }) + .then(function () { + return ng([ + 'new', + 'foo', + '--skip-npm', + '--skip-bower' + ]); + }); + }); + + afterEach(function () { + this.timeout(10000); + + return tmp.teardown('./tmp'); + }); + + it('ng generate route my-route', function () { + return ng([ + 'generate', + 'route', + 'my-route' + ]) + .then(_ => { + var ngCliConfig = JSON.parse(fs.readFileSync(path.join(root, 'tmp', 'foo', 'angular-cli.json'), 'utf-8')); + var routeConfigString = fs.readFileSync(path.join(appPath, 'route-config.ts'), 'utf-8').toString(); + expect(routeConfigString.match(routeRegex).length).to.equal(1); + + fileExpectations(true); + + expect(ngCliConfig.routes[0]).to.deep.equal(route); + }); + }); + it('ng generate route my-route with skip-router-generation flag does not generate router config', function () { + return ng([ + 'generate', + 'route', + 'my-route', + '--skip-router-generation' + ]) + .then(_ => { + var ngCliConfig = JSON.parse(fs.readFileSync(path.join(root, 'tmp', 'foo', 'angular-cli.json'), 'utf-8')); + + fileExpectations(true); + + expect(ngCliConfig.routes.length).to.equal(0); + }); + }); + it('ng generate route my-route then destroy', function () { + return ng([ + 'generate', + 'route', + 'my-route' + ]) + .then(() => { + var ngCliConfig = JSON.parse(fs.readFileSync(path.join(root, 'tmp', 'foo', 'angular-cli.json'), 'utf-8')); + var routeConfigString = fs.readFileSync(path.join(appPath, 'route-config.ts'), 'utf-8').toString(); + expect(routeConfigString.match(routeRegex).length).to.equal(1); + + fileExpectations(true); + + expect(ngCliConfig.routes.length).to.equal(1); + expect(ngCliConfig.routes[0]).to.deep.equal(route); + }).then(() => { + return ng([ + 'destroy', + 'route', + 'my-route' + ]).then(() => { + var ngCliConfig = JSON.parse(fs.readFileSync(path.join(root, 'tmp', 'foo', 'angular-cli.json'), 'utf-8')); + var routeConfigString = fs.readFileSync(path.join(appPath, 'route-config.ts'), 'utf-8').toString(); + expect(routeConfigString.match(routeRegex)).to.equal(null); + + fileExpectations(false); + + expect(ngCliConfig.routes.length).to.equal(0); + }) + }); + }); +}); \ No newline at end of file