Code Monkey home page Code Monkey logo

worker-plugin's Introduction

worker-plugin

👩‍🏭 worker-plugin

Automatically bundle & compile Web Workers within Webpack.

Features

Automatically compiles modules loaded in Web Workers:

const worker = new Worker('./foo.js', { type: 'module' });
                          ^^^^^^^^^^
                          gets bundled using webpack

The best part? That worker constructor works just fine without bundling turned on, but when bundled the result is supported in all browsers that support Web Workers - all the way back to IE 10!

Workers with fully dynamic URLs, Blob URLs, data URLs or with no { type:'module' } option are left unchanged.

Compatibility Note: Webpack 5 now includes worker bundling. It uses a slightly different syntax:
new Worker(new URL("./my_worker.js", import.meta.url))

Installation

npm install -D worker-plugin

Then drop it into your webpack.config.js:

+ const WorkerPlugin = require('worker-plugin');

module.exports = {
  <...>
  plugins: [
+    new WorkerPlugin()
  ]
  <...>
}

Note: If you're planning on having more than one worker, you'll need to make sure output.filename is set to something dynamic, e.g. "[name].bundle.js" otherwise the generated filenames will overwrite one another.

Usage

worker.js: (our worker module)

// This is a module worker, so we can use imports (in the browser too!)
import { calculatePi } from './some-other-module';

addEventListener('message', event => {
  postMessage(calculatePi(event.data));
});

main.js: (our demo, on the main thread)

const piWorker = new Worker('./worker.js', { type: 'module' });
piWorker.onmessage = event => {
  console.log('pi: ' + event.data);
};
piWorker.postMessage(42);

Note: in order to ensure WorkerPlugin bundles your worker, make sure you're passing a string URL/filename to the Worker constructor. WorkerPlugin cannot bundle workers with dynamic/variable filenames, Blob or data URLs - it will leave them unmodified and print a warning during your build.

Options

In most cases, no options are necessary to use WorkerPlugin.

globalObject (string | false)

WorkerPlugin will print a warning if your Webpack configuration has output.globalObject set to window, since doing so breaks Hot Module Replacement in web workers.

If you're not using HMR and want to disable this warning, pass globalObject:false:

new WorkerPlugin({
  // disable warnings about "window" breaking HMR:
  globalObject: false
})

To configure the value of output.globalObject for WorkerPlugin's internal Webpack Compiler, set globalObject to any String:

new WorkerPlugin({
  // use "self" as the global object when receiving hot updates.
  globalObject: 'self' // <-- this is the default value
})

plugins (array)

By default, WorkerPlugin doesn't run any of your configured Webpack plugins when bundling worker code - this avoids running things like html-webpack-plugin twice. For cases where it's necessary to apply a plugin to Worker code, use the plugins option.

Here you can specify the names of plugins to "copy" from your existing Webpack configuration, or provide specific plugins to apply only to worker code:

module.exports = {
  <...>
  plugins: [
    // an example of a plugin already being used:
    new SomeExistingPlugin({ <...> }),

    new WorkerPlugin({
      plugins: [
        // A string here will copy the named plugin from your configuration:
        'SomeExistingPlugin',
        
        // Or you can specify a plugin directly, only applied to Worker code:
        new SomePluginToApplyOnlyToWorkers({ <...> })
      ]
    })
  ]
  <...>
}

sharedWorker (boolean)

If set to true, this option enables the bundling of SharedWorker:

const shared = new SharedWorker('./my-shared-worker.js', { type: 'module' });

worker (boolean)

If set to false, this option disables the bundling of [Worker]. Intended to be used with { sharedWorker: true } to allow bundling of [SharedWorker] only without also bundling [Worker].

preserveTypeModule (boolean)

workerType (string)

Normally, WorkerPlugin will transform new Worker('./a.js', { type: 'module' }) to completely remove the type option, outputting something like new Worker('a.worker.js'). This allows the plugin to compile Module Workers to Classic Workers, which are supported in all browsers.

To instead retain {type:'module'} in bundled output, set the preserveTypeModule option to true:

  plugins: [
    new WorkerPlugin({
      preserveTypeModule: true
    })
  ]

Similarly, if you need to have WorkerPlugin output a specific type value, use the workerType option to specify it:

  plugins: [
    new WorkerPlugin({
      workerType: 'foo'  // note: this isn't a thing!
    })
  ]

Loader

At its core, worker-plugin provides two features: parsing and handling of new Worker(), and standalone bundling of modules for use in a different JavaScript context.

If all you want is to compile separate bundles for a module, worker-plugin/loader provides the bundling functionality of worker-plugin as a standalone Webpack loader. This is useful for generating bundles for use in iframes, Service Workers or Worklets. Applying worker-plugin/loader to an import will bundle that module and return its URL:

import workerUrl from 'worker-plugin/loader!./my-worker';

console.log(workerUrl); // "/0.worker.js"

CSS.paintWorklet.addModule(workerUrl);

Two options are available:

Option Type Description
name string Controls the name of the generated chunk.
The name is used to generate a URL according to output.chunkFilename.
esModule boolean Export the URL from an ES Module (export default url).
The default is CommonJS (module.exports = url).

Options can be supplied inline:

import url from 'worker-plugin/loader?name=foo&esModule!./foo';

... or by setting up a loader alias:

// webpack.config.js to enable this:
// import url from 'worker!./foo';
{
  resolveLoader: {
    alias: {
      worker: 'worker-plugin/loader?esModule'
    }
  }
}

License

Apache-2.0

worker-plugin's People

Contributors

3846masa avatar alan-agius4 avatar arturovt avatar brn avatar developit avatar dkozma avatar gbmatt avatar gluck avatar jephron avatar jkjustjoshing avatar krivega avatar lacolaco avatar mrkiffie avatar stutrek avatar timvdlippe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

worker-plugin's Issues

Help: Integration of WebpackHTMLPlugin

I am actually using a worker to outsource work of a component that is loaded at start time of the page. My "index" chunk is loading the worker chunk on page load - the will slow down the app.

From reading the "plugins" section of the docs I don't get how to integrate my worker chunk as asset in the HTMLPlugin.

My simple HTMLPlugin config (for the hole app):

new HtmlWebpackPlugin({
      template: path.resolve(paths.srcPath, 'index.html'),
      minify: {
        collapseWhitespace: true,
        removeComments: true,
        processScripts: ['text/javascript']
      },
      filename: 'index.html'
})

Also using additonal plugins for the production build:

new ScriptExtHtmlWebpackPlugin({
      // inline: ['runtime'],
      async: /\.js$/,
      // defer: /\.wc.js$/,
      prefetch: {
        test: /\.js$/,
        chunks: 'async'
      },
      preload: {
        test: /\.js$/,
        chunks: 'initial'
      }
})

Does anyone have an idea how to get the same behavior for worker chunks generated by the worker-plugin?

Thanks in advance!

[question] Support service workers?

I know this is specific to web workers, however I think this could be adapted to work for service workers. I was looking to create a separate plugin but the code would be mostly the same.

Looks like it scans the AST for a keyword (Worker) and generates an entry for the referenced file.

// web worker
new Worker('./foo.js', { type: 'module' })

// service worker
navigator.serviceWorker.register('./foo.js', { type: 'module' })

Could it be adapted to be more generic or am I missing something specific this does for web workers?

Uncaught SyntaxError: Unexpected token <

I wanted to create an npm package that internally uses web worker. I got working configuration for that in the worker-loader package for webpack as follows.

{
  test: /\.worker\.js$/,
  use: {
    loader: 'worker-loader',
    options: {
      inline: true
    }
  }
}

then I could use my library in some react project like that

