3
votes

Imagine this project scaffold:

  • utils.js
  • module1/controllers.js
  • module1/services.js
  • module2/controllers.js
  • module2/services.js

utils.js

define([], function(){  /* My utils functions */ });

module1/controllers.js

define(['../utils'], function(utils){  /* Module1 stuff */ });

module2/controllers.js

define(['../utils'], function(utils){  /* Module2 stuff */ });

This works very well in a non-optimization context, because utils.js is downloaded only once, no matter whether is from module1 or module2. However I need to optimize and package each module in a JS file.

In order to get this, I add a main.js file in each module, for example:

module1/main.js

define(['./controllers', './services.js'], function(){});

Now I start playing with the optimization tool, this is my app.build.js

...
modules: [
        { name: "module1/main" },
        { name: "module2/main" },
    ]
...

Ok, I get the behaviour I wanted, but I see that both modules include the utils.js code. This behaviour is right because you can't guarantee the loading order.

So, these are my questions:

  1. Does exist any smart solution for this?
  2. If this can't be skipped, does anybody know how requirejs works under the hood? Could this strategy generate any kind of problems?
2

2 Answers

4
votes

Finally I got the answer using the exclude option in the build script:

...
modules:[
  { name: 'utils' },
  { name: 'module1/main', exclude: ['utils'] }
  { name: 'module2/main', exclude: ['utils'] }
]
...

This way I create a separated module for the utils and avoid to include it in the other modules code using the option "exclude". This method forces you to write this kind of "global" dependencies manually, but it works :)

1
votes

Not sure how you're 'playing' with the optimization tool, but here's how I handle it when I've got a base library (like all of my backbone extensions), and then a view or two:

Base library: something like 'base.js'

define([
'foo',
'bar',
'baz'
]);

Code for page x: let's say 'x.js'

define([
    'foo',
    'bar'
], function () {

});

Set up a 'libraryBuild.js' file and a 'build.js' file as appropriate - the difference between the two is in build.js, you'll set all of the files defined in 'base.js' to 'empty'.

libraryBuild.js

({
    baseUrl: ".",
    name: "base",
    out: "base-built.js"
})

build.js

({
    baseUrl: ".",
    paths: {
        foo: "empty:",
        bar: "empty:",
        baz: "empty:"
    }
})

Now, run the optimization twice (I have requirejs installed via npm as a global package, so I can just call r.js - otherwise it would be node ../path/to/r.js).

r.js -o libraryBuild.js

r.js -o build.js name=x out=x-optimized.js (note that you could instead apply this to the whole project instead of just an individual file.. but this is a better example for the moment)

You should notice that the "x-optimized.js" file does not include the code that was set up in base-built.js.

So, then in your pages...

<script>
require.config({
    paths: {
        base: "base-built"
    }
});
require(['base'], function () {
    require(['x'], function (x) {
        //The stuff you'd normally do when X is included
    })
});
</script>

I'm sure there's a better way of doing this, but this at least does work and keeps the file sizes down.