Importing ES2015 module in Safari DP requires file extension - javascript

I'm currently testing ES2015 coverage on Safari Developer Preview (which claims to support 100% ES2015, modules included).
I've made a simple test, using the same syntax I've been using regularly when developing using ES2015 code (along with Babel.JS for transpiling and Browserify for bundling).
Unexpectedly my code wouldn't work without including the .js extension in the import statement. Is that standard behavior? I thought you could omit that.
/* filename: scripts/alert.js */
export default class Alert {
constructor(message) {
this.message = message;
}
show() {
alert(this.message);
}
}
// Another file
/* filename: scripts/index.js */
import Alert from "./alert.js"; // this won't work if I change it to 'import Alert from "./alert";'
(new Alert("Hello, World!")).show();
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>ES2015 Modules</title>
</head>
<body>
<h1>ES2015 Modules</h1>
<script async="async" type="module" src"scripts/index.js">
</script>
</body>
</html>

Unexpectedly my code wouldn't work without including the .js extension in the import statement. Is that standard behavior? I thought you could omit that.
It's not the browser's job to second-guess what that resource specifier means to the server. You can certainly configure your server to respond to the GET without the .js by delivering a matching file that has .js, but that's server configuration.
There's likely to be evolution in this regard. For instance, right now the spec requires that a module resource specifier start with either / or ./. This is specifically so that...
...in the future we can allow custom module loaders to give special meaning to "bare" import specifiers, like import "jquery" or import "web/crypto". For now any such imports will fail, instead of being treated as relative URLs.

Related

"Import" using ESM from CDN throwing errors (Picmo)

I am trying to install the Popup picker from Picmojs but I am limited by the following:
I can't modify any file as I am integrating the code in a co-code builder (no access to index.js,..) but can host files
I can't use NPM nor Yarn as I am working in the browser
Here's what the documentation says
Use ESM from CDN You can also import the ESM version of PicMo
directly. You will first to create an ES module that imports PicMo:
index.js import { createPicker } from 'https://unpkg.com/picmo#latest/dist/index.js';
createPicker(...);
Then you can import the local module from a script tag:
<script type="module" src="index.js"></script>
I indeed tried in a Fiddle (even without the script that as I didn't understand this part) and it seems to be working all fine: Link to Fiddle
However, this relates to the createPicker function, but I'm interested in the Popup picker and therefore need the createPopup function.
According to their documentation:
A popup picker is not displayed until it is triggered by clicking on a
popup trigger, usually a button.
To use a popup picker, you must first install the #picmo/popup-picker
package. This package contains the createPopup function.
createPopup(pickerOptions: PickerOptions, popupOptions: PopupOptions):PopupPickerController
-> I don't know how to "first install the #picmo/popup-picker package".
Here's what I tried based on the previous working example:
import { createPopup } from 'https://unpkg.com/#picmo/popup-picker#latest/dist/umd/picmo-popup.js';
Link to Fiddle
But I always get the same error: "<a class='gotoLine' href='#43:10'>43:10</a> Uncaught SyntaxError: The requested module 'https://unpkg.com/#picmo/popup-picker#latest/dist/umd/picmo-popup.js' does not provide an export named 'createPopup'"
Any hint for me? I'm really stuck on this part.
You're trying to use the UMD version of the library, but you can't use UMD with ESM.
Based on your URL, I tried https://unpkg.com/#picmo/popup-picker#5.4.0/dist/index.js which gave me this error:
TypeError: Failed to resolve module specifier "picmo". Relative references must start with either "/", "./", or "../".
That tells us that the file uses import ____ from "picmo", which won't work in the browser without an import map. If your target browsers support them, we can do that like this:
<script type="importmap">
{
"imports": {
"picmo": "https://unpkg.com/picmo#5.4.2/dist/index.js",
"#picmo/popup-picker": "https://unpkg.com/#picmo/popup-picker#5.4.0/dist/index.js"
}
}
</script>
<script type="module">
import { createPopup } from "#picmo/popup-picker";
console.log(typeof createPopup);
</script>
...but sadly as I write this import maps are just supported by Chromium-based browsers like Chrome, Edge, and Opera, not Firefox or Safari.
If you need to target those as well, the import ____ from "picmo" would seem like an insurmountable barrier, but unpkg.com has a feature to "expand" bare imports. From the unpkg home page:
Query Parameters
?meta
Return metadata about any file in a package as JSON (e.g. /any/file?meta)
?module
Expands all “bare” import specifiers in JavaScript modules to unpkg URLs. This feature is very experimental
And indeed, adding ?module to that URL to ask unpkg to do that for us works:
<script type="module">
import { createPopup } from "https://unpkg.com/#picmo/popup-picker#5.4.0/dist/index.js?module";
console.log(typeof createPopup);
</script>
Since that imports picmo, you'll want to watch the network tab to see what exact URL is used to import picmo and use that if you need to import it in your code (so you're getting the same instance). For instance, when I did that just now it requested https://unpkg.com/picmo#%5E5.0.1?module and got a redirect pointing to https://unpkg.com/picmo#5.4.2?module which also returned a redirect pointing to https://unpkg.com/picmo#5.4.2/dist/index.js?module. That would suggest you want to import picmo from https://unpkg.com/picmo#%5E5.0.1?module (the first URL, since that's what the JavaScript engine will have seen), but you'll need to experiment to be sure.
All of that aside, it's well worth dropping them a note asking whether this is really how you should do it and/or asking for a way that doesn't rely on a "very experimental" feature of unpkg.com.
(Source #joeattardi from Github)
You're using the UMD version of the module with an import statement. This won't work; only ES modules work with imports. UMD modules are loaded with a script tag.
But then you are mixing an ESM import (your import of the main picmo package) with a UMD, which won't work either.
Two options:
Use the UMD version of the base picmo package as well, both loaded with script tags.
Use the ESM distribution of #picmo/popup-picker from unpkg. Note that you will need to add ?module to the URL, otherwise your browser will likely give an error about an invalid relative path. If you do it this way you don't need the picmo import since the popup module imports it.
So for the ESM route, you need just a single import:
import { createPopup } from 'https://unpkg.com/#picmo/popup-picker#latest/dist/index.js?module';

Website is not loading the script, CSS, or Three.js

Let me start out by saying that I have very little experience with HTML, javascript and overall website creation and hosting, so sorry in advance if the information I am providing is lacking.
I am trying to make a website by using a 3d object from three.js, however, nothing is loading in the 'live server' (when I upload the entire website to Cpanel), however, when I use visual studio code to run it through my local server (through the command npm run dev) the website is showing as intended. I have screenshotted the pages:
correct page
incorrect page
When I open the element inspect on the broken page, I get the following error through the console:
Failed to load module script: Expected a JavaScript module script but the server responded with a
MIME type of "text/css". Strict MIME type checking is enforced for
module scripts per HTML spec.
and
Uncaught SyntaxError: Cannot use import statement outside a module
i have the following code in my script.js:
import './style.css'
import * as THREE from '../node_modules/three/build/three.module.js'
import { OrbitControls } from '../node_modules/three/examples/jsm/controls/OrbitControls.js'
import { GLTFLoader } from '../node_modules/three/examples/jsm/loaders/GLTFLoader.js'
import { HemisphereLight, Sphere, SRGB8_ALPHA8_ASTC_12x12_Format, sRGBEncoding, Texture, TextureEncoding } from '../node_modules/three/build/three.module.js'
import gsap from '../node_modules/gsap/all.js'
var gltfLoader = new GLTFLoader()
let tl = gsap.timeline()
var diamond = null
I am also using this to call the script in the index.html, however, I am uncertain if this is the correct way of calling the script.
<script type=module src="script.js"></script>
How would I be able to fix this? any help would be appreciated!
No.
Understand that the browser import functionality is very different than that of Node, or development with a bundler like Webpack. In browser imports, scripts have to be of type module (thus causing the cannot use import statement out of module error) <script type="module" ... (with the quotes!). You also need to reference an import file starting with ./, ../, or / (which you already are doing). Finally, you may only import JavaScript files, not CSS.
You have two options:
Use a bundler like Webpack to compile your files into a single one (and remove the import statements)
(preferred) Remove import './style.css' and add <link rel="stylesheet" href="./style.css" type="text/css" /> in your HTML <head>

How to use vaadin-text-field in script module

I have tried to use vaadin-text-field in a script module, but it fails with the following message
Uncaught TypeError: Failed to resolve module specifier "#vaadin/vaadin-lumo-styles/color.js". Relative references must start with either "/", "./", or "../".
Now I know that "Bare" import specifiers aren't supported in ES6
But is there a way to make this work without hacking on the component's imports.
I mean locally of course
Here is my code :
<!doctype html>
<html>
<head>
<!-- Polyfills only needed for Firefox and Edge. -->
<script src="node_modules/#webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
</head>
<body>
<script type="module">
import {PolymerElement, html} from './node_modules/#polymer/polymer/polymer-element.js';
import './node_modules/#vaadin/vaadin-text-field/theme/lumo/vaadin-text-field.js';
class MyElement extends PolymerElement {
static get properties() { return { }}
static get template() {
return html`
<vaadin-text-field></vaadin-text-field>
`;
}
}
customElements.define('my-element', MyElement);
</script>
<my-element></my-element>
</body>
</html>
Note: I am using server to serve the file not polymer CLI
I found that serving the file with polymer serve is the fastest way to solve the problem.
According to Polymer's Documentation
The browser accepts only one kind of module specifier in an import
statement: a URL, which must be either fully-qualified, or a path
starting with /, ./ or ../. This works fine for importing
application-specific elements and modules:
However, it's challenging when you're writing a reusable component,
and you want to import a peer dependency installed using npm. The path
may vary depending on how the components are installed. So Polymer
supports the use of Node-style named import specifiers
Where #polymer/polymer is the name of the npm package. (This style of
specifier is sometimes called a "bare module specifier".)
These module specifiers need to be transformed to paths before they're
served to the browser. The Polymer CLI can transform them at build
time, and the Polymer development server can transform them at
runtime, so you can test code without a build step. Many third-party
build tools, like WebPack and Rollup also support named modules.

"import" unexpected token? (chrome 62)

While trying to troubleshoot why systemjs didn't find a custom library I installed (could be a follow up question) I was stuck when trying to do things "manually".
So I have a simple system that consists of 3 files:
index.html
hi.js
hi2.js
the index is just:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<script src="hi.js"></script>
</body>
</html>
hi.js:
import * as hi from "hi2.js";
hi.myFunction();
hi2.js:
function myFunction() {
alert('hi')
}
export { myFunction };
Now when I run (using webstorm and chrome 62) above code, I get the following error, reported by the (chrome) debugger:
"Uncaught SyntaxError: Unexpected token import"
What's happening here? I checked for javascript compliance on mdn and it tells me import is supported by chrome 61 and newer. - I use chrome 62 for testing this.
So what's going on, and how to make it work?
Per recomendation I also changed the html line to <script type="module" src="hi.js"></script>. That didn't help at all, same error.
You're correct that you need type="module" on your script tag:
<script src="hi.js" type="module"></script>
<!-- ---------------^^^^^^^^^^^^^ -->
You also need a ./ prefix on your module specifier:
import * as hi from "./hi2.js";
// ------------------^^
This is to leave the door open for a specifier with no path at all to have special meaning at some stage as things evolve. From the WHAT-WG spec:
This restriction (that a relative URL specifier must start with /, ./, or ../ – T.J.) is in place so that in the future we can allow custom module loaders to give special meaning to "bare" import specifiers, like import "jquery" or import "web/crypto". For now any such imports will fail, instead of being treated as relative URLs.
When I make those two changes to your files, with Chrome 62 with no experimental flags set, I get the alert.