import {BoardGenerator} from '@yufuzu/shipbattle-board';
const generator = new BoardGenerator();

Board generator is my npm library that uses it's web worker.

When I switched to this plugin I get an error that it cannot find web worker. Everything compiles properly and I am able to see use it in live mode in the browser. (From this project npm start). Problem occurs only when I import that library in some other project.

Repo:
https://github.com/Jitra/shipbattle-generatorv2 working in here npm start
React repo importing npm package:
https://github.com/Jitra/react-shipbattle-generator-example not working here

Old version with worker-loader
https://github.com/Jitra/shipbattle-generator

[Bug?] Parent compiler's plugins are not inherited into the child compiler.

My story is here: https://medium.com/lacolaco-blog/an-issue-around-angular-cli-comlink-workerplugin-585be1c8d087

My friend @manfredsteyer figured out the cause of the error. WorkerPlugin creates a child webpack compiler for worker module's compilation.

https://github.com/GoogleChromeLabs/worker-plugin/blob/master/src/loader.js#L42

const workerCompiler = this._compilation.createChildCompiler(NAME, workerOptions);

createChildCompiler API takes three arguments; its name, outputOptions, and plugins. The current implementation in WorkerPlugin doesn't pass the 3rd argument, so the child compiler doesn't has plugins inherited from the parent.

Solution:

Add the 3rd argument and pass compilerOptions.plugin.

const workerCompiler = this._compilation.createChildCompiler(NAME, workerOptions, compilerOptions.plugins);

In my case, it works well as I expected.

If this is by design, I'd like to know the reason.

Creating a worker on electron doesn't include Node.js

Hi, amazing library.
I'm having problems because when I create a simple worker like this one:

// node.js library
var child_process = require('child_process');

console.log("This is a testing string");

If I create the worker like this:

var worker1 = new Worker('./workerTest.js');

It works, but when I create the worker like this:

var worker2 = new Worker('./workerTest.js', { type: 'module' });

It shows this message Module not found: Error: Can't resolve 'child_process' meaning that the worker2 doesn't include Node.js. I don't know if I should include another option apart from type: 'module' or if this is not supported by this library.

Also, I can't use worker1 method because the real worker is more complex and has a lot of imports.

WorkerOptions object is order dependent

When adding a name to a worker:

new Worker("path/to/worker.js", { name: "worker", type: "module" })

The order of the keys in the WorkerOptions object can cause a build output to be malformed.

If the keys are reversed (type first, name second), the output build is not parseable.

new Worker("path/to/worker.js", { name: "worker", type: "module" })

image

Here's a minimal repro demonstrating the issue: https://github.com/zevisert/worker-plugin-name-order-repro/

type: module support for older browsers with worker-plugin

Hey,

First of all, thanks for worker-plugin - I'm really enjoying using it. I found myself pondering a question and the docs don't seem to answer it. So I thought I'd raise it as a question (and a great outcome of this issue could be me raising a docs PR so others get the benefit too)

Consider:

const worker = new Worker('./foo.js', { type: 'module' });

I love { type: 'module' } because it provides the ability for standard import module syntax that I'm entirely used to. Honestly, I've never sufficiently understood importScripts, and given that I'm used to ES modules I'd rather stick with one way to do modules rather than two.

As I understand it, { type: 'module' } has only recently started being supported in Chrome and is only supported there so far. Most of my users aren't on the latest greatest Chrome.

Does worker-plugin require that { type: 'module' } is in the browser to work, or does it do some clever transpilation / shimming so that's not a problem?

Trailing comma when options parameter contains only `type: module`

When the Worker call expression 2 parameter contains only an object literal with type: 'module' it will produce a trailing comma in the output which will result in a syntax error in some browsers such as IE11.

Input:

const hashcashWorker = new Worker('./hashcash.worker', {
  type: 'module'
});

Output

const hashcashWorker = new Worker(__webpack__worker__0, );

Version: 4.0.2
Related to: angular/angular-cli#17373

Causes webpack to run out of heap when building

Hi there.

When I include your plugin in my webpack.config.js file and run build I get a Javascript heap out of memory exception.

When I provide the --max_old_space_size argument and give it 10GB of ram it just builds to what seems infinity.

Stacktrace:
WEBPACK_ENV=build webpack

Warning (worker-plugin): output.globalObject is set to "window". It must be set to "self" to support HMR in Workers.
[BABEL] Note: The code generator has deoptimised the styling of /home/daniel/Documents/Dynex/Canvas/canvas.iron.js/src/components/data/changeFocusHolder.js as it exceeds the max of 500KB.

<--- Last few GCs --->
art of marking 194 ms) (average mu = 0.198, current mu = 0.079) allocation[10962:0x3598210] 290153 ms: Mark-sweep 2045.5 (2052.8) -> 2043.0 (2050.6) MB, 163.7 / 0.0 ms (+ 0.0 ms in 31 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 178 ms) (average mu = 0.144, current mu = 0.079) allocation[10962:0x3598210] 290433 ms: Mark-sweep 2043.0 (2050.6) -> 2043.0 (2050.6) MB, 248.2 / 0.0 ms (average mu = 0.126, current mu = 0.113) allocation failure GC in old space requested

<--- JS stacktrace --->

==== JS stack trace =========================================

0: ExitFrame [pc: 0x13162b9]
1: StubFrame [pc: 0x13718dc]

Security context: 0x35a54a940911
2: walkExpressions [0x2d890b3a71d1] [/home/daniel/Documents/Dynex/Canvas/canvas.iron.js/node_modules/webpack/lib/Parser.js:1475] [bytecode=0x20b4258b8f81 offset=69](this=0x29546e2436a1 ,0x3fec23d59ed9 <JSArray[1]>)
3: walkCallExpression [0x2d890b3a76a1] [/home/daniel/Documents/Dynex/Canvas/can...

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
1: 0x9aedf0 node::Abort() [node]
2: 0x9aff86 node::OnFatalError(char const*, char const*) [node]
3: 0xb078ce v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
4: 0xb07c49 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
5: 0xce4ae5 [node]
6: 0xcf032b v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [node]
7: 0xcf1047 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
8: 0xcf3b78 v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationType, v8::internal::AllocationAlignment) [node]
9: 0xcc1b5f v8::internal::Factory::NewByteArray(int, v8::internal::AllocationType) [node]
10: 0xc4419a v8::internal::TranslationBuffer::CreateByteArray(v8::internal::Factory*) [node]
11: 0x191fe07 v8::internal::compiler::CodeGenerator::GenerateDeoptimizationData() [node]
12: 0x1920502 v8::internal::compiler::CodeGenerator::FinalizeCode() [node]
13: 0x1993abd v8::internal::compiler::PipelineImpl::FinalizeCode(bool) [node]
14: 0x1994b60 v8::internal::compiler::PipelineCompilationJob::FinalizeJobImpl(v8::internal::Isolate*) [node]
15: 0xc06ea0 v8::internal::Compiler::FinalizeOptimizedCompilationJob(v8::internal::OptimizedCompilationJob*, v8::internal::Isolate*) [node]
16: 0xc00766 v8::internal::OptimizingCompileDispatcher::InstallOptimizedFunctions() [node]
17: 0xc8ae5c v8::internal::StackGuard::HandleInterrupts() [node]
18: 0xf93ba7 v8::internal::Runtime_StackGuard(int, unsigned long*, v8::internal::Isolate*) [node]
19: 0x13162b9 [node]
Aborted (core dumped)
npm ERR! code ELIFECYCLE
npm ERR! errno 134
npm ERR! [email protected] build: WEBPACK_ENV=build webpack --max_old_space_size=10000
npm ERR! Exit status 134
npm ERR!
npm ERR! Failed at the [email protected] build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! /home/daniel/.npm/_logs/2019-07-05T11_46_34_789Z-debug.log

