Consolidate RequireJS Shim definition across multiple configurations - javascript

I am using RequireJS in a large application. I also make use of the optimization tool to optimize/minify my code.
I have my data-main file, Main.js. In this file, I define a set of shims:
require.config({
baseUrl: "js",
shim: {
"libs/shimmedLibrary": {
deps: ["libs/blah"],
exports: "shimmedLibrary"
}
}
});
In addition, I also have my buildconfig.js file, which defines the optimization parameters:
({
baseUrl: "../js",
shim: {
"libs/shimmedLibrary": {
deps: ["libs/blah"],
exports: "shimmedLibrary"
}
},
include: ["requireLib"],
name: "js/Main",
out: "../bin/js/Main-built.js",
optimize: "none"
})
Looking at these two configurations, one can see that the shim object is identical in both files. This creates a bit of a headache because if I ever have to add in a shim object, I have to MAKE SURE that I add the shim reference in both configuration files. In reality, my shim object actually contains about a dozen shims, so this makes maintenance a bit of a pain.
Is there any way for me to create a single, shared reference to my shim object such that both my data-main Main.js file, and my optimization buildconfig.js file can both re-use that same reference?

Use the mainConfigFile option in your build config so that it points to the file that contains your runtime configuration:
mainConfigFile: "js/Main.js"

Related

Require.js - loading modules relative to the root of the server

Imagine you have a large web application and you decide to use the require.js for loading some of your modules. To stir things up a little bit let's suppose that all your modules are stored directly at the root of the server and all the modules have an additional prefix (let's say the prefix consists of 'n/') which should be ignored when loading the module.
On some nested page - e.g. [host]/path/to/page you want to define some entry point module with some dependency on some arbitrary module e.g. n/my/modulename. So you have:
define('entrypoint', ['n/my/modulename'], function(modulename){ ....
});
But when you open the page, require.js decides to load your module according to the module name relatively to current document location - so it tries to fetch
[host]/path/to/page/n/my/modulename. So far you think that the poor require.js is just blindly doing its work and does not know about your crazy location of your modules.
So how do you tell require to rewrite the path ? According to Require.js documentation we might try configuring it in a way similar to this:
require.config({
baseUrl: '/'
})
That does not work
So let's try this
require.config({
baseUrl: ''
})
No.
require.config({
baseUrl: '/',
paths: {
n: ''
}
})
Still wrong
require.config({
baseUrl: '/',
paths: {
n: '/'
}
})
Almost there. Now you realize that the require tries to load your modules from this path: http://my/modulename and you start wondering what were the authors of the library smoking.
After some struggling I think I figured out the only suitable solution for my use case. You have to configure the require with this:
require.config({
baseUrl: '/',
paths: {
n: 'FOO/../'
}
})
Now it finally grabs the n/my/modulename from url http://[host]/my/modulename.

Webpack :use legacy global functions in another module [duplicate]

