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 worklet #11543

Open
alexander-akait opened this issue Sep 28, 2020 · 113 comments
Open

support worklet #11543

alexander-akait opened this issue Sep 28, 2020 · 113 comments

Comments

@alexander-akait
Copy link
Member

Feature request

// Worklets are always modules
myAudioContext.audioWorklet.addModule('./audio-worklet.js')
CSS.paintWorklet.addModule('./paint-worklet.js')
CSS.layoutWorklet.addModule('./layout-worklet.js')
CSS.animationWorklet.addModule('./animation-worklet.js')

What is the expected behavior?

Working with worklets out of box

What is motivation or use case for adding/changing the behavior?

Why not?

How should this be implemented in your opinion?

Like web workers, maybe under options

Are you willing to work on this yourself?

Not right now

@vankop
Copy link
Member

vankop commented Sep 29, 2020

hm.. I think parser.worker should work here.. Since structure should be <defined path>(new URL(...))

@sokra
Copy link
Member

sokra commented Sep 29, 2020

hm.. I think parser.worker should work here.. Since structure should be <defined path>(new URL(...))

yep, expect for the audio worklet, that should work.

@vankop
Copy link
Member

vankop commented Sep 29, 2020

for "base" audio there is https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/audioWorklet

for custom thing there is a workaround to
MyAwesomeWorker.js

export default whateverWorker.a.b;

index.js

import Worker from "./MyAwesomeWorker";
// use Worker

in parser.worker => ["Worker from ./MyAwesomeWorker"]

@alexander-akait
Copy link
Member Author

@vankop Maybe we can support it out of box as I written in the issue 😄

@vankop
Copy link
Member

vankop commented Sep 29, 2020

@sokra
Copy link
Member

sokra commented Sep 29, 2020

Which stage is the worklet spec? Is this final or still a proposal?

@alexander-akait
Copy link
Member Author

Working Draft https://developer.mozilla.org/en-US/docs/Web/API/Worklet, but many used them already, maybe improving docs is enough or using experimentals

@geekuillaume
Copy link

I've been trying to create a AudioWorklet with Webpack 5 for 3 hours and I cannot seem to find any way to make it work. Even by trying to add everything I could think of in parser.worker, nothing works. In some case the AudioWorklet file is imported as a normal JS file (and emit an error as the class AudioWorkletProcessor is not available in the main context), in other case the file is imported a a data URI with a video mimetype (is this the default?) and lastly, some time it throws this error:

Uncaught (in promise) ReferenceError: importScripts is not defined
    at Object.__webpack_require__.f.i (importScripts chunk loading:25)
    at ensure chunk:6
    at Array.reduce (<anonymous>)
    at Function.__webpack_require__.e (ensure chunk:5)
    at Function.__webpack_require__.x (startup chunk dependencies:3)
    at startup:2
    at startup:2

With Webpack 4 I can use https://github.com/reklawnos/worklet-loader but it's not compatible with Webpack 5. It would be useful to have a modular way to indicate that we want to create a new JS chunk when importing and return this chunk URL instead of the actual JS import. For now, I'm going to stick with Webpack 4 as it seems that there is no way to use Worklets with Webpack 5.

@alexander-akait
Copy link
Member Author

@geekuillaume Can you provide reproducible test repo?

@geekuillaume
Copy link

My bad, worklet-loader is working with Webpack 5 (with some deprecation warnings).

@alexander-akait
Copy link
Member Author

alexander-akait commented Oct 26, 2020

@geekuillaume Anyway can you provide example of the problem, I want to fix it, we should avoid worklet-loader in future in favor built-in support

@geekuillaume
Copy link

@evilebottnawi Here is the test case: https://github.com/geekuillaume/webpack-audioworklet-testcase

If you look at the console, you'll see this error:

Uncaught DOMException: Failed to construct 'AudioWorkletNode': AudioWorkletNode cannot be created: AudioWorklet does not have a valid AudioWorkletGlobalScope. Load a script via audioWorklet.addModule() first.

The src/audioworklet.js file is not emitted as a new entrypoint. It is directly loaded in the main scope.