loader worker-plugin/loader is not found by webpack

Using worker-plugin version 4.0.2, and trying to use the worker-plugin/loader syntax, I get the following error in Webpack:

ERROR in /home/steph/data/enlightware/game-creator/src/blocks/actions/music/music.ts
./src/blocks/actions/music/music.ts
[tsl] ERROR in /home/steph/data/enlightware/game-creator/src/blocks/actions/music/music.ts(18,29)
      TS2307: Cannot find module 'worker-plugin/loader!./music.worklet'.

is the line:

import musicWorkletURL from 'worker-plugin/loader!./music.worklet';

The file music.ts imports the worker music.worklet.ts.

I am using Webpack 4.42 and Typescript 3.8.3 (both latest).

While my configuration files have nothing exotic IMHO, I am aware that this problem still is likely the result of a complex interaction between Webpack and Typescript, and I will produce a minimal test case as soon as possible. I still post the issue already in case someone else meets the same problem.

Feature request: support fixed filename

compilerOptions.output.chunkFilename is [name].[hash].js and i need to provide the generated name.hash.worker.js to sw-precache-webpack-plugin

I cant use the generated .worker.js filename, the [name] could not be used from /sw-precache-webpack-plugin (ok thats their problem) and the hash seems not to match the generated file.

An option to set a fixed target filename in webpack config would be perfect:

      new WorkerPlugin({
        filename: 'worker.js'
      }),

Question: New Worker Inside Loop Continuously Requests Script?

Curious if there is a more appropriate way to use the worker-plugin inside a loop? What I'm seeing is on every iteration the browser reloads the ./worker.js file on each new Worker...

Say I have something like inside a loop...

     const worker = new Worker('./worker.js', { type: 'module' });
     const wrapper = Comlink.wrap(worker);
     const proxy = await new proxy();

    results.push(await proxy.data);     

    await proxy[Comlink.releaseProxy]();
    await worker.terminate();

Having the browser continuously request the same ./worker.js file seems inappropriate. Ideally I would request the script once and be able to simply create new/multiple workers with the same script. I'm sure I am doing something wrong...

how to change directory of worker?

im using laravel mix and by default it places the file at public directory

here's my webpack Config

.webpackConfig({ plugins: [ new WorkerPlugin({ filename: '[name].worker.js' }) ], });

if i use the output

output: { path: path.join(__dirname, 'public/js/workers'), },

It places all my files there including my app.js which i don't want

Thank you for the help

worker_threads compatibility?

Understood if this is off-topic, and feel free to close, but I was just wondering if you'd considered compatibility with Node.js worker_threads here at all?

It seems like the sort of thing that would be generally useful for Node.js optimization as well, in having some kind of --node output, since you're already in the game of worker builds.

Although perhaps, a translation layer from web-style workers to Node.js-style worker_threads would be the better route as its own project.

Otherwise do you know anyone working on this problem / who considers this important?

Cannot load worker module unless the worker.js is under public/.

I would like put my worker.js under the same src/ directory as main.js, which loads worker. (src/ is parallel to public/) However I got the following error if worker.js is under src/.
//in main.js
let worker = new Worker('./worker.js', {type:"module"});

Failed to load module script: The server responded with a non-JavaScript MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec.

However if I put worker.js under public/, or any directory under public/, everything works fine. Seems like it can only load from public/ aka. http://<my domain>/ , if it is under any directory parallel to public/, then it cannot find it. am I right on this?

Plus, in worker.js, I cannot import any thing in node_modules/ like what I do for other js files in src/.

thanks.

Handle import.meta.url

