Search code examples
typescriptsystemjsrxjs5

Issue loading rxjs bundle with SystemJs


Versions

npm: 3.10.9
systemjs: 0.19.41
typescript: 1.8.10
rxjs: 5.0.3

Background

I have a project (that's written in typescript) that i'm trying to add rxjs to, but have come across an issue when loading the bundled file through systemjs.

SystemJs config

System.config({
    transpiler: 'typescript',
    paths: {
        'src:*': '/src/*',
        'npm:*': '/node_modules/*'
    },
    map: {
        'lib': 'src:lib',
        'rxjs': 'npm:rxjs/bundles/Rx.js',
        'typescript': 'npm:typescript/lib/typescript.js',
        'systemjs': 'npm:systemjs/dist/system.src.js'
    },
    packages: {
        lib: {
            defaultExtension: 'js'
        }
    }
});

Problem

Using the above config, i get the following error.

Error: (SystemJS) undefined is not a constructor (evaluating '__extends(UnsubscriptionError, _super)')

Which is caused by the fact that systemjs incorrectly defaults to amd format and the exporter function on line 245 of Rx.js never gets executed.

From the systemjs docs:

Module format detection

When the module format is not set, automatic regular-expression-based detection is used. This module format detection is never completely accurate, but caters well for the majority use cases.

Attempted solution

I presumed that explicitly setting the rxjs package format to global in the config would fix this issue.

System.config({
    transpiler: 'typescript',
    paths: {
        'src:*': '/src/*',
        'npm:*': '/node_modules/*'
    },
    map: {
        'lib': 'src:lib',
        'rxjs': 'npm:rxjs/bundles/Rx.js',
        'typescript': 'npm:typescript/lib/typescript.js',
        'systemjs': 'npm:systemjs/dist/system.src.js'
    },
    packages: {
        lib: {
            defaultExtension: 'js'
        },
        rxjs: {
            format: 'global'
        }
    }
});

Problem 2

Although this fixed the first issue, it created a second. As everywhere that i attempt to use the rxjs imports now throws an error, as they're undefined.

import {Observable} from 'rxjs'

export class SomeClass {

    ...
    
    private _isObservable(arg: any): boolean {
        return arg instanceof Observable;
    }
}

Uncaught TypeError: Right-hand side of 'instanceof' is not an object at SomeClass._isObservable

Debugging the transpiled code in the console reveals that although window.Rx gets correctly set to the Rx object, the rxjs_1_1 object that gets set on SomeClass, is not the global Rx object, but instead another object with the Rx global set as a property 'Rx' on it.

So rxjs_1.Rx.Observable works, but not of rxjs_1.Observable`.

(function(System, SystemJS) {System.register(['rxjs'], function(exports_1, context_1) {
"use strict";
var __moduleName = context_1 && context_1.id;
var rxjs_1;
var SomeClass;
return {
    setters:[
        function (rxjs_1_1) {
            rxjs_1 = rxjs_1_1;
        }],
    execute: function() {
        SomeClass = (function () {
            function SomeClass() {
            }
            ...
            SomeClass.prototype._isObservable = function (arg) {
                return arg instanceof rxjs_1.Observable;
            };
            return SomeClass;
        }());
        exports_1("SomeClass", SomeClass);
    }
}

});

Question

Any idea how i get it to pass in Rx object instead?


Solution

  • By adding 'Rx' as the meta.rxjs.exports value in system.config, it allows you to select the Rx object to be imported, instead of its parent. You can use dot notation to select an even deeper object if necessary.

    Not that you would want to, but just as an example, exports: 'Rx.Observable' would set the property rxjs_1_1 to Observable in my original questions transpiled code.

    So the full system.config would be:

    System.config({
        transpiler: 'typescript',
        paths: {
            'src:*': '/src/*',
            'npm:*': '/node_modules/*'
        },
        map: {
            'lib': 'src:lib',
            'rxjs': 'npm:rxjs/bundles/Rx.js',
            'typescript': 'npm:typescript/lib/typescript.js',
            'systemjs': 'npm:systemjs/dist/system.src.js'
        },
        packages: {
            lib: {
                defaultExtension: 'js'
            }
        },
        meta: {
            rxjs: {
                format: 'global',
                exports: 'Rx'
            }
        }
    });