Skip to content

Commit

Permalink
build(tsc): Refactor emotion types so we use less memory
Browse files Browse the repository at this point in the history
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** |
  • Loading branch information
BYK committed Feb 10, 2020
1 parent bd07c26 commit ae12a75
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 20 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ env:
- DJANGO_VERSION=">=1.11,<1.12"
# node's version is pinned by .nvmrc and is autodetected by `nvm install`.
- NODE_DIR="${HOME}/.nvm/versions/node/v$(< .nvmrc)"
- NODE_OPTIONS=--max-old-space-size=4096

before_install:
- &pip_install pip install --no-cache-dir "pip>=20.0.2"
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 0 additions & 5 deletions src/sentry/runner/commands/devserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,6 @@ def devserver(reload, watchers, workers, experimental_spa, styleguide, prefix, e
os.environ["SENTRY_WEBPACK_PROXY_PORT"] = "%s" % proxy_port
os.environ["SENTRY_BACKEND_PORT"] = "%s" % port

# webpack and/or typescript is causing memory issues
os.environ["NODE_OPTIONS"] = (
(os.environ.get("NODE_OPTIONS", "") + " --max-old-space-size=4096")
).lstrip()

# Replace the webpack watcher with the drop-in webpack-dev-server
webpack_config = next(w for w in daemons if w[0] == "webpack")[1]
webpack_config[0] = os.path.join(
Expand Down
199 changes: 196 additions & 3 deletions src/sentry/static/sentry/app/styled.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,198 @@
// 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';
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;
4 changes: 0 additions & 4 deletions src/sentry/utils/distutils/commands/build_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,6 @@ def _build_static(self):
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):
Expand Down
7 changes: 1 addition & 6 deletions tests/acceptance/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,5 @@ def pytest_configure(config):
)

subprocess.call(
["yarn", "webpack"],
env={
"NODE_ENV": "development",
"PATH": os.environ["PATH"],
"NODE_OPTIONS": "--max-old-space-size=4096",
},
["yarn", "webpack"], env={"NODE_ENV": "development", "PATH": os.environ["PATH"]},
)
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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==
Expand Down

0 comments on commit ae12a75

Please sign in to comment.