Schematicsでちょっとだけgenerateされるファイルをカスタマイズ

# 発端
`ng g s --name my`でservice作る時に

```ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class MyService {

  constructor() { }
}
```

が作られますが

```ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: null
})
export class MyService {

  constructor() { }
}
```

としたいなぁと。
少しだけ変更したいだけなので、もともとのパラメータの役割とかも引き継ぎたい。そんな時
`ng g --help`をして出てくる `schematic`を利用して試してみる。


```
$ ng g --help
Generates and/or modifies files based on a schematic.
usage: ng generate <schematic> [options]

arguments:
  schematic
    The schematic or collection:schematic to generate.

options:
  --defaults
    When true, disables interactive input prompts for options with a default.
  --dry-run (-d)
    When true, runs through and reports activity without writing out results.
  --force (-f)
    When true, forces overwriting of existing files.
  --help
    Shows a help message for this command in the console.
  --interactive
    When false, disables interactive input prompts.
```

# やってみた

` npm i -g @angular-devkit/schematics-cli`したり
`schematics blank　my-schematics`したり

等の説明は省略します。

構成としてはこんな感じで作ります。

```
my-schematics
├── README.md
├── package-lock.json
├── package.json
├── src
│   ├── collection.json
│   ├── component
│   │   ├── files
│   │   │   └── __name@dasherize@if-flat__
│   │   │       ├── __name@dasherize__.component.__styleExt__.template
│   │   │       ├── __name@dasherize__.component.html.template
│   │   │       ├── __name@dasherize__.component.spec.ts.template
│   │   │       └── __name@dasherize__.component.ts.template
│   │   ├── index.ts
│       ├── index_spec.ts
│   │   ├── schema.json
│   │   └── schema.ts
│   └── service
│       ├── files
│       │   └── __name@dasherize@if-flat__
│       │       ├── __name@dasherize__.service.spec.ts.template
│       │       └── __name@dasherize__.service.ts.template
│       ├── index.ts
│       ├── index_spec.ts
│       ├── schema.json
│       └── schema.ts
└── tsconfig.json
```

`npm i @schematics/angular`で `@schematics/angular`を入れておきます。

```json:my-schematics/src/collection.ts
{
  "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
  "schematics": {
    "component": {
      "aliases": [ "c" ],
      "description": "create component",
      "factory": "./component",
      "schema": "./component/schema.json"
    },
    "service": {
      "aliases": [ "s" ],
      "description": "create service",
      "factory": "./service",
      "schema": "./service/schema.json"
    }
  }
}

```

```ts:my-schematics/src/service/index.ts
import { Rule, chain } from '@angular-devkit/schematics';

import buildService from '@schematics/angular/service';
import { Schema as ServiceOptions } from './schema';

export default function (options: ServiceOptions): Rule {

  return chain([
    buildService( {...options} ),
  ]);
}
```

```ts:my-schematics/src/service/schema.ts
import { Schema as ServiceSchema } from '@schematics/angular/service/schema';

export interface Schema extends ServiceSchema {
  root?: boolean;
}
```

```json:my-schematics/src/service/schema.json
{
  "$schema": "http://json-schema.org/schema",
  "id": "SchematicsAngularService",
  "title": "Angular Service Options Schema",
  "type": "object",
  "description": "Creates a new, generic service definition in the given or default project.",
  "properties": {
    "name": {
      "type": "string",
      "description": "The name of the service.",
      "$default": {
        "$source": "argv",
        "index": 0
      },
      "x-prompt": "What name would you like to use for the service?"
    },
    "path": {
      "type": "string",
      "format": "path",
      "description": "The path at which to create the service, relative to the workspace root.",
      "visible": false
    },
    "project": {
      "type": "string",
      "description": "The name of the project.",
      "$default": {
        "$source": "projectName"
      }
    },
    "flat": {
      "type": "boolean",
      "default": true,
      "description": "When true (the default), creates files at the top level of the project."
    },
    "spec": {
      "type": "boolean",
      "default": true,
      "description": "When true (the default), generates a  \"spec.ts\" test file for the new service.",
      "x-deprecated": "Use \"skipTests\" instead."
    },
    "skipTests": {
      "type": "boolean",
      "description": "When true, does not create \"spec.ts\" test files for the new service.",
      "default": false
    },
    "lintFix": {
      "type": "boolean",
      "default": false,
      "description": "When true, applies lint fixes after generating the pipe."
    },
    "root": "--- 追加部分 ---",
    "root": {
      "type": "boolean",
      "default": false,
      "description": "When true, provided in will be 'root'. If not null"
    }
  },
  "required": [
    "name"
  ]
}

```

```ts:my-schematics/src/service/files/__name@dasherize@if-flat__/__name@dasherize__.service.ts.template
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: <% if(root) { %>'root'<% } else { %>null<% } %>
})
export class <%= classify(name) %>Service {

  constructor() { }
}

```

これで`npm run build`し、あとはpublishするなり、privateや相対パスリポジトリとしてinstallするなりします。

installしたプロジェクト内で
`ng g my-schematics:service --name my`

```ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: null
})
export class MyService {

  constructor() { }
}
```

`ng g --help`すると使えるschematicsとして一覧に追加される

```
$ ng g --help
Generates and/or modifies files based on a schematic.
usage: ng generate <schematic> [options]

arguments:
  schematic
    The schematic or collection:schematic to generate.

options:
  --defaults
    When true, disables interactive input prompts for options with a default.
  --dry-run (-d)
    When true, runs through and reports activity without writing out results.
  --force (-f)
    When true, forces overwriting of existing files.
  --help
    Shows a help message for this command in the console.
  --interactive
    When false, disables interactive input prompts.

Available Schematics:
  Collection "my-schematics" (default):
    appShell
    application
    class
    component
    directive
    enum
    guard
    interface
    library
    module
    pipe
    service
    serviceWorker
    universal
```

[続く](https://qiita.com/cilly/items/57465a54c2e479aa313d)