Shim Configuration

94 views
Skip to first unread message

Richard Backhouse

unread,
Nov 17, 2012, 8:14:58 AM11/17/12
to amd-im...@googlegroups.com
I have recently spent some time adding shim configuration support to my Zazl AMD loader/optimizer. It follows the same API that James has in require.js.

What are peoples thoughts (James in particular) on making this an optional configuration in the AMD spec ?

Richard

James Burke

unread,
Nov 28, 2012, 1:48:33 PM11/28/12
to amd-im...@googlegroups.com
I changed the shim support slightly in requirejs 2.1:
https://github.com/jrburke/requirejs/wiki/Upgrading-to-RequireJS-2.1#wiki-breaking-shim

If that does not match what you do, then we should definitely talk
about it more.

For others on the list, this is how shim config works in requirejs,
including important optimization notes near the bottom:
http://requirejs.org/docs/api.html#config-shim

I'm open to adding it to
https://github.com/amdjs/amdjs-api/wiki/Common-Config -- the idea
behind that doc is that *if* a loader decides to provide a
functionality similar to one of those values, it should match what is
specified there, but none are required to be supported. Since you have
implemented shim, we have at least two implementations with it, so
that seems like the minimum bar for support, but we should talk
through it a bit more to make sure we are doing the same thing.

James

Richard Backhouse

unread,
Nov 29, 2012, 8:45:56 AM11/29/12
to amd-im...@googlegroups.com
I followed the requirejs 2.1 shim syntax and it worked out fine.

I'm definitely in favor of adding it to the Common-Config as an optional.

Richard

James Burke

unread,
Dec 10, 2012, 10:55:30 AM12/10/12
to amd-im...@googlegroups.com
I added shim config to the common config section (recall that all are optional):

https://github.com/amdjs/amdjs-api/wiki/Common-Config#wiki-shim

and I also added tests to the amdjs-tests section for pathsConfig,
packagesConfig, moduleConfig and shimConfig, all opt-in. However I
would appreciate someone else trying the tests, to make sure I did not
inadvertently bring over requirejs-isms into the tests. Scrubbing the
text for the shim config on the wiki also appreciated.

James

On Thu, Nov 29, 2012 at 5:45 AM, Richard Backhouse

Jakob Heuser