It should be possible to apply a pre/pitching loader to all files that transforms import.meta.url to __filename. This would allow correct Worker URL creation (solving #31) using URL:

const url = new URL('./my-worker.js', import.meta.url);
const worker = new Worker(url, { type: 'module' });

I believe Webpack's simple partial evaluation should be enough to consider this a "static" URL under the current logic.

Resolving webpack externals

I'm just going to leave this here as this problem took quite a bit of digging to figure out. Sometimes you can have webpack configs that target nodejs, (e.g., Gatsby to do SSR) and you may have a module that you're bundling in a worker that requires an undesired node module like fs. Usually, you'd specify something like "browser": { "fs": false } in package.json, config.node = { "fs": "empty" } or config.externals in your webpack config. It turns out none of these are picked up by the child compiler used by worker-plugin.

A workaround can be to manually apply the ExternalsPlugin to worker-loader:

import { ExternalsPlugin } from "webpack"

...

config.plugins = [
  new WorkerPlugin({
    plugins: [
      new ExternalsPlugin(`commonjs`, config.externals)
    ]
  })
]

...

This would apply your webpack's externals configuration to the web workers built by worker-loader. Hopefully, this saves someone's time.

Uncaught TypeError: Failed to construct 'Worker'

I tried minimum example. (on Chrome 69/Mac)

// src/index.js
const worker = new Worker("./worker.js", { type: "module" });
worker.onmessage = event => {
  console.log("response: " + event.data);
};
worker.postMessage(0);
// webpack.config.js
const HTMLPlugin = require("html-webpack-plugin");
const WorkerPlugin = require("worker-plugin");

module.exports = {
  mode: process.env.NODE_ENV || "development",
  plugins: [new HTMLPlugin(), new WorkerPlugin()]
};

Compile success but I got this error on browser.

Uncaught TypeError: Failed to construct 'Worker': Module scripts are not supported on DedicatedWorker yet. You can try the feature with '--enable-experimental-web-platform-features' flag (see https://crbug.com/680046)
    at Object.<anonymous> (main.js:9346)
    at Object../src/index.js (main.js:9354)
    at __webpack_require__ (main.js:20)
    at Object.0 (main.js:9366)
    at __webpack_require__ (main.js:20)
    at ./node_modules/ansi-html/index.js.module.exports (main.js:84)
    at main.js:87

Build result is here

/* WEBPACK VAR INJECTION */(function(__webpack__worker__1) {const worker = new Worker(__webpack__worker__1, { type: "module" });
// ...

I tried enable-experimental-web-platform-features but it does not work too. (and ChromeCanary too)

I think, it is lack of some pre-conditions.

My full code is here https://github.com/mizchi-sandbox/try-worker-plugin

Breaks Angular templates

I followed this guide: https://medium.com/lacolaco-blog/enjoyable-webworkers-in-angular-41cfeb0e6519

When I open my app it boots Angular and immediately fails accessing template files (Failed to load failed-foo.component.html, app.com/foo.component.html 404 (Not Found))

Commenting out the new WorkerPlugin line solves this, although of course it fails trying to access the workers. The build always passes successfully with no errors, even with --verbose; and when WorkerPlugin is configured it seems to be building a worker chunk.

Any clue what might be causing this? I don't even know what kind of informative log I can send. Very confusing.

Creating a Worker using a URL string

I'm currently have a WebWorkerPool module, where a worker is created based on a URL string that is passed as a variable :

this._worker = new Worker(this._script, { type: 'module' });

Is seems this plugin only supports string literals as a URL.

Is there a way around this?

Documentation needs updating

Digging around some of the issues here, it seems that there are more options available than what's in the README.md.

Also, there should be clarification that if you pass a variable, instead of a hardcoded string, into the Worker call, that it will not be picked up by the WebPack compilation, and will use the browser's native implementation w/o module compilation.

Allow for dynamic webpack public paths

According to the webpack docs, you can configure a dynamic public path for output files (like the web worker file) at runtime by adding __webpack_public_path__ = myRuntimePublicPath; to the top of your entry. However, this plugin generates a one-line entry e.exports = __webpack_public_path__ + "rustworker.worker.js".

It would be great if there were some configuration option for this plugin to allow adding the runtime public path override line to the generated entry.

[feature request] Cross domain blob building fallback

I tried to publish 3rd party script with webpack and worker-plugin.

https://cdn.example.com/main.js <- entry
https://cdn.example.com/sub.js <- chunk
https://cdn.example.com/0.worker.js <- worker

I set output.publicPath to "https://cdn.example.com/" in this case;

But I can not exec worker because of cross domain restriction.

> new Worker("http://localhost:8080/0.worker.js")
VM84:1 Uncaught DOMException: Failed to construct 'Worker': Script at 'http://localhost:8080/0.worker.js' cannot be accessed from origin 'https://www.google.com'.

I know this fallback works to avoid it.

// ASSET_HOST="https://cdn.example.com/" webpack --mode production
if (process.env.ASSET_HOST === location.protocol + "//" + location.host) {
    return new Worker(process.env.ASSET_HOST + "0.worker.js")
} else {
    const code = await fetch(process.env.ASSET_HOST + "0.worker.js").then(res =>
      res.text()
    );
    // console.log(t);
    const blob = new Blob([code], { type: "text/javascript" });
    const url = URL.createObjectURL(blob);
    return worker = new Worker(url);
}

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.

MIME type checking error

When I am trying the latest release in Chrome 73. I get the following error.
Failed to load module script: The server responded with a non-JavaScript MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec. at the beginning 1st line of the worker.jsx file.

in the main.jsx file I have

const worker = new Worker('./worker.jsx',{type:"module"});
worker.onmessage = event=>{
console.log("WORKER response: ",event.data);
};
worker.postMessage(0);

In the worker.jsx file, I have

import answer from './answer';
self.addEventListener('message',function(e){
self.postMessage("123 123 abc abc");
});

In answer.js file I have

export default () => 42;

Can you please let me know what the issue is with MIME type and how can I fix it?

thank you

Plugins used when bundling worker

According to the docs:

By default, WorkerPlugin doesn't run any of your configured Webpack plugins when bundling worker code

However, this doesn't seem to be true. Consider the following partial config:

plugins: [
  new webpack.DefinePlugin({
    _A_: JSON.stringify('this text should never appear in worker'),
  }),
  new WorkerPlugin({
    plugins: [
      new webpack.DefinePlugin({
        _B_: JSON.stringify('this text should definitely appear in worker'),
      }),
    ],
  }),
]

Given the documentation, the expectation would be that:

  • In the worker:
    • _A_ is untouched
    • _B_ is replaced with 'this text should definitely appear in worker'
  • Everywhere else:
    • _A_ is replaced with 'this text should never appear in worker'
    • _B_ is untouched

However, in all code _A_ is replaced with 'this text should never appear in worker' and _B_ is untouched.

This is apparently because the DefinePlugin is using hooks that are copied into the child compiler created by WorkerPlugin (see createChildCompiler()).

For background, my actual use case is to use ProvidesPlugin (not DefinePlugin) to replace a free variable with a different implementation (module) in workers vs elsewhere.

'reactHotLoaderGlobal' is not defined

I was using this plugin successfully with Gatsby version 2.7.5. But when I tried to upgrade Gatsby to 2.17.15 I'm getting this error.

/home/sakib/codes/personal/blog/src/posts/may-2019/SlidingPuzzle/main.worker.js
  6:5   warning  '__signature__' is assigned a value but never used  no-unused-vars
  6:67  error    'reactHotLoaderGlobal' is not defined               no-undef

I'm not sure whats going on. Is it possible that gatsby and worker-plugin is using different configuration for compiling?

Error when using dynamic imports

If I include the following in my worker code (assuming dummyModule.js just contains export default 1)

const unusedImport = () => import('./dummyModule')

then I get this error:

./src/myWorker.ts (./node_modules/worker-plugin/dist/loader.js?{"name":"0"}!./src/myWorker.ts)
ChunkRenderError: Conflict: Multiple chunks emit assets to the same filename static/js/0.chunk.worker.js (chunks 0 and 0)

Is this something we can work around with worker-plugin's options?

Error when using comlink

Hi, thanks for putting together this loader. Having an issue with integrating comlink.

main.ts

void async function() {
  const worker = new Worker('./test.js', { type: 'module' })
  const TestStore = Comlink.wrap(worker)
  console.log(await new TestStore())
}()

test.js

import { expose } from 'comlink'

class Thing {
  constructor() {
    this.state = ''
  }

  setState(v) {
    this.state = v
  }
}

expose(Thing)

results in the error

Uncaught (in promise) TypeError: Cannot read property 'apply' of undefined
    at MessagePort.eval

I am using the latest Chrome

Subworkers loaded from incorrect URL?

I have worker that is loaded from the main code:

const worker = new Worker('./MainWorker.js', {
    name: 'main',
    type: 'module'
});

And within this worker, it tries to load a subworker:

const subworker = new Worker('./Subworker.js', {
    name: 'sub',
    type: 'module'
});

When webpack bundles this, the browser correctly and successfully loads the main worker from 'dist/main.worker.js', however it then tries to load the subworker from 'dist/dist/sub.worker.js' which isn't where the worker bundle was built to.

I'm not sure if this is a config problem on my side or an issue with the plugin. Here are the relevant parts of my webpack config:

{
    entry: ['./src/index.js'],
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name].js',
        publicPath: 'dist/'
    },
    plugins: [
        new WorkerPlugin({
            filename: '[name].worker.js'
        })
    ]
}

Trailing Comma in the LoaderOption causes SyntaxError on removing `type:module`

At first, I think this is not only by the WorkerPlugin's implementation.

Problem

When {type: 'module'} contains trailing-comma like below, compiled JavaScript is broken.

// pre-compile
new Worker('./foo.js', { 
  type: 'module',
});
// post-compile
new Worker('./foo.js', { 
  , // <--- SyntaxError
});

Workaround

Removing the comma.

 new Worker('./foo.js', { type: 'module' });

Version

I confirm reproducing in v3.0.0 and v3.1.0.

How to do the configuration change if I use react-scripts?

I use react-scripts, not webpack. How to do the configuration change to use worker-plugin?
I think I found the solution by adding

const WorkerPlugin = require('worker-plugin');
plugins:[
new WorkerPlugin({globalObject: 'self'}),
....]

into the node_modules/react-scripts/config/webpack.config.js file. Now seems like the worker.js can be bundled and automatically compiled. However I met the compiling error when compile the following worker.js file.

Line 2: Unexpected use of 'addEventListener' no-restricted-globals

in worker.js

import answer from './answer';

addEventListener('message',function(e){
console.log("WORKER data ",e.data);
postMessage("123 123 abc abc");
});

What am I missing here?

thanks

Feature request: option to exclude some files

It'll be great if we could specify files to exclude. Currently, I get this warning WARNING in new Worker() will only be bundled if passed a String. because a dependency dynamically creates workers. But it's not actionable from me because I don't control this code.

hot reload doesn't work in Create React App

i'm using create-react-app with worker-plugin and see that for worker js files:

  • changes do not trigger page hot reloads
  • eslint-loader doesn't see lint errors
const worker = new Worker('workers/my-worker', {
  name: 'my-worker',
  type: 'module'
});

everything above works if i'm also importing worker directly

import 'workers/my-worker';

but this runs worker code inside main thread, which is ugly

i've ejected from create-react-app to show complete webpack configs

create-react-app v3.3.0
worker-plugin v3.2.0

webpack.config.js
'use strict';

const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const resolve = require('resolve');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const paths = require('./paths');
const modules = require('./modules');
const getClientEnvironment = require('./env');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
const WorkerPlugin = require('worker-plugin');

const postcssNormalize = require('postcss-normalize');

const appPackageJson = require(paths.appPackageJson);

// Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
// makes for a smoother build process.
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';

const imageInlineSizeLimit = parseInt(
  process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
);

// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);

// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;

// This is the production and development configuration.
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
module.exports = function(webpackEnv) {
  const isEnvDevelopment = webpackEnv === 'development';
  const isEnvProduction = webpackEnv === 'production';

  // Variable used for enabling profiling in Production
  // passed into alias object. Uses a flag if passed into the build command
  const isEnvProductionProfile =
    isEnvProduction && process.argv.includes('--profile');

  // Webpack uses `publicPath` to determine where the app is being served from.
  // It requires a trailing slash, or the file assets will get an incorrect path.
  // In development, we always serve from the root. This makes config easier.
  const publicPath = isEnvProduction
    ? paths.servedPath
    : isEnvDevelopment && '/';
  // Some apps do not use client-side routing with pushState.
  // For these, "homepage" can be set to "." to enable relative asset paths.
  const shouldUseRelativeAssetPaths = publicPath === './';

  // `publicUrl` is just like `publicPath`, but we will provide it to our app
  // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
  // Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
  const publicUrl = isEnvProduction
    ? publicPath.slice(0, -1)
    : isEnvDevelopment && '';
  // Get environment variables to inject into our app.
  const env = getClientEnvironment(publicUrl);

  // common function to get style loaders
  const getStyleLoaders = (cssOptions, preProcessor) => {
    const loaders = [
      isEnvDevelopment && require.resolve('style-loader'),
      isEnvProduction && {
        loader: MiniCssExtractPlugin.loader,
        options: shouldUseRelativeAssetPaths ? {publicPath: '../../'} : {}
      },
      {
        loader: require.resolve('css-loader'),
        options: cssOptions
      },
      {
        // Options for PostCSS as we reference these options twice
        // Adds vendor prefixing based on your specified browser support in
        // package.json
        loader: require.resolve('postcss-loader'),
        options: {
          // Necessary for external CSS imports to work
          // https://github.com/facebook/create-react-app/issues/2677
          ident: 'postcss',
          plugins: () => [
            require('postcss-flexbugs-fixes'),
            require('postcss-preset-env')({
              autoprefixer: {
                flexbox: 'no-2009'
              },
              stage: 3
            }),
            // Adds PostCSS Normalize as the reset css with default options,
            // so that it honors browserslist config in package.json
            // which in turn let's users customize the target behavior as per their needs.
            postcssNormalize()
          ],
          sourceMap: isEnvProduction && shouldUseSourceMap
        }
      }
    ].filter(Boolean);
    if (preProcessor) {
      loaders.push(
        {
          loader: require.resolve('resolve-url-loader'),
          options: {
            sourceMap: isEnvProduction && shouldUseSourceMap
          }
        },
        {
          loader: require.resolve(preProcessor),
          options: {
            sourceMap: true
          }
        }
      );
    }
    return loaders;
  };

  return {
    mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
    // Stop compilation early in production
    bail: isEnvProduction,
    devtool: isEnvProduction
      ? shouldUseSourceMap
        ? 'source-map'
        : false
      : isEnvDevelopment && 'cheap-module-source-map',
    // These are the "entry points" to our application.
    // This means they will be the "root" imports that are included in JS bundle.
    entry: [
      // Include an alternative client for WebpackDevServer. A client's job is to
      // connect to WebpackDevServer by a socket and get notified about changes.
      // When you save a file, the client will either apply hot updates (in case
      // of CSS changes), or refresh the page (in case of JS changes). When you
      // make a syntax error, this client will display a syntax error overlay.
      // Note: instead of the default WebpackDevServer client, we use a custom one
      // to bring better experience for Create React App users. You can replace
      // the line below with these two lines if you prefer the stock client:
      // require.resolve('webpack-dev-server/client') + '?/',
      // require.resolve('webpack/hot/dev-server'),
      isEnvDevelopment &&
        require.resolve('react-dev-utils/webpackHotDevClient'),
      // Finally, this is your app's code:
      paths.appIndexJs
      // We include the app code last so that if there is a runtime error during
      // initialization, it doesn't blow up the WebpackDevServer client, and
      // changing JS code would still trigger a refresh.
    ].filter(Boolean),
    output: {
      // The build folder.
      path: isEnvProduction ? paths.appBuild : undefined,
      // Add /* filename */ comments to generated require()s in the output.
      pathinfo: isEnvDevelopment,
      // There will be one main bundle, and one file per asynchronous chunk.
      // In development, it does not produce real files.
      filename: isEnvProduction
        ? 'static/js/[name].[contenthash:8].js'
        : isEnvDevelopment && 'static/js/bundle.js',
      // TODO: remove this when upgrading to webpack 5
      futureEmitAssets: true,
      // There are also additional JS chunk files if you use code splitting.
      chunkFilename: isEnvProduction
        ? 'static/js/[name].[contenthash:8].chunk.js'
        : isEnvDevelopment && 'static/js/[name].chunk.js',
      // We inferred the "public path" (such as / or /my-project) from homepage.
      // We use "/" in development.
      publicPath: publicPath,
      // Point sourcemap entries to original disk location (format as URL on Windows)
      devtoolModuleFilenameTemplate: isEnvProduction
        ? info =>
            path
              .relative(paths.appSrc, info.absoluteResourcePath)
              .replace(/\\/g, '/')
        : isEnvDevelopment &&
          (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
      // Prevents conflicts when multiple Webpack runtimes (from different apps)
      // are used on the same page.
      jsonpFunction: `webpackJsonp${appPackageJson.name}`,
      // this defaults to 'window', but by setting it to 'this' then
      // module chunks which are built will work in web workers as well.
      globalObject: 'this'
    },
    optimization: {
      minimize: isEnvProduction,
      minimizer: [
        // This is only used in production mode
        new TerserPlugin({
          terserOptions: {
            parse: {
              // We want terser to parse ecma 8 code. However, we don't want it
              // to apply any minification steps that turns valid ecma 5 code
              // into invalid ecma 5 code. This is why the 'compress' and 'output'
              // sections only apply transformations that are ecma 5 safe
              // https://github.com/facebook/create-react-app/pull/4234
              ecma: 8
            },
            compress: {
              ecma: 5,
              warnings: false,
              // Disabled because of an issue with Uglify breaking seemingly valid code:
              // https://github.com/facebook/create-react-app/issues/2376
              // Pending further investigation:
              // https://github.com/mishoo/UglifyJS2/issues/2011
              comparisons: false,
              // Disabled because of an issue with Terser breaking valid code:
              // https://github.com/facebook/create-react-app/issues/5250
              // Pending further investigation:
              // https://github.com/terser-js/terser/issues/120
              inline: 2
            },
            mangle: {
              safari10: true
            },
            // Added for profiling in devtools
            keep_classnames: isEnvProductionProfile,
            keep_fnames: isEnvProductionProfile,
            output: {
              ecma: 5,
              comments: false,
              // Turned on because emoji and regex is not minified properly using default
              // https://github.com/facebook/create-react-app/issues/2488
              ascii_only: true
            }
          },
          sourceMap: shouldUseSourceMap
        }),
        // This is only used in production mode
        new OptimizeCSSAssetsPlugin({
          cssProcessorOptions: {
            parser: safePostCssParser,
            map: shouldUseSourceMap
              ? {
                  // `inline: false` forces the sourcemap to be output into a
                  // separate file
                  inline: false,
                  // `annotation: true` appends the sourceMappingURL to the end of
                  // the css file, helping the browser find the sourcemap
                  annotation: true
                }
              : false
          }
        })
      ],
      // Automatically split vendor and commons
      // https://twitter.com/wSokra/status/969633336732905474
      // https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
      splitChunks: {
        chunks: 'all',
        name: false
      },
      // Keep the runtime chunk separated to enable long term caching
      // https://twitter.com/wSokra/status/969679223278505985
      // https://github.com/facebook/create-react-app/issues/5358
      runtimeChunk: {
        name: entrypoint => `runtime-${entrypoint.name}`
      }
    },
    resolve: {
      // This allows you to set a fallback for where Webpack should look for modules.
      // We placed these paths second because we want `node_modules` to "win"
      // if there are any conflicts. This matches Node resolution mechanism.
      // https://github.com/facebook/create-react-app/issues/253
      modules: ['node_modules', paths.appNodeModules].concat(
        modules.additionalModulePaths || []
      ),
      // These are the reasonable defaults supported by the Node ecosystem.
      // We also include JSX as a common component filename extension to support
      // some tools, although we do not recommend using it, see:
      // https://github.com/facebook/create-react-app/issues/290
      // `web` extension prefixes have been added for better support
      // for React Native Web.
      extensions: paths.moduleFileExtensions
        .map(ext => `.${ext}`)
        .filter(ext => useTypeScript || !ext.includes('ts')),
      alias: {
        // Support React Native Web
        // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
        'react-native': 'react-native-web',
        // Allows for better profiling with ReactDevTools
        ...(isEnvProductionProfile && {
          'react-dom$': 'react-dom/profiling',
          'scheduler/tracing': 'scheduler/tracing-profiling'
        }),
        ...(modules.webpackAliases || {})
      },
      plugins: [
        // Adds support for installing with Plug'n'Play, leading to faster installs and adding
        // guards against forgotten dependencies and such.
        PnpWebpackPlugin,
        // Prevents users from importing files from outside of src/ (or node_modules/).
        // This often causes confusion because we only process files within src/ with babel.
        // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
        // please link the files into your node_modules/ and let module-resolution kick in.
        // Make sure your source files are compiled, as they will not be processed in any way.
        new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson])
      ]
    },
    resolveLoader: {
      plugins: [
        // Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
        // from the current package.
        PnpWebpackPlugin.moduleLoader(module)
      ]
    },
    module: {
      strictExportPresence: true,
      rules: [
        // Disable require.ensure as it's not a standard language feature.
        {parser: {requireEnsure: false}},

        // First, run the linter.
        // It's important to do this before Babel processes the JS.
        {
          test: /\.(js|mjs|jsx|ts|tsx)$/,
          enforce: 'pre',
          use: [
            {
              options: {
                cache: true,
                formatter: require.resolve('react-dev-utils/eslintFormatter'),
                eslintPath: require.resolve('eslint'),
                resolvePluginsRelativeTo: __dirname
              },
              loader: require.resolve('eslint-loader')
            }
          ],
          include: paths.appSrc,

          // TODO: figure out how to remove this. for now disable linter for
          // workers, because worker-plugin transforms worker code, breaks some
          // prettier rules and blocks the build
          exclude: /worker-bootstrap\.js/
        },
        {
          // "oneOf" will traverse all following loaders until one will
          // match the requirements. When no loader matches it will fall
          // back to the "file" loader at the end of the loader list.
          oneOf: [
            // "url" loader works like "file" loader except that it embeds assets
            // smaller than specified limit in bytes as data URLs to avoid requests.
            // A missing `test` is equivalent to a match.
            {
              test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
              loader: require.resolve('url-loader'),
              options: {
                limit: imageInlineSizeLimit,
                name: 'static/media/[name].[hash:8].[ext]'
              }
            },
            // Process application JS with Babel.
            // The preset includes JSX, Flow, TypeScript, and some ESnext features.
            {
              test: /\.(js|mjs|jsx|ts|tsx)$/,
              include: paths.appSrc,
              loader: require.resolve('babel-loader'),
              options: {
                customize: require.resolve(
                  'babel-preset-react-app/webpack-overrides'
                ),

                plugins: [
                  [
                    require.resolve('babel-plugin-named-asset-import'),
                    {
                      loaderMap: {
                        svg: {
                          ReactComponent:
                            '@svgr/webpack?-svgo,+titleProp,+ref![path]'
                        }
                      }
                    }
                  ]
                ],
                // This is a feature of `babel-loader` for webpack (not Babel itself).
                // It enables caching results in ./node_modules/.cache/babel-loader/
                // directory for faster rebuilds.
                cacheDirectory: true,
                // See #6846 for context on why cacheCompression is disabled
                cacheCompression: false,
                compact: isEnvProduction
              }
            },
            // Process any JS outside of the app with Babel.
            // Unlike the application JS, we only compile the standard ES features.
            {
              test: /\.(js|mjs)$/,
              exclude: /@babel(?:\/|\\{1,2})runtime/,
              loader: require.resolve('babel-loader'),
              options: {
                babelrc: false,
                configFile: false,
                compact: false,
                presets: [
                  [
                    require.resolve('babel-preset-react-app/dependencies'),
                    {helpers: true}
                  ]
                ],
                cacheDirectory: true,
                // See #6846 for context on why cacheCompression is disabled
                cacheCompression: false,

                // Babel sourcemaps are needed for debugging into node_modules
                // code.  Without the options below, debuggers like VSCode
                // show incorrect code and set breakpoints on the wrong lines.
                sourceMaps: shouldUseSourceMap,
                inputSourceMap: shouldUseSourceMap
              }
            },
            // "postcss" loader applies autoprefixer to our CSS.
            // "css" loader resolves paths in CSS and adds assets as dependencies.
            // "style" loader turns CSS into JS modules that inject <style> tags.
            // In production, we use MiniCSSExtractPlugin to extract that CSS
            // to a file, but in development "style" loader enables hot editing
            // of CSS.
            // By default we support CSS Modules with the extension .module.css
            {
              test: cssRegex,
              exclude: cssModuleRegex,
              use: getStyleLoaders({
                importLoaders: 1,
                sourceMap: isEnvProduction && shouldUseSourceMap
              }),
              // Don't consider CSS imports dead code even if the
              // containing package claims to have no side effects.
              // Remove this when webpack adds a warning or an error for this.
              // See https://github.com/webpack/webpack/issues/6571
              sideEffects: true
            },
            // Adds support for CSS Modules (https://github.com/css-modules/css-modules)
            // using the extension .module.css
            {
              test: cssModuleRegex,
              use: getStyleLoaders({
                importLoaders: 1,
                sourceMap: isEnvProduction && shouldUseSourceMap,
                modules: {
                  getLocalIdent: getCSSModuleLocalIdent
                }
              })
            },
            // Opt-in support for SASS (using .scss or .sass extensions).
            // By default we support SASS Modules with the
            // extensions .module.scss or .module.sass
            {
              test: sassRegex,
              exclude: sassModuleRegex,
              use: getStyleLoaders(
                {
                  importLoaders: 2,
                  sourceMap: isEnvProduction && shouldUseSourceMap
                },
                'sass-loader'
              ),
              // Don't consider CSS imports dead code even if the
              // containing package claims to have no side effects.
              // Remove this when webpack adds a warning or an error for this.
              // See https://github.com/webpack/webpack/issues/6571
              sideEffects: true
            },
            // Adds support for CSS Modules, but using SASS
            // using the extension .module.scss or .module.sass
            {
              test: sassModuleRegex,
              use: getStyleLoaders(
                {
                  importLoaders: 2,
                  sourceMap: isEnvProduction && shouldUseSourceMap,
                  modules: {
                    getLocalIdent: getCSSModuleLocalIdent
                  }
                },
                'sass-loader'
              )
            },
            // "file" loader makes sure those assets get served by WebpackDevServer.
            // When you `import` an asset, you get its (virtual) filename.
            // In production, they would get copied to the `build` folder.
            // This loader doesn't use a "test" so it will catch all modules
            // that fall through the other loaders.
            {
              loader: require.resolve('file-loader'),
              // Exclude `js` files to keep "css" loader working as it injects
              // its runtime that would otherwise be processed through "file" loader.
              // Also exclude `html`, `wasm` and `json` extensions so they get processed
              // by webpacks internal loaders.
              exclude: [/\.(js|mjs|jsx|ts|tsx|wasm)$/, /\.html$/, /\.json$/],
              options: {
                name: 'static/media/[name].[hash:8].[ext]'
              }
            }
            // ** STOP ** Are you adding a new loader?
            // Make sure to add the new loader(s) before the "file" loader.
          ]
        }
      ]
    },
    plugins: [
      // Generates an `index.html` file with the <script> injected.
      new HtmlWebpackPlugin(
        Object.assign(
          {},
          {
            inject: true,
            template: paths.appHtml
          },
          isEnvProduction
            ? {
                minify: {
                  removeComments: true,
                  collapseWhitespace: true,
                  removeRedundantAttributes: true,
                  useShortDoctype: true,
                  removeEmptyAttributes: true,
                  removeStyleLinkTypeAttributes: true,
                  keepClosingSlash: true,
                  minifyJS: true,
                  minifyCSS: true,
                  minifyURLs: true
                }
              }
            : undefined
        )
      ),
      // Inlines the webpack runtime script. This script is too small to warrant
      // a network request.
      // https://github.com/facebook/create-react-app/issues/5358
      isEnvProduction &&
        shouldInlineRuntimeChunk &&
        new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
      // Makes some environment variables available in index.html.
      // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
      // <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
      // In production, it will be an empty string unless you specify "homepage"
      // in `package.json`, in which case it will be the pathname of that URL.
      // In development, this will be an empty string.
      new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
      // This gives some necessary context to module not found errors, such as
      // the requesting resource.
      new ModuleNotFoundPlugin(paths.appPath),
      // Makes some environment variables available to the JS code, for example:
      // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
      // It is absolutely essential that NODE_ENV is set to production
      // during a production build.
      // Otherwise React will be compiled in the very slow development mode.
      new webpack.DefinePlugin(env.stringified),
      // This is necessary to emit hot updates (currently CSS only):
      isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
      // Watcher doesn't work well if you mistype casing in a path so we use
      // a plugin that prints an error when you attempt to do this.
      // See https://github.com/facebook/create-react-app/issues/240
      isEnvDevelopment && new CaseSensitivePathsPlugin(),
      // If you require a missing module and then `npm install` it, you still have
      // to restart the development server for Webpack to discover it. This plugin
      // makes the discovery automatic so you don't have to restart.
      // See https://github.com/facebook/create-react-app/issues/186
      isEnvDevelopment &&
        new WatchMissingNodeModulesPlugin(paths.appNodeModules),
      isEnvProduction &&
        new MiniCssExtractPlugin({
          // Options similar to the same options in webpackOptions.output
          // both options are optional
          filename: 'static/css/[name].[contenthash:8].css',
          chunkFilename: 'static/css/[name].[contenthash:8].chunk.css'
        }),
      // Generate an asset manifest file with the following content:
      // - "files" key: Mapping of all asset filenames to their corresponding
      //   output file so that tools can pick it up without having to parse
      //   `index.html`
      // - "entrypoints" key: Array of files which are included in `index.html`,
      //   can be used to reconstruct the HTML if necessary
      new ManifestPlugin({
        fileName: 'asset-manifest.json',
        publicPath: publicPath,
        generate: (seed, files, entrypoints) => {
          const manifestFiles = files.reduce((manifest, file) => {
            manifest[file.name] = file.path;
            return manifest;
          }, seed);
          const entrypointFiles = entrypoints.main.filter(
            fileName => !fileName.endsWith('.map')
          );

          return {
            files: manifestFiles,
            entrypoints: entrypointFiles
          };
        }
      }),
      // Moment.js is an extremely popular library that bundles large locale files
      // by default due to how Webpack interprets its code. This is a practical
      // solution that requires the user to opt into importing specific locales.
      // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
      // You can remove this if you don't use Moment.js:
      new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
      // Generate a service worker script that will precache, and keep up to date,
      // the HTML & assets that are part of the Webpack build.
      isEnvProduction &&
        new WorkboxWebpackPlugin.GenerateSW({
          clientsClaim: true,
          exclude: [/\.map$/, /asset-manifest\.json$/],
          importWorkboxFrom: 'cdn',
          navigateFallback: publicUrl + '/index.html',
          navigateFallbackBlacklist: [
            // Exclude URLs starting with /_, as they're likely an API call
            new RegExp('^/_'),
            // Exclude any URLs whose last part seems to be a file extension
            // as they're likely a resource and not a SPA route.
            // URLs containing a "?" character won't be blacklisted as they're likely
            // a route with query params (e.g. auth callbacks).
            new RegExp('/[^/?]+\\.[^/]+$')
          ]
        }),
      // TypeScript type checking
      useTypeScript &&
        new ForkTsCheckerWebpackPlugin({
          typescript: resolve.sync('typescript', {
            basedir: paths.appNodeModules
          }),
          async: isEnvDevelopment,
          useTypescriptIncrementalApi: true,
          checkSyntacticErrors: true,
          resolveModuleNameModule: process.versions.pnp
            ? `${__dirname}/pnpTs.js`
            : undefined,
          resolveTypeReferenceDirectiveModule: process.versions.pnp
            ? `${__dirname}/pnpTs.js`
            : undefined,
          tsconfig: paths.appTsConfig,
          reportFiles: [
            '**',
            '!**/__tests__/**',
            '!**/?(*.)(spec|test).*',
            '!**/src/setupProxy.*',
            '!**/src/setupTests.*'
          ],
          silent: true,
          // The formatter is invoked directly in WebpackDevServerUtils during development
          formatter: isEnvProduction ? typescriptFormatter : undefined
        }),
      new WorkerPlugin()
    ].filter(Boolean),
    // Some libraries import Node modules but don't use them in the browser.
    // Tell Webpack to provide empty mocks for them so importing them works.
    node: {
      module: 'empty',
      dgram: 'empty',
      dns: 'mock',
      fs: 'empty',
      http2: 'empty',
      net: 'empty',
      tls: 'empty',
      child_process: 'empty'
    },
    // Turn off performance processing because we utilize
    // our own hints via the FileSizeReporter
    performance: false
  };
};
webpackDevServer.config.js
'use strict';

const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
const ignoredFiles = require('react-dev-utils/ignoredFiles');
const paths = require('./paths');
const fs = require('fs');

const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const host = process.env.HOST || '0.0.0.0';

module.exports = function(proxy, allowedHost) {
  return {
    // WebpackDevServer 2.4.3 introduced a security fix that prevents remote
    // websites from potentially accessing local content through DNS rebinding:
    // https://github.com/webpack/webpack-dev-server/issues/887
    // https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
    // However, it made several existing use cases such as development in cloud
    // environment or subdomains in development significantly more complicated:
    // https://github.com/facebook/create-react-app/issues/2271
    // https://github.com/facebook/create-react-app/issues/2233
    // While we're investigating better solutions, for now we will take a
    // compromise. Since our WDS configuration only serves files in the `public`
    // folder we won't consider accessing them a vulnerability. However, if you
    // use the `proxy` feature, it gets more dangerous because it can expose
    // remote code execution vulnerabilities in backends like Django and Rails.
    // So we will disable the host check normally, but enable it if you have
    // specified the `proxy` setting. Finally, we let you override it if you
    // really know what you're doing with a special environment variable.
    disableHostCheck:
      !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
    // Enable gzip compression of generated files.
    compress: true,
    // Silence WebpackDevServer's own logs since they're generally not useful.
    // It will still show compile warnings and errors with this setting.
    clientLogLevel: 'none',
    // By default WebpackDevServer serves physical files from current directory
    // in addition to all the virtual build products that it serves from memory.
    // This is confusing because those files won’t automatically be available in
    // production build folder unless we copy them. However, copying the whole
    // project directory is dangerous because we may expose sensitive files.
    // Instead, we establish a convention that only files in `public` directory
    // get served. Our build script will copy `public` into the `build` folder.
    // In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
    // <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
    // In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
    // Note that we only recommend to use `public` folder as an escape hatch
    // for files like `favicon.ico`, `manifest.json`, and libraries that are
    // for some reason broken when imported through Webpack. If you just want to
    // use an image, put it in `src` and `import` it from JavaScript instead.
    contentBase: paths.appPublic,
    // By default files from `contentBase` will not trigger a page reload.
    watchContentBase: true,
    // Enable hot reloading server. It will provide /sockjs-node/ endpoint
    // for the WebpackDevServer client so it can learn when the files were
    // updated. The WebpackDevServer client is included as an entry point
    // in the Webpack development configuration. Note that only changes
    // to CSS are currently hot reloaded. JS changes will refresh the browser.
    hot: true,
    // Use 'ws' instead of 'sockjs-node' on server since we're using native
    // websockets in `webpackHotDevClient`.
    transportMode: 'ws',
    // Prevent a WS client from getting injected as we're already including
    // `webpackHotDevClient`.
    injectClient: false,
    // It is important to tell WebpackDevServer to use the same "root" path
    // as we specified in the config. In development, we always serve from /.
    publicPath: '/',
    // WebpackDevServer is noisy by default so we emit custom message instead
    // by listening to the compiler events with `compiler.hooks[...].tap` calls above.
    quiet: true,
    // 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
    watchOptions: {
      ignored: ignoredFiles(paths.appSrc),

      // turn polling on since it doesn't detect changes sometimes
      poll: 500
    },
    // Enable HTTPS if the HTTPS environment variable is set to 'true'
    https: protocol === 'https',
    host,
    overlay: false,
    historyApiFallback: {
      // Paths with dots should still use the history fallback.
      // See https://github.com/facebook/create-react-app/issues/387.
      disableDotRule: true
    },
    public: allowedHost,
    proxy,
    before(app, server) {
      if (fs.existsSync(paths.proxySetup)) {
        // This registers user provided middleware for proxy reasons
        require(paths.proxySetup)(app);
      }

      // This lets us fetch source contents from webpack for the error overlay
      app.use(evalSourceMapMiddleware(server));
      // This lets us open files from the runtime error overlay.
      app.use(errorOverlayMiddleware());

      // This service worker file is effectively a 'no-op' that will reset any
      // previous service worker registered for the same host:port combination.
      // We do this in development to avoid hitting the production cache if
      // it used the same host and port.
      // https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
      app.use(noopServiceWorkerMiddleware());
    }
  };
};

Tries to load module object instead of name

I have the following in my main-thread.js:

const worker = new Worker('./counter-worker.js', {type: 'module'});

With an empty counter-worker.js. When I open the build, it loads http://localhost:8000/build/[object%20Module] rather than http://localhost:8000/build/0.main-thread.worker.js It appears that the following diff in the build output makes it work again:

- const worker = new Worker(__webpack__worker__1, {type: 'module'});
+ const worker = new Worker(__webpack__worker__1.default, {type: 'module'});

As the console output of __webpack__worker__1 is the following:

> Module {default: "0.main-thread.worker.js", __esModule: true, Symbol(Symbol.toStringTag): "Module"}
default: "0.main-thread.worker.js"
Symbol(Symbol.toStringTag): "Module"
__esModule: true
__proto__: Object

That appears to be the result of __webpack_require__(1), which returns the module object. However, I can't find any reference to the Module object in that module.

This happens on both Chrome 69 and Firefox 62.

Running into error: No template for dependency: ConstDependency

I'm not 100% sure what's causing this, but I have a project with a dependency and that dependency loads a Worker. When I try to run webpack I get:

ERROR in chunk main [entry]
    main.js
    /path/to/project/node_modules/babel-loader/lib/index.js??ref--4!/path/to/project/node_modules/thrid-party-library/loadsWorker.jsx
    No template for dependency: ConstDependency

Maybe it has to do with the combination of babel and loading the Worker from a dependency of the main project. Any ideas?

Other webpack plugins are executed twice although it is not necessary

Problem

Because of #14, other webpack plugins are executed twice. However, many plugins are not necessary to run twice. (e.g. html-webpack-plugin, clean-webpack-plugin)

In my case, @wasm-tool/wasm-pack-plugin and clean-webpack-plugin cannot be used together.
@wasm-tool/wasm-pack-plugin build wasm file before bundling scripts, but clean-webpack-plugin clean twice before and after running @wasm-tool/wasm-pack-plugin, so wasm file will be removed when bundling.

I think, how about setting plugins as an option like below?

[
  new WorkerPlugin({
    plugins: [new SomePluginInWorker()],
  }),
]
// Or
[
  new SomePluginInWorkerAndBrowser(),
  new WorkerPlugin({
    plugins: ['SomePluginInWorkerAndBrowser'],
  }),
]

Webpack 5 support

Hi,

any plan for supporting the upcoming webpack 5?

I had a quick try and I istantly hit a wall at this point which should be updated to:

import ParserHelpers from 'webpack/lib/javascript/JavascriptParserHelpers';

Thanks 💯 for the awesome lib!

Why cannot I use multiple workers for different tasks?

I have two worker scripts. workerFile1.js and workerFile2.js, which do different works in the same application. In the main script, I have

const worker1 = new Worker(‘workerFile1.js',{type:'module'});
const worker2 = new Worker(‘workerFile2.js',{type:'module'});
...
worker1.postMessage(obj1);
...
worker2.postMessage(obj2);

But seems like this not working. both worker1 and worker2 are trying to use the latest compiled worker file logic. i.e. If I do some modification in workerFile2.js, both of the worker1 and worker2 will do the task defined in workerFile2.js, and vice versa. Of course, only one of the worker1 or worker2 works, and the other will throw error.

I wonder if this is a configuration issue. thanks.

Warning (workerize-loader): output.globalObject is set to "window". It should be set to "self" or "this" to support HMR in Workers

Heya,

I'm trying to follow up your initial work in angular/angular-cli#12575.

I see Warning (workerize-loader): output.globalObject is set to "window". It should be set to "self" or "this" to support HMR in Workers when serving, but not when building.

That warning seems to come from here:

console.warn('Warning (workerize-loader): output.globalObject is set to "window". It should be set to "self" or "this" to support HMR in Workers.');

What's the intended way of addressing this warning? I'd like to keep the globalObject settings of the main compilation unaltered.

As a side-note, the name workerize-loader is misleading. It took me a while to figure out that it came from this package since I was looking for a workerize-loader in node modules.

postMessage is not a function

I met the error

Uncaught TypeError: worker.postMessage is not a function ...

Can you please figure out why I get this error?

Here is my code.

import Worker from 'worker-plugin';

const worker = new Worker('./worker.jsx');
worker.onmessage = event=>{
console.log("WORKER response: ",event.data);
};
worker.postMessage(0); //this is the LINE the error complains about.

where worker.jsx is like following.

addEventListener('message',function(e){
postMessage("123 123 abc abc");
});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.