Search code examples
javascriptsystemjses6-modulescyclejs

Load CycleJS with SystemJS


I tried to load Cycle DOM from their CDN through SystemJS with something like:

System.config({
  map: {
    'cycle-dom': 'https://unpkg.com/@cycle/dom@17.1.0/dist/cycle-dom.js',
    'xstream': 'https://cdnjs.cloudflare.com/ajax/libs/xstream/10.3.0/xstream.min.js',
  }
});

System.import('cycle-dom', cycleDOM => {
    ...
});

But I quickly found out cycle-dom needs xstream. So I try to load both:

Promise.all([
  System.import('xstream'),
  System.import('cycle-dom')
])
.then(([xs, cycleDOM]) => {
  ...
});

But I still get the same error. It looks like cycle-dom is expecting xstream to exist on window when it's first loaded. So I tried:

System.import('xstream')
  .then(xs => window['xstream'] = xs)
  .then(() => System.import('cycle-dom'))
  .then(cycleDOM => {
    ...
  });

I feel like I'm going about this all wrong. How can I do this?

Update:

Following martin's advice below, I tried configuring xstream as a dependency of cycle-dom.

Here's a jsbin that demonstrates. What I'm doing is loading cycle-run and cycle-dom and then running the example off the cycle home page.

But I get the error:

"TypeError: Cannot read property 'default' of undefined"

Undefined in this case is cycle-dom trying to load window['xstream'], which isn't being loaded.

Thanks.


Solution

  • The System.import() call returns a Promise so you need to put the callback into its then() method (the second parameter is the parent name; not a callback).

    System.import('cycle-dom').then(function(cycleDOM) {
      console.log(cycleDOM);
    });
    

    This prints the module exports.

    I don't have any experience with cycle.js so I can't tell whether this is enough or not. Nonetheless you can set this package dependencies with meta config:

    System.config({
      map: {
        'cycle-dom': 'https://unpkg.com/@cycle/dom@17.1.0/dist/cycle-dom.js',
        'xstream': 'https://cdnjs.cloudflare.com/ajax/libs/xstream/10.3.0/xstream.min.js',
      },
      meta: {
        'cycle-dom': {
          deps: [
            'xstream'
          ]
        }
      }
    });
    

    Again, I don't know whether this is enough or not. The SystemJS documentation contains pretty well explained example how to load dependencies that need to register some global variables. See https://github.com/systemjs/systemjs/blob/master/docs/module-formats.md#shim-dependencies

    Edit:
    In this case it's a little more complicated. The cycle-run.js script is generated probably by browserify and you can see it contains a line as follows:

    var xstream_1 = (typeof window !== "undefined" ? window['xstream'] : typeof global !== "undefined" ? global['xstream'] : null);
    

    This checks whether window['xstream'] exists when it's loaded. This means that the xstream has to be loaded before loading the cycle-run.js script. The way SystemJS works is that it loads the requested module and then loads its dependencies (you can see the order in Developer Tools). So it's the opposite order than you need (this is very similar to my question on SystemJS GitHub page).

    This means you need to restructure the import calls:

    System.config({
      // ...
      meta: {
        'xstream': {
          format: 'global',
          exports: 'xstream',
        }
      }
    });
    
    System.import('xstream').then(function() {
      Promise.all([
        System.import('cycle-run'),
        System.import('cycle-dom'),
      ])
      .then(([cycle, cycleDOM]) => {
        // ...
      });
    });
    

    This registers the xstream before loading cycle-run. Also with the meta configuration for xstream this ensures that the window.xstream exists only inside these callbacks and doesn't leak to the global scope.

    See your updated demo: https://jsbin.com/qadezus/35/edit?js,output

    Also to use format and exports you need to use the newer SystemJS 0.20.* and not 0.19.*.