unread,
Dec 10, 2012, 10:55:25 PM12/10/12
to amd-im...@googlegroups.com
This is pretty awesome work James. I had a couple questions

  • Is there any reason that we should support exports and init in the shim? In theory a init of "init: function() { return some.thing }" would obviate the need for the exports parameter and save on the conversion of a string into an object path. Alternatively, since a lot of define() operates on an overloaded API, we could have either init or exports serve both roles.
  • On the section about jQuery plugins, the correct export is "technically" window.jQuery, right? This way you can just depend on the plugin. I suppose that is more of a best practices question.
  • I would include a few recipes (see: https://github.com/linkedin/inject/wiki/0.4.x-addRule-and-Your-Favorite-Library) this really helped people understand why shims were useful to have

James Burke

unread,
Dec 11, 2012, 1:00:07 PM12/11/12
to amd-im...@googlegroups.com
On Mon, Dec 10, 2012 at 7:55 PM, Jakob Heuser <ja...@felocity.com> wrote:
> Is there any reason that we should support exports and init in the shim? In
> theory a init of "init: function() { return some.thing }" would obviate the
> need for the exports parameter and save on the conversion of a string into
> an object path. Alternatively, since a lot of define() operates on an
> overloaded API, we could have either init or exports serve both roles.

I actually had something more similar to this in requirejs 2.0 but
modified it for 2.1 -- by always having exports as a string, it allows
the loader to check for a load failure in old IE. The init function
some folks wanted because it allowed returning a slightly modified
export, maybe doing a noConflict, but by also specifying the "exports"
string, the loader could verify the script loaded correctly and if
not, properly trigger errbacks.

The string was important because the checking of the exports value
could happen twice: once in the "did things load correctly" stage of
the loader, and then later to actually set the exports value. The
init() function however can modify state (noConflict() being the
worst) so running the init function twice in place of using an exports
string would cause problems.

I expect most folks to just use the string exports and not specify
init(). However, there was a bug report for a 2.1.x requirejs release
when I mandated always having an exports string about allowing init
even if the exports string was not there. They did not care about the
old IE handling in that case, and as I recall wanted to compose the
exports out of a few different globals.

That is some background on the forces involved, but I'm open to
something else if it satisfies those forces.

> On the section about jQuery plugins, the correct export is "technically"
> window.jQuery, right? This way you can just depend on the plugin. I suppose
> that is more of a best practices question.

For a jquery plugin, the deps value should just be "jquery" -- those
deps values should be module IDs. For the exports check, I would use
"jQuery.fn.pluginName", and not use "window" on the front.

This is more of my preference to avoid using "window" explicitly when
I can for something that is reflected in the global space. The global
space could be some environment, not "window". The jQuery source code
explicitly attaches to window and not the "this" value it could use
for the immediately invoked function it uses around its code, but the
user could be using something else to fulfill the "jquery" dependency
(like zepto) and those libs may do something different and may even
work in an env where window is not there.

So, no solid slam dunk on the reasoning, just wanting to avoid extra
typing and discouraging the use "window" as a global.

> I would include a few recipes (see:
> https://github.com/linkedin/inject/wiki/0.4.x-addRule-and-Your-Favorite-Library)
> this really helped people understand why shims were useful to have

Good suggestion. I did a quick pass at an example that includes
underscore, Backbone and a jquery plugin to the end of the section:
https://github.com/amdjs/amdjs-api/wiki/Common-Config#wiki-shim

feel free to modify if you think it should be fleshed out more.

James

Richard Backhouse

unread,
Dec 13, 2012, 7:04:47 AM12/13/12
to amd-im...@googlegroups.com
James,

The packagesConfig test has a reference to requirejs.isBrowser in
packages-test.js

config({
baseUrl: requirejs.isBrowser ? "./" : "./packages/",

Richard

Richard Backhouse

unread,
Dec 13, 2012, 7:14:02 AM12/13/12
to amd-im...@googlegroups.com
And I think there is a mistake in the configuration itself in packages-test.js

        {
            name: 'funky',
            main: 'index.js'
        },

Shouldn't it just be

        {
            name: 'funky',
            main: 'index'
        },

My loader does not expect the main property to have the ".js" suffix

Richard

James Burke

unread,
Dec 13, 2012, 4:20:54 PM12/13/12
to amd-im...@googlegroups.com
I fixed the requirejs.isBrowser reference.

On the main: "index.js", looking at the commonjs package spec, it says
"module id", and that is what our common config doc says too. However,
I have found in nodejs packages for it to be a mixed bag, some have a
'.js' on the end and some do not.

I suggest we allow a '.js' and update the common config doc to say
that if the main property has a .js, strip it off and treat it like a
local module reference. This way we can more directly consume package
config from those packages. What do you think?

James

On Thu, Dec 13, 2012 at 4:14 AM, Richard Backhouse

Richard Backhouse

unread,
Dec 13, 2012, 5:10:55 PM12/13/12
to amd-im...@googlegroups.com
That's fine, I'll add support to my loader to strip off the ".js" if
need be.

Richard

James Burke

unread,
Dec 13, 2012, 5:29:41 PM12/13/12
to amd-im...@googlegroups.com
I updated the common config doc with this information for "main":

* **main**: String. Optional. Default value is "main". The module
ID to use inside the package for the "main module". The value may have
a ".js" at the end of it. In that case, the ".js" should be ignored by
the loader. This is done to accommodate a package config style in use
by some node packages.

James

Richard Backhouse

unread,
Dec 13, 2012, 8:45:29 PM12/13/12
to amd-im...@googlegroups.com
I have a couple of comments/questions on the moduleConfig test. :

1) moduleConfig-test.js is configuring by 2 methods, one via the call to  "config()" and the other by passing a config object on the call to "go()". Is this intentional ? I hadn't seen anywhere yet where the spec talked about configuration overrides.

2). The "plain" module is expecting an empty object to be returned for its call to module.config(). I had assumed that if no config was specified for a given module undefined would be returned. Re-reading what the spec says currently I guess the statement " The Object value is mutable" indicates that all modules should have a config object. Perhaps the spec should say that an empty object is returned if none is configured.


Richard

On 12/10/12 10:55 AM, James Burke wrote:

James Burke

unread,
Dec 14, 2012, 3:23:39 AM12/14/12
to amd-im...@googlegroups.com
On Thu, Dec 13, 2012 at 5:45 PM, Richard Backhouse
<richarda...@gmail.com> wrote:
> 1) moduleConfig-test.js is configuring by 2 methods, one via the call to
> "config()" and the other by passing a config object on the call to "go()".
> Is this intentional ? I hadn't seen anywhere yet where the spec talked about
> configuration overrides.

Sorry, another requirejs-ism leaked in. I changed it to be one
config() call followed by a go() call that just does the module
loading.

> 2). The "plain" module is expecting an empty object to be returned for its
> call to module.config(). I had assumed that if no config was specified for a
> given module undefined would be returned. Re-reading what the spec says
> currently I guess the statement " The Object value is mutable" indicates
> that all modules should have a config object. Perhaps the spec should say
> that an empty object is returned if none is configured.

