Open mizchi opened 5 years ago
Note for folks finding this: this is a proposal,
inline
doesn't yet exist
This isn't because of CORS, but rather because sites like google.com tend to disallow all subresources using something like a Content Security Policy. The issue with CSP is that most configurations you find in the wild also block Blob and Data URL sources, so none of these solutions would work.
CSP issues aside, I wonder if your use-case an inline Blob would work? It would be easy to add an option for this in worker-plugin
. It would look something like this:
input: (your source code)
const w = new Worker('./my-worker.js', {
type: 'module',
inline: true // <-- special property observed by the plugin
})
compiled output:
var w = new Worker(URL.createObjectURL(
new Blob(["onmessage=e=>postMessage('pong')"])
));
// ^ bundled worker code compiled into main JS as a string
@developit I tried setup you suggested but it doesn't work.
const worker = new Worker('./worker.js', {
inline: true,
type: 'module'
});
ia have next error:
Uncaught DOMException: Failed to construct 'Worker': ..... cannot be accessed from origin......
@developit Adding an inline
option would be great! I came from using workerize-loader
, then comlink-loader
, and now trying to move to worker-plugin
+ comlink
because the formers are no longer actively maintained. This is the only thing keeping me from moving.
Same here - inline mode is the one thing stopping me from moving from the seemingly unmaintained worker-loader.
Does anyone have a solution for this? The inline flag doesn't seem to do anything, compiled code just ends up like this:
var s=new Worker(e,{inline:!0})
For the folks commenting about inline
not working - I was proposing that feature, it is not something WorkerPlugin currently implements.
Building transparent Blob/inline support is likely possible, but in the meantime I would suggest using something like this to patch Worker for your use-case:
function Worker(url, opts) {
return self.Worker('data:,importScripts('+JSON.stringify(url)+')', opts);
}
new Worker("./my-worker.js", { type: "module" });
The result will be a Worker instantiated via a data URL (which will mean it has an opaque origin), where the bundled worker script is fetched via importScripts. This may help with CSP, since the request will be validated as "script-src", not "worker-src".
Honestly though, most websites that ship restrictive CSP's also disable Blob, Data URL and eval() scripts. I don't think any solution that tries to "work around" this is going to help much.
@developit
Thanks for your answer, I think the inline
feature you propose would be a great enhancement for my use case at least
I tried using the workaround you mentioned but that still couldn't bypass the error
Blobs apparently bypass it successfully though, I've been using worker-loader's blobs so far and they work just fine
But as some of the other comments here mention, if possible, I would rather drop the unmaintained worker-loader for this plugin so it would be amazing if the inline feature became a reality
@alangdm try this version:
function Worker(url, opts) {
var blob = new Blob(['importScripts('+JSON.stringify(url)+')'], { type: 'text/javascript' });
return self.Worker(URL.createObjectURL(blob), opts);
}
new Worker("./my-worker.js", { type: "module" });
The issue I have with the inline
option I proposed above is that "inline"
isn't spec'd anywhere and only works if bundled. It breaks the premise of this plugin, which is that it transparently bundles Module Workers.
@developit
I tried doing it as you said but got an error regarding the usage of new
, I changed it slightly and the closest I got was by doing this:
export function Worker(url, opts) {
var blob = new Blob(["importScripts(" + JSON.stringify(url) + ")"], {
type: "text/javascript"
});
return new self.Worker(URL.createObjectURL(blob), opts);
}
new Worker("../workers/my.worker.js", {
type: "module"
});
Which threw this error in Chrome:
Uncaught TypeError: Failed to execute 'importScripts' on 'WorkerGlobalScope':
Module scripts don't support importScripts().
Doing it like this also didn't seem to be bundling the worker at all though 😢
The issue I have with the inline option I proposed above is that "inline" isn't spec'd anywhere and only works if bundled. It breaks the premise of this plugin, which is that it transparently bundles Module Workers.
To be honest I agree with you on this, I don't really like that kind of non-standard syntax, but Blobs seem to be the only option for use cases like mine and some of the other people who commented before me
@developit You can pretty much ignore my last comment, I managed to get this working, thanks a lot!! 💯
The important steps are: Add the following to a script that's directly on the html:
(function() {
var _Worker = window.Worker;
window.Worker = function (url, opts) {
var blob = new Blob(["importScripts(" + JSON.stringify(url) + ")"], {
type: "text/javascript"
});
return new _Worker(URL.createObjectURL(blob), opts);
}
})();
And on the code actually getting bundled just use it as normally recommended:
new Worker("./my-worker.js", {
type: "module"
});
(My problem on the last comment was that I was adding the fix as part of the bundled code, that didn't go well)
I didn't make it work from the first time, so just wanted to clarify that it's not necessary to add that script directly to HTML. It's a hack where you just replace native Worker with your own implementation (with importScript that won't suffer from CORS). So it's ok to just have that in your code:
const _Worker = window.Worker;
window.Worker = function (url, opts) {
const blob = new Blob(["importScripts(" + JSON.stringify(url) + ")"], {
type: "text/javascript"
});
return new _Worker(URL.createObjectURL(blob), opts);
}
// worker-plugin magic still works,
// but now you can use CORS, you can specify webpack's publicPath pointing to CDN
new Worker("./my-worker.js", {
type: "module"
});
window.Worker = _Worker // put it back to not break any other worker usage
FWIW I'd be open to adding an option to worker-plugin that outputs the shimmed code.
Better yet, an option to specify "here's how to get the Worker constructor", like:
// to use Node worker_threads:
new WorkerPlugin({
workerConstructor: `require('web-worker')`
})
// to use the importScripts workaround for CORS/preload:
new WorkerPlugin({
workerConstructor: `(function(u,o){return new Worker(URL.createObjectURL(new Blob(['importScripts('+JSON.stringify(u)+')'])),o)})`
})
@developit You can pretty much ignore my last comment, I managed to get this working, thanks a lot!! 💯
The important steps are: Add the following to a script that's directly on the html:
(function() { var _Worker = window.Worker; window.Worker = function (url, opts) { var blob = new Blob(["importScripts(" + JSON.stringify(url) + ")"], { type: "text/javascript" }); return new _Worker(URL.createObjectURL(blob), opts); } })();
And on the code actually getting bundled just use it as normally recommended:
new Worker("./my-worker.js", { type: "module" });
(My problem on the last comment was that I was adding the fix as part of the bundled code, that didn't go well)
this works for me, with protocal. url =location.protocol + url;
I tried to publish 3rd party script with webpack and
worker-plugin
.I set
output.publicPath
to "https://cdn.example.com/" in this case;But I can not exec worker because of cross domain restriction.
I know this fallback works to avoid it.
but publisher need to add CORS header to fetch. (Most CDN have CORS header)
I will fork and try it at first in my hand.