From 2fecf1532e5e2c972a4eda28761020f01840c1d4 Mon Sep 17 00:00:00 2001 From: Federico Zivolo Date: Fri, 25 Oct 2019 16:56:22 +0200 Subject: [PATCH 1/7] fix: refactor styled-base Flow types to work again --- .changeset/red-chefs-camp.md | 5 ++++ .flowconfig | 1 + .flowconfig-ci | 1 + packages/styled-base/src/index.js | 16 +++++-------- packages/styled-base/src/utils.js | 38 ++++++++++++++++++++---------- packages/styled-base/tests/flow.js | 8 +++++++ 6 files changed, 46 insertions(+), 23 deletions(-) create mode 100644 .changeset/red-chefs-camp.md create mode 100644 packages/styled-base/tests/flow.js diff --git a/.changeset/red-chefs-camp.md b/.changeset/red-chefs-camp.md new file mode 100644 index 000000000..413d55896 --- /dev/null +++ b/.changeset/red-chefs-camp.md @@ -0,0 +1,5 @@ +--- +'@emotion/styled-base': patch +--- + +Fixed package's Flow types diff --git a/.flowconfig b/.flowconfig index d903bf2eb..2b17eb2da 100644 --- a/.flowconfig +++ b/.flowconfig @@ -23,4 +23,5 @@ [options] suppress_comment=.*\\$FlowFixMe +suppress_comment=.*\\$FlowExpectError sharedmemory.hash_table_pow=21 diff --git a/.flowconfig-ci b/.flowconfig-ci index 812eee58e..83001e2f6 100644 --- a/.flowconfig-ci +++ b/.flowconfig-ci @@ -24,5 +24,6 @@ [options] suppress_comment=.*\\$FlowFixMe +suppress_comment=.*\\$FlowExpectError server.max_workers=1 sharedmemory.hash_table_pow=21 diff --git a/packages/styled-base/src/index.js b/packages/styled-base/src/index.js index 1ac85c977..1e1a657b1 100644 --- a/packages/styled-base/src/index.js +++ b/packages/styled-base/src/index.js @@ -4,7 +4,9 @@ import type { ElementType } from 'react' import { getDefaultShouldForwardProp, type StyledOptions, - type CreateStyled + type CreateStyled, + type CreateStyledComponent, + type StyledComponent } from './utils' import { withEmotionCache, ThemeContext } from '@emotion/core' import { getRegisteredStyles, insertStyles } from '@emotion/utils' @@ -17,12 +19,6 @@ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_liter let isBrowser = typeof document !== 'undefined' -type StyledComponent = ( - props: * -) => React.Node & { - withComponent(nextTag: ElementType, nextOptions?: StyledOptions): * -} - let createStyled: CreateStyled = (tag: any, options?: StyledOptions) => { if (process.env.NODE_ENV !== 'production') { if (tag === undefined) { @@ -55,7 +51,7 @@ let createStyled: CreateStyled = (tag: any, options?: StyledOptions) => { shouldForwardProp || getDefaultShouldForwardProp(baseTag) const shouldUseAs = !defaultShouldForwardProp('as') - return function(): StyledComponent { + return (function() { let args = arguments let styles = isReal && tag.__emotion_styles !== undefined @@ -82,7 +78,7 @@ let createStyled: CreateStyled = (tag: any, options?: StyledOptions) => { } } - const Styled: any = withEmotionCache((props, context, ref) => { + const Styled: StyledComponent = withEmotionCache((props, context, ref) => { return ( {theme => { @@ -221,7 +217,7 @@ let createStyled: CreateStyled = (tag: any, options?: StyledOptions) => { )(...styles) } return Styled - } + }: CreateStyledComponent) } export default createStyled diff --git a/packages/styled-base/src/utils.js b/packages/styled-base/src/utils.js index 92eefd517..f334c8286 100644 --- a/packages/styled-base/src/utils.js +++ b/packages/styled-base/src/utils.js @@ -1,9 +1,31 @@ // @flow import * as React from 'react' +import type { ElementType } from 'react' import isPropValid from '@emotion/is-prop-valid' export type Interpolations = Array +export type StyledOptions = { + label?: string, + shouldForwardProp?: string => boolean, + target?: string +} + +export type StyledComponent = { + (Interpolations): React$Node, + withComponent: ( + nextTag: ElementType, + nextOptions?: StyledOptions + ) => StyledComponent, + displayName: string, + defaultProps: any, + __emotion_real: StyledComponent, + __emotion_base: any, + __emotion_styles: any, + __emotion_forwardProp: any, + toString: () => string +} + const testOmitPropsOnStringTag = isPropValid const testOmitPropsOnComponent = (key: string) => key !== 'theme' && key !== 'innerRef' @@ -17,19 +39,9 @@ export const getDefaultShouldForwardProp = (tag: React.ElementType) => ? testOmitPropsOnStringTag : testOmitPropsOnComponent -export type StyledOptions = { - label?: string, - shouldForwardProp?: string => boolean, - target?: string -} - -type CreateStyledComponent = (...args: Interpolations) => * - -type BaseCreateStyled = ( - tag: React.ElementType, - options?: StyledOptions -) => CreateStyledComponent +export type CreateStyledComponent = (...args: Interpolations) => StyledComponent -export type CreateStyled = BaseCreateStyled & { +export type CreateStyled = { + (tag: React.ElementType, options?: StyledOptions): CreateStyledComponent, [key: string]: CreateStyledComponent } diff --git a/packages/styled-base/tests/flow.js b/packages/styled-base/tests/flow.js new file mode 100644 index 000000000..ab7aa2e8a --- /dev/null +++ b/packages/styled-base/tests/flow.js @@ -0,0 +1,8 @@ +// @flow +import createStyled from '../src' +import type { CreateStyledComponent } from '../src/utils' + +export const valid: CreateStyledComponent = createStyled('div') + +// $FlowExpectError: we can't cast a StyledComponent to string +export const invalid: string = createStyled('div') From ee69409b0409dcdeecb5de53be482d29646dc293 Mon Sep 17 00:00:00 2001 From: Federico Zivolo Date: Sat, 26 Oct 2019 13:33:33 +0200 Subject: [PATCH 2/7] fix: improve Styled type to accept optional props type --- .changeset/red-chefs-camp.md | 1 + packages/styled-base/flow-tests/flow.js | 21 +++ packages/styled-base/src/index.js | 190 ++++++++++++------------ packages/styled-base/src/utils.js | 21 +-- packages/styled-base/tests/flow.js | 8 - 5 files changed, 130 insertions(+), 111 deletions(-) create mode 100644 packages/styled-base/flow-tests/flow.js delete mode 100644 packages/styled-base/tests/flow.js diff --git a/.changeset/red-chefs-camp.md b/.changeset/red-chefs-camp.md index 413d55896..2612374bc 100644 --- a/.changeset/red-chefs-camp.md +++ b/.changeset/red-chefs-camp.md @@ -1,5 +1,6 @@ --- '@emotion/styled-base': patch +'@emotion/styled': patch --- Fixed package's Flow types diff --git a/packages/styled-base/flow-tests/flow.js b/packages/styled-base/flow-tests/flow.js new file mode 100644 index 000000000..9d372a9c3 --- /dev/null +++ b/packages/styled-base/flow-tests/flow.js @@ -0,0 +1,21 @@ +// @flow +import * as React from 'react' +import createStyled from '../src' +import type { CreateStyledComponent } from '../src/utils' + +export const valid: CreateStyledComponent = createStyled('div') + +// $FlowExpectError: we can't cast a StyledComponent to string +export const invalid: string = createStyled('div') + +const styled = createStyled('div') +type Props = { color: string } +const Div = styled({ color: props => props.color }) + +export const validProp =
+ +// $FlowExpectError: color property should be a string +export const invalidProp =
+ +// $FlowExpectError: we don't expose the private StyledComponent properties +export const invalidPropAccess = styled().__emotion_base diff --git a/packages/styled-base/src/index.js b/packages/styled-base/src/index.js index 1e1a657b1..b5cd64eb3 100644 --- a/packages/styled-base/src/index.js +++ b/packages/styled-base/src/index.js @@ -5,8 +5,7 @@ import { getDefaultShouldForwardProp, type StyledOptions, type CreateStyled, - type CreateStyledComponent, - type StyledComponent + type PrivateStyledComponent } from './utils' import { withEmotionCache, ThemeContext } from '@emotion/core' import { getRegisteredStyles, insertStyles } from '@emotion/utils' @@ -51,7 +50,7 @@ let createStyled: CreateStyled = (tag: any, options?: StyledOptions) => { shouldForwardProp || getDefaultShouldForwardProp(baseTag) const shouldUseAs = !defaultShouldForwardProp('as') - return (function() { + return function

