From 78d2e9e2a894a7ea9aa3f9faadfc4c6038e86a75 Mon Sep 17 00:00:00 2001 From: Mark Erikson Date: Wed, 8 Feb 2023 20:00:22 -0500 Subject: [PATCH] Replace DevTools `semver` usages with `compare-versions` for smaller bundle size (#26122) ## Summary This PR: - Replaces the existing usages of methods from the `semver` library in the React DevTools source with an inlined version based on https://www.npmjs.com/package/semver-compare. This appears to drop the unminified bundle sizes of 3 separate `react-devtools-extensions` build artifacts by about 50K: ![image](https://user-images.githubusercontent.com/1128784/217326947-4c26d1be-d834-4f77-9e6e-be2d5ed0954d.png) ## How did you test this change? I was originally working on [a fork of React DevTools](https://github.com/replayio/react/pull/2) for use with https://replay.io , specifically our integration of the React DevTools UI to show the React component tree while users are debugging a recorded application. As part of the dev work on that fork, I wanted to shrink the bundle size of the extension's generated JS build artifacts. I noted that the official NPM `semver` library was taking up a noticeable chunk of space in the bundles, and saw that it's only being used in a handful of places to do some very simple version string comparisons. I was able to replace the `semver` imports and usages with a simple alternate comparison function, and confirmed via hands-on checks and console logging that the checks behaved the same way. Given that, I wanted to upstream this particular change to help shrink the real extension's bundle sizes. I know that it's an extension, so bundle size isn't _as_ critical a concern as it would be for a pure library. But, smaller download sizes do benefit all users, and that also includes sites like CodeSandbox and Replay that are using the React DevTools as a library as well. I'm happy to tweak this PR if necessary. Thanks! --- packages/react-devtools-shared/package.json | 4 ++-- .../src/__tests__/utils-test.js | 16 ++++++++++++++++ .../src/backend/renderer.js | 2 +- .../react-devtools-shared/src/backend/utils.js | 9 +++++++++ .../src/e2e-regression/app-legacy.js | 3 ++- yarn.lock | 5 +++++ 6 files changed, 35 insertions(+), 4 deletions(-) diff --git a/packages/react-devtools-shared/package.json b/packages/react-devtools-shared/package.json index 9d15ae7869..b03ce9ea74 100644 --- a/packages/react-devtools-shared/package.json +++ b/packages/react-devtools-shared/package.json @@ -18,11 +18,11 @@ "@reach/menu-button": "^0.16.1", "@reach/tooltip": "^0.16.0", "clipboard-js": "^0.3.6", + "compare-versions": "^5.0.3", "json5": "^2.1.3", "local-storage-fallback": "^4.1.1", "lodash.throttle": "^4.1.1", "memoize-one": "^3.1.1", - "react-virtualized-auto-sizer": "^1.0.6", - "semver": "^6.3.0" + "react-virtualized-auto-sizer": "^1.0.6" } } diff --git a/packages/react-devtools-shared/src/__tests__/utils-test.js b/packages/react-devtools-shared/src/__tests__/utils-test.js index ecb2cac33e..5cb94fc051 100644 --- a/packages/react-devtools-shared/src/__tests__/utils-test.js +++ b/packages/react-devtools-shared/src/__tests__/utils-test.js @@ -15,6 +15,8 @@ import {stackToComponentSources} from 'react-devtools-shared/src/devtools/utils' import { format, formatWithStyles, + gt, + gte, } from 'react-devtools-shared/src/backend/utils'; import { REACT_SUSPENSE_LIST_TYPE as SuspenseList, @@ -252,4 +254,18 @@ describe('utils', () => { ]); }); }); + + describe('semver comparisons', () => { + it('gte should compare versions correctly', () => { + expect(gte('1.2.3', '1.2.1')).toBe(true); + expect(gte('1.2.1', '1.2.1')).toBe(true); + expect(gte('1.2.1', '1.2.2')).toBe(false); + }); + + it('gt should compare versions correctly', () => { + expect(gt('1.2.3', '1.2.1')).toBe(true); + expect(gt('1.2.1', '1.2.1')).toBe(false); + expect(gt('1.2.1', '1.2.2')).toBe(false); + }); + }); }); diff --git a/packages/react-devtools-shared/src/backend/renderer.js b/packages/react-devtools-shared/src/backend/renderer.js index 1028e35726..cf2660fc69 100644 --- a/packages/react-devtools-shared/src/backend/renderer.js +++ b/packages/react-devtools-shared/src/backend/renderer.js @@ -7,7 +7,6 @@ * @flow */ -import {gt, gte} from 'semver'; import { ComponentFilterDisplayName, ComponentFilterElementType, @@ -39,6 +38,7 @@ import { utfEncodeString, } from 'react-devtools-shared/src/utils'; import {sessionStorageGetItem} from 'react-devtools-shared/src/storage'; +import {gt, gte} from 'react-devtools-shared/src/backend/utils'; import { cleanForBridge, copyToClipboard, diff --git a/packages/react-devtools-shared/src/backend/utils.js b/packages/react-devtools-shared/src/backend/utils.js index be5bfa56a8..7ae15e173d 100644 --- a/packages/react-devtools-shared/src/backend/utils.js +++ b/packages/react-devtools-shared/src/backend/utils.js @@ -9,6 +9,7 @@ */ import {copy} from 'clipboard-js'; +import {compareVersions} from 'compare-versions'; import {dehydrate} from '../hydration'; import isArray from 'shared/isArray'; @@ -275,3 +276,11 @@ export function isSynchronousXHRSupported(): boolean { window.document.featurePolicy.allowsFeature('sync-xhr') ); } + +export function gt(a: string = '', b: string = ''): boolean { + return compareVersions(a, b) === 1; +} + +export function gte(a: string = '', b: string = ''): boolean { + return compareVersions(a, b) > -1; +} diff --git a/packages/react-devtools-shell/src/e2e-regression/app-legacy.js b/packages/react-devtools-shell/src/e2e-regression/app-legacy.js index dedbed5e73..e8bb10fda0 100644 --- a/packages/react-devtools-shell/src/e2e-regression/app-legacy.js +++ b/packages/react-devtools-shell/src/e2e-regression/app-legacy.js @@ -4,9 +4,10 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import {gte} from 'semver'; import ListApp from '../e2e-apps/ListApp'; import ListAppLegacy from '../e2e-apps/ListAppLegacy'; +import {gte} from 'react-devtools-shared/src/backend/utils'; + const version = process.env.E2E_APP_REACT_VERSION; function mountApp(App: () => React$Node) { diff --git a/yarn.lock b/yarn.lock index 37be19fac6..966bfe2fa5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5458,6 +5458,11 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= +compare-versions@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-5.0.3.tgz#a9b34fea217472650ef4a2651d905f42c28ebfd7" + integrity sha512-4UZlZP8Z99MGEY+Ovg/uJxJuvoXuN4M6B3hKaiackiHrgzQFEe3diJi1mf1PNHbFujM7FvLrK2bpgIaImbtZ1A== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"