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

WIP: Add support for WebWorker with worker-loader #5886

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docusaurus/docs/adding-a-sass-stylesheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ This will allow you to do imports like
To use imports relative to a path you specify, and from `node_modules` without adding the `~` prefix, you can add a [`.env` file](https://github.com/facebook/create-react-app/blob/master/docusaurus/docs/adding-custom-environment-variables.md#adding-development-environment-variables-in-env) at the project root with the variable `SASS_PATH=node_modules:src`. To specify more directories you can add them to `SASS_PATH` separated by a `:` like `path1:path2:path3`.

If you set `SASS_PATH=node_modules:src`, this will allow you to do imports like

```scss
@import 'styles/colors'; // assuming a styles directory under src/, where _colors.scss partial file exists.
@import 'nprogress/nprogress'; // importing a css file from the nprogress node module
Expand Down
4 changes: 2 additions & 2 deletions docusaurus/docs/advanced-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ id: advanced-configuration
title: Advanced Configuration
---

You can adjust various development and production settings by setting environment variables in your shell or with [.env](adding-custom-environment-variables.md#adding-development-environment-variables-in-env).
You can adjust various development and production settings by setting environment variables in your shell or with [.env](adding-custom-environment-variables.md#adding-development-environment-variables-in-env).

> Note: You do not need to declare `REACT_APP_` before the below variables as you would with custom environment variables.

Expand All @@ -13,7 +13,7 @@ You can adjust various development and production settings by setting environmen
| HOST | ✅ Used | 🚫 Ignored | By default, the development web server binds to `localhost`. You may use this variable to specify a different host. |
| PORT | ✅ Used | 🚫 Ignored | By default, the development web server will attempt to listen on port 3000 or prompt you to attempt the next available port. You may use this variable to specify a different port. |
| HTTPS | ✅ Used | 🚫 Ignored | When set to `true`, Create React App will run the development server in `https` mode. |
| PUBLIC_URL | 🚫 Ignored | ✅ Used | Create React App assumes your application is hosted at the serving web server's root or a subpath as specified in [`package.json` (`homepage`)](deployment#building-for-relative-paths). Normally, Create React App ignores the hostname. You may use this variable to force assets to be referenced verbatim to the url you provide (hostname included). This may be particularly useful when using a CDN to host your application. |
| PUBLIC_URL | 🚫 Ignored | ✅ Used | Create React App assumes your application is hosted at the serving web server's root or a subpath as specified in [`package.json` (`homepage`)](deployment#building-for-relative-paths). Normally, Create React App ignores the hostname. You may use this variable to force assets to be referenced verbatim to the url you provide (hostname included). This may be particularly useful when using a CDN to host your application. |
| CI | ✅ Used | ✅ Used | When set to `true`, Create React App treats warnings as failures in the build. It also makes the test runner non-watching. Most CIs set this flag by default. |
| REACT_EDITOR | ✅ Used | 🚫 Ignored | When an app crashes in development, you will see an error overlay with clickable stack trace. When you click on it, Create React App will try to determine the editor you are using based on currently running processes, and open the relevant source file. You can [send a pull request to detect your editor of choice](https://github.com/facebook/create-react-app/issues/2636). Setting this environment variable overrides the automatic detection. If you do it, make sure your systems [PATH](<https://en.wikipedia.org/wiki/PATH_(variable)>) environment variable points to your editor’s bin folder. You can also set it to `none` to disable it completely. |
| CHOKIDAR_USEPOLLING | ✅ Used | 🚫 Ignored | When set to `true`, the watcher runs in polling mode, as necessary inside a VM. Use this option if `npm start` isn't detecting changes. |
Expand Down
2 changes: 1 addition & 1 deletion docusaurus/docs/loading-graphql-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ You can also use the `gql` template tag the same way you would use the non-macro

```js
import { gql } from 'graphql.macro';

const query = gql`
query User {
user(id: 5) {
Expand Down
101 changes: 101 additions & 0 deletions docusaurus/docs/using-web-workers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---
id: using-web-workers
titile: Using Web Workers
---

[Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers)
can be used by including files with the `.worker.js` in your application. Files
with this extension make use of [`worker-loader`](https://github.com/webpack-contrib/worker-loader)
to bundle worker files which can be used by your main application.

A sample WebWorker may look like:

```js
// hello.worker.js

let helloInterval;

const sayHello = () => {
self.postMessage({ message: 'Hello' });
};

self.addEventListener('message', event => {
if (event.data.run === true) {
self.postMessage({ status: 'Worker started' });
helloInterval = setInterval(sayHello, 1000);
}

if (event.data.run === false) {
self.postMessage({ status: 'Worker stopped' });
clearInterval(helloInterval);
}
});
```

This can subsequently be imported and used in your application as:

```js
// index.js

import HelloWorker from './hello.worker.js';

const helloWorker = new HelloWorker();
let messageCount = 0;

helloWorker.postMessage({ run: true });

helloWorker.onmessage = event => {
if (event.data.status) {
console.log('STATUS', event.data.status);
}

if (event.data.message) {
messageCount += 1;
console.log('MESSAGE', event.data.message);

if (messageCount >= 5) {
helloWorker.postMessage({ run: false });
}
}
};
```

## Importing modules into your workers

Worker files can import modules just the same as the rest of your
application. These will be compiled following the same rules and features as
regular `.js` or `.ts` files.

## Usage with TypeScript

Workers can be written in TypeScript, however you are required to declare the
file as a worker in order for the compiler to understand that it is a worker.
This can be done by including:

```ts
/// <reference lib="webworker" />
```

at the beginning of all of your `.worker.ts` files.

Because the interface imported is different from what is in your worker files,
you'll also need to tell TypeScript what you're expecting this interface to look
like. To achieve this, you will need to have a module declaration in each of
your worker files like so:

```ts
// my.worker.ts
// <worker code>

// Necessary to tell typescript that this worker file is a module even though
// it may not have any explicit imports or exports
export {};

// Override the module declaration to tell Typescript that when imported, this
// is what the imported types will look like.
declare module './my.worker' {
export default class TestWorker extends Worker {
constructor();
}
}
```
1 change: 1 addition & 0 deletions docusaurus/website/sidebars.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"adding-a-router",
"adding-custom-environment-variables",
"making-a-progressive-web-app",
"using-web-workers",
"production-build"
],
"Testing": ["running-tests", "debugging-tests"],
Expand Down
73 changes: 42 additions & 31 deletions packages/eslint-config-react-app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,40 +52,51 @@ module.exports = {
},
},

overrides: {
files: ['**/*.ts', '**/*.tsx'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
overrides: [
{
files: ['**/*.ts', '**/*.tsx'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},

// typescript-eslint specific options
warnOnUnsupportedTypeScriptVersion: true,
// typescript-eslint specific options
warnOnUnsupportedTypeScriptVersion: true,
},
plugins: ['@typescript-eslint'],
rules: {
// These ESLint rules are known to cause issues with typescript-eslint
// See https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/src/configs/recommended.json
camelcase: 'off',
indent: 'off',
'no-array-constructor': 'off',
'no-unused-vars': 'off',
'@typescript-eslint/no-angle-bracket-type-assertion': 'warn',
'@typescript-eslint/no-array-constructor': 'warn',
'@typescript-eslint/no-namespace': 'error',
'@typescript-eslint/no-unused-vars': [
'warn',
{
args: 'none',
ignoreRestSiblings: true,
},
],
},
},
plugins: ['@typescript-eslint'],
rules: {
// These ESLint rules are known to cause issues with typescript-eslint
// See https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/src/configs/recommended.json
camelcase: 'off',
indent: 'off',
'no-array-constructor': 'off',
'no-unused-vars': 'off',

'@typescript-eslint/no-angle-bracket-type-assertion': 'warn',
'@typescript-eslint/no-array-constructor': 'warn',
'@typescript-eslint/no-namespace': 'error',
'@typescript-eslint/no-unused-vars': [
'warn',
{
args: 'none',
ignoreRestSiblings: true,
},
],
{
files: ['**/*.worker.js', '**/*.worker.mjs', '**/*.worker.ts'],
rules: {
'no-restricted-globals': ['error'].concat(
restrictedGlobals.filter(g => g !== 'self')
),
// Necessary to allow stubbed class declartions in typescript workers
'no-useless-constructor': 'off',
},
},
},
],

// NOTE: When adding rules here, you need to make sure they are compatible with
// `typescript-eslint`, as some rules such as `no-array-constructor` aren't compatible.
Expand Down
58 changes: 58 additions & 0 deletions packages/react-scripts/config/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ module.exports = function(webpackEnv) {
// We inferred the "public path" (such as / or /my-project) from homepage.
// We use "/" in development.
publicPath: publicPath,
globalObject: 'this',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required due to what seems to be a bug/regression in webpack 4. More details can be found here: webpack/webpack#6642

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @iansu , can i ask a question . Why globalObject value is this , not self ?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ri7nz -- as far as I can tell, the eslint config inside create-react-app bugs out on self keyword saying it's invalid, and hence the empty production build when using worker-loader yourself.

// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
Expand Down Expand Up @@ -350,6 +351,63 @@ module.exports = function(webpackEnv) {
name: 'static/media/[name].[hash:8].[ext]',
},
},
// Process WebWorker JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
{
test: /\.worker\.(js|mjs|ts)$/,
include: paths.appSrc,
use: [
require.resolve('worker-loader'),
{
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
// @remove-on-eject-begin
babelrc: false,
configFile: false,
presets: [require.resolve('babel-preset-react-app')],
// Make sure we have a unique cache identifier, erring on the
// side of caution.
// We remove this when the user ejects because the default
// is sane and uses Babel options. Instead of options, we use
// the react-scripts and babel-preset-react-app versions.
cacheIdentifier: getCacheIdentifier(
isEnvProduction
? 'production'
: isEnvDevelopment && 'development',
[
'babel-plugin-named-asset-import',
'babel-preset-react-app',
'react-dev-utils',
'react-scripts',
]
),
// @remove-on-eject-end
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent:
'@svgr/webpack?-prettier,-svgo![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,
Copy link

@amankkg amankkg May 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be isEnvDevelopment instead of true? nvm...

cacheCompression: isEnvProduction,
compact: isEnvProduction,
},
},
],
},
// Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
{
Expand Down
2 changes: 1 addition & 1 deletion packages/react-scripts/fixtures/kitchensink/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class App extends Component {
// This works around an issue of a duplicate hash in the href
// Ex: http://localhost:3001/#array-destructuring#array-destructuring
// This seems like a jsdom bug as the URL in initDom.js appears to be correct
const feature = url.slice(url.lastIndexOf("#") + 1);
const feature = url.slice(url.lastIndexOf('#') + 1);

switch (feature) {
case 'array-destructuring':
Expand Down
5 changes: 3 additions & 2 deletions packages/react-scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"pnp-webpack-plugin": "1.2.1",
"postcss-flexbugs-fixes": "4.1.0",
"postcss-loader": "3.0.0",
"postcss-normalize": "7.0.1",
"postcss-normalize": "7.0.1",
"postcss-preset-env": "6.6.0",
"postcss-safe-parser": "4.0.1",
"react-app-polyfill": "^0.2.2",
Expand All @@ -72,7 +72,8 @@
"webpack": "4.29.6",
"webpack-dev-server": "3.2.1",
"webpack-manifest-plugin": "2.0.4",
"workbox-webpack-plugin": "4.2.0"
"workbox-webpack-plugin": "4.2.0",
"worker-loader": "^2.0.0"
},
"devDependencies": {
"react": "^16.8.4",
Expand Down
7 changes: 5 additions & 2 deletions packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ const immer = require('react-dev-utils/immer').produce;
const globby = require('react-dev-utils/globby').sync;

function writeJson(fileName, object) {
fs.writeFileSync(fileName, JSON.stringify(object, null, 2).replace(/\n/g, os.EOL) + os.EOL);
fs.writeFileSync(
fileName,
JSON.stringify(object, null, 2).replace(/\n/g, os.EOL) + os.EOL
);
}

function verifyNoTypeScript() {
Expand Down Expand Up @@ -184,7 +187,7 @@ function verifyTypeScriptSetup() {
)
);
}

console.log(e && e.message ? `${e.message}` : '');
process.exit(1);
}
Expand Down