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

Provide esm entry point for react-is #13321

Closed
wants to merge 10 commits into from
Closed

Provide esm entry point for react-is #13321

wants to merge 10 commits into from

Conversation

TrySound
Copy link
Contributor

@TrySound TrySound commented Aug 3, 2018

Refs
#13272 (comment)
reactjs/rfcs#38

In this PR I modified build script to generate two more bundles
react-is.development.mjs and react-is.production.min.mjs and
entry point index.mjs which reexports all named values depending on
production/development environments like this

import * as dev from './esm/development.mjs'
import * as prod from './esm/production.min.mjs'

export var name =
  process.env.NODE_ENV !== 'production' ? dev.name : prod.name;

I replaced closure wrapper with vendor plugin which is able to process
es modules by replacing them before and returning them after closure
minification.

To prove treeshakability of dual entry point we can generate rollup
bundle with import {} from 'package'. If nothing will left then
user bundler is able to eliminate unused code from dual entry point.

@pull-bot
Copy link

pull-bot commented Aug 3, 2018

React: size: -0.5%, gzip: -0.1%

ReactDOM: size: 0.0%, gzip: 0.0%

Details of bundled changes.

Comparing: 212437e...e2dae11

react

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react.development.js +3.0% -1.3% 58.04 KB 59.77 KB 16.21 KB 16 KB UMD_DEV
react.production.min.js -0.5% -0.1% 7.01 KB 6.98 KB 2.96 KB 2.96 KB UMD_PROD
react.development.js -2.0% -3.0% 52.23 KB 51.17 KB 14.39 KB 13.95 KB NODE_DEV
react.production.min.js -0.6% -0.5% 6.02 KB 5.98 KB 2.57 KB 2.55 KB NODE_PROD
React-dev.js -1.1% -1.4% 50.06 KB 49.5 KB 13.8 KB 13.61 KB FB_WWW_DEV

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.development.js -0.7% -4.4% 643.97 KB 639.77 KB 151.3 KB 144.61 KB UMD_DEV
react-dom.production.min.js 0.0% 0.0% 96.37 KB 96.37 KB 31.23 KB 31.25 KB UMD_PROD
react-dom.development.js -5.4% -5.4% 640.11 KB 605.74 KB 150.12 KB 141.94 KB NODE_DEV
react-dom.production.min.js 0.0% 0.0% 96.36 KB 96.37 KB 30.77 KB 30.78 KB NODE_PROD
react-dom-test-utils.development.js +2.6% -2.2% 43.74 KB 44.9 KB 11.9 KB 11.64 KB UMD_DEV
react-dom-test-utils.production.min.js 🔺+0.1% 🔺+0.3% 10.06 KB 10.07 KB 3.72 KB 3.73 KB UMD_PROD
react-dom-test-utils.development.js -1.9% -3.1% 43.46 KB 42.64 KB 11.84 KB 11.47 KB NODE_DEV
react-dom-test-utils.production.min.js 🔺+0.2% 🔺+0.3% 9.85 KB 9.87 KB 3.66 KB 3.67 KB NODE_PROD
react-dom-unstable-native-dependencies.development.js -0.4% -5.2% 58.81 KB 58.57 KB 15.4 KB 14.59 KB UMD_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% 🔺+0.1% 11.04 KB 11.04 KB 3.8 KB 3.8 KB UMD_PROD
react-dom-unstable-native-dependencies.development.js -4.9% -6.2% 58.48 KB 55.59 KB 15.27 KB 14.33 KB NODE_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% -0.0% 10.86 KB 10.86 KB 3.73 KB 3.73 KB NODE_PROD
react-dom-server.browser.development.js +0.8% -3.1% 102.43 KB 103.26 KB 27.3 KB 26.44 KB UMD_DEV
react-dom-server.browser.development.js -4.2% -4.5% 98.56 KB 94.4 KB 26.35 KB 25.17 KB NODE_DEV
react-dom-server.browser.production.min.js 0.0% -0.0% 15.21 KB 15.21 KB 5.78 KB 5.78 KB NODE_PROD
react-dom-server.node.development.js -4.1% -4.4% 100.48 KB 96.33 KB 26.88 KB 25.7 KB NODE_DEV
react-dom-server.node.production.min.js 0.0% -0.0% 16.02 KB 16.02 KB 6.08 KB 6.08 KB NODE_PROD
ReactDOM-dev.js -2.4% -2.3% 647.35 KB 631.52 KB 148.44 KB 145.04 KB FB_WWW_DEV
ReactDOM-prod.js -0.0% -0.0% 278.39 KB 278.37 KB 52.24 KB 52.23 KB FB_WWW_PROD
ReactTestUtils-dev.js -2.1% -3.2% 40.66 KB 39.8 KB 10.92 KB 10.57 KB FB_WWW_DEV
ReactDOMUnstableNativeDependencies-dev.js -5.2% -6.7% 56.24 KB 53.31 KB 14.23 KB 13.28 KB FB_WWW_DEV
ReactDOMServer-dev.js -1.7% -2.4% 99.73 KB 97.99 KB 26.07 KB 25.45 KB FB_WWW_DEV
ReactDOMServer-prod.js -0.0% 0.0% 33.08 KB 33.07 KB 7.98 KB 7.98 KB FB_WWW_PROD
react-dom.profiling.min.js 0.0% 0.0% 97.49 KB 97.51 KB 31.17 KB 31.17 KB NODE_PROFILING
ReactDOM-profiling.js -0.0% -0.0% 281.68 KB 281.64 KB 52.97 KB 52.96 KB FB_WWW_PROFILING

