Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better support for @wordpress/element Fragment JSX #34217

Merged
merged 6 commits into from
Aug 8, 2019

Conversation

sirreal
Copy link
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
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. [Type] Enhancement Changes to an existing feature — removing, adding, or changing parts of it [Type] Task Build labels Jun 24, 2019
@sirreal sirreal requested review from ockham and a team June 24, 2019 11:51
@matticbot
Copy link
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])

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])

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
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 as a code owner June 24, 2019 17:03
@sirreal
Copy link
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
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
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
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
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
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
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
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
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
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
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
Member Author

sirreal commented Aug 8, 2019

@ockham
Copy link
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 Packages [Type] Enhancement Changes to an existing feature — removing, adding, or changing parts of it [Type] Task
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants