Add support for modules import in workers
Right now, there is no way to import external modules in workers.
When trying to import something it shows this
import code example
import React from "https://esm.sh/react";
(importing via CDN here, since worker only support single file)
Some ideas I thought:
- Somehow support ESM module import
- Enable upload worker as
.mjs(?), since that error message specifically talks about worker not being in a module
Haven't tried yet:
- Importing CommonJS with require
Not sure how possible this is without a bundler but I don't think react in a worker makes sense in the first place really, isn't that a front end framework? Or is this some ssg thing
actually, the react is just an example module, since that was what i copied from this ESM import via CDN https://esm.sh/
for my case I needed the xmldom lib (https://esm.sh/@xmldom/xmldom)
The point of the issue was to enable external module import 🙏
i don't know if it can help, but i found this https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts
I'm not sure if this will work, probably not, but what happens if you do
const module = await import(someURL)
dynamic imports might work
sure, i'll try several variations and let you know
update on this @ProgrammerIn-wonderland all the variations of trying to import, in this worker https://rey-import.puter.work
unfortunately none of them work
- basic import esm fails, because worker is not inside a module
- dynamic import via import() doesn't work, no such module
- service worker
importScripts()doesn't work, not defined - hack using fetch + eval, cannot run eval inside worker
- esm hack, using data url for dynamic import, does not work, no such module
// import {nanoid} from "https://cdn.jsdelivr.net/npm/dayjs@1/+esm";
// above would cause the following error:
// Failed to deploy rey-import! Uncaught SyntaxError: Cannot use import statement outside a module at worker.js:1
router.get('/', () => {
return "Endpoints:\n/esm\n/import-scripts\n/eval\n/esm-hack"
})
router.get('/esm', async ({request}) => {
const {nanoid} = await import('https://cdn.jsdelivr.net/npm/dayjs@1/+esm');
return 'esm';
});
router.get('/import-scripts', ({request}) => {
importScripts("https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js");
return 'importScripts';
});
router.get('/eval', async ({request, params}) => {
const res = await fetch('https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js');
const code = await res.text();
(0, eval)(code); // run in global scope
return 'eval';
});
router.get('/esm-hack', async ({request, params}) => {
const mod = await import(
'data:text/javascript,' +
encodeURIComponent('export const echo = (m) => console.log("[echo]", m);')
);
mod.echo('hello from data: url');
return 'esm-hack';
});
knowing that our worker is built on top of cf workers, their solution for this is the packages must be bundled with the worker and it's doing something like wrangler deploy
personally i think this is annoying, and i wonder if we can just automatically detect cdn import, and build that into the worker
like if we are doing the following
import {nanoid} from "https://cdn.jsdelivr.net/npm/dayjs@1/+esm";
router.get('/esm', async ({request}) => {
console.log(nanoid())
return 'esm';
});
although i'm not sure how feasible this is, so i'll just hear what you think
I'm unsure either, I'll probably need to look into this more deeply. If we have to webpack on every upload then we can no longer do it every save for performance reasons so it's one of those things which feels like walking on landmines
if anyone's looking for how to import module in workers, for now you can follow this demo https://github.com/reynaldichernando/workers-import-demo/
basically, setup your worker as nodejs project, where you can import libraries as normally
then build it using esbuild where it will bundle all your imported library into a single worker.js that you can use in puter