diff --git a/.changeset/red-chefs-camp.md b/.changeset/red-chefs-camp.md new file mode 100644 index 000000000..2612374bc --- /dev/null +++ b/.changeset/red-chefs-camp.md @@ -0,0 +1,6 @@ +--- +'@emotion/styled-base': patch +'@emotion/styled': 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/flow-tests/flow.js b/packages/styled-base/flow-tests/flow.js new file mode 100644 index 000000000..110dd2b49 --- /dev/null +++ b/packages/styled-base/flow-tests/flow.js @@ -0,0 +1,27 @@ +/* eslint-disable no-unused-vars */ +// @flow +import * as React from 'react' +import createStyled from '../src' +import type { CreateStyledComponent, StyledComponent } 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 } +// prettier-ignore +const Div = styled({ color: props => props.color }) + +const validProp =
+ +// $FlowExpectError: color property should be a string +const invalidProp =
+ +// $FlowExpectError: we don't expose the private StyledComponent properties +const invalidPropAccess = styled().__emotion_base + +// We allow styled components not to specify their props types +// NOTE: this is allowed only if you don't attempt to export it! +const untyped: StyledComponent = styled({}) diff --git a/packages/styled-base/src/index.js b/packages/styled-base/src/index.js index 1ac85c977..ca79488e3 100644 --- a/packages/styled-base/src/index.js +++ b/packages/styled-base/src/index.js @@ -4,7 +4,8 @@ import type { ElementType } from 'react' import { getDefaultShouldForwardProp, type StyledOptions, - type CreateStyled + type CreateStyled, + type PrivateStyledComponent } from './utils' import { withEmotionCache, ThemeContext } from '@emotion/core' import { getRegisteredStyles, insertStyles } from '@emotion/utils' @@ -17,12 +18,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 +50,7 @@ let createStyled: CreateStyled = (tag: any, options?: StyledOptions) => { shouldForwardProp || getDefaultShouldForwardProp(baseTag) const shouldUseAs = !defaultShouldForwardProp('as') - return function(): StyledComponent { + return function

(): PrivateStyledComponent

{ let args = arguments let styles = isReal && tag.__emotion_styles !== undefined @@ -82,104 +77,107 @@ let createStyled: CreateStyled = (tag: any, options?: StyledOptions) => { } } - const Styled: any = 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] + // $FlowFixMe: we need to cast StatelessFunctionalComponent to our PrivateStyledComponent class + 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] + } + } + + newProps.className = className + + 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}\``) + ) } - return ( - -