Using a full URL in a dynamic import()
Asked Answered
F

3

32

Is it possible to use a full URL in a dynamic import() statement in ES6/Typescript?

import('https://foo.com/mymodule.js').then(() => {
    console.log('mymodule is loaded');
});

I get an error

//Cannot find module 'https://foo.com/mymodule.js'.

With Webpack and Typescript, we're already successfully using a relative path with a dynamic import

import(/* webpackChunkName: "mymodule" */ '../mymodule');

so it seems that Webpack already does module loading at runtime.

Frowsy answered 30/4, 2018 at 9:9 Comment(3)
I don't think Webpack supports loading modules over HTTP (yet).Deb
This is not safe at all, you should not do it IMO. This is as evil as eval.Kutch
In the meantime I got around this by using github.com/ded/script.jsFrowsy
A
21

ES2020 introduces a new function-like syntax for import, so-called "dynamic imports" permitting the dynamic import of JavaScript modules. The precise implementation of the importing process is left to the host (eg the browser, or Node.js), but modern web browsers do implement dynamic loading over HTTP using this syntax, with the module identified using a URL:

// foo.js at example.com
export function foo() {
    return 'this is foo'
}

// bar.js, running in the client
const { foo } = await import('http://example.com/my-module.js')
foo() // "this is foo"

Note that there are CORS and MIME-type constraints that you need to bear in mind. This and that are relevant.

Amman answered 16/3, 2020 at 15:44 Comment(3)
I'm afraid I couldn't easily follow the content at those links (although I'm sure the information is there) ... is there an "in a nutshell" version of the MIME constraints?Denudation
If the URL is dynamic, how do you tell webpack to leave this import function alone and let the browser handle it?Neuromuscular
@ShawnMclean I had the same issue, the answer is import(/* webpackIgnore: true */'...') - see webpack.js.org/api/module-methods/#magic-commentsRepresent
A
11

Pure ES6 module without bundler

Most of the browsers now support ES6 modules natively. You need to define type="module" in the script tag.

<!DOCTYPE html>
<html>
<head>
    <title>Test</title>
    <script type="module">
        import camelCase from "http://localhost:40080/camelCase.js";
        import * as systemX from "https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.21.4/system.js";
        console.log(systemX);
        console.log(camelCase);
    </script>
</head>
<body>
    <h1>Hello</h1>
</body>
</html>

Alternatively, you can link to file as well.

<!-- move all previoous code to main.js -->
<script type="module" src="./main.js"></script>

Further reading:

https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/

Webpack

I tested the below trick and it works.

import(/* webpackIgnore: true */ 'http://example.com/some-module/some-module.bundle.js').then(module => console.log(module.default));

https://github.com/webpack/webpack/issues/8341#issuecomment-436550381

**Please note that CORS must be allowed from the webserver for both use cases.

Appellant answered 13/11, 2021 at 4:0 Comment(1)
If you are in Angular, use /* webpackIgnore: true */ for absolute urls!Making
J
7

ES modules aren't guaranteed to support URLs neither in static import nor in dynamic import(). Current browser versions (Chrome-based, Firefox, Safari) support URLs for both static and dynamic import, the implementation relies on CORS mechanism and may vary. Node.js supports this only with custom ESM loader, it's unlikely that it will allow this by default in future because of security concerns.

It may be possible to handle HTTP transport in Webpack via third-party plugin.

Script loading can be handled by SystemJS:

System.config({
  paths: {
    'mymodule': 'https://example.com/mymodule.js'
  }
});

System.import('mymodule')
.then(mymodule => {
  ...
})
.catch(error => console.error(error));

Or:

System.import('https://example.com/mymodule.js')
.then(mymodule => {
  ...
})
.catch(error => console.error(error));

SystemJS also provides capabilities to process loaded scripts when necessary, e.g. a way in which modules should be handled in these scripts.

Johnnajohnnie answered 30/4, 2018 at 15:38 Comment(9)
Your second example using System.import('https://example.com/mymodule.js') is not working and is throwing me and error at build time: Module not found: Error: Can't resolve 'https://example.com/mymodule.js' 🤔Rete
@YvesM. This depends on the module, I guess. It works, plnkr.co/edit/JQnq5HTAKAY2fongEzb2?p=previewJohnnajohnnie
Yes but my problem is much more about my code being transpiled by Babel and bundle by Webpack. And Babel looks like transforming all System.importRete
Glad you figured out what the problem is. There's github.com/thgreasi/babel-plugin-system-import-transformer but it isn't enabled unless you did this explicitly, more likely it's Webpack. Try something like System['import'](...) or let SystemJs = System; SystemJs.import(...) to disable it or check webpack.js.org/api/module-methods/#magic-commentsJohnnajohnnie
@EstusFlask This link indicates that URLs may be used in dynamic imports. Has this changed since your answer was written? v8.dev/features/dynamic-importAmman
@52d6c6af To my knowledge, it wasn't for Webpack. Current browsers support this, at least Chrome and FF (the link you listed refers to Chrome-based browsers). Node doesn't.Johnnajohnnie
To clarify: it would seem the first sentence of this answer is now out of date? ES modules don't support URLs neither in static import nor in dynamic import(). The "function" syntax of import(<url>) supports URLs.Amman
@52d6c6af I have no proof that this behaviour is a part of specs. ES modules != browser support. Currently it seems that this is an initiative from browser vendors that has a chance to end in specs. I'd be happy to clarify this. I updated the post.Johnnajohnnie
I think this and that are relevant. Basically, I think the host environment can define how module specifier strings are resolved.Amman

© 2022 - 2024 — McMap. All rights reserved.