I'm exploring the idea of using Webpack with Backbone.js.
I've followed the quick start guide and has a general idea of how Webpack works, but I'm unclear on how to load dependency library like jquery / backbone / underscore.
Should they be loaded externally with <script> or is this something Webpack can handle like RequireJS's shim?
According to the webpack doc: shimming modules, ProvidePlugin and externals seem to be related to this (so is bundle! loader somewhere) but I cannot figure out when to use which.
Thanks
It's both possible: You can include libraries with a <script> (i. e. to use a library from a CDN) or include them into the generated bundle.
If you load it via <script> tag, you can use the externals option to allow to write require(...) in your modules.
Example with library from CDN:
<script src="https://code.jquery.com/jquery-git2.min.js"></script>
// the artifial module "jquery" exports the global var "jQuery"
externals: { jquery: "jQuery" }
// inside any module
var $ = require("jquery");
Example with library included in bundle:
copy `jquery-git2.min.js` to your local filesystem
// make "jquery" resolve to your local copy of the library
// i. e. through the resolve.alias option
resolve: { alias: { jquery: "/path/to/jquery-git2.min.js" } }
// inside any module
var $ = require("jquery");
The ProvidePlugin can map modules to (free) variables. So you could define: "Every time I use the (free) variable xyz inside a module you (webpack) should set xyz to require("abc")."
Example without ProvidePlugin:
// You need to require underscore before you can use it
var _ = require("underscore");
_.size(...);
Example with ProvidePlugin:
plugins: [
new webpack.ProvidePlugin({
"_": "underscore"
})
]
// If you use "_", underscore is automatically required
_.size(...)
Summary:
Library from CDN: Use <script> tag and externals option
Library from filesystem: Include the library in the bundle.
(Maybe modify resolve options to find the library)
externals: Make global vars available as module
ProvidePlugin: Make modules available as free variables inside modules
Something cool to note is that if you use the ProvidePlugin in combination with the externals property it will allow you to have jQuery passed into your webpack module closure without having to explicitly require it. This can be useful for refactoring legacy code with a lot of different files referencing $.
//webpack.config.js
module.exports = {
entry: './index.js',
output: {
filename: '[name].js'
},
externals: {
jquery: 'jQuery'
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
})
]
};
now in index.js
console.log(typeof $ === 'function');
will have a compiled output with something like below passed into the webpackBootstrap closure:
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function($) {
console.log(typeof $ === 'function');
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1)))
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
module.exports = jQuery;
/***/ }
/******/ ])
Therefore, you can see that $ is referencing the global/window jQuery from the CDN, but is being passed into the closure. I'm not sure if this is intended functionality or a lucky hack but it seems to work well for my use case.
I know this is an old post but thought it would be useful to mention that the webpack script loader may be useful in this case as well. From the webpack docs:
"script: Executes a JavaScript file once in global context (like in script tag), requires are not parsed."
http://webpack.github.io/docs/list-of-loaders.html
https://github.com/webpack/script-loader
I have found this particularly useful when migrating older build processes that concat JS vendor files and app files together. A word of warning is that the script loader seems only to work through overloading require() and doesn't work as far as I can tell by being specified within a webpack.config file. Although, many argue that overloading require is bad practice, it can be quite useful for concating vendor and app script in one bundle, and at the same time exposing JS Globals that don't have to be shimmed into addition webpack bundles. For example:
require('script!jquery-cookie/jquery.cookie');
require('script!history.js/scripts/bundled-uncompressed/html4+html5/jquery.history');
require('script!momentjs');
require('./scripts/main.js');
This would make $.cookie, History, and moment globally available inside and outside of this bundle, and bundle these vendor libs with the main.js script and all it's required files.
Also, useful with this technique is:
resolve: {
extensions: ["", ".js"],
modulesDirectories: ['node_modules', 'bower_components']
},
plugins: [
new webpack.ResolverPlugin(
new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", ["main"])
)
]
which is using Bower, will look at the main file in each required libraries package.json. In the above example, History.js doesn't have a main file specified, so the path to the file is necessary.

Requirejs configuration in different file

I am using requirejs. My main.js content is like following.
requirejs.config({
async: true,
parseOnLoad: true,
packages: [],
paths: {
jquery: 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min'
}
});
require(["login"], function (loginService) {
loginService.login('validUser');
});
Now, my config elements are little. But later, I will add packages, paths and others, so the require.config lines will increase.
I wanna separate require.config as a different file and use it?
If jquery load delays, does the error occurs? My other javascript files are using it.
Yes you can, require your config before you require anything else, like this:
config example:
require.config({
baseUrl: '/Public/js',
paths: {
jquery: '../../Scripts/jquery-1.10.2.min',
jqueryui: '../../Scripts/jquery-ui-1.10.2.min',
},
shim: {
jqueryui: {
deps: ['jquery']
},
}
waitSeconds: 3
});
and, then I load it:
require(['/Public/js/config.js'], function() {
require(['home/index'], function() {
});
});
Just remember that you reference the config.js by path in the first require-statement because require.js can not resolve by baseUrl since it has not been loaded. When you get to the inner require()-statement, its loaded and you can reference dependencies relative to baseUrl.
You can put the config into a separate JS file, that's not a problem. Just make sure that file is loaded prior to the require() call in your main code.
If you're using jQuery for other scripts that are not loaded via requireJS, you will get errors if they happen to load sooner than jQuery. What you need to do is convert all those static files into requireJS modules and load them all via requireJS. By using a define() function in each of the modules, you can set up dependencies, so all modules will wait for jQuery to load prior to executing their own code.
This is an example of a multipage requirejs based project where the requirejs.config call is in a separate file
https://github.com/requirejs/example-multipage/tree/master/www

Preserving jQuery dependency for highcharts with requireJS optimizer

