Skip to content

Commit 44cb5a4

Browse files
committed
feat(schematics): add ng-add schematics implementation
1 parent 43242ff commit 44cb5a4

File tree

11 files changed

+173
-56
lines changed

11 files changed

+173
-56
lines changed

package-lock.json

Lines changed: 7 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/model/model.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import * as assert from 'assert';
2-
import * as sinon from 'sinon';
1+
import assert from 'assert';
2+
import sinon from 'sinon';
33

44
import { ModelFactory } from './model';
55

src/schematics/model/index.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import * as assert from 'assert';
2-
import * as path from 'path';
1+
import assert from 'assert';
2+
import path from 'path';
33
import {
44
SchematicTestRunner,
55
UnitTestTree
@@ -36,7 +36,7 @@ const runner = new SchematicTestRunner('schematics', collectionPath);
3636

3737
let appTree: UnitTestTree;
3838

39-
describe('Model Schematic', () => {
39+
describe('Schematic: Model', () => {
4040
beforeEach(() => {
4141
appTree = runner.runExternalSchematic(
4242
'@schematics/angular',
@@ -114,7 +114,7 @@ describe('Model Schematic', () => {
114114
const content = tree.readContent(
115115
'/projects/bar/src/app/foo/foo.service.ts'
116116
);
117-
assert(content.includes("providedIn: 'root'"));
117+
assert(content.includes(`providedIn: 'root'`));
118118
});
119119

120120
it('should not be tree-shakeable if module is set', () => {
@@ -124,7 +124,7 @@ describe('Model Schematic', () => {
124124
const content = tree.readContent(
125125
'/projects/bar/src/app/foo/foo.service.ts'
126126
);
127-
assert(!content.includes("providedIn: 'root'"));
127+
assert(!content.includes(`providedIn: 'root'`));
128128
});
129129

130130
it('should create model interface', () => {
@@ -152,15 +152,15 @@ describe('Model Schematic', () => {
152152

153153
const tree = runner.runSchematic('model', options, appTree);
154154
const content = tree.readContent('/projects/bar/src/app/app.module.ts');
155-
assert(!content.includes("import { FooService } from './foo/foo.service'"));
155+
assert(!content.includes(`import { FooService } from './foo/foo.service'`));
156156
});
157157

158158
it('should import into a specified module', () => {
159159
const options = { ...defaultOptions, module: 'app.module.ts' };
160160

161161
const tree = runner.runSchematic('model', options, appTree);
162162
const content = tree.readContent('/projects/bar/src/app/app.module.ts');
163-
assert(content.includes("import { FooService } from './foo/foo.service'"));
163+
assert(content.includes(`import { FooService } from './foo/foo.service'`));
164164
});
165165

166166
it('should fail if specified module does not exist', () => {
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import assert from 'assert';
2+
import path from 'path';
3+
import {
4+
SchematicTestRunner,
5+
UnitTestTree
6+
} from '@angular-devkit/schematics/testing';
7+
import { Schema as ApplicationOptions } from '@schematics/angular/application/schema';
8+
import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema';
9+
10+
import packageJson from '../../../package.json';
11+
12+
import { Schema as NgAddOptions } from './schema';
13+
14+
const workspaceOptions: WorkspaceOptions = {
15+
name: 'workspace',
16+
newProjectRoot: 'projects',
17+
version: '6.0.0'
18+
};
19+
20+
const appOptions: ApplicationOptions = {
21+
name: 'bar',
22+
inlineStyle: false,
23+
inlineTemplate: false,
24+
routing: false,
25+
style: 'css',
26+
skipTests: false,
27+
skipPackageJson: false
28+
};
29+
30+
const defaultOptions: NgAddOptions = {
31+
skipInstall: false
32+
};
33+
34+
const collectionPath = path.join(__dirname, '../collection.json');
35+
const runner = new SchematicTestRunner('schematics', collectionPath);
36+
const version = packageJson.version;
37+
38+
let appTree: UnitTestTree;
39+
40+
describe('Schematic: ng-add', () => {
41+
beforeEach(() => {
42+
appTree = runner.runExternalSchematic(
43+
'@schematics/angular',
44+
'workspace',
45+
workspaceOptions
46+
);
47+
appTree = runner.runExternalSchematic(
48+
'@schematics/angular',
49+
'application',
50+
appOptions,
51+
appTree
52+
);
53+
});
54+
55+
it('should add @angular-extensions/model to dependencies in package.json', () => {
56+
const options = { ...defaultOptions };
57+
58+
const tree = runner.runSchematic('ng-add', options, appTree);
59+
const content = tree.readContent('/package.json');
60+
const contentAsObject = JSON.parse(content);
61+
assert.equal(runner.tasks.length, 1);
62+
assert(content.includes(`"@angular-extensions/model": "^${version}"`));
63+
assert.equal(
64+
contentAsObject.dependencies['@angular-extensions/model'],
65+
`^${version}`
66+
);
67+
});
68+
69+
it('should respect skipInstall flag', () => {
70+
const options = { ...defaultOptions, skipInstall: true };
71+
72+
const tree = runner.runSchematic('ng-add', options, appTree);
73+
assert.equal(runner.tasks.length, 0);
74+
});
75+
});

src/schematics/ng-add/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
2+
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
3+
4+
import packageJson from '../../../package.json';
5+
import { addPackageToPackageJson } from '../utils';
6+
7+
import { Schema as NgAddOptions } from './schema';
8+
9+
export default function(options: NgAddOptions): Rule {
10+
return (host: Tree, context: SchematicContext) => {
11+
const version = packageJson.version;
12+
addPackageToPackageJson(host, '@angular-extensions/model', `^${version}`);
13+
context.logger.log(
14+
'info',
15+
`✅️ Added "@angular-extensions/model@^${version}" into dependencies`
16+
);
17+
18+
if (!options.skipInstall) {
19+
context.addTask(new NodePackageInstallTask());
20+
}
21+
return host;
22+
};
23+
}

src/schematics/ng-add/schema.d.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
export interface Schema {
2-
name: string;
3-
skipInstall: boolean;
4-
}
1+
export interface Schema {
2+
skipInstall: boolean;
3+
}

src/schematics/ng-add/schema.json

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
1-
{
2-
"$schema": "http://json-schema.org/schema",
3-
"id": "@angular-extensions/model/ng-add",
4-
"title": "Add Angular model to the project",
5-
"description": "NOTE: Does not work in the --dry-run mode",
6-
"type": "object",
7-
"properties": {
8-
"skipInstall": {
9-
"type": "boolean",
10-
"description": "Skip installing after adding @angular-extensions/model",
11-
"default": false
12-
},
13-
"name": {
14-
"type": "string",
15-
"description": "Project name.",
16-
"$default": {
17-
"$source": "projectName"
18-
}
19-
}
20-
}
21-
}
1+
{
2+
"$schema": "http://json-schema.org/schema",
3+
"id": "@angular-extensions/model/ng-add",
4+
"title": "Add Angular model to the project",
5+
"description": "NOTE: Does not work in the --dry-run mode",
6+
"type": "object",
7+
"properties": {
8+
"skipInstall": {
9+
"type": "boolean",
10+
"description": "Skip installing after adding @angular-extensions/model",
11+
"default": false
12+
}
13+
},
14+
"required": [],
15+
"additionalProperties": false
16+
}

src/schematics/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"skipLibCheck": true,
1818
"sourceMap": false,
1919
"strictNullChecks": true,
20+
"resolveJsonModule": true,
2021
"target": "es6",
2122
"types": ["node", "mocha"]
2223
},

src/schematics/utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Tree } from '@angular-devkit/schematics';
2+
3+
export function addPackageToPackageJson(
4+
host: Tree,
5+
pkg: string,
6+
version: string
7+
): Tree {
8+
if (host.exists('package.json')) {
9+
const sourceText = host.read('package.json')!.toString('utf-8');
10+
const json = JSON.parse(sourceText);
11+
12+
if (!json.dependencies) {
13+
json.dependencies = {};
14+
}
15+
16+
if (!json.dependencies[pkg]) {
17+
json.dependencies[pkg] = version;
18+
json.dependencies = sortObjectByKeys(json.dependencies);
19+
}
20+
21+
host.overwrite('package.json', JSON.stringify(json, null, 2));
22+
}
23+
24+
return host;
25+
}
26+
27+
function sortObjectByKeys(obj: any) {
28+
return Object.keys(obj)
29+
.sort()
30+
.reduce((result: any, key) => {
31+
result[key] = obj[key];
32+
return result;
33+
}, {});
34+
}

tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
"compilerOptions": {
33
"lib": ["es2017", "dom"],
44
"experimentalDecorators": true,
5+
"module": "commonjs",
6+
"resolveJsonModule": true,
7+
"esModuleInterop": true,
58
"strict": true
69
}
710
}

0 commit comments

Comments
 (0)