@alexander-akait
Copy link
Member Author

alexander-akait commented Oct 26, 2020

@geekuillaume Do you use https (stupid question 😄 )?

@geekuillaume
Copy link

It is running on localhost, there is no protection about loading external module on local hosts. You can verify this as there is no warning or error in the console.

@alexander-akait
Copy link
Member Author

alexander-akait commented Oct 28, 2020

@geekuillaume Sorry you need https for this feature, try to use this feature without webpack - the same problem

@geekuillaume
Copy link

@evilebottnawi My bad, there was an error in my code, I was not waiting for the promise returned by addModule(). It's working now (even in http when running on 127.0.0.1). I still need to find out why it's not working in my other project but as the build system is quite complex this can be because of a number of things unrelated to this issue.

We can close this issue but it would be great to document this in the webpack doc.

@alexander-akait
Copy link
Member Author

@geekuillaume Let's keep open, because we need to add tests and improve docs after this

@skratchdot
Copy link
Contributor

If you use new URL() then it's treating the worklet as an asset, and the code is not bundled/transpiled. That means you can't import other files and have them bundled with the worklet. Is there a way to get around this?

Using worket-loader:
https://codesandbox.io/s/webpack-v5-audio-worklet-89vy3?file=/src/index.js

Using new URL():
https://codesandbox.io/s/webpack-v5-audio-worklet-forked-3292t?file=/src/index.js

(notice the 2nd example fails due to the import statement in the noise-generator.js file).

@alexander-akait
Copy link
Member Author

@skratchdot For this you need to specify parser.worker, so webpack can understand what it is not assets, it is entry point

@skratchdot
Copy link
Contributor

@skratchdot For this you need to specify parser.worker, so webpack can understand what it is not assets, it is entry point

I don't know how to do that. I've tried a few things and keep seeing errors. When I try examples higher up in the thread I see ReferenceError: AudioWorkletProcessor is not defined. It would be nice if someone could modify this example:
https://codesandbox.io/s/webpack-v5-audio-worklet-forked-3292t?file=/src/index.js
to use parser.worker appropriately.

@alexander-akait
Copy link
Member Author

Yep, I will update example in the next future

@alexander-akait
Copy link
Member Author

alexander-akait commented Dec 15, 2020

Sorry for delay, I think should work:

{
  module: {
    rules: [
      {
        test: /\.js$/,
        parser: {
          // For other use the same 
          worker: ["CSS.paintWorklet.addModule()", "..."]
        }
      }
    ]
  },
}

@chenxsan I think we can create small guide for worklets on docs site

@Doesntmeananything
Copy link

Sorry for delay, I think should work:

{
  module: {
    rules: [
      {
        test: /\.js$/,
        parser: {
          // For other use the same 
          worker: ["CSS.paintWorklet.addModule()", "..."]
        }
      }
    ]
  },
}

@alexander-akait Sorry for a dumb question, but how would you specify an audio worklet parser.worker?

@alexander-akait
Copy link
Member Author

alexander-akait commented Feb 15, 2021

myAudioContext.audioWorklet.addModule() (myAudioContext depend on now you named audio context), maybe we should improve this like ["*.audioWorklet.addModule()", "..."]

@FezVrasta
Copy link

Could somebody provide a complete example or link to some documentation please?

@thurinus
Copy link

thurinus commented Jun 8, 2023

@alexander-akait ah okay. I wasn't sure if the configs cited in #17212 had to be added explicitly, or if worklets are supported by default as long as webpack@5.85.0+ is installed (looks like I have 5.86.0).

I think I can use react-app-rewired to modify the webpack configs? Tried it, but it didn't seem to do anything. I might be doing it wrong, though. I've updated the sandbox repo with my changes if you'd like to take a look.

@TheLarkInn
Copy link
Member

@thurinus if you open an issue on CRA we can help them land this change if needed. But yeah it is just a webpack config update so that should work for you.

@alexander-akait
Copy link
Member Author

@thurinus try this:

