Is there any way to detect 404 erros in AMD loaders? - javascript

I'm working on a framework-like structure that uses curl.js as script loader. Modules can be located in either the App or Core directory, which allows me to override Core modules easily from the app. I had a server-side script to search these directories and return the first found file but then I decided that the client-side should be able to work without any help from backend, so now I have a function that tries listed directories until one of them has a file.
After a while I realized that I don't have any way to differentiate between 404 errors and syntax errors. I want the loader to fail if there is an actual error in code but in case of 404 it should just look for the next file and finally fail if no file can be found.
So, is it possible to detect if a script failed(in curl.js, AMD or generally in js) due to 404 and not because of errors in the loaded code?

Because of security concerns, browsers don't provide much information when scripts fail. In fact, IE refuses to even call the "load" event at all. The only information an AMD loader has is whether the define() was called or not.
There might be a way to detect some types of script errors, if you place your own global function call at the beginning of the script file. You can check if that function has been called. You can't detect SyntaxErrors that prevent parsing of the script, but you can detect other types of errors.
It might be possible to return the exact error to the errback function ( curl([]).then(callback, errback)) when the error happens while executing the factory function. Have you verified if this works or not?

Related

What happens if polyfill script fails to download?

I have a SPA (built on webpack, with babel, etc) that includes a polyfill in the index.html:
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Promise,Array.prototype.includes,Element.prototype.remove"></script>
One use-case for the polyfill is in order to use the Promise API on IE 10-11.
My error monitoring reporting an error on an IE 11 client of the following:
ReferenceError: 'Promise' is undefined
So I assume that particular session failed to download the polyfill for some reason.
My question is: How should I deal with this case? Is it a scenario I should expect to happen sometimes? Is the user expected to noticed the application not working properly, and reload the page?
There is an error event you can attach to allow for more control if you are really worried. You don't usually need to handle this explicitly though.
In this particular case you could migrate towards using babel to build a bundle with polyfills included in your scripts. This adds an additional build step to your process though.
Since you mentioned you're using webpack, it would just be best to include the necessary polyfills directly in the project via an import statement (something like core.js) rather than using a cdn - polyfill.io.
However, you could alternatively add an ID to the script element and listen to the onload and onerror events to determine whether the script (un)successfully loaded like so:
<script id="polyfillScript" src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Promise,Array.prototype.includes,Element.prototype.remove"></script>
In your project index.js:
document.getElementById('polyfillScript').addEventListener('error', () => {
alert('Failed to load polyfill!');
});

Track JS CSS Asset Errors on page load

Is it possible to track the number of errors and warning thrown by a browser on page load using javascript?
For example CSS Assets missing or images missing or js script errors? I don't need a detailed log but maybe a count of each type of error.
Like a count of CSS, Assets missing and JS Errors?
This needs to be tracked using JS and not in the console. The script needs to collect all these errors from the console and send them back to my server.
Yea, absolutely. There are a number of ways to do this.
For JavaScript, you can capture errors by listening to window.onerror. This will give you the error information and you can AJAX the data back to your infrastructure.
If you don't want to support the infrastructure log them yourself, you can use a JavaScript Error Logging service like TrackJS to do this automatically.
For Asset load failures, you can attach onerror listeners to the css tags, although this won't work in all browsers. You could send the load failures back to the same logging infrastructure.
You should also check out the new proposed standard on Network Error Logging, which might be relevant for what you're trying to do.

How do I include the CLDR.js library in my Ember application?

