Skip to content

Commit

Permalink
Migrate build tooling to Vite
Browse files Browse the repository at this point in the history
`jaeger-ui` currently uses build tooling based on `react-scripts` (née
CRA) 2.1.3, which dates back to early 2019. Since `react-scripts` is an
opinionated wrapper around other tooling such as `jest` and `webpack`,
it in turn keeps those locked on outdated versions.

There are a few options I considered here:

* **Do nothing** - ignore the outdated `react-scripts` and try to
  upgrade other things without touching it. This is what I tried first,
  when I tested out migrating some tests to `@testing-library` from
  `enzyme`. Unfortunately, I found that the outdated `jest` / `jsdom`
  combo precluded effective use of that library as some events and APIs
  were simply not available.
* **Try to update `react-scripts`**. Unfortunately, there is a problem
  here - although `react-scripts` is intended to be a zero-configuration
  wrapper around CRA tooling, the project attempts to modify its
  settings via two different packages (`react-scripts-rewired` and
  `customize-cra`) that power a custom buildscript which attempts to
  manipulate the webpack configs normally abstracted away by
  `react-scripts`. Given that all this hassle is done only to perform
  some mundane tasks like integrating with AntD's Less styles, or making
  the build step respect the project's ESLint config, I am not sure if
  staying with `react-scripts` and trying to customize it with a bag of
  tricks is a sustainable approach.
* **Eject the `react-scripts` and work with that**. `react-scripts` has
  an escape hatch that dumps the build tooling it uses internally in the
  project, should you decide that you want to roll your own build. The
  problem I saw with this is that this simply leaves us with a bunch of
  messy webpack config files that we then need to maintain going
  forward.
* **Migrate to a different build tool**. There are some promising tools
  out there that try to strike a reasonable balance between ease of
  configuration and use and extensibility, such as Vite and Parcel.
  I ended up settling on this approach after exhausting the above
  options, using Vite as the bundler of choice. I also gave Parcel a try
  but unfortunately I ran into issues trying to get it to transpile the
  AntD Less stylesheet, so I ended up not exploring it further for now.

So, per the above points, this PR reconfigures the build step to use
Vite rather than `react-scripts`.

There are a few caveats I ran into:
* Vite by default doesn't interpret JSX tags in .js files. This proved
  surprisingly difficult to override, so I renamed the few affected
  files to `.tsx` (if migrating didn't require sweeping changes)
  or `.jsx` otherwise.
* Consuming the plexus build resulted in some errors due to ambiguous
  exports, so I reconfigured it to output ESM rather than UMD.
* The abandoned `react-dimensions` package, used to automatically
  size/resize the scatterplot diagram on the trace search page, threw
  an error at runtime due to gratuitous use of `this`. I replaced it
  with a simple hooks-based implementation.
* The `rc-menu` component used internally by AntD would crash the entire
  page due to it merrily using `require()` at runtime and assuming it
  would be present in the environment. I ended up just providing a basic
  `require()` stub, since it was only using it to load a
  `MutationObserver` polyfill. Fortunately, the problematic code was
  removed in newer versions of the component.
* The customized `react-scripts` setup used a Babel plugin to
  automatically import styles whenever an AntD React component was
  imported. This also seems doable with Vite; for simplicity and to
  reduce the number of build deps, I haven't configured it though.
  If the bundle size impact is considered prohibitive, this can be
  revisited.

Overall, I am cautiously optimistic about Vite - the developer
experience seems significantly better as the HMR server is notably
snappier, at least on my machine. Most of the above issues stemmed from
the fact that many dependencies are outdated (and therefore more likely
to contain code that doesn't play that well with present tooling). This
change also allowed me to migrate the test suite from Jest v23 to Jest
v28 with minimal changes. Jest v29 unfortunately doesn't yet work as it
doesn't play well with the old TypeScript version currently used by the
project.

It may be worth noting that the `react-scripts` build step would also
run ESLint at build time. This is doable with Vite as well, I'm just not
sure if it's worth it, since the linter is already executed separately
in continuous integration. It therefore may not be necessary to have it
slow down the build.

Signed-off-by: Máté Szabó <mszabo@fandom.com>
  • Loading branch information
mszabo-wikia committed Feb 25, 2023
1 parent adf7e8e commit 1ba2345
Show file tree
Hide file tree
Showing 43 changed files with 1,803 additions and 6,217 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module.exports = {
settings: {
'import/resolver': {
node: {
extensions: ['.js', 'json', '.tsx'],
extensions: ['.js', '.jsx', 'json', '.tsx'],
},
},
},
Expand Down
7 changes: 1 addition & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,7 @@
"packages/*"
],
"nohoist": [
"**/customize-cra",
"**/customize-cra/**",
"**/react-scripts",
"**/react-scripts/**",
"**/react-app-rewired",
"**/react-app-rewired/**"
"**/parse5"
]
},
"scripts": {
Expand Down
7 changes: 7 additions & 0 deletions packages/jaeger-ui/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
globals: {
__REACT_APP_GA_DEBUG__: false,
__REACT_APP_VSN_STATE__: false,
__APP_ENVIRONMENT__: false,
},
};
24 changes: 0 additions & 24 deletions packages/jaeger-ui/config-overrides-antd-vars.less

This file was deleted.

73 changes: 0 additions & 73 deletions packages/jaeger-ui/config-overrides.js

This file was deleted.

File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<!-- NOTE: The document MUST have a <base> element. package.json#homepage is set to "." as part of resolving https://github.com/jaegertracing/jaeger-ui/issues/42 and therefore static assets are linked via relative URLs. This will break on many document URLs, e.g. /trace/abc, unless a valid base URL is provided. The base href defaults to "/" but the query-service can inject an override. -->
<base href="/" data-inject-target="BASE_URL" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link rel="shortcut icon" href="/favicon.ico">
<title>Jaeger UI</title>
<script>
// Jaeger UI config data is embedded by the query-service via search-replace.
Expand All @@ -38,15 +38,18 @@
</head>
<body>
<div id="jaeger-ui-root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` in this folder.
To create a production bundle, use `npm run build`.
-->
<script>
// Workaround some legacy NPM dependencies that assume this is always defined.
window.global = {};
// Avoid noise from redux-form until https://github.com/redux-form/redux-form/pull/4723 is released.
window.module = {};
// Prevent antd's rc-menu from breaking the page.
window.require = function (module) {
if (module !== 'mutationobserver-shim') {
console.error('attempted to fetch module ' + module + ' via unsupported require() call')
}
};
</script>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>
77 changes: 42 additions & 35 deletions packages/jaeger-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,42 @@
"private": true,
"name": "jaeger-ui",
"version": "1.27.3",
"main": "src/index.js",
"main": "src/index.tsx",
"license": "Apache-2.0",
"homepage": ".",
"workspaces": {
"nohoist": [
"customize-cra",
"customize-cra/**",
"react-scripts",
"react-scripts/**",
"react-app-rewired",
"react-app-rewired/**"
]
},
"repository": {
"type": "git",
"url": "https://github.com/jaegertracing/jaeger-ui.git",
"directory": "packages/jaeger-ui"
},
"devDependencies": {
"@babel/core": "^7.21.0",
"@babel/preset-env": "^7.20.2",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.0",
"@svgr/babel-plugin-transform-svg-component": "^6.5.1",
"@svgr/babel-preset": "^6.5.1",
"@types/match-sorter": "^2.3.0",
"@types/react-router-redux": "^5.0.21",
"@types/react-window": "^1.8.0",
"@types/redux-form": "^8.3.5",
"@vitejs/plugin-legacy": "^4.0.1",
"@vitejs/plugin-react": "^3.1.0",
"@wojtekmaj/enzyme-adapter-react-17": "^0.8.0",
"babel-plugin-import": "1.13.5",
"babel-jest": "^28.1.3",
"babel-plugin-inline-react-svg": "^2.0.2",
"bluebird": "^3.5.0",
"customize-cra": "0.2.9",
"enzyme": "^3.8.0",
"enzyme-to-json": "^3.6.2",
"http-proxy-middleware": "^2.0.6",
"jest": "^28.1.3",
"jest-environment-jsdom": "^28.1.3",
"jest-junit": "^15.0.0",
"less": "3.13.1",
"less-loader": "4.1.0",
"less-vars-to-js": "^1.2.1",
"react-app-rewired": "2.2.1",
"react-scripts": "2.1.3",
"react-test-renderer": "^18.2.0",
"sinon": "7.3.2",
"source-map-explorer": "^2.5.3"
"source-map-explorer": "^2.5.3",
"terser": "^5.16.5",
"vite": "^4.1.4"
},
"dependencies": {
"@jaegertracing/plexus": "0.2.0",
Expand Down Expand Up @@ -87,7 +86,6 @@
"raven-js": "^3.22.1",
"react": "^18.2.0",
"react-circular-progressbar": "^2.1.0",
"react-dimensions": "^1.3.1",
"react-dom": "^18.2.0",
"react-ga": "^3.3.1",
"react-helmet": "^6.1.0",
Expand All @@ -104,7 +102,7 @@
"redux": "^3.7.2",
"redux-actions": "^2.2.1",
"redux-async-middleware": "^0.0.0",
"redux-form": "^8.3.8",
"redux-form": "^8.3.9",
"redux-promise-middleware": "^5.1.1",
"reselect": "^4.1.6",
"store": "^2.0.12",
Expand All @@ -116,15 +114,18 @@
},
"scripts": {
"analyze": "source-map-explorer build/static/js/main.*",
"build": "REACT_APP_VSN_STATE=$(../../scripts/get-tracking-version.js) react-app-rewired build",
"build": "NODE_ENV=production REACT_APP_VSN_STATE=$(../../scripts/get-tracking-version.js) vite build",
"coverage": "yarn run test --coverage",
"start:ga-debug": "REACT_APP_GA_DEBUG=1 REACT_APP_VSN_STATE=$(../../scripts/get-tracking-version.js) react-app-rewired start",
"start": "react-app-rewired start",
"test-dev": "react-app-rewired test --env=jsdom",
"test": "CI=1 react-app-rewired test --env=jsdom --color"
"start:ga-debug": "REACT_APP_GA_DEBUG=1 REACT_APP_VSN_STATE=$(../../scripts/get-tracking-version.js) vite",
"start": "vite",
"test-dev": "jest",
"test": "CI=1 jest --color"
},
"jest": {
"globalSetup": "./jest.global-setup.js",
"globalSetup": "./test/jest.global-setup.js",
"setupFilesAfterEnv": [
"./test/jest-per-test-setup.js"
],
"collectCoverageFrom": [
"!src/setup*.js",
"!src/utils/DraggableManager/demo/*.tsx",
Expand All @@ -135,12 +136,18 @@
"reporters": [
"default",
"jest-junit"
]
},
"browserslist": [
">0.5%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
],
"transform": {
"\\.(css|png)$": "./test/generic-file-transform.js",
"\\.([jt]sx?|svg)$": "./test/babel-transform.js"
},
"transformIgnorePatterns": [
"[/\\\\\\\\]node_modules[/\\\\\\\\].+\\\\.(js|jsx|mjs|cjs|ts|tsx)$"
],
"testEnvironment": "jsdom",
"snapshotFormat": {
"escapeString": true,
"printBasicPrototype": true
}
}
}
10 changes: 8 additions & 2 deletions packages/jaeger-ui/src/components/DeepDependencies/traces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
// limitations under the License.

