VSCodeでASP.Net core(C#)+Svelte環境構築

　以前にも書いていたのですが、もう少し良いものにしようと環境を調整したものを書いてみました。主に自分用のメモです。
　今回の設定でフロントエンドとバックエンドがフォルダで分離されたのと、Tasksやlauncherの設定が少し良くなりました。Add-Migrationもタスクに登録したりと。

## フォルダ構成

開発プロジェクトフォルダ/
　├ .vscode/ 				// VSCodeの設定
　｜ ├ tasks.json			// タスクの設定
　｜ └ launch.json			// デバッグの設定
　｜
　├ Backend/				// バックエンドのDotnet core WebAPI
　｜ └ wwwroot				// wwwのルートフォルダ
　｜
　├ Frontend/				// フロントエンドのSvelte
　｜ ├ package.json		// パッケージの設定
　｜ └ rollup.config.js	// rollupの設定
　｜
　└ omnisharp.json			// msbuildの設定変更

## VSCode拡張機能
### 必須
- C# for Visual Studio Code (powered by OmniSharp)
	C#を利用するには必須

- Svelte for VSCode
	Svelteを利用するには必須

- Svelte Intellisense
	最悪無くても開発はできるが基本的にはコード補完が無いと効率が著しく落ちるので必須

### 推奨
- Japanese Language Pack for Visual Studio Code
	VSCodeの日本語化。英語で問題ないなら不要
	
- GitHistory
	Gitの履歴を表示したいなら必要。同じようなツールはいろいろあるので好きなものを使えばよい。
	
- Nuget GUI Manager (NuGet関連のツールんなら何でも良い)
	C#のパッケージ管理ツール。プロジェクトファイルを右クリックするとメニューに「NuGet」が出る。同じようなツールはいろいろあるので好きなものを使えばよい。

## 開発プロジェクトの作成手順
１ プロジェクトフォルダを作成。

２ ASP.Net core Web APIのテンプレートを作成（プロジェクトフォルダで実行）
```
> dotnet new webapi -o Backend
```

３ フロントエンドプロジェクトのひな形を作成（プロジェクトフォルダで実行）
```
> npx degit sveltejs/template Frontend
```

４ フロントエンドをタイプスクリプト対応させる（フロントエンドのフォルダで実行）
```
> cd Frontend
> node scripts/setupTypeScript.js
```

５ 必要なパッケージをインストールする（フロントエンドのフォルダで実行）
```
> npm install
```

６ MSBuilderの設定
　VisualStudioをインストールしていると、C#のビルダーがVisualStudioのものを利用するのでその場合の対応
　[開発プロジェクトフォルダ/omnisharp.json]を作成し、以下の内容を記述する
``` json:omnisharp.json
{ 
    "msbuild": {  "useBundledOnly": true } 
}
```

７ バックエンドに「wwwroot」フォルダを追加（フォルダ構成参照）

８ 基本設定の生成
　VSCodeを起動して作成したfolderを開くと、「Required assets to build and debug are missing from '********'. Add them?」
というmessageが表示されるので、[Yes]を選択すると、ASP.Net core用の設定の「tasks.json」と「launch.json」が作成される。

９ フロントエンドのデバッグコンパイルを変更
　フロントエンドの「package.json」を変更してデバッグ用のコンパイル設定を少し変更する。-w（ウォッチ）は便利なんですが、うまく機能しないときがあったりするので止めました。その代わりデバッグ時に毎回コンパイルしているのでデバッグの開始が遅くなります。
```
{
  "name": "svelte-app",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c --environment DEV",				// <= "dev": "rollup -c -w", から変更する
    "start": "sirv public --no-clear",
    "check": "svelte-check --tsconfig ./tsconfig.json"
  },
  "devDependencies": {
    "@rollup/plugin-commonjs": "^17.0.0",
    "@rollup/plugin-node-resolve": "^11.0.0",
    "rollup": "^2.3.4",
    "rollup-plugin-css-only": "^3.1.0",
    "rollup-plugin-livereload": "^2.0.0",
    "rollup-plugin-svelte": "^7.0.0",
    "rollup-plugin-terser": "^7.0.0",
    "svelte": "^3.0.0",
    "svelte-check": "^2.0.0",
    "svelte-preprocess": "^4.0.0",
    "@rollup/plugin-typescript": "^8.0.0",
    "typescript": "^4.0.0",
    "tslib": "^2.0.0",
    "@tsconfig/svelte": "^2.0.0"
  },
  "dependencies": {
    "sirv-cli": "^2.0.0"
  }
}
```

10. rollupの設定を変更してデバッグ用と発行用に動作する環境を作る。
　以下の様な変更を加える（scssの設定も追加しています。不要な方は設定しないでいいです）
```
import svelte from 'rollup-plugin-svelte';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
// import livereload from 'rollup-plugin-livereload';		// <= このコンポーネントは不要になる
import { terser } from 'rollup-plugin-terser';
import sveltePreprocess from 'svelte-preprocess';
import typescript from '@rollup/plugin-typescript';
import css from 'rollup-plugin-css-only';
import scss from "rollup-plugin-scss";					// <= scssでglobal.cssを作成する場合に設定

// const production = !process.env.ROLLUP_WATCH;			// <= productioの判定を変更する。元々は「-w」オプションだが環境変数に変更
const production = !process.env.DEV;
if(process.env.DEV){
	console.log('This compile is development mode');
}


// function serve() {										// <= サーバーはASP.Net coreが実行されるので、このコマンドはコメントアウト
// 	let server;

// 	function toExit() {
// 		if (server) server.kill(0);
// 	}

// 	return {
// 		writeBundle() {
// 			if (server) return;
// 			server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
// 				stdio: ['ignore', 'inherit', 'inherit'],
// 				shell: true
// 			});

// 			process.on('SIGTERM', toExit);
// 			process.on('exit', toExit);
// 		}
// 	};
// }

export default {
	input: 'src/main.ts',
	output: {
		sourcemap: true,
		format: 'iife',
		name: 'app',
		file: '../Backend/wwwroot/build/bundle.js'					// <= 元のフォルダから出力先を変更
	},
	plugins: [
		svelte({
			preprocess: sveltePreprocess({ sourceMap: !production }),
			compilerOptions: {
				// enable run-time checks when not in production
				dev: !production
			}
		}),
		// we'll extract any component CSS out into
		// a separate file - better for performance
		css({ output: 'bundle.css' }),
		
		scss({														// <= scssでglobal.cssを作成する場合に設定
			output: '../Backend/wwwroot/global.css'
			}),
		
		// If you have external dependencies installed from
		// npm, you'll most likely need these plugins. In
		// some cases you'll need additional configuration -
		// consult the documentation for details:
		// https://github.com/rollup/plugins/tree/master/packages/commonjs
		resolve({
			browser: true,
			dedupe: ['svelte']
		}),
		commonjs(),
		typescript({
			sourceMap: !production,
			inlineSources: !production
		}),

		// // In dev mode, call `npm run start` once				// <= サーバーはASP.Net coreが実行されるので、このコマンドはコメントアウト
		// // the bundle has been generated
		// !production && serve(),

		// // Watch the `public` directory and refresh the			// <= 動的なファイルの変更には対応できないのでこの処理はコメントアウト
		// // browser on changes when not in production
		// !production && livereload('public'),

		// If we're building for production (npm run build
		// instead of npm run dev), minify
		production && terser(),
	],
	watch: {
		clearScreen: false
	}
};

```

11. [tasks.json]を変更して機能を追加
- Svelteのコンパイル用設定
- publish時にsvelteのミニファイが有効なコンパイルを実行
- ASP.Net core でAdd-Migrationを使いやすくする設定（入力のために[inputs]を追加している）
- AllBuildにより、デバッグ時にバックエンドとフロントエンドの両方をコンパイル

```
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "command": "dotnet",
            "type": "process",
            "args": [
                "build",
                "${workspaceFolder}/Backend/Backend.csproj",
                "/property:GenerateFullPaths=true",
                "/consoleloggerparameters:NoSummary"
            ],
            "problemMatcher": "$msCompile"
        },
        {
            "label": "publish",
            "command": "dotnet",
            "type": "process",
            "args": [
                "publish",
                "${workspaceFolder}/Backend/Backend.csproj",
                "/property:GenerateFullPaths=true",
                "/consoleloggerparameters:NoSummary"
            ],
            "problemMatcher": "$msCompile",
            "dependsOn": "Frontend-debug"                       // これを追加することで発行前にフロントエンドを発行用にコンパイルする（ミニファイが有効になる）
        },
        {
            "label": "watch",
            "command": "dotnet",
            "type": "process",
            "args": [
                "watch",
                "run",
                "--project",
                "${workspaceFolder}/Backend/Backend.csproj"
            ],
            "problemMatcher": "$msCompile"
        },
        {
            "type": "npm",                                      // Svelteのコンパイル（publish）用設定
            "script": "build",
            "path": "Frontend/",
            "group": "build",
            "problemMatcher": [],
            "label": "Frontend-build",
            "detail": "rollup -c"
        },
        {
            "type": "npm",                                      // Svelteのコンパイル（デバッグ）用設定
            "script": "dev",
            "path": "Frontend/",
            "group": "build",
            "problemMatcher": [],
            "label": "Frontend-debug",
            "detail": "rollup -c --environment DEV"
        },
        {
            "label": "Add-Migration",                           // ASP.Net core でAdd-Migrationを使いやすくする設定
            "type": "shell",
            "command": "dotnet",
            "args": [
                "ef",
                "migrations",
                "add",
                "${input:migration-name}",
                "-p",
                "${workspaceFolder}/Backend/Backend.csproj"
            ],
            "problemMatcher": "$msCompile"
        },
        {
            "label": "AllBuild",                                // launchでデバッグ実行の際にフロントエンドとバックエンドの両方を同時にコンパイルするための設定
            "dependsOn": [
                "build",
                "Frontend-debug"
            ]
        }
    ],
    "inputs": [
        {
            "id": "migration-name",                                     // ASP.Net core でAdd-Migrationの入力用
            "description": "マイグレーション名を設定してください。", 
            "default": "init",
            "type": "promptString"
        }
    ]
}
```

12. [launch.json]を変更してJavaScriptのデバッグを可能にする
　[serverReadyAction]の[action]をデバッグ用に変更、デバッグの設定として[uriFormat]と[webRoot]を追加する。また、実行前のコンパイルを「AllBuild」にしてフロントエンドとバックエンドの両方をコンパイルさせる。
※「webRoot」の記述が誤っていました。ソースが有るフォルダを指定する必要があります。（2022/4/23修正）
```
{
    "version": "0.2.0",
    "configurations": [
        {
            // Use IntelliSense to find out which attributes exist for C# debugging
            // Use hover for the description of the existing attributes
            // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
            "name": ".NET Core Launch (web)",
            "type": "coreclr",
            "request": "launch",
            // "preLaunchTask": "build",
            "preLaunchTask": "AllBuild",                                // 実行前にフロントエンドとバックエンドの両方をコンパイル
            // If you have changed target frameworks, make sure to update the program path.
            "program": "${workspaceFolder}/Backend/bin/Debug/net6.0/Backend.dll",
            "args": [],
            "cwd": "${workspaceFolder}/Backend",
            "stopAtEntry": false,
            // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
            "serverReadyAction": {
                // "action": "openExternally",
                "action": "debugWithEdge",                              // actionを変更してJavaScriptのデバッグを可能にする
                //"pattern": "\\bNow listening on:\\s+(https?://\\S+)",
                "pattern": "\\bNow listening on:\\s+https://\\S+:([0-9]+)", // ちょっと変更してます
                "uriFormat":"https://localhost:%s/",                    // FrontendのエントリーURLを設定。「%s」には上の「pattern」で起動時のログから取得したポート番号が入る
                "webRoot": "${workspaceFolder}/Backend/wwwroot/build"         // デバッグ用にFrontEndの基本フォルダを指定
            },
            "env": {
                "ASPNETCORE_ENVIRONMENT": "Development"
            },
            "sourceFileMap": {
                "/Views": "${workspaceFolder}/Views"
            }
        },
        {
            "name": ".NET Core Attach",
            "type": "coreclr",
            "request": "attach"
        }
    ]
}
```

### index.htmlの作成
　この辺りからは、個々の環境で変わってくるので基本的な設定として書いておきます。
　Backend/wwwroot/index.htmlを作成
``` html;index.html
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset='utf-8'>
	<meta name='viewport' content='width=device-width,initial-scale=1'>

	<title>Svelte app</title>

	<link rel='icon' type='image/png' href='/favicon.png'>
	<link rel='stylesheet' href='/global.css'>
	<link rel='stylesheet' href='/build/bundle.css'>
	<script defer src='/build/bundle.js'></script>
</head>

<body>
</body>
</html>
```

## デフォルトファイルの有効化
　ASP.net coreの「Program.cs」に以下の設定を追加して「/」の時に「index.html」が呼ばれるように変更
``` cs:program.cs
...

// デフォルトファイルを有効にする（/ => /index.htmlを開くようにする）
app.UseDefaultFiles();

// 静的ファイル（wwwroot）の有効化
app.UseStaticFiles();

...
```

## Git関連
1. .gitignoreの作成
	以下のコマンドで、ASP.Net用のgit除外の基本が作成できます。(最初に作成したfolderで実行する)
```
> dotnet new gitignore
```

2. 以下の設定を上記のファイルに追加する
```
#
# for local settings	// これは開発環境は個人に依存するように設定している。共通にする場合はこの設定は削除すること
#
/Backend/appsettings.Development.json

#
# for Svelte			// sveletで出力されるデータは除外している
#
/Frontend/src/**/*.js
/Frontend/src/**/*.map
/Frontend/src/global.d.ts
/Backend/wwwroot/build/
/Backend/wwwroot/global.css
```