Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configuring RequireJS at runtime

Is it possible to modify the requirejs (or any AMD loader for that matter) config at runtime?

As an example, I have two log modules: one that simply wraps console.log, and other that logs to the server. I'd like modules to simply require log (require(['log'], function(log) { ... });) and not care which logger it's using.

I can set 'log' as a package in require's main config and this works fine. But what I'd rather do is set this option in main.js based on a few conditions. Something like:

if( ... ) {
    require.config({
        packages: [
            { name: 'log', location: 'app/base/utils/consolelog' }
        ]
    });
}

But this doesn't seem to work. Is there a way to do this, or am I pushing the capabilities of requirejs as a dependency injection framework?

Cheers

like image 762
nicholas Avatar asked Mar 06 '26 22:03

nicholas


1 Answers

The general instrumentation of any dependency injection framework is that you have one straightforward approach to configure what kind of a dependency container it will resolve at runtime and it is constructed only once and kept static through the lifespan of the application.

This is done specifically that you do not get into such cases that the dependencies can change at some point in the application. Having this notion that one or all of your application sources may need to beg for permission before they consume some or any dependency from the container instead of just going nom on the container is one awful idea that has lots of drawbacks. The main drawback being that you know, any of the references in accounts.all().each().withdraw(billing.MONTHLY_SERVICE_FEE).return() could otherwise behave differently at any point for no obvious reason. It would be bad if Accounts changed and new Accounts().all() didn't return any account, or if billing.MONTHLY_SERVICE_FEE changed all of a sudden and you charged some accounts with X amount and others with Y.

So, the short answer is no, you can't dynamically configure RequireJS. RequireJS will always load a module only once, so the callbacks return value is always a static object, or if you'd rather, when you look at the module sources you know that it's define() is going to be evaluated only once for the lifespan of the document.

The are several pretty simple approaches to how you abstract a particular implementation of an object at runtime. You simply create several modules with callbacks that return objects with an identical public interface, but with different behavior and then you either statically or dynamically resolve which one to return.

// core/logging/base.js
define(function() {
  var static;
  var base =  {
      _log: undefined,
      create: function(name) {},
      // ..
  }

  return base;
});

// core/logging/client.js
define(function(['core/logging/base'], function(base) {
  var client = extend(base, {_log: console.log});
  return client;
});

// core/logging.js
define(['core/logging/server', 'core/logging/client', 'core/conf/settings'], 
  function(server, client, environment) {
  if(settings.LOGGER === 'server') {
    return server;
  } else {
    return client;
  }
};

// app/foo.js
define(['core/logging'], function(logging) {
  var log = logging.createLog('foo');
  log.info('bar');
  // > INFO foo: bar
  // or...
  // POST http://server/log {type: 2, name: 'foo', message: 'bar'}
}

There are several approaches to how you can design such support, but this one appeals with it's simplicity and it works for your particular case. In general, you can perform this resolution statically in a module or dynamically in any block through a getter function and the module callbacks themselves could also return constructors or functions and not just objects.

like image 157
Filip Dupanović Avatar answered Mar 08 '26 11:03

Filip Dupanović



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!