Skip to content

Commit fc4d7bd

Browse files
authored
Convert @emotion/react's source code to TypeScript (#3281)
* Convert `@emotion/react`'s source code to TypeScript * add changeset * limit `package.json['files']` * fixed type issue * try `esModuleInterop` * add required cast * use `resolveJsonModule` too * fixed extra things * fixed a silly mistake * one extra thing * fixed type error * try building before running dtslint * fixed import * try to bump tested TS versions * add extra ts-ignore * make sure the ts-ignore is above the problematic line * rewrite tests to stop relying on implementation detail * fixed type error * corrext tests * fixed entrypoints types
1 parent 8dc1a6d commit fc4d7bd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+582
-585
lines changed

.changeset/rotten-baboons-knock.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@emotion/react': minor
3+
---
4+
5+
Source code has been migrated to TypeScript. From now on type declarations will be emitted based on that, instead of being hand-written.

.github/workflows/main.yml

+3
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,8 @@ jobs:
8888
- uses: actions/checkout@v3
8989
- uses: ./.github/actions/ci-setup
9090

91+
- name: build
92+
run: yarn build
93+
9194
- name: dtslint
9295
run: yarn test:typescript

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@
188188
"@testing-library/react": "13.0.0-alpha.5",
189189
"@types/jest": "^29.5.12",
190190
"@types/node": "^12.20.37",
191-
"@types/react": "18.2.6",
191+
"@types/react": "18.3.12",
192192
"@typescript-eslint/eslint-plugin": "^7.13.0",
193193
"@typescript-eslint/parser": "^7.13.0",
194194
"babel-check-duplicated-nodes": "^1.0.0",

packages/cache/types/index.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export * from '../src'
1+
export * from '..'

packages/css/test/no-babel/index.test.js

-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import styled from '@emotion/styled'
77
let consoleError = console.error
88

99
afterEach(() => {
10-
// $FlowFixMe
1110
console.error = consoleError
1211
})
1312

@@ -146,7 +145,6 @@ describe('css', () => {
146145
})
147146

148147
const spy = jest.fn()
149-
// $FlowFixMe
150148
console.error = spy
151149

