Skip to content

Commit a01a84c

Browse files
committed
feat(generate): add guard generation
1 parent f70feae commit a01a84c

File tree

10 files changed

+365
-0
lines changed

10 files changed

+365
-0
lines changed

docs/documentation/generate.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- [component](component)
99
- [directive](directive)
1010
- [enum](enum)
11+
- [guard](guard)
1112
- [interface](interface)
1213
- [module](module)
1314
- [pipe](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: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
const path = require('path');
2+
const fs = require('fs');
3+
const chalk = require('chalk');
4+
const dynamicPathParser = require('../../utilities/dynamic-path-parser');
5+
const Blueprint = require('../../ember-cli/lib/models/blueprint');
6+
const NodeHost = require('@angular-cli/ast-tools').NodeHost;
7+
const stringUtils = require('ember-cli-string-utils');
8+
const astUtils = require('../../utilities/ast-utils');
9+
const getFiles = Blueprint.prototype.files;
10+
11+
module.exports = {
12+
description: '',
13+
14+
availableOptions: [
15+
{ name: 'flat', type: Boolean, default: true },
16+
{ name: 'spec', type: Boolean },
17+
{ name: 'module', type: String, aliases: ['m'] }
18+
],
19+
20+
beforeInstall: function(options) {
21+
if (options.module) {
22+
// Resolve path to module
23+
const modulePath = options.module.endsWith('.ts') ? options.module : `${options.module}.ts`;
24+
const parsedPath = dynamicPathParser(this.project, modulePath);
25+
this.pathToModule = path.join(this.project.root, parsedPath.dir, parsedPath.base);
26+
27+
if (!fs.existsSync(this.pathToModule)) {
28+
throw 'Module specified does not exist';
29+
}
30+
}
31+
},
32+
33+
normalizeEntityName: function (entityName) {
34+
var parsedPath = dynamicPathParser(this.project, entityName);
35+
36+
this.dynamicPath = parsedPath;
37+
return parsedPath.name;
38+
},
39+
40+
locals: function (options) {
41+
options.spec = options.spec !== undefined ?
42+
options.spec :
43+
this.project.ngConfigObj.get('defaults.spec.guard');
44+
45+
return {
46+
dynamicPath: this.dynamicPath.dir,
47+
flat: options.flat
48+
};
49+
},
50+
51+
files: function() {
52+
var fileList = getFiles.call(this);
53+
54+
if (this.options && !this.options.spec) {
55+
fileList = fileList.filter(p => p.indexOf('__name__.guard.spec.ts') < 0);
56+
}
57+
58+
return fileList;
59+
},
60+
61+
fileMapTokens: function (options) {
62+
// Return custom template variables here.
63+
return {
64+
__path__: () => {
65+
var dir = this.dynamicPath.dir;
66+
if (!options.locals.flat) {
67+
dir += path.sep + options.dasherizedModuleName;
68+
}
69+
this.generatePath = dir;
70+
return dir;
71+
}
72+
};
73+
},
74+
75+
afterInstall(options) {
76+
const returns = [];
77+
78+
if (!this.pathToModule) {
79+
const warningMessage = 'Guard is generated but not provided, it must be provided to be used';
80+
this._writeStatusToUI(chalk.yellow, 'WARNING', warningMessage);
81+
} else {
82+
const className = stringUtils.classify(`${options.entity.name}Guard`);
83+
const fileName = stringUtils.dasherize(`${options.entity.name}.guard`);
84+
const fullGeneratePath = path.join(this.project.root, this.generatePath);
85+
const moduleDir = path.parse(this.pathToModule).dir;
86+
const relativeDir = path.relative(moduleDir, fullGeneratePath);
87+
const importPath = relativeDir ? `./${relativeDir}/${fileName}` : `./${fileName}`;
88+
returns.push(
89+
astUtils.addProviderToModule(this.pathToModule, className, importPath)
90+
.then(change => change.apply(NodeHost)));
91+
this._writeStatusToUI(chalk.yellow, 'update', path.relative(this.project.root, this.pathToModule));
92+
}
93+
94+
return Promise.all(returns);
95+
}
96+
};

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@
215215
"type": "boolean",
216216
"default": true
217217
},
218+
"guard": {
219+
"type": "boolean",
220+
"default": true
221+
},
218222
"module": {
219223
"type": "boolean",
220224
"default": false
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-npm']);
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+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {join} from 'path';
2+
import {ng} from '../../../utils/process';
3+
import {expectFileToMatch} from '../../../utils/fs';
4+
5+
6+
export default function() {
7+
const modulePath = join('src', 'app', 'app.module.ts');
8+
9+
return ng('generate', 'guard', 'test-guard', '--module', 'app.module.ts')
10+
.then(() => expectFileToMatch(modulePath,
11+
/import { TestGuardGuard } from '.\/test-guard.guard'/))
12+
.then(() => expectFileToMatch(modulePath,
13+
/providers:\s*\[TestGuardGuard\]/m))
14+
.then(() => ng('build'));
15+
}

0 commit comments

Comments
 (0)