Saturday, December 2, 2017

Fixing RxJS import problems with Ionic's production build

Recently, I upgraded to Ionic 3.9.x, which included an upgrade to Angular 5.x as well. Everything went smooth, even I did not understand the RXJS stuff in Ionic's change log. But... when I did the first time a production build (ionic build --prod), I got a strange error:

Error: ./node_modules/rxjs/observable/BoundCallbackObservable.js Module build failed: TypeError: Cannot read property 'type' of undefined at Object.getEffectiveTypeAnnotationNode

Read more here: Fixing RxJS import problems with Ionic's production build.

Monday, May 1, 2017

Updating from Angular 2.4.8 to 4.0.0

Recently, I updated my component ionic-logging-service to the Angular version used by Ionic 3.0.0. After installing the new npm packages, I was able to build the component. But… tslint did no longer work. I got

.../ionic-logging-service/node_modules/tslint/lib/runner.js:92
    throw new Error(messages.join("\n"));
    ^

Error: Error at .../ionic-logging-service/node_modules/@angular/core/src/change_detection/differs/iterable_differs.d.ts:15:48: Cannot find name 'Iterable'.
  at Runner.run (.../ionic-logging-service/node_modules/tslint/lib/runner.js:92:27)
  at Object. (.../ionic-logging-service/node_modules/tslint/lib/tslint-cli.js:139:6)
  at Module._compile (module.js:409:26)
  at Object.Module._extensions..js (module.js:416:10)
  at Module.load (module.js:343:32)
  at Function.Module._load (module.js:300:12)
  at Module.require (module.js:353:17)
  at require (internal/module.js:12:17)
  at Object. (/Users/markus/Documents/Ritzlgrmft/ionic-logging-service/node_modules/tslint/bin/tslint:3:1)
  at Module._compile (module.js:409:26)
This happens due to Iterable, which is a es2015 feature. I solved it by adding the following to my tsconfig.json:

"compilerOptions": {
  "lib": [
    "dom",
    "es2015"
  ],
}

But now I got a lot of errors like

Error: Error at /Users/markus/Documents/Ritzlgrmft/ionic-logging-service/node_modules/@types/es6-shim/index.d.ts:6:14: Duplicate identifier 'PropertyKey’.

This happens, since es6-shim defines the same types as es2015 lib. Therefore I removed the package @types/es6-shim.

Now tslint was fine, again. But the karma tests failed. I got the error

Error: No provider for "framework:es6-shim"! (Resolving: framework:es6-shim)

This one was easy, since I uninstalled the package, I had to remove it from the frameworks. But now I got errors like

ERROR [compiler.karma-typescript]: node_modules/@angular/core/src/change_detection/differs/default_keyvalue_differ.d.ts(23,15): error TS2304: Cannot find name 'Map'. 
ERROR [compiler.karma-typescript]: node_modules/@angular/core/src/change_detection/differs/default_keyvalue_differ.d.ts(27,16): error TS2304: Cannot find name 'Map'.
ERROR [compiler.karma-typescript]: node_modules/@angular/core/src/change_detection/differs/iterable_differs.d.ts(15,48): error TS2304: Cannot find name 'Iterable'.

Again the problem are the es2015 features. Therefore I had to add the lib to the compilerOptions in karma.conf.js. However, this was not enough, since currently PhantomJS does not support the es2015 features. Therefore I had to add a polyfill to my files:

files: [
  { pattern:
      "src/**/*.+(ts|html)" },
      "node_modules/reflect-metadata/Reflect.js",
      "node_modules/babel-polyfill/dist/polyfill.js"
  }  
],

The used babel-polyfill was already there as a dependency of another package.

And finally, everything was working as before.

Saturday, March 25, 2017

karma-typescript: Invalid syntax in bundle: 'import' and 'export' may only appear at the top level

After upgrading some dependent modules, I got:
[Error: Invalid syntax in bundle: 'import' and 'export' may only appear at the top level (79532:186) in /var/folders/zk/6bvt6fls7g97ry69c8jykn_h0000gn/T/karma-typescript-bundle-62976QkhHVRO4DC9R.js]
Error: Invalid syntax in bundle: 'import' and 'export' may only appear at the top level (79532:186) in /var/folders/zk/6bvt6fls7g97ry69c8jykn_h0000gn/T/karma-typescript-bundle-62976QkhHVRO4DC9R.js
    at writeBundleFile (.../node_modules/karma-typescript/lib/bundler.js:171:23)
    at flushQueue (.../node_modules/karma-typescript/lib/bundler.js:99:9)
    at bundleQueuedModules (.../node_modules/karma-typescript/lib/bundler.js:94:9)
    at invokeFunc (.../node_modules/lodash.debounce/index.js:160:19)
    at trailingEdge (.../node_modules/lodash.debounce/index.js:207:14)
    at timerExpired [as _onTimeout] (.../node_modules/lodash.debounce/index.js:195:14)
    at Timer.listOnTimeout (timers.js:92:15)