react-art

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-art.development.js -3.0% -6.8% 435.7 KB 422.72 KB 98.76 KB 92.04 KB UMD_DEV
react-art.production.min.js -0.0% -0.1% 85.19 KB 85.17 KB 26.43 KB 26.41 KB UMD_PROD
react-art.development.js -9.0% -9.3% 368.22 KB 334.9 KB 81.68 KB 74.06 KB NODE_DEV
react-art.production.min.js -0.0% -0.0% 50.18 KB 50.17 KB 15.8 KB 15.8 KB NODE_PROD
ReactART-dev.js -4.0% -3.7% 358.73 KB 344.46 KB 76.55 KB 73.71 KB FB_WWW_DEV
ReactART-prod.js -0.0% 0.0% 153 KB 152.95 KB 26.37 KB 26.38 KB FB_WWW_PROD

react-test-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-test-renderer.development.js -8.2% -11.9% 364.12 KB 334.16 KB 80.09 KB 70.52 KB UMD_DEV
react-test-renderer.production.min.js -0.0% 0.0% 49.05 KB 49.03 KB 15.16 KB 15.17 KB UMD_PROD
react-test-renderer.development.js -12.6% -13.0% 360.25 KB 315 KB 79.11 KB 68.79 KB NODE_DEV
react-test-renderer.production.min.js -0.0% -0.0% 48.75 KB 48.74 KB 15 KB 15 KB NODE_PROD
react-test-renderer-shallow.development.js +3.6% +0.5% 23.23 KB 24.06 KB 6.29 KB 6.32 KB UMD_DEV
react-test-renderer-shallow.development.js -0.3% -0.5% 18.42 KB 18.37 KB 5.11 KB 5.08 KB NODE_DEV
ReactTestRenderer-dev.js -8.2% -8.1% 365.39 KB 335.44 KB 78.04 KB 71.73 KB FB_WWW_DEV
ReactShallowRenderer-dev.js -0.4% -0.6% 16.91 KB 16.85 KB 4.42 KB 4.4 KB FB_WWW_DEV

react-reconciler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-reconciler.development.js -7.1% -8.2% 349.24 KB 324.46 KB 75.71 KB 69.48 KB NODE_DEV
react-reconciler.production.min.js -0.0% -0.1% 47.73 KB 47.71 KB 14.47 KB 14.46 KB NODE_PROD
react-reconciler-persistent.development.js -7.0% -7.7% 347.85 KB 323.67 KB 75.16 KB 69.36 KB NODE_DEV
react-reconciler-persistent.production.min.js -0.0% -0.1% 47.74 KB 47.73 KB 14.48 KB 14.47 KB NODE_PROD
react-reconciler-reflection.development.js -2.6% -4.1% 14.24 KB 13.86 KB 4.47 KB 4.29 KB NODE_DEV

