📝

codemodsを使って、Ionic Angularを自動的にStandalone構成に移行する

2023/12/20に公開

Ionic公式が提供している @ionic/angular-standalone-codemods を使って、Ionic Angularを自動的にStandalone構成に移行する方法についてご紹介します。なお、Ionic AngularのStandalone構成については、以下の記事をご参照ください。

https://zenn.dev/rdlabo/articles/8beb8c91e7d337

推奨する事前準備

@ionic/angular-standalone-codemods は、 ts-morph を使ってコード変換を行います。変換されるすべてのファイルは ts-morph を通して改行やインテントが変更されるため、変更箇所以外にも大きな差分がでます。そのため、コード整形ツールを使って、変更前後の差分を必要最小限にすることをおすすめします。以下はひとつの整形例です。

% npm install -D prettier
% npx prettier --parser typescript --write "./src/**/*.ts" &&  prettier --parser angular --write "./src/**/*.html"

もちろん、すでにPrettierを導入してコード整形をしている場合は、この手順は不要です。

@ionic/angular-standalone-codemodsとは

@ionic/angular-standalone-codemods は、Ionic公式が提供している、Ionic AngularをStandalone構成に移行するためのコード変換ツールです。AngularのCLIで提供されている ng update と同じように、Ionic AngularのStandalone構成に移行するためのコード変換を自動的に行ってくれます。

https://github.com/ionic-team/ionic-angular-standalone-codemods

実行するのはとても簡単で、以下をIonic Angularのプロジェクトルートフォルダで実行するだけです。

% npx @ionic/angular-standalone-codemods

実行すると、プロンプトが表示されます。まず表示されるのは以下の通り。

▲  ⚠️  This utility is experimental. Always review the changes made before committing them to your project. ⚠️
│
▲  For manual migration, see the guide at: https://www.ionicframework.com/docs/angular/build-options#migrating-from-modules-to-standalone

ただの確認ですね。自動的に大きな差分をつくりだすので、実行する前には必ず今までの変更をコミットするようにしてください。続いて、以下が表示されます。

◆  Would you like to run this migration as a dry run? No changes will be written to your project.
│  ● Yes / ○ No
└

ここでは、実際にコードの変更を実行するのか、変更内容をプレビューするだけ(dry run)なのかの確認が行われます。 Yes を選択すると、プレビューだけを行うことができます。実際にマイグレーションを行う場合は No を変更しましょう。

◆  Please enter the path to your project (default is the current working directory):
│  /Users/sakakibara/dev/winecode/app_

最後にプロジェクトパスを確認されます。デフォルトでは、カレントディレクトリが指定されていますが、プロジェクトのパスを指定することもできます。そして、エンターキーを押すと、実行されます。数秒で終了し、以下のメッセージが表示されます。

◇  Project migration at /Users/sakakibara/dev/winecode/app completed successfully.
│
◆  We recommend reviewing the changes made by this migration and formatting your code (e.g., with Prettier) before committing.

これで、Ionic AngularのStandalone構成に移行するためのコード変換が完了しました。Prettierを使って再度整形し、どう差分がでたかGitでみることをおすすめします。

% npx prettier --parser typescript --write "./src/**/*.ts" &&  prettier --parser angular --write "./src/**/*.html"

どういった変更が行われているか

0001-migrate-app-module

ひとつめの変更は、ルートのNgModuleでインポートしてる IonicModule.forRoot が、provideIonicAngularに置き換わります。これはAngularをStandalone構成に移行していないユーザのための変更です。

       import { NgModule } from '@angular/core';
-      import { IonicModule } from '@ionic/angular';
-
+      import { provideIonicAngular } from '@ionic/angular/standalone';
+  
       @NgModule({
-        imports: [IonicModule.forRoot({ mode: 'md' })]
+        imports: [],
+        providers: [provideIonicAngular({ mode: 'md' })]
       })
-      export class AppModule {}
+      export class AppModule { }

0002-import-standalone-componen

ふたつめの変更は、IonicのStandaloneコンポーネントをインポートするようになります。IonicModuleを使わなくなったためですね。

        import { Component } from "@angular/core";
+       import { IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem, IonLabel } from "@ionic/angular/standalone";

        @Component({
          selector: 'my-component',
...
               </ion-list>
             </ion-content>
           \`,
-          standalone: true
-        }) 
+          standalone: true,
+          imports: [IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem, IonLabel]
+        })
         export class MyComponent { }

また、IonIconが、 assets の中身を参照するのではなく、 addIcons で登録したアイコンを参照するようになったため、以下も変わりました。

         import { Component } from "@angular/core";
+        import { addIcons } from "ionicons";
+        import { logoIonic } from "ionicons/icons";
+        import { IonIcon } from "@ionic/angular/standalone";
 
         @Component({
           selector: 'my-component',
           template: '<ion-icon name="logo-ionic"></ion-icon>',
-          standalone: true
-        }) 
-        export class MyComponent { }
+          standalone: true,
+          imports: [IonIcon]
+        })
+        export class MyComponent {
+          constructor() {
+            addIcons({ logoIonic });
+          }
+        }

addIcons で登録されていないアイコンは表示されないことに注意が必要です。ただ、事前に他の場所(Window関数に格納されるので、 main.ts でも、他のコンポーネントでも、他のサービスでも)、登録されていれば表示することができます。ただ、当該コンポーネントで登録するのが一番総バンドルサイズは小さくなります。

0003-migrate-bootstrap-application / 0006-migrate-angular-app-config

3つめの変更は、bootstrapApplication でimportProvidersFromを使って読み込んでた IonicModule.forRoot が、provideIonicAngularに置き換わります。これは、AngularをStandalone構成に移行済のユーザ向けの変更です。

-    import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
+    import { IonicRouteStrategy, provideIonicAngular } from '@ionic/angular/standalone';
...
     bootstrapApplication(AppComponent, {
       providers: [
         { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
-        importProvidersFrom(IonicModule.forRoot({ mode: 'ios' })),
         provideRouter(routes),
+        provideIonicAngular({ mode: 'ios' })
       ],
     });

0004-migrate-import-statements

4つめの変更は、 @ionic/angular からのインポートが、すべて @ionic/angular/standalone からのインポートになります。 なお、移行後は、 @ionic/angular からのインポートは利用できません。 Moduleを読み込んでないので当然ですよね。

       import { Injectable } from '@angular/core';
-      import { ModalController } from '@ionic/angular';
+      import { ModalController } from '@ionic/angular/standalone';
 
       @Injectable()
       export class MyService {

0005-migrate-angular-json-assets

5つめの変更は、 angular.jsonnode_modules/ionicons/dist/ionicons/svg をコピーしてた部分が削除されます。これは、Ionic AngularのStandalone構成では、IonIconは addIcons 経由にBundleするようになり、 assets から削除されたためです。

           architect: {
             build: {
               options: {
-                assets: [
-                  "src/favicon.ico",
-                  "src/assets",
-                  {
-                    glob: "**/*.svg",
-                    input: "node_modules/ionicons/dist/ionicons/svg",
-                    output: "./svg",
-                  },
-                ],
+                assets: ["src/favicon.ico", "src/assets"],
               },
             },
           },

まとめ

Ionicチームは、 ng update に触発され、大きな変更がある度に(Capacitorのメジャーバージョンアップ含め)マイグレーションツールを提供しています。これらのツールを使うことで、Ionicのアップデートをより簡単に行うことができます。ぜひ、ご利用ください。

Discussion