Dynamic requirejs configuration extending - javascript

I'm using requirejs for multipage project. Each page is an App. All of the apps have some common dependencies, i.e. jquery, backbone, underscore etc.
I want to move all this dependencies to the one single file.
That's how the js folder structure looks like:
js
base-app-require-configuration.coffee
app
homepeage
init.coffee
build.js
application.coffee
app1
init.coffee
build.js
application.coffee
app2
init.coffee
build.js
application.coffee
Homepage application example:
js/base-app-require-configuration.coffee
define ->
requirejs.config
urlArgs: "bust=#{ new Date().getTime() }"
# yep, tricky paths here
paths:
jquery: '../../jquery.min'
underscore: '../../underscore-min'
backbone: '../../backbone.min'
js/app/homepage/init.coffee
define [
'../../base-app-require-configuration'
], (
baseRequireConfig
) ->
requirejs.config
paths:
'jquery.alphanum': '../../jquery.alphanum'
shim:
'jquery.alphanum':
deps: ['jquery']
require [
'jquery'
'application'
], (
$
Application
) ->
$ -> new Application
js/app/homepage/build.js
({
mainConfigFile: ['../../base-app-require-configuration.js', 'init.js'],
wrapShim: 'true',
baseUrl: './',
name: 'init',
findNestedDependencies: true,
out: 'init.js'
})
My data-name is init.js
The thing works pretty well for multiple apps with the common dependencies moved to one sigle file - base-app-require-configuration.coffee, except one thing: the only way to compress/optimize this using r.js is to set the flag findNestedDependencies to true, because otherwise r.js won't see requirejs.config calls nested into define/require.
My questions are:
Is using findNestedDependencies a good practice?
Is there a prettier way to organize my dependencies without repeating?
If there is such a way - will it be compatible with r.js?

Let me share this solution with you.
I'm also looking for the similar solution with requirejs (how to organize the multipage project without repetitions of a long configuration, with a "shim" feature), and I have found the following one (I would be glad if this snippet can help you):
Inside HTML:
...
<script src="js/lib/require.js"></script>
<script>
//Load common code that includes config, then load the app
//logic for this page. Do the requirejs calls here instead of
//a separate file so after a build there are only 2 HTTP
//requests instead of three.
requirejs(['./js/common'], function (common) {
//js/common sets the baseUrl to be js/ so
//can just ask for 'app/main1' here instead
//of 'js/app/main1'
requirejs(['app/main1']);
});
</script>
...
where "common.js" contains the common configuration of requirejs for your project. This sample is from: https://github.com/requirejs/example-multipage-shim/blob/master/www/page1.html.
The full code of a sample project is here: https://github.com/requirejs/example-multipage-shim. The sample "build.js" file also providen, I see there is no necessity in "findNestedDependencies" in this case.
Sure, there is a bit more code inside HTML, but I think this is not significant downside.

Is using findNestedDependencies a good practice?
Not sure. the only thing i know, is that this option can slow down the bundling process quite a lot:
Is r.js dependency tracing significantly slower since v2.1.16? Or is it just me?
Is there a prettier way to organize my dependencies without repeating?
If there is such a way - will it be compatible with r.js?
this is a great article out organising backbone modules using r.js:
https://cdnjs.com/libraries/backbone.js/tutorials/organizing-backbone-using-modules

Related

RequireJS: optimizer generates name for define module

I'm using gulp.js and an optimization tool for requirejs (gulp-requirejs) it combines all scritps into one file. I have one define module with no name but it generates a name for it. The problem is I don't know how to call that module from another file.
For example in page1.js:
...
define("map",["jquery"],function($){
...
});
define("lib", ["jquery"],function($){
...
});
and in page2.js I would like to call page1.js lib module but I am not sure how to do it? I would prefer if the optimization tool did not set a name then my code works but this way I have no idea how to make it work. Any ideas?
It is not possible to use multiple modules in a single file, unless these modules are named. Otherwise RequireJS won't know how to relate a request for a module with the actual code that defines it.
The typical scenario when optimizing all modules into a single bundle is that there is going to be one specific module in the bundle which serves as the entry point of your application, and then you can just put that module name in paths:
require.config({
paths: {
lib: 'path/to/page1',
}
});
If you do not have a single entry point but may in fact also have code outside the bundle that will initiate loading modules that are in the bundle, then you need to list those modules in bundles:
require.config({
paths: {
lib: 'path/to/page1',
},
bundles: {
lib: ['map', ...],
}
});
The bundles setting I have shown above says essentially "when you look for the module named map, fetch the module lib, and you will have the definition of map."

RequireJS non-AMD files

I'm attempting to use LeafletJS with requireJS and some LeafletJS plugins. I know that if you are using files that aren't AMD modules, you need to shim them in the config. Does this mean I will have to shim each individual plugin with the LeafletJS dependency? Ex:
require.config({
paths: {
'leaflet': 'vendor/leaflet/leafletjs',
'leafletplugin1': 'vendor/leaflet/leafletplugin1',
'leafletplugin2': 'vendor/leaflet/leafletplugin2'
},
shim: {
'leafletplugin1': {
deps: 'leaflet'
},
'leafletplugin2': {
deps: 'leaflet'
}
}
});
The problem is that I plan to have many libraries with many plugins, and this config will get extremely long since I have to not only shim each individual plugin but also provide a path for each. Is there a simpler way to do this? It would be nice if I could even define a require.config inside of a module for use only with that module, that way I could keep my project better organized and less cluttered.
Also, there is no other way to simply require(['leafletplugin1'], function(){}); without shimming it, correct?
EDIT: Just to clarify, this doesn't have to be a LeafletJS-specific question/answer. This is just the current example I'm working with.
Option 1: depending on your build process and..
Number of plugs
Total size of plugins
would be concat all of the 'leaf' plugins into a single file.
Option 2: Turn all the plugins into proper modules.
This would mean having a ~/leaf directory and a ~/leafModule directory and having the build process "wrap" each plugin into a proper module.
In a similar situation, I did option 2. Was fairly easy and made the rest of my code far easier to work on.