Good suggestion, updated. As I recall, Rawld asked for the mutability
and also think he suggested always returning an object when config()
is called. At least that is what I recall, what was going through my
head when I implemented.

James

Richard Backhouse

unread,
Dec 15, 2012, 6:42:25 AM12/15/12
to amd-im...@googlegroups.com
Looking at the shimConfig test I'm having a problem with one of the modules used.

Module b has a dependency on Module d. Module d does not have a shim config and is not an AMD module in that it contains a define call. Neither is it a dependency of any regular AMD modules. Because of that my code does not process it.

If a shimConfig is added, e.g

        'd': []

then I can deal with it.

Was it intentional to have it this way ? Your requirejs shimConfig page says this about this sort of thing :

" Only use other "shim" modules as dependencies for shimmed scripts, or AMD libraries that have no dependencies and call define() after they also create a global (like jQuery or lodash). "


Richard

On 12/10/12 10:55 AM, James Burke wrote:

James Burke

unread,
Dec 16, 2012, 11:09:08 PM12/16/12
to amd-im...@googlegroups.com
On Sat, Dec 15, 2012 at 3:42 AM, Richard Backhouse
<richarda...@gmail.com> wrote:
> Looking at the shimConfig test I'm having a problem with one of the modules
> used.
>
> Module b has a dependency on Module d. Module d does not have a shim config
> and is not an AMD module in that it contains a define call. Neither is it a
> dependency of any regular AMD modules. Because of that my code does not
> process it.
>
> If a shimConfig is added, e.g
>
> 'd': []
>
> then I can deal with it.
>
> Was it intentional to have it this way ? Your requirejs shimConfig page says
> this about this sort of thing :

My reasoning about shim config: it just specifies what scripts to load
before a certain script, and what global to use an exports value, if
any.

There are no modules that need an AMD exports value for d, so it did
not declare a shim config as it also does not have any dependencies.
The b module grabs the global d, since b is a shimmed script itself.

> " Only use other "shim" modules as dependencies for shimmed scripts, or AMD
> libraries that have no dependencies and call define() after they also create
> a global (like jQuery or lodash). "

That note was for people who were trying to use an amd script that has
dependencies, say an AMD version of backbone that needs to wait for
underscore and jquery to load before its factory function was called.
If an AMD lib with dependencies is used as a shim dependency, things
will break because shim just makes sure the scripts have been loaded
(the script load event has fired) and does not wait for a factory
function to run.

So maybe the disconnect is that for me, a shimmed script is considered
"loaded" once the script load event fires. It sounds like though you
are using some other indicator to know when "d" would be loaded? Maybe
you only bind a script load listener if there is a shim config?

James

Richard Backhouse

unread,
Dec 18, 2012, 1:31:04 PM12/18/12
to amd-im...@googlegroups.com
Ok, I understand the reasoning now. My loader has a server-side
component that is doing the dependency analysis. I have now made it
handle modules that don't contain a call to define or have a shim
configuration explicitly defined (which was probably an oversight anyways).

Thanks James, all the new tests run fine for me now.

Richard
Reply all
Reply to author
Forward
0 new messages