The solution was to:
  • npm install karma-typescript@beta (I am using now 3.0.0-beta.2)
  • npm install karma-typescript-es6-transform@latest (I am using 1.0.0-beta.6)
  • add to karmaTypescriptConfig:
    bundlerOptions: {
      transforms: [
        require("karma-typescript-es6-transform")()
      ]
    }
    
For details see How to use es6 library.

Friday, March 24, 2017

Upgrading Angular dependencies to the versions used by Ionic 2.2

I provide a npm package, which contains a service thought for use in Ionic applications.
This service doesn't have any dependencies to Ionic itself, only to some Angular packages.
Since Ionic 2.2 uses newer versions of Angular (2.4.8 instead of 2.2.1), I updated the
Angular dependencies to the new versions. And then, ngc didn't work anymore. I got a strange error:
Error encountered resolving symbol values statically. Calling function 'NoOpAnimationDriver', function calls are not supported. Consider replacing the function or lambda with a reference to an exported function, resolving symbol AnimationDriver.NOOP in .../ionic-configuration-service/node_modules/@angular/platform-browser/src/dom/animation_driver.d.ts, resolving symbol BrowserTestingModule in .../ionic-configuration-service/node_modules/@angular/platform-browser/testing/browser.d.ts, resolving symbol BrowserTestingModule in .../ionic-configuration-service/node_modules/@angular/platform-browser/testing/browser.d.ts
The error message was not very helpful, also Google did not provide a lot of information.
I found only some posts from end of last year.

The solution was - finally - quite easy. I had to add a specific tsconfig.aot.json, which is used by ngc.
The differences to the original tsconfig.json are:
{
  "compilerOptions": {
    "module": "es2015"
  },
  "files": [
    "src/index.ts"
  ],
  "angularCompilerOptions": {
    "genDir": "aot"
  }
}
  • module es2015 is needed for tree shaking
  • files defines the entry point
  • genDir specifies where ngc stores its intermediate files
The last bit was to tell ngc to use the new configuration:
ngc -p tsconfig.aot.json
For a detailed explanation, just check the Angular Cookbook.

Tuesday, February 21, 2017

Ionic serve: watch failed: A watch configured to watch the following paths failed to start

Today I had the problem that every time, I called ionic serve, I got the following error:
...
[10:28:18]  build dev finished in 52.67 s
[10:28:22]  watch failed: A watch configured to watch the following paths failed to start. It likely that a file
            referenced does not exist:
            ...\src\**\*.(ts|html|s(c|a)ss)
[10:28:22]  dev server running: http://localhost:8100/
I found the error creation in node_modules\@ionic\app-scripts\dist\watch.js. The function startWatcher rejects the promise, if the start of watching needs more than 3 seconds. This is (roundabout) the timespan between "build dev finished" and "watch failed".

So I thought my machine might be too slow and increased the timeout value to 30 seconds:
[10:33:49]  build dev finished in 53.87 s
[10:34:09]  lint finished in 20.95 s
[10:34:19]  watch failed: A watch configured to watch the following paths failed to start. It likely that a file
            referenced does not exist: ...\src\assets\**\*,
            ...\src\index.html,
            ...\src\manifest.json,
            ...\src\service-worker.js,
            ...\node_modules\ionicons\dist\fonts\**\*,
            ...\node_modules\ionic-angular\fonts\**\*,
            ...\node_modules\ionic-angular\polyfills\polyfills.js,
            ...\node_modules\sw-toolbox\sw-toolbox.js,
            ...\environments\api-dwp\settings.json,
            ...\node_modules\leaflet\dist\leaflet.css
[10:34:19]  dev server running: http://localhost:8100/
As you can see, the error is the same, this time after 30 seconds, but there are more files reported. And indeed, the files ...\src\manifest.json and ...\src\service-worker.js do not exist. They are part of the default node_modules\@ionic\app-scripts\config\copy.config.js.

It seems, I have 2 possibilities now:
  • add the 2 files to my app
  • modify the copy.config.js

Since I have already a custom copy.config.js, and since I do not want to litter my source code with unneeded files, I used the second approach. In the copy.config.js, I had to override the key copyIndexContent (just remove '{{SRC}}/manifest.json' and '{{SRC}}/service-worker.js'):
copyIndexContent: {
  src: ['{{SRC}}/index.html'],
  dest: '{{WWW}}'
},
Now ionic serve didn't produce watch errors any more:
[11:19:40]  build dev finished in 50.56 s
[11:19:51]  watch ready in 62.29 s
[11:19:51]  dev server running: http://localhost:8100/
As you can see, the timespan for starting the watching is 11 seconds. So my modification of node_modules\@ionic\app-scripts\dist\watch.js is still needed.

Update 23-Feb-2017

Starting with version 1.1.4 of @ionic/app-scripts, there is a new option --startWatchTimeout to the ionic command. Instead of modifying the file node_modules\@ionic\app-scripts\dist\watch.js (which is not very reliable, anyway), it's enough to add the additional parameter: ionic serve --startWatchTimeout 30000.

See Issue 772.