Search code examples
ember.jssystemjs

Issue in loading ember app as child application in single-spa


I am trying to use ember as one of the child application with single spa(https://github.com/CanopyTax/single-spa), however I am unable to load the js files assets/vendor.js, assets/ember-app.js properly using systemJS.

Steps to reproduce issue:

1) Setting up single spa example with ember

git clone git@github.com:CanopyTax/single-spa-examples.git
cd single-spa-examples
cd src
ember new ember-app
cd ember-app
npm install

2) Add the skeleton single spa hooks methods for ember(not complete but should be ok for now)

//src/ember-app/public/assets/apa.app.js
export async function bootstrap() {
  return new Promise((resolve, reject) => {
    resolve();
  });
}

export async function mount() {
  return new Promise((resolve, reject) => {
    resolve();
  });
}

export async function unmount() {
  return new Promise((resolve, reject) => {
    resolve();
  });
}

function cleanupInspector() {
  return new Promise((resolve, reject) => {
    resolve();
  });
}

2a) Add placeholder div for ember app.

#in single-spa-example-master/index.html add
<div id="ember-app"></div>

2b) add rootElement property in ember environments

#in src/ember-app/config/environment.js
APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
      rootElement: '#ember-app'
    }

3) add build script for single spa(add bin/build-emberjs)

#!/usr/bin/env bash
set -e

cd src/ember-app
npm install
ember build
cd ..
cd ..
rm -rf build/ember-app
cp -a src/ember-app/dist build/ember-app

./node_modules/jspm/jspm.js build \
    src/ember-app/public/assets/spa.app.js \
    - common/colored-border.js \
    build/ember.app.js \
    --format amd \
    --source-map-contents \
    --skip-rollup \
    "$@"

4) add the build as part of the build-code

#!/usr/bin/env bash
set -e

rm -rf build
mkdir build

cp jspm_packages/system.src.js build/system.src.js
cp jspm_packages/system-polyfills.src.js build/system-polyfills.src.js

./bin/build-root
./bin/build-common-deps

./bin/build-home
./bin/build-navbar
./bin/build-angular1
./bin/build-react -p
./bin/build-angular2
./bin/build-vue
./bin/build-svelte
./bin/build-preact
./bin/build-vanillajs
./bin/build-inferno
./bin/build-emberjs

5) declare ember app as child application and run the app

//src/single-spa-examples.js
singleSpa.declareChildApplication('ember', ()=> {
    /**ISSUE: ember-app.js does not get loaded*/
    return SystemJS.import('/build/ember-app/assets/vendor.js').then(function() {
        return SystemJS.import('/build/ember-app/assets/ember-app.js');
    });

    /*
    return Promise.all([
        SystemJS.import('/build/ember-app/assets/vendor.js'),
        SystemJS.import('/build/ember-app/assets/ember-app.js')
    ])*/
}, hashPrefix('/ember'));

6) start the application

npm install
npm start

7) go to ember app at http://localhost:8080/#/ember

THE ISSUE:

1) vendor.js file loads but the ember-app.js file does not get loaded(can be verified in chrome network tab)

child-app-errors.js:12 Uncaught Error: 'ember' died in status LOADING_SOURCE_CODE: (SystemJS) Cannot read property 'Ember' of undefined
    TypeError: Cannot read property 'Ember' of undefined
        at eval (http://localhost:8080/build/ember-app/assets/vendor.js!transpiled:10200:38)
        at eval (http://localhost:8080/build/ember-app/assets/vendor.js!transpiled:10301:11)
        at execute (http://localhost:8080/build/ember-app/assets/vendor.js!transpiled:64708:9)
    Error loading http://localhost:8080/build/ember-app/assets/vendor.js
        at eval (http://localhost:8080/build/ember-app/assets/vendor.js!transpiled:10200:38)
        at eval (http://localhost:8080/build/ember-app/assets/vendor.js!transpiled:10301:11)
        at execute (http://localhost:8080/build/ember-app/assets/vendor.js!transpiled:64708:9)
    Error loading http://localhost:8080/build/ember-app/assets/vendor.js 

2) In step 5, if I replace the code with the commented version then I get following error(belive that its because the files should be loaded in specific order since "runningTests" is defined in vendor.js file):

child-app-errors.js:12 Uncaught Error: 'ember' died in status LOADING_SOURCE_CODE: (SystemJS) runningTests is not defined
    ReferenceError: runningTests is not defined
        at eval (http://localhost:8080/build/ember-app/assets/ember-app.js:294:1)
        at eval (http://localhost:8080/build/ember-app/assets/ember-app.js:299:3)
        at eval (<anonymous>)
        at tryCatchReject (http://localhost:8080/build/system-polyfills.src.js:1188:30)
        at runContinuation1 (http://localhost:8080/build/system-polyfills.src.js:1147:4)
        at Fulfilled.when (http://localhost:8080/build/system-polyfills.src.js:935:4)
    Evaluating http://localhost:8080/build/ember-app/assets/ember-app.js
    Error loading http://localhost:8080/build/ember-app/assets/ember-app.js
        at eval (http://localhost:8080/build/ember-app/assets/ember-app.js:294:1)
        at eval (http://localhost:8080/build/ember-app/assets/ember-app.js:299:3)
        at eval (<anonymous>)
        at tryCatchReject (http://localhost:8080/build/system-polyfills.src.js:1188:30)
        at runContinuation1 (http://localhost:8080/build/system-polyfills.src.js:1147:4)
        at Fulfilled.when (http://localhost:8080/build/system-polyfills.src.js:935:4)
    Evaluating http://localhost:8080/build/ember-app/assets/ember-app.js
    Error loading http://localhost:8080/build/ember-app/assets/ember-app.js

Solution

  • Thanks for the clear description and steps to reproduce! I was able to do all the steps and get exactly the same error that you reported.

    The tldr for what you are seeing is that SystemJS doesn't know how to execute the ember-app.js and vendor.js bundles correctly.

    The longer explanation: when SystemJS downloads the code for those bundles, it runs a regular expression on the code to see what format it is in. The vendor.js file has a combination of amd, cjs, and esm module syntax, but SystemJS decides to interpret the bundles as esm (es6) module syntax. It then transpiles the code into System.register format and forces the code to be executed in javascript strict mode with a "use strict" statement. Once in strict mode, the code runs with a different context (this), which is why this.Ember results in the error Cannot read property 'Ember' of undefined. Let me know if you want more detail on any of that or if it doesn't make sense. It took me a while to track down, but I believe I now understand it and I can give you more detail if you'd like.

    With that said, all of this is pretty deep in the bowels of SystemJS and pretty removed from how single-spa works.

    If you're wanting to just see what running Ember inside of a single-spa app looks like, I would check out https://github.com/CanopyTax/single-spa-examples/pull/37#pullrequestreview-53690825 for where I have worked around this issue. Also see https://github.com/CanopyTax/single-spa-examples/issues/33#issuecomment-319359153 and for related discussions.