module.exports = function override(config, env) {
  //do stuff with the webpack config...
  config.module.parser =  {
    javascript: {
      worker: [
        "*context.audioWorklet.addModule()",
        "*audioWorklet.addModule()",
        // *addModule() is not valid syntax
        "...",
      ],
    }
  };

  return config;
};

Better to define syntax globally, because you can already have rules for js/ts/etc, you still can apply them for rules, but it is fine tuning, you don't need it in 99%

After this I put console.log(globalThis) in the each worker and get output:

AudioWorkletGlobalScope {currentFrame: 0, currentTime: 0, sampleRate: 48000, registerProcessor: ƒ, Object: ƒ, …}
App.tsx:17 js worklet loaded
TypescriptWorklet.worklet.ts:14 AudioWorkletGlobalScope {currentFrame: 50176, currentTime: 1.0453333333333332, sampleRate: 48000, registerProcessor: ƒ, Object: ƒ, …}
App.tsx:31 TS worklet loaded

So we are in valid scope 😄

@thurinus
Copy link

thurinus commented Jun 9, 2023

@alexander-akait I assume you got it working in the sandbox just by changing the config-overrides.js? I tried that, but am now getting this error:

importScripts chunk loading:26 Uncaught ReferenceError: importScripts is not defined
    at __webpack_require__.f.i (importScripts chunk loading:26:1)
    at ensure chunk:6:1
    at Array.reduce (<anonymous>)
    at __webpack_require__.e (ensure chunk:5:1)
    at __webpack_require__.x (startup chunk dependencies:3:1)
    at startup:3:1
    at startup:3:1
__webpack_require__.f.i @ importScripts chunk loading:26
(anonymous) @ ensure chunk:6
__webpack_require__.e @ ensure chunk:5
__webpack_require__.x @ startup chunk dependencies:3
(anonymous) @ startup:3
(anonymous) @ startup:3
App.tsx:31 TS worklet loaded

Was there anything else you had to change?

Somewhat tangential, but I also wasn't able to add console.log(globalThis) to the JS worklet. Gives me 'globalThis' is not defined. Could add it to the TS worker, but couldn't verify owing to the issue above.

@alexander-akait
Copy link
Member Author

@thurinus weird, maybe cache? Becaue you even don't have import/require in worklets, so there is no importScripts

@thurinus
Copy link

thurinus commented Jun 9, 2023

@alexander-akait I had to clear node_modules cache to get the overrides to take, so I did that, and also cleared browser cache. Yeah this is really weird, but probably out of scope for this thread. I'll keep digging, and update here if I get any breakthroughs. Thanks much for helping me with this so far!

@alexander-akait
Copy link
Member Author

@thurinus weird, give me a time to look at this again

@alexander-akait
Copy link
Member Author

@thurinus Oh, the problem is only for start, because of ReactRefreshPlugin, it injects extra code into worklets (I think we need a worklet target in future), you can use:

const ReactRefreshPlugin = require("@pmmmwh/react-refresh-webpack-plugin");

module.exports = function override(config, env) {
  if (env === "development") {
    config.plugins = config.plugins.filter(plugin => plugin.constructor.name !== 'ReactRefreshPlugin');
    config.plugins.push(new ReactRefreshPlugin({
      overlay: false,
      exclude: /(node_modules)|(.+\.worklet\.(ts|js)$)/i,
      include: /\.([cm]js|[jt]sx?|flow)$/i
    }));
  }

  config.module.parser =  {
    javascript: {
      worker: [
        "*context.audioWorklet.addModule()",
        "*audioWorklet.addModule()",
        // *addModule() is not valid syntax
        "...",
      ],
    }
  };

  return config;
};

it will fix a problem, but there is another problem - autoreloading, CRA has:

watch: {
        // Reportedly, this avoids CPU overload on some systems.
        // https://github.com/facebook/create-react-app/issues/293
        // src/node_modules is not ignored to support absolute imports
        // https://github.com/facebook/create-react-app/issues/1065
        ignored: ignoredFiles(paths.appSrc),
      },