152150
expect(() =>

packages/css/test/sheet.dom.test.js

-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { sheet } from '@emotion/css'
33
const consoleError = console.error
44

55
afterEach(() => {
6-
// $FlowFixMe
76
console.error = consoleError
87
})
98

@@ -38,7 +37,6 @@ describe('sheet', () => {
3837
test('throws', () => {
3938
sheet.speedy(true)
4039
const spy = jest.fn()
41-
// $FlowFixMe
4240
console.error = spy
4341
sheet.insert('.asdfasdf4###112121211{')
4442
expect(spy.mock.calls.length).toBe(1)

packages/native/types/index.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Definitions by: Pat Sissons <https://github.com/patsissons>
2-
// TypeScript Version: 3.4
2+
// TypeScript Version: 4.1
33

44
import * as RN from 'react-native'
55
import { Theme } from '@emotion/react'

packages/native/types/tsconfig.json

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
"jsx": "react",
66
"lib": ["es6", "dom"],
77
"module": "commonjs",
8+
"esModuleInterop": true,
9+
"resolveJsonModule": true,
810
"noEmit": true,
911
"strict": true,
1012
"target": "es5",

packages/primitives-core/src/styled.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ export function createStyled(
5151

5252
// do we really want to use the same infra as the web since it only really uses theming?
5353
let Styled = React.forwardRef<unknown, StyledProps>((props, ref) => {
54-
const finalTag = (shouldUseAs && props.as) || component
54+
const finalTag =
55+
(shouldUseAs && (props.as as React.ElementType)) || component
5556

5657
let mergedProps = props
5758
if (props.theme == null) {

packages/react/__tests__/element.js

-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
// @flow
21
/** @jsx jsx */
32
import { render } from '@testing-library/react'
43
import { jsx, css, CacheProvider, ThemeProvider } from '@emotion/react'
54
import createCache from '@emotion/cache'
65

7-
// $FlowFixMe
86
console.error = jest.fn()
97

108
beforeEach(() => {
11-
// $FlowFixMe
129
document.head.innerHTML = ''
1310
jest.clearAllMocks()
1411
})
@@ -18,7 +15,6 @@ describe('EmotionElement', () => {
1815
const theme = { color: 'blue' }
1916
const cache = createCache({ key: 'context' })
2017

21-
// $FlowFixMe
2218
const Comp = ({ flag }) => (
2319
<ThemeProvider theme={theme}>
2420
<CacheProvider value={cache}>

packages/react/__tests__/get-label-from-stack-trace.js

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// @flow
21
import { getLabelFromStackTrace } from '../src/get-label-from-stack-trace'
32

43
/**

packages/react/__tests__/global.js

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
} from '@emotion/react'
1010
import createCache from '@emotion/cache'
1111

12-
// $FlowFixMe
1312
console.error = jest.fn()
1413

1514
beforeEach(() => {

packages/react/__tests__/import-prod.js

-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ expect.addSnapshotSerializer({
2323
const render = children =>
2424
new Promise(resolve => {
2525
const el = document.createElement('div')
26-
// $FlowFixMe
2726
document.body.appendChild(el)
2827

2928
if (ReactDOM.createRoot) {

packages/react/__tests__/theme-provider.dom.js

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import * as React from 'react'
66
import { jsx, ThemeProvider } from '@emotion/react'
77

88
beforeEach(() => {
9-
// $FlowFixMe
109
document.head.innerHTML = ''
1110
})
1211

packages/react/_isolated-hnrs/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"main": "dist/emotion-react-_isolated-hnrs.cjs.js",
33
"module": "dist/emotion-react-_isolated-hnrs.esm.js",
44
"umd:main": "dist/emotion-react-_isolated-hnrs.umd.min.js",
5+
"types": "dist/emotion-react-_isolated-hnrs.cjs.d.ts",
56
"sideEffects": false,
67
"preconstruct": {
78
"umdName": "emotionHoistNonReactStatics"

packages/react/jsx-dev-runtime/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"main": "dist/emotion-react-jsx-dev-runtime.cjs.js",
33
"module": "dist/emotion-react-jsx-dev-runtime.esm.js",
44
"umd:main": "dist/emotion-react-jsx-dev-runtime.umd.min.js",
5-
"types": "../types/jsx-dev-runtime",
5+
"types": "dist/emotion-react-jsx-dev-runtime.cjs.d.ts",
66
"sideEffects": false,
77
"preconstruct": {
88
"umdName": "emotionReactJSXDevRuntime"

packages/react/jsx-runtime/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"main": "dist/emotion-react-jsx-runtime.cjs.js",
33
"module": "dist/emotion-react-jsx-runtime.esm.js",
44
"umd:main": "dist/emotion-react-jsx-runtime.umd.min.js",
5-
"types": "../types/jsx-runtime",
5+
"types": "dist/emotion-react-jsx-runtime.cjs.d.ts",
66
"sideEffects": false,
77
"preconstruct": {
88
"umdName": "emotionReactJSXRuntime"

packages/react/package.json

+14-13
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "11.13.5",
44
"main": "dist/emotion-react.cjs.js",
55
"module": "dist/emotion-react.esm.js",
6+
"types": "dist/emotion-react.cjs.d.ts",
67
"exports": {
78
".": {
89
"types": {
@@ -232,25 +233,24 @@
232233
},
233234
"imports": {
234235
"#is-development": {
235-
"development": "./src/conditions/true.js",
236-
"default": "./src/conditions/false.js"
236+
"development": "./src/conditions/true.ts",
237+
"default": "./src/conditions/false.ts"
237238
},
238239
"#is-browser": {
239-
"edge-light": "./src/conditions/false.js",
240-
"workerd": "./src/conditions/false.js",
241-
"worker": "./src/conditions/false.js",
242-
"browser": "./src/conditions/true.js",
243-
"default": "./src/conditions/is-browser.js"
240+
"edge-light": "./src/conditions/false.ts",
241+
"workerd": "./src/conditions/false.ts",
242+
"worker": "./src/conditions/false.ts",
243+
"browser": "./src/conditions/true.ts",
244+
"default": "./src/conditions/is-browser.ts"
244245
}
245246
},
246-
"types": "types/index.d.ts",
247247
"files": [
248248
"src",
249249
"dist",
250250
"jsx-runtime",
251251
"jsx-dev-runtime",
252252
"_isolated-hnrs",
253-
"types/*.d.ts",
253+
"types/css-prop.d.ts",
254254
"macro.*"
255255
],
256256
"sideEffects": false,
@@ -283,6 +283,7 @@
283283
"@emotion/css-prettifier": "1.1.4",
284284
"@emotion/server": "11.11.0",
285285
"@emotion/styled": "11.13.5",
286+
"@types/hoist-non-react-statics": "^3.3.5",
286287
"html-tag-names": "^1.1.2",
287288
"react": "16.14.0",
288289
"svg-tag-names": "^1.1.1",
@@ -295,10 +296,10 @@
295296
"umd:main": "dist/emotion-react.umd.min.js",
296297
"preconstruct": {
297298
"entrypoints": [
298-
"./index.js",
299-
"./jsx-runtime.js",
300-
"./jsx-dev-runtime.js",
301-
"./_isolated-hnrs.js"
299+
"./index.ts",
300+
"./jsx-runtime.ts",
301+
"./jsx-dev-runtime.ts",
302+
"./_isolated-hnrs.ts"
302303
],
303304
"umdName": "emotionReact",
304305
"exports": {

packages/react/src/_isolated-hnrs.d.ts

-3
This file was deleted.

packages/react/src/_isolated-hnrs.js packages/react/src/_isolated-hnrs.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,10 @@ import hoistNonReactStatics from 'hoist-non-react-statics'
66
// have to wrap it in a proxy function because Rollup is too damn smart
77
// and if this module doesn't actually contain any logic of its own
88
// then Rollup just use 'hoist-non-react-statics' directly in other chunks
9-
export default (targetComponent, sourceComponent) =>
10-
hoistNonReactStatics(targetComponent, sourceComponent)
9+
export default <
10+
T extends React.ComponentType<any>,
11+
S extends React.ComponentType<any>
12+
>(
13+
targetComponent: T,
14+
sourceComponent: S
15+
) => hoistNonReactStatics(targetComponent, sourceComponent)

packages/react/src/class-names.js packages/react/src/class-names.tsx

+43-32
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
import * as React from 'react'
22
import {
3+
EmotionCache,
34
getRegisteredStyles,
45
insertStyles,
5-
registerStyles
6+
registerStyles,
7+
SerializedStyles
68
} from '@emotion/utils'
7-
import { serializeStyles } from '@emotion/serialize'
9+
import { CSSInterpolation, serializeStyles } from '@emotion/serialize'
810
import isDevelopment from '#is-development'
911
import { withEmotionCache } from './context'
10-
import { ThemeContext } from './theming'
12+
import { Theme, ThemeContext } from './theming'
1113
import { useInsertionEffectAlwaysWithSyncFallback } from '@emotion/use-insertion-effect-with-fallbacks'
1214
import isBrowser from '#is-browser'
1315

14-
/*
15-
type ClassNameArg =
16+
export interface ArrayClassNamesArg extends Array<ClassNamesArg> {}
17+
18+
export type ClassNamesArg =
19+
| undefined
20+
| null
1621
| string
1722
| boolean
18-
| { [key: string]: boolean }
19-
| Array<ClassNameArg>
20-
| null
21-
| void
22-
*/
23+
| { [className: string]: boolean | null | undefined }
24+
| ArrayClassNamesArg
2325

24-
let classnames = (args /*: Array<ClassNameArg> */) /*: string */ => {
26+
let classnames = (args: ArrayClassNamesArg): string => {
2527
let len = args.length
2628
let i = 0
2729
let cls = ''
@@ -69,11 +71,11 @@ let classnames = (args /*: Array<ClassNameArg> */) /*: string */ => {
6971
return cls
7072
}
7173
function merge(
72-
registered /*: Object */,
73-
css /*: (...args: Array<any>) => string */,
74-
className /*: string */
74+
registered: EmotionCache['registered'],
75+
css: ClassNamesContent['css'],
76+
className: string
7577
) {
76-
const registeredStyles = []
78+
const registeredStyles: string[] = []
7779

7880
const rawClassName = getRegisteredStyles(
7981
registered,
@@ -87,7 +89,13 @@ function merge(
8789
return rawClassName + css(registeredStyles)
8890
}
8991

90-
const Insertion = ({ cache, serializedArr }) => {
92+
const Insertion = ({
93+
cache,
94+
serializedArr
95+
}: {
96+
cache: EmotionCache
97+
serializedArr: SerializedStyles[]
98+
}) => {
9199
let rules = useInsertionEffectAlwaysWithSyncFallback(() => {
92100
let rules = ''
93101
for (let i = 0; i < serializedArr.length; i++) {
@@ -101,14 +109,14 @@ const Insertion = ({ cache, serializedArr }) => {
101109
}
102110
})
103111

104-
if (!isBrowser && rules.length !== 0) {
112+
if (!isBrowser && rules!.length !== 0) {
105113
return (
106114
<style
107115
{...{
108116
[`data-emotion`]: `${cache.key} ${serializedArr
109117
.map(serialized => serialized.name)
110118
.join(' ')}`,
111-
dangerouslySetInnerHTML: { __html: rules },
119+
dangerouslySetInnerHTML: { __html: rules! },
112120
nonce: cache.sheet.nonce
113121
}}
114122
/>
@@ -117,21 +125,23 @@ const Insertion = ({ cache, serializedArr }) => {
117125
return null
118126
}
119127

120-
/*
121-
type Props = {
122-
children: ({
123-
css: (...args: any) => string,
124-
cx: (...args: Array<ClassNameArg>) => string,
125-
theme: Object
126-
}) => React.Node
127-
} */
128+
export interface ClassNamesContent {
129+
css(template: TemplateStringsArray, ...args: Array<CSSInterpolation>): string
130+
css(...args: Array<CSSInterpolation>): string
131+
cx(...args: Array<ClassNamesArg>): string
132+
theme: Theme
133+
}
134+
135+
export interface ClassNamesProps {
136+
children(content: ClassNamesContent): React.ReactNode
137+
}
128138

129-
export const ClassNames /*: React.AbstractComponent<Props>*/ =
130-
/* #__PURE__ */ withEmotionCache((props, cache) => {
139+
export const ClassNames = /* #__PURE__ */ withEmotionCache<ClassNamesProps>(
140+
(props, cache) => {
131141
let hasRendered = false
132-
let serializedArr = []
142+
let serializedArr: SerializedStyles[] = []
133143

134-
let css = (...args /*: Array<any> */) => {
144+
let css: ClassNamesContent['css'] = (...args) => {
135145
if (hasRendered && isDevelopment) {
136146
throw new Error('css can only be used during render')
137147
}
@@ -142,7 +152,7 @@ export const ClassNames /*: React.AbstractComponent<Props>*/ =
142152
registerStyles(cache, serialized, false)
143153
return `${cache.key}-${serialized.name}`
144154
}
145-
let cx = (...args /*: Array<ClassNameArg>*/) => {
155+
let cx = (...args: Array<ClassNamesArg>) => {
146156
if (hasRendered && isDevelopment) {
147157
throw new Error('cx can only be used during render')
148158
}
@@ -162,7 +172,8 @@ export const ClassNames /*: React.AbstractComponent<Props>*/ =
162172
{ele}
163173
</>
164174
)
165-
})
175+
}
176+
)
166177

167178
if (isDevelopment) {
168179
ClassNames.displayName = 'EmotionClassNames'
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)