Visitar URL original
Worker type classic is not preserved when using libraryTarget module · Issue #14818 · webpack/webpack · GitHub
Skip to content

Worker type classic is not preserved when using libraryTarget module #14818

Description

@dmnsgn

Bug report

What is the current behavior?

Bundling with libraryTarget module, new Worker with options type classic will be rewritten from:

const myWorker = new Worker(new URL("worker.js", import.meta.url), {
  type: "classic",
});

to:

const myWorker = new Worker(new URL(/* worker import */ __webpack_require__.p + __webpack_require__.u(695), __webpack_require__.b), {
  type: "module",
});

That's an issue when the imported worker contains importScripts as loading it as module will throw:
Uncaught TypeError: Failed to execute 'importScripts' on 'WorkerGlobalScope': Module scripts don't support importScripts().

If the current behavior is a bug, please provide the steps to reproduce.

Gist with repro case: https://gist.github.com/dmnsgn/1b6b464a6c6aa2ed4d4c1d2a18042e02

git clone https://gist.github.com/dmnsgn/1b6b464a6c6aa2ed4d4c1d2a18042e02
cd 1b6b464a6c6aa2ed4d4c1d2a18042e02/
npm i
npm run build

To see the importScripts error in a browser (otherwise just look at the output dist/bundle.js):

npm serve

What is the expected behavior?

It should keep type classic even when setting a libraryTarget to module.

Other relevant information:
webpack version: 5.64.3
Node.js version: 17.1
Operating System: MacOS
Additional tools:

