Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support native ES6 module loading #31944

Open
KubaSzostak opened this issue Oct 20, 2020 · 11 comments
Open

Support native ES6 module loading #31944

KubaSzostak opened this issue Oct 20, 2020 · 11 comments

Comments

@KubaSzostak
Copy link

According to v5.0 documentation Bootstrap provides a version of Bootstrap built as ESM (bootstrap.esm.js and bootstrap.esm.min.js) which allows you to use Bootstrap as a module in your browser. It's not true if you want to imort bootstrap.esm.js module in vanilla Javascript (tested in 5.0.0-alpha2). In documentation we have example with such module import:

import { Toast } from 'bootstrap.esm.min.js'

It wouldn't work in pure Javascript because so-called “bare” module specifiers are currently not supported in ECMAScript modules specification. Module specifiers must be full URLs, or relative URLs starting with /, ./, or ../. It's not a problem to set proper URL:

import { Toast } from './js/bootstrap.esm.min.js'

Unfortunately such import raises browser exception Uncaught TypeError: Failed to resolve module specifier "popper.js". Relative references must start with either "/", "./", or "../"..
image

Changing import Popper from 'popper.js' in to import Popper from './popper.js' doesn't help. Such import raises another browser exception Uncaught SyntaxError: The requested module './popper.js' does not provide an export named 'default'.
image

As far I find out Propper 1.16 doesn't provide native ES6 module. So for testing I commented out Propper import from bootstrap.esm.js source code:

// import Popper from 'popper.js';

And now native ES6 import system is working!

    <script type="module">
        import { Toast } from './js/bootstrap.esm.js'

        let toastElement = document.getElementById("toast");
        let toast = new Toast(toastElement);

        let buttonElement = document.getElementById("button");
        buttonElement.addEventListener("click", ()=> {
            toast.show();
        });        
    </script>

Demo page: http://localhost:5500/bootstrap-v5/bootstrap-v5.0.0-alpha2-esm-test/

I know removing Popper causes Dropdowns and Tooltips not working but for me it's not a problem. I think it would be great to have each component (Toast, Alert, Button, ...) in separate ESM module. Folks loving vanilla Javascript like me would be able to import classes that don't depend on Propper without modifying Bootstrap's source code.

I also think that writing in documentation that Bootstrap provides a version of Bootstrap built as ESM is misleading. It's rather UMD module.



Se also #28386 and #27119

@FezVrasta
Copy link
Contributor

Popper 1.6 does support ES modules

https://unpkg.com/browse/popper.js@1.16.1/dist/esm/

@XhmikosR
Copy link
Member

Any PRs to improve things are welcome. It's been a while since we set up ESM in v5 and things have progressed since then.

@septatrix
Copy link
Contributor

I do not see this working with popper in the near future. The best move preventing any confusion would be to adjust the docs to explain that this only works when using a bundler like webpack or similar.

The problem is that popper (even though it supports es modules) is not able to be resolved by the browser. The spec currently only covers relative references like /script.js and ./script.js but not specifiers like @popperjs/core which is used in the code. This leads to the aforementioned errors and the module becomes unusable.

The only way I know to fix this would be to use a module bundler which correctly resolves these modules, generate code which uses a relative reference (and therefore locks the location of the dependency in place) or just creating a module with all components except Popper. The first is what I propose we mention in the docs while the last one might be something we could consider for v5.1 or similar although I do not see a big benefit as bundler should solve this IIRC.

@ShivPandey
Copy link

How to use Bootstrap as a module in Angular Project?

@colecrouter
Copy link

I'm confused; isn't this supposed to be the EJS module? Why are there non-EJS things in the EJS-only module? Why has someone/something been compiling non-EJS compliant code into a specifically EJS-compliant CDN link for the past two years?

??? Can someone advise me?

@earcam
Copy link
Contributor

earcam commented Mar 14, 2022

This isn't an issue specific to Bootstrap - in order to distribute a library with external dependencies, some indirection around resolution is clearly needed (or otherwise hardcoded CDN URIs)

Native ES6 module loading in the browser does work, if you use an importmap

(... by native, I mean you'll need a shim for Firefox for a while.

<!doctype html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha256-YvdLHPgkqJ8DVUxjjnGVlMMJtNimJ6dYkowFFvp4kKs=" crossorigin="anonymous">
		<title>Hello, modularity!</title>
	</head>
	<body>
		<h1>Hello, modularity!</h1>
		<button id="pops" type="button" class="btn btn-primary btn-lg" class="btn btn-lg btn-danger" data-bs-toggle="popover" title="ESM in Browser" data-bs-content="Bang!">Pop, over</button>

		<script async src="https://cdn.jsdelivr.net/npm/es-module-shims@1.4.7/dist/es-module-shims.js" integrity="sha256-nSakAbR1isjLi28AfJqaTRXkE1yTLYyjx785E+nX9mo=" crossorigin="anonymous"></script>
		<script type="importmap">
		{
			"imports": {
				"@popperjs/core": "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.4/dist/esm/index.js",
				"@twbs/bootstrap": "https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.esm.min.js"
			}
		}
		</script>
	    <script type="module">
			import * as bootstrap from '@twbs/bootstrap'

			new bootstrap.Popover(document.getElementById('pops'))
		</script>
  </body>
</html>

The above works in Chrome without the es-module-shims script, and in both Chrome and Firefox as is.

@XhmikosR
Copy link
Member

@earcam could you open a PR to add this in docs please?

@earcam
Copy link
Contributor

earcam commented Mar 14, 2022

@XhmikosR, please see #36017 (pipes not yet run)

mdo pushed a commit that referenced this issue May 20, 2022
mdo pushed a commit that referenced this issue May 24, 2022
mdo added a commit that referenced this issue May 24, 2022
* Docs: dependencies mgmt for vanilla ESM in browser

Doc change for #31944

* Update javascript.md

* Update javascript.md

* Update .cspell.json

* Update javascript.md

* Update javascript.md

* rewrite

* Add link

* edit

* eslint-skip

Co-authored-by: Caspar MacRae <earcam@gmail.com>
Co-authored-by: XhmikosR <xhmikosr@gmail.com>
@XhmikosR
Copy link
Member

@GeoSot isn't this fixedx already on main by #36414 and/or 36854?

@lekoala
Copy link

lekoala commented Mar 7, 2023

Wouldn't it be much easier to import popper as a esm module rather than deal with import maps?

@RPGillespie6
Copy link

Here's how I did it:

  1. Download Bootstrap esm: https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.esm.min.js
  2. npm i esbuild
  3. npm i @popperjs/core
  4. npx esbuild --bundle bootstrap.esm.min.js --format=esm --minify --outfile=bootstrap.min.mjs

You can now import { Tooltip } from './bootstrap.min.mjs'; and it should work as expected in-browser

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Status: To do
Development

No branches or pull requests

10 participants