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
Improve tree-shaking in bundles for new installation methods #16292
Labels
squad:core
Issue to be handled by the Core team.
type:task
This issue reports a chore (non-production change) and other types of "todos".
Milestone
Comments
filipsobol
added
the
type:task
This issue reports a chore (non-production change) and other types of "todos".
label
Apr 26, 2024
filipsobol
added a commit
that referenced
this issue
May 8, 2024
Fix: Improve tree-shaking by prefixing mixin calls with `/* #__PURE__ */` magic comment. See #16292.
Important This comment shows progress (from top to bottom) of how each change improved the JavaScript bundle size. The bundles are minified, but not gzipped. Side-effectsSide-effect codeimport 'ckeditor5';
import 'ckeditor5-premium-features';
BuildBuild codeimport {
ClassicEditor,
Essentials,
CKFinderUploadAdapter,
Autoformat,
Bold,
Italic,
BlockQuote,
CKBox,
CKFinder,
EasyImage,
Heading,
Image,
ImageCaption,
ImageStyle,
ImageToolbar,
ImageUpload,
PictureEditing,
Indent,
Link,
List,
MediaEmbed,
Paragraph,
PasteFromOffice,
Table,
TableToolbar,
TextTransformation,
CloudServices,
Mention
} from 'ckeditor5';
import { CaseChange, SlashCommand } from 'ckeditor5-premium-features';
import 'ckeditor5/index.css';
import 'ckeditor5-premium-features/index.css';
ClassicEditor.create( document.querySelector( '#editor' ) as HTMLElement, {
plugins: [
Essentials,
CKFinderUploadAdapter,
Autoformat,
Bold,
Italic,
BlockQuote,
CKBox,
CKFinder,
CloudServices,
EasyImage,
Heading,
Image,
ImageCaption,
ImageStyle,
ImageToolbar,
ImageUpload,
Indent,
Link,
List,
MediaEmbed,
Paragraph,
PasteFromOffice,
PictureEditing,
Table,
TableToolbar,
TextTransformation,
Mention,
CaseChange,
SlashCommand
],
licenseKey: '<LICENSE_KEY>', // Replace this with your license key.
toolbar: {
items: [
'undo', 'redo',
'|', 'heading',
'|', 'bold', 'italic',
'|', 'link', 'uploadImage', 'insertTable', 'blockQuote', 'mediaEmbed',
'|', 'bulletedList', 'numberedList', 'outdent', 'indent', 'caseChange'
]
},
image: {
toolbar: [
'imageStyle:inline',
'imageStyle:block',
'imageStyle:side',
'|',
'toggleImageCaption',
'imageTextAlternative'
]
},
table: {
contentToolbar: [
'tableColumn',
'tableRow',
'mergeTableCells'
]
},
language: 'en'
} );
|
filipsobol
added a commit
that referenced
this issue
May 15, 2024
Internal: Improve tree-shaking by adding `#__PURE__` comments before function calls in global scope. See #16292.
filipsobol
added a commit
that referenced
this issue
May 20, 2024
…cts-and-arrays-in-global-scope Other: Add `#__PURE__` comments before objects and arrays in global scope. Related to #16292.
This was referenced May 20, 2024
This task is complete. I created a new one describing how we can further improve the bundle size. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
squad:core
Issue to be handled by the Core team.
type:task
This issue reports a chore (non-production change) and other types of "todos".
When working on the new installation methods (NIM), we noticed that the bundle size in the consuming projects was larger than we expected. Investigation showed that there was a lot of code in various core and commercial packages that was not properly tree-shaken.
Although we noticed this while working on NIM, this is not a new problem. Because in the old installation methods the plugins are imported directly from their respective packages, it was much less likely that the code that wasn't properly tree-shaken would end up unused. However, in NIM, all core and commercial packages are imported from the
ckeditor5
andckeditor5-premium-feature
packages. This means that pulling code from one plugin will also pull side-effects from all other plugins in these packages.How significant is this issue? When using the
0.0.0-nightly-20240426.0
build, the following imports will result in a bundle size of 1390.65 kB in Rollup and esbuild (minified, not gzipped):In a perfect world, the bundle size should be 0.0 kB because we didn't import anything specific. However, about 550 kB of that 1390.65 kB are essential packages (
engine
,ui
,core
,utils
,watchdog
) that are used by every plugin. While we could try to make this part tree-shakable, that's not the main focus of this task. The main focus now is to make the other 840 kB tree-shakable.Our initial tests show that we can reduce the bundle size to 738 kB (or 182 kB minzipped) without major code changes, with the potential to reduce the size by another 150 kB. This would be a 58% reduction from the current state.
Problems
Below are the main problems that we identified.
Static methods and fields
In our codebase, we make heavy use of static methods and fields. While support for static public methods was introduced in ES2015, support for static private methods and static fields (public and private) was added in ES2022.
Because support for all four combinations has long been available in TypeScript, it and other bundlers had to transpile them to support the older browser. Unfortunately, these workarounds make the output code less tree-shakable. For a simple example, consider the following code:
Since public static methods are well supported, the
public static method()
is transpiled tostatic method()
. However, thepublic static field
is transpiled to code that cannot be tree-shaken, and the output varies between transpilers. See the following links for TypeScript, esbuild, and SWC outputs:Change the output target to ES2022 to see how the workarounds disappears to something that can be easily analyzed and tree-shaken.
We can't change the target in the entire codebase because that would break support for webpack 4 (that's why we only target ES2019 for now), but we can change the target only in our new CLI tool to only affect new installation methods.
Before we can do that, however, we need to fix the problem described in #14703 and #16386, because part of our code is not valid and only works because of the transpilation.
Mixins
We use mixins in our codebase to share code between classes. However, the current implementation of mixins in TypeScript is not tree-shakable. Consider the following code:
When statically analyzing the code, it's impossible for bundlers to know what
ObservableMixin()
will return and if it contains side effects. This makes (some) classes that extend mixins not tree-shakable.This can be easily fixed by adding the
#__PURE__
comment to all mixin calls, like this:Alternatively (or even preferably), we could try annotating the mixin functions with
#__NO_SIDE_EFFECTS__
, but we need to make sure that this comment works when using webpack, Vite, and esbuild.Other problems
mix
.is
methods right, we used some workarounds which are not tree-shakable. We should move theis
implementations to static methods without breaking the API / types.turndown
andmarked
makes the bundlers unable to tree-shake them. We should refactor the code to fix it.How to debug
(One time) Setup repository
external
folder in the root of the repository.external
folder.external/ckeditor5-dev/packages/ckeditor5-dev-build-tools/src/config.ts
file and add theexperimentalLogSideEffects: true
property to the object returned from thegetRollupConfig
function.external/ckeditor5-dev/packages/ckeditor5-dev-build-tools/src/build.ts
file and addpreserveModules: true
to the object passed to thebuild.write
in thebuild
function (not thegenerateUmdBuild
function).yarn reinstall
in the root of theckeditor5
repository. This should install all dependencies and build the changes we made in theckeditor5-dev
repository.(One time) Setup Verdaccio
.npmrc
file and addregistry=http://localhost:4873/
to it.npm adduser --registry http://localhost:4873/
. Make sure to name the userckeditor
(this is required by one of our internal scripts).(One time) Setup test repository
vite
directory create the.npmrc
file and addregistry=http://localhost:4873/
to it.npm install
in thevite
directory.vite/src/main.ts
to:Test changes
In the
ckeditor5
repository:git add .
).npm run release:prepare-packages -- --nightly
in the root of theckeditor5
repository (you can comment out some of the steps in thescripts/release/preparepackages.js
file to speed up the process).npm run release:publish-packages -- --nightly
to publish the changes to Verdaccio.git restore .
).In the
new-installation-methods
repository:vite
directory.npm run build
to see the changes in the bundle size.meta.json
file to the esbuild analyzer for a more detailed analysis.The text was updated successfully, but these errors were encountered: