Browse Source

build(tsc): Refactor emotion types so we use less memory (#16913)

* build(tsc): Refactor emotion types so we use less memory

This patch essentially backports emotion-js/emotion#1501 to dramatticaly
reduce the number of types generated during compilation. This reduces
the number of types from ~700k to 200k which translates to more than 50%
reduction in type check time and memory consumption.

Using the command `tsc --noEmit --extendedDiagnostics`

| Metric | Before | After |
|:------|--------:|------:|
| Files | 1189 |    1189 |
| Lines | 484378 |  484571 |
| Nodes | 892394 |  893030 |
| Identifiers | 296394 |  296638 |
| Symbols | 769880 |  719992 |
| Types | **705230** | **202553** |
| Memory used | **1024710K** | **747802K** |
| Assignability cache size | 576214 |  382608 |
| Identity cache size | 7697 | 6167 |
| Subtype cache size | 18326 | 18307 |
| I/O Read time | 0.36s | 0.30s |
| Parse time | 2.13s | 1.76s |
| Program time | 4.28s | 3.48s |
| Bind time | 1.25s | 0.94s |
| Check time | **40.85s** | **16.66s** |
| Total time | **46.38s** | **21.09s** |
Burak Yigit Kaya 5 years ago
parent
commit
15f9171354

+ 1 - 0
package.json

@@ -148,6 +148,7 @@
     "babel-gettext-extractor": "^4.1.3",
     "babel-jest": "24.9.0",
     "babel-plugin-dynamic-import-node": "^2.2.0",
+    "csstype": "^2.6.8",
     "enzyme": "3.10.0",
     "enzyme-adapter-react-16": "1.15.1",
     "enzyme-to-json": "3.4.3",

+ 200 - 3
src/sentry/static/sentry/app/styled.tsx

@@ -1,5 +1,202 @@
-// TODO(ts): Figure out why eslint chokes on this import
-import styled, {CreateStyled} from '@original-emotion/styled'; // eslint-disable-line import/named
+/* HACK(BYK): This file is a slightly modified backport of
+ *
+ * !!! DELETE ME WHEN UPGRADING TO EMOTION@11 !!!
+ *
+ * https://github.com/emotion-js/emotion/pull/1501 and
+ * https://github.com/emotion-js/emotion/pull/1664 to
+ * fix our TypeScript compile times and memory usage as
+ * emotion@10 is known to generate too many new types due
+ * to improper use of generics.
+ *
+ * This is especially pronounced with TS 3.7+
+ * See https://github.com/microsoft/TypeScript/issues/24435
+ * See https://github.com/microsoft/TypeScript/issues/34920
+ */
+
+import styled from '@original-emotion/styled';
+import * as React from 'react';
+// TODO(BYK): Figure out why ESLint cannot resolve this
+//            probably need to include `.d.ts` extension
+//            in some resolver config.
+// eslint-disable-next-line import/no-unresolved
+import * as CSS from 'csstype';
+
 import theme from './utils/theme';
 
-export default styled as CreateStyled<typeof theme>;
+type Theme = typeof theme;
+
+/**
+ * @desc Utility type for getting props type of React component.
+ */
+export type PropsOf<
+  C extends keyof JSX.IntrinsicElements | React.JSXElementConstructor<any>
+> = JSX.LibraryManagedAttributes<C, React.ComponentProps<C>>;
+
+export interface SerializedStyles {
+  name: string;
+  styles: string;
+  map?: string;
+  next?: SerializedStyles;
+}
+
+export interface StyledOptions {
+  label?: string;
+  shouldForwardProp?(propName: string): boolean;
+  target?: string;
+}
+
+export interface CSSObject
+  extends CSSPropertiesWithMultiValues,
+    CSSPseudosForCSSObject,
+    CSSOthersObjectForCSSObject {}
+
+export interface ComponentSelector {
+  __emotion_styles: any;
+}
+
+export type Keyframes = {
+  name: string;
+  styles: string;
+  anim: number;
+  toString: () => string;
+} & string;
+
+export type CSSProperties = CSS.PropertiesFallback<number | string>;
+export type CSSPropertiesWithMultiValues = {
+  [K in keyof CSSProperties]: CSSProperties[K] | Array<Extract<CSSProperties[K], string>>;
+};
+/**
+ * @desc Following type exists for autocompletion of key.
+ */
+export type CSSPseudos<MP> = {[K in CSS.Pseudos]?: ObjectInterpolation<MP>};
+export interface CSSOthersObject<MP> {
+  [propertiesName: string]: Interpolation<MP>;
+}
+
+export type CSSPseudosForCSSObject = {[K in CSS.Pseudos]?: CSSObject};
+
+export interface ArrayCSSInterpolation extends Array<CSSInterpolation> {}
+
+export type CSSInterpolation =
+  | null
+  | undefined
+  | boolean
+  | number
+  | string
+  | ComponentSelector
+  | Keyframes
+  | SerializedStyles
+  | CSSObject
+  | ArrayCSSInterpolation;
+
+export interface CSSOthersObjectForCSSObject {
+  [propertiesName: string]: CSSInterpolation;
+}
+
+export interface ArrayInterpolation<MP> extends Array<Interpolation<MP>> {}
+export interface ObjectInterpolation<MP>
+  extends CSSPropertiesWithMultiValues,
+    CSSPseudos<MP>,
+    CSSOthersObject<MP> {}
+
+export interface FunctionInterpolation<MergedProps> {
+  (mergedProps: MergedProps): Interpolation<MergedProps>;
+}
+
+export type Interpolation<MergedProps = undefined> =
+  | null
+  | undefined
+  | boolean
+  | number
+  | string
+  | ComponentSelector
+  | Keyframes
+  | SerializedStyles
+  | ArrayInterpolation<MergedProps>
+  | ObjectInterpolation<MergedProps>
+  | FunctionInterpolation<MergedProps>;
+
+/**
+ * @typeparam ComponentProps  Props which will be included when withComponent is called
+ * @typeparam SpecificComponentProps  Props which will *not* be included when withComponent is called
+ */
+export interface StyledComponent<
+  ComponentProps extends {},
+  SpecificComponentProps extends {} = {}
+> extends React.FC<ComponentProps & SpecificComponentProps>, ComponentSelector {
+  withComponent<C extends React.ComponentType<React.ComponentProps<C>>>(
+    component: C
+  ): StyledComponent<ComponentProps & PropsOf<C>>;
+  withComponent<Tag extends keyof JSX.IntrinsicElements>(
+    tag: Tag
+  ): StyledComponent<ComponentProps, JSX.IntrinsicElements[Tag]>;
+}
+
+/**
+ * @typeparam ComponentProps  Props which will be included when withComponent is called
+ * @typeparam SpecificComponentProps  Props which will *not* be included when withComponent is called
+ * @typeparam StyleProps  Params passed to styles but not exposed as React props. These are normally library provided props
+ */
+export interface CreateStyledComponent<
+  ComponentProps extends {},
+  SpecificComponentProps extends {} = {},
+  StyleProps extends {} = {}
+> {
+  /**
+   * @typeparam AdditionalProps  Additional props to add to your styled component
+   */
+  <AdditionalProps extends {} = {}>(
+    ...styles: Array<
+      Interpolation<
+        ComponentProps &
+          SpecificComponentProps &
+          StyleProps &
+          AdditionalProps & {theme: Theme}
+      >
+    >
+  ): StyledComponent<ComponentProps & AdditionalProps, SpecificComponentProps>;
+
+  (
+    template: TemplateStringsArray,
+    ...styles: Array<Interpolation<ComponentProps & SpecificComponentProps & StyleProps>>
+  ): StyledComponent<ComponentProps, SpecificComponentProps>;
+
+  /**
+   * @typeparam AdditionalProps  Additional props to add to your styled component
+   */
+  <AdditionalProps extends {}>(
+    template: TemplateStringsArray,
+    ...styles: Array<
+      Interpolation<
+        ComponentProps &
+          SpecificComponentProps &
+          StyleProps &
+          AdditionalProps & {theme: Theme}
+      >
+    >
+  ): StyledComponent<ComponentProps & AdditionalProps, SpecificComponentProps>;
+}
+
+/**
+ * @desc
+ * This function accepts a React component or tag ('div', 'a' etc).
+ *
+ * @example styled(MyComponent)({ width: 100 })
+ * @example styled(MyComponent)(myComponentProps => ({ width: myComponentProps.width })
+ * @example styled('div')({ width: 100 })
+ * @example styled('div')<Props>(props => ({ width: props.width })
+ */
+export interface CreateStyled {
+  // This `any` below, should be `React.ComponentProps` but https://github.com/getsentry/sentry/pull/15383 prevents it
+  <C extends React.ComponentType<any>>(
+    component: C,
+    options?: StyledOptions
+  ): CreateStyledComponent<PropsOf<C> & {theme?: Theme}, {}, {theme: Theme}>;
+
+  <Tag extends keyof JSX.IntrinsicElements>(
+    tag: Tag,
+    options?: StyledOptions
+  ): CreateStyledComponent<{theme?: Theme}, JSX.IntrinsicElements[Tag], {theme: Theme}>;
+}
+
+export default styled as CreateStyled;

+ 0 - 4
src/sentry/utils/distutils/commands/build_assets.py

@@ -127,10 +127,6 @@ class BuildAssetsCommand(BaseBuildCommand):
         env = dict(os.environ)
         env["SENTRY_STATIC_DIST_PATH"] = self.sentry_static_dist_path
         env["NODE_ENV"] = "production"
-        # TODO: Our JS builds should not require 4GB heap space
-        env["NODE_OPTIONS"] = (
-            (env.get("NODE_OPTIONS", "") + " --max-old-space-size=4096")
-        ).lstrip()
         self._run_yarn_command(["webpack", "--bail"], env=env)
 
     def _write_version_file(self, version_info):

+ 1 - 0
webpack.config.js

@@ -275,6 +275,7 @@ let appConfig = {
       /dist\/jquery\.js/,
       /jed\/jed\.js/,
       /marked\/lib\/marked\.js/,
+      /terser\/dist\/bundle\.min\.js/,
     ],
   },
   plugins: [

+ 1 - 1
yarn.lock

@@ -5160,7 +5160,7 @@ cssstyle@^1.0.0:
   dependencies:
     cssom "0.3.x"
 
-csstype@^2.2.0, csstype@^2.5.7, csstype@^2.6.4, csstype@^2.6.7:
+csstype@^2.2.0, csstype@^2.5.7, csstype@^2.6.4, csstype@^2.6.7, csstype@^2.6.8:
   version "2.6.8"
   resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.8.tgz#0fb6fc2417ffd2816a418c9336da74d7f07db431"
   integrity sha512-msVS9qTuMT5zwAGCVm4mxfrZ18BNc6Csd0oJAtiFMZ1FAx1CCvy2+5MDmYoix63LM/6NDbNtodCiGYGmFgO0dA==