react-native-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactNativeRenderer-dev.js -3.2% -3.0% 483.56 KB 467.96 KB 106.9 KB 103.71 KB RN_FB_DEV
ReactNativeRenderer-prod.js -0.1% -0.0% 214.24 KB 214.11 KB 37.54 KB 37.53 KB RN_FB_PROD
ReactNativeRenderer-dev.js -6.9% -6.9% 483.29 KB 449.96 KB 106.84 KB 99.48 KB RN_OSS_DEV
ReactNativeRenderer-prod.js -0.1% -0.0% 204.29 KB 204.16 KB 35.93 KB 35.92 KB RN_OSS_PROD
ReactFabric-dev.js -6.8% -6.8% 473.77 KB 441.56 KB 104.46 KB 97.34 KB RN_FB_DEV
ReactFabric-prod.js -0.0% -0.0% 195.4 KB 195.35 KB 34.36 KB 34.35 KB RN_FB_PROD
ReactFabric-dev.js -6.8% -6.8% 473.81 KB 441.52 KB 104.48 KB 97.34 KB RN_OSS_DEV
ReactFabric-prod.js -0.0% -0.0% 195.44 KB 195.39 KB 34.38 KB 34.37 KB RN_OSS_PROD
ReactNativeRenderer-profiling.js -0.0% -0.0% 207.8 KB 207.72 KB 36.63 KB 36.62 KB RN_OSS_PROFILING
ReactFabric-profiling.js -0.0% -0.0% 198.46 KB 198.38 KB 35 KB 35 KB RN_OSS_PROFILING
ReactNativeRenderer-profiling.js -0.0% -0.0% 217.71 KB 217.64 KB 38.26 KB 38.25 KB RN_FB_PROFILING
ReactFabric-profiling.js -0.0% -0.0% 198.42 KB 198.34 KB 34.98 KB 34.98 KB RN_FB_PROFILING

react-scheduler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-scheduler.development.js -1.6% -7.2% 15.39 KB 15.15 KB 4.64 KB 4.31 KB UMD_DEV
react-scheduler.development.js -6.0% -7.8% 15.2 KB 14.29 KB 4.6 KB 4.24 KB NODE_DEV
ReactScheduler-dev.js -5.9% -7.7% 15.42 KB 14.51 KB 4.64 KB 4.28 KB FB_WWW_DEV

react-is

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-is.development.js +4.4% +0.5% 4.68 KB 4.89 KB 1.29 KB 1.3 KB UMD_DEV
react-is.development.mjs n/a n/a 0 B 3.92 KB 0 B 1.09 KB ESM_DEV
react-is.production.min.mjs n/a n/a 0 B 3.33 KB 0 B 1.16 KB ESM_PROD

Generated by 🚫 dangerJS

@gaearon
Copy link
Collaborator

gaearon commented Aug 3, 2018

Does this mjs work on Node?

@TrySound
Copy link
Contributor Author

TrySound commented Aug 3, 2018

Yes, I just tried

@gaearon
Copy link
Collaborator

gaearon commented Aug 3, 2018

Can we test it on CI somehow?

@TrySound
Copy link
Contributor Author

TrySound commented Aug 3, 2018

I can try to setup esm, but i'm not sure it works with jest already.

@gaearon
Copy link
Collaborator

gaearon commented Aug 3, 2018

Hmm, no I mean real Node (experimental flag). We don’t want to be in a situation where it works in esm but fails in real Node.

It doesn’t need to be in Jest. I was thinking a standalone script that acts as a smoke test.

@TrySound
Copy link
Contributor Author

TrySound commented Aug 3, 2018

That's easier. Can we change node version in nvm to 10?

@gaearon
Copy link
Collaborator

gaearon commented Aug 3, 2018

Sure.

@TrySound
Copy link
Contributor Author

TrySound commented Aug 3, 2018

Do you want me to add treeshakability stat?

@TrySound
Copy link
Contributor Author

TrySound commented Aug 5, 2018

image

@gaearon
Copy link
Collaborator

gaearon commented Aug 6, 2018

Fix lint please?

@TrySound
Copy link
Contributor Author

TrySound commented Aug 6, 2018

Done

@TrySound
Copy link
Contributor Author

TrySound commented Aug 7, 2018

Rebased

@gaearon
Copy link
Collaborator

gaearon commented Sep 1, 2018

Do you mind rebasing again? Sorry.

This is a significant change and I'll need to look at it in more detail.

@gaearon gaearon self-assigned this Sep 1, 2018
@TrySound
Copy link
Contributor Author

TrySound commented Sep 1, 2018

Sure. We need to solve #13356 first though. I just blindly removed legacy option.

