RequireJS bundles fail in optimizer - "modules share the same URL" - javascript

tl;dr
RequireJS optimizer doesn't like me defining bundle definitions on a module, but also does not find the modules if I don't define the bundles.
long version
I am getting the following error when trying to use the requirejs optimizer:
Error: Module loading did not complete for: scripts/simulation.bundle, app_mini, testservice
The following modules share the same URL. This could be a misconfiguration if that URL only has one anonymous module in it:
.../web/dist/scripts/app.bundle.js: app_mini, testservice
I am actually using grunt-contrib-requirejs to optimize my js scripts for production. It was all working fine before adding the simulator.bundle
I have 3 bundles:
app.bundle (main bundle)
simulation.bundle
vendor.bundle
This is the modules option of the requirejs grunt tasks
[{
name: 'scripts/vendor.bundle',
exclude: [],
override: {
paths: {
angular: 'bower/angular/angular',
jquery: 'bower/jquery/dist/jquery',
ngRoute: "bower/angular-route/angular-route"
},
shim: {
angular: {
exports: 'angular',
deps: ['jquery'] // make jquery dependency - angular will replace jquery lite with full jquery
},
bundles: {
'scripts/app.bundle': ['app_mini', 'testservice'],
},
}
}
},
{
name: 'scripts/simulation.bundle',
exclude: [],
override: {
paths: {},
shim: {},
bundles: {
'scripts/vendor.bundle': ['angular', 'jquery'],
'scripts/app.bundle': ['app_mini', 'testservice']
}
}
},
{
name: 'scripts/app.bundle',
exclude: ['scripts/vendor.bundle'],
override: {
paths: {
app_mini: 'scripts/app.mini',
testservice: 'scripts/features/test.service'
},
shim: {},
bundles: {
'scripts/vendor.bundle': ['angular', 'jquery']
}
}
}
]
The bundles in simulation.bundle seem to be the problem. However, if I remove them, the files cannot be found:
>> Error: ENOENT: no such file or directory, open
>> '...\web\dist\app_mini.js'
>> In module tree:
>> scripts/simulation.bundle
The simulation.bundle is just a dummy module, loading angular and app_mini:
define(['app_mini', 'angular'], function(app_mini, angular) {
// nothing here
}
So either way, the optimizer cannot process the dependencies. How do I have to configure it to make it work?

Alright, once again I am posting the answer to my own question, and I hope some other people will benefit from my mistakes ;)
So what I found out is:
Bundle config is only for requireJS and not for the optimizer!
The bundles I defined in the config are leading to the error of modules sharing the same url.
The right way to do it, is to define ALL the paths for ALL the modules, and to specifically exclude the modules by name that should not be included in a module.
For example, app_mini should go into the app.bundle, but because it is required in the simulation.bundle it would get included there, because excluding app.bundle is not yet possible (it has not been optimized at this point), we need to exclude app_mini directly.
So a working config would look like this: (not tested)
paths: {
angular: 'bower/angular/angular',
jquery: 'bower/jquery/dist/jquery',
ngRoute: "bower/angular-route/angular-route"
app_mini: 'scripts/app.mini',
testservice: 'scripts/features/test.service'
},
shim: {
angular: {
exports: 'angular',
deps: ['jquery'] // make jquery dependency - angular will replace jquery lite with full jquery
}
},
modules: [
{
name: 'scripts/vendor.bundle',
exclude: [],
},
{
name: 'scripts/simulation.bundle',
exclude: [`app_mini`],
},
{
name: 'scripts/app.bundle',
exclude: ['scripts/vendor.bundle'],
}
}]

Related

Downloading Script Dependencies in Parallel RequireJS