ES6: import module from URL

Is it possible to import javascript module from external url in ES6?
I tried (using babel-node):
import mymodule from 'http://...mysite.../myscript.js';
// Error: Cannot find module 'http://...mysite.../myscript.js'
2018 Update: The module loader spec is now a part of the ES Spec - what you are describing is allowed and possible with <script type="module"> in browsers and with a custom --loader with Node.js as well as with Deno if you're into that.
The module loader spec and the import/export syntax are separate. So this is a property of the module loader (not a part of the ES spec). If you use a module loader that supports plugins like SystemJS.
Update in 2022, it seems it works at least in latest Chrome, Firefox and Safari as of now, as long as the server provides a response header of content-type: application/javascript; charset=utf-8 for the js file.
Try these two files with a vanilla web server:
index.html
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<title>Hello World</title>
<script type="module" src="./hello.js"></script>
</head>
<body>
</body>
</html>
hello.js
import ip6 from 'https://cdn.jsdelivr.net/gh/elgs/ip6/ip6.js';
const el = document.createElement('h1');
const words = "::1";
const text = document.createTextNode(ip6.normalize(words));
el.appendChild(text);
document.body.appendChild(el);
This is a HUGE deal! Because we can say bye to Webpack now. I am a little too excited now!
You could also use scriptjs which in my case requires less configs.
var scriptjs = require('scriptjs');
scriptjs('https://api.mapbox.com/mapbox.js/v3.0.1/mapbox.standalone.js', function() {
L.mapbox.accessToken = 'MyToken';
});
TL;DR:
For now, no.
Long answer:
There are two different specs: the ES6 defines the syntax to exporting/importing.
And there is the Loader Spec that actually defines how this modules will load.
Spec-speak aside, the important part for us developers is:
The JavaScript Loader allows host environments, like Node.js and browsers, to fetch and load modules on demand. It provides a hookable pipeline, to allow front-end packaging solutions like Browserify, WebPack and jspm to hook into the loading process.
This division provides a single format that developers can use in all JavaScript environments, and a separate loading mechanism for each environment. For example, a Node Loader would load its modules from the file system, using its own module lookup algorithm, while a Browser Loader would fetch modules and use browser-supplied packaging formats.
(...)
The primary goal is to make as much of this process as possible consistent between Node and Browser environments. For example, if a JavaScript program wants to translate .coffee files to JavaScript on the fly, the Loader defines a "translate" hook that can be used. This allows programs to participate in the loading process, even though some details (specifically, the process of getting a particular module from its host-defined storage) will be different between environments.
So we depend on the host environment (node, browser, babel, etc) to resolve/load the modules for us and provide hooks to the process.
The specification describes how exactly a module specifier in import is resolved:
https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier
It says URLs are allowed, both absolute and relative ones (starting with /, ./, ../), and it does not differentiate between static and dynamic imports. Further in the text, there's an "Example" box showing examples of valid specifiers:
https://example.com/apples.mjs
http:example.com\pears.js (becomes http://example.com/pears.js as step 1 parses with no base URL)
//example.com/bananas
./strawberries.mjs.cgi
../lychees
/limes.jsx
data:text/javascript,export default 'grapes';
blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f
example in pure javascript how to import google code and replace element on any page to google translate button (can be run from browser debug console for any site you want)
importScriptURI("https://translate.google.com/translate_a/element.js");
document.getElementsByTagName("h1")[0].innerHTML='<div id="google_translate_element"></div>';
setTimeout(()=>{ new google.translate.TranslateElement({pageLanguage: 'en'},'google_translate_element');},1000);

Categories