LoginSignup
10
7

More than 5 years have passed since last update.

一つのプロジェクトにAngularアプリを複数入れてみる

Last updated at Posted at 2018-03-19

はじめに

とあるWebアプリを開発するにあたって、一つのプロジェクトに複数のAngularアプリを構成したくなった。
したくなったので、やってみた。

「もっといい方法あるよー」とか「これ間違ってるよー」とかあったら教えてください。

2018/3/20 更新
アプリでassetsを使っているときにpathが合わなくなることが判明。
assets内のリソースへのpathを相対pathで指定することと、
.angular-cli.jsonの設定に以下を追加することで対応できた。

2018/12/30 追記
タグにもありますが、angular v5の時代の記事です。
angular v7.1.4現在、.angular-cli.jsonangular.jsonに置き換わっており、中身を見る限りマルチプロジェクトに対応しているようです。
angular.jsonの解説は2018 Angular advent calender内の記事などあるので、そちらを参照ください。

.angular-cli.json
  "apps": [
    {
      "baseHref": "./",
      ...

フォルダ構成

書いてみたけどスーパー見づらい。

.
|-- coverage    // テストカバレッジ
|   |-- first-app
|   |-- second-app
|   `-- server
|-- dist    // ビルド結果
|   |-- first-app
|   |-- second-app
|   `-- server
|-- e2e    // e2eテストは一旦パス
|-- server    // バックエンド側ソース
|   |-- index.spec.ts
|   |-- index.ts
|   `-- tsconfig.json
|-- src    // フロント(Angular)アプリ側ソース
|   |-- first-app
|   |   |-- app
|   |   |   |-- app.component.css
|   |   |   |-- app.component.html
|   |   |   |-- app.component.spec.ts
|   |   |   |-- app.component.ts
|   |   |   `-- app.module.ts
|   |   |-- assets
|   |   |-- environments
|   |   |   |-- environment.prod.ts
|   |   |   `-- environment.ts
|   |   |-- favicon.ico
|   |   |-- index.html
|   |   |-- main.ts
|   |   |-- polyfills.ts
|   |   |-- styles.css
|   |   |-- test.ts
|   |   |-- tsconfig.app.json
|   |   |-- tsconfig.spec.json
|   |   `-- typings.d.ts
|   |-- second-app
|   |   |-- app
|   |   |   |-- app.component.css
|   |   |   |-- app.component.html
|   |   |   |-- app.component.spec.ts
|   |   |   |-- app.component.ts
|   |   |   `-- app.module.ts
|   |   |-- assets
|   |   |-- environments
|   |   |   |-- environment.prod.ts
|   |   |   `-- environment.ts
|   |   |-- favicon.ico
|   |   |-- index.html
|   |   |-- main.ts
|   |   |-- polyfills.ts
|   |   |-- styles.css
|   |   |-- test.ts
|   |   |-- tsconfig.app.json
|   |   |-- tsconfig.spec.json
|   |   `-- typings.d.ts
|   |-- polyfills.ts    // アプリ一括テスト用polyfill
|   |-- test.ts    // アプリ一括テスト用ブートストラップ
|   `-- tsconfig.spec.json    // アプリ一括テスト用コンフィグ
|-- README.md
|-- karma.conf.js
|-- package-lock.json
|-- package.json
|-- protractor.conf.js
|-- tsconfig.json
`-- tslint.json

複数アプリ構成になるまで

それぞれのアプリのソースを置く

ng newで生成されたソースだと、./src以下にアプリのソースが集まっている。
複数アプリに対応できるように、ソースの移動&コピーを行う。

# './src' copy to './src/first-app' & './src/second-app'

mv ./src ./first-app
mkdir ./src
mv ./first-app ./src
cp -r ./src/first-app ./src/second-app

それぞれのtsconfig.xxx.jsonのパスがずれるので直す

tsconfig.app.json(tsconfig.spec.jsonも同様)
{
  "extends": "../../tsconfig.json",  // <-- ここと
  "compilerOptions": {
    "outDir": "../../out-tsc/app",   // <-- ここ
    "baseUrl": "./",
    ...

アプリ側はこれだけ。

.angular-cli.jsonを直す

Angular CLIが複数アプリを正しく扱えるように設定してやる。

.angular-cli.json
  ...
  "apps": [
    {
      "name": "first-app",        // (Optional) 名前を付けてやるとあとでうれしいかも。
      "root": "src/first-app",    // ソースコードのルートディレクトリ
      "outDir": "dist/first",     // 出力先ディレクトリ
      "deployUrl": "/first-app",  // デプロイするurl (expressのルート設定と合わせる)
      ...
    },
    {
      "name": "second-app",        // 二つ目のアプリも同様に
      "root": "src/second-app",
      "outDir": "dist/second",
      "deployUrl": "/second-app",
      ...
    }
  ],
  ...

パスによってExpressが返すページを変える

サーバのフレームワークはExpressを使ってます。
執筆時点ではexpress@4.16.3でした。

また、AngularがTypescriptなのでサーバサイドもTypescriptで書いちゃいました。
tsconfig.jsonもフロント側のtsconfig.app.jsonあたりから持ってきてサクッと書いちゃいます。

(server)index.ts
import * as path from "path";
import * as express from "express";
const app = express();

// ...

// .angular-cli.jsonで指定したdeployUrlとパスを合わせること
app.use("/first-app", express.static(path.resolve(`${__dirname}/../first-app`));
app.use("/second-app", express.static(path.resolve(`${__dirname}/../second-app`));

// ...
(server)tsconfig.json
{
  "extends": "../tsconfig.json", // Anguler CLIでルートディレクトリに吐き出される設定を使っとく
  "compilerOptions": {
    "outDir": "../dist/server",  // ビルド出力先を設定
    "baseUrl": "./",
    "module": "commonjs",        // イマイチ把握してない。誰か教えてください。
    "target": "es2015",          // 使っているNode.jsのバージョンに合わせて出力フォーマットを決める。(node v8系なのでes2015は動く)
    "types": []
  },
  "exclude": [
    "test.ts",
    "**/*.spec.ts"
  ]
}

複数アプリをそれぞれビルドする

今の所、.angular-cli.jsonのappsに複数アプリを書いても一括で全部ビルドしてくれたりserveしてくれたりする機能はないようです。
なので、個別にビルドします。
サーバサイドもついでにビルドします。

# .angular-cli.jsonにnameプロパティをちゃんと付けてたらアプリ名で指定できる
ng build --prod -a first-app
ng build --prod -a second-app
tsc -p server/

# nameプロパティ付けてない場合は配列のインデックスで。
# あとの工程でインデックスをずらすので注意。
ng build --prod -a 0
ng build --prod -a 1
tsc -p server/

こんだけ打つの面倒なので、npm scriptに書いちゃいましょう。
開発に使ってるPCがWindowsで、デプロイ先がDebian系だったりするので、npm-run-allのお世話になってます。
Linux系OS使わせて。

package.json
  "scripts": {
    "build": "npm-run-all -s build:**:*",
    "build:first": "ng build --prod -a first",
    "build:second": "ng build --prod -a second",
    "build:server": "tsc -p server/",
  }

実行する!

node dist/server/index.js

http://<deploy-domain>/first-apphttp://<deploy-domain>/second-appにアクセスするとそれぞれのアプリが表示されるはずです。

実行まではできたけど。

もうちょっとやりたいことがある。

  • ng serveで画面を確認しながらコード書きたい。
  • ユニットテストはどうやるの?

ng serveしたい

これは、ng serveにオプションを付けるだけで出来ました。

ng serve --app first-app
ng serve --app second-app

ng buildと同じですね。

ユニットテストしたい

アプリをそれぞれ別個にテストするだけなら簡単。

ng test --app first-app
ng test --app second-app

これもng buildと同じくオプション付けるだけでした。
ここで疑問。

ユニットテストのカバレッジってどう取る…?

それぞれ別個にカバレッジを取るだけならまぁ良い。

ng test --app first-app --code-coverage
ng test --app second-app --code-coverage

不満があるとすれば、片方実行するたびにもう一方のカバレッジ結果に上書きされてしまう点。
この方法だとこのプロジェクト全体のカバレッジを見ることができないんですね。

プロジェクト全体のテストカバレッジを俯瞰したい!

というわけで構成をもう少しいじります。

src以下に全体ユニットテスト用ファイルを置く

全体ユニットテストって良く分かんない言葉ですね。でも他に思いつかないからそのまま書きます。
src以下に以下の3ファイルを置きましょう。

polyfills.ts
import "./first-app/polyfills";
import "./second-app/polyfills";
test.ts
(デフォルトのtest.tsをそのままコピーしてくるだけ)
tsconfig.spec.json
(デフォルトのtsconfig.spec.jsonをそのままコピーしてくるだけ)

.angular-cli.jsonに全体ユニットテスト用構成を追加する

apps以下にこんなのを追加します。

.angular-cli.json
  "apps": [
    {
      "name": "testAll",   // 分かりやすい名前にしましょう。
      "root": "src/",      // テスト用ファイルを置いたディレクトリをルートにする。
      "main": "test.ts",   // 実際使わないのでなんでもいい。"省略する"ことに対しては怒られるのでなんか書く。
      "test": "test.ts",   // さっき置いたファイル。
      "testTsconfig": "tsconfig.spec.json",  // さっき置いたファイル。
      "polyfills": "polyfills.ts"  // さっき置いたファイル。ブラウザ使ってテストするので、各アプリで必要なpolyfillを適用してやる必要がある。
    },
    ...

僕はapps以下の先頭にこいつを追加しました。
先頭にある場合は、ng test(appオプションを省略)したときの動作がデフォルトでこの構成になります。
アプリ名を指定しなかった場合は全体テスト、指定した場合はアプリ個別のテスト、というように使い分けています。

あくまでこの構成はテストしか考えていないので、ng serveng build(appオプションを省略)はどちらもエラーで通らなくなっています。
servebuildをしたいときは--app 1--app 2を付けてあげましょう。(さっき書いてたインデックスとはずれてるので注意)

テストしてみる

おもむろにng testしてみましょう。全てのアプリに対してテストが実行されたと思います。
ng test --code-coverageしてやると、全てのアプリのテストカバレッジが取得できるようになりました。
めでたし。

おわりに

あ、サーバサイドのテスト入れるの忘れてた…。

10
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
7