dotnet new コマンドで使うテンプレートを作成する

[Azure/azure-functions-templates](https://github.com/Azure/azure-functions-templates) のリポジトリで初めて dotnet の template を触るので個別の技術を理解しようとチュートリアルを流して意味を見ることにした。

* [Tutorial: Create an item template](https://docs.microsoft.com/en-us/dotnet/core/tutorials/cli-templates-create-item-template)

これを流して理解してみる。

# テンプレートのディレクトリを作成する

下記のようなディレクトリ構造を作る。`extensions` の配下がテンプレートのルートになる。`.template.config` ディレクトリが存在する箇所がテンプレートの場所になる。

```cmd
└───working
    └───templates
        └───extensions
            └───.template.config
```

# テンプレートの作成 (Item) 

サンプルでは簡単な Extension メソッドを作成する。単純に文字列をリバースするだけの簡単なコードだ。このコードを `extension` ディレクトリ配下に配置する。今回のテンプレートは、`Item` と呼ばれる種類で、プロジェクト自体を生成するテンプレートではなくて、プロジェクトを作った後に、プロジェクトの中に追加するアイテムを作成するためのテンプレートになる。

_StringExtensions.cs_

```csharp
using System;

namespace System {
    public static class StringExtensions
    {
        public static string Reverse(this string value)
        {
            var tempArray = value.ToCharArray();
            Array.Reverse(tempArray);
            return new string(tempArray);
        }
    }
}
```

# コンフィグファイルの作成

`.template.config` の下に `template.json` を作成する。下記のような内容になる。

_template.json_

```json
{
  "$schema": "http://json.schemastore.org/template",
  "author": "Tsuyoshi Ushio",
  "classifications": [ "Common", "Code" ],
  "identity": "ExampleTemplate.StringExtensions",
  "name": "Example templates: string extensions",
  "shortName": "stringext",
  "tags": {
    "language": "C#",
    "type": "item"
  }
}
```

大切な箇所は少ない。よくある extension の設定ファイルという感じだ。`classifications` はいわゆるTagである。 `name`, `shortname` はご想像の通りで、名前を付けているのみ。ポイントは、 `type`: の箇所に `item` を指定している。`project` というのも選択可能のようだ。スキーマを確認してみよう。

* [json.schemastore.org/template](http://json.schemastore.org/template)

スキーマを読めば定義は明確で、 `identity` は、テンプレートのユニークな名前なので、他と被らない名前付けが必要なのだろう。作成は上記で終了。
思ったよりずっと簡単だ。

# テンプレートのインストール

テンプレートのインストールは、`dotnet new -i <directory>` になる。今回は、テンプレートのルートを指定すると良い。ちなみに、`-i` のオプションをつけて、新しいバージョンをインストールすると、単に上書きされる。インストールは、今回のようにファイルシステムでも良いが、Nuget パッケージからも可能なようだ。

```cmd
dotnet new -i working\templates\extensions
```

次のような実行結果になり、再生されたテンプレートがインストールされたのがわかる。では実際につかってみよう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/c4b54841-7369-c6b2-5661-ac84d49be565.png)

ところで、インストールされたテンプレートはどこに保存されるのだろうか？指定したディレクトリを参照するのだろうか？もしくしゃ、どこかに`Cache` が村債するのだろうか？あたりをつけて調べてみたけどわからないので、Windows のディレクトリをフル検索してみたが、キャッシュ的なディレクトリは見つからなかった。（もしくは、アーカイブされている可能性はあるのだが）この辺りも今後読み進めていくと、明確になってくるだろうか。


# テンプレートの使用

コンソールアプリのテンプレートを使って、コンソールアプリのプロジェクトを作る。既にテンプレートをインストールしているので、どこのディレクトリでも良い。

```cmd
> dotnet new console
> dotnet run
Hello World!
```

## Item テンプレートの適用

出来たプロジェクトに対して、Itemテンプレートを適用してみよう。先ほどのテンプレートの `shortname` を指定してインストールする。

```cmd
> dotnet new stringext
```

しっかり拡張メソッドを持ったクラスが追加されている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/76fa313c-e011-d10b-12c3-20ad2f05b2a7.png)

## Program の変更

`Program` を拡張メソッドを使いように改造する。

_Program.cs_

```csharp
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!".Reverse());
        }
```

## テンプレートの活用

```cmd
> dotnet run
!dlroW olleH
```

うむ。いい感じである。ではテンプレートの削除はどうだろう？

# テンプレートのアンインストール

アンインストールの場合は相対パスではなく、フルパスが必要になる。

```
> dotnet new -u C:\Users\tsushi\Code\spike\Template\working\templates\extensions
```

その情報を見つけるにはどうするか？というと

```
> dotnet new -u
``` 

で現在インストールされているリストを見ることが出来るのでそれを活用する。

# Project のテンプレート

* [Tutorial: Create a project template](https://docs.microsoft.com/en-us/dotnet/core/tutorials/cli-templates-create-project-template)

これは、Item とさほど大差はない。単純にプロジェクトを作ってみよう

```
> dotnet new console
> dir
    Directory: C:\Users\tsushi\Code\spike\Template\test2

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           1/25/2021 10:49 PM            211 consoleasync.csproj
-a---           1/25/2021 10:49 PM            249 Program.cs
```

`csproj` と `cs` ファイルが出来上がる。中身をちょっとだけ変える。

_Program.cs_

```csharp
    class Program
    {
        static async Task Main(string[] args)
        {
            await Console.Out.WriteAsync("Hello World with C# 9.0!");
        }
    }
```

## 設定ファイル

先ほどと違うのは、`tags` の `type` の箇所だ。`project` が指定されている。

_.template.config/template.json_

```json
{
    "$schema": "http://json.schemastore.org/template",
    "author": "Me",
    "classifications": [ "Common", "Console", "C#9" ],
    "identity": "ExampleTemplate.AsyncProject",
    "name": "Example templates: async project",
    "shortName": "consoleasync",
    "tags": {
      "language": "C#",
      "type": "project"
    }
  }
  
```

## インストール
同様の方法でインストール

```cmd
> dotnet new -i .\
```

# テンプレートパックからのインストール

NuGetファイルを作ると、複数のテンプレートをまとめることが出来る。

* [Tutorial: Create a template pack](https://docs.microsoft.com/en-us/dotnet/core/tutorials/cli-templates-create-template-pack)

２つのテンプレートが既にあるので、その上位のディレクトリ、`working` ディレクトリに行く。その中に `templates`ディレクトリがある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/9599504f-d1c7-3f37-f7e9-e84142fde7a9.png)

ここで、テンプレートパックと呼ばれるNuGetファイルを作る。名前は、`templatepack` カレントディレクトリにテンプレートを出力する。

```cmd
dotnet new console -n templatepack -o .
```

## csprojの編集
生成された `csproj` ファイルを編集する　`PackageId`でユニークな名前を指定している。またプロジェクトに含む、含まないのエントリが `ItemGroup` で指定されているのに注目したい。ここで先に作成されたテンプレートを含むようにする。

```xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <PackageType>Template</PackageType>
    <PackageVersion>1.0</PackageVersion>
    <PackageId>AdatumCorporation.Utility.Templates</PackageId>
    <Title>AdatumCorporation Templates</Title>
    <Authors>Tsuyoshi Ushio</Authors>
    <Description>Templates to use when creating an application for Adatum Corporation.</Description>
    <PackageTags>dotnet-new;templates;contoso</PackageTags>

    <TargetFramework>netstandard2.0</TargetFramework>

    <IncludeContentInPack>true</IncludeContentInPack>
    <IncludeBuildOutput>false</IncludeBuildOutput>
    <ContentTargetFolders>content</ContentTargetFolders>
    <NoWarn>$(NoWarn);NU5128</NoWarn>
  </PropertyGroup>

  <ItemGroup>
    <Content Include="templates\**\*" Exclude="templates\**\bin\**;templates\**\obj\**" />
    <Compile Remove="**\*" />
  </ItemGroup>

</Project>
```

## NuGet ファイルの作成

```cmd
> dotnet pack
```

## インストール

これも上記のものと同様だが、作成されたNuGetのファイルを指定する。先ほど作成されたテンプレートが二つとも含まれているのがわかるだろう。

```cmd
> dotnet new -i C:\Users\tsushi\Code\spike\Template\working\bin\Debug\AdatumCorporation.Utility.Templates.1.0.0.nupkg
Templates                                         Short Name               Language          Tags
--------------------------------------------      -------------------      ------------      ----------------------------------------------
DurableFunctionsOrchestration                     durable                  [C#]              Azure Function/Durable Functions Orchestration
SendGrid                                          sendgrid                 [C#]              Azure Function/Ouput/SendGrid
BlobTrigger                                       blob                     [C#], F#          Azure Function/Trigger/Blob
CosmosDBTrigger                                   cosmos                   [C#], F#          Azure Function/Trigger/Cosmos DB
EventGridTrigger                                  eventgrid                [C#]              Azure Function/Trigger/EventGrid
EventHubTrigger                                   eventhub                 [C#], F#          Azure Function/Trigger/EventHub
HttpTrigger                                       http                     [C#], F#          Azure Function/Trigger/Http
SignalRTrigger                                    signalr                  [C#]              Azure Function/Trigger/Http/SignalR
IotHubTrigger                                     iothub                   [C#]              Azure Function/Trigger/IotHub
ServiceBusQueueTrigger                            squeue                   [C#]              Azure Function/Trigger/Service Bus/Queue
ServiceBusTopicTrigger                            stopic                   [C#]              Azure Function/Trigger/Service Bus/Topic
QueueTrigger                                      queue                    [C#]              Azure Function/Trigger/Storage Queue
TimerTrigger                                      timer                    [C#], F#          Azure Function/Trigger/Timer
Azure Functions                                   func                     [C#], F#          Azure Functions/Solution
Example templates: string extensions              stringext                [C#]              Common/Code
Console Application                               console                  [C#], F#, VB      Common/Console
Example templates: async project                  consoleasync             [C#]              Common/Console/C#9
Class library                                     classlib                 [C#], F#, VB      Common/Library
WPF Application                                   wpf                      [C#], VB          Common/WPF
WPF Class library                                 wpflib                   [C#], VB          Common/WPF
WPF Custom Control Library                        wpfcustomcontrollib      [C#], VB          Common/WPF
WPF User Control Library                          wpfusercontrollib        [C#], VB          Common/WPF
Windows Forms App                                 winforms                 [C#], VB          Common/WinForms
Windows Forms Control Library                     winformscontrollib       [C#], VB          Common/WinForms
Windows Forms Class Library                       winformslib              [C#], VB          Common/WinForms
Worker Service                                    worker                   [C#], F#          Common/Worker/Web
Unit Test Project                                 mstest                   [C#], F#, VB      Test/MSTest
NUnit 3 Test Project                              nunit                    [C#], F#, VB      Test/NUnit
NUnit 3 Test Item                                 nunit-test               [C#], F#, VB      Test/NUnit
xUnit Test Project                                xunit                    [C#], F#, VB      Test/xUnit
Razor Component                                   razorcomponent           [C#]              Web/ASP.NET
Razor Page                                        page                     [C#]              Web/ASP.NET
MVC ViewImports                                   viewimports              [C#]              Web/ASP.NET
MVC ViewStart                                     viewstart                [C#]              Web/ASP.NET
Blazor Server App                                 blazorserver             [C#]              Web/Blazor
Blazor WebAssembly App                            blazorwasm               [C#]              Web/Blazor/WebAssembly
ASP.NET Core Empty                                web                      [C#], F#          Web/Empty
ASP.NET Core Web App (Model-View-Controller)      mvc                      [C#], F#          Web/MVC
ASP.NET Core Web App                              webapp                   [C#]              Web/MVC/Razor Pages
ASP.NET Core with Angular                         angular                  [C#]              Web/MVC/SPA
ASP.NET Core with React.js                        react                    [C#]              Web/MVC/SPA
ASP.NET Core with React.js and Redux              reactredux               [C#]              Web/MVC/SPA
Razor Class Library                               razorclasslib            [C#]              Web/Razor/Library
ASP.NET Core Web API                              webapi                   [C#], F#          Web/WebAPI
ASP.NET Core gRPC Service                         grpc                     [C#]              Web/gRPC
dotnet gitignore file                             gitignore                                  Config
global.json file                                  globaljson                                 Config
NuGet Config                                      nugetconfig                                Config
Dotnet local tool manifest file                   tool-manifest                              Config
Web Config                                        webconfig                                  Config
Solution File                                     sln                                        Solution
Protocol Buffer File                              proto                                      Web/gRPC

Examples:
    dotnet new mvc --auth Individual
    dotnet new webapi
    dotnet new --help
    dotnet new consoleasync --help
```

## クリーンアップ

クリーンアップも同じだが、NuGetからインストールした場合は、分からなくなると思うので確認する。

```cmd
> dotnet new -u
Template Instantiation Commands for .NET Core CLI

Currently installed items:
  Microsoft.DotNet.Common.ProjectTemplates.2.2
    Details:
      NuGetPackageId: Microsoft.DotNet.Common.ProjectTemplates.2.2
      Version: 1.0.2-beta4
      Author: Microsoft
    Templates:
      Class library (classlib) C#
      Class library (classlib) F#
      Class library (classlib) VB
      Console Application (console) C#
      Console Application (console) F#
      Console Application (console) VB
    Uninstall Command:
      dotnet new -u Microsoft.DotNet.Common.ProjectTemplates.2.2

              :
  AdatumCorporation.Utility.Templates
    Details:
      NuGetPackageId: AdatumCorporation.Utility.Templates
      Version: 1.0.0
      Author: Tsuyoshi Ushio
    Templates:
      Example templates: async project (consoleasync) C#
      Example templates: string extensions (stringext) C#
    Uninstall Command:
      dotnet new -u AdatumCorporation.Utility.Templates
```

テンプレートの名前を指定する。

```cmd
dotnet new -u AdatumCorporation.Utility.Templates
```

# 次回
次は、Action Id 等のテンプレートでジェネレートされるIDの意味も読み解いていきたい。