I'm testing out requireJS and am trying to make a simple project using highcharts. I've started with the requireJS multipage example project as a starting point.
My dir structure looks the same as the base structure, with highstock.js added in the lib directory.
page1.html: page 1 of the app.
page2.html: page 2 of the app.
js
app: the directory to store app-specific modules.
lib: the directory to hold third party modules, like jQuery.
common.js: contains the requirejs config, and it will be the build
target for the set of common modules.
page1.js: used for the data-main for page1.html. Loads the common
module, then loads app/main1, the main module for page 1.
page2.js: used for the data-main for page2.html. Loads the common
module, then loads app/main2, the main module for page 2.
common.js holds the configuration and I've added a shim for highstock there:
requirejs.config({
baseUrl: 'js/lib',
paths: {
app: '../app'
},
shim: {
"highstock": {
"exports": "Highcharts",
"deps": [ "jquery"]
},
} // end Shim Configuration
} );
I also am using the base build file, with the addition of a line to set common.js as the config file and another to disable minifying.
optimize: "none",
mainConfigFile: '../www/js/common.js',
In apps/main1.js I've added a var HighCharts= require('highstock'); and I then try to use it.
When I run this in the normal build everything works fine. All the dependencies hold and everything loads.
When I attempt to optimize my build, highcharts doesn't receive the jQuery dependency. I think I see why its happening, but I'm not sure how to fix it.
My build creates 3 files, common.js, page1.js, and page2.js.
The relevant parts of the build output:
js/lib/../common.js
----------------
js/lib/../common.js
js/lib/jquery.js
...
js/lib/../page1.js
----------------
js/lib/../page1.js
js/lib/highstock.js
js/app/main1.js
...
My page then references the built page1. When it attempts to load the highstock module it errors out since jQuery has not yet been loaded/isn't accessible.
When I see the built page1 I can see why.
require(['./common'], function (common) {
require(['app/main1']); //highcharts is in main1 in the non-optimized version
});
define("../page1", function(){});
//a few more defines
(function () { // start highcharts module definition HERE
So instead of being defined in the callback after common (including jQuery) has been loaded, its loaded after making the request, but before the callback executes.
My question is, why is this happening there instead of inside the callback (which is where it is loaded in the non-optimized version). I've tried multiple options in the build.js file and config file and I seem to be missing some key concept or small error.
Sorry for the super long question but I felt all the info was necessary. If more info is needed I can post it, or get rid of something superfluous.
Please take look at very simple example which use require js http://jsfiddle.net/wAM3h/
require({
paths: {
jquery: "//cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min",
hchart: [
"http://code.highcharts.com/highcharts",
"http://code.highcharts.com/highcharts-more",
"http://code.highcharts.com/modules/exporting"
]
}
},
['jquery', 'hchart'], function($, hc) {
window.chart = new Highcharts.Chart(options);
});
Not sure you're still involved with the project or not:
I see that you've not defined the path to the highcharts library in the code above. I could not see it even in the repo you mentioned.
And, again, highcharts prevents re-declaration of this namespace, so you must use a different name
- Hence, you must use a different name while shim-ming it
Note: Libraries like highcharts can be safely used in an amd module without using a shim (unless you need explicit access to the object exported by it).
So, your Config File should look like this:
requirejs.config({
baseUrl: 'js/lib',
paths: {
app: '../app',
'highstock-custom-name': 'path/to/highcharts.js'
},
shim: {
"highstock-custom-name": {
... //as is, although not necessary
}
}
});

require js on build file combine

I use require js in my single page app and I am wondering if it is possible to combine all modules, vendor libraries (jQuery, Underscore, Backbone) in one single file, since this would speed up my app.
app
css
html
homepage
main.html
login.html
signup.html
js
build.js
libs
jquery.js
backbone.js
underscore.js
require.js
modules
collections
models
views
main
main.js
app.js
router.js
app-build
My build configuration file currently is:
({
appDir: '../',
baseUrl: 'js/modules',
mainConfigFile: 'modules/main/main.js',
dir: '../../app-build',
optimize: 'uglify',
uglify: {
toplevel: true,
ascii_only: true,
beautify: true,
max_line_length: 1000
},
preserveLicenseComments: true,
logLevel: 0
})
I'm certainly not an expert in this area, but I did manage to get this working for one of my projects, admittedly mainly by trial and error.
I found that I needed to make either/both of appdir and baseUrl be a directory which contained both my code and the library code - in your case, this would either be ./app/js or ./app (depending on whether you also wanted your templates combined).
I also supplied paths to the various library functions.
Having got it working I really ought to go back and see what was actually necessary, and what was cargo-cult coding. For now I have more pressing code to be writing...
For your interest I include my options: note I am optimizing from node.js via rjs.optimize(...).
var options = {
baseUrl: "./site/app",
appdir: "./site/app",
name: "js/main",
out: "main-built.js",
paths: {
jQuery: 'js/libs/jquery/jquery',
Underscore: 'js/libs/underscore/underscore',
Backbone: 'js/libs/backbone/backbone',
Handlebars: 'js/libs/handlebars/handlebars',
templates: 'templates'
}
};
I'm not expecting this to be the correct answer, but hopefully it will lead you somewhere useful.

Categories