From fb60a6cb35e237d14a3602e2d56fbb9a4cf9ee4b Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 15 Jan 2019 15:53:19 +0800 Subject: [PATCH 01/22] Add new package for editor configuration, initially containing just feature flags Rework build commands to use correct NODE_ENV for feature flags --- docs/manifest.json | 6 ++ lib/packages-dependencies.php | 1 + package.json | 9 ++- packages/editor-configuration/.npmrc | 1 + packages/editor-configuration/CHANGELOG.md | 5 ++ packages/editor-configuration/README.md | 73 +++++++++++++++++++ packages/editor-configuration/package.json | 27 +++++++ .../src/config/development.js | 3 + .../editor-configuration/src/config/plugin.js | 3 + .../src/config/production.js | 3 + packages/editor-configuration/src/index.js | 20 +++++ .../editor-configuration/src/utils/index.js | 15 ++++ .../src/utils/test/index.js | 21 ++++++ webpack.config.js | 6 +- 14 files changed, 187 insertions(+), 6 deletions(-) create mode 100644 packages/editor-configuration/.npmrc create mode 100644 packages/editor-configuration/CHANGELOG.md create mode 100644 packages/editor-configuration/README.md create mode 100644 packages/editor-configuration/package.json create mode 100644 packages/editor-configuration/src/config/development.js create mode 100644 packages/editor-configuration/src/config/plugin.js create mode 100644 packages/editor-configuration/src/config/production.js create mode 100644 packages/editor-configuration/src/index.js create mode 100644 packages/editor-configuration/src/utils/index.js create mode 100644 packages/editor-configuration/src/utils/test/index.js diff --git a/docs/manifest.json b/docs/manifest.json index 675c7034e52a1..a1759f578302c 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -653,6 +653,12 @@ "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/edit-post/README.md", "parent": "packages" }, + { + "title": "@wordpress/editor-configuration", + "slug": "packages-editor-configuration", + "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/editor-configuration/README.md", + "parent": "packages" + }, { "title": "@wordpress/editor", "slug": "packages-editor", diff --git a/lib/packages-dependencies.php b/lib/packages-dependencies.php index fbf5eda814717..5acc6dff476d8 100644 --- a/lib/packages-dependencies.php +++ b/lib/packages-dependencies.php @@ -157,6 +157,7 @@ 'wp-viewport', 'wp-wordcount', ), + 'wp-editor-configuration' => array(), 'wp-element' => array( 'lodash', 'react', diff --git a/package.json b/package.json index 07cd37a6a9364..4e81085718581 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@wordpress/dom-ready": "file:packages/dom-ready", "@wordpress/edit-post": "file:packages/edit-post", "@wordpress/editor": "file:packages/editor", + "@wordpress/editor-configuration": "file:packages/editor-configuration", "@wordpress/element": "file:packages/element", "@wordpress/escape-html": "file:packages/escape-html", "@wordpress/format-library": "file:packages/format-library", @@ -169,13 +170,13 @@ "lint-pkg-json": "wp-scripts lint-pkg-json ./packages", "lint-css": "wp-scripts lint-style '**/*.scss'", "lint-css:fix": "npm run lint-css -- --fix", - "package-plugin": "./bin/build-plugin-zip.sh", + "package-plugin": "cross-env NODE_ENV=plugin ./bin/build-plugin-zip.sh", "postinstall": "npm run check-licenses && npm run build:packages", "pot-to-php": "./bin/pot-to-php.js", "precommit": "lint-staged", - "publish:check": "npm run build:packages && lerna updated", - "publish:dev": "npm run build:packages && lerna publish --npm-tag next", - "publish:prod": "npm run build:packages && lerna publish", + "publish:check": "cross-env NODE_ENV=production npm run build:packages && lerna updated", + "publish:dev": "cross-env NODE_ENV=production npm run build:packages && lerna publish --npm-tag next", + "publish:prod": "cross-env NODE_ENV=production npm run build:packages && lerna publish", "test": "npm run lint && npm run test-unit", "pretest-e2e": "./bin/reset-e2e-tests.sh", "test-e2e": "wp-scripts test-e2e --config packages/e2e-tests/jest.config.js", diff --git a/packages/editor-configuration/.npmrc b/packages/editor-configuration/.npmrc new file mode 100644 index 0000000000000..43c97e719a5a8 --- /dev/null +++ b/packages/editor-configuration/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/editor-configuration/CHANGELOG.md b/packages/editor-configuration/CHANGELOG.md new file mode 100644 index 0000000000000..c9970e2e618b5 --- /dev/null +++ b/packages/editor-configuration/CHANGELOG.md @@ -0,0 +1,5 @@ +## 1.0.0 (Unreleased) + +### New Features + +- Initial release. diff --git a/packages/editor-configuration/README.md b/packages/editor-configuration/README.md new file mode 100644 index 0000000000000..797981dc712bc --- /dev/null +++ b/packages/editor-configuration/README.md @@ -0,0 +1,73 @@ +# Editor configuration + +Editor configuration is a package that stores configuration values for Gutenberg. + +The package contains three different configuration files per environment: +- `config/development.js` - configuration values for local development environments and testing. +- `config/plugin.js` - configuration values for the Gutenberg plugin. +- `config/production.js` - configuration values for the block editor in Wordpress Core. + +## Installation + +Install the module + +```bash +npm install @wordpress/editor-configuration --save +``` + +_This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for ES2015+ such as lower versions of IE then using [core-js](https://github.com/zloirock/core-js) or [@babel/polyfill](https://babeljs.io/docs/en/next/babel-polyfill) will add support for these methods. Learn more about it in [Babel docs](https://babeljs.io/docs/en/next/caveats)._ + +## Usage + +### Feature flags + +Feature flags (also known as 'feature toggles') are boolean values that indicate whether a specific feature is active. + +By defining a per-environment flag for a feature, fine-grain control can be achieved over when a feature is released. Generally a feature will initally only be enabled in `development` environments. Once it reaches a certain maturity, it can be enabled in the plugin. Finally, once a feature is ready for release, it can be enabled in production. + +An advantage of feature flags is that a pull request implementing a new feature no longer needs to be held back from being merged into `master`. As long as the code passes review, a feature can be merged but disabled until it's ready to be released. + +### Adding a feature flag + +For each configuration file, add a new item in the `features` object. Add new flags in alphabetical order to make it easier to browse them: + +development.js +```javascript +export default { + features: { + // ... + 'editor/my-first-feature': true, + // ... + } +} +``` + +plugin.js / production.js +```javascript +export default { + features: { + // ... + 'editor/my-first-feature': false, + // ... + } +} +``` + +In the file(s) the feature is implemented, import the `getFeatureFlag` function from this package, and use it to determine whether the feature is active: +```javascript +import { getFeatureFlag } from '@wordpress/editor-configuration'; + +const isMyFirstFeatureActive = getFeatureFlag( 'editor/my-first-feature' ); +``` + +Finally, avoid executing code that implements the feature at the earliest possible point: +```javascript +function myFirstFeature() { + if ( ! isMyFirstFeatureActive ) { + return; + } + + // ... implementation of feature +} + +``` diff --git a/packages/editor-configuration/package.json b/packages/editor-configuration/package.json new file mode 100644 index 0000000000000..bed1ff4fd6005 --- /dev/null +++ b/packages/editor-configuration/package.json @@ -0,0 +1,27 @@ +{ + "name": "@wordpress/editor-configuration", + "version": "1.0.0", + "description": "Configuration for the WordPress block editor.", + "author": "The WordPress Contributors", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress", + "config", + "configuration" + ], + "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/editor-configuration/README.md", + "repository": { + "type": "git", + "url": "https://github.com/WordPress/gutenberg.git" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/issues" + }, + "main": "build/index.js", + "module": "build-module/index.js", + "react-native": "src/index", + "dependencies": {}, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/editor-configuration/src/config/development.js b/packages/editor-configuration/src/config/development.js new file mode 100644 index 0000000000000..6ed10e3b2a47c --- /dev/null +++ b/packages/editor-configuration/src/config/development.js @@ -0,0 +1,3 @@ +export default { + features: {}, +}; diff --git a/packages/editor-configuration/src/config/plugin.js b/packages/editor-configuration/src/config/plugin.js new file mode 100644 index 0000000000000..6ed10e3b2a47c --- /dev/null +++ b/packages/editor-configuration/src/config/plugin.js @@ -0,0 +1,3 @@ +export default { + features: {}, +}; diff --git a/packages/editor-configuration/src/config/production.js b/packages/editor-configuration/src/config/production.js new file mode 100644 index 0000000000000..6ed10e3b2a47c --- /dev/null +++ b/packages/editor-configuration/src/config/production.js @@ -0,0 +1,3 @@ +export default { + features: {}, +}; diff --git a/packages/editor-configuration/src/index.js b/packages/editor-configuration/src/index.js new file mode 100644 index 0000000000000..1c27b8750ecba --- /dev/null +++ b/packages/editor-configuration/src/index.js @@ -0,0 +1,20 @@ +/** + * Internal dependencies + */ +import development from './config/development'; +import plugin from './config/plugin'; +import production from './config/production'; + +import { + getFeatureFlag as unboundGetFeatureFlag, +} from './utils'; + +const configMap = { + development, + plugin, + production, +}; + +const activeConfig = configMap[ process.env.NODE_ENV ] || development; + +export const getFeatureFlag = unboundGetFeatureFlag.bind( null, activeConfig ); diff --git a/packages/editor-configuration/src/utils/index.js b/packages/editor-configuration/src/utils/index.js new file mode 100644 index 0000000000000..cb5e5ddaa421f --- /dev/null +++ b/packages/editor-configuration/src/utils/index.js @@ -0,0 +1,15 @@ + +/** + * Get the value of a feature flag by its name. + * + * Note, when this function is exported from `@wordpress/editor-configurations, + * it is pre-bound and only requires the `featureName` argument. + * + * @param {Object} config The configuration object. + * @param {string} featureName The name of the feature. + * + * @return {boolean} Whether the feature is enabled (`true`) or disabled (`false`). + */ +export function getFeatureFlag( config, featureName ) { + return !! config.features[ featureName ]; +} diff --git a/packages/editor-configuration/src/utils/test/index.js b/packages/editor-configuration/src/utils/test/index.js new file mode 100644 index 0000000000000..6066444f6cee9 --- /dev/null +++ b/packages/editor-configuration/src/utils/test/index.js @@ -0,0 +1,21 @@ +import { getFeatureFlag } from '../'; + +describe( 'getFeatureFlag', () => { + it( 'returns false for an unknown feature flag', () => { + const config = { + features: {}, + }; + expect( getFeatureFlag( config, 'foo' ) ).toBe( false ); + } ); + + it( 'returns the truthiness of the value set in the config for a known feature flag', () => { + const config = { + features: { + foo: true, + bar: false, + }, + }; + expect( getFeatureFlag( config, 'foo' ) ).toBe( true ); + expect( getFeatureFlag( config, 'bar' ) ).toBe( false ); + } ); +} ); diff --git a/webpack.config.js b/webpack.config.js index 7d9877d7c15e3..166c2c14c244b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -60,8 +60,10 @@ gutenbergPackages.forEach( ( name ) => { }; } ); +const isDevelopment = process.env.NODE_ENV === 'development'; +const isPlugin = process.env.NODE_ENV === 'plugin'; const isProduction = process.env.NODE_ENV === 'production'; -const mode = isProduction ? 'production' : 'development'; +const mode = isProduction || isPlugin ? 'production' : 'development'; const config = { mode, @@ -167,7 +169,7 @@ const config = { process.env.GUTENBERG_BUNDLE_ANALYZER && new BundleAnalyzerPlugin(), // GUTENBERG_LIVE_RELOAD_PORT global variable changes port on which live reload works // when running watch mode. - ! isProduction && new LiveReloadPlugin( { port: process.env.GUTENBERG_LIVE_RELOAD_PORT || 35729 } ), + isDevelopment && new LiveReloadPlugin( { port: process.env.GUTENBERG_LIVE_RELOAD_PORT || 35729 } ), ].filter( Boolean ), stats: { children: false, From 32154065009ee2c21f198f0533664af37a5bd408 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 24 Jan 2019 15:42:39 +0800 Subject: [PATCH 02/22] Revert "Rework build commands to use correct NODE_ENV for feature flags" This reverts commit 4cb0a39282d57abb8cd5c398ae20b9466d67b056. Revert "Add new package for editor configuration, initially containing just feature flags" This reverts commit 0c21fc215b4e6f0d4b415aabb9f8297635b7d148. --- docs/manifest.json | 6 -- lib/packages-dependencies.php | 1 - package.json | 9 +-- packages/editor-configuration/.npmrc | 1 - packages/editor-configuration/CHANGELOG.md | 5 -- packages/editor-configuration/README.md | 73 ------------------- packages/editor-configuration/package.json | 27 ------- .../src/config/development.js | 3 - .../editor-configuration/src/config/plugin.js | 3 - .../src/config/production.js | 3 - packages/editor-configuration/src/index.js | 20 ----- .../editor-configuration/src/utils/index.js | 15 ---- .../src/utils/test/index.js | 21 ------ webpack.config.js | 6 +- 14 files changed, 6 insertions(+), 187 deletions(-) delete mode 100644 packages/editor-configuration/.npmrc delete mode 100644 packages/editor-configuration/CHANGELOG.md delete mode 100644 packages/editor-configuration/README.md delete mode 100644 packages/editor-configuration/package.json delete mode 100644 packages/editor-configuration/src/config/development.js delete mode 100644 packages/editor-configuration/src/config/plugin.js delete mode 100644 packages/editor-configuration/src/config/production.js delete mode 100644 packages/editor-configuration/src/index.js delete mode 100644 packages/editor-configuration/src/utils/index.js delete mode 100644 packages/editor-configuration/src/utils/test/index.js diff --git a/docs/manifest.json b/docs/manifest.json index a1759f578302c..675c7034e52a1 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -653,12 +653,6 @@ "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/edit-post/README.md", "parent": "packages" }, - { - "title": "@wordpress/editor-configuration", - "slug": "packages-editor-configuration", - "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/editor-configuration/README.md", - "parent": "packages" - }, { "title": "@wordpress/editor", "slug": "packages-editor", diff --git a/lib/packages-dependencies.php b/lib/packages-dependencies.php index 5acc6dff476d8..fbf5eda814717 100644 --- a/lib/packages-dependencies.php +++ b/lib/packages-dependencies.php @@ -157,7 +157,6 @@ 'wp-viewport', 'wp-wordcount', ), - 'wp-editor-configuration' => array(), 'wp-element' => array( 'lodash', 'react', diff --git a/package.json b/package.json index 4e81085718581..07cd37a6a9364 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "@wordpress/dom-ready": "file:packages/dom-ready", "@wordpress/edit-post": "file:packages/edit-post", "@wordpress/editor": "file:packages/editor", - "@wordpress/editor-configuration": "file:packages/editor-configuration", "@wordpress/element": "file:packages/element", "@wordpress/escape-html": "file:packages/escape-html", "@wordpress/format-library": "file:packages/format-library", @@ -170,13 +169,13 @@ "lint-pkg-json": "wp-scripts lint-pkg-json ./packages", "lint-css": "wp-scripts lint-style '**/*.scss'", "lint-css:fix": "npm run lint-css -- --fix", - "package-plugin": "cross-env NODE_ENV=plugin ./bin/build-plugin-zip.sh", + "package-plugin": "./bin/build-plugin-zip.sh", "postinstall": "npm run check-licenses && npm run build:packages", "pot-to-php": "./bin/pot-to-php.js", "precommit": "lint-staged", - "publish:check": "cross-env NODE_ENV=production npm run build:packages && lerna updated", - "publish:dev": "cross-env NODE_ENV=production npm run build:packages && lerna publish --npm-tag next", - "publish:prod": "cross-env NODE_ENV=production npm run build:packages && lerna publish", + "publish:check": "npm run build:packages && lerna updated", + "publish:dev": "npm run build:packages && lerna publish --npm-tag next", + "publish:prod": "npm run build:packages && lerna publish", "test": "npm run lint && npm run test-unit", "pretest-e2e": "./bin/reset-e2e-tests.sh", "test-e2e": "wp-scripts test-e2e --config packages/e2e-tests/jest.config.js", diff --git a/packages/editor-configuration/.npmrc b/packages/editor-configuration/.npmrc deleted file mode 100644 index 43c97e719a5a8..0000000000000 --- a/packages/editor-configuration/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false diff --git a/packages/editor-configuration/CHANGELOG.md b/packages/editor-configuration/CHANGELOG.md deleted file mode 100644 index c9970e2e618b5..0000000000000 --- a/packages/editor-configuration/CHANGELOG.md +++ /dev/null @@ -1,5 +0,0 @@ -## 1.0.0 (Unreleased) - -### New Features - -- Initial release. diff --git a/packages/editor-configuration/README.md b/packages/editor-configuration/README.md deleted file mode 100644 index 797981dc712bc..0000000000000 --- a/packages/editor-configuration/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# Editor configuration - -Editor configuration is a package that stores configuration values for Gutenberg. - -The package contains three different configuration files per environment: -- `config/development.js` - configuration values for local development environments and testing. -- `config/plugin.js` - configuration values for the Gutenberg plugin. -- `config/production.js` - configuration values for the block editor in Wordpress Core. - -## Installation - -Install the module - -```bash -npm install @wordpress/editor-configuration --save -``` - -_This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for ES2015+ such as lower versions of IE then using [core-js](https://github.com/zloirock/core-js) or [@babel/polyfill](https://babeljs.io/docs/en/next/babel-polyfill) will add support for these methods. Learn more about it in [Babel docs](https://babeljs.io/docs/en/next/caveats)._ - -## Usage - -### Feature flags - -Feature flags (also known as 'feature toggles') are boolean values that indicate whether a specific feature is active. - -By defining a per-environment flag for a feature, fine-grain control can be achieved over when a feature is released. Generally a feature will initally only be enabled in `development` environments. Once it reaches a certain maturity, it can be enabled in the plugin. Finally, once a feature is ready for release, it can be enabled in production. - -An advantage of feature flags is that a pull request implementing a new feature no longer needs to be held back from being merged into `master`. As long as the code passes review, a feature can be merged but disabled until it's ready to be released. - -### Adding a feature flag - -For each configuration file, add a new item in the `features` object. Add new flags in alphabetical order to make it easier to browse them: - -development.js -```javascript -export default { - features: { - // ... - 'editor/my-first-feature': true, - // ... - } -} -``` - -plugin.js / production.js -```javascript -export default { - features: { - // ... - 'editor/my-first-feature': false, - // ... - } -} -``` - -In the file(s) the feature is implemented, import the `getFeatureFlag` function from this package, and use it to determine whether the feature is active: -```javascript -import { getFeatureFlag } from '@wordpress/editor-configuration'; - -const isMyFirstFeatureActive = getFeatureFlag( 'editor/my-first-feature' ); -``` - -Finally, avoid executing code that implements the feature at the earliest possible point: -```javascript -function myFirstFeature() { - if ( ! isMyFirstFeatureActive ) { - return; - } - - // ... implementation of feature -} - -``` diff --git a/packages/editor-configuration/package.json b/packages/editor-configuration/package.json deleted file mode 100644 index bed1ff4fd6005..0000000000000 --- a/packages/editor-configuration/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "@wordpress/editor-configuration", - "version": "1.0.0", - "description": "Configuration for the WordPress block editor.", - "author": "The WordPress Contributors", - "license": "GPL-2.0-or-later", - "keywords": [ - "wordpress", - "config", - "configuration" - ], - "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/editor-configuration/README.md", - "repository": { - "type": "git", - "url": "https://github.com/WordPress/gutenberg.git" - }, - "bugs": { - "url": "https://github.com/WordPress/gutenberg/issues" - }, - "main": "build/index.js", - "module": "build-module/index.js", - "react-native": "src/index", - "dependencies": {}, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/editor-configuration/src/config/development.js b/packages/editor-configuration/src/config/development.js deleted file mode 100644 index 6ed10e3b2a47c..0000000000000 --- a/packages/editor-configuration/src/config/development.js +++ /dev/null @@ -1,3 +0,0 @@ -export default { - features: {}, -}; diff --git a/packages/editor-configuration/src/config/plugin.js b/packages/editor-configuration/src/config/plugin.js deleted file mode 100644 index 6ed10e3b2a47c..0000000000000 --- a/packages/editor-configuration/src/config/plugin.js +++ /dev/null @@ -1,3 +0,0 @@ -export default { - features: {}, -}; diff --git a/packages/editor-configuration/src/config/production.js b/packages/editor-configuration/src/config/production.js deleted file mode 100644 index 6ed10e3b2a47c..0000000000000 --- a/packages/editor-configuration/src/config/production.js +++ /dev/null @@ -1,3 +0,0 @@ -export default { - features: {}, -}; diff --git a/packages/editor-configuration/src/index.js b/packages/editor-configuration/src/index.js deleted file mode 100644 index 1c27b8750ecba..0000000000000 --- a/packages/editor-configuration/src/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Internal dependencies - */ -import development from './config/development'; -import plugin from './config/plugin'; -import production from './config/production'; - -import { - getFeatureFlag as unboundGetFeatureFlag, -} from './utils'; - -const configMap = { - development, - plugin, - production, -}; - -const activeConfig = configMap[ process.env.NODE_ENV ] || development; - -export const getFeatureFlag = unboundGetFeatureFlag.bind( null, activeConfig ); diff --git a/packages/editor-configuration/src/utils/index.js b/packages/editor-configuration/src/utils/index.js deleted file mode 100644 index cb5e5ddaa421f..0000000000000 --- a/packages/editor-configuration/src/utils/index.js +++ /dev/null @@ -1,15 +0,0 @@ - -/** - * Get the value of a feature flag by its name. - * - * Note, when this function is exported from `@wordpress/editor-configurations, - * it is pre-bound and only requires the `featureName` argument. - * - * @param {Object} config The configuration object. - * @param {string} featureName The name of the feature. - * - * @return {boolean} Whether the feature is enabled (`true`) or disabled (`false`). - */ -export function getFeatureFlag( config, featureName ) { - return !! config.features[ featureName ]; -} diff --git a/packages/editor-configuration/src/utils/test/index.js b/packages/editor-configuration/src/utils/test/index.js deleted file mode 100644 index 6066444f6cee9..0000000000000 --- a/packages/editor-configuration/src/utils/test/index.js +++ /dev/null @@ -1,21 +0,0 @@ -import { getFeatureFlag } from '../'; - -describe( 'getFeatureFlag', () => { - it( 'returns false for an unknown feature flag', () => { - const config = { - features: {}, - }; - expect( getFeatureFlag( config, 'foo' ) ).toBe( false ); - } ); - - it( 'returns the truthiness of the value set in the config for a known feature flag', () => { - const config = { - features: { - foo: true, - bar: false, - }, - }; - expect( getFeatureFlag( config, 'foo' ) ).toBe( true ); - expect( getFeatureFlag( config, 'bar' ) ).toBe( false ); - } ); -} ); diff --git a/webpack.config.js b/webpack.config.js index 166c2c14c244b..7d9877d7c15e3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -60,10 +60,8 @@ gutenbergPackages.forEach( ( name ) => { }; } ); -const isDevelopment = process.env.NODE_ENV === 'development'; -const isPlugin = process.env.NODE_ENV === 'plugin'; const isProduction = process.env.NODE_ENV === 'production'; -const mode = isProduction || isPlugin ? 'production' : 'development'; +const mode = isProduction ? 'production' : 'development'; const config = { mode, @@ -169,7 +167,7 @@ const config = { process.env.GUTENBERG_BUNDLE_ANALYZER && new BundleAnalyzerPlugin(), // GUTENBERG_LIVE_RELOAD_PORT global variable changes port on which live reload works // when running watch mode. - isDevelopment && new LiveReloadPlugin( { port: process.env.GUTENBERG_LIVE_RELOAD_PORT || 35729 } ), + ! isProduction && new LiveReloadPlugin( { port: process.env.GUTENBERG_LIVE_RELOAD_PORT || 35729 } ), ].filter( Boolean ), stats: { children: false, From a8a3494d3c6676dab38333fae4db0feff5a48c6e Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 24 Jan 2019 15:48:09 +0800 Subject: [PATCH 03/22] Switch to using webpack define plugin to inject a global GUTENBERG_PHASE variable --- package.json | 4 ++-- webpack.config.js | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 07cd37a6a9364..6b78aeb4d9106 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "precheck-local-changes": "npm run docs:build", "check-local-changes": "( git diff -U0 | xargs -0 node bin/process-git-diff ) || ( echo \"There are local uncommitted changes after one or both of 'npm install' or 'npm run docs:build'!\" && exit 1 );", "predev": "npm run check-engines", - "dev": "npm run build:packages && concurrently \"wp-scripts start\" \"npm run dev:packages\"", + "dev": "cross-env GUTENBERG_PHASE=2 npm run build:packages && concurrently \"wp-scripts start\" \"npm run dev:packages\"", "dev:packages": "node ./bin/packages/watch.js", "docs:build": "node docs/tool", "fixtures:clean": "rimraf \"test/integration/full-content/fixtures/*.+(json|serialized.html)\"", @@ -169,7 +169,7 @@ "lint-pkg-json": "wp-scripts lint-pkg-json ./packages", "lint-css": "wp-scripts lint-style '**/*.scss'", "lint-css:fix": "npm run lint-css -- --fix", - "package-plugin": "./bin/build-plugin-zip.sh", + "package-plugin": "cross-env GUTENBERG_PHASE=2 ./bin/build-plugin-zip.sh", "postinstall": "npm run check-licenses && npm run build:packages", "pot-to-php": "./bin/pot-to-php.js", "precommit": "lint-staged", diff --git a/webpack.config.js b/webpack.config.js index 7d9877d7c15e3..5c565d01cb283 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,7 @@ /** * External dependencies */ +const { DefinePlugin } = require( 'webpack' ); const WebpackRTLPlugin = require( 'webpack-rtl-plugin' ); const LiveReloadPlugin = require( 'webpack-livereload-plugin' ); const CopyWebpackPlugin = require( 'copy-webpack-plugin' ); @@ -105,6 +106,10 @@ const config = { ], }, plugins: [ + // Inject the `GUTENBERG_PHASE` global, used for feature flagging. + new DefinePlugin( { + GUTENBERG_PHASE: JSON.stringify( process.env.GUTENBERG_PHASE || 1 ), + } ), // Create RTL files with a -rtl suffix new WebpackRTLPlugin( { suffix: '-rtl', From 4d8f8f9cd1a95e803fafc6b5994c355ecde604c5 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 25 Jan 2019 15:00:37 +0800 Subject: [PATCH 04/22] Iterate: use window.GUTENBERG_PHASE to avoid thrown errors from an undefined global --- webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 5c565d01cb283..0f14d0a462f1e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -108,7 +108,7 @@ const config = { plugins: [ // Inject the `GUTENBERG_PHASE` global, used for feature flagging. new DefinePlugin( { - GUTENBERG_PHASE: JSON.stringify( process.env.GUTENBERG_PHASE || 1 ), + 'window.GUTENBERG_PHASE': JSON.stringify( process.env.GUTENBERG_PHASE || 1 ), } ), // Create RTL files with a -rtl suffix new WebpackRTLPlugin( { From 116be55baa6f347b898c33838d98bb2334cd96d6 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 31 Jan 2019 17:10:27 +0800 Subject: [PATCH 05/22] Add custom eslint rule for usage of GUTENBERG_PHASE --- packages/eslint-plugin/CHANGELOG.md | 1 + packages/eslint-plugin/README.md | 1 + packages/eslint-plugin/configs/custom.js | 1 + .../docs/rules/gutenberg-phase.md | 62 +++++++ .../rules/__tests__/gutenberg-phase.js | 58 +++++++ .../eslint-plugin/rules/gutenberg-phase.js | 164 ++++++++++++++++++ 6 files changed, 287 insertions(+) create mode 100644 packages/eslint-plugin/docs/rules/gutenberg-phase.md create mode 100644 packages/eslint-plugin/rules/__tests__/gutenberg-phase.js create mode 100644 packages/eslint-plugin/rules/gutenberg-phase.js diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index daf663e8157e9..4e3f6f8430996 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -9,6 +9,7 @@ - New Rule: [`@wordpress/no-unused-vars-before-return`](https://github.com/WordPress/gutenberg/blob/master/packages/eslint-plugin/docs/rules/no-unused-vars-before-return.md) - New Rule: [`@wordpress/dependency-group`](https://github.com/WordPress/gutenberg/blob/master/packages/eslint-plugin/docs/rules/dependency-group.md) - New Rule: [`@wordpress/valid-sprintf`](https://github.com/WordPress/gutenberg/blob/master/packages/eslint-plugin/docs/rules/valid-sprintf.md) +- New Rule: [`@wordpress/gutenberg-phase`](https://github.com/WordPress/gutenberg/blob/master/packages/eslint-plugin/docs/rules/gutenberg-phase.md) ## 1.0.0 (2018-12-12) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 2af095ba3afec..eab56cc10cbf1 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -52,6 +52,7 @@ Rule|Description [no-unused-vars-before-return](/packages/eslint-plugin/docs/rules/no-unused-vars-before-return.md)|Disallow assigning variable values if unused before a return [dependency-group](/packages/eslint-plugin/docs/rules/dependency-group.md)|Enforce dependencies docblocks formatting [valid-sprintf](/packages/eslint-plugin/docs/rules/valid-sprintf.md)|Disallow assigning variable values if unused before a return +[gutenberg-phase](docs/rules/gutenberg-phase.md)|Governs the use of the `window.GUTENBERG_PHASE` constant ### Legacy diff --git a/packages/eslint-plugin/configs/custom.js b/packages/eslint-plugin/configs/custom.js index 7d79a3e055b18..3439a1cec6feb 100644 --- a/packages/eslint-plugin/configs/custom.js +++ b/packages/eslint-plugin/configs/custom.js @@ -6,6 +6,7 @@ module.exports = { '@wordpress/dependency-group': 'error', '@wordpress/no-unused-vars-before-return': 'error', '@wordpress/valid-sprintf': 'error', + '@wordpress/gutenberg-phase': 'error', 'no-restricted-syntax': [ 'error', { diff --git a/packages/eslint-plugin/docs/rules/gutenberg-phase.md b/packages/eslint-plugin/docs/rules/gutenberg-phase.md new file mode 100644 index 0000000000000..fa28de0461892 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/gutenberg-phase.md @@ -0,0 +1,62 @@ +# The `GUTENBERG_PHASE` global (gutenberg-phase) + +To enable the use of feature flags in Gutenberg, the GUTENBERG_PHASE global constant was introduced. This constant is replaced with a number value at build time using webpack's define plugin. + +There are a few rules around using this constant: + +- Only access `GUTENBERG_PHASE` via the window object, e.g. `window.GUTENBERG_PHASE`. This ensures that environments that do not inject the variable at build time do not encounter an error due to an undefined global variable (`window.GUTENBERG_PHASE` evaluates as undefined, while `GUTENBERG_PHASE` throws an error.). The webpack configuration will also only replace exact matches of `window.GUTENBERG_PHASE`. +- The `GUTENBERG_PHASE` variable should only be used in a strict equality comparison with a number, e.g. `window.GUTENBERG_PHASE === 2` or `window.GUTENBERG_PHASE !== 2`. The value of the injected variable should always be a number, so this ensures the correct evaluation of the expression. Furthermore, when `GUTENBERG_PHASE` is undefined this comparison still returns either true (for `!==`) or false (for `===`), whereas both the `<` and `>` operators will always return false. +- `GUTENBERG_PHASE` should only be used within the condition of an if statement, e.g. `if ( window.GUTENBERG_PHASE === 2 ) { // implement feature here }`. This rule ensure that where the expression `window.GUTENBERG_PHASE === 2` resolves to false, the entire if statement and its body is removed through dead code elimination. + + +## Rule details + +The following patterns are considered warnings: + +```js +if ( GUTENBERG_PHASE === 2 ) { + // implement feature here. +} +``` + +```js +if ( window[ 'GUTENBERG_PHASE' ] === 2 ) { + // implement feature here. +} +``` + +```js +if ( window.GUTENBERG_PHASE === '2' ) { + // implement feature here. +} +``` + +```js +if ( window.GUTENBERG_PHASE > 2 ) { + // implement feature here. +} +``` + +```js +if ( true || window.GUTENBERG_PHASE > 2 ) { + // implement feature here. +} +``` + +```js +const isMyFeatureActive = window.GUTENBERG_PHASE === 2; +``` + +The following patterns are not considered warnings: + +```js +if ( window.GUTENBERG_PHASE === 2 ) { + // implement feature here. +} +``` + +```js +if ( window.GUTENBERG_PHASE !== 2 ) { + return; +} +``` diff --git a/packages/eslint-plugin/rules/__tests__/gutenberg-phase.js b/packages/eslint-plugin/rules/__tests__/gutenberg-phase.js new file mode 100644 index 0000000000000..9e318d01d0b78 --- /dev/null +++ b/packages/eslint-plugin/rules/__tests__/gutenberg-phase.js @@ -0,0 +1,58 @@ +/** + * External dependencies + */ +import { RuleTester } from 'eslint'; + +/** + * Internal dependencies + */ +import rule from '../gutenberg-phase'; + +const ruleTester = new RuleTester( { + parserOptions: { + ecmaVersion: 6, + }, +} ); + +const ACCESS_ERROR = 'The `GUTENBERG_PHASE` constant should only be accessed as a property of the `window` object using dot notation.'; +const EQUALITY_ERROR = 'The `GUTENBERG_PHASE` constant should only be used in a strict equality comparison with a primitive number.'; +const IF_ERROR = 'The `GUTENBERG_PHASE` constant should only be used as part of an expression that is the only condition of an if statement.'; + +ruleTester.run( 'gutenberg-phase', rule, { + valid: [ + { code: `if ( window.GUTENBERG_PHASE === 2 ) {}` }, + { code: `if ( window.GUTENBERG_PHASE !== 2 ) {}` }, + { code: `if ( 2 === window.GUTENBERG_PHASE ) {}` }, + { code: `if ( 2 !== window.GUTENBERG_PHASE ) {}` }, + ], + invalid: [ + { + code: `if ( GUTENBERG_PHASE === 1 ) {}`, + errors: [ { message: ACCESS_ERROR } ], + }, + { + code: `if ( window[ 'GUTENBERG_PHASE' ] === 1 ) {}`, + errors: [ { message: ACCESS_ERROR } ], + }, + { + code: `if ( window.GUTENBERG_PHASE > 1 ) {}`, + errors: [ { message: EQUALITY_ERROR } ], + }, + { + code: `if ( window.GUTENBERG_PHASE === '2' ) {}`, + errors: [ { message: EQUALITY_ERROR } ], + }, + { + code: `if ( true ) { window.GUTENBERG_PHASE === 2 }`, + errors: [ { message: IF_ERROR } ], + }, + { + code: `if ( true || window.GUTENBERG_PHASE === 2 ) {}`, + errors: [ { message: IF_ERROR } ], + }, + { + code: `const isFeatureActive = window.GUTENBERG_PHASE === 2;`, + errors: [ { message: IF_ERROR } ], + }, + ], +} ); diff --git a/packages/eslint-plugin/rules/gutenberg-phase.js b/packages/eslint-plugin/rules/gutenberg-phase.js new file mode 100644 index 0000000000000..a93b2fe7aa9bc --- /dev/null +++ b/packages/eslint-plugin/rules/gutenberg-phase.js @@ -0,0 +1,164 @@ +/** + * Traverse up through the chain of parent AST nodes returning the first parent + * the predicate returns a truthy value for. + * + * @param {Object} sourceNode The AST node to search from. + * @param {function} predicate A predicate invoked for each parent. + * + * @return {?Object } The first encountered parent node where the predicate + * returns a truthy value. + */ +function findParent( sourceNode, predicate ) { + if ( ! sourceNode.parent ) { + return; + } + + if ( predicate( sourceNode.parent ) ) { + return sourceNode.parent; + } + + return findParent( sourceNode.parent, predicate ); +} + +/** + * Tests whether the GUTENBERG_PHASE variable is accessed via the global window + * object, triggering a violation if not. + * + * @example + * ```js + * // good + * if ( window.GUTENBERG_PHASE === 2 ) { + * + * // bad + * if ( GUTENBERG_PHASE === 2 ) { + * ``` + * + * @param {Object} node The GUTENBERG_PHASE identifier node. + * @param {Object} context The eslint context object. + */ +function testIsAccessedViaWindowObject( node, context ) { + const parent = node.parent; + + if ( + parent && + node.type === 'Identifier' && + parent.type === 'MemberExpression' && + parent.object.name === 'window' && + ! parent.computed + + ) { + return; + } + + context.report( + node, + 'The `GUTENBERG_PHASE` constant should only be accessed as a property of the `window` object using dot notation.', + ); +} + +/** + * Tests whether the GUTENBERG_PHASE variable is used in a strict binary + * equality expression in a comparison with a number, triggering a + * violation if not. + * + * @example + * ```js + * // good + * if ( window.GUTENBERG_PHASE === 2 ) { + * + * // bad + * if ( window.GUTENBERG_PHASE >= '2' ) { + * ``` + * + * @param {Object} node The GUTENBERG_PHASE identifier node. + * @param {Object} context The eslint context object. + */ +function testIsUsedInStrictBinaryExpression( node, context ) { + const parent = findParent( node, ( candidate ) => candidate.type === 'BinaryExpression' ); + + if ( parent ) { + const comparisonNode = node.parent.type === 'MemberExpression' ? node.parent : node; + + // Test for window.GUTENBERG_PHASE === or === window.GUTENBERG_PHASE + const hasCorrectOperator = [ '===', '!==' ].includes( parent.operator ); + const hasCorrectOperands = ( + ( parent.left === comparisonNode && typeof parent.right.value === 'number' ) || + ( parent.right === comparisonNode && typeof parent.left.value === 'number' ) + ); + + if ( hasCorrectOperator && hasCorrectOperands ) { + return; + } + } + + context.report( + node, + 'The `GUTENBERG_PHASE` constant should only be used in a strict equality comparison with a primitive number.', + ); +} + +/** + * Tests whether the GUTENBERG_PHASE variable is used as the condition for an + * if statement, triggering a violation if not. + * + * @example + * ```js + * // good + * if ( window.GUTENBERG_PHASE === 2 ) { + * + * // bad + * const isFeatureActive = window.GUTENBERG_PHASE === 2; + * ``` + * + * @param {Object} node The GUTENBERG_PHASE identifier node. + * @param {Object} context The eslint context object. + */ +function testIsUsedInIfStatement( node, context ) { + const ifParent = findParent( node, ( candidate ) => candidate.type === 'IfStatement' ); + const binaryParent = findParent( node, ( candidate ) => candidate.type === 'BinaryExpression' ); + + if ( ifParent && + binaryParent && + ifParent.test && + ifParent.test.start === binaryParent.start && + ifParent.test.end === binaryParent.end + ) { + return; + } + + context.report( + node, + 'The `GUTENBERG_PHASE` constant should only be used as part of an expression that is the only condition of an if statement.', + ); +} + +module.exports = { + meta: { + type: 'problem', + schema: [], + }, + create( context ) { + return { + Identifier( node ) { + // Bypass any identifiers with a node name different to `GUTENBERG_PHASE`. + if ( node.name !== 'GUTENBERG_PHASE' ) { + return; + } + + testIsAccessedViaWindowObject( node, context ); + testIsUsedInStrictBinaryExpression( node, context ); + testIsUsedInIfStatement( node, context ); + }, + Literal( node ) { + // Bypass any identifiers with a node value different to `GUTENBERG_PHASE`. + if ( node.value !== 'GUTENBERG_PHASE' ) { + return; + } + + if ( node.parent && node.parent.type === 'MemberExpression' ) { + testIsAccessedViaWindowObject( node, context ); + } + }, + }; + }, +}; From 2116715814f277e7d700c60e98ab3c8992e73e80 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 1 Feb 2019 12:09:23 +0800 Subject: [PATCH 06/22] Disable new eslint rule when used in webpack config --- webpack.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 0f14d0a462f1e..1cfe378b0a716 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -106,8 +106,9 @@ const config = { ], }, plugins: [ - // Inject the `GUTENBERG_PHASE` global, used for feature flagging. new DefinePlugin( { + // Inject the `GUTENBERG_PHASE` global, used for feature flagging. + // eslint-disable-next-line @wordpress/gutenberg-phase 'window.GUTENBERG_PHASE': JSON.stringify( process.env.GUTENBERG_PHASE || 1 ), } ), // Create RTL files with a -rtl suffix From c6784134fc31aef6ee47a75d85f3548772d0bcd2 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 1 Feb 2019 16:19:51 +0800 Subject: [PATCH 07/22] Add readme --- .../developers/feature-flags.md | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 docs/designers-developers/developers/feature-flags.md diff --git a/docs/designers-developers/developers/feature-flags.md b/docs/designers-developers/developers/feature-flags.md new file mode 100644 index 0000000000000..b52c67270fc51 --- /dev/null +++ b/docs/designers-developers/developers/feature-flags.md @@ -0,0 +1,121 @@ +# 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 should only be released to the Gutenberg plugin, while improvements and bug fixes should still continue to make their way into core releases. + +The technique for handling this is known as a 'feature flag'. + +## Introducing `window.GUTENBERG_PHASE` + +The `window.GUTENBERG_PHASE` constant is a global variable containing a number representing 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`. + +### How it works + +More precisely, the `GUTENBERG_PHASE` variable isn't actually set to a value. Instead, any instances of `window.GUTENBERG_PHASE` in the codebase will be replaced during the webpack build by webpack's define plugin (https://webpack.js.org/plugins/define-plugin/). + +If you write the following code: +```js +if ( window.GUTENBERG_PHASE === 2 ) { + phaseTwoFeature(); +} +``` + +When building the codebase for the plugin the variable will be replaced with the number literal `2`: +```js +if ( 2 === 2 ) { + phaseTwoFeature(); +} +``` + +Any code within the body of the if statement will be executed within the gutenberg plugin since `2 === 2` evaluates to `true`. + +For core, the `window.GUTENBERG_PHASE` variable is set to `1`, so the built code will look like: +```js +if ( 1 === 2 ) { + phaseTwoFeature(); +} +``` + +`1 === 2` evaluates to false so the phase 2 feature will not be executed within core. + +### Dead Code Elimination + +When building code for production, webpack 'minifies' code (https://en.wikipedia.org/wiki/Minification_(programming)), removing the amount of unnecessary JavaScript as much as possible. One of the steps involves something known as 'dead code elimination'. + +When the following code is encountered, webpack determines that the surrounding `if`statement is unnecessary: +```js +if ( 2 === 2 ) { + phaseTwoFeature(); +} +``` + + The condition will alway evaluates to `true`, so can be removed leaving just the code in the body: + ```js + phaseTwoFeature(); + ``` + +Similarly when building for core, the condition in the following `if` statement always resolves to false: +```js +if ( 1 === 2 ) { + phaseTwoFeature(); +} +``` + +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. + +## Basic Use + +As a simple rule, try to avoid executing any code for a phase 2 feature by using the following techniques: + +### Simple if statement + +If you're enhancing existing code with a phase 2 feature, try extracting phase 2 code into a separate function as much as possible, and avoid calling that function: +```js +if ( window.GUTENBERG_PHASE === 2) { + myPhaseTwoFeature(); +} +``` + +### Early return + +Alternatively, inside a function, return as early as possible before any phase 2 code can be executed: + +```js +export default function myPhaseTwoFeature() { + if ( window.GUTENBERG_PHASE !== 2 ) { + return; + } + + // implementation of feature +} +``` + +An early return may have to be used for a function that is exported since `export` statements can't be dynamic. + + +## FAQ + +### Since `GUTENBERG_PHASE` is a global object, why do I have to access it through the `window` object, e.g. `window.GUTENBERG_PHASE`? + +Third-party consumers of WordPress npm packages may have their own build process, resulting in `window.GUTENBERG_PHASE` not being replaced at build time with the appropriate number. In JavaScript, when attempting to access an undefined global variable an error is thrown, a situation that would cause lots of issues for third-parties (or additional build configuration). Accessing the `GUTENBERG_PHASE` variable through the `window` object circumvents this, the value of the variable evaluates to `undefined` resulting in WordPress packages continuing to work for third-parties. + +### Why should I only use `===` or `!==` when comparing `window.GUTENBERG_PHASE` and not `>`, `>=`, `<` or `<=`? + +This is a restriction due to the behaviour of the greater than or less than operators in JavaScript when `window.GUTENBERG_PHASE` is undefined, as might be the case for third party users of WordPress npm packages. Both `window.GUTENBERG_PHASE < 2` and `window.GUTENBERG_PHASE > 1` resolve to false. When writing `if ( window.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 ( window.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 = window.GUTENBERG_PHASE === 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. From fb9768de7a6cc35f91e81aa859e8ba6b041ae7eb Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 5 Feb 2019 15:32:59 +0800 Subject: [PATCH 08/22] Include phase 2 features in e2e tests --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6b78aeb4d9106..03519007bc97c 100644 --- a/package.json +++ b/package.json @@ -178,7 +178,7 @@ "publish:prod": "npm run build:packages && lerna publish", "test": "npm run lint && npm run test-unit", "pretest-e2e": "./bin/reset-e2e-tests.sh", - "test-e2e": "wp-scripts test-e2e --config packages/e2e-tests/jest.config.js", + "test-e2e": "cross-env GUTENBERG_PHASE=2 wp-scripts test-e2e --config packages/e2e-tests/jest.config.js", "test-e2e:watch": "npm run test-e2e -- --watch", "test-php": "npm run lint-php && npm run test-unit-php", "test-unit": "wp-scripts test-unit-js --config test/unit/jest.config.json", From 73f13dca72b6cd59f2f4c72a35ee30467954d1ad Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 6 Feb 2019 14:55:05 +0800 Subject: [PATCH 09/22] Allow use of GUTENBERG_PHASE in a ternary and update documentation. --- .../developers/feature-flags.md | 29 +++++++------------ .../rules/__tests__/gutenberg-phase.js | 4 ++- .../eslint-plugin/rules/gutenberg-phase.js | 19 +++++++----- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/docs/designers-developers/developers/feature-flags.md b/docs/designers-developers/developers/feature-flags.md index b52c67270fc51..4e9a0c61687f8 100644 --- a/docs/designers-developers/developers/feature-flags.md +++ b/docs/designers-developers/developers/feature-flags.md @@ -1,6 +1,6 @@ # 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 should only be released to the Gutenberg plugin, while improvements and bug fixes should still continue to make their way into core releases. +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. The technique for handling this is known as a 'feature flag'. @@ -64,34 +64,27 @@ The minification process will remove the entire `if` statement including the bod ## Basic Use -As a simple rule, try to avoid executing any code for a phase 2 feature by using the following techniques: +A phase 2 function or constant should be exported using the following ternary syntax: -### Simple if statement - -If you're enhancing existing code with a phase 2 feature, try extracting phase 2 code into a separate function as much as possible, and avoid calling that function: ```js -if ( window.GUTENBERG_PHASE === 2) { - myPhaseTwoFeature(); +function myPhaseTwoFeature() { + // implementation } -``` -### Early return +export const phaseTwoFeature = window.GUTENBERG_PHASE === 2 ? myPhaseTwoFeature : undefined; +``` -Alternatively, inside a function, return as early as possible before any phase 2 code can be executed: +In phase 1 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: ```js -export default function myPhaseTwoFeature() { - if ( window.GUTENBERG_PHASE !== 2 ) { - return; - } +import { phaseTwoFeature } from '@wordpress/foo'; - // implementation of feature +if ( window.GUTENBERG_PHASE === 2) { + phaseTwoFeature(); } ``` -An early return may have to be used for a function that is exported since `export` statements can't be dynamic. - - ## FAQ ### Since `GUTENBERG_PHASE` is a global object, why do I have to access it through the `window` object, e.g. `window.GUTENBERG_PHASE`? diff --git a/packages/eslint-plugin/rules/__tests__/gutenberg-phase.js b/packages/eslint-plugin/rules/__tests__/gutenberg-phase.js index 9e318d01d0b78..21d2f14da9c85 100644 --- a/packages/eslint-plugin/rules/__tests__/gutenberg-phase.js +++ b/packages/eslint-plugin/rules/__tests__/gutenberg-phase.js @@ -16,7 +16,7 @@ const ruleTester = new RuleTester( { const ACCESS_ERROR = 'The `GUTENBERG_PHASE` constant should only be accessed as a property of the `window` object using dot notation.'; const EQUALITY_ERROR = 'The `GUTENBERG_PHASE` constant should only be used in a strict equality comparison with a primitive number.'; -const IF_ERROR = 'The `GUTENBERG_PHASE` constant should only be used as part of an expression that is the only condition of an if statement.'; +const IF_ERROR = 'The `GUTENBERG_PHASE` constant should only be used as part of the condition in an if statement or ternary expression.'; ruleTester.run( 'gutenberg-phase', rule, { valid: [ @@ -24,6 +24,8 @@ ruleTester.run( 'gutenberg-phase', rule, { { code: `if ( window.GUTENBERG_PHASE !== 2 ) {}` }, { code: `if ( 2 === window.GUTENBERG_PHASE ) {}` }, { code: `if ( 2 !== window.GUTENBERG_PHASE ) {}` }, + { code: `const test = window.GUTENBERG_PHASE === 2 ? foo : bar` }, + { code: `const test = window.GUTENBERG_PHASE !== 2 ? foo : bar` }, ], invalid: [ { diff --git a/packages/eslint-plugin/rules/gutenberg-phase.js b/packages/eslint-plugin/rules/gutenberg-phase.js index a93b2fe7aa9bc..18ca3774c8955 100644 --- a/packages/eslint-plugin/rules/gutenberg-phase.js +++ b/packages/eslint-plugin/rules/gutenberg-phase.js @@ -113,22 +113,25 @@ function testIsUsedInStrictBinaryExpression( node, context ) { * @param {Object} node The GUTENBERG_PHASE identifier node. * @param {Object} context The eslint context object. */ -function testIsUsedInIfStatement( node, context ) { - const ifParent = findParent( node, ( candidate ) => candidate.type === 'IfStatement' ); +function testIsUsedInIfOrTernary( node, context ) { + const conditionalParent = findParent( + node, + ( candidate ) => [ 'IfStatement', 'ConditionalExpression' ].includes( candidate.type ) + ); const binaryParent = findParent( node, ( candidate ) => candidate.type === 'BinaryExpression' ); - if ( ifParent && + if ( conditionalParent && binaryParent && - ifParent.test && - ifParent.test.start === binaryParent.start && - ifParent.test.end === binaryParent.end + conditionalParent.test && + conditionalParent.test.start === binaryParent.start && + conditionalParent.test.end === binaryParent.end ) { return; } context.report( node, - 'The `GUTENBERG_PHASE` constant should only be used as part of an expression that is the only condition of an if statement.', + 'The `GUTENBERG_PHASE` constant should only be used as part of the condition in an if statement or ternary expression.', ); } @@ -147,7 +150,7 @@ module.exports = { testIsAccessedViaWindowObject( node, context ); testIsUsedInStrictBinaryExpression( node, context ); - testIsUsedInIfStatement( node, context ); + testIsUsedInIfOrTernary( node, context ); }, Literal( node ) { // Bypass any identifiers with a node value different to `GUTENBERG_PHASE`. From f395860e55831b2d4029cdabf163778659308b7b Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 6 Feb 2019 16:51:24 +0800 Subject: [PATCH 10/22] Add links to docs --- docs/manifest.json | 6 ++++++ docs/toc.json | 1 + 2 files changed, 7 insertions(+) diff --git a/docs/manifest.json b/docs/manifest.json index 675c7034e52a1..038b93e03f25a 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -107,6 +107,12 @@ "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/accessibility.md", "parent": "developers" }, + { + "title": "Feature Flags", + "slug": "feature-flags", + "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/designers-developers/developers/feature-flags.md", + "parent": "developers" + }, { "title": "Data Module Reference", "slug": "data", diff --git a/docs/toc.json b/docs/toc.json index d3e7b6048a130..8d87e52bc8975 100644 --- a/docs/toc.json +++ b/docs/toc.json @@ -19,6 +19,7 @@ ]}, {"docs/designers-developers/developers/internationalization.md": []}, {"docs/designers-developers/developers/accessibility.md": []}, + {"docs/designers-developers/developers/feature-flags.md": []}, {"docs/designers-developers/developers/data/README.md": "{{data}}"}, {"docs/designers-developers/developers/packages.md": "{{packages}}"}, {"packages/components/README.md": "{{components}}"}, From 4be7f07dbe4fbc46db1fe6c70034559810b4f533 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 6 Feb 2019 16:56:45 +0800 Subject: [PATCH 11/22] Minor docs changes --- docs/designers-developers/developers/feature-flags.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/designers-developers/developers/feature-flags.md b/docs/designers-developers/developers/feature-flags.md index 4e9a0c61687f8..cd8ed0e1d08d7 100644 --- a/docs/designers-developers/developers/feature-flags.md +++ b/docs/designers-developers/developers/feature-flags.md @@ -28,7 +28,7 @@ if ( 2 === 2 ) { Any code within the body of the if statement will be executed within the gutenberg plugin since `2 === 2` evaluates to `true`. -For core, the `window.GUTENBERG_PHASE` variable is set to `1`, so the built code will look like: +For core, the `window.GUTENBERG_PHASE` variable is replaced with `1`, so the built code will look like: ```js if ( 1 === 2 ) { phaseTwoFeature(); @@ -87,11 +87,11 @@ if ( window.GUTENBERG_PHASE === 2) { ## FAQ -### Since `GUTENBERG_PHASE` is a global object, why do I have to access it through the `window` object, e.g. `window.GUTENBERG_PHASE`? +#### Since `GUTENBERG_PHASE` is a global object, why do I have to access it through the `window` object, e.g. `window.GUTENBERG_PHASE`? Third-party consumers of WordPress npm packages may have their own build process, resulting in `window.GUTENBERG_PHASE` not being replaced at build time with the appropriate number. In JavaScript, when attempting to access an undefined global variable an error is thrown, a situation that would cause lots of issues for third-parties (or additional build configuration). Accessing the `GUTENBERG_PHASE` variable through the `window` object circumvents this, the value of the variable evaluates to `undefined` resulting in WordPress packages continuing to work for third-parties. -### Why should I only use `===` or `!==` when comparing `window.GUTENBERG_PHASE` and not `>`, `>=`, `<` or `<=`? +#### Why should I only use `===` or `!==` when comparing `window.GUTENBERG_PHASE` and not `>`, `>=`, `<` or `<=`? This is a restriction due to the behaviour of the greater than or less than operators in JavaScript when `window.GUTENBERG_PHASE` is undefined, as might be the case for third party users of WordPress npm packages. Both `window.GUTENBERG_PHASE < 2` and `window.GUTENBERG_PHASE > 1` resolve to false. When writing `if ( window.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. @@ -109,6 +109,6 @@ function myPhaseTwoFeature() { 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 = window.GUTENBERG_PHASE === 2`? +#### Why shouldn't I assign the result of an expression involving `GUTENBERG_PHASE` to a variable, e.g. `const isMyFeatureActive = window.GUTENBERG_PHASE === 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. +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. From ada6f4cd0c191d07ea1d0216e67cc5871fa4550c Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Mon, 11 Feb 2019 17:10:39 +0800 Subject: [PATCH 12/22] Switch from window.GUTENBERG_PHASE to process.env.GUTENBERG_PHASE --- .../developers/feature-flags.md | 26 ++++++++--------- packages/eslint-plugin/README.md | 2 +- .../docs/rules/gutenberg-phase.md | 22 +++++++-------- .../rules/__tests__/gutenberg-phase.js | 24 ++++++++-------- .../eslint-plugin/rules/gutenberg-phase.js | 28 +++++++++---------- webpack.config.js | 2 +- 6 files changed, 49 insertions(+), 55 deletions(-) diff --git a/docs/designers-developers/developers/feature-flags.md b/docs/designers-developers/developers/feature-flags.md index cd8ed0e1d08d7..ec8087a401862 100644 --- a/docs/designers-developers/developers/feature-flags.md +++ b/docs/designers-developers/developers/feature-flags.md @@ -4,17 +4,17 @@ With phase 2 of the Gutenberg project there's a need for improved control over h The technique for handling this is known as a 'feature flag'. -## Introducing `window.GUTENBERG_PHASE` +## Introducing `process.env.GUTENBERG_PHASE` -The `window.GUTENBERG_PHASE` constant is a global variable containing a number representing 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.GUTENBERG_PHASE` is an environment variable containing a number that reprsents 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`. ### How it works -More precisely, the `GUTENBERG_PHASE` variable isn't actually set to a value. Instead, any instances of `window.GUTENBERG_PHASE` in the codebase will be replaced during the webpack build by webpack's define plugin (https://webpack.js.org/plugins/define-plugin/). +More precisely, the `GUTENBERG_PHASE` variable isn't actually set to a value. Instead, any instances of `process.env.GUTENBERG_PHASE` in the codebase will be replaced during the webpack build by webpack's define plugin (https://webpack.js.org/plugins/define-plugin/). If you write the following code: ```js -if ( window.GUTENBERG_PHASE === 2 ) { +if ( process.env.GUTENBERG_PHASE === 2 ) { phaseTwoFeature(); } ``` @@ -28,7 +28,7 @@ if ( 2 === 2 ) { Any code within the body of the if statement will be executed within the gutenberg plugin since `2 === 2` evaluates to `true`. -For core, the `window.GUTENBERG_PHASE` variable is replaced with `1`, so the built code will look like: +For core, the `process.env.GUTENBERG_PHASE` variable is replaced with `1`, so the built code will look like: ```js if ( 1 === 2 ) { phaseTwoFeature(); @@ -71,7 +71,7 @@ function myPhaseTwoFeature() { // implementation } -export const phaseTwoFeature = window.GUTENBERG_PHASE === 2 ? myPhaseTwoFeature : undefined; +export const phaseTwoFeature = process.env.GUTENBERG_PHASE === 2 ? myPhaseTwoFeature : undefined; ``` In phase 1 environments the `phaseTwoFeature` export will be `undefined`. @@ -80,26 +80,22 @@ If you're attempting to import and call a phase 2 feature, be sure to wrap the c ```js import { phaseTwoFeature } from '@wordpress/foo'; -if ( window.GUTENBERG_PHASE === 2) { +if ( process.env.GUTENBERG_PHASE === 2) { phaseTwoFeature(); } ``` ## FAQ -#### Since `GUTENBERG_PHASE` is a global object, why do I have to access it through the `window` object, e.g. `window.GUTENBERG_PHASE`? +#### Why should I only use `===` or `!==` when comparing `process.env.GUTENBERG_PHASE` and not `>`, `>=`, `<` or `<=`? -Third-party consumers of WordPress npm packages may have their own build process, resulting in `window.GUTENBERG_PHASE` not being replaced at build time with the appropriate number. In JavaScript, when attempting to access an undefined global variable an error is thrown, a situation that would cause lots of issues for third-parties (or additional build configuration). Accessing the `GUTENBERG_PHASE` variable through the `window` object circumvents this, the value of the variable evaluates to `undefined` resulting in WordPress packages continuing to work for third-parties. - -#### Why should I only use `===` or `!==` when comparing `window.GUTENBERG_PHASE` and not `>`, `>=`, `<` or `<=`? - -This is a restriction due to the behaviour of the greater than or less than operators in JavaScript when `window.GUTENBERG_PHASE` is undefined, as might be the case for third party users of WordPress npm packages. Both `window.GUTENBERG_PHASE < 2` and `window.GUTENBERG_PHASE > 1` resolve to false. When writing `if ( window.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. +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 ( window.GUTENBERG_PHASE < 2 ) { + if ( process.env.GUTENBERG_PHASE < 2 ) { return; } @@ -109,6 +105,6 @@ function myPhaseTwoFeature() { 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 = window.GUTENBERG_PHASE === 2`? +#### 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`? 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. diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index eab56cc10cbf1..f7d7171eeea9c 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -52,7 +52,7 @@ Rule|Description [no-unused-vars-before-return](/packages/eslint-plugin/docs/rules/no-unused-vars-before-return.md)|Disallow assigning variable values if unused before a return [dependency-group](/packages/eslint-plugin/docs/rules/dependency-group.md)|Enforce dependencies docblocks formatting [valid-sprintf](/packages/eslint-plugin/docs/rules/valid-sprintf.md)|Disallow assigning variable values if unused before a return -[gutenberg-phase](docs/rules/gutenberg-phase.md)|Governs the use of the `window.GUTENBERG_PHASE` constant +[gutenberg-phase](docs/rules/gutenberg-phase.md)|Governs the use of the `process.env.GUTENBERG_PHASE` constant ### Legacy diff --git a/packages/eslint-plugin/docs/rules/gutenberg-phase.md b/packages/eslint-plugin/docs/rules/gutenberg-phase.md index fa28de0461892..abe70575c9cfb 100644 --- a/packages/eslint-plugin/docs/rules/gutenberg-phase.md +++ b/packages/eslint-plugin/docs/rules/gutenberg-phase.md @@ -4,14 +4,14 @@ To enable the use of feature flags in Gutenberg, the GUTENBERG_PHASE global cons There are a few rules around using this constant: -- Only access `GUTENBERG_PHASE` via the window object, e.g. `window.GUTENBERG_PHASE`. This ensures that environments that do not inject the variable at build time do not encounter an error due to an undefined global variable (`window.GUTENBERG_PHASE` evaluates as undefined, while `GUTENBERG_PHASE` throws an error.). The webpack configuration will also only replace exact matches of `window.GUTENBERG_PHASE`. -- The `GUTENBERG_PHASE` variable should only be used in a strict equality comparison with a number, e.g. `window.GUTENBERG_PHASE === 2` or `window.GUTENBERG_PHASE !== 2`. The value of the injected variable should always be a number, so this ensures the correct evaluation of the expression. Furthermore, when `GUTENBERG_PHASE` is undefined this comparison still returns either true (for `!==`) or false (for `===`), whereas both the `<` and `>` operators will always return false. -- `GUTENBERG_PHASE` should only be used within the condition of an if statement, e.g. `if ( window.GUTENBERG_PHASE === 2 ) { // implement feature here }`. This rule ensure that where the expression `window.GUTENBERG_PHASE === 2` resolves to false, the entire if statement and its body is removed through dead code elimination. +- Only access `GUTENBERG_PHASE` via `process.env`, e.g. `process.env.GUTENBERG_PHASE`. This is required since webpack's define plugin only replaces exact matches of `process.env.GUTENBERG_PHASE` in the codebase. +- The `GUTENBERG_PHASE` variable should only be used in a strict equality comparison with a number, e.g. `process.env.GUTENBERG_PHASE === 2` or `process.env.GUTENBERG_PHASE !== 2`. The value of the injected variable should always be a number, so this ensures the correct evaluation of the expression. Furthermore, when `process.env.GUTENBERG_PHASE` is undefined this comparison still returns either true (for `!==`) or false (for `===`), whereas both the `<` and `>` operators will always return false. +- `GUTENBERG_PHASE` should only be used within the condition of an if statement, e.g. `if ( process.env.GUTENBERG_PHASE === 2 ) { // implement feature here }` or ternary `process.env.GUTENBERG_PHASE === 2 ? something : somethingElse`. This rule ensures code that is disabled through a feature flag is removed by dead code elimination. ## Rule details -The following patterns are considered warnings: +Examples of **incorrect** code for this rule: ```js if ( GUTENBERG_PHASE === 2 ) { @@ -26,37 +26,37 @@ if ( window[ 'GUTENBERG_PHASE' ] === 2 ) { ``` ```js -if ( window.GUTENBERG_PHASE === '2' ) { +if ( process.env.GUTENBERG_PHASE === '2' ) { // implement feature here. } ``` ```js -if ( window.GUTENBERG_PHASE > 2 ) { +if ( process.env.GUTENBERG_PHASE > 2 ) { // implement feature here. } ``` ```js -if ( true || window.GUTENBERG_PHASE > 2 ) { +if ( true || process.env.GUTENBERG_PHASE > 2 ) { // implement feature here. } ``` ```js -const isMyFeatureActive = window.GUTENBERG_PHASE === 2; +const isMyFeatureActive = process.env.GUTENBERG_PHASE === 2; ``` -The following patterns are not considered warnings: +Examples of **correct** code for this rule: ```js -if ( window.GUTENBERG_PHASE === 2 ) { +if ( process.env.GUTENBERG_PHASE === 2 ) { // implement feature here. } ``` ```js -if ( window.GUTENBERG_PHASE !== 2 ) { +if ( process.env.GUTENBERG_PHASE !== 2 ) { return; } ``` diff --git a/packages/eslint-plugin/rules/__tests__/gutenberg-phase.js b/packages/eslint-plugin/rules/__tests__/gutenberg-phase.js index 21d2f14da9c85..579e127855090 100644 --- a/packages/eslint-plugin/rules/__tests__/gutenberg-phase.js +++ b/packages/eslint-plugin/rules/__tests__/gutenberg-phase.js @@ -14,18 +14,18 @@ const ruleTester = new RuleTester( { }, } ); -const ACCESS_ERROR = 'The `GUTENBERG_PHASE` constant should only be accessed as a property of the `window` object using dot notation.'; +const ACCESS_ERROR = 'The `GUTENBERG_PHASE` constant should be accessed using `process.env.GUTENBERG_PHASE`.'; const EQUALITY_ERROR = 'The `GUTENBERG_PHASE` constant should only be used in a strict equality comparison with a primitive number.'; const IF_ERROR = 'The `GUTENBERG_PHASE` constant should only be used as part of the condition in an if statement or ternary expression.'; ruleTester.run( 'gutenberg-phase', rule, { valid: [ - { code: `if ( window.GUTENBERG_PHASE === 2 ) {}` }, - { code: `if ( window.GUTENBERG_PHASE !== 2 ) {}` }, - { code: `if ( 2 === window.GUTENBERG_PHASE ) {}` }, - { code: `if ( 2 !== window.GUTENBERG_PHASE ) {}` }, - { code: `const test = window.GUTENBERG_PHASE === 2 ? foo : bar` }, - { code: `const test = window.GUTENBERG_PHASE !== 2 ? foo : bar` }, + { code: `if ( process.env.GUTENBERG_PHASE === 2 ) {}` }, + { code: `if ( process.env.GUTENBERG_PHASE !== 2 ) {}` }, + { code: `if ( 2 === process.env.GUTENBERG_PHASE ) {}` }, + { code: `if ( 2 !== process.env.GUTENBERG_PHASE ) {}` }, + { code: `const test = process.env.GUTENBERG_PHASE === 2 ? foo : bar` }, + { code: `const test = process.env.GUTENBERG_PHASE !== 2 ? foo : bar` }, ], invalid: [ { @@ -37,23 +37,23 @@ ruleTester.run( 'gutenberg-phase', rule, { errors: [ { message: ACCESS_ERROR } ], }, { - code: `if ( window.GUTENBERG_PHASE > 1 ) {}`, + code: `if ( process.env.GUTENBERG_PHASE > 1 ) {}`, errors: [ { message: EQUALITY_ERROR } ], }, { - code: `if ( window.GUTENBERG_PHASE === '2' ) {}`, + code: `if ( process.env.GUTENBERG_PHASE === '2' ) {}`, errors: [ { message: EQUALITY_ERROR } ], }, { - code: `if ( true ) { window.GUTENBERG_PHASE === 2 }`, + code: `if ( true ) { process.env.GUTENBERG_PHASE === 2 }`, errors: [ { message: IF_ERROR } ], }, { - code: `if ( true || window.GUTENBERG_PHASE === 2 ) {}`, + code: `if ( true || process.env.GUTENBERG_PHASE === 2 ) {}`, errors: [ { message: IF_ERROR } ], }, { - code: `const isFeatureActive = window.GUTENBERG_PHASE === 2;`, + code: `const isFeatureActive = process.env.GUTENBERG_PHASE === 2;`, errors: [ { message: IF_ERROR } ], }, ], diff --git a/packages/eslint-plugin/rules/gutenberg-phase.js b/packages/eslint-plugin/rules/gutenberg-phase.js index 18ca3774c8955..98c7a4216a4f7 100644 --- a/packages/eslint-plugin/rules/gutenberg-phase.js +++ b/packages/eslint-plugin/rules/gutenberg-phase.js @@ -21,13 +21,13 @@ function findParent( sourceNode, predicate ) { } /** - * Tests whether the GUTENBERG_PHASE variable is accessed via the global window - * object, triggering a violation if not. + * Tests whether the GUTENBERG_PHASE variable is accessed via + * `process.env.GUTENBERG_PHASE`. * * @example * ```js * // good - * if ( window.GUTENBERG_PHASE === 2 ) { + * if ( process.env.GUTENBERG_PHASE === 2 ) { * * // bad * if ( GUTENBERG_PHASE === 2 ) { @@ -36,15 +36,13 @@ function findParent( sourceNode, predicate ) { * @param {Object} node The GUTENBERG_PHASE identifier node. * @param {Object} context The eslint context object. */ -function testIsAccessedViaWindowObject( node, context ) { +function testIsAccessedViaProcessEnv( node, context ) { const parent = node.parent; if ( parent && - node.type === 'Identifier' && parent.type === 'MemberExpression' && - parent.object.name === 'window' && - ! parent.computed + context.getSource( parent ) === 'process.env.GUTENBERG_PHASE' ) { return; @@ -52,7 +50,7 @@ function testIsAccessedViaWindowObject( node, context ) { context.report( node, - 'The `GUTENBERG_PHASE` constant should only be accessed as a property of the `window` object using dot notation.', + 'The `GUTENBERG_PHASE` constant should be accessed using `process.env.GUTENBERG_PHASE`.', ); } @@ -64,10 +62,10 @@ function testIsAccessedViaWindowObject( node, context ) { * @example * ```js * // good - * if ( window.GUTENBERG_PHASE === 2 ) { + * if ( process.env.GUTENBERG_PHASE === 2 ) { * * // bad - * if ( window.GUTENBERG_PHASE >= '2' ) { + * if ( process.env.GUTENBERG_PHASE >= '2' ) { * ``` * * @param {Object} node The GUTENBERG_PHASE identifier node. @@ -79,7 +77,7 @@ function testIsUsedInStrictBinaryExpression( node, context ) { if ( parent ) { const comparisonNode = node.parent.type === 'MemberExpression' ? node.parent : node; - // Test for window.GUTENBERG_PHASE === or === window.GUTENBERG_PHASE + // Test for process.env.GUTENBERG_PHASE === or === process.env.GUTENBERG_PHASE const hasCorrectOperator = [ '===', '!==' ].includes( parent.operator ); const hasCorrectOperands = ( ( parent.left === comparisonNode && typeof parent.right.value === 'number' ) || @@ -104,10 +102,10 @@ function testIsUsedInStrictBinaryExpression( node, context ) { * @example * ```js * // good - * if ( window.GUTENBERG_PHASE === 2 ) { + * if ( process.env.GUTENBERG_PHASE === 2 ) { * * // bad - * const isFeatureActive = window.GUTENBERG_PHASE === 2; + * const isFeatureActive = process.env.GUTENBERG_PHASE === 2; * ``` * * @param {Object} node The GUTENBERG_PHASE identifier node. @@ -148,7 +146,7 @@ module.exports = { return; } - testIsAccessedViaWindowObject( node, context ); + testIsAccessedViaProcessEnv( node, context ); testIsUsedInStrictBinaryExpression( node, context ); testIsUsedInIfOrTernary( node, context ); }, @@ -159,7 +157,7 @@ module.exports = { } if ( node.parent && node.parent.type === 'MemberExpression' ) { - testIsAccessedViaWindowObject( node, context ); + testIsAccessedViaProcessEnv( node, context ); } }, }; diff --git a/webpack.config.js b/webpack.config.js index 1cfe378b0a716..f53d21e6355ec 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -109,7 +109,7 @@ const config = { new DefinePlugin( { // Inject the `GUTENBERG_PHASE` global, used for feature flagging. // eslint-disable-next-line @wordpress/gutenberg-phase - 'window.GUTENBERG_PHASE': JSON.stringify( process.env.GUTENBERG_PHASE || 1 ), + 'process.env.GUTENBERG_PHASE': JSON.stringify( process.env.GUTENBERG_PHASE || 1 ), } ), // Create RTL files with a -rtl suffix new WebpackRTLPlugin( { From ddce9f731e398f8aff45d3dfea1f1f88ec2edc0c Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Mon, 11 Feb 2019 18:04:57 +0800 Subject: [PATCH 13/22] Update docs for feature flags. Move `Basic Use` section higher up, and simplify a sentence --- .../developers/feature-flags.md | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/designers-developers/developers/feature-flags.md b/docs/designers-developers/developers/feature-flags.md index ec8087a401862..47a28d0fa42c3 100644 --- a/docs/designers-developers/developers/feature-flags.md +++ b/docs/designers-developers/developers/feature-flags.md @@ -6,11 +6,34 @@ The technique for handling this is known as a 'feature flag'. ## Introducing `process.env.GUTENBERG_PHASE` -The `process.env.GUTENBERG_PHASE` is an environment variable containing a number that reprsents 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.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`. + +## Basic Use + +A phase 2 function or constant should be exported using the following ternary syntax: + +```js +function myPhaseTwoFeature() { + // implementation +} + +export const phaseTwoFeature = process.env.GUTENBERG_PHASE === 2 ? myPhaseTwoFeature : undefined; +``` + +In phase 1 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: +```js +import { phaseTwoFeature } from '@wordpress/foo'; + +if ( process.env.GUTENBERG_PHASE === 2) { + phaseTwoFeature(); +} +``` ### How it works -More precisely, the `GUTENBERG_PHASE` variable isn't actually set to a value. Instead, any instances of `process.env.GUTENBERG_PHASE` in the codebase will be replaced during the webpack build by webpack's define plugin (https://webpack.js.org/plugins/define-plugin/). +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/). If you write the following code: ```js @@ -62,29 +85,6 @@ if ( 1 === 2 ) { 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. -## Basic Use - -A phase 2 function or constant should be exported using the following ternary syntax: - -```js -function myPhaseTwoFeature() { - // implementation -} - -export const phaseTwoFeature = process.env.GUTENBERG_PHASE === 2 ? myPhaseTwoFeature : undefined; -``` - -In phase 1 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: -```js -import { phaseTwoFeature } from '@wordpress/foo'; - -if ( process.env.GUTENBERG_PHASE === 2) { - phaseTwoFeature(); -} -``` - ## FAQ #### Why should I only use `===` or `!==` when comparing `process.env.GUTENBERG_PHASE` and not `>`, `>=`, `<` or `<=`? From e91a027441fad9ffc3df6d747480af979b5d8567 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 12 Feb 2019 11:37:12 +0800 Subject: [PATCH 14/22] Ensure GUTENBERG_PHASE environment variable is available for webpack --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 03519007bc97c..ce95e8eac35ea 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "precheck-local-changes": "npm run docs:build", "check-local-changes": "( git diff -U0 | xargs -0 node bin/process-git-diff ) || ( echo \"There are local uncommitted changes after one or both of 'npm install' or 'npm run docs:build'!\" && exit 1 );", "predev": "npm run check-engines", - "dev": "cross-env GUTENBERG_PHASE=2 npm run build:packages && concurrently \"wp-scripts start\" \"npm run dev:packages\"", + "dev": "npm run build:packages && concurrently \"cross-env GUTENBERG_PHASE=2 wp-scripts start\" \"npm run dev:packages\"", "dev:packages": "node ./bin/packages/watch.js", "docs:build": "node docs/tool", "fixtures:clean": "rimraf \"test/integration/full-content/fixtures/*.+(json|serialized.html)\"", From 7edd5c5488c92e0a0dd0fe960de35b0ed4809fcd Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 12 Feb 2019 11:37:46 +0800 Subject: [PATCH 15/22] Ensure GUTENBERG_PHASE is a number --- webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index f53d21e6355ec..eb18103618645 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -109,7 +109,7 @@ const config = { new DefinePlugin( { // Inject the `GUTENBERG_PHASE` global, used for feature flagging. // eslint-disable-next-line @wordpress/gutenberg-phase - 'process.env.GUTENBERG_PHASE': JSON.stringify( process.env.GUTENBERG_PHASE || 1 ), + 'process.env.GUTENBERG_PHASE': JSON.stringify( parseInt( process.env.GUTENBERG_PHASE, 10 ) || 1 ), } ), // Create RTL files with a -rtl suffix new WebpackRTLPlugin( { From 6c93c7b7a16438dd4b74ecbb3235536d62e0bf7f Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 12 Feb 2019 12:59:53 +0800 Subject: [PATCH 16/22] Ensure GUTENBERG_PHASE is set in unit tests --- test/unit/jest.config.json | 3 ++- test/unit/setup/gutenberg-phase.js | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 test/unit/setup/gutenberg-phase.js diff --git a/test/unit/jest.config.json b/test/unit/jest.config.json index ed9e9ebc52774..1b28b4f1ee100 100644 --- a/test/unit/jest.config.json +++ b/test/unit/jest.config.json @@ -6,7 +6,8 @@ }, "preset": "@wordpress/jest-preset-default", "setupFiles": [ - "core-js/fn/symbol/async-iterator" + "core-js/fn/symbol/async-iterator", + "./test/unit/setup/gutenberg-phase.js" ], "testURL": "http://localhost", "testPathIgnorePatterns": [ diff --git a/test/unit/setup/gutenberg-phase.js b/test/unit/setup/gutenberg-phase.js new file mode 100644 index 0000000000000..363e7816e8718 --- /dev/null +++ b/test/unit/setup/gutenberg-phase.js @@ -0,0 +1,5 @@ +process.env = { + // eslint-disable-next-line @wordpress/gutenberg-phase + GUTENBERG_PHASE: 2, + ...process.env, +}; From 806cdb85139c4aa7a46f9c2b3d56007a03a21fe7 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 13 Feb 2019 13:52:20 +0800 Subject: [PATCH 17/22] Use in jest config --- test/unit/jest.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/jest.config.json b/test/unit/jest.config.json index 1b28b4f1ee100..3a8d066063f28 100644 --- a/test/unit/jest.config.json +++ b/test/unit/jest.config.json @@ -7,7 +7,7 @@ "preset": "@wordpress/jest-preset-default", "setupFiles": [ "core-js/fn/symbol/async-iterator", - "./test/unit/setup/gutenberg-phase.js" + "/test/unit/setup/gutenberg-phase.js" ], "testURL": "http://localhost", "testPathIgnorePatterns": [ From 235a770e1174c6748f8e14d963c0ae0847e609e5 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 14 Feb 2019 14:39:59 +0800 Subject: [PATCH 18/22] switch to using package.json config to define the value of GUTENBERG_PHASE --- package.json | 9 ++++++--- test/unit/setup/gutenberg-phase.js | 2 +- webpack.config.js | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index ce95e8eac35ea..3669fef6c58f9 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,9 @@ "WordPress", "editor" ], + "config": { + "GUTENBERG_PHASE": 2 + }, "dependencies": { "@wordpress/a11y": "file:packages/a11y", "@wordpress/annotations": "file:packages/annotations", @@ -155,7 +158,7 @@ "precheck-local-changes": "npm run docs:build", "check-local-changes": "( git diff -U0 | xargs -0 node bin/process-git-diff ) || ( echo \"There are local uncommitted changes after one or both of 'npm install' or 'npm run docs:build'!\" && exit 1 );", "predev": "npm run check-engines", - "dev": "npm run build:packages && concurrently \"cross-env GUTENBERG_PHASE=2 wp-scripts start\" \"npm run dev:packages\"", + "dev": "npm run build:packages && concurrently \"wp-scripts start\" \"npm run dev:packages\"", "dev:packages": "node ./bin/packages/watch.js", "docs:build": "node docs/tool", "fixtures:clean": "rimraf \"test/integration/full-content/fixtures/*.+(json|serialized.html)\"", @@ -169,7 +172,7 @@ "lint-pkg-json": "wp-scripts lint-pkg-json ./packages", "lint-css": "wp-scripts lint-style '**/*.scss'", "lint-css:fix": "npm run lint-css -- --fix", - "package-plugin": "cross-env GUTENBERG_PHASE=2 ./bin/build-plugin-zip.sh", + "package-plugin": "./bin/build-plugin-zip.sh", "postinstall": "npm run check-licenses && npm run build:packages", "pot-to-php": "./bin/pot-to-php.js", "precommit": "lint-staged", @@ -178,7 +181,7 @@ "publish:prod": "npm run build:packages && lerna publish", "test": "npm run lint && npm run test-unit", "pretest-e2e": "./bin/reset-e2e-tests.sh", - "test-e2e": "cross-env GUTENBERG_PHASE=2 wp-scripts test-e2e --config packages/e2e-tests/jest.config.js", + "test-e2e": "wp-scripts test-e2e --config packages/e2e-tests/jest.config.js", "test-e2e:watch": "npm run test-e2e -- --watch", "test-php": "npm run lint-php && npm run test-unit-php", "test-unit": "wp-scripts test-unit-js --config test/unit/jest.config.json", diff --git a/test/unit/setup/gutenberg-phase.js b/test/unit/setup/gutenberg-phase.js index 363e7816e8718..04db92a042a23 100644 --- a/test/unit/setup/gutenberg-phase.js +++ b/test/unit/setup/gutenberg-phase.js @@ -1,5 +1,5 @@ process.env = { // eslint-disable-next-line @wordpress/gutenberg-phase - GUTENBERG_PHASE: 2, + GUTENBERG_PHASE: parseInt( process.env.npm_package_config_GUTENBERG_PHASE, 10 ), ...process.env, }; diff --git a/webpack.config.js b/webpack.config.js index eb18103618645..998cabd9c9ace 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -109,7 +109,7 @@ const config = { 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.GUTENBERG_PHASE, 10 ) || 1 ), + 'process.env.GUTENBERG_PHASE': JSON.stringify( parseInt( process.env.npm_package_config_GUTENBERG_PHASE, 10 ) || 1 ), } ), // Create RTL files with a -rtl suffix new WebpackRTLPlugin( { From ddc3853294fef486ad853f427dea7505122869b5 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 15 Feb 2019 14:17:21 +0800 Subject: [PATCH 19/22] Sort custom lint rules alphabetically --- packages/eslint-plugin/README.md | 4 ++-- packages/eslint-plugin/configs/custom.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index f7d7171eeea9c..cd1ab687823cd 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -49,10 +49,10 @@ The granular rulesets will not define any environment globals. As such, if they Rule|Description ---|--- -[no-unused-vars-before-return](/packages/eslint-plugin/docs/rules/no-unused-vars-before-return.md)|Disallow assigning variable values if unused before a return [dependency-group](/packages/eslint-plugin/docs/rules/dependency-group.md)|Enforce dependencies docblocks formatting -[valid-sprintf](/packages/eslint-plugin/docs/rules/valid-sprintf.md)|Disallow assigning variable values if unused before a return [gutenberg-phase](docs/rules/gutenberg-phase.md)|Governs the use of the `process.env.GUTENBERG_PHASE` constant +[no-unused-vars-before-return](/packages/eslint-plugin/docs/rules/no-unused-vars-before-return.md)|Disallow assigning variable values if unused before a return +[valid-sprintf](/packages/eslint-plugin/docs/rules/valid-sprintf.md)|Disallow assigning variable values if unused before a return ### Legacy diff --git a/packages/eslint-plugin/configs/custom.js b/packages/eslint-plugin/configs/custom.js index 3439a1cec6feb..82ba39265eb43 100644 --- a/packages/eslint-plugin/configs/custom.js +++ b/packages/eslint-plugin/configs/custom.js @@ -4,9 +4,9 @@ module.exports = { ], rules: { '@wordpress/dependency-group': 'error', + '@wordpress/gutenberg-phase': 'error', '@wordpress/no-unused-vars-before-return': 'error', '@wordpress/valid-sprintf': 'error', - '@wordpress/gutenberg-phase': 'error', 'no-restricted-syntax': [ 'error', { From 94ffc010fcd0c36b5e2b824af0be5221ebcfcad6 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 15 Feb 2019 14:20:54 +0800 Subject: [PATCH 20/22] Add comment about GUTENBERG_PHASE --- test/unit/setup/gutenberg-phase.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/setup/gutenberg-phase.js b/test/unit/setup/gutenberg-phase.js index 04db92a042a23..ab9e383e86584 100644 --- a/test/unit/setup/gutenberg-phase.js +++ b/test/unit/setup/gutenberg-phase.js @@ -1,4 +1,5 @@ process.env = { + // Inject the `GUTENBERG_PHASE` global, used for feature flagging. // eslint-disable-next-line @wordpress/gutenberg-phase GUTENBERG_PHASE: parseInt( process.env.npm_package_config_GUTENBERG_PHASE, 10 ), ...process.env, From c78d6b4076be6e9ee5977af4e5d03c221fb90d9a Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 15 Feb 2019 14:38:05 +0800 Subject: [PATCH 21/22] Update jest config for GUTENBERG_PHASE --- .../setup => packages/e2e-tests/config}/gutenberg-phase.js | 4 ++-- packages/e2e-tests/jest.config.js | 3 +++ test/unit/config/gutenberg-phase.js | 6 ++++++ test/unit/jest.config.json | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) rename {test/unit/setup => packages/e2e-tests/config}/gutenberg-phase.js (81%) create mode 100644 test/unit/config/gutenberg-phase.js diff --git a/test/unit/setup/gutenberg-phase.js b/packages/e2e-tests/config/gutenberg-phase.js similarity index 81% rename from test/unit/setup/gutenberg-phase.js rename to packages/e2e-tests/config/gutenberg-phase.js index ab9e383e86584..1b6117b3236a6 100644 --- a/test/unit/setup/gutenberg-phase.js +++ b/packages/e2e-tests/config/gutenberg-phase.js @@ -1,6 +1,6 @@ -process.env = { +global.process.env = { + ...global.process.env, // Inject the `GUTENBERG_PHASE` global, used for feature flagging. // eslint-disable-next-line @wordpress/gutenberg-phase GUTENBERG_PHASE: parseInt( process.env.npm_package_config_GUTENBERG_PHASE, 10 ), - ...process.env, }; diff --git a/packages/e2e-tests/jest.config.js b/packages/e2e-tests/jest.config.js index 9984044fd0ccd..f241715197b97 100644 --- a/packages/e2e-tests/jest.config.js +++ b/packages/e2e-tests/jest.config.js @@ -1,4 +1,7 @@ module.exports = { ...require( '@wordpress/scripts/config/jest-e2e.config' ), setupTestFrameworkScriptFile: '/config/setup-test-framework.js', + setupFiles: [ + '/config/gutenberg-phase.js', + ], }; diff --git a/test/unit/config/gutenberg-phase.js b/test/unit/config/gutenberg-phase.js new file mode 100644 index 0000000000000..1b6117b3236a6 --- /dev/null +++ b/test/unit/config/gutenberg-phase.js @@ -0,0 +1,6 @@ +global.process.env = { + ...global.process.env, + // Inject the `GUTENBERG_PHASE` global, used for feature flagging. + // eslint-disable-next-line @wordpress/gutenberg-phase + GUTENBERG_PHASE: parseInt( process.env.npm_package_config_GUTENBERG_PHASE, 10 ), +}; diff --git a/test/unit/jest.config.json b/test/unit/jest.config.json index 3a8d066063f28..3b7d0ca732a99 100644 --- a/test/unit/jest.config.json +++ b/test/unit/jest.config.json @@ -7,7 +7,7 @@ "preset": "@wordpress/jest-preset-default", "setupFiles": [ "core-js/fn/symbol/async-iterator", - "/test/unit/setup/gutenberg-phase.js" + "/test/unit/config/gutenberg-phase.js" ], "testURL": "http://localhost", "testPathIgnorePatterns": [ From 57731f50cb4dc89d297df39b14766941ffe951c1 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 15 Feb 2019 15:23:49 +0800 Subject: [PATCH 22/22] Add webpack as dependency to main package.json --- package-lock.json | 723 ++++++++++++++++++++++++++++++++++++++++------ package.json | 1 + 2 files changed, 634 insertions(+), 90 deletions(-) diff --git a/package-lock.json b/package-lock.json index e59039690658a..69e25252f2a91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3177,9 +3177,9 @@ } }, "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", + "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", "dev": true }, "alphanum-sort": { @@ -4147,9 +4147,9 @@ "dev": true }, "binary-extensions": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", - "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", + "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==", "dev": true }, "bindings": { @@ -4761,26 +4761,573 @@ } }, "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.1.tgz", + "integrity": "sha512-gfw3p2oQV2wEt+8VuMlNsPjCxDxvvgnm/kz+uATu805mWVF8IJN7uz9DN7iBz+RMJISmiVbCOBFs9qBGMjtPfQ==", "dev": true, "requires": { "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", "glob-parent": "^3.1.0", - "inherits": "^2.0.1", + "inherits": "^2.0.3", "is-binary-path": "^1.0.0", "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", + "normalize-path": "^3.0.0", "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "readdirp": "^2.2.1", + "upath": "^1.1.0" }, "dependencies": { + "fsevents": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", + "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true + } + } + }, "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -4816,6 +5363,12 @@ "requires": { "is-extglob": "^2.1.1" } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true } } }, @@ -6772,9 +7325,9 @@ "dev": true }, "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", "dev": true, "requires": { "bn.js": "^4.4.0", @@ -7365,9 +7918,9 @@ } }, "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", "dev": true }, "evp_bytestokey": { @@ -9652,9 +10205,9 @@ } }, "hash.js": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", - "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -12908,9 +13461,9 @@ } }, "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", - "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", "dev": true }, "loader-utils": { @@ -12979,12 +13532,6 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, "lodash.escape": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", @@ -13401,13 +13948,14 @@ "dev": true }, "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, "requires": { "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, "mdast-util-compact": { @@ -14013,9 +14561,9 @@ "dev": true }, "node-libs-browser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", - "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", + "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", "dev": true, "requires": { "assert": "^1.1.1", @@ -14025,7 +14573,7 @@ "constants-browserify": "^1.0.0", "crypto-browserify": "^3.11.0", "domain-browser": "^1.1.1", - "events": "^1.0.0", + "events": "^3.0.0", "https-browserify": "^1.0.0", "os-browserify": "^0.3.0", "path-browserify": "0.0.0", @@ -14039,7 +14587,7 @@ "timers-browserify": "^2.0.4", "tty-browserify": "0.0.0", "url": "^0.11.0", - "util": "^0.10.3", + "util": "^0.11.0", "vm-browserify": "0.0.4" }, "dependencies": { @@ -15090,9 +15638,9 @@ } }, "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz", + "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==", "dev": true }, "parallel-transform": { @@ -15124,16 +15672,17 @@ } }, "parse-asn1": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", - "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", + "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", "dev": true, "requires": { "asn1.js": "^4.0.0", "browserify-aes": "^1.0.0", "create-hash": "^1.1.0", "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3" + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" } }, "parse-entities": { @@ -15299,9 +15848,9 @@ } }, "pbkdf2": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", - "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", "dev": true, "requires": { "create-hash": "^1.1.2", @@ -17278,16 +17827,17 @@ "dev": true }, "public-encrypt": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", - "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, "requires": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", "create-hash": "^1.1.0", "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1" + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" } }, "pump": { @@ -17835,15 +18385,14 @@ } }, "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" } }, "realpath-native": { @@ -18757,9 +19306,9 @@ } }, "schema-utils": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", - "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", "dev": true, "requires": { "ajv": "^6.1.0", @@ -18767,15 +19316,15 @@ }, "dependencies": { "ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", + "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" + "uri-js": "^4.2.2" } }, "fast-deep-equal": { @@ -18891,12 +19440,6 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -19453,9 +19996,9 @@ "dev": true }, "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", "dev": true, "requires": { "inherits": "~2.0.1", @@ -20594,9 +21137,9 @@ } }, "uglifyjs-webpack-plugin": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.7.tgz", - "integrity": "sha512-1VicfKhCYHLS8m1DCApqBhoulnASsEoJ/BvpUpP4zoNAPpKzdH+ghk0olGJMmwX2/jprK2j3hAHdUbczBSy2FA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", + "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", "dev": true, "requires": { "cacache": "^10.0.4", @@ -20944,9 +21487,9 @@ "dev": true }, "util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", "dev": true, "requires": { "inherits": "2.0.3" @@ -21245,9 +21788,9 @@ }, "dependencies": { "ajv": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.1.tgz", - "integrity": "sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", + "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", diff --git a/package.json b/package.json index 3669fef6c58f9..758213c258bea 100644 --- a/package.json +++ b/package.json @@ -116,6 +116,7 @@ "sprintf-js": "1.1.1", "stylelint-config-wordpress": "13.1.0", "uuid": "3.3.2", + "webpack": "4.8.3", "webpack-bundle-analyzer": "3.0.2", "webpack-livereload-plugin": "2.1.1", "webpack-rtl-plugin": "github:yoavf/webpack-rtl-plugin#develop"