Skip to content
This repository was archived by the owner on Jul 29, 2024. It is now read-only.

Idea: Detect angular pages automatically #3859

Closed
sjelin opened this issue Dec 21, 2016 · 1 comment
Closed

Idea: Detect angular pages automatically #3859

sjelin opened this issue Dec 21, 2016 · 1 comment

Comments

@sjelin
Copy link
Contributor

sjelin commented Dec 21, 2016

Preface: This idea got uglier while researching the implementation details. I think #3858 is probably a better idea, but I'm making this issue anyway just to write everything down

We could replace browser.ignoreSynchronization with browser.synchronizationMode which would be either 'ALWAYS', 'NEVER', or 'DETECT' (I'm open to suggestions for different names). ALWAYS would be equivalent to browser.ignoreSynchronization = true, NEVER would be equivalent to browser.ignoreSynchronization = false, and DETECT would work as follows:

First, we replace testForAngular with the following logic:

function testForAngular(callback: (data: {ver?: number}) => void) {
  // Pretend we add `undefined` checks throughout this function

  function testForAngular_inner(callback: (data: {ver?: number}) => void) {
    if (looksLikeNg1Obj(window.angular) || systemJsHas('angular')) {
      callback({ver: 1});
    } else if (looksLikeModernNgObj(window.ng || window.angular) || hasTestabilityFunctions(window) || systemJsHas('@angular')) {
      callback({ver: 2});
    } else {
      waitForSystemJS(callback);
    }
  }

  function systemJsHas(packageName: string): boolean {
    let sysJs = window.SystemJS || window.System;
    // Pretend this for loop syntax works
    for (let name in (sysJs.map or _.invert(sysJs.map) or sysJs.defined or sysJs._loader.modules or sysJs._loader.importPromises)) {
      if ((new RegExp('(\/|^)' + packageName + '(\/|$)').test(packageName)) {
        return true;
      }
    }
    return false;
  }

  var importDone: {[url: string]: boolean} = {};
  function waitForSystemJS(callback: (data: {ver?: number}) => void) {
    let sysJs = window.SystemJS || window.System;
    let waitForImport = (promise: Promise<any>, url: string) => {
      importDone[url] = false;
      let onComplete = () => {
        importDone[url] = true;
        // If all promises are resolved, call testForAngular_inner() again
        for (let resolved of importResolved) {
          if (!resolved) {
            return;
          }
        }
        testForAngular_inner(callback);
      };
      promise.then(onComplete, onComplete);
    }
    let newPromises = false;
    for (let url in sysJs._loader.importPromises) {
      if (sysJs._loader.importPromises[url] && (importDone[url] === undefined)) {
        importResolved[url] = false;
        waitForImport(sysJs._loader.importPromises[url], url);
      }
    }
    if (!newPromises) {
      callback({}); // Give up, no angular
    }
  }

  testForAngular_inner(callback);
}

(back when I came up with this idea, I didn't realize what an issue SystemJS was going to be)

Now, in browser.get, we use testForAngular, and based on the result of that we decide if we want to do synchronization. Some pitfalls:

  • If navigation happened via something other than browser.get (e.g. someone clicks on a link), we wouldn't bootstrap and end up using an outdated synchronization setting from a previous page. However, Bootstrapping should (mostly) work with Browser-initiated navigation (e.g. clicking on links) #3857 should address that.
  • SystemJS.defined and SystemJS._loader are not public APIs
  • No support for other package managers
  • Eventually there will be native package managers, but maybe they'll block js execution like <script> tags do, or maybe there will be something analogous to DOMContentLoaded for them.
  • This could still fail if someone is accessing angular indirectly through another package which has angular hard-coded

Alternative ideas

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

1 participant