(): PrivateStyledComponent

{ let args = arguments let styles = isReal && tag.__emotion_styles !== undefined @@ -78,104 +77,106 @@ let createStyled: CreateStyled = (tag: any, options?: StyledOptions) => { } } - const Styled: StyledComponent = withEmotionCache((props, context, ref) => { - return ( - - {theme => { - const finalTag = (shouldUseAs && props.as) || baseTag - - let className = '' - let classInterpolations = [] - let mergedProps = props - if (props.theme == null) { - mergedProps = {} - for (let key in props) { - mergedProps[key] = props[key] + const Styled: PrivateStyledComponent

= withEmotionCache( + (props, context, ref) => { + return ( + + {theme => { + const finalTag = (shouldUseAs && props.as) || baseTag + + let className = '' + let classInterpolations = [] + let mergedProps = props + if (props.theme == null) { + mergedProps = {} + for (let key in props) { + mergedProps[key] = props[key] + } + mergedProps.theme = theme + } + + if (typeof props.className === 'string') { + className = getRegisteredStyles( + context.registered, + classInterpolations, + props.className + ) + } else if (props.className != null) { + className = `${props.className} ` } - mergedProps.theme = theme - } - if (typeof props.className === 'string') { - className = getRegisteredStyles( + const serialized = serializeStyles( + styles.concat(classInterpolations), context.registered, - classInterpolations, - props.className + mergedProps ) - } else if (props.className != null) { - className = `${props.className} ` - } - - const serialized = serializeStyles( - styles.concat(classInterpolations), - context.registered, - mergedProps - ) - const rules = insertStyles( - context, - serialized, - typeof finalTag === 'string' - ) - className += `${context.key}-${serialized.name}` - if (targetClassName !== undefined) { - className += ` ${targetClassName}` - } - - const finalShouldForwardProp = - shouldUseAs && shouldForwardProp === undefined - ? getDefaultShouldForwardProp(finalTag) - : defaultShouldForwardProp - - let newProps = {} - - for (let key in props) { - if (shouldUseAs && key === 'as') continue - - if ( - // $FlowFixMe - finalShouldForwardProp(key) - ) { - newProps[key] = props[key] + const rules = insertStyles( + context, + serialized, + typeof finalTag === 'string' + ) + className += `${context.key}-${serialized.name}` + if (targetClassName !== undefined) { + className += ` ${targetClassName}` } - } - newProps.className = className + const finalShouldForwardProp = + shouldUseAs && shouldForwardProp === undefined + ? getDefaultShouldForwardProp(finalTag) + : defaultShouldForwardProp - newProps.ref = ref || props.innerRef - if (process.env.NODE_ENV !== 'production' && props.innerRef) { - console.error( - '`innerRef` is deprecated and will be removed in a future major version of Emotion, please use the `ref` prop instead' + - (identifierName === undefined - ? '' - : ` in the usage of \`${identifierName}\``) - ) - } - - const ele = React.createElement(finalTag, newProps) - if (!isBrowser && rules !== undefined) { - let serializedNames = serialized.name - let next = serialized.next - while (next !== undefined) { - serializedNames += ' ' + next.name - next = next.next + let newProps = {} + + for (let key in props) { + if (shouldUseAs && key === 'as') continue + + if ( + // $FlowFixMe + finalShouldForwardProp(key) + ) { + newProps[key] = props[key] + } } - return ( - -