-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Add feature flags for Phase 2 #13324
Conversation
Thanks for getting started on this! Nothing moves the conversation forward like putting up some strawman code 😀
@youknowriad and @gziolo should be able to give us some direction here once they're back from their meetup. Paging @WordPress/gutenberg-core, too.
We probably want to give this a more generic name as
My first reaction is that overloading Perhaps we could have two environment variables, e.g. Or perhaps we ought to ditch what Calypso does and go back to something like Riad first suggested, e.g.
What do we need for static analysis to work? How complex can an expression be before Webpack gives up on trying to eliminate it as dead code?
What's involved in getting |
True, hadn't thought about that. Let's see how this develops before renaming it.
Hard to say. I think there would definitely need to be no runtime logic, which would require some build time magic. It might also preclude using a function like A tricky aspect of handling this at build time is that (from my understanding) the integration with core has its own separate webpack setup:
Perhaps that leaves an option of a webpack or babel plugin as the best way to inject the correct flags at build time?
Thinking some more on it, I'm a bit unclear on how much this will help. I think it just ensures unused exports are culled from the final bundle, which isn't helpful for feature flags since exports or imports can't be conditional. The webpack docs are definitely not very clear on the exact behaviour. |
This was my first thought as well. I see no reason this needs to be specific to editor at all. I think we could come up with a convention around naming specific features to allow some subgrouping ( |
I agree with a Also, can we take a step back and figure out what the minimum (and simplest) we can do here is? Calypso offers a series of environments from least to most production-ready, and features become exposed to more environments as they mature. But strictly speaking that doesn't fall within the scope of feature flags for Gutenberg phases, and also Gutenberg has its own production line for maturing features. Here's how I see it:
We could argue that pushing updates to core WordPress is a subsequent step based on the plugin release. Wouldn't the above make things a little easier for us? It would mean that the only real branching hinges on one flag, In short, we should be clear about our goals for feature flags:
|
I don't think it's necessary (yet) but it's worth noting that
Or for super experimental features one could even gate the feature to All unnecessary food for thought. My only point is that the idea scales fairly well 🙂 |
Expanding on #13324 (comment), I thought I'd lay out a proposal for how the release process using
if ( process.env.GUTENBERG_PHASE >= 2) {
// The RSS block is under development, so only load it in the Gutenberg plugin and not WP Core
registerBlockType( 'core/rss', rssSettings );
}
Considerations:
|
4cb0a39
to
53eb3a1
Compare
Based on the discussion, I've reworked this to use webpack's define plugin to inject a Dead code elimination seems to work. I did a quick test by setting the phase to
It looked like none of the code in the function body was bundled (I tested by searching for a class name string, which wasn't present at all in the bundled code). 🎉 Using the define plugin does add a restriction that other consumers of packages would also have to use it (or a similar solution). For example, core's webpack implementation could hard-code this value to Thoughts on this approach? |
Ouch, ignore my closing this issue. My keyboard magic with Vimium went a little too far. 😅 |
Thanks, this looks a lot nicer.
I'm not sure we would have a good way to enforce the presence of the env var for third-party consumers: since they can use any config for the bundling, we can't require anything in webpack, and since they can build and consume packages independently, we can't very reliably throw runtime errors if the var is missing. It seems safer to just assume Valid: if ( process.env.GUTENBERG_PHASE === 2 ) { if (
process.env.GUTENBERG_PHASE === 2 ||
process.env.GUTENBERG_PHASE === 3
) { Invalid: if ( process.env.GUTENBERG_PHASE === 1 ) { if ( process.env.GUTENBERG_PHASE ) { if ( process.env.GUTENBERG_PHASE > 1 ) { |
@talldan and I were discussing this yesterday and we concluded that it's arguably fine that we publish code on npm that requires The alternative is that we modify
That's an interesting idea. I think I'm into it. At the very least it makes sense to forbid One consideration: What will it look like when we "ship" Phase 2 to WordPress Core? Would we simply remove all of the |
Assuming Some off-the-bat ideas:
I'm leaning towards (3). |
With the define plugin we can explicitly replace only
☝️ that will only replace instances of
This is a bit crazy, but it's also possible to do something like this with the define plugin:
|
Hey @mcsf thanks for the comments. Linting is a good idea, it would also mean there's less chance of dead code elimination being bypassed if the usage is enforced. Is there a preference for I did notice we already have a line of code that references
|
I didn't think too hard about it, but I did go along with our prior art.
I also lean towards idea number 3, with whatever syntax fits best ( |
eef83e5
to
b7d2a5e
Compare
@gziolo Thanks for the reviews, really appreciate the help 😄 The merge conflict should now be fixed. 👍 The only thing I'd say is that it'd be good to do some testing with how this works downstream (checking that everything works as expected when feature flagged code is integrated in core, or when it's consumed by plugins). That could always come after this is merged. |
Let's merge and test once published to npm. In the worst case scenario, we will publish patch later but I personally feel pretty confident about the proposed approach. 🚢 |
I shared some details about this PR in #core-js channel on WordPress Slack (link requires registration at https://make.wordpress.org/chat/): |
* Add new package for editor configuration, initially containing just feature flags Rework build commands to use correct NODE_ENV for feature flags * Revert "Rework build commands to use correct NODE_ENV for feature flags" This reverts commit 4cb0a39. Revert "Add new package for editor configuration, initially containing just feature flags" This reverts commit 0c21fc2. * Switch to using webpack define plugin to inject a global GUTENBERG_PHASE variable * Iterate: use window.GUTENBERG_PHASE to avoid thrown errors from an undefined global * Add custom eslint rule for usage of GUTENBERG_PHASE * Disable new eslint rule when used in webpack config * Add readme * Include phase 2 features in e2e tests * Allow use of GUTENBERG_PHASE in a ternary and update documentation. * Add links to docs * Minor docs changes * Switch from window.GUTENBERG_PHASE to process.env.GUTENBERG_PHASE * Update docs for feature flags. Move `Basic Use` section higher up, and simplify a sentence * Ensure GUTENBERG_PHASE environment variable is available for webpack * Ensure GUTENBERG_PHASE is a number * Ensure GUTENBERG_PHASE is set in unit tests * Use <rootDir> in jest config * switch to using package.json config to define the value of GUTENBERG_PHASE * Sort custom lint rules alphabetically * Add comment about GUTENBERG_PHASE * Update jest config for GUTENBERG_PHASE * Add webpack as dependency to main package.json
* Add new package for editor configuration, initially containing just feature flags Rework build commands to use correct NODE_ENV for feature flags * Revert "Rework build commands to use correct NODE_ENV for feature flags" This reverts commit 4cb0a39. Revert "Add new package for editor configuration, initially containing just feature flags" This reverts commit 0c21fc2. * Switch to using webpack define plugin to inject a global GUTENBERG_PHASE variable * Iterate: use window.GUTENBERG_PHASE to avoid thrown errors from an undefined global * Add custom eslint rule for usage of GUTENBERG_PHASE * Disable new eslint rule when used in webpack config * Add readme * Include phase 2 features in e2e tests * Allow use of GUTENBERG_PHASE in a ternary and update documentation. * Add links to docs * Minor docs changes * Switch from window.GUTENBERG_PHASE to process.env.GUTENBERG_PHASE * Update docs for feature flags. Move `Basic Use` section higher up, and simplify a sentence * Ensure GUTENBERG_PHASE environment variable is available for webpack * Ensure GUTENBERG_PHASE is a number * Ensure GUTENBERG_PHASE is set in unit tests * Use <rootDir> in jest config * switch to using package.json config to define the value of GUTENBERG_PHASE * Sort custom lint rules alphabetically * Add comment about GUTENBERG_PHASE * Update jest config for GUTENBERG_PHASE * Add webpack as dependency to main package.json
new DefinePlugin( { | ||
// Inject the `GUTENBERG_PHASE` global, used for feature flagging. | ||
// eslint-disable-next-line @wordpress/gutenberg-phase | ||
'process.env.GUTENBERG_PHASE': JSON.stringify( parseInt( process.env.npm_package_config_GUTENBERG_PHASE, 10 ) || 1 ), |
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.
JSON.stringify
is often used with DefinePlugin
because the substitution is verbatim, and thus for something like a string, you'd want to be certain you're replacing it with a string, not an identifier token (the difference between foo( bar );
and foo( "bar" )
). For a number, though, the verbatim substitution is fine, i.e. there's no difference with or without JSON.stringify
(it's foo( 10 )
with or without JSON.stringify
).
@@ -105,6 +106,11 @@ const config = { | |||
], | |||
}, | |||
plugins: [ | |||
new DefinePlugin( { | |||
// Inject the `GUTENBERG_PHASE` global, used for feature flagging. | |||
// eslint-disable-next-line @wordpress/gutenberg-phase |
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.
There's no error if this line is removed?
Description
An attempt at introducing feature flags for phase 2 (see #11016).
The proposal is to use webpack's define plugin (https://webpack.js.org/plugins/define-plugin/) to replace instances of
process.env.GUTENBERG_PHASE
with an integer denoting the phase (1
or2
) during the build.Within the codebase, the
GUTENBERG_PHASE
variable would be used to conditionally avoid executing code:When building for core, the majority code would be stripped out by webpack's minfication/dead code elimination process.
This PR also introduces docs and linting rules to ensure that
process.env.GUTENBERG_PHASE
is used in the correct way.