Visual Studioから利用できるプロジェクトテンプレートを作成する

Visual Studioから使えるプロジェクトテンプレートを作成する機会があったので、作り方をまとめておきます。

## 1. テンプレートにしたいプロジェクトを用意する

VisualStudioでテンプレートを選択してプロジェクトを作成するときに生成されてほしいソースコードを含んだプロジェクトを用意します。

ただし、.NET Frameworkのプロジェクトテンプレートから作成したプロジェクト・クラスライブラリの場合、プロジェクトファイルの形式が古いもの(.NET SDK形式ではないもの)になっています。
このままでもテンプレート化は可能ですが、.NET SDK形式ではないため、後述する`<PackageReference>`タグを利用したNugetパッケージの参照ができません。
自分は.NET Coreのプロジェクトファイルの中身をテンプレートにしたいプロジェクトファイルにコピペして、`<TargetFramework>`タグの中身を.NET Frameworkの利用するバージョンに書き換えました。
```
<PropertyGroup>
    <TargetFramework>netXXX(XXXは利用する.NET Frameworkのバージョン)</TargetFramework>
...
</PropertyGroup>
```
また、テンプレートとするプロジェクトはデバッグを完了させておくようにしてください。
この後の手順でプロジェクトをテンプレートパッケージプロジェクトという別のプロジェクトに含めますが、その過程でテンプレート化するプロジェクトをコンパイルから除外する設定をするためです。

プロジェクトファイルとソースコード一式は作業フォルダにtemplatesというサブフォルダを作成し、その中にさらにプロジェクトごとにサブフォルダとして格納します。

フォルダ・ファイル構成は以下のようになります。
```text
作業フォルダ // ここにテンプレートパッケージプロジェクトを作成する
└───templates
    └───mytemplate // テンプレート化する対象のプロジェクトを以下フォルダに格納
    │   │   mytemplate.csproj // テンプレート化するプロジェクト
    │   └───.template.config // 次の手順で作成
    │       template.json 
    │
    └───mytemplate2 // テンプレートは一つのパッケージに複数含められる
        │   mytemplate.csproj 
        └───.template.config
            template.json 
```

## 2. template.jsonを準備する

プロジェクトのフォルダと同じ階層に.template.configフォルダを作成し、template.jsonファイルを作成、格納します。
そして、jsonファイルに以下の内容を記載します。
```json
{
  "$schema": "http://json.schemastore.org/template",
  "author": "(作成者)",
  "classifications": [ "Common", "Console" ],
  "identity": "(テンプレートプロジェクトの一意の名前)",
  "name": "(テンプレートプロジェクトのユーザーへの表示名)",
  "shortName": "(テンプレートプロジェクトの短縮名)",
  "sourceName": "(プロジェクト内でユーザーの入力文字列に置き換える文字列)",
  "tags": {
    "language": "C#", 
    "type": "project"
  }
}

```
`shortName`は、コマンドプロンプトから`dotnet new`でプロジェクトを作成する場合の呼び出しに利用できます。
プロジェクト作成時は、`identification`で設定した名前か、`shortName`で設定した名前で呼び出すことができます。

`sourceName`はユーザーがVisual Studioでテンプレートを選択してプロジェクトを作成するときに入力するプロジェクト名に置き換えられます。
ソースコード内のクラス名・メソッド名はもちろん、名前空間やアセンブリ名に指定しておいても置き換えられます。

`dotnet new --list`でインストールされているテンプレートを一覧で見ることができますが、`classifications`で指定した内容はここで表示され、確認することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1062427/57d6f675-f433-4cb1-3afe-8183e01bca67.png)

## 3. テンプレートパッケージプロジェクトを準備する

作業フォルダに新しくプロジェクトを作成します。
コマンドプロンプトで`dotnet new console`で作成してもよいですし、VisualStudio上で[プロジェクトの新規作成]でも大丈夫です。
プロジェクトを作成するとPrograms.csファイルが作成されますが、テンプレートでは使わないので消してください。

新しく作成したプロジェクトの.csprojファイルを編集します。
.csprojの内容を、以下で書き換えます。
[msdnのチュートリアル](https://learn.microsoft.com/ja-jp/dotnet/core/tutorials/cli-templates-create-template-package?source=recommendations#create-a-template-package-project)の内容ほぼそのままです。
```xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <PackageType>Template</PackageType>
    <PackageVersion>1.0</PackageVersion>
    <PackageId>(パッケージのID)</PackageId>
    <Title>(パッケージのタイトル)</Title>
    <Authors>(パッケージの作成者)</Authors>
    <Description>(パッケージの説明)</Description>
    <PackageTags>dotnet-new;templates;contoso</PackageTags>

    <TargetFramework>netstandard2.0</TargetFramework>

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

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

</Project>
```
以下、調べて分かった範囲での各設定の解説です。

`<PackageType>`・`<PackageVersion>`・`<PackageId>`は、Nugetにこのテンプレートをパッケージとしてアップロードする場合に必要な設定です。
`<PackageType>Template</PackageType>`を指定することで、パッケージをテンプレートとして検索できるようになります。
`<PackageVersion>`は、Nugetパッケージのバージョンになります。テンプレートパッケージを一度Nugetにアップロードした後にパッケージを修正した場合、このバージョンを変更してパッケージを作成する必要があります。同じバージョン番号で同名のNugetパッケージはアップロードできないためです。
`<PackageId>`は、パッケージをNugetにアップロードした場合に、このPackageIdを指定してインストール・アンインストールができます。
TitleやDescriptionなどはNugetパッケージの名前や説明の項目です。
作成したパッケージをNuget Explorerで開くと見れます。

`<TargetFramework>`は中身のテンプレートのフレームワークに関係なく`netstandard2.0`のままで大丈夫でした。

`<IncludeContentInPack>`は、`<Content>`タグで指定した項目をNugetパッケージに自動で含めるかどうかの設定です。規定値のtrueのままで大丈夫です。
`<ItemGroup>`の`<Content>`内でパッケージに含める項目・含めない項目を指定しています。
今回のテンプレートの`<Content>`の場合は、templatesフォルダ以下の要素の中で、bin・objフォルダとその中身はパッケージに含めないという設定をしています。

`<IncludeBuildOutput>`は、ビルド結果を .nupkg ファイルにパッケージ化するかどうかの設定です。パッケージ化は後程自分で実行するのでfalseにします。

`<BuildOutputTargetFolder>`は、Nugetパッケージの構成で指定した名前のフォルダの中にテンプレートの中身を入れてくれるものです。

`<NoWarn>`で消しているNU5128の警告は、パッケージのインストール時に画像のように表示される警告です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1062427/f169ff09-3fe0-9c6c-ca86-3d66bdac95ee.png)
`<NoDefaultExcludes>`は、`.`で始まる名前のファイルやフォルダをパッケージから除外しないようにする設定です。trueにします。(.template.configフォルダをパッケージに含める必要があるためです。)
`<ItemGroup>`の`<Compile>`の設定は、テンプレートプロジェクトをコンパイルしないための設定です。

これで、テンプレートパッケージプロジェクトの設定は完了です。

## 4.テンプレートをNugetパッケージにする

テンプレートパッケージプロジェクトを作成したら、コマンドプロンプトを開いてテンプレートパッケージプロジェクトのあるフォルダへ移動し、
`C:\(作業フォルダのパス)> dotnet pack`
を実行します。すると、プロジェクトがビルドされ、作業フォルダのbin\Debug フォルダーに NuGetパッケージが作成されます。
後は、`dotnet new install (作業フォルダのパス)\bin\Debug\(テンプレート名+バージョン番号).nupkg`で作成したパッケージをインストールできます。
パッケージをNugetにアップロードした場合は、3章の.csprojファイルの編集で<PackageId>に指定したIDを利用してインストールします。

## 参考

[dotnet new のカスタム テンプレート](https://learn.microsoft.com/ja-jp/dotnet/core/tools/custom-templates)
[チュートリアル: プロジェクト テンプレートを作成する](https://learn.microsoft.com/ja-jp/dotnet/core/tutorials/cli-templates-create-project-template?source=recommendations)
[チュートリアル: テンプレートパッケージを作成する](https://learn.microsoft.com/ja-jp/dotnet/core/tutorials/cli-templates-create-template-package)