📝

Nrwl/Nx での Angular アプリケーション新規作成手段について 3 つ比較してみた

2021/07/04に公開

こんにちは、奥野賢太郎 ( @okunokentaro ) です。今回は Nrwl/Nx #というウェブアプリケーション開発におけるモノレポ管理のための開発ツールを用いて、3 通りのやり方で Angular アプリケーションを新規作成してみました。そのやり方の紹介と、生成結果の違いについて紹介します。

この記事で紹介しないこと

  • モノレポとはなにか、その有用性についてなどは紹介しません
  • Nx とは何かについての詳細は紹介しません

おすすめ記事

上から見ていけば、だいたい Nx とは何かがわかります。

Nx の利用開始

Nx とはコマンドラインツールです。そのため npx で呼び出すか、 npm install -g にてインストールします。

公式の Getting Startedでは 2020/2/23 現在失敗してしまったので、ここに成功する方法をまとめます。

インストール

暗黙でグローバルに @nrwl/workspace, typescript を求められるのが少々惜しいですが、 npx create-nx-workspace ではモジュールが不足してしまうようでグローバルインストールをおすすめします。(不具合にみえるが、これが意図的なら不親切

npm install -g create-nx-workspace @nrwl/workspace typescript

インストールの確認には次のコマンドを実行します。

create-nx-workspace --help

ヘルプが表示されれば、インストールに成功しています。

生成

create-nx-workspace を使うことによってアプリケーション用の各種設定ファイルを自動生成します。

create-react-app #create-nuxt-app # のようなものですね。ここ 3 年くらいでウェブアプリケーションの立ち上げにスケルトンの複製やツールチェインの記述をする機会がかなり減りました。Nx もそういったツールチェインを隠蔽する目的を持っています。

さて、 create-nx-workspace を使うと Angular with Nx なアプリケーション開発基盤が整うわけですが、そのやり方は 3 つあります。筆者も Nx を実務で利用したことはまだないため、どれを使うとよいか判断すべく、比較していきます。

生成方法

  1. create-nx-workspace にて empty な workspace を作成し、そこにng g @nrwl/angular:applicationにて Angular アプリケーションを追加する
  2. create-nx-workspace にて新規の Angular アプリケーションを含む workspace を作成する
  3. ng newにて新規の Angular アプリケーションを作成し、その後ng add @nrwl/workspaceにて workspace に変換する

1.はチュートリアルにて紹介されている手法、2.は CLI を実行したときに現れる選択肢から進める手法、3.はもっともリアルユースでありそうな既存アプリに Nx を採用する手法です。

以下、それぞれの手法を実行した結果を紹介します。

生成結果

まずは無機質に生成結果だけ貼ります。比較は以後の節にて。

1. empty な workspace に Angular アプリを作成

生成過程

create-nx-workspace myworkspace

<details><summary>実行中のログ</summary><div>

? What to create in the new workspace (Use arrow keys)
❯ empty             [an empty workspace]
  web components    [a workspace with a single app built using web components]
  angular           [a workspace with a single Angular application]
  angular-nest      [a workspace with a full stack application (Angular + Nest)]
  react             [a workspace with a single React application]
  react-express     [a workspace with a full stack application (React + Express)]
  next.js           [a workspace with a single Next.js application]

? CLI to power the Nx workspace
  Nx           [Extensible CLI for JavaScript and TypeScript applications]
❯ Angular CLI  [Extensible CLI for Angular applications. Recommended for Angular projects.]

Creating a sandbox with Nx...
new myworkspace --preset="empty" --interactive=false --collection=@nrwl/workspace
CREATE myworkspace/nx.json (262 bytes)
CREATE myworkspace/tsconfig.json (509 bytes)
CREATE myworkspace/README.md (2705 bytes)
CREATE myworkspace/.editorconfig (245 bytes)
CREATE myworkspace/.gitignore (503 bytes)
CREATE myworkspace/.prettierignore (74 bytes)
CREATE myworkspace/.prettierrc (25 bytes)
CREATE myworkspace/angular.json (96 bytes)
CREATE myworkspace/package.json (1187 bytes)
CREATE myworkspace/apps/.gitkeep (1 bytes)
CREATE myworkspace/libs/.gitkeep (0 bytes)
CREATE myworkspace/tools/tsconfig.tools.json (218 bytes)
CREATE myworkspace/tools/schematics/.gitkeep (0 bytes)
CREATE myworkspace/.vscode/extensions.json (170 bytes)
✔ Packages installed successfully.
    Directory is already under version control. Skipping initialization of git.

———————————————————————————————————————————————


>  NX   NOTE  Nx CLI is not installed globally.

  This means that you might have to use "yarn nx" or "npm nx" to execute commands in the workspace.
  Run "yarn global add @nrwl/cli" or "npm install -g @nrwl/cli" to be able to execute command directly.

</div></details>

cd myworkspace
ng add @nrwl/angular --defaults

<details><summary>実行中のログ</summary><div>

? Would you like to share anonymous usage data about this project with the Angular Team at
Google under Google’s Privacy Policy at https://policies.google.com/privacy? For more
details and how to change this setting, see http://angular.io/analytics. (y/N) No
Installing packages for tooling via npm.
Installed packages for tooling via npm.
CREATE jest.config.js (250 bytes)
UPDATE angular.json (316 bytes)
UPDATE package.json (2006 bytes)
✔ Packages installed successfully.

</div></details>

ng g @nrwl/angular:application myapp

<details><summary>実行中のログ</summary><div>

? Which stylesheet format would you like to use? (Use arrow keys)
❯ CSS
  SASS(.scss)  [ http://sass-lang.com   ]
  Stylus(.styl)[ http://stylus-lang.com ]
  LESS         [ http://lesscss.org     ]

? Would you like to configure routing for this application? No
CREATE tslint.json (2261 bytes)
CREATE apps/myapp/tsconfig.json (97 bytes)
CREATE apps/myapp/src/favicon.ico (15086 bytes)
CREATE apps/myapp/browserslist (429 bytes)
CREATE apps/myapp/tsconfig.app.json (163 bytes)
CREATE apps/myapp/tslint.json (203 bytes)
CREATE apps/myapp/src/index.html (335 bytes)
CREATE apps/myapp/src/main.ts (375 bytes)
CREATE apps/myapp/src/polyfills.ts (2836 bytes)
CREATE apps/myapp/src/styles.css (80 bytes)
CREATE apps/myapp/src/assets/.gitkeep (0 bytes)
CREATE apps/myapp/src/environments/environment.prod.ts (51 bytes)
CREATE apps/myapp/src/environments/environment.ts (662 bytes)
CREATE apps/myapp/src/app/app.module.ts (297 bytes)
CREATE apps/myapp/src/app/app.component.css (2088 bytes)
CREATE apps/myapp/src/app/app.component.html (2537 bytes)
CREATE apps/myapp/src/app/app.component.spec.ts (919 bytes)
CREATE apps/myapp/src/app/app.component.ts (217 bytes)
CREATE apps/myapp/tsconfig.spec.json (233 bytes)
CREATE apps/myapp/jest.config.js (347 bytes)
CREATE apps/myapp/src/test-setup.ts (30 bytes)
CREATE apps/myapp-e2e/tslint.json (48 bytes)
CREATE apps/myapp-e2e/cypress.json (410 bytes)
CREATE apps/myapp-e2e/tsconfig.e2e.json (167 bytes)
CREATE apps/myapp-e2e/tsconfig.json (137 bytes)
CREATE apps/myapp-e2e/src/fixtures/example.json (80 bytes)
CREATE apps/myapp-e2e/src/integration/app.spec.ts (402 bytes)
CREATE apps/myapp-e2e/src/plugins/index.js (832 bytes)
CREATE apps/myapp-e2e/src/support/app.po.ts (47 bytes)
CREATE apps/myapp-e2e/src/support/commands.ts (1068 bytes)
CREATE apps/myapp-e2e/src/support/index.ts (599 bytes)
UPDATE package.json (2006 bytes)
UPDATE angular.json (4063 bytes)
UPDATE nx.json (387 bytes)

</div></details>

ファイルツリー

myworkspace
.
├── .editorconfig
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode
│   └── extensions.json
├── README.md
├── angular.json
├── apps
│   ├── .gitkeep
│   ├── myapp
│   │   ├── browserslist
│   │   ├── jest.config.js
│   │   ├── src
│   │   │   ├── app
│   │   │   │   ├── app.component.css
│   │   │   │   ├── app.component.html
│   │   │   │   ├── app.component.spec.ts
│   │   │   │   ├── app.component.ts
│   │   │   │   └── app.module.ts
│   │   │   ├── assets
│   │   │   │   └── .gitkeep
│   │   │   ├── environments
│   │   │   │   ├── environment.prod.ts
│   │   │   │   └── environment.ts
│   │   │   ├── favicon.ico
│   │   │   ├── index.html
│   │   │   ├── main.ts
│   │   │   ├── polyfills.ts
│   │   │   ├── styles.css
│   │   │   └── test-setup.ts
│   │   ├── tsconfig.app.json
│   │   ├── tsconfig.json
│   │   ├── tsconfig.spec.json
│   │   └── tslint.json
│   └── myapp-e2e
│       ├── cypress.json
│       ├── src
│       │   ├── fixtures
│       │   │   └── example.json
│       │   ├── integration
│       │   │   └── app.spec.ts
│       │   ├── plugins
│       │   │   └── index.js
│       │   └── support
│       │       ├── app.po.ts
│       │       ├── commands.ts
│       │       └── index.ts
│       ├── tsconfig.e2e.json
│       ├── tsconfig.json
│       └── tslint.json
├── jest.config.js
├── libs
│   └── .gitkeep
├── node_modules
├── nx.json
├── package-lock.json
├── package.json
├── tools
│   ├── schematics
│   │   └── .gitkeep
│   └── tsconfig.tools.json
├── tsconfig.json
└── tslint.json

2. 最初から Angular アプリを含む workspace を作成

生成過程

create-nx-workspace myworkspace

<details><summary>実行中のログ</summary><div>

? What to create in the new workspace
  empty             [an empty workspace]
  web components    [a workspace with a single app built using web components]
❯ angular           [a workspace with a single Angular application]
  angular-nest      [a workspace with a full stack application (Angular + Nest)]
  react             [a workspace with a single React application]
  react-express     [a workspace with a full stack application (React + Express)]
  next.js           [a workspace with a single Next.js application]

? Application name                    myapp
? Default stylesheet format           (Use arrow keys)
❯ CSS
  SASS(.scss)  [ http://sass-lang.com   ]
  Stylus(.styl)[ http://stylus-lang.com ]
  LESS         [ http://lesscss.org     ]
Creating a sandbox with Nx...

new myworkspace --preset="angular" --appName="myapp" --style="css" --interactive=false --collection=@nrwl/workspace
CREATE myworkspace/nx.json (262 bytes)
CREATE myworkspace/tsconfig.json (509 bytes)
CREATE myworkspace/README.md (2705 bytes)
CREATE myworkspace/.editorconfig (245 bytes)
CREATE myworkspace/.gitignore (503 bytes)
CREATE myworkspace/.prettierignore (74 bytes)
CREATE myworkspace/.prettierrc (25 bytes)
CREATE myworkspace/angular.json (96 bytes)
CREATE myworkspace/package.json (1190 bytes)
CREATE myworkspace/apps/.gitkeep (1 bytes)
CREATE myworkspace/libs/.gitkeep (0 bytes)
CREATE myworkspace/tools/tsconfig.tools.json (218 bytes)
CREATE myworkspace/tools/schematics/.gitkeep (0 bytes)
CREATE myworkspace/.vscode/extensions.json (170 bytes)
✔ Packages installed successfully.
? Would you like to share anonymous usage data about this project with the Angular Team at
Google under Google’s Privacy Policy at https://policies.google.com/privacy? For more
details and how to change this setting, see http://angular.io/analytics. No

CREATE jest.config.js (250 bytes)
CREATE tslint.json (2261 bytes)
CREATE apps/myapp/tsconfig.json (97 bytes)
CREATE apps/myapp/src/favicon.ico (15086 bytes)
CREATE apps/myapp/browserslist (429 bytes)
CREATE apps/myapp/tsconfig.app.json (163 bytes)
CREATE apps/myapp/tslint.json (203 bytes)
CREATE apps/myapp/src/index.html (335 bytes)
CREATE apps/myapp/src/main.ts (375 bytes)
CREATE apps/myapp/src/polyfills.ts (2833 bytes)
CREATE apps/myapp/src/styles.css (80 bytes)
CREATE apps/myapp/src/assets/.gitkeep (0 bytes)
CREATE apps/myapp/src/environments/environment.prod.ts (51 bytes)
CREATE apps/myapp/src/environments/environment.ts (662 bytes)
CREATE apps/myapp/src/app/app.module.ts (297 bytes)
CREATE apps/myapp/src/app/app.component.css (2088 bytes)
CREATE apps/myapp/src/app/app.component.html (2537 bytes)
CREATE apps/myapp/src/app/app.component.spec.ts (919 bytes)
CREATE apps/myapp/src/app/app.component.ts (217 bytes)
CREATE apps/myapp/tsconfig.spec.json (233 bytes)
CREATE apps/myapp/jest.config.js (347 bytes)
CREATE apps/myapp/src/test-setup.ts (30 bytes)
CREATE apps/myapp-e2e/tslint.json (48 bytes)
CREATE apps/myapp-e2e/cypress.json (410 bytes)
CREATE apps/myapp-e2e/tsconfig.e2e.json (167 bytes)
CREATE apps/myapp-e2e/tsconfig.json (137 bytes)
CREATE apps/myapp-e2e/src/fixtures/example.json (80 bytes)
CREATE apps/myapp-e2e/src/integration/app.spec.ts (402 bytes)
CREATE apps/myapp-e2e/src/plugins/index.js (832 bytes)
CREATE apps/myapp-e2e/src/support/app.po.ts (47 bytes)
CREATE apps/myapp-e2e/src/support/commands.ts (1068 bytes)
CREATE apps/myapp-e2e/src/support/index.ts (599 bytes)
UPDATE angular.json (4063 bytes)
UPDATE package.json (2005 bytes)
UPDATE nx.json (387 bytes)

✔ Packages installed successfully.

>  NX   NOTE  Because you selected an Angular-specific preset, we generated an Nx workspace powered by the Angular CLI.

  Run 'create-nx-workspace --help' to see how to select a different CLI.


———————————————————————————————————————————————


>  NX   NOTE  Nx CLI is not installed globally.

  This means that you might have to use "yarn nx" or "npm nx" to execute commands in the workspace.
  Run "yarn global add @nrwl/cli" or "npm install -g @nrwl/cli" to be able to execute command directly.


———————————————————————————————————————————————


>  NX   NOTE  First time using Nx? Check out this interactive Nx tutorial.

  https://nx.dev/angular/tutorial/01-create-application

  Prefer watching videos? Check out this free Nx course on YouTube.
  https://www.youtube.com/watch?v=2mYLe9Kp9VM&list=PLakNactNC1dH38AfqmwabvOszDmKriGco

</div></details>

ファイルツリー

myworkspace
.
├── .editorconfig
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode
│   └── extensions.json
├── README.md
├── angular.json
├── apps
│   ├── .gitkeep
│   ├── myapp
│   │   ├── browserslist
│   │   ├── jest.config.js
│   │   ├── src
│   │   │   ├── app
│   │   │   │   ├── app.component.css
│   │   │   │   ├── app.component.html
│   │   │   │   ├── app.component.spec.ts
│   │   │   │   ├── app.component.ts
│   │   │   │   └── app.module.ts
│   │   │   ├── assets
│   │   │   │   └── .gitkeep
│   │   │   ├── environments
│   │   │   │   ├── environment.prod.ts
│   │   │   │   └── environment.ts
│   │   │   ├── favicon.ico
│   │   │   ├── index.html
│   │   │   ├── main.ts
│   │   │   ├── polyfills.ts
│   │   │   ├── styles.css
│   │   │   └── test-setup.ts
│   │   ├── tsconfig.app.json
│   │   ├── tsconfig.json
│   │   ├── tsconfig.spec.json
│   │   └── tslint.json
│   └── myapp-e2e
│       ├── cypress.json
│       ├── src
│       │   ├── fixtures
│       │   │   └── example.json
│       │   ├── integration
│       │   │   └── app.spec.ts
│       │   ├── plugins
│       │   │   └── index.js
│       │   └── support
│       │       ├── app.po.ts
│       │       ├── commands.ts
│       │       └── index.ts
│       ├── tsconfig.e2e.json
│       ├── tsconfig.json
│       └── tslint.json
├── jest.config.js
├── libs
│   └── .gitkeep
├── node_modules
├── nx.json
├── package-lock.json
├── package.json
├── tools
│   ├── schematics
│   │   └── .gitkeep
│   └── tsconfig.tools.json
├── tsconfig.json
└── tslint.json

3. 既存の Angular アプリを Nx workspace に変換

生成過程

ng new myapp

<details><summary>実行中のログ</summary><div>

? Would you like to add Angular routing? 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                                             ]
  Stylus [ http://stylus-lang.com                                         ]
CREATE myapp/README.md (1022 bytes)
CREATE myapp/.editorconfig (246 bytes)
CREATE myapp/.gitignore (631 bytes)
CREATE myapp/angular.json (3559 bytes)
CREATE myapp/package.json (1282 bytes)
CREATE myapp/tsconfig.json (543 bytes)
CREATE myapp/tslint.json (1953 bytes)
CREATE myapp/browserslist (429 bytes)
CREATE myapp/karma.conf.js (1017 bytes)
CREATE myapp/tsconfig.app.json (210 bytes)
CREATE myapp/tsconfig.spec.json (270 bytes)
CREATE myapp/src/favicon.ico (948 bytes)
CREATE myapp/src/index.html (291 bytes)
CREATE myapp/src/main.ts (372 bytes)
CREATE myapp/src/polyfills.ts (2835 bytes)
CREATE myapp/src/styles.css (80 bytes)
CREATE myapp/src/test.ts (753 bytes)
CREATE myapp/src/assets/.gitkeep (0 bytes)
CREATE myapp/src/environments/environment.prod.ts (51 bytes)
CREATE myapp/src/environments/environment.ts (662 bytes)
CREATE myapp/src/app/app.module.ts (314 bytes)
CREATE myapp/src/app/app.component.css (0 bytes)
CREATE myapp/src/app/app.component.html (25723 bytes)
CREATE myapp/src/app/app.component.spec.ts (939 bytes)
CREATE myapp/src/app/app.component.ts (209 bytes)
CREATE myapp/e2e/protractor.conf.js (808 bytes)
CREATE myapp/e2e/tsconfig.json (214 bytes)
CREATE myapp/e2e/src/app.e2e-spec.ts (638 bytes)
CREATE myapp/e2e/src/app.po.ts (301 bytes)
✔ Packages installed successfully.

</div></details>

cd myapp
ng add @nrwl/workspace

<details><summary>実行中のログ</summary><div>

? Would you like to share anonymous usage data about this project with the Angular Team at
Google under Google’s Privacy Policy at https://policies.google.com/privacy? For more
details and how to change this setting, see http://angular.io/analytics. No
Skipping installation: Package already installed
    Renamed tsconfig.app.json -> apps/myapp/tsconfig.app.json
    Renamed karma.conf.js -> apps/myapp/karma.conf.js
    Renamed tsconfig.spec.json -> apps/myapp/tsconfig.spec.json
    Renamed src -> apps/myapp/src
    Renamed e2e -> apps/myapp-e2e
DELETE browserslist
DELETE tsconfig.app.json
DELETE tsconfig.spec.json
DELETE src/favicon.ico
DELETE src/index.html
DELETE src/main.ts
DELETE src/polyfills.ts
DELETE src/styles.css
DELETE src/test.ts
DELETE src/app/app.component.css
DELETE src/app/app.component.html
DELETE src/app/app.component.spec.ts
DELETE src/app/app.component.ts
DELETE src/app/app.module.ts
DELETE src/assets/.gitkeep
DELETE src/environments/environment.prod.ts
DELETE src/environments/environment.ts
DELETE e2e/protractor.conf.js
DELETE e2e/tsconfig.json
DELETE e2e/src/app.e2e-spec.ts
DELETE e2e/src/app.po.ts
CREATE apps/myapp/browserslist (429 bytes)
CREATE apps/myapp/tsconfig.app.json (219 bytes)
CREATE apps/myapp/karma.conf.js (1017 bytes)
CREATE apps/myapp/tsconfig.spec.json (278 bytes)
CREATE apps/myapp/src/favicon.ico (1642 bytes)
CREATE apps/myapp/src/index.html (291 bytes)
CREATE apps/myapp/src/main.ts (372 bytes)
CREATE apps/myapp/src/polyfills.ts (2835 bytes)
CREATE apps/myapp/src/styles.css (80 bytes)
CREATE apps/myapp/src/test.ts (753 bytes)
CREATE apps/myapp/src/app/app.component.css (0 bytes)
CREATE apps/myapp/src/app/app.component.html (25723 bytes)
CREATE apps/myapp/src/app/app.component.spec.ts (939 bytes)
CREATE apps/myapp/src/app/app.component.ts (209 bytes)
CREATE apps/myapp/src/app/app.module.ts (314 bytes)
CREATE apps/myapp/src/assets/.gitkeep (0 bytes)
CREATE apps/myapp/src/environments/environment.prod.ts (51 bytes)
CREATE apps/myapp/src/environments/environment.ts (662 bytes)
CREATE apps/myapp-e2e/protractor.conf.js (808 bytes)
CREATE apps/myapp-e2e/tsconfig.json (221 bytes)
CREATE apps/myapp-e2e/src/app.e2e-spec.ts (638 bytes)
CREATE apps/myapp-e2e/src/app.po.ts (301 bytes)
CREATE .prettierignore (57 bytes)
CREATE tools/tsconfig.tools.json (218 bytes)
CREATE tools/schematics/.gitkeep (0 bytes)
CREATE nx.json (280 bytes)
CREATE libs/.gitkeep (0 bytes)
CREATE .vscode/extensions.json (164 bytes)
CREATE .prettierrc (26 bytes)
UPDATE karma.conf.js (1012 bytes)
UPDATE package.json (2031 bytes)
UPDATE angular.json (4168 bytes)
UPDATE tslint.json (2261 bytes)
UPDATE tsconfig.json (579 bytes)
✔ Packages installed successfully.

</div></details>

ファイルツリー

ファイル移動後の、なにもファイルが入っていないディレクトリがノイズとして残るので除去します。

rm -rf ./e2e
rm -rf ./src
myapp
.
├── README.md
├── angular.json
├── apps
│   ├── myapp
│   │   ├── browserslist
│   │   ├── karma.conf.js
│   │   ├── src
│   │   │   ├── 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
│   └── myapp-e2e
│       ├── protractor.conf.js
│       ├── src
│       │   ├── app.e2e-spec.ts
│       │   └── app.po.ts
│       └── tsconfig.json
├── karma.conf.js
├── libs
├── node_modules
├── nx.json
├── package-lock.json
├── package.json
├── tools
│   ├── schematics
│   └── tsconfig.tools.json
├── tsconfig.json
└── tslint.json

結果を比較

ファイルツリー

ファイルツリーにもすでに違いがあるようです。1.を基準に「1.と 2.」「1.と 3.」の差分を確認します。

1.と 2.

こちらは差がありませんでした。生成されるファイル、そのディレクトリ構造はともに同一です。

1.と 3.

まぁまぁ差が出ました。

- │   ├── .gitkeep
- │   │   ├── jest.config.js
+ │   │   ├── karma.conf.js
- │   │   │   └── test-setup.ts
+ │   │   │   └── test.ts
- │   │   ├── tsconfig.json
- │   │   ├── tsconfig.spec.json
- │   │   └── tslint.json
+ │   │   └── tsconfig.spec.json
- │       ├── cypress.json
+ │       ├── protractor.conf.js
- │       │   ├── fixtures
- │       │   │   └── example.json
- │       │   ├── integration
- │       │   │   └── app.spec.ts
- │       │   ├── plugins
- │       │   │   └── index.js
- │       │   └── support
- │       │       ├── app.po.ts
- │       │       ├── commands.ts
- │       │       └── index.ts
- │       ├── tsconfig.e2e.json
- │       ├── tsconfig.json
- │       └── tslint.json
- ├── jest.config.js
+ │       │   ├── app.e2e-spec.ts
+ │       │   └── app.po.ts
+ │       └── tsconfig.json
+ ├── karma.conf.js

大きな違いはテストランナーJestKarmaの違い、E2E テストフレームワークCypressProtractorの違いです。つまりテスト環境全般については Angular ng newによる生成から変換したときと、最初から Nx で生成したときで大きく異なります。

  • ng new
    • Karma
    • Protractor
  • create-nx-workspace
    • Jest
    • Cypress

package.json

変化の大きそうな package.json についても見ておきます。

1.と 2.

diff a-nx-empty/myworkspace/package.json b-nx-angular/myworkspace/package.json
32c32
<     "@nrwl/angular": "^9.0.2",
---
>     "@nrwl/angular": "9.0.2",

非常に地味ですが^有無だけ差異ありで、内容自体は同一とみてよいでしょう。

1.と 3.

こちらに関しては差が大きすぎるので、一旦fixpackをかけます。

<details><summary>1.のpackage.json</summary><div>

package.json
{
  "name": "myworkspace",
  "version": "0.0.0",
  "dependencies": {
    "@angular/animations": "9.0.0",
    "@angular/common": "9.0.0",
    "@angular/compiler": "9.0.0",
    "@angular/core": "9.0.0",
    "@angular/forms": "9.0.0",
    "@angular/platform-browser": "9.0.0",
    "@angular/platform-browser-dynamic": "9.0.0",
    "@angular/router": "9.0.0",
    "@nrwl/angular": "^9.0.2",
    "core-js": "^2.5.4",
    "rxjs": "~6.5.0",
    "zone.js": "^0.10.2"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "0.900.1",
    "@angular/cli": "9.0.1",
    "@angular/compiler-cli": "9.0.0",
    "@angular/language-service": "9.0.0",
    "@nrwl/cypress": "9.0.2",
    "@nrwl/jest": "9.0.2",
    "@nrwl/workspace": "9.0.2",
    "@types/jest": "24.0.9",
    "@types/node": "~8.9.4",
    "codelyzer": "~5.0.1",
    "cypress": "^3.8.2",
    "dotenv": "6.2.0",
    "eslint": "6.1.0",
    "jest": "24.1.0",
    "jest-preset-angular": "8.0.0",
    "prettier": "1.18.2",
    "ts-jest": "24.0.0",
    "ts-node": "~7.0.0",
    "tslint": "~5.11.0",
    "typescript": "~3.7.4"
  },
  "license": "MIT",
  "private": true,
  "scripts": {
    "affected": "nx affected",
    "affected:apps": "nx affected:apps",
    "affected:build": "nx affected:build",
    "affected:dep-graph": "nx affected:dep-graph",
    "affected:e2e": "nx affected:e2e",
    "affected:libs": "nx affected:libs",
    "affected:lint": "nx affected:lint",
    "affected:test": "nx affected:test",
    "build": "ng build",
    "dep-graph": "nx dep-graph",
    "e2e": "ng e2e",
    "format": "nx format:write",
    "format:check": "nx format:check",
    "format:write": "nx format:write",
    "help": "nx help",
    "lint": "nx workspace-lint && ng lint",
    "ng": "ng",
    "nx": "nx",
    "postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
    "start": "ng serve",
    "test": "ng test",
    "update": "ng update @nrwl/workspace",
    "workspace-schematic": "nx workspace-schematic"
  }
}

</div></details>

<   "name": "myworkspace",
---
>   "name": "myapp",
5,16c5,16
<     "@angular/animations": "9.0.0",
<     "@angular/common": "9.0.0",
<     "@angular/compiler": "9.0.0",
<     "@angular/core": "9.0.0",
<     "@angular/forms": "9.0.0",
<     "@angular/platform-browser": "9.0.0",
<     "@angular/platform-browser-dynamic": "9.0.0",
<     "@angular/router": "9.0.0",
<     "@nrwl/angular": "^9.0.2",
<     "core-js": "^2.5.4",
<     "rxjs": "~6.5.0",
<     "zone.js": "^0.10.2"
---
>     "@angular/animations": "~9.0.2",
>     "@angular/common": "~9.0.2",
>     "@angular/compiler": "~9.0.2",
>     "@angular/core": "~9.0.2",
>     "@angular/forms": "~9.0.2",
>     "@angular/platform-browser": "~9.0.2",
>     "@angular/platform-browser-dynamic": "~9.0.2",
>     "@angular/router": "~9.0.2",
>     "@nrwl/angular": "9.0.2",
>     "rxjs": "~6.5.4",
>     "tslib": "^1.10.0",
>     "zone.js": "~0.10.2"
19,24c19,22
<     "@angular-devkit/build-angular": "0.900.1",
<     "@angular/cli": "9.0.1",
<     "@angular/compiler-cli": "9.0.0",
<     "@angular/language-service": "9.0.0",
<     "@nrwl/cypress": "9.0.2",
<     "@nrwl/jest": "9.0.2",
---
>     "@angular-devkit/build-angular": "~0.900.3",
>     "@angular/cli": "~9.0.3",
>     "@angular/compiler-cli": "~9.0.2",
>     "@angular/language-service": "~9.0.2",
26,33c24,34
<     "@types/jest": "24.0.9",
<     "@types/node": "~8.9.4",
<     "codelyzer": "~5.0.1",
<     "cypress": "^3.8.2",
<     "dotenv": "6.2.0",
<     "eslint": "6.1.0",
<     "jest": "24.1.0",
<     "jest-preset-angular": "8.0.0",
---
>     "@types/jasmine": "~3.5.0",
>     "@types/jasminewd2": "~2.0.3",
>     "@types/node": "^12.11.1",
>     "codelyzer": "^5.1.2",
>     "jasmine-core": "~3.5.0",
>     "jasmine-spec-reporter": "~4.2.1",
>     "karma": "~4.3.0",
>     "karma-chrome-launcher": "~3.1.0",
>     "karma-coverage-istanbul-reporter": "~2.1.0",
>     "karma-jasmine": "~2.0.1",
>     "karma-jasmine-html-reporter": "^1.4.2",
35,38c36,39
<     "ts-jest": "24.0.0",
<     "ts-node": "~7.0.0",
<     "tslint": "~5.11.0",
<     "typescript": "~3.7.4"
---
>     "protractor": "~5.4.3",
>     "ts-node": "~8.3.0",
>     "tslint": "~5.18.0",
>     "typescript": "~3.7.5"
40d40
<   "license": "MIT",
61d60
<     "postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
64a64
>     "update:check": "ng update",

やはり Jest, Karma, Protractor, Cypress 周りに差分が現れました。Angular CLI をベースにしている 3.は Jasmine に関する依存が残っているのも特徴です。

angular.json

Angular CLI にて Angular アプリケーションを管理する際の定義ファイルとなるangular.jsonについて確認します。

1.と 2.

まったく差分はありませんでした。

diff a-nx-empty/myworkspace/angular.json b-nx-angular/myworkspace/angular.json

1.と 3.

こちらは激しく差分が生じているので先にangular.json自体に Prettier を処理し改行や整形由来の差分を除外します。

diff a-fixed/angular.json c-fixed/angular.json
1a2
>   "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
2a4
>   "newProjectRoot": "",
9c11
<       "prefix": "myworkspace",
---
>       "prefix": "app",
71a74,85
>         "test": {
>           "builder": "@angular-devkit/build-angular:karma",
>           "options": {
>             "main": "apps/myapp/src/test.ts",
>             "polyfills": "apps/myapp/src/polyfills.ts",
>             "tsConfig": "apps/myapp/tsconfig.spec.json",
>             "karmaConfig": "apps/myapp/karma.conf.js",
>             "assets": ["apps/myapp/src/favicon.ico", "apps/myapp/src/assets"],
>             "styles": ["apps/myapp/src/styles.css"],
>             "scripts": []
>           }
>         },
79,88c93
<             "exclude": ["**/node_modules/**", "!apps/myapp/**"]
<           }
<         },
<         "test": {
<           "builder": "@nrwl/jest:jest",
<           "options": {
<             "jestConfig": "apps/myapp/jest.config.js",
<             "tsConfig": "apps/myapp/tsconfig.spec.json",
<             "passWithNoTests": true,
<             "setupFile": "apps/myapp/src/test-setup.ts"
---
>             "exclude": ["**/node_modules/**"]
94,95d98
<       "root": "apps/myapp-e2e",
<       "sourceRoot": "apps/myapp-e2e/src",
96a100
>       "root": "apps/myapp-e2e",
99c103
<           "builder": "@nrwl/cypress:cypress",
---
>           "builder": "@angular-devkit/build-angular:protractor",
101,102c105
<             "cypressConfig": "apps/myapp-e2e/cypress.json",
<             "tsConfig": "apps/myapp-e2e/tsconfig.e2e.json",
---
>             "protractorConfig": "apps/myapp-e2e/protractor.conf.js",
114,115c117,118
<             "tsConfig": ["apps/myapp-e2e/tsconfig.e2e.json"],
<             "exclude": ["**/node_modules/**", "!apps/myapp-e2e/**"]
---
>             "tsConfig": "apps/myapp-e2e/tsconfig.json",
>             "exclude": ["**/node_modules/**"]
120a124
>   "defaultProject": "myapp",
122d125
<     "defaultCollection": "@nrwl/angular",
124,134c127
<   },
<   "schematics": {
<     "@nrwl/angular:application": {
<       "unitTestRunner": "jest",
<       "e2eTestRunner": "cypress"
<     },
<     "@nrwl/angular:library": {
<       "unitTestRunner": "jest"
<     }
<   },
<   "defaultProject": "myapp"
---
>   }

案の定、テスト周りの設定が大きく異なります。"builder": "@nrwl/jest:jest", "builder": "@nrwl/cypress:cypress" が最初から入っているのはありがたいですね。

"prefix": "myworkspace"については、おそらく社名やサービス名を想定しているようなのですが、適宜変更することもあり得ると思います。

tsconfig.json

TypeScript の設定ファイルについても確認します。

1.と 2.

diff a-nx-empty/myworkspace/tsconfig.json b-nx-angular/myworkspace/tsconfig.json

差分はありません、安心です。

1.と 3.

Prettier 処理、プロパティのソート後について確認します。

diff a-fixed/tsconfig.json c-fixed/tsconfig.json
<     "emitDecoratorMetadata": true,
---
>     "downlevelIteration": true,
9c9
<     "lib": ["es2017", "dom"],
---
>     "lib": ["es2018", "dom"],
11a12
>     "outDir": "./dist/out-tsc",
14,15d14
<     "skipDefaultLibCheck": true,
<     "skipLibCheck": true,
20c19,22
<   "exclude": ["node_modules", "tmp"]
---
>   "angularCompilerOptions": {
>     "fullTemplateTypeCheck": true,
>     "strictInjectionParameters": true
>   }

3.のケースでemitDecoratorMetadataが無くなっているのが驚きで、Angular アプリケーション開発には必須オプションだと思っていただけに、別の関心が生まれましたが今回は深追いしないことにします。

angularCompilerOptionsについては 3.のケースでは生成されていますが、1.2.のケースでは生成されないので、必要に応じて追記がよいかと思います。

なお、すべてのケースでstrict: trueは表記されないので、こちらも忘れず追記する必要があります。

nx.json

最後に Nx のワークスペースの定義を記述するファイルについて確認します。

1.と 2.

diff a-nx-empty/myworkspace/nx.json b-nx-angular/myworkspace/nx.json

差分はありませんでした。これによって 1.と 2.の生成結果には違いがほぼないと言ってよさそうです。

1.と 3.

a-fixed/nx.json
{
  "npmScope": "myworkspace",
  "implicitDependencies": {
    "angular.json": "*",
    "package.json": {
      "dependencies": "*",
      "devDependencies": "*"
    },
    "tsconfig.json": "*",
    "tslint.json": "*",
    "nx.json": "*"
  },
  "projects": {
    "myapp": {
      "tags": []
    },
    "myapp-e2e": {
      "tags": [],
      "implicitDependencies": ["myapp"]
    }
  }
}
diff a-fixed/nx.json c-fixed/nx.json
2c2
<   "npmScope": "myworkspace",
---
>   "npmScope": "myapp",
5,8c5
<     "package.json": {
<       "dependencies": "*",
<       "devDependencies": "*"
<     },
---
>     "package.json": "*",
18,19c15
<       "tags": [],
<       "implicitDependencies": ["myapp"]
---
>       "tags": []

もともと 1.にてmyworkspaceとして作るか、3.にてmyappとして作るかの違いが反映されていますが、それ以外は大きな違いはないようでした。

総括

少々マニアックな記事になりましたが、業務案件での導入を検討する際、1.のケースと 3.のケースについて差を気にする方は多いと思いまとめを作成しました。

1.2.についてはどちらも同じようですが、ひとつ重要な違いとして

Would you like to configure routing for this application?

この質問を CLI 上でされるかどうかがあります。Routing に関する処理はあとからでも追加できますので、さほど大きな問題ではないですが、そこのひと手間を省略する場合は 1.の手法をおすすめします。

1.3.をどちらにするかは工数次第かなと思いますが、既存案件をなるべく 1.生成結果に馴染むようリファクタリングしたあと、1.で作った空ディレクトリに既存案件を配置する移行を進めたほうが、Nx の恩恵はより受けられるかと思います。特にテスト周りはゆくゆく Karma, Protractor などが不要となる場合、依存関係定義上のノイズとなり得るため、angular.jsonの生成結果の美しさを考慮すると、1.を基とした移行の方がよいかと思います。ここはこだわりが反映されそうです。

どのみち、CircleCI の設定ファイルなどの見直しのほうが影響大きいと思うので、迷いどころではありますね。サブパッケージの利用や Schematics の利用を本格的に進めている場合でなければ見送りでよいかと思いますが、Schematics, Storybook, Cypress, Jest などのサポートを魅力と捉える場合、工数次第では悪くないと感じます。

モノレポにすると何がうれしいか、Nx を導入すると何がうれしいかという観点はまったく触れなかった本稿ですが、何かの参考になれば。

それではまた。

Discussion