I'm building an Ember application which started using the ember-skeleton (which means I'm using Rake Pipeline as my build toolchain). I'm using ember-i18n, and that constantly throws warnings about not having the CLDR.pluralForm function.
I've added the CLDR plural functions which ember-i18n uses to the app/vendor/ directory and added that file to my Assetfile. I've verified that the code is included in my app.js before the ember-i18n code. I've also added the appropriate require lines in my main.js:
require('plurals');
require('ember-i18n');
Still, ember-i18n is giving warnings. This is the code where it's happening:
if (typeof CLDR !== "undefined" && CLDR !== null) {
pluralForm = CLDR.pluralForm;
}
if (pluralForm == null) {
Ember.Logger.warn("CLDR.pluralForm not found. Em.I18n will not support count-based inflection.");
}
How do I make sure CLDR is defined in my app?
Because no-one else has, I will suggest a few things I might look at if I were debugging the problem myself.
If the message you see in your site is the same as in the fiddler, it is due to the files just load out of order. Pretty much "plural" must load before the "i18n" plugin. If you flip the order of the linked files it will work (I forked the error-ing example).
I would try validating I am wrong by throwing some debugger; statements into each file to see which actually gets loaded first on the browser.
Because of your require('plurals') I am going to assume you are using requirejs (github is down for maintenance so I can't actually examine ember-skeleton at the moment... so this might get an edit in the future). If this assertion is true, you need to declare that 'plurals' is a dependency of 'i18n'.
"If you do not express the dependencies, you will likely get loading errors since RequireJS loads scripts asynchronously and out of order for speed." - RequireJS API
This means you probably will end up wrapping the two calls into one, adding a define or making a "shim"; these (and more) examples can be found on the RequireJS site.
Sorry if this does not help you but without seeing it actually broken; it is hard for me to debug the problem.
EDIT: Looking at the ember-skeleton, I don't see requireJS. I have never used rake (we ended up writing our pipeline in nodeJS) so I am not quite sure if you were actually trying to concat the files with require "blaw" in rake or if you actually trying to use both.... So now I assume this answer might not be super helpful.

Curl throwing ‘Promise already completed’ error

I’m currently testing various asynchronous-resource-loaders to see which one I want to use. At the moment Curl is throwing a ‘Promise already completed’ error…and their documentation says ‘this should never happen’.
I “suspect” I must to use a ‘define’ within each file being loaded (hope not). Further, their documentation says Curl can work with non-AMD javascript files. But...I am new to AMD and since Curl is far-faster than the other ones I'm testing...I am willing to put some time into understanding this better.
Lastly...
Even though FireBug shows the errors...all the files STILL LOAD asynchronously! But, BECAUSE there are errors...the 'then' portion of the code never gets called.
So My Questions Are:
Do I have to update all the JavaScript file-objects to be contained in a 'define'? (...hope not)
Can you see any other problem syntactically?
The Head’s Code Looks Like This:
<script type="text/javascript">
///<summary>Configuration</summary>
curl = { baseUrl: 'Includes/JavaScript/' };
</script>
<script src="Loaders/Curl/curl.js" type="text/javascript"></script>
<script type="text/javascript">
function onSuccess() {
}
function onError(ex) {
//alert(ex);
}
require(["MooTools/Core/mootools-1.2.2-core-jm",
"MooTools/mGeneral",
"jQuery/Core/jquery-1.3.2",
"jQuery/Core/jquery.tools.min",
"jQuery/ThirdPartyPlugIns/jquery.tmpl"])
.then(onSuccess, onError);
//require(["jQuery/TopUp/top_up-min"], null);
require(["jQuery/ThirdPartyPlugIns/jquery.dimensions",
"jQuery/ThirdPartyPlugIns/jQuery.Color.Animations",
"jQuery/ThirdPartyPlugIns/jquery.corners.min",
"jQuery/ThirdPartyPlugIns/jquery.tipsy",
"jQuery/ThirdPartyPlugIns/jquery.numberformatter-1.1.0",
"jQuery/ThirdPartyPlugIns/jquery.tipsy"]);
require(["general",
"WindowCenter",
"ThirdPartyPlugin/KeyBoardCapture",
"ThirdPartyPlugin/bsn.AutoSuggest_2.1.3",
"ee/8Ball",
"ee/EE"]);
</script>
Again...I'm sure it is caused by inexperience with AMD-styled code, but I still need help...so any is appreciated.
Typically, you should wrap your modules in a define() or append a define() at the end of the file if those modules have no dependencies. It seems, though, that those modules all depend on jQuery, if not other modules/files.
Since you're not using standard AMD require()/define() protocol, AMD is not really helping you with these modules. Unless you plan to write your own modules using define(), then you could use just about any async loader.
That said, there is a way to make curl.js work with non-AMD modules/files. Use the js! plugin. Here's your first block of files translated to use the js! plugin (note I also added the ".js" ext back on which I like to do with non-modules):
// we load the core files first, then get the next block that depends on them
curl([
"js!MooTools/Core/mootools-1.2.2-core-jm.js",
"js!jQuery/Core/jquery-1.3.2.js"
]).next([
"js!MooTools/mGeneral.js",
"js!jQuery/Core/jquery.tools.min.js",
"js!jQuery/ThirdPartyPlugIns/jquery.tmpl.js"
]).then(onSuccess, onError);
If any of those files within each array depend on each other, you can also use the !order suffix on them to ensure they wait for other files before executing/evaluating (be sure you're setting proper cache headers, though). Actually, the !order suffix is the fastest method as long as there are no caching issues (mobile browsers add some additional constraints on file size).
Were there any other error messages? Specifically, I would expect curl.js to throw at least one error besides just "Promise not completed".
Also, please check the Net tab (Firebug) or the Network tab (Chrome) to check that curl.js is looking in the correct location for the modules.
FWIW, I am planning to remove the alias require --> curl. The reason is that the global require function is non-standard (and explicitly not standardized in the AMD proposal). I suggest you use curl() instead of require().
curl.js also allows it's top-level api to be aliased explicitly via the "apiName" config param if you really, really want to use the name "require". :)
<script>curl = { apiName: "require" }; </script>
<script src="path/to/curl.js"></script>
<script>require(["some/modules"]).then(success, failure);</script>
More FWIW: the standard require is typically only needed in a module and can be requested by including it as a dependency:
define(["require"], function (require) {
require(["another/module"], function (another) {
// use another module here
});
});
-- John
If you're only doing normal javascript file loading (not modules), as it appears, i would encourage you to check out LABjs (http://labjs.com). LABjs focuses on being the most performance optimized loading solution (to the exclusion of some other features like module/dependency style stuff).
In fact, LABjs 2.0a (https://github.com/getify/LABjs/blob/master/next/LAB.src.js), which will be fully released in the next few days, is really exceptionally fast (even more than 1.2) at loading general scripts in parallel. I encourage you to give it a try, unless (as John eludes to above) you plan to go to module syntax... then stick with Curl or RequireJS.

Getting started with Yabble--browser-side CommonJS module loading

Is anyone familiar with Yabble or other browser-side CommonJS loaders?
I'm experimenting with Node.js and would very much like to create Javascript modules which can be used interchangeably on the server side and the client side. This may end up being more a "because it's awesome" kind of thing more so than "because it's practical and useful" kind of thing.
As such, I'm basically trying to get the CommonJS require() method to work on the browser-side, which is exactly what Yabble is supposed to do. I don't really know where to start with it though. I can't seem to find any documentation other than what's found in Yabble's Github readme, and that's not helping much.
Essentially all I've done is put this in an HTML page...
<script src="yabble.js"></script>
<!-- Uses require -->
<script>
require.setModuleRoot('http://localhost:8030/')
my_module = require('my_module')
</script>
But anytime I call the require() function all I get a Synchronous require() is not supported. exception thrown.
Can someone help me get started? Where am I supposed to load yabble.js where am I supposed to call require? Is there a special way to run my Javascript modules?
When loading Javascript code that will need to use the require() function into the browser, the entry point into that code must be the require.run() function.
e.g., Good:
<script src="yabble.js"></script>
<script>
require.setModuleRoot('http://localhost:8030/')
require.run('my_module') // <-- Uses require() function somewhere
</script>
e.g., Bad (Will get the Synchronous require() is not supported error):
<script src="yabble.js"></script>
<script src="http://localhost:8030/my_module.js"></script> <!-- <== Use's require function somewhere -->
FYI, it's pretty nifty how Yabble does this. It will actually statically analyze your Javascript source code, I think basically just using a regular expression to look for the require() method, and then try to pull that .js script from the server, then it does the same static analysis of that .js script, and on and on.
This can be particularly confusing because it will actually load those .js scripts even if the control logic would mean program flow would never reach the require() function. e.g., if you had...
if (False) { require('some_module'); }
... Yabble will still load this module.
Synchronous loading of modules in the browser is problematic. A construct like:
var x = require('something_remote.js')
Implies that the browser will halt your code (i.e. block), go and fetch the remote file, parse it, and then return to you the exports. But this does not jive with the single-threaded browser environment - we would be halting the main JavaScript thread (and thus page interactivity for the user) on the performance of the network. So browsers have evolved against this scenario, to favor async loading on their own schedule. There is some good discussion here:
http://www.sitepen.com/blog/2010/07/16/asynchronous-commonjs-modules-for-the-browser-and-introducing-transporter/
One pattern which might work here is that a require() implementation does synchronously block, fetches the file via XHR and then evals it, but that seems to run against all the browser support/infrastructure for async file-based loading. Also I'm curious what implications that would have for the cross-domain primitive of browser security.
So in order to fit the browser async loading model, we'll need to use a callback mechanism like:
require("something.js", function () { // called later, after something.js has loaded! })
It looks like RequireJS is doing this:
http://requirejs.org/docs/start.html
Perhaps give that a shot?
JavaScript environments like NodeJS etc - built with the provision of loading "local" modules from disk, instead of foreign network hosts - can do the synchronous load.
I would greatly appreciate any corrections from JS experts :-)

Categories