reactrb 0.8.7 → 0.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ddbe495a34c70871decbabe9d4caf2816922686c
4
- data.tar.gz: c04dd8a078c54622c08dc2442d593d21cea54fca
3
+ metadata.gz: 56bb10a0b7e0570fcd71cd6eb8a84b683d7c5477
4
+ data.tar.gz: 6f3556f79685a0ed59fa24e3adffe9865f2c37d7
5
5
  SHA512:
6
- metadata.gz: d1465573197ce0818e16208eab0dd5155e6bb121ca1219df7ea9a12b2a44181e14c6b2fcb0076455ce9fd70441d4112b23d340afe12dc9d3c4c1f62d09caa28b
7
- data.tar.gz: 87b26b83e9467871b9d3be9a7e905060e5ddbb4893b1ae10e63ae56f36bb74d512af5ded00b5392f9a77742398998617e94a8252dc263c924dd51c1a8b8ff319
6
+ metadata.gz: 659a94fbc934575d85084a11a0006ccb358abe68cf9a77553bf2d64e794bd5eb424ff1399dfaac62cb37d7b4c5c7ec5c00b225eae49939b01af24262de989b42
7
+ data.tar.gz: e42a8bf45da2475c6069b10956a770bdbffb73ee53cb96f72f1021a7e7e832b9cab9fc381e3ba11f29e08fbbccf3e513fa0ffbb1258cfa4c9ad6ef97024cc9d5
data/CHANGELOG.md CHANGED
@@ -6,7 +6,7 @@ This project *tries* to adhere to [Semantic Versioning](http://semver.org/), eve
6
6
  Changes are grouped as follows:
7
7
  - **Added** for new features.
8
8
  - **Changed** for changes in existing functionality.
9
- - **Deprecated** for once-stable features removed in upcoming releases.
9
+ - **Deprecated** for once-stable features to be removed in upcoming releases.
10
10
  - **Removed** for deprecated features removed in this release.
11
11
  - **Fixed** for any bug fixes.
12
12
  - **Security** to invite users to upgrade in case of vulnerabilities.
@@ -18,7 +18,35 @@ Whitespace conventions:
18
18
  - 1 spaces before normal text
19
19
  -->
20
20
 
21
+ ## [0.8.8] - 2016-07-13
21
22
 
23
+ ### Added
24
+
25
+ - More helpful error messages on render failures (#152)
26
+ - `Element#on('<my_event_name>')` subscribes `my_event_name` (#153)
27
+
28
+ ### Changed
29
+
30
+ - `Element#on(:event)` subscribes to `on_event` for reactrb components and `onEvent` for native components. (#153)
31
+
32
+ ### Deprecated
33
+
34
+ - `Element#(:event)` subscription to `_onEvent` is deprecated. Once you have changed params named `_on...` to `on_...` you can `require 'reactrb/new-event-name-convention.rb'` to avoid spurious react warning messages. (#153)
35
+
36
+
37
+ ### Fixed
38
+
39
+ - when using the Element['#container'].render... method generates spurious react error (#154)
40
+
41
+
42
+
43
+
44
+ ## [0.8.7] - 2016-07-08
45
+
46
+
47
+ ### Fixed
48
+
49
+ - Opal 0.10.x compatibility
22
50
 
23
51
 
24
52
  ## [0.8.6] - 2016-06-30
data/README.md CHANGED
@@ -21,7 +21,7 @@ Install the gem, or load the js library
21
21
 
22
22
  + add `gem 'reactrb'` to your gem file or
23
23
  + `gem install reactrb` or
24
- + install (or load via cdn) [inline-reactrb.js](http://github.com/reactrb/inline-reactrb)
24
+ + install (or load via cdn) [reactrb-express.js](http://github.com/reactrb/reactrb-express)
25
25
 
26
26
  For gem installation it is highly recommended to read [the getting started section at reactrb.org](http://reactrb.org/docs/getting-started.html)
27
27
 
data/lib/react/api.rb CHANGED
@@ -16,6 +16,7 @@ module React
16
16
  @@component_classes = {}
17
17
 
18
18
  def self.import_native_component(opal_class, native_class)
19
+ opal_class.instance_variable_set("@native_import", true)
19
20
  @@component_classes[opal_class] = native_class
20
21
  end
21
22
 
@@ -2,6 +2,11 @@ module React
2
2
  module Component
3
3
  # class level methods (macros) for components
4
4
  module ClassMethods
5
+
6
+ def reactrb_component?
7
+ true
8
+ end
9
+
5
10
  def backtrace(*args)
6
11
  @dont_catch_exceptions = (args[0] == :none)
7
12
  @backtrace_off = @dont_catch_exceptions || (args[0] == :off)
@@ -43,7 +48,7 @@ module React
43
48
  _componentValidator: %x{
44
49
  function(props, propName, componentName) {
45
50
  var errors = #{validator.validate(Hash.new(`props`))};
46
- var error = new Error(#{"In component `" + self.name + "`\n" + `errors`.join("\n")});
51
+ var error = new Error(#{"In component `#{name}`\n" + `errors`.join("\n")});
47
52
  return #{`errors`.count > 0 ? `error` : `undefined`};
48
53
  }
49
54
  }
data/lib/react/element.rb CHANGED
@@ -1,6 +1,18 @@
1
1
  require 'react/ext/string'
2
2
 
3
3
  module React
4
+ #
5
+ # Wraps the React Native element class
6
+ #
7
+ # adds the #on method to add event handlers to the element
8
+ #
9
+ # adds the #render method to place elements in the DOM and
10
+ # #delete (alias/deprecated #as_node) method to remove elements from the DOM
11
+ #
12
+ # handles the haml style class notation so that
13
+ # div.bar.blat becomes div(class: "bar blat")
14
+ # by using method missing
15
+ #
4
16
  class Element
5
17
  include Native
6
18
 
@@ -20,58 +32,124 @@ module React
20
32
  @native = native_element
21
33
  end
22
34
 
23
- def on(event_name)
24
- name = event_name.to_s.event_camelize
25
- props = if React::Event::BUILT_IN_EVENTS.include?("on#{name}")
26
- {"on#{name}" => %x{
27
- function(event){
28
- #{yield React::Event.new(`event`)}
29
- }
30
- }}
31
- else
32
- {"_on#{name}" => %x{
33
- function(){
34
- #{yield *Array(`arguments`)}
35
- }
36
- }}
37
- end
38
- @native = `React.cloneElement(#{self.to_n}, #{props.to_n})`
39
- @properties.merge! props
35
+ # Attach event handlers.
36
+
37
+ def on(*event_names, &block)
38
+ event_names.each { |event_name| merge_event_prop!(event_name, &block) }
39
+ @native = `React.cloneElement(#{to_n}, #{properties.to_n})`
40
40
  self
41
41
  end
42
42
 
43
- def render(props = {}) # for rendering children
43
+ # Render element into DOM in the current rendering context.
44
+ # Used for elements that are not yet in DOM, i.e. they are provided as children
45
+ # or they have been explicitly removed from the rendering context using the delete method.
46
+
47
+ def render(props = {})
44
48
  if props.empty?
45
49
  React::RenderingContext.render(self)
46
50
  else
47
51
  React::RenderingContext.render(
48
52
  Element.new(
49
- `React.cloneElement(#{self.to_n}, #{API.convert_props(props)})`,
50
- type,
51
- properties.merge(props),
52
- block
53
+ `React.cloneElement(#{to_n}, #{API.convert_props(props)})`,
54
+ type, properties.merge(props), block
53
55
  )
54
56
  )
55
57
  end
56
58
  end
57
59
 
60
+ # Delete (remove) element from rendering context, the element may later be added back in
61
+ # using the render method.
62
+
63
+ def delete
64
+ React::RenderingContext.delete(self)
65
+ end
66
+
67
+ # Deprecated version of delete method
68
+
69
+ def as_node
70
+ React::RenderingContext.as_node(self)
71
+ end
72
+
73
+ # Any other method applied to an element will be treated as class name (haml style) thus
74
+ # div.foo.bar(id: :fred) is the same as saying div(class: "foo bar", id: :fred)
75
+ #
76
+ # single underscores become dashes, and double underscores become a single underscore
77
+ #
78
+ # params may be provide to each class (but typically only to the last for easy reading.)
79
+
58
80
  def method_missing(class_name, args = {}, &new_block)
59
- class_name = class_name.split("__").collect { |s| s.gsub("_", "-") }.join("_")
81
+ class_name = class_name.gsub(/__|_/, '__' => '_', '_' => '-')
60
82
  new_props = properties.dup
61
- new_props["class"] = "#{new_props['class']} #{class_name} #{args.delete("class")} #{args.delete('className')}".split(" ").uniq.join(" ")
83
+ new_props[:class] = "\
84
+ #{class_name} #{new_props[:class]} #{args.delete(:class)} #{args.delete(:className)}\
85
+ ".split(' ').uniq.join(' ')
62
86
  new_props.merge! args
63
87
  React::RenderingContext.replace(
64
88
  self,
65
- React::RenderingContext.build { React::RenderingContext.render(type, new_props, &new_block) }
89
+ RenderingContext.build { RenderingContext.render(type, new_props, &new_block) }
66
90
  )
67
91
  end
68
92
 
69
- def as_node
70
- React::RenderingContext.as_node(self)
93
+ private
94
+
95
+ # built in events, events going to native components, and events going to reactrb
96
+
97
+ # built in events will have their event param translated to the Event wrapper
98
+ # and the name will camelcased and have on prefixed, so :click becomes onClick.
99
+ #
100
+ # events emitting from native components are assumed to have the same camel case and
101
+ # on prefixed.
102
+ #
103
+ # events emitting from reactrb components will just have on_ prefixed. So
104
+ # :play_button_pushed attaches to the :on_play_button_pushed param
105
+ #
106
+ # in all cases the default name convention can be overriden by wrapping in <...> brackets.
107
+ # So on("<MyEvent>") will attach to the "MyEvent" param.
108
+
109
+ def merge_event_prop!(event_name, &block)
110
+ if event_name =~ /^<(.+)>$/
111
+ merge_component_event_prop! event_name.gsub(/^<(.+)>$/, '\1'), &block
112
+ elsif React::Event::BUILT_IN_EVENTS.include?(name = "on#{event_name.event_camelize}")
113
+ merge_built_in_event_prop! name, &block
114
+ elsif @type.instance_variable_get('@native_import')
115
+ merge_component_event_prop! name, &block
116
+ else
117
+ merge_deprecated_component_event_prop! event_name, &block
118
+ merge_component_event_prop! "on_#{event_name}", &block
119
+ end
71
120
  end
72
121
 
73
- def delete
74
- React::RenderingContext.delete(self)
122
+ def merge_built_in_event_prop!(prop_name)
123
+ @properties.merge!(
124
+ prop_name => %x{
125
+ function(event){
126
+ return #{yield(React::Event.new(`event`))}
127
+ }
128
+ }
129
+ )
130
+ end
131
+
132
+ def merge_component_event_prop!(prop_name)
133
+ @properties.merge!(
134
+ prop_name => %x{
135
+ function(){
136
+ return #{yield(*Array(`arguments`))}
137
+ }
138
+ }
139
+ )
140
+ end
141
+
142
+ def merge_deprecated_component_event_prop!(event_name)
143
+ prop_name = "_on#{event_name.event_camelize}"
144
+ fn = %x{function(){#{
145
+ React::Component.deprecation_warning(
146
+ "In future releases React::Element#on('#{event_name}') will no longer respond "\
147
+ "to the '#{prop_name}' emitter.\n"\
148
+ "Rename your emitter param to 'on_#{event_name}' or use .on('<#{prop_name}>')"
149
+ )}
150
+ return #{yield(*Array(`arguments`))}
151
+ }}
152
+ @properties.merge!(prop_name => fn)
75
153
  end
76
154
  end
77
155
  end
@@ -13,24 +13,12 @@ module React
13
13
 
14
14
  def self.render(name, *args, &block)
15
15
  remove_nodes_from_args(args)
16
- @buffer = [] unless @buffer
16
+ @buffer ||= [] unless @buffer
17
17
  if block
18
18
  element = build do
19
19
  saved_waiting_on_resources = waiting_on_resources
20
20
  self.waiting_on_resources = nil
21
- result = block.call
22
- # Todo figure out how children rendering should happen, probably should have special method that pushes children into the buffer
23
- # i.e. render_child/render_children that takes Element/Array[Element] and does the push into the buffer
24
- if !name && ( # !name means called from outer render so we check that it has rendered correctly
25
- (@buffer.count > 1) || # should only render one element
26
- (@buffer.count == 1 && @buffer.last != result) || # it should return that element
27
- (@buffer.count == 0 && !(result.is_a?(String) || (result.respond_to?(:acts_as_string?) && result.acts_as_string?) || result.is_a?(Element))) #for convience we will also convert the return value to a span if its a string
28
- )
29
- raise "a components render method must generate and return exactly 1 element or a string"
30
- end
31
-
32
- @buffer << result.to_s if result.is_a? String || (result.respond_to?(:acts_as_string?) && result.acts_as_string?) # For convience we push the last return value on if its a string
33
- @buffer << result if result.is_a?(Element) && @buffer.count == 0
21
+ run_child_block(name.nil?, &block)
34
22
  if name
35
23
  buffer = @buffer.dup
36
24
  React.create_element(name, *args) { buffer }.tap do |element|
@@ -44,7 +32,6 @@ module React
44
32
  end
45
33
  elsif name.is_a? React::Element
46
34
  element = name
47
- # I BELIEVE WAITING ON RESOURCES SHOULD ALREADY BE SET
48
35
  else
49
36
  element = React.create_element(name, *args)
50
37
  element.waiting_on_resources = waiting_on_resources
@@ -78,6 +65,51 @@ module React
78
65
  value.as_node if value.is_a?(Element) rescue nil
79
66
  end if args[0] && args[0].is_a?(Hash)
80
67
  end
68
+
69
+ # run_child_block gathers the element(s) generated by a child block.
70
+ # for example when rendering this div: div { "hello".span; "goodby".span }
71
+ # two child Elements will be generated.
72
+ #
73
+ # the final value of the block should either be
74
+ # 1 an object that responds to :acts_as_string?
75
+ # 2 a string,
76
+ # 3 an element that is NOT yet pushed on the rendering buffer
77
+ # 4 or the last element pushed on the buffer
78
+ #
79
+ # in case 1 we change the object to a string, and then it becomes case 2
80
+ # in case 2 we automatically push the string onto the buffer
81
+ # in case 3 we also push the Element onto the buffer IF the buffer is empty
82
+ # case 4 requires no special processing
83
+ #
84
+ # Once we have taken care of these special cases we do a check IF we are in an
85
+ # outer rendering scope. In this case react only allows us to generate 1 Element
86
+ # so we insure that is the case, and also check to make sure that element in the buffer
87
+ # is the element returned
88
+
89
+ def self.run_child_block(is_outer_scope)
90
+ result = yield
91
+ result = result.to_s if result.try :acts_as_string?
92
+ @buffer << result if result.is_a?(String) || (result.is_a?(Element) && @buffer.empty?)
93
+ raise_render_error(result) if is_outer_scope && @buffer != [result]
94
+ end
95
+
96
+ # heurestically raise a meaningful error based on the situation
97
+
98
+ def self.raise_render_error(result)
99
+ improper_render 'A different element was returned than was generated within the DSL.',
100
+ 'Possibly improper use of Element#delete.' if @buffer.count == 1
101
+ improper_render "Instead #{@buffer.count} elements were generated.",
102
+ 'Do you want to wrap your elements in a div?' if @buffer.count > 1
103
+ improper_render "Instead the component #{result} was returned.",
104
+ "Did you mean #{result}()?" if result.try :reactrb_component?
105
+ improper_render "Instead the #{result.class} #{result} was returned.",
106
+ 'You may need to convert this to a string.'
107
+ end
108
+
109
+ def self.improper_render(message, solution)
110
+ raise "a component's render method must generate and return exactly 1 element or a string.\n"\
111
+ " #{message} #{solution}"
112
+ end
81
113
  end
82
114
 
83
115
  class ::Object
@@ -1,3 +1,3 @@
1
1
  module React
2
- VERSION = '0.8.7'
2
+ VERSION = '0.8.8'
3
3
  end
@@ -0,0 +1,11 @@
1
+ # rubocop:disable Style/FileName
2
+ # require 'reactrb/new-event-name-convention' to remove missing param declaration "_onXXXX"
3
+ if RUBY_ENGINE == 'opal'
4
+ # removes generation of the deprecated "_onXXXX" event param syntax
5
+ module React
6
+ class Element
7
+ def merge_deprecated_component_event_prop!(event_name)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -477,6 +477,77 @@ describe React::Component do
477
477
  end
478
478
  end
479
479
 
480
+ describe 'Anonymous Component' do
481
+ it "will not generate spurious warning messages" do
482
+ foo = Class.new(React::Component::Base)
483
+ foo.class_eval do
484
+ def render; "hello" end
485
+ end
486
+
487
+ %x{
488
+ var log = [];
489
+ var org_warn_console = window.console.warn;
490
+ var org_error_console = window.console.error
491
+ window.console.warn = window.console.error = function(str){log.push(str)}
492
+ }
493
+ renderToDocument(foo)
494
+ `window.console.warn = org_warn_console; window.console.error = org_error_console;`
495
+ expect(`log`).to eq([])
496
+ end
497
+ end
498
+
499
+ describe 'Render Error Handling' do
500
+ before(:each) do
501
+ %x{
502
+ window.test_log = [];
503
+ window.org_warn_console = window.console.warn;
504
+ window.org_error_console = window.console.error
505
+ window.console.warn = window.console.error = function(str){window.test_log.push(str)}
506
+ }
507
+ end
508
+ it "will generate a message if render returns something other than an Element or a String" do
509
+ foo = Class.new(React::Component::Base)
510
+ foo.class_eval do
511
+ def render; Hash.new; end
512
+ end
513
+
514
+ renderToDocument(foo)
515
+ `window.console.warn = window.org_warn_console; window.console.error = window.org_error_console;`
516
+ expect(`test_log`.first).to match /Instead the Hash \{\} was returned/
517
+ end
518
+ it "will generate a message if render returns a Component class" do
519
+ stub_const 'Foo', Class.new(React::Component::Base)
520
+ foo = Class.new(React::Component::Base)
521
+ foo.class_eval do
522
+ def render; Foo; end
523
+ end
524
+
525
+ renderToDocument(foo)
526
+ `window.console.warn = window.org_warn_console; window.console.error = window.org_error_console;`
527
+ expect(`test_log`.first).to match /Did you mean Foo()/
528
+ end
529
+ it "will generate a message if more than 1 element is generated" do
530
+ foo = Class.new(React::Component::Base)
531
+ foo.class_eval do
532
+ def render; "hello".span; "goodby".span; end
533
+ end
534
+
535
+ renderToDocument(foo)
536
+ `window.console.warn = window.org_warn_console; window.console.error = window.org_error_console;`
537
+ expect(`test_log`.first).to match /Instead 2 elements were generated/
538
+ end
539
+ it "will generate a message if the element generated is not the element returned" do
540
+ foo = Class.new(React::Component::Base)
541
+ foo.class_eval do
542
+ def render; "hello".span; "goodby".span.delete; end
543
+ end
544
+
545
+ renderToDocument(foo)
546
+ `window.console.warn = window.org_warn_console; window.console.error = window.org_error_console;`
547
+ expect(`test_log`.first).to match /A different element was returned than was generated within the DSL/
548
+ end
549
+ end
550
+
480
551
  describe 'Event handling' do
481
552
  before do
482
553
  stub_const 'Foo', Class.new
@@ -1,6 +1,8 @@
1
1
  require "spec_helper"
2
2
 
3
3
  if opal?
4
+ # require 'reactrb/new-event-name-convention' # this require will get rid of any error messages but
5
+ # the on method will no longer attach to the param prefixed with _on
4
6
  describe React::Element do
5
7
  it 'bridges `type` of native React.Element attributes' do
6
8
  element = React.create_element('div')
@@ -17,7 +19,79 @@ describe React::Element do
17
19
  end
18
20
  end
19
21
 
20
- describe 'Event subscription' do
22
+ describe 'Component Event Subscription' do
23
+
24
+ it 'will subscribe to a component event param' do
25
+ stub_const 'Foo', Class.new(React::Component::Base)
26
+ Foo.class_eval do
27
+ param :on_event, type: Proc, default: nil, allow_nil: true
28
+ def render
29
+ params.on_event
30
+ end
31
+ end
32
+ expect(React.render_to_static_markup(React.create_element(Foo).on(:event) {'works!'})).to eq('<span>works!</span>')
33
+ end
34
+
35
+ it 'will subscribe to multiple component event params' do
36
+ stub_const 'Foo', Class.new(React::Component::Base)
37
+ Foo.class_eval do
38
+ param :on_event1, type: Proc, default: nil, allow_nil: true
39
+ param :on_event2, type: Proc, default: nil, allow_nil: true
40
+ def render
41
+ params.on_event1+params.on_event2
42
+ end
43
+ end
44
+ expect(React.render_to_static_markup(React.create_element(Foo).on(:event1, :event2) {'works!'})).to eq('<span>works!works!</span>')
45
+ end
46
+
47
+ it 'will subscribe to a native components event param' do
48
+ %x{
49
+ window.NativeComponent = React.createClass({
50
+ displayName: "HelloMessage",
51
+ render: function render() {
52
+ return React.createElement("span", null, this.props.onEvent());
53
+ }
54
+ })
55
+ }
56
+ stub_const 'Foo', Class.new(React::Component::Base)
57
+ Foo.class_eval do
58
+ imports "NativeComponent"
59
+ end
60
+ expect(React.render_to_static_markup(React.create_element(Foo).on(:event) {'works!'})).to eq('<span>works!</span>')
61
+ end
62
+
63
+ it 'will subscribe to a component event param with a non-default name' do
64
+ stub_const 'Foo', Class.new(React::Component::Base)
65
+ Foo.class_eval do
66
+ param :my_event, type: Proc, default: nil, allow_nil: true
67
+ def render
68
+ params.my_event
69
+ end
70
+ end
71
+ expect(React.render_to_static_markup(React.create_element(Foo).on("<my_event>") {'works!'})).to eq('<span>works!</span>')
72
+ end
73
+
74
+ it 'will subscribe to a component event param using the deprecated naming convention and generate a message' do
75
+ stub_const 'Foo', Class.new(React::Component::Base)
76
+ Foo.class_eval do
77
+ param :_onEvent, type: Proc, default: nil, allow_nil: true
78
+ def render
79
+ params._onEvent
80
+ end
81
+ end
82
+ %x{
83
+ var log = [];
84
+ var org_warn_console = window.console.warn;
85
+ var org_error_console = window.console.error;
86
+ window.console.warn = window.console.error = function(str){log.push(str)}
87
+ }
88
+ expect(React.render_to_static_markup(React.create_element(Foo).on(:event) {'works!'})).to eq('<span>works!</span>')
89
+ `window.console.warn = org_warn_console; window.console.error = org_error_console;`
90
+ expect(`log`).to eq(["Warning: Failed propType: In component `Foo`\nProvided prop `on_event` not specified in spec", "Warning: Deprecated feature used in React::Component. In future releases React::Element#on('event') will no longer respond to the '_onEvent' emitter.\nRename your emitter param to 'on_event' or use .on('<_onEvent>')"])
91
+ end
92
+ end
93
+
94
+ describe 'Builtin Event subscription' do
21
95
  it 'is subscribable through `on(:event_name)` method' do
22
96
  expect { |b|
23
97
  element = React.create_element("div").on(:click, &b)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reactrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.7
4
+ version: 0.8.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Chang
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2016-07-08 00:00:00.000000000 Z
13
+ date: 2016-07-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: opal
@@ -377,6 +377,7 @@ files:
377
377
  - lib/reactive-ruby/version.rb
378
378
  - lib/reactrb.rb
379
379
  - lib/reactrb/auto-import.rb
380
+ - lib/reactrb/new-event-name-convention.rb
380
381
  - lib/sources/react-latest.js
381
382
  - lib/sources/react-v13.js
382
383
  - lib/sources/react-v14.js