Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditional Configuration #126

Closed
guybedford opened this issue Jun 21, 2014 · 17 comments
Closed

Conditional Configuration #126

guybedford opened this issue Jun 21, 2014 · 17 comments

Comments

@guybedford
Copy link
Member

This is a very rough shot in the dark to spur further discussion / ideas.

The argument against conditional loading within module syntax is that it is using incompatible syntax (#9).

The argument against upfront feature detection is that it requires manual configuration to set up (#9 (comment)).

We need some way for a package to specify its target environment, and then be dynamically given the environment it needs and in a modular way. The environment could just be the global state, or a combination of globals and locals during execution.

The environment itself can be a module name.

I was considering something along the lines of:

"uses environments/promises";
import { a } from 'b';

Where this meta gets picked up by the loader, and it first ensures that the environment has promises before going further. The environment setup itself is an asynchronous function such as:

environments/promises.js:

export default function() {
  if (!window.Promise)
    return System.import('promises-polyfill');
}

Promises is a bad example, but that is the rough idea.

We maintain modular configuration without obscuring the syntax. And environments that don't support this modular environment configuration won't notice a difference.

@guybedford
Copy link
Member Author

Perhaps we need to go back to the idea of package-level meta configuration, and have this meta specified at the package level.

@juandopazo
Copy link

Having this at the package level makes more sense to me too.

@guybedford
Copy link
Member Author

Thanks, yeah I'm currently playing with the idea of deprecating meta in favour of package config. This was something I implemented back in December but removed.... we all make mistakes!

@caridy
Copy link

caridy commented Jun 23, 2014

agreed. meta at the pkg level is the way to go here. Also, you should use Intl polyfill as a better example, since people might get confused with promises, because they are required by the loader anyways.

@guybedford guybedford changed the title Environment Configuration Conditional Configuration Jul 17, 2014
@guybedford
Copy link
Member Author

Here's a proposal for conditional package configuration (in combination with #141):

System.config({
  packages: {
    'my/package': {
      branches: { // branches or conditionals?
        'graphics': { // this refers to the 'my/package/graphics.js' module (may or may not actually exist)
          condition: 'platform', // this refers to a condition module, System.import('platform')
          map: { // this is the map value depending on the default result of the condition module
            'mobile': 'graphics/mobile', // use 'my/package/graphics/mobile.js'
            'tablet': 'graphics/tablet' // use 'my/package/graphics/tablet.js'
            // if no value is matched, we use the original module name
          }
        },
        'some/polyfill': {
          condition: './has-feature', // this is a polyfill check returning true or false. Module name is relative to package itself.
          map: '@empty' // boolean, so we only map if the condition returns true, in this case to an empty module (which is always defined in the registry in SystemJS. This way the polyfill at 'my/package/some/polyfill.js' is only loaded if needed).
        }
      }
    }
  }
});

platform.js

export default getPlatformType();

my/package/has-feature.js

export default checkNeedsPolyfill() ? true : false;

Feedback welcome. I'm pretty happy with the above.

@guybedford
Copy link
Member Author

One of the great features of the above, is it is possible to statically analyze all the branches in the conditional tree really easily, since we know what conditions lead to what outcomes without having to execute browser code.

@guybedford
Copy link
Member Author

Note that because this uses map config, we can conditionally map entire folders too, and not just modules.

@caridy
Copy link

caridy commented Jul 17, 2014

IMO, this is way too much magic. I think we should focus on the lower level, and let devs to build on top of that. The bit that is a no-go for me is the fact that you have to go and fetch the platform module which is the one doing the feature detection and exporting a default string value, in order to decide what other module to load, that's a huge perf penalty just to do feature detection. In our current implementation, we are using something like this:

System.trigger['foo'] = {
    test: !!window.XMLHttpRequest,
    replaceWith: 'bar'
};

that's very straight forward, and you could add as much complexity as you want if you make test member a function or a promise.

@caridy
Copy link

caridy commented Jul 17, 2014

/cc @juandopazo

@juandopazo
Copy link

I like the map config, but yes, I'd like to see an option not to define the test in a module. This is hard considering you're using strings for both the module name and the test result though.

@guybedford
Copy link
Member Author

What if we allowed both condition and conditionValue to be set, where conditionValue takes preference?

The reason I value having the condition set by a module is because it makes this into a true modular configuration (something that can be stored in JSON), as opposed to something that has to be manually configured.

@juandopazo
Copy link

I totally agree with pure JSON configuration being desirable. I see many users just defining the test module with System.define before including the configuration. We'd just have to see if this is too much computational cost for mobile browsers.

@guybedford
Copy link
Member Author

You're right - typically one would build in the conditional modules necessary for my code at the top of the bundle, and then create the bundle to include modules in the tree, stopping at these conditional branches.

Surely that is the ideal production situation (tracing all possible conditionals as module trees themselves), and the information is as simple as possible for this need?

If performance were an issue, we could write in the conditional modules as System.set calls even - System.set('some/condition', Reflect.Loader.newModule({ default: 'value' }));. Ideally we shouldn't make API decisions based on the performance of the loader now, but on how it can / should behave in future.

@guybedford
Copy link
Member Author

Any further thoughts on conditionals or packages please let me know... I'm keen to start implementing soon.

@guybedford
Copy link
Member Author

Perhaps we could also allow the main itself for a package to be a configuration object:

  System.config({
  packages: {
    'my/package': {
      main: 'index',
      mainBranch: {
        condition: 'platform',
        map: {
          'ie': 'main-ie',
          'safari': 'main-safari'
          'firefox': 'main-firefox'
        }
      }
    }
  }
});

Perhaps we still need a better name for some of these properties?

Perhaps we could simplify the entire process and only allow branching at the main entry point level?

That makes sub packages a little harder though.

@guybedford
Copy link
Member Author

I've implemented conditional configuration based roughly on these ideas at #189. feedback welcome.

@guybedford
Copy link
Member Author

#285 is now the primary proposal.

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

No branches or pull requests

3 participants