Skip to content

Commit

Permalink
feat: add route command's blueprint
Browse files Browse the repository at this point in the history
addon/ng2/blueprints/routes/* provides a blueprint for installing routes.ts,
configuring main.ts and adding routes to routes.ts.
The blueprint name 'routes' will be replaced by 'route' when completed.
  • Loading branch information
EmmanuelAzuh committed Aug 11, 2016
1 parent 7565f2d commit 12c9a38
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 4 deletions.
2 changes: 2 additions & 0 deletions addon/ng2/blueprints/routes/files/__path__/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

export default [];
99 changes: 99 additions & 0 deletions addon/ng2/blueprints/routes/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
const Blueprint = require('ember-cli/lib/models/blueprint');
const getFiles = Blueprint.prototype.files;
const path = require('path');
const fs = require('fs');
var chalk = require('chalk');
const util = require('../../utilities/route-utils');
const SilentError = require('silent-error');

module.exports = {
description: 'Generates a route and template',

availableOptions: [
{ name: 'default', type: Boolean, default: false },
{ name: 'route', type: String },
{ name: 'parent', type: String, default: '' },
{ name: 'outlet', type: Boolean, default: false },
{ name: 'with-component', type: Boolean, default: false, aliases: ['wc'] } // isn't functional yet
],

beforeInstall: function(options){
this._locals(options)
.then(names => {
var directory = this.newRoutePath[0] === path.sep ?
path.resolve(path.join(this.project.root, 'src', 'app', this.newRoutePath)):
path.resolve(process.env.PWD, this.newRoutePath);
// get route to be used in routes.ts
var route = directory.replace(path.join(this.project.root, 'src', 'app'), '');
var parsedRoute = path.parse(route);
var routeName = parsedRoute.name.split('.')[0];
// take care of the cases /**/home/home.component.ts vs /**/home
route = routeName === path.parse(parsedRoute.dir).name ?
path.parse(route).dir : `${parsedRoute.dir}/${routeName}`;
// setup options needed for adding path to routes.ts
this.pathOptions = {
isDefault: options.default,
route: options.route || route,
parent: options.parent,
outlet: options.outlet,
component: `${names.classifiedModuleName}Component`,
dasherizedName: names.dasherizedModuleName,
mainFile: path.join(this.project.root, 'src/main.ts'),
routesFile: path.join(this.project.root, 'src/routes.ts')
};
});
},

files: function() {
var fileList = getFiles.call(this);
if (this.project && fs.existsSync(path.join(this.project.root, 'src/routes.ts'))) {
return [];
}
return fileList;
},

fileMapTokens: function() {
return {
__path__: () => 'src'
};
},

normalizeEntityName: function(entityName) {
this.newRoutePath = entityName
entityName = path.parse(entityName).name;
if (!entityName) {
throw new SilentError('Please provide new route\'s name');
}
return entityName.split('.')[0];
},

afterInstall: function() {
return Promise.resolve().then(() => {
// confirm componentFile and presence of export of the component in componentFile
var component = this.pathOptions.component;
var file = util.resolveComponentPath(this.project.root, process.env.PWD, this.newRoutePath);
if (!util.confirmComponentExport(file, component)) {
throw new SilentError(`Please add export for '${component}' to '${file}'`);
}
return Promise.resolve();
}).then(() => {
// update routes in routes.ts
return util.applyChanges(util.addPathToRoutes(this.pathOptions.routesFile, this.pathOptions));
}).then(() => {
// add import to routes.ts in main.ts
return util.applyChanges(
[util.insertImport(this.pathOptions.mainFile, 'routes', './routes', true)]);
}).then(() => {
// bootstrap routes
var routes = { 'provideRouter': ['@angular/router'] };
var toBootstrap = 'provideRouter(routes)';
return util.applyChanges(util.bootstrapItem(this.pathOptions.mainFile, routes, toBootstrap));
}).catch(e => {
if (e.message.indexOf('Did not bootstrap') !== -1) {
this._writeStatusToUI(chalk.yellow, e.message, '');
} else {
throw new SilentError(e.message);
}
});
}
}
14 changes: 11 additions & 3 deletions addon/ng2/utilities/route-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ export function bootstrapItem(mainFile, imports: {[key: string]: [string, boolea
// get ExpressionStatements from the top level syntaxList of the sourceFile
let bootstrapNodes = rootNode.getChildAt(0).getChildren().filter(node => {
// get bootstrap expressions
return node.kind === ts.SyntaxKind.ExpressionStatement &&
node.getChildAt(0).getChildAt(0).text.toLowerCase() === 'bootstrap';
return node.kind === ts.SyntaxKind.ExpressionStatement && node.getChildAt(0) &&
node.getChildAt(0).getChildAt(0) && node.getChildAt(0).getChildAt(0).text
&& node.getChildAt(0).getChildAt(0).text.toLowerCase() === 'bootstrap';
});
if (bootstrapNodes.length !== 1) {
throw new Error(`Did not bootstrap provideRouter in ${mainFile}` +
' because of multiple or no bootstrap calls');
' because of multiple or no traditional bootstrap calls');
}
let bootstrapNode = bootstrapNodes[0].getChildAt(0);
let isBootstraped = findNodes(bootstrapNode, ts.SyntaxKind.SyntaxList) // get bootstrapped items
Expand Down Expand Up @@ -520,3 +521,10 @@ function getValueForKey(objectLiteralNode: ts.TypeNode.ObjectLiteralExpression,
function getRootNode(file: string) {
return ts.createSourceFile(file, fs.readFileSync(file).toString(), ts.ScriptTarget.ES6, true);
}

function printAll(node, d = 0) {
let text = node.text ? `-----> ${node.text}` : '';
console.log(new Array(d).join('####'), ts.SyntaxKind[node.kind], text);
d++;
node.getChildren().forEach(_ => printAll(_, d));
}
Binary file added angular-cli-1.0.0-beta.9.tgz
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/acceptance/route-utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ describe('route utils', () => {
.then(() => nru.bootstrapItem(mainFile, routes, toBootstrap))
.catch(e =>
expect(e.message).to.equal('Did not bootstrap provideRouter in' +
' tmp/main.ts because of multiple or no bootstrap calls')
' tmp/main.ts because of multiple or no traditional bootstrap calls')
);
});
it('configures correctly if bootstrap or provide router is not at top level', () => {
Expand Down
16 changes: 16 additions & 0 deletions tests/e2e/e2e_workflow.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,22 @@ describe('Basic end-to-end Workflow', function () {
});
});

it('creates routes.ts, configures main.ts, and adds routes', function() {
return ng(['generate', 'component', 'test-route']).then(function() {
return ng(['generate', 'routes', '/test-route']).then(function () {
var routesFile = path.join(process.cwd(), 'src', 'routes.ts');
var mainFile = path.join(process.cwd(), 'src', 'main.ts');
expect(existsSync(routesFile)).to.be.truthy;
var mainFileContent = fs.readFileSync(mainFile, 'utf8');
var routesFileContent = fs.readFileSync(routesFile, 'utf8');

expect(mainFileContent).to.contain('import routes from \'./routes\';');
expect(routesFileContent).to.contain('import { TestRouteComponent } from \'./app/test-route/test-route.component\';');
expect(routesFileContent).to.contain('[\n { path: \'test-route\', component: TestRouteComponent }\n]');
});
});
});

xit('Perform `ng test` after adding a route', function () {
this.timeout(420000);

Expand Down

0 comments on commit 12c9a38

Please sign in to comment.