-
Notifications
You must be signed in to change notification settings - Fork 799
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
Boost: refactor Critical CSS: Part 1 #22163
Conversation
Signed-off-by: pyronaur <hi@pyronaur.com>
Signed-off-by: pyronaur <hi@pyronaur.com>
Signed-off-by: pyronaur <hi@pyronaur.com>
Added Notification base class and extended to Recommendation class to cleanup the current design.
Change module constructors to on_prepare so that the actions calls are not duplicated.
Signed-off-by: pyronaur <hi@pyronaur.com>
…etpack into refactor/boost-critical-css
Signed-off-by: pyronaur <hi@pyronaur.com>
Creating an absolute mess out of the Critical CSS Class. Signed-off-by: pyronaur <hi@pyronaur.com>
Signed-off-by: pyronaur <hi@pyronaur.com>
Signed-off-by: pyronaur <hi@pyronaur.com>
Signed-off-by: pyronaur <hi@pyronaur.com>
Signed-off-by: pyronaur <hi@pyronaur.com>
Signed-off-by: pyronaur <hi@pyronaur.com>
`! is_admin()` guard clause was protecting against showing Critical CSS anywhere else but the admin :) Signed-off-by: pyronaur <hi@pyronaur.com>
Signed-off-by: pyronaur <hi@pyronaur.com>
Move providers to `critical-css/path-providers/providers` Signed-off-by: pyronaur <hi@pyronaur.com>
Signed-off-by: pyronaur <hi@pyronaur.com>
Signed-off-by: pyronaur <hi@pyronaur.com>
Signed-off-by: pyronaur <hi@pyronaur.com>
Signed-off-by: pyronaur <hi@pyronaur.com>
This is not a permanent fix, but I just need e2e tests to run right now
Add an interface and a class to ensure that a class is going to need to be set-up at a later point in time via hook.
@@ -54,21 +54,13 @@ class Admin { | |||
*/ | |||
private $speed_score; | |||
|
|||
/** | |||
* Initialize the class and set its properties. |
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.
😲 Is THAT what a constructor does?!
…ting interfaces without warty comments
…he UI doesn't freeze if the server 500s
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 made a couple of improvements:
- b7401e5 adds an exception to the "unused variable" rule, to allow any that start with an
_
. That way we won't need linter warts any time an interface implementor doesn't want to use a function parameter specified by the interface. - 8893c9c ensures 500 errors which occur during critical css generation initialization aren't ignored (one crashed the UI during my testing)
This is beautiful work. I particularly love the interfaces, and how they are implemented and used. Let's get this thing merged.
@@ -9,14 +9,24 @@ import { writable } from 'svelte/store'; | |||
import config from './config'; | |||
import { setModuleState } from '../api/modules'; | |||
|
|||
export type Optimizations = { |
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.
Is Optimization
a synonym for Module?
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.
Optimization is a feature, but a feature doesn't necessarily mean optimization.
This came out with a brainstorming call with @eAdnan007 where we had this idea that Boost has other "features" that aren't optimizations, and so they're hanging in the air somewhere.
I actually think this is going to need a follow-up PR to get rid of the "Module" terminology entirely, at least when it comes to describing Optimizations and Features. I think that distinction makes it a lot easier to reason about what belongs where.
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.
That seems reasonable to me, and I'm fine towards replacing Module with Feature / Optimization.
@@ -0,0 +1,11 @@ | |||
<?php | |||
|
|||
namespace Automattic\Jetpack_Boost\Contracts; |
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 really like this namespace and what it represents.
use Automattic\Jetpack_Boost\REST_API\Endpoints\Recommendations_Reset; | ||
use Automattic\Jetpack_Boost\REST_API\REST_API; | ||
|
||
class Critical_CSS implements Feature, Has_Endpoints { |
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 really, really love this contract syntax which tells you in the header some common ways you can interact with the class.
use Automattic\Jetpack_Boost\REST_API\Contracts\Endpoint; | ||
use Automattic\Jetpack_Boost\REST_API\Permissions\Current_User_Admin; | ||
|
||
class Generator_Request implements Endpoint { |
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 think it would be nice to include a comment with each endpoint which says its HTTP method, path, and purpose. e.g.:
/***
* POST critical-css/request-generate
*
* Ask the plugin to start a new Critical CSS Generation process. Returns the updated state of the Critical CSS system.
*/
Just to make it easier when browsing endpoints.
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.
👍 That makes sense
* Check whether the Jetpack Lazy Images module is enabled. | ||
*/ | ||
public static function is_jetpack_lazy_images_module_enabled() { | ||
return class_exists( 'Jetpack' ) && Jetpack::is_module_active( self::MODULE_SLUG ); |
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.
Ok I just imported my changes from my fixes branch, and there are two tests failing. It is not keeping the "lazy-images" feature in lock-step between Jetpack and Boost. This line is why -- the Modules used to be able to override the default status behavior.
I started down the road to fix this -- I created a new JetpackDeferredStatus
class which inherits from Status
and overrides its is_enabled
and update
methods to defer to Jetpack's module status.
But then how do we make some Optimization
s use Status
, and others use JetpackDeferredStatus
?
I could see a few solutions. Among them, this one felt the least messy to me:
- Change
Feature
from an interface to a base class, so that it can define default methods, - Add a
create_status_manager
method to theFeature
base class, which returns a newStatus
object, and override it for lazy images to return a newJetpackDeferredStatus
object instead. - Profit!
But that's a big enough design change that I didn't want to do it without consulting with @pyronaur first.
Alternately, we could:
- Add a
FeatureDefaults
base class which each feature inherits in addition to using theFeature
interface, so that we have somewhere to park defaults which apply to most features - but also maintain the cleanFeature
contract. - Add a
create_status_manager
method to every feature. That's kind of tiresome. - Add an empty interface like
UsesJetpackStatus
to tag theLazy_Images
class as wanting to defer its status to Jetpack, and checking for it in theOptimization
class constructor. But that feels like magic-at-a-distance which will trip us up later. - Add the deferral logic to the
Status
class, and have it know a bunch of slugs to defer to Jetpack's status. That feels like it puts the intelligence in the wrong place. - Add a special case to the
Optimization
class and have it know which Features need special status treatment. Same problem with intelligence going in the wrong place.
None of those options seem clean to me.
So @pyronaur, how would you go about this?
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 started down the road to fix this -- I created a new JetpackDeferredStatus class which inherits from Status and overrides its is_enabled and update methods to defer to Jetpack's module status.
This is certainly one way of doing things. Although, whenever possible - I think we should try to think how we can do this with composition first (instead of inhertiance)
But then how do we make some Optimizations use Status, and others use JetpackDeferredStatus?
Not entirely sure either, but I think it's worth exploring adding a status contract where both Jetpack_Status and Boost_Status both subscribe to that contract and become interchangeable. At that point, whoever is instantiating a status can decide which type of status is needed (for example, depending on whether Jetpack is active)
But there's a problem with this approach still - because both plugins can be activated without the other, so if the place of storage changes, there's no easy way to silently sync and migrate the option.
I have to look closer at the lazy images module - but I think maybe that module itself should be able to know whether it's active or not? I'm not sure, but it would be a way to neatly fix this.
Alternately, we could:
Hm. I need to do a little bit of thinking on this, but it seems that if we use any type of inheritance it's automatically going to tie down the parent class.
Thinking from a single-responsibility angle - what's the single responsibility we're missing here? Sounds like we're talking about where to store the status - either in Jetpack options or Boost options. So I think that's the hint of the direction we need to go in.
No other classes should be aware that we have a compatibility layer with Jetpack. Where to place it exactly - I'm not entirely sure. I think it needs a bit of thought as a part of #22542
Boost modules were moved to a features/ directory in #22163, but this task wasn't updated accordingly. This should fix things.
Refactoring the Critical CSS class without modifying the plugin behavior.
It's going to take some time to fully refactor Critical CSS, but this is at least a good first step.
Fixes #21725
Changes in this pull request:
Jetpack product discussion
pc9hqz-cz-p2
Does this pull request change what data or activity we track or use?
No
Testing instructions:
refactor/boost-critical-css
jetpack build plugins/boost
composer phpcs:lint
projects/plugins/boost
pnpm run env-start && pnpm run tunnel-on
andpnpm run test-e2e
)