-
Notifications
You must be signed in to change notification settings - Fork 3k
perf(richtext-lexical): decrease size of field schema, minor perf optimizations #14248
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
Conversation
| ...serverProps, | ||
| // Manually inject lexical-specific `sanitizedEditorConfig` server prop, in order to reduce the size of the field schema. | ||
| // Otherwise, the editorConfig would be included twice - once on the top-level, and once as part of the `FieldComponent` server props. | ||
| sanitizedEditorConfig: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment - the sanitizedEditorConfig is huge. It's worth extracting this lexical-specific prop injection out of the lexical package and do it here in the ui package, in order to avoid the duplication
| sanitizedEditorConfig: finalSanitizedEditorConfig, | ||
| }, | ||
| }, | ||
| CellComponent: '@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed passing severProps that were completely unused. They just unnecessarily blew up the size of this field config.
| } | ||
| // @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve | ||
| featureI18n[lang].lexical.general = i18n[lang] | ||
| const featureI18n: Partial<GenericLanguages> = finalSanitizedEditorConfig.features.i18n |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TypeScript fixes. This gets rid of the // @ts-expect-error
📦 esbuild Bundle Analysis for payloadThis analysis was generated by esbuild-bundle-analyzer. 🤖
Largest pathsThese visualization shows top 20 largest paths in the bundle.Meta file: packages/next/meta_index.json, Out file: esbuild/index.js
Meta file: packages/payload/meta_index.json, Out file: esbuild/index.js
Meta file: packages/payload/meta_shared.json, Out file: esbuild/exports/shared.js
Meta file: packages/richtext-lexical/meta_client.json, Out file: esbuild/exports/client_optimized/index.js
Meta file: packages/ui/meta_client.json, Out file: esbuild/exports/client_optimized/index.js
Meta file: packages/ui/meta_shared.json, Out file: esbuild/exports/shared_optimized/index.js
DetailsNext to the size is how much the size has increased or decreased compared with the base branch of this PR.
|
| if ( | ||
| resolvedFeature.componentImports && | ||
| typeof resolvedFeature.componentImports === 'object' && | ||
| !Array.isArray(resolvedFeature.componentImports) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to previous weak typing, we were incorrectly adding componentImports to the featureClientImportMap, even though the JSDocs said that only objects will be added
| 'lexical_internal_feature', | ||
| featureKey, | ||
| ].join('.') | ||
| const featureSchemaPath = `${args.schemaPath}.lexical_internal_feature.${featureKey}` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perf improvement. Why .split and .join, if we can just manually append to the schemaPath string.
| const resolvedFeatureMapArray = Array.from( | ||
| args.sanitizedEditorConfig.resolvedFeatureMap.entries(), | ||
| ).sort((a, b) => a[1].order - b[1].order) | ||
| const resolvedFeatureMapArray = [...args.sanitizedEditorConfig.resolvedFeatureMap].sort( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently a tiny bit faster than Array.from() + .entries(). Also easier to read
| if (ClientComponent) { | ||
| addToImportMap(ClientComponent) | ||
| } | ||
| addToImportMap(resolvedFeature.ClientFeature) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addToImportMap already skips null/undefined components => remove duplicative check
| for (const component of Object.values(resolvedFeature.componentImports)) { | ||
| addToImportMap(component as PayloadComponent) | ||
| } | ||
| addToImportMap(Object.values(resolvedFeature.componentImports)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addToImportMap already checks and handles arrays
| 'A Feature you have installed does not return the client props as clientFeatureProps. Please make sure to always return those props, even if they are null, as other important props like order and featureKey are later on injected.', | ||
| ) | ||
| } | ||
| featureProviderMap.set(featureProvider.clientFeatureProps.featureKey, featureProvider) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One less for loop by using existing for loop here
| props.admin = admin | ||
| } | ||
| if (Object.keys(featureClientImportMap).length) { | ||
| props.featureClientImportMap = featureClientImportMap |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not unnecessarily send featureClientImportMap to client if it's empty. We do the same for other props
| ) // Execute the clientFeatureProvider function here, as the server cannot execute functions imported from use client files | ||
| } | ||
|
|
||
| const finalLexicalEditorConfig = lexicalEditorConfig |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already fall back to defaultEditorLexicalConfig when destructuring the component props. => unnecessary to do that here
|
🚀 This is included in version v3.61.0 |
Following up on #14228, this PR further reduces the size of the lexical field schema. Reasoning can be found in that PR.
Additionally, this PR includes a bunch of random TypeScript/JSDocs/Performance improvements, extracted from #14244 to keep the PR diff small. Not worth making separate PRs for each individual change.