<commit_before>// @flow
import { Component, createElement } from 'react'
import PropTypes from 'prop-types'

import type { Theme } from './ThemeProvider'

import isTag from '../utils/isTag'
import isStyledComponent from '../utils/isStyledComponent'
import getComponentName from '../utils/getComponentName'
import type { RuleSet, Target } from '../types'

import { CHANNEL, CHANNEL_NEXT, CONTEXT_CHANNEL_SHAPE } from './ThemeProvider'

export default (constructWithOptions: Function, InlineStyle: Function) => {
  class BaseStyledNativeComponent extends Component {
    static target: Target
    static styledComponentId: string
    static attrs: Object
    static inlineStyle: Object
    root: ?Object

    attrs = {}
    state = {
      theme: null,
      generatedStyles: undefined,
    }

    unsubscribeId: number = -1

    unsubscribeFromContext() {
      if (this.unsubscribeId !== -1) {
        this.context[CHANNEL_NEXT].unsubscribe(this.unsubscribeId)
      }
    }


    buildExecutionContext(theme: any, props: any) {
      const { attrs } = this.constructor
      const context = { ...props, theme }
      if (attrs === undefined) {
        return context
      }

      this.attrs = Object.keys(attrs).reduce((acc, key) => {
        const attr = attrs[key]
        // eslint-disable-next-line no-param-reassign
        acc[key] = typeof attr === 'function' ? attr(context) : attr
        return acc
      }, {})

      return { ...context, ...this.attrs }
    }

    generateAndInjectStyles(theme: any, props: any) {
      const { inlineStyle } = this.constructor
      const executionContext = this.buildExecutionContext(theme, props)

      return inlineStyle.generateStyleObject(executionContext)
    }

    componentWillMount() {
      // If there is a theme in the context, subscribe to the event emitter. This
      // is necessary due to pure components blocking context updates, this circumvents
      // that by updating when an event is emitted
      const styledContext = this.context[CHANNEL_NEXT]
      if (styledContext !== undefined) {
        const { subscribe } = styledContext
        this.unsubscribeId = subscribe(nextTheme => {
          // This will be called once immediately

          // Props should take precedence over ThemeProvider, which should take precedence over
          // defaultProps, but React automatically puts defaultProps on props.
          const { defaultProps } = this.constructor
          /* eslint-disable react/prop-types */
          const isDefaultTheme = defaultProps && this.props.theme === defaultProps.theme
          const theme = this.props.theme && !isDefaultTheme ? this.props.theme : nextTheme
          /* eslint-enable */
          const generatedStyles = this.generateAndInjectStyles(theme, this.props)
          this.setState({ theme, generatedStyles })
        })
      } else {
        // eslint-disable-next-line react/prop-types
        const theme = this.props.theme || {}
        const generatedStyles = this.generateAndInjectStyles(
          theme,
          this.props,
        )
        this.setState({ theme, generatedStyles })
      }
    }

    componentWillReceiveProps(nextProps: { theme?: Theme, [key: string]: any }) {
      this.setState((oldState) => {
        // Props should take precedence over ThemeProvider, which should take precedence over
        // defaultProps, but React automatically puts defaultProps on props.
        const { defaultProps } = this.constructor
        /* eslint-disable react/prop-types */
        const isDefaultTheme = defaultProps && nextProps.theme === defaultProps.theme
        const theme = nextProps.theme && !isDefaultTheme ? nextProps.theme : oldState.theme
        /* eslint-enable */
        const generatedStyles = this.generateAndInjectStyles(theme, nextProps)

        return { theme, generatedStyles }
      })
    }

    componentWillUnmount() {
      this.unsubscribeFromContext()
    }

    setNativeProps(nativeProps: Object) {
      if (this.root !== undefined) {
        // $FlowFixMe
        this.root.setNativeProps(nativeProps)
      } else {
        const { displayName } = this.constructor

        // eslint-disable-next-line no-console
        console.warn(
          'setNativeProps was called on a Styled Component wrapping a stateless functional component. ' +
          'In this case no ref will be stored, and instead an innerRef prop will be passed on.\n' +
          `Check whether the stateless functional component is passing on innerRef as a ref in ${displayName}.`,
        )
      }
    }

    onRef = (node: any) => {
      // eslint-disable-next-line react/prop-types
      const { innerRef } = this.props
      this.root = node

      if (typeof innerRef === 'function') {
        innerRef(node)
      }
    }

    render() {
      // eslint-disable-next-line react/prop-types
      const { children, style } = this.props
      const { generatedStyles } = this.state
      const { target } = this.constructor

      const propsForElement = {
        ...this.attrs,
        ...this.props,
        style: [generatedStyles, style],
      }

      if (
        !isStyledComponent(target) &&
        (
          // NOTE: We can't pass a ref to a stateless functional component
          typeof target !== 'function' ||
          (target.prototype && 'isReactComponent' in target.prototype)
        )
      ) {
        propsForElement.ref = this.onRef
        delete propsForElement.innerRef
      } else {
        propsForElement.innerRef = this.onRef
      }

      return createElement(target, propsForElement, children)
    }
  }

  const createStyledNativeComponent = (
    target: Target,
    options: Object,
    rules: RuleSet,
  ) => {
    const {
      displayName = isTag(target) ? `styled.${target}` : `Styled(${getComponentName(target)})`,
      ParentComponent = BaseStyledNativeComponent,
      rules: extendingRules,
      attrs,
    } = options

    const inlineStyle = new InlineStyle(
      extendingRules === undefined ? rules : extendingRules.concat(rules),
    )

    class StyledNativeComponent extends ParentComponent {
      static displayName = displayName
      static target = target
      static attrs = attrs
      static inlineStyle = inlineStyle

      static contextTypes = {
        [CHANNEL]: PropTypes.func,
        [CHANNEL_NEXT]: CONTEXT_CHANNEL_SHAPE,
      }

      // NOTE: This is so that isStyledComponent passes for the innerRef unwrapping
      static styledComponentId = 'StyledNativeComponent'

      static withComponent(tag) {
        const { displayName: _, componentId: __, ...optionsToCopy } = options
        const newOptions = { ...optionsToCopy, ParentComponent: StyledNativeComponent }
        return createStyledNativeComponent(tag, newOptions, rules)
      }

      static get extend() {
        const {
          displayName: _,
          componentId: __,
          rules: rulesFromOptions,
          ...optionsToCopy
        } = options

        const newRules = rulesFromOptions === undefined
          ? rules
          : rulesFromOptions.concat(rules)

        const newOptions = {
          ...optionsToCopy,
          rules: newRules,
          ParentComponent: StyledNativeComponent,
        }

        return constructWithOptions(
          createStyledNativeComponent,
          target,
          newOptions,
        )
      }
    }

    return StyledNativeComponent
  }

  return createStyledNativeComponent
}
<commit_msg>use determineTheme in StyledNativeComponent<commit_after>// @flow
import { Component, createElement } from 'react'
import PropTypes from 'prop-types'

import type { Theme } from './ThemeProvider'

import isTag from '../utils/isTag'
import isStyledComponent from '../utils/isStyledComponent'
import getComponentName from '../utils/getComponentName'
import determineTheme from '../utils/determineTheme'
import type { RuleSet, Target } from '../types'

import { CHANNEL, CHANNEL_NEXT, CONTEXT_CHANNEL_SHAPE } from './ThemeProvider'

export default (constructWithOptions: Function, InlineStyle: Function) => {
  class BaseStyledNativeComponent extends Component {
    static target: Target
    static styledComponentId: string
    static attrs: Object
    static inlineStyle: Object
    root: ?Object

    attrs = {}
    state = {
      theme: null,
      generatedStyles: undefined,
    }

    unsubscribeId: number = -1

    unsubscribeFromContext() {
      if (this.unsubscribeId !== -1) {
        this.context[CHANNEL_NEXT].unsubscribe(this.unsubscribeId)
      }
    }


    buildExecutionContext(theme: any, props: any) {
      const { attrs } = this.constructor
      const context = { ...props, theme }
      if (attrs === undefined) {
        return context
      }

      this.attrs = Object.keys(attrs).reduce((acc, key) => {
        const attr = attrs[key]
        // eslint-disable-next-line no-param-reassign
        acc[key] = typeof attr === 'function' ? attr(context) : attr
        return acc
      }, {})

      return { ...context, ...this.attrs }
    }

    generateAndInjectStyles(theme: any, props: any) {
      const { inlineStyle } = this.constructor
      const executionContext = this.buildExecutionContext(theme, props)

      return inlineStyle.generateStyleObject(executionContext)
    }

    componentWillMount() {
      // If there is a theme in the context, subscribe to the event emitter. This
      // is necessary due to pure components blocking context updates, this circumvents
      // that by updating when an event is emitted
      const styledContext = this.context[CHANNEL_NEXT]
      if (styledContext !== undefined) {
        const { subscribe } = styledContext
        this.unsubscribeId = subscribe(nextTheme => {
          // This will be called once immediately
          const theme = determineTheme(this.props, nextTheme, this.constructor.defaultProps)
          const generatedStyles = this.generateAndInjectStyles(theme, this.props)

          this.setState({ theme, generatedStyles })
        })
      } else {
        // eslint-disable-next-line react/prop-types
        const theme = this.props.theme || {}
        const generatedStyles = this.generateAndInjectStyles(
          theme,
          this.props,
        )
        this.setState({ theme, generatedStyles })
      }
    }

    componentWillReceiveProps(nextProps: { theme?: Theme, [key: string]: any }) {
      this.setState((oldState) => {
        const theme = determineTheme(nextProps, oldState.theme, this.constructor.defaultProps)
        const generatedClassName = this.generateAndInjectStyles(theme, nextProps)

        return { theme, generatedClassName }
      })
    }

    componentWillUnmount() {
      this.unsubscribeFromContext()
    }

    setNativeProps(nativeProps: Object) {
      if (this.root !== undefined) {
        // $FlowFixMe
        this.root.setNativeProps(nativeProps)
      } else {
        const { displayName } = this.constructor

        // eslint-disable-next-line no-console
        console.warn(
          'setNativeProps was called on a Styled Component wrapping a stateless functional component. ' +
          'In this case no ref will be stored, and instead an innerRef prop will be passed on.\n' +
          `Check whether the stateless functional component is passing on innerRef as a ref in ${displayName}.`,
        )
      }
    }

    onRef = (node: any) => {
      // eslint-disable-next-line react/prop-types
      const { innerRef } = this.props
      this.root = node

      if (typeof innerRef === 'function') {
        innerRef(node)
      }
    }

    render() {
      // eslint-disable-next-line react/prop-types
      const { children, style } = this.props
      const { generatedStyles } = this.state
      const { target } = this.constructor

      const propsForElement = {
        ...this.attrs,
        ...this.props,
        style: [generatedStyles, style],
      }

      if (
        !isStyledComponent(target) &&
        (
          // NOTE: We can't pass a ref to a stateless functional component
          typeof target !== 'function' ||
          (target.prototype && 'isReactComponent' in target.prototype)
        )
      ) {
        propsForElement.ref = this.onRef
        delete propsForElement.innerRef
      } else {
        propsForElement.innerRef = this.onRef
      }

      return createElement(target, propsForElement, children)
    }
  }

  const createStyledNativeComponent = (
    target: Target,
    options: Object,
    rules: RuleSet,
  ) => {
    const {
      displayName = isTag(target) ? `styled.${target}` : `Styled(${getComponentName(target)})`,
      ParentComponent = BaseStyledNativeComponent,
      rules: extendingRules,
      attrs,
    } = options

    const inlineStyle = new InlineStyle(
      extendingRules === undefined ? rules : extendingRules.concat(rules),
    )

    class StyledNativeComponent extends ParentComponent {
      static displayName = displayName
      static target = target
      static attrs = attrs
      static inlineStyle = inlineStyle

      static contextTypes = {
        [CHANNEL]: PropTypes.func,
        [CHANNEL_NEXT]: CONTEXT_CHANNEL_SHAPE,
      }

      // NOTE: This is so that isStyledComponent passes for the innerRef unwrapping
      static styledComponentId = 'StyledNativeComponent'

      static withComponent(tag) {
        const { displayName: _, componentId: __, ...optionsToCopy } = options
        const newOptions = { ...optionsToCopy, ParentComponent: StyledNativeComponent }
        return createStyledNativeComponent(tag, newOptions, rules)
      }

      static get extend() {
        const {
          displayName: _,
          componentId: __,
          rules: rulesFromOptions,
          ...optionsToCopy
        } = options

        const newRules = rulesFromOptions === undefined
          ? rules
          : rulesFromOptions.concat(rules)

        const newOptions = {
          ...optionsToCopy,
          rules: newRules,
          ParentComponent: StyledNativeComponent,
        }

        return constructWithOptions(
          createStyledNativeComponent,
          target,
          newOptions,
        )
      }
    }

    return StyledNativeComponent
  }

  return createStyledNativeComponent
}

{'fraction_non_alphanumeric': 0.09556601784835922, 'fraction_numerical': 0.00028107652308340944, 'mean_word_length': 2.3789173789173788, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3293620', 'n_tokens_mistral': 3773, 'n_tokens_neox': 3703, 'n_words': 987}