anodynos / uRequire

The Ultimate JavaScript Module Builder & Automagical Task Runner. Convert AMD & CommonJS/NodeJS modules to UMD, AMD, CommonJS or bundle them as `combined.js` (rjs & almond, AMDclean soon) & automagically run/test/watch them on nodejs, Web/AMD or Web/Script. Declarative & DRY config with inheritance. Manipulate & inject dependencies, module code, banners, version etc while building with a single line. Support two kinds of plugins, ResourceConverter (i.e file level) and AfterBuilder (i.e the whole bundle). Transparent support for Coffeescript, IcedCoffeescript, Coco, LiveScript – they’re just JavaScript :-)
http://anodynos.github.io/uRequire
MIT License
265 stars 20 forks source link

combined Template & Sub-Directories/Aliasing #51

Closed pvoisin closed 10 years ago

pvoisin commented 10 years ago

I have the following structure of source files:

- source/
    - library/
        - C.js
        - D.js
    - A.js
    - B.js

Let's say B depends on A which in turn requires C and D. When developing I use RequireJS and some configuration block like:

    require.config({
        baseUrl: "/source",
        paths: {
            "C": "library/C",
            "D": "library/D"
        }
    });

I'm willing to have A & B or whatever other module that requires lodash and/or EventEmitter find them in the global scope when running the standalone, combined version of the project. Here's the uRequire Grunt configuration block I'm using:


            standalone: {
                template: "combined",
                path: "source",
                filez: [
                    "library/*.js",
                    "*.js"
                ],
//*
                dependencies: {
                    exports: {
                        bundle: {
                            "lodash": "_",
                            "EventEmitter": "EventEmitter2"
                        }
                    }
                },
//*/
                dstPath: "distribution/Z-standalone.js"

What's problematic, and I may be missing something obvious, is C & D don't seem to be included in the bundle, even with filez containing those. I'm getting the following error:

Uncaught Error: uRequire detected missing dependency: 'C' - in a non-nodejs runtime. All it's binding variables were 'undefined'.

Having a look at Z-standalone.js I' reading things like (which are pretty close to what's done for global dependencies):

define('C',[],function () {
  if (__isNode) {
  return __nodeRequire('C');
} else {
  return (typeof C !== 'undefined') ? C : __nodeRequire('C')
}
});

How does it sound to you?


Note: issue initialy created here.

anodynos commented 10 years ago

As a note, you don't need to list all files / directories in your filez, it adds no value as its just matching / globbing. All you need you is a '**/*.js'. or a /./

The bad news is that uRequire has no 'aliases' per se. The good news is you have at least 4 solutions, one better than the other.

1) Note that all require('noPathDepName') are considered as global packages by uRequire, so when you have a Cvar = require('C') its creating the dummy define('C',... global dep & pseudo module as you correctly pointed out, and its expecting a Cvar global variable to be available. So for lodash its expecting a _ to be loaded already, in your case this it not happening for 'C' & 'D'. So you might want to load 'library/C.js' and export it to C global variable before loading your A.js, so the generated code above will use that 'C'. Same goes for 'D', if you have a require('D') in your A & B code.

2) I haven't tried it, but I guess you can have rjs-style aliased paths using the rjs config key that _.entends (i.e overwrites) whatever global (eg lodash, C) deps uRequire thinks you have.

To define the alias like this, use a rjs config fragment under rjs (in coffeescript):

  filez: /./
  rjs: paths: 
     "C": "library/C"
     "D": "library/D"
  ....

and then you can require('C') in your combined template (almond) build.

Note that this is NOT recommended, cause it will NOT work on other standalone file templates that know nothing about this rjs build setting.

3) The better solution is to export C as a bundle dependency (if you know you 'll need it hroughout your bundle) just like you do with lodash etc. So have a:

dependencies: 
      exports: 
          bundle: 
              "lodash": "_"
              "EventEmitter": "EventEmitter2"
              "C": "library/C"
              "D": "library/D"

This is applied to ALL templates gracefully and also you can save your self from typing C = require ('C'); at each module.

4) If you need C & D strictly in some parts of your app you should require by their real paths, i.e var C = require("library/C"). uRequire makes it easier since you dont need to have the relative-to-requiring file paths (like nodejs mandates) but you can have bundle-relative paths as well (they are translated at build).

I think your problem stems from the fact you really have TWO bundles, one depending on the other. Perhaps the best 5th bonus solution is to split the project in two bundles & builds.

I hope I 've covered you, let me know If I there's something I 've missed in your requirements.

pvoisin commented 10 years ago

Awesome! Thank yo very much for your time and efforts.

I finally succeeded with using the rjs: path hint which worked flawlessly!

I'm now having two dependent projects both written with AMD and distributable as UMD and standalone. With all those configuration options (which aren't that many finally) I can deal only with sources all the way while developing!

As for the present issue, feel free to close it since its author is happy now... :)

anodynos commented 10 years ago

Ok, glad I 've helped. But bear in mind that with the chosen solution your project(s) wont run as plain UMD / AMD templates, only as combined !