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

Migrate to ESBuild in Webpack #10055

Merged
merged 6 commits into from
Aug 18, 2023
Merged

Migrate to ESBuild in Webpack #10055

merged 6 commits into from
Aug 18, 2023

Conversation

ShGKme
Copy link
Contributor

@ShGKme ShGKme commented Jul 26, 2023

☑️ Resolves

Part 1: fixing issues with webrtc-adapter and attachmediastream.

webrtc-adapter library is supposed to be imported as ESModule and doesn't work with require.

Because of this, we had a special processing for webrtc-adapter with babel-plugin-add-module-export:

/**
* webrtc-adapter main module does no longer provide
* "module.exports", which is expected by some elements using it
* (like "attachmediastream"), so it needs to be added back with
* a plugin.
*/
test: /node_modules[\\/]webrtc-adapter[\\/].*\.js$/,
loader: 'babel-loader',
options: {
plugins: ['add-module-exports'],
presets: [
/**
* From "add-module-exports" documentation:
* "webpack doesn't perform commonjs transformation for
* codesplitting. Need to set commonjs conversion."
*/
['@babel/env', { modules: 'commonjs' }],
],
},
},

But this problem can be avoided by:

  1. Importing webrtc-adapter with ESM import instead of require
  2. Using attachmediastream with its build for bundlers (
    import attachmediastream from 'attachmediastream/attachmediastream.bundle.js'`

This PR does as described above. So we don't need additional babel plugin and special processing for webrtc-adapter. This is also required for migration to ESBuild as well as Vite.

This change can be separated into individual PR.

cc @danxuliu

Part 2: Use ESBuild for transpiling JS instead of Terser and minimizing instead of Terser

Production build with Terser minification in Webpack is very slow and requires much RAM.
The same (but not so huge) problem with Babel.

I close all applications on my 16GB RAM server to be able to make a production build 😣

ESBuild is slightly worse as a minifier but much more resource-effective. Results on my server:

Metric Babel + Terser ESBuild
npm run build assets size 51.33 MB 52.41 MB
npm run build time 83.9s 26.5s
npm run build RAM 8.8 GB 3.3 GB
npm run dev time 25.5s 13.8s
npm run dev RAM 1.3 GB 0.9 GB

👀 Alternative solutions

Fully migrate to Vite

🚧 Tasks

  • Add esbuild-loader
  • Use ESBuildPlugin as minimizer
  • Use esbuild-loader as JavaScript transpiler
  • Setup config merging rules
  • Check with Talk Desktop
    • Only requires to add esbuild-loader as a dependency and change target to es2022
    • But requires a bit more work to make a config compatible with both esbuild and babel
  • Update Babel config to be used only for Jest tests

🏁 Checklist

@ShGKme
Copy link
Contributor Author

ShGKme commented Jul 26, 2023

We can use this as a simple solution to save some time and RAM requirements for production build.

Or we can try to make a full Vite migration and make a hackable shared Vite config with @susnux

@ShGKme
Copy link
Contributor Author

ShGKme commented Jul 26, 2023

UPD: this vite config https://github.com/nextcloud/nextcloud-vite-config

@Antreesy
Copy link
Contributor

So far, here a mine results (16 Gb RAM):

Param Babel + Terser ESBuild
Notes Had to leave only IDE working IDE + Talk Desktop + Docker
assets by status 55 MiB 56 MiB
compiled in 193 700 ms 47 070 ms

@ShGKme
Copy link
Contributor Author

ShGKme commented Jul 27, 2023

Vite migration is not doing well for me for now.

I'd consider using this PR as a very simple and not so risky solution.

@SystemKeeper
Copy link
Contributor

Works for me only if I downgrade esbuild-loader to version 2:

diff --git a/package.json b/package.json
index 8520ca615..79ce7f82d 100644
--- a/package.json
+++ b/package.json
@@ -78,7 +78,7 @@
     "@vue/vue2-jest": "^29.2.4",
     "babel-loader-exclude-node-modules-except": "^1.2.1",
     "babel-plugin-add-module-exports": "^1.0.4",
-    "esbuild-loader": "^3.0.1",
+    "esbuild-loader": "^2.21.0",
     "flush-promises": "^1.0.2",
     "jest": "^29.6.1",
     "jest-environment-jsdom": "^29.6.1",
diff --git a/webpack.config.js b/webpack.config.js
index 450b878a2..b87670a7c 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,6 +1,6 @@
 const path = require('node:path')
 
-const { EsbuildPlugin } = require('esbuild-loader')
+const { ESBuildMinifyPlugin } = require('esbuild-loader')
 const webpack = require('webpack')
 const { mergeWithRules } = require('webpack-merge')
 
@@ -62,7 +62,7 @@ module.exports = talkWebMerge(nextcloudWebpackConfig, commonWebpackConfig, {
 
        optimization: {
                minimizer: [
-                       new EsbuildPlugin({
+                       new ESBuildMinifyPlugin({
                                target: 'es2020',
                        }),
                ],

If I use version 3, I get

Error: File '@vue/tsconfig/tsconfig.json' not found.
    at Oe (/Users/marcel/Desktop/Nextcloud/workspace/server/apps-extra/spreed/node_modules/get-tsconfig/dist/index.cjs:3:9061)
    at G (/Users/marcel/Desktop/Nextcloud/workspace/server/apps-extra/spreed/node_modules/get-tsconfig/dist/index.cjs:3:9848)
    at Object.je [as getTsconfig] (/Users/marcel/Desktop/Nextcloud/workspace/server/apps-extra/spreed/node_modules/get-tsconfig/dist/index.cjs:3:10586)
    at Object.ESBuildLoader (/Users/marcel/Desktop/Nextcloud/workspace/server/apps-extra/spreed/node_modules/esbuild-loader/dist/index.cjs:49:23)
    at LOADER_EXECUTION (/Users/marcel/Desktop/Nextcloud/workspace/server/apps-extra/spreed/node_modules/loader-runner/lib/LoaderRunner.js:132:14)
    at runSyncOrAsync (/Users/marcel/Desktop/Nextcloud/workspace/server/apps-extra/spreed/node_modules/loader-runner/lib/LoaderRunner.js:133:4)
    at iterateNormalLoaders (/Users/marcel/Desktop/Nextcloud/workspace/server/apps-extra/spreed/node_modules/loader-runner/lib/LoaderRunner.js:250:2)
    at /Users/marcel/Desktop/Nextcloud/workspace/server/apps-extra/spreed/node_modules/loader-runner/lib/LoaderRunner.js:223:4
    at /Users/marcel/Desktop/Nextcloud/workspace/server/apps-extra/spreed/node_modules/webpack/lib/NormalModule.js:836:15
    at eval (eval at create (/Users/marcel/Desktop/Nextcloud/workspace/server/apps-extra/spreed/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:12:1)

It works if I add a empty tsconfig.json file...

@SystemKeeper
Copy link
Contributor

Okay, so the issue is the following:

  • I have cloned the spreed repo in server/apps-extra/spreed/.
  • According to https://www.typescriptlang.org/docs/handbook/tsconfig-json.html the tsconfig.json file is searched in the current directory and if not found, searched in the parent directory
  • Since we don't have a tsconfig.json in talk, the tsconfig.json of server is used
  • As I usually don't develop frontend on server, I did not run npm ci in the server repository. As the tsconfig.json from server extends @vue/tsconfig/tsconfig.json, this file can't be found on my machine.
  • Running npm ci inside the server repo was enough to make talk in a subdirectory build (note: it worked fine as well when moved outside of the server repository, e.g. no tsconfig.json could be located in any parent directory).

Would it make sense to adjust the behaviour in talk about that? Either webpack config or include a tsconfig.json to prevent any sideeffects with parent-level tsconfig.json files?

@SystemKeeper
Copy link
Contributor

SystemKeeper commented Jul 27, 2023

For the performance:

# npm run build
webpack 5.76.2 compiled with 2 warnings in 15411 ms

# npm run dev
webpack 5.76.2 compiled successfully in 7790 ms

so that's quite nice ;)

Reference on master:

# npm run build
webpack 5.76.2 compiled with 2 warnings in 49627 ms

# npm run dev
webpack 5.76.2 compiled successfully in 12890 ms

@ShGKme
Copy link
Contributor Author

ShGKme commented Jul 28, 2023

Thank you @SystemKeeper for your investigation!

So it seems that eslint-loader is trying to find tsconfig by get-tsconfig and then the config is found in the server root. And there is no way to disable it ><

https://github.com/esbuild-kit/esbuild-loader/blob/60cb3b4746c675167ff3a3ab4f4bddefde9e44fe/src/loader.ts#L51-L65

I'll implicitly set some config to avoid such errors.

@ShGKme
Copy link
Contributor Author

ShGKme commented Jul 28, 2023

Reference on master:
# npm run build
49627 ms
# npm run dev
12890 ms

So fast 😯

@ShGKme ShGKme requested a review from danxuliu July 28, 2023 10:30
@ShGKme ShGKme marked this pull request as ready for review July 28, 2023 10:30
@ShGKme
Copy link
Contributor Author

ShGKme commented Jul 28, 2023

@SystemKeeper Could you, please, check that now it works for you?

@ShGKme ShGKme changed the title chore: use ESBuild in Webpack Migrate to ESBuild in Webpack Jul 28, 2023
@ShGKme ShGKme removed the request for review from skjnldsv July 28, 2023 10:32
@SystemKeeper
Copy link
Contributor

@SystemKeeper Could you, please, check that now it works for you?

Works fine now for me, thanks 👍

@ShGKme
Copy link
Contributor Author

ShGKme commented Jul 28, 2023

Ready for the the review, description updated

Copy link
Contributor

@Antreesy Antreesy left a comment

Choose a reason for hiding this comment

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

Worked for me as well, finally can build a Talk app 🥲

Copy link
Member

@nickvergessen nickvergessen left a comment

Choose a reason for hiding this comment

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

Also compiles here, 11s dev build, 21s prod build

@susnux
Copy link

susnux commented Aug 4, 2023

Use only one module system (ESM) everywhere and do not mix

Much cleaner solution 👍

@ShGKme ShGKme marked this pull request as ready for review August 4, 2023 19:36
@ShGKme
Copy link
Contributor Author

ShGKme commented Aug 4, 2023

Calls work now, including video, virtual backgrounds, and blur.

Copy link
Contributor

@Antreesy Antreesy left a comment

Choose a reason for hiding this comment

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

Also tested the main functionality in calls and chats, Firefox and Chrome (Linux). Consoles are clean 🏁

We could try to merge it and see if we'll catch some bugs (because of these changes) during the development or backport (though any issues with bundling will be seen on sermo next day, I suppose).

module.exports = function(mode, constraints, cb) {
/**
*
* @param mode
Copy link
Contributor

Choose a reason for hiding this comment

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

@ShGKme ShGKme requested a review from danxuliu August 14, 2023 15:13
@nickvergessen
Copy link
Member

or backport (though any issues with bundling will be seen on sermo next day, I suppose).

This is far from a change I would put into a stable branch …

@Antreesy
Copy link
Contributor

This is far from a change I would put into a stable branch …

I meant, that it could bring changes we will notice when backporting another fixes. I also don't mean to backport exactly this PR, and let it simmer on the master branch

Copy link
Member

@danxuliu danxuliu left a comment

Choose a reason for hiding this comment

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

Tested and works 👍

As a rebase is anyway needed I would suggest to squash the last commit, build: always use ESM and don't mix with CJS, into the first one (and adjust the commit description if needed), build: fix bundling issues with webrtc-adapter and attachmediastream , as calls do not work after the first commit without the changes from the last commit.

Besides that, when import attachMediaStream from 'attachmediastream was used, a mock for attachmediastream was needed in the unit tests. Now that the bundle is used the mock is no longer needed (I have not actually checked why, though; maybe because the bundle includes its own copy of webrtc-adapter or something like that, but I do not really know), so it could be removed.

src/utils/webrtc/simplewebrtc/localmedia.js Outdated Show resolved Hide resolved
@ShGKme ShGKme force-pushed the chore/esbuild branch 2 times, most recently from 9c5f4e4 to 2ba4088 Compare August 17, 2023 14:50
@ShGKme
Copy link
Contributor Author

ShGKme commented Aug 17, 2023

As a rebase is anyway needed I would suggest to squash the last commit, build: always use ESM and don't mix with CJS, into the first one (and adjust the commit description if needed), build: fix bundling issues with webrtc-adapter and attachmediastream , as calls do not work after the first commit without the changes from the last commit.

Not exactly. They are different issues.

The first commit fixes the issue we had even in Webpack, having to add the Babel plugin as a fix.

While moving from mixed modules to ESM is required for ESBuild (and Vite in the future), because it has different module interop mechanism.

@ShGKme
Copy link
Contributor Author

ShGKme commented Aug 17, 2023

@ShGKme ShGKme requested a review from danxuliu August 17, 2023 14:53
@ShGKme ShGKme self-assigned this Aug 17, 2023
Copy link
Member

@danxuliu danxuliu left a comment

Choose a reason for hiding this comment

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

As a rebase is anyway needed I would suggest to squash the last commit, build: always use ESM and don't mix with CJS, into the first one (and adjust the commit description if needed), build: fix bundling issues with webrtc-adapter and attachmediastream , as calls do not work after the first commit without the changes from the last commit.

Not exactly. They are different issues.

The first commit fixes the issue we had even in Webpack, having to add the Babel plugin as a fix.

While moving from mixed modules to ESM is required for ESBuild (and Vite in the future), because it has different module interop mechanism.

I have to admit that I forgot that changing require('XXX') calls to require('XXX').default calls was another option to fix the first commit (and, in fact, changing only require('./peer.js') would be needed, not the others). That is why I thought that moving from mixed modules to ESM was also a requirement to fix the issues in Webpack 🤦

Had I remembered it before I would have suggested to change require('./peer.js') to require('./peer.js').default in the first commit instead ;-) (but it would still be a nice change for correctness and future reference :-) )

In any case, tested and works 👍

- Always use ES import to import `webrtc-adapter`
- Use `attachmediastream` with its build for bundlers
- Remove special transpiling babel config with `add-module-exports` for importing `webrtc-adapter`

Signed-off-by: Grigorii K. Shartsev <[email protected]>
Signed-off-by: Daniel Calviño Sánchez <[email protected]>
It is unused after fixing errors with webrtc-adapter (attachmediastream).

Signed-off-by: Grigorii K. Shartsev <[email protected]>
Signed-off-by: Grigorii K. Shartsev <[email protected]>
Without local tsconfig.json esbuild-loader is searchting for the config in any parent directory.
Then server's root tsconfig is used. It is not related to Talk and requires server's node_modules to be
installed.

Signed-off-by: Grigorii K. Shartsev <[email protected]>
Copy link
Member

@danxuliu danxuliu left a comment

Choose a reason for hiding this comment

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

As agreed with @ShGKme , rebased to fix the first and last commits.

Tested and works 👍

@danxuliu danxuliu merged commit bd997d3 into master Aug 18, 2023
18 checks passed
@danxuliu danxuliu deleted the chore/esbuild branch August 18, 2023 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Decrease the bundle size and the build time
6 participants