In this project some experiments are done with the following concepts:
- dynamic imports
- import maps
These two are interlinked in the situation where modules that are dynamically imported, themselves import modules and resolution needs to happen in the browser. Question - is there an alternative where resolution happens in a different way (maybe with a NodeJS backend)?
The basics of this example are relatively simple on purpose, and consists of:
- Two projects
- root project (which mimics an actual application)
- packages/dynamic project (which mimics an extension/plugin that needs to be loaded through dynamic imports)
A very simple project that uses moment
as a package import and uses the function now()
to return current date time.
The project consists of:
- vite.config.js: to drive vite bundler for
library mode
(goal is to create an ES6 module) - index.html, src/main.ts: these are used to 'test' drive the module in isolation
- src/dynamic.ts and src/index.ts: these are actually the artefacts from which this module is build
You can build the project through: npm run build
.
This will result in an ES6 module in the dist
directory.
Note that in vite.config.js
we declare moment
as an external dependency (to be loaded in 'some way' == import maps).
This is again a very simple project that dynamically imports
the aformentioned dynamic module.
For this purpose in vite.config.js (in the root) we define a proxy location.
This proxy is actually pointing to a location contained by remote-extensions
folder, also present in the root.
This remote-extensions
folder has an index.html
file, which needs to be started with LiveServer
.
As such LiveServer
will serve the ES6 module created in the former project.
The project consists of:
- vite.config.js: to drive vite, mainly for proxying resolution of the dynamically imported ES6 module.
- index.html: which does a couple of things
- it loads a
import map
shim to support browsers that not natively support this feature (e.g. FF) - it declares the import map to
moment
, in an attempt to satisfy the externally defined dependency on it by thedynamic extension
. - it provides the slot for the
main.ts
module to complete the application
- it loads a
- main.ts: then basically declares the
dynamic import
and console logs some output on success
let module = 'dynamic';
import(/* @vite-ignore */ `/ext/${module}/dist/extension-${module}.esm.js`)
.then((m) => m.default)
.then(() => {
console.info('loaded');
});
Steps to reproduce:
-
go into
packages/dynamic
and runnpm run build
-
in that same
packages/dynamic
folder, runnpm run copy
This will copy the ES6 module toremote-extensions
folder -
go to the
remote-extensions
folder and selectindex.html
, after which you startLiveServer
This will expose a location fromremote-extensions
(Check: http://localhost:5500/remote-extensions/) -
go to
root
folder and runpnpm run dev
\- in Google Chrome this will result in this error:
An import map is added after module script load was triggered.
This is probably because vitejs injects a piece of JavaScript at the very top of the index.html which chokes import maps.
Regardless of whether the shim is present or not (Chrome and Edge have standard import maps support). - in FF this will result in this error:
Uncaught (in promise) TypeError: Error resolving module specifier “moment”. Relative module specifiers must start with “./”, “../” or “/”.
This is (also) strange as the import map is there to resolve exactly this problem.
- in Google Chrome this will result in this error:
-
go to
root
folder and runpnpm run build
This will create a distribution of the project. -
go to the
root
folder and runpnpm run preview
\- in Google CHrome this will result in this error:
Uncaught (in promise) SyntaxError: The requested module 'moment' does not provide an export named 'default'
At the same time the network tab actually shows that resolution seems to work (but not really)
- in FF this will (again) result in the same error as is shown for FF at nr. 4
- in Google CHrome this will result in this error: