JavaScript || Using API with ES6 modules - javascript

I am currently practing using API with es6 Modules in JS (vanilla).
app.js
import Game from './model/Game';
const proxy = 'https://cors-anywhere.herokuapp.com/';
let key = 'MY_API_KEY_PRIVATE'; //kept private for StackOverflow
let steamID = '76561197996900532';
getOwnedGames();
async function getOwnedGames() {
try {
const result = await fetch(`${proxy}http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=${key}&steamid=${steamID}&format=json&include_appinfo=true`);
const data = await result.json();
let gamesList = data.response.games;
console.log(gamesList);
} catch(error) {
console.error(error);
}
}
.
Game.js
export class Game {
}
Now this works without using import GotGames from './model/Game';, but with it gets the following error :
Uncaught SyntaxError: Cannot use import statement outside a module
I have seen a similar problem in this space by adding type="module" in the script tag in HTML, but that gives the following error :
Access to script at 'my_file_path' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
Why has adding type="module" affected my API calls and how can this be done? Has this got something to do with requiring Node.js to install webpacks by any chance?

Yours is actually a very broad question, but I will try to clarify some important aspects of it.
-Spoiler Starts-
Webpack.
-Spoiler Ends-
Take into account that even if it is famous for not being beginner-friendly, some refer to it, together with Babel and React, as the bleeding edge of web development technologies. So it is worth to be studied at least once. In a few words, it will transform your ES modules to a custom system (which is written in plain old JavaScript) that will be compatible with technically all browsers and will preserve functionality along with many other advantages.
However, for simplicity, you may want to use a framework like React which uses Webpack under the hood, so you won't have to directly deal with it.
I really recommend you this Medium article to discover some interesting facts about the history of modules in JavaScript and NodeJS.
At the end, we will both agree that ES6 modules are the future, but then, even after resolving your specific issue, you will sadly find that ES6 modules are a very recent standard and that it currently has relatively poor browsers support.
Answering your question:
Browsers do not interpret JavasScript files as modules by default, so that is the reason why you have to explicitly declare them using type="module" attribute in your script tags. That is obviously why you got your first error.
To correctly import "Game.js" module, you have to re-write your import statement as follows: import {Game} from './model/Game'; because you made a named export. As written in comments, do some reading about import/export on MDN and it will be clearer for you.
Finally, CORS errors are likely to be caused by a server misconfiguration. Especially with headers. You may want to try setting Access-Control-Allow-Origin header to * or your specific server name, so new requests will have Origin header different than null.
I hope this will indicate you a good path to expand your knowledge.
* EDIT: To solve the issue from the comments about the error when click opening the file I would suggest to use the following not so known meta tag <meta http-equiv="Access-Control-Allow-Origin" content="*"> to emulate the http header when there is no server. I'm not sure if it will work, but technically it should, please let me know in the comments because I'm curious.
See this

It seems by adding default in export default class Game with type="module" included in the script, it seems to be working fine again.
Is there an explanation as to why this works ? so that I can understand this.
EDIT : It seems that this only works when the html file is run in LiveServer (VS Code ext which acts as a local server)
Whereas when I manually run (click open index.html) file the same error :
Access to script at 'file:///*path_to_.js_file*' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.

Related

JavaScript ES6 import syntax?

I am just testing the new import syntax. But I get a bit frustrated since the syntax I have to use does not seem to be what I have been reading. Why is the syntax below working (in Chrome, in an extension)?
import * as lib from "./lib-file.js";
What alternatives are there for this special syntax?
EDIT: I think I must clarify some things. I am trying to get this to work in a Chrome extension. The syntax above works in the "popup" script (in a script loaded from the html file there). I wrote this post because I was surprised that I needed the ".js" extension.
However then a new problem showed up. The above syntax does not work in a "content" script. The error I get is the same as the one I get from the popup when there is no type="module" in the script tag in the html file. (Uncaught SyntaxError: Unexpected token *) This looks like a bug when importing to a content script, but I am not at all sure. Am I missing something there?
EDIT 2: I just found this article which seems to tell how to fix these problems for now:
Using JavaScript ES6 import/export modules in Chrome Extensions https://medium.com/#martinnovk_22870/using-javascript-es6-import-export-modules-in-chrome-extensions-f63a3a0d2736
EDIT 3:
I just got a pointer to this utility. I notice that they do not use ES6 import in their suggested setup in the browser.
mozilla/webextension-polyfill: A lightweight polyfill library for Promise-based WebExtension APIs in Chrome https://github.com/mozilla/webextension-polyfill
You can use different import calls - maybe even require if your server is Node:
const lib = require("./lib-file");
import "./lib-file";
import libFile from "lib-file";
Note that in the third one, you need to export libFile from your lib-file.js.

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!');
});

import or require any library in browser console

While debugging application most of the time I feel like it would hve easier if I could include any library into browser console and try some of the function from that libraty.
Now in modern javascript/es6/es6/typescript world is there any thing short and quick available to to import a script instantly to the brower so that it can be used to directly
Example
While debugging, if I want Observable I should be able to do something like this
import {Observable} from 'rxjs/Observable'; //typescript way
const Observable= require('rxjs/Observable'); // require way
But these are not working.
already explored dynamic <script> tag
I already explore old way to dynamically using the <script> tag like below but its dificult for large number of libs and also not elegant
var jq = document.createElement('script');
jq.src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js";
document.getElementsByTagName('head')[0].appendChild(jq);
// ... give time for script to load, then type (or see below for non wait option)
jQuery.noConflict();
Some browsers (including Chrome) can use snippets in console, either as built-in features or extensions.
One-liner script to do this is
document.head.appendChild(Object.assign(
document.createElement('script'),
{ src: '...' }
));
Considering that most websites already have jQuery loaded (even if they don't, this can be handled by browser extensions, such as Chrome jQuery Injector) it can be shortened to:
$.getScript('...');
Any piece of code that should be always available in console can also be exposed as global function with user scripts (Tampermonkey in Chrome, etc), with some limitations imposed by user script security.
This will probably will be possible with dynamic import(), which is currently stage 3 proposal and isn't implemented in browsers.
A suitable solution that works for most major libraries the one may be curious to quickly experiment with is to navigate to official library website and open the console. Due to the fact that the websites often provide live examples, relevant variables are exposed to global scope. Known examples are $ for jQuery, angular for AngularJS, ng for Angular, Rx for RxJS, moment for Moment.js...

Log javascript errors from another subdomain

window.onerror in Firefox and Chrome seems to discard the real error message/location and always pass "Script error.", "", 0 when the offending script is on a different domain than the page itself. I have a site with separate www and static subdomains for pages and css/js, which renders error logging rather useless. Is there any way to enable the proper logging of such errors?
It doesn't sound like there's a workaround for this at the moment (I asked in the Mozilla IRC channel).
I've filed these bug reports to track this issue. Please chime in if you have opinions on what the best solution to this would be:
https://bugzilla.mozilla.org/show_bug.cgi?id=696301
https://bugs.webkit.org/show_bug.cgi?id=70574
The real solution is to use proper try { ... } catch(e) { ... } blocks in all code. However, I understand that may not always be an option.
If you don't have control over these other scripts, your next best option would be to load them as strings via JSONP, then use eval() (yes, I know eval is evil) to "inject" them into the current page. This way you'll still get the benefits of using a static domain (no cookies, CDN option, additional concurrent requests, etc), but the JS will end up being on the page's request domain.
Within your js file, on the top try doing
document.domain = "yourdomain.com"

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.

Categories