So they forcibly disable page reloading in src, I really sometimes don't undestand why they do some strange things and block developers to be more flexible

@thurinus
Copy link

@alexander-akait it works! Thanks so much for digging into CRA's scripts to find the issue.

Yeah CRA has its problems, and looks like people are generally moving away from it now. I don't know if it's worth raising this with them to update their configs. This'll be a good solution for existing CRA apps - I'll update my sandbox and leave it up for anyone else who might stumble on this looking for the same answers.

@Bohooslav
Copy link

Bohooslav commented Jul 3, 2023

So is there a complete, reliable solution? None of your comments were helpful. Just some random chunks of code who knows how to get together. Maybe my issue is that I use project type module?

@marcusletric
Copy link

I have tried so many workarounds / new features that were merged, none of them allowed me to import external files. I always ended up with unable to read 'register' of undefined or importScripts is not defined error when I tried to import an external file to my audioWorklet

The only thing that worked for me is to set up copy-webpack-plugin on my workerLib directory and use the vanilla API to load workers and import files....
It is sad that in 2023 I'm still struggling to set up a webpack configuration even when I read all the possible documentation and the community discussions in issue threads.

@Johennes
Copy link

The syntax specified in webpack/webpack.js.org#6869 appears to work for me but I'm hitting an issue with publicPath. Webpack appears to inject automatic public path resolution logic into the compiled worklet file that doesn't seem to work in the worklet context.

Automatic publicPath is not supported in this browser

I can make that error go away by setting output.publicPath to something else than "auto" but that then breaks other things. Has anyone seen this before or has a tip for how to work around it?

@alexander-akait
Copy link
Member Author

@Johennes Sorry for delay, sounds like a bug, I can fix it, do you use new URL(...) syntax in the worklet?

@alexander-akait
Copy link
Member Author

@marcusletric Sorry for delay too, can you provide a code where you try to use them, because we have a lot of tests and I think you have something strange in your configuration, thank you

@Danielku15
Copy link

Danielku15 commented Feb 11, 2024

@alexander-akait Regarding the importScripts topic I think I have the same problem like @marcusletric : The key point is that the worklet needs to be in a separate chunk which is loaded by the bootstrapping script. WebPack currently has the wrong assumption that importScripts is available in worklets. When the main worker script is in a different chunk, the bootstrap script will try to load the worker file via importScripts and this fails.

