Skip to content

Commit 2c1e877

Browse files
delastevefilipesilva
authored andcommitted
feat(generate): add guard generation (#4055)
1 parent 1655e51 commit 2c1e877

File tree

10 files changed

+388
-0
lines changed

10 files changed

+388
-0
lines changed

docs/documentation/generate.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- [component](generate/component)
1111
- [directive](generate/directive)
1212
- [enum](generate/enum)
13+
- [guard](generate/guard)
1314
- [interface](generate/interface)
1415
- [module](generate/module)
1516
- [pipe](generate/pipe)

docs/documentation/generate/guard.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# ng generate guard
2+
3+
## Overview
4+
`ng generate guard [name]` generates a guard
5+
6+
## Options
7+
`--flat` flag to indicate if a dir is created
8+
9+
`--spec` specifies if a spec file is generated
10+
11+
`--module` (`-m`) allows specification of the declaring module
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* tslint:disable:no-unused-variable */
2+
3+
import { TestBed, async, inject } from '@angular/core/testing';
4+
import { <%= classifiedModuleName %>Guard } from './<%= dasherizedModuleName %>.guard';
5+
6+
describe('<%= classifiedModuleName %>Guard', () => {
7+
beforeEach(() => {
8+
TestBed.configureTestingModule({
9+
providers: [<%= classifiedModuleName %>Guard]
10+
});
11+
});
12+
13+
it('should ...', inject([<%= classifiedModuleName %>Guard], (guard: <%= classifiedModuleName %>Guard) => {
14+
expect(guard).toBeTruthy();
15+
}));
16+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Injectable } from '@angular/core';
2+
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
3+
import { Observable } from 'rxjs/Observable';
4+
5+
@Injectable()
6+
export class <%= classifiedModuleName %>Guard implements CanActivate {
7+
canActivate(
8+
next: ActivatedRouteSnapshot,
9+
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
10+
return true;
11+
}
12+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import {NodeHost} from '../../lib/ast-tools';
2+
import { oneLine } from 'common-tags';
3+
4+
const path = require('path');
5+
const fs = require('fs');
6+
const chalk = require('chalk');
7+
const dynamicPathParser = require('../../utilities/dynamic-path-parser');
8+
const Blueprint = require('../../ember-cli/lib/models/blueprint');
9+
const stringUtils = require('ember-cli-string-utils');
10+
const astUtils = require('../../utilities/ast-utils');
11+
const getFiles = Blueprint.prototype.files;
12+
13+
export default Blueprint.extend({
14+
description: '',
15+
16+
availableOptions: [
17+
{ name: 'flat', type: Boolean },
18+
{ name: 'spec', type: Boolean },
19+
{ name: 'module', type: String, aliases: ['m'] }
20+
],
21+
22+
beforeInstall: function(options: any) {
23+
if (options.module) {
24+
// Resolve path to module
25+
const modulePath = options.module.endsWith('.ts') ? options.module : `${options.module}.ts`;
26+
const parsedPath = dynamicPathParser(this.project, modulePath);
27+
this.pathToModule = path.join(this.project.root, parsedPath.dir, parsedPath.base);
28+
29+
if (!fs.existsSync(this.pathToModule)) {
30+
throw 'Module specified does not exist';
31+
}
32+
}
33+
},
34+
35+
normalizeEntityName: function (entityName: string) {
36+
const parsedPath = dynamicPathParser(this.project, entityName);
37+
38+
this.dynamicPath = parsedPath;
39+
return parsedPath.name;
40+
},
41+
42+
locals: function (options: any) {
43+
options.flat = options.flat !== undefined ?
44+
options.flat :
45+
this.project.ngConfigObj.get('defaults.guard.flat');
46+
47+
options.spec = options.spec !== undefined ?
48+
options.spec :
49+
this.project.ngConfigObj.get('defaults.guard.spec');
50+
51+
return {
52+
dynamicPath: this.dynamicPath.dir,
53+
flat: options.flat
54+
};
55+
},
56+
57+
files: function() {
58+
let fileList = getFiles.call(this) as Array<string>;
59+
60+
if (this.options && !this.options.spec) {
61+
fileList = fileList.filter(p => p.indexOf('__name__.guard.spec.ts') < 0);
62+
}
63+
64+
return fileList;
65+
},
66+
67+
fileMapTokens: function (options: any) {
68+
// Return custom template variables here.
69+
return {
70+
__path__: () => {
71+
let dir = this.dynamicPath.dir;
72+
if (!options.locals.flat) {
73+
dir += path.sep + options.dasherizedModuleName;
74+
}
75+
this.generatePath = dir;
76+
return dir;
77+
}
78+
};
79+
},
80+
81+
afterInstall(options: any) {
82+
const returns: Array<any> = [];
83+
84+
if (!this.pathToModule) {
85+
const warningMessage = oneLine`
86+
Guard is generated but not provided,
87+
it must be provided to be used
88+
`;
89+
this._writeStatusToUI(chalk.yellow, 'WARNING', warningMessage);
90+
} else {
91+
const className = stringUtils.classify(`${options.entity.name}Guard`);
92+
const fileName = stringUtils.dasherize(`${options.entity.name}.guard`);
93+
const fullGeneratePath = path.join(this.project.root, this.generatePath);
94+
const moduleDir = path.parse(this.pathToModule).dir;
95+
const relativeDir = path.relative(moduleDir, fullGeneratePath);
96+
const importPath = relativeDir ? `./${relativeDir}/${fileName}` : `./${fileName}`;
97+
returns.push(
98+
astUtils.addProviderToModule(this.pathToModule, className, importPath)
99+
.then((change: any) => change.apply(NodeHost)));
100+
this._writeStatusToUI(chalk.yellow,
101+
'update',
102+
path.relative(this.project.root, this.pathToModule));
103+
}
104+
105+
return Promise.all(returns);
106+
}
107+
});

packages/@angular/cli/lib/config/schema.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,22 @@
339339
}
340340
}
341341
},
342+
"guard": {
343+
"description": "Options for generating a guard.",
344+
"type": "object",
345+
"properties": {
346+
"flat": {
347+
"description": "Flag to indicate if a dir is created.",
348+
"type": "boolean",
349+
"default": true
350+
},
351+
"spec": {
352+
"description": "Specifies if a spec file is generated.",
353+
"type": "boolean",
354+
"default": true
355+
}
356+
}
357+
},
342358
"interface": {
343359
"description": "Options for generating an interface.",
344360
"type": "object",
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
'use strict';
2+
3+
var fs = require('fs-extra');
4+
var ng = require('../helpers/ng');
5+
var existsSync = require('exists-sync');
6+
var expect = require('chai').expect;
7+
var path = require('path');
8+
var tmp = require('../helpers/tmp');
9+
var root = process.cwd();
10+
var Promise = require('@angular/cli/ember-cli/lib/ext/promise');
11+
var SilentError = require('silent-error');
12+
const denodeify = require('denodeify');
13+
14+
const readFile = denodeify(fs.readFile);
15+
16+
describe('Acceptance: ng generate guard', function () {
17+
beforeEach(function () {
18+
return tmp.setup('./tmp').then(function () {
19+
process.chdir('./tmp');
20+
}).then(function () {
21+
return ng(['new', 'foo', '--skip-install']);
22+
});
23+
});
24+
25+
afterEach(function () {
26+
this.timeout(10000);
27+
28+
return tmp.teardown('./tmp');
29+
});
30+
31+
it('ng generate guard my-guard', function () {
32+
const appRoot = path.join(root, 'tmp/foo');
33+
const testPath = path.join(appRoot, 'src/app/my-guard.guard.ts');
34+
const testSpecPath = path.join(appRoot, 'src/app/my-guard.guard.spec.ts');
35+
const appModulePath = path.join(appRoot, 'src/app/app.module.ts');
36+
37+
return ng(['generate', 'guard', 'my-guard'])
38+
.then(() => {
39+
expect(existsSync(testPath)).to.equal(true);
40+
expect(existsSync(testSpecPath)).to.equal(true);
41+
})
42+
.then(() => readFile(appModulePath, 'utf-8'))
43+
.then(content => {
44+
expect(content).not.to.matches(/import.*\MyGuardGuard\b.*from '.\/my-guard.guard';/);
45+
expect(content).not.to.matches(/providers:\s*\[MyGuardGuard\]/m);
46+
});
47+
});
48+
49+
it('ng generate guard my-guard --no-spec', function () {
50+
const appRoot = path.join(root, 'tmp/foo');
51+
const testPath = path.join(appRoot, 'src/app/my-guard.guard.ts');
52+
const testSpecPath = path.join(appRoot, 'src/app/my-guard.guard.spec.ts');
53+
const appModulePath = path.join(appRoot, 'src/app/app.module.ts');
54+
55+
return ng(['generate', 'guard', 'my-guard', '--no-spec'])
56+
.then(() => {
57+
expect(existsSync(testPath)).to.equal(true);
58+
expect(existsSync(testSpecPath)).to.equal(false);
59+
})
60+
.then(() => readFile(appModulePath, 'utf-8'))
61+
.then(content => {
62+
expect(content).not.to.matches(/import.*\MyGuardGuard\b.*from '.\/my-guard.guard';/);
63+
expect(content).not.to.matches(/providers:\s*\[MyGuardGuard\]/m);
64+
});
65+
});
66+
67+
it('ng generate guard my-guard --module=app.module.ts', function () {
68+
const appRoot = path.join(root, 'tmp/foo');
69+
const testPath = path.join(appRoot, 'src/app/my-guard.guard.ts');
70+
const testSpecPath = path.join(appRoot, 'src/app/my-guard.guard.spec.ts');
71+
const appModulePath = path.join(appRoot, 'src/app/app.module.ts');
72+
73+
return ng(['generate', 'guard', 'my-guard', '--module', 'app.module.ts'])
74+
.then(() => {
75+
expect(existsSync(testPath)).to.equal(true);
76+
expect(existsSync(testSpecPath)).to.equal(true);
77+
})
78+
.then(() => readFile(appModulePath, 'utf-8'))
79+
.then(content => {
80+
expect(content).to.matches(/import.*\MyGuardGuard\b.*from '.\/my-guard.guard';/);
81+
expect(content).to.matches(/providers:\s*\[MyGuardGuard\]/m);
82+
});
83+
});
84+
85+
it('ng generate guard test' + path.sep + 'my-guard', function () {
86+
fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', 'test'));
87+
return ng(['generate', 'guard', 'test' + path.sep + 'my-guard']).then(() => {
88+
var testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'test', 'my-guard.guard.ts');
89+
expect(existsSync(testPath)).to.equal(true);
90+
});
91+
});
92+
93+
it('ng generate guard test' + path.sep + '..' + path.sep + 'my-guard', function () {
94+
return ng(['generate', 'guard', 'test' + path.sep + '..' + path.sep + 'my-guard']).then(() => {
95+
var testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'my-guard.guard.ts');
96+
expect(existsSync(testPath)).to.equal(true);
97+
});
98+
});
99+
100+
it('ng generate guard my-guard from a child dir', () => {
101+
fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1'));
102+
return new Promise(function (resolve) {
103+
process.chdir('./src');
104+
resolve();
105+
})
106+
.then(() => process.chdir('./app'))
107+
.then(() => process.chdir('./1'))
108+
.then(() => {
109+
process.env.CWD = process.cwd();
110+
return ng(['generate', 'guard', 'my-guard'])
111+
})
112+
.then(() => {
113+
var testPath = path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'my-guard.guard.ts');
114+
expect(existsSync(testPath)).to.equal(true);
115+
});
116+
});
117+
118+
it('ng generate guard child-dir' + path.sep + 'my-guard from a child dir', () => {
119+
fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'child-dir'));
120+
return new Promise(function (resolve) {
121+
process.chdir('./src');
122+
resolve();
123+
})
124+
.then(() => process.chdir('./app'))
125+
.then(() => process.chdir('./1'))
126+
.then(() => {
127+
process.env.CWD = process.cwd();
128+
return ng(['generate', 'guard', 'child-dir' + path.sep + 'my-guard'])
129+
})
130+
.then(() => {
131+
var testPath = path.join(
132+
root, 'tmp', 'foo', 'src', 'app', '1', 'child-dir', 'my-guard.guard.ts');
133+
expect(existsSync(testPath)).to.equal(true);
134+
});
135+
});
136+
137+
it('ng generate guard child-dir' + path.sep + '..' + path.sep + 'my-guard from a child dir',
138+
() => {
139+
fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1'));
140+
return new Promise(function (resolve) {
141+
process.chdir('./src');
142+
resolve();
143+
})
144+
.then(() => process.chdir('./app'))
145+
.then(() => process.chdir('./1'))
146+
.then(() => {
147+
process.env.CWD = process.cwd();
148+
return ng(
149+
['generate', 'guard', 'child-dir' + path.sep + '..' + path.sep + 'my-guard'])
150+
})
151+
.then(() => {
152+
var testPath =
153+
path.join(root, 'tmp', 'foo', 'src', 'app', '1', 'my-guard.guard.ts');
154+
expect(existsSync(testPath)).to.equal(true);
155+
});
156+
});
157+
158+
it('ng generate guard ' + path.sep + 'my-guard from a child dir, gens under ' +
159+
path.join('src', 'app'),
160+
() => {
161+
fs.mkdirsSync(path.join(root, 'tmp', 'foo', 'src', 'app', '1'));
162+
return new Promise(function (resolve) {
163+
process.chdir('./src');
164+
resolve();
165+
})
166+
.then(() => process.chdir('./app'))
167+
.then(() => process.chdir('./1'))
168+
.then(() => {
169+
process.env.CWD = process.cwd();
170+
return ng(['generate', 'guard', path.sep + 'my-guard'])
171+
})
172+
.then(() => {
173+
var testPath = path.join(root, 'tmp', 'foo', 'src', 'app', 'my-guard.guard.ts');
174+
expect(existsSync(testPath)).to.equal(true);
175+
});
176+
});
177+
178+
it('ng generate guard ..' + path.sep + 'my-guard from root dir will fail', () => {
179+
return ng(['generate', 'guard', '..' + path.sep + 'my-guard']).then(() => {
180+
throw new SilentError(`ng generate guard ..${path.sep}my-guard from root dir should fail.`);
181+
}, (err) => {
182+
expect(err).to.equal(`Invalid path: "..${path.sep}my-guard" cannot be above the "src${path.sep}app" directory`);
183+
});
184+
});
185+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {join} from 'path';
2+
import {ng} from '../../../utils/process';
3+
import {expectFileToExist} from '../../../utils/fs';
4+
5+
6+
export default function() {
7+
// Does not create a sub directory.
8+
const guardDir = join('src', 'app');
9+
10+
return ng('generate', 'guard', 'test-guard')
11+
.then(() => expectFileToExist(guardDir))
12+
.then(() => expectFileToExist(join(guardDir, 'test-guard.guard.ts')))
13+
.then(() => expectFileToExist(join(guardDir, 'test-guard.guard.spec.ts')))
14+
15+
// Try to run the unit tests.
16+
.then(() => ng('test', '--single-run'));
17+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import {ng} from '../../../utils/process';
2+
import {expectToFail} from '../../../utils/utils';
3+
4+
export default function() {
5+
return Promise.resolve()
6+
.then(() => expectToFail(() =>
7+
ng('generate', 'guard', 'test-guard', '--module', 'app.moduleXXX.ts')));
8+
}

0 commit comments

Comments
 (0)