import * as React from 'react';
import { History as RouterHistory, Location } from 'history';
import _get from 'lodash/get';
import memoizeOne from 'memoize-one';
import queryString from 'query-string';
import { connect } from 'react-redux';

import { DeepDependencyGraphPageImpl, TOwnProps, TProps, TReduxProps } from '.';
import { DeepDependencyGraphPageImpl, TReduxProps } from '.';
import { getUrlState, sanitizeUrlState } from './url';
import { ROUTE_PATH } from '../SearchTracePage/url';
import GraphModel, { makeGraph } from '../../model/ddg/GraphModel';
Expand All @@ -33,6 +34,11 @@ import { ReduxState } from '../../types';
// Required for proper memoization of subsequent function calls
const svcOp = memoizeOne((service, operation) => ({ service, operation }));

type TOwnProps = {
history: RouterHistory;
location: Location;
};

// export for tests
export function mapStateToProps(state: ReduxState, ownProps: TOwnProps): TReduxProps {
const urlState = getUrlState(ownProps.location.search);
Expand Down Expand Up @@ -60,7 +66,7 @@ export function mapStateToProps(state: ReduxState, ownProps: TOwnProps): TReduxP
}

// export for tests
export class TracesDdgImpl extends React.PureComponent<TProps & { showSvcOpsHeader: never; baseUrl: never }> {
export class TracesDdgImpl extends React.PureComponent<TOwnProps & TReduxProps> {
render(): React.ReactNode {
const { location } = this.props;
const urlArgs = queryString.parse(location.search);
Expand Down
14 changes: 8 additions & 6 deletions packages/jaeger-ui/src/components/Monitor/ServicesView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import _debounce from 'lodash/debounce';
import _isEqual from 'lodash/isEqual';
import _isEmpty from 'lodash/isEmpty';
// @ts-ignore
import { Field, formValueSelector, reduxForm } from 'redux-form';
import { Field, formValueSelector, reduxForm, InjectedFormProps } from 'redux-form';
// @ts-ignore
import store from 'store';
import { connect } from 'react-redux';
Expand Down Expand Up @@ -137,8 +137,10 @@ const convertServiceErrorRateToPercentages = (serviceErrorRate: null | ServiceMe
return { ...serviceErrorRate, metricPoints: convertedMetricsPoints };
};

type TPropsWithInjectedFormProps = TProps & InjectedFormProps<{}, TProps>;

// export for tests
export class MonitorATMServicesViewImpl extends React.PureComponent<TProps, StateType> {
export class MonitorATMServicesViewImpl extends React.PureComponent<TPropsWithInjectedFormProps, StateType> {
docsLink: string;
graphDivWrapper: React.RefObject<HTMLInputElement>;
serviceSelectorValue: string = '';
Expand All @@ -150,7 +152,7 @@ export class MonitorATMServicesViewImpl extends React.PureComponent<TProps, Stat
graphXDomain: [],
};

constructor(props: TProps) {
constructor(props: TPropsWithInjectedFormProps) {
super(props);
this.graphDivWrapper = React.createRef();
this.docsLink = getConfigValue('monitor.docsLink');
Expand Down Expand Up @@ -271,7 +273,7 @@ export class MonitorATMServicesViewImpl extends React.PureComponent<TProps, Stat
<Col span={6}>
<h2 className="service-selector-header">Choose service</h2>
<Field
onChange={(e: Event, newValue: string) => trackSelectService(newValue)}
onChange={(e, newValue: string) => trackSelectService(newValue)}
name="service"
component={AdaptedVirtualSelect}
placeholder="Select A Service"
Expand Down Expand Up @@ -310,7 +312,7 @@ export class MonitorATMServicesViewImpl extends React.PureComponent<TProps, Stat
name="timeframe"
component={AdaptedVirtualSelect}
placeholder="Select A Timeframe"
onChange={(e: Event, value: number) => {
onChange={(e, value: number) => {
const { label } = timeFrameOptions.find(option => option.value === value)!;
trackSelectTimeframe(label);
}}
Expand Down Expand Up @@ -455,7 +457,7 @@ export default connect(
mapStateToProps,
mapDispatchToProps
)(
reduxForm({
reduxForm<{}, TProps>({
form: 'serviceForm',
})(MonitorATMServicesViewImpl)
);
Loading

0 comments on commit 1ba2345

Please sign in to comment.