Refs
  #13272 (comment)
  reactjs/rfcs#38

In this PR I modified build script to generate two more bundles
`react-is.development.mjs` and `react-is.production.min.mjs` and
entry point `index.mjs` which reexports all named values depending on
production/development environments like this

```
import * as dev from './esm/development.mjs'
import * as prod from './esm/production.min.mjs'

export var name =
  process.env.NODE_ENV !== 'production' ? dev.name : prod.name;
```

I replaced closure wrapper with vendor plugin which is able to process
es modules by replacing them before and returning them after closure
minification.

To prove treeshakability of dual entry point we can generate rollup
bundle with `import {} from 'package'`. If nothing will left then
user bundler is able to eliminate unused code from dual entry point.
@Andarist
Copy link
Contributor

@TrySound If I've checked this correctly the production ESM bundle has quite some bloat - looks like some closure compiler stuff or similar.

@TrySound
Copy link
Contributor Author

@Andarist Yes, that's why I changed treeshakability detection to only development bundle. This will be a bloat only for dev environment. Production will use only what is expected.

@Andarist
Copy link
Contributor

How? I don't quite get it - from what I remember from looking through this yesterday you create a "proxy" entry which reexports dev or prod according to NODE_ENV. How production build will use only what is expected if it has to load both dev & prod upfront (due to static nature of ESM) and even without that it will reexport prod build which has this bloat in it?

@TrySound
Copy link
Contributor Author

@Andarist Dev bundle will be treeshaked out in production builds
https://github.com/facebook/react/pull/13321/files#diff-7a4b1c45d0fef2bb9fa773a2942c4080R788

This pattern is treeshakable too. And since it's generated it will always work. The only requirement is treeshakability of its dependencies. In this case at least dev should be treeshakable to prevent production builds bloat.

import * as prod from './prod.js';
import * as dev from './dev.js';
export const A = process.env.NODE_ENV !== 'production' ? dev.A : prod.A;

@Andarist
Copy link
Contributor

I understand how dev bundle can be tree-shaken, I'm talking specifically about prod bundle itself. It has been bloated and thus this might prevent tree-shaking of those bloated parts of the production bundle

@TrySound
Copy link
Contributor Author

I don't think anybody is going to achieve treeshakability in react packages. They are quite atomic.

@Andarist
Copy link
Contributor

Andarist commented Sep 11, 2018

I don't think anybody is going to achieve treeshakability in react packages. They are quite atomic.

That's generally true, but I think it's also less true for react-is. Either way I'm not even talking about general tree-shakeability here, but rather that there is a bloat in the esm production build of react-is, this code is not react-is', but rather something added by closure-compiler or similar. Just look at the beginning of this:

