L08084のブログ

技術記事の執筆は、祈りに近い

Ionic2からIonic3(Angular2.2 -> 5.2)へアップグレードした

f:id:l08084:20180403202712p:plain

アプリのIonicバージョンを2から3に上げた。

特に参考にしたサイト

forum.ionicframework.com

このサイトが特に参考になった。この記事のStep1とStep2と同じことを本記事でもやっている(Step3は飛ばした気がする…)。ただ、本記事では、トライアンドエラー方式でこのアップグレード作業を進めたので、参考記事とは作業の順番と内容が多少違っている。

他にも、Angularのアップグレード前のバージョンと、後のバージョンを入力すると、更新作業のガイドを出してくれる便利なサイトもあったので、紹介しておく。

Angular Update Guide - Beta

アップグレードの大まかな流れ

実装により違いは出るだろうが、Ionic 2 -> Ionic 3 へのアップグレード作業では、だいたい下記の手順が必要になる。ただ、前節でも述べたように、本記事では、修正→動作確認→エラー→修正 のトライアンドエラー方式で作業を進めたため、順不同であり、通常発生しないエラーも出ていると思われる。

  1. package.jsonの更新

  2. app.module.tsにBrowserModuleを追加する

  3. httpを使っている場合は、HttpClientModuleに切り替える

  4. index.htmlに<script src="build/vendor.js"></script>を追加する

バージョン情報

PC周りの環境

  • Node : v8.1.4
  • npm : 5.0.3
  • OS : macOS High Sierra

アプリのバージョン - アップグレード前

  • Ionic Framework : ionic-angular 2.0.0-rc.4
  • Angular: 2.2.1
  • @ionic/app-scripts : 1.1.4
  • @ionic/cli-utils : 1.19.2
  • ionic (Ionic CLI) : 3.20.0

アプリのバージョン - アップグレード後

  • Ionic Framework : ionic-angular 3.9.2
  • Angular: 5.2.9
  • @ionic/app-scripts : 3.1.8
  • @ionic/cli-utils : 1.19.2
  • ionic (Ionic CLI) : 3.20.0

package.jsonを更新する

まず、npm-check-updatesを使って、更新可能なライブラリのバージョンを全てあげていく。

npm-check-updatesを使う前に、Ionic CLIのionic infoコマンドでアプリにおける現在のIonicのバージョンを確認する。

$ ionic info

cli packages: (/usr/local/lib/node_modules)

    @ionic/cli-utils  : 1.19.2
    ionic (Ionic CLI) : 3.20.0

local packages:

    @ionic/app-scripts : 1.1.4
    Ionic Framework    : ionic-angular 2.0.0-rc.4

System:

    Node : v8.1.4
    npm  : 5.0.3
    OS   : macOS High Sierra

Misc:

    backend : legacy

まだ何もしていないので、Ionicのバージョンは2のままである。

続いて、ncuコマンドを実行して、アップグレード可能なライブラリをチェックする。なお、npm-check-updateをインストールしていない場合は、npm i -g npm-check-updateコマンドでインストールできる。

$ ncu
Using package.json
⸨░░░░░░░░░░░░░░░░░░⸩ ⠙ :
 @angular/common                         2.2.1  →   5.2.9
 @angular/compiler                       2.2.1  →   5.2.9
 @angular/compiler-cli                   2.2.1  →   5.2.9
 @angular/core                           2.2.1  →   5.2.9
 @angular/forms                          2.2.1  →   5.2.9
 @angular/http                           2.2.1  →   5.2.9
 @angular/platform-browser               2.2.1  →   5.2.9
 @angular/platform-browser-dynamic       2.2.1  →   5.2.9
 @angular/platform-server                2.2.1  →   5.2.9
 @ionic-native/core                    ^3.12.1  →  ^4.6.0
 @ionic-native/in-app-browser          ^3.12.1  →  ^4.6.0
 @ionic/storage                          1.1.7  →   2.1.3
 cordova-ios                             4.4.0  →   4.5.4
 cordova-plugin-device                  ^1.1.6  →  ^2.0.1
 cordova-plugin-geolocation             ^2.4.3  →  ^4.0.1
 cordova-plugin-inappbrowser            ^1.7.1  →  ^2.0.2
 cordova-plugin-splashscreen            ^4.0.3  →  ^5.0.2
 ionic-angular                      2.0.0-rc.4  →   3.9.2
 ionic-native                           2.2.11  →   2.9.0
 zone.js                                0.6.26  →  0.8.24
 @ionic/app-scripts                      1.1.4  →   3.1.8
 typescript                              2.0.9  →   2.8.1

The following dependencies are satisfied by their declared version range, but the installed versionsare behind. You can install the latest versions without modifying your package file by using npm update. If you want to update the dependencies in your package file anyway, run ncu -a.

 cordova-plugin-statusbar  ^2.2.3  →  ^2.4.1
 cordova-plugin-whitelist  ^1.3.2  →  ^1.3.3
 cordova-sqlite-storage    ^2.0.4  →  ^2.3.0
 rxjs                      ^5.0.1  →  ^5.5.8

Run ncu with -u to upgrade package.json

更新可能なライブラリのバージョンは全てあげたいので、ncu -uコマンドを実行する。これで、package.jsonが実際に更新される。

$ ncu -u
⸨░░░░░░░░░░░░░░░░░░⸩ ⠧ :
 @angular/common                         2.2.1  →   5.2.9
 @angular/compiler                       2.2.1  →   5.2.9
 @angular/compiler-cli                   2.2.1  →   5.2.9
 @angular/core                           2.2.1  →   5.2.9
 @angular/forms                          2.2.1  →   5.2.9
 @angular/http                           2.2.1  →   5.2.9
 @angular/platform-browser               2.2.1  →   5.2.9
 @angular/platform-browser-dynamic       2.2.1  →   5.2.9
 @angular/platform-server                2.2.1  →   5.2.9
 @ionic-native/core                    ^3.12.1  →  ^4.6.0
 @ionic-native/in-app-browser          ^3.12.1  →  ^4.6.0
 @ionic/storage                          1.1.7  →   2.1.3
 cordova-ios                             4.4.0  →   4.5.4
 cordova-plugin-device                  ^1.1.6  →  ^2.0.1
 cordova-plugin-geolocation             ^2.4.3  →  ^4.0.1
 cordova-plugin-inappbrowser            ^1.7.1  →  ^2.0.2
 cordova-plugin-splashscreen            ^4.0.3  →  ^5.0.2
 ionic-angular                      2.0.0-rc.4  →   3.9.2
 ionic-native                           2.2.11  →   2.9.0
 zone.js                                0.6.26  →  0.8.24
 @ionic/app-scripts                      1.1.4  →   3.1.8
 typescript                              2.0.9  →   2.8.1
 cordova-plugin-statusbar  ^2.2.3  →  ^2.4.1
 cordova-plugin-whitelist  ^1.3.2  →  ^1.3.3
 cordova-sqlite-storage    ^2.0.4  →  ^2.3.0
 rxjs                      ^5.0.1  →  ^5.5.8

この時点では、まだpackage.jsonしか更新されていないので、npm updateコマンドで最新バージョンのライブラリをインストールする。

$ npm update
npm WARN tsickle@0.27.2 requires a peer of typescript@>=2.4.2 <2.8 but none was installed.
npm WARN @angular/platform-server@5.2.9 requires a peer of @angular/animations@5.2.9 but none was installed.
npm WARN @ionic/app-scripts@1.1.4 requires a peer of @angular/tsc-wrapped@* but none was installed.
npm WARN @ionic/app-scripts@1.1.4 requires a peer of sw-toolbox@* but none was installed.

+ @angular/compiler-cli@5.2.9
+ @angular/core@5.2.9
+ @angular/common@5.2.9
+ cordova-ios@4.5.4
+ cordova-plugin-device@2.0.1
+ cordova-plugin-geolocation@4.0.1
+ cordova-plugin-inappbrowser@2.0.2
+ cordova-plugin-splashscreen@5.0.2
+ ionic-angular@3.9.2
+ ionic-native@2.9.0
+ @ionic-native/in-app-browser@4.6.0
+ zone.js@0.8.24
+ cordova-plugin-statusbar@2.4.1
+ @angular/forms@5.2.9
+ rxjs@5.5.8
+ @angular/compiler@5.2.9
+ @angular/platform-browser-dynamic@5.2.9
+ @angular/http@5.2.9
+ @ionic/storage@2.1.3
+ cordova-plugin-whitelist@1.3.3
+ cordova-sqlite-storage@2.3.0
+ @ionic-native/core@4.6.0
+ @angular/platform-server@5.2.9
+ @angular/platform-browser@5.2.9
added 16 packages, removed 19 packages and updated 68 packages in 15.598s

npm updateコマンド後に、ionic infoでアプリのIonicバージョンを確認すると、ionic-angular 2.0.0-rc.4からionic-angular 3.9.2に上がっていることがわかる。

$ ionic info

cli packages: (/usr/local/lib/node_modules)

    @ionic/cli-utils  : 1.19.2
    ionic (Ionic CLI) : 3.20.0

local packages:

    @ionic/app-scripts : 1.1.4
    Ionic Framework    : ionic-angular 3.9.2

System:

    Node : v8.1.4
    npm  : 5.0.3
    OS   : macOS High Sierra

Misc:

    backend : legacy

ブラウザで動作確認(1回目):エラー

この段階で一度ブラウザで動作確認をしてみた。Error: Cannot find module '@angular/tsc-wrapped/src/tsc'が発生して、失敗した。

$ ionic serve
Error: Cannot find module '@angular/tsc-wrapped/src/tsc'
    at Function.Module._resolveFilename (module.js:485:15)
    at Function.Module._load (module.js:437:25)
    at Module.require (module.js:513:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/node_modules/@ionic/app-scripts/dist/aot/aot-compiler.js:6:13)
    at Module._compile (module.js:569:30)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:503:32)
    at tryModuleLoad (module.js:466:12)
    at Function.Module._load (module.js:458:3)

エラーError: Cannot find module '@angular/tsc-wrapped/src/tsc'を回避するために、npm install @angular/tsc-wrapped --saveを実施した。

$ npm install @angular/tsc-wrapped --save
npm WARN tsickle@0.27.2 requires a peer of typescript@>=2.4.2 <2.8 but none was installed.
npm WARN @angular/platform-server@5.2.9 requires a peer of @angular/animations@5.2.9 but none was installed.
npm WARN @ionic/app-scripts@1.1.4 requires a peer of sw-toolbox@* but none was installed.

+ @angular/tsc-wrapped@4.4.6
added 5 packages in 4.94s

ブラウザで動作確認(2回目):エラー

再度、ionic serveコマンドでブラウザデバックを実施、別のエラーTypeError: ts.createNodeArray is not a functionが発生した。

$ ionic serve
TypeError: ts.createNodeArray is not a function
    at Object.<anonymous> (node_modules/@angular/compiler-cli/src/metadata/evaluator.js:661:16)
    at Module._compile (module.js:569:30)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:503:32)
    at tryModuleLoad (module.js:466:12)
    at Function.Module._load (module.js:458:3)
    at Module.require (module.js:513:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/node_modules/@angular/compiler-cli/src/metadata/collector.js:19:19)
    at Module._compile (module.js:569:30)

npm updateコマンドを実施した時に、npm WARN tsickle@0.27.2 requires a peer of typescript@>=2.4.2 <2.8 but none was installed.と怒られていたのを思い出したので、typescriptのバージョンを下げる。

$ npm i --save typescript@2.6.2
npm WARN @angular/platform-server@5.2.9 requires a peer of @angular/animations@5.2.9 but none was installed.
npm WARN @ionic/app-scripts@1.1.4 requires a peer of sw-toolbox@* but none was installed.

+ typescript@2.6.2
updated 1 package in 2.9s

ブラウザで動作確認(3回目):エラー

三度、ブラウザデバックを実施、2件のエラーが発生した。

エラー: 1

Class 'Subject<T>' incorrectly extends base class 'Observable<T>'. Types of property 'lift' are
            incompatible. Type '<R>(operator: Operator<T, R>) => Observable<T>' is not assignable totype '<R>(operator:
            Operator<T, R>) => Observable<R>'. Type 'Observable<T>' is not assignable to type 'Observable<R>'. Type 'T'
            is not assignable to type 'R'.

