Skip to content

Commit

Permalink
Rename GUTENBERG_PHASE to IS_GUTENBERG_PLUGIN (WordPress#38202)
Browse files Browse the repository at this point in the history
* Change everything apart from linting rule

* Update linting rule

* Rename files

* Update changelog

* Fix linting rule

* Deprecate eslint gutenberg plugin rule

* Update changelog entry

* Update changelogs for packages that used GUTENBERG_PHASE
  • Loading branch information
talldan authored Feb 1, 2022
1 parent 086a6b2 commit a719ea0
Show file tree
Hide file tree
Showing 33 changed files with 335 additions and 241 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ module.exports = {
rules: {
'jest/expect-expect': 'off',
'@wordpress/dependency-group': 'error',
'@wordpress/gutenberg-phase': 'error',
'@wordpress/is-gutenberg-plugin': 'error',
'@wordpress/react-no-unsafe-timeout': 'error',
'@wordpress/i18n-text-domain': [
'error',
Expand Down
80 changes: 31 additions & 49 deletions docs/how-to-guides/feature-flags.md
Original file line number Diff line number Diff line change
@@ -1,69 +1,69 @@
# Feature Flags

With phase 2 of the Gutenberg project there's a need for improved control over how code changes are released. Newer features developed for phase 2 and beyond should only be released to the Gutenberg plugin, while improvements and bug fixes should still continue to make their way into core releases.
Often in the Gutenberg project, there's a need to control whether the code we write is shipped to WordPress core, or whether certain more experimental features are only active in the plugin.

The technique for handling this is known as a 'feature flag'.
Often this is handled using a 'feature flag'.

## Introducing `process.env.GUTENBERG_PHASE`
## Introducing `process.env.IS_GUTENBERG_PLUGIN`

The `process.env.GUTENBERG_PHASE` is an environment variable containing a number that represents the phase. When the codebase is built for the plugin, this variable will be set to `2`. When building for core, it will be set to `1`.
The `process.env.IS_GUTENBERG_PLUGIN` is an environment variable that represents whether code is currently executing within the plugin. When the codebase is built for the plugin, this variable will be set to `true`. When building for core, it will be set to `false` or `undefined`.

## Basic Use

A phase 2 function or constant should be exported using the following ternary syntax:
A plugin only function or constant should be exported using the following ternary syntax:

```js
function myPhaseTwoFeature() {
function myPluginOnlyFeature() {
// implementation
}

export const phaseTwoFeature =
process.env.GUTENBERG_PHASE === 2 ? myPhaseTwoFeature : undefined;
export const pluginOnlyFeature =
process.env.IS_GUTENBERG_PLUGIN ? myPluginOnlyFeature : undefined;
```

In phase 1 environments the `phaseTwoFeature` export will be `undefined`.
In non-plugin environments the `phaseTwoFeature` export will be `undefined`.

If you're attempting to import and call a phase 2 feature, be sure to wrap the call to the function in an if statement to avoid an error:
If you're attempting to import and call a plugin only feature, be sure to wrap the call to the function in an if statement to avoid an error:

```js
import { phaseTwoFeature } from '@wordpress/foo';
import { pluginOnlyFeature } from '@wordpress/foo';

if ( process.env.GUTENBERG_PHASE === 2 ) {
phaseTwoFeature();
if ( process.env.IS_GUTENBERG_PLUGIN ) {
pluginOnlyFeature();
}
```

### How it works

During the webpack build, any instances of `process.env.GUTENBERG_PHASE` will be replaced using webpack's define plugin (https://webpack.js.org/plugins/define-plugin/).
During the webpack build, any instances of `process.env.IS_GUTENBERG_PLUGIN` will be replaced using webpack's define plugin (https://webpack.js.org/plugins/define-plugin/).

If you write the following code:

```js
if ( process.env.GUTENBERG_PHASE === 2 ) {
phaseTwoFeature();
if ( process.env.IS_GUTENBERG_PLUGIN ) {
pluginOnlyFeature();
}
```

When building the codebase for the plugin the variable will be replaced with the number literal `2`:
When building the codebase for the plugin the variable will be replaced with the boolean `true`:

```js
if ( 2 === 2 ) {
phaseTwoFeature();
if ( true ) {
pluginOnlyFeature();
}
```

Any code within the body of the if statement will be executed within the gutenberg plugin since `2 === 2` evaluates to `true`.
Any code within the body of the if statement will be executed because of this truthyness.

For core, the `process.env.GUTENBERG_PHASE` variable is replaced with `1`, so the built code will look like:
For core, the `process.env.IS_GUTENBERG_PLUGIN` variable is replaced with `undefined`, so the built code will look like:

```js
if ( 1 === 2 ) {
phaseTwoFeature();
if ( undefined ) {
pluginOnlyFeature();
}
```

`1 === 2` evaluates to false so the phase 2 feature will not be executed within core.
`undefined` evaluates to `false` so the plugin only feature will not be executed within core.

### Dead Code Elimination

Expand All @@ -72,47 +72,29 @@ When building code for production, webpack 'minifies' code (https://en.wikipedia
When the following code is encountered, webpack determines that the surrounding `if`statement is unnecessary:

```js
if ( 2 === 2 ) {
phaseTwoFeature();
if ( true ) {
pluginOnlyFeature();
}
```

The condition will always evaluates to `true`, so can be removed leaving just the code in the body:

```js
phaseTwoFeature();
pluginOnlyFeature();
```

Similarly when building for core, the condition in the following `if` statement always resolves to false:

```js
if ( 1 === 2 ) {
phaseTwoFeature();
if ( undefined ) {
pluginOnlyFeature();
}
```

The minification process will remove the entire `if` statement including the body, ensuring code destined for phase 2 is not included in the built JavaScript intended for core.
The minification process will remove the entire `if` statement including the body, ensuring plugin only code is not included in the built JavaScript intended for core.

## FAQ

#### Why should I only use `===` or `!==` when comparing `process.env.GUTENBERG_PHASE` and not `>`, `>=`, `<` or `<=`?

This is a restriction due to the behaviour of the greater than or less than operators in JavaScript when `process.env.GUTENBERG_PHASE` is undefined, as might be the case for third party users of WordPress npm packages. Both `process.env.GUTENBERG_PHASE < 2` and `process.env.GUTENBERG_PHASE > 1` resolve to false. When writing `if ( process.env.GUTENBERG_PHASE > 1 )`, the intention might be to avoid executing the phase 2 code in the following `if` statement's body. That's fine since it will evaluate to false.

However, the following code doesn't quite have the intended behaviour:

```
function myPhaseTwoFeature() {
if ( process.env.GUTENBERG_PHASE < 2 ) {
return;
}
// implementation of phase 2 feature
}
```

Here an early return is used to avoid execution of a phase 2 feature, but because the `if` condition resolves to false, the early return is bypassed and the phase 2 feature is incorrectly triggered.

#### Why shouldn't I assign the result of an expression involving `GUTENBERG_PHASE` to a variable, e.g. `const isMyFeatureActive = process.env.GUTENBERG_PHASE === 2`?
#### Why shouldn't I assign the result of an expression involving `IS_GUTENBERG_PLUGIN` to a variable, e.g. `const isMyFeatureActive = process.env.IS_GUTENBERG_PLUGIN === 2`?

The aim here is to avoid introducing any complexity that could result in webpack's minifier not being able to eliminate dead code. See the [Dead Code Elimination](#dead-code-elimination) section for further details.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"npm": ">=6.9.0 <7"
},
"config": {
"GUTENBERG_PHASE": 2
"IS_GUTENBERG_PLUGIN": true
},
"dependencies": {
"@wordpress/a11y": "file:packages/a11y",
Expand Down
4 changes: 4 additions & 0 deletions packages/block-library/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Breaking Change

- The `GUTENBERG_PHASE` environment variable has been renamed to `IS_GUTENBERG_PLUGIN` and is now a boolean ([#38202](https://github.com/WordPress/gutenberg/pull/38202)).

## 6.1.0 (2022-01-27)

- Code quality: Add block schema to each core block ([#35900](https://github.com/WordPress/gutenberg/pull/35900)).
Expand Down
2 changes: 1 addition & 1 deletion packages/block-library/src/gallery/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const pickRelevantMediaFiles = ( image, sizeSlug = 'large' ) => {
export function isGalleryV2Enabled() {
// Only run the Gallery version compat check if the plugin is running, otherwise
// assume we are in 5.9 core and enable by default.
if ( process.env.GUTENBERG_PHASE === 2 ) {
if ( process.env.IS_GUTENBERG_PLUGIN ) {
// We want to fail early here, at least during beta testing phase, to ensure
// there aren't instances where undefined values cause false negatives.
if (
Expand Down
62 changes: 31 additions & 31 deletions packages/block-library/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,35 +242,35 @@ export const registerCoreBlocks = (
* __experimentalRegisterExperimentalCoreBlocks( settings );
* ```
*/
export const __experimentalRegisterExperimentalCoreBlocks =
process.env.GUTENBERG_PHASE === 2
? ( { enableFSEBlocks } = {} ) => {
[
// Experimental blocks.
homeLink,
export const __experimentalRegisterExperimentalCoreBlocks = process.env
.IS_GUTENBERG_PLUGIN
? ( { enableFSEBlocks } = {} ) => {
[
// Experimental blocks.
homeLink,

// Full Site Editing blocks.
...( enableFSEBlocks
? [
commentAuthorAvatar,
commentAuthorName,
commentContent,
commentDate,
commentEditLink,
commentReplyLink,
commentTemplate,
commentsQueryLoop,
commentsPagination,
commentsPaginationNext,
commentsPaginationNumbers,
commentsPaginationPrevious,
navigationArea,
postComment,
postCommentsCount,
postCommentsForm,
postCommentsLink,
]
: [] ),
].forEach( registerBlock );
}
: undefined;
// Full Site Editing blocks.
...( enableFSEBlocks
? [
commentAuthorAvatar,
commentAuthorName,
commentContent,
commentDate,
commentEditLink,
commentReplyLink,
commentTemplate,
commentsQueryLoop,
commentsPagination,
commentsPaginationNext,
commentsPaginationNumbers,
commentsPaginationPrevious,
navigationArea,
postComment,
postCommentsCount,
postCommentsForm,
postCommentsLink,
]
: [] ),
].forEach( registerBlock );
}
: undefined;
2 changes: 1 addition & 1 deletion packages/block-library/src/navigation/edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ function Navigation( {
setAreaMenu = noop;
// Navigation areas are deprecated and on their way out. Let's not perform
// the request unless we're in an environment where the endpoint exists.
if ( process.env.GUTENBERG_PHASE === 2 ) {
if ( process.env.IS_GUTENBERG_PLUGIN ) {
// eslint-disable-next-line react-hooks/rules-of-hooks
[ areaMenu, setAreaMenu ] = useEntityProp(
'root',
Expand Down
4 changes: 4 additions & 0 deletions packages/customize-widgets/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Breaking Change

- The `GUTENBERG_PHASE` environment variable has been renamed to `IS_GUTENBERG_PLUGIN` and is now a boolean ([#38202](https://github.com/WordPress/gutenberg/pull/38202)).

## 2.1.0 (2022-01-27)

## 2.0.0 (2021-07-29)
Expand Down
2 changes: 1 addition & 1 deletion packages/customize-widgets/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function initialize( editorName, blockEditorSettings ) {
} );
registerCoreBlocks( coreBlocks );
registerLegacyWidgetBlock();
if ( process.env.GUTENBERG_PHASE === 2 ) {
if ( process.env.IS_GUTENBERG_PLUGIN ) {
__experimentalRegisterExperimentalCoreBlocks( {
enableFSEBlocks: ENABLE_EXPERIMENTAL_FSE_BLOCKS,
} );
Expand Down
9 changes: 0 additions & 9 deletions packages/e2e-tests/config/gutenberg-phase.js

This file was deleted.

6 changes: 6 additions & 0 deletions packages/e2e-tests/config/is-gutenberg-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
global.process.env = {
...global.process.env,
// Inject the `IS_GUTENBERG_PLUGIN` global, used for feature flagging.
// eslint-disable-next-line @wordpress/is-gutenberg-plugin
IS_GUTENBERG_PLUGIN: process.env.npm_package_config_IS_GUTENBERG_PLUGIN,
};
2 changes: 1 addition & 1 deletion packages/e2e-tests/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const baseConfig = require( '@wordpress/scripts/config/jest-e2e.config' );
module.exports = {
...baseConfig,
testMatch: [ '<rootDir>/specs/**/*.test.js' ],
setupFiles: [ '<rootDir>/config/gutenberg-phase.js' ],
setupFiles: [ '<rootDir>/config/is-gutenberg-plugin.js' ],
setupFilesAfterEnv: [
'<rootDir>/config/setup-test-framework.js',
'@wordpress/jest-console',
Expand Down
2 changes: 1 addition & 1 deletion packages/e2e-tests/jest.performance.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
...require( '@wordpress/scripts/config/jest-e2e.config' ),
testMatch: [ '**/performance/*.test.js' ],
setupFiles: [ '<rootDir>/config/gutenberg-phase.js' ],
setupFiles: [ '<rootDir>/config/is-gutenberg-plugin.js' ],
setupFilesAfterEnv: [
'<rootDir>/config/setup-performance-test.js',
'@wordpress/jest-console',
Expand Down
2 changes: 1 addition & 1 deletion packages/edit-navigation/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ function setUpEditor( settings ) {

dispatch( blocksStore ).__experimentalReapplyBlockTypeFilters();
registerCoreBlocks();
if ( process.env.GUTENBERG_PHASE === 2 ) {
if ( process.env.IS_GUTENBERG_PLUGIN ) {
__experimentalRegisterExperimentalCoreBlocks();
}
}
Expand Down
4 changes: 4 additions & 0 deletions packages/edit-post/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Breaking Change

- The `GUTENBERG_PHASE` environment variable has been renamed to `IS_GUTENBERG_PLUGIN` and is now a boolean ([#38202](https://github.com/WordPress/gutenberg/pull/38202)).

## 5.1.0 (2022-01-27)

## 5.0.0 (2021-07-29)
Expand Down
2 changes: 1 addition & 1 deletion packages/edit-post/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export function initializeEditor(

dispatch( blocksStore ).__experimentalReapplyBlockTypeFilters();
registerCoreBlocks();
if ( process.env.GUTENBERG_PHASE === 2 ) {
if ( process.env.IS_GUTENBERG_PLUGIN ) {
__experimentalRegisterExperimentalCoreBlocks( {
enableFSEBlocks: settings.__unstableEnableFullSiteEditingBlocks,
} );
Expand Down
4 changes: 4 additions & 0 deletions packages/edit-site/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Breaking Change

- The `GUTENBERG_PHASE` environment variable has been renamed to `IS_GUTENBERG_PLUGIN` and is now a boolean ([#38202](https://github.com/WordPress/gutenberg/pull/38202)).

## 3.1.0 (2022-01-27)

## 3.0.0 (2021-07-29)
Expand Down
2 changes: 1 addition & 1 deletion packages/edit-site/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function initializeEditor( id, settings ) {

dispatch( blocksStore ).__experimentalReapplyBlockTypeFilters();
registerCoreBlocks();
if ( process.env.GUTENBERG_PHASE === 2 ) {
if ( process.env.IS_GUTENBERG_PLUGIN ) {
__experimentalRegisterExperimentalCoreBlocks( {
enableFSEBlocks: true,
} );
Expand Down
6 changes: 5 additions & 1 deletion packages/edit-widgets/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@

## Unreleased

### Breaking Change

- The `GUTENBERG_PHASE` environment variable has been renamed to `IS_GUTENBERG_PLUGIN` and is now a boolean ([#38202](https://github.com/WordPress/gutenberg/pull/38202)).

## 3.2.0 (2022-01-27)

## 3.1.0 (2021-11-07)

### Enhancement

- Enable persistent List View in the widget editor [#35706](https://github.com/WordPress/gutenberg/pull/35706).
- Enable persistent List View in the widget editor [#35706](https://github.com/WordPress/gutenberg/pull/35706).

## 3.0.0 (2021-07-29)

Expand Down
2 changes: 1 addition & 1 deletion packages/edit-widgets/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export function initialize( id, settings ) {
dispatch( blocksStore ).__experimentalReapplyBlockTypeFilters();
registerCoreBlocks( coreBlocks );
registerLegacyWidgetBlock();
if ( process.env.GUTENBERG_PHASE === 2 ) {
if ( process.env.IS_GUTENBERG_PLUGIN ) {
__experimentalRegisterExperimentalCoreBlocks( {
enableFSEBlocks: ENABLE_EXPERIMENTAL_FSE_BLOCKS,
} );
Expand Down
Loading

0 comments on commit a719ea0

Please sign in to comment.