react-is.production.min.mjs var b=b||{};b.scope={};b.ASSUME_ES5=!1;b.ASSUME_NO_NATIVE_MAP=!1;b.ASSUME_NO_NATIVE_SET=!1;b.defineProperty=b.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,c,r){a!=Array.prototype&&a!=Object.prototype&&(a[c]=r.value)};b.getGlobal=function(a){return"undefined"!=typeof window&&window===a?a:"undefined"!=typeof global&&null!=global?global:a};b.global=b.getGlobal(this);b.SYMBOL_PREFIX="jscomp_symbol_"; b.initSymbol=function(){b.initSymbol=function(){};b.global.Symbol||(b.global.Symbol=b.Symbol)};b.Symbol=function(){var a=0;return function(c){return b.SYMBOL_PREFIX+(c||"")+a++}}();b.initSymbolIterator=function(){b.initSymbol();var a=b.global.Symbol.iterator;a||(a=b.global.Symbol.iterator=b.global.Symbol("iterator"));"function"!=typeof Array.prototype[a]&&b.defineProperty(Array.prototype,a,{configurable:!0,writable:!0,value:function(){return b.arrayIterator(this)}});b.initSymbolIterator=function(){}}; b.arrayIterator=function(a){var c=0;return b.iteratorPrototype(function(){return c< a.length?{done:!1,value:a[c++]}:{done:!0}})};b.iteratorPrototype=function(a){b.initSymbolIterator();a={next:a};a[b.global.Symbol.iterator]=function(){return this};return a};b.initSymbol();b.initSymbol();var d="function"===typeof Symbol&&Symbol.for;b.initSymbol();var e=d?Symbol.for("react.element"):60103;b.initSymbol();var f=d?Symbol.for("react.portal"):60106;b.initSymbol();var g=d?Symbol.for("react.fragment"):60107; b.initSymbol();var h=d?Symbol.for("react.strict_mode"):60108;b.initSymbol();var k=d?Symbol.for("react.profiler"):60114;b.initSymbol();var l=d?Symbol.for("react.provider"):60109;b.initSymbol();var m=d?Symbol.for("react.context"):60110;b.initSymbol();var n=d?Symbol.for("react.async_mode"):60111;b.initSymbol();var p=d?Symbol.for("react.forward_ref"):60112;b.initSymbol();var q=d?Symbol.for("react.placeholder"):60113; function t(a){if("object"===typeof a&&null!==a){var c=a.$$typeof;switch(c){case e:switch(a=a.type,a){case n:case g:case k:case h:return a;default:switch(a=a&&a.$$typeof,a){case m:case p:case l:return a;default:return c}}case f:return c}}}var typeOf=t;var AsyncMode=n;var ContextConsumer=m;var ContextProvider=l;var Element=e;var ForwardRef=p;var Fragment=g;var Profiler=k;var Portal=f;var StrictMode=h; var isValidElementType=function(a){return"string"===typeof a||"function"===typeof a||a===g||a===n||a===k||a===h||a===q||"object"===typeof a&&null!==a&&("function"===typeof a.then||a.$$typeof===l||a.$$typeof===m||a.$$typeof===p)};var isAsyncMode=function(a){return t(a)===n};var isContextConsumer=function(a){return t(a)===m};var isContextProvider=function(a){return t(a)===l};var isElement=function(a){return"object"===typeof a&&null!==a&&a.$$typeof===e}; var isForwardRef=function(a){return t(a)===p};var isFragment=function(a){return t(a)===g};var isProfiler=function(a){return t(a)===k};var isPortal=function(a){return t(a)===f};var isStrictMode=function(a){return t(a)===h};export{typeOf,AsyncMode,ContextConsumer,ContextProvider,Element,ForwardRef,Fragment,Profiler,Portal,StrictMode,isValidElementType,isAsyncMode,isContextConsumer,isContextProvider,isElement,isForwardRef,isFragment,isProfiler,isPortal,isStrictMode};

I have deleted the bloat manually to estimate (roughly ofc) how does it cost and it's 1067 vs 630 bytes (gzipped), so the bloat "costs" 437 bytes which is 41% of the distributed file.

@TrySound
Copy link
Contributor Author

Well, esm doesn't solve this like it wasn't solved before with cjs. I think this is the question for another thread.

@Andarist
Copy link
Contributor

Equivalent cjs production build has no such bloat, it seems to me this is issue with current setup - not sure if we are on the same page here, maybe we both have some information in our heads that make it harder to come to a common ground for both of us. I'll ping u on twitter, we'll be able to discuss it much quicker than in here.

Side note - cjs production build weighs 677 bytes (gzipped), so it's much more near to my estimate of what esm production build should weight too.

@kentcdodds
Copy link

Cool! Any chance we could do this for all of React's packages?

@TrySound
Copy link
Contributor Author

TrySound commented Oct 9, 2018

@kentcdodds Sure, but we need to decide at least on one package first.

@Andarist
Copy link
Contributor

@TrySound @gaearon could we move this along? is there any blocker for this one? Is there any interest in this from the React team?

I can help to rebase this and in general finish it if @TrySound doesn't have time right now.

@TrySound
Copy link
Contributor Author

@Andarist This is the blocker.
#15037

@frehner
Copy link

frehner commented Feb 21, 2020

The blocker was just merged! :)

@Andarist
Copy link
Contributor

Im super glad to hear that! @TrySound will you want to continue work on this one?

@stale
Copy link

stale bot commented May 22, 2020

This pull request has been automatically marked as stale. If this pull request is still relevant, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize reviewing it yet. Your contribution is very much appreciated.

@stale stale bot added the Resolution: Stale Automatically closed due to inactivity label May 22, 2020
@stale
Copy link

stale bot commented May 30, 2020

Closing this pull request after a prolonged period of inactivity. If this issue is still present in the latest release, please ask for this pull request to be reopened. Thank you!

@stale stale bot closed this May 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed Resolution: Stale Automatically closed due to inactivity
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants