ember-cli/ember-twiddle

View on GitHub
app/services/dependency-resolver.js

Summary

Maintainability
C
1 day
Test Coverage
/* eslint-disable no-console */
import $ from 'jquery';

import Service, { inject as service } from '@ember/service';
import { computed } from '@ember/object';
import RSVP from 'rsvp';
import Ember from 'ember';
import config from '../config/environment';
import { task, timeout } from 'ember-concurrency';
import compareVersions from 'compare-versions';

const {
  deprecate
} = Ember;

const EMBER_VERSIONS = ['3.18.1', '3.17.3', '3.16.7', '3.15.0', '3.14.3', '3.13.4', '3.12.2', '3.11.1', '3.10.2', '3.9.1', '3.8.3', '3.7.3', '3.6.1', '3.5.1', '3.4.3', '3.3.2', '3.2.2', '3.1.4', '3.0.0', '2.18.2', '2.17.2', '2.16.2'];
const EMBER_DATA_VERSIONS = ['3.18.0', '3.17.1', '3.16.5', '3.15.1', '3.14.1', '3.13.2', '3.12.5', '3.11.5', '3.10.0', '3.9.3', '3.8.1', '3.7.0', '3.6.0', '3.5.2', '3.4.2', '3.3.2', '3.2.2', '3.1.2', '3.0.4', '2.18.5', '2.17.1', '2.16.4'];

const VERSION_REGEX = /^\d+.\d+.\d+(-beta\.\d+)?$/;

const CDN_MAP = {
  'ember': {
    pakage: 'ember-source',
    library: 'ember.js',
    fileName: 'ember.debug.js'
  },

  'ember-template-compiler': {
    pakage: 'ember-source',
    library: 'ember.js',
    fileName: 'ember-template-compiler.js'
  },

  'ember-testing': {
    pakage: 'ember-source',
    library: 'ember.js',
    fileName: 'ember-testing.js'
  },

  'ember-data': {
    pakage: 'ember-data',
    library: 'ember-data.js',
    fileName: 'ember-data.js'
  }
};

const POLL_INTERVAL = 10000;

export default Service.extend({
  notify: service(),

  resolveDependencies(dependencies) {
    Object.keys(dependencies).forEach((name) => {
      let value = dependencies[name];

      dependencies[name] = this.resolveDependency(name, value);
    });
  },

  resolveAddons(addons, dependencies, emberVersion) {
    const taskInstance = this.resolveAddonsTask.perform(addons, dependencies, emberVersion);
    return taskInstance.then(() => taskInstance.value);
  },

  resolveAddonsTask: task(function *(addons, dependencies, emberVersion) {
    let done = false;
    while (!done) {
      let addonPromises = {};
      let addonNames = Object.keys(addons);
      for (let i = 0; i < addonNames.length; ++i) {
        const name = addonNames[i];
        const value = addons[name];
        addonPromises[name] = this.resolveAddon(name, value, emberVersion);
      }
      let allAddonsLoaded = true;
      try {
        let hash = yield RSVP.hash(addonPromises);
        for (let j = 0; j < addonNames.length; ++j) {
          const name = addonNames[j];
          const addon = hash[name];
          if (addon.status === 'build_success') {
            dependencies[name+'_js'] = addon.addon_js;
            dependencies[name+'_css'] = addon.addon_css;
            if (!Ember.testing) {
              console.log(`Addon ${name} is loaded...`);
            }
            delete addons[name];
          } else if (addon.status === 'building') {
            console.log(`Addon ${name} is still building...`);
            allAddonsLoaded = false;
          } else if (addon.status === 'build_error') {
            console.error(`Addon ${name} encountered a build error:`);
            if (addon.error_log) {
              console.error(addon.error_log);
            }
            allAddonsLoaded = false;
            throw addon;
          } else {
            console.error(addon);
          }
        }
      } catch(e) {
        try {
          this.notify.error(JSON.parse(e.responseText).errorMessage, {
            closeAfter: 10000
          });
        } catch(e2) {
          console.error(e);
        }
        throw e;
      }
      if (allAddonsLoaded) {
        done = true;
      } else {
        yield timeout(POLL_INTERVAL);
      }
    }
    return dependencies;
  }),

  resolveAddon(name, value, emberVersion) {
    const url = `${config.addonUrl}?ember_version=${emberVersion}&addon=${name}&addon_version=${value}`;
    return new RSVP.Promise(function(resolve) {
      $.getJSON(url).then(resolve);
    });
  },

  resolveDependency(name, value) {
    switch (name) {
      case 'ember':
      case 'ember-template-compiler':
      case 'ember-testing':
      case 'ember-data':
        return this.resolveEmberDependency(name, value);

      default:
        return value;
    }
  },

  resolveEmberDependency(name, value) {
    if (VERSION_REGEX.test(value)) {
      return this.cdnURL(name, value);
    }

    return value;
  },

  cdnURL(name, version) {
    let { pakage, library, fileName } = CDN_MAP[name];

    let deprecatedUrl = `//cdnjs.cloudflare.com/ajax/libs/${library}/${version}/${fileName}`;

    if (name === 'ember-data') {
      const msg = 'It is recommended you use ember-data as an addon';
      deprecate(msg, Ember.testing, {
        id: 'ember-twiddle.deprecate-ember-data-as-dependency',
        until: '0.16.0',
      });
      this.notify.warning(msg);

      return deprecatedUrl;
    }

    if (compareVersions(version, '2.16.0') === -1) {
      const msg = 'Versions of Ember prior to 2.16.0 are no longer supported in Ember Twiddle';
      deprecate(msg, Ember.testing, {
        id: 'ember-twiddle.deprecate-ember-versions-before-2-16',
        until: '0.17.0',
      });
      this.notify.warning(msg);

      return deprecatedUrl;
    }

    return `//cdn.jsdelivr.net/npm/${pakage}@${version}/dist/${fileName}`;
  },

  emberVersions: computed(function() {
    return EMBER_VERSIONS;
  }),

  emberDataVersions: computed(function() {
    return EMBER_DATA_VERSIONS;
  })
});