[Angular] ng new で生成されるワークスペースの構成詳細をみる

## きっかけ
- Angularに触れる機会があり、チュートリアルを進めていく中で自動生成されるファイルを読み解くのが大変。
- あまり理解していなくとも、気付けばアプリが動いていた。（フレームワークのいいところでもある）

という経験から、アプリ構成ファイルをメモついでにまとめたくなったのでこの記事にまとめます。

公式ドキュメントのチュートリアル[「ツアーオブヒーローズ」](https://angular.jp/tutorial)で初めに叩くコマンド `ng new angular-tour-heroes` を参照の元、各生成ファイルの概要を解説します。公式ドキュメントのリンクも添えていますので、詳細はリンク先をご覧ください。

### 実行環境
- angular-cli: 13.3.0
- Node: 14.18.1
- Package Manager: npm 8.5.5

## 生成されるファイル、ディレクトリ構成
`ng new angular-tour-heroes` コマンドを実行すると、以下のような対話が始まりrouting設定と利用するStylesheetのフォーマットを選択できます。
今回はデフォルト設定のrouting ⇒ No、stylesheet format ⇒ CSSの場合の構成とします。
```shell
C:\Angular>ng new angular-tour-heroes
? Would you like to add Angular routing? (y/N) y or N ⇒デフォルトはNo
? Which stylesheet format would you like to use? (Use arrow keys)
> CSS
  SCSS   [ https://sass-lang.com/documentation/syntax#scss                ]
  Sass   [ https://sass-lang.com/documentation/syntax#the-indented-syntax ]
  Less   [ http://lesscss.org                                             ]
```

コマンド実行後、angular-tour-heroesというAngularワークスペースが自動生成され、必要なファイルをCREATEしてくれて、以下のフォルダが構成されます。
https://angular.jp/guide/file-structure
```
/angular-tour-heroes                  // アプリルート
├── /node_modules                     // npmで管理されたパッケージまとめフォルダ
├── /src                              // アプリ本体の格納フォルダ
│   ├── /app                          // Angularアプリのコード一式
│   │   ├── app.component.html        // ルートコンポーネントhtml
│   │   ├── app.component.css         // ルートコンポーネントcss
│   │   ├── app.component.spec.ts     // テストスクリプト
│   │   ├── app.component.ts          // ルートコンポーネント
│   │   └── app.module.ts             // ルートモジュール
│   ├── /assets                       // 静的アセットの格納フォルダ
│   ├── /environments                 // 環境設定の格納フォルダ
│   │   ├── environment.prod.ts       // ターゲット固有向け
│   │   └── environment.ts            // 本番環境向け
│   ├── favicon.ico                   // ファビコン（Webページのアイコン）
│   ├── index.html    - ★            // メインページ
│   ├── main.ts       - ★ 　　　　　　// Angularアプリ起動コード 
│   ├── polyfills.ts　　　　　　　　　 // ポリフィル（古いWebブラウザへのサポート設定）
│   ├── styles.css                    // Angularアプリ全体のスタイルファイル
│   └── test.ts                       // テスト設定ファイル（基本編集しない）
├── .browserslistrc                   // ターゲットブラウザ、フロントエンドツール間のNode.jsバージョン共有設定
├── .editorconfig                     // コードエディタ向け設定
├── .gitignore                        // Gitに無視してほしいファイルの設定
├── angular.json      - ★            // Angularワークスペース設定
├── karma.conf.js                     // テストフレームワークKarma(*1)設定ファイル
├── package-lock.json                 // 利用するパッケージ情報まとめ（直接編集しない）
├── package.json      - ★            // パッケージ情報まとめ
├── README.md                         // 本プロジェクトに関するREADME
├── tsconfig.app.json                 // アプリケーション固有のTypeScript(*2)設定
├── tsconfig.json     - ★            // TypeScriptコンパイラの設定情報
└── tsconfig.spec.json                // アプリケーションテスト用のTypeScript設定
```
(*1)Karma: https://karma-runner.github.io/2.0/config/configuration-file.html
(*2)TypeScript: https://www.typescriptlang.org/

以上の中から、Angularアプリが動作するための根幹となる（が、意外と知られていない）ところ（★印）を抜粋します。

- メインファイル
    - main.ts
    - index.html
- 各種設定ファイル
    - package.json
    - tsconfig.json
    - angular.json


## メインファイル
- main.ts
- index.html

### main.ts
Angularアプリを起動するためのコード。
https://angular.io/guide/file-structure#application-source-files
```typescript
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));
```
`platformBrowserDynamic`は、ブラウザでアプリを起動するために用意されたAngular標準の関数で、`platformBrowserDynamic#bootstrapModule`メソッドでルートモジュールであるAppModuleを起動しています。
つまり、アプリケーションのルートモジュールAppModuleをブートストラップしてブラウザで走らせることでアプリが動作しています。

### index.html
アプリが動作するメインページ。
```html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>AngularTourHeroes</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root></app-root>
</body>
</html>
```

`<body>`タグで囲われた`<app-root></app-root>`でルートコンポーネントであるAppComponentのテンプレート（templateUrlで指定されたhtml; app.component.html）が表示されます。


## 設定ファイル
- package.json
- tsconfig.json
- angular.json


### package.json
アプリで利用するライブラリ情報を管理する設定ファイル。
`npm`コマンド、`yarn`コマンドは、このファイルで定義したパッケージをインストールします。
https://angular.jp/guide/npm-packages#packagejson
```json-doc
{
  "name": "angular-tour-heroes",            // アプリ名
  "version": "0.0.0",                       // バージョン
  "scripts": {                              // npm run で実行できるコマンド
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test"
  },
  "private": true,                          // モジュール公開の可否
  "dependencies": {                         // 依存するモジュールとバージョン管理
    ...
  },
  "devDependencies": {                      // アプリで利用する開発ライブラリ
    ...
  }
}
```

### tsconfig.json
TypeScriptコンパイラの挙動に関する設定ファイル。
https://angular.io/guide/typescript-configuration#configuration-files
```json-doc
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
  "compileOnSave": false,
  "compilerOptions": {                            // コンパイルオプション
    "baseUrl": "./",                              // 相対パスの起点となるディレクトリパス
    "outDir": "./dist/out-tsc",                   // .jsファイルが出力されるディレクトリパス
    "forceConsistentCasingInFileNames": true,     // ファイル名の大文字小文字を区別する
    "strict": true,                               // true:strict系(*1)が全て有効となる
    "noImplicitOverride": true,                   // true:継承クラスがもつ同名の関数には`override`をつけないといけない
    "noPropertyAccessFromIndexSignature": true,   // true:定義していないfieldへのアクセスを許容しない
    "noImplicitReturns": true,                    // true:戻り値が必要な関数にreturnが存在しなくてもよいことを許容しない
    "noFallthroughCasesInSwitch": true,           // true:switch文において、次のcaseへ処理を持ち越すことを許容しない
    "sourceMap": true,                            // ソースマップの生成
    "declaration": false,                         // 全てのTypeScript/JavaScriptファイルに`d.ts`を生成するか
    "downlevelIteration": true,                   // 古いバージョンのJavaScriptにトランスパイルするか
    "experimentalDecorators": true,               // デコレーターを有効にするか
    "moduleResolution": "node",                   // モジュールの解決方法; node
    "importHelpers": true,                        // ヘルパー関数をtslibモジュールからインストールするか
    "target": "es2017",                           // 生成するJavaScriptのバージョン
    "module": "es2020",                           // 生成するJavaScriptモジュールの形式
    "lib": [                                      // コンパイル時にincludeされるライブラリ
      "es2020",
      "dom"
    ]
  },
  "angularCompilerOptions": {
    "enableI18nLegacyMessageIdFormat": false,     // false: i18n属性によってテンプレートでタグづけされたメッセージのレガシーID生成を指示しない
    "strictInjectionParameters": true,            // true:インジェクションタイプを必ず指定しなければならない
    "strictInputAccessModifiers": true,           // Inputプロパティにバインディングする際、アクセス修飾子private/protected/readonlyを許容しない
    "strictTemplates": true                       // true: 厳密なテンプレートタイプチェック(*2)を有効
  }
}
```
(*1)strict: https://www.typescriptlang.org/tsconfig#strict
(*2)strictTemplates: https://angular.jp/guide/template-typecheck#strict-mode

### angular.json
Angular CLIによるビルドや開発ツールに対する、ワークスペース全体・プロジェクト固有の構成に関するデフォルト設定ファイル。
指定のパス値は、ルートワークスペースフォルダが基準になっています。
https://angular.jp/guide/workspace-config

<details> <summary>angular.json(長くなるので折り畳んでいます)</summary> <div>

```json-doc
{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "angular-tour-heroes": {
      "projectType": "application",
      "schematics": {
        "@schematics/angular:application": {
          "strict": true
        }
      },
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/angular-tour-heroes",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.css"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "500kb",
                  "maximumError": "1mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "2kb",
                  "maximumError": "4kb"
                }
              ],
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "outputHashing": "all"
            },
            "development": {
              "buildOptimizer": false,
              "optimization": false,
              "vendorChunk": true,
              "extractLicenses": false,
              "sourceMap": true,
              "namedChunks": true
            }
          },
          "defaultConfiguration": "production"
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "configurations": {
            "production": {
              "browserTarget": "angular-tour-heroes:build:production"
            },
            "development": {
              "browserTarget": "angular-tour-heroes:build:development"
            }
          },
          "defaultConfiguration": "development"
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "angular-tour-heroes:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.spec.json",
            "karmaConfig": "karma.conf.js",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.css"
            ],
            "scripts": []
          }
        }
      }
    }
  },
  "defaultProject": "angular-tour-heroes"
}
```
</div></details>