Activity

  1. dmnsgn commented on Nov 24, 2021

    @dmnsgn
    Author

    Without libraryTarget module, it outputs type: undefined btw.

  2. alexander-akait commented on Nov 24, 2021

    @alexander-akait
    Member

    Expected, you ask webpack to output ESM, webpack rewrite code to ESM, you can't use importScripts

  3. alexander-akait commented on Nov 24, 2021

    @alexander-akait
    Member

    As you can't use importScripts to import ESM or inside in ESM without webpack

  4. sokra commented on Nov 24, 2021

    @sokra
    Member

    And webpack doesn't support bundling importScript as dependency within workers. We only support ESM and CommonJs in workers.

  5. dmnsgn commented on Nov 24, 2021

    @dmnsgn
    Author

    In principle it makes sense, you ask ESM you get ESM. But does that mean that Workers should necessarily be transformed as module Workers? Their execution context being different.

    My point are:

    • in regular JavaScript, without a bundler, passing type: "module" is opt-in, the default is "classic".
    • module Worker support is far from JavaScript modules support
    • if my library has an external dependency on a worker using importScripts and I want it bundled in my esm output lib, I am stuck

    And webpack doesn't support bundling importScript as dependency within workers. We only support ESM and CommonJs in workers.

    Could type: 'classic' and their importScripts be bundled as is, with just their publicPath updated?

  6. alexander-akait commented on Nov 24, 2021

    @alexander-akait
    Member

    But it is weird self.importScripts(new URL("./foo.js", import.meta.url));, import.meta.url available only in module, but you use classic...

    And versa vice if you use module, you can't use importScripts, try it without webpack...

  7. dmnsgn commented on Nov 24, 2021

    @dmnsgn
    Author

    But it is weird self.importScripts(new URL("./foo.js", import.meta.url));, import.meta.url available only in module, but you use classic...

    And versa vice if you use module, you can't use importScripts, try it without webpack...

    Yeah true, I did this because with only self.importScripts("./foo.js"); it doesn't output foo.js to dist.

  8. alexander-akait commented on Nov 24, 2021

    @alexander-akait
    Member

    So you can:

    • do not use new URL("worker.js", import.meta.url) and webpack will not change it, bad idea for you, I think
    • use multi compiler mode set target: webworker and bundle worker as script output, then load it inside application using import
    • do not use importScripts in favor import()
  9. dmnsgn commented on Nov 26, 2021

    @dmnsgn
    Author

    Separately bundling with target: webworker does work but produces bigger files and put the Workers outside webpack module resolution logic.
    I'd still like to be able to use the more future proof syntax:

        new Worker(
          new URL('some-dependency/worker.js', import.meta.url),
          { name: 'my-dep-worker', type: 'classic' }
        )

    than hoping that url resolution still works new Worker('./worker.js').

    Currently it also look like any other properties in the worker options (credentials or name) would they be scrapped off?

  10. alexander-akait commented on Nov 27, 2021

    @alexander-akait
    Member

    Separately bundling with target: webworker does work but produces bigger files

    It should be not very big... Can you provide example?

    I'd still like to be able to use the more future proof syntax:

    I don't think we should add new semantic to JS, it can be unsafe

    Currently it also look like any other properties in the worker options (credentials or name) would they be scrapped off?

    We should keep them, it is bug if we remove them

  11. dmnsgn commented on Nov 27, 2021

    @dmnsgn
    Author

    We should keep them, it is bug if we remove them

    Nevermind, they are preserved, no problem

    I don't think we should add new semantic to JS, it can be unsafe

    Ha I am not suggesting to add anything. Just hoping that the native and valid JS code above stays the same once bundled and that webpack can determine if the target should be compiled as "classic" or "module" according to this property since it is in the spec. I understand it is probably requiring some work to make the compiler aware of a different target for a dependency in the webpack graph but making the WorkerPlugin a bit more flexible in checking options might be enough?

    if (expressions.type) {
    const expr = expressions.type;
    if (options.type !== false) {
    const dep = new ConstDependency(
    this._module ? '"module"' : "undefined",
    expr.range
    );
    dep.loc = expr.loc;
    parser.state.module.addPresentationalDependency(dep);
    expressions.type = undefined;
    }
    } else if (insertType === "comma") {
    if (this._module || hasSpreadInOptions) {
    const dep = new ConstDependency(
    `, type: ${this._module ? '"module"' : "undefined"}`,
    insertLocation
    );
    dep.loc = expr.loc;
    parser.state.module.addPresentationalDependency(dep);
    }
    } else if (insertType === "spread") {
    const dep1 = new ConstDependency(
    "Object.assign({}, ",
    insertLocation[0]
    );
    const dep2 = new ConstDependency(
    `, { type: ${this._module ? '"module"' : "undefined"} })`,
    insertLocation[1]
    );
    dep1.loc = expr.loc;
    dep2.loc = expr.loc;
    parser.state.module.addPresentationalDependency(dep1);
    parser.state.module.addPresentationalDependency(dep2);
    } else if (insertType === "argument") {
    if (this._module) {
    const dep = new ConstDependency(
    ', { type: "module" }',
    insertLocation
    );
    dep.loc = expr.loc;
    parser.state.module.addPresentationalDependency(dep);
    }
    }

    It should be not very big... Can you provide example?

    With monaco-editor/esm/vs/language/typescript/ts.worker.js for instance it goes from 4.33 MiB (libraryTarget/library.type = module) to 4.51 MiB (target: 'webworker').

  12. alexander-akait commented on Nov 27, 2021

    @alexander-akait
    Member

    Weird, we need investigate why it is bigger, should be same, with small runtime overhead. Maybe we should support allow to keep script worker as is and do not rewrite it to esm, but I think it will not solve your problem, because you use import.meta.url, as I written before you can't use it in classic workers

  13. dmnsgn commented on Jan 10, 2022

    @dmnsgn
    Author

    In an ideal world, the Worker script I am importing should be valid either as classic or module worker. But because of the complexity of bundling workers in the JavaScript ecosystem, it looks like there are a lot of edge cases.

    Maybe we should support allow to keep script worker as is and do not rewrite it to esm, but I think it will not solve your problem, because you use import.meta.url, as I written before you can't use it in classic workers

    Loading the worker as is would definitely work for some of these edge cases but yes, for my use case above, I would need more granularity to be able to set a different target (webworker) for these "external" graph dependencies.

  14. alexander-akait commented on Jan 10, 2022

    @alexander-akait
    Member

    In theory you can create custom plugin and do what you want and generate any runtime code (you can look at our source code), but I agree, it is not easy and you have to support it and spend time on it

  15. 3 remaining items

  16. alexander-akait commented on May 4, 2022

    @alexander-akait
    Member

    bump

  17. removed
    status: staleNo author response after a needs: request; close candidate
    on May 4, 2022
  18. joemarshall commented on Jul 3, 2022

    @joemarshall

    Finding this when I'm trying to bundle jupyterlite in an es module. It imports pyodide from an external URL, entirely different project, and uses a non module script to do that which is created on the fly in a blob URL. Which given the source is created entirely outside webpack doesn't get moduleised. Check out the constructor here:

    https://github.com/jupyterlite/jupyterlite/blob/ba4c2450cdfb1c84b3eaab25e09e361fa4a17fa7/packages/pyolite-kernel/src/kernel.ts

    I'm gonna do a pr to jupyterlite to make it use either the standard or es module version of pyodide, but it would be nice if the webpack code didn't force modules for things that it isn't generating.

  19. vankop commented on Jul 5, 2022

    @vankop
    Contributor

    @joemarshall didn't get what you mean.. webpack does nothing with

    this._worker = new Worker(window.URL.createObjectURL(blob));

    since this is not statically analyzable

  20. joemarshall commented on Jul 6, 2022

    @joemarshall

    @vankop You're absolutely right, sorry. The code I was running had added a sub-module inside a worker which was breaking things. Ignore me.

  21. added
    status: staleNo author response after a needs: request; close candidate
    on Oct 5, 2022
  22. webpack-bot commented on Oct 20, 2022

    @webpack-bot
    Contributor

    Issue was closed because of inactivity.

    If you think this is still a valid issue, please file a new issue with additional information.

  23. removed
    status: staleNo author response after a needs: request; close candidate
    on Oct 20, 2022
  24. added
    status: staleNo author response after a needs: request; close candidate
    on Jan 20, 2023
  25. removed
    status: staleNo author response after a needs: request; close candidate
    on Mar 4, 2025
  26. Ekagrashharma commented on Oct 15, 2026

    @Ekagrashharma

    is this issue still valid ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    webpack-5Concerns the webpack 5 line

    Fields

    Priority

    None yet

    Start date

    None yet

    Target date

    None yet

    Effort

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions