-
Notifications
You must be signed in to change notification settings - Fork 3
Faster and more robust editor asset management #233
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
base: task/editor-settings
Are you sure you want to change the base?
Conversation
| import CryptoKit | ||
| import SwiftSoup | ||
|
|
||
| struct EditorAssetsManifest: Codable { |
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.
This is an existing struct. I made no changes to it – just renamed the file.
a9806a5 to
0b9616f
Compare
0b9616f to
dac7dc6
Compare
dcalhoun
left a comment
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.
Thank you for creating this!
This makes sense to me a high level. It feels like a worthwhile improvement overall, especially given the performance improvements.
I left a few thoughts to consider. Many are for bettering my own understanding.
| let config = EditorConfigurationBuilder() | ||
| .setShouldUsePlugins(false) | ||
| .setSiteUrl("") | ||
| .setSiteUrl("bundled") |
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.
What is the purpose of this change? Is this merely an identifier of why the configuration is empty?
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.
Oh, I added it as part of another changes I later reverted. I'll revert this as well.
| /// Endpoint for loading editor assets, used when enabling `shouldUsePlugins` | ||
| public var editorAssetsEndpoint: URL? |
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.
Why are we removing editorAssetsEndpoint? I believe it's useful in the future whenever we change the REST API endpoint used, it also allows alternatives for third-parties as needed.
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.
I suggest to add it again in the future if it's needed. For now, I kept it simple as EditorService knows what endpoints to use, and the code in the app was duplicating the same endpoint.
| /// Logs emitted at or above this level will be printed to the debug console | ||
| public let logLevel: LogLevel |
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.
Are host apps still able to configure the log levels emitted from the JavaScript logger utility? I believe that to be beneficial.
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.
Yes, I still inject the same log level in JS: logLevel: '\(EditorLogger.logLevel.rawValue)',
| }, | ||
| logLevel: '\(configuration.logLevel)' | ||
| logLevel: '\(EditorLogger.logLevel.rawValue)', | ||
| manifest: \(dependencies?.manifest ?? "undefined") |
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.
Any performance implications from adding this? 🤔 I suppose it is no larger than editorSettings, eh?
I wonder if it's worth spreading the properties over the top-level object. I could see one day allowing GBK configuration to control allowedBlockTypes.
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.
Any performance implications from adding this? 🤔 I suppose it is no larger than editorSettings, eh?
I wouldn't expect it to be slower than the current implementation, but I already had some second thoughts yesterday. Is it safe to pass these larger strings directly in JS this way? Does it need escaping? Is there a more robust way to do it? Should we pass the whole pre-processed manifest as a separate object? Both settings and manifest seem to have <scripts> and <styles>, is there a better way to inject them? What about allowedBlockTypes? Should the app combine them (for core and plugins)?
| const gbkit = getGBKit(); | ||
|
|
||
| // iOS provides manifest directly in GBKit configuration | ||
| if ( window.webkit && gbkit.manifest ) { | ||
| return gbkit.manifest; | ||
| } | ||
|
|
||
| const { siteApiRoot, editorAssetsEndpoint, authHeader } = getGBKit(); | ||
| // Android fallback - fetch from API | ||
| const { siteApiRoot, editorAssetsEndpoint, authHeader } = gbkit; |
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.
I don't believe there is a downside to de-structuring all values up front.
| const gbkit = getGBKit(); | |
| // iOS provides manifest directly in GBKit configuration | |
| if ( window.webkit && gbkit.manifest ) { | |
| return gbkit.manifest; | |
| } | |
| const { siteApiRoot, editorAssetsEndpoint, authHeader } = getGBKit(); | |
| // Android fallback - fetch from API | |
| const { siteApiRoot, editorAssetsEndpoint, authHeader } = gbkit; | |
| const { manifest, siteApiRoot, editorAssetsEndpoint, authHeader } = getGBKit(); | |
| // iOS provides manifest directly in GBKit configuration | |
| if ( window.webkit && manifest ) { | |
| return manifest; | |
| } | |
| // Android fallback - fetch from API |
Alternatively, I don't believe there is a downside to invoking getGBKit() multiple times, as it early returns with the cached value on the window.
| const gbkit = getGBKit(); | |
| // iOS provides manifest directly in GBKit configuration | |
| if ( window.webkit && gbkit.manifest ) { | |
| return gbkit.manifest; | |
| } | |
| const { siteApiRoot, editorAssetsEndpoint, authHeader } = getGBKit(); | |
| // Android fallback - fetch from API | |
| const { siteApiRoot, editorAssetsEndpoint, authHeader } = gbkit; | |
| const { manifest } = getGBKit(); | |
| // iOS provides manifest directly in GBKit configuration | |
| if ( window.webkit && gbkit.manifest ) { | |
| return gbkit.manifest; | |
| } | |
| // Android fallback - fetch from API | |
| const { siteApiRoot, editorAssetsEndpoint, authHeader } = getGBKit(); |
|
I made a few more changes:
With these changes, this whole step now takes 2ms. I had to switch to a different task today and didn't have time to review the final PR. I'll do it when I have time. It should be good. |
What?
The subsequent editor loads are now ~1.3s faster. The first launch is ~1s faster.
There are a few changes compared to the previous implementation:
EditorService. The assets manifest is now fetched in parallel with the editor settings.EditorServicenow triggers the actual download of the assets. The assets are put into aGutenbergKit/{siteURL}/assetsfolder.EditorAssetsProviderand instead inject the plugin styles and scripts inwindow.GBKitwhen launching the editor (the same way it's done for editor settings). It happens only ifEditorServiceverifies that all the required files exist on disk. If not, the plugins are disabled until the next time.EditorServicereads editor settings and assets manifest in one atomic operation. When new manifest is loaded, it replaces the current one only after fetching all assets for it. Until then, the editor will always use the previous version of the manifest.editorSettingsfromEditorConfiguration. Add them to internalEditorDependenciestypes fetched byEditorViewControllerMinor changes:
editorAssetsEndpointparameter fromEditorConfigurationPlanned future changes:
Why?
How?
Testing Instructions
Accessibility Testing Instructions
Screenshots or screencast
Before / After
Note: the preparing editor message disappears quicker because "fetch editor settings" is now a step inside
EditorViewController.comparison.mp4