I use r.js optimizer to combine js files based on build profile as it's suggested in documentation. Here's my build-config.js:
({
baseUrl: ".",
paths: {
jquery: '//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min',
},
name: "main",
out: "main-built.2013-07-30.js"
})
As you can see it's based upon main.js file, here's a code of it:
requirejs.config({
baseUrl: 'scripts',
urlArgs: "bust=" + (new Date()).getTime(),
paths: {
jquery: [
'//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min',
'lib/jquery-1.9.1.min',
],
},
});
require([
'layout',
'cue',
], function() {
});
If I preserve urlArgs: "bust=" + (new Date()).getTime() in main.js all external files (here jquery which is loaded from CDN) look like .../jquery.js?bust=1377412213
So it's PITA to comment out this line every time I make a build. I've read through all the documentation and googled for a solution but everything in vain. Maybe I do it wrong?
Way late to the party on this, but here the solution I used: append the urlArgs param to the config with a subsequent call.
HTML:
<script src="js/libs/require.js"></script>
<script src="js/config.js"></script>
<script>require(['main-app']);</script>
Config File:
requirejs.config({
paths: {...},
shim: {...}
});
// Apply the cache bust only for the browser.
if (window) {
requirejs.config({
urlArgs: REQUIRE_NOCACHE ? "bust="+(new Date()).getTime() : null
});
}
The optimizer only takes the first requirejs.config declaration and it ignores the subsequent code. The second requirejs.config declaration extends rather than overrides the first, so urlArgs is still successfully applied to modules in the browser. Hope that helps.
The following solution would work in your case, where you're renaming the main.js file in the r.js build:
urlArgs: require.specified('main') ? "bust="+(new Date()).getTime() : null
The above snippet will check for the module named 'main', which will match in development, but not in production, where the module is named 'main-built.2013-07-30'.
I've tested in development and production builds and it works! :-)
On the require.specified() function:
With requirejs is it possible to check if a module is defined without attempting to load it?
As of version 2.2.0, urlArgs now accepts functions.
It sends the moduleId and the url, so you can segment depending on the path if it should have extra args or not. See https://requirejs.org/docs/api.html#config-urlArgs
Related
I keep having issues with the cached js files, which causes the user to not have the latest js and the browser to give js errors on files it can't find that should be included with requirejs.
My requirejs (version 2.3.6) setup is as follows:
<script src="{% static 'main/js/require.js' %}"></script>
<script>
function requireConfig(urlArgs) {
requirejs.config({
baseUrl: '/static/main/js/',
urlArgs: urlArgs,
waitSeconds: 15,
packages: [{
name: 'moment',
location: '//cdnjs.cloudflare.com/ajax/libs/moment.js/2.23.0',
main: 'moment'
}],
paths: {
// vendors
'jquery': '//cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min',
'jquery_ui': '//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min' ,
'text_contrast': './vendors/text_contrast',
},
shim: {
'jquery_ui': {
deps: ['jquery']
},
'text_contrast': {
exports: 'hex',
},
}
});
}
</script>
<script type="text/javascript">
requireConfig("bust=v18");
require(['platform/init'], function (m) {
m.init({
debug: {{ debug|yesno:"true,false" }},
});
});
</script>
I include this before the closing body tag. Whenever the user refreshes the page after clearing his cache, all (new) js files with the bust argument are loaded correctly. The problemen is, I cant force my users to clear its cache everytime I deploy some new js.
Update
If I add +(new Date()).getTime() to the requireConfig function, the files now get the time argument, which seems to work. But on some urls the browser still looks for a js file at the wrong path. For example when I go to an url it loads:
/static/main/js/text_contrast.js?bust=v181560938449189
which gives a 404.
After clearing my cache and a reload the file is found at:
/static/main/js/vendors/text_contrast.js?bust=v181560939213670
This seems to occur on urls I've already loaded in the past with that wrong path pointing to the text_contrast.js file
You can add time stamp to the urlArgs in the requirejs.config object so that url's will not be cached.
One way you can do it is by changing your code from
requireConfig("bust=v18");
to
requireConfig("bust=v18"+(new Date()).getTime());
When using grunt-ts and specifying and out file my app runs as expected, but since that option has no support for fast compilation, i tried using a regular compilation where all my .ts files live on the dist folder
There are two issues, first, it will fail to load any imported file, since it will try to look for it without extension. As a quick fix i edited the load fn on the require.js file and all my dependencies load correctly, but then the sourcemaps stopped working, all i get is a blank file on the chrome inspector (and of course i don't want to rely on a dirty hack) .
Please note that i'm not very familiar with requirejs so I'm not quite sure if this is a misconfiguration on my side, a bug, or something that is missing.
My grunt config, related to ts
ts: {
options: {
module: 'amd',
target: 'es5',
experimentalDecorators: true
},
dev: {
src: ['client-app/**/*.ts'],
outDir: "dist",
watch: '.'
}
},
My bootstrap.js which is just the entry point and require.js config file
requirejs.config({
baseUrl: '.',
waitSeconds: 20
});
requirejs(['/init'], function(module) {
module.main()
});
A simplified version of the compiled init file
define(["require", "exports", './section-manager.view'], function (require, exports, section_manager_view_1,) {
"use strict";
function main() {
///
}
exports.main = main;
});
//# sourceMappingURL=init.js.map
And the html
<script src="/js/lib/require.js" data-main="/bootstrap"></script>
Any help is appreciated, thanks
Edit:
Using System.js or #Luis answer solves the loading issue.
The sourcemap issue is solved by using sourceRoot or
inlineSourceMap: true,
inlineSources: true
To the ts options
Don't use an absolute module name. If you do use an absolute name, RequireJS assumes that you do not want any alteration when it generates a path from the module name and will not add the .js extension. You should use a relative path, or you could do this:
requirejs.config({
baseUrl: '/'
});
requirejs(['init'], function(module) {
module.main()
});
By doing this, RequireJS will automatically add the .js extension.
I've been researching this question a lot. I know a common issue is caused with asynchronous loading before require has been defined. I dont think that's the issue that I'm experiencing. I'm declaring baseURL and paths in a config file, but I always get an error that the scripts can't be found because it seems to ignore my paths and just look from the baseURL.
This is my folder structure
index.html
js/
app/
jsonLoader.js
notionalAddGraph.js
lib/
config.js
require.js
There's nothing in lib, but I'm mimicking the structure of a project that I'm working on. Here is the html for the page.
HTML:
<html>
<head>
<title>Blah Blah Blah</title>
<script data-main="js/config.js" src="js/require.js"></script>
<script id="graphData" type="application/json">
{
"json": "Is better than",
"global": "but it requires a change to the authoring",
"environment": "also, because of the JSON.parse",
"required": "it is probably slower too"
}
</script>
</head>
Here is the code for config.js
config.js:
require({
baseUrl: 'js/lib',
paths: {
app: '../app'
},
urlArgs: 'bust=' + (new Date()).getTime()
});
require(['app/jsonLoader','app/notionalAddGraph'],
function(require) {
'use strict';
});
and here is the code for jsonLoader.js
jsonLoader.js:
define(function() {
var jsonString = document.getElementById('graphData').textContent;
return JSON.parse(jsonString);
})
annnd here is the code for notionalAddGraph.js
notionalAddGraph.js:
define([
'jsonLoader'
], function(json) {
console.log(json.required);
});
so every time that I try this, require tries to load js/lib/jsonloader.js and fails because it should be looking under app, not lib. Oddly enough, if I intentionally specify the incorrect path when I define app, requireJS will at least look for jsonLoader where I told it to. Obviously though, its not there, because I specified the wrong path. When I specify the correct path, it doesn't work and tries to load from baseURL. I've tried changing everything I can imagine for config.js, but nothing seems to work. I've copied the format directly from requireJS API, but still nothing. Does anyone have an idea as to what I might be doing wrong?
In notionalAddGraph.js you have define with 'jsonLoader' so its looking in the correct path as expected, add ../ to your define and should work correctly
Here's what i'd try
First, call the scripts using an absolute path (you can rollback this change later, but it's just to be on the safe side)
<script data-main="/js/config.js" src="/js/require.js"></script>
Second, declare the config parameters using requirejs.config. I guess that the way you're doing now they are just variables.
requirejs.config({
baseUrl: 'js/lib',
paths: {
app: '../app'
},
urlArgs: 'bust=' + (new Date()).getTime()
});
Third, the return of the jsonLoader isn't the require object, but the object that comes from parsing the jsonString. The way you're doing now is replacing the require object.
require(['app/jsonLoader','app/notionalAddGraph'], function(jsonObject) {
'use strict';
console.log('require is still', require, 'and jsonobject is ',jsonObject);
});
I'm having pretty simple directory structure for scripts:
/js/ <-- located in site root
libs/
jquery-1.10.1.min.js
knockout-2.2.1.js
knockout.mapping.js
models/
model-one.js
model-two.js
...
require.js
config.js
Since the site engine uses clean URLs I'm using absolute paths in <script>:
<script type="text/javascript" data-main="/js/config.js" src="/js/require.js"></script>
RequireJS config:
requirejs.config({
baseUrl: "/js/libs",
paths: {
"jquery": "jquery-1.10.1.min",
"knockout": "knockout-2.2.1",
"komapping": "knockout.mapping"
}
});
Somewhere in HTML:
require(["jquery", "knockout", "komapping"], function($, ko, mapping){
// ...
});
So the problem is that RequireJS completely ignores baseUrl and paths defined in config file. I get 404 error for every module required in the code below. I see in browser console that RequireJS tries to load these modules from /js/ without any path translations:
404: http://localhost/js/jquery.js
404: http://localhost/js/knockout.js
404: http://localhost/js/komapping.js
However after the page is loaded and the errors are shown I type in console and...
> require.toUrl("jquery")
"/js/libs/jquery-1.10.1.min"
Why so? How to solve this problem?
It's my first experience using RequireJS, so I'm feeling like I've skipped something very simple and obvious. Help, please.
Update
Just discovered this question: Require.js ignoring baseUrl
It's definitely my case. I see in my Network panel that config.js is not completely loaded before require(...) fires own dependency loading.
But I don't want to place my require(...) in config because it is very specific for the page that calls it. I've never noticed such problem with asynchronicity in any example seen before. How do authors of these examples keep them working?
Solved.
The problem was that config file defined in data-main attribute is loaded asynchronously just like other dependencies. So my config.js accidentally was never completely loaded and executed before require call.
The solution is described in official RequireJS API: http://requirejs.org/docs/api.html#config
... Also, you can define the config object as the global variable require before require.js is loaded, and have the values applied automatically.
So I've just changed my config.js to define global require hash with proper configuration:
var require = {
baseUrl: "/js/libs",
paths: {
"jquery": "jquery-1.10.1.min",
"knockout": "knockout-2.2.1",
"komapping": "knockout.mapping"
}
};
and included it just BEFORE require.js:
<script type="text/javascript" src="/js/config.js"></script>
<script type="text/javascript" src="/js/require.js"></script>
This approach allows to control script execution order, so config.js will always be loaded before next require calls.
All works perfectly now.
Fixed the issue.
My config was being loaded asynchronously, and therefore the config paths weren't set before my require statement was being called.
As per the RequireJS docs Link here, I added a script call to my config before the require.js call. And removed the data-main attribute.
var require = {
baseUrl: '/js',
paths: {
'jquery': 'vendor/jquery/jquery-2.0.3.min',
'picker': 'vendor/pickadate/picker.min',
'pickadate': 'vendor/pickadate/picker.date.min'
},
shim: {
'jquery': {
exports: '$'
},
'picker': ['jquery'],
'pickadate': {
deps: ['jquery', 'picker'],
exports: 'DatePicker'
}
}
}
All is now working
I'm trying to write a custom script/plugin to be included in the requireJS r.js optimizer (for fingerprinting static files and one for bundling JSON resources).
What I have not figured out is how to trigger my custom-optimizer during the build process. I can write the scripts allright, but how would I trigger a bundle-build.js module during optimization.
Thanks for hints!
EDIT:
I know I can use 'onBuildRead/Write', but this is not the place to add a optimizer plugin. Something like require-css is closer, but a file prefix, which triggers the plugin, which has a pluginBuilder specified to use during build. Not quite what I'm looking for.
Optimizer should pickup all files that are specified as dependencies. If you need to include something extra then you can add extra logic using onBuildRead or onBuildWrite callback, where you can add/remove extra stuff:
({
name: 'main',
baseUrl: '../',
// optimize: 'none',
optimize: 'uglify2',
exclude: ['jquery'],
mainConfigFile: '../main.js',
out: '../main.min.js',
// A function that if defined will be called for every file read in the
// build that is done to trace JS dependencies.
// Remove references to console.log(...)
onBuildRead: function (moduleName, path, contents) {
return contents;
// return contents.replace(/console.log(.*);/g, '');
},
onBuildWrite: function (moduleName, path, contents) {
// Add extra stufff;
return contents;
}
})