Following this RequireJS example I'm trying to have a single file for all vendor js assets like jquery and foundation, whilst loading page specific code in other modules.
While I can build and copy the js successfully (using grunt requirejs optimiser) into a build folder, the baseUrl in the require.config is obviously now wrong.
baseUrl: 'js' points to public/js instead of public/build/js and so all paths are incorrect.
Is there a way of dynamically updating the baseUrl when running the optimiser? So it points to public/build/js?
Here's my grunt task:
requirejs: {
dist: {
options: {
baseUrl: '<%=pkg.paths.js%>',
dir:'project/public/build/js',
mainConfigFile: '<%=pkg.paths.js%>main.js',
optimize: 'none',
removeCombined: true,
uglify2: {
no_mangle: true,
compress: {
drop_console: true
}
},
modules: [
{
name: 'vendorCommon'
},
{
name: 'dashboard',
exclude: ['vendorCommon']
}
]
}
}
}
Vendor Common
define(['jquery', 'foundation'],
function () {
//Just an empty function, this is a place holder
//module that will be optimized to include the
//common depenendencies listed in this module's dependency array.
});
Require Config
require.config({
baseUrl: '/js',
priority: ['vendorCommon'],
paths: {
'vendorCommon':'vendor/vendor-common',
'jquery':'../bower_components/jquery/dist/jquery.min',
'foundation':'../bower_components/foundation/js/foundation/foundation',
'dashboard':'views/dashboard'
},
shim: {
'foundation': ['jquery']
}
});
I've used the optimizer's onBuildWrite setting to modify some modules when they are optimized. If your configuration is included in your optimized bundle then you could use onBuildWrite to patch it:
onBuildWrite: function (moduleName, path, contents) {
if (moduleName === '<%=pkg.paths.js%>main') {
return contents.replace("baseUrl: '/js'", "baseUrl: '/build/js'");
}
return contents;
}
Disclaimer: I'm writing this off the top of my head. Beware of typos.
Another possibility would be to override baseUrl at runtime. RequireJS is able to combine multiple configurations into a single configuration. A new value of baseUrl in a later call would override the earlier value. So if you have a way to set up the optimized version of your app (for instance, through different HTML served by your server) to call require.config({baseUrl: '/build/js'}); after the call you show in your question but before any code that needs the correct baseUrl, this could also be an option.

Getting dependencies to load correctly in requirejs (autobahn and whenjs)

I have been stuck on this problem for the past few hours. I'm trying to get autobahnjs and whenjs to be loaded correctly by requirejs.
require.config({
paths: {
angular: '../bower_components/angular/angular',
angularBootstrap: '../bower_components/angular-bootstrap/ui-bootstrap',
bootstrap: '../bower_components/bootstrap/dist/js/bootstrap',
jquery: '../bower_components/jquery/jquery',
chosen: '../bower_components/chosen/chosen.jquery.min',
text: '../bower_components/requirejs-text/text',
autobahn: '../bower_components/autobahnjs/autobahn/autobahn'
},
packages: [
{ name: 'when', location: '../bower_components/when/', main: 'when' }
],
baseUrl: '/bundles/example/app/scripts/',
shim: {
angular : {
exports : 'angular'
},
angularBootstrap: {
deps: ['angular']
},
autobahn: {
deps: ['when']
}
},
priority: [
'angular'
]
});
require
( [
'angular',
'app',
'autobahn',
'angularBootstrap',
'jquery',
'bootstrap',
'chosen',
'controllers/event',
'services/notify'
], function(angular, app) {
// more code here
});
Autobahnjs has a dependency on whenjs. All the files are loaded (and in the correct order). but when is always undefined. I have absolutely no idea what I'm doing wrong. I've tried all sorts of ways to solve it. I also have a bower.json file if this helps anyone replicate the problem. Thanks in advance.
EDIT: Autobahnjs does not currently support AMD. Whenjs, however, does support it.
As you have noticed already, there is an issue for adding requirejs support to AutobahnJS. There is also more embedded stuff inside AutobahnJS bundled for "convenience", mainly parts from cryptojs.
The challenge simply is: how to best serve all users, not matter if and what module loader they use, and if they want convenience (bundled stuff) or prefer to have stuff separate (and manage/load that themselves).
I can't promise, but I try to address it with priority. However, for further discussion, I think the best place would be the GitHub issue.
This has now been implemented in v0.8.0

Concatenate with requirejs and exclude a single file?

I have build and app using requirejs and use it to load html templates and a single config file via the text plugin.
After concatenation the html templates are inlined in the JS, which is fine, but I need the config.json file to still remain as a file which is loaded externally like before concatenation.
Example code of the main view
define([
'jquery',
'underscore',
'backbone',
'lib/text!templates/main.html',
'lib/text!config.json',
], function($, _, Backbone, projectListTemplate, Config) {
var MainView = Backbone.View.extend({});
})
This is my build file
({
appDir: './../public',
baseUrl: 'static/js/app',
dir: './../dist',
modules: [
{
name: 'main'
}
],
fileExclusionRegExp: /^(r|build)\.js$/,
exclude: ['config.json'],
optimizeCss: 'standard',
removeCombined: true,
paths: {
jquery: 'lib/jquery-2.0.3.min',
waypoints: 'lib/waypoints.min',
underscore: 'lib/underscore',
backbone: 'lib/backbone',
text: 'lib/text',
},
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: [
'underscore',
'jquery',
'waypoints'
],
exports: 'Backbone'
},
}
})
You should exclude your file in the same way you included it. Try this way:
exclude: ['lib/text!config.json']
But I just stopped using this approach, because it requires existence of config.json file in build phase. Instead I'm using nested require calls (You may use require inside your defined modules to load files in runtime). Unlike define, nested require may not be included in build process (and by default they are not) and validation phase will be fine.
Be advised, this approach has at least one disadvantage:
Nested require calls are also asynchronous. But in my case that is not a problem.