エラー: 2

Class 'WebSocketSubject<T>' incorrectly extends base class 'AnonymousSubject<T>'. Types of property 'lift'
            are incompatible. Type '<R>(operator: Operator<T, R>) => WebSocketSubject<R>' is not assignable to type
            '<R>(operator: Operator<T, R>) => Observable<T>'. Type 'WebSocketSubject<R>' is not assignable to type
            'Observable<T>'. Types of property 'operator' are incompatible. Type 'Operator<any, R>' is not assignable to
            type 'Operator<any, T>'. Type 'R' is not assignable to type 'T'.
$ ionic serve
Starting app-scripts server: --address 0.0.0.0 --port 8100 --livereload-port 35729 --dev-logger-port
53703 --nobrowser - Ctrl+C to cancel
[13:46:00]  watch started ...
[13:46:00]  build dev started ...
[13:46:00]  clean started ...
[13:46:00]  clean finished in 2 ms
[13:46:00]  copy started ...
[13:46:00]  transpile started ...
[13:46:04]  typescript: node_modules/ionic-native/node_modules/rxjs/Subject.d.ts, line: 16
            Class 'Subject<T>' incorrectly extends base class 'Observable<T>'. Types of property 'lift' are
            incompatible. Type '<R>(operator: Operator<T, R>) => Observable<T>' is not assignable totype '<R>(operator:
            Operator<T, R>) => Observable<R>'. Type 'Observable<T>' is not assignable to type 'Observable<R>'. Type 'T'
            is not assignable to type 'R'.

      L16:  export declare class Subject<T> extends Observable<T> implements ISubscription {
      L17:      observers: Observer<T>[];

[13:46:04]  typescript: node_modules/ionic-native/node_modules/rxjs/observable/dom/WebSocketSubject.d.ts, line: 23
            Class 'WebSocketSubject<T>' incorrectly extends base class 'AnonymousSubject<T>'. Types of property 'lift'
            are incompatible. Type '<R>(operator: Operator<T, R>) => WebSocketSubject<R>' is not assignable to type
            '<R>(operator: Operator<T, R>) => Observable<T>'. Type 'WebSocketSubject<R>' is not assignable to type
            'Observable<T>'. Types of property 'operator' are incompatible. Type 'Operator<any, R>' is not assignable to
            type 'Operator<any, T>'. Type 'R' is not assignable to type 'T'.

      L23:  export declare class WebSocketSubject<T> extends AnonymousSubject<T> {
      L24:      url: string;

[13:46:04]  transpile failed
[13:46:04]  dev server running: http://localhost:8100/

[OK] Development server running!
     Local: http://localhost:8100
     External: http://10.4.71.18:8100
     DevApp: TSCGuide@8100 on MacBook-Pro.local

[13:46:04]  copy finished in 4.32 s
[13:46:04]  watch ready in 4.35 s

このサイトを参考に、tsconfing.jsonに"compilerOptions":{"skipLibCheck": true}を追加することによって、エラーを回避した。

"compilerOptions":{

"skipLibCheck": true

}

ブラウザで動作確認(4回目):エラー

4度目のブラウザでの動作確認を実施

$ ionic serve
Starting app-scripts server: --address 0.0.0.0 --port 8100 --livereload-port 35729 --dev-logger-port
53703 --nobrowser - Ctrl+C to cancel
[14:04:00]  watch started ...
[14:04:00]  build dev started ...
[14:04:00]  clean started ...
[14:04:00]  clean finished in 1 ms
[14:04:00]  copy started ...
[14:04:00]  transpile started ...
[14:04:02]  transpile finished in 2.33 s
[14:04:02]  preprocess started ...
[14:04:02]  preprocess finished in less than 1 ms
[14:04:02]  webpack started ...
[14:04:02]  copy finished in 2.46 s
[14:04:09]  webpack finished in 6.65 s
[14:04:09]  sass started ...
[14:04:10]  sass finished in 1.25 s
[14:04:10]  postprocess started ...
[14:04:10]  postprocess finished in less than 1 ms
[14:04:10]  lint started ...
[14:04:10]  build dev finished in 10.26 s
[14:04:10]  watch ready in 10.30 s
[14:04:10]  dev server running: http://localhost:8100/

[OK] Development server running!

[14:04:12]  lint finished in 2.51 s

ランタイムエラーCannot find module "localforage"が発生した。

Runtime Error
Cannot find module "localforage"
Stack
Error: Cannot find module "localforage"
    at Object.<anonymous> (http://localhost:8100/build/main.js:152241:7)
    at __webpack_require__ (http://localhost:8100/build/main.js:20:30)
    at Object.<anonymous> (http://localhost:8100/build/main.js:44047:67)
    at __webpack_require__ (http://localhost:8100/build/main.js:20:30)
    at Object.<anonymous> (http://localhost:8100/build/main.js:37686:73)
    at __webpack_require__ (http://localhost:8100/build/main.js:20:30)
    at Object.<anonymous> (http://localhost:8100/build/main.js:151732:80)
    at __webpack_require__ (http://localhost:8100/build/main.js:20:30)
    at Object.<anonymous> (http://localhost:8100/build/main.js:113027:73)
    at __webpack_require__ (http://localhost:8100/build/main.js:20:30)

このサイトを参考に、ライブラリionic-storageのアンインストールとインストールを実施することによって、エラーを回避した。

npm uninstall --save @ionic/storage
npm install --save @ionic/storage

ブラウザで動作確認(5回目):エラー

ブラウザで動作確認、エラーCan't resolve all parameters for Storage: (?).が発生した。ionic-storageのバージョンが上がり、使い方が変わったことが原因となる。

Runtime Error
Can't resolve all parameters for Storage: (?).

Stack
Error: Can't resolve all parameters for Storage: (?).
    at syntaxError (http://localhost:8100/build/main.js:113815:34)
    at CompileMetadataResolver._getDependenciesMetadata (http://localhost:8100/build/main.js:129030:35)
    at CompileMetadataResolver._getTypeMetadata (http://localhost:8100/build/main.js:128865:26)
    at CompileMetadataResolver._getInjectableMetadata (http://localhost:8100/build/main.js:128845:21)
    at CompileMetadataResolver.getProviderMetadata (http://localhost:8100/build/main.js:129205:40)
    at http://localhost:8100/build/main.js:129116:49
    at Array.forEach (<anonymous>)
    at CompileMetadataResolver._getProvidersMetadata (http://localhost:8100/build/main.js:129076:19)
    at CompileMetadataResolver.getNgModuleMetadata (http://localhost:8100/build/main.js:128644:50)
    at JitCompiler._loadModules (http://localhost:8100/build/main.js:147735:87)

このサイトの「Update Steps:」の通りに、app.module.tsのionic-storageの設定を下記の通り変更する。

  1. インポートをimport { IonicStorageModule } from '@ionic/storage'に変更する

  2. IonicStorageModule.forRoot()imports: []配列に追加する

修正前のsrc/app/app.module.ts:

// ...省略
import { Storage } from '@ionic/storage';
// ...省略

@NgModule({
  // ...省略
  providers: [
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    Storage,
    InAppBrowser
  ]
})
export class AppModule {}

修正後のsrc/app/app.module.ts:

// ...省略
import { IonicStorageModule } from '@ionic/storage';
// ...省略

@NgModule({
  // ...省略
  imports: [IonicModule.forRoot(MyApp), IonicStorageModule.forRoot()],
  // ...省略
  providers: [
    { provide: ErrorHandler, useClass: IonicErrorHandler },
    InAppBrowser
  ]
})
export class AppModule {}

ブラウザで動作確認(6回目):エラー

ブラウザで動作確認して、StaticInjectorError(AppModule)[ApplicationInitStatus]: StaticInjectorError(Platform: core)[ApplicationInitStatus]: NullInjectorError: No provider for ApplicationInitStatus!エラーが発生した。

Runtime Error
StaticInjectorError(AppModule)[ApplicationInitStatus]: StaticInjectorError(Platform: core)[ApplicationInitStatus]: NullInjectorError: No provider for ApplicationInitStatus!
Stack
Error: StaticInjectorError(AppModule)[ApplicationInitStatus]: 
  StaticInjectorError(Platform: core)[ApplicationInitStatus]: 
    NullInjectorError: No provider for ApplicationInitStatus!
    at _NullInjector.get (http://localhost:8100/build/main.js:1606:19)
    at resolveToken (http://localhost:8100/build/main.js:1904:24)
    at tryResolveToken (http://localhost:8100/build/main.js:1846:16)
    at StaticInjector.get (http://localhost:8100/build/main.js:1714:20)
    at resolveToken (http://localhost:8100/build/main.js:1904:24)
    at tryResolveToken (http://localhost:8100/build/main.js:1846:16)
    at StaticInjector.get (http://localhost:8100/build/main.js:1714:20)
    at resolveNgModuleDep (http://localhost:8100/build/main.js:11458:25)
    at NgModuleRef_.get (http://localhost:8100/build/main.js:12691:16)
    at http://localhost:8100/build/main.js:6103:70

app.module.tsにBrowserModuleを追加する

このサイトを参考に、app.module.tsにBrowserModuleを追加したら、エラーが出なくなった。

src/app/app.module.ts:

import { NgModule, ErrorHandler } from '@angular/core';
// add this!
import { BrowserModule } from '@angular/platform-browser';
// ... 省略

@NgModule({
  declarations: [
  // ... 省略
  imports: [
    IonicModule.forRoot(MyApp),
    IonicStorageModule.forRoot(),
    // add this !
    BrowserModule,
    HttpClientModule
  ],
  // ... 省略
export class AppModule {}

ブラウザで動作確認(7回目):エラー

NullInjectorError: No provider for Http!エラーが発生した。

Runtime Error
StaticInjectorError(AppModule)[XXXService -> Http]: StaticInjectorError(Platform: core)[XXXService -> Http]: NullInjectorError: No provider for Http!
Stack
Error: StaticInjectorError(AppModule)[XXXService -> Http]: 
  StaticInjectorError(Platform: core)[XXXService -> Http]: 
    NullInjectorError: No provider for Http!
    at _NullInjector.get (http://localhost:8100/build/main.js:1606:19)
    at resolveToken (http://localhost:8100/build/main.js:1904:24)
    at tryResolveToken (http://localhost:8100/build/main.js:1846:16)
    at StaticInjector.get (http://localhost:8100/build/main.js:1714:20)
    at resolveToken (http://localhost:8100/build/main.js:1904:24)
    at tryResolveToken (http://localhost:8100/build/main.js:1846:16)
    at StaticInjector.get (http://localhost:8100/build/main.js:1714:20)
    at resolveNgModuleDep (http://localhost:8100/build/main.js:11458:25)
    at NgModuleRef_.get (http://localhost:8100/build/main.js:12691:16)
    at resolveDep (http://localhost:8100/build/main.js:13181:45)

HttpClientModuleに切り替える

Angularの古いタイプのhttpを使っていることが原因だったので、HttpClientModuleに切り替えたら、エラーは解決した。

ブラウザで動作確認(8回目):エラー

webpackJsonp is not definedエラーが発生した。

Error
Close
Runtime Error
webpackJsonp is not defined
Stack
ReferenceError: webpackJsonp is not defined
    at http://localhost:8100/build/main.js:1:1
Ionic Framework: ^3.9.2
Ionic Native: ^2.9.0
Ionic App Scripts: 3.1.8
Angular Core: ^5.2.9
Angular Compiler CLI: ^5.2.9
Node: 8.1.4
OS Platform: macOS High Sierra
Navigator Platform: MacIntel
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36

index.htmlに<script src="build/vendor.js"></script>を追加する

<script src="build/vendor.js"></script><script src="build/main.js"></script>の前に追加するとエラーは消える。

src/index.html:

<!-- ... 省略 -->
<body>

  <!-- Ionic's root component and where the app will load -->
  <ion-app></ion-app>

  <script src="cordova.js"></script>

  <!-- The polyfills js is generated during the build process -->
  <script src="build/polyfills.js"></script>

  <!-- add this! -->
  <script src="build/vendor.js"></script>

  <!-- The bundle js is generated during the build process -->
  <script src="build/main.js"></script>

</body>
<!-- ... 省略 -->

上記の対応をした段階で、全てのエラーが解決し、動作確認に成功した。