How to prevent browserify-shim from requiring all shims?

I am using browserify and browserify-shim in a project, run through gulp using gulp-browserify.
gulp.src(['./resources/js/main.js'])
.pipe(browserify({
shim: {
angular: {
path: './node_modules/angular/angular.js',
exports: 'angular'
},
'angular-animate': {
path: './node_modules/angular-animate/angular-animate.js',
exports: 'ngAnimate',
depends: {
angular: 'angular',
jQuery: 'jQuery'
}
},
[...]
}
}))
.pipe(concat('app.js'))
.pipe(gulp.dest('./web/js'));
This setup works fine and, for most parts, as intended. However, Browserify will always include all shimmed libraries in the build, even if none of them is called by require().
The documentation seems to be non-existant on this topic. Is there a way to prevent this? It seems very counter-intuitive to me - the build should only contain what I actually require.
(Update: I installed angular and other libs using napa/npm)
When you shim with Browserify, it's making those libraries (and specifically the objects you tell it to "export") global. The convention is to still use require() for those libraries, however, doing so is just best practice so that if the library were to convert to module.exports down the road, you won't have to replace the global references in your code. Plus, it's nicer to list all of the files you require at the top in good node form. :)
So to answer your question, by shimming those libraries, you've told browserify to include them as global variables so they can be used anywhere, so they'll be included in the build automatically, regardless of whether you require() them.
If you want to include some and not others based on something like gulp.env, you could try building the options object separately and passing it into the browserify function.

AngularJS and Require: r.js gives undefined

I understand there's some debate about whether or not to use require.js with AngularJS, but I am doing so at this time. I have the whole project set up and running with require, and am now attempting to use r.js to optimize and minify.
After running r.js and changing my data-main in my index.html file, I am now getting that angular is undefined and cannot proceed.
I was able to reproduce the same behavior using the angular-require-seed. I followed the instructions on github to install and then made the following build definition for r.js.
build.js
({
baseUrl: "./app/js",
name: "main",
out: "index-built.js",
paths: {
angular: '../../bower_components/angular/angular',
'angular-scenario': '../../bower_components/angular-scenario/angular-scenario',
'angularRoute': '../../bower_components/angular-route/angular-route',
'angular-mocks': '../../bower_components/angular-mocks/angular-mocks'
}
})
I then ran r.js using the following command at the top level of the app.
r.js.cmd -o build.js
Once r.js completed I changed the data-main in index.html to be data-main="index-built.js"
This is once again resulting in angular being undefined. Can anyone point me to the error of my ways?
Are you remembering to shim the library?
If you are then you will also need to include that same config in your build.js. The best way to do this is point the mainConfigFile property at your main.js and let it pick up the config values from there (including the paths above too).

Optimize/build modules under parallel directories using the same config file in RequireJS

I have a feeling that the title just might not be explanatory :)
Setup
Suppose that I have the following structure:
where app.js files are main bootstrapping/entry modules for the applications that look like this:
app01
require.config({});
require([
'app/component1.js'
],
function(component){
// do something with component1
});
app02
require.config({});
require([
'app/component2.js'
],
function(component){
// do something with component2
});
which both work with appropriate index.html files.
I have a RequireJS build configuration file (assume correct placement related to the paths) for app01:
({
appDir: 'apps/app01',
baseUrl: '.',
dir: 'built_apps/app01',
optimize: 'closure',
paths: {
},
modules: [
{
name: 'app'
}
]
})
which works just fine. Similar file (replacing app01 with app02) works just fine for app02.
Problem/target
Now I want to be able to run RequireJS build tool (using Google Closure with Rhino under Ant, not that it really matters in this case) for both app01 and app02 applications using the same build configuration file and, preferably, without actually listing all the apps by name (since the number and names may vary over time).
Basically I expect (or rather hope) to have something like this:
({
appDir: 'apps',
baseUrl: '.',
dir: 'built_apps',
optimize: 'closure',
paths: {
},
modules: [
{
name: 'app*/app' // notice the wildcard
}
]
})
which would run over built_apps directory, find all apps under app*/app and optimize each one of them.
I know I can use Ant to create such build configuration file on the fly per app, run build against it and then clean up, but I'd rather have RequireJS solution.
Is there a way to do something like this with RequireJS?
There's no built-in wildcarding configuration for RequireJS. One way or another, you'll need code to do this. I'll observe that what you're asking for here amounts to translating a wildcard into some kind of implicit iteration on the module objects, akin to what mustache.js provides for its templates. IMO, that's a fairly brittle and limited approach in this context.
Instead, I recommend generating your modules array programmatically in JavaScript right in the build config. Recall, the build config is JavaScript not just a JSON data structure. This gives you sophisticated scripting capabilities there.
I've done this kind of scripting in the build config for the requirejs-rails gem. Here's an example gist that shows what r.js would see at build time.

Categories

Resources