diff --git a/packages/schematics/angular/collection.json b/packages/schematics/angular/collection.json index 5c86460300..b880bb98e4 100644 --- a/packages/schematics/angular/collection.json +++ b/packages/schematics/angular/collection.json @@ -29,6 +29,11 @@ "factory": "./module", "description": "Create an Angular module.", "schema": "./module/schema.json" + }, + "service": { + "factory": "./service", + "description": "Create an Angular service.", + "schema": "./service/schema.json" } } } diff --git a/packages/schematics/angular/service/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.service.spec.ts b/packages/schematics/angular/service/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.service.spec.ts new file mode 100644 index 0000000000..d83dc49be5 --- /dev/null +++ b/packages/schematics/angular/service/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { <%= classify(name) %>Service } from './<%= dasherize(name) %>.service'; + +describe('<%= classify(name) %>Service', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [<%= classify(name) %>Service] + }); + }); + + it('should be created', inject([<%= classify(name) %>Service], (service: <%= classify(name) %>Service) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/packages/schematics/angular/service/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.service.ts b/packages/schematics/angular/service/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.service.ts new file mode 100644 index 0000000000..f685263951 --- /dev/null +++ b/packages/schematics/angular/service/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@angular/core'; + +@Injectable() +export class <%= classify(name) %>Service { + + constructor() { } + +} diff --git a/packages/schematics/angular/service/index.ts b/packages/schematics/angular/service/index.ts new file mode 100644 index 0000000000..9decb93699 --- /dev/null +++ b/packages/schematics/angular/service/index.ts @@ -0,0 +1,82 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +// TODO: replace `options: any` with an actual type generated from the schema. +// tslint:disable:no-any +import { + Rule, + Tree, + apply, + branchAndMerge, + chain, + filter, + mergeWith, + move, + noop, + template, + url, +} from '@angular-devkit/schematics'; +import 'rxjs/add/operator/merge'; +import * as ts from 'typescript'; +import * as stringUtils from '../strings'; +import {addProviderToModule} from '../utility/ast-utils'; +import {InsertChange} from '../utility/change'; +import {buildRelativePath} from '../utility/find-module'; + +function addProviderToNgModule(options: any): Rule { + return (host: Tree) => { + if (!options.module) { + return host; + } + + const modulePath = options.module; + if (!host.exists(options.module)) { + throw new Error(`Module specified (${options.module}) does not exist.`); + } + + const sourceText = host.read(modulePath) !.toString('utf-8'); + const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); + + const servicePath = `/${options.sourceDir}/${options.path}/` + + (options.flat ? '' : stringUtils.dasherize(options.name) + '/') + + stringUtils.dasherize(options.name) + + '.service'; + const relativePath = buildRelativePath(modulePath, servicePath); + const changes = addProviderToModule(source, modulePath, + stringUtils.classify(`${options.name}Service`), + relativePath); + const recorder = host.beginUpdate(modulePath); + for (const change of changes) { + if (change instanceof InsertChange) { + recorder.insertLeft(change.pos, change.toAdd); + } + } + host.commitUpdate(recorder); + + return host; + }; +} + +export default function (options: any): Rule { + const templateSource = apply(url('./files'), [ + options.spec ? noop() : filter(path => !path.endsWith('.spec.ts')), + template({ + ...stringUtils, + 'if-flat': (s: string) => options.flat ? '' : s, + ...options, + }), + move(options.sourceDir), + ]); + + return chain([ + branchAndMerge(chain([ + filter(path => path.endsWith('.module.ts') && !path.endsWith('-routing.module.ts')), + addProviderToNgModule(options), + mergeWith(templateSource), + ])), + ]); +} diff --git a/packages/schematics/angular/service/schema.json b/packages/schematics/angular/service/schema.json new file mode 100644 index 0000000000..0c31c7fbc5 --- /dev/null +++ b/packages/schematics/angular/service/schema.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/schema", + "id": "SchematicsAngularService", + "title": "Angular Service Options Schema", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "path": { + "type": "string", + "default": "app" + }, + "sourceDir": { + "type": "string", + "default": "src" + }, + "flat": { + "type": "boolean", + "default": false, + "description": "Flag to indicate if a dir is created." + }, + "spec": { + "type": "boolean", + "default": true, + "description": "Specifies if a spec file is generated." + }, + "module": { + "type": "string", + "default": "", + "description": "Allows specification of the declaring module." + } + }, + "required": [ + "name" + ] +}