To reproduce:

  1. Move the worklet into a chunk (https://github.com/webpack/webpack/tree/main/test/configCases/worker/worklet/webpack.config.js)
...
	optimization: {
        splitChunks: {
          cacheGroups: {
            worklet: {
              minSize: 0,
              chunks: "all",
              name: "chunk-worklet",
              priority: 10,
              test: /.*worklet\.js.*/,
            },
          },
        },
      },
  1. Run yarn test:basic to execute this test: https://github.com/webpack/webpack/tree/main/test/configCases/worker/worklet/
  2. Find the written worker file in test/js/ConfigTestCases/worker/worklet
    1. Open the main.js and search for audioWorklet.addModule
    2. Take the number in __webpack_require__.u and open the matching <number>.js
  3. Notice how webpack will have now the startup script which results in following calls:
    image

The current solution to support worklets is to use the Web Worker plugin with an additional syntax matching. This results in importScripts being used.

My initial thought was that we could maybe do this:

if (typeof importScripts === 'function') {
   importScripts(...)
} else {
   promises.push(import(...))
}

I'm not sure yet how webpack could solve this. Fetch, XMLHttpRequest and dynamic imports are not available. But WebPack might need to load chunks dynamically.

@alexander-akait
Copy link
Member Author

@mrmachine Thank you your feedback and report, I will look at this soon

@alexander-akait
Copy link
Member Author

alexander-akait commented Feb 14, 2024

@Danielku15 I see your problem, we should not apply splitChunks for worklet chunks (runtime optimization too).

Why do you use cacheGroups for worklet? Do you have a problem with built-in default/defaultVendor splitting?

There are three problems:

  • Automatic public path is impossible...
  • We don't have self...
  • We don't have importScripts

Current workaround - exclude worklet chunks in splitChunks

I think we need to implement module.rules.parser.worklet (like we have module.rules.parser.worker) and no optimizations should be applied for such chunks, also make sense to implement target: "worklet".

I am fine if someone will send PR

@alexander-akait
Copy link
Member Author

Shorty - if you have:

  • Automatic publicPath is not supported in this browser
  • unable to read 'register' of undefined
  • importScripts is not defined

Check your optimization.splitChunks configuration, you should exclude worklet chunk(s) from them, we have https://webpack.js.org/plugins/split-chunks-plugin/#splitchunkschunks to allow specify it

@LoserAntbear
Copy link

LoserAntbear commented Feb 15, 2024

Greetings, everyone, I really do hope you'll be able to assist me with this exact problem. I've tried everything described here, but still receive: DOMException: The user aborted a request. error.

Prerequisites:

  1. Ejected CRA app with typescript preset.
  2. Typescript-written audio worklet.
  3. Added rules.%babel_loader%.parser.worker and parser.javascript.worker entries into ejected config - still no luck.

It seems I've tried everything I could've found in the net and thought myself with zero result.

Link to the repo in the current state: https://github.com/LoserAntbear/telenes

  • AudioWorklet can be found under src/core/workers/worklets/AudioWorklet.worklet.ts
  • it's import can be found at src/core/systems/audio/AudioSystem.ts:64

Really hope to find some help, since I'm desperately stuck.

I was able to compile it using worklet-loader plugin, but then I get next error:
"Failed to construct 'AudioWorkletNode': AudioWorkletNode cannot be created: The node name 'AUDIO_WORKER' is not defined in AudioWorkletGlobalScope."

Which, it seems, means, that despite it was compiled it is not being loaded for some reason.

[UPD1]:
The thing that I learned, if you receive DOMException: The user aborted a request. error - that means js/ts has an error within that file (e.g. you have imports/exports, which are not allowed it seems) OR it cannot reach it (e.g. wrong path).

Interestingly enough, it appeared, that after adjusting webpack config's parser it actually works. Webpack-dev-server DOES serve the static worklet .ts file. Although it does nothing with it. Possibly, since it's .ts it can't read it properly.
After converting the file to .js it loaded the worklet, hooray.

Although it does not want to process imports within that file, I receive TypeError: Failed to resolve module specifier "module". Relative references must start with either "/", "./", or "../ error for any import (library or relative one).

I expect it happens since worklet is being served and treated as public static file, and relative imports just break here.
From the other hand, it's not clear, why node modules cannot be loaded, but I feel that's happening for the same reason.

@Danielku15
Copy link

@alexander-akait

Why do you use cacheGroups for worklet? Do you have a problem with built-in default/defaultVendor splitting?

On one hand it is primarily to show the problem that the worklet support is not capable of dynamically loading chunks. The problem does not appear in the tests due to the small size and not using any node_modules.

In reality I am having a setup like described in here. (expand me)

I have a shared foundation for my main library code and background workers/worklets. And this shared foundation might also pull further dependencies from external libraries.

flowchart LR
    app.js-- import -->mylib.main.js
    mylib.main.js-- new Worker -->mylib.worker.js
    mylib.main.js-- import -->mylib.shared.js  
    mylib.main.js-- audioworklet.addModule -->mylib.worklet.js
    mylib.worker.js-- import -->mylib.shared.js  
    mylib.worklet.js-- import -->mylib.shared.js  
    mylib.shared.js-- import --> vendor[some npm libs from node_modules]
  • The mylib.main.js is simply the end user facing API surface to use the lib. You can consider this the API client for the backend.
  • The mylib.worker.js and mylib.worklet.js are the backend / background workers where processing is off-loaded. You can consider this the API server offering API calls.
  • The mylib.shared.js contains shared code like validators, common data structures, helper functions etc.

I've seen in various scenarios that some code (mylib.shared.js) was duplicated into multiple chunks. To optimize the sizes of chunks and avoid the duplication I deliberatly move some files into a dedicated chunk.

If WebPack is used together with frameworks like Angular, React or Vue, I think it is easily possible to end up in a scenario where a worklet might need to pull some other chunks or dependencies (e.g. the typical vendor.js).

Current workaround - exclude worklet chunks in splitChunks

Unfortunately I have to agree that this might be the only workaround unless we or the standard comitees invent something which allows loading other files into the worklet context.

But I fear it might be against the goals developers want to achieve with WebPack.

Unfortunately many people assume still that workers or worklets might be just some simple small code snippets. But in reality (considering background canvas rendering and audio synthesizing) those workers might be doing very complex tasks. And for this also the worklets/workers might require/import again other modules and dependencies.

For worklets this means: Duplicating all dependencies and modules into the worklet file instead of benefiting from single chunks being cached and reused by the browser.

@LoserAntbear

... Interestingly enough, it appeared, that after adjusting webpack config's parser it actually works. Webpack-dev-server DOES serve the static worklet .ts file. Although it does nothing with it. Possibly, since it's .ts it can't read it properly.
After converting the file to .js it loaded the worklet, hooray.

It is great to here that you made progress. I am currently working on an approach where at least I can import 1 level of dependencies into the worklet by using an inline blob worklet and static imports. I am not there yet but progressing. Maybe it also helps you getting things running:

class AlphaTabWorkletUrl {
    static create(url: URL) { return url; }
}

function createWebPackWorklet(context: AudioContext) {
    // ./alphaTab.worklet is rewritten to ./alphaTab.worklet.mjd in the typescript transpilation
    // before things go into webpack
    const workletUrl = AlphaTabWorkletUrl.create(new URL('./alphaTab.worklet', import.meta.url));
    const sharedPartUrl = AlphaTabWorkletUrl.create(new URL('./alphaTab.core', import.meta.url)); // TODO: just need the URL of the chunk containing alphaTab.core, not an own worker bootstrapping script. 
    const workletCode = `
        debugger;
        import * as shared from ${JSON.stringify(sharedPartUrl)};
        import * as worklet from ${JSON.stringify(workletUrl)};
    `;
    const workletBlob = new Blob([workletCode], { type: "application/javascript; charset=utf-8" });
    const workletBlobUrl = window.URL.createObjectURL(workletBlob);

    return context.audioWorklet.addModule(workletBlobUrl);
}

/* 
combined with: 
    module: { 
        rules: [ 
            {
                test(path) {
                    return path.includes('alphaTab.worklet.mjs');
                },
                parser: {
                    worker: [
                        "AlphaTabWorkletUrl.create()",
                        '...'
                    ]
                }
            }
        ]
    }
*/

@alexander-akait
Copy link
Member Author

Yeah, anyway if you have ideas how we can solve it feel free to send any PRs, I see the problem, I suggested excluding this from any optimization so that it would at least work out of the box without problems

@LoserAntbear
Copy link

LoserAntbear commented Feb 16, 2024

@Danielku15 Thanks for the heads up!
In my investigations i've stumbled upon audio-worklet-loader on npm, github link. It allowed me to load both .js and .ts worklet together with inner imports (library and relative imports).

So great thanks and big credit to @leviance for the effort and making it work.
I'll dig further in his approach, maybe it'll help with a proper application without external loaders.

@Danielku15

This comment was marked as outdated.

@Danielku15
Copy link

@alexander-akait I have a draft change to the worker plugin ready which works for my library to import some dependencies into the worklet before launching the original worklet. But to finalize it properly I'd need some guidance. I'm lacking a bit the knowledge how to do things correctly within WebPack.

The current change:
main...Danielku15:webpack:feature/worklets-with-imports

The change does following:

  1. It introduces a separate syntax to indicate the worklet generation <> instead of ().
  2. It rewrites the original call to addModule with an async wrapper function setting things up:
    1. An an inline bootstrap script is injected to the worklet making the current webworker runtime module compatible with the worklet scopes.
    2. It injects all the entrypoint.getEntrypointChunk().getAllReferencedChunks() individually ensuring the dependencies are there. The worklet itself is part of this and will launch.
  3. It ensures pre-loaded chunks are installed on the bootstrapping script.

Clarifications I need:

  1. I wanted to keep using the WorkerPlugin and not refactor too much into common functions. But if you prefer, I could make a dedicated WorkletPlugin with respective settings and then share some common functions for parsing and decoding the AST. Currently I inject different dependencies based on the syntax.
  2. Is there a better way to transform the original expression to the new one? I'd like to reuse the code of some expressions and emit it directly in my DependencyTemplate instead.
  3. Is there a more reliable or better way of obtaining all chunk URLs from the dependency graph than I'm doing now? The current solution is more a result of trial-and-error using own project.

Of course alternatively you can adopt my idea and write a plugin/change in your preferred style. I'm happy to test anything in my project.

@alexander-akait
Copy link
Member Author

@Danielku15 Looks intresting, can you send this PR and adopt existing worklet tests for it?

I wanted to keep using the WorkerPlugin and not refactor too much into common functions. But if you prefer, I could make a dedicated WorkletPlugin with respective settings and then share some common functions for parsing and decoding the AST. Currently I inject different dependencies based on the syntax.

Good idea, I like it

Is there a better way to transform the original expression to the new one? I'd like to reuse the code of some expressions and emit it directly in my DependencyTemplate instead.

Can you clarify?

Is there a more reliable or better way of obtaining all chunk URLs from the dependency graph than I'm doing now? The current solution is more a result of trial-and-error using own project.

it is not trivial task

@Danielku15
Copy link

Is there a better way to transform the original expression to the new one? I'd like to reuse the code of some expressions and emit it directly in my DependencyTemplate instead.

I mean this part of the code which is quite messy: https://github.com/Danielku15/webpack/blob/765efb28a6122c0a3bc97690b2d0438de4fe3c12/lib/dependencies/WorkerPlugin.js#L454-L493 ModuleDependencies can overwrite source file ranges with custom strings but I need to know how I can (efficiently) adopt parts of the original sources to be used in the string I generate in my ModuleDependency.

The inefficient solution I found is to copy known ranges from the ReplaceSource via source.original().source().slice(). Also it doesn't allow other replacements to occur on this location as we access the original.

My goal:

We have an expression of context.audioWorklet.addModule(new URL('./worklet', import.meta.url), { credentials: true }) which I want to rewrite to (async function(__webpack__worklet__, __webpack__worklet_options__) { ... })(context.audioWorklet, { credentials: true }). Depending on the syntax (constructor, function call, method call) this rewrite might also vary .

Talking in placeholders we have on a *.moduletype.modulefunction() syntax an expression like <memberaccess>.<moduletype>.<modulefunction>(<url>, <options>) and I want to get it rewritten to <custom code>(<memberacces>.<moduletype>, <option>) within my ModuleDependency implementation.

Is there a more reliable or better way of obtaining all chunk URLs from the dependency graph than I'm doing now? The current solution is more a result of trial-and-error using own project.

it is not trivial task

Any suggestion how the new worklet plugin should deal with the import? The html-webpack-plugin relies on EntryPoint.getFiles() but it this seems not available for the worker entry point in the ModuleDependency source generation stage. Otherwise I'd stick with the EntryPoint.getEntryPointChunk().getAllReferencedChunks() solution for now.

@Danielku15
Copy link

I prepared a draft of a new plugin which I tested successfully in my project. The hurdles to actually contribute to WebPack feel still a bit high, maybe with community effort we can complete this PR (unittest, manual tests in some projects, writing website/docs,...).

@iSuslov
Copy link

iSuslov commented Apr 9, 2024

Workaround for now since navigator.serviceWorker.register is supported:

const {register} = navigator.serviceWorker;
// @ts-ignore hack to make webpack believe that it is registering a worker
navigator.serviceWorker.register = (url:URL) => context.audioWorklet.addModule(url)
navigator.serviceWorker.register(new URL('./path/to/worklet', import.meta.url))
navigator.serviceWorker.register = register

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Priority - Medium
Development

No branches or pull requests