Requirejs, almond, backbone, handlebars

Here's my situation, using Backbone and Handlebars with Requirejs.
I'm following CommonJS module definition style, because I find myself to be more comfortable with it:
define(function(require) {
var Backbone = require('Backbone')
var Item = require('model/item')
// ...
})
And this is my requirejs config:
require.config({
baseUrl: "/javascripts/",
paths: {
jquery: 'components/jquery/jquery',
underscore: 'components/underscore/underscore',
backbone: 'components/backbone/backbone',
handlebars: 'components/handlebars/handlebars',
text: 'components/text/text'
},
shim: {
underscore: {
exports: "_"
},
handlebars : {
exports: "Handlebars"
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
Everything is running smooth before optimization, no problem occurs.
But, after optimization with r.js dependencies seem to break.
I'd like to use Almond js in production, so here's my build file:
({
baseUrl: ".",
paths: {
jquery: "components/jquery/jquery",
underscore: "components/underscore/underscore",
handlebars: "components/handlebars/handlebars",
backbone: "components/backbone/backbone",
text: "components/text/text"
},
// we use almond minimal amd module loader
name: "components/almond/almond",
// the application entry point
include: ['app/init'],
// we need to teel almond to require app/init
insertRequire: ['app/init'],
out: "main.js",
cjsTranslate: true,
wrap: true,
optimize: "none"
})
Now, when I run the optimized javascript in browser, all I get are error messages, saying me that jQuery and Handlebars are undefined (neither Backbone.$ is, of course).
A simple workaround was to force jQuery loading, and assigning it to Backbone, like this:
var $ = require('jQuery')
var Backbone = require('Backbone')
Backbone.$ = $
But it sounds very silly and redundant to me.
I feel like I'm doing something wrong but cannot figure out what.
After optimization Handlebars fail to load as dependency too.
If I force its loading (as I did with jQuery), I get an error message during the build process, saying me that the module fs (a npm package) cannot be found.
I googled but found only this topic on Google groups (https://groups.google.com/forum/?fromgroups=#!topic/requirejs/lYwXS-3qjXg) that seems to be related to my problem, even if proposed solutions are not working at all.
I think you should add the Shim's config in your build file too.

Require.js + Backbone optimization

Good afternoon,
I'm trying to optimize a source code based on Require.js and Backbone using r.js but I'm getting the following error during the compilation :
Tracing dependencies for: main
Cannot optimize network URL, skipping: empty:.js
TypeError: Cannot read property 'normalize' of undefined
In module tree:
main
app
router
views/main_panel/event_details
helpers/template_manager
My template_manager module does not try to access any 'normalize' property so I don't really understand what is that supposed to mean.
Here's the entry point to my application as well as the require.js configuration.
require.config({
paths: {
order: 'libs/requirejs-plugins/order',
text: 'libs/requirejs-plugins/text',
jQuery: 'libs/jquery/jquery',
Underscore: 'libs/underscore/underscore',
Backbone: 'libs/backbone/backbone',
templates: '../templates',
Sync: 'helpers/sync'
}
});
require([
'app',
'event_manager',
'order!https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',
'order!libs/underscore/underscore-min',
'order!libs/backbone/backbone-min',
'helpers/objects_extension',
'helpers/date_extension',
'helpers/assets'
], function(App){
App.initialize();
});
The application itself more or less follows what's in this tutorial.
My app.build.js file is as follow
({
appDir: "../",
baseUrl: "js",
dir: "../app-build",
modules: [
{
name: "main"
}
],
paths: {
order: 'empty:',
text: 'empty:',
jQuery: 'empty:',
Underscore: 'empty:',
Backbone: 'empty:',
templates: '../templates',
Sync: 'helpers/sync'
}
})
Thank you for your help.
James Burke says:
The text plugin needs be loaded by the loader for it to process text! depenencies. Loader plugins are executed as part of a build to resolve their resources.
So it should be enough to remove the text: "empty:" from the paths config, and just leave the excludes: in so it is not included in the final build result. This assumes you have text.js available locally to be read by the optimizer.
https://github.com/jrburke/r.js/issues/221

Categories

Resources