Skip to content

Better support for @wordpress/element Fragment JSX#34217

Merged
sirreal merged 6 commits intomasterfrom
update/calypso-build-wp-jsx
Aug 8, 2019
Merged

Better support for @wordpress/element Fragment JSX#34217
sirreal merged 6 commits intomasterfrom
update/calypso-build-wp-jsx

Conversation

@sirreal
Copy link
Copy Markdown
Member

@sirreal sirreal commented Jun 24, 2019

See WordPress/gutenberg#15120

Matches Gutenberg babel preset for JSX:
https://github.com/WordPress/gutenberg/blob/af6e5ecd8b398e08127ad1b3c784ce97899de100/packages/babel-preset-default/index.js#L58-L70

Provides better calypso-build support for <></>.

Testing instructions

  • Extensions and apps built with calypso-build should continue to work as before.

Provided that <></> syntax was basically unsupported by the previous setup, this should introduce few or no changes for consumers.

It will allow <Fragment></Fragment> to be replaced by <></>, leveraging the correct @wordpress/element Fragment.

@matticbot
Copy link
Copy Markdown
Contributor

@sirreal sirreal self-assigned this Jun 24, 2019
@sirreal sirreal added [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. Enhancement Changes to an existing feature — removing, adding, or changing parts of it Task Build labels Jun 24, 2019
@sirreal sirreal requested review from a team and ockham June 24, 2019 11:51
@matticbot
Copy link
Copy Markdown
Contributor

matticbot commented Jun 24, 2019

Here is how your PR affects size of JS and CSS bundles shipped to the user's browser:

Sections (~816 bytes added 📈 [gzipped])

Details
name                parsed_size           gzip_size
woocommerce              +513 B  (+0.0%)     +182 B  (+0.0%)
google-my-business       +513 B  (+0.2%)     +182 B  (+0.2%)
gutenberg-editor         +314 B  (+0.0%)     +112 B  (+0.1%)
themes                    +40 B  (+0.0%)      +17 B  (+0.0%)
stats                     +40 B  (+0.0%)      +17 B  (+0.0%)
settings-writing          +40 B  (+0.0%)      +17 B  (+0.0%)
security                  +40 B  (+0.0%)      +17 B  (+0.0%)
purchases                 +40 B  (+0.0%)      +17 B  (+0.0%)
posts-pages               +40 B  (+0.0%)      +17 B  (+0.0%)
posts-custom              +40 B  (+0.0%)      +17 B  (+0.0%)
post-editor               +40 B  (+0.0%)      +17 B  (+0.0%)
plans                     +40 B  (+0.0%)      +17 B  (+0.0%)
people                    +40 B  (+0.0%)      +17 B  (+0.0%)
login                     +40 B  (+0.0%)      +17 B  (+0.0%)
jetpack-connect           +40 B  (+0.0%)      +17 B  (+0.0%)
help                      +40 B  (+0.0%)      +17 B  (+0.0%)
export                    +40 B  (+0.0%)      +17 B  (+0.0%)
domains                   +40 B  (+0.0%)      +17 B  (+0.0%)
concierge                 +40 B  (+0.0%)      +17 B  (+0.0%)
comments                  +40 B  (+0.0%)      +17 B  (+0.0%)
checkout                  +40 B  (+0.0%)      +17 B  (+0.0%)
activity                  +40 B  (+0.0%)      +17 B  (+0.0%)
account                   +40 B  (+0.0%)      +17 B  (+0.0%)

Sections contain code specific for a given set of routes. Is downloaded and parsed only when a particular route is navigated to.

Async-loaded Components (~564 bytes added 📈 [gzipped])

Details
name                                        parsed_size           gzip_size
async-load-design-playground                     +617 B  (+0.0%)     +202 B  (+0.1%)
async-load-design                                +617 B  (+0.0%)     +202 B  (+0.0%)
async-load-design-blocks                         +150 B  (+0.0%)      +41 B  (+0.0%)
async-load-signup-steps-plans-atomic-store        +40 B  (+0.0%)      +17 B  (+0.1%)
async-load-signup-steps-plans                     +40 B  (+0.0%)      +17 B  (+0.0%)
async-load-signup-steps-domains                   +40 B  (+0.0%)      +17 B  (+0.0%)
async-load-signup-steps-clone-point               +40 B  (+0.0%)      +17 B  (+0.0%)
async-load-reader-search-stream                   +40 B  (+0.0%)      +17 B  (+0.1%)
async-load-reader-following-manage                +40 B  (+0.0%)      +17 B  (+0.0%)
async-load-blocks-inline-help-popover             +40 B  (+0.0%)      +17 B  (+0.0%)

React components that are loaded lazily, when a certain part of UI is displayed for the first time.

Legend

What is parsed and gzip size?

Parsed Size: Uncompressed size of the JS and CSS files. This much code needs to be parsed and stored in memory.
Gzip Size: Compressed size of the JS and CSS files. This much data needs to be downloaded over network.

Generated by performance advisor bot at iscalypsofastyet.com.

Copy link
Copy Markdown
Contributor

@ockham ockham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes a ton of sense 🚢

Do we need a Changelog entry for this?

@sirreal sirreal requested a review from a team June 24, 2019 17:03
@sirreal
Copy link
Copy Markdown
Member Author

sirreal commented Jun 24, 2019

A bit of detail, you can test this by replacing an app entry point with something including a <></> Fragment and building it (npx lerna run build --scope='*/o2-blocks').

diff --git a/apps/o2-blocks/src/editor.js b/apps/o2-blocks/src/editor.js
index 1cbc25a62b..b1ddef73ad 100644
--- a/apps/o2-blocks/src/editor.js
+++ b/apps/o2-blocks/src/editor.js
@@ -1,7 +1,3 @@
-/**
- * Internal dependencies
- */
-import './category';
-import './editor-notes/editor';
-import './prev-next/editor';
-import './todo/editor';
+function MyComponent() {
+	return <>👋 Howdy!</>;
+}

This results in a failed build on master transform-react-jsx: pragma has been set but pragmafrag has not been set.

Actually testing this with <></> led me to discover that the dependency responsible for this transformation was never updated so this wasn't actually working properly. I've updated the necessary dependency and added a changelog entry. Now when we try our test build we get the following output:

/***/ "./src/editor.js":
/*!***********************!*\
  !*** ./src/editor.js ***!
  \***********************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);


function MyComponent() {
  return Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["Fragment"], null, "\uD83D\uDC4B Howdy!");
}

/***/ }),

/***/ "@wordpress/element":
/*!*****************************!*\
  !*** external "wp.element" ***!
  \*****************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = wp.element;

/***/ })

Squint and you can see that createElement and Fragment are properties of wp.element.

Full output
(function(e, a) { for(var i in a) e[i] = a[i]; }(window, /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = "./src/editor.js");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./src/editor.js":
/*!***********************!*\
  !*** ./src/editor.js ***!
  \***********************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);


function MyComponent() {
  return Object(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["createElement"])(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__["Fragment"], null, "\uD83D\uDC4B Howdy!");
}

/***/ }),

/***/ "@wordpress/element":
/*!*****************************!*\
  !*** external "wp.element" ***!
  \*****************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = wp.element;

/***/ })

/******/ })));
//# sourceMappingURL=editor.js.map

@sirreal sirreal requested a review from ockham June 24, 2019 17:04
@ockham
Copy link
Copy Markdown
Contributor

ockham commented Jun 24, 2019

Thanks 😬 Sorry for not testing with </> myself earlier 😅

Tree-shaking seems to outsmart me, so I have to actually make webpack believe the file does something in order to repro on master:

diff --git a/apps/o2-blocks/src/editor.js b/apps/o2-blocks/src/editor.js
index 1cbc25a62b..e5fe650697 100644
--- a/apps/o2-blocks/src/editor.js
+++ b/apps/o2-blocks/src/editor.js
@@ -1,7 +1,3 @@
-/**
- * Internal dependencies
- */
-import './category';
-import './editor-notes/editor';
-import './prev-next/editor';
-import './todo/editor';
+export default function MyComponent() {
+       return <>👋 Howdy!</>;
+}

ockham
ockham previously approved these changes Jun 24, 2019
Copy link
Copy Markdown
Contributor

@ockham ockham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge once e2e tests pass +1

@sirreal
Copy link
Copy Markdown
Member Author

sirreal commented Jun 25, 2019

There may be some valid failures on e2e tests I'll need to take a look at.

@sirreal sirreal added [Status] In Progress and removed [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. labels Jun 25, 2019
@ockham ockham force-pushed the update/calypso-build-wp-jsx branch 2 times, most recently from d26aff0 to d6f9efa Compare July 5, 2019 06:37
@sirreal sirreal force-pushed the update/calypso-build-wp-jsx branch from d6f9efa to c174c1d Compare July 18, 2019 13:05
@sirreal sirreal requested a review from a team July 18, 2019 13:16
@sirreal
Copy link
Copy Markdown
Member Author

sirreal commented Jul 18, 2019

After a rebase and a fresh deps update, there aren't any issues with tests and I think we can move forward with this.

I'd appreciate reviews, especially from @Automattic/cylon @Automattic/serenity and @Automattic/ajax who are using the build tool in the monorepo.

@sirreal
Copy link
Copy Markdown
Member Author

sirreal commented Jul 18, 2019

There are indeed failing e2e tests when accessing the editor, apparently from process.env.FORCE_REDUCED_MOTION:

entry-main.7e542f228a4b55baac16.min.js:formatted:1180 ReferenceError: process is not defined
    at Object../node_modules/@wordpress/blocks/build-module/index.js (199.2cb57698dc1bf19debcc.min.js:formatted:1114)
    at n (simplepaymemts2m7cn2m7.wordpress.com?retry=1:34)
    at Module../client/gutenberg/editor/index.js (gutenberg-editor.c502fffddc3f0da7a380.min.js:1)
    at n (simplepaymemts2m7cn2m7.wordpress.com?retry=1:34)
    at /block-editor/post/async https:/hash-c174c1d5ea4a7dae21d7e8a61969f12884f50901.calypso.live/calypso/evergreen/entry-main.7e542f228a4b55baac16.min.js:1
    at /block-editor/post/async https:/hash-c174c1d5ea4a7dae21d7e8a61969f12884f50901.calypso.live/calypso/evergreen/entry-main.7e542f228a4b55baac16.min.js:1

A quick search suggests this is coming from unguarded process access in Gutenberg: https://github.com/WordPress/gutenberg/blob/eba2fc8ba1ffbd5997cf47b3aa7002f8a5597e9f/packages/compose/src/hooks/use-reduced-motion/index.js#L19

This could easily come from some of the @WordPress package updates in shrinkwrap, I'll need to dig around and see if issues have been reported and find out exactly what's going on.

@sirreal sirreal force-pushed the update/calypso-build-wp-jsx branch 2 times, most recently from def967c to df706bc Compare July 19, 2019 10:07
@sirreal
Copy link
Copy Markdown
Member Author

sirreal commented Jul 19, 2019

I believe failures in the editor e2e tests after running update-deps were caused by this issue:

WordPress/gutenberg#16679

We'll need to wait for that to be fixed and released before upgrading @wordpress/compose (cc: @Automattic/team-calypso / #32607)

@sirreal
Copy link
Copy Markdown
Member Author

sirreal commented Jul 19, 2019

I'm not excited about the fix in df706bc - we seem to have node_modules installed under packages, which means that from apps we can't resolve the dependency under packages/calypso-build, which is confusing. I didn't expect node_modules to be created under package directories 😕 .

npm dedupe may be a solution, but I'd like to fully understand the issue before settling on a fix.

@ockham
Copy link
Copy Markdown
Contributor

ockham commented Aug 6, 2019

Interesting that desktop e2e tests are passing now -- only mobile ones are failing...

Ooh, rebuilding seems to have fixed them 🎉

@sirreal sirreal added [Status] Needs Rebase and removed [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. [Status] Needs e2e Testing labels Aug 6, 2019
@sirreal sirreal force-pushed the update/calypso-build-wp-jsx branch from 7167f91 to dd0e8f1 Compare August 6, 2019 20:16
@sirreal
Copy link
Copy Markdown
Member Author

sirreal commented Aug 6, 2019

@sirreal sirreal added [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. and removed [Status] Needs Rebase labels Aug 6, 2019
Copy link
Copy Markdown
Contributor

@ockham ockham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be working fine now! :shipit:

(I might've noticed that build time has gone up 😕 )

@sirreal
Copy link
Copy Markdown
Member Author

sirreal commented Aug 8, 2019

@ockham
Copy link
Copy Markdown
Contributor

ockham commented Aug 8, 2019

@sirreal FWIW, I've stopped using the 'Needs e2e Testing' label to retrigger e2e tests if just one of them is failing. Instead, I rebuild manually from within CircleCI's admin dashboard.

@sirreal sirreal added [Status] Ready to Merge Packages and removed [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. [Status] Needs e2e Testing labels Aug 8, 2019
@sirreal sirreal merged commit 01da7be into master Aug 8, 2019
@sirreal sirreal deleted the update/calypso-build-wp-jsx branch August 8, 2019 10:38
getdave pushed a commit that referenced this pull request Aug 13, 2019
* Better support for @wordpress/element Fragment JSX

See WordPress/gutenberg#15120

* Upgrade dependencies

* Add CHANGELOG entry

* Add DefinePlugin FORCE_REDUCED_MOTION to webpack configs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Build Enhancement Changes to an existing feature — removing, adding, or changing parts of it Packages Task

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants